// Win32-Steuercodes und -Konstanten für optische Laufwerke — HERGELEITET, // nicht abgeschrieben (Regel D). Bewusst OHNE koffi: disc.ts und die Tests // brauchen die Zahlen, aber kein natives Modul. Die echten Aufrufe stehen // allein in win32.ts (Wächter R1). // // Ein Zahlendreher liefert kein „unbekannter Befehl", sondern // ERROR_INVALID_FUNCTION — das sieht aus wie „das Laufwerk kann das nicht"; // test/laufwerk.test.ts rechnet die Werte deshalb gegen die in der // Microsoft-Dokumentation genannten Zahlen gegen. // // Fundstellen: CTL_CODE/FILE_DEVICE_*/METHOD_* — winioctl.h; // IOCTL_STORAGE_* — learn.microsoft.com/windows/win32/api/winioctl; // IOCTL_CDROM_* — ntddcdrm.h. Gemessen in beweise/laufwerk.js (30.08.2026), // Herleitung identisch mit src/rippy/drives/win_ioctl.py (Docker-Zweig, // dort an echter Hardware bewiesen). // ── CTL_CODE aus winioctl.h, eins zu eins ─────────────────────────────── export function ctlCode(geraetetyp: number, funktion: number, methode: number, zugriff: number): number { return ((geraetetyp << 16) | (zugriff << 14) | (funktion << 2) | methode) >>> 0 } const FILE_DEVICE_CD_ROM = 0x02 const FILE_DEVICE_DISK = 0x07 const FILE_DEVICE_MASS_STORAGE = 0x2d const METHOD_BUFFERED = 0 const METHOD_OUT_DIRECT = 2 const FILE_ANY_ACCESS = 0 const FILE_READ_ACCESS = 0x0001 export const IOCTL_STORAGE_CHECK_VERIFY2 = ctlCode(FILE_DEVICE_MASS_STORAGE, 0x0200, METHOD_BUFFERED, FILE_ANY_ACCESS) export const IOCTL_STORAGE_MEDIA_REMOVAL = ctlCode(FILE_DEVICE_MASS_STORAGE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS) export const IOCTL_STORAGE_EJECT_MEDIA = ctlCode(FILE_DEVICE_MASS_STORAGE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS) // LOAD_MEDIA (Schublade einziehen) fehlt MIT ABSICHT: Am BU40N des // Commanders antwortet es mit ERROR_INVALID_FUNCTION — bei flachen und // externen Laufwerken die Regel. Ein Knopf, der auf der Hälfte aller // Laufwerke still nichts tut, wäre ein Placebo (windows.py, 28.08.2026). export const IOCTL_STORAGE_QUERY_PROPERTY = ctlCode(FILE_DEVICE_MASS_STORAGE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) export const IOCTL_CDROM_DISK_TYPE = ctlCode(FILE_DEVICE_CD_ROM, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS) export const IOCTL_DISK_GET_LENGTH_INFO = ctlCode(FILE_DEVICE_DISK, 0x0017, METHOD_BUFFERED, FILE_READ_ACCESS) // Audio-CD (W-7): TOC + rohe Sektoren. Herleitung wie win_ioctl.py. export const IOCTL_CDROM_READ_TOC = ctlCode(FILE_DEVICE_CD_ROM, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) export const IOCTL_CDROM_RAW_READ = ctlCode(FILE_DEVICE_CD_ROM, 0x000f, METHOD_OUT_DIRECT, FILE_READ_ACCESS) export const CDROM_TOC_BYTES = 4 + 100 * 8 // STORAGE_DEVICE_DESCRIPTOR: Offsets der Zeichenketten-Verweise (winioctl.h). export const SDD_VENDOR_ID_OFFSET = 12 export const SDD_PRODUCT_ID_OFFSET = 16 export const SDD_PRODUCT_REVISION_OFFSET = 20 export const SDD_SERIAL_NUMBER_OFFSET = 24 export const CDROM_DISK_AUDIO_TRACK = 0x01 export const CDROM_DISK_DATA_TRACK = 0x02 // GetLastError-Werte, an denen Zugriffe scheitern (winerror.h). export const ERROR_INVALID_FUNCTION = 1 export const ERROR_FILE_NOT_FOUND = 2 export const ERROR_ACCESS_DENIED = 5 export const ERROR_NOT_READY = 21 export const ERROR_NOT_SUPPORTED = 50 export const ERROR_NO_MEDIA_IN_DRIVE = 1112 export const DRIVE_CDROM = 5 export const GENERIC_READ = 0x8000_0000 export const FILE_SHARE_READ = 0x01 export const FILE_SHARE_WRITE = 0x02 export const OPEN_EXISTING = 3 /** Ein fehlgeschlagener Win32-Aufruf, mit dem Code von GetLastError(). */ export class Win32Fehler extends Error { constructor( was: string, readonly code: number, ) { super(`${was} scheiterte (Win32-Fehler ${code})`) this.name = 'Win32Fehler' } } /** Die Schnittstelle, gegen die disc.ts arbeitet — in Tests nachgebildet. */ export interface LaufwerkApi { laufwerksbuchstaben(): string[] laufwerkstyp(buchstabe: string): number /** Öffnet das Gerät. `zugriff` 0 fragt das GERÄT, GENERIC_READ das MEDIUM — * bei gestörter Disc scheitert das zweite und das erste geht weiter * (rc11-Befund, in beweise/laufwerk.js nachgemessen). Wirft Win32Fehler. */ oeffnen(geraetepfad: string, zugriff: number): unknown schliessen(handle: unknown): void /** DeviceIoControl. Wirft Win32Fehler, sonst die Antwort (ggf. leer). */ steuern(handle: unknown, code: number, eingabe?: Buffer | null, ausgabeBytes?: number): Buffer }