sl@0: /* sl@0: * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of the License "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: Native implementation of the Loader class sl@0: * sl@0: */ sl@0: sl@0: sl@0: /*! sl@0: * \internal sl@0: * \file sl@0: * \brief Native implementation of the Loader class sl@0: * sl@0: */ sl@0: sl@0: #include "m3g_object.h" sl@0: #include "m3g_array.h" sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Internal data types sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Possible global states for the loader sl@0: */ sl@0: typedef enum { sl@0: /*! \internal \brief Loading not supported yet */ sl@0: LOADSTATE_NOT_SUPPORTED = -2, sl@0: /*! \internal \brief Loading has terminated with an error */ sl@0: LOADSTATE_ERROR = -1, sl@0: /*! \internal \brief Loading has not started yet */ sl@0: LOADSTATE_INITIAL = 0, sl@0: /*! \internal \brief The identifier of the file is being read */ sl@0: LOADSTATE_IDENTIFIER, sl@0: /*! \internal \brief The header of the section is being read */ sl@0: LOADSTATE_SECTION, sl@0: /*! \internal \brief The header field of an object is being read */ sl@0: LOADSTATE_OBJECT, sl@0: /*! \internal \brief Loading is finished */ sl@0: LOADSTATE_DONE sl@0: } LoaderState; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Possible local states for the loader sl@0: */ sl@0: typedef enum { sl@0: /*! \internal \brief Local state is entered */ sl@0: LOADSTATE_ENTER, sl@0: /*! \internal \brief Local state is exited */ sl@0: LOADSTATE_EXIT, sl@0: /*! \internal \brief Local state is section checksum */ sl@0: LOADSTATE_CHECKSUM sl@0: } LoaderLocalState; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Buffered byte stream class sl@0: */ sl@0: typedef struct sl@0: { sl@0: M3Gubyte *allocatedData; sl@0: M3Gubyte *data; sl@0: M3Gsizei capacity, bytesAvailable, totalBytes; sl@0: } BufferedStream; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief User data for a loaded object sl@0: */ sl@0: typedef struct sl@0: { sl@0: M3GObject object; sl@0: M3Gint numParams; sl@0: M3Gbyte **params; sl@0: M3Gsizei *paramLengths; sl@0: M3Gint *paramId; sl@0: } UserData; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loader instance data sl@0: */ sl@0: typedef struct M3GLoaderImpl sl@0: { sl@0: Object object; sl@0: sl@0: BufferedStream stream; sl@0: M3Gsizei bytesRequired; sl@0: M3Gsizei sectionBytesRequired; sl@0: sl@0: PointerArray refArray; sl@0: PointerArray userDataArray; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief The global state the loader is in sl@0: * sl@0: * This is a rather ordinary state machine thing; basically the sl@0: * type of object being loaded, or one of the possible error sl@0: * conditions. In here, it also amounts to a particular coroutine sl@0: * execution context. sl@0: */ sl@0: LoaderState state; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief The local state of the loader sl@0: * sl@0: * This is basically the line number within a particular coroutine sl@0: * function. sl@0: */ sl@0: M3Gint localState; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Object being loaded sl@0: */ sl@0: M3Gint objectType; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loaded object sl@0: */ sl@0: M3GObject loadedObject; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Pointer to the beginning of an object sl@0: */ sl@0: M3Gubyte *objectData; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Pointer to the end of an object sl@0: */ sl@0: M3Gubyte *objectDataEnd; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Pointer to the context data for the current coroutine sl@0: * context sl@0: */ sl@0: M3Gubyte *localData; sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Size of the current coroutine data sl@0: * sl@0: * This is grown dynamically as necessary, rather than trying to sl@0: * maintain a single size that fits all coroutines. sl@0: */ sl@0: size_t localDataSize; sl@0: sl@0: /* File information */ sl@0: M3Gbool hasReferences; sl@0: M3Gsizei fileSize; sl@0: M3Gsizei contentSize; sl@0: M3Gint triCount; sl@0: M3Gint triConstraint; sl@0: sl@0: /* Section information */ sl@0: M3Gbool compressed; sl@0: M3Gint sectionLength; sl@0: M3Gint sectionNum; sl@0: M3Gint inflatedLength; sl@0: M3Gubyte *sectionData; sl@0: M3Gubyte *allocatedSectionData; sl@0: sl@0: /* Adler data */ sl@0: M3Gint S12[2]; sl@0: } Loader; sl@0: sl@0: typedef struct { sl@0: const unsigned char *data; sl@0: int read; sl@0: int length; sl@0: } compressedData; sl@0: sl@0: /* Type ID used for classifying objects derived from Node */ sl@0: #define ANY_NODE_CLASS ((M3GClass)(-1)) sl@0: sl@0: #include sl@0: #define m3gCmp(s1, s2, len) memcmp(s1, s2, len) sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Private constants sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: #define M3G_MIN_OBJECT_SIZE (1 + 4) sl@0: #define M3G_MIN_SECTION_SIZE (1 + 4 + 4) sl@0: #define M3G_CHECKSUM_SIZE 4 sl@0: sl@0: #define M3G_ADLER_CONST 65521; sl@0: sl@0: static const M3Gubyte M3G_FILE_IDENTIFIER[] = { sl@0: 0xAB, 0x4A, 0x53, 0x52, 0x31, 0x38, 0x34, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A sl@0: }; sl@0: sl@0: static const M3Gubyte PNG_FILE_IDENTIFIER[] = { sl@0: 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a sl@0: }; sl@0: sl@0: static void m3gCleanupLoader(M3GLoader loader); sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Platform-specific "inflate" decompression code sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Decompresses a block of data into an output buffer sl@0: * sl@0: * \param srcLength number of bytes in the input (compressed) buffer sl@0: * \param src pointer to the input buffer sl@0: * \param dstLength number of bytes allocated in the output buffer sl@0: * \param dst pointer to the output buffer sl@0: * \return the number of bytes written to \c dst sl@0: */ sl@0: static M3Gsizei m3gInflateBlock(M3Gsizei srcLength, const M3Gubyte *src, sl@0: M3Gsizei dstLength, M3Gubyte *dst); sl@0: sl@0: /* Include the platform-dependent implementation */ sl@0: #if !defined(M3G_TARGET_GENERIC) sl@0: # include "m3g_loader_inflate.inl" sl@0: #endif sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Private functions sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Destructor sl@0: */ sl@0: static void m3gDestroyLoader(Object *obj) sl@0: { sl@0: Loader* loader = (Loader *) obj; sl@0: M3G_VALIDATE_OBJECT(loader); sl@0: { sl@0: Interface *m3g = M3G_INTERFACE(loader); sl@0: M3Gint n, i; sl@0: sl@0: m3gCleanupLoader(loader); sl@0: m3gDestroyArray(&loader->refArray, m3g); sl@0: n = m3gArraySize(&loader->userDataArray); sl@0: for (i = 0; i < n; ++i) sl@0: { sl@0: UserData *data = (UserData *)m3gGetArrayElement(&loader->userDataArray, i); sl@0: m3gFree(m3g, data->params); sl@0: m3gFree(m3g, data->paramLengths); sl@0: m3gFree(m3g, data->paramId); sl@0: m3gFree(m3g, data); sl@0: } sl@0: m3gDestroyArray(&loader->userDataArray, m3g); sl@0: m3gFree(m3g, loader->stream.allocatedData); sl@0: m3gFree(m3g, loader->allocatedSectionData); sl@0: } sl@0: m3gDestroyObject(obj); sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Stores new data in the stream buffer of this loader sl@0: */ sl@0: static M3Gbool m3gBufferData( M3GInterface m3g, sl@0: BufferedStream *stream, sl@0: M3Gsizei bytes, sl@0: const M3Gubyte *data) sl@0: { sl@0: M3Gsizei used; sl@0: sl@0: /* Allocate initial buffer */ sl@0: if (stream->allocatedData == NULL) { sl@0: stream->capacity = bytes + 512; sl@0: stream->allocatedData = m3gAllocZ(m3g, stream->capacity); sl@0: if (!stream->allocatedData) { sl@0: return M3G_FALSE; sl@0: } sl@0: stream->data = stream->allocatedData; sl@0: stream->bytesAvailable = 0; sl@0: stream->totalBytes = 0; sl@0: } sl@0: sl@0: /* First skip used bytes */ sl@0: used = stream->data - stream->allocatedData; sl@0: if (used > 0) { sl@0: m3gMove(stream->allocatedData, stream->data, stream->bytesAvailable); sl@0: stream->data = stream->allocatedData; sl@0: } sl@0: sl@0: /* Check if new data fits in current buffer */ sl@0: if ((stream->capacity - stream->bytesAvailable) < bytes) { sl@0: M3Gubyte *newData; sl@0: stream->capacity = stream->capacity + bytes + 512; sl@0: newData = m3gAllocZ(m3g, stream->capacity); sl@0: if (!newData) { sl@0: m3gFree(m3g, stream->allocatedData); sl@0: stream->allocatedData = NULL; sl@0: return M3G_FALSE; sl@0: } sl@0: m3gCopy(newData, stream->data, stream->bytesAvailable); sl@0: m3gFree(m3g, stream->allocatedData); sl@0: stream->allocatedData = newData; sl@0: stream->data = stream->allocatedData; sl@0: } sl@0: sl@0: m3gCopy(stream->data + stream->bytesAvailable, data, bytes); sl@0: stream->bytesAvailable += bytes; sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Resets buffered data sl@0: */ sl@0: static void m3gResetBufferedData(BufferedStream *stream) sl@0: { sl@0: stream->bytesAvailable = 0; sl@0: stream->data = stream->allocatedData; sl@0: stream->totalBytes = 0; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Gets buffered data pointer sl@0: */ sl@0: static M3Gubyte *m3gGetBufferedDataPtr(BufferedStream *stream, M3Gint length) sl@0: { sl@0: if (stream->bytesAvailable >= length) { sl@0: return stream->data; sl@0: } sl@0: else { sl@0: return NULL; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Advances buffered data pointer sl@0: */ sl@0: static void m3gAdvanceBufferedData(BufferedStream *stream, M3Gint length) sl@0: { sl@0: stream->data += length; sl@0: stream->bytesAvailable -= length; sl@0: stream->totalBytes += length; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Verify a boolean sl@0: */ sl@0: static M3Gbool m3gVerifyBool(M3Gubyte *data) sl@0: { sl@0: return (*data == 0 || *data == 1); sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads ARGB color from data array sl@0: */ sl@0: static M3Guint m3gLoadARGB(M3Gubyte *data) sl@0: { sl@0: M3Guint v = data[3]; sl@0: v <<= 8; sl@0: v |= data[0]; sl@0: v <<= 8; sl@0: v |= data[1]; sl@0: v <<= 8; sl@0: v |= data[2]; sl@0: sl@0: return v; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads RGB color from data array sl@0: */ sl@0: static M3Guint m3gLoadRGB(M3Gubyte *data) sl@0: { sl@0: M3Guint v = data[0]; sl@0: v <<= 8; sl@0: v |= data[1]; sl@0: v <<= 8; sl@0: v |= data[2]; sl@0: sl@0: return v; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads short from data array sl@0: */ sl@0: static M3Gshort m3gLoadShort(M3Gubyte *data) sl@0: { sl@0: M3Gshort v = data[1]; sl@0: v <<= 8; sl@0: v |= data[0]; sl@0: sl@0: return v; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads integer from data array sl@0: */ sl@0: static M3Gint m3gLoadInt(M3Gubyte *data) sl@0: { sl@0: M3Gint v = data[3]; sl@0: v <<= 8; sl@0: v |= data[2]; sl@0: v <<= 8; sl@0: v |= data[1]; sl@0: v <<= 8; sl@0: v |= data[0]; sl@0: sl@0: return v; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads integer from data array sl@0: */ sl@0: static M3Gbool m3gLoadFloat(M3Gubyte *data, M3Gfloat *res) sl@0: { sl@0: M3Guint v = data[3]; sl@0: v <<= 8; sl@0: v |= data[2]; sl@0: v <<= 8; sl@0: v |= data[1]; sl@0: v <<= 8; sl@0: v |= data[0]; sl@0: sl@0: *res = (*(M3Gfloat*)&(v)); sl@0: if ((v & 0x7f800000) == 0x7f800000 || sl@0: v == 0x80000000 || // negative zero sl@0: ((v & 0x007FFFFF ) != 0 && ( v & 0x7F800000 ) == 0)) sl@0: return M3G_FALSE; sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads 4 * 4 matrix from data array sl@0: */ sl@0: static M3Gbool m3gLoadMatrix(Matrix *m, M3Gubyte *data) sl@0: { sl@0: M3Gint i; sl@0: M3Gfloat array[16]; sl@0: sl@0: for (i = 0; i < 16; i++) { sl@0: if (!m3gLoadFloat(data + 4 * i, &array[i])) sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: m3gSetMatrixRows(m, array); sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Inflates a section sl@0: */ sl@0: static M3Gubyte *m3gInflateSection(Loader *loader, sl@0: M3Gubyte *compressed, sl@0: M3Gint cLength, M3Gint iLength) sl@0: { sl@0: M3Gubyte *inflated = m3gAllocZ(M3G_INTERFACE(loader), iLength); sl@0: if (inflated && !m3gInflateBlock(cLength, compressed, iLength, inflated)) { sl@0: m3gFree(M3G_INTERFACE(loader), inflated); sl@0: return NULL; sl@0: } sl@0: sl@0: return inflated; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads file identifier sl@0: */ sl@0: static LoaderState m3gLoadIdentifier(Loader *loader) sl@0: { sl@0: M3Gubyte *data = m3gGetBufferedDataPtr(&loader->stream, loader->bytesRequired); sl@0: sl@0: if (loader->localState == LOADSTATE_ENTER) { sl@0: if (m3gCmp(data, PNG_FILE_IDENTIFIER, sizeof(PNG_FILE_IDENTIFIER)) == 0) { sl@0: m3gAdvanceBufferedData(&loader->stream, loader->bytesRequired); sl@0: return LOADSTATE_NOT_SUPPORTED; sl@0: } sl@0: else { sl@0: loader->localState = LOADSTATE_EXIT; sl@0: loader->bytesRequired = sizeof(M3G_FILE_IDENTIFIER); sl@0: return LOADSTATE_IDENTIFIER; sl@0: } sl@0: } sl@0: else { sl@0: if (m3gCmp(data, M3G_FILE_IDENTIFIER, sizeof(M3G_FILE_IDENTIFIER)) == 0) { sl@0: m3gAdvanceBufferedData(&loader->stream, loader->bytesRequired); sl@0: loader->localState = LOADSTATE_ENTER; sl@0: loader->bytesRequired = M3G_MIN_SECTION_SIZE; sl@0: return LOADSTATE_SECTION; sl@0: } sl@0: sl@0: loader->bytesRequired = 0; sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Adler helper functions sl@0: */ sl@0: sl@0: static void m3gInitAdler(M3Gint *S12) sl@0: { sl@0: S12[0] = 1; S12[1] = 0; sl@0: } sl@0: sl@0: static void m3gUpdateAdler(M3Gint *S12, M3Gubyte *data, M3Gint length) sl@0: { sl@0: int i; sl@0: for (i = 0; i < length; i++) { sl@0: S12[0] = (S12[0] + data[i]) % M3G_ADLER_CONST; sl@0: S12[1] = (S12[1] + S12[0]) % M3G_ADLER_CONST; sl@0: } sl@0: } sl@0: sl@0: static M3Gint m3gGetAdler(M3Gint *S12) sl@0: { sl@0: return (S12[1] << 16) | S12[0]; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a section sl@0: */ sl@0: static LoaderState m3gLoadSection(Loader *loader) sl@0: { sl@0: M3Gubyte *data = m3gGetBufferedDataPtr(&loader->stream, loader->bytesRequired); sl@0: sl@0: if (data == NULL) { sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: sl@0: switch(loader->localState) { sl@0: case LOADSTATE_ENTER: sl@0: m3gAdvanceBufferedData(&loader->stream, loader->bytesRequired); sl@0: m3gInitAdler(loader->S12); sl@0: m3gUpdateAdler(loader->S12, data, loader->bytesRequired); sl@0: sl@0: if (*data > 1) sl@0: { sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: loader->compressed = data[0]; sl@0: loader->sectionLength = m3gLoadInt(data + 1); sl@0: loader->inflatedLength = m3gLoadInt(data + 5); sl@0: sl@0: loader->localState = LOADSTATE_EXIT; sl@0: loader->bytesRequired = loader->sectionLength - loader->bytesRequired; sl@0: if (!loader->compressed && loader->inflatedLength != (loader->bytesRequired - M3G_CHECKSUM_SIZE)) sl@0: { sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: sl@0: loader->sectionNum++; sl@0: sl@0: /* Special case for empty sections */ sl@0: if (loader->bytesRequired == M3G_CHECKSUM_SIZE) { sl@0: loader->localData = data + loader->sectionLength - M3G_CHECKSUM_SIZE; sl@0: loader->sectionData = loader->localData; sl@0: loader->compressed = M3G_FALSE; sl@0: loader->localState = LOADSTATE_CHECKSUM; sl@0: } sl@0: return LOADSTATE_SECTION; sl@0: sl@0: case LOADSTATE_EXIT: sl@0: default: sl@0: m3gUpdateAdler(loader->S12, data, loader->bytesRequired - M3G_CHECKSUM_SIZE); sl@0: sl@0: if (loader->compressed) { sl@0: if (loader->inflatedLength > 0) { sl@0: m3gFree(M3G_INTERFACE(loader), loader->allocatedSectionData); sl@0: loader->sectionData = m3gInflateSection(loader, data, loader->bytesRequired, loader->inflatedLength); sl@0: loader->allocatedSectionData = loader->sectionData; sl@0: sl@0: if (!loader->sectionData) { sl@0: if (m3gErrorRaised(M3G_INTERFACE(loader)) == M3G_NO_ERROR) sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: } sl@0: else { sl@0: loader->sectionData = NULL; sl@0: } sl@0: } sl@0: else { sl@0: loader->sectionData = data; sl@0: } sl@0: sl@0: loader->localData = loader->sectionData; sl@0: loader->sectionBytesRequired = M3G_MIN_OBJECT_SIZE; sl@0: loader->localState = LOADSTATE_ENTER; sl@0: return LOADSTATE_OBJECT; sl@0: sl@0: case LOADSTATE_CHECKSUM: sl@0: if (loader->localData != loader->sectionData + loader->inflatedLength || /* Length */ sl@0: m3gLoadInt(data + loader->bytesRequired - M3G_CHECKSUM_SIZE) != m3gGetAdler(loader->S12)) sl@0: { sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: m3gFree(M3G_INTERFACE(loader), loader->allocatedSectionData); sl@0: loader->allocatedSectionData = NULL; sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: m3gAdvanceBufferedData(&loader->stream, loader->bytesRequired); sl@0: sl@0: m3gFree(M3G_INTERFACE(loader), loader->allocatedSectionData); sl@0: loader->allocatedSectionData = NULL; sl@0: sl@0: loader->localState = LOADSTATE_ENTER; sl@0: loader->bytesRequired = M3G_MIN_SECTION_SIZE; sl@0: return LOADSTATE_SECTION; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Resets section data pointer to the beginning of an object sl@0: */ sl@0: static void m3gRewindObject(Loader *loader) sl@0: { sl@0: loader->localData = loader->objectData; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Resets section data pointer to the end of an object sl@0: */ sl@0: static void m3gSkipObject(Loader *loader) sl@0: { sl@0: loader->localData = loader->objectDataEnd; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Marks object to begin sl@0: */ sl@0: static void m3gBeginObject(Loader *loader) sl@0: { sl@0: loader->objectData = loader->localData; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Marks object to end sl@0: */ sl@0: static void m3gEndObject(Loader *loader) sl@0: { sl@0: loader->objectDataEnd = loader->localData; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Gets section data pointer sl@0: */ sl@0: static M3Gubyte *m3gGetSectionDataPtr(Loader *loader, M3Gint length) sl@0: { sl@0: if ((loader->localData + length) <= (loader->sectionData + loader->inflatedLength)) { sl@0: return loader->localData; sl@0: } sl@0: else { sl@0: return NULL; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Advances section data pointer sl@0: */ sl@0: static void m3gAdvanceSectionData(Loader *loader, M3Gint length) sl@0: { sl@0: loader->localData += length; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Check length of the available section data sl@0: */ sl@0: static M3Gbool m3gCheckSectionDataLength(Loader *loader, const M3Gubyte *data, M3Gsizei length) sl@0: { sl@0: if (data + length < data) return M3G_FALSE; /* Check for overflow */ sl@0: return ((data + length) <= (loader->sectionData + loader->inflatedLength)); sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief References an object in the object array sl@0: * sl@0: * \note Uses lowest bit of the pointer to mark a reference sl@0: */ sl@0: static void m3gReferenceLoaded(PointerArray *array, M3Gint idx) sl@0: { sl@0: M3Guint ptr = (M3Guint)m3gGetArrayElement(array, idx); sl@0: ptr |= 1; sl@0: m3gSetArrayElement(array, idx, (void *)ptr); sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Gets an object in the object array and sl@0: * returns reference status sl@0: */ sl@0: static M3GObject m3gGetLoadedPtr(PointerArray *array, M3Gint idx, M3Gbool *referenced) sl@0: { sl@0: M3Guint ptr = (M3Guint)m3gGetArrayElement(array, idx); sl@0: if (referenced != NULL) { sl@0: *referenced = ptr & 1; sl@0: } sl@0: return (M3GObject)(ptr & (~1)); sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Gets a loaded object and marks it referenced sl@0: */ sl@0: static M3GObject m3gGetLoaded(Loader *loader, M3Gint idx, M3GClass classID) sl@0: { sl@0: M3GObject obj; sl@0: M3GClass objClassID; sl@0: M3Gbool isCompatible; sl@0: sl@0: if (idx == 0) return NULL; sl@0: idx -= 2; sl@0: sl@0: if (idx < 0 || idx >= m3gArraySize(&loader->refArray)) { sl@0: /* Error, not loaded */ sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: return NULL; sl@0: } sl@0: sl@0: obj = m3gGetLoadedPtr(&loader->refArray, idx, NULL); sl@0: objClassID = M3G_CLASS(obj); sl@0: sl@0: /* Class type check; handle nodes as a special case */ sl@0: sl@0: if (classID == ANY_NODE_CLASS) { sl@0: switch (objClassID) { sl@0: case M3G_CLASS_CAMERA: sl@0: case M3G_CLASS_GROUP: sl@0: case M3G_CLASS_LIGHT: sl@0: case M3G_CLASS_MESH: sl@0: case M3G_CLASS_MORPHING_MESH: sl@0: case M3G_CLASS_SKINNED_MESH: sl@0: case M3G_CLASS_SPRITE: sl@0: case M3G_CLASS_WORLD: sl@0: isCompatible = M3G_TRUE; sl@0: break; sl@0: default: sl@0: isCompatible = M3G_FALSE; sl@0: } sl@0: } sl@0: else { sl@0: switch (classID) { sl@0: case M3G_ABSTRACT_CLASS: sl@0: M3G_ASSERT(M3G_FALSE); sl@0: isCompatible = M3G_FALSE; sl@0: break; sl@0: case M3G_CLASS_MESH: sl@0: isCompatible = (objClassID == M3G_CLASS_MESH) sl@0: || (objClassID == M3G_CLASS_MORPHING_MESH) sl@0: || (objClassID == M3G_CLASS_SKINNED_MESH); sl@0: break; sl@0: default: sl@0: isCompatible = (classID == objClassID); sl@0: } sl@0: } sl@0: sl@0: if (!isCompatible) { sl@0: /* Error, class mismatch */ sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: return NULL; sl@0: } sl@0: sl@0: /* Mark object as referenced */ sl@0: m3gReferenceLoaded(&loader->refArray, idx); sl@0: return obj; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads Object3D data sl@0: */ sl@0: static M3Gbool m3gLoadObject3DData(Loader *loader, M3GObject obj) sl@0: { sl@0: M3Guint animTracks, i, userParams, paramId, paramLength; sl@0: UserData *userData = NULL; sl@0: M3Gubyte *data = m3gGetSectionDataPtr(loader, 8); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: m3gSetUserID(obj, m3gLoadInt(data)); sl@0: data += 4; sl@0: sl@0: animTracks = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (animTracks >= 0x1fffffff) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, animTracks * 4 + 4) == M3G_FALSE) return M3G_FALSE; sl@0: for (i = 0; i < animTracks; i++) { sl@0: M3GAnimationTrack at = (M3GAnimationTrack)m3gGetLoaded(loader, m3gLoadInt(data),M3G_CLASS_ANIMATION_TRACK); sl@0: if (at == NULL || m3gAddAnimationTrack(obj, at) == -1) { sl@0: return M3G_FALSE; sl@0: } sl@0: data += 4; sl@0: } sl@0: sl@0: userParams = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: if (userParams != 0) { sl@0: /* Overflow? */ sl@0: if (userParams >= 0x10000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, userParams * 8) == M3G_FALSE) sl@0: return M3G_FALSE; /* Check the minimum size to avoid useless allocation */ sl@0: userData = (UserData *)m3gAllocZ(M3G_INTERFACE(loader), sizeof(UserData)); sl@0: if (userData == NULL) sl@0: return M3G_FALSE; sl@0: userData->object = obj; sl@0: userData->numParams = userParams; sl@0: userData->params = (M3Gbyte **)m3gAllocZ(M3G_INTERFACE(loader), userParams*sizeof(M3Gbyte *)); sl@0: userData->paramLengths = (M3Gsizei *)m3gAlloc(M3G_INTERFACE(loader), userParams*sizeof(M3Gsizei)); sl@0: userData->paramId = (M3Gint *)m3gAlloc(M3G_INTERFACE(loader), userParams*sizeof(M3Gint)); sl@0: if (userData->params == NULL || sl@0: userData->paramLengths == NULL || sl@0: userData->paramId == NULL || sl@0: m3gArrayAppend(&loader->userDataArray, userData, M3G_INTERFACE(loader)) == -1) { sl@0: m3gFree(M3G_INTERFACE(loader), userData->params); sl@0: m3gFree(M3G_INTERFACE(loader), userData->paramLengths); sl@0: m3gFree(M3G_INTERFACE(loader), userData->paramId); sl@0: m3gFree(M3G_INTERFACE(loader), userData); sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: for (i = 0; i < userParams; i++) { sl@0: if (m3gCheckSectionDataLength(loader, data, 8) == M3G_FALSE) return M3G_FALSE; sl@0: paramId = m3gLoadInt(data); sl@0: data += 4; sl@0: paramLength = m3gLoadInt(data); sl@0: data += 4; sl@0: userData->paramId[i] = paramId; sl@0: userData->paramLengths[i] = paramLength; sl@0: if (m3gCheckSectionDataLength(loader, data, paramLength) == M3G_FALSE) return M3G_FALSE; sl@0: userData->params[i] = (M3Gbyte *)m3gAlloc(M3G_INTERFACE(loader), paramLength*sizeof(M3Gbyte)); sl@0: if (userData->params[i] == NULL) sl@0: return M3G_FALSE; sl@0: m3gCopy(userData->params[i], data, paramLength); sl@0: data += paramLength; sl@0: } sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Skips Object3D data sl@0: */ sl@0: static M3Gbool m3gSkipObject3DData(Loader *loader) sl@0: { sl@0: M3Guint animTracks, i, userParams, paramLength; sl@0: M3Gubyte *data = m3gGetSectionDataPtr(loader, 8); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: data += 4; sl@0: animTracks = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (animTracks >= 0x1fffffff) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, animTracks * 4 + 4) == M3G_FALSE) return M3G_FALSE; sl@0: for (i = 0; i < animTracks; i++) { sl@0: data += 4; sl@0: } sl@0: sl@0: userParams = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: for (i = 0; i < userParams; i++) { sl@0: if (m3gCheckSectionDataLength(loader, data, 8) == M3G_FALSE) return M3G_FALSE; sl@0: data += 4; sl@0: paramLength = m3gLoadInt(data); sl@0: if (m3gCheckSectionDataLength(loader, data, paramLength) == M3G_FALSE) return M3G_FALSE; sl@0: data += 4 + paramLength; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads transformable data sl@0: */ sl@0: static M3Gbool m3gLoadTransformableData(Loader *loader, M3GTransformable obj) sl@0: { sl@0: M3Gfloat f1, f2, f3, f4; sl@0: M3Gubyte *data; sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 1); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: /* Component transform ? */ sl@0: if (*data++) { sl@0: if (m3gCheckSectionDataLength(loader, data, 40) == M3G_FALSE) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 0, &f1) || sl@0: !m3gLoadFloat(data + 4, &f2) || sl@0: !m3gLoadFloat(data + 8, &f3)) sl@0: return M3G_FALSE; sl@0: m3gSetTranslation(obj, f1, f2, f3); sl@0: if (!m3gLoadFloat(data + 12, &f1) || sl@0: !m3gLoadFloat(data + 16, &f2) || sl@0: !m3gLoadFloat(data + 20, &f3)) sl@0: return M3G_FALSE; sl@0: m3gSetScale(obj, f1, f2, f3); sl@0: if (!m3gLoadFloat(data + 24, &f1) || sl@0: !m3gLoadFloat(data + 28, &f2) || sl@0: !m3gLoadFloat(data + 32, &f3) || sl@0: !m3gLoadFloat(data + 36, &f4)) sl@0: return M3G_FALSE; sl@0: m3gSetOrientation(obj, f1, f2, f3, f4); sl@0: data += 40; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) return M3G_FALSE; sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: /* Generic transform */ sl@0: if (*data++) { sl@0: Matrix m; sl@0: if (m3gCheckSectionDataLength(loader, data, 64) == M3G_FALSE) return M3G_FALSE; sl@0: if (!m3gLoadMatrix(&m, data)) return M3G_FALSE; sl@0: m3gSetTransform(obj, &m); sl@0: data += 64; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Skips transformable data sl@0: */ sl@0: static M3Gbool m3gSkipTransformableData(Loader *loader) sl@0: { sl@0: M3Gubyte *data; sl@0: if (!m3gSkipObject3DData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 1); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: /* Component transform ? */ sl@0: if (*data++) { sl@0: if (m3gCheckSectionDataLength(loader, data, 40) == M3G_FALSE) return M3G_FALSE; sl@0: data += 40; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) return M3G_FALSE; sl@0: /* Generic transform */ sl@0: if (*data++) { sl@0: if (m3gCheckSectionDataLength(loader, data, 64) == M3G_FALSE) return M3G_FALSE; sl@0: data += 64; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads node data sl@0: */ sl@0: static M3Gbool m3gLoadNodeData(Loader *loader, M3GNode obj) sl@0: { sl@0: M3Gubyte *data; sl@0: if (!m3gLoadTransformableData(loader, (M3GTransformable)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 8); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gEnable(obj, 0, *data++); sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gEnable(obj, 1, *data++); sl@0: { sl@0: unsigned a = *data++; sl@0: m3gSetAlphaFactor(obj, m3gDivif(a, 255)); sl@0: } sl@0: m3gSetScope(obj, m3gLoadInt(data)); sl@0: data += 4; sl@0: sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: if (*data++) { sl@0: M3Gubyte zt, yt; sl@0: M3Gint zr, yr; sl@0: if (m3gCheckSectionDataLength(loader, data, 10) == M3G_FALSE) return M3G_FALSE; sl@0: zt = *data++; sl@0: yt = *data++; sl@0: zr = m3gLoadInt(data); sl@0: yr = m3gLoadInt(data + 4); sl@0: m3gSetAlignment(obj, (M3GNode)m3gGetLoaded(loader, zr, ANY_NODE_CLASS), sl@0: zt, sl@0: (M3GNode)m3gGetLoaded(loader, yr, ANY_NODE_CLASS), sl@0: yt); sl@0: data += 8; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Skips node data sl@0: */ sl@0: static M3Gbool m3gSkipNodeData(Loader *loader) sl@0: { sl@0: M3Gubyte *data; sl@0: if (!m3gSkipTransformableData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 8); sl@0: if (data == NULL) return M3G_FALSE; sl@0: data += 7; sl@0: sl@0: /* Alignment? */ sl@0: if (*data++) { sl@0: if (m3gCheckSectionDataLength(loader, data, 10) == M3G_FALSE) return M3G_FALSE; sl@0: data += 10; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a camera sl@0: */ sl@0: static M3Gbool m3gLoadCamera(Loader *loader) sl@0: { sl@0: M3Gfloat f1, f2, f3, f4; sl@0: M3Gubyte *data; sl@0: M3GCamera obj = m3gCreateCamera(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadNodeData(loader, (M3GNode)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 1); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: switch(*data++) { sl@0: case M3G_GENERIC: sl@0: { sl@0: Matrix m; sl@0: if (m3gCheckSectionDataLength(loader, data, 64) == M3G_FALSE) return M3G_FALSE; sl@0: if (!m3gLoadMatrix(&m, data)) return M3G_FALSE; sl@0: m3gSetProjectionMatrix(obj, &m); sl@0: data += 64; sl@0: } sl@0: break; sl@0: case M3G_PERSPECTIVE: sl@0: if (m3gCheckSectionDataLength(loader, data, 16) == M3G_FALSE) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 0, &f1) || sl@0: !m3gLoadFloat(data + 4, &f2) || sl@0: !m3gLoadFloat(data + 8, &f3) || sl@0: !m3gLoadFloat(data + 12, &f4)) sl@0: return M3G_FALSE; sl@0: m3gSetPerspective(obj, f1, f2, f3, f4); sl@0: data += 16; sl@0: break; sl@0: case M3G_PARALLEL: sl@0: if (m3gCheckSectionDataLength(loader, data, 16) == M3G_FALSE) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 0, &f1) || sl@0: !m3gLoadFloat(data + 4, &f2) || sl@0: !m3gLoadFloat(data + 8, &f3) || sl@0: !m3gLoadFloat(data + 12, &f4)) sl@0: return M3G_FALSE; sl@0: m3gSetParallel(obj, f1, f2, f3, f4); sl@0: data += 16; sl@0: break; sl@0: default: sl@0: /* Error */ sl@0: break; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a background sl@0: */ sl@0: static M3Gbool m3gLoadBackground(Loader *loader) sl@0: { sl@0: M3Gubyte *data; sl@0: M3GBackground obj = m3gCreateBackground(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 28); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: m3gSetBgColor(obj, m3gLoadARGB(data)); sl@0: data += 4; sl@0: m3gSetBgImage(obj, (M3GImage)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_IMAGE)); sl@0: data += 4; sl@0: m3gSetBgMode(obj, data[0], data[1]); sl@0: data += 2; sl@0: m3gSetBgCrop(obj, m3gLoadInt(data), sl@0: m3gLoadInt(data + 4), sl@0: m3gLoadInt(data + 8), sl@0: m3gLoadInt(data + 12) ); sl@0: data += 16; sl@0: sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gSetBgEnable(obj, 0, *data++); sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gSetBgEnable(obj, 1, *data++); sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a vertex array sl@0: */ sl@0: static M3Gbool m3gLoadVertexArray(Loader *loader) sl@0: { sl@0: M3Gint i, j; sl@0: M3Guint size; sl@0: M3Gushort vertices; sl@0: M3Gubyte *data, components, encoding; sl@0: M3Gdatatype componentSize; sl@0: M3GVertexArray obj; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipObject3DData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 5); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: if (*data != 1 && *data != 2) return M3G_FALSE; sl@0: componentSize = (*data++ == 1) ? M3G_BYTE : M3G_SHORT; sl@0: components = *data++; sl@0: encoding = *data++; sl@0: vertices = m3gLoadShort(data); sl@0: data += 2; sl@0: sl@0: size = vertices * components * (componentSize == M3G_BYTE ? 1 : 2); sl@0: sl@0: /* Overflow? */ sl@0: if (size < vertices) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, size) == M3G_FALSE) return M3G_FALSE; sl@0: obj = m3gCreateVertexArray(M3G_INTERFACE(loader), vertices, components, componentSize); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (componentSize == M3G_BYTE) { sl@0: M3Gbyte previousValues[4]; sl@0: m3gZero(previousValues, sizeof(previousValues)); sl@0: sl@0: for (i = 0; i < vertices; i++) { sl@0: for (j = 0; j < components; j++) { sl@0: if (encoding == 0) { sl@0: previousValues[j] = *data++; sl@0: } sl@0: else { sl@0: previousValues[j] = (M3Gbyte) (previousValues[j] + *data++); sl@0: } sl@0: } sl@0: m3gSetVertexArrayElements(obj, i, 1, sizeof(previousValues), componentSize, previousValues); sl@0: } sl@0: } sl@0: else { sl@0: M3Gshort previousValues[4]; sl@0: m3gZero(previousValues, sizeof(previousValues)); sl@0: sl@0: for (i = 0; i < vertices; i++) { sl@0: for (j = 0; j < components; j++) { sl@0: if (encoding == 0) { sl@0: previousValues[j] = m3gLoadShort(data); sl@0: } sl@0: else { sl@0: previousValues[j] = (M3Gshort) (previousValues[j] + m3gLoadShort(data)); sl@0: } sl@0: data += 2; sl@0: } sl@0: m3gSetVertexArrayElements(obj, i, 1, sizeof(previousValues), componentSize, previousValues); sl@0: } sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a vertex buffer sl@0: */ sl@0: static M3Gbool m3gLoadVertexBuffer(Loader *loader) sl@0: { sl@0: M3Gubyte *data; sl@0: M3GVertexArray va; sl@0: M3Gfloat bias[3], scale; sl@0: M3Guint i, taCount; sl@0: M3GVertexBuffer obj = m3gCreateVertexBuffer(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 36); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: m3gSetVertexDefaultColor(obj, m3gLoadARGB(data)); sl@0: data += 4; sl@0: sl@0: /* Positions */ sl@0: va = (M3GVertexArray)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_ARRAY); sl@0: data += 4; sl@0: if (!m3gLoadFloat(data + 0, &bias[0])) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 4, &bias[1])) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 8, &bias[2])) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 12, &scale)) return M3G_FALSE; sl@0: if (va != NULL) { sl@0: m3gSetVertexArray(obj, va, scale, bias, 3); sl@0: } sl@0: data += 16; sl@0: sl@0: /* Normals */ sl@0: va = (M3GVertexArray)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_ARRAY); sl@0: data += 4; sl@0: if (va != NULL) { sl@0: m3gSetNormalArray(obj, va); sl@0: } sl@0: sl@0: /* Colors */ sl@0: va = (M3GVertexArray)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_ARRAY); sl@0: data += 4; sl@0: if (va != NULL) { sl@0: m3gSetColorArray(obj, va); sl@0: } sl@0: sl@0: /* Texture coordinates */ sl@0: taCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (taCount >= 0x0ccccccc) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, taCount * 20) == M3G_FALSE) return M3G_FALSE; sl@0: for (i = 0; i < taCount; i++) { sl@0: va = (M3GVertexArray)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_ARRAY); sl@0: data += 4; sl@0: if (!m3gLoadFloat(data + 0, &bias[0])) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 4, &bias[1])) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 8, &bias[2])) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data + 12, &scale)) return M3G_FALSE; sl@0: if (va != NULL) { sl@0: m3gSetTexCoordArray(obj, i, va, scale, bias, 3); sl@0: } sl@0: else { sl@0: return M3G_FALSE; sl@0: } sl@0: data += 16; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a triangle strip array sl@0: */ sl@0: static M3Gbool m3gLoadTsa(Loader *loader) sl@0: { sl@0: M3Gubyte *data; sl@0: M3GIndexBuffer obj = 0; sl@0: M3Gubyte encoding; sl@0: M3Guint startIndex = 0, indicesCount = 0, *indices = NULL; sl@0: M3Guint lengthCount; sl@0: M3Gint *lengths = NULL; sl@0: M3Guint i; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipObject3DData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 1); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: encoding = *data++; sl@0: sl@0: switch(encoding) { sl@0: case 0: sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE; sl@0: startIndex = m3gLoadInt(data); sl@0: data += 4; sl@0: break; sl@0: sl@0: case 1: sl@0: if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) return M3G_FALSE; sl@0: startIndex = *data++; sl@0: break; sl@0: sl@0: case 2: sl@0: if (m3gCheckSectionDataLength(loader, data, 2) == M3G_FALSE) return M3G_FALSE; sl@0: startIndex = (M3Gushort) m3gLoadShort(data); sl@0: data += 2; sl@0: break; sl@0: sl@0: case 128: sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE; sl@0: indicesCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (indicesCount >= 0x20000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, indicesCount * 4) == M3G_FALSE) return M3G_FALSE; sl@0: indices = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gint) * indicesCount); sl@0: if (!indices) return M3G_FALSE; sl@0: for (i = 0; i < indicesCount; i++ ) { sl@0: indices[i] = m3gLoadInt(data); sl@0: data += 4; sl@0: } sl@0: break; sl@0: sl@0: case 129: sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE; sl@0: indicesCount = m3gLoadInt(data); sl@0: data += 4; sl@0: if (m3gCheckSectionDataLength(loader, data, indicesCount) == M3G_FALSE) return M3G_FALSE; sl@0: indices = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gint) * indicesCount); sl@0: if (!indices) return M3G_FALSE; sl@0: for (i = 0; i < indicesCount; i++ ) { sl@0: indices[i] = *data++; sl@0: } sl@0: break; sl@0: sl@0: case 130: sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE; sl@0: indicesCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (indicesCount >= 0x40000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, indicesCount * 2) == M3G_FALSE) return M3G_FALSE; sl@0: indices = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gint) * indicesCount); sl@0: if (!indices) return M3G_FALSE; sl@0: for (i = 0; i < indicesCount; i++) { sl@0: indices[i] = (M3Gushort)m3gLoadShort(data); sl@0: data += 2; sl@0: } sl@0: break; sl@0: sl@0: default: sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) goto cleanup; sl@0: lengthCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (lengthCount >= 0x20000000) { sl@0: goto cleanup; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, lengthCount * 4) == M3G_FALSE) goto cleanup; sl@0: lengths = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gint) * lengthCount); sl@0: if (!lengths) goto cleanup; sl@0: sl@0: for (i = 0; i < lengthCount; i++) { sl@0: lengths[i] = m3gLoadInt(data); sl@0: data += 4; sl@0: } sl@0: sl@0: if (encoding == 0 || encoding == 1 || encoding == 2) { sl@0: obj = m3gCreateImplicitStripBuffer( M3G_INTERFACE(loader), sl@0: lengthCount, sl@0: lengths, sl@0: startIndex); sl@0: } sl@0: else { sl@0: obj = m3gCreateStripBuffer( M3G_INTERFACE(loader), sl@0: M3G_TRIANGLE_STRIPS, sl@0: lengthCount, sl@0: lengths, sl@0: M3G_INT, sl@0: indicesCount, sl@0: indices); sl@0: } sl@0: sl@0: cleanup: sl@0: m3gFree(M3G_INTERFACE(loader), indices); sl@0: m3gFree(M3G_INTERFACE(loader), lengths); sl@0: sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a compositing mode sl@0: */ sl@0: static M3Gbool m3gLoadCompositingMode(Loader *loader) sl@0: { sl@0: M3Gfloat f1, f2; sl@0: M3Gubyte *data; sl@0: M3GCompositingMode obj = m3gCreateCompositingMode(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 14); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gEnableDepthTest (obj, *data++); sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gEnableDepthWrite (obj, *data++); sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gEnableColorWrite (obj, *data++); sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gSetAlphaWriteEnable (obj, *data++); sl@0: m3gSetBlending (obj, *data++); sl@0: { sl@0: unsigned a = *data++; sl@0: m3gSetAlphaThreshold(obj, m3gDivif(a, 255)); sl@0: } sl@0: if (!m3gLoadFloat(data, &f1) || !m3gLoadFloat(data + 4, &f2)) return M3G_FALSE; sl@0: m3gSetDepthOffset (obj, f1, f2); sl@0: data += 8; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a polygon mode sl@0: */ sl@0: static M3Gbool m3gLoadPolygonMode(Loader *loader) sl@0: { sl@0: M3Gubyte *data; sl@0: M3GPolygonMode obj = m3gCreatePolygonMode(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 6); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: m3gSetCulling (obj, *data++); sl@0: m3gSetShading (obj, *data++); sl@0: m3gSetWinding (obj, *data++); sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gSetTwoSidedLightingEnable (obj, *data++); sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gSetLocalCameraLightingEnable (obj, *data++); sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gSetPerspectiveCorrectionEnable (obj, *data++); sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads an appearance sl@0: */ sl@0: static M3Gbool m3gLoadAppearance(Loader *loader) sl@0: { sl@0: M3Guint textures, i; sl@0: M3Gubyte *data; sl@0: M3GAppearance obj = m3gCreateAppearance(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 21); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: m3gSetLayer(obj, (M3Gbyte)*data++); sl@0: m3gSetCompositingMode(obj, (M3GCompositingMode)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_COMPOSITING_MODE)); sl@0: data += 4; sl@0: m3gSetFog(obj, (M3GFog)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_FOG)); sl@0: data += 4; sl@0: m3gSetPolygonMode(obj, (M3GPolygonMode)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_POLYGON_MODE)); sl@0: data += 4; sl@0: m3gSetMaterial(obj, (M3GMaterial)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_MATERIAL)); sl@0: data += 4; sl@0: sl@0: textures = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (textures >= 0x20000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, textures * 4) == M3G_FALSE) return M3G_FALSE; sl@0: for (i = 0; i < textures; i++) { sl@0: M3GTexture tex = (M3GTexture)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_TEXTURE); sl@0: if (!tex) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSetTexture(obj, i, tex); sl@0: data += 4; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a mesh sl@0: */ sl@0: static M3Gbool m3gLoadMesh(Loader *loader) sl@0: { sl@0: M3Guint subMeshCount, i; sl@0: M3GVertexBuffer vb; sl@0: M3GIndexBuffer *ib = NULL; sl@0: M3GAppearance *ap = NULL; sl@0: M3Gubyte *data; sl@0: M3GMesh obj = NULL; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipNodeData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 8); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: vb = (M3GVertexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_BUFFER); sl@0: if (vb == NULL) return M3G_FALSE; sl@0: data += 4; sl@0: subMeshCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (subMeshCount >= 0x10000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, subMeshCount * 8) == M3G_FALSE) return M3G_FALSE; sl@0: ib = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ib) * subMeshCount); sl@0: ap = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ap) * subMeshCount); sl@0: if (!ib || !ap) goto cleanup; sl@0: sl@0: for (i = 0; i < subMeshCount; i++) { sl@0: M3GIndexBuffer indexBuffer; sl@0: indexBuffer = (M3GIndexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_INDEX_BUFFER); sl@0: if (indexBuffer != NULL && loader->triConstraint != 0) { sl@0: loader->triCount += indexBuffer->indexCount; sl@0: if (loader->triCount > loader->triConstraint) goto cleanup; sl@0: } sl@0: ib[i] = indexBuffer; sl@0: data += 4; sl@0: ap[i] = (M3GAppearance)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_APPEARANCE); sl@0: data += 4; sl@0: } sl@0: sl@0: obj = m3gCreateMesh( M3G_INTERFACE(loader), sl@0: vb, sl@0: ib, sl@0: ap, sl@0: subMeshCount); sl@0: sl@0: cleanup: sl@0: m3gFree(M3G_INTERFACE(loader), ib); sl@0: m3gFree(M3G_INTERFACE(loader), ap); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadNodeData(loader, (M3GNode)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads group data sl@0: */ sl@0: static M3Gbool m3gLoadGroupData(Loader *loader, M3GGroup obj) sl@0: { sl@0: M3Guint childCount, i; sl@0: M3Gubyte *data; sl@0: sl@0: if (!m3gLoadNodeData(loader, (M3GNode)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 4); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: childCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (childCount >= 0x20000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, childCount * 4) == M3G_FALSE) return M3G_FALSE; sl@0: for (i = 0; i < childCount; i++) { sl@0: m3gAddChild(obj, (M3GNode)m3gGetLoaded(loader, m3gLoadInt(data), ANY_NODE_CLASS)); sl@0: data += 4; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a group sl@0: */ sl@0: static M3Gbool m3gLoadGroup(Loader *loader) sl@0: { sl@0: M3GGroup obj = m3gCreateGroup(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadGroupData(loader, obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a world sl@0: */ sl@0: static M3Gbool m3gLoadWorld(Loader *loader) sl@0: { sl@0: M3GCamera cam; sl@0: M3Gubyte *data; sl@0: M3GWorld obj = m3gCreateWorld(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadGroupData(loader, (M3GGroup)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 8); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: cam = (M3GCamera)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_CAMERA); sl@0: data += 4; sl@0: if (cam != NULL) { sl@0: m3gSetActiveCamera(obj, cam); sl@0: } sl@0: m3gSetBackground(obj, (M3GBackground)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_BACKGROUND)); sl@0: data += 4; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a light sl@0: */ sl@0: static M3Gbool m3gLoadLight(Loader *loader) sl@0: { sl@0: M3Gfloat f1, f2, f3; sl@0: M3Gubyte *data; sl@0: M3GLight obj = m3gCreateLight(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadNodeData(loader, (M3GNode)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 28); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadFloat(data, &f1) || sl@0: !m3gLoadFloat(data + 4, &f2) || sl@0: !m3gLoadFloat(data + 8, &f3)) return M3G_FALSE; sl@0: m3gSetAttenuation (obj, f1, f2, f3); sl@0: data += 12; sl@0: m3gSetLightColor (obj, m3gLoadRGB(data)); sl@0: data += 3; sl@0: m3gSetLightMode (obj, *data++); sl@0: if (!m3gLoadFloat(data, &f1)) return M3G_FALSE; sl@0: m3gSetIntensity (obj, f1); sl@0: data += 4; sl@0: if (!m3gLoadFloat(data, &f1)) return M3G_FALSE; sl@0: m3gSetSpotAngle (obj, f1); sl@0: data += 4; sl@0: if (!m3gLoadFloat(data, &f1)) return M3G_FALSE; sl@0: m3gSetSpotExponent (obj, f1); sl@0: data += 4; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a keyframe sequence sl@0: */ sl@0: static M3Gbool m3gLoadKeyframeSequence(Loader *loader) sl@0: { sl@0: M3Guint i, j, interpolation, repeatMode, encoding, duration, sl@0: rangeFirst, rangeLast, components, keyFrames, size; sl@0: M3Gfloat *values; sl@0: M3Gubyte *data; sl@0: M3GKeyframeSequence obj; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipObject3DData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 23); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: interpolation = *data++; sl@0: repeatMode = *data++; sl@0: encoding = *data++; sl@0: duration = m3gLoadInt(data); sl@0: data += 4; sl@0: rangeFirst = m3gLoadInt(data); sl@0: data += 4; sl@0: rangeLast = m3gLoadInt(data); sl@0: data += 4; sl@0: components = m3gLoadInt(data); sl@0: data += 4; sl@0: keyFrames = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: if (encoding == 0) { sl@0: size = keyFrames * (4 + components * 4); sl@0: } sl@0: else { sl@0: size = components * 8 + keyFrames * (4 + components * (encoding == 1 ? 1 : 2)); sl@0: } sl@0: sl@0: /* Overflow? */ sl@0: if (size < keyFrames) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, size) == M3G_FALSE) return M3G_FALSE; sl@0: sl@0: obj = m3gCreateKeyframeSequence(M3G_INTERFACE(loader), keyFrames, components, interpolation); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: m3gSetRepeatMode(obj, repeatMode); sl@0: m3gSetDuration(obj, duration); sl@0: m3gSetValidRange(obj, rangeFirst, rangeLast); sl@0: sl@0: values = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gfloat) * components); sl@0: if (!values) return M3G_FALSE; sl@0: sl@0: if (encoding == 0) { sl@0: for (i = 0; i < keyFrames; i++ ) { sl@0: M3Gint time = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: for (j = 0; j < components; j++ ) { sl@0: if (!m3gLoadFloat(data, &values[j])) { sl@0: m3gFree(M3G_INTERFACE(loader), values); sl@0: return M3G_FALSE; sl@0: } sl@0: data += 4; sl@0: } sl@0: sl@0: m3gSetKeyframe(obj, i, time, components, values); sl@0: } sl@0: } sl@0: else { sl@0: M3Gfloat *vectorBiasScale = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gfloat) * components * 2); sl@0: if (!vectorBiasScale) { sl@0: m3gFree(M3G_INTERFACE(loader), values); sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: for (i = 0; i < components; i++ ) { sl@0: if (!m3gLoadFloat(data, &vectorBiasScale[i])) { sl@0: m3gFree(M3G_INTERFACE(loader), vectorBiasScale); sl@0: m3gFree(M3G_INTERFACE(loader), values); sl@0: return M3G_FALSE; sl@0: } sl@0: data += 4; sl@0: } sl@0: for (i = 0; i < components; i++ ) { sl@0: if (!m3gLoadFloat(data, &vectorBiasScale[i + components])) { sl@0: m3gFree(M3G_INTERFACE(loader), vectorBiasScale); sl@0: m3gFree(M3G_INTERFACE(loader), values); sl@0: return M3G_FALSE; sl@0: } sl@0: data += 4; sl@0: } sl@0: sl@0: for (i = 0; i < keyFrames; i++ ) { sl@0: M3Gint time; sl@0: time = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: if (encoding == 1) { sl@0: for (j = 0; j < components; j++ ) { sl@0: M3Gubyte v = *data++; sl@0: values[j] = vectorBiasScale[j] + ((vectorBiasScale[j + components] * v ) / 255.0f); sl@0: } sl@0: } sl@0: else { sl@0: for (j = 0; j < components; j++ ) { sl@0: M3Gushort v = m3gLoadShort(data); sl@0: data += 2; sl@0: values[j] = vectorBiasScale[j] + ((vectorBiasScale[j + components] * v) / 65535.0f); sl@0: } sl@0: } sl@0: sl@0: m3gSetKeyframe(obj, i, time, components, values); sl@0: } sl@0: sl@0: m3gFree(M3G_INTERFACE(loader), vectorBiasScale); sl@0: } sl@0: sl@0: m3gFree(M3G_INTERFACE(loader), values); sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads an animation controller sl@0: */ sl@0: static M3Gbool m3gLoadAnimationController(Loader *loader) sl@0: { sl@0: M3Gfloat speed, weight, referenceSeqTime; sl@0: M3Gint referenceWorldTime; sl@0: M3Gubyte *data; sl@0: M3GAnimationController obj = m3gCreateAnimationController(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 24); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadFloat(data, &speed)) return M3G_FALSE; sl@0: data += 4; sl@0: if (!m3gLoadFloat(data, &weight)) return M3G_FALSE; sl@0: data += 4; sl@0: m3gSetActiveInterval(obj, m3gLoadInt(data), m3gLoadInt(data + 4)); sl@0: data += 8; sl@0: if (!m3gLoadFloat(data, &referenceSeqTime)) return M3G_FALSE; sl@0: data += 4; sl@0: referenceWorldTime = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: m3gSetPosition(obj, referenceSeqTime, referenceWorldTime); sl@0: m3gSetSpeed(obj, speed, referenceWorldTime); sl@0: m3gSetWeight(obj, weight); sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads an animation track sl@0: */ sl@0: static M3Gbool m3gLoadAnimationTrack(Loader *loader) sl@0: { sl@0: M3Gint property; sl@0: M3GKeyframeSequence ks; sl@0: M3GAnimationController ac; sl@0: M3Gubyte *data; sl@0: M3GAnimationTrack obj; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipObject3DData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 12); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: ks = (M3GKeyframeSequence)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_KEYFRAME_SEQUENCE); sl@0: data += 4; sl@0: ac = (M3GAnimationController)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_ANIMATION_CONTROLLER); sl@0: data += 4; sl@0: property = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: obj = m3gCreateAnimationTrack(M3G_INTERFACE(loader), ks, property); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: m3gSetController(obj, ac); sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a material sl@0: */ sl@0: static M3Gbool m3gLoadMaterial(Loader *loader) sl@0: { sl@0: M3Gfloat f1; sl@0: M3Gubyte *data; sl@0: M3GMaterial obj = m3gCreateMaterial(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 18); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: m3gSetColor(obj, M3G_AMBIENT_BIT, m3gLoadRGB(data)); sl@0: data += 3; sl@0: m3gSetColor(obj, M3G_DIFFUSE_BIT, m3gLoadARGB(data)); sl@0: data += 4; sl@0: m3gSetColor(obj, M3G_EMISSIVE_BIT, m3gLoadRGB(data)); sl@0: data += 3; sl@0: m3gSetColor(obj, M3G_SPECULAR_BIT, m3gLoadRGB(data)); sl@0: data += 3; sl@0: if (!m3gLoadFloat(data, &f1)) return M3G_FALSE; sl@0: m3gSetShininess(obj, f1); sl@0: data += 4; sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: m3gSetVertexColorTrackingEnable(obj, *data++); sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a fog sl@0: */ sl@0: static M3Gbool m3gLoadFog(Loader *loader) sl@0: { sl@0: M3Gfloat f1, f2; sl@0: M3Gubyte *data; sl@0: M3GFog obj = m3gCreateFog(M3G_INTERFACE(loader)); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 4); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: m3gSetFogColor(obj, m3gLoadRGB(data)); sl@0: data += 3; sl@0: m3gSetFogMode(obj, *data); sl@0: sl@0: if (*data++ == M3G_EXPONENTIAL_FOG) { sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data, &f1)) return M3G_FALSE; sl@0: m3gSetFogDensity(obj, f1); sl@0: data += 4; sl@0: } sl@0: else { sl@0: if (m3gCheckSectionDataLength(loader, data, 8) == M3G_FALSE) return M3G_FALSE; sl@0: if (!m3gLoadFloat(data, &f1) || !m3gLoadFloat(data + 4, &f2)) return M3G_FALSE; sl@0: m3gSetFogLinear(obj, f1, f2); sl@0: data += 8; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads an image sl@0: */ sl@0: static M3Gbool m3gLoadImage(Loader *loader) sl@0: { sl@0: M3GImageFormat format; sl@0: M3Guint width, height; sl@0: M3Gbyte isMutable; sl@0: M3Gubyte *data; sl@0: M3GImage obj; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipObject3DData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 10); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: format = (M3GImageFormat)*data++; sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: isMutable = *data++; sl@0: width = m3gLoadInt(data); sl@0: data += 4; sl@0: height = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: if (isMutable) { sl@0: obj = m3gCreateImage(M3G_INTERFACE(loader), format, sl@0: width, height, sl@0: M3G_RENDERING_TARGET); sl@0: } sl@0: else { sl@0: M3Gubyte *palette = NULL, *pixels = NULL; sl@0: M3Gint paletteLength, pixelsLength, bpp; sl@0: sl@0: switch(format) { sl@0: case M3G_ALPHA: bpp = 1; break; sl@0: case M3G_LUMINANCE: bpp = 1; break; sl@0: case M3G_LUMINANCE_ALPHA: bpp = 2; break; sl@0: case M3G_RGB: bpp = 3; break; sl@0: case M3G_RGBA: bpp = 4; break; sl@0: default: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE; sl@0: paletteLength = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: if (paletteLength > 0) { sl@0: if (m3gCheckSectionDataLength(loader, data, paletteLength) == M3G_FALSE) return M3G_FALSE; sl@0: palette = data; sl@0: data += paletteLength; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE; sl@0: pixelsLength = m3gLoadInt(data); sl@0: data += 4; sl@0: if (m3gCheckSectionDataLength(loader, data, pixelsLength) == M3G_FALSE) return M3G_FALSE; sl@0: pixels = data; sl@0: data += pixelsLength; sl@0: sl@0: if (palette != NULL) { sl@0: obj = m3gCreateImage(M3G_INTERFACE(loader), format, sl@0: width, height, sl@0: M3G_PALETTED); sl@0: if (obj != NULL) { sl@0: M3Gint numEntries = paletteLength / bpp; sl@0: if (numEntries > 256) { sl@0: numEntries = 256; sl@0: } sl@0: m3gSetImage(obj, pixels); sl@0: m3gSetImagePalette(obj, numEntries, palette); sl@0: m3gCommitImage(obj); sl@0: } sl@0: } sl@0: else { sl@0: obj = m3gCreateImage(M3G_INTERFACE(loader), format, sl@0: width, height, sl@0: 0); sl@0: if (obj != NULL) { sl@0: m3gSetImage(obj, pixels); sl@0: m3gCommitImage(obj); sl@0: } sl@0: } sl@0: } sl@0: sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadObject3DData(loader, (M3GObject)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a texture sl@0: */ sl@0: static M3Gbool m3gLoadTexture(Loader *loader) sl@0: { sl@0: M3GImage image; sl@0: M3Gubyte *data; sl@0: M3GTexture obj; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipTransformableData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 12); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: image = (M3GImage)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_IMAGE); sl@0: data += 4; sl@0: sl@0: obj = m3gCreateTexture(M3G_INTERFACE(loader), image); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: m3gSetBlendColor(obj, m3gLoadRGB(data)); sl@0: data += 3; sl@0: m3gTextureSetBlending(obj, *data++); sl@0: m3gSetWrapping(obj, data[0], data[1]); sl@0: data += 2; sl@0: m3gSetFiltering(obj, data[0], data[1]); sl@0: data += 2; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadTransformableData(loader, (M3GTransformable)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a skinned mesh sl@0: */ sl@0: static M3Gbool m3gLoadSkinnedMesh(Loader *loader) sl@0: { sl@0: M3GVertexBuffer vb; sl@0: M3Guint i, subMeshCount, transformReferenceCount, firstVertex, vertexCount; sl@0: M3Gint weight; sl@0: M3GIndexBuffer *ib = NULL; sl@0: M3GAppearance *ap = NULL; sl@0: M3GGroup skeleton; sl@0: M3GNode bone; sl@0: M3Gubyte *data; sl@0: M3GSkinnedMesh obj = NULL; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipNodeData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 8); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: vb = (M3GVertexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_BUFFER); sl@0: if (vb == NULL) return M3G_FALSE; sl@0: data += 4; sl@0: subMeshCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (subMeshCount >= 0x10000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, subMeshCount * 8) == M3G_FALSE) return M3G_FALSE; sl@0: ib = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ib) * subMeshCount); sl@0: ap = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ap) * subMeshCount); sl@0: if (!ib || !ap) goto cleanup; sl@0: sl@0: for (i = 0; i < subMeshCount; i++) { sl@0: ib[i] = (M3GIndexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_INDEX_BUFFER); sl@0: data += 4; sl@0: ap[i] = (M3GAppearance)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_APPEARANCE); sl@0: data += 4; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, 8) == M3G_FALSE) goto cleanup; sl@0: skeleton = (M3GGroup)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_GROUP); sl@0: data += 4; sl@0: sl@0: obj = m3gCreateSkinnedMesh( M3G_INTERFACE(loader), sl@0: vb, sl@0: ib, sl@0: ap, sl@0: subMeshCount, sl@0: skeleton); sl@0: sl@0: cleanup: sl@0: m3gFree(M3G_INTERFACE(loader), ib); sl@0: m3gFree(M3G_INTERFACE(loader), ap); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: transformReferenceCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (transformReferenceCount >= 0x08000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, transformReferenceCount * 16) == M3G_FALSE) return M3G_FALSE; sl@0: for (i = 0; i < transformReferenceCount; i++) { sl@0: bone = (M3GNode)m3gGetLoaded(loader, m3gLoadInt(data), ANY_NODE_CLASS); sl@0: data += 4; sl@0: firstVertex = m3gLoadInt(data); sl@0: data += 4; sl@0: vertexCount = m3gLoadInt(data); sl@0: data += 4; sl@0: weight = m3gLoadInt(data); sl@0: data += 4; sl@0: m3gAddTransform(obj, bone, weight, firstVertex, vertexCount); sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadNodeData(loader, (M3GNode)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a morphing mesh sl@0: */ sl@0: static M3Gbool m3gLoadMorphingMesh(Loader *loader) sl@0: { sl@0: M3GVertexBuffer vb, *targets = NULL; sl@0: M3Guint i, subMeshCount, targetCount; sl@0: M3Gfloat *weights = NULL; sl@0: M3GIndexBuffer *ib = NULL; sl@0: M3GAppearance *ap = NULL; sl@0: M3Gubyte *data; sl@0: M3GMorphingMesh obj = NULL; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipNodeData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 8); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: vb = (M3GVertexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_BUFFER); sl@0: if (vb == NULL) return M3G_FALSE; sl@0: data += 4; sl@0: subMeshCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (subMeshCount >= 0x10000000) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, subMeshCount * 8) == M3G_FALSE) return M3G_FALSE; sl@0: ib = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ib) * subMeshCount); sl@0: ap = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ap) * subMeshCount); sl@0: if (!ib || !ap) goto cleanup; sl@0: sl@0: for (i = 0; i < subMeshCount; i++) { sl@0: ib[i] = (M3GIndexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_INDEX_BUFFER); sl@0: data += 4; sl@0: ap[i] = (M3GAppearance)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_APPEARANCE); sl@0: data += 4; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) goto cleanup; sl@0: targetCount = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Overflow? */ sl@0: if (targetCount >= 0x10000000) { sl@0: goto cleanup; sl@0: } sl@0: sl@0: if (m3gCheckSectionDataLength(loader, data, targetCount * 8) == M3G_FALSE) goto cleanup; sl@0: weights = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gfloat) * targetCount); sl@0: targets = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*targets) * targetCount); sl@0: if (!weights || !targets) goto cleanup; sl@0: sl@0: for (i = 0; i < targetCount; i++) { sl@0: targets[i] = (M3GVertexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_BUFFER); sl@0: data += 4; sl@0: if (!m3gLoadFloat(data, &weights[i])) goto cleanup; sl@0: data += 4; sl@0: } sl@0: sl@0: obj = m3gCreateMorphingMesh( M3G_INTERFACE(loader), sl@0: vb, sl@0: targets, sl@0: ib, sl@0: ap, sl@0: subMeshCount, sl@0: targetCount); sl@0: sl@0: cleanup: sl@0: m3gFree(M3G_INTERFACE(loader), ib); sl@0: m3gFree(M3G_INTERFACE(loader), ap); sl@0: m3gFree(M3G_INTERFACE(loader), targets); sl@0: m3gFree(M3G_INTERFACE(loader), weights); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadNodeData(loader, (M3GNode)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a sprite sl@0: */ sl@0: static M3Gbool m3gLoadSprite(Loader *loader) sl@0: { sl@0: M3GImage image; sl@0: M3GAppearance appearance; sl@0: M3Gubyte *data; sl@0: M3GSprite obj; sl@0: sl@0: m3gBeginObject(loader); sl@0: if (!m3gSkipNodeData(loader)) return M3G_FALSE; sl@0: sl@0: data = m3gGetSectionDataPtr(loader, 25); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: image = (M3GImage)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_IMAGE); sl@0: data += 4; sl@0: appearance = (M3GAppearance)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_APPEARANCE); sl@0: data += 4; sl@0: sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: obj = m3gCreateSprite( M3G_INTERFACE(loader), sl@0: *data++, sl@0: image, sl@0: appearance); sl@0: loader->loadedObject = (M3GObject)obj; sl@0: if (!obj) return M3G_FALSE; sl@0: sl@0: m3gSetCrop(obj, m3gLoadInt(data), sl@0: m3gLoadInt(data + 4), sl@0: m3gLoadInt(data + 8), sl@0: m3gLoadInt(data + 12)); sl@0: data += 16; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: m3gEndObject(loader); sl@0: m3gRewindObject(loader); sl@0: if (!m3gLoadNodeData(loader, (M3GNode)obj)) { sl@0: return M3G_FALSE; sl@0: } sl@0: m3gSkipObject(loader); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads a M3G file header sl@0: */ sl@0: static M3Gbool m3gLoadHeader(Loader *loader) sl@0: { sl@0: M3Gubyte *data; sl@0: data = m3gGetSectionDataPtr(loader, 12); sl@0: if (data == NULL) return M3G_FALSE; sl@0: sl@0: /* Check version */ sl@0: if (data[0] != 1 || data[1] != 0 || loader->sectionNum != 0) { sl@0: return M3G_FALSE; sl@0: } sl@0: data += 2; sl@0: sl@0: if (!m3gVerifyBool(data)) return M3G_FALSE; sl@0: loader->hasReferences = *data++; sl@0: loader->fileSize = m3gLoadInt(data); sl@0: data += 4; sl@0: loader->contentSize = m3gLoadInt(data); sl@0: data += 4; sl@0: sl@0: /* Skip authoring field */ sl@0: while(*data++) sl@0: if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) return M3G_FALSE; sl@0: sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Skips external reference sl@0: */ sl@0: static M3Gbool m3gLoadExternalReference(Loader *loader) sl@0: { sl@0: M3Gubyte *data; sl@0: if (loader->sectionNum != 1 || !loader->hasReferences) sl@0: return M3G_FALSE; sl@0: data = m3gGetSectionDataPtr(loader, 1); sl@0: while(*data++) { /* Skip string */ sl@0: if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) sl@0: return M3G_FALSE; sl@0: } sl@0: m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0)); sl@0: return M3G_TRUE; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Loads an object sl@0: */ sl@0: static LoaderState m3gLoadObject(Loader *loader) sl@0: { sl@0: M3Gubyte *data = m3gGetSectionDataPtr(loader, loader->sectionBytesRequired); sl@0: sl@0: if (data == NULL) { sl@0: loader->localState = LOADSTATE_CHECKSUM; sl@0: return LOADSTATE_SECTION; sl@0: } sl@0: sl@0: m3gAdvanceSectionData(loader, loader->sectionBytesRequired); sl@0: sl@0: if (loader->localState == LOADSTATE_ENTER) { sl@0: M3Gbool status = M3G_TRUE; sl@0: sl@0: loader->objectType = *data++; sl@0: loader->loadedObject = NULL; sl@0: loader->localState = LOADSTATE_EXIT; sl@0: loader->sectionBytesRequired = m3gLoadInt(data); sl@0: data += 4; sl@0: if (loader->sectionNum == 0 && loader->objectType != 0) { sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: sl@0: switch(loader->objectType) { sl@0: case 0: /* Header Object */ sl@0: status = m3gLoadHeader(loader); sl@0: break; sl@0: case 1: /* AnimationController */ sl@0: status = m3gLoadAnimationController(loader); sl@0: break; sl@0: case 2: /* AnimationTrack */ sl@0: status = m3gLoadAnimationTrack(loader); sl@0: break; sl@0: case 3: /* Appearance */ sl@0: status = m3gLoadAppearance(loader); sl@0: break; sl@0: case 4: /* Background */ sl@0: status = m3gLoadBackground(loader); sl@0: break; sl@0: case 5: /* Camera */ sl@0: status = m3gLoadCamera(loader); sl@0: break; sl@0: case 6: /* CompositingMode */ sl@0: status = m3gLoadCompositingMode(loader); sl@0: break; sl@0: case 7: /* Fog */ sl@0: status = m3gLoadFog(loader); sl@0: break; sl@0: case 8: /* PolygonMode */ sl@0: status = m3gLoadPolygonMode(loader); sl@0: break; sl@0: case 9: /* Group */ sl@0: status = m3gLoadGroup(loader); sl@0: break; sl@0: case 10: /* Image2D */ sl@0: status = m3gLoadImage(loader); sl@0: break; sl@0: case 11: /* TriangleStripArray */ sl@0: status = m3gLoadTsa(loader); sl@0: break; sl@0: case 12: /* Light */ sl@0: status = m3gLoadLight(loader); sl@0: break; sl@0: case 13: /* Material */ sl@0: status = m3gLoadMaterial(loader); sl@0: break; sl@0: case 14: /* Mesh */ sl@0: status = m3gLoadMesh(loader); sl@0: break; sl@0: case 15: /* MorphingMesh */ sl@0: status = m3gLoadMorphingMesh(loader); sl@0: break; sl@0: case 16: /* SkinnedMesh */ sl@0: status = m3gLoadSkinnedMesh(loader); sl@0: break; sl@0: case 17: /* Texture2D */ sl@0: status = m3gLoadTexture(loader); sl@0: break; sl@0: case 18: /* Sprite */ sl@0: status = m3gLoadSprite(loader); sl@0: break; sl@0: case 19: /* KeyframeSequence */ sl@0: status = m3gLoadKeyframeSequence(loader); sl@0: break; sl@0: case 20: /* VertexArray */ sl@0: status = m3gLoadVertexArray(loader); sl@0: break; sl@0: case 21: /* VertexBuffer */ sl@0: status = m3gLoadVertexBuffer(loader); sl@0: break; sl@0: case 22: /* World */ sl@0: status = m3gLoadWorld(loader); sl@0: break; sl@0: case 255: /* External Reference */ sl@0: status = m3gLoadExternalReference(loader); sl@0: break; sl@0: default: /* 23 ... 254 Reserved for use in future versions of the file format */ sl@0: status = M3G_FALSE; sl@0: break; sl@0: } sl@0: sl@0: /* Check if object loading caused an error */ sl@0: if (m3gErrorRaised(M3G_INTERFACE(loader))) { sl@0: m3gDeleteObject(loader->loadedObject); sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: if (!status || sl@0: m3gGetSectionDataPtr(loader, 0) != data + loader->sectionBytesRequired) { sl@0: m3gDeleteObject(loader->loadedObject); sl@0: m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR); sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: loader->sectionBytesRequired = 0; sl@0: sl@0: /* Add object to loaded objects array */ sl@0: if (loader->loadedObject != NULL) { sl@0: if (m3gArrayAppend(&loader->refArray, loader->loadedObject, M3G_INTERFACE(loader)) == -1) { sl@0: /* OOM */ sl@0: m3gDeleteObject(loader->loadedObject); sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: m3gAddRef(loader->loadedObject); sl@0: } sl@0: } sl@0: else { sl@0: loader->sectionBytesRequired = M3G_MIN_OBJECT_SIZE; sl@0: loader->localState = LOADSTATE_ENTER; sl@0: } sl@0: sl@0: return LOADSTATE_OBJECT; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Handles branching to different subroutines upon re-entry sl@0: * sl@0: * When sufficient data is available in internal buffers, this sl@0: * function gets called and directs execution into the coroutine sl@0: * matching the current global state. sl@0: */ sl@0: static LoaderState m3gLoaderMain(Loader *loader) sl@0: { sl@0: M3G_VALIDATE_OBJECT(loader); sl@0: M3G_ASSERT(loader->bytesRequired > 0); sl@0: M3G_ASSERT(loader->bytesRequired <= loader->stream.bytesAvailable); sl@0: sl@0: switch (loader->state) { sl@0: case LOADSTATE_INITIAL: sl@0: loader->stream.totalBytes = 0; sl@0: loader->localState = LOADSTATE_ENTER; sl@0: loader->fileSize = 0x00ffffff; sl@0: loader->bytesRequired = sizeof(PNG_FILE_IDENTIFIER); sl@0: return LOADSTATE_IDENTIFIER; sl@0: case LOADSTATE_IDENTIFIER: sl@0: return m3gLoadIdentifier(loader); sl@0: case LOADSTATE_SECTION: sl@0: return m3gLoadSection(loader); sl@0: case LOADSTATE_OBJECT: sl@0: return m3gLoadObject(loader); sl@0: default: sl@0: loader->bytesRequired = 0; sl@0: return LOADSTATE_ERROR; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \brief Deletes all unreferenced objects sl@0: */ sl@0: static void m3gCleanupLoader(M3GLoader loader) sl@0: { sl@0: M3Gint i, j, n; sl@0: PointerArray *refs; sl@0: M3Gbool referenced; sl@0: M3GObject obj; sl@0: sl@0: refs = &loader->refArray; sl@0: n = m3gArraySize(refs); sl@0: sl@0: /* All unreferenced objects will be deleted, as their ref count becomes 0 */ sl@0: for (i = 0; i < n; ++i) { sl@0: obj = m3gGetLoadedPtr(refs, i, &referenced); sl@0: m3gDeleteRef(obj); sl@0: } sl@0: m3gClearArray(&loader->refArray); sl@0: sl@0: n = m3gArraySize(&loader->userDataArray); sl@0: for (i = 0; i < n; ++i) sl@0: { sl@0: UserData *data = (UserData *)m3gGetArrayElement(&loader->userDataArray, i); sl@0: for (j = 0; j < data->numParams; ++j) sl@0: m3gFree(M3G_INTERFACE(loader), data->params[j]); sl@0: m3gFree(M3G_INTERFACE(loader), data->params); sl@0: m3gFree(M3G_INTERFACE(loader), data->paramLengths); sl@0: m3gFree(M3G_INTERFACE(loader), data->paramId); sl@0: m3gFree(M3G_INTERFACE(loader), data); sl@0: } sl@0: m3gClearArray(&loader->userDataArray); sl@0: sl@0: m3gFree(M3G_INTERFACE(loader), loader->allocatedSectionData); sl@0: loader->allocatedSectionData = NULL; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Resets the loader sl@0: */ sl@0: static void m3gResetLoader(Loader *loader) sl@0: { sl@0: /* Reset loader state */ sl@0: loader->state = LOADSTATE_INITIAL; sl@0: loader->bytesRequired = sizeof(PNG_FILE_IDENTIFIER); sl@0: sl@0: m3gCleanupLoader(loader); sl@0: m3gResetBufferedData(&loader->stream); sl@0: } sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Virtual function table sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: static const ObjectVFTable m3gvf_Loader = { sl@0: NULL, /* ApplyAnimation */ sl@0: NULL, /* IsCompatible */ sl@0: NULL, /* UpdateProperty */ sl@0: NULL, /* DoGetReferences */ sl@0: NULL, /* FindID */ sl@0: NULL, /* Duplicate */ sl@0: m3gDestroyLoader sl@0: }; sl@0: sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Public interface sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: /*! sl@0: * \brief Creates a new loader instance sl@0: */ sl@0: M3G_API M3GLoader m3gCreateLoader(M3GInterface m3g) sl@0: { sl@0: Loader *loader; sl@0: M3G_VALIDATE_INTERFACE(m3g); sl@0: sl@0: loader = m3gAllocZ(m3g, sizeof(*loader)); sl@0: if (loader != NULL) { sl@0: m3gInitObject(&loader->object, m3g, M3G_CLASS_LOADER); sl@0: m3gInitArray(&loader->refArray); sl@0: m3gInitArray(&loader->userDataArray); sl@0: loader->bytesRequired = sizeof(PNG_FILE_IDENTIFIER); sl@0: loader->state = LOADSTATE_INITIAL; sl@0: loader->sectionNum = -1; sl@0: } sl@0: return loader; sl@0: } sl@0: sl@0: /*! sl@0: * \brief Import a set of objects into the reference table of a loader sl@0: * sl@0: * This is intended for passing in external references, decoded in sl@0: * Java sl@0: */ sl@0: M3G_API void m3gImportObjects(M3GLoader loader, M3Gint n, M3GObject *refs) sl@0: { sl@0: int i; sl@0: M3G_VALIDATE_OBJECT(loader); sl@0: sl@0: if (loader->state == LOADSTATE_DONE) sl@0: m3gResetLoader(loader); sl@0: for (i = 0; i < n; ++i) { sl@0: /* For loop is interrupted in case of OOM */ sl@0: if (m3gArrayAppend(&loader->refArray, sl@0: refs[i], sl@0: M3G_INTERFACE(loader)) == -1) { sl@0: break; sl@0: } sl@0: m3gAddRef(refs[i]); sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \brief Return the complete reference table for this loader instance sl@0: * sl@0: * The reference table will contain the handle of each object that has sl@0: * been loaded so far. sl@0: * sl@0: * \param loader loader instance sl@0: * \param buffer destination buffer, or NULL to just get sl@0: * the number of unreferenced objects sl@0: * \return number of unreferenced objects sl@0: */ sl@0: M3G_API M3Gint m3gGetLoadedObjects(M3GLoader loader, M3GObject *buffer) sl@0: { sl@0: PointerArray *refs; sl@0: int i, n, unref = 0; sl@0: M3Gbool referenced; sl@0: M3GObject obj, *dst = buffer; sl@0: M3G_VALIDATE_OBJECT(loader); sl@0: sl@0: /* If error in decoding, reset and return 0 objects */ sl@0: if (loader->state < LOADSTATE_INITIAL) { sl@0: return 0; sl@0: } sl@0: sl@0: refs = &loader->refArray; sl@0: n = m3gArraySize(refs); sl@0: sl@0: /* Scan unreferenced objects */ sl@0: for (i = 0; i < n; ++i) { sl@0: obj = m3gGetLoadedPtr(refs, i, &referenced); sl@0: if (!referenced) { sl@0: unref++; sl@0: if (dst != NULL) { sl@0: *dst++ = obj; sl@0: } sl@0: } sl@0: } sl@0: sl@0: return unref; sl@0: } sl@0: sl@0: /*! sl@0: * \brief Submits data to the loader for processing sl@0: * sl@0: * Upon data input, the loader will either read more objects from the sl@0: * stream or buffer the data for processing later on. The return value sl@0: * will indicate how many bytes of data are required before the next sl@0: * data element can be loaded, but that data can still be submitted in sl@0: * smaller blocks. sl@0: * sl@0: * \param loader the loader instance sl@0: * \param bytes the number of bytes in the data sl@0: * \param data pointer to the data sl@0: * sl@0: * \return the number of bytes required to load the next data element, sl@0: * or zero to indicate that loading has finished sl@0: */ sl@0: sl@0: #ifdef M3G_ENABLE_PROFILING sl@0: static M3Gsizei m3gDecodeDataInternal(M3GLoader loader, sl@0: M3Gsizei bytes, sl@0: const M3Gubyte *data); sl@0: sl@0: M3G_API M3Gsizei m3gDecodeData(M3GLoader loader, sl@0: M3Gsizei bytes, sl@0: const M3Gubyte *data) sl@0: { sl@0: M3Gsizei bytesReq; sl@0: M3G_BEGIN_PROFILE(M3G_INTERFACE(loader), M3G_PROFILE_LOADER_DECODE); sl@0: bytesReq = m3gDecodeDataInternal(loader, bytes, data); sl@0: M3G_END_PROFILE(M3G_INTERFACE(loader), M3G_PROFILE_LOADER_DECODE); sl@0: return bytesReq; sl@0: } sl@0: sl@0: static M3Gsizei m3gDecodeDataInternal(M3GLoader loader, sl@0: M3Gsizei bytes, sl@0: const M3Gubyte *data) sl@0: sl@0: #else sl@0: M3G_API M3Gsizei m3gDecodeData(M3GLoader loader, sl@0: M3Gsizei bytes, sl@0: const M3Gubyte *data) sl@0: sl@0: #endif sl@0: { sl@0: m3gErrorHandler *errorHandler; sl@0: Interface *m3g = M3G_INTERFACE(loader); sl@0: sl@0: M3G_VALIDATE_OBJECT(loader); sl@0: M3G_VALIDATE_INTERFACE(m3g); sl@0: sl@0: /* Check for errors */ sl@0: if (bytes <= 0 || data == NULL) { sl@0: m3gRaiseError(m3g, M3G_INVALID_VALUE); sl@0: return 0; sl@0: } sl@0: sl@0: if (loader->state == LOADSTATE_DONE) sl@0: m3gResetLoader(loader); sl@0: sl@0: /* Submit data, then load until we run out of data again or are sl@0: * finished */ sl@0: sl@0: if (!m3gBufferData(m3g, &loader->stream, bytes, data)) { sl@0: return 0; sl@0: } sl@0: sl@0: /* Disable error handler */ sl@0: errorHandler = m3gSetErrorHandler(m3g, NULL); sl@0: sl@0: /* Continue loading if sufficient data has arrived */ sl@0: while (loader->bytesRequired > 0 sl@0: && loader->bytesRequired <= loader->stream.bytesAvailable) { sl@0: loader->state = m3gLoaderMain(loader); sl@0: } sl@0: sl@0: /* Restore error handler */ sl@0: m3gSetErrorHandler(m3g, errorHandler); sl@0: sl@0: /* Check if error was raised */ sl@0: if (m3gErrorRaised(m3g) != M3G_NO_ERROR) { sl@0: /* Need to free all loaded objects */ sl@0: m3gResetLoader(loader); sl@0: sl@0: /* Raise again with original error handler in place */ sl@0: if (m3gErrorRaised(m3g) == M3G_OUT_OF_MEMORY) sl@0: m3gRaiseError(m3g, M3G_OUT_OF_MEMORY); sl@0: else sl@0: m3gRaiseError(m3g, M3G_IO_ERROR); sl@0: return 0; sl@0: } sl@0: sl@0: /* Return the number of bytes we need for loading to proceed sl@0: * further; clamp to zero in case we're exiting due to an error, sl@0: * or just have been fed excess data */ sl@0: { sl@0: M3Gsizei bytesReq = sl@0: loader->bytesRequired - loader->stream.bytesAvailable; sl@0: sl@0: /* Check if whole file is done */ sl@0: if (loader->stream.totalBytes >= loader->fileSize) { sl@0: loader->state = LOADSTATE_DONE; sl@0: bytesReq = 0; sl@0: } sl@0: sl@0: return (bytesReq >= 0) ? bytesReq : 0; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \brief Return all loaded objects with user parameters sl@0: * sl@0: * \param loader the loader instance sl@0: * \param objects an array for objects with user parameters, sl@0: * or null to return the number of objects sl@0: * sl@0: * \return Number of objects with user parameters sl@0: */ sl@0: M3G_API M3Gint m3gGetObjectsWithUserParameters(M3GLoader loader, M3GObject *objects) { sl@0: const Loader *ldr = (const Loader *) loader; sl@0: M3G_VALIDATE_OBJECT(ldr); sl@0: { sl@0: M3Gint i, n; sl@0: n = m3gArraySize(&ldr->userDataArray); sl@0: sl@0: if (objects != NULL) sl@0: for (i = 0; i < n; ++i) sl@0: { sl@0: const UserData *data = (const UserData *)m3gGetArrayElement(&ldr->userDataArray, i); sl@0: objects[i] = data->object; sl@0: } sl@0: return n; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \brief Return the number of user parameters loaded for an object sl@0: */ sl@0: M3G_API M3Gint m3gGetNumUserParameters(M3GLoader loader, M3Gint object) sl@0: { sl@0: const Loader *ldr = (const Loader *) loader; sl@0: M3G_VALIDATE_OBJECT(ldr); sl@0: { sl@0: const UserData *data = (const UserData *)m3gGetArrayElement(&ldr->userDataArray, object); sl@0: return (data != NULL) ? data->numParams : 0; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \brief Set constraints for loading. sl@0: * \param triConstraint maximum triangle count sl@0: */ sl@0: M3G_API void m3gSetConstraints(M3GLoader loader, M3Gint triConstraint) sl@0: { sl@0: M3G_VALIDATE_OBJECT(loader); sl@0: loader->triConstraint = triConstraint; sl@0: } sl@0: sl@0: /*! sl@0: * \brief Return the given user parameter for an object sl@0: * sl@0: * \param loader the loader instance sl@0: * \param object the object to query sl@0: * \param index index of the string to query sl@0: * \param buffer buffer to copy the data into, sl@0: * or NULL to just query the length sl@0: * sl@0: * \return id of the parameter, or the length of the string if data was NULL sl@0: */ sl@0: M3G_API M3Gsizei m3gGetUserParameter(M3GLoader loader, sl@0: M3Gint object, sl@0: M3Gint index, sl@0: M3Gbyte *buffer) sl@0: { sl@0: const Loader *ldr = (const Loader *) loader; sl@0: M3G_VALIDATE_OBJECT(ldr); sl@0: { sl@0: const UserData *data = (const UserData *)m3gGetArrayElement(&ldr->userDataArray, object); sl@0: if (data != NULL && m3gInRange(index, 0, data->numParams - 1)) { sl@0: const char *src = (const char *)data->params[index]; sl@0: M3Gsizei len = data->paramLengths[index]; sl@0: if (buffer != NULL) { sl@0: m3gCopy(buffer, src, len); sl@0: return data->paramId[index]; sl@0: } sl@0: else sl@0: return len; sl@0: } sl@0: else sl@0: return 0; sl@0: } sl@0: } sl@0: sl@0: #undef ANY_NODE_CLASS sl@0: