crypt32: Remove an unnecessary if.
[wine] / dlls / winedos / int2f.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * DOS interrupt 2fh handler
4  *
5  *  Cdrom - device driver emulation - Audio features.
6  *     Copyright (c) 1998 Petr Tomasek <tomasek@etf.cuni.cz>
7  *     Copyright (c) 1999,2002 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32 #include "wine/winbase16.h"
33 #include "wine/debug.h"
34 #include "winternl.h"
35 #include "winioctl.h"
36 #include "ntddstor.h"
37 #include "ntddcdrm.h"
38 #include "dosexe.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(int);
41
42 /* base WPROCS.DLL ordinal number for VxDs */
43 #define VXD_BASE 400
44
45 typedef struct
46 {
47     DOS_DEVICE_HEADER hdr;
48
49     WORD  reserved;             /* must be 0 */
50     BYTE  drive;                /* drive letter (0=A:, 1=B:, ...) */
51     BYTE  units;                /* number of supported units */
52 } CDROM_DEVICE_HEADER;
53
54 typedef struct
55 {
56     CDROM_DEVICE_HEADER hdr;
57     WINEDEV_THUNK thunk;
58
59     WORD  cdrom_segment;        /* Real mode segment for CDROM_HEAP */
60     WORD  cdrom_selector;       /* Protected mode selector for CDROM_HEAP */
61 } CDROM_HEAP;
62
63 static void do_int2f_16( CONTEXT86 *context );
64 static void MSCDEX_Handler( CONTEXT86 *context );
65
66 /***********************************************************************
67  *         GetVersion   (KERNEL.3)
68  *
69  * FIXME: Duplicated from kernel since it's a 16-bit function.
70  */
71 DWORD WINAPI GetVersion16(void)
72 {
73     static WORD dosver, winver;
74
75     if (!dosver)  /* not determined yet */
76     {
77         RTL_OSVERSIONINFOEXW info;
78
79         info.dwOSVersionInfoSize = sizeof(info);
80         if (RtlGetVersion( &info )) return 0;
81
82         if (info.dwMajorVersion <= 3)
83             winver = MAKEWORD( info.dwMajorVersion, info.dwMinorVersion );
84         else
85             winver = MAKEWORD( 3, 95 );
86
87         switch(info.dwPlatformId)
88         {
89         case VER_PLATFORM_WIN32s:
90             switch(MAKELONG( info.dwMinorVersion, info.dwMajorVersion ))
91             {
92             case 0x0200:
93                 dosver = 0x0303;  /* DOS 3.3 for Windows 2.0 */
94                 break;
95             case 0x0300:
96                 dosver = 0x0500;  /* DOS 5.0 for Windows 3.0 */
97                 break;
98             default:
99                 dosver = 0x0616;  /* DOS 6.22 for Windows 3.1 and later */
100                 break;
101             }
102             break;
103         case VER_PLATFORM_WIN32_WINDOWS:
104             /* DOS 8.0 for WinME, 7.0 for Win95/98 */
105             if (info.dwMinorVersion >= 90) dosver = 0x0800;
106             else dosver = 0x0700;
107             break;
108         case VER_PLATFORM_WIN32_NT:
109             dosver = 0x0500;  /* always DOS 5.0 for NT */
110             break;
111         }
112         TRACE( "DOS %d.%02d Win %d.%02d\n",
113                HIBYTE(dosver), LOBYTE(dosver), LOBYTE(winver), HIBYTE(winver) );
114     }
115     return MAKELONG( winver, dosver );
116 }
117
118 /**********************************************************************
119  *          DOSVM_Int2fHandler (WINEDOS16.147)
120  *
121  * Handler for int 2fh (multiplex).
122  */
123 void WINAPI DOSVM_Int2fHandler( CONTEXT86 *context )
124 {
125     TRACE("Subfunction 0x%X\n", AX_reg(context));
126
127     switch(AH_reg(context))
128     {
129     case 0x10:
130         SET_AL( context, 0xff ); /* share is installed */
131         break;
132
133     case 0x11:  /* Network Redirector / IFSFUNC */
134         switch (LOBYTE(context->Eax))
135         {
136         case 0x00:  /* Install check */
137             /* not installed */
138             break;
139         case 0x80:  /* Enhanced services - Install check */
140             /* not installed */
141             break;
142         default:
143            INT_BARF( context, 0x2f );
144             break;
145         }
146         break;
147
148     case 0x12:
149         switch (LOBYTE(context->Eax))
150         {
151         case 0x2e: /* get or set DOS error table address */
152             switch (DL_reg(context))
153             {
154             /* Four tables: even commands are 'get', odd are 'set' */
155             /* DOS 5.0+ ignores "set" commands */
156             case 0x01:
157             case 0x03:
158             case 0x05:
159             case 0x07:
160             case 0x09:
161                 break;
162             /* Instead of having a message table in DOS-space, */
163             /* we can use a special case for MS-DOS to force   */
164             /* the secondary interface.                               */
165             case 0x00:
166             case 0x02:
167             case 0x04:
168             case 0x06:
169                 context->SegEs = 0x0001;
170                 SET_DI( context, 0x0000 );
171                 break;
172             case 0x08:
173                 FIXME("No real-mode handler for errors yet! (bye!)\n");
174                 break;
175             default:
176                 INT_BARF(context, 0x2f);
177             }
178             break;
179         default:
180            INT_BARF(context, 0x2f);
181         }
182         break;
183
184     case 0x15: /* mscdex */
185         MSCDEX_Handler(context);
186         break;
187
188     case 0x16:
189         do_int2f_16( context );
190         break;
191
192     case 0x1a: /* ANSI.SYS / AVATAR.SYS Install Check */
193         /* Not supported yet, do nothing */
194         break;
195
196     case 0x43:
197 #if 1
198        switch (LOBYTE(context->Eax))
199        {
200        case 0x00:   /* XMS v2+ installation check */
201            WARN("XMS is not fully implemented\n");
202            SET_AL( context, 0x80 );
203            break;
204        case 0x10:   /* XMS v2+ get driver address */
205        {
206             context->SegEs = DOSVM_dpmi_segments->xms_seg;
207             SET_BX( context, 0 );
208             break;
209        }
210        default:
211            INT_BARF( context, 0x2f );
212        }
213 #else
214        FIXME("check for XMS (not supported)\n");
215        SET_AL( context, 0x42 ); /* != 0x80 */
216 #endif
217        break;
218
219     case 0x45:
220        switch (LOBYTE(context->Eax))
221        {
222        case 0x00:
223        case 0x01:
224        case 0x02:
225        case 0x03:
226        case 0x04:
227        case 0x05:
228        case 0x06:
229        case 0x07:
230        case 0x08:
231            /* Microsoft Profiler - not installed */
232            break;
233        default:
234             INT_BARF( context, 0x2f );
235        }
236        break;
237
238     case 0x4a:
239         switch(LOBYTE(context->Eax))
240         {
241        case 0x10:  /* smartdrv */
242            break;  /* not installed */
243         case 0x11:  /* dblspace */
244             break;  /* not installed */
245         case 0x12:  /* realtime compression interface */
246             break;  /* not installed */
247         case 0x32:  /* patch IO.SYS (???) */
248             break;  /* we have no IO.SYS, so we can't patch it :-/ */
249         default:
250             INT_BARF( context, 0x2f );
251         }
252         break;
253     case 0x4b:
254        switch(LOBYTE(context->Eax))
255        {
256        case 0x01:
257        case 0x02:
258        case 0x03:
259        case 0x04:
260        case 0x05:
261            FIXME("Task Switcher - not implemented\n");
262            break;
263        default:
264            INT_BARF( context, 0x2f );
265        }
266        break;
267     case 0x56:  /* INTERLNK */
268        switch(LOBYTE(context->Eax))
269        {
270        case 0x01:  /* check if redirected drive */
271            SET_AL( context, 0 ); /* not redirected */
272            break;
273        default:
274            INT_BARF( context, 0x2f );
275        }
276        break;
277     case 0x7a:  /* NOVELL NetWare */
278         switch (LOBYTE(context->Eax))
279         {
280        case 0x0:  /* Low-level Netware installation check AL=0 not installed.*/
281             SET_AL( context, 0 );
282             break;
283         case 0x20:  /* Get VLM Call Address */
284             /* return nothing -> NetWare not installed */
285             break;
286         default:
287            INT_BARF( context, 0x2f );
288             break;
289         }
290         break;
291     case 0xb7:  /* append */
292         SET_AL( context, 0 ); /* not installed */
293         break;
294     case 0xb8:  /* network */
295         switch (LOBYTE(context->Eax))
296         {
297         case 0x00:  /* Install check */
298             /* not installed */
299             break;
300         default:
301            INT_BARF( context, 0x2f );
302             break;
303         }
304         break;
305     case 0xbc:
306         if (AL_reg(context) == 0x00 && BX_reg(context) == 0x3f3f) {
307             /* MVSOUND.SYS - Install check: not installed */
308         } else { 
309             INT_BARF( context, 0x2f );
310         }
311        break;
312     case 0xbd:  /* some Novell network install check ??? */
313         SET_AX( context, 0xa5a5 ); /* pretend to have Novell IPX installed */
314        break;
315     case 0xbf:  /* REDIRIFS.EXE */
316         switch (LOBYTE(context->Eax))
317         {
318         case 0x00:  /* Install check */
319             /* not installed */
320             break;
321         default:
322            INT_BARF( context, 0x2f );
323             break;
324         }
325         break;
326     case 0xd2:
327        switch(LOBYTE(context->Eax))
328        {
329        case 0x01: /* Quarterdeck RPCI - QEMM/QRAM - PCL-838.EXE functions */
330            if(BX_reg(context) == 0x5145 && CX_reg(context) == 0x4D4D
331              && DX_reg(context) == 0x3432)
332                TRACE("Check for QEMM v5.0+ (not installed)\n");
333                break;
334        default:
335            INT_BARF( context, 0x2f );
336            break;
337        }
338        break;
339     case 0xd7:  /* Banyan Vines */
340         switch (LOBYTE(context->Eax))
341         {
342         case 0x01:  /* Install check - Get Int Number */
343             /* not installed */
344             break;
345         default:
346            INT_BARF( context, 0x2f );
347             break;
348         }
349         break;
350     case 0xde:
351        switch(LOBYTE(context->Eax))
352        {
353        case 0x01:   /* Quarterdeck QDPMI.SYS - DESQview */
354            if(BX_reg(context) == 0x4450 && CX_reg(context) == 0x4d49
355              && DX_reg(context) == 0x8f4f)
356                TRACE("Check for QDPMI.SYS (not installed)\n");
357                break;
358        default:
359            INT_BARF( context, 0x2f );
360            break;
361        }
362        break;
363     case 0xfa:  /* Watcom debugger check, returns 0x666 if installed */
364         break;
365     default:
366         INT_BARF( context, 0x2f );
367         break;
368     }
369 }
370
371
372 /**********************************************************************
373  *         do_int2f_16
374  */
375 static void do_int2f_16( CONTEXT86 *context )
376 {
377     DWORD addr;
378
379     switch(LOBYTE(context->Eax))
380     {
381     case 0x00:  /* Windows enhanced mode installation check */
382         SET_AX( context, LOWORD(GetVersion16()) );
383         break;
384
385     case 0x0a:  /* Get Windows version and type */
386         SET_AX( context, 0 );
387         SET_BX( context, (LOWORD(GetVersion16()) << 8) | (LOWORD(GetVersion16()) >> 8) );
388         SET_CX( context, 3 );
389         break;
390
391     case 0x0b:  /* Identify Windows-aware TSRs */
392         /* we don't have any pre-Windows TSRs */
393         break;
394
395     case 0x11:  /* Get Shell Parameters - (SHELL= in CONFIG.SYS) */
396         /* We can mock this up. But not today... */
397         FIXME("Get Shell Parameters\n");
398         break;
399
400     case 0x80:  /* Release time-slice */
401        /* Linux sched_yield() still keeps burning CPU cycles
402         * if the current process is the only one in highest priority list
403         * (as Linux will immediately return to this process to waste
404         * more CPU cycles), so sched_yield() is essentially useless for us
405         * (poor API, if you ask me: its return code should indicate
406         * whether other processes did run in between, in order for us
407         * to be able to decide whether to do an additional Sleep() or not...)
408         * Thus we better unconditionally use a well-balanced Sleep()
409         * instead to really make sure the process calling int 0x2f/0x1680
410         * *doesn't* use 100% CPU...
411         */
412        Sleep(55); /* just wait 55ms (one "timer tick") for now. */
413        SET_AL( context, 0 );
414         break;
415
416     case 0x81: /* Begin critical section.  */
417         /* FIXME? */
418         break;
419
420     case 0x82: /* End critical section.  */
421         /* FIXME? */
422         break;
423
424     case 0x83:  /* Return Current Virtual Machine ID */
425         /* Virtual Machines are usually created/destroyed when Windows runs
426          * DOS programs. Since we never do, we are always in the System VM.
427          * According to Ralf Brown's Interrupt List, never return 0. But it
428          * seems to work okay (returning 0), just to be sure we return 1.
429          */
430        SET_BX( context, 1 ); /* VM 1 is probably the System VM */
431        break;
432
433     case 0x84:  /* Get device API entry point */
434         {
435             HMODULE16 mod = GetModuleHandle16("wprocs");
436             if (mod < 32) mod = LoadLibrary16( "wprocs" );
437             addr = (DWORD)GetProcAddress16( mod, (LPCSTR)(VXD_BASE + BX_reg(context)) );
438             if (!addr)  /* not supported */
439                 ERR("Accessing unknown VxD %04x - Expect a failure now.\n", BX_reg(context) );
440             context->SegEs = SELECTOROF(addr);
441             SET_DI( context, OFFSETOF(addr) );
442         }
443        break;
444
445     case 0x86:  /* DPMI detect mode */
446         SET_AX( context, 0 );  /* Running under DPMI */
447         break;
448
449     case 0x87: /* DPMI installation check */
450         {
451            SYSTEM_INFO si;
452            GetSystemInfo(&si);
453            SET_AX( context, 0x0000 ); /* DPMI Installed */
454             SET_BX( context, 0x0001 ); /* 32bits available */
455             SET_CL( context, si.wProcessorLevel );
456             SET_DX( context, 0x005a ); /* DPMI major/minor 0.90 */
457             SET_SI( context, 0 );      /* # of para. of DOS extended private data */
458             context->SegEs = DOSVM_dpmi_segments->dpmi_seg;
459             SET_DI( context, 0 );      /* ES:DI is DPMI switch entry point */
460             break;
461         }
462     case 0x8a:  /* DPMI get vendor-specific API entry point. */
463        /* The 1.0 specs say this should work with all 0.9 hosts.  */
464        break;
465
466     default:
467         INT_BARF( context, 0x2f );
468     }
469 }
470
471 /* FIXME: this macro may have to be changed on architectures where <size> reads/writes
472  * must be <size> aligned
473  * <size> could be WORD, DWORD...
474  * in this case, we would need two macros, one for read, the other one for write
475  * Note: PTR_AT can be used as an l-value
476  */
477 #define        PTR_AT(_ptr, _ofs, _typ)        (*((_typ*)(((char*)_ptr)+(_ofs))))
478
479 /* Use #if 1 if you want full int 2f debug... normal users can leave it at 0 */
480 #if 0
481 /**********************************************************************
482  *         MSCDEX_Dump                                 [internal]
483  *
484  * Dumps mscdex requests to int debug channel.
485  */
486 static void    MSCDEX_Dump(char* pfx, BYTE* req, int dorealmode)
487 {
488     int        i;
489     BYTE       buf[2048];
490     BYTE*      ptr;
491     BYTE*      ios;
492
493     ptr = buf;
494     ptr += sprintf(ptr, "%s\tCommand => ", pfx);
495     for (i = 0; i < req[0]; i++) {
496        ptr += sprintf(ptr, "%02x ", req[i]);
497     }
498
499     switch (req[2]) {
500     case 3:
501     case 12:
502        ptr += sprintf(ptr, "\n\t\t\t\tIO_struct => ");
503        ios = (dorealmode) ? PTR_REAL_TO_LIN( PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD)) :
504                              MapSL(MAKESEGPTR(PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD)));
505
506        for (i = 0; i < PTR_AT(req, 18, WORD); i++) {
507            ptr += sprintf(ptr, "%02x ", ios[i]);
508            if ((i & 0x1F) == 0x1F) {
509                *ptr++ = '\n';
510                *ptr = 0;
511            }
512        }
513        break;
514     }
515     TRACE("%s\n", buf);
516 }
517 #else
518 #define MSCDEX_Dump(pfx, req, drm)
519 #endif
520
521 #define CDFRAMES_PERSEC                 75
522 #define CDFRAMES_PERMIN                 (CDFRAMES_PERSEC * 60)
523 #define FRAME_OF_ADDR(a)        ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
524 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
525 #define CTRL_OF_TOC(toc, idx)   (((toc).TrackData[idx - (toc).FirstTrack].Control << 4) | \
526                                   (toc).TrackData[idx - (toc).FirstTrack].Adr)
527
528 static void    MSCDEX_StoreMSF(DWORD frame, BYTE* val)
529 {
530     val[3] = 0;        /* zero */
531     val[2] = frame / CDFRAMES_PERMIN; /* minutes */
532     val[1] = (frame / CDFRAMES_PERSEC) % 60; /* seconds */
533     val[0] = frame % CDFRAMES_PERSEC; /* frames */
534 }
535
536 static int is_cdrom( int drive)
537 {
538     char root[] = "A:\\";
539     root[0] += drive;
540     return (GetDriveTypeA(root) == DRIVE_CDROM);
541 }
542
543 /***********************************************************************
544  *           CDROM_FillHeap
545  *
546  * Initialize CDROM heap.
547  *
548  */
549 static void CDROM_FillHeap( CDROM_HEAP *heap )
550 {
551     int drive, count;
552
553     /* Count the number of contiguous CDROM drives
554      */
555     for (drive = count = 0; drive < 26; drive++) {
556         if (is_cdrom(drive)) {
557             while (is_cdrom(drive + count)) count++;
558             break;
559         }
560     }
561     TRACE("Installation check: %d cdroms, starting at %d\n", count, drive);
562     heap->hdr.drive = (drive < 26) ? drive : 0;
563     heap->hdr.units = count;
564     heap->hdr.reserved = 0;
565 }
566
567 /**********************************************************************
568  *         CDROM_GetHeap
569  *
570  * Get pointer for CDROM heap (CDROM_HEAP).
571  * Creates and initializes heap on first call.
572  */
573 static CDROM_HEAP *CDROM_GetHeap( void )
574 {
575     static CDROM_HEAP *heap_pointer = NULL;
576
577     if ( !heap_pointer )
578     {
579         WORD heap_segment;
580         WORD heap_selector;
581
582         /* allocate a new DOS data segment */
583         heap_pointer = DOSVM_AllocDataUMB( sizeof(CDROM_HEAP),
584                                            &heap_segment,
585                                            &heap_selector );
586
587         heap_pointer->cdrom_segment  = heap_segment;
588         heap_pointer->cdrom_selector = heap_selector;
589         CDROM_FillHeap( heap_pointer );
590     }
591
592     return heap_pointer;
593 }
594
595 static void MSCDEX_Request(BYTE *driver_request, BOOL dorealmode)
596 {
597     BYTE*       io_stru;
598     BYTE        Error = 255; /* No Error */
599     char        devName[] = "\\\\.\\@:";
600     HANDLE      h;
601     CDROM_TOC                   toc;
602     CDROM_SUB_Q_DATA_FORMAT     fmt;
603     SUB_Q_CHANNEL_DATA          data;
604     DWORD                       br;
605     DWORD                       present = TRUE;
606
607     /* FIXME
608      * the following tests are wrong because lots of functions don't require the
609      * tray to be closed with a CD inside
610      */
611     TRACE("CDROM device driver -> command <%d>\n", driver_request[2]);
612
613     MSCDEX_Dump("Beg", driver_request, dorealmode);
614
615     /* set status to 0 */
616     PTR_AT(driver_request, 3, WORD) = 0;
617     devName[4] = 'A' + CDROM_GetHeap()->hdr.drive + driver_request[1];
618     h = CreateFileA(devName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
619     if (h == INVALID_HANDLE_VALUE) {
620         WARN("Couldn't open cdrom handle\n");
621         driver_request[4] |= 0x80;
622         driver_request[3] = 1;  /* unknown unit */
623         return;
624     }
625
626     fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
627     if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, NULL) ||
628         !DeviceIoControl(h, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
629                          &data, sizeof(data), &br, NULL)) {
630         if (GetLastError() == STATUS_NO_MEDIA_IN_DEVICE)
631         {
632             if (driver_request[2] != 6 && driver_request[2] != 15)
633             {
634                 driver_request[4] |= 0x80;
635                 driver_request[3] = 2; /* drive not ready */
636                 CloseHandle(h);
637                 return;
638             }
639             present = FALSE;
640         }
641         else
642         {
643             driver_request[4] |= 0x80;
644             driver_request[3] = 1;     /* unknown unit */
645             CloseHandle(h);
646             return;
647         }
648     }
649
650     switch (driver_request[2]) {
651     case 3:
652         io_stru = (dorealmode) ?
653             PTR_REAL_TO_LIN( PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD) ) :
654             MapSL( MAKESEGPTR(PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD)));
655
656         TRACE(" --> IOCTL INPUT <%d>\n", io_stru[0]);
657         switch (io_stru[0]) {
658 #if 0
659         case 0: /* Get device Header */
660         {
661             static  LPSTR ptr = 0;
662             if (ptr == 0)   {
663                 ptr = SEGPTR_ALLOC(22);
664                 PTR_AT(ptr,  0, DWORD) = ~1;        /* Next Device Driver */
665                 PTR_AT(ptr,  4,  WORD) = 0xC800;    /* Device attributes  */
666                 PTR_AT(ptr,  6,  WORD) = 0x1234;    /* Pointer to device strategy routine: FIXME */
667                 PTR_AT(ptr,  8,  WORD) = 0x3142;    /* Pointer to device interrupt routine: FIXME */
668                 PTR_AT(ptr, 10,  char) = 'W';       /* 8-byte character device name field */
669                 PTR_AT(ptr, 11,  char) = 'I';
670                 PTR_AT(ptr, 12,  char) = 'N';
671                 PTR_AT(ptr, 13,  char) = 'E';
672                 PTR_AT(ptr, 14,  char) = '_';
673                 PTR_AT(ptr, 15,  char) = 'C';
674                 PTR_AT(ptr, 16,  char) = 'D';
675                 PTR_AT(ptr, 17,  char) = '_';
676                 PTR_AT(ptr, 18,  WORD) = 0;         /* Reserved (must be zero) */
677                 PTR_AT(ptr, 20,  BYTE) = 0;         /* Drive letter (must be zero) */
678                 PTR_AT(ptr, 21,  BYTE) = 1;         /* Number of units supported (one or more) FIXME*/
679             }
680             PTR_AT(io_stru, DWORD,  0) = SEGPTR_GET(ptr);
681         }
682         break;
683 #endif
684
685         case 1: /* location of head */
686             switch (io_stru[1]) {
687             case 0:
688                 PTR_AT(io_stru, 2, DWORD) =
689                     FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
690                 break;
691             case 1:
692                 MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress),
693                                 io_stru + 2);
694                 break;
695             default:
696                 ERR("CD-ROM driver: unsupported addressing mode !!\n");
697                 Error = 0x0c;
698             }
699             TRACE(" ----> HEAD LOCATION <%d>\n", PTR_AT(io_stru, 2, DWORD));
700             break;
701
702         case 4: /* Audio channel info */
703             io_stru[1] = 0;
704             io_stru[2] = 0xff;
705             io_stru[3] = 1;
706             io_stru[4] = 0xff;
707             io_stru[5] = 2;
708             io_stru[6] = 0;
709             io_stru[7] = 3;
710             io_stru[8] = 0;
711             TRACE(" ----> AUDIO CHANNEL INFO\n");
712             break;
713
714         case 6: /* device status */
715             PTR_AT(io_stru, 1, DWORD) = 0x00000290;
716             /* 290 =>
717              * 1        Supports HSG and Red Book addressing modes
718              * 0        Supports audio channel manipulation
719              *
720              * 1        Supports prefetching requests
721              * 0        Reserved
722              * 0        No interleaving
723              * 1        Data read and plays audio/video tracks
724              *
725              * 0        Read only
726              * 0        Supports only cooked reading
727              * 0        Door locked
728              * 0        see below (Door closed/opened)
729              */
730             if (!present) PTR_AT(io_stru, 1, DWORD) |= 1;
731             TRACE(" ----> DEVICE STATUS <0x%08x>\n", PTR_AT(io_stru, 1, DWORD));
732             break;
733
734         case 8: /* Volume size */
735             PTR_AT(io_stru, 1, DWORD) = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
736                 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
737             TRACE(" ----> VOLUME SIZE <%d>\n", PTR_AT(io_stru, 1, DWORD));
738             break;
739
740         case 9: /* media changed ? */
741             /* answers don't know... -1/1 for yes/no would be better */
742             io_stru[1] = 0; /* FIXME? 1? */
743             TRACE(" ----> MEDIA CHANGED <%d>\n", io_stru[1]);
744             break;
745
746         case 10: /* audio disk info */
747             io_stru[1] = toc.FirstTrack; /* starting track of the disc */
748             io_stru[2] = toc.LastTrack;  /* ending track */
749             MSCDEX_StoreMSF(FRAME_OF_TOC(toc, toc.LastTrack + 1) -
750                             FRAME_OF_TOC(toc, toc.FirstTrack) - 1, io_stru + 3);
751
752             TRACE(" ----> AUDIO DISK INFO <%d-%d/%08x>\n",
753                   io_stru[1], io_stru[2], PTR_AT(io_stru, 3, DWORD));
754             break;
755
756         case 11: /* audio track info */
757             if (io_stru[1] >= toc.FirstTrack && io_stru[1] <= toc.LastTrack) {
758                 MSCDEX_StoreMSF(FRAME_OF_TOC(toc, io_stru[1]), io_stru + 2);
759                 /* starting point if the track */
760                 io_stru[6] = CTRL_OF_TOC(toc, io_stru[1]);
761             } else {
762                 PTR_AT(io_stru, 2, DWORD) = 0;
763                 io_stru[6] = 0;
764             }
765             TRACE(" ----> AUDIO TRACK INFO[%d] = [%08x:%d]\n",
766                   io_stru[1], PTR_AT(io_stru, 2, DWORD), io_stru[6]);
767             break;
768
769         case 12: /* get Q-Channel info */
770             io_stru[1] = CTRL_OF_TOC(toc, data.CurrentPosition.TrackNumber);
771             io_stru[2] = data.CurrentPosition.TrackNumber;
772             io_stru[3] = 0; /* FIXME ?? */
773
774             /* why the heck did MS use another format for 0MSF information... sigh */
775             {
776                 BYTE    bTmp[4];
777
778                 MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.TrackRelativeAddress), bTmp);
779                 io_stru[ 4] = bTmp[2];
780                 io_stru[ 5] = bTmp[1];
781                 io_stru[ 6] = bTmp[0];
782                 io_stru[ 7] = 0;
783
784                 MSCDEX_StoreMSF(FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress), bTmp);
785                 io_stru[ 8] = bTmp[2];
786                 io_stru[ 9] = bTmp[1];
787                 io_stru[10] = bTmp[0];
788                 io_stru[11] = 0;
789             }
790             TRACE("Q-Channel info: Ctrl/adr=%02x TNO=%02x X=%02x rtt=%02x:%02x:%02x rtd=%02x:%02x:%02x (cf=%08x, tp=%08x)\n",
791                   io_stru[ 1], io_stru[ 2], io_stru[ 3],
792                   io_stru[ 4], io_stru[ 5], io_stru[ 6],
793                   io_stru[ 8], io_stru[ 9], io_stru[10],
794                   FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress),
795                   FRAME_OF_TOC(toc, data.CurrentPosition.TrackNumber));
796             break;
797
798         case 15: /* Audio status info */
799             /* !!!! FIXME FIXME FIXME !! */
800             PTR_AT(io_stru, 1,  WORD) = 2 | ((data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_PAUSED) ? 1 : 0);
801             if (!present) {
802                 PTR_AT(io_stru, 3, DWORD) = 0;
803                 PTR_AT(io_stru, 7, DWORD) = 0;
804             } else {
805                 PTR_AT(io_stru, 3, DWORD) = FRAME_OF_TOC(toc, toc.FirstTrack);
806                 PTR_AT(io_stru, 7, DWORD) = FRAME_OF_TOC(toc, toc.LastTrack + 1);
807             }
808             TRACE("Audio status info: status=%04x startLoc=%d endLoc=%d\n",
809                   PTR_AT(io_stru, 1, WORD), PTR_AT(io_stru, 3, DWORD), PTR_AT(io_stru, 7, DWORD));
810             break;
811
812         default:
813             FIXME("IOCTL INPUT: Unimplemented <%d>!!\n", io_stru[0]);
814             Error = 0x0c;
815             break;
816         }
817         break;
818
819     case 12:
820         io_stru = (dorealmode) ?
821             PTR_REAL_TO_LIN( PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD)) :
822             MapSL( MAKESEGPTR(PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD)));
823
824         TRACE(" --> IOCTL OUTPUT <%d>\n", io_stru[0]);
825         switch (io_stru[0]) {
826         case 0: /* eject */
827             DeviceIoControl(h, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &br, NULL);
828             TRACE(" ----> EJECT\n");
829             break;
830         case 2: /* reset drive */
831             DeviceIoControl(h, IOCTL_STORAGE_RESET_DEVICE, NULL, 0, NULL, 0, &br, NULL);
832             TRACE(" ----> RESET\n");
833             break;
834         case 3: /* Audio Channel Control */
835             FIXME(" ----> AUDIO CHANNEL CONTROL (NIY)\n");
836             break;
837         case 5: /* close tray */
838             DeviceIoControl(h, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &br, NULL);
839             TRACE(" ----> CLOSE TRAY\n");
840             break;
841         default:
842             FIXME(" IOCTL OUTPUT: Unimplemented <%d>!!\n", io_stru[0]);
843             Error = 0x0c;
844             break;
845         }
846         break;
847
848     case 128: /* read long */
849     {
850         LPVOID              dst = MapSL(MAKESEGPTR(PTR_AT(driver_request, 16, WORD),
851                                                    PTR_AT(driver_request, 14, WORD)));
852         DWORD               at = PTR_AT(driver_request, 20, DWORD);
853         WORD                num = PTR_AT(driver_request, 18, WORD);
854         RAW_READ_INFO       rri;
855
856         switch (driver_request[13]) {
857         case 1: /* Red book addressing mode = 0:m:s:f */
858             /* FIXME : frame <=> msf conversion routines could be shared
859              * between mscdex and mcicda
860              */
861             at = LOBYTE(HIWORD(at)) * CDFRAMES_PERMIN +
862                 HIBYTE(LOWORD(at)) * CDFRAMES_PERSEC +
863                 LOBYTE(LOWORD(at));
864             /* fall through */
865         case 0: /* HSG addressing mode */
866             switch (PTR_AT(driver_request, 24, BYTE))
867             {
868             case 0: /* cooked */
869                 ReadFile(h, dst, num * 2048, &br, NULL);
870                 break;
871             case 1:
872                 /* FIXME: computation is wrong */
873                 rri.DiskOffset.u.HighPart = 0;
874                 rri.DiskOffset.u.LowPart = at << 11;
875                 rri.TrackMode = YellowMode2;
876                 rri.SectorCount = num;
877                 DeviceIoControl(h, IOCTL_CDROM_RAW_READ, &rri, sizeof(rri),
878                                 dst, num * 2352, &br, NULL);
879                 break;
880             default:
881                 ERR("Unsupported read mode !!\n");
882                 Error = 0x0c;
883                 break;
884             }
885             break;
886         default:
887             ERR("Unsupported address mode !!\n");
888             Error = 0x0c;
889             break;
890         }
891     }
892     break;
893
894     case 131: /* seek */
895     {
896         DWORD                       at;
897         CDROM_SEEK_AUDIO_MSF        seek;
898
899         at = PTR_AT(driver_request, 20, DWORD);
900
901         TRACE(" --> SEEK AUDIO mode :<0x%02X>, [%d]\n", driver_request[13], at);
902
903         switch (driver_request[13]) {
904         case 1: /* Red book addressing mode = 0:m:s:f */
905             /* FIXME : frame <=> msf conversion routines could be shared
906              * between mscdex and mcicda
907              */
908             at = LOBYTE(HIWORD(at)) * CDFRAMES_PERMIN +
909                 HIBYTE(LOWORD(at)) * CDFRAMES_PERSEC +
910                 LOBYTE(LOWORD(at));
911             /* fall through */
912         case 0: /* HSG addressing mode */
913             seek.M = at / CDFRAMES_PERMIN;
914             seek.S = (at / CDFRAMES_PERSEC) % 60;
915             seek.F = at % CDFRAMES_PERSEC;
916             DeviceIoControl(h, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
917                             NULL, 0, &br, NULL);
918             break;
919         default:
920             ERR("Unsupported address mode !!\n");
921             Error = 0x0c;
922             break;
923         }
924     }
925     break;
926
927     case 132: /* play */
928     {
929         DWORD                       beg, end;
930         CDROM_PLAY_AUDIO_MSF        play;
931
932         beg = end = PTR_AT(driver_request, 14, DWORD);
933         end += PTR_AT(driver_request, 18, DWORD);
934
935         TRACE(" --> PLAY AUDIO mode :<0x%02X>, [%d-%d]\n", driver_request[13], beg, end);
936
937         switch (driver_request[13]) {
938         case 1:
939             /* Red book addressing mode = 0:m:s:f */
940             /* FIXME : frame <=> msf conversion routines could be shared
941              * between mscdex and mcicda
942              */
943             beg = LOBYTE(LOWORD(beg)) * CDFRAMES_PERMIN +
944                 HIBYTE(LOWORD(beg)) * CDFRAMES_PERSEC +
945                 LOBYTE(HIWORD(beg));
946             end = LOBYTE(LOWORD(end)) * CDFRAMES_PERMIN +
947                 HIBYTE(LOWORD(end)) * CDFRAMES_PERSEC +
948                 LOBYTE(HIWORD(end));
949             /* fall through */
950         case 0: /* HSG addressing mode */
951             play.StartingM = beg / CDFRAMES_PERMIN;
952             play.StartingS = (beg / CDFRAMES_PERSEC) % 60;
953             play.StartingF = beg % CDFRAMES_PERSEC;
954             play.EndingM   = end / CDFRAMES_PERMIN;
955             play.EndingS   = (end / CDFRAMES_PERSEC) % 60;
956             play.EndingF   = end % CDFRAMES_PERSEC;
957             DeviceIoControl(h, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
958                             NULL, 0, &br, NULL);
959             break;
960         default:
961             ERR("Unsupported address mode !!\n");
962             Error = 0x0c;
963             break;
964         }
965     }
966     break;
967
968     case 133:
969         if (data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {
970             DeviceIoControl(h, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL);
971             TRACE(" --> STOP AUDIO (Paused)\n");
972         } else {
973             DeviceIoControl(h, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL);
974             TRACE(" --> STOP AUDIO (Stopped)\n");
975         }
976         break;
977
978     case 136:
979         TRACE(" --> RESUME AUDIO\n");
980         DeviceIoControl(h, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL);
981         break;
982
983     default:
984         FIXME(" ioctl unimplemented <%d>\n", driver_request[2]);
985         Error = 0x0c;
986     }
987
988     /* setting error codes if any */
989     if (Error < 255) {
990         driver_request[4] |= 0x80;
991         driver_request[3] = Error;
992     }
993
994     CloseHandle(h);
995     /* setting status bits
996      * 3 == playing && done
997      * 1 == done
998      */
999     driver_request[4] |=
1000         (data.CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) ? 3 : 1;
1001
1002     MSCDEX_Dump("End", driver_request, dorealmode);
1003 }
1004
1005 static void MSCDEX_Handler(CONTEXT86* context)
1006 {
1007     int        drive, count;
1008     char*      p;
1009
1010     switch (LOBYTE(context->Eax)) {
1011     case 0x00: /* Installation check */
1012        /* Count the number of contiguous CDROM drives
1013         */
1014        for (drive = count = 0; drive < 26; drive++) {
1015            if (is_cdrom(drive)) {
1016                while (is_cdrom(drive + count)) count++;
1017                break;
1018            }
1019        }
1020        TRACE("Installation check: %d cdroms, starting at %d\n", count, drive);
1021        SET_BX( context, count );
1022        SET_CX( context, (drive < 26) ? drive : 0 );
1023        break;
1024
1025     case 0x01: /* get drive device list */
1026        {
1027            CDROM_HEAP*          cdrom_heap = CDROM_GetHeap();
1028            CDROM_DEVICE_HEADER* dev = &cdrom_heap->hdr;
1029            SEGPTR ptr_dev = ISV86(context)
1030                ? MAKESEGPTR( cdrom_heap->cdrom_segment,
1031                              FIELD_OFFSET(CDROM_HEAP, hdr) )
1032                : MAKESEGPTR( cdrom_heap->cdrom_selector,
1033                              FIELD_OFFSET(CDROM_HEAP, hdr) );
1034
1035            p = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
1036            for (drive = 0; drive < dev->units; drive++) {
1037                *p = drive; /* subunit */
1038                ++p;
1039                *(DWORD*)p = ptr_dev;
1040                p += sizeof(DWORD);
1041            }
1042            TRACE("Get drive device list\n");
1043        }
1044        break;
1045
1046     case 0x0B: /* drive check */
1047        SET_AX( context, is_cdrom(CX_reg(context)) );
1048        SET_BX( context, 0xADAD );
1049        break;
1050
1051     case 0x0C: /* get version */
1052        SET_BX( context, 0x020a );
1053        TRACE("Version number => %04x\n", BX_reg(context));
1054        break;
1055
1056     case 0x0D: /* get drive letters */
1057        p = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
1058        memset(p, 0, 26);
1059        for (drive = 0; drive < 26; drive++) {
1060            if (is_cdrom(drive)) *p++ = drive;
1061        }
1062        TRACE("Get drive letters\n");
1063        break;
1064
1065     case 0x10: /* direct driver access */
1066        {
1067            BYTE*       driver_request;
1068            CDROM_HEAP* cdrom_heap = CDROM_GetHeap();
1069
1070            if (!is_cdrom(CX_reg(context))) {
1071                WARN("Request made doesn't match a CD ROM drive (%d)\n", CX_reg(context));
1072                SET_CFLAG( context );
1073                SET_AX( context, 0x000f ); /* invalid drive */
1074                return;
1075            }
1076
1077            driver_request = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
1078
1079            if (!driver_request) {
1080                /* FIXME - to be deleted ?? */
1081                ERR("ES:BX==0 ! SEGFAULT ?\n");
1082                ERR("-->BX=0x%04x, ES=0x%04x, DS=0x%04x, CX=0x%04x\n",
1083                    BX_reg(context), context->SegEs, context->SegDs, CX_reg(context));
1084                driver_request[4] |= 0x80;
1085                driver_request[3] = 5;  /* bad request length */
1086                return;
1087            }
1088
1089            driver_request[1] = CX_reg(context) - cdrom_heap->hdr.drive;
1090            MSCDEX_Request(driver_request, ISV86(context));
1091        }
1092        break;
1093     default:
1094        FIXME("Unimplemented MSCDEX function 0x%02X.\n", LOBYTE(context->Eax));
1095        break;
1096     }
1097 }
1098
1099 /* prototypes */
1100 static void WINAPI cdrom_strategy(CONTEXT86*ctx);
1101 static void WINAPI cdrom_interrupt(CONTEXT86*ctx);
1102
1103 /* device info */
1104 static const WINEDEV cdromdev =
1105 {
1106     "WINE_CD_",
1107     ATTR_CHAR|ATTR_REMOVABLE|ATTR_IOCTL,
1108     cdrom_strategy, cdrom_interrupt
1109 };
1110
1111 static REQUEST_HEADER *cdrom_driver_request;
1112
1113 /* Return to caller */
1114 static void do_lret(CONTEXT86*ctx)
1115 {
1116     WORD *stack = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegSs, ctx->Esp);
1117
1118     ctx->Eip   = *(stack++);
1119     ctx->SegCs = *(stack++);
1120     ctx->Esp  += 2*sizeof(WORD);
1121 }
1122
1123 static void WINAPI cdrom_strategy(CONTEXT86*ctx)
1124 {
1125     cdrom_driver_request = CTX_SEG_OFF_TO_LIN(ctx, ctx->SegEs, ctx->Ebx);
1126     do_lret( ctx );
1127 }
1128
1129 static void WINAPI cdrom_interrupt(CONTEXT86*ctx)
1130 {
1131     if (cdrom_driver_request->unit > CDROM_GetHeap()->hdr.units)
1132         cdrom_driver_request->status = STAT_ERROR | 1; /* unknown unit */
1133     else
1134         MSCDEX_Request((BYTE*)cdrom_driver_request, ISV86(ctx));
1135
1136     do_lret( ctx );
1137 }
1138
1139 /**********************************************************************
1140  *         MSCDEX_InstallCDROM  [internal]
1141  *
1142  * Install the CDROM driver into the DOS device driver chain.
1143  */
1144 void MSCDEX_InstallCDROM(void)
1145 {
1146     CDROM_HEAP *cdrom_heap = CDROM_GetHeap();
1147
1148     DOSDEV_SetupDevice( &cdromdev,
1149                         cdrom_heap->cdrom_segment,
1150                         FIELD_OFFSET(CDROM_HEAP, hdr),
1151                         FIELD_OFFSET(CDROM_HEAP, thunk) );
1152 }