Beispiel #1
0
QList<int> KCharSelectData::sectionContents(int section)
{
    if(!openDataFile()) {
        return QList<int>();
    }


    const char* data = charselect->dataFile;
    const uint32_t offsetBegin = FromLittleEndian32(data+28);
    const uint32_t offsetEnd = FromLittleEndian32(data+32);

    int max = ((offsetEnd - offsetBegin) / 4) - 1;

    QList<int> res;

    if(section > max)
        return res;

    for(int i = 0; i <= max; i++) {
        const uint16_t currSection = FromLittleEndian16(data + offsetBegin + i*4);
        if(currSection == section) {
            res.append( FromLittleEndian16(data + offsetBegin + i*4 + 2) );
        }
    }

    return res;
}
Beispiel #2
0
QChar::Category KCharSelectData::category(CharSelectData* charselect, uint16_t unicode)
{
    if(!openDataFile()) {
        return c.category();
    }

    uint16_t unicode = c.unicode();


    const char* data = charselect->dataFile;
    const uint32_t offsetBegin = FromLittleEndian32(data+4);
    const uint32_t offsetEnd = FromLittleEndian32(data+8);

    int min = 0;
    int mid;
    int max = ((offsetEnd - offsetBegin) / 6) - 1;
    QString s;

    while (max >= min) {
        mid = (min + max) / 2;
        const uint16_t midUnicode = FromLittleEndian16(data + offsetBegin + mid*6);
        if (unicode > midUnicode)
            min = mid + 1;
        else if (unicode < midUnicode)
            max = mid - 1;
        else {
            uint32_t offset = FromLittleEndian32(data + offsetBegin + mid*6 + 2);
            const uint8_t categoryCode = * (uint8_t *)(data + offset);
            return QChar::Category(categoryCode);
        }
    }

    return c.category();
}
Beispiel #3
0
QList<QChar> KCharSelectData::blockContents(int block)
{
    if(!openDataFile()) {
        return QList<QChar>();
    }


    const char* data = charselect->dataFile;
    const uint32_t offsetBegin = FromLittleEndian32(data+20);
    const uint32_t offsetEnd = FromLittleEndian32(data+24);

    int max = ((offsetEnd - offsetBegin) / 4) - 1;

    QList<QChar> res;

    if(block > max)
        return res;

    uint16_t unicodeBegin = FromLittleEndian16(data + offsetBegin + block*4);
    uint16_t unicodeEnd = FromLittleEndian16(data + offsetBegin + block*4 + 2);

    while(unicodeBegin < unicodeEnd) {
        res.append(unicodeBegin);
        unicodeBegin++;
    }
    res.append(unicodeBegin); // Be carefull when unicodeEnd==0xffff

    return res;
}
Beispiel #4
0
bool FileUpload::TryDecodeRecvBuff(ServerResponse & SvrRep)
{
    if (mRecvSize < 10) // 2 + 4 + 4
    {
        return false;
    }
    
    // 标记
    short Flag = FromLittleEndian16(&mRecvBuff[0]);     // +2
    if (Flag != 0x0D10)
    {
        CloseSocket(eClient_STATE_DISCONNECT, true);
        return false;
    }
    
    // 校验码
    uint32_t Value = FromLittleEndian32(&mRecvBuff[2]); // +4
    
    // 流长度
    int StreamSize = FromBigEndian32(&mRecvBuff[6]);    // +4
    if (StreamSize <= 0 || StreamSize < 271) // < SizeOf(TFileHead)
    {
        CloseSocket(eClient_STATE_DISCONNECT, true);
        return false;
    }
    
    // 未收完
    if (StreamSize > mRecvSize - 10) // 2 + 4 + 4
    {
        return false;
    }
    
    // 校验之
    if (VerifyData(&mRecvBuff[10], StreamSize) != Value)
    {
        CloseSocket(eClient_STATE_DISCONNECT, true);
        return false;
    }
    
    //
    SvrRep.mFlag = FromLittleEndian16(&mRecvBuff[10]);
    SvrRep.mUserId = FromLittleEndian32(&mRecvBuff[14]);
    SvrRep.mFileType = static_cast<UploadFileType>(FromLittleEndian32(&mRecvBuff[18]));
    SvrRep.mSrvFileName = reinterpret_cast<char*>(&mRecvBuff[26]);
    
    //
    memmove(&mRecvBuff[0], &mRecvBuff[10 + StreamSize], mRecvSize - 10 - StreamSize);
    mRecvSize -= (10 + StreamSize);
    
    return true;
}
Beispiel #5
0
int KCharSelectData::sectionIndex(int block)
{
    if(!openDataFile()) {
        return 0;
    }


    const char* data = charselect->dataFile;
    const uint32_t offsetBegin = FromLittleEndian32(data+28);
    const uint32_t offsetEnd = FromLittleEndian32(data+32);

    int max = ((offsetEnd - offsetBegin) / 4) - 1;

    for(int i = 0; i <= max; i++) {
        if( FromLittleEndian16(data + offsetBegin + i*4 + 2) == block) {
            return FromLittleEndian16(data + offsetBegin + i*4);
        }
    }

    return 0;
}
Beispiel #6
0
UT_array* KCharSelectData::sectionList()
{
    if(!openDataFile()) {
        return fcitx_utils_new_string_list();
    }


    const char* data = charselect->dataFile;
    const uint32_t stringBegin = FromLittleEndian32(data+24);
    const uint32_t stringEnd = FromLittleEndian32(data+28);

    const char* data = dataFile.constData();
    UT_array* list;
    uint32_t i = stringBegin;
    while(i < stringEnd) {
        list.append(i18nc("KCharSelect section name", data + i));
        i += strlen(data + i) + 1;
    }

    return list;
}
Beispiel #7
0
QString KCharSelectData::sectionName(int index)
{
    if(!openDataFile()) {
        return QString();
    }


    const char* data = charselect->dataFile;
    const uint32_t stringBegin = FromLittleEndian32(data+24);
    const uint32_t stringEnd = FromLittleEndian32(data+28);

    uint32_t i = stringBegin;
    int currIndex = 0;

    const char* data = dataFile.constData();
    while(i < stringEnd && currIndex < index) {
        i += strlen(data + i) + 1;
        currIndex++;
    }

    return i18nc("KCharselect unicode section name", data + i);
}
Beispiel #8
0
int KCharSelectData::blockIndex(CharSelectData* charselect, uint16_t unicode)
{
    if(!openDataFile()) {
        return 0;
    }


    const char* data = charselect->dataFile;
    const uint32_t offsetBegin = FromLittleEndian32(data+20);
    const uint32_t offsetEnd = FromLittleEndian32(data+24);
    const uint16_t unicode = c.unicode();

    int max = ((offsetEnd - offsetBegin) / 4) - 1;

    int i = 0;

    while (unicode > FromLittleEndian16(data + offsetBegin + i*4 + 2) && i < max) {
        i++;
    }

    return i;
}
Beispiel #9
0
uint32_t CharSelectDataGetDetailIndex(CharSelectData* charselect, uint32_t unicode)
{
    const char* data = charselect->dataFile;
    // Convert from little-endian, so that this code works on PPC too.
    // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=482286
    const uint32_t offsetBegin = FromLittleEndian32(data+12);
    const uint32_t offsetEnd = FromLittleEndian32(data+16);

    int min = 0;
    int mid;
    int max = ((offsetEnd - offsetBegin) / 29) - 1;

    static uint32_t most_recent_searched;
    static uint32_t most_recent_result;


    if (unicode == most_recent_searched)
        return most_recent_result;

    most_recent_searched = unicode;

    while (max >= min) {
        mid = (min + max) / 2;
        const uint32_t midUnicode = FromLittleEndian16(data + offsetBegin + mid*29);
        if (unicode > midUnicode)
            min = mid + 1;
        else if (unicode < midUnicode)
            max = mid - 1;
        else {
            most_recent_result = offsetBegin + mid*29;

            return most_recent_result;
        }
    }

    most_recent_result = 0;
    return 0;
}
Beispiel #10
0
UT_array* CharSelectDataUnihanInfo(CharSelectData* charselect, uint32_t unicode)
{
    UT_array* res = fcitx_utils_new_string_list();

    const char* data = charselect->dataFile;
    const uint32_t offsetBegin = FromLittleEndian32(data+36);
    const uint32_t offsetEnd = charselect->size;

    int min = 0;
    int mid;
    int max = ((offsetEnd - offsetBegin) / 32) - 1;

    while (max >= min) {
        mid = (min + max) / 2;
        const uint32_t midUnicode = FromLittleEndian16(data + offsetBegin + mid*32);
        if (unicode > midUnicode)
            min = mid + 1;
        else if (unicode < midUnicode)
            max = mid - 1;
        else {
            int i;
            for(i = 0; i < 7; i++) {
                uint32_t offset = FromLittleEndian32(data + offsetBegin + mid*32 + 4 + i*4);
                const char* empty = "";
                if(offset != 0) {
                    const char* r = data + offset;
                    utarray_push_back(res, &r);
                } else {
                    utarray_push_back(res, &empty);
                }
            }
            return res;
        }
    }

    return res;
}
Beispiel #11
0
char* CharSelectDataName(CharSelectData* charselect, uint32_t unicode)
{
    char* result = NULL;
    do {
        if ((unicode >= 0x3400 && unicode <= 0x4DB5)
                || (unicode >= 0x4e00 && unicode <= 0x9fa5)
                || (unicode >= 0x20000 && unicode <= 0x2A6D6)) {
            asprintf(&result, "CJK UNIFIED IDEOGRAPH-%x", unicode);
        } else if (unicode >= 0xac00 && unicode <= 0xd7af) {
            /* compute hangul syllable name as per UAX #15 */
            int SIndex = unicode - SBase;
            int LIndex, VIndex, TIndex;

            if (SIndex < 0 || SIndex >= SCount) {
                result = strdup("");
                break;
            }

            LIndex = SIndex / NCount;
            VIndex = (SIndex % NCount) / TCount;
            TIndex = SIndex % TCount;

            fcitx_utils_alloc_cat_str(result, "HANGUL SYLLABLE ",
                                      JAMO_L_TABLE[LIndex],
                                      JAMO_V_TABLE[VIndex],
                                      JAMO_T_TABLE[TIndex]);
        } else if (unicode >= 0xD800 && unicode <= 0xDB7F)
            result = strdup(_("<Non Private Use High Surrogate>"));
        else if (unicode >= 0xDB80 && unicode <= 0xDBFF)
            result = strdup(_("<Private Use High Surrogate>"));
        else if (unicode >= 0xDC00 && unicode <= 0xDFFF)
            result = strdup(_("<Low Surrogate>"));
        else if (unicode >= 0xE000 && unicode <= 0xF8FF)
            result = strdup(_("<Private Use>"));
        else {

        const char* data = charselect->dataFile;
            const uint32_t offsetBegin = FromLittleEndian32(data+4);
            const uint32_t offsetEnd = FromLittleEndian32(data+8);

            int min = 0;
            int mid;
            int max = ((offsetEnd - offsetBegin) / 8) - 1;

            while (max >= min) {
                mid = (min + max) / 2;
                const uint32_t midUnicode = FromLittleEndian32(data + offsetBegin + mid*8);
                if (unicode > midUnicode)
                    min = mid + 1;
                else if (unicode < midUnicode)
                    max = mid - 1;
                else {
                    uint32_t offset = FromLittleEndian32(data + offsetBegin + mid*8 + 4);
                    result = strdup(charselect->dataFile + offset + 1);
                    break;
                }
            }
        }
    } while(0);

    if (!result) {
        result = strdup(_("<not assigned>"));
    }
    return result;
}
Beispiel #12
0
DFFMesh* DFFLoader::loadMesh(RWSection* clump)
{
	DFFMesh* mesh = new DFFMesh;

	// **************************************************
	// *				LOAD THE FRAMES					*
	// **************************************************

	RWSection* frameList = clump->getChild(RW_SECTION_FRAMELIST);

	RWSection* frameListStruct = frameList->getChild(RW_SECTION_STRUCT);
	uint8_t* frameListStructData = frameListStruct->getData();

	uint32_t numFrames = FromLittleEndian32(*((uint32_t*) frameListStructData));

	// In DFF, the frames are stored and indexed as a flat data structure, so at first we keep them flat.
	IndexedDFFFrame* indexedFrames = new IndexedDFFFrame[numFrames];

	DFFFrame** boneFrames = NULL;

	map<int32_t, DFFBone*> bonesByIndex;
	map<int32_t, DFFBone*> bonesByNum;

	for (uint32_t i = 0 ; i < numFrames ; i++) {
		IndexedDFFFrame& indexedFrame = indexedFrames[i];
		indexedFrame.boneID = -1;
		uint8_t* offData = frameListStructData + 4 + i*56;

#ifdef GTAFORMATS_LITTLE_ENDIAN
		Matrix4* mm = new Matrix4 (
				*((float*) (offData)),		*((float*) (offData + 4)),	*((float*) (offData + 8)),	0.0f,
				*((float*) (offData + 12)),	*((float*) (offData + 16)),	*((float*) (offData + 20)),	0.0f,
				*((float*) (offData + 24)),	*((float*) (offData + 28)),	*((float*) (offData + 32)),	0.0f,
				*((float*) (offData + 36)),	*((float*) (offData + 40)),	*((float*) (offData + 44)),	1.0f
		);
#else
		Matrix4* mm = new Matrix4 (
				FromLittleEndianF32(*((float*) (offData))),
				FromLittleEndianF32(*((float*) (offData + 4))),
				FromLittleEndianF32(*((float*) (offData + 8))),
				0.0f,

				FromLittleEndianF32(*((float*) (offData + 12))),
				FromLittleEndianF32(*((float*) (offData + 16))),
				FromLittleEndianF32(*((float*) (offData + 20))),
				0.0f,

				FromLittleEndianF32(*((float*) (offData + 24))),
				FromLittleEndianF32(*((float*) (offData + 28))),
				FromLittleEndianF32(*((float*) (offData + 32))),
				0.0f,

				FromLittleEndianF32(*((float*) (offData + 36))),
				FromLittleEndianF32(*((float*) (offData + 40))),
				FromLittleEndianF32(*((float*) (offData + 44))),
				1.0f
		);
#endif

/*#ifdef GTAFORMATS_LITTLE_ENDIAN
		Matrix3* rotMatrix = new Matrix3((float*) offData);
		Vector3* posVector = new Vector3((float*) (offData + 9*4));
#else
		float* rData = (float*) offData;
		float* pData = (float*) (offData + 9*4);
		Matrix3* rotMatrix = new Matrix3(
				FromLittleEndianF32(rData[0]), FromLittleEndianF32(rData[1]), FromLittleEndianF32(rData[2]),
				FromLittleEndianF32(rData[3]), FromLittleEndianF32(rData[4]), FromLittleEndianF32(rData[5]),
				FromLittleEndianF32(rData[6]), FromLittleEndianF32(rData[7]), FromLittleEndianF32(rData[8])
				);
		Vector3* posVector = new Vector3(FromLittleEndianF32(pData[0]), FromLittleEndianF32(pData[1]),
				FromLittleEndianF32(pData[2]));
#endif*/

		indexedFrame.parentIdx = FromLittleEndian32(*((uint32_t*) (offData + 12*4)));
		DFFFrame* frame = new DFFFrame(mm);
		frame->flags = FromLittleEndian32(*((uint32_t*) (offData + 13*4)));
		indexedFrame.frame = frame;
	}

	// Now we read the frame names.
	uint32_t i = 0;
	RWSection::ChildIterator it = frameList->getChildBegin();
	while ((it = frameList->nextChild(RW_SECTION_EXTENSION, it))  !=  frameList->getChildEnd()) {
		if (i >= numFrames) {
			throw DFFException("Too many frame list extensions", __FILE__, __LINE__);
		}

		DFFFrame* frame = indexedFrames[i].frame;

		RWSection* frameListExt = *it;

		RWSection* frameSect = frameListExt->getChild(RW_SECTION_FRAME);

		if (frameSect) {
			char* frameName = new char[frameSect->getSize() + 1];
			frameName[frameSect->getSize()] = '\0';
			memcpy(frameName, frameSect->getData(), frameSect->getSize());
			frame->setName(CString::from(frameName).trim());
		}

		RWSection* hAnimPlg = frameListExt->getChild(RW_SECTION_HANIM_PLG);

		if (hAnimPlg) {
			// We have a hierarchical animation plugin (HANIM_PLG) section. Such a section can exist for each frame, but
			// does not have to. If it is present for a frame, this means that the frame acts as a bone for an animation,
			// and the frame gets assigned a bone ID. HANIM_PLG also doubles as a section that describes the types and
			// properties of bones when the boneCount property is not zero. This code currently assumes that all this
			// additional bone information is contained completely in a single HANIM_PLG section, and not spread across
			// multiple (which seems to be the case in all of the original DFF meshes).

			uint8_t* adata = hAnimPlg->getData();

			// The bone ID of this specific frame.
			int32_t boneID = FromLittleEndian32(*((int32_t*) (adata+4)));

			// Number of bones for which additional properties are stored. See above.
			int32_t boneCount = FromLittleEndian32(*((int32_t*) (adata+8)));

			adata += 12;

			indexedFrames[i].boneID = boneID;

			if (boneCount != 0) {
				// We have additional bone data in this section.

				adata += 8;

				mesh->boneCount = boneCount;

				boneFrames = new DFFFrame*[boneCount];

				// We assume that all bone detail data is contained in a single section. If we have multiple sections
				// containing bone detail, we keep the data of the more recent section.
				for (map<int32_t, DFFBone*>::iterator it = bonesByNum.begin() ; it != bonesByNum.end() ; it++)
					delete it->second;

				bonesByIndex.clear();
				bonesByNum.clear();

				for (int32_t i = 0 ; i < boneCount ; i++) {
					// NOTE: The 'type' value might be the node topology flags. I don't know this for sure
					// and we don't seem to need this either way, but it might be like this:
					// 0 = NONE
					// 1 = POP
					// 2 = PUSH
					// 3 = PUSH/POP

					DFFBone* bone = new DFFBone;
					memcpy(bone, adata, 12);


#ifndef GTAFORMATS_LITTLE_ENDIAN
					bone->index = FromLittleEndian32(bone->index);
					bone->num = FromLittleEndian32(bone->num);
					bone->type = FromLittleEndian32(bone->type);
#endif

					bonesByIndex[bone->getIndex()] = bone;
					bonesByNum[bone->getNumber()] = bone;

					adata += 12;
				}
			}
		}

		i++;
		it++;
	}

	// Associate frames with bones
	if (bonesByIndex.size() != 0) {
		uint32_t boneIdx = 0;
		for (uint32_t i = 0 ; i < numFrames ; i++) {
			if (indexedFrames[i].boneID != -1) {
				indexedFrames[i].frame->bone = bonesByIndex[indexedFrames[i].boneID];
				boneFrames[boneIdx++] = indexedFrames[i].frame;
			}
		}
	}

	// And now we will actually build the frame hierarchy.
	// We still keep the flat structure (indexedFrames), because we will need this later when we read the
	// ATOMIC sections, which link frames and geometries.

	DFFFrame* rootFrame = mesh->getRootFrame();

	for (i = 0 ; i < numFrames ; i++) {
		IndexedDFFFrame& indexedFrame = indexedFrames[i];

		if (indexedFrame.parentIdx != -1) {
			indexedFrames[indexedFrame.parentIdx].frame->addChild(indexedFrame.frame);
		} else {
			rootFrame->addChild(indexedFrame.frame);
		}
	}



	// ******************************************************
	// *				LOAD THE GEOMETRIES					*
	// ******************************************************

	RWSection* geomList = clump->getChild(RW_SECTION_GEOMETRYLIST);
	RWSection* geomListStruct = geomList->getChild(RW_SECTION_STRUCT);

	uint32_t numGeoms = FromLittleEndian32(*((uint32_t*) geomListStruct->getData()));

	RWSection::ChildIterator geomIt = geomList->getChildBegin();
	while ((geomIt = geomList->nextChild(RW_SECTION_GEOMETRY, geomIt))  !=  geomList->getChildEnd()) {
		RWSection* geomSect = *geomIt;


		// ********** LOAD THE GEOMETRY STRUCT **********

		// This is most notably the vertex data.

		RWSection* geomStruct = geomSect->getChild(RW_SECTION_STRUCT);
		uint8_t* geomData = geomStruct->getData();

		uint16_t flags = FromLittleEndian16(*((uint16_t*) geomData));
		uint8_t uvSetCount = *(geomData+2);
		// uint8_t unknown = *(geomData+3);
		uint32_t triCount = FromLittleEndian32(*((uint32_t*) (geomData+4)));
		uint32_t vertexCount = FromLittleEndian32(*((uint32_t*) (geomData+8)));
		uint32_t frameCount = FromLittleEndian32(*((uint32_t*) (geomData+12)));

		if ((flags & GEOMETRY_FLAG_TEXCOORDS)  !=  0  &&  (flags & GEOMETRY_FLAG_MULTIPLEUVSETS) == 0) {
			// At least some meshes in GTA 3 have the UV set count set to 0 although GEOMETRY_FLAG_TEXCOORDS
			// is set.
			uvSetCount = 1;
		}

		// Check if the flags are correct.
		/*assert (
					(flags & GEOMETRY_FLAG_MULTIPLEUVSETS) != 0
				||	(
							(flags & GEOMETRY_FLAG_TEXCOORDS) != 0
						&&	uvSetCount == 1
					)
				||	uvSetCount == 0
		);*/

		float ambientColor = 0.0f;
		float diffuseColor = 0.0f;
		float specularColor = 0.0f;

		uint8_t* vertexColors = NULL;
		float* uvCoords = NULL;
		float* vertices = NULL;
		float* normals = NULL;

		geomData += 16;

		if (geomStruct->getVersion() < RW_VERSION_GTAVC_2) {
			ambientColor = FromLittleEndianF32(*((float*) geomData));
			diffuseColor = FromLittleEndianF32(*((float*) (geomData + 4)));
			specularColor = FromLittleEndianF32(*((float*) (geomData + 8)));
			geomData += 12;
		}

		if ((flags & GEOMETRY_FLAG_COLORS)  !=  0) {
			vertexColors = new uint8_t[vertexCount*4];
			memcpy(vertexColors, geomData, vertexCount*4);
			geomData += vertexCount*4;
		}

		if ((flags & (GEOMETRY_FLAG_TEXCOORDS | GEOMETRY_FLAG_MULTIPLEUVSETS))  !=  0) {
			size_t size = uvSetCount * vertexCount * 2;
			uvCoords = new float[size];
			memcpy(uvCoords, geomData, size*4);

#ifndef GTAFORMATS_LITTLE_ENDIAN
			for (size_t i = 0 ; i < size ; i++) {
				uvCoords[i] = SwapEndiannessF32(uvCoords[i]);
			}
#endif

			geomData += size*4;
		}

		// Skip the indices, we'll use the ones from the material split section
		geomData += triCount * 8;

		DFFBoundingSphere* bounds = new DFFBoundingSphere;
		memcpy(bounds, geomData, 4*4);

#ifndef GTAFORMATS_LITTLE_ENDIAN
		bounds->x = SwapEndiannessF32(bounds->x);
		bounds->y = SwapEndiannessF32(bounds->y);
		bounds->z = SwapEndiannessF32(bounds->z);
		bounds->radius = SwapEndiannessF32(bounds->radius);
#endif

		// uint32_t hasPositions = FromLittleEndian32(*((uint32_t*) (geomData+16)));
		// uint32_t hasNormals = FromLittleEndian32(*((uint32_t*) (geomData+20)));
		geomData += 6*4;

		// We ignore the setting of GEOMETRY_FLAG_POSITIONS. There are some meshes in GTA 3 where this flag
		// is not set but which have vertex positions nonetheless. The engine seems to ignore the flag.
		size_t size = vertexCount*3;
		vertices = new float[size];
		memcpy(vertices, geomData, size*4);


#ifndef GTAFORMATS_LITTLE_ENDIAN
		for (size_t i = 0 ; i < size ; i++) {
			vertices[i] = SwapEndiannessF32(vertices[i]);
		}
#endif

		geomData += size*4;

		if ((flags & GEOMETRY_FLAG_NORMALS)  !=  0) {
			size = vertexCount*3;
			normals = new float[size];
			memcpy(normals, geomData, size*4);

#ifndef GTAFORMATS_LITTLE_ENDIAN
			for (size_t i = 0 ; i < size ; i++) {
				normals[i] = SwapEndiannessF32(normals[i]);
			}
#endif

			geomData += size*4;
		}

		DFFGeometry* geom = new DFFGeometry(vertexCount, vertices, normals, uvCoords, uvSetCount,
				vertexColors);
		geom->setFlags(flags);
		geom->setFrameCount(frameCount);
		geom->setBounds(bounds);

		if (geomStruct->getVersion() < RW_VERSION_GTAVC_2) {
			geom->setAmbientLight(ambientColor);
			geom->setDiffuseLight(diffuseColor);
			geom->setSpecularLight(specularColor);
		}


		// ********** LOAD THE MATERIALS **********

		RWSection* materialList = geomSect->getChild(RW_SECTION_MATERIALLIST);

		RWSection* materialListStruct = materialList->getChild(RW_SECTION_STRUCT);

		uint32_t numMaterials = FromLittleEndian32(*((uint32_t*) materialListStruct->getData()));

		RWSection::ChildIterator matIt = materialList->getChildBegin();
		while ((matIt = materialList->nextChild(RW_SECTION_MATERIAL, matIt))
				!=  materialList->getChildEnd())
		{
			RWSection* matSect = *matIt;

			RWSection* matStruct = matSect->getChild(RW_SECTION_STRUCT);
			uint8_t* matData = matStruct->getData();

			DFFMaterial* mat = new DFFMaterial;

			// uint32_t unknown = *((uint32_t*) matData);
			memcpy(mat->color, matData+4, 4);
			// uint32_t unknown = *((uint32_t*) (matData+8));
			uint32_t texCount = FromLittleEndian32(*((uint32_t*) (matData+12)));

			// Load the textures

			RWSection::ChildIterator texIt = matSect->getChildBegin();
			while ((texIt = matSect->nextChild(RW_SECTION_TEXTURE, texIt))  !=  matSect->getChildEnd()) {
				RWSection* texSect = *texIt;

				RWSection* texStruct = texSect->getChild(RW_SECTION_STRUCT);
				uint16_t filterFlags = FromLittleEndian16(*((uint16_t*) texStruct->getData()));

				RWSection::ChildIterator it = texSect->getChildBegin();

				it = texSect->nextChild(RW_SECTION_STRING, it);
				RWSection* diffNameSect = *it;
				char* diffuseName = new char[diffNameSect->getSize()];
				memcpy(diffuseName, diffNameSect->getData(), diffNameSect->getSize());
				it++;

				it = texSect->nextChild(RW_SECTION_STRING, it);
				RWSection* alphaNameSect = *it;
				char* alphaName = new char[alphaNameSect->getSize()];
				memcpy(alphaName, alphaNameSect->getData(), alphaNameSect->getSize());

				DFFTexture* tex = new DFFTexture(CString::from(diffuseName), CString::from(alphaName),
						filterFlags);

				mat->addTexture(tex);

				texIt++;
			}

			geom->addMaterial(mat);

			matIt++;
		}


		RWSection* geomExt = geomSect->getChild(RW_SECTION_EXTENSION);

		// ********** Load the material split data **********

		RWSection* materialSplit = geomExt->getChild(RW_SECTION_MATERIALSPLIT);
		uint8_t* msData = materialSplit->getData();

		// uint32_t faceType = FromLittleEndian32(*((uint32_t*) msData));
		uint32_t splitCount = FromLittleEndian32(*((uint32_t*) (msData+4)));
		// uint32_t numFaces = FromLittleEndian32(*((uint32_t*) (msData+8)));
		msData += 12;

		for (uint32_t j = 0 ; j < splitCount ; j++) {
			uint32_t idxCount = FromLittleEndian32(*((uint32_t*) msData));
			//printf("Split %u: %u indices", j, idxCount);
			uint32_t materialIdx = FromLittleEndian32(*((uint32_t*) (msData+4)));
			msData += 8;

			uint32_t* indices = new uint32_t[idxCount];
			memcpy(indices, msData, idxCount*4);

#ifndef GTAFORMATS_LITTLE_ENDIAN
			for (size_t k = 0 ; k < idxCount ; k++) {
				indices[k] = SwapEndianness32(indices[k]);
			}
#endif

			msData += idxCount*4;

			DFFGeometryPart* part = new DFFGeometryPart(idxCount, indices);

			geom->addPart(part);
			part->setMaterial(geom->getMaterial(materialIdx));
		}


		// ********** Load the vertex skinning data **********

		RWSection* skin = geomExt->getChild(RW_SECTION_SKIN_PLG);

		if (skin) {
			uint8_t* sData = skin->getData();

			uint8_t boneCount = *sData;
			uint8_t spCount = *(sData + 1);
			//uint8_t unknown1 = *(sData + 2);
			//uint8_t unknown2 = *(sData + 3);

			sData += 4;
			sData += spCount; // spCount times unknown3

			geom->boneIndices = new uint8_t[vertexCount*4];
			memcpy(geom->boneIndices, sData, vertexCount*4);
			sData += vertexCount*4;

			geom->boneWeights = new float[vertexCount*4];
			memcpy(geom->boneWeights, sData, vertexCount*4*sizeof(float));
			sData += vertexCount*4*sizeof(float);

#ifndef GTAFORMATS_LITTLE_ENDIAN
			for (size_t j = 0 ; j < vertexCount*4 ; j++) {
				geom->boneWeights[j] = SwapEndiannessF32(geom->boneWeights[j]);
			}
#endif

			// Inverse Bone Matrices and possibly (version dependent) some unknown data follows...

			geom->boneCount = boneCount;
			//geom->inverseBoneMatrices = new Matrix4*[boneCount];

			for (uint8_t i = 0 ; i < boneCount ; i++) {
				if (skin->getVersion() != RW_VERSION_GTASA) {
					sData += 4; // constDEAD
				}

				Matrix4 ibm;
				memcpy(&ibm, sData, 16*sizeof(float));
				sData += 16*sizeof(float);

				const float* adata = ibm.toArray();

				ibm = Matrix4 (
						adata[0], adata[1], adata[2], 0.0f,
						adata[4], adata[5], adata[6], 0.0f,
						adata[8], adata[9], adata[10], 0.0f,
						adata[12], adata[13], adata[14], 1.0f
				);

				DFFBone* bone = bonesByNum[i];
				bone->ibm = ibm;

				/*const float* a = geom->inverseBoneMatrices[i]->toArray();
				printf("Loading IBM #%u:\n%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f\n%f\t%f\t%f\t%f\n\n", i,
					a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);*/
			}
		}


		mesh->addGeometry(geom);

		geomIt++;
	}


	// **********************************************************************
	// *				LOAD THE GEOMETRY <-> FRAME LINKS					*
	// **********************************************************************

	RWSection::ChildIterator atomicIt = clump->getChildBegin();
	while ((atomicIt = clump->nextChild(RW_SECTION_ATOMIC, atomicIt))  !=  clump->getChildEnd()) {
		RWSection* atomic = *atomicIt;

		RWSection* atomicStruct = atomic->getChild(RW_SECTION_STRUCT);
		uint8_t* asData = atomicStruct->getData();

		uint32_t frameIdx = FromLittleEndian32(*((uint32_t*) asData));
		uint32_t geomIdx = FromLittleEndian32(*((uint32_t*) (asData+4)));

		mesh->getGeometry(geomIdx)->setAssociatedFrame(indexedFrames[frameIdx].frame);

		atomicIt++;
	}

	mesh->integratedCOLModel = NULL;

	atomicIt = clump->getChildBegin();
	while (!mesh->integratedCOLModel  &&  (atomicIt = clump->nextChild(RW_SECTION_EXTENSION, atomicIt))  !=  clump->getChildEnd()) {
		RWSection* ext = *atomicIt;

		RWSection* colSect = ext->getChild(RW_SECTION_COLLISION_MODEL);

		if (colSect) {
			uint8_t* data = colSect->getData();

			// TODO This is ridiculous. std::string makes a copy of the data, which is absolutely not needed...
			std::string tmp((char*) data, colSect->getSize());
			std::stringstream in(tmp);

			COLLoader col;
			COLModel* model = col.loadModel(&in);

			if (model) {
				mesh->integratedCOLModel = model;
			}
		}

		atomicIt++;
	}


	// Delete the flat frame structure.
	delete[] indexedFrames;

	if (boneFrames)
		delete[] boneFrames;

	return mesh;
}