# HG changeset patch # User John Tsiombikas # Date 1380160025 -10800 # Node ID 188c697b3b49aa17e4848bd58310a7dc0a047e50 # Parent 798df5111b560846b8c1213ef52bc5b686cfa3d8 - added a document describing the goat3d file format chunk hierarchy - started an alternative XML-based file format - added the openctm library diff -r 798df5111b56 -r 188c697b3b49 .hgignore --- a/.hgignore Tue Sep 10 15:29:45 2013 +0300 +++ b/.hgignore Thu Sep 26 04:47:05 2013 +0300 @@ -9,3 +9,4 @@ \.suo$ x64/ \.tlog$ +^tags$ diff -r 798df5111b56 -r 188c697b3b49 Makefile --- a/Makefile Tue Sep 10 15:29:45 2013 +0300 +++ b/Makefile Thu Sep 26 04:47:05 2013 +0300 @@ -8,6 +8,11 @@ obj = $(src:.cc=.o) dep = $(obj:.o=.d) +openctm = libs/openctm/libopenctm.a + +extinc = -Ilibs/openctm +extlibs = $(openctm) + name = goat3d so_major = 0 so_minor = 1 @@ -26,18 +31,21 @@ pic = -fPIC endif -CXXFLAGS = -pedantic -Wall $(dbg) $(opt) $(pic) -LDFLAGS = -lvmath -lanim +CXXFLAGS = -pedantic -Wall $(dbg) $(opt) $(pic) $(extinc) +LDFLAGS = $(extlibs) -lvmath -lanim .PHONY: all all: $(lib_so) $(lib_a) -$(lib_so): $(obj) +$(lib_so): $(obj) $(extlibs) $(CXX) -o $@ $(shared) $(obj) $(LDFLAGS) -$(lib_a): $(obj) +$(lib_a): $(obj) $(extlibs) $(AR) rcs $@ $(obj) +$(openctm): + $(MAKE) -C libs/openctm + -include $(dep) %.d: %.cc diff -r 798df5111b56 -r 188c697b3b49 doc/goatfmt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/goatfmt Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,110 @@ +Chunk structure +--------------- + +Leaf nodes which carry actual data are marked with , or +[MULTIPLE_CHILDREN]. Where choice is involved, options are seperated with the | +symbol. + +SCENE + +--ENV + | +--AMBIENT + | | +-- + | +--FOG + | +--FOG_COLOR + | | +-- + | +--FOG_EXP + | +-- + +--MTL + | +--MTL_NAME + | | +-- + | +--MTL_ATTR + | +--MTL_ATTR_NAME + | | +-- + | +--MTL_ATTR_VAL + | | +-- + | +--MTL_ATTR_MAP + | +-- (filename) + +--MESH + | +--MESH_NAME + | | +-- + | +--MESH_MATERIAL + | | +-- (material name) + | +--MESH_VERTEX_LIST + | | +--[FLOAT3] + | +--MESH_NORMAL_LIST + | | +--[FLOAT3] + | +--MESH_TANGENT_LIST + | | +--[FLOAT3] + | +--MESH_TEXCOORD_LIST + | | +--[FLOAT3] + | +--MESH_SKINWEIGHT_LIST + | | +--[FLOAT4] (vector elements are the 4 skin weights/vertex) + | +--MESH_SKINMATRIX_LIST + | | +--[INT4] (vector elements are the 4 matrix indices/vertex) + | +--MESH_COLOR_LIST + | | +--[FLOAT4] + | +--MESH_BONES_LIST + | | +--[INT|STRING] (list of bone nodes by name or index) + | +--MESH_FACE_LIST + | | +--MESH_FACE + | | +--[INT] (three INT chunks for the vertex indices) + | +--MESH_FILE + | +-- (filename of mesh file to get all the data from) + +--LIGHT + | +--LIGHT_NAME + | | +-- + | +--LIGHT_POS + | | +-- + | +--LIGHT_COLOR + | | +-- + | +--LIGHT_ATTEN + | | +-- (constant, linear, and quadratic attenuation) + | +--LIGHT_DISTANCE + | | +-- + | +--LIGHT_DIR + | | +-- + | +--LIGHT_CONE_INNER + | | +-- + | +--LIGHT_CONE_OUTER + | +-- + +--CAMERA + | +--CAMERA_NAME + | | +-- + | +--CAMERA_POS + | | +-- + | +--CAMERA_TARGET + | | +-- + | +--CAMERA_FOV + | | +-- + | +--CAMERA_NEARCLIP + | | +-- + | +--CAMERA_FARCLIP + | +-- + +--NODE + +--NODE_NAME + | +-- + +--NODE_PARENT + | +-- + +--NODE_MESH|NODE_LIGHT|NODE_CAMERA + | +-- + +--NODE_POS + | +-- + +--NODE_ROT + | +-- ([x, y, z], w <- real part) + +--NODE_SCALE + | +-- + +--NODE_PIVOT + | +-- + +--NODE_MATRIX0 + | +-- (first row) + +--NODE_MATRIX1 + | +-- (second row) + +--NODE_MATRIX2 + +-- (third row) + +NOTES: + * Name chunks are mandatory. If something doesn't have a name, one must be made + up, even if it's just "object%d". + * In node chunks, both POS/ROT/SCALE, and MATRIX0/MATRIX1/MATRIX2 are mandatory + and they must agree. Makes it easy for the reader to pick the transformation + data in whichever way is more convenient. diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/LICENSE.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/LICENSE.txt Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,20 @@ +Copyright (c) 2009-2010 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/Makefile Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,16 @@ +src = $(wildcard *.c) $(wildcard liblzma/*.c) +obj = $(src:.c=.o) +lib = libopenctm.a + +ifneq ($(shell uname -s), Darwin) + pic = -fPIC +endif + +CFLAGS = -g -Iliblzma $(pic) + +$(lib): $(obj) + $(AR) rcs $@ $(obj) + +.PHONY: clean +clean: + rm -f $(obj) $(lib) diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/README.txt Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,152 @@ +1. INTRODUCTION +=============== + +Welcome to OpenCTM! + +OpenCTM is a file format, a software library and a tool set for compression of +3D triangle meshes. The geometry is compressed to a fraction of comparable file +formats (3DS, STL, COLLADA, VRML...), and the format is easily accessible +through a simple, portable API. + +The library is written in portable C (C99), and should compile nicely on any +32/64-bit system regardless of endianity (big endian or little endian). + + +2. LICENSE +========== + +The OpenCTM API and the OpenCTM tools are released under the zlib/libpng +license (see LICENSE.txt). + +3. CREDITS +========== + +Many people have helped out in the development process of OpenCTM, with +valuable feedback, programming efforts, test models and conceptual ideas. +Also, OpenCTM relies heavily on many other open source projects. + +Here is an incomplete list of persons that deserve credit: + +- Igor Pavlov (LZMA library) +- Jonas Innala (COLLADA importer, Maya exporter plugin) +- Ilian Dinev (help with the OpenCTM file format design and the LWO loader) +- Lee Thomason (TinyXML) +- Diego Nehab (RPly - for loading PLY files) +- Lev Povalahev, Marcelo E. Magallon, Milan Ikits (GLEW) +- Thomas G. Lane, Guido Vollbeding (libjpeg) +- Jean-loup Gailly, Mark Adler (zlib) +- Daniel Karling (pnglite) + +During the development of OpenCTM, the following software has been used +extensively: + +- Ubuntu (www.ubuntu.com) +- Blender (www.blender.org) +- GCC (gcc.gnu.org) +- SciTE (www.scintilla.org/SciTE.html) +- Notepad++ (notepad-plus.sourceforge.net) +- Smultron (smultron.sourceforge.net) + +Legal notices: + +- This software is based in part on the work of the Independent JPEG Group. + + +4. CHANGES +========== + +v1.0.3 - 2010.01.15 +------------------- +- Added support for PNG format textures (ctmviewer). + +- Added support for LightWave LWO files (ctmconv and ctmviewer). + +- Added support for Geomview OFF files, e.g. as used by the Princeton Shape + Benchmark (ctmconv and ctmviewer). + +- Improved the OBJ file loader (ctmviewer and ctmconv). + +- Experimental support for VRML 2.0 files - export only (ctmconv and ctmviewer). + +- Made it possible to run ctmviewer without command line arguments. + +- Improved the normal calculation algorithm (ctmviewer and ctmconv). + +- Normals are no longer exported if no normals were present in the input file + (ctmviewer). + + +v1.0.2 - 2009.12.13 +------------------- +- Added an OpenCTM exporter plugin for Maya [Jonas Innala]. + +- Added the possiblity to save and load files from ctmviewer, effectively + turning it into a quick and simple converter tool (without all the options + in the ctmconv program, though). + +- Added a function to load texture files from ctmviewer. + +- Improved the camera control in ctmviewer (panning with the right mouse + button, zooming with the middle mouse button and the mouse wheel, feature + focusing by double clicking, Y/Z up axis selection and "fit to screen" + function). + +- Added a GUI dialog for showing errors in ctmviewer (this is especially useful + under Windows, where console output is disabeled). + +- Added an option for calculating the normals in ctmconv (if the input file + does not have normals). + +- Added options for turning off normals, texture coordinates and/or vertex + colors for the output file in ctmconv. + +- Added manuals for ctmviewer and ctmconv (man pages). + +- Added a "make install" build target for Mac OS X and Linux for simple system + wide installation (see COMPILING.txt). + +- NOTE: The Linux/X11 version of ctmviewer now reqires GTK+ 2.0. + + +v1.0.1 - 2009.11.15 +------------------- +- Notable reduction of the memory footprint by tuning of the LZMA compression + parameters. + +- Added a Wavefront OBJ file importer/exporter. + +- Some improvements to ctmviewer and ctmconv. + +- Some directory structure and build system cleanups. + + +v1.0 - 2009.11.09 +----------------- +- Added a COLLADA converter module to the ctmconv program [Jonas Innala]. + +- Added Python bindings and a demo Python program. + +- Improved the internal mesh integrity checking, to minimize the risk of invalid + data processing. + +- Improved the file format specification document. + + +v0.8 (beta) - 2009.09.14 +------------------------ +- Introduced a new API function for controlling the compression level + (ctmCompressionLevel), and set the default compression level to 5 (rather + than 9, which would eat a lot of memory, usally without much difference). + +- Changed the name "texture map" in the API to "UV map" (and all + corresponding constant and function names). This is more in line with + the nomenclature of most 3D authoring software, and avoids the confusion + with the term texture mapping in 3D hardware (which is not limited to + 2D UV mapping coordinates). + +- A few updates to the documentation. + + +v0.7 (beta) - 2009.08.29 +------------------------ +- This was the first public release of OpenCTM. diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/compressMG1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/compressMG1.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,324 @@ +//----------------------------------------------------------------------------- +// Product: OpenCTM +// File: compressMG1.c +// Description: Implementation of the MG1 compression method. +//----------------------------------------------------------------------------- +// Copyright (c) 2009-2010 Marcus Geelnard +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +//----------------------------------------------------------------------------- + +#include +#include +#include "openctm.h" +#include "internal.h" + +#ifdef __DEBUG_ +#include +#endif + + +//----------------------------------------------------------------------------- +// _compareTriangle() - Comparator for the triangle sorting. +//----------------------------------------------------------------------------- +static int _compareTriangle(const void * elem1, const void * elem2) +{ + CTMuint * tri1 = (CTMuint *) elem1; + CTMuint * tri2 = (CTMuint *) elem2; + if(tri1[0] != tri2[0]) + return tri1[0] - tri2[0]; + else + return tri1[1] - tri2[1]; +} + +//----------------------------------------------------------------------------- +// _ctmReArrangeTriangles() - Re-arrange all triangles for optimal +// compression. +//----------------------------------------------------------------------------- +static void _ctmReArrangeTriangles(_CTMcontext * self, CTMuint * aIndices) +{ + CTMuint * tri, tmp, i; + + // Step 1: Make sure that the first index of each triangle is the smallest + // one (rotate triangle nodes if necessary) + for(i = 0; i < self->mTriangleCount; ++ i) + { + tri = &aIndices[i * 3]; + if((tri[1] < tri[0]) && (tri[1] < tri[2])) + { + tmp = tri[0]; + tri[0] = tri[1]; + tri[1] = tri[2]; + tri[2] = tmp; + } + else if((tri[2] < tri[0]) && (tri[2] < tri[1])) + { + tmp = tri[0]; + tri[0] = tri[2]; + tri[2] = tri[1]; + tri[1] = tmp; + } + } + + // Step 2: Sort the triangles based on the first triangle index + qsort((void *) aIndices, self->mTriangleCount, sizeof(CTMuint) * 3, _compareTriangle); +} + +//----------------------------------------------------------------------------- +// _ctmMakeIndexDeltas() - Calculate various forms of derivatives in order to +// reduce data entropy. +//----------------------------------------------------------------------------- +static void _ctmMakeIndexDeltas(_CTMcontext * self, CTMuint * aIndices) +{ + CTMint i; + for(i = self->mTriangleCount - 1; i >= 0; -- i) + { + // Step 1: Calculate delta from second triangle index to the previous + // second triangle index, if the previous triangle shares the same first + // index, otherwise calculate the delta to the first triangle index + if((i >= 1) && (aIndices[i * 3] == aIndices[(i - 1) * 3])) + aIndices[i * 3 + 1] -= aIndices[(i - 1) * 3 + 1]; + else + aIndices[i * 3 + 1] -= aIndices[i * 3]; + + // Step 2: Calculate delta from third triangle index to the first triangle + // index + aIndices[i * 3 + 2] -= aIndices[i * 3]; + + // Step 3: Calculate derivative of the first triangle index + if(i >= 1) + aIndices[i * 3] -= aIndices[(i - 1) * 3]; + } +} + +//----------------------------------------------------------------------------- +// _ctmRestoreIndices() - Restore original indices (inverse derivative +// operation). +//----------------------------------------------------------------------------- +static void _ctmRestoreIndices(_CTMcontext * self, CTMuint * aIndices) +{ + CTMuint i; + + for(i = 0; i < self->mTriangleCount; ++ i) + { + // Step 1: Reverse derivative of the first triangle index + if(i >= 1) + aIndices[i * 3] += aIndices[(i - 1) * 3]; + + // Step 2: Reverse delta from third triangle index to the first triangle + // index + aIndices[i * 3 + 2] += aIndices[i * 3]; + + // Step 3: Reverse delta from second triangle index to the previous + // second triangle index, if the previous triangle shares the same first + // index, otherwise reverse the delta to the first triangle index + if((i >= 1) && (aIndices[i * 3] == aIndices[(i - 1) * 3])) + aIndices[i * 3 + 1] += aIndices[(i - 1) * 3 + 1]; + else + aIndices[i * 3 + 1] += aIndices[i * 3]; + } +} + +//----------------------------------------------------------------------------- +// _ctmCompressMesh_MG1() - Compress the mesh that is stored in the CTM +// context, and write it the the output stream in the CTM context. +//----------------------------------------------------------------------------- +int _ctmCompressMesh_MG1(_CTMcontext * self) +{ + CTMuint * indices; + _CTMfloatmap * map; + CTMuint i; + +#ifdef __DEBUG_ + printf("COMPRESSION METHOD: MG1\n"); +#endif + + // Perpare (sort) indices + indices = (CTMuint *) malloc(sizeof(CTMuint) * self->mTriangleCount * 3); + if(!indices) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + for(i = 0; i < self->mTriangleCount * 3; ++ i) + indices[i] = self->mIndices[i]; + _ctmReArrangeTriangles(self, indices); + + // Calculate index deltas (entropy-reduction) + _ctmMakeIndexDeltas(self, indices); + + // Write triangle indices +#ifdef __DEBUG_ + printf("Inidices: "); +#endif + _ctmStreamWrite(self, (void *) "INDX", 4); + if(!_ctmStreamWritePackedInts(self, (CTMint *) indices, self->mTriangleCount, 3, CTM_FALSE)) + { + free((void *) indices); + return CTM_FALSE; + } + + // Free temporary resources + free((void *) indices); + + // Write vertices +#ifdef __DEBUG_ + printf("Vertices: "); +#endif + _ctmStreamWrite(self, (void *) "VERT", 4); + if(!_ctmStreamWritePackedFloats(self, self->mVertices, self->mVertexCount * 3, 1)) + { + free((void *) indices); + return CTM_FALSE; + } + + // Write normals + if(self->mNormals) + { +#ifdef __DEBUG_ + printf("Normals: "); +#endif + _ctmStreamWrite(self, (void *) "NORM", 4); + if(!_ctmStreamWritePackedFloats(self, self->mNormals, self->mVertexCount, 3)) + return CTM_FALSE; + } + + // Write UV maps + map = self->mUVMaps; + while(map) + { +#ifdef __DEBUG_ + printf("UV coordinates (%s): ", map->mName ? map->mName : "no name"); +#endif + _ctmStreamWrite(self, (void *) "TEXC", 4); + _ctmStreamWriteSTRING(self, map->mName); + _ctmStreamWriteSTRING(self, map->mFileName); + if(!_ctmStreamWritePackedFloats(self, map->mValues, self->mVertexCount, 2)) + return CTM_FALSE; + map = map->mNext; + } + + // Write attribute maps + map = self->mAttribMaps; + while(map) + { +#ifdef __DEBUG_ + printf("Vertex attributes (%s): ", map->mName ? map->mName : "no name"); +#endif + _ctmStreamWrite(self, (void *) "ATTR", 4); + _ctmStreamWriteSTRING(self, map->mName); + if(!_ctmStreamWritePackedFloats(self, map->mValues, self->mVertexCount, 4)) + return CTM_FALSE; + map = map->mNext; + } + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// _ctmUncompressMesh_MG1() - Uncmpress the mesh from the input stream in the +// CTM context, and store the resulting mesh in the CTM context. +//----------------------------------------------------------------------------- +int _ctmUncompressMesh_MG1(_CTMcontext * self) +{ + CTMuint * indices; + _CTMfloatmap * map; + CTMuint i; + + // Allocate memory for the indices + indices = (CTMuint *) malloc(sizeof(CTMuint) * self->mTriangleCount * 3); + if(!indices) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Read triangle indices + if(_ctmStreamReadUINT(self) != FOURCC("INDX")) + { + self->mError = CTM_BAD_FORMAT; + free(indices); + return CTM_FALSE; + } + if(!_ctmStreamReadPackedInts(self, (CTMint *) indices, self->mTriangleCount, 3, CTM_FALSE)) + return CTM_FALSE; + + // Restore indices + _ctmRestoreIndices(self, indices); + for(i = 0; i < self->mTriangleCount * 3; ++ i) + self->mIndices[i] = indices[i]; + + // Free temporary resources + free(indices); + + // Read vertices + if(_ctmStreamReadUINT(self) != FOURCC("VERT")) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + if(!_ctmStreamReadPackedFloats(self, self->mVertices, self->mVertexCount * 3, 1)) + return CTM_FALSE; + + // Read normals + if(self->mNormals) + { + if(_ctmStreamReadUINT(self) != FOURCC("NORM")) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + if(!_ctmStreamReadPackedFloats(self, self->mNormals, self->mVertexCount, 3)) + return CTM_FALSE; + } + + // Read UV maps + map = self->mUVMaps; + while(map) + { + if(_ctmStreamReadUINT(self) != FOURCC("TEXC")) + { + self->mError = CTM_BAD_FORMAT; + return 0; + } + _ctmStreamReadSTRING(self, &map->mName); + _ctmStreamReadSTRING(self, &map->mFileName); + if(!_ctmStreamReadPackedFloats(self, map->mValues, self->mVertexCount, 2)) + return CTM_FALSE; + map = map->mNext; + } + + // Read vertex attribute maps + map = self->mAttribMaps; + while(map) + { + if(_ctmStreamReadUINT(self) != FOURCC("ATTR")) + { + self->mError = CTM_BAD_FORMAT; + return 0; + } + _ctmStreamReadSTRING(self, &map->mName); + if(!_ctmStreamReadPackedFloats(self, map->mValues, self->mVertexCount, 4)) + return CTM_FALSE; + map = map->mNext; + } + + return CTM_TRUE; +} diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/compressMG2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/compressMG2.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,1319 @@ +//----------------------------------------------------------------------------- +// Product: OpenCTM +// File: compressMG2.c +// Description: Implementation of the MG2 compression method. +//----------------------------------------------------------------------------- +// Copyright (c) 2009-2010 Marcus Geelnard +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +//----------------------------------------------------------------------------- + +#include +#include +#include "openctm.h" +#include "internal.h" + +#ifdef __DEBUG_ +#include +#endif + +// We need PI +#ifndef PI +#define PI 3.141592653589793238462643f +#endif + + +//----------------------------------------------------------------------------- +// _CTMgrid - 3D space subdivision grid. +//----------------------------------------------------------------------------- +typedef struct { + // Axis-aligned boudning box for the grid. + CTMfloat mMin[3]; + CTMfloat mMax[3]; + + // How many divisions per axis (minimum 1). + CTMuint mDivision[3]; + + // Size of each grid box. + CTMfloat mSize[3]; +} _CTMgrid; + +//----------------------------------------------------------------------------- +// _CTMsortvertex - Vertex information. +//----------------------------------------------------------------------------- +typedef struct { + // Vertex X coordinate (used for sorting). + CTMfloat x; + + // Grid index. This is the index into the 3D space subdivision grid. + CTMuint mGridIndex; + + // Original index (before sorting). + CTMuint mOriginalIndex; +} _CTMsortvertex; + +//----------------------------------------------------------------------------- +// _ctmSetupGrid() - Setup the 3D space subdivision grid. +//----------------------------------------------------------------------------- +static void _ctmSetupGrid(_CTMcontext * self, _CTMgrid * aGrid) +{ + CTMuint i; + CTMfloat factor[3], sum, wantedGrids; + + // Calculate the mesh bounding box + aGrid->mMin[0] = aGrid->mMax[0] = self->mVertices[0]; + aGrid->mMin[1] = aGrid->mMax[1] = self->mVertices[1]; + aGrid->mMin[2] = aGrid->mMax[2] = self->mVertices[2]; + for(i = 1; i < self->mVertexCount; ++ i) + { + if(self->mVertices[i * 3] < aGrid->mMin[0]) + aGrid->mMin[0] = self->mVertices[i * 3]; + else if(self->mVertices[i * 3] > aGrid->mMax[0]) + aGrid->mMax[0] = self->mVertices[i * 3]; + if(self->mVertices[i * 3 + 1] < aGrid->mMin[1]) + aGrid->mMin[1] = self->mVertices[i * 3 + 1]; + else if(self->mVertices[i * 3 + 1] > aGrid->mMax[1]) + aGrid->mMax[1] = self->mVertices[i * 3 + 1]; + if(self->mVertices[i * 3 + 2] < aGrid->mMin[2]) + aGrid->mMin[2] = self->mVertices[i * 3 + 2]; + else if(self->mVertices[i * 3 + 2] > aGrid->mMax[2]) + aGrid->mMax[2] = self->mVertices[i * 3 + 2]; + } + + // Determine optimal grid resolution, based on the number of vertices and + // the bounding box. + // NOTE: This algorithm is quite crude, and could very well be optimized for + // better compression levels in the future without affecting the file format + // or backward compatibility at all. + for(i = 0; i < 3; ++ i) + factor[i] = aGrid->mMax[i] - aGrid->mMin[i]; + sum = factor[0] + factor[1] + factor[2]; + if(sum > 1e-30f) + { + sum = 1.0f / sum; + for(i = 0; i < 3; ++ i) + factor[i] *= sum; + wantedGrids = powf(100.0f * self->mVertexCount, 1.0f / 3.0f); + for(i = 0; i < 3; ++ i) + { + aGrid->mDivision[i] = (CTMuint) ceilf(wantedGrids * factor[i]); + if(aGrid->mDivision[i] < 1) + aGrid->mDivision[i] = 1; + } + } + else + { + aGrid->mDivision[0] = 4; + aGrid->mDivision[1] = 4; + aGrid->mDivision[2] = 4; + } +#ifdef __DEBUG_ + printf("Division: (%d %d %d)\n", aGrid->mDivision[0], aGrid->mDivision[1], aGrid->mDivision[2]); +#endif + + // Calculate grid sizes + for(i = 0; i < 3; ++ i) + aGrid->mSize[i] = (aGrid->mMax[i] - aGrid->mMin[i]) / aGrid->mDivision[i]; +} + +//----------------------------------------------------------------------------- +// _ctmPointToGridIdx() - Convert a point to a grid index. +//----------------------------------------------------------------------------- +static CTMuint _ctmPointToGridIdx(_CTMgrid * aGrid, CTMfloat * aPoint) +{ + CTMuint i, idx[3]; + + for(i = 0; i < 3; ++ i) + { + idx[i] = (CTMuint) floorf((aPoint[i] - aGrid->mMin[i]) / aGrid->mSize[i]); + if(idx[i] >= aGrid->mDivision[i]) + idx[i] = aGrid->mDivision[i] - 1; + } + + return idx[0] + aGrid->mDivision[0] * (idx[1] + aGrid->mDivision[1] * idx[2]); +} + +//----------------------------------------------------------------------------- +// _ctmGridIdxToPoint() - Convert a grid index to a point (the min x/y/z for +// the given grid box). +//----------------------------------------------------------------------------- +static void _ctmGridIdxToPoint(_CTMgrid * aGrid, CTMuint aIdx, CTMfloat * aPoint) +{ + CTMuint gridIdx[3], zdiv, ydiv, i; + + zdiv = aGrid->mDivision[0] * aGrid->mDivision[1]; + ydiv = aGrid->mDivision[0]; + + gridIdx[2] = aIdx / zdiv; + aIdx -= gridIdx[2] * zdiv; + gridIdx[1] = aIdx / ydiv; + aIdx -= gridIdx[1] * ydiv; + gridIdx[0] = aIdx; + + for(i = 0; i < 3; ++ i) + aPoint[i] = gridIdx[i] * aGrid->mSize[i] + aGrid->mMin[i]; +} + +//----------------------------------------------------------------------------- +// _compareVertex() - Comparator for the vertex sorting. +//----------------------------------------------------------------------------- +static int _compareVertex(const void * elem1, const void * elem2) +{ + _CTMsortvertex * v1 = (_CTMsortvertex *) elem1; + _CTMsortvertex * v2 = (_CTMsortvertex *) elem2; + if(v1->mGridIndex != v2->mGridIndex) + return v1->mGridIndex - v2->mGridIndex; + else if(v1->x < v2->x) + return -1; + else if(v1->x > v2->x) + return 1; + else + return 0; +} + +//----------------------------------------------------------------------------- +// _ctmSortVertices() - Setup the vertex array. Assign each vertex to a grid +// box, and sort all vertices. +//----------------------------------------------------------------------------- +static void _ctmSortVertices(_CTMcontext * self, _CTMsortvertex * aSortVertices, + _CTMgrid * aGrid) +{ + CTMuint i; + + // Prepare sort vertex array + for(i = 0; i < self->mVertexCount; ++ i) + { + // Store vertex properties in the sort vertex array + aSortVertices[i].x = self->mVertices[i * 3]; + aSortVertices[i].mGridIndex = _ctmPointToGridIdx(aGrid, &self->mVertices[i * 3]); + aSortVertices[i].mOriginalIndex = i; + } + + // Sort vertices. The elements are first sorted by their grid indices, and + // scondly by their x coordinates. + qsort((void *) aSortVertices, self->mVertexCount, sizeof(_CTMsortvertex), _compareVertex); +} + +//----------------------------------------------------------------------------- +// _ctmReIndexIndices() - Re-index all indices, based on the sorted vertices. +//----------------------------------------------------------------------------- +static int _ctmReIndexIndices(_CTMcontext * self, _CTMsortvertex * aSortVertices, + CTMuint * aIndices) +{ + CTMuint i, * indexLUT; + + // Create temporary lookup-array, O(n) + indexLUT = (CTMuint *) malloc(sizeof(CTMuint) * self->mVertexCount); + if(!indexLUT) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + for(i = 0; i < self->mVertexCount; ++ i) + indexLUT[aSortVertices[i].mOriginalIndex] = i; + + // Convert old indices to new indices, O(n) + for(i = 0; i < self->mTriangleCount * 3; ++ i) + aIndices[i] = indexLUT[self->mIndices[i]]; + + // Free temporary lookup-array + free((void *) indexLUT); + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// _compareTriangle() - Comparator for the triangle sorting. +//----------------------------------------------------------------------------- +static int _compareTriangle(const void * elem1, const void * elem2) +{ + CTMuint * tri1 = (CTMuint *) elem1; + CTMuint * tri2 = (CTMuint *) elem2; + if(tri1[0] != tri2[0]) + return tri1[0] - tri2[0]; + else + return tri1[1] - tri2[1]; +} + +//----------------------------------------------------------------------------- +// _ctmReArrangeTriangles() - Re-arrange all triangles for optimal +// compression. +//----------------------------------------------------------------------------- +static void _ctmReArrangeTriangles(_CTMcontext * self, CTMuint * aIndices) +{ + CTMuint * tri, tmp, i; + + // Step 1: Make sure that the first index of each triangle is the smallest + // one (rotate triangle nodes if necessary) + for(i = 0; i < self->mTriangleCount; ++ i) + { + tri = &aIndices[i * 3]; + if((tri[1] < tri[0]) && (tri[1] < tri[2])) + { + tmp = tri[0]; + tri[0] = tri[1]; + tri[1] = tri[2]; + tri[2] = tmp; + } + else if((tri[2] < tri[0]) && (tri[2] < tri[1])) + { + tmp = tri[0]; + tri[0] = tri[2]; + tri[2] = tri[1]; + tri[1] = tmp; + } + } + + // Step 2: Sort the triangles based on the first triangle index + qsort((void *) aIndices, self->mTriangleCount, sizeof(CTMuint) * 3, _compareTriangle); +} + +//----------------------------------------------------------------------------- +// _ctmMakeIndexDeltas() - Calculate various forms of derivatives in order to +// reduce data entropy. +//----------------------------------------------------------------------------- +static void _ctmMakeIndexDeltas(_CTMcontext * self, CTMuint * aIndices) +{ + CTMint i; + for(i = self->mTriangleCount - 1; i >= 0; -- i) + { + // Step 1: Calculate delta from second triangle index to the previous + // second triangle index, if the previous triangle shares the same first + // index, otherwise calculate the delta to the first triangle index + if((i >= 1) && (aIndices[i * 3] == aIndices[(i - 1) * 3])) + aIndices[i * 3 + 1] -= aIndices[(i - 1) * 3 + 1]; + else + aIndices[i * 3 + 1] -= aIndices[i * 3]; + + // Step 2: Calculate delta from third triangle index to the first triangle + // index + aIndices[i * 3 + 2] -= aIndices[i * 3]; + + // Step 3: Calculate derivative of the first triangle index + if(i >= 1) + aIndices[i * 3] -= aIndices[(i - 1) * 3]; + } +} + +//----------------------------------------------------------------------------- +// _ctmRestoreIndices() - Restore original indices (inverse derivative +// operation). +//----------------------------------------------------------------------------- +static void _ctmRestoreIndices(_CTMcontext * self, CTMuint * aIndices) +{ + CTMuint i; + + for(i = 0; i < self->mTriangleCount; ++ i) + { + // Step 1: Reverse derivative of the first triangle index + if(i >= 1) + aIndices[i * 3] += aIndices[(i - 1) * 3]; + + // Step 2: Reverse delta from third triangle index to the first triangle + // index + aIndices[i * 3 + 2] += aIndices[i * 3]; + + // Step 3: Reverse delta from second triangle index to the previous + // second triangle index, if the previous triangle shares the same first + // index, otherwise reverse the delta to the first triangle index + if((i >= 1) && (aIndices[i * 3] == aIndices[(i - 1) * 3])) + aIndices[i * 3 + 1] += aIndices[(i - 1) * 3 + 1]; + else + aIndices[i * 3 + 1] += aIndices[i * 3]; + } +} + +//----------------------------------------------------------------------------- +// _ctmMakeVertexDeltas() - Calculate various forms of derivatives in order to +// reduce data entropy. +//----------------------------------------------------------------------------- +static void _ctmMakeVertexDeltas(_CTMcontext * self, CTMint * aIntVertices, + _CTMsortvertex * aSortVertices, _CTMgrid * aGrid) +{ + CTMuint i, gridIdx, prevGridIndex, oldIdx; + CTMfloat gridOrigin[3], scale; + CTMint deltaX, prevDeltaX; + + // Vertex scaling factor + scale = 1.0f / self->mVertexPrecision; + + prevGridIndex = 0x7fffffff; + prevDeltaX = 0; + for(i = 0; i < self->mVertexCount; ++ i) + { + // Get grid box origin + gridIdx = aSortVertices[i].mGridIndex; + _ctmGridIdxToPoint(aGrid, gridIdx, gridOrigin); + + // Get old vertex coordinate index (before vertex sorting) + oldIdx = aSortVertices[i].mOriginalIndex; + + // Store delta to the grid box origin in the integer vertex array. For the + // X axis (which is sorted) we also do the delta to the previous coordinate + // in the box. + deltaX = (CTMint) floorf(scale * (self->mVertices[oldIdx * 3] - gridOrigin[0]) + 0.5f); + if(gridIdx == prevGridIndex) + aIntVertices[i * 3] = deltaX - prevDeltaX; + else + aIntVertices[i * 3] = deltaX; + aIntVertices[i * 3 + 1] = (CTMint) floorf(scale * (self->mVertices[oldIdx * 3 + 1] - gridOrigin[1]) + 0.5f); + aIntVertices[i * 3 + 2] = (CTMint) floorf(scale * (self->mVertices[oldIdx * 3 + 2] - gridOrigin[2]) + 0.5f); + + prevGridIndex = gridIdx; + prevDeltaX = deltaX; + } +} + +//----------------------------------------------------------------------------- +// _ctmRestoreVertices() - Calculate inverse derivatives of the vertices. +//----------------------------------------------------------------------------- +static void _ctmRestoreVertices(_CTMcontext * self, CTMint * aIntVertices, + CTMuint * aGridIndices, _CTMgrid * aGrid, CTMfloat * aVertices) +{ + CTMuint i, gridIdx, prevGridIndex; + CTMfloat gridOrigin[3], scale; + CTMint deltaX, prevDeltaX; + + scale = self->mVertexPrecision; + + prevGridIndex = 0x7fffffff; + prevDeltaX = 0; + for(i = 0; i < self->mVertexCount; ++ i) + { + // Get grid box origin + gridIdx = aGridIndices[i]; + _ctmGridIdxToPoint(aGrid, gridIdx, gridOrigin); + + // Restore original point + deltaX = aIntVertices[i * 3]; + if(gridIdx == prevGridIndex) + deltaX += prevDeltaX; + aVertices[i * 3] = scale * deltaX + gridOrigin[0]; + aVertices[i * 3 + 1] = scale * aIntVertices[i * 3 + 1] + gridOrigin[1]; + aVertices[i * 3 + 2] = scale * aIntVertices[i * 3 + 2] + gridOrigin[2]; + + prevGridIndex = gridIdx; + prevDeltaX = deltaX; + } +} + +//----------------------------------------------------------------------------- +// _ctmCalcSmoothNormals() - Calculate the smooth normals for a given mesh. +// These are used as the nominal normals for normal deltas & reconstruction. +//----------------------------------------------------------------------------- +static void _ctmCalcSmoothNormals(_CTMcontext * self, CTMfloat * aVertices, + CTMuint * aIndices, CTMfloat * aSmoothNormals) +{ + CTMuint i, j, k, tri[3]; + CTMfloat len; + CTMfloat v1[3], v2[3], n[3]; + + // Clear smooth normals array + for(i = 0; i < 3 * self->mVertexCount; ++ i) + aSmoothNormals[i] = 0.0f; + + // Calculate sums of all neigbouring triangle normals for each vertex + for(i = 0; i < self->mTriangleCount; ++ i) + { + // Get triangle corner indices + for(j = 0; j < 3; ++ j) + tri[j] = aIndices[i * 3 + j]; + + // Calculate the normalized cross product of two triangle edges (i.e. the + // flat triangle normal) + for(j = 0; j < 3; ++ j) + { + v1[j] = aVertices[tri[1] * 3 + j] - aVertices[tri[0] * 3 + j]; + v2[j] = aVertices[tri[2] * 3 + j] - aVertices[tri[0] * 3 + j]; + } + n[0] = v1[1] * v2[2] - v1[2] * v2[1]; + n[1] = v1[2] * v2[0] - v1[0] * v2[2]; + n[2] = v1[0] * v2[1] - v1[1] * v2[0]; + len = sqrtf(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]); + if(len > 1e-10f) + len = 1.0f / len; + else + len = 1.0f; + for(j = 0; j < 3; ++ j) + n[j] *= len; + + // Add the flat normal to all three triangle vertices + for(k = 0; k < 3; ++ k) + for(j = 0; j < 3; ++ j) + aSmoothNormals[tri[k] * 3 + j] += n[j]; + } + + // Normalize the normal sums, which gives the unit length smooth normals + for(i = 0; i < self->mVertexCount; ++ i) + { + len = sqrtf(aSmoothNormals[i * 3] * aSmoothNormals[i * 3] + + aSmoothNormals[i * 3 + 1] * aSmoothNormals[i * 3 + 1] + + aSmoothNormals[i * 3 + 2] * aSmoothNormals[i * 3 + 2]); + if(len > 1e-10f) + len = 1.0f / len; + else + len = 1.0f; + for(j = 0; j < 3; ++ j) + aSmoothNormals[i * 3 + j] *= len; + } +} + +//----------------------------------------------------------------------------- +// _ctmMakeNormalCoordSys() - Create an ortho-normalized coordinate system +// where the Z-axis is aligned with the given normal. +// Note 1: This function is central to how the compressed normal data is +// interpreted, and it can not be changed (mathematically) without making the +// coder/decoder incompatible with other versions of the library! +// Note 2: Since we do this for every single normal, this routine needs to be +// fast. The current implementation uses: 12 MUL, 1 DIV, 1 SQRT, ~6 ADD. +//----------------------------------------------------------------------------- +static void _ctmMakeNormalCoordSys(CTMfloat * aNormal, CTMfloat * aBasisAxes) +{ + CTMfloat len, * x, * y, * z; + CTMuint i; + + // Pointers to the basis axes (aBasisAxes is a 3x3 matrix) + x = aBasisAxes; + y = &aBasisAxes[3]; + z = &aBasisAxes[6]; + + // Z = normal (must be unit length!) + for(i = 0; i < 3; ++ i) + z[i] = aNormal[i]; + + // Calculate a vector that is guaranteed to be orthogonal to the normal, non- + // zero, and a continuous function of the normal (no discrete jumps): + // X = (0,0,1) x normal + (1,0,0) x normal + x[0] = -aNormal[1]; + x[1] = aNormal[0] - aNormal[2]; + x[2] = aNormal[1]; + + // Normalize the new X axis (note: |x[2]| = |x[0]|) + len = sqrtf(2.0 * x[0] * x[0] + x[1] * x[1]); + if(len > 1.0e-20f) + { + len = 1.0f / len; + x[0] *= len; + x[1] *= len; + x[2] *= len; + } + + // Let Y = Z x X (no normalization needed, since |Z| = |X| = 1) + y[0] = z[1] * x[2] - z[2] * x[1]; + y[1] = z[2] * x[0] - z[0] * x[2]; + y[2] = z[0] * x[1] - z[1] * x[0]; +} + +//----------------------------------------------------------------------------- +// _ctmMakeNormalDeltas() - Convert the normals to a new coordinate system: +// magnitude, phi, theta (relative to predicted smooth normals). +//----------------------------------------------------------------------------- +static CTMint _ctmMakeNormalDeltas(_CTMcontext * self, CTMint * aIntNormals, + CTMfloat * aVertices, CTMuint * aIndices, _CTMsortvertex * aSortVertices) +{ + CTMuint i, j, oldIdx, intPhi; + CTMfloat magn, phi, theta, scale, thetaScale; + CTMfloat * smoothNormals, n[3], n2[3], basisAxes[9]; + + // Allocate temporary memory for the nominal vertex normals + smoothNormals = (CTMfloat *) malloc(3 * sizeof(CTMfloat) * self->mVertexCount); + if(!smoothNormals) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Calculate smooth normals (Note: aVertices and aIndices use the sorted + // index space, so smoothNormals will too) + _ctmCalcSmoothNormals(self, aVertices, aIndices, smoothNormals); + + // Normal scaling factor + scale = 1.0f / self->mNormalPrecision; + + for(i = 0; i < self->mVertexCount; ++ i) + { + // Get old normal index (before vertex sorting) + oldIdx = aSortVertices[i].mOriginalIndex; + + // Calculate normal magnitude (should always be 1.0 for unit length normals) + magn = sqrtf(self->mNormals[oldIdx * 3] * self->mNormals[oldIdx * 3] + + self->mNormals[oldIdx * 3 + 1] * self->mNormals[oldIdx * 3 + 1] + + self->mNormals[oldIdx * 3 + 2] * self->mNormals[oldIdx * 3 + 2]); + if(magn < 1e-10f) + magn = 1.0f; + + // Invert magnitude if the normal is negative compared to the predicted + // smooth normal + if((smoothNormals[i * 3] * self->mNormals[oldIdx * 3] + + smoothNormals[i * 3 + 1] * self->mNormals[oldIdx * 3 + 1] + + smoothNormals[i * 3 + 2] * self->mNormals[oldIdx * 3 + 2]) < 0.0f) + magn = -magn; + + // Store the magnitude in the first element of the three normal elements + aIntNormals[i * 3] = (CTMint) floorf(scale * magn + 0.5f); + + // Normalize the normal (1 / magn) - and flip it if magn < 0 + magn = 1.0f / magn; + for(j = 0; j < 3; ++ j) + n[j] = self->mNormals[oldIdx * 3 + j] * magn; + + // Convert the normal to angular representation (phi, theta) in a coordinate + // system where the nominal (smooth) normal is the Z-axis + _ctmMakeNormalCoordSys(&smoothNormals[i * 3], basisAxes); + for(j = 0; j < 3; ++ j) + n2[j] = basisAxes[j * 3] * n[0] + + basisAxes[j * 3 + 1] * n[1] + + basisAxes[j * 3 + 2] * n[2]; + if(n2[2] >= 1.0f) + phi = 0.0f; + else + phi = acosf(n2[2]); + theta = atan2f(n2[1], n2[0]); + + // Round phi and theta (spherical coordinates) to integers. Note: We let the + // theta resolution vary with the x/y circumference (roughly phi). + intPhi = (CTMint) floorf(phi * (scale / (0.5f * PI)) + 0.5f); + if(intPhi == 0) + thetaScale = 0.0f; + else if(intPhi <= 4) + thetaScale = 2.0f / PI; + else + thetaScale = ((CTMfloat) intPhi) / (2.0f * PI); + aIntNormals[i * 3 + 1] = intPhi; + aIntNormals[i * 3 + 2] = (CTMint) floorf((theta + PI) * thetaScale + 0.5f); + } + + // Free temporary resources + free(smoothNormals); + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// _ctmRestoreNormals() - Convert the normals back to cartesian coordinates. +//----------------------------------------------------------------------------- +static CTMint _ctmRestoreNormals(_CTMcontext * self, CTMint * aIntNormals) +{ + CTMuint i, j, intPhi; + CTMfloat magn, phi, theta, scale, thetaScale; + CTMfloat * smoothNormals, n[3], n2[3], basisAxes[9]; + + // Allocate temporary memory for the nominal vertex normals + smoothNormals = (CTMfloat *) malloc(3 * sizeof(CTMfloat) * self->mVertexCount); + if(!smoothNormals) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Calculate smooth normals (nominal normals) + _ctmCalcSmoothNormals(self, self->mVertices, self->mIndices, smoothNormals); + + // Normal scaling factor + scale = self->mNormalPrecision; + + for(i = 0; i < self->mVertexCount; ++ i) + { + // Get the normal magnitude from the first of the three normal elements + magn = aIntNormals[i * 3] * scale; + + // Get phi and theta (spherical coordinates, relative to the smooth normal). + intPhi = aIntNormals[i * 3 + 1]; + phi = intPhi * (0.5f * PI) * scale; + if(intPhi == 0) + thetaScale = 0.0f; + else if(intPhi <= 4) + thetaScale = PI / 2.0f; + else + thetaScale = (2.0f * PI) / ((CTMfloat) intPhi); + theta = aIntNormals[i * 3 + 2] * thetaScale - PI; + + // Convert the normal from the angular representation (phi, theta) back to + // cartesian coordinates + n2[0] = sinf(phi) * cosf(theta); + n2[1] = sinf(phi) * sinf(theta); + n2[2] = cosf(phi); + _ctmMakeNormalCoordSys(&smoothNormals[i * 3], basisAxes); + for(j = 0; j < 3; ++ j) + n[j] = basisAxes[j] * n2[0] + + basisAxes[3 + j] * n2[1] + + basisAxes[6 + j] * n2[2]; + + // Apply normal magnitude, and output to the normals array + for(j = 0; j < 3; ++ j) + self->mNormals[i * 3 + j] = n[j] * magn; + } + + // Free temporary resources + free(smoothNormals); + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// _ctmMakeUVCoordDeltas() - Calculate various forms of derivatives in order +// to reduce data entropy. +//----------------------------------------------------------------------------- +static void _ctmMakeUVCoordDeltas(_CTMcontext * self, _CTMfloatmap * aMap, + CTMint * aIntUVCoords, _CTMsortvertex * aSortVertices) +{ + CTMuint i, oldIdx; + CTMint u, v, prevU, prevV; + CTMfloat scale; + + // UV coordinate scaling factor + scale = 1.0f / aMap->mPrecision; + + prevU = prevV = 0; + for(i = 0; i < self->mVertexCount; ++ i) + { + // Get old UV coordinate index (before vertex sorting) + oldIdx = aSortVertices[i].mOriginalIndex; + + // Convert to fixed point + u = (CTMint) floorf(scale * aMap->mValues[oldIdx * 2] + 0.5f); + v = (CTMint) floorf(scale * aMap->mValues[oldIdx * 2 + 1] + 0.5f); + + // Calculate delta and store it in the converted array. NOTE: Here we rely + // on the fact that vertices are sorted, and usually close to each other, + // which means that UV coordinates should also be close to each other... + aIntUVCoords[i * 2] = u - prevU; + aIntUVCoords[i * 2 + 1] = v - prevV; + + prevU = u; + prevV = v; + } +} + +//----------------------------------------------------------------------------- +// _ctmRestoreUVCoords() - Calculate inverse derivatives of the UV +// coordinates. +//----------------------------------------------------------------------------- +static void _ctmRestoreUVCoords(_CTMcontext * self, _CTMfloatmap * aMap, + CTMint * aIntUVCoords) +{ + CTMuint i; + CTMint u, v, prevU, prevV; + CTMfloat scale; + + // UV coordinate scaling factor + scale = aMap->mPrecision; + + prevU = prevV = 0; + for(i = 0; i < self->mVertexCount; ++ i) + { + // Calculate inverse delta + u = aIntUVCoords[i * 2] + prevU; + v = aIntUVCoords[i * 2 + 1] + prevV; + + // Convert to floating point + aMap->mValues[i * 2] = (CTMfloat) u * scale; + aMap->mValues[i * 2 + 1] = (CTMfloat) v * scale; + + prevU = u; + prevV = v; + } +} + +//----------------------------------------------------------------------------- +// _ctmMakeAttribDeltas() - Calculate various forms of derivatives in order +// to reduce data entropy. +//----------------------------------------------------------------------------- +static void _ctmMakeAttribDeltas(_CTMcontext * self, _CTMfloatmap * aMap, + CTMint * aIntAttribs, _CTMsortvertex * aSortVertices) +{ + CTMuint i, j, oldIdx; + CTMint value[4], prev[4]; + CTMfloat scale; + + // Attribute scaling factor + scale = 1.0f / aMap->mPrecision; + + for(j = 0; j < 4; ++ j) + prev[j] = 0; + + for(i = 0; i < self->mVertexCount; ++ i) + { + // Get old attribute index (before vertex sorting) + oldIdx = aSortVertices[i].mOriginalIndex; + + // Convert to fixed point, and calculate delta and store it in the converted + // array. NOTE: Here we rely on the fact that vertices are sorted, and + // usually close to each other, which means that attributes should also + // be close to each other (and we assume that they somehow vary slowly with + // the geometry)... + for(j = 0; j < 4; ++ j) + { + value[j] = (CTMint) floorf(scale * aMap->mValues[oldIdx * 4 + j] + 0.5f); + aIntAttribs[i * 4 + j] = value[j] - prev[j]; + prev[j] = value[j]; + } + } +} + +//----------------------------------------------------------------------------- +// _ctmRestoreAttribs() - Calculate inverse derivatives of the vertex +// attributes. +//----------------------------------------------------------------------------- +static void _ctmRestoreAttribs(_CTMcontext * self, _CTMfloatmap * aMap, + CTMint * aIntAttribs) +{ + CTMuint i, j; + CTMint value[4], prev[4]; + CTMfloat scale; + + // Attribute scaling factor + scale = aMap->mPrecision; + + for(j = 0; j < 4; ++ j) + prev[j] = 0; + + for(i = 0; i < self->mVertexCount; ++ i) + { + // Calculate inverse delta, and convert to floating point + for(j = 0; j < 4; ++ j) + { + value[j] = aIntAttribs[i * 4 + j] + prev[j]; + aMap->mValues[i * 4 + j] = (CTMfloat) value[j] * scale; + prev[j] = value[j]; + } + } +} + +//----------------------------------------------------------------------------- +// _ctmCompressMesh_MG2() - Compress the mesh that is stored in the CTM +// context, and write it the the output stream in the CTM context. +//----------------------------------------------------------------------------- +int _ctmCompressMesh_MG2(_CTMcontext * self) +{ + _CTMgrid grid; + _CTMsortvertex * sortVertices; + _CTMfloatmap * map; + CTMuint * indices, * deltaIndices, * gridIndices; + CTMint * intVertices, * intNormals, * intUVCoords, * intAttribs; + CTMfloat * restoredVertices; + CTMuint i; + +#ifdef __DEBUG_ + printf("COMPRESSION METHOD: MG2\n"); +#endif + + // Setup 3D space subdivision grid + _ctmSetupGrid(self, &grid); + + // Write MG2-specific header information to the stream + _ctmStreamWrite(self, (void *) "MG2H", 4); + _ctmStreamWriteFLOAT(self, self->mVertexPrecision); + _ctmStreamWriteFLOAT(self, self->mNormalPrecision); + _ctmStreamWriteFLOAT(self, grid.mMin[0]); + _ctmStreamWriteFLOAT(self, grid.mMin[1]); + _ctmStreamWriteFLOAT(self, grid.mMin[2]); + _ctmStreamWriteFLOAT(self, grid.mMax[0]); + _ctmStreamWriteFLOAT(self, grid.mMax[1]); + _ctmStreamWriteFLOAT(self, grid.mMax[2]); + _ctmStreamWriteUINT(self, grid.mDivision[0]); + _ctmStreamWriteUINT(self, grid.mDivision[1]); + _ctmStreamWriteUINT(self, grid.mDivision[2]); + + // Prepare (sort) vertices + sortVertices = (_CTMsortvertex *) malloc(sizeof(_CTMsortvertex) * self->mVertexCount); + if(!sortVertices) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + _ctmSortVertices(self, sortVertices, &grid); + + // Convert vertices to integers and calculate vertex deltas (entropy-reduction) + intVertices = (CTMint *) malloc(sizeof(CTMint) * 3 * self->mVertexCount); + if(!intVertices) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) sortVertices); + return CTM_FALSE; + } + _ctmMakeVertexDeltas(self, intVertices, sortVertices, &grid); + + // Write vertices +#ifdef __DEBUG_ + printf("Vertices: "); +#endif + _ctmStreamWrite(self, (void *) "VERT", 4); + if(!_ctmStreamWritePackedInts(self, intVertices, self->mVertexCount, 3, CTM_FALSE)) + { + free((void *) intVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + + // Prepare grid indices (deltas) + gridIndices = (CTMuint *) malloc(sizeof(CTMuint) * self->mVertexCount); + if(!gridIndices) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) intVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + gridIndices[0] = sortVertices[0].mGridIndex; + for(i = 1; i < self->mVertexCount; ++ i) + gridIndices[i] = sortVertices[i].mGridIndex - sortVertices[i - 1].mGridIndex; + + // Write grid indices +#ifdef __DEBUG_ + printf("Grid indices: "); +#endif + _ctmStreamWrite(self, (void *) "GIDX", 4); + if(!_ctmStreamWritePackedInts(self, (CTMint *) gridIndices, self->mVertexCount, 1, CTM_FALSE)) + { + free((void *) gridIndices); + free((void *) intVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + + // Calculate the result of the compressed -> decompressed vertices, in order + // to use the same vertex data for calculating nominal normals as the + // decompression routine (i.e. compensate for the vertex error when + // calculating the normals) + restoredVertices = (CTMfloat *) malloc(sizeof(CTMfloat) * 3 * self->mVertexCount); + if(!restoredVertices) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) gridIndices); + free((void *) intVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + for(i = 1; i < self->mVertexCount; ++ i) + gridIndices[i] += gridIndices[i - 1]; + _ctmRestoreVertices(self, intVertices, gridIndices, &grid, restoredVertices); + + // Free temporary resources + free((void *) gridIndices); + free((void *) intVertices); + + // Perpare (sort) indices + indices = (CTMuint *) malloc(sizeof(CTMuint) * self->mTriangleCount * 3); + if(!indices) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) restoredVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + if(!_ctmReIndexIndices(self, sortVertices, indices)) + { + free((void *) indices); + free((void *) restoredVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + _ctmReArrangeTriangles(self, indices); + + // Calculate index deltas (entropy-reduction) + deltaIndices = (CTMuint *) malloc(sizeof(CTMuint) * self->mTriangleCount * 3); + if(!indices) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) indices); + free((void *) restoredVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + for(i = 0; i < self->mTriangleCount * 3; ++ i) + deltaIndices[i] = indices[i]; + _ctmMakeIndexDeltas(self, deltaIndices); + + // Write triangle indices +#ifdef __DEBUG_ + printf("Indices: "); +#endif + _ctmStreamWrite(self, (void *) "INDX", 4); + if(!_ctmStreamWritePackedInts(self, (CTMint *) deltaIndices, self->mTriangleCount, 3, CTM_FALSE)) + { + free((void *) deltaIndices); + free((void *) indices); + free((void *) restoredVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + + // Free temporary data for the indices + free((void *) deltaIndices); + + if(self->mNormals) + { + // Convert normals to integers and calculate deltas (entropy-reduction) + intNormals = (CTMint *) malloc(sizeof(CTMint) * 3 * self->mVertexCount); + if(!intNormals) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) indices); + free((void *) restoredVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + if(!_ctmMakeNormalDeltas(self, intNormals, restoredVertices, indices, sortVertices)) + { + free((void *) indices); + free((void *) intNormals); + free((void *) restoredVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + + // Write normals +#ifdef __DEBUG_ + printf("Normals: "); +#endif + _ctmStreamWrite(self, (void *) "NORM", 4); + if(!_ctmStreamWritePackedInts(self, intNormals, self->mVertexCount, 3, CTM_FALSE)) + { + free((void *) indices); + free((void *) intNormals); + free((void *) restoredVertices); + free((void *) sortVertices); + return CTM_FALSE; + } + + // Free temporary normal data + free((void *) intNormals); + } + + // Free restored indices and vertices + free((void *) indices); + free((void *) restoredVertices); + + // Write UV maps + map = self->mUVMaps; + while(map) + { + // Convert UV coordinates to integers and calculate deltas (entropy-reduction) + intUVCoords = (CTMint *) malloc(sizeof(CTMint) * 2 * self->mVertexCount); + if(!intUVCoords) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) sortVertices); + return CTM_FALSE; + } + _ctmMakeUVCoordDeltas(self, map, intUVCoords, sortVertices); + + // Write UV coordinates +#ifdef __DEBUG_ + printf("Texture coordinates (%s): ", map->mName ? map->mName : "no name"); +#endif + _ctmStreamWrite(self, (void *) "TEXC", 4); + _ctmStreamWriteSTRING(self, map->mName); + _ctmStreamWriteSTRING(self, map->mFileName); + _ctmStreamWriteFLOAT(self, map->mPrecision); + if(!_ctmStreamWritePackedInts(self, intUVCoords, self->mVertexCount, 2, CTM_TRUE)) + { + free((void *) intUVCoords); + free((void *) sortVertices); + return CTM_FALSE; + } + + // Free temporary UV coordinate data + free((void *) intUVCoords); + + map = map->mNext; + } + + // Write vertex attribute maps + map = self->mAttribMaps; + while(map) + { + // Convert vertex attributes to integers and calculate deltas (entropy-reduction) + intAttribs = (CTMint *) malloc(sizeof(CTMint) * 4 * self->mVertexCount); + if(!intAttribs) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) sortVertices); + return CTM_FALSE; + } + _ctmMakeAttribDeltas(self, map, intAttribs, sortVertices); + + // Write vertex attributes +#ifdef __DEBUG_ + printf("Vertex attributes (%s): ", map->mName ? map->mName : "no name"); +#endif + _ctmStreamWrite(self, (void *) "ATTR", 4); + _ctmStreamWriteSTRING(self, map->mName); + _ctmStreamWriteFLOAT(self, map->mPrecision); + if(!_ctmStreamWritePackedInts(self, intAttribs, self->mVertexCount, 4, CTM_TRUE)) + { + free((void *) intAttribs); + free((void *) sortVertices); + return CTM_FALSE; + } + + // Free temporary vertex attribute data + free((void *) intAttribs); + + map = map->mNext; + } + + // Free temporary data + free((void *) sortVertices); + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// _ctmUncompressMesh_MG2() - Uncmpress the mesh from the input stream in the +// CTM context, and store the resulting mesh in the CTM context. +//----------------------------------------------------------------------------- +int _ctmUncompressMesh_MG2(_CTMcontext * self) +{ + CTMuint * gridIndices, i; + CTMint * intVertices, * intNormals, * intUVCoords, * intAttribs; + _CTMfloatmap * map; + _CTMgrid grid; + + // Read MG2-specific header information from the stream + if(_ctmStreamReadUINT(self) != FOURCC("MG2H")) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + self->mVertexPrecision = _ctmStreamReadFLOAT(self); + if(self->mVertexPrecision <= 0.0f) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + self->mNormalPrecision = _ctmStreamReadFLOAT(self); + if(self->mNormalPrecision <= 0.0f) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + grid.mMin[0] = _ctmStreamReadFLOAT(self); + grid.mMin[1] = _ctmStreamReadFLOAT(self); + grid.mMin[2] = _ctmStreamReadFLOAT(self); + grid.mMax[0] = _ctmStreamReadFLOAT(self); + grid.mMax[1] = _ctmStreamReadFLOAT(self); + grid.mMax[2] = _ctmStreamReadFLOAT(self); + if((grid.mMax[0] < grid.mMin[0]) || + (grid.mMax[1] < grid.mMin[1]) || + (grid.mMax[2] < grid.mMin[2])) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + grid.mDivision[0] = _ctmStreamReadUINT(self); + grid.mDivision[1] = _ctmStreamReadUINT(self); + grid.mDivision[2] = _ctmStreamReadUINT(self); + if((grid.mDivision[0] < 1) || (grid.mDivision[1] < 1) || (grid.mDivision[2] < 1)) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + + // Initialize 3D space subdivision grid + for(i = 0; i < 3; ++ i) + grid.mSize[i] = (grid.mMax[i] - grid.mMin[i]) / grid.mDivision[i]; + + // Read vertices + if(_ctmStreamReadUINT(self) != FOURCC("VERT")) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + intVertices = (CTMint *) malloc(sizeof(CTMint) * self->mVertexCount * 3); + if(!intVertices) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + if(!_ctmStreamReadPackedInts(self, intVertices, self->mVertexCount, 3, CTM_FALSE)) + { + free((void *) intVertices); + return CTM_FALSE; + } + + // Read grid indices + if(_ctmStreamReadUINT(self) != FOURCC("GIDX")) + { + free((void *) intVertices); + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + gridIndices = (CTMuint *) malloc(sizeof(CTMuint) * self->mVertexCount); + if(!gridIndices) + { + self->mError = CTM_OUT_OF_MEMORY; + free((void *) intVertices); + return CTM_FALSE; + } + if(!_ctmStreamReadPackedInts(self, (CTMint *) gridIndices, self->mVertexCount, 1, CTM_FALSE)) + { + free((void *) gridIndices); + free((void *) intVertices); + return CTM_FALSE; + } + + // Restore grid indices (deltas) + for(i = 1; i < self->mVertexCount; ++ i) + gridIndices[i] += gridIndices[i - 1]; + + // Restore vertices + _ctmRestoreVertices(self, intVertices, gridIndices, &grid, self->mVertices); + + // Free temporary resources + free((void *) gridIndices); + free((void *) intVertices); + + // Read triangle indices + if(_ctmStreamReadUINT(self) != FOURCC("INDX")) + { + self->mError = CTM_BAD_FORMAT; + return CTM_FALSE; + } + if(!_ctmStreamReadPackedInts(self, (CTMint *) self->mIndices, self->mTriangleCount, 3, CTM_FALSE)) + return CTM_FALSE; + + // Restore indices + _ctmRestoreIndices(self, self->mIndices); + + // Check that all indices are within range + for(i = 0; i < (self->mTriangleCount * 3); ++ i) + { + if(self->mIndices[i] >= self->mVertexCount) + { + self->mError = CTM_INVALID_MESH; + return CTM_FALSE; + } + } + + // Read normals + if(self->mNormals) + { + intNormals = (CTMint *) malloc(sizeof(CTMint) * self->mVertexCount * 3); + if(!intNormals) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + if(_ctmStreamReadUINT(self) != FOURCC("NORM")) + { + self->mError = CTM_BAD_FORMAT; + free((void *) intNormals); + return CTM_FALSE; + } + if(!_ctmStreamReadPackedInts(self, intNormals, self->mVertexCount, 3, CTM_FALSE)) + { + free((void *) intNormals); + return CTM_FALSE; + } + + // Restore normals + if(!_ctmRestoreNormals(self, intNormals)) + { + free((void *) intNormals); + return CTM_FALSE; + } + + // Free temporary normals data + free((void *) intNormals); + } + + // Read UV maps + map = self->mUVMaps; + while(map) + { + intUVCoords = (CTMint *) malloc(sizeof(CTMint) * self->mVertexCount * 2); + if(!intUVCoords) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + if(_ctmStreamReadUINT(self) != FOURCC("TEXC")) + { + self->mError = CTM_BAD_FORMAT; + free((void *) intUVCoords); + return CTM_FALSE; + } + _ctmStreamReadSTRING(self, &map->mName); + _ctmStreamReadSTRING(self, &map->mFileName); + map->mPrecision = _ctmStreamReadFLOAT(self); + if(map->mPrecision <= 0.0f) + { + self->mError = CTM_BAD_FORMAT; + free((void *) intUVCoords); + return CTM_FALSE; + } + if(!_ctmStreamReadPackedInts(self, intUVCoords, self->mVertexCount, 2, CTM_TRUE)) + { + free((void *) intUVCoords); + return CTM_FALSE; + } + + // Restore UV coordinates + _ctmRestoreUVCoords(self, map, intUVCoords); + + // Free temporary UV coordinate data + free((void *) intUVCoords); + + map = map->mNext; + } + + // Read vertex attribute maps + map = self->mAttribMaps; + while(map) + { + intAttribs = (CTMint *) malloc(sizeof(CTMint) * self->mVertexCount * 4); + if(!intAttribs) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + if(_ctmStreamReadUINT(self) != FOURCC("ATTR")) + { + self->mError = CTM_BAD_FORMAT; + free((void *) intAttribs); + return CTM_FALSE; + } + _ctmStreamReadSTRING(self, &map->mName); + map->mPrecision = _ctmStreamReadFLOAT(self); + if(map->mPrecision <= 0.0f) + { + self->mError = CTM_BAD_FORMAT; + free((void *) intAttribs); + return CTM_FALSE; + } + if(!_ctmStreamReadPackedInts(self, intAttribs, self->mVertexCount, 4, CTM_TRUE)) + { + free((void *) intAttribs); + return CTM_FALSE; + } + + // Restore vertex attributes + _ctmRestoreAttribs(self, map, intAttribs); + + // Free temporary vertex attribute data + free((void *) intAttribs); + + map = map->mNext; + } + + return CTM_TRUE; +} diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/compressRAW.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/compressRAW.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,181 @@ +//----------------------------------------------------------------------------- +// Product: OpenCTM +// File: compressRAW.c +// Description: Implementation of the RAW compression method. +//----------------------------------------------------------------------------- +// Copyright (c) 2009-2010 Marcus Geelnard +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +//----------------------------------------------------------------------------- + +#include "openctm.h" +#include "internal.h" + +#ifdef __DEBUG_ +#include +#endif + + +//----------------------------------------------------------------------------- +// _ctmCompressMesh_RAW() - Compress the mesh that is stored in the CTM +// context using the RAW method, and write it the the output stream in the CTM +// context. +//----------------------------------------------------------------------------- +int _ctmCompressMesh_RAW(_CTMcontext * self) +{ + CTMuint i; + _CTMfloatmap * map; + +#ifdef __DEBUG_ + printf("COMPRESSION METHOD: RAW\n"); +#endif + + // Write triangle indices +#ifdef __DEBUG_ + printf("Inidices: %d bytes\n", (CTMuint)(self->mTriangleCount * 3 * sizeof(CTMuint))); +#endif + _ctmStreamWrite(self, (void *) "INDX", 4); + for(i = 0; i < self->mTriangleCount * 3; ++ i) + _ctmStreamWriteUINT(self, self->mIndices[i]); + + // Write vertices +#ifdef __DEBUG_ + printf("Vertices: %d bytes\n", (CTMuint)(self->mVertexCount * 3 * sizeof(CTMfloat))); +#endif + _ctmStreamWrite(self, (void *) "VERT", 4); + for(i = 0; i < self->mVertexCount * 3; ++ i) + _ctmStreamWriteFLOAT(self, self->mVertices[i]); + + // Write normals + if(self->mNormals) + { +#ifdef __DEBUG_ + printf("Normals: %d bytes\n", (CTMuint)(self->mVertexCount * 3 * sizeof(CTMfloat))); +#endif + _ctmStreamWrite(self, (void *) "NORM", 4); + for(i = 0; i < self->mVertexCount * 3; ++ i) + _ctmStreamWriteFLOAT(self, self->mNormals[i]); + } + + // Write UV maps + map = self->mUVMaps; + while(map) + { +#ifdef __DEBUG_ + printf("UV coordinates (%s): %d bytes\n", map->mName ? map->mName : "no name", (CTMuint)(self->mVertexCount * 2 * sizeof(CTMfloat))); +#endif + _ctmStreamWrite(self, (void *) "TEXC", 4); + _ctmStreamWriteSTRING(self, map->mName); + _ctmStreamWriteSTRING(self, map->mFileName); + for(i = 0; i < self->mVertexCount * 2; ++ i) + _ctmStreamWriteFLOAT(self, map->mValues[i]); + map = map->mNext; + } + + // Write attribute maps + map = self->mAttribMaps; + while(map) + { +#ifdef __DEBUG_ + printf("Vertex attributes (%s): %d bytes\n", map->mName ? map->mName : "no name", (CTMuint)(self->mVertexCount * 4 * sizeof(CTMfloat))); +#endif + _ctmStreamWrite(self, (void *) "ATTR", 4); + _ctmStreamWriteSTRING(self, map->mName); + for(i = 0; i < self->mVertexCount * 4; ++ i) + _ctmStreamWriteFLOAT(self, map->mValues[i]); + map = map->mNext; + } + + return 1; +} + +//----------------------------------------------------------------------------- +// _ctmUncompressMesh_RAW() - Uncmpress the mesh from the input stream in the +// CTM context using the RAW method, and store the resulting mesh in the CTM +// context. +//----------------------------------------------------------------------------- +int _ctmUncompressMesh_RAW(_CTMcontext * self) +{ + CTMuint i; + _CTMfloatmap * map; + + // Read triangle indices + if(_ctmStreamReadUINT(self) != FOURCC("INDX")) + { + self->mError = CTM_BAD_FORMAT; + return 0; + } + for(i = 0; i < self->mTriangleCount * 3; ++ i) + self->mIndices[i] = _ctmStreamReadUINT(self); + + // Read vertices + if(_ctmStreamReadUINT(self) != FOURCC("VERT")) + { + self->mError = CTM_BAD_FORMAT; + return 0; + } + for(i = 0; i < self->mVertexCount * 3; ++ i) + self->mVertices[i] = _ctmStreamReadFLOAT(self); + + // Read normals + if(self->mNormals) + { + if(_ctmStreamReadUINT(self) != FOURCC("NORM")) + { + self->mError = CTM_BAD_FORMAT; + return 0; + } + for(i = 0; i < self->mVertexCount * 3; ++ i) + self->mNormals[i] = _ctmStreamReadFLOAT(self); + } + + // Read UV maps + map = self->mUVMaps; + while(map) + { + if(_ctmStreamReadUINT(self) != FOURCC("TEXC")) + { + self->mError = CTM_BAD_FORMAT; + return 0; + } + _ctmStreamReadSTRING(self, &map->mName); + _ctmStreamReadSTRING(self, &map->mFileName); + for(i = 0; i < self->mVertexCount * 2; ++ i) + map->mValues[i] = _ctmStreamReadFLOAT(self); + map = map->mNext; + } + + // Read attribute maps + map = self->mAttribMaps; + while(map) + { + if(_ctmStreamReadUINT(self) != FOURCC("ATTR")) + { + self->mError = CTM_BAD_FORMAT; + return 0; + } + _ctmStreamReadSTRING(self, &map->mName); + for(i = 0; i < self->mVertexCount * 4; ++ i) + map->mValues[i] = _ctmStreamReadFLOAT(self); + map = map->mNext; + } + + return 1; +} diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/internal.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// Product: OpenCTM +// File: internal.h +// Description: Internal (private) declarations, types and function prototypes. +//----------------------------------------------------------------------------- +// Copyright (c) 2009-2010 Marcus Geelnard +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +//----------------------------------------------------------------------------- + +#ifndef __OPENCTM_INTERNAL_H_ +#define __OPENCTM_INTERNAL_H_ + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +// OpenCTM file format version (v5). +#define _CTM_FORMAT_VERSION 0x00000005 + +// Flags for the Mesh flags field of the file header +#define _CTM_HAS_NORMALS_BIT 0x00000001 + +//----------------------------------------------------------------------------- +// _CTMfloatmap - Internal representation of a floating point based vertex map +// (used for UV maps and attribute maps). +//----------------------------------------------------------------------------- +typedef struct _CTMfloatmap_struct _CTMfloatmap; +struct _CTMfloatmap_struct { + char * mName; // Unique name + char * mFileName; // File name reference (used only for UV maps) + CTMfloat mPrecision; // Precision for this map + CTMfloat * mValues; // Attribute/UV coordinate values (per vertex) + _CTMfloatmap * mNext; // Pointer to the next map in the list (linked list) +}; + +//----------------------------------------------------------------------------- +// _CTMcontext - Internal CTM context structure. +//----------------------------------------------------------------------------- +typedef struct { + // Context mode (import or export) + CTMenum mMode; + + // Vertices + CTMfloat * mVertices; + CTMuint mVertexCount; + + // Indices + CTMuint * mIndices; + CTMuint mTriangleCount; + + // Normals (optional) + CTMfloat * mNormals; + + // Multiple sets of UV coordinate maps (optional) + CTMuint mUVMapCount; + _CTMfloatmap * mUVMaps; + + // Multiple sets of custom vertex attribute maps (optional) + CTMuint mAttribMapCount; + _CTMfloatmap * mAttribMaps; + + // Last error code + CTMenum mError; + + // The selected compression method + CTMenum mMethod; + + // The selected compression level + CTMuint mCompressionLevel; + + // Vertex coordinate precision + CTMfloat mVertexPrecision; + + // Normal precision (angular + magnitude) + CTMfloat mNormalPrecision; + + // File comment + char * mFileComment; + + // Read() function pointer + CTMreadfn mReadFn; + + // Write() function pointer + CTMwritefn mWriteFn; + + // User data (for stream read/write - usually the stream handle) + void * mUserData; +} _CTMcontext; + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- +#define FOURCC(str) (((CTMuint) str[0]) | (((CTMuint) str[1]) << 8) | \ + (((CTMuint) str[2]) << 16) | (((CTMuint) str[3]) << 24)) + +//----------------------------------------------------------------------------- +// Funcion prototypes for stream.c +//----------------------------------------------------------------------------- +CTMuint _ctmStreamRead(_CTMcontext * self, void * aBuf, CTMuint aCount); +CTMuint _ctmStreamWrite(_CTMcontext * self, void * aBuf, CTMuint aCount); +CTMuint _ctmStreamReadUINT(_CTMcontext * self); +void _ctmStreamWriteUINT(_CTMcontext * self, CTMuint aValue); +CTMfloat _ctmStreamReadFLOAT(_CTMcontext * self); +void _ctmStreamWriteFLOAT(_CTMcontext * self, CTMfloat aValue); +void _ctmStreamReadSTRING(_CTMcontext * self, char ** aValue); +void _ctmStreamWriteSTRING(_CTMcontext * self, const char * aValue); +int _ctmStreamReadPackedInts(_CTMcontext * self, CTMint * aData, CTMuint aCount, CTMuint aSize, CTMint aSignedInts); +int _ctmStreamWritePackedInts(_CTMcontext * self, CTMint * aData, CTMuint aCount, CTMuint aSize, CTMint aSignedInts); +int _ctmStreamReadPackedFloats(_CTMcontext * self, CTMfloat * aData, CTMuint aCount, CTMuint aSize); +int _ctmStreamWritePackedFloats(_CTMcontext * self, CTMfloat * aData, CTMuint aCount, CTMuint aSize); + +//----------------------------------------------------------------------------- +// Funcion prototypes for compressRAW.c +//----------------------------------------------------------------------------- +int _ctmCompressMesh_RAW(_CTMcontext * self); +int _ctmUncompressMesh_RAW(_CTMcontext * self); + +//----------------------------------------------------------------------------- +// Funcion prototypes for compressMG1.c +//----------------------------------------------------------------------------- +int _ctmCompressMesh_MG1(_CTMcontext * self); +int _ctmUncompressMesh_MG1(_CTMcontext * self); + +//----------------------------------------------------------------------------- +// Funcion prototypes for compressMG2.c +//----------------------------------------------------------------------------- +int _ctmCompressMesh_MG2(_CTMcontext * self); +int _ctmUncompressMesh_MG2(_CTMcontext * self); + +#endif // __OPENCTM_INTERNAL_H_ diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/Alloc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/Alloc.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,127 @@ +/* Alloc.c -- Memory allocation functions +2008-09-24 +Igor Pavlov +Public domain */ + +#ifdef _WIN32 +#include +#endif +#include + +#include "Alloc.h" + +/* #define _SZ_ALLOC_DEBUG */ + +/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ +#ifdef _SZ_ALLOC_DEBUG +#include +int g_allocCount = 0; +int g_allocCountMid = 0; +int g_allocCountBig = 0; +#endif + +void *MyAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + { + void *p = malloc(size); + fprintf(stderr, "\nAlloc %10d bytes, count = %10d, addr = %8X", size, g_allocCount++, (unsigned)p); + return p; + } + #else + return malloc(size); + #endif +} + +void MyFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree; count = %10d, addr = %8X", --g_allocCount, (unsigned)address); + #endif + free(address); +} + +#ifdef _WIN32 + +void *MidAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_Mid %10d bytes; count = %10d", size, g_allocCountMid++); + #endif + return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +} + +void MidFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid); + #endif + if (address == 0) + return; + VirtualFree(address, 0, MEM_RELEASE); +} + +#ifndef MEM_LARGE_PAGES +#undef _7ZIP_LARGE_PAGES +#endif + +#ifdef _7ZIP_LARGE_PAGES +SIZE_T g_LargePageSize = 0; +typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); +#endif + +void SetLargePageSize() +{ + #ifdef _7ZIP_LARGE_PAGES + SIZE_T size = 0; + GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) + GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); + if (largePageMinimum == 0) + return; + size = largePageMinimum(); + if (size == 0 || (size & (size - 1)) != 0) + return; + g_LargePageSize = size; + #endif +} + + +void *BigAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_Big %10d bytes; count = %10d", size, g_allocCountBig++); + #endif + + #ifdef _7ZIP_LARGE_PAGES + if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18)) + { + void *res = VirtualAlloc(0, (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)), + MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + if (res != 0) + return res; + } + #endif + return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +} + +void BigFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig); + #endif + + if (address == 0) + return; + VirtualFree(address, 0, MEM_RELEASE); +} + +#endif diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/Alloc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/Alloc.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,34 @@ +/* Alloc.h -- Memory allocation functions +2008-03-13 +Igor Pavlov +Public domain */ + +#ifndef __COMMON_ALLOC_H +#define __COMMON_ALLOC_H + +#include + +#include "NameMangle.h" + +void *MyAlloc(size_t size); +void MyFree(void *address); + +#ifdef _WIN32 + +void SetLargePageSize(); + +void *MidAlloc(size_t size); +void MidFree(void *address); +void *BigAlloc(size_t size); +void BigFree(void *address); + +#else + +#define MidAlloc(size) MyAlloc(size) +#define MidFree(address) MyFree(address) +#define BigAlloc(size) MyAlloc(size) +#define BigFree(address) MyFree(address) + +#endif + +#endif diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzFind.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzFind.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,751 @@ +/* LzFind.c -- Match finder for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#include + +#include "LzFind.h" +#include "LzHash.h" + +#define kEmptyHashValue 0 +#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) +#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ +#define kNormalizeMask (~(kNormalizeStepMin - 1)) +#define kMaxHistorySize ((UInt32)3 << 30) + +#define kStartMaxLen 3 + +static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + if (!p->directInput) + { + alloc->Free(alloc, p->bufferBase); + p->bufferBase = 0; + } +} + +/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ + +static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) +{ + UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; + if (p->directInput) + { + p->blockSize = blockSize; + return 1; + } + if (p->bufferBase == 0 || p->blockSize != blockSize) + { + LzInWindow_Free(p, alloc); + p->blockSize = blockSize; + p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); + } + return (p->bufferBase != 0); +} + +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } +Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } + +UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } + +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) +{ + p->posLimit -= subValue; + p->pos -= subValue; + p->streamPos -= subValue; +} + +static void MatchFinder_ReadBlock(CMatchFinder *p) +{ + if (p->streamEndWasReached || p->result != SZ_OK) + return; + for (;;) + { + Byte *dest = p->buffer + (p->streamPos - p->pos); + size_t size = (p->bufferBase + p->blockSize - dest); + if (size == 0) + return; + p->result = p->stream->Read(p->stream, dest, &size); + if (p->result != SZ_OK) + return; + if (size == 0) + { + p->streamEndWasReached = 1; + return; + } + p->streamPos += (UInt32)size; + if (p->streamPos - p->pos > p->keepSizeAfter) + return; + } +} + +void MatchFinder_MoveBlock(CMatchFinder *p) +{ + memmove(p->bufferBase, + p->buffer - p->keepSizeBefore, + (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); + p->buffer = p->bufferBase + p->keepSizeBefore; +} + +int MatchFinder_NeedMove(CMatchFinder *p) +{ + /* if (p->streamEndWasReached) return 0; */ + return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); +} + +void MatchFinder_ReadIfRequired(CMatchFinder *p) +{ + if (p->streamEndWasReached) + return; + if (p->keepSizeAfter >= p->streamPos - p->pos) + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) +{ + if (MatchFinder_NeedMove(p)) + MatchFinder_MoveBlock(p); + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_SetDefaultSettings(CMatchFinder *p) +{ + p->cutValue = 32; + p->btMode = 1; + p->numHashBytes = 4; + /* p->skipModeBits = 0; */ + p->directInput = 0; + p->bigHash = 0; +} + +#define kCrcPoly 0xEDB88320 + +void MatchFinder_Construct(CMatchFinder *p) +{ + UInt32 i; + p->bufferBase = 0; + p->directInput = 0; + p->hash = 0; + MatchFinder_SetDefaultSettings(p); + + for (i = 0; i < 256; i++) + { + UInt32 r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + p->crc[i] = r; + } +} + +static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->hash); + p->hash = 0; +} + +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + MatchFinder_FreeThisClassMemory(p, alloc); + LzInWindow_Free(p, alloc); +} + +static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) +{ + size_t sizeInBytes = (size_t)num * sizeof(CLzRef); + if (sizeInBytes / sizeof(CLzRef) != num) + return 0; + return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); +} + +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc) +{ + UInt32 sizeReserv; + if (historySize > kMaxHistorySize) + { + MatchFinder_Free(p, alloc); + return 0; + } + sizeReserv = historySize >> 1; + if (historySize > ((UInt32)2 << 30)) + sizeReserv = historySize >> 2; + sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); + + p->keepSizeBefore = historySize + keepAddBufferBefore + 1; + p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; + /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ + if (LzInWindow_Create(p, sizeReserv, alloc)) + { + UInt32 newCyclicBufferSize = (historySize /* >> p->skipModeBits */) + 1; + UInt32 hs; + p->matchMaxLen = matchMaxLen; + { + p->fixedHashSize = 0; + if (p->numHashBytes == 2) + hs = (1 << 16) - 1; + else + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + /* hs >>= p->skipModeBits; */ + hs |= 0xFFFF; /* don't change it! It's required for Deflate */ + if (hs > (1 << 24)) + { + if (p->numHashBytes == 3) + hs = (1 << 24) - 1; + else + hs >>= 1; + } + } + p->hashMask = hs; + hs++; + if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; + if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; + if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; + hs += p->fixedHashSize; + } + + { + UInt32 prevSize = p->hashSizeSum + p->numSons; + UInt32 newSize; + p->historySize = historySize; + p->hashSizeSum = hs; + p->cyclicBufferSize = newCyclicBufferSize; + p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); + newSize = p->hashSizeSum + p->numSons; + if (p->hash != 0 && prevSize == newSize) + return 1; + MatchFinder_FreeThisClassMemory(p, alloc); + p->hash = AllocRefs(newSize, alloc); + if (p->hash != 0) + { + p->son = p->hash + p->hashSizeSum; + return 1; + } + } + } + MatchFinder_Free(p, alloc); + return 0; +} + +static void MatchFinder_SetLimits(CMatchFinder *p) +{ + UInt32 limit = kMaxValForNormalize - p->pos; + UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; + if (limit2 < limit) + limit = limit2; + limit2 = p->streamPos - p->pos; + if (limit2 <= p->keepSizeAfter) + { + if (limit2 > 0) + limit2 = 1; + } + else + limit2 -= p->keepSizeAfter; + if (limit2 < limit) + limit = limit2; + { + UInt32 lenLimit = p->streamPos - p->pos; + if (lenLimit > p->matchMaxLen) + lenLimit = p->matchMaxLen; + p->lenLimit = lenLimit; + } + p->posLimit = p->pos + limit; +} + +void MatchFinder_Init(CMatchFinder *p) +{ + UInt32 i; + for (i = 0; i < p->hashSizeSum; i++) + p->hash[i] = kEmptyHashValue; + p->cyclicBufferPos = 0; + p->buffer = p->bufferBase; + p->pos = p->streamPos = p->cyclicBufferSize; + p->result = SZ_OK; + p->streamEndWasReached = 0; + MatchFinder_ReadBlock(p); + MatchFinder_SetLimits(p); +} + +static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) +{ + return (p->pos - p->historySize - 1) & kNormalizeMask; +} + +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) +{ + UInt32 i; + for (i = 0; i < numItems; i++) + { + UInt32 value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } +} + +static void MatchFinder_Normalize(CMatchFinder *p) +{ + UInt32 subValue = MatchFinder_GetSubValue(p); + MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); + MatchFinder_ReduceOffsets(p, subValue); +} + +static void MatchFinder_CheckLimits(CMatchFinder *p) +{ + if (p->pos == kMaxValForNormalize) + MatchFinder_Normalize(p); + if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) + MatchFinder_CheckAndMoveAndRead(p); + if (p->cyclicBufferPos == p->cyclicBufferSize) + p->cyclicBufferPos = 0; + MatchFinder_SetLimits(p); +} + +static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) +{ + son[_cyclicBufferPos] = curMatch; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + return distances; + { + const Byte *pb = cur - delta; + curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; + if (pb[maxLen] == cur[maxLen] && *pb == *cur) + { + UInt32 len = 0; + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + return distances; + } + } + } + } +} + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return distances; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + if (++len != lenLimit && pb[len] == cur[len]) + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return distances; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + { + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +#define MOVE_POS \ + ++p->cyclicBufferPos; \ + p->buffer++; \ + if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); + +#define MOVE_POS_RET MOVE_POS return offset; + +static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } + +#define GET_MATCHES_HEADER2(minLen, ret_op) \ + UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ + lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ + cur = p->buffer; + +#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) +#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) + +#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue + +#define GET_MATCHES_FOOTER(offset, maxLen) \ + offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ + distances + offset, maxLen) - distances); MOVE_POS_RET; + +#define SKIP_FOOTER \ + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; + +static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 1) +} + +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 2) +} + +static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, delta2, maxLen, offset; + GET_MATCHES_HEADER(3) + + HASH3_CALC; + + delta2 = p->pos - p->hash[hash2Value]; + curMatch = p->hash[kFix3HashSize + hashValue]; + + p->hash[hash2Value] = + p->hash[kFix3HashSize + hashValue] = p->pos; + + + maxLen = 2; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[0] = maxLen; + distances[1] = delta2 - 1; + offset = 2; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + GET_MATCHES_FOOTER(offset, maxLen) +} + +static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[ hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + GET_MATCHES_FOOTER(offset, maxLen) +} + +static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[ hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances + offset, maxLen) - (distances)); + MOVE_POS_RET +} + +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances, 2) - (distances)); + MOVE_POS_RET +} + +static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value; + SKIP_HEADER(3) + HASH3_CALC; + curMatch = p->hash[kFix3HashSize + hashValue]; + p->hash[hash2Value] = + p->hash[kFix3HashSize + hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = p->pos; + p->hash[kFix4HashSize + hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) +{ + vTable->Init = (Mf_Init_Func)MatchFinder_Init; + vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; + vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; + vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; + if (!p->btMode) + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; + } + else if (p->numHashBytes == 2) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; + } + else if (p->numHashBytes == 3) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; + } + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; + } +} diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzFind.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzFind.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,107 @@ +/* LzFind.h -- Match finder for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZFIND_H +#define __LZFIND_H + +#include "Types.h" + +typedef UInt32 CLzRef; + +typedef struct _CMatchFinder +{ + Byte *buffer; + UInt32 pos; + UInt32 posLimit; + UInt32 streamPos; + UInt32 lenLimit; + + UInt32 cyclicBufferPos; + UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ + + UInt32 matchMaxLen; + CLzRef *hash; + CLzRef *son; + UInt32 hashMask; + UInt32 cutValue; + + Byte *bufferBase; + ISeqInStream *stream; + int streamEndWasReached; + + UInt32 blockSize; + UInt32 keepSizeBefore; + UInt32 keepSizeAfter; + + UInt32 numHashBytes; + int directInput; + int btMode; + /* int skipModeBits; */ + int bigHash; + UInt32 historySize; + UInt32 fixedHashSize; + UInt32 hashSizeSum; + UInt32 numSons; + SRes result; + UInt32 crc[256]; +} CMatchFinder; + +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) +#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) + +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +int MatchFinder_NeedMove(CMatchFinder *p); +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +void MatchFinder_MoveBlock(CMatchFinder *p); +void MatchFinder_ReadIfRequired(CMatchFinder *p); + +void MatchFinder_Construct(CMatchFinder *p); + +/* Conditions: + historySize <= 3 GB + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB +*/ +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc); +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, + UInt32 *distances, UInt32 maxLen); + +/* +Conditions: + Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. + Mf_GetPointerToCurrentPos_Func's result must be used only before any other function +*/ + +typedef void (*Mf_Init_Func)(void *object); +typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); +typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); +typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); +typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); +typedef void (*Mf_Skip_Func)(void *object, UInt32); + +typedef struct _IMatchFinder +{ + Mf_Init_Func Init; + Mf_GetIndexByte_Func GetIndexByte; + Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; + Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; + Mf_GetMatches_Func GetMatches; + Mf_Skip_Func Skip; +} IMatchFinder; + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +void MatchFinder_Init(CMatchFinder *p); +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); + +#endif diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzHash.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzHash.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,54 @@ +/* LzHash.h -- HASH functions for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZHASH_H +#define __LZHASH_H + +#define kHash2Size (1 << 10) +#define kHash3Size (1 << 16) +#define kHash4Size (1 << 20) + +#define kFix3HashSize (kHash2Size) +#define kFix4HashSize (kHash2Size + kHash3Size) +#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) + +#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); + +#define HASH3_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } + +#define HASH4_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } + +#define HASH5_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ + hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ + hash4Value &= (kHash4Size - 1); } + +/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ +#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; + + +#define MT_HASH2_CALC \ + hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); + +#define MT_HASH3_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } + +#define MT_HASH4_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } + +#endif diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzmaDec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzmaDec.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,1007 @@ +/* LzmaDec.c -- LZMA Decoder +2008-11-06 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" + +#include + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ + { UPDATE_0(p); i = (i + i); A0; } else \ + { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ + { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ + { UPDATE_0_CHECK; i = (i + i); A0; } else \ + { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +static const Byte kLiteralNextStates[kNumStates * 2] = +{ + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5, + 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 +}; + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker + = kMatchSpecLenStart + 2 : State Init Marker +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + UInt32 processedPos = p->processedPos; + UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + UInt32 range = p->range; + UInt32 code = p->code; + + do + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (checkDicSize != 0 || processedPos != 0) + prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + + if (state < kNumLitStates) + { + symbol = 1; + do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned offs = 0x100; + symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + dic[dicPos++] = (Byte)symbol; + processedPos++; + + state = kLiteralNextStates[state]; + /* if (state < 4) state = 0; else if (state < 10) state -= 3; else state -= 6; */ + continue; + } + else + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, limit, len); + len += offset; + } + + if (state >= kNumStates) + { + UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + int numDirectBits = (int)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + UInt32 t; + code -= range; + t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + return SZ_ERROR_DATA; + } + else if (distance >= checkDicSize) + return SZ_ERROR_DATA; + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + /* state = kLiteralNextStates[state]; */ + } + + len += kMatchMinLen; + + if (limit == dicPos) + return SZ_ERROR_DATA; + { + SizeT rem = limit - dicPos; + unsigned curLen = ((rem < len) ? (unsigned)rem : len); + SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (pos + curLen <= dicBufSize) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + UInt32 rep0 = p->reps[0]; + if (limit - dicPos < len) + len = (unsigned)(limit - dicPos); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len-- != 0) + { + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + if (p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + { + p->remainLen = kMatchSpecLenStart; + } + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + UInt32 range = p->range; + UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += (LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) +{ + p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; +} + +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); + UInt32 i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush != 0) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + + LzmaDec_InitRc(p, p->tempBuf); + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen; + SizeT inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->dic); + p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ + LzmaDec_FreeProbs(p, alloc); + LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (p->probs == 0 || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (p->probs == 0) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + SizeT dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + dicBufSize = propNew.dicSize; + if (p->dic == 0 || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p, alloc); + p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + if (p->dic == 0) + { + LzmaDec_FreeProbs(p, alloc); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzmaDec p; + SRes res; + SizeT inSize = *srcLen; + SizeT outSize = *destLen; + *srcLen = *destLen = 0; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + + LzmaDec_Construct(&p); + res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); + if (res != 0) + return res; + p.dic = dest; + p.dicBufSize = outSize; + + LzmaDec_Init(&p); + + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + (*destLen) = p.dicPos; + LzmaDec_FreeProbs(&p, alloc); + return res; +} diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzmaDec.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzmaDec.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,223 @@ +/* LzmaDec.h -- LZMA Decoder +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMADEC_H +#define __LZMADEC_H + +#include "Types.h" + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + UInt32 processedPos; + UInt32 checkDicSize; + unsigned state; + UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +#endif diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzmaEnc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzmaEnc.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,2281 @@ +/* LzmaEnc.c -- LZMA Encoder +2009-02-02 : Igor Pavlov : Public domain */ + +#include + +/* #define SHOW_STAT */ +/* #define SHOW_STAT2 */ + +#if defined(SHOW_STAT) || defined(SHOW_STAT2) +#include +#endif + +#include "LzmaEnc.h" + +#include "LzFind.h" +#ifdef COMPRESS_MF_MT +#include "LzFindMt.h" +#endif + +#ifdef SHOW_STAT +static int ttt = 0; +#endif + +#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) + +#define kBlockSize (9 << 10) +#define kUnpackBlockSize (1 << 18) +#define kMatchArraySize (1 << 21) +#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) + +#define kNumMaxDirectBits (31) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 +#define kProbInitValue (kBitModelTotal >> 1) + +#define kNumMoveReducingBits 4 +#define kNumBitPriceShiftBits 4 +#define kBitPrice (1 << kNumBitPriceShiftBits) + +void LzmaEncProps_Init(CLzmaEncProps *p) +{ + p->level = 5; + p->dictSize = p->mc = 0; + p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; + p->writeEndMark = 0; +} + +void LzmaEncProps_Normalize(CLzmaEncProps *p) +{ + int level = p->level; + if (level < 0) level = 5; + p->level = level; + if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); + if (p->lc < 0) p->lc = 3; + if (p->lp < 0) p->lp = 0; + if (p->pb < 0) p->pb = 2; + if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); + if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); + if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); + if (p->numHashBytes < 0) p->numHashBytes = 4; + if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + if (p->numThreads < 0) + p->numThreads = + #ifdef COMPRESS_MF_MT + ((p->btMode && p->algo) ? 2 : 1); + #else + 1; + #endif +} + +UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) +{ + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + return props.dictSize; +} + +/* #define LZMA_LOG_BSR */ +/* Define it for Intel's CPU */ + + +#ifdef LZMA_LOG_BSR + +#define kDicLogSizeMaxCompress 30 + +#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } + +static UInt32 GetPosSlot1(UInt32 pos) +{ + UInt32 res; + BSR2_RET(pos, res); + return res; +} +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } + +#else + +#define kNumLogBits (9 + (int)sizeof(size_t) / 2) +#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) + +void LzmaEnc_FastPosInit(Byte *g_FastPos) +{ + int c = 2, slotFast; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + + for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) + { + UInt32 k = (1 << ((slotFast >> 1) - 1)); + UInt32 j; + for (j = 0; j < k; j++, c++) + g_FastPos[c] = (Byte)slotFast; + } +} + +#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ + (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ + res = p->g_FastPos[pos >> i] + (i * 2); } +/* +#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ + p->g_FastPos[pos >> 6] + 12 : \ + p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } +*/ + +#define GetPosSlot1(pos) p->g_FastPos[pos] +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } + +#endif + + +#define LZMA_NUM_REPS 4 + +typedef unsigned CState; + +typedef struct _COptimal +{ + UInt32 price; + + CState state; + int prev1IsChar; + int prev2; + + UInt32 posPrev2; + UInt32 backPrev2; + + UInt32 posPrev; + UInt32 backPrev; + UInt32 backs[LZMA_NUM_REPS]; +} COptimal; + +#define kNumOpts (1 << 12) + +#define kNumLenToPosStates 4 +#define kNumPosSlotBits 6 +#define kDicLogSizeMin 0 +#define kDicLogSizeMax 32 +#define kDistTableSizeMax (kDicLogSizeMax * 2) + + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) +#define kAlignMask (kAlignTableSize - 1) + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) + +#define kNumFullDistances (1 << (kEndPosModelIndex / 2)) + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + +#define LZMA_PB_MAX 4 +#define LZMA_LC_MAX 8 +#define LZMA_LP_MAX 4 + +#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) + + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define LZMA_MATCH_LEN_MIN 2 +#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) + +#define kNumStates 12 + +typedef struct +{ + CLzmaProb choice; + CLzmaProb choice2; + CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; + CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; + CLzmaProb high[kLenNumHighSymbols]; +} CLenEnc; + +typedef struct +{ + CLenEnc p; + UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; + UInt32 tableSize; + UInt32 counters[LZMA_NUM_PB_STATES_MAX]; +} CLenPriceEnc; + +typedef struct _CRangeEnc +{ + UInt32 range; + Byte cache; + UInt64 low; + UInt64 cacheSize; + Byte *buf; + Byte *bufLim; + Byte *bufBase; + ISeqOutStream *outStream; + UInt64 processed; + SRes res; +} CRangeEnc; + +typedef struct _CSeqInStreamBuf +{ + ISeqInStream funcTable; + const Byte *data; + SizeT rem; +} CSeqInStreamBuf; + +static SRes MyRead(void *pp, void *data, size_t *size) +{ + size_t curSize = *size; + CSeqInStreamBuf *p = (CSeqInStreamBuf *)pp; + if (p->rem < curSize) + curSize = p->rem; + memcpy(data, p->data, curSize); + p->rem -= curSize; + p->data += curSize; + *size = curSize; + return SZ_OK; +} + +typedef struct +{ + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + UInt32 reps[LZMA_NUM_REPS]; + UInt32 state; +} CSaveState; + +typedef struct _CLzmaEnc +{ + IMatchFinder matchFinder; + void *matchFinderObj; + + #ifdef COMPRESS_MF_MT + Bool mtMode; + CMatchFinderMt matchFinderMt; + #endif + + CMatchFinder matchFinderBase; + + #ifdef COMPRESS_MF_MT + Byte pad[128]; + #endif + + UInt32 optimumEndIndex; + UInt32 optimumCurrentIndex; + + UInt32 longestMatchLength; + UInt32 numPairs; + UInt32 numAvail; + COptimal opt[kNumOpts]; + + #ifndef LZMA_LOG_BSR + Byte g_FastPos[1 << kNumLogBits]; + #endif + + UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; + UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; + UInt32 numFastBytes; + UInt32 additionalOffset; + UInt32 reps[LZMA_NUM_REPS]; + UInt32 state; + + UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; + UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; + UInt32 alignPrices[kAlignTableSize]; + UInt32 alignPriceCount; + + UInt32 distTableSize; + + unsigned lc, lp, pb; + unsigned lpMask, pbMask; + + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + unsigned lclp; + + Bool fastMode; + + CRangeEnc rc; + + Bool writeEndMark; + UInt64 nowPos64; + UInt32 matchPriceCount; + Bool finished; + Bool multiThread; + + SRes result; + UInt32 dictSize; + UInt32 matchFinderCycles; + + ISeqInStream *inStream; + CSeqInStreamBuf seqBufInStream; + + CSaveState saveState; +} CLzmaEnc; + +void LzmaEnc_SaveState(CLzmaEncHandle pp) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CSaveState *dest = &p->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); +} + +void LzmaEnc_RestoreState(CLzmaEncHandle pp) +{ + CLzmaEnc *dest = (CLzmaEnc *)pp; + const CSaveState *p = &dest->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); +} + +SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + + if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || + props.dictSize > (1U << kDicLogSizeMaxCompress) || props.dictSize > (1U << 30)) + return SZ_ERROR_PARAM; + p->dictSize = props.dictSize; + p->matchFinderCycles = props.mc; + { + unsigned fb = props.fb; + if (fb < 5) + fb = 5; + if (fb > LZMA_MATCH_LEN_MAX) + fb = LZMA_MATCH_LEN_MAX; + p->numFastBytes = fb; + } + p->lc = props.lc; + p->lp = props.lp; + p->pb = props.pb; + p->fastMode = (props.algo == 0); + p->matchFinderBase.btMode = props.btMode; + { + UInt32 numHashBytes = 4; + if (props.btMode) + { + if (props.numHashBytes < 2) + numHashBytes = 2; + else if (props.numHashBytes < 4) + numHashBytes = props.numHashBytes; + } + p->matchFinderBase.numHashBytes = numHashBytes; + } + + p->matchFinderBase.cutValue = props.mc; + + p->writeEndMark = props.writeEndMark; + + #ifdef COMPRESS_MF_MT + /* + if (newMultiThread != _multiThread) + { + ReleaseMatchFinder(); + _multiThread = newMultiThread; + } + */ + p->multiThread = (props.numThreads > 1); + #endif + + return SZ_OK; +} + +static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +#define IsCharState(s) ((s) < 7) + +#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) + +#define kInfinityPrice (1 << 30) + +static void RangeEnc_Construct(CRangeEnc *p) +{ + p->outStream = 0; + p->bufBase = 0; +} + +#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) + +#define RC_BUF_SIZE (1 << 16) +static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) +{ + if (p->bufBase == 0) + { + p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); + if (p->bufBase == 0) + return 0; + p->bufLim = p->bufBase + RC_BUF_SIZE; + } + return 1; +} + +static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->bufBase); + p->bufBase = 0; +} + +static void RangeEnc_Init(CRangeEnc *p) +{ + /* Stream.Init(); */ + p->low = 0; + p->range = 0xFFFFFFFF; + p->cacheSize = 1; + p->cache = 0; + + p->buf = p->bufBase; + + p->processed = 0; + p->res = SZ_OK; +} + +static void RangeEnc_FlushStream(CRangeEnc *p) +{ + size_t num; + if (p->res != SZ_OK) + return; + num = p->buf - p->bufBase; + if (num != p->outStream->Write(p->outStream, p->bufBase, num)) + p->res = SZ_ERROR_WRITE; + p->processed += num; + p->buf = p->bufBase; +} + +static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) +{ + if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) + { + Byte temp = p->cache; + do + { + Byte *buf = p->buf; + *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); + p->buf = buf; + if (buf == p->bufLim) + RangeEnc_FlushStream(p); + temp = 0xFF; + } + while (--p->cacheSize != 0); + p->cache = (Byte)((UInt32)p->low >> 24); + } + p->cacheSize++; + p->low = (UInt32)p->low << 8; +} + +static void RangeEnc_FlushData(CRangeEnc *p) +{ + int i; + for (i = 0; i < 5; i++) + RangeEnc_ShiftLow(p); +} + +static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) +{ + do + { + p->range >>= 1; + p->low += p->range & (0 - ((value >> --numBits) & 1)); + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } + } + while (numBits != 0); +} + +static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) +{ + UInt32 ttt = *prob; + UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; + if (symbol == 0) + { + p->range = newBound; + ttt += (kBitModelTotal - ttt) >> kNumMoveBits; + } + else + { + p->low += newBound; + p->range -= newBound; + ttt -= ttt >> kNumMoveBits; + } + *prob = (CLzmaProb)ttt; + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } +} + +static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) +{ + symbol |= 0x100; + do + { + RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); +} + +static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) +{ + UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); +} + +void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) +{ + UInt32 i; + for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) + { + const int kCyclesBits = kNumBitPriceShiftBits; + UInt32 w = i; + UInt32 bitCount = 0; + int j; + for (j = 0; j < kCyclesBits; j++) + { + w = w * w; + bitCount <<= 1; + while (w >= ((UInt32)1 << 16)) + { + w >>= 1; + bitCount++; + } + } + ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + } +} + + +#define GET_PRICE(prob, symbol) \ + p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICEa(prob, symbol) \ + ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) +{ + UInt32 price = 0; + symbol |= 0x100; + do + { + price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); + return price; +} + +static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) +{ + UInt32 price = 0; + UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); + return price; +} + + +static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) +{ + UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0;) + { + UInt32 bit; + i--; + bit = (symbol >> i) & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + } +} + +static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) +{ + UInt32 m = 1; + int i; + for (i = 0; i < numBitLevels; i++) + { + UInt32 bit = symbol & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + symbol >>= 1; + } +} + +static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) +{ + UInt32 price = 0; + symbol |= (1 << numBitLevels); + while (symbol != 1) + { + price += GET_PRICEa(probs[symbol >> 1], symbol & 1); + symbol >>= 1; + } + return price; +} + +static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) +{ + UInt32 price = 0; + UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += GET_PRICEa(probs[m], bit); + m = (m << 1) | bit; + } + return price; +} + + +static void LenEnc_Init(CLenEnc *p) +{ + unsigned i; + p->choice = p->choice2 = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) + p->low[i] = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) + p->mid[i] = kProbInitValue; + for (i = 0; i < kLenNumHighSymbols; i++) + p->high[i] = kProbInitValue; +} + +static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) +{ + if (symbol < kLenNumLowSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice, 0); + RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice, 1); + if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice2, 0); + RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice2, 1); + RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); + } + } +} + +static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) +{ + UInt32 a0 = GET_PRICE_0a(p->choice); + UInt32 a1 = GET_PRICE_1a(p->choice); + UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); + UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); + UInt32 i = 0; + for (i = 0; i < kLenNumLowSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); + } + for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); + } + for (; i < numSymbols; i++) + prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); +} + +static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) +{ + LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); + p->counters[posState] = p->tableSize; +} + +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) +{ + UInt32 posState; + for (posState = 0; posState < numPosStates; posState++) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + +static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) +{ + LenEnc_Encode(&p->p, rc, symbol, posState); + if (updatePrice) + if (--p->counters[posState] == 0) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + + + + +static void MovePos(CLzmaEnc *p, UInt32 num) +{ + #ifdef SHOW_STAT + ttt += num; + printf("\n MovePos %d", num); + #endif + if (num != 0) + { + p->additionalOffset += num; + p->matchFinder.Skip(p->matchFinderObj, num); + } +} + +static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) +{ + UInt32 lenRes = 0, numPairs; + p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); + numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); + #ifdef SHOW_STAT + printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); + ttt++; + { + UInt32 i; + for (i = 0; i < numPairs; i += 2) + printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); + } + #endif + if (numPairs > 0) + { + lenRes = p->matches[numPairs - 2]; + if (lenRes == p->numFastBytes) + { + const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + UInt32 distance = p->matches[numPairs - 1] + 1; + UInt32 numAvail = p->numAvail; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + { + const Byte *pby2 = pby - distance; + for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); + } + } + } + p->additionalOffset++; + *numDistancePairsRes = numPairs; + return lenRes; +} + + +#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; +#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; +#define IsShortRep(p) ((p)->backPrev == 0) + +static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) +{ + return + GET_PRICE_0(p->isRepG0[state]) + + GET_PRICE_0(p->isRep0Long[state][posState]); +} + +static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) +{ + UInt32 price; + if (repIndex == 0) + { + price = GET_PRICE_0(p->isRepG0[state]); + price += GET_PRICE_1(p->isRep0Long[state][posState]); + } + else + { + price = GET_PRICE_1(p->isRepG0[state]); + if (repIndex == 1) + price += GET_PRICE_0(p->isRepG1[state]); + else + { + price += GET_PRICE_1(p->isRepG1[state]); + price += GET_PRICE(p->isRepG2[state], repIndex - 2); + } + } + return price; +} + +static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) +{ + return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + + GetPureRepPrice(p, repIndex, state, posState); +} + +static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) +{ + UInt32 posMem = p->opt[cur].posPrev; + UInt32 backMem = p->opt[cur].backPrev; + p->optimumEndIndex = cur; + do + { + if (p->opt[cur].prev1IsChar) + { + MakeAsChar(&p->opt[posMem]) + p->opt[posMem].posPrev = posMem - 1; + if (p->opt[cur].prev2) + { + p->opt[posMem - 1].prev1IsChar = False; + p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; + p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; + } + } + { + UInt32 posPrev = posMem; + UInt32 backCur = backMem; + + backMem = p->opt[posPrev].backPrev; + posMem = p->opt[posPrev].posPrev; + + p->opt[posPrev].backPrev = backCur; + p->opt[posPrev].posPrev = cur; + cur = posPrev; + } + } + while (cur != 0); + *backRes = p->opt[0].backPrev; + p->optimumCurrentIndex = p->opt[0].posPrev; + return p->optimumCurrentIndex; +} + +#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) + +static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) +{ + UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; + UInt32 matchPrice, repMatchPrice, normalMatchPrice; + UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; + UInt32 *matches; + const Byte *data; + Byte curByte, matchByte; + if (p->optimumEndIndex != p->optimumCurrentIndex) + { + const COptimal *opt = &p->opt[p->optimumCurrentIndex]; + UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; + *backRes = opt->backPrev; + p->optimumCurrentIndex = opt->posPrev; + return lenRes; + } + p->optimumCurrentIndex = p->optimumEndIndex = 0; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + if (numAvail < 2) + { + *backRes = (UInt32)(-1); + return 1; + } + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + repMaxIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 lenTest; + const Byte *data2; + reps[i] = p->reps[i]; + data2 = data - (reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + { + repLens[i] = 0; + continue; + } + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + repLens[i] = lenTest; + if (lenTest > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= p->numFastBytes) + { + UInt32 lenRes; + *backRes = repMaxIndex; + lenRes = repLens[repMaxIndex]; + MovePos(p, lenRes - 1); + return lenRes; + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) + { + *backRes = (UInt32)-1; + return 1; + } + + p->opt[0].state = (CState)p->state; + + posState = (position & p->pbMask); + + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + + (!IsCharState(p->state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + MakeAsChar(&p->opt[1]); + + matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); + + if (matchByte == curByte) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); + if (shortRepPrice < p->opt[1].price) + { + p->opt[1].price = shortRepPrice; + MakeAsShortRep(&p->opt[1]); + } + } + lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); + + if (lenEnd < 2) + { + *backRes = p->opt[1].backPrev; + return 1; + } + + p->opt[1].posPrev = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + p->opt[0].backs[i] = reps[i]; + + len = lenEnd; + do + p->opt[len--].price = kInfinityPrice; + while (len >= 2); + + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 repLen = repLens[i]; + UInt32 price; + if (repLen < 2) + continue; + price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); + do + { + UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; + COptimal *opt = &p->opt[repLen]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = i; + opt->prev1IsChar = False; + } + } + while (--repLen >= 2); + } + + normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= mainLen) + { + UInt32 offs = 0; + while (len > matches[offs]) + offs += 2; + for (; ; len++) + { + COptimal *opt; + UInt32 distance = matches[offs + 1]; + + UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; + UInt32 lenToPosState = GetLenToPosState(len); + if (distance < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][distance]; + else + { + UInt32 slot; + GetPosSlot2(distance, slot); + curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; + } + opt = &p->opt[len]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = distance + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + if (len == matches[offs]) + { + offs += 2; + if (offs == numPairs) + break; + } + } + } + + cur = 0; + + #ifdef SHOW_STAT2 + if (position >= 0) + { + unsigned i; + printf("\n pos = %4X", position); + for (i = cur; i <= lenEnd; i++) + printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); + } + #endif + + for (;;) + { + UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; + UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; + Bool nextIsChar; + Byte curByte, matchByte; + const Byte *data; + COptimal *curOpt; + COptimal *nextOpt; + + cur++; + if (cur == lenEnd) + return Backward(p, backRes, cur); + + newLen = ReadMatchDistances(p, &numPairs); + if (newLen >= p->numFastBytes) + { + p->numPairs = numPairs; + p->longestMatchLength = newLen; + return Backward(p, backRes, cur); + } + position++; + curOpt = &p->opt[cur]; + posPrev = curOpt->posPrev; + if (curOpt->prev1IsChar) + { + posPrev--; + if (curOpt->prev2) + { + state = p->opt[curOpt->posPrev2].state; + if (curOpt->backPrev2 < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + else + state = p->opt[posPrev].state; + state = kLiteralNextStates[state]; + } + else + state = p->opt[posPrev].state; + if (posPrev == cur - 1) + { + if (IsShortRep(curOpt)) + state = kShortRepNextStates[state]; + else + state = kLiteralNextStates[state]; + } + else + { + UInt32 pos; + const COptimal *prevOpt; + if (curOpt->prev1IsChar && curOpt->prev2) + { + posPrev = curOpt->posPrev2; + pos = curOpt->backPrev2; + state = kRepNextStates[state]; + } + else + { + pos = curOpt->backPrev; + if (pos < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + prevOpt = &p->opt[posPrev]; + if (pos < LZMA_NUM_REPS) + { + UInt32 i; + reps[0] = prevOpt->backs[pos]; + for (i = 1; i <= pos; i++) + reps[i] = prevOpt->backs[i - 1]; + for (; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i]; + } + else + { + UInt32 i; + reps[0] = (pos - LZMA_NUM_REPS); + for (i = 1; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i - 1]; + } + } + curOpt->state = (CState)state; + + curOpt->backs[0] = reps[0]; + curOpt->backs[1] = reps[1]; + curOpt->backs[2] = reps[2]; + curOpt->backs[3] = reps[3]; + + curPrice = curOpt->price; + nextIsChar = False; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + posState = (position & p->pbMask); + + curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + curAnd1Price += + (!IsCharState(state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + nextOpt = &p->opt[cur + 1]; + + if (curAnd1Price < nextOpt->price) + { + nextOpt->price = curAnd1Price; + nextOpt->posPrev = cur; + MakeAsChar(nextOpt); + nextIsChar = True; + } + + matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); + + if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); + if (shortRepPrice <= nextOpt->price) + { + nextOpt->price = shortRepPrice; + nextOpt->posPrev = cur; + MakeAsShortRep(nextOpt); + nextIsChar = True; + } + } + numAvailFull = p->numAvail; + { + UInt32 temp = kNumOpts - 1 - cur; + if (temp < numAvailFull) + numAvailFull = temp; + } + + if (numAvailFull < 2) + continue; + numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); + + if (!nextIsChar && matchByte != curByte) /* speed optimization */ + { + /* try Literal + rep0 */ + UInt32 temp; + UInt32 lenTest2; + const Byte *data2 = data - (reps[0] + 1); + UInt32 limit = p->numFastBytes + 1; + if (limit > numAvailFull) + limit = numAvailFull; + + for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); + lenTest2 = temp - 1; + if (lenTest2 >= 2) + { + UInt32 state2 = kLiteralNextStates[state]; + UInt32 posStateNext = (position + 1) & p->pbMask; + UInt32 nextRepMatchPrice = curAnd1Price + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 curAndLenPrice; + COptimal *opt; + UInt32 offset = cur + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = False; + } + } + } + } + + startLen = 2; /* speed optimization */ + { + UInt32 repIndex; + for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) + { + UInt32 lenTest; + UInt32 lenTestTemp; + UInt32 price; + const Byte *data2 = data - (reps[repIndex] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + while (lenEnd < cur + lenTest) + p->opt[++lenEnd].price = kInfinityPrice; + lenTestTemp = lenTest; + price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); + do + { + UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; + COptimal *opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = repIndex; + opt->prev1IsChar = False; + } + } + while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + /* if (_maxMode) */ + { + UInt32 lenTest2 = lenTest + 1; + UInt32 limit = lenTest2 + p->numFastBytes; + UInt32 nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + UInt32 state2 = kRepNextStates[state]; + UInt32 posStateNext = (position + lenTest) & p->pbMask; + UInt32 curAndLenCharPrice = + price + p->repLenEnc.prices[posState][lenTest - 2] + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (position + lenTest + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 curAndLenPrice; + COptimal *opt; + UInt32 offset = cur + lenTest + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = repIndex; + } + } + } + } + } + } + /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ + if (newLen > numAvail) + { + newLen = numAvail; + for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); + matches[numPairs] = newLen; + numPairs += 2; + } + if (newLen >= startLen) + { + UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); + UInt32 offs, curBack, posSlot; + UInt32 lenTest; + while (lenEnd < cur + newLen) + p->opt[++lenEnd].price = kInfinityPrice; + + offs = 0; + while (startLen > matches[offs]) + offs += 2; + curBack = matches[offs + 1]; + GetPosSlot2(curBack, posSlot); + for (lenTest = /*2*/ startLen; ; lenTest++) + { + UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; + UInt32 lenToPosState = GetLenToPosState(lenTest); + COptimal *opt; + if (curBack < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; + else + curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; + + opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = curBack + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + + if (/*_maxMode && */lenTest == matches[offs]) + { + /* Try Match + Literal + Rep0 */ + const Byte *data2 = data - (curBack + 1); + UInt32 lenTest2 = lenTest + 1; + UInt32 limit = lenTest2 + p->numFastBytes; + UInt32 nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + UInt32 state2 = kMatchNextStates[state]; + UInt32 posStateNext = (position + lenTest) & p->pbMask; + UInt32 curAndLenCharPrice = curAndLenPrice + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (posStateNext + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 offset = cur + lenTest + 1 + lenTest2; + UInt32 curAndLenPrice; + COptimal *opt; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = curBack + LZMA_NUM_REPS; + } + } + } + offs += 2; + if (offs == numPairs) + break; + curBack = matches[offs + 1]; + if (curBack >= kNumFullDistances) + GetPosSlot2(curBack, posSlot); + } + } + } + } +} + +#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) + +static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) +{ + UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; + const Byte *data; + const UInt32 *matches; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + *backRes = (UInt32)-1; + if (numAvail < 2) + return 1; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + + repLen = repIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 len; + const Byte *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (len = 2; len < numAvail && data[len] == data2[len]; len++); + if (len >= p->numFastBytes) + { + *backRes = i; + MovePos(p, len - 1); + return len; + } + if (len > repLen) + { + repIndex = i; + repLen = len; + } + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + + mainDist = 0; /* for GCC */ + if (mainLen >= 2) + { + mainDist = matches[numPairs - 1]; + while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) + { + if (!ChangePair(matches[numPairs - 3], mainDist)) + break; + numPairs -= 2; + mainLen = matches[numPairs - 2]; + mainDist = matches[numPairs - 1]; + } + if (mainLen == 2 && mainDist >= 0x80) + mainLen = 1; + } + + if (repLen >= 2 && ( + (repLen + 1 >= mainLen) || + (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || + (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) + { + *backRes = repIndex; + MovePos(p, repLen - 1); + return repLen; + } + + if (mainLen < 2 || numAvail <= 2) + return 1; + + p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); + if (p->longestMatchLength >= 2) + { + UInt32 newDistance = matches[p->numPairs - 1]; + if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || + (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || + (p->longestMatchLength > mainLen + 1) || + (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) + return 1; + } + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 len, limit; + const Byte *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + limit = mainLen - 1; + for (len = 2; len < limit && data[len] == data2[len]; len++); + if (len >= limit) + return 1; + } + *backRes = mainDist + LZMA_NUM_REPS; + MovePos(p, mainLen - 2); + return mainLen; +} + +static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) +{ + UInt32 len; + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + len = LZMA_MATCH_LEN_MIN; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); + RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); +} + +static SRes CheckErrors(CLzmaEnc *p) +{ + if (p->result != SZ_OK) + return p->result; + if (p->rc.res != SZ_OK) + p->result = SZ_ERROR_WRITE; + if (p->matchFinderBase.result != SZ_OK) + p->result = SZ_ERROR_READ; + if (p->result != SZ_OK) + p->finished = True; + return p->result; +} + +static SRes Flush(CLzmaEnc *p, UInt32 nowPos) +{ + /* ReleaseMFStream(); */ + p->finished = True; + if (p->writeEndMark) + WriteEndMarker(p, nowPos & p->pbMask); + RangeEnc_FlushData(&p->rc); + RangeEnc_FlushStream(&p->rc); + return CheckErrors(p); +} + +static void FillAlignPrices(CLzmaEnc *p) +{ + UInt32 i; + for (i = 0; i < kAlignTableSize; i++) + p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); + p->alignPriceCount = 0; +} + +static void FillDistancesPrices(CLzmaEnc *p) +{ + UInt32 tempPrices[kNumFullDistances]; + UInt32 i, lenToPosState; + for (i = kStartPosModelIndex; i < kNumFullDistances; i++) + { + UInt32 posSlot = GetPosSlot1(i); + UInt32 footerBits = ((posSlot >> 1) - 1); + UInt32 base = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); + } + + for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) + { + UInt32 posSlot; + const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; + UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; + for (posSlot = 0; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); + for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); + + { + UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; + UInt32 i; + for (i = 0; i < kStartPosModelIndex; i++) + distancesPrices[i] = posSlotPrices[i]; + for (; i < kNumFullDistances; i++) + distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; + } + } + p->matchPriceCount = 0; +} + +void LzmaEnc_Construct(CLzmaEnc *p) +{ + RangeEnc_Construct(&p->rc); + MatchFinder_Construct(&p->matchFinderBase); + #ifdef COMPRESS_MF_MT + MatchFinderMt_Construct(&p->matchFinderMt); + p->matchFinderMt.MatchFinder = &p->matchFinderBase; + #endif + + { + CLzmaEncProps props; + LzmaEncProps_Init(&props); + LzmaEnc_SetProps(p, &props); + } + + #ifndef LZMA_LOG_BSR + LzmaEnc_FastPosInit(p->g_FastPos); + #endif + + LzmaEnc_InitPriceTables(p->ProbPrices); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) +{ + void *p; + p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); + if (p != 0) + LzmaEnc_Construct((CLzmaEnc *)p); + return p; +} + +void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->litProbs); + alloc->Free(alloc, p->saveState.litProbs); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + #ifdef COMPRESS_MF_MT + MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); + #endif + MatchFinder_Free(&p->matchFinderBase, allocBig); + LzmaEnc_FreeLits(p, alloc); + RangeEnc_Free(&p->rc, alloc); +} + +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); + alloc->Free(alloc, p); +} + +static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) +{ + UInt32 nowPos32, startPos32; + if (p->inStream != 0) + { + p->matchFinderBase.stream = p->inStream; + p->matchFinder.Init(p->matchFinderObj); + p->inStream = 0; + } + + if (p->finished) + return p->result; + RINOK(CheckErrors(p)); + + nowPos32 = (UInt32)p->nowPos64; + startPos32 = nowPos32; + + if (p->nowPos64 == 0) + { + UInt32 numPairs; + Byte curByte; + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + return Flush(p, nowPos32); + ReadMatchDistances(p, &numPairs); + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); + p->state = kLiteralNextStates[p->state]; + curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); + LitEnc_Encode(&p->rc, p->litProbs, curByte); + p->additionalOffset--; + nowPos32++; + } + + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) + for (;;) + { + UInt32 pos, len, posState; + + if (p->fastMode) + len = GetOptimumFast(p, &pos); + else + len = GetOptimum(p, nowPos32, &pos); + + #ifdef SHOW_STAT2 + printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); + #endif + + posState = nowPos32 & p->pbMask; + if (len == 1 && pos == (UInt32)-1) + { + Byte curByte; + CLzmaProb *probs; + const Byte *data; + + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; + curByte = *data; + probs = LIT_PROBS(nowPos32, *(data - 1)); + if (IsCharState(p->state)) + LitEnc_Encode(&p->rc, probs, curByte); + else + LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); + p->state = kLiteralNextStates[p->state]; + } + else + { + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + if (pos < LZMA_NUM_REPS) + { + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); + if (pos == 0) + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); + RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); + } + else + { + UInt32 distance = p->reps[pos]; + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); + if (pos == 1) + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); + else + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); + if (pos == 3) + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + } + p->reps[1] = p->reps[0]; + p->reps[0] = distance; + } + if (len == 1) + p->state = kShortRepNextStates[p->state]; + else + { + LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + p->state = kRepNextStates[p->state]; + } + } + else + { + UInt32 posSlot; + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + pos -= LZMA_NUM_REPS; + GetPosSlot(pos, posSlot); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); + + if (posSlot >= kStartPosModelIndex) + { + UInt32 footerBits = ((posSlot >> 1) - 1); + UInt32 base = ((2 | (posSlot & 1)) << footerBits); + UInt32 posReduced = pos - base; + + if (posSlot < kEndPosModelIndex) + RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); + else + { + RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); + p->alignPriceCount++; + } + } + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + p->reps[1] = p->reps[0]; + p->reps[0] = pos; + p->matchPriceCount++; + } + } + p->additionalOffset -= len; + nowPos32 += len; + if (p->additionalOffset == 0) + { + UInt32 processed; + if (!p->fastMode) + { + if (p->matchPriceCount >= (1 << 7)) + FillDistancesPrices(p); + if (p->alignPriceCount >= kAlignTableSize) + FillAlignPrices(p); + } + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + break; + processed = nowPos32 - startPos32; + if (useLimits) + { + if (processed + kNumOpts + 300 >= maxUnpackSize || + RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) + break; + } + else if (processed >= (1 << 15)) + { + p->nowPos64 += nowPos32 - startPos32; + return CheckErrors(p); + } + } + } + p->nowPos64 += nowPos32 - startPos32; + return Flush(p, nowPos32); +} + +#define kBigHashDicLimit ((UInt32)1 << 24) + +static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + UInt32 beforeSize = kNumOpts; + Bool btMode; + if (!RangeEnc_Alloc(&p->rc, alloc)) + return SZ_ERROR_MEM; + btMode = (p->matchFinderBase.btMode != 0); + #ifdef COMPRESS_MF_MT + p->mtMode = (p->multiThread && !p->fastMode && btMode); + #endif + + { + unsigned lclp = p->lc + p->lp; + if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) + { + LzmaEnc_FreeLits(p, alloc); + p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); + p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); + if (p->litProbs == 0 || p->saveState.litProbs == 0) + { + LzmaEnc_FreeLits(p, alloc); + return SZ_ERROR_MEM; + } + p->lclp = lclp; + } + } + + p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); + + if (beforeSize + p->dictSize < keepWindowSize) + beforeSize = keepWindowSize - p->dictSize; + + #ifdef COMPRESS_MF_MT + if (p->mtMode) + { + RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); + p->matchFinderObj = &p->matchFinderMt; + MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); + } + else + #endif + { + if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) + return SZ_ERROR_MEM; + p->matchFinderObj = &p->matchFinderBase; + MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); + } + return SZ_OK; +} + +void LzmaEnc_Init(CLzmaEnc *p) +{ + UInt32 i; + p->state = 0; + for (i = 0 ; i < LZMA_NUM_REPS; i++) + p->reps[i] = 0; + + RangeEnc_Init(&p->rc); + + + for (i = 0; i < kNumStates; i++) + { + UInt32 j; + for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) + { + p->isMatch[i][j] = kProbInitValue; + p->isRep0Long[i][j] = kProbInitValue; + } + p->isRep[i] = kProbInitValue; + p->isRepG0[i] = kProbInitValue; + p->isRepG1[i] = kProbInitValue; + p->isRepG2[i] = kProbInitValue; + } + + { + UInt32 num = 0x300 << (p->lp + p->lc); + for (i = 0; i < num; i++) + p->litProbs[i] = kProbInitValue; + } + + { + for (i = 0; i < kNumLenToPosStates; i++) + { + CLzmaProb *probs = p->posSlotEncoder[i]; + UInt32 j; + for (j = 0; j < (1 << kNumPosSlotBits); j++) + probs[j] = kProbInitValue; + } + } + { + for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + p->posEncoders[i] = kProbInitValue; + } + + LenEnc_Init(&p->lenEnc.p); + LenEnc_Init(&p->repLenEnc.p); + + for (i = 0; i < (1 << kNumAlignBits); i++) + p->posAlignEncoder[i] = kProbInitValue; + + p->optimumEndIndex = 0; + p->optimumCurrentIndex = 0; + p->additionalOffset = 0; + + p->pbMask = (1 << p->pb) - 1; + p->lpMask = (1 << p->lp) - 1; +} + +void LzmaEnc_InitPrices(CLzmaEnc *p) +{ + if (!p->fastMode) + { + FillDistancesPrices(p); + FillAlignPrices(p); + } + + p->lenEnc.tableSize = + p->repLenEnc.tableSize = + p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; + LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); +} + +static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + UInt32 i; + for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) + if (p->dictSize <= ((UInt32)1 << i)) + break; + p->distTableSize = i * 2; + + p->finished = False; + p->result = SZ_OK; + RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + p->nowPos64 = 0; + return SZ_OK; +} + +static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqInStream *inStream, ISeqOutStream *outStream, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + p->rc.outStream = outStream; + return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); +} + +SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, + ISeqInStream *inStream, UInt32 keepWindowSize, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) +{ + p->seqBufInStream.funcTable.Read = MyRead; + p->seqBufInStream.data = src; + p->seqBufInStream.rem = srcLen; +} + +SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, + UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + LzmaEnc_SetInputBuf(p, src, srcLen); + p->inStream = &p->seqBufInStream.funcTable; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +void LzmaEnc_Finish(CLzmaEncHandle pp) +{ + #ifdef COMPRESS_MF_MT + CLzmaEnc *p = (CLzmaEnc *)pp; + if (p->mtMode) + MatchFinderMt_ReleaseStream(&p->matchFinderMt); + #else + pp = pp; + #endif +} + +typedef struct _CSeqOutStreamBuf +{ + ISeqOutStream funcTable; + Byte *data; + SizeT rem; + Bool overflow; +} CSeqOutStreamBuf; + +static size_t MyWrite(void *pp, const void *data, size_t size) +{ + CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; + if (p->rem < size) + { + size = p->rem; + p->overflow = True; + } + memcpy(p->data, data, size); + p->rem -= size; + p->data += size; + return size; +} + + +UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +} + +const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +} + +SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, + Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + UInt64 nowPos64; + SRes res; + CSeqOutStreamBuf outStream; + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = False; + p->finished = False; + p->result = SZ_OK; + + if (reInit) + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + nowPos64 = p->nowPos64; + RangeEnc_Init(&p->rc); + p->rc.outStream = &outStream.funcTable; + + res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); + + *unpackSize = (UInt32)(p->nowPos64 - nowPos64); + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + + return res; +} + +SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + SRes res = SZ_OK; + + #ifdef COMPRESS_MF_MT + Byte allocaDummy[0x300]; + int i = 0; + for (i = 0; i < 16; i++) + allocaDummy[i] = (Byte)i; + #endif + + RINOK(LzmaEnc_Prepare(pp, inStream, outStream, alloc, allocBig)); + + for (;;) + { + res = LzmaEnc_CodeOneBlock(p, False, 0, 0); + if (res != SZ_OK || p->finished != 0) + break; + if (progress != 0) + { + res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); + if (res != SZ_OK) + { + res = SZ_ERROR_PROGRESS; + break; + } + } + } + LzmaEnc_Finish(pp); + return res; +} + +SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + int i; + UInt32 dictSize = p->dictSize; + if (*size < LZMA_PROPS_SIZE) + return SZ_ERROR_PARAM; + *size = LZMA_PROPS_SIZE; + props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); + + for (i = 11; i <= 30; i++) + { + if (dictSize <= ((UInt32)2 << i)) + { + dictSize = (2 << i); + break; + } + if (dictSize <= ((UInt32)3 << i)) + { + dictSize = (3 << i); + break; + } + } + + for (i = 0; i < 4; i++) + props[1 + i] = (Byte)(dictSize >> (8 * i)); + return SZ_OK; +} + +SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + SRes res; + CLzmaEnc *p = (CLzmaEnc *)pp; + + CSeqOutStreamBuf outStream; + + LzmaEnc_SetInputBuf(p, src, srcLen); + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = writeEndMark; + res = LzmaEnc_Encode(pp, &outStream.funcTable, &p->seqBufInStream.funcTable, + progress, alloc, allocBig); + + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + return res; +} + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); + SRes res; + if (p == 0) + return SZ_ERROR_MEM; + + res = LzmaEnc_SetProps(p, props); + if (res == SZ_OK) + { + res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); + if (res == SZ_OK) + res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, + writeEndMark, progress, alloc, allocBig); + } + + LzmaEnc_Destroy(p, alloc, allocBig); + return res; +} diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzmaEnc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzmaEnc.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,72 @@ +/* LzmaEnc.h -- LZMA Encoder +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMAENC_H +#define __LZMAENC_H + +#include "Types.h" + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaEncProps +{ + int level; /* 0 <= level <= 9 */ + UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version + (1 << 12) <= dictSize <= (1 << 30) for 64-bit version + default = (1 << 24) */ + int lc; /* 0 <= lc <= 8, default = 3 */ + int lp; /* 0 <= lp <= 4, default = 0 */ + int pb; /* 0 <= pb <= 4, default = 2 */ + int algo; /* 0 - fast, 1 - normal, default = 1 */ + int fb; /* 5 <= fb <= 273, default = 32 */ + int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ + int numHashBytes; /* 2, 3 or 4, default = 4 */ + UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ + unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ + int numThreads; /* 1 or 2, default = 2 */ +} CLzmaEncProps; + +void LzmaEncProps_Init(CLzmaEncProps *p); +void LzmaEncProps_Normalize(CLzmaEncProps *p); +UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); + + +/* ---------- CLzmaEncHandle Interface ---------- */ + +/* LzmaEnc_* functions can return the following exit codes: +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater in props + SZ_ERROR_WRITE - Write callback error. + SZ_ERROR_PROGRESS - some break from progress callback + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +typedef void * CLzmaEncHandle; + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); +SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); +SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +/* ---------- One Call Interface ---------- */ + +/* LzmaEncode +Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +#endif diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzmaLib.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzmaLib.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,48 @@ +/* LzmaLib.c -- LZMA library wrapper +2008-08-05 +Igor Pavlov +Public domain */ + +#include "LzmaEnc.h" +#include "LzmaDec.h" +#include "Alloc.h" +#include "LzmaLib.h" + +static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } +static void SzFree(void *p, void *address) { p = p; MyFree(address); } +static ISzAlloc g_Alloc = { SzAlloc, SzFree }; + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* use (1 << N) or (3 << N). 4 KB < dictSize <= 128 MB */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads, /* 1 or 2, default = 2 */ + int algo /* 0 = fast, 1 = normal */ +) +{ + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = level; + props.dictSize = dictSize; + props.lc = lc; + props.lp = lp; + props.pb = pb; + props.fb = fb; + props.numThreads = numThreads; + props.algo = algo; + + return LzmaEncode(dest, destLen, src, srcLen, &props, outProps, outPropsSize, 0, + NULL, &g_Alloc, &g_Alloc); +} + + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t *srcLen, + const unsigned char *props, size_t propsSize) +{ + ELzmaStatus status; + return LzmaDecode(dest, destLen, src, srcLen, props, (unsigned)propsSize, LZMA_FINISH_ANY, &status, &g_Alloc); +} diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/LzmaLib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/LzmaLib.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,136 @@ +/* LzmaLib.h -- LZMA library interface +2008-08-05 +Igor Pavlov +Public domain */ + +#ifndef __LZMALIB_H +#define __LZMALIB_H + +#include "Types.h" + +#ifdef __cplusplus + #define MY_EXTERN_C extern "C" +#else + #define MY_EXTERN_C extern +#endif + +#define MY_STDAPI MY_EXTERN_C int MY_STD_CALL + +#define LZMA_PROPS_SIZE 5 + +/* +RAM requirements for LZMA: + for compression: (dictSize * 11.5 + 6 MB) + state_size + for decompression: dictSize + state_size + state_size = (4 + (1.5 << (lc + lp))) KB + by default (lc=3, lp=0), state_size = 16 KB. + +LZMA properties (5 bytes) format + Offset Size Description + 0 1 lc, lp and pb in encoded form. + 1 4 dictSize (little endian). +*/ + +/* +LzmaCompress +------------ + +outPropsSize - + In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + + LZMA Encoder will use defult values for any parameter, if it is + -1 for any from: level, loc, lp, pb, fb, numThreads + 0 for dictSize + +level - compression level: 0 <= level <= 9; + + level dictSize algo fb + 0: 16 KB 0 32 + 1: 64 KB 0 32 + 2: 256 KB 0 32 + 3: 1 MB 0 32 + 4: 4 MB 0 32 + 5: 16 MB 1 32 + 6: 32 MB 1 32 + 7+: 64 MB 1 64 + + The default value for "level" is 5. + + algo = 0 means fast method + algo = 1 means normal method + +dictSize - The dictionary size in bytes. The maximum value is + 128 MB = (1 << 27) bytes for 32-bit version + 1 GB = (1 << 30) bytes for 64-bit version + The default value is 16 MB = (1 << 24) bytes. + It's recommended to use the dictionary that is larger than 4 KB and + that can be calculated as (1 << N) or (3 << N) sizes. + +lc - The number of literal context bits (high bits of previous literal). + It can be in the range from 0 to 8. The default value is 3. + Sometimes lc=4 gives the gain for big files. + +lp - The number of literal pos bits (low bits of current position for literals). + It can be in the range from 0 to 4. The default value is 0. + The lp switch is intended for periodical data when the period is equal to 2^lp. + For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's + better to set lc=0, if you change lp switch. + +pb - The number of pos bits (low bits of current position). + It can be in the range from 0 to 4. The default value is 2. + The pb switch is intended for periodical data when the period is equal 2^pb. + +fb - Word size (the number of fast bytes). + It can be in the range from 5 to 273. The default value is 32. + Usually, a big number gives a little bit better compression ratio and + slower compression process. + +numThreads - The number of thereads. 1 or 2. The default value is 2. + Fast mode (algo = 0) can use only 1 thread. + +Out: + destLen - processed output size +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */ + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* default = (1 << 24) */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads, /* 1 or 2, default = 2 */ + int algo /* 0 = fast, 1 = normal, default = 0 for level < 5, 1 for level >= 5 */ + ); + +/* +LzmaUncompress +-------------- +In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size +Out: + destLen - processed output size + srcLen - processed input size +Returns: + SZ_OK - OK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation arror + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) +*/ + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen, + const unsigned char *props, size_t propsSize); + +#endif diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/NameMangle.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/NameMangle.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,84 @@ +/* NameMangle.h -- Name mangling to avoid linking conflicts +2009-04-15 : Marcus Geelnard : Public domain */ + +#ifndef __7Z_NAMEMANGLE_H +#define __7Z_NAMEMANGLE_H + +#ifdef LZMA_PREFIX_CTM + +/* Alloc.c */ +#define MyAlloc _ctm_MyAlloc +#define MyFree _ctm_MyFree +#ifdef _WIN32 +#define MidAlloc _ctm_MidAlloc +#define MidFree _ctm_MidFree +#define SetLargePageSize _ctm_SetLargePageSize +#define BigAlloc _ctm_BigAlloc +#define BigFree _ctm_BigFree +#endif /* _WIN32 */ + +/* LzFind.c */ +#define MatchFinder_GetPointerToCurrentPos _ctm_MatchFinder_GetPointerToCurrentPos +#define MatchFinder_GetIndexByte _ctm_MatchFinder_GetIndexByte +#define MatchFinder_GetNumAvailableBytes _ctm_MatchFinder_GetNumAvailableBytes +#define MatchFinder_ReduceOffsets _ctm_MatchFinder_ReduceOffsets +#define MatchFinder_MoveBlock _ctm_MatchFinder_MoveBlock +#define MatchFinder_NeedMove _ctm_MatchFinder_NeedMove +#define MatchFinder_ReadIfRequired _ctm_MatchFinder_ReadIfRequired +#define MatchFinder_Construct _ctm_MatchFinder_Construct +#define MatchFinder_Free _ctm_MatchFinder_Free +#define MatchFinder_Create _ctm_MatchFinder_Create +#define MatchFinder_Init _ctm_MatchFinder_Init +#define MatchFinder_Normalize3 _ctm_MatchFinder_Normalize3 +#define GetMatchesSpec1 _ctm_GetMatchesSpec1 +#define Bt3Zip_MatchFinder_GetMatches _ctm_Bt3Zip_MatchFinder_GetMatches +#define Hc3Zip_MatchFinder_GetMatches _ctm_Hc3Zip_MatchFinder_GetMatches +#define Bt3Zip_MatchFinder_Skip _ctm_Bt3Zip_MatchFinder_Skip +#define Hc3Zip_MatchFinder_Skip _ctm_Hc3Zip_MatchFinder_Skip +#define MatchFinder_CreateVTable _ctm_MatchFinder_CreateVTable + +/* LzmaDec.c */ +#define LzmaDec_InitDicAndState _ctm_LzmaDec_InitDicAndState +#define LzmaDec_Init _ctm_LzmaDec_Init +#define LzmaDec_DecodeToDic _ctm_LzmaDec_DecodeToDic +#define LzmaDec_DecodeToBuf _ctm_LzmaDec_DecodeToBuf +#define LzmaDec_FreeProbs _ctm_LzmaDec_FreeProbs +#define LzmaDec_Free _ctm_LzmaDec_Free +#define LzmaProps_Decode _ctm_LzmaProps_Decode +#define LzmaDec_AllocateProbs _ctm_LzmaDec_AllocateProbs +#define LzmaDec_Allocate _ctm_LzmaDec_Allocate +#define LzmaDecode _ctm_LzmaDecode + +/* LzmaEnc.c */ +#define LzmaEncProps_Init _ctm_LzmaEncProps_Init +#define LzmaEncProps_Normalize _ctm_LzmaEncProps_Normalize +#define LzmaEncProps_GetDictSize _ctm_LzmaEncProps_GetDictSize +#define LzmaEnc_FastPosInit _ctm_LzmaEnc_FastPosInit +#define LzmaEnc_SaveState _ctm_LzmaEnc_SaveState +#define LzmaEnc_RestoreState _ctm_LzmaEnc_RestoreState +#define LzmaEnc_SetProps _ctm_LzmaEnc_SetProps +#define LzmaEnc_InitPriceTables _ctm_LzmaEnc_InitPriceTables +#define LzmaEnc_Construct _ctm_LzmaEnc_Construct +#define LzmaEnc_Create _ctm_LzmaEnc_Create +#define LzmaEnc_FreeLits _ctm_LzmaEnc_FreeLits +#define LzmaEnc_Destruct _ctm_LzmaEnc_Destruct +#define LzmaEnc_Destroy _ctm_LzmaEnc_Destroy +#define LzmaEnc_Init _ctm_LzmaEnc_Init +#define LzmaEnc_InitPrices _ctm_LzmaEnc_InitPrices +#define LzmaEnc_PrepareForLzma2 _ctm_LzmaEnc_PrepareForLzma2 +#define LzmaEnc_MemPrepare _ctm_LzmaEnc_MemPrepare +#define LzmaEnc_Finish _ctm_LzmaEnc_Finish +#define LzmaEnc_GetNumAvailableBytes _ctm_LzmaEnc_GetNumAvailableBytes +#define LzmaEnc_GetCurBuf _ctm_LzmaEnc_GetCurBuf +#define LzmaEnc_CodeOneMemBlock _ctm_LzmaEnc_CodeOneMemBlock +#define LzmaEnc_Encode _ctm_LzmaEnc_Encode +#define LzmaEnc_WriteProperties _ctm_LzmaEnc_WriteProperties +#define LzmaEnc_MemEncode _ctm_LzmaEnc_MemEncode + +/* LzmaLib.c */ +#define LzmaCompress _ctm_LzmaCompress +#define LzmaUncompress _ctm_LzmaUncompress + +#endif /* LZMA_PREFIX_CTM */ + +#endif /* __7Z_NAMEMANGLE_H */ diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/Types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/Types.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,210 @@ +/* Types.h -- Basic types +2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#include + +#ifdef _WIN32 +#include +#endif + +#include "NameMangle.h" + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifdef _WIN32 +typedef DWORD WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_STD_CALL __stdcall +#define MY_FAST_CALL MY_NO_INLINE __fastcall + +#else + +#define MY_CDECL +#define MY_STD_CALL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ + SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#endif diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/liblzma/readme.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/liblzma/readme.txt Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,7 @@ +This is the C library implementation of LZMA compression/decompression by Igor Pavlov. + +Author: Igor Pavlov +License: Public domain +Version: 4.65 (2009-02-03) + +Some administrative adaptations for integration in OpenCTM were made by Marcus Geelnard. diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/openctm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/openctm.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,1423 @@ +//----------------------------------------------------------------------------- +// Product: OpenCTM +// File: openctm.c +// Description: API functions. +//----------------------------------------------------------------------------- +// Copyright (c) 2009-2010 Marcus Geelnard +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "openctm.h" +#include "internal.h" + + +// The C99 macro isfinite() is not supported on all platforms (specifically, +// MS Visual Studio does not support C99) +#if !defined(isfinite) && defined(_MSC_VER) + #include + #define isfinite(x) _finite(x) +#endif + + +//----------------------------------------------------------------------------- +// _ctmFreeMapList() - Free a float map list. +//----------------------------------------------------------------------------- +static void _ctmFreeMapList(_CTMcontext * self, _CTMfloatmap * aMapList) +{ + _CTMfloatmap * map, * nextMap; + map = aMapList; + while(map) + { + // Free internally allocated array (if we are in import mode) + if((self->mMode == CTM_IMPORT) && map->mValues) + free(map->mValues); + + // Free map name + if(map->mName) + free(map->mName); + + // Free file name + if(map->mFileName) + free(map->mFileName); + + nextMap = map->mNext; + free(map); + map = nextMap; + } +} + +//----------------------------------------------------------------------------- +// _ctmClearMesh() - Clear the mesh in a CTM context. +//----------------------------------------------------------------------------- +static void _ctmClearMesh(_CTMcontext * self) +{ + // Free internally allocated mesh arrays + if(self->mMode == CTM_IMPORT) + { + if(self->mVertices) + free(self->mVertices); + if(self->mIndices) + free(self->mIndices); + if(self->mNormals) + free(self->mNormals); + } + + // Clear externally assigned mesh arrays + self->mVertices = (CTMfloat *) 0; + self->mVertexCount = 0; + self->mIndices = (CTMuint *) 0; + self->mTriangleCount = 0; + self->mNormals = (CTMfloat *) 0; + + // Free UV coordinate map list + _ctmFreeMapList(self, self->mUVMaps); + self->mUVMaps = (_CTMfloatmap *) 0; + self->mUVMapCount = 0; + + // Free attribute map list + _ctmFreeMapList(self, self->mAttribMaps); + self->mAttribMaps = (_CTMfloatmap *) 0; + self->mAttribMapCount = 0; +} + +//----------------------------------------------------------------------------- +// _ctmCheckMeshIntegrity() - Check if a mesh is valid (i.e. is non-empty, and +// contains valid data). +//----------------------------------------------------------------------------- + +static CTMint _ctmCheckMeshIntegrity(_CTMcontext * self) +{ + CTMuint i; + _CTMfloatmap * map; + + // Check that we have all the mandatory data + if(!self->mVertices || !self->mIndices || (self->mVertexCount < 1) || + (self->mTriangleCount < 1)) + { + return CTM_FALSE; + } + + // Check that all indices are within range + for(i = 0; i < (self->mTriangleCount * 3); ++ i) + { + if(self->mIndices[i] >= self->mVertexCount) + { + return CTM_FALSE; + } + } + + // Check that all vertices are finite (non-NaN, non-inf) + for(i = 0; i < self->mVertexCount * 3; ++ i) + { + if(!isfinite(self->mVertices[i])) + { + return CTM_FALSE; + } + } + + // Check that all normals are finite (non-NaN, non-inf) + if(self->mNormals) + { + for(i = 0; i < self->mVertexCount * 3; ++ i) + { + if(!isfinite(self->mNormals[i])) + { + return CTM_FALSE; + } + } + } + + // Check that all UV maps are finite (non-NaN, non-inf) + map = self->mUVMaps; + while(map) + { + for(i = 0; i < self->mVertexCount * 2; ++ i) + { + if(!isfinite(map->mValues[i])) + { + return CTM_FALSE; + } + } + map = map->mNext; + } + + // Check that all attribute maps are finite (non-NaN, non-inf) + map = self->mAttribMaps; + while(map) + { + for(i = 0; i < self->mVertexCount * 4; ++ i) + { + if(!isfinite(map->mValues[i])) + { + return CTM_FALSE; + } + } + map = map->mNext; + } + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// ctmNewContext() +//----------------------------------------------------------------------------- +CTMEXPORT CTMcontext CTMCALL ctmNewContext(CTMenum aMode) +{ + _CTMcontext * self; + + // Allocate memory for the new structure + self = (_CTMcontext *) malloc(sizeof(_CTMcontext)); + + // Initialize structure (set null pointers and zero array lengths) + memset(self, 0, sizeof(_CTMcontext)); + self->mMode = aMode; + self->mError = CTM_NONE; + self->mMethod = CTM_METHOD_MG1; + self->mCompressionLevel = 1; + self->mVertexPrecision = 1.0f / 1024.0f; + self->mNormalPrecision = 1.0f / 256.0f; + + return (CTMcontext) self; +} + +//----------------------------------------------------------------------------- +// ctmFreeContext() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmFreeContext(CTMcontext aContext) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return; + + // Free all mesh resources + _ctmClearMesh(self); + + // Free the file comment + if(self->mFileComment) + free(self->mFileComment); + + // Free the context + free(self); +} + +//----------------------------------------------------------------------------- +// ctmGetError() +//----------------------------------------------------------------------------- +CTMEXPORT CTMenum CTMCALL ctmGetError(CTMcontext aContext) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + CTMenum err; + + if(!self) return CTM_INVALID_CONTEXT; + + // Get error code and reset error state + err = self->mError; + self->mError = CTM_NONE; + return err; +} + +//----------------------------------------------------------------------------- +// ctmErrorString() +//----------------------------------------------------------------------------- +CTMEXPORT const char * CTMCALL ctmErrorString(CTMenum aError) +{ + switch(aError) + { + case CTM_INVALID_CONTEXT: + return "CTM_INVALID_CONTEXT"; + case CTM_INVALID_ARGUMENT: + return "CTM_INVALID_ARGUMENT"; + case CTM_INVALID_OPERATION: + return "CTM_INVALID_OPERATION"; + case CTM_INVALID_MESH: + return "CTM_INVALID_MESH"; + case CTM_OUT_OF_MEMORY: + return "CTM_OUT_OF_MEMORY"; + case CTM_FILE_ERROR: + return "CTM_FILE_ERROR"; + case CTM_BAD_FORMAT: + return "CTM_BAD_FORMAT"; + case CTM_LZMA_ERROR: + return "CTM_LZMA_ERROR"; + case CTM_INTERNAL_ERROR: + return "CTM_INTERNAL_ERROR"; + case CTM_UNSUPPORTED_FORMAT_VERSION: + return "CTM_UNSUPPORTED_FORMAT_VERSION"; + default: + return "Unknown error code"; + } +} + +//----------------------------------------------------------------------------- +// ctmGetInteger() +//----------------------------------------------------------------------------- +CTMEXPORT CTMuint CTMCALL ctmGetInteger(CTMcontext aContext, CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return 0; + + switch(aProperty) + { + case CTM_VERTEX_COUNT: + return self->mVertexCount; + + case CTM_TRIANGLE_COUNT: + return self->mTriangleCount; + + case CTM_UV_MAP_COUNT: + return self->mUVMapCount; + + case CTM_ATTRIB_MAP_COUNT: + return self->mAttribMapCount; + + case CTM_HAS_NORMALS: + return self->mNormals ? CTM_TRUE : CTM_FALSE; + + case CTM_COMPRESSION_METHOD: + return (CTMuint) self->mMethod; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// ctmGetFloat() +//----------------------------------------------------------------------------- +CTMEXPORT CTMfloat CTMCALL ctmGetFloat(CTMcontext aContext, CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return 0.0f; + + switch(aProperty) + { + case CTM_VERTEX_PRECISION: + return self->mVertexPrecision; + + case CTM_NORMAL_PRECISION: + return self->mNormalPrecision; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return 0.0f; +} + +//----------------------------------------------------------------------------- +// ctmGetIntegerArray() +//----------------------------------------------------------------------------- +CTMEXPORT const CTMuint * CTMCALL ctmGetIntegerArray(CTMcontext aContext, + CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return (CTMuint *) 0; + + switch(aProperty) + { + case CTM_INDICES: + return self->mIndices; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return (CTMuint *) 0; +} + +//----------------------------------------------------------------------------- +// ctmGetFloatArray() +//----------------------------------------------------------------------------- +CTMEXPORT const CTMfloat * CTMCALL ctmGetFloatArray(CTMcontext aContext, + CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint i; + if(!self) return (CTMfloat *) 0; + + // Did the user request a UV map? + if((aProperty >= CTM_UV_MAP_1) && + ((CTMuint)(aProperty - CTM_UV_MAP_1) < self->mUVMapCount)) + { + map = self->mUVMaps; + i = CTM_UV_MAP_1; + while(map && (i != aProperty)) + { + map = map->mNext; + ++ i; + } + if(!map) + { + self->mError = CTM_INTERNAL_ERROR; + return (CTMfloat *) 0; + } + return map->mValues; + } + + // Did the user request an attribute map? + if((aProperty >= CTM_ATTRIB_MAP_1) && + ((CTMuint)(aProperty - CTM_ATTRIB_MAP_1) < self->mAttribMapCount)) + { + map = self->mAttribMaps; + i = CTM_ATTRIB_MAP_1; + while(map && (i != aProperty)) + { + map = map->mNext; + ++ i; + } + if(!map) + { + self->mError = CTM_INTERNAL_ERROR; + return (CTMfloat *) 0; + } + return map->mValues; + } + + switch(aProperty) + { + case CTM_VERTICES: + return self->mVertices; + + case CTM_NORMALS: + return self->mNormals; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return (CTMfloat *) 0; +} + +//----------------------------------------------------------------------------- +// ctmGetNamedUVMap() +//----------------------------------------------------------------------------- +CTMEXPORT CTMenum CTMCALL ctmGetNamedUVMap(CTMcontext aContext, + const char * aName) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint result; + if(!self) return CTM_NONE; + + map = self->mUVMaps; + result = CTM_UV_MAP_1; + while(map && (strcmp(aName, map->mName) != 0)) + { + map = map->mNext; + ++ result; + } + if(!map) + { + return CTM_NONE; + } + return result; +} + +//----------------------------------------------------------------------------- +// ctmGetUVMapString() +//----------------------------------------------------------------------------- +CTMEXPORT const char * CTMCALL ctmGetUVMapString(CTMcontext aContext, + CTMenum aUVMap, CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint i; + if(!self) return (const char *) 0; + + // Find the indicated map + map = self->mUVMaps; + i = CTM_UV_MAP_1; + while(map && (i != aUVMap)) + { + ++ i; + map = map->mNext; + } + if(!map) + { + self->mError = CTM_INVALID_ARGUMENT; + return (const char *) 0; + } + + // Get the requested string + switch(aProperty) + { + case CTM_NAME: + return (const char *) map->mName; + + case CTM_FILE_NAME: + return (const char *) map->mFileName; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return (const char *) 0; +} + +//----------------------------------------------------------------------------- +// ctmGetUVMapFloat() +//----------------------------------------------------------------------------- +CTMEXPORT CTMfloat CTMCALL ctmGetUVMapFloat(CTMcontext aContext, + CTMenum aUVMap, CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint i; + if(!self) return 0.0f; + + // Find the indicated map + map = self->mUVMaps; + i = CTM_UV_MAP_1; + while(map && (i != aUVMap)) + { + ++ i; + map = map->mNext; + } + if(!map) + { + self->mError = CTM_INVALID_ARGUMENT; + return 0.0f; + } + + // Get the requested string + switch(aProperty) + { + case CTM_PRECISION: + return map->mPrecision; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return 0.0f; +} + +//----------------------------------------------------------------------------- +// ctmGetAttribMapString() +//----------------------------------------------------------------------------- +CTMEXPORT const char * CTMCALL ctmGetAttribMapString(CTMcontext aContext, + CTMenum aAttribMap, CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint i; + if(!self) return (const char *) 0; + + // Find the indicated map + map = self->mAttribMaps; + i = CTM_ATTRIB_MAP_1; + while(map && (i != aAttribMap)) + { + ++ i; + map = map->mNext; + } + if(!map) + { + self->mError = CTM_INVALID_ARGUMENT; + return (const char *) 0; + } + + // Get the requested string + switch(aProperty) + { + case CTM_NAME: + return (const char *) map->mName; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return (const char *) 0; +} + +//----------------------------------------------------------------------------- +// ctmGetAttribMapFloat() +//----------------------------------------------------------------------------- +CTMEXPORT CTMfloat CTMCALL ctmGetAttribMapFloat(CTMcontext aContext, + CTMenum aAttribMap, CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint i; + if(!self) return 0.0f; + + // Find the indicated map + map = self->mAttribMaps; + i = CTM_ATTRIB_MAP_1; + while(map && (i != aAttribMap)) + { + ++ i; + map = map->mNext; + } + if(!map) + { + self->mError = CTM_INVALID_ARGUMENT; + return 0.0f; + } + + // Get the requested string + switch(aProperty) + { + case CTM_PRECISION: + return map->mPrecision; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return 0.0f; +} + +//----------------------------------------------------------------------------- +// ctmGetNamedAttribMap() +//----------------------------------------------------------------------------- +CTMEXPORT CTMenum CTMCALL ctmGetNamedAttribMap(CTMcontext aContext, + const char * aName) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint result; + if(!self) return CTM_NONE; + + map = self->mAttribMaps; + result = CTM_ATTRIB_MAP_1; + while(map && (strcmp(aName, map->mName) != 0)) + { + map = map->mNext; + ++ result; + } + if(!map) + { + return CTM_NONE; + } + return result; +} + +//----------------------------------------------------------------------------- +// ctmGetString() +//----------------------------------------------------------------------------- +CTMEXPORT const char * CTMCALL ctmGetString(CTMcontext aContext, + CTMenum aProperty) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return 0; + + switch(aProperty) + { + case CTM_FILE_COMMENT: + return (const char *) self->mFileComment; + + default: + self->mError = CTM_INVALID_ARGUMENT; + } + + return (const char *) 0; +} + +//----------------------------------------------------------------------------- +// ctmCompressionMethod() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmCompressionMethod(CTMcontext aContext, + CTMenum aMethod) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return; + + // You are only allowed to change compression attributes in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check arguments + if((aMethod != CTM_METHOD_RAW) && (aMethod != CTM_METHOD_MG1) && + (aMethod != CTM_METHOD_MG2)) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Set method + self->mMethod = aMethod; +} + +//----------------------------------------------------------------------------- +// ctmCompressionLevel() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmCompressionLevel(CTMcontext aContext, + CTMuint aLevel) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return; + + // You are only allowed to change compression attributes in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check arguments + if(aLevel > 9) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Set the compression level + self->mCompressionLevel = aLevel; +} + +//----------------------------------------------------------------------------- +// ctmVertexPrecision() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmVertexPrecision(CTMcontext aContext, + CTMfloat aPrecision) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return; + + // You are only allowed to change compression attributes in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check arguments + if(aPrecision <= 0.0f) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Set precision + self->mVertexPrecision = aPrecision; +} + +//----------------------------------------------------------------------------- +// ctmVertexPrecisionRel() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmVertexPrecisionRel(CTMcontext aContext, + CTMfloat aRelPrecision) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + CTMfloat avgEdgeLength, * p1, * p2; + CTMuint edgeCount, i, j; + if(!self) return; + + // You are only allowed to change compression attributes in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check arguments + if(aRelPrecision <= 0.0f) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Calculate the average edge length (Note: we actually sum up all the half- + // edges, so in a proper solid mesh all connected edges are counted twice) + avgEdgeLength = 0.0f; + edgeCount = 0; + for(i = 0; i < self->mTriangleCount; ++ i) + { + p1 = &self->mVertices[self->mIndices[i * 3 + 2] * 3]; + for(j = 0; j < 3; ++ j) + { + p2 = &self->mVertices[self->mIndices[i * 3 + j] * 3]; + avgEdgeLength += sqrtf((p2[0] - p1[0]) * (p2[0] - p1[0]) + + (p2[1] - p1[1]) * (p2[1] - p1[1]) + + (p2[2] - p1[2]) * (p2[2] - p1[2])); + p1 = p2; + ++ edgeCount; + } + } + if(edgeCount == 0) + { + self->mError = CTM_INVALID_MESH; + return; + } + avgEdgeLength /= (CTMfloat) edgeCount; + + // Set precision + self->mVertexPrecision = aRelPrecision * avgEdgeLength; +} + +//----------------------------------------------------------------------------- +// ctmNormalPrecision() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmNormalPrecision(CTMcontext aContext, + CTMfloat aPrecision) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return; + + // You are only allowed to change compression attributes in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check arguments + if(aPrecision <= 0.0f) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Set precision + self->mNormalPrecision = aPrecision; +} + +//----------------------------------------------------------------------------- +// ctmUVCoordPrecision() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmUVCoordPrecision(CTMcontext aContext, + CTMenum aUVMap, CTMfloat aPrecision) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint i; + if(!self) return; + + // You are only allowed to change compression attributes in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check arguments + if(aPrecision <= 0.0f) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Find the indicated map + map = self->mUVMaps; + i = CTM_UV_MAP_1; + while(map && (i != aUVMap)) + { + ++ i; + map = map->mNext; + } + if(!map) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Update the precision + map->mPrecision = aPrecision; +} + +//----------------------------------------------------------------------------- +// ctmAttribPrecision() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmAttribPrecision(CTMcontext aContext, + CTMenum aAttribMap, CTMfloat aPrecision) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + CTMuint i; + if(!self) return; + + // You are only allowed to change compression attributes in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check arguments + if(aPrecision <= 0.0f) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Find the indicated map + map = self->mAttribMaps; + i = CTM_ATTRIB_MAP_1; + while(map && (i != aAttribMap)) + { + ++ i; + map = map->mNext; + } + if(!map) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Update the precision + map->mPrecision = aPrecision; +} + +//----------------------------------------------------------------------------- +// ctmFileComment() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmFileComment(CTMcontext aContext, + const char * aFileComment) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + int len; + if(!self) return; + + // You are only allowed to change file attributes in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Free the old comment string, if necessary + if(self->mFileComment) + { + free(self->mFileComment); + self->mFileComment = (char *) 0; + } + + // Get length of string (if empty, do nothing) + if(!aFileComment) + return; + len = strlen(aFileComment); + if(!len) + return; + + // Copy the string + self->mFileComment = (char *) malloc(len + 1); + if(!self->mFileComment) + { + self->mError = CTM_OUT_OF_MEMORY; + return; + } + strcpy(self->mFileComment, aFileComment); +} + +//----------------------------------------------------------------------------- +// ctmDefineMesh() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmDefineMesh(CTMcontext aContext, + const CTMfloat * aVertices, CTMuint aVertexCount, const CTMuint * aIndices, + CTMuint aTriangleCount, const CTMfloat * aNormals) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + if(!self) return; + + // You are only allowed to (re)define the mesh in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check arguments + if(!aVertices || !aIndices || !aVertexCount || !aTriangleCount) + { + self->mError = CTM_INVALID_ARGUMENT; + return; + } + + // Clear the old mesh, if any + _ctmClearMesh(self); + + // Set vertex array pointer + self->mVertices = (CTMfloat *) aVertices; + self->mVertexCount = aVertexCount; + + // Set index array pointer + self->mIndices = (CTMuint *) aIndices; + self->mTriangleCount = aTriangleCount; + + // Set normal array pointer + self->mNormals = (CTMfloat *) aNormals; +} + +//----------------------------------------------------------------------------- +// _ctmAddFloatMap() +//----------------------------------------------------------------------------- +static _CTMfloatmap * _ctmAddFloatMap(_CTMcontext * self, + const CTMfloat * aValues, const char * aName, const char * aFileName, + _CTMfloatmap ** aList) +{ + _CTMfloatmap * map; + CTMuint len; + + // Allocate memory for a new map list item and append it to the list + if(!*aList) + { + *aList = (_CTMfloatmap *) malloc(sizeof(_CTMfloatmap)); + map = *aList; + } + else + { + map = *aList; + while(map->mNext) + map = map->mNext; + map->mNext = (_CTMfloatmap *) malloc(sizeof(_CTMfloatmap)); + map = map->mNext; + } + if(!map) + { + self->mError = CTM_OUT_OF_MEMORY; + return (_CTMfloatmap *) 0; + } + + // Init the map item + memset(map, 0, sizeof(_CTMfloatmap)); + map->mPrecision = 1.0f / 1024.0f; + map->mValues = (CTMfloat *) aValues; + + // Set name of the map + if(aName) + { + // Get length of string (if empty, do nothing) + len = strlen(aName); + if(len) + { + // Copy the string + map->mName = (char *) malloc(len + 1); + if(!map->mName) + { + self->mError = CTM_OUT_OF_MEMORY; + free(map); + return (_CTMfloatmap *) 0; + } + strcpy(map->mName, aName); + } + } + + // Set file name reference for the map + if(aFileName) + { + // Get length of string (if empty, do nothing) + len = strlen(aFileName); + if(len) + { + // Copy the string + map->mFileName = (char *) malloc(len + 1); + if(!map->mFileName) + { + self->mError = CTM_OUT_OF_MEMORY; + if(map->mName) + free(map->mName); + free(map); + return (_CTMfloatmap *) 0; + } + strcpy(map->mFileName, aFileName); + } + } + + return map; +} + +//----------------------------------------------------------------------------- +// ctmAddUVMap() +//----------------------------------------------------------------------------- +CTMEXPORT CTMenum CTMCALL ctmAddUVMap(CTMcontext aContext, + const CTMfloat * aUVCoords, const char * aName, const char * aFileName) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + if(!self) return CTM_NONE; + + // Add a new UV map to the UV map list + map = _ctmAddFloatMap(self, aUVCoords, aName, aFileName, &self->mUVMaps); + if(!map) + return CTM_NONE; + else + { + // The default UV coordinate precision is 2^-12 + map->mPrecision = 1.0f / 4096.0f; + ++ self->mUVMapCount; + return CTM_UV_MAP_1 + self->mUVMapCount - 1; + } +} + +//----------------------------------------------------------------------------- +// ctmAddAttribMap() +//----------------------------------------------------------------------------- +CTMEXPORT CTMenum CTMCALL ctmAddAttribMap(CTMcontext aContext, + const CTMfloat * aAttribValues, const char * aName) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + _CTMfloatmap * map; + if(!self) return CTM_NONE; + + // Add a new attribute map to the attribute map list + map = _ctmAddFloatMap(self, aAttribValues, aName, (const char *) 0, + &self->mAttribMaps); + if(!map) + return CTM_NONE; + else + { + // The default vertex attribute precision is 2^-8 + map->mPrecision = 1.0f / 256.0f; + ++ self->mAttribMapCount; + return CTM_ATTRIB_MAP_1 + self->mAttribMapCount - 1; + } +} + +//----------------------------------------------------------------------------- +// _ctmDefaultRead() +//----------------------------------------------------------------------------- +static CTMuint CTMCALL _ctmDefaultRead(void * aBuf, CTMuint aCount, + void * aUserData) +{ + return (CTMuint) fread(aBuf, 1, (size_t) aCount, (FILE *) aUserData); +} + +//----------------------------------------------------------------------------- +// ctmLoad() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmLoad(CTMcontext aContext, const char * aFileName) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + FILE * f; + if(!self) return; + + // You are only allowed to load data in import mode + if(self->mMode != CTM_IMPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Open file stream + f = fopen(aFileName, "rb"); + if(!f) + { + self->mError = CTM_FILE_ERROR; + return; + } + + // Load the file + ctmLoadCustom(self, _ctmDefaultRead, (void *) f); + + // Close file stream + fclose(f); +} + +//----------------------------------------------------------------------------- +// _ctmAllocateFloatMaps() +//----------------------------------------------------------------------------- +static CTMuint _ctmAllocateFloatMaps(_CTMcontext * self, + _CTMfloatmap ** aMapListPtr, CTMuint aCount, CTMuint aChannels) +{ + _CTMfloatmap ** mapListPtr; + CTMuint i, size; + + mapListPtr = aMapListPtr; + for(i = 0; i < aCount; ++ i) + { + // Allocate & clear memory for this map + *mapListPtr = (_CTMfloatmap *) malloc(sizeof(_CTMfloatmap)); + if(!*mapListPtr) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + memset(*mapListPtr, 0, sizeof(_CTMfloatmap)); + + // Allocate & clear memory for the float array + size = aChannels * sizeof(CTMfloat) * self->mVertexCount; + (*mapListPtr)->mValues = (CTMfloat *) malloc(size); + if(!(*mapListPtr)->mValues) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + memset((*mapListPtr)->mValues, 0, size); + + // Next map... + mapListPtr = &(*mapListPtr)->mNext; + } + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// ctmLoadCustom() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmLoadCustom(CTMcontext aContext, CTMreadfn aReadFn, + void * aUserData) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + CTMuint formatVersion, flags, method; + if(!self) return; + + // You are only allowed to load data in import mode + if(self->mMode != CTM_IMPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Initialize stream + self->mReadFn = aReadFn; + self->mUserData = aUserData; + + // Clear any old mesh arrays + _ctmClearMesh(self); + + // Read header from stream + if(_ctmStreamReadUINT(self) != FOURCC("OCTM")) + { + self->mError = CTM_BAD_FORMAT; + return; + } + formatVersion = _ctmStreamReadUINT(self); + if(formatVersion != _CTM_FORMAT_VERSION) + { + self->mError = CTM_UNSUPPORTED_FORMAT_VERSION; + return; + } + method = _ctmStreamReadUINT(self); + if(method == FOURCC("RAW\0")) + self->mMethod = CTM_METHOD_RAW; + else if(method == FOURCC("MG1\0")) + self->mMethod = CTM_METHOD_MG1; + else if(method == FOURCC("MG2\0")) + self->mMethod = CTM_METHOD_MG2; + else + { + self->mError = CTM_BAD_FORMAT; + return; + } + self->mVertexCount = _ctmStreamReadUINT(self); + if(self->mVertexCount == 0) + { + self->mError = CTM_BAD_FORMAT; + return; + } + self->mTriangleCount = _ctmStreamReadUINT(self); + if(self->mTriangleCount == 0) + { + self->mError = CTM_BAD_FORMAT; + return; + } + self->mUVMapCount = _ctmStreamReadUINT(self); + self->mAttribMapCount = _ctmStreamReadUINT(self); + flags = _ctmStreamReadUINT(self); + _ctmStreamReadSTRING(self, &self->mFileComment); + + // Allocate memory for the mesh arrays + self->mVertices = (CTMfloat *) malloc(self->mVertexCount * sizeof(CTMfloat) * 3); + if(!self->mVertices) + { + self->mError = CTM_OUT_OF_MEMORY; + return; + } + self->mIndices = (CTMuint *) malloc(self->mTriangleCount * sizeof(CTMuint) * 3); + if(!self->mIndices) + { + _ctmClearMesh(self); + self->mError = CTM_OUT_OF_MEMORY; + return; + } + if(flags & _CTM_HAS_NORMALS_BIT) + { + self->mNormals = (CTMfloat *) malloc(self->mVertexCount * sizeof(CTMfloat) * 3); + if(!self->mNormals) + { + _ctmClearMesh(self); + self->mError = CTM_OUT_OF_MEMORY; + return; + } + } + + // Allocate memory for the UV and attribute maps (if any) + if(!_ctmAllocateFloatMaps(self, &self->mUVMaps, self->mUVMapCount, 2)) + { + _ctmClearMesh(self); + self->mError = CTM_OUT_OF_MEMORY; + return; + } + if(!_ctmAllocateFloatMaps(self, &self->mAttribMaps, self->mAttribMapCount, 4)) + { + _ctmClearMesh(self); + self->mError = CTM_OUT_OF_MEMORY; + return; + } + + // Uncompress from stream + switch(self->mMethod) + { + case CTM_METHOD_RAW: + _ctmUncompressMesh_RAW(self); + break; + + case CTM_METHOD_MG1: + _ctmUncompressMesh_MG1(self); + break; + + case CTM_METHOD_MG2: + _ctmUncompressMesh_MG2(self); + break; + + default: + self->mError = CTM_INTERNAL_ERROR; + } + + // Check mesh integrity + if(!_ctmCheckMeshIntegrity(self)) + { + self->mError = CTM_INVALID_MESH; + return; + } +} + +//----------------------------------------------------------------------------- +// _ctmDefaultWrite() +//----------------------------------------------------------------------------- +static CTMuint CTMCALL _ctmDefaultWrite(const void * aBuf, CTMuint aCount, + void * aUserData) +{ + return (CTMuint) fwrite(aBuf, 1, (size_t) aCount, (FILE *) aUserData); +} + +//----------------------------------------------------------------------------- +// ctmSave() +//----------------------------------------------------------------------------- +CTMEXPORT void CTMCALL ctmSave(CTMcontext aContext, const char * aFileName) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + FILE * f; + if(!self) return; + + // You are only allowed to save data in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Open file stream + f = fopen(aFileName, "wb"); + if(!f) + { + self->mError = CTM_FILE_ERROR; + return; + } + + // Save the file + ctmSaveCustom(self, _ctmDefaultWrite, (void *) f); + + // Close file stream + fclose(f); +} + +//----------------------------------------------------------------------------- +// ctmSaveCustom() +//----------------------------------------------------------------------------- +void CTMCALL ctmSaveCustom(CTMcontext aContext, CTMwritefn aWriteFn, + void * aUserData) +{ + _CTMcontext * self = (_CTMcontext *) aContext; + CTMuint flags; + if(!self) return; + + // You are only allowed to save data in export mode + if(self->mMode != CTM_EXPORT) + { + self->mError = CTM_INVALID_OPERATION; + return; + } + + // Check mesh integrity + if(!_ctmCheckMeshIntegrity(self)) + { + self->mError = CTM_INVALID_MESH; + return; + } + + // Initialize stream + self->mWriteFn = aWriteFn; + self->mUserData = aUserData; + + // Determine flags + flags = 0; + if(self->mNormals) + flags |= _CTM_HAS_NORMALS_BIT; + + // Write header to stream + _ctmStreamWrite(self, (void *) "OCTM", 4); + _ctmStreamWriteUINT(self, _CTM_FORMAT_VERSION); + switch(self->mMethod) + { + case CTM_METHOD_RAW: + _ctmStreamWrite(self, (void *) "RAW\0", 4); + break; + + case CTM_METHOD_MG1: + _ctmStreamWrite(self, (void *) "MG1\0", 4); + break; + + case CTM_METHOD_MG2: + _ctmStreamWrite(self, (void *) "MG2\0", 4); + break; + + default: + self->mError = CTM_INTERNAL_ERROR; + return; + } + _ctmStreamWriteUINT(self, self->mVertexCount); + _ctmStreamWriteUINT(self, self->mTriangleCount); + _ctmStreamWriteUINT(self, self->mUVMapCount); + _ctmStreamWriteUINT(self, self->mAttribMapCount); + _ctmStreamWriteUINT(self, flags); + _ctmStreamWriteSTRING(self, self->mFileComment); + + // Compress to stream + switch(self->mMethod) + { + case CTM_METHOD_RAW: + _ctmCompressMesh_RAW(self); + break; + + case CTM_METHOD_MG1: + _ctmCompressMesh_MG1(self); + break; + + case CTM_METHOD_MG2: + _ctmCompressMesh_MG2(self); + break; + + default: + self->mError = CTM_INTERNAL_ERROR; + return; + } +} diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/openctm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/openctm.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,655 @@ +//----------------------------------------------------------------------------- +// Product: OpenCTM +// File: openctm.h +// Description: OpenCTM API definition. +//----------------------------------------------------------------------------- +// Copyright (c) 2009-2010 Marcus Geelnard +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +//----------------------------------------------------------------------------- + +#ifndef __OPENCTM_H_ +#define __OPENCTM_H_ + +/*! @mainpage OpenCTM API Reference + * + * @section intro_sec Introduction + * + * OpenCTM is an open file format for storing compressed triangle meshes. + * In order to easily read and write OpenCTM files (usually suffixed .ctm) an + * API (Application Program Interface) is provided that can easily be used from + * most modern programming languages. + * + * The OpenCTM functionality itself is written in highly portable standard C + * (C99). + * + * @section usage_sec Usage + * + * For information about how to use the OpenCTM API, see openctm.h. + * + * For information about the C++ wrapper classes, see CTMimporter and + * CTMexporter. + * + * @section example_sec Example usage + * + * @subsection example_load_sec Loading a CTM file + * + * Here is a simple example of loading a CTM file: + * + * @code + * CTMcontext context; + * CTMuint vertCount, triCount, * indices; + * CTMfloat * vertices; + * + * // Create a new context + * context = ctmNewContext(CTM_IMPORT); + * + * // Load the OpenCTM file + * ctmLoad(context, "mymesh.ctm"); + * if(ctmGetError(context) == CTM_NONE) + * { + * // Access the mesh data + * vertCount = ctmGetInteger(context, CTM_VERTEX_COUNT); + * vertices = ctmGetFloatArray(context, CTM_VERTICES); + * triCount = ctmGetInteger(context, CTM_TRIANGLE_COUNT); + * indices = ctmGetIntegerArray(context, CTM_INDICES); + * + * // Deal with the mesh (e.g. transcode it to our internal representation) + * // ... + * } + * + * // Free the context + * ctmFreeContext(context); + * @endcode + * + * @subsection example_create_sec Creating a CTM file + * + * Here is a simple example of creating a CTM file: + * + * @code + * CTMcontext context; + * CTMuint vertCount, triCount, * indices; + * CTMfloat * vertices; + * + * // Create our mesh in memory + * vertCount = 100; + * triCount = 120; + * vertices = (CTMfloat *) malloc(3 * sizeof(CTMfloat) * vertCount); + * indices = (CTMuint *) malloc(3 * sizeof(CTMuint) * triCount); + * // ... + * + * // Create a new context + * context = ctmNewContext(CTM_EXPORT); + * + * // Define our mesh representation to OpenCTM (store references to it in + * // the context) + * ctmDefineMesh(context, vertices, vertCount, indices, triCount, NULL); + * + * // Save the OpenCTM file + * ctmSave(context, "mymesh.ctm"); + * + * // Free the context + * ctmFreeContext(context); + * + * // Free our mesh + * free(indices); + * free(vertices); + * @endcode + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +// Declare calling conventions etc. +#if defined(WIN32) || defined(_WIN32) + // Windows + #if defined(OPENCTM_STATIC) + #define CTMEXPORT + #else + #if defined(OPENCTM_BUILD) + #define CTMEXPORT __declspec(dllexport) + #else + #define CTMEXPORT __declspec(dllimport) + #endif + #endif + #if defined(__MINGW32__) + #define CTMCALL __attribute__ ((__stdcall__)) + #elif (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) + #define CTMCALL __stdcall + #else + #define CTMCALL + #endif +#else + // Unix + #if !defined(OPENCTM_STATIC) && !defined(OPENCTM_BUILD) + #define CTMEXPORT extern + #else + #if defined(OPENCTM_BUILD) && defined(__GNUC__) && (__GNUC__ >= 4) + #define CTMEXPORT __attribute__ ((visibility("default"))) + #else + #define CTMEXPORT + #endif + #endif + #define CTMCALL +#endif + + +// Get system specific type definitions for sized integers. We use the C99 +// standard stdint.h for this. +#ifdef _MSC_VER + // MS Visual Studio does not support C99 + typedef int int32_t; + typedef unsigned int uint32_t; +#else + #include +#endif + + +/// OpenCTM API version (1.0). +#define CTM_API_VERSION 0x00000100 + +/// Boolean TRUE. +#define CTM_TRUE 1 + +/// Boolean FALSE. +#define CTM_FALSE 0 + +/// Single precision floating point type (IEEE 754 32 bits wide). +typedef float CTMfloat; + +/// Signed integer (32 bits wide). +typedef int32_t CTMint; + +/// Unsigned integer (32 bits wide). +typedef uint32_t CTMuint; + +/// OpenCTM context handle. +typedef void * CTMcontext; + +/// OpenCTM specific enumerators. +/// @note For the information query functions, it is an error to query a value +/// of the wrong type (e.g. to query a string value with the +/// ctmGetInteger() function). +typedef enum { + // Error codes (see ctmGetError()) + CTM_NONE = 0x0000, ///< No error has occured (everything is OK). + /// Also used as an error return value for + /// functions that should return a CTMenum + /// value. + CTM_INVALID_CONTEXT = 0x0001, ///< The OpenCTM context was invalid (e.g. NULL). + CTM_INVALID_ARGUMENT = 0x0002, ///< A function argument was invalid. + CTM_INVALID_OPERATION = 0x0003, ///< The operation is not allowed. + CTM_INVALID_MESH = 0x0004, ///< The mesh was invalid (e.g. no vertices). + CTM_OUT_OF_MEMORY = 0x0005, ///< Not enough memory to proceed. + CTM_FILE_ERROR = 0x0006, ///< File I/O error. + CTM_BAD_FORMAT = 0x0007, ///< File format error (e.g. unrecognized format or corrupted file). + CTM_LZMA_ERROR = 0x0008, ///< An error occured within the LZMA library. + CTM_INTERNAL_ERROR = 0x0009, ///< An internal error occured (indicates a bug). + CTM_UNSUPPORTED_FORMAT_VERSION = 0x000A, ///< Unsupported file format version. + + // OpenCTM context modes + CTM_IMPORT = 0x0101, ///< The OpenCTM context will be used for importing data. + CTM_EXPORT = 0x0102, ///< The OpenCTM context will be used for exporting data. + + // Compression methods + CTM_METHOD_RAW = 0x0201, ///< Just store the raw data. + CTM_METHOD_MG1 = 0x0202, ///< Lossless compression (floating point). + CTM_METHOD_MG2 = 0x0203, ///< Lossless compression (fixed point). + + // Context queries + CTM_VERTEX_COUNT = 0x0301, ///< Number of vertices in the mesh (integer). + CTM_TRIANGLE_COUNT = 0x0302, ///< Number of triangles in the mesh (integer). + CTM_HAS_NORMALS = 0x0303, ///< CTM_TRUE if the mesh has normals (integer). + CTM_UV_MAP_COUNT = 0x0304, ///< Number of UV coordinate sets (integer). + CTM_ATTRIB_MAP_COUNT = 0x0305, ///< Number of custom attribute sets (integer). + CTM_VERTEX_PRECISION = 0x0306, ///< Vertex precision - for MG2 (float). + CTM_NORMAL_PRECISION = 0x0307, ///< Normal precision - for MG2 (float). + CTM_COMPRESSION_METHOD = 0x0308, ///< Compression method (integer). + CTM_FILE_COMMENT = 0x0309, ///< File comment (string). + + // UV/attribute map queries + CTM_NAME = 0x0501, ///< Unique name (UV/attrib map string). + CTM_FILE_NAME = 0x0502, ///< File name reference (UV map string). + CTM_PRECISION = 0x0503, ///< Value precision (UV/attrib map float). + + // Array queries + CTM_INDICES = 0x0601, ///< Triangle indices (integer array). + CTM_VERTICES = 0x0602, ///< Vertex point coordinates (float array). + CTM_NORMALS = 0x0603, ///< Per vertex normals (float array). + CTM_UV_MAP_1 = 0x0700, ///< Per vertex UV map 1 (float array). + CTM_UV_MAP_2 = 0x0701, ///< Per vertex UV map 2 (float array). + CTM_UV_MAP_3 = 0x0702, ///< Per vertex UV map 3 (float array). + CTM_UV_MAP_4 = 0x0703, ///< Per vertex UV map 4 (float array). + CTM_UV_MAP_5 = 0x0704, ///< Per vertex UV map 5 (float array). + CTM_UV_MAP_6 = 0x0705, ///< Per vertex UV map 6 (float array). + CTM_UV_MAP_7 = 0x0706, ///< Per vertex UV map 7 (float array). + CTM_UV_MAP_8 = 0x0707, ///< Per vertex UV map 8 (float array). + CTM_ATTRIB_MAP_1 = 0x0800, ///< Per vertex attribute map 1 (float array). + CTM_ATTRIB_MAP_2 = 0x0801, ///< Per vertex attribute map 2 (float array). + CTM_ATTRIB_MAP_3 = 0x0802, ///< Per vertex attribute map 3 (float array). + CTM_ATTRIB_MAP_4 = 0x0803, ///< Per vertex attribute map 4 (float array). + CTM_ATTRIB_MAP_5 = 0x0804, ///< Per vertex attribute map 5 (float array). + CTM_ATTRIB_MAP_6 = 0x0805, ///< Per vertex attribute map 6 (float array). + CTM_ATTRIB_MAP_7 = 0x0806, ///< Per vertex attribute map 7 (float array). + CTM_ATTRIB_MAP_8 = 0x0807 ///< Per vertex attribute map 8 (float array). +} CTMenum; + +/// Stream read() function pointer. +/// @param[in] aBuf Pointer to the memory buffer to which data should be read. +/// @param[in] aCount The number of bytes to read. +/// @param[in] aUserData The custom user data that was passed to the +/// ctmLoadCustom() function. +/// @return The number of bytes actually read (if this is less than aCount, it +/// indicates that an error occured or the end of file was reached +/// before all bytes were read). +typedef CTMuint (CTMCALL * CTMreadfn)(void * aBuf, CTMuint aCount, void * aUserData); + +/// Stream write() function pointer. +/// @param[in] aBuf Pointer to the memory buffer from which data should be written. +/// @param[in] aCount The number of bytes to write. +/// @param[in] aUserData The custom user data that was passed to the +/// ctmSaveCustom() function. +/// @return The number of bytes actually written (if this is less than aCount, it +/// indicates that an error occured). +typedef CTMuint (CTMCALL * CTMwritefn)(const void * aBuf, CTMuint aCount, void * aUserData); + +/// Create a new OpenCTM context. The context is used for all subsequent +/// OpenCTM function calls. Several contexts can coexist at the same time. +/// @param[in] aMode An OpenCTM context mode. Set this to CTM_IMPORT if the +/// context will be used for importing data, or set it to CTM_EXPORT +/// if it will be used for exporting data. +/// @return An OpenCTM context handle (or NULL if no context could be created). +CTMEXPORT CTMcontext CTMCALL ctmNewContext(CTMenum aMode); + +/// Free an OpenCTM context. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @see ctmNewContext() +CTMEXPORT void CTMCALL ctmFreeContext(CTMcontext aContext); + +/// Returns the latest error. Calling this function will return the last +/// produced error code, or CTM_NO_ERROR (zero) if no error has occured since +/// the last call to ctmGetError(). When this function is called, the internal +/// error varibale will be reset to CTM_NONE. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @return An OpenCTM error code. +/// @see CTMenum +CTMEXPORT CTMenum CTMCALL ctmGetError(CTMcontext aContext); + +/// Converts an OpenCTM error code to a zero-terminated string. +/// @param[in] aError An OpenCTM error code, as returned by ctmGetError(). +/// @return A zero terminated string that describes the error. For instance, +/// if \c aError is CTM_INVALID_OPERATION, then the return value will +/// be "CTM_INVALID_OPERATION". +/// @see CTMenum +CTMEXPORT const char * CTMCALL ctmErrorString(CTMenum aError); + +/// Get information about an OpenCTM context. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aProperty Which property to return. +/// @return An integer value, representing the OpenCTM context property given +/// by \c aProperty. +/// @see CTMenum +CTMEXPORT CTMuint CTMCALL ctmGetInteger(CTMcontext aContext, CTMenum aProperty); + +/// Get information about an OpenCTM context. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aProperty Which property to return. +/// @return A floating point value, representing the OpenCTM context property +/// given by \c aProperty. +/// @see CTMenum +CTMEXPORT CTMfloat CTMCALL ctmGetFloat(CTMcontext aContext, CTMenum aProperty); + +/// Get an integer array from an OpenCTM context. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aProperty Which array to return. +/// @return An integer array. If the requested array does not exist, or +/// if \c aProperty does not indicate an integer array, the function +/// returns NULL. +/// @note The array is only valid as long as the OpenCTM context is valid, or +/// until the corresponding array changes within the OpenCTM context. +/// Trying to access an invalid array will result in undefined +/// behaviour. Therefor it is recommended that the array is copied to +/// a new variable if it is to be used other than directly after the call +/// to ctmGetIntegerArray(). +/// @see CTMenum +CTMEXPORT const CTMuint * CTMCALL ctmGetIntegerArray(CTMcontext aContext, + CTMenum aProperty); + +/// Get a floating point array from an OpenCTM context. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aProperty Which array to return. +/// @return A floating point array. If the requested array does not exist, or +/// if \c aProperty does not indicate a float array, the function +/// returns NULL. +/// @note The array is only valid as long as the OpenCTM context is valid, or +/// until the corresponding array changes within the OpenCTM context. +/// Trying to access an invalid array will result in undefined +/// behaviour. Therefor it is recommended that the array is copied to +/// a new variable if it is to be used other than directly after the call +/// to ctmGetFloatArray(). +/// @see CTMenum +CTMEXPORT const CTMfloat * CTMCALL ctmGetFloatArray(CTMcontext aContext, + CTMenum aProperty); + +/// Get a reference to the named UV map. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aName The name of the UV map that should be returned. +/// @return A reference to a UV map. If the UV map was found, a value of +/// CTM_UV_MAP_1 or higher is returned, otherwise CTM_NONE is +/// returned. +CTMEXPORT CTMenum CTMCALL ctmGetNamedUVMap(CTMcontext aContext, + const char * aName); + +/// Get information about a UV map. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aUVMap Which UV map to query (CTM_UV_MAP_1 or higher). +/// @param[in] aProperty Which UV map property to return. +/// @return A string value, representing the UV map property given +/// by \c aProperty. +/// @note The string is only valid as long as the UV map within the OpenCTM +/// context is valid. Trying to access an invalid string will result in +/// undefined behaviour. Therefor it is recommended that the string is +/// copied to a new variable if it is to be used other than directly after +/// the call to ctmGetUVMapString(). +/// @see CTMenum +CTMEXPORT const char * CTMCALL ctmGetUVMapString(CTMcontext aContext, + CTMenum aUVMap, CTMenum aProperty); + +/// Get information about a UV map. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aUVMap Which UV map to query (CTM_UV_MAP_1 or higher). +/// @param[in] aProperty Which UV map property to return. +/// @return A floating point value, representing the UV map property given +/// by \c aProperty. +/// @see CTMenum +CTMEXPORT CTMfloat CTMCALL ctmGetUVMapFloat(CTMcontext aContext, + CTMenum aUVMap, CTMenum aProperty); + +/// Get a reference to the named vertex attribute map. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aName The name of the attribute map that should be returned. +/// @return A reference to an attribute map. If the attribute map was found, +/// a value of CTM_ATTRIB_MAP_1 or higher is returned, otherwise +/// CTM_NONE is returned. +CTMEXPORT CTMenum CTMCALL ctmGetNamedAttribMap(CTMcontext aContext, + const char * aName); + +/// Get information about a vertex attribute map. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aAttribMap Which vertex attribute map to query (CTM_ATTRIB_MAP_1 +/// or higher). +/// @param[in] aProperty Which vertex attribute map property to return. +/// @return A string value, representing the vertex attribute map property given +/// by \c aProperty. +/// @note The string is only valid as long as the vertex attribute map within +/// the OpenCTM context is valid. Trying to access an invalid string will +/// result in undefined behaviour. Therefor it is recommended that the +/// string is copied to a new variable if it is to be used other than +/// directly after the call to ctmGetAttribMapString(). +/// @see CTMenum +CTMEXPORT const char * CTMCALL ctmGetAttribMapString(CTMcontext aContext, + CTMenum aAttribMap, CTMenum aProperty); + +/// Get information about a vertex attribute map. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aAttribMap Which vertex attribute map to query (CTM_ATTRIB_MAP_1 +/// or higher). +/// @param[in] aProperty Which vertex attribute map property to return. +/// @return A floating point value, representing the vertex attribute map +/// property given by \c aProperty. +/// @see CTMenum +CTMEXPORT CTMfloat CTMCALL ctmGetAttribMapFloat(CTMcontext aContext, + CTMenum aAttribMap, CTMenum aProperty); + +/// Get information about an OpenCTM context. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aProperty Which property to return. +/// @return A string value, representing the OpenCTM context property given +/// by \c aProperty. +/// @note The string is only valid as long as the OpenCTM context is valid, or +/// until the corresponding string changes within the OpenCTM context +/// (e.g. calling ctmFileComment() invalidates the CTM_FILE_COMMENT +/// string). Trying to access an invalid string will result in undefined +/// behaviour. Therefor it is recommended that the string is copied to +/// a new variable if it is to be used other than directly after the call +/// to ctmGetString(). +/// @see CTMenum +CTMEXPORT const char * CTMCALL ctmGetString(CTMcontext aContext, + CTMenum aProperty); + +/// Set which compression method to use for the given OpenCTM context. +/// The selected compression method will be used when calling the ctmSave() +/// function. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aMethod Which compression method to use: CTM_METHOD_RAW, +/// CTM_METHOD_MG1 or CTM_METHOD_MG2 (the default method is +/// CTM_METHOD_MG1). +/// @see CTM_METHOD_RAW, CTM_METHOD_MG1, CTM_METHOD_MG2 +CTMEXPORT void CTMCALL ctmCompressionMethod(CTMcontext aContext, + CTMenum aMethod); + +/// Set which LZMA compression level to use for the given OpenCTM context. +/// The compression level can be between 0 (fastest) and 9 (best). The higher +/// the compression level, the more memory is required for compression and +/// decompression. The default compression level is 1. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aLevel Which compression level to use (0 to 9). +CTMEXPORT void CTMCALL ctmCompressionLevel(CTMcontext aContext, + CTMuint aLevel); + +/// Set the vertex coordinate precision (only used by the MG2 compression +/// method). +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aPrecision Fixed point precision. For instance, if this value is +/// 0.001, all vertex coordinates will be rounded to three decimals. +/// The default vertex coordinate precision is 2^-10 ~= 0.00098. +CTMEXPORT void CTMCALL ctmVertexPrecision(CTMcontext aContext, + CTMfloat aPrecision); + +/// Set the vertex coordinate precision, relative to the mesh dimensions (only +/// used by the MG2 compression method). +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aRelPrecision Relative precision. This factor is multiplied by the +/// average triangle edge length in the mesh in order to obtain the +/// final, fixed point precision. For instance, if aRelPrecision is +/// 0.01, and the average edge length is 3.7, then the fixed point +/// precision is set to 0.037. +/// @note The mesh must have been defined using the ctmDefineMesh() function +/// before calling this function. +/// @see ctmVertexPrecision(). +CTMEXPORT void CTMCALL ctmVertexPrecisionRel(CTMcontext aContext, + CTMfloat aRelPrecision); + +/// Set the normal precision (only used by the MG2 compression method). The +/// normal is represented in spherical coordinates in the MG2 compression +/// method, and the normal precision controls the angular and radial resolution. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aPrecision Fixed point precision. For the angular information, +/// this value represents the angular precision. For the radial +/// information, this value is the linear resolution. For instance, +/// 0.01 means that the circle is divided into 100 steps, and the +/// normal magnitude is rounded to 2 decimals. The default normal +/// precision is 2^-8 ~= 0.0039. +CTMEXPORT void CTMCALL ctmNormalPrecision(CTMcontext aContext, + CTMfloat aPrecision); + +/// Set the coordinate precision for the specified UV map (only used by the +/// MG2 compression method). +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aUVMap A UV map specifier for a defined UV map +/// (CTM_UV_MAP_1, ...). +/// @param[in] aPrecision Fixed point precision. For instance, if this value is +/// 0.001, all UV coordinates will be rounded to three decimals. +/// The default UV coordinate precision is 2^-12 ~= 0.00024. +/// @see ctmAddUVMap(). +CTMEXPORT void CTMCALL ctmUVCoordPrecision(CTMcontext aContext, + CTMenum aUVMap, CTMfloat aPrecision); + +/// Set the attribute value precision for the specified attribute map (only +/// used by the MG2 compression method). +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aAttribMap An attribute map specifier for a defined attribute map +/// (CTM_ATTRIB_MAP_1, ...). +/// @param[in] aPrecision Fixed point precision. For instance, if this value is +/// 0.001, all attribute values will be rounded to three decimals. +/// If the attributes represent integer values, set the precision +/// to 1.0. The default attribute precision is 2^-8 ~= 0.0039. +/// @see ctmAddAttribMap(). +CTMEXPORT void CTMCALL ctmAttribPrecision(CTMcontext aContext, + CTMenum aAttribMap, CTMfloat aPrecision); + +/// Set the file comment for the given OpenCTM context. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aFileComment The file comment (zero terminated UTF-8 string). +CTMEXPORT void CTMCALL ctmFileComment(CTMcontext aContext, + const char * aFileComment); + +/// Define a triangle mesh. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aVertices An array of vertices (three consecutive floats make +/// one vertex). +/// @param[in] aVertexCount The number of vertices in \c aVertices (and +/// optionally \c aTexCoords). +/// @param[in] aIndices An array of vertex indices (three consecutive integers +/// make one triangle). +/// @param[in] aTriangleCount The number of triangles in \c aIndices (there +/// must be exactly 3 x \c aTriangleCount indices in \c aIndices). +/// @param[in] aNormals An array of per-vertex normals (or NULL if there are +/// no normals). Each normal is made up by three consecutive floats, +/// and there must be \c aVertexCount normals. +/// @see ctmAddUVMap(), ctmAddAttribMap(), ctmSave(), ctmSaveCustom(). +CTMEXPORT void CTMCALL ctmDefineMesh(CTMcontext aContext, + const CTMfloat * aVertices, CTMuint aVertexCount, const CTMuint * aIndices, + CTMuint aTriangleCount, const CTMfloat * aNormals); + +/// Define a UV map. There can be several UV maps in a mesh. A UV map is +/// typically used for 2D texture mapping. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aUVCoords An array of UV coordinates. Each UV coordinate is made +/// up by two consecutive floats, and there must be as many +/// coordinates as there are vertices in the mesh. +/// @param[in] aName A unique name for this UV map (zero terminated UTF-8 +/// string). +/// @param[in] aFileName A reference to a image file (zero terminated +/// UTF-8 string). If no file name reference exists, pass NULL. +/// @return A UV map index (CTM_UV_MAP_1 and higher). If the function +/// failed, it will return the zero valued CTM_NONE (use ctmGetError() +/// to determine the cause of the error). +/// @note A triangle mesh must have been defined before calling this function, +/// since the number of vertices is defined by the triangle mesh. +/// @see ctmDefineMesh(). +CTMEXPORT CTMenum CTMCALL ctmAddUVMap(CTMcontext aContext, + const CTMfloat * aUVCoords, const char * aName, const char * aFileName); + +/// Define a custom vertex attribute map. Custom vertex attributes can be used +/// for defining special per-vertex attributes, such as color, weight, ambient +/// occlusion factor, etc. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aAttribValues An array of attribute values. Each attribute value +/// is made up by four consecutive floats, and there must be as many +/// values as there are vertices in the mesh. +/// @param[in] aName A unique name for this attribute map (zero terminated UTF-8 +/// string). +/// @return A attribute map index (CTM_ATTRIB_MAP_1 and higher). If the function +/// failed, it will return the zero valued CTM_NONE (use ctmGetError() +/// to determine the cause of the error). +/// @note A triangle mesh must have been defined before calling this function, +/// since the number of vertices is defined by the triangle mesh. +/// @see ctmDefineMesh(). +CTMEXPORT CTMenum CTMCALL ctmAddAttribMap(CTMcontext aContext, + const CTMfloat * aAttribValues, const char * aName); + +/// Load an OpenCTM format file into the context. The mesh data can be retrieved +/// with the various ctmGet functions. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aFileName The name of the file to be loaded. +CTMEXPORT void CTMCALL ctmLoad(CTMcontext aContext, const char * aFileName); + +/// Load an OpenCTM format file using a custom stream read function. The mesh +/// data can be retrieved with the various ctmGet functions. +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aReadFn Pointer to a custom stream read function. +/// @param[in] aUserData Custom user data, which can be a C language FILE +/// handle, C++ istream object, or a custom object pointer +/// of any type. The user data pointer will be passed to the +/// custom stream read function. +/// @see CTMreadfn. +CTMEXPORT void CTMCALL ctmLoadCustom(CTMcontext aContext, CTMreadfn aReadFn, + void * aUserData); + +/// Save an OpenCTM format file. The mesh must have been defined by +/// ctmDefineMesh(). +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aFileName The name of the file to be saved. +CTMEXPORT void CTMCALL ctmSave(CTMcontext aContext, const char * aFileName); + +/// Save an OpenCTM format file using a custom stream write function. The mesh +/// must have been defined by ctmDefineMesh(). +/// @param[in] aContext An OpenCTM context that has been created by +/// ctmNewContext(). +/// @param[in] aWriteFn Pointer to a custom stream write function. +/// @param[in] aUserData Custom user data, which can be a C language FILE +/// handle, C++ ostream object, or a custom object pointer +/// of any type. The user data pointer will be passed to the +/// custom stream write function. +/// @see CTMwritefn. +CTMEXPORT void CTMCALL ctmSaveCustom(CTMcontext aContext, CTMwritefn aWriteFn, + void * aUserData); + +#ifdef __cplusplus +} +#endif + + +// C++ extensions to the API (to disable C++ extensions, define OPENCTM_NO_CPP) +#if defined(__cplusplus) && !defined(OPENCTM_NO_CPP) + #include "openctmpp.h" +#endif + +#endif // __OPENCTM_H_ diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/openctmpp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/openctmpp.h Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,377 @@ +//----------------------------------------------------------------------------- +// Product: OpenCTM +// File: openctmpp.h +// Description: C++ wrapper for the OpenCTM API. +//----------------------------------------------------------------------------- +// Copyright (c) 2009-2010 Marcus Geelnard +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +//----------------------------------------------------------------------------- + +// To disable C++ extensions, define OPENCTM_NO_CPP +#ifndef OPENCTM_NO_CPP + +#ifndef __OPENCTMPP_H_ +#define __OPENCTMPP_H_ + +// Just in case (if this file was included from outside openctm.h)... +#ifndef __OPENCTM_H_ +#include "openctm.h" +#endif + +#include + +/// OpenCTM exception. When an error occurs, a \c ctm_error exception is +/// thrown. Its what() function returns the name of the OpenCTM error code +/// (for instance "CTM_INVALID_OPERATION"). +class ctm_error: public std::exception +{ + private: + CTMenum mErrorCode; + + public: + explicit ctm_error(CTMenum aError) + { + mErrorCode = aError; + } + + virtual const char* what() const throw() + { + return ctmErrorString(mErrorCode); + } + + CTMenum error_code() const throw() + { + return mErrorCode; + } +}; + + +/// OpenCTM importer class. This is a C++ wrapper class for an OpenCTM import +/// context. Usage example: +/// +/// @code +/// // Create a new OpenCTM importer object +/// CTMimporter ctm; +/// +/// // Load the OpenCTM file +/// ctm.Load("mymesh.ctm"); +/// +/// // Access the mesh data +/// vertCount = ctm.GetInteger(CTM_VERTEX_COUNT); +/// vertices = ctm.GetFloatArray(CTM_VERTICES); +/// triCount = ctm.GetInteger(CTM_TRIANGLE_COUNT); +/// indices = ctm.GetIntegerArray(CTM_INDICES); +/// +/// // Deal with the mesh (e.g. transcode it to our internal representation) +/// // ... +/// @endcode + +class CTMimporter { + private: + /// The OpenCTM context handle. + CTMcontext mContext; + + /// Check for OpenCTM errors, and throw an exception if an error has + /// occured. + void CheckError() + { + CTMenum err = ctmGetError(mContext); + if(err != CTM_NONE) + throw ctm_error(err); + } + + public: + /// Constructor + CTMimporter() + { + mContext = ctmNewContext(CTM_IMPORT); + } + + /// Destructor + ~CTMimporter() + { + ctmFreeContext(mContext); + } + + /// Wrapper for ctmGetInteger() + CTMuint GetInteger(CTMenum aProperty) + { + CTMuint res = ctmGetInteger(mContext, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmGetFloat() + CTMfloat GetFloat(CTMenum aProperty) + { + CTMfloat res = ctmGetFloat(mContext, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmGetIntegerArray() + const CTMuint * GetIntegerArray(CTMenum aProperty) + { + const CTMuint * res = ctmGetIntegerArray(mContext, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmGetFloatArray() + const CTMfloat * GetFloatArray(CTMenum aProperty) + { + const CTMfloat * res = ctmGetFloatArray(mContext, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmGetNamedUVMap() + CTMenum GetNamedUVMap(const char * aName) + { + CTMenum res = ctmGetNamedUVMap(mContext, aName); + CheckError(); + return res; + } + + /// Wrapper for ctmGetUVMapString() + const char * GetUVMapString(CTMenum aUVMap, CTMenum aProperty) + { + const char * res = ctmGetUVMapString(mContext, aUVMap, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmGetUVMapFloat() + CTMfloat GetUVMapFloat(CTMenum aUVMap, CTMenum aProperty) + { + CTMfloat res = ctmGetUVMapFloat(mContext, aUVMap, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmGetNamedAttribMap() + CTMenum GetNamedAttribMap(const char * aName) + { + CTMenum res = ctmGetNamedAttribMap(mContext, aName); + CheckError(); + return res; + } + + /// Wrapper for ctmGetAttribMapString() + const char * GetAttribMapString(CTMenum aAttribMap, CTMenum aProperty) + { + const char * res = ctmGetAttribMapString(mContext, aAttribMap, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmGetAttribMapFloat() + CTMfloat GetAttribMapFloat(CTMenum aAttribMap, CTMenum aProperty) + { + CTMfloat res = ctmGetAttribMapFloat(mContext, aAttribMap, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmGetString() + const char * GetString(CTMenum aProperty) + { + const char * res = ctmGetString(mContext, aProperty); + CheckError(); + return res; + } + + /// Wrapper for ctmLoad() + void Load(const char * aFileName) + { + ctmLoad(mContext, aFileName); + CheckError(); + } + + /// Wrapper for ctmLoadCustom() + void LoadCustom(CTMreadfn aReadFn, void * aUserData) + { + ctmLoadCustom(mContext, aReadFn, aUserData); + CheckError(); + } + + // You can not copy nor assign from one CTMimporter object to another, since + // the object contains hidden state. By declaring these dummy prototypes + // without an implementation, you will at least get linker errors if you try + // to copy or assign a CTMimporter object. + CTMimporter(const CTMimporter& v); + CTMimporter& operator=(const CTMimporter& v); +}; + + +/// OpenCTM exporter class. This is a C++ wrapper class for an OpenCTM export +/// context. Usage example: +/// @code +/// void MySaveFile(CTMuint aVertCount, CTMuint aTriCount, CTMfloat * aVertices, +/// CTMuint * aIndices, const char * aFileName) +/// { +/// // Create a new OpenCTM exporter object +/// CTMexporter ctm; +/// +/// // Define our mesh representation to OpenCTM (store references to it in +/// // the context) +/// ctm.DefineMesh(aVertices, aVertCount, aIndices, aTriCount, NULL); +/// +/// // Save the OpenCTM file +/// ctm.Save(aFileName); +/// } +/// @endcode + +class CTMexporter { + private: + /// The OpenCTM context handle. + CTMcontext mContext; + + /// Check for OpenCTM errors, and throw an exception if an error has + /// occured. + void CheckError() + { + CTMenum err = ctmGetError(mContext); + if(err != CTM_NONE) + throw ctm_error(err); + } + + public: + /// Constructor + CTMexporter() + { + mContext = ctmNewContext(CTM_EXPORT); + } + + /// Destructor + ~CTMexporter() + { + ctmFreeContext(mContext); + } + + /// Wrapper for ctmCompressionMethod() + void CompressionMethod(CTMenum aMethod) + { + ctmCompressionMethod(mContext, aMethod); + CheckError(); + } + + /// Wrapper for ctmCompressionLevel() + void CompressionLevel(CTMuint aLevel) + { + ctmCompressionLevel(mContext, aLevel); + CheckError(); + } + + /// Wrapper for ctmVertexPrecision() + void VertexPrecision(CTMfloat aPrecision) + { + ctmVertexPrecision(mContext, aPrecision); + CheckError(); + } + + /// Wrapper for ctmVertexPrecisionRel() + void VertexPrecisionRel(CTMfloat aRelPrecision) + { + ctmVertexPrecisionRel(mContext, aRelPrecision); + CheckError(); + } + + /// Wrapper for ctmNormalPrecision() + void NormalPrecision(CTMfloat aPrecision) + { + ctmNormalPrecision(mContext, aPrecision); + CheckError(); + } + + /// Wrapper for ctmUVCoordPrecision() + void UVCoordPrecision(CTMenum aUVMap, CTMfloat aPrecision) + { + ctmUVCoordPrecision(mContext, aUVMap, aPrecision); + CheckError(); + } + + /// Wrapper for ctmAttribPrecision() + void AttribPrecision(CTMenum aAttribMap, CTMfloat aPrecision) + { + ctmAttribPrecision(mContext, aAttribMap, aPrecision); + CheckError(); + } + + /// Wrapper for ctmFileComment() + void FileComment(const char * aFileComment) + { + ctmFileComment(mContext, aFileComment); + CheckError(); + } + + /// Wrapper for ctmDefineMesh() + void DefineMesh(const CTMfloat * aVertices, CTMuint aVertexCount, + const CTMuint * aIndices, CTMuint aTriangleCount, + const CTMfloat * aNormals) + { + ctmDefineMesh(mContext, aVertices, aVertexCount, aIndices, aTriangleCount, + aNormals); + CheckError(); + } + + /// Wrapper for ctmAddUVMap() + CTMenum AddUVMap(const CTMfloat * aUVCoords, const char * aName, + const char * aFileName) + { + CTMenum res = ctmAddUVMap(mContext, aUVCoords, aName, aFileName); + CheckError(); + return res; + } + + /// Wrapper for ctmAddAttribMap() + CTMenum AddAttribMap(const CTMfloat * aAttribValues, const char * aName) + { + CTMenum res = ctmAddAttribMap(mContext, aAttribValues, aName); + CheckError(); + return res; + } + + /// Wrapper for ctmSave() + void Save(const char * aFileName) + { + ctmSave(mContext, aFileName); + CheckError(); + } + + /// Wrapper for ctmSaveCustom() + void SaveCustom(CTMwritefn aWriteFn, void * aUserData) + { + ctmSaveCustom(mContext, aWriteFn, aUserData); + CheckError(); + } + + // You can not copy nor assign from one CTMexporter object to another, since + // the object contains hidden state. By declaring these dummy prototypes + // without an implementation, you will at least get linker errors if you try + // to copy or assign a CTMexporter object. + CTMexporter(const CTMexporter& v); + CTMexporter& operator=(const CTMexporter& v); +}; + +#endif // __OPENCTMPP_H_ + +#endif // OPENCTM_NO_CPP diff -r 798df5111b56 -r 188c697b3b49 libs/openctm/stream.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/openctm/stream.c Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,512 @@ +//----------------------------------------------------------------------------- +// Product: OpenCTM +// File: stream.c +// Description: Stream I/O functions. +//----------------------------------------------------------------------------- +// Copyright (c) 2009-2010 Marcus Geelnard +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include "openctm.h" +#include "internal.h" + +#ifdef __DEBUG_ +#include +#endif + +//----------------------------------------------------------------------------- +// _ctmStreamRead() - Read data from a stream. +//----------------------------------------------------------------------------- +CTMuint _ctmStreamRead(_CTMcontext * self, void * aBuf, CTMuint aCount) +{ + if(!self->mUserData || !self->mReadFn) + return 0; + + return self->mReadFn(aBuf, aCount, self->mUserData); +} + +//----------------------------------------------------------------------------- +// _ctmStreamWrite() - Write data to a stream. +//----------------------------------------------------------------------------- +CTMuint _ctmStreamWrite(_CTMcontext * self, void * aBuf, CTMuint aCount) +{ + if(!self->mUserData || !self->mWriteFn) + return 0; + + return self->mWriteFn(aBuf, aCount, self->mUserData); +} + +//----------------------------------------------------------------------------- +// _ctmStreamReadUINT() - Read an unsigned integer from a stream in a machine +// endian independent manner (for portability). +//----------------------------------------------------------------------------- +CTMuint _ctmStreamReadUINT(_CTMcontext * self) +{ + unsigned char buf[4]; + _ctmStreamRead(self, (void *) buf, 4); + return ((CTMuint) buf[0]) | + (((CTMuint) buf[1]) << 8) | + (((CTMuint) buf[2]) << 16) | + (((CTMuint) buf[3]) << 24); +} + +//----------------------------------------------------------------------------- +// _ctmStreamWriteUINT() - Write an unsigned integer to a stream in a machine +// endian independent manner (for portability). +//----------------------------------------------------------------------------- +void _ctmStreamWriteUINT(_CTMcontext * self, CTMuint aValue) +{ + unsigned char buf[4]; + buf[0] = aValue & 0x000000ff; + buf[1] = (aValue >> 8) & 0x000000ff; + buf[2] = (aValue >> 16) & 0x000000ff; + buf[3] = (aValue >> 24) & 0x000000ff; + _ctmStreamWrite(self, (void *) buf, 4); +} + +//----------------------------------------------------------------------------- +// _ctmStreamReadFLOAT() - Read a floating point value from a stream in a +// machine endian independent manner (for portability). +//----------------------------------------------------------------------------- +CTMfloat _ctmStreamReadFLOAT(_CTMcontext * self) +{ + union { + CTMfloat f; + CTMuint i; + } u; + u.i = _ctmStreamReadUINT(self); + return u.f; +} + +//----------------------------------------------------------------------------- +// _ctmStreamWriteFLOAT() - Write a floating point value to a stream in a +// machine endian independent manner (for portability). +//----------------------------------------------------------------------------- +void _ctmStreamWriteFLOAT(_CTMcontext * self, CTMfloat aValue) +{ + union { + CTMfloat f; + CTMuint i; + } u; + u.f = aValue; + _ctmStreamWriteUINT(self, u.i); +} + +//----------------------------------------------------------------------------- +// _ctmStreamReadSTRING() - Read a string value from a stream. The format of +// the string in the stream is: an unsigned integer (string length) followed by +// the string (without null termination). +//----------------------------------------------------------------------------- +void _ctmStreamReadSTRING(_CTMcontext * self, char ** aValue) +{ + CTMuint len; + + // Clear the old string + if(*aValue) + { + free(*aValue); + *aValue = (char *) 0; + } + + // Get string length + len = _ctmStreamReadUINT(self); + + // Read string + if(len > 0) + { + *aValue = (char *) malloc(len + 1); + if(*aValue) + { + _ctmStreamRead(self, (void *) *aValue, len); + (*aValue)[len] = 0; + } + } +} + +//----------------------------------------------------------------------------- +// _ctmStreamWriteSTRING() - Write a string value to a stream. The format of +// the string in the stream is: an unsigned integer (string length) followed by +// the string (without null termination). +//----------------------------------------------------------------------------- +void _ctmStreamWriteSTRING(_CTMcontext * self, const char * aValue) +{ + CTMuint len; + + // Get string length + if(aValue) + len = strlen(aValue); + else + len = 0; + + // Write string length + _ctmStreamWriteUINT(self, len); + + // Write string + if(len > 0) + _ctmStreamWrite(self, (void *) aValue, len); +} + +//----------------------------------------------------------------------------- +// _ctmStreamReadPackedInts() - Read an compressed binary integer data array +// from a stream, and uncompress it. +//----------------------------------------------------------------------------- +int _ctmStreamReadPackedInts(_CTMcontext * self, CTMint * aData, + CTMuint aCount, CTMuint aSize, CTMint aSignedInts) +{ + size_t packedSize, unpackedSize; + CTMuint i, k, x; + CTMint value; + unsigned char * packed, * tmp; + unsigned char props[5]; + int lzmaRes; + + // Read packed data size from the stream + packedSize = (size_t) _ctmStreamReadUINT(self); + + // Read LZMA compression props from the stream + _ctmStreamRead(self, (void *) props, 5); + + // Allocate memory and read the packed data from the stream + packed = (unsigned char *) malloc(packedSize); + if(!packed) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + _ctmStreamRead(self, (void *) packed, packedSize); + + // Allocate memory for interleaved array + tmp = (unsigned char *) malloc(aCount * aSize * 4); + if(!tmp) + { + free(packed); + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Uncompress + unpackedSize = aCount * aSize * 4; + lzmaRes = LzmaUncompress(tmp, &unpackedSize, packed, + &packedSize, props, 5); + + // Free the packed array + free(packed); + + // Error? + if((lzmaRes != SZ_OK) || (unpackedSize != aCount * aSize * 4)) + { + self->mError = CTM_LZMA_ERROR; + free(tmp); + return CTM_FALSE; + } + + // Convert interleaved array to integers + for(i = 0; i < aCount; ++ i) + { + for(k = 0; k < aSize; ++ k) + { + value = (CTMint) tmp[i + k * aCount + 3 * aCount * aSize] | + (((CTMint) tmp[i + k * aCount + 2 * aCount * aSize]) << 8) | + (((CTMint) tmp[i + k * aCount + aCount * aSize]) << 16) | + (((CTMint) tmp[i + k * aCount]) << 24); + // Convert signed magnitude to two's complement? + if(aSignedInts) + { + x = (CTMuint) value; + value = (x & 1) ? -(CTMint)((x + 1) >> 1) : (CTMint)(x >> 1); + } + aData[i * aSize + k] = value; + } + } + + // Free the interleaved array + free(tmp); + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// _ctmStreamWritePackedInts() - Compress a binary integer data array, and +// write it to a stream. +//----------------------------------------------------------------------------- +int _ctmStreamWritePackedInts(_CTMcontext * self, CTMint * aData, + CTMuint aCount, CTMuint aSize, CTMint aSignedInts) +{ + int lzmaRes, lzmaAlgo; + CTMuint i, k; + CTMint value; + size_t bufSize, outPropsSize; + unsigned char * packed, outProps[5], *tmp; +#ifdef __DEBUG_ + CTMuint negCount = 0; +#endif + + // Allocate memory for interleaved array + tmp = (unsigned char *) malloc(aCount * aSize * 4); + if(!tmp) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Convert integers to an interleaved array + for(i = 0; i < aCount; ++ i) + { + for(k = 0; k < aSize; ++ k) + { + value = aData[i * aSize + k]; + // Convert two's complement to signed magnitude? + if(aSignedInts) + value = value < 0 ? -1 - (value << 1) : value << 1; +#ifdef __DEBUG_ + else if(value < 0) + ++ negCount; +#endif + tmp[i + k * aCount + 3 * aCount * aSize] = value & 0x000000ff; + tmp[i + k * aCount + 2 * aCount * aSize] = (value >> 8) & 0x000000ff; + tmp[i + k * aCount + aCount * aSize] = (value >> 16) & 0x000000ff; + tmp[i + k * aCount] = (value >> 24) & 0x000000ff; + } + } + + // Allocate memory for the packed data + bufSize = 1000 + aCount * aSize * 4; + packed = (unsigned char *) malloc(bufSize); + if(!packed) + { + free(tmp); + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Call LZMA to compress + outPropsSize = 5; + lzmaAlgo = (self->mCompressionLevel < 1 ? 0 : 1); + lzmaRes = LzmaCompress(packed, + &bufSize, + (const unsigned char *) tmp, + aCount * aSize * 4, + outProps, + &outPropsSize, + self->mCompressionLevel, // Level (0-9) + 0, -1, -1, -1, -1, -1, // Default values (set by level) + lzmaAlgo // Algorithm (0 = fast, 1 = normal) + ); + + // Free temporary array + free(tmp); + + // Error? + if(lzmaRes != SZ_OK) + { + self->mError = CTM_LZMA_ERROR; + free(packed); + return CTM_FALSE; + } + +#ifdef __DEBUG_ + printf("%d->%d bytes (%d negative words)\n", aCount * aSize * 4, (int) bufSize, negCount); +#endif + + // Write packed data size to the stream + _ctmStreamWriteUINT(self, (CTMuint) bufSize); + + // Write LZMA compression props to the stream + _ctmStreamWrite(self, (void *) outProps, 5); + + // Write the packed data to the stream + _ctmStreamWrite(self, (void *) packed, (CTMuint) bufSize); + + // Free the packed data + free(packed); + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// _ctmStreamReadPackedFloats() - Read an compressed binary float data array +// from a stream, and uncompress it. +//----------------------------------------------------------------------------- +int _ctmStreamReadPackedFloats(_CTMcontext * self, CTMfloat * aData, + CTMuint aCount, CTMuint aSize) +{ + CTMuint i, k; + size_t packedSize, unpackedSize; + union { + CTMfloat f; + CTMint i; + } value; + unsigned char * packed, * tmp; + unsigned char props[5]; + int lzmaRes; + + // Read packed data size from the stream + packedSize = (size_t) _ctmStreamReadUINT(self); + + // Read LZMA compression props from the stream + _ctmStreamRead(self, (void *) props, 5); + + // Allocate memory and read the packed data from the stream + packed = (unsigned char *) malloc(packedSize); + if(!packed) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + _ctmStreamRead(self, (void *) packed, packedSize); + + // Allocate memory for interleaved array + tmp = (unsigned char *) malloc(aCount * aSize * 4); + if(!tmp) + { + free(packed); + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Uncompress + unpackedSize = aCount * aSize * 4; + lzmaRes = LzmaUncompress(tmp, &unpackedSize, packed, + &packedSize, props, 5); + + // Free the packed array + free(packed); + + // Error? + if((lzmaRes != SZ_OK) || (unpackedSize != aCount * aSize * 4)) + { + self->mError = CTM_LZMA_ERROR; + free(tmp); + return CTM_FALSE; + } + + // Convert interleaved array to floats + for(i = 0; i < aCount; ++ i) + { + for(k = 0; k < aSize; ++ k) + { + value.i = (CTMint) tmp[i + k * aCount + 3 * aCount * aSize] | + (((CTMint) tmp[i + k * aCount + 2 * aCount * aSize]) << 8) | + (((CTMint) tmp[i + k * aCount + aCount * aSize]) << 16) | + (((CTMint) tmp[i + k * aCount]) << 24); + aData[i * aSize + k] = value.f; + } + } + + // Free the interleaved array + free(tmp); + + return CTM_TRUE; +} + +//----------------------------------------------------------------------------- +// _ctmStreamWritePackedFloats() - Compress a binary float data array, and +// write it to a stream. +//----------------------------------------------------------------------------- +int _ctmStreamWritePackedFloats(_CTMcontext * self, CTMfloat * aData, + CTMuint aCount, CTMuint aSize) +{ + int lzmaRes, lzmaAlgo; + CTMuint i, k; + union { + CTMfloat f; + CTMint i; + } value; + size_t bufSize, outPropsSize; + unsigned char * packed, outProps[5], *tmp; + + // Allocate memory for interleaved array + tmp = (unsigned char *) malloc(aCount * aSize * 4); + if(!tmp) + { + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Convert floats to an interleaved array + for(i = 0; i < aCount; ++ i) + { + for(k = 0; k < aSize; ++ k) + { + value.f = aData[i * aSize + k]; + tmp[i + k * aCount + 3 * aCount * aSize] = value.i & 0x000000ff; + tmp[i + k * aCount + 2 * aCount * aSize] = (value.i >> 8) & 0x000000ff; + tmp[i + k * aCount + aCount * aSize] = (value.i >> 16) & 0x000000ff; + tmp[i + k * aCount] = (value.i >> 24) & 0x000000ff; + } + } + + // Allocate memory for the packed data + bufSize = 1000 + aCount * aSize * 4; + packed = (unsigned char *) malloc(bufSize); + if(!packed) + { + free(tmp); + self->mError = CTM_OUT_OF_MEMORY; + return CTM_FALSE; + } + + // Call LZMA to compress + outPropsSize = 5; + lzmaAlgo = (self->mCompressionLevel < 1 ? 0 : 1); + lzmaRes = LzmaCompress(packed, + &bufSize, + (const unsigned char *) tmp, + aCount * aSize * 4, + outProps, + &outPropsSize, + self->mCompressionLevel, // Level (0-9) + 0, -1, -1, -1, -1, -1, // Default values (set by level) + lzmaAlgo // Algorithm (0 = fast, 1 = normal) + ); + + // Free temporary array + free(tmp); + + // Error? + if(lzmaRes != SZ_OK) + { + self->mError = CTM_LZMA_ERROR; + free(packed); + return CTM_FALSE; + } + +#ifdef __DEBUG_ + printf("%d->%d bytes\n", aCount * aSize * 4, (int) bufSize); +#endif + + // Write packed data size to the stream + _ctmStreamWriteUINT(self, (CTMuint) bufSize); + + // Write LZMA compression props to the stream + _ctmStreamWrite(self, (void *) outProps, 5); + + // Write the packed data to the stream + _ctmStreamWrite(self, (void *) packed, (CTMuint) bufSize); + + // Free the packed data + free(packed); + + return CTM_TRUE; +} diff -r 798df5111b56 -r 188c697b3b49 src/chunk.h --- a/src/chunk.h Tue Sep 10 15:29:45 2013 +0300 +++ b/src/chunk.h Thu Sep 26 04:47:05 2013 +0300 @@ -18,33 +18,28 @@ // --- first level chunks --- // children of CNK_SCENE CNK_ENV, // environmental parameters - CNK_MTL_LIST, // material library - CNK_MESH_LIST, // all the meshes hang under this chunk - CNK_LIGHT_LIST, // likewise for lights - CNK_CAMERA_LIST, // likewise for cameras - CNK_NODE_LIST, // likewise for nodes + CNK_MTL, // material + CNK_MESH, + CNK_LIGHT, + CNK_CAMERA, + CNK_NODE, // --- second level chunks --- // children of CNK_ENV CNK_ENV_AMBIENT, // ambient color, contains a single CNK_FLOAT3 CNK_ENV_FOG, - // children of CNK_*_LIST - CNK_MTL, - CNK_MESH, - CNK_LIGHT, - CNK_CAMERA, - CNK_NODE, - // --- third level chunks --- // children of CNK_FOG CNK_FOG_COLOR, // fog color, contains a single CNK_FLOAT3 - CNK_FOG_EXP, // fog exponent, contains a single CNK_REAL + CNK_FOG_EXP, // fog exponent, contains a single CNK_FLOAT // children of CNK_MTL + CNK_MTL_NAME, // has a single CNK_STRING CNK_MTL_ATTR, // material attribute, has a CNK_STRING for its name, // a CNK_MTL_ATTR_VAL, and optionally a CNK_MTL_ATTR_MAP // children of CNK_MTL_ATTR + CNK_MTL_ATTR_NAME, // has a single CNK_STRING CNK_MTL_ATTR_VAL, // can have a single CNK_FLOAT, CNK_FLOAT3, or CNK_FLOAT4 CNK_MTL_ATTR_MAP, // has a single CNK_STRING @@ -60,6 +55,7 @@ CNK_MESH_COLOR_LIST, // has a series of CNK_FLOAT4 chunks CNK_MESH_BONES_LIST, // has a series of CNK_INT or CNK_STRING chunks identifying the bone nodes CNK_MESH_FACE_LIST, // has a series of CNK_FACE chunks + CNK_MESH_FILE, // optionally mesh data may be in another file, has a CNK_STRING filename // child of CNK_MESH_FACE_LIST CNK_MESH_FACE, // has three CNK_INT chunks @@ -90,14 +86,14 @@ CNK_NODE_LIGHT, // same as CNK_NODE_MESH CNK_NODE_CAMERA, // same as CNK_NODE_MESH - CNK_NODE_POS, // has a CNK_VEC3, position vector - CNK_NODE_ROT, // has a CNK_VEC4, rotation quaternion (x, y, z imaginary, w real) - CNK_NODE_SCALE, // has a CNK_VEC3, scaling - CNK_NODE_PIVOT, // has a CNK_VEC3, pivot point + CNK_NODE_POS, // has a CNK_FLOAT3, position vector + CNK_NODE_ROT, // has a CNK_FLOAT4, rotation quaternion (x, y, z imaginary, w real) + CNK_NODE_SCALE, // has a CNK_FLOAT3, scaling + CNK_NODE_PIVOT, // has a CNK_FLOAT3, pivot point - CNK_NODE_MATRIX0, // has a CNK_VEC4, first matrix row (4x3) - CNK_NODE_MATRXI1, // has a CNK_VEC4, second matrix row (4x3) - CNK_NODE_MATRIX2, // has a CNK_VEC4, third matrix row (4x3) + CNK_NODE_MATRIX0, // has a CNK_FLOAT4, first matrix row (4x3) + CNK_NODE_MATRXI1, // has a CNK_FLOAT4, second matrix row (4x3) + CNK_NODE_MATRIX2, // has a CNK_FLOAT4, third matrix row (4x3) MAX_NUM_CHUNKS }; diff -r 798df5111b56 -r 188c697b3b49 src/goat3d.cc --- a/src/goat3d.cc Tue Sep 10 15:29:45 2013 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,409 +0,0 @@ -#include "goat3d.h" -#include "goat3d_impl.h" -#include "chunk.h" - -Scene::Scene() - : name("unnamed"), ambient(0.05, 0.05, 0.05) -{ -} - -Scene::~Scene() -{ - clear(); -} - -void Scene::clear() -{ - for(size_t i=0; iname = name; -} - -const char *Scene::get_name() const -{ - return name.c_str(); -} - -void Scene::set_ambient(const Vector3 &amb) -{ - ambient = amb; -} - -const Vector3 &Scene::get_ambient() const -{ - return ambient; -} - -void Scene::add_material(Material *mat) -{ - materials.push_back(mat); -} - -Material *Scene::get_material(int idx) const -{ - return idx >=0 && idx < (int)materials.size() ? materials[idx] : 0; -} - -Material *Scene::get_material(const char *name) const -{ - for(size_t i=0; iname == std::string(name)) { - return materials[i]; - } - } - return 0; -} - -int Scene::get_material_count() const -{ - return (int)materials.size(); -} - - -void Scene::add_mesh(Mesh *mesh) -{ - meshes.push_back(mesh); -} - -Mesh *Scene::get_mesh(int idx) const -{ - return idx >= 0 && idx < (int)meshes.size() ? meshes[idx] : 0; -} - -Mesh *Scene::get_mesh(const char *name) const -{ - for(size_t i=0; iname == std::string(name)) { - return meshes[i]; - } - } - return 0; -} - -int Scene::get_mesh_count() const -{ - return (int)meshes.size(); -} - - -void Scene::add_light(Light *light) -{ - lights.push_back(light); -} - -Light *Scene::get_light(int idx) const -{ - return idx >= 0 && idx < (int)lights.size() ? lights[idx] : 0; -} - -Light *Scene::get_light(const char *name) const -{ - for(size_t i=0; iname == std::string(name)) { - return lights[i]; - } - } - return 0; -} - -int Scene::get_light_count() const -{ - return (int)lights.size(); -} - - -void Scene::add_camera(Camera *cam) -{ - cameras.push_back(cam); -} - -Camera *Scene::get_camera(int idx) const -{ - return idx >= 0 && idx < (int)cameras.size() ? cameras[idx] : 0; -} - -Camera *Scene::get_camera(const char *name) const -{ - for(size_t i=0; iname == std::string(name)) { - return cameras[i]; - } - } - return 0; -} - -int Scene::get_camera_count() const -{ - return (int)cameras.size(); -} - - -void Scene::add_node(Node *node) -{ - nodes.push_back(node); -} - -Node *Scene::get_node(int idx) const -{ - return idx >= 0 && idx < (int)nodes.size() ? nodes[idx] : 0; -} - -Node *Scene::get_node(const char *name) const -{ - for(size_t i=0; iget_name(), name) == 0) { - return nodes[i]; - } - } - return 0; -} - -int Scene::get_node_count() const -{ - return (int)nodes.size(); -} - - -bool Scene::load(goat3d_io *io) -{ - return false; -} - -static long save_env(const Scene *scn, long offset, goat3d_io *io); -static long save_materials(const Scene *scn, long offset, goat3d_io *io); -static long save_material(const Material *mat, long offset, goat3d_io *io); -static long save_mat_attrib(const char *name, const MaterialAttrib &attr, long offset, goat3d_io *io); -static long save_meshes(const Scene *scn, long offset, goat3d_io *io); -static long save_lights(const Scene *scn, long offset, goat3d_io *io); -static long save_cameras(const Scene *scn, long offset, goat3d_io *io); -static long save_nodes(const Scene *scn, long offset, goat3d_io *io); - -static long write_chunk_float(int id, float val, long offs, goat3d_io *io); -static long write_chunk_float3(int id, const Vector3 &vec, long offs, goat3d_io *io); -static long write_chunk_float4(int id, const Vector4 &vec, long offs, goat3d_io *io); - -bool Scene::save(goat3d_io *io) const -{ - long res; - - ChunkHeader hdr; - hdr.id = CNK_SCENE; - hdr.size = sizeof hdr; - - if((res = save_env(this, hdr.size, io)) < 0) { - return false; - } - hdr.size += res; - - if((res = save_materials(this, hdr.size, io)) < 0) { - return false; - } - hdr.size += res; - - if((res = save_meshes(this, hdr.size, io)) < 0) { - return false; - } - hdr.size += res; - - if((res = save_lights(this, hdr.size, io)) < 0) { - return false; - } - hdr.size += res; - - if((res = save_cameras(this, hdr.size, io)) < 0) { - return false; - } - hdr.size += res; - - if((res = save_nodes(this, hdr.size, io)) < 0) { - return false; - } - hdr.size += res; - - // now go back and write the root chunk - io->seek(0, SEEK_SET, io->cls); - if(io->write(&hdr, sizeof hdr, io->cls) < (ssize_t)sizeof hdr) { - return false; - } - - return true; -} - - -static long save_env(const Scene *scn, long offset, goat3d_io *io) -{ - long res; - - ChunkHeader hdr; - hdr.id = CNK_ENV; - hdr.size = sizeof hdr; - - if((res = write_chunk_float3(CNK_ENV_AMBIENT, scn->get_ambient(), offset, io)) < 0) { - return -1; - } - hdr.size += res; - - // TODO add fog chunk - - io->seek(offset, SEEK_SET, io->cls); - if(io->write(&hdr, sizeof hdr, io->cls) < (ssize_t)sizeof hdr) { - return -1; - } - return hdr.size; -} - -static long save_materials(const Scene *scn, long offset, goat3d_io *io) -{ - long res; - - ChunkHeader hdr; - hdr.id = CNK_MTL_LIST; - hdr.size = sizeof hdr; - - for(int i=0; iget_material_count(); i++) { - if((res = save_material(scn->get_material(i), offset + hdr.size, io)) < 0) { - return -1; - } - hdr.size += res; - } - - io->seek(offset, SEEK_SET, io->cls); - if(io->write(&hdr, hdr.size, io->cls) < hdr.size) { - return -1; - } - return hdr.size; -} - -static long save_material(const Material *mat, long offset, goat3d_io *io) -{ - long res; - - ChunkHeader hdr; - hdr.id = CNK_MTL; - hdr.size = sizeof hdr; - - for(int i=0; iget_attrib_count(); i++) { - const char *name = mat->get_attrib_name(i); - if((res = save_mat_attrib(name, (*mat)[i], offset + hdr.size, io)) < 0) { - return -1; - } - hdr.size += res; - } - - io->seek(offset, SEEK_SET, io->cls); - if(io->write(&hdr, hdr.size, io->cls) < hdr.size) { - return -1; - } - return hdr.size; -} - -static long save_mat_attrib(const char *name, const MaterialAttrib &attr, long offset, goat3d_io *io) -{ - long res; - - ChunkHeader hdr; - hdr.id = CNK_MTL_ATTR; - hdr.size = sizeof hdr; - - // TODO cont. - return -1; -} - -static long save_meshes(const Scene *scn, long offset, goat3d_io *io) -{ - return 0; -} - -static long save_lights(const Scene *scn, long offset, goat3d_io *io) -{ - return 0; -} - -static long save_cameras(const Scene *scn, long offset, goat3d_io *io) -{ - return 0; -} - -static long save_nodes(const Scene *scn, long offset, goat3d_io *io) -{ - return 0; -} - -static long write_chunk_float(int id, float val, long offs, goat3d_io *io) -{ - int size = sizeof(ChunkHeader) + sizeof val; - char *buf = (char*)alloca(size); - - Chunk *c = (Chunk*)buf; - c->hdr.id = id; - c->hdr.size = size; - *(float*)c->data = val; - - io->seek(offs, SEEK_SET, io->cls); - if(io->write(buf, size, io->cls) < size) { - return -1; - } - return size; -} - -static long write_chunk_float3(int id, const Vector3 &vec, long offs, goat3d_io *io) -{ - int size = sizeof(ChunkHeader) + sizeof vec; - char *buf = (char*)alloca(size); - - Chunk *c = (Chunk*)buf; - c->hdr.id = id; - c->hdr.size = size; - *(Vector3*)c->data = vec; - - io->seek(offs, SEEK_SET, io->cls); - if(io->write(buf, size, io->cls) < size) { - return -1; - } - return size; -} - -static long write_chunk_float4(int id, const Vector4 &vec, long offs, goat3d_io *io) -{ - int size = sizeof(ChunkHeader) + sizeof vec; - char *buf = (char*)alloca(size); - - Chunk *c = (Chunk*)buf; - c->hdr.id = id; - c->hdr.size = size; - *(Vector4*)c->data = vec; - - io->seek(offs, SEEK_SET, io->cls); - if(io->write(buf, size, io->cls) < size) { - return -1; - } - return size; -} diff -r 798df5111b56 -r 188c697b3b49 src/goat3d_impl.h --- a/src/goat3d_impl.h Tue Sep 10 15:29:45 2013 +0300 +++ b/src/goat3d_impl.h Thu Sep 26 04:47:05 2013 +0300 @@ -60,7 +60,12 @@ bool load(goat3d_io *io); bool save(goat3d_io *io) const; + + bool loadxml(goat3d_io *io); + bool savexml(goat3d_io *io) const; }; +void io_fprintf(goat3d_io *io, const char *fmt, ...); +void io_vfprintf(goat3d_io *io, const char *fmt, va_list ap); #endif // GOAT3D_IMPL_H_ diff -r 798df5111b56 -r 188c697b3b49 src/goat3d_scene.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/goat3d_scene.cc Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,253 @@ +#include +#include "goat3d.h" +#include "goat3d_impl.h" +#include "chunk.h" + +Scene::Scene() + : name("unnamed"), ambient(0.05, 0.05, 0.05) +{ +} + +Scene::~Scene() +{ + clear(); +} + +void Scene::clear() +{ + for(size_t i=0; iname = name; +} + +const char *Scene::get_name() const +{ + return name.c_str(); +} + +void Scene::set_ambient(const Vector3 &amb) +{ + ambient = amb; +} + +const Vector3 &Scene::get_ambient() const +{ + return ambient; +} + +void Scene::add_material(Material *mat) +{ + materials.push_back(mat); +} + +Material *Scene::get_material(int idx) const +{ + return idx >=0 && idx < (int)materials.size() ? materials[idx] : 0; +} + +Material *Scene::get_material(const char *name) const +{ + for(size_t i=0; iname == std::string(name)) { + return materials[i]; + } + } + return 0; +} + +int Scene::get_material_count() const +{ + return (int)materials.size(); +} + + +void Scene::add_mesh(Mesh *mesh) +{ + meshes.push_back(mesh); +} + +Mesh *Scene::get_mesh(int idx) const +{ + return idx >= 0 && idx < (int)meshes.size() ? meshes[idx] : 0; +} + +Mesh *Scene::get_mesh(const char *name) const +{ + for(size_t i=0; iname == std::string(name)) { + return meshes[i]; + } + } + return 0; +} + +int Scene::get_mesh_count() const +{ + return (int)meshes.size(); +} + + +void Scene::add_light(Light *light) +{ + lights.push_back(light); +} + +Light *Scene::get_light(int idx) const +{ + return idx >= 0 && idx < (int)lights.size() ? lights[idx] : 0; +} + +Light *Scene::get_light(const char *name) const +{ + for(size_t i=0; iname == std::string(name)) { + return lights[i]; + } + } + return 0; +} + +int Scene::get_light_count() const +{ + return (int)lights.size(); +} + + +void Scene::add_camera(Camera *cam) +{ + cameras.push_back(cam); +} + +Camera *Scene::get_camera(int idx) const +{ + return idx >= 0 && idx < (int)cameras.size() ? cameras[idx] : 0; +} + +Camera *Scene::get_camera(const char *name) const +{ + for(size_t i=0; iname == std::string(name)) { + return cameras[i]; + } + } + return 0; +} + +int Scene::get_camera_count() const +{ + return (int)cameras.size(); +} + + +void Scene::add_node(Node *node) +{ + nodes.push_back(node); +} + +Node *Scene::get_node(int idx) const +{ + return idx >= 0 && idx < (int)nodes.size() ? nodes[idx] : 0; +} + +Node *Scene::get_node(const char *name) const +{ + for(size_t i=0; iget_name(), name) == 0) { + return nodes[i]; + } + } + return 0; +} + +int Scene::get_node_count() const +{ + return (int)nodes.size(); +} + + +bool Scene::load(goat3d_io *io) +{ + return false; +} + +// Scene::save is defined in goat3d_write.cc + + +void io_fprintf(goat3d_io *io, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + io_vfprintf(io, fmt, ap); + va_end(ap); +} + + +void io_vfprintf(goat3d_io *io, const char *fmt, va_list ap) +{ + char smallbuf[256]; + char *buf = smallbuf; + int sz = sizeof smallbuf; + + int retsz = vsnprintf(buf, sz - 1, fmt, ap); + + if(retsz >= sz) { + /* C99 mandates that snprintf with a short count should return the + * number of characters that *would* be printed. + */ + buf = new char[retsz + 1]; + + vsnprintf(buf, retsz, fmt, ap); + + } else if(retsz <= 0) { + /* SUSv2 and microsoft specify that snprintf with a short count + * returns an arbitrary value <= 0. So let's try allocating + * bigger and bigger arrays until we find the correct size. + */ + sz = sizeof smallbuf; + do { + sz *= 2; + if(buf != smallbuf) { + delete [] buf; + } + buf = new char[sz + 1]; + + retsz = vsnprintf(buf, sz, fmt, ap); + } while(retsz <= 0); + } + + io->write(buf, sz, io->cls); + + if(buf != smallbuf) { + delete [] buf; + } + +} diff -r 798df5111b56 -r 188c697b3b49 src/goat3d_write.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/goat3d_write.cc Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,220 @@ +#include "goat3d_impl.h" +#include "chunk.h" + +/* +static long save_env(const Scene *scn, long offset, goat3d_io *io); +static long save_materials(const Scene *scn, long offset, goat3d_io *io); +static long save_material(const Material *mat, long offset, goat3d_io *io); +static long save_mat_attrib(const char *name, const MaterialAttrib &attr, long offset, goat3d_io *io); +static long save_meshes(const Scene *scn, long offset, goat3d_io *io); +static long save_lights(const Scene *scn, long offset, goat3d_io *io); +static long save_cameras(const Scene *scn, long offset, goat3d_io *io); +static long save_nodes(const Scene *scn, long offset, goat3d_io *io); + +static long write_chunk_float(int id, float val, long offs, goat3d_io *io); +static long write_chunk_float3(int id, const Vector3 &vec, long offs, goat3d_io *io); +static long write_chunk_float4(int id, const Vector4 &vec, long offs, goat3d_io *io); +*/ + +bool Scene::save(goat3d_io *io) const +{ + /* + long res; + + ChunkHeader hdr; + hdr.id = CNK_SCENE; + hdr.size = sizeof hdr; + + if((res = save_env(this, hdr.size, io)) < 0) { + return false; + } + hdr.size += res; + + if((res = save_materials(this, hdr.size, io)) < 0) { + return false; + } + hdr.size += res; + + if((res = save_meshes(this, hdr.size, io)) < 0) { + return false; + } + hdr.size += res; + + if((res = save_lights(this, hdr.size, io)) < 0) { + return false; + } + hdr.size += res; + + if((res = save_cameras(this, hdr.size, io)) < 0) { + return false; + } + hdr.size += res; + + if((res = save_nodes(this, hdr.size, io)) < 0) { + return false; + } + hdr.size += res; + + // now go back and write the root chunk + io->seek(0, SEEK_SET, io->cls); + if(io->write(&hdr, sizeof hdr, io->cls) < (ssize_t)sizeof hdr) { + return false; + } + + return true; + */ + return false; +} + + +#if 0 +static long save_env(const Scene *scn, long offset, goat3d_io *io) +{ + long res; + + ChunkHeader hdr; + hdr.id = CNK_ENV; + hdr.size = sizeof hdr; + + if((res = write_chunk_float3(CNK_ENV_AMBIENT, scn->get_ambient(), offset, io)) < 0) { + return -1; + } + hdr.size += res; + + // TODO add fog chunk + + io->seek(offset, SEEK_SET, io->cls); + if(io->write(&hdr, sizeof hdr, io->cls) < (ssize_t)sizeof hdr) { + return -1; + } + return hdr.size; +} + +static long save_materials(const Scene *scn, long offset, goat3d_io *io) +{ + long res; + + ChunkHeader hdr; + hdr.id = CNK_MTL_LIST; + hdr.size = sizeof hdr; + + for(int i=0; iget_material_count(); i++) { + if((res = save_material(scn->get_material(i), offset + hdr.size, io)) < 0) { + return -1; + } + hdr.size += res; + } + + io->seek(offset, SEEK_SET, io->cls); + if(io->write(&hdr, hdr.size, io->cls) < hdr.size) { + return -1; + } + return hdr.size; +} + +static long save_material(const Material *mat, long offset, goat3d_io *io) +{ + long res; + + ChunkHeader hdr; + hdr.id = CNK_MTL; + hdr.size = sizeof hdr; + + for(int i=0; iget_attrib_count(); i++) { + const char *name = mat->get_attrib_name(i); + if((res = save_mat_attrib(name, (*mat)[i], offset + hdr.size, io)) < 0) { + return -1; + } + hdr.size += res; + } + + io->seek(offset, SEEK_SET, io->cls); + if(io->write(&hdr, hdr.size, io->cls) < hdr.size) { + return -1; + } + return hdr.size; +} + +static long save_mat_attrib(const char *name, const MaterialAttrib &attr, long offset, goat3d_io *io) +{ + long res; + + ChunkHeader hdr; + hdr.id = CNK_MTL_ATTR; + hdr.size = sizeof hdr; + + // TODO cont. + return -1; +} + +static long save_meshes(const Scene *scn, long offset, goat3d_io *io) +{ + return 0; +} + +static long save_lights(const Scene *scn, long offset, goat3d_io *io) +{ + return 0; +} + +static long save_cameras(const Scene *scn, long offset, goat3d_io *io) +{ + return 0; +} + +static long save_nodes(const Scene *scn, long offset, goat3d_io *io) +{ + return 0; +} + +static long write_chunk_float(int id, float val, long offs, goat3d_io *io) +{ + int size = sizeof(ChunkHeader) + sizeof val; + char *buf = (char*)alloca(size); + + Chunk *c = (Chunk*)buf; + c->hdr.id = id; + c->hdr.size = size; + *(float*)c->data = val; + + io->seek(offs, SEEK_SET, io->cls); + if(io->write(buf, size, io->cls) < size) { + return -1; + } + return size; +} + +static long write_chunk_float3(int id, const Vector3 &vec, long offs, goat3d_io *io) +{ + int size = sizeof(ChunkHeader) + sizeof vec; + char *buf = (char*)alloca(size); + + Chunk *c = (Chunk*)buf; + c->hdr.id = id; + c->hdr.size = size; + *(Vector3*)c->data = vec; + + io->seek(offs, SEEK_SET, io->cls); + if(io->write(buf, size, io->cls) < size) { + return -1; + } + return size; +} + +static long write_chunk_float4(int id, const Vector4 &vec, long offs, goat3d_io *io) +{ + int size = sizeof(ChunkHeader) + sizeof vec; + char *buf = (char*)alloca(size); + + Chunk *c = (Chunk*)buf; + c->hdr.id = id; + c->hdr.size = size; + *(Vector4*)c->data = vec; + + io->seek(offs, SEEK_SET, io->cls); + if(io->write(buf, size, io->cls) < size) { + return -1; + } + return size; +} +#endif diff -r 798df5111b56 -r 188c697b3b49 src/goat3d_writexml.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/goat3d_writexml.cc Thu Sep 26 04:47:05 2013 +0300 @@ -0,0 +1,187 @@ +#include +#include "goat3d_impl.h" +#include "chunk.h" +#include "openctm.h" + +static bool write_material(const Scene *scn, goat3d_io *io, const Material *mat, int level); +static bool write_mesh(const Scene *scn, goat3d_io *io, const Mesh *mesh, int idx, int level); +static void write_ctm_mesh(const Mesh *mesh, const char *fname); +static bool write_light(const Scene *scn, goat3d_io *io, const Light *light, int level); +static bool write_camera(const Scene *scn, goat3d_io *io, const Camera *cam, int level); +static bool write_node(const Scene *scn, goat3d_io *io, const Node *node, int level); +static void xmlout(goat3d_io *io, int level, const char *fmt, ...); + +bool Scene::savexml(goat3d_io *io) const +{ + xmlout(io, 0, "\n"); + + // write environment stuff + xmlout(io, 1, "\n"); + xmlout(io, 1, "\n"); + + for(size_t i=0; i\n"); + return true; +} + +static bool write_material(const Scene *scn, goat3d_io *io, const Material *mat, int level) +{ + xmlout(io, level, "\n"); + xmlout(io, level + 1, "\n", mat->name.c_str()); + + for(int i=0; iget_attrib_count(); i++) { + xmlout(io, level + 1, "\n"); + xmlout(io, level + 2, "\n", mat->get_attrib_name(i)); + + const MaterialAttrib &attr = (*mat)[i]; + xmlout(io, level + 2, "\n", attr.value.x, + attr.value.y, attr.value.z, attr.value.w); + if(!attr.map.empty()) { + xmlout(io, level + 2, "\n", attr.map.c_str()); + } + xmlout(io, level + 1, "\n"); + } + xmlout(io, level, "\n"); + return true; +} + +static bool write_mesh(const Scene *scn, goat3d_io *io, const Mesh *mesh, int idx, int level) +{ + // first write the external (openctm) mesh file + const char *prefix = scn->get_name(); + if(!prefix) { + prefix = "goat"; + } + + char *mesh_filename = (char*)alloca(strlen(prefix) + 32); + sprintf(mesh_filename, "%s-mesh%04d.ctm", prefix, idx); + + write_ctm_mesh(mesh, mesh_filename); + + // then refer to that filename in the XML tags + xmlout(io, level, "\n"); + xmlout(io, level + 1, "\n", mesh->name.c_str()); + xmlout(io, level + 1, "\n", mesh->material->name.c_str()); + xmlout(io, level + 1, "\n", mesh_filename); + xmlout(io, level, "\n"); + return true; +} + +static void write_ctm_mesh(const Mesh *mesh, const char *fname) +{ + int vnum = (int)mesh->vertices.size(); + + CTMcontext ctm = ctmNewContext(CTM_EXPORT); + + // vertices, normals, and face-vertex indices + ctmDefineMesh(ctm, &mesh->vertices[0].x, vnum, (CTMuint*)mesh->faces[0].v, + mesh->faces.size(), mesh->normals.empty() ? 0 : &mesh->normals[0].x); + + // texture coordinates + if(!mesh->texcoords.empty()) { + CTMfloat *uvarray = new CTMfloat[vnum * 2 * sizeof *uvarray]; + CTMfloat *uvptr = uvarray; + + for(int i=0; itexcoords[i].x; + *uvptr++ = mesh->texcoords[i].y; + } + ctmAddUVMap(ctm, uvarray, "texcoord", 0); + delete [] uvarray; + } + + // vertex colors + if(!mesh->colors.empty()) { + ctmAddAttribMap(ctm, &mesh->colors[0].x, "color"); + } + + // skin weights + if(!mesh->skin_weights.empty()) { + ctmAddAttribMap(ctm, &mesh->skin_weights[0].x, "skin_weight"); + } + + // if either of the non-float4 attributes are present we need to make a tmp array + CTMfloat *attr_array = 0; + if(!mesh->tangents.empty() || !mesh->skin_matrices.empty()) { + attr_array = new CTMfloat[vnum * 4 * sizeof *attr_array]; + } + + // tangents + if(!mesh->tangents.empty()) { + CTMfloat *ptr = attr_array; + + for(int i=0; itangents[i].x; + *ptr++ = mesh->tangents[i].y; + *ptr++ = mesh->tangents[i].z; + *ptr++ = 1.0; + } + ctmAddAttribMap(ctm, attr_array, "tangent"); + } + + // skin matrix indices (4 per vertex) + if(!mesh->skin_matrices.empty()) { + CTMfloat *ptr = attr_array; + + for(int i=0; iskin_matrices[i].x; + *ptr++ = (float)mesh->skin_matrices[i].y; + *ptr++ = (float)mesh->skin_matrices[i].z; + *ptr++ = (float)mesh->skin_matrices[i].w; + } + ctmAddAttribMap(ctm, attr_array, "skin_matrix"); + } + + delete [] attr_array; + + /* TODO find a way to specify the nodes participating in the skinning of this mesh + * probably in the comment field? + */ + + ctmSave(ctm, fname); + + ctmFreeContext(ctm); +} + +static bool write_light(const Scene *scn, goat3d_io *io, const Light *light, int level) +{ + return true; +} + +static bool write_camera(const Scene *scn, goat3d_io *io, const Camera *cam, int level) +{ + return true; +} + +static bool write_node(const Scene *scn, goat3d_io *io, const Node *node, int level) +{ + return true; +} + + +static void xmlout(goat3d_io *io, int level, const char *fmt, ...) +{ + for(int i=0; i