-/*\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;
+}