From eda32948987d81c0df777fbe997b2ce8eaa4cbd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Fri, 26 Feb 2016 03:54:28 +0100
Subject: mmsystem.dll16: Refcount midihdr to work around buggy application
 which unprepares buffer during a callback.

---
 dlls/mmsystem.dll16/message16.c | 121 ++++++++++++++++++++++++----------------
 1 file changed, 73 insertions(+), 48 deletions(-)

diff --git a/dlls/mmsystem.dll16/message16.c b/dlls/mmsystem.dll16/message16.c
index 012d3dc..26e47b2 100644
--- a/dlls/mmsystem.dll16/message16.c
+++ b/dlls/mmsystem.dll16/message16.c
@@ -33,6 +33,13 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
 
+struct mihdrWrap
+{
+    int ref;
+    SEGPTR mh16;
+    MIDIHDR hdr;
+};
+
 /* =================================
  *       A U X    M A P P E R S
  * ================================= */
@@ -117,13 +124,13 @@ static  void            	MMSYSTDRV_MidiIn_MapCB(UINT uMsg, DWORD_PTR* dwUser, DW
     case MIM_LONGDATA:
     case MIM_LONGERROR:
         {
-	    LPMIDIHDR		mh32 = (LPMIDIHDR)(*dwParam1);
-	    SEGPTR		segmh16 = *(SEGPTR*)((LPSTR)mh32 - sizeof(LPMIDIHDR));
-	    LPMIDIHDR16		mh16 = MapSL(segmh16);
+	        struct mihdrWrap   *mh32 = CONTAINING_RECORD((MIDIHDR *)*dwParam1, struct mihdrWrap, hdr);
+	        SEGPTR              segmh16 = mh32->mh16;
+	        LPMIDIHDR16         mh16 = MapSL(segmh16);
 
-	    *dwParam1 = (DWORD)segmh16;
-	    mh16->dwFlags = mh32->dwFlags;
-	    mh16->dwBytesRecorded = mh32->dwBytesRecorded;
+	        *dwParam1 = (DWORD)segmh16;
+	        mh16->dwFlags = mh32->hdr.dwFlags;
+	        mh16->dwBytesRecorded = mh32->hdr.dwBytesRecorded;
 	}
 	break;
     default:
@@ -175,41 +182,48 @@ static MMSYSTEM_MapType	MMSYSTDRV_MidiOut_Map16To32W  (UINT wMsg, DWORD_PTR* lpP
 	break;
     case MODM_PREPARE:
 	{
-	    LPMIDIHDR		mh32 = HeapAlloc(GetProcessHeap(), 0, sizeof(LPMIDIHDR) + sizeof(MIDIHDR));
-	    LPMIDIHDR16		mh16 = MapSL(*lpParam1);
-
-	    if (mh32) {
-		*(LPMIDIHDR*)mh32 = (LPMIDIHDR)*lpParam1;
-		mh32 = (LPMIDIHDR)((LPSTR)mh32 + sizeof(LPMIDIHDR));
-		mh32->lpData = MapSL((SEGPTR)mh16->lpData);
-		mh32->dwBufferLength = mh16->dwBufferLength;
-		mh32->dwBytesRecorded = mh16->dwBytesRecorded;
-		mh32->dwUser = mh16->dwUser;
-		mh32->dwFlags = mh16->dwFlags;
-		mh16->lpNext = (MIDIHDR16*)mh32; /* for reuse in unprepare and write */
-		*lpParam1 = (DWORD)mh32;
-		*lpParam2 = offsetof(MIDIHDR,dwOffset); /* old size, without dwOffset */
+        struct mihdrWrap   *mh32 = HeapAlloc(GetProcessHeap(), 0, sizeof(struct mihdrWrap));
+        LPMIDIHDR16         mh16 = MapSL(*lpParam1);
 
-		ret = MMSYSTEM_MAP_OKMEM;
-	    } else {
-		ret = MMSYSTEM_MAP_NOMEM;
-	    }
+        if (mh32)
+        {
+            mh32->ref = 2;
+            mh32->mh16 = (SEGPTR)*lpParam1;
+            mh32->hdr.lpData = MapSL((SEGPTR)mh16->lpData);
+            mh32->hdr.dwBufferLength = mh16->dwBufferLength;
+            mh32->hdr.dwBytesRecorded = mh16->dwBytesRecorded;
+            mh32->hdr.dwUser = mh16->dwUser;
+            mh32->hdr.dwFlags = mh16->dwFlags;
+            mh16->lpNext = (MIDIHDR16*)mh32; /* for reuse in unprepare and write */
+            *lpParam1 = (DWORD)&mh32->hdr;
+            *lpParam2 = offsetof(MIDIHDR,dwOffset); /* old size, without dwOffset */
+
+            ret = MMSYSTEM_MAP_OKMEM;
+        }
+        else
+            ret = MMSYSTEM_MAP_NOMEM;
 	}
 	break;
     case MODM_UNPREPARE:
     case MODM_LONGDATA:
 	{
-	    LPMIDIHDR16		mh16 = MapSL(*lpParam1);
-	    LPMIDIHDR		mh32 = (MIDIHDR*)mh16->lpNext;
+        LPMIDIHDR16         mh16 = MapSL(*lpParam1);
+        struct mihdrWrap   *mh32 = (struct mihdrWrap*)mh16->lpNext;
+
+        mh32->ref++;
+
+        *lpParam1 = (DWORD)&mh32->hdr;
+        *lpParam2 = offsetof(MIDIHDR,dwOffset);
+
+        /* dwBufferLength can be reduced between prepare & write */
+        if (wMsg == MODM_LONGDATA && mh32->hdr.dwBufferLength < mh16->dwBufferLength)
+        {
+            ERR("Size of buffer has been increased from %d to %d, keeping initial value\n",
+                mh32->hdr.dwBufferLength, mh16->dwBufferLength);
+        }
+        else
+            mh32->hdr.dwBufferLength = mh16->dwBufferLength;
 
-	    *lpParam1 = (DWORD)mh32;
-	    *lpParam2 = offsetof(MIDIHDR,dwOffset);
-	    /* dwBufferLength can be reduced between prepare & write */
-	    if (wMsg == MODM_LONGDATA && mh32->dwBufferLength < mh16->dwBufferLength) {
-		ERR("Size of buffer has been increased from %d to %d, keeping initial value\n",
-		    mh32->dwBufferLength, mh16->dwBufferLength);
-	    } else
-                mh32->dwBufferLength = mh16->dwBufferLength;
 	    ret = MMSYSTEM_MAP_OKMEM;
 	}
 	break;
@@ -267,17 +281,28 @@ static  MMSYSTEM_MapType	MMSYSTDRV_MidiOut_UnMap16To32W(UINT wMsg, DWORD_PTR* lp
     case MODM_UNPREPARE:
     case MODM_LONGDATA:
 	{
-	    LPMIDIHDR		mh32 = (LPMIDIHDR)(*lpParam1);
-	    LPMIDIHDR16		mh16 = MapSL(*(SEGPTR*)((LPSTR)mh32 - sizeof(LPMIDIHDR)));
+        struct mihdrWrap   *mh32 = CONTAINING_RECORD((MIDIHDR *)*lpParam1, struct mihdrWrap, hdr);
+        LPMIDIHDR16         mh16;
 
-	    assert((MIDIHDR*)mh16->lpNext == mh32);
-	    mh16->dwFlags = mh32->dwFlags;
+        /* Prosound unprepares the buffer during a callback */
+        if (mh32->mh16)
+        {
+            mh16 = MapSL(mh32->mh16);
+            assert((struct mihdrWrap *)mh16->lpNext == mh32);
+            mh16->dwFlags = mh32->hdr.dwFlags;
 
-	    if (wMsg == MODM_UNPREPARE && fn_ret == MMSYSERR_NOERROR) {
-		HeapFree(GetProcessHeap(), 0, (LPSTR)mh32 - sizeof(LPMIDIHDR));
-		mh16->lpNext = 0;
-	    }
-	    ret = MMSYSTEM_MAP_OK;
+            if (wMsg == MODM_UNPREPARE && fn_ret == MMSYSERR_NOERROR)
+            {
+                mh32->mh16 = 0;
+                mh32->ref--;
+                mh16->lpNext = 0;
+            }
+        }
+
+        if (!--mh32->ref)
+            HeapFree(GetProcessHeap(), 0, mh32);
+
+        ret = MMSYSTEM_MAP_OK;
 	}
 	break;
 
@@ -307,12 +332,12 @@ static  void MMSYSTDRV_MidiOut_MapCB(UINT uMsg, DWORD_PTR* dwUser, DWORD_PTR* dw
     case MOM_DONE:
         {
 	    /* initial map is: 16 => 32 */
-	    LPMIDIHDR		mh32 = (LPMIDIHDR)(*dwParam1);
-	    SEGPTR		segmh16 = *(SEGPTR*)((LPSTR)mh32 - sizeof(LPMIDIHDR));
-	    LPMIDIHDR16		mh16 = MapSL(segmh16);
+        struct mihdrWrap   *mh32 = CONTAINING_RECORD((MIDIHDR *)*dwParam1, struct mihdrWrap, hdr);
+        SEGPTR              segmh16 = mh32->mh16;
+        LPMIDIHDR16         mh16 = MapSL(segmh16);
 
-	    *dwParam1 = (DWORD)segmh16;
-	    mh16->dwFlags = mh32->dwFlags;
+        *dwParam1 = (DWORD)segmh16;
+        mh16->dwFlags = mh32->hdr.dwFlags;
 	}
 	break;
     default:
-- 
2.7.1

