Projekte > Optische Laufwerke > Medieninformationen

CD Text (IOCTL)

IOCTL_CDROM_READ_TOC_EX

CDROM_READ_TOC_EX_FORMAT_CDTEXT

Mit dem IOCTL_CDROM_READ_TOC_EX control code, CDROM_READ_TOC_EX Struktur mit dem Format CDROM_READ_TOC_EX_FORMAT_CDTEXT als Input und der CDROM_TOC_CD_TEXT_DATA Struktur als Output erhält man dasselbe Ergebnis wie in CD Text (MMC). Die Deklarationen finden sich in der ntddcdrm.h. Zu finden zum Beispiel bei Google.

 
#define IOCTL_CDROM_READ_TOC_EX
              CTL_CODE(IOCTL_CDROM_BASE, 0x0015, METHOD_BUFFERED, FILE_READ_ACCESS)
          

Die Function CTL_CODE ist zum Beispiel bei OSR Online erläutert. Die Konstanten finden sich in der Ntddcdrm.h und der devioctl.h (Zum Beispiel hier).

 
  #define IOCTL_CDROM_BASE          FILE_DEVICE_CD_ROM
 
  #define FILE_DEVICE_CD_ROM        0x00000002
 
  #define FILE_READ_ACCESS          ( 0x0001 )
 
  #define METHOD_BUFFERED           0
          

Daraus ergibt sich dann in Delphi folgendes:

 
  const
    FILE_DEVICE_CD_ROM = $00000002;
    IOCTL_CDROM_BASE   = FILE_DEVICE_CD_ROM;
    FILE_READ_ACCESS   = $0001;
    METHOD_BUFFERED    = 0;
 
  const
    IOCTL_CDROM_READ_TOC_EX = (IOCTL_CDROM_BASE shl 16) or
                              (FILE_READ_ACCESS shl 14) or
                              ($0015 shl 2) or
                              METHOD_BUFFERED;
          
 

Inputstruktur

Die CDROM_READ_TOC_EX Stuktur und die dazugehörigen Konstanten sind in der ntddcdrm.h deklariert.

 
  typedef struct _CDROM_READ_TOC_EX {
    UCHAR Format  :4;
    UCHAR Reserved1  :3;
    UCHAR Msf  :1;
    UCHAR SessionTrack;
    UCHAR Reserved2;
    UCHAR Reserved3;
  } CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX;
 
  #define CDROM_READ_TOC_EX_FORMAT_TOC      0x00
  #define CDROM_READ_TOC_EX_FORMAT_SESSION  0x01
  #define CDROM_READ_TOC_EX_FORMAT_FULL_TOC 0x02
  #define CDROM_READ_TOC_EX_FORMAT_PMA      0x03
  #define CDROM_READ_TOC_EX_FORMAT_ATIP     0x04
  #define CDROM_READ_TOC_EX_FORMAT_CDTEXT   0x05
          

In Delphi ergibt sich daraus:

 
  type
    TCDROM_READ_TOC_EX = Record
      Format_Reserved1_MSF : Byte;
      SessionTrack         : Byte;
      Reserved2            : Byte;
      Reserved3            : Byte;
    end;
    PCDROM_READ_TOC_EX = ^TCDROM_READ_TOC_EX;
 
  const
    CDROM_READ_TOC_EX_FORMAT_TOC      = $00;
    CDROM_READ_TOC_EX_FORMAT_SESSION  = $01;
    CDROM_READ_TOC_EX_FORMAT_FULL_TOC = $02;
    CDROM_READ_TOC_EX_FORMAT_PMA      = $03;
    CDROM_READ_TOC_EX_FORMAT_ATIP     = $04;
    CDROM_READ_TOC_EX_FORMAT_CDTEXT   = $05;
          

Es muss nur der Wert für das gewünschte Format gesetzt werden.

 

Outputstruktur

Die CDROM_TOC_CD_TEXT_DATA und die CDROM_TOC_CD_TEXT_DATA_BLOCK Struktur sind ebenfalls in der Ntddcdrm.h deklariert.

 
  //
  // CD ROM Table OF Contents
  // Format 5 - CD Text Info
  //
  typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK {
      UCHAR PackType;
      UCHAR TrackNumber       : 7;
      UCHAR ExtensionFlag     : 1;  // should be zero!
      UCHAR SequenceNumber;
      UCHAR CharacterPosition : 4;
      UCHAR BlockNumber       : 3;
      UCHAR Unicode           : 1;
      union {
          UCHAR Text[12];
          WCHAR WText[6];
      };
      UCHAR CRC[2];
  } CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK;
 
  typedef struct _CDROM_TOC_CD_TEXT_DATA {
 
      //
      // Header
      //
 
      UCHAR Length[2];  // add two bytes for this field
      UCHAR Reserved1;
      UCHAR Reserved2;
 
      //
      // the text info comes in discrete blocks of
      // a heavily-overloaded structure
      //
 
      CDROM_TOC_CD_TEXT_DATA_BLOCK Descriptors[0];
 
  } CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA;
 
  //
  // These are the types used for PackType field in CDROM_TOC_CD_TEXT_DATA_BLOCK
  // and also for requesting specific info from IOCTL_CDROM_READ_CD_TEXT
  //
  #define CDROM_CD_TEXT_PACK_ALBUM_NAME 0x80
  #define CDROM_CD_TEXT_PACK_PERFORMER  0x81
  #define CDROM_CD_TEXT_PACK_SONGWRITER 0x82
  #define CDROM_CD_TEXT_PACK_COMPOSER   0x83
  #define CDROM_CD_TEXT_PACK_ARRANGER   0x84
  #define CDROM_CD_TEXT_PACK_MESSAGES   0x85
  #define CDROM_CD_TEXT_PACK_DISC_ID    0x86
  #define CDROM_CD_TEXT_PACK_GENRE      0x87
  #define CDROM_CD_TEXT_PACK_TOC_INFO   0x88
  #define CDROM_CD_TEXT_PACK_TOC_INFO2  0x89
  // 0x8a - 0x8d are reserved....
  #define CDROM_CD_TEXT_PACK_UPC_EAN    0x8e
  #define CDROM_CD_TEXT_PACK_SIZE_INFO  0x8f
          

Ohne Berücksichtigung von Unicode könnte man in Delphi dieses deklarieren. Dabei wird die Anzahl der Datensätze willkürlich begrenzt.

 
  type
    TCDROM_TOC_CD_TEXT_DATA_BLOCK = Record
      Header : Array[0..3] of Byte;
      Data   : Array[0..11] of AnsiChar;
      CRC    : Array[0..1] of Byte;
    end;
    PCDROM_TOC_CD_TEXT_DATA_BLOCK = ^TCDROM_TOC_CD_TEXT_DATA_BLOCK;
 
  type
    TCDROM_TOC_CD_TEXT_DATA = Record
      Length     : Word;
      Reserved1  : Byte;
      Reserved2  : Byte;
      Data_Block : Array[$00..$63] of TCDROM_TOC_CD_TEXT_DATA_BLOCK;
    end;
    PCDROM_TOC_CD_TEXT_DATA = ^TCDROM_TOC_CD_TEXT_DATA;
 
  const
    CDROM_CD_TEXT_PACK_ALBUM_NAME  = $80;
    CDROM_CD_TEXT_PACK_PERFORMER   = $81;
    CDROM_CD_TEXT_PACK_SONGWRITER  = $82;
    CDROM_CD_TEXT_PACK_COMPOSER    = $83;
    CDROM_CD_TEXT_PACK_ARRANGER    = $84;
    CDROM_CD_TEXT_PACK_MESSAGES    = $85;
    CDROM_CD_TEXT_PACK_DISC_ID     = $86;
    CDROM_CD_TEXT_PACK_GENRE       = $87;
    CDROM_CD_TEXT_PACK_TOC_INFO    = $88;
    CDROM_CD_TEXT_PACK_TOC_INFO2   = $89;
    CDROM_CD_TEXT_PACK_UPC_EAN     = $8E;
    CDROM_CD_TEXT_PACK_SIZE_INFO   = $8F;
          

Die Antwortstruktur entspricht damit der im Kapitel CD Text (MMC) dargestellten. Die Stuktur TTableOfContentsCDText wird hier für die Auswertung verwendet.

 

Ausführung und Auswertung

Nach dem Ausfüllen des Command Descriptors Blockes wird die Abfrage ausgeführt. Auch hier erfolgt derzeit noch keine weitere Auswertung.

 
  function TOptDrives.IOCTL_CDROM_READ_TOC_EX_CDText(aDevice: THandle): Boolean;
  {*******************************************************************************
  *  Den CD-Text auslesen.
  }
  var
    pCDTocEx    : PCDROM_READ_TOC_EX;
    pCDTextData : PCDROM_TOC_CD_TEXT_DATA;
    nReturned   : Cardinal;
    i, j        : Cardinal;
  begin
    Result := False;
    {
    *  CDText initialisieren.
    }
    FillChar(FDrive[FActive].CDROM_CDText, SizeOf(FDrive[FActive].CDROM_CDText), $00);
    {
    *  Input
    }
    GetMem(pCDTocEx, SizeOf(TCDROM_READ_TOC_EX));
    pCDTocEx.Format_Reserved1_MSF := CDROM_READ_TOC_EX_FORMAT_CDTEXT;
    pCDTocEx.SessionTrack         := $00;
    pCDTocEx.Reserved2            := $00;
    pCDTocEx.Reserved3            := $00;
    {
    *  Output
    }
    GetMem(pCDTextData, SizeOf(TCDROM_TOC_CD_TEXT_DATA));
    {
    *  Größe des Outputs abfragen.
    }
    if DeviceIoControl(aDevice,
                       IOCTL_CDROM_READ_TOC_EX,
                       pCDTocEx, SizeOf(TCDROM_READ_TOC_EX),
                       pCDTextData, SizeOf(TCDROM_TOC_CD_TEXT_DATA),
                       nReturned, nil)
    then begin
      pCDTextData.Length := Swap(pCDTextData.Length);
      {
      *  Größe des Outputs neu festlegen.
      }
      nReturned := pCDTextData.Length + SizeOf(Word);
      GetMem(pCDTextData, nReturned);
      {
      *  Neu abfragen.
      }
      if DeviceIoControl(aDevice,
                         IOCTL_CDROM_READ_TOC_EX,
                         pCDTocEx, SizeOf(TCDROM_READ_TOC_EX),
                         pCDTextData, nReturned,
                         nReturned, nil) then
      with FDrive[FActive].CDROM_CDText
      do begin
        DataLength := Swap(pCDTextData.Length);
        {
        *  Prüfen, ob Daten folgen.
        *  Bei einer leeren Antwort ist der Wert wegen der Initialisierung 0.
        }
        if DataLength > 2
        then begin
          {
          *  Rohen Text übernehmen.
          }
          SetLength(Pack, (DataLength - 2) div 18);
          for i := 0 to ((DataLength - 2) div 18) - 1
          do begin
            {  Header  }
            for j := 0 to 3
            do Pack[i][j] := pCDTextData.Data_Block[i].Header[j];
            {  Daten  }
            for j := 0 to 11
            do Pack[i][j + 4] := Ord(pCDTextData.Data_Block[i].Data[j]);
            {  CRC  }
            for j := 0 to 1
            do Pack[i][j + 16] := pCDTextData.Data_Block[i].CRC[j];
          end;
        end;
        {
        *  Ergebnis
        }
        Result := True;
      end;
    end;
  end;
          

 

Demo, welche diese Funktion nutzt:

CD Text IOCTL (podCDTextIOCTL.7z - 314 kb) MD5 (1 kb). Stand: 11. August 2013