/*********************************************************************** 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 . ***********************************************************************/ #define WIN32_LEAN_AND_MEAN #include #include #include "scsipt.h" #include "myvector.h" #include "resource.h" #include "../include/dvdsynth-device.h" #include "miniport.h" #include "kernel.h" #include "ConfigMgr.h" #include "SharedPool.h" #include "udfrecognize.h" #include /*******************************************************************\ \*******************************************************************/ void AsyncUserModeCall(void (*f)(void*, int), void* p, int i); /*******************************************************************\ \*******************************************************************/ DvsBasePluginCallbacks* g_callbacks; /*******************************************************************\ \*******************************************************************/ class Device { DvsDeviceUser* u; public: Device() { u = 0; } void set(DvsDeviceUser* _u) { u = _u; } Device(DvsDeviceUser* _u) { set(_u); } bool Occupied() { return u != 0; } void AddDeviceMenuItems(DvsMenu* menu) { if (u->vtable->AddDeviceMenuItems) { u->vtable->AddDeviceMenuItems(u, menu); } } int QueryUnplug() { if (u->vtable->QueryUnplug != 0) { return u->vtable->QueryUnplug(u); } else { return 0; } } void Delete() { u->vtable->Delete(u); u = 0; } }; class Plugin { DvsDeviceGlobal* p; public: Plugin(DvsDeviceGlobal* plugin) { p = plugin; } Plugin(const Plugin& plugin) { p = plugin.p; } void operator=(DvsDeviceGlobal* plugin) { p = plugin; } void operator=(const Plugin& plugin) { p = plugin.p; } void AddNewMenuItems(DvsMenu* menu) { if (p->AddNewMenuItems != 0) p->AddNewMenuItems(menu); } void AddAboutMenuItems(DvsMenu* menu) { if (p->AddAboutMenuItems != 0) p->AddAboutMenuItems(menu); } void AddMainMenuItems(DvsMenu* menu) { if (p->AddMainMenuItems != 0) p->AddMainMenuItems(menu); } }; /*******************************************************************\ \*******************************************************************/ template class Optional { char space[sizeof(T)]; bool exists; public: Optional() { exists = false; } void _Construct() { if (!exists) { exists = true; new ((void*)space) T(); } } template void _Construct(A&a) { if (!exists) { exists = true; new ((void*)space) T(a); } } template void _Construct(A&a,B&b) { if (!exists) { exists = true; new ((void*)space) T(a,b); } } template void _Construct(A&a,B&b,C&c) { if (!exists) { exists = true; new ((void*)space) T(a,b,c); } } template void _Construct(A&a,B&b,C&c,D&d) { if (!exists) { exists = true; new ((void*)space) T(a,b,c,d); } } template void _Construct(A&a,B&b,C&c,D&d,E&e) { if (!exists) { exists = true; new ((void*)space) T(a,b,c,d,e); } } T* operator->() { return (T*)space; } void _Destruct() { if (exists) { ((T*)space)->~T(); exists = false; } } ~Optional() { if (exists) { ((T*)space)->~T(); } } }; /*******************************************************************\ \*******************************************************************/ int Is95() { return 0; } /*******************************************************************\ \*******************************************************************/ class DockingBay : public DvsDockingBay { int scsi_id; unsigned drive_letters; Device device; void* pool; public: DockingBay(int _scsi_id) { static DvsDockingBay_vtable vt = { StaticSetHandlers, StaticRequestUnplug, StaticSharedPool_Alloc, StaticGetScsiID, StaticGetDriveLetters }; vtable = &vt; scsi_id = _scsi_id; drive_letters = 0; pool = 0; } bool Occupied() { return device.Occupied(); } unsigned GetDriveLetters() const { return drive_letters; } void ClearDriveLetters() { drive_letters = 0; } void AddDriveLetter(unsigned mask) { drive_letters |= mask; } void AddDeviceMenuItems(DvsMenu* menu) { device.AddDeviceMenuItems(menu); } void SetHandlers(DvsDeviceUser* user_handler, DvsDeviceKernel* kernel_handler) { device.set(user_handler); KernelThread::AsyncSetHandler(scsi_id, kernel_handler); } void Unplug() { KernelThread::AsyncSetHandler(scsi_id, 0); device.Delete(); if (pool) SharedPool_Clear(pool); } void ForceUnplug() { Unplug(); ListenerThread::AsyncRescanBus(); } bool RequestUnplug() { if (device.QueryUnplug() < 0) return false; if (!ConfigManager::QueryAndRemoveTarget(scsi_id)) return false; Unplug(); return true; } void* SharedPool_Alloc(unsigned len) { if (pool == 0) pool = ::SharedPool_Create(true); return ::SharedPool_Alloc(pool, len); } int GetScsiID() { return scsi_id; } static void StaticSetHandlers(DvsDockingBay* self, DvsDeviceUser* user_handler, DvsDeviceKernel* kernel_handler) { ((DockingBay*)self)->SetHandlers(user_handler, kernel_handler); } static void StaticRequestUnplug(DvsDockingBay* self) { ((DockingBay*)self)->RequestUnplug(); } static void* StaticSharedPool_Alloc(DvsDockingBay* self, unsigned len) { return ((DockingBay*)self)->SharedPool_Alloc(len); } static int StaticGetScsiID(DvsDockingBay* self) { return ((DockingBay*)self)->GetScsiID(); } static unsigned StaticGetDriveLetters(DvsDockingBay* self) { return ((DockingBay*)self)->GetDriveLetters(); } }; /*******************************************************************\ \*******************************************************************/ DockingBay* original_bays[KernelThread::num_bays]; DvsDockingBay* hooked_bays[KernelThread::num_bays]; void InitDockingBays() { for (int i=0; i= 16 && !displayed_16_warning) { MessageBox(NULL, "Old versions of ASPI cannot handle devices with ID numbers of 16 or greater. Be sure you are using a recent version of ASPI. In addition, some applications may have trouble using these devices.", "DVDSynth", MB_OK); displayed_16_warning = displayed_8_warning = true; } else if (id >= 8 && !displayed_8_warning) { MessageBox(NULL, "Some old applications may have trouble using virtual devices with an ID number of 8 or greater.", "DVDSynth", MB_OK); displayed_8_warning = true; } } DvsDockingBay* ReserveDockingBay() { for (int i=0; iOccupied()) { int scsi_id = original_bays[i]->GetScsiID(); DisplayScsiIDWarning(scsi_id); return hooked_bays[i]; } } MessageBox(NULL, "no more devices available", "DVDSynth", MB_OK); return 0; } /*******************************************************************\ \*******************************************************************/ namespace SPTI { extern int scsi_port_number; }; void ScanDriveLetters() { for (int b=0; bClearDriveLetters(); DWORD drive_mask = GetLogicalDrives(); for (int l=2; l<26; ++l) { if (!(drive_mask & (1< 0 && sa.TargetId <= KernelThread::num_bays) { original_bays[sa.TargetId-1]->AddDriveLetter(1<f(acr->p, acr->i); delete acr; return 0; } else { if (msg == WM_APP && lparam == WM_MOUSEMOVE) { // Because drive-letter scanning is not instantaneous, try // to do it slightly in advance of the user's clicking on // the icon. static DWORD last_scan_time = 0; DWORD current_time = GetTickCount(); if (DWORD(current_time - last_scan_time) >= 5000) { last_scan_time = current_time; ScanDriveLetters(); } } return OldWndProc(hwnd, msg, wparam, lparam); } } void SubclassTrayWindow() { HWND hwnd = (HWND)g_callbacks->GetTaskbarHWND(); OldWndProc = (WindowProcedure*)GetWindowLong(hwnd, GWL_WNDPROC); SetWindowLong(hwnd, GWL_WNDPROC, (LONG)MyWndProc); } void AsyncUserModeCall(void (*f)(void*, int), void* p, int i) { AsyncCallRecord* acr = new AsyncCallRecord; acr->f = f; acr->p = p; acr->i = i; PostMessage((HWND)g_callbacks->GetTaskbarHWND(), WM_APP_APC, 0, (LPARAM)acr); } /*******************************************************************\ \*******************************************************************/ #define PELDR_ERR_NOERROR 0 #define PELDR_ERR_INVALIDHANDLE -1 dvs_driver_handle Driver_Load(const char* filename) { // FIXME: corresponding work in dvdsynth95 if (strchr(filename, '\\') || strchr(filename, '/') || strchr(filename, ':')) { MessageBox(NULL, "INTERNAL ERROR: A plugin tried to load a kernel component from outside the DVDSynth directory", "DVDSynth", MB_OK); return 0; } HMODULE h = LoadLibrary(filename); // FIXME: need explicit path if (h) { KernelThread::AsyncCallInitFunc(h); return dvs_driver_handle(h); } else { DWORD error = GetLastError(); char buf[1024]; g_callbacks->Sprint(buf, 256, "INTERNAL ERROR: There was an error loading the driver file \"%1\": ", 0, filename); int len = lstrlen(buf); if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, buf+len, sizeof(buf)-len, NULL)) { wsprintf(buf+len, "error #%d", error); } MessageBox(NULL, buf, "DVDSynth", MB_OK); return 0; } } void Driver_Unload(dvs_driver_handle handle) { if (!FreeLibrary(HMODULE(handle))) { MessageBox(NULL, "INTERNAL ERROR: An invalid handle was passed to UnloadDriver. This is almost certainly a plugin bug.", "DVDSynth", MB_OK); } } /*******************************************************************\ \*******************************************************************/ int OpenFileRO(dvs_file_handle* phandle, const char* pathname) { HANDLE h = CreateFile(pathname, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); if (h == INVALID_HANDLE_VALUE) { return -1; } else { *phandle = dvs_file_handle(h); return 0; } } int CreateOrOpenFileRW(dvs_file_handle* phandle, const char* pathname, int creation_disposition) { DWORD disp = (creation_disposition <= 0) ? OPEN_EXISTING : (creation_disposition == 1) ? CREATE_ALWAYS : CREATE_NEW; HANDLE h = CreateFile(pathname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { return -1; } else { *phandle = dvs_file_handle(h); return 0; } } void CloseFile(dvs_file_handle handle) { CloseHandle(HANDLE(handle)); } /*******************************************************************\ \*******************************************************************/ unsigned OpenKernelEventHandle(void* user_handle) { return unsigned(user_handle); } void CloseKernelEventHandle(unsigned kernel_handle) {} /*******************************************************************\ \*******************************************************************/ scsi_result_t KernelScsiCommand(DvsDeviceKernel* kdev, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { return kdev->ScsiCommand(kdev, cdb, cdblen, buffer, pbuflen, inout, sense); } /*******************************************************************\ \*******************************************************************/ Vector plugins; typedef DvsDeviceGlobal* __cdecl DDPE_func(DvsDockingBayGlobal*); void AddPlugin(DDPE_func* plugin_entry) { static DvsDockingBayGlobal callbacks = { ReserveDockingBay, Driver_Load, KernelThread::AsyncDriverCall, Driver_Unload, OpenFileRO, CreateOrOpenFileRW, CloseFile, OpenKernelEventHandle, CloseKernelEventHandle, Is95, KernelScsiCommand, GetDvdVideoInfo // FIXME: add equiv. to ConfigManager::GetDvdproxyDevnode }; callbacks.Sprint = g_callbacks->Sprint; callbacks.GetDvdsynthDirectory = g_callbacks->GetDvdsynthDirectory; callbacks.GetTaskbarHWND = g_callbacks->GetTaskbarHWND; DvsDeviceGlobal* plugin_functions = plugin_entry(&callbacks); if (plugin_functions) { if (plugin_functions->HookDockingBay != 0) { for (int i=0; iHookDockingBay(hooked_bays[i]); } } plugins.push_back(plugin_functions); } } void LoadPlugins() { // Add external plugins from DLLs in program directory WIN32_FIND_DATA wfd; HANDLE h = FindFirstFile("*.dll", &wfd); if (h != INVALID_HANDLE_VALUE) { do { HMODULE hmod = LoadLibrary(wfd.cFileName); if (hmod != NULL) { DDPE_func* plugin_entry = (DDPE_func*)GetProcAddress(hmod, "DvdsynthDevicePluginEntry"); if (plugin_entry) { AddPlugin(plugin_entry); } else { FreeLibrary(hmod); } } } while (FindNextFile(h, &wfd)); FindClose(h); } } /*******************************************************************\ \*******************************************************************/ // 1. QueryUnplug() on all // 2. QueryAndRemove() on all // 3. detach // 4. Delete() all int QueryExit() { bool asked_for_confirmation = false; for (int i=0; i<31; ++i) { if (original_bays[i]->Occupied()) { if (!asked_for_confirmation) { if (IDYES != MessageBox(NULL, "If you quit DVDSynth, all virtual devices will be unplugged. "/*"They will be restored when you next start DVDSynth."*/ "Are you sure you want to quit?", "DVDSynth", MB_YESNO)) { return -1; } asked_for_confirmation = true; } if (!original_bays[i]->RequestUnplug()) { return -1; } } } return 0; } void NotifyExit() {} /*******************************************************************\ \*******************************************************************/ void UnplugDevice(void*, int index) { original_bays[index]->RequestUnplug(); } void AddMainMenuItems(DvsMenu* menu) { menu->vtable->AddSeparator(menu); menu->vtable->BeginSubmenu(menu, "&New...", false); { for (int i=0; ivtable->EndSubmenu(menu); menu->vtable->AddSeparator(menu); for (int i=0; iOccupied()) { unsigned letters = original_bays[i]->GetDriveLetters(); char letter; if (letters) { letter = 'A'; while (!(letters&1)) { letters >>= 1; ++letter; } } char buf[256]; g_callbacks->Sprint(buf, 256, letters ? "Device #&%1 (%2:)" : "Device #&%1", "ic", original_bays[i]->GetScsiID(), letter); menu->vtable->BeginSubmenu(menu, buf, false); { original_bays[i]->AddDeviceMenuItems(menu); menu->vtable->AddSeparator(menu); menu->vtable->AddItem(menu, "Unplug this device", false, UnplugDevice, 0, i); } menu->vtable->EndSubmenu(menu); } } menu->vtable->AddSeparator(menu); for (int j=0; jvtable->AddItem(menu, "NT &Disk Manager (change drive letters)", false, OpenNTDiskManager, 0, 0); } /*******************************************************************\ \*******************************************************************/ void AddAboutMenuItems(DvsMenu* menu) { } /*******************************************************************\ \*******************************************************************/ DvsBasePluginFunctions plugin_functions = { AddMainMenuItems, AddOptionsMenuItems, AddAboutMenuItems, QueryExit, NotifyExit }; DvsBasePluginFunctions* __cdecl VDevice_DvdsynthBasePluginEntry(DvsBasePluginCallbacks* callbacks) { g_callbacks = callbacks; if (!Miniport::Init()) { printf("Miniport/SPTI init failed!\n"); return 0; } if (!ConfigManager::Init()) { return 0; } SubclassTrayWindow(); InitDockingBays(); LoadPlugins(); KernelThread::Spawn(); ListenerThread::Spawn(); return &plugin_functions; }