Esempio n. 1
0
MFString MFString::SubStr(int offset, int count) const
{
	if(!pData)
		return *this;

	// limit within the strings range
	int maxChars = (int)pData->bytes - offset;
	if(count < 0 || count > maxChars)
		count = maxChars;

	// bail if we don't need to do anything
	if(count == (int)pData->bytes)
		return *this;

	// allocate a new string
	MFString t;
    t.Reserve(count + 1);
	t.pData->bytes = count;

	// copy sub string
	MFString_CopyN(t.pData->pMemory, pData->pMemory + offset, count);
	t.pData->pMemory[count] = 0;

	return t;
}
Esempio n. 2
0
void dBTextProp::SetJustification(dBEntity *pEntity, dBRuntimeArgs *pArguments)
{
	dBTextProp *pTextProp = (dBTextProp*)pEntity;
	MFString arg = pArguments->GetString(0);
	const char *pStr = arg.CStr();

	if(!MFString_CaseCmp(pStr, "top_left"))
		pTextProp->justification = MFFontJustify_Top_Left;
	else if(!MFString_CaseCmp(pStr, "top_center"))
		pTextProp->justification = MFFontJustify_Top_Center;
	else if(!MFString_CaseCmp(pStr, "top_right"))
		pTextProp->justification = MFFontJustify_Top_Right;
	else if(!MFString_CaseCmp(pStr, "top_full"))
		pTextProp->justification = MFFontJustify_Top_Full;
	else if(!MFString_CaseCmp(pStr, "center_left"))
		pTextProp->justification = MFFontJustify_Center_Left;
	else if(!MFString_CaseCmp(pStr, "center"))
		pTextProp->justification = MFFontJustify_Center;
	else if(!MFString_CaseCmp(pStr, "center_right"))
		pTextProp->justification = MFFontJustify_Center_Right;
	else if(!MFString_CaseCmp(pStr, "center_full"))
		pTextProp->justification = MFFontJustify_Center_Full;
	else if(!MFString_CaseCmp(pStr, "bottom_Left"))
		pTextProp->justification = MFFontJustify_Bottom_Left;
	else if(!MFString_CaseCmp(pStr, "bottom_center"))
		pTextProp->justification = MFFontJustify_Bottom_Center;
	else if(!MFString_CaseCmp(pStr, "bottom_right"))
		pTextProp->justification = MFFontJustify_Bottom_Right;
	else if(!MFString_CaseCmp(pStr, "bottom_full"))
		pTextProp->justification = MFFontJustify_Bottom_Full;
}
Esempio n. 3
0
MF_API MFString MFString_GetStats()
{
	size_t overhead = stringPool.GetTotalMemory() + stringPool.GetOverheadMemory() + stringHeap.GetOverheadMemory();
	size_t waste = 0, averageSize = 0;

	// calculate waste
	int numStrings = stringPool.GetNumAllocated();
	for(int a=0; a<numStrings; ++a)
	{
		MFStringData *pString = (MFStringData*)stringPool.GetItem(a);
		size_t allocated = pString->allocated;
		if(allocated)
		{
			size_t bytes = pString->bytes+1;
			averageSize += bytes;
			waste += allocated - bytes;
		}
	}

	if(numStrings)
		averageSize /= numStrings;

	MFString desc;
    desc.Reserve(1024);
	desc = MFStr("String heap memory: " MFFMT_SIZE_T " allocated/" MFFMT_SIZE_T " reserved + " MFFMT_SIZE_T " overhead  Waste: " MFFMT_SIZE_T "  Average length: " MFFMT_SIZE_T "\n\tPool: %d/%d", stringHeap.GetAllocatedMemory(), stringHeap.GetTotalMemory(), overhead, waste, averageSize, stringPool.GetNumAllocated(), stringPool.GetNumReserved());

	int numGroups = stringHeap.GetNumPools();
	for(int a=0; a<numGroups; ++a)
	{
		MFObjectPool *pPool = stringHeap.GetPool(a);
		desc += MFStr("\n\t\t" MFFMT_SIZE_T " byte: %d/%d", pPool->GetObjectSize(), pPool->GetNumAllocated(), pPool->GetNumReserved());
	}

	return desc;
}
Esempio n. 4
0
static void MissingStates(MFStateBlockConstantType type, uint32 missing)
{
	static const char * const sStateType[MFSB_CT_TypeCount] =
	{
		"Bool",
		"Vector",
		"Matrix",
		"RenderState",
		"Texture",
		"Misc",
		"",
		"Unknown"
	};

	MFString states;
	for(int a=0; a<32; ++a)
	{
		if(missing & (1<<a))
		{
			if(!states.IsNull())
				states += ", ";
			states += MFStateBlock_GetRenderStateName(type, a);
		}
	}

	MFDebug_Assert(missing == 0, MFStr("Missing %s states: %s", sStateType[type], states.CStr()));
}
Esempio n. 5
0
static int MFFileSystem_GetNumEntries(const char *pFindPattern, bool recursive, bool flatten, size_t *pStringLengths)
{
	MFFindData findData;
	MFFind *hFind;

	int numFiles = 0;

	hFind = MFFileSystem_FindFirst(MFString::Format("%s*", pFindPattern).CStr(), &findData);

	if(hFind)
	{
		*pStringLengths += MFString_Length(findData.pSystemPath) + 1;
	}

	while(hFind)
	{
		if(MFString_Compare(findData.pFilename, ".") && MFString_Compare(findData.pFilename, "..") && MFString_Compare(findData.pFilename, ".svn"))
		{
			if(findData.info.attributes & MFFA_Directory)
			{
				if(recursive)
				{
					if(flatten)
					{
						MFString newPath = MFString::Format("%s%s/", pFindPattern, findData.pFilename);
						numFiles += MFFileSystem_GetNumEntries(newPath.CStr(), recursive, flatten, pStringLengths);
					}
					else
					{
						*pStringLengths += MFString_Length(findData.pFilename) + 1;
						++numFiles;
					}
				}
			}
			else
			{
				*pStringLengths += MFString_Length(findData.pFilename) + 1;
				++numFiles;
			}
		}

		if(!MFFileSystem_FindNext(hFind, &findData))
		{
			MFFileSystem_FindClose(hFind);
			hFind = NULL;
		}
	}

	return numFiles;
}
Esempio n. 6
0
MFString MFString::Lower() const
{
	if(!pData)
		return *this;

	// allocate a new string
	MFString t;
    t.Reserve(pData->bytes + 1);
	t.pData->bytes = pData->bytes;

	// copy lower case string
	for(size_t a=0; a<pData->bytes + 1; ++a)
		t.pData->pMemory[a] = (char)MFToLower(pData->pMemory[a]);

	return t;
}
Esempio n. 7
0
MFString MFString::operator+(const MFString &string) const
{
	if(string.IsEmpty())
		return *this;

	if(IsEmpty())
		return string;

	size_t bytes = pData->bytes + string.pData->bytes;

	MFString t;
    t.Reserve(bytes + 1);
	MFString_CopyCat(t.pData->pMemory, pData->pMemory, string.pData->pMemory);
	t.pData->bytes = bytes;

	return t;
}
Esempio n. 8
0
MF_API void MFString_Dump()
{
	MFString temp = MFString_GetStats();

	MFDebug_Log(1, "\n-------------------------------------------------------------------------------------------------------");
	MFDebug_Log(1, temp.CStr());

	// dump all strings...
	MFDebug_Log(1, "");

	int numStrings = stringPool.GetNumAllocated();
	for(int a=0; a<numStrings; ++a)
	{
		MFStringData *pString = (MFStringData*)stringPool.GetItem(a);
		MFDebug_Log(1, MFStr("%d refs, " MFFMT_SIZE_T "b: \"%s\"", pString->refCount, pString->allocated, pString->pMemory));
	}
}
Esempio n. 9
0
MFString MFString::operator+(const char *pString) const
{
	if(!pString || *pString == 0)
		return *this;

	if(IsEmpty())
		return MFString(pString);

	size_t bytes = pData->bytes + MFString_Length(pString);

	MFString t;
    t.Reserve(bytes + 1);
	MFString_CopyCat(t.pData->pMemory, pData->pMemory, pString);
	t.pData->bytes = bytes;

	return t;
}
Esempio n. 10
0
void X3DConverter::startGroup(const std::string& name, const Vector3F& translation, const int& materialId) {
    // Ensuring the name is compatible with the DEF attribute
    string x3dName = name;
    size_t p;
    while ((p = x3dName.find_first_of(' ')) != string::npos)
        x3dName[p] = '_';
    while (x3dName[0] == '-')
        x3dName[0] = '_';
    while ((p = x3dName.find_first_of('/')) != string::npos) {
        x3dName[p] = '_';
    }
    m_materials.push_back(materialId);
    m_groups.push_back(name);

    if (m_split) {
        startNode(ID::Inline);

        MFString name;
        name.push_back(x3dName + ".x3d");
        m_writers.back()->setMFString(ID::url, name);

        X3DWriter* writer = m_binary ? (X3DWriter*)new X3DWriterFI() : (X3DWriter*)new X3DWriterXML();
        writer->setProperty(Property::IntEncodingAlgorithm, (void*)Encoder::DeltazlibIntArrayEncoder);
        writer->setProperty(Property::FloatEncodingAlgorithm, (void*)Encoder::QuantizedzlibFloatArrayEncoder);
        writer->openFile((x3dName + ".x3d").data());
        m_writers.push_back(writer);
        multimap<std::string, std::string> meta;
        meta.insert(pair<string, string>("generator", "Plant Mock-Up Converter 0.1"));
        m_writers.back()->startX3DDocument(Immersive, VERSION_3_0, &meta, false);
        startNode(ID::Background);
        m_writers.back()->setSFColor(ID::skyColor, .9f, .9f, .9f);
        endNode(ID::Background);
    }
    startNode(ID::Transform);
	// Problems with encoding and unicity so changed name from DEF storage to metadata. See after translation.
    //m_writers.back()->setSFString(ID::DEF, x3dName);
    /*m_writers.back()->setSFVec3f(ID::translation,
                                 (translation[0] - m_translations.back()[0]),
                                 (translation[1] - m_translations.back()[1]),
                                 (translation[2] - m_translations.back()[2]));*/
    m_translations.push_back(translation);

    // PDMS name as metadata.
    // writeMetaDataString("pdms", name);
}
Esempio n. 11
0
void X3DConverter::startModel(const string& projectName, const string& name) {
    startNode(ID::WorldInfo);
    MFString info;
    info.push_back("info");
    m_writers.back()->setMFString(ID::info, info);
    startNode(ID::MetadataSet);
    if (!projectName.empty()) {
        writeMetaDataString("projectName", projectName, true);
    }
    if (!name.empty()) {
        writeMetaDataString("name", name, true);
    }
    endNode(ID::MetadataSet); // MetaDataSet
    endNode(ID::WorldInfo); // WorldInfo
    startNode(ID::Background);
    m_writers.back()->setSFColor(ID::skyColor, .9f, .9f, .9f);
    endNode(ID::Background);
}
Esempio n. 12
0
MFString operator+(const char *pString, const MFString &string)
{
	if(string.IsEmpty())
		return MFString(pString);

	if(!pString || *pString == 0)
		return string;

	return MFString::Static(pString) + string;
}
Esempio n. 13
0
void HKWidgetRendererLabel::Render(const HKWidget &widget, const MFMatrix &worldTransform)
{
	HKWidgetRenderer::Render(widget, worldTransform);

	HKWidgetLabel &label = (HKWidgetLabel&)widget;

	MFString l = label.GetText();
	if(!l.IsEmpty())
	{
		MFFont *pFont = label.GetFont();
		float height = label.GetTextHeight();
		float shadowDepth = label.GetShadowDepth();
		const MFVector &size = widget.GetSize();
		const MFVector &colour = widget.GetColour();
		const MFVector &textColour = label.GetTextColour();
		MFFontJustify j = label.GetTextJustification();
		const char *pString = l.CStr();

		if(shadowDepth > 0.f)
			MFFont_DrawTextJustified(pFont, pString, MakeVector(shadowDepth, shadowDepth), size.x, size.y, j, height, MFVector::black, -1, worldTransform);
		MFFont_DrawTextJustified(pFont, pString, MFVector::zero, size.x, size.y, j, height, textColour * colour, -1, worldTransform);
	}
}
Esempio n. 14
0
void Scan(MFString path)
{
	MFFindData fd;
	MFFind *pFind = MFFileSystem_FindFirst(MFStr("%s*", path.CStr()), &fd);
	if(pFind)
	{
		do
		{
			if(fd.attributes & MFFA_Directory)
			{
				Scan(MFString::Format("%s%s/", path.CStr(), fd.pFilename).CStr());
			}
			else
			{
				MFString ext = MFString(fd.pFilename).GetExtension();
				if(ext.Enumerate(ppFormats, sizeof(ppFormats) / sizeof(ppFormats[0])) > -1)
					models.push(MFString::Format("%s%s", path.CStr(), fd.pFilename));
			}
		}
		while(MFFileSystem_FindNext(pFind, &fd));

		MFFileSystem_FindClose(pFind);
	}
}
Esempio n. 15
0
MFTOCEntry* MFFileSystem_BuildToc(const char *pFindPattern, MFTOCEntry *pToc, MFTOCEntry *pParent, char* &pStringCache, bool recursive, bool flatten)
{
	MFFindData findData;
	MFFind *hFind;

	hFind = MFFileSystem_FindFirst(MFString::Format("%s*", pFindPattern).CStr(), &findData);

	char *pCurrentDir = pStringCache;

	if(hFind)
	{
		MFString_Copy(pCurrentDir, findData.pSystemPath);
		pStringCache += MFString_Length(findData.pSystemPath) + 1;
	}

	while(hFind)
	{
		if(MFString_Compare(findData.pFilename, ".") && MFString_Compare(findData.pFilename, "..") && MFString_Compare(findData.pFilename, ".svn"))
		{
			if(findData.info.attributes & MFFA_Directory)
			{
				if(recursive)
				{
					MFString newPath = MFString::Format("%s%s/", pFindPattern, findData.pFilename);
					if(flatten)
					{
						pToc = MFFileSystem_BuildToc(newPath.CStr(), pToc, pParent, pStringCache, recursive, flatten);
					}
					else
					{
						size_t stringCacheSize = 0;
						pToc->numChildren = MFFileSystem_GetNumEntries(newPath.CStr(), recursive, flatten, &stringCacheSize);

						MFString_Copy(pStringCache, findData.pFilename);
						pToc->pName = pStringCache;
						pStringCache += MFString_Length(findData.pFilename)+1;

						pToc->info = findData.info;
						pToc->pFilesysData = pCurrentDir;
						pToc->pParent = pParent;

						if (pToc->numChildren)
						{
							size_t sizeOfToc = sizeof(MFTOCEntry)*pToc->numChildren;
							pToc->pChildren = (MFTOCEntry*)MFHeap_Alloc(sizeOfToc + stringCacheSize);

							char *pNewStringCache = (char*)pToc->pChildren + sizeOfToc;
							MFFileSystem_BuildToc(newPath.CStr(), pToc->pChildren, pToc, pNewStringCache, recursive, flatten);
						}
						else
							pToc->pChildren = NULL;

						++pToc;
					}
				}
			}
			else
			{
				MFString_Copy(pStringCache, findData.pFilename);
				pToc->pName = pStringCache;
				pStringCache += MFString_Length(findData.pFilename)+1;

				pToc->pFilesysData = pCurrentDir;

				pToc->pParent = pParent;
				pToc->pChildren = NULL;
				pToc->numChildren = 0;

				pToc->info = findData.info;

				++pToc;
			}
		}

		if(!MFFileSystem_FindNext(hFind, &findData))
		{
			MFFileSystem_FindClose(hFind);
			hFind = NULL;
		}
	}

	return pToc;
}
Esempio n. 16
0
MFTOCEntry* MFFileSystem_BuildToc(const char *pFindPattern, MFTOCEntry *pToc, MFTOCEntry *pParent, char* &pStringCache, bool recursive, bool flatten)
{
	MFFindData findData;
	MFFind *hFind;

	hFind = MFFileSystem_FindFirst(MFString::Format("%s*", pFindPattern).CStr(), &findData);

	char *pCurrentDir = pStringCache;

	if(hFind)
	{
		MFString_Copy(pCurrentDir, findData.pSystemPath);
		pStringCache += MFString_Length(findData.pSystemPath) + 1;
	}

	while(hFind)
	{
		if(MFString_Compare(findData.pFilename, ".") && MFString_Compare(findData.pFilename, "..") && MFString_Compare(findData.pFilename, ".svn"))
		{
			if(findData.attributes & MFFA_Directory)
			{
				if(recursive)
				{
					MFString newPath = MFString::Format("%s%s/", pFindPattern, findData.pFilename);
					if(flatten)
					{
						pToc = MFFileSystem_BuildToc(newPath.CStr(), pToc, pParent, pStringCache, recursive, flatten);
					}
					else
					{
						size_t stringCacheSize = 0;
						pToc->size = MFFileSystem_GetNumEntries(newPath.CStr(), recursive, flatten, &stringCacheSize);

						if(pToc->size)
						{
							MFString_Copy(pStringCache, findData.pFilename);
							pToc->pName = pStringCache;
							pStringCache += MFString_Length(findData.pFilename)+1;

							pToc->flags = MFTF_Directory;
							pToc->pFilesysData = pCurrentDir;
							pToc->pParent = pParent;
							pToc->size = 0;

							size_t sizeOfToc = sizeof(MFTOCEntry)*pToc->size;
							pToc->pChild = (MFTOCEntry*)MFHeap_Alloc(sizeof(MFTOCEntry)*sizeOfToc + stringCacheSize);

							char *pNewStringCache = ((char*)pToc->pChild)+sizeOfToc;
							MFFileSystem_BuildToc(newPath.CStr(), pToc->pChild, pToc, pNewStringCache, recursive, flatten);

							++pToc;
						}
					}
				}
			}
			else
			{
				MFString_Copy(pStringCache, findData.pFilename);
				pToc->pName = pStringCache;
				pStringCache += MFString_Length(findData.pFilename)+1;

				pToc->pFilesysData = pCurrentDir;

				pToc->pParent = pParent;
				pToc->pChild = NULL;

				MFDebug_Assert(findData.fileSize < 0x100000000LL, "Files larger than 4gb not yet supported...");
				pToc->size = (uint32)findData.fileSize;
				pToc->flags = 0;

				++pToc;
			}
		}

		if(!MFFileSystem_FindNext(hFind, &findData))
		{
			MFFileSystem_FindClose(hFind);
			hFind = NULL;
		}
	}

	return pToc;
}
Esempio n. 17
0
int MFString::Parse(const char *pFormat, ...)
{
	if(!pData || !pFormat)
		return 0;

	va_list arglist;
	va_start(arglist, pFormat);

	const char *pS = pData->pMemory;
	int numArgs = 0;

	MFString format = GetNextBit(pFormat);
	int numChars = Match(pS, format.CStr());

	while(*pFormat && numChars >= 0)
	{
		pS += numChars;
		++pFormat;

		int length = -1;

		// gather format options...
		while(*pFormat)
		{
			int c = MFToLower(*pFormat++);
			if(c == 's')
			{
				MFString *pStr = va_arg(arglist, MFString*);
				++numArgs;

				format = GetNextBit(pFormat);

				if(length >= 0)
				{
					MFString s(pS, (size_t)length);
					*pStr = s;

					numChars = Match(pS, format.CStr());
				}
				else if(format.NumBytes() == 0)
				{
					*pStr = pS;
				}
				else
				{
					const char *pEnd = pS;
					while(*pEnd && (numChars = Match(pEnd, format.CStr())) < 0)
						++pEnd;

					MFString s(pS, (size_t)(pEnd - pS));
					*pStr = s;
				}

				pS += pStr->NumBytes();
				break;
			}
			else if(c == 'd' || c == 'i')
			{
				int *pInt = va_arg(arglist, int*);
				++numArgs;

				bool bNeg = *pS == '-';
				if(*pS == '-' || *pS == '+')
					++pS;

				*pInt = 0;
				while(MFIsNumeric(*pS) && ((uint32&)length)-- > 0)
					*pInt = *pInt*10 + *pS++ - '0';

				if(bNeg)
					*pInt = -*pInt;

				format = GetNextBit(pFormat);
				numChars = Match(pS, format.CStr());
				break;
			}
Esempio n. 18
0
int F3DFile::ReadMD3(const char *pFilename)
{
	pModel = this;

	char *pBuffer = NULL;

	// open .pk3 file
	unzFile zipFile = unzOpen(pFilename);

	// iterate files in zip
	int zipFileIndex = unzGoToFirstFile(zipFile);

	while(zipFileIndex != UNZ_END_OF_LIST_OF_FILE)
	{
		MFDebug_Assert(zipFileIndex == UNZ_OK, "Error in .zip file.");

		char fileNameBuf[256];
		unz_file_info fileInfo;
		unzGetCurrentFileInfo(zipFile, &fileInfo, fileNameBuf, sizeof(fileNameBuf), NULL, 0, NULL, 0);
		MFString fileName = fileNameBuf;

		if(fileName.EndsWith(".md3"))
		{
			// read fle from zip
			pBuffer = (char*)malloc(fileInfo.uncompressed_size);

			unzOpenCurrentFile(zipFile);
			uint32 bytesRead = unzReadCurrentFile(zipFile, pBuffer, fileInfo.uncompressed_size);
			unzCloseCurrentFile(zipFile);

			MFDebug_Assert(bytesRead == fileInfo.uncompressed_size, "Incorrect number of bytes read..");

			// get subobject and model name..
			int slash = MFMax(fileName.FindCharReverse('/'), fileName.FindCharReverse('\\'));
			MFString subobjectName = fileName.SubStr(slash + 1);

			MFString modelName = fileName.SubStr(0, slash);
			slash = MFMax(modelName.FindCharReverse('/'), modelName.FindCharReverse('\\'));
			pModel->name = modelName.SubStr(slash + 1);

			// search for skin file
			MFString skinFilename = fileName;
			skinFilename.TruncateExtension();
			skinFilename += "_";
			skinFilename += pModel->name;
			skinFilename += ".skin";

			// attempt to read skin..
			char *pSkinFile = NULL;

			zipFileIndex = unzLocateFile(zipFile, skinFilename.CStr(), 0);

			if(zipFileIndex != UNZ_END_OF_LIST_OF_FILE)
			{
				// read skin file from zip
				unz_file_info skinInfo;
				char skinName[256];

				unzGetCurrentFileInfo(zipFile, &skinInfo, skinName, 256, NULL, 0, NULL, 0);

				pSkinFile = (char*)MFHeap_TAlloc(skinInfo.uncompressed_size + 1);
				pSkinFile[skinInfo.uncompressed_size] = 0;

				unzOpenCurrentFile(zipFile);
				uint32 skinBytesRead = unzReadCurrentFile(zipFile, pSkinFile, skinInfo.uncompressed_size);
				unzCloseCurrentFile(zipFile);

				MFDebug_Assert(skinBytesRead == skinInfo.uncompressed_size, "Incorrect number of bytes read..");
			}

			zipFileIndex = unzLocateFile(zipFile, fileName.CStr(), 0);

			// parse MD3
			ParseMD3File(pBuffer, fileInfo.uncompressed_size, subobjectName.CStr(), pSkinFile);

			// free file
			MFHeap_Free(pBuffer);
			pBuffer = NULL;
		}
/*
		else if(!MFString_CaseCmp(".skin", &fileName[Max(filenameLen - 5, 0)]))
		{
			int a, b;
			char skinName[256];

			// get subobject and model name
			for(a = filenameLen - 5; a >= 0; a--)
			{
				if(fileName[a] == '/' || fileName[a] == '\\')
					break;
			}

			char *pSkinName = &fileName[a+1];

			for(b = a-1; b >= 0; b--)
			{
				if(fileName[b] == '/' || fileName[b] == '\\')
					break;
			}

			MFString_Copy(skinName, &fileName[b+1]);
			skinName[a-b-1] = 0;

			pSkinName = strchr(pSkinName, '_');
			DBGASSERT(pSkinName, "Incorrectly named .skin file in .pk3");
			++pSkinName;

			// check that this is the default skin for this model
			if(!MFString_CaseCmpN(pSkinName, skinName, MFString_Length(skinName)))
			{
				// read material file from zip
				pBuffer = (char*)malloc(fileInfo.uncompressed_size);

				unzOpenCurrentFile(zipFile);
				uint32 bytesRead = unzReadCurrentFile(zipFile, pBuffer, fileInfo.uncompressed_size);
				unzCloseCurrentFile(zipFile);

				// parse .skin file

				free(pBuffer);
				pBuffer = NULL;
			}
			else
			{
				// we have an alternate skin.. do nothing..
			}
		}
*/

		zipFileIndex = unzGoToNextFile(zipFile);
	}

	// close .pk3 file
	unzClose(zipFile);

	return 0;
}
Esempio n. 19
0
MF_API MFModel* MFModel_Create(const char *pFilename)
{
	const char* pOriginalFilename = pFilename;

	// see if it's already loaded
	MFModelPool::Iterator it = gModelBank.Get(pOriginalFilename);
	MFModelTemplate *pTemplate = it ? *it : NULL;

	if(!pTemplate)
	{
		char *pTemplateData = NULL;

		MFFile *hFile = MFFileSystem_Open(MFStr("%s.mdl", pFilename), MFOF_Read|MFOF_Binary);
		if(hFile)
		{
			int64 size = MFFile_GetSize(hFile);

			if(size > 0)
			{
				// allocate memory and load file
				pTemplateData = (char*)MFHeap_Alloc((size_t)size + MFString_Length(pFilename) + 1);
				MFFile_Read(hFile, pTemplateData, (size_t)size);
				MFFile_Close(hFile);

				MFString_Copy(&pTemplateData[size], pFilename);
				pFilename = &pTemplateData[size];
			}
		}
		else
		{
#if defined(ALLOW_LOAD_FROM_SOURCE_DATA)
			// try to load from source data
			const char * const pExt[] = { ".f3d", ".dae", ".x", ".ase", ".obj", ".md2", ".md3", ".me2", NULL };
			const char * const *ppExt = pExt;
			MFIntModel *pIM = NULL;
			while(!pIM && *ppExt)
			{
				MFString tempFilename = MFString::Format("%s%s", pFilename, *ppExt);
				pIM = MFIntModel_CreateFromFile(tempFilename.CStr());
				if(pIM)
				{
					pFilename = MFString_Copy((char*)MFHeap_Alloc(tempFilename.NumBytes()+1), tempFilename.CStr());
					break;
				}
				++ppExt;
			}

			if(pIM)
			{
				MFIntModel_Optimise(pIM);

				size_t size;
				MFIntModel_CreateRuntimeData(pIM, (void**)&pTemplateData, &size, MFSystem_GetCurrentPlatform());

				MFFile *pFile = MFFileSystem_Open(MFStr("cache:%s.mdl", pOriginalFilename), MFOF_Write | MFOF_Binary);
				if(pFile)
				{
					MFFile_Write(pFile, pTemplateData, size, false);
					MFFile_Close(pFile);
				}

				MFIntModel_Destroy(pIM);
			}
#endif
		}

		if(!pTemplateData)
			return NULL;

		// check ID string
		MFDebug_Assert(*(uint32*)pTemplateData == MFMAKEFOURCC('M', 'D', 'L', '2'), "Incorrect MFModel version.");

		// store filename for later reference
		pTemplate = (MFModelTemplate*)pTemplateData;
		pTemplate->pFilename = pFilename;

		gModelBank.Add(pOriginalFilename, pTemplate);

		MFModel_FixUp(pTemplate, true);

		MFModelDataChunk *pChunk = MFModel_GetDataChunk(pTemplate, MFChunkType_SubObjects);

		if(pChunk)
		{
			MFModelSubObject *pSubobjects = (MFModelSubObject*)pChunk->pData;

			for(int a=0; a<pChunk->count; a++)
			{
//				pSubobjects[a].pMaterial = MFMaterial_Create((char*)pSubobjects[a].pMaterial);

				for(int b=0; b<pSubobjects[a].numMeshChunks; b++)
				{
					MFModel_CreateMeshChunk(MFModel_GetMeshChunkInternal(pTemplate, a, b));
				}
			}
		}
	}

	MFModel *pModel;
	pModel = (MFModel*)MFHeap_Alloc(sizeof(MFModel));

	pModel->worldMatrix = MFMatrix::identity;
	pModel->modelColour = MFVector::one;
	pModel->pTemplate = pTemplate;
	pModel->pAnimation = NULL;

	++pTemplate->refCount;

	return pModel;
}