ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/sys_windows.cpp
Revision: 1.1
Committed: 2004-12-05T17:11:15Z (19 years, 6 months ago) by gbeauche
Branch: MAIN
Log Message:
Refactor native windows system support routines from sys_unix.cpp. This
includes CD-ROM support but still lacks original B2 features involving
floppies, real hard disk, and hard file options

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * sys_windows.cpp - System dependent routines, Windows implementation
3     *
4     * Basilisk II (C) 1997-2004 Christian Bauer
5     *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include "sysdeps.h"
22    
23     #define WIN32_LEAN_AND_MEAN
24     #include <windows.h>
25     #include <winioctl.h>
26    
27     #include <string>
28     using std::string;
29    
30     #include <algorithm>
31     using std::min;
32    
33     #include "main.h"
34     #include "macos_util.h"
35     #include "prefs.h"
36     #include "user_strings.h"
37     #include "sys.h"
38    
39     #include "cd_defs.h"
40     #include "cdenable/ntcd.h"
41     #include "cdenable/cache.h"
42     #include "cdenable/eject_nt.h"
43    
44     #define DEBUG 0
45     #include "debug.h"
46    
47    
48     // File handles are pointers to these structures
49     struct file_handle {
50     char *name; // Copy of device/file name
51     HANDLE fh;
52     bool is_file; // Flag: plain file or physical device?
53     bool is_floppy; // Flag: floppy device
54     bool is_cdrom; // Flag: CD-ROM device
55     bool read_only; // Copy of Sys_open() flag
56     loff_t start_byte; // Size of file header (if any)
57     loff_t file_size; // Size of file data (only valid if is_file is true)
58     cachetype cache;
59     bool is_media_present;
60     };
61    
62     // File handle of first floppy drive (for SysMountFirstFloppy())
63     static file_handle *first_floppy = NULL;
64    
65     // CD-ROM variables
66     static const int CD_READ_AHEAD_SECTORS = 16;
67     static char *sector_buffer = NULL;
68    
69    
70     /*
71     * Initialization
72     */
73    
74     void SysInit(void)
75     {
76     // Initialize CD-ROM driver
77     sector_buffer = (char *)VirtualAlloc(NULL, 8192, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
78     CdenableSysInstallStart();
79     }
80    
81    
82     /*
83     * Deinitialization
84     */
85    
86     void SysExit(void)
87     {
88     if (sector_buffer) {
89     VirtualFree(sector_buffer, 0, MEM_RELEASE );
90     sector_buffer = NULL;
91     }
92     }
93    
94    
95     /*
96     * Mount first floppy disk
97     */
98    
99     void SysMountFirstFloppy(void)
100     {
101     if (first_floppy)
102     MountVolume(first_floppy);
103     }
104    
105    
106     /*
107     * This gets called when no "floppy" prefs items are found
108     * It scans for available floppy drives and adds appropriate prefs items
109     */
110    
111     void SysAddFloppyPrefs(void)
112     {
113     }
114    
115    
116     /*
117     * This gets called when no "disk" prefs items are found
118     * It scans for available HFS volumes and adds appropriate prefs items
119     */
120    
121     void SysAddDiskPrefs(void)
122     {
123     }
124    
125    
126     /*
127     * This gets called when no "cdrom" prefs items are found
128     * It scans for available CD-ROM drives and adds appropriate prefs items
129     */
130    
131     void SysAddCDROMPrefs(void)
132     {
133     // Don't scan for drives if nocdrom option given
134     if (PrefsFindBool("nocdrom"))
135     return;
136    
137     for (char letter = 'C'; letter <= 'Z'; letter++) {
138     int i = (int)(letter - 'A');
139     string rootdir = letter + ":\\";
140     if (GetDriveType(rootdir.c_str()) == DRIVE_CDROM)
141     PrefsAddString("cdrom", rootdir.c_str());
142     }
143     }
144    
145    
146     /*
147     * Add default serial prefs (must be added, even if no ports present)
148     */
149    
150     void SysAddSerialPrefs(void)
151     {
152     PrefsAddString("seriala", "COM1");
153     PrefsAddString("serialb", "COM2");
154     }
155    
156    
157     /*
158     * Read CD-ROM
159     * Must give cd some time to settle
160     * Can't give too much however, would be annoying, this is difficult..
161     */
162    
163     static inline int cd_read_with_retry(file_handle *fh, ULONG LBA, int count, char *buf )
164     {
165     if (!fh || !fh->fh)
166     return 0;
167    
168     return CdenableSysReadCdBytes(fh->fh, LBA, count, buf);
169     }
170    
171     static int cd_read(file_handle *fh, cachetype *cptr, ULONG LBA, int count, char *buf)
172     {
173     ULONG l1, l2, cc;
174     int i, c_count, got_bytes = 0, nblocks, s_inx, ss, first_block;
175     int ok_bytes = 0;
176     char *ptr, *ttptr = 0, *tmpbuf;
177    
178     if (count <= 0)
179     return 0;
180    
181     if (!fh || !fh->fh)
182     return 0;
183    
184     ss = 2048;
185     l1 = (LBA / ss) * ss;
186     l2 = ((LBA + count - 1 + ss) / ss) * ss;
187     cc = l2 - l1;
188     nblocks = cc / ss;
189     first_block = LBA / ss;
190    
191     ptr = buf;
192     s_inx = LBA - l1;
193     c_count = ss - s_inx;
194     if (c_count > count)
195     c_count = count;
196    
197     for (i = 0; i < nblocks; i++) {
198     if (!cache_get(cptr, first_block + i, sector_buffer))
199     break;
200    
201     memcpy(ptr, sector_buffer + s_inx, c_count);
202     ok_bytes += c_count;
203     ptr += c_count;
204     s_inx = 0;
205     c_count = ss;
206     if (c_count > count - ok_bytes)
207     c_count = count - ok_bytes;
208     }
209    
210     if (i != nblocks && count != ok_bytes) {
211     int bytes_left = count - ok_bytes;
212     int blocks_left = nblocks - i;
213     int alignedleft;
214    
215     // NEW read ahead code:
216     int ahead = CD_READ_AHEAD_SECTORS;
217     if (blocks_left < ahead) {
218     nblocks += (ahead - blocks_left);
219     blocks_left = ahead;
220     }
221    
222     alignedleft = blocks_left*ss;
223    
224     tmpbuf = (char *)VirtualAlloc(
225     NULL, alignedleft,
226     MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
227     if (tmpbuf) {
228     got_bytes = cd_read_with_retry(fh, (first_block + i) * ss, alignedleft, tmpbuf);
229     if (got_bytes != alignedleft) {
230     // should never happen
231     // Yes it does ...
232     if (got_bytes < 0)
233     got_bytes = 0;
234     if (c_count > got_bytes)
235     c_count = got_bytes;
236     if (c_count > 0) {
237     ttptr = tmpbuf;
238     memcpy(ptr, ttptr + s_inx, c_count);
239     ok_bytes += c_count;
240     }
241     VirtualFree(tmpbuf, 0, MEM_RELEASE );
242     return ok_bytes;
243     }
244     ttptr = tmpbuf;
245     for ( ; i < nblocks; i++) {
246     if (c_count > 0) {
247     memcpy(ptr, ttptr + s_inx, c_count);
248     ok_bytes += c_count;
249     ptr += c_count;
250     }
251     s_inx = 0;
252     c_count = ss;
253     if (c_count > count - ok_bytes)
254     c_count = count - ok_bytes;
255     cache_put(cptr, first_block + i, ttptr, ss);
256     ttptr += ss;
257     }
258     VirtualFree(tmpbuf, 0, MEM_RELEASE );
259     }
260     }
261    
262     return ok_bytes;
263     }
264    
265    
266     /*
267     * Check if file handle FH represents a readable CD-ROM
268     */
269    
270     static bool is_cdrom_readable(file_handle *fh)
271     {
272     if (!fh || !fh->fh)
273     return false;
274    
275     cache_clear(&fh->cache);
276    
277     DWORD dummy;
278     bool result = (0 != DeviceIoControl(
279     fh->fh,
280     IOCTL_STORAGE_CHECK_VERIFY,
281     NULL, 0,
282     NULL, 0,
283     &dummy,
284     NULL));
285     if (!result) {
286     const size_t n_bytes = 2048;
287     char *buffer = (char *)VirtualAlloc(NULL, n_bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
288     if (buffer) {
289     result = (cd_read_with_retry(fh, 0, n_bytes, buffer) == n_bytes);
290     VirtualFree(buffer, 0, MEM_RELEASE);
291     }
292     }
293    
294     return result;
295     }
296    
297    
298     /*
299     * Check if NAME represents a read-only file
300     */
301    
302     static bool is_read_only_path(const char *name)
303     {
304     DWORD attrib = GetFileAttributes((char *)name);
305     return (attrib != INVALID_FILE_ATTRIBUTES && ((attrib & FILE_ATTRIBUTE_READONLY) != 0));
306     }
307    
308    
309     /*
310     * Open file/device, create new file handle (returns NULL on error)
311     */
312    
313     void *Sys_open(const char *path_name, bool read_only)
314     {
315     file_handle * fh = NULL;
316    
317     // Parse path name and options
318     char name[MAX_PATH];
319     strcpy(name, path_name);
320    
321     // Normalize floppy / cd path
322     int name_len = strlen(name);
323     if (name_len == 1 && isalpha(name[0]))
324     strcat(name, ":\\");
325     if (name_len > 0 && name[name_len - 1] == ':')
326     strcat(name, "\\");
327     name_len = strlen(name);
328    
329     D(bug("Sys_open(%s, %s)\n", name, read_only ? "read-only" : "read/write"));
330     if (name_len > 0 && name[name_len - 1] == '\\') {
331     int type = GetDriveType(name);
332    
333     if (type == DRIVE_CDROM) {
334     read_only = true;
335     char device_name[MAX_PATH];
336     sprintf(device_name, "\\\\.\\%c:", name[0]);
337    
338     // Open device
339     HANDLE h = CreateFile(
340     device_name,
341     GENERIC_READ,
342     0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
343    
344     if (h != INVALID_HANDLE_VALUE) {
345     fh = new file_handle;
346     fh->name = strdup(name);
347     fh->fh = h;
348     fh->is_file = false;
349     fh->read_only = read_only;
350     fh->start_byte = 0;
351     fh->is_floppy = false;
352     fh->is_cdrom = true;
353     if (!PrefsFindBool("nocdrom"))
354     fh->is_media_present = is_cdrom_readable(fh);
355     }
356     }
357     }
358    
359     else { // Hard file
360    
361     // Check if write access is allowed, set read-only flag if not
362     if (!read_only && is_read_only_path(name))
363     read_only = true;
364    
365     // Open file
366     HANDLE h = CreateFile(
367     name,
368     read_only ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
369     0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
370    
371     if (h == INVALID_HANDLE_VALUE && !read_only) {
372     // Read-write failed, try read-only
373     read_only = true;
374     h = CreateFile(
375     name,
376     GENERIC_READ,
377     0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
378     }
379    
380     if (h != INVALID_HANDLE_VALUE) {
381     fh = new file_handle;
382     fh->name = strdup(name);
383     fh->fh = h;
384     fh->is_file = true;
385     fh->read_only = read_only;
386     fh->start_byte = 0;
387     fh->is_floppy = false;
388     fh->is_cdrom = false;
389    
390     // Detect disk image file layout
391     loff_t size = GetFileSize(h, NULL);
392     DWORD bytes_read;
393     uint8 data[256];
394     ReadFile(h, data, sizeof(data), &bytes_read, NULL);
395     FileDiskLayout(size, data, fh->start_byte, fh->file_size);
396     }
397     }
398    
399     if (fh->is_floppy && first_floppy == NULL)
400     first_floppy = fh;
401    
402     return fh;
403     }
404    
405    
406     /*
407     * Close file/device, delete file handle
408     */
409    
410     void Sys_close(void *arg)
411     {
412     file_handle *fh = (file_handle *)arg;
413     if (!fh)
414     return;
415    
416     if (fh->is_cdrom) {
417     cache_final(&fh->cache);
418     SysAllowRemoval((void *)fh);
419     }
420     if (fh->fh != NULL) {
421     CloseHandle(fh->fh);
422     fh->fh = NULL;
423     }
424     if (fh->name)
425     free(fh->name);
426    
427     delete fh;
428     }
429    
430    
431     /*
432     * Read "length" bytes from file/device, starting at "offset", to "buffer",
433     * returns number of bytes read (or 0)
434     */
435    
436     size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
437     {
438     file_handle *fh = (file_handle *)arg;
439     if (!fh)
440     return 0;
441    
442     DWORD bytes_read = 0;
443    
444     if (fh->is_file) {
445     // Seek to position
446     LONG lo = (LONG)offset;
447     LONG hi = (LONG)(offset >> 32);
448     DWORD r = SetFilePointer(fh->fh, lo, &hi, FILE_BEGIN);
449     if (r == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
450     return 0;
451    
452     // Read data
453     if (ReadFile(fh->fh, buffer, length, &bytes_read, NULL) == 0)
454     bytes_read = 0;
455     }
456     else if (fh->is_cdrom) {
457     int bytes_left, try_bytes, got_bytes;
458     char *b = (char *)buffer;
459     bytes_left = length;
460     while (bytes_left) {
461     try_bytes = min(bytes_left, 32768);
462     if (fh->is_cdrom) {
463     got_bytes = cd_read(fh, &fh->cache, (DWORD)offset, try_bytes, b);
464     if (got_bytes != try_bytes && !PrefsFindBool("nocdrom"))
465     fh->is_media_present = is_cdrom_readable(fh);
466     }
467     b += got_bytes;
468     offset += got_bytes;
469     bytes_read += got_bytes;
470     bytes_left -= got_bytes;
471     if (got_bytes != try_bytes)
472     bytes_left = 0;
473     }
474     }
475     // TODO: other media
476    
477     return bytes_read;
478     }
479    
480    
481     /*
482     * Write "length" bytes from "buffer" to file/device, starting at "offset",
483     * returns number of bytes written (or 0)
484     */
485    
486     size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length)
487     {
488     file_handle *fh = (file_handle *)arg;
489     if (!fh)
490     return 0;
491    
492     DWORD bytes_written = 0;
493    
494     if (fh->is_file) {
495     // Seek to position
496     LONG lo = (LONG)offset;
497     LONG hi = (LONG)(offset >> 32);
498     DWORD r = SetFilePointer(fh->fh, lo, &hi, FILE_BEGIN);
499     if (r == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
500     return 0;
501    
502     // Write data
503     if (WriteFile(fh->fh, buffer, length, &bytes_written, NULL) == 0)
504     bytes_written = 0;
505     }
506     // TODO: other media
507    
508     return bytes_written;
509     }
510    
511    
512     /*
513     * Return size of file/device (minus header)
514     */
515    
516     loff_t SysGetFileSize(void *arg)
517     {
518     file_handle *fh = (file_handle *)arg;
519     if (!fh)
520     return true;
521    
522     if (fh->is_file)
523     return fh->file_size;
524     else if (fh->is_cdrom)
525     return 0x28A00000; // FIXME: get real CD-ROM size
526     else {
527     // TODO: other media
528     return 0;
529     }
530     }
531    
532    
533     /*
534     * Eject volume (if applicable)
535     */
536    
537     void SysEject(void *arg)
538     {
539     file_handle *fh = (file_handle *)arg;
540     if (!fh)
541     return;
542    
543     if (fh->is_cdrom && fh->fh) {
544     fh->is_media_present = false;
545     // Commented out because there was some problems, but can't remember
546     // exactly ... need to find out
547     // EjectVolume(toupper(*fh->name),false);
548    
549     // Preventing is cumulative, try to make sure it's indeed released now
550     for (int i = 0; i < 10; i++)
551     PreventRemovalOfVolume(fh->fh, false);
552    
553     if (!PrefsFindBool("nocdrom")) {
554     DWORD dummy;
555     DeviceIoControl(
556     fh->fh,
557     IOCTL_STORAGE_EJECT_MEDIA,
558     NULL, 0,
559     NULL, 0,
560     &dummy,
561     NULL
562     );
563     }
564     cache_clear(&fh->cache);
565     fh->start_byte = 0;
566     }
567     // TODO: handle floppies
568     }
569    
570    
571     /*
572     * Format volume (if applicable)
573     */
574    
575     bool SysFormat(void *arg)
576     {
577     file_handle *fh = (file_handle *)arg;
578     if (!fh)
579     return false;
580    
581     //!!
582     return true;
583     }
584    
585    
586     /*
587     * Check if file/device is read-only (this includes the read-only flag on Sys_open())
588     */
589    
590     bool SysIsReadOnly(void *arg)
591     {
592     file_handle *fh = (file_handle *)arg;
593     if (!fh)
594     return true;
595    
596     return fh->read_only;
597     }
598    
599    
600     /*
601     * Check if the given file handle refers to a fixed or a removable disk
602     */
603    
604     bool SysIsFixedDisk(void *arg)
605     {
606     file_handle *fh = (file_handle *)arg;
607     if (!fh)
608     return true;
609    
610     if (fh->is_file)
611     return true;
612     else if (fh->is_floppy || fh->is_cdrom)
613     return false;
614     else
615     return true;
616     }
617    
618    
619     /*
620     * Check if a disk is inserted in the drive (always true for files)
621     */
622    
623     bool SysIsDiskInserted(void *arg)
624     {
625     file_handle *fh = (file_handle *)arg;
626     if (!fh)
627     return false;
628    
629     if (fh->is_file)
630     return true;
631     else if (fh->is_cdrom && !PrefsFindBool("nocdrom")) {
632     if (PrefsFindBool("pollmedia"))
633     fh->is_media_present = is_cdrom_readable(fh);
634     return fh->is_media_present;
635     }
636     else {
637     // TODO: other media
638     }
639    
640     return false;
641     }
642    
643    
644     /*
645     * Prevent medium removal (if applicable)
646     */
647    
648     void SysPreventRemoval(void *arg)
649     {
650     file_handle *fh = (file_handle *)arg;
651     if (!fh)
652     return;
653    
654     if (fh->is_cdrom && fh->fh)
655     PreventRemovalOfVolume(fh->fh, true);
656     }
657    
658    
659     /*
660     * Allow medium removal (if applicable)
661     */
662    
663     void SysAllowRemoval(void *arg)
664     {
665     file_handle *fh = (file_handle *)arg;
666     if (!fh)
667     return;
668    
669     if (fh->is_cdrom && fh->fh)
670     PreventRemovalOfVolume(fh->fh, false);
671     }
672    
673    
674     /*
675     * Read CD-ROM TOC (binary MSF format, 804 bytes max.)
676     */
677    
678     bool SysCDReadTOC(void *arg, uint8 *toc)
679     {
680     file_handle *fh = (file_handle *)arg;
681     if (!fh || !fh->fh || !fh->is_cdrom)
682     return false;
683    
684     DWORD dummy;
685     return DeviceIoControl(fh->fh,
686     IOCTL_CDROM_READ_TOC,
687     NULL, 0,
688     toc, min((int)sizeof(CDROM_TOC), 804),
689     &dummy,
690     NULL);
691     }
692    
693    
694     /*
695     * Read CD-ROM position data (Sub-Q Channel, 16 bytes, see SCSI standard)
696     */
697    
698     bool SysCDGetPosition(void *arg, uint8 *pos)
699     {
700     file_handle *fh = (file_handle *)arg;
701     if (!fh || !fh->fh || !fh->is_cdrom)
702     return false;
703    
704     SUB_Q_CHANNEL_DATA q_data;
705    
706     CDROM_SUB_Q_DATA_FORMAT q_format;
707     q_format.Format = IOCTL_CDROM_CURRENT_POSITION;
708     q_format.Track = 0; // used only by ISRC reads
709    
710     DWORD dwBytesReturned = 0;
711     bool ok = DeviceIoControl(fh->fh,
712     IOCTL_CDROM_READ_Q_CHANNEL,
713     &q_format, sizeof(CDROM_SUB_Q_DATA_FORMAT),
714     &q_data, sizeof(SUB_Q_CHANNEL_DATA),
715     &dwBytesReturned,
716     NULL);
717     if (ok)
718     memcpy(pos, &q_data.CurrentPosition, sizeof(SUB_Q_CURRENT_POSITION));
719    
720     return ok;
721     }
722    
723    
724     /*
725     * Play CD audio
726     */
727    
728     bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end_m, uint8 end_s, uint8 end_f)
729     {
730     file_handle *fh = (file_handle *)arg;
731     if (!fh || !fh->fh || !fh->is_cdrom)
732     return false;
733    
734     CDROM_PLAY_AUDIO_MSF msf;
735     msf.StartingM = start_m;
736     msf.StartingS = start_s;
737     msf.StartingF = start_f;
738     msf.EndingM = end_m;
739     msf.EndingS = end_s;
740     msf.EndingF = end_f;
741    
742     DWORD dwBytesReturned = 0;
743     return DeviceIoControl(fh->fh,
744     IOCTL_CDROM_PLAY_AUDIO_MSF,
745     &msf, sizeof(CDROM_PLAY_AUDIO_MSF),
746     NULL, 0,
747     &dwBytesReturned,
748     NULL);
749     }
750    
751    
752     /*
753     * Pause CD audio
754     */
755    
756     bool SysCDPause(void *arg)
757     {
758     file_handle *fh = (file_handle *)arg;
759     if (!fh || !fh->fh || !fh->is_cdrom)
760     return false;
761    
762     DWORD dwBytesReturned = 0;
763     return DeviceIoControl(fh->fh,
764     IOCTL_CDROM_PAUSE_AUDIO,
765     NULL, 0,
766     NULL, 0,
767     &dwBytesReturned,
768     NULL);
769     }
770    
771    
772     /*
773     * Resume paused CD audio
774     */
775    
776     bool SysCDResume(void *arg)
777     {
778     file_handle *fh = (file_handle *)arg;
779     if (!fh || !fh->fh || !fh->is_cdrom)
780     return false;
781    
782     DWORD dwBytesReturned = 0;
783     return DeviceIoControl(fh->fh,
784     IOCTL_CDROM_RESUME_AUDIO,
785     NULL, 0,
786     NULL, 0,
787     &dwBytesReturned, NULL);
788     }
789    
790    
791     /*
792     * Stop CD audio
793     */
794    
795     bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f)
796     {
797     file_handle *fh = (file_handle *)arg;
798     if (!fh || !fh->fh || !fh->is_cdrom)
799     return false;
800    
801     DWORD dwBytesReturned = 0;
802     return DeviceIoControl(fh->fh,
803     IOCTL_CDROM_STOP_AUDIO,
804     NULL, 0,
805     NULL, 0,
806     &dwBytesReturned,
807     NULL);
808     }
809    
810    
811     /*
812     * Perform CD audio fast-forward/fast-reverse operation starting from specified address
813     */
814    
815     bool SysCDScan(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, bool reverse)
816     {
817     file_handle *fh = (file_handle *)arg;
818     if (!fh || !fh->fh || !fh->is_cdrom)
819     return false;
820    
821     CDROM_SEEK_AUDIO_MSF msf;
822     msf.M = start_m;
823     msf.S = start_s;
824     msf.F = start_f;
825    
826     DWORD dwBytesReturned = 0;
827     return DeviceIoControl(fh->fh,
828     IOCTL_CDROM_SEEK_AUDIO_MSF,
829     &msf, sizeof(CDROM_SEEK_AUDIO_MSF),
830     NULL, 0,
831     &dwBytesReturned,
832     NULL);
833     }
834    
835    
836     /*
837     * Set CD audio volume (0..255 each channel)
838     */
839    
840     void SysCDSetVolume(void *arg, uint8 left, uint8 right)
841     {
842     file_handle *fh = (file_handle *)arg;
843     if (!fh || !fh->fh || !fh->is_cdrom)
844     return;
845    
846     VOLUME_CONTROL vc;
847     vc.PortVolume[0] = left;
848     vc.PortVolume[1] = right;
849     vc.PortVolume[2] = left;
850     vc.PortVolume[3] = right;
851    
852     DWORD dwBytesReturned = 0;
853     DeviceIoControl(fh->fh,
854     IOCTL_CDROM_SET_VOLUME,
855     &vc, sizeof(VOLUME_CONTROL),
856     NULL, 0,
857     &dwBytesReturned,
858     NULL);
859     }
860    
861    
862     /*
863     * Get CD audio volume (0..255 each channel)
864     */
865    
866     void SysCDGetVolume(void *arg, uint8 &left, uint8 &right)
867     {
868     file_handle *fh = (file_handle *)arg;
869     if (!fh)
870     return;
871    
872     left = right = 0;
873     if (!fh->fh || !fh->is_cdrom)
874     return;
875    
876     VOLUME_CONTROL vc;
877     memset(&vc, 0, sizeof(vc));
878    
879     DWORD dwBytesReturned = 0;
880     if (DeviceIoControl(fh->fh,
881     IOCTL_CDROM_GET_VOLUME,
882     NULL, 0,
883     &vc, sizeof(VOLUME_CONTROL),
884     &dwBytesReturned,
885     NULL))
886     {
887     left = vc.PortVolume[0];
888     right = vc.PortVolume[1];
889     }
890     }