Esempio n. 1
0
MF_API MFRenderer* MFRenderer_Create(MFRenderLayerDescription *pLayers, int numLayers, MFStateBlock *pGlobal, MFStateBlock *pOverride)
{
	int debugLayer = numLayers;
#if !defined(MF_RETAIL)
	// add a layer for the system/debug stuff
	numLayers += 1;
#endif

	MFRenderer *pRenderer = (MFRenderer*)MFHeap_AllocAndZero(sizeof(MFRenderer) + sizeof(MFRenderLayer)*numLayers);

	pRenderer->pLayers = (MFRenderLayer*)&pRenderer[1];
	pRenderer->numLayers = numLayers;
	pRenderer->pGlobal = pGlobal;
	pRenderer->pOverride = pOverride;

	MFRenderTarget *pRT = MFRenderer_GetDeviceRenderTarget();
	for(int a=0; a<numLayers; ++a)
	{
		pRenderer->pLayers[a].pName = a < debugLayer ? pLayers[a].pName : "Debug";
		pRenderer->pLayers[a].pRenderTarget = pRT;
		MFResource_AddRef(pRT);
	}

#if !defined(MF_RETAIL)
	MFRenderLayer_SetLayerSortMode(&pRenderer->pLayers[debugLayer], MFRL_SM_None);
#endif

	return pRenderer;
}
Esempio n. 2
0
MF_API MFMaterial* MFMaterial_Create(const char *pName)
{
	MFCALLSTACK;

	MFMaterial *pMat = MFMaterial_Find(pName);

	if(!pMat)
	{
		pMat = (MFMaterial*)MFHeap_AllocAndZero(sizeof(MFMaterial) + MFString_Length(pName) + 1);

		pName = MFString_Copy((char*)&pMat[1], pName);

		MFResource_AddResource(pMat, MFRT_Material, MFUtil_HashString(pName) ^ 0x0a7e01a1, pName);

		// TODO: how to determine size?
		pMat->pMaterialState = MFStateBlock_Create(256);
		pMat->bStateDirty = true;

		MaterialDefinition *pDef = pDefinitionRegistry;
		while(pDef)
		{
			MFIniLine *pLine = pDef->pIni->GetFirstLine()->FindEntry("section",pName);
			if (pLine)
			{
				MaterialInternal_InitialiseFromDefinition(pDef->pIni, pMat, pName);
				break;
			}

			pDef = pDef->pNextDefinition;
		}

		if(!pDef)
		{
			// assign material type
			pMat->pType = MaterialInternal_GetMaterialType("Standard");

			pMat->pInstanceData = MFHeap_AllocAndZero(pMat->pType->instanceDataSize);
			pMat->pType->materialCallbacks.pCreateInstance(pMat);

			// set diffuse map parameter
			MFMaterial_SetParameterS(pMat, MFMatStandard_Texture, MFMatStandard_Tex_DifuseMap, pName);
		}
	}

	return pMat;
}
Esempio n. 3
0
MF_API MFTexture* MFTexture_CreateRenderTarget(const char *pName, int width, int height, MFTextureFormat targetFormat)
{
	MFTexture *pTexture = MFTexture_FindTexture(pName);

	if(!pTexture)
	{
		pTexture = &gTextureBank.Create(pName);

		if(targetFormat & TexFmt_SelectNicest)
		{
			targetFormat = TexFmt_A8R8G8B8;
		}

		pTexture->pTemplateData = (MFTextureTemplateData*)MFHeap_AllocAndZero(sizeof(MFTextureTemplateData) + sizeof(MFTextureSurfaceLevel));
		pTexture->pTemplateData->pSurfaces = (MFTextureSurfaceLevel*)&pTexture->pTemplateData[1];
		pTexture->pTemplateData->magicNumber = MFMAKEFOURCC('F','T','E','X');
		pTexture->pTemplateData->imageFormat = targetFormat;
		pTexture->pTemplateData->mipLevels = 1;
		pTexture->pTemplateData->flags = TEX_RenderTarget;

		pTexture->pTemplateData->pSurfaces->width = width;
		pTexture->pTemplateData->pSurfaces->height = height;
		pTexture->pTemplateData->pSurfaces->bitsPerPixel = MFTexture_GetBitsPerPixel(pTexture->pTemplateData->imageFormat);
		pTexture->pTemplateData->pSurfaces->xBlocks = width;
		pTexture->pTemplateData->pSurfaces->yBlocks = height;
		pTexture->pTemplateData->pSurfaces->bitsPerBlock = pTexture->pTemplateData->pSurfaces->bitsPerPixel;
		pTexture->pTemplateData->pSurfaces->pImageData = NULL;
		pTexture->pTemplateData->pSurfaces->bufferLength = 0;
		pTexture->pTemplateData->pSurfaces->pPaletteEntries = NULL;
		pTexture->pTemplateData->pSurfaces->paletteBufferLength = 0;

		pTexture->refCount = 1;
		MFString_CopyN(pTexture->name, pName, sizeof(pTexture->name) - 1);
		pTexture->name[sizeof(pTexture->name) - 1] = 0;

		D3DFORMAT platformFormat = (D3DFORMAT)MFTexture_GetPlatformFormatID(targetFormat, MFDD_D3D9);
		HRESULT hr = pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, platformFormat, D3DPOOL_DEFAULT, (IDirect3DTexture9**)&pTexture->pInternalData, NULL);

		if(hr != D3D_OK)
		{
			MFHeap_Free(pTexture->pTemplateData);
			gTextureBank.DestroyItem(*pTexture);
			pTexture = NULL;
		}
	}
	else
	{
		pTexture->refCount++;
	}

	return pTexture;
}
Esempio n. 4
0
MF_API MFWindow *MFWindow_BindExisting(void *pWindowHandle)
{
	MFWindow_PC *pWindow = (MFWindow_PC*)MFHeap_AllocAndZero(sizeof(MFWindow_PC));
	pWindow->hWnd = (HWND)pWindowHandle;

	MFDebug_Assert(false, "TODO!");
	// TODO: fill out params from window data...
//	pWindow->params.x = 

	SetWindowLongPtr(pWindow->hWnd, GWLP_USERDATA, (LONG_PTR)pWindow);

	return pWindow;
}
Esempio n. 5
0
MF_API MFWindow *MFWindow_Create(MFWindowParams *pParams)
{
	MFWindow_PC *pWindow = (MFWindow_PC*)MFHeap_AllocAndZero(sizeof(MFWindow_PC));
	pWindow->params = *pParams;

	// create window
	RECT rect;
	rect.left = 0;
	rect.right = (long)pParams->width;
	rect.top = 0;
	rect.bottom = (long)pParams->height;

	DWORD dwStyle = WS_POPUP;
	DWORD dwExStyle = WS_EX_APPWINDOW;
	if(!pParams->bFullscreen)
	{
		if(pParams->flags & MFWF_WindowFrame)
		{
			dwExStyle |= WS_EX_WINDOWEDGE;
			dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
			if(pParams->flags & MFWF_CanResize)
				dwStyle |= WS_THICKFRAME;
		}
		if(pParams->flags & MFWF_AlwaysOnTop)
			dwExStyle |= WS_EX_TOPMOST;

		AdjustWindowRectEx(&rect, dwStyle, FALSE, dwExStyle);
		rect.right += -rect.left + (long)pParams->x;
		rect.left = (long)pParams->x;
		rect.bottom += -rect.top + (long)pParams->y;
		rect.top = (long)pParams->y;
	}

	pWindow->hWnd = CreateWindowEx(dwExStyle, "FujiWin", gDefaults.display.pWindowTitle, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dwStyle, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, NULL, NULL, hInstance, NULL);
	if(!pWindow->hWnd)
	{
		MessageBox(NULL,"Failed To Create Window.","Error!",MB_OK|MB_ICONERROR);
		MFHeap_Free(pWindow);
		return NULL;
	}

	SetWindowLongPtr(pWindow->hWnd, GWLP_USERDATA, (LONG_PTR)pWindow);

	ShowWindow(pWindow->hWnd, SW_SHOW);
	SetForegroundWindow(pWindow->hWnd);
	SetFocus(pWindow->hWnd);

	return pWindow;
}
Esempio n. 6
0
MF_API MFIndexBuffer *MFVertex_CreateIndexBuffer(int numIndices, uint16 *pIndexBufferMemory, const char *pName)
{
	int nameLen = pName ? MFString_Length(pName) + 1 : 0;
	MFIndexBuffer *pIB = (MFIndexBuffer*)MFHeap_AllocAndZero(sizeof(MFIndexBuffer) + nameLen);

	if(pName)
		pName = MFString_Copy((char*)&pIB[1], pName);

	pIB->numIndices = numIndices;

	if(!MFVertex_CreateIndexBufferPlatformSpecific(pIB, pIndexBufferMemory))
	{
		MFHeap_Free(pIB);
		return NULL;
	}

	MFResource_AddResource(pIB, MFRT_IndexBuffer, (uint32)(MFUtil_HashPointer(pIB) & 0xFFFFFFFF), pName);

	return pIB;
}
Esempio n. 7
0
// mount a filesystem
MF_API int MFFileSystem_Mount(MFFileSystemHandle fileSystem, MFMountData *pMountData)
{
	GET_MODULE_DATA(MFFileSystemState);

	MFMount *pMount;
	pMount = (MFMount*)MFHeap_AllocAndZero(sizeof(MFMount) + MFString_Length(pMountData->pMountpoint) + 1);

	pMount->volumeInfo.flags = pMountData->flags;
	pMount->volumeInfo.fileSystem = fileSystem;
	pMount->volumeInfo.priority = pMountData->priority;
	pMount->volumeInfo.pVolumeName = (const char*)&pMount[1];
	MFString_Copy((char*)&pMount[1], pMountData->pMountpoint);

	// call the mount callback
	int result = pModuleData->ppFileSystemList[fileSystem]->callbacks.FSMount(pMount, pMountData);

	if(result < 0)
	{
		MFHeap_Free(pMount);
		return -1;
	}

	return MFFileSystem_AddVolume(pMount);
}
Esempio n. 8
0
void MaterialInternal_InitialiseFromDefinition(MFIni *pDefIni, MFMaterial *pMat, const char *pDefinition)
{
	MFCALLSTACK;

	MFIniLine *pLine = pDefIni->GetFirstLine()->FindEntry("section", pDefinition);

	if(pLine)
	{
		pLine = pLine->Sub();

		if(pLine && pLine->IsString(0, "type"))
		{
			pMat->pType = MaterialInternal_GetMaterialType(pLine->GetString(1));

			if(!pMat->pType)
				pMat->pType = MaterialInternal_GetMaterialType("Standard");

			pLine = pLine->Next();
		}
		else
		{
			pMat->pType = MaterialInternal_GetMaterialType("Standard");
		}

		pMat->pInstanceData = MFHeap_AllocAndZero(pMat->pType->instanceDataSize);

		if(pMat->pType->materialCallbacks.pCreateInstance)
			pMat->pType->materialCallbacks.pCreateInstance(pMat);

		while(pLine)
		{
			if(pLine->IsString(0,"type"))
			{
				MFDebug_Warn(2, MFStr("'type' MUST be the first parameter in a material definition... Ignored, Using type '%s'.", pMat->pType->pTypeName));
			}
			else if(pLine->IsString(0,"alias"))
			{
				MFDebug_Warn(2, "'alias' MUST be the first parameter in a material definition... Ignored.");

MFDebug_Assert(false, "Fix Me!!!");
//				const char *pAlias = pLine->GetString(1);
//				MaterialInternal_InitialiseFromDefinition(pDefIni, pMat, pAlias);
			}
			else
			{
				const char *pParam = pLine->GetString(0);
				const MFMaterialParameterInfo *pInfo = MFMaterial_GetParameterInfoFromName(pMat, pParam);

				int lineArg = 1;
				int param = pInfo->parameterIndex;
				int argIndex = 0;

				switch(pInfo->argIndex.type)
				{
					case MFParamType_Int:
						argIndex = pLine->GetInt(lineArg++);
						break;
					case MFParamType_Enum:
						argIndex = pLine->GetEnum(lineArg++, pInfo->argIndex.pEnumKeys);
						break;
					default:
						argIndex = pInfo->argIndex.defaultValue;
						break;
				}

				switch(pInfo->argIndexHigh.type)
				{
					case MFParamType_Int:
						argIndex |= pLine->GetInt(lineArg++) << 16;
						break;
					case MFParamType_Enum:
						argIndex |= pLine->GetEnum(lineArg++, pInfo->argIndexHigh.pEnumKeys) << 16;
						break;
					default:
						argIndex |= pInfo->argIndexHigh.defaultValue << 16;
						break;
				}

				if(pInfo->numValues == 1)
				{
					switch(pInfo->pValues[0].type)
					{
						case MFParamType_Constant:
						{
							MFMaterial_SetParameterI(pMat, param, argIndex, pInfo->pValues[0].defaultValue);
							break;
						}

						case MFParamType_String:
						{
							const char *pString = pLine->GetString(lineArg);
							MFMaterial_SetParameterS(pMat, param, argIndex, pString);
							break;
						}

						case MFParamType_Float:
						{
							float value = pLine->GetFloat(lineArg);
							MFMaterial_SetParameterF(pMat, param, argIndex, value);
							break;
						}

						case MFParamType_Int:
						{
							int value = pLine->GetStringCount() > lineArg ? pLine->GetInt(lineArg) : pInfo->pValues[0].defaultValue;
							MFMaterial_SetParameterI(pMat, param, argIndex, value);
							break;
						}

						case MFParamType_Enum:
						{
							int value;
							if(pLine->GetStringCount() > lineArg)
								value = pLine->GetEnum(lineArg, pInfo->pValues[0].pEnumKeys);
							else
								value = pInfo->pValues[0].defaultValue;
							MFMaterial_SetParameterI(pMat, param, argIndex, value);
							break;
						}

						case MFParamType_Bool:
						{
							bool value = pLine->GetStringCount() > lineArg ? pLine->GetBool(lineArg) : !!pInfo->pValues[0].defaultValue;
							MFMaterial_SetParameterI(pMat, param, argIndex, value ? 1 : 0);
							break;
						}

						case MFParamType_Colour:
						{
							MFVector vector = pLine->GetColour(lineArg);
							MFMaterial_SetParameterV(pMat, param, argIndex, vector);
							break;
						}

						case MFParamType_Vector3:
						{
							MFVector vector = pLine->GetVector3(lineArg);
							MFMaterial_SetParameterV(pMat, param, argIndex, vector);
							break;
						}

						case MFParamType_Vector4:
						{
							MFVector vector = pLine->GetVector4(lineArg);
							MFMaterial_SetParameterV(pMat, param, argIndex, vector);
							break;
						}

						case MFParamType_Matrix:
						{
							MFDebug_Assert(false, "Cant read a matrix from an ini file... yet...");
							break;
						}

						default:
							MFDebug_Assert(false, "Unknown parameter type..");
					}
				}
				else if(pInfo->numValues > 1)
				{
					// produce a struct representing the args
					MFALIGN_BEGIN(16)
						char argBuffer[256]
					MFALIGN_END(16);
					int offset = 0;

					for(int a=0; a<pInfo->numValues; a++)
					{
						switch(pInfo->pValues[a].type)
						{
							case MFParamType_Constant:
							{
								offset = MFALIGN(offset, sizeof(int));
								(int&)argBuffer[offset] = pInfo->pValues[a].defaultValue;
								offset += sizeof(int);
								break;
							}

							case MFParamType_String:
							{
								offset = MFALIGN(offset, sizeof(const char *));
								(const char *&)argBuffer[offset] = pLine->GetString(lineArg++);
								offset += sizeof(const char *);
								break;
							}

							case MFParamType_Float:
							{
								offset = MFALIGN(offset, sizeof(float));
								(float&)argBuffer[offset] = pLine->GetFloat(lineArg++);
								offset += sizeof(float);
								break;
							}

							case MFParamType_Int:
							{
								offset = MFALIGN(offset, sizeof(int));
								(int&)argBuffer[offset] = pLine->GetInt(lineArg++);
								offset += sizeof(int);
								break;
							}

							case MFParamType_Enum:
							{
								offset = MFALIGN(offset, sizeof(int));
								(int&)argBuffer[offset] = pLine->GetEnum(lineArg++, pInfo->pValues[a].pEnumKeys);
								offset += sizeof(int);
								break;
							}

							case MFParamType_Bool:
							{
								offset = MFALIGN(offset, sizeof(bool));
								(bool&)argBuffer[offset] = pLine->GetBool(lineArg++);
								offset += sizeof(bool);
								break;
							}

							case MFParamType_Colour:
							case MFParamType_Vector3:
							case MFParamType_Vector4:
							case MFParamType_Matrix:
							{
								MFDebug_Assert(false, "Cant read type into structure... yet...");
								break;
							}

							default:
								MFDebug_Assert(false, "Unknown parameter type..");
						}
					}

					MFMaterial_SetParameter(pMat, param, argIndex, (uintp)argBuffer);
				}
			}

			pLine = pLine->Next();
		}
	}
Esempio n. 9
0
MF_API MFVertexDeclaration *MFVertex_CreateVertexDeclaration(const MFVertexElement *pElementArray, int elementCount)
{
	// assign the auto format components before calculating the hash
	MFVertexElement elements[16];
	MFCopyMemory(elements, pElementArray, sizeof(MFVertexElement)*elementCount);
	for(int e=0; e<elementCount; ++e)
	{
		if(pElementArray[e].format == MFVDF_Auto)
			elements[e].format = MFVertex_ChoooseVertexDataTypePlatformSpecific(pElementArray[e].type, pElementArray[e].componentCount);
	}

	uint32 hash = MFUtil_HashBuffer(elements, sizeof(MFVertexElement)*elementCount);

	MFVertexDeclaration *pDecl = (MFVertexDeclaration*)MFResource_Find(hash);
	if(!pDecl)
	{
		pDecl = (MFVertexDeclaration*)MFHeap_AllocAndZero(sizeof(MFVertexDeclaration) + (sizeof(MFVertexElement) + sizeof(MFVertexElementData))*elementCount);

		pDecl->numElements = elementCount;
		pDecl->pElements = (MFVertexElement*)&pDecl[1];
		pDecl->pElementData = (MFVertexElementData*)&pDecl->pElements[elementCount];

		MFCopyMemory(pDecl->pElements, elements, sizeof(MFVertexElement)*elementCount);

		int streamOffsets[16];
		MFZeroMemory(streamOffsets, sizeof(streamOffsets));

		// set the element data and calculate the strides
		for(int e=0; e<elementCount; ++e)
		{
			pDecl->pElementData[e].offset = streamOffsets[elements[e].stream];
			pDecl->pElementData[e].stride = 0;

			streamOffsets[elements[e].stream] += gVertexDataStride[elements[e].format];
			pDecl->streamsUsed |= MFBIT(elements[e].stream);
		}

		// set the strides for each component
		for(int e=0; e<elementCount; ++e)
			pDecl->pElementData[e].stride = streamOffsets[elements[e].stream];

		if(!MFVertex_CreateVertexDeclarationPlatformSpecific(pDecl))
		{
			MFHeap_Free(pDecl);
			return NULL;
		}

		MFResource_AddResource(pDecl, MFRT_VertexDecl, hash);

		if(pDecl->streamsUsed != 1)
		{
			// create the stream declarations...
			MFVertexElement streamElements[64];
			for(int s=0; s<16; ++s)
			{
				if(!(pDecl->streamsUsed & (1 << s)))
					continue;

				int numStreamElements = 0;
				for(int e=0; e<elementCount; ++e)
				{
					if(elements[e].stream == s)
					{
						streamElements[numStreamElements] = elements[e];
						streamElements[numStreamElements].stream = 0;
						++numStreamElements;
					}
				}

				if(numStreamElements)
					pDecl->pStreamDecl[s] = MFVertex_CreateVertexDeclaration(streamElements, numStreamElements);
			}
		}
	}

	return pDecl;
}
Esempio n. 10
0
void MFMidi_InitModulePlatformSpecific()
{
	UINT numInputDevices = midiInGetNumDevs();
	for (UINT i = 0; i < numInputDevices; ++i)
	{
		MIDIINCAPS caps;
		MMRESULT r = midiInGetDevCaps(i, &caps, sizeof(caps));
		if (r != MMSYSERR_NOERROR)
		{
			wchar_t errorBuffer[256];
			midiOutGetErrorText(r, errorBuffer, sizeof(errorBuffer));
			MFDebug_Warn(1, MFStr("Failed to query midi input device: %s", MFString_WCharAsUTF8(errorBuffer)));
			continue;
		}

		MFDevice *pDevice = MFDevice_AllocDevice(MFDT_MidiInput, &DestroyInputDevice);
		pDevice->pInternal = MFHeap_AllocAndZero(sizeof(MFMidiPC_MidiInputDevice));
		pDevice->state = MFDevState_Available;

		MFMidiPC_MidiOutputDevice *pDev = (MFMidiPC_MidiOutputDevice*)pDevice->pInternal;
		pDev->mid = caps.wMid;
		pDev->pid = caps.wPid;

		MFString_CopyUTF16ToUTF8(pDevice->strings[MFDS_ID], caps.szPname);
		MFString_CopyUTF16ToUTF8(pDevice->strings[MFDS_DeviceName], caps.szPname);
		MFString_CopyUTF16ToUTF8(pDevice->strings[MFDS_Description], caps.szPname);
		MFString_CopyUTF16ToUTF8(pDevice->strings[MFDS_InterfaceName], caps.szPname);
//		MFDS_Manufacturer

		MFDebug_Log(0, MFStr("Found midi input device: %s (%04X:%04X) - state: %d", pDevice->strings[MFDS_ID], caps.wMid, caps.wPid, pDevice->state));
	}

	UINT numOutputDevices = midiOutGetNumDevs();
	for (UINT i = 0; i < numOutputDevices; ++i)
	{
		MIDIOUTCAPS caps;
		MMRESULT r = midiOutGetDevCaps(i, &caps, sizeof(caps));
		if (r != MMSYSERR_NOERROR)
		{
			wchar_t errorBuffer[256];
			midiOutGetErrorText(r, errorBuffer, sizeof(errorBuffer));
			MFDebug_Warn(1, MFStr("Failed to query midi output device: %s", MFString_WCharAsUTF8(errorBuffer)));
			continue;
		}

		MFDevice *pDevice = MFDevice_AllocDevice(MFDT_MidiOutput, &DestroyOutputDevice);
		pDevice->pInternal = MFHeap_AllocAndZero(sizeof(MFMidiPC_MidiOutputDevice));
		pDevice->state = MFDevState_Available;

		MFMidiPC_MidiOutputDevice *pDev = (MFMidiPC_MidiOutputDevice*)pDevice->pInternal;
		pDev->mid = caps.wMid;
		pDev->pid = caps.wPid;

		MFString_CopyUTF16ToUTF8(pDevice->strings[MFDS_ID], caps.szPname);
		MFString_CopyUTF16ToUTF8(pDevice->strings[MFDS_DeviceName], caps.szPname);
		MFString_CopyUTF16ToUTF8(pDevice->strings[MFDS_Description], caps.szPname);
		MFString_CopyUTF16ToUTF8(pDevice->strings[MFDS_InterfaceName], caps.szPname);
//		MFDS_Manufacturer

		MFDebug_Log(0, MFStr("Found midi output device: %s (%04X:%04X) - state: %d", pDevice->strings[MFDS_ID], caps.wMid, caps.wPid, pDevice->state));
	}
}
Esempio n. 11
0
void CreateWAVStream(MFAudioStream *pStream, const char *pFilename)
{
	MFCALLSTACK;

	// open wav file
	MFFile* hFile = MFFileSystem_Open(pFilename);
	if(!hFile)
		return;

	// attempt to cache the vorbis stream
	MFOpenDataCachedFile cachedOpen;
	cachedOpen.cbSize = sizeof(MFOpenDataCachedFile);
	cachedOpen.openFlags = MFOF_Read | MFOF_Binary | MFOF_Cached_CleanupBaseFile;
	cachedOpen.maxCacheSize = 256*1024; // 256k cache for wav stream should be heaps!!
	cachedOpen.pBaseFile = hFile;

	MFFile *pCachedFile = MFFile_Open(MFFileSystem_GetInternalFileSystemHandle(MFFSH_CachedFileSystem), &cachedOpen);
	if(pCachedFile)
		hFile = pCachedFile;

	RIFFHeader header;
	MFFile_Read(hFile, &header, sizeof(header));

	if(header.RIFF != MFMAKEFOURCC('R', 'I', 'F', 'F') || header.WAVE != MFMAKEFOURCC('W', 'A', 'V', 'E'))
		return;	// not a .wav file...

	// if everything's good, and it appears to be a valid wav file
	MFWAVStream *pWS = (MFWAVStream*)MFHeap_AllocAndZero(sizeof(MFWAVStream));
	pStream->pStreamData = pWS;
	pWS->pStream = hFile;

	size_t read;
	size_t fptr = sizeof(header);
	do
	{
		MFFile_Seek(hFile, fptr, MFSeek_Begin);

		WAVChunk dataChunk;
		MFZeroMemory(&dataChunk, sizeof(dataChunk));
		read = MFFile_Read(hFile, &dataChunk, sizeof(dataChunk));
		fptr += sizeof(dataChunk) + dataChunk.size;

		if(dataChunk.id == MFMAKEFOURCC('f', 'm', 't', ' '))
		{
			read = MFFile_Read(hFile, &pWS->format, dataChunk.size);
			if(pWS->format.cbSize)
				read = (size_t)MFFile_Seek(hFile, pWS->format.cbSize, MFSeek_Current);
		}
		else if(dataChunk.id == MFMAKEFOURCC('d', 'a', 't', 'a'))
		{
			pWS->dataOffset = (size_t)MFFile_Tell(hFile);
			pWS->dataSize = dataChunk.size;
		}
	}
	while(read);

	// return to the start of the audio data
	MFFile_Seek(pWS->pStream, (int)pWS->dataOffset, MFSeek_Begin);

	// calculate the track length
	pWS->sampleSize = (pWS->format.nChannels * pWS->format.wBitsPerSample) >> 3;
	pStream->trackLength = (float)(pWS->dataSize / pWS->sampleSize) / (float)pWS->format.nSamplesPerSec;

	// fill out the stream info
	pStream->streamInfo.sampleRate = pWS->format.nSamplesPerSec;
	pStream->streamInfo.channels = pWS->format.nChannels;
	pStream->streamInfo.bitsPerSample = pWS->format.wBitsPerSample;
	pStream->streamInfo.bufferLength = pWS->format.nSamplesPerSec;
}