HostCDROM.java
     1: //========================================================================================
     2: //  HostCDROM.java
     3: //    en:Host CD-ROM
     4: //    ja:ホストCD-ROM
     5: //  Copyright (C) 2003-2025 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.awt.event.*;  //ActionListener
    16: import java.lang.foreign.*;
    17: import java.lang.invoke.*;
    18: import java.util.*;  //NoSuchElementException
    19: import javax.sound.sampled.*;
    20: import javax.swing.*;
    21: import javax.swing.event.*;  //ChangeListener
    22: 
    23: //class HostCDROM
    24: //  WindowsのときホストマシンのDVD/CD-ROMドライブをSCSI CD-ROMドライブとして使用します
    25: public class HostCDROM {
    26: 
    27:   //定数
    28: 
    29:   //設定
    30:   public static final boolean HCD_ENABLED = true;  //true=有効
    31:   static final int HCD_DEFAULT_SCSI_ID = 6;  //デフォルトのSCSI ID(0~15)
    32:   static final int HCD_DEFAULT_VOLUME = 25;  //デフォルトの音量(0~100)
    33:   static final int HCD_PLAY_QUEUE_SIZE = 4;  //再生キューのサイズ
    34:   static final int HCD_PLAY_SECTORS = 30;  //一度に再生するセクタ数。30セクタ
    35:   static final int HCD_PLAY_MILLIS = 1000 * HCD_PLAY_SECTORS / 75;  //一度に再生するミリ秒数。1000*30/75=400ms
    36:   static final int HCD_PLAY_BYTES = 2352 * HCD_PLAY_SECTORS;  //一度に再生するバイト数。2352*30=70560バイト
    37: 
    38:   //エラーコード
    39:   //  https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
    40:   static final int ERROR_NOT_READY = 21;
    41:   static final int ERROR_WRONG_DISK = 34;
    42:   static final int ERROR_NO_MORE_ITEMS = 259;
    43: 
    44:   //ファイル操作
    45:   static final long INVALID_HANDLE_VALUE = -1L;
    46:   //  https://learn.microsoft.com/ja-jp/windows/win32/secauthz/generic-access-rights
    47:   static final int GENERIC_ALL = 0x10000000;
    48:   static final int GENERIC_EXECUTE = 0x20000000;
    49:   static final int GENERIC_WRITE = 0x40000000;
    50:   static final int GENERIC_READ = 0x80000000;
    51:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilea
    52:   static final int FILE_SHARE_READ = 0x00000001;
    53:   static final int FILE_SHARE_WRITE = 0x00000002;
    54:   static final int FILE_SHARE_DELETE = 0x00000004;
    55:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilea
    56:   static final int CREATE_NEW = 1;
    57:   static final int CREATE_ALWAYS = 2;
    58:   static final int OPEN_EXISTING = 3;
    59:   static final int OPEN_ALWAYS = 4;
    60:   static final int TRUNCATE_EXISTING = 5;
    61:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-setfilepointerex
    62:   static final int FILE_BEGIN = 0;
    63:   static final int FILE_CURRENT = 1;
    64:   static final int FILE_END = 2;
    65: 
    66:   //CD-ROM操作
    67:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-getdrivetypea
    68:   static final int DRIVE_UNKNOWN = 0;
    69:   static final int DRIVE_NO_ROOT_DIR = 1;
    70:   static final int DRIVE_REMOVABLE = 2;
    71:   static final int DRIVE_FIXED = 3;
    72:   static final int DRIVE_REMOTE = 4;
    73:   static final int DRIVE_CDROM = 5;
    74:   static final int DRIVE_RAMDISK = 6;
    75:   //DeviceIoControl
    76:   static final int IOCTL_CDROM_READ_TOC = 0x00024000;
    77:   static final int IOCTL_CDROM_RAW_READ = 0x0002403e;
    78:   static final int IOCTL_CDROM_READ_TOC_EX = 0x00024054;
    79:   static final int IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x0004d014;
    80:   static final int IOCTL_SCSI_PASS_THROUGH_DIRECT_EX = 0x0004d048;
    81:   static final int IOCTL_STORAGE_CHECK_VERIFY2 = 0x002d0800;
    82:   static final int IOCTL_STORAGE_LOAD_MEDIA2 = 0x002d080c;
    83:   static final int IOCTL_STORAGE_QUERY_PROPERTY = 0x002d1400;
    84:   static final int IOCTL_STORAGE_CHECK_VERIFY = 0x002d4800;
    85:   static final int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002d4804;
    86:   static final int IOCTL_STORAGE_EJECT_MEDIA = 0x002d4808;
    87:   static final int IOCTL_STORAGE_LOAD_MEDIA = 0x002d480c;
    88:   static final int CDROM_READ_TOC_EX_FORMAT_TOC = 0x00000000;
    89:   //CDROM_TOC
    90:   static final int MAXIMUM_NUMBER_TRACKS = 0x00000064;
    91:   //TRACK_MODE_TYPE
    92:   //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddcdrm/ne-ntddcdrm-_track_mode_type
    93:   static final int YellowMode2 = 0;
    94:   static final int XAForm2 = 1;
    95:   static final int CDDA = 2;
    96:   static final int RawWithC2AndSubCode = 3;
    97:   static final int RawWithC2 = 4;
    98:   static final int RawWithSubCode = 5;
    99: 
   100:   //STORAGE_PROPERTY_ID列挙
   101:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/winioctl/ne-winioctl-storage_property_id
   102:   static final int StorageDeviceProperty = 0;
   103: 
   104:   //STORAGE_QUERY_TYPE列挙
   105:   //  https://learn.microsoft.com/ja-jp/windows/win32/api/winioctl/ne-winioctl-storage_query_type
   106:   static final int PropertyStandardQuery = 0;
   107: 
   108:   //バスタイプ
   109:   static final int BusTypeUnknown = 0;
   110:   static final int BusTypeScsi = 1;
   111:   static final int BusTypeAtapi = 2;
   112:   static final int BusTypeAta = 3;
   113:   static final int BusType1394 = 4;
   114:   static final int BusTypeSsa = 5;
   115:   static final int BusTypeFibre = 6;
   116:   static final int BusTypeUsb = 7;
   117:   static final int BusTypeRAID = 8;
   118:   static final int BusTypeiScsi = 9;
   119:   static final int BusTypeSas = 10;
   120:   static final int BusTypeSata = 11;
   121:   static final int BusTypeSd = 12;
   122:   static final int BusTypeMmc = 13;
   123:   static final int BusTypeVirtual = 14;
   124:   static final int BusTypeFileBackedVirtual = 15;
   125:   static final int BusTypeSpaces = 16;
   126:   static final int BusTypeNvme = 17;
   127:   static final int BusTypeSCM = 18;
   128:   static final int BusTypeUfs = 19;
   129:   static final int BusTypeNvmeof = 20;
   130:   static final int BusTypeMax = 21;
   131:   static final int BusTypeMaxReserved = 127;
   132: 
   133:   //変数
   134: 
   135:   //パラメータ
   136:   static boolean hcdAvailable;  //true=ホストのCD-ROMが利用可能。WindowsでCD-ROMドライブがあり操作できる
   137:   static boolean hcdDebugInfo;  //true=デバッグ情報を出力する
   138:   static boolean hcdConnectNext;  //true=次回は接続する
   139:   static boolean hcdConnected;  //true=接続している
   140:   static int hcdSCSIIdNext;  //次回のSCSI ID
   141:   public static int hcdSCSIId;  //SCSI ID
   142:   static int hcdVolumeInt;  //音量(0~100)
   143:   static float hcdVolumeFloat;  //音量(0.0~1.0)
   144: 
   145:   //メニュー
   146:   static JSpinner hcdIdSpinner;  //SCSI IDスピナー
   147:   static SpinnerNumberModel hcdIdModel;  //SCSI IDスピナーのスピナーモデル
   148:   static JLabel hcdVolumeLabel;  //音量を表示するラベル
   149:   static JSlider hcdVolumeSlider;  //音量スライダー
   150:   public static JMenu hcdMenu;  //メニュー。hcdAvailableのとき有効
   151: 
   152:   //構造体
   153:   static MemoryLayout TRACK_DATA;
   154:   static MemoryLayout CDROM_TOC;
   155:   static MemoryLayout CDROM_READ_TOC_EX;
   156:   static MemoryLayout RAW_READ_INFO;
   157:   static MemoryLayout STORAGE_DEVICE_DESCRIPTOR;
   158:   static MemoryLayout STORAGE_PROPERTY_QUERY;
   159: 
   160:   //リンカ
   161:   static Linker linker;
   162:   static MethodHandle downcallHandle (MemorySegment address, FunctionDescriptor function) {
   163:     return linker.downcallHandle (address, function);
   164:   }
   165: 
   166:   //アリーナ
   167:   static Arena arena;
   168: 
   169:   //関数
   170:   static MethodHandle CloseHandle;
   171:   static MethodHandle CreateFileA;
   172:   static MethodHandle DeviceIoControl;
   173:   static MethodHandle GetDiskFreeSpaceA;
   174:   static MethodHandle GetDriveTypeA;
   175:   static MethodHandle GetLastError;
   176:   static MethodHandle GetLogicalDrives;
   177:   static MethodHandle QueryDosDeviceA;
   178:   static MethodHandle ReadFile;
   179:   static MethodHandle SetFilePointerEx;
   180: 
   181:   //CD-ROM
   182:   static int hcdDriveLetter;  //ドライブレター。'E'など
   183:   static String hcdRootPath;  //ルートパス。"E:\\"など
   184:   static String hcdDevicePath;  //デバイスパス。"\\\\.\\E:"など
   185:   public static String hcdDeviceName;  //デバイス名。"E:"など
   186:   static byte[] hcdVendorProduct;  //ベンダーID[4],プロダクトID[16],プロダクトリビジョン[4]
   187: 
   188:   //動作中フラグ
   189:   static volatile boolean hcdRunning;  //動作中
   190:   static volatile boolean hcdPlaying;  //再生中
   191:   static volatile boolean hcdPausing;  //中断中
   192:   static volatile int hcdAudioStatus;  //0x11=再生中,0x12=中断中,0x13=正常終了,0x14=エラー終了,0x15=情報なし
   193: 
   194:   //メモリセグメント
   195:   static MemorySegment hcdReadTocEx;
   196:   static MemorySegment hcdToc;
   197:   static MemorySegment hcdBytesReturned;
   198:   static MemorySegment hcdBufferSegment;
   199:   static MemorySegment hcdReadInfo;
   200: 
   201:   //ハンドル
   202:   static MemorySegment hcdHandle;
   203: 
   204:   //ソースデータライン
   205:   static SourceDataLine hcdSourceDataLine;
   206: 
   207:   //再生キュー
   208:   static byte[][] hcdPlayQueueArray;
   209:   static volatile int hcdPlayQueueWrite;
   210:   static volatile int hcdPlayQueueRead;
   211: 
   212:   //読み出しスレッド
   213:   static Thread hcdReadThread;
   214:   static volatile int hcdStartSector;
   215:   static volatile int hcdCurrentSector;
   216:   static volatile int hcdEndSector;
   217: 
   218:   //トラック
   219:   static int[] hcdTOCAddressArray;  //TrackDataのAddressの配列。null=未確認
   220:   static int hcdTOCFirstTrack;
   221:   static int hcdTOCLastTrack;
   222: 
   223:   //CDXA
   224:   static int hcdDataOffset;  //物理セクタの先頭からデータの先頭までのオフセット。-1=未確認
   225: 
   226:   //再生スレッド
   227:   static Thread hcdPlayThread;
   228: 
   229:   //コマンド
   230:   static volatile int hcdRequested;  //要求カウンタ
   231:   static volatile int hcdCompleted;  //完了カウンタ
   232:   static volatile int hcdRetrieved;  //回収カウンタ
   233:   static volatile SPC.SCUnit hcdUnit;  //要求したユニット
   234:   static volatile SPC.SPCChip hcdChip;  //要求したインタフェイス
   235:   static volatile byte[] hcdResultBuffer;  //結果のデータ
   236:   static volatile int hcdResultLength;  //結果の長さ
   237:   static volatile int hcdResultSense0;  //結果のセンスデータ[0]
   238:   static volatile int hcdResultSense2;  //結果のセンスデータ[2]
   239:   static volatile int hcdResultStatus;  //結果のステータス
   240:   static volatile int hcdResultMessage;  //結果のメッセージ
   241:   static volatile int hcdBytesPerSector;  //Readで読み出す1セクタのバイト数
   242: 
   243: 
   244: 
   245:   //メソッド
   246: 
   247:   //hcdInit ()
   248:   //  初期化
   249:   public static void hcdInit () {
   250: 
   251:     //パラメータ
   252:     hcdAvailable = HCD_ENABLED && XEiJ.prgIsWindows;
   253:     hcdDebugInfo = Settings.sgsGetOnOff ("hcddebug");
   254:     hcdConnectNext = Settings.sgsGetOnOff ("hcdconnect");
   255:     hcdConnected = hcdConnectNext;
   256:     hcdSCSIIdNext = Settings.sgsGetInt ("hcdscsiid", HCD_DEFAULT_SCSI_ID);
   257:     if (hcdSCSIIdNext < 0 || 15 < hcdSCSIIdNext) {
   258:       hcdSCSIIdNext = HCD_DEFAULT_SCSI_ID;
   259:     }
   260:     hcdSCSIId = hcdSCSIIdNext;
   261:     hcdVolumeInt = Settings.sgsGetInt ("hcdvolume", HCD_DEFAULT_VOLUME);
   262:     if (hcdVolumeInt < 0 || 100 < hcdVolumeInt) {
   263:       hcdVolumeInt = HCD_DEFAULT_VOLUME;
   264:     }
   265:     if (hcdDebugInfo) {
   266:       System.out.printf ("volume=%d\n", hcdVolumeInt);
   267:     }
   268:     hcdVolumeFloat = (float) hcdVolumeInt / 100F;
   269: 
   270:     //メニュー
   271:     ActionListener listener = new ActionListener () {
   272:       @Override public void actionPerformed (ActionEvent ae) {
   273:         Object source = ae.getSource ();
   274:         String command = ae.getActionCommand ();
   275:         switch (command) {
   276:         case "Connect on next execution":
   277:           hcdConnectNext = ((JCheckBoxMenuItem) source).isSelected ();
   278:           break;
   279:         case "Debug info":
   280:           hcdDebugInfo = ((JCheckBoxMenuItem) source).isSelected ();
   281:           break;
   282:         default:
   283:           System.out.println ("unknown action command " + command);
   284:         }
   285:       }
   286:     };
   287:     hcdMenu = Multilingual.mlnText (
   288:       ComponentFactory.createMenu (
   289:         "Host CD-ROM",
   290:         Multilingual.mlnText (
   291:           ComponentFactory.createCheckBoxMenuItem (hcdConnectNext, "Connect on next execution", listener),
   292:           "ja", "次回の実行時に接続する"),
   293:         ComponentFactory.createHorizontalBox (
   294:           Box.createHorizontalStrut (20),
   295:           ComponentFactory.createLabel ("SCSI ID "),
   296:           hcdIdSpinner = ComponentFactory.createNumberSpinner (
   297:             hcdIdModel = new SpinnerNumberModel (hcdSCSIIdNext, 0, 15, 1),
   298:             2,
   299:             new ChangeListener () {
   300:               @Override public void stateChanged (ChangeEvent ce) {
   301:                 hcdSCSIIdNext = hcdIdModel.getNumber ().intValue ();
   302:               }
   303:             }
   304:             ),
   305:           Box.createHorizontalGlue ()
   306:           ),
   307:         ComponentFactory.createHorizontalBox (
   308:           Box.createHorizontalGlue (),
   309:           Multilingual.mlnText (ComponentFactory.createLabel ("Volume "), "ja", "音量 "),
   310:           hcdVolumeLabel = ComponentFactory.createLabel (String.valueOf (hcdVolumeInt)),
   311:           Box.createHorizontalGlue ()
   312:           ),
   313:         ComponentFactory.createHorizontalBox (
   314:           hcdVolumeSlider = ComponentFactory.setPreferredSize (
   315:             ComponentFactory.createHorizontalSlider (
   316:               0, 100, hcdVolumeInt, 10, 1,
   317:               new ChangeListener () {
   318:                 @Override public void stateChanged (ChangeEvent ce) {
   319:                   hcdVolumeInt = ((JSlider) ce.getSource ()).getValue ();
   320:                   if (hcdDebugInfo) {
   321:                     System.out.printf ("volume=%d\n", hcdVolumeInt);
   322:                   }
   323:                   hcdVolumeFloat = (float) hcdVolumeInt / 100F;
   324:                   hcdVolumeLabel.setText (String.valueOf (hcdVolumeInt));
   325:                 }
   326:               }
   327:               ),
   328:             LnF.lnfFontSize * 18, LnF.lnfFontSize * 2 + 28)
   329:           ),
   330:         ComponentFactory.createHorizontalSeparator (),
   331:         Multilingual.mlnText (
   332:           ComponentFactory.createCheckBoxMenuItem (hcdDebugInfo, "Debug info", listener),
   333:           "ja", "デバッグ情報")
   334:         ),
   335:       "ja", "ホスト CD-ROM");
   336:     hcdMenu.setEnabled (false);
   337: 
   338:     if (!hcdAvailable) {  //有効になっていないかWindowsでない
   339:       hcdConnected = false;
   340:       return;
   341:     }
   342: 
   343:     //TRACK_DATA構造体
   344:     //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddcdrm/ns-ntddcdrm-_track_data
   345:     TRACK_DATA = MemoryLayout.structLayout (
   346:       ValueLayout.JAVA_BYTE.withName ("Reserved"),  //UCHAR Reserved
   347:       ValueLayout.JAVA_BYTE.withName ("Adr_Control"),  //UCHAR Control:4; UCHAR Adr:4
   348:       ValueLayout.JAVA_BYTE.withName ("TrackNumber"),  //UCHAR TrackNumber
   349:       ValueLayout.JAVA_BYTE.withName ("Reserved1"),  //UCHAR Reserved1
   350:       MemoryLayout.sequenceLayout (4, ValueLayout.JAVA_BYTE).withName ("Address"));  //UCHAR Address[4]
   351: 
   352:     //CDROM_TOC構造体
   353:     //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddcdrm/ns-ntddcdrm-_cdrom_toc
   354:     CDROM_TOC = MemoryLayout.structLayout (
   355:       MemoryLayout.sequenceLayout (2, ValueLayout.JAVA_BYTE).withName ("Length"),  //UCHAR Length[2]
   356:       ValueLayout.JAVA_BYTE.withName ("FirstTrack"),  //UCHAR FirstTrack
   357:       ValueLayout.JAVA_BYTE.withName ("LastTrack"),  //UCHAR LastTrack
   358:       MemoryLayout.sequenceLayout (MAXIMUM_NUMBER_TRACKS, TRACK_DATA).withName ("TrackData"));  //TrackData[MAXIMUM_NUMBER_TRACKS]
   359: 
   360:     //CDROM_READ_TOC_EX構造体
   361:     CDROM_READ_TOC_EX = MemoryLayout.structLayout (
   362:       ValueLayout.JAVA_BYTE.withName ("Msf_Reserved1_Format"),  //UCHAR Format:4; UCHAR Reserved1:3; UCHAR Msf:1
   363:       ValueLayout.JAVA_BYTE.withName ("SessionTrack"),  //UCHAR SessionTrack
   364:       ValueLayout.JAVA_BYTE.withName ("Reserved2"),  //UCHAR Reserved2
   365:       ValueLayout.JAVA_BYTE.withName ("Reserved3"));  //UCHAR Reserved3
   366: 
   367:     //RAW_READ_INFO構造体
   368:     //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddcdrm/ns-ntddcdrm-__raw_read_info
   369:     RAW_READ_INFO = MemoryLayout.structLayout (
   370:       ValueLayout.JAVA_LONG.withName ("DiskOffset"),  //LARGE_INTEGER DiskOffset
   371:       ValueLayout.JAVA_INT.withName ("SectorCount"),  //ULONG SectorCount
   372:       ValueLayout.JAVA_INT.withName ("TrackMode"));  //TRACK_MODE_TYPE TrackMode
   373: 
   374:     //STORAGE_DEVICE_DESCRIPTOR構造体
   375:     //  https://learn.microsoft.com/ja-jp/windows/win32/api/winioctl/ns-winioctl-storage_device_descriptor
   376:     //  https://learn.microsoft.com/ja-jp/windows/win32/api/winioctl/ne-winioctl-storage_bus_type
   377:     STORAGE_DEVICE_DESCRIPTOR = MemoryLayout.structLayout (
   378:       ValueLayout.JAVA_INT.withName ("Version"),  //0 DWORD Version
   379:       ValueLayout.JAVA_INT.withName ("Size"),  //4 DWORD Size
   380:       ValueLayout.JAVA_BYTE.withName ("DeviceType"),  //8 BYTE DeviceType
   381:       ValueLayout.JAVA_BYTE.withName ("DeviceTypeModifier"),  //9 BYTE DeviceTypeModifier
   382:       ValueLayout.JAVA_BYTE.withName ("RemovableMedia"),  //10 BOOLEAN RemovableMedia
   383:       ValueLayout.JAVA_BYTE.withName ("CommandQueueing"),  //11 BOOLEAN CommandQueueing
   384:       ValueLayout.JAVA_INT.withName ("VendorIdOffset"),  //12 DWORD VendorIdOffset
   385:       ValueLayout.JAVA_INT.withName ("ProductIdOffset"),  //16 DWORD ProductIdOffset
   386:       ValueLayout.JAVA_INT.withName ("ProductRevisionOffset"),  //20 DWORD ProductRevisionOffset
   387:       ValueLayout.JAVA_INT.withName ("SerialNumberOffset"),  //24 DWORD SerialNumberOffset
   388:       ValueLayout.JAVA_INT.withName ("BusType"),  //28 STORAGE_BUS_TYPE BusType
   389:       ValueLayout.JAVA_INT.withName ("RawPropertiesLength"),  //32 DWORD RawPropertiesLength
   390:       MemoryLayout.sequenceLayout (1, ValueLayout.JAVA_BYTE).withName ("RawDeviceProperties"),  //36 BYTE RawDeviceProperties[1]
   391:       MemoryLayout.paddingLayout (3)  //37
   392:       //40
   393:       );
   394: 
   395:     //STORAGE_PROPERTY_QUERY構造体
   396:     //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddstor/ni-ntddstor-ioctl_storage_query_property
   397:     //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddstor/ns-ntddstor-_storage_property_query
   398:     //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddstor/ne-ntddstor-storage_property_id
   399:     //  https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddstor/ne-ntddstor-_storage_query_type
   400:     STORAGE_PROPERTY_QUERY = MemoryLayout.structLayout (
   401:       ValueLayout.JAVA_INT.withName ("PropertyId"),  //0 STORAGE_PROPERTY_ID PropertyId
   402:       ValueLayout.JAVA_INT.withName ("QueryType"),  //4 STORAGE_QUERY_TYPE QueryType
   403:       MemoryLayout.sequenceLayout (1, ValueLayout.JAVA_BYTE).withName ("AdditionalParameters"),  //8 UCHAR AdditionalParameters[1]
   404:       MemoryLayout.paddingLayout (3)  //9
   405:       //12
   406:       );
   407: 
   408:     //リンカ
   409:     linker = Linker.nativeLinker ();
   410: 
   411:     //アリーナ
   412:     arena = Arena.ofAuto ();
   413: 
   414:     //ライブラリ
   415:     SymbolLookup kernel32 = SymbolLookup.libraryLookup ("kernel32", arena);
   416: 
   417:     //関数
   418:     try {
   419: 
   420:       //CloseHandle関数
   421:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle
   422:       CloseHandle = downcallHandle (
   423:         kernel32.findOrThrow ("CloseHandle"),
   424:         FunctionDescriptor.of (
   425:           ValueLayout.JAVA_INT,  //BOOL
   426:           ValueLayout.ADDRESS));  //HANDLE hObject
   427: 
   428:       //CreateFileA関数
   429:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilea
   430:       CreateFileA = downcallHandle (
   431:         kernel32.findOrThrow ("CreateFileA"),
   432:         FunctionDescriptor.of (
   433:           ValueLayout.ADDRESS,  //HANDLE
   434:           ValueLayout.ADDRESS,  //LPCSTR lpFileName
   435:           ValueLayout.JAVA_INT,  //DWORD dwDesiredAccess
   436:           ValueLayout.JAVA_INT,  //DWORD dwShareMode
   437:           ValueLayout.ADDRESS,  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
   438:           ValueLayout.JAVA_INT,  //DWORD dwCreationDisposition
   439:           ValueLayout.JAVA_INT,  //DWORD dwFlagsAndAttributes
   440:           ValueLayout.ADDRESS));  //HANDLE hTemplateFile
   441: 
   442:       //DeviceIoControl関数
   443:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol
   444:       DeviceIoControl = downcallHandle (
   445:         kernel32.findOrThrow ("DeviceIoControl"),
   446:         FunctionDescriptor.of (
   447:           ValueLayout.JAVA_INT,  //BOOL
   448:           ValueLayout.ADDRESS,  //HANDLE hDevice
   449:           ValueLayout.JAVA_INT,  //DWORD dwIoControlCode
   450:           ValueLayout.ADDRESS,  //LPVOID lpInBuffer
   451:           ValueLayout.JAVA_INT,  //DWORD nInBufferSize
   452:           ValueLayout.ADDRESS,  //LPVOID lpOutBuffer
   453:           ValueLayout.JAVA_INT,  //DWORD nOutBufferSize
   454:           ValueLayout.ADDRESS,  //LPDWORD lpBytesReturned
   455:           ValueLayout.ADDRESS));  //LPOVERLAPPED lpOverlapped
   456: 
   457:       //GetDiskFreeSpaceA関数
   458:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-getdiskfreespacea
   459:       GetDiskFreeSpaceA = downcallHandle (
   460:         kernel32.findOrThrow ("GetDiskFreeSpaceA"),
   461:         FunctionDescriptor.of (
   462:           ValueLayout.JAVA_INT,  //BOOL
   463:           ValueLayout.ADDRESS,  //LPCSTR lpRootPathName
   464:           ValueLayout.ADDRESS,  //LPDWORD lpSectorsPerCluster
   465:           ValueLayout.ADDRESS,  //LPDWORD lpBytesPerSector
   466:           ValueLayout.ADDRESS,  //LPDWORD lpNumberOfFreeClusters
   467:           ValueLayout.ADDRESS));  //LPDWORD lpTotalNumberOfClusters
   468: 
   469:       //GetDriveTypeA関数
   470:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-getdrivetypea
   471:       GetDriveTypeA = downcallHandle (
   472:         kernel32.findOrThrow ("GetDriveTypeA"),
   473:         FunctionDescriptor.of (
   474:           ValueLayout.JAVA_INT,  //UINT
   475:           ValueLayout.ADDRESS));  //LPCWSTR lpRootPathName
   476: 
   477:       //GetLastError関数
   478:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
   479:       GetLastError = downcallHandle (
   480:         kernel32.findOrThrow ("GetLastError"),
   481:         FunctionDescriptor.of (
   482:           ValueLayout.JAVA_INT));  //DWORD
   483: 
   484:       //GetLogicalDrives関数
   485:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives
   486:       GetLogicalDrives = downcallHandle (
   487:         kernel32.findOrThrow ("GetLogicalDrives"),
   488:         FunctionDescriptor.of (
   489:           ValueLayout.JAVA_INT));  //DWORD
   490: 
   491:       //QueryDosDeviceA
   492:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/winbase/nf-winbase-querydosdevicea
   493:       QueryDosDeviceA = downcallHandle (
   494:         kernel32.findOrThrow ("QueryDosDeviceA"),
   495:         FunctionDescriptor.of (
   496:           ValueLayout.JAVA_INT,  //DWORD
   497:           ValueLayout.ADDRESS,  //LPCSTR lpDeviceName
   498:           ValueLayout.ADDRESS,  //LPSTR lpTargetPath
   499:           ValueLayout.JAVA_INT));  //DWORD ucchMax
   500: 
   501:       //ReadFile関数
   502:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-readfile
   503:       ReadFile = downcallHandle (
   504:         kernel32.findOrThrow ("ReadFile"),
   505:         FunctionDescriptor.of (
   506:           ValueLayout.JAVA_INT,  //BOOL
   507:           ValueLayout.ADDRESS,  //HANDLE hFile
   508:           ValueLayout.ADDRESS,  //LPVOID lpBuffer
   509:           ValueLayout.JAVA_INT,  //DWORD nNumberOfBytesToRead
   510:           ValueLayout.ADDRESS,  //LPDWORD lpNumberOfBytesRead
   511:           ValueLayout.ADDRESS));  //LPOVERLAPPED lpOverlapped
   512: 
   513:       //SetFilePointerEx関数
   514:       //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-setfilepointerex
   515:       SetFilePointerEx = downcallHandle (
   516:         kernel32.findOrThrow ("SetFilePointerEx"),
   517:         FunctionDescriptor.of (
   518:           ValueLayout.JAVA_INT,  //BOOL
   519:           ValueLayout.ADDRESS,  //HANDLE hFile
   520:           ValueLayout.JAVA_LONG,  //LARGE_INTEGER liDistanceToMove
   521:           ValueLayout.ADDRESS,  //PLARGE_INTEGER lpNewFilePointer
   522:           ValueLayout.JAVA_INT));  //DWORD dwMoveMethod
   523: 
   524:     } catch (NoSuchElementException nsee) {  //操作できない
   525:       if (hcdDebugInfo) {
   526:         nsee.printStackTrace ();
   527:       }
   528:       hcdAvailable = false;
   529:       hcdConnected = false;
   530:       return;
   531:     }
   532: 
   533:     //CD-ROMドライブを探す
   534:     hcdDriveLetter = 0;
   535:     hcdRootPath = null;
   536:     hcdDevicePath = null;
   537:     hcdDeviceName = null;
   538:     try {
   539:       int error;
   540:       int logicalDrives = 0;
   541:       if ((logicalDrives = (int) GetLogicalDrives.invoke ()) == 0 &&
   542:           (error = (int) GetLastError.invoke ()) != -1) {
   543:         if (hcdDebugInfo) {
   544:           System.out.printf ("GetLogicalDrives returned error %d\n",
   545:                              error);
   546:         }
   547:         hcdAvailable = false;
   548:         hcdConnected = false;
   549:         return;
   550:       }
   551:       for (int driveLetter = 'A'; driveLetter <= 'Z'; driveLetter++) {
   552:         if ((logicalDrives & (1 << (driveLetter - 'A'))) != 0) {
   553:           String rootPath = String.format ("%c:\\", driveLetter);
   554:           if ((int) GetDriveTypeA.invoke (
   555:             arena.allocateFrom (rootPath)) == DRIVE_CDROM) {  //LPCWSTR lpRootPathName
   556:             hcdDriveLetter = driveLetter;
   557:             hcdRootPath = rootPath;
   558:             break;
   559:           }
   560:         }
   561:       }  //for
   562:       if (hcdDriveLetter == 0) {  //CD-ROMドライブが見つからない
   563:         if (hcdDebugInfo) {
   564:           System.out.println ("CD-ROM drive not found");
   565:         }
   566:         hcdAvailable = false;
   567:         hcdConnected = false;
   568:         return;
   569:       }
   570:       hcdDevicePath = String.format ("\\\\.\\%c:", hcdDriveLetter);
   571:       hcdDeviceName = String.format ("%c:", hcdDriveLetter);
   572:       if (hcdDebugInfo) {
   573:         System.out.printf ("CD-ROM drive is %s\n",
   574:                            hcdDeviceName);
   575:       }
   576: 
   577:       //ベンダー、プロダクト、リビジョンの取得を試みる
   578:       //  メディアが入っていない場合があることに注意する
   579:       MemorySegment handle;
   580:       if ((handle = (MemorySegment) CreateFileA.invoke (
   581:         arena.allocateFrom (hcdDevicePath),  //LPCSTR lpFileName
   582:         0,  //DWORD dwDesiredAccess
   583:         FILE_SHARE_READ | FILE_SHARE_WRITE,  //DWORD dwShareMode
   584:         MemorySegment.NULL,  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
   585:         OPEN_EXISTING,  //DWORD dwCreationDisposition
   586:         0,   //DWORD dwFlagsAndAttributes
   587:         MemorySegment.NULL)).address () == INVALID_HANDLE_VALUE &&  //hcdHandle hTemplateFile
   588:           (error = (int) GetLastError.invoke ()) != -1) {
   589:         if (hcdDebugInfo) {
   590:           System.out.printf ("CreateFileA returned error %d\n",
   591:                              error);
   592:         }
   593:       } else {
   594:         MemorySegment query = arena.allocate (STORAGE_PROPERTY_QUERY);
   595:         MemorySegment descriptor = arena.allocate (1024);  //STORAGE_DEVICE_DESCRIPTOR
   596:         MemorySegment bytesReturned = arena.allocate (ValueLayout.JAVA_INT);
   597:         query.set (ValueLayout.JAVA_INT,
   598:                    STORAGE_PROPERTY_QUERY.byteOffset (MemoryLayout.PathElement.groupElement ("PropertyId")),
   599:                    StorageDeviceProperty);
   600:         query.set (ValueLayout.JAVA_INT,
   601:                    STORAGE_PROPERTY_QUERY.byteOffset (MemoryLayout.PathElement.groupElement ("QueryType")),
   602:                    PropertyStandardQuery);
   603:         if ((int) DeviceIoControl.invoke (
   604:           handle,  //hcdHandle hDevice
   605:           IOCTL_STORAGE_QUERY_PROPERTY,  //DWORD dwIoControlCode
   606:           query,  //LPVOID lpInBuffer
   607:           (int) query.byteSize (),  //DWORD nInBufferSize
   608:           descriptor,  //LPVOID lpOutBuffer
   609:           (int) descriptor.byteSize (),  //DWORD nOutBufferSize
   610:           bytesReturned,  //LPDWORD lpBytesReturned
   611:           MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
   612:             (error = (int) GetLastError.invoke ()) != -1) {
   613:           if (hcdDebugInfo) {
   614:             System.out.printf ("DeviceIoControl IOCTL_STORAGE_QUERY_PROPERTY returned error %d\n",
   615:                                error);
   616:           }
   617:         } else {
   618:           hcdVendorProduct = new byte[8 + 16 + 4];
   619:           Arrays.fill (hcdVendorProduct, (byte) ' ');
   620:           for (int k = 0; k < 3; k++) {
   621:             int o = descriptor.get (ValueLayout.JAVA_INT,
   622:                                     STORAGE_DEVICE_DESCRIPTOR.byteOffset (MemoryLayout.PathElement.groupElement (
   623:                                       k == 0 ? "VendorIdOffset" :
   624:                                       k == 1 ? "ProductIdOffset" :
   625:                                       "ProductRevisionOffset")));  //入力開始位置
   626:             if (o != 0) {
   627:               int p = k == 0 ? 0 : k == 1 ? 8 : 8 + 16;  //出力開始位置
   628:               int l = k == 0 ? 8 : k == 1 ? 16 : 4;  //長さ
   629:               for (int i = 0; i < l; i++) {
   630:                 int c = 0xff & descriptor.get (ValueLayout.JAVA_BYTE, o++);
   631:                 if (c == 0) {
   632:                   break;
   633:                 }
   634:                 hcdVendorProduct[p++] = (byte) (0x20 <= c && c <= 0x7e ? c : '?');
   635:               }
   636:             }
   637:           }
   638:           if (hcdDebugInfo) {
   639:             System.out.print ("VendorProduct is ");
   640:             for (int i = 0; i < 8 + 16 + 4; i++) {
   641:               System.out.printf ("%c", 0xff & hcdVendorProduct[i]);
   642:             }
   643:             System.out.println ();
   644:             int BusType = descriptor.get (ValueLayout.JAVA_INT,
   645:                                           STORAGE_DEVICE_DESCRIPTOR.byteOffset (MemoryLayout.PathElement.groupElement ("BusType")));
   646:             System.out.printf ("BusType is %s\n",
   647:                                BusType == BusTypeUnknown ? "BusTypeUnknown" :
   648:                                BusType == BusTypeScsi ? "BusTypeScsi" :
   649:                                BusType == BusTypeAtapi ? "BusTypeAtapi" :
   650:                                BusType == BusTypeAta ? "BusTypeAta" :
   651:                                BusType == BusType1394 ? "BusType1394" :
   652:                                BusType == BusTypeSsa ? "BusTypeSsa" :
   653:                                BusType == BusTypeFibre ? "BusTypeFibre" :
   654:                                BusType == BusTypeUsb ? "BusTypeUsb" :
   655:                                BusType == BusTypeRAID ? "BusTypeRAID" :
   656:                                BusType == BusTypeiScsi ? "BusTypeiScsi" :
   657:                                BusType == BusTypeSas ? "BusTypeSas" :
   658:                                BusType == BusTypeSata ? "BusTypeSata" :
   659:                                BusType == BusTypeSd ? "BusTypeSd" :
   660:                                BusType == BusTypeMmc ? "BusTypeMmc" :
   661:                                BusType == BusTypeVirtual ? "BusTypeVirtual" :
   662:                                BusType == BusTypeFileBackedVirtual ? "BusTypeFileBackedVirtual" :
   663:                                BusType == BusTypeSpaces ? "BusTypeSpaces" :
   664:                                BusType == BusTypeNvme ? "BusTypeNvme" :
   665:                                BusType == BusTypeSCM ? "BusTypeSCM" :
   666:                                BusType == BusTypeUfs ? "BusTypeUfs" :
   667:                                BusType == BusTypeNvmeof ? "BusTypeNvmeof" :
   668:                                BusType == BusTypeMax ? "BusTypeMax" :
   669:                                BusType == BusTypeMaxReserved ? "BusTypeMaxReserved" :
   670:                                String.valueOf (BusType));
   671:           }
   672:         }
   673:         CloseHandle.invoke (handle);
   674:       }
   675: 
   676:     } catch (Throwable e) {  //操作できない
   677:       if (hcdDebugInfo) {
   678:         e.printStackTrace ();
   679:       }
   680:       hcdAvailable = false;
   681:       hcdConnected = false;
   682:       return;
   683:     }
   684: 
   685:     hcdMenu.setEnabled (true);
   686: 
   687:     //動作中フラグ
   688:     hcdRunning = true;
   689: 
   690:     //メモリセグメント
   691:     hcdReadTocEx = arena.allocate (CDROM_READ_TOC_EX);
   692:     hcdToc = arena.allocate (CDROM_TOC);
   693:     hcdBytesReturned = arena.allocate (ValueLayout.JAVA_INT);
   694:     hcdBufferSegment = arena.allocate (HCD_PLAY_BYTES);
   695:     hcdReadInfo = arena.allocate (RAW_READ_INFO);
   696: 
   697:     //ハンドル
   698:     hcdHandle = null;
   699: 
   700:     //ソースデータライン
   701:     hcdSourceDataLine = null;
   702:     try {
   703:       AudioFormat audioFormat = new AudioFormat (44100F,  //sampleRate
   704:                                                  16,  //sampleSizeInBits
   705:                                                  2,  //channels
   706:                                                  true,  //signed
   707:                                                  false);  //bigEndian
   708:       hcdSourceDataLine = AudioSystem.getSourceDataLine (audioFormat);
   709:       hcdSourceDataLine.open (audioFormat, HCD_PLAY_BYTES * 2);
   710:       hcdSourceDataLine.start ();
   711:     } catch (LineUnavailableException lue) {
   712:       if (hcdDebugInfo) {
   713:         lue.printStackTrace ();
   714:       }
   715:     }
   716: 
   717:     //再生キュー
   718:     hcdPlayQueueArray = new byte[HCD_PLAY_QUEUE_SIZE][];
   719:     for (int i = 0; i < HCD_PLAY_QUEUE_SIZE; i++) {
   720:       hcdPlayQueueArray[i] = new byte[HCD_PLAY_BYTES];
   721:     }
   722:     hcdPlayQueueWrite = 0;
   723:     hcdPlayQueueRead = 0;
   724: 
   725:     //読み出しスレッド
   726:     //  動作中のとき繰り返す
   727:     //    コマンドがあるとき
   728:     //      コマンドを実行する
   729:     //    開いていないか再生中でないか中断中か最後まで読み出したかキューが満杯のとき
   730:     //      200ms待つ
   731:     //    さもなくば
   732:     //      400msぶんのデータを読み出してキューに追加する
   733:     hcdReadThread = new Thread (() -> {
   734:       while (hcdRunning) {  //動作中のとき繰り返す
   735:         if (hcdCompleted != hcdRequested) {  //完了カウンタ!=予約カウンタのとき
   736:           hcdCommandComplete_2nd ();  //コマンドを実行する
   737:           hcdCompleted = hcdRequested;
   738:         } else if (hcdHandle == null ||  //開いていないか
   739:                    !hcdPlaying ||  //再生中でないか
   740:                    hcdPausing ||  //中断中か
   741:                    hcdEndSector <= hcdCurrentSector ||  //最後まで読み出したか
   742:                    (hcdPlayQueueWrite - hcdPlayQueueRead) == HCD_PLAY_QUEUE_SIZE) {  //キューが満杯のとき
   743:           try {
   744:             Thread.sleep ((long) (HCD_PLAY_MILLIS / 2));  //200ms待つ
   745:           } catch (InterruptedException ie) {
   746:           }
   747:         } else {  //さもなくば
   748:           //400msぶんのデータを読み出してキューに追加する
   749:           byte[] buffer = hcdPlayQueueArray[hcdPlayQueueWrite & (HCD_PLAY_QUEUE_SIZE - 1)];
   750:           int sectors = Math.min (HCD_PLAY_SECTORS, hcdEndSector - hcdCurrentSector);  //今回読み込むセクタ数
   751:           hcdReadInfo.set (ValueLayout.JAVA_LONG,
   752:                            RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("DiskOffset")),
   753:                            2048L * (long) hcdCurrentSector);
   754:           hcdReadInfo.set (ValueLayout.JAVA_INT,
   755:                            RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("SectorCount")),
   756:                            sectors);
   757:           hcdReadInfo.set (ValueLayout.JAVA_INT,
   758:                            RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("TrackMode")),
   759:                            CDDA);
   760:           try {
   761:             int error;
   762:             if ((int) DeviceIoControl.invoke (
   763:               hcdHandle,  //hcdHandle hDevice
   764:               IOCTL_CDROM_RAW_READ,  //DWORD dwIoControlCode
   765:               hcdReadInfo,  //LPVOID lpInBuffer
   766:               (int) hcdReadInfo.byteSize (),  //DWORD nInBufferSize
   767:               hcdBufferSegment,  //LPVOID lpOutBuffer
   768:               2352 * sectors,  //DWORD nOutBufferSize
   769:               hcdBytesReturned,  //LPDWORD lpBytesReturned
   770:               MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
   771:                 (error = (int) GetLastError.invoke ()) != -1) {  //読めない
   772:               if (hcdDebugInfo) {
   773:                 System.out.printf ("DeviceIoControl IOCTL_CDROM_RAW_READ returned error %d\n",
   774:                                    error);
   775:               }
   776:               hcdPlaying = false;
   777:               hcdAudioStatus = 0x14;  //エラー終了
   778:               Arrays.fill (buffer, (byte) 0);
   779:             }
   780:           } catch (Throwable e) {  //操作できない
   781:             if (hcdDebugInfo) {
   782:               e.printStackTrace ();
   783:             }
   784:             hcdPlaying = false;
   785:             hcdAudioStatus = 0x14;  //エラー終了
   786:             Arrays.fill (buffer, (byte) 0);
   787:           }
   788:           //byte[] array = hcdBufferSegment.toArray (ValueLayout.JAVA_BYTE);  //毎回必要。メモリ消費が激しい
   789:           for (int i = 0; i < 588 * sectors; i++) {
   790:             //int l = array[4 * i + 1] << 8 | (0xff & array[4 * i + 0]);
   791:             //int r = array[4 * i + 3] << 8 | (0xff & array[4 * i + 2]);
   792:             int l = ((int) hcdBufferSegment.get (ValueLayout.JAVA_BYTE, 4 * i + 1) << 8 |
   793:                      (0xff & (int) hcdBufferSegment.get (ValueLayout.JAVA_BYTE, 4 * i + 0)));
   794:             int r = ((int) hcdBufferSegment.get (ValueLayout.JAVA_BYTE, 4 * i + 3) << 8 |
   795:                      (0xff & (int) hcdBufferSegment.get (ValueLayout.JAVA_BYTE, 4 * i + 2)));
   796:             l = Math.max (-32768, Math.min (32767, Math.round ((float) l * hcdVolumeFloat)));
   797:             r = Math.max (-32768, Math.min (32767, Math.round ((float) r * hcdVolumeFloat)));
   798:             buffer[4 * i + 0] = (byte) l;
   799:             buffer[4 * i + 1] = (byte) (l >> 8);
   800:             buffer[4 * i + 2] = (byte) r;
   801:             buffer[4 * i + 3] = (byte) (r >> 8);
   802:           }
   803:           if (sectors < HCD_PLAY_SECTORS) {
   804:             Arrays.fill (buffer,
   805:                          2352 * sectors,  //from
   806:                          HCD_PLAY_BYTES,  //to
   807:                          (byte) 0);
   808:           }
   809:           hcdCurrentSector += sectors;
   810:           hcdPlayQueueWrite++;
   811:         }
   812:       }  //while
   813:     });
   814:     hcdReadThread.start ();
   815: 
   816:     //再生スレッド
   817:     //  終了するまで繰り返す
   818:     //    再生中でないか中断中か4周目以前でキューが空のとき
   819:     //      200ms待つ
   820:     //    5周目以後でキューが空のとき
   821:     //      エラー終了
   822:     //    ソースデータラインが使用できないとき
   823:     //      400msぶんのデータをキューから取り出して400ms待つ
   824:     //    さもなくば
   825:     //      400msぶんのデータをキューから取り出して再生する。ブロックする
   826:     //      最後まで再生したとき
   827:     //        正常終了
   828:     hcdPlayThread = new Thread (() -> {
   829:       while (hcdRunning) {  //終了するまで繰り返す
   830:         if (!hcdPlaying ||  //再生中でないか
   831:             hcdPausing ||  //中断中か
   832:             (((hcdCurrentSector - hcdStartSector) < HCD_PLAY_SECTORS * HCD_PLAY_QUEUE_SIZE * 2) &&  //4周目以前で
   833:              hcdPlayQueueRead == hcdPlayQueueWrite)) {  //キューが空のとき
   834:           try {
   835:             Thread.sleep ((long) (HCD_PLAY_MILLIS / 2));  //200ms待つ
   836:           } catch (InterruptedException ie) {
   837:           }
   838:         } else if (hcdPlayQueueRead == hcdPlayQueueWrite) {  //5周目以後でキューが空のとき
   839:           hcdPlaying = false;
   840:           hcdAudioStatus = 0x14;  //エラー終了
   841:         } else if (hcdSourceDataLine == null) {  //ソースデータラインが使用できないとき
   842:           //400msぶんのデータをキューから取り出して400ms待つ
   843:           hcdPlayQueueRead++;
   844:           try {
   845:             Thread.sleep ((long) (HCD_PLAY_MILLIS));
   846:           } catch (InterruptedException ie) {
   847:           }
   848:         } else {  //さもなくば
   849:           //400msぶんのデータをキューから取り出して再生する。ブロックする
   850:           hcdSourceDataLine.write (hcdPlayQueueArray[hcdPlayQueueRead & (HCD_PLAY_QUEUE_SIZE - 1)], 0, HCD_PLAY_BYTES);
   851:           hcdPlayQueueRead++;
   852:           if (hcdEndSector <= hcdCurrentSector) {  //最後まで再生したとき
   853:             hcdPlaying = false;
   854:             hcdAudioStatus = 0x13;  //正常終了
   855:           }
   856:         }
   857:       }  //while
   858:     });
   859:     hcdPlayThread.start ();
   860: 
   861:   }  //hcdInit
   862: 
   863:   //hcdTini ()
   864:   //  後始末
   865:   public static void hcdTini () {
   866: 
   867:     //動作中フラグ
   868:     hcdRunning = false;
   869: 
   870:     //読み出しスレッド
   871:     if (hcdReadThread != null) {
   872:       hcdReadThread.interrupt ();
   873:       try {
   874:         hcdReadThread.join ((long) (HCD_PLAY_MILLIS * 2));
   875:       } catch (InterruptedException ie) {
   876:       }
   877:       hcdReadThread = null;
   878:     }
   879: 
   880:     //再生スレッド
   881:     if (hcdPlayThread != null) {
   882:       hcdPlayThread.interrupt ();
   883:       try {
   884:         hcdPlayThread.join ((long) (HCD_PLAY_MILLIS * 2));
   885:       } catch (InterruptedException ie) {
   886:       }
   887:       hcdPlayThread = null;
   888:     }
   889: 
   890:     //ソースデータライン
   891:     if (hcdSourceDataLine != null) {
   892:       hcdSourceDataLine.stop ();
   893:       hcdSourceDataLine.close ();
   894:       hcdSourceDataLine = null;
   895:     }
   896: 
   897:     //パラメータ
   898:     Settings.sgsPutOnOff ("hcddebug", hcdDebugInfo);
   899:     Settings.sgsPutOnOff ("hcdconnect", hcdConnectNext);
   900:     Settings.sgsPutInt ("hcdscsiid", hcdSCSIIdNext);
   901:     Settings.sgsPutInt ("hcdvolume", hcdVolumeInt);
   902: 
   903:   }  //hcdTini
   904: 
   905:   //hcdReset ()
   906:   //  リセット
   907:   public static void hcdReset () {
   908:     if (hcdConnected) {
   909:       hcdRequested = 0;
   910:       hcdCompleted = 0;
   911:       hcdRetrieved = 0;
   912:       hcdTOCAddressArray = null;
   913:       hcdDataOffset = -1;
   914:       hcdUnit = null;
   915:       hcdChip = null;
   916:       hcdResultBuffer = null;
   917:       hcdResultLength = 0;
   918:       hcdResultSense0 = 0;
   919:       hcdResultSense2 = 0;
   920:       hcdResultStatus = SPC.SPC_GOOD;
   921:       hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
   922:       hcdBytesPerSector = 2048;
   923:       hcdPlaying = false;
   924:       hcdPausing = false;
   925:       hcdAudioStatus = 0x15;  //情報なし
   926:     }
   927:   }  //hcdReset
   928: 
   929:   //success = hcdOpen ()
   930:   //  開く
   931:   static boolean hcdOpen () {
   932:     if (hcdHandle != null) {  //すでに開いている
   933:       return true;
   934:     }
   935:     if (hcdRootPath == null) {  //CD-ROMドライブがない
   936:       return false;
   937:     }
   938:     try {
   939:       int error;
   940:       //挿入されているか
   941:       MemorySegment sectorsPerCluster = arena.allocate (ValueLayout.JAVA_INT);
   942:       MemorySegment bytesPerSector = arena.allocate (ValueLayout.JAVA_INT);
   943:       MemorySegment numberOfFreeClusters = arena.allocate (ValueLayout.JAVA_INT);
   944:       MemorySegment totalNumberOfClusters = arena.allocate (ValueLayout.JAVA_INT);
   945:       if ((int) GetDiskFreeSpaceA.invoke (
   946:         arena.allocateFrom (hcdRootPath),  //LPCSTR lpRootPathName
   947:         sectorsPerCluster,  //LPDWORD lpSectorsPerCluster
   948:         bytesPerSector,  //LPDWORD lpBytesPerSector
   949:         numberOfFreeClusters,  //LPDWORD lpNumberOfFreeClusters
   950:         totalNumberOfClusters) == 0 &&  //LPDWORD lpTotalNumberOfClusters
   951:           (error = (int) GetLastError.invoke ()) != -1) {
   952:         if (hcdDebugInfo) {
   953:           System.out.printf ("GetDiskFreeSpaceA returned error %d\n",
   954:                              error);
   955:         }
   956:         return false;
   957:       }
   958:       if (hcdDebugInfo) {
   959:         System.out.printf ("sectorsPerCluster=%d\n",
   960:                            sectorsPerCluster.get (ValueLayout.JAVA_INT, 0));
   961:         System.out.printf ("bytesPerSector=%d\n",
   962:                            bytesPerSector.get (ValueLayout.JAVA_INT, 0));
   963:         System.out.printf ("numberOfFreeClusters=%d\n",
   964:                            numberOfFreeClusters.get (ValueLayout.JAVA_INT, 0));
   965:         System.out.printf ("totalNumberOfClusters=%d\n",
   966:                            totalNumberOfClusters.get (ValueLayout.JAVA_INT, 0));
   967:       }
   968:       //開く
   969:       if ((hcdHandle = (MemorySegment) CreateFileA.invoke (
   970:         arena.allocateFrom (hcdDevicePath),  //LPCSTR lpFileName
   971:         GENERIC_READ,  //DWORD dwDesiredAccess
   972:         FILE_SHARE_READ | FILE_SHARE_WRITE,  //DWORD dwShareMode
   973:         MemorySegment.NULL,  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
   974:         OPEN_EXISTING,  //DWORD dwCreationDisposition
   975:         0,   //DWORD dwFlagsAndAttributes
   976:         MemorySegment.NULL)).address () == INVALID_HANDLE_VALUE &&  //hcdHandle hTemplateFile
   977:           (error = (int) GetLastError.invoke ()) != -1) {  //開けない
   978:         if (hcdDebugInfo) {
   979:           System.out.printf ("CreateFileA returned error %d\n",
   980:                              error);
   981:         }
   982:         hcdHandle = null;
   983:         return false;
   984:       }
   985:     } catch (Throwable e) {  //操作できない
   986:       if (hcdDebugInfo) {
   987:         e.printStackTrace ();
   988:       }
   989:       hcdHandle = null;
   990:       return false;
   991:     }
   992:     return true;
   993:   }  //hcdOpen
   994: 
   995:   //hcdClose ()
   996:   //  閉じる
   997:   static void hcdClose () {
   998:     if (hcdHandle == null) {  //開いていない
   999:       return;
  1000:     }
  1001:     try {
  1002:       CloseHandle.invoke (hcdHandle);
  1003:     } catch (Throwable e) {  //操作できない
  1004:       if (hcdDebugInfo) {
  1005:         e.printStackTrace ();
  1006:       }
  1007:     }
  1008:     hcdHandle = null;
  1009:     hcdTOCAddressArray = null;
  1010:     hcdDataOffset = -1;
  1011:   }  //hcdClose
  1012: 
  1013:   //buffer = hcdReadTOC (msf)
  1014:   //  TOCを読む
  1015:   static byte[] hcdReadTOC (boolean msf) {
  1016:     if (!hcdOpen ()) {  //開けない
  1017:       return null;
  1018:     }
  1019:     try {
  1020:       int error;
  1021:       hcdReadTocEx.set (ValueLayout.JAVA_BYTE,
  1022:                         CDROM_READ_TOC_EX.byteOffset (MemoryLayout.PathElement.groupElement ("Msf_Reserved1_Format")),
  1023:                         (byte) ((msf ? 1 : 0) << 7 |  //Msf
  1024:                                 0 << 4 |  //Reserved1
  1025:                                 CDROM_READ_TOC_EX_FORMAT_TOC));  //Format
  1026:       hcdReadTocEx.set (ValueLayout.JAVA_BYTE,
  1027:                         CDROM_READ_TOC_EX.byteOffset (MemoryLayout.PathElement.groupElement ("SessionTrack")),
  1028:                         (byte) 1);
  1029:       hcdReadTocEx.set (ValueLayout.JAVA_BYTE,
  1030:                         CDROM_READ_TOC_EX.byteOffset (MemoryLayout.PathElement.groupElement ("Reserved2")),
  1031:                         (byte) 0);
  1032:       hcdReadTocEx.set (ValueLayout.JAVA_BYTE,
  1033:                         CDROM_READ_TOC_EX.byteOffset (MemoryLayout.PathElement.groupElement ("Reserved3")),
  1034:                         (byte) 0);
  1035:       if ((int) DeviceIoControl.invoke (
  1036:         hcdHandle,  //hcdHandle hDevice
  1037:         IOCTL_CDROM_READ_TOC_EX,  //DWORD dwIoControlCode
  1038:         hcdReadTocEx,  //LPVOID lpInBuffer
  1039:         (int) hcdReadTocEx.byteSize (),  //DWORD nInBufferSize
  1040:         hcdToc,  //LPVOID lpOutBuffer
  1041:         (int) hcdToc.byteSize (),  //DWORD nOutBufferSize
  1042:         hcdBytesReturned,  //LPDWORD lpBytesReturned
  1043:         MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
  1044:           (error = (int) GetLastError.invoke ()) != -1) {  //読めない
  1045:         if (hcdDebugInfo) {
  1046:           System.out.printf ("DeviceIoControl IOCTL_CDROM_READ_TOC_EX returned error %d\n",
  1047:                              error);
  1048:         }
  1049:         hcdClose ();
  1050:         return (error == 0 ||
  1051:                 error == ERROR_NOT_READY ||
  1052:                 error == ERROR_WRONG_DISK ? null :
  1053:                 new byte[0]);
  1054:       }
  1055:       int Length_0 = (0xff & (int) (hcdToc.get (ValueLayout.JAVA_BYTE,
  1056:                                                 CDROM_TOC.byteOffset (MemoryLayout.PathElement.groupElement ("Length"),
  1057:                                                                       MemoryLayout.PathElement.sequenceElement (0)))));
  1058:       int Length_1 = (0xff & (int) (hcdToc.get (ValueLayout.JAVA_BYTE,
  1059:                                                 CDROM_TOC.byteOffset (MemoryLayout.PathElement.groupElement ("Length"),
  1060:                                                                       MemoryLayout.PathElement.sequenceElement (1)))));
  1061:       int Length = 256 * Length_0 + Length_1;
  1062:       byte[] buffer = new byte[2 + Length];
  1063:       for (int i = 0; i < buffer.length; i++) {
  1064:         buffer[i] = hcdToc.get (ValueLayout.JAVA_BYTE, (long) i);
  1065:       }
  1066:       if (false) {
  1067:         for (int i = 0; i < buffer.length; i++) {
  1068:           System.out.printf ("%02x ", 0xff & buffer[i]);
  1069:         }
  1070:         System.out.println ();
  1071:       }
  1072:       return buffer;
  1073:     } catch (Throwable e) {  //操作できない
  1074:       if (hcdDebugInfo) {
  1075:         e.printStackTrace ();
  1076:       }
  1077:       return null;
  1078:     }
  1079:   }  //hcdReadTOC
  1080: 
  1081:   //hcdPlay (startSector, endSector)
  1082:   //  再生を開始する
  1083:   static void hcdPlay (int startSector, int endSector) {
  1084:     if (!hcdPlaying) {  //再生していない
  1085:       if (!hcdOpen ()) {  //開けない
  1086:         return;
  1087:       }
  1088:     }
  1089:     //すでに再生しているときは上書きする
  1090:     //中断中のデータがキューに残っていると上書き後に再生されてしまうので読み出し位置を進める
  1091:     hcdPlayQueueRead = hcdPlayQueueWrite;
  1092:     hcdStartSector = startSector;
  1093:     hcdCurrentSector = startSector;
  1094:     hcdEndSector = endSector;
  1095:     hcdPlaying = true;
  1096:     hcdPausing = false;
  1097:     hcdAudioStatus = 0x11;  //再生中
  1098:   }  //hcdPlay
  1099: 
  1100:   //hcdPause ()
  1101:   //  再生を中断する
  1102:   static void hcdPause () {
  1103:     if (!hcdPlaying) {  //再生していない
  1104:       return;
  1105:     }
  1106:     hcdPausing = true;
  1107:     hcdAudioStatus = 0x12;  //中断中
  1108:   }  //hcdPause
  1109: 
  1110:   //hcdResume ()
  1111:   //  再生を再開する
  1112:   static void hcdResume () {
  1113:     if (!hcdPlaying) {  //再生していない
  1114:       return;
  1115:     }
  1116:     hcdStartSector = hcdCurrentSector;  //再開した位置を開始時刻にする。1周目からやり直さないとアンダーランで停止してしまう
  1117:     hcdPausing = false;
  1118:     hcdAudioStatus = 0x11;  //再生中
  1119:   }  //hcdResume
  1120: 
  1121:   //hcdSetBytesPerSector (bytesPerSector)
  1122:   //  セクタの長さを設定する
  1123:   static void hcdSetBytesPerSector (int bytesPerSector) {
  1124:     if (bytesPerSector == 2048 ||
  1125:         bytesPerSector == 2336 ||
  1126:         bytesPerSector == 2352) {
  1127:       hcdBytesPerSector = bytesPerSector;
  1128:       if (hcdDebugInfo) {
  1129:         System.out.printf ("bytesPerSector=%d\n", bytesPerSector);
  1130:       }
  1131:     }
  1132:   }  //hcdSetBytesPerSector
  1133: 
  1134:   //buffer = hcdRead (startSector, sectors)
  1135:   //  セクタを読む
  1136:   static byte[] hcdRead (int startSector, int sectors) {
  1137:     if (hcdDebugInfo) {
  1138:       System.out.printf ("hcdRead(%d,%d)\n", startSector, sectors);
  1139:     }
  1140:     if (!hcdOpen ()) {  //開けない
  1141:       return null;
  1142:     }
  1143:     byte[] buffer = new byte[hcdBytesPerSector * sectors];
  1144:     try {
  1145:       int error;
  1146:       if (hcdBytesPerSector == 2048) {
  1147:         if ((int) SetFilePointerEx.invoke (
  1148:           hcdHandle,  //HANDLE hFile
  1149:           2048L * (long) startSector,  //LARGE_INTEGER liDistanceToMove
  1150:           MemorySegment.NULL,  //PLARGE_INTEGER lpNewFilePointer
  1151:           FILE_BEGIN) == 0 &&  //DWORD dwMoveMethod
  1152:             (error = (int) GetLastError.invoke ()) != -1) {  //シークできない
  1153:           if (hcdDebugInfo) {
  1154:             System.out.printf ("SetFilePointerEx returned error %d\n",
  1155:                                error);
  1156:           }
  1157:           hcdClose ();
  1158:           return (error == 0 ||
  1159:                   error == ERROR_NOT_READY ||
  1160:                   error == ERROR_WRONG_DISK ? null :
  1161:                   new byte[0]);
  1162:         }
  1163:       }
  1164:       for (int offset = 0; offset < sectors; ) {  //今回読む位置
  1165:         int step = Math.min (HCD_PLAY_SECTORS, sectors - offset);  //今回読むセクタ数
  1166:         if (hcdBytesPerSector == 2048) {
  1167:           if ((int) ReadFile.invoke (
  1168:             hcdHandle,  //HANDLE hFile
  1169:             hcdBufferSegment,  //LPVOID lpBuffer
  1170:             2048 * step,  //DWORD nNumberOfBytesToRead
  1171:             hcdBytesReturned,  //LPDWORD lpNumberOfBytesRead
  1172:             MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
  1173:               (error = (int) GetLastError.invoke ()) != -1) {  //読めない
  1174:             if (hcdDebugInfo) {
  1175:               System.out.printf ("ReadFile returned error %d\n",
  1176:                                  error);
  1177:             }
  1178:             hcdClose ();
  1179:             return (error == 0 ||
  1180:                     error == ERROR_NOT_READY ||
  1181:                     error == ERROR_WRONG_DISK ? null :
  1182:                     new byte[0]);
  1183:           }
  1184:         } else {
  1185:           hcdReadInfo.set (ValueLayout.JAVA_LONG,
  1186:                            RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("DiskOffset")),
  1187:                            2048L * (long) (startSector + offset));  //開始位置。2048L以外はエラー87 ERROR_INVALID_PARAMETER
  1188:           hcdReadInfo.set (ValueLayout.JAVA_INT,
  1189:                            RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("SectorCount")),
  1190:                            step);  //セクタ数
  1191:           hcdReadInfo.set (ValueLayout.JAVA_INT,
  1192:                            RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("TrackMode")),
  1193:                            hcdBytesPerSector == 2336 ? YellowMode2 : CDDA);  //モード。手元の環境ではYellowMode2はエラー87 ERROR_INVALID_PARAMETER
  1194:           if ((int) DeviceIoControl.invoke (
  1195:             hcdHandle,  //hcdHandle hDevice
  1196:             IOCTL_CDROM_RAW_READ,  //DWORD dwIoControlCode
  1197:             hcdReadInfo,  //LPVOID lpInBuffer
  1198:             (int) hcdReadInfo.byteSize (),  //DWORD nInBufferSize
  1199:             hcdBufferSegment,  //LPVOID lpOutBuffer
  1200:             hcdBytesPerSector * step,  //DWORD nOutBufferSize
  1201:             hcdBytesReturned,  //LPDWORD lpBytesReturned
  1202:             MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
  1203:               (error = (int) GetLastError.invoke ()) != -1) {  //読めない
  1204:             if (hcdDebugInfo) {
  1205:               System.out.printf ("DeviceIoControl IOCTL_CDROM_RAW_READ returned error %d\n",
  1206:                                  error);
  1207:             }
  1208:             hcdClose ();
  1209:             return (error == 0 ||
  1210:                     error == ERROR_NOT_READY ||
  1211:                     error == ERROR_WRONG_DISK ? null :
  1212:                     new byte[0]);
  1213:           }
  1214:         }
  1215:         for (int i = 0; i < hcdBytesPerSector * step; i++) {
  1216:           buffer[hcdBytesPerSector * offset + i] = hcdBufferSegment.get (ValueLayout.JAVA_BYTE, i);
  1217:         }
  1218:         offset += step;
  1219:       }  //for offset
  1220:       return buffer;
  1221:     } catch (Throwable e) {  //操作できない
  1222:       if (hcdDebugInfo) {
  1223:         e.printStackTrace ();
  1224:       }
  1225:       return null;
  1226:     }
  1227:   }  //hcdRead
  1228: 
  1229:   //buffer = hcdReadCapacity ()
  1230:   static byte[] hcdReadCapacity () {
  1231:     if (!hcdOpen ()) {  //開けない
  1232:       return null;
  1233:     }
  1234:     try {
  1235:       int error;
  1236:       MemorySegment sectorsPerCluster = arena.allocate (ValueLayout.JAVA_INT);
  1237:       MemorySegment bytesPerSector = arena.allocate (ValueLayout.JAVA_INT);
  1238:       MemorySegment numberOfFreeClusters = arena.allocate (ValueLayout.JAVA_INT);
  1239:       MemorySegment totalNumberOfClusters = arena.allocate (ValueLayout.JAVA_INT);
  1240:       if ((int) GetDiskFreeSpaceA.invoke (
  1241:         arena.allocateFrom (hcdRootPath),  //LPCSTR lpRootPathName
  1242:         sectorsPerCluster,  //LPDWORD lpSectorsPerCluster
  1243:         bytesPerSector,  //LPDWORD lpBytesPerSector
  1244:         numberOfFreeClusters,  //LPDWORD lpNumberOfFreeClusters
  1245:         totalNumberOfClusters) == 0 &&  //LPDWORD lpTotalNumberOfClusters
  1246:           (error = (int) GetLastError.invoke ()) != -1) {
  1247:         if (hcdDebugInfo) {
  1248:           System.out.printf ("GetDiskFreeSpaceA returned error %d\n",
  1249:                              error);
  1250:         }
  1251:         hcdClose ();
  1252:         return (error == 0 ||
  1253:                 error == ERROR_NOT_READY ||
  1254:                 error == ERROR_WRONG_DISK ? null :
  1255:                 new byte[0]);
  1256:       }
  1257:       byte[] buffer = new byte[8];
  1258:       ByteArray.byaWl (buffer,
  1259:                        0,
  1260:                        sectorsPerCluster.get (ValueLayout.JAVA_INT, 0) *
  1261:                        totalNumberOfClusters.get (ValueLayout.JAVA_INT, 0) - 1);  //最終論理ブロック
  1262:       ByteArray.byaWl (buffer,
  1263:                        4,
  1264:                        bytesPerSector.get (ValueLayout.JAVA_INT, 0));  //ブロック長
  1265:       return buffer;
  1266:     } catch (Throwable e) {  //操作できない
  1267:       if (hcdDebugInfo) {
  1268:         e.printStackTrace ();
  1269:       }
  1270:       return null;
  1271:     }
  1272:   }  //hcdReadCapacity
  1273: 
  1274:   //hcdEject ()
  1275:   //  イジェクトする
  1276:   //!!!メディアがないときトレイを出すか出さないか
  1277:   static void hcdEject () {
  1278:     hcdClose ();
  1279:     try {
  1280:       int error;
  1281:       MemorySegment handle;
  1282:       if ((handle = (MemorySegment) CreateFileA.invoke (
  1283:         arena.allocateFrom (hcdDevicePath),  //LPCSTR lpFileName
  1284:         0,  //DWORD dwDesiredAccess
  1285:         FILE_SHARE_READ | FILE_SHARE_WRITE,  //DWORD dwShareMode
  1286:         MemorySegment.NULL,  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
  1287:         OPEN_EXISTING,  //DWORD dwCreationDisposition
  1288:         0,   //DWORD dwFlagsAndAttributes
  1289:         MemorySegment.NULL)).address () == INVALID_HANDLE_VALUE &&  //hcdHandle hTemplateFile
  1290:           (error = (int) GetLastError.invoke ()) != -1) {
  1291:         if (hcdDebugInfo) {
  1292:           System.out.printf ("CreateFileA returned error %d\n",
  1293:                              error);
  1294:         }
  1295:         return;
  1296:       }
  1297:       MemorySegment bytesReturned = arena.allocate (ValueLayout.JAVA_INT);
  1298:       if ((int) DeviceIoControl.invoke (
  1299:         handle,  //hcdHandle hDevice
  1300:         IOCTL_STORAGE_EJECT_MEDIA,  //DWORD dwIoControlCode
  1301:         MemorySegment.NULL,  //LPVOID lpInBuffer
  1302:         0,  //DWORD nInBufferSize
  1303:         MemorySegment.NULL,  //LPVOID lpOutBuffer
  1304:         0,  //DWORD nOutBufferSize
  1305:         bytesReturned,  //LPDWORD lpBytesReturned
  1306:         MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
  1307:           (error = (int) GetLastError.invoke ()) != -1) {
  1308:         if (hcdDebugInfo) {
  1309:           System.out.printf ("DeviceIoControl IOCTL_STORAGE_EJECT_MEDIA returned error %d\n",
  1310:                              error);
  1311:         }
  1312:       }
  1313:       CloseHandle.invoke (handle);
  1314:     } catch (Throwable e) {  //操作できない
  1315:       if (hcdDebugInfo) {
  1316:         e.printStackTrace ();
  1317:       }
  1318:     }
  1319:   }  //hcdEject
  1320: 
  1321: 
  1322:   //hcdCommandComplete_1st (unit, oc)
  1323:   //  コマンドフェーズの転送が終了した
  1324:   public static void hcdCommandComplete_1st (SPC.SCUnit unit, int oc) {
  1325:     if (hcdDebugInfo) {
  1326:       unit.scuPrintCommand (oc);
  1327:     }
  1328:     hcdUnit = unit;
  1329:     hcdChip = unit.scuChip;
  1330:     hcdResultBuffer = null;
  1331:     hcdResultLength = 0;
  1332:     hcdResultSense0 = 0;
  1333:     hcdResultSense2 = 0;
  1334:     hcdResultStatus = SPC.SPC_GOOD;
  1335:     hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
  1336:     if (!(oc == 0x03 ||  //Request Senseまたは
  1337:           oc == 0x12) &&  //Inquiry以外で
  1338:         hcdChip.spiLUN != 0) {  //LUNが0でない
  1339:       unit.scuNotReady ();
  1340:       return;
  1341:     }
  1342:     switch (oc) {
  1343:       //
  1344:       //ステータスフェーズへ進むもの
  1345:       //
  1346:     case 0x01:  //Rezero Unit
  1347:       hcdDoRezeroUnit_1st ();
  1348:       break;
  1349:     case 0x03:  //Request Sense
  1350:       unit.scuDoRequestSense ();
  1351:       break;
  1352:     case 0x12:  //Inquiry
  1353:       hcdDoInquiry_1st ();
  1354:       break;
  1355:     case 0x1a:  //Mode Sense(6)
  1356:       hcdDoModeSense6_1st ();
  1357:       break;
  1358:     case 0x42:  //Read Sub-Channel
  1359:       hcdDoReadSubChannel_1st ();
  1360:       break;
  1361:     case 0x45:  //Play Audio(10)
  1362:       hcdDoPlayAudio10_1st ();
  1363:       break;
  1364:     case 0x47:  //Play Audio MSF
  1365:       hcdDoPlayAudioMSF_1st ();
  1366:       break;
  1367:     case 0x4b:  //Pause Resume
  1368:       hcdDoPauseResume_1st ();
  1369:       break;
  1370:     case 0xa5:  //Play Audio(12)
  1371:       hcdDoPlayAudio12_1st ();
  1372:       break;
  1373:       //
  1374:       //データアウトフェーズへ進むもの
  1375:       //
  1376:     case 0x15:  //Mode Select(6)
  1377:       hcdDoModeSelect6_1st ();
  1378:       break;
  1379:       //
  1380:       //読み出しスレッドで処理するもの
  1381:       //
  1382:     case 0x00:  //Test Unit Ready
  1383:     case 0x08:  //Read(6)
  1384:     case 0x1b:  //Start-Stop Unit
  1385:     case 0x25:  //Read Capacity
  1386:     case 0x28:  //Read(10)
  1387:     case 0x43:  //Read TOC
  1388:     case 0xd8:  //ReadCDDA
  1389:       hcdRequested++;
  1390:       if (hcdReadThread != null) {
  1391:         hcdReadThread.interrupt ();
  1392:       }
  1393:       break;
  1394:       //
  1395:       //未実装
  1396:       //
  1397:     default:
  1398:       hcdUnit.scuDoInvalid ();
  1399:     }
  1400:   }  //hcdCommandComplete_1st
  1401: 
  1402: 
  1403:   //hcdCommandComplete_2nd ()
  1404:   //  コマンドフェーズの転送が終了した後、読み込みスレッドがコマンドを実行する
  1405:   static void hcdCommandComplete_2nd () {
  1406:     int oc = hcdChip.spiCommandBuffer[0] & 255;  //オペレーションコード
  1407:     switch (oc) {
  1408:       //
  1409:       //読み出しスレッドで処理するもの
  1410:       //
  1411:     case 0x00:  //Test Unit Ready
  1412:       hcdDoTestUnitReady_2nd ();
  1413:       break;
  1414:     case 0x08:  //Read(6)
  1415:       hcdDoRead6_2nd ();
  1416:       break;
  1417:     case 0x1b:  //Start-Stop Unit
  1418:       hcdDoStartStopUnit_2nd ();
  1419:       break;
  1420:     case 0x25:  //Read Capacity
  1421:       hcdDoReadCapacity_2nd ();
  1422:       break;
  1423:     case 0x28:  //Read(10)
  1424:       hcdDoRead10_2nd ();
  1425:       break;
  1426:     case 0x43:  //Read TOC
  1427:       hcdDoReadTOC_2nd ();
  1428:       break;
  1429:     case 0xd8:  //ReadCDDA
  1430:       hcdDoReadCDDA_2nd ();
  1431:       break;
  1432:     }
  1433:   }  //hcdCommandComplete_2nd
  1434: 
  1435: 
  1436:   //hcdDoTestUnitReady_2nd ()
  1437:   //  [0]  0x00
  1438:   //  [1]  |LUN###|-----|
  1439:   //  [5]  |..|----|Flag|Link|
  1440:   static void hcdDoTestUnitReady_2nd () {
  1441:     if (!hcdOpen ()) {  //開けない
  1442:       hcdNotReady ();
  1443:       return;
  1444:     }
  1445:     try {
  1446:       int error;
  1447:       MemorySegment bytesReturned = arena.allocate (ValueLayout.JAVA_INT);
  1448:       if ((int) DeviceIoControl.invoke (
  1449:         hcdHandle,  //hcdHandle hDevice
  1450:         IOCTL_STORAGE_CHECK_VERIFY,  //DWORD dwIoControlCode
  1451:         MemorySegment.NULL,  //LPVOID lpInBuffer
  1452:         0,  //DWORD nInBufferSize
  1453:         MemorySegment.NULL,  //LPVOID lpOutBuffer
  1454:         0,  //DWORD nOutBufferSize
  1455:         bytesReturned,  //LPDWORD lpBytesReturned
  1456:         MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
  1457:           (error = (int) GetLastError.invoke ()) != -1) {
  1458:         if (hcdDebugInfo) {
  1459:           System.out.printf ("DeviceIoControl IOCTL_STORAGE_CHECK_VERIFY returned error %d\n",
  1460:                              error);
  1461:         }
  1462:         hcdNotReady ();
  1463:         return;
  1464:       }
  1465:     } catch (Throwable e) {  //操作できない
  1466:       if (hcdDebugInfo) {
  1467:         e.printStackTrace ();
  1468:       }
  1469:       hcdNotReady ();
  1470:       return;
  1471:     }
  1472:     hcdGood ();
  1473:   }  //hcdDoTestUnitReady_2nd
  1474: 
  1475:   //hcdDoRezeroUnit_1st ()
  1476:   //  [0]  0x01
  1477:   //  [1]  |LUN###|-----|
  1478:   //  [5]  |..|----|Flag|Link|
  1479:   static void hcdDoRezeroUnit_1st () {
  1480:     hcdRequested = 0;
  1481:     hcdCompleted = 0;
  1482:     hcdRetrieved = 0;
  1483:     hcdBytesPerSector = 2048;
  1484:     //Rezero Unitは再生を止めない
  1485:     hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE);  //エラーなしでステータスフェーズに移行する
  1486:   }  //hcdDoRezeroUnit_1st
  1487: 
  1488:   //hcdDoRead6_2nd ()
  1489:   //  [0]  0x08
  1490:   //  [1][2][3]  LUN<<21|論理ブロックアドレス
  1491:   //  [4]  論理ブロック数
  1492:   //  [5]  |..|----|Flag|Link|
  1493:   static void hcdDoRead6_2nd () {
  1494:     if (!hcdOpen ()) {  //開けない
  1495:       hcdNotReady ();
  1496:       return;
  1497:     }
  1498:     int a = ByteArray.byaRls (hcdChip.spiCommandBuffer, 0) & 0x001fffff;  //論理ブロックアドレス
  1499:     int n = hcdChip.spiCommandBuffer[4] & 255;  //論理ブロック数
  1500:     if (n == 0) {
  1501:       n = 256;
  1502:     }
  1503:     byte[] buffer = hcdRead (a, n);
  1504:     if (buffer == null) {
  1505:       hcdNotReady ();
  1506:       return;
  1507:     }
  1508:     if (buffer.length == 0) {
  1509:       hcdMediumError ();
  1510:       return;
  1511:     }
  1512:     //データインフェーズに移行する
  1513:     hcdResultBuffer = buffer;
  1514:     hcdResultLength = buffer.length;
  1515:   }  //hcdDoRead6_2nd
  1516: 
  1517:   //hcdDoInquiry_1st ()
  1518:   //  [0]  0x12
  1519:   //  [1]  |LUN###|----|EVPD|
  1520:   //  [2]  ページコード
  1521:   //  [4]  アロケーション長
  1522:   //  [5]  |..|----|Flag|Link|
  1523:   static void hcdDoInquiry_1st () {
  1524:     boolean evpd = (hcdChip.spiCommandBuffer[1] & 1) != 0;
  1525:     if (evpd) {  //VPD情報
  1526:       hcdUnit.scuIllegalRequest ();
  1527:       return;
  1528:     }
  1529:     int n = hcdChip.spiCommandBuffer[4] & 255;  //アロケーション長
  1530:     byte[] buffer = new byte[36];
  1531:     if (hcdChip.spiLUN != 0) {  //LUNが0でない
  1532:       buffer[0] = 0x7f;  //指定されたロジカル・ユニットは存在しない
  1533:     } else {
  1534:       buffer[0] = (byte) (SPC.SPC_CDROM_DEVICE);  //CD-ROMデバイス
  1535:       buffer[1] = (byte) (1 << 7);  //0=固定,1=リムーバブル
  1536:       buffer[2] = 2;  //ISO/ECMA/ANSIバージョン。SCSI-1/SCSI-2
  1537:       buffer[3] = 1;  //レスポンスデータ形式。SCSI-1/SCSI-2
  1538:       buffer[4] = 31;  //追加データ長
  1539:       buffer[5] = 0;  //予約
  1540:       buffer[6] = 0;  //予約
  1541:       buffer[7] = 0;  //サポート機能なし
  1542:       System.arraycopy (hcdVendorProduct, 0,
  1543:                         buffer, 8,
  1544:                         8 + 16 + 4);  //ベンダーID[4],プロダクトID[16],プロダクトリビジョン[4]
  1545:     }
  1546:     if (hcdDebugInfo) {
  1547:       hcdDumpBuffer (buffer, buffer.length);
  1548:     }
  1549:     hcdChip.spiDataInPhase (buffer, 0, Math.min (n, buffer.length), 0);  //データインフェーズに移行する
  1550:   }  //hcdDoInquiry_1st
  1551: 
  1552:   //hcdDoModeSelect6_1st ()
  1553:   //  [0]  0x15
  1554:   //  [1]  |LUN###|PF|---|SP|
  1555:   //  [4]  パラメータリスト長
  1556:   //  [5]  コントロールバイト
  1557:   //
  1558:   //  パラメータ・リスト
  1559:   //    モード・パラメータ・ヘッダ
  1560:   //    ブロック・ディスクリプタ
  1561:   //      :
  1562:   //    パラメータ・ページ
  1563:   //      :
  1564:   //  モード・パラメータ・ヘッダ
  1565:   //    [0]  モード・パラメータ長
  1566:   //         Mode Senseのとき(アロケーション長に関係なく)全パラメータリストの長さ。このフィールドを含まない
  1567:   //         Mode Selectのとき0x00
  1568:   //    [1]  メディア・タイプ
  1569:   //    [2]  デバイス固有パラメータ
  1570:   //    [3]  ブロック・ディスクリプタ長
  1571:   //         ブロック・ディスクリプタのバイト数。0を含む8の倍数
  1572:   //  ブロック・ディスクリプタ
  1573:   //    [0]  デンシティ・コード
  1574:   //    [1][2][3]  ブロック数
  1575:   //    [5][6][7]  ブロック長
  1576:   //  パラメータ・ページ
  1577:   //    [0]  |PS|-|ページ・コード######|
  1578:   //    [1]  ページ長n-1
  1579:   //    [2]~[n]  モード・パラメータ
  1580:   static void hcdDoModeSelect6_1st () {
  1581:     int n = hcdChip.spiCommandBuffer[4] & 255;  //パラメータリスト長
  1582:     hcdChip.spiDataOutPhase (hcdChip.spiDataOutBuffer, 0, n, 0);  //データアウトフェーズに移行する
  1583:   }  //hcdDoModeSelect6_1st
  1584: 
  1585:   //hcdDoModeSense6_1st ()
  1586:   //  [0]  0x1a
  1587:   //  [1]  |LUN###|R|DBD|---|
  1588:   //  [2]  |PC##|ページコード######|
  1589:   //  [4]  アロケーション長
  1590:   //  [5]  |..|----|Flag|Link|
  1591:   static void hcdDoModeSense6_1st () {
  1592:     boolean dbd = (hcdChip.spiCommandBuffer[1] & 8) != 0;
  1593:     int pc = (hcdChip.spiCommandBuffer[2] >> 6) & 3;  //0=カレント値,1=変更可能,2=デフォルト値,3=セーブ値
  1594:     if (pc == 3) {  //セーブ値
  1595:       hcdUnit.scuIllegalRequest ();
  1596:       return;
  1597:     }
  1598:     int pageCode = hcdChip.spiCommandBuffer[2] & 63;
  1599:     int n = hcdChip.spiCommandBuffer[4] & 255;
  1600:     //  0x01  リード・エラー・リカバリ・パラメータ p253
  1601:     //        [0]  |PS|-|ページコード0x01######|
  1602:     //        [1]  ページ長0x06
  1603:     //        [2]  エラー・リカバリ・パラメータ
  1604:     //             |--|TB|RC|-|PER|DTE|DCR|
  1605:     //             TB  回復できなくても転送する
  1606:     //             RC  回復が必要でも中断しない
  1607:     //             PER  回復したエラーを報告する
  1608:     //             DTE  回復した時点で終了する
  1609:     //             DCR  ECCによる訂正を禁止する
  1610:     //        [3]  リード・リトライ回数
  1611:     //        [4][5][6][7]
  1612:     //  0x03  フォーマット・パラメータ p199
  1613:     //  0x04  ドライブ・パラメータ p200
  1614:     //  0x0d  CD-ROMデバイス・パラメータ p255
  1615:     //  0x0e  CD-ROMオーディオ・コントロール・パラメータ p255
  1616:     //  0x3f  全パラメータ・ページの報告
  1617:     if (pageCode != 0x01 &&
  1618:         pageCode != 0x03 &&
  1619:         pageCode != 0x0e &&
  1620:         pageCode != 0x31 &&
  1621:         pageCode != 0x3f) {
  1622:       hcdUnit.scuIllegalRequest ();
  1623:       return;
  1624:     }
  1625:     byte[] buffer = new byte[256];
  1626:     //モード・パラメータ・ヘッダ
  1627:     buffer[0] = 3;  //[0]  モード・パラメータ長(Sense時のみ)
  1628:     buffer[1] = 0x00;  //[1]  メディア・タイプ
  1629:     buffer[2] = 0x00;  //[2]  デバイス固有パラメータ
  1630:     buffer[3] = 0;  //[3]  ブロック・ディスクリプタ長
  1631:     int length = 4;
  1632:     if (!dbd) {
  1633:       //ブロック・ディスクリプタ
  1634:       if (pc == 0) {  //カレント値
  1635:         buffer[length + 6] = (byte) (hcdBytesPerSector >> 8);
  1636:         buffer[length + 7] = (byte) hcdBytesPerSector;  //[5][6][7]  ブロック長
  1637:       } else if (pc == 1) {  //変更可能
  1638:         buffer[length + 6] = -1;
  1639:         buffer[length + 7] = -1;  //[5][6][7]  ブロック長
  1640:       } else {  //デフォルト値
  1641:         buffer[length + 6] = (byte) (2048 >> 8);
  1642:         buffer[length + 7] = (byte) 2048;  //[5][6][7]  ブロック長
  1643:       }
  1644:       buffer[0] += 8;  //[0]  モード・パラメータ長(Sense時のみ)
  1645:       buffer[3] += 8;  //[3]  ブロック・ディスクリプタ長
  1646:       length += 8;
  1647:     }
  1648:     if (pageCode == 0x01 ||  //リード・エラー・リカバリ・パラメータ
  1649:         pageCode == 0x3f) {  //全パラメータ・ページの報告
  1650:       //リード・エラー・リカバリ・パラメータ
  1651:       buffer[length + 0] = 0x01;  //[0]  ページ・コード
  1652:       buffer[length + 1] = 6;  //[1]  ページ長
  1653:       if (pc == 0) {  //カレント値
  1654:         buffer[length + 2] = 0x11;  //[2]  リード・エラー・リカバリ・パラメータ。エラー無視、訂正はCIRCのみ
  1655:         buffer[length + 3] = 0x00;  //[3]  リード・リトライ回数
  1656:       } else if (pc == 1) {  //変更可能
  1657:         buffer[length + 2] = 0;  //[2]  リード・エラー・リカバリ・パラメータ。エラー無視、訂正はCIRCのみ
  1658:         buffer[length + 3] = 0;  //[3]  リード・リトライ回数
  1659:       } else {  //デフォルト値
  1660:         buffer[length + 2] = 0x11;  //[2]  リード・エラー・リカバリ・パラメータ。エラー無視、訂正はCIRCのみ
  1661:         buffer[length + 3] = 0x00;  //[3]  リード・リトライ回数
  1662:       }
  1663:       buffer[0] += 8;  //[0]  モード・パラメータ長(Sense時のみ)
  1664:       length += 8;
  1665:     }
  1666:     if (pageCode == 0x03 ||  //フォーマット・パラメータ
  1667:         pageCode == 0x3f) {  //全パラメータ・ページの報告
  1668:       buffer[length + 0] = 0x03;  //[0]  ページ・コード
  1669:       buffer[length + 1] = 22;  //[1]  ページ長
  1670:       if (pc == 0) {  //カレント値
  1671:         buffer[length + 20] = 0x20;  //[20]  RMB
  1672:       } else if (pc == 1) {  //変更可能
  1673:         buffer[length + 20] = 0;  //[20]  RMB
  1674:       } else {  //デフォルト値
  1675:         buffer[length + 20] = 0x20;  //[20]  RMB
  1676:       }
  1677:       buffer[0] += 24;  //[0]  モード・パラメータ長(Sense時のみ)
  1678:       length += 24;
  1679:     }
  1680:     if (pageCode == 0x0e ||  //CD-ROMオーディオ・コントロール・パラメータ
  1681:         pageCode == 0x3f) {  //全パラメータ・ページの報告
  1682:       buffer[length + 0] = 0x0e;  //[0]  ページ・コード
  1683:       buffer[length + 1] = 14;  //[1]  ページ長
  1684:       if (pc == 0) {  //カレント値
  1685:         buffer[length + 9] =
  1686:           buffer[length + 11] =
  1687:             buffer[length + 13] =
  1688:               buffer[length + 15] = (byte) (hcdVolumeInt * 255 / 100);  //[9][11][13][15]  ボリューム
  1689:       } else if (pc == 1) {  //変更可能
  1690:         buffer[length + 9] =
  1691:           buffer[length + 11] =
  1692:             buffer[length + 13] =
  1693:               buffer[length + 15] = -1;
  1694:       } else {  //デフォルト値
  1695:         buffer[length + 9] =
  1696:           buffer[length + 11] =
  1697:             buffer[length + 13] =
  1698:               buffer[length + 15] = (byte) (HCD_DEFAULT_VOLUME * 255 / 100);  //[9][11][13][15]  ボリューム
  1699:       }
  1700:       length += 16;
  1701:     }
  1702:     if (pageCode == 0x31 ||  //転送速度
  1703:         pageCode == 0x3f) {  //全パラメータ・ページの報告
  1704:       buffer[length + 0] = 0x31;  //[0]  ページ・コード
  1705:       buffer[length + 1] = 2;  //[1]  ページ長
  1706:       if (pc == 0) {  //カレント値
  1707:         buffer[length + 2] = 24;  //[2]  転送速度
  1708:       } else if (pc == 1) {  //変更可能
  1709:         buffer[length + 2] = 0;  //[2]  転送速度
  1710:       } else {  //デフォルト値
  1711:         buffer[length + 2] = 24;  //[2]  転送速度
  1712:       }
  1713:       buffer[0] += 4;  //[0]  モード・パラメータ長(Sense時のみ)
  1714:       length += 4;
  1715:     }
  1716:     if (hcdDebugInfo) {
  1717:       hcdDumpBuffer (buffer, length);
  1718:     }
  1719:     hcdChip.spiDataInPhase (buffer, 0, Math.min (n, length), 0);  //データインフェーズに移行する
  1720:   }  //hcdDoModeSense6_1st
  1721: 
  1722:   //hcdDoStartStopUnit_2nd ()
  1723:   //  [0]  0x1b
  1724:   //  [1]  |LUN###|-----|
  1725:   //  [4]  |------|LoEj|Start|
  1726:   //  [5]  |..|----|Flag|Link|
  1727:   static void hcdDoStartStopUnit_2nd () {
  1728:     int loejStart = hcdChip.spiCommandBuffer[4] & 3;  //LoEj|Start
  1729:     if (loejStart == 2) {  //イジェクト
  1730:       //!!!再生終了
  1731:       hcdEject ();
  1732:     }
  1733:     hcdGood ();
  1734:   }  //hcdDoStartStopUnit_2nd
  1735: 
  1736:   //hcdDoReadCapacity_2nd ()
  1737:   //  [0]  0x25
  1738:   //  [1]  |LUN###|----|RelAdr|
  1739:   //  [2][3][4][5]  論理ブロックアドレス
  1740:   //  [8]  |-------|PMI|
  1741:   //  [9]  |..|----|Flag|Link|
  1742:   static void hcdDoReadCapacity_2nd () {
  1743:     if (!hcdOpen ()) {  //開けない
  1744:       hcdNotReady ();
  1745:       return;
  1746:     }
  1747:     byte[] buffer = hcdReadCapacity ();
  1748:     if (buffer == null) {
  1749:       hcdNotReady ();
  1750:       return;
  1751:     }
  1752:     if (buffer.length == 0) {
  1753:       hcdMediumError ();
  1754:       return;
  1755:     }
  1756:     //データインフェーズに移行する
  1757:     hcdResultBuffer = buffer;
  1758:     hcdResultLength = buffer.length;
  1759:   }  //hcdDoReadCapacity_2nd
  1760: 
  1761:   //hcdDoRead10_2nd ()
  1762:   //  [0]  0x28
  1763:   //  [1]  |LUN###|DPO|FUA|--|RelAdr|
  1764:   //  [2][3][4][5]  論理ブロックアドレス
  1765:   //  [7][8]  論理ブロック数
  1766:   //  [9]  |..|----|Flag|Link|
  1767:   static void hcdDoRead10_2nd () {
  1768:     if (!hcdOpen ()) {  //開けない
  1769:       hcdNotReady ();
  1770:       return;
  1771:     }
  1772:     int a = ByteArray.byaRls (hcdChip.spiCommandBuffer, 2);  //論理ブロックアドレス
  1773:     int n = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 7);  //論理ブロック数
  1774:     if (n == 0) {
  1775:       hcdGood ();
  1776:       return;
  1777:     }
  1778:     byte[] buffer = hcdRead (a, n);
  1779:     if (buffer == null) {
  1780:       hcdNotReady ();
  1781:       return;
  1782:     }
  1783:     if (buffer.length == 0) {
  1784:       hcdMediumError ();
  1785:       return;
  1786:     }
  1787:     //データインフェーズに移行する
  1788:     hcdResultBuffer = buffer;
  1789:     hcdResultLength = buffer.length;
  1790:   }  //hcdDoRead10_2nd
  1791: 
  1792:   //hcdDoReadSubChannel_1st ()
  1793:   //  [0]  0x42
  1794:   //  [1]  |LUN###|---|MSF|-|
  1795:   //  [2]  |-|SubQ|------|
  1796:   //  [3]  サブチャネル・データ形式
  1797:   //  [6]  トラック番号
  1798:   //  [7][8]  アロケーション長
  1799:   //  [9]  コントロール・バイト
  1800:   static void hcdDoReadSubChannel_1st () {
  1801:     boolean msf = (hcdChip.spiCommandBuffer[1] & 2) != 0;
  1802:     boolean subQ = (hcdChip.spiCommandBuffer[2] & 64) != 0;
  1803:     int type = hcdChip.spiCommandBuffer[3] & 255;
  1804:     int n = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 7);
  1805:     if (subQ &&
  1806:         type == 0x01) {  //カレント・ポジション
  1807:       int currentSector = 0;  //現在のセクタ
  1808:       int currentTrack = hcdTOCFirstTrack;  //現在のセクタのトラックの番号
  1809:       int currentOffset = 0;  //現在のセクタのトラックの先頭からのオフセット
  1810:       if (hcdPlaying &&  //再生中
  1811:           hcdTOCAddressArray != null) {  //TOC情報あり
  1812:         currentSector = hcdCurrentSector;
  1813:         while (currentTrack + 1 <= hcdTOCLastTrack &&
  1814:                hcdTOCAddressArray[currentTrack + 1 - hcdTOCFirstTrack] <= currentSector) {
  1815:           currentTrack++;
  1816:         }
  1817:         currentOffset = currentSector - hcdTOCAddressArray[currentTrack - hcdTOCFirstTrack];
  1818:       }
  1819:       //
  1820:       byte[] buffer = new byte[16];
  1821:       buffer[1] = (byte) hcdAudioStatus;  //オーディオ・ステータス
  1822:       if (hcdAudioStatus == 0x13 ||  //正常終了または
  1823:           hcdAudioStatus == 0x14) {  //エラー終了のとき
  1824:         hcdAudioStatus = 0x15;  //情報なし
  1825:       }
  1826:       buffer[2] = 0;  //サブチャネル・データ長
  1827:       buffer[3] = 12;
  1828:       buffer[3] = 0x01;  //サブチャネル・データ形式
  1829:       buffer[5] = 0x10;  //ADR<<4|コントロール
  1830:       buffer[6] = (byte) ((currentTrack / 10) << 4 |
  1831:                           (currentTrack % 10));  //トラック番号。BCD形式
  1832:       buffer[7] = 0x01;  //インデックス番号。BCD形式。!!!トラックの先頭のポーズ領域は0x00
  1833:       if (msf) {
  1834:         int t = currentSector + 75 * 2;
  1835:         buffer[8] = 0;  //アブソリュートCD-ROMアドレス
  1836:         buffer[9] = (byte) (t / (75 * 60));
  1837:         buffer[10] = (byte) ((t / 75) % 60);
  1838:         buffer[11] = (byte) (t % 75);
  1839:         buffer[12] = 0;  //トラック相対CD-ROMアドレス
  1840:         buffer[13] = (byte) (currentOffset / (75 * 60));
  1841:         buffer[14] = (byte) ((currentOffset / 75) % 60);
  1842:         buffer[15] = (byte) (currentOffset % 75);
  1843:       } else {
  1844:         buffer[8] = (byte) (currentSector >> 24);  //アブソリュートCD-ROMアドレス
  1845:         buffer[9] = (byte) (currentSector >> 16);
  1846:         buffer[10] = (byte) (currentSector >> 8);
  1847:         buffer[11] = (byte) currentSector;
  1848:         buffer[12] = (byte) (currentOffset >> 24);  //トラック相対CD-ROMアドレス
  1849:         buffer[13] = (byte) (currentOffset >> 16);
  1850:         buffer[14] = (byte) (currentOffset >> 8);
  1851:         buffer[15] = (byte) currentOffset;
  1852:       }
  1853:       if (false) {
  1854:         System.out.printf ("currentSector=%d\n", currentSector);
  1855:         System.out.printf ("currentTrack=%d\n", currentTrack);
  1856:         System.out.printf ("currentOffset=%d\n", currentOffset);
  1857:         for (int i = 0; i < buffer.length; i++) {
  1858:           System.out.printf ("%02x ", buffer[i] & 255);
  1859:         }
  1860:         System.out.println ();
  1861:       }
  1862:       if (hcdDebugInfo) {
  1863:         hcdDumpBuffer (buffer, buffer.length);
  1864:       }
  1865:       hcdChip.spiDataInPhase (buffer, 0, Math.min (n, buffer.length), 0);  //データインフェーズに移行する
  1866:     } else {
  1867:       hcdIllegalRequest ();
  1868:     }
  1869:   }  //hcdDoReadSubChannel_1st
  1870: 
  1871:   //hcdDoReadTOC_2nd ()
  1872:   //  [0]  0x43
  1873:   //  [1]  |LUN###|---|MSF|-|
  1874:   //  [6]  開始トラック
  1875:   //  [7][8]  アロケーション長
  1876:   //  [9]  コントロールバイト
  1877:   static void hcdDoReadTOC_2nd () {
  1878:     boolean msf = (hcdChip.spiCommandBuffer[1] & 2) != 0;  //true=MSFアドレス形式,false=論理ブロックアドレス形式
  1879:     int startTrack = hcdChip.spiCommandBuffer[6] & 255;  //開始トラック
  1880:     if (startTrack == 0) {
  1881:       startTrack = 1;
  1882:     }
  1883:     if (0xaa < startTrack) {  //開始トラック番号が範囲外
  1884:       hcdIllegalRequest ();
  1885:       return;
  1886:     }
  1887:     int allocLength = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 7);  //アロケーション長
  1888:     byte[] buffer = hcdReadTOC (msf);
  1889:     if (buffer == null) {
  1890:       hcdNotReady ();
  1891:       return;
  1892:     }
  1893:     if (buffer.length == 0) {
  1894:       hcdMediumError ();
  1895:       return;
  1896:     }
  1897:     //トラックの開始セクタを集める
  1898:     //  TrackData[0].TrackNumber==FirstTrack
  1899:     //  TrackData[LastTrack-FirstTrack].TrackNumber==LastTrack
  1900:     //  TrackData[LastTrack-FirstTrack+1].TrackNumber==170
  1901:     hcdTOCAddressArray = new int[(buffer.length - 4) / 8];  //TrackDataのAddressの配列
  1902:     hcdTOCFirstTrack = buffer[2] & 255;
  1903:     hcdTOCLastTrack = buffer[3] & 255;
  1904:     for (int i = 0; i < hcdTOCAddressArray.length; i++) {
  1905:       hcdTOCAddressArray[i] = (msf ?
  1906:                                -75 * 2 +
  1907:                                (buffer[4 + 8 * i + 5] & 255) * (75 * 60) +
  1908:                                (buffer[4 + 8 * i + 6] & 255) * 75 +
  1909:                                (buffer[4 + 8 * i + 7] & 255) :
  1910:                                (buffer[4 + 8 * i + 4] & 255) << 24 |
  1911:                                (buffer[4 + 8 * i + 5] & 255) << 16 |
  1912:                                (buffer[4 + 8 * i + 6] & 255) << 8 |
  1913:                                (buffer[4 + 8 * i + 7] & 255));
  1914:     }
  1915:     //開始トラックに満たないデータトラックを詰める
  1916:     int dataLength = buffer.length;  //全体の長さ
  1917:     int p = 4;  //残すデータの開始位置
  1918:     while (p < dataLength - 8 &&  //リードアウトトラックでなく
  1919:            (0xff & buffer[p + 2]) < startTrack) {  //開始トラックに満たない
  1920:       p += 8;
  1921:     }
  1922:     if (4 < p) {
  1923:       for (int i = 0; i < dataLength - p; i++) {
  1924:         buffer[4 + i] = buffer[p + i];
  1925:       }
  1926:       dataLength -= p - 4;
  1927:       ByteArray.byaWw (buffer, 0, dataLength - 2);
  1928:     }
  1929:     //データインフェーズに移行する
  1930:     hcdResultBuffer = buffer;
  1931:     hcdResultLength = Math.min (dataLength, allocLength);
  1932:   }  //hcdDoReadTOC_2nd
  1933: 
  1934:   //hcdDoPlayAudio10_1st ()
  1935:   //  [0]  0x45
  1936:   //  [1]  |LUN###|----|RelAdr|
  1937:   //  [2][3][4][5]  論理ブロック・アドレス
  1938:   //  [7][8]  転送データ長
  1939:   //  [9]  コントロール・バイト
  1940:   static void hcdDoPlayAudio10_1st () {
  1941:     int start = ByteArray.byaRls (hcdChip.spiCommandBuffer, 2);  //開始位置
  1942:     int length = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 7);  //長さ
  1943:     hcdPlay (start, start + length);
  1944:     hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE);  //エラーなしでステータスフェーズに移行する
  1945:   }  //hcdDoPlayAudio10_1st
  1946: 
  1947:   //hcdDoPlayAudioMSF_1st ()
  1948:   //  [0]  0x47
  1949:   //  [1]  |LUN###|-----|
  1950:   //  [3]  開始・Mフィールド
  1951:   //  [4]  開始・Sフィールド
  1952:   //  [5]  開始・Fフィールド
  1953:   //  [6]  終了・Mフィールド
  1954:   //  [7]  終了・Sフィールド
  1955:   //  [8]  終了・Fフィールド
  1956:   //  [9]  コントロール・バイト
  1957:   static void hcdDoPlayAudioMSF_1st () {
  1958:     int start = (-75 * 2 +
  1959:                  (hcdChip.spiCommandBuffer[3] & 255) * (75 * 60) +
  1960:                  (hcdChip.spiCommandBuffer[4] & 255) * 75 +
  1961:                  (hcdChip.spiCommandBuffer[5] & 255));  //開始位置
  1962:     int end = (-75 * 2 +
  1963:                (hcdChip.spiCommandBuffer[6] & 255) * (75 * 60) +
  1964:                (hcdChip.spiCommandBuffer[7] & 255) * 75 +
  1965:                (hcdChip.spiCommandBuffer[8] & 255));  //終了位置
  1966:     hcdPlay (start, end);
  1967:     hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE);  //エラーなしでステータスフェーズに移行する
  1968:   }  //hcd.DoPlayAudioMSF
  1969: 
  1970:   //hcdDoPauseResume_1st ()
  1971:   //  [0]  0x4b
  1972:   //  [1]  |LUN###|-----|
  1973:   //  [8]  |-------|Resume|
  1974:   //  [9]  コントロール・バイト
  1975:   static void hcdDoPauseResume_1st () {
  1976:     boolean resume = (hcdChip.spiCommandBuffer[8] & 1) != 0;
  1977:     if (resume) {
  1978:       hcdResume ();
  1979:     } else {
  1980:       hcdPause ();
  1981:     }
  1982:     hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE);  //エラーなしでステータスフェーズに移行する
  1983:   }  //hcdDoPauseResume_1st
  1984: 
  1985:   //hcdDoPlayAudio12_1st ()
  1986:   //  [0]  0xa5
  1987:   //  [1]  |LUN###|----|RelAdr|
  1988:   //  [2][3][4][5]  論理ブロック・アドレス
  1989:   //  [6][7][8][9]  転送データ長
  1990:   //  [11]  コントロール・バイト
  1991:   static void hcdDoPlayAudio12_1st () {
  1992:     int start = ByteArray.byaRls (hcdChip.spiCommandBuffer, 2);  //開始位置
  1993:     int length = ByteArray.byaRls (hcdChip.spiCommandBuffer, 6);  //長さ
  1994:     hcdPlay (start, start + length);
  1995:     hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE);  //エラーなしでステータスフェーズに移行する
  1996:   }  //hcdDoPlayAudio12_1st
  1997: 
  1998:   //hcdDoReadCDDA_2nd ()
  1999:   //  [0]  0xd8
  2000:   //  [1]  |LUN###|-----|
  2001:   //  [2][3][4][5]  論理ブロックアドレス
  2002:   //  [8][9]  論理ブロック数
  2003:   static void hcdDoReadCDDA_2nd () {
  2004:     if (!hcdOpen ()) {  //開けない
  2005:       hcdNotReady ();
  2006:       return;
  2007:     }
  2008:     int a = ByteArray.byaRls (hcdChip.spiCommandBuffer, 2);  //論理ブロックアドレス
  2009:     int n = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 8);  //論理ブロック数
  2010:     if (n == 0) {
  2011:       hcdGood ();
  2012:       return;
  2013:     }
  2014:     byte[] buffer = hcdRead (a, n);
  2015:     if (buffer == null) {
  2016:       hcdNotReady ();
  2017:       return;
  2018:     }
  2019:     if (buffer.length == 0) {
  2020:       hcdMediumError ();
  2021:       return;
  2022:     }
  2023:     //データインフェーズに移行する
  2024:     hcdResultBuffer = buffer;
  2025:     hcdResultLength = buffer.length;
  2026:   }  //hcdDoReadCDDA_2nd
  2027: 
  2028: 
  2029:   static void hcdGood () {
  2030:     hcdResultSense0 = 0;
  2031:     hcdResultSense2 = 0;
  2032:     hcdResultStatus = SPC.SPC_GOOD;
  2033:     hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
  2034:   }  //hcdGood
  2035: 
  2036:   static void hcdNotReady () {
  2037:     hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
  2038:     hcdResultSense2 = SPC.SPC_NOT_READY;
  2039:     hcdResultStatus = SPC.SPC_CHECK_CONDITION;
  2040:     hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
  2041:   }  //hcdNotReady
  2042: 
  2043:   static void hcdUnitAttention () {
  2044:     hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
  2045:     hcdResultSense2 = SPC.SPC_UNIT_ATTENTION;
  2046:     hcdResultStatus = SPC.SPC_CHECK_CONDITION;
  2047:     hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
  2048:   }  //hcdUnitAttention
  2049: 
  2050:   static void hcdDataProtect () {
  2051:     hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
  2052:     hcdResultSense2 = SPC.SPC_DATA_PROTECT;
  2053:     hcdResultStatus = SPC.SPC_CHECK_CONDITION;
  2054:     hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
  2055:   }  //hcdDataProtect
  2056: 
  2057:   static void hcdMediumError () {
  2058:     hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
  2059:     hcdResultSense2 = SPC.SPC_MEDIUM_ERROR;
  2060:     hcdResultStatus = SPC.SPC_CHECK_CONDITION;
  2061:     hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
  2062:   }  //hcdMediumError
  2063: 
  2064:   static void hcdIllegalRequest () {
  2065:     hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
  2066:     hcdResultSense2 = SPC.SPC_ILLEGAL_REQUEST;
  2067:     hcdResultStatus = SPC.SPC_CHECK_CONDITION;
  2068:     hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
  2069:   }  //hcdIllegalRequest
  2070: 
  2071: 
  2072:   //hcdReadINTSPSNS ()
  2073:   //  INTSまたはPSNSが読み出された
  2074:   public static void hcdReadINTSPSNS () {
  2075:     if (hcdRetrieved != hcdCompleted) {  //回収カウンタ!=完了カウンタのとき
  2076:       if (hcdResultBuffer != null) {  //結果のデータがあるとき
  2077:         hcdChip.spiDataInPhase (hcdResultBuffer, 0, hcdResultLength, 0);  //データインフェーズに移行する
  2078:       } else {  //結果のデータがないとき
  2079:         hcdChip.spiSenseBuffer[hcdChip.spiLUN][0] = (byte) hcdResultSense0;
  2080:         hcdChip.spiSenseBuffer[hcdChip.spiLUN][2] = (byte) hcdResultSense2;
  2081:         hcdChip.spiStatusPhase (hcdResultStatus, hcdResultMessage);  //ステータスフェーズに移行する
  2082:       }
  2083:       hcdChip.spiSetInterruptStatus (SPC.SPC_INTS_CC);
  2084:       hcdRetrieved = hcdCompleted;
  2085:     }
  2086:   }  //hcdReadINTSPSNS
  2087: 
  2088: 
  2089:   //hcdDataOutComplete (unit, oc)
  2090:   //  データアウトフェーズの転送が終了した
  2091:   public static void hcdDataOutComplete (int oc) {
  2092:     switch (oc) {
  2093:     case 0x15:  //Mode Select(6)
  2094:       hcdDoModeSelect6_2nd ();
  2095:       break;
  2096:     default:
  2097:       hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE);  //エラーなしでステータスフェーズに移行する
  2098:     }
  2099:   }  //hcdDataOutComplete
  2100: 
  2101:   //hcdDoModeSelect6_2ns ()
  2102:   static void hcdDoModeSelect6_2nd () {
  2103:     int oc = hcdChip.spiCommandBuffer[0] & 255;  //オペレーションコード
  2104:     if (hcdDebugInfo) {
  2105:       hcdUnit.scuPrintDataOut (oc);
  2106:     }
  2107:     int length = 4;
  2108:     if (length + 8 <= hcdChip.spiBufferLimit &&
  2109:         (hcdChip.spiDataOutBuffer[3] & 255) == 8) {  //ブロック・ディスクリプタ
  2110:       hcdSetBytesPerSector ((hcdChip.spiDataOutBuffer[length + 6] & 255) << 8 |
  2111:                             (hcdChip.spiDataOutBuffer[length + 7] & 255));
  2112:       length += 8;
  2113:     }
  2114:     while (length + 1 <= hcdChip.spiBufferLimit) {
  2115:       int pageCode = hcdChip.spiDataOutBuffer[length] & 63;  //ページコード
  2116:       if (length + 8 <= hcdChip.spiBufferLimit &&
  2117:           pageCode == 0x01) {  //リード・エラー・リカバリ・パラメータ
  2118:         length += 8;
  2119:       } else if (length + 24 <= hcdChip.spiBufferLimit &&
  2120:                  pageCode == 0x03) {  //フォーマット・パラメータ
  2121:         length += 24;
  2122:       } else if (length + 16 <= hcdChip.spiBufferLimit &&
  2123:                  pageCode == 0x0e) {  //CD-ROMオーディオ・コントロール・パラメータ
  2124:         hcdVolumeInt = (hcdChip.spiDataOutBuffer[length + 9] & 255) * 100 / 255;  //ボリューム(ポート#0)
  2125:         if (hcdDebugInfo) {
  2126:           System.out.printf ("volume=%d\n", hcdVolumeInt);
  2127:         }
  2128:         hcdVolumeFloat = (float) hcdVolumeInt / 100F;
  2129:         hcdVolumeSlider.setValue (hcdVolumeInt);
  2130:         hcdVolumeLabel.setText (String.valueOf (hcdVolumeInt));
  2131:         length += 16;
  2132:       } else if (length + 4 <= hcdChip.spiBufferLimit &&
  2133:                  pageCode == 0x31) {  //転送速度
  2134:         length += 4;
  2135:       } else {
  2136:         break;
  2137:       }
  2138:     }
  2139:     hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE);  //エラーなしでステータスフェーズに移行する
  2140:   }  //hcdDoModeSelect6_2nd
  2141: 
  2142:   //hcdDumpBuffer (buffer, length)
  2143:   static void hcdDumpBuffer (byte[] buffer, int length) {
  2144:     System.out.print ("[");
  2145:     for (int i = 0; i < length; i++) {
  2146:       if (i != 0) {
  2147:         System.out.print (",");
  2148:       }
  2149:       System.out.printf ("0x%02x", buffer[i] & 255);
  2150:     }
  2151:     System.out.println ("]");
  2152:   }  //hcdDumpBuffer
  2153: 
  2154: }  //class HostCDROM