| /* | |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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); | |
| static WINE_MMIO *MMIOList; | |
| /************************************************************************** | |
| * 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] = OpenFile(szFileName, &ofs, lpmmioinfo->dwFlags & 0xFFFF); | |
| } | |
| if (lpmmioinfo->adwInfo[0] == HFILE_ERROR) | |
| ret = MMIOERR_FILENOTFOUND; | |
| } | |
| 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 position, -1 on error | |
| */ | |
| if (lParam2 == SEEK_END) | |
| ret = _llseek((HFILE)lpmmioinfo->adwInfo[0], -(LONG)lParam1, (LONG)lParam2); | |
| else | |
| 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 position, -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, FALSE, 0}, | |
| {NULL, FOURCC_MEM, (LPMMIOPROC)mmioMemIOProc, FALSE, 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] | |
| */ | |
| static LPMMIOPROC MMIO_InstallIOProc(FOURCC fccIOProc, LPMMIOPROC pIOProc, | |
| DWORD dwFlags, BOOL is_unicode) | |
| { | |
| LPMMIOPROC lpProc = NULL; | |
| struct IOProcList* pListNode; | |
| struct IOProcList** ppListNode; | |
| TRACE("(%08x, %p, %08X, %s)\n", fccIOProc, pIOProc, dwFlags, is_unicode ? "unicode" : "ansi"); | |
| 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->is_unicode = is_unicode; | |
| 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) / sizeof(defaultProcs[0])) { | |
| 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, BOOL is_unicode) | |
| { | |
| LRESULT result = MMSYSERR_ERROR; | |
| LPARAM lp1 = lParam1, lp2 = lParam2; | |
| if (!ioProc) { | |
| ERR("ioProc NULL\n"); | |
| return MMSYSERR_INVALPARAM; | |
| } | |
| if (ioProc->is_unicode != is_unicode) { | |
| /* 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->is_unicode != is_unicode) { | |
| /* unmap (lParam1, lParam2) into (lp1, lp2) 32 A<=>W */ | |
| } | |
| #endif | |
| 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 */ | |
| LPCSTR extEnd; | |
| LPCSTR extStart; | |
| CHAR ext[5]; | |
| TRACE("(%s)\n", debugstr_a(szFileName)); | |
| if (!szFileName) | |
| return ret; | |
| /* Find the last '+' */ | |
| extEnd = strrchr(szFileName,'+'); | |
| if (!extEnd) { | |
| /* No + so just an extension */ | |
| return ret; | |
| } else { | |
| /* Find the first '.' before '+' */ | |
| extStart = extEnd - 1; | |
| while (extStart >= szFileName && *extStart != '.') { | |
| extStart--; | |
| } | |
| if (extStart < szFileName) { | |
| ERR("No extension in szFileName: %s\n", debugstr_a(szFileName)); | |
| return ret; | |
| } | |
| } | |
| if (extEnd - extStart - 1 > 4) | |
| WARN("Extension length > 4\n"); | |
| lstrcpynA(ext, extStart + 1, min(extEnd-extStart,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 | |
| */ | |
| static LPWINE_MMIO MMIO_Get(HMMIO h) | |
| { | |
| LPWINE_MMIO wm = NULL; | |
| EnterCriticalSection(&WINMM_cs); | |
| for (wm = MMIOList; wm; wm = wm->lpNext) { | |
| if (wm->info.hmmio == h) | |
| break; | |
| } | |
| LeaveCriticalSection(&WINMM_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_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 = MMIOList; | |
| MMIOList = wm; | |
| LeaveCriticalSection(&WINMM_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_cs); | |
| /* search for the matching one... */ | |
| m = &MMIOList; | |
| while (*m && *m != wm) m = &(*m)->lpNext; | |
| /* ...and destroy */ | |
| if (*m) { | |
| *m = (*m)->lpNext; | |
| HeapFree(GetProcessHeap(), 0, wm); | |
| wm = NULL; | |
| } | |
| LeaveCriticalSection(&WINMM_cs); | |
| return !wm; | |
| } | |
| /**************************************************************** | |
| * 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, FALSE); | |
| send_message(wm->ioProc, &wm->info, MMIOM_WRITE, | |
| (LPARAM)wm->info.pchBuffer, | |
| wm->info.pchNext - wm->info.pchBuffer, FALSE); | |
| } | |
| 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=%x do=%x\n", | |
| wm->info.lBufOffset, wm->info.lDiskOffset); | |
| 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, FALSE); | |
| 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 %d %u)\n", wm, pchBuffer, cchBuffer, uFlags); | |
| if (cchBuffer > 0xFFFF) | |
| WARN("Untested handling of huge mmio buffers (%d >= 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 = wm->info.lDiskOffset; | |
| wm->bBufferLoaded = (wm->info.fccIOProc == FOURCC_MEM); | |
| return MMSYSERR_NOERROR; | |
| } | |
| /************************************************************************** | |
| * MMIO_Open [internal] | |
| */ | |
| static HMMIO MMIO_Open(LPSTR szFileName, MMIOINFO* refmminfo, DWORD dwOpenFlags, BOOL is_unicode) | |
| { | |
| LPWINE_MMIO wm; | |
| MMIOINFO mmioinfo; | |
| DWORD pos; | |
| TRACE("(%s, %p, %08X, %s);\n", debugstr_a(szFileName), refmminfo, dwOpenFlags, is_unicode ? "unicode" : "ansi"); | |
| if (!refmminfo) { | |
| refmminfo = &mmioinfo; | |
| memset(&mmioinfo, 0, sizeof(mmioinfo)); | |
| is_unicode = FALSE; | |
| } | |
| if (dwOpenFlags & (MMIO_PARSE|MMIO_EXIST)) { | |
| char buffer[MAX_PATH]; | |
| if (!szFileName) | |
| return (HMMIO)FALSE; | |
| 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; | |
| wm->ioProc = &defaultProcs[0]; | |
| } | |
| else if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) { | |
| /* If not found, assume DOS file */ | |
| wm->ioProc = &defaultProcs[0]; | |
| } | |
| 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, is_unicode); | |
| if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2; | |
| assert(wm->ioProc->pIOProc == refmminfo->pIOProc); | |
| wm->bTmpIOProc = TRUE; | |
| } | |
| wm->ioProc->count++; | |
| wm->info.dwFlags = dwOpenFlags; | |
| if (dwOpenFlags & MMIO_ALLOCBUF) { | |
| refmminfo->wErrorRet = MMIO_SetBuffer(wm, refmminfo->pchBuffer, | |
| refmminfo->cchBuffer ? refmminfo->cchBuffer : MMIO_DEFAULTBUFFER, 0); | |
| if (refmminfo->wErrorRet != MMSYSERR_NOERROR) | |
| goto error1; | |
| } else { | |
| refmminfo->wErrorRet = MMIO_SetBuffer(wm, refmminfo->pchBuffer, refmminfo->cchBuffer, 0); | |
| if (refmminfo->wErrorRet != MMSYSERR_NOERROR) | |
| goto error1; | |
| } | |
| /* see mmioDosIOProc for that one */ | |
| memcpy( wm->info.adwInfo, refmminfo->adwInfo, sizeof(wm->info.adwInfo) ); | |
| /* call IO proc to actually open file */ | |
| refmminfo->wErrorRet = send_message(wm->ioProc, &wm->info, MMIOM_OPEN, | |
| (LPARAM)szFileName, 0, FALSE); | |
| /* update offsets and grab file size, when possible */ | |
| if (wm->info.fccIOProc == FOURCC_DOS && (send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 0, SEEK_CUR, FALSE)) != -1) { | |
| pos = wm->info.lBufOffset = wm->info.lDiskOffset; | |
| send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 0, SEEK_END, FALSE); | |
| wm->dwFileSize = wm->info.lDiskOffset; | |
| send_message(wm->ioProc, &wm->info, MMIOM_SEEK, pos, SEEK_SET, FALSE); | |
| } | |
| else wm->dwFileSize = 0; | |
| if (refmminfo->wErrorRet == 0) | |
| return wm->info.hmmio; | |
| error1: | |
| if (wm->info.dwFlags & MMIO_ALLOCBUF) | |
| HeapFree(GetProcessHeap(), 0, wm->info.pchBuffer); | |
| 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, TRUE); | |
| HeapFree(GetProcessHeap(), 0, szFn); | |
| return ret; | |
| } | |
| /************************************************************************** | |
| * mmioOpenA [WINMM.@] | |
| */ | |
| HMMIO WINAPI mmioOpenA(LPSTR szFileName, MMIOINFO* lpmmioinfo, | |
| DWORD dwOpenFlags) | |
| { | |
| return MMIO_Open(szFileName, lpmmioinfo, dwOpenFlags, FALSE); | |
| } | |
| /************************************************************************** | |
| * 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, FALSE); | |
| MMIO_SetBuffer(wm, NULL, 0, 0); | |
| wm->ioProc->count--; | |
| if (wm->bTmpIOProc) | |
| MMIO_InstallIOProc(wm->info.fccIOProc, wm->ioProc->pIOProc, | |
| MMIO_REMOVEPROC, wm->ioProc->is_unicode); | |
| MMIO_Destroy(wm); | |
| return result; | |
| } | |
| /************************************************************************** | |
| * mmioRead [WINMM.@] | |
| */ | |
| LONG WINAPI mmioRead(HMMIO hmmio, HPSTR pch, LONG cch) | |
| { | |
| LPWINE_MMIO wm; | |
| LONG count; | |
| TRACE("(%p, %p, %d);\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, FALSE); | |
| /* first try from current buffer */ | |
| if (cch && wm->info.fccIOProc != FOURCC_MEM && wm->info.pchNext == wm->info.pchEndRead) | |
| MMIO_GrabNextBuffer(wm, TRUE); | |
| 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; | |
| } | |
| wm->bBufferLoaded = FALSE; | |
| mmioSeek(hmmio, 0, SEEK_CUR); | |
| } | |
| TRACE("count=%d\n", count); | |
| return count; | |
| } | |
| /************************************************************************** | |
| * mmioWrite [WINMM.@] | |
| */ | |
| LONG WINAPI mmioWrite(HMMIO hmmio, HPCSTR pch, LONG cch) | |
| { | |
| LPWINE_MMIO wm; | |
| LONG count; | |
| TRACE("(%p, %p, %d);\n", hmmio, pch, cch); | |
| if ((wm = MMIO_Get(hmmio)) == NULL) | |
| return -1; | |
| if (wm->info.cchBuffer) { | |
| LONG bytesW = 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, FALSE); | |
| wm->info.lBufOffset = wm->info.lDiskOffset; | |
| } | |
| TRACE("bytes written=%d\n", count); | |
| return count; | |
| } | |
| /************************************************************************** | |
| * mmioSeek [WINMM.@] | |
| */ | |
| LONG WINAPI mmioSeek(HMMIO hmmio, LONG lOffset, INT iOrigin) | |
| { | |
| LPWINE_MMIO wm; | |
| LONG offset; | |
| TRACE("(%p, %08X, %d);\n", hmmio, lOffset, iOrigin); | |
| if ((wm = MMIO_Get(hmmio)) == NULL) | |
| return MMSYSERR_INVALHANDLE; | |
| /* not buffered, direct seek on file */ | |
| if (!wm->info.pchBuffer && wm->info.fccIOProc != FOURCC_MEM) { | |
| LRESULT ret = send_message(wm->ioProc, &wm->info, MMIOM_SEEK, lOffset, iOrigin, FALSE); | |
| if (ret != -1) | |
| wm->info.lBufOffset = wm->info.lDiskOffset; | |
| return ret; | |
| } | |
| 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: | |
| switch (wm->info.fccIOProc) { | |
| case FOURCC_MEM: | |
| offset = wm->info.cchBuffer - lOffset; | |
| break; | |
| case FOURCC_DOS: | |
| offset = wm->dwFileSize - lOffset; | |
| break; | |
| default: | |
| offset = send_message(wm->ioProc, &wm->info, MMIOM_SEEK, lOffset, SEEK_END, FALSE); | |
| break; | |
| } | |
| break; | |
| default: | |
| return -1; | |
| } | |
| /* stay in same buffer ? */ | |
| /* some memory mapped buffers are defined with -1 as a size */ | |
| if ((wm->info.cchBuffer > 0) && | |
| ((offset < wm->info.lBufOffset) || | |
| (offset > wm->info.lBufOffset + wm->info.cchBuffer) || | |
| (offset > wm->dwFileSize && wm->info.fccIOProc == FOURCC_DOS) || | |
| !wm->bBufferLoaded)) { | |
| /* 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, SEEK_SET, FALSE) == -1) | |
| return -1; | |
| wm->info.lBufOffset = offset; | |
| wm->bBufferLoaded = FALSE; | |
| wm->info.pchNext = wm->info.pchEndRead = wm->info.pchBuffer; | |
| } | |
| else | |
| wm->info.pchNext = wm->info.pchBuffer + (offset - wm->info.lBufOffset); | |
| TRACE("=> %d\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; | |
| *lpmmioinfo = wm->info; | |
| 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 = *lpmmioinfo; | |
| 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=%d, 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 (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR) | |
| return MMIOERR_CANNOTWRITE; | |
| if (uFlags == MMIO_WRITE && (lpmmioinfo->dwFlags & MMIO_DIRTY)) | |
| { | |
| send_message(wm->ioProc, &wm->info, MMIOM_SEEK, lpmmioinfo->lBufOffset, SEEK_SET, FALSE); | |
| send_message(wm->ioProc, &wm->info, MMIOM_WRITE, (LPARAM)lpmmioinfo->pchBuffer, | |
| lpmmioinfo->pchNext - lpmmioinfo->pchBuffer, FALSE); | |
| lpmmioinfo->dwFlags &= ~MMIO_DIRTY; | |
| } | |
| if (lpmmioinfo && lpmmioinfo->fccIOProc == FOURCC_DOS) { | |
| 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 %s\n",debugstr_an(cc,4)); | |
| 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, FALSE); | |
| } | |
| /************************************************************************** | |
| * mmioInstallIOProcW [WINMM.@] | |
| */ | |
| LPMMIOPROC WINAPI mmioInstallIOProcW(FOURCC fccIOProc, | |
| LPMMIOPROC pIOProc, DWORD dwFlags) | |
| { | |
| return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, TRUE); | |
| } | |
| /****************************************************************** | |
| * MMIO_SendMessage | |
| * | |
| * | |
| */ | |
| static LRESULT MMIO_SendMessage(HMMIO hmmio, UINT uMessage, LPARAM lParam1, | |
| LPARAM lParam2, BOOL is_unicode) | |
| { | |
| LPWINE_MMIO wm; | |
| TRACE("(%p, %u, %ld, %ld, %s)\n", hmmio, uMessage, lParam1, lParam2, is_unicode ? "unicode" : "ansi"); | |
| 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, is_unicode); | |
| } | |
| /************************************************************************** | |
| * mmioSendMessage [WINMM.@] | |
| */ | |
| LRESULT WINAPI mmioSendMessage(HMMIO hmmio, UINT uMessage, | |
| LPARAM lParam1, LPARAM lParam2) | |
| { | |
| return MMIO_SendMessage(hmmio, uMessage, lParam1, lParam2, FALSE); | |
| } | |
| /************************************************************************** | |
| * 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=%d\n", dwOldPos); | |
| if (lpckParent != NULL) { | |
| TRACE("seek inside parent at %d !\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. | |
| */ | |
| srchCkId = 0; | |
| 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; | |
| } | |
| TRACE("searching for %s.%s\n", | |
| debugstr_an((LPCSTR)&srchCkId, 4), srchType ? debugstr_an((LPCSTR)&srchType, 4) : "<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); | |
| TRACE("ckid=%s fcc=%s cksize=%08X !\n", | |
| debugstr_an((LPCSTR)&lpck->ckid, 4), | |
| srchType ? debugstr_an((LPCSTR)&lpck->fccType, 4) : "<na>", | |
| lpck->cksize); | |
| if ( (!srchCkId || (srchCkId == lpck->ckid)) && | |
| (!srchType || (srchType == lpck->fccType)) ) | |
| break; | |
| dwOldPos = lpck->dwDataOffset + ((lpck->cksize + 1) & ~1); | |
| mmioSeek(hmmio, dwOldPos, SEEK_SET); | |
| } | |
| 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); | |
| lpck->fccType = 0; | |
| } | |
| TRACE("lpck: ckid=%s, cksize=%d, dwDataOffset=%d fccType=%08X (%s)!\n", | |
| debugstr_an((LPSTR)&lpck->ckid, 4), lpck->cksize, lpck->dwDataOffset, | |
| lpck->fccType, srchType ? debugstr_an((LPSTR)&lpck->fccType, 4):""); | |
| 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=%d lpck->dwDataOffset = %d\n", dwOldPos, lpck->dwDataOffset); | |
| dwNewSize = dwOldPos - lpck->dwDataOffset; | |
| if (dwNewSize != lpck->cksize) { | |
| TRACE("Nope: lpck->cksize=%d dwNewSize=%d\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=%d\n", dwOldPos); | |
| if (uFlags == MMIO_CREATELIST) | |
| lpck->ckid = FOURCC_LIST; | |
| else if (uFlags == MMIO_CREATERIFF) | |
| lpck->ckid = FOURCC_RIFF; | |
| TRACE("ckid=%s\n", debugstr_an((LPSTR)&lpck->ckid, 4)); | |
| 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 = %d req = %d, 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, %08X);\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 = &defaultProcs[0]; | |
| /* 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.is_unicode = FALSE; | |
| 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, FALSE); | |
| } | |
| /************************************************************************** | |
| * 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; | |
| } |