[2/2] d3dx9: Implement D3DXLoadMeshHierarchy for basic mesh hierarchy.

Dylan Smith dylan.ah.smith at gmail.com
Thu May 19 03:03:19 CDT 2011


---
 dlls/d3dx9_36/Makefile.in   |    2 +-
 dlls/d3dx9_36/d3dx9_36.spec |    2 +-
 dlls/d3dx9_36/mesh.c        |  512 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 514 insertions(+), 2 deletions(-)

diff --git a/dlls/d3dx9_36/Makefile.in b/dlls/d3dx9_36/Makefile.in
index 238939e..cba0ea7 100644
--- a/dlls/d3dx9_36/Makefile.in
+++ b/dlls/d3dx9_36/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = d3dx9_36.dll
 IMPORTLIB = d3dx9
-IMPORTS   = d3d9 d3dcompiler ole32 gdi32 user32
+IMPORTS   = d3d9 d3dcompiler dxguid d3dxof ole32 gdi32 user32
 
 C_SRCS = \
 	core.c \
diff --git a/dlls/d3dx9_36/d3dx9_36.spec b/dlls/d3dx9_36/d3dx9_36.spec
index 80f34e2..9f42937 100644
--- a/dlls/d3dx9_36/d3dx9_36.spec
+++ b/dlls/d3dx9_36/d3dx9_36.spec
@@ -174,7 +174,7 @@
 @ stub D3DXLoadMeshFromXW(ptr long ptr ptr ptr ptr ptr ptr)
 @ stub D3DXLoadMeshFromXof(ptr long ptr ptr ptr ptr ptr ptr)
 @ stub D3DXLoadMeshHierarchyFromXA(ptr long ptr ptr ptr ptr ptr)
-@ stub D3DXLoadMeshHierarchyFromXInMemory(ptr long long ptr ptr ptr ptr ptr)
+@ stdcall D3DXLoadMeshHierarchyFromXInMemory(ptr long long ptr ptr ptr ptr ptr)
 @ stub D3DXLoadMeshHierarchyFromXW(ptr long ptr ptr ptr ptr ptr)
 @ stub D3DXLoadPatchMeshFromXof(ptr long ptr ptr ptr long ptr)
 @ stub D3DXLoadPRTBufferFromFileA(ptr ptr)
diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c
index c5954b3..8947b0f 100644
--- a/dlls/d3dx9_36/mesh.c
+++ b/dlls/d3dx9_36/mesh.c
@@ -30,6 +30,10 @@
 #include "windef.h"
 #include "wingdi.h"
 #include "d3dx9.h"
+#undef MAKE_DDHRESULT
+#include "dxfile.h"
+#include "rmxfguid.h"
+#include "rmxftmpl.h"
 #include "wine/debug.h"
 #include "wine/unicode.h"
 #include "d3dx9_36_private.h"
@@ -1809,6 +1813,514 @@ HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD option
 }
 
 
+struct mesh_data {
+    DWORD num_vertices;
+    DWORD num_poly_faces;
+    DWORD num_tri_faces;
+    D3DXVECTOR3 *vertices;
+    DWORD *num_tri_per_face;
+    DWORD *indices;
+
+    DWORD fvf;
+};
+
+static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
+{
+    HRESULT hr;
+    IDirectXFileDataReference *child_ref = NULL;
+    IDirectXFileObject *child_obj = NULL;
+    IDirectXFileData *child_data = NULL;
+
+    hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
+    if (FAILED(hr)) return hr;
+
+    hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
+    if (SUCCEEDED(hr)) {
+        hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
+        IDirectXFileDataReference_Release(child_ref);
+    } else {
+        hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
+    }
+    IDirectXFileObject_Release(child_obj);
+    if (FAILED(hr))
+        return hr;
+
+    hr = IDirectXFileData_GetType(child_data, type);
+    if (FAILED(hr)) {
+        IDirectXFileData_Release(child_data);
+    } else {
+        *child = child_data;
+    }
+
+    return hr;
+}
+
+/* for provide_flags parameters */
+#define PROVIDE_MATERIALS 0x1
+#define PROVIDE_SKININFO  0x2
+
+static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
+{
+    HRESULT hr;
+    DWORD data_size;
+    BYTE *data, *in_ptr;
+    DWORD *index_out_ptr;
+    const GUID *type;
+    IDirectXFileData *child;
+    int i;
+
+    /*
+     * template Mesh {
+     *     DWORD nVertices;
+     *     array Vector vertices[nVertices];
+     *     DWORD nFaces;
+     *     array MeshFace faces[nFaces];
+     *     [ ... ]
+     * }
+     */
+
+    hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
+    if (FAILED(hr)) return hr;
+
+    in_ptr = data;
+    if (data_size < sizeof(DWORD) * 2)
+        goto truncated_data_error;
+    mesh_data->num_vertices = *(DWORD*)in_ptr;
+    if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
+        goto truncated_data_error;
+    in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
+
+    mesh_data->num_poly_faces = *(DWORD*)in_ptr;
+    in_ptr += sizeof(DWORD);
+
+    mesh_data->num_tri_faces = 0;
+    for (i = 0; i < mesh_data->num_poly_faces; i++)
+    {
+        DWORD num_poly_vertices;
+        DWORD j;
+
+        if (data_size - (in_ptr - data) < sizeof(DWORD))
+            goto truncated_data_error;
+        num_poly_vertices = *(DWORD*)in_ptr;
+        in_ptr += sizeof(DWORD);
+        if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
+            goto truncated_data_error;
+        if (num_poly_vertices < 3) {
+            WARN("face %u has only %u vertices\n", i, num_poly_vertices);
+            return E_FAIL;
+        }
+        for (j = 0; j < num_poly_vertices; j++) {
+            if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
+                WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
+                     i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
+                return E_FAIL;
+            }
+            in_ptr += sizeof(DWORD);
+        }
+        mesh_data->num_tri_faces += num_poly_vertices - 2;
+    }
+
+    mesh_data->fvf = D3DFVF_XYZ;
+
+    mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
+            mesh_data->num_vertices * sizeof(*mesh_data->vertices));
+    mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
+            mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
+    mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
+            (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
+    if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
+        return E_OUTOFMEMORY;
+
+    in_ptr = data + sizeof(DWORD);
+    memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
+    in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
+
+    index_out_ptr = mesh_data->indices;
+    for (i = 0; i < mesh_data->num_poly_faces; i++)
+    {
+        DWORD count;
+
+        count = *(DWORD*)in_ptr;
+        in_ptr += sizeof(DWORD);
+        mesh_data->num_tri_per_face[i] = count - 2;
+
+        while (count--) {
+            *index_out_ptr++ = *(DWORD*)in_ptr;
+            in_ptr += sizeof(DWORD);
+        }
+    }
+
+    while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
+    {
+        if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
+            FIXME("Mesh normal loading not implemented.\n");
+            hr = E_NOTIMPL;
+        } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
+            FIXME("Mesh vertex color loading not implemented.\n");
+            hr = E_NOTIMPL;
+        } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
+            FIXME("Mesh texture coordinate loading not implemented.\n");
+            hr = E_NOTIMPL;
+        } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
+                   (provide_flags & PROVIDE_MATERIALS))
+        {
+            FIXME("Mesh material list loading not implemented.\n");
+            hr = E_NOTIMPL;
+        } else if (provide_flags & PROVIDE_SKININFO) {
+            if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
+                FIXME("Skin mesh loading not implemented.\n");
+                hr = E_NOTIMPL;
+            } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
+                /* ignored without XSkinMeshHeader */
+            }
+        }
+        if (FAILED(hr))
+            break;
+    }
+    return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
+truncated_data_error:
+    WARN("truncated data (%u bytes)\n", data_size);
+    return E_FAIL;
+}
+
+/* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
+static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
+                                       DWORD options,
+                                       LPDIRECT3DDEVICE9 device,
+                                       LPD3DXBUFFER *adjacency_out,
+                                       LPD3DXBUFFER *materials_out,
+                                       LPD3DXBUFFER *effects_out,
+                                       DWORD *num_materials_out,
+                                       LPD3DXSKININFO *skin_info_out,
+                                       LPD3DXMESH *mesh_out)
+{
+    HRESULT hr;
+    DWORD *index_in_ptr;
+    struct mesh_data mesh_data;
+    ID3DXMesh *d3dxmesh = NULL;
+    ID3DXBuffer *adjacency = NULL;
+    int i;
+    void *vertices = NULL;
+    void *indices = NULL;
+    BYTE *out_ptr;
+    DWORD provide_flags = 0;
+
+    ZeroMemory(&mesh_data, sizeof(mesh_data));
+
+    if (num_materials_out || materials_out || effects_out)
+        provide_flags |= PROVIDE_MATERIALS;
+    if (skin_info_out)
+        provide_flags |= PROVIDE_SKININFO;
+
+    hr = parse_mesh(filedata, &mesh_data, provide_flags);
+    if (FAILED(hr)) goto cleanup;
+
+    hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, mesh_data.num_vertices, D3DXMESH_MANAGED, mesh_data.fvf, device, &d3dxmesh);
+    if (FAILED(hr)) goto cleanup;
+
+    hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, D3DLOCK_DISCARD, &vertices);
+    if (FAILED(hr)) goto cleanup;
+
+    out_ptr = vertices;
+    for (i = 0; i < mesh_data.num_vertices; i++) {
+        *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
+        out_ptr += sizeof(D3DXVECTOR3);
+    }
+    d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
+
+    hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&indices);
+    if (FAILED(hr)) goto cleanup;
+
+    index_in_ptr = mesh_data.indices;
+#define FILL_INDEX_BUFFER(indices_var) \
+        for (i = 0; i < mesh_data.num_poly_faces; i++) \
+        { \
+            DWORD count = mesh_data.num_tri_per_face[i]; \
+            WORD first_index = *index_in_ptr++; \
+            while (count--) { \
+                *indices_var++ = first_index; \
+                *indices_var++ = *index_in_ptr; \
+                index_in_ptr++; \
+                *indices_var++ = *index_in_ptr; \
+            } \
+            index_in_ptr++; \
+        }
+    if (options & D3DXMESH_32BIT) {
+        DWORD *dword_indices = indices;
+        FILL_INDEX_BUFFER(dword_indices)
+    } else {
+        WORD *word_indices = indices;
+        FILL_INDEX_BUFFER(word_indices)
+    }
+#undef FILL_INDEX_BUFFER
+    d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
+
+    if (adjacency_out) {
+        hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
+        if (FAILED(hr)) goto cleanup;
+        hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
+        if (FAILED(hr)) goto cleanup;
+    }
+
+    *mesh_out = d3dxmesh;
+    if (adjacency_out) *adjacency_out = adjacency;
+    if (num_materials_out) *num_materials_out = 0;
+    if (materials_out) *materials_out = NULL;
+    if (effects_out) *effects_out = NULL;
+    if (skin_info_out) *skin_info_out = NULL;
+
+    hr = D3D_OK;
+cleanup:
+    if (FAILED(hr)) {
+        if (d3dxmesh) IUnknown_Release(d3dxmesh);
+        if (adjacency) ID3DXBuffer_Release(adjacency);
+    }
+    HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
+    HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
+    HeapFree(GetProcessHeap(), 0, mesh_data.indices);
+    return hr;
+}
+
+static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
+{
+    HRESULT hr;
+    DWORD name_len;
+
+    hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
+    if (FAILED(hr)) return hr;
+
+    if (!name_len)
+        name_len++;
+    *name = HeapAlloc(GetProcessHeap(), 0, name_len);
+    if (!*name) return E_OUTOFMEMORY;
+
+    hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
+    if (FAILED(hr))
+        HeapFree(GetProcessHeap(), 0, name);
+    if (!name_len)
+        (*name)[0] = 0;
+
+    return hr;
+}
+
+static HRESULT load_mesh_container(IDirectXFileData *filedata,
+                                   DWORD options,
+                                   LPDIRECT3DDEVICE9 device,
+                                   LPD3DXALLOCATEHIERARCHY alloc_hier,
+                                   D3DXMESHCONTAINER **mesh_container)
+{
+    HRESULT hr;
+    ID3DXBuffer *adjacency = NULL;
+    ID3DXBuffer *materials = NULL;
+    ID3DXBuffer *effects = NULL;
+    ID3DXSkinInfo *skin_info = NULL;
+    D3DXMESHDATA mesh_data;
+    DWORD num_materials = 0;
+    char *name = NULL;
+
+    mesh_data.Type = D3DXMESHTYPE_MESH;
+    mesh_data.u.pMesh = NULL;
+
+    hr = load_skin_mesh_from_xof(filedata, options, device,
+            &adjacency, &materials, &effects, &num_materials,
+            &skin_info, &mesh_data.u.pMesh);
+    if (FAILED(hr)) return hr;
+
+    hr = filedata_get_name(filedata, &name);
+    if (FAILED(hr)) goto cleanup;
+
+    hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
+            materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
+            effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
+            num_materials,
+            adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
+            skin_info, mesh_container);
+
+cleanup:
+    if (materials) ID3DXBuffer_Release(materials);
+    if (effects) ID3DXBuffer_Release(effects);
+    if (adjacency) ID3DXBuffer_Release(adjacency);
+    if (skin_info) IUnknown_Release(skin_info);
+    if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
+    HeapFree(GetProcessHeap(), 0, name);
+    return hr;
+}
+
+static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
+{
+    HRESULT hr;
+    DWORD data_size;
+    BYTE *data;
+
+    /* template Matrix4x4 {
+     *     array FLOAT matrix[16];
+     * }
+     * template FrameTransformMatrix {
+     *     Matrix4x4 frameMatrix;
+     * }
+     */
+
+    hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
+    if (FAILED(hr)) return hr;
+
+    if (data_size != sizeof(D3DXMATRIX)) {
+        WARN("incorrect data size (%u bytes)\n", data_size);
+        return E_FAIL;
+    }
+
+    memcpy(transform, data, sizeof(D3DXMATRIX));
+
+    return D3D_OK;
+}
+
+static HRESULT load_frame(IDirectXFileData *filedata,
+                          DWORD options,
+                          LPDIRECT3DDEVICE9 device,
+                          LPD3DXALLOCATEHIERARCHY alloc_hier,
+                          D3DXFRAME **frame_out)
+{
+    HRESULT hr;
+    const GUID *type;
+    IDirectXFileData *child;
+    char *name = NULL;
+    D3DXFRAME *frame = NULL;
+    D3DXMESHCONTAINER **next_container;
+    D3DXFRAME **next_child;
+
+    hr = filedata_get_name(filedata, &name);
+    if (FAILED(hr)) return hr;
+
+    hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
+    HeapFree(GetProcessHeap(), 0, name);
+    if (FAILED(hr)) return E_FAIL;
+
+    frame = *frame_out;
+    D3DXMatrixIdentity(&frame->TransformationMatrix);
+    next_child = &frame->pFrameFirstChild;
+    next_container = &frame->pMeshContainer;
+
+    while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
+    {
+        if (IsEqualGUID(type, &TID_D3DRMMesh)) {
+            hr = load_mesh_container(child, options, device, alloc_hier, next_container);
+            if (SUCCEEDED(hr))
+                next_container = &(*next_container)->pNextMeshContainer;
+        } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
+            hr = parse_transform_matrix(child, &frame->TransformationMatrix);
+        } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
+            hr = load_frame(child, options, device, alloc_hier, next_child);
+            if (SUCCEEDED(hr))
+                next_child = &(*next_child)->pFrameSibling;
+        }
+        if (FAILED(hr)) break;
+    }
+    if (hr == DXFILEERR_NOMOREOBJECTS)
+        hr = D3D_OK;
+
+    return hr;
+}
+
+HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
+                                                  DWORD memory_size,
+                                                  DWORD options,
+                                                  LPDIRECT3DDEVICE9 device,
+                                                  LPD3DXALLOCATEHIERARCHY alloc_hier,
+                                                  LPD3DXLOADUSERDATA load_user_data,
+                                                  LPD3DXFRAME *frame_hierarchy,
+                                                  LPD3DXANIMATIONCONTROLLER *anim_controller)
+{
+    HRESULT hr;
+    IDirectXFile *dxfile = NULL;
+    IDirectXFileEnumObject *enumobj = NULL;
+    IDirectXFileData *filedata = NULL;
+    DXFILELOADMEMORY source;
+    D3DXFRAME *first_frame = NULL;
+    D3DXFRAME **next_frame = &first_frame;
+
+    TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
+          device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
+
+    if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
+        return D3DERR_INVALIDCALL;
+    if (load_user_data || anim_controller) {
+        if (load_user_data)
+            FIXME("Loading user data not implemented\n");
+        if (anim_controller)
+            FIXME("Animation controller creation not implemented\n");
+        return E_NOTIMPL;
+    }
+
+    hr = DirectXFileCreate(&dxfile);
+    if (FAILED(hr)) goto cleanup;
+
+    hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
+    if (FAILED(hr)) goto cleanup;
+
+    source.lpMemory = (void*)memory;
+    source.dSize = memory_size;
+    hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
+    if (FAILED(hr)) goto cleanup;
+
+    while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
+    {
+        const GUID *guid = NULL;
+
+        hr = IDirectXFileData_GetType(filedata, &guid);
+        if (SUCCEEDED(hr)) {
+            if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
+
+                hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
+                if (FAILED(hr)) {
+                    hr = E_FAIL;
+                    goto cleanup;
+                }
+
+                D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
+
+                hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
+                if (FAILED(hr)) goto cleanup;
+            } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
+                hr = load_frame(filedata, options, device, alloc_hier, next_frame);
+                if (FAILED(hr)) goto cleanup;
+            }
+            while (*next_frame)
+                next_frame = &(*next_frame)->pFrameSibling;
+        }
+
+        IDirectXFileData_Release(filedata);
+        filedata = NULL;
+        if (FAILED(hr))
+            goto cleanup;
+    }
+    if (hr != DXFILEERR_NOMOREOBJECTS)
+        goto cleanup;
+
+    if (!first_frame) {
+        hr = E_FAIL;
+    } else if (first_frame->pFrameSibling) {
+        D3DXFRAME *root_frame = NULL;
+        hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
+        if (FAILED(hr)) {
+            hr = E_FAIL;
+            goto cleanup;
+        }
+        D3DXMatrixIdentity(&root_frame->TransformationMatrix);
+        root_frame->pFrameFirstChild = first_frame;
+        *frame_hierarchy = root_frame;
+        hr = D3D_OK;
+    } else {
+        *frame_hierarchy = first_frame;
+        hr = D3D_OK;
+    }
+
+cleanup:
+    if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
+    if (filedata) IDirectXFileData_Release(filedata);
+    if (enumobj) IDirectXFileEnumObject_Release(enumobj);
+    if (dxfile) IDirectXFile_Release(dxfile);
+    return hr;
+}
+
 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
 {
     HRESULT hr;
-- 
1.7.4.1



More information about the wine-patches mailing list