/*********************************************************************** 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 "myvector.h" #include "resource.h" #include "aspi.h" #include "../dvdproxy95/dvdproxy95.h" #include "../include/dvdsynth-device.h" #include "miniport.h" #include "ConfigMgr.h" #include "SharedPool.h" #include "udfrecognize.h" #include /*******************************************************************\ \*******************************************************************/ DvsBasePluginCallbacks* g_callbacks; /*******************************************************************\ \*******************************************************************/ class Device { DvsDeviceUser* d; public: Device() { d = 0; } Device(DvsDeviceUser* device) { d = device; } void operator=(DvsDeviceUser* device) { d = device; } void operator=(const Device& device) { d = device.d; } DvsDeviceUser* GetPtr() { return d; } void AddDeviceMenuItems(DvsMenu* menu) { if (d->vtable->AddDeviceMenuItems) { d->vtable->AddDeviceMenuItems(d, menu); } } int QueryUnplug() { if (d->vtable->QueryUnplug != 0) { return d->vtable->QueryUnplug(d); } else { return 0; } } void Delete() { d->vtable->Delete(d); d = 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 1; } /*******************************************************************\ \*******************************************************************/ int SystemQueryUnplug(int target) { // if (ConfigManager::QueryRemoveTarget(target)) { // return 0; // } // return (IDYES != MessageBox(NULL, "Windows has indicated that it is not safe to unplug this device. There may be a file or folder still open on it.\n\nYou can unplug the device anyway, but this may cause applications to crash or make your system unstable.\n\nDo you want to forcibly unplug the device?", "DVDSynth", MB_YESNO|MB_DEFBUTTON2)); return 0; } inline void SystemUnplug(int target) { // ConfigManager::RemoveTarget(target); ConfigManager::Reenumerate(false); } inline void SystemRescanBus() { ConfigManager::Reenumerate(false); } /*******************************************************************\ \*******************************************************************/ class DockingBay : public DvsDockingBay { int scsi_id; 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; device = 0; pool = 0; } bool Occupied() { return device.GetPtr() != 0; } unsigned GetDriveLetters() const { return ConfigManager::GetDriveLetters(scsi_id); } void AddDeviceMenuItems(DvsMenu* menu) { device.AddDeviceMenuItems(menu); } void SetHandlers(DvsDeviceUser* user_handler, DvsDeviceKernel* kernel_handler) { device = user_handler; Miniport::DriverCall(miniport_handle, (char*)mpdfunc_SetHandler, "ip", GetScsiID(), kernel_handler); SystemRescanBus(); } void ForceUnplug() { SystemUnplug(GetScsiID()); Miniport::DriverCall(miniport_handle, (char*)mpdfunc_SetHandler, "ip", GetScsiID(), 0); device.Delete(); if (pool) SharedPool_Clear(pool); } int QueryUnplug() { return (SystemQueryUnplug(GetScsiID()) || device.QueryUnplug()); } void RequestUnplug() { if (QueryUnplug() == 0) ForceUnplug(); } 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(); } }; /*******************************************************************\ \*******************************************************************/ enum { num_bays = 31 }; DockingBay* original_bays[num_bays]; DvsDockingBay* hooked_bays[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; } /*******************************************************************\ \*******************************************************************/ #define PELDR_ERR_NOERROR 0 #define PELDR_ERR_INVALIDHANDLE -1 const char* PELDR_LoadModule(dvs_driver_handle* ph, const char* filename) { char full_pathname[MAX_PATH]; if (g_callbacks->Sprint(full_pathname, MAX_PATH, "%1%2", 0, g_callbacks->GetDvdsynthDirectory(), filename) < 0) { return "Pathname too long"; } char short_pathname[MAX_PATH]; DWORD short_len = GetShortPathName(full_pathname, short_pathname, MAX_PATH); if (short_len == 0 || short_len > MAX_PATH) { printf("full_pathname = %s\n", full_pathname); return "GetShortPathName failed (file not found?)"; } int result = (int)Miniport::DriverCall(miniport_handle, (char*)mpdfunc__PELDR_LoadModule, "&sp", ph, short_pathname, 0); if (result == PELDR_ERR_NOERROR) { return 0; } else { static const char* peldr_errors[] = { "Invalid handle", "Out of memory", "Unable to open the file", "Error reading the file", "Invalid image size", "Invalid image format", "Unknown fixup in image", "Invalid fixup in image", "Undefined import in image", "Duplicate export in image", "Init was completed", "Undefined DLL in image" }; if (result < 0 && result >= -12) { return peldr_errors[~(unsigned)result]; } else { static char buf[64]; g_callbacks->Sprint(buf, 64, "Unknown PELDR error #%1", "d", result); return buf; } } } dvs_driver_handle Driver_Load(const char* filename) { 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; } dvs_driver_handle h = 0; const char* error_string = PELDR_LoadModule(&h, filename); if (error_string == 0) { Miniport::DriverCall(miniport_handle, (char*)mpdfunc_CallDriverInitFunc, "p", h); return h; } else { char buf[256]; g_callbacks->Sprint(buf, 256, "INTERNAL ERROR: There was an error loading the driver file \"%1\":\n\n%2.", 0, filename, error_string); MessageBox(NULL, buf, "DVDSynth", MB_OK); return 0; } } void Driver_Unload(dvs_driver_handle handle) { int result = (int)Miniport::DriverCall(miniport_handle, (char*)mpdfunc__PELDR_FreeModule, "pp", handle, 0); if (result == PELDR_ERR_INVALIDHANDLE) { MessageBox(NULL, "INTERNAL ERROR: An invalid handle was passed to UnloadDriver. This is almost certainly a plugin bug.", "DVDSynth", MB_OK); } } /*******************************************************************\ \*******************************************************************/ static const char* GetFullPath(const char* pathname, char* buffer) { buffer[0] = 0; DWORD path_length = GetShortPathName(pathname, buffer, MAX_PATH); if (path_length > 0 && path_length <= MAX_PATH) { pathname = buffer; } return pathname; } int OpenFileRO(dvs_file_handle* phandle, const char* pathname) { char buffer[MAX_PATH]; return (int)Miniport::DriverCall(miniport_handle, (char*)mpdfunc_OpenFileRO, "&s", phandle, GetFullPath(pathname, buffer)); } int CreateOrOpenFileRW(dvs_file_handle* phandle, const char* pathname, int creation_disposition) { char buffer[MAX_PATH]; return (int)Miniport::DriverCall(miniport_handle, (char*)mpdfunc_CreateOrOpenFileRW, "&si", phandle, GetFullPath(pathname, buffer), creation_disposition); } void CloseFile(dvs_file_handle handle) { Miniport::DriverCall(miniport_handle, (char*)mpdfunc_CloseFile, "i", handle); } unsigned OpenKernelEventHandle(void* handle) { static DWORD (WINAPI *_OpenVxDHandle)(void*) = (DWORD(WINAPI*)(void*))GetProcAddress(GetModuleHandle("kernel32.dll"), "OpenVxDHandle"); if (_OpenVxDHandle == 0) { MessageBox(NULL, "Export KERNEL32.OpenVxDHandle not found!", "DVDSynth", MB_OK); return 0; } return _OpenVxDHandle(handle); } void CloseKernelEventHandle(unsigned handle) { Miniport::DriverCall(miniport_handle, (char*)mpdfunc_CloseEvent, "p", handle); } /*******************************************************************\ \*******************************************************************/ scsi_result_t KernelScsiCommand(DvsDeviceKernel* device, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { // note the final dummy argument specifying the size of the SenseData buffer return (scsi_result_t)Miniport::DriverCall(miniport_handle, (const char*)mpdfunc_ScsiDispatch, "p>i>&i>i", device, cdb, cdblen, buffer, pbuflen, inout, sense, sizeof(SenseData)); } /*******************************************************************\ \*******************************************************************/ Vector plugins; typedef DvsDeviceGlobal* __cdecl DDPE_func(DvsDockingBayGlobal*); void AddPlugin(DDPE_func* plugin_entry) { static DvsDockingBayGlobal callbacks = { ReserveDockingBay, Driver_Load, Miniport::DriverCall, Driver_Unload, OpenFileRO, CreateOrOpenFileRW, CloseFile, OpenKernelEventHandle, CloseKernelEventHandle, Is95, KernelScsiCommand, GetDvdVideoInfo, 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); } } /*******************************************************************\ \*******************************************************************/ 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]->QueryUnplug() != 0) { return -1; } } } return 0; } void NotifyExit() { for (int i=0; i<31; ++i) { if (original_bays[i]->Occupied()) { original_bays[i]->ForceUnplug(); } } } /*******************************************************************\ \*******************************************************************/ void UnplugDevice(void*, int index) { original_bays[index]->RequestUnplug(); } BOOL CALLBACK ChangeDriveLetterDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { static char* fromto = 0; switch (msg) { case WM_INITDIALOG: { fromto = (char*)lparam; static char drivebuf[3] = "@:"; // set the "current drive letter" edit box drivebuf[0] = fromto[0]; SetDlgItemText(hwnd, IDC_OLDLETTER, fromto[0] ? drivebuf : "none"); // fill in the "change to" drop-down list box HWND hwndCombo = GetDlgItem(hwnd, IDC_NEWLETTER); ComboBox_AddString(hwndCombo, "(no preference)"); for (char i = 'A'; i <= 'Z'; ++i) { drivebuf[0] = i; ComboBox_AddString(hwndCombo, drivebuf); } ComboBox_SetCurSel(hwndCombo, fromto[1] ? (fromto[1] - 'A' + 1) : 0); return TRUE; } case WM_COMMAND: switch (LOWORD(wparam)) { case IDOK: { int selection = ComboBox_GetCurSel(GetDlgItem(hwnd, IDC_NEWLETTER)); fromto[1] = (selection >= 1 && selection <= 26) ? selection + 'A' - 1 : 0; } // fall thru case IDCANCEL: EndDialog(hwnd, 0); return TRUE; } default: return FALSE; } } void ChangeDriveLetter(void*, int scsi_id) { char fromto[2] = {0,0}; char oldto = 0; char buf[64]; if (ConfigManager::GetTargetRegString(scsi_id, "CurrentDriveLetterAssignment", buf, 64)) { if (buf[0] >= 'A' && buf[0] <= 'Z') fromto[0] = buf[0]; } if (ConfigManager::GetTargetRegString(scsi_id, "UserDriveLetterAssignment", buf, 64)) { if (buf[0] >= 'A' && buf[0] <= 'Z') oldto = fromto[1] = buf[0]; } DialogBoxParam(GetModuleHandle(0)/*careful!*/, MAKEINTRESOURCE(IDD_DRIVELETTER), NULL, ChangeDriveLetterDlgProc, (LPARAM)fromto); if (fromto[1] != oldto) { wsprintf(buf, "%c%c", fromto[1]); if (!ConfigManager::SetTargetRegString(scsi_id, "UserDriveLetterAssignment", buf)) { MessageBox(NULL, "Couldn't change the drive letter in the registry!", "DVDSynth", MB_OK); } } } 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, "Change drive letter", false, ChangeDriveLetter, 0, original_bays[i]->GetScsiID()); menu->vtable->AddItem(menu, "Unplug this device", false, UnplugDevice, 0, i); } menu->vtable->EndSubmenu(menu); } } menu->vtable->AddSeparator(menu); for (int j=0; jtail; while (t < acq->head) { const AsyncCallRecord& acr = acq->q[t%AsyncCallQueue::queue_size]; acr.f(acr.p, acr.i); ++t; } acq->tail = t; return 0; } else { return OldWndProc(hwnd, msg, wparam, lparam); } } void SubclassTrayWindow(HWND hwnd) { OldWndProc = (WindowProcedure*)GetWindowLong(hwnd, GWL_WNDPROC); SetWindowLong(hwnd, GWL_WNDPROC, (LONG)MyWndProc); } /*******************************************************************\ \*******************************************************************/ DvsBasePluginFunctions plugin_functions = { AddMainMenuItems, AddOptionsMenuItems, AddAboutMenuItems, QueryExit, NotifyExit }; DvsBasePluginFunctions* __cdecl VDevice_DvdsynthBasePluginEntry(DvsBasePluginCallbacks* callbacks) { g_callbacks = callbacks; if (Miniport::Init() != 0) { printf("ASPI init failed!\n"); return 0; } if (!ConfigManager::Init()) { return 0; } SubclassTrayWindow((HWND)g_callbacks->GetTaskbarHWND()); InitDockingBays(); LoadPlugins(); return &plugin_functions; }