Implemented a large number of the msvideo dll routines.
[wine] / dlls / winmm / mmio.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * MMIO functions
4  *
5  * Copyright 1998 Andrew Taylor
6  * Copyright 1998 Ove Kåven
7  * Copyright 2000 Eric Pouech
8  */
9
10 /* Still to be done:
11  *      + correct handling of global/local IOProcs
12  *      + mode of mmio objects is not used (read vs write vs readwrite)
13  *      + IO buffering is limited to 64k
14  *      + optimization of internal buffers (seg / lin)
15  *      + even in 32 bit only, a seg ptr IO buffer is allocated (after this is 
16  *        fixed, we'll have a proper 32/16 separation)
17  *      + thread safeness
18  *      + rename operation is broken
19  */
20
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include "windef.h"
27 #include "wine/winbase16.h"
28 #include "heap.h"
29 #include "selectors.h"
30 #include "file.h"
31 #include "mmsystem.h"
32 #include "debugtools.h"
33 #include "winemm.h"
34
35 DEFAULT_DEBUG_CHANNEL(mmio);
36
37 /**************************************************************************
38  *                      mmioDosIOProc                           [internal]
39  */
40 static LRESULT CALLBACK mmioDosIOProc(LPMMIOINFO lpmmioinfo, UINT uMessage, 
41                                       LPARAM lParam1, LPARAM lParam2) 
42 {
43     LRESULT     ret = MMSYSERR_NOERROR;
44
45     TRACE("(%p, %X, %ld, %ld);\n", lpmmioinfo, uMessage, lParam1, lParam2);
46     
47     switch (uMessage) {
48     case MMIOM_OPEN: 
49         {
50             /* Parameters:
51              * lParam1 = szFileName parameter from mmioOpen
52              * lParam2 = reserved (we use it for 16-bitness)
53              * Returns: zero on success, error code on error
54              * NOTE: lDiskOffset automatically set to zero
55              */
56             OFSTRUCT ofs;
57             LPCSTR szFileName = (LPCSTR)lParam1;
58             
59             if (lpmmioinfo->dwFlags & MMIO_GETTEMP) {
60                 FIXME("MMIO_GETTEMP not implemented\n");
61                 return MMIOERR_CANNOTOPEN;
62             }
63             
64             /* if filename NULL, assume open file handle in adwInfo[0] */
65             if (!szFileName) {
66                 if (lParam2) 
67                     lpmmioinfo->adwInfo[0] = FILE_GetHandle(lpmmioinfo->adwInfo[0]);
68                 break;
69             }
70             
71             lpmmioinfo->adwInfo[0] = (DWORD)OpenFile(szFileName, &ofs, 
72                                                      lpmmioinfo->dwFlags);
73             if (lpmmioinfo->adwInfo[0] == -1)
74                 ret = MMIOERR_CANNOTOPEN;
75         }
76         break;
77     
78     case MMIOM_CLOSE: 
79         /* Parameters:
80          * lParam1 = wFlags parameter from mmioClose
81          * lParam2 = unused
82          * Returns: zero on success, error code on error
83          */
84         if (!(lParam1 & MMIO_FHOPEN))
85             _lclose((HFILE)lpmmioinfo->adwInfo[0]);
86         break;
87             
88     case MMIOM_READ: 
89         /* Parameters:
90          * lParam1 = huge pointer to read buffer
91          * lParam2 = number of bytes to read
92          * Returns: number of bytes read, 0 for EOF, -1 for error (error code
93          *         in wErrorRet)
94          */
95         ret = _lread((HFILE)lpmmioinfo->adwInfo[0], (HPSTR)lParam1, (LONG)lParam2);
96         if (ret != -1)
97             lpmmioinfo->lDiskOffset += ret;
98         
99         break;
100     
101     case MMIOM_WRITE:
102     case MMIOM_WRITEFLUSH: 
103         /* no internal buffering, so WRITEFLUSH handled same as WRITE */
104         
105         /* Parameters:
106          * lParam1 = huge pointer to write buffer
107          * lParam2 = number of bytes to write
108          * Returns: number of bytes written, -1 for error (error code in
109          *              wErrorRet)
110          */
111         ret = _hwrite((HFILE)lpmmioinfo->adwInfo[0], (HPSTR)lParam1, (LONG)lParam2);
112         if (ret != -1)
113             lpmmioinfo->lDiskOffset += ret;
114         break;
115     
116     case MMIOM_SEEK: 
117         /* Parameters:
118          * lParam1 = new position
119          * lParam2 = from whence to seek (SEEK_SET, SEEK_CUR, SEEK_END)
120          * Returns: new file postion, -1 on error
121          */
122         ret = _llseek((HFILE)lpmmioinfo->adwInfo[0], (LONG)lParam1, (LONG)lParam2);
123         if (ret != -1)
124             lpmmioinfo->lDiskOffset = ret;
125         return ret;
126     
127     case MMIOM_RENAME: 
128         /* Parameters:
129          * lParam1 = old name
130          * lParam2 = new name
131          * Returns: zero on success, non-zero on failure
132          */
133          FIXME("MMIOM_RENAME incomplete\n");
134          if (!MoveFileA((const char*)lParam1, (const char*)lParam2))
135              ret = MMIOERR_FILENOTFOUND;
136          break;
137
138     default:
139         FIXME("unexpected message %u\n", uMessage);
140         return 0;
141     }
142     
143     return ret;
144 }
145
146 /**************************************************************************
147  *                      mmioMemIOProc                           [internal]
148  */
149 static LRESULT CALLBACK mmioMemIOProc(LPMMIOINFO lpmmioinfo, UINT uMessage, 
150                                       LPARAM lParam1, LPARAM lParam2) 
151 {
152     TRACE("(%p,0x%04x,0x%08lx,0x%08lx)\n", lpmmioinfo, uMessage, lParam1, lParam2);
153
154     switch (uMessage) {
155         
156     case MMIOM_OPEN: 
157         /* Parameters:
158          * lParam1 = filename (must be NULL)
159          * lParam2 = reserved (we use it for 16-bitness)
160          * Returns: zero on success, error code on error
161          * NOTE: lDiskOffset automatically set to zero
162          */
163         /* FIXME: io proc shouldn't change it */
164         if (!(lpmmioinfo->dwFlags & MMIO_CREATE))
165             lpmmioinfo->pchEndRead = lpmmioinfo->pchEndWrite;
166         return 0;
167         
168     case MMIOM_CLOSE: 
169         /* Parameters:
170          * lParam1 = wFlags parameter from mmioClose
171          * lParam2 = unused
172          * Returns: zero on success, error code on error
173          */
174         return 0;
175         
176     case MMIOM_READ: 
177         /* Parameters:
178          * lParam1 = huge pointer to read buffer
179          * lParam2 = number of bytes to read
180          * Returns: number of bytes read, 0 for EOF, -1 for error (error code
181          *         in wErrorRet)
182          * NOTE: lDiskOffset should be updated
183          */
184         FIXME("MMIOM_READ on memory files should not occur, buffer may be lost!\n");
185         return 0;
186     
187     case MMIOM_WRITE:
188     case MMIOM_WRITEFLUSH: 
189         /* no internal buffering, so WRITEFLUSH handled same as WRITE */
190         
191         /* Parameters:
192          * lParam1 = huge pointer to write buffer
193          * lParam2 = number of bytes to write
194          * Returns: number of bytes written, -1 for error (error code in
195          *              wErrorRet)
196          * NOTE: lDiskOffset should be updated
197          */
198         FIXME("MMIOM_WRITE on memory files should not occur, buffer may be lost!\n");
199         return 0;
200     
201     case MMIOM_SEEK: 
202         /* Parameters:
203          * lParam1 = new position
204          * lParam2 = from whence to seek (SEEK_SET, SEEK_CUR, SEEK_END)
205          * Returns: new file postion, -1 on error
206          * NOTE: lDiskOffset should be updated
207          */
208         FIXME("MMIOM_SEEK on memory files should not occur, buffer may be lost!\n");
209         return -1;
210     
211     default:
212         FIXME("unexpected message %u\n", uMessage);
213         return 0;
214     }
215     
216     return 0;
217 }
218
219
220 enum mmioProcType {MMIO_PROC_16,MMIO_PROC_32A,MMIO_PROC_32W};
221
222 struct IOProcList
223 {
224     struct IOProcList*pNext;       /* Next item in linked list */
225     FOURCC            fourCC;      /* four-character code identifying IOProc */
226     LPMMIOPROC        pIOProc;     /* pointer to IProc */
227     enum mmioProcType type;        /* 16, 32A or 32W */
228     int               count;       /* number of objects linked to it */
229 };
230
231 /* This array will be the entire list for most apps */
232
233 static struct IOProcList defaultProcs[] = {
234     {&defaultProcs[1], FOURCC_DOS, (LPMMIOPROC)mmioDosIOProc, MMIO_PROC_32A, 0},
235     {NULL,             FOURCC_MEM, (LPMMIOPROC)mmioMemIOProc, MMIO_PROC_32A, 0},
236 };
237
238 static struct IOProcList*       pIOProcListAnchor = &defaultProcs[0];
239
240 /****************************************************************
241  *              MMIO_FindProcNode                       [INTERNAL]
242  *
243  * Finds the ProcList node associated with a given FOURCC code.
244  */
245 static struct IOProcList*       MMIO_FindProcNode(FOURCC fccIOProc) 
246 {
247     struct IOProcList*  pListNode;
248
249     for (pListNode = pIOProcListAnchor; pListNode; pListNode = pListNode->pNext) {
250         if (pListNode->fourCC == fccIOProc) {
251             return pListNode;
252         }
253     }
254     return NULL;
255 }
256
257 /****************************************************************
258  *              MMIO_InstallIOProc                      [INTERNAL]
259  */
260 static LPMMIOPROC MMIO_InstallIOProc(FOURCC fccIOProc, LPMMIOPROC pIOProc,
261                                      DWORD dwFlags, enum mmioProcType type)
262 {
263     LPMMIOPROC          lpProc = NULL;
264     struct IOProcList*  pListNode;
265     struct IOProcList** ppListNode;
266
267     TRACE("(%ld, %p, %08lX, %i)\n", fccIOProc, pIOProc, dwFlags, type);
268
269     if (dwFlags & MMIO_GLOBALPROC)
270         FIXME("Global procedures not implemented\n");
271
272     /* just handle the known procedures for now */
273     switch (dwFlags & (MMIO_INSTALLPROC|MMIO_REMOVEPROC|MMIO_FINDPROC)) {
274     case MMIO_INSTALLPROC:
275         /* Create new entry for the IOProc list */
276         pListNode = HeapAlloc(GetProcessHeap(), 0, sizeof(*pListNode));
277         if (pListNode) {
278             /* Fill in this node */
279             pListNode->fourCC = fccIOProc;
280             pListNode->pIOProc = pIOProc;
281             pListNode->type = type;
282             pListNode->count = 0;
283
284             /* Stick it on the end of the list */
285             pListNode->pNext = pIOProcListAnchor;
286             pIOProcListAnchor = pListNode;
287             
288             /* Return this IOProc - that's how the caller knows we succeeded */
289             lpProc = pIOProc;
290         } 
291         break;
292                   
293     case MMIO_REMOVEPROC:
294         /* 
295          * Search for the node that we're trying to remove - note
296          * that this method won't find the first item on the list, but
297          * since the first two items on this list are ones we won't
298          * let the user delete anyway, that's okay
299          */         
300         ppListNode = &pIOProcListAnchor;
301         while ((*ppListNode) && (*ppListNode)->fourCC != fccIOProc)
302             ppListNode = &((*ppListNode)->pNext);
303
304         if (*ppListNode) { /* found it */
305             /* FIXME: what should be done if an open mmio object uses this proc ?
306              * shall we return an error, nuke the mmio object ?
307              */
308             if ((*ppListNode)->count) {
309                 ERR("Cannot remove a mmIOProc while in use\n");
310                 break;
311             }
312             /* remove it, but only if it isn't builtin */
313             if ((*ppListNode) >= defaultProcs && 
314                 (*ppListNode) < defaultProcs + sizeof(defaultProcs)) {
315                 WARN("Tried to remove built-in mmio proc. Skipping\n");
316             } else {
317                 /* Okay, nuke it */
318                 struct IOProcList*  ptmpNode = *ppListNode;
319                 lpProc = (*ppListNode)->pIOProc;
320                 *ppListNode = (*ppListNode)->pNext;
321                 HeapFree(GetProcessHeap(), 0, ptmpNode);
322             }
323         }
324         break;
325             
326     case MMIO_FINDPROC:
327         if ((pListNode = MMIO_FindProcNode(fccIOProc))) {
328             lpProc = pListNode->pIOProc;
329         }
330         break;
331     }
332     
333     return lpProc;
334 }
335
336 /****************************************************************
337  *                      MMIO_Map32To16                  [INTERNAL]
338  */
339 static LRESULT  MMIO_Map32To16(DWORD wMsg, LPARAM* lp1, LPARAM* lp2)
340 {
341     switch (wMsg) {
342     case MMIOM_OPEN:
343         {
344             void*       lp = SEGPTR_ALLOC(strlen((LPSTR)*lp1) + 1);
345             if (!lp) return MMSYSERR_NOMEM;
346            
347             strcpy((void*)SEGPTR_GET(lp), (LPSTR)*lp1);
348             *lp1 = (LPARAM)lp;
349         }
350         break;
351     case MMIOM_CLOSE:
352     case MMIOM_SEEK:
353         /* nothing to do */
354         break;
355     case MMIOM_READ:
356     case MMIOM_WRITE:
357     case MMIOM_WRITEFLUSH:
358         {
359             void*       lp = SEGPTR_ALLOC(*lp2);
360             if (!lp) return MMSYSERR_NOMEM;
361            
362             if (wMsg != MMIOM_READ)
363                 memcpy((void*)SEGPTR_GET(lp), (void*)*lp1, *lp2);
364             *lp1 = (LPARAM)lp;
365         }
366         break;
367     default:
368         TRACE("Not a mappable message (%ld)\n", wMsg);
369     }
370     return MMSYSERR_NOERROR;
371 }
372
373 /****************************************************************
374  *              MMIO_UnMap32To16                        [INTERNAL]
375  */
376 static LRESULT  MMIO_UnMap32To16(DWORD wMsg, LPARAM lParam1, LPARAM lParam2,
377                                  LPARAM lp1, LPARAM lp2)
378 {
379     switch (wMsg) {
380     case MMIOM_OPEN:
381         if (!SEGPTR_FREE((void*)lp1)) {
382             FIXME("bad free line=%d\n", __LINE__);
383         } 
384         break;
385     case MMIOM_CLOSE:
386     case MMIOM_SEEK:
387         /* nothing to do */
388         break;
389     case MMIOM_READ:
390         memcpy((void*)lParam1, (void*)SEGPTR_GET((void*)lp1), lp2);
391         /* fall thru */
392     case MMIOM_WRITE:
393     case MMIOM_WRITEFLUSH:
394         if (!SEGPTR_FREE((void*)lp1)) {
395             FIXME("bad free line=%d\n", __LINE__);
396         } 
397         break;
398     default:
399         TRACE("Not a mappable message (%ld)\n", wMsg);
400     }
401     return MMSYSERR_NOERROR;
402 }
403
404 /****************************************************************
405  *              MMIO_GenerateInfoForIOProc              [INTERNAL]
406  */
407 static  SEGPTR  MMIO_GenerateInfoForIOProc(const WINE_MMIO* wm)
408 {
409     SEGPTR              lp = (SEGPTR)SEGPTR_ALLOC(sizeof(MMIOINFO16));
410     LPMMIOINFO16        mmioInfo16 = (LPMMIOINFO16)SEGPTR_GET((void*)lp);
411
412     memset(mmioInfo16, 0, sizeof(MMIOINFO16));
413
414     mmioInfo16->lDiskOffset = wm->info.lDiskOffset;    
415     mmioInfo16->adwInfo[0]  = wm->info.adwInfo[0];
416     mmioInfo16->adwInfo[1]  = wm->info.adwInfo[1];
417     mmioInfo16->adwInfo[2]  = wm->info.adwInfo[2];
418     mmioInfo16->adwInfo[3]  = wm->info.adwInfo[3];
419
420     return lp;
421 }
422
423 /****************************************************************
424  *              MMIO_UpdateInfoForIOProc                [INTERNAL]
425  */
426 static  LRESULT MMIO_UpdateInfoForIOProc(WINE_MMIO* wm, SEGPTR segmmioInfo16)
427 {
428     const MMIOINFO16*   mmioInfo16;
429
430     mmioInfo16 = (const MMIOINFO16*)SEGPTR_GET((void*)segmmioInfo16);
431
432     wm->info.lDiskOffset = mmioInfo16->lDiskOffset;
433     wm->info.adwInfo[0]  = mmioInfo16->adwInfo[0];
434     wm->info.adwInfo[1]  = mmioInfo16->adwInfo[1];
435     wm->info.adwInfo[2]  = mmioInfo16->adwInfo[2];
436     wm->info.adwInfo[3]  = mmioInfo16->adwInfo[3];
437
438     if (!SEGPTR_FREE((void*)segmmioInfo16)) {
439         FIXME("bad free line=%d\n", __LINE__);
440     } 
441
442     return MMSYSERR_NOERROR;
443 }
444
445 /****************************************************************
446  *              MMIO_SendMessage                        [INTERNAL]
447  */
448 static LRESULT  MMIO_SendMessage(LPWINE_MMIO wm, DWORD wMsg, LPARAM lParam1, 
449                                  LPARAM lParam2, enum mmioProcType type)
450 {
451     LRESULT             result;
452     SEGPTR              segmmioInfo16;
453     LPARAM              lp1 = lParam1, lp2 = lParam2;
454
455     if (!wm->ioProc || !wm->info.pIOProc) {
456         ERR("brrr\n");
457         result = MMSYSERR_INVALPARAM;
458     }
459
460     switch (wm->ioProc->type) {
461     case MMIO_PROC_16:
462         segmmioInfo16 = MMIO_GenerateInfoForIOProc(wm);
463         if (wm->ioProc->type != type) {
464             /* map (lParam1, lParam2) into (lp1, lp2) 32=>16 */
465             if ((result = MMIO_Map32To16(wMsg, &lp1, &lp2)) != MMSYSERR_NOERROR)
466                 return result;
467         }
468         /* FIXME: is wm->info.pIOProc a segmented or a linear address ?
469          * sounds to me it's a segmented one, should use a thunk somewhere
470          */
471         result = ((LPMMIOPROC16)wm->info.pIOProc)((LPSTR)segmmioInfo16, 
472                                                   wMsg, lp1, lp2);
473         
474         if (wm->ioProc->type != type) {
475             MMIO_UnMap32To16(wMsg, lParam1, lParam2, lp1, lp2);
476         }
477         MMIO_UpdateInfoForIOProc(wm, segmmioInfo16);
478         break;
479     case MMIO_PROC_32A:
480     case MMIO_PROC_32W:
481         if (wm->ioProc->type != type) {
482             /* map (lParam1, lParam2) into (lp1, lp2) 16=>32 */
483             WARN("NIY\n");
484         }
485         result = (wm->info.pIOProc)((LPSTR)&wm->info, wMsg, lp1, lp2);
486         
487 #if 0
488         if (wm->ioProc->type != type) {
489             /* unmap (lParam1, lParam2) into (lp1, lp2) 16=>32 */
490         }
491 #endif
492         break;
493     default:  
494         FIXME("Internal error\n");
495         result = MMSYSERR_ERROR;
496     }
497
498     return result;
499 }
500
501 /**************************************************************************
502  *                              MMIO_ParseExt                   [internal]
503  *
504  * Parses a filename for the extension.
505  *
506  * RETURNS
507  *  The FOURCC code for the extension if found, else 0.
508  */
509 static FOURCC MMIO_ParseExt(LPCSTR szFileName)
510 {
511     /* Filenames are of the form file.ext+ABC
512        FIXME: What if a '+' is part of the file name?
513        For now, we take the last '+' present */
514     
515     FOURCC ret = 0;
516     
517     /* Note that ext{Start,End} point to the . and + respectively */
518     LPSTR extEnd;
519     
520     TRACE("(%s)\n",debugstr_a(szFileName));
521     
522     extEnd = strrchr(szFileName,'+');
523     if (extEnd) {
524         /* Need to parse to find the extension */
525         LPSTR extStart;
526         
527         extStart = extEnd;
528         while (extStart > szFileName && extStart[0] != '.') {
529             extStart--;
530         }
531         
532         if (extStart == szFileName) {
533             ERR("+ but no . in szFileName: %s\n", debugstr_a(szFileName));
534         } else {
535             CHAR ext[5];
536             
537             if (extEnd - extStart - 1 > 4)
538                 WARN("Extension length > 4\n");
539             lstrcpynA(ext,extStart + 1,min(extEnd-extStart,5));
540             TRACE("Got extension: %s\n", debugstr_a(ext));
541             /* FOURCC codes identifying file-extentions must be uppercase */
542             ret = mmioStringToFOURCCA(ext, MMIO_TOUPPER);
543         }
544     }
545     return ret;
546 }
547
548 /**************************************************************************
549  *                              MMIO_Get                        [internal]
550  *
551  * Retirieves from current process the mmio object
552  */
553 static  LPWINE_MMIO     MMIO_Get(LPWINE_MM_IDATA iData, HMMIO h)
554 {
555     LPWINE_MMIO         wm = NULL;
556
557     if (!iData) iData = MULTIMEDIA_GetIData();
558
559     EnterCriticalSection(&iData->cs);
560     for (wm = iData->lpMMIO; wm; wm = wm->lpNext) {
561         if (wm->info.hmmio == h)
562             break;
563     }
564     LeaveCriticalSection(&iData->cs);
565     return wm;
566 }
567
568 /**************************************************************************
569  *                              MMIO_Create                     [internal]
570  *
571  * Creates an internal representation for a mmio instance
572  */
573 static  LPWINE_MMIO             MMIO_Create(void)
574 {
575     static      WORD    MMIO_counter = 0;
576     LPWINE_MMIO         wm;
577     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
578
579     wm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MMIO));
580     if (wm) {
581         EnterCriticalSection(&iData->cs);
582         while (MMIO_Get(iData, ++MMIO_counter));
583         wm->info.hmmio = MMIO_counter;
584         wm->lpNext = iData->lpMMIO;
585         iData->lpMMIO = wm;
586         LeaveCriticalSection(&iData->cs);
587     }
588     return wm;
589 }
590
591 /**************************************************************************
592  *                              MMIO_Destroy                    [internal]
593  *-
594  * Destroys an internal representation for a mmio instance
595  */
596 static  BOOL            MMIO_Destroy(LPWINE_MMIO wm)
597 {
598     LPWINE_MM_IDATA     iData = MULTIMEDIA_GetIData();
599     LPWINE_MMIO*        m;
600
601     EnterCriticalSection(&iData->cs);
602     for (m = &(iData->lpMMIO); *m && *m != wm; m = &(*m)->lpNext);
603     if (*m) {
604         *m = (*m)->lpNext;
605         HeapFree(GetProcessHeap(), 0, wm);
606         wm = NULL;
607     }
608     LeaveCriticalSection(&iData->cs);
609     return wm ? FALSE : TRUE;
610 }
611
612 /****************************************************************
613  *                      MMIO_Flush                      [INTERNAL]
614  */
615 static  LRESULT MMIO_Flush(WINE_MMIO* wm, UINT uFlags)
616 {
617     if ((!wm->info.cchBuffer) || (wm->info.fccIOProc == FOURCC_MEM)) {
618         return 0;
619     }
620
621     /* not quite sure what to do here, but I'll guess */
622     if (wm->info.dwFlags & MMIO_DIRTY) {
623         MMIO_SendMessage(wm, MMIOM_SEEK, wm->info.lDiskOffset,
624                          SEEK_SET, MMIO_PROC_32A);
625         MMIO_SendMessage(wm, MMIOM_WRITE, (LPARAM)wm->info.pchBuffer,
626                          wm->info.pchNext - wm->info.pchBuffer, MMIO_PROC_32A);
627         wm->info.dwFlags &= ~MMIO_DIRTY;
628     }
629     if (uFlags & MMIO_EMPTYBUF)
630         wm->info.pchNext = wm->info.pchBuffer;
631
632     return 0;
633 }
634
635 /***************************************************************************
636  *                      MMIO_GrabNextBuffer                     [INTERNAL]
637  */
638 static LONG     MMIO_GrabNextBuffer(LPWINE_MMIO wm, int for_read)
639 {
640     LONG        size = wm->info.cchBuffer;
641
642     TRACE("bo=%lx do=%lx of=%lx\n", 
643           wm->info.lBufOffset, wm->info.lDiskOffset, 
644           MMIO_SendMessage(wm, MMIOM_SEEK, 0, SEEK_CUR, MMIO_PROC_32A));
645           
646     wm->info.lBufOffset = wm->info.lDiskOffset;
647     wm->info.pchNext = wm->info.pchBuffer;
648     wm->info.pchEndRead = wm->info.pchBuffer;
649     wm->info.pchEndWrite = wm->info.pchBuffer;
650
651     if (for_read) {
652         size = MMIO_SendMessage(wm, MMIOM_READ, (LPARAM)wm->info.pchBuffer,
653                                 size, MMIO_PROC_32A);
654         if (size > 0)
655             wm->info.pchEndRead += size;
656     }
657     wm->bBufferLoaded = TRUE;
658     return size;
659 }
660
661 /***************************************************************************
662  *                      MMIO_SetBuffer                          [INTERNAL]
663  */
664 static  UINT    MMIO_SetBuffer(WINE_MMIO* wm, void* pchBuffer, LONG cchBuffer, 
665                                UINT uFlags, BOOL bFrom32)
666 {
667     TRACE("(%p %p %ld %u %d)\n", wm, pchBuffer, cchBuffer, uFlags, bFrom32);
668
669     if (uFlags)                 return MMSYSERR_INVALPARAM;
670     if (cchBuffer > 0xFFFF) {
671         FIXME("Not handling huge mmio buffers yet (%ld >= 64k)\n", cchBuffer);
672         return MMSYSERR_INVALPARAM;
673     }
674         
675     if (MMIO_Flush(wm, MMIO_EMPTYBUF) != 0)
676         return MMIOERR_CANNOTWRITE;
677     
678     if ((!cchBuffer || pchBuffer) && (wm->info.dwFlags & MMIO_ALLOCBUF)) {
679         GlobalUnlock16(wm->hMem);
680         GlobalFree16(wm->hMem);
681         wm->info.dwFlags &= ~MMIO_ALLOCBUF;
682     }
683     if (pchBuffer) {
684         if (bFrom32) {
685             wm->info.pchBuffer = pchBuffer;
686             wm->buffer16 = 0;
687         } else {
688             wm->info.pchBuffer = PTR_SEG_TO_LIN(pchBuffer);
689             wm->buffer16 = (SEGPTR)pchBuffer;
690         }
691         wm->hMem = 0;
692     } else if (cchBuffer && (wm->info.dwFlags & MMIO_ALLOCBUF)) {
693         HGLOBAL16 hNewBuf;
694         GlobalUnlock16(wm->hMem);
695         hNewBuf = GlobalReAlloc16(wm->hMem, cchBuffer, 0);
696         if (!hNewBuf) {
697             /* FIXME: this assumes the memory block didn't move */
698             GlobalLock16(wm->hMem);
699             return MMIOERR_OUTOFMEMORY;
700         }
701         wm->hMem = hNewBuf;
702     } else if (cchBuffer) {
703         if (!(wm->hMem = GlobalAlloc16(GMEM_MOVEABLE, cchBuffer)))
704             return MMIOERR_OUTOFMEMORY;
705         wm->info.dwFlags |= MMIO_ALLOCBUF;
706     } else {
707         wm->info.pchBuffer = NULL;
708         wm->hMem = 0;
709         wm->buffer16 = 0;
710     }
711
712     if (wm->hMem) {
713         wm->buffer16 = WIN16_GlobalLock16(wm->hMem);
714         wm->info.pchBuffer = (void*)PTR_SEG_TO_LIN((void*)wm->buffer16);
715     }
716
717     wm->info.cchBuffer = cchBuffer;
718     wm->info.pchNext = wm->info.pchBuffer;
719     wm->info.pchEndRead = wm->info.pchBuffer;
720     wm->info.pchEndWrite = wm->info.pchBuffer + cchBuffer;
721     wm->info.lBufOffset = 0;
722     wm->bBufferLoaded = FALSE;
723
724     return 0;
725 }
726
727 /**************************************************************************
728  *                      MMIO_Open                               [internal]
729  */
730 static HMMIO MMIO_Open(LPSTR szFileName, MMIOINFO* refmminfo, 
731                        DWORD dwOpenFlags, enum mmioProcType type)
732 {
733     LPWINE_MMIO         wm;
734
735     TRACE("('%s', %p, %08lX, %d);\n", szFileName, refmminfo, dwOpenFlags, type);
736     
737     if (dwOpenFlags & (MMIO_PARSE|MMIO_EXIST)) {
738         char    buffer[MAX_PATH];
739         
740         if (GetFullPathNameA(szFileName, sizeof(buffer), buffer, NULL) >= sizeof(buffer))
741             return (HMMIO16)FALSE;
742         if ((dwOpenFlags & MMIO_EXIST) && (GetFileAttributesA(buffer) == -1))
743             return (HMMIO)FALSE;
744         strcpy(szFileName, buffer);
745         return (HMMIO)TRUE;
746     }
747     
748     if ((wm = MMIO_Create()) == NULL)
749         return 0;
750
751     /* If both params are NULL, then parse the file name */
752     if (refmminfo->fccIOProc == 0 && refmminfo->pIOProc == NULL) {
753         wm->info.fccIOProc = MMIO_ParseExt(szFileName);
754         /* Handle any unhandled/error case. Assume DOS file */
755         if (wm->info.fccIOProc == 0)
756             wm->info.fccIOProc = FOURCC_DOS;
757         if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;
758         wm->info.pIOProc = wm->ioProc->pIOProc;
759         wm->bTmpIOProc = FALSE;
760     }
761     /* if just the four character code is present, look up IO proc */
762     else if (refmminfo->pIOProc == NULL) {      
763         wm->info.fccIOProc = refmminfo->fccIOProc;
764         if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;
765         wm->info.pIOProc = wm->ioProc->pIOProc;
766         wm->bTmpIOProc = FALSE;
767     } 
768     /* if IO proc specified, use it and specified four character code */
769     else {
770         wm->info.fccIOProc = refmminfo->fccIOProc;
771         wm->info.pIOProc = refmminfo->pIOProc;
772         MMIO_InstallIOProc(wm->info.fccIOProc, wm->info.pIOProc, 
773                            MMIO_INSTALLPROC, type);
774         if (!(wm->ioProc = MMIO_FindProcNode(wm->info.fccIOProc))) goto error2;
775         assert(wm->ioProc->pIOProc == refmminfo->pIOProc);
776         wm->info.pIOProc = wm->ioProc->pIOProc;
777         wm->bTmpIOProc = TRUE;
778     }
779     
780     wm->bBufferLoaded = FALSE;
781     wm->ioProc->count++;
782
783     if (dwOpenFlags & MMIO_ALLOCBUF) {
784         if ((refmminfo->wErrorRet = mmioSetBuffer(wm->info.hmmio, NULL, 
785                                                   MMIO_DEFAULTBUFFER, 0)))
786             goto error1;
787     } else if (wm->info.fccIOProc == FOURCC_MEM) {
788         refmminfo->wErrorRet = MMIO_SetBuffer(wm, refmminfo->pchBuffer, 
789                                               refmminfo->cchBuffer, 0, 
790                                               type != MMIO_PROC_16);
791         if (refmminfo->wErrorRet != MMSYSERR_NOERROR)
792             goto error1;
793     } /* else => unbuffered, wm->info.pchBuffer == NULL */
794     
795     /* see mmioDosIOProc for that one */
796     wm->info.adwInfo[0] = refmminfo->adwInfo[0];
797     wm->info.dwFlags = dwOpenFlags;
798     
799     /* call IO proc to actually open file */
800     refmminfo->wErrorRet = MMIO_SendMessage(wm, MMIOM_OPEN, (LPARAM)szFileName, 
801                                             type == MMIO_PROC_16, MMIO_PROC_32A);
802
803     if (refmminfo->wErrorRet == 0)
804         return wm->info.hmmio;
805  error1:
806     if (wm->ioProc) wm->ioProc->count--;
807  error2:
808     MMIO_Destroy(wm);
809     return 0;
810 }
811
812 /**************************************************************************
813  *                              mmioOpenW                       [WINMM.123]
814  */
815 HMMIO WINAPI mmioOpenW(LPWSTR szFileName, MMIOINFO* lpmmioinfo,
816                        DWORD dwOpenFlags)
817 {
818     HMMIO       ret;
819     LPSTR       szFn = HEAP_strdupWtoA(GetProcessHeap(), 0, szFileName);
820
821     if (lpmmioinfo) {
822         ret = MMIO_Open(szFn, lpmmioinfo, dwOpenFlags, MMIO_PROC_32W);
823     } else {
824         MMIOINFO        mmioinfo;
825         
826         mmioinfo.fccIOProc = 0;
827         mmioinfo.pIOProc = NULL;
828         mmioinfo.pchBuffer = NULL;
829         mmioinfo.cchBuffer = 0;
830         
831         ret = MMIO_Open(szFn, &mmioinfo, dwOpenFlags, MMIO_PROC_32W);
832     }
833
834     HeapFree(GetProcessHeap(), 0, szFn);
835     return ret;
836 }
837
838 /**************************************************************************
839  *                              mmioOpenA                       [WINMM.122]
840  */
841 HMMIO WINAPI mmioOpenA(LPSTR szFileName, MMIOINFO* lpmmioinfo, 
842                        DWORD dwOpenFlags)
843 {
844     HMMIO       ret;
845     
846     if (lpmmioinfo) {
847         ret = MMIO_Open(szFileName, lpmmioinfo, dwOpenFlags, MMIO_PROC_32A);
848     } else {
849         MMIOINFO        mmioinfo;
850         
851         mmioinfo.fccIOProc = 0;
852         mmioinfo.pIOProc = NULL;
853         mmioinfo.pchBuffer = NULL;
854         mmioinfo.cchBuffer = 0;
855         
856         ret = MMIO_Open(szFileName, &mmioinfo, dwOpenFlags, MMIO_PROC_32A);
857     }
858     return ret;
859 }
860
861 /**************************************************************************
862  *                              mmioOpen                [MMSYSTEM.1210]
863  */
864 HMMIO16 WINAPI mmioOpen16(LPSTR szFileName, MMIOINFO16* lpmmioinfo16, 
865                           DWORD dwOpenFlags)
866 {
867     HMMIO       ret;
868     MMIOINFO    mmio;
869     
870     if (lpmmioinfo16) {
871         MMIOINFO        mmioinfo;
872        
873         memset(&mmioinfo, 0, sizeof(mmioinfo));
874
875         mmioinfo.dwFlags     = lpmmioinfo16->dwFlags; 
876         mmioinfo.fccIOProc   = lpmmioinfo16->fccIOProc; 
877         mmioinfo.pIOProc     = (LPMMIOPROC)lpmmioinfo16->pIOProc; 
878         mmioinfo.cchBuffer   = lpmmioinfo16->cchBuffer; 
879         mmioinfo.pchBuffer   = lpmmioinfo16->pchBuffer;
880         mmioinfo.adwInfo[0]  = lpmmioinfo16->adwInfo[0]; 
881         mmioinfo.adwInfo[1]  = lpmmioinfo16->adwInfo[1]; 
882         mmioinfo.adwInfo[2]  = lpmmioinfo16->adwInfo[2]; 
883         mmioinfo.adwInfo[3]  = lpmmioinfo16->adwInfo[3]; 
884         
885         ret = MMIO_Open(szFileName, &mmioinfo, dwOpenFlags, MMIO_PROC_16);
886
887         mmioGetInfo16(mmioinfo.hmmio, lpmmioinfo16, 0);
888         lpmmioinfo16->wErrorRet = ret;
889     } else {
890         mmio.fccIOProc = 0;
891         mmio.pIOProc = NULL;
892         mmio.pchBuffer = NULL;
893         mmio.cchBuffer = 0;
894         ret = MMIO_Open(szFileName, &mmio, dwOpenFlags, FALSE);
895     }
896     return ret;
897 }
898
899     
900 /**************************************************************************
901  *                              mmioClose               [WINMM.114]
902  */
903 MMRESULT WINAPI mmioClose(HMMIO hmmio, UINT uFlags)
904 {
905     LPWINE_MMIO wm;
906     MMRESULT    result;
907     
908     TRACE("(%04X, %04X);\n", hmmio, uFlags);
909     
910     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
911         return MMSYSERR_INVALHANDLE;
912     
913     if ((result = MMIO_Flush(wm, MMIO_EMPTYBUF)) != 0)
914         return result;
915     
916     result = MMIO_SendMessage(wm, MMIOM_CLOSE, uFlags, 0, MMIO_PROC_32A);
917     
918     mmioSetBuffer(hmmio, NULL, 0, 0);
919     
920     wm->ioProc->count--;
921
922     if (wm->bTmpIOProc)
923         MMIO_InstallIOProc(wm->info.fccIOProc, NULL, 
924                            MMIO_REMOVEPROC, wm->ioProc->type);
925
926     MMIO_Destroy(wm);
927
928     return result;
929 }
930
931 /**************************************************************************
932  *                              mmioClose               [MMSYSTEM.1211]
933  */
934 MMRESULT16 WINAPI mmioClose16(HMMIO16 hmmio, UINT16 uFlags)
935 {
936     return mmioClose(hmmio, uFlags);
937 }
938
939 /**************************************************************************
940  *                              mmioRead                [WINMM.124]
941  */
942 LONG WINAPI mmioRead(HMMIO hmmio, HPSTR pch, LONG cch)
943 {
944     LPWINE_MMIO wm;
945     LONG        count;
946     
947     TRACE("(%04X, %p, %ld);\n", hmmio, pch, cch);
948     
949     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
950         return -1;
951
952     /* unbuffered case first */
953     if (!wm->info.pchBuffer)
954         return MMIO_SendMessage(wm, MMIOM_READ, (LPARAM)pch, cch, MMIO_PROC_32A);
955
956     /* first try from current buffer */
957     if (wm->info.pchNext != wm->info.pchEndRead) {
958         count = wm->info.pchEndRead - wm->info.pchNext;
959         if (count > cch || count < 0) count = cch;
960         memcpy(pch, wm->info.pchNext, count);
961         wm->info.pchNext += count;
962         pch += count;
963         cch -= count;
964     } else
965         count = 0;
966     
967     if (cch && (wm->info.fccIOProc != FOURCC_MEM)) {
968         assert(wm->info.cchBuffer);
969
970         while (cch) {
971             LONG size;
972
973             size = MMIO_GrabNextBuffer(wm, TRUE);
974             if (size <= 0) break;
975             if (size > cch) size = cch;
976             memcpy(pch, wm->info.pchBuffer, size);
977             wm->info.pchNext += size;
978             pch += size;
979             cch -= size;
980             count += size;
981         }
982     }
983     
984     TRACE("count=%ld\n", count);
985     return count;
986 }
987
988 /**************************************************************************
989  *                              mmioRead                [MMSYSTEM.1212]
990  */
991 LONG WINAPI mmioRead16(HMMIO16 hmmio, HPSTR pch, LONG cch)
992 {
993     return mmioRead(hmmio, pch, cch);
994 }
995
996 /**************************************************************************
997  *                              mmioWrite               [WINMM.133]
998  */
999 LONG WINAPI mmioWrite(HMMIO hmmio, HPCSTR pch, LONG cch)
1000 {
1001     LPWINE_MMIO wm;
1002     LONG count,bytesW=0;
1003
1004     TRACE("(%04X, %p, %ld);\n", hmmio, pch, cch);
1005
1006     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1007         return -1;
1008     
1009     if (wm->info.cchBuffer) {
1010         count = 0;
1011         while (cch) {
1012             if (wm->info.pchNext != wm->info.pchEndWrite) {
1013                 count = wm->info.pchEndWrite - wm->info.pchNext;
1014                 if (count > cch || count < 0) count = cch;
1015                 memcpy(wm->info.pchNext, pch, count);
1016                 wm->info.pchNext += count;
1017                 pch += count;
1018                 cch -= count;
1019                 bytesW+=count;                
1020                 wm->info.dwFlags |= MMIO_DIRTY;
1021             } else
1022                 if (wm->info.fccIOProc == FOURCC_MEM) {
1023                     if (wm->info.adwInfo[0]) {
1024                         /* from where would we get the memory handle? */
1025                         FIXME("memory file expansion not implemented!\n");
1026                         break;
1027                     } else break;
1028                 }
1029
1030             if (wm->info.pchNext == wm->info.pchEndWrite) MMIO_Flush(wm, MMIO_EMPTYBUF);
1031             else break;
1032         }
1033     } else {
1034         bytesW = MMIO_SendMessage(wm, MMIOM_WRITE, (LPARAM)pch, cch, MMIO_PROC_32A);
1035         wm->info.lBufOffset = wm->info.lDiskOffset;
1036     }
1037     
1038     TRACE("bytes written=%ld\n", bytesW);
1039     return bytesW;
1040 }
1041
1042 /**************************************************************************
1043  *                              mmioWrite               [MMSYSTEM.1213]
1044  */
1045 LONG WINAPI mmioWrite16(HMMIO16 hmmio, HPCSTR pch, LONG cch)
1046 {
1047     return mmioWrite(hmmio,pch,cch);
1048 }
1049
1050 /**************************************************************************
1051  *                              mmioSeek                [MMSYSTEM.1214]
1052  */
1053 LONG WINAPI mmioSeek(HMMIO hmmio, LONG lOffset, INT iOrigin)
1054 {
1055     LPWINE_MMIO wm;
1056     LONG        offset;
1057
1058     TRACE("(%04X, %08lX, %d);\n", hmmio, lOffset, iOrigin);
1059     
1060     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1061         return MMSYSERR_INVALHANDLE;
1062
1063     /* not buffered, direct seek on file */
1064     if (!wm->info.pchBuffer)
1065         return MMIO_SendMessage(wm, MMIOM_SEEK, lOffset, iOrigin, MMIO_PROC_32A);
1066
1067     switch (iOrigin) {
1068     case SEEK_SET: 
1069         offset = lOffset;
1070         break;
1071     case SEEK_CUR: 
1072         offset = wm->info.lBufOffset + (wm->info.pchNext - wm->info.pchBuffer) + lOffset;
1073         break;
1074     case SEEK_END: 
1075         if (wm->info.fccIOProc == FOURCC_MEM) {
1076             offset = wm->info.cchBuffer;
1077         } else {
1078             assert(MMIO_SendMessage(wm, MMIOM_SEEK, 0, SEEK_CUR, MMIO_PROC_32A) == wm->info.lDiskOffset);
1079             offset = MMIO_SendMessage(wm, MMIOM_SEEK, 0, SEEK_END, MMIO_PROC_32A);
1080             MMIO_SendMessage(wm, MMIOM_SEEK, wm->info.lDiskOffset, SEEK_SET, MMIO_PROC_32A);
1081         }
1082         offset -= lOffset;
1083         break;
1084     default:
1085         return -1;
1086     }
1087
1088     /* stay in same buffer ? */
1089     /* some memory mapped buffers are defined with -1 as a size */
1090     if ((wm->info.cchBuffer > 0) &&
1091         ((offset < wm->info.lBufOffset) ||
1092          (offset >= wm->info.lBufOffset + wm->info.cchBuffer) ||
1093          !wm->bBufferLoaded)) {
1094
1095         /* condition to change buffer */
1096         if ((wm->info.fccIOProc == FOURCC_MEM) || 
1097             MMIO_Flush(wm, MMIO_EMPTYBUF) ||
1098             /* this also sets the wm->info.lDiskOffset field */
1099             MMIO_SendMessage(wm, MMIOM_SEEK, 
1100                              (offset / wm->info.cchBuffer) * wm->info.cchBuffer,
1101                              SEEK_SET, MMIO_PROC_32A) == -1)
1102             return -1;
1103         MMIO_GrabNextBuffer(wm, TRUE);
1104     }
1105
1106     wm->info.pchNext = wm->info.pchBuffer + (offset - wm->info.lBufOffset);
1107
1108     TRACE("=> %ld\n", offset);
1109     return offset;
1110 }
1111
1112 /**************************************************************************
1113  *                              mmioSeek                [MMSYSTEM.1214]
1114  */
1115 LONG WINAPI mmioSeek16(HMMIO16 hmmio, LONG lOffset, INT16 iOrigin)
1116 {
1117     return mmioSeek(hmmio, lOffset, iOrigin);
1118 }
1119
1120 /**************************************************************************
1121  *                              mmioGetInfo             [MMSYSTEM.1215]
1122  */
1123 UINT16 WINAPI mmioGetInfo16(HMMIO16 hmmio, MMIOINFO16* lpmmioinfo, UINT16 uFlags)
1124 {
1125     LPWINE_MMIO wm;
1126
1127     TRACE("mmioGetInfo\n");
1128
1129     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1130         return MMSYSERR_INVALHANDLE;
1131
1132     if (!wm->buffer16)
1133         return MMSYSERR_ERROR;
1134
1135     lpmmioinfo->dwFlags     = wm->info.dwFlags; 
1136     lpmmioinfo->fccIOProc   = wm->info.fccIOProc; 
1137     lpmmioinfo->pIOProc     = (LPMMIOPROC16)wm->info.pIOProc; 
1138     lpmmioinfo->wErrorRet   = wm->info.wErrorRet; 
1139     lpmmioinfo->hTask       = wm->info.hTask; 
1140     lpmmioinfo->cchBuffer   = wm->info.cchBuffer; 
1141     lpmmioinfo->pchBuffer   = (void*)wm->buffer16;
1142     lpmmioinfo->pchNext     = (void*)(wm->buffer16 + (wm->info.pchNext - wm->info.pchBuffer));
1143     lpmmioinfo->pchEndRead  = (void*)(wm->buffer16 + (wm->info.pchEndRead - wm->info.pchBuffer));
1144     lpmmioinfo->pchEndWrite = (void*)(wm->buffer16 + (wm->info.pchEndWrite - wm->info.pchBuffer)); 
1145     lpmmioinfo->lBufOffset  = wm->info.lBufOffset; 
1146     lpmmioinfo->lDiskOffset = wm->info.lDiskOffset; 
1147     lpmmioinfo->adwInfo[0]  = wm->info.adwInfo[0]; 
1148     lpmmioinfo->adwInfo[1]  = wm->info.adwInfo[1]; 
1149     lpmmioinfo->adwInfo[2]  = wm->info.adwInfo[2]; 
1150     lpmmioinfo->adwInfo[3]  = wm->info.adwInfo[3]; 
1151     lpmmioinfo->dwReserved1 = 0;
1152     lpmmioinfo->dwReserved2 = 0;
1153     lpmmioinfo->hmmio = wm->info.hmmio; 
1154
1155     return 0;
1156 }
1157
1158 /**************************************************************************
1159  *                              mmioGetInfo             [WINMM.118]
1160  */
1161 UINT WINAPI mmioGetInfo(HMMIO hmmio, MMIOINFO* lpmmioinfo, UINT uFlags)
1162 {
1163     LPWINE_MMIO         wm;
1164     
1165     TRACE("(0x%04x,%p,0x%08x)\n",hmmio,lpmmioinfo,uFlags);
1166
1167     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1168         return MMSYSERR_INVALHANDLE;
1169
1170     memcpy(lpmmioinfo, &wm->info, sizeof(MMIOINFO));
1171
1172     return 0;
1173 }
1174
1175 /**************************************************************************
1176  *                              mmioSetInfo16           [MMSYSTEM.1216]
1177  */
1178 UINT16 WINAPI mmioSetInfo16(HMMIO16 hmmio, const MMIOINFO16* lpmmioinfo, UINT16 uFlags)
1179 {
1180     LPWINE_MMIO         wm;
1181
1182     TRACE("mmioSetInfo\n");
1183
1184     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1185         return MMSYSERR_INVALHANDLE;
1186
1187     /* check if seg and lin buffers are the same */
1188     if (wm->info.cchBuffer != lpmmioinfo->cchBuffer ||
1189         wm->info.pchBuffer != PTR_SEG_TO_LIN((void*)wm->buffer16))
1190         return MMSYSERR_INVALPARAM;
1191         
1192     /* check pointers coherence */
1193     if (lpmmioinfo->pchNext < lpmmioinfo->pchBuffer || 
1194         lpmmioinfo->pchNext > lpmmioinfo->pchBuffer + lpmmioinfo->cchBuffer ||
1195         lpmmioinfo->pchEndRead < lpmmioinfo->pchBuffer || 
1196         lpmmioinfo->pchEndRead > lpmmioinfo->pchBuffer + lpmmioinfo->cchBuffer ||
1197         lpmmioinfo->pchEndWrite < lpmmioinfo->pchBuffer || 
1198         lpmmioinfo->pchEndWrite > lpmmioinfo->pchBuffer + lpmmioinfo->cchBuffer)
1199         return MMSYSERR_INVALPARAM;
1200
1201     wm->info.pchNext     = wm->info.pchBuffer + (lpmmioinfo->pchNext     - lpmmioinfo->pchBuffer);
1202     wm->info.pchEndRead  = wm->info.pchBuffer + (lpmmioinfo->pchEndRead  - lpmmioinfo->pchBuffer);
1203     wm->info.pchEndWrite = wm->info.pchBuffer + (lpmmioinfo->pchEndWrite - lpmmioinfo->pchBuffer);
1204
1205     return 0;
1206 }
1207
1208 /**************************************************************************
1209  *                              mmioSetInfo             [WINMM.130]
1210  */
1211 UINT WINAPI mmioSetInfo(HMMIO hmmio, const MMIOINFO* lpmmioinfo, UINT uFlags)
1212 {
1213     LPWINE_MMIO         wm;
1214
1215     TRACE("mmioSetInfo\n");
1216
1217     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1218         return MMSYSERR_INVALHANDLE;
1219     
1220     /* check pointers coherence */
1221     if (lpmmioinfo->pchNext < wm->info.pchBuffer || 
1222         lpmmioinfo->pchNext > wm->info.pchBuffer + wm->info.cchBuffer ||
1223         lpmmioinfo->pchEndRead < wm->info.pchBuffer || 
1224         lpmmioinfo->pchEndRead > wm->info.pchBuffer + wm->info.cchBuffer ||
1225         lpmmioinfo->pchEndWrite < wm->info.pchBuffer || 
1226         lpmmioinfo->pchEndWrite > wm->info.pchBuffer + wm->info.cchBuffer)
1227         return MMSYSERR_INVALPARAM;
1228
1229     wm->info.pchNext = lpmmioinfo->pchNext;
1230     wm->info.pchEndRead = lpmmioinfo->pchEndRead;
1231
1232     return 0;
1233 }
1234
1235 /**************************************************************************
1236 *                               mmioSetBuffer           [WINMM.129]
1237 */
1238 UINT WINAPI mmioSetBuffer(HMMIO hmmio, LPSTR pchBuffer, LONG cchBuffer, UINT uFlags)
1239 {
1240     LPWINE_MMIO         wm;
1241
1242     TRACE("(hmmio=%04x, pchBuf=%p, cchBuf=%ld, uFlags=%#08x)\n",
1243           hmmio, pchBuffer, cchBuffer, uFlags);
1244     
1245     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1246         return MMSYSERR_INVALHANDLE;
1247
1248     return MMIO_SetBuffer(wm, pchBuffer, cchBuffer, uFlags, TRUE);
1249 }
1250
1251 /**************************************************************************
1252  *                              mmioSetBuffer           [MMSYSTEM.1217]
1253  */
1254 UINT16 WINAPI mmioSetBuffer16(HMMIO16 hmmio, LPSTR segpchBuffer, 
1255                               LONG cchBuffer, UINT16 uFlags)
1256 {
1257     LPWINE_MMIO         wm;
1258
1259     TRACE("(hmmio=%04x, segpchBuf=%p, cchBuf=%ld, uFlags=%#08x)\n",
1260           hmmio, segpchBuffer, cchBuffer, uFlags);
1261     
1262     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1263         return MMSYSERR_INVALHANDLE;
1264
1265     return MMIO_SetBuffer(wm, segpchBuffer, cchBuffer, uFlags, FALSE);
1266 }
1267
1268 /**************************************************************************
1269  *                              mmioFlush               [WINMM.117]
1270  */
1271 UINT WINAPI mmioFlush(HMMIO hmmio, UINT uFlags)
1272 {
1273     LPWINE_MMIO         wm;
1274
1275     TRACE("(%04X, %04X)\n", hmmio, uFlags);
1276
1277     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1278         return MMSYSERR_INVALHANDLE;
1279        
1280     return MMIO_Flush(wm, uFlags);
1281 }
1282
1283 /**************************************************************************
1284  *                              mmioFlush               [MMSYSTEM.1218]
1285  */
1286 UINT16 WINAPI mmioFlush16(HMMIO16 hmmio, UINT16 uFlags)
1287 {
1288     return mmioFlush(hmmio, uFlags);
1289 }
1290
1291 /**************************************************************************
1292  *                              mmioAdvance             [MMSYSTEM.1219]
1293  */
1294 UINT WINAPI mmioAdvance(HMMIO hmmio, MMIOINFO* lpmmioinfo, UINT uFlags)
1295 {
1296     LPWINE_MMIO         wm;
1297     
1298     TRACE("hmmio=%04X, lpmmioinfo=%p, uFlags=%04X\n", hmmio, lpmmioinfo, uFlags);
1299
1300     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1301         return MMSYSERR_INVALHANDLE;
1302
1303     if (!wm->info.cchBuffer)
1304         return MMIOERR_UNBUFFERED;
1305
1306     if (uFlags != MMIO_READ && uFlags != MMIO_WRITE)
1307         return MMSYSERR_INVALPARAM;
1308
1309     if (MMIO_Flush(wm, MMIO_EMPTYBUF))
1310         return MMIOERR_CANNOTWRITE;
1311
1312     MMIO_GrabNextBuffer(wm, uFlags == MMIO_READ);
1313
1314     lpmmioinfo->pchNext = lpmmioinfo->pchBuffer;
1315     lpmmioinfo->pchEndRead  = lpmmioinfo->pchBuffer + 
1316         (wm->info.pchEndRead - wm->info.pchBuffer);
1317     lpmmioinfo->pchEndWrite = lpmmioinfo->pchBuffer + 
1318         (wm->info.pchEndWrite - wm->info.pchBuffer);
1319     lpmmioinfo->lDiskOffset = wm->info.lDiskOffset;
1320     lpmmioinfo->lBufOffset = wm->info.lBufOffset;
1321     return 0;
1322 }
1323
1324 /***********************************************************************
1325  *                              mmioAdvance             [MMSYSTEM.1219]
1326  */
1327 UINT16 WINAPI mmioAdvance16(HMMIO16 hmmio, MMIOINFO16* lpmmioinfo, UINT16 uFlags)
1328 {
1329     LPWINE_MMIO         wm;
1330
1331     TRACE("mmioAdvance\n");
1332
1333     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1334         return MMSYSERR_INVALHANDLE;
1335
1336     if (!wm->info.cchBuffer)
1337         return MMIOERR_UNBUFFERED;
1338
1339     if (uFlags != MMIO_READ && uFlags != MMIO_WRITE)
1340         return MMSYSERR_INVALPARAM;
1341
1342     if (MMIO_Flush(wm, MMIO_EMPTYBUF))
1343         return MMIOERR_CANNOTWRITE;
1344
1345     MMIO_GrabNextBuffer(wm, uFlags == MMIO_READ);
1346         
1347     lpmmioinfo->pchNext = lpmmioinfo->pchBuffer;
1348     lpmmioinfo->pchEndRead  = lpmmioinfo->pchBuffer + 
1349         (wm->info.pchEndRead - wm->info.pchBuffer);
1350     lpmmioinfo->pchEndWrite = lpmmioinfo->pchBuffer + 
1351         (wm->info.pchEndWrite - wm->info.pchBuffer);
1352     lpmmioinfo->lDiskOffset = wm->info.lDiskOffset;
1353     lpmmioinfo->lBufOffset = wm->info.lBufOffset;
1354
1355     return 0;
1356 }
1357
1358 /**************************************************************************
1359  *                              mmioStringToFOURCCA     [WINMM.131]
1360  */
1361 FOURCC WINAPI mmioStringToFOURCCA(LPCSTR sz, UINT uFlags)
1362 {
1363     CHAR cc[4];
1364     int i = 0;
1365     
1366     for (i = 0; i < 4 && sz[i]; i++) {
1367         if (uFlags & MMIO_TOUPPER) {
1368             cc[i] = toupper(sz[i]);
1369         } else {
1370             cc[i] = sz[i];
1371         }
1372     }
1373     
1374     /* Pad with spaces */
1375     while (i < 4) {
1376         cc[i] = ' ';
1377         i++;
1378     }
1379     
1380     TRACE("Got %c%c%c%c\n",cc[0],cc[1],cc[2],cc[3]);
1381     return mmioFOURCC(cc[0],cc[1],cc[2],cc[3]);
1382 }
1383
1384 /**************************************************************************
1385  *                              mmioStringToFOURCCW     [WINMM.132]
1386  */
1387 FOURCC WINAPI mmioStringToFOURCCW(LPCWSTR sz, UINT uFlags)
1388 {
1389     LPSTR       szA = HEAP_strdupWtoA(GetProcessHeap(),0,sz);
1390     FOURCC      ret = mmioStringToFOURCCA(szA,uFlags);
1391     
1392     HeapFree(GetProcessHeap(),0,szA);
1393     return ret;
1394 }
1395
1396 /**************************************************************************
1397  *                              mmioStringToFOURCC      [MMSYSTEM.1220]
1398  */
1399 FOURCC WINAPI mmioStringToFOURCC16(LPCSTR sz, UINT16 uFlags)
1400 {
1401     return mmioStringToFOURCCA(sz, uFlags);
1402 }
1403
1404 /**************************************************************************
1405  *              mmioInstallIOProc16    [MMSYSTEM.1221]
1406  */
1407 LPMMIOPROC16 WINAPI mmioInstallIOProc16(FOURCC fccIOProc, LPMMIOPROC16 pIOProc,
1408                                         DWORD dwFlags)
1409 {
1410     return (LPMMIOPROC16)MMIO_InstallIOProc(fccIOProc, (LPMMIOPROC)pIOProc, 
1411                                             dwFlags, MMIO_PROC_16); 
1412 }
1413
1414 /**************************************************************************
1415  *                              mmioInstallIOProcA         [WINMM.120]
1416  */
1417 LPMMIOPROC WINAPI mmioInstallIOProcA(FOURCC fccIOProc, 
1418                                      LPMMIOPROC pIOProc, DWORD dwFlags)
1419 {
1420     return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, MMIO_PROC_32A);
1421 }
1422
1423 /**************************************************************************
1424  *                              mmioInstallIOProcW         [WINMM.]
1425  */
1426 LPMMIOPROC WINAPI mmioInstallIOProcW(FOURCC fccIOProc, 
1427                                      LPMMIOPROC pIOProc, DWORD dwFlags)
1428 {
1429     return MMIO_InstallIOProc(fccIOProc, pIOProc, dwFlags, MMIO_PROC_32W);
1430 }
1431
1432 /**************************************************************************
1433  *                              mmioSendMessage16       [MMSYSTEM.1222]
1434  */
1435 LRESULT WINAPI mmioSendMessage16(HMMIO16 hmmio, UINT16 uMessage,
1436                                  LPARAM lParam1, LPARAM lParam2)
1437 {
1438     LPWINE_MMIO         wm;
1439     
1440     TRACE("(%04X, %u, %ld, %ld)\n", hmmio, uMessage, lParam1, lParam2);
1441
1442     if (uMessage < MMIOM_USER)
1443         return MMSYSERR_INVALPARAM;
1444     
1445     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1446         return MMSYSERR_INVALHANDLE;
1447     
1448     return MMIO_SendMessage(wm, uMessage, lParam1, lParam2, MMIO_PROC_16);
1449 }
1450
1451 /**************************************************************************
1452  *                              mmioSendMessage         [WINMM.]
1453  */
1454 LRESULT WINAPI mmioSendMessage(HMMIO hmmio, UINT uMessage,
1455                                LPARAM lParam1, LPARAM lParam2)
1456 {
1457     LPWINE_MMIO         wm;
1458     
1459     TRACE("(%04X, %u, %ld, %ld)\n", hmmio, uMessage, lParam1, lParam2);
1460
1461     if (uMessage < MMIOM_USER)
1462         return MMSYSERR_INVALPARAM;
1463     
1464     if ((wm = MMIO_Get(NULL, hmmio)) == NULL)
1465         return MMSYSERR_INVALHANDLE;
1466     
1467     return MMIO_SendMessage(wm, uMessage, lParam1, lParam2, MMIO_PROC_32A);
1468 }
1469
1470 /**************************************************************************
1471  *                              mmioDescend             [MMSYSTEM.1223]
1472  */
1473 UINT WINAPI mmioDescend(HMMIO hmmio, LPMMCKINFO lpck,
1474                         const MMCKINFO* lpckParent, UINT uFlags)
1475 {
1476     DWORD               dwOldPos;
1477     FOURCC              srchCkId;
1478     FOURCC              srchType;
1479     
1480     
1481     TRACE("(%04X, %p, %p, %04X);\n", hmmio, lpck, lpckParent, uFlags);
1482     
1483     if (lpck == NULL)
1484         return MMSYSERR_INVALPARAM;
1485     
1486     dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
1487     TRACE("dwOldPos=%ld\n", dwOldPos);
1488     
1489     if (lpckParent != NULL) {
1490         TRACE("seek inside parent at %ld !\n", lpckParent->dwDataOffset);
1491         /* EPP: was dwOldPos = mmioSeek(hmmio,lpckParent->dwDataOffset,SEEK_SET); */
1492         if (dwOldPos < lpckParent->dwDataOffset || 
1493             dwOldPos >= lpckParent->dwDataOffset + lpckParent->cksize) {
1494             WARN("outside parent chunk\n");
1495             return MMIOERR_CHUNKNOTFOUND;
1496         }
1497     }
1498     
1499     /* The SDK docu says 'ckid' is used for all cases. Real World
1500      * examples disagree -Marcus,990216. 
1501      */
1502     
1503     srchType = 0;
1504     /* find_chunk looks for 'ckid' */
1505     if (uFlags & MMIO_FINDCHUNK)
1506         srchCkId = lpck->ckid;
1507     /* find_riff and find_list look for 'fccType' */
1508     if (uFlags & MMIO_FINDLIST) {
1509         srchCkId = FOURCC_LIST;
1510         srchType = lpck->fccType;
1511     }
1512     if (uFlags & MMIO_FINDRIFF) {
1513         srchCkId = FOURCC_RIFF;
1514         srchType = lpck->fccType;
1515     }
1516     
1517     if (uFlags & (MMIO_FINDCHUNK|MMIO_FINDLIST|MMIO_FINDRIFF)) {
1518         TRACE("searching for %.4s.%.4s\n", 
1519               (LPSTR)&srchCkId,
1520               srchType?(LPSTR)&srchType:"any ");
1521         
1522         while (TRUE) {
1523             LONG ix;
1524             
1525             ix = mmioRead(hmmio, (LPSTR)lpck, 3 * sizeof(DWORD));
1526             if (ix < 2*sizeof(DWORD)) {
1527                 mmioSeek(hmmio, dwOldPos, SEEK_SET);
1528                 WARN("return ChunkNotFound\n");
1529                 return MMIOERR_CHUNKNOTFOUND;
1530             }
1531             lpck->dwDataOffset = dwOldPos + 2 * sizeof(DWORD);
1532             if (ix < lpck->dwDataOffset - dwOldPos) {
1533                 mmioSeek(hmmio, dwOldPos, SEEK_SET);
1534                 WARN("return ChunkNotFound\n");
1535                 return MMIOERR_CHUNKNOTFOUND;
1536             }
1537             TRACE("ckid=%.4s fcc=%.4s cksize=%08lX !\n",
1538                   (LPSTR)&lpck->ckid, 
1539                   srchType?(LPSTR)&lpck->fccType:"<na>",
1540                   lpck->cksize);
1541             if ((srchCkId == lpck->ckid) &&
1542                 (!srchType || (srchType == lpck->fccType))
1543                 )
1544                 break;
1545             
1546             dwOldPos = lpck->dwDataOffset + ((lpck->cksize + 1) & ~1);
1547             mmioSeek(hmmio, dwOldPos, SEEK_SET);
1548         }
1549     } else {
1550         /* FIXME: unverified, does it do this? */
1551         /* NB: This part is used by WAVE_mciOpen, among others */
1552         if (mmioRead(hmmio, (LPSTR)lpck, 3 * sizeof(DWORD)) < 3 * sizeof(DWORD)) {
1553             mmioSeek(hmmio, dwOldPos, SEEK_SET);
1554             WARN("return ChunkNotFound 2nd\n");
1555             return MMIOERR_CHUNKNOTFOUND;
1556         }
1557         lpck->dwDataOffset = dwOldPos + 2 * sizeof(DWORD);
1558     }
1559     lpck->dwFlags = 0;
1560     /* If we were looking for RIFF/LIST chunks, the final file position
1561      * is after the chunkid. If we were just looking for the chunk
1562      * it is after the cksize. So add 4 in RIFF/LIST case.
1563      */
1564     if (lpck->ckid == FOURCC_RIFF || lpck->ckid == FOURCC_LIST)
1565         mmioSeek(hmmio, lpck->dwDataOffset + sizeof(DWORD), SEEK_SET);
1566     else
1567         mmioSeek(hmmio, lpck->dwDataOffset, SEEK_SET);
1568     TRACE("lpck: ckid=%.4s, cksize=%ld, dwDataOffset=%ld fccType=%08lX (%.4s)!\n", 
1569           (LPSTR)&lpck->ckid, lpck->cksize, lpck->dwDataOffset, 
1570           lpck->fccType, srchType?(LPSTR)&lpck->fccType:"");
1571     return 0;
1572 }
1573
1574 /**************************************************************************
1575  *                              mmioDescend16           [MMSYSTEM.1223]
1576  */
1577 UINT16 WINAPI mmioDescend16(HMMIO16 hmmio, LPMMCKINFO lpck,
1578                             const MMCKINFO* lpckParent, UINT16 uFlags)
1579 {
1580     return mmioDescend(hmmio, lpck, lpckParent, uFlags);
1581 }
1582
1583 /**************************************************************************
1584  *                              mmioAscend              [WINMM.113]
1585  */
1586 UINT WINAPI mmioAscend(HMMIO hmmio, LPMMCKINFO lpck, UINT uFlags)
1587 {
1588     TRACE("(%04X, %p, %04X);\n", hmmio, lpck, uFlags);
1589     
1590     if (lpck->dwFlags & MMIO_DIRTY) {
1591         DWORD   dwOldPos, dwNewSize, dwSizePos;
1592         
1593         TRACE("chunk is marked MMIO_DIRTY, correcting chunk size\n");
1594         dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
1595         TRACE("dwOldPos=%ld\n", dwOldPos);
1596         dwNewSize = dwOldPos - lpck->dwDataOffset;
1597         if (dwNewSize != lpck->cksize) {
1598             TRACE("dwNewSize=%ld\n", dwNewSize);
1599             lpck->cksize = dwNewSize;
1600             
1601             dwSizePos = lpck->dwDataOffset - sizeof(DWORD);
1602             TRACE("dwSizePos=%ld\n", dwSizePos);
1603             
1604             mmioSeek(hmmio, dwSizePos, SEEK_SET);
1605             mmioWrite(hmmio, (LPSTR)&dwNewSize, sizeof(DWORD));
1606         }
1607     }
1608     
1609     mmioSeek(hmmio, lpck->dwDataOffset + ((lpck->cksize + 1) & ~1), SEEK_SET);
1610     
1611     return 0;
1612 }
1613
1614 /**************************************************************************
1615  *                              mmioAscend              [MMSYSTEM.1224]
1616  */
1617 UINT16 WINAPI mmioAscend16(HMMIO16 hmmio, MMCKINFO* lpck, UINT16 uFlags)
1618 {
1619     return mmioAscend(hmmio,lpck,uFlags);
1620 }
1621
1622 /**************************************************************************
1623  *                              mmioCreateChunk         [MMSYSTEM.1225]
1624  */
1625 UINT16 WINAPI mmioCreateChunk16(HMMIO16 hmmio, MMCKINFO* lpck, UINT16 uFlags)
1626 {
1627     DWORD       dwOldPos;
1628     LONG        size;
1629     LONG        ix;
1630     
1631     TRACE("(%04X, %p, %04X);\n", hmmio, lpck, uFlags);
1632     
1633     dwOldPos = mmioSeek(hmmio, 0, SEEK_CUR);
1634     TRACE("dwOldPos=%ld\n", dwOldPos);
1635     
1636     if (uFlags == MMIO_CREATELIST)
1637         lpck->ckid = FOURCC_LIST;
1638     else if (uFlags == MMIO_CREATERIFF)
1639         lpck->ckid = FOURCC_RIFF;
1640     
1641     TRACE("ckid=%08lX\n", lpck->ckid);
1642     
1643     size = 2 * sizeof(DWORD);
1644     lpck->dwDataOffset = dwOldPos + size;
1645     
1646     if (lpck->ckid == FOURCC_RIFF || lpck->ckid == FOURCC_LIST) 
1647         size += sizeof(DWORD);
1648     lpck->dwFlags = MMIO_DIRTY;
1649     
1650     ix = mmioWrite(hmmio, (LPSTR)lpck, size);
1651     TRACE("after mmioWrite ix = %ld req = %ld, errno = %d\n",ix, size, errno);
1652     if (ix < size) {
1653         mmioSeek(hmmio, dwOldPos, SEEK_SET);
1654         WARN("return CannotWrite\n");
1655         return MMIOERR_CANNOTWRITE;
1656     }
1657     
1658     return 0;
1659 }
1660
1661 /**************************************************************************
1662  *                      mmioCreateChunk                         [WINMM.115]
1663  */
1664 UINT WINAPI mmioCreateChunk(HMMIO hmmio, MMCKINFO* lpck, UINT uFlags)
1665 {
1666     return mmioCreateChunk16(hmmio, lpck, uFlags);
1667 }
1668
1669 /**************************************************************************
1670  *                              mmioRename              [MMSYSTEM.1226]
1671  */
1672 UINT16 WINAPI mmioRename16(LPCSTR szFileName, LPCSTR szNewFileName,
1673                            MMIOINFO16* lpmmioinfo, DWORD dwRenameFlags)
1674 {
1675     UINT16              result = MMSYSERR_ERROR;
1676     LPMMIOPROC16        ioProc;
1677
1678     TRACE("('%s', '%s', %p, %08lX);\n",
1679           szFileName, szNewFileName, lpmmioinfo, dwRenameFlags);
1680     
1681     /* If both params are NULL, then parse the file name */
1682     if (lpmmioinfo && lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL)
1683         lpmmioinfo->fccIOProc = MMIO_ParseExt(szFileName);
1684
1685     /* Handle any unhandled/error case from above. Assume DOS file */
1686     if (!lpmmioinfo || (lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL))
1687         ioProc = (LPMMIOPROC16)mmioDosIOProc;
1688     /* if just the four character code is present, look up IO proc */
1689     else if (lpmmioinfo->pIOProc == NULL)
1690         ioProc = mmioInstallIOProc16(lpmmioinfo->fccIOProc, NULL, MMIO_FINDPROC);
1691     else
1692         ioProc = lpmmioinfo->pIOProc;
1693
1694     /* FIXME: ioProc is likely a segmented address, thus needing a
1695      * thunk somewhere. The main issue is that Wine's current thunking
1696      * 32 to 16 only supports pascal calling convention
1697      */
1698     if (ioProc) 
1699         result = (ioProc)(0, MMIOM_RENAME, 
1700                           (LPARAM)szFileName, (LPARAM)szNewFileName);
1701     
1702     return result;
1703 }
1704
1705 /**************************************************************************
1706  *                              mmioRenameA                     [WINMM.125]
1707  */
1708 UINT WINAPI mmioRenameA(LPCSTR szFileName, LPCSTR szNewFileName,
1709                         MMIOINFO* lpmmioinfo, DWORD dwRenameFlags)
1710 {
1711     UINT        result = MMSYSERR_ERROR;
1712     LPMMIOPROC  ioProc;
1713
1714     TRACE("('%s', '%s', %p, %08lX);\n",
1715           szFileName, szNewFileName, lpmmioinfo, dwRenameFlags);
1716
1717     /* If both params are NULL, then parse the file name */
1718     if (lpmmioinfo && lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL)
1719         lpmmioinfo->fccIOProc = MMIO_ParseExt(szFileName);
1720
1721     /* Handle any unhandled/error case from above. Assume DOS file */
1722     if (!lpmmioinfo || (lpmmioinfo->fccIOProc == 0 && lpmmioinfo->pIOProc == NULL))
1723         ioProc = (LPMMIOPROC)mmioDosIOProc;
1724     /* if just the four character code is present, look up IO proc */
1725     else if (lpmmioinfo->pIOProc == NULL)
1726         ioProc = MMIO_InstallIOProc(lpmmioinfo->fccIOProc, NULL, 
1727                                     MMIO_FINDPROC, MMIO_PROC_32A);
1728     else /* use relevant ioProc */
1729         ioProc = lpmmioinfo->pIOProc;
1730
1731     if (ioProc) 
1732         result = (ioProc)(0, MMIOM_RENAME, 
1733                           (LPARAM)szFileName, (LPARAM)szNewFileName);
1734     
1735     return result;
1736 }
1737
1738 /**************************************************************************
1739  *                              mmioRenameW                     [WINMM.126]
1740  */
1741 UINT WINAPI mmioRenameW(LPCWSTR szFileName, LPCWSTR szNewFileName,
1742                         MMIOINFO* lpmmioinfo, DWORD dwRenameFlags)
1743 {
1744     LPSTR       szFn = HEAP_strdupWtoA(GetProcessHeap(), 0, szFileName);
1745     LPSTR       sznFn = HEAP_strdupWtoA(GetProcessHeap(), 0, szNewFileName);
1746     UINT        ret = mmioRenameA(szFn, sznFn, lpmmioinfo, dwRenameFlags);
1747     
1748     HeapFree(GetProcessHeap(),0,szFn);
1749     HeapFree(GetProcessHeap(),0,sznFn);
1750     return ret;
1751 }