os/graphics/m3g/m3gcore11/src/m3g_loader.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
/*
sl@0
     2
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     3
* All rights reserved.
sl@0
     4
* This component and the accompanying materials are made available
sl@0
     5
* under the terms of the License "Eclipse Public License v1.0"
sl@0
     6
* which accompanies this distribution, and is available
sl@0
     7
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     8
*
sl@0
     9
* Initial Contributors:
sl@0
    10
* Nokia Corporation - initial contribution.
sl@0
    11
*
sl@0
    12
* Contributors:
sl@0
    13
*
sl@0
    14
* Description: Native implementation of the Loader class
sl@0
    15
*
sl@0
    16
*/
sl@0
    17
sl@0
    18
sl@0
    19
/*!
sl@0
    20
 * \internal
sl@0
    21
 * \file
sl@0
    22
 * \brief Native implementation of the Loader class
sl@0
    23
 *
sl@0
    24
*/
sl@0
    25
sl@0
    26
#include "m3g_object.h"
sl@0
    27
#include "m3g_array.h"
sl@0
    28
sl@0
    29
/*----------------------------------------------------------------------
sl@0
    30
 * Internal data types
sl@0
    31
 *--------------------------------------------------------------------*/
sl@0
    32
sl@0
    33
/*!
sl@0
    34
 * \internal
sl@0
    35
 * \brief Possible global states for the loader
sl@0
    36
 */
sl@0
    37
typedef enum {
sl@0
    38
    /*! \internal \brief Loading not supported yet */
sl@0
    39
    LOADSTATE_NOT_SUPPORTED = -2,
sl@0
    40
    /*! \internal \brief Loading has terminated with an error */
sl@0
    41
    LOADSTATE_ERROR         = -1,
sl@0
    42
    /*! \internal \brief Loading has not started yet */
sl@0
    43
    LOADSTATE_INITIAL       =  0,
sl@0
    44
    /*! \internal \brief The identifier of the file is being read */
sl@0
    45
    LOADSTATE_IDENTIFIER,
sl@0
    46
    /*! \internal \brief The header of the section is being read */
sl@0
    47
    LOADSTATE_SECTION,
sl@0
    48
    /*! \internal \brief The header field of an object is being read */
sl@0
    49
    LOADSTATE_OBJECT,
sl@0
    50
    /*! \internal \brief Loading is finished */
sl@0
    51
    LOADSTATE_DONE
sl@0
    52
} LoaderState;
sl@0
    53
sl@0
    54
/*!
sl@0
    55
 * \internal
sl@0
    56
 * \brief Possible local states for the loader
sl@0
    57
 */
sl@0
    58
typedef enum {
sl@0
    59
    /*! \internal \brief Local state is entered */
sl@0
    60
    LOADSTATE_ENTER,
sl@0
    61
    /*! \internal \brief Local state is exited */
sl@0
    62
    LOADSTATE_EXIT,
sl@0
    63
    /*! \internal \brief Local state is section checksum */
sl@0
    64
    LOADSTATE_CHECKSUM
sl@0
    65
} LoaderLocalState;
sl@0
    66
sl@0
    67
/*!
sl@0
    68
 * \internal
sl@0
    69
 * \brief Buffered byte stream class
sl@0
    70
 */
sl@0
    71
typedef struct
sl@0
    72
{
sl@0
    73
    M3Gubyte *allocatedData;
sl@0
    74
    M3Gubyte *data;
sl@0
    75
    M3Gsizei capacity, bytesAvailable, totalBytes;
sl@0
    76
} BufferedStream;
sl@0
    77
sl@0
    78
/*!
sl@0
    79
 * \internal
sl@0
    80
 * \brief User data for a loaded object
sl@0
    81
 */
sl@0
    82
typedef struct
sl@0
    83
{
sl@0
    84
    M3GObject object;
sl@0
    85
    M3Gint numParams;
sl@0
    86
    M3Gbyte **params;
sl@0
    87
    M3Gsizei *paramLengths;
sl@0
    88
    M3Gint *paramId;
sl@0
    89
} UserData;
sl@0
    90
sl@0
    91
/*!
sl@0
    92
 * \internal
sl@0
    93
 * \brief Loader instance data
sl@0
    94
 */
sl@0
    95
typedef struct M3GLoaderImpl
sl@0
    96
{
sl@0
    97
    Object object;
sl@0
    98
sl@0
    99
    BufferedStream stream;
sl@0
   100
    M3Gsizei bytesRequired;
sl@0
   101
    M3Gsizei sectionBytesRequired;
sl@0
   102
sl@0
   103
    PointerArray refArray;
sl@0
   104
    PointerArray userDataArray;
sl@0
   105
sl@0
   106
    /*!
sl@0
   107
     * \internal
sl@0
   108
     * \brief The global state the loader is in
sl@0
   109
     *
sl@0
   110
     * This is a rather ordinary state machine thing; basically the
sl@0
   111
     * type of object being loaded, or one of the possible error
sl@0
   112
     * conditions. In here, it also amounts to a particular coroutine
sl@0
   113
     * execution context.
sl@0
   114
     */
sl@0
   115
    LoaderState state;
sl@0
   116
sl@0
   117
    /*!
sl@0
   118
     * \internal
sl@0
   119
     * \brief The local state of the loader
sl@0
   120
     *
sl@0
   121
     * This is basically the line number within a particular coroutine
sl@0
   122
     * function.
sl@0
   123
     */
sl@0
   124
    M3Gint localState;
sl@0
   125
sl@0
   126
    /*!
sl@0
   127
     * \internal
sl@0
   128
     * \brief Object being loaded
sl@0
   129
     */
sl@0
   130
    M3Gint objectType;
sl@0
   131
sl@0
   132
    /*!
sl@0
   133
     * \internal
sl@0
   134
     * \brief Loaded object
sl@0
   135
     */
sl@0
   136
    M3GObject loadedObject;
sl@0
   137
sl@0
   138
    /*!
sl@0
   139
     * \internal
sl@0
   140
     * \brief Pointer to the beginning of an object
sl@0
   141
     */
sl@0
   142
    M3Gubyte *objectData;
sl@0
   143
sl@0
   144
    /*!
sl@0
   145
     * \internal
sl@0
   146
     * \brief Pointer to the end of an object
sl@0
   147
     */
sl@0
   148
    M3Gubyte *objectDataEnd;
sl@0
   149
sl@0
   150
    /*!
sl@0
   151
     * \internal
sl@0
   152
     * \brief Pointer to the context data for the current coroutine
sl@0
   153
     * context
sl@0
   154
     */
sl@0
   155
    M3Gubyte *localData;
sl@0
   156
sl@0
   157
    /*!
sl@0
   158
     * \internal
sl@0
   159
     * \brief Size of the current coroutine data
sl@0
   160
     *
sl@0
   161
     * This is grown dynamically as necessary, rather than trying to
sl@0
   162
     * maintain a single size that fits all coroutines.
sl@0
   163
     */
sl@0
   164
    size_t localDataSize;
sl@0
   165
sl@0
   166
    /* File information */
sl@0
   167
    M3Gbool hasReferences;
sl@0
   168
    M3Gsizei fileSize;
sl@0
   169
    M3Gsizei contentSize;
sl@0
   170
    M3Gint triCount;
sl@0
   171
    M3Gint triConstraint;
sl@0
   172
sl@0
   173
    /* Section information */
sl@0
   174
    M3Gbool compressed;
sl@0
   175
    M3Gint sectionLength;
sl@0
   176
    M3Gint sectionNum;
sl@0
   177
    M3Gint inflatedLength;
sl@0
   178
    M3Gubyte *sectionData;
sl@0
   179
    M3Gubyte *allocatedSectionData;
sl@0
   180
    
sl@0
   181
    /* Adler data */
sl@0
   182
    M3Gint S12[2];
sl@0
   183
} Loader;
sl@0
   184
sl@0
   185
typedef struct {
sl@0
   186
    const unsigned char *data;
sl@0
   187
    int read;
sl@0
   188
    int length;
sl@0
   189
} compressedData;
sl@0
   190
sl@0
   191
/* Type ID used for classifying objects derived from Node */
sl@0
   192
#define ANY_NODE_CLASS ((M3GClass)(-1))
sl@0
   193
sl@0
   194
#include <string.h>
sl@0
   195
#define m3gCmp(s1, s2, len)  memcmp(s1, s2, len)
sl@0
   196
sl@0
   197
/*----------------------------------------------------------------------
sl@0
   198
 * Private constants
sl@0
   199
 *--------------------------------------------------------------------*/
sl@0
   200
sl@0
   201
#define M3G_MIN_OBJECT_SIZE     (1 + 4)
sl@0
   202
#define M3G_MIN_SECTION_SIZE    (1 + 4 + 4)
sl@0
   203
#define M3G_CHECKSUM_SIZE       4
sl@0
   204
sl@0
   205
#define M3G_ADLER_CONST         65521;
sl@0
   206
sl@0
   207
static const M3Gubyte M3G_FILE_IDENTIFIER[] = {
sl@0
   208
    0xAB, 0x4A, 0x53, 0x52, 0x31, 0x38, 0x34, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
sl@0
   209
};
sl@0
   210
sl@0
   211
static const M3Gubyte PNG_FILE_IDENTIFIER[] = {
sl@0
   212
    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a
sl@0
   213
};
sl@0
   214
sl@0
   215
static void m3gCleanupLoader(M3GLoader loader);
sl@0
   216
sl@0
   217
/*----------------------------------------------------------------------
sl@0
   218
 * Platform-specific "inflate" decompression code
sl@0
   219
 *--------------------------------------------------------------------*/
sl@0
   220
sl@0
   221
/*!
sl@0
   222
 * \internal
sl@0
   223
 * \brief Decompresses a block of data into an output buffer
sl@0
   224
 *
sl@0
   225
 * \param srcLength number of bytes in the input (compressed) buffer
sl@0
   226
 * \param src       pointer to the input buffer
sl@0
   227
 * \param dstLength number of bytes allocated in the output buffer
sl@0
   228
 * \param dst       pointer to the output buffer
sl@0
   229
 * \return the number of bytes written to \c dst
sl@0
   230
 */
sl@0
   231
static M3Gsizei m3gInflateBlock(M3Gsizei srcLength, const M3Gubyte *src,
sl@0
   232
                                M3Gsizei dstLength, M3Gubyte *dst);
sl@0
   233
                           
sl@0
   234
/* Include the platform-dependent implementation */
sl@0
   235
#if !defined(M3G_TARGET_GENERIC)
sl@0
   236
#   include "m3g_loader_inflate.inl"
sl@0
   237
#endif
sl@0
   238
sl@0
   239
/*----------------------------------------------------------------------
sl@0
   240
 * Private functions
sl@0
   241
 *--------------------------------------------------------------------*/
sl@0
   242
sl@0
   243
/*!
sl@0
   244
 * \internal
sl@0
   245
 * \brief Destructor
sl@0
   246
 */
sl@0
   247
static void m3gDestroyLoader(Object *obj)
sl@0
   248
{
sl@0
   249
    Loader* loader = (Loader *) obj;
sl@0
   250
    M3G_VALIDATE_OBJECT(loader);
sl@0
   251
    {
sl@0
   252
        Interface *m3g = M3G_INTERFACE(loader);
sl@0
   253
        M3Gint n, i;
sl@0
   254
sl@0
   255
        m3gCleanupLoader(loader);
sl@0
   256
        m3gDestroyArray(&loader->refArray, m3g);
sl@0
   257
        n = m3gArraySize(&loader->userDataArray);
sl@0
   258
        for (i = 0; i < n; ++i)
sl@0
   259
        {
sl@0
   260
            UserData *data = (UserData *)m3gGetArrayElement(&loader->userDataArray, i);
sl@0
   261
            m3gFree(m3g, data->params);
sl@0
   262
            m3gFree(m3g, data->paramLengths);
sl@0
   263
            m3gFree(m3g, data->paramId);
sl@0
   264
            m3gFree(m3g, data);
sl@0
   265
        }
sl@0
   266
        m3gDestroyArray(&loader->userDataArray, m3g);
sl@0
   267
        m3gFree(m3g, loader->stream.allocatedData);
sl@0
   268
        m3gFree(m3g, loader->allocatedSectionData);
sl@0
   269
    }
sl@0
   270
    m3gDestroyObject(obj);
sl@0
   271
}
sl@0
   272
sl@0
   273
/*!
sl@0
   274
 * \internal
sl@0
   275
 * \brief Stores new data in the stream buffer of this loader
sl@0
   276
 */
sl@0
   277
static M3Gbool m3gBufferData( M3GInterface m3g,
sl@0
   278
                           BufferedStream *stream,
sl@0
   279
                           M3Gsizei bytes,
sl@0
   280
                           const M3Gubyte *data)
sl@0
   281
{
sl@0
   282
    M3Gsizei used;
sl@0
   283
sl@0
   284
    /* Allocate initial buffer */
sl@0
   285
    if (stream->allocatedData == NULL) {
sl@0
   286
        stream->capacity = bytes + 512;
sl@0
   287
        stream->allocatedData = m3gAllocZ(m3g, stream->capacity);
sl@0
   288
        if (!stream->allocatedData) {
sl@0
   289
            return M3G_FALSE;
sl@0
   290
        }
sl@0
   291
        stream->data = stream->allocatedData;
sl@0
   292
        stream->bytesAvailable = 0;
sl@0
   293
        stream->totalBytes = 0;
sl@0
   294
    }
sl@0
   295
sl@0
   296
    /* First skip used bytes */
sl@0
   297
    used = stream->data - stream->allocatedData;
sl@0
   298
    if (used > 0) {
sl@0
   299
        m3gMove(stream->allocatedData, stream->data, stream->bytesAvailable);
sl@0
   300
        stream->data = stream->allocatedData;
sl@0
   301
    }
sl@0
   302
sl@0
   303
    /* Check if new data fits in current buffer */
sl@0
   304
    if ((stream->capacity - stream->bytesAvailable) < bytes) {
sl@0
   305
        M3Gubyte *newData;
sl@0
   306
        stream->capacity = stream->capacity + bytes + 512;
sl@0
   307
        newData = m3gAllocZ(m3g, stream->capacity);
sl@0
   308
        if (!newData) {
sl@0
   309
            m3gFree(m3g, stream->allocatedData);
sl@0
   310
            stream->allocatedData = NULL;
sl@0
   311
            return M3G_FALSE;
sl@0
   312
        }
sl@0
   313
        m3gCopy(newData, stream->data, stream->bytesAvailable);
sl@0
   314
        m3gFree(m3g, stream->allocatedData);
sl@0
   315
        stream->allocatedData = newData;
sl@0
   316
        stream->data = stream->allocatedData;
sl@0
   317
    }
sl@0
   318
sl@0
   319
    m3gCopy(stream->data + stream->bytesAvailable, data, bytes);
sl@0
   320
    stream->bytesAvailable += bytes;
sl@0
   321
sl@0
   322
    return M3G_TRUE;
sl@0
   323
}
sl@0
   324
sl@0
   325
/*!
sl@0
   326
 * \internal
sl@0
   327
 * \brief Resets buffered data
sl@0
   328
 */
sl@0
   329
static void m3gResetBufferedData(BufferedStream *stream)
sl@0
   330
{
sl@0
   331
    stream->bytesAvailable = 0;
sl@0
   332
    stream->data = stream->allocatedData;
sl@0
   333
    stream->totalBytes = 0;
sl@0
   334
}
sl@0
   335
sl@0
   336
/*!
sl@0
   337
 * \internal
sl@0
   338
 * \brief Gets buffered data pointer
sl@0
   339
 */
sl@0
   340
static M3Gubyte *m3gGetBufferedDataPtr(BufferedStream *stream, M3Gint length)
sl@0
   341
{
sl@0
   342
    if (stream->bytesAvailable >= length) {
sl@0
   343
        return stream->data;
sl@0
   344
    }
sl@0
   345
    else {
sl@0
   346
        return NULL;
sl@0
   347
    }
sl@0
   348
}
sl@0
   349
sl@0
   350
/*!
sl@0
   351
 * \internal
sl@0
   352
 * \brief Advances buffered data pointer
sl@0
   353
 */
sl@0
   354
static void m3gAdvanceBufferedData(BufferedStream *stream, M3Gint length)
sl@0
   355
{
sl@0
   356
    stream->data += length;
sl@0
   357
    stream->bytesAvailable -= length;
sl@0
   358
    stream->totalBytes += length;
sl@0
   359
}
sl@0
   360
sl@0
   361
/*!
sl@0
   362
 * \internal
sl@0
   363
 * \brief Verify a boolean
sl@0
   364
 */
sl@0
   365
static M3Gbool m3gVerifyBool(M3Gubyte *data)
sl@0
   366
{
sl@0
   367
    return (*data == 0 || *data == 1);
sl@0
   368
}
sl@0
   369
sl@0
   370
/*!
sl@0
   371
 * \internal
sl@0
   372
 * \brief Loads ARGB color from data array
sl@0
   373
 */
sl@0
   374
static M3Guint m3gLoadARGB(M3Gubyte *data)
sl@0
   375
{
sl@0
   376
    M3Guint v = data[3];
sl@0
   377
    v <<= 8;
sl@0
   378
    v |=  data[0];
sl@0
   379
    v <<= 8;
sl@0
   380
    v |=  data[1];
sl@0
   381
    v <<= 8;
sl@0
   382
    v |=  data[2];
sl@0
   383
sl@0
   384
    return v;
sl@0
   385
}
sl@0
   386
sl@0
   387
/*!
sl@0
   388
 * \internal
sl@0
   389
 * \brief Loads RGB color from data array
sl@0
   390
 */
sl@0
   391
static M3Guint m3gLoadRGB(M3Gubyte *data)
sl@0
   392
{
sl@0
   393
    M3Guint v = data[0];
sl@0
   394
    v <<= 8;
sl@0
   395
    v |=  data[1];
sl@0
   396
    v <<= 8;
sl@0
   397
    v |=  data[2];
sl@0
   398
sl@0
   399
    return v;
sl@0
   400
}
sl@0
   401
sl@0
   402
/*!
sl@0
   403
 * \internal
sl@0
   404
 * \brief Loads short from data array
sl@0
   405
 */
sl@0
   406
static M3Gshort m3gLoadShort(M3Gubyte *data)
sl@0
   407
{
sl@0
   408
    M3Gshort v = data[1];
sl@0
   409
    v <<= 8;
sl@0
   410
    v |=  data[0];
sl@0
   411
sl@0
   412
    return v;
sl@0
   413
}
sl@0
   414
sl@0
   415
/*!
sl@0
   416
 * \internal
sl@0
   417
 * \brief Loads integer from data array
sl@0
   418
 */
sl@0
   419
static M3Gint m3gLoadInt(M3Gubyte *data)
sl@0
   420
{
sl@0
   421
    M3Gint v = data[3];
sl@0
   422
    v <<= 8;
sl@0
   423
    v |=  data[2];
sl@0
   424
    v <<= 8;
sl@0
   425
    v |=  data[1];
sl@0
   426
    v <<= 8;
sl@0
   427
    v |=  data[0];
sl@0
   428
sl@0
   429
    return v;
sl@0
   430
}
sl@0
   431
sl@0
   432
/*!
sl@0
   433
 * \internal
sl@0
   434
 * \brief Loads integer from data array
sl@0
   435
 */
sl@0
   436
static M3Gbool m3gLoadFloat(M3Gubyte *data, M3Gfloat *res)
sl@0
   437
{
sl@0
   438
    M3Guint v = data[3];
sl@0
   439
    v <<= 8;
sl@0
   440
    v |=  data[2];
sl@0
   441
    v <<= 8;
sl@0
   442
    v |=  data[1];
sl@0
   443
    v <<= 8;
sl@0
   444
    v |=  data[0];
sl@0
   445
sl@0
   446
    *res = (*(M3Gfloat*)&(v));
sl@0
   447
    if ((v & 0x7f800000) ==  0x7f800000 ||
sl@0
   448
        v == 0x80000000 || // negative zero
sl@0
   449
        ((v & 0x007FFFFF ) != 0 && ( v & 0x7F800000 ) == 0))
sl@0
   450
        return M3G_FALSE;
sl@0
   451
    return M3G_TRUE;
sl@0
   452
}
sl@0
   453
sl@0
   454
/*!
sl@0
   455
 * \internal
sl@0
   456
 * \brief Loads 4 * 4 matrix from data array
sl@0
   457
 */
sl@0
   458
static M3Gbool m3gLoadMatrix(Matrix *m, M3Gubyte *data)
sl@0
   459
{
sl@0
   460
    M3Gint i;
sl@0
   461
    M3Gfloat array[16];
sl@0
   462
sl@0
   463
    for (i = 0; i < 16; i++) {
sl@0
   464
        if (!m3gLoadFloat(data + 4 * i, &array[i]))
sl@0
   465
            return M3G_FALSE;
sl@0
   466
    }
sl@0
   467
sl@0
   468
    m3gSetMatrixRows(m, array);
sl@0
   469
    return M3G_TRUE;
sl@0
   470
}
sl@0
   471
sl@0
   472
/*!
sl@0
   473
 * \internal
sl@0
   474
 * \brief Inflates a section
sl@0
   475
 */
sl@0
   476
static M3Gubyte *m3gInflateSection(Loader *loader,
sl@0
   477
                                   M3Gubyte *compressed,
sl@0
   478
                                   M3Gint cLength, M3Gint iLength)
sl@0
   479
{
sl@0
   480
    M3Gubyte *inflated = m3gAllocZ(M3G_INTERFACE(loader), iLength);
sl@0
   481
    if (inflated && !m3gInflateBlock(cLength, compressed, iLength, inflated)) {
sl@0
   482
        m3gFree(M3G_INTERFACE(loader), inflated);
sl@0
   483
        return NULL;
sl@0
   484
    }
sl@0
   485
sl@0
   486
    return inflated;
sl@0
   487
}
sl@0
   488
sl@0
   489
/*!
sl@0
   490
 * \internal
sl@0
   491
 * \brief Loads file identifier
sl@0
   492
 */
sl@0
   493
static LoaderState m3gLoadIdentifier(Loader *loader)
sl@0
   494
{
sl@0
   495
    M3Gubyte *data = m3gGetBufferedDataPtr(&loader->stream, loader->bytesRequired);
sl@0
   496
sl@0
   497
    if (loader->localState == LOADSTATE_ENTER) {
sl@0
   498
        if (m3gCmp(data, PNG_FILE_IDENTIFIER, sizeof(PNG_FILE_IDENTIFIER)) == 0) {
sl@0
   499
            m3gAdvanceBufferedData(&loader->stream, loader->bytesRequired);
sl@0
   500
            return LOADSTATE_NOT_SUPPORTED;
sl@0
   501
        }
sl@0
   502
        else {
sl@0
   503
            loader->localState = LOADSTATE_EXIT;
sl@0
   504
            loader->bytesRequired = sizeof(M3G_FILE_IDENTIFIER);
sl@0
   505
            return LOADSTATE_IDENTIFIER;
sl@0
   506
        }
sl@0
   507
    }
sl@0
   508
    else {
sl@0
   509
        if (m3gCmp(data, M3G_FILE_IDENTIFIER, sizeof(M3G_FILE_IDENTIFIER)) == 0) {
sl@0
   510
            m3gAdvanceBufferedData(&loader->stream, loader->bytesRequired);
sl@0
   511
            loader->localState = LOADSTATE_ENTER;
sl@0
   512
            loader->bytesRequired = M3G_MIN_SECTION_SIZE;
sl@0
   513
            return LOADSTATE_SECTION;
sl@0
   514
        }
sl@0
   515
sl@0
   516
        loader->bytesRequired = 0;
sl@0
   517
        return LOADSTATE_ERROR;
sl@0
   518
    }
sl@0
   519
}
sl@0
   520
sl@0
   521
/*!
sl@0
   522
 * \internal
sl@0
   523
 * \brief Adler helper functions
sl@0
   524
 */
sl@0
   525
sl@0
   526
static void m3gInitAdler(M3Gint *S12)
sl@0
   527
{
sl@0
   528
    S12[0] = 1; S12[1] = 0;
sl@0
   529
}
sl@0
   530
sl@0
   531
static void m3gUpdateAdler(M3Gint *S12, M3Gubyte *data, M3Gint length)
sl@0
   532
{
sl@0
   533
    int i;
sl@0
   534
    for (i = 0; i < length; i++) {
sl@0
   535
        S12[0] = (S12[0] + data[i]) % M3G_ADLER_CONST;
sl@0
   536
        S12[1] = (S12[1] + S12[0])  % M3G_ADLER_CONST;
sl@0
   537
    }
sl@0
   538
}
sl@0
   539
sl@0
   540
static M3Gint m3gGetAdler(M3Gint *S12)
sl@0
   541
{
sl@0
   542
    return (S12[1] << 16) | S12[0];
sl@0
   543
}
sl@0
   544
sl@0
   545
/*!
sl@0
   546
 * \internal
sl@0
   547
 * \brief Loads a section
sl@0
   548
 */
sl@0
   549
static LoaderState m3gLoadSection(Loader *loader)
sl@0
   550
{
sl@0
   551
    M3Gubyte *data = m3gGetBufferedDataPtr(&loader->stream, loader->bytesRequired);
sl@0
   552
sl@0
   553
    if (data == NULL) {
sl@0
   554
        m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
   555
        return LOADSTATE_ERROR;
sl@0
   556
    }
sl@0
   557
sl@0
   558
    switch(loader->localState) {
sl@0
   559
    case LOADSTATE_ENTER:
sl@0
   560
        m3gAdvanceBufferedData(&loader->stream, loader->bytesRequired);
sl@0
   561
        m3gInitAdler(loader->S12);
sl@0
   562
        m3gUpdateAdler(loader->S12, data, loader->bytesRequired);
sl@0
   563
sl@0
   564
        if (*data > 1)
sl@0
   565
        {
sl@0
   566
            m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
   567
            return LOADSTATE_ERROR;
sl@0
   568
        }
sl@0
   569
        loader->compressed = data[0];
sl@0
   570
        loader->sectionLength = m3gLoadInt(data + 1);
sl@0
   571
        loader->inflatedLength = m3gLoadInt(data + 5);
sl@0
   572
sl@0
   573
        loader->localState = LOADSTATE_EXIT;
sl@0
   574
        loader->bytesRequired = loader->sectionLength - loader->bytesRequired;
sl@0
   575
        if (!loader->compressed && loader->inflatedLength != (loader->bytesRequired - M3G_CHECKSUM_SIZE))
sl@0
   576
        {
sl@0
   577
            m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
   578
            return LOADSTATE_ERROR;
sl@0
   579
        }
sl@0
   580
sl@0
   581
        loader->sectionNum++;
sl@0
   582
sl@0
   583
        /* Special case for empty sections */
sl@0
   584
        if (loader->bytesRequired == M3G_CHECKSUM_SIZE) {
sl@0
   585
            loader->localData = data + loader->sectionLength - M3G_CHECKSUM_SIZE;
sl@0
   586
            loader->sectionData = loader->localData;
sl@0
   587
            loader->compressed = M3G_FALSE;
sl@0
   588
            loader->localState = LOADSTATE_CHECKSUM;
sl@0
   589
        }
sl@0
   590
        return LOADSTATE_SECTION;
sl@0
   591
sl@0
   592
    case LOADSTATE_EXIT:
sl@0
   593
    default:
sl@0
   594
        m3gUpdateAdler(loader->S12, data, loader->bytesRequired - M3G_CHECKSUM_SIZE);
sl@0
   595
sl@0
   596
        if (loader->compressed) {
sl@0
   597
            if (loader->inflatedLength > 0) {
sl@0
   598
                m3gFree(M3G_INTERFACE(loader), loader->allocatedSectionData);
sl@0
   599
                loader->sectionData = m3gInflateSection(loader, data, loader->bytesRequired, loader->inflatedLength);
sl@0
   600
                loader->allocatedSectionData = loader->sectionData;
sl@0
   601
sl@0
   602
                if (!loader->sectionData) {
sl@0
   603
                    if (m3gErrorRaised(M3G_INTERFACE(loader)) == M3G_NO_ERROR)
sl@0
   604
                        m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
   605
                    return LOADSTATE_ERROR;
sl@0
   606
                }
sl@0
   607
            }
sl@0
   608
            else {
sl@0
   609
                loader->sectionData = NULL;
sl@0
   610
            }
sl@0
   611
        }
sl@0
   612
        else {
sl@0
   613
            loader->sectionData = data;
sl@0
   614
        }
sl@0
   615
sl@0
   616
        loader->localData = loader->sectionData;
sl@0
   617
        loader->sectionBytesRequired = M3G_MIN_OBJECT_SIZE;
sl@0
   618
        loader->localState = LOADSTATE_ENTER;
sl@0
   619
        return LOADSTATE_OBJECT;
sl@0
   620
sl@0
   621
    case LOADSTATE_CHECKSUM:
sl@0
   622
        if (loader->localData != loader->sectionData + loader->inflatedLength || /* Length */
sl@0
   623
            m3gLoadInt(data + loader->bytesRequired - M3G_CHECKSUM_SIZE) != m3gGetAdler(loader->S12))
sl@0
   624
        {
sl@0
   625
            m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
   626
            m3gFree(M3G_INTERFACE(loader), loader->allocatedSectionData);
sl@0
   627
            loader->allocatedSectionData = NULL;
sl@0
   628
            return LOADSTATE_ERROR;
sl@0
   629
        }
sl@0
   630
        m3gAdvanceBufferedData(&loader->stream, loader->bytesRequired);
sl@0
   631
sl@0
   632
        m3gFree(M3G_INTERFACE(loader), loader->allocatedSectionData);
sl@0
   633
        loader->allocatedSectionData = NULL;
sl@0
   634
sl@0
   635
        loader->localState = LOADSTATE_ENTER;
sl@0
   636
        loader->bytesRequired = M3G_MIN_SECTION_SIZE;
sl@0
   637
        return LOADSTATE_SECTION;
sl@0
   638
    }
sl@0
   639
}
sl@0
   640
sl@0
   641
/*!
sl@0
   642
 * \internal
sl@0
   643
 * \brief Resets section data pointer to the beginning of an object
sl@0
   644
 */
sl@0
   645
static void m3gRewindObject(Loader *loader)
sl@0
   646
{
sl@0
   647
    loader->localData = loader->objectData;
sl@0
   648
}
sl@0
   649
sl@0
   650
/*!
sl@0
   651
 * \internal
sl@0
   652
 * \brief Resets section data pointer to the end of an object
sl@0
   653
 */
sl@0
   654
static void m3gSkipObject(Loader *loader)
sl@0
   655
{
sl@0
   656
    loader->localData = loader->objectDataEnd;
sl@0
   657
}
sl@0
   658
sl@0
   659
/*!
sl@0
   660
 * \internal
sl@0
   661
 * \brief Marks object to begin
sl@0
   662
 */
sl@0
   663
static void m3gBeginObject(Loader *loader)
sl@0
   664
{
sl@0
   665
    loader->objectData = loader->localData;
sl@0
   666
}
sl@0
   667
sl@0
   668
/*!
sl@0
   669
 * \internal
sl@0
   670
 * \brief Marks object to end
sl@0
   671
 */
sl@0
   672
static void m3gEndObject(Loader *loader)
sl@0
   673
{
sl@0
   674
    loader->objectDataEnd = loader->localData;
sl@0
   675
}
sl@0
   676
sl@0
   677
/*!
sl@0
   678
 * \internal
sl@0
   679
 * \brief Gets section data pointer
sl@0
   680
 */
sl@0
   681
static M3Gubyte *m3gGetSectionDataPtr(Loader *loader, M3Gint length)
sl@0
   682
{
sl@0
   683
    if ((loader->localData + length) <= (loader->sectionData + loader->inflatedLength)) {
sl@0
   684
        return loader->localData;
sl@0
   685
    }
sl@0
   686
    else {
sl@0
   687
        return NULL;
sl@0
   688
    }
sl@0
   689
}
sl@0
   690
sl@0
   691
/*!
sl@0
   692
 * \internal
sl@0
   693
 * \brief Advances section data pointer
sl@0
   694
 */
sl@0
   695
static void m3gAdvanceSectionData(Loader *loader, M3Gint length)
sl@0
   696
{
sl@0
   697
    loader->localData += length;
sl@0
   698
}
sl@0
   699
sl@0
   700
/*!
sl@0
   701
 * \internal
sl@0
   702
 * \brief Check length of the available section data
sl@0
   703
 */
sl@0
   704
static M3Gbool m3gCheckSectionDataLength(Loader *loader, const M3Gubyte *data, M3Gsizei length)
sl@0
   705
{
sl@0
   706
    if (data + length < data) return M3G_FALSE; /* Check for overflow */
sl@0
   707
    return ((data + length) <= (loader->sectionData + loader->inflatedLength));
sl@0
   708
}
sl@0
   709
sl@0
   710
/*!
sl@0
   711
 * \internal
sl@0
   712
 * \brief References an object in the object array
sl@0
   713
 *
sl@0
   714
 * \note Uses lowest bit of the pointer to mark a reference
sl@0
   715
 */
sl@0
   716
static void m3gReferenceLoaded(PointerArray *array, M3Gint idx)
sl@0
   717
{
sl@0
   718
    M3Guint ptr = (M3Guint)m3gGetArrayElement(array, idx);
sl@0
   719
    ptr |= 1;
sl@0
   720
    m3gSetArrayElement(array, idx, (void *)ptr);
sl@0
   721
}
sl@0
   722
sl@0
   723
/*!
sl@0
   724
 * \internal
sl@0
   725
 * \brief Gets an object in the object array and
sl@0
   726
 * returns reference status
sl@0
   727
 */
sl@0
   728
static M3GObject m3gGetLoadedPtr(PointerArray *array, M3Gint idx, M3Gbool *referenced)
sl@0
   729
{
sl@0
   730
    M3Guint ptr = (M3Guint)m3gGetArrayElement(array, idx);
sl@0
   731
    if (referenced != NULL) {
sl@0
   732
        *referenced = ptr & 1;
sl@0
   733
    }
sl@0
   734
    return (M3GObject)(ptr & (~1));
sl@0
   735
}
sl@0
   736
sl@0
   737
/*!
sl@0
   738
 * \internal
sl@0
   739
 * \brief Gets a loaded object and marks it referenced
sl@0
   740
 */
sl@0
   741
static M3GObject m3gGetLoaded(Loader *loader, M3Gint idx, M3GClass classID)
sl@0
   742
{
sl@0
   743
    M3GObject obj;
sl@0
   744
    M3GClass objClassID;
sl@0
   745
    M3Gbool isCompatible;
sl@0
   746
sl@0
   747
    if (idx == 0) return NULL;
sl@0
   748
    idx -= 2;
sl@0
   749
sl@0
   750
    if (idx < 0 || idx >= m3gArraySize(&loader->refArray)) {
sl@0
   751
        /* Error, not loaded */
sl@0
   752
        m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
   753
        return NULL;
sl@0
   754
    }
sl@0
   755
sl@0
   756
    obj = m3gGetLoadedPtr(&loader->refArray, idx, NULL);
sl@0
   757
    objClassID = M3G_CLASS(obj);
sl@0
   758
sl@0
   759
    /* Class type check; handle nodes as a special case */
sl@0
   760
    
sl@0
   761
    if (classID == ANY_NODE_CLASS) {
sl@0
   762
        switch (objClassID) {
sl@0
   763
        case M3G_CLASS_CAMERA:
sl@0
   764
        case M3G_CLASS_GROUP:
sl@0
   765
        case M3G_CLASS_LIGHT:
sl@0
   766
        case M3G_CLASS_MESH:
sl@0
   767
        case M3G_CLASS_MORPHING_MESH:
sl@0
   768
        case M3G_CLASS_SKINNED_MESH:
sl@0
   769
        case M3G_CLASS_SPRITE:
sl@0
   770
        case M3G_CLASS_WORLD:
sl@0
   771
            isCompatible = M3G_TRUE;
sl@0
   772
            break;
sl@0
   773
        default:
sl@0
   774
            isCompatible = M3G_FALSE;
sl@0
   775
        }
sl@0
   776
    }
sl@0
   777
    else {
sl@0
   778
        switch (classID) {
sl@0
   779
        case M3G_ABSTRACT_CLASS:
sl@0
   780
            M3G_ASSERT(M3G_FALSE);
sl@0
   781
            isCompatible = M3G_FALSE;
sl@0
   782
            break;
sl@0
   783
        case M3G_CLASS_MESH:
sl@0
   784
            isCompatible = (objClassID == M3G_CLASS_MESH)
sl@0
   785
                || (objClassID == M3G_CLASS_MORPHING_MESH)
sl@0
   786
                || (objClassID == M3G_CLASS_SKINNED_MESH);
sl@0
   787
            break;
sl@0
   788
        default:
sl@0
   789
            isCompatible = (classID == objClassID);
sl@0
   790
        }
sl@0
   791
    }
sl@0
   792
sl@0
   793
    if (!isCompatible) {
sl@0
   794
        /* Error, class mismatch */
sl@0
   795
        m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
   796
        return NULL;
sl@0
   797
    }
sl@0
   798
sl@0
   799
    /* Mark object as referenced */
sl@0
   800
    m3gReferenceLoaded(&loader->refArray, idx);
sl@0
   801
    return obj;
sl@0
   802
}
sl@0
   803
sl@0
   804
/*!
sl@0
   805
 * \internal
sl@0
   806
 * \brief Loads Object3D data
sl@0
   807
 */
sl@0
   808
static M3Gbool m3gLoadObject3DData(Loader *loader, M3GObject obj)
sl@0
   809
{
sl@0
   810
    M3Guint animTracks, i, userParams, paramId, paramLength;
sl@0
   811
    UserData *userData = NULL;
sl@0
   812
    M3Gubyte *data = m3gGetSectionDataPtr(loader, 8);
sl@0
   813
    if (data == NULL) return M3G_FALSE;
sl@0
   814
sl@0
   815
    m3gSetUserID(obj, m3gLoadInt(data));
sl@0
   816
    data += 4;
sl@0
   817
sl@0
   818
    animTracks = m3gLoadInt(data);
sl@0
   819
    data += 4;
sl@0
   820
sl@0
   821
    /* Overflow? */
sl@0
   822
    if (animTracks >= 0x1fffffff) {
sl@0
   823
        return M3G_FALSE;
sl@0
   824
    }
sl@0
   825
sl@0
   826
    if (m3gCheckSectionDataLength(loader, data, animTracks * 4 + 4) == M3G_FALSE) return M3G_FALSE;
sl@0
   827
    for (i = 0; i < animTracks; i++) {
sl@0
   828
        M3GAnimationTrack at = (M3GAnimationTrack)m3gGetLoaded(loader, m3gLoadInt(data),M3G_CLASS_ANIMATION_TRACK);
sl@0
   829
        if (at == NULL || m3gAddAnimationTrack(obj, at) == -1) {
sl@0
   830
            return M3G_FALSE;
sl@0
   831
        }
sl@0
   832
        data += 4;
sl@0
   833
    }
sl@0
   834
sl@0
   835
    userParams = m3gLoadInt(data);
sl@0
   836
    data += 4;
sl@0
   837
sl@0
   838
    if (userParams != 0) {
sl@0
   839
        /* Overflow? */
sl@0
   840
        if (userParams >= 0x10000000) {
sl@0
   841
            return M3G_FALSE;
sl@0
   842
        }
sl@0
   843
sl@0
   844
        if (m3gCheckSectionDataLength(loader, data, userParams * 8) == M3G_FALSE)
sl@0
   845
            return M3G_FALSE; /* Check the minimum size to avoid useless allocation */
sl@0
   846
        userData = (UserData *)m3gAllocZ(M3G_INTERFACE(loader), sizeof(UserData));
sl@0
   847
        if (userData == NULL)
sl@0
   848
            return M3G_FALSE;
sl@0
   849
        userData->object = obj;
sl@0
   850
        userData->numParams = userParams;
sl@0
   851
        userData->params = (M3Gbyte **)m3gAllocZ(M3G_INTERFACE(loader), userParams*sizeof(M3Gbyte *));
sl@0
   852
        userData->paramLengths = (M3Gsizei *)m3gAlloc(M3G_INTERFACE(loader), userParams*sizeof(M3Gsizei));
sl@0
   853
        userData->paramId = (M3Gint *)m3gAlloc(M3G_INTERFACE(loader), userParams*sizeof(M3Gint));
sl@0
   854
        if (userData->params == NULL ||
sl@0
   855
            userData->paramLengths == NULL ||
sl@0
   856
            userData->paramId == NULL ||
sl@0
   857
            m3gArrayAppend(&loader->userDataArray, userData, M3G_INTERFACE(loader)) == -1) {
sl@0
   858
            m3gFree(M3G_INTERFACE(loader), userData->params);
sl@0
   859
            m3gFree(M3G_INTERFACE(loader), userData->paramLengths);
sl@0
   860
            m3gFree(M3G_INTERFACE(loader), userData->paramId);
sl@0
   861
            m3gFree(M3G_INTERFACE(loader), userData);
sl@0
   862
            return M3G_FALSE;
sl@0
   863
        }
sl@0
   864
sl@0
   865
        for (i = 0; i < userParams; i++) {
sl@0
   866
            if (m3gCheckSectionDataLength(loader, data, 8) == M3G_FALSE) return M3G_FALSE;
sl@0
   867
            paramId = m3gLoadInt(data);
sl@0
   868
            data += 4;
sl@0
   869
            paramLength = m3gLoadInt(data);
sl@0
   870
            data += 4;
sl@0
   871
            userData->paramId[i] = paramId;
sl@0
   872
            userData->paramLengths[i] = paramLength;
sl@0
   873
            if (m3gCheckSectionDataLength(loader, data, paramLength) == M3G_FALSE) return M3G_FALSE;
sl@0
   874
            userData->params[i] = (M3Gbyte *)m3gAlloc(M3G_INTERFACE(loader), paramLength*sizeof(M3Gbyte));
sl@0
   875
            if (userData->params[i] == NULL)
sl@0
   876
                return M3G_FALSE;
sl@0
   877
            m3gCopy(userData->params[i], data, paramLength);
sl@0
   878
            data += paramLength;
sl@0
   879
        }
sl@0
   880
    }
sl@0
   881
sl@0
   882
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
   883
    return M3G_TRUE;
sl@0
   884
}
sl@0
   885
sl@0
   886
/*!
sl@0
   887
 * \internal
sl@0
   888
 * \brief Skips Object3D data
sl@0
   889
 */
sl@0
   890
static M3Gbool m3gSkipObject3DData(Loader *loader)
sl@0
   891
{
sl@0
   892
    M3Guint animTracks, i, userParams, paramLength;
sl@0
   893
    M3Gubyte *data = m3gGetSectionDataPtr(loader, 8);
sl@0
   894
    if (data == NULL) return M3G_FALSE;
sl@0
   895
sl@0
   896
    data += 4;
sl@0
   897
    animTracks = m3gLoadInt(data);
sl@0
   898
    data += 4;
sl@0
   899
sl@0
   900
    /* Overflow? */
sl@0
   901
    if (animTracks >= 0x1fffffff) {
sl@0
   902
        return M3G_FALSE;
sl@0
   903
    }
sl@0
   904
sl@0
   905
    if (m3gCheckSectionDataLength(loader, data, animTracks * 4 + 4) == M3G_FALSE) return M3G_FALSE;
sl@0
   906
    for (i = 0; i < animTracks; i++) {
sl@0
   907
        data += 4;
sl@0
   908
    }
sl@0
   909
sl@0
   910
    userParams = m3gLoadInt(data);
sl@0
   911
    data += 4;
sl@0
   912
sl@0
   913
    for (i = 0; i < userParams; i++) {
sl@0
   914
        if (m3gCheckSectionDataLength(loader, data, 8) == M3G_FALSE) return M3G_FALSE;
sl@0
   915
        data += 4;
sl@0
   916
        paramLength = m3gLoadInt(data);
sl@0
   917
        if (m3gCheckSectionDataLength(loader, data, paramLength) == M3G_FALSE) return M3G_FALSE;
sl@0
   918
        data += 4 + paramLength;
sl@0
   919
    }
sl@0
   920
sl@0
   921
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
   922
    return M3G_TRUE;
sl@0
   923
}
sl@0
   924
sl@0
   925
/*!
sl@0
   926
 * \internal
sl@0
   927
 * \brief Loads transformable data
sl@0
   928
 */
sl@0
   929
static M3Gbool m3gLoadTransformableData(Loader *loader, M3GTransformable obj)
sl@0
   930
{
sl@0
   931
    M3Gfloat f1, f2, f3, f4;
sl@0
   932
    M3Gubyte *data;
sl@0
   933
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
   934
        return M3G_FALSE;
sl@0
   935
    }
sl@0
   936
sl@0
   937
    data = m3gGetSectionDataPtr(loader, 1);
sl@0
   938
    if (data == NULL) return M3G_FALSE;
sl@0
   939
sl@0
   940
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
   941
    /* Component transform ? */
sl@0
   942
    if (*data++) {
sl@0
   943
        if (m3gCheckSectionDataLength(loader, data, 40) == M3G_FALSE) return M3G_FALSE;
sl@0
   944
        if (!m3gLoadFloat(data + 0, &f1) ||
sl@0
   945
            !m3gLoadFloat(data + 4, &f2) ||
sl@0
   946
            !m3gLoadFloat(data + 8, &f3))
sl@0
   947
            return M3G_FALSE;
sl@0
   948
        m3gSetTranslation(obj, f1, f2, f3);
sl@0
   949
        if (!m3gLoadFloat(data + 12, &f1) ||
sl@0
   950
            !m3gLoadFloat(data + 16, &f2) ||
sl@0
   951
            !m3gLoadFloat(data + 20, &f3))
sl@0
   952
            return M3G_FALSE;
sl@0
   953
        m3gSetScale(obj, f1, f2, f3);
sl@0
   954
        if (!m3gLoadFloat(data + 24, &f1) ||
sl@0
   955
            !m3gLoadFloat(data + 28, &f2) ||
sl@0
   956
            !m3gLoadFloat(data + 32, &f3) ||
sl@0
   957
            !m3gLoadFloat(data + 36, &f4))
sl@0
   958
            return M3G_FALSE;
sl@0
   959
        m3gSetOrientation(obj, f1, f2, f3, f4);
sl@0
   960
        data += 40;
sl@0
   961
    }
sl@0
   962
sl@0
   963
    if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) return M3G_FALSE;
sl@0
   964
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
   965
    /* Generic transform */
sl@0
   966
    if (*data++) {
sl@0
   967
        Matrix m;
sl@0
   968
        if (m3gCheckSectionDataLength(loader, data, 64) == M3G_FALSE) return M3G_FALSE;
sl@0
   969
        if (!m3gLoadMatrix(&m, data)) return M3G_FALSE;
sl@0
   970
        m3gSetTransform(obj, &m);
sl@0
   971
        data += 64;
sl@0
   972
    }
sl@0
   973
sl@0
   974
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
   975
    return M3G_TRUE;
sl@0
   976
}
sl@0
   977
sl@0
   978
/*!
sl@0
   979
 * \internal
sl@0
   980
 * \brief Skips transformable data
sl@0
   981
 */
sl@0
   982
static M3Gbool m3gSkipTransformableData(Loader *loader)
sl@0
   983
{
sl@0
   984
    M3Gubyte *data;
sl@0
   985
    if (!m3gSkipObject3DData(loader)) return M3G_FALSE;
sl@0
   986
sl@0
   987
    data = m3gGetSectionDataPtr(loader, 1);
sl@0
   988
    if (data == NULL) return M3G_FALSE;
sl@0
   989
sl@0
   990
    /* Component transform ? */
sl@0
   991
    if (*data++) {
sl@0
   992
        if (m3gCheckSectionDataLength(loader, data, 40) == M3G_FALSE) return M3G_FALSE;
sl@0
   993
        data += 40;
sl@0
   994
    }
sl@0
   995
sl@0
   996
    if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) return M3G_FALSE;
sl@0
   997
    /* Generic transform */
sl@0
   998
    if (*data++) {
sl@0
   999
        if (m3gCheckSectionDataLength(loader, data, 64) == M3G_FALSE) return M3G_FALSE;
sl@0
  1000
        data += 64;
sl@0
  1001
    }
sl@0
  1002
sl@0
  1003
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1004
    return M3G_TRUE;
sl@0
  1005
}
sl@0
  1006
sl@0
  1007
/*!
sl@0
  1008
 * \internal
sl@0
  1009
 * \brief Loads node data
sl@0
  1010
 */
sl@0
  1011
static M3Gbool m3gLoadNodeData(Loader *loader, M3GNode obj)
sl@0
  1012
{
sl@0
  1013
    M3Gubyte *data;
sl@0
  1014
    if (!m3gLoadTransformableData(loader, (M3GTransformable)obj)) {
sl@0
  1015
        return M3G_FALSE;
sl@0
  1016
    }
sl@0
  1017
sl@0
  1018
    data = m3gGetSectionDataPtr(loader, 8);
sl@0
  1019
    if (data == NULL) return M3G_FALSE;
sl@0
  1020
sl@0
  1021
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1022
    m3gEnable(obj, 0, *data++);
sl@0
  1023
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1024
    m3gEnable(obj, 1, *data++);
sl@0
  1025
    {
sl@0
  1026
        unsigned a = *data++; 
sl@0
  1027
        m3gSetAlphaFactor(obj, m3gDivif(a, 255));
sl@0
  1028
    }
sl@0
  1029
    m3gSetScope(obj, m3gLoadInt(data));
sl@0
  1030
    data += 4;
sl@0
  1031
sl@0
  1032
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1033
    if (*data++) {
sl@0
  1034
        M3Gubyte zt, yt;
sl@0
  1035
        M3Gint zr, yr;
sl@0
  1036
        if (m3gCheckSectionDataLength(loader, data, 10) == M3G_FALSE) return M3G_FALSE;
sl@0
  1037
        zt = *data++;
sl@0
  1038
        yt = *data++;
sl@0
  1039
        zr = m3gLoadInt(data);
sl@0
  1040
        yr = m3gLoadInt(data + 4);
sl@0
  1041
        m3gSetAlignment(obj,    (M3GNode)m3gGetLoaded(loader, zr, ANY_NODE_CLASS),
sl@0
  1042
                                zt,
sl@0
  1043
                                (M3GNode)m3gGetLoaded(loader, yr, ANY_NODE_CLASS),
sl@0
  1044
                                yt);
sl@0
  1045
        data += 8;
sl@0
  1046
    }
sl@0
  1047
sl@0
  1048
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1049
    return M3G_TRUE;
sl@0
  1050
}
sl@0
  1051
sl@0
  1052
/*!
sl@0
  1053
 * \internal
sl@0
  1054
 * \brief Skips node data
sl@0
  1055
 */
sl@0
  1056
static M3Gbool m3gSkipNodeData(Loader *loader)
sl@0
  1057
{
sl@0
  1058
    M3Gubyte *data;
sl@0
  1059
    if (!m3gSkipTransformableData(loader)) return M3G_FALSE;
sl@0
  1060
sl@0
  1061
    data = m3gGetSectionDataPtr(loader, 8);
sl@0
  1062
    if (data == NULL) return M3G_FALSE;
sl@0
  1063
    data += 7;
sl@0
  1064
sl@0
  1065
    /* Alignment? */
sl@0
  1066
    if (*data++) {
sl@0
  1067
        if (m3gCheckSectionDataLength(loader, data, 10) == M3G_FALSE) return M3G_FALSE;
sl@0
  1068
        data += 10;
sl@0
  1069
    }
sl@0
  1070
sl@0
  1071
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1072
    return M3G_TRUE;
sl@0
  1073
}
sl@0
  1074
sl@0
  1075
/*!
sl@0
  1076
 * \internal
sl@0
  1077
 * \brief Loads a camera
sl@0
  1078
 */
sl@0
  1079
static M3Gbool m3gLoadCamera(Loader *loader)
sl@0
  1080
{
sl@0
  1081
    M3Gfloat f1, f2, f3, f4;
sl@0
  1082
    M3Gubyte *data;
sl@0
  1083
    M3GCamera obj = m3gCreateCamera(M3G_INTERFACE(loader));
sl@0
  1084
    loader->loadedObject = (M3GObject)obj;
sl@0
  1085
    if (!obj) return M3G_FALSE;
sl@0
  1086
sl@0
  1087
    if (!m3gLoadNodeData(loader, (M3GNode)obj)) {
sl@0
  1088
        return M3G_FALSE;
sl@0
  1089
    }
sl@0
  1090
sl@0
  1091
    data = m3gGetSectionDataPtr(loader, 1);
sl@0
  1092
    if (data == NULL) return M3G_FALSE;
sl@0
  1093
sl@0
  1094
    switch(*data++) {
sl@0
  1095
    case M3G_GENERIC:
sl@0
  1096
        {
sl@0
  1097
            Matrix m;
sl@0
  1098
            if (m3gCheckSectionDataLength(loader, data, 64) == M3G_FALSE) return M3G_FALSE;
sl@0
  1099
            if (!m3gLoadMatrix(&m, data)) return M3G_FALSE;
sl@0
  1100
            m3gSetProjectionMatrix(obj, &m);
sl@0
  1101
            data += 64;
sl@0
  1102
        }
sl@0
  1103
        break;
sl@0
  1104
    case M3G_PERSPECTIVE:
sl@0
  1105
        if (m3gCheckSectionDataLength(loader, data, 16) == M3G_FALSE) return M3G_FALSE;
sl@0
  1106
        if (!m3gLoadFloat(data + 0, &f1) ||
sl@0
  1107
            !m3gLoadFloat(data + 4, &f2) ||
sl@0
  1108
            !m3gLoadFloat(data + 8, &f3) ||
sl@0
  1109
            !m3gLoadFloat(data + 12, &f4))
sl@0
  1110
            return M3G_FALSE;
sl@0
  1111
        m3gSetPerspective(obj, f1, f2, f3, f4);
sl@0
  1112
        data += 16;
sl@0
  1113
        break;
sl@0
  1114
    case M3G_PARALLEL:
sl@0
  1115
        if (m3gCheckSectionDataLength(loader, data, 16) == M3G_FALSE) return M3G_FALSE;
sl@0
  1116
        if (!m3gLoadFloat(data + 0, &f1) ||
sl@0
  1117
            !m3gLoadFloat(data + 4, &f2) ||
sl@0
  1118
            !m3gLoadFloat(data + 8, &f3) ||
sl@0
  1119
            !m3gLoadFloat(data + 12, &f4))
sl@0
  1120
            return M3G_FALSE;
sl@0
  1121
        m3gSetParallel(obj, f1, f2, f3, f4);
sl@0
  1122
        data += 16;
sl@0
  1123
        break;
sl@0
  1124
    default:
sl@0
  1125
        /* Error */
sl@0
  1126
        break;
sl@0
  1127
    }
sl@0
  1128
sl@0
  1129
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1130
sl@0
  1131
    return M3G_TRUE;
sl@0
  1132
}
sl@0
  1133
sl@0
  1134
/*!
sl@0
  1135
 * \internal
sl@0
  1136
 * \brief Loads a background
sl@0
  1137
 */
sl@0
  1138
static M3Gbool m3gLoadBackground(Loader *loader)
sl@0
  1139
{
sl@0
  1140
    M3Gubyte *data;
sl@0
  1141
    M3GBackground obj = m3gCreateBackground(M3G_INTERFACE(loader));
sl@0
  1142
    loader->loadedObject = (M3GObject)obj;
sl@0
  1143
    if (!obj) return M3G_FALSE;
sl@0
  1144
sl@0
  1145
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1146
        return M3G_FALSE;
sl@0
  1147
    }
sl@0
  1148
sl@0
  1149
    data = m3gGetSectionDataPtr(loader, 28);
sl@0
  1150
    if (data == NULL) return M3G_FALSE;
sl@0
  1151
sl@0
  1152
    m3gSetBgColor(obj, m3gLoadARGB(data));
sl@0
  1153
    data += 4;
sl@0
  1154
    m3gSetBgImage(obj, (M3GImage)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_IMAGE));
sl@0
  1155
    data += 4;
sl@0
  1156
    m3gSetBgMode(obj, data[0], data[1]);
sl@0
  1157
    data += 2;
sl@0
  1158
    m3gSetBgCrop(obj,   m3gLoadInt(data),
sl@0
  1159
                        m3gLoadInt(data + 4),
sl@0
  1160
                        m3gLoadInt(data + 8),
sl@0
  1161
                        m3gLoadInt(data + 12) );
sl@0
  1162
    data += 16;
sl@0
  1163
sl@0
  1164
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1165
    m3gSetBgEnable(obj, 0, *data++);
sl@0
  1166
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1167
    m3gSetBgEnable(obj, 1, *data++);
sl@0
  1168
sl@0
  1169
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1170
sl@0
  1171
    return M3G_TRUE;
sl@0
  1172
}
sl@0
  1173
sl@0
  1174
/*!
sl@0
  1175
 * \internal
sl@0
  1176
 * \brief Loads a vertex array
sl@0
  1177
 */
sl@0
  1178
static M3Gbool m3gLoadVertexArray(Loader *loader)
sl@0
  1179
{
sl@0
  1180
    M3Gint i, j;
sl@0
  1181
    M3Guint size;
sl@0
  1182
    M3Gushort vertices;
sl@0
  1183
    M3Gubyte *data, components, encoding;
sl@0
  1184
    M3Gdatatype componentSize;
sl@0
  1185
    M3GVertexArray obj;
sl@0
  1186
sl@0
  1187
    m3gBeginObject(loader);
sl@0
  1188
    if (!m3gSkipObject3DData(loader)) return M3G_FALSE;
sl@0
  1189
sl@0
  1190
    data = m3gGetSectionDataPtr(loader, 5);
sl@0
  1191
    if (data == NULL) return M3G_FALSE;
sl@0
  1192
sl@0
  1193
    if (*data != 1 && *data != 2) return M3G_FALSE;
sl@0
  1194
    componentSize = (*data++ == 1) ? M3G_BYTE : M3G_SHORT;
sl@0
  1195
    components    = *data++;
sl@0
  1196
    encoding      = *data++;
sl@0
  1197
    vertices      = m3gLoadShort(data);
sl@0
  1198
    data += 2;
sl@0
  1199
sl@0
  1200
    size = vertices * components * (componentSize == M3G_BYTE ? 1 : 2);
sl@0
  1201
sl@0
  1202
    /* Overflow? */
sl@0
  1203
    if (size < vertices) {
sl@0
  1204
        return M3G_FALSE;
sl@0
  1205
    }
sl@0
  1206
sl@0
  1207
    if (m3gCheckSectionDataLength(loader, data, size) == M3G_FALSE) return M3G_FALSE;
sl@0
  1208
    obj = m3gCreateVertexArray(M3G_INTERFACE(loader), vertices, components, componentSize);
sl@0
  1209
    loader->loadedObject = (M3GObject)obj;
sl@0
  1210
    if (!obj) return M3G_FALSE;
sl@0
  1211
sl@0
  1212
    if (componentSize == M3G_BYTE) {
sl@0
  1213
        M3Gbyte previousValues[4];
sl@0
  1214
        m3gZero(previousValues, sizeof(previousValues));
sl@0
  1215
sl@0
  1216
        for (i = 0; i < vertices; i++) {
sl@0
  1217
            for (j = 0; j < components; j++) {
sl@0
  1218
                if (encoding == 0) {
sl@0
  1219
                    previousValues[j] = *data++;
sl@0
  1220
                }
sl@0
  1221
                else {
sl@0
  1222
                    previousValues[j] = (M3Gbyte) (previousValues[j] + *data++);
sl@0
  1223
                }
sl@0
  1224
            }
sl@0
  1225
            m3gSetVertexArrayElements(obj, i, 1, sizeof(previousValues), componentSize, previousValues);
sl@0
  1226
        }
sl@0
  1227
    }
sl@0
  1228
    else {
sl@0
  1229
        M3Gshort previousValues[4];
sl@0
  1230
        m3gZero(previousValues, sizeof(previousValues));
sl@0
  1231
sl@0
  1232
        for (i = 0; i < vertices; i++) {
sl@0
  1233
            for (j = 0; j < components; j++) {
sl@0
  1234
                if (encoding == 0) {
sl@0
  1235
                    previousValues[j] = m3gLoadShort(data);
sl@0
  1236
                }
sl@0
  1237
                else {
sl@0
  1238
                    previousValues[j] = (M3Gshort) (previousValues[j] + m3gLoadShort(data));
sl@0
  1239
                }
sl@0
  1240
                data += 2;
sl@0
  1241
            }
sl@0
  1242
            m3gSetVertexArrayElements(obj, i, 1, sizeof(previousValues), componentSize, previousValues);
sl@0
  1243
        }
sl@0
  1244
    }
sl@0
  1245
sl@0
  1246
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1247
sl@0
  1248
    m3gEndObject(loader);
sl@0
  1249
    m3gRewindObject(loader);
sl@0
  1250
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1251
        return M3G_FALSE;
sl@0
  1252
    }
sl@0
  1253
    m3gSkipObject(loader);
sl@0
  1254
sl@0
  1255
    return M3G_TRUE;
sl@0
  1256
}
sl@0
  1257
sl@0
  1258
/*!
sl@0
  1259
 * \internal
sl@0
  1260
 * \brief Loads a vertex buffer
sl@0
  1261
 */
sl@0
  1262
static M3Gbool m3gLoadVertexBuffer(Loader *loader)
sl@0
  1263
{
sl@0
  1264
    M3Gubyte *data;
sl@0
  1265
    M3GVertexArray va;
sl@0
  1266
    M3Gfloat bias[3], scale;
sl@0
  1267
    M3Guint i, taCount;
sl@0
  1268
    M3GVertexBuffer obj = m3gCreateVertexBuffer(M3G_INTERFACE(loader));
sl@0
  1269
    loader->loadedObject = (M3GObject)obj;
sl@0
  1270
    if (!obj) return M3G_FALSE;
sl@0
  1271
sl@0
  1272
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1273
        return M3G_FALSE;
sl@0
  1274
    }
sl@0
  1275
sl@0
  1276
    data = m3gGetSectionDataPtr(loader, 36);
sl@0
  1277
    if (data == NULL) return M3G_FALSE;
sl@0
  1278
sl@0
  1279
    m3gSetVertexDefaultColor(obj, m3gLoadARGB(data));
sl@0
  1280
    data += 4;
sl@0
  1281
sl@0
  1282
    /* Positions */
sl@0
  1283
    va = (M3GVertexArray)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_ARRAY);
sl@0
  1284
    data += 4;
sl@0
  1285
    if (!m3gLoadFloat(data + 0, &bias[0])) return M3G_FALSE;
sl@0
  1286
    if (!m3gLoadFloat(data + 4, &bias[1])) return M3G_FALSE;
sl@0
  1287
    if (!m3gLoadFloat(data + 8, &bias[2])) return M3G_FALSE;
sl@0
  1288
    if (!m3gLoadFloat(data + 12, &scale)) return M3G_FALSE;
sl@0
  1289
    if (va != NULL) {
sl@0
  1290
        m3gSetVertexArray(obj, va, scale, bias, 3);
sl@0
  1291
    }
sl@0
  1292
    data += 16;
sl@0
  1293
sl@0
  1294
    /* Normals */
sl@0
  1295
    va = (M3GVertexArray)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_ARRAY);
sl@0
  1296
    data += 4;
sl@0
  1297
    if (va != NULL) {
sl@0
  1298
        m3gSetNormalArray(obj, va);
sl@0
  1299
    }
sl@0
  1300
sl@0
  1301
    /* Colors */
sl@0
  1302
    va = (M3GVertexArray)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_ARRAY);
sl@0
  1303
    data += 4;
sl@0
  1304
    if (va != NULL) {
sl@0
  1305
        m3gSetColorArray(obj, va);
sl@0
  1306
    }
sl@0
  1307
sl@0
  1308
    /* Texture coordinates */
sl@0
  1309
    taCount = m3gLoadInt(data);
sl@0
  1310
    data += 4;
sl@0
  1311
sl@0
  1312
    /* Overflow? */
sl@0
  1313
    if (taCount >= 0x0ccccccc) {
sl@0
  1314
        return M3G_FALSE;
sl@0
  1315
    }
sl@0
  1316
sl@0
  1317
    if (m3gCheckSectionDataLength(loader, data, taCount * 20) == M3G_FALSE) return M3G_FALSE;
sl@0
  1318
    for (i = 0; i < taCount; i++) {
sl@0
  1319
        va = (M3GVertexArray)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_ARRAY);
sl@0
  1320
        data += 4;
sl@0
  1321
        if (!m3gLoadFloat(data + 0, &bias[0])) return M3G_FALSE;
sl@0
  1322
        if (!m3gLoadFloat(data + 4, &bias[1])) return M3G_FALSE;
sl@0
  1323
        if (!m3gLoadFloat(data + 8, &bias[2])) return M3G_FALSE;
sl@0
  1324
        if (!m3gLoadFloat(data + 12, &scale)) return M3G_FALSE;
sl@0
  1325
        if (va != NULL) {
sl@0
  1326
            m3gSetTexCoordArray(obj, i, va, scale, bias, 3);
sl@0
  1327
        }
sl@0
  1328
        else {
sl@0
  1329
            return M3G_FALSE;
sl@0
  1330
        }
sl@0
  1331
        data += 16;
sl@0
  1332
    }
sl@0
  1333
sl@0
  1334
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1335
sl@0
  1336
    return M3G_TRUE;
sl@0
  1337
}
sl@0
  1338
sl@0
  1339
sl@0
  1340
/*!
sl@0
  1341
 * \internal
sl@0
  1342
 * \brief Loads a triangle strip array
sl@0
  1343
 */
sl@0
  1344
static M3Gbool m3gLoadTsa(Loader *loader)
sl@0
  1345
{
sl@0
  1346
    M3Gubyte *data;
sl@0
  1347
    M3GIndexBuffer obj = 0;
sl@0
  1348
    M3Gubyte encoding;
sl@0
  1349
    M3Guint startIndex = 0, indicesCount = 0, *indices = NULL;
sl@0
  1350
    M3Guint lengthCount;
sl@0
  1351
    M3Gint *lengths = NULL;
sl@0
  1352
    M3Guint i;
sl@0
  1353
sl@0
  1354
    m3gBeginObject(loader);
sl@0
  1355
    if (!m3gSkipObject3DData(loader)) return M3G_FALSE;
sl@0
  1356
sl@0
  1357
    data = m3gGetSectionDataPtr(loader, 1);
sl@0
  1358
    if (data == NULL) return M3G_FALSE;
sl@0
  1359
sl@0
  1360
    encoding = *data++;
sl@0
  1361
sl@0
  1362
    switch(encoding) {
sl@0
  1363
    case 0:
sl@0
  1364
        if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  1365
        startIndex = m3gLoadInt(data);
sl@0
  1366
        data += 4;
sl@0
  1367
        break;
sl@0
  1368
sl@0
  1369
    case 1:
sl@0
  1370
        if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) return M3G_FALSE;
sl@0
  1371
        startIndex = *data++;
sl@0
  1372
        break;
sl@0
  1373
sl@0
  1374
    case 2:
sl@0
  1375
        if (m3gCheckSectionDataLength(loader, data, 2) == M3G_FALSE) return M3G_FALSE;
sl@0
  1376
        startIndex = (M3Gushort) m3gLoadShort(data);
sl@0
  1377
        data += 2;
sl@0
  1378
        break;
sl@0
  1379
sl@0
  1380
    case 128:
sl@0
  1381
        if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  1382
        indicesCount = m3gLoadInt(data);
sl@0
  1383
        data += 4;
sl@0
  1384
sl@0
  1385
        /* Overflow? */
sl@0
  1386
        if (indicesCount >= 0x20000000) {
sl@0
  1387
            return M3G_FALSE;
sl@0
  1388
        }
sl@0
  1389
sl@0
  1390
        if (m3gCheckSectionDataLength(loader, data, indicesCount * 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  1391
        indices = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gint) * indicesCount);
sl@0
  1392
        if (!indices) return M3G_FALSE;
sl@0
  1393
        for (i = 0; i < indicesCount; i++ ) {
sl@0
  1394
            indices[i] = m3gLoadInt(data);
sl@0
  1395
            data += 4;
sl@0
  1396
        }
sl@0
  1397
        break;
sl@0
  1398
sl@0
  1399
    case 129:
sl@0
  1400
        if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  1401
        indicesCount = m3gLoadInt(data);
sl@0
  1402
        data += 4;
sl@0
  1403
        if (m3gCheckSectionDataLength(loader, data, indicesCount) == M3G_FALSE) return M3G_FALSE;
sl@0
  1404
        indices = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gint) * indicesCount);
sl@0
  1405
        if (!indices) return M3G_FALSE;
sl@0
  1406
        for (i = 0; i < indicesCount; i++ ) {
sl@0
  1407
            indices[i] = *data++;
sl@0
  1408
        }
sl@0
  1409
        break;
sl@0
  1410
sl@0
  1411
    case 130:
sl@0
  1412
        if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  1413
        indicesCount = m3gLoadInt(data);
sl@0
  1414
        data += 4;
sl@0
  1415
sl@0
  1416
        /* Overflow? */
sl@0
  1417
        if (indicesCount >= 0x40000000) {
sl@0
  1418
            return M3G_FALSE;
sl@0
  1419
        }
sl@0
  1420
sl@0
  1421
        if (m3gCheckSectionDataLength(loader, data, indicesCount * 2) == M3G_FALSE) return M3G_FALSE;
sl@0
  1422
        indices = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gint) * indicesCount);
sl@0
  1423
        if (!indices) return M3G_FALSE;
sl@0
  1424
        for (i = 0; i < indicesCount; i++) {
sl@0
  1425
            indices[i] = (M3Gushort)m3gLoadShort(data);
sl@0
  1426
            data += 2;
sl@0
  1427
        }
sl@0
  1428
        break;
sl@0
  1429
sl@0
  1430
    default:
sl@0
  1431
        return M3G_FALSE;
sl@0
  1432
    }
sl@0
  1433
sl@0
  1434
    if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) goto cleanup;
sl@0
  1435
    lengthCount = m3gLoadInt(data);
sl@0
  1436
    data += 4;
sl@0
  1437
sl@0
  1438
    /* Overflow? */
sl@0
  1439
    if (lengthCount >= 0x20000000) {
sl@0
  1440
        goto cleanup;
sl@0
  1441
    }
sl@0
  1442
sl@0
  1443
    if (m3gCheckSectionDataLength(loader, data, lengthCount * 4) == M3G_FALSE) goto cleanup;
sl@0
  1444
    lengths = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gint) * lengthCount);
sl@0
  1445
    if (!lengths) goto cleanup;
sl@0
  1446
sl@0
  1447
    for (i = 0; i < lengthCount; i++) {
sl@0
  1448
        lengths[i] = m3gLoadInt(data);
sl@0
  1449
        data += 4;
sl@0
  1450
    }
sl@0
  1451
sl@0
  1452
    if (encoding == 0 || encoding == 1 || encoding == 2) {
sl@0
  1453
        obj = m3gCreateImplicitStripBuffer( M3G_INTERFACE(loader),
sl@0
  1454
                                            lengthCount,
sl@0
  1455
                                            lengths,
sl@0
  1456
                                            startIndex);
sl@0
  1457
    }
sl@0
  1458
    else {
sl@0
  1459
        obj = m3gCreateStripBuffer( M3G_INTERFACE(loader),
sl@0
  1460
                                    M3G_TRIANGLE_STRIPS,
sl@0
  1461
                                    lengthCount,
sl@0
  1462
                                    lengths,
sl@0
  1463
                                    M3G_INT,
sl@0
  1464
                                    indicesCount,
sl@0
  1465
                                    indices);
sl@0
  1466
    }
sl@0
  1467
sl@0
  1468
cleanup:
sl@0
  1469
    m3gFree(M3G_INTERFACE(loader), indices);
sl@0
  1470
    m3gFree(M3G_INTERFACE(loader), lengths);
sl@0
  1471
sl@0
  1472
    loader->loadedObject = (M3GObject)obj;
sl@0
  1473
    if (!obj) return M3G_FALSE;
sl@0
  1474
sl@0
  1475
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1476
sl@0
  1477
    m3gEndObject(loader);
sl@0
  1478
    m3gRewindObject(loader);
sl@0
  1479
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1480
        return M3G_FALSE;
sl@0
  1481
    }
sl@0
  1482
    m3gSkipObject(loader);
sl@0
  1483
sl@0
  1484
    return M3G_TRUE;
sl@0
  1485
}
sl@0
  1486
sl@0
  1487
/*!
sl@0
  1488
 * \internal
sl@0
  1489
 * \brief Loads a compositing mode
sl@0
  1490
 */
sl@0
  1491
static M3Gbool m3gLoadCompositingMode(Loader *loader)
sl@0
  1492
{
sl@0
  1493
    M3Gfloat f1, f2;
sl@0
  1494
    M3Gubyte *data;
sl@0
  1495
    M3GCompositingMode obj = m3gCreateCompositingMode(M3G_INTERFACE(loader));
sl@0
  1496
    loader->loadedObject = (M3GObject)obj;
sl@0
  1497
    if (!obj) return M3G_FALSE;
sl@0
  1498
sl@0
  1499
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1500
        return M3G_FALSE;
sl@0
  1501
    }
sl@0
  1502
sl@0
  1503
    data = m3gGetSectionDataPtr(loader, 14);
sl@0
  1504
    if (data == NULL) return M3G_FALSE;
sl@0
  1505
sl@0
  1506
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1507
    m3gEnableDepthTest      (obj, *data++);
sl@0
  1508
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1509
    m3gEnableDepthWrite     (obj, *data++);
sl@0
  1510
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1511
    m3gEnableColorWrite     (obj, *data++);
sl@0
  1512
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1513
    m3gSetAlphaWriteEnable  (obj, *data++);
sl@0
  1514
    m3gSetBlending          (obj, *data++);
sl@0
  1515
    {
sl@0
  1516
        unsigned a = *data++; 
sl@0
  1517
        m3gSetAlphaThreshold(obj, m3gDivif(a, 255));
sl@0
  1518
    }
sl@0
  1519
    if (!m3gLoadFloat(data, &f1) || !m3gLoadFloat(data + 4, &f2)) return M3G_FALSE;
sl@0
  1520
    m3gSetDepthOffset       (obj, f1, f2);
sl@0
  1521
    data += 8;
sl@0
  1522
sl@0
  1523
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1524
sl@0
  1525
    return M3G_TRUE;
sl@0
  1526
}
sl@0
  1527
sl@0
  1528
/*!
sl@0
  1529
 * \internal
sl@0
  1530
 * \brief Loads a polygon mode
sl@0
  1531
 */
sl@0
  1532
static M3Gbool m3gLoadPolygonMode(Loader *loader)
sl@0
  1533
{
sl@0
  1534
    M3Gubyte *data;
sl@0
  1535
    M3GPolygonMode obj = m3gCreatePolygonMode(M3G_INTERFACE(loader));
sl@0
  1536
    loader->loadedObject = (M3GObject)obj;
sl@0
  1537
    if (!obj) return M3G_FALSE;
sl@0
  1538
sl@0
  1539
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1540
        return M3G_FALSE;
sl@0
  1541
    }
sl@0
  1542
sl@0
  1543
    data = m3gGetSectionDataPtr(loader, 6);
sl@0
  1544
    if (data == NULL) return M3G_FALSE;
sl@0
  1545
sl@0
  1546
    m3gSetCulling                       (obj, *data++);
sl@0
  1547
    m3gSetShading                       (obj, *data++);
sl@0
  1548
    m3gSetWinding                       (obj, *data++);
sl@0
  1549
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1550
    m3gSetTwoSidedLightingEnable        (obj, *data++);
sl@0
  1551
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1552
    m3gSetLocalCameraLightingEnable     (obj, *data++);
sl@0
  1553
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  1554
    m3gSetPerspectiveCorrectionEnable   (obj, *data++);
sl@0
  1555
sl@0
  1556
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1557
sl@0
  1558
    return M3G_TRUE;
sl@0
  1559
}
sl@0
  1560
sl@0
  1561
/*!
sl@0
  1562
 * \internal
sl@0
  1563
 * \brief Loads an appearance
sl@0
  1564
 */
sl@0
  1565
static M3Gbool m3gLoadAppearance(Loader *loader)
sl@0
  1566
{
sl@0
  1567
    M3Guint textures, i;
sl@0
  1568
    M3Gubyte *data;
sl@0
  1569
    M3GAppearance obj = m3gCreateAppearance(M3G_INTERFACE(loader));
sl@0
  1570
    loader->loadedObject = (M3GObject)obj;
sl@0
  1571
    if (!obj) return M3G_FALSE;
sl@0
  1572
sl@0
  1573
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1574
        return M3G_FALSE;
sl@0
  1575
    }
sl@0
  1576
sl@0
  1577
    data = m3gGetSectionDataPtr(loader, 21);
sl@0
  1578
    if (data == NULL) return M3G_FALSE;
sl@0
  1579
sl@0
  1580
    m3gSetLayer(obj, (M3Gbyte)*data++);
sl@0
  1581
    m3gSetCompositingMode(obj, (M3GCompositingMode)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_COMPOSITING_MODE));
sl@0
  1582
    data += 4;
sl@0
  1583
    m3gSetFog(obj, (M3GFog)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_FOG));
sl@0
  1584
    data += 4;
sl@0
  1585
    m3gSetPolygonMode(obj, (M3GPolygonMode)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_POLYGON_MODE));
sl@0
  1586
    data += 4;
sl@0
  1587
    m3gSetMaterial(obj, (M3GMaterial)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_MATERIAL));
sl@0
  1588
    data += 4;
sl@0
  1589
sl@0
  1590
    textures = m3gLoadInt(data);
sl@0
  1591
    data += 4;
sl@0
  1592
sl@0
  1593
    /* Overflow? */
sl@0
  1594
    if (textures >= 0x20000000) {
sl@0
  1595
        return M3G_FALSE;
sl@0
  1596
    }
sl@0
  1597
sl@0
  1598
    if (m3gCheckSectionDataLength(loader, data, textures * 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  1599
    for (i = 0; i < textures; i++) {
sl@0
  1600
        M3GTexture tex = (M3GTexture)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_TEXTURE);
sl@0
  1601
        if (!tex) {
sl@0
  1602
            return M3G_FALSE;
sl@0
  1603
        }
sl@0
  1604
        m3gSetTexture(obj, i, tex);
sl@0
  1605
        data += 4;
sl@0
  1606
    }
sl@0
  1607
sl@0
  1608
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1609
sl@0
  1610
    return M3G_TRUE;
sl@0
  1611
}
sl@0
  1612
sl@0
  1613
/*!
sl@0
  1614
 * \internal
sl@0
  1615
 * \brief Loads a mesh
sl@0
  1616
 */
sl@0
  1617
static M3Gbool m3gLoadMesh(Loader *loader)
sl@0
  1618
{
sl@0
  1619
    M3Guint subMeshCount, i;
sl@0
  1620
    M3GVertexBuffer vb;
sl@0
  1621
    M3GIndexBuffer *ib = NULL;
sl@0
  1622
    M3GAppearance *ap = NULL;
sl@0
  1623
    M3Gubyte *data;
sl@0
  1624
    M3GMesh obj = NULL;
sl@0
  1625
sl@0
  1626
    m3gBeginObject(loader);
sl@0
  1627
    if (!m3gSkipNodeData(loader)) return M3G_FALSE;
sl@0
  1628
sl@0
  1629
    data = m3gGetSectionDataPtr(loader, 8);
sl@0
  1630
    if (data == NULL) return M3G_FALSE;
sl@0
  1631
sl@0
  1632
    vb = (M3GVertexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_BUFFER);
sl@0
  1633
    if (vb == NULL) return M3G_FALSE;
sl@0
  1634
    data += 4;
sl@0
  1635
    subMeshCount = m3gLoadInt(data);
sl@0
  1636
    data += 4;
sl@0
  1637
sl@0
  1638
    /* Overflow? */
sl@0
  1639
    if (subMeshCount >= 0x10000000) {
sl@0
  1640
        return M3G_FALSE;
sl@0
  1641
    }
sl@0
  1642
sl@0
  1643
    if (m3gCheckSectionDataLength(loader, data, subMeshCount * 8) == M3G_FALSE) return M3G_FALSE;
sl@0
  1644
    ib = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ib) * subMeshCount);
sl@0
  1645
    ap = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ap) * subMeshCount);
sl@0
  1646
    if (!ib || !ap) goto cleanup;
sl@0
  1647
sl@0
  1648
    for (i = 0; i < subMeshCount; i++) {
sl@0
  1649
        M3GIndexBuffer indexBuffer;
sl@0
  1650
        indexBuffer = (M3GIndexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_INDEX_BUFFER);
sl@0
  1651
        if (indexBuffer != NULL && loader->triConstraint != 0) {
sl@0
  1652
            loader->triCount += indexBuffer->indexCount;
sl@0
  1653
            if (loader->triCount > loader->triConstraint) goto cleanup;
sl@0
  1654
        }
sl@0
  1655
        ib[i] = indexBuffer;
sl@0
  1656
        data += 4;
sl@0
  1657
        ap[i] = (M3GAppearance)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_APPEARANCE);
sl@0
  1658
        data += 4;
sl@0
  1659
    }
sl@0
  1660
sl@0
  1661
    obj = m3gCreateMesh(    M3G_INTERFACE(loader),
sl@0
  1662
                            vb,
sl@0
  1663
                            ib,
sl@0
  1664
                            ap,
sl@0
  1665
                            subMeshCount);
sl@0
  1666
sl@0
  1667
cleanup:
sl@0
  1668
    m3gFree(M3G_INTERFACE(loader), ib);
sl@0
  1669
    m3gFree(M3G_INTERFACE(loader), ap);
sl@0
  1670
    loader->loadedObject = (M3GObject)obj;
sl@0
  1671
    if (!obj) return M3G_FALSE;
sl@0
  1672
sl@0
  1673
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1674
sl@0
  1675
    m3gEndObject(loader);
sl@0
  1676
    m3gRewindObject(loader);
sl@0
  1677
    if (!m3gLoadNodeData(loader, (M3GNode)obj)) {
sl@0
  1678
        return M3G_FALSE;
sl@0
  1679
    }
sl@0
  1680
    m3gSkipObject(loader);
sl@0
  1681
sl@0
  1682
    return M3G_TRUE;
sl@0
  1683
}
sl@0
  1684
sl@0
  1685
/*!
sl@0
  1686
 * \internal
sl@0
  1687
 * \brief Loads group data
sl@0
  1688
 */
sl@0
  1689
static M3Gbool m3gLoadGroupData(Loader *loader, M3GGroup obj)
sl@0
  1690
{
sl@0
  1691
    M3Guint childCount, i;
sl@0
  1692
    M3Gubyte *data;
sl@0
  1693
sl@0
  1694
    if (!m3gLoadNodeData(loader, (M3GNode)obj)) {
sl@0
  1695
        return M3G_FALSE;
sl@0
  1696
    }
sl@0
  1697
sl@0
  1698
    data = m3gGetSectionDataPtr(loader, 4);
sl@0
  1699
    if (data == NULL) return M3G_FALSE;
sl@0
  1700
sl@0
  1701
    childCount = m3gLoadInt(data);
sl@0
  1702
    data += 4;
sl@0
  1703
sl@0
  1704
    /* Overflow? */
sl@0
  1705
    if (childCount >= 0x20000000) {
sl@0
  1706
        return M3G_FALSE;
sl@0
  1707
    }
sl@0
  1708
sl@0
  1709
    if (m3gCheckSectionDataLength(loader, data, childCount * 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  1710
    for (i = 0; i < childCount; i++) {
sl@0
  1711
        m3gAddChild(obj, (M3GNode)m3gGetLoaded(loader, m3gLoadInt(data), ANY_NODE_CLASS));
sl@0
  1712
        data += 4;
sl@0
  1713
    }
sl@0
  1714
sl@0
  1715
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1716
sl@0
  1717
    return M3G_TRUE;
sl@0
  1718
}
sl@0
  1719
sl@0
  1720
/*!
sl@0
  1721
 * \internal
sl@0
  1722
 * \brief Loads a group
sl@0
  1723
 */
sl@0
  1724
static M3Gbool m3gLoadGroup(Loader *loader)
sl@0
  1725
{
sl@0
  1726
    M3GGroup obj = m3gCreateGroup(M3G_INTERFACE(loader));
sl@0
  1727
    loader->loadedObject = (M3GObject)obj;
sl@0
  1728
    if (!obj) return M3G_FALSE;
sl@0
  1729
sl@0
  1730
    if (!m3gLoadGroupData(loader, obj)) {
sl@0
  1731
        return M3G_FALSE;
sl@0
  1732
    }
sl@0
  1733
sl@0
  1734
    return M3G_TRUE;
sl@0
  1735
}
sl@0
  1736
sl@0
  1737
/*!
sl@0
  1738
 * \internal
sl@0
  1739
 * \brief Loads a world
sl@0
  1740
 */
sl@0
  1741
static M3Gbool m3gLoadWorld(Loader *loader)
sl@0
  1742
{
sl@0
  1743
    M3GCamera cam;
sl@0
  1744
    M3Gubyte *data;
sl@0
  1745
    M3GWorld obj = m3gCreateWorld(M3G_INTERFACE(loader));
sl@0
  1746
    loader->loadedObject = (M3GObject)obj;
sl@0
  1747
    if (!obj) return M3G_FALSE;
sl@0
  1748
sl@0
  1749
    if (!m3gLoadGroupData(loader, (M3GGroup)obj)) {
sl@0
  1750
        return M3G_FALSE;
sl@0
  1751
    }
sl@0
  1752
sl@0
  1753
    data = m3gGetSectionDataPtr(loader, 8);
sl@0
  1754
    if (data == NULL) return M3G_FALSE;
sl@0
  1755
sl@0
  1756
    cam = (M3GCamera)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_CAMERA);
sl@0
  1757
    data += 4;
sl@0
  1758
    if (cam != NULL) {
sl@0
  1759
        m3gSetActiveCamera(obj, cam);
sl@0
  1760
    }
sl@0
  1761
    m3gSetBackground(obj, (M3GBackground)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_BACKGROUND));
sl@0
  1762
    data += 4;
sl@0
  1763
sl@0
  1764
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1765
sl@0
  1766
    return M3G_TRUE;
sl@0
  1767
}
sl@0
  1768
sl@0
  1769
/*!
sl@0
  1770
 * \internal
sl@0
  1771
 * \brief Loads a light
sl@0
  1772
 */
sl@0
  1773
static M3Gbool m3gLoadLight(Loader *loader)
sl@0
  1774
{
sl@0
  1775
    M3Gfloat f1, f2, f3;
sl@0
  1776
    M3Gubyte *data;
sl@0
  1777
    M3GLight obj = m3gCreateLight(M3G_INTERFACE(loader));
sl@0
  1778
    loader->loadedObject = (M3GObject)obj;
sl@0
  1779
    if (!obj) return M3G_FALSE;
sl@0
  1780
sl@0
  1781
    if (!m3gLoadNodeData(loader, (M3GNode)obj)) {
sl@0
  1782
        return M3G_FALSE;
sl@0
  1783
    }
sl@0
  1784
sl@0
  1785
    data = m3gGetSectionDataPtr(loader, 28);
sl@0
  1786
    if (data == NULL) return M3G_FALSE;
sl@0
  1787
sl@0
  1788
    if (!m3gLoadFloat(data, &f1) ||
sl@0
  1789
        !m3gLoadFloat(data + 4, &f2) ||
sl@0
  1790
        !m3gLoadFloat(data + 8, &f3)) return M3G_FALSE;
sl@0
  1791
    m3gSetAttenuation   (obj, f1, f2, f3);
sl@0
  1792
    data += 12;
sl@0
  1793
    m3gSetLightColor    (obj, m3gLoadRGB(data));
sl@0
  1794
    data += 3;
sl@0
  1795
    m3gSetLightMode     (obj, *data++);
sl@0
  1796
    if (!m3gLoadFloat(data, &f1)) return M3G_FALSE;
sl@0
  1797
    m3gSetIntensity     (obj, f1);
sl@0
  1798
    data += 4;
sl@0
  1799
    if (!m3gLoadFloat(data, &f1)) return M3G_FALSE;
sl@0
  1800
    m3gSetSpotAngle     (obj, f1);
sl@0
  1801
    data += 4;
sl@0
  1802
    if (!m3gLoadFloat(data, &f1)) return M3G_FALSE;
sl@0
  1803
    m3gSetSpotExponent  (obj, f1);
sl@0
  1804
    data += 4;
sl@0
  1805
sl@0
  1806
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1807
sl@0
  1808
    return M3G_TRUE;
sl@0
  1809
}
sl@0
  1810
sl@0
  1811
/*!
sl@0
  1812
 * \internal
sl@0
  1813
 * \brief Loads a keyframe sequence
sl@0
  1814
 */
sl@0
  1815
static M3Gbool m3gLoadKeyframeSequence(Loader *loader)
sl@0
  1816
{
sl@0
  1817
    M3Guint i, j, interpolation, repeatMode, encoding, duration,
sl@0
  1818
            rangeFirst, rangeLast, components, keyFrames, size;
sl@0
  1819
    M3Gfloat *values;
sl@0
  1820
    M3Gubyte *data;
sl@0
  1821
    M3GKeyframeSequence obj;
sl@0
  1822
sl@0
  1823
    m3gBeginObject(loader);
sl@0
  1824
    if (!m3gSkipObject3DData(loader)) return M3G_FALSE;
sl@0
  1825
sl@0
  1826
    data = m3gGetSectionDataPtr(loader, 23);
sl@0
  1827
    if (data == NULL) return M3G_FALSE;
sl@0
  1828
sl@0
  1829
    interpolation = *data++;
sl@0
  1830
    repeatMode    = *data++;
sl@0
  1831
    encoding      = *data++;
sl@0
  1832
    duration      = m3gLoadInt(data);
sl@0
  1833
    data += 4;
sl@0
  1834
    rangeFirst    = m3gLoadInt(data);
sl@0
  1835
    data += 4;
sl@0
  1836
    rangeLast     = m3gLoadInt(data);
sl@0
  1837
    data += 4;
sl@0
  1838
    components    = m3gLoadInt(data);
sl@0
  1839
    data += 4;
sl@0
  1840
    keyFrames     = m3gLoadInt(data);
sl@0
  1841
    data += 4;
sl@0
  1842
sl@0
  1843
    if (encoding == 0) {
sl@0
  1844
        size = keyFrames * (4 + components * 4);
sl@0
  1845
    }
sl@0
  1846
    else {
sl@0
  1847
        size = components * 8 + keyFrames * (4 + components * (encoding == 1 ? 1 : 2));
sl@0
  1848
    }
sl@0
  1849
sl@0
  1850
    /* Overflow? */
sl@0
  1851
    if (size < keyFrames) {
sl@0
  1852
        return M3G_FALSE;
sl@0
  1853
    }
sl@0
  1854
sl@0
  1855
    if (m3gCheckSectionDataLength(loader, data, size) == M3G_FALSE) return M3G_FALSE;
sl@0
  1856
sl@0
  1857
    obj = m3gCreateKeyframeSequence(M3G_INTERFACE(loader), keyFrames, components, interpolation);
sl@0
  1858
    loader->loadedObject = (M3GObject)obj;
sl@0
  1859
    if (!obj) return M3G_FALSE;
sl@0
  1860
sl@0
  1861
    m3gSetRepeatMode(obj, repeatMode);
sl@0
  1862
    m3gSetDuration(obj, duration);
sl@0
  1863
    m3gSetValidRange(obj, rangeFirst, rangeLast);
sl@0
  1864
sl@0
  1865
    values = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gfloat) * components);
sl@0
  1866
    if (!values) return M3G_FALSE;
sl@0
  1867
sl@0
  1868
    if (encoding == 0) {
sl@0
  1869
        for (i = 0; i < keyFrames; i++ ) {
sl@0
  1870
            M3Gint time = m3gLoadInt(data);
sl@0
  1871
            data += 4;
sl@0
  1872
sl@0
  1873
            for (j = 0; j < components; j++ ) {
sl@0
  1874
                if (!m3gLoadFloat(data, &values[j])) {
sl@0
  1875
                    m3gFree(M3G_INTERFACE(loader), values);
sl@0
  1876
                    return M3G_FALSE;
sl@0
  1877
                }
sl@0
  1878
                data += 4;
sl@0
  1879
            }
sl@0
  1880
sl@0
  1881
            m3gSetKeyframe(obj, i, time, components, values);
sl@0
  1882
        }
sl@0
  1883
    }
sl@0
  1884
    else {
sl@0
  1885
        M3Gfloat *vectorBiasScale = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gfloat) * components * 2);
sl@0
  1886
        if (!vectorBiasScale) {
sl@0
  1887
            m3gFree(M3G_INTERFACE(loader), values);
sl@0
  1888
            return M3G_FALSE;
sl@0
  1889
        }
sl@0
  1890
sl@0
  1891
        for (i = 0; i < components; i++ ) {
sl@0
  1892
            if (!m3gLoadFloat(data, &vectorBiasScale[i])) {
sl@0
  1893
                m3gFree(M3G_INTERFACE(loader), vectorBiasScale);
sl@0
  1894
                m3gFree(M3G_INTERFACE(loader), values);
sl@0
  1895
                return M3G_FALSE;
sl@0
  1896
            }
sl@0
  1897
            data += 4;
sl@0
  1898
        }
sl@0
  1899
        for (i = 0; i < components; i++ ) {
sl@0
  1900
            if (!m3gLoadFloat(data, &vectorBiasScale[i + components])) {
sl@0
  1901
                m3gFree(M3G_INTERFACE(loader), vectorBiasScale);
sl@0
  1902
                m3gFree(M3G_INTERFACE(loader), values);
sl@0
  1903
                return M3G_FALSE;
sl@0
  1904
            }
sl@0
  1905
            data += 4;
sl@0
  1906
        }
sl@0
  1907
sl@0
  1908
        for (i = 0; i < keyFrames; i++ ) {
sl@0
  1909
            M3Gint time;
sl@0
  1910
            time = m3gLoadInt(data);
sl@0
  1911
            data += 4;
sl@0
  1912
sl@0
  1913
            if (encoding == 1) {
sl@0
  1914
                for (j = 0; j < components; j++ ) {
sl@0
  1915
                    M3Gubyte v = *data++;
sl@0
  1916
                    values[j] = vectorBiasScale[j] + ((vectorBiasScale[j + components] * v ) / 255.0f);
sl@0
  1917
                }
sl@0
  1918
            }
sl@0
  1919
            else {
sl@0
  1920
                for (j = 0; j < components; j++ ) {
sl@0
  1921
                    M3Gushort v = m3gLoadShort(data);
sl@0
  1922
                    data += 2;
sl@0
  1923
                    values[j] = vectorBiasScale[j] + ((vectorBiasScale[j + components] * v) / 65535.0f);
sl@0
  1924
                }
sl@0
  1925
            }
sl@0
  1926
sl@0
  1927
            m3gSetKeyframe(obj, i, time, components, values);
sl@0
  1928
        }
sl@0
  1929
sl@0
  1930
        m3gFree(M3G_INTERFACE(loader), vectorBiasScale);
sl@0
  1931
    }
sl@0
  1932
sl@0
  1933
    m3gFree(M3G_INTERFACE(loader), values);
sl@0
  1934
sl@0
  1935
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1936
sl@0
  1937
    m3gEndObject(loader);
sl@0
  1938
    m3gRewindObject(loader);
sl@0
  1939
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1940
        return M3G_FALSE;
sl@0
  1941
    }
sl@0
  1942
    m3gSkipObject(loader);
sl@0
  1943
sl@0
  1944
    return M3G_TRUE;
sl@0
  1945
}
sl@0
  1946
sl@0
  1947
/*!
sl@0
  1948
 * \internal
sl@0
  1949
 * \brief Loads an animation controller
sl@0
  1950
 */
sl@0
  1951
static M3Gbool m3gLoadAnimationController(Loader *loader)
sl@0
  1952
{
sl@0
  1953
    M3Gfloat speed, weight, referenceSeqTime;
sl@0
  1954
    M3Gint referenceWorldTime;
sl@0
  1955
    M3Gubyte *data;
sl@0
  1956
    M3GAnimationController obj = m3gCreateAnimationController(M3G_INTERFACE(loader));
sl@0
  1957
    loader->loadedObject = (M3GObject)obj;
sl@0
  1958
    if (!obj) return M3G_FALSE;
sl@0
  1959
sl@0
  1960
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  1961
        return M3G_FALSE;
sl@0
  1962
    }
sl@0
  1963
sl@0
  1964
    data = m3gGetSectionDataPtr(loader, 24);
sl@0
  1965
    if (data == NULL) return M3G_FALSE;
sl@0
  1966
sl@0
  1967
    if (!m3gLoadFloat(data, &speed)) return M3G_FALSE;
sl@0
  1968
    data += 4;
sl@0
  1969
    if (!m3gLoadFloat(data, &weight)) return M3G_FALSE;
sl@0
  1970
    data += 4;
sl@0
  1971
    m3gSetActiveInterval(obj, m3gLoadInt(data), m3gLoadInt(data + 4));
sl@0
  1972
    data += 8;
sl@0
  1973
    if (!m3gLoadFloat(data, &referenceSeqTime)) return M3G_FALSE;
sl@0
  1974
    data += 4;
sl@0
  1975
    referenceWorldTime = m3gLoadInt(data);
sl@0
  1976
    data += 4;
sl@0
  1977
sl@0
  1978
    m3gSetPosition(obj, referenceSeqTime, referenceWorldTime);
sl@0
  1979
    m3gSetSpeed(obj, speed, referenceWorldTime);
sl@0
  1980
    m3gSetWeight(obj, weight);
sl@0
  1981
sl@0
  1982
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  1983
sl@0
  1984
    return M3G_TRUE;
sl@0
  1985
}
sl@0
  1986
sl@0
  1987
/*!
sl@0
  1988
 * \internal
sl@0
  1989
 * \brief Loads an animation track
sl@0
  1990
 */
sl@0
  1991
static M3Gbool m3gLoadAnimationTrack(Loader *loader)
sl@0
  1992
{
sl@0
  1993
    M3Gint property;
sl@0
  1994
    M3GKeyframeSequence ks;
sl@0
  1995
    M3GAnimationController ac;
sl@0
  1996
    M3Gubyte *data;
sl@0
  1997
    M3GAnimationTrack obj;
sl@0
  1998
sl@0
  1999
    m3gBeginObject(loader);
sl@0
  2000
    if (!m3gSkipObject3DData(loader)) return M3G_FALSE;
sl@0
  2001
sl@0
  2002
    data = m3gGetSectionDataPtr(loader, 12);
sl@0
  2003
    if (data == NULL) return M3G_FALSE;
sl@0
  2004
sl@0
  2005
    ks = (M3GKeyframeSequence)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_KEYFRAME_SEQUENCE);
sl@0
  2006
    data += 4;
sl@0
  2007
    ac = (M3GAnimationController)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_ANIMATION_CONTROLLER);
sl@0
  2008
    data += 4;
sl@0
  2009
    property = m3gLoadInt(data);
sl@0
  2010
    data += 4;
sl@0
  2011
sl@0
  2012
    obj = m3gCreateAnimationTrack(M3G_INTERFACE(loader), ks, property);
sl@0
  2013
    loader->loadedObject = (M3GObject)obj;
sl@0
  2014
    if (!obj) return M3G_FALSE;
sl@0
  2015
sl@0
  2016
    m3gSetController(obj, ac);
sl@0
  2017
sl@0
  2018
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2019
sl@0
  2020
    m3gEndObject(loader);
sl@0
  2021
    m3gRewindObject(loader);
sl@0
  2022
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  2023
        return M3G_FALSE;
sl@0
  2024
    }
sl@0
  2025
    m3gSkipObject(loader);
sl@0
  2026
sl@0
  2027
    return M3G_TRUE;
sl@0
  2028
}
sl@0
  2029
sl@0
  2030
/*!
sl@0
  2031
 * \internal
sl@0
  2032
 * \brief Loads a material
sl@0
  2033
 */
sl@0
  2034
static M3Gbool m3gLoadMaterial(Loader *loader)
sl@0
  2035
{
sl@0
  2036
    M3Gfloat f1;
sl@0
  2037
    M3Gubyte *data;
sl@0
  2038
    M3GMaterial obj = m3gCreateMaterial(M3G_INTERFACE(loader));
sl@0
  2039
    loader->loadedObject = (M3GObject)obj;
sl@0
  2040
    if (!obj) return M3G_FALSE;
sl@0
  2041
sl@0
  2042
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  2043
        return M3G_FALSE;
sl@0
  2044
    }
sl@0
  2045
sl@0
  2046
    data = m3gGetSectionDataPtr(loader, 18);
sl@0
  2047
    if (data == NULL) return M3G_FALSE;
sl@0
  2048
sl@0
  2049
    m3gSetColor(obj, M3G_AMBIENT_BIT, m3gLoadRGB(data));
sl@0
  2050
    data += 3;
sl@0
  2051
    m3gSetColor(obj, M3G_DIFFUSE_BIT, m3gLoadARGB(data));
sl@0
  2052
    data += 4;
sl@0
  2053
    m3gSetColor(obj, M3G_EMISSIVE_BIT, m3gLoadRGB(data));
sl@0
  2054
    data += 3;
sl@0
  2055
    m3gSetColor(obj, M3G_SPECULAR_BIT, m3gLoadRGB(data));
sl@0
  2056
    data += 3;
sl@0
  2057
    if (!m3gLoadFloat(data, &f1)) return M3G_FALSE;
sl@0
  2058
    m3gSetShininess(obj, f1);
sl@0
  2059
    data += 4;
sl@0
  2060
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  2061
    m3gSetVertexColorTrackingEnable(obj, *data++);
sl@0
  2062
sl@0
  2063
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2064
sl@0
  2065
    return M3G_TRUE;
sl@0
  2066
}
sl@0
  2067
sl@0
  2068
/*!
sl@0
  2069
 * \internal
sl@0
  2070
 * \brief Loads a fog
sl@0
  2071
 */
sl@0
  2072
static M3Gbool m3gLoadFog(Loader *loader)
sl@0
  2073
{
sl@0
  2074
    M3Gfloat f1, f2;
sl@0
  2075
    M3Gubyte *data;
sl@0
  2076
    M3GFog obj = m3gCreateFog(M3G_INTERFACE(loader));
sl@0
  2077
    loader->loadedObject = (M3GObject)obj;
sl@0
  2078
    if (!obj) return M3G_FALSE;
sl@0
  2079
sl@0
  2080
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  2081
        return M3G_FALSE;
sl@0
  2082
    }
sl@0
  2083
sl@0
  2084
    data = m3gGetSectionDataPtr(loader, 4);
sl@0
  2085
    if (data == NULL) return M3G_FALSE;
sl@0
  2086
sl@0
  2087
    m3gSetFogColor(obj, m3gLoadRGB(data));
sl@0
  2088
    data += 3;
sl@0
  2089
    m3gSetFogMode(obj, *data);
sl@0
  2090
sl@0
  2091
    if (*data++ == M3G_EXPONENTIAL_FOG) {
sl@0
  2092
        if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  2093
        if (!m3gLoadFloat(data, &f1)) return M3G_FALSE;
sl@0
  2094
        m3gSetFogDensity(obj, f1);
sl@0
  2095
        data += 4;
sl@0
  2096
    }
sl@0
  2097
    else {
sl@0
  2098
        if (m3gCheckSectionDataLength(loader, data, 8) == M3G_FALSE) return M3G_FALSE;
sl@0
  2099
        if (!m3gLoadFloat(data, &f1) || !m3gLoadFloat(data + 4, &f2)) return M3G_FALSE;
sl@0
  2100
        m3gSetFogLinear(obj, f1, f2);
sl@0
  2101
        data += 8;
sl@0
  2102
    }
sl@0
  2103
sl@0
  2104
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2105
sl@0
  2106
    return M3G_TRUE;
sl@0
  2107
}
sl@0
  2108
sl@0
  2109
/*!
sl@0
  2110
 * \internal
sl@0
  2111
 * \brief Loads an image
sl@0
  2112
 */
sl@0
  2113
static M3Gbool m3gLoadImage(Loader *loader)
sl@0
  2114
{
sl@0
  2115
    M3GImageFormat format;
sl@0
  2116
    M3Guint width, height;
sl@0
  2117
    M3Gbyte isMutable;
sl@0
  2118
    M3Gubyte *data;
sl@0
  2119
    M3GImage obj;
sl@0
  2120
sl@0
  2121
    m3gBeginObject(loader);
sl@0
  2122
    if (!m3gSkipObject3DData(loader)) return M3G_FALSE;
sl@0
  2123
sl@0
  2124
    data = m3gGetSectionDataPtr(loader, 10);
sl@0
  2125
    if (data == NULL) return M3G_FALSE;
sl@0
  2126
sl@0
  2127
    format = (M3GImageFormat)*data++;
sl@0
  2128
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  2129
    isMutable = *data++;
sl@0
  2130
    width = m3gLoadInt(data);
sl@0
  2131
    data += 4;
sl@0
  2132
    height = m3gLoadInt(data);
sl@0
  2133
    data += 4;
sl@0
  2134
sl@0
  2135
    if (isMutable) {
sl@0
  2136
        obj = m3gCreateImage(M3G_INTERFACE(loader), format,
sl@0
  2137
                                                    width, height,
sl@0
  2138
                                                    M3G_RENDERING_TARGET);
sl@0
  2139
    }
sl@0
  2140
    else {
sl@0
  2141
        M3Gubyte *palette = NULL, *pixels = NULL;
sl@0
  2142
        M3Gint paletteLength, pixelsLength, bpp;
sl@0
  2143
sl@0
  2144
        switch(format) {
sl@0
  2145
        case M3G_ALPHA:             bpp = 1; break;
sl@0
  2146
        case M3G_LUMINANCE:         bpp = 1; break;
sl@0
  2147
        case M3G_LUMINANCE_ALPHA:   bpp = 2; break;
sl@0
  2148
        case M3G_RGB:               bpp = 3; break;
sl@0
  2149
        case M3G_RGBA:              bpp = 4; break;
sl@0
  2150
        default:                    return M3G_FALSE;
sl@0
  2151
        }
sl@0
  2152
sl@0
  2153
        if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  2154
        paletteLength = m3gLoadInt(data);
sl@0
  2155
        data += 4;
sl@0
  2156
sl@0
  2157
        if (paletteLength > 0) {
sl@0
  2158
            if (m3gCheckSectionDataLength(loader, data, paletteLength) == M3G_FALSE) return M3G_FALSE;
sl@0
  2159
            palette = data;
sl@0
  2160
            data += paletteLength;
sl@0
  2161
        }
sl@0
  2162
sl@0
  2163
        if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) return M3G_FALSE;
sl@0
  2164
        pixelsLength = m3gLoadInt(data);
sl@0
  2165
        data += 4;
sl@0
  2166
        if (m3gCheckSectionDataLength(loader, data, pixelsLength) == M3G_FALSE) return M3G_FALSE;
sl@0
  2167
        pixels = data;
sl@0
  2168
        data += pixelsLength;
sl@0
  2169
sl@0
  2170
        if (palette != NULL) {
sl@0
  2171
            obj = m3gCreateImage(M3G_INTERFACE(loader), format,
sl@0
  2172
                                                        width, height,
sl@0
  2173
                                                        M3G_PALETTED);
sl@0
  2174
            if (obj != NULL) {
sl@0
  2175
                M3Gint numEntries = paletteLength / bpp;
sl@0
  2176
                if (numEntries > 256) {
sl@0
  2177
                    numEntries = 256;
sl@0
  2178
                }
sl@0
  2179
                m3gSetImage(obj, pixels);
sl@0
  2180
                m3gSetImagePalette(obj, numEntries, palette);
sl@0
  2181
                m3gCommitImage(obj);
sl@0
  2182
            }
sl@0
  2183
        }
sl@0
  2184
        else {
sl@0
  2185
            obj = m3gCreateImage(M3G_INTERFACE(loader), format,
sl@0
  2186
                                                        width, height,
sl@0
  2187
                                                        0);
sl@0
  2188
            if (obj != NULL) {
sl@0
  2189
                m3gSetImage(obj, pixels);
sl@0
  2190
                m3gCommitImage(obj);
sl@0
  2191
            }
sl@0
  2192
        }
sl@0
  2193
    }
sl@0
  2194
sl@0
  2195
    loader->loadedObject = (M3GObject)obj;
sl@0
  2196
    if (!obj) return M3G_FALSE;
sl@0
  2197
sl@0
  2198
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2199
sl@0
  2200
    m3gEndObject(loader);
sl@0
  2201
    m3gRewindObject(loader);
sl@0
  2202
    if (!m3gLoadObject3DData(loader, (M3GObject)obj)) {
sl@0
  2203
        return M3G_FALSE;
sl@0
  2204
    }
sl@0
  2205
    m3gSkipObject(loader);
sl@0
  2206
sl@0
  2207
    return M3G_TRUE;
sl@0
  2208
}
sl@0
  2209
sl@0
  2210
/*!
sl@0
  2211
 * \internal
sl@0
  2212
 * \brief Loads a texture
sl@0
  2213
 */
sl@0
  2214
static M3Gbool m3gLoadTexture(Loader *loader)
sl@0
  2215
{
sl@0
  2216
    M3GImage image;
sl@0
  2217
    M3Gubyte *data;
sl@0
  2218
    M3GTexture obj;
sl@0
  2219
sl@0
  2220
    m3gBeginObject(loader);
sl@0
  2221
    if (!m3gSkipTransformableData(loader)) return M3G_FALSE;
sl@0
  2222
sl@0
  2223
    data = m3gGetSectionDataPtr(loader, 12);
sl@0
  2224
    if (data == NULL) return M3G_FALSE;
sl@0
  2225
sl@0
  2226
    image = (M3GImage)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_IMAGE);
sl@0
  2227
    data += 4;
sl@0
  2228
sl@0
  2229
    obj = m3gCreateTexture(M3G_INTERFACE(loader), image);
sl@0
  2230
    loader->loadedObject = (M3GObject)obj;
sl@0
  2231
    if (!obj) return M3G_FALSE;
sl@0
  2232
sl@0
  2233
    m3gSetBlendColor(obj, m3gLoadRGB(data));
sl@0
  2234
    data += 3;
sl@0
  2235
    m3gTextureSetBlending(obj, *data++);
sl@0
  2236
    m3gSetWrapping(obj, data[0], data[1]);
sl@0
  2237
    data += 2;
sl@0
  2238
    m3gSetFiltering(obj, data[0], data[1]);
sl@0
  2239
    data += 2;
sl@0
  2240
sl@0
  2241
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2242
sl@0
  2243
    m3gEndObject(loader);
sl@0
  2244
    m3gRewindObject(loader);
sl@0
  2245
    if (!m3gLoadTransformableData(loader, (M3GTransformable)obj)) {
sl@0
  2246
        return M3G_FALSE;
sl@0
  2247
    }
sl@0
  2248
    m3gSkipObject(loader);
sl@0
  2249
sl@0
  2250
    return M3G_TRUE;
sl@0
  2251
}
sl@0
  2252
sl@0
  2253
/*!
sl@0
  2254
 * \internal
sl@0
  2255
 * \brief Loads a skinned mesh
sl@0
  2256
 */
sl@0
  2257
static M3Gbool m3gLoadSkinnedMesh(Loader *loader)
sl@0
  2258
{
sl@0
  2259
    M3GVertexBuffer vb;
sl@0
  2260
    M3Guint i, subMeshCount, transformReferenceCount, firstVertex, vertexCount;
sl@0
  2261
    M3Gint weight;
sl@0
  2262
    M3GIndexBuffer *ib = NULL;
sl@0
  2263
    M3GAppearance *ap = NULL;
sl@0
  2264
    M3GGroup skeleton;
sl@0
  2265
    M3GNode bone;
sl@0
  2266
    M3Gubyte *data;
sl@0
  2267
    M3GSkinnedMesh obj = NULL;
sl@0
  2268
sl@0
  2269
    m3gBeginObject(loader);
sl@0
  2270
    if (!m3gSkipNodeData(loader)) return M3G_FALSE;
sl@0
  2271
sl@0
  2272
    data = m3gGetSectionDataPtr(loader, 8);
sl@0
  2273
    if (data == NULL) return M3G_FALSE;
sl@0
  2274
sl@0
  2275
    vb = (M3GVertexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_BUFFER);
sl@0
  2276
    if (vb == NULL) return M3G_FALSE;
sl@0
  2277
    data += 4;
sl@0
  2278
    subMeshCount = m3gLoadInt(data);
sl@0
  2279
    data += 4;
sl@0
  2280
sl@0
  2281
    /* Overflow? */
sl@0
  2282
    if (subMeshCount >= 0x10000000) {
sl@0
  2283
        return M3G_FALSE;
sl@0
  2284
    }
sl@0
  2285
sl@0
  2286
    if (m3gCheckSectionDataLength(loader, data, subMeshCount * 8) == M3G_FALSE) return M3G_FALSE;
sl@0
  2287
    ib = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ib) * subMeshCount);
sl@0
  2288
    ap = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ap) * subMeshCount);
sl@0
  2289
    if (!ib || !ap) goto cleanup;
sl@0
  2290
sl@0
  2291
    for (i = 0; i < subMeshCount; i++) {
sl@0
  2292
        ib[i] = (M3GIndexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_INDEX_BUFFER);
sl@0
  2293
        data += 4;
sl@0
  2294
        ap[i] = (M3GAppearance)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_APPEARANCE);
sl@0
  2295
        data += 4;
sl@0
  2296
    }
sl@0
  2297
sl@0
  2298
    if (m3gCheckSectionDataLength(loader, data, 8) == M3G_FALSE) goto cleanup;
sl@0
  2299
    skeleton = (M3GGroup)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_GROUP);
sl@0
  2300
    data += 4;
sl@0
  2301
sl@0
  2302
    obj = m3gCreateSkinnedMesh( M3G_INTERFACE(loader),
sl@0
  2303
                                vb,
sl@0
  2304
                                ib,
sl@0
  2305
                                ap,
sl@0
  2306
                                subMeshCount,
sl@0
  2307
                                skeleton);
sl@0
  2308
sl@0
  2309
cleanup:
sl@0
  2310
    m3gFree(M3G_INTERFACE(loader), ib);
sl@0
  2311
    m3gFree(M3G_INTERFACE(loader), ap);
sl@0
  2312
    loader->loadedObject = (M3GObject)obj;
sl@0
  2313
    if (!obj) return M3G_FALSE;
sl@0
  2314
sl@0
  2315
    transformReferenceCount = m3gLoadInt(data);
sl@0
  2316
    data += 4;
sl@0
  2317
sl@0
  2318
    /* Overflow? */
sl@0
  2319
    if (transformReferenceCount >= 0x08000000) {
sl@0
  2320
        return M3G_FALSE;
sl@0
  2321
    }
sl@0
  2322
sl@0
  2323
    if (m3gCheckSectionDataLength(loader, data, transformReferenceCount * 16) == M3G_FALSE) return M3G_FALSE;
sl@0
  2324
    for (i = 0; i < transformReferenceCount; i++) {
sl@0
  2325
        bone        = (M3GNode)m3gGetLoaded(loader, m3gLoadInt(data), ANY_NODE_CLASS);
sl@0
  2326
        data += 4;
sl@0
  2327
        firstVertex = m3gLoadInt(data);
sl@0
  2328
        data += 4;
sl@0
  2329
        vertexCount = m3gLoadInt(data);
sl@0
  2330
        data += 4;
sl@0
  2331
        weight      = m3gLoadInt(data);
sl@0
  2332
        data += 4;
sl@0
  2333
        m3gAddTransform(obj, bone, weight, firstVertex, vertexCount);
sl@0
  2334
    }
sl@0
  2335
sl@0
  2336
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2337
sl@0
  2338
    m3gEndObject(loader);
sl@0
  2339
    m3gRewindObject(loader);
sl@0
  2340
    if (!m3gLoadNodeData(loader, (M3GNode)obj)) {
sl@0
  2341
        return M3G_FALSE;
sl@0
  2342
    }
sl@0
  2343
    m3gSkipObject(loader);
sl@0
  2344
sl@0
  2345
    return M3G_TRUE;
sl@0
  2346
}
sl@0
  2347
sl@0
  2348
/*!
sl@0
  2349
 * \internal
sl@0
  2350
 * \brief Loads a morphing mesh
sl@0
  2351
 */
sl@0
  2352
static M3Gbool m3gLoadMorphingMesh(Loader *loader)
sl@0
  2353
{
sl@0
  2354
    M3GVertexBuffer vb, *targets = NULL;
sl@0
  2355
    M3Guint i, subMeshCount, targetCount;
sl@0
  2356
    M3Gfloat *weights = NULL;
sl@0
  2357
    M3GIndexBuffer *ib = NULL;
sl@0
  2358
    M3GAppearance *ap = NULL;
sl@0
  2359
    M3Gubyte *data;
sl@0
  2360
    M3GMorphingMesh obj = NULL;
sl@0
  2361
sl@0
  2362
    m3gBeginObject(loader);
sl@0
  2363
    if (!m3gSkipNodeData(loader)) return M3G_FALSE;
sl@0
  2364
sl@0
  2365
    data = m3gGetSectionDataPtr(loader, 8);
sl@0
  2366
    if (data == NULL) return M3G_FALSE;
sl@0
  2367
sl@0
  2368
    vb = (M3GVertexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_BUFFER);
sl@0
  2369
    if (vb == NULL) return M3G_FALSE;
sl@0
  2370
    data += 4;
sl@0
  2371
    subMeshCount = m3gLoadInt(data);
sl@0
  2372
    data += 4;
sl@0
  2373
sl@0
  2374
    /* Overflow? */
sl@0
  2375
    if (subMeshCount >= 0x10000000) {
sl@0
  2376
        return M3G_FALSE;
sl@0
  2377
    }
sl@0
  2378
sl@0
  2379
    if (m3gCheckSectionDataLength(loader, data, subMeshCount * 8) == M3G_FALSE) return M3G_FALSE;
sl@0
  2380
    ib = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ib) * subMeshCount);
sl@0
  2381
    ap = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*ap) * subMeshCount);
sl@0
  2382
    if (!ib || !ap) goto cleanup;
sl@0
  2383
sl@0
  2384
    for (i = 0; i < subMeshCount; i++) {
sl@0
  2385
        ib[i] = (M3GIndexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_INDEX_BUFFER);
sl@0
  2386
        data += 4;
sl@0
  2387
        ap[i] = (M3GAppearance)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_APPEARANCE);
sl@0
  2388
        data += 4;
sl@0
  2389
    }
sl@0
  2390
sl@0
  2391
    if (m3gCheckSectionDataLength(loader, data, 4) == M3G_FALSE) goto cleanup;
sl@0
  2392
    targetCount = m3gLoadInt(data);
sl@0
  2393
    data += 4;
sl@0
  2394
sl@0
  2395
    /* Overflow? */
sl@0
  2396
    if (targetCount >= 0x10000000) {
sl@0
  2397
        goto cleanup;
sl@0
  2398
    }
sl@0
  2399
sl@0
  2400
    if (m3gCheckSectionDataLength(loader, data, targetCount * 8) == M3G_FALSE) goto cleanup;
sl@0
  2401
    weights = m3gAllocZ(M3G_INTERFACE(loader), sizeof(M3Gfloat) * targetCount);
sl@0
  2402
    targets = m3gAllocZ(M3G_INTERFACE(loader), sizeof(*targets) * targetCount);
sl@0
  2403
    if (!weights || !targets) goto cleanup;
sl@0
  2404
sl@0
  2405
    for (i = 0; i < targetCount; i++) {
sl@0
  2406
        targets[i] = (M3GVertexBuffer)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_VERTEX_BUFFER);
sl@0
  2407
        data += 4;
sl@0
  2408
        if (!m3gLoadFloat(data, &weights[i])) goto cleanup;
sl@0
  2409
        data += 4;
sl@0
  2410
    }
sl@0
  2411
sl@0
  2412
    obj = m3gCreateMorphingMesh(    M3G_INTERFACE(loader),
sl@0
  2413
                                    vb,
sl@0
  2414
                                    targets,
sl@0
  2415
                                    ib,
sl@0
  2416
                                    ap,
sl@0
  2417
                                    subMeshCount,
sl@0
  2418
                                    targetCount);
sl@0
  2419
sl@0
  2420
cleanup:
sl@0
  2421
    m3gFree(M3G_INTERFACE(loader), ib);
sl@0
  2422
    m3gFree(M3G_INTERFACE(loader), ap);
sl@0
  2423
    m3gFree(M3G_INTERFACE(loader), targets);
sl@0
  2424
    m3gFree(M3G_INTERFACE(loader), weights);
sl@0
  2425
    loader->loadedObject = (M3GObject)obj;
sl@0
  2426
    if (!obj) return M3G_FALSE;
sl@0
  2427
sl@0
  2428
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2429
sl@0
  2430
    m3gEndObject(loader);
sl@0
  2431
    m3gRewindObject(loader);
sl@0
  2432
    if (!m3gLoadNodeData(loader, (M3GNode)obj)) {
sl@0
  2433
        return M3G_FALSE;
sl@0
  2434
    }
sl@0
  2435
    m3gSkipObject(loader);
sl@0
  2436
sl@0
  2437
    return M3G_TRUE;
sl@0
  2438
}
sl@0
  2439
sl@0
  2440
/*!
sl@0
  2441
 * \internal
sl@0
  2442
 * \brief Loads a sprite
sl@0
  2443
 */
sl@0
  2444
static M3Gbool m3gLoadSprite(Loader *loader)
sl@0
  2445
{
sl@0
  2446
    M3GImage image;
sl@0
  2447
    M3GAppearance appearance;
sl@0
  2448
    M3Gubyte *data;
sl@0
  2449
    M3GSprite obj;
sl@0
  2450
sl@0
  2451
    m3gBeginObject(loader);
sl@0
  2452
    if (!m3gSkipNodeData(loader)) return M3G_FALSE;
sl@0
  2453
sl@0
  2454
    data = m3gGetSectionDataPtr(loader, 25);
sl@0
  2455
    if (data == NULL) return M3G_FALSE;
sl@0
  2456
sl@0
  2457
    image = (M3GImage)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_IMAGE);
sl@0
  2458
    data += 4;
sl@0
  2459
    appearance = (M3GAppearance)m3gGetLoaded(loader, m3gLoadInt(data), M3G_CLASS_APPEARANCE);
sl@0
  2460
    data += 4;
sl@0
  2461
sl@0
  2462
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  2463
    obj = m3gCreateSprite(  M3G_INTERFACE(loader),
sl@0
  2464
                            *data++,
sl@0
  2465
                            image,
sl@0
  2466
                            appearance);
sl@0
  2467
    loader->loadedObject = (M3GObject)obj;
sl@0
  2468
    if (!obj) return M3G_FALSE;
sl@0
  2469
sl@0
  2470
    m3gSetCrop(obj, m3gLoadInt(data),
sl@0
  2471
                    m3gLoadInt(data + 4),
sl@0
  2472
                    m3gLoadInt(data + 8),
sl@0
  2473
                    m3gLoadInt(data + 12));
sl@0
  2474
    data += 16;
sl@0
  2475
sl@0
  2476
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2477
sl@0
  2478
    m3gEndObject(loader);
sl@0
  2479
    m3gRewindObject(loader);
sl@0
  2480
    if (!m3gLoadNodeData(loader, (M3GNode)obj)) {
sl@0
  2481
        return M3G_FALSE;
sl@0
  2482
    }
sl@0
  2483
    m3gSkipObject(loader);
sl@0
  2484
sl@0
  2485
    return M3G_TRUE;
sl@0
  2486
}
sl@0
  2487
sl@0
  2488
/*!
sl@0
  2489
 * \internal
sl@0
  2490
 * \brief Loads a M3G file header
sl@0
  2491
 */
sl@0
  2492
static M3Gbool m3gLoadHeader(Loader *loader)
sl@0
  2493
{
sl@0
  2494
    M3Gubyte *data;
sl@0
  2495
    data = m3gGetSectionDataPtr(loader, 12);
sl@0
  2496
    if (data == NULL) return M3G_FALSE;
sl@0
  2497
sl@0
  2498
    /* Check version */
sl@0
  2499
    if (data[0] != 1 || data[1] != 0 || loader->sectionNum != 0) {
sl@0
  2500
        return M3G_FALSE;
sl@0
  2501
    }
sl@0
  2502
    data += 2;
sl@0
  2503
sl@0
  2504
    if (!m3gVerifyBool(data)) return M3G_FALSE;
sl@0
  2505
    loader->hasReferences = *data++;
sl@0
  2506
    loader->fileSize = m3gLoadInt(data);
sl@0
  2507
    data += 4;
sl@0
  2508
    loader->contentSize = m3gLoadInt(data);
sl@0
  2509
    data += 4;
sl@0
  2510
sl@0
  2511
    /* Skip authoring field */
sl@0
  2512
    while(*data++)
sl@0
  2513
        if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE) return M3G_FALSE;
sl@0
  2514
sl@0
  2515
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2516
sl@0
  2517
    return M3G_TRUE;
sl@0
  2518
}
sl@0
  2519
sl@0
  2520
/*!
sl@0
  2521
 * \internal
sl@0
  2522
 * \brief Skips external reference
sl@0
  2523
 */
sl@0
  2524
static M3Gbool m3gLoadExternalReference(Loader *loader)
sl@0
  2525
{
sl@0
  2526
    M3Gubyte *data;
sl@0
  2527
    if (loader->sectionNum != 1 || !loader->hasReferences)
sl@0
  2528
        return M3G_FALSE;
sl@0
  2529
    data = m3gGetSectionDataPtr(loader, 1);
sl@0
  2530
    while(*data++) { /* Skip string */
sl@0
  2531
        if (m3gCheckSectionDataLength(loader, data, 1) == M3G_FALSE)
sl@0
  2532
            return M3G_FALSE;
sl@0
  2533
    }
sl@0
  2534
    m3gAdvanceSectionData(loader, data - m3gGetSectionDataPtr(loader, 0));
sl@0
  2535
    return M3G_TRUE;
sl@0
  2536
}
sl@0
  2537
sl@0
  2538
/*!
sl@0
  2539
 * \internal
sl@0
  2540
 * \brief Loads an object
sl@0
  2541
 */
sl@0
  2542
static LoaderState m3gLoadObject(Loader *loader)
sl@0
  2543
{
sl@0
  2544
    M3Gubyte *data = m3gGetSectionDataPtr(loader, loader->sectionBytesRequired);
sl@0
  2545
sl@0
  2546
    if (data == NULL) {
sl@0
  2547
        loader->localState = LOADSTATE_CHECKSUM;
sl@0
  2548
        return LOADSTATE_SECTION;
sl@0
  2549
    }
sl@0
  2550
sl@0
  2551
    m3gAdvanceSectionData(loader, loader->sectionBytesRequired);
sl@0
  2552
sl@0
  2553
    if (loader->localState == LOADSTATE_ENTER) {
sl@0
  2554
        M3Gbool status = M3G_TRUE;
sl@0
  2555
sl@0
  2556
        loader->objectType = *data++;
sl@0
  2557
        loader->loadedObject = NULL;
sl@0
  2558
        loader->localState = LOADSTATE_EXIT;
sl@0
  2559
        loader->sectionBytesRequired = m3gLoadInt(data);
sl@0
  2560
        data += 4;
sl@0
  2561
        if (loader->sectionNum == 0 && loader->objectType != 0) {
sl@0
  2562
            m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
  2563
            return LOADSTATE_ERROR;
sl@0
  2564
        }
sl@0
  2565
sl@0
  2566
        switch(loader->objectType) {
sl@0
  2567
        case 0: /* Header Object */
sl@0
  2568
            status = m3gLoadHeader(loader);
sl@0
  2569
            break;
sl@0
  2570
        case 1: /* AnimationController */
sl@0
  2571
            status = m3gLoadAnimationController(loader);
sl@0
  2572
            break;
sl@0
  2573
        case 2: /* AnimationTrack */
sl@0
  2574
            status = m3gLoadAnimationTrack(loader);
sl@0
  2575
            break;
sl@0
  2576
        case 3: /* Appearance */
sl@0
  2577
            status = m3gLoadAppearance(loader);
sl@0
  2578
            break;
sl@0
  2579
        case 4: /* Background */
sl@0
  2580
            status = m3gLoadBackground(loader);
sl@0
  2581
            break;
sl@0
  2582
        case 5: /* Camera */
sl@0
  2583
            status = m3gLoadCamera(loader);
sl@0
  2584
            break;
sl@0
  2585
        case 6: /* CompositingMode */
sl@0
  2586
            status = m3gLoadCompositingMode(loader);
sl@0
  2587
            break;
sl@0
  2588
        case 7: /* Fog */
sl@0
  2589
            status = m3gLoadFog(loader);
sl@0
  2590
            break;
sl@0
  2591
        case 8: /* PolygonMode */
sl@0
  2592
            status = m3gLoadPolygonMode(loader);
sl@0
  2593
            break;
sl@0
  2594
        case 9: /* Group */
sl@0
  2595
            status = m3gLoadGroup(loader);
sl@0
  2596
            break;
sl@0
  2597
        case 10: /* Image2D */
sl@0
  2598
            status = m3gLoadImage(loader);
sl@0
  2599
            break;
sl@0
  2600
        case 11: /* TriangleStripArray */
sl@0
  2601
            status = m3gLoadTsa(loader);
sl@0
  2602
            break;
sl@0
  2603
        case 12: /* Light */
sl@0
  2604
            status = m3gLoadLight(loader);
sl@0
  2605
            break;
sl@0
  2606
        case 13: /* Material */
sl@0
  2607
            status = m3gLoadMaterial(loader);
sl@0
  2608
            break;
sl@0
  2609
        case 14: /* Mesh */
sl@0
  2610
            status = m3gLoadMesh(loader);
sl@0
  2611
            break;
sl@0
  2612
        case 15: /* MorphingMesh */
sl@0
  2613
            status = m3gLoadMorphingMesh(loader);
sl@0
  2614
            break;
sl@0
  2615
        case 16: /* SkinnedMesh */
sl@0
  2616
            status = m3gLoadSkinnedMesh(loader);
sl@0
  2617
            break;
sl@0
  2618
        case 17: /* Texture2D */
sl@0
  2619
            status = m3gLoadTexture(loader);
sl@0
  2620
            break;
sl@0
  2621
        case 18: /* Sprite */
sl@0
  2622
            status = m3gLoadSprite(loader);
sl@0
  2623
            break;
sl@0
  2624
        case 19: /* KeyframeSequence */
sl@0
  2625
            status = m3gLoadKeyframeSequence(loader);
sl@0
  2626
            break;
sl@0
  2627
        case 20: /* VertexArray */
sl@0
  2628
            status = m3gLoadVertexArray(loader);
sl@0
  2629
            break;
sl@0
  2630
        case 21: /* VertexBuffer */
sl@0
  2631
            status = m3gLoadVertexBuffer(loader);
sl@0
  2632
            break;
sl@0
  2633
        case 22: /* World */
sl@0
  2634
            status = m3gLoadWorld(loader);
sl@0
  2635
            break;
sl@0
  2636
        case 255: /* External Reference */
sl@0
  2637
            status = m3gLoadExternalReference(loader);
sl@0
  2638
            break;
sl@0
  2639
        default:  /* 23 ... 254 Reserved for use in future versions of the file format */
sl@0
  2640
            status = M3G_FALSE;
sl@0
  2641
            break;
sl@0
  2642
        }
sl@0
  2643
sl@0
  2644
        /* Check if object loading caused an error */
sl@0
  2645
        if (m3gErrorRaised(M3G_INTERFACE(loader))) {
sl@0
  2646
            m3gDeleteObject(loader->loadedObject);
sl@0
  2647
            return LOADSTATE_ERROR;
sl@0
  2648
        }
sl@0
  2649
        if (!status || 
sl@0
  2650
            m3gGetSectionDataPtr(loader, 0) != data + loader->sectionBytesRequired) {
sl@0
  2651
            m3gDeleteObject(loader->loadedObject);
sl@0
  2652
            m3gRaiseError(M3G_INTERFACE(loader), M3G_IO_ERROR);
sl@0
  2653
            return LOADSTATE_ERROR;
sl@0
  2654
        }
sl@0
  2655
        loader->sectionBytesRequired = 0;
sl@0
  2656
        
sl@0
  2657
        /* Add object to loaded objects array */
sl@0
  2658
        if (loader->loadedObject != NULL) {
sl@0
  2659
            if (m3gArrayAppend(&loader->refArray, loader->loadedObject, M3G_INTERFACE(loader)) == -1) {
sl@0
  2660
                /* OOM */
sl@0
  2661
                m3gDeleteObject(loader->loadedObject);
sl@0
  2662
                return LOADSTATE_ERROR;
sl@0
  2663
            }
sl@0
  2664
            m3gAddRef(loader->loadedObject);
sl@0
  2665
        }
sl@0
  2666
    }
sl@0
  2667
    else {
sl@0
  2668
        loader->sectionBytesRequired = M3G_MIN_OBJECT_SIZE;
sl@0
  2669
        loader->localState = LOADSTATE_ENTER;
sl@0
  2670
    }
sl@0
  2671
sl@0
  2672
    return LOADSTATE_OBJECT;
sl@0
  2673
}
sl@0
  2674
sl@0
  2675
/*!
sl@0
  2676
 * \internal
sl@0
  2677
 * \brief Handles branching to different subroutines upon re-entry
sl@0
  2678
 *
sl@0
  2679
 * When sufficient data is available in internal buffers, this
sl@0
  2680
 * function gets called and directs execution into the coroutine
sl@0
  2681
 * matching the current global state.
sl@0
  2682
 */
sl@0
  2683
static LoaderState m3gLoaderMain(Loader *loader)
sl@0
  2684
{
sl@0
  2685
    M3G_VALIDATE_OBJECT(loader);
sl@0
  2686
    M3G_ASSERT(loader->bytesRequired > 0);
sl@0
  2687
    M3G_ASSERT(loader->bytesRequired <= loader->stream.bytesAvailable);
sl@0
  2688
sl@0
  2689
    switch (loader->state) {
sl@0
  2690
    case LOADSTATE_INITIAL:
sl@0
  2691
        loader->stream.totalBytes = 0;
sl@0
  2692
        loader->localState = LOADSTATE_ENTER;
sl@0
  2693
        loader->fileSize = 0x00ffffff;
sl@0
  2694
        loader->bytesRequired = sizeof(PNG_FILE_IDENTIFIER);
sl@0
  2695
        return LOADSTATE_IDENTIFIER;
sl@0
  2696
    case LOADSTATE_IDENTIFIER:
sl@0
  2697
        return m3gLoadIdentifier(loader);
sl@0
  2698
    case LOADSTATE_SECTION:
sl@0
  2699
        return m3gLoadSection(loader);
sl@0
  2700
    case LOADSTATE_OBJECT:
sl@0
  2701
        return m3gLoadObject(loader);
sl@0
  2702
    default:
sl@0
  2703
        loader->bytesRequired = 0;
sl@0
  2704
        return LOADSTATE_ERROR;
sl@0
  2705
    }
sl@0
  2706
}
sl@0
  2707
sl@0
  2708
/*!
sl@0
  2709
 * \brief Deletes all unreferenced objects
sl@0
  2710
 */
sl@0
  2711
static void m3gCleanupLoader(M3GLoader loader)
sl@0
  2712
{
sl@0
  2713
    M3Gint i, j, n;
sl@0
  2714
    PointerArray *refs;
sl@0
  2715
    M3Gbool referenced;
sl@0
  2716
    M3GObject obj;    
sl@0
  2717
sl@0
  2718
    refs = &loader->refArray;
sl@0
  2719
    n = m3gArraySize(refs);
sl@0
  2720
sl@0
  2721
    /* All unreferenced objects will be deleted, as their ref count becomes 0 */
sl@0
  2722
    for (i = 0; i < n; ++i) {
sl@0
  2723
        obj = m3gGetLoadedPtr(refs, i, &referenced);
sl@0
  2724
        m3gDeleteRef(obj);
sl@0
  2725
    }
sl@0
  2726
    m3gClearArray(&loader->refArray);
sl@0
  2727
sl@0
  2728
    n = m3gArraySize(&loader->userDataArray);
sl@0
  2729
    for (i = 0; i < n; ++i)
sl@0
  2730
    {
sl@0
  2731
        UserData *data = (UserData *)m3gGetArrayElement(&loader->userDataArray, i);
sl@0
  2732
        for (j = 0; j < data->numParams; ++j)
sl@0
  2733
            m3gFree(M3G_INTERFACE(loader), data->params[j]);
sl@0
  2734
        m3gFree(M3G_INTERFACE(loader), data->params);
sl@0
  2735
        m3gFree(M3G_INTERFACE(loader), data->paramLengths);
sl@0
  2736
        m3gFree(M3G_INTERFACE(loader), data->paramId);
sl@0
  2737
        m3gFree(M3G_INTERFACE(loader), data);
sl@0
  2738
    }
sl@0
  2739
    m3gClearArray(&loader->userDataArray);
sl@0
  2740
sl@0
  2741
    m3gFree(M3G_INTERFACE(loader), loader->allocatedSectionData);
sl@0
  2742
    loader->allocatedSectionData = NULL;
sl@0
  2743
}
sl@0
  2744
sl@0
  2745
/*!
sl@0
  2746
 * \internal
sl@0
  2747
 * \brief Resets the loader
sl@0
  2748
 */
sl@0
  2749
static void m3gResetLoader(Loader *loader)
sl@0
  2750
{
sl@0
  2751
    /* Reset loader state */
sl@0
  2752
    loader->state = LOADSTATE_INITIAL;
sl@0
  2753
    loader->bytesRequired = sizeof(PNG_FILE_IDENTIFIER);
sl@0
  2754
sl@0
  2755
    m3gCleanupLoader(loader);
sl@0
  2756
    m3gResetBufferedData(&loader->stream);
sl@0
  2757
}
sl@0
  2758
sl@0
  2759
/*----------------------------------------------------------------------
sl@0
  2760
 * Virtual function table
sl@0
  2761
 *--------------------------------------------------------------------*/
sl@0
  2762
sl@0
  2763
static const ObjectVFTable m3gvf_Loader = {
sl@0
  2764
    NULL, /* ApplyAnimation */
sl@0
  2765
    NULL, /* IsCompatible */
sl@0
  2766
    NULL, /* UpdateProperty */
sl@0
  2767
    NULL, /* DoGetReferences */
sl@0
  2768
    NULL, /* FindID */
sl@0
  2769
    NULL, /* Duplicate */
sl@0
  2770
    m3gDestroyLoader
sl@0
  2771
};
sl@0
  2772
    
sl@0
  2773
sl@0
  2774
/*----------------------------------------------------------------------
sl@0
  2775
 * Public interface
sl@0
  2776
 *--------------------------------------------------------------------*/
sl@0
  2777
sl@0
  2778
/*!
sl@0
  2779
 * \brief Creates a new loader instance
sl@0
  2780
 */
sl@0
  2781
M3G_API M3GLoader m3gCreateLoader(M3GInterface m3g)
sl@0
  2782
{
sl@0
  2783
    Loader *loader;
sl@0
  2784
    M3G_VALIDATE_INTERFACE(m3g);
sl@0
  2785
sl@0
  2786
    loader = m3gAllocZ(m3g, sizeof(*loader));
sl@0
  2787
    if (loader != NULL) {
sl@0
  2788
        m3gInitObject(&loader->object, m3g, M3G_CLASS_LOADER);
sl@0
  2789
        m3gInitArray(&loader->refArray);
sl@0
  2790
        m3gInitArray(&loader->userDataArray);
sl@0
  2791
        loader->bytesRequired = sizeof(PNG_FILE_IDENTIFIER);
sl@0
  2792
        loader->state = LOADSTATE_INITIAL;
sl@0
  2793
        loader->sectionNum = -1;
sl@0
  2794
    }
sl@0
  2795
    return loader;
sl@0
  2796
}
sl@0
  2797
sl@0
  2798
/*!
sl@0
  2799
 * \brief Import a set of objects into the reference table of a loader
sl@0
  2800
 *
sl@0
  2801
 * This is intended for passing in external references, decoded in
sl@0
  2802
 * Java
sl@0
  2803
 */
sl@0
  2804
M3G_API void m3gImportObjects(M3GLoader loader, M3Gint n, M3GObject *refs)
sl@0
  2805
{
sl@0
  2806
    int i;
sl@0
  2807
    M3G_VALIDATE_OBJECT(loader);
sl@0
  2808
sl@0
  2809
    if (loader->state == LOADSTATE_DONE)
sl@0
  2810
        m3gResetLoader(loader);
sl@0
  2811
    for (i = 0; i < n; ++i) {
sl@0
  2812
        /* For loop is interrupted in case of OOM */
sl@0
  2813
        if (m3gArrayAppend(&loader->refArray,
sl@0
  2814
                           refs[i],
sl@0
  2815
                           M3G_INTERFACE(loader)) == -1) {
sl@0
  2816
            break;
sl@0
  2817
        }
sl@0
  2818
        m3gAddRef(refs[i]);
sl@0
  2819
    }
sl@0
  2820
}
sl@0
  2821
sl@0
  2822
/*!
sl@0
  2823
 * \brief Return the complete reference table for this loader instance
sl@0
  2824
 *
sl@0
  2825
 * The reference table will contain the handle of each object that has
sl@0
  2826
 * been loaded so far.
sl@0
  2827
 *
sl@0
  2828
 * \param loader loader instance
sl@0
  2829
 * \param buffer destination buffer, or NULL to just get
sl@0
  2830
 *               the number of unreferenced objects
sl@0
  2831
 * \return number of unreferenced objects
sl@0
  2832
 */
sl@0
  2833
M3G_API M3Gint m3gGetLoadedObjects(M3GLoader loader, M3GObject *buffer)
sl@0
  2834
{
sl@0
  2835
    PointerArray *refs;
sl@0
  2836
    int i, n, unref = 0;
sl@0
  2837
    M3Gbool referenced;
sl@0
  2838
    M3GObject obj, *dst = buffer;
sl@0
  2839
    M3G_VALIDATE_OBJECT(loader);
sl@0
  2840
sl@0
  2841
    /* If error in decoding, reset and return 0 objects */
sl@0
  2842
    if (loader->state < LOADSTATE_INITIAL) {
sl@0
  2843
        return 0;
sl@0
  2844
    }
sl@0
  2845
sl@0
  2846
    refs = &loader->refArray;
sl@0
  2847
    n = m3gArraySize(refs);
sl@0
  2848
sl@0
  2849
    /* Scan unreferenced objects */
sl@0
  2850
    for (i = 0; i < n; ++i) {
sl@0
  2851
        obj = m3gGetLoadedPtr(refs, i, &referenced);
sl@0
  2852
        if (!referenced) {
sl@0
  2853
            unref++;
sl@0
  2854
            if (dst != NULL) {
sl@0
  2855
                *dst++ = obj;
sl@0
  2856
            }
sl@0
  2857
        }
sl@0
  2858
    }
sl@0
  2859
sl@0
  2860
    return unref;
sl@0
  2861
}
sl@0
  2862
sl@0
  2863
/*!
sl@0
  2864
 * \brief Submits data to the loader for processing
sl@0
  2865
 *
sl@0
  2866
 * Upon data input, the loader will either read more objects from the
sl@0
  2867
 * stream or buffer the data for processing later on. The return value
sl@0
  2868
 * will indicate how many bytes of data are required before the next
sl@0
  2869
 * data element can be loaded, but that data can still be submitted in
sl@0
  2870
 * smaller blocks.
sl@0
  2871
 *
sl@0
  2872
 * \param loader the loader instance
sl@0
  2873
 * \param bytes  the number of bytes in the data
sl@0
  2874
 * \param data   pointer to the data
sl@0
  2875
 *
sl@0
  2876
 * \return the number of bytes required to load the next data element,
sl@0
  2877
 * or zero to indicate that loading has finished
sl@0
  2878
 */
sl@0
  2879
sl@0
  2880
#ifdef M3G_ENABLE_PROFILING
sl@0
  2881
static M3Gsizei m3gDecodeDataInternal(M3GLoader loader,
sl@0
  2882
                               M3Gsizei bytes,
sl@0
  2883
                               const M3Gubyte *data);
sl@0
  2884
sl@0
  2885
M3G_API M3Gsizei m3gDecodeData(M3GLoader loader,
sl@0
  2886
                               M3Gsizei bytes,
sl@0
  2887
                               const M3Gubyte *data)
sl@0
  2888
{
sl@0
  2889
    M3Gsizei bytesReq;
sl@0
  2890
    M3G_BEGIN_PROFILE(M3G_INTERFACE(loader), M3G_PROFILE_LOADER_DECODE);
sl@0
  2891
    bytesReq = m3gDecodeDataInternal(loader, bytes, data);
sl@0
  2892
    M3G_END_PROFILE(M3G_INTERFACE(loader), M3G_PROFILE_LOADER_DECODE);
sl@0
  2893
    return bytesReq;
sl@0
  2894
}
sl@0
  2895
sl@0
  2896
static M3Gsizei m3gDecodeDataInternal(M3GLoader loader,
sl@0
  2897
                               M3Gsizei bytes,
sl@0
  2898
                               const M3Gubyte *data)
sl@0
  2899
sl@0
  2900
#else
sl@0
  2901
M3G_API M3Gsizei m3gDecodeData(M3GLoader loader,
sl@0
  2902
                               M3Gsizei bytes,
sl@0
  2903
                               const M3Gubyte *data)
sl@0
  2904
sl@0
  2905
#endif
sl@0
  2906
{
sl@0
  2907
    m3gErrorHandler *errorHandler;
sl@0
  2908
    Interface *m3g = M3G_INTERFACE(loader);
sl@0
  2909
sl@0
  2910
    M3G_VALIDATE_OBJECT(loader);
sl@0
  2911
    M3G_VALIDATE_INTERFACE(m3g);
sl@0
  2912
sl@0
  2913
    /* Check for errors */
sl@0
  2914
    if (bytes <= 0 || data == NULL) {
sl@0
  2915
        m3gRaiseError(m3g, M3G_INVALID_VALUE);
sl@0
  2916
        return 0;
sl@0
  2917
    }
sl@0
  2918
sl@0
  2919
    if (loader->state == LOADSTATE_DONE)
sl@0
  2920
        m3gResetLoader(loader);
sl@0
  2921
sl@0
  2922
    /* Submit data, then load until we run out of data again or are
sl@0
  2923
     * finished */
sl@0
  2924
sl@0
  2925
    if (!m3gBufferData(m3g, &loader->stream, bytes, data)) {
sl@0
  2926
        return 0;
sl@0
  2927
    }
sl@0
  2928
sl@0
  2929
    /* Disable error handler */
sl@0
  2930
    errorHandler = m3gSetErrorHandler(m3g, NULL);
sl@0
  2931
sl@0
  2932
    /* Continue loading if sufficient data has arrived */
sl@0
  2933
    while (loader->bytesRequired > 0
sl@0
  2934
           && loader->bytesRequired <= loader->stream.bytesAvailable) {
sl@0
  2935
        loader->state = m3gLoaderMain(loader);
sl@0
  2936
    }
sl@0
  2937
sl@0
  2938
    /* Restore error handler */
sl@0
  2939
    m3gSetErrorHandler(m3g, errorHandler); 
sl@0
  2940
sl@0
  2941
    /* Check if error was raised */
sl@0
  2942
    if (m3gErrorRaised(m3g) != M3G_NO_ERROR) {
sl@0
  2943
        /* Need to free all loaded objects */
sl@0
  2944
        m3gResetLoader(loader);
sl@0
  2945
sl@0
  2946
        /* Raise again with original error handler in place */
sl@0
  2947
        if (m3gErrorRaised(m3g) == M3G_OUT_OF_MEMORY)
sl@0
  2948
            m3gRaiseError(m3g, M3G_OUT_OF_MEMORY);
sl@0
  2949
        else
sl@0
  2950
            m3gRaiseError(m3g, M3G_IO_ERROR);
sl@0
  2951
        return 0;
sl@0
  2952
    }
sl@0
  2953
sl@0
  2954
    /* Return the number of bytes we need for loading to proceed
sl@0
  2955
     * further; clamp to zero in case we're exiting due to an error,
sl@0
  2956
     * or just have been fed excess data */
sl@0
  2957
    {
sl@0
  2958
        M3Gsizei bytesReq =
sl@0
  2959
            loader->bytesRequired - loader->stream.bytesAvailable;
sl@0
  2960
sl@0
  2961
        /* Check if whole file is done */
sl@0
  2962
        if (loader->stream.totalBytes >= loader->fileSize) {
sl@0
  2963
            loader->state = LOADSTATE_DONE;
sl@0
  2964
            bytesReq = 0;
sl@0
  2965
        }
sl@0
  2966
sl@0
  2967
        return (bytesReq >= 0) ? bytesReq : 0;
sl@0
  2968
    }
sl@0
  2969
}
sl@0
  2970
sl@0
  2971
/*!
sl@0
  2972
 * \brief Return all loaded objects with user parameters
sl@0
  2973
 *
sl@0
  2974
 * \param loader  the loader instance
sl@0
  2975
 * \param objects an array for objects with user parameters,
sl@0
  2976
 *                or null to return the number of objects
sl@0
  2977
 *
sl@0
  2978
 * \return Number of objects with user parameters
sl@0
  2979
 */
sl@0
  2980
M3G_API M3Gint m3gGetObjectsWithUserParameters(M3GLoader loader, M3GObject *objects) {
sl@0
  2981
    const Loader *ldr = (const Loader *) loader;
sl@0
  2982
    M3G_VALIDATE_OBJECT(ldr);
sl@0
  2983
    {
sl@0
  2984
        M3Gint i, n;
sl@0
  2985
        n = m3gArraySize(&ldr->userDataArray);
sl@0
  2986
    
sl@0
  2987
        if (objects != NULL)
sl@0
  2988
            for (i = 0; i < n; ++i)
sl@0
  2989
            {
sl@0
  2990
                const UserData *data = (const UserData *)m3gGetArrayElement(&ldr->userDataArray, i);
sl@0
  2991
                objects[i] = data->object;
sl@0
  2992
            }
sl@0
  2993
        return n;
sl@0
  2994
    }
sl@0
  2995
}
sl@0
  2996
sl@0
  2997
/*!
sl@0
  2998
 * \brief Return the number of user parameters loaded for an object
sl@0
  2999
 */
sl@0
  3000
M3G_API M3Gint m3gGetNumUserParameters(M3GLoader loader, M3Gint object)
sl@0
  3001
{
sl@0
  3002
    const Loader *ldr = (const Loader *) loader;
sl@0
  3003
    M3G_VALIDATE_OBJECT(ldr);
sl@0
  3004
    {
sl@0
  3005
        const UserData *data = (const UserData *)m3gGetArrayElement(&ldr->userDataArray, object);
sl@0
  3006
        return (data != NULL) ? data->numParams : 0;
sl@0
  3007
    }
sl@0
  3008
}
sl@0
  3009
sl@0
  3010
/*!
sl@0
  3011
 * \brief Set constraints for loading.
sl@0
  3012
 * \param triConstraint maximum triangle count
sl@0
  3013
 */
sl@0
  3014
M3G_API void m3gSetConstraints(M3GLoader loader, M3Gint triConstraint)
sl@0
  3015
{
sl@0
  3016
    M3G_VALIDATE_OBJECT(loader);
sl@0
  3017
    loader->triConstraint = triConstraint;
sl@0
  3018
}
sl@0
  3019
sl@0
  3020
/*!
sl@0
  3021
 * \brief Return the given user parameter for an object
sl@0
  3022
 *
sl@0
  3023
 * \param loader the loader instance
sl@0
  3024
 * \param object the object to query
sl@0
  3025
 * \param index  index of the string to query
sl@0
  3026
 * \param buffer buffer to copy the data into,
sl@0
  3027
 *               or NULL to just query the length
sl@0
  3028
 *
sl@0
  3029
 * \return id of the parameter, or the length of the string if data was NULL
sl@0
  3030
 */
sl@0
  3031
M3G_API M3Gsizei m3gGetUserParameter(M3GLoader loader,
sl@0
  3032
                                     M3Gint object,
sl@0
  3033
                                     M3Gint index,
sl@0
  3034
                                     M3Gbyte *buffer)
sl@0
  3035
{
sl@0
  3036
    const Loader *ldr = (const Loader *) loader;
sl@0
  3037
    M3G_VALIDATE_OBJECT(ldr);
sl@0
  3038
    {
sl@0
  3039
        const UserData *data = (const UserData *)m3gGetArrayElement(&ldr->userDataArray, object);
sl@0
  3040
        if (data != NULL && m3gInRange(index, 0, data->numParams - 1)) {
sl@0
  3041
            const char *src = (const char *)data->params[index];
sl@0
  3042
            M3Gsizei len = data->paramLengths[index];
sl@0
  3043
            if (buffer != NULL) {
sl@0
  3044
                m3gCopy(buffer, src, len);
sl@0
  3045
                return data->paramId[index];
sl@0
  3046
            }
sl@0
  3047
            else
sl@0
  3048
                return len;
sl@0
  3049
        }
sl@0
  3050
        else
sl@0
  3051
            return 0;
sl@0
  3052
    }
sl@0
  3053
}
sl@0
  3054
sl@0
  3055
#undef ANY_NODE_CLASS
sl@0
  3056