📄 mirrordrive2k.cpp
字号:
/***********************************************************************
Copyright 2002 Ben Rudiak-Gould.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
or visit <http://www.gnu.org/copyleft/gpl.html>.
***********************************************************************/
#define WIN32_LEAN_AND_MEAN
#define _WIN32_IE 0x300
#include <windows.h>
#include <commctrl.h>
#include "resource.h"
#include "../include/dvdsynth-device.h"
#include "../dvdsynth2k/scsipt.h"
#include <stddef.h>
#include <stdio.h>
HINSTANCE g_hinstance;
DvsDockingBayGlobal* g_callback;
struct DeviceInfo {
unsigned char port, path, target, lun;
char devname[8];
unsigned char type;
char model[27];
};
const int MAX_DEVICES = 64;
DeviceInfo g_device_info[MAX_DEVICES];
int g_num_devices;
HWND g_hlistview;
scsi_result_t SendScsiCommand(HANDLE hdevice, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) {
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
memset(&swb, 0, sizeof(swb));
swb.spt.Length = sizeof(SCSI_PASS_THROUGH);
swb.spt.CdbLength = cdblen;
swb.spt.DataIn = inout==1 ? SCSI_IOCTL_DATA_IN : inout==2 ? SCSI_IOCTL_DATA_OUT : SCSI_IOCTL_DATA_UNSPECIFIED;
swb.spt.DataTransferLength = *pbuflen;
swb.spt.TimeOutValue = 60;
swb.spt.DataBuffer = swb.spt.DataTransferLength ? buffer : NULL;
swb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
swb.spt.SenseInfoLength = 18;
memcpy(swb.ucSenseBuf, sense, swb.spt.SenseInfoLength);
memcpy(swb.spt.Cdb, cdb, cdblen);
ULONG length = sizeof(swb);
ULONG returned;
if (DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &swb, length, &swb, length, &returned, NULL)) {
*pbuflen = swb.spt.DataTransferLength;
memcpy(sense, swb.ucSenseBuf, swb.spt.SenseInfoLength);
if (swb.spt.ScsiStatus) {
return MAKE_SCSIRESULT(SRB_STATUS_ERROR, swb.spt.ScsiStatus, sense->sense_key, sense->asc, sense->ascq);
} else {
return SCSIRESULT_SUCCESS;
}
} else {
DWORD error = GetLastError();
UCHAR srb_status; // Why can't the damned ioctl return this???
// It's impossible to recover the original value because many
// different codes are collapsed into the same Win32 error. Is
// this even worth it?
switch (error) {
case 37:
srb_status = SRB_STATUS_INVALID_TARGET_ID;
break;
case 79:
srb_status = SRB_STATUS_TIMEOUT;
break;
case 1167:
srb_status = SRB_STATUS_SELECTION_TIMEOUT;
break;
default:
printf("DeviceIoControl: got error %d\n", error);
srb_status = SRB_STATUS_BUS_RESET; // no clue here...
break;
}
return MAKE_SCSIRESULT(srb_status, 0, 0, 0, 0);
}
}
class MirrorInstance : public DvsDeviceUser, public DvsDeviceKernel {
DvsDockingBay* bay;
HANDLE hdevice;
bool need_not_ready_to_ready;
public:
MirrorInstance(DvsDockingBay* _bay, HANDLE _hdevice) {
static DvsDeviceUser_vtable vt = {
0,
0,
StaticDelete,
};
vtable = &vt;
ScsiCommand = StaticScsiCommand;
bay = _bay; hdevice = _hdevice;
need_not_ready_to_ready = false;
}
~MirrorInstance() {
CloseHandle(hdevice);
}
scsi_result_t _ScsiCommand(const BYTE* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) {
scsi_result_t result = SendScsiCommand(hdevice, cdb, cdblen, buffer, pbuflen, inout, sense);
if (result == SCSIRESULT_SUCCESS) {
if (need_not_ready_to_ready) {
result = MAKE_SCSIRESULT_ERROR(0x62800); // NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED
need_not_ready_to_ready = false;
}
} else if (result == MAKE_SCSIRESULT_ERROR(0x62800)) {
need_not_ready_to_ready = false;
} else if ((result & 0xFFFFFF00) == MAKE_SCSIRESULT_ERROR(0x23A00)) {
need_not_ready_to_ready = true;
}
return result;
}
// scsi_result_t ScsiCommand(const BYTE* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) {
// return g_callback->SendScsiCommand(ha, targ, lun, cdb, cdblen, buffer, pbuflen, inout, sense);
// }
static void StaticDelete(DvsDeviceUser* self) {
delete (MirrorInstance*)self;
}
static scsi_result_t StaticScsiCommand(DvsDeviceKernel* self, const BYTE* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) {
return ((MirrorInstance*)self)->_ScsiCommand(cdb, cdblen, buffer, pbuflen, inout, sense);
}
};
void SetModelField(char* dst, const unsigned char* src) {
// copy vendor ID field
memcpy(dst, src, 8);
char* p;
// lop off trailing spaces
p = dst+8;
while (p > dst && p[-1] == ' ')
--p;
// copy product ID field, with delimiting space
*p++ = ' ';
memcpy(p, src+8, 16);
// lop off trailing spaces
p += 16;
while (p > dst && p[-1] == ' ')
--p;
*p = 0;
}
HANDLE OpenScsiDevice(const char* name) {
char buf[32];
wsprintf(buf, "\\\\.\\%s", name);
return CreateFile(buf, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
}
void ScanDevice(const char* name) {
if (g_num_devices >= MAX_DEVICES) return;
HANDLE hdevice = OpenScsiDevice(name);
if (hdevice == INVALID_HANDLE_VALUE) return;
SCSI_ADDRESS sa;
ULONG returned;
memset(&sa, 0, sizeof(sa));
if (!DeviceIoControl(hdevice, IOCTL_SCSI_GET_ADDRESS, NULL, 0, &sa, sizeof(sa), &returned, NULL)) {
CloseHandle(hdevice);
return;
}
for (int n=0; n<g_num_devices; ++n) {
if (g_device_info[n].port == sa.PortNumber
&& g_device_info[n].path == sa.PathId
&& g_device_info[n].target == sa.TargetId
&& g_device_info[n].lun == sa.Lun)
{
CloseHandle(hdevice);
return;
}
}
static const unsigned char cdb_inquiry[6] = { 0x12, 0, 0, 0, 36, 0 };
unsigned char inquiry_buf[36];
memset(inquiry_buf, 0, sizeof(inquiry_buf));
unsigned long buflen = 36;
SenseData sense;
if (SCSIRESULT_SUCCESS != SendScsiCommand(hdevice, cdb_inquiry, 6, inquiry_buf, &buflen, 1, &sense)) {
CloseHandle(hdevice);
return;
}
DeviceInfo& di = g_device_info[g_num_devices++];
di.port = sa.PortNumber;
di.path = sa.PathId;
di.target = sa.TargetId;
di.lun = sa.Lun;
lstrcpy(di.devname, name);
di.type = inquiry_buf[0];
SetModelField(di.model, inquiry_buf+8);
CloseHandle(hdevice);
}
void ScanForDevices() {
// first try drive letters
DWORD drive_mask = GetLogicalDrives();
for (int i=2; i<26; ++i) {
if (drive_mask & (1<<i)) {
char buf[16];
wsprintf(buf, "%c:\\", i + 'A');
if (GetDriveType(buf) != DRIVE_REMOTE) {
wsprintf(buf, "%c:", i + 'A');
ScanDevice(buf);
}
}
}
// Next try CdromNN devices. On my system there were at one time
// three such devices, numbered Cdrom0, Cdrom1 and Cdrom13. So I
// guess I can't stop when I find a nonexistent one. :-(
for (int j=0; j<100; ++j) {
char buf[16];
wsprintf(buf, "Cdrom%d", j);
ScanDevice(buf);
}
}
// HWND hlv = (HWND)arg;
// if (g_hlistview != hlv)
// return;
void AddListViewColumn(HWND hlv, int index, const char* text, int width) {
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvc.fmt = 0;
lvc.cx = width;
lvc.pszText = (LPTSTR)text;
ListView_InsertColumn(hlv, index, &lvc);
}
BOOL NewInstanceDialog_InitDialog(HWND hdlg) {
HWND hlist = GetDlgItem(hdlg, IDC_DEVICELIST);
ListView_SetExtendedListViewStyle(hlist, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);
// get the width of the list view and size its columns proportionally
RECT r;
GetClientRect(hlist, &r);
unsigned width = r.right;
unsigned divider1 = width*14/32;
unsigned divider2 = width*21/32;
unsigned divider3 = width*27/32;
AddListViewColumn(hlist, 0, "Make and model", divider1);
AddListViewColumn(hlist, 1, "Type", divider2-divider1);
AddListViewColumn(hlist, 2, "Name", divider3-divider2);
AddListViewColumn(hlist, 3, "Address", width-divider3);
g_hlistview = hlist;
g_num_devices = 0;
ScanForDevices();
ListView_SetItemCount(hlist, g_num_devices);
return TRUE;
}
int GetLVSelection(HWND hlv) {
return ListView_GetNextItem(hlv, (unsigned)-1, LVNI_SELECTED);
}
const char* scsi_device_type[] = {
"Disk drive",
"Tape drive",
"Printer",
"Processor",
"WORM drive",
"CD/DVD drive",
"Scanner",
"Optical drive",
"Medium changer",
"Network device",
"ASCIT8",
"ASCIT8",
"Array",
"Enclosure",
"RBC",
"Card reader",
"Bridge"
};
void GetDispInfo(LVITEM* plvi) {
if (!(plvi->mask & LVIF_TEXT))
return;
int i = plvi->iItem, c = plvi->iSubItem;
if (i < 0 || i >= g_num_devices || i >= MAX_DEVICES)
return;
char buf[32];
const char* text;
const DeviceInfo& di = g_device_info[i];
switch (c) {
case 0:
text = di.model;
break;
case 1:
if (di.type <= sizeof(scsi_device_type)/sizeof(scsi_device_type[0])) {
text = scsi_device_type[di.type];
} else {
wsprintf(buf, "unknown (0x%02X)", di.type);
text = buf;
}
break;
case 2:
text = di.devname;
break;
case 3:
if (di.path) {
wsprintf(buf, "%d.%d:%d:%d", di.port, di.path, di.target, di.lun);
} else {
wsprintf(buf, "%d:%d:%d", di.port, di.target, di.lun);
}
text = buf;
break;
default:
return;
}
lstrcpyn(plvi->pszText, text, plvi->cchTextMax);
plvi->pszText[plvi->cchTextMax-1] = 0;
}
BOOL CALLBACK NewInstanceDialogProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) {
switch (msg) {
case WM_INITDIALOG:
return NewInstanceDialog_InitDialog(hdlg);
case WM_COMMAND:
switch (LOWORD(wparam)) {
case IDOK:
EndDialog(hdlg, GetLVSelection(GetDlgItem(hdlg, IDC_DEVICELIST)));
return TRUE;
case IDCANCEL:
EndDialog(hdlg, -1);
return TRUE;
}
break;
case WM_NOTIFY:
if (wparam == IDC_DEVICELIST) {
LPNMHDR pnmh = (LPNMHDR)lparam;
if (pnmh->code == LVN_GETDISPINFO) {
GetDispInfo(&((LV_DISPINFO*)pnmh)->item);
return TRUE;
} else if (pnmh->code == LVN_ITEMCHANGED) {
EnableWindow(GetDlgItem(hdlg, IDOK), ListView_GetSelectedCount(pnmh->hwndFrom));
return TRUE;
} else if (pnmh->code == LVN_ITEMACTIVATE) {
EndDialog(hdlg, GetLVSelection(GetDlgItem(hdlg, IDC_DEVICELIST)));
return TRUE;
}
}
break;
}
return FALSE;
}
void CreateNewInstance(void*, int) {
DvsDockingBay* bay = g_callback->ReserveDockingBay();
if (!bay) return;
int selection = DialogBox(g_hinstance, MAKEINTRESOURCE(IDD_CHOOSEDEVICE), NULL, NewInstanceDialogProc);
if (selection >= 0 && selection < g_num_devices) {
HANDLE hdevice = OpenScsiDevice(g_device_info[selection].devname);
if (hdevice != INVALID_HANDLE_VALUE) {
MirrorInstance* instance = new MirrorInstance(bay, hdevice);
bay->vtable->SetHandlers(bay, instance, instance);
} else {
MessageBox(NULL, "Unable to connect to the drive!", "Mirror Drive", MB_OK);
}
}
}
void AddNewMenuItems(DvsMenu* menu) {
menu->vtable->AddItem(menu, "Mirror Drive", false, CreateNewInstance, 0, 0);
}
void AddAboutMenuItems(DvsMenu* menu) {}
DvsDeviceGlobal global_funcs = {
AddNewMenuItems,
AddAboutMenuItems,
0,//AddMainMenuItems,
0
};
extern "C"
DvsDeviceGlobal* DvdsynthDevicePluginEntry(DvsDockingBayGlobal* bays) {
if (bays->Is95())
return 0;
g_callback = bays;
return &global_funcs;
}
extern "C"
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID) {
g_hinstance = hinst;
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -