| /* | |
| * util_windows.cpp - Miscellaneous utilities for Win32 | |
| * | |
| * Basilisk II (C) 1997-2008 Christian Bauer | |
| * | |
| * Windows platform specific code copyright (C) Lauri Pesonen | |
| * | |
| * 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 | |
| */ | |
| #include "sysdeps.h" | |
| #include "util_windows.h" | |
| #include "main.h" | |
| #include <io.h> | |
| #include <fcntl.h> | |
| #include <list> | |
| using std::list; | |
| #include <string> | |
| using std::string; | |
| using std::wstring; | |
| typedef std::basic_string<TCHAR> tstring; | |
| std::unique_ptr<char[]> str(const wchar_t* s) | |
| { | |
| auto length = WideCharToMultiByte(CP_ACP, 0, s, -1, nullptr, 0, nullptr, nullptr); | |
| if (length == -1) | |
| return nullptr; | |
| std::unique_ptr<char[]> p(new char[length]); | |
| WideCharToMultiByte(CP_ACP, 0, s, -1, p.get(), length, nullptr, nullptr); | |
| return p; | |
| } | |
| std::unique_ptr<wchar_t[]> wstr(const char* s) | |
| { | |
| auto length = MultiByteToWideChar(CP_ACP, 0, s, -1, nullptr, 0); | |
| if (length == -1) | |
| return nullptr; | |
| std::unique_ptr<wchar_t[]> p(new wchar_t[length]); | |
| MultiByteToWideChar(CP_ACP, 0, s, -1, p.get(), length); | |
| return p; | |
| } | |
| string to_string(const wchar_t* s) | |
| { | |
| auto wlen = wcslen(s); // length without null terminator | |
| auto len = WideCharToMultiByte(CP_ACP, 0, s, wlen, nullptr, 0, nullptr, nullptr); | |
| if (len == -1) | |
| return string(); | |
| string str(len, '\0'); | |
| WideCharToMultiByte(CP_ACP, 0, s, wlen, &str.front(), len, nullptr, nullptr); | |
| return str; | |
| } | |
| wstring to_wstring(const char* s) | |
| { | |
| auto len = strlen(s); // length without null terminator | |
| auto wlen = MultiByteToWideChar(CP_ACP, 0, s, len, nullptr, 0); | |
| if (len == -1) | |
| return wstring(); | |
| wstring str(wlen, L'\0'); | |
| MultiByteToWideChar(CP_ACP, 0, s, len, &str.front(), wlen); | |
| return str; | |
| } | |
| size_t strlcpy(char* dst, const char* src, size_t size) | |
| { | |
| size_t length = strlen(src); | |
| if (size-- > 0) { | |
| if (length < size) | |
| size = length; | |
| memcpy(dst, src, size); | |
| dst[size] = '\0'; | |
| } | |
| return length; | |
| } | |
| size_t strlcpy(char* dst, const wchar_t* src, size_t size) | |
| { | |
| size_t length = WideCharToMultiByte(CP_ACP, 0, src, -1, dst, size, nullptr, nullptr); | |
| if (size > 0) { | |
| if (length == 0) | |
| return strlcpy(dst, str(src).get(), size); | |
| --length; | |
| } | |
| return length; | |
| } | |
| size_t strlcat(char* dst, const char* src, size_t size) | |
| { | |
| char* end = static_cast<char*>(memchr(dst, '\0', size)); | |
| if (end == nullptr) | |
| return size; | |
| size_t length = end - dst; | |
| return length + strlcpy(end, src, size - length); | |
| } | |
| size_t strlcat(char* dst, const wchar_t* src, size_t size) | |
| { | |
| char* end = static_cast<char*>(memchr(dst, '\0', size)); | |
| if (end == nullptr) | |
| return size; | |
| size_t length = end - dst; | |
| return length + strlcpy(end, src, size - length); | |
| } | |
| size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t size) | |
| { | |
| size_t length = wcslen(src); | |
| if (size-- > 0) { | |
| if (length < size) | |
| size = length; | |
| wmemcpy(dst, src, size); | |
| dst[size] = '\0'; | |
| } | |
| return length; | |
| } | |
| size_t wcslcpy(wchar_t* dst, const char* src, size_t size) | |
| { | |
| size_t length = MultiByteToWideChar(CP_ACP, 0, src, -1, dst, size); | |
| if (size > 0) { | |
| if (length == 0) | |
| return wcslcpy(dst, wstr(src).get(), size); | |
| --length; | |
| } | |
| return length; | |
| } | |
| size_t wcslcat(wchar_t* dst, const wchar_t* src, size_t size) | |
| { | |
| wchar_t* end = wmemchr(dst, L'\0', size); | |
| if (end == nullptr) | |
| return size; | |
| size_t length = end - dst; | |
| return length + wcslcpy(end, src, size - length); | |
| } | |
| size_t wcslcat(wchar_t* dst, const char* src, size_t size) | |
| { | |
| wchar_t* end = wmemchr(dst, L'\0', size); | |
| if (end == nullptr) | |
| return size; | |
| size_t length = end - dst; | |
| return length + wcslcpy(end, src, size - length); | |
| } | |
| BOOL exists( const TCHAR *path ) | |
| { | |
| HFILE h; | |
| bool ret = false; | |
| h = _topen( path, _O_RDONLY | _O_BINARY ); | |
| if(h != -1) { | |
| ret = true; | |
| _close(h); | |
| } | |
| return(ret); | |
| } | |
| BOOL create_file( const TCHAR *path, DWORD size ) | |
| { | |
| HANDLE h; | |
| bool ok = false; | |
| h = CreateFile( path, | |
| GENERIC_READ | GENERIC_WRITE, | |
| 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL | |
| ); | |
| if(h != INVALID_HANDLE_VALUE) { | |
| if(size == 0) { | |
| ok = true; | |
| } else if(SetFilePointer( h, size, NULL, FILE_BEGIN) != 0xFFFFFFFF) { | |
| if(SetEndOfFile(h)) { | |
| ok = true; | |
| if(SetFilePointer( h, 0, NULL, FILE_BEGIN) != 0xFFFFFFFF) { | |
| DWORD written; | |
| DWORD zeroed_size = size; | |
| if (zeroed_size > 1024*1024) | |
| zeroed_size = 1024*1024; | |
| char *b = (char *)malloc(zeroed_size); | |
| if(b) { | |
| memset( b, 0, zeroed_size ); | |
| WriteFile( h, b, zeroed_size, &written, NULL ); | |
| free(b); | |
| } | |
| } | |
| } | |
| } | |
| CloseHandle(h); | |
| } | |
| if(!ok) DeleteFile(path); | |
| return(ok); | |
| } | |
| int32 get_file_size( const TCHAR *path ) | |
| { | |
| HANDLE h; | |
| DWORD size = 0; | |
| h = CreateFile( path, | |
| GENERIC_READ, | |
| 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL | |
| ); | |
| if(h != INVALID_HANDLE_VALUE) { | |
| size = GetFileSize( h, NULL ); | |
| CloseHandle(h); | |
| } | |
| return(size); | |
| } | |
| /* | |
| * Thread wrappers | |
| */ | |
| HANDLE create_thread(LPTHREAD_START_ROUTINE start_routine, void *arg) | |
| { | |
| DWORD dwThreadId; | |
| return CreateThread(NULL, 0, start_routine, arg, 0, &dwThreadId); | |
| } | |
| void wait_thread(HANDLE thread) | |
| { | |
| WaitForSingleObject(thread, INFINITE); | |
| CloseHandle(thread); | |
| } | |
| void kill_thread(HANDLE thread) | |
| { | |
| TerminateThread(thread, 0); | |
| } | |
| /* | |
| * Check that drivers are installed | |
| */ | |
| bool check_drivers(void) | |
| { | |
| TCHAR path[_MAX_PATH]; | |
| GetSystemDirectory(path, lengthof(path)); | |
| _tcscat(path, TEXT("\\drivers\\cdenable.sys")); | |
| if (exists(path)) { | |
| int32 size = get_file_size(path); | |
| if (size != 6112) { | |
| TCHAR str[256]; | |
| _sntprintf(str, lengthof(str), TEXT("The CD-ROM driver file \"%s\" is too old or corrupted."), path); | |
| ErrorAlert(str); | |
| return false; | |
| } | |
| } | |
| else { | |
| TCHAR str[256]; | |
| _sntprintf(str, lengthof(str), TEXT("The CD-ROM driver file \"%s\" is missing."), path); | |
| WarningAlert(str); | |
| } | |
| return true; | |
| } | |
| /* | |
| * Network control panel helpers | |
| */ | |
| struct panel_reg { | |
| tstring name; | |
| tstring guid; | |
| }; | |
| static list<panel_reg> network_registry; | |
| typedef list<panel_reg>::const_iterator network_registry_iterator; | |
| #define NETWORK_CONNECTIONS_KEY \ | |
| TEXT("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}") | |
| static void get_network_registry(void) | |
| { | |
| LONG status; | |
| HKEY network_connections_key; | |
| DWORD len; | |
| int i = 0; | |
| if (network_registry.size() > 0) | |
| return; | |
| status = RegOpenKeyEx( | |
| HKEY_LOCAL_MACHINE, | |
| NETWORK_CONNECTIONS_KEY, | |
| 0, | |
| KEY_READ, | |
| &network_connections_key); | |
| if (status != ERROR_SUCCESS) | |
| return; | |
| while (true) { | |
| TCHAR enum_name[256]; | |
| TCHAR connection_string[256]; | |
| HKEY connection_key; | |
| TCHAR name_data[256]; | |
| DWORD name_type; | |
| const TCHAR name_string[] = TEXT("Name"); | |
| len = lengthof(enum_name); | |
| status = RegEnumKeyEx( | |
| network_connections_key, | |
| i, | |
| enum_name, | |
| &len, | |
| NULL, | |
| NULL, | |
| NULL, | |
| NULL); | |
| if (status != ERROR_SUCCESS) | |
| break; | |
| _sntprintf (connection_string, lengthof(connection_string), | |
| TEXT("%s\\%s\\Connection"), | |
| NETWORK_CONNECTIONS_KEY, enum_name); | |
| status = RegOpenKeyEx( | |
| HKEY_LOCAL_MACHINE, | |
| connection_string, | |
| 0, | |
| KEY_READ, | |
| &connection_key); | |
| if (status == ERROR_SUCCESS) { | |
| len = lengthof(name_data); | |
| status = RegQueryValueEx( | |
| connection_key, | |
| name_string, | |
| NULL, | |
| &name_type, | |
| (BYTE *)name_data, | |
| &len); | |
| if (status == ERROR_SUCCESS && name_type == REG_SZ) { | |
| panel_reg pr; | |
| pr.name = name_data; | |
| pr.guid = enum_name; | |
| network_registry.push_back(pr); | |
| } | |
| RegCloseKey (connection_key); | |
| } | |
| ++i; | |
| } | |
| RegCloseKey (network_connections_key); | |
| } | |
| const TCHAR *ether_name_to_guid(const TCHAR *name) | |
| { | |
| get_network_registry(); | |
| for (network_registry_iterator it = network_registry.begin(); it != network_registry.end(); it++) { | |
| if (_tcscmp((*it).name.c_str(), name) == 0) | |
| return (*it).guid.c_str(); | |
| } | |
| return NULL; | |
| } | |
| const TCHAR *ether_guid_to_name(const TCHAR *guid) | |
| { | |
| get_network_registry(); | |
| for (network_registry_iterator it = network_registry.begin(); it != network_registry.end(); it++) { | |
| if (_tcscmp((*it).guid.c_str(), guid) == 0) | |
| return (*it).name.c_str(); | |
| } | |
| return NULL; | |
| } | |
| /* | |
| * Get TAP-Win32 adapters | |
| */ | |
| #define ADAPTER_KEY TEXT("SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}") | |
| #define TAP_COMPONENT_ID TEXT("tap0801") | |
| const TCHAR *ether_tap_devices(void) | |
| { | |
| HKEY adapter_key; | |
| LONG status; | |
| DWORD len; | |
| int i = 0; | |
| status = RegOpenKeyEx( | |
| HKEY_LOCAL_MACHINE, | |
| ADAPTER_KEY, | |
| 0, | |
| KEY_READ, | |
| &adapter_key); | |
| if (status != ERROR_SUCCESS) | |
| return NULL; | |
| list<tstring> devices; | |
| while (true) { | |
| TCHAR enum_name[256]; | |
| TCHAR unit_string[256]; | |
| HKEY unit_key; | |
| TCHAR component_id_string[] = TEXT("ComponentId"); | |
| TCHAR component_id[256]; | |
| TCHAR net_cfg_instance_id_string[] = TEXT("NetCfgInstanceId"); | |
| TCHAR net_cfg_instance_id[256]; | |
| DWORD data_type; | |
| len = lengthof(enum_name); | |
| status = RegEnumKeyEx( | |
| adapter_key, | |
| i, | |
| enum_name, | |
| &len, | |
| NULL, | |
| NULL, | |
| NULL, | |
| NULL); | |
| if (status != ERROR_SUCCESS) | |
| break; | |
| _sntprintf (unit_string, lengthof(unit_string), TEXT("%s\\%s"), | |
| ADAPTER_KEY, enum_name); | |
| status = RegOpenKeyEx( | |
| HKEY_LOCAL_MACHINE, | |
| unit_string, | |
| 0, | |
| KEY_READ, | |
| &unit_key); | |
| if (status == ERROR_SUCCESS) { | |
| len = lengthof(component_id); | |
| status = RegQueryValueEx( | |
| unit_key, | |
| component_id_string, | |
| NULL, | |
| &data_type, | |
| (BYTE *)component_id, | |
| &len); | |
| if (status == ERROR_SUCCESS && data_type == REG_SZ) { | |
| len = lengthof(net_cfg_instance_id); | |
| status = RegQueryValueEx( | |
| unit_key, | |
| net_cfg_instance_id_string, | |
| NULL, | |
| &data_type, | |
| (BYTE *)net_cfg_instance_id, | |
| &len); | |
| if (status == ERROR_SUCCESS && data_type == REG_SZ) { | |
| if (!_tcscmp (component_id, TAP_COMPONENT_ID)) | |
| devices.push_back(net_cfg_instance_id); | |
| } | |
| } | |
| RegCloseKey (unit_key); | |
| } | |
| ++i; | |
| } | |
| RegCloseKey (adapter_key); | |
| if (devices.empty()) | |
| return NULL; | |
| // The result is a '\0' separated list of strings | |
| list<tstring>::const_iterator it; | |
| len = 0; | |
| for (it = devices.begin(); it != devices.end(); it++) | |
| len += (*it).length() + 1; | |
| TCHAR *names = (TCHAR *)malloc(len * sizeof(TCHAR)); | |
| if (names) { | |
| TCHAR *p = names; | |
| for (it = devices.begin(); it != devices.end(); it++) { | |
| len = (*it).length(); | |
| _tcscpy(p, (*it).c_str()); | |
| p[len] = '\0'; | |
| p += len + 1; | |
| } | |
| } | |
| return names; | |
| } |