Convert MsiEnumComponentQualifiers to use msi_strcpy_to_awstring.
[wine] / dlls / winmm / mmio.c
1 /*
2  * MMIO functions
3  *
4  * Copyright 1998 Andrew Taylor
5  * Copyright 1998 Ove Kåven
6  * Copyright 2000,2002 Eric Pouech
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /* Still to be done:
24  *      + correct handling of global/local IOProcs (and temporary IOProcs)
25  *      + mode of mmio objects is not used (read vs write vs readwrite)
26  *      + thread safeness
27  *      + 32 A <=> W message mapping
28  */
29
30
31 #include <ctype.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <assert.h>
37
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winnls.h"
41 #include "mmsystem.h"
42 #include "winemm.h"
43
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(mmio);
47
48 LRESULT         (*pFnMmioCallback16)(DWORD,LPMMIOINFO,UINT,LPARAM,LPARAM) /* = NULL */;
49
50 /**************************************************************************
51  *                      mmioDosIOProc                           [internal]
52  */
53 static LRESULT CALLBACK mmioDosIOProc(LPMMIOINFO lpmmioinfo, UINT uMessage,
54                                       LPARAM lParam1, LPARAM lParam2)
55 {
56     LRESULT     ret = MMSYSERR_NOERROR;
57
58     TRACE("(%p, %X, 0x%lx, 0x%lx);\n", lpmmioinfo, uMessage, lParam1, lParam2);
59
60     switch (uMessage) {
61     case MMIOM_OPEN:
62         {
63             /* Parameters:
64              * lParam1 = szFileName parameter from mmioOpen
65              * lParam2 = reserved
66              * Returns: zero on success, error code on error
67              * NOTE: lDiskOffset automatically set to zero
68              */
69             LPCSTR      szFileName = (LPCSTR)lParam1;
70
71             if (lpmmioinfo->dwFlags & MMIO_GETTEMP) {
72                 FIXME("MMIO_GETTEMP not implemented\n");
73                 return MMIOERR_CANNOTOPEN;
74             }
75
76             /* if filename NULL, assume open file handle in adwInfo[0] */
77             if (szFileName) {
78                 OFSTRUCT    ofs;
79                 lpmmioinfo->adwInfo[0] = (DWORD)OpenFile(szFileName, &ofs, lpmmioinfo->dwFlags & 0xFFFF);
80             }
81             if (lpmmioinfo->adwInfo[0] == (DWORD)HFILE_ERROR)
82                 ret = MMIOERR_CANNOTOPEN;
83         }
84         break;
85
86     case MMIOM_CLOSE:
87         /* Parameters:
88          * lParam1 = wFlags parameter from mmioClose
89          * lParam2 = unused
90          * Returns: zero on success, error code on error
91          */
92         if (!(lParam1 & MMIO_FHOPEN))
93             _lclose((HFILE)lpmmioinfo->adwInfo[0]);
94         break;
95
96     case MMIOM_READ:
97         /* Parameters:
98          * lParam1 = huge pointer to read buffer
99          * lParam2 = number of bytes to read
100          * Returns: number of bytes read, 0 for EOF, -1 for error (error code
101          *         in wErrorRet)
102          */
103         ret = _lread((HFILE)lpmmioinfo->adwInfo[0], (HPSTR)lParam1, (LONG)lParam2);
104         if (ret != -1)
105             lpmmioinfo->lDiskOffset += ret;
106
107         break;
108
109     case MMIOM_WRITE:
110     case MMIOM_WRITEFLUSH:
111         /* no internal buffering, so WRITEFLUSH handled same as WRITE */
112
113         /* Parameters:
114          * lParam1 = huge pointer to write buffer
115          * lParam2 = number of bytes to write
116          * Returns: number of bytes written, -1 for error (error code in
117          *              wErrorRet)
118          */
119         ret = _hwrite((HFILE)lpmmioinfo->adwInfo[0], (HPSTR)lParam1, (LONG)lParam2);
120         if (ret != -1)
121             lpmmioinfo->lDiskOffset += ret;
122         break;
123
124     case MMIOM_SEEK:
125         /* Parameters:
126          * lParam1 = new position
127          * lParam2 = from whence to seek (SEEK_SET, SEEK_CUR, SEEK_END)
128          * Returns: new file postion, -1 on error
129          */
130         ret = _llseek((HFILE)lpmmioinfo->adwInfo[0], (LONG)lParam1, (LONG)lParam2);
131         if (ret != -1)
132             lpmmioinfo->lDiskOffset = ret;
133         return ret;
134
135     case MMIOM_RENAME:
136         /* Parameters:
137          * lParam1 = old name
138          * lParam2 = new name
139          * Returns: zero on success, non-zero on failure
140          */
141          if (!MoveFileA((const char*)lParam1, (const char*)lParam2))
142              ret = MMIOERR_FILENOTFOUND;
143          break;
144
145     default:
146         FIXME("unexpected message %u\n", uMessage);
147         return 0;
148     }
149
150     return ret;
151 }
152
153 /**************************************************************************
154  *                      mmioMemIOProc                           [internal]
155  */
156 static LRESULT CALLBACK mmioMemIOProc(LPMMIOINFO lpmmioinfo, UINT uMessage,
157                                       LPARAM lParam1, LPARAM lParam2)
158 {
159     TRACE("(%p,0x%04x,0x%08lx,0x%08lx)\n", lpmmioinfo, uMessage, lParam1, lParam2);
160
161     switch (uMessage) {
162
163     case MMIOM_OPEN:
164         /* Parameters:
165          * lParam1 = filename (must be NULL)
166          * lParam2 = reserved
167          * Returns: zero on success, error code on error
168          * NOTE: lDiskOffset automatically set to zero
169          */
170         /* FIXME: io proc shouldn't change it */
171         if (!(lpmmioinfo->dwFlags & MMIO_CREATE))
172             lpmmioinfo->pchEndRead = lpmmioinfo->pchEndWrite;
173         lpmmioinfo->adwInfo[0] = HFILE_ERROR;
174         return 0;
175
176     case MMIOM_CLOSE:
177         /* Parameters:
178          * lParam1 = wFlags parameter from mmioClose
179          * lParam2 = unused
180          * Returns: zero on success, error code on error
181          */
182         return 0;
183
184     case MMIOM_READ:
185         /* Parameters:
186          * lParam1 = huge pointer to read buffer
187          * lParam2 = number of bytes to read
188          * Returns: number of bytes read, 0 for EOF, -1 for error (error code
189          *         in wErrorRet)
190          * NOTE: lDiskOffset should be updated
191          */
192         FIXME("MMIOM_READ on memory files should not occur, buffer may be lost!\n");
193         return 0;
194
195     case MMIOM_WRITE:
196     case MMIOM_WRITEFLUSH:
197         /* no internal buffering, so WRITEFLUSH handled same as WRITE */
198
199         /* Parameters:
200          * lParam1 = huge pointer to write buffer
201          * lParam2 = number of bytes to write
202          * Returns: number of bytes written, -1 for error (error code in
203          *              wErrorRet)
204          * NOTE: lDiskOffset should be updated
205          */
206         FIXME("MMIOM_WRITE on memory files should not occur, buffer may be lost!\n");
207         return 0;
208
209     case MMIOM_SEEK:
210         /* Parameters:
211          * lParam1 = new position
212          * lParam2 = from whence to seek (SEEK_SET, SEEK_CUR, SEEK_END)
213          * Returns: new file postion, -1 on error
214          * NOTE: lDiskOffset should be updated
215          */
216         FIXME("MMIOM_SEEK on memory files should not occur, buffer may be lost!\n");
217         return -1;
218
219     default:
220         FIXME("unexpected message %u\n", uMessage);
221         return 0;
222     }
223 }
224
225 /* This array will be the entire list for most apps 
226  * Note that temporary ioProcs will be stored with a 0 fourCC code
227  */
228
229 static struct IOProcList defaultProcs[] = {
230     {&defaultProcs[1], FOURCC_DOS, (LPMMIOPROC)mmioDosIOProc, MMIO_PROC_32A, 0},
231     {NULL,             FOURCC_MEM, (LPMMIOPROC)mmioMemIOProc, MMIO_PROC_32A, 0},
232 };
233
234 static struct IOProcList*       pIOProcListAnchor = &defaultProcs[0];
235
236 /****************************************************************
237  *              MMIO_FindProcNode                       [INTERNAL]
238  *
239  * Finds the ProcList node associated with a given FOURCC code.
240  */
241 static struct IOProcList*       MMIO_FindProcNode(FOURCC fccIOProc)
242 {
243     struct IOProcList*  pListNode;
244
245     for (pListNode = pIOProcListAnchor; pListNode; pListNode = pListNode->pNext) {
246         if (pListNode->fourCC == fccIOProc) {
247             return pListNode;
248         }
249     }
250     return NULL;
251 }
252
253 /****************************************************************
254  *              MMIO_InstallIOProc                      [INTERNAL]
255  */
256 LPMMIOPROC MMIO_InstallIOProc(FOURCC fccIOProc, LPMMIOPROC pIOProc,
257                               DWORD dwFlags, enum mmioProcType type)
258 {
259     LPMMIOPROC          lpProc = NULL;
260     struct IOProcList*  pListNode;
261     struct IOProcList** ppListNode;
262
263     TRACE("(%08lx, %p, %08lX, %i)\n", fccIOProc, pIOProc, dwFlags, type);
264
265     if (dwFlags & MMIO_GLOBALPROC)
266         FIXME("Global procedures not implemented\n");
267
268     /* just handle the known procedures for now */
269     switch (dwFlags & (MMIO_INSTALLPROC|MMIO_REMOVEPROC|MMIO_FINDPROC)) {
270     case MMIO_INSTALLPROC:
271         /* Create new entry for the IOProc list */
272         pListNode = HeapAlloc(GetProcessHeap(), 0, sizeof(*pListNode));
273         if (pListNode) {
274             /* Fill in this node */
275             pListNode->fourCC = fccIOProc;
276             pListNode->pIOProc = pIOProc;
277             pListNode->type = type;
278             pListNode->count = 0;
279
280             /* Stick it on the end of the list */
281             pListNode->pNext = pIOProcListAnchor;
282             pIOProcListAnchor = pListNode;
283
284             /* Return this IOProc - that's how the caller knows we succeeded */
285             lpProc = pIOProc;
286         }
287         break;
288
289     case MMIO_REMOVEPROC:
290         /*
291          * Search for the node that we're trying to remove
292          * We search for a matching fourCC code if it's non null, or the proc
293          * address otherwise
294          * note that this method won't find the first item on the list, but
295          * since the first two items on this list are ones we won't
296          * let the user delete anyway, that's okay
297          */
298         ppListNode = &pIOProcListAnchor;
299         while ((*ppListNode) && 
300                ((fccIOProc != 0) ? 
301                 (*ppListNode)->fourCC != fccIOProc : 
302                 (*ppListNode)->pIOProc != pIOProc))
303             ppListNode = &((*ppListNode)->pNext);
304
305         if (*ppListNode) { /* found it */
306             /* FIXME: what should be done if an open mmio object uses this proc ?
307              * shall we return an error, nuke the mmio object ?
308              */
309             if ((*ppListNode)->count) {
310                 ERR("Cannot remove a mmIOProc while in use\n");
311                 break;
312             }
313             /* remove it, but only if it isn't builtin */
314             if ((*ppListNode) >= defaultProcs &&
315                 (*ppListNode) < defaultProcs + sizeof(defaultProcs)) {
316                 WARN("Tried to remove built-in mmio proc. Skipping\n");
317             } else {
318                 /* Okay, nuke it */
319                 struct IOProcList*  ptmpNode = *ppListNode;
320                 lpProc = (*ppListNode)->pIOProc;
321                 *ppListNode = (*ppListNode)->pNext;
322                 HeapFree(GetProcessHeap(), 0, ptmpNode);
323             }
324         }
325         break;
326
327     case MMIO_FINDPROC:
328         if ((pListNode = MMIO_FindProcNode(fccIOProc))) {
329             lpProc = pListNode->pIOProc;
330         }
331         break;
332     }
333
334     return lpProc;
335 }
336
337 /****************************************************************
338  *              send_message                            [INTERNAL]
339  */
340 static LRESULT  send_message(struct IOProcList* ioProc, LPMMIOINFO mmioinfo,
341                              DWORD wMsg, LPARAM lParam1,
342                              LPARAM lParam2, enum mmioProcType type)
343 {
344     LRESULT             result = MMSYSERR_ERROR;
345     LPARAM              lp1 = lParam1, lp2 = lParam2;
346
347     if (!ioProc) {
348         ERR("brrr\n");
349         result = MMSYSERR_INVALPARAM;
350     }
351
352     switch (ioProc->type) {
353     case MMIO_PROC_16:
354         if (pFnMmioCallback16)
355             result = pFnMmioCallback16((DWORD)ioProc->pIOProc,
356                                        mmioinfo, wMsg, lp1, lp2);
357         break;
358     case MMIO_PROC_32A:
359     case MMIO_PROC_32W:
360         if (ioProc->type != type) {
361             /* map (lParam1, lParam2) into (lp1, lp2) 32 A<=>W */
362             FIXME("NIY 32 A<=>W mapping\n");
363         }
364         result = (ioProc->pIOProc)((LPSTR)mmioinfo, wMsg, lp1, lp2);
365
366 #if 0
367         if (ioProc->type != type) {
368             /* unmap (lParam1, lParam2) into (lp1, lp2) 32 A<=>W */
369         }
370 #endif
371         break;
372     default:
373         FIXME("Internal error\n");
374     }
375
376     return result;
377 }
378
379 /**************************************************************************
380  *                              MMIO_ParseExtA                  [internal]
381  *
382  * Parses a filename for the extension.
383  *
384  * RETURNS
385  *  The FOURCC code for the extension if found, else 0.
386  */
387 static FOURCC MMIO_ParseExtA(LPCSTR szFileName)
388 {
389     /* Filenames are of the form file.ext{+ABC}
390        For now, we take the last '+' if present */
391
392     FOURCC ret = 0;
393
394     /* Note that ext{Start,End} point to the . and + respectively */
395     LPSTR extEnd;
396     LPSTR extStart;
397
398     TRACE("(%s)\n", debugstr_a(szFileName));
399
400     if (!szFileName)
401         return ret;
402
403     /* Find the last '.' */
404     extStart = strrchr(szFileName,'.');
405
406     if (!extStart) {
407          ERR("No . in szFileName: %s\n", debugstr_a(szFileName));
408     } else {
409         CHAR ext[5];
410
411         /* Find the '+' afterwards */
412         extEnd = strchr(extStart,'+');
413         if (extEnd) {
414
415             if (extEnd - extStart - 1 > 4)
416                 WARN("Extension length > 4\n");
417             lstrcpynA(ext, extStart + 1, min(extEnd-extStart,5));
418
419         } else {
420             /* No + so just an extension */
421             if (strlen(extStart) > 4) {
422                 WARN("Extension length > 4\n");
423             }
424             lstrcpynA(ext, extStart + 1, 5);
425         }
426         TRACE("Got extension: %s\n", debugstr_a(ext));
427
428         /* FOURCC codes identifying file-extensions must be uppercase */
429         ret = mmioStringToFOURCCA(ext, MMIO_TOUPPER);
430     }
431     return ret;
432 }
433
434 /**************************************************************************
435  *                              MMIO_Get                        [internal]
436  *
437  * Retrieves the mmio object from current process
438  */
439 LPWINE_MMIO     MMIO_Get(HMMIO h)
440 {
441     LPWINE_MMIO         wm = NULL;
442
443     EnterCriticalSection(&WINMM_IData.cs);
444     for (wm = WINMM_IData.lpMMIO; wm; wm = wm->lpNext) {
445         if (wm->info.hmmio == h)
446             break;
447     }
448     LeaveCriticalSection(&WINMM_IData.cs);
449     return wm;
450 }
451
452 /**************************************************************************
453  *                              MMIO_Create                     [internal]
454  *
455  * Creates an internal representation for a mmio instance
456  */
457 static  LPWINE_MMIO             MMIO_Create(void)
458 {
459     static      WORD    MMIO_counter = 0;
460     LPWINE_MMIO         wm;
461
462     wm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MMIO));
463     if (wm) {
464         EnterCriticalSection(&WINMM_IData.cs);
465         /* lookup next unallocated WORD handle, with a non NULL value */
466         while (++MMIO_counter == 0 || MMIO_Get((HMMIO)(ULONG_PTR)MMIO_counter));
467         wm->info.hmmio = (HMMIO)(ULONG_PTR)MMIO_counter;
468         wm->lpNext = WINMM_IData.lpMMIO;
469         WINMM_IData.lpMMIO = wm;
470         LeaveCriticalSection(&WINMM_IData.cs);
471     }
472     return wm;
473 }
474
475 /**************************************************************************
476  *                              MMIO_Destroy                    [internal]
477  *-
478  * Destroys an internal representation for a mmio instance
479  */
480 static  BOOL            MMIO_Destroy(LPWINE_MMIO wm)
481 {
482     LPWINE_MMIO*        m;
483
484     EnterCriticalSection(&WINMM_IData.cs);
485     /* search for the matching one... */
486     m = &(WINMM_IData.lpMMIO);
487     while (*m && *m != wm) m = &(*m)->lpNext;
488     /* ...and destroy */
489     if (*m) {
490         *m = (*m)->lpNext;
491         HeapFree(GetProcessHeap(), 0, wm);
492         wm = NULL;
493     }
494     LeaveCriticalSection(&WINMM_IData.cs);
495     return wm ? FALSE : TRUE;
496 }
497
498 /****************************************************************
499  *                      MMIO_Flush                      [INTERNAL]
500  */
501 static  MMRESULT MMIO_Flush(WINE_MMIO* wm, UINT uFlags)
502 {
503     if (wm->info.cchBuffer && (wm->info.fccIOProc != FOURCC_MEM)) {
504         /* not quite sure what to do here, but I'll guess */
505         if (wm->info.dwFlags & MMIO_DIRTY) {
506             /* FIXME: error handling */
507             send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 
508                          wm->info.lBufOffset, SEEK_SET, MMIO_PROC_32A);
509             send_message(wm->ioProc, &wm->info, MMIOM_WRITE, 
510                          (LPARAM)wm->info.pchBuffer,
511                          wm->info.pchNext - wm->info.pchBuffer, MMIO_PROC_32A);
512         }
513         if (uFlags & MMIO_EMPTYBUF)
514             wm->info.pchNext = wm->info.pchEndRead = wm->info.pchBuffer;
515     }
516     wm->info.dwFlags &= ~MMIO_DIRTY;
517
518     return MMSYSERR_NOERROR;
519 }
520
521 /***************************************************************************
522  *                      MMIO_GrabNextBuffer                     [INTERNAL]
523  */
524 static LONG     MMIO_GrabNextBuffer(LPWINE_MMIO wm, int for_read)
525 {
526     LONG        size = wm->info.cchBuffer;
527
528     TRACE("bo=%lx do=%lx of=%lx\n",
529           wm->info.lBufOffset, wm->info.lDiskOffset,
530           send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 0, SEEK_CUR, MMIO_PROC_32A));
531
532     wm->info.lBufOffset = wm->info.lDiskOffset;
533     wm->info.pchNext = wm->info.pchBuffer;
534     wm->info.pchEndRead = wm->info.pchBuffer;
535     wm->info.pchEndWrite = wm->info.pchBuffer + wm->info.cchBuffer;
536
537     wm->bBufferLoaded = TRUE;
538     if (for_read) {
539         size = send_message(wm->ioProc, &wm->info, MMIOM_READ, 
540                             (LPARAM)wm->info.pchBuffer, size, MMIO_PROC_32A);
541         if (size > 0)
542             wm->info.pchEndRead += size;
543         else
544             wm->bBufferLoaded = FALSE;
545     }
546
547     return size;
548 }
549
550 /***************************************************************************
551  *                      MMIO_SetBuffer                          [INTERNAL]
552  */
553 static MMRESULT MMIO_SetBuffer(WINE_MMIO* wm, void* pchBuffer, LONG cchBuffer,
554                                UINT uFlags)
555 {
556     TRACE("(%p %p %ld %u)\n", wm, pchBuffer, cchBuffer, uFlags);
557
558     if (uFlags)                 return MMSYSERR_INVALPARAM;
559     if (cchBuffer > 0xFFFF)
560         WARN("Untested handling of huge mmio buffers (%ld >= 64k)\n", cchBuffer);
561
562     if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)
563         return MMIOERR_CANNOTWRITE;
564
565     /* free previous buffer if allocated */
566     if (wm->info.dwFlags & MMIO_ALLOCBUF) {
567         HeapFree(GetProcessHeap(), 0, wm->info.pchBuffer);
568         wm->info.pchBuffer = NULL;
569         wm->info.dwFlags &= ~MMIO_ALLOCBUF;
570     }
571
572     if (pchBuffer) {
573         wm->info.pchBuffer = pchBuffer;
574     } else if (cchBuffer) {
575         if (!(wm->info.pchBuffer = HeapAlloc(GetProcessHeap(), 0, cchBuffer)))
576             return MMIOERR_OUTOFMEMORY;
577         wm->info.dwFlags |= MMIO_ALLOCBUF;
578     } else {
579         wm->info.pchBuffer = NULL;
580     }
581
582     wm->info.cchBuffer = cchBuffer;
583     wm->info.pchNext = wm->info.pchBuffer;
584     wm->info.pchEndRead = wm->info.pchBuffer;
585     wm->info.pchEndWrite = wm->info.pchBuffer + cchBuffer;
586     wm->info.lBufOffset = 0;
587     wm->bBufferLoaded = FALSE;
588
589     return MMSYSERR_NOERROR;
590 }
591
592 /**************************************************************************
593  *                      MMIO_Open                               [internal]
594  */
595 HMMIO MMIO_Open(LPSTR szFileName, MMIOINFO* refmminfo, DWORD dwOpenFlags, 
596                 enum mmioProcType type)
597 {
598     LPWINE_MMIO         wm;
599     MMIOINFO            mmioinfo;
600
601     TRACE("('%s', %p, %08lX, %d);\n", szFileName, refmminfo, dwOpenFlags, type);
602
603     if (!refmminfo) {
604         refmminfo = &mmioinfo;
605
606         mmioinfo.fccIOProc = 0;
607         mmioinfo.pIOProc = NULL;
608         mmioinfo.pchBuffer = NULL;
609         mmioinfo.cchBuffer = 0;
610         type = MMIO_PROC_32A;
611     }
612
613     if (dwOpenFlags & (MMIO_PARSE|MMIO_EXIST)) {
614         char    buffer[MAX_PATH];
615
616         if (GetFullPathNameA(szFileName, sizeof(buffer), buffer, NULL) >= sizeof(buffer))
617             return (HMMIO)FALSE;
618         if ((dwOpenFlags & MMIO_EXIST) && (GetFileAttributesA(buffer) == INVALID_FILE_ATTRIBUTES))
619             return (HMMIO)FALSE;
620         strcpy(szFileName, buffer);
621         return (HMMIO)TRUE;
622     }
623
624     if ((wm = MMIO_Create()) == NULL)
625         return 0;
626
627     /* If both params are NULL, then parse the file name if available */
628     if (refmminfo->fccIOProc == 0 && refmminfo->pIOProc == NULL) {
629         wm->info.fccIOProc = MMIO_ParseExtA(szFileName);
630         /* Handle any unhandled/error case. Assume DOS file */
631         if (wm->info.fccIOProc == 0)
632             wm->info.fccIOProc = FOURCC_DOS;
633         if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) {
634             /* If not found, retry with FOURCC_DOS */
635             wm->info.fccIOProc = FOURCC_DOS;
636             if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc)))
637                 goto error2;
638         }
639         wm->bTmpIOProc = FALSE;
640     }
641     /* if just the four character code is present, look up IO proc */
642     else if (refmminfo->pIOProc == NULL) {
643         wm->info.fccIOProc = refmminfo->fccIOProc;
644         if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;
645         wm->bTmpIOProc = FALSE;
646     }
647     /* if IO proc specified, use it and specified four character code */
648     else {
649         wm->info.fccIOProc = refmminfo->fccIOProc;
650         MMIO_InstallIOProc(wm->info.fccIOProc, refmminfo->pIOProc,
651                            MMIO_INSTALLPROC, type);
652         if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;
653         assert(wm->ioProc->pIOProc == refmminfo->pIOProc);
654         wm->bTmpIOProc = TRUE;
655     }
656
657     wm->bBufferLoaded = FALSE;
658     wm->ioProc->count++;
659
660     if (dwOpenFlags & MMIO_ALLOCBUF) {
661         if ((refmminfo->wErrorRet = MMIO_SetBuffer(wm, NULL, MMIO_DEFAULTBUFFER, 0)))
662             goto error1;
663     } else if (wm->info.fccIOProc == FOURCC_MEM) {
664         refmminfo->wErrorRet = MMIO_SetBuffer(wm, refmminfo->pchBuffer, refmminfo->cchBuffer, 0);
665         if (refmminfo->wErrorRet != MMSYSERR_NOERROR)
666             goto error1;
667         wm->bBufferLoaded = TRUE;
668     } /* else => unbuffered, wm->info.pchBuffer == NULL */
669
670     /* see mmioDosIOProc for that one */
671     wm->info.adwInfo[0] = refmminfo->adwInfo[0];
672     wm->info.dwFlags = dwOpenFlags;
673
674     /* call IO proc to actually open file */
675     refmminfo->wErrorRet = send_message(wm->ioProc, &wm->info, MMIOM_OPEN, 
676                                         (LPARAM)szFileName, 0, MMIO_PROC_32A);
677
678     /* grab file size, when possible */
679     wm->dwFileSize = GetFileSize((HANDLE)wm->info.adwInfo[0], NULL);
680
681     if (refmminfo->wErrorRet == 0)
682         return wm->info.hmmio;
683  error1:
684     if (wm->ioProc) wm->ioProc->count--;
685  error2:
686     MMIO_Destroy(wm);
687     return 0;
688 }
689
690 /**************************************************************************
691  *                              mmioOpenW                       [WINMM.@]
692  */
693 HMMIO WINAPI mmioOpenW(LPWSTR szFileName, MMIOINFO* lpmmioinfo,
694                        DWORD dwOpenFlags)
695 {
696     HMMIO       ret;
697     LPSTR       szFn = NULL;
698
699     if (szFileName)
700     {
701         INT     len = WideCharToMultiByte( CP_ACP, 0, szFileName, -1, NULL, 0, NULL, NULL );
702         szFn = HeapAlloc( GetProcessHeap(), 0, len );
703         if (!szFn) return NULL;
704         WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szFn, len, NULL, NULL );
705     }
706
707     ret = MMIO_Open(szFn, lpmmioinfo, dwOpenFlags, MMIO_PROC_32W);
708
709     HeapFree(GetProcessHeap(), 0, szFn);
710     return ret;
711 }
712
713 /**************************************************************************
714  *                              mmioOpenA                       [WINMM.@]
715  */
716 HMMIO WINAPI mmioOpenA(LPSTR szFileName, MMIOINFO* lpmmioinfo,
717                        DWORD dwOpenFlags)
718 {
719     return  MMIO_Open(szFileName, lpmmioinfo, dwOpenFlags, MMIO_PROC_32A);
720 }
721
722 /**************************************************************************
723  *                              mmioClose               [WINMM.@]
724  */
725 MMRESULT WINAPI mmioClose(HMMIO hmmio, UINT uFlags)
726 {
727     LPWINE_MMIO wm;
728     MMRESULT    result;
729
730     TRACE("(%p, %04X);\n", hmmio, uFlags);
731
732     if ((wm = MMIO_Get(hmmio)) == NULL)
733         return MMSYSERR_INVALHANDLE;
734
735     if ((result = MMIO_Flush(wm, 0)) != MMSYSERR_NOERROR)
736         return result;
737
738     result = send_message(wm->ioProc, &wm->info, MMIOM_CLOSE, 
739                           uFlags, 0, MMIO_PROC_32A);
740
741     MMIO_SetBuffer(wm, NULL, 0, 0);
742
743     wm->ioProc->count--;
744
745     if (wm->bTmpIOProc)
746         MMIO_InstallIOProc(wm->info.fccIOProc, wm->ioProc->pIOProc,
747                            MMIO_REMOVEPROC, wm->ioProc->type);
748
749     MMIO_Destroy(wm);
750
751     return result;
752 }
753
754 /**************************************************************************
755  *                              mmioRead                [WINMM.@]
756  */
757 LONG WINAPI mmioRead(HMMIO hmmio, HPSTR pch, LONG cch)
758 {
759     LPWINE_MMIO wm;
760     LONG        count;
761
762     TRACE("(%p, %p, %ld);\n", hmmio, pch, cch);
763
764     if ((wm = MMIO_Get(hmmio)) == NULL)
765         return -1;
766
767     /* unbuffered case first */
768     if (!wm->info.pchBuffer)
769         return send_message(wm->ioProc, &wm->info, MMIOM_READ, 
770                             (LPARAM)pch, cch, MMIO_PROC_32A);
771
772     /* first try from current buffer */
773     if (wm->info.pchNext != wm->info.pchEndRead) {
774         count = wm->info.pchEndRead - wm->info.pchNext;
775         if (count > cch || count < 0) count = cch;
776         memcpy(pch, wm->info.pchNext, count);
777         wm->info.pchNext += count;
778         pch += count;
779         cch -= count;
780     } else
781         count = 0;
782
783     if (cch && (wm->info.fccIOProc != FOURCC_MEM)) {
784         assert(wm->info.cchBuffer);
785
786         while (cch) {
787             LONG size;
788
789             size = MMIO_GrabNextBuffer(wm, TRUE);
790             if (size <= 0) break;
791             if (size > cch) size = cch;
792             memcpy(pch, wm->info.pchBuffer, size);
793             wm->info.pchNext += size;
794             pch += size;
795             cch -= size;
796             count += size;
797         }
798     }
799
800     TRACE("count=%ld\n", count);
801     return count;
802 }
803
804 /**************************************************************************
805  *                              mmioWrite               [WINMM.@]
806  */
807 LONG WINAPI mmioWrite(HMMIO hmmio, HPCSTR pch, LONG cch)
808 {
809     LPWINE_MMIO wm;
810     LONG        count;
811
812     TRACE("(%p, %p, %ld);\n", hmmio, pch, cch);
813
814     if ((wm = MMIO_Get(hmmio)) == NULL)
815         return -1;
816
817     if (wm->info.cchBuffer) {
818         LONG    bytesW = 0;
819
820         count = 0;
821         while (cch) {
822             if (wm->info.pchNext != wm->info.pchEndWrite) {
823                 count = wm->info.pchEndWrite - wm->info.pchNext;
824                 if (count > cch || count < 0) count = cch;
825                 memcpy(wm->info.pchNext, pch, count);
826                 wm->info.pchNext += count;
827                 pch += count;
828                 cch -= count;
829                 bytesW += count;
830                 wm->info.dwFlags |= MMIO_DIRTY;
831             } else {
832                 if (wm->info.fccIOProc == FOURCC_MEM) {
833                     if (wm->info.adwInfo[0]) {
834                         /* from where would we get the memory handle? */
835                         FIXME("memory file expansion not implemented!\n");
836                         break;
837                     } else break;
838                 }
839             }
840
841             if (wm->info.pchNext == wm->info.pchEndWrite)
842             {
843                 MMIO_Flush(wm, MMIO_EMPTYBUF);
844                 MMIO_GrabNextBuffer(wm, FALSE);
845             }
846             else break;
847         }
848         count = bytesW;
849     } else {
850         count = send_message(wm->ioProc, &wm->info, MMIOM_WRITE, 
851                              (LPARAM)pch, cch, MMIO_PROC_32A);
852         wm->info.lBufOffset = wm->info.lDiskOffset;
853     }
854
855     TRACE("bytes written=%ld\n", count);
856     return count;
857 }
858
859 /**************************************************************************
860  *                              mmioSeek                [WINMM.@]
861  */
862 LONG WINAPI mmioSeek(HMMIO hmmio, LONG lOffset, INT iOrigin)
863 {
864     LPWINE_MMIO wm;
865     LONG        offset;
866
867     TRACE("(%p, %08lX, %d);\n", hmmio, lOffset, iOrigin);
868
869     if ((wm = MMIO_Get(hmmio)) == NULL)
870         return MMSYSERR_INVALHANDLE;
871
872     /* not buffered, direct seek on file */
873     if (!wm->info.pchBuffer)
874         return send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 
875                             lOffset, iOrigin, MMIO_PROC_32A);
876
877     switch (iOrigin) {
878     case SEEK_SET:
879         offset = lOffset;
880         break;
881     case SEEK_CUR:
882         offset = wm->info.lBufOffset + (wm->info.pchNext - wm->info.pchBuffer) + lOffset;
883         break;
884     case SEEK_END:
885         offset = ((wm->info.fccIOProc == FOURCC_MEM)? wm->info.cchBuffer : wm->dwFileSize) - lOffset;
886         break;
887     default:
888         return -1;
889     }
890
891     if (offset && offset >= wm->dwFileSize && wm->info.fccIOProc != FOURCC_MEM) {
892         /* should check that write mode exists */
893         if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)
894             return -1;
895         wm->info.lBufOffset = offset;
896         wm->info.pchEndRead = wm->info.pchBuffer;
897         wm->info.pchEndWrite = wm->info.pchBuffer + wm->info.cchBuffer;
898         if ((wm->info.dwFlags & MMIO_RWMODE) == MMIO_READ) {
899             wm->info.lDiskOffset = wm->dwFileSize;
900         }
901     } else if ((wm->info.cchBuffer > 0) &&
902         ((offset < wm->info.lBufOffset) ||
903          (offset >= wm->info.lBufOffset + wm->info.cchBuffer) ||
904          !wm->bBufferLoaded)) {
905         /* stay in same buffer ? */
906         /* some memory mapped buffers are defined with -1 as a size */
907
908         /* condition to change buffer */
909         if ((wm->info.fccIOProc == FOURCC_MEM) ||
910             MMIO_Flush(wm, 0) != MMSYSERR_NOERROR ||
911             /* this also sets the wm->info.lDiskOffset field */
912             send_message(wm->ioProc, &wm->info, MMIOM_SEEK,
913                          (offset / wm->info.cchBuffer) * wm->info.cchBuffer,
914                          SEEK_SET, MMIO_PROC_32A) == -1)
915             return -1;
916         MMIO_GrabNextBuffer(wm, TRUE);
917     }
918
919     wm->info.pchNext = wm->info.pchBuffer + (offset - wm->info.lBufOffset);
920
921     TRACE("=> %ld\n", offset);
922     return offset;
923 }
924
925 /**************************************************************************
926  *                              mmioGetInfo             [WINMM.@]
927  */
928 MMRESULT WINAPI mmioGetInfo(HMMIO hmmio, MMIOINFO* lpmmioinfo, UINT uFlags)
929 {
930     LPWINE_MMIO         wm;
931
932     TRACE("(%p,%p,0x%08x)\n",hmmio,lpmmioinfo,uFlags);
933
934     if ((wm = MMIO_Get(hmmio)) == NULL)
935         return MMSYSERR_INVALHANDLE;
936
937     memcpy(lpmmioinfo, &wm->info, sizeof(MMIOINFO));
938     /* don't expose 16 bit ioproc:s */
939     if (wm->ioProc->type != MMIO_PROC_16)
940         lpmmioinfo->pIOProc = wm->ioProc->pIOProc;
941
942     return MMSYSERR_NOERROR;
943 }
944
945 /**************************************************************************
946  *                              mmioSetInfo             [WINMM.@]
947  */
948 MMRESULT WINAPI mmioSetInfo(HMMIO hmmio, const MMIOINFO* lpmmioinfo, UINT uFlags)
949 {
950     LPWINE_MMIO         wm;
951
952     TRACE("(%p,%p,0x%08x)\n",hmmio,lpmmioinfo,uFlags);
953
954     if ((wm = MMIO_Get(hmmio)) == NULL)
955         return MMSYSERR_INVALHANDLE;
956
957     /* check pointers coherence */
958     if (lpmmioinfo->pchNext < wm->info.pchBuffer ||
959         lpmmioinfo->pchNext > wm->info.pchBuffer + wm->info.cchBuffer ||
960         lpmmioinfo->pchEndRead < wm->info.pchBuffer ||
961         lpmmioinfo->pchEndRead > wm->info.pchBuffer + wm->info.cchBuffer ||
962         lpmmioinfo->pchEndWrite < wm->info.pchBuffer ||
963         lpmmioinfo->pchEndWrite > wm->info.pchBuffer + wm->info.cchBuffer)
964         return MMSYSERR_INVALPARAM;
965
966     wm->info.pchNext = lpmmioinfo->pchNext;
967     wm->info.pchEndRead = lpmmioinfo->pchEndRead;
968
969     return MMSYSERR_NOERROR;
970 }
971
972 /**************************************************************************
973 *                               mmioSetBuffer           [WINMM.@]
974 */
975 MMRESULT WINAPI mmioSetBuffer(HMMIO hmmio, LPSTR pchBuffer, LONG cchBuffer, UINT uFlags)
976 {
977     LPWINE_MMIO         wm;
978
979     TRACE("(hmmio=%p, pchBuf=%p, cchBuf=%ld, uFlags=%#08x)\n",
980           hmmio, pchBuffer, cchBuffer, uFlags);
981
982     if ((wm = MMIO_Get(hmmio)) == NULL)
983         return MMSYSERR_INVALHANDLE;
984
985     return MMIO_SetBuffer(wm, pchBuffer, cchBuffer, uFlags);
986 }
987
988 /**************************************************************************
989  *                              mmioFlush               [WINMM.@]
990  */
991 MMRESULT WINAPI mmioFlush(HMMIO hmmio, UINT uFlags)
992 {
993     LPWINE_MMIO         wm;
994
995     TRACE("(%p, %04X)\n", hmmio, uFlags);
996
997     if ((wm = MMIO_Get(hmmio)) == NULL)
998         return MMSYSERR_INVALHANDLE;
999
1000     return MMIO_Flush(wm, uFlags);
1001 }
1002
1003 /**************************************************************************
1004  *                              mmioAdvance             [WINMM.@]
1005  */
1006 MMRESULT WINAPI mmioAdvance(HMMIO hmmio, MMIOINFO* lpmmioinfo, UINT uFlags)
1007 {
1008     LPWINE_MMIO         wm;
1009
1010     TRACE("hmmio=%p, lpmmioinfo=%p, uFlags=%04X\n", hmmio, lpmmioinfo, uFlags);
1011
1012     /* NOTE: mmioAdvance16 heavily relies on parameters from lpmmioinfo we're using
1013      * here. be sure if you change something here to check mmioAdvance16 as well
1014      */
1015     if ((wm = MMIO_Get(hmmio)) == NULL)
1016         return MMSYSERR_INVALHANDLE;
1017
1018     if (!wm->info.cchBuffer)
1019         return MMIOERR_UNBUFFERED;
1020
1021     if (uFlags != MMIO_READ && uFlags != MMIO_WRITE)
1022         return MMSYSERR_INVALPARAM;
1023
1024     if (uFlags == MMIO_WRITE && (lpmmioinfo->dwFlags & MMIO_DIRTY))
1025     {
1026         send_message(wm->ioProc, &wm->info, MMIOM_SEEK, 
1027                      lpmmioinfo->lBufOffset, SEEK_SET, MMIO_PROC_32A);
1028         send_message(wm->ioProc, &wm->info, MMIOM_WRITE, 
1029                      (LPARAM)lpmmioinfo->pchBuffer,
1030                      lpmmioinfo->pchNext - lpmmioinfo->pchBuffer, MMIO_PROC_32A);
1031         lpmmioinfo->dwFlags &= ~MMIO_DIRTY;
1032     }
1033     if (MMIO_Flush(wm, 0) != MMSYSERR_NOERROR)
1034         return MMIOERR_CANNOTWRITE;
1035
1036     if (lpmmioinfo) {
1037         wm->dwFileSize = max(wm->dwFileSize, lpmmioinfo->lBufOffset + 
1038                              (lpmmioinfo->pchNext - lpmmioinfo->pchBuffer));
1039     }
1040     MMIO_GrabNextBuffer(wm, uFlags == MMIO_READ);
1041
1042     if (lpmmioinfo) {
1043         lpmmioinfo->pchNext = lpmmioinfo->pchBuffer;
1044         lpmmioinfo->pchEndRead  = lpmmioinfo->pchBuffer +
1045             (wm->info.pchEndRead - wm->info.pchBuffer);
1046         lpmmioinfo->pchEndWrite = lpmmioinfo->pchBuffer +
1047             (wm->info.pchEndWrite - wm->info.pchBuffer);
1048         lpmmioinfo->lDiskOffset = wm->info.lDiskOffset;
1049         lpmmioinfo->lBufOffset = wm->info.lBufOffset;
1050     }
1051     return MMSYSERR_NOERROR;
1052 }
1053
1054 /**************************************************************************
1055  *                              mmioStringToFOURCCA     [WINMM.@]
1056  */
1057 FOURCC WINAPI mmioStringToFOURCCA(LPCSTR sz, UINT uFlags)
1058 {
1059     CHAR cc[4];
1060     int i = 0;
1061
1062     for (i = 0; i < 4 && sz[i]; i++) {
1063         if (uFlags & MMIO_TOUPPER) {
1064             cc[i] = toupper(sz[i]);
1065         } else {
1066             cc[i] = sz[i];
1067         }
1068     }
1069
1070     /* Pad with spaces */
1071     while (i < 4) cc[i++] = ' ';
1072
1073     TRACE("Got '%.4s'\n",cc);
1074     return mmioFOURCC(cc[0],cc[1],cc[2],cc[3]);
1075 }
1076
1077 /**************************************************************************
1078  *                              mmioStringToFOURCCW     [WINMM.@]
1079  */
1080 FOURCC WINAPI mmioStringToFOURCCW(LPCWSTR sz, UINT uFlags)
1081 {
1082     char        szA[4];
1083
1084     WideCharToMultiByte( CP_ACP, 0, sz, 4, szA, sizeof(szA), NULL, NULL );
1085     return mmioStringToFOURCCA(szA,uFlags);
1086 }
1087
1088 /**************************************************************************
1089  *                              mmioInstallIOProcA         [WINMM.@]
1090  */
1091 LPMMIOPROC WINAPI mmioInstallIOProcA(FOURCC fccIOProc,
1092                                      LPMMIOPROC pIOProc, DWORD dwFlags)
1093 {
1094     return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, MMIO_PROC_32A);
1095 }
1096
1097 /**************************************************************************
1098  *                              mmioInstallIOProcW         [WINMM.@]
1099  */
1100 LPMMIOPROC WINAPI mmioInstallIOProcW(FOURCC fccIOProc,
1101                                      LPMMIOPROC pIOProc, DWORD dwFlags)
1102 {
1103     return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, MMIO_PROC_32W);
1104 }
1105
1106 /******************************************************************
1107  *              MMIO_SendMessage
1108  *
1109  *
1110  */
1111 LRESULT         MMIO_SendMessage(HMMIO hmmio, UINT uMessage, LPARAM lParam1, 
1112                                  LPARAM lParam2, enum mmioProcType type)
1113 {
1114     LPWINE_MMIO         wm;
1115
1116     TRACE("(%p, %u, %ld, %ld, %d)\n", hmmio, uMessage, lParam1, lParam2, type);
1117
1118     if (uMessage < MMIOM_USER)
1119         return MMSYSERR_INVALPARAM;
1120
1121     if ((wm = MMIO_Get(hmmio)) == NULL)
1122         return MMSYSERR_INVALHANDLE;
1123
1124     return send_message(wm->ioProc, &wm->info, uMessage, lParam1, lParam2, type);
1125 }
1126
1127 /**************************************************************************
1128  *                              mmioSendMessage         [WINMM.@]
1129  */
1130 LRESULT WINAPI mmioSendMessage(HMMIO hmmio, UINT uMessage,
1131                                LPARAM lParam1, LPARAM lParam2)
1132 {
1133     return MMIO_SendMessage(hmmio, uMessage, lParam1, lParam2, MMIO_PROC_32A);
1134 }
1135
1136 /**************************************************************************
1137  *                              mmioDescend             [WINMM.@]
1138  */
1139 MMRESULT WINAPI mmioDescend(HMMIO hmmio, LPMMCKINFO lpck,
1140                             const MMCKINFO* lpckParent, UINT uFlags)
1141 {
1142     DWORD               dwOldPos;
1143     FOURCC              srchCkId;
1144     FOURCC              srchType;
1145
1146     TRACE("(%p, %p, %p, %04X);\n", hmmio, lpck, lpckParent, uFlags);
1147
1148     if (lpck == NULL)
1149         return MMSYSERR_INVALPARAM;
1150
1151     dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
1152     TRACE("dwOldPos=%ld\n", dwOldPos);
1153
1154     if (lpckParent != NULL) {
1155         TRACE("seek inside parent at %ld !\n", lpckParent->dwDataOffset);
1156         /* EPP: was dwOldPos = mmioSeek(hmmio,lpckParent->dwDataOffset,SEEK_SET); */
1157         if (dwOldPos < lpckParent->dwDataOffset ||
1158             dwOldPos >= lpckParent->dwDataOffset + lpckParent->cksize) {
1159             WARN("outside parent chunk\n");
1160             return MMIOERR_CHUNKNOTFOUND;
1161         }
1162     }
1163
1164     /* The SDK docu says 'ckid' is used for all cases. Real World
1165      * examples disagree -Marcus,990216.
1166      */
1167
1168     srchCkId = 0;
1169     srchType = 0;
1170
1171     /* find_chunk looks for 'ckid' */
1172     if (uFlags & MMIO_FINDCHUNK)
1173         srchCkId = lpck->ckid;
1174
1175     /* find_riff and find_list look for 'fccType' */
1176     if (uFlags & MMIO_FINDLIST)
1177     {
1178         srchCkId = FOURCC_LIST;
1179         srchType = lpck->fccType;
1180     }
1181
1182     if (uFlags & MMIO_FINDRIFF)
1183     {
1184         srchCkId = FOURCC_RIFF;
1185         srchType = lpck->fccType;
1186     }
1187
1188     TRACE("searching for %4.4s.%4.4s\n",
1189           (LPCSTR)&srchCkId, srchType ? (LPCSTR)&srchType : "any");
1190
1191     while (TRUE)
1192     {
1193         LONG ix;
1194
1195         ix = mmioRead(hmmio, (LPSTR)lpck, 3 * sizeof(DWORD));
1196         if (ix < 2*sizeof(DWORD))
1197         {
1198             mmioSeek(hmmio, dwOldPos, SEEK_SET);
1199             WARN("return ChunkNotFound\n");
1200             return MMIOERR_CHUNKNOTFOUND;
1201         }
1202
1203         lpck->dwDataOffset = dwOldPos + 2 * sizeof(DWORD);
1204         TRACE("ckid=%4.4s fcc=%4.4s cksize=%08lX !\n",
1205               (LPCSTR)&lpck->ckid,
1206               srchType ? (LPCSTR)&lpck->fccType:"<na>",
1207               lpck->cksize);
1208         if ( (!srchCkId || (srchCkId == lpck->ckid)) &&
1209              (!srchType || (srchType == lpck->fccType)) )
1210             break;
1211
1212         dwOldPos = lpck->dwDataOffset + ((lpck->cksize + 1) & ~1);
1213         mmioSeek(hmmio, dwOldPos, SEEK_SET);
1214     }
1215
1216     lpck->dwFlags = 0;
1217     /* If we were looking for RIFF/LIST chunks, the final file position
1218      * is after the chunkid. If we were just looking for the chunk
1219      * it is after the cksize. So add 4 in RIFF/LIST case.
1220      */
1221     if (lpck->ckid == FOURCC_RIFF || lpck->ckid == FOURCC_LIST)
1222         mmioSeek(hmmio, lpck->dwDataOffset + sizeof(DWORD), SEEK_SET);
1223     else
1224         mmioSeek(hmmio, lpck->dwDataOffset, SEEK_SET);
1225     TRACE("lpck: ckid=%.4s, cksize=%ld, dwDataOffset=%ld fccType=%08lX (%.4s)!\n",
1226           (LPSTR)&lpck->ckid, lpck->cksize, lpck->dwDataOffset,
1227           lpck->fccType, srchType?(LPSTR)&lpck->fccType:"");
1228     return MMSYSERR_NOERROR;
1229 }
1230
1231 /**************************************************************************
1232  *                              mmioAscend              [WINMM.@]
1233  */
1234 MMRESULT WINAPI mmioAscend(HMMIO hmmio, LPMMCKINFO lpck, UINT uFlags)
1235 {
1236     TRACE("(%p, %p, %04X);\n", hmmio, lpck, uFlags);
1237
1238     if (lpck->dwFlags & MMIO_DIRTY) {
1239         DWORD   dwOldPos, dwNewSize;
1240
1241         TRACE("Chunk is dirty, checking if chunk's size is correct\n");
1242         dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
1243         TRACE("dwOldPos=%ld lpck->dwDataOffset = %ld\n", dwOldPos, lpck->dwDataOffset);
1244         dwNewSize = dwOldPos - lpck->dwDataOffset;
1245         if (dwNewSize != lpck->cksize) {
1246             TRACE("Nope: lpck->cksize=%ld dwNewSize=%ld\n", lpck->cksize, dwNewSize);
1247             lpck->cksize = dwNewSize;
1248
1249             /* pad odd size with 0 */
1250             if (dwNewSize & 1) {
1251                 char ch = 0;
1252                 mmioWrite(hmmio, &ch, 1);
1253             }
1254             mmioSeek(hmmio, lpck->dwDataOffset - sizeof(DWORD), SEEK_SET);
1255             mmioWrite(hmmio, (LPSTR)&dwNewSize, sizeof(DWORD));
1256         }
1257         lpck->dwFlags = 0;
1258     }
1259
1260     mmioSeek(hmmio, lpck->dwDataOffset + ((lpck->cksize + 1) & ~1), SEEK_SET);
1261
1262     return MMSYSERR_NOERROR;
1263 }
1264
1265 /**************************************************************************
1266  *                      mmioCreateChunk                         [WINMM.@]
1267  */
1268 MMRESULT WINAPI mmioCreateChunk(HMMIO hmmio, MMCKINFO* lpck, UINT uFlags)
1269 {
1270     DWORD       dwOldPos;
1271     LONG        size;
1272     LONG        ix;
1273
1274     TRACE("(%p, %p, %04X);\n", hmmio, lpck, uFlags);
1275
1276     dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
1277     TRACE("dwOldPos=%ld\n", dwOldPos);
1278
1279     if (uFlags == MMIO_CREATELIST)
1280         lpck->ckid = FOURCC_LIST;
1281     else if (uFlags == MMIO_CREATERIFF)
1282         lpck->ckid = FOURCC_RIFF;
1283
1284     TRACE("ckid=%.4s\n", (LPSTR)&lpck->ckid);
1285
1286     size = 2 * sizeof(DWORD);
1287     lpck->dwDataOffset = dwOldPos + size;
1288
1289     if (lpck->ckid == FOURCC_RIFF || lpck->ckid == FOURCC_LIST)
1290         size += sizeof(DWORD);
1291     lpck->dwFlags = MMIO_DIRTY;
1292
1293     ix = mmioWrite(hmmio, (LPSTR)lpck, size);
1294     TRACE("after mmioWrite ix = %ld req = %ld, errno = %d\n",ix, size, errno);
1295     if (ix < size) {
1296         mmioSeek(hmmio, dwOldPos, SEEK_SET);
1297         WARN("return CannotWrite\n");
1298         return MMIOERR_CANNOTWRITE;
1299     }
1300
1301     return MMSYSERR_NOERROR;
1302 }
1303
1304 /**************************************************************************
1305  *                              mmioRenameA                     [WINMM.@]
1306  */
1307 MMRESULT WINAPI mmioRenameA(LPCSTR szFileName, LPCSTR szNewFileName,
1308                             const MMIOINFO* lpmmioinfo, DWORD dwFlags)
1309 {
1310     struct IOProcList*  ioProc = NULL;
1311     struct IOProcList   tmp;
1312     FOURCC              fcc;
1313
1314     TRACE("('%s', '%s', %p, %08lX);\n",
1315           debugstr_a(szFileName), debugstr_a(szNewFileName), lpmmioinfo, dwFlags);
1316
1317     /* If both params are NULL, then parse the file name */
1318     if (lpmmioinfo && lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL)
1319     {
1320         fcc = MMIO_ParseExtA(szFileName);
1321         if (fcc) ioProc = MMIO_FindProcNode(fcc);
1322     }
1323
1324     /* Handle any unhandled/error case from above. Assume DOS file */
1325     if (!lpmmioinfo || (lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL && ioProc == NULL))
1326         ioProc = MMIO_FindProcNode(FOURCC_DOS);
1327     /* if just the four character code is present, look up IO proc */
1328     else if (lpmmioinfo->pIOProc == NULL)
1329         ioProc = MMIO_FindProcNode(lpmmioinfo->fccIOProc);
1330     else /* use relevant ioProc */
1331     {
1332         ioProc = &tmp;
1333         tmp.fourCC = lpmmioinfo->fccIOProc;
1334         tmp.pIOProc = lpmmioinfo->pIOProc;
1335         tmp.type = MMIO_PROC_32A;
1336         tmp.count = 1;
1337     }
1338
1339     /* FIXME: should we actually pass lpmmioinfo down the drain ???
1340      * or make a copy of it because it's const ???
1341      */
1342     return send_message(ioProc, (MMIOINFO*)lpmmioinfo, MMIOM_RENAME,
1343                         (LPARAM)szFileName, (LPARAM)szNewFileName, MMIO_PROC_32A);
1344 }
1345
1346 /**************************************************************************
1347  *                              mmioRenameW                     [WINMM.@]
1348  */
1349 MMRESULT WINAPI mmioRenameW(LPCWSTR szFileName, LPCWSTR szNewFileName,
1350                             const MMIOINFO* lpmmioinfo, DWORD dwFlags)
1351 {
1352     LPSTR       szFn = NULL;
1353     LPSTR       sznFn = NULL;
1354     UINT        ret = MMSYSERR_NOMEM;
1355     INT         len;
1356
1357     if (szFileName)
1358     {
1359         len = WideCharToMultiByte( CP_ACP, 0, szFileName, -1, NULL, 0, NULL, NULL );
1360         szFn = HeapAlloc( GetProcessHeap(), 0, len );
1361         if (!szFn) goto done;
1362         WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szFn, len, NULL, NULL );
1363     }
1364     if (szNewFileName)
1365     {
1366         len = WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, NULL, 0, NULL, NULL );
1367         sznFn = HeapAlloc( GetProcessHeap(), 0, len );
1368         if (!sznFn) goto done;
1369         WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, sznFn, len, NULL, NULL );
1370     }
1371
1372     ret = mmioRenameA(szFn, sznFn, lpmmioinfo, dwFlags);
1373
1374 done:
1375     HeapFree(GetProcessHeap(),0,szFn);
1376     HeapFree(GetProcessHeap(),0,sznFn);
1377     return ret;
1378 }