xeij/HostCDROM.java
//========================================================================================
// HostCDROM.java
// en:Host CD-ROM
// ja:ホストCD-ROM
// Copyright (C) 2003-2025 Makoto Kamada
//
// This file is part of the XEiJ (X68000 Emulator in Java).
// You can use, modify and redistribute the XEiJ if the conditions are met.
// Read the XEiJ License for more details.
// https://stdkmd.net/xeij/
//========================================================================================
package xeij;
import java.awt.event.*; //ActionListener
import java.lang.foreign.*;
import java.lang.invoke.*;
import java.util.*; //NoSuchElementException
import javax.sound.sampled.*;
import javax.swing.*;
import javax.swing.event.*; //ChangeListener
//class HostCDROM
// WindowsのときホストマシンのDVD/CD-ROMドライブをSCSI CD-ROMドライブとして使用します
public class HostCDROM {
//定数
//設定
public static final boolean HCD_ENABLED = true; //true=有効
static final int HCD_DEFAULT_SCSI_ID = 6; //デフォルトのSCSI ID(0~15)
static final int HCD_DEFAULT_VOLUME = 25; //デフォルトの音量(0~100)
static final int HCD_PLAY_QUEUE_SIZE = 4; //再生キューのサイズ
static final int HCD_PLAY_SECTORS = 30; //一度に再生するセクタ数。30セクタ
static final int HCD_PLAY_MILLIS = 1000 * HCD_PLAY_SECTORS / 75; //一度に再生するミリ秒数。1000*30/75=400ms
static final int HCD_PLAY_BYTES = 2352 * HCD_PLAY_SECTORS; //一度に再生するバイト数。2352*30=70560バイト
//エラーコード
// https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
static final int ERROR_NOT_READY = 21;
static final int ERROR_WRONG_DISK = 34;
static final int ERROR_NO_MORE_ITEMS = 259;
//ファイル操作
static final long INVALID_HANDLE_VALUE = -1L;
// https://learn.microsoft.com/ja-jp/windows/win32/secauthz/generic-access-rights
static final int GENERIC_ALL = 0x10000000;
static final int GENERIC_EXECUTE = 0x20000000;
static final int GENERIC_WRITE = 0x40000000;
static final int GENERIC_READ = 0x80000000;
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilea
static final int FILE_SHARE_READ = 0x00000001;
static final int FILE_SHARE_WRITE = 0x00000002;
static final int FILE_SHARE_DELETE = 0x00000004;
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilea
static final int CREATE_NEW = 1;
static final int CREATE_ALWAYS = 2;
static final int OPEN_EXISTING = 3;
static final int OPEN_ALWAYS = 4;
static final int TRUNCATE_EXISTING = 5;
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-setfilepointerex
static final int FILE_BEGIN = 0;
static final int FILE_CURRENT = 1;
static final int FILE_END = 2;
//CD-ROM操作
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-getdrivetypea
static final int DRIVE_UNKNOWN = 0;
static final int DRIVE_NO_ROOT_DIR = 1;
static final int DRIVE_REMOVABLE = 2;
static final int DRIVE_FIXED = 3;
static final int DRIVE_REMOTE = 4;
static final int DRIVE_CDROM = 5;
static final int DRIVE_RAMDISK = 6;
//DeviceIoControl
static final int IOCTL_CDROM_READ_TOC = 0x00024000;
static final int IOCTL_CDROM_RAW_READ = 0x0002403e;
static final int IOCTL_CDROM_READ_TOC_EX = 0x00024054;
static final int IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x0004d014;
static final int IOCTL_SCSI_PASS_THROUGH_DIRECT_EX = 0x0004d048;
static final int IOCTL_STORAGE_CHECK_VERIFY2 = 0x002d0800;
static final int IOCTL_STORAGE_LOAD_MEDIA2 = 0x002d080c;
static final int IOCTL_STORAGE_QUERY_PROPERTY = 0x002d1400;
static final int IOCTL_STORAGE_CHECK_VERIFY = 0x002d4800;
static final int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002d4804;
static final int IOCTL_STORAGE_EJECT_MEDIA = 0x002d4808;
static final int IOCTL_STORAGE_LOAD_MEDIA = 0x002d480c;
static final int CDROM_READ_TOC_EX_FORMAT_TOC = 0x00000000;
//CDROM_TOC
static final int MAXIMUM_NUMBER_TRACKS = 0x00000064;
//TRACK_MODE_TYPE
// https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddcdrm/ne-ntddcdrm-_track_mode_type
static final int YellowMode2 = 0;
static final int XAForm2 = 1;
static final int CDDA = 2;
static final int RawWithC2AndSubCode = 3;
static final int RawWithC2 = 4;
static final int RawWithSubCode = 5;
//STORAGE_PROPERTY_ID列挙
// https://learn.microsoft.com/ja-jp/windows/win32/api/winioctl/ne-winioctl-storage_property_id
static final int StorageDeviceProperty = 0;
//STORAGE_QUERY_TYPE列挙
// https://learn.microsoft.com/ja-jp/windows/win32/api/winioctl/ne-winioctl-storage_query_type
static final int PropertyStandardQuery = 0;
//バスタイプ
static final int BusTypeUnknown = 0;
static final int BusTypeScsi = 1;
static final int BusTypeAtapi = 2;
static final int BusTypeAta = 3;
static final int BusType1394 = 4;
static final int BusTypeSsa = 5;
static final int BusTypeFibre = 6;
static final int BusTypeUsb = 7;
static final int BusTypeRAID = 8;
static final int BusTypeiScsi = 9;
static final int BusTypeSas = 10;
static final int BusTypeSata = 11;
static final int BusTypeSd = 12;
static final int BusTypeMmc = 13;
static final int BusTypeVirtual = 14;
static final int BusTypeFileBackedVirtual = 15;
static final int BusTypeSpaces = 16;
static final int BusTypeNvme = 17;
static final int BusTypeSCM = 18;
static final int BusTypeUfs = 19;
static final int BusTypeNvmeof = 20;
static final int BusTypeMax = 21;
static final int BusTypeMaxReserved = 127;
//変数
//パラメータ
static boolean hcdAvailable; //true=ホストのCD-ROMが利用可能。WindowsでCD-ROMドライブがあり操作できる
static boolean hcdDebugInfo; //true=デバッグ情報を出力する
static boolean hcdConnectNext; //true=次回は接続する
static boolean hcdConnected; //true=接続している
static int hcdSCSIIdNext; //次回のSCSI ID
public static int hcdSCSIId; //SCSI ID
static int hcdVolumeInt; //音量(0~100)
static float hcdVolumeFloat; //音量(0.0~1.0)
//メニュー
static JSpinner hcdIdSpinner; //SCSI IDスピナー
static SpinnerNumberModel hcdIdModel; //SCSI IDスピナーのスピナーモデル
static JLabel hcdVolumeLabel; //音量を表示するラベル
static JSlider hcdVolumeSlider; //音量スライダー
public static JMenu hcdMenu; //メニュー。hcdAvailableのとき有効
//構造体
static MemoryLayout TRACK_DATA;
static MemoryLayout CDROM_TOC;
static MemoryLayout CDROM_READ_TOC_EX;
static MemoryLayout RAW_READ_INFO;
static MemoryLayout STORAGE_DEVICE_DESCRIPTOR;
static MemoryLayout STORAGE_PROPERTY_QUERY;
//リンカ
static Linker linker;
static MethodHandle downcallHandle (MemorySegment address, FunctionDescriptor function) {
return linker.downcallHandle (address, function);
}
//アリーナ
static Arena arena;
//関数
static MethodHandle CloseHandle;
static MethodHandle CreateFileA;
static MethodHandle DeviceIoControl;
static MethodHandle GetDiskFreeSpaceA;
static MethodHandle GetDriveTypeA;
static MethodHandle GetLastError;
static MethodHandle GetLogicalDrives;
static MethodHandle QueryDosDeviceA;
static MethodHandle ReadFile;
static MethodHandle SetFilePointerEx;
//CD-ROM
static int hcdDriveLetter; //ドライブレター。'E'など
static String hcdRootPath; //ルートパス。"E:\\"など
static String hcdDevicePath; //デバイスパス。"\\\\.\\E:"など
public static String hcdDeviceName; //デバイス名。"E:"など
static byte[] hcdVendorProduct; //ベンダーID[4],プロダクトID[16],プロダクトリビジョン[4]
//動作中フラグ
static volatile boolean hcdRunning; //動作中
static volatile boolean hcdPlaying; //再生中
static volatile boolean hcdPausing; //中断中
static volatile int hcdAudioStatus; //0x11=再生中,0x12=中断中,0x13=正常終了,0x14=エラー終了,0x15=情報なし
//メモリセグメント
static MemorySegment hcdReadTocEx;
static MemorySegment hcdToc;
static MemorySegment hcdBytesReturned;
static MemorySegment hcdBufferSegment;
static MemorySegment hcdReadInfo;
//ハンドル
static MemorySegment hcdHandle;
//ソースデータライン
static SourceDataLine hcdSourceDataLine;
//再生キュー
static byte[][] hcdPlayQueueArray;
static volatile int hcdPlayQueueWrite;
static volatile int hcdPlayQueueRead;
//読み出しスレッド
static Thread hcdReadThread;
static volatile int hcdStartSector;
static volatile int hcdCurrentSector;
static volatile int hcdEndSector;
//トラック
static int[] hcdTOCAddressArray; //TrackDataのAddressの配列。null=未確認
static int hcdTOCFirstTrack;
static int hcdTOCLastTrack;
//CDXA
static int hcdDataOffset; //物理セクタの先頭からデータの先頭までのオフセット。-1=未確認
//再生スレッド
static Thread hcdPlayThread;
//コマンド
static volatile int hcdRequested; //要求カウンタ
static volatile int hcdCompleted; //完了カウンタ
static volatile int hcdRetrieved; //回収カウンタ
static volatile SPC.SCUnit hcdUnit; //要求したユニット
static volatile SPC.SPCChip hcdChip; //要求したインタフェイス
static volatile byte[] hcdResultBuffer; //結果のデータ
static volatile int hcdResultLength; //結果の長さ
static volatile int hcdResultSense0; //結果のセンスデータ[0]
static volatile int hcdResultSense2; //結果のセンスデータ[2]
static volatile int hcdResultStatus; //結果のステータス
static volatile int hcdResultMessage; //結果のメッセージ
static volatile int hcdBytesPerSector; //Readで読み出す1セクタのバイト数
//メソッド
//hcdInit ()
// 初期化
public static void hcdInit () {
//パラメータ
hcdAvailable = HCD_ENABLED && XEiJ.prgIsWindows;
hcdDebugInfo = Settings.sgsGetOnOff ("hcddebug");
hcdConnectNext = Settings.sgsGetOnOff ("hcdconnect");
hcdConnected = hcdConnectNext;
hcdSCSIIdNext = Settings.sgsGetInt ("hcdscsiid", HCD_DEFAULT_SCSI_ID);
if (hcdSCSIIdNext < 0 || 15 < hcdSCSIIdNext) {
hcdSCSIIdNext = HCD_DEFAULT_SCSI_ID;
}
hcdSCSIId = hcdSCSIIdNext;
hcdVolumeInt = Settings.sgsGetInt ("hcdvolume", HCD_DEFAULT_VOLUME);
if (hcdVolumeInt < 0 || 100 < hcdVolumeInt) {
hcdVolumeInt = HCD_DEFAULT_VOLUME;
}
if (hcdDebugInfo) {
System.out.printf ("volume=%d\n", hcdVolumeInt);
}
hcdVolumeFloat = (float) hcdVolumeInt / 100F;
//メニュー
ActionListener listener = new ActionListener () {
@Override public void actionPerformed (ActionEvent ae) {
Object source = ae.getSource ();
String command = ae.getActionCommand ();
switch (command) {
case "Connect on next execution":
hcdConnectNext = ((JCheckBoxMenuItem) source).isSelected ();
break;
case "Debug info":
hcdDebugInfo = ((JCheckBoxMenuItem) source).isSelected ();
break;
default:
System.out.println ("unknown action command " + command);
}
}
};
hcdMenu = Multilingual.mlnText (
ComponentFactory.createMenu (
"Host CD-ROM",
Multilingual.mlnText (
ComponentFactory.createCheckBoxMenuItem (hcdConnectNext, "Connect on next execution", listener),
"ja", "次回の実行時に接続する"),
ComponentFactory.createHorizontalBox (
Box.createHorizontalStrut (20),
ComponentFactory.createLabel ("SCSI ID "),
hcdIdSpinner = ComponentFactory.createNumberSpinner (
hcdIdModel = new SpinnerNumberModel (hcdSCSIIdNext, 0, 15, 1),
2,
new ChangeListener () {
@Override public void stateChanged (ChangeEvent ce) {
hcdSCSIIdNext = hcdIdModel.getNumber ().intValue ();
}
}
),
Box.createHorizontalGlue ()
),
ComponentFactory.createHorizontalBox (
Box.createHorizontalGlue (),
Multilingual.mlnText (ComponentFactory.createLabel ("Volume "), "ja", "音量 "),
hcdVolumeLabel = ComponentFactory.createLabel (String.valueOf (hcdVolumeInt)),
Box.createHorizontalGlue ()
),
ComponentFactory.createHorizontalBox (
hcdVolumeSlider = ComponentFactory.setPreferredSize (
ComponentFactory.createHorizontalSlider (
0, 100, hcdVolumeInt, 10, 1,
new ChangeListener () {
@Override public void stateChanged (ChangeEvent ce) {
hcdVolumeInt = ((JSlider) ce.getSource ()).getValue ();
if (hcdDebugInfo) {
System.out.printf ("volume=%d\n", hcdVolumeInt);
}
hcdVolumeFloat = (float) hcdVolumeInt / 100F;
hcdVolumeLabel.setText (String.valueOf (hcdVolumeInt));
}
}
),
LnF.lnfFontSize * 18, LnF.lnfFontSize * 2 + 28)
),
ComponentFactory.createHorizontalSeparator (),
Multilingual.mlnText (
ComponentFactory.createCheckBoxMenuItem (hcdDebugInfo, "Debug info", listener),
"ja", "デバッグ情報")
),
"ja", "ホスト CD-ROM");
hcdMenu.setEnabled (false);
if (!hcdAvailable) { //有効になっていないかWindowsでない
hcdConnected = false;
return;
}
//TRACK_DATA構造体
// https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddcdrm/ns-ntddcdrm-_track_data
TRACK_DATA = MemoryLayout.structLayout (
ValueLayout.JAVA_BYTE.withName ("Reserved"), //UCHAR Reserved
ValueLayout.JAVA_BYTE.withName ("Adr_Control"), //UCHAR Control:4; UCHAR Adr:4
ValueLayout.JAVA_BYTE.withName ("TrackNumber"), //UCHAR TrackNumber
ValueLayout.JAVA_BYTE.withName ("Reserved1"), //UCHAR Reserved1
MemoryLayout.sequenceLayout (4, ValueLayout.JAVA_BYTE).withName ("Address")); //UCHAR Address[4]
//CDROM_TOC構造体
// https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddcdrm/ns-ntddcdrm-_cdrom_toc
CDROM_TOC = MemoryLayout.structLayout (
MemoryLayout.sequenceLayout (2, ValueLayout.JAVA_BYTE).withName ("Length"), //UCHAR Length[2]
ValueLayout.JAVA_BYTE.withName ("FirstTrack"), //UCHAR FirstTrack
ValueLayout.JAVA_BYTE.withName ("LastTrack"), //UCHAR LastTrack
MemoryLayout.sequenceLayout (MAXIMUM_NUMBER_TRACKS, TRACK_DATA).withName ("TrackData")); //TrackData[MAXIMUM_NUMBER_TRACKS]
//CDROM_READ_TOC_EX構造体
CDROM_READ_TOC_EX = MemoryLayout.structLayout (
ValueLayout.JAVA_BYTE.withName ("Msf_Reserved1_Format"), //UCHAR Format:4; UCHAR Reserved1:3; UCHAR Msf:1
ValueLayout.JAVA_BYTE.withName ("SessionTrack"), //UCHAR SessionTrack
ValueLayout.JAVA_BYTE.withName ("Reserved2"), //UCHAR Reserved2
ValueLayout.JAVA_BYTE.withName ("Reserved3")); //UCHAR Reserved3
//RAW_READ_INFO構造体
// https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddcdrm/ns-ntddcdrm-__raw_read_info
RAW_READ_INFO = MemoryLayout.structLayout (
ValueLayout.JAVA_LONG.withName ("DiskOffset"), //LARGE_INTEGER DiskOffset
ValueLayout.JAVA_INT.withName ("SectorCount"), //ULONG SectorCount
ValueLayout.JAVA_INT.withName ("TrackMode")); //TRACK_MODE_TYPE TrackMode
//STORAGE_DEVICE_DESCRIPTOR構造体
// https://learn.microsoft.com/ja-jp/windows/win32/api/winioctl/ns-winioctl-storage_device_descriptor
// https://learn.microsoft.com/ja-jp/windows/win32/api/winioctl/ne-winioctl-storage_bus_type
STORAGE_DEVICE_DESCRIPTOR = MemoryLayout.structLayout (
ValueLayout.JAVA_INT.withName ("Version"), //0 DWORD Version
ValueLayout.JAVA_INT.withName ("Size"), //4 DWORD Size
ValueLayout.JAVA_BYTE.withName ("DeviceType"), //8 BYTE DeviceType
ValueLayout.JAVA_BYTE.withName ("DeviceTypeModifier"), //9 BYTE DeviceTypeModifier
ValueLayout.JAVA_BYTE.withName ("RemovableMedia"), //10 BOOLEAN RemovableMedia
ValueLayout.JAVA_BYTE.withName ("CommandQueueing"), //11 BOOLEAN CommandQueueing
ValueLayout.JAVA_INT.withName ("VendorIdOffset"), //12 DWORD VendorIdOffset
ValueLayout.JAVA_INT.withName ("ProductIdOffset"), //16 DWORD ProductIdOffset
ValueLayout.JAVA_INT.withName ("ProductRevisionOffset"), //20 DWORD ProductRevisionOffset
ValueLayout.JAVA_INT.withName ("SerialNumberOffset"), //24 DWORD SerialNumberOffset
ValueLayout.JAVA_INT.withName ("BusType"), //28 STORAGE_BUS_TYPE BusType
ValueLayout.JAVA_INT.withName ("RawPropertiesLength"), //32 DWORD RawPropertiesLength
MemoryLayout.sequenceLayout (1, ValueLayout.JAVA_BYTE).withName ("RawDeviceProperties"), //36 BYTE RawDeviceProperties[1]
MemoryLayout.paddingLayout (3) //37
//40
);
//STORAGE_PROPERTY_QUERY構造体
// https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddstor/ni-ntddstor-ioctl_storage_query_property
// https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddstor/ns-ntddstor-_storage_property_query
// https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddstor/ne-ntddstor-storage_property_id
// https://learn.microsoft.com/ja-jp/windows-hardware/drivers/ddi/ntddstor/ne-ntddstor-_storage_query_type
STORAGE_PROPERTY_QUERY = MemoryLayout.structLayout (
ValueLayout.JAVA_INT.withName ("PropertyId"), //0 STORAGE_PROPERTY_ID PropertyId
ValueLayout.JAVA_INT.withName ("QueryType"), //4 STORAGE_QUERY_TYPE QueryType
MemoryLayout.sequenceLayout (1, ValueLayout.JAVA_BYTE).withName ("AdditionalParameters"), //8 UCHAR AdditionalParameters[1]
MemoryLayout.paddingLayout (3) //9
//12
);
//リンカ
linker = Linker.nativeLinker ();
//アリーナ
arena = Arena.ofAuto ();
//ライブラリ
SymbolLookup kernel32 = SymbolLookup.libraryLookup ("kernel32", arena);
//関数
try {
//CloseHandle関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle
CloseHandle = downcallHandle (
kernel32.findOrThrow ("CloseHandle"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT, //BOOL
ValueLayout.ADDRESS)); //HANDLE hObject
//CreateFileA関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-createfilea
CreateFileA = downcallHandle (
kernel32.findOrThrow ("CreateFileA"),
FunctionDescriptor.of (
ValueLayout.ADDRESS, //HANDLE
ValueLayout.ADDRESS, //LPCSTR lpFileName
ValueLayout.JAVA_INT, //DWORD dwDesiredAccess
ValueLayout.JAVA_INT, //DWORD dwShareMode
ValueLayout.ADDRESS, //LPSECURITY_ATTRIBUTES lpSecurityAttributes
ValueLayout.JAVA_INT, //DWORD dwCreationDisposition
ValueLayout.JAVA_INT, //DWORD dwFlagsAndAttributes
ValueLayout.ADDRESS)); //HANDLE hTemplateFile
//DeviceIoControl関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol
DeviceIoControl = downcallHandle (
kernel32.findOrThrow ("DeviceIoControl"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT, //BOOL
ValueLayout.ADDRESS, //HANDLE hDevice
ValueLayout.JAVA_INT, //DWORD dwIoControlCode
ValueLayout.ADDRESS, //LPVOID lpInBuffer
ValueLayout.JAVA_INT, //DWORD nInBufferSize
ValueLayout.ADDRESS, //LPVOID lpOutBuffer
ValueLayout.JAVA_INT, //DWORD nOutBufferSize
ValueLayout.ADDRESS, //LPDWORD lpBytesReturned
ValueLayout.ADDRESS)); //LPOVERLAPPED lpOverlapped
//GetDiskFreeSpaceA関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-getdiskfreespacea
GetDiskFreeSpaceA = downcallHandle (
kernel32.findOrThrow ("GetDiskFreeSpaceA"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT, //BOOL
ValueLayout.ADDRESS, //LPCSTR lpRootPathName
ValueLayout.ADDRESS, //LPDWORD lpSectorsPerCluster
ValueLayout.ADDRESS, //LPDWORD lpBytesPerSector
ValueLayout.ADDRESS, //LPDWORD lpNumberOfFreeClusters
ValueLayout.ADDRESS)); //LPDWORD lpTotalNumberOfClusters
//GetDriveTypeA関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-getdrivetypea
GetDriveTypeA = downcallHandle (
kernel32.findOrThrow ("GetDriveTypeA"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT, //UINT
ValueLayout.ADDRESS)); //LPCWSTR lpRootPathName
//GetLastError関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
GetLastError = downcallHandle (
kernel32.findOrThrow ("GetLastError"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT)); //DWORD
//GetLogicalDrives関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives
GetLogicalDrives = downcallHandle (
kernel32.findOrThrow ("GetLogicalDrives"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT)); //DWORD
//QueryDosDeviceA
// https://learn.microsoft.com/ja-jp/windows/win32/api/winbase/nf-winbase-querydosdevicea
QueryDosDeviceA = downcallHandle (
kernel32.findOrThrow ("QueryDosDeviceA"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT, //DWORD
ValueLayout.ADDRESS, //LPCSTR lpDeviceName
ValueLayout.ADDRESS, //LPSTR lpTargetPath
ValueLayout.JAVA_INT)); //DWORD ucchMax
//ReadFile関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-readfile
ReadFile = downcallHandle (
kernel32.findOrThrow ("ReadFile"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT, //BOOL
ValueLayout.ADDRESS, //HANDLE hFile
ValueLayout.ADDRESS, //LPVOID lpBuffer
ValueLayout.JAVA_INT, //DWORD nNumberOfBytesToRead
ValueLayout.ADDRESS, //LPDWORD lpNumberOfBytesRead
ValueLayout.ADDRESS)); //LPOVERLAPPED lpOverlapped
//SetFilePointerEx関数
// https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-setfilepointerex
SetFilePointerEx = downcallHandle (
kernel32.findOrThrow ("SetFilePointerEx"),
FunctionDescriptor.of (
ValueLayout.JAVA_INT, //BOOL
ValueLayout.ADDRESS, //HANDLE hFile
ValueLayout.JAVA_LONG, //LARGE_INTEGER liDistanceToMove
ValueLayout.ADDRESS, //PLARGE_INTEGER lpNewFilePointer
ValueLayout.JAVA_INT)); //DWORD dwMoveMethod
} catch (NoSuchElementException nsee) { //操作できない
if (hcdDebugInfo) {
nsee.printStackTrace ();
}
hcdAvailable = false;
hcdConnected = false;
return;
}
//CD-ROMドライブを探す
hcdDriveLetter = 0;
hcdRootPath = null;
hcdDevicePath = null;
hcdDeviceName = null;
try {
int error;
int logicalDrives = 0;
if ((logicalDrives = (int) GetLogicalDrives.invoke ()) == 0 &&
(error = (int) GetLastError.invoke ()) != -1) {
if (hcdDebugInfo) {
System.out.printf ("GetLogicalDrives returned error %d\n",
error);
}
hcdAvailable = false;
hcdConnected = false;
return;
}
for (int driveLetter = 'A'; driveLetter <= 'Z'; driveLetter++) {
if ((logicalDrives & (1 << (driveLetter - 'A'))) != 0) {
String rootPath = String.format ("%c:\\", driveLetter);
if ((int) GetDriveTypeA.invoke (
arena.allocateFrom (rootPath)) == DRIVE_CDROM) { //LPCWSTR lpRootPathName
hcdDriveLetter = driveLetter;
hcdRootPath = rootPath;
break;
}
}
} //for
if (hcdDriveLetter == 0) { //CD-ROMドライブが見つからない
if (hcdDebugInfo) {
System.out.println ("CD-ROM drive not found");
}
hcdAvailable = false;
hcdConnected = false;
return;
}
hcdDevicePath = String.format ("\\\\.\\%c:", hcdDriveLetter);
hcdDeviceName = String.format ("%c:", hcdDriveLetter);
if (hcdDebugInfo) {
System.out.printf ("CD-ROM drive is %s\n",
hcdDeviceName);
}
//ベンダー、プロダクト、リビジョンの取得を試みる
// メディアが入っていない場合があることに注意する
MemorySegment handle;
if ((handle = (MemorySegment) CreateFileA.invoke (
arena.allocateFrom (hcdDevicePath), //LPCSTR lpFileName
0, //DWORD dwDesiredAccess
FILE_SHARE_READ | FILE_SHARE_WRITE, //DWORD dwShareMode
MemorySegment.NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes
OPEN_EXISTING, //DWORD dwCreationDisposition
0, //DWORD dwFlagsAndAttributes
MemorySegment.NULL)).address () == INVALID_HANDLE_VALUE && //hcdHandle hTemplateFile
(error = (int) GetLastError.invoke ()) != -1) {
if (hcdDebugInfo) {
System.out.printf ("CreateFileA returned error %d\n",
error);
}
} else {
MemorySegment query = arena.allocate (STORAGE_PROPERTY_QUERY);
MemorySegment descriptor = arena.allocate (1024); //STORAGE_DEVICE_DESCRIPTOR
MemorySegment bytesReturned = arena.allocate (ValueLayout.JAVA_INT);
query.set (ValueLayout.JAVA_INT,
STORAGE_PROPERTY_QUERY.byteOffset (MemoryLayout.PathElement.groupElement ("PropertyId")),
StorageDeviceProperty);
query.set (ValueLayout.JAVA_INT,
STORAGE_PROPERTY_QUERY.byteOffset (MemoryLayout.PathElement.groupElement ("QueryType")),
PropertyStandardQuery);
if ((int) DeviceIoControl.invoke (
handle, //hcdHandle hDevice
IOCTL_STORAGE_QUERY_PROPERTY, //DWORD dwIoControlCode
query, //LPVOID lpInBuffer
(int) query.byteSize (), //DWORD nInBufferSize
descriptor, //LPVOID lpOutBuffer
(int) descriptor.byteSize (), //DWORD nOutBufferSize
bytesReturned, //LPDWORD lpBytesReturned
MemorySegment.NULL) == 0 && //LPOVERLAPPED lpOverlapped
(error = (int) GetLastError.invoke ()) != -1) {
if (hcdDebugInfo) {
System.out.printf ("DeviceIoControl IOCTL_STORAGE_QUERY_PROPERTY returned error %d\n",
error);
}
} else {
hcdVendorProduct = new byte[8 + 16 + 4];
Arrays.fill (hcdVendorProduct, (byte) ' ');
for (int k = 0; k < 3; k++) {
int o = descriptor.get (ValueLayout.JAVA_INT,
STORAGE_DEVICE_DESCRIPTOR.byteOffset (MemoryLayout.PathElement.groupElement (
k == 0 ? "VendorIdOffset" :
k == 1 ? "ProductIdOffset" :
"ProductRevisionOffset"))); //入力開始位置
if (o != 0) {
int p = k == 0 ? 0 : k == 1 ? 8 : 8 + 16; //出力開始位置
int l = k == 0 ? 8 : k == 1 ? 16 : 4; //長さ
for (int i = 0; i < l; i++) {
int c = 0xff & descriptor.get (ValueLayout.JAVA_BYTE, o++);
if (c == 0) {
break;
}
hcdVendorProduct[p++] = (byte) (0x20 <= c && c <= 0x7e ? c : '?');
}
}
}
if (hcdDebugInfo) {
System.out.print ("VendorProduct is ");
for (int i = 0; i < 8 + 16 + 4; i++) {
System.out.printf ("%c", 0xff & hcdVendorProduct[i]);
}
System.out.println ();
int BusType = descriptor.get (ValueLayout.JAVA_INT,
STORAGE_DEVICE_DESCRIPTOR.byteOffset (MemoryLayout.PathElement.groupElement ("BusType")));
System.out.printf ("BusType is %s\n",
BusType == BusTypeUnknown ? "BusTypeUnknown" :
BusType == BusTypeScsi ? "BusTypeScsi" :
BusType == BusTypeAtapi ? "BusTypeAtapi" :
BusType == BusTypeAta ? "BusTypeAta" :
BusType == BusType1394 ? "BusType1394" :
BusType == BusTypeSsa ? "BusTypeSsa" :
BusType == BusTypeFibre ? "BusTypeFibre" :
BusType == BusTypeUsb ? "BusTypeUsb" :
BusType == BusTypeRAID ? "BusTypeRAID" :
BusType == BusTypeiScsi ? "BusTypeiScsi" :
BusType == BusTypeSas ? "BusTypeSas" :
BusType == BusTypeSata ? "BusTypeSata" :
BusType == BusTypeSd ? "BusTypeSd" :
BusType == BusTypeMmc ? "BusTypeMmc" :
BusType == BusTypeVirtual ? "BusTypeVirtual" :
BusType == BusTypeFileBackedVirtual ? "BusTypeFileBackedVirtual" :
BusType == BusTypeSpaces ? "BusTypeSpaces" :
BusType == BusTypeNvme ? "BusTypeNvme" :
BusType == BusTypeSCM ? "BusTypeSCM" :
BusType == BusTypeUfs ? "BusTypeUfs" :
BusType == BusTypeNvmeof ? "BusTypeNvmeof" :
BusType == BusTypeMax ? "BusTypeMax" :
BusType == BusTypeMaxReserved ? "BusTypeMaxReserved" :
String.valueOf (BusType));
}
}
CloseHandle.invoke (handle);
}
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
hcdAvailable = false;
hcdConnected = false;
return;
}
hcdMenu.setEnabled (true);
//動作中フラグ
hcdRunning = true;
//メモリセグメント
hcdReadTocEx = arena.allocate (CDROM_READ_TOC_EX);
hcdToc = arena.allocate (CDROM_TOC);
hcdBytesReturned = arena.allocate (ValueLayout.JAVA_INT);
hcdBufferSegment = arena.allocate (HCD_PLAY_BYTES);
hcdReadInfo = arena.allocate (RAW_READ_INFO);
//ハンドル
hcdHandle = null;
//ソースデータライン
hcdSourceDataLine = null;
try {
AudioFormat audioFormat = new AudioFormat (44100F, //sampleRate
16, //sampleSizeInBits
2, //channels
true, //signed
false); //bigEndian
hcdSourceDataLine = AudioSystem.getSourceDataLine (audioFormat);
hcdSourceDataLine.open (audioFormat, HCD_PLAY_BYTES * 2);
hcdSourceDataLine.start ();
} catch (LineUnavailableException lue) {
if (hcdDebugInfo) {
lue.printStackTrace ();
}
}
//再生キュー
hcdPlayQueueArray = new byte[HCD_PLAY_QUEUE_SIZE][];
for (int i = 0; i < HCD_PLAY_QUEUE_SIZE; i++) {
hcdPlayQueueArray[i] = new byte[HCD_PLAY_BYTES];
}
hcdPlayQueueWrite = 0;
hcdPlayQueueRead = 0;
//読み出しスレッド
// 動作中のとき繰り返す
// コマンドがあるとき
// コマンドを実行する
// 開いていないか再生中でないか中断中か最後まで読み出したかキューが満杯のとき
// 200ms待つ
// さもなくば
// 400msぶんのデータを読み出してキューに追加する
hcdReadThread = new Thread (() -> {
while (hcdRunning) { //動作中のとき繰り返す
if (hcdCompleted != hcdRequested) { //完了カウンタ!=予約カウンタのとき
hcdCommandComplete_2nd (); //コマンドを実行する
hcdCompleted = hcdRequested;
} else if (hcdHandle == null || //開いていないか
!hcdPlaying || //再生中でないか
hcdPausing || //中断中か
hcdEndSector <= hcdCurrentSector || //最後まで読み出したか
(hcdPlayQueueWrite - hcdPlayQueueRead) == HCD_PLAY_QUEUE_SIZE) { //キューが満杯のとき
try {
Thread.sleep ((long) (HCD_PLAY_MILLIS / 2)); //200ms待つ
} catch (InterruptedException ie) {
}
} else { //さもなくば
//400msぶんのデータを読み出してキューに追加する
byte[] buffer = hcdPlayQueueArray[hcdPlayQueueWrite & (HCD_PLAY_QUEUE_SIZE - 1)];
int sectors = Math.min (HCD_PLAY_SECTORS, hcdEndSector - hcdCurrentSector); //今回読み込むセクタ数
hcdReadInfo.set (ValueLayout.JAVA_LONG,
RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("DiskOffset")),
2048L * (long) hcdCurrentSector);
hcdReadInfo.set (ValueLayout.JAVA_INT,
RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("SectorCount")),
sectors);
hcdReadInfo.set (ValueLayout.JAVA_INT,
RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("TrackMode")),
CDDA);
try {
int error;
if ((int) DeviceIoControl.invoke (
hcdHandle, //hcdHandle hDevice
IOCTL_CDROM_RAW_READ, //DWORD dwIoControlCode
hcdReadInfo, //LPVOID lpInBuffer
(int) hcdReadInfo.byteSize (), //DWORD nInBufferSize
hcdBufferSegment, //LPVOID lpOutBuffer
2352 * sectors, //DWORD nOutBufferSize
hcdBytesReturned, //LPDWORD lpBytesReturned
MemorySegment.NULL) == 0 && //LPOVERLAPPED lpOverlapped
(error = (int) GetLastError.invoke ()) != -1) { //読めない
if (hcdDebugInfo) {
System.out.printf ("DeviceIoControl IOCTL_CDROM_RAW_READ returned error %d\n",
error);
}
hcdPlaying = false;
hcdAudioStatus = 0x14; //エラー終了
Arrays.fill (buffer, (byte) 0);
}
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
hcdPlaying = false;
hcdAudioStatus = 0x14; //エラー終了
Arrays.fill (buffer, (byte) 0);
}
//byte[] array = hcdBufferSegment.toArray (ValueLayout.JAVA_BYTE); //毎回必要。メモリ消費が激しい
for (int i = 0; i < 588 * sectors; i++) {
//int l = array[4 * i + 1] << 8 | (0xff & array[4 * i + 0]);
//int r = array[4 * i + 3] << 8 | (0xff & array[4 * i + 2]);
int l = ((int) hcdBufferSegment.get (ValueLayout.JAVA_BYTE, 4 * i + 1) << 8 |
(0xff & (int) hcdBufferSegment.get (ValueLayout.JAVA_BYTE, 4 * i + 0)));
int r = ((int) hcdBufferSegment.get (ValueLayout.JAVA_BYTE, 4 * i + 3) << 8 |
(0xff & (int) hcdBufferSegment.get (ValueLayout.JAVA_BYTE, 4 * i + 2)));
l = Math.max (-32768, Math.min (32767, Math.round ((float) l * hcdVolumeFloat)));
r = Math.max (-32768, Math.min (32767, Math.round ((float) r * hcdVolumeFloat)));
buffer[4 * i + 0] = (byte) l;
buffer[4 * i + 1] = (byte) (l >> 8);
buffer[4 * i + 2] = (byte) r;
buffer[4 * i + 3] = (byte) (r >> 8);
}
if (sectors < HCD_PLAY_SECTORS) {
Arrays.fill (buffer,
2352 * sectors, //from
HCD_PLAY_BYTES, //to
(byte) 0);
}
hcdCurrentSector += sectors;
hcdPlayQueueWrite++;
}
} //while
});
hcdReadThread.start ();
//再生スレッド
// 終了するまで繰り返す
// 再生中でないか中断中か4周目以前でキューが空のとき
// 200ms待つ
// 5周目以後でキューが空のとき
// エラー終了
// ソースデータラインが使用できないとき
// 400msぶんのデータをキューから取り出して400ms待つ
// さもなくば
// 400msぶんのデータをキューから取り出して再生する。ブロックする
// 最後まで再生したとき
// 正常終了
hcdPlayThread = new Thread (() -> {
while (hcdRunning) { //終了するまで繰り返す
if (!hcdPlaying || //再生中でないか
hcdPausing || //中断中か
(((hcdCurrentSector - hcdStartSector) < HCD_PLAY_SECTORS * HCD_PLAY_QUEUE_SIZE * 2) && //4周目以前で
hcdPlayQueueRead == hcdPlayQueueWrite)) { //キューが空のとき
try {
Thread.sleep ((long) (HCD_PLAY_MILLIS / 2)); //200ms待つ
} catch (InterruptedException ie) {
}
} else if (hcdPlayQueueRead == hcdPlayQueueWrite) { //5周目以後でキューが空のとき
hcdPlaying = false;
hcdAudioStatus = 0x14; //エラー終了
} else if (hcdSourceDataLine == null) { //ソースデータラインが使用できないとき
//400msぶんのデータをキューから取り出して400ms待つ
hcdPlayQueueRead++;
try {
Thread.sleep ((long) (HCD_PLAY_MILLIS));
} catch (InterruptedException ie) {
}
} else { //さもなくば
//400msぶんのデータをキューから取り出して再生する。ブロックする
hcdSourceDataLine.write (hcdPlayQueueArray[hcdPlayQueueRead & (HCD_PLAY_QUEUE_SIZE - 1)], 0, HCD_PLAY_BYTES);
hcdPlayQueueRead++;
if (hcdEndSector <= hcdCurrentSector) { //最後まで再生したとき
hcdPlaying = false;
hcdAudioStatus = 0x13; //正常終了
}
}
} //while
});
hcdPlayThread.start ();
} //hcdInit
//hcdTini ()
// 後始末
public static void hcdTini () {
//動作中フラグ
hcdRunning = false;
//読み出しスレッド
if (hcdReadThread != null) {
hcdReadThread.interrupt ();
try {
hcdReadThread.join ((long) (HCD_PLAY_MILLIS * 2));
} catch (InterruptedException ie) {
}
hcdReadThread = null;
}
//再生スレッド
if (hcdPlayThread != null) {
hcdPlayThread.interrupt ();
try {
hcdPlayThread.join ((long) (HCD_PLAY_MILLIS * 2));
} catch (InterruptedException ie) {
}
hcdPlayThread = null;
}
//ソースデータライン
if (hcdSourceDataLine != null) {
hcdSourceDataLine.stop ();
hcdSourceDataLine.close ();
hcdSourceDataLine = null;
}
//パラメータ
Settings.sgsPutOnOff ("hcddebug", hcdDebugInfo);
Settings.sgsPutOnOff ("hcdconnect", hcdConnectNext);
Settings.sgsPutInt ("hcdscsiid", hcdSCSIIdNext);
Settings.sgsPutInt ("hcdvolume", hcdVolumeInt);
} //hcdTini
//hcdReset ()
// リセット
public static void hcdReset () {
if (hcdConnected) {
hcdRequested = 0;
hcdCompleted = 0;
hcdRetrieved = 0;
hcdTOCAddressArray = null;
hcdDataOffset = -1;
hcdUnit = null;
hcdChip = null;
hcdResultBuffer = null;
hcdResultLength = 0;
hcdResultSense0 = 0;
hcdResultSense2 = 0;
hcdResultStatus = SPC.SPC_GOOD;
hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
hcdBytesPerSector = 2048;
hcdPlaying = false;
hcdPausing = false;
hcdAudioStatus = 0x15; //情報なし
}
} //hcdReset
//success = hcdOpen ()
// 開く
static boolean hcdOpen () {
if (hcdHandle != null) { //すでに開いている
return true;
}
if (hcdRootPath == null) { //CD-ROMドライブがない
return false;
}
try {
int error;
//挿入されているか
MemorySegment sectorsPerCluster = arena.allocate (ValueLayout.JAVA_INT);
MemorySegment bytesPerSector = arena.allocate (ValueLayout.JAVA_INT);
MemorySegment numberOfFreeClusters = arena.allocate (ValueLayout.JAVA_INT);
MemorySegment totalNumberOfClusters = arena.allocate (ValueLayout.JAVA_INT);
if ((int) GetDiskFreeSpaceA.invoke (
arena.allocateFrom (hcdRootPath), //LPCSTR lpRootPathName
sectorsPerCluster, //LPDWORD lpSectorsPerCluster
bytesPerSector, //LPDWORD lpBytesPerSector
numberOfFreeClusters, //LPDWORD lpNumberOfFreeClusters
totalNumberOfClusters) == 0 && //LPDWORD lpTotalNumberOfClusters
(error = (int) GetLastError.invoke ()) != -1) {
if (hcdDebugInfo) {
System.out.printf ("GetDiskFreeSpaceA returned error %d\n",
error);
}
return false;
}
if (hcdDebugInfo) {
System.out.printf ("sectorsPerCluster=%d\n",
sectorsPerCluster.get (ValueLayout.JAVA_INT, 0));
System.out.printf ("bytesPerSector=%d\n",
bytesPerSector.get (ValueLayout.JAVA_INT, 0));
System.out.printf ("numberOfFreeClusters=%d\n",
numberOfFreeClusters.get (ValueLayout.JAVA_INT, 0));
System.out.printf ("totalNumberOfClusters=%d\n",
totalNumberOfClusters.get (ValueLayout.JAVA_INT, 0));
}
//開く
if ((hcdHandle = (MemorySegment) CreateFileA.invoke (
arena.allocateFrom (hcdDevicePath), //LPCSTR lpFileName
GENERIC_READ, //DWORD dwDesiredAccess
FILE_SHARE_READ | FILE_SHARE_WRITE, //DWORD dwShareMode
MemorySegment.NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes
OPEN_EXISTING, //DWORD dwCreationDisposition
0, //DWORD dwFlagsAndAttributes
MemorySegment.NULL)).address () == INVALID_HANDLE_VALUE && //hcdHandle hTemplateFile
(error = (int) GetLastError.invoke ()) != -1) { //開けない
if (hcdDebugInfo) {
System.out.printf ("CreateFileA returned error %d\n",
error);
}
hcdHandle = null;
return false;
}
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
hcdHandle = null;
return false;
}
return true;
} //hcdOpen
//hcdClose ()
// 閉じる
static void hcdClose () {
if (hcdHandle == null) { //開いていない
return;
}
try {
CloseHandle.invoke (hcdHandle);
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
}
hcdHandle = null;
hcdTOCAddressArray = null;
hcdDataOffset = -1;
} //hcdClose
//buffer = hcdReadTOC (msf)
// TOCを読む
static byte[] hcdReadTOC (boolean msf) {
if (!hcdOpen ()) { //開けない
return null;
}
try {
int error;
hcdReadTocEx.set (ValueLayout.JAVA_BYTE,
CDROM_READ_TOC_EX.byteOffset (MemoryLayout.PathElement.groupElement ("Msf_Reserved1_Format")),
(byte) ((msf ? 1 : 0) << 7 | //Msf
0 << 4 | //Reserved1
CDROM_READ_TOC_EX_FORMAT_TOC)); //Format
hcdReadTocEx.set (ValueLayout.JAVA_BYTE,
CDROM_READ_TOC_EX.byteOffset (MemoryLayout.PathElement.groupElement ("SessionTrack")),
(byte) 1);
hcdReadTocEx.set (ValueLayout.JAVA_BYTE,
CDROM_READ_TOC_EX.byteOffset (MemoryLayout.PathElement.groupElement ("Reserved2")),
(byte) 0);
hcdReadTocEx.set (ValueLayout.JAVA_BYTE,
CDROM_READ_TOC_EX.byteOffset (MemoryLayout.PathElement.groupElement ("Reserved3")),
(byte) 0);
if ((int) DeviceIoControl.invoke (
hcdHandle, //hcdHandle hDevice
IOCTL_CDROM_READ_TOC_EX, //DWORD dwIoControlCode
hcdReadTocEx, //LPVOID lpInBuffer
(int) hcdReadTocEx.byteSize (), //DWORD nInBufferSize
hcdToc, //LPVOID lpOutBuffer
(int) hcdToc.byteSize (), //DWORD nOutBufferSize
hcdBytesReturned, //LPDWORD lpBytesReturned
MemorySegment.NULL) == 0 && //LPOVERLAPPED lpOverlapped
(error = (int) GetLastError.invoke ()) != -1) { //読めない
if (hcdDebugInfo) {
System.out.printf ("DeviceIoControl IOCTL_CDROM_READ_TOC_EX returned error %d\n",
error);
}
hcdClose ();
return (error == 0 ||
error == ERROR_NOT_READY ||
error == ERROR_WRONG_DISK ? null :
new byte[0]);
}
int Length_0 = (0xff & (int) (hcdToc.get (ValueLayout.JAVA_BYTE,
CDROM_TOC.byteOffset (MemoryLayout.PathElement.groupElement ("Length"),
MemoryLayout.PathElement.sequenceElement (0)))));
int Length_1 = (0xff & (int) (hcdToc.get (ValueLayout.JAVA_BYTE,
CDROM_TOC.byteOffset (MemoryLayout.PathElement.groupElement ("Length"),
MemoryLayout.PathElement.sequenceElement (1)))));
int Length = 256 * Length_0 + Length_1;
byte[] buffer = new byte[2 + Length];
for (int i = 0; i < buffer.length; i++) {
buffer[i] = hcdToc.get (ValueLayout.JAVA_BYTE, (long) i);
}
if (false) {
for (int i = 0; i < buffer.length; i++) {
System.out.printf ("%02x ", 0xff & buffer[i]);
}
System.out.println ();
}
return buffer;
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
return null;
}
} //hcdReadTOC
//hcdPlay (startSector, endSector)
// 再生を開始する
static void hcdPlay (int startSector, int endSector) {
if (!hcdPlaying) { //再生していない
if (!hcdOpen ()) { //開けない
return;
}
}
//すでに再生しているときは上書きする
//中断中のデータがキューに残っていると上書き後に再生されてしまうので読み出し位置を進める
hcdPlayQueueRead = hcdPlayQueueWrite;
hcdStartSector = startSector;
hcdCurrentSector = startSector;
hcdEndSector = endSector;
hcdPlaying = true;
hcdPausing = false;
hcdAudioStatus = 0x11; //再生中
} //hcdPlay
//hcdPause ()
// 再生を中断する
static void hcdPause () {
if (!hcdPlaying) { //再生していない
return;
}
hcdPausing = true;
hcdAudioStatus = 0x12; //中断中
} //hcdPause
//hcdResume ()
// 再生を再開する
static void hcdResume () {
if (!hcdPlaying) { //再生していない
return;
}
hcdStartSector = hcdCurrentSector; //再開した位置を開始時刻にする。1周目からやり直さないとアンダーランで停止してしまう
hcdPausing = false;
hcdAudioStatus = 0x11; //再生中
} //hcdResume
//hcdSetBytesPerSector (bytesPerSector)
// セクタの長さを設定する
static void hcdSetBytesPerSector (int bytesPerSector) {
if (bytesPerSector == 2048 ||
bytesPerSector == 2336 ||
bytesPerSector == 2352) {
hcdBytesPerSector = bytesPerSector;
if (hcdDebugInfo) {
System.out.printf ("bytesPerSector=%d\n", bytesPerSector);
}
}
} //hcdSetBytesPerSector
//buffer = hcdRead (startSector, sectors)
// セクタを読む
static byte[] hcdRead (int startSector, int sectors) {
if (hcdDebugInfo) {
System.out.printf ("hcdRead(%d,%d)\n", startSector, sectors);
}
if (!hcdOpen ()) { //開けない
return null;
}
byte[] buffer = new byte[hcdBytesPerSector * sectors];
try {
int error;
if (hcdBytesPerSector == 2048) {
if ((int) SetFilePointerEx.invoke (
hcdHandle, //HANDLE hFile
2048L * (long) startSector, //LARGE_INTEGER liDistanceToMove
MemorySegment.NULL, //PLARGE_INTEGER lpNewFilePointer
FILE_BEGIN) == 0 && //DWORD dwMoveMethod
(error = (int) GetLastError.invoke ()) != -1) { //シークできない
if (hcdDebugInfo) {
System.out.printf ("SetFilePointerEx returned error %d\n",
error);
}
hcdClose ();
return (error == 0 ||
error == ERROR_NOT_READY ||
error == ERROR_WRONG_DISK ? null :
new byte[0]);
}
}
for (int offset = 0; offset < sectors; ) { //今回読む位置
int step = Math.min (HCD_PLAY_SECTORS, sectors - offset); //今回読むセクタ数
if (hcdBytesPerSector == 2048) {
if ((int) ReadFile.invoke (
hcdHandle, //HANDLE hFile
hcdBufferSegment, //LPVOID lpBuffer
2048 * step, //DWORD nNumberOfBytesToRead
hcdBytesReturned, //LPDWORD lpNumberOfBytesRead
MemorySegment.NULL) == 0 && //LPOVERLAPPED lpOverlapped
(error = (int) GetLastError.invoke ()) != -1) { //読めない
if (hcdDebugInfo) {
System.out.printf ("ReadFile returned error %d\n",
error);
}
hcdClose ();
return (error == 0 ||
error == ERROR_NOT_READY ||
error == ERROR_WRONG_DISK ? null :
new byte[0]);
}
} else {
hcdReadInfo.set (ValueLayout.JAVA_LONG,
RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("DiskOffset")),
2048L * (long) (startSector + offset)); //開始位置。2048L以外はエラー87 ERROR_INVALID_PARAMETER
hcdReadInfo.set (ValueLayout.JAVA_INT,
RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("SectorCount")),
step); //セクタ数
hcdReadInfo.set (ValueLayout.JAVA_INT,
RAW_READ_INFO.byteOffset (MemoryLayout.PathElement.groupElement ("TrackMode")),
hcdBytesPerSector == 2336 ? YellowMode2 : CDDA); //モード。手元の環境ではYellowMode2はエラー87 ERROR_INVALID_PARAMETER
if ((int) DeviceIoControl.invoke (
hcdHandle, //hcdHandle hDevice
IOCTL_CDROM_RAW_READ, //DWORD dwIoControlCode
hcdReadInfo, //LPVOID lpInBuffer
(int) hcdReadInfo.byteSize (), //DWORD nInBufferSize
hcdBufferSegment, //LPVOID lpOutBuffer
hcdBytesPerSector * step, //DWORD nOutBufferSize
hcdBytesReturned, //LPDWORD lpBytesReturned
MemorySegment.NULL) == 0 && //LPOVERLAPPED lpOverlapped
(error = (int) GetLastError.invoke ()) != -1) { //読めない
if (hcdDebugInfo) {
System.out.printf ("DeviceIoControl IOCTL_CDROM_RAW_READ returned error %d\n",
error);
}
hcdClose ();
return (error == 0 ||
error == ERROR_NOT_READY ||
error == ERROR_WRONG_DISK ? null :
new byte[0]);
}
}
for (int i = 0; i < hcdBytesPerSector * step; i++) {
buffer[hcdBytesPerSector * offset + i] = hcdBufferSegment.get (ValueLayout.JAVA_BYTE, i);
}
offset += step;
} //for offset
return buffer;
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
return null;
}
} //hcdRead
//buffer = hcdReadCapacity ()
static byte[] hcdReadCapacity () {
if (!hcdOpen ()) { //開けない
return null;
}
try {
int error;
MemorySegment sectorsPerCluster = arena.allocate (ValueLayout.JAVA_INT);
MemorySegment bytesPerSector = arena.allocate (ValueLayout.JAVA_INT);
MemorySegment numberOfFreeClusters = arena.allocate (ValueLayout.JAVA_INT);
MemorySegment totalNumberOfClusters = arena.allocate (ValueLayout.JAVA_INT);
if ((int) GetDiskFreeSpaceA.invoke (
arena.allocateFrom (hcdRootPath), //LPCSTR lpRootPathName
sectorsPerCluster, //LPDWORD lpSectorsPerCluster
bytesPerSector, //LPDWORD lpBytesPerSector
numberOfFreeClusters, //LPDWORD lpNumberOfFreeClusters
totalNumberOfClusters) == 0 && //LPDWORD lpTotalNumberOfClusters
(error = (int) GetLastError.invoke ()) != -1) {
if (hcdDebugInfo) {
System.out.printf ("GetDiskFreeSpaceA returned error %d\n",
error);
}
hcdClose ();
return (error == 0 ||
error == ERROR_NOT_READY ||
error == ERROR_WRONG_DISK ? null :
new byte[0]);
}
byte[] buffer = new byte[8];
ByteArray.byaWl (buffer,
0,
sectorsPerCluster.get (ValueLayout.JAVA_INT, 0) *
totalNumberOfClusters.get (ValueLayout.JAVA_INT, 0) - 1); //最終論理ブロック
ByteArray.byaWl (buffer,
4,
bytesPerSector.get (ValueLayout.JAVA_INT, 0)); //ブロック長
return buffer;
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
return null;
}
} //hcdReadCapacity
//hcdEject ()
// イジェクトする
//!!!メディアがないときトレイを出すか出さないか
static void hcdEject () {
hcdClose ();
try {
int error;
MemorySegment handle;
if ((handle = (MemorySegment) CreateFileA.invoke (
arena.allocateFrom (hcdDevicePath), //LPCSTR lpFileName
0, //DWORD dwDesiredAccess
FILE_SHARE_READ | FILE_SHARE_WRITE, //DWORD dwShareMode
MemorySegment.NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes
OPEN_EXISTING, //DWORD dwCreationDisposition
0, //DWORD dwFlagsAndAttributes
MemorySegment.NULL)).address () == INVALID_HANDLE_VALUE && //hcdHandle hTemplateFile
(error = (int) GetLastError.invoke ()) != -1) {
if (hcdDebugInfo) {
System.out.printf ("CreateFileA returned error %d\n",
error);
}
return;
}
MemorySegment bytesReturned = arena.allocate (ValueLayout.JAVA_INT);
if ((int) DeviceIoControl.invoke (
handle, //hcdHandle hDevice
IOCTL_STORAGE_EJECT_MEDIA, //DWORD dwIoControlCode
MemorySegment.NULL, //LPVOID lpInBuffer
0, //DWORD nInBufferSize
MemorySegment.NULL, //LPVOID lpOutBuffer
0, //DWORD nOutBufferSize
bytesReturned, //LPDWORD lpBytesReturned
MemorySegment.NULL) == 0 && //LPOVERLAPPED lpOverlapped
(error = (int) GetLastError.invoke ()) != -1) {
if (hcdDebugInfo) {
System.out.printf ("DeviceIoControl IOCTL_STORAGE_EJECT_MEDIA returned error %d\n",
error);
}
}
CloseHandle.invoke (handle);
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
}
} //hcdEject
//hcdCommandComplete_1st (unit, oc)
// コマンドフェーズの転送が終了した
public static void hcdCommandComplete_1st (SPC.SCUnit unit, int oc) {
if (hcdDebugInfo) {
unit.scuPrintCommand (oc);
}
hcdUnit = unit;
hcdChip = unit.scuChip;
hcdResultBuffer = null;
hcdResultLength = 0;
hcdResultSense0 = 0;
hcdResultSense2 = 0;
hcdResultStatus = SPC.SPC_GOOD;
hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
if (!(oc == 0x03 || //Request Senseまたは
oc == 0x12) && //Inquiry以外で
hcdChip.spiLUN != 0) { //LUNが0でない
unit.scuNotReady ();
return;
}
switch (oc) {
//
//ステータスフェーズへ進むもの
//
case 0x01: //Rezero Unit
hcdDoRezeroUnit_1st ();
break;
case 0x03: //Request Sense
unit.scuDoRequestSense ();
break;
case 0x12: //Inquiry
hcdDoInquiry_1st ();
break;
case 0x1a: //Mode Sense(6)
hcdDoModeSense6_1st ();
break;
case 0x42: //Read Sub-Channel
hcdDoReadSubChannel_1st ();
break;
case 0x45: //Play Audio(10)
hcdDoPlayAudio10_1st ();
break;
case 0x47: //Play Audio MSF
hcdDoPlayAudioMSF_1st ();
break;
case 0x4b: //Pause Resume
hcdDoPauseResume_1st ();
break;
case 0xa5: //Play Audio(12)
hcdDoPlayAudio12_1st ();
break;
//
//データアウトフェーズへ進むもの
//
case 0x15: //Mode Select(6)
hcdDoModeSelect6_1st ();
break;
//
//読み出しスレッドで処理するもの
//
case 0x00: //Test Unit Ready
case 0x08: //Read(6)
case 0x1b: //Start-Stop Unit
case 0x25: //Read Capacity
case 0x28: //Read(10)
case 0x43: //Read TOC
case 0xd8: //ReadCDDA
hcdRequested++;
if (hcdReadThread != null) {
hcdReadThread.interrupt ();
}
break;
//
//未実装
//
default:
hcdUnit.scuDoInvalid ();
}
} //hcdCommandComplete_1st
//hcdCommandComplete_2nd ()
// コマンドフェーズの転送が終了した後、読み込みスレッドがコマンドを実行する
static void hcdCommandComplete_2nd () {
int oc = hcdChip.spiCommandBuffer[0] & 255; //オペレーションコード
switch (oc) {
//
//読み出しスレッドで処理するもの
//
case 0x00: //Test Unit Ready
hcdDoTestUnitReady_2nd ();
break;
case 0x08: //Read(6)
hcdDoRead6_2nd ();
break;
case 0x1b: //Start-Stop Unit
hcdDoStartStopUnit_2nd ();
break;
case 0x25: //Read Capacity
hcdDoReadCapacity_2nd ();
break;
case 0x28: //Read(10)
hcdDoRead10_2nd ();
break;
case 0x43: //Read TOC
hcdDoReadTOC_2nd ();
break;
case 0xd8: //ReadCDDA
hcdDoReadCDDA_2nd ();
break;
}
} //hcdCommandComplete_2nd
//hcdDoTestUnitReady_2nd ()
// [0] 0x00
// [1] |LUN###|-----|
// [5] |..|----|Flag|Link|
static void hcdDoTestUnitReady_2nd () {
if (!hcdOpen ()) { //開けない
hcdNotReady ();
return;
}
try {
int error;
MemorySegment bytesReturned = arena.allocate (ValueLayout.JAVA_INT);
if ((int) DeviceIoControl.invoke (
hcdHandle, //hcdHandle hDevice
IOCTL_STORAGE_CHECK_VERIFY, //DWORD dwIoControlCode
MemorySegment.NULL, //LPVOID lpInBuffer
0, //DWORD nInBufferSize
MemorySegment.NULL, //LPVOID lpOutBuffer
0, //DWORD nOutBufferSize
bytesReturned, //LPDWORD lpBytesReturned
MemorySegment.NULL) == 0 && //LPOVERLAPPED lpOverlapped
(error = (int) GetLastError.invoke ()) != -1) {
if (hcdDebugInfo) {
System.out.printf ("DeviceIoControl IOCTL_STORAGE_CHECK_VERIFY returned error %d\n",
error);
}
hcdNotReady ();
return;
}
} catch (Throwable e) { //操作できない
if (hcdDebugInfo) {
e.printStackTrace ();
}
hcdNotReady ();
return;
}
hcdGood ();
} //hcdDoTestUnitReady_2nd
//hcdDoRezeroUnit_1st ()
// [0] 0x01
// [1] |LUN###|-----|
// [5] |..|----|Flag|Link|
static void hcdDoRezeroUnit_1st () {
hcdRequested = 0;
hcdCompleted = 0;
hcdRetrieved = 0;
hcdBytesPerSector = 2048;
//Rezero Unitは再生を止めない
hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE); //エラーなしでステータスフェーズに移行する
} //hcdDoRezeroUnit_1st
//hcdDoRead6_2nd ()
// [0] 0x08
// [1][2][3] LUN<<21|論理ブロックアドレス
// [4] 論理ブロック数
// [5] |..|----|Flag|Link|
static void hcdDoRead6_2nd () {
if (!hcdOpen ()) { //開けない
hcdNotReady ();
return;
}
int a = ByteArray.byaRls (hcdChip.spiCommandBuffer, 0) & 0x001fffff; //論理ブロックアドレス
int n = hcdChip.spiCommandBuffer[4] & 255; //論理ブロック数
if (n == 0) {
n = 256;
}
byte[] buffer = hcdRead (a, n);
if (buffer == null) {
hcdNotReady ();
return;
}
if (buffer.length == 0) {
hcdMediumError ();
return;
}
//データインフェーズに移行する
hcdResultBuffer = buffer;
hcdResultLength = buffer.length;
} //hcdDoRead6_2nd
//hcdDoInquiry_1st ()
// [0] 0x12
// [1] |LUN###|----|EVPD|
// [2] ページコード
// [4] アロケーション長
// [5] |..|----|Flag|Link|
static void hcdDoInquiry_1st () {
boolean evpd = (hcdChip.spiCommandBuffer[1] & 1) != 0;
if (evpd) { //VPD情報
hcdUnit.scuIllegalRequest ();
return;
}
int n = hcdChip.spiCommandBuffer[4] & 255; //アロケーション長
byte[] buffer = new byte[36];
if (hcdChip.spiLUN != 0) { //LUNが0でない
buffer[0] = 0x7f; //指定されたロジカル・ユニットは存在しない
} else {
buffer[0] = (byte) (SPC.SPC_CDROM_DEVICE); //CD-ROMデバイス
buffer[1] = (byte) (1 << 7); //0=固定,1=リムーバブル
buffer[2] = 2; //ISO/ECMA/ANSIバージョン。SCSI-1/SCSI-2
buffer[3] = 1; //レスポンスデータ形式。SCSI-1/SCSI-2
buffer[4] = 31; //追加データ長
buffer[5] = 0; //予約
buffer[6] = 0; //予約
buffer[7] = 0; //サポート機能なし
System.arraycopy (hcdVendorProduct, 0,
buffer, 8,
8 + 16 + 4); //ベンダーID[4],プロダクトID[16],プロダクトリビジョン[4]
}
if (hcdDebugInfo) {
hcdDumpBuffer (buffer, buffer.length);
}
hcdChip.spiDataInPhase (buffer, 0, Math.min (n, buffer.length), 0); //データインフェーズに移行する
} //hcdDoInquiry_1st
//hcdDoModeSelect6_1st ()
// [0] 0x15
// [1] |LUN###|PF|---|SP|
// [4] パラメータリスト長
// [5] コントロールバイト
//
// パラメータ・リスト
// モード・パラメータ・ヘッダ
// ブロック・ディスクリプタ
// :
// パラメータ・ページ
// :
// モード・パラメータ・ヘッダ
// [0] モード・パラメータ長
// Mode Senseのとき(アロケーション長に関係なく)全パラメータリストの長さ。このフィールドを含まない
// Mode Selectのとき0x00
// [1] メディア・タイプ
// [2] デバイス固有パラメータ
// [3] ブロック・ディスクリプタ長
// ブロック・ディスクリプタのバイト数。0を含む8の倍数
// ブロック・ディスクリプタ
// [0] デンシティ・コード
// [1][2][3] ブロック数
// [5][6][7] ブロック長
// パラメータ・ページ
// [0] |PS|-|ページ・コード######|
// [1] ページ長n-1
// [2]~[n] モード・パラメータ
static void hcdDoModeSelect6_1st () {
int n = hcdChip.spiCommandBuffer[4] & 255; //パラメータリスト長
hcdChip.spiDataOutPhase (hcdChip.spiDataOutBuffer, 0, n, 0); //データアウトフェーズに移行する
} //hcdDoModeSelect6_1st
//hcdDoModeSense6_1st ()
// [0] 0x1a
// [1] |LUN###|R|DBD|---|
// [2] |PC##|ページコード######|
// [4] アロケーション長
// [5] |..|----|Flag|Link|
static void hcdDoModeSense6_1st () {
boolean dbd = (hcdChip.spiCommandBuffer[1] & 8) != 0;
int pc = (hcdChip.spiCommandBuffer[2] >> 6) & 3; //0=カレント値,1=変更可能,2=デフォルト値,3=セーブ値
if (pc == 3) { //セーブ値
hcdUnit.scuIllegalRequest ();
return;
}
int pageCode = hcdChip.spiCommandBuffer[2] & 63;
int n = hcdChip.spiCommandBuffer[4] & 255;
// 0x01 リード・エラー・リカバリ・パラメータ p253
// [0] |PS|-|ページコード0x01######|
// [1] ページ長0x06
// [2] エラー・リカバリ・パラメータ
// |--|TB|RC|-|PER|DTE|DCR|
// TB 回復できなくても転送する
// RC 回復が必要でも中断しない
// PER 回復したエラーを報告する
// DTE 回復した時点で終了する
// DCR ECCによる訂正を禁止する
// [3] リード・リトライ回数
// [4][5][6][7]
// 0x03 フォーマット・パラメータ p199
// 0x04 ドライブ・パラメータ p200
// 0x0d CD-ROMデバイス・パラメータ p255
// 0x0e CD-ROMオーディオ・コントロール・パラメータ p255
// 0x3f 全パラメータ・ページの報告
if (pageCode != 0x01 &&
pageCode != 0x03 &&
pageCode != 0x0e &&
pageCode != 0x31 &&
pageCode != 0x3f) {
hcdUnit.scuIllegalRequest ();
return;
}
byte[] buffer = new byte[256];
//モード・パラメータ・ヘッダ
buffer[0] = 3; //[0] モード・パラメータ長(Sense時のみ)
buffer[1] = 0x00; //[1] メディア・タイプ
buffer[2] = 0x00; //[2] デバイス固有パラメータ
buffer[3] = 0; //[3] ブロック・ディスクリプタ長
int length = 4;
if (!dbd) {
//ブロック・ディスクリプタ
if (pc == 0) { //カレント値
buffer[length + 6] = (byte) (hcdBytesPerSector >> 8);
buffer[length + 7] = (byte) hcdBytesPerSector; //[5][6][7] ブロック長
} else if (pc == 1) { //変更可能
buffer[length + 6] = -1;
buffer[length + 7] = -1; //[5][6][7] ブロック長
} else { //デフォルト値
buffer[length + 6] = (byte) (2048 >> 8);
buffer[length + 7] = (byte) 2048; //[5][6][7] ブロック長
}
buffer[0] += 8; //[0] モード・パラメータ長(Sense時のみ)
buffer[3] += 8; //[3] ブロック・ディスクリプタ長
length += 8;
}
if (pageCode == 0x01 || //リード・エラー・リカバリ・パラメータ
pageCode == 0x3f) { //全パラメータ・ページの報告
//リード・エラー・リカバリ・パラメータ
buffer[length + 0] = 0x01; //[0] ページ・コード
buffer[length + 1] = 6; //[1] ページ長
if (pc == 0) { //カレント値
buffer[length + 2] = 0x11; //[2] リード・エラー・リカバリ・パラメータ。エラー無視、訂正はCIRCのみ
buffer[length + 3] = 0x00; //[3] リード・リトライ回数
} else if (pc == 1) { //変更可能
buffer[length + 2] = 0; //[2] リード・エラー・リカバリ・パラメータ。エラー無視、訂正はCIRCのみ
buffer[length + 3] = 0; //[3] リード・リトライ回数
} else { //デフォルト値
buffer[length + 2] = 0x11; //[2] リード・エラー・リカバリ・パラメータ。エラー無視、訂正はCIRCのみ
buffer[length + 3] = 0x00; //[3] リード・リトライ回数
}
buffer[0] += 8; //[0] モード・パラメータ長(Sense時のみ)
length += 8;
}
if (pageCode == 0x03 || //フォーマット・パラメータ
pageCode == 0x3f) { //全パラメータ・ページの報告
buffer[length + 0] = 0x03; //[0] ページ・コード
buffer[length + 1] = 22; //[1] ページ長
if (pc == 0) { //カレント値
buffer[length + 20] = 0x20; //[20] RMB
} else if (pc == 1) { //変更可能
buffer[length + 20] = 0; //[20] RMB
} else { //デフォルト値
buffer[length + 20] = 0x20; //[20] RMB
}
buffer[0] += 24; //[0] モード・パラメータ長(Sense時のみ)
length += 24;
}
if (pageCode == 0x0e || //CD-ROMオーディオ・コントロール・パラメータ
pageCode == 0x3f) { //全パラメータ・ページの報告
buffer[length + 0] = 0x0e; //[0] ページ・コード
buffer[length + 1] = 14; //[1] ページ長
if (pc == 0) { //カレント値
buffer[length + 9] =
buffer[length + 11] =
buffer[length + 13] =
buffer[length + 15] = (byte) (hcdVolumeInt * 255 / 100); //[9][11][13][15] ボリューム
} else if (pc == 1) { //変更可能
buffer[length + 9] =
buffer[length + 11] =
buffer[length + 13] =
buffer[length + 15] = -1;
} else { //デフォルト値
buffer[length + 9] =
buffer[length + 11] =
buffer[length + 13] =
buffer[length + 15] = (byte) (HCD_DEFAULT_VOLUME * 255 / 100); //[9][11][13][15] ボリューム
}
length += 16;
}
if (pageCode == 0x31 || //転送速度
pageCode == 0x3f) { //全パラメータ・ページの報告
buffer[length + 0] = 0x31; //[0] ページ・コード
buffer[length + 1] = 2; //[1] ページ長
if (pc == 0) { //カレント値
buffer[length + 2] = 24; //[2] 転送速度
} else if (pc == 1) { //変更可能
buffer[length + 2] = 0; //[2] 転送速度
} else { //デフォルト値
buffer[length + 2] = 24; //[2] 転送速度
}
buffer[0] += 4; //[0] モード・パラメータ長(Sense時のみ)
length += 4;
}
if (hcdDebugInfo) {
hcdDumpBuffer (buffer, length);
}
hcdChip.spiDataInPhase (buffer, 0, Math.min (n, length), 0); //データインフェーズに移行する
} //hcdDoModeSense6_1st
//hcdDoStartStopUnit_2nd ()
// [0] 0x1b
// [1] |LUN###|-----|
// [4] |------|LoEj|Start|
// [5] |..|----|Flag|Link|
static void hcdDoStartStopUnit_2nd () {
int loejStart = hcdChip.spiCommandBuffer[4] & 3; //LoEj|Start
if (loejStart == 2) { //イジェクト
//!!!再生終了
hcdEject ();
}
hcdGood ();
} //hcdDoStartStopUnit_2nd
//hcdDoReadCapacity_2nd ()
// [0] 0x25
// [1] |LUN###|----|RelAdr|
// [2][3][4][5] 論理ブロックアドレス
// [8] |-------|PMI|
// [9] |..|----|Flag|Link|
static void hcdDoReadCapacity_2nd () {
if (!hcdOpen ()) { //開けない
hcdNotReady ();
return;
}
byte[] buffer = hcdReadCapacity ();
if (buffer == null) {
hcdNotReady ();
return;
}
if (buffer.length == 0) {
hcdMediumError ();
return;
}
//データインフェーズに移行する
hcdResultBuffer = buffer;
hcdResultLength = buffer.length;
} //hcdDoReadCapacity_2nd
//hcdDoRead10_2nd ()
// [0] 0x28
// [1] |LUN###|DPO|FUA|--|RelAdr|
// [2][3][4][5] 論理ブロックアドレス
// [7][8] 論理ブロック数
// [9] |..|----|Flag|Link|
static void hcdDoRead10_2nd () {
if (!hcdOpen ()) { //開けない
hcdNotReady ();
return;
}
int a = ByteArray.byaRls (hcdChip.spiCommandBuffer, 2); //論理ブロックアドレス
int n = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 7); //論理ブロック数
if (n == 0) {
hcdGood ();
return;
}
byte[] buffer = hcdRead (a, n);
if (buffer == null) {
hcdNotReady ();
return;
}
if (buffer.length == 0) {
hcdMediumError ();
return;
}
//データインフェーズに移行する
hcdResultBuffer = buffer;
hcdResultLength = buffer.length;
} //hcdDoRead10_2nd
//hcdDoReadSubChannel_1st ()
// [0] 0x42
// [1] |LUN###|---|MSF|-|
// [2] |-|SubQ|------|
// [3] サブチャネル・データ形式
// [6] トラック番号
// [7][8] アロケーション長
// [9] コントロール・バイト
static void hcdDoReadSubChannel_1st () {
boolean msf = (hcdChip.spiCommandBuffer[1] & 2) != 0;
boolean subQ = (hcdChip.spiCommandBuffer[2] & 64) != 0;
int type = hcdChip.spiCommandBuffer[3] & 255;
int n = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 7);
if (subQ &&
type == 0x01) { //カレント・ポジション
int currentSector = 0; //現在のセクタ
int currentTrack = hcdTOCFirstTrack; //現在のセクタのトラックの番号
int currentOffset = 0; //現在のセクタのトラックの先頭からのオフセット
if (hcdPlaying && //再生中
hcdTOCAddressArray != null) { //TOC情報あり
currentSector = hcdCurrentSector;
while (currentTrack + 1 <= hcdTOCLastTrack &&
hcdTOCAddressArray[currentTrack + 1 - hcdTOCFirstTrack] <= currentSector) {
currentTrack++;
}
currentOffset = currentSector - hcdTOCAddressArray[currentTrack - hcdTOCFirstTrack];
}
//
byte[] buffer = new byte[16];
buffer[1] = (byte) hcdAudioStatus; //オーディオ・ステータス
if (hcdAudioStatus == 0x13 || //正常終了または
hcdAudioStatus == 0x14) { //エラー終了のとき
hcdAudioStatus = 0x15; //情報なし
}
buffer[2] = 0; //サブチャネル・データ長
buffer[3] = 12;
buffer[3] = 0x01; //サブチャネル・データ形式
buffer[5] = 0x10; //ADR<<4|コントロール
buffer[6] = (byte) ((currentTrack / 10) << 4 |
(currentTrack % 10)); //トラック番号。BCD形式
buffer[7] = 0x01; //インデックス番号。BCD形式。!!!トラックの先頭のポーズ領域は0x00
if (msf) {
int t = currentSector + 75 * 2;
buffer[8] = 0; //アブソリュートCD-ROMアドレス
buffer[9] = (byte) (t / (75 * 60));
buffer[10] = (byte) ((t / 75) % 60);
buffer[11] = (byte) (t % 75);
buffer[12] = 0; //トラック相対CD-ROMアドレス
buffer[13] = (byte) (currentOffset / (75 * 60));
buffer[14] = (byte) ((currentOffset / 75) % 60);
buffer[15] = (byte) (currentOffset % 75);
} else {
buffer[8] = (byte) (currentSector >> 24); //アブソリュートCD-ROMアドレス
buffer[9] = (byte) (currentSector >> 16);
buffer[10] = (byte) (currentSector >> 8);
buffer[11] = (byte) currentSector;
buffer[12] = (byte) (currentOffset >> 24); //トラック相対CD-ROMアドレス
buffer[13] = (byte) (currentOffset >> 16);
buffer[14] = (byte) (currentOffset >> 8);
buffer[15] = (byte) currentOffset;
}
if (false) {
System.out.printf ("currentSector=%d\n", currentSector);
System.out.printf ("currentTrack=%d\n", currentTrack);
System.out.printf ("currentOffset=%d\n", currentOffset);
for (int i = 0; i < buffer.length; i++) {
System.out.printf ("%02x ", buffer[i] & 255);
}
System.out.println ();
}
if (hcdDebugInfo) {
hcdDumpBuffer (buffer, buffer.length);
}
hcdChip.spiDataInPhase (buffer, 0, Math.min (n, buffer.length), 0); //データインフェーズに移行する
} else {
hcdIllegalRequest ();
}
} //hcdDoReadSubChannel_1st
//hcdDoReadTOC_2nd ()
// [0] 0x43
// [1] |LUN###|---|MSF|-|
// [6] 開始トラック
// [7][8] アロケーション長
// [9] コントロールバイト
static void hcdDoReadTOC_2nd () {
boolean msf = (hcdChip.spiCommandBuffer[1] & 2) != 0; //true=MSFアドレス形式,false=論理ブロックアドレス形式
int startTrack = hcdChip.spiCommandBuffer[6] & 255; //開始トラック
if (startTrack == 0) {
startTrack = 1;
}
if (0xaa < startTrack) { //開始トラック番号が範囲外
hcdIllegalRequest ();
return;
}
int allocLength = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 7); //アロケーション長
byte[] buffer = hcdReadTOC (msf);
if (buffer == null) {
hcdNotReady ();
return;
}
if (buffer.length == 0) {
hcdMediumError ();
return;
}
//トラックの開始セクタを集める
// TrackData[0].TrackNumber==FirstTrack
// TrackData[LastTrack-FirstTrack].TrackNumber==LastTrack
// TrackData[LastTrack-FirstTrack+1].TrackNumber==170
hcdTOCAddressArray = new int[(buffer.length - 4) / 8]; //TrackDataのAddressの配列
hcdTOCFirstTrack = buffer[2] & 255;
hcdTOCLastTrack = buffer[3] & 255;
for (int i = 0; i < hcdTOCAddressArray.length; i++) {
hcdTOCAddressArray[i] = (msf ?
-75 * 2 +
(buffer[4 + 8 * i + 5] & 255) * (75 * 60) +
(buffer[4 + 8 * i + 6] & 255) * 75 +
(buffer[4 + 8 * i + 7] & 255) :
(buffer[4 + 8 * i + 4] & 255) << 24 |
(buffer[4 + 8 * i + 5] & 255) << 16 |
(buffer[4 + 8 * i + 6] & 255) << 8 |
(buffer[4 + 8 * i + 7] & 255));
}
//開始トラックに満たないデータトラックを詰める
int dataLength = buffer.length; //全体の長さ
int p = 4; //残すデータの開始位置
while (p < dataLength - 8 && //リードアウトトラックでなく
(0xff & buffer[p + 2]) < startTrack) { //開始トラックに満たない
p += 8;
}
if (4 < p) {
for (int i = 0; i < dataLength - p; i++) {
buffer[4 + i] = buffer[p + i];
}
dataLength -= p - 4;
ByteArray.byaWw (buffer, 0, dataLength - 2);
}
//データインフェーズに移行する
hcdResultBuffer = buffer;
hcdResultLength = Math.min (dataLength, allocLength);
} //hcdDoReadTOC_2nd
//hcdDoPlayAudio10_1st ()
// [0] 0x45
// [1] |LUN###|----|RelAdr|
// [2][3][4][5] 論理ブロック・アドレス
// [7][8] 転送データ長
// [9] コントロール・バイト
static void hcdDoPlayAudio10_1st () {
int start = ByteArray.byaRls (hcdChip.spiCommandBuffer, 2); //開始位置
int length = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 7); //長さ
hcdPlay (start, start + length);
hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE); //エラーなしでステータスフェーズに移行する
} //hcdDoPlayAudio10_1st
//hcdDoPlayAudioMSF_1st ()
// [0] 0x47
// [1] |LUN###|-----|
// [3] 開始・Mフィールド
// [4] 開始・Sフィールド
// [5] 開始・Fフィールド
// [6] 終了・Mフィールド
// [7] 終了・Sフィールド
// [8] 終了・Fフィールド
// [9] コントロール・バイト
static void hcdDoPlayAudioMSF_1st () {
int start = (-75 * 2 +
(hcdChip.spiCommandBuffer[3] & 255) * (75 * 60) +
(hcdChip.spiCommandBuffer[4] & 255) * 75 +
(hcdChip.spiCommandBuffer[5] & 255)); //開始位置
int end = (-75 * 2 +
(hcdChip.spiCommandBuffer[6] & 255) * (75 * 60) +
(hcdChip.spiCommandBuffer[7] & 255) * 75 +
(hcdChip.spiCommandBuffer[8] & 255)); //終了位置
hcdPlay (start, end);
hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE); //エラーなしでステータスフェーズに移行する
} //hcd.DoPlayAudioMSF
//hcdDoPauseResume_1st ()
// [0] 0x4b
// [1] |LUN###|-----|
// [8] |-------|Resume|
// [9] コントロール・バイト
static void hcdDoPauseResume_1st () {
boolean resume = (hcdChip.spiCommandBuffer[8] & 1) != 0;
if (resume) {
hcdResume ();
} else {
hcdPause ();
}
hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE); //エラーなしでステータスフェーズに移行する
} //hcdDoPauseResume_1st
//hcdDoPlayAudio12_1st ()
// [0] 0xa5
// [1] |LUN###|----|RelAdr|
// [2][3][4][5] 論理ブロック・アドレス
// [6][7][8][9] 転送データ長
// [11] コントロール・バイト
static void hcdDoPlayAudio12_1st () {
int start = ByteArray.byaRls (hcdChip.spiCommandBuffer, 2); //開始位置
int length = ByteArray.byaRls (hcdChip.spiCommandBuffer, 6); //長さ
hcdPlay (start, start + length);
hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE); //エラーなしでステータスフェーズに移行する
} //hcdDoPlayAudio12_1st
//hcdDoReadCDDA_2nd ()
// [0] 0xd8
// [1] |LUN###|-----|
// [2][3][4][5] 論理ブロックアドレス
// [8][9] 論理ブロック数
static void hcdDoReadCDDA_2nd () {
if (!hcdOpen ()) { //開けない
hcdNotReady ();
return;
}
int a = ByteArray.byaRls (hcdChip.spiCommandBuffer, 2); //論理ブロックアドレス
int n = ByteArray.byaRwz (hcdChip.spiCommandBuffer, 8); //論理ブロック数
if (n == 0) {
hcdGood ();
return;
}
byte[] buffer = hcdRead (a, n);
if (buffer == null) {
hcdNotReady ();
return;
}
if (buffer.length == 0) {
hcdMediumError ();
return;
}
//データインフェーズに移行する
hcdResultBuffer = buffer;
hcdResultLength = buffer.length;
} //hcdDoReadCDDA_2nd
static void hcdGood () {
hcdResultSense0 = 0;
hcdResultSense2 = 0;
hcdResultStatus = SPC.SPC_GOOD;
hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
} //hcdGood
static void hcdNotReady () {
hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
hcdResultSense2 = SPC.SPC_NOT_READY;
hcdResultStatus = SPC.SPC_CHECK_CONDITION;
hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
} //hcdNotReady
static void hcdUnitAttention () {
hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
hcdResultSense2 = SPC.SPC_UNIT_ATTENTION;
hcdResultStatus = SPC.SPC_CHECK_CONDITION;
hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
} //hcdUnitAttention
static void hcdDataProtect () {
hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
hcdResultSense2 = SPC.SPC_DATA_PROTECT;
hcdResultStatus = SPC.SPC_CHECK_CONDITION;
hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
} //hcdDataProtect
static void hcdMediumError () {
hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
hcdResultSense2 = SPC.SPC_MEDIUM_ERROR;
hcdResultStatus = SPC.SPC_CHECK_CONDITION;
hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
} //hcdMediumError
static void hcdIllegalRequest () {
hcdResultSense0 = SPC.SPC_EXTENDED_SENSE;
hcdResultSense2 = SPC.SPC_ILLEGAL_REQUEST;
hcdResultStatus = SPC.SPC_CHECK_CONDITION;
hcdResultMessage = SPC.SPC_COMMAND_COMPLETE;
} //hcdIllegalRequest
//hcdReadINTSPSNS ()
// INTSまたはPSNSが読み出された
public static void hcdReadINTSPSNS () {
if (hcdRetrieved != hcdCompleted) { //回収カウンタ!=完了カウンタのとき
if (hcdResultBuffer != null) { //結果のデータがあるとき
hcdChip.spiDataInPhase (hcdResultBuffer, 0, hcdResultLength, 0); //データインフェーズに移行する
} else { //結果のデータがないとき
hcdChip.spiSenseBuffer[hcdChip.spiLUN][0] = (byte) hcdResultSense0;
hcdChip.spiSenseBuffer[hcdChip.spiLUN][2] = (byte) hcdResultSense2;
hcdChip.spiStatusPhase (hcdResultStatus, hcdResultMessage); //ステータスフェーズに移行する
}
hcdChip.spiSetInterruptStatus (SPC.SPC_INTS_CC);
hcdRetrieved = hcdCompleted;
}
} //hcdReadINTSPSNS
//hcdDataOutComplete (unit, oc)
// データアウトフェーズの転送が終了した
public static void hcdDataOutComplete (int oc) {
switch (oc) {
case 0x15: //Mode Select(6)
hcdDoModeSelect6_2nd ();
break;
default:
hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE); //エラーなしでステータスフェーズに移行する
}
} //hcdDataOutComplete
//hcdDoModeSelect6_2ns ()
static void hcdDoModeSelect6_2nd () {
int oc = hcdChip.spiCommandBuffer[0] & 255; //オペレーションコード
if (hcdDebugInfo) {
hcdUnit.scuPrintDataOut (oc);
}
int length = 4;
if (length + 8 <= hcdChip.spiBufferLimit &&
(hcdChip.spiDataOutBuffer[3] & 255) == 8) { //ブロック・ディスクリプタ
hcdSetBytesPerSector ((hcdChip.spiDataOutBuffer[length + 6] & 255) << 8 |
(hcdChip.spiDataOutBuffer[length + 7] & 255));
length += 8;
}
while (length + 1 <= hcdChip.spiBufferLimit) {
int pageCode = hcdChip.spiDataOutBuffer[length] & 63; //ページコード
if (length + 8 <= hcdChip.spiBufferLimit &&
pageCode == 0x01) { //リード・エラー・リカバリ・パラメータ
length += 8;
} else if (length + 24 <= hcdChip.spiBufferLimit &&
pageCode == 0x03) { //フォーマット・パラメータ
length += 24;
} else if (length + 16 <= hcdChip.spiBufferLimit &&
pageCode == 0x0e) { //CD-ROMオーディオ・コントロール・パラメータ
hcdVolumeInt = (hcdChip.spiDataOutBuffer[length + 9] & 255) * 100 / 255; //ボリューム(ポート#0)
if (hcdDebugInfo) {
System.out.printf ("volume=%d\n", hcdVolumeInt);
}
hcdVolumeFloat = (float) hcdVolumeInt / 100F;
hcdVolumeSlider.setValue (hcdVolumeInt);
hcdVolumeLabel.setText (String.valueOf (hcdVolumeInt));
length += 16;
} else if (length + 4 <= hcdChip.spiBufferLimit &&
pageCode == 0x31) { //転送速度
length += 4;
} else {
break;
}
}
hcdChip.spiStatusPhase (SPC.SPC_GOOD, SPC.SPC_COMMAND_COMPLETE); //エラーなしでステータスフェーズに移行する
} //hcdDoModeSelect6_2nd
//hcdDumpBuffer (buffer, length)
static void hcdDumpBuffer (byte[] buffer, int length) {
System.out.print ("[");
for (int i = 0; i < length; i++) {
if (i != 0) {
System.out.print (",");
}
System.out.printf ("0x%02x", buffer[i] & 255);
}
System.out.println ("]");
} //hcdDumpBuffer
} //class HostCDROM