set svn:eol-style to native
[reactos.git] / reactos / lib / winmm / mmio.c
index ab13649..8cf05e9 100644 (file)
-/*\r
- * MMIO functions\r
- *\r
- * Copyright 1998 Andrew Taylor\r
- * Copyright 1998 Ove Kåven\r
- * Copyright 2000,2002 Eric Pouech\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
- */\r
-\r
-/* Still to be done:\r
- *     + correct handling of global/local IOProcs (and temporary IOProcs)\r
- *     + mode of mmio objects is not used (read vs write vs readwrite)\r
- *     + thread safeness\r
- *      + 32 A <=> W message mapping\r
- */\r
-\r
-\r
-#include <ctype.h>\r
-#include <stdarg.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <errno.h>\r
-#include <assert.h>\r
-\r
-#include "windef.h"\r
-#include "winbase.h"\r
-#include "winnls.h"\r
-#include "mmsystem.h"\r
-#include "winemm.h"\r
-\r
-#include "wine/debug.h"\r
-\r
-WINE_DEFAULT_DEBUG_CHANNEL(mmio);\r
-\r
-LRESULT         (*pFnMmioCallback16)(DWORD,LPMMIOINFO,UINT,LPARAM,LPARAM) /* = NULL */;\r
-\r
-/**************************************************************************\r
- *                     mmioDosIOProc                           [internal]\r
- */\r
-static LRESULT CALLBACK mmioDosIOProc(LPMMIOINFO lpmmioinfo, UINT uMessage,\r
-                                     LPARAM lParam1, LPARAM lParam2)\r
-{\r
-    LRESULT    ret = MMSYSERR_NOERROR;\r
-\r
-    TRACE("(%p, %X, 0x%lx, 0x%lx);\n", lpmmioinfo, uMessage, lParam1, lParam2);\r
-\r
-    switch (uMessage) {\r
-    case MMIOM_OPEN:\r
-       {\r
-           /* Parameters:\r
-            * lParam1 = szFileName parameter from mmioOpen\r
-            * lParam2 = reserved\r
-            * Returns: zero on success, error code on error\r
-            * NOTE: lDiskOffset automatically set to zero\r
-            */\r
-           LPCSTR      szFileName = (LPCSTR)lParam1;\r
-\r
-           if (lpmmioinfo->dwFlags & MMIO_GETTEMP) {\r
-               FIXME("MMIO_GETTEMP not implemented\n");\r
-               return MMIOERR_CANNOTOPEN;\r
-           }\r
-\r
-           /* if filename NULL, assume open file handle in adwInfo[0] */\r
-           if (szFileName) {\r
-                OFSTRUCT    ofs;\r
-                lpmmioinfo->adwInfo[0] = (DWORD)OpenFile(szFileName, &ofs, lpmmioinfo->dwFlags & 0xFFFF);\r
-            }\r
-           if (lpmmioinfo->adwInfo[0] == (DWORD)HFILE_ERROR)\r
-               ret = MMIOERR_CANNOTOPEN;\r
-       }\r
-       break;\r
-\r
-    case MMIOM_CLOSE:\r
-       /* Parameters:\r
-        * lParam1 = wFlags parameter from mmioClose\r
-        * lParam2 = unused\r
-        * Returns: zero on success, error code on error\r
-        */\r
-       if (!(lParam1 & MMIO_FHOPEN))\r
-           _lclose((HFILE)lpmmioinfo->adwInfo[0]);\r
-       break;\r
-\r
-    case MMIOM_READ:\r
-       /* Parameters:\r
-        * lParam1 = huge pointer to read buffer\r
-        * lParam2 = number of bytes to read\r
-        * Returns: number of bytes read, 0 for EOF, -1 for error (error code\r
-        *         in wErrorRet)\r
-        */\r
-       ret = _lread((HFILE)lpmmioinfo->adwInfo[0], (HPSTR)lParam1, (LONG)lParam2);\r
-       if (ret != -1)\r
-           lpmmioinfo->lDiskOffset += ret;\r
-\r
-       break;\r
-\r
-    case MMIOM_WRITE:\r
-    case MMIOM_WRITEFLUSH:\r
-       /* no internal buffering, so WRITEFLUSH handled same as WRITE */\r
-\r
-       /* Parameters:\r
-        * lParam1 = huge pointer to write buffer\r
-        * lParam2 = number of bytes to write\r
-        * Returns: number of bytes written, -1 for error (error code in\r
-        *              wErrorRet)\r
-        */\r
-       ret = _hwrite((HFILE)lpmmioinfo->adwInfo[0], (HPSTR)lParam1, (LONG)lParam2);\r
-       if (ret != -1)\r
-           lpmmioinfo->lDiskOffset += ret;\r
-       break;\r
-\r
-    case MMIOM_SEEK:\r
-       /* Parameters:\r
-        * lParam1 = new position\r
-        * lParam2 = from whence to seek (SEEK_SET, SEEK_CUR, SEEK_END)\r
-        * Returns: new file postion, -1 on error\r
-        */\r
-       ret = _llseek((HFILE)lpmmioinfo->adwInfo[0], (LONG)lParam1, (LONG)lParam2);\r
-       if (ret != -1)\r
-           lpmmioinfo->lDiskOffset = ret;\r
-       return ret;\r
-\r
-    case MMIOM_RENAME:\r
-       /* Parameters:\r
-        * lParam1 = old name\r
-        * lParam2 = new name\r
-        * Returns: zero on success, non-zero on failure\r
-        */\r
-        if (!MoveFileA((const char*)lParam1, (const char*)lParam2))\r
-            ret = MMIOERR_FILENOTFOUND;\r
-        break;\r
-\r
-    default:\r
-       FIXME("unexpected message %u\n", uMessage);\r
-       return 0;\r
-    }\r
-\r
-    return ret;\r
-}\r
-\r
-/**************************************************************************\r
- *                     mmioMemIOProc                           [internal]\r
- */\r
-static LRESULT CALLBACK mmioMemIOProc(LPMMIOINFO lpmmioinfo, UINT uMessage,\r
-                                     LPARAM lParam1, LPARAM lParam2)\r
-{\r
-    TRACE("(%p,0x%04x,0x%08lx,0x%08lx)\n", lpmmioinfo, uMessage, lParam1, lParam2);\r
-\r
-    switch (uMessage) {\r
-\r
-    case MMIOM_OPEN:\r
-       /* Parameters:\r
-        * lParam1 = filename (must be NULL)\r
-        * lParam2 = reserved\r
-        * Returns: zero on success, error code on error\r
-        * NOTE: lDiskOffset automatically set to zero\r
-        */\r
-       /* FIXME: io proc shouldn't change it */\r
-       if (!(lpmmioinfo->dwFlags & MMIO_CREATE))\r
-           lpmmioinfo->pchEndRead = lpmmioinfo->pchEndWrite;\r
-        lpmmioinfo->adwInfo[0] = HFILE_ERROR;\r
-       return 0;\r
-\r
-    case MMIOM_CLOSE:\r
-       /* Parameters:\r
-        * lParam1 = wFlags parameter from mmioClose\r
-        * lParam2 = unused\r
-        * Returns: zero on success, error code on error\r
-        */\r
-       return 0;\r
-\r
-    case MMIOM_READ:\r
-       /* Parameters:\r
-        * lParam1 = huge pointer to read buffer\r
-        * lParam2 = number of bytes to read\r
-        * Returns: number of bytes read, 0 for EOF, -1 for error (error code\r
-        *         in wErrorRet)\r
-        * NOTE: lDiskOffset should be updated\r
-        */\r
-       FIXME("MMIOM_READ on memory files should not occur, buffer may be lost!\n");\r
-       return 0;\r
-\r
-    case MMIOM_WRITE:\r
-    case MMIOM_WRITEFLUSH:\r
-       /* no internal buffering, so WRITEFLUSH handled same as WRITE */\r
-\r
-       /* Parameters:\r
-        * lParam1 = huge pointer to write buffer\r
-        * lParam2 = number of bytes to write\r
-        * Returns: number of bytes written, -1 for error (error code in\r
-        *              wErrorRet)\r
-        * NOTE: lDiskOffset should be updated\r
-        */\r
-       FIXME("MMIOM_WRITE on memory files should not occur, buffer may be lost!\n");\r
-       return 0;\r
-\r
-    case MMIOM_SEEK:\r
-       /* Parameters:\r
-        * lParam1 = new position\r
-        * lParam2 = from whence to seek (SEEK_SET, SEEK_CUR, SEEK_END)\r
-        * Returns: new file postion, -1 on error\r
-        * NOTE: lDiskOffset should be updated\r
-        */\r
-       FIXME("MMIOM_SEEK on memory files should not occur, buffer may be lost!\n");\r
-       return -1;\r
-\r
-    default:\r
-       FIXME("unexpected message %u\n", uMessage);\r
-       return 0;\r
-    }\r
-}\r
-\r
-/* This array will be the entire list for most apps \r
- * Note that temporary ioProcs will be stored with a 0 fourCC code\r
- */\r
-\r
-static struct IOProcList defaultProcs[] = {\r
-    {&defaultProcs[1], FOURCC_DOS, (LPMMIOPROC)mmioDosIOProc, MMIO_PROC_32A, 0},\r
-    {NULL,             FOURCC_MEM, (LPMMIOPROC)mmioMemIOProc, MMIO_PROC_32A, 0},\r
-};\r
-\r
-static struct IOProcList*      pIOProcListAnchor = &defaultProcs[0];\r
-\r
-/****************************************************************\r
- *             MMIO_FindProcNode                       [INTERNAL]\r
- *\r
- * Finds the ProcList node associated with a given FOURCC code.\r
- */\r
-static struct IOProcList*      MMIO_FindProcNode(FOURCC fccIOProc)\r
-{\r
-    struct IOProcList* pListNode;\r
-\r
-    for (pListNode = pIOProcListAnchor; pListNode; pListNode = pListNode->pNext) {\r
-       if (pListNode->fourCC == fccIOProc) {\r
-           return pListNode;\r
-       }\r
-    }\r
-    return NULL;\r
-}\r
-\r
-/****************************************************************\r
- *             MMIO_InstallIOProc                      [INTERNAL]\r
- */\r
-LPMMIOPROC MMIO_InstallIOProc(FOURCC fccIOProc, LPMMIOPROC pIOProc,\r
-                              DWORD dwFlags, enum mmioProcType type)\r
-{\r
-    LPMMIOPROC         lpProc = NULL;\r
-    struct IOProcList*  pListNode;\r
-    struct IOProcList** ppListNode;\r
-\r
-    TRACE("(%08lx, %p, %08lX, %i)\n", fccIOProc, pIOProc, dwFlags, type);\r
-\r
-    if (dwFlags & MMIO_GLOBALPROC)\r
-       FIXME("Global procedures not implemented\n");\r
-\r
-    /* just handle the known procedures for now */\r
-    switch (dwFlags & (MMIO_INSTALLPROC|MMIO_REMOVEPROC|MMIO_FINDPROC)) {\r
-    case MMIO_INSTALLPROC:\r
-       /* Create new entry for the IOProc list */\r
-       pListNode = HeapAlloc(GetProcessHeap(), 0, sizeof(*pListNode));\r
-       if (pListNode) {\r
-           /* Fill in this node */\r
-           pListNode->fourCC = fccIOProc;\r
-           pListNode->pIOProc = pIOProc;\r
-           pListNode->type = type;\r
-           pListNode->count = 0;\r
-\r
-           /* Stick it on the end of the list */\r
-           pListNode->pNext = pIOProcListAnchor;\r
-           pIOProcListAnchor = pListNode;\r
-\r
-           /* Return this IOProc - that's how the caller knows we succeeded */\r
-           lpProc = pIOProc;\r
-       }\r
-       break;\r
-\r
-    case MMIO_REMOVEPROC:\r
-       /*\r
-        * Search for the node that we're trying to remove\r
-         * We search for a matching fourCC code if it's non null, or the proc\r
-         * address otherwise\r
-         * note that this method won't find the first item on the list, but\r
-        * since the first two items on this list are ones we won't\r
-        * let the user delete anyway, that's okay\r
-        */\r
-       ppListNode = &pIOProcListAnchor;\r
-       while ((*ppListNode) && \r
-               ((fccIOProc != 0) ? \r
-                (*ppListNode)->fourCC != fccIOProc : \r
-                (*ppListNode)->pIOProc != pIOProc))\r
-           ppListNode = &((*ppListNode)->pNext);\r
-\r
-       if (*ppListNode) { /* found it */\r
-           /* FIXME: what should be done if an open mmio object uses this proc ?\r
-            * shall we return an error, nuke the mmio object ?\r
-            */\r
-           if ((*ppListNode)->count) {\r
-               ERR("Cannot remove a mmIOProc while in use\n");\r
-               break;\r
-           }\r
-           /* remove it, but only if it isn't builtin */\r
-           if ((*ppListNode) >= defaultProcs &&\r
-               (*ppListNode) < defaultProcs + sizeof(defaultProcs)) {\r
-               WARN("Tried to remove built-in mmio proc. Skipping\n");\r
-           } else {\r
-               /* Okay, nuke it */\r
-               struct IOProcList*  ptmpNode = *ppListNode;\r
-               lpProc = (*ppListNode)->pIOProc;\r
-               *ppListNode = (*ppListNode)->pNext;\r
-               HeapFree(GetProcessHeap(), 0, ptmpNode);\r
-           }\r
-       }\r
-       break;\r
-\r
-    case MMIO_FINDPROC:\r
-       if ((pListNode = MMIO_FindProcNode(fccIOProc))) {\r
-           lpProc = pListNode->pIOProc;\r
-       }\r
-       break;\r
-    }\r
-\r
-    return lpProc;\r
-}\r
-\r
-/****************************************************************\r
- *             send_message                            [INTERNAL]\r
- */\r
-static LRESULT send_message(struct IOProcList* ioProc, LPMMIOINFO mmioinfo,\r
-                             DWORD wMsg, LPARAM lParam1,\r
-                             LPARAM lParam2, enum mmioProcType type)\r
-{\r
-    LRESULT            result = MMSYSERR_ERROR;\r
-    LPARAM             lp1 = lParam1, lp2 = lParam2;\r
-\r
-    if (!ioProc) {\r
-       ERR("brrr\n");\r
-       result = MMSYSERR_INVALPARAM;\r
-    }\r
-\r
-    switch (ioProc->type) {\r
-    case MMIO_PROC_16:\r
-        if (pFnMmioCallback16)\r
-            result = pFnMmioCallback16((DWORD)ioProc->pIOProc,\r
-                                       mmioinfo, wMsg, lp1, lp2);\r
-        break;\r
-    case MMIO_PROC_32A:\r
-    case MMIO_PROC_32W:\r
-       if (ioProc->type != type) {\r
-           /* map (lParam1, lParam2) into (lp1, lp2) 32 A<=>W */\r
-           FIXME("NIY 32 A<=>W mapping\n");\r
-       }\r
-       result = (ioProc->pIOProc)((LPSTR)mmioinfo, wMsg, lp1, lp2);\r
-\r
-#if 0\r
-       if (ioProc->type != type) {\r
-           /* unmap (lParam1, lParam2) into (lp1, lp2) 32 A<=>W */\r
-       }\r
-#endif\r
-       break;\r
-    default:\r
-       FIXME("Internal error\n");\r
-    }\r
-\r
-    return result;\r
-}\r
-\r
-/**************************************************************************\r
- *                             MMIO_ParseExtA                  [internal]\r
- *\r
- * Parses a filename for the extension.\r
- *\r
- * RETURNS\r
- *  The FOURCC code for the extension if found, else 0.\r
- */\r
-static FOURCC MMIO_ParseExtA(LPCSTR szFileName)\r
-{\r
-    /* Filenames are of the form file.ext{+ABC}\r
-       For now, we take the last '+' if present */\r
-\r
-    FOURCC ret = 0;\r
-\r
-    /* Note that ext{Start,End} point to the . and + respectively */\r
-    LPSTR extEnd;\r
-    LPSTR extStart;\r
-\r
-    TRACE("(%s)\n", debugstr_a(szFileName));\r
-\r
-    if (!szFileName)\r
-       return ret;\r
-\r
-    /* Find the last '.' */\r
-    extStart = strrchr(szFileName,'.');\r
-\r
-    if (!extStart) {\r
-         ERR("No . in szFileName: %s\n", debugstr_a(szFileName));\r
-    } else {\r
-        CHAR ext[5];\r
-\r
-        /* Find the '+' afterwards */\r
-        extEnd = strchr(extStart,'+');\r
-        if (extEnd) {\r
-\r
-            if (extEnd - extStart - 1 > 4)\r
-                WARN("Extension length > 4\n");\r
-            lstrcpynA(ext, extStart + 1, min(extEnd-extStart,5));\r
-\r
-        } else {\r
-            /* No + so just an extension */\r
-            if (strlen(extStart) > 4) {\r
-                WARN("Extension length > 4\n");\r
-            }\r
-            lstrcpynA(ext, extStart + 1, 5);\r
-        }\r
-        TRACE("Got extension: %s\n", debugstr_a(ext));\r
-\r
-        /* FOURCC codes identifying file-extensions must be uppercase */\r
-        ret = mmioStringToFOURCCA(ext, MMIO_TOUPPER);\r
-    }\r
-    return ret;\r
-}\r
-\r
-/**************************************************************************\r
- *                             MMIO_Get                        [internal]\r
- *\r
- * Retrieves the mmio object from current process\r
- */\r
-LPWINE_MMIO    MMIO_Get(HMMIO h)\r
-{\r
-    LPWINE_MMIO                wm = NULL;\r
-\r
-    EnterCriticalSection(&WINMM_IData.cs);\r
-    for (wm = WINMM_IData.lpMMIO; wm; wm = wm->lpNext) {\r
-       if (wm->info.hmmio == h)\r
-           break;\r
-    }\r
-    LeaveCriticalSection(&WINMM_IData.cs);\r
-    return wm;\r
-}\r
-\r
-/**************************************************************************\r
- *                             MMIO_Create                     [internal]\r
- *\r
- * Creates an internal representation for a mmio instance\r
- */\r
-static LPWINE_MMIO             MMIO_Create(void)\r
-{\r
-    static     WORD    MMIO_counter = 0;\r
-    LPWINE_MMIO                wm;\r
-\r
-    wm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MMIO));\r
-    if (wm) {\r
-       EnterCriticalSection(&WINMM_IData.cs);\r
-        /* lookup next unallocated WORD handle, with a non NULL value */\r
-       while (++MMIO_counter == 0 || MMIO_Get((HMMIO)(ULONG_PTR)MMIO_counter));\r
-       wm->info.hmmio = (HMMIO)(ULONG_PTR)MMIO_counter;\r
-       wm->lpNext = WINMM_IData.lpMMIO;\r
-       WINMM_IData.lpMMIO = wm;\r
-       LeaveCriticalSection(&WINMM_IData.cs);\r
-    }\r
-    return wm;\r
-}\r
-\r
-/**************************************************************************\r
- *                             MMIO_Destroy                    [internal]\r
- *-\r
- * Destroys an internal representation for a mmio instance\r
- */\r
-static BOOL            MMIO_Destroy(LPWINE_MMIO wm)\r
-{\r
-    LPWINE_MMIO*       m;\r
-\r
-    EnterCriticalSection(&WINMM_IData.cs);\r
-    /* search for the matching one... */\r
-    m = &(WINMM_IData.lpMMIO);\r
-    while (*m && *m != wm) m = &(*m)->lpNext;\r
-    /* ...and destroy */\r
-    if (*m) {\r
-       *m = (*m)->lpNext;\r
-       HeapFree(GetProcessHeap(), 0, wm);\r
-       wm = NULL;\r
-    }\r
-    LeaveCriticalSection(&WINMM_IData.cs);\r
-    return wm ? FALSE : TRUE;\r
-}\r
-\r
-/****************************************************************\r
- *                     MMIO_Flush                      [INTERNAL]\r
- */\r
-static MMRESULT MMIO_Flush(WINE_MMIO* wm, UINT uFlags)\r
-{\r
-    if (wm->info.cchBuffer && (wm->info.fccIOProc != FOURCC_MEM)) {\r
-       /* not quite sure what to do here, but I'll guess */\r
-       if (wm->info.dwFlags & MMIO_DIRTY) {\r
-            /* FIXME: error handling */\r
-           send_message(wm->ioProc, &wm->info, MMIOM_SEEK, \r
-                         wm->info.lBufOffset, SEEK_SET, MMIO_PROC_32A);\r
-           send_message(wm->ioProc, &wm->info, MMIOM_WRITE, \r
-                         (LPARAM)wm->info.pchBuffer,\r
-                         wm->info.pchNext - wm->info.pchBuffer, MMIO_PROC_32A);\r
-       }\r
-       if (uFlags & MMIO_EMPTYBUF)\r
-           wm->info.pchNext = wm->info.pchEndRead = wm->info.pchBuffer;\r
-    }\r
-    wm->info.dwFlags &= ~MMIO_DIRTY;\r
-\r
-    return MMSYSERR_NOERROR;\r
-}\r
-\r
-/***************************************************************************\r
- *                     MMIO_GrabNextBuffer                     [INTERNAL]\r
- */\r
-static LONG    MMIO_GrabNextBuffer(LPWINE_MMIO wm, int for_read)\r
-{\r
-    LONG       size = wm->info.cchBuffer;\r
-\r
-    TRACE("bo=%lx do=%lx of=%lx\n",\r
-         wm->info.lBufOffset, wm->info.lDiskOffset,\r
-         send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 0, SEEK_CUR, MMIO_PROC_32A));\r
-\r
-    wm->info.lBufOffset = wm->info.lDiskOffset;\r
-    wm->info.pchNext = wm->info.pchBuffer;\r
-    wm->info.pchEndRead = wm->info.pchBuffer;\r
-    wm->info.pchEndWrite = wm->info.pchBuffer + wm->info.cchBuffer;\r
-\r
-    wm->bBufferLoaded = TRUE;\r
-    if (for_read) {\r
-       size = send_message(wm->ioProc, &wm->info, MMIOM_READ, \r
-                            (LPARAM)wm->info.pchBuffer, size, MMIO_PROC_32A);\r
-       if (size > 0)\r
-           wm->info.pchEndRead += size;\r
-        else\r
-            wm->bBufferLoaded = FALSE;\r
-    }\r
-\r
-    return size;\r
-}\r
-\r
-/***************************************************************************\r
- *                     MMIO_SetBuffer                          [INTERNAL]\r
- */\r
-static MMRESULT MMIO_SetBuffer(WINE_MMIO* wm, void* pchBuffer, LONG cchBuffer,\r
-                              UINT uFlags)\r
-{\r
-    TRACE("(%p %p %ld %u)\n", wm, pchBuffer, cchBuffer, uFlags);\r
-\r
-    if (uFlags)                        return MMSYSERR_INVALPARAM;\r
-    if (cchBuffer > 0xFFFF)\r
-       WARN("Untested handling of huge mmio buffers (%ld >= 64k)\n", cchBuffer);\r
-\r
-    if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)\r
-       return MMIOERR_CANNOTWRITE;\r
-\r
-    /* free previous buffer if allocated */\r
-    if (wm->info.dwFlags & MMIO_ALLOCBUF) {\r
-        HeapFree(GetProcessHeap(), 0, wm->info.pchBuffer);\r
-        wm->info.pchBuffer = NULL;\r
-       wm->info.dwFlags &= ~MMIO_ALLOCBUF;\r
-    }\r
-\r
-    if (pchBuffer) {\r
-        wm->info.pchBuffer = pchBuffer;\r
-    } else if (cchBuffer) {\r
-       if (!(wm->info.pchBuffer = HeapAlloc(GetProcessHeap(), 0, cchBuffer)))\r
-           return MMIOERR_OUTOFMEMORY;\r
-       wm->info.dwFlags |= MMIO_ALLOCBUF;\r
-    } else {\r
-       wm->info.pchBuffer = NULL;\r
-    }\r
-\r
-    wm->info.cchBuffer = cchBuffer;\r
-    wm->info.pchNext = wm->info.pchBuffer;\r
-    wm->info.pchEndRead = wm->info.pchBuffer;\r
-    wm->info.pchEndWrite = wm->info.pchBuffer + cchBuffer;\r
-    wm->info.lBufOffset = 0;\r
-    wm->bBufferLoaded = FALSE;\r
-\r
-    return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- *                     MMIO_Open                               [internal]\r
- */\r
-HMMIO MMIO_Open(LPSTR szFileName, MMIOINFO* refmminfo, DWORD dwOpenFlags, \r
-                enum mmioProcType type)\r
-{\r
-    LPWINE_MMIO                wm;\r
-    MMIOINFO           mmioinfo;\r
-\r
-    TRACE("('%s', %p, %08lX, %d);\n", szFileName, refmminfo, dwOpenFlags, type);\r
-\r
-    if (!refmminfo) {\r
-        refmminfo = &mmioinfo;\r
-\r
-       mmioinfo.fccIOProc = 0;\r
-       mmioinfo.pIOProc = NULL;\r
-       mmioinfo.pchBuffer = NULL;\r
-       mmioinfo.cchBuffer = 0;\r
-        type = MMIO_PROC_32A;\r
-    }\r
-\r
-    if (dwOpenFlags & (MMIO_PARSE|MMIO_EXIST)) {\r
-       char    buffer[MAX_PATH];\r
-\r
-       if (GetFullPathNameA(szFileName, sizeof(buffer), buffer, NULL) >= sizeof(buffer))\r
-           return (HMMIO)FALSE;\r
-       if ((dwOpenFlags & MMIO_EXIST) && (GetFileAttributesA(buffer) == INVALID_FILE_ATTRIBUTES))\r
-           return (HMMIO)FALSE;\r
-       strcpy(szFileName, buffer);\r
-       return (HMMIO)TRUE;\r
-    }\r
-\r
-    if ((wm = MMIO_Create()) == NULL)\r
-       return 0;\r
-\r
-    /* If both params are NULL, then parse the file name if available */\r
-    if (refmminfo->fccIOProc == 0 && refmminfo->pIOProc == NULL) {\r
-       wm->info.fccIOProc = MMIO_ParseExtA(szFileName);\r
-       /* Handle any unhandled/error case. Assume DOS file */\r
-       if (wm->info.fccIOProc == 0)\r
-           wm->info.fccIOProc = FOURCC_DOS;\r
-       if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) {\r
-           /* If not found, retry with FOURCC_DOS */\r
-           wm->info.fccIOProc = FOURCC_DOS;\r
-           if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc)))\r
-               goto error2;\r
-       }\r
-       wm->bTmpIOProc = FALSE;\r
-    }\r
-    /* if just the four character code is present, look up IO proc */\r
-    else if (refmminfo->pIOProc == NULL) {\r
-       wm->info.fccIOProc = refmminfo->fccIOProc;\r
-       if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;\r
-       wm->bTmpIOProc = FALSE;\r
-    }\r
-    /* if IO proc specified, use it and specified four character code */\r
-    else {\r
-       wm->info.fccIOProc = refmminfo->fccIOProc;\r
-       MMIO_InstallIOProc(wm->info.fccIOProc, refmminfo->pIOProc,\r
-                           MMIO_INSTALLPROC, type);\r
-       if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;\r
-       assert(wm->ioProc->pIOProc == refmminfo->pIOProc);\r
-       wm->bTmpIOProc = TRUE;\r
-    }\r
-\r
-    wm->bBufferLoaded = FALSE;\r
-    wm->ioProc->count++;\r
-\r
-    if (dwOpenFlags & MMIO_ALLOCBUF) {\r
-       if ((refmminfo->wErrorRet = MMIO_SetBuffer(wm, NULL, MMIO_DEFAULTBUFFER, 0)))\r
-           goto error1;\r
-    } else if (wm->info.fccIOProc == FOURCC_MEM) {\r
-        refmminfo->wErrorRet = MMIO_SetBuffer(wm, refmminfo->pchBuffer, refmminfo->cchBuffer, 0);\r
-       if (refmminfo->wErrorRet != MMSYSERR_NOERROR)\r
-           goto error1;\r
-       wm->bBufferLoaded = TRUE;\r
-    } /* else => unbuffered, wm->info.pchBuffer == NULL */\r
-\r
-    /* see mmioDosIOProc for that one */\r
-    wm->info.adwInfo[0] = refmminfo->adwInfo[0];\r
-    wm->info.dwFlags = dwOpenFlags;\r
-\r
-    /* call IO proc to actually open file */\r
-    refmminfo->wErrorRet = send_message(wm->ioProc, &wm->info, MMIOM_OPEN, \r
-                                        (LPARAM)szFileName, 0, MMIO_PROC_32A);\r
-\r
-    /* grab file size, when possible */\r
-    wm->dwFileSize = GetFileSize((HANDLE)wm->info.adwInfo[0], NULL);\r
-\r
-    if (refmminfo->wErrorRet == 0)\r
-       return wm->info.hmmio;\r
- error1:\r
-    if (wm->ioProc) wm->ioProc->count--;\r
- error2:\r
-    MMIO_Destroy(wm);\r
-    return 0;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioOpenW                       [WINMM.@]\r
- */\r
-HMMIO WINAPI mmioOpenW(LPWSTR szFileName, MMIOINFO* lpmmioinfo,\r
-                      DWORD dwOpenFlags)\r
-{\r
-    HMMIO      ret;\r
-    LPSTR      szFn = NULL;\r
-\r
-    if (szFileName)\r
-    {\r
-        INT     len = WideCharToMultiByte( CP_ACP, 0, szFileName, -1, NULL, 0, NULL, NULL );\r
-        szFn = HeapAlloc( GetProcessHeap(), 0, len );\r
-        if (!szFn) return NULL;\r
-        WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szFn, len, NULL, NULL );\r
-    }\r
-\r
-    ret = MMIO_Open(szFn, lpmmioinfo, dwOpenFlags, MMIO_PROC_32W);\r
-\r
-    HeapFree(GetProcessHeap(), 0, szFn);\r
-    return ret;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioOpenA                       [WINMM.@]\r
- */\r
-HMMIO WINAPI mmioOpenA(LPSTR szFileName, MMIOINFO* lpmmioinfo,\r
-                      DWORD dwOpenFlags)\r
-{\r
-    return  MMIO_Open(szFileName, lpmmioinfo, dwOpenFlags, MMIO_PROC_32A);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioClose               [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioClose(HMMIO hmmio, UINT uFlags)\r
-{\r
-    LPWINE_MMIO        wm;\r
-    MMRESULT   result;\r
-\r
-    TRACE("(%p, %04X);\n", hmmio, uFlags);\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return MMSYSERR_INVALHANDLE;\r
-\r
-    if ((result = MMIO_Flush(wm, 0)) != MMSYSERR_NOERROR)\r
-       return result;\r
-\r
-    result = send_message(wm->ioProc, &wm->info, MMIOM_CLOSE, \r
-                          uFlags, 0, MMIO_PROC_32A);\r
-\r
-    MMIO_SetBuffer(wm, NULL, 0, 0);\r
-\r
-    wm->ioProc->count--;\r
-\r
-    if (wm->bTmpIOProc)\r
-       MMIO_InstallIOProc(wm->info.fccIOProc, wm->ioProc->pIOProc,\r
-                           MMIO_REMOVEPROC, wm->ioProc->type);\r
-\r
-    MMIO_Destroy(wm);\r
-\r
-    return result;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioRead                [WINMM.@]\r
- */\r
-LONG WINAPI mmioRead(HMMIO hmmio, HPSTR pch, LONG cch)\r
-{\r
-    LPWINE_MMIO        wm;\r
-    LONG       count;\r
-\r
-    TRACE("(%p, %p, %ld);\n", hmmio, pch, cch);\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return -1;\r
-\r
-    /* unbuffered case first */\r
-    if (!wm->info.pchBuffer)\r
-       return send_message(wm->ioProc, &wm->info, MMIOM_READ, \r
-                            (LPARAM)pch, cch, MMIO_PROC_32A);\r
-\r
-    /* first try from current buffer */\r
-    if (wm->info.pchNext != wm->info.pchEndRead) {\r
-       count = wm->info.pchEndRead - wm->info.pchNext;\r
-       if (count > cch || count < 0) count = cch;\r
-       memcpy(pch, wm->info.pchNext, count);\r
-       wm->info.pchNext += count;\r
-       pch += count;\r
-       cch -= count;\r
-    } else\r
-       count = 0;\r
-\r
-    if (cch && (wm->info.fccIOProc != FOURCC_MEM)) {\r
-       assert(wm->info.cchBuffer);\r
-\r
-       while (cch) {\r
-           LONG size;\r
-\r
-           size = MMIO_GrabNextBuffer(wm, TRUE);\r
-           if (size <= 0) break;\r
-           if (size > cch) size = cch;\r
-           memcpy(pch, wm->info.pchBuffer, size);\r
-           wm->info.pchNext += size;\r
-           pch += size;\r
-           cch -= size;\r
-           count += size;\r
-       }\r
-    }\r
-\r
-    TRACE("count=%ld\n", count);\r
-    return count;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioWrite               [WINMM.@]\r
- */\r
-LONG WINAPI mmioWrite(HMMIO hmmio, HPCSTR pch, LONG cch)\r
-{\r
-    LPWINE_MMIO        wm;\r
-    LONG       count;\r
-\r
-    TRACE("(%p, %p, %ld);\n", hmmio, pch, cch);\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return -1;\r
-\r
-    if (wm->info.cchBuffer) {\r
-       LONG    bytesW = 0;\r
-\r
-        count = 0;\r
-        while (cch) {\r
-            if (wm->info.pchNext != wm->info.pchEndWrite) {\r
-                count = wm->info.pchEndWrite - wm->info.pchNext;\r
-                if (count > cch || count < 0) count = cch;\r
-                memcpy(wm->info.pchNext, pch, count);\r
-                wm->info.pchNext += count;\r
-                pch += count;\r
-                cch -= count;\r
-                bytesW += count;\r
-                wm->info.dwFlags |= MMIO_DIRTY;\r
-           } else {\r
-                if (wm->info.fccIOProc == FOURCC_MEM) {\r
-                    if (wm->info.adwInfo[0]) {\r
-                        /* from where would we get the memory handle? */\r
-                        FIXME("memory file expansion not implemented!\n");\r
-                        break;\r
-                   } else break;\r
-                }\r
-            }\r
-\r
-            if (wm->info.pchNext == wm->info.pchEndWrite)\r
-            {\r
-                MMIO_Flush(wm, MMIO_EMPTYBUF);\r
-                MMIO_GrabNextBuffer(wm, FALSE);\r
-            }\r
-            else break;\r
-        }\r
-       count = bytesW;\r
-    } else {\r
-       count = send_message(wm->ioProc, &wm->info, MMIOM_WRITE, \r
-                             (LPARAM)pch, cch, MMIO_PROC_32A);\r
-       wm->info.lBufOffset = wm->info.lDiskOffset;\r
-    }\r
-\r
-    TRACE("bytes written=%ld\n", count);\r
-    return count;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioSeek                [WINMM.@]\r
- */\r
-LONG WINAPI mmioSeek(HMMIO hmmio, LONG lOffset, INT iOrigin)\r
-{\r
-    LPWINE_MMIO        wm;\r
-    LONG       offset;\r
-\r
-    TRACE("(%p, %08lX, %d);\n", hmmio, lOffset, iOrigin);\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return MMSYSERR_INVALHANDLE;\r
-\r
-    /* not buffered, direct seek on file */\r
-    if (!wm->info.pchBuffer)\r
-       return send_message(wm->ioProc, &wm->info, MMIOM_SEEK, \r
-                            lOffset, iOrigin, MMIO_PROC_32A);\r
-\r
-    switch (iOrigin) {\r
-    case SEEK_SET:\r
-       offset = lOffset;\r
-       break;\r
-    case SEEK_CUR:\r
-       offset = wm->info.lBufOffset + (wm->info.pchNext - wm->info.pchBuffer) + lOffset;\r
-       break;\r
-    case SEEK_END:\r
-       offset = ((wm->info.fccIOProc == FOURCC_MEM)? wm->info.cchBuffer : wm->dwFileSize) - lOffset;\r
-       break;\r
-    default:\r
-       return -1;\r
-    }\r
-\r
-    if (offset && offset >= wm->dwFileSize && wm->info.fccIOProc != FOURCC_MEM) {\r
-        /* should check that write mode exists */\r
-        if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)\r
-            return -1;\r
-        wm->info.lBufOffset = offset;\r
-        wm->info.pchEndRead = wm->info.pchBuffer;\r
-        wm->info.pchEndWrite = wm->info.pchBuffer + wm->info.cchBuffer;\r
-        if ((wm->info.dwFlags & MMIO_RWMODE) == MMIO_READ) {\r
-            wm->info.lDiskOffset = wm->dwFileSize;\r
-        }\r
-    } else if ((wm->info.cchBuffer > 0) &&\r
-       ((offset < wm->info.lBufOffset) ||\r
-        (offset >= wm->info.lBufOffset + wm->info.cchBuffer) ||\r
-        !wm->bBufferLoaded)) {\r
-        /* stay in same buffer ? */\r
-        /* some memory mapped buffers are defined with -1 as a size */\r
-\r
-       /* condition to change buffer */\r
-       if ((wm->info.fccIOProc == FOURCC_MEM) ||\r
-           MMIO_Flush(wm, 0) != MMSYSERR_NOERROR ||\r
-           /* this also sets the wm->info.lDiskOffset field */\r
-           send_message(wm->ioProc, &wm->info, MMIOM_SEEK,\r
-                         (offset / wm->info.cchBuffer) * wm->info.cchBuffer,\r
-                         SEEK_SET, MMIO_PROC_32A) == -1)\r
-           return -1;\r
-       MMIO_GrabNextBuffer(wm, TRUE);\r
-    }\r
-\r
-    wm->info.pchNext = wm->info.pchBuffer + (offset - wm->info.lBufOffset);\r
-\r
-    TRACE("=> %ld\n", offset);\r
-    return offset;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioGetInfo             [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioGetInfo(HMMIO hmmio, MMIOINFO* lpmmioinfo, UINT uFlags)\r
-{\r
-    LPWINE_MMIO                wm;\r
-\r
-    TRACE("(%p,%p,0x%08x)\n",hmmio,lpmmioinfo,uFlags);\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return MMSYSERR_INVALHANDLE;\r
-\r
-    memcpy(lpmmioinfo, &wm->info, sizeof(MMIOINFO));\r
-    /* don't expose 16 bit ioproc:s */\r
-    if (wm->ioProc->type != MMIO_PROC_16)\r
-        lpmmioinfo->pIOProc = wm->ioProc->pIOProc;\r
-\r
-    return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioSetInfo             [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioSetInfo(HMMIO hmmio, const MMIOINFO* lpmmioinfo, UINT uFlags)\r
-{\r
-    LPWINE_MMIO                wm;\r
-\r
-    TRACE("(%p,%p,0x%08x)\n",hmmio,lpmmioinfo,uFlags);\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return MMSYSERR_INVALHANDLE;\r
-\r
-    /* check pointers coherence */\r
-    if (lpmmioinfo->pchNext < wm->info.pchBuffer ||\r
-       lpmmioinfo->pchNext > wm->info.pchBuffer + wm->info.cchBuffer ||\r
-       lpmmioinfo->pchEndRead < wm->info.pchBuffer ||\r
-       lpmmioinfo->pchEndRead > wm->info.pchBuffer + wm->info.cchBuffer ||\r
-       lpmmioinfo->pchEndWrite < wm->info.pchBuffer ||\r
-       lpmmioinfo->pchEndWrite > wm->info.pchBuffer + wm->info.cchBuffer)\r
-       return MMSYSERR_INVALPARAM;\r
-\r
-    wm->info.pchNext = lpmmioinfo->pchNext;\r
-    wm->info.pchEndRead = lpmmioinfo->pchEndRead;\r
-\r
-    return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
-*                              mmioSetBuffer           [WINMM.@]\r
-*/\r
-MMRESULT WINAPI mmioSetBuffer(HMMIO hmmio, LPSTR pchBuffer, LONG cchBuffer, UINT uFlags)\r
-{\r
-    LPWINE_MMIO                wm;\r
-\r
-    TRACE("(hmmio=%p, pchBuf=%p, cchBuf=%ld, uFlags=%#08x)\n",\r
-         hmmio, pchBuffer, cchBuffer, uFlags);\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return MMSYSERR_INVALHANDLE;\r
-\r
-    return MMIO_SetBuffer(wm, pchBuffer, cchBuffer, uFlags);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioFlush               [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioFlush(HMMIO hmmio, UINT uFlags)\r
-{\r
-    LPWINE_MMIO                wm;\r
-\r
-    TRACE("(%p, %04X)\n", hmmio, uFlags);\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return MMSYSERR_INVALHANDLE;\r
-\r
-    return MMIO_Flush(wm, uFlags);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioAdvance             [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioAdvance(HMMIO hmmio, MMIOINFO* lpmmioinfo, UINT uFlags)\r
-{\r
-    LPWINE_MMIO                wm;\r
-\r
-    TRACE("hmmio=%p, lpmmioinfo=%p, uFlags=%04X\n", hmmio, lpmmioinfo, uFlags);\r
-\r
-    /* NOTE: mmioAdvance16 heavily relies on parameters from lpmmioinfo we're using\r
-     * here. be sure if you change something here to check mmioAdvance16 as well\r
-     */\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return MMSYSERR_INVALHANDLE;\r
-\r
-    if (!wm->info.cchBuffer)\r
-       return MMIOERR_UNBUFFERED;\r
-\r
-    if (uFlags != MMIO_READ && uFlags != MMIO_WRITE)\r
-       return MMSYSERR_INVALPARAM;\r
-\r
-    if (uFlags == MMIO_WRITE && (lpmmioinfo->dwFlags & MMIO_DIRTY))\r
-    {\r
-        send_message(wm->ioProc, &wm->info, MMIOM_SEEK, \r
-                     lpmmioinfo->lBufOffset, SEEK_SET, MMIO_PROC_32A);\r
-        send_message(wm->ioProc, &wm->info, MMIOM_WRITE, \r
-                     (LPARAM)lpmmioinfo->pchBuffer,\r
-                     lpmmioinfo->pchNext - lpmmioinfo->pchBuffer, MMIO_PROC_32A);\r
-        lpmmioinfo->dwFlags &= ~MMIO_DIRTY;\r
-    }\r
-    if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)\r
-       return MMIOERR_CANNOTWRITE;\r
-\r
-    if (lpmmioinfo) {\r
-       wm->dwFileSize = max(wm->dwFileSize, lpmmioinfo->lBufOffset + \r
-                             (lpmmioinfo->pchNext - lpmmioinfo->pchBuffer));\r
-    }\r
-    MMIO_GrabNextBuffer(wm, uFlags == MMIO_READ);\r
-\r
-    if (lpmmioinfo) {\r
-       lpmmioinfo->pchNext = lpmmioinfo->pchBuffer;\r
-       lpmmioinfo->pchEndRead  = lpmmioinfo->pchBuffer +\r
-           (wm->info.pchEndRead - wm->info.pchBuffer);\r
-       lpmmioinfo->pchEndWrite = lpmmioinfo->pchBuffer +\r
-           (wm->info.pchEndWrite - wm->info.pchBuffer);\r
-       lpmmioinfo->lDiskOffset = wm->info.lDiskOffset;\r
-       lpmmioinfo->lBufOffset = wm->info.lBufOffset;\r
-    }\r
-    return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioStringToFOURCCA     [WINMM.@]\r
- */\r
-FOURCC WINAPI mmioStringToFOURCCA(LPCSTR sz, UINT uFlags)\r
-{\r
-    CHAR cc[4];\r
-    int i = 0;\r
-\r
-    for (i = 0; i < 4 && sz[i]; i++) {\r
-       if (uFlags & MMIO_TOUPPER) {\r
-           cc[i] = toupper(sz[i]);\r
-       } else {\r
-           cc[i] = sz[i];\r
-       }\r
-    }\r
-\r
-    /* Pad with spaces */\r
-    while (i < 4) cc[i++] = ' ';\r
-\r
-    TRACE("Got '%.4s'\n",cc);\r
-    return mmioFOURCC(cc[0],cc[1],cc[2],cc[3]);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioStringToFOURCCW     [WINMM.@]\r
- */\r
-FOURCC WINAPI mmioStringToFOURCCW(LPCWSTR sz, UINT uFlags)\r
-{\r
-    char       szA[4];\r
-\r
-    WideCharToMultiByte( CP_ACP, 0, sz, 4, szA, sizeof(szA), NULL, NULL );\r
-    return mmioStringToFOURCCA(szA,uFlags);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioInstallIOProcA         [WINMM.@]\r
- */\r
-LPMMIOPROC WINAPI mmioInstallIOProcA(FOURCC fccIOProc,\r
-                                    LPMMIOPROC pIOProc, DWORD dwFlags)\r
-{\r
-    return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, MMIO_PROC_32A);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioInstallIOProcW         [WINMM.@]\r
- */\r
-LPMMIOPROC WINAPI mmioInstallIOProcW(FOURCC fccIOProc,\r
-                                    LPMMIOPROC pIOProc, DWORD dwFlags)\r
-{\r
-    return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, MMIO_PROC_32W);\r
-}\r
-\r
-/******************************************************************\r
- *             MMIO_SendMessage\r
- *\r
- *\r
- */\r
-LRESULT         MMIO_SendMessage(HMMIO hmmio, UINT uMessage, LPARAM lParam1, \r
-                                 LPARAM lParam2, enum mmioProcType type)\r
-{\r
-    LPWINE_MMIO                wm;\r
-\r
-    TRACE("(%p, %u, %ld, %ld, %d)\n", hmmio, uMessage, lParam1, lParam2, type);\r
-\r
-    if (uMessage < MMIOM_USER)\r
-       return MMSYSERR_INVALPARAM;\r
-\r
-    if ((wm = MMIO_Get(hmmio)) == NULL)\r
-       return MMSYSERR_INVALHANDLE;\r
-\r
-    return send_message(wm->ioProc, &wm->info, uMessage, lParam1, lParam2, type);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioSendMessage         [WINMM.@]\r
- */\r
-LRESULT WINAPI mmioSendMessage(HMMIO hmmio, UINT uMessage,\r
-                              LPARAM lParam1, LPARAM lParam2)\r
-{\r
-    return MMIO_SendMessage(hmmio, uMessage, lParam1, lParam2, MMIO_PROC_32A);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioDescend             [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioDescend(HMMIO hmmio, LPMMCKINFO lpck,\r
-                            const MMCKINFO* lpckParent, UINT uFlags)\r
-{\r
-    DWORD              dwOldPos;\r
-    FOURCC             srchCkId;\r
-    FOURCC             srchType;\r
-\r
-\r
-    TRACE("(%p, %p, %p, %04X);\n", hmmio, lpck, lpckParent, uFlags);\r
-\r
-    if (lpck == NULL)\r
-       return MMSYSERR_INVALPARAM;\r
-\r
-    dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);\r
-    TRACE("dwOldPos=%ld\n", dwOldPos);\r
-\r
-    if (lpckParent != NULL) {\r
-       TRACE("seek inside parent at %ld !\n", lpckParent->dwDataOffset);\r
-       /* EPP: was dwOldPos = mmioSeek(hmmio,lpckParent->dwDataOffset,SEEK_SET); */\r
-       if (dwOldPos < lpckParent->dwDataOffset ||\r
-           dwOldPos >= lpckParent->dwDataOffset + lpckParent->cksize) {\r
-           WARN("outside parent chunk\n");\r
-           return MMIOERR_CHUNKNOTFOUND;\r
-       }\r
-    }\r
-\r
-    /* The SDK docu says 'ckid' is used for all cases. Real World\r
-     * examples disagree -Marcus,990216.\r
-     */\r
-\r
-    srchType = 0;\r
-    /* find_chunk looks for 'ckid' */\r
-    if (uFlags & MMIO_FINDCHUNK)\r
-       srchCkId = lpck->ckid;\r
-    /* find_riff and find_list look for 'fccType' */\r
-    if (uFlags & MMIO_FINDLIST) {\r
-       srchCkId = FOURCC_LIST;\r
-       srchType = lpck->fccType;\r
-    }\r
-    if (uFlags & MMIO_FINDRIFF) {\r
-       srchCkId = FOURCC_RIFF;\r
-       srchType = lpck->fccType;\r
-    }\r
-\r
-    if (uFlags & (MMIO_FINDCHUNK|MMIO_FINDLIST|MMIO_FINDRIFF)) {\r
-       TRACE("searching for %.4s.%.4s\n",\r
-             (LPSTR)&srchCkId,\r
-             srchType?(LPSTR)&srchType:"any ");\r
-\r
-       while (TRUE) {\r
-           LONG ix;\r
-\r
-           ix = mmioRead(hmmio, (LPSTR)lpck, 3 * sizeof(DWORD));\r
-           if (ix < 2*sizeof(DWORD)) {\r
-               mmioSeek(hmmio, dwOldPos, SEEK_SET);\r
-               WARN("return ChunkNotFound\n");\r
-               return MMIOERR_CHUNKNOTFOUND;\r
-           }\r
-           lpck->dwDataOffset = dwOldPos + 2 * sizeof(DWORD);\r
-           if (ix < lpck->dwDataOffset - dwOldPos) {\r
-               mmioSeek(hmmio, dwOldPos, SEEK_SET);\r
-               WARN("return ChunkNotFound\n");\r
-               return MMIOERR_CHUNKNOTFOUND;\r
-           }\r
-           TRACE("ckid=%.4s fcc=%.4s cksize=%08lX !\n",\r
-                 (LPSTR)&lpck->ckid,\r
-                 srchType?(LPSTR)&lpck->fccType:"<na>",\r
-                 lpck->cksize);\r
-           if ((srchCkId == lpck->ckid) &&\r
-               (!srchType || (srchType == lpck->fccType))\r
-               )\r
-               break;\r
-\r
-           dwOldPos = lpck->dwDataOffset + ((lpck->cksize + 1) & ~1);\r
-           mmioSeek(hmmio, dwOldPos, SEEK_SET);\r
-       }\r
-    } else {\r
-       /* FIXME: unverified, does it do this? */\r
-       /* NB: This part is used by WAVE_mciOpen, among others */\r
-       if (mmioRead(hmmio, (LPSTR)lpck, 3 * sizeof(DWORD)) < 3 * sizeof(DWORD)) {\r
-           mmioSeek(hmmio, dwOldPos, SEEK_SET);\r
-           WARN("return ChunkNotFound 2nd\n");\r
-           return MMIOERR_CHUNKNOTFOUND;\r
-       }\r
-       lpck->dwDataOffset = dwOldPos + 2 * sizeof(DWORD);\r
-    }\r
-    lpck->dwFlags = 0;\r
-    /* If we were looking for RIFF/LIST chunks, the final file position\r
-     * is after the chunkid. If we were just looking for the chunk\r
-     * it is after the cksize. So add 4 in RIFF/LIST case.\r
-     */\r
-    if (lpck->ckid == FOURCC_RIFF || lpck->ckid == FOURCC_LIST)\r
-       mmioSeek(hmmio, lpck->dwDataOffset + sizeof(DWORD), SEEK_SET);\r
-    else\r
-       mmioSeek(hmmio, lpck->dwDataOffset, SEEK_SET);\r
-    TRACE("lpck: ckid=%.4s, cksize=%ld, dwDataOffset=%ld fccType=%08lX (%.4s)!\n",\r
-         (LPSTR)&lpck->ckid, lpck->cksize, lpck->dwDataOffset,\r
-         lpck->fccType, srchType?(LPSTR)&lpck->fccType:"");\r
-    return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioAscend              [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioAscend(HMMIO hmmio, LPMMCKINFO lpck, UINT uFlags)\r
-{\r
-    TRACE("(%p, %p, %04X);\n", hmmio, lpck, uFlags);\r
-\r
-    if (lpck->dwFlags & MMIO_DIRTY) {\r
-       DWORD   dwOldPos, dwNewSize;\r
-\r
-       TRACE("Chunk is dirty, checking if chunk's size is correct\n");\r
-       dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);\r
-       TRACE("dwOldPos=%ld lpck->dwDataOffset = %ld\n", dwOldPos, lpck->dwDataOffset);\r
-       dwNewSize = dwOldPos - lpck->dwDataOffset;\r
-       if (dwNewSize != lpck->cksize) {\r
-           TRACE("Nope: lpck->cksize=%ld dwNewSize=%ld\n", lpck->cksize, dwNewSize);\r
-           lpck->cksize = dwNewSize;\r
-\r
-           /* pad odd size with 0 */\r
-           if (dwNewSize & 1) {\r
-               char ch = 0;\r
-               mmioWrite(hmmio, &ch, 1);\r
-           }\r
-           mmioSeek(hmmio, lpck->dwDataOffset - sizeof(DWORD), SEEK_SET);\r
-           mmioWrite(hmmio, (LPSTR)&dwNewSize, sizeof(DWORD));\r
-       }\r
-       lpck->dwFlags = 0;\r
-    }\r
-\r
-    mmioSeek(hmmio, lpck->dwDataOffset + ((lpck->cksize + 1) & ~1), SEEK_SET);\r
-\r
-    return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- *                     mmioCreateChunk                         [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioCreateChunk(HMMIO hmmio, MMCKINFO* lpck, UINT uFlags)\r
-{\r
-    DWORD      dwOldPos;\r
-    LONG       size;\r
-    LONG       ix;\r
-\r
-    TRACE("(%p, %p, %04X);\n", hmmio, lpck, uFlags);\r
-\r
-    dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);\r
-    TRACE("dwOldPos=%ld\n", dwOldPos);\r
-\r
-    if (uFlags == MMIO_CREATELIST)\r
-       lpck->ckid = FOURCC_LIST;\r
-    else if (uFlags == MMIO_CREATERIFF)\r
-       lpck->ckid = FOURCC_RIFF;\r
-\r
-    TRACE("ckid=%.4s\n", (LPSTR)&lpck->ckid);\r
-\r
-    size = 2 * sizeof(DWORD);\r
-    lpck->dwDataOffset = dwOldPos + size;\r
-\r
-    if (lpck->ckid == FOURCC_RIFF || lpck->ckid == FOURCC_LIST)\r
-       size += sizeof(DWORD);\r
-    lpck->dwFlags = MMIO_DIRTY;\r
-\r
-    ix = mmioWrite(hmmio, (LPSTR)lpck, size);\r
-    TRACE("after mmioWrite ix = %ld req = %ld, errno = %d\n",ix, size, errno);\r
-    if (ix < size) {\r
-       mmioSeek(hmmio, dwOldPos, SEEK_SET);\r
-       WARN("return CannotWrite\n");\r
-       return MMIOERR_CANNOTWRITE;\r
-    }\r
-\r
-    return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioRenameA                     [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioRenameA(LPCSTR szFileName, LPCSTR szNewFileName,\r
-                            const MMIOINFO* lpmmioinfo, DWORD dwFlags)\r
-{\r
-    struct IOProcList*  ioProc = NULL;\r
-    struct IOProcList   tmp;\r
-    FOURCC              fcc;\r
-\r
-    TRACE("('%s', '%s', %p, %08lX);\n",\r
-         debugstr_a(szFileName), debugstr_a(szNewFileName), lpmmioinfo, dwFlags);\r
-\r
-    /* If both params are NULL, then parse the file name */\r
-    if (lpmmioinfo && lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL)\r
-    {\r
-       fcc = MMIO_ParseExtA(szFileName);\r
-        if (fcc) ioProc = MMIO_FindProcNode(fcc);\r
-    }\r
-\r
-    /* Handle any unhandled/error case from above. Assume DOS file */\r
-    if (!lpmmioinfo || (lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL && ioProc == NULL))\r
-       ioProc = MMIO_FindProcNode(FOURCC_DOS);\r
-    /* if just the four character code is present, look up IO proc */\r
-    else if (lpmmioinfo->pIOProc == NULL)\r
-        ioProc = MMIO_FindProcNode(lpmmioinfo->fccIOProc);\r
-    else /* use relevant ioProc */\r
-    {\r
-        ioProc = &tmp;\r
-        tmp.fourCC = lpmmioinfo->fccIOProc;\r
-        tmp.pIOProc = lpmmioinfo->pIOProc;\r
-        tmp.type = MMIO_PROC_32A;\r
-        tmp.count = 1;\r
-    }\r
-\r
-    /* FIXME: should we actually pass lpmmioinfo down the drain ???\r
-     * or make a copy of it because it's const ???\r
-     */\r
-    return send_message(ioProc, (MMIOINFO*)lpmmioinfo, MMIOM_RENAME,\r
-                        (LPARAM)szFileName, (LPARAM)szNewFileName, MMIO_PROC_32A);\r
-}\r
-\r
-/**************************************************************************\r
- *                             mmioRenameW                     [WINMM.@]\r
- */\r
-MMRESULT WINAPI mmioRenameW(LPCWSTR szFileName, LPCWSTR szNewFileName,\r
-                            const MMIOINFO* lpmmioinfo, DWORD dwFlags)\r
-{\r
-    LPSTR      szFn = NULL;\r
-    LPSTR      sznFn = NULL;\r
-    UINT       ret = MMSYSERR_NOMEM;\r
-    INT         len;\r
-\r
-    if (szFileName)\r
-    {\r
-        len = WideCharToMultiByte( CP_ACP, 0, szFileName, -1, NULL, 0, NULL, NULL );\r
-        szFn = HeapAlloc( GetProcessHeap(), 0, len );\r
-        if (!szFn) goto done;\r
-        WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szFn, len, NULL, NULL );\r
-    }\r
-    if (szNewFileName)\r
-    {\r
-        len = WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, NULL, 0, NULL, NULL );\r
-        sznFn = HeapAlloc( GetProcessHeap(), 0, len );\r
-        if (!sznFn) goto done;\r
-        WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, sznFn, len, NULL, NULL );\r
-    }\r
-\r
-    ret = mmioRenameA(szFn, sznFn, lpmmioinfo, dwFlags);\r
-\r
-done:\r
-    HeapFree(GetProcessHeap(),0,szFn);\r
-    HeapFree(GetProcessHeap(),0,sznFn);\r
-    return ret;\r
-}\r
+/*
+ * MMIO functions
+ *
+ * Copyright 1998 Andrew Taylor
+ * Copyright 1998 Ove Kåven
+ * Copyright 2000,2002 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Still to be done:
+ *     + correct handling of global/local IOProcs (and temporary IOProcs)
+ *     + mode of mmio objects is not used (read vs write vs readwrite)
+ *     + thread safeness
+ *      + 32 A <=> W message mapping
+ */
+
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "mmsystem.h"
+#include "winemm.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mmio);
+
+LRESULT         (*pFnMmioCallback16)(DWORD,LPMMIOINFO,UINT,LPARAM,LPARAM) /* = NULL */;
+
+/**************************************************************************
+ *                     mmioDosIOProc                           [internal]
+ */
+static LRESULT CALLBACK mmioDosIOProc(LPMMIOINFO lpmmioinfo, UINT uMessage,
+                                     LPARAM lParam1, LPARAM lParam2)
+{
+    LRESULT    ret = MMSYSERR_NOERROR;
+
+    TRACE("(%p, %X, 0x%lx, 0x%lx);\n", lpmmioinfo, uMessage, lParam1, lParam2);
+
+    switch (uMessage) {
+    case MMIOM_OPEN:
+       {
+           /* Parameters:
+            * lParam1 = szFileName parameter from mmioOpen
+            * lParam2 = reserved
+            * Returns: zero on success, error code on error
+            * NOTE: lDiskOffset automatically set to zero
+            */
+           LPCSTR      szFileName = (LPCSTR)lParam1;
+
+           if (lpmmioinfo->dwFlags & MMIO_GETTEMP) {
+               FIXME("MMIO_GETTEMP not implemented\n");
+               return MMIOERR_CANNOTOPEN;
+           }
+
+           /* if filename NULL, assume open file handle in adwInfo[0] */
+           if (szFileName) {
+                OFSTRUCT    ofs;
+                lpmmioinfo->adwInfo[0] = (DWORD)OpenFile(szFileName, &ofs, lpmmioinfo->dwFlags & 0xFFFF);
+            }
+           if (lpmmioinfo->adwInfo[0] == (DWORD)HFILE_ERROR)
+               ret = MMIOERR_CANNOTOPEN;
+       }
+       break;
+
+    case MMIOM_CLOSE:
+       /* Parameters:
+        * lParam1 = wFlags parameter from mmioClose
+        * lParam2 = unused
+        * Returns: zero on success, error code on error
+        */
+       if (!(lParam1 & MMIO_FHOPEN))
+           _lclose((HFILE)lpmmioinfo->adwInfo[0]);
+       break;
+
+    case MMIOM_READ:
+       /* Parameters:
+        * lParam1 = huge pointer to read buffer
+        * lParam2 = number of bytes to read
+        * Returns: number of bytes read, 0 for EOF, -1 for error (error code
+        *         in wErrorRet)
+        */
+       ret = _lread((HFILE)lpmmioinfo->adwInfo[0], (HPSTR)lParam1, (LONG)lParam2);
+       if (ret != -1)
+           lpmmioinfo->lDiskOffset += ret;
+
+       break;
+
+    case MMIOM_WRITE:
+    case MMIOM_WRITEFLUSH:
+       /* no internal buffering, so WRITEFLUSH handled same as WRITE */
+
+       /* Parameters:
+        * lParam1 = huge pointer to write buffer
+        * lParam2 = number of bytes to write
+        * Returns: number of bytes written, -1 for error (error code in
+        *              wErrorRet)
+        */
+       ret = _hwrite((HFILE)lpmmioinfo->adwInfo[0], (HPSTR)lParam1, (LONG)lParam2);
+       if (ret != -1)
+           lpmmioinfo->lDiskOffset += ret;
+       break;
+
+    case MMIOM_SEEK:
+       /* Parameters:
+        * lParam1 = new position
+        * lParam2 = from whence to seek (SEEK_SET, SEEK_CUR, SEEK_END)
+        * Returns: new file postion, -1 on error
+        */
+       ret = _llseek((HFILE)lpmmioinfo->adwInfo[0], (LONG)lParam1, (LONG)lParam2);
+       if (ret != -1)
+           lpmmioinfo->lDiskOffset = ret;
+       return ret;
+
+    case MMIOM_RENAME:
+       /* Parameters:
+        * lParam1 = old name
+        * lParam2 = new name
+        * Returns: zero on success, non-zero on failure
+        */
+        if (!MoveFileA((const char*)lParam1, (const char*)lParam2))
+            ret = MMIOERR_FILENOTFOUND;
+        break;
+
+    default:
+       FIXME("unexpected message %u\n", uMessage);
+       return 0;
+    }
+
+    return ret;
+}
+
+/**************************************************************************
+ *                     mmioMemIOProc                           [internal]
+ */
+static LRESULT CALLBACK mmioMemIOProc(LPMMIOINFO lpmmioinfo, UINT uMessage,
+                                     LPARAM lParam1, LPARAM lParam2)
+{
+    TRACE("(%p,0x%04x,0x%08lx,0x%08lx)\n", lpmmioinfo, uMessage, lParam1, lParam2);
+
+    switch (uMessage) {
+
+    case MMIOM_OPEN:
+       /* Parameters:
+        * lParam1 = filename (must be NULL)
+        * lParam2 = reserved
+        * Returns: zero on success, error code on error
+        * NOTE: lDiskOffset automatically set to zero
+        */
+       /* FIXME: io proc shouldn't change it */
+       if (!(lpmmioinfo->dwFlags & MMIO_CREATE))
+           lpmmioinfo->pchEndRead = lpmmioinfo->pchEndWrite;
+        lpmmioinfo->adwInfo[0] = HFILE_ERROR;
+       return 0;
+
+    case MMIOM_CLOSE:
+       /* Parameters:
+        * lParam1 = wFlags parameter from mmioClose
+        * lParam2 = unused
+        * Returns: zero on success, error code on error
+        */
+       return 0;
+
+    case MMIOM_READ:
+       /* Parameters:
+        * lParam1 = huge pointer to read buffer
+        * lParam2 = number of bytes to read
+        * Returns: number of bytes read, 0 for EOF, -1 for error (error code
+        *         in wErrorRet)
+        * NOTE: lDiskOffset should be updated
+        */
+       FIXME("MMIOM_READ on memory files should not occur, buffer may be lost!\n");
+       return 0;
+
+    case MMIOM_WRITE:
+    case MMIOM_WRITEFLUSH:
+       /* no internal buffering, so WRITEFLUSH handled same as WRITE */
+
+       /* Parameters:
+        * lParam1 = huge pointer to write buffer
+        * lParam2 = number of bytes to write
+        * Returns: number of bytes written, -1 for error (error code in
+        *              wErrorRet)
+        * NOTE: lDiskOffset should be updated
+        */
+       FIXME("MMIOM_WRITE on memory files should not occur, buffer may be lost!\n");
+       return 0;
+
+    case MMIOM_SEEK:
+       /* Parameters:
+        * lParam1 = new position
+        * lParam2 = from whence to seek (SEEK_SET, SEEK_CUR, SEEK_END)
+        * Returns: new file postion, -1 on error
+        * NOTE: lDiskOffset should be updated
+        */
+       FIXME("MMIOM_SEEK on memory files should not occur, buffer may be lost!\n");
+       return -1;
+
+    default:
+       FIXME("unexpected message %u\n", uMessage);
+       return 0;
+    }
+}
+
+/* This array will be the entire list for most apps 
+ * Note that temporary ioProcs will be stored with a 0 fourCC code
+ */
+
+static struct IOProcList defaultProcs[] = {
+    {&defaultProcs[1], FOURCC_DOS, (LPMMIOPROC)mmioDosIOProc, MMIO_PROC_32A, 0},
+    {NULL,             FOURCC_MEM, (LPMMIOPROC)mmioMemIOProc, MMIO_PROC_32A, 0},
+};
+
+static struct IOProcList*      pIOProcListAnchor = &defaultProcs[0];
+
+/****************************************************************
+ *             MMIO_FindProcNode                       [INTERNAL]
+ *
+ * Finds the ProcList node associated with a given FOURCC code.
+ */
+static struct IOProcList*      MMIO_FindProcNode(FOURCC fccIOProc)
+{
+    struct IOProcList* pListNode;
+
+    for (pListNode = pIOProcListAnchor; pListNode; pListNode = pListNode->pNext) {
+       if (pListNode->fourCC == fccIOProc) {
+           return pListNode;
+       }
+    }
+    return NULL;
+}
+
+/****************************************************************
+ *             MMIO_InstallIOProc                      [INTERNAL]
+ */
+LPMMIOPROC MMIO_InstallIOProc(FOURCC fccIOProc, LPMMIOPROC pIOProc,
+                              DWORD dwFlags, enum mmioProcType type)
+{
+    LPMMIOPROC         lpProc = NULL;
+    struct IOProcList*  pListNode;
+    struct IOProcList** ppListNode;
+
+    TRACE("(%08lx, %p, %08lX, %i)\n", fccIOProc, pIOProc, dwFlags, type);
+
+    if (dwFlags & MMIO_GLOBALPROC)
+       FIXME("Global procedures not implemented\n");
+
+    /* just handle the known procedures for now */
+    switch (dwFlags & (MMIO_INSTALLPROC|MMIO_REMOVEPROC|MMIO_FINDPROC)) {
+    case MMIO_INSTALLPROC:
+       /* Create new entry for the IOProc list */
+       pListNode = HeapAlloc(GetProcessHeap(), 0, sizeof(*pListNode));
+       if (pListNode) {
+           /* Fill in this node */
+           pListNode->fourCC = fccIOProc;
+           pListNode->pIOProc = pIOProc;
+           pListNode->type = type;
+           pListNode->count = 0;
+
+           /* Stick it on the end of the list */
+           pListNode->pNext = pIOProcListAnchor;
+           pIOProcListAnchor = pListNode;
+
+           /* Return this IOProc - that's how the caller knows we succeeded */
+           lpProc = pIOProc;
+       }
+       break;
+
+    case MMIO_REMOVEPROC:
+       /*
+        * Search for the node that we're trying to remove
+         * We search for a matching fourCC code if it's non null, or the proc
+         * address otherwise
+         * note that this method won't find the first item on the list, but
+        * since the first two items on this list are ones we won't
+        * let the user delete anyway, that's okay
+        */
+       ppListNode = &pIOProcListAnchor;
+       while ((*ppListNode) && 
+               ((fccIOProc != 0) ? 
+                (*ppListNode)->fourCC != fccIOProc : 
+                (*ppListNode)->pIOProc != pIOProc))
+           ppListNode = &((*ppListNode)->pNext);
+
+       if (*ppListNode) { /* found it */
+           /* FIXME: what should be done if an open mmio object uses this proc ?
+            * shall we return an error, nuke the mmio object ?
+            */
+           if ((*ppListNode)->count) {
+               ERR("Cannot remove a mmIOProc while in use\n");
+               break;
+           }
+           /* remove it, but only if it isn't builtin */
+           if ((*ppListNode) >= defaultProcs &&
+               (*ppListNode) < defaultProcs + sizeof(defaultProcs)) {
+               WARN("Tried to remove built-in mmio proc. Skipping\n");
+           } else {
+               /* Okay, nuke it */
+               struct IOProcList*  ptmpNode = *ppListNode;
+               lpProc = (*ppListNode)->pIOProc;
+               *ppListNode = (*ppListNode)->pNext;
+               HeapFree(GetProcessHeap(), 0, ptmpNode);
+           }
+       }
+       break;
+
+    case MMIO_FINDPROC:
+       if ((pListNode = MMIO_FindProcNode(fccIOProc))) {
+           lpProc = pListNode->pIOProc;
+       }
+       break;
+    }
+
+    return lpProc;
+}
+
+/****************************************************************
+ *             send_message                            [INTERNAL]
+ */
+static LRESULT send_message(struct IOProcList* ioProc, LPMMIOINFO mmioinfo,
+                             DWORD wMsg, LPARAM lParam1,
+                             LPARAM lParam2, enum mmioProcType type)
+{
+    LRESULT            result = MMSYSERR_ERROR;
+    LPARAM             lp1 = lParam1, lp2 = lParam2;
+
+    if (!ioProc) {
+       ERR("brrr\n");
+       result = MMSYSERR_INVALPARAM;
+    }
+
+    switch (ioProc->type) {
+    case MMIO_PROC_16:
+        if (pFnMmioCallback16)
+            result = pFnMmioCallback16((DWORD)ioProc->pIOProc,
+                                       mmioinfo, wMsg, lp1, lp2);
+        break;
+    case MMIO_PROC_32A:
+    case MMIO_PROC_32W:
+       if (ioProc->type != type) {
+           /* map (lParam1, lParam2) into (lp1, lp2) 32 A<=>W */
+           FIXME("NIY 32 A<=>W mapping\n");
+       }
+       result = (ioProc->pIOProc)((LPSTR)mmioinfo, wMsg, lp1, lp2);
+
+#if 0
+       if (ioProc->type != type) {
+           /* unmap (lParam1, lParam2) into (lp1, lp2) 32 A<=>W */
+       }
+#endif
+       break;
+    default:
+       FIXME("Internal error\n");
+    }
+
+    return result;
+}
+
+/**************************************************************************
+ *                             MMIO_ParseExtA                  [internal]
+ *
+ * Parses a filename for the extension.
+ *
+ * RETURNS
+ *  The FOURCC code for the extension if found, else 0.
+ */
+static FOURCC MMIO_ParseExtA(LPCSTR szFileName)
+{
+    /* Filenames are of the form file.ext{+ABC}
+       For now, we take the last '+' if present */
+
+    FOURCC ret = 0;
+
+    /* Note that ext{Start,End} point to the . and + respectively */
+    LPSTR extEnd;
+    LPSTR extStart;
+
+    TRACE("(%s)\n", debugstr_a(szFileName));
+
+    if (!szFileName)
+       return ret;
+
+    /* Find the last '.' */
+    extStart = strrchr(szFileName,'.');
+
+    if (!extStart) {
+         ERR("No . in szFileName: %s\n", debugstr_a(szFileName));
+    } else {
+        CHAR ext[5];
+
+        /* Find the '+' afterwards */
+        extEnd = strchr(extStart,'+');
+        if (extEnd) {
+
+            if (extEnd - extStart - 1 > 4)
+                WARN("Extension length > 4\n");
+            lstrcpynA(ext, extStart + 1, min(extEnd-extStart,5));
+
+        } else {
+            /* No + so just an extension */
+            if (strlen(extStart) > 4) {
+                WARN("Extension length > 4\n");
+            }
+            lstrcpynA(ext, extStart + 1, 5);
+        }
+        TRACE("Got extension: %s\n", debugstr_a(ext));
+
+        /* FOURCC codes identifying file-extensions must be uppercase */
+        ret = mmioStringToFOURCCA(ext, MMIO_TOUPPER);
+    }
+    return ret;
+}
+
+/**************************************************************************
+ *                             MMIO_Get                        [internal]
+ *
+ * Retrieves the mmio object from current process
+ */
+LPWINE_MMIO    MMIO_Get(HMMIO h)
+{
+    LPWINE_MMIO                wm = NULL;
+
+    EnterCriticalSection(&WINMM_IData.cs);
+    for (wm = WINMM_IData.lpMMIO; wm; wm = wm->lpNext) {
+       if (wm->info.hmmio == h)
+           break;
+    }
+    LeaveCriticalSection(&WINMM_IData.cs);
+    return wm;
+}
+
+/**************************************************************************
+ *                             MMIO_Create                     [internal]
+ *
+ * Creates an internal representation for a mmio instance
+ */
+static LPWINE_MMIO             MMIO_Create(void)
+{
+    static     WORD    MMIO_counter = 0;
+    LPWINE_MMIO                wm;
+
+    wm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MMIO));
+    if (wm) {
+       EnterCriticalSection(&WINMM_IData.cs);
+        /* lookup next unallocated WORD handle, with a non NULL value */
+       while (++MMIO_counter == 0 || MMIO_Get((HMMIO)(ULONG_PTR)MMIO_counter));
+       wm->info.hmmio = (HMMIO)(ULONG_PTR)MMIO_counter;
+       wm->lpNext = WINMM_IData.lpMMIO;
+       WINMM_IData.lpMMIO = wm;
+       LeaveCriticalSection(&WINMM_IData.cs);
+    }
+    return wm;
+}
+
+/**************************************************************************
+ *                             MMIO_Destroy                    [internal]
+ *-
+ * Destroys an internal representation for a mmio instance
+ */
+static BOOL            MMIO_Destroy(LPWINE_MMIO wm)
+{
+    LPWINE_MMIO*       m;
+
+    EnterCriticalSection(&WINMM_IData.cs);
+    /* search for the matching one... */
+    m = &(WINMM_IData.lpMMIO);
+    while (*m && *m != wm) m = &(*m)->lpNext;
+    /* ...and destroy */
+    if (*m) {
+       *m = (*m)->lpNext;
+       HeapFree(GetProcessHeap(), 0, wm);
+       wm = NULL;
+    }
+    LeaveCriticalSection(&WINMM_IData.cs);
+    return wm ? FALSE : TRUE;
+}
+
+/****************************************************************
+ *                     MMIO_Flush                      [INTERNAL]
+ */
+static MMRESULT MMIO_Flush(WINE_MMIO* wm, UINT uFlags)
+{
+    if (wm->info.cchBuffer && (wm->info.fccIOProc != FOURCC_MEM)) {
+       /* not quite sure what to do here, but I'll guess */
+       if (wm->info.dwFlags & MMIO_DIRTY) {
+            /* FIXME: error handling */
+           send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 
+                         wm->info.lBufOffset, SEEK_SET, MMIO_PROC_32A);
+           send_message(wm->ioProc, &wm->info, MMIOM_WRITE, 
+                         (LPARAM)wm->info.pchBuffer,
+                         wm->info.pchNext - wm->info.pchBuffer, MMIO_PROC_32A);
+       }
+       if (uFlags & MMIO_EMPTYBUF)
+           wm->info.pchNext = wm->info.pchEndRead = wm->info.pchBuffer;
+    }
+    wm->info.dwFlags &= ~MMIO_DIRTY;
+
+    return MMSYSERR_NOERROR;
+}
+
+/***************************************************************************
+ *                     MMIO_GrabNextBuffer                     [INTERNAL]
+ */
+static LONG    MMIO_GrabNextBuffer(LPWINE_MMIO wm, int for_read)
+{
+    LONG       size = wm->info.cchBuffer;
+
+    TRACE("bo=%lx do=%lx of=%lx\n",
+         wm->info.lBufOffset, wm->info.lDiskOffset,
+         send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 0, SEEK_CUR, MMIO_PROC_32A));
+
+    wm->info.lBufOffset = wm->info.lDiskOffset;
+    wm->info.pchNext = wm->info.pchBuffer;
+    wm->info.pchEndRead = wm->info.pchBuffer;
+    wm->info.pchEndWrite = wm->info.pchBuffer + wm->info.cchBuffer;
+
+    wm->bBufferLoaded = TRUE;
+    if (for_read) {
+       size = send_message(wm->ioProc, &wm->info, MMIOM_READ, 
+                            (LPARAM)wm->info.pchBuffer, size, MMIO_PROC_32A);
+       if (size > 0)
+           wm->info.pchEndRead += size;
+        else
+            wm->bBufferLoaded = FALSE;
+    }
+
+    return size;
+}
+
+/***************************************************************************
+ *                     MMIO_SetBuffer                          [INTERNAL]
+ */
+static MMRESULT MMIO_SetBuffer(WINE_MMIO* wm, void* pchBuffer, LONG cchBuffer,
+                              UINT uFlags)
+{
+    TRACE("(%p %p %ld %u)\n", wm, pchBuffer, cchBuffer, uFlags);
+
+    if (uFlags)                        return MMSYSERR_INVALPARAM;
+    if (cchBuffer > 0xFFFF)
+       WARN("Untested handling of huge mmio buffers (%ld >= 64k)\n", cchBuffer);
+
+    if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)
+       return MMIOERR_CANNOTWRITE;
+
+    /* free previous buffer if allocated */
+    if (wm->info.dwFlags & MMIO_ALLOCBUF) {
+        HeapFree(GetProcessHeap(), 0, wm->info.pchBuffer);
+        wm->info.pchBuffer = NULL;
+       wm->info.dwFlags &= ~MMIO_ALLOCBUF;
+    }
+
+    if (pchBuffer) {
+        wm->info.pchBuffer = pchBuffer;
+    } else if (cchBuffer) {
+       if (!(wm->info.pchBuffer = HeapAlloc(GetProcessHeap(), 0, cchBuffer)))
+           return MMIOERR_OUTOFMEMORY;
+       wm->info.dwFlags |= MMIO_ALLOCBUF;
+    } else {
+       wm->info.pchBuffer = NULL;
+    }
+
+    wm->info.cchBuffer = cchBuffer;
+    wm->info.pchNext = wm->info.pchBuffer;
+    wm->info.pchEndRead = wm->info.pchBuffer;
+    wm->info.pchEndWrite = wm->info.pchBuffer + cchBuffer;
+    wm->info.lBufOffset = 0;
+    wm->bBufferLoaded = FALSE;
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                     MMIO_Open                               [internal]
+ */
+HMMIO MMIO_Open(LPSTR szFileName, MMIOINFO* refmminfo, DWORD dwOpenFlags, 
+                enum mmioProcType type)
+{
+    LPWINE_MMIO                wm;
+    MMIOINFO           mmioinfo;
+
+    TRACE("('%s', %p, %08lX, %d);\n", szFileName, refmminfo, dwOpenFlags, type);
+
+    if (!refmminfo) {
+        refmminfo = &mmioinfo;
+
+       mmioinfo.fccIOProc = 0;
+       mmioinfo.pIOProc = NULL;
+       mmioinfo.pchBuffer = NULL;
+       mmioinfo.cchBuffer = 0;
+        type = MMIO_PROC_32A;
+    }
+
+    if (dwOpenFlags & (MMIO_PARSE|MMIO_EXIST)) {
+       char    buffer[MAX_PATH];
+
+       if (GetFullPathNameA(szFileName, sizeof(buffer), buffer, NULL) >= sizeof(buffer))
+           return (HMMIO)FALSE;
+       if ((dwOpenFlags & MMIO_EXIST) && (GetFileAttributesA(buffer) == INVALID_FILE_ATTRIBUTES))
+           return (HMMIO)FALSE;
+       strcpy(szFileName, buffer);
+       return (HMMIO)TRUE;
+    }
+
+    if ((wm = MMIO_Create()) == NULL)
+       return 0;
+
+    /* If both params are NULL, then parse the file name if available */
+    if (refmminfo->fccIOProc == 0 && refmminfo->pIOProc == NULL) {
+       wm->info.fccIOProc = MMIO_ParseExtA(szFileName);
+       /* Handle any unhandled/error case. Assume DOS file */
+       if (wm->info.fccIOProc == 0)
+           wm->info.fccIOProc = FOURCC_DOS;
+       if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) {
+           /* If not found, retry with FOURCC_DOS */
+           wm->info.fccIOProc = FOURCC_DOS;
+           if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc)))
+               goto error2;
+       }
+       wm->bTmpIOProc = FALSE;
+    }
+    /* if just the four character code is present, look up IO proc */
+    else if (refmminfo->pIOProc == NULL) {
+       wm->info.fccIOProc = refmminfo->fccIOProc;
+       if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;
+       wm->bTmpIOProc = FALSE;
+    }
+    /* if IO proc specified, use it and specified four character code */
+    else {
+       wm->info.fccIOProc = refmminfo->fccIOProc;
+       MMIO_InstallIOProc(wm->info.fccIOProc, refmminfo->pIOProc,
+                           MMIO_INSTALLPROC, type);
+       if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;
+       assert(wm->ioProc->pIOProc == refmminfo->pIOProc);
+       wm->bTmpIOProc = TRUE;
+    }
+
+    wm->bBufferLoaded = FALSE;
+    wm->ioProc->count++;
+
+    if (dwOpenFlags & MMIO_ALLOCBUF) {
+       if ((refmminfo->wErrorRet = MMIO_SetBuffer(wm, NULL, MMIO_DEFAULTBUFFER, 0)))
+           goto error1;
+    } else if (wm->info.fccIOProc == FOURCC_MEM) {
+        refmminfo->wErrorRet = MMIO_SetBuffer(wm, refmminfo->pchBuffer, refmminfo->cchBuffer, 0);
+       if (refmminfo->wErrorRet != MMSYSERR_NOERROR)
+           goto error1;
+       wm->bBufferLoaded = TRUE;
+    } /* else => unbuffered, wm->info.pchBuffer == NULL */
+
+    /* see mmioDosIOProc for that one */
+    wm->info.adwInfo[0] = refmminfo->adwInfo[0];
+    wm->info.dwFlags = dwOpenFlags;
+
+    /* call IO proc to actually open file */
+    refmminfo->wErrorRet = send_message(wm->ioProc, &wm->info, MMIOM_OPEN, 
+                                        (LPARAM)szFileName, 0, MMIO_PROC_32A);
+
+    /* grab file size, when possible */
+    wm->dwFileSize = GetFileSize((HANDLE)wm->info.adwInfo[0], NULL);
+
+    if (refmminfo->wErrorRet == 0)
+       return wm->info.hmmio;
+ error1:
+    if (wm->ioProc) wm->ioProc->count--;
+ error2:
+    MMIO_Destroy(wm);
+    return 0;
+}
+
+/**************************************************************************
+ *                             mmioOpenW                       [WINMM.@]
+ */
+HMMIO WINAPI mmioOpenW(LPWSTR szFileName, MMIOINFO* lpmmioinfo,
+                      DWORD dwOpenFlags)
+{
+    HMMIO      ret;
+    LPSTR      szFn = NULL;
+
+    if (szFileName)
+    {
+        INT     len = WideCharToMultiByte( CP_ACP, 0, szFileName, -1, NULL, 0, NULL, NULL );
+        szFn = HeapAlloc( GetProcessHeap(), 0, len );
+        if (!szFn) return NULL;
+        WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szFn, len, NULL, NULL );
+    }
+
+    ret = MMIO_Open(szFn, lpmmioinfo, dwOpenFlags, MMIO_PROC_32W);
+
+    HeapFree(GetProcessHeap(), 0, szFn);
+    return ret;
+}
+
+/**************************************************************************
+ *                             mmioOpenA                       [WINMM.@]
+ */
+HMMIO WINAPI mmioOpenA(LPSTR szFileName, MMIOINFO* lpmmioinfo,
+                      DWORD dwOpenFlags)
+{
+    return  MMIO_Open(szFileName, lpmmioinfo, dwOpenFlags, MMIO_PROC_32A);
+}
+
+/**************************************************************************
+ *                             mmioClose               [WINMM.@]
+ */
+MMRESULT WINAPI mmioClose(HMMIO hmmio, UINT uFlags)
+{
+    LPWINE_MMIO        wm;
+    MMRESULT   result;
+
+    TRACE("(%p, %04X);\n", hmmio, uFlags);
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return MMSYSERR_INVALHANDLE;
+
+    if ((result = MMIO_Flush(wm, 0)) != MMSYSERR_NOERROR)
+       return result;
+
+    result = send_message(wm->ioProc, &wm->info, MMIOM_CLOSE, 
+                          uFlags, 0, MMIO_PROC_32A);
+
+    MMIO_SetBuffer(wm, NULL, 0, 0);
+
+    wm->ioProc->count--;
+
+    if (wm->bTmpIOProc)
+       MMIO_InstallIOProc(wm->info.fccIOProc, wm->ioProc->pIOProc,
+                           MMIO_REMOVEPROC, wm->ioProc->type);
+
+    MMIO_Destroy(wm);
+
+    return result;
+}
+
+/**************************************************************************
+ *                             mmioRead                [WINMM.@]
+ */
+LONG WINAPI mmioRead(HMMIO hmmio, HPSTR pch, LONG cch)
+{
+    LPWINE_MMIO        wm;
+    LONG       count;
+
+    TRACE("(%p, %p, %ld);\n", hmmio, pch, cch);
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return -1;
+
+    /* unbuffered case first */
+    if (!wm->info.pchBuffer)
+       return send_message(wm->ioProc, &wm->info, MMIOM_READ, 
+                            (LPARAM)pch, cch, MMIO_PROC_32A);
+
+    /* first try from current buffer */
+    if (wm->info.pchNext != wm->info.pchEndRead) {
+       count = wm->info.pchEndRead - wm->info.pchNext;
+       if (count > cch || count < 0) count = cch;
+       memcpy(pch, wm->info.pchNext, count);
+       wm->info.pchNext += count;
+       pch += count;
+       cch -= count;
+    } else
+       count = 0;
+
+    if (cch && (wm->info.fccIOProc != FOURCC_MEM)) {
+       assert(wm->info.cchBuffer);
+
+       while (cch) {
+           LONG size;
+
+           size = MMIO_GrabNextBuffer(wm, TRUE);
+           if (size <= 0) break;
+           if (size > cch) size = cch;
+           memcpy(pch, wm->info.pchBuffer, size);
+           wm->info.pchNext += size;
+           pch += size;
+           cch -= size;
+           count += size;
+       }
+    }
+
+    TRACE("count=%ld\n", count);
+    return count;
+}
+
+/**************************************************************************
+ *                             mmioWrite               [WINMM.@]
+ */
+LONG WINAPI mmioWrite(HMMIO hmmio, HPCSTR pch, LONG cch)
+{
+    LPWINE_MMIO        wm;
+    LONG       count;
+
+    TRACE("(%p, %p, %ld);\n", hmmio, pch, cch);
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return -1;
+
+    if (wm->info.cchBuffer) {
+       LONG    bytesW = 0;
+
+        count = 0;
+        while (cch) {
+            if (wm->info.pchNext != wm->info.pchEndWrite) {
+                count = wm->info.pchEndWrite - wm->info.pchNext;
+                if (count > cch || count < 0) count = cch;
+                memcpy(wm->info.pchNext, pch, count);
+                wm->info.pchNext += count;
+                pch += count;
+                cch -= count;
+                bytesW += count;
+                wm->info.dwFlags |= MMIO_DIRTY;
+           } else {
+                if (wm->info.fccIOProc == FOURCC_MEM) {
+                    if (wm->info.adwInfo[0]) {
+                        /* from where would we get the memory handle? */
+                        FIXME("memory file expansion not implemented!\n");
+                        break;
+                   } else break;
+                }
+            }
+
+            if (wm->info.pchNext == wm->info.pchEndWrite)
+            {
+                MMIO_Flush(wm, MMIO_EMPTYBUF);
+                MMIO_GrabNextBuffer(wm, FALSE);
+            }
+            else break;
+        }
+       count = bytesW;
+    } else {
+       count = send_message(wm->ioProc, &wm->info, MMIOM_WRITE, 
+                             (LPARAM)pch, cch, MMIO_PROC_32A);
+       wm->info.lBufOffset = wm->info.lDiskOffset;
+    }
+
+    TRACE("bytes written=%ld\n", count);
+    return count;
+}
+
+/**************************************************************************
+ *                             mmioSeek                [WINMM.@]
+ */
+LONG WINAPI mmioSeek(HMMIO hmmio, LONG lOffset, INT iOrigin)
+{
+    LPWINE_MMIO        wm;
+    LONG       offset;
+
+    TRACE("(%p, %08lX, %d);\n", hmmio, lOffset, iOrigin);
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return MMSYSERR_INVALHANDLE;
+
+    /* not buffered, direct seek on file */
+    if (!wm->info.pchBuffer)
+       return send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 
+                            lOffset, iOrigin, MMIO_PROC_32A);
+
+    switch (iOrigin) {
+    case SEEK_SET:
+       offset = lOffset;
+       break;
+    case SEEK_CUR:
+       offset = wm->info.lBufOffset + (wm->info.pchNext - wm->info.pchBuffer) + lOffset;
+       break;
+    case SEEK_END:
+       offset = ((wm->info.fccIOProc == FOURCC_MEM)? wm->info.cchBuffer : wm->dwFileSize) - lOffset;
+       break;
+    default:
+       return -1;
+    }
+
+    if (offset && offset >= wm->dwFileSize && wm->info.fccIOProc != FOURCC_MEM) {
+        /* should check that write mode exists */
+        if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)
+            return -1;
+        wm->info.lBufOffset = offset;
+        wm->info.pchEndRead = wm->info.pchBuffer;
+        wm->info.pchEndWrite = wm->info.pchBuffer + wm->info.cchBuffer;
+        if ((wm->info.dwFlags & MMIO_RWMODE) == MMIO_READ) {
+            wm->info.lDiskOffset = wm->dwFileSize;
+        }
+    } else if ((wm->info.cchBuffer > 0) &&
+       ((offset < wm->info.lBufOffset) ||
+        (offset >= wm->info.lBufOffset + wm->info.cchBuffer) ||
+        !wm->bBufferLoaded)) {
+        /* stay in same buffer ? */
+        /* some memory mapped buffers are defined with -1 as a size */
+
+       /* condition to change buffer */
+       if ((wm->info.fccIOProc == FOURCC_MEM) ||
+           MMIO_Flush(wm, 0) != MMSYSERR_NOERROR ||
+           /* this also sets the wm->info.lDiskOffset field */
+           send_message(wm->ioProc, &wm->info, MMIOM_SEEK,
+                         (offset / wm->info.cchBuffer) * wm->info.cchBuffer,
+                         SEEK_SET, MMIO_PROC_32A) == -1)
+           return -1;
+       MMIO_GrabNextBuffer(wm, TRUE);
+    }
+
+    wm->info.pchNext = wm->info.pchBuffer + (offset - wm->info.lBufOffset);
+
+    TRACE("=> %ld\n", offset);
+    return offset;
+}
+
+/**************************************************************************
+ *                             mmioGetInfo             [WINMM.@]
+ */
+MMRESULT WINAPI mmioGetInfo(HMMIO hmmio, MMIOINFO* lpmmioinfo, UINT uFlags)
+{
+    LPWINE_MMIO                wm;
+
+    TRACE("(%p,%p,0x%08x)\n",hmmio,lpmmioinfo,uFlags);
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return MMSYSERR_INVALHANDLE;
+
+    memcpy(lpmmioinfo, &wm->info, sizeof(MMIOINFO));
+    /* don't expose 16 bit ioproc:s */
+    if (wm->ioProc->type != MMIO_PROC_16)
+        lpmmioinfo->pIOProc = wm->ioProc->pIOProc;
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                             mmioSetInfo             [WINMM.@]
+ */
+MMRESULT WINAPI mmioSetInfo(HMMIO hmmio, const MMIOINFO* lpmmioinfo, UINT uFlags)
+{
+    LPWINE_MMIO                wm;
+
+    TRACE("(%p,%p,0x%08x)\n",hmmio,lpmmioinfo,uFlags);
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return MMSYSERR_INVALHANDLE;
+
+    /* check pointers coherence */
+    if (lpmmioinfo->pchNext < wm->info.pchBuffer ||
+       lpmmioinfo->pchNext > wm->info.pchBuffer + wm->info.cchBuffer ||
+       lpmmioinfo->pchEndRead < wm->info.pchBuffer ||
+       lpmmioinfo->pchEndRead > wm->info.pchBuffer + wm->info.cchBuffer ||
+       lpmmioinfo->pchEndWrite < wm->info.pchBuffer ||
+       lpmmioinfo->pchEndWrite > wm->info.pchBuffer + wm->info.cchBuffer)
+       return MMSYSERR_INVALPARAM;
+
+    wm->info.pchNext = lpmmioinfo->pchNext;
+    wm->info.pchEndRead = lpmmioinfo->pchEndRead;
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+*                              mmioSetBuffer           [WINMM.@]
+*/
+MMRESULT WINAPI mmioSetBuffer(HMMIO hmmio, LPSTR pchBuffer, LONG cchBuffer, UINT uFlags)
+{
+    LPWINE_MMIO                wm;
+
+    TRACE("(hmmio=%p, pchBuf=%p, cchBuf=%ld, uFlags=%#08x)\n",
+         hmmio, pchBuffer, cchBuffer, uFlags);
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return MMSYSERR_INVALHANDLE;
+
+    return MMIO_SetBuffer(wm, pchBuffer, cchBuffer, uFlags);
+}
+
+/**************************************************************************
+ *                             mmioFlush               [WINMM.@]
+ */
+MMRESULT WINAPI mmioFlush(HMMIO hmmio, UINT uFlags)
+{
+    LPWINE_MMIO                wm;
+
+    TRACE("(%p, %04X)\n", hmmio, uFlags);
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return MMSYSERR_INVALHANDLE;
+
+    return MMIO_Flush(wm, uFlags);
+}
+
+/**************************************************************************
+ *                             mmioAdvance             [WINMM.@]
+ */
+MMRESULT WINAPI mmioAdvance(HMMIO hmmio, MMIOINFO* lpmmioinfo, UINT uFlags)
+{
+    LPWINE_MMIO                wm;
+
+    TRACE("hmmio=%p, lpmmioinfo=%p, uFlags=%04X\n", hmmio, lpmmioinfo, uFlags);
+
+    /* NOTE: mmioAdvance16 heavily relies on parameters from lpmmioinfo we're using
+     * here. be sure if you change something here to check mmioAdvance16 as well
+     */
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return MMSYSERR_INVALHANDLE;
+
+    if (!wm->info.cchBuffer)
+       return MMIOERR_UNBUFFERED;
+
+    if (uFlags != MMIO_READ && uFlags != MMIO_WRITE)
+       return MMSYSERR_INVALPARAM;
+
+    if (uFlags == MMIO_WRITE && (lpmmioinfo->dwFlags & MMIO_DIRTY))
+    {
+        send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 
+                     lpmmioinfo->lBufOffset, SEEK_SET, MMIO_PROC_32A);
+        send_message(wm->ioProc, &wm->info, MMIOM_WRITE, 
+                     (LPARAM)lpmmioinfo->pchBuffer,
+                     lpmmioinfo->pchNext - lpmmioinfo->pchBuffer, MMIO_PROC_32A);
+        lpmmioinfo->dwFlags &= ~MMIO_DIRTY;
+    }
+    if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)
+       return MMIOERR_CANNOTWRITE;
+
+    if (lpmmioinfo) {
+       wm->dwFileSize = max(wm->dwFileSize, lpmmioinfo->lBufOffset + 
+                             (lpmmioinfo->pchNext - lpmmioinfo->pchBuffer));
+    }
+    MMIO_GrabNextBuffer(wm, uFlags == MMIO_READ);
+
+    if (lpmmioinfo) {
+       lpmmioinfo->pchNext = lpmmioinfo->pchBuffer;
+       lpmmioinfo->pchEndRead  = lpmmioinfo->pchBuffer +
+           (wm->info.pchEndRead - wm->info.pchBuffer);
+       lpmmioinfo->pchEndWrite = lpmmioinfo->pchBuffer +
+           (wm->info.pchEndWrite - wm->info.pchBuffer);
+       lpmmioinfo->lDiskOffset = wm->info.lDiskOffset;
+       lpmmioinfo->lBufOffset = wm->info.lBufOffset;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                             mmioStringToFOURCCA     [WINMM.@]
+ */
+FOURCC WINAPI mmioStringToFOURCCA(LPCSTR sz, UINT uFlags)
+{
+    CHAR cc[4];
+    int i = 0;
+
+    for (i = 0; i < 4 && sz[i]; i++) {
+       if (uFlags & MMIO_TOUPPER) {
+           cc[i] = toupper(sz[i]);
+       } else {
+           cc[i] = sz[i];
+       }
+    }
+
+    /* Pad with spaces */
+    while (i < 4) cc[i++] = ' ';
+
+    TRACE("Got '%.4s'\n",cc);
+    return mmioFOURCC(cc[0],cc[1],cc[2],cc[3]);
+}
+
+/**************************************************************************
+ *                             mmioStringToFOURCCW     [WINMM.@]
+ */
+FOURCC WINAPI mmioStringToFOURCCW(LPCWSTR sz, UINT uFlags)
+{
+    char       szA[4];
+
+    WideCharToMultiByte( CP_ACP, 0, sz, 4, szA, sizeof(szA), NULL, NULL );
+    return mmioStringToFOURCCA(szA,uFlags);
+}
+
+/**************************************************************************
+ *                             mmioInstallIOProcA         [WINMM.@]
+ */
+LPMMIOPROC WINAPI mmioInstallIOProcA(FOURCC fccIOProc,
+                                    LPMMIOPROC pIOProc, DWORD dwFlags)
+{
+    return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, MMIO_PROC_32A);
+}
+
+/**************************************************************************
+ *                             mmioInstallIOProcW         [WINMM.@]
+ */
+LPMMIOPROC WINAPI mmioInstallIOProcW(FOURCC fccIOProc,
+                                    LPMMIOPROC pIOProc, DWORD dwFlags)
+{
+    return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, MMIO_PROC_32W);
+}
+
+/******************************************************************
+ *             MMIO_SendMessage
+ *
+ *
+ */
+LRESULT         MMIO_SendMessage(HMMIO hmmio, UINT uMessage, LPARAM lParam1, 
+                                 LPARAM lParam2, enum mmioProcType type)
+{
+    LPWINE_MMIO                wm;
+
+    TRACE("(%p, %u, %ld, %ld, %d)\n", hmmio, uMessage, lParam1, lParam2, type);
+
+    if (uMessage < MMIOM_USER)
+       return MMSYSERR_INVALPARAM;
+
+    if ((wm = MMIO_Get(hmmio)) == NULL)
+       return MMSYSERR_INVALHANDLE;
+
+    return send_message(wm->ioProc, &wm->info, uMessage, lParam1, lParam2, type);
+}
+
+/**************************************************************************
+ *                             mmioSendMessage         [WINMM.@]
+ */
+LRESULT WINAPI mmioSendMessage(HMMIO hmmio, UINT uMessage,
+                              LPARAM lParam1, LPARAM lParam2)
+{
+    return MMIO_SendMessage(hmmio, uMessage, lParam1, lParam2, MMIO_PROC_32A);
+}
+
+/**************************************************************************
+ *                             mmioDescend             [WINMM.@]
+ */
+MMRESULT WINAPI mmioDescend(HMMIO hmmio, LPMMCKINFO lpck,
+                            const MMCKINFO* lpckParent, UINT uFlags)
+{
+    DWORD              dwOldPos;
+    FOURCC             srchCkId;
+    FOURCC             srchType;
+
+
+    TRACE("(%p, %p, %p, %04X);\n", hmmio, lpck, lpckParent, uFlags);
+
+    if (lpck == NULL)
+       return MMSYSERR_INVALPARAM;
+
+    dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
+    TRACE("dwOldPos=%ld\n", dwOldPos);
+
+    if (lpckParent != NULL) {
+       TRACE("seek inside parent at %ld !\n", lpckParent->dwDataOffset);
+       /* EPP: was dwOldPos = mmioSeek(hmmio,lpckParent->dwDataOffset,SEEK_SET); */
+       if (dwOldPos < lpckParent->dwDataOffset ||
+           dwOldPos >= lpckParent->dwDataOffset + lpckParent->cksize) {
+           WARN("outside parent chunk\n");
+           return MMIOERR_CHUNKNOTFOUND;
+       }
+    }
+
+    /* The SDK docu says 'ckid' is used for all cases. Real World
+     * examples disagree -Marcus,990216.
+     */
+
+    srchType = 0;
+    /* find_chunk looks for 'ckid' */
+    if (uFlags & MMIO_FINDCHUNK)
+       srchCkId = lpck->ckid;
+    /* find_riff and find_list look for 'fccType' */
+    if (uFlags & MMIO_FINDLIST) {
+       srchCkId = FOURCC_LIST;
+       srchType = lpck->fccType;
+    }
+    if (uFlags & MMIO_FINDRIFF) {
+       srchCkId = FOURCC_RIFF;
+       srchType = lpck->fccType;
+    }
+
+    if (uFlags & (MMIO_FINDCHUNK|MMIO_FINDLIST|MMIO_FINDRIFF)) {
+       TRACE("searching for %.4s.%.4s\n",
+             (LPSTR)&srchCkId,
+             srchType?(LPSTR)&srchType:"any ");
+
+       while (TRUE) {
+           LONG ix;
+
+           ix = mmioRead(hmmio, (LPSTR)lpck, 3 * sizeof(DWORD));
+           if (ix < 2*sizeof(DWORD)) {
+               mmioSeek(hmmio, dwOldPos, SEEK_SET);
+               WARN("return ChunkNotFound\n");
+               return MMIOERR_CHUNKNOTFOUND;
+           }
+           lpck->dwDataOffset = dwOldPos + 2 * sizeof(DWORD);
+           if (ix < lpck->dwDataOffset - dwOldPos) {
+               mmioSeek(hmmio, dwOldPos, SEEK_SET);
+               WARN("return ChunkNotFound\n");
+               return MMIOERR_CHUNKNOTFOUND;
+           }
+           TRACE("ckid=%.4s fcc=%.4s cksize=%08lX !\n",
+                 (LPSTR)&lpck->ckid,
+                 srchType?(LPSTR)&lpck->fccType:"<na>",
+                 lpck->cksize);
+           if ((srchCkId == lpck->ckid) &&
+               (!srchType || (srchType == lpck->fccType))
+               )
+               break;
+
+           dwOldPos = lpck->dwDataOffset + ((lpck->cksize + 1) & ~1);
+           mmioSeek(hmmio, dwOldPos, SEEK_SET);
+       }
+    } else {
+       /* FIXME: unverified, does it do this? */
+       /* NB: This part is used by WAVE_mciOpen, among others */
+       if (mmioRead(hmmio, (LPSTR)lpck, 3 * sizeof(DWORD)) < 3 * sizeof(DWORD)) {
+           mmioSeek(hmmio, dwOldPos, SEEK_SET);
+           WARN("return ChunkNotFound 2nd\n");
+           return MMIOERR_CHUNKNOTFOUND;
+       }
+       lpck->dwDataOffset = dwOldPos + 2 * sizeof(DWORD);
+    }
+    lpck->dwFlags = 0;
+    /* If we were looking for RIFF/LIST chunks, the final file position
+     * is after the chunkid. If we were just looking for the chunk
+     * it is after the cksize. So add 4 in RIFF/LIST case.
+     */
+    if (lpck->ckid == FOURCC_RIFF || lpck->ckid == FOURCC_LIST)
+       mmioSeek(hmmio, lpck->dwDataOffset + sizeof(DWORD), SEEK_SET);
+    else
+       mmioSeek(hmmio, lpck->dwDataOffset, SEEK_SET);
+    TRACE("lpck: ckid=%.4s, cksize=%ld, dwDataOffset=%ld fccType=%08lX (%.4s)!\n",
+         (LPSTR)&lpck->ckid, lpck->cksize, lpck->dwDataOffset,
+         lpck->fccType, srchType?(LPSTR)&lpck->fccType:"");
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                             mmioAscend              [WINMM.@]
+ */
+MMRESULT WINAPI mmioAscend(HMMIO hmmio, LPMMCKINFO lpck, UINT uFlags)
+{
+    TRACE("(%p, %p, %04X);\n", hmmio, lpck, uFlags);
+
+    if (lpck->dwFlags & MMIO_DIRTY) {
+       DWORD   dwOldPos, dwNewSize;
+
+       TRACE("Chunk is dirty, checking if chunk's size is correct\n");
+       dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
+       TRACE("dwOldPos=%ld lpck->dwDataOffset = %ld\n", dwOldPos, lpck->dwDataOffset);
+       dwNewSize = dwOldPos - lpck->dwDataOffset;
+       if (dwNewSize != lpck->cksize) {
+           TRACE("Nope: lpck->cksize=%ld dwNewSize=%ld\n", lpck->cksize, dwNewSize);
+           lpck->cksize = dwNewSize;
+
+           /* pad odd size with 0 */
+           if (dwNewSize & 1) {
+               char ch = 0;
+               mmioWrite(hmmio, &ch, 1);
+           }
+           mmioSeek(hmmio, lpck->dwDataOffset - sizeof(DWORD), SEEK_SET);
+           mmioWrite(hmmio, (LPSTR)&dwNewSize, sizeof(DWORD));
+       }
+       lpck->dwFlags = 0;
+    }
+
+    mmioSeek(hmmio, lpck->dwDataOffset + ((lpck->cksize + 1) & ~1), SEEK_SET);
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                     mmioCreateChunk                         [WINMM.@]
+ */
+MMRESULT WINAPI mmioCreateChunk(HMMIO hmmio, MMCKINFO* lpck, UINT uFlags)
+{
+    DWORD      dwOldPos;
+    LONG       size;
+    LONG       ix;
+
+    TRACE("(%p, %p, %04X);\n", hmmio, lpck, uFlags);
+
+    dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
+    TRACE("dwOldPos=%ld\n", dwOldPos);
+
+    if (uFlags == MMIO_CREATELIST)
+       lpck->ckid = FOURCC_LIST;
+    else if (uFlags == MMIO_CREATERIFF)
+       lpck->ckid = FOURCC_RIFF;
+
+    TRACE("ckid=%.4s\n", (LPSTR)&lpck->ckid);
+
+    size = 2 * sizeof(DWORD);
+    lpck->dwDataOffset = dwOldPos + size;
+
+    if (lpck->ckid == FOURCC_RIFF || lpck->ckid == FOURCC_LIST)
+       size += sizeof(DWORD);
+    lpck->dwFlags = MMIO_DIRTY;
+
+    ix = mmioWrite(hmmio, (LPSTR)lpck, size);
+    TRACE("after mmioWrite ix = %ld req = %ld, errno = %d\n",ix, size, errno);
+    if (ix < size) {
+       mmioSeek(hmmio, dwOldPos, SEEK_SET);
+       WARN("return CannotWrite\n");
+       return MMIOERR_CANNOTWRITE;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                             mmioRenameA                     [WINMM.@]
+ */
+MMRESULT WINAPI mmioRenameA(LPCSTR szFileName, LPCSTR szNewFileName,
+                            const MMIOINFO* lpmmioinfo, DWORD dwFlags)
+{
+    struct IOProcList*  ioProc = NULL;
+    struct IOProcList   tmp;
+    FOURCC              fcc;
+
+    TRACE("('%s', '%s', %p, %08lX);\n",
+         debugstr_a(szFileName), debugstr_a(szNewFileName), lpmmioinfo, dwFlags);
+
+    /* If both params are NULL, then parse the file name */
+    if (lpmmioinfo && lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL)
+    {
+       fcc = MMIO_ParseExtA(szFileName);
+        if (fcc) ioProc = MMIO_FindProcNode(fcc);
+    }
+
+    /* Handle any unhandled/error case from above. Assume DOS file */
+    if (!lpmmioinfo || (lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL && ioProc == NULL))
+       ioProc = MMIO_FindProcNode(FOURCC_DOS);
+    /* if just the four character code is present, look up IO proc */
+    else if (lpmmioinfo->pIOProc == NULL)
+        ioProc = MMIO_FindProcNode(lpmmioinfo->fccIOProc);
+    else /* use relevant ioProc */
+    {
+        ioProc = &tmp;
+        tmp.fourCC = lpmmioinfo->fccIOProc;
+        tmp.pIOProc = lpmmioinfo->pIOProc;
+        tmp.type = MMIO_PROC_32A;
+        tmp.count = 1;
+    }
+
+    /* FIXME: should we actually pass lpmmioinfo down the drain ???
+     * or make a copy of it because it's const ???
+     */
+    return send_message(ioProc, (MMIOINFO*)lpmmioinfo, MMIOM_RENAME,
+                        (LPARAM)szFileName, (LPARAM)szNewFileName, MMIO_PROC_32A);
+}
+
+/**************************************************************************
+ *                             mmioRenameW                     [WINMM.@]
+ */
+MMRESULT WINAPI mmioRenameW(LPCWSTR szFileName, LPCWSTR szNewFileName,
+                            const MMIOINFO* lpmmioinfo, DWORD dwFlags)
+{
+    LPSTR      szFn = NULL;
+    LPSTR      sznFn = NULL;
+    UINT       ret = MMSYSERR_NOMEM;
+    INT         len;
+
+    if (szFileName)
+    {
+        len = WideCharToMultiByte( CP_ACP, 0, szFileName, -1, NULL, 0, NULL, NULL );
+        szFn = HeapAlloc( GetProcessHeap(), 0, len );
+        if (!szFn) goto done;
+        WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szFn, len, NULL, NULL );
+    }
+    if (szNewFileName)
+    {
+        len = WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, NULL, 0, NULL, NULL );
+        sznFn = HeapAlloc( GetProcessHeap(), 0, len );
+        if (!sznFn) goto done;
+        WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, sznFn, len, NULL, NULL );
+    }
+
+    ret = mmioRenameA(szFn, sznFn, lpmmioinfo, dwFlags);
+
+done:
+    HeapFree(GetProcessHeap(),0,szFn);
+    HeapFree(GetProcessHeap(),0,sznFn);
+    return ret;
+}