Release 940706

Sun, 3 Jul 1994 20:15:56 +0100 (BST)  David Metcalfe <david@prism.demon.co.uk>

	* [controls/edit.c]
	Bug fixes and tidying up.  Preliminary tab stop support
	(doesn't work yet).

	* [windows/dialog.c]
	Reversed order of buttons in CheckRadioButtons so that all
	buttons are now displayed.

Tue Jul  5 18:30:24 1994  Alexandre Julliard  (julliard@lamisun.epfl.ch)

	* [include/options.h] [misc/main.c] [windows/win.c]
	Removed nosaveunders option, replaced by handling
	the CS_SAVEBITS flag.

	* [windows/class.c]
	Modified the fix for negative size in class extra bytes to
	avoid modifying the caller's data.

	* [windows/dc.c]
	Bug fix: system font must be a proportional font.
	Fixed a bug that caused the default pen to not be selected
	correctly in a DC.

	* [windows/graphics.c]
	Bug fix in GRAPH_DrawArc(). Thanks to Adriano Azevedo for
	noticing it.

	* [windows/painting.c]
	Removed incorrect selecting of default objects in BeginPaint()
	(no longer needed because of the fix in dc.c).

Jul 4, 94 martin2@trgcorp.solucorp.qc.ca (Martin Ayotte)

	* [misc/mmsystem.c]
	* [misc/audio.c]
	Add more code to interface '/dev/dsp'.

	* New file [misc/mcicda.c]
	Create an MCI_DEVTYPE_CD_AUDIO driver connected to '/dev/sbpcd'.

	* New file [misc/mmaux.c]
	Stubs to make a future driver connected to '/dev/mixer'.

	* [windows/win.c]
	Temporary patch to CreateWindowEx() for reseting negative
	coordinates to 0,0 ; because 'soundrec.exe' give negative values
	and I need it to work on MMSYSTEM ... :-)

	* [miscemu/int2f.c]
	add a stub 'do_int2f_16' (function 0x16) for DMPI server.

Mon Jun 20 10:08:40 BST 1994  William Smith (wos@dcs.warwick.ac.uk)

	* include/comm.h
	New file -- some definitions that were in comm.c now need to
	be shared with misc/dos_fs.c

	* misc/comm.c
	Some definitions moved into include/comm.h

	* misc/dos_fs.c (DOS_GetEquipment):
	Fixed error in equipment -- bitwise or of two values should
	be used instead of logical or.  Also added code to correctly
	report the number of serial and parallel devices.
diff --git a/ChangeLog b/ChangeLog
index 0e61684..adcba41 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,72 @@
 ----------------------------------------------------------------------
+Sun, 3 Jul 1994 20:15:56 +0100 (BST)  David Metcalfe <david@prism.demon.co.uk>
+
+	* [controls/edit.c]
+	Bug fixes and tidying up.  Preliminary tab stop support
+	(doesn't work yet).
+
+	* [windows/dialog.c]
+	Reversed order of buttons in CheckRadioButtons so that all
+	buttons are now displayed.
+
+Tue Jul  5 18:30:24 1994  Alexandre Julliard  (julliard@lamisun.epfl.ch)
+
+	* [include/options.h] [misc/main.c] [windows/win.c]
+	Removed nosaveunders option, replaced by handling
+	the CS_SAVEBITS flag.
+
+	* [windows/class.c]
+	Modified the fix for negative size in class extra bytes to
+	avoid modifying the caller's data.
+
+	* [windows/dc.c]
+	Bug fix: system font must be a proportional font.
+	Fixed a bug that caused the default pen to not be selected
+	correctly in a DC.
+
+	* [windows/graphics.c]
+	Bug fix in GRAPH_DrawArc(). Thanks to Adriano Azevedo for
+	noticing it.
+
+	* [windows/painting.c]
+	Removed incorrect selecting of default objects in BeginPaint()
+	(no longer needed because of the fix in dc.c).
+
+Jul 4, 94 martin2@trgcorp.solucorp.qc.ca (Martin Ayotte)
+
+	* [misc/mmsystem.c]
+	* [misc/audio.c]
+	Add more code to interface '/dev/dsp'.
+
+	* New file [misc/mcicda.c]
+	Create an MCI_DEVTYPE_CD_AUDIO driver connected to '/dev/sbpcd'.
+
+	* New file [misc/mmaux.c]
+	Stubs to make a future driver connected to '/dev/mixer'.
+
+	* [windows/win.c]
+	Temporary patch to CreateWindowEx() for reseting negative
+	coordinates to 0,0 ; because 'soundrec.exe' give negative values
+	and I need it to work on MMSYSTEM ... :-)
+
+	* [miscemu/int2f.c]
+	add a stub 'do_int2f_16' (function 0x16) for DMPI server.
+
+Mon Jun 20 10:08:40 BST 1994  William Smith (wos@dcs.warwick.ac.uk)
+
+	* include/comm.h
+	New file -- some definitions that were in comm.c now need to
+	be shared with misc/dos_fs.c
+
+	* misc/comm.c
+	Some definitions moved into include/comm.h
+
+	* misc/dos_fs.c (DOS_GetEquipment):
+	Fixed error in equipment -- bitwise or of two values should
+	be used instead of logical or.  Also added code to correctly
+	report the number of serial and parallel devices.
+
+----------------------------------------------------------------------
 Mon Jun 20 14:26:41 1994  Bob Amstadt  (bob@pooh)
 
 	* [objects/bitmap.c]
diff --git a/controls/edit.c b/controls/edit.c
index 3aff8f8..2a1c12c 100644
--- a/controls/edit.c
+++ b/controls/edit.c
@@ -67,6 +67,8 @@
     int DeletedLength;       /* length of deleted text */
     int DeletedCurrLine;     /* starting line from which text was deleted */
     int DeletedCurrCol;      /* starting col from which text was deleted */
+    int NumTabStops;         /* number of tab stops in buffer hTabStops */
+    HANDLE hTabStops;        /* handle of tab stops buffer */
 } EDITSTATE;
 
 
@@ -79,6 +81,9 @@
 #define CurrChar (EDIT_TextLine(hwnd, es->CurrLine) + es->CurrCol)
 #define SelMarked(es) (es->SelBegLine != 0 || es->SelBegCol != 0 || \
 		       es->SelEndLine != 0 || es->SelEndCol != 0)
+#define ROUNDUP(numer, denom) ((numer % denom) \
+			       ? (((numer + denom) / denom) * denom) \
+			       : numer)
 
 /* macros to access window styles */
 #define IsAutoVScroll() (wndPtr->dwStyle & ES_AUTOVSCROLL)
@@ -104,14 +109,16 @@
 void EDIT_PaintMsg(HWND hwnd);
 HANDLE EDIT_GetTextLine(HWND hwnd, int selection);
 char *EDIT_TextLine(HWND hwnd, int sel);
-int EDIT_LineLength(EDITSTATE *es, char *str, int len);
+int EDIT_StrLength(EDITSTATE *es, char *str, int len, int pcol);
+int EDIT_LineLength(HWND hwnd, int num);
 void EDIT_WriteTextLine(HWND hwnd, RECT *rc, int y);
 void EDIT_WriteText(HWND hwnd, char *lp, int off, int len, int row, 
 		    int col, RECT *rc, BOOL blank, BOOL reverse);
 HANDLE EDIT_GetStr(EDITSTATE *es, char *lp, int off, int len, int *diff);
 void EDIT_CharMsg(HWND hwnd, WORD wParam);
 void EDIT_KeyTyped(HWND hwnd, short ch);
-int EDIT_CharWidth(EDITSTATE *es, short ch);
+int EDIT_CharWidth(EDITSTATE *es, short ch, int pcol);
+int EDIT_GetNextTabStop(EDITSTATE *es, int pcol);
 void EDIT_Forward(HWND hwnd);
 void EDIT_Downward(HWND hwnd);
 void EDIT_Upward(HWND hwnd);
@@ -162,6 +169,7 @@
 void *EDIT_TextAddr(EDITSTATE *es, unsigned int handle);
 unsigned int EDIT_TextReAlloc(EDITSTATE *es, unsigned int handle, int bytes);
 void EDIT_SetHandleMsg(HWND hwnd, WORD wParam);
+LONG EDIT_SetTabStopsMsg(HWND hwnd, WORD wParam, LONG lParam);
 void swap(int *a, int *b);
 
 
@@ -295,7 +303,7 @@
 	break;
 
     case EM_SETTABSTOPS:
-	printf("edit: cannot process EM_SETTABSTOPS message\n");
+	lResult = EDIT_SetTabStopsMsg(hwnd, wParam, lParam);
 	break;
 
     case EM_SETWORDBREAKPROC:
@@ -451,7 +459,7 @@
     /* --- text buffer */
     es->localheap = &HEAP_LocalFindHeap(createStruct->hInstance)->free_list;
     es->MaxTextLen = MAXTEXTLEN + 1;
-    if (!(es->hText))
+    if (!(createStruct->lpszName))
     {
 	es->textlen = EditBufLen(wndPtr) + 1;
 	es->hText = EDIT_TextAlloc(es, EditBufLen(wndPtr) + 2);
@@ -464,15 +472,18 @@
 	if (strlen(createStruct->lpszName) < EditBufLen(wndPtr))
 	{
 	    es->hText = EDIT_TextAlloc(es, EditBufLen(wndPtr) + 2);
-	    es->textlen = EditBufLen(wndPtr) + 1;
+	    text = EDIT_TextAddr(es, es->hText);
+	    strcpy(text, createStruct->lpszName);
 	    *(text + es->textlen) = '\0';
+	    es->textlen = EditBufLen(wndPtr) + 1;
 	}
 	else
 	{
 	    es->hText = EDIT_TextAlloc(es, strlen(createStruct->lpszName) + 2);
+	    text = EDIT_TextAddr(es, es->hText);
+	    strcpy(text, createStruct->lpszName);
 	    es->textlen = strlen(createStruct->lpszName) + 1;
 	}
-	text = EDIT_TextAddr(es, es->hText);
 	*(text + es->textlen + 1) = '\0';
 	EDIT_BuildTextPointers(hwnd);
     }
@@ -524,6 +535,8 @@
     es->hFont = 0;
     es->hDeletedText = 0;
     es->DeletedLength = 0;
+    es->NumTabStops = 0;
+    es->hTabStops = EDIT_HEAP_ALLOC(sizeof(int));
 
     /* allocate space for a line full of blanks to speed up */
     /* line filling */
@@ -608,14 +621,15 @@
 	/* advance through current line */
 	while (*cp && *cp != '\n')
 	{
-	    len += charWidths[*cp];          /* width of line in pixels */
+	    len += EDIT_CharWidth(es, *cp, len); /* width of line in pixels */
 	    cp++;
 	}
 	es->textwidth = max(es->textwidth, len);
 	if (*cp)
 	    cp++;                            /* skip '\n' */
     }
-    off = (unsigned int)(cp - text);     /* offset of beginning of line */
+
+    off = (unsigned int)(cp - text);
     *(textPtrs + es->wlines) = off;
 }
 
@@ -722,27 +736,46 @@
     
 
 /*********************************************************************
- *  EDIT_LineLength
+ *  EDIT_StrLength
  *
- *  Return length of line _str_ of length _len_ characters in pixels.
+ *  Return length of string _str_ of length _len_ characters in pixels.
+ *  The current column offset in pixels _pcol_ is required to calculate 
+ *  the width of a tab.
  */
 
-int EDIT_LineLength(EDITSTATE *es, char *str, int len)
+int EDIT_StrLength(EDITSTATE *es, char *str, int len, int pcol)
 {
     int i, plen = 0;
-    short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
 
     for (i = 0; i < len; i++)
-	plen += charWidths[*(str + i)];
+	plen += EDIT_CharWidth(es, *(str + i), pcol + plen);
 
 #ifdef DEBUG_EDIT
-    printf("EDIT_LineLength: returning %d\n", plen);
+    printf("EDIT_StrLength: returning %d\n", plen);
 #endif
     return plen;
 }
 
 
 /*********************************************************************
+ *  EDIT_LineLength
+ *
+ *  Return length of line _num_ in characters.
+ */
+
+int EDIT_LineLength(HWND hwnd, int num)
+{
+    WND *wndPtr = WIN_FindWndPtr(hwnd);
+    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
+    char *cp = EDIT_TextLine(hwnd, num);
+    char *cp1;
+
+    cp1 = strchr(cp, '\n');
+    return cp1 ? (int)(cp1 - cp) : strlen(cp);
+}
+
+
+/*********************************************************************
  *  EDIT_WriteTextLine
  *
  *  Write the line of text at offset _y_ in text buffer to a window.
@@ -812,7 +845,7 @@
     if ((hLine = EDIT_GetTextLine(hwnd, y)) == 0)
 	return;
     lp = (unsigned char *)EDIT_HEAP_ADDR(hLine);
-    lnlen = EDIT_LineLength(es, lp, strlen(lp));
+    lnlen = EDIT_StrLength(es, lp, strlen(lp), 0);
     lnlen1 = lnlen;
 
     /* build the line to display */
@@ -852,7 +885,7 @@
 			   TRUE, TRUE);
 	else if (y == sbl)
 	{
-	    col = EDIT_LineLength(es, lp, sbc);
+	    col = EDIT_StrLength(es, lp, sbc, 0);
 	    if (col > (es->wleft + rc.left))
 	    {
 		len = min(col - off, rc.right - off);
@@ -862,7 +895,7 @@
 	    }
 	    if (y == sel)
 	    {
-		col = EDIT_LineLength(es, lp, sec);
+		col = EDIT_StrLength(es, lp, sec, 0);
 		if (col < (es->wleft + rc.right))
 		{
 		    len = min(col - off, rc.right - off);
@@ -890,7 +923,7 @@
 	}
 	else if (y == sel)
 	{
-	    col = EDIT_LineLength(es, lp, sec);
+	    col = EDIT_StrLength(es, lp, sec, 0);
 	    if (col < (es->wleft + rc.right))
 	    {
 		len = min(col - off, rc.right - off);
@@ -930,14 +963,15 @@
 {
     HDC hdc;
     HANDLE hStr;
-    char *str, *blanks;
-    int diff, num_spaces;
+    char *str, *cp, *cp1;
+    int diff, num_spaces, tabwidth, scol;
     HRGN hrgnClip;
     COLORREF oldTextColor, oldBkgdColor;
     HFONT oldfont;
     WND *wndPtr = WIN_FindWndPtr(hwnd);
     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
     short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
+    char *blanks = (char *)EDIT_HEAP_ADDR(es->hBlankLine);
 
 #ifdef DEBUG_EDIT
     printf("EDIT_WriteText lp=%s, off=%d, len=%d, row=%d, col=%d, reverse=%d\n", lp, off, len, row, col, reverse);
@@ -963,7 +997,31 @@
 	SetTextColor(hdc, oldBkgdColor);
     }
 
-    TextOut(hdc, col - diff, row * es->txtht, str, strlen(str));
+    if (!(cp = strchr(str, VK_TAB)))
+	TextOut(hdc, col - diff, row * es->txtht, str, strlen(str));
+    else
+    {
+	TextOut(hdc, col - diff, row * es->txtht, str, (int)(cp - str));
+	scol = EDIT_StrLength(es, str, (int)(cp - str), 0);
+	tabwidth = EDIT_CharWidth(es, VK_TAB, scol);
+	num_spaces = tabwidth / charWidths[32] + 1;
+	TextOut(hdc, scol, row * es->txtht, blanks, num_spaces);
+	cp++;
+	scol += tabwidth;
+
+	while (cp1 = strchr(cp, VK_TAB))
+	{
+	    TextOut(hdc, scol, row * es->txtht, cp, (int)(cp1 - cp));
+	    scol = EDIT_StrLength(es, cp, (int)(cp1 - cp), scol);
+	    tabwidth = EDIT_CharWidth(es, VK_TAB, scol);
+	    num_spaces = tabwidth / charWidths[32] + 1;
+	    TextOut(hdc, scol, row * es->txtht, blanks, num_spaces);
+	    cp = ++cp1;
+	    scol += tabwidth;
+	}
+
+	TextOut(hdc, scol, row * es->txtht, cp, strlen(cp));
+    }
 
     if (reverse)
     {
@@ -976,7 +1034,6 @@
     {
 	if ((rc->right - col) > len)
 	{
-	    blanks = EDIT_HEAP_ADDR(es->hBlankLine);
 	    num_spaces = (rc->right - col - len) / charWidths[32];
 	    TextOut(hdc, col + len, row * es->txtht, blanks, num_spaces);
 	}
@@ -1003,9 +1060,8 @@
 {
     HANDLE hStr;
     char *str;
-    int ch = 0, i = 0, j, tmp;
+    int ch = 0, i = 0, j, s_i;
     int ch1;
-    short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
 
 #ifdef DEBUG_EDIT
     printf("EDIT_GetStr %s %d %d\n", lp, off, len);
@@ -1013,19 +1069,23 @@
 
     while (i < off)
     {
-	i += charWidths[*(lp + ch)];
+	s_i = i;
+	i += EDIT_CharWidth(es, *(lp + ch), i);
 	ch++;
     }
 
     /* if stepped past _off_, go back a character */
     if (i - off)
-	i -= charWidths[*(lp + --ch)];
+    {
+	i = s_i;
+	ch--;
+    }
     *diff = off - i;
     ch1 = ch;
 
     while (i < len + off)
     {
-	i += charWidths[*(lp + ch)];
+	i += EDIT_CharWidth(es, *(lp + ch), i);
 	ch++;
     }
     
@@ -1102,7 +1162,7 @@
 	return;
     }
 
-    if (*currchar == '\0')
+    if (*currchar == '\0' && IsMultiLine())
     {
 	/* insert a newline at end of text */
 	*currchar = '\n';
@@ -1145,13 +1205,13 @@
     if (IsMultiLine() && es->wlines > 1)
     {
 	es->textwidth = max(es->textwidth,
-		    EDIT_LineLength(es, EDIT_TextLine(hwnd, es->CurrLine),
+		    EDIT_StrLength(es, EDIT_TextLine(hwnd, es->CurrLine),
 		    (int)(EDIT_TextLine(hwnd, es->CurrLine + 1) -
-			  EDIT_TextLine(hwnd, es->CurrLine))));
+			  EDIT_TextLine(hwnd, es->CurrLine)), 0));
     }
     else
 	es->textwidth = max(es->textwidth,
-			    EDIT_LineLength(es, text, strlen(text)));
+			    EDIT_StrLength(es, text, strlen(text), 0));
     EDIT_WriteTextLine(hwnd, NULL, es->wtop + es->WndRow);
 
     if (ch == '\n')
@@ -1177,14 +1237,15 @@
     }
 
     /* test end of window */
-    if (es->WndCol >= ClientWidth(wndPtr) - EDIT_CharWidth(es, ch))
+    if (es->WndCol >= ClientWidth(wndPtr) - 
+	                    EDIT_CharWidth(es, ch, es->WndCol + es->wleft))
     {
 	/* TODO:- Word wrap to be handled here */
 
 /*	if (!(currchar == text + es->MaxTextLen - 2)) */
 	    EDIT_KeyHScroll(hwnd, SB_LINEDOWN);
     }
-    es->WndCol += EDIT_CharWidth(es, ch);
+    es->WndCol += EDIT_CharWidth(es, ch, es->WndCol + es->wleft);
     es->CurrCol++;
     SetCaretPos(es->WndCol, es->WndRow * es->txtht);
     ShowCaret(hwnd);
@@ -1196,13 +1257,46 @@
  *  EDIT_CharWidth
  *
  *  Return the width of the given character in pixels.
+ *  The current column offset in pixels _pcol_ is required to calculate 
+ *  the width of a tab.
  */
 
-int EDIT_CharWidth(EDITSTATE *es, short ch)
+int EDIT_CharWidth(EDITSTATE *es, short ch, int pcol)
 {
     short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
 
-    return (charWidths[ch]);
+    if (ch != VK_TAB)
+	return (charWidths[ch]);
+    else
+	return (EDIT_GetNextTabStop(es, pcol) - pcol);
+}
+
+
+/*********************************************************************
+ *  EDIT_GetNextTabStop
+ *
+ *  Return the next tab stop beyond _pcol_.
+ */
+
+int EDIT_GetNextTabStop(EDITSTATE *es, int pcol)
+{
+    int i;
+    int baseUnitWidth = LOWORD(GetDialogBaseUnits());
+    unsigned short *tabstops = EDIT_HEAP_ADDR(es->hTabStops);
+
+    if (es->NumTabStops == 0)
+	return ROUNDUP(pcol, 8);
+    else if (es->NumTabStops == 1)
+	return ROUNDUP(pcol, *tabstops * baseUnitWidth / 4);
+    else
+    {
+	for (i = 0; i < es->NumTabStops; i++)
+	{
+	    if (*(tabstops + i) * baseUnitWidth / 4 >= pcol)
+		return (*(tabstops + i) * baseUnitWidth / 4);
+	}
+	return pcol;
+    }
 }
 
 
@@ -1217,9 +1311,8 @@
     WND *wndPtr = WIN_FindWndPtr(hwnd);
     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
     char *text = EDIT_TextAddr(es, es->hText);
-    char *cc = CurrChar + 1;
 
-    if (*cc == '\0')
+    if (*CurrChar == '\0')
 	return;
 
     if (*CurrChar == '\n')
@@ -1229,7 +1322,7 @@
     }
     else
     {
-	es->WndCol += EDIT_CharWidth(es, *CurrChar);
+	es->WndCol += EDIT_CharWidth(es, *CurrChar, es->WndCol + es->wleft);
 	es->CurrCol++;
 	if (es->WndCol >= ClientWidth(wndPtr))
 	    EDIT_KeyHScroll(hwnd, SB_LINEDOWN);
@@ -1309,7 +1402,7 @@
     if (es->CurrCol)
     {
 	--es->CurrCol;
-	es->WndCol -= EDIT_CharWidth(es, *CurrChar);
+	es->WndCol -= EDIT_CharWidth(es, *CurrChar, es->WndCol + es->wleft);
 	if (es->WndCol < 0)
 	    EDIT_KeyHScroll(hwnd, SB_LINEUP);
     }
@@ -1336,7 +1429,7 @@
 
     while (*CurrChar && *CurrChar != '\n')
     {
-	es->WndCol += EDIT_CharWidth(es, *CurrChar);
+	es->WndCol += EDIT_CharWidth(es, *CurrChar, es->WndCol + es->wleft);
 	es->CurrCol++;
     }
 
@@ -1382,15 +1475,13 @@
 {
     WND *wndPtr = WIN_FindWndPtr(hwnd);
     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
+    int len = EDIT_LineLength(hwnd, es->CurrLine);
+    char *cp = EDIT_TextLine(hwnd, es->CurrLine);
     char currpel;
 
-    char *cp = EDIT_TextLine(hwnd, es->CurrLine);
-    char *cp1 = strchr(cp, '\n');
-    int len = cp1 ? (int)(cp1 - cp) : 0;
-
     es->CurrCol = min(len, es->CurrCol);
-    es->WndCol = min(EDIT_LineLength(es, cp, len) - es->wleft, es->WndCol);
-    currpel = EDIT_LineLength(es, cp, es->CurrCol);
+    es->WndCol = min(EDIT_StrLength(es, cp, len, 0) - es->wleft, es->WndCol);
+    currpel = EDIT_StrLength(es, cp, es->CurrCol, 0);
 
     if (es->wleft > currpel)
     {
@@ -1848,7 +1939,6 @@
 	if (es->wtop + ClientHeight(wndPtr, es) >= es->wlines)
 	    return;
 	es->wtop++;
-	printf("Scroll line down: wtop=%d\n", es->wtop);
     }
     else
     {
@@ -1856,7 +1946,6 @@
 	if (es->wtop == 0)
 	    return;
 	--es->wtop;
-	printf("Scroll line up: wtop=%d\n", es->wtop);
     }
 
     if (IsWindowVisible(hwnd))
@@ -1974,7 +2063,7 @@
 
 void EDIT_LButtonDownMsg(HWND hwnd, WORD wParam, LONG lParam)
 {
-    char *cp, *cp1;
+    char *cp;
     int len;
     BOOL end = FALSE;
     WND *wndPtr = WIN_FindWndPtr(hwnd);
@@ -1995,12 +2084,10 @@
     es->CurrLine = es->wtop + es->WndRow;
 
     cp = EDIT_TextLine(hwnd, es->CurrLine);
-    cp1 = strchr(cp, '\n');
-    len = cp1 ? (int)(cp1 - cp) : 0;
-
+    len = EDIT_LineLength(hwnd, es->CurrLine);
     es->WndCol = LOWORD(lParam);
-    if (es->WndCol > EDIT_LineLength(es, cp, len) - es->wleft || end)
-	es->WndCol = EDIT_LineLength(es, cp, len) - es->wleft;
+    if (es->WndCol > EDIT_StrLength(es, cp, len, 0) - es->wleft || end)
+	es->WndCol = EDIT_StrLength(es, cp, len, 0) - es->wleft;
     es->CurrCol = EDIT_PixelToChar(hwnd, es->CurrLine, &(es->WndCol));
 
     ButtonDown = TRUE;
@@ -2040,11 +2127,10 @@
 
 int EDIT_PixelToChar(HWND hwnd, int row, int *pixel)
 {
-    int ch = 0, i = 0;
+    int ch = 0, i = 0, s_i;
     char *text;
     WND *wndPtr = WIN_FindWndPtr(hwnd);
     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
-    short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
 
 #ifdef DEBUG_EDIT
     printf("EDIT_PixelToChar: row=%d, pixel=%d\n", row, *pixel);
@@ -2053,13 +2139,17 @@
     text = EDIT_TextLine(hwnd, row);
     while (i < *pixel)
     {
-	i += charWidths[*(text + ch)];
+	s_i = i;
+	i += EDIT_CharWidth(es, *(text + ch), i);
 	ch++;
     }
 
     /* if stepped past _pixel_, go back a character */
     if (i - *pixel)
-	i -= charWidths[*(text + ch)];
+    {
+	i = s_i;
+	--ch;
+    }
     *pixel = i;
     return ch;
 }
@@ -2154,8 +2244,8 @@
 	    es->wtop = es->SelEndLine;
 	    es->WndRow = 0;
 	}
-	es->WndCol = EDIT_LineLength(es, EDIT_TextLine(hwnd, es->SelEndLine), 
-				     es->SelEndCol) - es->wleft;
+	es->WndCol = EDIT_StrLength(es, EDIT_TextLine(hwnd, es->SelEndLine), 
+				     es->SelEndCol, 0) - es->wleft;
     }
     InvalidateRect(hwnd, NULL, TRUE);
     UpdateWindow(hwnd);
@@ -2202,8 +2292,10 @@
     }
     *line = lineno - 1;
     *col = off - (int)(cp1 - text);
+#if 0
     if (*(text + *col) == '\0')
 	(*col)--;
+#endif
 }
 
 
@@ -2238,8 +2330,8 @@
 	    es->wtop = es->SelBegLine;
 	    es->WndRow = 0;
 	}
-	es->WndCol = EDIT_LineLength(es, bbl - es->SelBegCol, 
-				     es->SelBegCol) - es->wleft;
+	es->WndCol = EDIT_StrLength(es, bbl - es->SelBegCol, 
+				     es->SelBegCol, 0) - es->wleft;
 
 	EDIT_BuildTextPointers(hwnd);
 	es->PaintBkgd = TRUE;
@@ -2329,7 +2421,7 @@
 void EDIT_ExtendSel(HWND hwnd, int x, int y)
 {
     int bbl, bel, bbc, bec;
-    char *cp, *cp1;
+    char *cp;
     int len;
     BOOL end = FALSE;
     WND *wndPtr = WIN_FindWndPtr(hwnd);
@@ -2342,8 +2434,7 @@
     bbl = es->SelEndLine;
     bbc = es->SelEndCol;
     cp = EDIT_TextLine(hwnd, es->wtop + y / es->txtht);
-    cp1 = strchr(cp, '\n');
-    len = cp1 ? (int)(cp1 - cp) : 0;
+    len = EDIT_LineLength(hwnd, es->wtop + y / es->txtht);
 
     es->WndRow = y / es->txtht;
     if (es->WndRow > es->wlines - es->wtop - 1)
@@ -2358,8 +2449,8 @@
     es->SelEndLine = es->CurrLine;
 
     es->WndCol = x;
-    if (es->WndCol > EDIT_LineLength(es, cp, len) - es->wleft || end)
-	es->WndCol = EDIT_LineLength(es, cp, len) - es->wleft;
+    if (es->WndCol > EDIT_StrLength(es, cp, len, 0) - es->wleft || end)
+	es->WndCol = EDIT_StrLength(es, cp, len, 0) - es->wleft;
     es->CurrCol = EDIT_PixelToChar(hwnd, es->CurrLine, &(es->WndCol));
     es->SelEndCol = es->CurrCol - 1;
 
@@ -2404,7 +2495,7 @@
 {
     RECT rc;
     int scol, ecol;
-    char *cp, *cp1;
+    char *cp;
     HDC hdc;
     HBRUSH hbrush, holdbrush;
     int olddm;
@@ -2425,15 +2516,12 @@
 
     /* get length of line if end == -1 */
     if (end == -1)
-    {
-	cp1 = strchr(cp, '\n');
-	end = cp1 ? (int)(cp1 - cp) : 0;
-    }
+	end = EDIT_LineLength(hwnd, y);
 
-    scol = EDIT_LineLength(es, cp, start);
+    scol = EDIT_StrLength(es, cp, start, 0);
     if (scol > rc.right) return;
     if (scol < rc.left) scol = rc.left;
-    ecol = EDIT_LineLength(es, cp, end);
+    ecol = EDIT_StrLength(es, cp, end, 0);
     if (ecol < rc.left) return;
     if (ecol > rc.right) ecol = rc.right;
 
@@ -2551,8 +2639,8 @@
     EDIT_GetLineCol(hwnd, (int)((CurrChar + len) - text), &(es->CurrLine),
 		                                    &(es->CurrCol));
     es->WndRow = es->CurrLine - es->wtop;
-    es->WndCol = EDIT_LineLength(es, EDIT_TextLine(hwnd, es->CurrLine),
-				 es->CurrCol) - es->wleft;
+    es->WndCol = EDIT_StrLength(es, EDIT_TextLine(hwnd, es->CurrLine),
+				 es->CurrCol, 0) - es->wleft;
 }
 
 
@@ -2669,8 +2757,8 @@
     ReleaseDC(hwnd, hdc);
 
     es->WndRow = (es->CurrLine - es->wtop) / es->txtht;
-    es->WndCol = EDIT_LineLength(es, EDIT_TextLine(hwnd, es->CurrLine),
-				 es->CurrCol) - es->wleft;
+    es->WndCol = EDIT_StrLength(es, EDIT_TextLine(hwnd, es->CurrLine),
+				 es->CurrCol, 0) - es->wleft;
 
     InvalidateRect(hwnd, NULL, TRUE);
     es->PaintBkgd = TRUE;
@@ -2741,8 +2829,8 @@
 	EDIT_GetLineCol(hwnd, (int)((CurrChar + es->DeletedLength) - text), 
 			&(es->CurrLine), &(es->CurrCol));
 	es->WndRow = es->CurrLine - es->wtop;
-	es->WndCol = EDIT_LineLength(es, EDIT_TextLine(hwnd, es->CurrLine),
-				     es->CurrCol) - es->wleft;
+	es->WndCol = EDIT_StrLength(es, EDIT_TextLine(hwnd, es->CurrLine),
+				     es->CurrCol, 0) - es->wleft;
 	es->SelEndLine = es->CurrLine;
 	es->SelEndCol = es->CurrCol;
 
@@ -2826,6 +2914,35 @@
 
 
 /*********************************************************************
+ *  EM_SETTABSTOPS message function
+ */
+
+LONG EDIT_SetTabStopsMsg(HWND hwnd, WORD wParam, LONG lParam)
+{
+    unsigned short *tabstops;
+    WND *wndPtr = WIN_FindWndPtr(hwnd);
+    EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
+
+    es->NumTabStops = wParam;
+    if (wParam == 0)
+	es->hTabStops = EDIT_HEAP_REALLOC(es->hTabStops, 1);
+    else if (wParam == 1)
+    {
+	es->hTabStops = EDIT_HEAP_REALLOC(es->hTabStops, 1);
+	tabstops = (unsigned short *)EDIT_HEAP_ADDR(es->hTabStops);
+	*tabstops = (unsigned short)lParam;
+    }
+    else
+    {
+	es->hTabStops = EDIT_HEAP_REALLOC(es->hTabStops, wParam);
+	tabstops = (unsigned short *)EDIT_HEAP_ADDR(es->hTabStops);
+	memcpy(tabstops, (unsigned short *)lParam, wParam);
+    }
+    return 0L;
+}
+
+
+/*********************************************************************
  *  Utility functions
  */
 
diff --git a/controls/listbox.c b/controls/listbox.c
index 196cb9b..8b7af56 100644
--- a/controls/listbox.c
+++ b/controls/listbox.c
@@ -1151,7 +1151,7 @@
 		{
 			if (DOS_ValidDrive(x))
 			{
-				sprintf(temp, "[-%c-]", 'A'+x);
+				sprintf(temp, "[-%c-]", 'a'+x);
 				if ( (wRet = ListBoxAddString(hwnd, temp)) == LB_ERR)
 	    				break;
 			}		
diff --git a/include/comm.h b/include/comm.h
new file mode 100644
index 0000000..7f74685
--- /dev/null
+++ b/include/comm.h
@@ -0,0 +1,21 @@
+/*
+ * Communications header
+ *
+ * 93 Erik Bos (erik@trashcan.hacktic.nl)
+ */
+
+#ifndef COMM_H
+#define COMM_H
+
+
+#define MAX_PORTS   16
+
+struct DosDeviceStruct {
+    char *devicename;   /* /dev/cua1 */
+    int fd;
+    int suspended;
+    int unget;
+    int unget_byte;
+};
+
+#endif  /* COMM_H */
diff --git a/include/mmsystem.h b/include/mmsystem.h
index de39b93..feb2eda 100644
--- a/include/mmsystem.h
+++ b/include/mmsystem.h
@@ -8,6 +8,11 @@
 typedef LPSTR		    HPSTR;          /* a huge version of LPSTR */
 typedef LPCSTR			HPCSTR;         /* a huge version of LPCSTR */
 
+#define MAXWAVEDRIVERS 10
+#define MAXMIDIDRIVERS 10
+#define MAXAUXDRIVERS 10
+#define MAXMCIDRIVERS 32
+
 #define MAXPNAMELEN      32     /* max product name length (including NULL) */
 #define MAXERRORLENGTH   128    /* max error text length (including NULL) */
 
@@ -166,7 +171,7 @@
 #define  WAVE_FORMAT_QUERY     0x0001
 #define  WAVE_ALLOWSYNC        0x0002
 
-typedef struct {
+typedef struct wavehdr_tag {
     LPSTR       lpData;                 /* pointer to locked data buffer */
     DWORD       dwBufferLength;         /* length of data buffer */
     DWORD       dwBytesRecorded;        /* used for input only */
@@ -764,8 +769,10 @@
 
 #define MCIERR_CUSTOM_DRIVER_BASE       (MCIERR_BASE + 256)
 
-#define MCI_OPEN                        0x0803
-#define MCI_CLOSE                       0x0804
+#define MCI_OPEN_DRIVER					0x0801
+#define MCI_CLOSE_DRIVER				0x0802
+#define MCI_OPEN						0x0803
+#define MCI_CLOSE						0x0804
 #define MCI_ESCAPE                      0x0805
 #define MCI_PLAY                        0x0806
 #define MCI_SEEK                        0x0807
@@ -1389,6 +1396,126 @@
  * 		Linux MMSYSTEM Internals & Sample Audio Drivers
  */
 
+#define DRVM_INIT             100
+#define WODM_INIT             DRVM_INIT
+#define WIDM_INIT             DRVM_INIT
+#define MODM_INIT             DRVM_INIT
+#define MIDM_INIT             DRVM_INIT
+#define AUXM_INIT             DRVM_INIT
+
+#define WODM_GETNUMDEVS       3
+#define WODM_GETDEVCAPS       4
+#define WODM_OPEN             5
+#define WODM_CLOSE            6
+#define WODM_PREPARE          7
+#define WODM_UNPREPARE        8
+#define WODM_WRITE            9
+#define WODM_PAUSE            10
+#define WODM_RESTART          11
+#define WODM_RESET            12 
+#define WODM_GETPOS           13
+#define WODM_GETPITCH         14
+#define WODM_SETPITCH         15
+#define WODM_GETVOLUME        16
+#define WODM_SETVOLUME        17
+#define WODM_GETPLAYBACKRATE  18
+#define WODM_SETPLAYBACKRATE  19
+#define WODM_BREAKLOOP        20
+
+#define WIDM_GETNUMDEVS  50
+#define WIDM_GETDEVCAPS  51
+#define WIDM_OPEN        52
+#define WIDM_CLOSE       53
+#define WIDM_PREPARE     54
+#define WIDM_UNPREPARE   55
+#define WIDM_ADDBUFFER   56
+#define WIDM_START       57
+#define WIDM_STOP        58
+#define WIDM_RESET       59
+#define WIDM_GETPOS      60
+
+#define MODM_GETNUMDEVS		1
+#define MODM_GETDEVCAPS		2
+#define MODM_OPEN			3
+#define MODM_CLOSE			4
+#define MODM_PREPARE		5
+#define MODM_UNPREPARE		6
+#define MODM_DATA			7
+#define MODM_LONGDATA		8
+#define MODM_RESET          9
+#define MODM_GETVOLUME		10
+#define MODM_SETVOLUME		11
+#define MODM_CACHEPATCHES		12      
+#define MODM_CACHEDRUMPATCHES	13     
+
+#define MIDM_GETNUMDEVS  53
+#define MIDM_GETDEVCAPS  54
+#define MIDM_OPEN        55
+#define MIDM_CLOSE       56
+#define MIDM_PREPARE     57
+#define MIDM_UNPREPARE   58
+#define MIDM_ADDBUFFER   59
+#define MIDM_START       60
+#define MIDM_STOP        61
+#define MIDM_RESET       62
+
+#define AUXDM_GETNUMDEVS    3
+#define AUXDM_GETDEVCAPS    4
+#define AUXDM_GETVOLUME     5
+#define AUXDM_SETVOLUME     6
+
+#define MCI_MAX_DEVICE_TYPE_LENGTH 80
+
+#define MCI_FALSE                       (MCI_STRING_OFFSET + 19)
+#define MCI_TRUE                        (MCI_STRING_OFFSET + 20)
+
+#define MCI_FORMAT_RETURN_BASE          MCI_FORMAT_MILLISECONDS_S
+#define MCI_FORMAT_MILLISECONDS_S       (MCI_STRING_OFFSET + 21)
+#define MCI_FORMAT_HMS_S                (MCI_STRING_OFFSET + 22)
+#define MCI_FORMAT_MSF_S                (MCI_STRING_OFFSET + 23)
+#define MCI_FORMAT_FRAMES_S             (MCI_STRING_OFFSET + 24)
+#define MCI_FORMAT_SMPTE_24_S           (MCI_STRING_OFFSET + 25)
+#define MCI_FORMAT_SMPTE_25_S           (MCI_STRING_OFFSET + 26)
+#define MCI_FORMAT_SMPTE_30_S           (MCI_STRING_OFFSET + 27)
+#define MCI_FORMAT_SMPTE_30DROP_S       (MCI_STRING_OFFSET + 28)
+#define MCI_FORMAT_BYTES_S              (MCI_STRING_OFFSET + 29)
+#define MCI_FORMAT_SAMPLES_S            (MCI_STRING_OFFSET + 30)
+#define MCI_FORMAT_TMSF_S               (MCI_STRING_OFFSET + 31)
+
+#define MCI_VD_FORMAT_TRACK_S           (MCI_VD_OFFSET + 5)
+
+#define WAVE_FORMAT_PCM_S               (MCI_WAVE_OFFSET + 0)
+#define WAVE_MAPPER_S                   (MCI_WAVE_OFFSET + 1)
+
+#define MCI_SEQ_MAPPER_S                (MCI_SEQ_OFFSET + 5)
+#define MCI_SEQ_FILE_S                  (MCI_SEQ_OFFSET + 6)
+#define MCI_SEQ_MIDI_S                  (MCI_SEQ_OFFSET + 7)
+#define MCI_SEQ_SMPTE_S                 (MCI_SEQ_OFFSET + 8)
+#define MCI_SEQ_FORMAT_SONGPTR_S        (MCI_SEQ_OFFSET + 9)
+#define MCI_SEQ_NONE_S                  (MCI_SEQ_OFFSET + 10)
+#define MIDIMAPPER_S                    (MCI_SEQ_OFFSET + 11)
+
+#define MCI_RESOURCE_RETURNED       0x00010000  /* resource ID */
+#define MCI_COLONIZED3_RETURN       0x00020000  /* colonized ID, 3 bytes data */
+#define MCI_COLONIZED4_RETURN       0x00040000  /* colonized ID, 4 bytes data */
+#define MCI_INTEGER_RETURNED        0x00080000  /* integer conversion needed */
+#define MCI_RESOURCE_DRIVER         0x00100000  /* driver owns returned resource */
+
+#define MCI_NO_COMMAND_TABLE    0xFFFF
+
+#define MCI_COMMAND_HEAD        0
+#define MCI_STRING              1
+#define MCI_INTEGER             2
+#define MCI_END_COMMAND         3
+#define MCI_RETURN              4
+#define MCI_FLAG                5
+#define MCI_END_COMMAND_LIST    6
+#define MCI_RECT                7
+#define MCI_CONSTANT            8
+#define MCI_END_CONSTANT        9
+
+#define MAKEMCIRESOURCE(wRet, wRes) MAKELRESULT((wRet), (wRes))
+
 typedef struct {
 	DWORD   	dwCallback;
 	DWORD   	dwInstance;
@@ -1397,6 +1524,56 @@
 	}	PORTALLOC;
 typedef PORTALLOC FAR *LPPORTALLOC;
 
+typedef struct {
+	HWAVE			hWave;
+	LPWAVEFORMAT	lpFormat;
+	DWORD			dwCallBack;
+	DWORD			dwInstance;
+	} WAVEOPENDESC;
+typedef WAVEOPENDESC FAR *LPWAVEOPENDESC;
+
+typedef struct {
+	HMIDI			hMidi;
+	DWORD			dwCallback;
+	DWORD			dwInstance;
+	} MIDIOPENDESC;
+typedef MIDIOPENDESC FAR *LPMIDIOPENDESC;
+
+typedef struct {
+	UINT			wDelay;
+	UINT			wResolution;
+	LPTIMECALLBACK	lpFunction;
+	DWORD			dwUser;
+	UINT			wFlags;
+	} TIMEREVENT;
+typedef TIMEREVENT FAR *LPTIMEREVENT;
+
+typedef struct {
+	UINT    wDeviceID;				/* device ID */
+	LPSTR 	lpstrParams;			/* parameter string for entry in SYSTEM.INI */
+	UINT    wCustomCommandTable;	/* custom command table (0xFFFF if none) */
+									/* filled in by the driver */
+	UINT    wType;					/* driver type */
+									/* filled in by the driver */
+	} MCI_OPEN_DRIVER_PARMS;
+typedef MCI_OPEN_DRIVER_PARMS FAR * LPMCI_OPEN_DRIVER_PARMS;
+
+DWORD WINAPI mciGetDriverData(UINT uDeviceID);
+BOOL  WINAPI mciSetDriverData(UINT uDeviceID, DWORD dwData);
+UINT  WINAPI mciDriverYield(UINT uDeviceID);
+BOOL  WINAPI mciDriverNotify(HWND hwndCallback, UINT uDeviceID,
+    UINT uStatus);
+UINT  WINAPI mciLoadCommandResource(HINSTANCE hInstance,
+    LPCSTR lpResName, UINT uType);
+BOOL  WINAPI mciFreeCommandResource(UINT uTable);
+
+#define DCB_NULL		0x0000
+#define DCB_WINDOW		0x0001			/* dwCallback is a HWND */
+#define DCB_TASK		0x0002			/* dwCallback is a HTASK */
+#define DCB_FUNCTION	0x0003			/* dwCallback is a FARPROC */
+#define DCB_TYPEMASK	0x0007
+#define DCB_NOSWITCH	0x0008			/* don't switch stacks for callback */
+
 BOOL DriverCallback(DWORD dwCallBack, UINT uFlags, HANDLE hDev, 
 		WORD wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);
 DWORD auxMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
diff --git a/include/options.h b/include/options.h
index 5003b9f..3caf1fd 100644
--- a/include/options.h
+++ b/include/options.h
@@ -15,7 +15,6 @@
     int    usePrivateMap;
     int    synchronous;
     int    nobackingstore;
-    int    nosaveunders;
     short  cmdShow;
     int    relay_debug;
     int    debug;
diff --git a/include/windows.h b/include/windows.h
index 5281abd..c389455 100644
--- a/include/windows.h
+++ b/include/windows.h
@@ -2790,8 +2790,8 @@
 Fc(BOOL,LineTo,HDC,a,short,b,short,c)
 Fc(WORD,GetInternalWindowPos,HWND,a,LPRECT,b,LPPOINT,c)
 Fc(LONG,_llseek,INT,a,LONG,b,INT,c)
-Fc(INT,_lread,INT,a,LPSTR,b,INT,c)
-Fc(INT,_lwrite,INT,a,LPSTR,b,INT,c)
+Fc(INT,_lread,INT,a,LPSTR,b,WORD,c)
+Fc(INT,_lwrite,INT,a,LPSTR,b,WORD,c)
 Fc(int,FillRect,HDC,a,LPRECT,b,HBRUSH,c)
 Fc(DWORD,MoveTo,HDC,a,short,b,short,c)
 Fc(BOOL,CheckMenuItem,HMENU,a,WORD,b,WORD,c)
diff --git a/loader/main.c b/loader/main.c
index 8161854..459e7bc 100644
--- a/loader/main.c
+++ b/loader/main.c
@@ -264,7 +264,10 @@
 	    strncpy(filename, Argv[0], p - Argv[0]);
 	    filename[p - Argv[0]] = '\0';
 	    strcat(WindowsPath, ";");
-	    strcat(WindowsPath, filename);
+	    if (strchr(filename, '/'))
+		    strcat(WindowsPath, GetDosFileName(filename));
+	    else
+	    	    strcat(WindowsPath, filename);
 	}
 	
 	if ((hInstMain = LoadImage(Argv[0], EXE, 1)) < 32) {
diff --git a/misc/Imakefile b/misc/Imakefile
index 5967031..09d194c 100644
--- a/misc/Imakefile
+++ b/misc/Imakefile
@@ -16,7 +16,9 @@
 	keyboard.c \
 	lstr.c \
 	main.c \
+	mcicda.c \
 	message.c \
+	mmaux.c \
 	mmsystem.c \
 	network.c \
 	profile.c \
diff --git a/misc/audio.c b/misc/audio.c
index 008029f..153223f 100644
--- a/misc/audio.c
+++ b/misc/audio.c
@@ -4,20 +4,109 @@
  * Copyright 1994 Martin Ayotte
  */
 
+#define DEBUG_MCIWAVE
+
 static char Copyright[] = "Copyright  Martin Ayotte, 1994";
 
 #include "stdio.h"
-#include "windows.h"
 #include "win.h"
 #include "user.h"
 #include "driver.h"
 #include "mmsystem.h"
 
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/soundcard.h>
+
+#define SOUND_DEV "/dev/dsp"
+
+#ifdef SOUND_VERSION
+#define IOCTL(a,b,c)		ioctl(a,b,&c)
+#else
+#define IOCTL(a,b,c)		(c = ioctl(a,b,c) )
+#endif
+
+#define MAX_WAVOUTDRV 	2
+#define MAX_WAVINDRV 	2
+#define MAX_MCIWAVDRV 	2
+
+typedef struct {
+	int				unixdev;
+	int				state;
+	DWORD			bufsize;
+	WAVEOPENDESC	waveDesc;
+	WORD			wFlags;
+	PCMWAVEFORMAT	Format;
+	LPWAVEHDR 		lpQueueHdr;
+	DWORD			dwTotalPlayed;
+	} LINUX_WAVEOUT;
+
+typedef struct {
+	int				unixdev;
+	int				state;
+	DWORD			bufsize;	/* Linux '/dev/dsp' give us that size */
+	WAVEOPENDESC	waveDesc;
+	WORD			wFlags;
+	PCMWAVEFORMAT	Format;
+	LPWAVEHDR 		lpQueueHdr;
+	DWORD			dwTotalRecorded;
+	} LINUX_WAVEIN;
+
+typedef struct {
+    int     nUseCount;          /* Incremented for each shared open */
+    BOOL    fShareable;         /* TRUE if first open was shareable */
+    WORD    wNotifyDeviceID;    /* MCI device ID with a pending notification */
+    HANDLE  hCallback;          /* Callback handle for pending notification */
+	int		hFile;				/* file handle open as Element		*/
+	MCI_WAVE_OPEN_PARMS openParms;
+	PCMWAVEFORMAT	WaveFormat;
+	WAVEHDR		WaveHdr;
+	} LINUX_MCIWAVE;
+
+static LINUX_WAVEOUT	WOutDev[MAX_WAVOUTDRV];
+static LINUX_WAVEIN		WInDev[MAX_WAVOUTDRV];
+static LINUX_MCIWAVE	MCIWavDev[MAX_MCIWAVDRV];
+
+DWORD WAVE_mciOpen(DWORD dwFlags, LPMCI_WAVE_OPEN_PARMS lpParms);
+DWORD WAVE_mciClose(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms);
+DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms);
+DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms);
+DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
+DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
+DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
+DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms);
+DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms);
+DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpParms);
+DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMS lpParms);
+
+DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS lpCaps, DWORD dwSize);
+DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags);
+DWORD wodClose(WORD wDevID);
+DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize);
+DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize);
+DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize);
+
+
+/**************************************************************************
+* 				WAVE_NotifyClient			[internal]
+*/
+DWORD WAVE_NotifyClient(UINT wDevID, WORD wMsg, 
+				DWORD dwParam1, DWORD dwParam2)
+{
+	if (WInDev[wDevID].wFlags != DCB_NULL && !DriverCallback(
+		WInDev[wDevID].waveDesc.dwCallBack, WInDev[wDevID].wFlags, 
+		WInDev[wDevID].waveDesc.hWave, wMsg, 
+		WInDev[wDevID].waveDesc.dwInstance, dwParam1, dwParam2)) {
+		printf("WAVE_NotifyClient // can't notify client !\n");
+		return MMSYSERR_NOERROR;
+		}
+}
+
 
 /**************************************************************************
 * 				AUDIO_DriverProc		[sample driver]
 */
-LRESULT AUDIO_DriverProc(DWORD dwDevID, HDRVR hDriv, WORD wMsg, 
+LRESULT WAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, WORD wMsg, 
 						DWORD dwParam1, DWORD dwParam2)
 {
 	switch(wMsg) {
@@ -43,17 +132,1320 @@
 			return (LRESULT)DRVCNF_RESTART;
 		case DRV_REMOVE:
 			return (LRESULT)DRVCNF_RESTART;
+		case MCI_OPEN_DRIVER:
+		case MCI_OPEN:
+			return WAVE_mciOpen(dwParam1, (LPMCI_WAVE_OPEN_PARMS)dwParam2);
+		case MCI_CLOSE_DRIVER:
+		case MCI_CLOSE:
+			return WAVE_mciClose(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+		case MCI_PLAY:
+			return WAVE_mciPlay(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
+		case MCI_RECORD:
+			return WAVE_mciRecord(dwDevID, dwParam1, (LPMCI_RECORD_PARMS)dwParam2);
+		case MCI_STOP:
+			return WAVE_mciStop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+		case MCI_SET:
+			return WAVE_mciSet(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
+		case MCI_PAUSE:
+			return WAVE_mciPause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+		case MCI_RESUME:
+			return WAVE_mciResume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+		case MCI_STATUS:
+			return WAVE_mciStatus(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
+		case MCI_GETDEVCAPS:
+			return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
+		case MCI_INFO:
+			return WAVE_mciInfo(dwDevID, dwParam1, (LPMCI_INFO_PARMS)dwParam2);
 		default:
 			return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
 		}
 }
 
 /**************************************************************************
+* 				WAVE_mciOpen	*/
+DWORD WAVE_mciOpen(DWORD dwFlags, LPMCI_WAVE_OPEN_PARMS lpParms)
+{
+	int			hFile;
+	UINT 		wDevID;
+	OFSTRUCT	OFstruct;
+	LPPCMWAVEFORMAT	lpWaveFormat;
+	WAVEOPENDESC 	WaveDesc;
+	DWORD		dwRet;
+	char		str[128];
+	LPSTR		ptr;
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciOpen(%08X, %08X)\n", dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	wDevID = lpParms->wDeviceID;
+	if (MCIWavDev[wDevID].nUseCount > 0) {
+		/* The driver already open on this channel */
+		/* If the driver was opened shareable before and this open specifies */
+		/* shareable then increment the use count */
+		if (MCIWavDev[wDevID].fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
+			++MCIWavDev[wDevID].nUseCount;
+		else
+			return MCIERR_MUST_USE_SHAREABLE;
+		}
+	else {
+		MCIWavDev[wDevID].nUseCount = 1;
+		MCIWavDev[wDevID].fShareable = dwFlags & MCI_OPEN_SHAREABLE;
+		}
+    if (dwFlags & MCI_OPEN_ELEMENT) {
+		printf("WAVE_mciOpen // MCI_OPEN_ELEMENT '%s' !\n", 
+								lpParms->lpstrElementName);
+		printf("WAVE_mciOpen // cdw='%s'\n", DOS_GetCurrentDir(DOS_GetDefaultDrive()));
+		if (strlen(lpParms->lpstrElementName) > 0) {
+			strcpy(str, lpParms->lpstrElementName);
+			AnsiUpper(str);
+			MCIWavDev[wDevID].hFile = _lopen(str, OF_READWRITE);
+			if (MCIWavDev[wDevID].hFile < 1) {
+				MCIWavDev[wDevID].hFile = 0;
+				printf("WAVE_mciOpen // can't find file='%s' !\n", str);
+				return MCIERR_FILE_NOT_FOUND;
+				}
+			}
+		else 
+			MCIWavDev[wDevID].hFile = 0;
+		}
+	printf("WAVE_mciOpen // hFile=%u\n", MCIWavDev[wDevID].hFile);
+	memcpy(&MCIWavDev[wDevID].openParms, lpParms, sizeof(MCI_WAVE_OPEN_PARMS));
+	MCIWavDev[wDevID].wNotifyDeviceID = lpParms->wDeviceID;
+	lpWaveFormat = &MCIWavDev[wDevID].WaveFormat;
+	WaveDesc.hWave = 0;
+	WaveDesc.lpFormat = (LPWAVEFORMAT)lpWaveFormat;
+	lpWaveFormat->wf.wFormatTag = WAVE_FORMAT_PCM;
+	lpWaveFormat->wBitsPerSample = 8;
+	lpWaveFormat->wf.nChannels = 1;
+	lpWaveFormat->wf.nSamplesPerSec = 11025;
+	lpWaveFormat->wf.nAvgBytesPerSec = 11025;
+	lpWaveFormat->wf.nBlockAlign = 1;
+	dwRet = wodMessage(0, WODM_OPEN, 0, (DWORD)&WaveDesc, CALLBACK_NULL);
+	dwRet = widMessage(0, WIDM_OPEN, 0, (DWORD)&WaveDesc, CALLBACK_NULL);
+	return 0;
+}
+
+/**************************************************************************
+* 				WAVE_mciClose		[internal]
+*/
+DWORD WAVE_mciClose(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
+{
+	DWORD		dwRet;
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciClose(%u, %08X, %08X);\n", wDevID, dwParam, lpParms);
+#endif
+	MCIWavDev[wDevID].nUseCount--;
+	if (MCIWavDev[wDevID].nUseCount == 0) {
+		if (MCIWavDev[wDevID].hFile != 0) {
+			close(MCIWavDev[wDevID].hFile);
+			MCIWavDev[wDevID].hFile = 0;
+			}
+		dwRet = wodMessage(0, WODM_CLOSE, 0, 0L, 0L);
+		if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
+		dwRet = widMessage(0, WIDM_CLOSE, 0, 0L, 0L);
+		if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
+		}
+	return 0;
+}
+
+
+/**************************************************************************
+* 				WAVE_mciPlay		[internal]
+*/
+DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
+{
+	int			count;
+	int			start, end;
+	LPWAVEHDR		lpWaveHdr;
+	DWORD		dwRet;
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciPlay(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (MCIWavDev[wDevID].hFile == 0) {
+		printf("WAVE_mciPlay // can't find file='%s' !\n", 
+				MCIWavDev[wDevID].openParms.lpstrElementName);
+		return MCIERR_FILE_NOT_FOUND;
+		}
+	start = 1; 		end = 99999;
+	if (dwFlags & MCI_FROM) {
+		start = lpParms->dwFrom; 
+		printf("WAVE_mciPlay // MCI_FROM=%d \n", start);
+		}
+	if (dwFlags & MCI_TO) {
+		end = lpParms->dwTo;
+		printf("WAVE_mciPlay // MCI_TO=%d \n", end);
+		}
+/*
+	if (dwFlags & MCI_NOTIFY) {
+		printf("WAVE_mciPlay // MCI_NOTIFY %08X !\n", lpParms->dwCallback);
+		switch(fork()) {
+			case -1:
+				printf("WAVE_mciPlay // Can't 'fork' process !\n");
+				break;
+			case 0:
+				break;         
+			default:
+				printf("WAVE_mciPlay // process started ! return to caller...\n");
+				return 0;
+			}
+		}
+*/
+	lpWaveHdr = &MCIWavDev[wDevID].WaveHdr;
+	lpWaveHdr->lpData = (LPSTR) malloc(64000);
+	lpWaveHdr->dwBufferLength = 32000;
+	lpWaveHdr->dwUser = 0L;
+	lpWaveHdr->dwFlags = 0L;
+	lpWaveHdr->dwLoops = 0L;
+	dwRet = wodMessage(0, WODM_PREPARE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
+	printf("WAVE_mciPlay // after WODM_PREPARE \n");
+	while(TRUE) {
+/*		printf("WAVE_mciPlay // before 'read' hFile=%u lpData=%08X dwBufferLength=%u\n",
+				MCIWavDev[wDevID].hFile, lpWaveHdr->lpData, lpWaveHdr->dwBufferLength); */
+		count = _lread(MCIWavDev[wDevID].hFile, lpWaveHdr->lpData, lpWaveHdr->dwBufferLength);
+		if (count < 1) break;
+		lpWaveHdr->dwBytesRecorded = count;
+		printf("WAVE_mciPlay // before WODM_WRITE lpWaveHdr=%08X dwBytesRecorded=%u\n",
+					lpWaveHdr, lpWaveHdr->dwBytesRecorded);
+		dwRet = wodMessage(0, WODM_WRITE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
+		}
+	printf("WAVE_mciPlay // before WODM_UNPREPARE \n");
+	dwRet = wodMessage(0, WODM_UNPREPARE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
+	printf("WAVE_mciPlay // after WODM_UNPREPARE \n");
+	if (lpWaveHdr->lpData != NULL) {
+		free(lpWaveHdr->lpData);
+		lpWaveHdr->lpData = NULL;
+		}
+	if (dwFlags & MCI_NOTIFY) {
+#ifdef DEBUG_MCIWAVE
+		printf("WAVE_mciPlay // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+#endif
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			MCIWavDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+	return 0;
+}
+
+
+/**************************************************************************
+* 				WAVE_mciRecord			[internal]
+*/
+DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
+{
+	int			count;
+	int			start, end;
+	LPWAVEHDR		lpWaveHdr;
+	DWORD		dwRet;
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciRecord(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (MCIWavDev[wDevID].hFile == 0) {
+		printf("WAVE_mciRecord // can't find file='%s' !\n", 
+				MCIWavDev[wDevID].openParms.lpstrElementName);
+		return MCIERR_FILE_NOT_FOUND;
+		}
+	start = 1; 		end = 99999;
+	if (dwFlags & MCI_FROM) {
+		start = lpParms->dwFrom; 
+		printf("WAVE_mciRecord // MCI_FROM=%d \n", start);
+		}
+	if (dwFlags & MCI_TO) {
+		end = lpParms->dwTo;
+		printf("WAVE_mciRecord // MCI_TO=%d \n", end);
+		}
+	lpWaveHdr = &MCIWavDev[wDevID].WaveHdr;
+	lpWaveHdr->lpData = (LPSTR) malloc(64000);
+	lpWaveHdr->dwBufferLength = 32000;
+	lpWaveHdr->dwUser = 0L;
+	lpWaveHdr->dwFlags = 0L;
+	lpWaveHdr->dwLoops = 0L;
+	dwRet = widMessage(0, WIDM_PREPARE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
+	printf("WAVE_mciRecord // after WIDM_PREPARE \n");
+	while(TRUE) {
+		lpWaveHdr->dwBytesRecorded = 0;
+		dwRet = widMessage(0, WIDM_START, 0, 0L, 0L);
+		printf("WAVE_mciRecord // after WIDM_START lpWaveHdr=%08X dwBytesRecorded=%u\n",
+					lpWaveHdr, lpWaveHdr->dwBytesRecorded);
+		if (lpWaveHdr->dwBytesRecorded == 0) break;
+		}
+	printf("WAVE_mciRecord // before WIDM_UNPREPARE \n");
+	dwRet = widMessage(0, WIDM_UNPREPARE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
+	printf("WAVE_mciRecord // after WIDM_UNPREPARE \n");
+	if (lpWaveHdr->lpData != NULL) {
+		free(lpWaveHdr->lpData);
+		lpWaveHdr->lpData = NULL;
+		}
+	if (dwFlags & MCI_NOTIFY) {
+#ifdef DEBUG_MCIWAVE
+		printf("WAVE_mciRecord // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+#endif
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			MCIWavDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+	return 0;
+}
+
+
+/**************************************************************************
+* 				WAVE_mciStop			[internal]
+*/
+DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciStop(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	return 0;
+}
+
+
+/**************************************************************************
+* 				WAVE_mciPause			[internal]
+*/
+DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciPause(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	return 0;
+}
+
+
+/**************************************************************************
+* 				WAVE_mciResume			[internal]
+*/
+DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciResume(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	return 0;
+}
+
+
+/**************************************************************************
+* 				WAVE_mciSet			[internal]
+*/
+DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
+{
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciSet(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciSet // dwTimeFormat=%08X\n", lpParms->dwTimeFormat);
+	printf("WAVE_mciSet // dwAudio=%08X\n", lpParms->dwAudio);
+#endif
+	if (dwFlags & MCI_SET_TIME_FORMAT) {
+		switch (lpParms->dwTimeFormat) {
+			case MCI_FORMAT_MILLISECONDS:
+				printf("WAVE_mciSet // MCI_FORMAT_MILLISECONDS !\n");
+				break;
+			case MCI_FORMAT_BYTES:
+				printf("WAVE_mciSet // MCI_FORMAT_BYTES !\n");
+				break;
+			case MCI_FORMAT_SAMPLES:
+				printf("WAVE_mciSet // MCI_FORMAT_SAMPLES !\n");
+				break;
+			default:
+				printf("WAVE_mciSet // bad time format !\n");
+				return MCIERR_BAD_TIME_FORMAT;
+			}
+		}
+	if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
+	if (dwFlags & MCI_SET_DOOR_OPEN) return MCIERR_UNSUPPORTED_FUNCTION;
+	if (dwFlags & MCI_SET_DOOR_CLOSED) return MCIERR_UNSUPPORTED_FUNCTION;
+	if (dwFlags & MCI_SET_AUDIO) {
+		printf("WAVE_mciSet // MCI_SET_AUDIO !\n");
+		}
+	if (dwFlags && MCI_SET_ON) {
+		printf("WAVE_mciSet // MCI_SET_ON !\n");
+		if (dwFlags && MCI_SET_AUDIO_LEFT) {
+			printf("WAVE_mciSet // MCI_SET_AUDIO_LEFT !\n");
+			}
+		if (dwFlags && MCI_SET_AUDIO_RIGHT) {
+			printf("WAVE_mciSet // MCI_SET_AUDIO_RIGHT !\n");
+			}
+		}
+	if (dwFlags & MCI_SET_OFF) {
+		printf("WAVE_mciSet // MCI_SET_OFF !\n");
+		}
+	if (dwFlags & MCI_WAVE_INPUT) {
+		printf("WAVE_mciSet // MCI_WAVE_INPUT !\n");
+		}
+	if (dwFlags & MCI_WAVE_OUTPUT) {
+		printf("WAVE_mciSet // MCI_WAVE_OUTPUT !\n");
+		}
+	if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
+		printf("WAVE_mciSet // MCI_WAVE_SET_ANYINPUT !\n");
+		}
+	if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
+		printf("WAVE_mciSet // MCI_WAVE_SET_ANYOUTPUT !\n");
+		}
+	if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
+		printf("WAVE_mciSet // MCI_WAVE_SET_AVGBYTESPERSEC !\n");
+		}
+	if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
+		printf("WAVE_mciSet // MCI_WAVE_SET_BITSPERSAMPLE !\n");
+		}
+	if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
+		printf("WAVE_mciSet // MCI_WAVE_SET_BLOCKALIGN !\n");
+		}
+	if (dwFlags & MCI_WAVE_SET_CHANNELS) {
+		printf("WAVE_mciSet // MCI_WAVE_SET_CHANNELS !\n");
+		}
+	if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
+		printf("WAVE_mciSet // MCI_WAVE_SET_FORMATTAG !\n");
+		}
+	if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
+		printf("WAVE_mciSet // MCI_WAVE_SET_SAMPLESPERSEC !\n");
+		}
+ 	return 0;
+}
+
+
+/**************************************************************************
+* 				WAVE_mciStatus		[internal]
+*/
+DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
+{
+#ifdef DEBUG_MCIWAVE
+	printf("WAVE_mciStatus(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (dwFlags & MCI_STATUS_ITEM) {
+		switch(lpParms->dwItem) {
+			case MCI_STATUS_CURRENT_TRACK:
+				lpParms->dwReturn = 1;
+				break;
+			case MCI_STATUS_LENGTH:
+				lpParms->dwReturn = 5555;
+				if (dwFlags & MCI_TRACK) {
+					lpParms->dwTrack = 1;
+					lpParms->dwReturn = 2222;
+					}
+				break;
+			case MCI_STATUS_MODE:
+				lpParms->dwReturn = MCI_MODE_STOP;
+				break;
+			case MCI_STATUS_MEDIA_PRESENT:
+				printf("WAVE_mciStatus // MCI_STATUS_MEDIA_PRESENT !\n");
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_STATUS_NUMBER_OF_TRACKS:
+				lpParms->dwReturn = 1;
+				break;
+			case MCI_STATUS_POSITION:
+				lpParms->dwReturn = 3333;
+				if (dwFlags & MCI_STATUS_START) {
+					lpParms->dwItem = 1;
+					}
+				if (dwFlags & MCI_TRACK) {
+					lpParms->dwTrack = 1;
+					lpParms->dwReturn = 777;
+					}
+				break;
+			case MCI_STATUS_READY:
+				printf("WAVE_mciStatus // MCI_STATUS_READY !\n");
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_STATUS_TIME_FORMAT:
+				printf("WAVE_mciStatus // MCI_STATUS_TIME_FORMAT !\n");
+				lpParms->dwReturn = MCI_FORMAT_MILLISECONDS;
+				break;
+			case MCI_WAVE_INPUT:
+				printf("WAVE_mciStatus // MCI_WAVE_INPUT !\n");
+				lpParms->dwReturn = 0;
+				break;
+			case MCI_WAVE_OUTPUT:
+				printf("WAVE_mciStatus // MCI_WAVE_OUTPUT !\n");
+				lpParms->dwReturn = 0;
+				break;
+			case MCI_WAVE_STATUS_AVGBYTESPERSEC:
+				printf("WAVE_mciStatus // MCI_WAVE_STATUS_AVGBYTESPERSEC !\n");
+				lpParms->dwReturn = 22050;
+				break;
+			case MCI_WAVE_STATUS_BITSPERSAMPLE:
+				printf("WAVE_mciStatus // MCI_WAVE_STATUS_BITSPERSAMPLE !\n");
+				lpParms->dwReturn = 8;
+				break;
+			case MCI_WAVE_STATUS_BLOCKALIGN:
+				printf("WAVE_mciStatus // MCI_WAVE_STATUS_BLOCKALIGN !\n");
+				lpParms->dwReturn = 1;
+				break;
+			case MCI_WAVE_STATUS_CHANNELS:
+				printf("WAVE_mciStatus // MCI_WAVE_STATUS_CHANNELS !\n");
+				lpParms->dwReturn = 1;
+				break;
+			case MCI_WAVE_STATUS_FORMATTAG:
+				printf("WAVE_mciStatus // MCI_WAVE_FORMATTAG !\n");
+				lpParms->dwReturn = WAVE_FORMAT_PCM;
+				break;
+			case MCI_WAVE_STATUS_LEVEL:
+				printf("WAVE_mciStatus // MCI_WAVE_STATUS_LEVEL !\n");
+				lpParms->dwReturn = 0xAAAA5555;
+				break;
+			case MCI_WAVE_STATUS_SAMPLESPERSEC:
+				printf("WAVE_mciStatus // MCI_WAVE_STATUS_SAMPLESPERSEC !\n");
+				lpParms->dwReturn = 22050;
+				break;
+			default:
+				printf("WAVE_mciStatus // unknowm command %04X !\n", lpParms->dwItem);
+				return MCIERR_UNRECOGNIZED_COMMAND;
+			}
+		}
+	if (dwFlags & MCI_NOTIFY) {
+		printf("WAVE_mciStatus // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			MCIWavDev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+ 	return 0;
+}
+
+/**************************************************************************
+* 				WAVE_mciGetDevCaps		[internal]
+*/
+DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags, 
+					LPMCI_GETDEVCAPS_PARMS lpParms)
+{
+	printf("WAVE_mciGetDevCaps(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (dwFlags & MCI_GETDEVCAPS_ITEM) {
+		switch(lpParms->dwItem) {
+			case MCI_GETDEVCAPS_CAN_RECORD:
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_GETDEVCAPS_HAS_AUDIO:
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_GETDEVCAPS_HAS_VIDEO:
+				lpParms->dwReturn = FALSE;
+				break;
+			case MCI_GETDEVCAPS_DEVICE_TYPE:
+				lpParms->dwReturn = MCI_DEVTYPE_WAVEFORM_AUDIO;
+				break;
+			case MCI_GETDEVCAPS_USES_FILES:
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_GETDEVCAPS_COMPOUND_DEVICE:
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_GETDEVCAPS_CAN_EJECT:
+				lpParms->dwReturn = FALSE;
+				break;
+			case MCI_GETDEVCAPS_CAN_PLAY:
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_GETDEVCAPS_CAN_SAVE:
+				lpParms->dwReturn = FALSE;
+				break;
+			case MCI_WAVE_GETDEVCAPS_INPUTS:
+				lpParms->dwReturn = 1;
+				break;
+			case MCI_WAVE_GETDEVCAPS_OUTPUTS:
+				lpParms->dwReturn = 1;
+				break;
+			default:
+				return MCIERR_UNRECOGNIZED_COMMAND;
+			}
+		}
+ 	return 0;
+}
+
+/**************************************************************************
+* 				WAVE_mciInfo			[internal]
+*/
+DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMS lpParms)
+{
+	printf("WAVE_mciInfo(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	lpParms->lpstrReturn = NULL;
+	switch(dwFlags) {
+		case MCI_INFO_PRODUCT:
+			lpParms->lpstrReturn = "Linux Sound System 0.5";
+			break;
+		case MCI_INFO_FILE:
+			lpParms->lpstrReturn = "FileName";
+			break;
+		case MCI_WAVE_INPUT:
+			lpParms->lpstrReturn = "Linux Sound System 0.5";
+			break;
+		case MCI_WAVE_OUTPUT:
+			lpParms->lpstrReturn = "Linux Sound System 0.5";
+			break;
+		default:
+			return MCIERR_UNRECOGNIZED_COMMAND;
+		}
+	if (lpParms->lpstrReturn != NULL)
+		lpParms->dwRetSize = strlen(lpParms->lpstrReturn);
+	else
+		lpParms->dwRetSize = 0;
+ 	return 0;
+}
+
+
+/*-----------------------------------------------------------------------*/
+
+
+/**************************************************************************
+* 				wodGetDevCaps				[internal]
+*/
+DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS lpCaps, DWORD dwSize)
+{
+	int 	audio;
+	int		smplrate;
+	int		samplesize = 16;
+	int		dsp_stereo = 1;
+	int		bytespersmpl;
+	printf("wodGetDevCaps(%u, %08X, %u);\n", wDevID, lpCaps, dwSize);
+	if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+	audio = open (SOUND_DEV, O_WRONLY, 0);
+	if (audio == -1) return MMSYSERR_NOTENABLED;
+	lpCaps->wMid = 0xFF; 	/* Manufac ID */
+	lpCaps->wPid = 0x01; 	/* Product ID */
+	strcpy(lpCaps->szPname, "Linux WAV Driver");
+	lpCaps->dwFormats = 0;
+	lpCaps->dwSupport = 0;
+	lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
+	bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
+	smplrate = 44100;
+	if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
+		lpCaps->dwFormats |= WAVE_FORMAT_4M08;
+		if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_4S08;
+		if (bytespersmpl > 1) {
+			lpCaps->dwFormats |= WAVE_FORMAT_4M16;
+			if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_4S16;
+			}
+		}
+	smplrate = 22050;
+	if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
+		lpCaps->dwFormats |= WAVE_FORMAT_2M08;
+		if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_2S08;
+		if (bytespersmpl > 1) {
+			lpCaps->dwFormats |= WAVE_FORMAT_2M16;
+			if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_2S16;
+			}
+		}
+	smplrate = 11025;
+	if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
+		lpCaps->dwFormats |= WAVE_FORMAT_1M08;
+		if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_1S08;
+		if (bytespersmpl > 1) {
+			lpCaps->dwFormats |= WAVE_FORMAT_1M16;
+			if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_1S16;
+			}
+		}
+	close(audio);
+	printf("wodGetDevCaps // dwFormats = %08X\n", lpCaps->dwFormats);
+	return MMSYSERR_NOERROR;
+}
+
+
+/**************************************************************************
+* 				wodOpen				[internal]
+*/
+DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
+{
+	int 		audio;
+	int			abuf_size;
+	int			smplrate;
+	int			samplesize;
+	int			dsp_stereo;
+	printf("wodOpen(%u, %08X, %08X);\n", wDevID, lpDesc, dwFlags);
+	if (lpDesc == NULL) {
+		printf("Linux 'wodOpen' // Invalid Parameter !\n");
+		return MMSYSERR_INVALPARAM;
+		}
+	if (wDevID >= MAX_WAVOUTDRV) {
+		printf("Linux 'wodOpen' // MAX_WAVOUTDRV reached !\n");
+		return MMSYSERR_ALLOCATED;
+		}
+	WOutDev[wDevID].unixdev = 0;
+	audio = open (SOUND_DEV, O_WRONLY, 0);
+	if (audio == -1) {
+		printf("Linux 'wodOpen' // can't open !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
+	if (abuf_size < 4096 || abuf_size > 65536) {
+		if (abuf_size == -1)
+			printf("Linux 'wodOpen' // IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
+		else
+			printf("Linux 'wodOpen' // SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+	switch(WOutDev[wDevID].wFlags) {
+		case DCB_NULL:
+			printf("Linux 'wodOpen' // CALLBACK_NULL !\n");
+			break;
+		case DCB_WINDOW:
+			printf("Linux 'wodOpen' // CALLBACK_WINDOW !\n");
+			break;
+		case DCB_TASK:
+			printf("Linux 'wodOpen' // CALLBACK_TASK !\n");
+			break;
+		case DCB_FUNCTION:
+			printf("Linux 'wodOpen' // CALLBACK_FUNCTION !\n");
+			break;
+		}
+	WOutDev[wDevID].lpQueueHdr = NULL;
+	WOutDev[wDevID].unixdev = audio;
+	WOutDev[wDevID].dwTotalPlayed = 0;
+	WOutDev[wDevID].bufsize = abuf_size;
+	memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
+	if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
+		printf("Linux 'wodOpen' // Bad format %04X !\n", 
+						lpDesc->lpFormat->wFormatTag);
+		return WAVERR_BADFORMAT;
+		}
+	memcpy(&WOutDev[wDevID].Format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
+	if (WOutDev[wDevID].Format.wf.nChannels == 0) return WAVERR_BADFORMAT;
+	if (WOutDev[wDevID].Format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
+	if (WOutDev[wDevID].Format.wBitsPerSample == 0) {
+		WOutDev[wDevID].Format.wBitsPerSample = 8 *
+		(WOutDev[wDevID].Format.wf.nAvgBytesPerSec /
+		WOutDev[wDevID].Format.wf.nSamplesPerSec) /
+		WOutDev[wDevID].Format.wf.nChannels;
+		}
+	samplesize = WOutDev[wDevID].Format.wBitsPerSample;
+	smplrate = WOutDev[wDevID].Format.wf.nSamplesPerSec;
+	dsp_stereo = (WOutDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE;
+	IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
+	IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
+	IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
+	printf("Linux 'wodOpen' // wBitsPerSample=%u !\n", 
+				WOutDev[wDevID].Format.wBitsPerSample);
+	printf("Linux 'wodOpen' // nSamplesPerSec=%u !\n", 
+				WOutDev[wDevID].Format.wf.nSamplesPerSec);
+	printf("Linux 'wodOpen' // nChannels=%u !\n", 
+				WOutDev[wDevID].Format.wf.nChannels);
+	if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
+		printf("Linux 'wodOpen' // can't notify client !\n");
+		return MMSYSERR_INVALPARAM;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				wodClose			[internal]
+*/
+DWORD wodClose(WORD wDevID)
+{
+	printf("wodClose(%u);\n", wDevID);
+	if (WOutDev[wDevID].unixdev == 0) {
+		printf("Linux 'wodClose' // can't close !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	close(WOutDev[wDevID].unixdev);
+	WOutDev[wDevID].unixdev = 0;
+	WOutDev[wDevID].bufsize = 0;
+	if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
+		printf("Linux 'wodClose' // can't notify client !\n");
+		return MMSYSERR_INVALPARAM;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				wodWrite			[internal]
+*/
+DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+	printf("wodWrite(%u, %08X, %08X);\n", wDevID, lpWaveHdr, dwSize);
+	if (WOutDev[wDevID].unixdev == 0) {
+		printf("Linux 'wodWrite' // can't play !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	if (lpWaveHdr->lpData == NULL) return WAVERR_UNPREPARED;
+	if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) return WAVERR_UNPREPARED;
+	if (lpWaveHdr->dwFlags & WHDR_INQUEUE) return WAVERR_STILLPLAYING;
+	lpWaveHdr->dwFlags &= ~WHDR_DONE;
+	lpWaveHdr->dwFlags |= WHDR_INQUEUE;
+	printf("wodWrite() // dwBytesRecorded %u !\n", lpWaveHdr->dwBytesRecorded);
+	if (write (WOutDev[wDevID].unixdev, lpWaveHdr->lpData, 
+		lpWaveHdr->dwBytesRecorded) != lpWaveHdr->dwBytesRecorded) {
+		return MMSYSERR_NOTENABLED;
+		}
+	lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+	lpWaveHdr->dwFlags |= WHDR_DONE;
+	if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
+		printf("Linux 'wodWrite' // can't notify client !\n");
+		return MMSYSERR_INVALPARAM;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				wodPrepare			[internal]
+*/
+DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+	printf("wodPrepare(%u, %08X, %08X);\n", wDevID, lpWaveHdr, dwSize);
+	if (WOutDev[wDevID].unixdev == 0) {
+		printf("Linux 'wodPrepare' // can't prepare !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	if (WOutDev[wDevID].lpQueueHdr != NULL) {
+		printf("Linux 'wodPrepare' // already prepare !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	WOutDev[wDevID].dwTotalPlayed = 0;
+	WOutDev[wDevID].lpQueueHdr = lpWaveHdr;
+	if (lpWaveHdr->dwFlags & WHDR_INQUEUE) return WAVERR_STILLPLAYING;
+	lpWaveHdr->dwFlags |= WHDR_PREPARED;
+	lpWaveHdr->dwFlags &= ~WHDR_DONE;
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				wodUnprepare			[internal]
+*/
+DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+	printf("wodUnprepare(%u, %08X, %08X);\n", wDevID, lpWaveHdr, dwSize);
+	if (WOutDev[wDevID].unixdev == 0) {
+		printf("Linux 'wodUnprepare' // can't unprepare !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				wodRestart				[internal]
+*/
+DWORD wodRestart(WORD wDevID)
+{
+	printf("wodRestart(%u);\n", wDevID);
+	if (WOutDev[wDevID].unixdev == 0) {
+		printf("Linux 'wodRestart' // can't restart !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				wodReset				[internal]
+*/
+DWORD wodReset(WORD wDevID)
+{
+	printf("wodReset(%u);\n", wDevID);
+	if (WOutDev[wDevID].unixdev == 0) {
+		printf("Linux 'wodReset' // can't reset !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+
+/**************************************************************************
+* 				wodGetPosition			[internal]
+*/
+DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
+{
+	int		time;
+	printf("wodGetPosition(%u, %08X, %u);\n", wDevID, lpTime, uSize);
+	if (WOutDev[wDevID].unixdev == 0) {
+		printf("Linux 'wodGetPosition' // can't get pos !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	if (lpTime == NULL)	return MMSYSERR_INVALPARAM;
+TryAGAIN:
+	switch(lpTime->wType) {
+		case TIME_BYTES:
+			lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed;
+			printf("wodGetPosition // TIME_BYTES=%u\n", lpTime->u.cb);
+			break;
+		case TIME_SAMPLES:
+			lpTime->u.sample = WOutDev[wDevID].dwTotalPlayed * 8 /
+						WOutDev[wDevID].Format.wBitsPerSample;
+			printf("wodGetPosition // TIME_SAMPLES=%u\n", lpTime->u.sample);
+			break;
+		case TIME_MS:
+			lpTime->u.ms = WOutDev[wDevID].dwTotalPlayed /
+					(WOutDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
+			printf("wodGetPosition // TIME_MS=%u\n", lpTime->u.ms);
+			break;
+		case TIME_SMPTE:
+			time = WOutDev[wDevID].dwTotalPlayed /
+				(WOutDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
+			lpTime->u.smpte.hour = time / 108000;
+			time -= lpTime->u.smpte.hour * 108000;
+			lpTime->u.smpte.min = time / 1800;
+			time -= lpTime->u.smpte.min * 1800;
+			lpTime->u.smpte.sec = time / 30;
+			time -= lpTime->u.smpte.sec * 30;
+			lpTime->u.smpte.frame = time;
+			lpTime->u.smpte.fps = 30;
+			printf("wodGetPosition // TIME_SMPTE=%02u:%02u:%02u:%02u\n", 
+					lpTime->u.smpte.hour, lpTime->u.smpte.min,
+					lpTime->u.smpte.sec, lpTime->u.smpte.frame);
+			break;
+		default:
+			printf("wodGetPosition() format not supported ! use TIME_MS !\n");
+			lpTime->wType = TIME_MS;
+			goto TryAGAIN;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				wodSetVolume			[internal]
+*/
+DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
+{
+	int 	mixer;
+	int		volume = 50;
+	printf("wodSetVolume(%u, %08X);\n", wDevID, dwParam);
+	if (WOutDev[wDevID].unixdev == 0) {
+		printf("Linux 'wodSetVolume' // can't set volume !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	if ((mixer = open("/dev/mixer", O_RDWR)) < 0) {
+		printf("Linux 'wodSetVolume' // mixer device not available !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+    if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
+		printf("Linux 'wodSetVolume' // unable set mixer !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	close(mixer);
+	return MMSYSERR_NOERROR;
+}
+
+
+/**************************************************************************
 * 				wodMessage			[sample driver]
 */
 DWORD wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
 					DWORD dwParam1, DWORD dwParam2)
 {
+	printf("wodMessage(%u, %04X, %08X, %08X, %08X);\n", 
+			wDevID, wMsg, dwUser, dwParam1, dwParam2);
+	switch(wMsg) {
+		case WODM_OPEN:
+			return wodOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
+		case WODM_CLOSE:
+			return wodClose(wDevID);
+		case WODM_WRITE:
+			return wodWrite(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+		case WODM_PAUSE:
+			return 0L;
+		case WODM_GETPOS:
+			return wodGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
+		case WODM_BREAKLOOP:
+			return 0L;
+		case WODM_PREPARE:
+			return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+		case WODM_UNPREPARE:
+			return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+		case WODM_GETDEVCAPS:
+			return wodGetDevCaps(wDevID, (LPWAVEOUTCAPS)dwParam1, dwParam2);
+		case WODM_GETNUMDEVS:
+			return 1L;
+		case WODM_GETPITCH:
+			return 0L;
+		case WODM_SETPITCH:
+			return 0L;
+		case WODM_GETPLAYBACKRATE:
+			return 0L;
+		case WODM_SETPLAYBACKRATE:
+			return 0L;
+		case WODM_GETVOLUME:
+			return 0L;
+		case WODM_SETVOLUME:
+			return wodSetVolume(wDevID, dwParam1);
+		case WODM_RESTART:
+			return wodRestart(wDevID);
+		case WODM_RESET:
+			return wodReset(wDevID);
+		}
+	return MMSYSERR_NOTSUPPORTED;
+}
+
+
+/*-----------------------------------------------------------------------*/
+
+/**************************************************************************
+* 				widGetDevCaps				[internal]
+*/
+DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS lpCaps, DWORD dwSize)
+{
+	int 	audio;
+	int		smplrate;
+	int		samplesize = 16;
+	int		dsp_stereo = 1;
+	int		bytespersmpl;
+	printf("widGetDevCaps(%u, %08X, %u);\n", wDevID, lpCaps, dwSize);
+	if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+	audio = open (SOUND_DEV, O_RDONLY, 0);
+	if (audio == -1) return MMSYSERR_NOTENABLED;
+	lpCaps->wMid = 0xFF; 	/* Manufac ID */
+	lpCaps->wPid = 0x01; 	/* Product ID */
+	strcpy(lpCaps->szPname, "Linux WAV Driver");
+	lpCaps->dwFormats = 0;
+	lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
+	bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
+	smplrate = 44100;
+	if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
+		lpCaps->dwFormats |= WAVE_FORMAT_4M08;
+		if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_4S08;
+		if (bytespersmpl > 1) {
+			lpCaps->dwFormats |= WAVE_FORMAT_4M16;
+			if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_4S16;
+			}
+		}
+	smplrate = 22050;
+	if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
+		lpCaps->dwFormats |= WAVE_FORMAT_2M08;
+		if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_2S08;
+		if (bytespersmpl > 1) {
+			lpCaps->dwFormats |= WAVE_FORMAT_2M16;
+			if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_2S16;
+			}
+		}
+	smplrate = 11025;
+	if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
+		lpCaps->dwFormats |= WAVE_FORMAT_1M08;
+		if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_1S08;
+		if (bytespersmpl > 1) {
+			lpCaps->dwFormats |= WAVE_FORMAT_1M16;
+			if (lpCaps->wChannels > 1)	lpCaps->dwFormats |= WAVE_FORMAT_1S16;
+			}
+		}
+	close(audio);
+	printf("widGetDevCaps // dwFormats = %08X\n", lpCaps->dwFormats);
+	return MMSYSERR_NOERROR;
+}
+
+
+/**************************************************************************
+* 				widOpen				[internal]
+*/
+DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
+{
+	int 		audio;
+	int			abuf_size;
+	int			smplrate;
+	int			samplesize;
+	int			dsp_stereo;
+	printf("widOpen(%u, %08X, %08X);\n", wDevID, lpDesc, dwFlags);
+	if (lpDesc == NULL) {
+		printf("Linux 'widOpen' // Invalid Parameter !\n");
+		return MMSYSERR_INVALPARAM;
+		}
+	if (wDevID >= MAX_WAVINDRV) {
+		printf("Linux 'widOpen' // MAX_WAVINDRV reached !\n");
+		return MMSYSERR_ALLOCATED;
+		}
+	WInDev[wDevID].unixdev = 0;
+	audio = open (SOUND_DEV, O_RDONLY, 0);
+	if (audio == -1) {
+		printf("Linux 'widOpen' // can't open !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
+	if (abuf_size < 4096 || abuf_size > 65536) {
+		if (abuf_size == -1)
+			printf("Linux 'widOpen' // IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
+		else
+			printf("Linux 'widOpen' // SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+	switch(WInDev[wDevID].wFlags) {
+		case DCB_NULL:
+			printf("Linux 'widOpen' // CALLBACK_NULL !\n");
+			break;
+		case DCB_WINDOW:
+			printf("Linux 'widOpen' // CALLBACK_WINDOW !\n");
+			break;
+		case DCB_TASK:
+			printf("Linux 'widOpen' // CALLBACK_TASK !\n");
+			break;
+		case DCB_FUNCTION:
+			printf("Linux 'widOpen' // CALLBACK_FUNCTION !\n");
+			break;
+		}
+	WInDev[wDevID].lpQueueHdr = NULL;
+	WInDev[wDevID].unixdev = audio;
+	WInDev[wDevID].bufsize = abuf_size;
+	WInDev[wDevID].dwTotalRecorded = 0;
+	memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
+	if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
+		printf("Linux 'widOpen' // Bad format %04X !\n", 
+						lpDesc->lpFormat->wFormatTag);
+		return WAVERR_BADFORMAT;
+		}
+	memcpy(&WInDev[wDevID].Format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
+	WInDev[wDevID].Format.wBitsPerSample = 8; /* <-------------- */
+	if (WInDev[wDevID].Format.wf.nChannels == 0) return WAVERR_BADFORMAT;
+	if (WInDev[wDevID].Format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
+	if (WInDev[wDevID].Format.wBitsPerSample == 0) {
+		WInDev[wDevID].Format.wBitsPerSample = 8 *
+		(WInDev[wDevID].Format.wf.nAvgBytesPerSec /
+		WInDev[wDevID].Format.wf.nSamplesPerSec) /
+		WInDev[wDevID].Format.wf.nChannels;
+		}
+	samplesize = WInDev[wDevID].Format.wBitsPerSample;
+	smplrate = WInDev[wDevID].Format.wf.nSamplesPerSec;
+	dsp_stereo = (WInDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE;
+	IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
+	IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
+	IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
+#ifdef DEBUG_MCIWAVE
+	printf("Linux 'widOpen' // wBitsPerSample=%u !\n", 
+				WInDev[wDevID].Format.wBitsPerSample);
+	printf("Linux 'widOpen' // nSamplesPerSec=%u !\n", 
+				WInDev[wDevID].Format.wf.nSamplesPerSec);
+	printf("Linux 'widOpen' // nChannels=%u !\n", 
+				WInDev[wDevID].Format.wf.nChannels);
+	printf("Linux 'widOpen' // nAvgBytesPerSec=%u\n",
+			WInDev[wDevID].Format.wf.nAvgBytesPerSec); 
+#endif
+	if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
+		printf("Linux 'widOpen' // can't notify client !\n");
+		return MMSYSERR_INVALPARAM;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				widClose			[internal]
+*/
+DWORD widClose(WORD wDevID)
+{
+	printf("widClose(%u);\n", wDevID);
+	if (WInDev[wDevID].unixdev == 0) {
+		printf("Linux 'widClose' // can't close !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	close(WInDev[wDevID].unixdev);
+	WInDev[wDevID].unixdev = 0;
+	WInDev[wDevID].bufsize = 0;
+	if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
+		printf("Linux 'widClose' // can't notify client !\n");
+		return MMSYSERR_INVALPARAM;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				widAddBuffer		[internal]
+*/
+DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+	int			count	= 1;
+	LPWAVEHDR 	lpWIHdr;
+	printf("widAddBuffer(%u, %08X, %08X);\n", wDevID, lpWaveHdr, dwSize);
+	if (WInDev[wDevID].unixdev == 0) {
+		printf("Linux 'widAddBuffer' // can't do it !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	if (WInDev[wDevID].lpQueueHdr == NULL || 
+		!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
+		printf("Linux 'widAddBuffer' // never been prepared !\n");
+		return WAVERR_UNPREPARED;
+		}
+	if ((lpWaveHdr->dwFlags & WHDR_INQUEUE) &&
+		(WInDev[wDevID].lpQueueHdr != lpWaveHdr)) {
+		/* except if it's the one just prepared ... */
+		printf("Linux 'widAddBuffer' // header already in use !\n");
+		return WAVERR_STILLPLAYING;
+		}
+	lpWaveHdr->dwFlags |= WHDR_PREPARED;
+	lpWaveHdr->dwFlags |= WHDR_INQUEUE;
+	lpWaveHdr->dwFlags &= ~WHDR_DONE;
+	lpWaveHdr->dwBytesRecorded = 0;
+	/* added to the queue, except if it's the one just prepared ... */
+	if (WInDev[wDevID].lpQueueHdr != lpWaveHdr) {
+		lpWIHdr = WInDev[wDevID].lpQueueHdr;
+		while (lpWIHdr->lpNext != NULL) {
+			lpWIHdr = lpWIHdr->lpNext;
+			count++;
+			}
+		lpWIHdr->lpNext = lpWaveHdr;
+		count++;
+		}
+	printf("widAddBuffer // buffer added ! (now %u in queue)\n", count);
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				widPrepare			[internal]
+*/
+DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+	printf("widPrepare(%u, %08X, %08X);\n", wDevID, lpWaveHdr, dwSize);
+	if (WInDev[wDevID].unixdev == 0) {
+		printf("Linux 'widPrepare' // can't prepare !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	if (WInDev[wDevID].lpQueueHdr != NULL) {
+		printf("Linux 'widPrepare' // already prepare !\n");
+		return WAVERR_BADFORMAT;
+		}
+	WInDev[wDevID].dwTotalRecorded = 0;
+	WInDev[wDevID].lpQueueHdr = lpWaveHdr;
+	if (lpWaveHdr->dwFlags & WHDR_INQUEUE) return WAVERR_STILLPLAYING;
+	lpWaveHdr->dwFlags |= WHDR_PREPARED;
+	lpWaveHdr->dwFlags |= WHDR_INQUEUE;
+	lpWaveHdr->dwFlags &= ~WHDR_DONE;
+	lpWaveHdr->dwBytesRecorded = 0;
+	printf("Linux 'widPrepare' // header prepared !\n");
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				widUnprepare			[internal]
+*/
+DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+	printf("widUnprepare(%u, %08X, %08X);\n", wDevID, lpWaveHdr, dwSize);
+	if (WInDev[wDevID].unixdev == 0) {
+		printf("Linux 'widUnprepare' // can't unprepare !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
+	lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+	lpWaveHdr->dwFlags |= WHDR_DONE;
+	WInDev[wDevID].lpQueueHdr = NULL;
+	printf("Linux 'widUnprepare' // all headers unprepared !\n");
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				widStart				[internal]
+*/
+DWORD widStart(WORD wDevID)
+{
+	int			count	= 1;
+	LPWAVEHDR 	lpWIHdr;
+	printf("widStart(%u);\n", wDevID);
+	if (WInDev[wDevID].unixdev == 0) {
+		printf("Linux 'widStart' // can't start recording !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	if (WInDev[wDevID].lpQueueHdr == NULL || 
+		WInDev[wDevID].lpQueueHdr->lpData == NULL) {
+		printf("Linux 'widStart' // never been prepared !\n");
+		return WAVERR_UNPREPARED;
+		}
+	lpWIHdr = WInDev[wDevID].lpQueueHdr;
+	while(lpWIHdr != NULL) {
+		lpWIHdr->dwBufferLength &= 0xFFFF;
+		printf("widStart // recording buf#%u=%08X size=%u \n", 
+			count, lpWIHdr->lpData, lpWIHdr->dwBufferLength);
+		fflush(stdout);
+		read (WInDev[wDevID].unixdev, lpWIHdr->lpData,
+							lpWIHdr->dwBufferLength);
+		lpWIHdr->dwBytesRecorded = lpWIHdr->dwBufferLength;
+		WInDev[wDevID].dwTotalRecorded += lpWIHdr->dwBytesRecorded;
+		lpWIHdr->dwFlags &= ~WHDR_INQUEUE;
+		lpWIHdr->dwFlags |= WHDR_DONE;
+		if (WAVE_NotifyClient(wDevID, WIM_DATA, (DWORD)lpWIHdr, 0L) != 
+			MMSYSERR_NOERROR) {
+			printf("Linux 'widStart' // can't notify client !\n");
+			return MMSYSERR_INVALPARAM;
+			}
+		lpWIHdr = lpWIHdr->lpNext;
+		count++;
+		}
+	printf("widStart // end of recording !\n");
+	fflush(stdout);
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				widStop					[internal]
+*/
+DWORD widStop(WORD wDevID)
+{
+	printf("widStop(%u);\n", wDevID);
+	if (WInDev[wDevID].unixdev == 0) {
+		printf("Linux 'widStop' // can't stop !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				widReset				[internal]
+*/
+DWORD widReset(WORD wDevID)
+{
+	printf("widReset(%u);\n", wDevID);
+	if (WInDev[wDevID].unixdev == 0) {
+		printf("Linux 'widReset' // can't reset !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				widGetPosition			[internal]
+*/
+DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
+{
+	int		time;
+#ifdef DEBUG_MCIWAVE
+	printf("widGetPosition(%u, %08X, %u);\n", wDevID, lpTime, uSize);
+#endif
+	if (WInDev[wDevID].unixdev == 0) {
+		printf("Linux 'widGetPosition' // can't get pos !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	if (lpTime == NULL)	return MMSYSERR_INVALPARAM;
+TryAGAIN:
+#ifdef DEBUG_MCIWAVE
+	printf("widGetPosition // wType=%04X !\n", lpTime->wType);
+	printf("widGetPosition // wBitsPerSample=%u\n",
+			WInDev[wDevID].Format.wBitsPerSample); 
+	printf("widGetPosition // nSamplesPerSec=%u\n",
+			WInDev[wDevID].Format.wf.nSamplesPerSec); 
+	printf("widGetPosition // nChannels=%u\n",
+			WInDev[wDevID].Format.wf.nChannels); 
+	printf("widGetPosition // nAvgBytesPerSec=%u\n",
+			WInDev[wDevID].Format.wf.nAvgBytesPerSec); 
+ 	fflush(stdout);
+#endif
+	switch(lpTime->wType) {
+		case TIME_BYTES:
+			lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
+			printf("widGetPosition // TIME_BYTES=%u\n", lpTime->u.cb);
+			break;
+		case TIME_SAMPLES:
+			lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
+						WInDev[wDevID].Format.wBitsPerSample;
+			printf("widGetPosition // TIME_SAMPLES=%u\n", lpTime->u.sample);
+			break;
+		case TIME_MS:
+			lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
+					(WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
+			printf("widGetPosition // TIME_MS=%u\n", lpTime->u.ms);
+			break;
+		case TIME_SMPTE:
+			time = WInDev[wDevID].dwTotalRecorded /
+				(WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
+			lpTime->u.smpte.hour = time / 108000;
+			time -= lpTime->u.smpte.hour * 108000;
+			lpTime->u.smpte.min = time / 1800;
+			time -= lpTime->u.smpte.min * 1800;
+			lpTime->u.smpte.sec = time / 30;
+			time -= lpTime->u.smpte.sec * 30;
+			lpTime->u.smpte.frame = time;
+			lpTime->u.smpte.fps = 30;
+			printf("widGetPosition // TIME_SMPTE=%02u:%02u:%02u:%02u\n", 
+					lpTime->u.smpte.hour, lpTime->u.smpte.min,
+					lpTime->u.smpte.sec, lpTime->u.smpte.frame);
+			break;
+		default:
+			printf("widGetPosition() format not supported ! use TIME_MS !\n");
+			lpTime->wType = TIME_MS;
+			goto TryAGAIN;
+		}
+	return MMSYSERR_NOERROR;
 }
 
 /**************************************************************************
@@ -62,15 +1454,39 @@
 DWORD widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
 					DWORD dwParam1, DWORD dwParam2)
 {
+	printf("widMessage(%u, %04X, %08X, %08X, %08X);\n", 
+			wDevID, wMsg, dwUser, dwParam1, dwParam2);
+	switch(wMsg) {
+		case WIDM_OPEN:
+			return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
+		case WIDM_CLOSE:
+			return widClose(wDevID);
+		case WIDM_ADDBUFFER:
+			return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+		case WIDM_PREPARE:
+			return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+		case WIDM_UNPREPARE:
+			return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+		case WIDM_GETDEVCAPS:
+			return widGetDevCaps(wDevID, (LPWAVEINCAPS)dwParam1, dwParam2);
+		case WIDM_GETNUMDEVS:
+			return 1L;
+		case WIDM_GETPOS:
+			return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
+		case WIDM_RESET:
+			return widReset(wDevID);
+		case WIDM_START:
+			return widStart(wDevID);
+		case WIDM_STOP:
+			return widStop(wDevID);
+		}
+	return MMSYSERR_NOTSUPPORTED;
 }
 
-/**************************************************************************
-* 				auxMessage			[sample driver]
-*/
-DWORD auxMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
-					DWORD dwParam1, DWORD dwParam2)
-{
-}
+
+/*-----------------------------------------------------------------------*/
+
+
 
 /**************************************************************************
 * 				midMessage			[sample driver]
@@ -78,6 +1494,7 @@
 DWORD midMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
 					DWORD dwParam1, DWORD dwParam2)
 {
+	return MMSYSERR_NOTENABLED;
 }
 
 /**************************************************************************
@@ -86,12 +1503,7 @@
 DWORD modMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
 					DWORD dwParam1, DWORD dwParam2)
 {
+	return MMSYSERR_NOTENABLED;
 }
 
 
-/*
-BOOL DriverCallback(DWORD dwCallBack, UINT uFlags, HANDLE hDev, 
-		WORD wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);
-*/
-
-
diff --git a/misc/comm.c b/misc/comm.c
index 8f7f9bc..4c6edfb 100644
--- a/misc/comm.c
+++ b/misc/comm.c
@@ -18,21 +18,12 @@
 
 #include "wine.h"
 #include "windows.h"
+#include "comm.h"
 
 /* #define DEBUG_COMM /* */
 
-#define MAX_PORTS	16
-
 int commerror = 0, eventmask = 0;
 
-struct DosDeviceStruct {
-	char *devicename;	/* /dev/cua1 */
-	int fd;
-	int suspended;
-	int unget;
-	int unget_byte;
-};
-
 struct DosDeviceStruct COM[MAX_PORTS];
 struct DosDeviceStruct LPT[MAX_PORTS];
 
diff --git a/misc/dos_fs.c b/misc/dos_fs.c
index dc3b1bb..bb1d3e5 100644
--- a/misc/dos_fs.c
+++ b/misc/dos_fs.c
@@ -27,6 +27,7 @@
 #include "msdos.h"
 #include "prototypes.h"
 #include "autoconf.h"
+#include "comm.h"
 
 /* #define DEBUG /* */
 
@@ -170,6 +171,11 @@
 {
 	WORD equipment;
 	int diskdrives = 0;
+	int parallelports = 0;
+	int serialports = 0;
+	int x;
+	extern struct DosDeviceStruct COM[MAX_PORTS];
+	extern struct DosDeviceStruct LPT[MAX_PORTS];
 
 /* borrowed from Ralph Brown's interrupt lists 
 
@@ -190,6 +196,12 @@
 		    bit      1: =1 if math co-processor
 		    bit      0: =1 if diskette available for boot
 */
+/*  Currently the only of these bits correctly set are:
+		bits 15-14 		} Added by William Owen Smith, 
+		bits 11-9		} wos@dcs.warwick.ac.uk
+		bits 7-6
+		bit  2			(always set)
+*/
 
 	if (DosDrives[0].rootdir != NULL)
 		diskdrives++;
@@ -197,8 +209,27 @@
 		diskdrives++;
 	if (diskdrives)
 		diskdrives--;
+	
+	for (x=0; x!=MAX_PORTS; x++) {
+		if (COM[x].devicename)
+			serialports++;
+		if (LPT[x].devicename)
+			parallelports++;
+	}
+	if (serialports > 7)		/* 3 bits -- maximum value = 7 */
+		serialports=7;
+	if (parallelports > 3)		/* 2 bits -- maximum value = 3 */
+		parallelports=3;
 
-	equipment = (diskdrives << 6) || 0x02;
+	equipment = (diskdrives << 6) | (serialports << 9) | 
+		    (parallelports << 14) | 0x02;
+
+#ifdef DEBUG
+	fprintf(stderr, "DOS_GetEquipment : diskdrives = %d serialports = %d "
+			"parallelports = %d\n"
+			"DOS_GetEquipment : equipment = %d\n",
+			diskdrives, serialports, parallelports, equipment);
+#endif
 
 	return (equipment);
 }
diff --git a/misc/file.c b/misc/file.c
index e5d7271..29ed173 100644
--- a/misc/file.c
+++ b/misc/file.c
@@ -61,7 +61,7 @@
 /***************************************************************************
  _lread
  ***************************************************************************/
-INT _lread (INT hFile, LPSTR lpBuffer, INT wBytes)
+INT _lread (INT hFile, LPSTR lpBuffer, WORD wBytes)
 {
   int result;
 
@@ -81,7 +81,7 @@
 /****************************************************************************
  _lwrite
 ****************************************************************************/
-INT _lwrite (INT hFile, LPSTR lpBuffer, INT wBytes)
+INT _lwrite (INT hFile, LPSTR lpBuffer, WORD wBytes)
 {
 	int result;
 
diff --git a/misc/main.c b/misc/main.c
index f238568..4e94a7a 100644
--- a/misc/main.c
+++ b/misc/main.c
@@ -53,7 +53,6 @@
     FALSE,          /* usePrivateMap */
     FALSE,          /* synchronous */
     FALSE,          /* no backing store */
-    FALSE,          /* no save unders */
     SW_SHOWNORMAL,  /* cmdShow */
     FALSE
 };
@@ -69,7 +68,6 @@
     { "-privatemap",    ".privatemap",      XrmoptionNoArg,  (caddr_t)"on" },
     { "-synchronous",   ".synchronous",     XrmoptionNoArg,  (caddr_t)"on" },
     { "-nobackingstore",".nobackingstore",  XrmoptionNoArg,  (caddr_t)"on" },
-    { "-nosaveunders",  ".nosaveunders",    XrmoptionNoArg,  (caddr_t)"on" },
     { "-spy",           ".spy",             XrmoptionSepArg, (caddr_t)NULL },
     { "-debug",         ".debug",           XrmoptionNoArg,  (caddr_t)"on" },
     { "-relaydbg",      ".relaydbg",        XrmoptionNoArg,  (caddr_t)"on" }
@@ -90,7 +88,6 @@
   "    -privatemap     Use a private color map\n" \
   "    -synchronous    Turn on synchronous display mode\n" \
   "    -nobackingstore Turn off backing store\n" \
-  "    -nosaveunders   Turn off saveunders\n" \
   "    -spy file       Turn on message spying to the specified file\n" \
   "    -relaydbg       Display call relay information\n"
 
@@ -245,8 +242,6 @@
 	Options.usePrivateMap = TRUE;
     if (MAIN_GetResource( db, ".synchronous", &value ))
 	Options.synchronous = TRUE;
-    if (MAIN_GetResource( db, ".nosaveunders", &value ))
-	Options.nosaveunders = TRUE;
     if (MAIN_GetResource( db, ".nobackingstore", &value ))
 	Options.nobackingstore = TRUE;	
     if (MAIN_GetResource( db, ".relaydbg", &value ))
@@ -297,16 +292,11 @@
     else
        win_attr.backing_store = Always;
 
-    if (Options.nosaveunders)
-       win_attr.save_under = FALSE;
-    else
-       win_attr.save_under = TRUE;        
-
     rootWindow = XCreateWindow( display, DefaultRootWindow(display),
 			        desktopX, desktopY, width, height, 0,
 			        CopyFromParent, InputOutput, CopyFromParent,
-			        CWEventMask | CWCursor | CWSaveUnder |
-				CWBackingStore, &win_attr );
+			        CWEventMask | CWCursor |
+			        CWBackingStore, &win_attr );
 
       /* Set window manager properties */
 
diff --git a/misc/mcicda.c b/misc/mcicda.c
new file mode 100644
index 0000000..55497be
--- /dev/null
+++ b/misc/mcicda.c
@@ -0,0 +1,859 @@
+/*
+ * Sample MCI CDAUDIO Wine Driver for Linux
+ *
+ * Copyright 1994 Martin Ayotte
+ */
+
+static char Copyright[] = "Copyright  Martin Ayotte, 1994";
+
+/*
+#define DEBUG_CDAUDIO
+*/
+
+#include "stdio.h"
+#include "win.h"
+#include "user.h"
+#include "driver.h"
+#include "mmsystem.h"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/soundcard.h>
+#include <linux/cdrom.h>
+
+#define SOUND_DEV "/dev/dsp"
+#define CDAUDIO_DEV "/dev/sbpcd"
+
+#ifdef SOUND_VERSION
+#define IOCTL(a,b,c)		ioctl(a,b,&c)
+#else
+#define IOCTL(a,b,c)		(c = ioctl(a,b,c) )
+#endif
+
+#define MAX_CDAUDIODRV 		2
+#define MAX_CDAUDIO_TRACKS 	256
+
+#define CDFRAMES_PERSEC 	75
+#define CDFRAMES_PERMIN 	4500
+#define SECONDS_PERMIN	 	60
+
+typedef struct {
+    int     nUseCount;          /* Incremented for each shared open */
+    BOOL    fShareable;         /* TRUE if first open was shareable */
+    WORD    wNotifyDeviceID;    /* MCI device ID with a pending notification */
+    HANDLE  hCallback;          /* Callback handle for pending notification */
+	MCI_OPEN_PARMS openParms;
+	DWORD	dwTimeFormat;
+	int		unixdev;
+	struct cdrom_subchnl	sc;
+	int		mode;
+	UINT	nCurTrack;
+	DWORD	dwCurFrame;
+	UINT	nTracks;
+	DWORD	dwTotalLen;
+	LPDWORD	lpdwTrackLen;
+	LPDWORD	lpdwTrackPos;
+	DWORD	dwFirstOffset;
+	} LINUX_CDAUDIO;
+
+static LINUX_CDAUDIO	CDADev[MAX_CDAUDIODRV];
+
+UINT CDAUDIO_GetNumberOfTracks(UINT wDevID);
+BOOL CDAUDIO_GetTracksInfo(UINT wDevID);
+BOOL CDAUDIO_GetCDStatus(UINT wDevID);
+DWORD CDAUDIO_CalcTime(UINT wDevID, DWORD dwFormatType, DWORD dwFrame);
+
+
+/*-----------------------------------------------------------------------*/
+
+
+/**************************************************************************
+* 				CDAUDIO_mciOpen			[internal]
+*/
+DWORD CDAUDIO_mciOpen(DWORD dwFlags, LPMCI_OPEN_PARMS lpParms)
+{
+	UINT	wDevID;
+	int		cdrom;
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciOpen(%08X, %08X);\n", dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	wDevID = lpParms->wDeviceID;
+	if (CDADev[wDevID].nUseCount > 0) {
+		/* The driver already open on this channel */
+		/* If the driver was% op, ened shareable before and this open specifies */
+		/* shareable then increment the use count */
+		if (CDADev[wDevID].fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
+			++CDADev[wDevID].nUseCount;
+		else
+			return MCIERR_MUST_USE_SHAREABLE;
+		}
+	else {
+		CDADev[wDevID].nUseCount = 1;
+		CDADev[wDevID].fShareable = dwFlags & MCI_OPEN_SHAREABLE;
+		}
+    if (dwFlags & MCI_OPEN_ELEMENT) {
+		printf("CDAUDIO_mciOpen // MCI_OPEN_ELEMENT !\n");
+/*		return MCIERR_NO_ELEMENT_ALLOWED; */
+		}
+	memcpy(&CDADev[wDevID].openParms, lpParms, sizeof(MCI_OPEN_PARMS));
+	CDADev[wDevID].wNotifyDeviceID = lpParms->wDeviceID;
+	CDADev[wDevID].unixdev = open (CDAUDIO_DEV, O_RDONLY, 0);
+	if (CDADev[wDevID].unixdev == -1) {
+		printf("CDAUDIO_mciOpen // can't open '%s' !\n", CDAUDIO_DEV);
+		return MCIERR_HARDWARE;
+		}
+	CDADev[wDevID].mode = 0;
+	CDADev[wDevID].dwTimeFormat = MCI_FORMAT_TMSF;
+	CDADev[wDevID].nCurTrack = 0;
+	CDADev[wDevID].nTracks = 0;
+	CDADev[wDevID].dwTotalLen = 0;
+	CDADev[wDevID].dwFirstOffset = 0;
+	CDADev[wDevID].lpdwTrackLen = NULL;
+	CDADev[wDevID].lpdwTrackPos = NULL;
+	if (!CDAUDIO_GetTracksInfo(wDevID)) {
+		printf("CDAUDIO_mciOpen // error reading TracksInfo !\n");
+/*		return MCIERR_INTERNAL; */
+		}
+	if (dwFlags & MCI_NOTIFY) {
+		printf("CDAUDIO_mciOpen // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			CDADev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+ 	return 0;
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciClose		[internal]
+*/
+DWORD CDAUDIO_mciClose(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
+{
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciClose(%u, %08X, %08X);\n", wDevID, dwParam, lpParms);
+#endif
+	if (CDADev[wDevID].lpdwTrackLen != NULL) free(CDADev[wDevID].lpdwTrackLen);
+	if (CDADev[wDevID].lpdwTrackPos != NULL) free(CDADev[wDevID].lpdwTrackPos);
+	close(CDADev[wDevID].unixdev);
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciGetDevCaps	[internal]
+*/
+DWORD CDAUDIO_mciGetDevCaps(UINT wDevID, DWORD dwFlags, 
+						LPMCI_GETDEVCAPS_PARMS lpParms)
+{
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciGetDevCaps(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (dwFlags & MCI_GETDEVCAPS_ITEM) {
+		printf("CDAUDIO_mciGetDevCaps // MCI_GETDEVCAPS_ITEM dwItem=%08X);\n", 
+				lpParms->dwItem);
+		switch(lpParms->dwItem) {
+			case MCI_GETDEVCAPS_CAN_RECORD:
+				lpParms->dwReturn = FALSE;
+				break;
+			case MCI_GETDEVCAPS_HAS_AUDIO:
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_GETDEVCAPS_HAS_VIDEO:
+				lpParms->dwReturn = FALSE;
+				break;
+			case MCI_GETDEVCAPS_DEVICE_TYPE:
+				lpParms->dwReturn = MCI_DEVTYPE_CD_AUDIO;
+				break;
+			case MCI_GETDEVCAPS_USES_FILES:
+				lpParms->dwReturn = FALSE;
+				break;
+			case MCI_GETDEVCAPS_COMPOUND_DEVICE:
+				lpParms->dwReturn = FALSE;
+				break;
+			case MCI_GETDEVCAPS_CAN_EJECT:
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_GETDEVCAPS_CAN_PLAY:
+				lpParms->dwReturn = TRUE;
+				break;
+			case MCI_GETDEVCAPS_CAN_SAVE:
+				lpParms->dwReturn = FALSE;
+				break;
+			default:
+				return MCIERR_UNRECOGNIZED_COMMAND;
+			}
+		}
+	printf("CDAUDIO_mciGetDevCaps // lpParms->dwReturn=%08X);\n", lpParms->dwReturn);
+ 	return 0;
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciInfo			[internal]
+*/
+DWORD CDAUDIO_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMS lpParms)
+{
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciInfo(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	lpParms->lpstrReturn = NULL;
+	switch(dwFlags) {
+		case MCI_INFO_PRODUCT:
+			lpParms->lpstrReturn = "Linux CDROM 0.5";
+			break;
+		default:
+			return MCIERR_UNRECOGNIZED_COMMAND;
+		}
+	if (lpParms->lpstrReturn != NULL)
+		lpParms->dwRetSize = strlen(lpParms->lpstrReturn);
+	else
+		lpParms->dwRetSize = 0;
+ 	return 0;
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciStatus		[internal]
+*/
+DWORD CDAUDIO_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
+{
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciStatus(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (CDADev[wDevID].unixdev == 0) return MMSYSERR_NOTENABLED;
+	if (dwFlags & MCI_NOTIFY) {
+#ifdef DEBUG_CDAUDIO
+		printf("CDAUDIO_mciStatus // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+#endif
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			CDADev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+	if (dwFlags & MCI_STATUS_ITEM) {
+		switch(lpParms->dwItem) {
+			case MCI_STATUS_CURRENT_TRACK:
+				if (!CDAUDIO_GetCDStatus(wDevID)) return MCIERR_INTERNAL;
+				lpParms->dwReturn = CDADev[wDevID].nCurTrack;
+#ifdef DEBUG_CDAUDIO
+				printf("CDAUDIO_mciStatus // CURRENT_TRACK=%u!\n", lpParms->dwReturn);
+#endif
+			 	return 0;
+			case MCI_STATUS_LENGTH:
+				if (CDADev[wDevID].nTracks == 0) {
+					if (!CDAUDIO_GetTracksInfo(wDevID)) {
+						printf("CDAUDIO_mciStatus // error reading TracksInfo !\n");
+						return MCIERR_INTERNAL;
+						}
+					}
+				if (dwFlags & MCI_TRACK) {
+					printf("CDAUDIO_mciStatus // MCI_TRACK #%u LENGTH=??? !\n", 
+														lpParms->dwTrack);
+					if (lpParms->dwTrack > CDADev[wDevID].nTracks)
+						return MCIERR_OUTOFRANGE;
+					lpParms->dwReturn = CDADev[wDevID].lpdwTrackLen[lpParms->dwTrack];
+					}
+				else
+					lpParms->dwReturn = CDADev[wDevID].dwTotalLen;
+				lpParms->dwReturn = CDAUDIO_CalcTime(wDevID, 
+					CDADev[wDevID].dwTimeFormat, lpParms->dwReturn);
+				printf("CDAUDIO_mciStatus // LENGTH=%u !\n", lpParms->dwReturn);
+			 	return 0;
+			case MCI_STATUS_MODE:
+				if (!CDAUDIO_GetCDStatus(wDevID)) return MCIERR_INTERNAL;
+				lpParms->dwReturn = CDADev[wDevID].mode;
+#ifdef DEBUG_CDAUDIO
+				printf("CDAUDIO_mciStatus // MCI_STATUS_MODE=%08X !\n", 
+												lpParms->dwReturn);
+#endif
+			 	return 0;
+			case MCI_STATUS_MEDIA_PRESENT:
+				lpParms->dwReturn = (CDADev[wDevID].nTracks > 0) ? TRUE : FALSE;
+				if (lpParms->dwReturn == FALSE)
+					printf("CDAUDIO_mciStatus // MEDIA_NOT_PRESENT !\n");
+				else
+					printf("CDAUDIO_mciStatus // MCI_STATUS_MEDIA_PRESENT !\n");
+			 	return 0;
+			case MCI_STATUS_NUMBER_OF_TRACKS:
+				lpParms->dwReturn = CDAUDIO_GetNumberOfTracks(wDevID);
+				printf("CDAUDIO_mciStatus // MCI_STATUS_NUMBER_OF_TRACKS = %u !\n",
+													lpParms->dwReturn);
+				if (lpParms->dwReturn == (WORD)-1) return MCIERR_INTERNAL;
+			 	return 0;
+			case MCI_STATUS_POSITION:
+				if (!CDAUDIO_GetCDStatus(wDevID)) return MCIERR_INTERNAL;
+				lpParms->dwReturn = CDADev[wDevID].dwCurFrame;
+				if (dwFlags & MCI_STATUS_START) {
+					lpParms->dwReturn = CDADev[wDevID].dwFirstOffset;
+#ifdef DEBUG_CDAUDIO
+					printf("CDAUDIO_mciStatus // get MCI_STATUS_START !\n");
+#endif
+					}
+				if (dwFlags & MCI_TRACK) {
+					if (lpParms->dwTrack > CDADev[wDevID].nTracks)
+						return MCIERR_OUTOFRANGE;
+					lpParms->dwReturn = CDADev[wDevID].lpdwTrackPos[lpParms->dwTrack - 1];
+#ifdef DEBUG_CDAUDIO
+					printf("CDAUDIO_mciStatus // get MCI_TRACK #%u !\n", lpParms->dwTrack);
+#endif
+					}
+				lpParms->dwReturn = CDAUDIO_CalcTime(wDevID, 
+					CDADev[wDevID].dwTimeFormat, lpParms->dwReturn);
+#ifdef DEBUG_CDAUDIO
+				printf("CDAUDIO_mciStatus // MCI_STATUS_POSITION=%08X !\n",
+														lpParms->dwReturn);
+#endif
+			 	return 0;
+			case MCI_STATUS_READY:
+				printf("CDAUDIO_mciStatus // MCI_STATUS_READY !\n");
+				lpParms->dwReturn = TRUE;
+			 	return 0;
+			case MCI_STATUS_TIME_FORMAT:
+				printf("CDAUDIO_mciStatus // MCI_STATUS_TIME_FORMAT !\n");
+				lpParms->dwReturn = MCI_FORMAT_MILLISECONDS;
+			 	return 0;
+			default:
+				printf("CDAUDIO_mciStatus // unknowm command %04X !\n", lpParms->dwItem);
+				return MCIERR_UNRECOGNIZED_COMMAND;
+			}
+		}
+	printf("CDAUDIO_mciStatus // not MCI_STATUS_ITEM !\n");
+ 	return 0;
+}
+
+
+/**************************************************************************
+* 				CDAUDIO_CalcTime			[internal]
+*/
+DWORD CDAUDIO_CalcTime(UINT wDevID, DWORD dwFormatType, DWORD dwFrame)
+{
+	DWORD	dwTime = 0;
+	UINT	wTrack;
+	UINT	wMinutes;
+	UINT	wSeconds;
+	UINT	wFrames;
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_CalcTime(%u, %08X, %lu);\n", wDevID, dwFormatType, dwFrame);
+#endif
+TryAGAIN:
+	switch (dwFormatType) {
+		case MCI_FORMAT_MILLISECONDS:
+			dwTime = dwFrame / CDFRAMES_PERSEC * 1000;
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_CalcTime // MILLISECONDS %u\n", dwTime);
+#endif
+			break;
+		case MCI_FORMAT_MSF:
+			wMinutes = dwFrame / CDFRAMES_PERMIN;
+			wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
+			wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - 
+								CDFRAMES_PERSEC * wSeconds;
+			dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_CalcTime // MSF %02u:%02u:%02u -> dwTime=%u\n", 
+								wMinutes, wSeconds, wFrames, dwTime);
+#endif
+			break;
+		case MCI_FORMAT_TMSF:
+			for (wTrack = 0; wTrack < CDADev[wDevID].nTracks; wTrack++) {
+/*				dwTime += CDADev[wDevID].lpdwTrackLen[wTrack - 1];
+				printf("Adding trk#%u curpos=%u \n", dwTime);
+				if (dwTime >= dwFrame) break; */
+				if (CDADev[wDevID].lpdwTrackPos[wTrack - 1] >= dwFrame) break;
+				}
+			wMinutes = dwFrame / CDFRAMES_PERMIN;
+			wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
+			wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - 
+								CDFRAMES_PERSEC * wSeconds;
+			dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_CalcTime // %02u-%02u:%02u:%02u\n", 
+					wTrack, wMinutes, wSeconds, wFrames);
+#endif
+			break;
+		default:
+			/* unknown format ! force TMSF ! ... */
+			dwFormatType = MCI_FORMAT_TMSF;
+			goto TryAGAIN;
+		}
+	return dwTime;
+}
+
+
+/**************************************************************************
+* 				CDAUDIO_CalcFrame			[internal]
+*/
+DWORD CDAUDIO_CalcFrame(UINT wDevID, DWORD dwFormatType, DWORD dwTime)
+{
+	DWORD	dwFrame = 0;
+	UINT	wTrack;
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_CalcFrame(%u, %08X, %lu);\n", wDevID, dwFormatType, dwTime);
+#endif
+TryAGAIN:
+	switch (dwFormatType) {
+		case MCI_FORMAT_MILLISECONDS:
+			dwFrame = dwTime * CDFRAMES_PERSEC / 1000;
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_CalcFrame // MILLISECONDS %u\n", dwFrame);
+#endif
+			break;
+		case MCI_FORMAT_MSF:
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_CalcFrame // MSF %02u:%02u:%02u\n", 
+				MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), 
+				MCI_MSF_FRAME(dwTime));
+#endif
+			dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
+			dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
+			dwFrame += MCI_MSF_FRAME(dwTime);
+			break;
+		case MCI_FORMAT_TMSF:
+			wTrack = MCI_TMSF_TRACK(dwTime);
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_CalcFrame // TMSF %02u-%02u:%02u:%02u\n", 
+					MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime), 
+					MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
+			printf("CDAUDIO_CalcFrame // TMSF trackpos[%u]=%u\n",
+				wTrack, CDADev[wDevID].lpdwTrackPos[wTrack - 1]);
+#endif
+			dwFrame = CDADev[wDevID].lpdwTrackPos[wTrack - 1];
+			dwFrame += CDFRAMES_PERMIN * MCI_TMSF_MINUTE(dwTime);
+			dwFrame += CDFRAMES_PERSEC * MCI_TMSF_SECOND(dwTime);
+			dwFrame += MCI_TMSF_FRAME(dwTime);
+			break;
+		default:
+			/* unknown format ! force TMSF ! ... */
+			dwFormatType = MCI_FORMAT_TMSF;
+			goto TryAGAIN;
+		}
+	return dwFrame;
+}
+
+
+/**************************************************************************
+* 				CDAUDIO_GetNumberOfTracks		[internal]
+*/
+UINT CDAUDIO_GetNumberOfTracks(UINT wDevID)
+{
+	struct cdrom_tochdr	hdr;
+	if (CDADev[wDevID].nTracks == 0) {
+		if (ioctl(CDADev[wDevID].unixdev, CDROMREADTOCHDR, &hdr)) {
+			printf("GetNumberOfTracks(%u) // Error occured !\n", wDevID);
+			return (WORD)-1;
+			}
+		CDADev[wDevID].nTracks = hdr.cdth_trk1;
+		}
+	return CDADev[wDevID].nTracks;
+}
+
+/**************************************************************************
+* 				CDAUDIO_GetNumberOfTracks		[internal]
+*/
+BOOL CDAUDIO_GetTracksInfo(UINT wDevID)
+{
+	int		i, length;
+	int		start, last_start;
+	int		total_length = 0;
+	struct cdrom_tocentry	entry;
+	if (CDADev[wDevID].nTracks == 0) {
+		if (CDAUDIO_GetNumberOfTracks(wDevID) == (WORD)-1) return FALSE;
+		}
+	if (CDADev[wDevID].lpdwTrackLen != NULL) 
+		free(CDADev[wDevID].lpdwTrackLen);
+	CDADev[wDevID].lpdwTrackLen = (LPDWORD)malloc(
+		(CDADev[wDevID].nTracks + 1) * sizeof(DWORD));
+	if (CDADev[wDevID].lpdwTrackPos != NULL) 
+		free(CDADev[wDevID].lpdwTrackPos);
+	CDADev[wDevID].lpdwTrackPos = (LPDWORD)malloc(
+		(CDADev[wDevID].nTracks + 1) * sizeof(DWORD));
+	if (CDADev[wDevID].lpdwTrackLen == NULL ||
+		CDADev[wDevID].lpdwTrackPos == NULL) {
+		printf("CDAUDIO_GetTracksInfo // error allocating track table !\n");
+		return FALSE;
+		}
+	memset(CDADev[wDevID].lpdwTrackLen, 0, 
+		(CDADev[wDevID].nTracks + 1) * sizeof(DWORD));
+	memset(CDADev[wDevID].lpdwTrackPos, 0, 
+		(CDADev[wDevID].nTracks + 1) * sizeof(DWORD));
+	for (i = 0; i <= CDADev[wDevID].nTracks; i++) {
+		if (i == CDADev[wDevID].nTracks)
+			entry.cdte_track = CDROM_LEADOUT;
+		else
+			entry.cdte_track = i + 1;
+		entry.cdte_format = CDROM_MSF;
+		if (ioctl(CDADev[wDevID].unixdev, CDROMREADTOCENTRY, &entry)) {
+			printf("CDAUDIO_GetTracksInfo // error read entry\n");
+			return FALSE;
+			}
+		start = CDFRAMES_PERSEC * (SECONDS_PERMIN * 
+			entry.cdte_addr.msf.minute + entry.cdte_addr.msf.second) + 
+			entry.cdte_addr.msf.frame;
+		if (i == 0) {
+			CDADev[wDevID].dwFirstOffset = last_start = start;
+			printf("CDAUDIO_GetTracksInfo // dwFirstOffset=%u\n", start);
+			}
+		else {
+			length = start - last_start;
+			last_start = start;
+			start = last_start - length;
+			total_length += length;
+			CDADev[wDevID].lpdwTrackLen[i - 1] = length;
+			CDADev[wDevID].lpdwTrackPos[i - 1] = start;
+			printf("CDAUDIO_GetTracksInfo // track #%u start=%u len=%u\n", 
+													i, start, length);
+			}
+		}
+	CDADev[wDevID].dwTotalLen = total_length;
+	printf("CDAUDIO_GetTracksInfo // total_len=%u\n", total_length);
+	return TRUE;
+}
+
+
+/**************************************************************************
+* 				CDAUDIO_GetNumberOfTracks		[internal]
+*/
+BOOL CDAUDIO_GetCDStatus(UINT wDevID)
+{
+	int		oldmode = CDADev[wDevID].mode;
+	CDADev[wDevID].sc.cdsc_format = CDROM_MSF;
+	if (ioctl(CDADev[wDevID].unixdev, CDROMSUBCHNL, &CDADev[wDevID].sc)) {
+#ifdef DEBUG_CDAUDIO
+		printf("CDAUDIO_GetCDStatus // opened or no_media !\n");
+#endif
+		CDADev[wDevID].mode = MCI_MODE_OPEN;
+		return TRUE;
+		}
+	switch (CDADev[wDevID].sc.cdsc_audiostatus) {
+		case CDROM_AUDIO_INVALID:
+			printf("CDAUDIO_GetCDStatus // device doesn't support status !\n");
+			return FALSE;
+		case CDROM_AUDIO_NO_STATUS: 
+			CDADev[wDevID].mode = MCI_MODE_STOP;
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_GetCDStatus // MCI_MODE_STOP !\n");
+#endif
+			break;
+		case CDROM_AUDIO_PLAY: 
+			CDADev[wDevID].mode = MCI_MODE_PLAY;
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_GetCDStatus // MCI_MODE_PLAY !\n");
+#endif
+			break;
+		case CDROM_AUDIO_PAUSED:
+			CDADev[wDevID].mode = MCI_MODE_PAUSE;
+#ifdef DEBUG_CDAUDIO
+			printf("CDAUDIO_GetCDStatus // MCI_MODE_PAUSE !\n");
+#endif
+			break;
+		default:
+			printf("CDAUDIO_GetCDStatus // status=%02X !\n", 
+					CDADev[wDevID].sc.cdsc_audiostatus);
+		}
+	CDADev[wDevID].nCurTrack = CDADev[wDevID].sc.cdsc_trk;
+	CDADev[wDevID].dwCurFrame = 
+		CDFRAMES_PERMIN * CDADev[wDevID].sc.cdsc_absaddr.msf.minute +
+		CDFRAMES_PERSEC * CDADev[wDevID].sc.cdsc_absaddr.msf.second +
+		CDADev[wDevID].sc.cdsc_absaddr.msf.frame;
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_GetCDStatus // %02u-%02u:%02u:%02u \n", 
+		CDADev[wDevID].sc.cdsc_trk,
+		CDADev[wDevID].sc.cdsc_absaddr.msf.minute,
+		CDADev[wDevID].sc.cdsc_absaddr.msf.second,
+		CDADev[wDevID].sc.cdsc_absaddr.msf.frame);
+#endif
+	if (oldmode != CDADev[wDevID].mode && oldmode == MCI_MODE_OPEN) {
+		if (!CDAUDIO_GetTracksInfo(wDevID)) {
+			printf("CDAUDIO_GetCDStatus // error updating TracksInfo !\n");
+			return MCIERR_INTERNAL;
+			}
+		}
+	return TRUE;
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciPlay			[internal]
+*/
+DWORD CDAUDIO_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
+{
+	int 	start, end;
+	struct 	cdrom_msf	msf;
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciPlay(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (CDADev[wDevID].unixdev == 0) return MMSYSERR_NOTENABLED;
+	start = 0; 		end = CDADev[wDevID].dwTotalLen;
+	CDADev[wDevID].nCurTrack = 1;
+	if (dwFlags & MCI_FROM) {
+		start = CDAUDIO_CalcFrame(wDevID, 
+			CDADev[wDevID].dwTimeFormat, lpParms->dwFrom); 
+#ifdef DEBUG_CDAUDIO
+		printf("CDAUDIO_mciPlay // MCI_FROM=%08X -> %u \n", 
+								lpParms->dwFrom, start);
+#endif
+		}
+	if (dwFlags & MCI_TO) {
+		end = CDAUDIO_CalcFrame(wDevID, 
+			CDADev[wDevID].dwTimeFormat, lpParms->dwTo);
+#ifdef DEBUG_CDAUDIO
+		printf("CDAUDIO_mciPlay // MCI_TO=%08X -> %u \n", 
+								lpParms->dwTo, end);
+#endif
+		}
+	start += CDADev[wDevID].dwFirstOffset;	
+	end += CDADev[wDevID].dwFirstOffset;
+	msf.cdmsf_min0 = start / CDFRAMES_PERMIN;
+	msf.cdmsf_sec0 = (start % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
+	msf.cdmsf_frame0 = start % CDFRAMES_PERSEC;
+	msf.cdmsf_min1 = end / CDFRAMES_PERMIN;
+	msf.cdmsf_sec1 = (end % CDFRAMES_PERMIN) / CDFRAMES_PERSEC;
+	msf.cdmsf_frame1 = end % CDFRAMES_PERSEC;
+	if (ioctl(CDADev[wDevID].unixdev, CDROMSTART)) {
+		printf("CDAUDIO_mciPlay // motor doesn't start !\n");
+		return MCIERR_HARDWARE;
+		}
+	if (ioctl(CDADev[wDevID].unixdev, CDROMPLAYMSF, &msf)) {
+		printf("CDAUDIO_mciPlay // device doesn't play !\n");
+		return MCIERR_HARDWARE;
+		}
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciPlay // msf = %d:%d:%d %d:%d:%d\n",
+		msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
+		msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
+#endif
+	CDADev[wDevID].mode = MCI_MODE_PLAY;
+	if (dwFlags & MCI_NOTIFY) {
+		printf("CDAUDIO_mciPlay // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			CDADev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+	return 0;
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciStop			[internal]
+*/
+DWORD CDAUDIO_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciStop(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (ioctl(CDADev[wDevID].unixdev, CDROMSTOP)) return MCIERR_HARDWARE;
+	CDADev[wDevID].mode = MCI_MODE_STOP;
+	if (dwFlags & MCI_NOTIFY) {
+		printf("CDAUDIO_mciStop // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			CDADev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+ 	return 0;
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciPause		[internal]
+*/
+DWORD CDAUDIO_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciPause(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (ioctl(CDADev[wDevID].unixdev, CDROMPAUSE)) return MCIERR_HARDWARE;
+	CDADev[wDevID].mode = MCI_MODE_PAUSE;
+	if (dwFlags & MCI_NOTIFY) {
+		printf("CDAUDIO_mciPause // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			CDADev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+	return 0;
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciResume		[internal]
+*/
+DWORD CDAUDIO_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+{
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciResume(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (ioctl(CDADev[wDevID].unixdev, CDROMRESUME)) return MCIERR_HARDWARE;
+	CDADev[wDevID].mode = MCI_MODE_STOP;
+	if (dwFlags & MCI_NOTIFY) {
+		printf("CDAUDIO_mciResume // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			CDADev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+	return 0;
+}
+
+/**************************************************************************
+* 				CDAUDIO_mciSeek			[internal]
+*/
+DWORD CDAUDIO_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
+{
+	DWORD	dwRet;
+	MCI_PLAY_PARMS PlayParms;
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciSeek(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+	if (ioctl(CDADev[wDevID].unixdev, CDROMRESUME)) return MCIERR_HARDWARE;
+	CDADev[wDevID].mode = MCI_MODE_SEEK;
+	switch(dwFlags) {
+		case MCI_SEEK_TO_START:
+			PlayParms.dwFrom = 0;
+			break;
+		case MCI_SEEK_TO_END:
+			PlayParms.dwFrom = CDADev[wDevID].dwTotalLen;
+			break;
+		case MCI_TO:
+			PlayParms.dwFrom = lpParms->dwTo;
+			break;
+		}
+	dwRet = CDAUDIO_mciPlay(wDevID, MCI_WAIT | MCI_FROM, &PlayParms);
+	if (dwRet != 0) return dwRet;
+	dwRet = CDAUDIO_mciStop(wDevID, MCI_WAIT, (LPMCI_GENERIC_PARMS)&PlayParms);
+	if (dwFlags & MCI_NOTIFY) {
+		printf("CDAUDIO_mciSeek // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			CDADev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+	return dwRet;
+}
+
+
+/**************************************************************************
+* 				CDAUDIO_mciSet			[internal]
+*/
+DWORD CDAUDIO_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
+{
+#ifdef DEBUG_CDAUDIO
+	printf("CDAUDIO_mciSet(%u, %08X, %08X);\n", wDevID, dwFlags, lpParms);
+#endif
+	if (lpParms == NULL) return MCIERR_INTERNAL;
+/*
+	printf("CDAUDIO_mciSet // dwTimeFormat=%08X\n", lpParms->dwTimeFormat);
+	printf("CDAUDIO_mciSet // dwAudio=%08X\n", lpParms->dwAudio);
+*/
+	if (dwFlags & MCI_SET_TIME_FORMAT) {
+		switch (lpParms->dwTimeFormat) {
+			case MCI_FORMAT_MILLISECONDS:
+#ifdef DEBUG_CDAUDIO
+				printf("CDAUDIO_mciSet // MCI_FORMAT_MILLISECONDS !\n");
+#endif
+				break;
+			case MCI_FORMAT_MSF:
+#ifdef DEBUG_CDAUDIO
+				printf("CDAUDIO_mciSet // MCI_FORMAT_MSF !\n");
+#endif
+				break;
+			case MCI_FORMAT_TMSF:
+#ifdef DEBUG_CDAUDIO
+				printf("CDAUDIO_mciSet // MCI_FORMAT_TMSF !\n");
+#endif
+				break;
+			default:
+				printf("CDAUDIO_mciSet // bad time format !\n");
+				return MCIERR_BAD_TIME_FORMAT;
+			}
+		CDADev[wDevID].dwTimeFormat = lpParms->dwTimeFormat;
+		}
+	if (dwFlags & MCI_SET_DOOR_OPEN) {
+		printf("CDAUDIO_mciSet // MCI_SET_DOOR_OPEN !\n");
+		if (ioctl(CDADev[wDevID].unixdev, CDROMEJECT)) return MCIERR_HARDWARE;
+		CDADev[wDevID].nTracks = 0;
+		}
+	if (dwFlags & MCI_SET_DOOR_CLOSED) {
+		printf("CDAUDIO_mciSet // MCI_SET_DOOR_CLOSED !\n");
+		if (ioctl(CDADev[wDevID].unixdev, CDROMEJECT)) return MCIERR_HARDWARE;
+		CDADev[wDevID].nTracks = 0;
+		}
+	if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
+	if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
+	if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
+	if (dwFlags & MCI_NOTIFY) {
+		printf("CDAUDIO_mciSet // MCI_NOTIFY_SUCCESSFUL %08X !\n", lpParms->dwCallback);
+		mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			CDADev[wDevID].wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+		}
+	return 0;
+}
+
+
+/**************************************************************************
+* 				CDAUDIO_DriverProc		[sample driver]
+*/
+LRESULT CDAUDIO_DriverProc(DWORD dwDevID, HDRVR hDriv, WORD wMsg, 
+							DWORD dwParam1, DWORD dwParam2)
+{
+	switch(wMsg) {
+		case DRV_LOAD:
+			return (LRESULT)1L;
+		case DRV_FREE:
+			return (LRESULT)1L;
+		case DRV_OPEN:
+		case MCI_OPEN_DRIVER:
+		case MCI_OPEN:
+			return CDAUDIO_mciOpen(dwParam1, (LPMCI_OPEN_PARMS)dwParam2); 
+		case DRV_CLOSE:
+		case MCI_CLOSE_DRIVER:
+		case MCI_CLOSE:
+			return CDAUDIO_mciClose(dwDevID, dwParam1, 
+					(LPMCI_GENERIC_PARMS)dwParam2);
+		case DRV_ENABLE:
+			return (LRESULT)1L;
+		case DRV_DISABLE:
+			return (LRESULT)1L;
+		case DRV_QUERYCONFIGURE:
+			return (LRESULT)1L;
+		case DRV_CONFIGURE:
+			MessageBox((HWND)NULL, "Sample MultiMedia Linux Driver !", 
+								"MMLinux Driver", MB_OK);
+			return (LRESULT)1L;
+		case DRV_INSTALL:
+			return (LRESULT)DRVCNF_RESTART;
+		case DRV_REMOVE:
+			return (LRESULT)DRVCNF_RESTART;
+		case MCI_GETDEVCAPS:
+			return CDAUDIO_mciGetDevCaps(dwDevID, dwParam1, 
+					(LPMCI_GETDEVCAPS_PARMS)dwParam2);
+		case MCI_INFO:
+			return CDAUDIO_mciInfo(dwDevID, dwParam1, 
+						(LPMCI_INFO_PARMS)dwParam2);
+		case MCI_STATUS:
+			return CDAUDIO_mciStatus(dwDevID, dwParam1, 
+						(LPMCI_STATUS_PARMS)dwParam2);
+		case MCI_SET:
+			return CDAUDIO_mciSet(dwDevID, dwParam1, 
+						(LPMCI_SET_PARMS)dwParam2);
+		case MCI_PLAY:
+			return CDAUDIO_mciPlay(dwDevID, dwParam1, 
+						(LPMCI_PLAY_PARMS)dwParam2);
+		case MCI_STOP:
+			return CDAUDIO_mciStop(dwDevID, dwParam1, 
+					(LPMCI_GENERIC_PARMS)dwParam2);
+		case MCI_PAUSE:
+			return CDAUDIO_mciPause(dwDevID, dwParam1, 
+					(LPMCI_GENERIC_PARMS)dwParam2);
+		case MCI_RESUME:
+			return CDAUDIO_mciResume(dwDevID, dwParam1, 
+					(LPMCI_GENERIC_PARMS)dwParam2);
+		case MCI_SEEK:
+			return CDAUDIO_mciSeek(dwDevID, dwParam1, 
+					(LPMCI_SEEK_PARMS)dwParam2);
+		case MCI_SET_DOOR_OPEN:
+			printf("CDAUDIO_DriverProc // MCI_SET_DOOR_OPEN !\n");
+			if (ioctl(CDADev[dwDevID].unixdev, CDROMEJECT)) return MCIERR_HARDWARE;
+			CDADev[dwDevID].nTracks = 0;
+			return 0;
+		case MCI_SET_DOOR_CLOSED:
+			printf("CDAUDIO_DriverProc // MCI_SET_DOOR_CLOSED !\n");
+			if (ioctl(CDADev[dwDevID].unixdev, CDROMEJECT, 1)) return MCIERR_HARDWARE;
+			CDADev[dwDevID].nTracks = 0;
+			return 0;
+		default:
+			return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+		}
+}
+
+
+/*-----------------------------------------------------------------------*/
+
diff --git a/misc/mmaux.c b/misc/mmaux.c
new file mode 100644
index 0000000..64627d8
--- /dev/null
+++ b/misc/mmaux.c
@@ -0,0 +1,116 @@
+/*
+ * Sample AUXILARY Wine Driver for Linux
+ *
+ * Copyright 1994 Martin Ayotte
+ */
+
+static char Copyright[] = "Copyright  Martin Ayotte, 1994";
+
+#include "stdio.h"
+#include "win.h"
+#include "user.h"
+#include "driver.h"
+#include "mmsystem.h"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/soundcard.h>
+
+#define SOUND_DEV "/dev/dsp"
+#define MIXER_DEV "/dev/mixer"
+
+#ifdef SOUND_VERSION
+#define IOCTL(a,b,c)		ioctl(a,b,&c)
+#else
+#define IOCTL(a,b,c)		(c = ioctl(a,b,c) )
+#endif
+
+
+/*-----------------------------------------------------------------------*/
+
+
+/**************************************************************************
+* 				AUX_GetDevCaps			[internal]
+*/
+DWORD AUX_GetDevCaps(WORD wDevID, LPAUXCAPS lpCaps, DWORD dwSize)
+{
+	int 	mixer;
+	int		volume;
+	printf("AUX_GetDevCaps(%u, %08X, %u);\n", wDevID, lpCaps, dwSize);
+	if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+	if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+		printf("AUX_GetDevCaps // mixer device not available !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+    if (ioctl(mixer, SOUND_MIXER_READ_LINE, &volume) == -1) {
+		printf("AUX_GetDevCaps // unable read mixer !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	close(mixer);
+	return MMSYSERR_NOERROR;
+}
+
+
+/**************************************************************************
+* 				AUX_GetVolume			[internal]
+*/
+DWORD AUX_GetVolume(WORD wDevID, DWORD dwParam)
+{
+	int 	mixer;
+	int		volume;
+	printf("AUX_GetVolume(%u, %08X);\n", wDevID, dwParam);
+	if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+		printf("Linux 'AUX_GetVolume' // mixer device not available !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+    if (ioctl(mixer, SOUND_MIXER_READ_LINE, &volume) == -1) {
+		printf("Linux 'AUX_GetVolume' // unable read mixer !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	close(mixer);
+	return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+* 				AUX_SetVolume			[internal]
+*/
+DWORD AUX_SetVolume(WORD wDevID, DWORD dwParam)
+{
+	int 	mixer;
+	int		volume = 50;
+	printf("AUX_SetVolume(%u, %08X);\n", wDevID, dwParam);
+	if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+		printf("Linux 'AUX_SetVolume' // mixer device not available !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+    if (ioctl(mixer, SOUND_MIXER_WRITE_LINE, &volume) == -1) {
+		printf("Linux 'AUX_SetVolume' // unable set mixer !\n");
+		return MMSYSERR_NOTENABLED;
+		}
+	close(mixer);
+	return MMSYSERR_NOERROR;
+}
+
+
+/**************************************************************************
+* 				auxMessage			[sample driver]
+*/
+DWORD auxMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
+					DWORD dwParam1, DWORD dwParam2)
+{
+	printf("auxMessage(%u, %04X, %08X, %08X, %08X);\n", 
+			wDevID, wMsg, dwUser, dwParam1, dwParam2);
+	switch(wMsg) {
+		case AUXDM_GETDEVCAPS:
+			return AUX_GetDevCaps(wDevID, (LPAUXCAPS)dwParam1, dwParam2);
+		case AUXDM_GETNUMDEVS:
+			return 0L;
+		case AUXDM_GETVOLUME:
+			return AUX_GetVolume(wDevID, dwParam1);
+		case AUXDM_SETVOLUME:
+			return AUX_SetVolume(wDevID, dwParam1);
+		}
+	return MMSYSERR_NOTSUPPORTED;
+}
+
+
diff --git a/misc/mmsystem.c b/misc/mmsystem.c
index 90fc2de..da5ad6f 100644
--- a/misc/mmsystem.c
+++ b/misc/mmsystem.c
@@ -7,7 +7,10 @@
 static char Copyright[] = "Copyright  Martin Ayotte, 1993";
 
 #include "stdio.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
 #include "win.h"
+#include "user.h"
 #include "driver.h"
 #include "mmsystem.h"
 
@@ -31,6 +34,8 @@
 
 static LPTIMERENTRY lpTimerList = NULL;
 
+static MCI_OPEN_DRIVER_PARMS	mciDrv[MAXMCIDRIVERS];
+
 UINT WINAPI midiGetErrorText(UINT uError, LPSTR lpText, UINT uSize);
 UINT WINAPI waveGetErrorText(UINT uError, LPSTR lpText, UINT uSize);
 
@@ -50,9 +55,67 @@
 */
 BOOL WINAPI sndPlaySound(LPCSTR lpszSoundName, UINT uFlags)
 {
-	printf("EMPTY STUB !!! sndPlaySound // SoundName='%s' uFlags=%04X !\n", 
-										lpszSoundName, uFlags);
-	return 0;
+	int			hFile;
+	int			count;
+	WAVEHDR		WaveHdr;
+	PCMWAVEFORMAT	WaveFormat;
+	WAVEOPENDESC 	WaveDesc;
+	DWORD		dwRet;
+	char		str[128];
+	LPSTR		ptr;
+	printf("sndPlaySound // SoundName='%s' uFlags=%04X !\n", 
+									lpszSoundName, uFlags);
+	if (lpszSoundName == NULL) {
+		printf("sndPlaySound // Stop !\n");
+		return FALSE;
+		}
+	hFile = open(lpszSoundName, O_RDONLY);
+	if (hFile == 0) {
+		printf("sndPlaySound // searching in SystemSound List !\n");
+		GetProfileString("Sounds", (LPSTR)lpszSoundName, "", str, sizeof(str));
+		if (strlen(str) == 0) return FALSE;
+		if ((ptr = strchr(str, ',')) != NULL) *ptr = '\0';
+		hFile = open(str, O_RDONLY);
+		if (hFile == 0) {
+			printf("sndPlaySound // can't find SystemSound='%s' !\n", str);
+			return FALSE;
+			}
+		}
+	WaveDesc.hWave = 0;
+	WaveDesc.lpFormat = (LPWAVEFORMAT)&WaveFormat;
+	WaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;
+	WaveFormat.wBitsPerSample = 8;
+	WaveFormat.wf.nChannels = 1;
+	WaveFormat.wf.nSamplesPerSec = 11025;
+	WaveFormat.wf.nAvgBytesPerSec = 11025;
+	WaveFormat.wf.nBlockAlign = 1;
+	dwRet = wodMessage(0, WODM_OPEN, 0, (DWORD)&WaveDesc, CALLBACK_NULL);
+	if (dwRet != MMSYSERR_NOERROR) {
+		printf("sndPlaySound // can't open WaveOut device !\n");
+		return FALSE;
+		}
+	WaveHdr.lpData = (LPSTR) malloc(64000);
+	WaveHdr.dwBufferLength = 64000;
+	WaveHdr.dwUser = 0L;
+	WaveHdr.dwFlags = 0L;
+	WaveHdr.dwLoops = 0L;
+	dwRet = wodMessage(0, WODM_PREPARE, 0, (DWORD)&WaveHdr, sizeof(WAVEHDR));
+	if (dwRet != MMSYSERR_NOERROR) {
+		printf("sndPlaySound // can't prepare WaveOut device !\n");
+		return FALSE;
+		}
+	while(TRUE) {
+		count = read(hFile, WaveHdr.lpData, WaveHdr.dwBufferLength);
+		if (count == 0) break;
+		WaveHdr.dwBytesRecorded = count;
+		wodMessage(0, WODM_WRITE, 0, (DWORD)&WaveHdr, sizeof(WAVEHDR));
+		}
+	wodMessage(0, WODM_UNPREPARE, 0, (DWORD)&WaveHdr, sizeof(WAVEHDR));
+	wodMessage(0, WODM_CLOSE, 0, 0L, 0L);
+	free(WaveHdr.lpData);
+	close(hFile);
+
+	return TRUE;
 }
 
 /**************************************************************************
@@ -87,7 +150,23 @@
 BOOL DriverCallback(DWORD dwCallBack, UINT uFlags, HANDLE hDev, 
 		WORD wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
 {
-	printf("EMPTY STUB !!! DriverCallback() !\n");
+	printf("DriverCallback(%08X, %04X, %04X, %04X, %08X, %08X, %08X); !\n",
+		dwCallBack, uFlags, hDev, wMsg, dwUser, dwParam1, dwParam2);
+	switch(uFlags & DCB_TYPEMASK) {
+		case DCB_NULL:
+			printf("DriverCallback() // CALLBACK_NULL !\n");
+			break;
+		case DCB_WINDOW:
+			printf("DriverCallback() // CALLBACK_WINDOW !\n");
+			break;
+		case DCB_TASK:
+			printf("DriverCallback() // CALLBACK_TASK !\n");
+			break;
+		case DCB_FUNCTION:
+			printf("DriverCallback() // CALLBACK_FUNCTION !\n");
+			break;
+		}
+	return TRUE;
 }
 
 /**************************************************************************
@@ -170,8 +249,11 @@
 */
 UINT WINAPI auxGetNumDevs()
 {
+	UINT	count = 0;
 	printf("auxGetNumDevs !\n");
-	return 0;
+	count += auxMessage(0, AUXDM_GETNUMDEVS, 0L, 0L, 0L);
+	printf("auxGetNumDevs return %u \n", count);
+	return count;
 }
 
 /**************************************************************************
@@ -474,25 +556,32 @@
 
 
 /**************************************************************************
-* 				mciWaveOpen					[internal]
+* 				mciDriverNotify			[MMSYSTEM.711]
 */
-DWORD mciWaveOpen(UINT wDevID, DWORD dwParam, LPMCI_WAVE_OPEN_PARMS lpParms)
+BOOL WINAPI mciDriverNotify(HWND hWndCallBack, UINT wDevID, UINT wStatus)
 {
-	if (lpParms == NULL) return MCIERR_INTERNAL;
-	printf("mciWaveOpen(%04X, %08X, %08X)\n", wDevID, dwParam, lpParms);
-	return MCIERR_INTERNAL;
+	printf("mciDriverNotify(%04X, %u, %04X)\n", hWndCallBack, wDevID, wStatus);
+	PostMessage(hWndCallBack, MM_MCINOTIFY, wStatus, 
+			MAKELONG(mciDrv[wDevID].wDeviceID, 0));
+	return TRUE;
 }
 
-
 /**************************************************************************
 * 				mciOpen					[internal]
 */
-DWORD mciOpen(UINT wDevID, DWORD dwParam, LPMCI_OPEN_PARMS lpParms)
+DWORD mciOpen(DWORD dwParam, LPMCI_OPEN_PARMS lpParms)
 {
 	char	str[128];
 	DWORD	dwDevTyp = 0;
+	UINT	wDevID = 1;
+	printf("mciOpen(%08X, %08X)\n", dwParam, lpParms);
 	if (lpParms == NULL) return MCIERR_INTERNAL;
-	printf("mciOpen(%04X, %08X, %08X)\n", wDevID, dwParam, lpParms);
+	while(mciDrv[wDevID].wType != 0) {
+		if (++wDevID >= MAXMCIDRIVERS) {
+			printf("MCI_OPEN // MAXMCIDRIVERS reached !\n");
+			return MCIERR_INTERNAL;
+			}
+		}
 	if (dwParam & MCI_OPEN_TYPE) {
 		if (lpParms->lpstrDeviceType == NULL) return MCIERR_INTERNAL;
 		if (dwParam & MCI_OPEN_TYPE_ID) {
@@ -519,13 +608,17 @@
 				dwDevTyp = MCI_DEVTYPE_ANIMATION;
 				}
 			}
+		mciDrv[wDevID].wType = dwDevTyp;
+		mciDrv[wDevID].wDeviceID = 1;
+		lpParms->wDeviceID = wDevID;
+		printf("MCI_OPEN // wDeviceID=%04X !\n", lpParms->wDeviceID);
 		switch(dwDevTyp) {
 			case MCI_DEVTYPE_CD_AUDIO:
-				printf("MCI_OPEN // No SEQUENCER yet !\n");
-				return MCIERR_DEVICE_NOT_INSTALLED;
+				return CDAUDIO_DriverProc(0, 0, MCI_OPEN_DRIVER, 
+									dwParam, (DWORD)lpParms);
 			case MCI_DEVTYPE_WAVEFORM_AUDIO:
-				printf("MCI_OPEN // No WAVEAUDIO yet !\n");
-				return MCIERR_DEVICE_NOT_INSTALLED;
+				return WAVE_DriverProc(0, 0, MCI_OPEN_DRIVER, 
+									dwParam, (DWORD)lpParms);
 			case MCI_DEVTYPE_SEQUENCER:
 				printf("MCI_OPEN // No SEQUENCER yet !\n");
 				return MCIERR_DEVICE_NOT_INSTALLED;
@@ -539,19 +632,36 @@
 				printf("MCI_OPEN // Invalid Device Name '%08X' !\n", lpParms->lpstrDeviceType);
 				return MCIERR_INVALID_DEVICE_NAME;
 			}
-		lpParms->wDeviceID = ++mciActiveDev;
-		printf("MCI_OPEN // wDeviceID=%04X !\n", lpParms->wDeviceID);
-		return 0;
-		}
-	if (dwParam & MCI_OPEN_ELEMENT) {
-		printf("MCI_OPEN // Element !\n");
-		printf("MCI_OPEN // Elem=%s' !\n", lpParms->lpstrElementName);
 		}
 	return MCIERR_INTERNAL;
 }
 
 
 /**************************************************************************
+* 				mciClose				[internal]
+*/
+DWORD mciClose(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
+{
+	DWORD	dwRet = MCIERR_INTERNAL;
+	printf("mciClose(%u, %08X, %08X)\n", wDevID, dwParam, lpParms);
+	switch(mciDrv[wDevID].wType) {
+		case MCI_DEVTYPE_CD_AUDIO:
+			dwRet = CDAUDIO_DriverProc(mciDrv[wDevID].wDeviceID, 0, 
+						MCI_CLOSE, dwParam, (DWORD)lpParms);
+			break;
+		case MCI_DEVTYPE_WAVEFORM_AUDIO:
+			dwRet = WAVE_DriverProc(mciDrv[wDevID].wDeviceID, 0, 
+						MCI_CLOSE, dwParam, (DWORD)lpParms);
+			break;
+		default:
+			printf("mciClose() // unknown type=%04X !\n", mciDrv[wDevID].wType);
+		}
+	mciDrv[wDevID].wType = 0;
+	return dwRet;
+}
+
+
+/**************************************************************************
 * 				mciSound				[internal]
 */
 DWORD mciSound(UINT wDevID, DWORD dwParam, LPMCI_SOUND_PARMS lpParms)
@@ -563,106 +673,36 @@
 }
 
 
-/**************************************************************************
-* 				mciGetDevCaps			[internal]
-*/
-DWORD mciGetDevCaps(UINT wDevID, DWORD dwParam, LPMCI_GETDEVCAPS_PARMS lpParms)
-{
-	if (lpParms == NULL) return MCIERR_INTERNAL;
-	lpParms->dwReturn = 0;
-	return 0;
-}
-
-
-/**************************************************************************
-* 				mciInfo					[internal]
-*/
-DWORD mciInfo(UINT wDevID, DWORD dwParam, LPMCI_INFO_PARMS lpParms)
-{
-	if (lpParms == NULL) return MCIERR_INTERNAL;
-	lpParms->lpstrReturn = NULL;
-	lpParms->dwRetSize = 0;
-	return 0;
-}
-
-
-/**************************************************************************
-* 				mciStatus				[internal]
-*/
-DWORD mciStatus(UINT wDevID, DWORD dwParam, LPMCI_STATUS_PARMS lpParms)
-{
-	if (lpParms == NULL) return MCIERR_INTERNAL;
-	return 0;
-}
-
-
-/**************************************************************************
-* 				mciPlay					[internal]
-*/
-DWORD mciPlay(UINT wDevID, DWORD dwParam, LPMCI_PLAY_PARMS lpParms)
-{
-	if (lpParms == NULL) return MCIERR_INTERNAL;
-	return 0;
-}
-
-
-/**************************************************************************
-* 				mciRecord				[internal]
-*/
-DWORD mciRecord(UINT wDevID, DWORD dwParam, LPMCI_RECORD_PARMS lpParms)
-{
-	if (lpParms == NULL) return MCIERR_INTERNAL;
-	return 0;
-}
-
-
-/**************************************************************************
-* 				mciClose				[internal]
-*/
-DWORD mciClose(UINT wDevID)
-{
-	return 0;
-}
-
 
 /**************************************************************************
 * 				mciSendCommand			[MMSYSTEM.701]
 */
 DWORD mciSendCommand(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2)
 {
+	HDRVR	hDrv = 0;
+#ifdef DEBUG_MCI
 	printf("mciSendCommand(%04X, %04X, %08X, %08X)\n", 
 					wDevID, wMsg, dwParam1, dwParam2);
+#endif
 	switch(wMsg) {
 		case MCI_OPEN:
-			printf("mciSendCommand // MCI_OPEN !\n");
-			if (dwParam1 & MCI_WAVE_OPEN_BUFFER)
-				return mciWaveOpen(wDevID, dwParam1, 
-					(LPMCI_WAVE_OPEN_PARMS)dwParam2);
-			else
-				return mciOpen(wDevID, dwParam1, (LPMCI_OPEN_PARMS)dwParam2);
-		case MCI_PLAY:
-			printf("mciSendCommand // MCI_PLAY !\n");
-			return mciPlay(wDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
-		case MCI_RECORD:
-			printf("mciSendCommand // MCI_RECORD !\n");
-			return mciRecord(wDevID, dwParam1, (LPMCI_RECORD_PARMS)dwParam2);
+			return mciOpen(dwParam1, (LPMCI_OPEN_PARMS)dwParam2);
 		case MCI_CLOSE:
-			printf("mciSendCommand // MCI_CLOSE !\n");
-			return mciClose(wDevID);
-		case MCI_SOUND:
-			printf("mciSendCommand // MCI_SOUND !\n");
-			return mciSound(wDevID, dwParam1, (LPMCI_SOUND_PARMS)dwParam2);
-		case MCI_STATUS:
-			printf("mciSendCommand // MCI_STATUS !\n");
-			return mciStatus(wDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
-		case MCI_INFO:
-			printf("mciSendCommand // MCI_INFO !\n");
-			return mciInfo(wDevID, dwParam1, (LPMCI_INFO_PARMS)dwParam2);
-		case MCI_GETDEVCAPS:
-			printf("mciSendCommand // MCI_GETDEVCAPS !\n");
-			return mciGetDevCaps(wDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
+			return mciClose(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
+		default:
+			switch(mciDrv[wDevID].wType) {
+				case MCI_DEVTYPE_CD_AUDIO:
+					return CDAUDIO_DriverProc(mciDrv[wDevID].wDeviceID, hDrv, 
+											wMsg, dwParam1, dwParam2);
+				case MCI_DEVTYPE_WAVEFORM_AUDIO:
+					return WAVE_DriverProc(mciDrv[wDevID].wDeviceID, hDrv, 
+											wMsg, dwParam1, dwParam2);
+				default:
+					printf("mciSendCommand() // unknown type=%04X !\n", 
+											mciDrv[wDevID].wType);
+				}
 		}
-	return MCIERR_DEVICE_NOT_INSTALLED;
+	return MMSYSERR_INVALPARAM;
 }
 
 /**************************************************************************
@@ -670,7 +710,13 @@
 */
 UINT mciGetDeviceID (LPCSTR lpstrName)
 {
+	char	str[128];
 	printf("mciGetDeviceID(%s)\n", lpstrName);
+	if (lpstrName != NULL) {
+		strcpy(str, lpstrName);
+		AnsiUpper(str);
+		if (strcmp(str, "ALL") == 0) return MCI_ALL_DEVICE_ID;
+		}
 	return 0;
 }
 
@@ -1046,8 +1092,11 @@
 */
 UINT WINAPI waveOutGetNumDevs()
 {
+	UINT	count = 0;
 	printf("waveOutGetNumDevs\n");
-	return 1;
+	count += wodMessage(0, WODM_GETNUMDEVS, 0L, 0L, 0L);
+	printf("waveOutGetNumDevs return %u \n", count);
+	return count;
 }
 
 /**************************************************************************
@@ -1056,7 +1105,7 @@
 UINT WINAPI waveOutGetDevCaps(UINT uDeviceID, WAVEOUTCAPS FAR* lpCaps, UINT uSize)
 {
 	printf("waveOutGetDevCaps\n");
-	return MMSYSERR_INVALHANDLE;
+	return wodMessage(uDeviceID, WODM_GETDEVCAPS, 0L, (DWORD)lpCaps, uSize);
 }
 
 /**************************************************************************
@@ -1144,20 +1193,42 @@
 UINT WINAPI waveOutOpen(HWAVEOUT FAR* lphWaveOut, UINT uDeviceID,
     const LPWAVEFORMAT lpFormat, DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
 {
+	HWAVE	hWaveOut;
+	LPWAVEOPENDESC	lpDesc;
+	DWORD	dwRet;
+	BOOL	bMapperFlg = FALSE;
 	printf("waveOutOpen(%08X, %d, %08X, %08X, %08X, %08X);\n", 
 		lphWaveOut, uDeviceID, lpFormat, dwCallback, dwInstance, dwFlags);
 	if (dwFlags & WAVE_FORMAT_QUERY) {
-		printf("waveOutOpen	// WAVE_FORMAT_QUERY !\n");
-		if (uDeviceID == (UINT)WAVE_MAPPER) {
-			printf("waveOutOpen	// No WAVE_MAPPER supported yet !\n");
-			return MMSYSERR_BADDEVICEID;
-			}
+		printf("waveOutOpen	// WAVE_FORMAT_QUERY requested !\n");
+		}
+	if (uDeviceID == (UINT)WAVE_MAPPER) {
+		printf("waveOutOpen	// WAVE_MAPPER mode requested !\n");
+		bMapperFlg = TRUE;
+		uDeviceID = 0;
 		}
 	if (lpFormat == NULL) return WAVERR_BADFORMAT;
-	if (lphWaveOut != NULL) *lphWaveOut = 0;
-	if (lphWaveOut != NULL) *lphWaveOut = ++mciActiveDev;
-	return 0;
-/*	return MMSYSERR_BADDEVICEID;*/
+	hWaveOut = GlobalAlloc(GMEM_MOVEABLE, sizeof(WAVEOPENDESC));
+	if (lphWaveOut != NULL) *lphWaveOut = hWaveOut;
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveOut);
+	if (lpDesc == NULL) return MMSYSERR_NOMEM;
+	lpDesc->hWave = hWaveOut;
+	lpDesc->lpFormat = lpFormat;
+	lpDesc->dwCallBack = dwCallback;
+	lpDesc->dwInstance = dwInstance;
+	while(uDeviceID < MAXWAVEDRIVERS) {
+		dwRet = wodMessage(uDeviceID, WODM_OPEN, 
+			lpDesc->dwInstance, (DWORD)lpDesc, 0L);
+		if (dwRet == MMSYSERR_NOERROR) break;
+		if (!bMapperFlg) break;
+		uDeviceID++;
+		printf("waveOutOpen	// WAVE_MAPPER mode ! try next driver...\n");
+		}
+	if (dwFlags & WAVE_FORMAT_QUERY) {
+		printf("waveOutOpen	// End of WAVE_FORMAT_QUERY !\n");
+		waveOutClose(hWaveOut);
+		}
+	return dwRet;
 }
 
 /**************************************************************************
@@ -1165,8 +1236,11 @@
 */
 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
 {
-	printf("waveOutClose\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveOutClose(%04X)\n", hWaveOut);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveOut);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return wodMessage(0, WODM_CLOSE, lpDesc->dwInstance, 0L, 0L);
 }
 
 /**************************************************************************
@@ -1175,8 +1249,13 @@
 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
      WAVEHDR FAR* lpWaveOutHdr, UINT uSize)
 {
-	printf("waveOutPrepareHeader\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveOutPrepareHeader(%04X, %08X, %u);\n", 
+					hWaveOut, lpWaveOutHdr, uSize);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveOut);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return wodMessage(0, WODM_PREPARE, lpDesc->dwInstance, 
+							(DWORD)lpWaveOutHdr, uSize);
 }
 
 /**************************************************************************
@@ -1185,8 +1264,13 @@
 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
     WAVEHDR FAR* lpWaveOutHdr, UINT uSize)
 {
-	printf("waveOutUnprepareHeader\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveOutUnprepareHeader(%04X, %08X, %u);\n", 
+						hWaveOut, lpWaveOutHdr, uSize);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveOut);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return wodMessage(0, WODM_PREPARE, lpDesc->dwInstance, 
+							(DWORD)lpWaveOutHdr, uSize);
 }
 
 /**************************************************************************
@@ -1194,8 +1278,12 @@
 */
 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR FAR* lpWaveOutHdr,  UINT uSize)
 {
-	printf("waveOutWrite\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveOutWrite(%04X, %08X, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveOut);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return wodMessage(0, WODM_WRITE, lpDesc->dwInstance, 
+							(DWORD)lpWaveOutHdr, uSize);
 }
 
 /**************************************************************************
@@ -1203,7 +1291,7 @@
 */
 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
 {
-	printf("waveOutPause\n");
+	printf("waveOutPause(%04X)\n", hWaveOut);
 	return MMSYSERR_INVALHANDLE;
 }
 
@@ -1212,7 +1300,7 @@
 */
 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
 {
-	printf("waveOutRestart\n");
+	printf("waveOutRestart(%04X)\n", hWaveOut);
 	return MMSYSERR_INVALHANDLE;
 }
 
@@ -1221,17 +1309,21 @@
 */
 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
 {
-	printf("waveOutReset\n");
+	printf("waveOutReset(%04X)\n", hWaveOut);
 	return MMSYSERR_INVALHANDLE;
 }
 
 /**************************************************************************
 * 				waveOutGetPosition	[MMSYSTEM.412]
 */
-UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, MMTIME FAR* lpInfo, UINT uSize)
+UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, MMTIME FAR* lpTime, UINT uSize)
 {
-	printf("waveOutGetPosition\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveOutGetPosition(%04X, %08X, %u);\n", hWaveOut, lpTime, uSize);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveOut);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return wodMessage(0, WODM_GETPOS, lpDesc->dwInstance, 
+							(DWORD)lpTime, (DWORD)uSize);
 }
 
 /**************************************************************************
@@ -1293,7 +1385,7 @@
 */
 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
 {
-	printf("waveOutBreakLoop\n");
+	printf("waveOutBreakLoop(%04X)\n", hWaveOut);
 	return MMSYSERR_INVALHANDLE;
 }
 
@@ -1319,8 +1411,11 @@
 */
 UINT WINAPI waveInGetNumDevs()
 {
+	UINT	count = 0;
 	printf("waveInGetNumDevs\n");
-	return 0;
+	count += widMessage(0, WIDM_GETNUMDEVS, 0L, 0L, 0L);
+	printf("waveInGetNumDevs return %u \n", count);
+	return count;
 }
 
 
@@ -1330,7 +1425,7 @@
 UINT WINAPI waveInGetDevCaps(UINT uDeviceID, WAVEINCAPS FAR* lpCaps, UINT uSize)
 {
 	printf("waveInGetDevCaps\n");
-	return MMSYSERR_INVALHANDLE;
+	return widMessage(uDeviceID, WIDM_GETDEVCAPS, 0L, (DWORD)lpCaps, uSize);
 }
 
 
@@ -1350,18 +1445,42 @@
 UINT WINAPI waveInOpen(HWAVEIN FAR* lphWaveIn, UINT uDeviceID,
     const LPWAVEFORMAT lpFormat, DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
 {
+	HWAVE	hWaveIn;
+	LPWAVEOPENDESC	lpDesc;
+	DWORD	dwRet;
+	BOOL	bMapperFlg = FALSE;
 	printf("waveInOpen(%08X, %d, %08X, %08X, %08X, %08X);\n", 
 		lphWaveIn, uDeviceID, lpFormat, dwCallback, dwInstance, dwFlags);
 	if (dwFlags & WAVE_FORMAT_QUERY) {
-		printf("waveInOpen	// WAVE_FORMAT_QUERY !\n");
-		if (uDeviceID == (UINT)WAVE_MAPPER) {
-			printf("waveInOpen	// No WAVE_MAPPER supported yet !\n");
-			return MMSYSERR_BADDEVICEID;
-			}
+		printf("waveInOpen // WAVE_FORMAT_QUERY requested !\n");
 		}
-	if (lphWaveIn != NULL) *lphWaveIn = 0;
+	if (uDeviceID == (UINT)WAVE_MAPPER) {
+		printf("waveInOpen	// WAVE_MAPPER mode requested !\n");
+		bMapperFlg = TRUE;
+		uDeviceID = 0;
+		}
 	if (lpFormat == NULL) return WAVERR_BADFORMAT;
-	return MMSYSERR_BADDEVICEID;
+	hWaveIn = GlobalAlloc(GMEM_MOVEABLE, sizeof(WAVEOPENDESC));
+	if (lphWaveIn != NULL) *lphWaveIn = hWaveIn;
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_NOMEM;
+	lpDesc->hWave = hWaveIn;
+	lpDesc->lpFormat = lpFormat;
+	lpDesc->dwCallBack = dwCallback;
+	lpDesc->dwInstance = dwInstance;
+	while(uDeviceID < MAXWAVEDRIVERS) {
+		dwRet = widMessage(uDeviceID, WIDM_OPEN, 
+			lpDesc->dwInstance, (DWORD)lpDesc, 0L);
+		if (dwRet == MMSYSERR_NOERROR) break;
+		if (!bMapperFlg) break;
+		uDeviceID++;
+		printf("waveInOpen	// WAVE_MAPPER mode ! try next driver...\n");
+		}
+	if (dwFlags & WAVE_FORMAT_QUERY) {
+		printf("waveInOpen	// End of WAVE_FORMAT_QUERY !\n");
+		waveInClose(hWaveIn);
+		}
+	return dwRet;
 }
 
 
@@ -1370,8 +1489,11 @@
 */
 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
 {
-	printf("waveInClose\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveInClose(%04X)\n", hWaveIn);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return widMessage(0, WIDM_CLOSE, lpDesc->dwInstance, 0L, 0L);
 }
 
 
@@ -1381,8 +1503,18 @@
 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn,
     WAVEHDR FAR* lpWaveInHdr, UINT uSize)
 {
-	printf("waveInPrepareHeader\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveInPrepareHeader(%04X, %08X, %u);\n", 
+					hWaveIn, lpWaveInHdr, uSize);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
+	lpWaveInHdr->lpNext = NULL;
+    lpWaveInHdr->dwBytesRecorded = 0;
+	printf("waveInPrepareHeader // lpData=%08X size=%u \n", 
+		lpWaveInHdr->lpData, lpWaveInHdr->dwBufferLength);
+	return widMessage(0, WIDM_PREPARE, lpDesc->dwInstance, 
+							(DWORD)lpWaveInHdr, uSize);
 }
 
 
@@ -1392,20 +1524,36 @@
 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn,
     WAVEHDR FAR* lpWaveInHdr, UINT uSize)
 {
-	printf("waveInUnprepareHeader\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveInUnprepareHeader(%04X, %08X, %u);\n", 
+						hWaveIn, lpWaveInHdr, uSize);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	USER_HEAP_FREE(HIWORD((DWORD)lpWaveInHdr->lpData));
+	lpWaveInHdr->lpData = NULL;
+	lpWaveInHdr->lpNext = NULL;
+	return widMessage(0, WIDM_PREPARE, lpDesc->dwInstance, 
+							(DWORD)lpWaveInHdr, uSize);
 }
 
 
-
 /**************************************************************************
 * 				waveInAddBuffer		[MMSYSTEM.508]
 */
 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
     WAVEHDR FAR* lpWaveInHdr, UINT uSize)
 {
-	printf("waveInAddBuffer\n");
-	return 0;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveInAddBuffer(%04X, %08X, %u);\n", hWaveIn, lpWaveInHdr, uSize);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	if (lpWaveInHdr == NULL) return MMSYSERR_INVALHANDLE;
+	lpWaveInHdr->lpNext = NULL;
+    lpWaveInHdr->dwBytesRecorded = 0;
+	printf("waveInAddBuffer // lpData=%08X size=%u \n", 
+		lpWaveInHdr->lpData, lpWaveInHdr->dwBufferLength);
+	return widMessage(0, WIDM_ADDBUFFER, lpDesc->dwInstance,
+								(DWORD)lpWaveInHdr, uSize);
 }
 
 
@@ -1414,8 +1562,11 @@
 */
 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
 {
-	printf("waveInStart\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveInStart(%04X)\n", hWaveIn);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return widMessage(0, WIDM_START, lpDesc->dwInstance, 0L, 0L);
 }
 
 
@@ -1424,8 +1575,11 @@
 */
 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
 {
-	printf("waveInStop\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveInStop(%04X)\n", hWaveIn);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return widMessage(0, WIDM_STOP, lpDesc->dwInstance, 0L, 0L);
 }
 
 
@@ -1434,18 +1588,25 @@
 */
 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
 {
-	printf("waveInReset\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveInReset(%04X)\n", hWaveIn);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return widMessage(0, WIDM_RESET, lpDesc->dwInstance, 0L, 0L);
 }
 
 
 /**************************************************************************
 * 				waveInGetPosition	[MMSYSTEM.512]
 */
-UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, MMTIME FAR* lpInfo, UINT uSize)
+UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, MMTIME FAR* lpTime, UINT uSize)
 {
-	printf("waveInGetPosition\n");
-	return MMSYSERR_INVALHANDLE;
+	LPWAVEOPENDESC	lpDesc;
+	printf("waveInGetPosition(%04X, %08X, %u);\n", hWaveIn, lpTime, uSize);
+	lpDesc = (LPWAVEOPENDESC) GlobalLock(hWaveIn);
+	if (lpDesc == NULL) return MMSYSERR_INVALHANDLE;
+	return widMessage(0, WIDM_GETPOS, lpDesc->dwInstance,
+							(DWORD)lpTime, (DWORD)uSize);
 }
 
 
@@ -1455,6 +1616,7 @@
 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT FAR* lpuDeviceID)
 {
 	printf("waveInGetID\n");
+	if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
 	return 0;
 }
 
@@ -1540,7 +1702,7 @@
 	printf("timeSetEvent(%u, %u, %08X, %08X, %04X);\n",
 			wDelay, wResol, lpFunc, dwUser, wFlags);
 	if (!mmTimeStarted) StartMMTime();
-	lpNewTimer = malloc(sizeof(TIMERENTRY));
+	lpNewTimer = (LPTIMERENTRY) malloc(sizeof(TIMERENTRY));
 	if (lpNewTimer == NULL)	return 0;
 	while (lpTimer != NULL) {
 		wNewID = max(wNewID, lpTimer->wTimerID);
@@ -1628,8 +1790,10 @@
 */
 HMMIO WINAPI mmioOpen(LPSTR szFileName, MMIOINFO FAR* lpmmioinfo, DWORD dwOpenFlags)
 {
+	int		hFile;
 	printf("mmioOpen('%s', %08X, %08X);\n", szFileName, lpmmioinfo, dwOpenFlags);
-	return 0;
+	hFile = _lopen(szFileName, dwOpenFlags);
+	return (HMMIO)hFile;
 }
 
 
@@ -1640,6 +1804,7 @@
 UINT WINAPI mmioClose(HMMIO hmmio, UINT uFlags)
 {
 	printf("mmioClose(%04X, %04X);\n", hmmio, uFlags);
+	_lclose(hmmio);
 	return 0;
 }
 
@@ -1651,6 +1816,7 @@
 LONG WINAPI mmioRead(HMMIO hmmio, HPSTR pch, LONG cch)
 {
 	printf("mmioRead\n");
+	_lread(hmmio, pch, cch);
 	return 0;
 }
 
@@ -1815,8 +1981,10 @@
 */
 LRESULT WINAPI DrvSendMessage(HDRVR hDriver, WORD msg, LPARAM lParam1, LPARAM lParam2)
 {
+	DWORD 	dwDevID = 0;
 	printf("DrvSendMessage(%04X, %04X, %08X, %08X);\n",
 					hDriver, msg, lParam1, lParam2);
+	return CDAUDIO_DriverProc(dwDevID, hDriver, msg, lParam1, lParam2);
 }
 
 /**************************************************************************
diff --git a/miscemu/int2f.c b/miscemu/int2f.c
index 0a7d14e..046337c 100644
--- a/miscemu/int2f.c
+++ b/miscemu/int2f.c
@@ -3,6 +3,9 @@
 #include "msdos.h"
 #include "wine.h"
 
+int do_int2f_16(struct sigcontext_struct *context);
+
+
 int do_int2f(struct sigcontext_struct *context)
 {
 	switch((context->sc_eax >> 8) & 0xff)
@@ -12,17 +15,41 @@
 		return 1;
 
 	case 0x16:
-		switch(context->sc_eax & 0xff)
-		{
-		case 0x00: /* windows enhanced mode install check */
-			   /* don't return anything as we're running in standard mode */
-			return 1;
-
-		default:
-		}
+		return do_int2f_16(context);
 
 	default:
 		IntBarf(0x2f, context);
 	};
 	return 1;
 }
+
+
+int do_int2f_16(struct sigcontext_struct *context)
+{
+	switch(context->sc_eax & 0xff) {
+		case 0x00: 
+			/* return 'major/minor' for MSWin 3.1 */
+			printf("do_int2f_16 // return 'major/minor' for MSWin 3.1 !\n");
+			context->sc_eax = 0x0310;
+			return 1;
+		case 0x86: 
+			/* operating in protected mode under DPMI */
+			printf("do_int2f_16 // operating in protected mode under DPMI !\n");
+			context->sc_eax = 0x0000;
+			return 1;
+		case 0x87: 
+			printf("do_int2f_16 // return DPMI flags !\n");
+			context->sc_eax = 0x0000;		/* DPMI Installed */
+			context->sc_ebx = 0x0001;		/* 32bits available */
+			context->sc_ecx = 0x04;			/* processor 486 */
+			context->sc_edx = 0x0100;		/* DPMI major/minor */
+			context->sc_esi = 0;			/* # of para. of DOS */
+											/* extended private data */
+			return 1;
+		default:
+			IntBarf(0x2f, context);
+		}
+	return 1;
+}
+
+
diff --git a/windows/class.c b/windows/class.c
index 753ee0d..622afe1 100644
--- a/windows/class.c
+++ b/windows/class.c
@@ -91,7 +91,8 @@
 {
     CLASS * newClass, * prevClassPtr;
     HCLASS handle, prevClass;
-    
+    int classExtra;
+
 #ifdef DEBUG_CLASS
     printf( "RegisterClass: wndproc=%08x hinst=%d name='%s' background %x\n", 
 	    class->lpfnWndProc, class->hInstance, class->lpszClassName,
@@ -110,20 +111,18 @@
 	if (!(prevClassPtr->wc.style & CS_GLOBALCLASS)) return 0;
     }
 
-      /* bug for bug compatible */
-
-    if (class->cbClsExtra < 0) class->cbClsExtra = 0;
-    if (class->cbWndExtra < 0) class->cbWndExtra = 0;
-
       /* Create class */
 
-    handle = USER_HEAP_ALLOC( GMEM_MOVEABLE, sizeof(CLASS)+class->cbClsExtra );
+    classExtra = (class->cbClsExtra < 0) ? 0 : class->cbClsExtra;
+    handle = USER_HEAP_ALLOC( GMEM_MOVEABLE, sizeof(CLASS) + classExtra );
     if (!handle) return 0;
     newClass = (CLASS *) USER_HEAP_ADDR( handle );
-    newClass->hNext      = firstClass;
-    newClass->wMagic     = CLASS_MAGIC;
-    newClass->cWindows   = 0;  
-    newClass->wc         = *class;
+    newClass->hNext         = firstClass;
+    newClass->wMagic        = CLASS_MAGIC;
+    newClass->cWindows      = 0;  
+    newClass->wc            = *class;
+    newClass->wc.cbWndExtra = (class->cbWndExtra < 0) ? 0 : class->cbWndExtra;
+    newClass->wc.cbClsExtra = classExtra;
 
     if (newClass->wc.style & CS_GLOBALCLASS)
 	newClass->atomName = GlobalAddAtom( class->lpszClassName );
@@ -147,7 +146,7 @@
 	}
     }
 
-    if (class->cbClsExtra) memset( newClass->wExtra, 0, class->cbClsExtra );
+    if (classExtra) memset( newClass->wExtra, 0, classExtra );
     firstClass = handle;
     return newClass->atomName;
 }
diff --git a/windows/dc.c b/windows/dc.c
index 947372d..056329a 100644
--- a/windows/dc.c
+++ b/windows/dc.c
@@ -105,7 +105,6 @@
     SelectObject( hdc, dc->w.hFont );
     XSetGraphicsExposures( XT_display, dc->u.x.gc, False );
     CLIPPING_SetDeviceClipping( dc );
-    FONT_SelectObject(dc, STOCK_SYSTEM_FIXED_FONT, NULL);
 }
 
 
@@ -204,7 +203,7 @@
 
     if (!dc->u.x.font.fstruct)
     {
-	FONT_SelectObject(dc, STOCK_SYSTEM_FIXED_FONT, NULL);
+	FONT_SelectObject(dc, STOCK_SYSTEM_FONT, NULL);
     }
     val.function   = DC_XROPfunction[dc->w.ROPmode-1];
     val.foreground = dc->w.textPixel;
@@ -235,6 +234,7 @@
 #endif
 
     memcpy( &newdc->w, &dc->w, sizeof(dc->w) );
+    memcpy( &newdc->u.x.pen, &dc->u.x.pen, sizeof(dc->u.x.pen) );
     newdc->saveLevel = 0;
     newdc->w.flags |= DC_SAVED;
 
diff --git a/windows/dialog.c b/windows/dialog.c
index 7edc656..47ad5b4 100644
--- a/windows/dialog.c
+++ b/windows/dialog.c
@@ -595,13 +595,13 @@
  */
 void CheckRadioButton( HWND hwndDlg, WORD firstID, WORD lastID, WORD checkID )
 {
-    HWND button = GetDlgItem( hwndDlg, firstID );
+    HWND button = GetDlgItem( hwndDlg, lastID );
     while (button != 0)
     {
 	WND * wndPtr = WIN_FindWndPtr( button );
 	if (!wndPtr) break;
 	SendMessage( button, BM_SETCHECK, (wndPtr->wIDmenu == checkID), 0 );
-        if (wndPtr->wIDmenu == lastID) break;
+        if (wndPtr->wIDmenu == firstID) break;
 	button = wndPtr->hwndNext;
     }
 }
diff --git a/windows/graphics.c b/windows/graphics.c
index 0d2392f..c21eff3 100644
--- a/windows/graphics.c
+++ b/windows/graphics.c
@@ -153,6 +153,8 @@
 			 (double)(xend-xcenter)*(bottom-top) );
     diff_angle  = end_angle - start_angle;
     if (diff_angle < 0.0) diff_angle += 2*PI;
+    if (left > right) swap_int( &left, &right );
+    if (top > bottom) swap_int( &top, &bottom );
 
     XDrawArc( display, dc->u.x.drawable, dc->u.x.gc,
 	      dc->w.DCOrgX + left, dc->w.DCOrgY + top,
diff --git a/windows/painting.c b/windows/painting.c
index 65a17ca..abac9b5 100644
--- a/windows/painting.c
+++ b/windows/painting.c
@@ -45,13 +45,6 @@
     if (!(wndPtr->flags & WIN_ERASE_UPDATERGN)) lps->fErase = TRUE;
     else lps->fErase = !SendMessage( hwnd, WM_ERASEBKGND, lps->hdc, 0 );
 
-    /*
-     * a BeginPaint should return with these objects set by default
-     */
-    SelectObject(lps->hdc, STOCK_BLACK_PEN);
-    SelectObject(lps->hdc, STOCK_WHITE_BRUSH);
-    SelectObject(lps->hdc, STOCK_SYSTEM_FONT);
-    
     return lps->hdc;
 }
 
diff --git a/windows/win.c b/windows/win.c
index cc11dd1..d768512 100644
--- a/windows/win.c
+++ b/windows/win.c
@@ -253,7 +253,10 @@
 				exStyle, className, windowName, style, x, y, width, height, 
 				parent, menu, instance, data);
 #endif
-
+	/* 'soundrec.exe' has negative position ! 
+	Why ? For now, here a patch : */
+	if (x < 0) x = 0;
+	if (y < 0) y = 0;
     if (x == CW_USEDEFAULT) x = y = 0;
     if (width == CW_USEDEFAULT)
     {
@@ -366,24 +369,15 @@
     else
        win_attr.backing_store = Always;
 
-    if (Options.nosaveunders)
-       win_attr.save_under = FALSE;
-    else
-       win_attr.save_under = TRUE;        
+    win_attr.save_under = ((classPtr->wc.style & CS_SAVEBITS) != 0);
 
-
-    /* set the background of all windows to be white, just like
-     * MS-Windows does (hopefully!)
-     */   
-    win_attr.background_pixel = WhitePixelOfScreen(screen);
-    
     wndPtr->window = XCreateWindow( display, parentPtr->window,
 		   x + parentPtr->rectClient.left - parentPtr->rectWindow.left,
 		   y + parentPtr->rectClient.top - parentPtr->rectWindow.top,
 		   width, height, 0,
 		   CopyFromParent, InputOutput, CopyFromParent,
 		   CWEventMask | CWOverrideRedirect | CWColormap |
-		   CWSaveUnder | CWBackingStore | CWBackPixel, &win_attr );
+		   CWSaveUnder | CWBackingStore, &win_attr );
     XStoreName( display, wndPtr->window, windowName );