/*
[構成プロパティ]-[VC++ ディレクトリ]-[インクルード ディレクトリ]
C:\WinDDK\7600.16385.1\src\storage\tools\spti
Unicode文字セット
*/
#include <Windows.h>
#include <tchar.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <ntddscsi.h>
#include <spti.h>
#define REVWORD(w) (((w<<8)&0xff00)|((w>>8)&0xff))
#define REVDWORD(dw) (((dw<<24)&0xff000000)|((dw<<8)&0xff0000)|((dw>>8)&0xff00)|((dw>>24)&0xff))
struct READTOC {
WORD wLen;
BYTE byStartTrack;
BYTE byEndTrack;
struct TRACKDESC {
BYTE d0;
BYTE data;
BYTE byTrack;
BYTE d1;
DWORD dwLBA;
} td[100];
};
// 関数プロトタイプ宣言
int ReadTrack(TCHAR cDrv, UINT uTrack, LPCTSTR pszFile);
DWORD ExecCommand(
HANDLE hDev, // デバイスハンドル
PUCHAR pucCdb, // コマンド(SCSI command descriptor block)
UCHAR ucCdbLen, // コマンドの長さ
PVOID pvData, // バッファ
ULONG ulDataLen); // バッファの長さ
int WriteWaveHeader(FILE *pFile, DWORD dwSampleLen);
//==============================================================================
int _tmain(int argc, LPTSTR argv[])
{
TCHAR szDrv[] = _T("@:\\");
UINT ur;
_tsetlocale(LC_CTYPE, _T(""));
if (argc != 4) {
_ftprintf(stderr, _T("usage: readtrack drive track file\n"));
return 1;
}
szDrv[0] = argv[1][0];
ur = GetDriveType(szDrv);
if (ur != DRIVE_CDROM) {
_ftprintf(stderr, _T("CD-ROMドライブではありません。\n"));
return 1;
}
ReadTrack(szDrv[0], _ttoi(argv[2]), argv[3]);
return 0;
}
//------------------------------------------------------------------------------
#define CDDASECT 2352
#define READSECT 15
int ReadTrack(TCHAR cDrv, UINT uTrack, LPCTSTR pszFile)
{
int nRet = -1; // 失敗
// デバイスのオープン
TCHAR szDev[] = _T("\\\\.\\@:");
HANDLE hDev;
BOOL br;
szDev[4] = cDrv;
hDev = CreateFile(szDev, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hDev == INVALID_HANDLE_VALUE) {
return nRet;
}
// TOCの読み込み
BYTE byBuf[sizeof (READTOC) + 0xf]; // パラグラフ境界
READTOC *ptoc = (READTOC *)(((UINT_PTR)byBuf + 0xf) & ~0xf);
UCHAR ucCdb[16];
DWORD dw;
ZeroMemory(ptoc, sizeof *ptoc);
ZeroMemory(ucCdb, sizeof ucCdb);
ucCdb[0] = SCSIOP_READ_TOC;
ucCdb[6] = 1;
*(PWORD)(ucCdb + 7) = REVWORD(sizeof *ptoc);
dw = ExecCommand(hDev, ucCdb, 10, ptoc, sizeof *ptoc);
if (dw == 0) {
goto Exit;
}
// トラックチェック
if (uTrack < 1 || ptoc->byEndTrack < uTrack) {
goto Exit;
}
if (ptoc->td[uTrack-1].data & 0x04) { // データトラック
goto Exit;
}
// ファイルのオープン
FILE *pFile = NULL;
errno_t er = _tfopen_s(&pFile, pszFile, _T("wb"));
if (er != 0) {
goto Exit;
}
// トラックLBA
DWORD dwStartAddr = REVDWORD(ptoc->td[uTrack-1].dwLBA);
DWORD dwEndAddr = REVDWORD(ptoc->td[uTrack].dwLBA);
DWORD dwLen = dwEndAddr - dwStartAddr;
_tprintf(_T("Track %u, LBA %u-%u, Len %u\n"), uTrack, dwStartAddr, dwEndAddr, dwLen);
WriteWaveHeader(pFile, CDDASECT * dwLen);
// バッファ
BYTE byBuf2[CDDASECT * READSECT + 0xf]; // パラグラフ境界
PBYTE pbyBuf = (PBYTE)(((UINT_PTR)byBuf2 + 0xf) & ~0xf);
ZeroMemory(pbyBuf, CDDASECT * READSECT);
ZeroMemory(ucCdb, sizeof ucCdb);
ucCdb[0] = 0xbe; // SCSIOP_READ_CD
ucCdb[9] = 16; // コマンド長
for (DWORD dwLBA = dwStartAddr; dwLBA < dwEndAddr; ) {
DWORD dwReadSize;
if (dwEndAddr < dwLBA + READSECT) {
dwReadSize = dwEndAddr - dwLBA;
} else {
dwReadSize = READSECT;
}
*(PDWORD)(ucCdb + 2) = REVDWORD(dwLBA);
ucCdb[8] = (UCHAR)dwReadSize;
dw = ExecCommand(hDev, ucCdb, 12, pbyBuf, CDDASECT * READSECT);
if (dw == 0) {
goto Exit;
}
fwrite(pbyBuf, sizeof (BYTE), CDDASECT * dwReadSize, pFile);
dwLBA += dwReadSize;
_tprintf(_T("\r%6u/%6u"), dwLBA, dwEndAddr);
}
nRet = 0; // 成功
Exit:
if (pFile) {
int nr = fclose(pFile);
}
br = CloseHandle(hDev);
return nRet;
}
//------------------------------------------------------------------------------
DWORD ExecCommand(
HANDLE hDev, // デバイスハンドル
PUCHAR pucCdb, // コマンド(SCSI command descriptor block)
UCHAR ucCdbLen, // コマンドの長さ
PVOID pvData, // バッファ
ULONG ulDataLen) // バッファの長さ
{
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
DWORD dwRet;
BOOL br;
ZeroMemory(&swb, sizeof swb);
swb.sptd.Length = sizeof swb.sptd;
//
swb.sptd.CdbLength = ucCdbLen;
swb.sptd.SenseInfoLength = sizeof swb.ucSenseBuf;
swb.sptd.DataIn = SCSI_IOCTL_DATA_IN;
swb.sptd.DataTransferLength = ulDataLen;
swb.sptd.TimeOutValue = 10; // 秒
swb.sptd.DataBuffer = pvData;
swb.sptd.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
memcpy_s(&swb.sptd.Cdb, sizeof swb.sptd.Cdb, pucCdb, ucCdbLen);
br = DeviceIoControl(
hDev,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
&swb, sizeof swb,
&swb, sizeof swb,
&dwRet,
NULL);
if (br == FALSE) {
return 0;
}
return dwRet;
}
//------------------------------------------------------------------------------
int WriteWaveHeader(FILE *pFile, DWORD dwSampleLen)
{
WAVEFORMATEX wf;
DWORD dw;
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.nChannels = 2;
wf.nSamplesPerSec = 44100;
wf.nAvgBytesPerSec = 44100 * 2 * 2;
wf.nBlockAlign = 2 * 2; // 16bit 2ch
wf.wBitsPerSample = 16;
wf.cbSize = 0;
fwrite("RIFF", sizeof (BYTE), 4, pFile);
dw = 4 + 8 + sizeof (WAVEFORMATEX) + 12 + 8 + dwSampleLen;
fwrite(&dw, sizeof (DWORD), 1, pFile); // RIFFチャンクサイズ
fwrite("WAVE", sizeof (BYTE), 4, pFile);
fwrite("fmt ", sizeof (BYTE), 4, pFile);
dw = sizeof (WAVEFORMATEX);
fwrite(&dw, sizeof (DWORD), 1, pFile); // fmtチャンクサイズ
fwrite(&wf, sizeof (WAVEFORMATEX), 1, pFile);
fwrite("fact", sizeof (BYTE), 4, pFile);
dw = sizeof (DWORD);
fwrite(&dw, sizeof (DWORD), 1, pFile); // factチャンクサイズ
fwrite(&dwSampleLen, sizeof (DWORD), 1, pFile);
fwrite("data", sizeof (BYTE), 4, pFile);
fwrite(&dwSampleLen, sizeof (DWORD), 1, pFile); // dataチャンクサイズ
return 0;
}