diff --git a/dll/win32/KernelBase/wine/Makefile.in b/dll/win32/KernelBase/wine/Makefile.in new file mode 100644 index 0000000000000..081e9bfbb6400 --- /dev/null +++ b/dll/win32/KernelBase/wine/Makefile.in @@ -0,0 +1,28 @@ +EXTRADEFS = -DWINBASEAPI= -DWINADVAPI= -DWINUSERAPI= -DWINSHLWAPI= +MODULE = kernelbase.dll +IMPORTLIB = kernelbase +IMPORTS = uuid ntdll winecrt0 + +EXTRADLLFLAGS = -nodefaultlibs -nostartfiles +i386_EXTRADLLFLAGS = -Wl,--image-base,0x7b000000 +x86_64_EXTRADLLFLAGS = -Wl,--image-base,0x174000000 + +SOURCES = \ + console.c \ + debug.c \ + file.c \ + kernelbase.rc \ + loader.c \ + locale.c \ + main.c \ + memory.c \ + path.c \ + process.c \ + registry.c \ + security.c \ + string.c \ + sync.c \ + thread.c \ + version.c \ + volume.c \ + winerror.mc diff --git a/dll/win32/KernelBase/wine/console.c b/dll/win32/KernelBase/wine/console.c new file mode 100644 index 0000000000000..10c8bb250375d --- /dev/null +++ b/dll/win32/KernelBase/wine/console.c @@ -0,0 +1,2386 @@ +/* + * Win32 console functions + * + * Copyright 1995 Martin von Loewis and Cameron Heide + * Copyright 1997 Karl Garrison + * Copyright 1998 John Richardson + * Copyright 1998 Marcus Meissner + * Copyright 2001,2002,2004,2005,2010 Eric Pouech + * Copyright 2001 Alexandre Julliard + * Copyright 2020 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winerror.h" +#include "wincon.h" +#include "winternl.h" +#include "wine/condrv.h" +#include "wine/exception.h" +#include "wine/debug.h" +#include "kernelbase.h" + +WINE_DEFAULT_DEBUG_CHANNEL(console); + + +static CRITICAL_SECTION console_section; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &console_section, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": console_section") } +}; +static CRITICAL_SECTION console_section = { &critsect_debug, -1, 0, 0, 0, 0 }; + +static HANDLE console_connection; +static unsigned int console_flags; + +#define CONSOLE_INPUT_HANDLE 0x01 +#define CONSOLE_OUTPUT_HANDLE 0x02 +#define CONSOLE_ERROR_HANDLE 0x04 + +static WCHAR input_exe[MAX_PATH + 1]; + +struct ctrl_handler +{ + PHANDLER_ROUTINE func; + struct ctrl_handler *next; +}; + +static BOOL WINAPI default_ctrl_handler( DWORD type ) +{ + FIXME( "Terminating process %lx on event %lx\n", GetCurrentProcessId(), type ); + RtlExitUserProcess( 0 ); + return TRUE; +} + +static struct ctrl_handler default_handler = { default_ctrl_handler, NULL }; +static struct ctrl_handler *ctrl_handlers = &default_handler; + +static BOOL console_ioctl( HANDLE handle, DWORD code, void *in_buff, DWORD in_count, + void *out_buff, DWORD out_count, DWORD *read ) +{ + IO_STATUS_BLOCK io; + NTSTATUS status; + + if (handle == CONSOLE_HANDLE_SHELL_NO_WINDOW) + { + WARN("Incorrect access to Shell-no-window console (ioctl=%lx)\n", code); + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io, code, in_buff, in_count, + out_buff, out_count ); + switch( status ) + { + case STATUS_SUCCESS: + if (read) *read = io.Information; + return TRUE; + case STATUS_INVALID_PARAMETER: + break; + default: + status = STATUS_INVALID_HANDLE; + break; + } + if (read) *read = 0; + return set_ntstatus( status ); +} + +/* map input records to ASCII */ +static void input_records_WtoA( INPUT_RECORD *buffer, int count ) +{ + UINT cp = GetConsoleCP(); + int i; + char ch; + + for (i = 0; i < count; i++) + { + if (buffer[i].EventType != KEY_EVENT) continue; + WideCharToMultiByte( cp, 0, &buffer[i].Event.KeyEvent.uChar.UnicodeChar, 1, &ch, 1, NULL, NULL ); + buffer[i].Event.KeyEvent.uChar.AsciiChar = ch; + } +} + +/* map input records to Unicode */ +static void input_records_AtoW( INPUT_RECORD *buffer, int count ) +{ + UINT cp = GetConsoleCP(); + int i; + WCHAR ch; + + for (i = 0; i < count; i++) + { + if (buffer[i].EventType != KEY_EVENT) continue; + MultiByteToWideChar( cp, 0, &buffer[i].Event.KeyEvent.uChar.AsciiChar, 1, &ch, 1 ); + buffer[i].Event.KeyEvent.uChar.UnicodeChar = ch; + } +} + +/* map char infos to ASCII */ +static void char_info_WtoA( UINT cp, CHAR_INFO *buffer, int count ) +{ + char ch; + + while (count-- > 0) + { + WideCharToMultiByte( cp, 0, &buffer->Char.UnicodeChar, 1, &ch, 1, NULL, NULL ); + buffer->Char.AsciiChar = ch; + buffer++; + } +} + +/* map char infos to Unicode */ +static void char_info_AtoW( CHAR_INFO *buffer, int count ) +{ + UINT cp = GetConsoleOutputCP(); + WCHAR ch; + + while (count-- > 0) + { + MultiByteToWideChar( cp, 0, &buffer->Char.AsciiChar, 1, &ch, 1 ); + buffer->Char.UnicodeChar = ch; + buffer++; + } +} + +/* helper function for GetLargestConsoleWindowSize */ +static COORD get_largest_console_window_size( HANDLE handle ) +{ + struct condrv_output_info info; + COORD c = { 0, 0 }; + + if (!console_ioctl( handle, IOCTL_CONDRV_GET_OUTPUT_INFO, NULL, 0, &info, sizeof(info), NULL )) + return c; + + c.X = info.max_width; + c.Y = info.max_height; + TRACE( "(%p), returning %dx%d\n", handle, c.X, c.Y ); + return c; +} + +/* helper function for GetConsoleFontSize */ +static COORD get_console_font_size( HANDLE handle, DWORD index ) +{ + struct condrv_output_info info; + COORD c = {0,0}; + + if (index >= 1 /* number of console fonts */) + { + SetLastError(ERROR_INVALID_PARAMETER); + return c; + } + + if (DeviceIoControl( handle, IOCTL_CONDRV_GET_OUTPUT_INFO, NULL, 0, &info, sizeof(info), NULL, NULL )) + { + c.X = info.font_width; + c.Y = info.font_height; + } + else SetLastError( ERROR_INVALID_HANDLE ); + return c; +} + +/* helper function for GetConsoleTitle and GetConsoleOriginalTitle */ +static DWORD get_console_title( WCHAR *title, DWORD size, BOOL current_title ) +{ + struct condrv_title_params *params; + size_t max_size = sizeof(*params) + (size - 1) * sizeof(WCHAR); + + if (!title || !size) return 0; + + if (!(params = HeapAlloc( GetProcessHeap(), 0, max_size ))) + return 0; + + if (console_ioctl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, IOCTL_CONDRV_GET_TITLE, + ¤t_title, sizeof(current_title), params, max_size, &size ) && + size >= sizeof(*params)) + { + size -= sizeof(*params); + memcpy( title, params->buffer, size ); + title[ size / sizeof(WCHAR) ] = 0; + size = params->title_len; + } + else size = 0; + + HeapFree( GetProcessHeap(), 0, params ); + return size; +} + +static HANDLE create_console_server( void ) +{ + OBJECT_ATTRIBUTES attr = {sizeof(attr)}; + UNICODE_STRING string = RTL_CONSTANT_STRING( L"\\Device\\ConDrv\\Server" ); + IO_STATUS_BLOCK iosb; + HANDLE handle; + NTSTATUS status; + + attr.ObjectName = &string; + attr.Attributes = OBJ_INHERIT; + status = NtCreateFile( &handle, FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES | SYNCHRONIZE, + &attr, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + return set_ntstatus( status ) ? handle : NULL; +} + +static HANDLE create_console_reference( HANDLE root ) +{ + OBJECT_ATTRIBUTES attr = {sizeof(attr)}; + UNICODE_STRING string = RTL_CONSTANT_STRING( L"Reference" ); + IO_STATUS_BLOCK iosb; + HANDLE handle; + NTSTATUS status; + + attr.RootDirectory = root; + attr.ObjectName = &string; + status = NtCreateFile( &handle, FILE_READ_DATA | FILE_WRITE_DATA | FILE_WRITE_PROPERTIES | + FILE_READ_PROPERTIES | SYNCHRONIZE, &attr, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, + 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + return set_ntstatus( status ) ? handle : NULL; +} + +static BOOL create_console_connection( HANDLE root ) +{ + OBJECT_ATTRIBUTES attr = {sizeof(attr)}; + UNICODE_STRING string; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + RtlInitUnicodeString( &string, root ? L"Connection" : L"\\Device\\ConDrv\\Connection" ); + attr.RootDirectory = root; + attr.ObjectName = &string; + status = NtCreateFile( &console_connection, FILE_WRITE_PROPERTIES | FILE_READ_PROPERTIES | SYNCHRONIZE, &attr, + &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, 0 ); + return set_ntstatus( status ); +} + +static BOOL init_console_std_handles( BOOL override_all ) +{ + HANDLE std_out = NULL, std_err = NULL, handle; + OBJECT_ATTRIBUTES attr = {sizeof(attr)}; + IO_STATUS_BLOCK iosb; + UNICODE_STRING name; + NTSTATUS status; + + attr.ObjectName = &name; + attr.Attributes = OBJ_INHERIT; + + if (override_all || !GetStdHandle( STD_INPUT_HANDLE )) + { + RtlInitUnicodeString( &name, L"\\Device\\ConDrv\\Input" ); + status = NtCreateFile( &handle, FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE | FILE_READ_ATTRIBUTES | + FILE_WRITE_ATTRIBUTES, &attr, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_CREATE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + if (!set_ntstatus( status )) return FALSE; + console_flags |= CONSOLE_INPUT_HANDLE; + SetStdHandle( STD_INPUT_HANDLE, handle ); + } + + if (!override_all) + { + std_out = GetStdHandle( STD_OUTPUT_HANDLE ); + std_err = GetStdHandle( STD_ERROR_HANDLE ); + if (std_out && std_err) return TRUE; + } + + RtlInitUnicodeString( &name, L"\\Device\\ConDrv\\Output" ); + status = NtCreateFile( &handle, FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE | FILE_READ_ATTRIBUTES | + FILE_WRITE_ATTRIBUTES, &attr, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_CREATE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + if (!set_ntstatus( status )) return FALSE; + if (!std_out) + { + console_flags |= CONSOLE_OUTPUT_HANDLE; + SetStdHandle( STD_OUTPUT_HANDLE, handle ); + } + + if (!std_err) + { + if (!std_out && !DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), + &handle, 0, TRUE, DUPLICATE_SAME_ACCESS )) + return FALSE; + console_flags |= CONSOLE_ERROR_HANDLE; + SetStdHandle( STD_ERROR_HANDLE, handle ); + } + + return TRUE; +} + + +/****************************************************************** + * AddConsoleAliasA (kernelbase.@) + */ +BOOL WINAPI AddConsoleAliasA( LPSTR source, LPSTR target, LPSTR exename ) +{ + FIXME( ": (%s, %s, %s) stub!\n", debugstr_a(source), debugstr_a(target), debugstr_a(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/****************************************************************** + * AddConsoleAliasW (kernelbase.@) + */ +BOOL WINAPI AddConsoleAliasW( LPWSTR source, LPWSTR target, LPWSTR exename ) +{ + FIXME( ": (%s, %s, %s) stub!\n", debugstr_w(source), debugstr_w(target), debugstr_w(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/****************************************************************** + * AttachConsole (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH AttachConsole( DWORD pid ) +{ + BOOL ret; + + TRACE( "(%lx)\n", pid ); + + RtlEnterCriticalSection( &console_section ); + + if (RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle) + { + RtlLeaveCriticalSection( &console_section ); + WARN( "console already attached\n" ); + SetLastError( ERROR_ACCESS_DENIED ); + return FALSE; + } + + ret = create_console_connection( NULL ) && + console_ioctl( console_connection, IOCTL_CONDRV_BIND_PID, &pid, sizeof(pid), NULL, 0, NULL ); + if (ret) + { + RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle = create_console_reference( console_connection ); + if (RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle) + { + STARTUPINFOW si; + GetStartupInfoW( &si ); + init_console_std_handles( !(si.dwFlags & STARTF_USESTDHANDLES) ); + } + else ret = FALSE; + } + + if (!ret) FreeConsole(); + RtlLeaveCriticalSection( &console_section ); + return ret; +} + + +static BOOL alloc_console( BOOL headless ) +{ + SECURITY_ATTRIBUTES inheritable_attr = { sizeof(inheritable_attr), NULL, TRUE }; + STARTUPINFOEXW console_si; + STARTUPINFOW app_si; + HANDLE server, console = NULL; + WCHAR buffer[1024], cmd[256], conhost_path[MAX_PATH]; + PROCESS_INFORMATION pi; + SIZE_T size; + void *redir; + BOOL ret; + + TRACE("()\n"); + + RtlEnterCriticalSection( &console_section ); + + if (RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle) + { + /* we already have a console opened on this process, don't create a new one */ + RtlLeaveCriticalSection( &console_section ); + SetLastError( ERROR_ACCESS_DENIED ); + return FALSE; + } + + memset( &console_si, 0, sizeof(console_si) ); + console_si.StartupInfo.cb = sizeof(console_si); + InitializeProcThreadAttributeList( NULL, 1, 0, &size ); + if (!(console_si.lpAttributeList = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; + InitializeProcThreadAttributeList( console_si.lpAttributeList, 1, 0, &size ); + + if (!(server = create_console_server()) || !(console = create_console_reference( server ))) goto error; + + GetStartupInfoW(&app_si); + + /* setup a view arguments for conhost (it'll use them as default values) */ + if (app_si.dwFlags & STARTF_USECOUNTCHARS) + { + console_si.StartupInfo.dwFlags |= STARTF_USECOUNTCHARS; + console_si.StartupInfo.dwXCountChars = app_si.dwXCountChars; + console_si.StartupInfo.dwYCountChars = app_si.dwYCountChars; + } + if (app_si.dwFlags & STARTF_USEFILLATTRIBUTE) + { + console_si.StartupInfo.dwFlags |= STARTF_USEFILLATTRIBUTE; + console_si.StartupInfo.dwFillAttribute = app_si.dwFillAttribute; + } + if (app_si.dwFlags & STARTF_USESHOWWINDOW) + { + console_si.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW; + console_si.StartupInfo.wShowWindow = app_si.wShowWindow; + } + if (app_si.lpTitle) + console_si.StartupInfo.lpTitle = app_si.lpTitle; + else if (GetModuleFileNameW(0, buffer, ARRAY_SIZE(buffer))) + { + buffer[ARRAY_SIZE(buffer) - 1] = 0; + console_si.StartupInfo.lpTitle = buffer; + } + + + UpdateProcThreadAttribute( console_si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + &server, sizeof(server), NULL, NULL ); + swprintf( conhost_path, ARRAY_SIZE(conhost_path), L"%s\\conhost.exe", system_dir ); + swprintf( cmd, ARRAY_SIZE(cmd), L"\"%s\" --server 0x%x", conhost_path, condrv_handle( server )); + if (headless) wcscat( cmd, L" --headless" ); + Wow64DisableWow64FsRedirection( &redir ); + ret = CreateProcessW( conhost_path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | EXTENDED_STARTUPINFO_PRESENT, + NULL, NULL, &console_si.StartupInfo, &pi ); + Wow64RevertWow64FsRedirection( redir ); + + if (!ret || !create_console_connection( console)) goto error; + if (!init_console_std_handles( !(app_si.dwFlags & STARTF_USESTDHANDLES) )) goto error; + + RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle = console; + TRACE( "Started conhost pid=%08lx tid=%08lx\n", pi.dwProcessId, pi.dwThreadId ); + + HeapFree( GetProcessHeap(), 0, console_si.lpAttributeList ); + CloseHandle( server ); + RtlLeaveCriticalSection( &console_section ); + SetLastError( ERROR_SUCCESS ); + return TRUE; + +error: + ERR("Can't allocate console\n"); + HeapFree( GetProcessHeap(), 0, console_si.lpAttributeList ); + NtClose( console ); + NtClose( server ); + FreeConsole(); + RtlLeaveCriticalSection( &console_section ); + return FALSE; +} + + +/****************************************************************** + * AllocConsole (kernelbase.@) + */ +BOOL WINAPI AllocConsole(void) +{ + return alloc_console( FALSE ); +} + + +/****************************************************************************** + * CreateConsoleScreenBuffer (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateConsoleScreenBuffer( DWORD access, DWORD share, + SECURITY_ATTRIBUTES *sa, DWORD flags, + void *data ) +{ + OBJECT_ATTRIBUTES attr = {sizeof(attr)}; + IO_STATUS_BLOCK iosb; + UNICODE_STRING name = RTL_CONSTANT_STRING( L"\\Device\\ConDrv\\ScreenBuffer" ); + HANDLE handle; + NTSTATUS status; + + TRACE( "(%lx,%lx,%p,%lx,%p)\n", access, share, sa, flags, data ); + + if (flags != CONSOLE_TEXTMODE_BUFFER || data) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return INVALID_HANDLE_VALUE; + } + + attr.ObjectName = &name; + attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL; + if (sa && sa->bInheritHandle) attr.Attributes |= OBJ_INHERIT; + status = NtCreateFile( &handle, access, &attr, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, + FILE_NON_DIRECTORY_FILE, NULL, 0 ); + return set_ntstatus( status ) ? handle : INVALID_HANDLE_VALUE; +} + + +/****************************************************************************** + * CtrlRoutine (kernelbase.@) + */ +DWORD WINAPI CtrlRoutine( void *arg ) +{ + DWORD_PTR event = (DWORD_PTR)arg; + struct ctrl_handler *handler; + + if (event == CTRL_C_EVENT) + { + BOOL caught_by_dbg = TRUE; + /* First, try to pass the ctrl-C event to the debugger (if any) + * If it continues, there's nothing more to do + * Otherwise, we need to send the ctrl-C event to the handlers + */ + __TRY + { + RaiseException( DBG_CONTROL_C, 0, 0, NULL ); + } + __EXCEPT_ALL + { + caught_by_dbg = FALSE; + } + __ENDTRY + if (caught_by_dbg) return 0; + } + + if (NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags & 1) return 0; + + RtlEnterCriticalSection( &console_section ); + for (handler = ctrl_handlers; handler; handler = handler->next) + { + if (handler->func( event )) break; + } + RtlLeaveCriticalSection( &console_section ); + return 1; +} + + +/****************************************************************** + * ExpungeConsoleCommandHistoryA (kernelbase.@) + */ +void WINAPI ExpungeConsoleCommandHistoryA( LPCSTR exename ) +{ + FIXME( ": (%s) stub!\n", debugstr_a(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); +} + + +/****************************************************************** + * ExpungeConsoleCommandHistoryW (kernelbase.@) + */ +void WINAPI ExpungeConsoleCommandHistoryW( LPCWSTR exename ) +{ + FIXME( ": (%s) stub!\n", debugstr_w(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); +} + + +/****************************************************************************** + * FillConsoleOutputAttribute (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FillConsoleOutputAttribute( HANDLE handle, WORD attr, DWORD length, + COORD coord, DWORD *written ) +{ + struct condrv_fill_output_params params; + + TRACE( "(%p,%d,%ld,(%dx%d),%p)\n", handle, attr, length, coord.X, coord.Y, written ); + + if (!written) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + *written = 0; + + params.mode = CHAR_INFO_MODE_ATTR; + params.x = coord.X; + params.y = coord.Y; + params.count = length; + params.wrap = TRUE; + params.ch = 0; + params.attr = attr; + return console_ioctl( handle, IOCTL_CONDRV_FILL_OUTPUT, ¶ms, sizeof(params), + written, sizeof(*written), NULL ); +} + + +/****************************************************************************** + * FillConsoleOutputCharacterA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FillConsoleOutputCharacterA( HANDLE handle, CHAR ch, DWORD length, + COORD coord, DWORD *written ) +{ + WCHAR wch; + + MultiByteToWideChar( GetConsoleOutputCP(), 0, &ch, 1, &wch, 1 ); + return FillConsoleOutputCharacterW( handle, wch, length, coord, written ); +} + + +/****************************************************************************** + * FillConsoleOutputCharacterW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FillConsoleOutputCharacterW( HANDLE handle, WCHAR ch, DWORD length, + COORD coord, DWORD *written ) +{ + struct condrv_fill_output_params params; + + TRACE( "(%p,%s,%ld,(%dx%d),%p)\n", handle, debugstr_wn(&ch, 1), length, coord.X, coord.Y, written ); + + if (!written) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + *written = 0; + + params.mode = CHAR_INFO_MODE_TEXT; + params.x = coord.X; + params.y = coord.Y; + params.count = length; + params.wrap = TRUE; + params.ch = ch; + params.attr = 0; + return console_ioctl( handle, IOCTL_CONDRV_FILL_OUTPUT, ¶ms, sizeof(params), + written, sizeof(*written), NULL ); +} + + +/*********************************************************************** + * FreeConsole (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FreeConsole(void) +{ + RtlEnterCriticalSection( &console_section ); + + if (RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle != CONSOLE_HANDLE_SHELL_NO_WINDOW) + { + NtClose( console_connection ); + console_connection = NULL; + + NtClose( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle ); + } + RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle = NULL; + + if (console_flags & CONSOLE_INPUT_HANDLE) NtClose( GetStdHandle( STD_INPUT_HANDLE )); + if (console_flags & CONSOLE_OUTPUT_HANDLE) NtClose( GetStdHandle( STD_OUTPUT_HANDLE )); + if (console_flags & CONSOLE_ERROR_HANDLE) NtClose( GetStdHandle( STD_ERROR_HANDLE )); + console_flags = 0; + + RtlLeaveCriticalSection( &console_section ); + return TRUE; +} + + +/****************************************************************************** + * GenerateConsoleCtrlEvent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GenerateConsoleCtrlEvent( DWORD event, DWORD group ) +{ + struct condrv_ctrl_event ctrl_event; + + TRACE( "(%ld, %lx)\n", event, group ); + + if (event != CTRL_C_EVENT && event != CTRL_BREAK_EVENT) + { + ERR( "Invalid event %ld for PGID %lx\n", event, group ); + return FALSE; + } + + ctrl_event.event = event; + ctrl_event.group_id = group; + return console_ioctl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, + IOCTL_CONDRV_CTRL_EVENT, &ctrl_event, sizeof(ctrl_event), NULL, 0, NULL ); +} + + +/****************************************************************** + * GetConsoleAliasA (kernelbase.@) + */ +DWORD WINAPI GetConsoleAliasA( LPSTR source, LPSTR buffer, DWORD len, LPSTR exename ) +{ + FIXME( "(%s,%p,%ld,%s): stub\n", debugstr_a(source), buffer, len, debugstr_a(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleAliasW (kernelbase.@) + */ +DWORD WINAPI GetConsoleAliasW( LPWSTR source, LPWSTR buffer, DWORD len, LPWSTR exename ) +{ + FIXME( "(%s,%p,%ld,%s): stub\n", debugstr_w(source), buffer, len, debugstr_w(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleAliasExesLengthA (kernelbase.@) + */ +DWORD WINAPI GetConsoleAliasExesLengthA(void) +{ + FIXME( ": stub\n" ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleAliasExesLengthW (kernelbase.@) + */ +DWORD WINAPI GetConsoleAliasExesLengthW(void) +{ + FIXME( ": stub\n" ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleAliasesLengthA (kernelbase.@) + */ +DWORD WINAPI GetConsoleAliasesLengthA( LPSTR unknown ) +{ + FIXME( ": (%s) stub!\n", debugstr_a(unknown) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleAliasesLengthW (kernelbase.@) + */ +DWORD WINAPI GetConsoleAliasesLengthW( LPWSTR unknown ) +{ + FIXME( ": (%s) stub!\n", debugstr_w(unknown) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleCommandHistoryA (kernelbase.@) + */ +DWORD WINAPI GetConsoleCommandHistoryA( LPSTR buffer, DWORD len, LPCSTR exename ) +{ + FIXME( ": (%p, 0x%lx, %s) stub\n", buffer, len, debugstr_a(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleCommandHistoryW (kernelbase.@) + */ +DWORD WINAPI GetConsoleCommandHistoryW( LPWSTR buffer, DWORD len, LPCWSTR exename ) +{ + FIXME( ": (%p, 0x%lx, %s) stub\n", buffer, len, debugstr_w(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleCommandHistoryLengthA (kernelbase.@) + */ +DWORD WINAPI GetConsoleCommandHistoryLengthA( LPCSTR exename ) +{ + FIXME( ": (%s) stub!\n", debugstr_a(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************** + * GetConsoleCommandHistoryLengthW (kernelbase.@) + */ +DWORD WINAPI GetConsoleCommandHistoryLengthW( LPCWSTR exename ) +{ + FIXME( ": (%s) stub!\n", debugstr_w(exename) ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + + +/****************************************************************************** + * GetConsoleCP (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetConsoleCP(void) +{ + struct condrv_input_info info; + + if (!console_ioctl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, + IOCTL_CONDRV_GET_INPUT_INFO, NULL, 0, &info, sizeof(info), NULL )) + return 0; + return info.input_cp; +} + + +/****************************************************************************** + * GetConsoleCursorInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetConsoleCursorInfo( HANDLE handle, CONSOLE_CURSOR_INFO *info ) +{ + struct condrv_output_info condrv_info; + + if (!console_ioctl( handle, IOCTL_CONDRV_GET_OUTPUT_INFO, NULL, 0, &condrv_info, sizeof(condrv_info), NULL )) + return FALSE; + + if (!info) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + info->dwSize = condrv_info.cursor_size; + info->bVisible = condrv_info.cursor_visible; + TRACE("(%p) returning (%ld,%d)\n", handle, info->dwSize, info->bVisible); + return TRUE; +} + + +/*********************************************************************** + * GetConsoleDisplayMode (kernelbase.@) + */ +BOOL WINAPI GetConsoleDisplayMode( LPDWORD flags ) +{ + TRACE( "semi-stub: %p\n", flags ); + /* It is safe to successfully report windowed mode */ + *flags = 0; + return TRUE; +} + + +/*********************************************************************** + * GetConsoleFontSize (kernelbase.@) + */ +#if defined(__i386__) && !defined(__MINGW32__) && !defined(_MSC_VER) +#undef GetConsoleFontSize +DWORD WINAPI GetConsoleFontSize( HANDLE handle, DWORD index ) +{ + union { + COORD c; + DWORD w; + } x; + + x.c = get_console_font_size( handle, index ); + return x.w; +} +#else +COORD WINAPI GetConsoleFontSize( HANDLE handle, DWORD index ) +{ + return get_console_font_size( handle, index ); +} +#endif /* !defined(__i386__) */ + + +/*********************************************************************** + * GetConsoleInputExeNameA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetConsoleInputExeNameA( DWORD len, LPSTR buffer ) +{ + RtlEnterCriticalSection( &console_section ); + if (WideCharToMultiByte( CP_ACP, 0, input_exe, -1, NULL, 0, NULL, NULL ) <= len) + WideCharToMultiByte( CP_ACP, 0, input_exe, -1, buffer, len, NULL, NULL ); + else SetLastError(ERROR_BUFFER_OVERFLOW); + RtlLeaveCriticalSection( &console_section ); + return TRUE; +} + + +/*********************************************************************** + * GetConsoleInputExeNameW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetConsoleInputExeNameW( DWORD len, LPWSTR buffer ) +{ + RtlEnterCriticalSection( &console_section ); + if (len > lstrlenW(input_exe)) lstrcpyW( buffer, input_exe ); + else SetLastError( ERROR_BUFFER_OVERFLOW ); + RtlLeaveCriticalSection( &console_section ); + return TRUE; +} + + +/*********************************************************************** + * GetConsoleMode (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetConsoleMode( HANDLE handle, DWORD *mode ) +{ + return console_ioctl( handle, IOCTL_CONDRV_GET_MODE, NULL, 0, mode, sizeof(*mode), NULL ); +} + + +/*********************************************************************** + * GetConsoleOriginalTitleA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetConsoleOriginalTitleA( LPSTR title, DWORD size ) +{ + WCHAR *ptr = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ); + DWORD ret; + + if (!ptr) return 0; + + ret = GetConsoleOriginalTitleW( ptr, size ); + if (ret) + WideCharToMultiByte( GetConsoleOutputCP(), 0, ptr, -1, title, size, NULL, NULL); + + HeapFree( GetProcessHeap(), 0, ptr ); + return ret; +} + + +/*********************************************************************** + * GetConsoleOriginalTitleW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetConsoleOriginalTitleW( LPWSTR title, DWORD size ) +{ + return get_console_title( title, size, FALSE ); +} + + +/*********************************************************************** + * GetConsoleOutputCP (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetConsoleOutputCP(void) +{ + struct condrv_input_info info; + + if (!console_ioctl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, + IOCTL_CONDRV_GET_INPUT_INFO, NULL, 0, &info, sizeof(info), NULL )) + return 0; + return info.output_cp; +} + + +/*********************************************************************** + * GetConsoleProcessList (kernelbase.@) + */ +DWORD WINAPI GetConsoleProcessList( DWORD *list, DWORD count ) +{ + DWORD saved; + NTSTATUS status; + IO_STATUS_BLOCK io; + + TRACE( "(%p,%ld)\n", list, count); + + if (!list || count < 1) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + saved = *list; + status = NtDeviceIoControlFile( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, + NULL, NULL, NULL, &io, IOCTL_CONDRV_GET_PROCESS_LIST, + NULL, 0, list, count * sizeof(DWORD) ); + + if (!status) return io.Information / sizeof(DWORD); + if (status == STATUS_BUFFER_TOO_SMALL) + { + DWORD ret = *list; + *list = saved; + return ret; + } + + *list = saved; + set_ntstatus( status ); + return 0; +} + + +/*********************************************************************** + * GetConsoleScreenBufferInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetConsoleScreenBufferInfo( HANDLE handle, CONSOLE_SCREEN_BUFFER_INFO *info ) +{ + struct condrv_output_info condrv_info; + + if (!console_ioctl( handle , IOCTL_CONDRV_GET_OUTPUT_INFO, NULL, 0, + &condrv_info, sizeof(condrv_info), NULL )) + return FALSE; + + info->dwSize.X = condrv_info.width; + info->dwSize.Y = condrv_info.height; + info->dwCursorPosition.X = condrv_info.cursor_x; + info->dwCursorPosition.Y = condrv_info.cursor_y; + info->wAttributes = condrv_info.attr; + info->srWindow.Left = condrv_info.win_left; + info->srWindow.Right = condrv_info.win_right; + info->srWindow.Top = condrv_info.win_top; + info->srWindow.Bottom = condrv_info.win_bottom; + info->dwMaximumWindowSize.X = min(condrv_info.width, condrv_info.max_width); + info->dwMaximumWindowSize.Y = min(condrv_info.height, condrv_info.max_height); + + TRACE( "(%p,(%d,%d) (%d,%d) %d (%d,%d-%d,%d) (%d,%d)\n", handle, + info->dwSize.X, info->dwSize.Y, info->dwCursorPosition.X, info->dwCursorPosition.Y, + info->wAttributes, info->srWindow.Left, info->srWindow.Top, info->srWindow.Right, + info->srWindow.Bottom, info->dwMaximumWindowSize.X, info->dwMaximumWindowSize.Y ); + return TRUE; +} + + +/*********************************************************************** + * GetConsoleScreenBufferInfoEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetConsoleScreenBufferInfoEx( HANDLE handle, + CONSOLE_SCREEN_BUFFER_INFOEX *info ) +{ + struct condrv_output_info condrv_info; + + if (info->cbSize != sizeof(CONSOLE_SCREEN_BUFFER_INFOEX)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (!console_ioctl( handle, IOCTL_CONDRV_GET_OUTPUT_INFO, NULL, 0, &condrv_info, + sizeof(condrv_info), NULL )) + return FALSE; + + info->dwSize.X = condrv_info.width; + info->dwSize.Y = condrv_info.height; + info->dwCursorPosition.X = condrv_info.cursor_x; + info->dwCursorPosition.Y = condrv_info.cursor_y; + info->wAttributes = condrv_info.attr; + info->srWindow.Left = condrv_info.win_left; + info->srWindow.Top = condrv_info.win_top; + info->srWindow.Right = condrv_info.win_right; + info->srWindow.Bottom = condrv_info.win_bottom; + info->dwMaximumWindowSize.X = min( condrv_info.width, condrv_info.max_width ); + info->dwMaximumWindowSize.Y = min( condrv_info.height, condrv_info.max_height ); + info->wPopupAttributes = condrv_info.popup_attr; + info->bFullscreenSupported = FALSE; + memcpy( info->ColorTable, condrv_info.color_map, sizeof(info->ColorTable) ); + return TRUE; +} + + +/****************************************************************************** + * GetConsoleTitleA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetConsoleTitleA( LPSTR title, DWORD size ) +{ + WCHAR *ptr = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * size ); + DWORD ret; + + if (!ptr) return 0; + + ret = GetConsoleTitleW( ptr, size ); + if (ret) + WideCharToMultiByte( GetConsoleOutputCP(), 0, ptr, -1, title, size, NULL, NULL); + + HeapFree( GetProcessHeap(), 0, ptr ); + return ret; +} + + +/****************************************************************************** + * GetConsoleTitleW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetConsoleTitleW( LPWSTR title, DWORD size ) +{ + return get_console_title( title, size, TRUE ); +} + + +/****************************************************************************** + * GetConsoleWindow (kernelbase.@) + */ +HWND WINAPI GetConsoleWindow(void) +{ + condrv_handle_t win; + BOOL ret; + + ret = DeviceIoControl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, + IOCTL_CONDRV_GET_WINDOW, NULL, 0, &win, sizeof(win), NULL, NULL ); + return ret ? LongToHandle( win ) : NULL; +} + + +/*********************************************************************** + * GetCurrentConsoleFontEx (kernelbase.@) + */ +BOOL WINAPI GetCurrentConsoleFontEx( HANDLE handle, BOOL maxwindow, CONSOLE_FONT_INFOEX *info ) +{ + DWORD size; + struct + { + struct condrv_output_info info; + WCHAR face_name[LF_FACESIZE - 1]; + } data; + + if (info->cbSize != sizeof(CONSOLE_FONT_INFOEX)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (!DeviceIoControl( handle, IOCTL_CONDRV_GET_OUTPUT_INFO, NULL, 0, + &data, sizeof(data), &size, NULL )) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + info->nFont = 0; + if (maxwindow) + { + info->dwFontSize.X = min( data.info.width, data.info.max_width ); + info->dwFontSize.Y = min( data.info.height, data.info.max_height ); + } + else + { + info->dwFontSize.X = data.info.font_width; + info->dwFontSize.Y = data.info.font_height; + } + size -= sizeof(data.info); + if (size) memcpy( info->FaceName, data.face_name, size ); + info->FaceName[size / sizeof(WCHAR)] = 0; + info->FontFamily = data.info.font_pitch_family; + info->FontWeight = data.info.font_weight; + return TRUE; +} + + +/*********************************************************************** + * GetCurrentConsoleFont (kernelbase.@) + */ +BOOL WINAPI GetCurrentConsoleFont( HANDLE handle, BOOL maxwindow, CONSOLE_FONT_INFO *info ) +{ + BOOL ret; + CONSOLE_FONT_INFOEX res; + + res.cbSize = sizeof(CONSOLE_FONT_INFOEX); + + ret = GetCurrentConsoleFontEx( handle, maxwindow, &res ); + if (ret) + { + info->nFont = res.nFont; + info->dwFontSize.X = res.dwFontSize.X; + info->dwFontSize.Y = res.dwFontSize.Y; + } + return ret; +} + + +/*********************************************************************** + * GetLargestConsoleWindowSize (kernelbase.@) + */ +#if defined(__i386__) && !defined(__MINGW32__) && !defined(_MSC_VER) +#undef GetLargestConsoleWindowSize +DWORD WINAPI DECLSPEC_HOTPATCH GetLargestConsoleWindowSize( HANDLE handle ) +{ + union { + COORD c; + DWORD w; + } x; + x.c = get_largest_console_window_size( handle ); + return x.w; +} + +#else + +COORD WINAPI DECLSPEC_HOTPATCH GetLargestConsoleWindowSize( HANDLE handle ) +{ + return get_largest_console_window_size( handle ); +} + +#endif /* !defined(__i386__) */ + + +/*********************************************************************** + * GetNumberOfConsoleInputEvents (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetNumberOfConsoleInputEvents( HANDLE handle, DWORD *count ) +{ + return console_ioctl( handle, IOCTL_CONDRV_GET_INPUT_COUNT, NULL, 0, + count, sizeof(*count), NULL ); +} + + +/*********************************************************************** + * GetNumberOfConsoleMouseButtons (kernelbase.@) + */ +BOOL WINAPI GetNumberOfConsoleMouseButtons( DWORD *count ) +{ + FIXME( "(%p): stub\n", count ); + *count = 2; + return TRUE; +} + + +/*********************************************************************** + * PeekConsoleInputA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH PeekConsoleInputA( HANDLE handle, INPUT_RECORD *buffer, + DWORD length, DWORD *count ) +{ + DWORD read; + + if (!PeekConsoleInputW( handle, buffer, length, &read )) return FALSE; + input_records_WtoA( buffer, read ); + if (count) *count = read; + return TRUE; +} + + +/*********************************************************************** + * PeekConsoleInputW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH PeekConsoleInputW( HANDLE handle, INPUT_RECORD *buffer, + DWORD length, DWORD *count ) +{ + DWORD read; + if (!console_ioctl( handle, IOCTL_CONDRV_PEEK, NULL, 0, buffer, length * sizeof(*buffer), &read )) + return FALSE; + if (count) *count = read / sizeof(*buffer); + return TRUE; +} + + +/****************************************************************************** + * ReadConsoleOutputAttribute (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadConsoleOutputAttribute( HANDLE handle, WORD *attr, DWORD length, + COORD coord, DWORD *count ) +{ + struct condrv_output_params params; + BOOL ret; + + TRACE( "(%p,%p,%ld,%dx%d,%p)\n", handle, attr, length, coord.X, coord.Y, count ); + + if (!count) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + params.mode = CHAR_INFO_MODE_ATTR; + params.x = coord.X; + params.y = coord.Y; + params.width = 0; + ret = console_ioctl( handle, IOCTL_CONDRV_READ_OUTPUT, ¶ms, sizeof(params), + attr, length * sizeof(*attr), count ); + *count /= sizeof(*attr); + return ret; +} + + +/****************************************************************************** + * ReadConsoleOutputCharacterA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadConsoleOutputCharacterA( HANDLE handle, LPSTR buffer, DWORD length, + COORD coord, DWORD *count ) +{ + DWORD read; + BOOL ret; + LPWSTR wptr; + + if (!count) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + *count = 0; + if (!(wptr = HeapAlloc( GetProcessHeap(), 0, length * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + if ((ret = ReadConsoleOutputCharacterW( handle, wptr, length, coord, &read ))) + { + read = WideCharToMultiByte( GetConsoleOutputCP(), 0, wptr, read, buffer, length, NULL, NULL); + *count = read; + } + HeapFree( GetProcessHeap(), 0, wptr ); + return ret; +} + + +/****************************************************************************** + * ReadConsoleOutputCharacterW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadConsoleOutputCharacterW( HANDLE handle, LPWSTR buffer, DWORD length, + COORD coord, DWORD *count ) +{ + struct condrv_output_params params; + BOOL ret; + + TRACE( "(%p,%p,%ld,%dx%d,%p)\n", handle, buffer, length, coord.X, coord.Y, count ); + + if (!count) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + params.mode = CHAR_INFO_MODE_TEXT; + params.x = coord.X; + params.y = coord.Y; + params.width = 0; + ret = console_ioctl( handle, IOCTL_CONDRV_READ_OUTPUT, ¶ms, sizeof(params), buffer, + length * sizeof(*buffer), count ); + *count /= sizeof(*buffer); + return ret; +} + + +/****************************************************************************** + * ReadConsoleOutputA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadConsoleOutputA( HANDLE handle, CHAR_INFO *buffer, COORD size, + COORD coord, SMALL_RECT *region ) +{ + BOOL ret; + int y; + + ret = ReadConsoleOutputW( handle, buffer, size, coord, region ); + if (ret && region->Right >= region->Left) + { + UINT cp = GetConsoleOutputCP(); + for (y = 0; y <= region->Bottom - region->Top; y++) + char_info_WtoA( cp, &buffer[(coord.Y + y) * size.X + coord.X], region->Right - region->Left + 1 ); + } + return ret; +} + + +/****************************************************************************** + * ReadConsoleOutputW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadConsoleOutputW( HANDLE handle, CHAR_INFO *buffer, COORD size, + COORD coord, SMALL_RECT *region ) +{ + struct condrv_output_params params; + unsigned int width, height, y; + SMALL_RECT *result; + DWORD count; + BOOL ret; + + if (region->Left > region->Right || region->Top > region->Bottom) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + if (size.X <= coord.X || size.Y <= coord.Y) + { + region->Right = region->Left - 1; + region->Bottom = region->Top - 1; + SetLastError( ERROR_INVALID_FUNCTION ); + return FALSE; + } + width = min( region->Right - region->Left + 1, size.X - coord.X ); + height = min( region->Bottom - region->Top + 1, size.Y - coord.Y ); + region->Right = region->Left + width - 1; + region->Bottom = region->Top + height - 1; + + count = sizeof(*result) + width * height * sizeof(*buffer); + if (!(result = HeapAlloc( GetProcessHeap(), 0, count ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + params.mode = CHAR_INFO_MODE_TEXTATTR; + params.x = region->Left; + params.y = region->Top; + params.width = width; + if ((ret = console_ioctl( handle, IOCTL_CONDRV_READ_OUTPUT, ¶ms, sizeof(params), result, count, &count )) && count) + { + CHAR_INFO *char_info = (CHAR_INFO *)(result + 1); + *region = *result; + width = region->Right - region->Left + 1; + height = region->Bottom - region->Top + 1; + for (y = 0; y < height; y++) + memcpy( &buffer[(y + coord.Y) * size.X + coord.X], &char_info[y * width], width * sizeof(*buffer) ); + } + HeapFree( GetProcessHeap(), 0, result ); + return ret; +} + + +/****************************************************************************** + * ScrollConsoleScreenBufferA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ScrollConsoleScreenBufferA( HANDLE handle, const SMALL_RECT *scroll, + const SMALL_RECT *clip, COORD origin, const CHAR_INFO *fill ) +{ + CHAR_INFO ciW; + + ciW.Attributes = fill->Attributes; + MultiByteToWideChar( GetConsoleOutputCP(), 0, &fill->Char.AsciiChar, 1, &ciW.Char.UnicodeChar, 1 ); + + return ScrollConsoleScreenBufferW( handle, scroll, clip, origin, &ciW ); +} + + +/****************************************************************************** + * ScrollConsoleScreenBufferW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ScrollConsoleScreenBufferW( HANDLE handle, const SMALL_RECT *scroll, + const SMALL_RECT *clip_rect, COORD origin, + const CHAR_INFO *fill ) +{ + struct condrv_scroll_params params; + + if (clip_rect) + TRACE( "(%p,(%d,%d-%d,%d),(%d,%d-%d,%d),%d-%d,%p)\n", handle, + scroll->Left, scroll->Top, scroll->Right, scroll->Bottom, + clip_rect->Left, clip_rect->Top, clip_rect->Right, clip_rect->Bottom, + origin.X, origin.Y, fill ); + else + TRACE("(%p,(%d,%d-%d,%d),(nil),%d-%d,%p)\n", handle, + scroll->Left, scroll->Top, scroll->Right, scroll->Bottom, + origin.X, origin.Y, fill ); + + params.scroll = *scroll; + params.origin = origin; + params.fill.ch = fill->Char.UnicodeChar; + params.fill.attr = fill->Attributes; + if (!clip_rect) + { + params.clip.Left = params.clip.Top = 0; + params.clip.Right = params.clip.Bottom = SHRT_MAX; + } + else params.clip = *clip_rect; + return console_ioctl( handle, IOCTL_CONDRV_SCROLL, (void *)¶ms, sizeof(params), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleActiveScreenBuffer (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleActiveScreenBuffer( HANDLE handle ) +{ + TRACE( "(%p)\n", handle ); + return console_ioctl( handle, IOCTL_CONDRV_ACTIVATE, NULL, 0, NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleCP (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleCP( UINT cp ) +{ + struct condrv_input_info_params params = { SET_CONSOLE_INPUT_INFO_INPUT_CODEPAGE }; + + params.info.input_cp = cp; + return console_ioctl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, + IOCTL_CONDRV_SET_INPUT_INFO, ¶ms, sizeof(params), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleCtrlHandler (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleCtrlHandler( PHANDLER_ROUTINE func, BOOL add ) +{ + struct ctrl_handler *handler; + BOOL ret = FALSE; + + TRACE( "(%p,%d)\n", func, add ); + + RtlEnterCriticalSection( &console_section ); + + if (!func) + { + if (add) NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags |= 1; + else NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags &= ~1; + ret = TRUE; + } + else if (add) + { + if ((handler = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler) ))) + { + handler->func = func; + handler->next = ctrl_handlers; + ctrl_handlers = handler; + ret = TRUE; + } + } + else + { + struct ctrl_handler **p_handler; + + for (p_handler = &ctrl_handlers; *p_handler; p_handler = &(*p_handler)->next) + { + if ((*p_handler)->func == func) break; + } + if (*p_handler && *p_handler != &default_handler) + { + handler = *p_handler; + *p_handler = handler->next; + RtlFreeHeap( GetProcessHeap(), 0, handler ); + ret = TRUE; + } + else SetLastError( ERROR_INVALID_PARAMETER ); + } + + RtlLeaveCriticalSection( &console_section ); + return ret; +} + + +/****************************************************************************** + * SetConsoleCursorInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleCursorInfo( HANDLE handle, CONSOLE_CURSOR_INFO *info ) +{ + struct condrv_output_info_params params = { SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM }; + + TRACE( "(%p,%ld,%d)\n", handle, info->dwSize, info->bVisible); + + params.info.cursor_size = info->dwSize; + params.info.cursor_visible = info->bVisible; + return console_ioctl( handle, IOCTL_CONDRV_SET_OUTPUT_INFO, ¶ms, sizeof(params), + NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleCursorPosition (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleCursorPosition( HANDLE handle, COORD pos ) +{ + struct condrv_output_info_params params = { SET_CONSOLE_OUTPUT_INFO_CURSOR_POS }; + + TRACE( "%p %d %d\n", handle, pos.X, pos.Y ); + + params.info.cursor_x = pos.X; + params.info.cursor_y = pos.Y; + return console_ioctl( handle, IOCTL_CONDRV_SET_OUTPUT_INFO, ¶ms, sizeof(params), NULL, 0, NULL ); +} + + +/*********************************************************************** + * SetConsoleDisplayMode (kernelbase.@) + */ +BOOL WINAPI SetConsoleDisplayMode( HANDLE handle, DWORD flags, COORD *size ) +{ + TRACE( "(%p, %lx, (%d, %d))\n", handle, flags, size->X, size->Y ); + if (flags == 1) + { + /* We cannot switch to fullscreen */ + return FALSE; + } + return TRUE; +} + + +/****************************************************************************** + * SetConsoleInputExeNameA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleInputExeNameA( LPCSTR name ) +{ + if (!name || !name[0]) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + RtlEnterCriticalSection( &console_section ); + MultiByteToWideChar( CP_ACP, 0, name, -1, input_exe, ARRAY_SIZE(input_exe) ); + RtlLeaveCriticalSection( &console_section ); + return TRUE; +} + + +/****************************************************************************** + * SetConsoleInputExeNameW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleInputExeNameW( LPCWSTR name ) +{ + if (!name || !name[0]) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + RtlEnterCriticalSection( &console_section ); + lstrcpynW( input_exe, name, ARRAY_SIZE(input_exe) ); + RtlLeaveCriticalSection( &console_section ); + return TRUE; +} + + +/****************************************************************************** + * SetConsoleMode (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleMode( HANDLE handle, DWORD mode ) +{ + TRACE( "(%p,%lx)\n", handle, mode ); + return console_ioctl( handle, IOCTL_CONDRV_SET_MODE, &mode, sizeof(mode), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleOutputCP (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleOutputCP( UINT cp ) +{ + struct condrv_input_info_params params = { SET_CONSOLE_INPUT_INFO_OUTPUT_CODEPAGE }; + + params.info.output_cp = cp; + return console_ioctl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, + IOCTL_CONDRV_SET_INPUT_INFO, ¶ms, sizeof(params), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleScreenBufferInfoEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleScreenBufferInfoEx( HANDLE handle, + CONSOLE_SCREEN_BUFFER_INFOEX *info ) +{ + struct condrv_output_info_params params = + { SET_CONSOLE_OUTPUT_INFO_CURSOR_POS | SET_CONSOLE_OUTPUT_INFO_SIZE | + SET_CONSOLE_OUTPUT_INFO_ATTR | SET_CONSOLE_OUTPUT_INFO_POPUP_ATTR | + SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW | SET_CONSOLE_OUTPUT_INFO_MAX_SIZE }; + + TRACE("(%p, %p)\n", handle, info); + + if (info->cbSize != sizeof(CONSOLE_SCREEN_BUFFER_INFOEX)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + params.info.width = info->dwSize.X; + params.info.height = info->dwSize.Y; + params.info.cursor_x = info->dwCursorPosition.X; + params.info.cursor_y = info->dwCursorPosition.Y; + params.info.attr = info->wAttributes; + params.info.win_left = info->srWindow.Left; + params.info.win_top = info->srWindow.Top; + params.info.win_right = info->srWindow.Right; + params.info.win_bottom = info->srWindow.Bottom; + params.info.popup_attr = info->wPopupAttributes; + params.info.max_width = min( info->dwMaximumWindowSize.X, info->dwSize.X ); + params.info.max_height = min( info->dwMaximumWindowSize.Y, info->dwSize.Y ); + return console_ioctl( handle, IOCTL_CONDRV_SET_OUTPUT_INFO, ¶ms, sizeof(params), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleScreenBufferSize (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleScreenBufferSize( HANDLE handle, COORD size ) +{ + struct condrv_output_info_params params = { SET_CONSOLE_OUTPUT_INFO_SIZE }; + + TRACE( "(%p,(%d,%d))\n", handle, size.X, size.Y ); + + params.info.width = size.X; + params.info.height = size.Y; + return console_ioctl( handle, IOCTL_CONDRV_SET_OUTPUT_INFO, ¶ms, sizeof(params), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleTextAttribute (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleTextAttribute( HANDLE handle, WORD attr ) +{ + struct condrv_output_info_params params = { SET_CONSOLE_OUTPUT_INFO_ATTR }; + + TRACE( "(%p,%d)\n", handle, attr ); + + params.info.attr = attr; + return console_ioctl( handle, IOCTL_CONDRV_SET_OUTPUT_INFO, ¶ms, sizeof(params), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleTitleA (kernelbase.@) + */ +BOOL WINAPI SetConsoleTitleA( LPCSTR title ) +{ + LPWSTR titleW; + BOOL ret; + DWORD len = MultiByteToWideChar( GetConsoleOutputCP(), 0, title, -1, NULL, 0 ); + if (!(titleW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE; + MultiByteToWideChar( GetConsoleOutputCP(), 0, title, -1, titleW, len ); + ret = SetConsoleTitleW(titleW); + HeapFree( GetProcessHeap(), 0, titleW ); + return ret; +} + + +/****************************************************************************** + * SetConsoleTitleW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleTitleW( LPCWSTR title ) +{ + TRACE( "%s\n", debugstr_w( title )); + + return console_ioctl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, IOCTL_CONDRV_SET_TITLE, + (void *)title, lstrlenW(title) * sizeof(WCHAR), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetConsoleWindowInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleWindowInfo( HANDLE handle, BOOL absolute, SMALL_RECT *window ) +{ + struct condrv_output_info_params params = { SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW }; + SMALL_RECT rect = *window; + + TRACE( "(%p,%d,(%d,%d-%d,%d))\n", handle, absolute, rect.Left, rect.Top, rect.Right, rect.Bottom ); + + if (!absolute) + { + CONSOLE_SCREEN_BUFFER_INFO info; + + if (!GetConsoleScreenBufferInfo( handle, &info )) return FALSE; + rect.Left += info.srWindow.Left; + rect.Top += info.srWindow.Top; + rect.Right += info.srWindow.Right; + rect.Bottom += info.srWindow.Bottom; + } + + params.info.win_left = rect.Left; + params.info.win_top = rect.Top; + params.info.win_right = rect.Right; + params.info.win_bottom = rect.Bottom; + return console_ioctl( handle, IOCTL_CONDRV_SET_OUTPUT_INFO, ¶ms, sizeof(params), NULL, 0, NULL ); +} + + +/****************************************************************************** + * SetCurrentConsoleFontEx (kernelbase.@) + */ +BOOL WINAPI SetCurrentConsoleFontEx( HANDLE handle, BOOL maxwindow, CONSOLE_FONT_INFOEX *info ) +{ + struct + { + struct condrv_output_info_params params; + WCHAR face_name[LF_FACESIZE]; + } data; + + size_t size; + + TRACE( "(%p %d %p)\n", handle, maxwindow, info ); + + if (info->cbSize != sizeof(*info)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + data.params.mask = SET_CONSOLE_OUTPUT_INFO_FONT; + + data.params.info.font_width = info->dwFontSize.X; + data.params.info.font_height = info->dwFontSize.Y; + data.params.info.font_pitch_family = info->FontFamily; + data.params.info.font_weight = info->FontWeight; + + size = wcsnlen( info->FaceName, LF_FACESIZE - 1 ) * sizeof(WCHAR); + memcpy( data.face_name, info->FaceName, size ); + + size += sizeof(struct condrv_output_info_params); + return console_ioctl( handle, IOCTL_CONDRV_SET_OUTPUT_INFO, &data, size, NULL, 0, NULL ); +} + + +/*********************************************************************** + * ReadConsoleInputA (kernelbase.@) + */ +BOOL WINAPI ReadConsoleInputA( HANDLE handle, INPUT_RECORD *buffer, DWORD length, DWORD *count ) +{ + DWORD read; + + if (!ReadConsoleInputW( handle, buffer, length, &read )) return FALSE; + input_records_WtoA( buffer, read ); + if (count) *count = read; + return TRUE; +} + + +/*********************************************************************** + * ReadConsoleInputW (kernelbase.@) + */ +BOOL WINAPI ReadConsoleInputW( HANDLE handle, INPUT_RECORD *buffer, DWORD length, DWORD *count ) +{ + if (!console_ioctl( handle, IOCTL_CONDRV_READ_INPUT, NULL, 0, + buffer, length * sizeof(*buffer), count )) + return FALSE; + *count /= sizeof(*buffer); + return TRUE; +} + + +/****************************************************************************** + * WriteConsoleInputA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleInputA( HANDLE handle, const INPUT_RECORD *buffer, + DWORD count, DWORD *written ) +{ + INPUT_RECORD *recW = NULL; + BOOL ret; + + if (count > 0) + { + if (!buffer) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + if (!(recW = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*recW) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + memcpy( recW, buffer, count * sizeof(*recW) ); + input_records_AtoW( recW, count ); + } + ret = WriteConsoleInputW( handle, recW, count, written ); + HeapFree( GetProcessHeap(), 0, recW ); + return ret; +} + + +/****************************************************************************** + * WriteConsoleInputW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleInputW( HANDLE handle, const INPUT_RECORD *buffer, + DWORD count, DWORD *written ) +{ + TRACE( "(%p,%p,%ld,%p)\n", handle, buffer, count, written ); + + if (count > 0 && !buffer) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + if (!DeviceIoControl( handle, IOCTL_CONDRV_WRITE_INPUT, (void *)buffer, count * sizeof(*buffer), NULL, 0, NULL, NULL )) + return FALSE; + + if (!written) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + *written = count; + return TRUE; +} + + +/*********************************************************************** + * WriteConsoleOutputA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleOutputA( HANDLE handle, const CHAR_INFO *buffer, + COORD size, COORD coord, SMALL_RECT *region ) +{ + int y; + BOOL ret; + COORD new_size, new_coord; + CHAR_INFO *ciW; + + new_size.X = min( region->Right - region->Left + 1, size.X - coord.X ); + new_size.Y = min( region->Bottom - region->Top + 1, size.Y - coord.Y ); + + if (new_size.X <= 0 || new_size.Y <= 0) + { + region->Bottom = region->Top + new_size.Y - 1; + region->Right = region->Left + new_size.X - 1; + return TRUE; + } + + /* only copy the useful rectangle */ + if (!(ciW = HeapAlloc( GetProcessHeap(), 0, sizeof(CHAR_INFO) * new_size.X * new_size.Y ))) + return FALSE; + for (y = 0; y < new_size.Y; y++) + memcpy( &ciW[y * new_size.X], &buffer[(y + coord.Y) * size.X + coord.X], + new_size.X * sizeof(CHAR_INFO) ); + char_info_AtoW( ciW, new_size.X * new_size.Y ); + new_coord.X = new_coord.Y = 0; + ret = WriteConsoleOutputW( handle, ciW, new_size, new_coord, region ); + HeapFree( GetProcessHeap(), 0, ciW ); + return ret; +} + + +/*********************************************************************** + * WriteConsoleOutputW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleOutputW( HANDLE handle, const CHAR_INFO *buffer, + COORD size, COORD coord, SMALL_RECT *region ) +{ + struct condrv_output_params *params; + unsigned int width, height, y; + size_t params_size; + BOOL ret; + + TRACE( "(%p,%p,(%d,%d),(%d,%d),(%d,%dx%d,%d)\n", + handle, buffer, size.X, size.Y, coord.X, coord.Y, + region->Left, region->Top, region->Right, region->Bottom ); + + if (region->Left > region->Right || region->Top > region->Bottom || size.X <= coord.X || size.Y <= coord.Y) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + width = min( region->Right - region->Left + 1, size.X - coord.X ); + height = min( region->Bottom - region->Top + 1, size.Y - coord.Y ); + region->Right = region->Left + width - 1; + region->Bottom = region->Top + height - 1; + + params_size = sizeof(*params) + width * height * sizeof(*buffer); + if (!(params = HeapAlloc( GetProcessHeap(), 0, params_size ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + params->mode = CHAR_INFO_MODE_TEXTATTR; + params->x = region->Left; + params->y = region->Top; + params->width = width; + + for (y = 0; y < height; y++) + memcpy( &((CHAR_INFO *)(params + 1))[y * width], &buffer[(y + coord.Y) * size.X + coord.X], width * sizeof(CHAR_INFO) ); + + ret = console_ioctl( handle, IOCTL_CONDRV_WRITE_OUTPUT, params, params_size, region, sizeof(*region), NULL ); + HeapFree( GetProcessHeap(), 0, params ); + return ret; +} + + +/****************************************************************************** + * WriteConsoleOutputAttribute (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleOutputAttribute( HANDLE handle, const WORD *attr, DWORD length, + COORD coord, DWORD *written ) +{ + struct condrv_output_params *params; + size_t size; + BOOL ret; + + TRACE( "(%p,%p,%ld,%dx%d,%p)\n", handle, attr, length, coord.X, coord.Y, written ); + + if ((length > 0 && !attr) || !written) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + *written = 0; + size = sizeof(*params) + length * sizeof(WORD); + if (!(params = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; + params->mode = CHAR_INFO_MODE_ATTR; + params->x = coord.X; + params->y = coord.Y; + params->width = 0; + memcpy( params + 1, attr, length * sizeof(*attr) ); + ret = console_ioctl( handle, IOCTL_CONDRV_WRITE_OUTPUT, params, size, written, sizeof(*written), NULL ); + HeapFree( GetProcessHeap(), 0, params ); + return ret; +} + + +/****************************************************************************** + * WriteConsoleOutputCharacterA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleOutputCharacterA( HANDLE handle, LPCSTR str, DWORD length, + COORD coord, DWORD *written ) +{ + BOOL ret; + LPWSTR strW = NULL; + DWORD lenW = 0; + + TRACE( "(%p,%s,%ld,%dx%d,%p)\n", handle, debugstr_an(str, length), length, coord.X, coord.Y, written ); + + if (length > 0) + { + UINT cp = GetConsoleOutputCP(); + if (!str) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + lenW = MultiByteToWideChar( cp, 0, str, length, NULL, 0 ); + + if (!(strW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + MultiByteToWideChar( cp, 0, str, length, strW, lenW ); + } + ret = WriteConsoleOutputCharacterW( handle, strW, lenW, coord, written ); + HeapFree( GetProcessHeap(), 0, strW ); + return ret; +} + + +/****************************************************************************** + * WriteConsoleOutputCharacterW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleOutputCharacterW( HANDLE handle, LPCWSTR str, DWORD length, + COORD coord, DWORD *written ) +{ + struct condrv_output_params *params; + size_t size; + BOOL ret; + + TRACE( "(%p,%s,%ld,%dx%d,%p)\n", handle, debugstr_wn(str, length), length, coord.X, coord.Y, written ); + + if ((length > 0 && !str) || !written) + { + SetLastError( ERROR_INVALID_ACCESS ); + return FALSE; + } + + *written = 0; + size = sizeof(*params) + length * sizeof(WCHAR); + if (!(params = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; + params->mode = CHAR_INFO_MODE_TEXT; + params->x = coord.X; + params->y = coord.Y; + params->width = 0; + memcpy( params + 1, str, length * sizeof(*str) ); + ret = console_ioctl( handle, IOCTL_CONDRV_WRITE_OUTPUT, params, size, written, sizeof(*written), NULL ); + HeapFree( GetProcessHeap(), 0, params ); + return ret; +} + + +/*********************************************************************** + * ReadConsoleA (kernelbase.@) + */ +BOOL WINAPI ReadConsoleA( HANDLE handle, void *buffer, DWORD length, DWORD *count, void *reserved ) +{ + if (length > INT_MAX) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + return console_ioctl( handle, IOCTL_CONDRV_READ_FILE, NULL, 0, buffer, length, count ); +} + + +/*********************************************************************** + * ReadConsoleW (kernelbase.@) + */ +BOOL WINAPI ReadConsoleW( HANDLE handle, void *buffer, DWORD length, DWORD *count, void *reserved ) +{ + BOOL ret; + + TRACE( "(%p,%p,%ld,%p,%p)\n", handle, buffer, length, count, reserved ); + + if (length > INT_MAX) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + if (reserved) + { + CONSOLE_READCONSOLE_CONTROL* crc = reserved; + char *tmp; + + if (crc->nLength != sizeof(*crc) || crc->nInitialChars >= length) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (!(tmp = HeapAlloc( GetProcessHeap(), 0, sizeof(DWORD) + length * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + memcpy( tmp, &crc->dwCtrlWakeupMask, sizeof(DWORD) ); + memcpy( tmp + sizeof(DWORD), buffer, crc->nInitialChars * sizeof(WCHAR) ); + ret = console_ioctl( handle, IOCTL_CONDRV_READ_CONSOLE_CONTROL, + tmp, sizeof(DWORD) + crc->nInitialChars * sizeof(WCHAR), + tmp, sizeof(DWORD) + length * sizeof(WCHAR), count ); + if (ret) + { + memcpy( &crc->dwConsoleKeyState, tmp, sizeof(DWORD) ); + *count -= sizeof(DWORD); + memcpy( buffer, tmp + sizeof(DWORD), *count ); + } + HeapFree( GetProcessHeap(), 0, tmp ); + } + else + { + ret = console_ioctl( handle, IOCTL_CONDRV_READ_CONSOLE, NULL, 0, buffer, + length * sizeof(WCHAR), count ); + } + if (ret) *count /= sizeof(WCHAR); + return ret; +} + + +/*********************************************************************** + * WriteConsoleA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleA( HANDLE handle, const void *buffer, DWORD length, + DWORD *written, void *reserved ) +{ + BOOL ret; + + TRACE( "(%p,%s,%ld,%p,%p)\n", handle, debugstr_an(buffer, length), length, written, reserved ); + + ret = console_ioctl( handle, IOCTL_CONDRV_WRITE_FILE, (void *)buffer, length, NULL, 0, NULL ); + if (written) *written = ret ? length : 0; + return ret; +} + + +/*********************************************************************** + * WriteConsoleW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteConsoleW( HANDLE handle, const void *buffer, DWORD length, + DWORD *written, void *reserved ) +{ + BOOL ret; + + TRACE( "(%p,%s,%ld,%p,%p)\n", handle, debugstr_wn(buffer, length), length, written, reserved ); + + ret = console_ioctl( handle, IOCTL_CONDRV_WRITE_CONSOLE, (void *)buffer, + length * sizeof(WCHAR), NULL, 0, NULL ); + if (written) *written = ret ? length : 0; + return ret; +} + + +/*********************************************************************** + * FlushConsoleInputBuffer (kernelbase.@) + */ +BOOL WINAPI FlushConsoleInputBuffer( HANDLE handle ) +{ + return console_ioctl( handle, IOCTL_CONDRV_FLUSH, NULL, 0, NULL, 0, NULL ); +} + + +/*********************************************************************** + * Beep (kernelbase.@) + */ +BOOL WINAPI Beep( DWORD frequency, DWORD duration ) +{ + /* FIXME: we should not require a console to be attached */ + console_ioctl( RtlGetCurrentPeb()->ProcessParameters->ConsoleHandle, + IOCTL_CONDRV_BEEP, NULL, 0, NULL, 0, NULL ); + return TRUE; +} + + +static HANDLE create_pseudo_console( COORD size, HANDLE input, HANDLE output, HANDLE signal, + DWORD flags, HANDLE *process ) +{ + WCHAR cmd[MAX_PATH], conhost_path[MAX_PATH]; + unsigned int inherit_count; + PROCESS_INFORMATION pi; + HANDLE server, console; + HANDLE inherit[2]; + STARTUPINFOEXW si; + SIZE_T attr_size; + void *redir; + BOOL res; + + if (!(server = create_console_server())) return NULL; + + console = create_console_reference( server ); + if (!console) + { + NtClose( server ); + return NULL; + } + + memset( &si, 0, sizeof(si) ); + si.StartupInfo.cb = sizeof(STARTUPINFOEXW); + si.StartupInfo.hStdInput = input; + si.StartupInfo.hStdOutput = output; + si.StartupInfo.hStdError = output; + si.StartupInfo.dwFlags = STARTF_USESTDHANDLES; + + inherit[0] = server; + inherit[1] = signal; + inherit_count = signal ? 2 : 1; + InitializeProcThreadAttributeList( NULL, inherit_count, 0, &attr_size ); + if (!(si.lpAttributeList = HeapAlloc( GetProcessHeap(), 0, attr_size ))) + { + NtClose( console ); + NtClose( server ); + return FALSE; + } + InitializeProcThreadAttributeList( si.lpAttributeList, inherit_count, 0, &attr_size ); + UpdateProcThreadAttribute( si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + inherit, sizeof(*inherit) * inherit_count, NULL, NULL ); + + swprintf( conhost_path, ARRAY_SIZE(conhost_path), L"%s\\conhost.exe", system_dir ); + if (signal) + { + swprintf( cmd, ARRAY_SIZE(cmd), + L"\"%s\" --headless %s--width %u --height %u --signal 0x%x --server 0x%x", + conhost_path, (flags & PSEUDOCONSOLE_INHERIT_CURSOR) ? L"--inheritcursor " : L"", + size.X, size.Y, signal, server ); + } + else + { + swprintf( cmd, ARRAY_SIZE(cmd), L"\"%s\" --unix --width %u --height %u --server 0x%x", + conhost_path, size.X, size.Y, server ); + } + Wow64DisableWow64FsRedirection( &redir ); + res = CreateProcessW( conhost_path, cmd, NULL, NULL, TRUE, DETACHED_PROCESS | EXTENDED_STARTUPINFO_PRESENT, + NULL, NULL, &si.StartupInfo, &pi ); + HeapFree( GetProcessHeap(), 0, si.lpAttributeList ); + Wow64RevertWow64FsRedirection( redir ); + NtClose( server ); + if (!res) + { + NtClose( console ); + return NULL; + } + + NtClose( pi.hThread ); + *process = pi.hProcess; + return console; +} + +/****************************************************************************** + * CreatePseudoConsole (kernelbase.@) + */ +HRESULT WINAPI CreatePseudoConsole( COORD size, HANDLE input, HANDLE output, DWORD flags, HPCON *ret ) +{ + SECURITY_ATTRIBUTES inherit_attr = { sizeof(inherit_attr), NULL, TRUE }; + struct pseudo_console *pseudo_console; + HANDLE tty_input = NULL, tty_output; + HANDLE signal = NULL; + WCHAR pipe_name[64]; + + TRACE( "(%u,%u) %p %p %lx %p\n", size.X, size.Y, input, output, flags, ret ); + + if (!size.X || !size.Y || !ret) return E_INVALIDARG; + + if (!(pseudo_console = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pseudo_console) ))) return E_OUTOFMEMORY; + + swprintf( pipe_name, ARRAY_SIZE(pipe_name), L"\\\\.\\pipe\\wine_pty_signal_pipe%x", + GetCurrentThreadId() ); + signal = CreateNamedPipeW( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, &inherit_attr ); + if (signal == INVALID_HANDLE_VALUE) + { + HeapFree( GetProcessHeap(), 0, pseudo_console ); + return HRESULT_FROM_WIN32( GetLastError() ); + } + pseudo_console->signal = CreateFileW( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); + if (pseudo_console->signal != INVALID_HANDLE_VALUE && + DuplicateHandle( GetCurrentProcess(), input, GetCurrentProcess(), &tty_input, 0, TRUE, DUPLICATE_SAME_ACCESS) && + DuplicateHandle( GetCurrentProcess(), output, GetCurrentProcess(), &tty_output, 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + pseudo_console->reference = create_pseudo_console( size, tty_input, tty_output, signal, flags, + &pseudo_console->process ); + NtClose( tty_output ); + } + NtClose( tty_input ); + NtClose( signal ); + if (!pseudo_console->reference) + { + ClosePseudoConsole( pseudo_console ); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + *ret = pseudo_console; + return S_OK; +} + +/****************************************************************************** + * ClosePseudoConsole (kernelbase.@) + */ +void WINAPI ClosePseudoConsole( HPCON handle ) +{ + struct pseudo_console *pseudo_console = handle; + + TRACE( "%p\n", handle ); + + if (!pseudo_console) return; + if (pseudo_console->signal) CloseHandle( pseudo_console->signal ); + if (pseudo_console->process) + { + WaitForSingleObject( pseudo_console->process, INFINITE ); + CloseHandle( pseudo_console->process ); + } + if (pseudo_console->reference) CloseHandle( pseudo_console->reference ); +} + +/****************************************************************************** + * ResizePseudoConsole (kernelbase.@) + */ +HRESULT WINAPI ResizePseudoConsole( HPCON handle, COORD size ) +{ + FIXME( "%p (%u,%u)\n", handle, size.X, size.Y ); + return E_NOTIMPL; +} + +static BOOL is_tty_handle( HANDLE handle ) +{ + return ((UINT_PTR)handle & 3) == 1; +} + +void init_console( void ) +{ + RTL_USER_PROCESS_PARAMETERS *params = RtlGetCurrentPeb()->ProcessParameters; + + if (params->ConsoleHandle == CONSOLE_HANDLE_SHELL) + { + HANDLE tty_in = NULL, tty_out = NULL, process = NULL; + COORD size; + + if (is_tty_handle( params->hStdInput )) + { + tty_in = params->hStdInput; + params->hStdInput = NULL; + } + if (is_tty_handle( params->hStdOutput )) + { + tty_out = params->hStdOutput; + params->hStdOutput = NULL; + } + if (is_tty_handle( params->hStdError )) + { + if (tty_out) CloseHandle( params->hStdError ); + else tty_out = params->hStdError; + params->hStdError = NULL; + } + + size.X = params->dwXCountChars; + size.Y = params->dwYCountChars; + TRACE( "creating unix console (size %u %u)\n", size.X, size.Y ); + params->ConsoleHandle = create_pseudo_console( size, tty_in, tty_out, NULL, 0, &process ); + CloseHandle( process ); + CloseHandle( tty_in ); + CloseHandle( tty_out ); + + if (params->ConsoleHandle && create_console_connection( params->ConsoleHandle )) + { + init_console_std_handles( FALSE ); + } + } + else if (params->ConsoleHandle == CONSOLE_HANDLE_ALLOC || + params->ConsoleHandle == CONSOLE_HANDLE_ALLOC_NO_WINDOW) + { + BOOL no_window = params->ConsoleHandle == CONSOLE_HANDLE_ALLOC_NO_WINDOW; + HMODULE mod = GetModuleHandleW( NULL ); + params->ConsoleHandle = NULL; + if (RtlImageNtHeader( mod )->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI) + alloc_console( no_window ); + } + else if (params->ConsoleHandle && params->ConsoleHandle != CONSOLE_HANDLE_SHELL_NO_WINDOW) + create_console_connection( params->ConsoleHandle ); +} diff --git a/dll/win32/KernelBase/wine/debug.c b/dll/win32/KernelBase/wine/debug.c new file mode 100644 index 0000000000000..cbefc0c873340 --- /dev/null +++ b/dll/win32/KernelBase/wine/debug.c @@ -0,0 +1,1820 @@ +/* + * Win32 debugger functions + * + * Copyright (C) 1999 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winnls.h" +#include "wingdi.h" +#include "winuser.h" +#define PSAPI_VERSION 1 /* avoid K32 function remapping */ +#include "psapi.h" +#include "werapi.h" + +#include "wine/exception.h" +#include "wine/asm.h" +#include "kernelbase.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(seh); +WINE_DECLARE_DEBUG_CHANNEL(winedbg); + +typedef INT (WINAPI *MessageBoxA_funcptr)(HWND,LPCSTR,LPCSTR,UINT); +typedef INT (WINAPI *MessageBoxW_funcptr)(HWND,LPCWSTR,LPCWSTR,UINT); + +static PTOP_LEVEL_EXCEPTION_FILTER top_filter; + +void *dummy = RtlUnwind; /* force importing RtlUnwind from ntdll */ + +/*********************************************************************** + * CheckRemoteDebuggerPresent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CheckRemoteDebuggerPresent( HANDLE process, BOOL *present ) +{ + DWORD_PTR port; + + if (!process || !present) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessDebugPort, &port, sizeof(port), NULL ))) + return FALSE; + *present = !!port; + return TRUE; +} + + +/********************************************************************** + * ContinueDebugEvent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ContinueDebugEvent( DWORD pid, DWORD tid, DWORD status ) +{ + CLIENT_ID id; + + id.UniqueProcess = ULongToHandle( pid ); + id.UniqueThread = ULongToHandle( tid ); + return set_ntstatus( DbgUiContinue( &id, status )); +} + + +/********************************************************************** + * DebugActiveProcess (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DebugActiveProcess( DWORD pid ) +{ + HANDLE process; + NTSTATUS status; + + if (!set_ntstatus( DbgUiConnectToDbg() )) return FALSE; + if (!(process = OpenProcess( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_SUSPEND_RESUME | + PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD, FALSE, pid ))) + return FALSE; + status = DbgUiDebugActiveProcess( process ); + NtClose( process ); + return set_ntstatus( status ); +} + + +/********************************************************************** + * DebugActiveProcessStop (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DebugActiveProcessStop( DWORD pid ) +{ + HANDLE process; + NTSTATUS status; + + if (!(process = OpenProcess( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_SUSPEND_RESUME, FALSE, pid ))) + return FALSE; + status = DbgUiStopDebugging( process ); + NtClose( process ); + return set_ntstatus( status ); +} + + +/*********************************************************************** + * DebugBreak (kernelbase.@) + */ +#ifdef __i386__ +__ASM_STDCALL_FUNC( DebugBreak, 0, "jmp " __ASM_STDCALL("DbgBreakPoint", 0) ) +#elif defined(__aarch64__) +__ASM_GLOBAL_FUNC( DebugBreak, "brk #0xf000; ret" ) +#elif defined(__arm64ec__) +void __attribute__((naked)) WINAPI DebugBreak(void) { asm( "brk #0xf000; ret" ); } +#elif defined(__x86_64__) +__ASM_GLOBAL_FUNC( DebugBreak, "jmp " __ASM_NAME("DbgBreakPoint") ) +#elif defined(__arm__) +__ASM_GLOBAL_FUNC( DebugBreak, "udf #0xfe; bx lr" ) +#endif + + +/************************************************************************** + * FatalAppExitA (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH FatalAppExitA( UINT action, LPCSTR str ) +{ + HMODULE mod = GetModuleHandleA( "user32.dll" ); + MessageBoxA_funcptr pMessageBoxA = NULL; + + if (mod) pMessageBoxA = (MessageBoxA_funcptr)GetProcAddress( mod, "MessageBoxA" ); + if (pMessageBoxA) pMessageBoxA( 0, str, NULL, MB_SYSTEMMODAL | MB_OK ); + else ERR( "%s\n", debugstr_a(str) ); + RtlExitUserProcess( 1 ); +} + + +/************************************************************************** + * FatalAppExitW (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH FatalAppExitW( UINT action, LPCWSTR str ) +{ + HMODULE mod = GetModuleHandleW( L"user32.dll" ); + MessageBoxW_funcptr pMessageBoxW = NULL; + + if (mod) pMessageBoxW = (MessageBoxW_funcptr)GetProcAddress( mod, "MessageBoxW" ); + if (pMessageBoxW) pMessageBoxW( 0, str, NULL, MB_SYSTEMMODAL | MB_OK ); + else ERR( "%s\n", debugstr_w(str) ); + RtlExitUserProcess( 1 ); +} + + +/*********************************************************************** + * IsDebuggerPresent (kernelbase.@) + */ +BOOL WINAPI IsDebuggerPresent(void) +{ + return NtCurrentTeb()->Peb->BeingDebugged; +} + + +static LONG WINAPI debug_exception_handler( EXCEPTION_POINTERS *eptr ) +{ + EXCEPTION_RECORD *rec = eptr->ExceptionRecord; + return (rec->ExceptionCode == DBG_PRINTEXCEPTION_C) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} + +/*********************************************************************** + * OutputDebugStringA (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH OutputDebugStringA( LPCSTR str ) +{ + static HANDLE DBWinMutex = NULL; + static BOOL mutex_inited = FALSE; + BOOL caught_by_dbg = TRUE; + + if (!str) str = ""; + WARN( "%s\n", debugstr_a(str) ); + + /* raise exception, WaitForDebugEvent() will generate a corresponding debug event */ + __TRY + { + ULONG_PTR args[2]; + args[0] = strlen(str) + 1; + args[1] = (ULONG_PTR)str; + RaiseException( DBG_PRINTEXCEPTION_C, 0, 2, args ); + } + __EXCEPT(debug_exception_handler) + { + caught_by_dbg = FALSE; + } + __ENDTRY + if (caught_by_dbg) return; + + /* send string to a system-wide monitor */ + if (!mutex_inited) + { + /* first call to OutputDebugString, initialize mutex handle */ + HANDLE mutex = CreateMutexExW( NULL, L"DBWinMutex", 0, SYNCHRONIZE ); + if (mutex) + { + if (InterlockedCompareExchangePointer( &DBWinMutex, mutex, 0 ) != 0) + /* someone beat us here... */ + CloseHandle( mutex ); + } + mutex_inited = TRUE; + } + + if (DBWinMutex) + { + HANDLE mapping; + + mapping = OpenFileMappingW( FILE_MAP_WRITE, FALSE, L"DBWIN_BUFFER" ); + if (mapping) + { + LPVOID buffer; + HANDLE eventbuffer, eventdata; + + buffer = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 0 ); + eventbuffer = OpenEventW( SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY" ); + eventdata = OpenEventW( EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY" ); + + if (buffer && eventbuffer && eventdata) + { + /* monitor is present, synchronize with other OutputDebugString invocations */ + WaitForSingleObject( DBWinMutex, INFINITE ); + + /* acquire control over the buffer */ + if (WaitForSingleObject( eventbuffer, 10000 ) == WAIT_OBJECT_0) + { + int str_len = strlen( str ); + struct _mon_buffer_t + { + DWORD pid; + char buffer[1]; + } *mon_buffer = (struct _mon_buffer_t*) buffer; + + if (str_len > (4096 - sizeof(DWORD) - 1)) str_len = 4096 - sizeof(DWORD) - 1; + mon_buffer->pid = GetCurrentProcessId(); + memcpy( mon_buffer->buffer, str, str_len ); + mon_buffer->buffer[str_len] = 0; + + /* signal data ready */ + SetEvent( eventdata ); + } + ReleaseMutex( DBWinMutex ); + } + + if (buffer) UnmapViewOfFile( buffer ); + if (eventbuffer) CloseHandle( eventbuffer ); + if (eventdata) CloseHandle( eventdata ); + CloseHandle( mapping ); + } + } +} + +static LONG WINAPI debug_exception_handler_wide( EXCEPTION_POINTERS *eptr ) +{ + EXCEPTION_RECORD *rec = eptr->ExceptionRecord; + return (rec->ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} + +/*********************************************************************** + * OutputDebugStringW (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH OutputDebugStringW( LPCWSTR str ) +{ + UNICODE_STRING strW; + STRING strA; + + WARN( "%s\n", debugstr_w(str) ); + + RtlInitUnicodeString( &strW, str ); + if (!RtlUnicodeStringToAnsiString( &strA, &strW, TRUE )) + { + BOOL exc_handled; + + __TRY + { + ULONG_PTR args[4]; + args[0] = wcslen(str) + 1; + args[1] = (ULONG_PTR)str; + args[2] = strlen(strA.Buffer) + 1; + args[3] = (ULONG_PTR)strA.Buffer; + RaiseException( DBG_PRINTEXCEPTION_WIDE_C, 0, 4, args ); + exc_handled = TRUE; + } + __EXCEPT(debug_exception_handler_wide) + { + exc_handled = FALSE; + } + __ENDTRY + + if (!exc_handled) + OutputDebugStringA( strA.Buffer ); + + RtlFreeAnsiString( &strA ); + } +} + + +/******************************************************************* + * RaiseException (kernelbase.@) + */ +#ifdef __x86_64__ +#ifdef __arm64ec__ +void __attribute__((naked)) RaiseException( DWORD code, DWORD flags, DWORD count, const ULONG_PTR *args ) +{ + asm( ".seh_proc RaiseException\n\t" + "stp x29, x30, [sp, #-0xb0]!\n\t" + ".seh_save_fplr_x 0xb0\n\t" + ".seh_endprologue\n\t" + "and w1, w1, #0x01\n\t" /* EXCEPTION_NONCONTINUABLE */ + "stp w0, w1, [sp, #0x10]\n\t" /* ExceptionCode, ExceptionFlags */ + "adr x4, RaiseException\n\t" + "stp xzr, x4, [sp, #0x18]\n\t" /* ExceptionRecord, ExceptionAddress */ + "mov w5, #0x0f\n\t" /* EXCEPTION_MAXIMUM_PARAMETERS */ + "cmp w2, w5\n\t" + "csel w2, w2, w5, lo\n\t" + "str x2, [sp, #0x28]\n\t" /* NumberParameters */ + "cbz x3, 1f\n\t" + "lsl w2, w2, #3\n\t" + "add x0, sp, #0x30\n\t" /* ExceptionInformation */ + "mov x1, x3\n\t" /* args */ + "bl \"#memcpy\"\n" + "1:\tadd x0, sp, #0x10\n\t" /* rec */ + "bl \"#RtlRaiseException\"\n\t" + "ldp x29, x30, [sp], #0xb0\n\t" + "ret\n\t" + ".seh_endproc" ); +} +#else +/* Some DRMs depend on RaiseException not altering non-volatile registers. */ +__ASM_GLOBAL_FUNC( RaiseException, + ".byte 0x48,0x8d,0xa4,0x24,0x00,0x00,0x00,0x00\n\t" /* hotpatch prolog */ + "sub $0xc8,%rsp\n\t" + __ASM_SEH(".seh_stackalloc 0xc8\n\t") + __ASM_SEH(".seh_endprologue\n\t") + __ASM_CFI(".cfi_adjust_cfa_offset 0xc8\n\t") + "leaq 0x20(%rsp),%rax\n\t" + "movl %ecx,(%rax)\n\t" /* ExceptionCode */ + "and $1,%edx\n\t" + "movl %edx,4(%rax)\n\t" /* ExceptionFlags */ + "movq $0,8(%rax)\n\t" /* ExceptionRecord */ + "leaq " __ASM_NAME("RaiseException") "(%rip),%rcx\n\t" + "movq %rcx,0x10(%rax)\n\t" /* ExceptionAddress */ + "movq %rax,%rcx\n\t" + "movl $0,0x18(%rcx)\n\t" /* NumberParameters */ + "testl %r8d,%r8d\n\t" + "jz 2f\n\t" + "testq %r9,%r9\n\t" + "jz 2f\n\t" + "movl $15,%edx\n\t" + "cmp %edx,%r8d\n\t" + "cmovb %r8d,%edx\n\t" + "movl %edx,0x18(%rcx)\n\t" /* NumberParameters */ + "leaq 0x20(%rcx),%rax\n" /* ExceptionInformation */ + "1:\tmovq (%r9),%r8\n\t" + "movq %r8,(%rax)\n\t" + "decl %edx\n\t" + "jz 2f\n\t" + "addq $8,%rax\n\t" + "addq $8,%r9\n\t" + "jmp 1b\n" + "2:\tcall " __ASM_NAME("RtlRaiseException") "\n\t" + "add $0xc8,%rsp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -0xc8\n\t") + "ret" ) +#endif /* __arm64ec__ */ +C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionCode) == 0 ); +C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionFlags) == 4 ); +C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionRecord) == 8 ); +C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionAddress) == 0x10 ); +C_ASSERT( offsetof(EXCEPTION_RECORD, NumberParameters) == 0x18 ); +C_ASSERT( offsetof(EXCEPTION_RECORD, ExceptionInformation) == 0x20 ); +#else /* __x86_64__ */ +void WINAPI DECLSPEC_HOTPATCH RaiseException( DWORD code, DWORD flags, DWORD count, const ULONG_PTR *args ) +{ + EXCEPTION_RECORD record; + + record.ExceptionCode = code; + record.ExceptionFlags = flags & EXCEPTION_NONCONTINUABLE; + record.ExceptionRecord = NULL; + record.ExceptionAddress = RaiseException; + if (count && args) + { + if (count > EXCEPTION_MAXIMUM_PARAMETERS) count = EXCEPTION_MAXIMUM_PARAMETERS; + record.NumberParameters = count; + memcpy( record.ExceptionInformation, args, count * sizeof(*args) ); + } + else record.NumberParameters = 0; + + RtlRaiseException( &record ); +} +#endif + +#ifdef __i386__ +__ASM_STDCALL_IMPORT(RaiseException,16) +#else +__ASM_GLOBAL_IMPORT(RaiseException) +#endif + +/******************************************************************* + * RaiseFailFastException (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH RaiseFailFastException( EXCEPTION_RECORD *record, CONTEXT *context, DWORD flags ) +{ + FIXME( "(%p, %p, %ld) stub\n", record, context, flags ); + TerminateProcess( GetCurrentProcess(), STATUS_FAIL_FAST_EXCEPTION ); +} + +/*********************************************************************** + * SetUnhandledExceptionFilter (kernelbase.@) + */ +LPTOP_LEVEL_EXCEPTION_FILTER WINAPI DECLSPEC_HOTPATCH SetUnhandledExceptionFilter( + LPTOP_LEVEL_EXCEPTION_FILTER filter ) +{ + return InterlockedExchangePointer( (void **)&top_filter, filter ); +} + + +/******************************************************************* + * format_exception_msg + */ +static void format_exception_msg( const EXCEPTION_POINTERS *ptr, char *buffer, int size ) +{ + const EXCEPTION_RECORD *rec = ptr->ExceptionRecord; + int len; + + switch(rec->ExceptionCode) + { + case EXCEPTION_INT_DIVIDE_BY_ZERO: + len = snprintf( buffer, size, "Unhandled division by zero" ); + break; + case EXCEPTION_INT_OVERFLOW: + len = snprintf( buffer, size, "Unhandled overflow" ); + break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + len = snprintf( buffer, size, "Unhandled array bounds" ); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + len = snprintf( buffer, size, "Unhandled illegal instruction" ); + break; + case EXCEPTION_STACK_OVERFLOW: + len = snprintf( buffer, size, "Unhandled stack overflow" ); + break; + case EXCEPTION_PRIV_INSTRUCTION: + len = snprintf( buffer, size, "Unhandled privileged instruction" ); + break; + case EXCEPTION_ACCESS_VIOLATION: + if (rec->NumberParameters == 2) + len = snprintf( buffer, size, "Unhandled page fault on %s access to %p", + rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" : + rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read", + (void *)rec->ExceptionInformation[1]); + else + len = snprintf( buffer, size, "Unhandled page fault"); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + len = snprintf( buffer, size, "Unhandled alignment" ); + break; + case CONTROL_C_EXIT: + len = snprintf( buffer, size, "Unhandled ^C"); + break; + case STATUS_POSSIBLE_DEADLOCK: + len = snprintf( buffer, size, "Critical section %p wait failed", + (void *)rec->ExceptionInformation[0]); + break; + case EXCEPTION_WINE_STUB: + if ((ULONG_PTR)rec->ExceptionInformation[1] >> 16) + len = snprintf( buffer, size, "Unimplemented function %s.%s called", + (char *)rec->ExceptionInformation[0], (char *)rec->ExceptionInformation[1] ); + else + len = snprintf( buffer, size, "Unimplemented function %s.%Id called", + (char *)rec->ExceptionInformation[0], rec->ExceptionInformation[1] ); + break; + case EXCEPTION_WINE_ASSERTION: + len = snprintf( buffer, size, "Assertion failed" ); + break; + default: + len = snprintf( buffer, size, "Unhandled exception 0x%08lx in thread %lx", + rec->ExceptionCode, GetCurrentThreadId()); + break; + } + if (len < 0 || len >= size) return; + snprintf( buffer + len, size - len, " at address %p", ptr->ExceptionRecord->ExceptionAddress ); +} + + +/****************************************************************** + * start_debugger + * + * Does the effective debugger startup according to 'format' + */ +static BOOL start_debugger( EXCEPTION_POINTERS *epointers, HANDLE event ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug" ); + WCHAR *cmdline, *env, *p, *format = NULL; + HANDLE dbg_key; + DWORD autostart = TRUE; + PROCESS_INFORMATION info; + STARTUPINFOW startup; + BOOL ret = FALSE; + char buffer[256]; + + format_exception_msg( epointers, buffer, sizeof(buffer) ); + MESSAGE( "wine: %s (thread %04lx), starting debugger...\n", buffer, GetCurrentThreadId() ); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + if (!NtOpenKey( &dbg_key, KEY_READ, &attr )) + { + KEY_VALUE_PARTIAL_INFORMATION *info; + DWORD format_size = 0; + + RtlInitUnicodeString( &nameW, L"Debugger" ); + if (NtQueryValueKey( dbg_key, &nameW, KeyValuePartialInformation, + NULL, 0, &format_size ) == STATUS_BUFFER_TOO_SMALL) + { + char *data = HeapAlloc( GetProcessHeap(), 0, format_size ); + NtQueryValueKey( dbg_key, &nameW, KeyValuePartialInformation, + data, format_size, &format_size ); + info = (KEY_VALUE_PARTIAL_INFORMATION *)data; + format = HeapAlloc( GetProcessHeap(), 0, info->DataLength + sizeof(WCHAR) ); + memcpy( format, info->Data, info->DataLength ); + format[info->DataLength / sizeof(WCHAR)] = 0; + + if (info->Type == REG_EXPAND_SZ) + { + WCHAR *tmp; + + format_size = ExpandEnvironmentStringsW( format, NULL, 0 ); + tmp = HeapAlloc( GetProcessHeap(), 0, format_size * sizeof(WCHAR)); + ExpandEnvironmentStringsW( format, tmp, format_size ); + HeapFree( GetProcessHeap(), 0, format ); + format = tmp; + } + HeapFree( GetProcessHeap(), 0, data ); + } + + RtlInitUnicodeString( &nameW, L"Auto" ); + if (!NtQueryValueKey( dbg_key, &nameW, KeyValuePartialInformation, + buffer, sizeof(buffer)-sizeof(WCHAR), &format_size )) + { + info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + if (info->Type == REG_DWORD) memcpy( &autostart, info->Data, sizeof(DWORD) ); + else if (info->Type == REG_SZ) + { + WCHAR *str = (WCHAR *)info->Data; + str[info->DataLength/sizeof(WCHAR)] = 0; + autostart = wcstol( str, NULL, 10 ); + } + } + + NtClose( dbg_key ); + } + + if (format) + { + size_t format_size = lstrlenW( format ) + 2*20; + cmdline = HeapAlloc( GetProcessHeap(), 0, format_size * sizeof(WCHAR) ); + swprintf( cmdline, format_size, format, GetCurrentProcessId(), HandleToLong(event) ); + HeapFree( GetProcessHeap(), 0, format ); + } + else + { + cmdline = HeapAlloc( GetProcessHeap(), 0, 80 * sizeof(WCHAR) ); + swprintf( cmdline, 80, L"winedbg --auto %ld %ld", GetCurrentProcessId(), HandleToLong(event) ); + } + + if (!autostart) + { + HMODULE mod = GetModuleHandleA( "user32.dll" ); + MessageBoxA_funcptr pMessageBoxA = NULL; + + if (mod) pMessageBoxA = (void *)GetProcAddress( mod, "MessageBoxA" ); + if (pMessageBoxA) + { + static const char msg[] = ".\nDo you wish to debug it?"; + + format_exception_msg( epointers, buffer, sizeof(buffer) - sizeof(msg) ); + strcat( buffer, msg ); + if (pMessageBoxA( 0, buffer, "Exception raised", MB_YESNO | MB_ICONHAND ) == IDNO) + { + TRACE( "Killing process\n" ); + goto exit; + } + } + } + + /* make WINEDEBUG empty in the environment */ + env = GetEnvironmentStringsW(); + if (!TRACE_ON(winedbg)) + { + for (p = env; *p; p += lstrlenW(p) + 1) + { + if (!wcsncmp( p, L"WINEDEBUG=", 10 )) + { + WCHAR *next = p + lstrlenW(p); + WCHAR *end = next + 1; + while (*end) end += lstrlenW(end) + 1; + memmove( p + 10, next, end + 1 - next ); + break; + } + } + } + + TRACE( "Starting debugger %s\n", debugstr_w(cmdline) ); + memset( &startup, 0, sizeof(startup) ); + startup.cb = sizeof(startup); + startup.dwFlags = STARTF_USESHOWWINDOW; + startup.wShowWindow = SW_SHOWNORMAL; + ret = CreateProcessW( NULL, cmdline, NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, env, NULL, &startup, &info ); + FreeEnvironmentStringsW( env ); + + if (ret) + { + /* wait for debugger to come up... */ + HANDLE handles[2]; + CloseHandle( info.hThread ); + handles[0] = event; + handles[1] = info.hProcess; + WaitForMultipleObjects( 2, handles, FALSE, INFINITE ); + CloseHandle( info.hProcess ); + } + else ERR( "Couldn't start debugger %s (%ld)\n" + "Read the Wine Developers Guide on how to set up winedbg or another debugger\n", + debugstr_w(cmdline), GetLastError() ); +exit: + HeapFree(GetProcessHeap(), 0, cmdline); + return ret; +} + +/****************************************************************** + * start_debugger_atomic + * + * starts the debugger in an atomic way: + * - either the debugger is not started and it is started + * - or the debugger has already been started by another thread + * - or the debugger couldn't be started + * + * returns TRUE for the two first conditions, FALSE for the last + */ +static BOOL start_debugger_atomic( EXCEPTION_POINTERS *epointers ) +{ + static HANDLE once; + + if (once == 0) + { + OBJECT_ATTRIBUTES attr; + HANDLE event; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_INHERIT; + attr.ObjectName = NULL; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + /* ask for manual reset, so that once the debugger is started, + * every thread will know it */ + NtCreateEvent( &event, EVENT_ALL_ACCESS, &attr, NotificationEvent, FALSE ); + if (InterlockedCompareExchangePointer( &once, event, 0 ) == 0) + { + /* ok, our event has been set... we're the winning thread */ + BOOL ret = start_debugger( epointers, once ); + + if (!ret) + { + /* so that the other threads won't be stuck */ + NtSetEvent( once, NULL ); + } + return ret; + } + + /* someone beat us here... */ + CloseHandle( event ); + } + + /* and wait for the winner to have actually created the debugger */ + WaitForSingleObject( once, INFINITE ); + /* in fact, here, we only know that someone has tried to start the debugger, + * we'll know by reposting the exception if it has actually attached + * to the current process */ + return TRUE; +} + + +/******************************************************************* + * check_resource_write + * + * Check if the exception is a write attempt to the resource data. + * If yes, we unprotect the resources to let broken apps continue + * (Windows does this too). + */ +static BOOL check_resource_write( void *addr ) +{ + DWORD old_prot; + void *rsrc; + DWORD size; + MEMORY_BASIC_INFORMATION info; + + if (!VirtualQuery( addr, &info, sizeof(info) )) return FALSE; + if (info.State == MEM_FREE || !(info.Type & MEM_IMAGE)) return FALSE; + if (!(rsrc = RtlImageDirectoryEntryToData( info.AllocationBase, TRUE, + IMAGE_DIRECTORY_ENTRY_RESOURCE, &size ))) + return FALSE; + if (addr < rsrc || (char *)addr >= (char *)rsrc + size) return FALSE; + TRACE( "Broken app is writing to the resource data, enabling work-around\n" ); + VirtualProtect( rsrc, size, PAGE_READWRITE, &old_prot ); + return TRUE; +} + + +/******************************************************************* + * UnhandledExceptionFilter (kernelbase.@) + */ +LONG WINAPI UnhandledExceptionFilter( EXCEPTION_POINTERS *epointers ) +{ + const EXCEPTION_RECORD *rec = epointers->ExceptionRecord; + + if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && rec->NumberParameters >= 2) + { + switch (rec->ExceptionInformation[0]) + { + case EXCEPTION_WRITE_FAULT: + if (check_resource_write( (void *)rec->ExceptionInformation[1] )) + return EXCEPTION_CONTINUE_EXECUTION; + break; + } + } + + if (!NtCurrentTeb()->Peb->BeingDebugged) + { + if (rec->ExceptionCode == CONTROL_C_EXIT) + { + /* do not launch the debugger on ^C, simply terminate the process */ + TerminateProcess( GetCurrentProcess(), 1 ); + } + + if (top_filter) + { + LONG ret = top_filter( epointers ); + if (ret != EXCEPTION_CONTINUE_SEARCH) return ret; + } + + if ((GetErrorMode() & SEM_NOGPFAULTERRORBOX) || + !start_debugger_atomic( epointers ) || !NtCurrentTeb()->Peb->BeingDebugged) + return EXCEPTION_EXECUTE_HANDLER; + } + return EXCEPTION_CONTINUE_SEARCH; +} + + +/*********************************************************************** + * WerGetFlags (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerGetFlags( HANDLE process, DWORD *flags ) +{ + FIXME( "(%p, %p) stub\n", process, flags ); + return E_NOTIMPL; +} + + +/*********************************************************************** + * WerRegisterFile (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerRegisterFile( const WCHAR *file, WER_REGISTER_FILE_TYPE type, + DWORD flags ) +{ + FIXME( "(%s, %d, %ld) stub\n", debugstr_w(file), type, flags ); + return E_NOTIMPL; +} + + +/*********************************************************************** + * WerRegisterMemoryBlock (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerRegisterMemoryBlock( void *block, DWORD size ) +{ + FIXME( "(%p %ld) stub\n", block, size ); + return E_NOTIMPL; +} + + +/*********************************************************************** + * WerRegisterRuntimeExceptionModule (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerRegisterRuntimeExceptionModule( const WCHAR *dll, void *context ) +{ + FIXME( "(%s, %p) stub\n", debugstr_w(dll), context ); + return S_OK; +} + + +/*********************************************************************** + * WerSetFlags (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerSetFlags( DWORD flags ) +{ + FIXME("(%ld) stub\n", flags); + return S_OK; +} + + +/*********************************************************************** + * WerUnregisterFile (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerUnregisterFile( const WCHAR *file ) +{ + FIXME( "(%s) stub\n", debugstr_w(file) ); + return E_NOTIMPL; +} + + +/*********************************************************************** + * WerUnregisterMemoryBlock (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerUnregisterMemoryBlock( void *block ) +{ + FIXME( "(%p) stub\n", block ); + return E_NOTIMPL; +} + + +/*********************************************************************** + * WerUnregisterRuntimeExceptionModule (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ WerUnregisterRuntimeExceptionModule( const WCHAR *dll, void *context ) +{ + FIXME( "(%s, %p) stub\n", debugstr_w(dll), context ); + return S_OK; +} + + +/*********************************************************************** + * psapi functions + ***********************************************************************/ + + +typedef struct _LDR_DATA_TABLE_ENTRY32 +{ + LIST_ENTRY32 InLoadOrderModuleList; + LIST_ENTRY32 InMemoryOrderModuleList; + LIST_ENTRY32 InInitializationOrderModuleList; + DWORD BaseAddress; + DWORD EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING32 FullDllName; + UNICODE_STRING32 BaseDllName; +} LDR_DATA_TABLE_ENTRY32; + +struct module_iterator +{ + HANDLE process; + LIST_ENTRY *head; + LIST_ENTRY *current; + BOOL wow64; + LDR_DATA_TABLE_ENTRY ldr_module; + LDR_DATA_TABLE_ENTRY32 ldr_module32; +}; + + +static BOOL init_module_iterator_wow64( struct module_iterator *iter, HANDLE process ) +{ + PEB_LDR_DATA32 *ldr_data32_ptr; + DWORD ldr_data32, first_module; + PEB32 *peb32; + + iter->wow64 = TRUE; + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessWow64Information, + &peb32, sizeof(peb32), NULL ))) + return FALSE; + if (!ReadProcessMemory( process, &peb32->LdrData, &ldr_data32, sizeof(ldr_data32), NULL )) + return FALSE; + ldr_data32_ptr = (PEB_LDR_DATA32 *)(DWORD_PTR) ldr_data32; + if (!ReadProcessMemory( process, &ldr_data32_ptr->InLoadOrderModuleList.Flink, + &first_module, sizeof(first_module), NULL )) + return FALSE; + iter->head = (LIST_ENTRY *)&ldr_data32_ptr->InLoadOrderModuleList; + iter->current = (LIST_ENTRY *)(DWORD_PTR)first_module; + iter->process = process; + return TRUE; +} + + +static BOOL init_module_iterator( struct module_iterator *iter, HANDLE process ) +{ + PROCESS_BASIC_INFORMATION pbi; + PPEB_LDR_DATA ldr_data; + + iter->wow64 = FALSE; + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessBasicInformation, + &pbi, sizeof(pbi), NULL ))) + return FALSE; + + /* read address of LdrData from PEB */ + if (!ReadProcessMemory( process, &pbi.PebBaseAddress->LdrData, &ldr_data, sizeof(ldr_data), NULL )) + return FALSE; + + /* This happens when running "old" wow64 configuration. Mark it as such. */ + if (!ldr_data) + { + SetLastError( ERROR_EMPTY ); + return FALSE; + } + /* read address of first module from LdrData */ + if (!ReadProcessMemory( process, &ldr_data->InLoadOrderModuleList.Flink, + &iter->current, sizeof(iter->current), NULL )) + return FALSE; + + iter->head = &ldr_data->InLoadOrderModuleList; + iter->process = process; + return TRUE; +} + + +static int module_iterator_next( struct module_iterator *iter ) +{ + if (iter->current == iter->head) return 0; + + if (is_win64 && iter->wow64) + { + LIST_ENTRY32 *entry32 = (LIST_ENTRY32 *)iter->current; + + if (!ReadProcessMemory( iter->process, + CONTAINING_RECORD(entry32, LDR_DATA_TABLE_ENTRY32, InLoadOrderModuleList), + &iter->ldr_module32, sizeof(iter->ldr_module32), NULL )) + return -1; + iter->current = (LIST_ENTRY *)(DWORD_PTR)iter->ldr_module32.InLoadOrderModuleList.Flink; + return 1; + } + + if (!ReadProcessMemory( iter->process, + CONTAINING_RECORD(iter->current, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks), + &iter->ldr_module, sizeof(iter->ldr_module), NULL )) + return -1; + + iter->current = iter->ldr_module.InLoadOrderLinks.Flink; + return 1; +} + + +static BOOL get_ldr_module( HANDLE process, HMODULE module, LDR_DATA_TABLE_ENTRY *ldr_module ) +{ + struct module_iterator iter; + INT ret; + + if (!init_module_iterator( &iter, process )) return FALSE; + + while ((ret = module_iterator_next( &iter )) > 0) + /* When hModule is NULL we return the process image - which will be + * the first module since our iterator uses InLoadOrderModuleList */ + if (!module || module == iter.ldr_module.DllBase) + { + *ldr_module = iter.ldr_module; + return TRUE; + } + + if (ret == 0) SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; +} + + +static BOOL get_ldr_module32( HANDLE process, HMODULE module, LDR_DATA_TABLE_ENTRY32 *ldr_module ) +{ + struct module_iterator iter; + INT ret; + +#ifdef _WIN64 + if ((ULONG_PTR)module >> 32) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } +#endif + if (!init_module_iterator_wow64( &iter, process )) return FALSE; + + while ((ret = module_iterator_next( &iter )) > 0) + /* When hModule is NULL we return the process image - which will be + * the first module since our iterator uses InLoadOrderModuleList */ + if (!module || (DWORD)(DWORD_PTR)module == iter.ldr_module32.BaseAddress) + { + *ldr_module = iter.ldr_module32; + return TRUE; + } + + if (ret == 0) SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; +} + + +/*********************************************************************** + * EmptyWorkingSet (kernelbase.@) + * K32EmptyWorkingSet (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EmptyWorkingSet( HANDLE process ) +{ + return SetProcessWorkingSetSizeEx( process, (SIZE_T)-1, (SIZE_T)-1, 0 ); +} + + +/*********************************************************************** + * EnumDeviceDrivers (kernelbase.@) + * K32EnumDeviceDrivers (kernelbase.@) + */ +BOOL WINAPI EnumDeviceDrivers( void **image_base, DWORD count, DWORD *needed ) +{ + FIXME( "(%p, %ld, %p): stub\n", image_base, count, needed ); + if (needed) *needed = 0; + return TRUE; +} + + +/*********************************************************************** + * EnumPageFilesA (kernelbase.@) + * K32EnumPageFilesA (kernelbase.@) + */ +BOOL WINAPI /* DECLSPEC_HOTPATCH */ EnumPageFilesA( PENUM_PAGE_FILE_CALLBACKA callback, void *context ) +{ + FIXME( "(%p, %p) stub\n", callback, context ); + return FALSE; +} + + +/*********************************************************************** + * EnumPageFilesW (kernelbase.@) + * K32EnumPageFilesW (kernelbase.@) + */ +BOOL WINAPI /* DECLSPEC_HOTPATCH */ EnumPageFilesW( PENUM_PAGE_FILE_CALLBACKW callback, void *context ) +{ + FIXME( "(%p, %p) stub\n", callback, context ); + return FALSE; +} + + +/*********************************************************************** + * EnumProcessModules (kernelbase.@) + * K32EnumProcessModules (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumProcessModules( HANDLE process, HMODULE *module, + DWORD count, DWORD *needed ) +{ + return EnumProcessModulesEx( process, module, count, needed, LIST_MODULES_DEFAULT ); +} + + +struct module_push +{ + HMODULE *module; + unsigned count; + unsigned size; +}; + +static void module_push( struct module_push *mp, HMODULE module ) +{ + if (mp->count >= sizeof(HMODULE)) + { + *mp->module++ = module; + mp->count -= sizeof(HMODULE); + } + mp->size += sizeof(HMODULE); +} + +static void module_push_iter( struct module_push *mp, struct module_iterator *iter ) +{ + if (is_win64 && iter->wow64) + module_push( mp, (HMODULE) (DWORD_PTR)iter->ldr_module32.BaseAddress ); + else + module_push( mp, iter->ldr_module.DllBase ); +} + +static int module_push_all( struct module_push *mp, struct module_iterator *iter ) +{ + int ret; + + while ((ret = module_iterator_next( iter )) > 0) + module_push_iter( mp, iter ); + + return ret; +} + +/*********************************************************************** + * EnumProcessModulesEx (kernelbase.@) + * K32EnumProcessModulesEx (kernelbase.@) + */ +BOOL WINAPI EnumProcessModulesEx( HANDLE process, HMODULE *module, DWORD count, + DWORD *needed, DWORD filter ) +{ + struct module_push mp = {module, count, 0}; + unsigned list_mode; + BOOL target_wow64; + INT ret = 0; + + TRACE( "(%p, %p, %ld, %p, %ld)\n", process, module, count, needed, filter ); + + if (process != GetCurrentProcess()) + { + if (!IsWow64Process( process, &target_wow64 )) return FALSE; + } + else target_wow64 = is_wow64; + + if (filter & ~LIST_MODULES_ALL) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + list_mode = filter & LIST_MODULES_ALL; + /* Can't access 64bit process from (wow64) 32bit */ + if (is_wow64 && !target_wow64) + { + SetLastError( ERROR_PARTIAL_COPY ); + return FALSE; + } + if (count && !module) + { + SetLastError( ERROR_NOACCESS ); + return FALSE; + } + + if (process == GetCurrentProcess()) + { + if (!(is_win64 && list_mode == LIST_MODULES_32BIT)) + { + PPEB_LDR_DATA ldr_data = NtCurrentTeb()->Peb->LdrData; + PLIST_ENTRY head = &ldr_data->InLoadOrderModuleList; + PLIST_ENTRY entry = head->Flink; + + while (entry != head) + { + LDR_DATA_TABLE_ENTRY *ldr = CONTAINING_RECORD( entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks ); + module_push( &mp, ldr->DllBase ); + entry = entry->Flink; + } + } + } + else + { + struct module_iterator iter; + + if (is_win64 && target_wow64 && (list_mode & LIST_MODULES_32BIT)) + { + if (!init_module_iterator_wow64( &iter, process ) || module_push_all( &mp, &iter ) < 0) + return FALSE; + } + if (!(is_win64 && list_mode == LIST_MODULES_32BIT)) + { + if (init_module_iterator( &iter, process )) + { + if (is_win64 && target_wow64 && (list_mode & LIST_MODULES_64BIT)) + /* Don't add main module twice in _ALL mode */ + ret = module_iterator_next( &iter ); + if (ret >= 0) ret = module_push_all( &mp, &iter ); + } + else if (GetLastError() == ERROR_EMPTY) + { + /* We're running on "old" wow configuration. + * Fallback to PEB32 to get at least main module if requested. + */ + if (list_mode == LIST_MODULES_DEFAULT) + { + if (init_module_iterator_wow64( &iter, process ) && module_iterator_next( &iter ) > 0) + module_push_iter( &mp, &iter ); + else + ret = -1; + } + } + else + return FALSE; + } + } + + if (!needed) + { + SetLastError( ERROR_NOACCESS ); + return FALSE; + } + *needed = mp.size; + return ret == 0; +} + + +/*********************************************************************** + * EnumProcesses (kernelbase.@) + * K32EnumProcesses (kernelbase.@) + */ +BOOL WINAPI EnumProcesses( DWORD *ids, DWORD count, DWORD *used ) +{ + SYSTEM_PROCESS_INFORMATION *spi; + ULONG size = 0x4000; + void *buf = NULL; + NTSTATUS status; + + do + { + size *= 2; + HeapFree( GetProcessHeap(), 0, buf ); + if (!(buf = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; + status = NtQuerySystemInformation( SystemProcessInformation, buf, size, NULL ); + } while (status == STATUS_INFO_LENGTH_MISMATCH); + + if (!set_ntstatus( status )) + { + HeapFree( GetProcessHeap(), 0, buf ); + return FALSE; + } + spi = buf; + for (*used = 0; count >= sizeof(DWORD); count -= sizeof(DWORD)) + { + *ids++ = HandleToUlong( spi->UniqueProcessId ); + *used += sizeof(DWORD); + if (spi->NextEntryOffset == 0) break; + spi = (SYSTEM_PROCESS_INFORMATION *)(((PCHAR)spi) + spi->NextEntryOffset); + } + HeapFree( GetProcessHeap(), 0, buf ); + return TRUE; +} + + +/*********************************************************************** + * GetDeviceDriverBaseNameA (kernelbase.@) + * K32GetDeviceDriverBaseNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetDeviceDriverBaseNameA( void *image_base, char *name, DWORD size ) +{ + FIXME( "(%p, %p, %ld): stub\n", image_base, name, size ); + if (name && size) name[0] = 0; + return 0; +} + + +/*********************************************************************** + * GetDeviceDriverBaseNameW (kernelbase.@) + * K32GetDeviceDriverBaseNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetDeviceDriverBaseNameW( void *image_base, WCHAR *name, DWORD size ) +{ + FIXME( "(%p, %p, %ld): stub\n", image_base, name, size ); + if (name && size) name[0] = 0; + return 0; +} + + +/*********************************************************************** + * GetDeviceDriverFileNameA (kernelbase.@) + * K32GetDeviceDriverFileNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetDeviceDriverFileNameA( void *image_base, char *name, DWORD size ) +{ + FIXME( "(%p, %p, %ld): stub\n", image_base, name, size ); + if (name && size) name[0] = 0; + return 0; +} + + +/*********************************************************************** + * GetDeviceDriverFileNameW (kernelbase.@) + * K32GetDeviceDriverFileNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetDeviceDriverFileNameW( void *image_base, WCHAR *name, DWORD size ) +{ + FIXME( "(%p, %p, %ld): stub\n", image_base, name, size ); + if (name && size) name[0] = 0; + return 0; +} + + +/*********************************************************************** + * GetMappedFileNameA (kernelbase.@) + * K32GetMappedFileNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetMappedFileNameA( HANDLE process, void *addr, char *name, DWORD size ) +{ + WCHAR nameW[MAX_PATH]; + DWORD len; + + if (size && !name) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (!GetMappedFileNameW( process, addr, nameW, MAX_PATH )) return 0; + if (!size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + len = file_name_WtoA( nameW, wcslen(nameW), name, size ); + name[min(len, size - 1)] = 0; + return len; +} + + +/*********************************************************************** + * GetMappedFileNameW (kernelbase.@) + * K32GetMappedFileNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetMappedFileNameW( HANDLE process, void *addr, WCHAR *name, DWORD size ) +{ + ULONG_PTR buffer[(sizeof(MEMORY_SECTION_NAME) + MAX_PATH * sizeof(WCHAR)) / sizeof(ULONG_PTR)]; + MEMORY_SECTION_NAME *mem = (MEMORY_SECTION_NAME *)buffer; + DWORD len; + + if (size && !name) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (!set_ntstatus( NtQueryVirtualMemory( process, addr, MemoryMappedFilenameInformation, + mem, sizeof(buffer), NULL ))) + return 0; + + len = mem->SectionFileName.Length / sizeof(WCHAR); + memcpy( name, mem->SectionFileName.Buffer, min( mem->SectionFileName.Length, size * sizeof(WCHAR) )); + if (len >= size) SetLastError( ERROR_INSUFFICIENT_BUFFER ); + name[min(len, size - 1)] = 0; + return len; +} + + +/*********************************************************************** + * GetModuleBaseNameA (kernelbase.@) + * K32GetModuleBaseNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetModuleBaseNameA( HANDLE process, HMODULE module, + char *name, DWORD size ) +{ + WCHAR *name_w; + DWORD len, ret = 0; + + if (!name || !size) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (!(name_w = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * size ))) return 0; + + len = GetModuleBaseNameW( process, module, name_w, size ); + TRACE( "%ld, %s\n", len, debugstr_w(name_w) ); + if (len) + { + ret = WideCharToMultiByte( CP_ACP, 0, name_w, len, name, size, NULL, NULL ); + if (ret < size) name[ret] = 0; + } + HeapFree( GetProcessHeap(), 0, name_w ); + return ret; +} + + +/*********************************************************************** + * GetModuleBaseNameW (kernelbase.@) + * K32GetModuleBaseNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetModuleBaseNameW( HANDLE process, HMODULE module, + WCHAR *name, DWORD size ) +{ + BOOL wow64, found = FALSE; + + if (!IsWow64Process( process, &wow64 )) return 0; + + if (is_win64 && wow64) + { + LDR_DATA_TABLE_ENTRY32 ldr_module32; + + if (get_ldr_module32(process, module, &ldr_module32)) + { + size = min( ldr_module32.BaseDllName.Length / sizeof(WCHAR), size ); + if (ReadProcessMemory( process, (void *)(DWORD_PTR)ldr_module32.BaseDllName.Buffer, + name, size * sizeof(WCHAR), NULL )) + found = TRUE; + } + } + if (!found) + { + LDR_DATA_TABLE_ENTRY ldr_module; + + if (!get_ldr_module( process, module, &ldr_module )) return 0; + size = min( ldr_module.BaseDllName.Length / sizeof(WCHAR), size ); + if (!ReadProcessMemory( process, ldr_module.BaseDllName.Buffer, + name, size * sizeof(WCHAR), NULL )) + return 0; + } + name[size] = 0; + return size; +} + + +/*********************************************************************** + * GetModuleFileNameExA (kernelbase.@) + * K32GetModuleFileNameExA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameExA( HANDLE process, HMODULE module, + char *name, DWORD size ) +{ + WCHAR *ptr; + DWORD len; + + TRACE( "(process=%p, module=%p, %p, %ld)\n", process, module, name, size ); + + if (!name || !size) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (process == GetCurrentProcess()) + { + len = GetModuleFileNameA( module, name, size ); + name[size - 1] = '\0'; + return len; + } + + if (!(ptr = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return 0; + len = GetModuleFileNameExW( process, module, ptr, size ); + if (!len) + { + name[0] = 0; + } + else + { + if (!WideCharToMultiByte( CP_ACP, 0, ptr, -1, name, size, NULL, NULL )) + { + name[size - 1] = 0; + len = size; + } + else if (len < size) len = strlen( name ); + } + HeapFree( GetProcessHeap(), 0, ptr ); + return len; +} + + +/*********************************************************************** + * GetModuleFileNameExW (kernelbase.@) + * K32GetModuleFileNameExW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameExW( HANDLE process, HMODULE module, + WCHAR *name, DWORD size ) +{ + BOOL wow64, found = FALSE; + DWORD len = 0; + + if (!size) return 0; + + if (!IsWow64Process( process, &wow64 )) return 0; + + if (is_win64 && wow64) + { + LDR_DATA_TABLE_ENTRY32 ldr_module32; + + if (get_ldr_module32( process, module, &ldr_module32 )) + { + len = ldr_module32.FullDllName.Length / sizeof(WCHAR); + if (ReadProcessMemory( process, (void *)(DWORD_PTR)ldr_module32.FullDllName.Buffer, + name, min( len, size ) * sizeof(WCHAR), NULL )) + found = TRUE; + } + } + if (!found) + { + LDR_DATA_TABLE_ENTRY ldr_module; + + if (!get_ldr_module(process, module, &ldr_module)) return 0; + len = ldr_module.FullDllName.Length / sizeof(WCHAR); + if (!ReadProcessMemory( process, ldr_module.FullDllName.Buffer, + name, min( len, size ) * sizeof(WCHAR), NULL )) + return 0; + } + + if (len < size) + { + name[len] = 0; + return len; + } + else + { + name[size - 1] = 0; + return size; + } +} + + +/*********************************************************************** + * GetModuleInformation (kernelbase.@) + * K32GetModuleInformation (kernelbase.@) + */ +BOOL WINAPI GetModuleInformation( HANDLE process, HMODULE module, MODULEINFO *modinfo, DWORD count ) +{ + BOOL wow64, found = FALSE; + + if (count < sizeof(MODULEINFO)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + if (!IsWow64Process( process, &wow64 )) return FALSE; + + if (is_win64 && wow64) + { + LDR_DATA_TABLE_ENTRY32 ldr_module32; + + if (get_ldr_module32( process, module, &ldr_module32 )) + { + modinfo->lpBaseOfDll = (void *)(DWORD_PTR)ldr_module32.BaseAddress; + modinfo->SizeOfImage = ldr_module32.SizeOfImage; + modinfo->EntryPoint = (void *)(DWORD_PTR)ldr_module32.EntryPoint; + found = TRUE; + } + } + if (!found) + { + LDR_DATA_TABLE_ENTRY ldr_module; + + if (!get_ldr_module( process, module, &ldr_module )) return FALSE; + modinfo->lpBaseOfDll = ldr_module.DllBase; + modinfo->SizeOfImage = ldr_module.SizeOfImage; + modinfo->EntryPoint = ldr_module.EntryPoint; + } + return TRUE; +} + + +/*********************************************************************** + * GetPerformanceInfo (kernelbase.@) + * K32GetPerformanceInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetPerformanceInfo( PPERFORMANCE_INFORMATION info, DWORD size ) +{ + SYSTEM_PERFORMANCE_INFORMATION perf; + SYSTEM_BASIC_INFORMATION basic; + SYSTEM_PROCESS_INFORMATION *process, *spi; + DWORD info_size; + NTSTATUS status; + + TRACE( "(%p, %ld)\n", info, size ); + + if (size < sizeof(*info)) + { + SetLastError( ERROR_BAD_LENGTH ); + return FALSE; + } + + status = NtQuerySystemInformation( SystemPerformanceInformation, &perf, sizeof(perf), NULL ); + if (!set_ntstatus( status )) return FALSE; + status = NtQuerySystemInformation( SystemBasicInformation, &basic, sizeof(basic), NULL ); + if (!set_ntstatus( status )) return FALSE; + + info->cb = sizeof(*info); + info->CommitTotal = perf.TotalCommittedPages; + info->CommitLimit = perf.TotalCommitLimit; + info->CommitPeak = perf.PeakCommitment; + info->PhysicalTotal = basic.MmNumberOfPhysicalPages; + info->PhysicalAvailable = perf.AvailablePages; + info->SystemCache = 0; + info->KernelTotal = perf.PagedPoolUsage + perf.NonPagedPoolUsage; + info->KernelPaged = perf.PagedPoolUsage; + info->KernelNonpaged = perf.NonPagedPoolUsage; + info->PageSize = basic.PageSize; + + /* fields from SYSTEM_PROCESS_INFORMATION */ + NtQuerySystemInformation( SystemProcessInformation, NULL, 0, &info_size ); + for (;;) + { + process = HeapAlloc( GetProcessHeap(), 0, info_size ); + if (!process) + { + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + status = NtQuerySystemInformation( SystemProcessInformation, process, info_size, &info_size ); + if (!status) break; + HeapFree( GetProcessHeap(), 0, process ); + if (status != STATUS_INFO_LENGTH_MISMATCH) + { + SetLastError( RtlNtStatusToDosError( status ) ); + return FALSE; + } + } + info->HandleCount = info->ProcessCount = info->ThreadCount = 0; + spi = process; + for (;;) + { + info->ProcessCount++; + info->HandleCount += spi->HandleCount; + info->ThreadCount += spi->dwThreadCount; + if (spi->NextEntryOffset == 0) break; + spi = (SYSTEM_PROCESS_INFORMATION *)((char *)spi + spi->NextEntryOffset); + } + HeapFree( GetProcessHeap(), 0, process ); + return TRUE; +} + + +/*********************************************************************** + * GetProcessImageFileNameA (kernelbase.@) + * K32GetProcessImageFileNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetProcessImageFileNameA( HANDLE process, char *file, DWORD size ) +{ + return QueryFullProcessImageNameA( process, PROCESS_NAME_NATIVE, file, &size ) ? size : 0; +} + + +/*********************************************************************** + * GetProcessImageFileNameW (kernelbase.@) + * K32GetProcessImageFileNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetProcessImageFileNameW( HANDLE process, WCHAR *file, DWORD size ) +{ + return QueryFullProcessImageNameW( process, PROCESS_NAME_NATIVE, file, &size ) ? size : 0; +} + + +/*********************************************************************** + * GetProcessMemoryInfo (kernelbase.@) + * K32GetProcessMemoryInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProcessMemoryInfo( HANDLE process, PROCESS_MEMORY_COUNTERS *pmc, + DWORD count ) +{ + VM_COUNTERS vmc; + + if (count < sizeof(PROCESS_MEMORY_COUNTERS)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessVmCounters, &vmc, sizeof(vmc), NULL ))) + return FALSE; + + pmc->cb = sizeof(PROCESS_MEMORY_COUNTERS); + pmc->PageFaultCount = vmc.PageFaultCount; + pmc->PeakWorkingSetSize = vmc.PeakWorkingSetSize; + pmc->WorkingSetSize = vmc.WorkingSetSize; + pmc->QuotaPeakPagedPoolUsage = vmc.QuotaPeakPagedPoolUsage; + pmc->QuotaPagedPoolUsage = vmc.QuotaPagedPoolUsage; + pmc->QuotaPeakNonPagedPoolUsage = vmc.QuotaPeakNonPagedPoolUsage; + pmc->QuotaNonPagedPoolUsage = vmc.QuotaNonPagedPoolUsage; + pmc->PagefileUsage = vmc.PagefileUsage; + pmc->PeakPagefileUsage = vmc.PeakPagefileUsage; + return TRUE; +} + + +/*********************************************************************** + * GetWsChanges (kernelbase.@) + * K32GetWsChanges (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetWsChanges( HANDLE process, PSAPI_WS_WATCH_INFORMATION *info, DWORD size ) +{ + TRACE( "(%p, %p, %ld)\n", process, info, size ); + return set_ntstatus( NtQueryInformationProcess( process, ProcessWorkingSetWatch, info, size, NULL )); +} + + +/*********************************************************************** + * GetWsChangesEx (kernelbase.@) + * K32GetWsChangesEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetWsChangesEx( HANDLE process, PSAPI_WS_WATCH_INFORMATION_EX *info, + DWORD *size ) +{ + FIXME( "(%p, %p, %p)\n", process, info, size ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * InitializeProcessForWsWatch (kernelbase.@) + * K32InitializeProcessForWsWatch (kernelbase.@) + */ +BOOL WINAPI /* DECLSPEC_HOTPATCH */ InitializeProcessForWsWatch( HANDLE process ) +{ + FIXME( "(process=%p): stub\n", process ); + return TRUE; +} + + +/*********************************************************************** + * QueryWorkingSet (kernelbase.@) + * K32QueryWorkingSet (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryWorkingSet( HANDLE process, void *buffer, DWORD size ) +{ + TRACE( "(%p, %p, %ld)\n", process, buffer, size ); + return set_ntstatus( NtQueryVirtualMemory( process, NULL, MemoryWorkingSetInformation, + buffer, size, NULL )); +} + + +/*********************************************************************** + * QueryWorkingSetEx (kernelbase.@) + * K32QueryWorkingSetEx (kernelbase.@) + */ +BOOL WINAPI QueryWorkingSetEx( HANDLE process, void *buffer, DWORD size ) +{ + TRACE( "(%p, %p, %ld)\n", process, buffer, size ); + return set_ntstatus( NtQueryVirtualMemory( process, NULL, MemoryWorkingSetExInformation, + buffer, size, NULL )); +} + + +/****************************************************************** + * QueryFullProcessImageNameA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryFullProcessImageNameA( HANDLE process, DWORD flags, + char *name, DWORD *size ) +{ + BOOL ret; + DWORD sizeW = *size; + WCHAR *nameW = HeapAlloc( GetProcessHeap(), 0, *size * sizeof(WCHAR) ); + + ret = QueryFullProcessImageNameW( process, flags, nameW, &sizeW ); + if (ret) ret = (WideCharToMultiByte( CP_ACP, 0, nameW, -1, name, *size, NULL, NULL) > 0); + if (ret) *size = strlen( name ); + HeapFree( GetProcessHeap(), 0, nameW ); + return ret; +} + + +/****************************************************************** + * QueryFullProcessImageNameW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryFullProcessImageNameW( HANDLE process, DWORD flags, + WCHAR *name, DWORD *size ) +{ + BYTE buffer[sizeof(UNICODE_STRING) + MAX_PATH*sizeof(WCHAR)]; /* this buffer should be enough */ + UNICODE_STRING *dynamic_buffer = NULL; + UNICODE_STRING *result = NULL; + NTSTATUS status; + DWORD needed; + + /* FIXME: Use ProcessImageFileName for the PROCESS_NAME_NATIVE case */ + status = NtQueryInformationProcess( process, ProcessImageFileNameWin32, buffer, + sizeof(buffer) - sizeof(WCHAR), &needed ); + if (status == STATUS_INFO_LENGTH_MISMATCH) + { + dynamic_buffer = HeapAlloc( GetProcessHeap(), 0, needed + sizeof(WCHAR) ); + status = NtQueryInformationProcess( process, ProcessImageFileNameWin32, dynamic_buffer, + needed, &needed ); + result = dynamic_buffer; + } + else + result = (UNICODE_STRING *)buffer; + + if (status) goto cleanup; + + if (flags & PROCESS_NAME_NATIVE && result->Length > 2 * sizeof(WCHAR)) + { + WCHAR drive[3]; + WCHAR device[1024]; + DWORD ntlen, devlen; + + if (result->Buffer[1] != ':' || result->Buffer[0] < 'A' || result->Buffer[0] > 'Z') + { + /* We cannot convert it to an NT device path so fail */ + status = STATUS_NO_SUCH_DEVICE; + goto cleanup; + } + + /* Find this drive's NT device path */ + drive[0] = result->Buffer[0]; + drive[1] = ':'; + drive[2] = 0; + if (!QueryDosDeviceW(drive, device, ARRAY_SIZE(device))) + { + status = STATUS_NO_SUCH_DEVICE; + goto cleanup; + } + + devlen = lstrlenW(device); + ntlen = devlen + (result->Length/sizeof(WCHAR) - 2); + if (ntlen + 1 > *size) + { + status = STATUS_BUFFER_TOO_SMALL; + goto cleanup; + } + *size = ntlen; + + memcpy( name, device, devlen * sizeof(*device) ); + memcpy( name + devlen, result->Buffer + 2, result->Length - 2 * sizeof(WCHAR) ); + name[*size] = 0; + TRACE( "NT path: %s\n", debugstr_w(name) ); + } + else + { + if (result->Length/sizeof(WCHAR) + 1 > *size) + { + status = STATUS_BUFFER_TOO_SMALL; + goto cleanup; + } + + *size = result->Length/sizeof(WCHAR); + memcpy( name, result->Buffer, result->Length ); + name[*size] = 0; + } + +cleanup: + HeapFree( GetProcessHeap(), 0, dynamic_buffer ); + return set_ntstatus( status ); +} diff --git a/dll/win32/KernelBase/wine/file.c b/dll/win32/KernelBase/wine/file.c new file mode 100644 index 0000000000000..249f476eb7e43 --- /dev/null +++ b/dll/win32/KernelBase/wine/file.c @@ -0,0 +1,4605 @@ +/* + * File handling functions + * + * Copyright 1993 John Burton + * Copyright 1996, 2004 Alexandre Julliard + * Copyright 2008 Jeff Zaroyko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "winerror.h" +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winternl.h" +#include "winioctl.h" +#include "wincon.h" +#include "fileapi.h" +#include "shlwapi.h" +#include "ddk/ntddk.h" +#include "ddk/ntddser.h" +#include "ioringapi.h" + +#include "kernelbase.h" +#include "wine/exception.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(file); + +/* info structure for FindFirstFile handle */ +typedef struct +{ + DWORD magic; /* magic number */ + HANDLE handle; /* handle to directory */ + CRITICAL_SECTION cs; /* crit section protecting this structure */ + FINDEX_SEARCH_OPS search_op; /* Flags passed to FindFirst. */ + FINDEX_INFO_LEVELS level; /* Level passed to FindFirst */ + UNICODE_STRING path; /* NT path used to open the directory */ + BOOL is_root; /* is directory the root of the drive? */ + UINT data_pos; /* current position in dir data */ + UINT data_len; /* length of dir data */ + UINT data_size; /* size of data buffer, or 0 when everything has been read */ + BYTE data[1]; /* directory data */ +} FIND_FIRST_INFO; + +#define FIND_FIRST_MAGIC 0xc0ffee11 + +static const UINT max_entry_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ); + +const WCHAR windows_dir[] = L"C:\\windows"; +const WCHAR system_dir[] = L"C:\\windows\\system32"; + +static BOOL oem_file_apis; + + +static void WINAPI read_write_apc( void *apc_user, PIO_STATUS_BLOCK io, ULONG reserved ) +{ + LPOVERLAPPED_COMPLETION_ROUTINE func = apc_user; + func( RtlNtStatusToDosError( io->Status ), io->Information, (LPOVERLAPPED)io ); +} + +static const WCHAR *get_machine_wow64_dir( WORD machine ) +{ + switch (machine) + { + case IMAGE_FILE_MACHINE_TARGET_HOST: return system_dir; + case IMAGE_FILE_MACHINE_I386: return L"C:\\windows\\syswow64"; + case IMAGE_FILE_MACHINE_ARMNT: return L"C:\\windows\\sysarm32"; + default: return NULL; + } +} + + +/*********************************************************************** + * Operations on file names + ***********************************************************************/ + + +/*********************************************************************** + * contains_path + * + * Check if the file name contains a path; helper for SearchPathW. + * A relative path is not considered a path unless it starts with ./ or ../ + */ +static inline BOOL contains_path( const WCHAR *name ) +{ + if (RtlDetermineDosPathNameType_U( name ) != RELATIVE_PATH) return TRUE; + if (name[0] != '.') return FALSE; + if (name[1] == '/' || name[1] == '\\') return TRUE; + return (name[1] == '.' && (name[2] == '/' || name[2] == '\\')); +} + + +/*********************************************************************** + * add_boot_rename_entry + * + * Adds an entry to the registry that is loaded when windows boots and + * checks if there are some files to be removed or renamed/moved. + * has to be valid and may be NULL. If both pointers are + * non-NULL then the file is moved, otherwise it is deleted. The + * entry of the registry key is always appended with two zero + * terminated strings. If is NULL then the second entry is + * simply a single 0-byte. Otherwise the second filename goes + * there. The entries are prepended with \??\ before the path and the + * second filename gets also a '!' as the first character if + * MOVEFILE_REPLACE_EXISTING is set. After the final string another + * 0-byte follows to indicate the end of the strings. + * i.e.: + * \??\D:\test\file1[0] + * !\??\D:\test\file1_renamed[0] + * \??\D:\Test|delete[0] + * [0] <- file is to be deleted, second string empty + * \??\D:\test\file2[0] + * !\??\D:\test\file2_renamed[0] + * [0] <- indicates end of strings + * + * or: + * \??\D:\test\file1[0] + * !\??\D:\test\file1_renamed[0] + * \??\D:\Test|delete[0] + * [0] <- file is to be deleted, second string empty + * [0] <- indicates end of strings + * + */ +static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags ) +{ + static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ); + + OBJECT_ATTRIBUTES attr; + UNICODE_STRING session_manager = RTL_CONSTANT_STRING( L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager" ); + UNICODE_STRING pending_file_rename_operations = RTL_CONSTANT_STRING( L"PendingFileRenameOperations" ); + UNICODE_STRING source_name, dest_name; + KEY_VALUE_PARTIAL_INFORMATION *info; + BOOL rc = FALSE; + HANDLE key = 0; + DWORD len1, len2; + DWORD size = 0; + BYTE *buffer = NULL; + WCHAR *p; + + if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + dest_name.Buffer = NULL; + if (dest && !RtlDosPathNameToNtPathName_U( dest, &dest_name, NULL, NULL )) + { + RtlFreeUnicodeString( &source_name ); + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &session_manager; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + if (NtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) + { + RtlFreeUnicodeString( &source_name ); + RtlFreeUnicodeString( &dest_name ); + return FALSE; + } + + len1 = source_name.Length + sizeof(WCHAR); + if (dest) + { + len2 = dest_name.Length + sizeof(WCHAR); + if (flags & MOVEFILE_REPLACE_EXISTING) + len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */ + } + else len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */ + + /* First we check if the key exists and if so how many bytes it already contains. */ + if (NtQueryValueKey( key, &pending_file_rename_operations, KeyValuePartialInformation, + NULL, 0, &size ) == STATUS_BUFFER_TOO_SMALL) + { + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size + len1 + len2 + sizeof(WCHAR) ))) goto done; + if (NtQueryValueKey( key, &pending_file_rename_operations, KeyValuePartialInformation, buffer, size, &size )) goto done; + info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + if (info->Type != REG_MULTI_SZ) goto done; + if (size > sizeof(info)) size -= sizeof(WCHAR); /* remove terminating null (will be added back later) */ + } + else + { + size = info_size; + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size + len1 + len2 + sizeof(WCHAR) ))) goto done; + } + + memcpy( buffer + size, source_name.Buffer, len1 ); + size += len1; + p = (WCHAR *)(buffer + size); + if (dest) + { + if (flags & MOVEFILE_REPLACE_EXISTING) *p++ = '!'; + memcpy( p, dest_name.Buffer, len2 ); + size += len2; + } + else + { + *p = 0; + size += sizeof(WCHAR); + } + + /* add final null */ + p = (WCHAR *)(buffer + size); + *p = 0; + size += sizeof(WCHAR); + rc = !NtSetValueKey( key, &pending_file_rename_operations, 0, REG_MULTI_SZ, buffer + info_size, size - info_size ); + + done: + RtlFreeUnicodeString( &source_name ); + RtlFreeUnicodeString( &dest_name ); + if (key) NtClose(key); + HeapFree( GetProcessHeap(), 0, buffer ); + return rc; +} + + +/*********************************************************************** + * append_ext + */ +static WCHAR *append_ext( const WCHAR *name, const WCHAR *ext ) +{ + const WCHAR *p; + WCHAR *ret; + DWORD len; + + if (!ext) return NULL; + p = wcsrchr( name, '.' ); + if (p && !wcschr( p, '/' ) && !wcschr( p, '\\' )) return NULL; + + len = lstrlenW( name ) + lstrlenW( ext ); + if ((ret = RtlAllocateHeap( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) + { + lstrcpyW( ret, name ); + lstrcatW( ret, ext ); + } + return ret; +} + + +/*********************************************************************** + * find_actctx_dllpath + * + * Find the path (if any) of the dll from the activation context. + * Returned path doesn't include a name. + */ +static NTSTATUS find_actctx_dllpath( const WCHAR *name, WCHAR **path ) +{ + ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *info; + ACTCTX_SECTION_KEYED_DATA data; + UNICODE_STRING nameW; + NTSTATUS status; + SIZE_T needed, size = 1024; + WCHAR *p; + + RtlInitUnicodeString( &nameW, name ); + data.cbSize = sizeof(data); + status = RtlFindActivationContextSectionString( FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL, + ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + &nameW, &data ); + if (status != STATUS_SUCCESS) return status; + + for (;;) + { + if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, size ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + status = RtlQueryInformationActivationContext( 0, data.hActCtx, &data.ulAssemblyRosterIndex, + AssemblyDetailedInformationInActivationContext, + info, size, &needed ); + if (status == STATUS_SUCCESS) break; + if (status != STATUS_BUFFER_TOO_SMALL) goto done; + RtlFreeHeap( GetProcessHeap(), 0, info ); + size = needed; + /* restart with larger buffer */ + } + + if (!info->lpAssemblyManifestPath) + { + status = STATUS_SXS_KEY_NOT_FOUND; + goto done; + } + + if ((p = wcsrchr( info->lpAssemblyManifestPath, '\\' ))) + { + DWORD dirlen = info->ulAssemblyDirectoryNameLength / sizeof(WCHAR); + + p++; + if (!dirlen || + CompareStringOrdinal( p, dirlen, info->lpAssemblyDirectoryName, dirlen, TRUE ) != CSTR_EQUAL || + wcsicmp( p + dirlen, L".manifest" )) + { + /* manifest name does not match directory name, so it's not a global + * windows/winsxs manifest; use the manifest directory name instead */ + dirlen = p - info->lpAssemblyManifestPath; + needed = (dirlen + 1) * sizeof(WCHAR); + if (!(*path = p = HeapAlloc( GetProcessHeap(), 0, needed ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + memcpy( p, info->lpAssemblyManifestPath, dirlen * sizeof(WCHAR) ); + *(p + dirlen) = 0; + goto done; + } + } + + if (!info->lpAssemblyDirectoryName) + { + status = STATUS_SXS_KEY_NOT_FOUND; + goto done; + } + + needed = sizeof(L"C:\\windows\\winsxs\\") + info->ulAssemblyDirectoryNameLength + sizeof(WCHAR); + + if (!(*path = p = RtlAllocateHeap( GetProcessHeap(), 0, needed ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + lstrcpyW( p, L"C:\\windows\\winsxs\\" ); + p += lstrlenW(p); + memcpy( p, info->lpAssemblyDirectoryName, info->ulAssemblyDirectoryNameLength ); + p += info->ulAssemblyDirectoryNameLength / sizeof(WCHAR); + *p++ = '\\'; + *p = 0; +done: + RtlFreeHeap( GetProcessHeap(), 0, info ); + RtlReleaseActivationContext( data.hActCtx ); + return status; +} + + +/*********************************************************************** + * copy_filename + */ +static DWORD copy_filename( const WCHAR *name, WCHAR *buffer, DWORD len ) +{ + UINT ret = lstrlenW( name ) + 1; + if (buffer && len >= ret) + { + lstrcpyW( buffer, name ); + ret--; + } + return ret; +} + + +/*********************************************************************** + * copy_filename_WtoA + * + * copy a file name back to OEM/Ansi, but only if the buffer is large enough + */ +static DWORD copy_filename_WtoA( LPCWSTR nameW, LPSTR buffer, DWORD len ) +{ + UNICODE_STRING strW; + DWORD ret; + + RtlInitUnicodeString( &strW, nameW ); + + ret = oem_file_apis ? RtlUnicodeStringToOemSize( &strW ) : RtlUnicodeStringToAnsiSize( &strW ); + if (buffer && ret <= len) + { + ANSI_STRING str; + + str.Buffer = buffer; + str.MaximumLength = min( len, UNICODE_STRING_MAX_CHARS ); + if (oem_file_apis) + RtlUnicodeStringToOemString( &str, &strW, FALSE ); + else + RtlUnicodeStringToAnsiString( &str, &strW, FALSE ); + ret = str.Length; /* length without terminating 0 */ + } + return ret; +} + + +/*********************************************************************** + * file_name_AtoW + * + * Convert a file name to Unicode, taking into account the OEM/Ansi API mode. + * + * If alloc is FALSE uses the TEB static buffer, so it can only be used when + * there is no possibility for the function to do that twice, taking into + * account any called function. + */ +WCHAR *file_name_AtoW( LPCSTR name, BOOL alloc ) +{ + ANSI_STRING str; + UNICODE_STRING strW, *pstrW; + NTSTATUS status; + + RtlInitAnsiString( &str, name ); + pstrW = alloc ? &strW : &NtCurrentTeb()->StaticUnicodeString; + if (oem_file_apis) + status = RtlOemStringToUnicodeString( pstrW, &str, alloc ); + else + status = RtlAnsiStringToUnicodeString( pstrW, &str, alloc ); + if (status == STATUS_SUCCESS) return pstrW->Buffer; + + if (status == STATUS_BUFFER_OVERFLOW) + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + else + SetLastError( RtlNtStatusToDosError(status) ); + return NULL; +} + + +/*********************************************************************** + * file_name_WtoA + * + * Convert a file name back to OEM/Ansi. Returns number of bytes copied. + */ +DWORD file_name_WtoA( LPCWSTR src, INT srclen, LPSTR dest, INT destlen ) +{ + DWORD ret; + + if (srclen < 0) srclen = lstrlenW( src ) + 1; + if (!destlen) + { + if (oem_file_apis) + { + UNICODE_STRING strW; + strW.Buffer = (WCHAR *)src; + strW.Length = srclen * sizeof(WCHAR); + ret = RtlUnicodeStringToOemSize( &strW ) - 1; + } + else + RtlUnicodeToMultiByteSize( &ret, src, srclen * sizeof(WCHAR) ); + } + else + { + if (oem_file_apis) + RtlUnicodeToOemN( dest, destlen, &ret, src, srclen * sizeof(WCHAR) ); + else + RtlUnicodeToMultiByteN( dest, destlen, &ret, src, srclen * sizeof(WCHAR) ); + } + return ret; +} + + +/*********************************************************************** + * is_same_file + */ +static BOOL is_same_file( HANDLE h1, HANDLE h2 ) +{ + FILE_OBJECTID_BUFFER id1, id2; + IO_STATUS_BLOCK io; + + return (!NtFsControlFile( h1, 0, NULL, NULL, &io, FSCTL_GET_OBJECT_ID, NULL, 0, &id1, sizeof(id1) ) && + !NtFsControlFile( h2, 0, NULL, NULL, &io, FSCTL_GET_OBJECT_ID, NULL, 0, &id2, sizeof(id2) ) && + !memcmp( &id1.ObjectId, &id2.ObjectId, sizeof(id1.ObjectId) )); +} + + +/****************************************************************************** + * AreFileApisANSI (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH AreFileApisANSI(void) +{ + return !oem_file_apis; +} + +/****************************************************************************** + * copy_file + */ +static BOOL copy_file( const WCHAR *source, const WCHAR *dest, COPYFILE2_EXTENDED_PARAMETERS *params ) +{ + DWORD flags = params ? params->dwCopyFlags : 0; + BOOL *cancel_ptr = params ? params->pfCancel : NULL; + PCOPYFILE2_PROGRESS_ROUTINE progress = params ? params->pProgressRoutine : NULL; + + static const int buffer_size = 65536; + HANDLE h1, h2; + FILE_BASIC_INFORMATION info; + IO_STATUS_BLOCK io; + DWORD count; + BOOL ret = FALSE; + char *buffer; + + if (cancel_ptr) + FIXME("pfCancel is not supported\n"); + if (progress) + FIXME("PCOPYFILE2_PROGRESS_ROUTINE is not supported\n"); + + if (!source || !dest) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + TRACE("%s -> %s, %lx\n", debugstr_w(source), debugstr_w(dest), flags); + + if (flags & COPY_FILE_RESTARTABLE) + FIXME("COPY_FILE_RESTARTABLE is not supported\n"); + if (flags & COPY_FILE_COPY_SYMLINK) + FIXME("COPY_FILE_COPY_SYMLINK is not supported\n"); + if (flags & COPY_FILE_OPEN_SOURCE_FOR_WRITE) + FIXME("COPY_FILE_OPEN_SOURCE_FOR_WRITE is not supported\n"); + + if ((h1 = CreateFileW( source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE) + { + WARN("Unable to open source %s\n", debugstr_w(source)); + HeapFree( GetProcessHeap(), 0, buffer ); + return FALSE; + } + + if (!set_ntstatus( NtQueryInformationFile( h1, &io, &info, sizeof(info), FileBasicInformation ))) + { + WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source)); + HeapFree( GetProcessHeap(), 0, buffer ); + CloseHandle( h1 ); + return FALSE; + } + + if (!(flags & COPY_FILE_FAIL_IF_EXISTS)) + { + BOOL same_file = FALSE; + h2 = CreateFileW( dest, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); + if (h2 != INVALID_HANDLE_VALUE) + { + same_file = is_same_file( h1, h2 ); + CloseHandle( h2 ); + } + if (same_file) + { + HeapFree( GetProcessHeap(), 0, buffer ); + CloseHandle( h1 ); + SetLastError( ERROR_SHARING_VIOLATION ); + return FALSE; + } + } + + if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + (flags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS, + info.FileAttributes, h1 )) == INVALID_HANDLE_VALUE) + { + WARN("Unable to open dest %s\n", debugstr_w(dest)); + HeapFree( GetProcessHeap(), 0, buffer ); + CloseHandle( h1 ); + return FALSE; + } + + while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count) + { + char *p = buffer; + while (count != 0) + { + DWORD res; + if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done; + p += res; + count -= res; + } + } + ret = TRUE; +done: + /* Maintain the timestamp of source file to destination file and read-only attribute */ + info.FileAttributes &= FILE_ATTRIBUTE_READONLY; + NtSetInformationFile( h2, &io, &info, sizeof(info), FileBasicInformation ); + HeapFree( GetProcessHeap(), 0, buffer ); + CloseHandle( h1 ); + CloseHandle( h2 ); + if (ret) SetLastError( 0 ); + return ret; +} + +/*********************************************************************** + * CopyFile2 (kernelbase.@) + */ +HRESULT WINAPI CopyFile2( const WCHAR *source, const WCHAR *dest, COPYFILE2_EXTENDED_PARAMETERS *params ) +{ + return copy_file(source, dest, params) ? S_OK : HRESULT_FROM_WIN32(GetLastError()); +} + + +/*********************************************************************** + * CopyFileExW (kernelbase.@) + */ +BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUTINE progress, + void *param, BOOL *cancel_ptr, DWORD flags ) +{ + COPYFILE2_EXTENDED_PARAMETERS params; + + if (progress) + FIXME("LPPROGRESS_ROUTINE is not supported\n"); + if (cancel_ptr) + FIXME("cancel_ptr is not supported\n"); + + params.dwSize = sizeof(params); + params.dwCopyFlags = flags; + params.pProgressRoutine = NULL; + params.pvCallbackContext = NULL; + params.pfCancel = NULL; + + return copy_file( source, dest, ¶ms ); +} + + +/************************************************************************** + * CopyFileW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CopyFileW( const WCHAR *source, const WCHAR *dest, BOOL fail_if_exists ) +{ + return CopyFileExW( source, dest, NULL, NULL, NULL, fail_if_exists ? COPY_FILE_FAIL_IF_EXISTS : 0 ); +} + + +/*********************************************************************** + * CreateDirectoryA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateDirectoryA( LPCSTR path, LPSECURITY_ATTRIBUTES sa ) +{ + WCHAR *pathW; + + if (!(pathW = file_name_AtoW( path, FALSE ))) return FALSE; + return CreateDirectoryW( pathW, sa ); +} + + +/*********************************************************************** + * CreateDirectoryW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateDirectoryW( LPCWSTR path, LPSECURITY_ATTRIBUTES sa ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nt_name; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + + TRACE( "%s\n", debugstr_w(path) ); + + if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL; + attr.SecurityQualityOfService = NULL; + + status = NtCreateFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, NULL, + FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_CREATE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + if (status == STATUS_SUCCESS) NtClose( handle ); + + RtlFreeUnicodeString( &nt_name ); + return set_ntstatus( status ); +} + + +/*********************************************************************** + * CreateDirectoryEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateDirectoryExW( LPCWSTR template, LPCWSTR path, + LPSECURITY_ATTRIBUTES sa ) +{ + return CreateDirectoryW( path, sa ); +} + + +/************************************************************************* + * CreateFile2 (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateFile2( LPCWSTR name, DWORD access, DWORD sharing, DWORD creation, + CREATEFILE2_EXTENDED_PARAMETERS *params ) +{ + static const DWORD attributes_mask = FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_ARCHIVE | + FILE_ATTRIBUTE_NORMAL | + FILE_ATTRIBUTE_TEMPORARY | + FILE_ATTRIBUTE_OFFLINE | + FILE_ATTRIBUTE_ENCRYPTED | + FILE_ATTRIBUTE_INTEGRITY_STREAM; + static const DWORD flags_mask = FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_DELETE_ON_CLOSE | + FILE_FLAG_NO_BUFFERING | + FILE_FLAG_OPEN_NO_RECALL | + FILE_FLAG_OPEN_REPARSE_POINT | + FILE_FLAG_OVERLAPPED | + FILE_FLAG_POSIX_SEMANTICS | + FILE_FLAG_RANDOM_ACCESS | + FILE_FLAG_SEQUENTIAL_SCAN | + FILE_FLAG_WRITE_THROUGH; + + LPSECURITY_ATTRIBUTES sa = params ? params->lpSecurityAttributes : NULL; + HANDLE template = params ? params->hTemplateFile : NULL; + DWORD attributes = params ? params->dwFileAttributes : 0; + DWORD flags = params ? params->dwFileFlags : 0; + + TRACE( "%s %#lx %#lx %#lx %p", debugstr_w(name), access, sharing, creation, params ); + if (params) FIXME( "Ignoring extended parameters %p\n", params ); + + if (attributes & ~attributes_mask) FIXME( "unsupported attributes %#lx\n", attributes ); + if (flags & ~flags_mask) FIXME( "unsupported flags %#lx\n", flags ); + attributes &= attributes_mask; + flags &= flags_mask; + + return CreateFileW( name, access, sharing, sa, creation, flags | attributes, template ); +} + + +/************************************************************************* + * CreateFileA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileA( LPCSTR name, DWORD access, DWORD sharing, + LPSECURITY_ATTRIBUTES sa, DWORD creation, + DWORD attributes, HANDLE template) +{ + WCHAR *nameW; + + if ((GetVersion() & 0x80000000) && IsBadStringPtrA( name, -1 )) return INVALID_HANDLE_VALUE; + if (!(nameW = file_name_AtoW( name, FALSE ))) return INVALID_HANDLE_VALUE; + return CreateFileW( nameW, access, sharing, sa, creation, attributes, template ); +} + +static UINT get_nt_file_options( DWORD attributes ) +{ + UINT options = 0; + + if (attributes & FILE_FLAG_BACKUP_SEMANTICS) + options |= FILE_OPEN_FOR_BACKUP_INTENT; + else + options |= FILE_NON_DIRECTORY_FILE; + if (attributes & FILE_FLAG_DELETE_ON_CLOSE) + options |= FILE_DELETE_ON_CLOSE; + if (attributes & FILE_FLAG_NO_BUFFERING) + options |= FILE_NO_INTERMEDIATE_BUFFERING; + if (!(attributes & FILE_FLAG_OVERLAPPED)) + options |= FILE_SYNCHRONOUS_IO_NONALERT; + if (attributes & FILE_FLAG_RANDOM_ACCESS) + options |= FILE_RANDOM_ACCESS; + if (attributes & FILE_FLAG_SEQUENTIAL_SCAN) + options |= FILE_SEQUENTIAL_ONLY; + if (attributes & FILE_FLAG_WRITE_THROUGH) + options |= FILE_WRITE_THROUGH; + return options; +} + +/************************************************************************* + * CreateFileW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing, + LPSECURITY_ATTRIBUTES sa, DWORD creation, + DWORD attributes, HANDLE template ) +{ + NTSTATUS status; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + IO_STATUS_BLOCK io; + HANDLE ret; + const WCHAR *vxd_name = NULL; + SECURITY_QUALITY_OF_SERVICE qos; + + static const UINT nt_disposition[5] = + { + FILE_CREATE, /* CREATE_NEW */ + FILE_OVERWRITE_IF, /* CREATE_ALWAYS */ + FILE_OPEN, /* OPEN_EXISTING */ + FILE_OPEN_IF, /* OPEN_ALWAYS */ + FILE_OVERWRITE /* TRUNCATE_EXISTING */ + }; + + + /* sanity checks */ + + if (!filename || !filename[0]) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return INVALID_HANDLE_VALUE; + } + + TRACE( "%s %s%s%s%s%s%s%s creation %ld attributes 0x%lx\n", debugstr_w(filename), + (access & GENERIC_READ) ? "GENERIC_READ " : "", + (access & GENERIC_WRITE) ? "GENERIC_WRITE " : "", + (access & GENERIC_EXECUTE) ? "GENERIC_EXECUTE " : "", + !access ? "QUERY_ACCESS " : "", + (sharing & FILE_SHARE_READ) ? "FILE_SHARE_READ " : "", + (sharing & FILE_SHARE_WRITE) ? "FILE_SHARE_WRITE " : "", + (sharing & FILE_SHARE_DELETE) ? "FILE_SHARE_DELETE " : "", + creation, attributes); + + if ((GetVersion() & 0x80000000) && !wcsncmp( filename, L"\\\\.\\", 4 ) && + !RtlIsDosDeviceName_U( filename + 4 ) && + wcsnicmp( filename + 4, L"PIPE\\", 5 ) && + wcsnicmp( filename + 4, L"MAILSLOT\\", 9 )) + { + vxd_name = filename + 4; + if (!creation) creation = OPEN_EXISTING; + } + + if (creation < CREATE_NEW || creation > TRUNCATE_EXISTING) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return INVALID_HANDLE_VALUE; + } + + if (!RtlDosPathNameToNtPathName_U( filename, &nameW, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return INVALID_HANDLE_VALUE; + } + + /* now call NtCreateFile */ + + if (attributes & FILE_FLAG_DELETE_ON_CLOSE) + access |= DELETE; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nameW; + attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL; + if (attributes & SECURITY_SQOS_PRESENT) + { + qos.Length = sizeof(qos); + qos.ImpersonationLevel = (attributes >> 16) & 0x3; + qos.ContextTrackingMode = attributes & SECURITY_CONTEXT_TRACKING ? SECURITY_DYNAMIC_TRACKING : SECURITY_STATIC_TRACKING; + qos.EffectiveOnly = (attributes & SECURITY_EFFECTIVE_ONLY) != 0; + attr.SecurityQualityOfService = &qos; + } + else + attr.SecurityQualityOfService = NULL; + + if (sa && sa->bInheritHandle) attr.Attributes |= OBJ_INHERIT; + + status = NtCreateFile( &ret, access | SYNCHRONIZE | FILE_READ_ATTRIBUTES, &attr, &io, + NULL, attributes & FILE_ATTRIBUTE_VALID_FLAGS, sharing, + nt_disposition[creation - CREATE_NEW], + get_nt_file_options( attributes ), NULL, 0 ); + if (status) + { + if (vxd_name && vxd_name[0]) + { + static HANDLE (*vxd_open)(LPCWSTR,DWORD,SECURITY_ATTRIBUTES*); + if (!vxd_open) vxd_open = (void *)GetProcAddress( GetModuleHandleW(L"krnl386.exe16"), + "__wine_vxd_open" ); + if (vxd_open && (ret = vxd_open( vxd_name, access, sa ))) goto done; + } + + WARN("Unable to create file %s (status %lx)\n", debugstr_w(filename), status); + ret = INVALID_HANDLE_VALUE; + + /* In the case file creation was rejected due to CREATE_NEW flag + * was specified and file with that name already exists, correct + * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS. + * Note: RtlNtStatusToDosError is not the subject to blame here. + */ + if (status == STATUS_OBJECT_NAME_COLLISION) + SetLastError( ERROR_FILE_EXISTS ); + else + SetLastError( RtlNtStatusToDosError(status) ); + } + else + { + if ((creation == CREATE_ALWAYS && io.Information == FILE_OVERWRITTEN) || + (creation == OPEN_ALWAYS && io.Information == FILE_OPENED)) + SetLastError( ERROR_ALREADY_EXISTS ); + else + SetLastError( 0 ); + } + RtlFreeUnicodeString( &nameW ); + + done: + if (!ret) ret = INVALID_HANDLE_VALUE; + TRACE("returning %p\n", ret); + return ret; +} + + +/************************************************************************* + * CreateHardLinkA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateHardLinkA( const char *dest, const char *source, + SECURITY_ATTRIBUTES *attr ) +{ + WCHAR *sourceW, *destW; + BOOL res; + + if (!(sourceW = file_name_AtoW( source, TRUE ))) return FALSE; + if (!(destW = file_name_AtoW( dest, TRUE ))) + { + HeapFree( GetProcessHeap(), 0, sourceW ); + return FALSE; + } + res = CreateHardLinkW( destW, sourceW, attr ); + HeapFree( GetProcessHeap(), 0, sourceW ); + HeapFree( GetProcessHeap(), 0, destW ); + return res; +} + + +/************************************************************************* + * CreateHardLinkW (kernelbase.@) + */ +BOOL WINAPI CreateHardLinkW( LPCWSTR dest, LPCWSTR source, SECURITY_ATTRIBUTES *sec_attr ) +{ + UNICODE_STRING ntDest, ntSource; + FILE_LINK_INFORMATION *info = NULL; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + BOOL ret = FALSE; + HANDLE file; + ULONG size; + + TRACE( "(%s, %s, %p)\n", debugstr_w(dest), debugstr_w(source), sec_attr ); + + ntDest.Buffer = ntSource.Buffer = NULL; + if (!RtlDosPathNameToNtPathName_U( dest, &ntDest, NULL, NULL ) || + !RtlDosPathNameToNtPathName_U( source, &ntSource, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + goto done; + } + + size = offsetof( FILE_LINK_INFORMATION, FileName ) + ntDest.Length; + if (!(info = HeapAlloc( GetProcessHeap(), 0, size ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + goto done; + } + + InitializeObjectAttributes( &attr, &ntSource, OBJ_CASE_INSENSITIVE, 0, NULL ); + if (!(ret = set_ntstatus( NtOpenFile( &file, SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT ) ))) + goto done; + + info->ReplaceIfExists = FALSE; + info->RootDirectory = NULL; + info->FileNameLength = ntDest.Length; + memcpy( info->FileName, ntDest.Buffer, ntDest.Length ); + ret = set_ntstatus( NtSetInformationFile( file, &io, info, size, FileLinkInformation ) ); + NtClose( file ); + +done: + RtlFreeUnicodeString( &ntSource ); + RtlFreeUnicodeString( &ntDest ); + HeapFree( GetProcessHeap(), 0, info ); + return ret; +} + + +/************************************************************************* + * CreateSymbolicLinkW (kernelbase.@) + */ +BOOLEAN WINAPI /* DECLSPEC_HOTPATCH */ CreateSymbolicLinkW( LPCWSTR link, LPCWSTR target, DWORD flags ) +{ + FIXME( "(%s %s %ld): stub\n", debugstr_w(link), debugstr_w(target), flags ); + return TRUE; +} + + +/*********************************************************************** + * DeleteFileA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DeleteFileA( LPCSTR path ) +{ + WCHAR *pathW; + + if (!(pathW = file_name_AtoW( path, FALSE ))) return FALSE; + return DeleteFileW( pathW ); +} + + +/*********************************************************************** + * DeleteFileW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DeleteFileW( LPCWSTR path ) +{ + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + HANDLE hFile; + IO_STATUS_BLOCK io; + + TRACE( "%s\n", debugstr_w(path) ); + + if (!RtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nameW; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtCreateFile(&hFile, SYNCHRONIZE | DELETE, &attr, &io, NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE, NULL, 0); + if (status == STATUS_SUCCESS) status = NtClose(hFile); + + RtlFreeUnicodeString( &nameW ); + return set_ntstatus( status ); +} + + +/**************************************************************************** + * FindCloseChangeNotification (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindCloseChangeNotification( HANDLE handle ) +{ + return CloseHandle( handle ); +} + + +/**************************************************************************** + * FindFirstChangeNotificationA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstChangeNotificationA( LPCSTR path, BOOL subtree, DWORD filter ) +{ + WCHAR *pathW; + + if (!(pathW = file_name_AtoW( path, FALSE ))) return INVALID_HANDLE_VALUE; + return FindFirstChangeNotificationW( pathW, subtree, filter ); +} + + +/* + * NtNotifyChangeDirectoryFile may write back to the IO_STATUS_BLOCK + * asynchronously. We don't care about the contents, but it can't + * be placed on the stack since it will go out of scope when we return. + */ +static IO_STATUS_BLOCK dummy_iosb; + +/**************************************************************************** + * FindFirstChangeNotificationW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstChangeNotificationW( LPCWSTR path, BOOL subtree, DWORD filter ) +{ + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + HANDLE handle = INVALID_HANDLE_VALUE; + + TRACE( "%s %d %lx\n", debugstr_w(path), subtree, filter ); + + if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return handle; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( &handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &attr, &dummy_iosb, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + + if (!set_ntstatus( status )) return INVALID_HANDLE_VALUE; + + status = NtNotifyChangeDirectoryFile( handle, NULL, NULL, NULL, &dummy_iosb, NULL, 0, filter, subtree ); + if (status != STATUS_PENDING) + { + NtClose( handle ); + SetLastError( RtlNtStatusToDosError(status) ); + return INVALID_HANDLE_VALUE; + } + return handle; +} + + +/**************************************************************************** + * FindNextChangeNotification (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindNextChangeNotification( HANDLE handle ) +{ + NTSTATUS status = NtNotifyChangeDirectoryFile( handle, NULL, NULL, NULL, &dummy_iosb, + NULL, 0, FILE_NOTIFY_CHANGE_SIZE, 0 ); + if (status == STATUS_PENDING) return TRUE; + return set_ntstatus( status ); +} + + +/****************************************************************************** + * FindFirstFileExA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExA( const char *filename, FINDEX_INFO_LEVELS level, + void *data, FINDEX_SEARCH_OPS search_op, + void *filter, DWORD flags ) +{ + HANDLE handle; + WIN32_FIND_DATAA *dataA = data; + WIN32_FIND_DATAW dataW; + WCHAR *nameW; + + if (!(nameW = file_name_AtoW( filename, FALSE ))) return INVALID_HANDLE_VALUE; + + handle = FindFirstFileExW( nameW, level, &dataW, search_op, filter, flags ); + if (handle == INVALID_HANDLE_VALUE) return handle; + + dataA->dwFileAttributes = dataW.dwFileAttributes; + dataA->ftCreationTime = dataW.ftCreationTime; + dataA->ftLastAccessTime = dataW.ftLastAccessTime; + dataA->ftLastWriteTime = dataW.ftLastWriteTime; + dataA->nFileSizeHigh = dataW.nFileSizeHigh; + dataA->nFileSizeLow = dataW.nFileSizeLow; + file_name_WtoA( dataW.cFileName, -1, dataA->cFileName, sizeof(dataA->cFileName) ); + file_name_WtoA( dataW.cAlternateFileName, -1, dataA->cAlternateFileName, + sizeof(dataA->cAlternateFileName) ); + return handle; +} + + +/*********************************************************************** + * fixup_mask + * + * Fixup mask with wildcards for use with NtQueryDirectoryFile(). + */ +static WCHAR *fixup_mask( const WCHAR *mask ) +{ + unsigned int len = lstrlenW( mask ), i; + BOOL no_ext; + WCHAR *ret; + + if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(*mask) ))) return NULL; + memcpy( ret, mask, (len + 1) * sizeof(*mask) ); + if (!len) return ret; + no_ext = ret[len - 1] == '.'; + while (len && (ret[len - 1] == '.' || ret[len - 1] == ' ')) --len; + + for (i = 0; i < len; ++i) + { + if (ret[i] == '.' && (ret[i + 1] == '*' || ret[i + 1] == '?')) ret[i] = '\"'; + else if (ret[i] == '?') ret[i] = '>'; + } + ret[len] = 0; + if (no_ext && len && ret[len - 1] == '*') ret[len - 1] = '<'; + return ret; +} + + +/****************************************************************************** + * FindFirstFileExW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level, + LPVOID data, FINDEX_SEARCH_OPS search_op, + LPVOID filter, DWORD flags ) +{ + WCHAR *mask; + BOOL has_wildcard = FALSE; + FIND_FIRST_INFO *info = NULL; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD size, device = 0; + + TRACE( "%s %d %p %d %p %lx\n", debugstr_w(filename), level, data, search_op, filter, flags ); + + if (flags & ~FIND_FIRST_EX_LARGE_FETCH) + { + FIXME("flags not implemented 0x%08lx\n", flags ); + } + if (search_op != FindExSearchNameMatch && search_op != FindExSearchLimitToDirectories) + { + FIXME( "search_op not implemented 0x%08x\n", search_op ); + SetLastError( ERROR_INVALID_PARAMETER ); + return INVALID_HANDLE_VALUE; + } + if (level != FindExInfoStandard && level != FindExInfoBasic) + { + FIXME("info level %d not implemented\n", level ); + SetLastError( ERROR_INVALID_PARAMETER ); + return INVALID_HANDLE_VALUE; + } + + if (!RtlDosPathNameToNtPathName_U( filename, &nt_name, &mask, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return INVALID_HANDLE_VALUE; + } + + if (!mask && (device = RtlIsDosDeviceName_U( filename ))) + { + WCHAR *dir = NULL; + + /* we still need to check that the directory can be opened */ + + if (HIWORD(device)) + { + if (!(dir = HeapAlloc( GetProcessHeap(), 0, HIWORD(device) + sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + memcpy( dir, filename, HIWORD(device) ); + dir[HIWORD(device)/sizeof(WCHAR)] = 0; + } + RtlFreeUnicodeString( &nt_name ); + if (!RtlDosPathNameToNtPathName_U( dir ? dir : L".", &nt_name, &mask, NULL )) + { + HeapFree( GetProcessHeap(), 0, dir ); + SetLastError( ERROR_PATH_NOT_FOUND ); + goto error; + } + HeapFree( GetProcessHeap(), 0, dir ); + size = 0; + } + else if (!mask || !*mask) + { + SetLastError( ERROR_FILE_NOT_FOUND ); + goto error; + } + else + { + nt_name.Length = (mask - nt_name.Buffer) * sizeof(WCHAR); + has_wildcard = wcspbrk( mask, L"*?<>" ) != NULL; + if (has_wildcard) + { + size = 8192; + mask = PathFindFileNameW( filename ); + } + else size = max_entry_size; + } + + if (!(info = HeapAlloc( GetProcessHeap(), 0, offsetof( FIND_FIRST_INFO, data[size] )))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + + /* check if path is the root of the drive, skipping the \??\ prefix */ + info->is_root = FALSE; + if (nt_name.Length >= 6 * sizeof(WCHAR) && nt_name.Buffer[5] == ':') + { + DWORD pos = 6; + while (pos * sizeof(WCHAR) < nt_name.Length && nt_name.Buffer[pos] == '\\') pos++; + info->is_root = (pos * sizeof(WCHAR) >= nt_name.Length); + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( &info->handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); + if (status != STATUS_SUCCESS) + { + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + SetLastError( ERROR_PATH_NOT_FOUND ); + else + SetLastError( RtlNtStatusToDosError(status) ); + goto error; + } + + RtlInitializeCriticalSectionEx( &info->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + info->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FIND_FIRST_INFO.cs"); + info->path = nt_name; + info->magic = FIND_FIRST_MAGIC; + info->data_pos = 0; + info->data_len = 0; + info->data_size = size; + info->search_op = search_op; + info->level = level; + + if (device) + { + WIN32_FIND_DATAW *wfd = data; + + memset( wfd, 0, sizeof(*wfd) ); + memcpy( wfd->cFileName, filename + HIWORD(device)/sizeof(WCHAR), LOWORD(device) ); + wfd->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; + CloseHandle( info->handle ); + info->handle = 0; + } + else + { + WCHAR *fixedup_mask = mask; + UNICODE_STRING mask_str; + + if (has_wildcard && !(fixedup_mask = fixup_mask( mask ))) status = STATUS_NO_MEMORY; + else + { + RtlInitUnicodeString( &mask_str, fixedup_mask ); + status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, + FileBothDirectoryInformation, FALSE, &mask_str, TRUE ); + } + if (fixedup_mask != mask) HeapFree( GetProcessHeap(), 0, fixedup_mask ); + if (status) + { + FindClose( info ); + SetLastError( RtlNtStatusToDosError( status ) ); + return INVALID_HANDLE_VALUE; + } + + info->data_len = io.Information; + if (!has_wildcard) info->data_size = 0; /* we read everything */ + + if (!FindNextFileW( info, data )) + { + TRACE( "%s not found\n", debugstr_w(filename) ); + FindClose( info ); + SetLastError( ERROR_FILE_NOT_FOUND ); + return INVALID_HANDLE_VALUE; + } + if (!has_wildcard) /* we can't find two files with the same name */ + { + CloseHandle( info->handle ); + info->handle = 0; + } + } + return info; + +error: + HeapFree( GetProcessHeap(), 0, info ); + RtlFreeUnicodeString( &nt_name ); + return INVALID_HANDLE_VALUE; +} + + +/****************************************************************************** + * FindFirstFileA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileA( const char *filename, WIN32_FIND_DATAA *data ) +{ + return FindFirstFileExA( filename, FindExInfoStandard, data, FindExSearchNameMatch, NULL, 0 ); +} + + +/****************************************************************************** + * FindFirstFileW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileW( const WCHAR *filename, WIN32_FIND_DATAW *data ) +{ + return FindFirstFileExW( filename, FindExInfoStandard, data, FindExSearchNameMatch, NULL, 0 ); +} + +/****************************************************************************** + * FindFirstFileNameW (kernelbase.@) + */ +HANDLE WINAPI FindFirstFileNameW( const WCHAR *file_name, DWORD flags, DWORD *len, WCHAR *link_name ) +{ + FIXME( "(%s, %lu, %p, %p): stub!\n", debugstr_w(file_name), flags, len, link_name ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return INVALID_HANDLE_VALUE; +} + +/************************************************************************** + * FindFirstStreamW (kernelbase.@) + */ +HANDLE WINAPI FindFirstStreamW( const WCHAR *filename, STREAM_INFO_LEVELS level, void *data, DWORD flags ) +{ + FIXME("(%s, %d, %p, %lx): stub!\n", debugstr_w(filename), level, data, flags); + SetLastError( ERROR_HANDLE_EOF ); + return INVALID_HANDLE_VALUE; +} + + +/****************************************************************************** + * FindNextFileA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data ) +{ + WIN32_FIND_DATAW dataW; + + if (!FindNextFileW( handle, &dataW )) return FALSE; + data->dwFileAttributes = dataW.dwFileAttributes; + data->ftCreationTime = dataW.ftCreationTime; + data->ftLastAccessTime = dataW.ftLastAccessTime; + data->ftLastWriteTime = dataW.ftLastWriteTime; + data->nFileSizeHigh = dataW.nFileSizeHigh; + data->nFileSizeLow = dataW.nFileSizeLow; + file_name_WtoA( dataW.cFileName, -1, data->cFileName, sizeof(data->cFileName) ); + file_name_WtoA( dataW.cAlternateFileName, -1, data->cAlternateFileName, + sizeof(data->cAlternateFileName) ); + return TRUE; +} + + +/****************************************************************************** + * FindNextFileW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data ) +{ + FIND_FIRST_INFO *info = handle; + FILE_BOTH_DIR_INFORMATION *dir_info; + BOOL ret = FALSE; + NTSTATUS status; + + TRACE( "%p %p\n", handle, data ); + + if (!handle || handle == INVALID_HANDLE_VALUE || info->magic != FIND_FIRST_MAGIC) + { + SetLastError( ERROR_INVALID_HANDLE ); + return ret; + } + + RtlEnterCriticalSection( &info->cs ); + + if (!info->handle) SetLastError( ERROR_NO_MORE_FILES ); + else for (;;) + { + if (info->data_pos >= info->data_len) /* need to read some more data */ + { + IO_STATUS_BLOCK io; + + if (info->data_size) + status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, + FileBothDirectoryInformation, FALSE, NULL, FALSE ); + else + status = STATUS_NO_MORE_FILES; + + if (!set_ntstatus( status )) + { + if (status == STATUS_NO_MORE_FILES) + { + CloseHandle( info->handle ); + info->handle = 0; + } + break; + } + info->data_len = io.Information; + info->data_pos = 0; + } + + dir_info = (FILE_BOTH_DIR_INFORMATION *)(info->data + info->data_pos); + + if (dir_info->NextEntryOffset) info->data_pos += dir_info->NextEntryOffset; + else info->data_pos = info->data_len; + + /* don't return '.' and '..' in the root of the drive */ + if (info->is_root) + { + const WCHAR *file_name = dir_info->FileName; + if (dir_info->FileNameLength == sizeof(WCHAR) && file_name[0] == '.') continue; + if (dir_info->FileNameLength == 2 * sizeof(WCHAR) && + file_name[0] == '.' && file_name[1] == '.') continue; + } + + data->dwFileAttributes = dir_info->FileAttributes; + data->ftCreationTime = *(FILETIME *)&dir_info->CreationTime; + data->ftLastAccessTime = *(FILETIME *)&dir_info->LastAccessTime; + data->ftLastWriteTime = *(FILETIME *)&dir_info->LastWriteTime; + data->nFileSizeHigh = dir_info->EndOfFile.QuadPart >> 32; + data->nFileSizeLow = (DWORD)dir_info->EndOfFile.QuadPart; + data->dwReserved0 = 0; + data->dwReserved1 = 0; + + memcpy( data->cFileName, dir_info->FileName, dir_info->FileNameLength ); + data->cFileName[dir_info->FileNameLength/sizeof(WCHAR)] = 0; + + if (info->level != FindExInfoBasic) + { + memcpy( data->cAlternateFileName, dir_info->ShortName, dir_info->ShortNameLength ); + data->cAlternateFileName[dir_info->ShortNameLength/sizeof(WCHAR)] = 0; + } + else + data->cAlternateFileName[0] = 0; + + TRACE( "returning %s (%s)\n", + debugstr_w(data->cFileName), debugstr_w(data->cAlternateFileName) ); + + ret = TRUE; + break; + } + + RtlLeaveCriticalSection( &info->cs ); + return ret; +} + + +/************************************************************************** + * FindNextStreamW (kernelbase.@) + */ +BOOL WINAPI FindNextStreamW( HANDLE handle, void *data ) +{ + FIXME( "(%p, %p): stub!\n", handle, data ); + SetLastError( ERROR_HANDLE_EOF ); + return FALSE; +} + + +/****************************************************************************** + * FindClose (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindClose( HANDLE handle ) +{ + FIND_FIRST_INFO *info = handle; + + if (!handle || handle == INVALID_HANDLE_VALUE) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + __TRY + { + if (info->magic == FIND_FIRST_MAGIC) + { + RtlEnterCriticalSection( &info->cs ); + if (info->magic == FIND_FIRST_MAGIC) /* in case someone else freed it in the meantime */ + { + info->magic = 0; + if (info->handle) CloseHandle( info->handle ); + info->handle = 0; + RtlFreeUnicodeString( &info->path ); + info->data_pos = 0; + info->data_len = 0; + RtlLeaveCriticalSection( &info->cs ); + info->cs.DebugInfo->Spare[0] = 0; + RtlDeleteCriticalSection( &info->cs ); + HeapFree( GetProcessHeap(), 0, info ); + } + } + } + __EXCEPT_PAGE_FAULT + { + WARN( "illegal handle %p\n", handle ); + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + __ENDTRY + + return TRUE; +} + + +/****************************************************************************** + * GetCompressedFileSizeA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetCompressedFileSizeA( LPCSTR name, LPDWORD size_high ) +{ + WCHAR *nameW; + + if (!(nameW = file_name_AtoW( name, FALSE ))) return INVALID_FILE_SIZE; + return GetCompressedFileSizeW( nameW, size_high ); +} + + +/****************************************************************************** + * GetCompressedFileSizeW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetCompressedFileSizeW( LPCWSTR name, LPDWORD size_high ) +{ + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + DWORD ret; + + TRACE("%s %p\n", debugstr_w(name), size_high); + + if (!RtlDosPathNameToNtPathName_U( name, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return INVALID_FILE_SIZE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( &handle, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + if (!set_ntstatus( status )) return INVALID_FILE_SIZE; + + /* we don't support compressed files, simply return the file size */ + ret = GetFileSize( handle, size_high ); + NtClose( handle ); + return ret; +} + + +/*********************************************************************** + * GetCurrentDirectoryA (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetCurrentDirectoryA( UINT buflen, LPSTR buf ) +{ + WCHAR bufferW[MAX_PATH]; + DWORD ret; + + if (buflen && buf && ((ULONG_PTR)buf >> 16) == 0) + { + /* Win9x catches access violations here, returning zero. + * This behaviour resulted in some people not noticing + * that they got the argument order wrong. So let's be + * nice and fail gracefully if buf is invalid and looks + * more like a buflen. */ + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + ret = RtlGetCurrentDirectory_U( sizeof(bufferW), bufferW ); + if (!ret) return 0; + if (ret > sizeof(bufferW)) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + return copy_filename_WtoA( bufferW, buf, buflen ); +} + + +/*********************************************************************** + * GetCurrentDirectoryW (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetCurrentDirectoryW( UINT buflen, LPWSTR buf ) +{ + return RtlGetCurrentDirectory_U( buflen * sizeof(WCHAR), buf ) / sizeof(WCHAR); +} + + +/************************************************************************** + * GetFileAttributesA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFileAttributesA( LPCSTR name ) +{ + WCHAR *nameW; + + if (!(nameW = file_name_AtoW( name, FALSE ))) return INVALID_FILE_ATTRIBUTES; + return GetFileAttributesW( nameW ); +} + + +/************************************************************************** + * GetFileAttributesW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFileAttributesW( LPCWSTR name ) +{ + FILE_BASIC_INFORMATION info; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + + TRACE( "%s\n", debugstr_w(name) ); + + if (!RtlDosPathNameToNtPathName_U( name, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return INVALID_FILE_ATTRIBUTES; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtQueryAttributesFile( &attr, &info ); + RtlFreeUnicodeString( &nt_name ); + + if (status == STATUS_SUCCESS) return info.FileAttributes; + + /* NtQueryAttributesFile fails on devices, but GetFileAttributesW succeeds */ + if (RtlIsDosDeviceName_U( name )) return FILE_ATTRIBUTE_ARCHIVE; + + SetLastError( RtlNtStatusToDosError(status) ); + return INVALID_FILE_ATTRIBUTES; +} + + +/************************************************************************** + * GetFileAttributesExA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetFileAttributesExA( LPCSTR name, GET_FILEEX_INFO_LEVELS level, void *ptr ) +{ + WCHAR *nameW; + + if (!(nameW = file_name_AtoW( name, FALSE ))) return FALSE; + return GetFileAttributesExW( nameW, level, ptr ); +} + + +/************************************************************************** + * GetFileAttributesExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetFileAttributesExW( LPCWSTR name, GET_FILEEX_INFO_LEVELS level, void *ptr ) +{ + FILE_NETWORK_OPEN_INFORMATION info; + WIN32_FILE_ATTRIBUTE_DATA *data = ptr; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + + TRACE("%s %d %p\n", debugstr_w(name), level, ptr); + + if (level != GetFileExInfoStandard) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (!RtlDosPathNameToNtPathName_U( name, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtQueryFullAttributesFile( &attr, &info ); + RtlFreeUnicodeString( &nt_name ); + if (!set_ntstatus( status )) return FALSE; + + data->dwFileAttributes = info.FileAttributes; + data->ftCreationTime.dwLowDateTime = info.CreationTime.u.LowPart; + data->ftCreationTime.dwHighDateTime = info.CreationTime.u.HighPart; + data->ftLastAccessTime.dwLowDateTime = info.LastAccessTime.u.LowPart; + data->ftLastAccessTime.dwHighDateTime = info.LastAccessTime.u.HighPart; + data->ftLastWriteTime.dwLowDateTime = info.LastWriteTime.u.LowPart; + data->ftLastWriteTime.dwHighDateTime = info.LastWriteTime.u.HighPart; + data->nFileSizeLow = info.EndOfFile.u.LowPart; + data->nFileSizeHigh = info.EndOfFile.u.HighPart; + return TRUE; +} + + +/*********************************************************************** + * GetFinalPathNameByHandleA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleA( HANDLE file, LPSTR path, + DWORD count, DWORD flags ) +{ + WCHAR *str; + DWORD result, len; + + TRACE( "(%p,%p,%ld,%lx)\n", file, path, count, flags); + + len = GetFinalPathNameByHandleW(file, NULL, 0, flags); + if (len == 0) return 0; + + str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!str) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + + result = GetFinalPathNameByHandleW(file, str, len, flags); + if (result != len - 1) + { + HeapFree(GetProcessHeap(), 0, str); + return 0; + } + + len = file_name_WtoA( str, -1, NULL, 0 ); + if (count < len) + { + HeapFree(GetProcessHeap(), 0, str); + return len - 1; + } + file_name_WtoA( str, -1, path, count ); + HeapFree(GetProcessHeap(), 0, str); + return len - 1; +} + + +/*********************************************************************** + * GetFinalPathNameByHandleW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR path, + DWORD count, DWORD flags ) +{ + WCHAR buffer[sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH + 1]; + OBJECT_NAME_INFORMATION *info = (OBJECT_NAME_INFORMATION*)&buffer; + WCHAR drive_part[MAX_PATH]; + DWORD drive_part_len = 0; + NTSTATUS status; + DWORD result = 0; + ULONG dummy; + WCHAR *ptr; + + TRACE( "(%p,%p,%ld,%lx)\n", file, path, count, flags ); + + if (flags & ~(FILE_NAME_OPENED | VOLUME_NAME_GUID | VOLUME_NAME_NONE | VOLUME_NAME_NT)) + { + WARN("Unknown flags: %lx\n", flags); + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + /* get object name */ + status = NtQueryObject( file, ObjectNameInformation, &buffer, sizeof(buffer) - sizeof(WCHAR), &dummy ); + if (!set_ntstatus( status )) return 0; + + if (!info->Name.Buffer) + { + SetLastError( ERROR_INVALID_HANDLE ); + return 0; + } + if (info->Name.Length < 4 * sizeof(WCHAR) || info->Name.Buffer[0] != '\\' || + info->Name.Buffer[1] != '?' || info->Name.Buffer[2] != '?' || info->Name.Buffer[3] != '\\' ) + { + FIXME("Unexpected object name: %s\n", debugstr_wn(info->Name.Buffer, info->Name.Length / sizeof(WCHAR))); + SetLastError( ERROR_GEN_FAILURE ); + return 0; + } + + /* add terminating null character, remove "\\??\\" */ + info->Name.Buffer[info->Name.Length / sizeof(WCHAR)] = 0; + info->Name.Length -= 4 * sizeof(WCHAR); + info->Name.Buffer += 4; + + /* FILE_NAME_OPENED is not supported yet, and would require Wineserver changes */ + if (flags & FILE_NAME_OPENED) + { + FIXME("FILE_NAME_OPENED not supported\n"); + flags &= ~FILE_NAME_OPENED; + } + + /* Get information required for VOLUME_NAME_NONE, VOLUME_NAME_GUID and VOLUME_NAME_NT */ + if (flags == VOLUME_NAME_NONE || flags == VOLUME_NAME_GUID || flags == VOLUME_NAME_NT) + { + if (!GetVolumePathNameW( info->Name.Buffer, drive_part, MAX_PATH )) return 0; + drive_part_len = lstrlenW(drive_part); + if (!drive_part_len || drive_part_len > lstrlenW(info->Name.Buffer) || + drive_part[drive_part_len-1] != '\\' || + CompareStringOrdinal( info->Name.Buffer, drive_part_len, drive_part, drive_part_len, TRUE ) != CSTR_EQUAL) + { + FIXME( "Path %s returned by GetVolumePathNameW does not match file path %s\n", + debugstr_w(drive_part), debugstr_w(info->Name.Buffer) ); + SetLastError( ERROR_GEN_FAILURE ); + return 0; + } + } + + if (flags == VOLUME_NAME_NONE) + { + ptr = info->Name.Buffer + drive_part_len - 1; + result = lstrlenW(ptr); + if (result < count) memcpy(path, ptr, (result + 1) * sizeof(WCHAR)); + else result++; + } + else if (flags == VOLUME_NAME_GUID) + { + WCHAR volume_prefix[51]; + + /* GetVolumeNameForVolumeMountPointW sets error code on failure */ + if (!GetVolumeNameForVolumeMountPointW( drive_part, volume_prefix, 50 )) return 0; + ptr = info->Name.Buffer + drive_part_len; + result = lstrlenW(volume_prefix) + lstrlenW(ptr); + if (result < count) + { + lstrcpyW(path, volume_prefix); + lstrcatW(path, ptr); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else if (flags == VOLUME_NAME_NT) + { + WCHAR nt_prefix[MAX_PATH]; + + /* QueryDosDeviceW sets error code on failure */ + drive_part[drive_part_len - 1] = 0; + if (!QueryDosDeviceW( drive_part, nt_prefix, MAX_PATH )) return 0; + ptr = info->Name.Buffer + drive_part_len - 1; + result = lstrlenW(nt_prefix) + lstrlenW(ptr); + if (result < count) + { + lstrcpyW(path, nt_prefix); + lstrcatW(path, ptr); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else if (flags == VOLUME_NAME_DOS) + { + result = 4 + lstrlenW(info->Name.Buffer); + if (result < count) + { + lstrcpyW(path, L"\\\\?\\"); + lstrcatW(path, info->Name.Buffer); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else + { + /* Windows crashes here, but we prefer returning ERROR_INVALID_PARAMETER */ + WARN("Invalid combination of flags: %lx\n", flags); + SetLastError( ERROR_INVALID_PARAMETER ); + } + return result; +} + + +/*********************************************************************** + * GetFullPathNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer, LPSTR *lastpart ) +{ + WCHAR *nameW; + WCHAR bufferW[MAX_PATH], *lastpartW = NULL; + DWORD ret; + + if (!(nameW = file_name_AtoW( name, FALSE ))) return 0; + + ret = GetFullPathNameW( nameW, MAX_PATH, bufferW, &lastpartW ); + + if (!ret) return 0; + if (ret > MAX_PATH) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + ret = copy_filename_WtoA( bufferW, buffer, len ); + if (ret < len && lastpart) + { + if (lastpartW) + *lastpart = buffer + file_name_WtoA( bufferW, lastpartW - bufferW, NULL, 0 ); + else + *lastpart = NULL; + } + return ret; +} + + +/*********************************************************************** + * GetFullPathNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer, LPWSTR *lastpart ) +{ + return RtlGetFullPathName_U( name, len * sizeof(WCHAR), buffer, lastpart ) / sizeof(WCHAR); +} + + +/*********************************************************************** + * GetLongPathNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen ) +{ + WCHAR *shortpathW; + WCHAR longpathW[MAX_PATH]; + DWORD ret; + + TRACE( "%s\n", debugstr_a( shortpath )); + + if (!(shortpathW = file_name_AtoW( shortpath, FALSE ))) return 0; + + ret = GetLongPathNameW( shortpathW, longpathW, MAX_PATH ); + + if (!ret) return 0; + if (ret > MAX_PATH) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + return copy_filename_WtoA( longpathW, longpath, longlen ); +} + + +/*********************************************************************** + * GetLongPathNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen ) +{ + WCHAR tmplongpath[1024]; + DWORD sp = 0, lp = 0, tmplen; + WIN32_FIND_DATAW wfd; + UNICODE_STRING nameW; + LPCWSTR p; + HANDLE handle; + + TRACE("%s,%p,%lu\n", debugstr_w(shortpath), longpath, longlen); + + if (!shortpath) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (!shortpath[0]) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return 0; + } + + if (shortpath[0] == '\\' && shortpath[1] == '\\') + { + FIXME( "UNC pathname %s\n", debugstr_w(shortpath) ); + tmplen = lstrlenW( shortpath ); + if (tmplen < longlen) + { + if (longpath != shortpath) lstrcpyW( longpath, shortpath ); + return tmplen; + } + return tmplen + 1; + } + + /* check for drive letter */ + if (shortpath[0] != '/' && shortpath[1] == ':' ) + { + tmplongpath[0] = shortpath[0]; + tmplongpath[1] = ':'; + lp = sp = 2; + } + + if (wcspbrk( shortpath + sp, L"*?" )) + { + SetLastError( ERROR_INVALID_NAME ); + return 0; + } + + while (shortpath[sp]) + { + /* check for path delimiters and reproduce them */ + if (shortpath[sp] == '\\' || shortpath[sp] == '/') + { + tmplongpath[lp++] = shortpath[sp++]; + tmplongpath[lp] = 0; /* terminate string */ + continue; + } + + for (p = shortpath + sp; *p && *p != '/' && *p != '\\'; p++); + tmplen = p - (shortpath + sp); + lstrcpynW( tmplongpath + lp, shortpath + sp, tmplen + 1 ); + + if (tmplongpath[lp] == '.') + { + if (tmplen == 1 || (tmplen == 2 && tmplongpath[lp + 1] == '.')) + { + lp += tmplen; + sp += tmplen; + continue; + } + } + + /* Check if the file exists */ + handle = FindFirstFileW( tmplongpath, &wfd ); + if (handle == INVALID_HANDLE_VALUE) + { + TRACE( "not found %s\n", debugstr_w( tmplongpath )); + SetLastError ( ERROR_FILE_NOT_FOUND ); + return 0; + } + FindClose( handle ); + + /* Use the existing file name if it's a short name */ + RtlInitUnicodeString( &nameW, tmplongpath + lp ); + if (RtlIsNameLegalDOS8Dot3( &nameW, NULL, NULL )) lstrcpyW( tmplongpath + lp, wfd.cFileName ); + lp += lstrlenW( tmplongpath + lp ); + sp += tmplen; + } + tmplen = lstrlenW( shortpath ) - 1; + if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') && + (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\')) + tmplongpath[lp++] = shortpath[tmplen]; + tmplongpath[lp] = 0; + + tmplen = lstrlenW( tmplongpath ) + 1; + if (tmplen <= longlen) + { + lstrcpyW( longpath, tmplongpath ); + TRACE("returning %s\n", debugstr_w( longpath )); + tmplen--; /* length without 0 */ + } + return tmplen; +} + + +/*********************************************************************** + * GetShortPathNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen ) +{ + WIN32_FIND_DATAW wfd; + WCHAR *tmpshortpath; + HANDLE handle; + LPCWSTR p; + DWORD sp = 0, lp = 0, tmplen, buf_len; + + TRACE( "%s,%p,%lu\n", debugstr_w(longpath), shortpath, shortlen ); + + if (!longpath) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (!longpath[0]) + { + SetLastError( ERROR_BAD_PATHNAME ); + return 0; + } + + /* code below only removes characters from string, never adds, so this is + * the largest buffer that tmpshortpath will need to have */ + buf_len = lstrlenW(longpath) + 1; + tmpshortpath = HeapAlloc( GetProcessHeap(), 0, buf_len * sizeof(WCHAR) ); + if (!tmpshortpath) + { + SetLastError( ERROR_OUTOFMEMORY ); + return 0; + } + + if (longpath[0] == '\\' && longpath[1] == '\\' && longpath[2] == '?' && longpath[3] == '\\') + { + memcpy( tmpshortpath, longpath, 4 * sizeof(WCHAR) ); + sp = lp = 4; + } + + if (wcspbrk( longpath + lp, L"*?" )) + { + HeapFree( GetProcessHeap(), 0, tmpshortpath ); + SetLastError( ERROR_INVALID_NAME ); + return 0; + } + + /* check for drive letter */ + if (longpath[lp] != '/' && longpath[lp + 1] == ':' ) + { + tmpshortpath[sp] = longpath[lp]; + tmpshortpath[sp + 1] = ':'; + sp += 2; + lp += 2; + } + + while (longpath[lp]) + { + /* check for path delimiters and reproduce them */ + if (longpath[lp] == '\\' || longpath[lp] == '/') + { + tmpshortpath[sp++] = longpath[lp++]; + tmpshortpath[sp] = 0; /* terminate string */ + continue; + } + + p = longpath + lp; + for (; *p && *p != '/' && *p != '\\'; p++); + tmplen = p - (longpath + lp); + lstrcpynW( tmpshortpath + sp, longpath + lp, tmplen + 1 ); + + if (tmpshortpath[sp] == '.') + { + if (tmplen == 1 || (tmplen == 2 && tmpshortpath[sp + 1] == '.')) + { + sp += tmplen; + lp += tmplen; + continue; + } + } + + /* Check if the file exists and use the existing short file name */ + handle = FindFirstFileW( tmpshortpath, &wfd ); + if (handle == INVALID_HANDLE_VALUE) goto notfound; + FindClose( handle ); + + /* In rare cases (like "a.abcd") short path may be longer than original path. + * Make sure we have enough space in temp buffer. */ + if (wfd.cAlternateFileName[0] && tmplen < lstrlenW(wfd.cAlternateFileName)) + { + WCHAR *new_buf; + buf_len += lstrlenW( wfd.cAlternateFileName ) - tmplen; + new_buf = HeapReAlloc( GetProcessHeap(), 0, tmpshortpath, buf_len * sizeof(WCHAR) ); + if(!new_buf) + { + HeapFree( GetProcessHeap(), 0, tmpshortpath ); + SetLastError( ERROR_OUTOFMEMORY ); + return 0; + } + tmpshortpath = new_buf; + } + + lstrcpyW( tmpshortpath + sp, wfd.cAlternateFileName[0] ? wfd.cAlternateFileName : wfd.cFileName ); + sp += lstrlenW( tmpshortpath + sp ); + lp += tmplen; + } + tmpshortpath[sp] = 0; + + tmplen = lstrlenW( tmpshortpath ) + 1; + if (tmplen <= shortlen) + { + lstrcpyW( shortpath, tmpshortpath ); + TRACE( "returning %s\n", debugstr_w( shortpath )); + tmplen--; /* length without 0 */ + } + + HeapFree( GetProcessHeap(), 0, tmpshortpath ); + return tmplen; + + notfound: + HeapFree( GetProcessHeap(), 0, tmpshortpath ); + TRACE( "not found\n" ); + SetLastError( ERROR_FILE_NOT_FOUND ); + return 0; +} + + +/*********************************************************************** + * GetSystemDirectoryA (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetSystemDirectoryA( LPSTR path, UINT count ) +{ + return copy_filename_WtoA( system_dir, path, count ); +} + + +/*********************************************************************** + * GetSystemDirectoryW (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetSystemDirectoryW( LPWSTR path, UINT count ) +{ + return copy_filename( system_dir, path, count ); +} + + +/*********************************************************************** + * GetSystemWindowsDirectoryA (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetSystemWindowsDirectoryA( LPSTR path, UINT count ) +{ + return GetWindowsDirectoryA( path, count ); +} + + +/*********************************************************************** + * GetSystemWindowsDirectoryW (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetSystemWindowsDirectoryW( LPWSTR path, UINT count ) +{ + return GetWindowsDirectoryW( path, count ); +} + + +/*********************************************************************** + * GetSystemWow64DirectoryA (kernelbase.@) + */ +UINT WINAPI /* DECLSPEC_HOTPATCH */ GetSystemWow64DirectoryA( LPSTR path, UINT count ) +{ + if (!is_win64 && !is_wow64) + { + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; + } + return copy_filename_WtoA( get_machine_wow64_dir( IMAGE_FILE_MACHINE_I386 ), path, count ); +} + + +/*********************************************************************** + * GetSystemWow64DirectoryW (kernelbase.@) + */ +UINT WINAPI /* DECLSPEC_HOTPATCH */ GetSystemWow64DirectoryW( LPWSTR path, UINT count ) +{ + if (!is_win64 && !is_wow64) + { + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; + } + return copy_filename( get_machine_wow64_dir( IMAGE_FILE_MACHINE_I386 ), path, count ); +} + + +/*********************************************************************** + * GetSystemWow64Directory2A (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetSystemWow64Directory2A( LPSTR path, UINT count, WORD machine ) +{ + const WCHAR *dir = get_machine_wow64_dir( machine ); + + return dir ? copy_filename_WtoA( dir, path, count ) : 0; +} + + +/*********************************************************************** + * GetSystemWow64Directory2W (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetSystemWow64Directory2W( LPWSTR path, UINT count, WORD machine ) +{ + const WCHAR *dir = get_machine_wow64_dir( machine ); + + return dir ? copy_filename( dir, path, count ) : 0; +} + + +/*********************************************************************** + * GetTempFileNameA (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique, LPSTR buffer ) +{ + WCHAR *pathW, *prefixW = NULL; + WCHAR bufferW[MAX_PATH]; + UINT ret; + + if (!(pathW = file_name_AtoW( path, FALSE ))) return 0; + if (prefix && !(prefixW = file_name_AtoW( prefix, TRUE ))) return 0; + + ret = GetTempFileNameW( pathW, prefixW, unique, bufferW ); + if (ret) file_name_WtoA( bufferW, -1, buffer, MAX_PATH ); + + HeapFree( GetProcessHeap(), 0, prefixW ); + return ret; +} + + +/*********************************************************************** + * GetTempFileNameW (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique, LPWSTR buffer ) +{ + int i; + LPWSTR p; + DWORD attr; + + if (!path || !buffer) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + /* ensure that the provided directory exists */ + attr = GetFileAttributesW( path ); + if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) + { + TRACE( "path not found %s\n", debugstr_w( path )); + SetLastError( ERROR_DIRECTORY ); + return 0; + } + + lstrcpyW( buffer, path ); + p = buffer + lstrlenW(buffer); + + /* add a \, if there isn't one */ + if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\'; + + if (prefix) for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++; + + unique &= 0xffff; + if (unique) swprintf( p, MAX_PATH - (p - buffer), L"%x.tmp", unique ); + else + { + /* get a "random" unique number and try to create the file */ + HANDLE handle; + UINT num = NtGetTickCount() & 0xffff; + static UINT last; + + /* avoid using the same name twice in a short interval */ + if (last - num < 10) num = last + 1; + if (!num) num = 1; + unique = num; + do + { + swprintf( p, MAX_PATH - (p - buffer), L"%x.tmp", unique ); + handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 ); + if (handle != INVALID_HANDLE_VALUE) + { /* We created it */ + CloseHandle( handle ); + last = unique; + break; + } + if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_SHARING_VIOLATION) + break; /* No need to go on */ + if (!(++unique & 0xffff)) unique = 1; + } while (unique != num); + } + TRACE( "returning %s\n", debugstr_w( buffer )); + return unique; +} + + +/*********************************************************************** + * GetTempPathA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetTempPathA( DWORD count, LPSTR path ) +{ + WCHAR pathW[MAX_PATH]; + UINT ret; + + if (!(ret = GetTempPathW( MAX_PATH, pathW ))) return 0; + if (ret > MAX_PATH) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + return copy_filename_WtoA( pathW, path, count ); +} + + +/*********************************************************************** + * GetTempPathW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetTempPathW( DWORD count, LPWSTR path ) +{ + WCHAR tmp_path[MAX_PATH]; + UINT ret; + + if (!(ret = GetEnvironmentVariableW( L"TMP", tmp_path, MAX_PATH )) && + !(ret = GetEnvironmentVariableW( L"TEMP", tmp_path, MAX_PATH )) && + !(ret = GetEnvironmentVariableW( L"USERPROFILE", tmp_path, MAX_PATH )) && + !(ret = GetWindowsDirectoryW( tmp_path, MAX_PATH ))) + return 0; + + if (ret > MAX_PATH) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + ret = GetFullPathNameW( tmp_path, MAX_PATH, tmp_path, NULL ); + if (!ret) return 0; + + if (ret > MAX_PATH - 2) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + if (tmp_path[ret-1] != '\\') + { + tmp_path[ret++] = '\\'; + tmp_path[ret] = '\0'; + } + + ret++; /* add space for terminating 0 */ + if (count >= ret) + { + lstrcpynW( path, tmp_path, count ); + /* the remaining buffer must be zeroed up to 32766 bytes in XP or 32767 + * bytes after it, we will assume the > XP behavior for now */ + memset( path + ret, 0, (min(count, 32767) - ret) * sizeof(WCHAR) ); + ret--; /* return length without 0 */ + } + else if (count) + { + /* the buffer must be cleared if contents will not fit */ + memset( path, 0, count * sizeof(WCHAR) ); + } + + TRACE( "returning %u, %s\n", ret, debugstr_w( path )); + return ret; +} + + +/*********************************************************************** + * GetTempPath2A (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetTempPath2A(DWORD count, LPSTR path) +{ + /* TODO: Set temp path to C:\Windows\SystemTemp\ when a SYSTEM process calls this function */ + FIXME("(%lu, %s) semi-stub\n", count, path); + return GetTempPathA(count, path); +} + + +/*********************************************************************** + * GetTempPath2W (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetTempPath2W(DWORD count, LPWSTR path) +{ + /* TODO: Set temp path to C:\Windows\SystemTemp\ when a SYSTEM process calls this function */ + FIXME("(%lu, %s) semi-stub\n", count, debugstr_w(path)); + return GetTempPathW(count, path); +} + + +/*********************************************************************** + * GetWindowsDirectoryA (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetWindowsDirectoryA( LPSTR path, UINT count ) +{ + return copy_filename_WtoA( windows_dir, path, count ); +} + + +/*********************************************************************** + * GetWindowsDirectoryW (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetWindowsDirectoryW( LPWSTR path, UINT count ) +{ + return copy_filename( windows_dir, path, count ); +} + + +/************************************************************************** + * MoveFileExW (kernelbase.@) + */ +BOOL WINAPI MoveFileExW( const WCHAR *source, const WCHAR *dest, DWORD flag ) +{ + return MoveFileWithProgressW( source, dest, NULL, NULL, flag ); +} + + +/************************************************************************** + * MoveFileWithProgressW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH MoveFileWithProgressW( const WCHAR *source, const WCHAR *dest, + LPPROGRESS_ROUTINE progress, + void *param, DWORD flag ) +{ + FILE_RENAME_INFORMATION *rename_info; + FILE_BASIC_INFORMATION info; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE source_handle = 0; + ULONG size; + + TRACE( "(%s,%s,%p,%p,%04lx)\n", debugstr_w(source), debugstr_w(dest), progress, param, flag ); + + if (flag & MOVEFILE_DELAY_UNTIL_REBOOT) return add_boot_rename_entry( source, dest, flag ); + + if (!dest) return DeleteFileW( source ); + + /* check if we are allowed to rename the source */ + + if (!RtlDosPathNameToNtPathName_U( source, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( &source_handle, DELETE | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + if (!set_ntstatus( status )) goto error; + + status = NtQueryInformationFile( source_handle, &io, &info, sizeof(info), FileBasicInformation ); + if (!set_ntstatus( status )) goto error; + + if (!RtlDosPathNameToNtPathName_U( dest, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + goto error; + } + + size = offsetof( FILE_RENAME_INFORMATION, FileName ) + nt_name.Length; + if (!(rename_info = HeapAlloc( GetProcessHeap(), 0, size ))) goto error; + + rename_info->ReplaceIfExists = !!(flag & MOVEFILE_REPLACE_EXISTING); + rename_info->RootDirectory = NULL; + rename_info->FileNameLength = nt_name.Length; + memcpy( rename_info->FileName, nt_name.Buffer, nt_name.Length ); + RtlFreeUnicodeString( &nt_name ); + status = NtSetInformationFile( source_handle, &io, rename_info, size, FileRenameInformation ); + HeapFree( GetProcessHeap(), 0, rename_info ); + if (status == STATUS_NOT_SAME_DEVICE && (flag & MOVEFILE_COPY_ALLOWED)) + { + NtClose( source_handle ); + if (!CopyFileExW( source, dest, progress, param, NULL, + flag & MOVEFILE_REPLACE_EXISTING ? 0 : COPY_FILE_FAIL_IF_EXISTS )) + return FALSE; + return DeleteFileW( source ); + } + + NtClose( source_handle ); + return set_ntstatus( status ); + +error: + if (source_handle) NtClose( source_handle ); + return FALSE; +} + + +/*********************************************************************** + * NeedCurrentDirectoryForExePathA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH NeedCurrentDirectoryForExePathA( LPCSTR name ) +{ + WCHAR *nameW; + + if (!(nameW = file_name_AtoW( name, FALSE ))) return TRUE; + return NeedCurrentDirectoryForExePathW( nameW ); +} + + +/*********************************************************************** + * NeedCurrentDirectoryForExePathW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH NeedCurrentDirectoryForExePathW( LPCWSTR name ) +{ + WCHAR env_val; + + if (wcschr( name, '\\' )) return TRUE; + /* check the existence of the variable, not value */ + return !GetEnvironmentVariableW( L"NoDefaultCurrentDirectoryInExePath", &env_val, 1 ); +} + + +/*********************************************************************** + * ReplaceFileW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReplaceFileW( const WCHAR *replaced, const WCHAR *replacement, + const WCHAR *backup, DWORD flags, + void *exclude, void *reserved ) +{ + UNICODE_STRING nt_replaced_name, nt_replacement_name; + HANDLE hReplacement = NULL; + NTSTATUS status; + IO_STATUS_BLOCK io; + OBJECT_ATTRIBUTES attr; + FILE_BASIC_INFORMATION info; + + TRACE( "%s %s %s 0x%08lx %p %p\n", debugstr_w(replaced), debugstr_w(replacement), debugstr_w(backup), + flags, exclude, reserved ); + + if (flags) FIXME("Ignoring flags %lx\n", flags); + + /* First two arguments are mandatory */ + if (!replaced || !replacement) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = NULL; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + /* Open the "replaced" file for reading */ + if (!RtlDosPathNameToNtPathName_U( replaced, &nt_replaced_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.ObjectName = &nt_replaced_name; + + /* Replacement should fail if replaced is READ_ONLY */ + status = NtQueryAttributesFile(&attr, &info); + RtlFreeUnicodeString(&nt_replaced_name); + if (!set_ntstatus( status )) return FALSE; + + if (info.FileAttributes & FILE_ATTRIBUTE_READONLY) + { + SetLastError( ERROR_ACCESS_DENIED ); + return FALSE; + } + + /* + * Open the replacement file for reading, writing, and deleting + * (writing and deleting are needed when finished) + */ + if (!RtlDosPathNameToNtPathName_U( replacement, &nt_replacement_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.ObjectName = &nt_replacement_name; + status = NtOpenFile( &hReplacement, GENERIC_READ | GENERIC_WRITE | DELETE | WRITE_DAC | SYNCHRONIZE, + &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ); + RtlFreeUnicodeString(&nt_replacement_name); + if (!set_ntstatus( status )) return FALSE; + NtClose( hReplacement ); + + /* If the user wants a backup then that needs to be performed first */ + if (backup) + { + if (!MoveFileExW( replaced, backup, MOVEFILE_REPLACE_EXISTING )) return FALSE; + } + else + { + /* ReplaceFile() can replace an open target. To do this, we need to move + * it out of the way first. */ + WCHAR temp_path[MAX_PATH], temp_file[MAX_PATH]; + + lstrcpynW( temp_path, replaced, ARRAY_SIZE( temp_path ) ); + PathRemoveFileSpecW( temp_path ); + if (!GetTempFileNameW( temp_path, L"rf", 0, temp_file ) || + !MoveFileExW( replaced, temp_file, MOVEFILE_REPLACE_EXISTING )) + return FALSE; + + DeleteFileW( temp_file ); + } + + /* + * Now that the backup has been performed (if requested), copy the replacement + * into place + */ + if (!MoveFileExW( replacement, replaced, 0 )) + { + /* on failure we need to indicate whether a backup was made */ + if (!backup) + SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT ); + else + SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 ); + return FALSE; + } + return TRUE; +} + + +/*********************************************************************** + * SearchPathA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext, + DWORD buflen, LPSTR buffer, LPSTR *lastpart ) +{ + WCHAR *pathW = NULL, *nameW, *extW = NULL; + WCHAR bufferW[MAX_PATH]; + DWORD ret; + + if (!name) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (!(nameW = file_name_AtoW( name, FALSE ))) return 0; + if (path && !(pathW = file_name_AtoW( path, TRUE ))) return 0; + if (ext && !(extW = file_name_AtoW( ext, TRUE ))) + { + RtlFreeHeap( GetProcessHeap(), 0, pathW ); + return 0; + } + + ret = SearchPathW( pathW, nameW, extW, MAX_PATH, bufferW, NULL ); + + RtlFreeHeap( GetProcessHeap(), 0, pathW ); + RtlFreeHeap( GetProcessHeap(), 0, extW ); + + if (!ret) return 0; + if (ret > MAX_PATH) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + ret = copy_filename_WtoA( bufferW, buffer, buflen ); + if (buflen > ret && lastpart) *lastpart = strrchr(buffer, '\\') + 1; + return ret; +} + + +/*********************************************************************** + * SearchPathW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext, DWORD buflen, + LPWSTR buffer, LPWSTR *lastpart ) +{ + DWORD ret = 0; + WCHAR *name_ext; + + if (!name || !name[0]) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + /* If the name contains an explicit path, ignore the path */ + + if (contains_path( name )) + { + /* try first without extension */ + if (RtlDoesFileExists_U( name )) return GetFullPathNameW( name, buflen, buffer, lastpart ); + + if ((name_ext = append_ext( name, ext ))) + { + if (RtlDoesFileExists_U( name_ext )) + ret = GetFullPathNameW( name_ext, buflen, buffer, lastpart ); + RtlFreeHeap( GetProcessHeap(), 0, name_ext ); + } + } + else if (path && path[0]) /* search in the specified path */ + { + ret = RtlDosSearchPath_U( path, name, ext, buflen * sizeof(WCHAR), + buffer, lastpart ) / sizeof(WCHAR); + } + else /* search in active context and default path */ + { + WCHAR *dll_path = NULL, *name_ext = append_ext( name, ext ); + + if (name_ext) name = name_ext; + + /* When file is found with activation context no attempt is made + to check if it's really exist, path is returned only basing on context info. */ + if (find_actctx_dllpath( name, &dll_path ) == STATUS_SUCCESS) + { + ret = lstrlenW( dll_path ) + lstrlenW( name ) + 1; + + /* count null termination char too */ + if (ret <= buflen) + { + lstrcpyW( buffer, dll_path ); + lstrcatW( buffer, name ); + if (lastpart) *lastpart = buffer + lstrlenW( dll_path ); + ret--; + } + else if (lastpart) *lastpart = NULL; + RtlFreeHeap( GetProcessHeap(), 0, dll_path ); + } + else if (!RtlGetSearchPath( &dll_path )) + { + ret = RtlDosSearchPath_U( dll_path, name, NULL, buflen * sizeof(WCHAR), + buffer, lastpart ) / sizeof(WCHAR); + RtlReleasePath( dll_path ); + } + RtlFreeHeap( GetProcessHeap(), 0, name_ext ); + } + + if (!ret) SetLastError( ERROR_FILE_NOT_FOUND ); + else TRACE( "found %s\n", debugstr_w(buffer) ); + return ret; +} + + +/*********************************************************************** + * SetCurrentDirectoryA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetCurrentDirectoryA( LPCSTR dir ) +{ + WCHAR *dirW; + UNICODE_STRING strW; + + if (!(dirW = file_name_AtoW( dir, FALSE ))) return FALSE; + RtlInitUnicodeString( &strW, dirW ); + return set_ntstatus( RtlSetCurrentDirectory_U( &strW )); +} + + +/*********************************************************************** + * SetCurrentDirectoryW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetCurrentDirectoryW( LPCWSTR dir ) +{ + UNICODE_STRING dirW; + + RtlInitUnicodeString( &dirW, dir ); + return set_ntstatus( RtlSetCurrentDirectory_U( &dirW )); +} + + +/************************************************************************** + * SetFileApisToANSI (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH SetFileApisToANSI(void) +{ + oem_file_apis = FALSE; +} + + +/************************************************************************** + * SetFileApisToOEM (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH SetFileApisToOEM(void) +{ + oem_file_apis = TRUE; +} + + +/************************************************************************** + * SetFileAttributesA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetFileAttributesA( LPCSTR name, DWORD attributes ) +{ + WCHAR *nameW; + + if (!(nameW = file_name_AtoW( name, FALSE ))) return FALSE; + return SetFileAttributesW( nameW, attributes ); +} + + +/************************************************************************** + * SetFileAttributesW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetFileAttributesW( LPCWSTR name, DWORD attributes ) +{ + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + + TRACE( "%s %lx\n", debugstr_w(name), attributes ); + + if (!RtlDosPathNameToNtPathName_U( name, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( &handle, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + + if (status == STATUS_SUCCESS) + { + FILE_BASIC_INFORMATION info; + + memset( &info, 0, sizeof(info) ); + info.FileAttributes = attributes | FILE_ATTRIBUTE_NORMAL; /* make sure it's not zero */ + status = NtSetInformationFile( handle, &io, &info, sizeof(info), FileBasicInformation ); + NtClose( handle ); + } + return set_ntstatus( status ); +} + + +/*********************************************************************** + * Wow64DisableWow64FsRedirection (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Wow64DisableWow64FsRedirection( PVOID *old_value ) +{ + return set_ntstatus( RtlWow64EnableFsRedirectionEx( TRUE, (ULONG *)old_value )); +} + + +/*********************************************************************** + * Wow64EnableWow64FsRedirection (kernelbase.@) + * + * Microsoft C++ Redistributable installers are depending on all %eax bits being set. + */ +DWORD /*BOOLEAN*/ WINAPI kernelbase_Wow64EnableWow64FsRedirection( BOOLEAN enable ) +{ + return set_ntstatus( RtlWow64EnableFsRedirection( enable )); +} + + +/*********************************************************************** + * Wow64RevertWow64FsRedirection (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Wow64RevertWow64FsRedirection( PVOID old_value ) +{ + return set_ntstatus( RtlWow64EnableFsRedirection( !old_value )); +} + + +/*********************************************************************** + * Operations on file handles + ***********************************************************************/ + + +/*********************************************************************** + * CancelIo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CancelIo( HANDLE handle ) +{ + IO_STATUS_BLOCK io; + + return set_ntstatus( NtCancelIoFile( handle, &io ) ); +} + + +/*********************************************************************** + * CancelIoEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CancelIoEx( HANDLE handle, LPOVERLAPPED overlapped ) +{ + IO_STATUS_BLOCK io; + + return set_ntstatus( NtCancelIoFileEx( handle, (PIO_STATUS_BLOCK)overlapped, &io ) ); +} + + +/*********************************************************************** + * CancelSynchronousIo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CancelSynchronousIo( HANDLE thread ) +{ + IO_STATUS_BLOCK io; + + return set_ntstatus( NtCancelSynchronousIoFile( thread, NULL, &io )); +} + + +/*********************************************************************** + * FlushFileBuffers (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FlushFileBuffers( HANDLE file ) +{ + IO_STATUS_BLOCK iosb; + + return set_ntstatus( NtFlushBuffersFile( file, &iosb )); +} + + +/*********************************************************************** + * GetFileInformationByHandle (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetFileInformationByHandle( HANDLE file, BY_HANDLE_FILE_INFORMATION *info ) +{ + FILE_FS_VOLUME_INFORMATION volume_info; + FILE_STAT_INFORMATION stat_info; + IO_STATUS_BLOCK io; + NTSTATUS status; + + status = NtQueryInformationFile( file, &io, &stat_info, sizeof(stat_info), FileStatInformation ); + if (!set_ntstatus( status )) return FALSE; + + info->dwFileAttributes = stat_info.FileAttributes; + info->ftCreationTime.dwHighDateTime = stat_info.CreationTime.u.HighPart; + info->ftCreationTime.dwLowDateTime = stat_info.CreationTime.u.LowPart; + info->ftLastAccessTime.dwHighDateTime = stat_info.LastAccessTime.u.HighPart; + info->ftLastAccessTime.dwLowDateTime = stat_info.LastAccessTime.u.LowPart; + info->ftLastWriteTime.dwHighDateTime = stat_info.LastWriteTime.u.HighPart; + info->ftLastWriteTime.dwLowDateTime = stat_info.LastWriteTime.u.LowPart; + info->dwVolumeSerialNumber = 0; + info->nFileSizeHigh = stat_info.EndOfFile.u.HighPart; + info->nFileSizeLow = stat_info.EndOfFile.u.LowPart; + info->nNumberOfLinks = stat_info.NumberOfLinks; + info->nFileIndexHigh = stat_info.FileId.u.HighPart; + info->nFileIndexLow = stat_info.FileId.u.LowPart; + + status = NtQueryVolumeInformationFile( file, &io, &volume_info, sizeof(volume_info), FileFsVolumeInformation ); + if (status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW) + info->dwVolumeSerialNumber = volume_info.VolumeSerialNumber; + + return TRUE; +} + + +/*********************************************************************** + * GetFileInformationByHandleEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetFileInformationByHandleEx( HANDLE handle, FILE_INFO_BY_HANDLE_CLASS class, + LPVOID info, DWORD size ) +{ + NTSTATUS status; + IO_STATUS_BLOCK io; + + switch (class) + { + case FileRemoteProtocolInfo: + case FileStorageInfo: + case FileDispositionInfoEx: + case FileRenameInfoEx: + case FileCaseSensitiveInfo: + case FileNormalizedNameInfo: + FIXME( "%p, %u, %p, %lu\n", handle, class, info, size ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; + + case FileStreamInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileStreamInformation ); + break; + + case FileCompressionInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileCompressionInformation ); + break; + + case FileAlignmentInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileAlignmentInformation ); + break; + + case FileAttributeTagInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileAttributeTagInformation ); + break; + + case FileBasicInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileBasicInformation ); + break; + + case FileStandardInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileStandardInformation ); + break; + + case FileNameInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileNameInformation ); + break; + + case FileIdInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileIdInformation ); + break; + + case FileIdBothDirectoryRestartInfo: + case FileIdBothDirectoryInfo: + status = NtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, info, size, + FileIdBothDirectoryInformation, FALSE, NULL, + (class == FileIdBothDirectoryRestartInfo) ); + break; + + case FileFullDirectoryInfo: + case FileFullDirectoryRestartInfo: + status = NtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, info, size, + FileFullDirectoryInformation, FALSE, NULL, + (class == FileFullDirectoryRestartInfo) ); + break; + + case FileIdExtdDirectoryInfo: + case FileIdExtdDirectoryRestartInfo: + status = NtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, info, size, + FileIdExtdDirectoryInformation, FALSE, NULL, + (class == FileIdExtdDirectoryRestartInfo) ); + break; + + case FileRenameInfo: + case FileDispositionInfo: + case FileAllocationInfo: + case FileIoPriorityHintInfo: + case FileEndOfFileInfo: + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return set_ntstatus( status ); +} + + +/*********************************************************************** + * GetFileSize (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFileSize( HANDLE file, LPDWORD size_high ) +{ + LARGE_INTEGER size; + + if (!GetFileSizeEx( file, &size )) return INVALID_FILE_SIZE; + if (size_high) *size_high = size.u.HighPart; + if (size.u.LowPart == INVALID_FILE_SIZE) SetLastError( 0 ); + return size.u.LowPart; +} + + +/*********************************************************************** + * GetFileSizeEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetFileSizeEx( HANDLE file, PLARGE_INTEGER size ) +{ + FILE_STANDARD_INFORMATION info; + IO_STATUS_BLOCK io; + + if (!set_ntstatus( NtQueryInformationFile( file, &io, &info, sizeof(info), FileStandardInformation ))) + return FALSE; + + *size = info.EndOfFile; + return TRUE; +} + + +/*********************************************************************** + * GetFileTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetFileTime( HANDLE file, FILETIME *creation, + FILETIME *access, FILETIME *write ) +{ + FILE_BASIC_INFORMATION info; + IO_STATUS_BLOCK io; + + if (!set_ntstatus( NtQueryInformationFile( file, &io, &info, sizeof(info), FileBasicInformation ))) + return FALSE; + + if (creation) + { + creation->dwHighDateTime = info.CreationTime.u.HighPart; + creation->dwLowDateTime = info.CreationTime.u.LowPart; + } + if (access) + { + access->dwHighDateTime = info.LastAccessTime.u.HighPart; + access->dwLowDateTime = info.LastAccessTime.u.LowPart; + } + if (write) + { + write->dwHighDateTime = info.LastWriteTime.u.HighPart; + write->dwLowDateTime = info.LastWriteTime.u.LowPart; + } + return TRUE; +} + + +/*********************************************************************** + * GetFileType (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFileType( HANDLE file ) +{ + FILE_FS_DEVICE_INFORMATION info; + IO_STATUS_BLOCK io; + + if (file == (HANDLE)STD_INPUT_HANDLE || + file == (HANDLE)STD_OUTPUT_HANDLE || + file == (HANDLE)STD_ERROR_HANDLE) + file = GetStdHandle( (DWORD_PTR)file ); + + if (!set_ntstatus( NtQueryVolumeInformationFile( file, &io, &info, sizeof(info), + FileFsDeviceInformation ))) + return FILE_TYPE_UNKNOWN; + + switch (info.DeviceType) + { + case FILE_DEVICE_NULL: + case FILE_DEVICE_CONSOLE: + case FILE_DEVICE_SERIAL_PORT: + case FILE_DEVICE_PARALLEL_PORT: + case FILE_DEVICE_TAPE: + case FILE_DEVICE_UNKNOWN: + return FILE_TYPE_CHAR; + case FILE_DEVICE_NAMED_PIPE: + return FILE_TYPE_PIPE; + default: + return FILE_TYPE_DISK; + } +} + + +/*********************************************************************** + * GetOverlappedResult (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResult( HANDLE file, LPOVERLAPPED overlapped, + LPDWORD result, BOOL wait ) +{ + return GetOverlappedResultEx( file, overlapped, result, wait ? INFINITE : 0, FALSE ); +} + + +/*********************************************************************** + * GetOverlappedResultEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResultEx( HANDLE file, OVERLAPPED *overlapped, + DWORD *result, DWORD timeout, BOOL alertable ) +{ + NTSTATUS status; + DWORD ret; + + TRACE( "(%p %p %p %lu %d)\n", file, overlapped, result, timeout, alertable ); + + /* Paired with the write-release in set_async_iosb() in ntdll; see the + * latter for details. */ + status = ReadAcquire( (LONG *)&overlapped->Internal ); + if (status == STATUS_PENDING) + { + if (!timeout) + { + SetLastError( ERROR_IO_INCOMPLETE ); + return FALSE; + } + ret = WaitForSingleObjectEx( overlapped->hEvent ? overlapped->hEvent : file, timeout, alertable ); + if (ret == WAIT_FAILED) + return FALSE; + else if (ret) + { + SetLastError( ret ); + return FALSE; + } + + /* We don't need to give this load acquire semantics; the wait above + * already guarantees that the IOSB and output buffer are filled. */ + status = overlapped->Internal; + if (status == STATUS_PENDING) status = STATUS_SUCCESS; + } + + *result = overlapped->InternalHigh; + return set_ntstatus( status ); +} + + +/************************************************************************** + * LockFile (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH LockFile( HANDLE file, DWORD offset_low, DWORD offset_high, + DWORD count_low, DWORD count_high ) +{ + LARGE_INTEGER count, offset; + + TRACE( "%p %lx%08lx %lx%08lx\n", file, offset_high, offset_low, count_high, count_low ); + + count.u.LowPart = count_low; + count.u.HighPart = count_high; + offset.u.LowPart = offset_low; + offset.u.HighPart = offset_high; + return set_ntstatus( NtLockFile( file, 0, NULL, NULL, NULL, &offset, &count, NULL, TRUE, TRUE )); +} + + +/************************************************************************** + * LockFileEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH LockFileEx( HANDLE file, DWORD flags, DWORD reserved, + DWORD count_low, DWORD count_high, LPOVERLAPPED overlapped ) +{ + LARGE_INTEGER count, offset; + LPVOID cvalue = NULL; + + if (reserved) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + TRACE( "%p %lx%08lx %lx%08lx flags %lx\n", + file, overlapped->OffsetHigh, overlapped->Offset, count_high, count_low, flags ); + + count.u.LowPart = count_low; + count.u.HighPart = count_high; + offset.u.LowPart = overlapped->Offset; + offset.u.HighPart = overlapped->OffsetHigh; + + if (((ULONG_PTR)overlapped->hEvent & 1) == 0) cvalue = overlapped; + + return set_ntstatus( NtLockFile( file, overlapped->hEvent, NULL, cvalue, + NULL, &offset, &count, NULL, + flags & LOCKFILE_FAIL_IMMEDIATELY, + flags & LOCKFILE_EXCLUSIVE_LOCK )); +} + + +/*********************************************************************** + * OpenFileById (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenFileById( HANDLE handle, LPFILE_ID_DESCRIPTOR id, DWORD access, + DWORD share, LPSECURITY_ATTRIBUTES sec_attr, DWORD flags ) +{ + UINT options; + HANDLE result; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + UNICODE_STRING objectName; + + if (!id) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return INVALID_HANDLE_VALUE; + } + + options = FILE_OPEN_BY_FILE_ID; + if (flags & FILE_FLAG_BACKUP_SEMANTICS) + options |= FILE_OPEN_FOR_BACKUP_INTENT; + else + options |= FILE_NON_DIRECTORY_FILE; + if (flags & FILE_FLAG_NO_BUFFERING) options |= FILE_NO_INTERMEDIATE_BUFFERING; + if (!(flags & FILE_FLAG_OVERLAPPED)) options |= FILE_SYNCHRONOUS_IO_NONALERT; + if (flags & FILE_FLAG_RANDOM_ACCESS) options |= FILE_RANDOM_ACCESS; + if (flags & FILE_FLAG_SEQUENTIAL_SCAN) options |= FILE_SEQUENTIAL_ONLY; + flags &= FILE_ATTRIBUTE_VALID_FLAGS; + + objectName.Length = sizeof(ULONGLONG); + objectName.Buffer = (WCHAR *)&id->FileId; + attr.Length = sizeof(attr); + attr.RootDirectory = handle; + attr.Attributes = 0; + attr.ObjectName = &objectName; + attr.SecurityDescriptor = sec_attr ? sec_attr->lpSecurityDescriptor : NULL; + attr.SecurityQualityOfService = NULL; + if (sec_attr && sec_attr->bInheritHandle) attr.Attributes |= OBJ_INHERIT; + + if (!set_ntstatus( NtCreateFile( &result, access | SYNCHRONIZE, &attr, &io, NULL, flags, + share, OPEN_EXISTING, options, NULL, 0 ))) + return INVALID_HANDLE_VALUE; + return result; +} + + +/*********************************************************************** + * ReOpenFile (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH ReOpenFile( HANDLE handle, DWORD access, DWORD sharing, DWORD attributes ) +{ + SECURITY_QUALITY_OF_SERVICE qos; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING empty = { 0 }; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE file; + + TRACE("handle %p, access %#lx, sharing %#lx, attributes %#lx.\n", handle, access, sharing, attributes); + + if (attributes & 0x7ffff) /* FILE_ATTRIBUTE_* flags are invalid */ + { + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + + if (attributes & FILE_FLAG_DELETE_ON_CLOSE) + access |= DELETE; + + InitializeObjectAttributes( &attr, &empty, OBJ_CASE_INSENSITIVE, handle, NULL ); + if (attributes & SECURITY_SQOS_PRESENT) + { + qos.Length = sizeof(qos); + qos.ImpersonationLevel = (attributes >> 16) & 0x3; + qos.ContextTrackingMode = attributes & SECURITY_CONTEXT_TRACKING ? SECURITY_DYNAMIC_TRACKING : SECURITY_STATIC_TRACKING; + qos.EffectiveOnly = (attributes & SECURITY_EFFECTIVE_ONLY) != 0; + attr.SecurityQualityOfService = &qos; + } + + status = NtCreateFile( &file, access | SYNCHRONIZE | FILE_READ_ATTRIBUTES, &attr, &io, NULL, + 0, sharing, FILE_OPEN, get_nt_file_options( attributes ), NULL, 0 ); + if (!set_ntstatus( status )) + return INVALID_HANDLE_VALUE; + return file; +} + + +static void WINAPI invoke_completion( void *context, IO_STATUS_BLOCK *io, ULONG res ) +{ + LPOVERLAPPED_COMPLETION_ROUTINE completion = context; + completion( RtlNtStatusToDosError( io->Status ), io->Information, (LPOVERLAPPED)io ); +} + +/**************************************************************************** + * ReadDirectoryChangesW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadDirectoryChangesW( HANDLE handle, LPVOID buffer, DWORD len, + BOOL subtree, DWORD filter, LPDWORD returned, + LPOVERLAPPED overlapped, + LPOVERLAPPED_COMPLETION_ROUTINE completion ) +{ + OVERLAPPED ov, *pov; + IO_STATUS_BLOCK *ios; + NTSTATUS status; + LPVOID cvalue = NULL; + + TRACE( "%p %p %08lx %d %08lx %p %p %p\n", + handle, buffer, len, subtree, filter, returned, overlapped, completion ); + + if (!overlapped) + { + memset( &ov, 0, sizeof ov ); + ov.hEvent = CreateEventW( NULL, 0, 0, NULL ); + pov = &ov; + } + else + { + pov = overlapped; + if (completion) cvalue = completion; + else if (((ULONG_PTR)overlapped->hEvent & 1) == 0) cvalue = overlapped; + } + + ios = (PIO_STATUS_BLOCK)pov; + ios->Status = STATUS_PENDING; + + status = NtNotifyChangeDirectoryFile( handle, completion && overlapped ? NULL : pov->hEvent, + completion && overlapped ? invoke_completion : NULL, + cvalue, ios, buffer, len, filter, subtree ); + if (status == STATUS_PENDING) + { + if (overlapped) return TRUE; + WaitForSingleObjectEx( ov.hEvent, INFINITE, TRUE ); + if (returned) *returned = ios->Information; + status = ios->Status; + } + if (!overlapped) CloseHandle( ov.hEvent ); + return set_ntstatus( status ); +} + + +/*********************************************************************** + * ReadFile (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadFile( HANDLE file, LPVOID buffer, DWORD count, + LPDWORD result, LPOVERLAPPED overlapped ) +{ + LARGE_INTEGER offset; + PLARGE_INTEGER poffset = NULL; + IO_STATUS_BLOCK iosb; + PIO_STATUS_BLOCK io_status = &iosb; + HANDLE event = 0; + NTSTATUS status; + LPVOID cvalue = NULL; + + TRACE( "%p %p %ld %p %p\n", file, buffer, count, result, overlapped ); + + if (result) *result = 0; + + if (overlapped) + { + offset.u.LowPart = overlapped->Offset; + offset.u.HighPart = overlapped->OffsetHigh; + poffset = &offset; + event = overlapped->hEvent; + io_status = (PIO_STATUS_BLOCK)overlapped; + if (((ULONG_PTR)event & 1) == 0) cvalue = overlapped; + } + else io_status->Information = 0; + io_status->Status = STATUS_PENDING; + + status = NtReadFile( file, event, NULL, cvalue, io_status, buffer, count, poffset, NULL); + + if (status == STATUS_PENDING && !overlapped) + { + WaitForSingleObject( file, INFINITE ); + status = io_status->Status; + } + + if (result) *result = overlapped && status ? 0 : io_status->Information; + + if (status == STATUS_END_OF_FILE) + { + if (overlapped != NULL) + { + SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } + } + else if (status && status != STATUS_TIMEOUT) + { + SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } + return TRUE; +} + + +/*********************************************************************** + * ReadFileEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadFileEx( HANDLE file, LPVOID buffer, DWORD count, LPOVERLAPPED overlapped, + LPOVERLAPPED_COMPLETION_ROUTINE completion ) +{ + PIO_STATUS_BLOCK io; + LARGE_INTEGER offset; + NTSTATUS status; + + TRACE( "(file=%p, buffer=%p, bytes=%lu, ovl=%p, ovl_fn=%p)\n", + file, buffer, count, overlapped, completion ); + + if (!overlapped) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + offset.u.LowPart = overlapped->Offset; + offset.u.HighPart = overlapped->OffsetHigh; + io = (PIO_STATUS_BLOCK)overlapped; + io->Status = STATUS_PENDING; + io->Information = 0; + + status = NtReadFile( file, NULL, read_write_apc, completion, io, buffer, count, &offset, NULL); + if (status == STATUS_PENDING) return TRUE; + return set_ntstatus( status ); +} + + +/*********************************************************************** + * ReadFileScatter (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadFileScatter( HANDLE file, FILE_SEGMENT_ELEMENT *segments, DWORD count, + LPDWORD reserved, LPOVERLAPPED overlapped ) +{ + PIO_STATUS_BLOCK io; + LARGE_INTEGER offset; + void *cvalue = NULL; + + TRACE( "(%p %p %lu %p)\n", file, segments, count, overlapped ); + + offset.u.LowPart = overlapped->Offset; + offset.u.HighPart = overlapped->OffsetHigh; + if (!((ULONG_PTR)overlapped->hEvent & 1)) cvalue = overlapped; + io = (PIO_STATUS_BLOCK)overlapped; + io->Status = STATUS_PENDING; + io->Information = 0; + + return set_ntstatus( NtReadFileScatter( file, overlapped->hEvent, NULL, cvalue, io, + segments, count, &offset, NULL )); +} + + +/*********************************************************************** + * RemoveDirectoryA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH RemoveDirectoryA( LPCSTR path ) +{ + WCHAR *pathW; + + if (!(pathW = file_name_AtoW( path, FALSE ))) return FALSE; + return RemoveDirectoryW( pathW ); +} + + +/*********************************************************************** + * RemoveDirectoryW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH RemoveDirectoryW( LPCWSTR path ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nt_name; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + + TRACE( "%s\n", debugstr_w(path) ); + + status = RtlDosPathNameToNtPathName_U_WithStatus( path, &nt_name, NULL, NULL ); + if (!set_ntstatus( status )) return FALSE; + + InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL ); + status = NtOpenFile( &handle, DELETE | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + + if (!status) + { + FILE_DISPOSITION_INFORMATION info = { TRUE }; + status = NtSetInformationFile( handle, &io, &info, sizeof(info), FileDispositionInformation ); + NtClose( handle ); + } + return set_ntstatus( status ); +} + + +/************************************************************************** + * SetEndOfFile (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetEndOfFile( HANDLE file ) +{ + FILE_POSITION_INFORMATION pos; + FILE_END_OF_FILE_INFORMATION eof; + IO_STATUS_BLOCK io; + NTSTATUS status; + + if (!(status = NtQueryInformationFile( file, &io, &pos, sizeof(pos), FilePositionInformation ))) + { + eof.EndOfFile = pos.CurrentByteOffset; + status = NtSetInformationFile( file, &io, &eof, sizeof(eof), FileEndOfFileInformation ); + } + return set_ntstatus( status ); +} + + +/*********************************************************************** + * SetFileInformationByHandle (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetFileInformationByHandle( HANDLE file, FILE_INFO_BY_HANDLE_CLASS class, + void *info, DWORD size ) +{ + NTSTATUS status; + IO_STATUS_BLOCK io; + + TRACE( "%p %u %p %lu\n", file, class, info, size ); + + switch (class) + { + case FileNameInfo: + case FileAllocationInfo: + case FileStreamInfo: + case FileIdBothDirectoryInfo: + case FileIdBothDirectoryRestartInfo: + case FileFullDirectoryInfo: + case FileFullDirectoryRestartInfo: + case FileStorageInfo: + case FileAlignmentInfo: + case FileIdInfo: + case FileIdExtdDirectoryInfo: + case FileIdExtdDirectoryRestartInfo: + FIXME( "%p, %u, %p, %lu\n", file, class, info, size ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; + + case FileEndOfFileInfo: + status = NtSetInformationFile( file, &io, info, size, FileEndOfFileInformation ); + break; + case FileBasicInfo: + status = NtSetInformationFile( file, &io, info, size, FileBasicInformation ); + break; + case FileDispositionInfo: + status = NtSetInformationFile( file, &io, info, size, FileDispositionInformation ); + break; + case FileDispositionInfoEx: + status = NtSetInformationFile( file, &io, info, size, FileDispositionInformationEx ); + break; + case FileIoPriorityHintInfo: + status = NtSetInformationFile( file, &io, info, size, FileIoPriorityHintInformation ); + break; + case FileRenameInfo: + { + FILE_RENAME_INFORMATION *rename_info; + UNICODE_STRING nt_name; + ULONG size; + + if ((status = RtlDosPathNameToNtPathName_U_WithStatus( ((FILE_RENAME_INFORMATION *)info)->FileName, + &nt_name, NULL, NULL ))) + break; + + size = sizeof(*rename_info) + nt_name.Length; + if ((rename_info = HeapAlloc( GetProcessHeap(), 0, size ))) + { + memcpy( rename_info, info, sizeof(*rename_info) ); + memcpy( rename_info->FileName, nt_name.Buffer, nt_name.Length + sizeof(WCHAR) ); + rename_info->FileNameLength = nt_name.Length; + status = NtSetInformationFile( file, &io, rename_info, size, FileRenameInformation ); + HeapFree( GetProcessHeap(), 0, rename_info ); + } + RtlFreeUnicodeString( &nt_name ); + break; + } + case FileStandardInfo: + case FileCompressionInfo: + case FileAttributeTagInfo: + case FileRemoteProtocolInfo: + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return set_ntstatus( status ); +} + + +/*********************************************************************** + * SetFilePointer (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH SetFilePointer( HANDLE file, LONG distance, LONG *highword, DWORD method ) +{ + LARGE_INTEGER dist, newpos; + + if (highword) + { + dist.u.LowPart = distance; + dist.u.HighPart = *highword; + } + else dist.QuadPart = distance; + + if (!SetFilePointerEx( file, dist, &newpos, method )) return INVALID_SET_FILE_POINTER; + + if (highword) *highword = newpos.u.HighPart; + if (newpos.u.LowPart == INVALID_SET_FILE_POINTER) SetLastError( 0 ); + return newpos.u.LowPart; +} + + +/*********************************************************************** + * SetFilePointerEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetFilePointerEx( HANDLE file, LARGE_INTEGER distance, + LARGE_INTEGER *newpos, DWORD method ) +{ + LONGLONG pos; + IO_STATUS_BLOCK io; + FILE_POSITION_INFORMATION info; + FILE_STANDARD_INFORMATION eof; + + switch(method) + { + case FILE_BEGIN: + pos = distance.QuadPart; + break; + case FILE_CURRENT: + if (NtQueryInformationFile( file, &io, &info, sizeof(info), FilePositionInformation )) + goto error; + pos = info.CurrentByteOffset.QuadPart + distance.QuadPart; + break; + case FILE_END: + if (NtQueryInformationFile( file, &io, &eof, sizeof(eof), FileStandardInformation )) + goto error; + pos = eof.EndOfFile.QuadPart + distance.QuadPart; + break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (pos < 0) + { + SetLastError( ERROR_NEGATIVE_SEEK ); + return FALSE; + } + + info.CurrentByteOffset.QuadPart = pos; + if (!NtSetInformationFile( file, &io, &info, sizeof(info), FilePositionInformation )) + { + if (newpos) newpos->QuadPart = pos; + return TRUE; + } + +error: + return set_ntstatus( io.Status ); +} + + +/*********************************************************************** + * SetFileTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetFileTime( HANDLE file, const FILETIME *ctime, + const FILETIME *atime, const FILETIME *mtime ) +{ + FILE_BASIC_INFORMATION info; + IO_STATUS_BLOCK io; + + memset( &info, 0, sizeof(info) ); + if (ctime) + { + info.CreationTime.u.HighPart = ctime->dwHighDateTime; + info.CreationTime.u.LowPart = ctime->dwLowDateTime; + } + if (atime) + { + info.LastAccessTime.u.HighPart = atime->dwHighDateTime; + info.LastAccessTime.u.LowPart = atime->dwLowDateTime; + } + if (mtime) + { + info.LastWriteTime.u.HighPart = mtime->dwHighDateTime; + info.LastWriteTime.u.LowPart = mtime->dwLowDateTime; + } + + return set_ntstatus( NtSetInformationFile( file, &io, &info, sizeof(info), FileBasicInformation )); +} + + +/*********************************************************************** + * SetFileValidData (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetFileValidData( HANDLE file, LONGLONG length ) +{ + FILE_VALID_DATA_LENGTH_INFORMATION info; + IO_STATUS_BLOCK io; + + info.ValidDataLength.QuadPart = length; + return set_ntstatus( NtSetInformationFile( file, &io, &info, sizeof(info), + FileValidDataLengthInformation )); +} + + +/************************************************************************** + * UnlockFile (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH UnlockFile( HANDLE file, DWORD offset_low, DWORD offset_high, + DWORD count_low, DWORD count_high ) +{ + LARGE_INTEGER count, offset; + + count.u.LowPart = count_low; + count.u.HighPart = count_high; + offset.u.LowPart = offset_low; + offset.u.HighPart = offset_high; + return set_ntstatus( NtUnlockFile( file, NULL, &offset, &count, NULL )); +} + + +/************************************************************************** + * UnlockFileEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH UnlockFileEx( HANDLE file, DWORD reserved, + DWORD count_low, DWORD count_high, LPOVERLAPPED overlapped ) +{ + if (reserved) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (overlapped->hEvent) FIXME("Unimplemented overlapped operation\n"); + + return UnlockFile( file, overlapped->Offset, overlapped->OffsetHigh, count_low, count_high ); +} + + +/*********************************************************************** + * WriteFile (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteFile( HANDLE file, LPCVOID buffer, DWORD count, + LPDWORD result, LPOVERLAPPED overlapped ) +{ + HANDLE event = NULL; + LARGE_INTEGER offset; + PLARGE_INTEGER poffset = NULL; + NTSTATUS status; + IO_STATUS_BLOCK iosb; + PIO_STATUS_BLOCK piosb = &iosb; + LPVOID cvalue = NULL; + + TRACE( "%p %p %ld %p %p\n", file, buffer, count, result, overlapped ); + + if (overlapped) + { + offset.u.LowPart = overlapped->Offset; + offset.u.HighPart = overlapped->OffsetHigh; + poffset = &offset; + event = overlapped->hEvent; + piosb = (PIO_STATUS_BLOCK)overlapped; + if (((ULONG_PTR)event & 1) == 0) cvalue = overlapped; + } + else piosb->Information = 0; + piosb->Status = STATUS_PENDING; + + status = NtWriteFile( file, event, NULL, cvalue, piosb, buffer, count, poffset, NULL ); + + if (status == STATUS_PENDING && !overlapped) + { + WaitForSingleObject( file, INFINITE ); + status = piosb->Status; + } + + if (result) *result = overlapped && status ? 0 : piosb->Information; + + if (status && status != STATUS_TIMEOUT) + { + SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } + return TRUE; +} + + +/*********************************************************************** + * WriteFileEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteFileEx( HANDLE file, LPCVOID buffer, + DWORD count, LPOVERLAPPED overlapped, + LPOVERLAPPED_COMPLETION_ROUTINE completion ) +{ + LARGE_INTEGER offset; + NTSTATUS status; + PIO_STATUS_BLOCK io; + + TRACE( "%p %p %ld %p %p\n", file, buffer, count, overlapped, completion ); + + if (!overlapped) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + offset.u.LowPart = overlapped->Offset; + offset.u.HighPart = overlapped->OffsetHigh; + + io = (PIO_STATUS_BLOCK)overlapped; + io->Status = STATUS_PENDING; + io->Information = 0; + + status = NtWriteFile( file, NULL, read_write_apc, completion, io, buffer, count, &offset, NULL ); + if (status == STATUS_PENDING) return TRUE; + return set_ntstatus( status ); +} + + +/*********************************************************************** + * WriteFileGather (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteFileGather( HANDLE file, FILE_SEGMENT_ELEMENT *segments, DWORD count, + LPDWORD reserved, LPOVERLAPPED overlapped ) +{ + PIO_STATUS_BLOCK io; + LARGE_INTEGER offset; + void *cvalue = NULL; + + TRACE( "%p %p %lu %p\n", file, segments, count, overlapped ); + + offset.u.LowPart = overlapped->Offset; + offset.u.HighPart = overlapped->OffsetHigh; + if (!((ULONG_PTR)overlapped->hEvent & 1)) cvalue = overlapped; + io = (PIO_STATUS_BLOCK)overlapped; + io->Status = STATUS_PENDING; + io->Information = 0; + + return set_ntstatus( NtWriteFileGather( file, overlapped->hEvent, NULL, cvalue, + io, segments, count, &offset, NULL )); +} + + +/*********************************************************************** + * Operations on file times + ***********************************************************************/ + + +/********************************************************************* + * CompareFileTime (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH CompareFileTime( const FILETIME *x, const FILETIME *y ) +{ + if (!x || !y) return -1; + if (x->dwHighDateTime > y->dwHighDateTime) return 1; + if (x->dwHighDateTime < y->dwHighDateTime) return -1; + if (x->dwLowDateTime > y->dwLowDateTime) return 1; + if (x->dwLowDateTime < y->dwLowDateTime) return -1; + return 0; +} + + +/********************************************************************* + * FileTimeToLocalFileTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FileTimeToLocalFileTime( const FILETIME *utc, FILETIME *local ) +{ + return set_ntstatus( RtlSystemTimeToLocalTime( (const LARGE_INTEGER *)utc, (LARGE_INTEGER *)local )); +} + + +/********************************************************************* + * FileTimeToSystemTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FileTimeToSystemTime( const FILETIME *ft, SYSTEMTIME *systime ) +{ + TIME_FIELDS tf; + const LARGE_INTEGER *li = (const LARGE_INTEGER *)ft; + + if (li->QuadPart < 0) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + RtlTimeToTimeFields( li, &tf ); + systime->wYear = tf.Year; + systime->wMonth = tf.Month; + systime->wDay = tf.Day; + systime->wHour = tf.Hour; + systime->wMinute = tf.Minute; + systime->wSecond = tf.Second; + systime->wMilliseconds = tf.Milliseconds; + systime->wDayOfWeek = tf.Weekday; + return TRUE; +} + + +/********************************************************************* + * GetLocalTime (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH GetLocalTime( SYSTEMTIME *systime ) +{ + LARGE_INTEGER ft, ft2; + + NtQuerySystemTime( &ft ); + RtlSystemTimeToLocalTime( &ft, &ft2 ); + FileTimeToSystemTime( (FILETIME *)&ft2, systime ); +} + + +/********************************************************************* + * GetSystemTime (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH GetSystemTime( SYSTEMTIME *systime ) +{ + LARGE_INTEGER ft; + + NtQuerySystemTime( &ft ); + FileTimeToSystemTime( (FILETIME *)&ft, systime ); +} + + +/*********************************************************************** + * GetSystemTimeAdjustment (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetSystemTimeAdjustment( DWORD *adjust, DWORD *increment, BOOL *disabled ) +{ + SYSTEM_TIME_ADJUSTMENT_QUERY st; + ULONG len; + + if (!set_ntstatus( NtQuerySystemInformation( SystemTimeAdjustmentInformation, &st, sizeof(st), &len ))) + return FALSE; + *adjust = st.TimeAdjustment; + *increment = st.TimeIncrement; + *disabled = st.TimeAdjustmentDisabled; + return TRUE; +} + + +/*********************************************************************** + * GetSystemTimeAsFileTime (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH GetSystemTimeAsFileTime( FILETIME *time ) +{ + NtQuerySystemTime( (LARGE_INTEGER *)time ); +} + + +/*********************************************************************** + * GetSystemTimePreciseAsFileTime (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH GetSystemTimePreciseAsFileTime( FILETIME *time ) +{ + LARGE_INTEGER t; + + t.QuadPart = RtlGetSystemTimePrecise(); + time->dwLowDateTime = t.u.LowPart; + time->dwHighDateTime = t.u.HighPart; +} + + +/********************************************************************* + * LocalFileTimeToFileTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH LocalFileTimeToFileTime( const FILETIME *local, FILETIME *utc ) +{ + return set_ntstatus( RtlLocalTimeToSystemTime( (const LARGE_INTEGER *)local, (LARGE_INTEGER *)utc )); +} + + +/*********************************************************************** + * SetLocalTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetLocalTime( const SYSTEMTIME *systime ) +{ + FILETIME ft; + LARGE_INTEGER st; + + if (!SystemTimeToFileTime( systime, &ft )) return FALSE; + RtlLocalTimeToSystemTime( (LARGE_INTEGER *)&ft, &st ); + return set_ntstatus( NtSetSystemTime( &st, NULL )); +} + + +/*********************************************************************** + * SetSystemTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetSystemTime( const SYSTEMTIME *systime ) +{ + FILETIME ft; + + if (!SystemTimeToFileTime( systime, &ft )) return FALSE; + return set_ntstatus( NtSetSystemTime( (LARGE_INTEGER *)&ft, NULL )); +} + + +/*********************************************************************** + * SetSystemTimeAdjustment (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetSystemTimeAdjustment( DWORD adjust, BOOL disabled ) +{ + SYSTEM_TIME_ADJUSTMENT st; + + st.TimeAdjustment = adjust; + st.TimeAdjustmentDisabled = disabled; + return set_ntstatus( NtSetSystemInformation( SystemTimeAdjustmentInformation, &st, sizeof(st) )); +} + + +/********************************************************************* + * SystemTimeToFileTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToFileTime( const SYSTEMTIME *systime, FILETIME *ft ) +{ + TIME_FIELDS tf; + + tf.Year = systime->wYear; + tf.Month = systime->wMonth; + tf.Day = systime->wDay; + tf.Hour = systime->wHour; + tf.Minute = systime->wMinute; + tf.Second = systime->wSecond; + tf.Milliseconds = systime->wMilliseconds; + if (RtlTimeFieldsToTime( &tf, (LARGE_INTEGER *)ft )) return TRUE; + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; +} + + +/*********************************************************************** + * I/O controls + ***********************************************************************/ + + +static void dump_dcb( const DCB *dcb ) +{ + TRACE( "size=%d rate=%ld fParity=%d Parity=%d stopbits=%d %sIXON %sIXOFF CTS=%d RTS=%d DSR=%d DTR=%d %sCRTSCTS\n", + dcb->ByteSize, dcb->BaudRate, dcb->fParity, dcb->Parity, + (dcb->StopBits == ONESTOPBIT) ? 1 : (dcb->StopBits == TWOSTOPBITS) ? 2 : 0, + dcb->fOutX ? "" : "~", dcb->fInX ? "" : "~", + dcb->fOutxCtsFlow, dcb->fRtsControl, dcb->fOutxDsrFlow, dcb->fDtrControl, + (dcb->fOutxCtsFlow || dcb->fRtsControl == RTS_CONTROL_HANDSHAKE) ? "" : "~" ); +} + +/***************************************************************************** + * ClearCommBreak (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ClearCommBreak( HANDLE handle ) +{ + return EscapeCommFunction( handle, CLRBREAK ); +} + + +/***************************************************************************** + * ClearCommError (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ClearCommError( HANDLE handle, DWORD *errors, COMSTAT *stat ) +{ + SERIAL_STATUS ss; + + if (!DeviceIoControl( handle, IOCTL_SERIAL_GET_COMMSTATUS, NULL, 0, &ss, sizeof(ss), NULL, NULL )) + return FALSE; + + TRACE( "status %#lx,%#lx, in %lu, out %lu, eof %d, wait %d\n", ss.Errors, ss.HoldReasons, + ss.AmountInInQueue, ss.AmountInOutQueue, ss.EofReceived, ss.WaitForImmediate ); + + if (errors) + { + *errors = 0; + if (ss.Errors & SERIAL_ERROR_BREAK) *errors |= CE_BREAK; + if (ss.Errors & SERIAL_ERROR_FRAMING) *errors |= CE_FRAME; + if (ss.Errors & SERIAL_ERROR_OVERRUN) *errors |= CE_OVERRUN; + if (ss.Errors & SERIAL_ERROR_QUEUEOVERRUN) *errors |= CE_RXOVER; + if (ss.Errors & SERIAL_ERROR_PARITY) *errors |= CE_RXPARITY; + } + if (stat) + { + stat->fCtsHold = !!(ss.HoldReasons & SERIAL_TX_WAITING_FOR_CTS); + stat->fDsrHold = !!(ss.HoldReasons & SERIAL_TX_WAITING_FOR_DSR); + stat->fRlsdHold = !!(ss.HoldReasons & SERIAL_TX_WAITING_FOR_DCD); + stat->fXoffHold = !!(ss.HoldReasons & SERIAL_TX_WAITING_FOR_XON); + stat->fXoffSent = !!(ss.HoldReasons & SERIAL_TX_WAITING_XOFF_SENT); + stat->fEof = !!ss.EofReceived; + stat->fTxim = !!ss.WaitForImmediate; + stat->cbInQue = ss.AmountInInQueue; + stat->cbOutQue = ss.AmountInOutQueue; + } + return TRUE; +} + + +/**************************************************************************** + * DeviceIoControl (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DeviceIoControl( HANDLE handle, DWORD code, void *in_buff, DWORD in_count, + void *out_buff, DWORD out_count, DWORD *returned, + OVERLAPPED *overlapped ) +{ + IO_STATUS_BLOCK iosb, *piosb = &iosb; + void *cvalue = NULL; + HANDLE event = 0; + NTSTATUS status; + + TRACE( "(%p,%lx,%p,%ld,%p,%ld,%p,%p)\n", + handle, code, in_buff, in_count, out_buff, out_count, returned, overlapped ); + + if (overlapped) + { + piosb = (IO_STATUS_BLOCK *)overlapped; + if (!((ULONG_PTR)overlapped->hEvent & 1)) cvalue = overlapped; + event = overlapped->hEvent; + overlapped->Internal = STATUS_PENDING; + overlapped->InternalHigh = 0; + } + + if (HIWORD(code) == FILE_DEVICE_FILE_SYSTEM) + status = NtFsControlFile( handle, event, NULL, cvalue, piosb, code, + in_buff, in_count, out_buff, out_count ); + else + status = NtDeviceIoControlFile( handle, event, NULL, cvalue, piosb, code, + in_buff, in_count, out_buff, out_count ); + + if (returned && !NT_ERROR(status)) *returned = piosb->Information; + if (status == STATUS_PENDING || !NT_SUCCESS( status )) return set_ntstatus( status ); + return TRUE; +} + + +/***************************************************************************** + * EscapeCommFunction (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EscapeCommFunction( HANDLE handle, DWORD func ) +{ + static const DWORD ioctls[] = + { + 0, + IOCTL_SERIAL_SET_XOFF, /* SETXOFF */ + IOCTL_SERIAL_SET_XON, /* SETXON */ + IOCTL_SERIAL_SET_RTS, /* SETRTS */ + IOCTL_SERIAL_CLR_RTS, /* CLRRTS */ + IOCTL_SERIAL_SET_DTR, /* SETDTR */ + IOCTL_SERIAL_CLR_DTR, /* CLRDTR */ + IOCTL_SERIAL_RESET_DEVICE, /* RESETDEV */ + IOCTL_SERIAL_SET_BREAK_ON, /* SETBREAK */ + IOCTL_SERIAL_SET_BREAK_OFF /* CLRBREAK */ + }; + + if (func >= ARRAY_SIZE(ioctls) || !ioctls[func]) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return DeviceIoControl( handle, ioctls[func], NULL, 0, NULL, 0, NULL, NULL ); +} + + +/*********************************************************************** + * GetCommConfig (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetCommConfig( HANDLE handle, COMMCONFIG *config, DWORD *size ) +{ + if (!config) return FALSE; + + TRACE( "(%p, %p, %p %lu)\n", handle, config, size, *size ); + + if (*size < sizeof(COMMCONFIG)) + { + *size = sizeof(COMMCONFIG); + return FALSE; + } + *size = sizeof(COMMCONFIG); + config->dwSize = sizeof(COMMCONFIG); + config->wVersion = 1; + config->wReserved = 0; + config->dwProviderSubType = PST_RS232; + config->dwProviderOffset = 0; + config->dwProviderSize = 0; + return GetCommState( handle, &config->dcb ); +} + + +/***************************************************************************** + * GetCommMask (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetCommMask( HANDLE handle, DWORD *mask ) +{ + return DeviceIoControl( handle, IOCTL_SERIAL_GET_WAIT_MASK, NULL, 0, mask, sizeof(*mask), + NULL, NULL ); +} + + +/*********************************************************************** + * GetCommModemStatus (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetCommModemStatus( HANDLE handle, DWORD *status ) +{ + return DeviceIoControl( handle, IOCTL_SERIAL_GET_MODEMSTATUS, NULL, 0, status, sizeof(*status), + NULL, NULL ); +} + + +/*********************************************************************** + * GetCommProperties (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetCommProperties( HANDLE handle, COMMPROP *prop ) +{ + return DeviceIoControl( handle, IOCTL_SERIAL_GET_PROPERTIES, NULL, 0, prop, sizeof(*prop), NULL, NULL ); +} + + +/***************************************************************************** + * GetCommState (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetCommState( HANDLE handle, DCB *dcb ) +{ + SERIAL_BAUD_RATE sbr; + SERIAL_LINE_CONTROL slc; + SERIAL_HANDFLOW shf; + SERIAL_CHARS sc; + + if (!dcb) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (!DeviceIoControl(handle, IOCTL_SERIAL_GET_BAUD_RATE, NULL, 0, &sbr, sizeof(sbr), NULL, NULL) || + !DeviceIoControl(handle, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &slc, sizeof(slc), NULL, NULL) || + !DeviceIoControl(handle, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &shf, sizeof(shf), NULL, NULL) || + !DeviceIoControl(handle, IOCTL_SERIAL_GET_CHARS, NULL, 0, &sc, sizeof(sc), NULL, NULL)) + return FALSE; + + dcb->DCBlength = sizeof(*dcb); + dcb->BaudRate = sbr.BaudRate; + /* yes, they seem no never be (re)set on NT */ + dcb->fBinary = 1; + dcb->fParity = 0; + dcb->fOutxCtsFlow = !!(shf.ControlHandShake & SERIAL_CTS_HANDSHAKE); + dcb->fOutxDsrFlow = !!(shf.ControlHandShake & SERIAL_DSR_HANDSHAKE); + dcb->fDsrSensitivity = !!(shf.ControlHandShake & SERIAL_DSR_SENSITIVITY); + dcb->fTXContinueOnXoff = !!(shf.FlowReplace & SERIAL_XOFF_CONTINUE); + dcb->fOutX = !!(shf.FlowReplace & SERIAL_AUTO_TRANSMIT); + dcb->fInX = !!(shf.FlowReplace & SERIAL_AUTO_RECEIVE); + dcb->fErrorChar = !!(shf.FlowReplace & SERIAL_ERROR_CHAR); + dcb->fNull = !!(shf.FlowReplace & SERIAL_NULL_STRIPPING); + dcb->fAbortOnError = !!(shf.ControlHandShake & SERIAL_ERROR_ABORT); + dcb->XonLim = shf.XonLimit; + dcb->XoffLim = shf.XoffLimit; + dcb->ByteSize = slc.WordLength; + dcb->Parity = slc.Parity; + dcb->StopBits = slc.StopBits; + dcb->XonChar = sc.XonChar; + dcb->XoffChar = sc.XoffChar; + dcb->ErrorChar = sc.ErrorChar; + dcb->EofChar = sc.EofChar; + dcb->EvtChar = sc.EventChar; + + switch (shf.ControlHandShake & (SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE)) + { + case SERIAL_DTR_CONTROL: dcb->fDtrControl = DTR_CONTROL_ENABLE; break; + case SERIAL_DTR_HANDSHAKE: dcb->fDtrControl = DTR_CONTROL_HANDSHAKE; break; + default: dcb->fDtrControl = DTR_CONTROL_DISABLE; break; + } + switch (shf.FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE)) + { + case SERIAL_RTS_CONTROL: dcb->fRtsControl = RTS_CONTROL_ENABLE; break; + case SERIAL_RTS_HANDSHAKE: dcb->fRtsControl = RTS_CONTROL_HANDSHAKE; break; + case SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE: + dcb->fRtsControl = RTS_CONTROL_TOGGLE; break; + default: dcb->fRtsControl = RTS_CONTROL_DISABLE; break; + } + dump_dcb( dcb ); + return TRUE; +} + + +/***************************************************************************** + * GetCommTimeouts (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetCommTimeouts( HANDLE handle, COMMTIMEOUTS *timeouts ) +{ + if (!timeouts) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return DeviceIoControl( handle, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, timeouts, sizeof(*timeouts), + NULL, NULL ); +} + +/******************************************************************** + * PurgeComm (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH PurgeComm(HANDLE handle, DWORD flags) +{ + return DeviceIoControl( handle, IOCTL_SERIAL_PURGE, &flags, sizeof(flags), + NULL, 0, NULL, NULL ); +} + + +/***************************************************************************** + * SetCommBreak (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetCommBreak( HANDLE handle ) +{ + return EscapeCommFunction( handle, SETBREAK ); +} + + +/*********************************************************************** + * SetCommConfig (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetCommConfig( HANDLE handle, COMMCONFIG *config, DWORD size ) +{ + TRACE( "(%p, %p, %lu)\n", handle, config, size ); + return SetCommState( handle, &config->dcb ); +} + + +/***************************************************************************** + * SetCommMask (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetCommMask( HANDLE handle, DWORD mask ) +{ + return DeviceIoControl( handle, IOCTL_SERIAL_SET_WAIT_MASK, &mask, sizeof(mask), + NULL, 0, NULL, NULL ); +} + + +/***************************************************************************** + * SetCommState (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetCommState( HANDLE handle, DCB *dcb ) +{ + SERIAL_BAUD_RATE sbr; + SERIAL_LINE_CONTROL slc; + SERIAL_HANDFLOW shf; + SERIAL_CHARS sc; + + if (!dcb) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + dump_dcb( dcb ); + + sbr.BaudRate = dcb->BaudRate; + slc.StopBits = dcb->StopBits; + slc.Parity = dcb->Parity; + slc.WordLength = dcb->ByteSize; + shf.ControlHandShake = 0; + shf.FlowReplace = 0; + if (dcb->fOutxCtsFlow) shf.ControlHandShake |= SERIAL_CTS_HANDSHAKE; + if (dcb->fOutxDsrFlow) shf.ControlHandShake |= SERIAL_DSR_HANDSHAKE; + switch (dcb->fDtrControl) + { + case DTR_CONTROL_DISABLE: break; + case DTR_CONTROL_ENABLE: shf.ControlHandShake |= SERIAL_DTR_CONTROL; break; + case DTR_CONTROL_HANDSHAKE: shf.ControlHandShake |= SERIAL_DTR_HANDSHAKE; break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + switch (dcb->fRtsControl) + { + case RTS_CONTROL_DISABLE: break; + case RTS_CONTROL_ENABLE: shf.FlowReplace |= SERIAL_RTS_CONTROL; break; + case RTS_CONTROL_HANDSHAKE: shf.FlowReplace |= SERIAL_RTS_HANDSHAKE; break; + case RTS_CONTROL_TOGGLE: shf.FlowReplace |= SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE; break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (dcb->fDsrSensitivity) shf.ControlHandShake |= SERIAL_DSR_SENSITIVITY; + if (dcb->fAbortOnError) shf.ControlHandShake |= SERIAL_ERROR_ABORT; + if (dcb->fErrorChar) shf.FlowReplace |= SERIAL_ERROR_CHAR; + if (dcb->fNull) shf.FlowReplace |= SERIAL_NULL_STRIPPING; + if (dcb->fTXContinueOnXoff) shf.FlowReplace |= SERIAL_XOFF_CONTINUE; + if (dcb->fOutX) shf.FlowReplace |= SERIAL_AUTO_TRANSMIT; + if (dcb->fInX) shf.FlowReplace |= SERIAL_AUTO_RECEIVE; + shf.XonLimit = dcb->XonLim; + shf.XoffLimit = dcb->XoffLim; + sc.EofChar = dcb->EofChar; + sc.ErrorChar = dcb->ErrorChar; + sc.BreakChar = 0; + sc.EventChar = dcb->EvtChar; + sc.XonChar = dcb->XonChar; + sc.XoffChar = dcb->XoffChar; + + /* note: change DTR/RTS lines after setting the comm attributes, + * so flow control does not interfere. + */ + return (DeviceIoControl( handle, IOCTL_SERIAL_SET_BAUD_RATE, &sbr, sizeof(sbr), NULL, 0, NULL, NULL ) && + DeviceIoControl( handle, IOCTL_SERIAL_SET_LINE_CONTROL, &slc, sizeof(slc), NULL, 0, NULL, NULL ) && + DeviceIoControl( handle, IOCTL_SERIAL_SET_HANDFLOW, &shf, sizeof(shf), NULL, 0, NULL, NULL ) && + DeviceIoControl( handle, IOCTL_SERIAL_SET_CHARS, &sc, sizeof(sc), NULL, 0, NULL, NULL )); +} + + +/***************************************************************************** + * SetCommTimeouts (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetCommTimeouts( HANDLE handle, COMMTIMEOUTS *timeouts ) +{ + if (!timeouts) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return DeviceIoControl( handle, IOCTL_SERIAL_SET_TIMEOUTS, timeouts, sizeof(*timeouts), + NULL, 0, NULL, NULL ); +} + + +/***************************************************************************** + * SetupComm (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetupComm( HANDLE handle, DWORD in_size, DWORD out_size ) +{ + SERIAL_QUEUE_SIZE sqs; + + sqs.InSize = in_size; + sqs.OutSize = out_size; + return DeviceIoControl( handle, IOCTL_SERIAL_SET_QUEUE_SIZE, &sqs, sizeof(sqs), NULL, 0, NULL, NULL ); +} + + +/***************************************************************************** + * TransmitCommChar (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH TransmitCommChar( HANDLE handle, CHAR ch ) +{ + return DeviceIoControl( handle, IOCTL_SERIAL_IMMEDIATE_CHAR, &ch, sizeof(ch), NULL, 0, NULL, NULL ); +} + + +/*********************************************************************** + * WaitCommEvent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WaitCommEvent( HANDLE handle, DWORD *events, OVERLAPPED *overlapped ) +{ + return DeviceIoControl( handle, IOCTL_SERIAL_WAIT_ON_MASK, NULL, 0, events, sizeof(*events), + NULL, overlapped ); +} + + +/*********************************************************************** + * QueryIoRingCapabilities (kernelbase.@) + */ +HRESULT WINAPI QueryIoRingCapabilities(IORING_CAPABILITIES *caps) +{ + FIXME( "caps %p stub.\n", caps ); + + return E_NOTIMPL; +} diff --git a/dll/win32/KernelBase/wine/kernelbase.h b/dll/win32/KernelBase/wine/kernelbase.h new file mode 100644 index 0000000000000..2209939a8a22c --- /dev/null +++ b/dll/win32/KernelBase/wine/kernelbase.h @@ -0,0 +1,58 @@ +/* + * Kernelbase internal definitions + * + * Copyright 2019 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_KERNELBASE_H +#define __WINE_KERNELBASE_H + +#include "windef.h" +#include "winbase.h" + +struct pseudo_console +{ + HANDLE signal; + HANDLE reference; + HANDLE process; +}; + +extern WCHAR *file_name_AtoW( LPCSTR name, BOOL alloc ); +extern DWORD file_name_WtoA( LPCWSTR src, INT srclen, LPSTR dest, INT destlen ); +extern void init_global_data(void); +extern void init_startup_info( RTL_USER_PROCESS_PARAMETERS *params ); +extern void init_locale( HMODULE module ); +extern void init_console(void); + +extern const WCHAR windows_dir[]; +extern const WCHAR system_dir[]; + +static const BOOL is_win64 = (sizeof(void *) > sizeof(int)); +extern BOOL is_wow64; + +static inline BOOL set_ntstatus( NTSTATUS status ) +{ + if (status) SetLastError( RtlNtStatusToDosError( status )); + return !status; +} + +/* make the kernel32 names available */ +#define HeapAlloc(heap, flags, size) RtlAllocateHeap(heap, flags, size) +#define HeapReAlloc(heap, flags, ptr, size) RtlReAllocateHeap(heap, flags, ptr, size) +#define HeapFree(heap, flags, ptr) RtlFreeHeap(heap, flags, ptr) + +#endif /* __WINE_KERNELBASE_H */ diff --git a/dll/win32/KernelBase/wine/kernelbase.rc b/dll/win32/KernelBase/wine/kernelbase.rc new file mode 100644 index 0000000000000..0469f80784b70 --- /dev/null +++ b/dll/win32/KernelBase/wine/kernelbase.rc @@ -0,0 +1,46 @@ +/* + * Copyright 2020 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma makedep po + +#include +#include + +/* @makedep: kernelbase.rgs */ +1 WINE_REGISTRY kernelbase.rgs + +STRINGTABLE LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +{ + LGRPID_WESTERN_EUROPE "Western Europe and United States" + LGRPID_CENTRAL_EUROPE "Central Europe" + LGRPID_BALTIC "Baltic" + LGRPID_GREEK "Greek" + LGRPID_CYRILLIC "Cyrillic" + LGRPID_TURKISH "Turkic" + LGRPID_JAPANESE "Japanese" + LGRPID_KOREAN "Korean" + LGRPID_TRADITIONAL_CHINESE "Traditional Chinese" + LGRPID_SIMPLIFIED_CHINESE "Simplified Chinese" + LGRPID_THAI "Thai" + LGRPID_HEBREW "Hebrew" + LGRPID_ARABIC "Arabic" + LGRPID_VIETNAMESE "Vietnamese" + LGRPID_INDIC "Indic" + LGRPID_GEORGIAN "Georgian" + LGRPID_ARMENIAN "Armenian" +} diff --git a/dll/win32/KernelBase/wine/kernelbase.rgs b/dll/win32/KernelBase/wine/kernelbase.rgs new file mode 100644 index 0000000000000..d4e166d0d575f --- /dev/null +++ b/dll/win32/KernelBase/wine/kernelbase.rgs @@ -0,0 +1,3755 @@ +HKLM +{ + NoRemove Software + { + NoRemove Microsoft + { + NoRemove 'Windows NT' + { + NoRemove CurrentVersion + { + 'Time Zones' + { + 'Afghanistan Standard Time' + { + val 'Display' = s '(UTC+04:30) Kabul' + val 'Dlt' = s 'Afghanistan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-39618' + val 'MUI_Dlt' = s '@tzres.dll,-39617' + val 'MUI_Std' = s '@tzres.dll,-39616' + val 'Std' = s 'Afghanistan Standard Time' + val 'TZI' = b f2feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Alaskan Standard Time' + { + val 'Display' = s '(UTC-09:00) Alaska' + val 'Dlt' = s 'Alaskan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-50194' + val 'MUI_Dlt' = s '@tzres.dll,-50193' + val 'MUI_Std' = s '@tzres.dll,-50192' + val 'Std' = s 'Alaskan Standard Time' + val 'TZI' = b 1c02000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b 1c02000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b 1c02000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b 1c02000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b 1c02000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b 1c02000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b 1c02000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b 1c02000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b 1c02000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'Aleutian Standard Time' + { + val 'Display' = s '(UTC-10:00) Aleutian Islands' + val 'Dlt' = s 'Aleutian Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-55202' + val 'MUI_Dlt' = s '@tzres.dll,-55201' + val 'MUI_Std' = s '@tzres.dll,-55200' + val 'Std' = s 'Aleutian Standard Time' + val 'TZI' = b 5802000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b 5802000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b 5802000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b 5802000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b 5802000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b 5802000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b 5802000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b 5802000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b 5802000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'Altai Standard Time' + { + val 'Display' = s '(UTC+07:00) Barnaul, Gorno-Altaysk' + val 'Dlt' = s 'Altai Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-4466' + val 'MUI_Dlt' = s '@tzres.dll,-4465' + val 'MUI_Std' = s '@tzres.dll,-4464' + val 'Std' = s 'Altai Standard Time' + val 'TZI' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 98feffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 98feffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 5cfeffff000000003c0000000000030000000500020000000000000000000100050001000000000000000000 + val '2017' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Arab Standard Time' + { + val 'Display' = s '(UTC+03:00) Kuwait, Riyadh' + val 'Dlt' = s 'Arab Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-41970' + val 'MUI_Dlt' = s '@tzres.dll,-41969' + val 'MUI_Std' = s '@tzres.dll,-41968' + val 'Std' = s 'Arab Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Arabian Standard Time' + { + val 'Display' = s '(UTC+04:00) Abu Dhabi, Muscat' + val 'Dlt' = s 'Arabian Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-42706' + val 'MUI_Dlt' = s '@tzres.dll,-42705' + val 'MUI_Std' = s '@tzres.dll,-42704' + val 'Std' = s 'Arabian Standard Time' + val 'TZI' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Arabic Standard Time' + { + val 'Display' = s '(UTC+03:00) Baghdad' + val 'Dlt' = s 'Arabic Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-8770' + val 'MUI_Dlt' = s '@tzres.dll,-8769' + val 'MUI_Std' = s '@tzres.dll,-8768' + val 'Std' = s 'Arabic Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 4cffffff00000000c4ffffff00000a0000000100040000000000000000000400060001000300000000000000 + val '2001' = b 4cffffff00000000c4ffffff00000a0001000100040000000000000000000400000001000300000000000000 + val '2002' = b 4cffffff00000000c4ffffff00000a0002000100040000000000000000000400010001000300000000000000 + val '2003' = b 4cffffff00000000c4ffffff00000a0003000100040000000000000000000400020001000300000000000000 + val '2004' = b 4cffffff00000000c4ffffff00000a0005000100040000000000000000000400040001000300000000000000 + val '2005' = b 4cffffff00000000c4ffffff00000a0006000100040000000000000000000400050001000300000000000000 + val '2006' = b 4cffffff00000000c4ffffff00000a0000000100040000000000000000000400060001000300000000000000 + val '2007' = b 4cffffff00000000c4ffffff00000a0001000100040000000000000000000400000001000300000000000000 + val '2008' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2008 + } + } + 'Argentina Standard Time' + { + val 'Display' = s '(UTC-03:00) City of Buenos Aires' + val 'Dlt' = s 'Argentina Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-19586' + val 'MUI_Dlt' = s '@tzres.dll,-19585' + val 'MUI_Std' = s '@tzres.dll,-19584' + val 'Std' = s 'Argentina Standard Time' + val 'TZI' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b b400000000000000c4ffffff0000030005000100000000000000000000000100060001000000000000000000 + val '2001' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b b400000000000000c4ffffff0000010001000100000000000000000000000c00000005000000000000000000 + val '2008' = b b400000000000000c4ffffff0000030000000300000000000000000000000a00000003000000000000000000 + val '2009' = b b400000000000000c4ffffff0000030000000300000000000000000000000100040001000000000000000000 + val '2010' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2010 + } + } + 'Astrakhan Standard Time' + { + val 'Display' = s '(UTC+04:00) Astrakhan, Ulyanovsk' + val 'Dlt' = s 'Astrakhan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-25106' + val 'MUI_Dlt' = s '@tzres.dll,-25105' + val 'MUI_Std' = s '@tzres.dll,-25104' + val 'Std' = s 'Astrakhan Standard Time' + val 'TZI' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 4cffffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 4cffffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 10ffffff000000003c0000000000030000000500020000000000000000000100050001000000000000000000 + val '2017' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Atlantic Standard Time' + { + val 'Display' = s '(UTC-04:00) Atlantic Time (Canada)' + val 'Dlt' = s 'Atlantic Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-34226' + val 'MUI_Dlt' = s '@tzres.dll,-34225' + val 'MUI_Std' = s '@tzres.dll,-34224' + val 'Std' = s 'Atlantic Standard Time' + val 'TZI' = b f000000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b f000000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b f000000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b f000000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b f000000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b f000000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b f000000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b f000000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b f000000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'AUS Central Standard Time' + { + val 'Display' = s '(UTC+09:30) Darwin' + val 'Dlt' = s 'AUS Central Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-47026' + val 'MUI_Dlt' = s '@tzres.dll,-47025' + val 'MUI_Std' = s '@tzres.dll,-47024' + val 'Std' = s 'AUS Central Standard Time' + val 'TZI' = b c6fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Aus Central W. Standard Time' + { + val 'Display' = s '(UTC+08:45) Eucla' + val 'Dlt' = s 'Aus Central W. Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-44674' + val 'MUI_Dlt' = s '@tzres.dll,-44673' + val 'MUI_Std' = s '@tzres.dll,-44672' + val 'Std' = s 'Aus Central W. Standard Time' + val 'TZI' = b f3fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2006' = b f3fdffff00000000c4ffffff0000010000000100000000000000000000000c00000001000200000000000000 + val '2007' = b f3fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2008' = b f3fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2009' = b f3fdffff00000000c4ffffff0000030000000500030000000000000000000100040001000000000000000000 + val '2010' = b f3fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2006 + val 'LastEntry' = d 2010 + } + } + 'AUS Eastern Standard Time' + { + val 'Display' = s '(UTC+10:00) Canberra, Melbourne, Sydney' + val 'Dlt' = s 'AUS Eastern Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-30370' + val 'MUI_Dlt' = s '@tzres.dll,-30369' + val 'MUI_Std' = s '@tzres.dll,-30368' + val 'Std' = s 'AUS Eastern Standard Time' + val 'TZI' = b a8fdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + 'Dynamic DST' + { + val '2000' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000800000005000200000000000000 + val '2001' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2002' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2003' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2004' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2005' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2006' = b a8fdffff00000000c4ffffff0000040000000100030000000000000000000a00000005000200000000000000 + val '2007' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2008' = b a8fdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2008 + } + } + 'Azerbaijan Standard Time' + { + val 'Display' = s '(UTC+04:00) Baku' + val 'Dlt' = s 'Azerbaijan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-11314' + val 'MUI_Dlt' = s '@tzres.dll,-11313' + val 'MUI_Std' = s '@tzres.dll,-11312' + val 'Std' = s 'Azerbaijan Standard Time' + val 'TZI' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2001' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2002' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2003' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2004' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2005' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2006' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2007' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2008' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2009' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2010' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2011' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2012' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2013' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2014' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2015' = b 10ffffff00000000c4ffffff00000a0000000500050000000000000000000300000005000400000000000000 + val '2016' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2016 + } + } + 'Azores Standard Time' + { + val 'Display' = s '(UTC-01:00) Azores' + val 'Dlt' = s 'Azores Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-18834' + val 'MUI_Dlt' = s '@tzres.dll,-18833' + val 'MUI_Std' = s '@tzres.dll,-18832' + val 'Std' = s 'Azores Standard Time' + val 'TZI' = b 3c00000000000000c4ffffff00000a0000000500010000000000000000000300000005000000000000000000 + } + 'Bahia Standard Time' + { + val 'Display' = s '(UTC-03:00) Salvador' + val 'Dlt' = s 'Bahia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-50898' + val 'MUI_Dlt' = s '@tzres.dll,-50897' + val 'MUI_Std' = s '@tzres.dll,-50896' + val 'Std' = s 'Bahia Standard Time' + val 'TZI' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b b400000000000000c4ffffff0000020000000500000000000000000000000a00000002000000000000000000 + val '2001' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000002000000000000000000 + val '2002' = b b400000000000000c4ffffff0000020000000300000000000000000000000b00000001000000000000000000 + val '2003' = b b400000000000000c4ffffff0000020000000300000000000000000000000100030001000000000000000000 + val '2004' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b b400000000000000c4ffffff0000010006000100000000000000000000000a00000003000000000000000000 + val '2012' = b b400000000000000c4ffffff0000020000000400000000000000000000000100000001000000000000000000 + val '2013' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2013 + } + } + 'Bangladesh Standard Time' + { + val 'Display' = s '(UTC+06:00) Dhaka' + val 'Dlt' = s 'Bangladesh Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-48722' + val 'MUI_Dlt' = s '@tzres.dll,-48721' + val 'MUI_Std' = s '@tzres.dll,-48720' + val 'Std' = s 'Bangladesh Standard Time' + val 'TZI' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2009' = b 98feffff00000000c4ffffff00000c000400050017003b003b00e70300000600050003001700000000000000 + val '2010' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2009 + val 'LastEntry' = d 2010 + } + } + 'Belarus Standard Time' + { + val 'Display' = s '(UTC+03:00) Minsk' + val 'Dlt' = s 'Belarus Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-56578' + val 'MUI_Dlt' = s '@tzres.dll,-56577' + val 'MUI_Std' = s '@tzres.dll,-56576' + val 'Std' = s 'Belarus Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 88ffffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2012 + } + } + 'Bougainville Standard Time' + { + val 'Display' = s '(UTC+11:00) Bougainville Island' + val 'Dlt' = s 'Bougainville Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-51970' + val 'MUI_Dlt' = s '@tzres.dll,-51969' + val 'MUI_Std' = s '@tzres.dll,-51968' + val 'Std' = s 'Bougainville Standard Time' + val 'TZI' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2001' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b a8fdffff00000000c4ffffff0000010003000100000000000000000000000c00000005000200000000000000 + val '2015' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'Canada Central Standard Time' + { + val 'Display' = s '(UTC-06:00) Saskatchewan' + val 'Dlt' = s 'Canada Central Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-25714' + val 'MUI_Dlt' = s '@tzres.dll,-25713' + val 'MUI_Std' = s '@tzres.dll,-25712' + val 'Std' = s 'Canada Central Standard Time' + val 'TZI' = b 6801000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Cape Verde Standard Time' + { + val 'Display' = s '(UTC-01:00) Cabo Verde Is.' + val 'Dlt' = s 'Cape Verde Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-2002' + val 'MUI_Dlt' = s '@tzres.dll,-2001' + val 'MUI_Std' = s '@tzres.dll,-2000' + val 'Std' = s 'Cape Verde Standard Time' + val 'TZI' = b 3c00000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Caucasus Standard Time' + { + val 'Display' = s '(UTC+04:00) Yerevan' + val 'Dlt' = s 'Caucasus Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-29874' + val 'MUI_Dlt' = s '@tzres.dll,-29873' + val 'MUI_Std' = s '@tzres.dll,-29872' + val 'Std' = s 'Caucasus Standard Time' + val 'TZI' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2012' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2012 + } + } + 'Cen. Australia Standard Time' + { + val 'Display' = s '(UTC+09:30) Adelaide' + val 'Dlt' = s 'Cen. Australia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-38930' + val 'MUI_Dlt' = s '@tzres.dll,-38929' + val 'MUI_Std' = s '@tzres.dll,-38928' + val 'Std' = s 'Cen. Australia Standard Time' + val 'TZI' = b c6fdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + 'Dynamic DST' + { + val '2000' = b c6fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2001' = b c6fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2002' = b c6fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2003' = b c6fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2004' = b c6fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2005' = b c6fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2006' = b c6fdffff00000000c4ffffff0000040000000100030000000000000000000a00000005000200000000000000 + val '2007' = b c6fdffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2008' = b c6fdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2008 + } + } + 'Central America Standard Time' + { + val 'Display' = s '(UTC-06:00) Central America' + val 'Dlt' = s 'Central America Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-36658' + val 'MUI_Dlt' = s '@tzres.dll,-36657' + val 'MUI_Std' = s '@tzres.dll,-36656' + val 'Std' = s 'Central America Standard Time' + val 'TZI' = b 6801000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2006' = b 6801000000000000c4ffffff00000a0000000100000000000000000000000400000005000000000000000000 + val '2007' = b 6801000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2006 + val 'LastEntry' = d 2007 + } + } + 'Central Asia Standard Time' + { + val 'Display' = s '(UTC+06:00) Astana' + val 'Dlt' = s 'Central Asia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-8178' + val 'MUI_Dlt' = s '@tzres.dll,-8177' + val 'MUI_Std' = s '@tzres.dll,-8176' + val 'Std' = s 'Central Asia Standard Time' + val 'TZI' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b d4feffff00000000c4ffffff00000a000000050002001e0000000000000003000000050002001e0000000000 + val '2001' = b d4feffff00000000c4ffffff00000a000000050002001e0000000000000003000000050002001e0000000000 + val '2002' = b d4feffff00000000c4ffffff00000a000000050002001e0000000000000003000000050002001e0000000000 + val '2003' = b d4feffff00000000c4ffffff00000a000000050002001e0000000000000003000000050002001e0000000000 + val '2004' = b d4feffff00000000c4ffffff00000a000000050002001e0000000000000003000000050002001e0000000000 + val '2005' = b d4feffff00000000c4ffffff00000100060001000000000000000000000003000000050002001e0000000000 + val '2006' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2006 + } + } + 'Central Brazilian Standard Time' + { + val 'Display' = s '(UTC-04:00) Cuiaba' + val 'Dlt' = s 'Central Brazilian Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-56338' + val 'MUI_Dlt' = s '@tzres.dll,-56337' + val 'MUI_Std' = s '@tzres.dll,-56336' + val 'Std' = s 'Central Brazilian Standard Time' + val 'TZI' = b f000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b f000000000000000c4ffffff0000020000000500000000000000000000000a00000002000000000000000000 + val '2001' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000002000000000000000000 + val '2002' = b f000000000000000c4ffffff0000020000000300000000000000000000000b00000001000000000000000000 + val '2003' = b f000000000000000c4ffffff0000020000000300000000000000000000000100030001000000000000000000 + val '2004' = b f000000000000000c4ffffff0000010004000100000000000000000000000b00020001000000000000000000 + val '2005' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2006' = b f000000000000000c4ffffff0000020000000300000000000000000000000b00000001000000000000000000 + val '2007' = b f000000000000000c4ffffff0000020000000500000000000000000000000a00000002000000000000000000 + val '2008' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2009' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2010' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2011' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2012' = b f000000000000000c4ffffff0000020000000400000000000000000000000a00000003000000000000000000 + val '2013' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2014' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2015' = b f000000000000000c4ffffff0000020000000400000000000000000000000a00000003000000000000000000 + val '2016' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2017' = b f000000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2018' = b f000000000000000c4ffffff0000020000000300000000000000000000000b00000001000000000000000000 + val '2019' = b f000000000000000c4ffffff0000020000000300000000000000000000000100020001000000000000000000 + val '2020' = b f000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2020 + } + } + 'Central Europe Standard Time' + { + val 'Display' = s '(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague' + val 'Dlt' = s 'Central Europe Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-4898' + val 'MUI_Dlt' = s '@tzres.dll,-4897' + val 'MUI_Std' = s '@tzres.dll,-4896' + val 'Std' = s 'Central Europe Standard Time' + val 'TZI' = b c4ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + } + 'Central European Standard Time' + { + val 'Display' = s '(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb' + val 'Dlt' = s 'Central European Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-2978' + val 'MUI_Dlt' = s '@tzres.dll,-2977' + val 'MUI_Std' = s '@tzres.dll,-2976' + val 'Std' = s 'Central European Standard Time' + val 'TZI' = b c4ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + } + 'Central Pacific Standard Time' + { + val 'Display' = s '(UTC+11:00) Solomon Is., New Caledonia' + val 'Dlt' = s 'Central Pacific Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-64418' + val 'MUI_Dlt' = s '@tzres.dll,-64417' + val 'MUI_Std' = s '@tzres.dll,-64416' + val 'Std' = s 'Central Pacific Standard Time' + val 'TZI' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Central Standard Time' + { + val 'Display' = s '(UTC-06:00) Central Time (US & Canada)' + val 'Dlt' = s 'Central Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-17458' + val 'MUI_Dlt' = s '@tzres.dll,-17457' + val 'MUI_Std' = s '@tzres.dll,-17456' + val 'Std' = s 'Central Standard Time' + val 'TZI' = b 6801000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b 6801000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'Central Standard Time (Mexico)' + { + val 'Display' = s '(UTC-06:00) Guadalajara, Mexico City, Monterrey' + val 'Dlt' = s 'Central Daylight Time (Mexico)' + val 'MUI_Display' = s '@tzres.dll,-32802' + val 'MUI_Dlt' = s '@tzres.dll,-32801' + val 'MUI_Std' = s '@tzres.dll,-32800' + val 'Std' = s 'Central Standard Time (Mexico)' + val 'TZI' = b 6801000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b 6801000000000000c4ffffff0000090000000500020000000000000000000500000001000200000000000000 + val '2002' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2008' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2009' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2010' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2011' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2012' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2013' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2014' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2015' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2016' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2017' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2018' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2019' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2020' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2021' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2022' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2023' = b 6801000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2023 + } + } + 'Chatham Islands Standard Time' + { + val 'Display' = s '(UTC+12:45) Chatham Islands' + val 'Dlt' = s 'Chatham Islands Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-62306' + val 'MUI_Dlt' = s '@tzres.dll,-62305' + val 'MUI_Std' = s '@tzres.dll,-62304' + val 'Std' = s 'Chatham Islands Standard Time' + val 'TZI' = b 03fdffff00000000c4ffffff000004000000010003002d0000000000000009000000050002002d0000000000 + 'Dynamic DST' + { + val '2000' = b 03fdffff00000000c4ffffff000003000000030003002d000000000000000a000000010002002d0000000000 + val '2001' = b 03fdffff00000000c4ffffff000003000000030003002d000000000000000a000000010002002d0000000000 + val '2002' = b 03fdffff00000000c4ffffff000003000000030003002d000000000000000a000000010002002d0000000000 + val '2003' = b 03fdffff00000000c4ffffff000003000000030003002d000000000000000a000000010002002d0000000000 + val '2004' = b 03fdffff00000000c4ffffff000003000000030003002d000000000000000a000000010002002d0000000000 + val '2005' = b 03fdffff00000000c4ffffff000003000000030003002d000000000000000a000000010002002d0000000000 + val '2006' = b 03fdffff00000000c4ffffff000003000000030003002d000000000000000a000000010002002d0000000000 + val '2007' = b 03fdffff00000000c4ffffff000003000000030003002d0000000000000009000000050002002d0000000000 + val '2008' = b 03fdffff00000000c4ffffff000004000000010003002d0000000000000009000000050002002d0000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2008 + } + } + 'China Standard Time' + { + val 'Display' = s '(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi' + val 'Dlt' = s 'China Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-162' + val 'MUI_Dlt' = s '@tzres.dll,-161' + val 'MUI_Std' = s '@tzres.dll,-160' + val 'Std' = s 'China Standard Time' + val 'TZI' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Cuba Standard Time' + { + val 'Display' = s '(UTC-05:00) Havana' + val 'Dlt' = s 'Cuba Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-17138' + val 'MUI_Dlt' = s '@tzres.dll,-17137' + val 'MUI_Std' = s '@tzres.dll,-17136' + val 'Std' = s 'Cuba Standard Time' + val 'TZI' = b 2c01000000000000c4ffffff00000b0000000100010000000000000000000300000002000000000000000000 + 'Dynamic DST' + { + val '2000' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000400000001000000000000000000 + val '2001' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000400000001000000000000000000 + val '2002' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000400000001000000000000000000 + val '2003' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000400000001000000000000000000 + val '2004' = b 2c01000000000000c4ffffff0000010004000100000000000000000000000300000005000000000000000000 + val '2005' = b f000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000100000001000000000000000000 + val '2007' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000300000002000000000000000000 + val '2008' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000300000003000000000000000000 + val '2009' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000300000002000000000000000000 + val '2010' = b 2c01000000000000c4ffffff00000a0000000500010000000000000000000300000002000000000000000000 + val '2011' = b 2c01000000000000c4ffffff00000b0000000200010000000000000000000300000003000000000000000000 + val '2012' = b 2c01000000000000c4ffffff00000b0000000100010000000000000000000400000001000000000000000000 + val '2013' = b 2c01000000000000c4ffffff00000b0000000100010000000000000000000300000002000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2013 + } + } + 'Dateline Standard Time' + { + val 'Display' = s '(UTC-12:00) International Date Line West' + val 'Dlt' = s 'Dateline Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-50386' + val 'MUI_Dlt' = s '@tzres.dll,-50385' + val 'MUI_Std' = s '@tzres.dll,-50384' + val 'Std' = s 'Dateline Standard Time' + val 'TZI' = b d002000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'E. Africa Standard Time' + { + val 'Display' = s '(UTC+03:00) Nairobi' + val 'Dlt' = s 'E. Africa Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-62722' + val 'MUI_Dlt' = s '@tzres.dll,-62721' + val 'MUI_Std' = s '@tzres.dll,-62720' + val 'Std' = s 'E. Africa Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'E. Australia Standard Time' + { + val 'Display' = s '(UTC+10:00) Brisbane' + val 'Dlt' = s 'E. Australia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-65266' + val 'MUI_Dlt' = s '@tzres.dll,-65265' + val 'MUI_Std' = s '@tzres.dll,-65264' + val 'Std' = s 'E. Australia Standard Time' + val 'TZI' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'E. Europe Standard Time' + { + val 'Display' = s '(UTC+02:00) Chisinau' + val 'Dlt' = s 'E. Europe Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-32130' + val 'MUI_Dlt' = s '@tzres.dll,-32129' + val 'MUI_Std' = s '@tzres.dll,-32128' + val 'Std' = s 'E. Europe Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + } + 'E. South America Standard Time' + { + val 'Display' = s '(UTC-03:00) Brasilia' + val 'Dlt' = s 'E. South America Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-64098' + val 'MUI_Dlt' = s '@tzres.dll,-64097' + val 'MUI_Std' = s '@tzres.dll,-64096' + val 'Std' = s 'E. South America Standard Time' + val 'TZI' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b b400000000000000c4ffffff0000020000000500000000000000000000000a00000002000000000000000000 + val '2001' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000002000000000000000000 + val '2002' = b b400000000000000c4ffffff0000020000000300000000000000000000000b00000001000000000000000000 + val '2003' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2004' = b b400000000000000c4ffffff0000020000000300000000000000000000000b00020001000000000000000000 + val '2005' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2006' = b b400000000000000c4ffffff0000020000000300000000000000000000000b00000001000000000000000000 + val '2007' = b b400000000000000c4ffffff0000020000000500000000000000000000000a00000002000000000000000000 + val '2008' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2009' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2010' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2011' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2012' = b b400000000000000c4ffffff0000020000000400000000000000000000000a00000003000000000000000000 + val '2013' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2014' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2015' = b b400000000000000c4ffffff0000020000000400000000000000000000000a00000003000000000000000000 + val '2016' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2017' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000003000000000000000000 + val '2018' = b b400000000000000c4ffffff0000020000000300000000000000000000000b00000001000000000000000000 + val '2019' = b b400000000000000c4ffffff0000020000000300000000000000000000000100020001000000000000000000 + val '2020' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2020 + } + } + 'Easter Island Standard Time' + { + val 'Display' = s '(UTC-06:00) Easter Island' + val 'Dlt' = s 'Easter Island Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-58594' + val 'MUI_Dlt' = s '@tzres.dll,-58593' + val 'MUI_Std' = s '@tzres.dll,-58592' + val 'Std' = s 'Easter Island Standard Time' + val 'TZI' = b 6801000000000000c4ffffff0000040006000100160000000000000000000900060001001600000000000000 + 'Dynamic DST' + { + val '2000' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2001' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2002' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2003' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2004' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2005' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2006' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2007' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2008' = b 6801000000000000c4ffffff0000030006000500160000000000000000000a00060002001600000000000000 + val '2009' = b 6801000000000000c4ffffff0000030006000200160000000000000000000a00060002001600000000000000 + val '2010' = b 6801000000000000c4ffffff0000040006000100160000000000000000000a00060002001600000000000000 + val '2011' = b 6801000000000000c4ffffff0000050006000100160000000000000000000800060003001600000000000000 + val '2012' = b 6801000000000000c4ffffff0000040006000500160000000000000000000900060001001600000000000000 + val '2013' = b 6801000000000000c4ffffff0000040006000400160000000000000000000900060001001600000000000000 + val '2014' = b 6801000000000000c4ffffff0000040006000400160000000000000000000900060001001600000000000000 + val '2015' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 6801000000000000c4ffffff0000050006000200160000000000000000000800060002001600000000000000 + val '2017' = b 6801000000000000c4ffffff0000050006000200160000000000000000000800060002001600000000000000 + val '2018' = b 6801000000000000c4ffffff0000050006000200160000000000000000000800060002001600000000000000 + val '2019' = b 6801000000000000c4ffffff0000040006000100160000000000000000000900060001001600000000000000 + val '2020' = b 6801000000000000c4ffffff0000040006000100160000000000000000000900060001001600000000000000 + val '2021' = b 6801000000000000c4ffffff0000040006000100160000000000000000000900060001001600000000000000 + val '2022' = b 6801000000000000c4ffffff0000040006000100160000000000000000000900060002001600000000000000 + val '2023' = b 6801000000000000c4ffffff0000040006000100160000000000000000000900060001001600000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2023 + } + } + 'Eastern Standard Time' + { + val 'Display' = s '(UTC-05:00) Eastern Time (US & Canada)' + val 'Dlt' = s 'Eastern Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-30802' + val 'MUI_Dlt' = s '@tzres.dll,-30801' + val 'MUI_Std' = s '@tzres.dll,-30800' + val 'Std' = s 'Eastern Standard Time' + val 'TZI' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'Eastern Standard Time (Mexico)' + { + val 'Display' = s '(UTC-05:00) Chetumal' + val 'Dlt' = s 'Eastern Daylight Time (Mexico)' + val 'MUI_Display' = s '@tzres.dll,-59538' + val 'MUI_Dlt' = s '@tzres.dll,-59537' + val 'MUI_Std' = s '@tzres.dll,-59536' + val 'Std' = s 'Eastern Standard Time (Mexico)' + val 'TZI' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b 6801000000000000c4ffffff0000090000000500020000000000000000000500000001000200000000000000 + val '2002' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2008' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2009' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2010' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2011' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2012' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2013' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2014' = b 6801000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2015' = b 6801000000000000c4ffffff0000010004000100000000000000000000000200000001000200000000000000 + val '2016' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2016 + } + } + 'Egypt Standard Time' + { + val 'Display' = s '(UTC+02:00) Cairo' + val 'Dlt' = s 'Egypt Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-59426' + val 'MUI_Dlt' = s '@tzres.dll,-59425' + val 'MUI_Std' = s '@tzres.dll,-59424' + val 'Std' = s 'Egypt Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff00000a000400050017003b003b00e70300000400050005000000000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff000009000400050017003b003b00e70300000400050005000000000000000000 + val '2001' = b 88ffffff00000000c4ffffff000009000400050017003b003b00e70300000400050005000000000000000000 + val '2002' = b 88ffffff00000000c4ffffff000009000400050017003b003b00e70300000400050005000000000000000000 + val '2003' = b 88ffffff00000000c4ffffff000009000400050017003b003b00e70300000400050005000000000000000000 + val '2004' = b 88ffffff00000000c4ffffff000009000400050017003b003b00e70300000400050005000000000000000000 + val '2005' = b 88ffffff00000000c4ffffff000009000400050017003b003b00e70300000400050005000000000000000000 + val '2006' = b 88ffffff00000000c4ffffff000009000400030017003b003b00e70300000400050005000000000000000000 + val '2007' = b 88ffffff00000000c4ffffff000009000400010017003b003b00e70300000400050005000000000000000000 + val '2008' = b 88ffffff00000000c4ffffff000008000400050017003b003b00e70300000400050005000000000000000000 + val '2009' = b 88ffffff00000000c4ffffff000008000400030017003b003b00e70300000400050005000000000000000000 + val '2010' = b 88ffffff00000000c4ffffff000009000400050017003b003b00e70300000400050005000000000000000000 + val '2011' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 88ffffff00000000c4ffffff000009000400050017003b003b00e703000005000400030017003b003b00e703 + val '2015' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2018' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2019' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2020' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2021' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2022' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2023' = b 88ffffff00000000c4ffffff00000a000400050017003b003b00e70300000400050005000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2023 + } + } + 'Ekaterinburg Standard Time' + { + val 'Display' = s '(UTC+05:00) Ekaterinburg' + val 'Dlt' = s 'Ekaterinburg Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-45954' + val 'MUI_Dlt' = s '@tzres.dll,-45953' + val 'MUI_Std' = s '@tzres.dll,-45952' + val 'Std' = s 'Ekaterinburg Standard Time' + val 'TZI' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b d4feffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b d4feffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'Fiji Standard Time' + { + val 'Display' = s '(UTC+12:00) Fiji' + val 'Dlt' = s 'Fiji Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-25682' + val 'MUI_Dlt' = s '@tzres.dll,-25681' + val 'MUI_Std' = s '@tzres.dll,-25680' + val 'Std' = s 'Fiji Standard Time' + val 'TZI' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 30fdffff00000000c4ffffff0000020000000500030000000000000000000100060001000000000000000000 + val '2001' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 30fdffff00000000c4ffffff0000010004000100000000000000000000000b00000005000200000000000000 + val '2010' = b 30fdffff00000000c4ffffff0000030000000500030000000000000000000a00000004000200000000000000 + val '2011' = b 30fdffff00000000c4ffffff0000030000000100030000000000000000000a00000004000200000000000000 + val '2012' = b 30fdffff00000000c4ffffff0000010000000400030000000000000000000a00000003000200000000000000 + val '2013' = b 30fdffff00000000c4ffffff0000010000000300030000000000000000000a00000004000200000000000000 + val '2014' = b 30fdffff00000000c4ffffff0000010000000300020000000000000000000b00000001000200000000000000 + val '2015' = b 30fdffff00000000c4ffffff0000010000000300030000000000000000000b00000001000200000000000000 + val '2016' = b 30fdffff00000000c4ffffff0000010000000300030000000000000000000b00000001000200000000000000 + val '2017' = b 30fdffff00000000c4ffffff0000010000000300030000000000000000000b00000001000200000000000000 + val '2018' = b 30fdffff00000000c4ffffff0000010000000200030000000000000000000b00000001000200000000000000 + val '2019' = b 30fdffff00000000c4ffffff0000010000000200030000000000000000000b00000002000200000000000000 + val '2020' = b 30fdffff00000000c4ffffff0000010000000200030000000000000000000c00000003000200000000000000 + val '2021' = b 30fdffff00000000c4ffffff0000010000000300030000000000000000000100050001000000000000000000 + val '2022' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2022 + } + } + 'FLE Standard Time' + { + val 'Display' = s '(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius' + val 'Dlt' = s 'FLE Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-63266' + val 'MUI_Dlt' = s '@tzres.dll,-63265' + val 'MUI_Std' = s '@tzres.dll,-63264' + val 'Std' = s 'FLE Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + } + 'Georgian Standard Time' + { + val 'Display' = s '(UTC+04:00) Tbilisi' + val 'Dlt' = s 'Georgian Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-418' + val 'MUI_Dlt' = s '@tzres.dll,-417' + val 'MUI_Std' = s '@tzres.dll,-416' + val 'Std' = s 'Georgian Standard Time' + val 'TZI' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 10ffffff00000000c4ffffff00000a0000000500000000000000000000000300000005000000000000000000 + val '2001' = b 10ffffff00000000c4ffffff00000a0000000500000000000000000000000300000005000000000000000000 + val '2002' = b 10ffffff00000000c4ffffff00000a0000000500000000000000000000000300000005000000000000000000 + val '2003' = b 10ffffff00000000c4ffffff00000a0000000500000000000000000000000300000005000000000000000000 + val '2004' = b 10ffffff000000003c00000000000a0000000500030000000000000000000300000005000000000000000000 + val '2005' = b 4cffffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2006' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2006 + } + } + 'GMT Standard Time' + { + val 'Display' = s '(UTC+00:00) Dublin, Edinburgh, Lisbon, London' + val 'Dlt' = s 'GMT Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-7410' + val 'MUI_Dlt' = s '@tzres.dll,-7409' + val 'MUI_Std' = s '@tzres.dll,-7408' + val 'Std' = s 'GMT Standard Time' + val 'TZI' = b 0000000000000000c4ffffff00000a0000000500020000000000000000000300000005000100000000000000 + } + 'Greenland Standard Time' + { + val 'Display' = s '(UTC-03:00) Greenland' + val 'Dlt' = s 'Greenland Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-58098' + val 'MUI_Dlt' = s '@tzres.dll,-58097' + val 'MUI_Std' = s '@tzres.dll,-58096' + val 'Std' = s 'Greenland Standard Time' + val 'TZI' = b 7800000000000000c4ffffff00000a0000000500000000000000000000000100020001000000000000000000 + 'Dynamic DST' + { + val '2000' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2001' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060004001600000000000000 + val '2002' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2003' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2004' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2005' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2006' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2007' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060004001600000000000000 + val '2008' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2009' = b b400000000000000c4ffffff00000a0006000400170000000000000000000300060005001600000000000000 + val '2010' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2011' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2012' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060004001600000000000000 + val '2013' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2014' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2015' = b b400000000000000c4ffffff00000a0006000400170000000000000000000300060005001600000000000000 + val '2016' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2017' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2018' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060004001600000000000000 + val '2019' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2020' = b b400000000000000c4ffffff00000a0006000400170000000000000000000300060005001600000000000000 + val '2021' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2022' = b b400000000000000c4ffffff00000a0006000500170000000000000000000300060005001600000000000000 + val '2023' = b b400000000000000c4ffffff00000a0000000500000000000000000000000300060005001600000000000000 + val '2024' = b 7800000000000000c4ffffff00000a0000000500000000000000000000000300060005001700000000000000 + val '2025' = b 7800000000000000c4ffffff00000a0000000500000000000000000000000300060005001700000000000000 + val '2026' = b 7800000000000000c4ffffff00000a0000000500000000000000000000000300060005001700000000000000 + val '2027' = b 7800000000000000c4ffffff00000a0000000500000000000000000000000300060005001700000000000000 + val '2028' = b 7800000000000000c4ffffff00000a0000000500000000000000000000000300060005001700000000000000 + val '2029' = b 7800000000000000c4ffffff00000a0000000500000000000000000000000300060004001700000000000000 + val '2030' = b 7800000000000000c4ffffff00000a0000000500000000000000000000000100020001000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2030 + } + } + 'Greenwich Standard Time' + { + val 'Display' = s '(UTC+00:00) Monrovia, Reykjavik' + val 'Dlt' = s 'Greenwich Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-47170' + val 'MUI_Dlt' = s '@tzres.dll,-47169' + val 'MUI_Std' = s '@tzres.dll,-47168' + val 'Std' = s 'Greenwich Standard Time' + val 'TZI' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'GTB Standard Time' + { + val 'Display' = s '(UTC+02:00) Athens, Bucharest' + val 'Dlt' = s 'GTB Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-24194' + val 'MUI_Dlt' = s '@tzres.dll,-24193' + val 'MUI_Std' = s '@tzres.dll,-24192' + val 'Std' = s 'GTB Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + } + 'Haiti Standard Time' + { + val 'Display' = s '(UTC-05:00) Haiti' + val 'Dlt' = s 'Haiti Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-2898' + val 'MUI_Dlt' = s '@tzres.dll,-2897' + val 'MUI_Std' = s '@tzres.dll,-2896' + val 'Std' = s 'Haiti Standard Time' + val 'TZI' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2001' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 2c01000000000000c4ffffff00000a0000000500000000000000000000000400000001000000000000000000 + val '2006' = b 2c01000000000000c4ffffff00000a0000000500000000000000000000000400000001000000000000000000 + val '2007' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2013' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2014' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2015' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2016' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Hawaiian Standard Time' + { + val 'Display' = s '(UTC-10:00) Hawaii' + val 'Dlt' = s 'Hawaiian Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-53378' + val 'MUI_Dlt' = s '@tzres.dll,-53377' + val 'MUI_Std' = s '@tzres.dll,-53376' + val 'Std' = s 'Hawaiian Standard Time' + val 'TZI' = b 5802000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'India Standard Time' + { + val 'Display' = s '(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi' + val 'Dlt' = s 'India Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-22402' + val 'MUI_Dlt' = s '@tzres.dll,-22401' + val 'MUI_Std' = s '@tzres.dll,-22400' + val 'Std' = s 'India Standard Time' + val 'TZI' = b b6feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Iran Standard Time' + { + val 'Display' = s '(UTC+03:30) Tehran' + val 'Dlt' = s 'Iran Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-5570' + val 'MUI_Dlt' = s '@tzres.dll,-5569' + val 'MUI_Std' = s '@tzres.dll,-5568' + val 'Std' = s 'Iran Standard Time' + val 'TZI' = b 2effffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 2effffff00000000c4ffffff000009000300030017003b003b00e703000003000100030017003b003b00e703 + val '2001' = b 2effffff00000000c4ffffff000009000500030017003b003b00e703000003000300030017003b003b00e703 + val '2002' = b 2effffff00000000c4ffffff000009000600030017003b003b00e703000003000400030017003b003b00e703 + val '2003' = b 2effffff00000000c4ffffff000009000000030017003b003b00e703000003000500030017003b003b00e703 + val '2004' = b 2effffff00000000c4ffffff000009000100030017003b003b00e703000003000600030017003b003b00e703 + val '2005' = b 2effffff00000000c4ffffff000009000300030017003b003b00e703000003000100030017003b003b00e703 + val '2006' = b 2effffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b 2effffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 2effffff00000000c4ffffff000009000600030017003b003b00e703000003000400030017003b003b00e703 + val '2009' = b 2effffff00000000c4ffffff000009000100030017003b003b00e703000003000600030017003b003b00e703 + val '2010' = b 2effffff00000000c4ffffff000009000200030017003b003b00e703000003000000030017003b003b00e703 + val '2011' = b 2effffff00000000c4ffffff000009000300030017003b003b00e703000003000100030017003b003b00e703 + val '2012' = b 2effffff00000000c4ffffff000009000400030017003b003b00e703000003000200030017003b003b00e703 + val '2013' = b 2effffff00000000c4ffffff000009000600030017003b003b00e703000003000400030017003b003b00e703 + val '2014' = b 2effffff00000000c4ffffff000009000000030017003b003b00e703000003000500030017003b003b00e703 + val '2015' = b 2effffff00000000c4ffffff000009000100030017003b003b00e703000003000600030017003b003b00e703 + val '2016' = b 2effffff00000000c4ffffff000009000200030017003b003b00e703000003000000030017003b003b00e703 + val '2017' = b 2effffff00000000c4ffffff000009000400030017003b003b00e703000003000200030017003b003b00e703 + val '2018' = b 2effffff00000000c4ffffff000009000500030017003b003b00e703000003000300030017003b003b00e703 + val '2019' = b 2effffff00000000c4ffffff000009000600030017003b003b00e703000003000400030017003b003b00e703 + val '2020' = b 2effffff00000000c4ffffff000009000000030017003b003b00e703000003000500030017003b003b00e703 + val '2021' = b 2effffff00000000c4ffffff000009000200030017003b003b00e703000003000000030017003b003b00e703 + val '2022' = b 2effffff00000000c4ffffff000009000300030017003b003b00e703000003000100030017003b003b00e703 + val '2023' = b 2effffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2023 + } + } + 'Israel Standard Time' + { + val 'Display' = s '(UTC+02:00) Jerusalem' + val 'Dlt' = s 'Israel Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-47970' + val 'MUI_Dlt' = s '@tzres.dll,-47969' + val 'MUI_Std' = s '@tzres.dll,-47968' + val 'Std' = s 'Israel Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050005000200000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff00000a0005000100010000000000000000000400050002000200000000000000 + val '2001' = b 88ffffff00000000c4ffffff0000090001000400010000000000000000000400010002000100000000000000 + val '2002' = b 88ffffff00000000c4ffffff00000a0001000100010000000000000000000300050005000100000000000000 + val '2003' = b 88ffffff00000000c4ffffff00000a0005000100010000000000000000000300050005000100000000000000 + val '2004' = b 88ffffff00000000c4ffffff0000090003000400010000000000000000000400030001000100000000000000 + val '2005' = b 88ffffff00000000c4ffffff00000a0000000200020000000000000000000400050001000200000000000000 + val '2006' = b 88ffffff00000000c4ffffff00000a0000000100020000000000000000000300050005000200000000000000 + val '2007' = b 88ffffff00000000c4ffffff0000090000000300020000000000000000000300050005000200000000000000 + val '2008' = b 88ffffff00000000c4ffffff00000a0000000100020000000000000000000300050005000200000000000000 + val '2009' = b 88ffffff00000000c4ffffff0000090000000500020000000000000000000300050005000200000000000000 + val '2010' = b 88ffffff00000000c4ffffff0000090000000200020000000000000000000300050005000200000000000000 + val '2011' = b 88ffffff00000000c4ffffff00000a0000000100020000000000000000000400050001000200000000000000 + val '2012' = b 88ffffff00000000c4ffffff0000090000000400020000000000000000000300050005000200000000000000 + val '2013' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050005000200000000000000 + val '2014' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2015' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2016' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2017' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2018' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2019' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050005000200000000000000 + val '2020' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2021' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2022' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2023' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2024' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050005000200000000000000 + val '2025' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2026' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2027' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2028' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2029' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050004000200000000000000 + val '2030' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300050005000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2030 + } + } + 'Jordan Standard Time' + { + val 'Display' = s '(UTC+02:00) Amman' + val 'Dlt' = s 'Jordan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-17234' + val 'MUI_Dlt' = s '@tzres.dll,-17233' + val 'MUI_Std' = s '@tzres.dll,-17232' + val 'Std' = s 'Jordan Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff0000090005000500010000000000000000000300040005000000000000000000 + val '2001' = b 88ffffff00000000c4ffffff0000090005000500010000000000000000000300040005000000000000000000 + val '2002' = b 88ffffff00000000c4ffffff00000900050005000100000000000000000003000400050017003b003b00e703 + val '2003' = b 88ffffff00000000c4ffffff00000a00050004000100000000000000000003000400050017003b003b00e703 + val '2004' = b 88ffffff00000000c4ffffff00000a00050003000100000000000000000003000400050017003b003b00e703 + val '2005' = b 88ffffff00000000c4ffffff00000900050005000100000000000000000003000400050017003b003b00e703 + val '2006' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2007' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2008' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2009' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2010' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2011' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2012' = b 88ffffff00000000c4ffffff00000100000001000000000000000000000003000400050017003b003b00e703 + val '2013' = b 88ffffff00000000c4ffffff00000c0005000300000000000000000000000100020001000000000000000000 + val '2014' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2015' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2016' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2017' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2018' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2019' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2020' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2021' = b 88ffffff00000000c4ffffff00000a00050005000100000000000000000003000400050017003b003b00e703 + val '2022' = b 88ffffff00000000c4ffffff00000100060001000000000000000000000002000400050017003b003b00e703 + val '2023' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2023 + } + } + 'Kaliningrad Standard Time' + { + val 'Display' = s '(UTC+02:00) Kaliningrad' + val 'Dlt' = s 'Kaliningrad Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-14722' + val 'MUI_Dlt' = s '@tzres.dll,-14721' + val 'MUI_Std' = s '@tzres.dll,-14720' + val 'Std' = s 'Kaliningrad Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 88ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 88ffffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'Korea Standard Time' + { + val 'Display' = s '(UTC+09:00) Seoul' + val 'Dlt' = s 'Korea Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-62050' + val 'MUI_Dlt' = s '@tzres.dll,-62049' + val 'MUI_Std' = s '@tzres.dll,-62048' + val 'Std' = s 'Korea Standard Time' + val 'TZI' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Libya Standard Time' + { + val 'Display' = s '(UTC+02:00) Tripoli' + val 'Dlt' = s 'Libya Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-50770' + val 'MUI_Dlt' = s '@tzres.dll,-50769' + val 'MUI_Std' = s '@tzres.dll,-50768' + val 'Std' = s 'Libya Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2012' = b c4ffffff00000000c4ffffff00000b0006000200020000000000000000000100000001000000000000000000 + val '2013' = b c4ffffff00000000c4ffffff0000010002000100000000000000000000000300050005000100000000000000 + val '2014' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2012 + val 'LastEntry' = d 2014 + } + } + 'Line Islands Standard Time' + { + val 'Display' = s '(UTC+14:00) Kiritimati Island' + val 'Dlt' = s 'Line Islands Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-16770' + val 'MUI_Dlt' = s '@tzres.dll,-16769' + val 'MUI_Std' = s '@tzres.dll,-16768' + val 'Std' = s 'Line Islands Standard Time' + val 'TZI' = b b8fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Lord Howe Standard Time' + { + val 'Display' = s '(UTC+10:30) Lord Howe Island' + val 'Dlt' = s 'Lord Howe Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-8658' + val 'MUI_Dlt' = s '@tzres.dll,-8657' + val 'MUI_Std' = s '@tzres.dll,-8656' + val 'Std' = s 'Lord Howe Standard Time' + val 'TZI' = b 8afdffff00000000e2ffffff0000040000000100020000000000000000000a00000001000200000000000000 + 'Dynamic DST' + { + val '2000' = b 8afdffff00000000e2ffffff0000030000000500020000000000000000000800000005000200000000000000 + val '2001' = b 8afdffff00000000e2ffffff0000030000000500020000000000000000000a00000005000200000000000000 + val '2002' = b 8afdffff00000000e2ffffff0000030000000500020000000000000000000a00000005000200000000000000 + val '2003' = b 8afdffff00000000e2ffffff0000030000000500020000000000000000000a00000005000200000000000000 + val '2004' = b 8afdffff00000000e2ffffff0000030000000500020000000000000000000a00000005000200000000000000 + val '2005' = b 8afdffff00000000e2ffffff0000030000000500020000000000000000000a00000005000200000000000000 + val '2006' = b 8afdffff00000000e2ffffff0000040000000100020000000000000000000a00000005000200000000000000 + val '2007' = b 8afdffff00000000e2ffffff0000030000000500020000000000000000000a00000005000200000000000000 + val '2008' = b 8afdffff00000000e2ffffff0000040000000100020000000000000000000a00000001000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2008 + } + } + 'Magadan Standard Time' + { + val 'Display' = s '(UTC+11:00) Magadan' + val 'Dlt' = s 'Magadan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-8834' + val 'MUI_Dlt' = s '@tzres.dll,-8833' + val 'MUI_Std' = s '@tzres.dll,-8832' + val 'Std' = s 'Magadan Standard Time' + val 'TZI' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 6cfdffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b a8fdffff0000000088ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 6cfdffff000000003c0000000000040000000400020000000000000000000100050001000000000000000000 + val '2017' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Magallanes Standard Time' + { + val 'Display' = s '(UTC-03:00) Punta Arenas' + val 'Dlt' = s 'Magallanes Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-13122' + val 'MUI_Dlt' = s '@tzres.dll,-13121' + val 'MUI_Std' = s '@tzres.dll,-13120' + val 'Std' = s 'Magallanes Standard Time' + val 'TZI' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2001' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2002' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2003' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2004' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2005' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2006' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2007' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2008' = b f000000000000000c4ffffff0000030000000500000000000000000000000a00000002000000000000000000 + val '2009' = b f000000000000000c4ffffff0000030000000300000000000000000000000a00000002000000000000000000 + val '2010' = b f000000000000000c4ffffff0000040000000100000000000000000000000a00000002000000000000000000 + val '2011' = b f000000000000000c4ffffff0000050000000200000000000000000000000800000003000000000000000000 + val '2012' = b f000000000000000c4ffffff0000040000000500000000000000000000000900000001000000000000000000 + val '2013' = b f000000000000000c4ffffff0000040000000400000000000000000000000900000002000000000000000000 + val '2014' = b f000000000000000c4ffffff0000040000000400000000000000000000000900000001000000000000000000 + val '2015' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b b4000000000000003c0000000000050000000300000000000000000000000800000002000000000000000000 + val '2017' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Marquesas Standard Time' + { + val 'Display' = s '(UTC-09:30) Marquesas Islands' + val 'Dlt' = s 'Marquesas Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-20738' + val 'MUI_Dlt' = s '@tzres.dll,-20737' + val 'MUI_Std' = s '@tzres.dll,-20736' + val 'Std' = s 'Marquesas Standard Time' + val 'TZI' = b 3a02000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Mauritius Standard Time' + { + val 'Display' = s '(UTC+04:00) Port Louis' + val 'Dlt' = s 'Mauritius Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-60898' + val 'MUI_Dlt' = s '@tzres.dll,-60897' + val 'MUI_Std' = s '@tzres.dll,-60896' + val 'Std' = s 'Mauritius Standard Time' + val 'TZI' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2008' = b 10ffffff00000000c4ffffff0000010002000100000000000000000000000a00000005000200000000000000 + val '2009' = b 10ffffff00000000c4ffffff0000030000000500020000000000000000000100040001000000000000000000 + val '2010' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2008 + val 'LastEntry' = d 2010 + } + } + 'Middle East Standard Time' + { + val 'Display' = s '(UTC+02:00) Beirut' + val 'Dlt' = s 'Middle East Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-15970' + val 'MUI_Dlt' = s '@tzres.dll,-15969' + val 'MUI_Std' = s '@tzres.dll,-15968' + val 'Std' = s 'Middle East Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff00000a0000000500000000000000000000000300000005000000000000000000 + } + 'Montevideo Standard Time' + { + val 'Display' = s '(UTC-03:00) Montevideo' + val 'Dlt' = s 'Montevideo Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-27970' + val 'MUI_Dlt' = s '@tzres.dll,-27969' + val 'MUI_Std' = s '@tzres.dll,-27968' + val 'Std' = s 'Montevideo Standard Time' + val 'TZI' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2004' = b b400000000000000c4ffffff0000010004000100000000000000000000000900000003000000000000000000 + val '2005' = b b400000000000000c4ffffff0000030000000500020000000000000000000a00000002000200000000000000 + val '2006' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2007' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2008' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2009' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2010' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2011' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2012' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2013' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2014' = b b400000000000000c4ffffff0000030000000200020000000000000000000a00000001000200000000000000 + val '2015' = b b400000000000000c4ffffff0000030000000200020000000000000000000100040001000000000000000000 + val '2016' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2004 + val 'LastEntry' = d 2016 + } + } + 'Morocco Standard Time' + { + val 'Display' = s '(UTC+01:00) Casablanca' + val 'Dlt' = s 'Morocco Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-2994' + val 'MUI_Dlt' = s '@tzres.dll,-2993' + val 'MUI_Std' = s '@tzres.dll,-2992' + val 'Std' = s 'Morocco Standard Time' + val 'TZI' = b 0000000000000000c4ffffff00000c0000000400030000000000000000000200000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2001' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 0000000000000000c4ffffff0000090001000100000000000000000000000600000001000000000000000000 + val '2009' = b 0000000000000000c4ffffff0000080005000300000000000000000000000600010001000000000000000000 + val '2010' = b 0000000000000000c4ffffff0000080000000200000000000000000000000500000001000000000000000000 + val '2011' = b 0000000000000000c4ffffff0000070000000500000000000000000000000400000001000000000000000000 + val '2012' = b 0000000000000000c4ffffff0000090000000500030000000000000000000400000005000200000000000000 + val '2013' = b 0000000000000000c4ffffff00000a0000000500030000000000000000000400000005000200000000000000 + val '2014' = b 0000000000000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2015' = b 0000000000000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2016' = b 0000000000000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2017' = b 0000000000000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2018' = b 0000000000000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2019' = b 0000000000000000c4ffffff0000050000000100030000000000000000000600000002000200000000000000 + val '2020' = b 0000000000000000c4ffffff0000040000000300030000000000000000000500000005000200000000000000 + val '2021' = b 0000000000000000c4ffffff0000040000000200030000000000000000000500000003000200000000000000 + val '2022' = b 0000000000000000c4ffffff0000030000000500030000000000000000000500000002000200000000000000 + val '2023' = b 0000000000000000c4ffffff0000030000000300030000000000000000000400000004000200000000000000 + val '2024' = b 0000000000000000c4ffffff0000030000000200030000000000000000000400000002000200000000000000 + val '2025' = b 0000000000000000c4ffffff0000020000000400030000000000000000000400000001000200000000000000 + val '2026' = b 0000000000000000c4ffffff0000020000000300030000000000000000000300000004000200000000000000 + val '2027' = b 0000000000000000c4ffffff0000020000000100030000000000000000000300000002000200000000000000 + val '2028' = b 0000000000000000c4ffffff0000010000000400030000000000000000000300000001000200000000000000 + val '2029' = b 0000000000000000c4ffffff0000010000000200030000000000000000000200000003000200000000000000 + val '2030' = b 0000000000000000c4ffffff00000c0000000400030000000000000000000200000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2030 + } + } + 'Mountain Standard Time' + { + val 'Display' = s '(UTC-07:00) Mountain Time (US & Canada)' + val 'Dlt' = s 'Mountain Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-34354' + val 'MUI_Dlt' = s '@tzres.dll,-34353' + val 'MUI_Std' = s '@tzres.dll,-34352' + val 'Std' = s 'Mountain Standard Time' + val 'TZI' = b a401000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b a401000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'Mountain Standard Time (Mexico)' + { + val 'Display' = s '(UTC-07:00) Chihuahua, La Paz, Mazatlan' + val 'Dlt' = s 'Mountain Daylight Time (Mexico)' + val 'MUI_Display' = s '@tzres.dll,-7250' + val 'MUI_Dlt' = s '@tzres.dll,-7249' + val 'MUI_Std' = s '@tzres.dll,-7248' + val 'Std' = s 'Mountain Standard Time (Mexico)' + val 'TZI' = b a401000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b a401000000000000c4ffffff0000090000000500020000000000000000000500000001000200000000000000 + val '2002' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2008' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2009' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2010' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2011' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2012' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2013' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2014' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2015' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2016' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2017' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2018' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2019' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2020' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2021' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2022' = b a401000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2023' = b a401000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2023 + } + } + 'Myanmar Standard Time' + { + val 'Display' = s '(UTC+06:30) Yangon (Rangoon)' + val 'Dlt' = s 'Myanmar Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-21106' + val 'MUI_Dlt' = s '@tzres.dll,-21105' + val 'MUI_Std' = s '@tzres.dll,-21104' + val 'Std' = s 'Myanmar Standard Time' + val 'TZI' = b 7afeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'N. Central Asia Standard Time' + { + val 'Display' = s '(UTC+07:00) Novosibirsk' + val 'Dlt' = s 'N. Central Asia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-30706' + val 'MUI_Dlt' = s '@tzres.dll,-30705' + val 'MUI_Std' = s '@tzres.dll,-30704' + val 'Std' = s 'N. Central Asia Standard Time' + val 'TZI' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 98feffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 98feffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 5cfeffff000000003c0000000000070000000400020000000000000000000100050001000000000000000000 + val '2017' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Namibia Standard Time' + { + val 'Display' = s '(UTC+02:00) Windhoek' + val 'Dlt' = s 'Namibia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-6210' + val 'MUI_Dlt' = s '@tzres.dll,-6209' + val 'MUI_Std' = s '@tzres.dll,-6208' + val 'Std' = s 'Namibia Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2001' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2002' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2003' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2004' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2005' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2006' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2007' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2008' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2009' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2010' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2011' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2012' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2013' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2014' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2015' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2016' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2017' = b c4ffffff00000000c4ffffff0000040000000100020000000000000000000900000001000200000000000000 + val '2018' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2018 + } + } + 'Nepal Standard Time' + { + val 'Display' = s '(UTC+05:45) Kathmandu' + val 'Dlt' = s 'Nepal Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-1218' + val 'MUI_Dlt' = s '@tzres.dll,-1217' + val 'MUI_Std' = s '@tzres.dll,-1216' + val 'Std' = s 'Nepal Standard Time' + val 'TZI' = b a7feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'New Zealand Standard Time' + { + val 'Display' = s '(UTC+12:00) Auckland, Wellington' + val 'Dlt' = s 'New Zealand Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-54898' + val 'MUI_Dlt' = s '@tzres.dll,-54897' + val 'MUI_Std' = s '@tzres.dll,-54896' + val 'Std' = s 'New Zealand Standard Time' + val 'TZI' = b 30fdffff00000000c4ffffff0000040000000100030000000000000000000900000005000200000000000000 + 'Dynamic DST' + { + val '2000' = b 30fdffff00000000c4ffffff0000030000000300030000000000000000000a00000001000200000000000000 + val '2001' = b 30fdffff00000000c4ffffff0000030000000300030000000000000000000a00000001000200000000000000 + val '2002' = b 30fdffff00000000c4ffffff0000030000000300030000000000000000000a00000001000200000000000000 + val '2003' = b 30fdffff00000000c4ffffff0000030000000300030000000000000000000a00000001000200000000000000 + val '2004' = b 30fdffff00000000c4ffffff0000030000000300030000000000000000000a00000001000200000000000000 + val '2005' = b 30fdffff00000000c4ffffff0000030000000300030000000000000000000a00000001000200000000000000 + val '2006' = b 30fdffff00000000c4ffffff0000030000000300030000000000000000000a00000001000200000000000000 + val '2007' = b 30fdffff00000000c4ffffff0000030000000300030000000000000000000900000005000200000000000000 + val '2008' = b 30fdffff00000000c4ffffff0000040000000100030000000000000000000900000005000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2008 + } + } + 'Newfoundland Standard Time' + { + val 'Display' = s '(UTC-03:30) Newfoundland' + val 'Dlt' = s 'Newfoundland Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-9506' + val 'MUI_Dlt' = s '@tzres.dll,-9505' + val 'MUI_Std' = s '@tzres.dll,-9504' + val 'Std' = s 'Newfoundland Standard Time' + val 'TZI' = b d200000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b d200000000000000c4ffffff00000a0000000500000001000000000000000400000001000000010000000000 + val '2001' = b d200000000000000c4ffffff00000a0000000500000001000000000000000400000001000000010000000000 + val '2002' = b d200000000000000c4ffffff00000a0000000500000001000000000000000400000001000000010000000000 + val '2003' = b d200000000000000c4ffffff00000a0000000500000001000000000000000400000001000000010000000000 + val '2004' = b d200000000000000c4ffffff00000a0000000500000001000000000000000400000001000000010000000000 + val '2005' = b d200000000000000c4ffffff00000a0000000500000001000000000000000400000001000000010000000000 + val '2006' = b d200000000000000c4ffffff00000a0000000500000001000000000000000400000001000000010000000000 + val '2007' = b d200000000000000c4ffffff00000b0000000100000001000000000000000300000002000000010000000000 + val '2008' = b d200000000000000c4ffffff00000b0000000100000001000000000000000300000002000000010000000000 + val '2009' = b d200000000000000c4ffffff00000b0000000100000001000000000000000300000002000000010000000000 + val '2010' = b d200000000000000c4ffffff00000b0000000100000001000000000000000300000002000000010000000000 + val '2011' = b d200000000000000c4ffffff00000b0002000100000000000000000000000300000002000000010000000000 + val '2012' = b d200000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2012 + } + } + 'Norfolk Standard Time' + { + val 'Display' = s '(UTC+11:00) Norfolk Island' + val 'Dlt' = s 'Norfolk Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-47442' + val 'MUI_Dlt' = s '@tzres.dll,-47441' + val 'MUI_Std' = s '@tzres.dll,-47440' + val 'Std' = s 'Norfolk Standard Time' + val 'TZI' = b 6cfdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + 'Dynamic DST' + { + val '2000' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2001' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 4efdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2015' = b 6cfdffff00000000e2ffffff00000a0000000100020000000000000000000100040001000000000000000000 + val '2016' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2018' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2019' = b 6cfdffff00000000c4ffffff0000010002000100000000000000000000000a00000001000200000000000000 + val '2020' = b 6cfdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2020 + } + } + 'North Asia East Standard Time' + { + val 'Display' = s '(UTC+08:00) Irkutsk' + val 'Dlt' = s 'North Asia East Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-19058' + val 'MUI_Dlt' = s '@tzres.dll,-19057' + val 'MUI_Std' = s '@tzres.dll,-19056' + val 'Std' = s 'North Asia East Standard Time' + val 'TZI' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 20feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 20feffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 20feffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'North Asia Standard Time' + { + val 'Display' = s '(UTC+07:00) Krasnoyarsk' + val 'Dlt' = s 'North Asia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-306' + val 'MUI_Dlt' = s '@tzres.dll,-305' + val 'MUI_Std' = s '@tzres.dll,-304' + val 'Std' = s 'North Asia Standard Time' + val 'TZI' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 5cfeffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 5cfeffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'North Korea Standard Time' + { + val 'Display' = s '(UTC+09:00) Pyongyang' + val 'Dlt' = s 'North Korea Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-35890' + val 'MUI_Dlt' = s '@tzres.dll,-35889' + val 'MUI_Std' = s '@tzres.dll,-35888' + val 'Std' = s 'North Korea Standard Time' + val 'TZI' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2015' = b 02feffff00000000e2ffffff0000080006000300000000000000000000000100040001000000000000000000 + val '2016' = b 02feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b 02feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2018' = b e4fdffff000000001e000000000005000500010017001e000000000000000100010001000000000000000000 + val '2019' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2015 + val 'LastEntry' = d 2019 + } + } + 'Omsk Standard Time' + { + val 'Display' = s '(UTC+06:00) Omsk' + val 'Dlt' = s 'Omsk Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-55954' + val 'MUI_Dlt' = s '@tzres.dll,-55953' + val 'MUI_Std' = s '@tzres.dll,-55952' + val 'Std' = s 'Omsk Standard Time' + val 'TZI' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 98feffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 98feffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'Pacific SA Standard Time' + { + val 'Display' = s '(UTC-04:00) Santiago' + val 'Dlt' = s 'Pacific SA Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-65010' + val 'MUI_Dlt' = s '@tzres.dll,-65009' + val 'MUI_Std' = s '@tzres.dll,-65008' + val 'Std' = s 'Pacific SA Standard Time' + val 'TZI' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000002000000000000000000 + 'Dynamic DST' + { + val '2000' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2001' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2002' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2003' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2004' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2005' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2006' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2007' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000002000000000000000000 + val '2008' = b f000000000000000c4ffffff0000030000000500000000000000000000000a00000002000000000000000000 + val '2009' = b f000000000000000c4ffffff0000030000000300000000000000000000000a00000002000000000000000000 + val '2010' = b f000000000000000c4ffffff0000040000000100000000000000000000000a00000002000000000000000000 + val '2011' = b f000000000000000c4ffffff0000050000000200000000000000000000000800000003000000000000000000 + val '2012' = b f000000000000000c4ffffff0000040000000500000000000000000000000900000001000000000000000000 + val '2013' = b f000000000000000c4ffffff0000040000000400000000000000000000000900000002000000000000000000 + val '2014' = b f000000000000000c4ffffff0000040000000400000000000000000000000900000001000000000000000000 + val '2015' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b f000000000000000c4ffffff0000050000000300000000000000000000000800000002000000000000000000 + val '2017' = b f000000000000000c4ffffff0000050000000200000000000000000000000800000002000000000000000000 + val '2018' = b f000000000000000c4ffffff0000050000000200000000000000000000000800000002000000000000000000 + val '2019' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000002000000000000000000 + val '2020' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2021' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2022' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000002000000000000000000 + val '2023' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2024' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000002000000000000000000 + val '2025' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2026' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2027' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2028' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2029' = b f000000000000000c4ffffff0000040000000200000000000000000000000900000001000000000000000000 + val '2030' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000002000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2030 + } + } + 'Pacific Standard Time' + { + val 'Display' = s '(UTC-08:00) Pacific Time (US & Canada)' + val 'Dlt' = s 'Pacific Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-11234' + val 'MUI_Dlt' = s '@tzres.dll,-11233' + val 'MUI_Std' = s '@tzres.dll,-11232' + val 'Std' = s 'Pacific Standard Time' + val 'TZI' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'Pacific Standard Time (Mexico)' + { + val 'Display' = s '(UTC-08:00) Baja California' + val 'Dlt' = s 'Pacific Daylight Time (Mexico)' + val 'MUI_Display' = s '@tzres.dll,-15586' + val 'MUI_Dlt' = s '@tzres.dll,-15585' + val 'MUI_Std' = s '@tzres.dll,-15584' + val 'Std' = s 'Pacific Standard Time (Mexico)' + val 'TZI' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2008' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2009' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2010' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2010 + } + } + 'Pakistan Standard Time' + { + val 'Display' = s '(UTC+05:00) Islamabad, Karachi' + val 'Dlt' = s 'Pakistan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-28754' + val 'MUI_Dlt' = s '@tzres.dll,-28753' + val 'MUI_Std' = s '@tzres.dll,-28752' + val 'Std' = s 'Pakistan Standard Time' + val 'TZI' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2002' = b d4feffff00000000c4ffffff00000a0000000100000000000000000000000400000001000000000000000000 + val '2003' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b d4feffff00000000c4ffffff00000b0006000100000000000000000000000600000001000000000000000000 + val '2009' = b d4feffff00000000c4ffffff00000b0000000100000000000000000000000400030003000000000000000000 + val '2010' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2002 + val 'LastEntry' = d 2010 + } + } + 'Paraguay Standard Time' + { + val 'Display' = s '(UTC-04:00) Asuncion' + val 'Dlt' = s 'Paraguay Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-50226' + val 'MUI_Dlt' = s '@tzres.dll,-50225' + val 'MUI_Std' = s '@tzres.dll,-50224' + val 'Std' = s 'Paraguay Standard Time' + val 'TZI' = b f000000000000000c4ffffff0000030000000400000000000000000000000a00000001000000000000000000 + 'Dynamic DST' + { + val '2000' = b f000000000000000c4ffffff0000030000000100000000000000000000000a00000001000000000000000000 + val '2001' = b f000000000000000c4ffffff0000030000000100000000000000000000000a00000001000000000000000000 + val '2002' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2003' = b f000000000000000c4ffffff0000040000000100000000000000000000000900000001000000000000000000 + val '2004' = b f000000000000000c4ffffff0000040000000100000000000000000000000a00000003000000000000000000 + val '2005' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2006' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2007' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2008' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2009' = b f000000000000000c4ffffff0000030000000200000000000000000000000a00000003000000000000000000 + val '2010' = b f000000000000000c4ffffff0000040000000200000000000000000000000a00000001000000000000000000 + val '2011' = b f000000000000000c4ffffff0000040000000200000000000000000000000a00000001000000000000000000 + val '2012' = b f000000000000000c4ffffff0000040000000200000000000000000000000a00000001000000000000000000 + val '2013' = b f000000000000000c4ffffff0000030000000400000000000000000000000a00000001000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2013 + } + } + 'Qyzylorda Standard Time' + { + val 'Display' = s '(UTC+05:00) Qyzylorda' + val 'Dlt' = s 'Qyzylorda Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-53762' + val 'MUI_Dlt' = s '@tzres.dll,-53761' + val 'MUI_Std' = s '@tzres.dll,-53760' + val 'Std' = s 'Qyzylorda Standard Time' + val 'TZI' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b d4feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b d4feffff00000000c4ffffff0000010004000100000000000000000000000300000005000200000000000000 + val '2005' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2015' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2018' = b d4feffff00000000c4ffffff00000c0005000300000000000000000000000100010001000000000000000000 + val '2019' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2019 + } + } + 'Romance Standard Time' + { + val 'Display' = s '(UTC+01:00) Brussels, Copenhagen, Madrid, Paris' + val 'Dlt' = s 'Romance Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-45106' + val 'MUI_Dlt' = s '@tzres.dll,-45105' + val 'MUI_Std' = s '@tzres.dll,-45104' + val 'Std' = s 'Romance Standard Time' + val 'TZI' = b c4ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + } + 'Russia Time Zone 10' + { + val 'Display' = s '(UTC+11:00) Chokurdakh' + val 'Dlt' = s 'Russia Time Zone 10' + val 'MUI_Display' = s '@tzres.dll,-59266' + val 'MUI_Dlt' = s '@tzres.dll,-59265' + val 'MUI_Std' = s '@tzres.dll,-59264' + val 'Std' = s 'Russia Time Zone 10' + val 'TZI' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 6cfdffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 6cfdffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'Russia Time Zone 11' + { + val 'Display' = s '(UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky' + val 'Dlt' = s 'Russia Time Zone 11' + val 'MUI_Display' = s '@tzres.dll,-46002' + val 'MUI_Dlt' = s '@tzres.dll,-46001' + val 'MUI_Std' = s '@tzres.dll,-46000' + val 'Std' = s 'Russia Time Zone 11' + val 'TZI' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 30fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 6cfdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 6cfdffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 30fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2012 + } + } + 'Russia Time Zone 3' + { + val 'Display' = s '(UTC+04:00) Izhevsk, Samara' + val 'Dlt' = s 'Russia Time Zone 3' + val 'MUI_Display' = s '@tzres.dll,-17922' + val 'MUI_Dlt' = s '@tzres.dll,-17921' + val 'MUI_Std' = s '@tzres.dll,-17920' + val 'Std' = s 'Russia Time Zone 3' + val 'TZI' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 10ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 4cffffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2012 + } + } + 'Russian Standard Time' + { + val 'Display' = s '(UTC+03:00) Moscow, St. Petersburg' + val 'Dlt' = s 'Russian Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-44562' + val 'MUI_Dlt' = s '@tzres.dll,-44561' + val 'MUI_Std' = s '@tzres.dll,-44560' + val 'Std' = s 'Russian Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 4cffffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 4cffffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'SA Eastern Standard Time' + { + val 'Display' = s '(UTC-03:00) Cayenne, Fortaleza' + val 'Dlt' = s 'SA Eastern Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-40658' + val 'MUI_Dlt' = s '@tzres.dll,-40657' + val 'MUI_Std' = s '@tzres.dll,-40656' + val 'Std' = s 'SA Eastern Standard Time' + val 'TZI' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'SA Pacific Standard Time' + { + val 'Display' = s '(UTC-05:00) Bogota, Lima, Quito, Rio Branco' + val 'Dlt' = s 'SA Pacific Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-49426' + val 'MUI_Dlt' = s '@tzres.dll,-49425' + val 'MUI_Std' = s '@tzres.dll,-49424' + val 'Std' = s 'SA Pacific Standard Time' + val 'TZI' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'SA Western Standard Time' + { + val 'Display' = s '(UTC-04:00) Georgetown, La Paz, Manaus, San Juan' + val 'Dlt' = s 'SA Western Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-57122' + val 'MUI_Dlt' = s '@tzres.dll,-57121' + val 'MUI_Std' = s '@tzres.dll,-57120' + val 'Std' = s 'SA Western Standard Time' + val 'TZI' = b f000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Saint Pierre Standard Time' + { + val 'Display' = s '(UTC-03:00) Saint Pierre and Miquelon' + val 'Dlt' = s 'Saint Pierre Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-5586' + val 'MUI_Dlt' = s '@tzres.dll,-5585' + val 'MUI_Std' = s '@tzres.dll,-5584' + val 'Std' = s 'Saint Pierre Standard Time' + val 'TZI' = b b400000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b b400000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b b400000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b b400000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b b400000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b b400000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b b400000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b b400000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b b400000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'Sakhalin Standard Time' + { + val 'Display' = s '(UTC+11:00) Sakhalin' + val 'Dlt' = s 'Sakhalin Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-35106' + val 'MUI_Dlt' = s '@tzres.dll,-35105' + val 'MUI_Std' = s '@tzres.dll,-35104' + val 'Std' = s 'Sakhalin Standard Time' + val 'TZI' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b a8fdffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b a8fdffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 6cfdffff000000003c0000000000030000000500020000000000000000000100050001000000000000000000 + val '2017' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Samoa Standard Time' + { + val 'Display' = s '(UTC+13:00) Samoa' + val 'Dlt' = s 'Samoa Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-14594' + val 'MUI_Dlt' = s '@tzres.dll,-14593' + val 'MUI_Std' = s '@tzres.dll,-14592' + val 'Std' = s 'Samoa Standard Time' + val 'TZI' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2001' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 9402000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 9402000000000000c4ffffff0000010005000100000000000000000000000900000005000000000000000000 + val '2011' = b 9402000000000000c4ffffff0000040006000100040000000000000000000900060005000300000000000000 + val '2012' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2013' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2014' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2015' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2016' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2017' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2018' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2019' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2020' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000900000005000300000000000000 + val '2021' = b f4fcffff00000000c4ffffff0000040000000100040000000000000000000100050001000000000000000000 + val '2022' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2022 + } + } + 'Sao Tome Standard Time' + { + val 'Display' = s '(UTC+00:00) Sao Tome' + val 'Dlt' = s 'Sao Tome Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-5986' + val 'MUI_Dlt' = s '@tzres.dll,-5985' + val 'MUI_Std' = s '@tzres.dll,-5984' + val 'Std' = s 'Sao Tome Standard Time' + val 'TZI' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2018' = b 0000000000000000c4ffffff0000010001000100000000000000000000000100010001000100000000000000 + val '2019' = b c4ffffff000000003c0000000000010002000100000000000000000000000100020001000200000000000000 + val '2020' = b 0000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2018 + val 'LastEntry' = d 2020 + } + } + 'Saratov Standard Time' + { + val 'Display' = s '(UTC+04:00) Saratov' + val 'Dlt' = s 'Saratov Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-33810' + val 'MUI_Dlt' = s '@tzres.dll,-33809' + val 'MUI_Std' = s '@tzres.dll,-33808' + val 'Std' = s 'Saratov Standard Time' + val 'TZI' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 4cffffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 4cffffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 10ffffff000000003c00000000000c0000000100020000000000000000000100050001000000000000000000 + val '2017' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'SE Asia Standard Time' + { + val 'Display' = s '(UTC+07:00) Bangkok, Hanoi, Jakarta' + val 'Dlt' = s 'SE Asia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-53730' + val 'MUI_Dlt' = s '@tzres.dll,-53729' + val 'MUI_Std' = s '@tzres.dll,-53728' + val 'Std' = s 'SE Asia Standard Time' + val 'TZI' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Singapore Standard Time' + { + val 'Display' = s '(UTC+08:00) Kuala Lumpur, Singapore' + val 'Dlt' = s 'Singapore Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-61842' + val 'MUI_Dlt' = s '@tzres.dll,-61841' + val 'MUI_Std' = s '@tzres.dll,-61840' + val 'Std' = s 'Singapore Standard Time' + val 'TZI' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'South Africa Standard Time' + { + val 'Display' = s '(UTC+02:00) Harare, Pretoria' + val 'Dlt' = s 'South Africa Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-38898' + val 'MUI_Dlt' = s '@tzres.dll,-38897' + val 'MUI_Std' = s '@tzres.dll,-38896' + val 'Std' = s 'South Africa Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'South Sudan Standard Time' + { + val 'Display' = s '(UTC+02:00) Juba' + val 'Dlt' = s 'South Sudan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-7666' + val 'MUI_Dlt' = s '@tzres.dll,-7665' + val 'MUI_Std' = s '@tzres.dll,-7664' + val 'Std' = s 'South Sudan Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff0000010006000100000000000000000000000100060003000c00000000000000 + val '2001' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2015' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2018' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2019' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2020' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2021' = b 88ffffff00000000c4ffffff0000020001000100000000000000000000000100050001000000000000000000 + val '2022' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2022 + } + } + 'Sri Lanka Standard Time' + { + val 'Display' = s '(UTC+05:30) Sri Jayawardenepura' + val 'Dlt' = s 'Sri Lanka Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-39410' + val 'MUI_Dlt' = s '@tzres.dll,-39409' + val 'MUI_Std' = s '@tzres.dll,-39408' + val 'Std' = s 'Sri Lanka Standard Time' + val 'TZI' = b b6feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2001' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b b6feffff00000000e2ffffff000004000600030000001e000000000000000100000001000000000000000000 + val '2007' = b b6feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'Sudan Standard Time' + { + val 'Display' = s '(UTC+02:00) Khartoum' + val 'Dlt' = s 'Sudan Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-9794' + val 'MUI_Dlt' = s '@tzres.dll,-9793' + val 'MUI_Std' = s '@tzres.dll,-9792' + val 'Std' = s 'Sudan Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff0000010006000100000000000000000000000100060003000c00000000000000 + val '2001' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2015' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b 88ffffff00000000c4ffffff00000b0003000100000000000000000000000100000001000000000000000000 + val '2018' = b 88ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2018 + } + } + 'Syria Standard Time' + { + val 'Display' = s '(UTC+02:00) Damascus' + val 'Dlt' = s 'Syria Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-46578' + val 'MUI_Dlt' = s '@tzres.dll,-46577' + val 'MUI_Std' = s '@tzres.dll,-46576' + val 'Std' = s 'Syria Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff00000a0000000100000000000000000000000400060001000000000000000000 + val '2001' = b 88ffffff00000000c4ffffff00000a0001000100000000000000000000000400000001000000000000000000 + val '2002' = b 88ffffff00000000c4ffffff00000a0002000100000000000000000000000400010001000000000000000000 + val '2003' = b 88ffffff00000000c4ffffff00000a0003000100000000000000000000000400020001000000000000000000 + val '2004' = b 88ffffff00000000c4ffffff00000a0005000100000000000000000000000400040001000000000000000000 + val '2005' = b 88ffffff00000000c4ffffff00000a0006000100000000000000000000000400050001000000000000000000 + val '2006' = b 88ffffff00000000c4ffffff0000090005000400000000000000000000000400060001000000000000000000 + val '2007' = b 88ffffff00000000c4ffffff00000b0005000100000000000000000000000300050005000000000000000000 + val '2008' = b 88ffffff00000000c4ffffff00000b0006000100000000000000000000000400050001000000000000000000 + val '2009' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2010' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000400050001000000000000000000 + val '2011' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000400050001000000000000000000 + val '2012' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2013' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2014' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2015' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2016' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2017' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2018' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2019' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2020' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2021' = b 88ffffff00000000c4ffffff00000a0005000500000000000000000000000300050005000000000000000000 + val '2022' = b 88ffffff00000000c4ffffff0000010006000100000000000000000000000300050005000000000000000000 + val '2023' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2023 + } + } + 'Taipei Standard Time' + { + val 'Display' = s '(UTC+08:00) Taipei' + val 'Dlt' = s 'Taipei Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-26434' + val 'MUI_Dlt' = s '@tzres.dll,-26433' + val 'MUI_Std' = s '@tzres.dll,-26432' + val 'Std' = s 'Taipei Standard Time' + val 'TZI' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Tasmania Standard Time' + { + val 'Display' = s '(UTC+10:00) Hobart' + val 'Dlt' = s 'Tasmania Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-36018' + val 'MUI_Dlt' = s '@tzres.dll,-36017' + val 'MUI_Std' = s '@tzres.dll,-36016' + val 'Std' = s 'Tasmania Standard Time' + val 'TZI' = b a8fdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + 'Dynamic DST' + { + val '2000' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000800000005000200000000000000 + val '2001' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000001000200000000000000 + val '2002' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000001000200000000000000 + val '2003' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000001000200000000000000 + val '2004' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000001000200000000000000 + val '2005' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000001000200000000000000 + val '2006' = b a8fdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + val '2007' = b a8fdffff00000000c4ffffff0000030000000500030000000000000000000a00000001000200000000000000 + val '2008' = b a8fdffff00000000c4ffffff0000040000000100030000000000000000000a00000001000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2008 + } + } + 'Tocantins Standard Time' + { + val 'Display' = s '(UTC-03:00) Araguaina' + val 'Dlt' = s 'Tocantins Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-43074' + val 'MUI_Dlt' = s '@tzres.dll,-43073' + val 'MUI_Std' = s '@tzres.dll,-43072' + val 'Std' = s 'Tocantins Standard Time' + val 'TZI' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b b400000000000000c4ffffff0000020000000500000000000000000000000a00000002000000000000000000 + val '2001' = b b400000000000000c4ffffff0000020000000300000000000000000000000a00000002000000000000000000 + val '2002' = b b400000000000000c4ffffff0000020000000300000000000000000000000b00000001000000000000000000 + val '2003' = b b400000000000000c4ffffff0000020000000300000000000000000000000100030001000000000000000000 + val '2004' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b b400000000000000c4ffffff0000010000000100000000000000000000000a00000003000000000000000000 + val '2013' = b b400000000000000c4ffffff0000020000000300000000000000000000000100020001000000000000000000 + val '2014' = b b400000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2014 + } + } + 'Tokyo Standard Time' + { + val 'Display' = s '(UTC+09:00) Osaka, Sapporo, Tokyo' + val 'Dlt' = s 'Tokyo Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-16242' + val 'MUI_Dlt' = s '@tzres.dll,-16241' + val 'MUI_Std' = s '@tzres.dll,-16240' + val 'Std' = s 'Tokyo Standard Time' + val 'TZI' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Tomsk Standard Time' + { + val 'Display' = s '(UTC+07:00) Tomsk' + val 'Dlt' = s 'Tomsk Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-29138' + val 'MUI_Dlt' = s '@tzres.dll,-29137' + val 'MUI_Std' = s '@tzres.dll,-29136' + val 'Std' = s 'Tomsk Standard Time' + val 'TZI' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 5cfeffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 5cfeffff000000003c00000000000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 98feffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 98feffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 98feffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 98feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 5cfeffff000000003c0000000000050000000500020000000000000000000100050001000000000000000000 + val '2017' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Tonga Standard Time' + { + val 'Display' = s '(UTC+13:00) Nuku''alofa' + val 'Dlt' = s 'Tonga Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-7218' + val 'MUI_Dlt' = s '@tzres.dll,-7217' + val 'MUI_Std' = s '@tzres.dll,-7216' + val 'Std' = s 'Tonga Standard Time' + val 'TZI' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b f4fcffff00000000c4ffffff0000030000000300030000000000000000000b00000001000200000000000000 + val '2001' = b f4fcffff00000000c4ffffff0000010000000500020000000000000000000b00000001000200000000000000 + val '2002' = b f4fcffff00000000c4ffffff0000010000000500020000000000000000000100020001000000000000000000 + val '2003' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2007' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2015' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b f4fcffff00000000c4ffffff0000010005000100000000000000000000000b00000001000200000000000000 + val '2017' = b f4fcffff00000000c4ffffff0000010000000300030000000000000000000100000001000000000000000000 + val '2018' = b f4fcffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2018 + } + } + 'Transbaikal Standard Time' + { + val 'Display' = s '(UTC+09:00) Chita' + val 'Dlt' = s 'Transbaikal Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-31474' + val 'MUI_Dlt' = s '@tzres.dll,-31473' + val 'MUI_Std' = s '@tzres.dll,-31472' + val 'Std' = s 'Transbaikal Standard Time' + val 'TZI' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b e4fdffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 20feffff0000000088ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b e4fdffff000000003c0000000000030000000500020000000000000000000100050001000000000000000000 + val '2017' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Turkey Standard Time' + { + val 'Display' = s '(UTC+03:00) Istanbul' + val 'Dlt' = s 'Turkey Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-24850' + val 'MUI_Dlt' = s '@tzres.dll,-24849' + val 'MUI_Std' = s '@tzres.dll,-24848' + val 'Std' = s 'Turkey Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300000005000100000000000000 + val '2001' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300000005000100000000000000 + val '2002' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300000005000100000000000000 + val '2003' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300000005000100000000000000 + val '2004' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300000005000100000000000000 + val '2005' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300000005000100000000000000 + val '2006' = b 88ffffff00000000c4ffffff00000a0000000500020000000000000000000300000005000100000000000000 + val '2007' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + val '2008' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + val '2009' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + val '2010' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + val '2011' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + val '2012' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + val '2013' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + val '2014' = b 88ffffff00000000c4ffffff00000a0000000500040000000000000000000300000005000300000000000000 + val '2015' = b 88ffffff00000000c4ffffff00000b0000000200040000000000000000000300000005000300000000000000 + val '2016' = b 88ffffff00000000c4ffffff0000010005000100000000000000000000000300000005000300000000000000 + val '2017' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2017 + } + } + 'Turks And Caicos Standard Time' + { + val 'Display' = s '(UTC-05:00) Turks and Caicos' + val 'Dlt' = s 'Turks And Caicos Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-20562' + val 'MUI_Dlt' = s '@tzres.dll,-20561' + val 'MUI_Std' = s '@tzres.dll,-20560' + val 'Std' = s 'Turks And Caicos Standard Time' + val 'TZI' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2008' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2009' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2010' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2011' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2012' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2013' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2014' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2015' = b 2c01000000000000c4ffffff0000010004000100000000000000000000000300000002000200000000000000 + val '2016' = b f000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b f000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2018' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000100010001000000000000000000 + val '2019' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2019 + } + } + 'Ulaanbaatar Standard Time' + { + val 'Display' = s '(UTC+08:00) Ulaanbaatar' + val 'Dlt' = s 'Ulaanbaatar Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-47202' + val 'MUI_Dlt' = s '@tzres.dll,-47201' + val 'MUI_Std' = s '@tzres.dll,-47200' + val 'Std' = s 'Ulaanbaatar Standard Time' + val 'TZI' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2001' = b 20feffff00000000c4ffffff0000090006000500020000000000000000000400060005000200000000000000 + val '2002' = b 20feffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2003' = b 20feffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2004' = b 20feffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2005' = b 20feffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2006' = b 20feffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2007' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2015' = b 20feffff00000000c4ffffff0000090006000500000000000000000000000300060005000200000000000000 + val '2016' = b 20feffff00000000c4ffffff0000090006000500000000000000000000000300060005000200000000000000 + val '2017' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2001 + val 'LastEntry' = d 2017 + } + } + 'US Eastern Standard Time' + { + val 'Display' = s '(UTC-05:00) Indiana (East)' + val 'Dlt' = s 'US Eastern Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-34930' + val 'MUI_Dlt' = s '@tzres.dll,-34929' + val 'MUI_Std' = s '@tzres.dll,-34928' + val 'Std' = s 'US Eastern Standard Time' + val 'TZI' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + 'Dynamic DST' + { + val '2000' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2001' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2002' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2003' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2004' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2005' = b 2c01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2006' = b 2c01000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b 2c01000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2007 + } + } + 'US Mountain Standard Time' + { + val 'Display' = s '(UTC-07:00) Arizona' + val 'Dlt' = s 'US Mountain Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-37474' + val 'MUI_Dlt' = s '@tzres.dll,-37473' + val 'MUI_Std' = s '@tzres.dll,-37472' + val 'Std' = s 'US Mountain Standard Time' + val 'TZI' = b a401000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + UTC + { + val 'Display' = s '(UTC) Coordinated Universal Time' + val 'Dlt' = s 'Coordinated Universal Time' + val 'MUI_Display' = s '@tzres.dll,-22002' + val 'MUI_Dlt' = s '@tzres.dll,-22001' + val 'MUI_Std' = s '@tzres.dll,-22000' + val 'Std' = s 'Coordinated Universal Time' + val 'TZI' = b 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + } + UTC+12 + { + val 'Display' = s '(UTC+12:00) Coordinated Universal Time+12' + val 'Dlt' = s 'UTC+12' + val 'MUI_Display' = s '@tzres.dll,-1202' + val 'MUI_Dlt' = s '@tzres.dll,-1201' + val 'MUI_Std' = s '@tzres.dll,-1200' + val 'Std' = s 'UTC+12' + val 'TZI' = b 30fdffff00000000000000000000000000000000000000000000000000000000000000000000000000000000 + } + UTC+13 + { + val 'Display' = s '(UTC+13:00) Coordinated Universal Time+13' + val 'Dlt' = s 'UTC+13' + val 'MUI_Display' = s '@tzres.dll,-8786' + val 'MUI_Dlt' = s '@tzres.dll,-8785' + val 'MUI_Std' = s '@tzres.dll,-8784' + val 'Std' = s 'UTC+13' + val 'TZI' = b f4fcffff00000000000000000000000000000000000000000000000000000000000000000000000000000000 + } + UTC-02 + { + val 'Display' = s '(UTC-02:00) Coordinated Universal Time-02' + val 'Dlt' = s 'UTC-02' + val 'MUI_Display' = s '@tzres.dll,-37330' + val 'MUI_Dlt' = s '@tzres.dll,-37329' + val 'MUI_Std' = s '@tzres.dll,-37328' + val 'Std' = s 'UTC-02' + val 'TZI' = b 7800000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + } + UTC-08 + { + val 'Display' = s '(UTC-08:00) Coordinated Universal Time-08' + val 'Dlt' = s 'UTC-08' + val 'MUI_Display' = s '@tzres.dll,-3346' + val 'MUI_Dlt' = s '@tzres.dll,-3345' + val 'MUI_Std' = s '@tzres.dll,-3344' + val 'Std' = s 'UTC-08' + val 'TZI' = b e001000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + } + UTC-09 + { + val 'Display' = s '(UTC-09:00) Coordinated Universal Time-09' + val 'Dlt' = s 'UTC-09' + val 'MUI_Display' = s '@tzres.dll,-39154' + val 'MUI_Dlt' = s '@tzres.dll,-39153' + val 'MUI_Std' = s '@tzres.dll,-39152' + val 'Std' = s 'UTC-09' + val 'TZI' = b 1c02000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + } + UTC-11 + { + val 'Display' = s '(UTC-11:00) Coordinated Universal Time-11' + val 'Dlt' = s 'UTC-11' + val 'MUI_Display' = s '@tzres.dll,-19410' + val 'MUI_Dlt' = s '@tzres.dll,-19409' + val 'MUI_Std' = s '@tzres.dll,-19408' + val 'Std' = s 'UTC-11' + val 'TZI' = b 9402000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + } + 'Venezuela Standard Time' + { + val 'Display' = s '(UTC-04:00) Caracas' + val 'Dlt' = s 'Venezuela Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-51474' + val 'MUI_Dlt' = s '@tzres.dll,-51473' + val 'MUI_Std' = s '@tzres.dll,-51472' + val 'Std' = s 'Venezuela Standard Time' + val 'TZI' = b f000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2007' = b 0e01000000000000e2ffffff00000c0000000200030000000000000000000100010001000000000000000000 + val '2008' = b 0e01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 0e01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 0e01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b 0e01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 0e01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 0e01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 0e01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2015' = b 0e01000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b f0000000000000001e000000000005000000010002001e000000000000000100050001000000000000000000 + val '2017' = b f000000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2007 + val 'LastEntry' = d 2017 + } + } + 'Vladivostok Standard Time' + { + val 'Display' = s '(UTC+10:00) Vladivostok' + val 'Dlt' = s 'Vladivostok Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-61378' + val 'MUI_Dlt' = s '@tzres.dll,-61377' + val 'MUI_Std' = s '@tzres.dll,-61376' + val 'Std' = s 'Vladivostok Standard Time' + val 'TZI' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b a8fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b a8fdffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 6cfdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b a8fdffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'Volgograd Standard Time' + { + val 'Display' = s '(UTC+04:00) Volgograd' + val 'Dlt' = s 'Volgograd Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-18050' + val 'MUI_Dlt' = s '@tzres.dll,-18049' + val 'MUI_Std' = s '@tzres.dll,-18048' + val 'Std' = s 'Volgograd Standard Time' + val 'TZI' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b 4cffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b 4cffffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 4cffffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2016' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2017' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2018' = b 10ffffff000000003c00000000000a0000000500020000000000000000000100010001000000000000000000 + val '2019' = b 10ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2020' = b 4cffffff00000000c4ffffff00000c0000000500020000000000000000000100030001000000000000000000 + val '2021' = b 4cffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2021 + } + } + 'W. Australia Standard Time' + { + val 'Display' = s '(UTC+08:00) Perth' + val 'Dlt' = s 'W. Australia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-65378' + val 'MUI_Dlt' = s '@tzres.dll,-65377' + val 'MUI_Std' = s '@tzres.dll,-65376' + val 'Std' = s 'W. Australia Standard Time' + val 'TZI' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2006' = b 20feffff00000000c4ffffff0000010000000100000000000000000000000c00000001000200000000000000 + val '2007' = b 20feffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2008' = b 20feffff00000000c4ffffff0000030000000500030000000000000000000a00000005000200000000000000 + val '2009' = b 20feffff00000000c4ffffff0000030000000500030000000000000000000100040001000000000000000000 + val '2010' = b 20feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2006 + val 'LastEntry' = d 2010 + } + } + 'W. Central Africa Standard Time' + { + val 'Display' = s '(UTC+01:00) West Central Africa' + val 'Dlt' = s 'W. Central Africa Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-64978' + val 'MUI_Dlt' = s '@tzres.dll,-64977' + val 'MUI_Std' = s '@tzres.dll,-64976' + val 'Std' = s 'W. Central Africa Standard Time' + val 'TZI' = b c4ffffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'W. Europe Standard Time' + { + val 'Display' = s '(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' + val 'Dlt' = s 'W. Europe Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-27698' + val 'MUI_Dlt' = s '@tzres.dll,-27697' + val 'MUI_Std' = s '@tzres.dll,-27696' + val 'Std' = s 'W. Europe Standard Time' + val 'TZI' = b c4ffffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + } + 'W. Mongolia Standard Time' + { + val 'Display' = s '(UTC+07:00) Hovd' + val 'Dlt' = s 'W. Mongolia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-53954' + val 'MUI_Dlt' = s '@tzres.dll,-53953' + val 'MUI_Std' = s '@tzres.dll,-53952' + val 'Std' = s 'W. Mongolia Standard Time' + val 'TZI' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2001' = b 5cfeffff00000000c4ffffff0000090006000500020000000000000000000400060005000200000000000000 + val '2002' = b 5cfeffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2003' = b 5cfeffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2004' = b 5cfeffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2005' = b 5cfeffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2006' = b 5cfeffff00000000c4ffffff0000090006000500020000000000000000000300060005000200000000000000 + val '2007' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2008' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2009' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2010' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2011' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2012' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2015' = b 5cfeffff00000000c4ffffff0000090006000500000000000000000000000300060005000200000000000000 + val '2016' = b 5cfeffff00000000c4ffffff0000090006000500000000000000000000000300060005000200000000000000 + val '2017' = b 5cfeffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2001 + val 'LastEntry' = d 2017 + } + } + 'West Asia Standard Time' + { + val 'Display' = s '(UTC+05:00) Ashgabat, Tashkent' + val 'Dlt' = s 'West Asia Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-49666' + val 'MUI_Dlt' = s '@tzres.dll,-49665' + val 'MUI_Std' = s '@tzres.dll,-49664' + val 'Std' = s 'West Asia Standard Time' + val 'TZI' = b d4feffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'West Bank Standard Time' + { + val 'Display' = s '(UTC+02:00) Gaza, Hebron' + val 'Dlt' = s 'West Bank Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-46162' + val 'MUI_Dlt' = s '@tzres.dll,-46161' + val 'MUI_Std' = s '@tzres.dll,-46160' + val 'Std' = s 'West Bank Standard Time' + val 'TZI' = b 88ffffff00000000c4ffffff00000a0006000400020000000000000000000300060005000200000000000000 + 'Dynamic DST' + { + val '2000' = b 88ffffff00000000c4ffffff00000a0005000300000000000000000000000400050003000000000000000000 + val '2001' = b 88ffffff00000000c4ffffff00000a0005000300000000000000000000000400050003000000000000000000 + val '2002' = b 88ffffff00000000c4ffffff00000a0005000300000000000000000000000400050003000000000000000000 + val '2003' = b 88ffffff00000000c4ffffff00000a0005000300000000000000000000000400050003000000000000000000 + val '2004' = b 88ffffff00000000c4ffffff00000a0005000100010000000000000000000400050003000000000000000000 + val '2005' = b 88ffffff00000000c4ffffff00000a0002000100020000000000000000000400050003000000000000000000 + val '2006' = b 88ffffff00000000c4ffffff0000090005000400000000000000000000000400060001000000000000000000 + val '2007' = b 88ffffff00000000c4ffffff0000090004000200020000000000000000000400000001000000000000000000 + val '2008' = b 88ffffff00000000c4ffffff0000090001000100000000000000000000000300050005000000000000000000 + val '2009' = b 88ffffff00000000c4ffffff0000090005000100010000000000000000000300050005000000000000000000 + val '2010' = b 88ffffff00000000c4ffffff0000080003000200000000000000000000000300050005000000000000000000 + val '2011' = b 88ffffff00000000c4ffffff0000090005000500000000000000000000000400050001000000010000000000 + val '2012' = b 88ffffff00000000c4ffffff00000900050003000100000000000000000003000400050017003b003b00e703 + val '2013' = b 88ffffff00000000c4ffffff00000900050005000000000000000000000003000400050017003b003b00e703 + val '2014' = b 88ffffff00000000c4ffffff00000a00050004000000000000000000000003000400050017003b003b00e703 + val '2015' = b 88ffffff00000000c4ffffff00000a0005000400010000000000000000000300060005000000000000000000 + val '2016' = b 88ffffff00000000c4ffffff00000a0006000500010000000000000000000300060004000100000000000000 + val '2017' = b 88ffffff00000000c4ffffff00000a0006000400010000000000000000000300060004000100000000000000 + val '2018' = b 88ffffff00000000c4ffffff00000a0006000400010000000000000000000300060004000100000000000000 + val '2019' = b 88ffffff00000000c4ffffff00000a0006000400000000000000000000000300050005000000000000000000 + val '2020' = b 88ffffff00000000c4ffffff00000a0006000400010000000000000000000300060004000000000000000000 + val '2021' = b 88ffffff00000000c4ffffff00000a0005000500010000000000000000000300060004000000000000000000 + val '2022' = b 88ffffff00000000c4ffffff00000a0006000500020000000000000000000300000005000000000000000000 + val '2023' = b 88ffffff00000000c4ffffff00000a0006000400020000000000000000000400060005000200000000000000 + val '2024' = b 88ffffff00000000c4ffffff00000a0006000400020000000000000000000400060003000200000000000000 + val '2025' = b 88ffffff00000000c4ffffff00000a0006000400020000000000000000000400060002000200000000000000 + val '2026' = b 88ffffff00000000c4ffffff00000a0006000400020000000000000000000300060004000200000000000000 + val '2027' = b 88ffffff00000000c4ffffff00000a0006000500020000000000000000000300060004000200000000000000 + val '2028' = b 88ffffff00000000c4ffffff00000a0006000400020000000000000000000300060004000200000000000000 + val '2029' = b 88ffffff00000000c4ffffff00000a0006000400020000000000000000000300060004000200000000000000 + val '2030' = b 88ffffff00000000c4ffffff00000a0006000400020000000000000000000300060005000200000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2030 + } + } + 'West Pacific Standard Time' + { + val 'Display' = s '(UTC+10:00) Guam, Port Moresby' + val 'Dlt' = s 'West Pacific Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-10226' + val 'MUI_Dlt' = s '@tzres.dll,-10225' + val 'MUI_Std' = s '@tzres.dll,-10224' + val 'Std' = s 'West Pacific Standard Time' + val 'TZI' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + } + 'Yakutsk Standard Time' + { + val 'Display' = s '(UTC+09:00) Yakutsk' + val 'Dlt' = s 'Yakutsk Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-40578' + val 'MUI_Dlt' = s '@tzres.dll,-40577' + val 'MUI_Std' = s '@tzres.dll,-40576' + val 'Std' = s 'Yakutsk Standard Time' + val 'TZI' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2001' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2002' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2003' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2004' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2005' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2006' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2007' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2008' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2009' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2010' = b e4fdffff00000000c4ffffff00000a0000000500030000000000000000000300000005000200000000000000 + val '2011' = b e4fdffff00000000c4ffffff0000010006000100000000000000000000000300000005000200000000000000 + val '2012' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2013' = b a8fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val '2014' = b e4fdffff00000000c4ffffff00000a0000000500020000000000000000000100030001000000000000000000 + val '2015' = b e4fdffff00000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2015 + } + } + 'Yukon Standard Time' + { + val 'Display' = s '(UTC-07:00) Yukon' + val 'Dlt' = s 'Yukon Daylight Time' + val 'MUI_Display' = s '@tzres.dll,-26290' + val 'MUI_Dlt' = s '@tzres.dll,-26289' + val 'MUI_Std' = s '@tzres.dll,-26288' + val 'Std' = s 'Yukon Standard Time' + val 'TZI' = b a401000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + 'Dynamic DST' + { + val '2000' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2001' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2002' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2003' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2004' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2005' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2006' = b e001000000000000c4ffffff00000a0000000500020000000000000000000400000001000200000000000000 + val '2007' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2008' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2009' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2010' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2011' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2012' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2013' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2014' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2015' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2016' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2017' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2018' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2019' = b e001000000000000c4ffffff00000b0000000100020000000000000000000300000002000200000000000000 + val '2020' = b e001000000000000c4ffffff0000010003000100000000000000000000000300000002000200000000000000 + val '2021' = b a401000000000000c4ffffff0000000000000000000000000000000000000000000000000000000000000000 + val 'FirstEntry' = d 2000 + val 'LastEntry' = d 2021 + } + } + } + } + } + } + } + NoRemove SYSTEM + { + NoRemove CurrentControlSet + { + NoRemove Control + { + NoRemove Nls + { + Codepage + { + val '10000' = s 'c_10000.nls' + val '10001' = s 'c_10001.nls' + val '10002' = s 'c_10002.nls' + val '10003' = s 'c_10003.nls' + val '10004' = s 'c_10004.nls' + val '10005' = s 'c_10005.nls' + val '10006' = s 'c_10006.nls' + val '10007' = s 'c_10007.nls' + val '10008' = s 'c_10008.nls' + val '10010' = s 'c_10010.nls' + val '10017' = s 'c_10017.nls' + val '10021' = s 'c_10021.nls' + val '10029' = s 'c_10029.nls' + val '10079' = s 'c_10079.nls' + val '10081' = s 'c_10081.nls' + val '10082' = s 'c_10082.nls' + val '1026' = s 'c_1026.nls' + val '1250' = s 'c_1250.nls' + val '1251' = s 'c_1251.nls' + val '1252' = s 'c_1252.nls' + val '1253' = s 'c_1253.nls' + val '1254' = s 'c_1254.nls' + val '1255' = s 'c_1255.nls' + val '1256' = s 'c_1256.nls' + val '1257' = s 'c_1257.nls' + val '1258' = s 'c_1258.nls' + val '1361' = s 'c_1361.nls' + val '20127' = s 'c_20127.nls' + val '20866' = s 'c_20866.nls' + val '20932' = s 'c_20932.nls' + val '20949' = s 'c_20949.nls' + val '21866' = s 'c_21866.nls' + val '28591' = s 'c_28591.nls' + val '28592' = s 'c_28592.nls' + val '28593' = s 'c_28593.nls' + val '28594' = s 'c_28594.nls' + val '28595' = s 'c_28595.nls' + val '28596' = s 'c_28596.nls' + val '28597' = s 'c_28597.nls' + val '28598' = s 'c_28598.nls' + val '28599' = s 'c_28599.nls' + val '28603' = s 'c_28603.nls' + val '28605' = s 'c_28605.nls' + val '37' = s 'c_037.nls' + val '437' = s 'c_437.nls' + val '500' = s 'c_500.nls' + val '708' = s 'c_708.nls' + val '720' = s 'c_720.nls' + val '737' = s 'c_737.nls' + val '775' = s 'c_775.nls' + val '850' = s 'c_850.nls' + val '852' = s 'c_852.nls' + val '855' = s 'c_855.nls' + val '857' = s 'c_857.nls' + val '860' = s 'c_860.nls' + val '861' = s 'c_861.nls' + val '862' = s 'c_862.nls' + val '863' = s 'c_863.nls' + val '864' = s 'c_864.nls' + val '865' = s 'c_865.nls' + val '866' = s 'c_866.nls' + val '869' = s 'c_869.nls' + val '874' = s 'c_874.nls' + val '875' = s 'c_875.nls' + val '932' = s 'c_932.nls' + val '936' = s 'c_936.nls' + val '949' = s 'c_949.nls' + val '950' = s 'c_950.nls' + } + 'Language Groups' + { + val '1' = s '1' + val '10' = s '1' + val '11' = s '1' + val '2' = s '1' + val '3' = s '1' + val '4' = s '1' + val '5' = s '1' + val '6' = s '1' + val '7' = s '1' + val '8' = s '1' + val '9' = s '1' + val 'a' = s '1' + val 'b' = s '1' + val 'c' = s '1' + val 'd' = s '1' + val 'e' = s '1' + val 'f' = s '1' + } + Locale = s '00000409' + { + val '00000401' = s 'd' + val '00000402' = s '5' + val '00000403' = s '1' + val '00000404' = s '9' + val '00000405' = s '2' + val '00000406' = s '1' + val '00000407' = s '1' + val '00000408' = s '4' + val '00000409' = s '1' + val '0000040a' = s '1' + val '0000040b' = s '1' + val '0000040c' = s '1' + val '0000040d' = s 'c' + val '0000040e' = s '2' + val '0000040f' = s '1' + val '00000410' = s '1' + val '00000411' = s '7' + val '00000412' = s '8' + val '00000413' = s '1' + val '00000414' = s '1' + val '00000415' = s '2' + val '00000416' = s '1' + val '00000417' = s '1' + val '00000418' = s '2' + val '00000419' = s '5' + val '0000041a' = s '2' + val '0000041b' = s '2' + val '0000041c' = s '2' + val '0000041d' = s '1' + val '0000041e' = s 'b' + val '0000041f' = s '6' + val '00000420' = s 'd' + val '00000421' = s '1' + val '00000422' = s '5' + val '00000423' = s '5' + val '00000424' = s '2' + val '00000425' = s '3' + val '00000426' = s '3' + val '00000427' = s '3' + val '00000428' = s '5' + val '00000429' = s 'd' + val '0000042a' = s 'e' + val '0000042b' = s '11' + val '0000042c' = s '2' + val '0000042d' = s '1' + val '0000042e' = s '1' + val '0000042f' = s '5' + val '00000430' = s '1' + val '00000431' = s '1' + val '00000432' = s '1' + val '00000433' = s '1' + val '00000434' = s '1' + val '00000435' = s '1' + val '00000436' = s '1' + val '00000437' = s '10' + val '00000438' = s '1' + val '00000439' = s 'f' + val '0000043a' = s '1' + val '0000043b' = s '1' + val '0000043d' = s '1' + val '0000043e' = s '1' + val '0000043f' = s '5' + val '00000440' = s '5' + val '00000441' = s '1' + val '00000442' = s '2' + val '00000443' = s '2' + val '00000444' = s '5' + val '00000445' = s 'f' + val '00000446' = s 'f' + val '00000447' = s 'f' + val '00000448' = s 'f' + val '00000449' = s 'f' + val '0000044a' = s 'f' + val '0000044b' = s 'f' + val '0000044c' = s 'f' + val '0000044d' = s 'f' + val '0000044e' = s 'f' + val '0000044f' = s 'f' + val '00000450' = s '5' + val '00000451' = s 'f' + val '00000452' = s '1' + val '00000453' = s 'f' + val '00000454' = s 'f' + val '00000455' = s '1' + val '00000456' = s '1' + val '00000457' = s 'f' + val '00000458' = s '1' + val '00000459' = s 'f' + val '0000045a' = s 'd' + val '0000045b' = s 'f' + val '0000045c' = s '1' + val '0000045d' = s '1' + val '0000045e' = s '1' + val '00000461' = s 'f' + val '00000462' = s '1' + val '00000463' = s 'd' + val '00000464' = s '1' + val '00000465' = s 'd' + val '00000466' = s '1' + val '00000467' = s '1' + val '00000468' = s '1' + val '0000046a' = s '1' + val '0000046b' = s '1' + val '0000046c' = s '1' + val '0000046d' = s '5' + val '0000046e' = s '1' + val '0000046f' = s '1' + val '00000470' = s '1' + val '00000471' = s '1' + val '00000472' = s '1' + val '00000473' = s '1' + val '00000474' = s '1' + val '00000475' = s '1' + val '00000476' = s '1' + val '00000477' = s '1' + val '00000478' = s '9' + val '0000047a' = s '1' + val '0000047c' = s '1' + val '0000047e' = s '1' + val '00000480' = s 'd' + val '00000481' = s '1' + val '00000482' = s '1' + val '00000483' = s '1' + val '00000484' = s '1' + val '00000485' = s '5' + val '00000486' = s '1' + val '00000487' = s '1' + val '00000488' = s '1' + val '0000048c' = s 'd' + val '00000491' = s '1' + val '00000492' = s 'd' + val '00000801' = s 'd' + val '00000803' = s '1' + val '00000804' = s 'a' + val '00000807' = s '1' + val '00000809' = s '1' + val '0000080a' = s '1' + val '0000080c' = s '1' + val '00000810' = s '1' + val '00000813' = s '1' + val '00000814' = s '1' + val '00000816' = s '1' + val '00000818' = s '1' + val '00000819' = s '1' + val '0000081d' = s '1' + val '00000820' = s '1' + val '0000082c' = s '5' + val '0000082e' = s '1' + val '00000832' = s '1' + val '0000083b' = s '1' + val '0000083c' = s '1' + val '0000083e' = s '1' + val '00000843' = s '5' + val '00000845' = s 'f' + val '00000846' = s 'd' + val '00000849' = s 'f' + val '00000850' = s 'f' + val '00000859' = s 'd' + val '0000085d' = s '1' + val '0000085f' = s '1' + val '00000860' = s 'f' + val '00000861' = s '1' + val '00000867' = s '1' + val '0000086b' = s '1' + val '00000873' = s '1' + val '00000c01' = s 'd' + val '00000c04' = s '9' + val '00000c07' = s '1' + val '00000c09' = s '1' + val '00000c0a' = s '1' + val '00000c0c' = s '1' + val '00000c3b' = s '1' + val '00000c50' = s 'f' + val '00000c51' = s '1' + val '00000c6b' = s '1' + val '00001001' = s 'd' + val '00001004' = s 'a' + val '00001007' = s '1' + val '00001009' = s '1' + val '0000100a' = s '1' + val '0000100c' = s '1' + val '0000101a' = s '2' + val '0000103b' = s '1' + val '00001401' = s 'd' + val '00001404' = s '9' + val '00001407' = s '1' + val '00001409' = s '1' + val '0000140a' = s '1' + val '0000140c' = s '1' + val '0000141a' = s '2' + val '0000143b' = s '1' + val '00001801' = s 'd' + val '00001809' = s '1' + val '0000180a' = s '1' + val '0000180c' = s '1' + val '0000181a' = s '2' + val '0000183b' = s '1' + val '00001c01' = s 'd' + val '00001c09' = s '1' + val '00001c0a' = s '1' + val '00001c0c' = s '1' + val '00001c1a' = s '5' + val '00001c3b' = s '1' + val '00002001' = s 'd' + val '00002009' = s '1' + val '0000200a' = s '1' + val '0000200c' = s '1' + val '0000201a' = s '5' + val '0000203b' = s '1' + val '00002401' = s 'd' + val '00002409' = s '1' + val '0000240a' = s '1' + val '0000240c' = s '1' + val '0000241a' = s '2' + val '0000243b' = s '1' + val '00002801' = s 'd' + val '00002809' = s '1' + val '0000280a' = s '1' + val '0000280c' = s '1' + val '0000281a' = s '5' + val '00002c01' = s 'd' + val '00002c09' = s '1' + val '00002c0a' = s '1' + val '00002c0c' = s '1' + val '00002c1a' = s '2' + val '00003001' = s 'd' + val '00003009' = s '1' + val '0000300a' = s '1' + val '0000300c' = s '1' + val '0000301a' = s '5' + val '00003401' = s 'd' + val '00003409' = s '1' + val '0000340a' = s '1' + val '0000340c' = s '1' + val '00003801' = s 'd' + val '00003809' = s '1' + val '0000380a' = s '1' + val '0000380c' = s '1' + val '00003c01' = s 'd' + val '00003c09' = s '1' + val '00003c0a' = s '1' + val '00003c0c' = s '1' + val '00004001' = s 'd' + val '00004009' = s '1' + val '0000400a' = s '1' + val '00004409' = s '1' + val '0000440a' = s '1' + val '00004809' = s '1' + val '0000480a' = s '1' + val '00004c09' = s '1' + val '00004c0a' = s '1' + val '0000500a' = s '1' + val '0000540a' = s '1' + val '0000580a' = s '1' + val '00005c0a' = s '1' + 'Alternate Sorts' + { + val '0000040a' = s '1' + val '0001007f' = s '1' + val '00010407' = s '1' + val '0001040e' = s '2' + val '00010437' = s '10' + val '00020804' = s 'a' + val '00021004' = s 'a' + val '00021404' = s '9' + val '00030404' = s '9' + val '00040404' = s '9' + val '00040411' = s '7' + val '00040c04' = s '9' + val '00041404' = s '9' + val '00050804' = s 'a' + val '00051004' = s 'a' + } + } + Normalization + { + val '1' = s 'normnfc.nls' + val '2' = s 'normnfd.nls' + val '5' = s 'normnfkc.nls' + val '6' = s 'normnfkd.nls' + val 'd' = s 'normidna.nls' + } + Sorting + { + Ids = s '{00000001-57ee-1e5c-00b4-d0000bb1e11e}' + { + val 'arn' = s '{00000012-57ee-1e5c-00b4-d0000bb1e11e}' + val 'as' = s '{00000031-57ee-1e5c-00b4-d0000bb1e11e}' + val 'az' = s '{00000023-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ba' = s '{0000003d-57ee-1e5c-00b4-d0000bb1e11e}' + val 'bg' = s '{0000004a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'bn' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'bo' = s '{00000034-57ee-1e5c-00b4-d0000bb1e11e}' + val 'br' = s '{0000000f-57ee-1e5c-00b4-d0000bb1e11e}' + val 'bs' = s '{00000019-57ee-1e5c-00b4-d0000bb1e11e}' + val 'co' = s '{0000000e-57ee-1e5c-00b4-d0000bb1e11e}' + val 'cs' = s '{0000001c-57ee-1e5c-00b4-d0000bb1e11e}' + val 'cy' = s '{0000002c-57ee-1e5c-00b4-d0000bb1e11e}' + val 'da' = s '{0000001f-57ee-1e5c-00b4-d0000bb1e11e}' + val 'de-DE_phoneb' = s '{0000003f-57ee-1e5c-00b4-d0000bb1e11e}' + val 'dv' = s '{00000045-57ee-1e5c-00b4-d0000bb1e11e}' + val 'es' = s '{0000002b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'es-ES_tradnl' = s '{0000002a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'et' = s '{00000027-57ee-1e5c-00b4-d0000bb1e11e}' + val 'fa' = s '{00000041-57ee-1e5c-00b4-d0000bb1e11e}' + val 'fi' = s '{0000001a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'fr' = s '{00000003-57ee-1e5c-00b4-d0000bb1e11e}' + val 'fy' = s '{0000003e-57ee-1e5c-00b4-d0000bb1e11e}' + val 'gu' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'haw' = s '{00000002-57ee-1e5c-00b4-d0000bb1e11e}' + val 'hi' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'hr' = s '{00000019-57ee-1e5c-00b4-d0000bb1e11e}' + val 'hsb' = s '{00000013-57ee-1e5c-00b4-d0000bb1e11e}' + val 'hu' = s '{00000004-57ee-1e5c-00b4-d0000bb1e11e}' + val 'hu-HU_technl' = s '{00000026-57ee-1e5c-00b4-d0000bb1e11e}' + val 'is' = s '{00000025-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ja' = s '{00000046-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ja-JP_radstr' = s '{00000036-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ka-GE_modern' = s '{00000048-57ee-1e5c-00b4-d0000bb1e11e}' + val 'kk' = s '{0000000b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'kl' = s '{0000001f-57ee-1e5c-00b4-d0000bb1e11e}' + val 'km' = s '{0000000c-57ee-1e5c-00b4-d0000bb1e11e}' + val 'kn' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ko' = s '{00000047-57ee-1e5c-00b4-d0000bb1e11e}' + val 'kok' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ky' = s '{0000004a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'lo' = s '{0000002e-57ee-1e5c-00b4-d0000bb1e11e}' + val 'lt' = s '{00000028-57ee-1e5c-00b4-d0000bb1e11e}' + val 'lv' = s '{00000005-57ee-1e5c-00b4-d0000bb1e11e}' + val 'lv-LV_tradnl' = s '{00000006-57ee-1e5c-00b4-d0000bb1e11e}' + val 'mi' = s '{00000014-57ee-1e5c-00b4-d0000bb1e11e}' + val 'mk' = s '{0000000a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ml' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'mn' = s '{0000004a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'moh' = s '{00000010-57ee-1e5c-00b4-d0000bb1e11e}' + val 'mr' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'mt' = s '{0000002d-57ee-1e5c-00b4-d0000bb1e11e}' + val 'nb' = s '{00000020-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ne' = s '{00000033-57ee-1e5c-00b4-d0000bb1e11e}' + val 'nn' = s '{00000020-57ee-1e5c-00b4-d0000bb1e11e}' + val 'no' = s '{00000020-57ee-1e5c-00b4-d0000bb1e11e}' + val 'oc' = s '{00000003-57ee-1e5c-00b4-d0000bb1e11e}' + val 'or' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'pa' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'pa-Arab' = s '{00000007-57ee-1e5c-00b4-d0000bb1e11e}' + val 'pl' = s '{0000001d-57ee-1e5c-00b4-d0000bb1e11e}' + val 'prs' = s '{00000016-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ps' = s '{00000016-57ee-1e5c-00b4-d0000bb1e11e}' + val 'quc' = s '{0000002b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'qut' = s '{0000002b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'rm' = s '{00000011-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ro' = s '{00000024-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ru' = s '{0000004a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sa' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sah' = s '{00000009-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sd' = s '{00000007-57ee-1e5c-00b4-d0000bb1e11e}' + val 'se' = s '{00000021-57ee-1e5c-00b4-d0000bb1e11e}' + val 'se-FI' = s '{0000001b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'se-SE' = s '{0000001b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'si' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sk' = s '{00000029-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sl' = s '{0000001e-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sma' = s '{0000001b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sma-NO' = s '{00000021-57ee-1e5c-00b4-d0000bb1e11e}' + val 'smj' = s '{0000001b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'smj-NO' = s '{00000021-57ee-1e5c-00b4-d0000bb1e11e}' + val 'smn' = s '{0000001b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sms' = s '{0000001b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sq' = s '{00000018-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sr' = s '{00000019-57ee-1e5c-00b4-d0000bb1e11e}' + val 'sv' = s '{0000001a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'syr' = s '{00000044-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ta' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'te' = s '{00000032-57ee-1e5c-00b4-d0000bb1e11e}' + val 'tg' = s '{0000004a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'th' = s '{0000002f-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ti' = s '{0000003c-57ee-1e5c-00b4-d0000bb1e11e}' + val 'tk' = s '{00000008-57ee-1e5c-00b4-d0000bb1e11e}' + val 'tr' = s '{00000022-57ee-1e5c-00b4-d0000bb1e11e}' + val 'tt' = s '{00000043-57ee-1e5c-00b4-d0000bb1e11e}' + val 'tzm' = s '{0000000d-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ug' = s '{00000017-57ee-1e5c-00b4-d0000bb1e11e}' + val 'uk' = s '{00000040-57ee-1e5c-00b4-d0000bb1e11e}' + val 'ur' = s '{00000015-57ee-1e5c-00b4-d0000bb1e11e}' + val 'uz-Cyrl' = s '{0000004a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'vi' = s '{00000030-57ee-1e5c-00b4-d0000bb1e11e}' + val 'wo' = s '{00000003-57ee-1e5c-00b4-d0000bb1e11e}' + val 'x-IV_mathan' = s '{00000035-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh' = s '{0000003a-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-CN_phoneb' = s '{0000004b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-CN_stroke' = s '{00000039-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-HK' = s '{00000037-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-HK_radstr' = s '{0000003b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-Hant' = s '{00000037-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-MO' = s '{00000037-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-MO_radstr' = s '{0000003b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-SG_phoneb' = s '{0000004b-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-SG_stroke' = s '{00000039-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-TW' = s '{00000037-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-TW_pronun' = s '{00000038-57ee-1e5c-00b4-d0000bb1e11e}' + val 'zh-TW_radstr' = s '{0000003b-57ee-1e5c-00b4-d0000bb1e11e}' + } + } + } + } + } + } +} diff --git a/dll/win32/KernelBase/wine/kernelbase.spec b/dll/win32/KernelBase/wine/kernelbase.spec new file mode 100644 index 0000000000000..e9b6e7306d570 --- /dev/null +++ b/dll/win32/KernelBase/wine/kernelbase.spec @@ -0,0 +1,1822 @@ +@ stdcall AccessCheck(ptr long long ptr ptr ptr ptr ptr) +@ stdcall AccessCheckAndAuditAlarmW(wstr ptr wstr wstr ptr long ptr long ptr ptr ptr) +@ stdcall AccessCheckByType(ptr ptr long long ptr long ptr ptr ptr ptr ptr) +@ stub AccessCheckByTypeAndAuditAlarmW +@ stub AccessCheckByTypeResultList +@ stub AccessCheckByTypeResultListAndAuditAlarmByHandleW +@ stub AccessCheckByTypeResultListAndAuditAlarmW +@ stdcall AcquireSRWLockExclusive(ptr) ntdll.RtlAcquireSRWLockExclusive +@ stdcall AcquireSRWLockShared(ptr) ntdll.RtlAcquireSRWLockShared +# @ stub AcquireStateLock +@ stdcall ActivateActCtx(ptr ptr) +@ stdcall AddAccessAllowedAce(ptr long long ptr) +@ stdcall AddAccessAllowedAceEx(ptr long long long ptr) +@ stdcall AddAccessAllowedObjectAce(ptr long long long ptr ptr ptr) +@ stdcall AddAccessDeniedAce(ptr long long ptr) +@ stdcall AddAccessDeniedAceEx(ptr long long long ptr) +@ stdcall AddAccessDeniedObjectAce(ptr long long long ptr ptr ptr) +@ stdcall AddAce(ptr long long ptr long) +@ stdcall AddAuditAccessAce(ptr long long ptr long long) +@ stdcall AddAuditAccessAceEx(ptr long long long ptr long long) +@ stdcall AddAuditAccessObjectAce(ptr long long long ptr ptr ptr long long) +@ stdcall AddConsoleAliasA(str str str) +@ stdcall AddConsoleAliasW(wstr wstr wstr) +@ stdcall AddDllDirectory(wstr) +@ stdcall AddMandatoryAce(ptr long long long ptr) +@ stdcall AddRefActCtx(ptr) +# @ stub AddResourceAttributeAce +# @ stub AddSIDToBoundaryDescriptor +# @ stub AddScopedPolicyIDAce +@ stdcall AddVectoredContinueHandler(long ptr) ntdll.RtlAddVectoredContinueHandler +@ stdcall AddVectoredExceptionHandler(long ptr) ntdll.RtlAddVectoredExceptionHandler +@ stdcall AdjustTokenGroups(long long ptr long ptr ptr) +@ stdcall AdjustTokenPrivileges(long long ptr long ptr ptr) +@ stdcall AllocConsole() +@ stdcall AllocateAndInitializeSid(ptr long long long long long long long long long ptr) +@ stdcall AllocateLocallyUniqueId(ptr) +@ stdcall AllocateUserPhysicalPages(long ptr ptr) +@ stdcall AllocateUserPhysicalPagesNuma(long ptr ptr long) +# @ stub AppContainerDeriveSidFromMoniker +# @ stub AppContainerFreeMemory +# @ stub AppContainerLookupDisplayNameMrtReference +# @ stub AppContainerLookupMoniker +# @ stub AppContainerRegisterSid +# @ stub AppContainerUnregisterSid +# @ stub AppPolicyGetClrCompat +# @ stub AppPolicyGetCreateFileAccess +# @ stub AppPolicyGetLifecycleManagement +@ stdcall AppPolicyGetMediaFoundationCodecLoading(ptr ptr) +@ stdcall AppPolicyGetProcessTerminationMethod(ptr ptr) +@ stdcall AppPolicyGetShowDeveloperDiagnostic(ptr ptr) +@ stdcall AppPolicyGetThreadInitializationType(ptr ptr) +@ stdcall AppPolicyGetWindowingModel(ptr ptr) +# @ stub AppXFreeMemory +# @ stub AppXGetApplicationData +# @ stub AppXGetDevelopmentMode +# @ stub AppXGetOSMaxVersionTested +# @ stub AppXGetOSMinVersion +# @ stub AppXGetPackageCapabilities +# @ stub AppXGetPackageSid +# @ stub AppXLookupDisplayName +# @ stub AppXLookupMoniker +# @ stub AppXPostSuccessExtension +# @ stub AppXPreCreationExtension +# @ stub AppXReleaseAppXContext +# @ stub AppXUpdatePackageCapabilities +# @ stub ApplicationUserModelIdFromProductId +@ stdcall AreAllAccessesGranted(long long) +@ stdcall AreAnyAccessesGranted(long long) +@ stdcall AreFileApisANSI() +# @ stub AreThereVisibleLogoffScriptsInternal +# @ stub AreThereVisibleShutdownScriptsInternal +@ stdcall AttachConsole(long) +@ stub BaseCheckAppcompatCache +# @ stub BaseCheckAppcompatCacheEx +@ stub BaseCleanupAppcompatCacheSupport +@ stub BaseDllFreeResourceId +@ stub BaseDllMapResourceIdW +@ stub BaseDumpAppcompatCache +@ stdcall BaseFlushAppcompatCache() +# @ stub BaseFormatObjectAttributes +# @ stub BaseFreeAppCompatDataForProcess +@ stdcall BaseGetNamedObjectDirectory(ptr) +@ stub BaseGetProcessDllPath +@ stub BaseGetProcessExePath +@ stub BaseInitAppcompatCacheSupport +@ stub BaseInvalidateDllSearchPathCache +@ stub BaseInvalidateProcessSearchPathCache +# @ stub BaseIsAppcompatInfrastructureDisabled +# @ stub BaseMarkFileForDelete +# @ stub BaseReadAppCompatDataForProcess +@ stub BaseReleaseProcessDllPath +@ stub BaseReleaseProcessExePath +@ stub BaseUpdateAppcompatCache +# @ stub BasepAdjustObjectAttributesForPrivateNamespace +# @ stub BasepCopyFileCallback +# @ stub BasepCopyFileExW +# @ stub BasepNotifyTrackingService +@ stdcall Beep(long long) +@ stub BemCopyReference +@ stub BemCreateContractFrom +@ stub BemCreateReference +@ stub BemFreeContract +@ stub BemFreeReference +# @ stub CLOSE_LOCAL_HANDLE_INTERNAL +@ stdcall CallNamedPipeW(wstr ptr long ptr long ptr long) +@ stdcall CallbackMayRunLong(ptr) +@ stdcall CancelIo(long) +@ stdcall CancelIoEx(long ptr) +@ stdcall CancelSynchronousIo(long) +@ stdcall CancelThreadpoolIo(ptr) ntdll.TpCancelAsyncIoOperation +@ stdcall CancelWaitableTimer(long) +# @ stub CeipIsOptedIn +@ stdcall ChangeTimerQueueTimer(ptr ptr long long) +@ stdcall CharLowerA(str) +@ stdcall CharLowerBuffA(str long) +@ stdcall CharLowerBuffW(wstr long) +@ stdcall CharLowerW(wstr) +@ stdcall CharNextA(str) +@ stdcall CharNextExA(long str long) +@ stdcall CharNextW(wstr) +@ stdcall CharPrevA(str str) +@ stdcall CharPrevExA(long str str long) +@ stdcall CharPrevW(wstr wstr) +@ stdcall CharUpperA(str) +@ stdcall CharUpperBuffA(str long) +@ stdcall CharUpperBuffW(wstr long) +@ stdcall CharUpperW(wstr) +# @ stub CheckAllowDecryptedRemoteDestinationPolicy +@ stub CheckGroupPolicyEnabled +# @ stub CheckIfStateChangeNotificationExists +@ stdcall CheckRemoteDebuggerPresent(long ptr) +# @ stub CheckTokenCapability +@ stdcall CheckTokenMembership(long ptr ptr) +# @ stub CheckTokenMembershipEx +@ stdcall ChrCmpIA(long long) +@ stdcall ChrCmpIW(long long) +@ stdcall ClearCommBreak(long) +@ stdcall ClearCommError(long ptr ptr) +# @ stub CloseGlobalizationUserSettingsKey +@ stdcall CloseHandle(long) +# @ stub ClosePackageInfo +# @ stub ClosePrivateNamespace +@ stdcall ClosePseudoConsole(ptr) +# @ stub CloseState +# @ stub CloseStateAtom +# @ stub CloseStateChangeNotification +# @ stub CloseStateContainer +# @ stub CloseStateLock +@ stdcall CloseThreadpool(ptr) ntdll.TpReleasePool +@ stdcall CloseThreadpoolCleanupGroup(ptr) ntdll.TpReleaseCleanupGroup +@ stdcall CloseThreadpoolCleanupGroupMembers(ptr long ptr) ntdll.TpReleaseCleanupGroupMembers +@ stdcall CloseThreadpoolIo(ptr) ntdll.TpReleaseIoCompletion +@ stdcall CloseThreadpoolTimer(ptr) ntdll.TpReleaseTimer +@ stdcall CloseThreadpoolWait(ptr) ntdll.TpReleaseWait +@ stdcall CloseThreadpoolWork(ptr) ntdll.TpReleaseWork +# @ stub CommitStateAtom +@ stdcall CompareFileTime(ptr ptr) +@ stdcall CompareObjectHandles(ptr ptr) +@ stdcall CompareStringA(long long str long str long) +@ stdcall CompareStringEx(wstr long wstr long wstr long ptr ptr long) +@ stdcall CompareStringOrdinal(wstr long wstr long long) +@ stdcall CompareStringW(long long wstr long wstr long) +@ stdcall ConnectNamedPipe(long ptr) +@ stdcall ContinueDebugEvent(long long long) +@ stdcall ConvertDefaultLocale(long) +@ stdcall ConvertFiberToThread() +@ stdcall ConvertThreadToFiber(ptr) +@ stdcall ConvertThreadToFiberEx(ptr long) +@ stdcall ConvertToAutoInheritPrivateObjectSecurity(ptr ptr ptr ptr long ptr) +@ stdcall CopyContext(ptr long ptr) +@ stdcall CopyFile2(wstr wstr ptr) +@ stdcall CopyFileExW(wstr wstr ptr ptr ptr long) +@ stdcall CopyFileW(wstr wstr long) +@ stdcall -arch=x86_64 CopyMemoryNonTemporal(ptr ptr long) ntdll.RtlCopyMemoryNonTemporal +@ stdcall CopySid(long ptr ptr) +# @ stub CouldMultiUserAppsBehaviorBePossibleForPackage +@ stdcall CreateActCtxW(ptr) +# @ stub CreateAppContainerToken +@ stdcall CreateBoundaryDescriptorW(wstr long) +@ stdcall CreateConsoleScreenBuffer(long long ptr long ptr) +@ stdcall CreateDirectoryA(str ptr) +@ stdcall CreateDirectoryExW(wstr wstr ptr) +@ stdcall CreateDirectoryW(wstr ptr) +# @ stub CreateEnclave +@ stdcall CreateEventA(ptr long long str) +@ stdcall CreateEventExA(ptr str long long) +@ stdcall CreateEventExW(ptr wstr long long) +@ stdcall CreateEventW(ptr long long wstr) +@ stdcall CreateFiber(long ptr ptr) +@ stdcall CreateFiberEx(long long long ptr ptr) +@ stdcall CreateFile2(wstr long long long ptr) +@ stdcall CreateFileA(str long long ptr long long long) +@ stdcall CreateFileMappingFromApp(long ptr long int64 wstr) +@ stdcall CreateFileMappingNumaW(long ptr long long long wstr long) +@ stdcall CreateFileMappingW(long ptr long long long wstr) +@ stdcall CreateFileW(wstr long long ptr long long long) +@ stdcall CreateHardLinkA(str str ptr) +@ stdcall CreateHardLinkW(wstr wstr ptr) +@ stdcall CreateIoCompletionPort(long long long long) +@ stdcall CreateMemoryResourceNotification(long) +@ stdcall CreateMutexA(ptr long str) +@ stdcall CreateMutexExA(ptr str long long) +@ stdcall CreateMutexExW(ptr wstr long long) +@ stdcall CreateMutexW(ptr long wstr) +@ stdcall CreateNamedPipeW(wstr long long long long long long ptr) +@ stdcall CreatePipe(ptr ptr ptr long) +# @ stub CreatePrivateNamespaceW +@ stdcall CreatePrivateObjectSecurity(ptr ptr ptr long long ptr) +@ stdcall CreatePrivateObjectSecurityEx(ptr ptr ptr ptr long long long ptr) +@ stdcall CreatePrivateObjectSecurityWithMultipleInheritance(ptr ptr ptr ptr long long long long ptr) +@ stdcall CreateProcessA(str str ptr ptr long long ptr str ptr ptr) +@ stdcall CreateProcessAsUserA(long str str ptr ptr long long ptr str ptr ptr) +@ stdcall CreateProcessAsUserW(long wstr wstr ptr ptr long long ptr wstr ptr ptr) +@ stdcall CreateProcessInternalA(long str str ptr ptr long long ptr str ptr ptr ptr) +@ stdcall CreateProcessInternalW(long wstr wstr ptr ptr long long ptr wstr ptr ptr ptr) +@ stdcall CreateProcessW(wstr wstr ptr ptr long long ptr wstr ptr ptr) +@ stdcall CreatePseudoConsole(long long long long ptr) +@ stdcall CreateRemoteThread(long ptr long ptr long long ptr) +@ stdcall CreateRemoteThreadEx(long ptr long ptr ptr long ptr ptr) +@ stdcall CreateRestrictedToken(long long long ptr long ptr long ptr ptr) +@ stdcall CreateSemaphoreExW(ptr long long wstr long long) +@ stdcall CreateSemaphoreW(ptr long long wstr) +# @ stub CreateStateAtom +# @ stub CreateStateChangeNotification +# @ stub CreateStateContainer +# @ stub CreateStateLock +# @ stub CreateStateSubcontainer +@ stdcall CreateSymbolicLinkW(wstr wstr long) +@ stdcall CreateThread(ptr long ptr long long ptr) +@ stdcall CreateThreadpool(ptr) +@ stdcall CreateThreadpoolCleanupGroup() +@ stdcall CreateThreadpoolIo(ptr ptr ptr ptr) +@ stdcall CreateThreadpoolTimer(ptr ptr ptr) +@ stdcall CreateThreadpoolWait(ptr ptr ptr) +@ stdcall CreateThreadpoolWork(ptr ptr ptr) +@ stdcall CreateTimerQueue() +@ stdcall CreateTimerQueueTimer(ptr long ptr ptr long long long) +@ stdcall CreateWaitableTimerExW(ptr wstr long long) +@ stdcall CreateWaitableTimerW(ptr long wstr) +@ stdcall CreateWellKnownSid(long ptr ptr ptr) +@ stdcall CtrlRoutine(ptr) +# @ stub CveEventWrite +@ stdcall DeactivateActCtx(long long) +@ stdcall DebugActiveProcess(long) +@ stdcall DebugActiveProcessStop(long) +@ stdcall DebugBreak() +@ stdcall DecodePointer(ptr) ntdll.RtlDecodePointer +# @ stub DecodeRemotePointer +@ stdcall DecodeSystemPointer(ptr) ntdll.RtlDecodeSystemPointer +@ stdcall DefineDosDeviceW(long wstr wstr) +@ stdcall DelayLoadFailureHook(str str) +# @ stub DelayLoadFailureHookLookup +@ stdcall DeleteAce(ptr long) +# @ stub DeleteBoundaryDescriptor +@ stdcall DeleteCriticalSection(ptr) ntdll.RtlDeleteCriticalSection +@ stdcall DeleteFiber(ptr) +@ stdcall DeleteFileA(str) +@ stdcall DeleteFileW(wstr) +@ stdcall DeleteProcThreadAttributeList(ptr) +# @ stub DeleteStateAtomValue +# @ stub DeleteStateContainer +# @ stub DeleteStateContainerValue +# @ stub DeleteSynchronizationBarrier +@ stdcall DeleteTimerQueueEx(long long) +@ stdcall DeleteTimerQueueTimer(long long long) +@ stdcall DeleteVolumeMountPointW(wstr) +@ stdcall DestroyPrivateObjectSecurity(ptr) +@ stdcall DeviceIoControl(long long ptr long ptr long ptr ptr) +@ stdcall DisablePredefinedHandleTableInternal(long) +@ stdcall DisableThreadLibraryCalls(long) +@ stdcall DisassociateCurrentThreadFromCallback(ptr) ntdll.TpDisassociateCallback +@ stdcall DiscardVirtualMemory(ptr long) +@ stdcall DisconnectNamedPipe(long) +@ stdcall DnsHostnameToComputerNameExW(wstr ptr ptr) +# @ stub DsBindWithSpnExW +# @ stub DsCrackNamesW +# @ stub DsFreeDomainControllerInfoW +# @ stub DsFreeNameResultW +# @ stub DsFreeNgcKey +# @ stub DsFreePasswordCredentials +# @ stub DsGetDomainControllerInfoW +# @ stub DsMakePasswordCredentialsW +# @ stub DsReadNgcKeyW +# @ stub DsUnBindW +# @ stub DsWriteNgcKeyW +@ stdcall DuplicateHandle(long long long ptr long long long) +# @ stub DuplicateStateContainerHandle +@ stdcall DuplicateToken(long long ptr) +@ stdcall DuplicateTokenEx(long long ptr long long ptr) +@ stdcall EmptyWorkingSet(long) +@ stdcall EncodePointer(ptr) ntdll.RtlEncodePointer +# @ stub EncodeRemotePointer +@ stdcall EncodeSystemPointer(ptr) ntdll.RtlEncodeSystemPointer +# @ stub EnterCriticalPolicySectionInternal +@ stdcall EnterCriticalSection(ptr) ntdll.RtlEnterCriticalSection +# @ stub EnterSynchronizationBarrier +@ stdcall EnumCalendarInfoExEx(ptr wstr long wstr long long) +@ stdcall EnumCalendarInfoExW(ptr long long long) +@ stdcall EnumCalendarInfoW(ptr long long long) +@ stdcall EnumDateFormatsExEx(ptr wstr long long) +@ stdcall EnumDateFormatsExW(ptr long long) +@ stdcall EnumDateFormatsW(ptr long long) +@ stdcall EnumDeviceDrivers(ptr long ptr) +@ stdcall EnumDynamicTimeZoneInformation(long ptr) +@ stdcall EnumLanguageGroupLocalesW(ptr long long ptr) +@ stdcall EnumPageFilesA(ptr ptr) +@ stdcall EnumPageFilesW(ptr ptr) +@ stdcall EnumProcessModules(long ptr long ptr) +@ stdcall EnumProcessModulesEx(long ptr long ptr long) +@ stdcall EnumProcesses(ptr long ptr) +@ stdcall EnumResourceLanguagesExA(long str str ptr long long long) +@ stdcall EnumResourceLanguagesExW(long wstr wstr ptr long long long) +@ stdcall EnumResourceNamesExA(long str ptr long long long) +@ stdcall EnumResourceNamesExW(long wstr ptr long long long) +@ stdcall EnumResourceNamesW(long wstr ptr long) +@ stdcall EnumResourceTypesExA(long ptr long long long) +@ stdcall EnumResourceTypesExW(long ptr long long long) +@ stdcall EnumSystemCodePagesW(ptr long) +@ stdcall EnumSystemFirmwareTables(long ptr long) +@ stdcall EnumSystemGeoID(long long ptr) +@ stdcall EnumSystemLanguageGroupsW(ptr long ptr) +@ stdcall EnumSystemLocalesA(ptr long) +@ stdcall EnumSystemLocalesEx(ptr long long ptr) +@ stdcall EnumSystemLocalesW(ptr long) +@ stdcall EnumTimeFormatsEx(ptr wstr long long) +@ stdcall EnumTimeFormatsW(ptr long long) +@ stdcall EnumUILanguagesW(ptr long long) +# @ stub EnumerateStateAtomValues +# @ stub EnumerateStateContainerItems +@ stdcall EqualDomainSid(ptr ptr ptr) +@ stdcall EqualPrefixSid(ptr ptr) +@ stdcall EqualSid(ptr ptr) +@ stdcall EscapeCommFunction(long long) +@ stdcall EventActivityIdControl(long ptr) ntdll.EtwEventActivityIdControl +@ stdcall EventEnabled(int64 ptr) ntdll.EtwEventEnabled +@ stdcall EventProviderEnabled(int64 long int64) ntdll.EtwEventProviderEnabled +@ stdcall EventRegister(ptr ptr ptr ptr) ntdll.EtwEventRegister +@ stdcall EventSetInformation(int64 long ptr long) ntdll.EtwEventSetInformation +@ stdcall EventUnregister(int64) ntdll.EtwEventUnregister +@ stdcall EventWrite(int64 ptr long ptr) ntdll.EtwEventWrite +# @ stub EventWriteEx +@ stdcall EventWriteString(int64 long int64 ptr) ntdll.EtwEventWriteString +@ stdcall EventWriteTransfer(int64 ptr ptr ptr long ptr) ntdll.EtwEventWriteTransfer +@ stdcall ExitProcess(long) ntdll.RtlExitUserProcess +@ stdcall ExitThread(long) ntdll.RtlExitUserThread +@ stdcall ExpandEnvironmentStringsA(str ptr long) +@ stdcall ExpandEnvironmentStringsW(wstr ptr long) +@ stdcall ExpungeConsoleCommandHistoryA(str) +@ stdcall ExpungeConsoleCommandHistoryW(wstr) +@ stdcall FatalAppExitA(long str) +@ stdcall FatalAppExitW(long wstr) +@ stdcall FileTimeToLocalFileTime(ptr ptr) +@ stdcall FileTimeToSystemTime(ptr ptr) +@ stdcall FillConsoleOutputAttribute(long long long long ptr) +@ stdcall FillConsoleOutputCharacterA(long long long long ptr) +@ stdcall FillConsoleOutputCharacterW(long long long long ptr) +@ stdcall FindActCtxSectionGuid(long ptr long ptr ptr) +@ stdcall FindActCtxSectionStringW(long ptr long wstr ptr) +@ stdcall FindClose(long) +@ stdcall FindCloseChangeNotification(long) +@ stdcall FindFirstChangeNotificationA(str long long) +@ stdcall FindFirstChangeNotificationW(wstr long long) +@ stdcall FindFirstFileA(str ptr) +@ stdcall FindFirstFileExA(str long ptr long ptr long) +@ stdcall FindFirstFileExW(wstr long ptr long ptr long) +@ stdcall FindFirstFileNameW(wstr long ptr ptr) +@ stdcall FindFirstFileW(wstr ptr) +@ stdcall FindFirstFreeAce(ptr ptr) +@ stdcall FindFirstStreamW(wstr long ptr long) +@ stdcall FindFirstVolumeW(ptr long) +@ stdcall FindNLSString(long long wstr long wstr long ptr) +@ stdcall FindNLSStringEx(wstr long wstr long wstr long ptr ptr ptr long) +@ stdcall FindNextChangeNotification(long) +@ stdcall FindNextFileA(long ptr) +# @ stub FindNextFileNameW +@ stdcall FindNextFileW(long ptr) +@ stdcall FindNextStreamW(long ptr) +@ stdcall FindNextVolumeW(long ptr long) +# @ stub FindPackagesByPackageFamily +@ stdcall FindResourceExW(long wstr wstr long) +@ stdcall FindResourceW(long wstr wstr) +@ stdcall FindStringOrdinal(long wstr long wstr long long) +@ stdcall FindVolumeClose(ptr) +@ stdcall FlsAlloc(ptr) +@ stdcall FlsFree(long) +@ stdcall FlsGetValue(long) +@ stdcall FlsSetValue(long ptr) +@ stdcall FlushConsoleInputBuffer(long) +@ stdcall FlushFileBuffers(long) +@ stdcall FlushInstructionCache(long long long) +@ stdcall FlushProcessWriteBuffers() ntdll.NtFlushProcessWriteBuffers +@ stdcall FlushViewOfFile(ptr long) +@ stdcall FoldStringW(long wstr long ptr long) +# @ stub ForceSyncFgPolicyInternal +# @ stub FormatApplicationUserModelId +@ stdcall FormatMessageA(long ptr long long ptr long ptr) +@ stdcall FormatMessageW(long ptr long long ptr long ptr) +@ stdcall FreeConsole() +@ stdcall FreeEnvironmentStringsA(ptr) FreeEnvironmentStringsW +@ stdcall FreeEnvironmentStringsW(ptr) +# @ stub FreeGPOListInternalA +# @ stub FreeGPOListInternalW +@ stdcall FreeLibrary(long) +@ stdcall FreeLibraryAndExitThread(long long) +@ stdcall FreeLibraryWhenCallbackReturns(ptr ptr) ntdll.TpCallbackUnloadDllOnCompletion +@ stdcall FreeResource(long) +@ stdcall FreeSid(ptr) +@ stdcall FreeUserPhysicalPages(long ptr ptr) +@ stdcall GenerateConsoleCtrlEvent(long long) +# @ stub GenerateGPNotificationInternal +@ stdcall GetACP() +@ stdcall GetAcceptLanguagesA(ptr ptr) +@ stdcall GetAcceptLanguagesW(ptr ptr) +@ stdcall GetAce(ptr long ptr) +@ stdcall GetAclInformation(ptr ptr long long) +# @ stub GetAdjustObjectAttributesForPrivateNamespaceRoutine +# @ stub GetAlternatePackageRoots +# @ stub GetAppContainerAce +# @ stub GetAppContainerNamedObjectPath +# @ stub GetAppDataFolder +# @ stub GetAppModelVersion +# @ stub GetApplicationRecoveryCallback +@ stdcall GetApplicationRestartSettings(long ptr ptr ptr) +# @ stub GetApplicationUserModelId +# @ stub GetApplicationUserModelIdFromToken +# @ stub GetAppliedGPOListInternalA +# @ stub GetAppliedGPOListInternalW +@ stub GetCPFileNameFromRegistry +@ stub GetCPHashNode +@ stdcall GetCPInfo(long ptr) +@ stdcall GetCPInfoExW(long long ptr) +# @ stub GetCachedSigningLevel +@ stub GetCalendar +@ stdcall GetCalendarInfoEx(wstr long ptr long ptr long ptr) +@ stdcall GetCalendarInfoW(long long long ptr long ptr) +@ stdcall GetCommConfig(long ptr ptr) +@ stdcall GetCommMask(long ptr) +@ stdcall GetCommModemStatus(long ptr) +@ stdcall GetCommProperties(long ptr) +@ stdcall GetCommState(long ptr) +@ stdcall GetCommTimeouts(long ptr) +@ stdcall GetCommandLineA() +@ stdcall GetCommandLineW() +@ stdcall GetCompressedFileSizeA(str ptr) +@ stdcall GetCompressedFileSizeW(wstr ptr) +@ stdcall GetComputerNameExA(long ptr ptr) +@ stdcall GetComputerNameExW(long ptr ptr) +@ stdcall GetConsoleAliasA(str ptr long str) +#@ stub GetConsoleAliasExesA +@ stdcall GetConsoleAliasExesLengthA() +@ stdcall GetConsoleAliasExesLengthW() +#@ stub GetConsoleAliasExesW +@ stdcall GetConsoleAliasW(wstr ptr long wstr) +#@ stub GetConsoleAliasesA +@ stdcall GetConsoleAliasesLengthA(str) +@ stdcall GetConsoleAliasesLengthW(wstr) +#@ stub GetConsoleAliasesW +@ stdcall GetConsoleCP() +@ stdcall GetConsoleCommandHistoryA(ptr long str) +@ stdcall GetConsoleCommandHistoryLengthA(str) +@ stdcall GetConsoleCommandHistoryLengthW(wstr) +@ stdcall GetConsoleCommandHistoryW(ptr long wstr) +@ stdcall GetConsoleCursorInfo(long ptr) +@ stdcall GetConsoleDisplayMode(ptr) +@ stdcall GetConsoleFontSize(long long) +@ stdcall GetConsoleInputExeNameA(long ptr) +@ stdcall GetConsoleInputExeNameW(long ptr) +@ stdcall GetConsoleMode(long ptr) +@ stdcall GetConsoleOriginalTitleA(ptr long) +@ stdcall GetConsoleOriginalTitleW(ptr long) +@ stdcall GetConsoleOutputCP() +@ stdcall GetConsoleProcessList(ptr long) +@ stdcall GetConsoleScreenBufferInfo(long ptr) +@ stdcall GetConsoleScreenBufferInfoEx(long ptr) +@ stdcall GetConsoleTitleA(ptr long) +@ stdcall GetConsoleTitleW(ptr long) +@ stdcall GetConsoleWindow() +@ stdcall GetCurrencyFormatEx(wstr long wstr ptr ptr long) +@ stdcall GetCurrencyFormatW(long long wstr ptr ptr long) +@ stdcall GetCurrentActCtx(ptr) +# @ stub GetCurrentApplicationUserModelId +@ stdcall GetCurrentConsoleFont(long long ptr) +@ stdcall GetCurrentConsoleFontEx(long long ptr) +@ stdcall GetCurrentDirectoryA(long ptr) +@ stdcall GetCurrentDirectoryW(long ptr) +# @ stub GetCurrentPackageApplicationContext +# @ stub GetCurrentPackageApplicationResourcesContext +# @ stub GetCurrentPackageContext +@ stdcall GetCurrentPackageFamilyName(ptr ptr) +@ stdcall GetCurrentPackageFullName(ptr ptr) +@ stdcall GetCurrentPackageId(ptr ptr) +# @ stub GetCurrentPackageInfo +@ stdcall GetCurrentPackagePath(ptr ptr) +# @ stub GetCurrentPackageResourcesContext +# @ stub GetCurrentPackageSecurityContext +@ stdcall -norelay GetCurrentProcess() kernelbase_GetCurrentProcess +@ stdcall -norelay GetCurrentProcessId() kernelbase_GetCurrentProcessId +@ stdcall GetCurrentProcessorNumber() ntdll.NtGetCurrentProcessorNumber +@ stdcall GetCurrentProcessorNumberEx(ptr) ntdll.RtlGetCurrentProcessorNumberEx +# @ stub GetCurrentTargetPlatformContext +@ stdcall -norelay GetCurrentThread() kernelbase_GetCurrentThread +@ stdcall -norelay GetCurrentThreadId() kernelbase_GetCurrentThreadId +@ stdcall GetCurrentThreadStackLimits(ptr ptr) +@ stdcall GetDateFormatA(long long ptr str ptr long) +@ stdcall GetDateFormatEx(wstr long ptr wstr ptr long wstr) +@ stdcall GetDateFormatW(long long ptr wstr ptr long) +@ stdcall GetDeviceDriverBaseNameA(ptr ptr long) +@ stdcall GetDeviceDriverBaseNameW(ptr ptr long) +@ stdcall GetDeviceDriverFileNameA(ptr ptr long) +@ stdcall GetDeviceDriverFileNameW(ptr ptr long) +@ stdcall GetDiskFreeSpaceA(str ptr ptr ptr ptr) +@ stdcall GetDiskFreeSpaceExA(str ptr ptr ptr) +@ stdcall GetDiskFreeSpaceExW(wstr ptr ptr ptr) +@ stdcall GetDiskFreeSpaceW(wstr ptr ptr ptr ptr) +@ stdcall GetDriveTypeA(str) +@ stdcall GetDriveTypeW(wstr) +# @ stub GetDurationFormatEx +@ stdcall GetDynamicTimeZoneInformation(ptr) +@ stdcall GetDynamicTimeZoneInformationEffectiveYears(ptr ptr ptr) +# @ stub GetEffectivePackageStatusForUser +# @ stub GetEightBitStringToUnicodeSizeRoutine +# @ stub GetEightBitStringToUnicodeStringRoutine +@ stdcall -ret64 -arch=i386,x86_64 GetEnabledXStateFeatures() +@ stdcall GetEnvironmentStrings() GetEnvironmentStringsA +@ stdcall GetEnvironmentStringsA() +@ stdcall GetEnvironmentStringsW() +@ stdcall GetEnvironmentVariableA(str ptr long) +@ stdcall GetEnvironmentVariableW(wstr ptr long) +@ stub GetEraNameCountedString +@ stdcall GetErrorMode() +@ stdcall GetExitCodeProcess(long ptr) +@ stdcall GetExitCodeThread(long ptr) +@ stub GetFallbackDisplayName +@ stdcall GetFileAttributesA(str) +@ stdcall GetFileAttributesExA(str long ptr) +@ stdcall GetFileAttributesExW(wstr long ptr) +@ stdcall GetFileAttributesW(wstr) +@ stdcall GetFileInformationByHandle(long ptr) +@ stdcall GetFileInformationByHandleEx(long long ptr long) +@ stdcall GetFileMUIInfo(long wstr ptr ptr) +@ stdcall GetFileMUIPath(long wstr wstr ptr ptr ptr ptr) +@ stdcall GetFileSecurityW(wstr long ptr long ptr) +@ stdcall GetFileSize(long ptr) +@ stdcall GetFileSizeEx(long ptr) +@ stdcall GetFileTime(long ptr ptr ptr) +@ stdcall GetFileType(long) +@ stdcall GetFileVersionInfoA(str long long ptr) +# @ stub GetFileVersionInfoByHandle +@ stdcall GetFileVersionInfoExA(long str long long ptr) +@ stdcall GetFileVersionInfoExW(long wstr long long ptr) +@ stdcall GetFileVersionInfoSizeA(str ptr) +@ stdcall GetFileVersionInfoSizeExA(long str ptr) +@ stdcall GetFileVersionInfoSizeExW(long wstr ptr) +@ stdcall GetFileVersionInfoSizeW(wstr ptr) +@ stdcall GetFileVersionInfoW(wstr long long ptr) +@ stdcall GetFinalPathNameByHandleA(long ptr long long) +@ stdcall GetFinalPathNameByHandleW(long ptr long long) +@ stdcall GetFullPathNameA(str long ptr ptr) +@ stdcall GetFullPathNameW(wstr long ptr ptr) +# @ stub GetGPOListInternalA +# @ stub GetGPOListInternalW +@ stdcall GetGeoInfoW(long long ptr long long) +@ stdcall GetGeoInfoEx(ptr long ptr long) +@ stdcall GetHandleInformation(long ptr) +# @ stub GetHivePath +# @ stub GetIntegratedDisplaySize +# @ stub GetIsEdpEnabled +@ stdcall GetKernelObjectSecurity(long long ptr long ptr) +@ stdcall GetLargePageMinimum() +@ stdcall GetLargestConsoleWindowSize(long) +@ stdcall GetLastError() kernelbase_GetLastError +@ stdcall GetLengthSid(ptr) +@ stdcall GetLocalTime(ptr) +@ stdcall GetLocaleInfoA(long long ptr long) +@ stdcall GetLocaleInfoEx(wstr long ptr long) +@ stub GetLocaleInfoHelper +@ stdcall GetLocaleInfoW(long long ptr long) +@ stdcall GetLogicalDriveStringsW(long ptr) +@ stdcall GetLogicalDrives() +@ stdcall GetLogicalProcessorInformation(ptr ptr) +@ stdcall GetLogicalProcessorInformationEx(long ptr ptr) +@ stdcall GetLongPathNameA(str ptr long) +@ stdcall GetLongPathNameW(wstr ptr long) +@ stdcall GetMappedFileNameA(long ptr ptr long) +@ stdcall GetMappedFileNameW(long ptr ptr long) +# @ stub GetMemoryErrorHandlingCapabilities +@ stdcall GetModuleBaseNameA(long long ptr long) +@ stdcall GetModuleBaseNameW(long long ptr long) +@ stdcall GetModuleFileNameA(long ptr long) +@ stdcall GetModuleFileNameExA(long long ptr long) +@ stdcall GetModuleFileNameExW(long long ptr long) +@ stdcall GetModuleFileNameW(long ptr long) +@ stdcall GetModuleHandleA(str) +@ stdcall GetModuleHandleExA(long ptr ptr) +@ stdcall GetModuleHandleExW(long ptr ptr) +@ stdcall GetModuleHandleW(wstr) +@ stdcall GetModuleInformation(long long ptr long) +@ stdcall GetNLSVersion(long long ptr) +@ stdcall GetNLSVersionEx(long wstr ptr) +@ stub GetNamedLocaleHashNode +@ stub GetNamedPipeAttribute +@ stub GetNamedPipeClientComputerNameW +@ stdcall GetNamedPipeHandleStateW(long ptr ptr ptr ptr ptr long) +@ stdcall GetNamedPipeInfo(long ptr ptr ptr ptr) +@ stdcall GetNativeSystemInfo(ptr) +# @ stub GetNextFgPolicyRefreshInfoInternal +@ stdcall GetNumaHighestNodeNumber(ptr) +@ stdcall GetNumaNodeProcessorMaskEx(long ptr) +@ stdcall GetNumaProximityNodeEx(long ptr) +@ stdcall GetNumberFormatEx(wstr long wstr ptr ptr long) +@ stdcall GetNumberFormatW(long long wstr ptr ptr long) +@ stdcall GetNumberOfConsoleInputEvents(long ptr) +@ stdcall GetNumberOfConsoleMouseButtons(ptr) +@ stdcall GetOEMCP() +# @ stub GetOsManufacturingMode +# @ stub GetOsSafeBootMode +@ stdcall GetOverlappedResult(long ptr ptr long) +@ stdcall GetOverlappedResultEx(long ptr ptr long long) +# @ stub GetPackageApplicationContext +# @ stub GetPackageApplicationIds +# @ stub GetPackageApplicationProperty +# @ stub GetPackageApplicationPropertyString +# @ stub GetPackageApplicationResourcesContext +# @ stub GetPackageContext +@ stdcall GetPackageFamilyName(long ptr ptr) +# @ stub GetPackageFamilyNameFromToken +@ stdcall GetPackageFullName(long ptr ptr) +# @ stub GetPackageFullNameFromToken +# @ stub GetPackageId +# @ stub GetPackageInfo +# @ stub GetPackageInstallTime +# @ stub GetPackageOSMaxVersionTested +# @ stub GetPackagePath +@ stdcall GetPackagePathByFullName(wstr ptr wstr) +# @ stub GetPackagePathOnVolume +# @ stub GetPackageProperty +# @ stub GetPackagePropertyString +# @ stub GetPackageResourcesContext +# @ stub GetPackageResourcesProperty +# @ stub GetPackageSecurityContext +# @ stub GetPackageSecurityProperty +# @ stub GetPackageStatus +# @ stub GetPackageStatusForUser +# @ stub GetPackageTargetPlatformProperty +# @ stub GetPackageVolumeSisPath +@ stdcall GetPackagesByPackageFamily(wstr ptr ptr ptr ptr) +@ stdcall GetPerformanceInfo(ptr long) +@ stdcall GetPhysicallyInstalledSystemMemory(ptr) +# @ stub GetPreviousFgPolicyRefreshInfoInternal +@ stdcall GetPriorityClass(long) +@ stdcall GetPrivateObjectSecurity(ptr long ptr long ptr) +@ stdcall GetProcAddress(long str) +# @ stub GetProcAddressForCaller +# @ stub GetProcessDefaultCpuSets +@ stdcall GetProcessGroupAffinity(long ptr ptr) +@ stdcall GetProcessHandleCount(long ptr) +@ stdcall -norelay GetProcessHeap() kernelbase_GetProcessHeap +@ stdcall -import GetProcessHeaps(long ptr) RtlGetProcessHeaps +@ stdcall GetProcessId(long) +@ stdcall GetProcessIdOfThread(long) +@ stdcall GetProcessImageFileNameA(long ptr long) +@ stdcall GetProcessImageFileNameW(long ptr long) +@ stdcall GetProcessInformation(long long ptr long) +@ stdcall GetProcessMemoryInfo(long ptr long) +@ stdcall GetProcessMitigationPolicy(long long ptr long) +@ stdcall GetProcessPreferredUILanguages(long ptr ptr ptr) +@ stdcall GetProcessPriorityBoost(long ptr) +@ stdcall GetProcessShutdownParameters(ptr ptr) +@ stdcall GetProcessTimes(long ptr ptr ptr ptr) +@ stdcall GetProcessVersion(long) +@ stdcall GetProcessWorkingSetSizeEx(long ptr ptr ptr) +# @ stub GetProcessorSystemCycleTime +@ stdcall GetProductInfo(long long long long ptr) +@ stub GetPtrCalData +@ stub GetPtrCalDataArray +# @ stub GetPublisherCacheFolder +# @ stub GetPublisherRootFolder +@ stdcall GetQueuedCompletionStatus(long ptr ptr ptr long) +@ stdcall GetQueuedCompletionStatusEx(ptr ptr long ptr long long) +# @ stub GetRegistryExtensionFlags +# @ stub GetRoamingLastObservedChangeTime +@ stdcall GetSecurityDescriptorControl(ptr ptr ptr) +@ stdcall GetSecurityDescriptorDacl(ptr ptr ptr ptr) +@ stdcall GetSecurityDescriptorGroup(ptr ptr ptr) +@ stdcall GetSecurityDescriptorLength(ptr) +@ stdcall GetSecurityDescriptorOwner(ptr ptr ptr) +@ stub GetSecurityDescriptorRMControl +@ stdcall GetSecurityDescriptorSacl(ptr ptr ptr ptr) +# @ stub GetSerializedAtomBytes +# @ stub GetSharedLocalFolder +@ stdcall GetShortPathNameW(wstr ptr long) +@ stdcall GetSidIdentifierAuthority(ptr) +@ stdcall GetSidLengthRequired(long) +@ stdcall GetSidSubAuthority(ptr long) +@ stdcall GetSidSubAuthorityCount(ptr) +# @ stub GetStagedPackageOrigin +# @ stub GetStagedPackagePathByFullName +@ stdcall GetStartupInfoW(ptr) +# @ stub GetStateContainerDepth +# @ stub GetStateFolder +# @ stub GetStateRootFolder +# @ stub GetStateRootFolderBase +# @ stub GetStateSettingsFolder +# @ stub GetStateVersion +@ stdcall GetStdHandle(long) +# @ stub GetStringScripts +@ stub GetStringTableEntry +@ stdcall GetStringTypeA(long long str long ptr) +@ stdcall GetStringTypeExW(long long wstr long ptr) +@ stdcall GetStringTypeW(long wstr long ptr) +# @ stub GetSystemAppDataFolder +# @ stub GetSystemAppDataKey +@ stdcall GetSystemCpuSetInformation(ptr long ptr ptr long) +@ stdcall GetSystemDefaultLCID() +@ stdcall GetSystemDefaultLangID() +@ stdcall GetSystemDefaultLocaleName(ptr long) +@ stdcall GetSystemDefaultUILanguage() +@ stdcall GetSystemDirectoryA(ptr long) +@ stdcall GetSystemDirectoryW(ptr long) +@ stdcall GetSystemFileCacheSize(ptr ptr ptr) +@ stdcall GetSystemFirmwareTable(long long ptr long) +@ stdcall GetSystemInfo(ptr) +# @ stub GetSystemMetadataPath +# @ stub GetSystemMetadataPathForPackage +# @ stub GetSystemMetadataPathForPackageFamily +@ stdcall GetSystemPreferredUILanguages(long ptr ptr ptr) +# @ stub GetSystemStateRootFolder +@ stdcall GetSystemTime(ptr) +@ stdcall GetSystemTimeAdjustment(ptr ptr ptr) +@ stdcall GetSystemTimeAsFileTime(ptr) +@ stdcall GetSystemTimePreciseAsFileTime(ptr) +@ stdcall GetSystemTimes(ptr ptr ptr) +@ stdcall GetSystemWindowsDirectoryA(ptr long) +@ stdcall GetSystemWindowsDirectoryW(ptr long) +@ stdcall GetSystemWow64Directory2A(ptr long long) +@ stdcall GetSystemWow64Directory2W(ptr long long) +@ stdcall GetSystemWow64DirectoryA(ptr long) +@ stdcall GetSystemWow64DirectoryW(ptr long) +# @ stub GetTargetPlatformContext +@ stdcall GetTempFileNameA(str str long ptr) +@ stdcall GetTempFileNameW(wstr wstr long ptr) +@ stdcall GetTempPath2W(long ptr) +@ stdcall GetTempPath2A(long ptr) +@ stdcall GetTempPathA(long ptr) +@ stdcall GetTempPathW(long ptr) +@ stdcall GetThreadContext(long ptr) +@ stdcall GetThreadDescription(long ptr) +@ stdcall GetThreadErrorMode() +@ stdcall GetThreadGroupAffinity(long ptr) +@ stdcall GetThreadIOPendingFlag(long ptr) +@ stdcall GetThreadId(ptr) +@ stdcall GetThreadIdealProcessorEx(long ptr) +# @ stub GetThreadInformation +@ stdcall GetThreadLocale() +@ stdcall GetThreadPreferredUILanguages(long ptr ptr ptr) +@ stdcall GetThreadPriority(long) +@ stdcall GetThreadPriorityBoost(long ptr) +# @ stub GetThreadSelectedCpuSets +@ stdcall GetThreadTimes(long ptr ptr ptr ptr) +@ stdcall GetThreadUILanguage() +@ stdcall GetTickCount() +@ stdcall -ret64 GetTickCount64() +@ stdcall GetTimeFormatA(long long ptr str ptr long) +@ stdcall GetTimeFormatEx(wstr long ptr wstr ptr long) +@ stdcall GetTimeFormatW(long long ptr wstr ptr long) +@ stdcall GetTimeZoneInformation(ptr) +@ stdcall GetTimeZoneInformationForYear(long ptr ptr) +@ stdcall GetTokenInformation(long long ptr long ptr) +@ stdcall GetTraceEnableFlags(int64) ntdll.EtwGetTraceEnableFlags +@ stdcall GetTraceEnableLevel(int64) ntdll.EtwGetTraceEnableLevel +@ stdcall -ret64 GetTraceLoggerHandle(ptr) ntdll.EtwGetTraceLoggerHandle +@ stub GetUILanguageInfo +# @ stub GetUnicodeStringToEightBitSizeRoutine +# @ stub GetUnicodeStringToEightBitStringRoutine +@ stdcall GetUserDefaultGeoName(ptr long) +@ stdcall GetUserDefaultLCID() +@ stdcall GetUserDefaultLangID() +@ stdcall GetUserDefaultLocaleName(ptr long) +@ stdcall GetUserDefaultUILanguage() +@ stdcall GetUserGeoID(long) +@ stub GetUserInfo +@ stub GetUserInfoWord +# @ stub GetUserOverrideString +# @ stub GetUserOverrideWord +@ stdcall GetUserPreferredUILanguages(long ptr ptr ptr) +@ stdcall GetVersion() +@ stdcall GetVersionExA(ptr) +@ stdcall GetVersionExW(ptr) +@ stdcall GetVolumeInformationA(str ptr long ptr ptr ptr ptr long) +@ stdcall GetVolumeInformationByHandleW(ptr ptr long ptr ptr ptr ptr long) +@ stdcall GetVolumeInformationW(wstr ptr long ptr ptr ptr ptr long) +@ stdcall GetVolumeNameForVolumeMountPointW(wstr ptr long) +@ stdcall GetVolumePathNameW(wstr ptr long) +@ stdcall GetVolumePathNamesForVolumeNameW(wstr ptr long ptr) +@ stdcall GetWindowsAccountDomainSid(ptr ptr ptr) +@ stdcall GetWindowsDirectoryA(ptr long) +@ stdcall GetWindowsDirectoryW(ptr long) +@ stdcall GetWriteWatch(long ptr long ptr ptr ptr) +@ stdcall GetWsChanges(long ptr long) +@ stdcall GetWsChangesEx(long ptr ptr) +@ stdcall -arch=i386,x86_64 GetXStateFeaturesMask(ptr ptr) +@ stdcall GlobalAlloc(long long) +@ stdcall GlobalFree(long) +@ stdcall GlobalMemoryStatusEx(ptr) +# @ stub GuardCheckLongJumpTarget +# @ stub HasPolicyForegroundProcessingCompletedInternal +@ stdcall HashData(ptr long ptr long) +@ stdcall HeapAlloc(long long long) ntdll.RtlAllocateHeap +@ stdcall HeapCompact(long long) +@ stdcall HeapCreate(long long long) +@ stdcall HeapDestroy(long) +@ stdcall HeapFree(long long ptr) ntdll.RtlFreeHeap +@ stdcall HeapLock(long) +@ stdcall HeapQueryInformation(long long ptr long ptr) +@ stdcall HeapReAlloc(long long ptr long) ntdll.RtlReAllocateHeap +@ stdcall HeapSetInformation(ptr long ptr long) +@ stdcall HeapSize(long long ptr) ntdll.RtlSizeHeap +@ stub HeapSummary +@ stdcall HeapUnlock(long) +@ stdcall HeapValidate(long long ptr) +@ stdcall HeapWalk(long ptr) +@ stdcall IdnToAscii(long wstr long ptr long) +@ stdcall IdnToNameprepUnicode(long wstr long ptr long) +@ stdcall IdnToUnicode(long wstr long ptr long) +@ stdcall ImpersonateAnonymousToken(long) +@ stdcall ImpersonateLoggedOnUser(long) +@ stdcall ImpersonateNamedPipeClient(long) +@ stdcall ImpersonateSelf(long) +# @ stub IncrementPackageStatusVersion +@ stdcall InitOnceBeginInitialize(ptr long ptr ptr) +@ stdcall InitOnceComplete(ptr long ptr) +@ stdcall InitOnceExecuteOnce(ptr ptr ptr ptr) +@ stdcall InitOnceInitialize(ptr) ntdll.RtlRunOnceInitialize +@ stdcall InitializeAcl(ptr long long) +@ stdcall InitializeConditionVariable(ptr) ntdll.RtlInitializeConditionVariable +@ stdcall InitializeContext(ptr long ptr ptr) +@ stdcall InitializeContext2(ptr long ptr ptr int64) +@ stdcall InitializeCriticalSection(ptr) ntdll.RtlInitializeCriticalSection +@ stdcall InitializeCriticalSectionAndSpinCount(ptr long) +@ stdcall InitializeCriticalSectionEx(ptr long long) +# @ stub InitializeEnclave +@ stdcall InitializeProcThreadAttributeList(ptr long long ptr) +@ stdcall InitializeProcessForWsWatch(long) +@ stdcall InitializeSListHead(ptr) ntdll.RtlInitializeSListHead +@ stdcall InitializeSRWLock(ptr) ntdll.RtlInitializeSRWLock +@ stdcall InitializeSecurityDescriptor(ptr long) +@ stdcall InitializeSid(ptr ptr long) +# @ stub InitializeSynchronizationBarrier +# @ stub InstallELAMCertificateInfo +@ stdcall -arch=i386 InterlockedCompareExchange(ptr long long) +@ stdcall -arch=i386 -ret64 InterlockedCompareExchange64(ptr int64 int64) ntdll.RtlInterlockedCompareExchange64 +@ stdcall -arch=i386 InterlockedDecrement(ptr) +@ stdcall -arch=i386 InterlockedExchange(ptr long) +@ stdcall -arch=i386 InterlockedExchangeAdd(ptr long ) +@ stdcall InterlockedFlushSList(ptr) ntdll.RtlInterlockedFlushSList +@ stdcall -arch=i386 InterlockedIncrement(ptr) +@ stdcall InterlockedPopEntrySList(ptr) ntdll.RtlInterlockedPopEntrySList +@ stdcall InterlockedPushEntrySList(ptr ptr) ntdll.RtlInterlockedPushEntrySList +@ stdcall -fastcall InterlockedPushListSList(ptr ptr ptr long) ntdll.RtlInterlockedPushListSList +@ stdcall InterlockedPushListSListEx(ptr ptr ptr long) ntdll.RtlInterlockedPushListSListEx +@ stub InternalLcidToName +@ stdcall Internal_EnumCalendarInfo(ptr long long long long long long long) +@ stdcall Internal_EnumDateFormats(ptr long long long long long long) +@ stdcall Internal_EnumLanguageGroupLocales(ptr long long ptr long) +@ stdcall Internal_EnumSystemCodePages(ptr long long) +@ stdcall Internal_EnumSystemLanguageGroups(ptr long ptr long) +@ stub Internal_EnumSystemLocales +@ stdcall Internal_EnumTimeFormats(ptr long long long long long) +@ stdcall Internal_EnumUILanguages(ptr long long long) +# @ stub InternetTimeFromSystemTimeA +# @ stub InternetTimeFromSystemTimeW +# @ stub InternetTimeToSystemTimeA +# @ stub InternetTimeToSystemTimeW +# @ stub InvalidateAppModelVersionCache +@ stub InvalidateTzSpecificCache +@ stdcall IsApiSetImplemented(str) +@ stdcall IsCharAlphaA(long) +@ stdcall IsCharAlphaNumericA(long) +@ stdcall IsCharAlphaNumericW(long) +@ stdcall IsCharAlphaW(long) +@ stdcall IsCharBlankW(long) +@ stdcall IsCharCntrlW(long) +@ stdcall IsCharDigitW(long) +@ stdcall IsCharLowerA(long) +@ stdcall IsCharLowerW(long) +@ stdcall IsCharPunctW(long) +@ stdcall IsCharSpaceA(long) +@ stdcall IsCharSpaceW(long) +@ stdcall IsCharUpperA(long) +@ stdcall IsCharUpperW(long) +@ stdcall IsCharXDigitW(long) +@ stdcall IsDBCSLeadByte(long) +@ stdcall IsDBCSLeadByteEx(long long) +@ stdcall IsDebuggerPresent() +# @ stub IsDeveloperModeEnabled +# @ stub IsDeveloperModePolicyApplied +# @ stub IsEnclaveTypeSupported +# @ stub IsGlobalizationUserSettingsKeyRedirected +@ stdcall IsInternetESCEnabled() +@ stdcall IsNLSDefinedString(long long ptr wstr long) +@ stdcall IsNormalizedString(long wstr long) +# @ stub IsProcessCritical +@ stdcall IsProcessInJob(long long ptr) +@ stdcall IsProcessorFeaturePresent(long) +# @ stub IsSideloadingEnabled +# @ stub IsSideloadingPolicyApplied +# @ stub IsSyncForegroundPolicyRefresh +@ stdcall IsThreadAFiber() +@ stdcall IsThreadpoolTimerSet(ptr) ntdll.TpIsTimerSet +# @ stub IsTimeZoneRedirectionEnabled +@ stdcall IsTokenRestricted(long) +@ stdcall IsValidAcl(ptr) +@ stdcall IsValidCodePage(long) +@ stdcall IsValidLanguageGroup(long long) +@ stdcall IsValidLocale(long long) +@ stdcall IsValidLocaleName(wstr) +@ stdcall IsValidNLSVersion(long wstr ptr) +@ stub IsValidRelativeSecurityDescriptor +@ stdcall IsValidSecurityDescriptor(ptr) +@ stdcall IsValidSid(ptr) +@ stdcall IsWellKnownSid(ptr long) +@ stdcall IsWow64Process(ptr ptr) +@ stdcall IsWow64Process2(ptr ptr ptr) +@ stdcall K32EmptyWorkingSet(long) EmptyWorkingSet +@ stdcall K32EnumDeviceDrivers(ptr long ptr) EnumDeviceDrivers +@ stdcall K32EnumPageFilesA(ptr ptr) EnumPageFilesA +@ stdcall K32EnumPageFilesW(ptr ptr) EnumPageFilesW +@ stdcall K32EnumProcessModules(long ptr long ptr) EnumProcessModules +@ stdcall K32EnumProcessModulesEx(long ptr long ptr long) EnumProcessModulesEx +@ stdcall K32EnumProcesses(ptr long ptr) EnumProcesses +@ stdcall K32GetDeviceDriverBaseNameA(ptr ptr long) GetDeviceDriverBaseNameA +@ stdcall K32GetDeviceDriverBaseNameW(ptr ptr long) GetDeviceDriverBaseNameW +@ stdcall K32GetDeviceDriverFileNameA(ptr ptr long) GetDeviceDriverFileNameA +@ stdcall K32GetDeviceDriverFileNameW(ptr ptr long) GetDeviceDriverFileNameW +@ stdcall K32GetMappedFileNameA(long ptr ptr long) GetMappedFileNameA +@ stdcall K32GetMappedFileNameW(long ptr ptr long) GetMappedFileNameW +@ stdcall K32GetModuleBaseNameA(long long ptr long) GetModuleBaseNameA +@ stdcall K32GetModuleBaseNameW(long long ptr long) GetModuleBaseNameW +@ stdcall K32GetModuleFileNameExA(long long ptr long) GetModuleFileNameExA +@ stdcall K32GetModuleFileNameExW(long long ptr long) GetModuleFileNameExW +@ stdcall K32GetModuleInformation(long long ptr long) GetModuleInformation +@ stdcall K32GetPerformanceInfo(ptr long) GetPerformanceInfo +@ stdcall K32GetProcessImageFileNameA(long ptr long) GetProcessImageFileNameA +@ stdcall K32GetProcessImageFileNameW(long ptr long) GetProcessImageFileNameW +@ stdcall K32GetProcessMemoryInfo(long ptr long) GetProcessMemoryInfo +@ stdcall K32GetWsChanges(long ptr long) GetWsChanges +@ stdcall K32GetWsChangesEx(long ptr ptr) GetWsChangesEx +@ stdcall K32InitializeProcessForWsWatch(long) InitializeProcessForWsWatch +@ stdcall K32QueryWorkingSet(long ptr long) QueryWorkingSet +@ stdcall K32QueryWorkingSetEx(long ptr long) QueryWorkingSetEx +@ stdcall KernelBaseGetGlobalData() +@ stdcall LCIDToLocaleName(long ptr long long) +@ stdcall LCMapStringA(long long str long ptr long) +@ stdcall LCMapStringEx(wstr long wstr long ptr long ptr ptr long) +@ stdcall LCMapStringW(long long wstr long ptr long) +# @ stub LeaveCriticalPolicySectionInternal +@ stdcall LeaveCriticalSection(ptr) ntdll.RtlLeaveCriticalSection +@ stdcall LeaveCriticalSectionWhenCallbackReturns(ptr ptr) ntdll.TpCallbackLeaveCriticalSectionOnCompletion +@ stdcall LoadAppInitDlls() +# @ stub LoadEnclaveData +@ stdcall LoadLibraryA(str) +@ stdcall LoadLibraryExA( str long long) +@ stdcall LoadLibraryExW(wstr long long) +@ stdcall LoadLibraryW(wstr) +@ stdcall LoadPackagedLibrary(wstr long) +@ stdcall LoadResource(long long) +@ stdcall LoadStringA(long long ptr long) +@ stub LoadStringBaseExW +@ stub LoadStringByReference +@ stdcall LoadStringW(long long ptr long) +@ stdcall LocalAlloc(long long) +@ stdcall LocalFileTimeToFileTime(ptr ptr) +@ stdcall LocalFree(long) +@ stdcall LocalLock(long) +@ stdcall LocalReAlloc(long long long) +@ stdcall LocalUnlock(long) +@ stdcall LocaleNameToLCID(wstr long) +@ stdcall -arch=i386,x86_64 LocateXStateFeature(ptr long ptr) +@ stdcall LockFile(long long long long long) +@ stdcall LockFileEx(long long long long long ptr) +@ stdcall LockResource(long) +@ stdcall MakeAbsoluteSD(ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stub MakeAbsoluteSD2 +@ stdcall MakeSelfRelativeSD(ptr ptr ptr) +@ stdcall MapGenericMask(ptr ptr) +# @ stub MapPredefinedHandleInternal +@ stdcall MapUserPhysicalPages(ptr long ptr) +@ stdcall MapViewOfFile(long long long long long) +@ stdcall MapViewOfFile3(long long ptr int64 long long long ptr long) +@ stdcall MapViewOfFileEx(long long long long long ptr) +@ stdcall MapViewOfFileExNuma(long long long long long ptr long) +@ stdcall MapViewOfFileFromApp(long long int64 long) +@ stdcall MoveFileExW(wstr wstr long) +# @ stub MoveFileWithProgressTransactedW +@ stdcall MoveFileWithProgressW(wstr wstr ptr ptr long) +@ stdcall MulDiv(long long long) +@ stdcall MultiByteToWideChar(long long str long ptr long) +# @ stub NamedPipeEventEnum +# @ stub NamedPipeEventSelect +@ stdcall NeedCurrentDirectoryForExePathA(str) +@ stdcall NeedCurrentDirectoryForExePathW(wstr) +@ stub NlsCheckPolicy +@ stub NlsDispatchAnsiEnumProc +@ stub NlsEventDataDescCreate +@ stub NlsGetACPFromLocale +@ stub NlsGetCacheUpdateCount +@ stub NlsIsUserDefaultLocale +@ stub NlsUpdateLocale +@ stub NlsUpdateSystemLocale +@ stdcall NlsValidateLocale(ptr long) +@ stub NlsWriteEtwEvent +@ stdcall NormalizeString(long wstr long ptr long) +@ stub NotifyMountMgr +@ stub NotifyRedirectedStringChange +@ stdcall ObjectCloseAuditAlarmW(wstr ptr long) +@ stdcall ObjectDeleteAuditAlarmW(wstr ptr long) +@ stdcall ObjectOpenAuditAlarmW(wstr ptr wstr wstr ptr long long long ptr long long ptr) +@ stdcall ObjectPrivilegeAuditAlarmW(wstr ptr long long ptr long) +# @ stub OfferVirtualMemory +@ stdcall OpenEventA(long long str) +@ stdcall OpenEventW(long long wstr) +@ stdcall OpenFileById(long ptr long long ptr long) +@ stdcall OpenFileMappingFromApp(long long wstr) +@ stdcall OpenFileMappingW(long long wstr) +# @ stub OpenGlobalizationUserSettingsKey +@ stdcall OpenMutexW(long long wstr) +# @ stub OpenPackageInfoByFullName +# @ stub OpenPackageInfoByFullNameForUser +# @ stub OpenPrivateNamespaceW +@ stdcall OpenProcess(long long long) +@ stdcall OpenProcessToken(long long ptr) +@ stub OpenRegKey +@ stdcall OpenSemaphoreW(long long wstr) +# @ stub OpenState +# @ stub OpenStateAtom +# @ stub OpenStateExplicit +# @ stub OpenStateExplicitForUserSid +# @ stub OpenStateExplicitForUserSidString +@ stdcall OpenThread(long long long) +@ stdcall OpenThreadToken(long long long ptr) +@ stdcall OpenWaitableTimerW(long long wstr) +@ stdcall OutputDebugStringA(str) +@ stdcall OutputDebugStringW(wstr) +# @ stub OverrideRoamingDataModificationTimesInRange +# @ stub PackageFamilyNameFromFullName +# @ stub PackageFamilyNameFromId +# @ stub PackageFamilyNameFromProductId +# @ stub PackageFullNameFromId +# @ stub PackageFullNameFromProductId +@ stdcall PackageIdFromFullName(wstr long ptr ptr) +# @ stub PackageIdFromProductId +# @ stub PackageNameAndPublisherIdFromFamilyName +# @ stub PackageRelativeApplicationIdFromProductId +# @ stub PackageSidFromFamilyName +# @ stub PackageSidFromProductId +# @ stub ParseApplicationUserModelId +@ stdcall ParseURLA(str ptr) +@ stdcall ParseURLW(wstr ptr) +@ stdcall PathAddBackslashA(str) +@ stdcall PathAddBackslashW(wstr) +@ stdcall PathAddExtensionA(str str) +@ stdcall PathAddExtensionW(wstr wstr) +@ stdcall PathAllocCanonicalize(wstr long ptr) +@ stdcall PathAllocCombine(wstr wstr long ptr) +@ stdcall PathAppendA(str str) +@ stdcall PathAppendW(wstr wstr) +@ stdcall PathCanonicalizeA(ptr str) +@ stdcall PathCanonicalizeW(ptr wstr) +@ stdcall PathCchAddBackslash(wstr long) +@ stdcall PathCchAddBackslashEx(wstr long ptr ptr) +@ stdcall PathCchAddExtension(wstr long wstr) +@ stdcall PathCchAppend(wstr long wstr) +@ stdcall PathCchAppendEx(wstr long wstr long) +@ stdcall PathCchCanonicalize(ptr long wstr) +@ stdcall PathCchCanonicalizeEx(ptr long wstr long) +@ stdcall PathCchCombine(ptr long wstr wstr) +@ stdcall PathCchCombineEx(ptr long wstr wstr long) +@ stdcall PathCchFindExtension(wstr long ptr) +@ stdcall PathCchIsRoot(wstr) +@ stdcall PathCchRemoveBackslash(wstr long) +@ stdcall PathCchRemoveBackslashEx(wstr long ptr ptr) +@ stdcall PathCchRemoveExtension(wstr long) +@ stdcall PathCchRemoveFileSpec(wstr long) +@ stdcall PathCchRenameExtension(wstr long wstr) +@ stdcall PathCchSkipRoot(wstr ptr) +@ stdcall PathCchStripPrefix(wstr long) +@ stdcall PathCchStripToRoot(wstr long) +@ stdcall PathCombineA(ptr str str) +@ stdcall PathCombineW(ptr wstr wstr) +@ stdcall PathCommonPrefixA(str str ptr) +@ stdcall PathCommonPrefixW(wstr wstr ptr) +@ stdcall PathCreateFromUrlA(str ptr ptr long) +@ stdcall PathCreateFromUrlAlloc(wstr ptr long) +@ stdcall PathCreateFromUrlW(wstr ptr ptr long) +@ stdcall PathFileExistsA(str) +@ stdcall PathFileExistsW(wstr) +@ stdcall PathFindExtensionA(str) +@ stdcall PathFindExtensionW(wstr) +@ stdcall PathFindFileNameA(str) +@ stdcall PathFindFileNameW(wstr) +@ stdcall PathFindNextComponentA(str) +@ stdcall PathFindNextComponentW(wstr) +@ stdcall PathGetArgsA(str) +@ stdcall PathGetArgsW(wstr) +@ stdcall PathGetCharTypeA(long) +@ stdcall PathGetCharTypeW(long) +@ stdcall PathGetDriveNumberA(str) +@ stdcall PathGetDriveNumberW(wstr) +@ stdcall PathIsFileSpecA(str) +@ stdcall PathIsFileSpecW(wstr) +@ stdcall PathIsLFNFileSpecA(str) +@ stdcall PathIsLFNFileSpecW(wstr) +@ stdcall PathIsPrefixA(str str) +@ stdcall PathIsPrefixW(wstr wstr) +@ stdcall PathIsRelativeA(str) +@ stdcall PathIsRelativeW(wstr) +@ stdcall PathIsRootA(str) +@ stdcall PathIsRootW(wstr) +@ stdcall PathIsSameRootA(str str) +@ stdcall PathIsSameRootW(wstr wstr) +@ stdcall PathIsUNCA(str) +@ stdcall PathIsUNCEx(wstr ptr) +@ stdcall PathIsUNCServerA(str) +@ stdcall PathIsUNCServerShareA(str) +@ stdcall PathIsUNCServerShareW(wstr) +@ stdcall PathIsUNCServerW(wstr) +@ stdcall PathIsUNCW(wstr) +@ stdcall PathIsURLA(str) +@ stdcall PathIsURLW(wstr) +@ stdcall PathIsValidCharA(long long) +@ stdcall PathIsValidCharW(long long) +@ stdcall PathMatchSpecA(str str) +@ stdcall PathMatchSpecExA(str str long) +@ stdcall PathMatchSpecExW(wstr wstr long) +@ stdcall PathMatchSpecW(wstr wstr) +@ stdcall PathParseIconLocationA(str) +@ stdcall PathParseIconLocationW(wstr) +@ stdcall PathQuoteSpacesA(str) +@ stdcall PathQuoteSpacesW(wstr) +@ stdcall PathRelativePathToA(ptr str long str long) +@ stdcall PathRelativePathToW(ptr wstr long wstr long) +@ stdcall PathRemoveBackslashA(str) +@ stdcall PathRemoveBackslashW(wstr) +@ stdcall PathRemoveBlanksA(str) +@ stdcall PathRemoveBlanksW(wstr) +@ stdcall PathRemoveExtensionA(str) +@ stdcall PathRemoveExtensionW(wstr) +@ stdcall PathRemoveFileSpecA(str) +@ stdcall PathRemoveFileSpecW(wstr) +@ stdcall PathRenameExtensionA(str str) +@ stdcall PathRenameExtensionW(wstr wstr) +@ stdcall PathSearchAndQualifyA(str ptr long) +@ stdcall PathSearchAndQualifyW(wstr ptr long) +@ stdcall PathSkipRootA(str) +@ stdcall PathSkipRootW(wstr) +@ stdcall PathStripPathA(str) +@ stdcall PathStripPathW(wstr) +@ stdcall PathStripToRootA(str) +@ stdcall PathStripToRootW(wstr) +@ stdcall PathUnExpandEnvStringsA(str ptr long) +@ stdcall PathUnExpandEnvStringsW(wstr ptr long) +@ stdcall PathUnquoteSpacesA(str) +@ stdcall PathUnquoteSpacesW(wstr) +# @ stub PcwAddQueryItem +# @ stub PcwClearCounterSetSecurity +# @ stub PcwCollectData +# @ stub PcwCompleteNotification +# @ stub PcwCreateNotifier +# @ stub PcwCreateQuery +# @ stub PcwDisconnectCounterSet +# @ stub PcwEnumerateInstances +# @ stub PcwIsNotifierAlive +# @ stub PcwQueryCounterSetSecurity +# @ stub PcwReadNotificationData +# @ stub PcwRegisterCounterSet +# @ stub PcwRemoveQueryItem +# @ stub PcwSendNotification +# @ stub PcwSendStatelessNotification +# @ stub PcwSetCounterSetSecurity +# @ stub PcwSetQueryItemUserData +@ stdcall PeekConsoleInputA(ptr ptr long ptr) +@ stdcall PeekConsoleInputW(ptr ptr long ptr) +@ stdcall PeekNamedPipe(long ptr long ptr ptr ptr) +@ stdcall PerfCreateInstance(long ptr wstr long) +# @ stub PerfDecrementULongCounterValue +# @ stub PerfDecrementULongLongCounterValue +@ stdcall PerfDeleteInstance(long ptr) +# @ stub PerfIncrementULongCounterValue +# @ stub PerfIncrementULongLongCounterValue +# @ stub PerfQueryInstance +@ stdcall PerfSetCounterRefValue(long ptr long ptr) +@ stdcall PerfSetCounterSetInfo(long ptr long) +# @ stub PerfSetULongCounterValue +# @ stub PerfSetULongLongCounterValue +@ stdcall PerfStartProvider(ptr ptr ptr) +@ stdcall PerfStartProviderEx(ptr ptr ptr) +@ stdcall PerfStopProvider(long) +# @ stub PoolPerAppKeyStateInternal +@ stdcall PostQueuedCompletionStatus(long long ptr ptr) +@ stdcall PrefetchVirtualMemory(ptr ptr ptr long) +@ stub PrivCopyFileExW +@ stdcall PrivilegeCheck(ptr ptr ptr) +@ stdcall PrivilegedServiceAuditAlarmW(wstr wstr long ptr long) +@ stdcall ProcessIdToSessionId(long ptr) +# @ stub ProductIdFromPackageFamilyName +# @ stub PsmCreateKey +# @ stub PsmCreateKeyWithDynamicId +# @ stub PsmEqualApplication +# @ stub PsmEqualPackage +# @ stub PsmGetApplicationNameFromKey +# @ stub PsmGetKeyFromProcess +# @ stub PsmGetKeyFromToken +# @ stub PsmGetPackageFullNameFromKey +# @ stub PsmIsChildKey +# @ stub PsmIsDynamicKey +# @ stub PsmIsValidKey +# @ stub PssCaptureSnapshot +# @ stub PssDuplicateSnapshot +# @ stub PssFreeSnapshot +# @ stub PssQuerySnapshot +# @ stub PssWalkMarkerCreate +# @ stub PssWalkMarkerFree +# @ stub PssWalkMarkerGetPosition +# @ stub PssWalkMarkerSeekToBeginning +# @ stub PssWalkMarkerSetPosition +# @ stub PssWalkSnapshot +# @ stub PublishStateChangeNotification +@ stdcall PulseEvent(long) +@ stdcall PurgeComm(long long) +@ stdcall QISearch(ptr ptr ptr ptr) +@ stdcall QueryActCtxSettingsW(long ptr wstr wstr ptr long ptr) +@ stdcall QueryActCtxW(long ptr ptr long ptr long ptr) +@ stdcall QueryDepthSList(ptr) ntdll.RtlQueryDepthSList +@ stdcall QueryDosDeviceW(wstr ptr long) +@ stdcall QueryFullProcessImageNameA(ptr long ptr ptr) +@ stdcall QueryFullProcessImageNameW(ptr long ptr ptr) +@ stdcall QueryIoRingCapabilities(ptr) +@ stdcall QueryIdleProcessorCycleTime(ptr ptr) +@ stdcall QueryIdleProcessorCycleTimeEx(long ptr ptr) +@ stdcall QueryInterruptTime(ptr) +@ stdcall QueryInterruptTimePrecise(ptr) +@ stdcall QueryMemoryResourceNotification(ptr ptr) +# @ stub QueryOptionalDelayLoadedAPI +@ stdcall QueryPerformanceCounter(ptr) ntdll.RtlQueryPerformanceCounter +@ stdcall QueryPerformanceFrequency(ptr) ntdll.RtlQueryPerformanceFrequency +@ stub QueryProcessAffinityUpdateMode +@ stdcall QueryProcessCycleTime(long ptr) +# @ stub QueryProtectedPolicy +@ stub QuerySecurityAccessMask +# @ stub QueryStateAtomValueInfo +# @ stub QueryStateContainerCreatedNew +# @ stub QueryStateContainerItemInfo +@ stdcall QueryThreadCycleTime(long ptr) +@ stdcall QueryThreadpoolStackInformation(ptr ptr) +@ stdcall QueryUnbiasedInterruptTime(ptr) ntdll.RtlQueryUnbiasedInterruptTime +@ stdcall QueryUnbiasedInterruptTimePrecise(ptr) +@ stdcall QueryVirtualMemoryInformation(long ptr long ptr long ptr) +@ stdcall QueryWorkingSet(long ptr long) +@ stdcall QueryWorkingSetEx(long ptr long) +@ stdcall QueueUserAPC(ptr long long) +@ stdcall QueueUserWorkItem(ptr ptr long) +# @ stub QuirkGetData +# @ stub QuirkGetData2 +@ stdcall QuirkIsEnabled(ptr) +# @ stub QuirkIsEnabled2 +@ stdcall QuirkIsEnabled3(ptr ptr) +# @ stub QuirkIsEnabledForPackage +# @ stub QuirkIsEnabledForPackage2 +# @ stub QuirkIsEnabledForPackage3 +# @ stub QuirkIsEnabledForPackage4 +# @ stub QuirkIsEnabledForProcess +@ stdcall RaiseException(long long long ptr) +@ stdcall RaiseFailFastException(ptr ptr long) +@ stdcall ReOpenFile(ptr long long long) +@ stdcall ReadConsoleA(long ptr long ptr ptr) +@ stdcall ReadConsoleInputA(long ptr long ptr) +@ stub ReadConsoleInputExA +@ stub ReadConsoleInputExW +@ stdcall ReadConsoleInputW(long ptr long ptr) +@ stdcall ReadConsoleOutputA(long ptr long long ptr) +@ stdcall ReadConsoleOutputAttribute(long ptr long long ptr) +@ stdcall ReadConsoleOutputCharacterA(long ptr long long ptr) +@ stdcall ReadConsoleOutputCharacterW(long ptr long long ptr) +@ stdcall ReadConsoleOutputW(long ptr long long ptr) +@ stdcall ReadConsoleW(long ptr long ptr ptr) +@ stdcall ReadDirectoryChangesW(long ptr long long long ptr ptr ptr) +@ stdcall ReadFile(long ptr long ptr ptr) +@ stdcall ReadFileEx(long ptr long ptr ptr) +@ stdcall ReadFileScatter(long ptr long ptr ptr) +@ stdcall ReadProcessMemory(long ptr ptr long ptr) +# @ stub ReadStateAtomValue +# @ stub ReadStateContainerValue +# @ stub ReclaimVirtualMemory +# @ stub RefreshPolicyExInternal +# @ stub RefreshPolicyInternal +@ stdcall RegCloseKey(long) +@ stdcall RegCopyTreeW(long wstr long) +@ stdcall RegCreateKeyExA(long str long ptr long long ptr ptr ptr) +# @ stub RegCreateKeyExInternalA +# @ stub RegCreateKeyExInternalW +@ stdcall RegCreateKeyExW(long wstr long ptr long long ptr ptr ptr) +@ stdcall RegDeleteKeyExA(long str long long) +# @ stub RegDeleteKeyExInternalA +# @ stub RegDeleteKeyExInternalW +@ stdcall RegDeleteKeyExW(long wstr long long) +@ stdcall RegDeleteKeyValueA(long str str) +@ stdcall RegDeleteKeyValueW(long wstr wstr) +@ stdcall RegDeleteTreeA(long str) +@ stdcall RegDeleteTreeW(long wstr) +@ stdcall RegDeleteValueA(long str) +@ stdcall RegDeleteValueW(long wstr) +# @ stub RegDisablePredefinedCacheEx +@ stdcall RegEnumKeyExA(long long ptr ptr ptr ptr ptr ptr) +@ stdcall RegEnumKeyExW(long long ptr ptr ptr ptr ptr ptr) +@ stdcall RegEnumValueA(long long ptr ptr ptr ptr ptr ptr) +@ stdcall RegEnumValueW(long long ptr ptr ptr ptr ptr ptr) +@ stdcall RegFlushKey(long) +@ stdcall RegGetKeySecurity(long long ptr ptr) +@ stdcall RegGetValueA(long str str long ptr ptr ptr) +@ stdcall RegGetValueW(long wstr wstr long ptr ptr ptr) +# @ stub RegKrnGetAppKeyEventAddressInternal +# @ stub RegKrnGetAppKeyLoaded +# @ stub RegKrnGetClassesEnumTableAddressInternal +# @ stub RegKrnGetHKEY_ClassesRootAddress +# @ stub RegKrnGetTermsrvRegistryExtensionFlags +# @ stub RegKrnResetAppKeyLoaded +# @ stub RegKrnSetDllHasThreadStateGlobal +# @ stub RegKrnSetTermsrvRegistryExtensionFlags +@ stdcall RegLoadAppKeyA(str ptr long long long) +@ stdcall RegLoadAppKeyW(wstr ptr long long long) +@ stdcall RegLoadKeyA(long str str) +@ stdcall RegLoadKeyW(long wstr wstr) +@ stdcall RegLoadMUIStringA(long str str long ptr long str) +@ stdcall RegLoadMUIStringW(long wstr wstr long ptr long wstr) +@ stdcall RegNotifyChangeKeyValue(long long long long long) +@ stdcall RegOpenCurrentUser(long ptr) +@ stdcall RegOpenKeyExA(long str long long ptr) +# @ stub RegOpenKeyExInternalA +# @ stub RegOpenKeyExInternalW +@ stdcall RegOpenKeyExW(long wstr long long ptr) +@ stdcall RegOpenUserClassesRoot(ptr long long ptr) +@ stdcall RegQueryInfoKeyA(long ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall RegQueryInfoKeyW(long ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall RegQueryValueExA(long str ptr ptr ptr ptr) +@ stdcall RegQueryValueExW(long wstr ptr ptr ptr ptr) +@ stdcall RegRestoreKeyA(long str long) +@ stdcall RegRestoreKeyW(long wstr long) +@ stdcall RegSaveKeyExA(long str ptr long) +@ stdcall RegSaveKeyExW(long wstr ptr long) +@ stdcall RegSetKeySecurity(long long ptr) +@ stdcall RegSetKeyValueA(long str str long ptr long) +@ stdcall RegSetKeyValueW(long wstr wstr long ptr long) +@ stdcall RegSetValueExA(long str long long ptr long) +@ stdcall RegSetValueExW(long wstr long long ptr long) +@ stdcall RegUnLoadKeyA(long str) +@ stdcall RegUnLoadKeyW(long wstr) +# @ stub RegisterBadMemoryNotification +# @ stub RegisterGPNotificationInternal +# @ stub RegisterStateChangeNotification +# @ stub RegisterStateLock +@ stdcall RegisterTraceGuidsW(ptr ptr ptr long ptr wstr wstr ptr) ntdll.EtwRegisterTraceGuidsW +@ stdcall RegisterWaitForSingleObjectEx(long ptr ptr long long) +@ stdcall ReleaseActCtx(ptr) +@ stdcall ReleaseMutex(long) +@ stdcall ReleaseMutexWhenCallbackReturns(ptr long) ntdll.TpCallbackReleaseMutexOnCompletion +@ stdcall ReleaseSRWLockExclusive(ptr) ntdll.RtlReleaseSRWLockExclusive +@ stdcall ReleaseSRWLockShared(ptr) ntdll.RtlReleaseSRWLockShared +@ stdcall ReleaseSemaphore(long long ptr) +@ stdcall ReleaseSemaphoreWhenCallbackReturns(ptr long long) ntdll.TpCallbackReleaseSemaphoreOnCompletion +# @ stub ReleaseStateLock +@ stdcall RemapPredefinedHandleInternal(long long) +@ stdcall RemoveDirectoryA(str) +@ stdcall RemoveDirectoryW(wstr) +@ stdcall RemoveDllDirectory(ptr) +# @ stub RemovePackageStatus +# @ stub RemovePackageStatusForUser +@ stdcall RemoveVectoredContinueHandler(ptr) ntdll.RtlRemoveVectoredContinueHandler +@ stdcall RemoveVectoredExceptionHandler(ptr) ntdll.RtlRemoveVectoredExceptionHandler +# @ stub ReplaceFileExInternal +@ stdcall ReplaceFileW(wstr wstr wstr long ptr ptr) +@ stdcall ResetEvent(long) +# @ stub ResetState +@ stdcall ResetWriteWatch(ptr long) +@ stdcall ResizePseudoConsole(ptr long) +@ stdcall -import ResolveDelayLoadedAPI(ptr ptr ptr ptr ptr long) LdrResolveDelayLoadedAPI +# @ stub ResolveDelayLoadsFromDll +@ stdcall ResolveLocaleName(wstr ptr long) +@ stdcall RestoreLastError(long) ntdll.RtlRestoreLastWin32Error +@ stdcall ResumeThread(long) +@ stdcall RevertToSelf() +# @ stub RsopLoggingEnabledInternal +# @ stub SHCoCreateInstance +@ stdcall SHExpandEnvironmentStringsA(str ptr long) ExpandEnvironmentStringsA +@ stdcall SHExpandEnvironmentStringsW(wstr ptr long) ExpandEnvironmentStringsW +@ stdcall SHLoadIndirectString(wstr ptr long ptr) +# @ stub SHLoadIndirectStringInternal +@ stdcall SHRegCloseUSKey(ptr) +@ stdcall SHRegCreateUSKeyA(str long long ptr long) +@ stdcall SHRegCreateUSKeyW(wstr long long ptr long) +@ stdcall SHRegDeleteEmptyUSKeyA(long str long) +@ stdcall SHRegDeleteEmptyUSKeyW(long wstr long) +@ stdcall SHRegDeleteUSValueA(long str long) +@ stdcall SHRegDeleteUSValueW(long wstr long) +@ stdcall SHRegEnumUSKeyA(long long str ptr long) +@ stdcall SHRegEnumUSKeyW(long long wstr ptr long) +@ stdcall SHRegEnumUSValueA(long long ptr ptr ptr ptr ptr long) +@ stdcall SHRegEnumUSValueW(long long ptr ptr ptr ptr ptr long) +@ stdcall SHRegGetBoolUSValueA(str str long long) +@ stdcall SHRegGetBoolUSValueW(wstr wstr long long) +@ stdcall SHRegGetUSValueA(str str ptr ptr ptr long ptr long) +@ stdcall SHRegGetUSValueW(wstr wstr ptr ptr ptr long ptr long) +@ stdcall SHRegOpenUSKeyA(str long long ptr long) +@ stdcall SHRegOpenUSKeyW(wstr long long ptr long) +@ stdcall SHRegQueryInfoUSKeyA(long ptr ptr ptr ptr long) +@ stdcall SHRegQueryInfoUSKeyW(long ptr ptr ptr ptr long) +@ stdcall SHRegQueryUSValueA(long str ptr ptr ptr long ptr long) +@ stdcall SHRegQueryUSValueW(long wstr ptr ptr ptr long ptr long) +@ stdcall SHRegSetUSValueA(str str long ptr long long) +@ stdcall SHRegSetUSValueW(wstr wstr long ptr long long) +@ stdcall SHRegWriteUSValueA(long str long ptr long long) +@ stdcall SHRegWriteUSValueW(long wstr long ptr long long) +@ stdcall SHTruncateString(str long) +# @ stub SaveAlternatePackageRootPath +# @ stub SaveStateRootFolderPath +@ stdcall ScrollConsoleScreenBufferA(long ptr ptr ptr ptr) +@ stdcall ScrollConsoleScreenBufferW(long ptr ptr ptr ptr) +@ stdcall SearchPathA(str str str long ptr ptr) +@ stdcall SearchPathW(wstr wstr wstr long ptr ptr) +@ stdcall SetAclInformation(ptr ptr long long) +@ stdcall SetCachedSigningLevel(ptr long long long) +@ stdcall SetCalendarInfoW(long long long wstr) +# @ stub SetClientDynamicTimeZoneInformation +# @ stub SetClientTimeZoneInformation +@ stdcall SetCommBreak(long) +@ stdcall SetCommConfig(long ptr long) +@ stdcall SetCommMask(long long) +@ stdcall SetCommState(long ptr) +@ stdcall SetCommTimeouts(long ptr) +@ stdcall SetComputerNameA(str) +# @ stub SetComputerNameEx2W +@ stdcall SetComputerNameExA(long str) +@ stdcall SetComputerNameExW(long wstr) +@ stdcall SetComputerNameW(wstr) +@ stdcall SetConsoleActiveScreenBuffer(long) +@ stdcall SetConsoleCP(long) +@ stdcall SetConsoleCtrlHandler(ptr long) +@ stdcall SetConsoleCursorInfo(long ptr) +@ stdcall SetConsoleCursorPosition(long long) +@ stdcall SetConsoleDisplayMode(long long ptr) +@ stdcall SetConsoleInputExeNameA(str) +@ stdcall SetConsoleInputExeNameW(wstr) +@ stdcall SetConsoleMode(long long) +@ stdcall SetConsoleOutputCP(long) +@ stdcall SetConsoleScreenBufferInfoEx(long ptr) +@ stdcall SetConsoleScreenBufferSize(long long) +@ stdcall SetConsoleTextAttribute(long long) +@ stdcall SetConsoleTitleA(str) +@ stdcall SetConsoleTitleW(wstr) +@ stdcall SetConsoleWindowInfo(long long ptr) +@ stdcall SetCriticalSectionSpinCount(ptr long) ntdll.RtlSetCriticalSectionSpinCount +@ stdcall SetCurrentConsoleFontEx(long long ptr) +@ stdcall SetCurrentDirectoryA(str) +@ stdcall SetCurrentDirectoryW(wstr) +@ stdcall SetDefaultDllDirectories(long) +# @ stub SetDynamicTimeZoneInformation +@ stdcall SetEndOfFile(long) +@ stdcall SetEnvironmentStringsA(str) +@ stdcall SetEnvironmentStringsW(wstr) +@ stdcall SetEnvironmentVariableA(str str) +@ stdcall SetEnvironmentVariableW(wstr wstr) +@ stdcall SetErrorMode(long) +@ stdcall SetEvent(long) +@ stdcall SetEventWhenCallbackReturns(ptr long) ntdll.TpCallbackSetEventOnCompletion +@ stdcall SetFileApisToANSI() +@ stdcall SetFileApisToOEM() +@ stdcall SetFileAttributesA(str long) +@ stdcall SetFileAttributesW(wstr long) +@ stdcall SetFileInformationByHandle(long long ptr long) +# @ stub SetFileIoOverlappedRange +@ stdcall SetFilePointer(long long ptr long) +@ stdcall SetFilePointerEx(long int64 ptr long) +@ stdcall SetFileSecurityW(wstr long ptr) +@ stdcall SetFileTime(long ptr ptr ptr) +@ stdcall SetFileValidData(ptr int64) +@ stdcall SetHandleCount(long) +@ stdcall SetHandleInformation(long long long) +# @ stub SetIsDeveloperModeEnabled +# @ stub SetIsSideloadingEnabled +@ stdcall SetKernelObjectSecurity(long long ptr) +@ stub SetLastConsoleEventActive +@ stdcall SetLastError(long) ntdll.RtlSetLastWin32Error +@ stdcall SetLocalTime(ptr) +@ stdcall SetLocaleInfoW(long long wstr) +@ stdcall SetNamedPipeHandleState(long ptr ptr ptr) +@ stdcall SetPriorityClass(long long) +@ stdcall SetPrivateObjectSecurity(long ptr ptr ptr long) +@ stdcall SetPrivateObjectSecurityEx(long ptr ptr long ptr long) +@ stdcall SetProcessAffinityUpdateMode(long long) +@ stdcall SetProcessDefaultCpuSets(ptr ptr long) +@ stdcall SetProcessGroupAffinity(long ptr ptr) +@ stdcall SetProcessInformation(long long ptr long) +@ stdcall SetProcessMitigationPolicy(long ptr long) +@ stdcall SetProcessPreferredUILanguages(long ptr ptr) +@ stdcall SetProcessPriorityBoost(long long) +@ stdcall SetProcessShutdownParameters(long long) +# @ stub SetProcessValidCallTargets +@ stdcall SetProcessWorkingSetSizeEx(long long long long) +# @ stub SetProtectedPolicy +# @ stub SetRoamingLastObservedChangeTime +@ stub SetSecurityAccessMask +@ stdcall SetSecurityDescriptorControl(ptr long long) +@ stdcall SetSecurityDescriptorDacl(ptr long ptr long) +@ stdcall SetSecurityDescriptorGroup(ptr ptr long) +@ stdcall SetSecurityDescriptorOwner(ptr ptr long) +@ stub SetSecurityDescriptorRMControl +@ stdcall SetSecurityDescriptorSacl(ptr long ptr long) +# @ stub SetStateVersion +@ stdcall SetStdHandle(long long) +@ stdcall SetStdHandleEx(long long ptr) +@ stdcall SetSystemFileCacheSize(long long long) +@ stdcall SetSystemTime(ptr) +@ stdcall SetSystemTimeAdjustment(long long) +@ stdcall SetThreadContext(long ptr) +@ stdcall SetThreadDescription(ptr wstr) +@ stdcall SetThreadErrorMode(long ptr) +@ stdcall SetThreadGroupAffinity(long ptr ptr) +@ stdcall SetThreadIdealProcessor(long long) +@ stdcall SetThreadIdealProcessorEx(long ptr ptr) +@ stdcall SetThreadInformation(long long ptr long) +@ stdcall SetThreadLocale(long) +@ stdcall SetThreadPreferredUILanguages(long ptr ptr) +@ stdcall SetThreadPriority(long long) +@ stdcall SetThreadPriorityBoost(long long) +@ stdcall SetThreadSelectedCpuSets(ptr ptr long) +@ stdcall SetThreadStackGuarantee(ptr) +@ stdcall SetThreadToken(ptr ptr) +@ stdcall SetThreadUILanguage(long) +@ stdcall SetThreadpoolStackInformation(ptr ptr) +@ stdcall SetThreadpoolThreadMaximum(ptr long) ntdll.TpSetPoolMaxThreads +@ stdcall SetThreadpoolThreadMinimum(ptr long) ntdll.TpSetPoolMinThreads +@ stdcall SetThreadpoolTimer(ptr ptr long long) ntdll.TpSetTimer +# @ stub SetThreadpoolTimerEx +@ stdcall SetThreadpoolWait(ptr long ptr) ntdll.TpSetWait +# @ stub SetThreadpoolWaitEx +@ stdcall SetTimeZoneInformation(ptr) +@ stdcall SetTokenInformation(long long ptr long) +@ stdcall SetUnhandledExceptionFilter(ptr) +@ stdcall SetUserGeoID(long) +@ stdcall SetUserGeoName(wstr) +@ stdcall SetWaitableTimer(long ptr long ptr ptr long) +@ stdcall SetWaitableTimerEx(long ptr long ptr ptr ptr long) +@ stdcall -arch=i386,x86_64 SetXStateFeaturesMask(ptr int64) +@ stdcall SetupComm(long long long) +# @ stub SharedLocalIsEnabled +@ stdcall SignalObjectAndWait(long long long long) +@ stdcall SizeofResource(long long) +@ stdcall Sleep(long) +@ stdcall SleepConditionVariableCS(ptr ptr long) +@ stdcall SleepConditionVariableSRW(ptr ptr long long) +@ stdcall SleepEx(long long) +@ stub SpecialMBToWC +@ stdcall StartThreadpoolIo(ptr) ntdll.TpStartAsyncIoOperation +# @ stub StmAlignSize +# @ stub StmAllocateFlat +# @ stub StmCoalesceChunks +# @ stub StmDeinitialize +# @ stub StmInitialize +# @ stub StmReduceSize +# @ stub StmReserve +# @ stub StmWrite +@ stdcall StrCSpnA(str str) +@ stdcall StrCSpnIA(str str) +@ stdcall StrCSpnIW(wstr wstr) +@ stdcall StrCSpnW(wstr wstr) +@ stdcall StrCatBuffA(str str long) +@ stdcall StrCatBuffW(wstr wstr long) +@ stdcall StrCatChainW(ptr long long wstr) +@ stdcall StrChrA(str long) +# @ stub StrChrA_MB +@ stdcall StrChrIA(str long) +@ stdcall StrChrIW(wstr long) +# @ stub StrChrNIW +@ stdcall StrChrNW(wstr long long) +@ stdcall StrChrW(wstr long) +@ stdcall StrCmpCA(str str) +@ stdcall StrCmpCW(wstr wstr) +@ stdcall StrCmpICA(str str) +@ stdcall StrCmpICW(wstr wstr) +@ stdcall StrCmpIW(wstr wstr) +@ stdcall StrCmpLogicalW(wstr wstr) +@ stdcall StrCmpNA(str str long) +@ stdcall StrCmpNCA(str str long) +@ stdcall StrCmpNCW(wstr wstr long) +@ stdcall StrCmpNIA(str str long) +@ stdcall StrCmpNICA(str str long) +@ stdcall StrCmpNICW(wstr wstr long) +@ stdcall StrCmpNIW(wstr wstr long) +@ stdcall StrCmpNW(wstr wstr long) +@ stdcall StrCmpW(wstr wstr) +@ stdcall StrCpyNW(ptr wstr long) +@ stdcall StrCpyNXA(ptr str long) +@ stdcall StrCpyNXW(ptr wstr long) +@ stdcall StrDupA(str) +@ stdcall StrDupW(wstr) +@ stdcall StrIsIntlEqualA(long str str long) +@ stdcall StrIsIntlEqualW(long wstr wstr long) +@ stdcall StrPBrkA(str str) +@ stdcall StrPBrkW(wstr wstr) +@ stdcall StrRChrA(str str long) +@ stdcall StrRChrIA(str str long) +@ stdcall StrRChrIW(wstr wstr long) +@ stdcall StrRChrW(wstr wstr long) +@ stdcall StrRStrIA(str str str) +@ stdcall StrRStrIW(wstr wstr wstr) +@ stdcall StrSpnA(str str) +@ stdcall StrSpnW(wstr wstr) +@ stdcall StrStrA(str str) +@ stdcall StrStrIA(str str) +@ stdcall StrStrIW(wstr wstr) +@ stdcall StrStrNIW(wstr wstr long) +@ stdcall StrStrNW(wstr wstr long) +@ stdcall StrStrW(wstr wstr) +@ stdcall StrToInt64ExA(str long ptr) +@ stdcall StrToInt64ExW(wstr long ptr) +@ stdcall StrToIntA(str) +@ stdcall StrToIntExA(str long ptr) +@ stdcall StrToIntExW(wstr long ptr) +@ stdcall StrToIntW(wstr) +@ stdcall StrTrimA(str str) +@ stdcall StrTrimW(wstr wstr) +@ stdcall SubmitThreadpoolWork(ptr) ntdll.TpPostWork +# @ stub SubscribeEdpEnabledStateChange +# @ stub SubscribeStateChangeNotification +@ stdcall SuspendThread(long) +@ stdcall SwitchToFiber(ptr) +@ stdcall SwitchToThread() +@ stdcall SystemTimeToFileTime(ptr ptr) +@ stdcall SystemTimeToTzSpecificLocalTime(ptr ptr ptr) +@ stub SystemTimeToTzSpecificLocalTimeEx +@ stdcall TerminateProcess(long long) +# @ stub TerminateProcessOnMemoryExhaustion +@ stdcall TerminateThread(long long) +@ stdcall TlsAlloc() +@ stdcall TlsFree(long) +@ stdcall TlsGetValue(long) +@ stdcall TlsSetValue(long ptr) +@ stdcall TraceEvent(int64 ptr) ntdll.EtwLogTraceEvent +@ varargs TraceMessage(int64 long ptr long) ntdll.EtwTraceMessage +@ stdcall TraceMessageVa(int64 long ptr long ptr) ntdll.EtwTraceMessageVa +@ stdcall TransactNamedPipe(long ptr long ptr long ptr ptr) +@ stdcall TransmitCommChar(long long) +@ stdcall TryAcquireSRWLockExclusive(ptr) ntdll.RtlTryAcquireSRWLockExclusive +@ stdcall TryAcquireSRWLockShared(ptr) ntdll.RtlTryAcquireSRWLockShared +@ stdcall TryEnterCriticalSection(ptr) ntdll.RtlTryEnterCriticalSection +@ stdcall TrySubmitThreadpoolCallback(ptr ptr ptr) +@ stdcall TzSpecificLocalTimeToSystemTime(ptr ptr ptr) +@ stub TzSpecificLocalTimeToSystemTimeEx +@ stdcall UnhandledExceptionFilter(ptr) +@ stdcall UnlockFile(long long long long long) +@ stdcall UnlockFileEx(long long long long ptr) +@ stdcall UnmapViewOfFile(ptr) +@ stdcall UnmapViewOfFile2(long ptr long) +@ stdcall UnmapViewOfFileEx(ptr long) +# @ stub UnregisterBadMemoryNotification +# @ stub UnregisterGPNotificationInternal +# @ stub UnregisterStateChangeNotification +# @ stub UnregisterStateLock +@ stdcall UnregisterTraceGuids(int64) ntdll.EtwUnregisterTraceGuids +@ stdcall UnregisterWaitEx(long long) +# @ stub UnsubscribeEdpEnabledStateChange +# @ stub UnsubscribeStateChangeNotification +# @ stub UpdatePackageStatus +# @ stub UpdatePackageStatusForUser +@ stdcall UpdateProcThreadAttribute(ptr long long ptr long ptr ptr) +@ stdcall UrlApplySchemeA(str ptr ptr long) +@ stdcall UrlApplySchemeW(wstr ptr ptr long) +@ stdcall UrlCanonicalizeA(str ptr ptr long) +@ stdcall UrlCanonicalizeW(wstr ptr ptr long) +@ stdcall UrlCombineA(str str ptr ptr long) +@ stdcall UrlCombineW(wstr wstr ptr ptr long) +@ stdcall UrlCompareA(str str long) +@ stdcall UrlCompareW(wstr wstr long) +@ stdcall UrlCreateFromPathA(str ptr ptr long) +@ stdcall UrlCreateFromPathW(wstr ptr ptr long) +@ stdcall UrlEscapeA(str ptr ptr long) +@ stdcall UrlEscapeW(wstr ptr ptr long) +@ stdcall UrlFixupW(wstr wstr long) +@ stdcall UrlGetLocationA(str) +@ stdcall UrlGetLocationW(wstr) +@ stdcall UrlGetPartA(str ptr ptr long long) +@ stdcall UrlGetPartW(wstr ptr ptr long long) +@ stdcall UrlHashA(str ptr long) +@ stdcall UrlHashW(wstr ptr long) +@ stdcall UrlIsA(str long) +@ stdcall UrlIsNoHistoryA(str) +@ stdcall UrlIsNoHistoryW(wstr) +@ stdcall UrlIsOpaqueA(str) +@ stdcall UrlIsOpaqueW(wstr) +@ stdcall UrlIsW(wstr long) +@ stdcall UrlUnescapeA(str ptr ptr long) +@ stdcall UrlUnescapeW(wstr ptr ptr long) +@ stdcall VerFindFileA(long str str str ptr ptr ptr ptr) +@ stdcall VerFindFileW(long wstr wstr wstr ptr ptr ptr ptr) +@ stdcall VerLanguageNameA(long str long) +@ stdcall VerLanguageNameW(long wstr long) +@ stdcall VerQueryValueA(ptr str ptr ptr) +@ stdcall VerQueryValueW(ptr wstr ptr ptr) +@ stdcall -ret64 VerSetConditionMask(long long long long) ntdll.VerSetConditionMask +# @ stub VerifyApplicationUserModelId +# @ stub VerifyPackageFamilyName +# @ stub VerifyPackageFullName +# @ stub VerifyPackageId +# @ stub VerifyPackageRelativeApplicationId +# @ stub VerifyScripts +@ stdcall VirtualAlloc2(long ptr long long long ptr long) +@ stdcall VirtualAlloc2FromApp(long ptr long long long ptr long) +@ stdcall VirtualAlloc(ptr long long long) +@ stdcall VirtualAllocEx(long ptr long long long) +@ stdcall VirtualAllocExNuma(long ptr long long long long) +@ stdcall VirtualAllocFromApp(ptr long long long) +@ stdcall VirtualFree(ptr long long) +@ stdcall VirtualFreeEx(long ptr long long) +@ stdcall VirtualLock(ptr long) +@ stdcall VirtualProtect(ptr long long ptr) +@ stdcall VirtualProtectEx(long ptr long long ptr) +# @ stub VirtualProtectFromApp +@ stdcall VirtualQuery(ptr ptr long) +@ stdcall VirtualQueryEx(long ptr ptr long) +@ stdcall VirtualUnlock(ptr long) +# @ stub WTSGetServiceSessionId +# @ stub WTSIsServerContainer +@ stdcall WaitCommEvent(long ptr ptr) +@ stdcall WaitForDebugEvent(ptr long) +@ stdcall WaitForDebugEventEx(ptr long) +# @ stub WaitForMachinePolicyForegroundProcessingInternal +@ stdcall WaitForMultipleObjects(long ptr long long) +@ stdcall WaitForMultipleObjectsEx(long ptr long long long) +@ stdcall WaitForSingleObject(long long) +@ stdcall WaitForSingleObjectEx(long long long) +@ stdcall WaitForThreadpoolIoCallbacks(ptr long) ntdll.TpWaitForIoCompletion +@ stdcall WaitForThreadpoolTimerCallbacks(ptr long) ntdll.TpWaitForTimer +@ stdcall WaitForThreadpoolWaitCallbacks(ptr long) ntdll.TpWaitForWait +@ stdcall WaitForThreadpoolWorkCallbacks(ptr long) ntdll.TpWaitForWork +# @ stub WaitForUserPolicyForegroundProcessingInternal +@ stdcall WaitNamedPipeW(wstr long) +@ stdcall WaitOnAddress(ptr ptr long long) +@ stdcall WakeAllConditionVariable(ptr) ntdll.RtlWakeAllConditionVariable +@ stdcall WakeByAddressAll(ptr) ntdll.RtlWakeAddressAll +@ stdcall WakeByAddressSingle(ptr) ntdll.RtlWakeAddressSingle +@ stdcall WakeConditionVariable(ptr) ntdll.RtlWakeConditionVariable +@ stdcall WerGetFlags(ptr ptr) +@ stdcall WerRegisterFile(wstr long long) +@ stdcall WerRegisterMemoryBlock(ptr long) +@ stdcall WerRegisterRuntimeExceptionModule(wstr ptr) +@ stdcall WerSetFlags(long) +@ stdcall WerUnregisterFile(wstr) +@ stdcall WerUnregisterMemoryBlock(ptr) +@ stdcall WerUnregisterRuntimeExceptionModule(wstr ptr) +# @ stub WerpNotifyLoadStringResource +# @ stub WerpNotifyUseStringResource +@ stdcall WideCharToMultiByte(long long wstr long ptr long ptr ptr) +@ stdcall Wow64DisableWow64FsRedirection(ptr) +@ stdcall Wow64EnableWow64FsRedirection(long) kernelbase_Wow64EnableWow64FsRedirection +@ stdcall Wow64GetThreadContext(long ptr) +@ stdcall Wow64RevertWow64FsRedirection(ptr) +@ stdcall Wow64SetThreadContext(long ptr) +# @ stub Wow64SetThreadDefaultGuestMachine +# @ stub Wow64SuspendThread +# @ stub -arch=i386 Wow64Transition +@ stdcall WriteConsoleA(long ptr long ptr ptr) +@ stdcall WriteConsoleInputA(long ptr long ptr) +@ stdcall WriteConsoleInputW(long ptr long ptr) +@ stdcall WriteConsoleOutputA(long ptr long long ptr) +@ stdcall WriteConsoleOutputAttribute(long ptr long long ptr) +@ stdcall WriteConsoleOutputCharacterA(long ptr long long ptr) +@ stdcall WriteConsoleOutputCharacterW(long ptr long long ptr) +@ stdcall WriteConsoleOutputW(long ptr long long ptr) +@ stdcall WriteConsoleW(long ptr long ptr ptr) +@ stdcall WriteFile(long ptr long ptr ptr) +@ stdcall WriteFileEx(long ptr long ptr ptr) +@ stdcall WriteFileGather(long ptr long ptr ptr) +@ stdcall WriteProcessMemory(long ptr ptr long ptr) +# @ stub WriteStateAtomValue +# @ stub WriteStateContainerValue +@ stdcall ZombifyActCtx(ptr) +# @ stub _AddMUIStringToCache +# @ stub _GetMUIStringFromCache +# @ stub _OpenMuiStringCache +@ stdcall -arch=!i386 -private __C_specific_handler(ptr long ptr ptr) ntdll.__C_specific_handler +@ cdecl -arch=!i386 -norelay __chkstk() ntdll.__chkstk +# @ stub __dllonexit3 +@ stub __misaligned_access +# @ stub __wgetmainargs +# @ stub _amsg_exit +# @ stub _c_exit +# @ stub _cexit +# @ stub _exit +# @ stub _initterm +# @ stub _initterm_e +# @ stub _invalid_parameter +@ stdcall -arch=x86_64 -private _local_unwind(ptr ptr) ntdll._local_unwind +# @ stub _onexit +# @ stub _purecall +# @ stub _time64 +# @ stub atexit +# @ stub exit +# @ stub hgets +# @ stub hwprintf +@ stdcall lstrcmp(str str) lstrcmpA +@ stdcall lstrcmpA(str str) +@ stdcall lstrcmpW(wstr wstr) +@ stdcall lstrcmpi(str str) lstrcmpiA +@ stdcall lstrcmpiA(str str) +@ stdcall lstrcmpiW(wstr wstr) +@ stdcall lstrcpyn(ptr str long) KERNELBASE_lstrcpynA +@ stdcall lstrcpynA(ptr str long) KERNELBASE_lstrcpynA +@ stdcall lstrcpynW(ptr wstr long) KERNELBASE_lstrcpynW +@ stdcall lstrlen(str) KERNELBASE_lstrlenA +@ stdcall lstrlenA(str) KERNELBASE_lstrlenA +@ stdcall lstrlenW(wstr) KERNELBASE_lstrlenW +# @ stub time +# @ stub wprintf diff --git a/dll/win32/KernelBase/wine/loader.c b/dll/win32/KernelBase/wine/loader.c new file mode 100644 index 0000000000000..7afbe0460ebbe --- /dev/null +++ b/dll/win32/KernelBase/wine/loader.c @@ -0,0 +1,1263 @@ +/* + * Module loader + * + * Copyright 1993 Robert J. Amstadt + * Copyright 2006 Mike McCormack + * Copyright 1995, 2003, 2019 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winternl.h" +#include "ddk/ntddk.h" +#include "kernelbase.h" +#include "wine/list.h" +#include "wine/asm.h" +#include "wine/debug.h" +#include "wine/exception.h" + +WINE_DEFAULT_DEBUG_CHANNEL(module); + + +/* to keep track of LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE file handles */ +struct exclusive_datafile +{ + struct list entry; + HMODULE module; + HANDLE file; +}; +static struct list exclusive_datafile_list = LIST_INIT( exclusive_datafile_list ); + +static CRITICAL_SECTION exclusive_datafile_list_section; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &exclusive_datafile_list_section, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": exclusive_datafile_list_section") } +}; +static CRITICAL_SECTION exclusive_datafile_list_section = { &critsect_debug, -1, 0, 0, 0, 0 }; + +/*********************************************************************** + * Modules + ***********************************************************************/ + + +/****************************************************************** + * get_proc_address + */ +FARPROC WINAPI get_proc_address( HMODULE module, LPCSTR function ) +{ + FARPROC proc; + ANSI_STRING str; + + if (!module) module = NtCurrentTeb()->Peb->ImageBaseAddress; + + if ((ULONG_PTR)function >> 16) + { + RtlInitAnsiString( &str, function ); + if (!set_ntstatus( LdrGetProcedureAddress( module, &str, 0, (void**)&proc ))) return NULL; + } + else if (!set_ntstatus( LdrGetProcedureAddress( module, NULL, LOWORD(function), (void**)&proc ))) + return NULL; + + return proc; +} + + +/****************************************************************** + * load_library_as_datafile + */ +static BOOL load_library_as_datafile( LPCWSTR load_path, DWORD flags, LPCWSTR name, HMODULE *mod_ret ) +{ + WCHAR filenameW[MAX_PATH]; + HANDLE mapping, file = INVALID_HANDLE_VALUE; + HMODULE module = 0; + DWORD protect = PAGE_READONLY; + + *mod_ret = 0; + + if (flags & LOAD_LIBRARY_AS_IMAGE_RESOURCE) protect |= SEC_IMAGE; + + if (SearchPathW( NULL, name, L".dll", ARRAY_SIZE( filenameW ), filenameW, NULL )) + { + file = CreateFileW( filenameW, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, 0 ); + } + if (file == INVALID_HANDLE_VALUE) return FALSE; + + mapping = CreateFileMappingW( file, NULL, protect, 0, 0, NULL ); + if (!mapping) goto failed; + + module = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 ); + CloseHandle( mapping ); + if (!module) goto failed; + + if (!(flags & LOAD_LIBRARY_AS_IMAGE_RESOURCE)) + { + /* make sure it's a valid PE file */ + if (!RtlImageNtHeader( module )) + { + SetLastError( ERROR_BAD_EXE_FORMAT ); + goto failed; + } + *mod_ret = (HMODULE)((char *)module + 1); /* set bit 0 for data file module */ + + if (flags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE) + { + struct exclusive_datafile *datafile = HeapAlloc( GetProcessHeap(), 0, sizeof(*datafile) ); + if (!datafile) goto failed; + datafile->module = *mod_ret; + datafile->file = file; + RtlEnterCriticalSection( &exclusive_datafile_list_section ); + list_add_head( &exclusive_datafile_list, &datafile->entry ); + RtlLeaveCriticalSection( &exclusive_datafile_list_section ); + TRACE( "delaying close %p for module %p\n", datafile->file, datafile->module ); + return TRUE; + } + } + else *mod_ret = (HMODULE)((char *)module + 2); /* set bit 1 for image resource module */ + + CloseHandle( file ); + return TRUE; + +failed: + if (module) UnmapViewOfFile( module ); + CloseHandle( file ); + return FALSE; +} + + +/****************************************************************** + * load_library + */ +static HMODULE load_library( const UNICODE_STRING *libname, DWORD flags ) +{ + const DWORD unsupported_flags = LOAD_IGNORE_CODE_AUTHZ_LEVEL | LOAD_LIBRARY_REQUIRE_SIGNED_TARGET; + NTSTATUS status; + HMODULE module; + WCHAR *load_path, *dummy; + + if (flags & unsupported_flags) FIXME( "unsupported flag(s) used %#08lx\n", flags ); + + if (!set_ntstatus( LdrGetDllPath( libname->Buffer, flags, &load_path, &dummy ))) return 0; + + if (flags & (LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | + LOAD_LIBRARY_AS_IMAGE_RESOURCE)) + { + if (LdrGetDllHandleEx( 0, load_path, NULL, libname, &module )) + load_library_as_datafile( load_path, flags, libname->Buffer, &module ); + } + else + { + status = LdrLoadDll( load_path, flags, libname, &module ); + if (!set_ntstatus( status )) + { + module = 0; + if (status == STATUS_DLL_NOT_FOUND && (GetVersion() & 0x80000000)) + SetLastError( ERROR_DLL_NOT_FOUND ); + } + } + + RtlReleasePath( load_path ); + return module; +} + + +/**************************************************************************** + * AddDllDirectory (kernelbase.@) + */ +DLL_DIRECTORY_COOKIE WINAPI DECLSPEC_HOTPATCH AddDllDirectory( const WCHAR *dir ) +{ + UNICODE_STRING str; + void *cookie; + + RtlInitUnicodeString( &str, dir ); + if (!set_ntstatus( LdrAddDllDirectory( &str, &cookie ))) return NULL; + return cookie; +} + + +/*********************************************************************** + * DelayLoadFailureHook (kernelbase.@) + */ +FARPROC WINAPI DECLSPEC_HOTPATCH DelayLoadFailureHook( LPCSTR name, LPCSTR function ) +{ + ULONG_PTR args[2]; + + if ((ULONG_PTR)function >> 16) + ERR( "failed to delay load %s.%s\n", name, function ); + else + ERR( "failed to delay load %s.%u\n", name, LOWORD(function) ); + args[0] = (ULONG_PTR)name; + args[1] = (ULONG_PTR)function; + RaiseException( EXCEPTION_WINE_STUB, EXCEPTION_NONCONTINUABLE, 2, args ); + return NULL; +} + + +/**************************************************************************** + * DisableThreadLibraryCalls (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DisableThreadLibraryCalls( HMODULE module ) +{ + return set_ntstatus( LdrDisableThreadCalloutsForDll( module )); +} + + +/*********************************************************************** + * FreeLibrary (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FreeLibrary( HINSTANCE module ) +{ + if (!module) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + if ((ULONG_PTR)module & 3) /* this is a datafile module */ + { + void *ptr = (void *)((ULONG_PTR)module & ~3); + if (!RtlImageNtHeader( ptr )) + { + SetLastError( ERROR_BAD_EXE_FORMAT ); + return FALSE; + } + if ((ULONG_PTR)module & 1) + { + struct exclusive_datafile *file; + + RtlEnterCriticalSection( &exclusive_datafile_list_section ); + LIST_FOR_EACH_ENTRY( file, &exclusive_datafile_list, struct exclusive_datafile, entry ) + { + if (file->module != module) continue; + TRACE( "closing %p for module %p\n", file->file, file->module ); + CloseHandle( file->file ); + list_remove( &file->entry ); + HeapFree( GetProcessHeap(), 0, file ); + break; + } + RtlLeaveCriticalSection( &exclusive_datafile_list_section ); + } + return UnmapViewOfFile( ptr ); + } + + return set_ntstatus( LdrUnloadDll( module )); +} + + +/*********************************************************************** + * GetModuleFileNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameA( HMODULE module, LPSTR filename, DWORD size ) +{ + LPWSTR filenameW = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ); + DWORD len; + + if (!filenameW) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return 0; + } + if ((len = GetModuleFileNameW( module, filenameW, size ))) + { + len = file_name_WtoA( filenameW, len, filename, size ); + if (len < size) + filename[len] = 0; + else + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + } + HeapFree( GetProcessHeap(), 0, filenameW ); + return len; +} + + +/*********************************************************************** + * GetModuleFileNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetModuleFileNameW( HMODULE module, LPWSTR filename, DWORD size ) +{ + ULONG len = 0; + WIN16_SUBSYSTEM_TIB *win16_tib; + UNICODE_STRING name; + NTSTATUS status; + + if (!module && ((win16_tib = NtCurrentTeb()->Tib.SubSystemTib)) && win16_tib->exe_name) + { + len = min( size, win16_tib->exe_name->Length / sizeof(WCHAR) ); + memcpy( filename, win16_tib->exe_name->Buffer, len * sizeof(WCHAR) ); + if (len < size) filename[len] = 0; + goto done; + } + + name.Buffer = filename; + name.MaximumLength = min( size, UNICODE_STRING_MAX_CHARS ) * sizeof(WCHAR); + status = LdrGetDllFullName( module, &name ); + if (!status || status == STATUS_BUFFER_TOO_SMALL) len = name.Length / sizeof(WCHAR); + SetLastError( RtlNtStatusToDosError( status )); +done: + TRACE( "%s\n", debugstr_wn(filename, len) ); + return len; +} + + +/*********************************************************************** + * GetModuleHandleA (kernelbase.@) + */ +HMODULE WINAPI DECLSPEC_HOTPATCH GetModuleHandleA( LPCSTR module ) +{ + HMODULE ret; + + GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, module, &ret ); + return ret; +} + + +/*********************************************************************** + * GetModuleHandleW (kernelbase.@) + */ +HMODULE WINAPI DECLSPEC_HOTPATCH GetModuleHandleW( LPCWSTR module ) +{ + HMODULE ret; + + GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, module, &ret ); + return ret; +} + + +/*********************************************************************** + * GetModuleHandleExA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetModuleHandleExA( DWORD flags, LPCSTR name, HMODULE *module ) +{ + WCHAR *nameW; + + if (!module) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (!name || (flags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)) + return GetModuleHandleExW( flags, (LPCWSTR)name, module ); + + if (!(nameW = file_name_AtoW( name, FALSE ))) + { + *module = NULL; + SetLastError( ERROR_MOD_NOT_FOUND ); + return FALSE; + } + return GetModuleHandleExW( flags, nameW, module ); +} + + +/*********************************************************************** + * GetModuleHandleExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetModuleHandleExW( DWORD flags, LPCWSTR name, HMODULE *module ) +{ + HMODULE ret = NULL; + NTSTATUS status; + void *dummy; + + if (!module) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if ((flags & ~(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT + | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)) + || (flags & (GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) + == (GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) + { + *module = NULL; + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (name && !(flags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)) + { + UNICODE_STRING wstr; + ULONG ldr_flags = 0; + + if (flags & GET_MODULE_HANDLE_EX_FLAG_PIN) + ldr_flags |= LDR_GET_DLL_HANDLE_EX_FLAG_PIN; + if (flags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) + ldr_flags |= LDR_GET_DLL_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; + + RtlInitUnicodeString( &wstr, name ); + status = LdrGetDllHandleEx( ldr_flags, NULL, NULL, &wstr, &ret ); + } + else + { + ret = name ? RtlPcToFileHeader( (void *)name, &dummy ) : NtCurrentTeb()->Peb->ImageBaseAddress; + + if (ret) + { + if (!(flags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) + status = LdrAddRefDll( flags & GET_MODULE_HANDLE_EX_FLAG_PIN ? LDR_ADDREF_DLL_PIN : 0, ret ); + else + status = STATUS_SUCCESS; + } else status = STATUS_DLL_NOT_FOUND; + } + + *module = ret; + return set_ntstatus( status ); +} + + +/*********************************************************************** + * GetProcAddress (kernelbase.@) + */ + +/* + * Work around a Delphi bug on x86_64. When delay loading a symbol, + * Delphi saves rcx, rdx, r8 and r9 to the stack. It then calls + * GetProcAddress(), pops the saved registers and calls the function. + * This works fine if all of the parameters are ints. However, since + * it does not save xmm0 - 3, it relies on GetProcAddress() preserving + * these registers if the function takes floating point parameters. + * This wrapper saves xmm0 - 3 to the stack. + */ +#ifdef __arm64ec__ +FARPROC WINAPI __attribute__((naked)) GetProcAddress( HMODULE module, LPCSTR function ) +{ + asm( ".seh_proc \"#GetProcAddress\"\n\t" + "stp x29, x30, [sp, #-48]!\n\t" + ".seh_save_fplr_x 48\n\t" + ".seh_endprologue\n\t" + "stp d0, d1, [sp, #16]\n\t" + "stp d2, d3, [sp, #32]\n\t" + "bl \"#get_proc_address\"\n\t" + "ldp d0, d1, [sp, #16]\n\t" + "ldp d2, d3, [sp, #32]\n\t" + "ldp x29, x30, [sp], #48\n\t" + "ret\n\t" + ".seh_endproc" ); +} +#elif defined(__x86_64__) +__ASM_GLOBAL_FUNC( GetProcAddress, + ".byte 0x48\n\t" /* hotpatch prolog */ + "pushq %rbp\n\t" + __ASM_SEH(".seh_pushreg %rbp\n\t") + __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") + __ASM_CFI(".cfi_rel_offset %rbp,0\n\t") + "movq %rsp,%rbp\n\t" + __ASM_SEH(".seh_setframe %rbp,0\n\t") + __ASM_CFI(".cfi_def_cfa_register %rbp\n\t") + __ASM_SEH(".seh_endprologue\n\t") + "subq $0x60,%rsp\n\t" + "andq $~15,%rsp\n\t" + "movaps %xmm0,0x20(%rsp)\n\t" + "movaps %xmm1,0x30(%rsp)\n\t" + "movaps %xmm2,0x40(%rsp)\n\t" + "movaps %xmm3,0x50(%rsp)\n\t" + "call " __ASM_NAME("get_proc_address") "\n\t" + "movaps 0x50(%rsp), %xmm3\n\t" + "movaps 0x40(%rsp), %xmm2\n\t" + "movaps 0x30(%rsp), %xmm1\n\t" + "movaps 0x20(%rsp), %xmm0\n\t" + "leaq 0(%rbp),%rsp\n\t" + __ASM_CFI(".cfi_def_cfa_register %rsp\n\t") + "popq %rbp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") + __ASM_CFI(".cfi_same_value %rbp\n\t") + "ret" ) +#else /* __x86_64__ */ + +FARPROC WINAPI DECLSPEC_HOTPATCH GetProcAddress( HMODULE module, LPCSTR function ) +{ + return get_proc_address( module, function ); +} + +#endif /* __x86_64__ */ + + +/*********************************************************************** + * IsApiSetImplemented (kernelbase.@) + */ +BOOL WINAPI IsApiSetImplemented( LPCSTR name ) +{ + UNICODE_STRING str; + NTSTATUS status; + BOOLEAN in_schema, present; + + if (!RtlCreateUnicodeStringFromAsciiz( &str, name )) return FALSE; + status = ApiSetQueryApiSetPresenceEx( &str, &in_schema, &present ); + RtlFreeUnicodeString( &str ); + return !status && present; +} + + +/*********************************************************************** + * LoadLibraryA (kernelbase.@) + */ +HMODULE WINAPI DECLSPEC_HOTPATCH LoadLibraryA( LPCSTR name ) +{ + return LoadLibraryExA( name, 0, 0 ); +} + + +/*********************************************************************** + * LoadLibraryW (kernelbase.@) + */ +HMODULE WINAPI DECLSPEC_HOTPATCH LoadLibraryW( LPCWSTR name ) +{ + return LoadLibraryExW( name, 0, 0 ); +} + + +/****************************************************************** + * LoadLibraryExA (kernelbase.@) + */ +HMODULE WINAPI DECLSPEC_HOTPATCH LoadLibraryExA( LPCSTR name, HANDLE file, DWORD flags ) +{ + WCHAR *nameW; + + if (!(nameW = file_name_AtoW( name, FALSE ))) return 0; + return LoadLibraryExW( nameW, file, flags ); +} + + +/*********************************************************************** + * LoadLibraryExW (kernelbase.@) + */ +HMODULE WINAPI DECLSPEC_HOTPATCH LoadLibraryExW( LPCWSTR name, HANDLE file, DWORD flags ) +{ + UNICODE_STRING str; + HMODULE module; + + if (!name) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + RtlInitUnicodeString( &str, name ); + if (str.Buffer[str.Length/sizeof(WCHAR) - 1] != ' ') return load_library( &str, flags ); + + /* library name has trailing spaces */ + RtlCreateUnicodeString( &str, name ); + while (str.Length > sizeof(WCHAR) && str.Buffer[str.Length/sizeof(WCHAR) - 1] == ' ') + str.Length -= sizeof(WCHAR); + + str.Buffer[str.Length/sizeof(WCHAR)] = 0; + module = load_library( &str, flags ); + RtlFreeUnicodeString( &str ); + return module; +} + + +/*********************************************************************** + * LoadPackagedLibrary (kernelbase.@) + */ +HMODULE WINAPI /* DECLSPEC_HOTPATCH */ LoadPackagedLibrary( LPCWSTR name, DWORD reserved ) +{ + FIXME( "semi-stub, name %s, reserved %#lx.\n", debugstr_w(name), reserved ); + SetLastError( APPMODEL_ERROR_NO_PACKAGE ); + return NULL; +} + + +/*********************************************************************** + * LoadAppInitDlls (kernelbase.@) + */ +void WINAPI LoadAppInitDlls(void) +{ + TRACE( "\n" ); +} + + +/**************************************************************************** + * RemoveDllDirectory (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH RemoveDllDirectory( DLL_DIRECTORY_COOKIE cookie ) +{ + return set_ntstatus( LdrRemoveDllDirectory( cookie )); +} + + +/************************************************************************* + * SetDefaultDllDirectories (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetDefaultDllDirectories( DWORD flags ) +{ + return set_ntstatus( LdrSetDefaultDllDirectories( flags )); +} + + +/*********************************************************************** + * Resources + ***********************************************************************/ + + +#define IS_INTRESOURCE(x) (((ULONG_PTR)(x) >> 16) == 0) + +/* retrieve the resource name to pass to the ntdll functions */ +static NTSTATUS get_res_nameA( LPCSTR name, UNICODE_STRING *str ) +{ + if (IS_INTRESOURCE(name)) + { + str->Buffer = ULongToPtr( LOWORD(name) ); + return STATUS_SUCCESS; + } + if (name[0] == '#') + { + ULONG value; + if (RtlCharToInteger( name + 1, 10, &value ) != STATUS_SUCCESS || HIWORD(value)) + return STATUS_INVALID_PARAMETER; + str->Buffer = ULongToPtr(value); + return STATUS_SUCCESS; + } + RtlCreateUnicodeStringFromAsciiz( str, name ); + RtlUpcaseUnicodeString( str, str, FALSE ); + return STATUS_SUCCESS; +} + +/* retrieve the resource name to pass to the ntdll functions */ +static NTSTATUS get_res_nameW( LPCWSTR name, UNICODE_STRING *str ) +{ + if (IS_INTRESOURCE(name)) + { + str->Buffer = ULongToPtr( LOWORD(name) ); + return STATUS_SUCCESS; + } + if (name[0] == '#') + { + ULONG value; + RtlInitUnicodeString( str, name + 1 ); + if (RtlUnicodeStringToInteger( str, 10, &value ) != STATUS_SUCCESS || HIWORD(value)) + return STATUS_INVALID_PARAMETER; + str->Buffer = ULongToPtr(value); + return STATUS_SUCCESS; + } + RtlCreateUnicodeString( str, name ); + RtlUpcaseUnicodeString( str, str, FALSE ); + return STATUS_SUCCESS; +} + + +/********************************************************************** + * EnumResourceLanguagesExA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumResourceLanguagesExA( HMODULE module, LPCSTR type, LPCSTR name, + ENUMRESLANGPROCA func, LONG_PTR param, + DWORD flags, LANGID lang ) +{ + int i; + BOOL ret = FALSE; + NTSTATUS status; + UNICODE_STRING typeW, nameW; + LDR_RESOURCE_INFO info; + const IMAGE_RESOURCE_DIRECTORY *basedir, *resdir; + const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; + + TRACE( "%p %s %s %p %Ix %lx %d\n", module, debugstr_a(type), debugstr_a(name), + func, param, flags, lang ); + + if (flags & (RESOURCE_ENUM_MUI | RESOURCE_ENUM_MUI_SYSTEM | RESOURCE_ENUM_VALIDATE)) + FIXME( "unimplemented flags: %lx\n", flags ); + + if (!flags) flags = RESOURCE_ENUM_LN | RESOURCE_ENUM_MUI; + if (!(flags & RESOURCE_ENUM_LN)) return ret; + + if (!module) module = GetModuleHandleW( 0 ); + typeW.Buffer = nameW.Buffer = NULL; + if ((status = LdrFindResourceDirectory_U( module, NULL, 0, &basedir )) != STATUS_SUCCESS) + goto done; + if ((status = get_res_nameA( type, &typeW )) != STATUS_SUCCESS) + goto done; + if ((status = get_res_nameA( name, &nameW )) != STATUS_SUCCESS) + goto done; + info.Type = (ULONG_PTR)typeW.Buffer; + info.Name = (ULONG_PTR)nameW.Buffer; + if ((status = LdrFindResourceDirectory_U( module, &info, 2, &resdir )) != STATUS_SUCCESS) + goto done; + + et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); + __TRY + { + for (i = 0; i < resdir->NumberOfNamedEntries + resdir->NumberOfIdEntries; i++) + { + ret = func( module, type, name, et[i].Id, param ); + if (!ret) break; + } + } + __EXCEPT_PAGE_FAULT + { + ret = FALSE; + status = STATUS_ACCESS_VIOLATION; + } + __ENDTRY +done: + if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); + if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree( GetProcessHeap(), 0, nameW.Buffer ); + if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); + return ret; +} + + +/********************************************************************** + * EnumResourceLanguagesExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumResourceLanguagesExW( HMODULE module, LPCWSTR type, LPCWSTR name, + ENUMRESLANGPROCW func, LONG_PTR param, + DWORD flags, LANGID lang ) +{ + int i; + BOOL ret = FALSE; + NTSTATUS status; + UNICODE_STRING typeW, nameW; + LDR_RESOURCE_INFO info; + const IMAGE_RESOURCE_DIRECTORY *basedir, *resdir; + const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; + + TRACE( "%p %s %s %p %Ix %lx %d\n", module, debugstr_w(type), debugstr_w(name), + func, param, flags, lang ); + + if (flags & (RESOURCE_ENUM_MUI | RESOURCE_ENUM_MUI_SYSTEM | RESOURCE_ENUM_VALIDATE)) + FIXME( "unimplemented flags: %lx\n", flags ); + + if (!flags) flags = RESOURCE_ENUM_LN | RESOURCE_ENUM_MUI; + if (!(flags & RESOURCE_ENUM_LN)) return ret; + + if (!module) module = GetModuleHandleW( 0 ); + typeW.Buffer = nameW.Buffer = NULL; + if ((status = LdrFindResourceDirectory_U( module, NULL, 0, &basedir )) != STATUS_SUCCESS) + goto done; + if ((status = get_res_nameW( type, &typeW )) != STATUS_SUCCESS) + goto done; + if ((status = get_res_nameW( name, &nameW )) != STATUS_SUCCESS) + goto done; + info.Type = (ULONG_PTR)typeW.Buffer; + info.Name = (ULONG_PTR)nameW.Buffer; + if ((status = LdrFindResourceDirectory_U( module, &info, 2, &resdir )) != STATUS_SUCCESS) + goto done; + + et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); + __TRY + { + for (i = 0; i < resdir->NumberOfNamedEntries + resdir->NumberOfIdEntries; i++) + { + ret = func( module, type, name, et[i].Id, param ); + if (!ret) break; + } + } + __EXCEPT_PAGE_FAULT + { + ret = FALSE; + status = STATUS_ACCESS_VIOLATION; + } + __ENDTRY +done: + if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); + if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree( GetProcessHeap(), 0, nameW.Buffer ); + if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); + return ret; +} + + +/********************************************************************** + * EnumResourceNamesExA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumResourceNamesExA( HMODULE module, LPCSTR type, ENUMRESNAMEPROCA func, + LONG_PTR param, DWORD flags, LANGID lang ) +{ + int i; + BOOL ret = FALSE; + DWORD len = 0, newlen; + LPSTR name = NULL; + NTSTATUS status; + UNICODE_STRING typeW; + LDR_RESOURCE_INFO info; + const IMAGE_RESOURCE_DIRECTORY *basedir, *resdir; + const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; + const IMAGE_RESOURCE_DIR_STRING_U *str; + + TRACE( "%p %s %p %Ix\n", module, debugstr_a(type), func, param ); + + if (flags & (RESOURCE_ENUM_MUI | RESOURCE_ENUM_MUI_SYSTEM | RESOURCE_ENUM_VALIDATE)) + FIXME( "unimplemented flags: %lx\n", flags ); + + if (!flags) flags = RESOURCE_ENUM_LN | RESOURCE_ENUM_MUI; + if (!(flags & RESOURCE_ENUM_LN)) return ret; + + if (!module) module = GetModuleHandleW( 0 ); + typeW.Buffer = NULL; + if ((status = LdrFindResourceDirectory_U( module, NULL, 0, &basedir )) != STATUS_SUCCESS) + goto done; + if ((status = get_res_nameA( type, &typeW )) != STATUS_SUCCESS) + goto done; + info.Type = (ULONG_PTR)typeW.Buffer; + if ((status = LdrFindResourceDirectory_U( module, &info, 1, &resdir )) != STATUS_SUCCESS) + goto done; + + et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); + __TRY + { + for (i = 0; i < resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries; i++) + { + if (et[i].NameIsString) + { + str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const BYTE *)basedir + et[i].NameOffset); + newlen = WideCharToMultiByte(CP_ACP, 0, str->NameString, str->Length, NULL, 0, NULL, NULL); + if (newlen + 1 > len) + { + len = newlen + 1; + HeapFree( GetProcessHeap(), 0, name ); + if (!(name = HeapAlloc( GetProcessHeap(), 0, len + 1 ))) + { + ret = FALSE; + break; + } + } + WideCharToMultiByte( CP_ACP, 0, str->NameString, str->Length, name, len, NULL, NULL ); + name[newlen] = 0; + ret = func( module, type, name, param ); + } + else + { + ret = func( module, type, UIntToPtr(et[i].Id), param ); + } + if (!ret) break; + } + } + __EXCEPT_PAGE_FAULT + { + ret = FALSE; + status = STATUS_ACCESS_VIOLATION; + } + __ENDTRY + +done: + HeapFree( GetProcessHeap(), 0, name ); + if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); + if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); + return ret; +} + + +/********************************************************************** + * EnumResourceNamesExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumResourceNamesExW( HMODULE module, LPCWSTR type, ENUMRESNAMEPROCW func, + LONG_PTR param, DWORD flags, LANGID lang ) +{ + int i, len = 0; + BOOL ret = FALSE; + LPWSTR name = NULL; + NTSTATUS status; + UNICODE_STRING typeW; + LDR_RESOURCE_INFO info; + const IMAGE_RESOURCE_DIRECTORY *basedir, *resdir; + const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; + const IMAGE_RESOURCE_DIR_STRING_U *str; + + TRACE( "%p %s %p %Ix\n", module, debugstr_w(type), func, param ); + + if (flags & (RESOURCE_ENUM_MUI | RESOURCE_ENUM_MUI_SYSTEM | RESOURCE_ENUM_VALIDATE)) + FIXME( "unimplemented flags: %lx\n", flags ); + + if (!flags) flags = RESOURCE_ENUM_LN | RESOURCE_ENUM_MUI; + if (!(flags & RESOURCE_ENUM_LN)) return ret; + + if (!module) module = GetModuleHandleW( 0 ); + typeW.Buffer = NULL; + if ((status = LdrFindResourceDirectory_U( module, NULL, 0, &basedir )) != STATUS_SUCCESS) + goto done; + if ((status = get_res_nameW( type, &typeW )) != STATUS_SUCCESS) + goto done; + info.Type = (ULONG_PTR)typeW.Buffer; + if ((status = LdrFindResourceDirectory_U( module, &info, 1, &resdir )) != STATUS_SUCCESS) + goto done; + + et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); + __TRY + { + for (i = 0; i < resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries; i++) + { + if (et[i].NameIsString) + { + str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const BYTE *)basedir + et[i].NameOffset); + if (str->Length + 1 > len) + { + len = str->Length + 1; + HeapFree( GetProcessHeap(), 0, name ); + if (!(name = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + { + ret = FALSE; + break; + } + } + memcpy(name, str->NameString, str->Length * sizeof (WCHAR)); + name[str->Length] = 0; + ret = func( module, type, name, param ); + } + else + { + ret = func( module, type, UIntToPtr(et[i].Id), param ); + } + if (!ret) break; + } + } + __EXCEPT_PAGE_FAULT + { + ret = FALSE; + status = STATUS_ACCESS_VIOLATION; + } + __ENDTRY +done: + HeapFree( GetProcessHeap(), 0, name ); + if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); + if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); + return ret; +} + + +/********************************************************************** + * EnumResourceNamesW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumResourceNamesW( HMODULE module, LPCWSTR type, + ENUMRESNAMEPROCW func, LONG_PTR param ) +{ + return EnumResourceNamesExW( module, type, func, param, 0, 0 ); +} + + +/********************************************************************** + * EnumResourceTypesExA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumResourceTypesExA( HMODULE module, ENUMRESTYPEPROCA func, LONG_PTR param, + DWORD flags, LANGID lang ) +{ + int i; + BOOL ret = FALSE; + LPSTR type = NULL; + DWORD len = 0, newlen; + const IMAGE_RESOURCE_DIRECTORY *resdir; + const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; + const IMAGE_RESOURCE_DIR_STRING_U *str; + + TRACE( "%p %p %Ix\n", module, func, param ); + + if (flags & (RESOURCE_ENUM_MUI | RESOURCE_ENUM_MUI_SYSTEM | RESOURCE_ENUM_VALIDATE)) + FIXME( "unimplemented flags: %lx\n", flags ); + + if (!flags) flags = RESOURCE_ENUM_LN | RESOURCE_ENUM_MUI; + if (!(flags & RESOURCE_ENUM_LN)) return ret; + + if (!module) module = GetModuleHandleW( 0 ); + + if (!set_ntstatus( LdrFindResourceDirectory_U( module, NULL, 0, &resdir ))) return FALSE; + + et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); + for (i = 0; i < resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries; i++) + { + if (et[i].NameIsString) + { + str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const BYTE *)resdir + et[i].NameOffset); + newlen = WideCharToMultiByte( CP_ACP, 0, str->NameString, str->Length, NULL, 0, NULL, NULL); + if (newlen + 1 > len) + { + len = newlen + 1; + HeapFree( GetProcessHeap(), 0, type ); + if (!(type = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE; + } + WideCharToMultiByte( CP_ACP, 0, str->NameString, str->Length, type, len, NULL, NULL); + type[newlen] = 0; + ret = func( module, type, param ); + } + else + { + ret = func( module, UIntToPtr(et[i].Id), param ); + } + if (!ret) break; + } + HeapFree( GetProcessHeap(), 0, type ); + return ret; +} + + +/********************************************************************** + * EnumResourceTypesExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumResourceTypesExW( HMODULE module, ENUMRESTYPEPROCW func, LONG_PTR param, + DWORD flags, LANGID lang ) +{ + int i, len = 0; + BOOL ret = FALSE; + LPWSTR type = NULL; + const IMAGE_RESOURCE_DIRECTORY *resdir; + const IMAGE_RESOURCE_DIRECTORY_ENTRY *et; + const IMAGE_RESOURCE_DIR_STRING_U *str; + + TRACE( "%p %p %Ix\n", module, func, param ); + + if (!flags) flags = RESOURCE_ENUM_LN | RESOURCE_ENUM_MUI; + if (!(flags & RESOURCE_ENUM_LN)) return ret; + + if (!module) module = GetModuleHandleW( 0 ); + + if (!set_ntstatus( LdrFindResourceDirectory_U( module, NULL, 0, &resdir ))) return FALSE; + + et = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(resdir + 1); + for (i = 0; i < resdir->NumberOfNamedEntries + resdir->NumberOfIdEntries; i++) + { + if (et[i].NameIsString) + { + str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const BYTE *)resdir + et[i].NameOffset); + if (str->Length + 1 > len) + { + len = str->Length + 1; + HeapFree( GetProcessHeap(), 0, type ); + if (!(type = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE; + } + memcpy(type, str->NameString, str->Length * sizeof (WCHAR)); + type[str->Length] = 0; + ret = func( module, type, param ); + } + else + { + ret = func( module, UIntToPtr(et[i].Id), param ); + } + if (!ret) break; + } + HeapFree( GetProcessHeap(), 0, type ); + return ret; +} + + +/********************************************************************** + * FindResourceExW (kernelbase.@) + */ +HRSRC WINAPI DECLSPEC_HOTPATCH FindResourceExW( HMODULE module, LPCWSTR type, LPCWSTR name, WORD lang ) +{ + NTSTATUS status; + UNICODE_STRING nameW, typeW; + LDR_RESOURCE_INFO info; + const IMAGE_RESOURCE_DATA_ENTRY *entry = NULL; + + TRACE( "%p %s %s %04x\n", module, debugstr_w(type), debugstr_w(name), lang ); + + if (!module) module = GetModuleHandleW( 0 ); + nameW.Buffer = typeW.Buffer = NULL; + + __TRY + { + if ((status = get_res_nameW( name, &nameW )) != STATUS_SUCCESS) goto done; + if ((status = get_res_nameW( type, &typeW )) != STATUS_SUCCESS) goto done; + info.Type = (ULONG_PTR)typeW.Buffer; + info.Name = (ULONG_PTR)nameW.Buffer; + info.Language = lang; + status = LdrFindResource_U( module, &info, 3, &entry ); + done: + if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); + } + __EXCEPT_PAGE_FAULT + { + SetLastError( ERROR_INVALID_PARAMETER ); + } + __ENDTRY + + if (!IS_INTRESOURCE(nameW.Buffer)) HeapFree( GetProcessHeap(), 0, nameW.Buffer ); + if (!IS_INTRESOURCE(typeW.Buffer)) HeapFree( GetProcessHeap(), 0, typeW.Buffer ); + return (HRSRC)entry; +} + + +/********************************************************************** + * FindResourceW (kernelbase.@) + */ +HRSRC WINAPI DECLSPEC_HOTPATCH FindResourceW( HINSTANCE module, LPCWSTR name, LPCWSTR type ) +{ + return FindResourceExW( module, type, name, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) ); +} + + +/********************************************************************** + * FreeResource (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FreeResource( HGLOBAL handle ) +{ + return FALSE; +} + + +/********************************************************************** + * LoadResource (kernelbase.@) + */ +HGLOBAL WINAPI DECLSPEC_HOTPATCH LoadResource( HINSTANCE module, HRSRC rsrc ) +{ + void *ret; + + TRACE( "%p %p\n", module, rsrc ); + + if (!rsrc) return 0; + if (!module) module = GetModuleHandleW( 0 ); + if (!set_ntstatus( LdrAccessResource( module, (IMAGE_RESOURCE_DATA_ENTRY *)rsrc, &ret, NULL ))) + return 0; + return ret; +} + + +/********************************************************************** + * LockResource (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH LockResource( HGLOBAL handle ) +{ + return handle; +} + + +/********************************************************************** + * SizeofResource (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH SizeofResource( HINSTANCE module, HRSRC rsrc ) +{ + if (!rsrc) return 0; + return ((IMAGE_RESOURCE_DATA_ENTRY *)rsrc)->Size; +} + + +/*********************************************************************** + * Activation contexts + ***********************************************************************/ + + +/*********************************************************************** + * ActivateActCtx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ActivateActCtx( HANDLE context, ULONG_PTR *cookie ) +{ + return set_ntstatus( RtlActivateActivationContext( 0, context, cookie )); +} + + +/*********************************************************************** + * AddRefActCtx (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH AddRefActCtx( HANDLE context ) +{ + RtlAddRefActivationContext( context ); +} + + +/*********************************************************************** + * CreateActCtxW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateActCtxW( PCACTCTXW ctx ) +{ + HANDLE context; + + TRACE( "%p %08lx\n", ctx, ctx ? ctx->dwFlags : 0 ); + + if (!set_ntstatus( RtlCreateActivationContext( &context, ctx ))) return INVALID_HANDLE_VALUE; + return context; +} + + +/*********************************************************************** + * DeactivateActCtx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DeactivateActCtx( DWORD flags, ULONG_PTR cookie ) +{ + RtlDeactivateActivationContext( flags, cookie ); + return TRUE; +} + + +/*********************************************************************** + * FindActCtxSectionGuid (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindActCtxSectionGuid( DWORD flags, const GUID *ext_guid, ULONG id, + const GUID *guid, PACTCTX_SECTION_KEYED_DATA info ) +{ + return set_ntstatus( RtlFindActivationContextSectionGuid( flags, ext_guid, id, guid, info )); +} + + +/*********************************************************************** + * FindActCtxSectionStringW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindActCtxSectionStringW( DWORD flags, const GUID *ext_guid, ULONG id, + LPCWSTR str, PACTCTX_SECTION_KEYED_DATA info ) +{ + UNICODE_STRING us; + + if (!info) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + RtlInitUnicodeString( &us, str ); + return set_ntstatus( RtlFindActivationContextSectionString( flags, ext_guid, id, &us, info )); +} + + +/*********************************************************************** + * GetCurrentActCtx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetCurrentActCtx( HANDLE *pcontext ) +{ + return set_ntstatus( RtlGetActiveActivationContext( pcontext )); +} + + +/*********************************************************************** + * QueryActCtxSettingsW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryActCtxSettingsW( DWORD flags, HANDLE ctx, const WCHAR *ns, + const WCHAR *settings, WCHAR *buffer, SIZE_T size, + SIZE_T *written ) +{ + return set_ntstatus( RtlQueryActivationContextApplicationSettings( flags, ctx, ns, settings, + buffer, size, written )); +} + + +/*********************************************************************** + * QueryActCtxW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryActCtxW( DWORD flags, HANDLE context, PVOID inst, ULONG class, + PVOID buffer, SIZE_T size, SIZE_T *written ) +{ + return set_ntstatus( RtlQueryInformationActivationContext( flags, context, inst, class, + buffer, size, written )); +} + + +/*********************************************************************** + * ReleaseActCtx (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH ReleaseActCtx( HANDLE context ) +{ + RtlReleaseActivationContext( context ); +} + + +/*********************************************************************** + * ZombifyActCtx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ZombifyActCtx( HANDLE context ) +{ + return set_ntstatus( RtlZombifyActivationContext( context )); +} diff --git a/dll/win32/KernelBase/wine/locale.c b/dll/win32/KernelBase/wine/locale.c new file mode 100644 index 0000000000000..651f45be8c201 --- /dev/null +++ b/dll/win32/KernelBase/wine/locale.c @@ -0,0 +1,8376 @@ +/* + * Locale support + * + * Copyright 1995 Martin von Loewis + * Copyright 1998 David Lee Lambert + * Copyright 2000 Julio César Gázquez + * Copyright 2003 Jon Griffiths + * Copyright 2005 Dmitry Timoshkov + * Copyright 2002, 2019 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#define WINNORMALIZEAPI +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "winuser.h" +#include "winternl.h" +#include "kernelbase.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(nls); + +#define CALINFO_MAX_YEAR 2029 + +static HMODULE kernelbase_handle; + +struct registry_entry +{ + const WCHAR *value; + const WCHAR *subkey; + enum { NOT_CACHED, CACHED, MISSING } status; + WCHAR data[80]; +}; + +static const WCHAR world_subkey[] = { 0xd83c, 0xdf0e, 0xd83c, 0xdf0f, 0xd83c, 0xdf0d, 0 }; /* 🌎🌏🌍 */ + +static struct registry_entry entry_icalendartype = { L"iCalendarType" }; +static struct registry_entry entry_icountry = { L"iCountry" }; +static struct registry_entry entry_icurrdigits = { L"iCurrDigits" }; +static struct registry_entry entry_icurrency = { L"iCurrency" }; +static struct registry_entry entry_idigits = { L"iDigits" }; +static struct registry_entry entry_idigitsubstitution = { L"NumShape" }; +static struct registry_entry entry_ifirstdayofweek = { L"iFirstDayOfWeek" }; +static struct registry_entry entry_ifirstweekofyear = { L"iFirstWeekOfYear" }; +static struct registry_entry entry_ilzero = { L"iLZero" }; +static struct registry_entry entry_imeasure = { L"iMeasure" }; +static struct registry_entry entry_inegcurr = { L"iNegCurr" }; +static struct registry_entry entry_inegnumber = { L"iNegNumber" }; +static struct registry_entry entry_ipapersize = { L"iPaperSize" }; +static struct registry_entry entry_s1159 = { L"s1159" }; +static struct registry_entry entry_s2359 = { L"s2359" }; +static struct registry_entry entry_scurrency = { L"sCurrency" }; +static struct registry_entry entry_sdecimal = { L"sDecimal" }; +static struct registry_entry entry_sgrouping = { L"sGrouping" }; +static struct registry_entry entry_sintlsymbol = { L"Currencies", world_subkey }; +static struct registry_entry entry_slist = { L"sList" }; +static struct registry_entry entry_slongdate = { L"sLongDate" }; +static struct registry_entry entry_smondecimalsep = { L"sMonDecimalSep" }; +static struct registry_entry entry_smongrouping = { L"sMonGrouping" }; +static struct registry_entry entry_smonthousandsep = { L"sMonThousandSep" }; +static struct registry_entry entry_snativedigits = { L"sNativeDigits" }; +static struct registry_entry entry_snegativesign = { L"sNegativeSign" }; +static struct registry_entry entry_spositivesign = { L"sPositiveSign" }; +static struct registry_entry entry_sshortdate = { L"sShortDate" }; +static struct registry_entry entry_sshorttime = { L"sShortTime" }; +static struct registry_entry entry_sthousand = { L"sThousand" }; +static struct registry_entry entry_stimeformat = { L"sTimeFormat" }; +static struct registry_entry entry_syearmonth = { L"sYearMonth" }; + + +static const struct { UINT cp; const WCHAR *name; } codepage_names[] = +{ + { 37, L"IBM EBCDIC US Canada" }, + { 424, L"IBM EBCDIC Hebrew" }, + { 437, L"OEM United States" }, + { 500, L"IBM EBCDIC International" }, + { 708, L"Arabic ASMO" }, + { 720, L"Arabic (Transparent ASMO)" }, + { 737, L"OEM Greek 437G" }, + { 775, L"OEM Baltic" }, + { 850, L"OEM Multilingual Latin 1" }, + { 852, L"OEM Slovak Latin 2" }, + { 855, L"OEM Cyrillic" }, + { 856, L"Hebrew PC" }, + { 857, L"OEM Turkish" }, + { 860, L"OEM Portuguese" }, + { 861, L"OEM Icelandic" }, + { 862, L"OEM Hebrew" }, + { 863, L"OEM Canadian French" }, + { 864, L"OEM Arabic" }, + { 865, L"OEM Nordic" }, + { 866, L"OEM Russian" }, + { 869, L"OEM Greek" }, + { 874, L"ANSI/OEM Thai" }, + { 875, L"IBM EBCDIC Greek" }, + { 878, L"Russian KOI8" }, + { 932, L"ANSI/OEM Japanese Shift-JIS" }, + { 936, L"ANSI/OEM Simplified Chinese GBK" }, + { 949, L"ANSI/OEM Korean Unified Hangul" }, + { 950, L"ANSI/OEM Traditional Chinese Big5" }, + { 1006, L"IBM Arabic" }, + { 1026, L"IBM EBCDIC Latin 5 Turkish" }, + { 1250, L"ANSI Eastern Europe" }, + { 1251, L"ANSI Cyrillic" }, + { 1252, L"ANSI Latin 1" }, + { 1253, L"ANSI Greek" }, + { 1254, L"ANSI Turkish" }, + { 1255, L"ANSI Hebrew" }, + { 1256, L"ANSI Arabic" }, + { 1257, L"ANSI Baltic" }, + { 1258, L"ANSI/OEM Viet Nam" }, + { 1361, L"Korean Johab" }, + { 10000, L"Mac Roman" }, + { 10001, L"Mac Japanese" }, + { 10002, L"Mac Traditional Chinese" }, + { 10003, L"Mac Korean" }, + { 10004, L"Mac Arabic" }, + { 10005, L"Mac Hebrew" }, + { 10006, L"Mac Greek" }, + { 10007, L"Mac Cyrillic" }, + { 10008, L"Mac Simplified Chinese" }, + { 10010, L"Mac Romanian" }, + { 10017, L"Mac Ukrainian" }, + { 10021, L"Mac Thai" }, + { 10029, L"Mac Latin 2" }, + { 10079, L"Mac Icelandic" }, + { 10081, L"Mac Turkish" }, + { 10082, L"Mac Croatian" }, + { 20127, L"US-ASCII (7bit)" }, + { 20866, L"Russian KOI8" }, + { 20932, L"EUC-JP" }, + { 20949, L"Korean Wansung" }, + { 21866, L"Ukrainian KOI8" }, + { 28591, L"ISO 8859-1 Latin 1" }, + { 28592, L"ISO 8859-2 Latin 2 (East European)" }, + { 28593, L"ISO 8859-3 Latin 3 (South European)" }, + { 28594, L"ISO 8859-4 Latin 4 (Baltic old)" }, + { 28595, L"ISO 8859-5 Cyrillic" }, + { 28596, L"ISO 8859-6 Arabic" }, + { 28597, L"ISO 8859-7 Greek" }, + { 28598, L"ISO 8859-8 Hebrew" }, + { 28599, L"ISO 8859-9 Latin 5 (Turkish)" }, + { 28600, L"ISO 8859-10 Latin 6 (Nordic)" }, + { 28601, L"ISO 8859-11 Latin (Thai)" }, + { 28603, L"ISO 8859-13 Latin 7 (Baltic)" }, + { 28604, L"ISO 8859-14 Latin 8 (Celtic)" }, + { 28605, L"ISO 8859-15 Latin 9 (Euro)" }, + { 28606, L"ISO 8859-16 Latin 10 (Balkan)" }, + { 65000, L"65000 (UTF-7)" }, + { 65001, L"65001 (UTF-8)" } +}; + +/* Unicode expanded ligatures */ +static const WCHAR ligatures[][5] = +{ + { 0x00c6, 'A','E',0 }, + { 0x00de, 'T','H',0 }, + { 0x00df, 's','s',0 }, + { 0x00e6, 'a','e',0 }, + { 0x00fe, 't','h',0 }, + { 0x0132, 'I','J',0 }, + { 0x0133, 'i','j',0 }, + { 0x0152, 'O','E',0 }, + { 0x0153, 'o','e',0 }, + { 0x01c4, 'D',0x017d,0 }, + { 0x01c5, 'D',0x017e,0 }, + { 0x01c6, 'd',0x017e,0 }, + { 0x01c7, 'L','J',0 }, + { 0x01c8, 'L','j',0 }, + { 0x01c9, 'l','j',0 }, + { 0x01ca, 'N','J',0 }, + { 0x01cb, 'N','j',0 }, + { 0x01cc, 'n','j',0 }, + { 0x01e2, 0x0100,0x0112,0 }, + { 0x01e3, 0x0101,0x0113,0 }, + { 0x01f1, 'D','Z',0 }, + { 0x01f2, 'D','z',0 }, + { 0x01f3, 'd','z',0 }, + { 0x01fc, 0x00c1,0x00c9,0 }, + { 0x01fd, 0x00e1,0x00e9,0 }, + { 0x05f0, 0x05d5,0x05d5,0 }, + { 0x05f1, 0x05d5,0x05d9,0 }, + { 0x05f2, 0x05d9,0x05d9,0 }, + { 0xfb00, 'f','f',0 }, + { 0xfb01, 'f','i',0 }, + { 0xfb02, 'f','l',0 }, + { 0xfb03, 'f','f','i',0 }, + { 0xfb04, 'f','f','l',0 }, + { 0xfb05, 0x017f,'t',0 }, + { 0xfb06, 's','t',0 }, +}; + +struct calendar +{ + USHORT icalintvalue; /* 00 */ + USHORT itwodigityearmax; /* 02 */ + UINT sshortdate; /* 04 */ + UINT syearmonth; /* 08 */ + UINT slongdate; /* 0c */ + UINT serastring; /* 10 */ + UINT iyearoffsetrange; /* 14 */ + UINT sdayname; /* 18 */ + UINT sabbrevdayname; /* 1c */ + UINT smonthname; /* 20 */ + UINT sabbrevmonthname; /* 24 */ + UINT scalname; /* 28 */ + UINT smonthday; /* 2c */ + UINT sabbreverastring; /* 30 */ + UINT sshortestdayname; /* 34 */ + UINT srelativelongdate; /* 38 */ + UINT unused[3]; /* 3c */ +}; + +static const struct geo_id +{ + GEOID id; + WCHAR latitude[12]; + WCHAR longitude[12]; + GEOCLASS class; + GEOID parent; + WCHAR iso2[4]; + WCHAR iso3[4]; + USHORT uncode; + USHORT dialcode; + WCHAR currcode[4]; + WCHAR currsymbol[8]; +} *geo_ids; + +static const struct geo_index +{ + WCHAR name[4]; + UINT idx; +} *geo_index; + +static unsigned int geo_ids_count; +static unsigned int geo_index_count; + +enum charmaps +{ + CHARMAP_FOLDDIGITS, + CHARMAP_COMPAT, + CHARMAP_HIRAGANA, + CHARMAP_KATAKANA, + CHARMAP_HALFWIDTH, + CHARMAP_FULLWIDTH, + CHARMAP_TRADITIONAL, + CHARMAP_SIMPLIFIED, + NB_CHARMAPS +}; + +static const USHORT *charmaps[NB_CHARMAPS]; + +/* NLS normalization file */ +struct norm_table +{ + WCHAR name[13]; /* 00 file name */ + USHORT checksum[3]; /* 1a checksum? */ + USHORT version[4]; /* 20 Unicode version */ + USHORT form; /* 28 normalization form */ + USHORT len_factor; /* 2a factor for length estimates */ + USHORT unknown1; /* 2c */ + USHORT decomp_size; /* 2e decomposition hash size */ + USHORT comp_size; /* 30 composition hash size */ + USHORT unknown2; /* 32 */ + USHORT classes; /* 34 combining classes table offset */ + USHORT props_level1; /* 36 char properties table level 1 offset */ + USHORT props_level2; /* 38 char properties table level 2 offset */ + USHORT decomp_hash; /* 3a decomposition hash table offset */ + USHORT decomp_map; /* 3c decomposition character map table offset */ + USHORT decomp_seq; /* 3e decomposition character sequences offset */ + USHORT comp_hash; /* 40 composition hash table offset */ + USHORT comp_seq; /* 42 composition character sequences offset */ + /* BYTE[] combining class values */ + /* BYTE[0x2200] char properties index level 1 */ + /* BYTE[] char properties index level 2 */ + /* WORD[] decomposition hash table */ + /* WORD[] decomposition character map */ + /* WORD[] decomposition character sequences */ + /* WORD[] composition hash table */ + /* WORD[] composition character sequences */ +}; + +static CPTABLEINFO ansi_cpinfo; +static CPTABLEINFO oem_cpinfo; +static UINT unix_cp = CP_UTF8; +static LCID system_lcid; +static LCID user_lcid; +static HKEY intl_key; +static HKEY nls_key; +static HKEY tz_key; +static const NLS_LOCALE_LCID_INDEX *lcids_index; +static const NLS_LOCALE_LCNAME_INDEX *lcnames_index; +static const NLS_LOCALE_HEADER *locale_table; +static const WCHAR *locale_strings; +static const NLS_LOCALE_DATA *system_locale; +static const NLS_LOCALE_DATA *user_locale; + +static CPTABLEINFO codepages[128]; +static unsigned int nb_codepages; + +static struct norm_table *norm_info; + +struct sortguid +{ + GUID id; /* sort GUID */ + UINT flags; /* flags */ + UINT compr; /* offset to compression table */ + UINT except; /* exception table offset in sortkey table */ + UINT ling_except; /* exception table offset for linguistic casing */ + UINT casemap; /* linguistic casemap table offset */ +}; + +/* flags for sortguid */ +#define FLAG_HAS_3_BYTE_WEIGHTS 0x01 +#define FLAG_REVERSEDIACRITICS 0x10 +#define FLAG_DOUBLECOMPRESSION 0x20 +#define FLAG_INVERSECASING 0x40 + +struct sort_expansion +{ + WCHAR exp[2]; +}; + +struct jamo_sort +{ + BYTE is_old; + BYTE leading; + BYTE vowel; + BYTE trailing; + BYTE weight; + BYTE pad[3]; +}; + +struct sort_compression +{ + UINT offset; + WCHAR minchar, maxchar; + WORD len[8]; +}; + +static inline int compression_size( int len ) { return 2 + len + (len & 1); } + +union char_weights +{ + UINT val; + struct { BYTE primary, script, diacritic, _case; }; +}; + +/* bits for case weights */ +#define CASE_FULLWIDTH 0x01 /* full width kana (vs. half width) */ +#define CASE_FULLSIZE 0x02 /* full size kana (vs. small) */ +#define CASE_SUBSCRIPT 0x08 /* sub/super script */ +#define CASE_UPPER 0x10 /* upper case */ +#define CASE_KATAKANA 0x20 /* katakana (vs. hiragana) */ +#define CASE_COMPR_2 0x40 /* compression exists for >= 2 chars */ +#define CASE_COMPR_4 0x80 /* compression exists for >= 4 chars */ +#define CASE_COMPR_6 0xc0 /* compression exists for >= 6 chars */ + +enum sortkey_script +{ + SCRIPT_UNSORTABLE = 0, + SCRIPT_NONSPACE_MARK = 1, + SCRIPT_EXPANSION = 2, + SCRIPT_EASTASIA_SPECIAL = 3, + SCRIPT_JAMO_SPECIAL = 4, + SCRIPT_EXTENSION_A = 5, + SCRIPT_PUNCTUATION = 6, + SCRIPT_SYMBOL_1 = 7, + SCRIPT_SYMBOL_2 = 8, + SCRIPT_SYMBOL_3 = 9, + SCRIPT_SYMBOL_4 = 10, + SCRIPT_SYMBOL_5 = 11, + SCRIPT_SYMBOL_6 = 12, + SCRIPT_DIGIT = 13, + SCRIPT_LATIN = 14, + SCRIPT_GREEK = 15, + SCRIPT_CYRILLIC = 16, + SCRIPT_KANA = 34, + SCRIPT_HEBREW = 40, + SCRIPT_ARABIC = 41, + SCRIPT_PUA_FIRST = 169, + SCRIPT_PUA_LAST = 175, + SCRIPT_CJK_FIRST = 192, + SCRIPT_CJK_LAST = 239, +}; + +static const struct sortguid **locale_sorts; +static const struct sortguid *current_locale_sort; + +static struct +{ + UINT version; /* NLS version */ + UINT guid_count; /* number of sort GUIDs */ + UINT exp_count; /* number of character expansions */ + UINT compr_count; /* number of compression tables */ + const UINT *keys; /* sortkey table, indexed by char */ + const USHORT *casemap; /* casemap table, in l_intl.nls format */ + const WORD *ctypes; /* CT_CTYPE1,2,3 values */ + const BYTE *ctype_idx; /* index to map char to ctypes array entry */ + const struct sortguid *guids; /* table of sort GUIDs */ + const struct sort_expansion *expansions; /* character expansions */ + const struct sort_compression *compressions; /* character compression tables */ + const WCHAR *compr_data; /* data for individual compressions */ + const struct jamo_sort *jamo; /* table for Jamo compositions */ +} sort; + +static CRITICAL_SECTION locale_section; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &locale_section, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": locale_section") } +}; +static CRITICAL_SECTION locale_section = { &critsect_debug, -1, 0, 0, 0, 0 }; + + +static void load_locale_nls(void) +{ + struct + { + UINT ctypes; + UINT unknown1; + UINT unknown2; + UINT unknown3; + UINT locales; + UINT charmaps; + UINT geoids; + UINT scripts; + } *header; + struct geo_header + { + WCHAR signature[4]; /* L"geo" */ + UINT total_size; + UINT ids_offset; + UINT ids_count; + UINT index_offset; + UINT index_count; + } *geo_header; + + LARGE_INTEGER dummy; + const USHORT *map_ptr; + unsigned int i; + + RtlGetLocaleFileMappingAddress( (void **)&header, &system_lcid, &dummy ); + locale_table = (const NLS_LOCALE_HEADER *)((char *)header + header->locales); + lcids_index = (const NLS_LOCALE_LCID_INDEX *)((char *)locale_table + locale_table->lcids_offset); + lcnames_index = (const NLS_LOCALE_LCNAME_INDEX *)((char *)locale_table + locale_table->lcnames_offset); + locale_strings = (const WCHAR *)((char *)locale_table + locale_table->strings_offset); + geo_header = (struct geo_header *)((char *)header + header->geoids); + geo_ids = (const struct geo_id *)((char *)geo_header + geo_header->ids_offset); + geo_index = (const struct geo_index *)((char *)geo_header + geo_header->index_offset); + geo_ids_count = geo_header->ids_count; + geo_index_count = geo_header->index_count; + map_ptr = (const USHORT *)((char *)header + header->charmaps); + for (i = 0; i < NB_CHARMAPS; i++, map_ptr += *map_ptr) charmaps[i] = map_ptr + 1; +} + + +static void load_sortdefault_nls(void) +{ + const struct + { + UINT sortkeys; + UINT casemaps; + UINT ctypes; + UINT sortids; + } *header; + + const WORD *ctype; + const UINT *table; + UINT i; + SIZE_T size; + const struct sort_compression *last_compr; + + NtGetNlsSectionPtr( 9, 0, NULL, (void **)&header, &size ); + + sort.keys = (UINT *)((char *)header + header->sortkeys); + sort.casemap = (USHORT *)((char *)header + header->casemaps); + + ctype = (WORD *)((char *)header + header->ctypes); + sort.ctypes = ctype + 2; + sort.ctype_idx = (BYTE *)ctype + ctype[1] + 2; + + table = (UINT *)((char *)header + header->sortids); + sort.version = table[0]; + sort.guid_count = table[1]; + sort.guids = (struct sortguid *)(table + 2); + + table = (UINT *)(sort.guids + sort.guid_count); + sort.exp_count = table[0]; + sort.expansions = (struct sort_expansion *)(table + 1); + + table = (UINT *)(sort.expansions + sort.exp_count); + sort.compr_count = table[0]; + sort.compressions = (struct sort_compression *)(table + 1); + sort.compr_data = (WCHAR *)(sort.compressions + sort.compr_count); + + last_compr = sort.compressions + sort.compr_count - 1; + table = (UINT *)(sort.compr_data + last_compr->offset); + for (i = 0; i < 7; i++) table += last_compr->len[i] * ((i + 5) / 2); + table += 1 + table[0] / 2; /* skip multiple weights */ + sort.jamo = (struct jamo_sort *)(table + 1); + + locale_sorts = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, + locale_table->nb_lcnames * sizeof(*locale_sorts) ); +} + + +static const struct sortguid *find_sortguid( const GUID *guid ) +{ + int pos, ret, min = 0, max = sort.guid_count - 1; + + while (min <= max) + { + pos = (min + max) / 2; + ret = memcmp( guid, &sort.guids[pos].id, sizeof(*guid) ); + if (!ret) return &sort.guids[pos]; + if (ret > 0) min = pos + 1; + else max = pos - 1; + } + ERR( "no sort found for %s\n", debugstr_guid( guid )); + return NULL; +} + + +static const NLS_LOCALE_DATA *get_locale_data( UINT idx ) +{ + ULONG offset = locale_table->locales_offset + idx * locale_table->locale_size; + return (const NLS_LOCALE_DATA *)((const char *)locale_table + offset); +} + + +static const struct calendar *get_calendar_data( const NLS_LOCALE_DATA *locale, UINT id ) +{ + if (id == CAL_HIJRI && locale->islamic_cal[0]) id = locale->islamic_cal[0]; + else if (id == CAL_PERSIAN && locale->islamic_cal[1]) id = locale->islamic_cal[1]; + + if (!id || id > locale_table->nb_calendars) return NULL; + return (const struct calendar *)((const char *)locale_table + locale_table->calendars_offset + + (id - 1) * locale_table->calendar_size); +} + + +static int compare_locale_names( const WCHAR *n1, const WCHAR *n2 ) +{ + for (;;) + { + WCHAR ch1 = *n1++; + WCHAR ch2 = *n2++; + if (ch1 >= 'a' && ch1 <= 'z') ch1 -= 'a' - 'A'; + else if (ch1 == '_') ch1 = '-'; + if (ch2 >= 'a' && ch2 <= 'z') ch2 -= 'a' - 'A'; + else if (ch2 == '_') ch2 = '-'; + if (!ch1 || ch1 != ch2) return ch1 - ch2; + } +} + + +static const NLS_LOCALE_LCNAME_INDEX *find_lcname_entry( const WCHAR *name ) +{ + int min = 0, max = locale_table->nb_lcnames - 1; + + while (min <= max) + { + int res, pos = (min + max) / 2; + const WCHAR *str = locale_strings + lcnames_index[pos].name; + res = compare_locale_names( name, str + 1 ); + if (res < 0) max = pos - 1; + else if (res > 0) min = pos + 1; + else return &lcnames_index[pos]; + } + return NULL; +} + + +static const NLS_LOCALE_LCID_INDEX *find_lcid_entry( LCID lcid ) +{ + int min = 0, max = locale_table->nb_lcids - 1; + + while (min <= max) + { + int pos = (min + max) / 2; + if (lcid < lcids_index[pos].id) max = pos - 1; + else if (lcid > lcids_index[pos].id) min = pos + 1; + else return &lcids_index[pos]; + } + return NULL; +} + + +static const struct geo_id *find_geo_id_entry( GEOID id ) +{ + int min = 0, max = geo_ids_count - 1; + + while (min <= max) + { + int pos = (min + max) / 2; + if (id < geo_ids[pos].id) max = pos - 1; + else if (id > geo_ids[pos].id) min = pos + 1; + else return &geo_ids[pos]; + } + return NULL; +} + + +static const struct geo_id *find_geo_name_entry( const WCHAR *name ) +{ + int min = 0, max = geo_index_count - 1; + + while (min <= max) + { + int res, pos = (min + max) / 2; + res = wcsicmp( name, geo_index[pos].name ); + if (res < 0) max = pos - 1; + else if (res > 0) min = pos + 1; + else return &geo_ids[geo_index[pos].idx]; + } + return NULL; +} + + +static const NLS_LOCALE_DATA *get_locale_by_name( const WCHAR *name, LCID *lcid ) +{ + const NLS_LOCALE_LCNAME_INDEX *entry; + + if (name == LOCALE_NAME_USER_DEFAULT) + { + *lcid = user_lcid; + return user_locale; + } + if (name[0] == '!' && !compare_locale_names( name, LOCALE_NAME_SYSTEM_DEFAULT )) + { + *lcid = system_lcid; + return system_locale; + } + if (!(entry = find_lcname_entry( name ))) return NULL; + *lcid = entry->id; + return get_locale_data( entry->idx ); +} + + +static const struct sortguid *get_language_sort( const WCHAR *name ) +{ + const NLS_LOCALE_LCNAME_INDEX *entry; + const NLS_LOCALE_DATA *locale; + WCHAR guidstr[39]; + const struct sortguid *ret; + UNICODE_STRING str; + LCID lcid; + GUID guid; + HKEY key = 0; + DWORD size, type; + + if (name == LOCALE_NAME_USER_DEFAULT) + { + if (current_locale_sort) return current_locale_sort; + name = locale_strings + user_locale->sname + 1; + } + else if (name[0] == '!' && !compare_locale_names( name, LOCALE_NAME_SYSTEM_DEFAULT )) + { + name = locale_strings + system_locale->sname + 1; + } + + if (!(entry = find_lcname_entry( name ))) + { + WARN( "unknown locale %s\n", debugstr_w(name) ); + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + if ((ret = locale_sorts[entry - lcnames_index])) return ret; + + lcid = entry->id; + name = locale_strings + entry->name + 1; + locale = get_locale_data( entry->idx ); + if (!RegOpenKeyExW( nls_key, L"Sorting\\Ids", 0, KEY_READ, &key )) + { + for (;;) + { + size = sizeof(guidstr); + if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)guidstr, &size ) && type == REG_SZ) + { + RtlInitUnicodeString( &str, guidstr ); + if (!RtlGUIDFromString( &str, &guid )) ret = find_sortguid( &guid ); + break; + } + if (!name[0]) break; + name = locale_strings + (SORTIDFROMLCID( lcid ) ? locale->sname : locale->sparent) + 1; + if (!(locale = get_locale_by_name( name, &lcid ))) break; + } + RegCloseKey( key ); + } + if (!ret) ret = &sort.guids[0]; + locale_sorts[entry - lcnames_index] = ret; + return ret; +} + + +/****************************************************************************** + * NlsValidateLocale (kernelbase.@) + * + * Note: it seems to return some internal data on Windows, we simply return the locale.nls data pointer. + */ +const NLS_LOCALE_DATA * WINAPI NlsValidateLocale( LCID *lcid, ULONG flags ) +{ + const NLS_LOCALE_LCNAME_INDEX *name_entry; + const NLS_LOCALE_LCID_INDEX *entry; + const NLS_LOCALE_DATA *locale; + + switch (*lcid) + { + case LOCALE_SYSTEM_DEFAULT: + *lcid = system_lcid; + return system_locale; + case LOCALE_NEUTRAL: + case LOCALE_USER_DEFAULT: + case LOCALE_CUSTOM_DEFAULT: + case LOCALE_CUSTOM_UNSPECIFIED: + case LOCALE_CUSTOM_UI_DEFAULT: + *lcid = user_lcid; + return user_locale; + default: + if (!(entry = find_lcid_entry( *lcid ))) return NULL; + locale = get_locale_data( entry->idx ); + if ((flags & LOCALE_ALLOW_NEUTRAL_NAMES) || locale->inotneutral) return locale; + if ((name_entry = find_lcname_entry( locale_strings + locale->ssortlocale + 1 ))) + locale = get_locale_data( name_entry->idx ); + return locale; + } +} + + +static int locale_return_data( const WCHAR *data, int datalen, LCTYPE type, WCHAR *buffer, int len ) +{ + if (type & LOCALE_RETURN_NUMBER) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + + if (!len) return datalen; + if (datalen > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + memcpy( buffer, data, datalen * sizeof(WCHAR) ); + return datalen; +} + + +static BOOL set_registry_entry( struct registry_entry *entry, const WCHAR *data ) +{ + DWORD size = (wcslen(data) + 1) * sizeof(WCHAR); + LSTATUS ret; + + if (size > sizeof(entry->data)) + { + SetLastError( ERROR_INVALID_FLAGS ); + return FALSE; + } + TRACE( "setting %s to %s\n", debugstr_w(entry->value), debugstr_w(data) ); + + RtlEnterCriticalSection( &locale_section ); + if (!(ret = RegSetKeyValueW( intl_key, entry->subkey, entry->value, REG_SZ, (BYTE *)data, size ))) + { + wcscpy( entry->data, data ); + entry->status = CACHED; + } + RtlLeaveCriticalSection( &locale_section ); + if (ret) SetLastError( ret ); + return !ret; +} + + +static int locale_return_reg_string( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len ) +{ + DWORD size; + LRESULT res; + int ret = -1; + + if (type & LOCALE_NOUSEROVERRIDE) return -1; + + RtlEnterCriticalSection( &locale_section ); + switch (entry->status) + { + case NOT_CACHED: + size = sizeof(entry->data); + if (entry->subkey) + { + HKEY key; + if (!(res = RegOpenKeyExW( intl_key, entry->subkey, 0, KEY_READ, &key ))) + { + res = RegQueryValueExW( key, entry->value, NULL, NULL, (BYTE *)entry->data, &size ); + RegCloseKey( key ); + } + } + else res = RegQueryValueExW( intl_key, entry->value, NULL, NULL, (BYTE *)entry->data, &size ); + + if (res) + { + entry->status = MISSING; + break; + } + entry->status = CACHED; + /* fall through */ + case CACHED: + ret = locale_return_data( entry->data, wcslen(entry->data) + 1, type, buffer, len ); + break; + case MISSING: + break; + } + RtlLeaveCriticalSection( &locale_section ); + return ret; +} + + +static int locale_return_string( DWORD pos, LCTYPE type, WCHAR *buffer, int len ) +{ + return locale_return_data( locale_strings + pos + 1, locale_strings[pos] + 1, type, buffer, len ); +} + + +static int locale_return_number( UINT val, LCTYPE type, WCHAR *buffer, int len ) +{ + int ret; + WCHAR tmp[80]; + + if (!(type & LOCALE_RETURN_NUMBER)) + { + switch (LOWORD(type)) + { + case LOCALE_ILANGUAGE: + case LOCALE_IDEFAULTLANGUAGE: + ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%04x", val ) + 1; + break; + case LOCALE_IDEFAULTEBCDICCODEPAGE: + ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", val ) + 1; + break; + default: + ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val ) + 1; + break; + } + } + else ret = sizeof(UINT) / sizeof(WCHAR); + + if (!len) return ret; + if (ret > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + + if (type & LOCALE_RETURN_NUMBER) memcpy( buffer, &val, sizeof(val) ); + else wcscpy( buffer, tmp ); + + return ret; +} + + +static int locale_return_reg_number( struct registry_entry *entry, LCTYPE type, WCHAR *buffer, int len ) +{ + int ret, val; + WCHAR *end, tmp[80]; + + if (type & LOCALE_RETURN_NUMBER) + { + ret = locale_return_reg_string( entry, type & ~LOCALE_RETURN_NUMBER, tmp, ARRAY_SIZE( tmp )); + if (ret == -1) return ret; + val = wcstol( tmp, &end, 10 ); + if (*end) /* invalid number */ + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + return locale_return_number( val, type, buffer, len ); + } + return locale_return_reg_string( entry, type, buffer, len ); +} + + +static int locale_return_grouping( DWORD pos, LCTYPE type, WCHAR *buffer, int len ) +{ + WORD i, count = locale_strings[pos]; + const WCHAR *str = locale_strings + pos + 1; + int ret; + + if (type & LOCALE_RETURN_NUMBER) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + ret = 2 * count; + if (str[count - 1]) ret += 2; /* for final zero */ + + if (!len) return ret; + if (ret > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + for (i = 0; i < count; i++) + { + if (!str[i]) /* explicit null termination */ + { + buffer[-1] = 0; + return ret; + } + *buffer++ = '0' + str[i]; + *buffer++ = ';'; + } + *buffer++ = '0'; + *buffer = 0; + return ret; +} + + +static int locale_return_strarray( DWORD pos, WORD idx, LCTYPE type, WCHAR *buffer, int len ) +{ + const DWORD *array = (const DWORD *)(locale_strings + pos + 1); + WORD count = locale_strings[pos]; + + return locale_return_string( idx < count ? array[idx] : 0, type, buffer, len ); +} + + +static int locale_return_strarray_concat( DWORD pos, LCTYPE type, WCHAR *buffer, int len ) +{ + WORD i, count = locale_strings[pos]; + const DWORD *array = (const DWORD *)(locale_strings + pos + 1); + int ret; + + if (type & LOCALE_RETURN_NUMBER) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + for (i = 0, ret = 1; i < count; i++) ret += locale_strings[array[i]]; + + if (!len) return ret; + if (ret > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + for (i = 0; i < count; i++) + { + memcpy( buffer, locale_strings + array[i] + 1, locale_strings[array[i]] * sizeof(WCHAR) ); + buffer += locale_strings[array[i]]; + } + *buffer = 0; + return ret; +} + + +static int cal_return_number( UINT val, CALTYPE type, WCHAR *buffer, int len, DWORD *value ) +{ + WCHAR tmp[12]; + int ret; + + if (type & CAL_RETURN_NUMBER) + { + *value = val; + return sizeof(UINT) / sizeof(WCHAR); + } + ret = swprintf( tmp, ARRAY_SIZE(tmp), L"%u", val ); + return locale_return_data( tmp, ret + 1, 0, buffer, len ); +} + + +/* find the first format char in a format string */ +static WCHAR *find_format( WCHAR *str, const WCHAR *accept ) +{ + for ( ; *str; str++) + { + if (*str == '\'') + { + if (!(str = wcschr( str + 1, '\'' ))) return NULL; + } + else if (wcschr( accept, *str )) + { + /* ignore "ddd" and "dddd" */ + if (str[0] != 'd' || str[1] != 'd' || str[2] != 'd') return str; + str += 2; + while (str[1] == 'd') str++; + } + } + return NULL; +} + + +/* replace the separator in a date/time format string */ +static WCHAR *locale_replace_separator( WCHAR *buffer, const WCHAR *sep ) +{ + UINT pos = 0; + WCHAR res[80]; + WCHAR *next, *str = find_format( buffer, L"dMyHhms" ); + + if (!str) return buffer; + pos = str - buffer; + memcpy( res, buffer, pos * sizeof(WCHAR) ); + for (;;) + { + res[pos++] = *str++; + while (str[0] == str[-1]) res[pos++] = *str++; /* copy repeated chars */ + if (!(next = find_format( str, L"dMyHhms" ))) break; + wcscpy( res + pos, sep ); + pos += wcslen(sep); + str = next; + } + wcscpy( res + pos, str ); + return wcscpy( buffer, res ); +} + + +/* FIXME: hardcoded, sortname is apparently not available in locale.nls */ +static const WCHAR *get_locale_sortname( LCID lcid ) +{ + switch (PRIMARYLANGID( lcid )) + { + case LANG_CHINESE: + switch (SORTIDFROMLCID( lcid )) + { + case SORT_CHINESE_PRCP: + switch (SUBLANGID( lcid )) + { + case SUBLANG_CHINESE_TRADITIONAL: + case SUBLANG_CHINESE_HONGKONG: + case 0x1f: + return L"Stroke Count"; + default: + return L"Pronunciation"; + } + case SORT_CHINESE_UNICODE: return L"Unicode"; + case SORT_CHINESE_PRC: return L"Stroke Count"; + case SORT_CHINESE_BOPOMOFO: return L"Bopomofo"; + case SORT_CHINESE_RADICALSTROKE: return L"Radical/Stroke"; + case 5: return L"Surname"; + } + break; + + case LANG_GEORGIAN: + if (SORTIDFROMLCID( lcid ) == SORT_GEORGIAN_MODERN) return L"Modern"; + return L"Traditional"; + + case LANG_GERMAN: + switch (SUBLANGID( lcid )) + { + case SUBLANG_NEUTRAL: + case SUBLANG_DEFAULT: + if (SORTIDFROMLCID( lcid ) == SORT_GERMAN_PHONE_BOOK) return L"Phone Book (DIN)"; + return L"Dictionary"; + } + break; + + case LANG_HUNGARIAN: + if (SORTIDFROMLCID( lcid ) == SORT_HUNGARIAN_TECHNICAL) return L"Technical"; + break; + + case LANG_INVARIANT: + if (SORTIDFROMLCID( lcid ) == SORT_INVARIANT_MATH) return L"Default"; + return L"Maths Alphanumerics"; + + case LANG_JAPANESE: + switch (SORTIDFROMLCID( lcid )) + { + case SORT_JAPANESE_XJIS: return L"XJIS"; + case SORT_JAPANESE_UNICODE: return L"Unicode"; + case SORT_JAPANESE_RADICALSTROKE: return L"Radical/Stroke"; + } + break; + + case LANG_KOREAN: + if (SORTIDFROMLCID( lcid ) == SORT_KOREAN_UNICODE) return L"Unicode"; + return L"Dictionary"; + + case LANG_SPANISH: + switch (SUBLANGID( lcid )) + { + case SUBLANG_NEUTRAL: + case SUBLANG_SPANISH_MODERN: + return L"International"; + case SUBLANG_DEFAULT: + return L"Traditional"; + } + break; + } + return L"Default"; +} + + +/* get locale information from the locale.nls file */ +static int get_locale_info( const NLS_LOCALE_DATA *locale, LCID lcid, LCTYPE type, + WCHAR *buffer, int len ) +{ + static const WCHAR spermille[] = { 0x2030, 0 }; /* this one seems hardcoded */ + static const BYTE ipossignposn[] = { 3, 3, 4, 2, 1, 1, 3, 4, 1, 3, 4, 2, 4, 3, 3, 1 }; + static const BYTE inegsignposn[] = { 0, 3, 4, 2, 0, 1, 3, 4, 1, 3, 4, 2, 4, 3, 0, 0 }; + static const BYTE inegsymprecedes[] = { 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, }; + const WCHAR *sort; + WCHAR *str, *end, tmp[80]; + UINT val; + int ret; + + if (locale != user_locale) type |= LOCALE_NOUSEROVERRIDE; + + switch (LOWORD(type)) + { + case LOCALE_ILANGUAGE: + /* return default language for neutral locales */ + val = locale->inotneutral ? locale->ilanguage : locale->idefaultlanguage; + return locale_return_number( val, type, buffer, len ); + + case LOCALE_SLOCALIZEDDISPLAYNAME: + /* FIXME: localization */ + return locale_return_string( locale->sengdisplayname, type, buffer, len ); + + case LOCALE_SABBREVLANGNAME: + return locale_return_string( locale->sabbrevlangname, type, buffer, len ); + + case LOCALE_SNATIVELANGNAME: + return locale_return_string( locale->snativelangname, type, buffer, len ); + + case LOCALE_ICOUNTRY: + if ((ret = locale_return_reg_number( &entry_icountry, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->icountry, type, buffer, len ); + + case LOCALE_SLOCALIZEDCOUNTRYNAME: + /* FIXME: localization */ + return locale_return_string( locale->sengcountry, type, buffer, len ); + + case LOCALE_SABBREVCTRYNAME: + return locale_return_string( locale->sabbrevctryname, type, buffer, len ); + + case LOCALE_SNATIVECTRYNAME: + return locale_return_string( locale->snativectryname, type, buffer, len ); + + case LOCALE_IDEFAULTLANGUAGE: + return locale_return_number( locale->idefaultlanguage, type, buffer, len ); + + case LOCALE_IDEFAULTCOUNTRY: + return locale_return_number( locale->icountry, type, buffer, len ); + + case LOCALE_IDEFAULTCODEPAGE: + val = locale->idefaultcodepage == CP_UTF8 ? CP_OEMCP : locale->idefaultcodepage; + return locale_return_number( val, type, buffer, len ); + + case LOCALE_SLIST: + if ((ret = locale_return_reg_string( &entry_slist, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->slist, type, buffer, len ); + + case LOCALE_IMEASURE: + if ((ret = locale_return_reg_number( &entry_imeasure, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->imeasure, type, buffer, len ); + + case LOCALE_SDECIMAL: + if ((ret = locale_return_reg_string( &entry_sdecimal, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->sdecimal, type, buffer, len ); + + case LOCALE_STHOUSAND: + if ((ret = locale_return_reg_string( &entry_sthousand, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->sthousand, type, buffer, len ); + + case LOCALE_SGROUPING: + if ((ret = locale_return_reg_string( &entry_sgrouping, type, buffer, len )) != -1) return ret; + return locale_return_grouping( locale->sgrouping, type, buffer, len ); + + case LOCALE_IDIGITS: + if ((ret = locale_return_reg_number( &entry_idigits, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->idigits, type, buffer, len ); + + case LOCALE_ILZERO: + if ((ret = locale_return_reg_number( &entry_ilzero, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->ilzero, type, buffer, len ); + + case LOCALE_SNATIVEDIGITS: + if ((ret = locale_return_reg_string( &entry_snativedigits, type, buffer, len )) != -1) return ret; + return locale_return_strarray_concat( locale->snativedigits, type, buffer, len ); + + case LOCALE_SCURRENCY: + if ((ret = locale_return_reg_string( &entry_scurrency, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->scurrency, type, buffer, len ); + + case LOCALE_SINTLSYMBOL: + if ((ret = locale_return_reg_string( &entry_sintlsymbol, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->sintlsymbol, type, buffer, len ); + + case LOCALE_SMONDECIMALSEP: + if ((ret = locale_return_reg_string( &entry_smondecimalsep, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->smondecimalsep, type, buffer, len ); + + case LOCALE_SMONTHOUSANDSEP: + if ((ret = locale_return_reg_string( &entry_smonthousandsep, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->smonthousandsep, type, buffer, len ); + + case LOCALE_SMONGROUPING: + if ((ret = locale_return_reg_string( &entry_smongrouping, type, buffer, len )) != -1) return ret; + return locale_return_grouping( locale->smongrouping, type, buffer, len ); + + case LOCALE_ICURRDIGITS: + case LOCALE_IINTLCURRDIGITS: + if ((ret = locale_return_reg_number( &entry_icurrdigits, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->icurrdigits, type, buffer, len ); + + case LOCALE_ICURRENCY: + if ((ret = locale_return_reg_number( &entry_icurrency, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->icurrency, type, buffer, len ); + + case LOCALE_INEGCURR: + if ((ret = locale_return_reg_number( &entry_inegcurr, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->inegcurr, type, buffer, len ); + + case LOCALE_SDATE: + if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + if (!(str = find_format( tmp, L"dMy" ))) break; + while (str[1] == str[0]) str++; /* skip repeated chars */ + if (!(end = find_format( ++str, L"dMy" ))) break; + *end++ = 0; + return locale_return_data( str, end - str, type, buffer, len ); + + case LOCALE_STIME: + if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + if (!(str = find_format( tmp, L"Hhms" ))) break; + while (str[1] == str[0]) str++; /* skip repeated chars */ + if (!(end = find_format( ++str, L"Hhms" ))) break; + *end++ = 0; + return locale_return_data( str, end - str, type, buffer, len ); + + case LOCALE_SSHORTDATE: + if ((ret = locale_return_reg_string( &entry_sshortdate, type, buffer, len )) != -1) return ret; + return locale_return_strarray( locale->sshortdate, 0, type, buffer, len ); + + case LOCALE_SLONGDATE: + if ((ret = locale_return_reg_string( &entry_slongdate, type, buffer, len )) != -1) return ret; + return locale_return_strarray( locale->slongdate, 0, type, buffer, len ); + + case LOCALE_IDATE: + if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + /* if both year and day are found before month, the last one takes precedence */ + for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" )) + { + if (*str == 'M') break; + val = (*str == 'y' ? 2 : 1); + } + return locale_return_number( val, type, buffer, len ); + + case LOCALE_ILDATE: + if (!get_locale_info( locale, lcid, LOCALE_SLONGDATE | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + /* if both year and day are found before month, the last one takes precedence */ + for (val = 0, str = find_format( tmp, L"dMy" ); str; str = find_format( str + 1, L"dMy" )) + { + if (*str == 'M') break; + val = (*str == 'y' ? 2 : 1); + } + return locale_return_number( val, type, buffer, len ); + + case LOCALE_ITIME: + if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + if (!(str = find_format( tmp, L"Hh" ))) break; + return locale_return_number( *str == 'H', type, buffer, len ); + + case LOCALE_ICENTURY: + if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + if (!(str = find_format( tmp, L"y" ))) break; + return locale_return_number( !wcsncmp( str, L"yyyy", 4 ), type, buffer, len ); + + case LOCALE_ITLZERO: + if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + if (!(str = find_format( tmp, L"Hh" ))) break; + return locale_return_number( str[1] == str[0], type, buffer, len ); + + case LOCALE_IDAYLZERO: + if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + if (!(str = find_format( tmp, L"d" ))) break; + return locale_return_number( str[1] == 'd', type, buffer, len ); + + case LOCALE_IMONLZERO: + if (!get_locale_info( locale, lcid, LOCALE_SSHORTDATE | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + if (!(str = find_format( tmp, L"M" ))) break; + return locale_return_number( str[1] == 'M', type, buffer, len ); + + case LOCALE_S1159: + if ((ret = locale_return_reg_string( &entry_s1159, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->s1159, type, buffer, len ); + + case LOCALE_S2359: + if ((ret = locale_return_reg_string( &entry_s2359, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->s2359, type, buffer, len ); + + case LOCALE_SDAYNAME1: + case LOCALE_SDAYNAME2: + case LOCALE_SDAYNAME3: + case LOCALE_SDAYNAME4: + case LOCALE_SDAYNAME5: + case LOCALE_SDAYNAME6: + case LOCALE_SDAYNAME7: + return locale_return_strarray( locale->sdayname, + LOWORD(type - LOCALE_SDAYNAME1 + 1) % 7, type, buffer, len ); + + case LOCALE_SABBREVDAYNAME1: + case LOCALE_SABBREVDAYNAME2: + case LOCALE_SABBREVDAYNAME3: + case LOCALE_SABBREVDAYNAME4: + case LOCALE_SABBREVDAYNAME5: + case LOCALE_SABBREVDAYNAME6: + case LOCALE_SABBREVDAYNAME7: + return locale_return_strarray( locale->sabbrevdayname, + LOWORD(type - LOCALE_SABBREVDAYNAME1 + 1) % 7, type, buffer, len ); + + case LOCALE_SMONTHNAME1: + case LOCALE_SMONTHNAME2: + case LOCALE_SMONTHNAME3: + case LOCALE_SMONTHNAME4: + case LOCALE_SMONTHNAME5: + case LOCALE_SMONTHNAME6: + case LOCALE_SMONTHNAME7: + case LOCALE_SMONTHNAME8: + case LOCALE_SMONTHNAME9: + case LOCALE_SMONTHNAME10: + case LOCALE_SMONTHNAME11: + case LOCALE_SMONTHNAME12: + return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ? + locale->sgenitivemonth : locale->smonthname, + type - LOCALE_SMONTHNAME1, type, buffer, len ); + + case LOCALE_SABBREVMONTHNAME1: + case LOCALE_SABBREVMONTHNAME2: + case LOCALE_SABBREVMONTHNAME3: + case LOCALE_SABBREVMONTHNAME4: + case LOCALE_SABBREVMONTHNAME5: + case LOCALE_SABBREVMONTHNAME6: + case LOCALE_SABBREVMONTHNAME7: + case LOCALE_SABBREVMONTHNAME8: + case LOCALE_SABBREVMONTHNAME9: + case LOCALE_SABBREVMONTHNAME10: + case LOCALE_SABBREVMONTHNAME11: + case LOCALE_SABBREVMONTHNAME12: + return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ? + locale->sabbrevgenitivemonth : locale->sabbrevmonthname, + type - LOCALE_SABBREVMONTHNAME1, type, buffer, len ); + + case LOCALE_SPOSITIVESIGN: + if ((ret = locale_return_reg_string( &entry_spositivesign, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->spositivesign, type, buffer, len ); + + case LOCALE_SNEGATIVESIGN: + if ((ret = locale_return_reg_string( &entry_snegativesign, type, buffer, len )) != -1) return ret; + return locale_return_string( locale->snegativesign, type, buffer, len ); + + case LOCALE_IPOSSIGNPOSN: + if (!get_locale_info( locale, lcid, + LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE), + (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break; + return locale_return_number( ipossignposn[val], type, buffer, len ); + + case LOCALE_INEGSIGNPOSN: + if (!get_locale_info( locale, lcid, + LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE), + (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break; + return locale_return_number( inegsignposn[val], type, buffer, len ); + + case LOCALE_IPOSSYMPRECEDES: + if (!get_locale_info( locale, lcid, + LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE), + (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break; + return locale_return_number( !(val & 1), type, buffer, len ); + + case LOCALE_IPOSSEPBYSPACE: + if (!get_locale_info( locale, lcid, + LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE), + (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break; + return locale_return_number( !!(val & 2), type, buffer, len ); + + case LOCALE_INEGSYMPRECEDES: + if (!get_locale_info( locale, lcid, + LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE), + (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break; + return locale_return_number( inegsymprecedes[val], type, buffer, len ); + + case LOCALE_INEGSEPBYSPACE: + if (!get_locale_info( locale, lcid, + LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | (type & LOCALE_NOUSEROVERRIDE), + (WCHAR *)&val, sizeof(val)/sizeof(WCHAR) )) break; + return locale_return_number( (val >= 8), type, buffer, len ); + + case LOCALE_FONTSIGNATURE: + return locale_return_data( locale_strings + locale->fontsignature + 1, + locale_strings[locale->fontsignature], type, buffer, len ); + + case LOCALE_SISO639LANGNAME: + return locale_return_string( locale->siso639langname, type, buffer, len ); + + case LOCALE_SISO3166CTRYNAME: + return locale_return_string( locale->siso3166ctryname, type, buffer, len ); + + case LOCALE_IGEOID: + return locale_return_number( locale->igeoid, type, buffer, len ); + + case LOCALE_SNAME: + if (SORTIDFROMLCID(lcid)) /* custom sort locale */ + { + const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 ); + if (entry) return locale_return_string( entry->name, type, buffer, len ); + } + return locale_return_string( locale->sname, type, buffer, len ); + + case LOCALE_SDURATION: + return locale_return_strarray( locale->sduration, 0, type, buffer, len ); + + case LOCALE_SKEYBOARDSTOINSTALL: + return locale_return_string( locale->skeyboardstoinstall, type, buffer, len ); + + case LOCALE_SSHORTESTDAYNAME1: + case LOCALE_SSHORTESTDAYNAME2: + case LOCALE_SSHORTESTDAYNAME3: + case LOCALE_SSHORTESTDAYNAME4: + case LOCALE_SSHORTESTDAYNAME5: + case LOCALE_SSHORTESTDAYNAME6: + case LOCALE_SSHORTESTDAYNAME7: + return locale_return_strarray( locale->sshortestdayname, + LOWORD(type - LOCALE_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len ); + + case LOCALE_SISO639LANGNAME2: + return locale_return_string( locale->siso639langname2, type, buffer, len ); + + case LOCALE_SISO3166CTRYNAME2: + return locale_return_string( locale->siso3166ctryname2, type, buffer, len ); + + case LOCALE_SNAN: + return locale_return_string( locale->snan, type, buffer, len ); + + case LOCALE_SPOSINFINITY: + return locale_return_string( locale->sposinfinity, type, buffer, len ); + + case LOCALE_SNEGINFINITY: + return locale_return_string( locale->sneginfinity, type, buffer, len ); + + case LOCALE_SSCRIPTS: + return locale_return_string( locale->sscripts, type, buffer, len ); + + case LOCALE_SPARENT: + return locale_return_string( locale->sparent, type, buffer, len ); + + case LOCALE_SCONSOLEFALLBACKNAME: + return locale_return_string( locale->sconsolefallbackname, type, buffer, len ); + + case LOCALE_SLOCALIZEDLANGUAGENAME: + /* FIXME: localization */ + return locale_return_string( locale->senglanguage, type, buffer, len ); + + case LOCALE_IREADINGLAYOUT: + return locale_return_number( locale->ireadinglayout, type, buffer, len ); + + case LOCALE_INEUTRAL: + return locale_return_number( !locale->inotneutral, type, buffer, len ); + + case LOCALE_SENGLISHDISPLAYNAME: + return locale_return_string( locale->sengdisplayname, type, buffer, len ); + + case LOCALE_SNATIVEDISPLAYNAME: + return locale_return_string( locale->snativedisplayname, type, buffer, len ); + + case LOCALE_INEGATIVEPERCENT: + return locale_return_number( locale->inegativepercent, type, buffer, len ); + + case LOCALE_IPOSITIVEPERCENT: + return locale_return_number( locale->ipositivepercent, type, buffer, len ); + + case LOCALE_SPERCENT: + return locale_return_string( locale->spercent, type, buffer, len ); + + case LOCALE_SPERMILLE: + return locale_return_data( spermille, ARRAY_SIZE(spermille), type, buffer, len ); + + case LOCALE_SMONTHDAY: + return locale_return_strarray( locale->smonthday, 0, type, buffer, len ); + + case LOCALE_SSHORTTIME: + if ((ret = locale_return_reg_string( &entry_sshorttime, type, buffer, len )) != -1) return ret; + return locale_return_strarray( locale->sshorttime, 0, type, buffer, len ); + + case LOCALE_SOPENTYPELANGUAGETAG: + return locale_return_string( locale->sopentypelanguagetag, type, buffer, len ); + + case LOCALE_SSORTLOCALE: + if (SORTIDFROMLCID(lcid)) /* custom sort locale */ + { + const NLS_LOCALE_LCID_INDEX *entry = find_lcid_entry( lcid & ~0x80000000 ); + if (entry) return locale_return_string( entry->name, type, buffer, len ); + } + return locale_return_string( locale->ssortlocale, type, buffer, len ); + + case LOCALE_SRELATIVELONGDATE: + return locale_return_string( locale->srelativelongdate, type, buffer, len ); + + case 0x007d: /* undocumented */ + return locale_return_number( 0, type, buffer, len ); + + case LOCALE_SSHORTESTAM: + return locale_return_string( locale->sshortestam, type, buffer, len ); + + case LOCALE_SSHORTESTPM: + return locale_return_string( locale->sshortestpm, type, buffer, len ); + + case LOCALE_SENGLANGUAGE: + return locale_return_string( locale->senglanguage, type, buffer, len ); + + case LOCALE_SENGCOUNTRY: + return locale_return_string( locale->sengcountry, type, buffer, len ); + + case LOCALE_STIMEFORMAT: + if ((ret = locale_return_reg_string( &entry_stimeformat, type, buffer, len )) != -1) return ret; + return locale_return_strarray( locale->stimeformat, 0, type, buffer, len ); + + case LOCALE_IDEFAULTANSICODEPAGE: + val = locale->idefaultansicodepage == CP_UTF8 ? CP_ACP : locale->idefaultansicodepage; + return locale_return_number( val, type, buffer, len ); + + case LOCALE_ITIMEMARKPOSN: + if (!get_locale_info( locale, lcid, LOCALE_STIMEFORMAT | (type & LOCALE_NOUSEROVERRIDE), + tmp, ARRAY_SIZE( tmp ))) break; + if (!(str = find_format( tmp, L"Hhmst" ))) break; + return locale_return_number( *str == 't', type, buffer, len ); + + case LOCALE_SYEARMONTH: + if ((ret = locale_return_reg_string( &entry_syearmonth, type, buffer, len )) != -1) return ret; + return locale_return_strarray( locale->syearmonth, 0, type, buffer, len ); + + case LOCALE_SENGCURRNAME: + return locale_return_string( locale->sengcurrname, type, buffer, len ); + + case LOCALE_SNATIVECURRNAME: + return locale_return_string( locale->snativecurrname, type, buffer, len ); + + case LOCALE_ICALENDARTYPE: + if ((ret = locale_return_reg_number( &entry_icalendartype, type, buffer, len )) != -1) return ret; + return locale_return_number( locale_strings[locale->scalendartype + 1], type, buffer, len ); + + case LOCALE_IPAPERSIZE: + if ((ret = locale_return_reg_number( &entry_ipapersize, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->ipapersize, type, buffer, len ); + + case LOCALE_IOPTIONALCALENDAR: + return locale_return_number( locale_strings[locale->scalendartype + 2], type, buffer, len ); + + case LOCALE_IFIRSTDAYOFWEEK: + if ((ret = locale_return_reg_number( &entry_ifirstdayofweek, type, buffer, len )) != -1) return ret; + return locale_return_number( (locale->ifirstdayofweek + 6) % 7, type, buffer, len ); + + case LOCALE_IFIRSTWEEKOFYEAR: + if ((ret = locale_return_reg_number( &entry_ifirstweekofyear, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->ifirstweekofyear, type, buffer, len ); + + case LOCALE_SMONTHNAME13: + return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) ? + locale->sgenitivemonth : locale->smonthname, + 12, type, buffer, len ); + + case LOCALE_SABBREVMONTHNAME13: + return locale_return_strarray( ((type & LOCALE_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) ? + locale->sabbrevgenitivemonth : locale->sabbrevmonthname, + 12, type, buffer, len ); + + case LOCALE_INEGNUMBER: + if ((ret = locale_return_reg_number( &entry_inegnumber, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->inegnumber, type, buffer, len ); + + case LOCALE_IDEFAULTMACCODEPAGE: + val = locale->idefaultmaccodepage == CP_UTF8 ? CP_MACCP : locale->idefaultmaccodepage; + return locale_return_number( val, type, buffer, len ); + + case LOCALE_IDEFAULTEBCDICCODEPAGE: + return locale_return_number( locale->idefaultebcdiccodepage, type, buffer, len ); + + case LOCALE_SSORTNAME: + sort = get_locale_sortname( lcid ); + return locale_return_data( sort, wcslen(sort) + 1, type, buffer, len ); + + case LOCALE_IDIGITSUBSTITUTION: + if ((ret = locale_return_reg_number( &entry_idigitsubstitution, type, buffer, len )) != -1) return ret; + return locale_return_number( locale->idigitsubstitution, type, buffer, len ); + } + SetLastError( ERROR_INVALID_FLAGS ); + return 0; +} + + +/* get calendar information from the locale.nls file */ +static int get_calendar_info( const NLS_LOCALE_DATA *locale, CALID id, CALTYPE type, + WCHAR *buffer, int len, DWORD *value ) +{ + unsigned int i, val = 0; + const struct calendar *cal; + + if (type & CAL_RETURN_NUMBER) + { + if (buffer || len || !value) goto invalid; + } + else if (len < 0 || value) goto invalid; + + if (id != CAL_GREGORIAN && type != CAL_ITWODIGITYEARMAX) + { + const USHORT *ids = locale_strings + locale->scalendartype; + for (i = 0; i < ids[0]; i++) if (ids[1 + i] == id) break; + if (i == ids[0]) goto invalid; + } + if (!(cal = get_calendar_data( locale, id ))) goto invalid; + + switch (LOWORD(type)) + { + case CAL_ICALINTVALUE: + return cal_return_number( cal->icalintvalue, type, buffer, len, value ); + + case CAL_SCALNAME: + return locale_return_strarray( locale->calnames, id - 1, type, buffer, len ); + + case CAL_IYEAROFFSETRANGE: + if (cal->iyearoffsetrange) + { + const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1); + const short *info = (const short *)locale_strings + array[0]; + val = (info[5] < 0) ? -info[5] : info[5] + 1; /* year zero */ + } + return cal_return_number( val, type, buffer, len, value ); + + case CAL_SERASTRING: + if (id == CAL_GREGORIAN) return locale_return_string( locale->serastring, type, buffer, len ); + if (cal->iyearoffsetrange) + { + const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1); + const short *info = (const short *)locale_strings + array[0]; + val = info[1] - 1; + } + return locale_return_strarray( cal->serastring, val, type, buffer, len ); + + case CAL_SSHORTDATE: + val = (id == CAL_GREGORIAN) ? locale->sshortdate : cal->sshortdate; + return locale_return_strarray( val, 0, type, buffer, len ); + + case CAL_SLONGDATE: + val = (id == CAL_GREGORIAN) ? locale->slongdate : cal->slongdate; + return locale_return_strarray( val, 0, type, buffer, len ); + + case CAL_SDAYNAME1: + case CAL_SDAYNAME2: + case CAL_SDAYNAME3: + case CAL_SDAYNAME4: + case CAL_SDAYNAME5: + case CAL_SDAYNAME6: + case CAL_SDAYNAME7: + val = (id == CAL_GREGORIAN) ? locale->sdayname : cal->sdayname; + return locale_return_strarray( val, (LOWORD(type) - CAL_SDAYNAME1 + 1) % 7, type, buffer, len ); + + case CAL_SABBREVDAYNAME1: + case CAL_SABBREVDAYNAME2: + case CAL_SABBREVDAYNAME3: + case CAL_SABBREVDAYNAME4: + case CAL_SABBREVDAYNAME5: + case CAL_SABBREVDAYNAME6: + case CAL_SABBREVDAYNAME7: + val = (id == CAL_GREGORIAN) ? locale->sabbrevdayname : cal->sabbrevdayname; + return locale_return_strarray( val, (LOWORD(type) - CAL_SABBREVDAYNAME1 + 1) % 7, type, buffer, len ); + case CAL_SMONTHNAME1: + case CAL_SMONTHNAME2: + case CAL_SMONTHNAME3: + case CAL_SMONTHNAME4: + case CAL_SMONTHNAME5: + case CAL_SMONTHNAME6: + case CAL_SMONTHNAME7: + case CAL_SMONTHNAME8: + case CAL_SMONTHNAME9: + case CAL_SMONTHNAME10: + case CAL_SMONTHNAME11: + case CAL_SMONTHNAME12: + case CAL_SMONTHNAME13: + if (id != CAL_GREGORIAN) val = cal->smonthname; + else if ((type & CAL_RETURN_GENITIVE_NAMES) && locale->sgenitivemonth) val = locale->sgenitivemonth; + else val = locale->smonthname; + return locale_return_strarray( val, LOWORD(type) - CAL_SMONTHNAME1, type, buffer, len ); + + case CAL_SABBREVMONTHNAME1: + case CAL_SABBREVMONTHNAME2: + case CAL_SABBREVMONTHNAME3: + case CAL_SABBREVMONTHNAME4: + case CAL_SABBREVMONTHNAME5: + case CAL_SABBREVMONTHNAME6: + case CAL_SABBREVMONTHNAME7: + case CAL_SABBREVMONTHNAME8: + case CAL_SABBREVMONTHNAME9: + case CAL_SABBREVMONTHNAME10: + case CAL_SABBREVMONTHNAME11: + case CAL_SABBREVMONTHNAME12: + case CAL_SABBREVMONTHNAME13: + if (id != CAL_GREGORIAN) val = cal->sabbrevmonthname; + else if ((type & CAL_RETURN_GENITIVE_NAMES) && locale->sabbrevgenitivemonth) val = locale->sabbrevgenitivemonth; + else val = locale->sabbrevmonthname; + return locale_return_strarray( val, LOWORD(type) - CAL_SABBREVMONTHNAME1, type, buffer, len ); + + case CAL_SYEARMONTH: + val = (id == CAL_GREGORIAN) ? locale->syearmonth : cal->syearmonth; + return locale_return_strarray( val, 0, type, buffer, len ); + + case CAL_ITWODIGITYEARMAX: + return cal_return_number( cal->itwodigityearmax, type, buffer, len, value ); + + case CAL_SSHORTESTDAYNAME1: + case CAL_SSHORTESTDAYNAME2: + case CAL_SSHORTESTDAYNAME3: + case CAL_SSHORTESTDAYNAME4: + case CAL_SSHORTESTDAYNAME5: + case CAL_SSHORTESTDAYNAME6: + case CAL_SSHORTESTDAYNAME7: + val = (id == CAL_GREGORIAN) ? locale->sshortestdayname : cal->sshortestdayname; + return locale_return_strarray( val, (LOWORD(type) - CAL_SSHORTESTDAYNAME1 + 1) % 7, type, buffer, len ); + + case CAL_SMONTHDAY: + val = (id == CAL_GREGORIAN) ? locale->smonthday : cal->smonthday; + return locale_return_strarray( val, 0, type, buffer, len ); + + case CAL_SABBREVERASTRING: + if (id == CAL_GREGORIAN) return locale_return_string( locale->sabbreverastring, type, buffer, len ); + if (cal->iyearoffsetrange) + { + const DWORD *array = (const DWORD *)(locale_strings + cal->iyearoffsetrange + 1); + const short *info = (const short *)locale_strings + array[0]; + val = info[1] - 1; + } + return locale_return_strarray( cal->sabbreverastring, val, type, buffer, len ); + + case CAL_SRELATIVELONGDATE: + val = (id == CAL_GREGORIAN) ? locale->srelativelongdate : cal->srelativelongdate; + return locale_return_string( val, type, buffer, len ); + + case CAL_SENGLISHERANAME: + case CAL_SENGLISHABBREVERANAME: + /* not supported on Windows */ + break; + } + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + +invalid: + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; +} + + +/* get geo information from the locale.nls file */ +static int get_geo_info( const struct geo_id *geo, enum SYSGEOTYPE type, + WCHAR *buffer, int len, LANGID lang ) +{ + WCHAR tmp[12]; + const WCHAR *str = tmp; + int ret; + + switch (type) + { + case GEO_NATION: + if (geo->class != GEOCLASS_NATION) return 0; + /* fall through */ + case GEO_ID: + swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->id ); + break; + case GEO_ISO_UN_NUMBER: + swprintf( tmp, ARRAY_SIZE(tmp), L"%03u", geo->uncode ); + break; + case GEO_PARENT: + swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->parent ); + break; + case GEO_DIALINGCODE: + swprintf( tmp, ARRAY_SIZE(tmp), L"%u", geo->dialcode ); + break; + case GEO_ISO2: + str = geo->iso2; + break; + case GEO_ISO3: + str = geo->iso3; + break; + case GEO_LATITUDE: + str = geo->latitude; + break; + case GEO_LONGITUDE: + str = geo->longitude; + break; + case GEO_CURRENCYCODE: + str = geo->currcode; + break; + case GEO_CURRENCYSYMBOL: + str = geo->currsymbol; + break; + case GEO_RFC1766: + case GEO_LCID: + case GEO_FRIENDLYNAME: + case GEO_OFFICIALNAME: + case GEO_TIMEZONES: + case GEO_OFFICIALLANGUAGES: + case GEO_NAME: + FIXME( "type %u is not supported\n", type ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; + default: + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + + ret = lstrlenW(str) + 1; + if (!buffer || !len) return ret; + + memcpy( buffer, str, min( ret, len ) * sizeof(WCHAR) ); + if (len < ret) SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return len < ret ? 0 : ret; +} + + +/* update a registry value based on the current user locale info */ +static void update_registry_value( UINT type, const WCHAR *subkey, const WCHAR *value ) +{ + WCHAR buffer[80]; + UINT len = get_locale_info( user_locale, user_lcid, type, buffer, ARRAY_SIZE(buffer) ); + if (len) RegSetKeyValueW( intl_key, subkey, value, REG_SZ, (BYTE *)buffer, len * sizeof(WCHAR) ); +} + + +/* update all registry values upon user locale change */ +static void update_locale_registry(void) +{ + WCHAR buffer[80]; + UINT len; + + len = swprintf( buffer, ARRAY_SIZE(buffer), L"%08x", GetUserDefaultLCID() ); + RegSetValueExW( intl_key, L"Locale", 0, REG_SZ, (BYTE *)buffer, (len + 1) * sizeof(WCHAR) ); + +#define UPDATE(val,entry) update_registry_value( LOCALE_NOUSEROVERRIDE | (val), (entry).subkey, (entry).value ) + UPDATE( LOCALE_ICALENDARTYPE, entry_icalendartype ); + UPDATE( LOCALE_ICOUNTRY, entry_icountry ); + UPDATE( LOCALE_ICURRDIGITS, entry_icurrdigits ); + UPDATE( LOCALE_ICURRENCY, entry_icurrency ); + UPDATE( LOCALE_IDIGITS, entry_idigits ); + UPDATE( LOCALE_IDIGITSUBSTITUTION, entry_idigitsubstitution ); + UPDATE( LOCALE_IFIRSTDAYOFWEEK, entry_ifirstdayofweek ); + UPDATE( LOCALE_IFIRSTWEEKOFYEAR, entry_ifirstweekofyear ); + UPDATE( LOCALE_ILZERO, entry_ilzero ); + UPDATE( LOCALE_IMEASURE, entry_imeasure ); + UPDATE( LOCALE_INEGCURR, entry_inegcurr ); + UPDATE( LOCALE_INEGNUMBER, entry_inegnumber ); + UPDATE( LOCALE_IPAPERSIZE, entry_ipapersize ); + UPDATE( LOCALE_S1159, entry_s1159 ); + UPDATE( LOCALE_S2359, entry_s2359 ); + UPDATE( LOCALE_SCURRENCY, entry_scurrency ); + UPDATE( LOCALE_SDECIMAL, entry_sdecimal ); + UPDATE( LOCALE_SGROUPING, entry_sgrouping ); + UPDATE( LOCALE_SINTLSYMBOL, entry_sintlsymbol ); + UPDATE( LOCALE_SLIST, entry_slist ); + UPDATE( LOCALE_SLONGDATE, entry_slongdate ); + UPDATE( LOCALE_SMONDECIMALSEP, entry_smondecimalsep ); + UPDATE( LOCALE_SMONGROUPING, entry_smongrouping ); + UPDATE( LOCALE_SMONTHOUSANDSEP, entry_smonthousandsep ); + UPDATE( LOCALE_SNATIVEDIGITS, entry_snativedigits ); + UPDATE( LOCALE_SNEGATIVESIGN, entry_snegativesign ); + UPDATE( LOCALE_SPOSITIVESIGN, entry_spositivesign ); + UPDATE( LOCALE_SSHORTDATE, entry_sshortdate ); + UPDATE( LOCALE_SSHORTTIME, entry_sshorttime ); + UPDATE( LOCALE_STHOUSAND, entry_sthousand ); + UPDATE( LOCALE_STIMEFORMAT, entry_stimeformat ); + UPDATE( LOCALE_SYEARMONTH, entry_syearmonth ); +#undef UPDATE + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_IDATE, NULL, L"iDate" ); + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIME, NULL, L"iTime" ); + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITIMEMARKPOSN, NULL, L"iTimePrefix" ); + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_ITLZERO, NULL, L"iTLZero" ); + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SDATE, NULL, L"sDate" ); + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_STIME, NULL, L"sTime" ); + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SABBREVLANGNAME, NULL, L"sLanguage" ); + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SCOUNTRY, NULL, L"sCountry" ); + update_registry_value( LOCALE_NOUSEROVERRIDE | LOCALE_SNAME, NULL, L"LocaleName" ); + SetUserGeoID( user_locale->igeoid ); +} + + +/*********************************************************************** + * init_locale + */ +void init_locale( HMODULE module ) +{ + USHORT utf8[2] = { 0, CP_UTF8 }; + USHORT *ansi_ptr, *oem_ptr; + WCHAR bufferW[LOCALE_NAME_MAX_LENGTH]; + DYNAMIC_TIME_ZONE_INFORMATION timezone; + const WCHAR *user_locale_name; + DWORD count; + SIZE_T size; + HKEY hkey; + + kernelbase_handle = module; + load_locale_nls(); + load_sortdefault_nls(); + + if (system_lcid == LOCALE_CUSTOM_UNSPECIFIED) system_lcid = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ); + system_locale = NlsValidateLocale( &system_lcid, 0 ); + + NtQueryDefaultLocale( TRUE, &user_lcid ); + if (!(user_locale = NlsValidateLocale( &user_lcid, 0 ))) + { + if (GetEnvironmentVariableW( L"WINEUSERLOCALE", bufferW, ARRAY_SIZE(bufferW) )) + user_locale = get_locale_by_name( bufferW, &user_lcid ); + if (!user_locale) user_locale = system_locale; + } + user_lcid = user_locale->ilanguage; + if (user_lcid == LOCALE_CUSTOM_UNSPECIFIED) user_lcid = LOCALE_CUSTOM_DEFAULT; + + if (GetEnvironmentVariableW( L"WINEUNIXCP", bufferW, ARRAY_SIZE(bufferW) )) + unix_cp = wcstoul( bufferW, NULL, 10 ); + + NtGetNlsSectionPtr( 12, NormalizationC, NULL, (void **)&norm_info, &size ); + + ansi_ptr = NtCurrentTeb()->Peb->AnsiCodePageData ? NtCurrentTeb()->Peb->AnsiCodePageData : utf8; + oem_ptr = NtCurrentTeb()->Peb->OemCodePageData ? NtCurrentTeb()->Peb->OemCodePageData : utf8; + RtlInitCodePageTable( ansi_ptr, &ansi_cpinfo ); + RtlInitCodePageTable( oem_ptr, &oem_cpinfo ); + + RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Nls", + 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &nls_key, NULL ); + RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", + 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &tz_key, NULL ); + RegCreateKeyExW( HKEY_CURRENT_USER, L"Control Panel\\International", + 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &intl_key, NULL ); + + current_locale_sort = get_language_sort( LOCALE_NAME_USER_DEFAULT ); + + if (GetDynamicTimeZoneInformation( &timezone ) != TIME_ZONE_ID_INVALID && + !RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\TimeZoneInformation", + 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { + RegSetValueExW( hkey, L"StandardName", 0, REG_SZ, (BYTE *)timezone.StandardName, + (lstrlenW(timezone.StandardName) + 1) * sizeof(WCHAR) ); + RegSetValueExW( hkey, L"TimeZoneKeyName", 0, REG_SZ, (BYTE *)timezone.TimeZoneKeyName, + (lstrlenW(timezone.TimeZoneKeyName) + 1) * sizeof(WCHAR) ); + RegCloseKey( hkey ); + } + + /* Update registry contents if the user locale has changed. + * This simulates the action of the Windows control panel. */ + + user_locale_name = locale_strings + user_locale->sname + 1; + count = sizeof(bufferW); + if (!RegQueryValueExW( intl_key, L"LocaleName", NULL, NULL, (BYTE *)bufferW, &count )) + { + if (!wcscmp( bufferW, user_locale_name )) return; /* unchanged */ + TRACE( "updating registry, locale changed %s -> %s\n", + debugstr_w(bufferW), debugstr_w(user_locale_name) ); + } + else TRACE( "updating registry, locale changed none -> %s\n", debugstr_w(user_locale_name) ); + + update_locale_registry(); + + if (!RegCreateKeyExW( nls_key, L"Codepage", + 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { + count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", GetACP() ); + RegSetValueExW( hkey, L"ACP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) ); + count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", GetOEMCP() ); + RegSetValueExW( hkey, L"OEMCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) ); + count = swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03d", system_locale->idefaultmaccodepage ); + RegSetValueExW( hkey, L"MACCP", 0, REG_SZ, (BYTE *)bufferW, (count + 1) * sizeof(WCHAR) ); + RegCloseKey( hkey ); + } +} + + +static inline WCHAR casemap( const USHORT *table, WCHAR ch ) +{ + return ch + table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0x0f)]; +} + + +static inline unsigned int casemap_high( const USHORT *table, WCHAR high, WCHAR low ) +{ + unsigned int off = table[table[256 + (high - 0xd800)] + ((low >> 5) & 0x1f)] + 2 * (low & 0x1f); + return 0x10000 + ((high - 0xd800) << 10) + (low - 0xdc00) + MAKELONG( table[off], table[off+1] ); +} + + +static inline BOOL table_has_high_planes( const USHORT *table ) +{ + return table[0] >= 0x500; +} + + +static inline int put_utf16( WCHAR *dst, int pos, int dstlen, unsigned int ch ) +{ + if (ch >= 0x10000) + { + if (pos < dstlen - 1) + { + ch -= 0x10000; + dst[pos] = 0xd800 | (ch >> 10); + dst[pos + 1] = 0xdc00 | (ch & 0x3ff); + } + return 2; + } + if (pos < dstlen) dst[pos] = ch; + return 1; +} + + +static inline WORD get_char_type( DWORD type, WCHAR ch ) +{ + const BYTE *ptr = sort.ctype_idx + ((const WORD *)sort.ctype_idx)[ch >> 8]; + ptr = sort.ctype_idx + ((const WORD *)ptr)[(ch >> 4) & 0x0f] + (ch & 0x0f); + return sort.ctypes[*ptr * 3 + type / 2]; +} + + +static inline void map_byterev( const WCHAR *src, int len, WCHAR *dst ) +{ + while (len--) *dst++ = RtlUshortByteSwap( *src++ ); +} + + +static int casemap_string( const USHORT *table, const WCHAR *src, int srclen, WCHAR *dst, int dstlen ) +{ + if (table_has_high_planes( table )) + { + unsigned int ch; + int pos = 0; + + while (srclen) + { + if (srclen > 1 && IS_SURROGATE_PAIR( src[0], src[1] )) + { + ch = casemap_high( table, src[0], src[1] ); + src += 2; + srclen -= 2; + } + else + { + ch = casemap( table, *src ); + src++; + srclen--; + } + pos += put_utf16( dst, pos, dstlen, ch ); + } + return pos; + } + else + { + int pos, ret = srclen; + + for (pos = 0; pos < dstlen && srclen; pos++, src++, srclen--) + dst[pos] = casemap( table, *src ); + return ret; + } +} + + +static union char_weights get_char_weights( WCHAR c, UINT except ) +{ + union char_weights ret; + + ret.val = except ? sort.keys[sort.keys[except + (c >> 8)] + (c & 0xff)] : sort.keys[c]; + return ret; +} + + +static BYTE rol( BYTE val, BYTE count ) +{ + return (val << count) | (val >> (8 - count)); +} + + +static BYTE get_char_props( const struct norm_table *info, unsigned int ch ) +{ + const BYTE *level1 = (const BYTE *)((const USHORT *)info + info->props_level1); + const BYTE *level2 = (const BYTE *)((const USHORT *)info + info->props_level2); + BYTE off = level1[ch / 128]; + + if (!off || off >= 0xfb) return rol( off, 5 ); + return level2[(off - 1) * 128 + ch % 128]; +} + + +static const WCHAR *get_decomposition( WCHAR ch, unsigned int *ret_len ) +{ + const struct pair { WCHAR src; USHORT dst; } *pairs; + const USHORT *hash_table = (const USHORT *)norm_info + norm_info->decomp_hash; + const WCHAR *ret; + unsigned int i, pos, end, len, hash; + + *ret_len = 1; + hash = ch % norm_info->decomp_size; + pos = hash_table[hash]; + if (pos >> 13) + { + if (get_char_props( norm_info, ch ) != 0xbf) return NULL; + ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pos & 0x1fff); + len = pos >> 13; + } + else + { + pairs = (const struct pair *)((const USHORT *)norm_info + norm_info->decomp_map); + + /* find the end of the hash bucket */ + for (i = hash + 1; i < norm_info->decomp_size; i++) if (!(hash_table[i] >> 13)) break; + if (i < norm_info->decomp_size) end = hash_table[i]; + else for (end = pos; pairs[end].src; end++) ; + + for ( ; pos < end; pos++) + { + if (pairs[pos].src != (WCHAR)ch) continue; + ret = (const USHORT *)norm_info + norm_info->decomp_seq + (pairs[pos].dst & 0x1fff); + len = pairs[pos].dst >> 13; + break; + } + if (pos >= end) return NULL; + } + + if (len == 7) while (ret[len]) len++; + if (!ret[0]) len = 0; /* ignored char */ + *ret_len = len; + return ret; +} + + +static WCHAR compose_chars( WCHAR ch1, WCHAR ch2 ) +{ + const USHORT *table = (const USHORT *)norm_info + norm_info->comp_hash; + const WCHAR *chars = (const USHORT *)norm_info + norm_info->comp_seq; + unsigned int hash, start, end, i; + WCHAR ch[3]; + + hash = (ch1 + 95 * ch2) % norm_info->comp_size; + start = table[hash]; + end = table[hash + 1]; + while (start < end) + { + for (i = 0; i < 3; i++, start++) + { + ch[i] = chars[start]; + if (IS_HIGH_SURROGATE( ch[i] )) start++; + } + if (ch[0] == ch1 && ch[1] == ch2) return ch[2]; + } + return 0; +} + + +static UINT get_locale_codepage( const NLS_LOCALE_DATA *locale, ULONG flags ) +{ + UINT ret = locale->idefaultansicodepage; + if ((flags & LOCALE_USE_CP_ACP) || ret == CP_UTF8) ret = ansi_cpinfo.CodePage; + return ret; +} + + +static UINT get_lcid_codepage( LCID lcid, ULONG flags ) +{ + UINT ret = ansi_cpinfo.CodePage; + + if (!(flags & LOCALE_USE_CP_ACP) && lcid != system_lcid) + { + const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 ); + if (locale) ret = locale->idefaultansicodepage; + } + return ret; +} + + +static const CPTABLEINFO *get_codepage_table( UINT codepage ) +{ + static const CPTABLEINFO utf7_cpinfo = { CP_UTF7, 5, '?', 0xfffd, '?', '?' }; + static const CPTABLEINFO utf8_cpinfo = { CP_UTF8, 4, '?', 0xfffd, '?', '?' }; + unsigned int i; + USHORT *ptr; + SIZE_T size; + + switch (codepage) + { + case CP_ACP: + return &ansi_cpinfo; + case CP_OEMCP: + return &oem_cpinfo; + case CP_MACCP: + codepage = system_locale->idefaultmaccodepage; + break; + case CP_THREAD_ACP: + codepage = get_lcid_codepage( NtCurrentTeb()->CurrentLocale, 0 ); + break; + } + if (codepage == ansi_cpinfo.CodePage) return &ansi_cpinfo; + if (codepage == oem_cpinfo.CodePage) return &oem_cpinfo; + if (codepage == CP_UTF8) return &utf8_cpinfo; + if (codepage == CP_UTF7) return &utf7_cpinfo; + + RtlEnterCriticalSection( &locale_section ); + + for (i = 0; i < nb_codepages; i++) if (codepages[i].CodePage == codepage) goto done; + + if (i == ARRAY_SIZE( codepages )) + { + RtlLeaveCriticalSection( &locale_section ); + ERR( "too many codepages\n" ); + return NULL; + } + if (NtGetNlsSectionPtr( 11, codepage, NULL, (void **)&ptr, &size )) + { + RtlLeaveCriticalSection( &locale_section ); + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + RtlInitCodePageTable( ptr, &codepages[i] ); + nb_codepages++; +done: + RtlLeaveCriticalSection( &locale_section ); + return &codepages[i]; +} + + +static const WCHAR *get_ligature( WCHAR wc ) +{ + int low = 0, high = ARRAY_SIZE( ligatures ) -1; + while (low <= high) + { + int pos = (low + high) / 2; + if (ligatures[pos][0] < wc) low = pos + 1; + else if (ligatures[pos][0] > wc) high = pos - 1; + else return ligatures[pos] + 1; + } + return NULL; +} + + +static NTSTATUS expand_ligatures( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen ) +{ + int i, len, pos = 0; + NTSTATUS ret = STATUS_SUCCESS; + const WCHAR *expand; + + for (i = 0; i < srclen; i++) + { + if (!(expand = get_ligature( src[i] ))) + { + expand = src + i; + len = 1; + } + else len = lstrlenW( expand ); + + if (*dstlen && ret == STATUS_SUCCESS) + { + if (pos + len <= *dstlen) memcpy( dst + pos, expand, len * sizeof(WCHAR) ); + else ret = STATUS_BUFFER_TOO_SMALL; + } + pos += len; + } + *dstlen = pos; + return ret; +} + + +static NTSTATUS fold_digits( const WCHAR *src, int srclen, WCHAR *dst, int *dstlen ) +{ + NTSTATUS ret = STATUS_SUCCESS; + int len = casemap_string( charmaps[CHARMAP_FOLDDIGITS], src, srclen, dst, *dstlen ); + + if (*dstlen && *dstlen < len) ret = STATUS_BUFFER_TOO_SMALL; + *dstlen = len; + return ret; +} + + +static NTSTATUS fold_string( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int *dstlen ) +{ + NTSTATUS ret; + WCHAR *tmp; + + switch (flags) + { + case MAP_PRECOMPOSED: + return RtlNormalizeString( NormalizationC, src, srclen, dst, dstlen ); + case MAP_FOLDCZONE: + case MAP_PRECOMPOSED | MAP_FOLDCZONE: + return RtlNormalizeString( NormalizationKC, src, srclen, dst, dstlen ); + case MAP_COMPOSITE: + return RtlNormalizeString( NormalizationD, src, srclen, dst, dstlen ); + case MAP_COMPOSITE | MAP_FOLDCZONE: + return RtlNormalizeString( NormalizationKD, src, srclen, dst, dstlen ); + case MAP_FOLDDIGITS: + return fold_digits( src, srclen, dst, dstlen ); + case MAP_EXPAND_LIGATURES: + case MAP_EXPAND_LIGATURES | MAP_FOLDCZONE: + return expand_ligatures( src, srclen, dst, dstlen ); + case MAP_FOLDDIGITS | MAP_PRECOMPOSED: + if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + fold_digits( src, srclen, tmp, &srclen ); + ret = RtlNormalizeString( NormalizationC, tmp, srclen, dst, dstlen ); + break; + case MAP_FOLDDIGITS | MAP_FOLDCZONE: + case MAP_FOLDDIGITS | MAP_PRECOMPOSED | MAP_FOLDCZONE: + if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + fold_digits( src, srclen, tmp, &srclen ); + ret = RtlNormalizeString( NormalizationKC, tmp, srclen, dst, dstlen ); + break; + case MAP_FOLDDIGITS | MAP_COMPOSITE: + if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + fold_digits( src, srclen, tmp, &srclen ); + ret = RtlNormalizeString( NormalizationD, tmp, srclen, dst, dstlen ); + break; + case MAP_FOLDDIGITS | MAP_COMPOSITE | MAP_FOLDCZONE: + if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + fold_digits( src, srclen, tmp, &srclen ); + ret = RtlNormalizeString( NormalizationKD, tmp, srclen, dst, dstlen ); + break; + case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS: + case MAP_EXPAND_LIGATURES | MAP_FOLDDIGITS | MAP_FOLDCZONE: + if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, srclen * sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + fold_digits( src, srclen, tmp, &srclen ); + ret = expand_ligatures( tmp, srclen, dst, dstlen ); + break; + default: + return STATUS_INVALID_PARAMETER_1; + } + RtlFreeHeap( GetProcessHeap(), 0, tmp ); + return ret; +} + + +static int mbstowcs_cpsymbol( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen ) +{ + int len, i; + + if (flags) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + if (!dstlen) return srclen; + len = min( srclen, dstlen ); + for (i = 0; i < len; i++) + { + unsigned char c = src[i]; + dst[i] = (c < 0x20) ? c : c + 0xf000; + } + if (len < srclen) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return len; +} + + +static int mbstowcs_utf7( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen ) +{ + static const signed char base64_decoding_table[] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */ + }; + + const char *source_end = src + srclen; + int offset = 0, pos = 0; + DWORD byte_pair = 0; + + if (flags) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } +#define OUTPUT(ch) \ + do { \ + if (dstlen > 0) \ + { \ + if (pos >= dstlen) goto overflow; \ + dst[pos] = (ch); \ + } \ + pos++; \ + } while(0) + + while (src < source_end) + { + if (*src == '+') + { + src++; + if (src >= source_end) break; + if (*src == '-') + { + /* just a plus sign escaped as +- */ + OUTPUT( '+' ); + src++; + continue; + } + + do + { + signed char sextet = *src; + if (sextet == '-') + { + /* skip over the dash and end base64 decoding + * the current, unfinished byte pair is discarded */ + src++; + offset = 0; + break; + } + if (sextet < 0) + { + /* the next character of src is < 0 and therefore not part of a base64 sequence + * the current, unfinished byte pair is NOT discarded in this case + * this is probably a bug in Windows */ + break; + } + sextet = base64_decoding_table[sextet]; + if (sextet == -1) + { + /* -1 means that the next character of src is not part of a base64 sequence + * in other words, all sextets in this base64 sequence have been processed + * the current, unfinished byte pair is discarded */ + offset = 0; + break; + } + + byte_pair = (byte_pair << 6) | sextet; + offset += 6; + if (offset >= 16) + { + /* this byte pair is done */ + OUTPUT( byte_pair >> (offset - 16) ); + offset -= 16; + } + src++; + } + while (src < source_end); + } + else + { + OUTPUT( (unsigned char)*src ); + src++; + } + } + return pos; + +overflow: + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; +#undef OUTPUT +} + + +static int mbstowcs_utf8( DWORD flags, const char *src, int srclen, WCHAR *dst, int dstlen ) +{ + DWORD reslen; + NTSTATUS status; + + if (!dstlen) dst = NULL; + status = RtlUTF8ToUnicodeN( dst, dstlen * sizeof(WCHAR), &reslen, src, srclen ); + if (status == STATUS_SOME_NOT_MAPPED) + { + if (flags & MB_ERR_INVALID_CHARS) + { + SetLastError( ERROR_NO_UNICODE_TRANSLATION ); + return 0; + } + } + else if (!set_ntstatus( status )) reslen = 0; + + return reslen / sizeof(WCHAR); +} + + +static inline int is_private_use_area_char( WCHAR code ) +{ + return (code >= 0xe000 && code <= 0xf8ff); +} + + +static int check_invalid_chars( const CPTABLEINFO *info, const unsigned char *src, int srclen ) +{ + if (info->DBCSOffsets) + { + for ( ; srclen; src++, srclen-- ) + { + USHORT off = info->DBCSOffsets[*src]; + if (off) + { + if (srclen == 1) break; /* partial char, error */ + if (info->DBCSOffsets[off + src[1]] == info->UniDefaultChar && + ((src[0] << 8) | src[1]) != info->TransUniDefaultChar) break; + src++; + srclen--; + continue; + } + if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar) + break; + if (is_private_use_area_char( info->MultiByteTable[*src] )) break; + } + } + else + { + for ( ; srclen; src++, srclen-- ) + { + if (info->MultiByteTable[*src] == info->UniDefaultChar && *src != info->TransUniDefaultChar) + break; + if (is_private_use_area_char( info->MultiByteTable[*src] )) break; + } + } + return !!srclen; + +} + + +static int mbstowcs_decompose( const CPTABLEINFO *info, const unsigned char *src, int srclen, + WCHAR *dst, int dstlen ) +{ + WCHAR ch; + USHORT off; + int len; + const WCHAR *decomp; + unsigned int decomp_len; + + if (info->DBCSOffsets) + { + if (!dstlen) /* compute length */ + { + for (len = 0; srclen; srclen--, src++, len += decomp_len) + { + if ((off = info->DBCSOffsets[*src])) + { + if (srclen > 1 && src[1]) + { + src++; + srclen--; + ch = info->DBCSOffsets[off + *src]; + } + else ch = info->UniDefaultChar; + } + else ch = info->MultiByteTable[*src]; + get_decomposition( ch, &decomp_len ); + } + return len; + } + + for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len) + { + if ((off = info->DBCSOffsets[*src])) + { + if (srclen > 1 && src[1]) + { + src++; + srclen--; + ch = info->DBCSOffsets[off + *src]; + } + else ch = info->UniDefaultChar; + } + else ch = info->MultiByteTable[*src]; + + if ((decomp = get_decomposition( ch, &decomp_len ))) + { + if (len < decomp_len) break; + memcpy( dst, decomp, decomp_len * sizeof(WCHAR) ); + } + else *dst = ch; + } + } + else + { + if (!dstlen) /* compute length */ + { + for (len = 0; srclen; srclen--, src++, len += decomp_len) + get_decomposition( info->MultiByteTable[*src], &decomp_len ); + return len; + } + + for (len = dstlen; srclen && len; srclen--, src++, dst += decomp_len, len -= decomp_len) + { + ch = info->MultiByteTable[*src]; + if ((decomp = get_decomposition( ch, &decomp_len ))) + { + if (len < decomp_len) break; + memcpy( dst, decomp, decomp_len * sizeof(WCHAR) ); + } + else *dst = ch; + } + } + + if (srclen) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return dstlen - len; +} + + +static int mbstowcs_sbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen, + WCHAR *dst, int dstlen ) +{ + const USHORT *table = info->MultiByteTable; + int ret = srclen; + + if (!dstlen) return srclen; + + if (dstlen < srclen) /* buffer too small: fill it up to dstlen and return error */ + { + srclen = dstlen; + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + ret = 0; + } + + while (srclen >= 16) + { + dst[0] = table[src[0]]; + dst[1] = table[src[1]]; + dst[2] = table[src[2]]; + dst[3] = table[src[3]]; + dst[4] = table[src[4]]; + dst[5] = table[src[5]]; + dst[6] = table[src[6]]; + dst[7] = table[src[7]]; + dst[8] = table[src[8]]; + dst[9] = table[src[9]]; + dst[10] = table[src[10]]; + dst[11] = table[src[11]]; + dst[12] = table[src[12]]; + dst[13] = table[src[13]]; + dst[14] = table[src[14]]; + dst[15] = table[src[15]]; + src += 16; + dst += 16; + srclen -= 16; + } + + /* now handle the remaining characters */ + src += srclen; + dst += srclen; + switch (srclen) + { + case 15: dst[-15] = table[src[-15]]; + case 14: dst[-14] = table[src[-14]]; + case 13: dst[-13] = table[src[-13]]; + case 12: dst[-12] = table[src[-12]]; + case 11: dst[-11] = table[src[-11]]; + case 10: dst[-10] = table[src[-10]]; + case 9: dst[-9] = table[src[-9]]; + case 8: dst[-8] = table[src[-8]]; + case 7: dst[-7] = table[src[-7]]; + case 6: dst[-6] = table[src[-6]]; + case 5: dst[-5] = table[src[-5]]; + case 4: dst[-4] = table[src[-4]]; + case 3: dst[-3] = table[src[-3]]; + case 2: dst[-2] = table[src[-2]]; + case 1: dst[-1] = table[src[-1]]; + case 0: break; + } + return ret; +} + + +static int mbstowcs_dbcs( const CPTABLEINFO *info, const unsigned char *src, int srclen, + WCHAR *dst, int dstlen ) +{ + USHORT off; + int i; + + if (!dstlen) + { + for (i = 0; srclen; i++, src++, srclen--) + if (info->DBCSOffsets[*src] && srclen > 1 && src[1]) { src++; srclen--; } + return i; + } + + for (i = dstlen; srclen && i; i--, srclen--, src++, dst++) + { + if ((off = info->DBCSOffsets[*src])) + { + if (srclen > 1 && src[1]) + { + src++; + srclen--; + *dst = info->DBCSOffsets[off + *src]; + } + else *dst = info->UniDefaultChar; + } + else *dst = info->MultiByteTable[*src]; + } + if (srclen) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return dstlen - i; +} + + +static int mbstowcs_codepage( const CPTABLEINFO *info, DWORD flags, const char *src, int srclen, + WCHAR *dst, int dstlen ) +{ + CPTABLEINFO local_info; + const unsigned char *str = (const unsigned char *)src; + + if ((flags & MB_USEGLYPHCHARS) && info->MultiByteTable[256] == 256) + { + local_info = *info; + local_info.MultiByteTable += 257; + info = &local_info; + } + if ((flags & MB_ERR_INVALID_CHARS) && check_invalid_chars( info, str, srclen )) + { + SetLastError( ERROR_NO_UNICODE_TRANSLATION ); + return 0; + } + + if (flags & MB_COMPOSITE) return mbstowcs_decompose( info, str, srclen, dst, dstlen ); + + if (info->DBCSOffsets) + return mbstowcs_dbcs( info, str, srclen, dst, dstlen ); + else + return mbstowcs_sbcs( info, str, srclen, dst, dstlen ); +} + + +static int wcstombs_cpsymbol( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen, + const char *defchar, BOOL *used ) +{ + int len, i; + + if (flags) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + if (defchar || used) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (!dstlen) return srclen; + len = min( srclen, dstlen ); + for (i = 0; i < len; i++) + { + if (src[i] < 0x20) dst[i] = src[i]; + else if (src[i] >= 0xf020 && src[i] < 0xf100) dst[i] = src[i] - 0xf000; + else + { + SetLastError( ERROR_NO_UNICODE_TRANSLATION ); + return 0; + } + } + if (srclen > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return len; +} + + +static int wcstombs_utf7( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen, + const char *defchar, BOOL *used ) +{ + static const char directly_encodable[] = + { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ + 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3f */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 0x70 - 0x7a */ + }; +#define ENCODABLE(ch) ((ch) <= 0x7a && directly_encodable[(ch)]) + + static const char base64_encoding_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + const WCHAR *source_end = src + srclen; + int pos = 0; + + if (defchar || used) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (flags) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + +#define OUTPUT(ch) \ + do { \ + if (dstlen > 0) \ + { \ + if (pos >= dstlen) goto overflow; \ + dst[pos] = (ch); \ + } \ + pos++; \ + } while (0) + + while (src < source_end) + { + if (*src == '+') + { + OUTPUT( '+' ); + OUTPUT( '-' ); + src++; + } + else if (ENCODABLE(*src)) + { + OUTPUT( *src ); + src++; + } + else + { + unsigned int offset = 0, byte_pair = 0; + + OUTPUT( '+' ); + while (src < source_end && !ENCODABLE(*src)) + { + byte_pair = (byte_pair << 16) | *src; + offset += 16; + while (offset >= 6) + { + offset -= 6; + OUTPUT( base64_encoding_table[(byte_pair >> offset) & 0x3f] ); + } + src++; + } + if (offset) + { + /* Windows won't create a padded base64 character if there's no room for the - sign + * as well ; this is probably a bug in Windows */ + if (dstlen > 0 && pos + 1 >= dstlen) goto overflow; + byte_pair <<= (6 - offset); + OUTPUT( base64_encoding_table[byte_pair & 0x3f] ); + } + /* Windows always explicitly terminates the base64 sequence + even though RFC 2152 (page 3, rule 2) does not require this */ + OUTPUT( '-' ); + } + } + return pos; + +overflow: + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; +#undef OUTPUT +#undef ENCODABLE +} + + +static int wcstombs_utf8( DWORD flags, const WCHAR *src, int srclen, char *dst, int dstlen, + const char *defchar, BOOL *used ) +{ + DWORD reslen; + NTSTATUS status; + + if (used) *used = FALSE; + if (!dstlen) dst = NULL; + status = RtlUnicodeToUTF8N( dst, dstlen, &reslen, src, srclen * sizeof(WCHAR) ); + if (status == STATUS_SOME_NOT_MAPPED) + { + if (flags & WC_ERR_INVALID_CHARS) + { + SetLastError( ERROR_NO_UNICODE_TRANSLATION ); + return 0; + } + if (used) *used = TRUE; + } + else if (!set_ntstatus( status )) reslen = 0; + return reslen; +} + + +static int wcstombs_sbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen, + char *dst, unsigned int dstlen ) +{ + const char *table = info->WideCharTable; + int ret = srclen; + + if (!dstlen) return srclen; + + if (dstlen < srclen) + { + /* buffer too small: fill it up to dstlen and return error */ + srclen = dstlen; + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + ret = 0; + } + + while (srclen >= 16) + { + dst[0] = table[src[0]]; + dst[1] = table[src[1]]; + dst[2] = table[src[2]]; + dst[3] = table[src[3]]; + dst[4] = table[src[4]]; + dst[5] = table[src[5]]; + dst[6] = table[src[6]]; + dst[7] = table[src[7]]; + dst[8] = table[src[8]]; + dst[9] = table[src[9]]; + dst[10] = table[src[10]]; + dst[11] = table[src[11]]; + dst[12] = table[src[12]]; + dst[13] = table[src[13]]; + dst[14] = table[src[14]]; + dst[15] = table[src[15]]; + src += 16; + dst += 16; + srclen -= 16; + } + + /* now handle remaining characters */ + src += srclen; + dst += srclen; + switch(srclen) + { + case 15: dst[-15] = table[src[-15]]; + case 14: dst[-14] = table[src[-14]]; + case 13: dst[-13] = table[src[-13]]; + case 12: dst[-12] = table[src[-12]]; + case 11: dst[-11] = table[src[-11]]; + case 10: dst[-10] = table[src[-10]]; + case 9: dst[-9] = table[src[-9]]; + case 8: dst[-8] = table[src[-8]]; + case 7: dst[-7] = table[src[-7]]; + case 6: dst[-6] = table[src[-6]]; + case 5: dst[-5] = table[src[-5]]; + case 4: dst[-4] = table[src[-4]]; + case 3: dst[-3] = table[src[-3]]; + case 2: dst[-2] = table[src[-2]]; + case 1: dst[-1] = table[src[-1]]; + case 0: break; + } + return ret; +} + + +static int wcstombs_dbcs( const CPTABLEINFO *info, const WCHAR *src, unsigned int srclen, + char *dst, unsigned int dstlen ) +{ + const USHORT *table = info->WideCharTable; + int i; + + if (!dstlen) + { + for (i = 0; srclen; src++, srclen--, i++) if (table[*src] & 0xff00) i++; + return i; + } + + for (i = dstlen; srclen && i; i--, srclen--, src++) + { + if (table[*src] & 0xff00) + { + if (i == 1) break; /* do not output a partial char */ + i--; + *dst++ = table[*src] >> 8; + } + *dst++ = (char)table[*src]; + } + if (srclen) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return dstlen - i; +} + + +static inline int is_valid_sbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch ) +{ + const unsigned char *table = info->WideCharTable; + + if (wch >= 0x10000) return 0; + if ((flags & WC_NO_BEST_FIT_CHARS) || table[wch] == info->DefaultChar) + return (info->MultiByteTable[table[wch]] == wch); + return 1; +} + + +static inline int is_valid_dbcs_mapping( const CPTABLEINFO *info, DWORD flags, unsigned int wch ) +{ + const unsigned short *table = info->WideCharTable; + unsigned short ch; + + if (wch >= 0x10000) return 0; + ch = table[wch]; + if ((flags & WC_NO_BEST_FIT_CHARS) || ch == info->DefaultChar) + { + if (ch >> 8) return info->DBCSOffsets[info->DBCSOffsets[ch >> 8] + (ch & 0xff)] == wch; + return info->MultiByteTable[ch] == wch; + } + return 1; +} + + +static int wcstombs_sbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen, + char *dst, unsigned int dstlen, const char *defchar, BOOL *used ) +{ + const char *table = info->WideCharTable; + const char def = defchar ? *defchar : (char)info->DefaultChar; + int i; + BOOL tmp; + WCHAR wch; + unsigned int composed; + + if (!used) used = &tmp; /* avoid checking on every char */ + *used = FALSE; + + if (!dstlen) + { + for (i = 0; srclen; i++, src++, srclen--) + { + wch = *src; + if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] ))) + { + /* now check if we can use the composed char */ + if (is_valid_sbcs_mapping( info, flags, composed )) + { + /* we have a good mapping, use it */ + src++; + srclen--; + continue; + } + /* no mapping for the composed char, check the other flags */ + if (flags & WC_DEFAULTCHAR) /* use the default char instead */ + { + *used = TRUE; + src++; /* skip the non-spacing char */ + srclen--; + continue; + } + if (flags & WC_DISCARDNS) /* skip the second char of the composition */ + { + src++; + srclen--; + } + /* WC_SEPCHARS is the default */ + } + if (!*used) *used = !is_valid_sbcs_mapping( info, flags, wch ); + } + return i; + } + + for (i = dstlen; srclen && i; dst++, i--, src++, srclen--) + { + wch = *src; + if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] ))) + { + /* now check if we can use the composed char */ + if (is_valid_sbcs_mapping( info, flags, composed )) + { + /* we have a good mapping, use it */ + *dst = table[composed]; + src++; + srclen--; + continue; + } + /* no mapping for the composed char, check the other flags */ + if (flags & WC_DEFAULTCHAR) /* use the default char instead */ + { + *dst = def; + *used = TRUE; + src++; /* skip the non-spacing char */ + srclen--; + continue; + } + if (flags & WC_DISCARDNS) /* skip the second char of the composition */ + { + src++; + srclen--; + } + /* WC_SEPCHARS is the default */ + } + + *dst = table[wch]; + if (!is_valid_sbcs_mapping( info, flags, wch )) + { + *dst = def; + *used = TRUE; + } + } + if (srclen) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return dstlen - i; +} + + +static int wcstombs_dbcs_slow( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, unsigned int srclen, + char *dst, unsigned int dstlen, const char *defchar, BOOL *used ) +{ + const USHORT *table = info->WideCharTable; + WCHAR wch, defchar_value; + unsigned int composed; + unsigned short res; + BOOL tmp; + int i; + + if (!defchar[1]) defchar_value = (unsigned char)defchar[0]; + else defchar_value = ((unsigned char)defchar[0] << 8) | (unsigned char)defchar[1]; + + if (!used) used = &tmp; /* avoid checking on every char */ + *used = FALSE; + + if (!dstlen) + { + if (!defchar && !used && !(flags & WC_COMPOSITECHECK)) + { + for (i = 0; srclen; srclen--, src++, i++) if (table[*src] & 0xff00) i++; + return i; + } + for (i = 0; srclen; srclen--, src++, i++) + { + wch = *src; + if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] ))) + { + /* now check if we can use the composed char */ + if (is_valid_dbcs_mapping( info, flags, composed )) + { + /* we have a good mapping for the composed char, use it */ + res = table[composed]; + if (res & 0xff00) i++; + src++; + srclen--; + continue; + } + /* no mapping for the composed char, check the other flags */ + if (flags & WC_DEFAULTCHAR) /* use the default char instead */ + { + if (defchar_value & 0xff00) i++; + *used = TRUE; + src++; /* skip the non-spacing char */ + srclen--; + continue; + } + if (flags & WC_DISCARDNS) /* skip the second char of the composition */ + { + src++; + srclen--; + } + /* WC_SEPCHARS is the default */ + } + + res = table[wch]; + if (!is_valid_dbcs_mapping( info, flags, wch )) + { + res = defchar_value; + *used = TRUE; + } + if (res & 0xff00) i++; + } + return i; + } + + + for (i = dstlen; srclen && i; i--, srclen--, src++) + { + wch = *src; + if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose_chars( src[0], src[1] ))) + { + /* now check if we can use the composed char */ + if (is_valid_dbcs_mapping( info, flags, composed )) + { + /* we have a good mapping for the composed char, use it */ + res = table[composed]; + src++; + srclen--; + goto output_char; + } + /* no mapping for the composed char, check the other flags */ + if (flags & WC_DEFAULTCHAR) /* use the default char instead */ + { + res = defchar_value; + *used = TRUE; + src++; /* skip the non-spacing char */ + srclen--; + goto output_char; + } + if (flags & WC_DISCARDNS) /* skip the second char of the composition */ + { + src++; + srclen--; + } + /* WC_SEPCHARS is the default */ + } + + res = table[wch]; + if (!is_valid_dbcs_mapping( info, flags, wch )) + { + res = defchar_value; + *used = TRUE; + } + + output_char: + if (res & 0xff00) + { + if (i == 1) break; /* do not output a partial char */ + i--; + *dst++ = res >> 8; + } + *dst++ = (char)res; + } + if (srclen) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return dstlen - i; +} + + +static int wcstombs_codepage( const CPTABLEINFO *info, DWORD flags, const WCHAR *src, int srclen, + char *dst, int dstlen, const char *defchar, BOOL *used ) +{ + if (flags || defchar || used) + { + if (!defchar) defchar = (const char *)&info->DefaultChar; + if (info->DBCSOffsets) + return wcstombs_dbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used ); + else + return wcstombs_sbcs_slow( info, flags, src, srclen, dst, dstlen, defchar, used ); + } + if (info->DBCSOffsets) + return wcstombs_dbcs( info, src, srclen, dst, dstlen ); + else + return wcstombs_sbcs( info, src, srclen, dst, dstlen ); +} + + +struct sortkey +{ + BYTE *buf; + BYTE *new_buf; /* allocated buf if static buf is not large enough */ + UINT size; /* buffer size */ + UINT max; /* max possible size */ + UINT len; /* current key length */ +}; + +static void append_sortkey( struct sortkey *key, BYTE val ) +{ + if (key->len >= key->max) return; + if (key->len >= key->size) + { + key->new_buf = RtlAllocateHeap( GetProcessHeap(), 0, key->max ); + if (key->new_buf) memcpy( key->new_buf, key->buf, key->len ); + else key->max = 0; + key->buf = key->new_buf; + key->size = key->max; + } + key->buf[key->len++] = val; +} + +static void reverse_sortkey( struct sortkey *key ) +{ + int i; + + for (i = 0; i < key->len / 2; i++) + { + BYTE tmp = key->buf[key->len - i - 1]; + key->buf[key->len - i - 1] = key->buf[i]; + key->buf[i] = tmp; + } +} + +static int compare_sortkeys( const struct sortkey *key1, const struct sortkey *key2, BOOL shorter_wins ) +{ + int ret = memcmp( key1->buf, key2->buf, min( key1->len, key2->len )); + if (!ret) ret = shorter_wins ? key2->len - key1->len : key1->len - key2->len; + return ret; +} + +static void append_normal_weights( const struct sortguid *sortid, struct sortkey *key_primary, + struct sortkey *key_diacritic, struct sortkey *key_case, + union char_weights weights, DWORD flags ) +{ + append_sortkey( key_primary, weights.script ); + append_sortkey( key_primary, weights.primary ); + + if ((weights.script >= SCRIPT_PUA_FIRST && weights.script <= SCRIPT_PUA_LAST) || + ((sortid->flags & FLAG_HAS_3_BYTE_WEIGHTS) && + (weights.script >= SCRIPT_CJK_FIRST && weights.script <= SCRIPT_CJK_LAST))) + { + append_sortkey( key_primary, weights.diacritic ); + append_sortkey( key_case, weights._case ); + return; + } + if (weights.script <= SCRIPT_ARABIC && weights.script != SCRIPT_HEBREW) + { + if (flags & LINGUISTIC_IGNOREDIACRITIC) weights.diacritic = 2; + if (flags & LINGUISTIC_IGNORECASE) weights._case = 2; + } + append_sortkey( key_diacritic, weights.diacritic ); + append_sortkey( key_case, weights._case ); +} + +static void append_nonspace_weights( struct sortkey *key, union char_weights weights, DWORD flags ) +{ + if (flags & LINGUISTIC_IGNOREDIACRITIC) weights.diacritic = 2; + if (key->len) key->buf[key->len - 1] += weights.diacritic; + else append_sortkey( key, weights.diacritic ); +} + +static void append_expansion_weights( const struct sortguid *sortid, struct sortkey *key_primary, + struct sortkey *key_diacritic, struct sortkey *key_case, + union char_weights weights, DWORD flags, BOOL is_compare ) +{ + /* sortkey and comparison behave differently here */ + if (is_compare) + { + if (weights.script == SCRIPT_UNSORTABLE) return; + if (weights.script == SCRIPT_NONSPACE_MARK) + { + append_nonspace_weights( key_diacritic, weights, flags ); + return; + } + } + append_normal_weights( sortid, key_primary, key_diacritic, key_case, weights, flags ); +} + +static const UINT *find_compression( const WCHAR *src, const WCHAR *table, int count, int len ) +{ + int elem_size = compression_size( len ), min = 0, max = count - 1; + + while (min <= max) + { + int pos = (min + max) / 2; + int res = wcsncmp( src, table + pos * elem_size, len ); + if (!res) return (UINT *)(table + (pos + 1) * elem_size) - 1; + if (res > 0) min = pos + 1; + else max = pos - 1; + } + return NULL; +} + +/* find a compression for a char sequence */ +/* return the number of extra chars to skip */ +static int get_compression_weights( UINT compression, const WCHAR *compr_tables[8], + const WCHAR *src, int srclen, union char_weights *weights ) +{ + const struct sort_compression *compr = sort.compressions + compression; + const UINT *ret; + BYTE size = weights->_case & CASE_COMPR_6; + int i, maxlen = 1; + + if (compression >= sort.compr_count) return 0; + if (size == CASE_COMPR_6) maxlen = 8; + else if (size == CASE_COMPR_4) maxlen = 5; + else if (size == CASE_COMPR_2) maxlen = 3; + maxlen = min( maxlen, srclen ); + for (i = 0; i < maxlen; i++) if (src[i] < compr->minchar || src[i] > compr->maxchar) break; + maxlen = i; + if (!compr_tables[0]) + { + compr_tables[0] = sort.compr_data + compr->offset; + for (i = 1; i < 8; i++) + compr_tables[i] = compr_tables[i - 1] + compr->len[i - 1] * compression_size( i + 1 ); + } + for (i = maxlen - 2; i >= 0; i--) + { + if (!(ret = find_compression( src, compr_tables[i], compr->len[i], i + 2 ))) continue; + weights->val = *ret; + return i + 1; + } + return 0; +} + +/* get the zero digit for the digit character range that contains 'ch' */ +static WCHAR get_digit_zero_char( WCHAR ch ) +{ + static const WCHAR zeroes[] = + { + 0x0030, 0x0660, 0x06f0, 0x0966, 0x09e6, 0x0a66, 0x0ae6, 0x0b66, 0x0be6, 0x0c66, + 0x0ce6, 0x0d66, 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x1090, 0x17e0, 0x1810, 0x1946, + 0x1bb0, 0x1c40, 0x1c50, 0xa620, 0xa8d0, 0xa900, 0xaa50, 0xff10 + }; + int min = 0, max = ARRAY_SIZE( zeroes ) - 1; + + while (min <= max) + { + int pos = (min + max) / 2; + if (zeroes[pos] <= ch && zeroes[pos] + 9 >= ch) return zeroes[pos]; + if (zeroes[pos] < ch) min = pos + 1; + else max = pos - 1; + } + return 0; +} + +/* append weights for digits when using SORT_DIGITSASNUMBERS */ +/* return the number of extra chars to skip */ +static int append_digit_weights( struct sortkey *key, const WCHAR *src, UINT srclen ) +{ + UINT i, zero, len, lzero; + BYTE val, values[19]; + + if (!(zero = get_digit_zero_char( *src ))) return -1; + + values[0] = *src - zero; + for (len = 1; len < ARRAY_SIZE(values) && len < srclen; len++) + { + if (src[len] < zero || src[len] > zero + 9) break; + values[len] = src[len] - zero; + } + for (lzero = 0; lzero < len; lzero++) if (values[lzero]) break; + + append_sortkey( key, SCRIPT_DIGIT ); + append_sortkey( key, 2 ); + append_sortkey( key, 2 + len - lzero ); + for (i = lzero, val = 2; i < len; i++) + { + if ((len - i) % 2) append_sortkey( key, (val << 4) + values[i] + 2 ); + else val = values[i] + 2; + } + append_sortkey( key, 0xfe - lzero ); + return len - 1; +} + +/* append the extra weights for kana prolonged sound / repeat marks */ +static int append_extra_kana_weights( struct sortkey keys[4], const WCHAR *src, int pos, UINT except, + BYTE case_mask, union char_weights *weights ) +{ + BYTE extra1 = 3, case_weight = weights->_case; + + if (weights->primary <= 1) + { + while (pos > 0) + { + union char_weights prev = get_char_weights( src[--pos], except ); + if (prev.script == SCRIPT_UNSORTABLE || prev.script == SCRIPT_NONSPACE_MARK) continue; + if (prev.script == SCRIPT_EXPANSION) return 0; + if (prev.script != SCRIPT_EASTASIA_SPECIAL) + { + *weights = prev; + return 1; + } + if (prev.primary <= 1) continue; + + case_weight = prev._case & case_mask; + if (weights->primary == 1) /* prolonged sound mark */ + { + prev.primary &= 0x87; + case_weight &= ~CASE_FULLWIDTH; + case_weight |= weights->_case & CASE_FULLWIDTH; + } + extra1 = 4 + weights->primary; + weights->primary = prev.primary; + goto done; + } + return 0; + } +done: + append_sortkey( &keys[0], 0xc4 | (case_weight & CASE_FULLSIZE) ); + append_sortkey( &keys[1], extra1 ); + append_sortkey( &keys[2], 0xc4 | (case_weight & CASE_KATAKANA) ); + append_sortkey( &keys[3], 0xc4 | (case_weight & CASE_FULLWIDTH) ); + weights->script = SCRIPT_KANA; + return 1; +} + + +#define HANGUL_SBASE 0xac00 +#define HANGUL_LCOUNT 19 +#define HANGUL_VCOUNT 21 +#define HANGUL_TCOUNT 28 + +static int append_hangul_weights( struct sortkey *key, const WCHAR *src, int srclen, UINT except ) +{ + int leading_idx = 0x115f - 0x1100; /* leading filler */ + int vowel_idx = 0x1160 - 0x1100; /* vowel filler */ + int trailing_idx = -1; + BYTE leading_off, vowel_off, trailing_off; + union char_weights weights; + WCHAR composed; + BYTE filler_mask = 0; + int pos = 0; + + /* leading */ + if (src[pos] >= 0x1100 && src[pos] <= 0x115f) leading_idx = src[pos++] - 0x1100; + else if (src[pos] >= 0xa960 && src[pos] <= 0xa97c) leading_idx = src[pos++] - (0xa960 - 0x100); + + /* vowel */ + if (srclen > pos) + { + if (src[pos] >= 0x1160 && src[pos] <= 0x11a7) vowel_idx = src[pos++] - 0x1100; + else if (src[pos] >= 0xd7b0 && src[pos] <= 0xd7c6) vowel_idx = src[pos++] - (0xd7b0 - 0x11d); + } + + /* trailing */ + if (srclen > pos) + { + if (src[pos] >= 0x11a8 && src[pos] <= 0x11ff) trailing_idx = src[pos++] - 0x1100; + else if (src[pos] >= 0xd7cb && src[pos] <= 0xd7fb) trailing_idx = src[pos++] - (0xd7cb - 0x134); + } + + if (!sort.jamo[leading_idx].is_old && !sort.jamo[vowel_idx].is_old && + (trailing_idx == -1 || !sort.jamo[trailing_idx].is_old)) + { + /* not old Hangul, only use leading char; vowel and trailing will be handled in the next pass */ + pos = 1; + vowel_idx = 0x1160 - 0x1100; + trailing_idx = -1; + } + + leading_off = max( sort.jamo[leading_idx].leading, sort.jamo[vowel_idx].leading ); + vowel_off = max( sort.jamo[leading_idx].vowel, sort.jamo[vowel_idx].vowel ); + trailing_off = max( sort.jamo[leading_idx].trailing, sort.jamo[vowel_idx].trailing ); + if (trailing_idx != -1) trailing_off = max( trailing_off, sort.jamo[trailing_idx].trailing ); + composed = HANGUL_SBASE + (leading_off * HANGUL_VCOUNT + vowel_off) * HANGUL_TCOUNT + trailing_off; + + if (leading_idx == 0x115f - 0x1100 || vowel_idx == 0x1160 - 0x1100) + { + filler_mask = 0x80; + composed--; + } + if (composed < HANGUL_SBASE) composed = 0x3260; + + weights = get_char_weights( composed, except ); + append_sortkey( key, weights.script ); + append_sortkey( key, weights.primary ); + append_sortkey( key, 0xff ); + append_sortkey( key, sort.jamo[leading_idx].weight | filler_mask ); + append_sortkey( key, 0xff ); + append_sortkey( key, sort.jamo[vowel_idx].weight ); + append_sortkey( key, 0xff ); + append_sortkey( key, trailing_idx != -1 ? sort.jamo[trailing_idx].weight : 2 ); + return pos - 1; +} + +/* put one of the elements of a sortkey into the dst buffer */ +static int put_sortkey( BYTE *dst, int dstlen, int pos, const struct sortkey *key, BYTE terminator ) +{ + if (dstlen > pos + key->len) + { + memcpy( dst + pos, key->buf, key->len ); + dst[pos + key->len] = terminator; + } + return pos + key->len + 1; +} + + +struct sortkey_state +{ + struct sortkey key_primary; + struct sortkey key_diacritic; + struct sortkey key_case; + struct sortkey key_special; + struct sortkey key_extra[4]; + UINT primary_pos; + BYTE buffer[3 * 128]; +}; + +static void init_sortkey_state( struct sortkey_state *s, DWORD flags, UINT srclen, + BYTE *primary_buf, UINT primary_size ) +{ + /* buffer for secondary weights */ + BYTE *secondary_buf = s->buffer; + UINT secondary_size; + + memset( s, 0, offsetof( struct sortkey_state, buffer )); + + s->key_primary.buf = primary_buf; + s->key_primary.size = primary_size; + + if (!(flags & NORM_IGNORENONSPACE)) /* reserve space for diacritics */ + { + secondary_size = sizeof(s->buffer) / 3; + s->key_diacritic.buf = secondary_buf; + s->key_diacritic.size = secondary_size; + secondary_buf += secondary_size; + } + else secondary_size = sizeof(s->buffer) / 2; + + s->key_case.buf = secondary_buf; + s->key_case.size = secondary_size; + s->key_special.buf = secondary_buf + secondary_size; + s->key_special.size = secondary_size; + + s->key_primary.max = srclen * 8; + s->key_case.max = srclen * 3; + s->key_special.max = srclen * 4; + s->key_extra[2].max = s->key_extra[3].max = srclen; + if (!(flags & NORM_IGNORENONSPACE)) + { + s->key_diacritic.max = srclen * 3; + s->key_extra[0].max = s->key_extra[1].max = srclen; + } +} + +static BOOL remove_unneeded_weights( const struct sortguid *sortid, struct sortkey_state *s ) +{ + const BYTE ignore[4] = { 0xc4 | CASE_FULLSIZE, 0x03, 0xc4 | CASE_KATAKANA, 0xc4 | CASE_FULLWIDTH }; + int i, j; + + if (sortid->flags & FLAG_REVERSEDIACRITICS) reverse_sortkey( &s->key_diacritic ); + + for (i = s->key_diacritic.len; i > 0; i--) if (s->key_diacritic.buf[i - 1] > 2) break; + s->key_diacritic.len = i; + + for (i = s->key_case.len; i > 0; i--) if (s->key_case.buf[i - 1] > 2) break; + s->key_case.len = i; + + if (!s->key_extra[2].len) return FALSE; + + for (i = 0; i < 4; i++) + { + for (j = s->key_extra[i].len; j > 0; j--) if (s->key_extra[i].buf[j - 1] != ignore[i]) break; + s->key_extra[i].len = j; + } + return TRUE; +} + +static void free_sortkey_state( struct sortkey_state *s ) +{ + RtlFreeHeap( GetProcessHeap(), 0, s->key_primary.new_buf ); + RtlFreeHeap( GetProcessHeap(), 0, s->key_diacritic.new_buf ); + RtlFreeHeap( GetProcessHeap(), 0, s->key_case.new_buf ); + RtlFreeHeap( GetProcessHeap(), 0, s->key_special.new_buf ); + RtlFreeHeap( GetProcessHeap(), 0, s->key_extra[0].new_buf ); + RtlFreeHeap( GetProcessHeap(), 0, s->key_extra[1].new_buf ); + RtlFreeHeap( GetProcessHeap(), 0, s->key_extra[2].new_buf ); + RtlFreeHeap( GetProcessHeap(), 0, s->key_extra[3].new_buf ); +} + +static int append_weights( const struct sortguid *sortid, DWORD flags, + const WCHAR *src, int srclen, int pos, BYTE case_mask, UINT except, + const WCHAR *compr_tables[8], struct sortkey_state *s, BOOL is_compare ) +{ + union char_weights weights = get_char_weights( src[pos], except ); + WCHAR idx = (weights.val >> 16) & ~(CASE_COMPR_6 << 8); /* expansion index */ + int ret = 1; + + if (weights._case & CASE_COMPR_6) + ret += get_compression_weights( sortid->compr, compr_tables, src + pos, srclen - pos, &weights ); + + weights._case &= case_mask; + + switch (weights.script) + { + case SCRIPT_UNSORTABLE: + break; + + case SCRIPT_NONSPACE_MARK: + append_nonspace_weights( &s->key_diacritic, weights, flags ); + break; + + case SCRIPT_EXPANSION: + while (weights.script == SCRIPT_EXPANSION) + { + weights = get_char_weights( sort.expansions[idx].exp[0], except ); + weights._case &= case_mask; + append_expansion_weights( sortid, &s->key_primary, &s->key_diacritic, + &s->key_case, weights, flags, is_compare ); + weights = get_char_weights( sort.expansions[idx].exp[1], except ); + idx = weights.val >> 16; + weights._case &= case_mask; + } + append_expansion_weights( sortid, &s->key_primary, &s->key_diacritic, + &s->key_case, weights, flags, is_compare ); + break; + + case SCRIPT_EASTASIA_SPECIAL: + if (!append_extra_kana_weights( s->key_extra, src, pos, except, case_mask, &weights )) + { + append_sortkey( &s->key_primary, 0xff ); + append_sortkey( &s->key_primary, 0xff ); + break; + } + weights._case = 2; + append_normal_weights( sortid, &s->key_primary, &s->key_diacritic, &s->key_case, weights, flags ); + break; + + case SCRIPT_JAMO_SPECIAL: + ret += append_hangul_weights( &s->key_primary, src + pos, srclen - pos, except ); + append_sortkey( &s->key_diacritic, 2 ); + append_sortkey( &s->key_case, 2 ); + break; + + case SCRIPT_EXTENSION_A: + append_sortkey( &s->key_primary, 0xfd ); + append_sortkey( &s->key_primary, 0xff ); + append_sortkey( &s->key_primary, weights.primary ); + append_sortkey( &s->key_primary, weights.diacritic ); + append_sortkey( &s->key_diacritic, 2 ); + append_sortkey( &s->key_case, 2 ); + break; + + case SCRIPT_PUNCTUATION: + if (flags & NORM_IGNORESYMBOLS) break; + if (!(flags & SORT_STRINGSORT)) + { + short len = -((s->key_primary.len + s->primary_pos) / 2) - 1; + if (flags & LINGUISTIC_IGNORECASE) weights._case = 2; + if (flags & LINGUISTIC_IGNOREDIACRITIC) weights.diacritic = 2; + append_sortkey( &s->key_special, len >> 8 ); + append_sortkey( &s->key_special, len & 0xff ); + append_sortkey( &s->key_special, weights.primary ); + append_sortkey( &s->key_special, weights._case | (weights.diacritic << 3) ); + break; + } + /* fall through */ + case SCRIPT_SYMBOL_1: + case SCRIPT_SYMBOL_2: + case SCRIPT_SYMBOL_3: + case SCRIPT_SYMBOL_4: + case SCRIPT_SYMBOL_5: + case SCRIPT_SYMBOL_6: + if (flags & NORM_IGNORESYMBOLS) break; + append_sortkey( &s->key_primary, weights.script ); + append_sortkey( &s->key_primary, weights.primary ); + append_sortkey( &s->key_diacritic, weights.diacritic ); + append_sortkey( &s->key_case, weights._case ); + break; + + case SCRIPT_DIGIT: + if (flags & SORT_DIGITSASNUMBERS) + { + int len = append_digit_weights( &s->key_primary, src + pos, srclen - pos ); + if (len >= 0) + { + ret += len; + append_sortkey( &s->key_diacritic, weights.diacritic ); + append_sortkey( &s->key_case, weights._case ); + break; + } + } + /* fall through */ + default: + append_normal_weights( sortid, &s->key_primary, &s->key_diacritic, &s->key_case, weights, flags ); + break; + } + + return ret; +} + +/* implementation of LCMAP_SORTKEY */ +static int get_sortkey( const struct sortguid *sortid, DWORD flags, + const WCHAR *src, int srclen, BYTE *dst, int dstlen ) +{ + struct sortkey_state s; + BYTE primary_buf[256]; + int ret = 0, pos = 0; + BOOL have_extra; + BYTE case_mask = 0x3f; + UINT except = sortid->except; + const WCHAR *compr_tables[8]; + + compr_tables[0] = NULL; + if (flags & NORM_IGNORECASE) case_mask &= ~(CASE_UPPER | CASE_SUBSCRIPT); + if (flags & NORM_IGNOREWIDTH) case_mask &= ~CASE_FULLWIDTH; + if (flags & NORM_IGNOREKANATYPE) case_mask &= ~CASE_KATAKANA; + if ((flags & NORM_LINGUISTIC_CASING) && except && sortid->ling_except) except = sortid->ling_except; + + init_sortkey_state( &s, flags, srclen, primary_buf, sizeof(primary_buf) ); + + while (pos < srclen) + pos += append_weights( sortid, flags, src, srclen, pos, case_mask, except, compr_tables, &s, FALSE ); + + have_extra = remove_unneeded_weights( sortid, &s ); + + ret = put_sortkey( dst, dstlen, ret, &s.key_primary, 0x01 ); + ret = put_sortkey( dst, dstlen, ret, &s.key_diacritic, 0x01 ); + ret = put_sortkey( dst, dstlen, ret, &s.key_case, 0x01 ); + + if (have_extra) + { + ret = put_sortkey( dst, dstlen, ret, &s.key_extra[0], 0xff ); + ret = put_sortkey( dst, dstlen, ret, &s.key_extra[1], 0x02 ); + ret = put_sortkey( dst, dstlen, ret, &s.key_extra[2], 0xff ); + ret = put_sortkey( dst, dstlen, ret, &s.key_extra[3], 0xff ); + } + if (dstlen > ret) dst[ret] = 0x01; + ret++; + + ret = put_sortkey( dst, dstlen, ret, &s.key_special, 0 ); + + free_sortkey_state( &s ); + + if (dstlen && dstlen < ret) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + if (flags & LCMAP_BYTEREV) + map_byterev( (WCHAR *)dst, min( ret, dstlen ) / sizeof(WCHAR), (WCHAR *)dst ); + return ret; +} + + +/* implementation of CompareStringEx */ +static int compare_string( const struct sortguid *sortid, DWORD flags, + const WCHAR *src1, int srclen1, const WCHAR *src2, int srclen2 ) +{ + struct sortkey_state s1; + struct sortkey_state s2; + BYTE primary1[32]; + BYTE primary2[32]; + int i, ret, len, pos1 = 0, pos2 = 0; + BOOL have_extra1, have_extra2; + BYTE case_mask = 0x3f; + UINT except = sortid->except; + const WCHAR *compr_tables[8]; + + compr_tables[0] = NULL; + if (flags & NORM_IGNORECASE) case_mask &= ~(CASE_UPPER | CASE_SUBSCRIPT); + if (flags & NORM_IGNOREWIDTH) case_mask &= ~CASE_FULLWIDTH; + if (flags & NORM_IGNOREKANATYPE) case_mask &= ~CASE_KATAKANA; + if ((flags & NORM_LINGUISTIC_CASING) && except && sortid->ling_except) except = sortid->ling_except; + + init_sortkey_state( &s1, flags, srclen1, primary1, sizeof(primary1) ); + init_sortkey_state( &s2, flags, srclen2, primary2, sizeof(primary2) ); + + while (pos1 < srclen1 || pos2 < srclen2) + { + while (pos1 < srclen1 && !s1.key_primary.len) + pos1 += append_weights( sortid, flags, src1, srclen1, pos1, + case_mask, except, compr_tables, &s1, TRUE ); + + while (pos2 < srclen2 && !s2.key_primary.len) + pos2 += append_weights( sortid, flags, src2, srclen2, pos2, + case_mask, except, compr_tables, &s2, TRUE ); + + if (!(len = min( s1.key_primary.len, s2.key_primary.len ))) break; + if ((ret = memcmp( primary1, primary2, len ))) goto done; + memmove( primary1, primary1 + len, s1.key_primary.len - len ); + memmove( primary2, primary2 + len, s2.key_primary.len - len ); + s1.key_primary.len -= len; + s2.key_primary.len -= len; + s1.primary_pos += len; + s2.primary_pos += len; + } + + if ((ret = s1.key_primary.len - s2.key_primary.len)) goto done; + + have_extra1 = remove_unneeded_weights( sortid, &s1 ); + have_extra2 = remove_unneeded_weights( sortid, &s2 ); + + if ((ret = compare_sortkeys( &s1.key_diacritic, &s2.key_diacritic, FALSE ))) goto done; + if ((ret = compare_sortkeys( &s1.key_case, &s2.key_case, FALSE ))) goto done; + + if (have_extra1 && have_extra2) + { + for (i = 0; i < 4; i++) + if ((ret = compare_sortkeys( &s1.key_extra[i], &s2.key_extra[i], i != 1 ))) goto done; + } + else if ((ret = have_extra1 - have_extra2)) goto done; + + ret = compare_sortkeys( &s1.key_special, &s2.key_special, FALSE ); + +done: + free_sortkey_state( &s1 ); + free_sortkey_state( &s2 ); + return ret; +} + + +/* implementation of FindNLSStringEx */ +static int find_substring( const struct sortguid *sortid, DWORD flags, const WCHAR *src, int srclen, + const WCHAR *value, int valuelen, int *reslen ) +{ + struct sortkey_state s; + struct sortkey_state val; + BYTE primary[32]; + BYTE primary_val[256]; + int i, start, len, found = -1, foundlen = 0, pos = 0; + BOOL have_extra, have_extra_val; + BYTE case_mask = 0x3f; + UINT except = sortid->except; + const WCHAR *compr_tables[8]; + + compr_tables[0] = NULL; + if (flags & NORM_IGNORECASE) case_mask &= ~(CASE_UPPER | CASE_SUBSCRIPT); + if (flags & NORM_IGNOREWIDTH) case_mask &= ~CASE_FULLWIDTH; + if (flags & NORM_IGNOREKANATYPE) case_mask &= ~CASE_KATAKANA; + if ((flags & NORM_LINGUISTIC_CASING) && except && sortid->ling_except) except = sortid->ling_except; + + init_sortkey_state( &s, flags, srclen, primary, sizeof(primary) ); + + /* build the value sortkey just once */ + init_sortkey_state( &val, flags, valuelen, primary_val, sizeof(primary_val) ); + while (pos < valuelen) + pos += append_weights( sortid, flags, value, valuelen, pos, + case_mask, except, compr_tables, &val, TRUE ); + have_extra_val = remove_unneeded_weights( sortid, &val ); + + for (start = 0; start < srclen; start++) + { + pos = start; + for (len = start + 1; len <= srclen; len++) + { + while (pos < len && s.primary_pos <= val.key_primary.len) + { + while (pos < len && !s.key_primary.len) + pos += append_weights( sortid, flags, src, srclen, pos, + case_mask, except, compr_tables, &s, TRUE ); + + if (s.primary_pos + s.key_primary.len > val.key_primary.len || + memcmp( primary, val.key_primary.buf + s.primary_pos, s.key_primary.len )) + { + len = srclen + 1; + goto next; + } + s.primary_pos += s.key_primary.len; + s.key_primary.len = 0; + } + if (s.primary_pos < val.key_primary.len) continue; + + have_extra = remove_unneeded_weights( sortid, &s ); + if (compare_sortkeys( &s.key_diacritic, &val.key_diacritic, FALSE )) goto next; + if (compare_sortkeys( &s.key_case, &val.key_case, FALSE )) goto next; + + if (have_extra && have_extra_val) + { + for (i = 0; i < 4; i++) + if (compare_sortkeys( &s.key_extra[i], &val.key_extra[i], i != 1 )) goto next; + } + else if (have_extra || have_extra_val) goto next; + + if (compare_sortkeys( &s.key_special, &val.key_special, FALSE )) goto next; + + found = start; + foundlen = pos - start; + len = srclen; /* no need to continue checking longer strings */ + + next: + /* reset state */ + s.key_primary.len = s.key_diacritic.len = s.key_case.len = s.key_special.len = 0; + s.key_extra[0].len = s.key_extra[1].len = s.key_extra[2].len = s.key_extra[3].len = 0; + s.primary_pos = 0; + pos = start; + } + if (flags & FIND_STARTSWITH) break; + if (flags & FIND_FROMSTART && found != -1) break; + } + + if (found != -1) + { + if ((flags & FIND_ENDSWITH) && found + foundlen != srclen) found = -1; + else if (reslen) *reslen = foundlen; + } + free_sortkey_state( &s ); + free_sortkey_state( &val ); + return found; +} + + +/* map buffer to full-width katakana */ +static int map_to_fullwidth( const USHORT *table, const WCHAR *src, int srclen, WCHAR *dst, int dstlen ) +{ + int pos, len; + + for (pos = 0; srclen; pos++, src += len, srclen -= len) + { + unsigned int wch = casemap( charmaps[CHARMAP_FULLWIDTH], *src ); + + len = 1; + if (srclen > 1) + { + if (table_has_high_planes( charmaps[CHARMAP_FULLWIDTH] ) && IS_SURROGATE_PAIR( src[0], src[1] )) + { + len = 2; + wch = casemap_high( charmaps[CHARMAP_FULLWIDTH], src[0], src[1] ); + if (wch >= 0x10000) + { + put_utf16( dst, pos, dstlen, wch ); + pos++; + continue; + } + } + else if (src[1] == 0xff9e) /* dakuten (voiced sound) */ + { + len = 2; + if ((*src >= 0xff76 && *src <= 0xff84) || + (*src >= 0xff8a && *src <= 0xff8e) || + *src == 0x30fd) + wch++; + else if (*src == 0xff73) + wch = 0x30f4; /* KATAKANA LETTER VU */ + else if (*src == 0xff9c) + wch = 0x30f7; /* KATAKANA LETTER VA */ + else if (*src == 0x30f0) + wch = 0x30f8; /* KATAKANA LETTER VI */ + else if (*src == 0x30f1) + wch = 0x30f9; /* KATAKANA LETTER VE */ + else if (*src == 0xff66) + wch = 0x30fa; /* KATAKANA LETTER VO */ + else + len = 1; + } + else if (src[1] == 0xff9f) /* handakuten (semi-voiced sound) */ + { + if (*src >= 0xff8a && *src <= 0xff8e) + { + wch += 2; + len = 2; + } + } + } + + if (pos < dstlen) dst[pos] = table ? casemap( table, wch ) : wch; + } + return pos; +} + + +static inline int nonspace_ignored( WCHAR ch ) +{ + if (get_char_type( CT_CTYPE2, ch ) != C2_OTHERNEUTRAL) return FALSE; + return (get_char_type( CT_CTYPE3, ch ) & (C3_NONSPACING | C3_DIACRITIC)); +} + +/* remove ignored chars for NORM_IGNORENONSPACE/NORM_IGNORESYMBOLS */ +static int map_remove_ignored( DWORD flags, const WCHAR *src, int srclen, WCHAR *dst, int dstlen ) +{ + int pos; + + for (pos = 0; srclen; src++, srclen--) + { + if (flags & NORM_IGNORESYMBOLS) + { + if (get_char_type( CT_CTYPE1, *src ) & C1_PUNCT) continue; + if (get_char_type( CT_CTYPE3, *src ) & C3_SYMBOL) continue; + } + if (flags & NORM_IGNORENONSPACE) + { + WCHAR buffer[8]; + const WCHAR *decomp; + unsigned int i, j, len; + + if ((decomp = get_decomposition( *src, &len )) && len > 1) + { + for (i = j = 0; i < len; i++) + if (!nonspace_ignored( decomp[i] )) buffer[j++] = decomp[i]; + + if (i > j) /* something was removed */ + { + if (pos + j <= dstlen) memcpy( dst + pos, buffer, j * sizeof(WCHAR) ); + pos += j; + continue; + } + } + else if (nonspace_ignored( *src )) continue; + } + if (pos < dstlen) dst[pos] = *src; + pos++; + } + return pos; +} + + +/* map full-width characters to single or double half-width characters. */ +static int map_to_halfwidth( const USHORT *table, const WCHAR *src, int srclen, WCHAR *dst, int dstlen ) +{ + static const BYTE katakana_map[] = + { + 0x01, 0x00, 0x01, 0x00, /* U+30a8- */ + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /* U+30b0- */ + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /* U+30b8- */ + 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, /* U+30c0- */ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30c8- */ + 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, /* U+30d0- */ + 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x00, /* U+30d8- */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30e0- */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* U+30e8- */ + 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x08, /* U+30f0- */ + 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x01 /* U+30f8- */ + }; + int pos; + + for (pos = 0; srclen; src++, srclen--) + { + WCHAR ch = table ? casemap( table, *src ) : *src; + USHORT shift = ch - 0x30ac; + BYTE k; + + if (shift < ARRAY_SIZE(katakana_map) && (k = katakana_map[shift])) + { + if (pos < dstlen - 1) + { + dst[pos] = casemap( charmaps[CHARMAP_HALFWIDTH], ch - k ); + dst[pos + 1] = (k == 2) ? 0xff9f : 0xff9e; + } + pos += 2; + } + else + { + if (pos < dstlen) dst[pos] = casemap( charmaps[CHARMAP_HALFWIDTH], ch ); + pos++; + } + } + return pos; +} + + +static int lcmap_string( const struct sortguid *sortid, DWORD flags, + const WCHAR *src, int srclen, WCHAR *dst, int dstlen ) +{ + const USHORT *case_table = NULL; + int ret; + + if (flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE)) + { + if ((flags & LCMAP_TITLECASE) == LCMAP_TITLECASE) /* FIXME */ + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + case_table = sort.casemap + (flags & LCMAP_LINGUISTIC_CASING ? sortid->casemap : 0); + case_table = case_table + 2 + (flags & LCMAP_LOWERCASE ? case_table[1] : 0); + } + + switch (flags & ~(LCMAP_BYTEREV | LCMAP_LOWERCASE | LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING)) + { + case LCMAP_HIRAGANA: + ret = casemap_string( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen ); + break; + case LCMAP_KATAKANA: + ret = casemap_string( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen ); + break; + case LCMAP_HALFWIDTH: + ret = map_to_halfwidth( NULL, src, srclen, dst, dstlen ); + break; + case LCMAP_HIRAGANA | LCMAP_HALFWIDTH: + ret = map_to_halfwidth( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen ); + break; + case LCMAP_KATAKANA | LCMAP_HALFWIDTH: + ret = map_to_halfwidth( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen ); + break; + case LCMAP_FULLWIDTH: + ret = map_to_fullwidth( NULL, src, srclen, dst, dstlen ); + break; + case LCMAP_HIRAGANA | LCMAP_FULLWIDTH: + ret = map_to_fullwidth( charmaps[CHARMAP_HIRAGANA], src, srclen, dst, dstlen ); + break; + case LCMAP_KATAKANA | LCMAP_FULLWIDTH: + ret = map_to_fullwidth( charmaps[CHARMAP_KATAKANA], src, srclen, dst, dstlen ); + break; + case LCMAP_SIMPLIFIED_CHINESE: + ret = casemap_string( charmaps[CHARMAP_SIMPLIFIED], src, srclen, dst, dstlen ); + break; + case LCMAP_TRADITIONAL_CHINESE: + ret = casemap_string( charmaps[CHARMAP_TRADITIONAL], src, srclen, dst, dstlen ); + break; + case NORM_IGNORENONSPACE: + case NORM_IGNORESYMBOLS: + case NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS: + if (flags & ~(NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | LCMAP_BYTEREV)) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + ret = map_remove_ignored( flags, src, srclen, dst, dstlen ); + break; + case 0: + if (case_table) + { + ret = casemap_string( case_table, src, srclen, dst, dstlen ); + case_table = NULL; + break; + } + if (flags & LCMAP_BYTEREV) + { + ret = min( srclen, dstlen ); + memcpy( dst, src, ret * sizeof(WCHAR) ); + break; + } + /* fall through */ + default: + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + + if (dstlen && case_table) ret = casemap_string( case_table, dst, ret, dst, dstlen ); + if (flags & LCMAP_BYTEREV) map_byterev( dst, min( dstlen, ret ), dst ); + + if (dstlen && dstlen < ret) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return ret; +} + + +static int compare_tzdate( const TIME_FIELDS *tf, const SYSTEMTIME *compare ) +{ + static const int month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int first, last, limit, dayinsecs; + + if (tf->Month < compare->wMonth) return -1; /* We are in a month before the date limit. */ + if (tf->Month > compare->wMonth) return 1; /* We are in a month after the date limit. */ + + /* if year is 0 then date is in day-of-week format, otherwise + * it's absolute date. + */ + if (!compare->wYear) + { + /* wDay is interpreted as number of the week in the month + * 5 means: the last week in the month */ + /* calculate the day of the first DayOfWeek in the month */ + first = (6 + compare->wDayOfWeek - tf->Weekday + tf->Day) % 7 + 1; + /* check needed for the 5th weekday of the month */ + last = month_lengths[tf->Month - 1] + + (tf->Month == 2 && (!(tf->Year % 4) && (tf->Year % 100 || !(tf->Year % 400)))); + limit = first + 7 * (compare->wDay - 1); + if (limit > last) limit -= 7; + } + else limit = compare->wDay; + + limit = ((limit * 24 + compare->wHour) * 60 + compare->wMinute) * 60; + dayinsecs = ((tf->Day * 24 + tf->Hour) * 60 + tf->Minute) * 60 + tf->Second; + return dayinsecs - limit; +} + + +static DWORD get_timezone_id( const TIME_ZONE_INFORMATION *info, LARGE_INTEGER time, BOOL is_local ) +{ + int year; + BOOL before_standard_date, after_daylight_date; + LARGE_INTEGER t2; + TIME_FIELDS tf; + + if (!info->DaylightDate.wMonth) return TIME_ZONE_ID_UNKNOWN; + + /* if year is 0 then date is in day-of-week format, otherwise it's absolute date */ + if (info->StandardDate.wMonth == 0 || + (info->StandardDate.wYear == 0 && + (info->StandardDate.wDay < 1 || info->StandardDate.wDay > 5 || + info->DaylightDate.wDay < 1 || info->DaylightDate.wDay > 5))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return TIME_ZONE_ID_INVALID; + } + + if (!is_local) time.QuadPart -= info->Bias * (LONGLONG)600000000; + RtlTimeToTimeFields( &time, &tf ); + year = tf.Year; + if (!is_local) + { + t2.QuadPart = time.QuadPart - info->DaylightBias * (LONGLONG)600000000; + RtlTimeToTimeFields( &t2, &tf ); + } + if (tf.Year == year) + before_standard_date = compare_tzdate( &tf, &info->StandardDate ) < 0; + else + before_standard_date = tf.Year < year; + + if (!is_local) + { + t2.QuadPart = time.QuadPart - info->StandardBias * (LONGLONG)600000000; + RtlTimeToTimeFields( &t2, &tf ); + } + if (tf.Year == year) + after_daylight_date = compare_tzdate( &tf, &info->DaylightDate ) >= 0; + else + after_daylight_date = tf.Year > year; + + if (info->DaylightDate.wMonth < info->StandardDate.wMonth) /* Northern hemisphere */ + { + if (before_standard_date && after_daylight_date) return TIME_ZONE_ID_DAYLIGHT; + } + else /* Down south */ + { + if (before_standard_date || after_daylight_date) return TIME_ZONE_ID_DAYLIGHT; + } + return TIME_ZONE_ID_STANDARD; +} + + +/* Note: the Internal_ functions are not documented. The number of parameters + * should be correct, but their exact meaning may not. + */ + +/****************************************************************************** + * Internal_EnumCalendarInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumCalendarInfo( CALINFO_ENUMPROCW proc, + const NLS_LOCALE_DATA *locale, CALID id, + CALTYPE type, BOOL unicode, BOOL ex, + BOOL exex, LPARAM lparam ) +{ + const USHORT *calendars; + USHORT cal = id; + WCHAR buffer[256]; + INT ret, i, count = 1; + + if (!proc || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (id == ENUM_ALL_CALENDARS) + { + count = locale_strings[locale->scalendartype]; + calendars = locale_strings + locale->scalendartype + 1; + } + else if (id <= CAL_UMALQURA) + { + calendars = &cal; + count = 1; + } + else + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + type &= ~CAL_RETURN_NUMBER; + + for (i = 0; i < count; i++) + { + id = calendars[i]; + if (unicode) + { + ret = get_calendar_info( locale, id, type, buffer, ARRAY_SIZE(buffer), NULL ); + } + else + { + WCHAR bufW[256]; + ret = get_calendar_info( locale, id, type, bufW, ARRAY_SIZE(bufW), NULL ); + if (ret) WideCharToMultiByte( get_locale_codepage( locale, type ), 0, + bufW, -1, (char *)buffer, sizeof(buffer), NULL, NULL ); + } + + if (ret) + { + if (exex) ret = ((CALINFO_ENUMPROCEXEX)proc)( buffer, id, NULL, lparam ); + else if (ex) ret = ((CALINFO_ENUMPROCEXW)proc)( buffer, id ); + else ret = proc( buffer ); + } + if (!ret) break; + } + return TRUE; +} + + +static BOOL call_enum_date_func( DATEFMT_ENUMPROCW proc, const NLS_LOCALE_DATA *locale, DWORD flags, + DWORD str, WCHAR *buffer, CALID id, BOOL unicode, + BOOL ex, BOOL exex, LPARAM lparam ) +{ + char buffA[256]; + + if (str) memcpy( buffer, locale_strings + str + 1, (locale_strings[str] + 1) * sizeof(WCHAR) ); + if (exex) return ((DATEFMT_ENUMPROCEXEX)proc)( buffer, id, lparam ); + if (ex) return ((DATEFMT_ENUMPROCEXW)proc)( buffer, id ); + if (unicode) return proc( buffer ); + WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1, + buffA, ARRAY_SIZE(buffA), NULL, NULL ); + return proc( (WCHAR *)buffA ); +} + + +/************************************************************************** + * Internal_EnumDateFormats (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumDateFormats( DATEFMT_ENUMPROCW proc, + const NLS_LOCALE_DATA *locale, DWORD flags, + BOOL unicode, BOOL ex, BOOL exex, LPARAM lparam ) +{ + WCHAR buffer[256]; + INT i, j, ret; + DWORD pos; + const struct calendar *cal; + const USHORT *calendars; + const DWORD *array; + + if (!proc || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + calendars = locale_strings + locale->scalendartype; + + switch (flags & ~LOCALE_USE_CP_ACP) + { + case 0: + case DATE_SHORTDATE: + if (!get_locale_info( locale, 0, LOCALE_SSHORTDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE; + pos = locale->sshortdate; + break; + case DATE_LONGDATE: + if (!get_locale_info( locale, 0, LOCALE_SLONGDATE, buffer, ARRAY_SIZE(buffer) )) return FALSE; + pos = locale->slongdate; + break; + case DATE_YEARMONTH: + if (!get_locale_info( locale, 0, LOCALE_SYEARMONTH, buffer, ARRAY_SIZE(buffer) )) return FALSE; + pos = locale->syearmonth; + break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + /* first the user override data */ + + ret = call_enum_date_func( proc, locale, flags, 0, buffer, 1, unicode, ex, exex, lparam ); + + /* then the remaining locale data */ + + array = (const DWORD *)(locale_strings + pos + 1); + for (i = 1; ret && i < locale_strings[pos]; i++) + ret = call_enum_date_func( proc, locale, flags, array[i], buffer, 1, unicode, ex, exex, lparam ); + + /* then the extra calendars */ + + for (i = 0; ret && i < calendars[0]; i++) + { + if (calendars[i + 1] == 1) continue; + if (!(cal = get_calendar_data( locale, calendars[i + 1] ))) continue; + switch (flags & ~LOCALE_USE_CP_ACP) + { + case 0: + case DATE_SHORTDATE: + pos = cal->sshortdate; + break; + case DATE_LONGDATE: + pos = cal->slongdate; + break; + case DATE_YEARMONTH: + pos = cal->syearmonth; + break; + } + array = (const DWORD *)(locale_strings + pos + 1); + for (j = 0; ret && j < locale_strings[pos]; j++) + ret = call_enum_date_func( proc, locale, flags, array[j], buffer, + calendars[i + 1], unicode, ex, exex, lparam ); + } + return TRUE; +} + + +/****************************************************************************** + * Internal_EnumLanguageGroupLocales (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumLanguageGroupLocales( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id, + DWORD flags, LONG_PTR param, BOOL unicode ) +{ + WCHAR name[10], value[10]; + DWORD name_len, value_len, type, index = 0, alt = 0; + HKEY key, altkey; + LCID lcid; + + if (!proc || id < LGRPID_WESTERN_EUROPE || id > LGRPID_ARMENIAN) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (RegOpenKeyExW( nls_key, L"Locale", 0, KEY_READ, &key )) return FALSE; + if (RegOpenKeyExW( key, L"Alternate Sorts", 0, KEY_READ, &altkey )) altkey = 0; + + for (;;) + { + name_len = ARRAY_SIZE(name); + value_len = sizeof(value); + if (RegEnumValueW( alt ? altkey : key, index++, name, &name_len, NULL, + &type, (BYTE *)value, &value_len )) + { + if (alt++) break; + index = 0; + continue; + } + if (type != REG_SZ) continue; + if (id != wcstoul( value, NULL, 16 )) continue; + lcid = wcstoul( name, NULL, 16 ); + if (!unicode) + { + char nameA[10]; + WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL ); + if (!((LANGGROUPLOCALE_ENUMPROCA)proc)( id, lcid, nameA, param )) break; + } + else if (!proc( id, lcid, name, param )) break; + } + RegCloseKey( altkey ); + RegCloseKey( key ); + return TRUE; +} + + +/*********************************************************************** + * Internal_EnumSystemCodePages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemCodePages( CODEPAGE_ENUMPROCW proc, DWORD flags, + BOOL unicode ) +{ + WCHAR name[10]; + DWORD name_len, type, index = 0; + HKEY key; + + if (RegOpenKeyExW( nls_key, L"Codepage", 0, KEY_READ, &key )) return FALSE; + + for (;;) + { + name_len = ARRAY_SIZE(name); + if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, NULL, NULL )) break; + if (type != REG_SZ) continue; + if (!wcstoul( name, NULL, 10 )) continue; + if (!unicode) + { + char nameA[10]; + WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL ); + if (!((CODEPAGE_ENUMPROCA)proc)( nameA )) break; + } + else if (!proc( name )) break; + } + RegCloseKey( key ); + return TRUE; +} + + +/****************************************************************************** + * Internal_EnumSystemLanguageGroups (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumSystemLanguageGroups( LANGUAGEGROUP_ENUMPROCW proc, + DWORD flags, LONG_PTR param, BOOL unicode ) +{ + WCHAR name[10], value[10], descr[80]; + DWORD name_len, value_len, type, index = 0; + HKEY key; + LGRPID id; + + if (!proc) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + switch (flags) + { + case 0: + flags = LGRPID_INSTALLED; + break; + case LGRPID_INSTALLED: + case LGRPID_SUPPORTED: + break; + default: + SetLastError( ERROR_INVALID_FLAGS ); + return FALSE; + } + + if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE; + + for (;;) + { + name_len = ARRAY_SIZE(name); + value_len = sizeof(value); + if (RegEnumValueW( key, index++, name, &name_len, NULL, &type, (BYTE *)value, &value_len )) break; + if (type != REG_SZ) continue; + + id = wcstoul( name, NULL, 16 ); + + if (!(flags & LGRPID_SUPPORTED) && !wcstoul( value, NULL, 10 )) continue; + if (!LoadStringW( kernelbase_handle, id, descr, ARRAY_SIZE(descr) )) descr[0] = 0; + TRACE( "%p: %lu %s %s %lx %Ix\n", proc, id, debugstr_w(name), debugstr_w(descr), flags, param ); + if (!unicode) + { + char nameA[10], descrA[80]; + WideCharToMultiByte( CP_ACP, 0, name, -1, nameA, sizeof(nameA), NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, descr, -1, descrA, sizeof(descrA), NULL, NULL ); + if (!((LANGUAGEGROUP_ENUMPROCA)proc)( id, nameA, descrA, flags, param )) break; + } + else if (!proc( id, name, descr, flags, param )) break; + } + RegCloseKey( key ); + return TRUE; +} + + +/************************************************************************** + * Internal_EnumTimeFormats (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumTimeFormats( TIMEFMT_ENUMPROCW proc, + const NLS_LOCALE_DATA *locale, DWORD flags, + BOOL unicode, BOOL ex, LPARAM lparam ) +{ + WCHAR buffer[256]; + INT ret = TRUE; + const DWORD *array; + DWORD pos, i; + + if (!proc || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + switch (flags & ~LOCALE_USE_CP_ACP) + { + case 0: + if (!get_locale_info( locale, 0, LOCALE_STIMEFORMAT, buffer, ARRAY_SIZE(buffer) )) return FALSE; + pos = locale->stimeformat; + break; + case TIME_NOSECONDS: + if (!get_locale_info( locale, 0, LOCALE_SSHORTTIME, buffer, ARRAY_SIZE(buffer) )) return FALSE; + pos = locale->sshorttime; + break; + default: + FIXME( "Unknown time format %lx\n", flags ); + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + array = (const DWORD *)(locale_strings + pos + 1); + for (i = 0; ret && i < locale_strings[pos]; i++) + { + if (i) memcpy( buffer, locale_strings + array[i] + 1, + (locale_strings[array[i]] + 1) * sizeof(WCHAR) ); + + if (ex) ret = ((TIMEFMT_ENUMPROCEX)proc)( buffer, lparam ); + else if (unicode) ret = proc( buffer ); + else + { + char buffA[256]; + WideCharToMultiByte( get_locale_codepage( locale, flags ), 0, buffer, -1, + buffA, ARRAY_SIZE(buffA), NULL, NULL ); + ret = proc( (WCHAR *)buffA ); + } + } + return TRUE; +} + + +/****************************************************************************** + * Internal_EnumUILanguages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH Internal_EnumUILanguages( UILANGUAGE_ENUMPROCW proc, DWORD flags, + LONG_PTR param, BOOL unicode ) +{ + WCHAR nameW[LOCALE_NAME_MAX_LENGTH]; + char nameA[LOCALE_NAME_MAX_LENGTH]; + DWORD i; + + if (!proc) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (flags & ~(MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME)) + { + SetLastError( ERROR_INVALID_FLAGS ); + return FALSE; + } + + for (i = 0; i < locale_table->nb_lcnames; i++) + { + if (!lcnames_index[i].name) continue; /* skip invariant locale */ + if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */ + if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */ + if (SORTIDFROMLCID( lcnames_index[i].id )) continue; /* skip alternate sorts */ + if (flags & MUI_LANGUAGE_NAME) + { + const WCHAR *str = locale_strings + lcnames_index[i].name; + + if (unicode) + { + memcpy( nameW, str + 1, (*str + 1) * sizeof(WCHAR) ); + if (!proc( nameW, param )) break; + } + else + { + WideCharToMultiByte( CP_ACP, 0, str + 1, -1, nameA, sizeof(nameA), NULL, NULL ); + if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break; + } + } + else + { + if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */ + if (unicode) + { + swprintf( nameW, ARRAY_SIZE(nameW), L"%04lx", lcnames_index[i].id ); + if (!proc( nameW, param )) break; + } + else + { + sprintf( nameA, "%04x", lcnames_index[i].id ); + if (!((UILANGUAGE_ENUMPROCA)proc)( nameA, param )) break; + } + } + } + return TRUE; +} + + +/****************************************************************************** + * CompareStringEx (kernelbase.@) + */ +INT WINAPI CompareStringEx( const WCHAR *locale, DWORD flags, const WCHAR *str1, int len1, + const WCHAR *str2, int len2, NLSVERSIONINFO *version, + void *reserved, LPARAM handle ) +{ + const struct sortguid *sortid; + const DWORD supported_flags = NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNORESYMBOLS | + SORT_STRINGSORT | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH | + NORM_LINGUISTIC_CASING | LINGUISTIC_IGNORECASE | + LINGUISTIC_IGNOREDIACRITIC | SORT_DIGITSASNUMBERS | + 0x10000000 | LOCALE_USE_CP_ACP; + /* 0x10000000 is related to diacritics in Arabic, Japanese, and Hebrew */ + int ret; + + if (version) FIXME( "unexpected version parameter\n" ); + if (reserved) FIXME( "unexpected reserved value\n" ); + if (handle) FIXME( "unexpected handle\n" ); + + if (flags & ~supported_flags) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + + if (!(sortid = get_language_sort( locale ))) return 0; + + if (!str1 || !str2) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (len1 < 0) len1 = lstrlenW(str1); + if (len2 < 0) len2 = lstrlenW(str2); + + ret = compare_string( sortid, flags, str1, len1, str2, len2 ); + if (ret < 0) return CSTR_LESS_THAN; + if (ret > 0) return CSTR_GREATER_THAN; + return CSTR_EQUAL; +} + + +/****************************************************************************** + * CompareStringA (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH CompareStringA( LCID lcid, DWORD flags, const char *str1, int len1, + const char *str2, int len2 ) +{ + WCHAR *buf1W = NtCurrentTeb()->StaticUnicodeBuffer; + WCHAR *buf2W = buf1W + 130; + LPWSTR str1W, str2W; + INT len1W = 0, len2W = 0, ret; + UINT locale_cp = CP_ACP; + + if (!str1 || !str2) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (flags & SORT_DIGITSASNUMBERS) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + + if (len1 < 0) len1 = strlen(str1); + if (len2 < 0) len2 = strlen(str2); + + locale_cp = get_lcid_codepage( lcid, flags ); + if (len1) + { + if (len1 <= 130) len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, buf1W, 130 ); + if (len1W) str1W = buf1W; + else + { + len1W = MultiByteToWideChar( locale_cp, 0, str1, len1, NULL, 0 ); + str1W = HeapAlloc( GetProcessHeap(), 0, len1W * sizeof(WCHAR) ); + if (!str1W) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return 0; + } + MultiByteToWideChar( locale_cp, 0, str1, len1, str1W, len1W ); + } + } + else + { + len1W = 0; + str1W = buf1W; + } + + if (len2) + { + if (len2 <= 130) len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, buf2W, 130 ); + if (len2W) str2W = buf2W; + else + { + len2W = MultiByteToWideChar( locale_cp, 0, str2, len2, NULL, 0 ); + str2W = HeapAlloc( GetProcessHeap(), 0, len2W * sizeof(WCHAR) ); + if (!str2W) + { + if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W ); + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return 0; + } + MultiByteToWideChar( locale_cp, 0, str2, len2, str2W, len2W ); + } + } + else + { + len2W = 0; + str2W = buf2W; + } + + ret = CompareStringW( lcid, flags, str1W, len1W, str2W, len2W ); + + if (str1W != buf1W) HeapFree( GetProcessHeap(), 0, str1W ); + if (str2W != buf2W) HeapFree( GetProcessHeap(), 0, str2W ); + return ret; +} + + +/****************************************************************************** + * CompareStringW (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH CompareStringW( LCID lcid, DWORD flags, const WCHAR *str1, int len1, + const WCHAR *str2, int len2 ) +{ + const WCHAR *locale = LOCALE_NAME_USER_DEFAULT; + const NLS_LOCALE_LCID_INDEX *entry; + + switch (lcid) + { + case LOCALE_NEUTRAL: + case LOCALE_USER_DEFAULT: + case LOCALE_SYSTEM_DEFAULT: + case LOCALE_CUSTOM_DEFAULT: + case LOCALE_CUSTOM_UNSPECIFIED: + case LOCALE_CUSTOM_UI_DEFAULT: + break; + default: + if (lcid == user_lcid || lcid == system_lcid) break; + if (!(entry = find_lcid_entry( lcid ))) + { + WARN( "unknown locale %04lx\n", lcid ); + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + locale = locale_strings + entry->name + 1; + break; + } + + return CompareStringEx( locale, flags, str1, len1, str2, len2, NULL, NULL, 0 ); +} + + +/****************************************************************************** + * CompareStringOrdinal (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH CompareStringOrdinal( const WCHAR *str1, INT len1, + const WCHAR *str2, INT len2, BOOL ignore_case ) +{ + int ret; + + if (!str1 || !str2) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (len1 < 0) len1 = lstrlenW( str1 ); + if (len2 < 0) len2 = lstrlenW( str2 ); + + ret = RtlCompareUnicodeStrings( str1, len1, str2, len2, ignore_case ); + if (ret < 0) return CSTR_LESS_THAN; + if (ret > 0) return CSTR_GREATER_THAN; + return CSTR_EQUAL; +} + + +/****************************************************************************** + * ConvertDefaultLocale (kernelbase.@) + */ +LCID WINAPI DECLSPEC_HOTPATCH ConvertDefaultLocale( LCID lcid ) +{ + const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 ); + if (locale) lcid = locale->ilanguage; + return lcid; +} + + +/****************************************************************************** + * EnumCalendarInfoW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoW( CALINFO_ENUMPROCW proc, LCID lcid, + CALID id, CALTYPE type ) +{ + return Internal_EnumCalendarInfo( proc, NlsValidateLocale( &lcid, 0 ), + id, type, TRUE, FALSE, FALSE, 0 ); +} + + +/****************************************************************************** + * EnumCalendarInfoExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExW( CALINFO_ENUMPROCEXW proc, LCID lcid, + CALID id, CALTYPE type ) +{ + return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ), + id, type, TRUE, TRUE, FALSE, 0 ); +} + +/****************************************************************************** + * EnumCalendarInfoExEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX proc, LPCWSTR locale, CALID id, + LPCWSTR reserved, CALTYPE type, LPARAM lparam ) +{ + LCID lcid; + return Internal_EnumCalendarInfo( (CALINFO_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ), + id, type, TRUE, TRUE, TRUE, lparam ); +} + + +/************************************************************************** + * EnumDateFormatsW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsW( DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags ) +{ + return Internal_EnumDateFormats( proc, NlsValidateLocale( &lcid, 0 ), + flags, TRUE, FALSE, FALSE, 0 ); +} + + +/************************************************************************** + * EnumDateFormatsExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExW( DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags ) +{ + return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, NlsValidateLocale( &lcid, 0 ), + flags, TRUE, TRUE, FALSE, 0 ); +} + + +/************************************************************************** + * EnumDateFormatsExEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumDateFormatsExEx( DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale, + DWORD flags, LPARAM lparam ) +{ + LCID lcid; + return Internal_EnumDateFormats( (DATEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ), + flags, TRUE, TRUE, TRUE, lparam ); +} + + + +/****************************************************************************** + * EnumDynamicTimeZoneInformation (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH EnumDynamicTimeZoneInformation( DWORD index, + DYNAMIC_TIME_ZONE_INFORMATION *info ) +{ + DYNAMIC_TIME_ZONE_INFORMATION tz; + LSTATUS ret; + DWORD size; + + if (!info) return ERROR_INVALID_PARAMETER; + + size = ARRAY_SIZE(tz.TimeZoneKeyName); + ret = RegEnumKeyExW( tz_key, index, tz.TimeZoneKeyName, &size, NULL, NULL, NULL, NULL ); + if (ret) return ret; + + tz.DynamicDaylightTimeDisabled = TRUE; + if (!GetTimeZoneInformationForYear( 0, &tz, (TIME_ZONE_INFORMATION *)info )) return GetLastError(); + + lstrcpyW( info->TimeZoneKeyName, tz.TimeZoneKeyName ); + info->DynamicDaylightTimeDisabled = FALSE; + return 0; +} + + +/****************************************************************************** + * EnumLanguageGroupLocalesW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumLanguageGroupLocalesW( LANGGROUPLOCALE_ENUMPROCW proc, LGRPID id, + DWORD flags, LONG_PTR param ) +{ + return Internal_EnumLanguageGroupLocales( proc, id, flags, param, TRUE ); +} + + +/****************************************************************************** + * EnumUILanguagesW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumUILanguagesW( UILANGUAGE_ENUMPROCW proc, DWORD flags, LONG_PTR param ) +{ + return Internal_EnumUILanguages( proc, flags, param, TRUE ); +} + + +/*********************************************************************** + * EnumSystemCodePagesW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemCodePagesW( CODEPAGE_ENUMPROCW proc, DWORD flags ) +{ + return Internal_EnumSystemCodePages( proc, flags, TRUE ); +} + + +/****************************************************************************** + * EnumSystemGeoID (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemGeoID( GEOCLASS class, GEOID parent, GEO_ENUMPROC proc ) +{ + INT i; + + TRACE( "(%ld, %ld, %p)\n", class, parent, proc ); + + if (!proc) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (class != GEOCLASS_NATION && class != GEOCLASS_REGION && class != GEOCLASS_ALL) + { + SetLastError( ERROR_INVALID_FLAGS ); + return FALSE; + } + + for (i = 0; i < geo_ids_count; i++) + { + if (class != GEOCLASS_ALL && geo_ids[i].class != class) continue; + if (parent && geo_ids[i].parent != parent) continue; + if (!proc( geo_ids[i].id )) break; + } + return TRUE; +} + + +/****************************************************************************** + * EnumSystemLanguageGroupsW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLanguageGroupsW( LANGUAGEGROUP_ENUMPROCW proc, + DWORD flags, LONG_PTR param ) +{ + return Internal_EnumSystemLanguageGroups( proc, flags, param, TRUE ); +} + + +/****************************************************************************** + * EnumSystemLocalesA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesA( LOCALE_ENUMPROCA proc, DWORD flags ) +{ + char name[10]; + DWORD i; + + if (!flags) + flags = LCID_SUPPORTED; + + for (i = 0; i < locale_table->nb_lcnames; i++) + { + if (!lcnames_index[i].name) continue; /* skip invariant locale */ + if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */ + if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */ + if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */ + if (SORTIDFROMLCID( lcnames_index[i].id ) != SORT_DEFAULT && !(flags & LCID_ALTERNATE_SORTS)) + continue; /* skip alternate sorts if not requested */ + if (SORTIDFROMLCID( lcnames_index[i].id ) == SORT_DEFAULT && !(flags & (LCID_INSTALLED | LCID_SUPPORTED))) + continue; /* skip default sorts if not requested */ + sprintf( name, "%08x", lcnames_index[i].id ); + if (!proc( name )) break; + } + return TRUE; +} + + +/****************************************************************************** + * EnumSystemLocalesW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesW( LOCALE_ENUMPROCW proc, DWORD flags ) +{ + WCHAR name[10]; + DWORD i; + + if (!flags) + flags = LCID_SUPPORTED; + + for (i = 0; i < locale_table->nb_lcnames; i++) + { + if (!lcnames_index[i].name) continue; /* skip invariant locale */ + if (lcnames_index[i].id == LOCALE_CUSTOM_UNSPECIFIED) continue; /* skip locales with no lcid */ + if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */ + if (!get_locale_data( lcnames_index[i].idx )->inotneutral) continue; /* skip neutral locales */ + if (SORTIDFROMLCID( lcnames_index[i].id ) != SORT_DEFAULT && !(flags & LCID_ALTERNATE_SORTS)) + continue; /* skip alternate sorts if not requested */ + if (SORTIDFROMLCID( lcnames_index[i].id ) == SORT_DEFAULT && !(flags & (LCID_INSTALLED | LCID_SUPPORTED))) + continue; /* skip default sorts if not requested */ + swprintf( name, ARRAY_SIZE(name), L"%08lx", lcnames_index[i].id ); + if (!proc( name )) break; + } + return TRUE; +} + + +/****************************************************************************** + * EnumSystemLocalesEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumSystemLocalesEx( LOCALE_ENUMPROCEX proc, DWORD wanted_flags, + LPARAM param, void *reserved ) +{ + WCHAR buffer[LOCALE_NAME_MAX_LENGTH]; + DWORD i, flags; + + if (reserved) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + for (i = 0; i < locale_table->nb_lcnames; i++) + { + const NLS_LOCALE_DATA *locale = get_locale_data( lcnames_index[i].idx ); + const WCHAR *str = locale_strings + lcnames_index[i].name; + + if (lcnames_index[i].id & 0x80000000) continue; /* skip aliases */ + memcpy( buffer, str + 1, (*str + 1) * sizeof(WCHAR) ); + if (SORTIDFROMLCID( lcnames_index[i].id ) || wcschr( str + 1, '_' )) + flags = LOCALE_ALTERNATE_SORTS; + else + flags = LOCALE_WINDOWS | (locale->inotneutral ? LOCALE_SPECIFICDATA : LOCALE_NEUTRALDATA); + if (wanted_flags && !(flags & wanted_flags)) continue; + if (!proc( buffer, flags, param )) break; + } + return TRUE; +} + + +/************************************************************************** + * EnumTimeFormatsW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsW( TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags ) +{ + return Internal_EnumTimeFormats( proc, NlsValidateLocale( &lcid, 0 ), flags, TRUE, FALSE, 0 ); +} + + +/************************************************************************** + * EnumTimeFormatsEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH EnumTimeFormatsEx( TIMEFMT_ENUMPROCEX proc, const WCHAR *locale, + DWORD flags, LPARAM lparam ) +{ + LCID lcid; + return Internal_EnumTimeFormats( (TIMEFMT_ENUMPROCW)proc, get_locale_by_name( locale, &lcid ), + flags, TRUE, TRUE, lparam ); +} + + +/************************************************************************** + * FindNLSString (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH FindNLSString( LCID lcid, DWORD flags, const WCHAR *src, + int srclen, const WCHAR *value, int valuelen, int *found ) +{ + const WCHAR *locale = LOCALE_NAME_USER_DEFAULT; + const NLS_LOCALE_LCID_INDEX *entry; + + switch (lcid) + { + case LOCALE_NEUTRAL: + case LOCALE_USER_DEFAULT: + case LOCALE_SYSTEM_DEFAULT: + case LOCALE_CUSTOM_DEFAULT: + case LOCALE_CUSTOM_UNSPECIFIED: + case LOCALE_CUSTOM_UI_DEFAULT: + break; + default: + if (lcid == user_lcid || lcid == system_lcid) break; + if (!(entry = find_lcid_entry( lcid ))) + { + WARN( "unknown locale %04lx\n", lcid ); + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + locale = locale_strings + entry->name + 1; + break; + } + + return FindNLSStringEx( locale, flags, src, srclen, value, valuelen, found, NULL, NULL, 0 ); +} + + +/************************************************************************** + * FindNLSStringEx (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH FindNLSStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, + int srclen, const WCHAR *value, int valuelen, int *found, + NLSVERSIONINFO *version, void *reserved, LPARAM handle ) +{ + const struct sortguid *sortid; + + TRACE( "%s %lx %s %d %s %d %p %p %p %Id\n", wine_dbgstr_w(locale), flags, + wine_dbgstr_w(src), srclen, wine_dbgstr_w(value), valuelen, found, + version, reserved, handle ); + + if (version) FIXME( "unexpected version parameter\n" ); + if (reserved) FIXME( "unexpected reserved value\n" ); + if (handle) FIXME( "unexpected handle\n" ); + + if (!src || !srclen || srclen < -1 || !value || !valuelen || valuelen < -1) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return -1; + } + if (!(sortid = get_language_sort( locale ))) return -1; + + if (srclen == -1) srclen = lstrlenW(src); + if (valuelen == -1) valuelen = lstrlenW(value); + + return find_substring( sortid, flags, src, srclen, value, valuelen, found ); +} + + +/****************************************************************************** + * FindStringOrdinal (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH FindStringOrdinal( DWORD flag, const WCHAR *src, INT src_size, + const WCHAR *val, INT val_size, BOOL ignore_case ) +{ + INT offset, inc, count; + + TRACE( "%#lx %s %d %s %d %d\n", flag, wine_dbgstr_w(src), src_size, + wine_dbgstr_w(val), val_size, ignore_case ); + + if (!src || !val) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return -1; + } + + if (flag != FIND_FROMSTART && flag != FIND_FROMEND && flag != FIND_STARTSWITH && flag != FIND_ENDSWITH) + { + SetLastError( ERROR_INVALID_FLAGS ); + return -1; + } + + if (src_size == -1) src_size = lstrlenW( src ); + if (val_size == -1) val_size = lstrlenW( val ); + + SetLastError( ERROR_SUCCESS ); + src_size -= val_size; + if (src_size < 0) return -1; + + count = flag & (FIND_FROMSTART | FIND_FROMEND) ? src_size + 1 : 1; + offset = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 0 : src_size; + inc = flag & (FIND_FROMSTART | FIND_STARTSWITH) ? 1 : -1; + while (count--) + { + if (CompareStringOrdinal( src + offset, val_size, val, val_size, ignore_case ) == CSTR_EQUAL) + return offset; + offset += inc; + } + return -1; +} + + +/****************************************************************************** + * FoldStringW (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH FoldStringW( DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen ) +{ + NTSTATUS status; + WCHAR *buf = dst; + int len = dstlen; + + if (!src || !srclen || dstlen < 0 || (dstlen && !dst) || src == dst) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (srclen == -1) srclen = lstrlenW(src) + 1; + + if (!dstlen && (flags & (MAP_PRECOMPOSED | MAP_FOLDCZONE | MAP_COMPOSITE))) + { + len = srclen * 4; + if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + return 0; + } + } + + for (;;) + { + status = fold_string( flags, src, srclen, buf, &len ); + if (buf != dst) RtlFreeHeap( GetProcessHeap(), 0, buf ); + if (status != STATUS_BUFFER_TOO_SMALL) break; + if (!(buf = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + return 0; + } + } + if (status == STATUS_INVALID_PARAMETER_1) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + if (!set_ntstatus( status )) return 0; + + if (dstlen && dstlen < len) SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return len; +} + + +static const WCHAR *get_message( DWORD flags, const void *src, UINT id, UINT lang, + BOOL ansi, WCHAR **buffer ) +{ + DWORD len; + + if (!(flags & FORMAT_MESSAGE_FROM_STRING)) + { + const MESSAGE_RESOURCE_ENTRY *entry; + NTSTATUS status = STATUS_INVALID_PARAMETER; + + if (flags & FORMAT_MESSAGE_FROM_HMODULE) + { + HMODULE module = (HMODULE)src; + if (!module) module = GetModuleHandleW( 0 ); + status = RtlFindMessage( module, RT_MESSAGETABLE, lang, id, &entry ); + } + if (status && (flags & FORMAT_MESSAGE_FROM_SYSTEM)) + { + /* Fold win32 hresult to its embedded error code. */ + if (HRESULT_SEVERITY(id) == SEVERITY_ERROR && HRESULT_FACILITY(id) == FACILITY_WIN32) + id = HRESULT_CODE( id ); + status = RtlFindMessage( kernelbase_handle, RT_MESSAGETABLE, lang, id, &entry ); + } + if (!set_ntstatus( status )) return NULL; + + src = entry->Text; + ansi = !(entry->Flags & MESSAGE_RESOURCE_UNICODE); + } + + if (!ansi) return src; + len = MultiByteToWideChar( CP_ACP, 0, src, -1, NULL, 0 ); + if (!(*buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL; + MultiByteToWideChar( CP_ACP, 0, src, -1, *buffer, len ); + return *buffer; +} + + +/*********************************************************************** + * FormatMessageA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageA( DWORD flags, const void *source, DWORD msgid, DWORD langid, + char *buffer, DWORD size, va_list *args ) +{ + DWORD ret = 0; + ULONG len, retsize = 0; + ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK); + const WCHAR *src; + WCHAR *result, *message = NULL; + NTSTATUS status; + + TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args ); + + if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) + { + if (!buffer) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return 0; + } + *(char **)buffer = NULL; + } + if (size >= 32768) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (width == 0xff) width = ~0u; + + if (!(src = get_message( flags, source, msgid, langid, TRUE, &message ))) return 0; + + if (!(result = HeapAlloc( GetProcessHeap(), 0, 65536 ))) + status = STATUS_NO_MEMORY; + else + status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS), + TRUE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args, + result, 65536, &retsize ); + + HeapFree( GetProcessHeap(), 0, message ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + goto done; + } + if (!set_ntstatus( status )) goto done; + + len = WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), NULL, 0, NULL, NULL ); + if (len <= 1) + { + SetLastError( ERROR_NO_WORK_DONE ); + goto done; + } + + if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) + { + char *buf = LocalAlloc( LMEM_ZEROINIT, max( size, len )); + if (!buf) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto done; + } + *(char **)buffer = buf; + WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buf, max( size, len ), NULL, NULL ); + } + else if (len > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + goto done; + } + else WideCharToMultiByte( CP_ACP, 0, result, retsize / sizeof(WCHAR), buffer, size, NULL, NULL ); + + ret = len - 1; + +done: + HeapFree( GetProcessHeap(), 0, result ); + return ret; +} + + +/*********************************************************************** + * FormatMessageW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, DWORD msgid, DWORD langid, + WCHAR *buffer, DWORD size, va_list *args ) +{ + ULONG retsize = 0; + ULONG width = (flags & FORMAT_MESSAGE_MAX_WIDTH_MASK); + const WCHAR *src; + WCHAR *message = NULL; + NTSTATUS status; + + TRACE( "(0x%lx,%p,%#lx,0x%lx,%p,%lu,%p)\n", flags, source, msgid, langid, buffer, size, args ); + + if (!buffer) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (width == 0xff) width = ~0u; + + if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) *(LPWSTR *)buffer = NULL; + + if (!(src = get_message( flags, source, msgid, langid, FALSE, &message ))) return 0; + + if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) + { + WCHAR *result; + va_list args_copy; + ULONG alloc = max( size * sizeof(WCHAR), 65536 ); + + for (;;) + { + if (!(result = HeapAlloc( GetProcessHeap(), 0, alloc ))) + { + status = STATUS_NO_MEMORY; + break; + } + if (args && !(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) + { + va_copy( args_copy, *args ); + status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS), + FALSE, FALSE, &args_copy, result, alloc, &retsize ); + va_end( args_copy ); + } + else + status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS), + FALSE, TRUE, args, result, alloc, &retsize ); + + if (!status) + { + if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result ); + else *(WCHAR **)buffer = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, + result, max( retsize, size * sizeof(WCHAR) )); + break; + } + HeapFree( GetProcessHeap(), 0, result ); + if (status != STATUS_BUFFER_OVERFLOW) break; + alloc *= 2; + } + } + else status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS), + FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args, + buffer, size * sizeof(WCHAR), &retsize ); + + HeapFree( GetProcessHeap(), 0, message ); + + if (status == STATUS_BUFFER_OVERFLOW) + { + if (size) buffer[size - 1] = 0; + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + if (!set_ntstatus( status )) return 0; + if (retsize <= sizeof(WCHAR)) SetLastError( ERROR_NO_WORK_DONE ); + return retsize / sizeof(WCHAR) - 1; +} + + +/****************************************************************************** + * GetACP (kernelbase.@) + */ +UINT WINAPI GetACP(void) +{ + return ansi_cpinfo.CodePage; +} + + +/*********************************************************************** + * GetCPInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetCPInfo( UINT codepage, CPINFO *cpinfo ) +{ + const CPTABLEINFO *table; + + if (!cpinfo || !(table = get_codepage_table( codepage ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + cpinfo->MaxCharSize = table->MaximumCharacterSize; + memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) ); + memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) ); + return TRUE; +} + + +/*********************************************************************** + * GetCPInfoExW (kernelbase.@) + */ +BOOL WINAPI GetCPInfoExW( UINT codepage, DWORD flags, CPINFOEXW *cpinfo ) +{ + const CPTABLEINFO *table; + int min, max, pos; + + if (!cpinfo || !(table = get_codepage_table( codepage ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + cpinfo->MaxCharSize = table->MaximumCharacterSize; + memcpy( cpinfo->DefaultChar, &table->DefaultChar, sizeof(cpinfo->DefaultChar) ); + memcpy( cpinfo->LeadByte, table->LeadByte, sizeof(cpinfo->LeadByte) ); + cpinfo->CodePage = table->CodePage; + cpinfo->UnicodeDefaultChar = table->UniDefaultChar; + + min = 0; + max = ARRAY_SIZE(codepage_names) - 1; + cpinfo->CodePageName[0] = 0; + while (min <= max) + { + pos = (min + max) / 2; + if (codepage_names[pos].cp < cpinfo->CodePage) min = pos + 1; + else if (codepage_names[pos].cp > cpinfo->CodePage) max = pos - 1; + else + { + wcscpy( cpinfo->CodePageName, codepage_names[pos].name ); + break; + } + } + return TRUE; +} + + +/*********************************************************************** + * GetCalendarInfoW (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, + WCHAR *buffer, INT len, DWORD *value ) +{ + const NLS_LOCALE_DATA *locale; + + TRACE( "%04lx %lu 0x%lx %p %d %p\n", lcid, calendar, type, buffer, len, value ); + + if (!(locale = NlsValidateLocale( &lcid, 0 ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + return get_calendar_info( locale, calendar, type, buffer, len, value ); +} + + +/*********************************************************************** + * GetCalendarInfoEx (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetCalendarInfoEx( const WCHAR *name, CALID calendar, const WCHAR *reserved, + CALTYPE type, WCHAR *buffer, INT len, DWORD *value ) +{ + LCID lcid; + const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid ); + + TRACE( "%s %lu 0x%lx %p %d %p\n", debugstr_w(name), calendar, type, buffer, len, value ); + + if (!locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + return get_calendar_info( locale, calendar, type, buffer, len, value ); +} + + +static CRITICAL_SECTION tzname_section; +static CRITICAL_SECTION_DEBUG tzname_section_debug = +{ + 0, 0, &tzname_section, + { &tzname_section_debug.ProcessLocksList, &tzname_section_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": tzname_section") } +}; +static CRITICAL_SECTION tzname_section = { &tzname_section_debug, -1, 0, 0, 0, 0 }; +static struct { + LCID lcid; + WCHAR key_name[128]; + WCHAR standard_name[32]; + WCHAR daylight_name[32]; +} cached_tzname; + +/*********************************************************************** + * GetDynamicTimeZoneInformation (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformation( DYNAMIC_TIME_ZONE_INFORMATION *info ) +{ + HKEY key; + LARGE_INTEGER now; + + if (!set_ntstatus( RtlQueryDynamicTimeZoneInformation( (RTL_DYNAMIC_TIME_ZONE_INFORMATION *)info ))) + return TIME_ZONE_ID_INVALID; + + RtlEnterCriticalSection( &tzname_section ); + if (cached_tzname.lcid == GetThreadLocale() && + !wcscmp( info->TimeZoneKeyName, cached_tzname.key_name )) + { + wcscpy( info->StandardName, cached_tzname.standard_name ); + wcscpy( info->DaylightName, cached_tzname.daylight_name ); + RtlLeaveCriticalSection( &tzname_section ); + } + else + { + RtlLeaveCriticalSection( &tzname_section ); + if (!RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) + { + RegLoadMUIStringW( key, L"MUI_Std", info->StandardName, + sizeof(info->StandardName), NULL, 0, system_dir ); + RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName, + sizeof(info->DaylightName), NULL, 0, system_dir ); + RegCloseKey( key ); + } + else return TIME_ZONE_ID_INVALID; + + RtlEnterCriticalSection( &tzname_section ); + cached_tzname.lcid = GetThreadLocale(); + wcscpy( cached_tzname.key_name, info->TimeZoneKeyName ); + wcscpy( cached_tzname.standard_name, info->StandardName ); + wcscpy( cached_tzname.daylight_name, info->DaylightName ); + RtlLeaveCriticalSection( &tzname_section ); + } + + NtQuerySystemTime( &now ); + return get_timezone_id( (TIME_ZONE_INFORMATION *)info, now, FALSE ); +} + + +/****************************************************************************** + * GetDynamicTimeZoneInformationEffectiveYears (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetDynamicTimeZoneInformationEffectiveYears( const DYNAMIC_TIME_ZONE_INFORMATION *info, + DWORD *first, DWORD *last ) +{ + HKEY key, dst_key = 0; + DWORD type, count, ret = ERROR_FILE_NOT_FOUND; + + if (RegOpenKeyExW( tz_key, info->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key )) return ret; + + if (RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) goto done; + count = sizeof(DWORD); + if (RegQueryValueExW( dst_key, L"FirstEntry", NULL, &type, (BYTE *)first, &count )) goto done; + if (type != REG_DWORD) goto done; + count = sizeof(DWORD); + if (RegQueryValueExW( dst_key, L"LastEntry", NULL, &type, (BYTE *)last, &count )) goto done; + if (type != REG_DWORD) goto done; + ret = 0; + +done: + RegCloseKey( dst_key ); + RegCloseKey( key ); + return ret; +} + + +#define MUI_SIGNATURE 0xfecdfecd +struct mui_resource +{ + DWORD signature; + DWORD size; + DWORD version; + DWORD path_type; + DWORD file_type; + DWORD system_attributes; + DWORD fallback_location; + BYTE service_checksum[16]; + BYTE checksum[16]; + DWORD unk1[2]; + DWORD mui_path_off; + DWORD mui_path_size; + DWORD unk2[2]; + DWORD ln_type_name_off; + DWORD ln_type_name_size; + DWORD ln_type_id_off; + DWORD ln_type_id_size; + DWORD mui_type_name_off; + DWORD mui_type_name_size; + DWORD mui_type_id_off; + DWORD mui_type_id_size; + DWORD lang_off; + DWORD lang_size; + DWORD fallback_lang_off; + DWORD fallback_lang_size; +}; + + +static BOOL validate_mui_resource(struct mui_resource *mui, DWORD size) +{ + if (size >= sizeof(DWORD) && mui->signature != MUI_SIGNATURE) + { + SetLastError(ERROR_MUI_INVALID_RC_CONFIG); + return FALSE; + } + + size = min( size, mui->size ); + if (size < sizeof(*mui) || + mui->ln_type_name_off >= size || mui->ln_type_name_size > size - mui->ln_type_name_off || + mui->ln_type_id_off >= size || mui->ln_type_id_size > size - mui->ln_type_id_off || + mui->mui_type_name_off >= size || mui->mui_type_name_size > size - mui->mui_type_name_off || + mui->mui_type_id_off >= size || mui->mui_type_id_size > size - mui->mui_type_id_off || + mui->lang_off >= size || mui->lang_size > size - mui->lang_off || + mui->fallback_lang_off >= size || mui->fallback_lang_size > size - mui->fallback_lang_off) + { + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + return TRUE; +} + + +/****************************************************************************** + * GetFileMUIInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetFileMUIInfo( DWORD flags, const WCHAR *path, + FILEMUIINFO *info, DWORD *size ) +{ + DWORD off, mui_size, type = MUI_FILETYPE_NOT_LANGUAGE_NEUTRAL; + struct mui_resource *mui = NULL; + HMODULE hmod; + HRSRC hrsrc; + + TRACE( "%lu, %s, %p, %p\n", flags, debugstr_w(path), info, size ); + + if (!path || !size || (*size && !info) || + (info && (*size < sizeof(*info) || info->dwSize != *size || + info->dwVersion != MUI_FILEINFO_VERSION))) + { + if (size) *size = 0; + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (!flags) flags = MUI_QUERY_TYPE | MUI_QUERY_CHECKSUM; + + hmod = LoadLibraryExW( path, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE ); + if (!hmod) return FALSE; + + hrsrc = FindResourceW( hmod, MAKEINTRESOURCEW(1), L"MUI" ); + if (hrsrc) + { + mui = LockResource( LoadResource(hmod, hrsrc) ); + if (mui) mui_size = SizeofResource( hmod, hrsrc ); + if (!mui || !validate_mui_resource( mui, mui_size )) + { + FreeLibrary( hmod ); + return FALSE; + } + if (mui->file_type & (MUI_FILETYPE_LANGUAGE_NEUTRAL_MAIN >> 1)) + type = MUI_FILETYPE_LANGUAGE_NEUTRAL_MAIN; + else if (mui->file_type & (MUI_FILETYPE_LANGUAGE_NEUTRAL_MUI >> 1)) + type = MUI_FILETYPE_LANGUAGE_NEUTRAL_MUI; + } + if (type == MUI_FILETYPE_NOT_LANGUAGE_NEUTRAL) + { + FreeLibrary( hmod ); + + if (!info) + { + *size = sizeof(*info); + return TRUE; + } + if (info->dwSize < sizeof(*info)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + memset( info, 0, sizeof(*info) ); + info->dwSize = *size; + info->dwVersion = MUI_FILEINFO_VERSION; + if (flags & MUI_QUERY_TYPE) info->dwFileType = type; + return TRUE; + } + + off = offsetof(FILEMUIINFO, abBuffer); + if (flags & MUI_QUERY_LANGUAGE_NAME) + { + off += type == MUI_FILETYPE_LANGUAGE_NEUTRAL_MAIN ? + mui->fallback_lang_size : mui->lang_size; + } + if (flags & MUI_QUERY_RESOURCE_TYPES) + { + if (type == MUI_FILETYPE_LANGUAGE_NEUTRAL_MAIN) + off += mui->ln_type_name_size + mui->ln_type_id_size; + off += mui->mui_type_name_size + mui->mui_type_id_size; + } + if (off < sizeof(*info)) off = sizeof(*info); + + if (!info || info->dwSize < off) + { + FreeLibrary( hmod ); + *size = off; + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + off = 0; + memset( info, 0, sizeof(*info) ); + info->dwSize = *size; + info->dwVersion = MUI_FILEINFO_VERSION; + if (flags & MUI_QUERY_TYPE) info->dwFileType = type; + if (flags & MUI_QUERY_CHECKSUM) + { + memcpy( info->pChecksum, mui->checksum, sizeof(info->pChecksum) ); + memcpy( info->pServiceChecksum, mui->service_checksum, sizeof(info->pServiceChecksum) ); + } + if (flags & MUI_QUERY_LANGUAGE_NAME) + { + if (type == MUI_FILETYPE_LANGUAGE_NEUTRAL_MAIN && mui->fallback_lang_off) + { + info->dwLanguageNameOffset = offsetof(FILEMUIINFO, abBuffer); + memcpy(info->abBuffer, ((BYTE *)mui) + mui->fallback_lang_off, + mui->fallback_lang_size); + off += mui->fallback_lang_size; + } + if (type == MUI_FILETYPE_LANGUAGE_NEUTRAL_MUI && mui->lang_off) + { + info->dwLanguageNameOffset = offsetof(FILEMUIINFO, abBuffer); + memcpy(info->abBuffer, ((BYTE *)mui) + mui->lang_off, mui->lang_size); + off += mui->lang_size; + } + } + if (flags & MUI_QUERY_RESOURCE_TYPES && type & MUI_FILETYPE_LANGUAGE_NEUTRAL_MAIN) + { + if (mui->ln_type_id_size && mui->ln_type_id_off) + { + info->dwTypeIDMainSize = mui->ln_type_id_size / sizeof(DWORD); + info->dwTypeIDMainOffset = offsetof(FILEMUIINFO, abBuffer[off]); + memcpy(info->abBuffer + off, ((BYTE *)mui) + mui->ln_type_id_off, mui->ln_type_id_size); + off += mui->ln_type_id_size; + } + if (mui->ln_type_name_off) + { + info->dwTypeNameMainOffset = offsetof(FILEMUIINFO, abBuffer[off]); + memcpy(info->abBuffer + off, ((BYTE *)mui) + mui->ln_type_name_off, mui->ln_type_name_size); + off += mui->ln_type_name_size; + } + if (mui->mui_type_id_size && mui->mui_type_id_off) + { + info->dwTypeIDMUISize = mui->mui_type_id_size / sizeof(DWORD); + info->dwTypeIDMUIOffset = offsetof(FILEMUIINFO, abBuffer[off]); + memcpy(info->abBuffer + off, ((BYTE *)mui) + mui->mui_type_id_off, mui->mui_type_id_size); + off += mui->mui_type_id_size; + } + if (mui->mui_type_name_off) + { + info->dwTypeNameMUIOffset = offsetof(FILEMUIINFO, abBuffer[off]); + memcpy(info->abBuffer + off, ((BYTE *)mui) + mui->mui_type_name_off, mui->mui_type_name_size); + off += mui->mui_type_name_size; + } + } + else if(flags & MUI_QUERY_RESOURCE_TYPES) + { + if (mui->ln_type_id_size && mui->ln_type_id_off) + { + info->dwTypeIDMUISize = mui->ln_type_id_size / sizeof(DWORD); + info->dwTypeIDMUIOffset = offsetof(FILEMUIINFO, abBuffer[off]); + memcpy(info->abBuffer + off, ((BYTE *)mui) + mui->ln_type_id_off, mui->ln_type_id_size); + off += mui->ln_type_id_size; + } + if (mui->ln_type_name_off) + { + info->dwTypeNameMUIOffset = offsetof(FILEMUIINFO, abBuffer[off]); + memcpy(info->abBuffer + off, ((BYTE *)mui) + mui->ln_type_name_off, mui->ln_type_name_size); + off += mui->ln_type_name_size; + } + } + + FreeLibrary( hmod ); + return TRUE; +} + + +/****************************************************************************** + * GetFileMUIPath (kernelbase.@) + */ +BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetFileMUIPath( DWORD flags, const WCHAR *filepath, + WCHAR *language, ULONG *languagelen, + WCHAR *muipath, ULONG *muipathlen, + ULONGLONG *enumerator ) +{ + FIXME( "stub: 0x%lx, %s, %s, %p, %p, %p, %p\n", flags, debugstr_w(filepath), + debugstr_w(language), languagelen, muipath, muipathlen, enumerator ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/****************************************************************************** + * GetGeoInfoW (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoW( GEOID id, GEOTYPE type, WCHAR *data, int count, LANGID lang ) +{ + const struct geo_id *ptr = find_geo_id_entry( id ); + + TRACE( "%ld %ld %p %d %d\n", id, type, data, count, lang ); + + if (!ptr) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + return get_geo_info( ptr, type, data, count, lang ); +} + + +INT WINAPI DECLSPEC_HOTPATCH GetGeoInfoEx( WCHAR *location, GEOTYPE type, WCHAR *data, int data_count ) +{ + const struct geo_id *ptr = find_geo_name_entry( location ); + + TRACE( "%s %lx %p %d\n", wine_dbgstr_w(location), type, data, data_count ); + + if (!ptr) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (type == GEO_LCID || type == GEO_NATION || type == GEO_RFC1766) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + + return get_geo_info( ptr, type, data, data_count, 0 ); +} + + +/****************************************************************************** + * GetLocaleInfoA (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoA( LCID lcid, LCTYPE lctype, char *buffer, INT len ) +{ + const NLS_LOCALE_DATA *locale; + WCHAR *bufferW; + INT lenW, ret; + + TRACE( "lcid=0x%lx lctype=0x%lx %p %d\n", lcid, lctype, buffer, len ); + + if (len < 0 || (len && !buffer)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (LOWORD(lctype) == LOCALE_SSHORTTIME || (lctype & LOCALE_RETURN_GENITIVE_NAMES)) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + if (!(locale = NlsValidateLocale( &lcid, 0 ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (LOWORD(lctype) == LOCALE_FONTSIGNATURE || (lctype & LOCALE_RETURN_NUMBER)) + { + ret = get_locale_info( locale, lcid, lctype, (WCHAR *)buffer, len / sizeof(WCHAR) ); + return ret * sizeof(WCHAR); + } + + if (!(lenW = get_locale_info( locale, lcid, lctype, NULL, 0 ))) return 0; + + if (!(bufferW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return 0; + } + ret = get_locale_info( locale, lcid, lctype, bufferW, lenW ); + if (ret) ret = WideCharToMultiByte( get_locale_codepage( locale, lctype ), 0, + bufferW, ret, buffer, len, NULL, NULL ); + RtlFreeHeap( GetProcessHeap(), 0, bufferW ); + return ret; +} + + +/****************************************************************************** + * GetLocaleInfoW (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoW( LCID lcid, LCTYPE lctype, WCHAR *buffer, INT len ) +{ + const NLS_LOCALE_DATA *locale; + + if (len < 0 || (len && !buffer)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(lcid=0x%lx,lctype=0x%lx,%p,%d)\n", lcid, lctype, buffer, len ); + + if (!(locale = NlsValidateLocale( &lcid, 0 ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + return get_locale_info( locale, lcid, lctype, buffer, len ); +} + + +/****************************************************************************** + * GetLocaleInfoEx (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetLocaleInfoEx( const WCHAR *name, LCTYPE info, WCHAR *buffer, INT len ) +{ + LCID lcid; + const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid ); + + TRACE( "%s 0x%lx %p %d\n", debugstr_w(name), info, buffer, len ); + + if (!locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + return get_locale_info( locale, lcid, info, buffer, len ); +} + + +/****************************************************************************** + * GetNLSVersion (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersion( NLS_FUNCTION func, LCID lcid, NLSVERSIONINFO *info ) +{ + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + + if (info->dwNLSVersionInfoSize < offsetof( NLSVERSIONINFO, dwEffectiveId )) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + if (!LCIDToLocaleName( lcid, locale, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return GetNLSVersionEx( func, locale, (NLSVERSIONINFOEX *)info ); +} + + +/****************************************************************************** + * GetNLSVersionEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetNLSVersionEx( NLS_FUNCTION func, const WCHAR *locale, + NLSVERSIONINFOEX *info ) +{ + const struct sortguid *sortid; + + if (func != COMPARE_STRING) + { + SetLastError( ERROR_INVALID_FLAGS ); + return FALSE; + } + if (info->dwNLSVersionInfoSize < sizeof(*info) && + (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId ))) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + if (!(sortid = get_language_sort( locale ))) return FALSE; + + info->dwNLSVersion = info->dwDefinedVersion = sort.version; + if (info->dwNLSVersionInfoSize >= sizeof(*info)) + { + info->dwEffectiveId = LocaleNameToLCID( locale, 0 ); + info->guidCustomVersion = sortid->id; + } + return TRUE; +} + + +/****************************************************************************** + * GetOEMCP (kernelbase.@) + */ +UINT WINAPI GetOEMCP(void) +{ + return oem_cpinfo.CodePage; +} + + +/*********************************************************************** + * GetProcessPreferredUILanguages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProcessPreferredUILanguages( DWORD flags, ULONG *count, + WCHAR *buffer, ULONG *size ) +{ + return set_ntstatus( RtlGetProcessPreferredUILanguages( flags, count, buffer, size )); +} + + +/*********************************************************************** + * GetStringTypeA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeA( LCID locale, DWORD type, const char *src, int count, + WORD *chartype ) +{ + UINT cp; + INT countW; + LPWSTR srcW; + BOOL ret = FALSE; + + if (count == -1) count = strlen(src) + 1; + + cp = get_lcid_codepage( locale, 0 ); + countW = MultiByteToWideChar(cp, 0, src, count, NULL, 0); + if((srcW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR)))) + { + MultiByteToWideChar(cp, 0, src, count, srcW, countW); + /* + * NOTE: the target buffer has 1 word for each CHARACTER in the source + * string, with multibyte characters there maybe be more bytes in count + * than character space in the buffer! + */ + ret = GetStringTypeW(type, srcW, countW, chartype); + HeapFree(GetProcessHeap(), 0, srcW); + } + return ret; +} + + +/*********************************************************************** + * GetStringTypeW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeW( DWORD type, const WCHAR *src, INT count, WORD *chartype ) +{ + if (!src) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (type != CT_CTYPE1 && type != CT_CTYPE2 && type != CT_CTYPE3) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (count == -1) count = lstrlenW(src) + 1; + + while (count--) *chartype++ = get_char_type( type, *src++ ); + + return TRUE; +} + + +/*********************************************************************** + * GetStringTypeExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetStringTypeExW( LCID locale, DWORD type, const WCHAR *src, int count, + WORD *chartype ) +{ + /* locale is ignored for Unicode */ + return GetStringTypeW( type, src, count, chartype ); +} + + +/*********************************************************************** + * GetSystemDefaultLCID (kernelbase.@) + */ +LCID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLCID(void) +{ + return system_lcid; +} + + +/*********************************************************************** + * GetSystemDefaultLangID (kernelbase.@) + */ +LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLangID(void) +{ + return LANGIDFROMLCID( GetSystemDefaultLCID() ); +} + + +/*********************************************************************** + * GetSystemDefaultLocaleName (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetSystemDefaultLocaleName( LPWSTR name, INT count ) +{ + return get_locale_info( system_locale, system_lcid, LOCALE_SNAME, name, count ); +} + + +/*********************************************************************** + * GetSystemDefaultUILanguage (kernelbase.@) + */ +LANGID WINAPI DECLSPEC_HOTPATCH GetSystemDefaultUILanguage(void) +{ + LANGID lang; + NtQueryInstallUILanguage( &lang ); + return lang; +} + + +/*********************************************************************** + * GetSystemPreferredUILanguages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetSystemPreferredUILanguages( DWORD flags, ULONG *count, + WCHAR *buffer, ULONG *size ) +{ + return set_ntstatus( RtlGetSystemPreferredUILanguages( flags, 0, count, buffer, size )); +} + + +/*********************************************************************** + * GetThreadPreferredUILanguages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetThreadPreferredUILanguages( DWORD flags, ULONG *count, + WCHAR *buffer, ULONG *size ) +{ + return set_ntstatus( RtlGetThreadPreferredUILanguages( flags, count, buffer, size )); +} + + +/*********************************************************************** + * GetTimeZoneInformation (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformation( TIME_ZONE_INFORMATION *info ) +{ + DYNAMIC_TIME_ZONE_INFORMATION tzinfo; + DWORD ret = GetDynamicTimeZoneInformation( &tzinfo ); + + memcpy( info, &tzinfo, sizeof(*info) ); + return ret; +} + + +/*********************************************************************** + * GetTimeZoneInformationForYear (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetTimeZoneInformationForYear( USHORT year, + DYNAMIC_TIME_ZONE_INFORMATION *dynamic, + TIME_ZONE_INFORMATION *info ) +{ + DYNAMIC_TIME_ZONE_INFORMATION local_info; + HKEY key = 0, dst_key; + DWORD count; + LRESULT ret; + struct + { + LONG bias; + LONG std_bias; + LONG dlt_bias; + SYSTEMTIME std_date; + SYSTEMTIME dlt_date; + } data; + + TRACE( "(%u,%p)\n", year, info ); + + if (!dynamic) + { + if (GetDynamicTimeZoneInformation( &local_info ) == TIME_ZONE_ID_INVALID) return FALSE; + dynamic = &local_info; + } + + if ((ret = RegOpenKeyExW( tz_key, dynamic->TimeZoneKeyName, 0, KEY_ALL_ACCESS, &key ))) goto done; + if (RegLoadMUIStringW( key, L"MUI_Std", info->StandardName, + sizeof(info->StandardName), NULL, 0, system_dir )) + { + count = sizeof(info->StandardName); + if ((ret = RegQueryValueExW( key, L"Std", NULL, NULL, (BYTE *)info->StandardName, &count ))) + goto done; + } + if (RegLoadMUIStringW( key, L"MUI_Dlt", info->DaylightName, + sizeof(info->DaylightName), NULL, 0, system_dir )) + { + count = sizeof(info->DaylightName); + if ((ret = RegQueryValueExW( key, L"Dlt", NULL, NULL, (BYTE *)info->DaylightName, &count ))) + goto done; + } + + ret = ERROR_FILE_NOT_FOUND; + if (!dynamic->DynamicDaylightTimeDisabled && + !RegOpenKeyExW( key, L"Dynamic DST", 0, KEY_ALL_ACCESS, &dst_key )) + { + WCHAR yearW[16]; + swprintf( yearW, ARRAY_SIZE(yearW), L"%u", year ); + count = sizeof(data); + ret = RegQueryValueExW( dst_key, yearW, NULL, NULL, (BYTE *)&data, &count ); + RegCloseKey( dst_key ); + } + if (ret) + { + count = sizeof(data); + ret = RegQueryValueExW( key, L"TZI", NULL, NULL, (BYTE *)&data, &count ); + } + + if (!ret) + { + info->Bias = data.bias; + info->StandardBias = data.std_bias; + info->DaylightBias = data.dlt_bias; + info->StandardDate = data.std_date; + info->DaylightDate = data.dlt_date; + } + +done: + RegCloseKey( key ); + if (ret) SetLastError( ret ); + return !ret; +} + + +/*********************************************************************** + * GetUserDefaultLCID (kernelbase.@) + */ +LCID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLCID(void) +{ + return user_lcid; +} + + +/*********************************************************************** + * GetUserDefaultLangID (kernelbase.@) + */ +LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultLangID(void) +{ + return LANGIDFROMLCID( GetUserDefaultLCID() ); +} + + +/*********************************************************************** + * GetUserDefaultLocaleName (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetUserDefaultLocaleName( LPWSTR name, INT len ) +{ + return get_locale_info( user_locale, user_lcid, LOCALE_SNAME, name, len ); +} + + +/*********************************************************************** + * GetUserDefaultUILanguage (kernelbase.@) + */ +LANGID WINAPI DECLSPEC_HOTPATCH GetUserDefaultUILanguage(void) +{ + return LANGIDFROMLCID( GetUserDefaultLCID() ); +} + + +/****************************************************************************** + * GetUserGeoID (kernelbase.@) + */ +GEOID WINAPI DECLSPEC_HOTPATCH GetUserGeoID( GEOCLASS geoclass ) +{ + GEOID ret = 39070; + const WCHAR *name; + WCHAR bufferW[40]; + HKEY hkey; + + switch (geoclass) + { + case GEOCLASS_NATION: + name = L"Nation"; + break; + case GEOCLASS_REGION: + name = L"Region"; + break; + default: + WARN("Unknown geoclass %ld\n", geoclass); + return GEOID_NOT_AVAILABLE; + } + if (!RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &hkey )) + { + DWORD count = sizeof(bufferW); + if (!RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)bufferW, &count )) + ret = wcstol( bufferW, NULL, 10 ); + RegCloseKey( hkey ); + } + return ret; +} + + +/****************************************************************************** + * GetUserPreferredUILanguages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetUserPreferredUILanguages( DWORD flags, ULONG *count, + WCHAR *buffer, ULONG *size ) +{ + return set_ntstatus( RtlGetUserPreferredUILanguages( flags, 0, count, buffer, size )); +} + + +/****************************************************************************** + * IdnToAscii (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH IdnToAscii( DWORD flags, const WCHAR *src, INT srclen, + WCHAR *dst, INT dstlen ) +{ + NTSTATUS status = RtlIdnToAscii( flags, src, srclen, dst, &dstlen ); + if (!set_ntstatus( status )) return 0; + return dstlen; +} + + +/****************************************************************************** + * IdnToNameprepUnicode (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH IdnToNameprepUnicode( DWORD flags, const WCHAR *src, INT srclen, + WCHAR *dst, INT dstlen ) +{ + NTSTATUS status = RtlIdnToNameprepUnicode( flags, src, srclen, dst, &dstlen ); + if (!set_ntstatus( status )) return 0; + return dstlen; +} + + +/****************************************************************************** + * IdnToUnicode (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH IdnToUnicode( DWORD flags, const WCHAR *src, INT srclen, + WCHAR *dst, INT dstlen ) +{ + NTSTATUS status = RtlIdnToUnicode( flags, src, srclen, dst, &dstlen ); + if (!set_ntstatus( status )) return 0; + return dstlen; +} + + +/****************************************************************************** + * IsCharAlphaA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaA( CHAR c ) +{ + WCHAR wc; + DWORD reslen; + RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 ); + return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_ALPHA); +} + + +/****************************************************************************** + * IsCharAlphaW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_ALPHA); +} + + +/****************************************************************************** + * IsCharAlphaNumericA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericA( CHAR c ) +{ + WCHAR wc; + DWORD reslen; + RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 ); + return reslen && (get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT)); +} + + +/****************************************************************************** + * IsCharAlphaNumericW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharAlphaNumericW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & (C1_ALPHA | C1_DIGIT)); +} + + +/****************************************************************************** + * IsCharBlankW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharBlankW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_BLANK); +} + + +/****************************************************************************** + * IsCharCntrlW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharCntrlW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_CNTRL); +} + + +/****************************************************************************** + * IsCharDigitW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharDigitW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_DIGIT); +} + + +/****************************************************************************** + * IsCharLowerA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerA( CHAR c ) +{ + WCHAR wc; + DWORD reslen; + RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 ); + return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_LOWER); +} + + +/****************************************************************************** + * IsCharLowerW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharLowerW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_LOWER); +} + + +/****************************************************************************** + * IsCharPunctW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharPunctW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_PUNCT); +} + + +/****************************************************************************** + * IsCharSpaceA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceA( CHAR c ) +{ + WCHAR wc; + DWORD reslen; + RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 ); + return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_SPACE); +} + + +/****************************************************************************** + * IsCharSpaceW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharSpaceW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_SPACE); +} + + +/****************************************************************************** + * IsCharUpperA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperA( CHAR c ) +{ + WCHAR wc; + DWORD reslen; + RtlMultiByteToUnicodeN( &wc, sizeof(WCHAR), &reslen, &c, 1 ); + return reslen && (get_char_type( CT_CTYPE1, wc ) & C1_UPPER); +} + + +/****************************************************************************** + * IsCharUpperW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharUpperW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_UPPER); +} + + +/****************************************************************************** + * IsCharXDigitW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsCharXDigitW( WCHAR wc ) +{ + return !!(get_char_type( CT_CTYPE1, wc ) & C1_XDIGIT); +} + + +/****************************************************************************** + * IsDBCSLeadByte (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByte( BYTE testchar ) +{ + return ansi_cpinfo.DBCSCodePage && ansi_cpinfo.DBCSOffsets[testchar]; +} + + +/****************************************************************************** + * IsDBCSLeadByteEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsDBCSLeadByteEx( UINT codepage, BYTE testchar ) +{ + const CPTABLEINFO *table = get_codepage_table( codepage ); + return table && table->DBCSCodePage && table->DBCSOffsets[testchar]; +} + + +/****************************************************************************** + * IsNormalizedString (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsNormalizedString( NORM_FORM form, const WCHAR *str, INT len ) +{ + BOOLEAN res; + if (!set_ntstatus( RtlIsNormalizedString( form, str, len, &res ))) res = FALSE; + return res; +} + + +/****************************************************************************** + * IsValidCodePage (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsValidCodePage( UINT codepage ) +{ + switch (codepage) + { + case CP_ACP: + case CP_OEMCP: + case CP_MACCP: + case CP_THREAD_ACP: + return FALSE; + default: + return get_codepage_table( codepage ) != NULL; + } +} + + +/****************************************************************************** + * IsValidLanguageGroup (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsValidLanguageGroup( LGRPID id, DWORD flags ) +{ + WCHAR name[10], value[10]; + DWORD type, value_len = sizeof(value); + BOOL ret = FALSE; + HKEY key; + + if (RegOpenKeyExW( nls_key, L"Language Groups", 0, KEY_READ, &key )) return FALSE; + + swprintf( name, ARRAY_SIZE(name), L"%x", id ); + if (!RegQueryValueExW( key, name, NULL, &type, (BYTE *)value, &value_len ) && type == REG_SZ) + ret = (flags & LGRPID_SUPPORTED) || wcstoul( value, NULL, 10 ); + + RegCloseKey( key ); + return ret; +} + + +/****************************************************************************** + * IsValidLocale (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocale( LCID lcid, DWORD flags ) +{ + switch (lcid) + { + case LOCALE_NEUTRAL: + case LOCALE_USER_DEFAULT: + case LOCALE_SYSTEM_DEFAULT: + return FALSE; + default: + return !!NlsValidateLocale( &lcid, LOCALE_ALLOW_NEUTRAL_NAMES ); + } +} + + +/****************************************************************************** + * IsValidLocaleName (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsValidLocaleName( const WCHAR *locale ) +{ + if (locale == LOCALE_NAME_USER_DEFAULT) return FALSE; + return !!find_lcname_entry( locale ); +} + + +/****************************************************************************** + * IsNLSDefinedString (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsNLSDefinedString( NLS_FUNCTION func, DWORD flags, NLSVERSIONINFO *info, + const WCHAR *str, int len ) +{ + int i; + + if (func != COMPARE_STRING) + { + SetLastError( ERROR_INVALID_FLAGS ); + return FALSE; + } + if (info) + { + if (info->dwNLSVersionInfoSize != sizeof(*info) && + (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId ))) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + } + + if (len < 0) len = lstrlenW( str ) + 1; + + for (i = 0; i < len; i++) + { + if (is_private_use_area_char( str[i] )) return FALSE; + if (IS_LOW_SURROGATE( str[i] )) return FALSE; + if (IS_HIGH_SURROGATE( str[i] )) + { + if (++i == len) return FALSE; + if (!IS_LOW_SURROGATE( str[i] )) return FALSE; + continue; + } + if (!(get_char_type( CT_CTYPE1, str[i] ) & C1_DEFINED)) return FALSE; + } + return TRUE; +} + + +/****************************************************************************** + * IsValidNLSVersion (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH IsValidNLSVersion( NLS_FUNCTION func, const WCHAR *locale, + NLSVERSIONINFOEX *info ) +{ + static const GUID GUID_NULL; + NLSVERSIONINFOEX infoex; + DWORD ret; + + if (func != COMPARE_STRING) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (info->dwNLSVersionInfoSize < sizeof(*info) && + (info->dwNLSVersionInfoSize != offsetof( NLSVERSIONINFO, dwEffectiveId ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + infoex.dwNLSVersionInfoSize = sizeof(infoex); + if (!GetNLSVersionEx( func, locale, &infoex )) return FALSE; + + ret = (infoex.dwNLSVersion & ~0xff) == (info->dwNLSVersion & ~0xff); + if (ret && !IsEqualGUID( &info->guidCustomVersion, &GUID_NULL )) + ret = find_sortguid( &info->guidCustomVersion ) != NULL; + + if (!ret) SetLastError( ERROR_SUCCESS ); + return ret; +} + + +/*********************************************************************** + * LCIDToLocaleName (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH LCIDToLocaleName( LCID lcid, WCHAR *name, INT count, DWORD flags ) +{ + const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, flags ); + + if (!locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + return get_locale_info( locale, lcid, LOCALE_SNAME, name, count ); +} + + +/*********************************************************************** + * LCMapStringEx (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH LCMapStringEx( const WCHAR *locale, DWORD flags, const WCHAR *src, int srclen, + WCHAR *dst, int dstlen, NLSVERSIONINFO *version, + void *reserved, LPARAM handle ) +{ + const struct sortguid *sortid = NULL; + + if (version) FIXME( "unsupported version structure %p\n", version ); + if (reserved) FIXME( "unsupported reserved pointer %p\n", reserved ); + if (handle) + { + static int once; + if (!once++) FIXME( "unsupported lparam %Ix\n", handle ); + } + + if (!src || !srclen || dstlen < 0) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (srclen < 0) srclen = lstrlenW(src) + 1; + + TRACE( "(%s,0x%08lx,%s,%d,%p,%d)\n", + debugstr_w(locale), flags, debugstr_wn(src, srclen), srclen, dst, dstlen ); + + flags &= ~LOCALE_USE_CP_ACP; + + if (src == dst && (flags & ~(LCMAP_LOWERCASE | LCMAP_UPPERCASE))) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + if (flags & (LCMAP_LOWERCASE | LCMAP_UPPERCASE | LCMAP_SORTKEY)) + { + if (!(sortid = get_language_sort( locale ))) return 0; + } + if (flags & LCMAP_HASH) + { + FIXME( "LCMAP_HASH %s not supported\n", debugstr_wn( src, srclen )); + return 0; + } + if (flags & LCMAP_SORTHANDLE) + { + FIXME( "LCMAP_SORTHANDLE not supported\n" ); + return 0; + } + if (flags & LCMAP_SORTKEY) return get_sortkey( sortid, flags, src, srclen, (BYTE *)dst, dstlen ); + + return lcmap_string( sortid, flags, src, srclen, dst, dstlen ); +} + + +/*********************************************************************** + * LCMapStringA (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH LCMapStringA( LCID lcid, DWORD flags, const char *src, int srclen, + char *dst, int dstlen ) +{ + WCHAR *bufW = NtCurrentTeb()->StaticUnicodeBuffer; + LPWSTR srcW, dstW; + INT ret = 0, srclenW, dstlenW; + UINT locale_cp = CP_ACP; + + if (!src || !srclen || dstlen < 0) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + locale_cp = get_lcid_codepage( lcid, flags ); + + srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, bufW, 260 ); + if (srclenW) srcW = bufW; + else + { + srclenW = MultiByteToWideChar( locale_cp, 0, src, srclen, NULL, 0 ); + srcW = HeapAlloc( GetProcessHeap(), 0, srclenW * sizeof(WCHAR) ); + if (!srcW) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return 0; + } + MultiByteToWideChar( locale_cp, 0, src, srclen, srcW, srclenW ); + } + + if (flags & LCMAP_SORTKEY) + { + if (src == dst) + { + SetLastError( ERROR_INVALID_FLAGS ); + goto done; + } + ret = LCMapStringW( lcid, flags, srcW, srclenW, (WCHAR *)dst, dstlen ); + goto done; + } + + if (flags & SORT_STRINGSORT) + { + SetLastError( ERROR_INVALID_FLAGS ); + goto done; + } + + dstlenW = LCMapStringW( lcid, flags, srcW, srclenW, NULL, 0 ); + if (!dstlenW) goto done; + + dstW = HeapAlloc( GetProcessHeap(), 0, dstlenW * sizeof(WCHAR) ); + if (!dstW) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto done; + } + LCMapStringW( lcid, flags, srcW, srclenW, dstW, dstlenW ); + ret = WideCharToMultiByte( locale_cp, 0, dstW, dstlenW, dst, dstlen, NULL, NULL ); + HeapFree( GetProcessHeap(), 0, dstW ); + +done: + if (srcW != bufW) HeapFree( GetProcessHeap(), 0, srcW ); + return ret; +} + + +/*********************************************************************** + * LCMapStringW (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH LCMapStringW( LCID lcid, DWORD flags, const WCHAR *src, int srclen, + WCHAR *dst, int dstlen ) +{ + const WCHAR *locale = LOCALE_NAME_USER_DEFAULT; + const NLS_LOCALE_LCID_INDEX *entry; + + switch (lcid) + { + case LOCALE_NEUTRAL: + case LOCALE_USER_DEFAULT: + case LOCALE_SYSTEM_DEFAULT: + case LOCALE_CUSTOM_DEFAULT: + case LOCALE_CUSTOM_UNSPECIFIED: + case LOCALE_CUSTOM_UI_DEFAULT: + break; + default: + if (lcid == user_lcid || lcid == system_lcid) break; + if (!(entry = find_lcid_entry( lcid ))) + { + WARN( "unknown locale %04lx\n", lcid ); + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + locale = locale_strings + entry->name + 1; + break; + } + + return LCMapStringEx( locale, flags, src, srclen, dst, dstlen, NULL, NULL, 0 ); +} + + +/*********************************************************************** + * LocaleNameToLCID (kernelbase.@) + */ +LCID WINAPI DECLSPEC_HOTPATCH LocaleNameToLCID( const WCHAR *name, DWORD flags ) +{ + LCID lcid; + const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid ); + + if (!locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (!(flags & LOCALE_ALLOW_NEUTRAL_NAMES) && !locale->inotneutral) + lcid = locale->idefaultlanguage; + return lcid; +} + + +/****************************************************************************** + * MultiByteToWideChar (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH MultiByteToWideChar( UINT codepage, DWORD flags, const char *src, INT srclen, + WCHAR *dst, INT dstlen ) +{ + const CPTABLEINFO *info; + int ret; + + if (!src || !srclen || (!dst && dstlen) || dstlen < 0) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (srclen < 0) srclen = strlen(src) + 1; + + switch (codepage) + { + case CP_SYMBOL: + ret = mbstowcs_cpsymbol( flags, src, srclen, dst, dstlen ); + break; + case CP_UTF7: + ret = mbstowcs_utf7( flags, src, srclen, dst, dstlen ); + break; + case CP_UNIXCP: + codepage = unix_cp; + /* fall through */ + default: + if (!(info = get_codepage_table( codepage ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (flags & ~(MB_PRECOMPOSED | MB_COMPOSITE | MB_USEGLYPHCHARS | MB_ERR_INVALID_CHARS)) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + if (info->CodePage == CP_UTF8) + ret = mbstowcs_utf8( flags, src, srclen, dst, dstlen ); + else + ret = mbstowcs_codepage( info, flags, src, srclen, dst, dstlen ); + break; + } + TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_an(src, srclen), debugstr_wn(dst, ret), ret ); + return ret; +} + + +/****************************************************************************** + * NormalizeString (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH NormalizeString(NORM_FORM form, const WCHAR *src, INT src_len, + WCHAR *dst, INT dst_len) +{ + NTSTATUS status = RtlNormalizeString( form, src, src_len, dst, &dst_len ); + + switch (status) + { + case STATUS_OBJECT_NAME_NOT_FOUND: + status = STATUS_INVALID_PARAMETER; + break; + case STATUS_BUFFER_TOO_SMALL: + case STATUS_NO_UNICODE_TRANSLATION: + dst_len = -dst_len; + break; + } + SetLastError( RtlNtStatusToDosError( status )); + return dst_len; +} + + +/****************************************************************************** + * ResolveLocaleName (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH ResolveLocaleName( LPCWSTR name, LPWSTR buffer, INT len ) +{ + LCID lcid; + UINT pos, datalen; + const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid ); + + if (!locale) + { + static const WCHAR valid[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + WCHAR *p, tmp[LOCALE_NAME_MAX_LENGTH]; + + if (wcsspn( name, valid ) < wcslen( name )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + lstrcpynW( tmp, name, LOCALE_NAME_MAX_LENGTH ); + while (!locale) + { + for (p = tmp + wcslen(tmp) - 1; p >= tmp; p--) if (*p == '-' || *p == '_') break; + if (p <= tmp) break; + *p = 0; + locale = get_locale_by_name( tmp, &lcid ); + } + } + + pos = locale ? (locale->inotneutral ? locale->sname : locale->ssortlocale) : 0; + datalen = locale_strings[pos] + 1; + + if (!len) return datalen; + lstrcpynW( buffer, locale_strings + pos + 1, len ); + if (datalen > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return datalen; +} + + +/****************************************************************************** + * SetLocaleInfoW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetLocaleInfoW( LCID lcid, LCTYPE lctype, const WCHAR *data ) +{ + WCHAR *str, tmp[80]; + + if (!data) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + switch (LOWORD(lctype)) + { + case LOCALE_ICALENDARTYPE: return set_registry_entry( &entry_icalendartype, data ); + case LOCALE_ICURRDIGITS: return set_registry_entry( &entry_icurrdigits, data ); + case LOCALE_ICURRENCY: return set_registry_entry( &entry_icurrency, data ); + case LOCALE_IDIGITS: return set_registry_entry( &entry_idigits, data ); + case LOCALE_IDIGITSUBSTITUTION: return set_registry_entry( &entry_idigitsubstitution, data ); + case LOCALE_IFIRSTDAYOFWEEK: return set_registry_entry( &entry_ifirstdayofweek, data ); + case LOCALE_IFIRSTWEEKOFYEAR: return set_registry_entry( &entry_ifirstweekofyear, data ); + case LOCALE_ILZERO: return set_registry_entry( &entry_ilzero, data ); + case LOCALE_IMEASURE: return set_registry_entry( &entry_imeasure, data ); + case LOCALE_INEGCURR: return set_registry_entry( &entry_inegcurr, data ); + case LOCALE_INEGNUMBER: return set_registry_entry( &entry_inegnumber, data ); + case LOCALE_IPAPERSIZE: return set_registry_entry( &entry_ipapersize, data ); + case LOCALE_S1159: return set_registry_entry( &entry_s1159, data ); + case LOCALE_S2359: return set_registry_entry( &entry_s2359, data ); + case LOCALE_SCURRENCY: return set_registry_entry( &entry_scurrency, data ); + case LOCALE_SDECIMAL: return set_registry_entry( &entry_sdecimal, data ); + case LOCALE_SGROUPING: return set_registry_entry( &entry_sgrouping, data ); + case LOCALE_SLIST: return set_registry_entry( &entry_slist, data ); + case LOCALE_SLONGDATE: return set_registry_entry( &entry_slongdate, data ); + case LOCALE_SMONDECIMALSEP: return set_registry_entry( &entry_smondecimalsep, data ); + case LOCALE_SMONGROUPING: return set_registry_entry( &entry_smongrouping, data ); + case LOCALE_SMONTHOUSANDSEP: return set_registry_entry( &entry_smonthousandsep, data ); + case LOCALE_SNATIVEDIGITS: return set_registry_entry( &entry_snativedigits, data ); + case LOCALE_SNEGATIVESIGN: return set_registry_entry( &entry_snegativesign, data ); + case LOCALE_SPOSITIVESIGN: return set_registry_entry( &entry_spositivesign, data ); + case LOCALE_SSHORTTIME: return set_registry_entry( &entry_sshorttime, data ); + case LOCALE_STHOUSAND: return set_registry_entry( &entry_sthousand, data ); + case LOCALE_SYEARMONTH: return set_registry_entry( &entry_syearmonth, data ); + + case LOCALE_SDATE: + if (!get_locale_info( user_locale, user_lcid, LOCALE_SSHORTDATE, tmp, ARRAY_SIZE(tmp) )) break; + data = locale_replace_separator( tmp, data ); + /* fall through */ + case LOCALE_SSHORTDATE: + if (!set_registry_entry( &entry_sshortdate, data )) return FALSE; + update_registry_value( LOCALE_IDATE, NULL, L"iDate" ); + update_registry_value( LOCALE_SDATE, NULL, L"sDate" ); + return TRUE; + + case LOCALE_STIME: + if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break; + data = locale_replace_separator( tmp, data ); + /* fall through */ + case LOCALE_STIMEFORMAT: + if (!set_registry_entry( &entry_stimeformat, data )) return FALSE; + update_registry_value( LOCALE_ITIME, NULL, L"iTime" ); + update_registry_value( LOCALE_ITIMEMARKPOSN, NULL, L"iTimePrefix" ); + update_registry_value( LOCALE_ITLZERO, NULL, L"iTLZero" ); + update_registry_value( LOCALE_STIME, NULL, L"sTime" ); + return TRUE; + + case LOCALE_ITIME: + if (!get_locale_info( user_locale, user_lcid, LOCALE_STIMEFORMAT, tmp, ARRAY_SIZE(tmp) )) break; + if (!(str = find_format( tmp, L"Hh" ))) break; + while (*str == 'h' || *str == 'H') *str++ = (*data == '0' ? 'h' : 'H'); + if (!set_registry_entry( &entry_stimeformat, tmp )) break; + update_registry_value( LOCALE_ITIME, NULL, L"iTime" ); + return TRUE; + + case LOCALE_SINTLSYMBOL: + if (!set_registry_entry( &entry_sintlsymbol, data )) return FALSE; + /* if restoring the original value, restore the original LOCALE_SCURRENCY as well */ + if (!wcsicmp( data, locale_strings + user_locale->sintlsymbol + 1 )) + data = locale_strings + user_locale->scurrency + 1; + set_registry_entry( &entry_scurrency, data ); + return TRUE; + } + SetLastError( ERROR_INVALID_FLAGS ); + return FALSE; +} + + +/*********************************************************************** + * SetCalendarInfoW (kernelbase.@) + */ +INT WINAPI /* DECLSPEC_HOTPATCH */ SetCalendarInfoW( LCID lcid, CALID calendar, CALTYPE type, const WCHAR *data ) +{ + FIXME( "(%08lx,%08lx,%08lx,%s): stub\n", lcid, calendar, type, debugstr_w(data) ); + return 0; +} + + +/*********************************************************************** + * SetProcessPreferredUILanguages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetProcessPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count ) +{ + return set_ntstatus( RtlSetProcessPreferredUILanguages( flags, buffer, count )); +} + + +/*********************************************************************** + * SetThreadPreferredUILanguages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPreferredUILanguages( DWORD flags, PCZZWSTR buffer, ULONG *count ) +{ + return set_ntstatus( RtlSetThreadPreferredUILanguages( flags, buffer, count )); +} + + +/*********************************************************************** + * SetTimeZoneInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetTimeZoneInformation( const TIME_ZONE_INFORMATION *info ) +{ + return set_ntstatus( RtlSetTimeZoneInformation( (const RTL_TIME_ZONE_INFORMATION *)info )); +} + + +/****************************************************************************** + * SetUserGeoID (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetUserGeoID( GEOID id ) +{ + const struct geo_id *geo = find_geo_id_entry( id ); + WCHAR bufferW[10]; + HKEY hkey; + + if (!geo) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (!RegCreateKeyExW( intl_key, L"Geo", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { + const WCHAR *name = geo->class == GEOCLASS_NATION ? L"Nation" : L"Region"; + swprintf( bufferW, ARRAY_SIZE(bufferW), L"%u", geo->id ); + RegSetValueExW( hkey, name, 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) ); + + if (geo->class == GEOCLASS_NATION || wcscmp( geo->iso2, L"XX" )) + lstrcpyW( bufferW, geo->iso2 ); + else + swprintf( bufferW, ARRAY_SIZE(bufferW), L"%03u", geo->uncode ); + RegSetValueExW( hkey, L"Name", 0, REG_SZ, (BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR) ); + RegCloseKey( hkey ); + } + return TRUE; +} + + +/*********************************************************************** + * SystemTimeToTzSpecificLocalTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *info, + const SYSTEMTIME *system, + SYSTEMTIME *local ) +{ + TIME_ZONE_INFORMATION tzinfo; + LARGE_INTEGER ft; + + if (!info) + { + RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo ); + info = &tzinfo; + } + + if (!SystemTimeToFileTime( system, (FILETIME *)&ft )) return FALSE; + switch (get_timezone_id( info, ft, FALSE )) + { + case TIME_ZONE_ID_UNKNOWN: + ft.QuadPart -= info->Bias * (LONGLONG)600000000; + break; + case TIME_ZONE_ID_STANDARD: + ft.QuadPart -= (info->Bias + info->StandardBias) * (LONGLONG)600000000; + break; + case TIME_ZONE_ID_DAYLIGHT: + ft.QuadPart -= (info->Bias + info->DaylightBias) * (LONGLONG)600000000; + break; + default: + return FALSE; + } + return FileTimeToSystemTime( (FILETIME *)&ft, local ); +} + + +/*********************************************************************** + * TzSpecificLocalTimeToSystemTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH TzSpecificLocalTimeToSystemTime( const TIME_ZONE_INFORMATION *info, + const SYSTEMTIME *local, + SYSTEMTIME *system ) +{ + TIME_ZONE_INFORMATION tzinfo; + LARGE_INTEGER ft; + + if (!info) + { + RtlQueryTimeZoneInformation( (RTL_TIME_ZONE_INFORMATION *)&tzinfo ); + info = &tzinfo; + } + + if (!SystemTimeToFileTime( local, (FILETIME *)&ft )) return FALSE; + switch (get_timezone_id( info, ft, TRUE )) + { + case TIME_ZONE_ID_UNKNOWN: + ft.QuadPart += info->Bias * (LONGLONG)600000000; + break; + case TIME_ZONE_ID_STANDARD: + ft.QuadPart += (info->Bias + info->StandardBias) * (LONGLONG)600000000; + break; + case TIME_ZONE_ID_DAYLIGHT: + ft.QuadPart += (info->Bias + info->DaylightBias) * (LONGLONG)600000000; + break; + default: + return FALSE; + } + return FileTimeToSystemTime( (FILETIME *)&ft, system ); +} + + +/*********************************************************************** + * VerLanguageNameA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameA( DWORD lang, LPSTR buffer, DWORD size ) +{ + return GetLocaleInfoA( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size ); +} + + +/*********************************************************************** + * VerLanguageNameW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH VerLanguageNameW( DWORD lang, LPWSTR buffer, DWORD size ) +{ + return GetLocaleInfoW( MAKELCID( lang, SORT_DEFAULT ), LOCALE_SENGLANGUAGE, buffer, size ); +} + + +/*********************************************************************** + * WideCharToMultiByte (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH WideCharToMultiByte( UINT codepage, DWORD flags, LPCWSTR src, INT srclen, + LPSTR dst, INT dstlen, LPCSTR defchar, BOOL *used ) +{ + const CPTABLEINFO *info; + int ret; + + if (!src || !srclen || (!dst && dstlen) || dstlen < 0) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (srclen < 0) srclen = lstrlenW(src) + 1; + + switch (codepage) + { + case CP_SYMBOL: + ret = wcstombs_cpsymbol( flags, src, srclen, dst, dstlen, defchar, used ); + break; + case CP_UTF7: + ret = wcstombs_utf7( flags, src, srclen, dst, dstlen, defchar, used ); + break; + case CP_UNIXCP: + codepage = unix_cp; + /* fall through */ + default: + if (!(info = get_codepage_table( codepage ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (flags & ~(WC_DISCARDNS | WC_SEPCHARS | WC_DEFAULTCHAR | WC_ERR_INVALID_CHARS | + WC_COMPOSITECHECK | WC_NO_BEST_FIT_CHARS)) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + if (info->CodePage == CP_UTF8) + ret = wcstombs_utf8( flags, src, srclen, dst, dstlen, defchar, used ); + else + ret = wcstombs_codepage( info, flags, src, srclen, dst, dstlen, defchar, used ); + break; + } + TRACE( "cp %d %s -> %s, ret = %d\n", codepage, debugstr_wn(src, srclen), debugstr_an(dst, ret), ret ); + return ret; +} + + +/*********************************************************************** + * GetUserDefaultGeoName (kernelbase.@) + */ +INT WINAPI GetUserDefaultGeoName(LPWSTR geo_name, int count) +{ + WCHAR buffer[32]; + LSTATUS status; + DWORD size; + HKEY key; + + TRACE( "geo_name %p, count %d.\n", geo_name, count ); + + if (count && !geo_name) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (!(status = RegOpenKeyExW( intl_key, L"Geo", 0, KEY_ALL_ACCESS, &key ))) + { + size = sizeof(buffer); + status = RegQueryValueExW( key, L"Name", NULL, NULL, (BYTE *)buffer, &size ); + RegCloseKey( key ); + } + if (status) + { + const struct geo_id *geo = find_geo_id_entry( GetUserGeoID( GEOCLASS_NATION )); + if (geo && geo->id != 39070) + lstrcpyW( buffer, geo->iso2 ); + else + lstrcpyW( buffer, L"001" ); + } + size = lstrlenW( buffer ) + 1; + if (count < size) + { + if (!count) + return size; + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + lstrcpyW( geo_name, buffer ); + return size; +} + + +/*********************************************************************** + * SetUserDefaultGeoName (kernelbase.@) + */ +BOOL WINAPI SetUserGeoName(PWSTR geo_name) +{ + const struct geo_id *geo; + + TRACE( "geo_name %s.\n", debugstr_w( geo_name )); + + if (!geo_name || !(geo = find_geo_name_entry( geo_name ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return SetUserGeoID( geo->id ); +} + + +static void grouping_to_string( UINT grouping, WCHAR *buffer ) +{ + UINT last_digit = grouping % 10; + WCHAR tmp[10], *p = tmp; + + /* The string is confusingly different when it comes to repetitions (trailing zeros). For a string, + * a 0 signals that the format needs to be repeated, which is the opposite of the grouping integer. */ + if (last_digit == 0) + { + grouping /= 10; + + /* Special case: two or more trailing zeros result in zero-sided groupings, with no repeats */ + if (grouping % 10 == 0) + last_digit = ~0; + } + + while (grouping) + { + *p++ = '0' + grouping % 10; + grouping /= 10; + } + while (p > tmp) + { + *buffer++ = *(--p); + if (p > tmp) *buffer++ = ';'; + } + if (last_digit != 0) + { + *buffer++ = ';'; + *buffer++ = '0'; + if (last_digit == ~0) + { + /* Add another trailing zero due to the weird way trailing zeros work in grouping string */ + *buffer++ = ';'; + *buffer++ = '0'; + } + } + *buffer = 0; +} + + +static WCHAR *prepend_str( WCHAR *end, const WCHAR *str ) +{ + UINT len = wcslen( str ); + return memcpy( end - len, str, len * sizeof(WCHAR) ); +} + + +/* format a positive number with decimal part; helper for get_number_format */ +static WCHAR *format_number( WCHAR *end, const WCHAR *value, const WCHAR *decimal_sep, + const WCHAR *thousand_sep, const WCHAR *grouping, UINT digits, BOOL lzero ) +{ + BOOL round = FALSE, repeat = FALSE; + UINT i, len = 0, prev = ~0; + const WCHAR *frac = NULL; + + *(--end) = 0; + + for (i = 0; value[i]; i++) + { + if (value[i] >= '0' && value[i] <= '9') continue; + if (value[i] != '.') return NULL; + if (frac) return NULL; + frac = value + i + 1; + } + + /* format fractional part */ + + len = frac ? wcslen( frac ) : 0; + + if (len > digits) + { + round = frac[digits] >= '5'; + len = digits; + } + while (digits > len) + { + (*--end) = '0'; + digits--; + } + while (len) + { + WCHAR ch = frac[--len]; + if (round) + { + if (ch != '9') + { + ch++; + round = FALSE; + } + else ch = '0'; + } + *(--end) = ch; + } + if (*end) end = prepend_str( end, decimal_sep ); + + /* format integer part */ + + len = frac ? frac - value - 1 : wcslen( value ); + + while (len && *value == '0') + { + value++; + len--; + } + if (len) lzero = FALSE; + + /* leading 0s are ignored */ + while (grouping[0] == '0' && grouping[1] == ';') + grouping += 2; + + while (len) + { + UINT limit = prev; + + if (!repeat) + { + limit = *grouping - '0'; + if (grouping[1] == ';') + { + grouping += 2; + if (limit) + prev = limit; + else + { + /* Trailing 0;0 is a special case */ + prev = ~0; + if (grouping[0] == '0' && grouping[1] != ';') + { + repeat = TRUE; + limit = prev; + } + } + } + else + { + repeat = TRUE; + if (!limit) + limit = prev; + else + prev = ~0; + } + } + + while (len && limit--) + { + WCHAR ch = value[--len]; + if (round) + { + if (ch != '9') + { + ch++; + round = FALSE; + } + else ch = '0'; + } + *(--end) = ch; + } + if (len) end = prepend_str( end, thousand_sep ); + } + if (round) *(--end) = '1'; + else if (lzero) *(--end) = '0'; + return end; +} + + +static int get_number_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value, + const NUMBERFMTW *format, WCHAR *buffer, int len ) +{ + WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_neg[5], grouping[24], output[256]; + const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand; + DWORD digits, lzero, order; + int ret = 0; + BOOL negative = (*value == '-'); + + flags &= LOCALE_NOUSEROVERRIDE; + + if (!format) + { + get_locale_info( locale, 0, LOCALE_SGROUPING | flags, grouping, ARRAY_SIZE(grouping) ); + get_locale_info( locale, 0, LOCALE_SDECIMAL | flags, fmt_decimal, ARRAY_SIZE(fmt_decimal) ); + get_locale_info( locale, 0, LOCALE_STHOUSAND | flags, fmt_thousand, ARRAY_SIZE(fmt_thousand) ); + get_locale_info( locale, 0, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER | flags, + (WCHAR *)&digits, sizeof(DWORD)/sizeof(WCHAR) ); + get_locale_info( locale, 0, LOCALE_ILZERO | LOCALE_RETURN_NUMBER | flags, + (WCHAR *)&lzero, sizeof(DWORD)/sizeof(WCHAR) ); + get_locale_info( locale, 0, LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER | flags, + (WCHAR *)&order, sizeof(DWORD)/sizeof(WCHAR) ); + } + else + { + if (flags) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + decimal_sep = format->lpDecimalSep; + thousand_sep = format->lpThousandSep; + grouping_to_string( format->Grouping, grouping ); + digits = format->NumDigits; + lzero = format->LeadingZero; + order = format->NegativeOrder; + if (!decimal_sep || !thousand_sep) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + } + + if (negative) + { + value++; + get_locale_info( locale, 0, LOCALE_SNEGATIVESIGN | flags, fmt_neg, ARRAY_SIZE(fmt_neg) ); + } + + if (!(num = format_number( output + ARRAY_SIZE(output) - 6, value, + decimal_sep, thousand_sep, grouping, digits, lzero ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (negative) + { + switch (order) + { + case 0: /* (1.1) */ + num = prepend_str( num, L"(" ); + wcscat( num, L")" ); + break; + case 2: /* - 1.1 */ + num = prepend_str( num, L" " ); + /* fall through */ + case 1: /* -1.1 */ + num = prepend_str( num, fmt_neg ); + break; + case 4: /* 1.1 - */ + wcscat( num, L" " ); + /* fall through */ + case 3: /* 1.1- */ + wcscat( num, fmt_neg ); + break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + } + + ret = wcslen( num ) + 1; + if (!len) return ret; + lstrcpynW( buffer, num, len ); + if (ret > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return ret; +} + + +static int get_currency_format( const NLS_LOCALE_DATA *locale, DWORD flags, const WCHAR *value, + const CURRENCYFMTW *format, WCHAR *buffer, int len ) +{ + WCHAR *num, fmt_decimal[4], fmt_thousand[4], fmt_symbol[13], fmt_neg[5], grouping[20], output[256]; + const WCHAR *decimal_sep = fmt_decimal, *thousand_sep = fmt_thousand, *symbol = fmt_symbol; + DWORD digits, lzero, pos_order, neg_order; + int ret = 0; + BOOL negative = (*value == '-'); + + flags &= LOCALE_NOUSEROVERRIDE; + + if (!format) + { + get_locale_info( locale, 0, LOCALE_SCURRENCY | flags, fmt_symbol, ARRAY_SIZE(fmt_symbol) ); + get_locale_info( locale, 0, LOCALE_SMONGROUPING | flags, grouping, ARRAY_SIZE(grouping) ); + get_locale_info( locale, 0, LOCALE_SMONDECIMALSEP | flags, fmt_decimal, ARRAY_SIZE(fmt_decimal) ); + get_locale_info( locale, 0, LOCALE_SMONTHOUSANDSEP | flags, fmt_thousand, ARRAY_SIZE(fmt_thousand) ); + get_locale_info( locale, 0, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER | flags, + (WCHAR *)&digits, sizeof(DWORD)/sizeof(WCHAR) ); + get_locale_info( locale, 0, LOCALE_ILZERO | LOCALE_RETURN_NUMBER | flags, + (WCHAR *)&lzero, sizeof(DWORD)/sizeof(WCHAR) ); + get_locale_info( locale, 0, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER | flags, + (WCHAR *)&pos_order, sizeof(DWORD)/sizeof(WCHAR) ); + get_locale_info( locale, 0, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER | flags, + (WCHAR *)&neg_order, sizeof(DWORD)/sizeof(WCHAR) ); + } + else + { + if (flags) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + decimal_sep = format->lpDecimalSep; + thousand_sep = format->lpThousandSep; + symbol = format->lpCurrencySymbol; + grouping_to_string( format->Grouping, grouping ); + digits = format->NumDigits; + lzero = format->LeadingZero; + pos_order = format->PositiveOrder; + neg_order = format->NegativeOrder; + if (!decimal_sep || !thousand_sep || !symbol) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + } + + if (negative) + { + value++; + get_locale_info( locale, 0, LOCALE_SNEGATIVESIGN | flags, fmt_neg, ARRAY_SIZE(fmt_neg) ); + } + + if (!(num = format_number( output + ARRAY_SIZE(output) - 20, value, + decimal_sep, thousand_sep, grouping, digits, lzero ))) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (negative) + { + switch (neg_order) + { + case 14: /* ($ 1.1) */ + num = prepend_str( num, L" " ); + /* fall through */ + case 0: /* ($1.1) */ + num = prepend_str( num, symbol ); + num = prepend_str( num, L"(" ); + wcscat( num, L")" ); + break; + case 9: /* -$ 1.1 */ + num = prepend_str( num, L" " ); + /* fall through */ + case 1: /* -$1.1 */ + num = prepend_str( num, symbol ); + num = prepend_str( num, fmt_neg ); + break; + case 2: /* $-1.1 */ + num = prepend_str( num, fmt_neg ); + num = prepend_str( num, symbol ); + break; + case 11: /* $ 1.1- */ + num = prepend_str( num, L" " ); + /* fall through */ + case 3: /* $1.1- */ + num = prepend_str( num, symbol ); + wcscat( num, fmt_neg ); + break; + case 15: /* (1.1 $) */ + wcscat( num, L" " ); + /* fall through */ + case 4: /* (1.1$) */ + wcscat( num, symbol ); + num = prepend_str( num, L"(" ); + wcscat( num, L")" ); + break; + case 8: /* -1.1 $ */ + wcscat( num, L" " ); + /* fall through */ + case 5: /* -1.1$ */ + num = prepend_str( num, fmt_neg ); + wcscat( num, symbol ); + break; + case 6: /* 1.1-$ */ + wcscat( num, fmt_neg ); + wcscat( num, symbol ); + break; + case 10: /* 1.1 $- */ + wcscat( num, L" " ); + /* fall through */ + case 7: /* 1.1$- */ + wcscat( num, symbol ); + wcscat( num, fmt_neg ); + break; + case 12: /* $ -1.1 */ + num = prepend_str( num, fmt_neg ); + num = prepend_str( num, L" " ); + num = prepend_str( num, symbol ); + break; + case 13: /* 1.1- $ */ + wcscat( num, fmt_neg ); + wcscat( num, L" " ); + wcscat( num, symbol ); + break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + } + else + { + switch (pos_order) + { + case 2: /* $ 1.1 */ + num = prepend_str( num, L" " ); + /* fall through */ + case 0: /* $1.1 */ + num = prepend_str( num, symbol ); + break; + case 3: /* 1.1 $ */ + wcscat( num, L" " ); + /* fall through */ + case 1: /* 1.1$ */ + wcscat( num, symbol ); + break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + } + + ret = wcslen( num ) + 1; + if (!len) return ret; + lstrcpynW( buffer, num, len ); + if (ret > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return ret; +} + + +/* get the length of a date/time formatting pattern */ +static int get_pattern_len( const WCHAR *pattern, const WCHAR *accept ) +{ + int i; + + if (*pattern == '\'') + { + for (i = 1; pattern[i]; i++) + { + if (pattern[i] != '\'') continue; + if (pattern[++i] != '\'') return i; + } + return i; + } + if (!wcschr( accept, *pattern )) return 1; + for (i = 1; pattern[i]; i++) if (pattern[i] != pattern[0]) break; + return i; +} + + +static int get_date_format( const NLS_LOCALE_DATA *locale, DWORD flags, const SYSTEMTIME *systime, + const WCHAR *format, WCHAR *buffer, int len ) +{ + DWORD override = flags & LOCALE_NOUSEROVERRIDE; + DWORD genitive = 0; + WCHAR *p, fmt[80], output[256]; + SYSTEMTIME time; + int ret, val, count, i; + + if (!format) + { + if (flags & DATE_USE_ALT_CALENDAR) FIXME( "alt calendar not supported\n" ); + switch (flags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH | DATE_MONTHDAY)) + { + case 0: + case DATE_SHORTDATE: + get_locale_info( locale, 0, LOCALE_SSHORTDATE | override, fmt, ARRAY_SIZE(fmt) ); + break; + case DATE_LONGDATE: + get_locale_info( locale, 0, LOCALE_SLONGDATE | override, fmt, ARRAY_SIZE(fmt) ); + break; + case DATE_YEARMONTH: + get_locale_info( locale, 0, LOCALE_SYEARMONTH | override, fmt, ARRAY_SIZE(fmt) ); + break; + case DATE_MONTHDAY: + get_locale_info( locale, 0, LOCALE_SMONTHDAY | override, fmt, ARRAY_SIZE(fmt) ); + break; + default: + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + format = fmt; + } + else if (override || (flags & (DATE_SHORTDATE | DATE_LONGDATE | DATE_YEARMONTH | DATE_MONTHDAY))) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + + if (systime) + { + FILETIME ft; + + time = *systime; + time.wHour = time.wMinute = time.wSecond = time.wMilliseconds = 0; + if (!SystemTimeToFileTime( &time, &ft ) || !FileTimeToSystemTime( &ft, &time )) return 0; + } + else GetLocalTime( &time ); + + for (p = output; *format; format += count) + { + count = get_pattern_len( format, L"yMd" ); + + switch (*format) + { + case '\'': + for (i = 1; i < count; i++) + { + if (format[i] == '\'') i++; + if (i < count) *p++ = format[i]; + } + break; + + case 'y': + p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%02u", + (count <= 2) ? time.wYear % 100 : time.wYear ); + break; + + case 'M': + if (count <= 2) + { + p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", count, time.wMonth ); + break; + } + val = (count == 3 ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1) + time.wMonth - 1; + if (!genitive) + { + for (i = count; format[i]; i += get_pattern_len( format + i, L"yMd" )) + { + if (format[i] != 'd') continue; + if (format[i + 1] != 'd' || format[i + 2] != 'd') + genitive = LOCALE_RETURN_GENITIVE_NAMES; + break; + } + } + p += get_locale_info( locale, 0, val | override | genitive, + p, output + ARRAY_SIZE(output) - p ) - 1; + break; + + case 'd': + if (count <= 2) + { + genitive = LOCALE_RETURN_GENITIVE_NAMES; + p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", count, time.wDay ); + break; + } + genitive = 0; + val = (count == 3 ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1) + (time.wDayOfWeek + 6) % 7; + p += get_locale_info( locale, 0, val | override, p, output + ARRAY_SIZE(output) - p ) - 1; + break; + + case 'g': + p += locale_return_string( count >= 2 ? locale->serastring : locale->sabbreverastring, + override, p, output + ARRAY_SIZE(output) - p ) - 1; + break; + + default: + *p++ = *format; + break; + } + } + *p++ = 0; + ret = p - output; + + if (!len) return ret; + lstrcpynW( buffer, output, len ); + if (ret > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return ret; +} + + +static int get_time_format( const NLS_LOCALE_DATA *locale, DWORD flags, const SYSTEMTIME *systime, + const WCHAR *format, WCHAR *buffer, int len ) +{ + DWORD override = flags & LOCALE_NOUSEROVERRIDE; + WCHAR *p, *last, fmt[80], output[256]; + SYSTEMTIME time; + int i, ret, val, count; + BOOL skip = FALSE; + + if (!format) + { + get_locale_info( locale, 0, LOCALE_STIMEFORMAT | override, fmt, ARRAY_SIZE(fmt) ); + format = fmt; + } + else if (override) + { + SetLastError( ERROR_INVALID_FLAGS ); + return 0; + } + + if (systime) + { + time = *systime; + if (time.wMilliseconds > 999 || time.wSecond > 59 || time.wMinute > 59 || time.wHour > 23) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + } + else GetLocalTime( &time ); + + for (p = last = output; *format; format += count) + { + count = get_pattern_len( format, L"Hhmst" ); + + switch (*format) + { + case '\'': + for (i = 1; i < count; i++) + { + if (format[i] == '\'') i++; + if (!skip && i < count) *p++ = format[i]; + } + continue; + + case 'H': + val = time.wHour; + break; + + case 'h': + val = time.wHour; + if (!(flags & TIME_FORCE24HOURFORMAT)) + { + val %= 12; + if (!val) val = 12; + } + break; + + case 'm': + if (flags & TIME_NOMINUTESORSECONDS) + { + p = last; + skip = TRUE; + continue; + } + val = time.wMinute; + break; + + case 's': + if (flags & (TIME_NOMINUTESORSECONDS | TIME_NOSECONDS)) + { + p = last; + skip = TRUE; + continue; + } + val = time.wSecond; + break; + + case 't': + if (flags & TIME_NOTIMEMARKER) + { + p = last; + skip = TRUE; + continue; + } + val = time.wHour < 12 ? LOCALE_S1159 : LOCALE_S2359; + ret = get_locale_info( locale, 0, val | override, p, output + ARRAY_SIZE(output) - p ); + p += (count > 1) ? ret - 1 : 1; + skip = FALSE; + continue; + + default: + if (!skip || *format == ' ') *p++ = *format; + continue; + } + + p += swprintf( p, output + ARRAY_SIZE(output) - p, L"%.*u", min( 2, count ), val ); + last = p; + skip = FALSE; + } + *p++ = 0; + ret = p - output; + + if (!len) return ret; + lstrcpynW( buffer, output, len ); + if (ret > len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + return ret; +} + + +/************************************************************************** + * GetNumberFormatW (kernelbase.@) + */ +int WINAPI GetNumberFormatW( LCID lcid, DWORD flags, const WCHAR *value, + const NUMBERFMTW *format, WCHAR *buffer, int len ) +{ + const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 ); + + if (len < 0 || (len && !buffer) || !value || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(%04lx,%lx,%s,%p,%p,%d)\n", lcid, flags, debugstr_w(value), format, buffer, len ); + return get_number_format( locale, flags, value, format, buffer, len ); +} + + +/************************************************************************** + * GetNumberFormatEx (kernelbase.@) + */ +int WINAPI GetNumberFormatEx( const WCHAR *name, DWORD flags, const WCHAR *value, + const NUMBERFMTW *format, WCHAR *buffer, int len ) +{ + LCID lcid; + const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid ); + + if (len < 0 || (len && !buffer) || !value || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(%s,%lx,%s,%p,%p,%d)\n", debugstr_w(name), flags, debugstr_w(value), format, buffer, len ); + return get_number_format( locale, flags, value, format, buffer, len ); +} + + +/*********************************************************************** + * GetCurrencyFormatW (kernelbase.@) + */ +int WINAPI GetCurrencyFormatW( LCID lcid, DWORD flags, const WCHAR *value, + const CURRENCYFMTW *format, WCHAR *buffer, int len ) +{ + const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 ); + + if (len < 0 || (len && !buffer) || !value || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(%04lx,%lx,%s,%p,%p,%d)\n", lcid, flags, debugstr_w(value), format, buffer, len ); + return get_currency_format( locale, flags, value, format, buffer, len ); +} + + +/*********************************************************************** + * GetCurrencyFormatEx (kernelbase.@) + */ +int WINAPI GetCurrencyFormatEx( const WCHAR *name, DWORD flags, const WCHAR *value, + const CURRENCYFMTW *format, WCHAR *buffer, int len ) +{ + LCID lcid; + const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid ); + + if (len < 0 || (len && !buffer) || !value || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(%s,%lx,%s,%p,%p,%d)\n", debugstr_w(name), flags, debugstr_w(value), format, buffer, len ); + return get_currency_format( locale, flags, value, format, buffer, len ); +} + + +/****************************************************************************** + * GetDateFormatA (KERNEL32.@) + */ +int WINAPI GetDateFormatA( LCID lcid, DWORD flags, const SYSTEMTIME *time, + const char *format, char *buffer, int len ) +{ + UINT cp = get_lcid_codepage( lcid, flags ); + WCHAR formatW[128], output[128]; + int ret; + + TRACE( "(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, flags, time, debugstr_a(format), buffer, len ); + + if (len < 0 || (len && !buffer)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (format) + { + MultiByteToWideChar( cp, 0, format, -1, formatW, ARRAY_SIZE(formatW) ); + ret = GetDateFormatW( lcid, flags, time, formatW, output, ARRAY_SIZE(output) ); + } + else ret = GetDateFormatW( lcid, flags, time, NULL, output, ARRAY_SIZE(output) ); + + if (ret) ret = WideCharToMultiByte( cp, 0, output, -1, buffer, len, 0, 0 ); + return ret; +} + + +/*********************************************************************** + * GetDateFormatW (kernelbase.@) + */ +int WINAPI GetDateFormatW( LCID lcid, DWORD flags, const SYSTEMTIME *systime, + const WCHAR *format, WCHAR *buffer, int len ) +{ + const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 ); + + if (len < 0 || (len && !buffer) || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(%04lx,%lx,%p,%s,%p,%d)\n", lcid, flags, systime, debugstr_w(format), buffer, len ); + return get_date_format( locale, flags, systime, format, buffer, len ); +} + + +/*********************************************************************** + * GetDateFormatEx (kernelbase.@) + */ +int WINAPI GetDateFormatEx( const WCHAR *name, DWORD flags, const SYSTEMTIME *systime, + const WCHAR *format, WCHAR *buffer, int len, const WCHAR *calendar ) +{ + LCID lcid; + const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid ); + + if (len < 0 || (len && !buffer) || !locale || calendar) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(%s,%lx,%p,%s,%p,%d)\n", debugstr_w(name), flags, systime, debugstr_w(format), buffer, len ); + return get_date_format( locale, flags, systime, format, buffer, len ); +} + + +/****************************************************************************** + * GetTimeFormatA (kernelbase.@) + */ +int WINAPI GetTimeFormatA( LCID lcid, DWORD flags, const SYSTEMTIME *time, + const char *format, char *buffer, int len ) +{ + UINT cp = get_lcid_codepage( lcid, flags ); + WCHAR formatW[128], output[128]; + int ret; + + TRACE( "(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, flags, time, debugstr_a(format), buffer, len ); + + if (len < 0 || (len && !buffer)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + if (format) + { + MultiByteToWideChar( cp, 0, format, -1, formatW, ARRAY_SIZE(formatW) ); + ret = GetTimeFormatW( lcid, flags, time, formatW, output, ARRAY_SIZE(output) ); + } + else ret = GetTimeFormatW( lcid, flags, time, NULL, output, ARRAY_SIZE(output) ); + + if (ret) ret = WideCharToMultiByte( cp, 0, output, -1, buffer, len, 0, 0 ); + return ret; +} + + +/*********************************************************************** + * GetTimeFormatW (kernelbase.@) + */ +int WINAPI GetTimeFormatW( LCID lcid, DWORD flags, const SYSTEMTIME *systime, + const WCHAR *format, WCHAR *buffer, int len ) +{ + const NLS_LOCALE_DATA *locale = NlsValidateLocale( &lcid, 0 ); + + if (len < 0 || (len && !buffer) || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(%04lx,%lx,%p,%s,%p,%d)\n", lcid, flags, systime, debugstr_w(format), buffer, len ); + return get_time_format( locale, flags, systime, format, buffer, len ); +} + + +/*********************************************************************** + * GetTimeFormatEx (kernelbase.@) + */ +int WINAPI GetTimeFormatEx( const WCHAR *name, DWORD flags, const SYSTEMTIME *systime, + const WCHAR *format, WCHAR *buffer, int len ) +{ + LCID lcid; + const NLS_LOCALE_DATA *locale = get_locale_by_name( name, &lcid ); + + if (len < 0 || (len && !buffer) || !locale) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + TRACE( "(%s,%lx,%p,%s,%p,%d)\n", debugstr_w(name), flags, systime, debugstr_w(format), buffer, len ); + return get_time_format( locale, flags, systime, format, buffer, len ); +} diff --git a/dll/win32/KernelBase/wine/main.c b/dll/win32/KernelBase/wine/main.c new file mode 100644 index 0000000000000..60173ba651300 --- /dev/null +++ b/dll/win32/KernelBase/wine/main.c @@ -0,0 +1,572 @@ +/* + * Copyright 2016 Michael Müller + * Copyright 2017 Andrey Gusev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windows.h" +#include "appmodel.h" +#include "shlwapi.h" +#include "perflib.h" +#include "winternl.h" + +#include "wine/debug.h" +#include "kernelbase.h" +#include "wine/heap.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(kernelbase); + + +BOOL is_wow64 = FALSE; + +/*********************************************************************** + * DllMain + */ +BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls( hinst ); + IsWow64Process( GetCurrentProcess(), &is_wow64 ); + init_global_data(); + init_locale( hinst ); + init_startup_info( NtCurrentTeb()->Peb->ProcessParameters ); + init_console(); + } + return TRUE; +} + + +/*********************************************************************** + * MulDiv (kernelbase.@) + */ +INT WINAPI MulDiv( INT a, INT b, INT c ) +{ + LONGLONG ret; + + if (!c) return -1; + + /* We want to deal with a positive divisor to simplify the logic. */ + if (c < 0) + { + a = -a; + c = -c; + } + + /* If the result is positive, we "add" to round. else, we subtract to round. */ + if ((a < 0 && b < 0) || (a >= 0 && b >= 0)) + ret = (((LONGLONG)a * b) + (c / 2)) / c; + else + ret = (((LONGLONG)a * b) - (c / 2)) / c; + + if (ret > 2147483647 || ret < -2147483647) return -1; + return ret; +} + +/*********************************************************************** + * AppPolicyGetMediaFoundationCodecLoading (KERNELBASE.@) + */ + +LONG WINAPI AppPolicyGetMediaFoundationCodecLoading(HANDLE token, AppPolicyMediaFoundationCodecLoading *policy) +{ + FIXME("%p, %p\n", token, policy); + + if(policy) + *policy = AppPolicyMediaFoundationCodecLoading_All; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * AppPolicyGetProcessTerminationMethod (KERNELBASE.@) + */ +LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessTerminationMethod *policy) +{ + FIXME("%p, %p\n", token, policy); + + if(policy) + *policy = AppPolicyProcessTerminationMethod_ExitProcess; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * AppPolicyGetThreadInitializationType (KERNELBASE.@) + */ +LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy) +{ + FIXME("%p, %p\n", token, policy); + + if(policy) + *policy = AppPolicyThreadInitializationType_None; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * AppPolicyGetShowDeveloperDiagnostic (KERNELBASE.@) + */ +LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy) +{ + FIXME("%p, %p\n", token, policy); + + if(policy) + *policy = AppPolicyShowDeveloperDiagnostic_ShowUI; + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * AppPolicyGetWindowingModel (KERNELBASE.@) + */ +LONG WINAPI AppPolicyGetWindowingModel(HANDLE token, AppPolicyWindowingModel *policy) +{ + FIXME("%p, %p\n", token, policy); + + if(policy) + *policy = AppPolicyWindowingModel_ClassicDesktop; + + return ERROR_SUCCESS; +} + +struct counterset_template +{ + PERF_COUNTERSET_INFO counterset; + PERF_COUNTER_INFO counter[1]; +}; + +struct counterset_instance +{ + struct list entry; + struct counterset_template *template; + PERF_COUNTERSET_INSTANCE instance; +}; + +struct perf_provider +{ + GUID guid; + PERFLIBREQUEST callback; + struct counterset_template **countersets; + unsigned int counterset_count; + + struct list instance_list; +}; + +static struct perf_provider *perf_provider_from_handle(HANDLE prov) +{ + return (struct perf_provider *)prov; +} + +/*********************************************************************** + * PerfCreateInstance (KERNELBASE.@) + */ +PERF_COUNTERSET_INSTANCE WINAPI *PerfCreateInstance( HANDLE handle, const GUID *guid, + const WCHAR *name, ULONG id ) +{ + struct perf_provider *prov = perf_provider_from_handle( handle ); + struct counterset_template *template; + struct counterset_instance *inst; + unsigned int i; + ULONG size; + + FIXME( "handle %p, guid %s, name %s, id %lu semi-stub.\n", handle, debugstr_guid(guid), debugstr_w(name), id ); + + if (!prov || !guid || !name) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + + for (i = 0; i < prov->counterset_count; ++i) + if (IsEqualGUID(guid, &prov->countersets[i]->counterset.CounterSetGuid)) break; + + if (i == prov->counterset_count) + { + SetLastError( ERROR_NOT_FOUND ); + return NULL; + } + + template = prov->countersets[i]; + + LIST_FOR_EACH_ENTRY(inst, &prov->instance_list, struct counterset_instance, entry) + { + if (inst->template == template && inst->instance.InstanceId == id) + { + SetLastError( ERROR_ALREADY_EXISTS ); + return NULL; + } + } + + size = (sizeof(PERF_COUNTERSET_INSTANCE) + template->counterset.NumCounters * sizeof(UINT64) + + (lstrlenW( name ) + 1) * sizeof(WCHAR) + 7) & ~7; + inst = heap_alloc_zero( offsetof(struct counterset_instance, instance) + size ); + if (!inst) + { + SetLastError( ERROR_OUTOFMEMORY ); + return NULL; + } + + inst->template = template; + inst->instance.CounterSetGuid = *guid; + inst->instance.dwSize = size; + inst->instance.InstanceId = id; + inst->instance.InstanceNameOffset = sizeof(PERF_COUNTERSET_INSTANCE) + + template->counterset.NumCounters * sizeof(UINT64); + inst->instance.InstanceNameSize = (lstrlenW( name ) + 1) * sizeof(WCHAR); + memcpy( (BYTE *)&inst->instance + inst->instance.InstanceNameOffset, name, inst->instance.InstanceNameSize ); + list_add_tail( &prov->instance_list, &inst->entry ); + + return &inst->instance; +} + +/*********************************************************************** + * PerfDeleteInstance (KERNELBASE.@) + */ +ULONG WINAPI PerfDeleteInstance(HANDLE provider, PERF_COUNTERSET_INSTANCE *block) +{ + struct perf_provider *prov = perf_provider_from_handle( provider ); + struct counterset_instance *inst; + + TRACE( "provider %p, block %p.\n", provider, block ); + + if (!prov || !block) return ERROR_INVALID_PARAMETER; + + inst = CONTAINING_RECORD(block, struct counterset_instance, instance); + list_remove( &inst->entry ); + heap_free( inst ); + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * PerfSetCounterSetInfo (KERNELBASE.@) + */ +ULONG WINAPI PerfSetCounterSetInfo( HANDLE handle, PERF_COUNTERSET_INFO *template, ULONG size ) +{ + struct perf_provider *prov = perf_provider_from_handle( handle ); + struct counterset_template **new_array; + struct counterset_template *new; + unsigned int i; + + FIXME( "handle %p, template %p, size %lu semi-stub.\n", handle, template, size ); + + if (!prov || !template) return ERROR_INVALID_PARAMETER; + if (!template->NumCounters) return ERROR_INVALID_PARAMETER; + if (size < sizeof(*template) || (size - (sizeof(*template))) / sizeof(PERF_COUNTER_INFO) < template->NumCounters) + return ERROR_INVALID_PARAMETER; + + for (i = 0; i < prov->counterset_count; ++i) + { + if (IsEqualGUID( &template->CounterSetGuid, &prov->countersets[i]->counterset.CounterSetGuid )) + return ERROR_ALREADY_EXISTS; + } + + size = offsetof( struct counterset_template, counter[template->NumCounters] ); + if (!(new = heap_alloc( size ))) return ERROR_OUTOFMEMORY; + + if (prov->counterset_count) + new_array = heap_realloc( prov->countersets, sizeof(*prov->countersets) * (prov->counterset_count + 1) ); + else + new_array = heap_alloc( sizeof(*prov->countersets) ); + + if (!new_array) + { + heap_free( new ); + return ERROR_OUTOFMEMORY; + } + memcpy( new, template, size ); + for (i = 0; i < template->NumCounters; ++i) + new->counter[i].Offset = i * sizeof(UINT64); + new_array[prov->counterset_count++] = new; + prov->countersets = new_array; + + return STATUS_SUCCESS; +} + +/*********************************************************************** + * PerfSetCounterRefValue (KERNELBASE.@) + */ +ULONG WINAPI PerfSetCounterRefValue(HANDLE provider, PERF_COUNTERSET_INSTANCE *instance, + ULONG counterid, void *address) +{ + struct perf_provider *prov = perf_provider_from_handle( provider ); + struct counterset_template *template; + struct counterset_instance *inst; + unsigned int i; + + FIXME( "provider %p, instance %p, counterid %lu, address %p semi-stub.\n", + provider, instance, counterid, address ); + + if (!prov || !instance || !address) return ERROR_INVALID_PARAMETER; + + inst = CONTAINING_RECORD(instance, struct counterset_instance, instance); + template = inst->template; + + for (i = 0; i < template->counterset.NumCounters; ++i) + if (template->counter[i].CounterId == counterid) break; + + if (i == template->counterset.NumCounters) return ERROR_NOT_FOUND; + *(void **)((BYTE *)&inst->instance + sizeof(PERF_COUNTERSET_INSTANCE) + template->counter[i].Offset) = address; + + return STATUS_SUCCESS; +} + +/*********************************************************************** + * PerfStartProvider (KERNELBASE.@) + */ +ULONG WINAPI PerfStartProvider( GUID *guid, PERFLIBREQUEST callback, HANDLE *provider ) +{ + PERF_PROVIDER_CONTEXT ctx; + + FIXME( "guid %s, callback %p, provider %p semi-stub.\n", debugstr_guid(guid), callback, provider ); + + memset( &ctx, 0, sizeof(ctx) ); + ctx.ContextSize = sizeof(ctx); + ctx.ControlCallback = callback; + + return PerfStartProviderEx( guid, &ctx, provider ); +} + +/*********************************************************************** + * PerfStartProviderEx (KERNELBASE.@) + */ +ULONG WINAPI PerfStartProviderEx( GUID *guid, PERF_PROVIDER_CONTEXT *context, HANDLE *provider ) +{ + struct perf_provider *prov; + + FIXME( "guid %s, context %p, provider %p semi-stub.\n", debugstr_guid(guid), context, provider ); + + if (!guid || !context || !provider) return ERROR_INVALID_PARAMETER; + if (context->ContextSize < sizeof(*context)) return ERROR_INVALID_PARAMETER; + + if (context->MemAllocRoutine || context->MemFreeRoutine) + FIXME("Memory allocation routine is not supported.\n"); + + if (!(prov = heap_alloc_zero( sizeof(*prov) ))) return ERROR_OUTOFMEMORY; + list_init( &prov->instance_list ); + memcpy( &prov->guid, guid, sizeof(prov->guid) ); + prov->callback = context->ControlCallback; + *provider = prov; + + return STATUS_SUCCESS; +} + +/*********************************************************************** + * PerfStopProvider (KERNELBASE.@) + */ +ULONG WINAPI PerfStopProvider(HANDLE handle) +{ + struct perf_provider *prov = perf_provider_from_handle( handle ); + struct counterset_instance *inst, *next; + unsigned int i; + + TRACE( "handle %p.\n", handle ); + + if (!list_empty( &prov->instance_list )) + WARN( "Stopping provider with active counter instances.\n" ); + + LIST_FOR_EACH_ENTRY_SAFE(inst, next, &prov->instance_list, struct counterset_instance, entry) + { + list_remove( &inst->entry ); + heap_free( inst ); + } + + for (i = 0; i < prov->counterset_count; ++i) + heap_free( prov->countersets[i] ); + heap_free( prov->countersets ); + heap_free( prov ); + return STATUS_SUCCESS; +} + +/*********************************************************************** + * QuirkIsEnabled (KERNELBASE.@) + */ +BOOL WINAPI QuirkIsEnabled(void *arg) +{ + FIXME("(%p): stub\n", arg); + return FALSE; +} + +/*********************************************************************** + * QuirkIsEnabled3 (KERNELBASE.@) + */ +BOOL WINAPI QuirkIsEnabled3(void *unk1, void *unk2) +{ + static int once; + + if (!once++) + FIXME("(%p, %p) stub!\n", unk1, unk2); + + return FALSE; +} + +HRESULT WINAPI QISearch(void *base, const QITAB *table, REFIID riid, void **obj) +{ + const QITAB *ptr; + IUnknown *unk; + + TRACE("%p, %p, %s, %p\n", base, table, debugstr_guid(riid), obj); + + if (!obj) + return E_POINTER; + + for (ptr = table; ptr->piid; ++ptr) + { + TRACE("trying (offset %ld) %s\n", ptr->dwOffset, debugstr_guid(ptr->piid)); + if (IsEqualIID(riid, ptr->piid)) + { + unk = (IUnknown *)((BYTE *)base + ptr->dwOffset); + TRACE("matched, returning (%p)\n", unk); + *obj = unk; + IUnknown_AddRef(unk); + return S_OK; + } + } + + if (IsEqualIID(riid, &IID_IUnknown)) + { + unk = (IUnknown *)((BYTE *)base + table->dwOffset); + TRACE("returning first for IUnknown (%p)\n", unk); + *obj = unk; + IUnknown_AddRef(unk); + return S_OK; + } + + WARN("Not found %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +HRESULT WINAPI GetAcceptLanguagesA(LPSTR langbuf, DWORD *buflen) +{ + DWORD buflenW, convlen; + WCHAR *langbufW; + HRESULT hr; + + TRACE("%p, %p, *%p: %ld\n", langbuf, buflen, buflen, buflen ? *buflen : -1); + + if (!langbuf || !buflen || !*buflen) + return E_FAIL; + + buflenW = *buflen; + langbufW = heap_alloc(sizeof(WCHAR) * buflenW); + hr = GetAcceptLanguagesW(langbufW, &buflenW); + + if (hr == S_OK) + { + convlen = WideCharToMultiByte(CP_ACP, 0, langbufW, -1, langbuf, *buflen, NULL, NULL); + convlen--; /* do not count the terminating 0 */ + } + else /* copy partial string anyway */ + { + convlen = WideCharToMultiByte(CP_ACP, 0, langbufW, *buflen, langbuf, *buflen, NULL, NULL); + if (convlen < *buflen) + { + langbuf[convlen] = 0; + convlen--; /* do not count the terminating 0 */ + } + else + { + convlen = *buflen; + } + } + *buflen = buflenW ? convlen : 0; + + heap_free(langbufW); + return hr; +} + +static HRESULT lcid_to_rfc1766(LCID lcid, WCHAR *rfc1766, INT len) +{ + WCHAR buffer[6 /* MAX_RFC1766_NAME */]; + INT n = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME, buffer, ARRAY_SIZE(buffer)); + INT i; + + if (n) + { + i = PRIMARYLANGID(lcid); + if ((((i == LANG_ENGLISH) || (i == LANG_CHINESE) || (i == LANG_ARABIC)) && + (SUBLANGID(lcid) == SUBLANG_DEFAULT)) || + (SUBLANGID(lcid) > SUBLANG_DEFAULT)) { + + buffer[n - 1] = '-'; + i = GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME, buffer + n, ARRAY_SIZE(buffer) - n); + if (!i) + buffer[n - 1] = '\0'; + } + else + i = 0; + + LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, buffer, n + i, rfc1766, len); + return ((n + i) > len) ? E_INVALIDARG : S_OK; + } + return E_FAIL; +} + +HRESULT WINAPI GetAcceptLanguagesW(WCHAR *langbuf, DWORD *buflen) +{ + DWORD mystrlen, mytype; + WCHAR *mystr; + LCID mylcid; + HKEY mykey; + LONG lres; + DWORD len; + + TRACE("%p, %p, *%p: %ld\n", langbuf, buflen, buflen, buflen ? *buflen : -1); + + if (!langbuf || !buflen || !*buflen) + return E_FAIL; + + mystrlen = (*buflen > 20) ? *buflen : 20 ; + len = mystrlen * sizeof(WCHAR); + mystr = heap_alloc(len); + mystr[0] = 0; + RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Internet Explorer\\International", + 0, KEY_QUERY_VALUE, &mykey); + lres = RegQueryValueExW(mykey, L"AcceptLanguage", 0, &mytype, (PBYTE)mystr, &len); + RegCloseKey(mykey); + len = lstrlenW(mystr); + + if (!lres && (*buflen > len)) + { + lstrcpyW(langbuf, mystr); + *buflen = len; + heap_free(mystr); + return S_OK; + } + + /* Did not find a value in the registry or the user buffer is too small */ + mylcid = GetUserDefaultLCID(); + lcid_to_rfc1766(mylcid, mystr, mystrlen); + len = lstrlenW(mystr); + + memcpy(langbuf, mystr, min(*buflen, len + 1)*sizeof(WCHAR)); + heap_free(mystr); + + if (*buflen > len) + { + *buflen = len; + return S_OK; + } + + *buflen = 0; + return E_NOT_SUFFICIENT_BUFFER; +} diff --git a/dll/win32/KernelBase/wine/memory.c b/dll/win32/KernelBase/wine/memory.c new file mode 100644 index 0000000000000..107453034991f --- /dev/null +++ b/dll/win32/KernelBase/wine/memory.c @@ -0,0 +1,1826 @@ +/* + * Win32 memory management functions + * + * Copyright 1997 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winternl.h" +#include "winerror.h" +#include "ddk/wdm.h" + +#include "kernelbase.h" +#include "wine/exception.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(heap); +WINE_DECLARE_DEBUG_CHANNEL(virtual); +WINE_DECLARE_DEBUG_CHANNEL(globalmem); + + + +static CROSS_PROCESS_WORK_LIST *open_cross_process_connection( HANDLE process ) +{ +#ifdef __aarch64__ + CROSS_PROCESS_WORK_LIST *list; + HANDLE section; + + RtlOpenCrossProcessEmulatorWorkConnection( process, §ion, (void **)&list ); + if (section) NtClose( section ); + return list; +#else + return NULL; +#endif +} + +static void close_cross_process_connection( CROSS_PROCESS_WORK_LIST *list ) +{ + if (list) NtUnmapViewOfSection( GetCurrentProcess(), list ); +} + +static void send_cross_process_notification( CROSS_PROCESS_WORK_LIST *list, UINT id, + const void *addr, SIZE_T size, int nb_args, ... ) +{ +#ifdef __aarch64__ + CROSS_PROCESS_WORK_ENTRY *entry; + void *unused; + va_list args; + int i; + + if (!list) return; + if ((entry = RtlWow64PopCrossProcessWorkFromFreeList( &list->free_list ))) + { + entry->id = id; + entry->addr = (ULONG_PTR)addr; + entry->size = size; + if (nb_args) + { + va_start( args, nb_args ); + for (i = 0; i < nb_args; i++) entry->args[i] = va_arg( args, int ); + va_end( args ); + } + RtlWow64PushCrossProcessWorkOntoWorkList( &list->work_list, entry, &unused ); + } +#endif +} + + +/*********************************************************************** + * Virtual memory functions + ***********************************************************************/ + +static const SIZE_T page_mask = 0xfff; +#define ROUND_ADDR(addr) ((void *)((UINT_PTR)(addr) & ~page_mask)) +#define ROUND_SIZE(addr,size) (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask) + +/*********************************************************************** + * DiscardVirtualMemory (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH DiscardVirtualMemory( void *addr, SIZE_T size ) +{ + NTSTATUS status; + LPVOID ret = addr; + + status = NtAllocateVirtualMemory( GetCurrentProcess(), &ret, 0, &size, MEM_RESET, PAGE_NOACCESS ); + return RtlNtStatusToDosError( status ); +} + + +/*********************************************************************** + * FlushViewOfFile (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FlushViewOfFile( const void *base, SIZE_T size ) +{ + NTSTATUS status = NtFlushVirtualMemory( GetCurrentProcess(), &base, &size, 0 ); + + if (status == STATUS_NOT_MAPPED_DATA) status = STATUS_SUCCESS; + return set_ntstatus( status ); +} + + +/**************************************************************************** + * FlushInstructionCache (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FlushInstructionCache( HANDLE process, LPCVOID addr, SIZE_T size ) +{ + CROSS_PROCESS_WORK_LIST *list; + + if ((list = open_cross_process_connection( process ))) + { + send_cross_process_notification( list, CrossProcessFlushCache, addr, size, 0 ); + close_cross_process_connection( list ); + } + return set_ntstatus( NtFlushInstructionCache( process, addr, size )); +} + + +/*********************************************************************** + * GetLargePageMinimum (kernelbase.@) + */ +SIZE_T WINAPI GetLargePageMinimum(void) +{ + return 2 * 1024 * 1024; +} + + +static void fill_system_info( SYSTEM_INFO *si, const SYSTEM_BASIC_INFORMATION *basic_info, + const SYSTEM_CPU_INFORMATION *cpu_info ) +{ + si->wProcessorArchitecture = cpu_info->ProcessorArchitecture; + si->wReserved = 0; + si->dwPageSize = basic_info->PageSize; + si->lpMinimumApplicationAddress = basic_info->LowestUserAddress; + si->lpMaximumApplicationAddress = basic_info->HighestUserAddress; + si->dwActiveProcessorMask = basic_info->ActiveProcessorsAffinityMask; + si->dwNumberOfProcessors = basic_info->NumberOfProcessors; + si->dwAllocationGranularity = basic_info->AllocationGranularity; + si->wProcessorLevel = cpu_info->ProcessorLevel; + si->wProcessorRevision = cpu_info->ProcessorRevision; + + switch (cpu_info->ProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_INTEL: + switch (cpu_info->ProcessorLevel) + { + case 3: si->dwProcessorType = PROCESSOR_INTEL_386; break; + case 4: si->dwProcessorType = PROCESSOR_INTEL_486; break; + case 5: + case 6: si->dwProcessorType = PROCESSOR_INTEL_PENTIUM; break; + default: si->dwProcessorType = PROCESSOR_INTEL_PENTIUM; break; + } + break; + case PROCESSOR_ARCHITECTURE_AMD64: + si->dwProcessorType = PROCESSOR_AMD_X8664; + break; + case PROCESSOR_ARCHITECTURE_ARM: + switch (cpu_info->ProcessorLevel) + { + case 4: si->dwProcessorType = PROCESSOR_ARM_7TDMI; break; + default: si->dwProcessorType = PROCESSOR_ARM920; + } + break; + case PROCESSOR_ARCHITECTURE_ARM64: + si->dwProcessorType = 0; + break; + default: + FIXME( "Unknown processor architecture %x\n", cpu_info->ProcessorArchitecture ); + si->dwProcessorType = 0; + break; + } +} + + +/*********************************************************************** + * GetNativeSystemInfo (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH GetNativeSystemInfo( SYSTEM_INFO *si ) +{ + SYSTEM_BASIC_INFORMATION basic_info; + SYSTEM_CPU_INFORMATION cpu_info; + + if (is_wow64) + { + USHORT current_machine, native_machine; + + RtlWow64GetProcessMachines( 0, ¤t_machine, &native_machine ); + if (native_machine != IMAGE_FILE_MACHINE_AMD64) + { + GetSystemInfo( si ); + si->wProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64; + return; + } + } + + if (!set_ntstatus( RtlGetNativeSystemInformation( SystemBasicInformation, + &basic_info, sizeof(basic_info), NULL )) || + !set_ntstatus( RtlGetNativeSystemInformation( SystemCpuInformation, + &cpu_info, sizeof(cpu_info), NULL ))) + return; + + fill_system_info( si, &basic_info, &cpu_info ); +} + + +/*********************************************************************** + * GetSystemInfo (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH GetSystemInfo( SYSTEM_INFO *si ) +{ + SYSTEM_BASIC_INFORMATION basic_info; + SYSTEM_CPU_INFORMATION cpu_info; + + if (!set_ntstatus( NtQuerySystemInformation( SystemBasicInformation, + &basic_info, sizeof(basic_info), NULL )) || + !set_ntstatus( NtQuerySystemInformation( SystemCpuInformation, + &cpu_info, sizeof(cpu_info), NULL ))) + return; + + fill_system_info( si, &basic_info, &cpu_info ); +} + + +/*********************************************************************** + * GetSystemFileCacheSize (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetSystemFileCacheSize( SIZE_T *mincache, SIZE_T *maxcache, DWORD *flags ) +{ + FIXME( "stub: %p %p %p\n", mincache, maxcache, flags ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * GetWriteWatch (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetWriteWatch( DWORD flags, void *base, SIZE_T size, void **addresses, + ULONG_PTR *count, ULONG *granularity ) +{ + if (!set_ntstatus( NtGetWriteWatch( GetCurrentProcess(), flags, base, size, + addresses, count, granularity ))) + return ~0u; + return 0; +} + + +/*********************************************************************** + * MapViewOfFile (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH MapViewOfFile( HANDLE mapping, DWORD access, DWORD offset_high, + DWORD offset_low, SIZE_T count ) +{ + return MapViewOfFileEx( mapping, access, offset_high, offset_low, count, NULL ); +} + + +/*********************************************************************** + * MapViewOfFileEx (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH MapViewOfFileEx( HANDLE handle, DWORD access, DWORD offset_high, + DWORD offset_low, SIZE_T count, LPVOID addr ) +{ + NTSTATUS status; + LARGE_INTEGER offset; + ULONG protect; + BOOL exec; + + offset.u.LowPart = offset_low; + offset.u.HighPart = offset_high; + + exec = access & FILE_MAP_EXECUTE; + access &= ~FILE_MAP_EXECUTE; + + if (access == FILE_MAP_COPY) + protect = exec ? PAGE_EXECUTE_WRITECOPY : PAGE_WRITECOPY; + else if (access & FILE_MAP_WRITE) + protect = exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; + else if (access & FILE_MAP_READ) + protect = exec ? PAGE_EXECUTE_READ : PAGE_READONLY; + else protect = PAGE_NOACCESS; + + if ((status = NtMapViewOfSection( handle, GetCurrentProcess(), &addr, 0, 0, &offset, + &count, ViewShare, 0, protect )) < 0) + { + SetLastError( RtlNtStatusToDosError(status) ); + addr = NULL; + } + return addr; +} + + +/*********************************************************************** + * MapViewOfFileFromApp (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH MapViewOfFileFromApp( HANDLE handle, ULONG access, ULONG64 offset, SIZE_T size ) +{ + return MapViewOfFile( handle, access, offset << 32, offset, size ); +} + +/*********************************************************************** + * MapViewOfFile3 (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH MapViewOfFile3( HANDLE handle, HANDLE process, PVOID baseaddr, ULONG64 offset, + SIZE_T size, ULONG alloc_type, ULONG protection, MEM_EXTENDED_PARAMETER *params, ULONG params_count ) +{ + LARGE_INTEGER off; + void *addr; + + if (!process) process = GetCurrentProcess(); + + addr = baseaddr; + off.QuadPart = offset; + if (!set_ntstatus( NtMapViewOfSectionEx( handle, process, &addr, &off, &size, alloc_type, protection, + params, params_count ))) + { + return NULL; + } + return addr; +} + +/*********************************************************************** + * ReadProcessMemory (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReadProcessMemory( HANDLE process, const void *addr, void *buffer, + SIZE_T size, SIZE_T *bytes_read ) +{ + return set_ntstatus( NtReadVirtualMemory( process, addr, buffer, size, bytes_read )); +} + + +/*********************************************************************** + * ResetWriteWatch (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH ResetWriteWatch( void *base, SIZE_T size ) +{ + if (!set_ntstatus( NtResetWriteWatch( GetCurrentProcess(), base, size ))) + return ~0u; + return 0; +} + + +/*********************************************************************** + * SetSystemFileCacheSize (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetSystemFileCacheSize( SIZE_T mincache, SIZE_T maxcache, DWORD flags ) +{ + FIXME( "stub: %Id %Id %ld\n", mincache, maxcache, flags ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * UnmapViewOfFile (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH UnmapViewOfFile( const void *addr ) +{ + if (GetVersion() & 0x80000000) + { + MEMORY_BASIC_INFORMATION info; + if (!VirtualQuery( addr, &info, sizeof(info) ) || info.AllocationBase != addr) + { + SetLastError( ERROR_INVALID_ADDRESS ); + return FALSE; + } + } + return set_ntstatus( NtUnmapViewOfSection( GetCurrentProcess(), (void *)addr )); +} + + +/*********************************************************************** + * UnmapViewOfFile2 (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH UnmapViewOfFile2( HANDLE process, void *addr, ULONG flags ) +{ + return set_ntstatus( NtUnmapViewOfSectionEx( process, addr, flags )); +} + + +/*********************************************************************** + * UnmapViewOfFileEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH UnmapViewOfFileEx( void *addr, ULONG flags ) +{ + return set_ntstatus( NtUnmapViewOfSectionEx( GetCurrentProcess(), addr, flags )); +} + + +/*********************************************************************** + * VirtualAlloc (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH VirtualAlloc( void *addr, SIZE_T size, DWORD type, DWORD protect ) +{ + return VirtualAllocEx( GetCurrentProcess(), addr, size, type, protect ); +} + + +/*********************************************************************** + * VirtualAllocEx (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH VirtualAllocEx( HANDLE process, void *addr, SIZE_T size, + DWORD type, DWORD protect ) +{ + LPVOID ret = addr; + + if (!set_ntstatus( NtAllocateVirtualMemory( process, &ret, 0, &size, type, protect ))) return NULL; + return ret; +} + + +/*********************************************************************** + * VirtualAlloc2 (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH VirtualAlloc2( HANDLE process, void *addr, SIZE_T size, + DWORD type, DWORD protect, + MEM_EXTENDED_PARAMETER *parameters, ULONG count ) +{ + LPVOID ret = addr; + + if (!process) process = GetCurrentProcess(); + if (!set_ntstatus( NtAllocateVirtualMemoryEx( process, &ret, &size, type, protect, parameters, count ))) + return NULL; + return ret; +} + +static BOOL is_exec_prot( DWORD protect ) +{ + return protect == PAGE_EXECUTE || protect == PAGE_EXECUTE_READ || protect == PAGE_EXECUTE_READWRITE + || protect == PAGE_EXECUTE_WRITECOPY; +} + +/*********************************************************************** + * VirtualAlloc2FromApp (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH VirtualAlloc2FromApp( HANDLE process, void *addr, SIZE_T size, + DWORD type, DWORD protect, MEM_EXTENDED_PARAMETER *parameters, ULONG count ) +{ + LPVOID ret = addr; + + TRACE_(virtual)( "addr %p, size %p, type %#lx, protect %#lx, params %p, count %lu.\n", addr, (void *)size, type, protect, + parameters, count ); + + if (is_exec_prot( protect )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + + if (!process) process = GetCurrentProcess(); + if (!set_ntstatus( NtAllocateVirtualMemoryEx( process, &ret, &size, type, protect, parameters, count ))) + return NULL; + return ret; +} + + +/*********************************************************************** + * VirtualAllocFromApp (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH VirtualAllocFromApp( void *addr, SIZE_T size, + DWORD type, DWORD protect ) +{ + LPVOID ret = addr; + + TRACE_(virtual)( "addr %p, size %p, type %#lx, protect %#lx.\n", addr, (void *)size, type, protect ); + + if (is_exec_prot( protect )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + + if (!set_ntstatus( NtAllocateVirtualMemory( GetCurrentProcess(), &ret, 0, &size, type, protect ))) return NULL; + return ret; +} + + +/*********************************************************************** + * PrefetchVirtualMemory (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH PrefetchVirtualMemory( HANDLE process, ULONG_PTR count, + WIN32_MEMORY_RANGE_ENTRY *addresses, ULONG flags ) +{ + return set_ntstatus( NtSetInformationVirtualMemory( process, VmPrefetchInformation, + count, (PMEMORY_RANGE_ENTRY)addresses, + &flags, sizeof(flags) )); +} + + +/*********************************************************************** + * VirtualFree (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH VirtualFree( void *addr, SIZE_T size, DWORD type ) +{ + return VirtualFreeEx( GetCurrentProcess(), addr, size, type ); +} + + +/*********************************************************************** + * VirtualFreeEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH VirtualFreeEx( HANDLE process, void *addr, SIZE_T size, DWORD type ) +{ + if (type == MEM_RELEASE && size) + { + WARN( "Trying to release memory with specified size.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return set_ntstatus( NtFreeVirtualMemory( process, &addr, &size, type )); +} + + +/*********************************************************************** + * VirtualLock (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH VirtualLock( void *addr, SIZE_T size ) +{ + return set_ntstatus( NtLockVirtualMemory( GetCurrentProcess(), &addr, &size, 1 )); +} + + +/*********************************************************************** + * VirtualProtect (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH VirtualProtect( void *addr, SIZE_T size, DWORD new_prot, DWORD *old_prot ) +{ + return VirtualProtectEx( GetCurrentProcess(), addr, size, new_prot, old_prot ); +} + + +/*********************************************************************** + * VirtualProtectEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH VirtualProtectEx( HANDLE process, void *addr, SIZE_T size, + DWORD new_prot, DWORD *old_prot ) +{ + DWORD prot; + + /* Win9x allows passing NULL as old_prot while this fails on NT */ + if (!old_prot && (GetVersion() & 0x80000000)) old_prot = &prot; + return set_ntstatus( NtProtectVirtualMemory( process, &addr, &size, new_prot, old_prot )); +} + + +/*********************************************************************** + * VirtualQuery (kernelbase.@) + */ +SIZE_T WINAPI DECLSPEC_HOTPATCH VirtualQuery( LPCVOID addr, PMEMORY_BASIC_INFORMATION info, SIZE_T len ) +{ + return VirtualQueryEx( GetCurrentProcess(), addr, info, len ); +} + + +/*********************************************************************** + * VirtualQueryEx (kernelbase.@) + */ +SIZE_T WINAPI DECLSPEC_HOTPATCH VirtualQueryEx( HANDLE process, LPCVOID addr, + PMEMORY_BASIC_INFORMATION info, SIZE_T len ) +{ + SIZE_T ret; + + if (!set_ntstatus( NtQueryVirtualMemory( process, addr, MemoryBasicInformation, info, len, &ret ))) + return 0; + return ret; +} + + +/*********************************************************************** + * VirtualUnlock (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH VirtualUnlock( void *addr, SIZE_T size ) +{ + return set_ntstatus( NtUnlockVirtualMemory( GetCurrentProcess(), &addr, &size, 1 )); +} + + +/*********************************************************************** + * WriteProcessMemory (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WriteProcessMemory( HANDLE process, void *addr, const void *buffer, + SIZE_T size, SIZE_T *bytes_written ) +{ + CROSS_PROCESS_WORK_LIST *list = open_cross_process_connection( process ); + DWORD old_prot, prot = PAGE_TARGETS_NO_UPDATE | PAGE_ENCLAVE_NO_CHANGE; + MEMORY_BASIC_INFORMATION info; + void *base_addr; + SIZE_T region_size; + NTSTATUS status, status2; + + if (!VirtualQueryEx( process, addr, &info, sizeof(info) )) + { + close_cross_process_connection( list ); + return FALSE; + } + + switch (info.Protect & ~(PAGE_GUARD | PAGE_NOCACHE)) + { + case PAGE_READWRITE: + case PAGE_WRITECOPY: + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + /* already writable */ + if ((status = NtWriteVirtualMemory( process, addr, buffer, size, bytes_written ))) break; + send_cross_process_notification( list, CrossProcessFlushCache, addr, size, 0 ); + NtFlushInstructionCache( process, addr, size ); + break; + + case PAGE_EXECUTE: + case PAGE_EXECUTE_READ: + /* make it writable */ + base_addr = ROUND_ADDR( addr ); + region_size = ROUND_SIZE( addr, size ); + region_size = min( region_size, (char *)info.BaseAddress + info.RegionSize - (char *)base_addr ); + prot |= (info.Type == MEM_PRIVATE) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_WRITECOPY; + + send_cross_process_notification( list, CrossProcessPreVirtualProtect, + base_addr, region_size, 1, prot ); + status = NtProtectVirtualMemory( process, &base_addr, ®ion_size, prot, &old_prot ); + send_cross_process_notification( list, CrossProcessPostVirtualProtect, + base_addr, region_size, 2, prot, status ); + if (status) break; + + status = NtWriteVirtualMemory( process, addr, buffer, size, bytes_written ); + if (!status) + { + send_cross_process_notification( list, CrossProcessFlushCache, addr, size, 0 ); + NtFlushInstructionCache( process, addr, size ); + } + + prot = PAGE_TARGETS_NO_UPDATE | PAGE_ENCLAVE_NO_CHANGE | old_prot; + send_cross_process_notification( list, CrossProcessPreVirtualProtect, + base_addr, region_size, 1, prot ); + status2 = NtProtectVirtualMemory( process, &base_addr, ®ion_size, prot, &old_prot ); + send_cross_process_notification( list, CrossProcessPostVirtualProtect, + base_addr, region_size, 2, prot, status2 ); + break; + + default: + /* not writable */ + status = STATUS_ACCESS_VIOLATION; + break; + } + + close_cross_process_connection( list ); + return set_ntstatus( status ); +} + + +/* IsBadStringPtrA replacement for kernelbase, to catch exception in debug traces. */ +BOOL WINAPI IsBadStringPtrA( LPCSTR str, UINT_PTR max ) +{ + if (!str) return TRUE; + __TRY + { + volatile const char *p = str; + while (p != str + max) if (!*p++) break; + } + __EXCEPT_PAGE_FAULT + { + return TRUE; + } + __ENDTRY + return FALSE; +} + + +/* IsBadStringPtrW replacement for kernelbase, to catch exception in debug traces. */ +BOOL WINAPI IsBadStringPtrW( LPCWSTR str, UINT_PTR max ) +{ + if (!str) return TRUE; + __TRY + { + volatile const WCHAR *p = str; + while (p != str + max) if (!*p++) break; + } + __EXCEPT_PAGE_FAULT + { + return TRUE; + } + __ENDTRY + return FALSE; +} + + +/*********************************************************************** + * Heap functions + ***********************************************************************/ + + +/*********************************************************************** + * HeapCompact (kernelbase.@) + */ +SIZE_T WINAPI DECLSPEC_HOTPATCH HeapCompact( HANDLE heap, DWORD flags ) +{ + return RtlCompactHeap( heap, flags ); +} + + +/*********************************************************************** + * HeapCreate (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH HeapCreate( DWORD flags, SIZE_T init_size, SIZE_T max_size ) +{ + HANDLE ret = RtlCreateHeap( flags, NULL, max_size, init_size, NULL, NULL ); + if (!ret) SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return ret; +} + + +/*********************************************************************** + * HeapDestroy (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH HeapDestroy( HANDLE heap ) +{ + if (!RtlDestroyHeap( heap )) return TRUE; + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; +} + + +/*********************************************************************** + * HeapLock (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH HeapLock( HANDLE heap ) +{ + return RtlLockHeap( heap ); +} + + +/*********************************************************************** + * HeapQueryInformation (kernelbase.@) + */ +BOOL WINAPI HeapQueryInformation( HANDLE heap, HEAP_INFORMATION_CLASS info_class, + PVOID info, SIZE_T size, PSIZE_T size_out ) +{ + return set_ntstatus( RtlQueryHeapInformation( heap, info_class, info, size, size_out )); +} + + +/*********************************************************************** + * HeapSetInformation (kernelbase.@) + */ +BOOL WINAPI HeapSetInformation( HANDLE heap, HEAP_INFORMATION_CLASS infoclass, PVOID info, SIZE_T size ) +{ + return set_ntstatus( RtlSetHeapInformation( heap, infoclass, info, size )); +} + + +/*********************************************************************** + * HeapUnlock (kernelbase.@) + */ +BOOL WINAPI HeapUnlock( HANDLE heap ) +{ + return RtlUnlockHeap( heap ); +} + + +/*********************************************************************** + * HeapValidate (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH HeapValidate( HANDLE heap, DWORD flags, LPCVOID ptr ) +{ + return RtlValidateHeap( heap, flags, ptr ); +} + + +/* undocumented RtlWalkHeap structure */ + +struct rtl_heap_entry +{ + LPVOID lpData; + SIZE_T cbData; /* differs from PROCESS_HEAP_ENTRY */ + BYTE cbOverhead; + BYTE iRegionIndex; + WORD wFlags; /* value differs from PROCESS_HEAP_ENTRY */ + union { + struct { + HANDLE hMem; + DWORD dwReserved[3]; + } Block; + struct { + DWORD dwCommittedSize; + DWORD dwUnCommittedSize; + LPVOID lpFirstBlock; + LPVOID lpLastBlock; + } Region; + }; +}; + +/* rtl_heap_entry flags, names made up */ + +#define RTL_HEAP_ENTRY_BUSY 0x0001 +#define RTL_HEAP_ENTRY_REGION 0x0002 +#define RTL_HEAP_ENTRY_BLOCK 0x0010 +#define RTL_HEAP_ENTRY_UNCOMMITTED 0x1000 +#define RTL_HEAP_ENTRY_COMMITTED 0x4000 +#define RTL_HEAP_ENTRY_LFH 0x8000 + + +/*********************************************************************** + * HeapWalk (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH HeapWalk( HANDLE heap, PROCESS_HEAP_ENTRY *entry ) +{ + struct rtl_heap_entry rtl_entry = {0}; + NTSTATUS status; + + if (!entry) return set_ntstatus( STATUS_INVALID_PARAMETER ); + + rtl_entry.lpData = entry->lpData; + rtl_entry.cbData = entry->cbData; + rtl_entry.cbOverhead = entry->cbOverhead; + rtl_entry.iRegionIndex = entry->iRegionIndex; + + if (entry->wFlags & PROCESS_HEAP_ENTRY_BUSY) + rtl_entry.wFlags |= RTL_HEAP_ENTRY_BUSY; + if (entry->wFlags & PROCESS_HEAP_REGION) + rtl_entry.wFlags |= RTL_HEAP_ENTRY_REGION; + if (entry->wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE) + rtl_entry.wFlags |= RTL_HEAP_ENTRY_UNCOMMITTED; + memcpy( &rtl_entry.Region, &entry->Region, sizeof(entry->Region) ); + + if (!(status = RtlWalkHeap( heap, &rtl_entry ))) + { + entry->lpData = rtl_entry.lpData; + entry->cbData = rtl_entry.cbData; + entry->cbOverhead = rtl_entry.cbOverhead; + entry->iRegionIndex = rtl_entry.iRegionIndex; + + if (rtl_entry.wFlags & RTL_HEAP_ENTRY_BUSY) + entry->wFlags = PROCESS_HEAP_ENTRY_BUSY; + else if (rtl_entry.wFlags & RTL_HEAP_ENTRY_REGION) + entry->wFlags = PROCESS_HEAP_REGION; + else if (rtl_entry.wFlags & RTL_HEAP_ENTRY_UNCOMMITTED) + entry->wFlags = PROCESS_HEAP_UNCOMMITTED_RANGE; + else + entry->wFlags = 0; + + memcpy( &entry->Region, &rtl_entry.Region, sizeof(entry->Region) ); + } + + return set_ntstatus( status ); +} + + +/*********************************************************************** + * Global/local heap functions + ***********************************************************************/ + +/* some undocumented flags (names are made up) */ +#define HEAP_ADD_USER_INFO 0x00000100 + +/* not compatible with windows */ +struct kernelbase_global_data +{ + struct mem_entry *mem_entries; + struct mem_entry *mem_entries_end; +}; + +#define MEM_FLAG_USED 1 +#define MEM_FLAG_MOVEABLE 2 +#define MEM_FLAG_DISCARDABLE 4 +#define MEM_FLAG_DISCARDED 8 +#define MEM_FLAG_DDESHARE 0x8000 + +struct mem_entry +{ + union + { + struct + { + WORD flags; + BYTE lock; + }; + void *next_free; + }; + void *ptr; +}; + +C_ASSERT(sizeof(struct mem_entry) == 2 * sizeof(void *)); + +#define MAX_MEM_HANDLES 0x10000 +static struct mem_entry *next_free_mem; +static struct kernelbase_global_data global_data = {0}; + +static inline struct mem_entry *unsafe_mem_from_HLOCAL( HLOCAL handle ) +{ + struct mem_entry *mem = CONTAINING_RECORD( *(volatile HANDLE *)&handle, struct mem_entry, ptr ); + struct kernelbase_global_data *data = &global_data; + if (((UINT_PTR)handle & ((sizeof(void *) << 1) - 1)) != sizeof(void *)) return NULL; + if (mem < data->mem_entries || mem >= data->mem_entries_end) return NULL; + if (!(mem->flags & MEM_FLAG_USED)) return NULL; + return mem; +} + +static inline HLOCAL HLOCAL_from_mem( struct mem_entry *mem ) +{ + if (!mem) return 0; + return &mem->ptr; +} + +static inline void *unsafe_ptr_from_HLOCAL( HLOCAL handle ) +{ + if (((UINT_PTR)handle & ((sizeof(void *) << 1) - 1))) return NULL; + return handle; +} + +void init_global_data(void) +{ + global_data.mem_entries = VirtualAlloc( NULL, MAX_MEM_HANDLES * sizeof(struct mem_entry), MEM_COMMIT, PAGE_READWRITE ); + if (!(next_free_mem = global_data.mem_entries)) ERR( "Failed to allocate kernelbase global handle table\n" ); + global_data.mem_entries_end = global_data.mem_entries + MAX_MEM_HANDLES; +} + +/*********************************************************************** + * KernelBaseGetGlobalData (kernelbase.@) + */ +void *WINAPI KernelBaseGetGlobalData(void) +{ + WARN_(globalmem)( "semi-stub!\n" ); + return &global_data; +} + + +/*********************************************************************** + * GlobalAlloc (kernelbase.@) + */ +HGLOBAL WINAPI DECLSPEC_HOTPATCH GlobalAlloc( UINT flags, SIZE_T size ) +{ + struct mem_entry *mem; + HGLOBAL handle; + + /* LocalAlloc allows a 0-size fixed block, but GlobalAlloc doesn't */ + if (!(flags & GMEM_MOVEABLE) && !size) size = 1; + + handle = LocalAlloc( flags, size ); + + if ((mem = unsafe_mem_from_HLOCAL( handle )) && (flags & GMEM_DDESHARE)) + mem->flags |= MEM_FLAG_DDESHARE; + + return handle; +} + + +/*********************************************************************** + * GlobalFree (kernelbase.@) + */ +HGLOBAL WINAPI DECLSPEC_HOTPATCH GlobalFree( HLOCAL handle ) +{ + return LocalFree( handle ); +} + + +/*********************************************************************** + * LocalAlloc (kernelbase.@) + */ +HLOCAL WINAPI DECLSPEC_HOTPATCH LocalAlloc( UINT flags, SIZE_T size ) +{ + DWORD heap_flags = 0x200 | HEAP_ADD_USER_INFO; + HANDLE heap = GetProcessHeap(); + struct mem_entry *mem; + HLOCAL handle; + void *ptr; + + TRACE_(globalmem)( "flags %#x, size %#Ix\n", flags, size ); + + if (flags & LMEM_ZEROINIT) heap_flags |= HEAP_ZERO_MEMORY; + + if (!(flags & LMEM_MOVEABLE)) /* pointer */ + { + ptr = HeapAlloc( heap, heap_flags, size ); + if (ptr) RtlSetUserValueHeap( heap, heap_flags, ptr, ptr ); + TRACE_(globalmem)( "return %p\n", ptr ); + return ptr; + } + + RtlLockHeap( heap ); + if ((mem = next_free_mem) < global_data.mem_entries || mem >= global_data.mem_entries_end) + mem = NULL; + else + { + if (!mem->next_free) next_free_mem++; + else next_free_mem = mem->next_free; + mem->next_free = NULL; + } + RtlUnlockHeap( heap ); + + if (!mem) goto failed; + handle = HLOCAL_from_mem( mem ); + + mem->flags = MEM_FLAG_USED | MEM_FLAG_MOVEABLE; + if (flags & LMEM_DISCARDABLE) mem->flags |= MEM_FLAG_DISCARDABLE; + mem->lock = 0; + mem->ptr = NULL; + + if (!size) mem->flags |= MEM_FLAG_DISCARDED; + else + { + if (!(ptr = HeapAlloc( heap, heap_flags, size ))) goto failed; + RtlSetUserValueHeap( heap, heap_flags, ptr, handle ); + mem->ptr = ptr; + } + + TRACE_(globalmem)( "return handle %p, ptr %p\n", handle, mem->ptr ); + return handle; + +failed: + if (mem) LocalFree( *(volatile HANDLE *)&handle ); + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return 0; +} + + +/*********************************************************************** + * LocalFree (kernelbase.@) + */ +HLOCAL WINAPI DECLSPEC_HOTPATCH LocalFree( HLOCAL handle ) +{ + HANDLE heap = GetProcessHeap(); + struct mem_entry *mem; + HLOCAL ret = handle; + void *ptr; + + TRACE_(globalmem)( "handle %p\n", handle ); + + RtlLockHeap( heap ); + if ((ptr = unsafe_ptr_from_HLOCAL( handle )) && + HeapValidate( heap, HEAP_NO_SERIALIZE, ptr )) + { + if (HeapFree( heap, HEAP_NO_SERIALIZE, ptr )) ret = 0; + } + else if ((mem = unsafe_mem_from_HLOCAL( handle ))) + { + if (HeapFree( heap, HEAP_NO_SERIALIZE, mem->ptr )) ret = 0; + mem->ptr = NULL; + mem->next_free = next_free_mem; + next_free_mem = mem; + } + RtlUnlockHeap( heap ); + + if (ret) + { + WARN_(globalmem)( "invalid handle %p\n", handle ); + SetLastError( ERROR_INVALID_HANDLE ); + } + return ret; +} + + +/*********************************************************************** + * LocalLock (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH LocalLock( HLOCAL handle ) +{ + HANDLE heap = GetProcessHeap(); + struct mem_entry *mem; + void *ret = NULL; + + TRACE_(globalmem)( "handle %p\n", handle ); + + if (!handle) return NULL; + if ((ret = unsafe_ptr_from_HLOCAL( handle ))) + { + __TRY + { + volatile char *p = ret; + *p |= 0; + } + __EXCEPT_PAGE_FAULT + { + return NULL; + } + __ENDTRY + return ret; + } + + RtlLockHeap( heap ); + if ((mem = unsafe_mem_from_HLOCAL( handle ))) + { + if (!(ret = mem->ptr)) SetLastError( ERROR_DISCARDED ); + else if (!++mem->lock) mem->lock--; + } + else + { + WARN_(globalmem)( "invalid handle %p\n", handle ); + SetLastError( ERROR_INVALID_HANDLE ); + } + RtlUnlockHeap( heap ); + + return ret; +} + + +/*********************************************************************** + * LocalReAlloc (kernelbase.@) + */ +HLOCAL WINAPI DECLSPEC_HOTPATCH LocalReAlloc( HLOCAL handle, SIZE_T size, UINT flags ) +{ + DWORD heap_flags = 0x200 | HEAP_ADD_USER_INFO | HEAP_NO_SERIALIZE; + HANDLE heap = GetProcessHeap(); + struct mem_entry *mem; + HLOCAL ret = 0; + void *ptr; + + TRACE_(globalmem)( "handle %p, size %#Ix, flags %#x\n", handle, size, flags ); + + if (flags & LMEM_ZEROINIT) heap_flags |= HEAP_ZERO_MEMORY; + + RtlLockHeap( heap ); + if ((ptr = unsafe_ptr_from_HLOCAL( handle )) && + HeapValidate( heap, HEAP_NO_SERIALIZE, ptr )) + { + if (flags & LMEM_MODIFY) ret = handle; + else if (flags & LMEM_DISCARDABLE) SetLastError( ERROR_INVALID_PARAMETER ); + else + { + if (!(flags & LMEM_MOVEABLE)) heap_flags |= HEAP_REALLOC_IN_PLACE_ONLY; + ret = HeapReAlloc( heap, heap_flags, ptr, size ); + if (ret) RtlSetUserValueHeap( heap, heap_flags, ret, ret ); + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + } + } + else if ((mem = unsafe_mem_from_HLOCAL( handle ))) + { + if (flags & LMEM_MODIFY) + { + if (flags & LMEM_DISCARDABLE) mem->flags |= MEM_FLAG_DISCARDABLE; + ret = handle; + } + else if (flags & LMEM_DISCARDABLE) SetLastError( ERROR_INVALID_PARAMETER ); + else + { + if (size) + { + if (mem->lock && !(flags & LMEM_MOVEABLE)) heap_flags |= HEAP_REALLOC_IN_PLACE_ONLY; + if (!mem->ptr) ptr = HeapAlloc( heap, heap_flags, size ); + else ptr = HeapReAlloc( heap, heap_flags, mem->ptr, size ); + + if (!ptr) SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + else + { + RtlSetUserValueHeap( heap, heap_flags, ptr, handle ); + mem->flags &= ~MEM_FLAG_DISCARDED; + mem->ptr = ptr; + ret = handle; + } + } + else if ((flags & LMEM_MOVEABLE) && !mem->lock) + { + HeapFree( heap, heap_flags, mem->ptr ); + mem->flags |= MEM_FLAG_DISCARDED; + mem->ptr = NULL; + ret = handle; + } + else SetLastError( ERROR_INVALID_PARAMETER ); + } + } + else SetLastError( ERROR_INVALID_HANDLE ); + RtlUnlockHeap( heap ); + + return ret; +} + + +/*********************************************************************** + * LocalUnlock (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH LocalUnlock( HLOCAL handle ) +{ + HANDLE heap = GetProcessHeap(); + struct mem_entry *mem; + BOOL ret = FALSE; + + TRACE_(globalmem)( "handle %p\n", handle ); + + if (unsafe_ptr_from_HLOCAL( handle )) + { + SetLastError( ERROR_NOT_LOCKED ); + return FALSE; + } + + RtlLockHeap( heap ); + if ((mem = unsafe_mem_from_HLOCAL( handle ))) + { + if (mem->lock) + { + ret = (--mem->lock != 0); + if (!ret) SetLastError( NO_ERROR ); + } + else + { + WARN_(globalmem)( "handle %p not locked\n", handle ); + SetLastError( ERROR_NOT_LOCKED ); + } + } + else + { + WARN_(globalmem)( "invalid handle %p\n", handle ); + SetLastError( ERROR_INVALID_HANDLE ); + } + RtlUnlockHeap( heap ); + + return ret; +} + + +/*********************************************************************** + * Memory resource functions + ***********************************************************************/ + + +/*********************************************************************** + * CreateMemoryResourceNotification (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateMemoryResourceNotification( MEMORY_RESOURCE_NOTIFICATION_TYPE type ) +{ + HANDLE ret; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + + switch (type) + { + case LowMemoryResourceNotification: + RtlInitUnicodeString( &nameW, L"\\KernelObjects\\LowMemoryCondition" ); + break; + case HighMemoryResourceNotification: + RtlInitUnicodeString( &nameW, L"\\KernelObjects\\HighMemoryCondition" ); + break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + InitializeObjectAttributes( &attr, &nameW, 0, 0, NULL ); + if (!set_ntstatus( NtOpenEvent( &ret, EVENT_ALL_ACCESS, &attr ))) return 0; + return ret; +} + +/*********************************************************************** + * QueryMemoryResourceNotification (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryMemoryResourceNotification( HANDLE handle, BOOL *state ) +{ + switch (WaitForSingleObject( handle, 0 )) + { + case WAIT_OBJECT_0: + *state = TRUE; + return TRUE; + case WAIT_TIMEOUT: + *state = FALSE; + return TRUE; + } + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; +} + + +/*********************************************************************** + * Physical memory functions + ***********************************************************************/ + + +/*********************************************************************** + * AllocateUserPhysicalPages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH AllocateUserPhysicalPages( HANDLE process, ULONG_PTR *pages, + ULONG_PTR *userarray ) +{ + FIXME( "stub: %p %p %p\n", process, pages, userarray ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * FreeUserPhysicalPages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FreeUserPhysicalPages( HANDLE process, ULONG_PTR *pages, + ULONG_PTR *userarray ) +{ + FIXME( "stub: %p %p %p\n", process, pages, userarray ); + *pages = 0; + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * GetPhysicallyInstalledSystemMemory (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetPhysicallyInstalledSystemMemory( ULONGLONG *memory ) +{ + MEMORYSTATUSEX status; + + if (!memory) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + status.dwLength = sizeof(status); + GlobalMemoryStatusEx( &status ); + *memory = status.ullTotalPhys / 1024; + return TRUE; +} + + +/*********************************************************************** + * GlobalMemoryStatusEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GlobalMemoryStatusEx( MEMORYSTATUSEX *status ) +{ + static MEMORYSTATUSEX cached_status; + static DWORD last_check; + SYSTEM_BASIC_INFORMATION basic_info; + SYSTEM_PERFORMANCE_INFORMATION perf_info; + VM_COUNTERS_EX vmc; + + if (status->dwLength != sizeof(*status)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if ((NtGetTickCount() - last_check) < 1000) + { + *status = cached_status; + return TRUE; + } + last_check = NtGetTickCount(); + + if (!set_ntstatus( NtQuerySystemInformation( SystemBasicInformation, + &basic_info, sizeof(basic_info), NULL )) || + !set_ntstatus( NtQuerySystemInformation( SystemPerformanceInformation, + &perf_info, sizeof(perf_info), NULL)) || + !set_ntstatus( NtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, + &vmc, sizeof(vmc), NULL ))) + return FALSE; + + status->dwMemoryLoad = 0; + status->ullTotalPhys = basic_info.MmNumberOfPhysicalPages; + status->ullAvailPhys = perf_info.AvailablePages; + status->ullTotalPageFile = perf_info.TotalCommitLimit; + status->ullAvailPageFile = status->ullTotalPageFile - perf_info.TotalCommittedPages; + status->ullTotalVirtual = (ULONG_PTR)basic_info.HighestUserAddress - (ULONG_PTR)basic_info.LowestUserAddress + 1; + status->ullAvailVirtual = status->ullTotalVirtual - (ULONGLONG)vmc.WorkingSetSize /* approximate */; + status->ullAvailExtendedVirtual = 0; + + status->ullTotalPhys *= basic_info.PageSize; + status->ullAvailPhys *= basic_info.PageSize; + status->ullTotalPageFile *= basic_info.PageSize; + status->ullAvailPageFile *= basic_info.PageSize; + + if (status->ullTotalPhys) + status->dwMemoryLoad = (status->ullTotalPhys - status->ullAvailPhys) / (status->ullTotalPhys / 100); + + TRACE_(virtual)( "MemoryLoad %lu, TotalPhys %I64u, AvailPhys %I64u, TotalPageFile %I64u, " + "AvailPageFile %I64u, TotalVirtual %I64u, AvailVirtual %I64u\n", + status->dwMemoryLoad, status->ullTotalPhys, status->ullAvailPhys, status->ullTotalPageFile, + status->ullAvailPageFile, status->ullTotalVirtual, status->ullAvailVirtual ); + + cached_status = *status; + return TRUE; +} + + +/*********************************************************************** + * MapUserPhysicalPages (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH MapUserPhysicalPages( void *addr, ULONG_PTR page_count, ULONG_PTR *pages ) +{ + FIXME( "stub: %p %Iu %p\n", addr, page_count, pages ); + *pages = 0; + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * NUMA functions + ***********************************************************************/ + + +/*********************************************************************** + * AllocateUserPhysicalPagesNuma (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH AllocateUserPhysicalPagesNuma( HANDLE process, ULONG_PTR *pages, + ULONG_PTR *userarray, DWORD node ) +{ + if (node) FIXME( "Ignoring preferred node %lu\n", node ); + return AllocateUserPhysicalPages( process, pages, userarray ); +} + + +/*********************************************************************** + * CreateFileMappingNumaW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileMappingNumaW( HANDLE file, LPSECURITY_ATTRIBUTES sa, + DWORD protect, DWORD size_high, DWORD size_low, + LPCWSTR name, DWORD node ) +{ + if (node) FIXME( "Ignoring preferred node %lu\n", node ); + return CreateFileMappingW( file, sa, protect, size_high, size_low, name ); +} + + +/*********************************************************************** + * GetLogicalProcessorInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetLogicalProcessorInformation( SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buffer, + DWORD *len ) +{ + NTSTATUS status; + + if (!len) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + status = NtQuerySystemInformation( SystemLogicalProcessorInformation, buffer, *len, len ); + if (status == STATUS_INFO_LENGTH_MISMATCH) status = STATUS_BUFFER_TOO_SMALL; + return set_ntstatus( status ); +} + + +/*********************************************************************** + * GetLogicalProcessorInformationEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetLogicalProcessorInformationEx( LOGICAL_PROCESSOR_RELATIONSHIP relationship, + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, DWORD *len ) +{ + NTSTATUS status; + + if (!len) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + status = NtQuerySystemInformationEx( SystemLogicalProcessorInformationEx, &relationship, + sizeof(relationship), buffer, *len, len ); + if (status == STATUS_INFO_LENGTH_MISMATCH) status = STATUS_BUFFER_TOO_SMALL; + return set_ntstatus( status ); +} + + +/*********************************************************************** + * GetSystemCpuSetInformation (kernelbase.@) + */ +BOOL WINAPI GetSystemCpuSetInformation(SYSTEM_CPU_SET_INFORMATION *info, ULONG buffer_length, ULONG *return_length, + HANDLE process, ULONG flags) +{ + if (flags) + FIXME("Unsupported flags %#lx.\n", flags); + + *return_length = 0; + + return set_ntstatus( NtQuerySystemInformationEx( SystemCpuSetInformation, &process, sizeof(process), info, + buffer_length, return_length )); +} + + +/*********************************************************************** + * SetThreadSelectedCpuSets (kernelbase.@) + */ +BOOL WINAPI SetThreadSelectedCpuSets(HANDLE thread, const ULONG *cpu_set_ids, ULONG count) +{ + FIXME( "thread %p, cpu_set_ids %p, count %lu stub.\n", thread, cpu_set_ids, count ); + + return TRUE; +} + + +/*********************************************************************** + * SetProcessDefaultCpuSets (kernelbase.@) + */ +BOOL WINAPI SetProcessDefaultCpuSets(HANDLE process, const ULONG *cpu_set_ids, ULONG count) +{ + FIXME( "process %p, cpu_set_ids %p, count %lu stub.\n", process, cpu_set_ids, count ); + + return TRUE; +} + + +/********************************************************************** + * GetNumaHighestNodeNumber (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetNumaHighestNodeNumber( ULONG *node ) +{ + FIXME( "semi-stub: %p\n", node ); + *node = 0; + return TRUE; +} + + +/********************************************************************** + * GetNumaNodeProcessorMaskEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetNumaNodeProcessorMaskEx( USHORT node, GROUP_AFFINITY *mask ) +{ + FIXME( "stub: %hu %p\n", node, mask ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * GetNumaProximityNodeEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetNumaProximityNodeEx( ULONG proximity_id, USHORT *node ) +{ + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * MapViewOfFileExNuma (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH MapViewOfFileExNuma( HANDLE handle, DWORD access, DWORD offset_high, + DWORD offset_low, SIZE_T count, LPVOID addr, + DWORD node ) +{ + if (node) FIXME( "Ignoring preferred node %lu\n", node ); + return MapViewOfFileEx( handle, access, offset_high, offset_low, count, addr ); +} + + +/*********************************************************************** + * VirtualAllocExNuma (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH VirtualAllocExNuma( HANDLE process, void *addr, SIZE_T size, + DWORD type, DWORD protect, DWORD node ) +{ + if (node) FIXME( "Ignoring preferred node %lu\n", node ); + return VirtualAllocEx( process, addr, size, type, protect ); +} + + +/*********************************************************************** + * QueryVirtualMemoryInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryVirtualMemoryInformation( HANDLE process, const void *addr, + WIN32_MEMORY_INFORMATION_CLASS info_class, void *info, SIZE_T size, SIZE_T *ret_size) +{ + switch (info_class) + { + case MemoryRegionInfo: + return set_ntstatus( NtQueryVirtualMemory( process, addr, MemoryRegionInformation, info, size, ret_size )); + default: + FIXME("Unsupported info class %u.\n", info_class); + return FALSE; + } +} + + +/*********************************************************************** + * CPU functions + ***********************************************************************/ + + +/*********************************************************************** + * InitializeContext2 (kernelbase.@) + */ +BOOL WINAPI InitializeContext2( void *buffer, DWORD context_flags, CONTEXT **context, DWORD *length, + ULONG64 compaction_mask ) +{ + ULONG orig_length; + NTSTATUS status; + + TRACE( "buffer %p, context_flags %#lx, context %p, ret_length %p, compaction_mask %s.\n", + buffer, context_flags, context, length, wine_dbgstr_longlong(compaction_mask) ); + + orig_length = *length; + + if ((status = RtlGetExtendedContextLength2( context_flags, length, compaction_mask ))) + { + if (status == STATUS_NOT_SUPPORTED && context_flags & 0x40) + { + context_flags &= ~0x40; + status = RtlGetExtendedContextLength2( context_flags, length, compaction_mask ); + } + + if (status) + return set_ntstatus( status ); + } + + if (!buffer || orig_length < *length) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + if ((status = RtlInitializeExtendedContext2( buffer, context_flags, (CONTEXT_EX **)context, compaction_mask ))) + return set_ntstatus( status ); + + *context = (CONTEXT *)((BYTE *)*context + (*(CONTEXT_EX **)context)->Legacy.Offset); + + return TRUE; +} + +/*********************************************************************** + * InitializeContext (kernelbase.@) + */ +BOOL WINAPI InitializeContext( void *buffer, DWORD context_flags, CONTEXT **context, DWORD *length ) +{ + return InitializeContext2( buffer, context_flags, context, length, ~(ULONG64)0 ); +} + +/*********************************************************************** + * CopyContext (kernelbase.@) + */ +BOOL WINAPI CopyContext( CONTEXT *dst, DWORD context_flags, CONTEXT *src ) +{ + return set_ntstatus( RtlCopyContext( dst, context_flags, src )); +} + + +#if defined(__x86_64__) + +/*********************************************************************** + * GetEnabledXStateFeatures (kernelbase.@) + */ +DWORD64 WINAPI GetEnabledXStateFeatures(void) +{ + TRACE( "\n" ); + return RtlGetEnabledExtendedFeatures( ~(ULONG64)0 ); +} + +/*********************************************************************** + * LocateXStateFeature (kernelbase.@) + */ +void * WINAPI LocateXStateFeature( CONTEXT *context, DWORD feature_id, DWORD *length ) +{ + if (!(context->ContextFlags & CONTEXT_AMD64)) + return NULL; + + if (feature_id >= 2) + return ((context->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE) + ? RtlLocateExtendedFeature( (CONTEXT_EX *)(context + 1), feature_id, length ) : NULL; + + if (feature_id == 1) + { + if (length) + *length = sizeof(M128A) * 16; + + return &context->FltSave.XmmRegisters; + } + + if (length) + *length = offsetof(XSAVE_FORMAT, XmmRegisters); + + return &context->FltSave; +} + +/*********************************************************************** + * SetXStateFeaturesMask (kernelbase.@) + */ +BOOL WINAPI SetXStateFeaturesMask( CONTEXT *context, DWORD64 feature_mask ) +{ + if (!(context->ContextFlags & CONTEXT_AMD64)) + return FALSE; + + if (feature_mask & 0x3) + context->ContextFlags |= CONTEXT_FLOATING_POINT; + + if ((context->ContextFlags & CONTEXT_XSTATE) != CONTEXT_XSTATE) + return !(feature_mask & ~(DWORD64)3); + + RtlSetExtendedFeaturesMask( (CONTEXT_EX *)(context + 1), feature_mask ); + return TRUE; +} + +/*********************************************************************** + * GetXStateFeaturesMask (kernelbase.@) + */ +BOOL WINAPI GetXStateFeaturesMask( CONTEXT *context, DWORD64 *feature_mask ) +{ + if (!(context->ContextFlags & CONTEXT_AMD64)) + return FALSE; + + *feature_mask = (context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT + ? 3 : 0; + + if ((context->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE) + *feature_mask |= RtlGetExtendedFeaturesMask( (CONTEXT_EX *)(context + 1) ); + + return TRUE; +} + +#elif defined(__i386__) + +/*********************************************************************** + * GetEnabledXStateFeatures (kernelbase.@) + */ +DWORD64 WINAPI GetEnabledXStateFeatures(void) +{ + TRACE( "\n" ); + return RtlGetEnabledExtendedFeatures( ~(ULONG64)0 ); +} + +/*********************************************************************** + * LocateXStateFeature (kernelbase.@) + */ +void * WINAPI LocateXStateFeature( CONTEXT *context, DWORD feature_id, DWORD *length ) +{ + if (!(context->ContextFlags & CONTEXT_i386)) + return NULL; + + if (feature_id >= 2) + return ((context->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE) + ? RtlLocateExtendedFeature( (CONTEXT_EX *)(context + 1), feature_id, length ) : NULL; + + if (feature_id == 1) + { + if (length) + *length = sizeof(M128A) * 8; + + return (BYTE *)&context->ExtendedRegisters + offsetof(XSAVE_FORMAT, XmmRegisters); + } + + if (length) + *length = offsetof(XSAVE_FORMAT, XmmRegisters); + + return &context->ExtendedRegisters; +} + +/*********************************************************************** + * SetXStateFeaturesMask (kernelbase.@) + */ +BOOL WINAPI SetXStateFeaturesMask( CONTEXT *context, DWORD64 feature_mask ) +{ + if (!(context->ContextFlags & CONTEXT_i386)) + return FALSE; + + if (feature_mask & 0x3) + context->ContextFlags |= CONTEXT_EXTENDED_REGISTERS; + + if ((context->ContextFlags & CONTEXT_XSTATE) != CONTEXT_XSTATE) + return !(feature_mask & ~(DWORD64)3); + + RtlSetExtendedFeaturesMask( (CONTEXT_EX *)(context + 1), feature_mask ); + return TRUE; +} + +/*********************************************************************** + * GetXStateFeaturesMask (kernelbase.@) + */ +BOOL WINAPI GetXStateFeaturesMask( CONTEXT *context, DWORD64 *feature_mask ) +{ + if (!(context->ContextFlags & CONTEXT_i386)) + return FALSE; + + *feature_mask = (context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS + ? 3 : 0; + + if ((context->ContextFlags & CONTEXT_XSTATE) == CONTEXT_XSTATE) + *feature_mask |= RtlGetExtendedFeaturesMask( (CONTEXT_EX *)(context + 1) ); + + return TRUE; +} +#endif + +/*********************************************************************** + * Firmware functions + ***********************************************************************/ + + +/*********************************************************************** + * EnumSystemFirmwareTable (kernelbase.@) + */ +UINT WINAPI EnumSystemFirmwareTables( DWORD provider, void *buffer, DWORD size ) +{ + FIXME( "(0x%08lx, %p, %ld)\n", provider, buffer, size ); + return 0; +} + + +/*********************************************************************** + * GetSystemFirmwareTable (kernelbase.@) + */ +UINT WINAPI GetSystemFirmwareTable( DWORD provider, DWORD id, void *buffer, DWORD size ) +{ + SYSTEM_FIRMWARE_TABLE_INFORMATION *info; + ULONG buffer_size = offsetof( SYSTEM_FIRMWARE_TABLE_INFORMATION, TableBuffer ) + size; + + TRACE( "(0x%08lx, 0x%08lx, %p, %ld)\n", provider, id, buffer, size ); + + if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, buffer_size ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + return 0; + } + + info->ProviderSignature = provider; + info->Action = SystemFirmwareTable_Get; + info->TableID = id; + + set_ntstatus( NtQuerySystemInformation( SystemFirmwareTableInformation, + info, buffer_size, &buffer_size )); + buffer_size -= offsetof( SYSTEM_FIRMWARE_TABLE_INFORMATION, TableBuffer ); + if (buffer_size <= size) memcpy( buffer, info->TableBuffer, buffer_size ); + + HeapFree( GetProcessHeap(), 0, info ); + return buffer_size; +} diff --git a/dll/win32/KernelBase/wine/path.c b/dll/win32/KernelBase/wine/path.c new file mode 100644 index 0000000000000..244b65d1755e6 --- /dev/null +++ b/dll/win32/KernelBase/wine/path.c @@ -0,0 +1,5249 @@ +/* + * Copyright 2018 Nikolay Sivov + * Copyright 2018 Zhiyi Zhang + * Copyright 2021-2023 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "pathcch.h" +#include "strsafe.h" +#include "shlwapi.h" +#include "wininet.h" +#include "intshcut.h" +#include "winternl.h" + +#include "kernelbase.h" +#include "wine/exception.h" +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(path); + +static const char hexDigits[] = "0123456789ABCDEF"; + +static const unsigned char hashdata_lookup[256] = +{ + 0x01, 0x0e, 0x6e, 0x19, 0x61, 0xae, 0x84, 0x77, 0x8a, 0xaa, 0x7d, 0x76, 0x1b, 0xe9, 0x8c, 0x33, + 0x57, 0xc5, 0xb1, 0x6b, 0xea, 0xa9, 0x38, 0x44, 0x1e, 0x07, 0xad, 0x49, 0xbc, 0x28, 0x24, 0x41, + 0x31, 0xd5, 0x68, 0xbe, 0x39, 0xd3, 0x94, 0xdf, 0x30, 0x73, 0x0f, 0x02, 0x43, 0xba, 0xd2, 0x1c, + 0x0c, 0xb5, 0x67, 0x46, 0x16, 0x3a, 0x4b, 0x4e, 0xb7, 0xa7, 0xee, 0x9d, 0x7c, 0x93, 0xac, 0x90, + 0xb0, 0xa1, 0x8d, 0x56, 0x3c, 0x42, 0x80, 0x53, 0x9c, 0xf1, 0x4f, 0x2e, 0xa8, 0xc6, 0x29, 0xfe, + 0xb2, 0x55, 0xfd, 0xed, 0xfa, 0x9a, 0x85, 0x58, 0x23, 0xce, 0x5f, 0x74, 0xfc, 0xc0, 0x36, 0xdd, + 0x66, 0xda, 0xff, 0xf0, 0x52, 0x6a, 0x9e, 0xc9, 0x3d, 0x03, 0x59, 0x09, 0x2a, 0x9b, 0x9f, 0x5d, + 0xa6, 0x50, 0x32, 0x22, 0xaf, 0xc3, 0x64, 0x63, 0x1a, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xbd, + 0x79, 0x40, 0x4d, 0x48, 0xd0, 0xf5, 0x82, 0x7a, 0x8f, 0x37, 0x69, 0x86, 0x1d, 0xa4, 0xb9, 0xc2, + 0xc1, 0xef, 0x65, 0xf2, 0x05, 0xab, 0x7e, 0x0b, 0x4a, 0x3b, 0x89, 0xe4, 0x6c, 0xbf, 0xe8, 0x8b, + 0x06, 0x18, 0x51, 0x14, 0x7f, 0x11, 0x5b, 0x5c, 0xfb, 0x97, 0xe1, 0xcf, 0x15, 0x62, 0x71, 0x70, + 0x54, 0xe2, 0x12, 0xd6, 0xc7, 0xbb, 0x0d, 0x20, 0x5e, 0xdc, 0xe0, 0xd4, 0xf7, 0xcc, 0xc4, 0x2b, + 0xf9, 0xec, 0x2d, 0xf4, 0x6f, 0xb6, 0x99, 0x88, 0x81, 0x5a, 0xd9, 0xca, 0x13, 0xa5, 0xe7, 0x47, + 0xe6, 0x8e, 0x60, 0xe3, 0x3e, 0xb3, 0xf6, 0x72, 0xa2, 0x35, 0xa0, 0xd7, 0xcd, 0xb4, 0x2f, 0x6d, + 0x2c, 0x26, 0x1f, 0x95, 0x87, 0x00, 0xd8, 0x34, 0x3f, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xb8, + 0xa3, 0xc8, 0xde, 0xeb, 0xf8, 0xf3, 0xdb, 0x0a, 0x98, 0x83, 0x7b, 0xe5, 0xcb, 0x4c, 0x78, 0xd1, +}; + +struct parsed_url +{ + const WCHAR *scheme; /* [out] start of scheme */ + DWORD scheme_len; /* [out] size of scheme (until colon) */ + const WCHAR *username; /* [out] start of Username */ + DWORD username_len; /* [out] size of Username (until ":" or "@") */ + const WCHAR *password; /* [out] start of Password */ + DWORD password_len; /* [out] size of Password (until "@") */ + const WCHAR *hostname; /* [out] start of Hostname */ + DWORD hostname_len; /* [out] size of Hostname (until ":" or "/") */ + const WCHAR *port; /* [out] start of Port */ + DWORD port_len; /* [out] size of Port (until "/" or eos) */ + const WCHAR *query; /* [out] start of Query */ + DWORD query_len; /* [out] size of Query (until eos) */ + DWORD scheme_number; +}; + +static WCHAR *heap_strdupAtoW(const char *str) +{ + WCHAR *ret = NULL; + + if (str) + { + DWORD len; + + len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + ret = heap_alloc(len * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); + } + + return ret; +} + +static bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) +{ + unsigned int new_capacity, max_capacity; + void *new_elements; + + if (count <= *capacity) + return true; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return false; + + new_capacity = max(4, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = max_capacity; + + if (!(new_elements = heap_realloc( *elements, new_capacity * size ))) + return false; + + *elements = new_elements; + *capacity = new_capacity; + + return true; +} + +static bool is_slash( char c ) +{ + return c == '/' || c == '\\'; +} + +static BOOL is_drive_specA( const char *str ) +{ + return isalpha( str[0] ) && str[1] == ':'; +} + +static BOOL is_drive_spec( const WCHAR *str ) +{ + return isalpha( str[0] ) && str[1] == ':'; +} + +static BOOL is_escaped_drive_spec( const WCHAR *str ) +{ + return isalpha( str[0] ) && (str[1] == ':' || str[1] == '|'); +} + +static BOOL is_prefixed_unc(const WCHAR *string) +{ + return !wcsnicmp(string, L"\\\\?\\UNC\\", 8 ); +} + +static BOOL is_prefixed_disk(const WCHAR *string) +{ + return !wcsncmp(string, L"\\\\?\\", 4) && is_drive_spec( string + 4 ); +} + +static BOOL is_prefixed_volume(const WCHAR *string) +{ + const WCHAR *guid; + INT i = 0; + + if (wcsnicmp( string, L"\\\\?\\Volume", 10 )) return FALSE; + + guid = string + 10; + + while (i <= 37) + { + switch (i) + { + case 0: + if (guid[i] != '{') return FALSE; + break; + case 9: + case 14: + case 19: + case 24: + if (guid[i] != '-') return FALSE; + break; + case 37: + if (guid[i] != '}') return FALSE; + break; + default: + if (!isxdigit(guid[i])) return FALSE; + break; + } + i++; + } + + return TRUE; +} + +/* Get the next character beyond end of the segment. + Return TRUE if the last segment ends with a backslash */ +static BOOL get_next_segment(const WCHAR *next, const WCHAR **next_segment) +{ + while (*next && *next != '\\') next++; + if (*next == '\\') + { + *next_segment = next + 1; + return TRUE; + } + else + { + *next_segment = next; + return FALSE; + } +} + +/* Find the last character of the root in a path, if there is one, without any segments */ +static const WCHAR *get_root_end(const WCHAR *path) +{ + /* Find path root */ + if (is_prefixed_volume(path)) + return path[48] == '\\' ? path + 48 : path + 47; + else if (is_prefixed_unc(path)) + return path + 7; + else if (is_prefixed_disk(path)) + return path[6] == '\\' ? path + 6 : path + 5; + /* \\ */ + else if (path[0] == '\\' && path[1] == '\\') + return path + 1; + /* \ */ + else if (path[0] == '\\') + return path; + /* X:\ */ + else if (is_drive_spec( path )) + return path[2] == '\\' ? path + 2 : path + 1; + else + return NULL; +} + +HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out) +{ + WCHAR *buffer, *dst; + const WCHAR *src; + const WCHAR *root_end; + SIZE_T buffer_size, length; + + TRACE("%s %#lx %p\n", debugstr_w(path_in), flags, path_out); + + if (!path_in || !path_out + || ((flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS) && (flags & PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS)) + || (flags & (PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS) + && !(flags & PATHCCH_ALLOW_LONG_PATHS)) + || ((flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) && (flags & PATHCCH_ALLOW_LONG_PATHS))) + { + if (path_out) *path_out = NULL; + return E_INVALIDARG; + } + + length = lstrlenW(path_in); + if ((length + 1 > MAX_PATH && !(flags & (PATHCCH_ALLOW_LONG_PATHS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH))) + || (length + 1 > PATHCCH_MAX_CCH)) + { + *path_out = NULL; + return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); + } + + /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH implies PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */ + if (flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) flags |= PATHCCH_DO_NOT_NORMALIZE_SEGMENTS; + + /* path length + possible \\?\ addition + possible \ addition + NUL */ + buffer_size = (length + 6) * sizeof(WCHAR); + buffer = LocalAlloc(LMEM_ZEROINIT, buffer_size); + if (!buffer) + { + *path_out = NULL; + return E_OUTOFMEMORY; + } + + src = path_in; + dst = buffer; + + root_end = get_root_end(path_in); + if (root_end) root_end = buffer + (root_end - path_in); + + /* Copy path root */ + if (root_end) + { + memcpy(dst, src, (root_end - buffer + 1) * sizeof(WCHAR)); + src += root_end - buffer + 1; + if(PathCchStripPrefix(dst, length + 6) == S_OK) + { + /* Fill in \ in X:\ if the \ is missing */ + if (is_drive_spec( dst ) && dst[2]!= '\\') + { + dst[2] = '\\'; + dst[3] = 0; + } + dst = buffer + lstrlenW(buffer); + root_end = dst; + } + else + dst += root_end - buffer + 1; + } + + while (*src) + { + if (src[0] == '.') + { + if (src[1] == '.') + { + /* Keep one . after * */ + if (dst > buffer && dst[-1] == '*') + { + *dst++ = *src++; + continue; + } + + /* Keep the .. if not surrounded by \ */ + if ((src[2] != '\\' && src[2]) || (dst > buffer && dst[-1] != '\\')) + { + *dst++ = *src++; + *dst++ = *src++; + continue; + } + + /* Remove the \ before .. if the \ is not part of root */ + if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end)) + { + *--dst = '\0'; + /* Remove characters until a \ is encountered */ + while (dst > buffer) + { + if (dst[-1] == '\\') + { + *--dst = 0; + break; + } + else + *--dst = 0; + } + } + /* Remove the extra \ after .. if the \ before .. wasn't deleted */ + else if (src[2] == '\\') + src++; + + src += 2; + } + else + { + /* Keep the . if not surrounded by \ */ + if ((src[1] != '\\' && src[1]) || (dst > buffer && dst[-1] != '\\')) + { + *dst++ = *src++; + continue; + } + + /* Remove the \ before . if the \ is not part of root */ + if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end)) dst--; + /* Remove the extra \ after . if the \ before . wasn't deleted */ + else if (src[1] == '\\') + src++; + + src++; + } + + /* If X:\ is not complete, then complete it */ + if (is_drive_spec( buffer ) && buffer[2] != '\\') + { + root_end = buffer + 2; + dst = buffer + 3; + buffer[2] = '\\'; + /* If next character is \, use the \ to fill in */ + if (src[0] == '\\') src++; + } + } + /* Copy over */ + else + *dst++ = *src++; + } + /* End the path */ + *dst = 0; + + /* Strip multiple trailing . */ + if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS)) + { + while (dst > buffer && dst[-1] == '.') + { + /* Keep a . after * */ + if (dst - 1 > buffer && dst[-2] == '*') + break; + /* If . follow a : at the second character, remove the . and add a \ */ + else if (dst - 1 > buffer && dst[-2] == ':' && dst - 2 == buffer + 1) + *--dst = '\\'; + else + *--dst = 0; + } + } + + /* If result path is empty, fill in \ */ + if (!*buffer) + { + buffer[0] = '\\'; + buffer[1] = 0; + } + + /* Extend the path if needed */ + length = lstrlenW(buffer); + if (((length + 1 > MAX_PATH && is_drive_spec( buffer )) + || (is_drive_spec( buffer ) && flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH)) + && !(flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS)) + { + memmove(buffer + 4, buffer, (length + 1) * sizeof(WCHAR)); + buffer[0] = '\\'; + buffer[1] = '\\'; + buffer[2] = '?'; + buffer[3] = '\\'; + } + + /* Add a trailing backslash to the path if needed */ + if (flags & PATHCCH_ENSURE_TRAILING_SLASH) + PathCchAddBackslash(buffer, buffer_size); + + *path_out = buffer; + return S_OK; +} + +HRESULT WINAPI PathAllocCombine(const WCHAR *path1, const WCHAR *path2, DWORD flags, WCHAR **out) +{ + SIZE_T combined_length, length2; + WCHAR *combined_path; + BOOL add_backslash = FALSE; + HRESULT hr; + + TRACE("%s %s %#lx %p\n", wine_dbgstr_w(path1), wine_dbgstr_w(path2), flags, out); + + if ((!path1 && !path2) || !out) + { + if (out) *out = NULL; + return E_INVALIDARG; + } + + if (!path1 || !path2) return PathAllocCanonicalize(path1 ? path1 : path2, flags, out); + + /* If path2 is fully qualified, use path2 only */ + if (is_drive_spec( path2 ) || (path2[0] == '\\' && path2[1] == '\\')) + { + path1 = path2; + path2 = NULL; + add_backslash = (is_drive_spec(path1) && !path1[2]) + || (is_prefixed_disk(path1) && !path1[6]); + } + + length2 = path2 ? lstrlenW(path2) : 0; + /* path1 length + path2 length + possible backslash + NULL */ + combined_length = lstrlenW(path1) + length2 + 2; + + combined_path = HeapAlloc(GetProcessHeap(), 0, combined_length * sizeof(WCHAR)); + if (!combined_path) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + lstrcpyW(combined_path, path1); + PathCchStripPrefix(combined_path, combined_length); + if (add_backslash) PathCchAddBackslashEx(combined_path, combined_length, NULL, NULL); + + if (path2 && path2[0]) + { + if (path2[0] == '\\' && path2[1] != '\\') + { + PathCchStripToRoot(combined_path, combined_length); + path2++; + } + + PathCchAddBackslashEx(combined_path, combined_length, NULL, NULL); + lstrcatW(combined_path, path2); + } + + hr = PathAllocCanonicalize(combined_path, flags, out); + HeapFree(GetProcessHeap(), 0, combined_path); + return hr; +} + +HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size) +{ + return PathCchAddBackslashEx(path, size, NULL, NULL); +} + +HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **endptr, SIZE_T *remaining) +{ + BOOL needs_termination; + SIZE_T length; + + TRACE("%s, %Iu, %p, %p\n", debugstr_w(path), size, endptr, remaining); + + length = lstrlenW(path); + needs_termination = size && length && path[length - 1] != '\\'; + + if (length >= (needs_termination ? size - 1 : size)) + { + if (endptr) *endptr = NULL; + if (remaining) *remaining = 0; + return STRSAFE_E_INSUFFICIENT_BUFFER; + } + + if (!needs_termination) + { + if (endptr) *endptr = path + length; + if (remaining) *remaining = size - length; + return S_FALSE; + } + + path[length++] = '\\'; + path[length] = 0; + + if (endptr) *endptr = path + length; + if (remaining) *remaining = size - length; + + return S_OK; +} + +HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extension) +{ + const WCHAR *existing_extension, *next; + SIZE_T path_length, extension_length, dot_length; + BOOL has_dot; + HRESULT hr; + + TRACE("%s %Iu %s\n", wine_dbgstr_w(path), size, wine_dbgstr_w(extension)); + + if (!path || !size || size > PATHCCH_MAX_CCH || !extension) return E_INVALIDARG; + + next = extension; + while (*next) + { + if ((*next == '.' && next > extension) || *next == ' ' || *next == '\\') return E_INVALIDARG; + next++; + } + + has_dot = extension[0] == '.'; + + hr = PathCchFindExtension(path, size, &existing_extension); + if (FAILED(hr)) return hr; + if (*existing_extension) return S_FALSE; + + path_length = wcsnlen(path, size); + dot_length = has_dot ? 0 : 1; + extension_length = lstrlenW(extension); + + if (path_length + dot_length + extension_length + 1 > size) return STRSAFE_E_INSUFFICIENT_BUFFER; + + /* If extension is empty or only dot, return S_OK with path unchanged */ + if (!extension[0] || (extension[0] == '.' && !extension[1])) return S_OK; + + if (!has_dot) + { + path[path_length] = '.'; + path_length++; + } + + lstrcpyW(path + path_length, extension); + return S_OK; +} + +HRESULT WINAPI PathCchAppend(WCHAR *path1, SIZE_T size, const WCHAR *path2) +{ + TRACE("%s %Iu %s\n", wine_dbgstr_w(path1), size, wine_dbgstr_w(path2)); + + return PathCchAppendEx(path1, size, path2, PATHCCH_NONE); +} + +HRESULT WINAPI PathCchAppendEx(WCHAR *path1, SIZE_T size, const WCHAR *path2, DWORD flags) +{ + HRESULT hr; + WCHAR *result; + + TRACE("%s %Iu %s %#lx\n", wine_dbgstr_w(path1), size, wine_dbgstr_w(path2), flags); + + if (!path1 || !size) return E_INVALIDARG; + + /* Create a temporary buffer for result because we need to keep path1 unchanged if error occurs. + * And PathCchCombineEx writes empty result if there is error so we can't just use path1 as output + * buffer for PathCchCombineEx */ + result = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); + if (!result) return E_OUTOFMEMORY; + + /* Avoid the single backslash behavior with PathCchCombineEx when appending */ + if (path2 && path2[0] == '\\' && path2[1] != '\\') path2++; + + hr = PathCchCombineEx(result, size, path1, path2, flags); + if (SUCCEEDED(hr)) memcpy(path1, result, size * sizeof(WCHAR)); + + HeapFree(GetProcessHeap(), 0, result); + return hr; +} + +HRESULT WINAPI PathCchCanonicalize(WCHAR *out, SIZE_T size, const WCHAR *in) +{ + TRACE("%p %Iu %s\n", out, size, wine_dbgstr_w(in)); + + /* Not X:\ and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */ + if (lstrlenW(in) > MAX_PATH - 4 && !(is_drive_spec( in ) && in[2] == '\\')) + return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); + + return PathCchCanonicalizeEx(out, size, in, PATHCCH_NONE); +} + +HRESULT WINAPI PathCchCanonicalizeEx(WCHAR *out, SIZE_T size, const WCHAR *in, DWORD flags) +{ + WCHAR *buffer; + SIZE_T length; + HRESULT hr; + + TRACE("%p %Iu %s %#lx\n", out, size, wine_dbgstr_w(in), flags); + + if (!size) return E_INVALIDARG; + + hr = PathAllocCanonicalize(in, flags, &buffer); + if (FAILED(hr)) return hr; + + length = lstrlenW(buffer); + if (size < length + 1) + { + /* No root and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */ + if (length > MAX_PATH - 4 && !(in[0] == '\\' || (is_drive_spec( in ) && in[2] == '\\'))) + hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); + else + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + + if (SUCCEEDED(hr)) + { + memcpy(out, buffer, (length + 1) * sizeof(WCHAR)); + + /* Fill a backslash at the end of X: */ + if (is_drive_spec( out ) && !out[2] && size > 3) + { + out[2] = '\\'; + out[3] = 0; + } + } + + LocalFree(buffer); + return hr; +} + +HRESULT WINAPI PathCchCombine(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2) +{ + TRACE("%p %s %s\n", out, wine_dbgstr_w(path1), wine_dbgstr_w(path2)); + + return PathCchCombineEx(out, size, path1, path2, PATHCCH_NONE); +} + +HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags) +{ + HRESULT hr; + WCHAR *buffer; + SIZE_T length; + + TRACE("%p %s %s %#lx\n", out, wine_dbgstr_w(path1), wine_dbgstr_w(path2), flags); + + if (!out || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + hr = PathAllocCombine(path1, path2, flags, &buffer); + if (FAILED(hr)) + { + out[0] = 0; + return hr; + } + + length = lstrlenW(buffer); + if (length + 1 > size) + { + out[0] = 0; + LocalFree(buffer); + return STRSAFE_E_INSUFFICIENT_BUFFER; + } + else + { + memcpy(out, buffer, (length + 1) * sizeof(WCHAR)); + LocalFree(buffer); + return S_OK; + } +} + +HRESULT WINAPI PathCchFindExtension(const WCHAR *path, SIZE_T size, const WCHAR **extension) +{ + const WCHAR *lastpoint = NULL; + SIZE_T counter = 0; + + TRACE("%s %Iu %p\n", wine_dbgstr_w(path), size, extension); + + if (!path || !size || size > PATHCCH_MAX_CCH) + { + *extension = NULL; + return E_INVALIDARG; + } + + while (*path) + { + if (*path == '\\' || *path == ' ') + lastpoint = NULL; + else if (*path == '.') + lastpoint = path; + + path++; + counter++; + if (counter == size || counter == PATHCCH_MAX_CCH) + { + *extension = NULL; + return E_INVALIDARG; + } + } + + *extension = lastpoint ? lastpoint : path; + return S_OK; +} + +BOOL WINAPI PathCchIsRoot(const WCHAR *path) +{ + const WCHAR *root_end; + const WCHAR *next; + BOOL is_unc; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path || !*path) return FALSE; + + root_end = get_root_end(path); + if (!root_end) return FALSE; + + if ((is_unc = is_prefixed_unc(path)) || (path[0] == '\\' && path[1] == '\\' && path[2] != '?')) + { + next = root_end + 1; + /* No extra segments */ + if ((is_unc && !*next) || (!is_unc && !*next)) return TRUE; + + /* Has first segment with an ending backslash but no remaining characters */ + if (get_next_segment(next, &next) && !*next) return FALSE; + /* Has first segment with no ending backslash */ + else if (!*next) + return TRUE; + /* Has first segment with an ending backslash and has remaining characters*/ + else + { + /* Second segment must have no backslash and no remaining characters */ + return !get_next_segment(next, &next) && !*next; + } + } + else if (*root_end == '\\' && !root_end[1]) + return TRUE; + else + return FALSE; +} + +HRESULT WINAPI PathCchRemoveBackslash(WCHAR *path, SIZE_T path_size) +{ + WCHAR *path_end; + SIZE_T free_size; + + TRACE("%s %Iu\n", debugstr_w(path), path_size); + + return PathCchRemoveBackslashEx(path, path_size, &path_end, &free_size); +} + +HRESULT WINAPI PathCchRemoveBackslashEx(WCHAR *path, SIZE_T path_size, WCHAR **path_end, SIZE_T *free_size) +{ + const WCHAR *root_end; + SIZE_T path_length; + + TRACE("%s %Iu %p %p\n", debugstr_w(path), path_size, path_end, free_size); + + if (!path_size || !path_end || !free_size) + { + if (path_end) *path_end = NULL; + if (free_size) *free_size = 0; + return E_INVALIDARG; + } + + path_length = wcsnlen(path, path_size); + if (path_length == path_size && !path[path_length]) return E_INVALIDARG; + + root_end = get_root_end(path); + if (path_length > 0 && path[path_length - 1] == '\\') + { + *path_end = path + path_length - 1; + *free_size = path_size - path_length + 1; + /* If the last character is beyond end of root */ + if (!root_end || path + path_length - 1 > root_end) + { + path[path_length - 1] = 0; + return S_OK; + } + else + return S_FALSE; + } + else + { + *path_end = path + path_length; + *free_size = path_size - path_length; + return S_FALSE; + } +} + +HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size) +{ + const WCHAR *extension; + WCHAR *next; + HRESULT hr; + + TRACE("%s %Iu\n", wine_dbgstr_w(path), size); + + if (!path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + hr = PathCchFindExtension(path, size, &extension); + if (FAILED(hr)) return hr; + + next = path + (extension - path); + while (next - path < size && *next) *next++ = 0; + + return next == extension ? S_FALSE : S_OK; +} + +HRESULT WINAPI PathCchRemoveFileSpec(WCHAR *path, SIZE_T size) +{ + WCHAR *last, *root_end; + + TRACE("%s %Iu\n", wine_dbgstr_w(path), size); + + if (!path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + if (FAILED(PathCchSkipRoot(path, (const WCHAR **)&root_end))) + root_end = path; + + /* The backslash at the end of UNC and \\* are not considered part of root in this case */ + if (root_end > path && root_end[-1] == '\\' && ((is_prefixed_unc(path) && path[8]) + || (path[0] == '\\' && path[1] == '\\' && path[2] && path[2] != '?'))) + root_end--; + + if (!(last = StrRChrW(root_end, NULL, '\\'))) last = root_end; + if (last > root_end && last[-1] == '\\' && last[1] != '?') --last; + if (last - path >= size) return E_INVALIDARG; + if (!*last) return S_FALSE; + *last = 0; + return S_OK; +} + +HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *extension) +{ + HRESULT hr; + + TRACE("%s %Iu %s\n", wine_dbgstr_w(path), size, wine_dbgstr_w(extension)); + + hr = PathCchRemoveExtension(path, size); + if (FAILED(hr)) return hr; + + hr = PathCchAddExtension(path, size, extension); + return FAILED(hr) ? hr : S_OK; +} + +HRESULT WINAPI PathCchSkipRoot(const WCHAR *path, const WCHAR **root_end) +{ + TRACE("%s %p\n", debugstr_w(path), root_end); + + if (!path || !path[0] || !root_end + || (!wcsnicmp(path, L"\\\\?", 3) && !is_prefixed_volume(path) && !is_prefixed_unc(path) + && !is_prefixed_disk(path))) + return E_INVALIDARG; + + *root_end = get_root_end(path); + if (*root_end) + { + (*root_end)++; + if (is_prefixed_unc(path)) + { + get_next_segment(*root_end, root_end); + get_next_segment(*root_end, root_end); + } + else if (path[0] == '\\' && path[1] == '\\' && path[2] != '?') + { + /* Skip share server */ + get_next_segment(*root_end, root_end); + /* If mount point is empty, don't skip over mount point */ + if (**root_end != '\\') get_next_segment(*root_end, root_end); + } + } + + return *root_end ? S_OK : E_INVALIDARG; +} + +HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size) +{ + TRACE("%s %Iu\n", wine_dbgstr_w(path), size); + + if (!path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + if (is_prefixed_unc(path)) + { + /* \\?\UNC\a -> \\a */ + if (size < lstrlenW(path + 8) + 3) return E_INVALIDARG; + lstrcpyW(path + 2, path + 8); + return S_OK; + } + else if (is_prefixed_disk(path)) + { + /* \\?\C:\ -> C:\ */ + if (size < lstrlenW(path + 4) + 1) return E_INVALIDARG; + lstrcpyW(path, path + 4); + return S_OK; + } + else + return S_FALSE; +} + +HRESULT WINAPI PathCchStripToRoot(WCHAR *path, SIZE_T size) +{ + const WCHAR *root_end; + WCHAR *segment_end; + + TRACE("%s %Iu\n", wine_dbgstr_w(path), size); + + if (!path || !*path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; + + if (PathCchSkipRoot(path, &root_end) == S_OK) + { + if (root_end && root_end > path && root_end[-1] == '\\' + && ((is_prefixed_unc(path) && path[8]) || (path[0] == '\\' && path[1] == '\\' && path[2] && path[2] != '?'))) + root_end--; + if (root_end - path >= size) return E_INVALIDARG; + + segment_end = path + (root_end - path); + if (!*segment_end) return S_FALSE; + + *segment_end = 0; + return S_OK; + } + else + { + *path = 0; + return E_INVALIDARG; + } +} + +BOOL WINAPI PathIsUNCEx(const WCHAR *path, const WCHAR **server) +{ + const WCHAR *result = NULL; + + TRACE("%s %p\n", wine_dbgstr_w(path), server); + + if (is_prefixed_unc(path)) + result = path + 8; + else if (path[0] == '\\' && path[1] == '\\' && path[2] != '?') + result = path + 2; + + if (server) *server = result; + return !!result; +} + +BOOL WINAPI PathIsUNCA(const char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + return path && (path[0] == '\\') && (path[1] == '\\'); +} + +BOOL WINAPI PathIsUNCW(const WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + return path && (path[0] == '\\') && (path[1] == '\\'); +} + +BOOL WINAPI PathIsRelativeA(const char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path || !*path || IsDBCSLeadByte(*path)) + return TRUE; + + return !(*path == '\\' || (*path && path[1] == ':')); +} + +BOOL WINAPI PathIsRelativeW(const WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path || !*path) + return TRUE; + + return !(*path == '\\' || (*path && path[1] == ':')); +} + +BOOL WINAPI PathIsUNCServerShareA(const char *path) +{ + BOOL seen_slash = FALSE; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (path && *path++ == '\\' && *path++ == '\\') + { + while (*path) + { + if (*path == '\\') + { + if (seen_slash) + return FALSE; + seen_slash = TRUE; + } + + path = CharNextA(path); + } + } + + return seen_slash; +} + +BOOL WINAPI PathIsUNCServerShareW(const WCHAR *path) +{ + BOOL seen_slash = FALSE; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (path && *path++ == '\\' && *path++ == '\\') + { + while (*path) + { + if (*path == '\\') + { + if (seen_slash) + return FALSE; + seen_slash = TRUE; + } + + path++; + } + } + + return seen_slash; +} + +BOOL WINAPI PathIsRootA(const char *path) +{ + WCHAR pathW[MAX_PATH]; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH)) + return FALSE; + if (is_prefixed_unc(pathW) || is_prefixed_disk(pathW) || is_prefixed_volume(pathW)) return FALSE; + + return PathIsRootW(pathW); +} + +BOOL WINAPI PathIsRootW(const WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + return PathCchIsRoot(path); +} + +BOOL WINAPI PathRemoveFileSpecA(char *path) +{ + char *root_end = NULL, *ptr; + + TRACE("%s\n", debugstr_a(path)); + + if (!path || !*path) + return FALSE; + + if (is_drive_specA(path)) + { + root_end = path + 2; + if (*root_end == '\\') ++root_end; + } + else + { + root_end = path; + if (*root_end == '\\') ++root_end; + if (root_end[1] != '?') + { + if (*root_end == '\\') ++root_end; + if (root_end - path > 1 && is_drive_specA(root_end)) root_end += 2; + if (*root_end == '\\' && root_end[1] && root_end[1] != '\\') ++root_end; + } + } + ptr = StrRChrA(root_end, NULL, '\\'); + if (ptr && ptr != root_end) + { + if (ptr[-1] == '\\') --ptr; + *ptr = 0; + return TRUE; + } + if (!*root_end) return FALSE; + *root_end = 0; + return TRUE; +} + +BOOL WINAPI PathRemoveFileSpecW(WCHAR *path) +{ + WCHAR *root_end = NULL, *ptr; + + TRACE("%s\n", debugstr_w(path)); + + if (!path || !*path) + return FALSE; + + if (is_prefixed_volume(path)) root_end = path + 48; + else if (is_prefixed_disk(path)) root_end = path + 6; + else if (is_drive_spec(path)) root_end = path + 2; + if (!root_end) + { + root_end = path; + if (*root_end == '\\') ++root_end; + if (root_end[1] != '?') + { + if (*root_end == '\\') ++root_end; + if (root_end - path > 1 && is_drive_spec(root_end)) root_end += 2; + if (*root_end == '\\' && root_end[1] && root_end[1] != '\\') ++root_end; + } + } + else if (*root_end == '\\') ++root_end; + ptr = StrRChrW(root_end, NULL, '\\'); + if (ptr && ptr != root_end) + { + if (ptr[-1] == '\\') --ptr; + *ptr = 0; + return TRUE; + } + if (!*root_end) return FALSE; + *root_end = 0; + return TRUE; +} + +BOOL WINAPI PathStripToRootA(char *path) +{ + WCHAR pathW[MAX_PATH]; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, MAX_PATH)) return FALSE; + + *path = 0; + if (is_prefixed_unc(pathW) || is_prefixed_disk(pathW) || is_prefixed_volume(pathW)) return FALSE; + if (!PathStripToRootW(pathW)) return FALSE; + return !!WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, MAX_PATH, 0, 0); +} + +BOOL WINAPI PathStripToRootW(WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + return SUCCEEDED(PathCchStripToRoot(path, PATHCCH_MAX_CCH)); +} + +LPSTR WINAPI PathAddBackslashA(char *path) +{ + unsigned int len; + char *prev = path; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path || (len = strlen(path)) >= MAX_PATH) + return NULL; + + if (len) + { + do + { + path = CharNextA(prev); + if (*path) + prev = path; + } while (*path); + + if (*prev != '\\') + { + *path++ = '\\'; + *path = '\0'; + } + } + + return path; +} + +LPWSTR WINAPI PathAddBackslashW(WCHAR *path) +{ + unsigned int len; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path || (len = lstrlenW(path)) >= MAX_PATH) + return NULL; + + if (len) + { + path += len; + if (path[-1] != '\\') + { + *path++ = '\\'; + *path = '\0'; + } + } + + return path; +} + +LPSTR WINAPI PathFindExtensionA(const char *path) +{ + const char *lastpoint = NULL; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (path) + { + while (*path) + { + if (*path == '\\' || *path == ' ') + lastpoint = NULL; + else if (*path == '.') + lastpoint = path; + path = CharNextA(path); + } + } + + return (LPSTR)(lastpoint ? lastpoint : path); +} + +LPWSTR WINAPI PathFindExtensionW(const WCHAR *path) +{ + const WCHAR *lastpoint = NULL; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (path) + { + while (*path) + { + if (*path == '\\' || *path == ' ') + lastpoint = NULL; + else if (*path == '.') + lastpoint = path; + path++; + } + } + + return (LPWSTR)(lastpoint ? lastpoint : path); +} + +BOOL WINAPI PathAddExtensionA(char *path, const char *ext) +{ + unsigned int len; + + TRACE("%s, %s\n", wine_dbgstr_a(path), wine_dbgstr_a(ext)); + + if (!path || !ext || *(PathFindExtensionA(path))) + return FALSE; + + len = strlen(path); + if (len + strlen(ext) >= MAX_PATH) + return FALSE; + + strcpy(path + len, ext); + return TRUE; +} + +BOOL WINAPI PathAddExtensionW(WCHAR *path, const WCHAR *ext) +{ + unsigned int len; + + TRACE("%s, %s\n", wine_dbgstr_w(path), wine_dbgstr_w(ext)); + + if (!path || !ext || *(PathFindExtensionW(path))) + return FALSE; + + len = lstrlenW(path); + if (len + lstrlenW(ext) >= MAX_PATH) + return FALSE; + + lstrcpyW(path + len, ext); + return TRUE; +} + +BOOL WINAPI PathCanonicalizeW(WCHAR *buffer, const WCHAR *path) +{ + const WCHAR *src = path; + WCHAR *dst = buffer; + + TRACE("%p, %s\n", buffer, wine_dbgstr_w(path)); + + if (dst) + *dst = '\0'; + + if (!dst || !path) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (!*path) + { + *buffer++ = '\\'; + *buffer = '\0'; + return TRUE; + } + + /* Copy path root */ + if (*src == '\\') + { + *dst++ = *src++; + } + else if (*src && src[1] == ':') + { + /* X:\ */ + *dst++ = *src++; + *dst++ = *src++; + if (*src == '\\') + *dst++ = *src++; + } + + /* Canonicalize the rest of the path */ + while (*src) + { + if (*src == '.') + { + if (src[1] == '\\' && (src == path || src[-1] == '\\' || src[-1] == ':')) + { + src += 2; /* Skip .\ */ + } + else if (src[1] == '.' && dst != buffer && dst[-1] == '\\') + { + /* \.. backs up a directory, over the root if it has no \ following X:. + * .. is ignored if it would remove a UNC server name or initial \\ + */ + if (dst != buffer) + { + *dst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */ + if (dst > buffer + 1 && dst[-1] == '\\' && (dst[-2] != '\\' || dst > buffer + 2)) + { + if (dst[-2] == ':' && (dst > buffer + 3 || dst[-3] == ':')) + { + dst -= 2; + while (dst > buffer && *dst != '\\') + dst--; + if (*dst == '\\') + dst++; /* Reset to last '\' */ + else + dst = buffer; /* Start path again from new root */ + } + else if (dst[-2] != ':' && !PathIsUNCServerShareW(buffer)) + dst -= 2; + } + while (dst > buffer && *dst != '\\') + dst--; + if (dst == buffer) + { + *dst++ = '\\'; + src++; + } + } + src += 2; /* Skip .. in src path */ + } + else + *dst++ = *src++; + } + else + *dst++ = *src++; + } + + /* Append \ to naked drive specs */ + if (dst - buffer == 2 && dst[-1] == ':') + *dst++ = '\\'; + *dst++ = '\0'; + return TRUE; +} + +BOOL WINAPI PathCanonicalizeA(char *buffer, const char *path) +{ + WCHAR pathW[MAX_PATH], bufferW[MAX_PATH]; + BOOL ret; + int len; + + TRACE("%p, %s\n", buffer, wine_dbgstr_a(path)); + + if (buffer) + *buffer = '\0'; + + if (!buffer || !path) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + len = MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, ARRAY_SIZE(pathW)); + if (!len) + return FALSE; + + ret = PathCanonicalizeW(bufferW, pathW); + WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, 0, 0); + + return ret; +} + +WCHAR * WINAPI PathCombineW(WCHAR *dst, const WCHAR *dir, const WCHAR *file) +{ + BOOL use_both = FALSE, strip = FALSE; + WCHAR tmp[MAX_PATH]; + + TRACE("%p, %s, %s\n", dst, wine_dbgstr_w(dir), wine_dbgstr_w(file)); + + /* Invalid parameters */ + if (!dst) + return NULL; + + if (!dir && !file) + { + dst[0] = 0; + return NULL; + } + + if ((!file || !*file) && dir) + { + /* Use dir only */ + lstrcpynW(tmp, dir, ARRAY_SIZE(tmp)); + } + else if (!dir || !*dir || !PathIsRelativeW(file)) + { + if (!dir || !*dir || *file != '\\' || PathIsUNCW(file)) + { + /* Use file only */ + lstrcpynW(tmp, file, ARRAY_SIZE(tmp)); + } + else + { + use_both = TRUE; + strip = TRUE; + } + } + else + use_both = TRUE; + + if (use_both) + { + lstrcpynW(tmp, dir, ARRAY_SIZE(tmp)); + if (strip) + { + PathStripToRootW(tmp); + file++; /* Skip '\' */ + } + + if (!PathAddBackslashW(tmp) || lstrlenW(tmp) + lstrlenW(file) >= MAX_PATH) + { + dst[0] = 0; + return NULL; + } + + lstrcatW(tmp, file); + } + + PathCanonicalizeW(dst, tmp); + return dst; +} + +LPSTR WINAPI PathCombineA(char *dst, const char *dir, const char *file) +{ + WCHAR dstW[MAX_PATH], dirW[MAX_PATH], fileW[MAX_PATH]; + + TRACE("%p, %s, %s\n", dst, wine_dbgstr_a(dir), wine_dbgstr_a(file)); + + /* Invalid parameters */ + if (!dst) + return NULL; + + if (!dir && !file) + goto fail; + + if (dir && !MultiByteToWideChar(CP_ACP, 0, dir, -1, dirW, ARRAY_SIZE(dirW))) + goto fail; + + if (file && !MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, ARRAY_SIZE(fileW))) + goto fail; + + if (PathCombineW(dstW, dir ? dirW : NULL, file ? fileW : NULL)) + if (WideCharToMultiByte(CP_ACP, 0, dstW, -1, dst, MAX_PATH, 0, 0)) + return dst; +fail: + dst[0] = 0; + return NULL; +} + +BOOL WINAPI PathAppendA(char *path, const char *append) +{ + TRACE("%s, %s\n", wine_dbgstr_a(path), wine_dbgstr_a(append)); + + if (path && append) + { + if (!PathIsUNCA(append)) + while (*append == '\\') + append++; + + if (PathCombineA(path, path, append)) + return TRUE; + } + + return FALSE; +} + +BOOL WINAPI PathAppendW(WCHAR *path, const WCHAR *append) +{ + TRACE("%s, %s\n", wine_dbgstr_w(path), wine_dbgstr_w(append)); + + if (path && append) + { + if (!PathIsUNCW(append)) + while (*append == '\\') + append++; + + if (PathCombineW(path, path, append)) + return TRUE; + } + + return FALSE; +} + +int WINAPI PathCommonPrefixA(const char *file1, const char *file2, char *path) +{ + const char *iter1 = file1; + const char *iter2 = file2; + unsigned int len = 0; + + TRACE("%s, %s, %p.\n", wine_dbgstr_a(file1), wine_dbgstr_a(file2), path); + + if (path) + *path = '\0'; + + if (!file1 || !file2) + return 0; + + /* Handle roots first */ + if (PathIsUNCA(file1)) + { + if (!PathIsUNCA(file2)) + return 0; + iter1 += 2; + iter2 += 2; + } + else if (PathIsUNCA(file2)) + return 0; + + for (;;) + { + /* Update len */ + if ((!*iter1 || *iter1 == '\\') && (!*iter2 || *iter2 == '\\')) + len = iter1 - file1; /* Common to this point */ + + if (!*iter1 || (tolower(*iter1) != tolower(*iter2))) + break; /* Strings differ at this point */ + + iter1++; + iter2++; + } + + if (len == 2) + len++; /* Feature/Bug compatible with Win32 */ + + if (len && path) + { + memcpy(path, file1, len); + path[len] = '\0'; + } + + return len; +} + +int WINAPI PathCommonPrefixW(const WCHAR *file1, const WCHAR *file2, WCHAR *path) +{ + const WCHAR *iter1 = file1; + const WCHAR *iter2 = file2; + unsigned int len = 0; + + TRACE("%s, %s, %p\n", wine_dbgstr_w(file1), wine_dbgstr_w(file2), path); + + if (path) + *path = '\0'; + + if (!file1 || !file2) + return 0; + + /* Handle roots first */ + if (PathIsUNCW(file1)) + { + if (!PathIsUNCW(file2)) + return 0; + iter1 += 2; + iter2 += 2; + } + else if (PathIsUNCW(file2)) + return 0; + + for (;;) + { + /* Update len */ + if ((!*iter1 || *iter1 == '\\') && (!*iter2 || *iter2 == '\\')) + len = iter1 - file1; /* Common to this point */ + + if (!*iter1 || (towupper(*iter1) != towupper(*iter2))) + break; /* Strings differ at this point */ + + iter1++; + iter2++; + } + + if (len == 2) + len++; /* Feature/Bug compatible with Win32 */ + + if (len && path) + { + memcpy(path, file1, len * sizeof(WCHAR)); + path[len] = '\0'; + } + + return len; +} + +BOOL WINAPI PathIsPrefixA(const char *prefix, const char *path) +{ + TRACE("%s, %s\n", wine_dbgstr_a(prefix), wine_dbgstr_a(path)); + + return prefix && path && PathCommonPrefixA(path, prefix, NULL) == (int)strlen(prefix); +} + +BOOL WINAPI PathIsPrefixW(const WCHAR *prefix, const WCHAR *path) +{ + TRACE("%s, %s\n", wine_dbgstr_w(prefix), wine_dbgstr_w(path)); + + return prefix && path && PathCommonPrefixW(path, prefix, NULL) == (int)lstrlenW(prefix); +} + +char * WINAPI PathFindFileNameA(const char *path) +{ + const char *last_slash = path; + + TRACE("%s\n", wine_dbgstr_a(path)); + + while (path && *path) + { + if ((*path == '\\' || *path == '/' || *path == ':') && + path[1] && path[1] != '\\' && path[1] != '/') + last_slash = path + 1; + path = CharNextA(path); + } + + return (char *)last_slash; +} + +WCHAR * WINAPI PathFindFileNameW(const WCHAR *path) +{ + const WCHAR *last_slash = path; + + TRACE("%s\n", wine_dbgstr_w(path)); + + while (path && *path) + { + if ((*path == '\\' || *path == '/' || *path == ':') && + path[1] && path[1] != '\\' && path[1] != '/') + last_slash = path + 1; + path++; + } + + return (WCHAR *)last_slash; +} + +char * WINAPI PathGetArgsA(const char *path) +{ + BOOL seen_quote = FALSE; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path) + return NULL; + + while (*path) + { + if (*path == ' ' && !seen_quote) + return (char *)path + 1; + + if (*path == '"') + seen_quote = !seen_quote; + path = CharNextA(path); + } + + return (char *)path; +} + +WCHAR * WINAPI PathGetArgsW(const WCHAR *path) +{ + BOOL seen_quote = FALSE; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path) + return NULL; + + while (*path) + { + if (*path == ' ' && !seen_quote) + return (WCHAR *)path + 1; + + if (*path == '"') + seen_quote = !seen_quote; + path++; + } + + return (WCHAR *)path; +} + +UINT WINAPI PathGetCharTypeW(WCHAR ch) +{ + UINT flags = 0; + + TRACE("%#x\n", ch); + + if (!ch || ch < ' ' || ch == '<' || ch == '>' || ch == '"' || ch == '|' || ch == '/') + flags = GCT_INVALID; /* Invalid */ + else if (ch == '*' || ch == '?') + flags = GCT_WILD; /* Wildchars */ + else if (ch == '\\' || ch == ':') + return GCT_SEPARATOR; /* Path separators */ + else + { + if (ch < 126) + { + if (((ch & 0x1) && ch != ';') || !ch || iswalnum(ch) || ch == '$' || ch == '&' || ch == '(' || + ch == '.' || ch == '@' || ch == '^' || ch == '\'' || ch == '`') + { + flags |= GCT_SHORTCHAR; /* All these are valid for DOS */ + } + } + else + flags |= GCT_SHORTCHAR; /* Bug compatible with win32 */ + + flags |= GCT_LFNCHAR; /* Valid for long file names */ + } + + return flags; +} + +UINT WINAPI PathGetCharTypeA(UCHAR ch) +{ + return PathGetCharTypeW(ch); +} + +int WINAPI PathGetDriveNumberA(const char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + if (path && *path && path[1] == ':') + { + if (*path >= 'a' && *path <= 'z') return *path - 'a'; + if (*path >= 'A' && *path <= 'Z') return *path - 'A'; + } + return -1; +} + +int WINAPI PathGetDriveNumberW(const WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path) + return -1; + + if (!wcsncmp(path, L"\\\\?\\", 4)) path += 4; + + if (!path[0] || path[1] != ':') return -1; + if (path[0] >= 'A' && path[0] <= 'Z') return path[0] - 'A'; + if (path[0] >= 'a' && path[0] <= 'z') return path[0] - 'a'; + return -1; +} + +BOOL WINAPI PathIsFileSpecA(const char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path) + return FALSE; + + while (*path) + { + if (*path == '\\' || *path == ':') + return FALSE; + path = CharNextA(path); + } + + return TRUE; +} + +BOOL WINAPI PathIsFileSpecW(const WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path) + return FALSE; + + while (*path) + { + if (*path == '\\' || *path == ':') + return FALSE; + path++; + } + + return TRUE; +} + +BOOL WINAPI PathIsUNCServerA(const char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!(path && path[0] == '\\' && path[1] == '\\')) + return FALSE; + + while (*path) + { + if (*path == '\\') + return FALSE; + path = CharNextA(path); + } + + return TRUE; +} + +BOOL WINAPI PathIsUNCServerW(const WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!(path && path[0] == '\\' && path[1] == '\\')) + return FALSE; + + return !wcschr(path + 2, '\\'); +} + +void WINAPI PathRemoveBlanksA(char *path) +{ + char *start, *first; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path || !*path) + return; + + start = first = path; + + while (*path == ' ') + path = CharNextA(path); + + while (*path) + *start++ = *path++; + + if (start != first) + while (start[-1] == ' ') + start--; + + *start = '\0'; +} + +void WINAPI PathRemoveBlanksW(WCHAR *path) +{ + WCHAR *start, *first; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path || !*path) + return; + + start = first = path; + + while (*path == ' ') + path++; + + while (*path) + *start++ = *path++; + + if (start != first) + while (start[-1] == ' ') + start--; + + *start = '\0'; +} + +void WINAPI PathRemoveExtensionA(char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path) + return; + + path = PathFindExtensionA(path); + if (path && *path) + *path = '\0'; +} + +void WINAPI PathRemoveExtensionW(WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path) + return; + + path = PathFindExtensionW(path); + if (path && *path) + *path = '\0'; +} + +BOOL WINAPI PathRenameExtensionA(char *path, const char *ext) +{ + char *extension; + + TRACE("%s, %s\n", wine_dbgstr_a(path), wine_dbgstr_a(ext)); + + extension = PathFindExtensionA(path); + + if (!extension || (extension - path + strlen(ext) >= MAX_PATH)) + return FALSE; + + strcpy(extension, ext); + return TRUE; +} + +BOOL WINAPI PathRenameExtensionW(WCHAR *path, const WCHAR *ext) +{ + WCHAR *extension; + + TRACE("%s, %s\n", wine_dbgstr_w(path), wine_dbgstr_w(ext)); + + extension = PathFindExtensionW(path); + + if (!extension || (extension - path + lstrlenW(ext) >= MAX_PATH)) + return FALSE; + + lstrcpyW(extension, ext); + return TRUE; +} + +void WINAPI PathUnquoteSpacesA(char *path) +{ + unsigned int len; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path || *path != '"') + return; + + len = strlen(path) - 1; + if (path[len] == '"') + { + path[len] = '\0'; + for (; *path; path++) + *path = path[1]; + } +} + +void WINAPI PathUnquoteSpacesW(WCHAR *path) +{ + unsigned int len; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path || *path != '"') + return; + + len = lstrlenW(path) - 1; + if (path[len] == '"') + { + path[len] = '\0'; + for (; *path; path++) + *path = path[1]; + } +} + +char * WINAPI PathRemoveBackslashA(char *path) +{ + char *ptr; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path) + return NULL; + + ptr = CharPrevA(path, path + strlen(path)); + if (!PathIsRootA(path) && *ptr == '\\') + *ptr = '\0'; + + return ptr; +} + +WCHAR * WINAPI PathRemoveBackslashW(WCHAR *path) +{ + WCHAR *ptr; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path) + return NULL; + + ptr = path + lstrlenW(path); + if (ptr > path) ptr--; + if (!PathIsRootW(path) && *ptr == '\\') + *ptr = '\0'; + + return ptr; +} + +BOOL WINAPI PathIsLFNFileSpecA(const char *path) +{ + unsigned int name_len = 0, ext_len = 0; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path) + return FALSE; + + while (*path) + { + if (*path == ' ') + return TRUE; /* DOS names cannot have spaces */ + if (*path == '.') + { + if (ext_len) + return TRUE; /* DOS names have only one dot */ + ext_len = 1; + } + else if (ext_len) + { + ext_len++; + if (ext_len > 4) + return TRUE; /* DOS extensions are <= 3 chars*/ + } + else + { + name_len++; + if (name_len > 8) + return TRUE; /* DOS names are <= 8 chars */ + } + path = CharNextA(path); + } + + return FALSE; /* Valid DOS path */ +} + +BOOL WINAPI PathIsLFNFileSpecW(const WCHAR *path) +{ + unsigned int name_len = 0, ext_len = 0; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path) + return FALSE; + + while (*path) + { + if (*path == ' ') + return TRUE; /* DOS names cannot have spaces */ + if (*path == '.') + { + if (ext_len) + return TRUE; /* DOS names have only one dot */ + ext_len = 1; + } + else if (ext_len) + { + ext_len++; + if (ext_len > 4) + return TRUE; /* DOS extensions are <= 3 chars*/ + } + else + { + name_len++; + if (name_len > 8) + return TRUE; /* DOS names are <= 8 chars */ + } + path++; + } + + return FALSE; /* Valid DOS path */ +} + +#define PATH_CHAR_CLASS_LETTER 0x00000001 +#define PATH_CHAR_CLASS_ASTERIX 0x00000002 +#define PATH_CHAR_CLASS_DOT 0x00000004 +#define PATH_CHAR_CLASS_BACKSLASH 0x00000008 +#define PATH_CHAR_CLASS_COLON 0x00000010 +#define PATH_CHAR_CLASS_SEMICOLON 0x00000020 +#define PATH_CHAR_CLASS_COMMA 0x00000040 +#define PATH_CHAR_CLASS_SPACE 0x00000080 +#define PATH_CHAR_CLASS_OTHER_VALID 0x00000100 +#define PATH_CHAR_CLASS_DOUBLEQUOTE 0x00000200 + +#define PATH_CHAR_CLASS_INVALID 0x00000000 +#define PATH_CHAR_CLASS_ANY 0xffffffff + +static const DWORD path_charclass[] = +{ + /* 0x00 */ PATH_CHAR_CLASS_INVALID, /* 0x01 */ PATH_CHAR_CLASS_INVALID, + /* 0x02 */ PATH_CHAR_CLASS_INVALID, /* 0x03 */ PATH_CHAR_CLASS_INVALID, + /* 0x04 */ PATH_CHAR_CLASS_INVALID, /* 0x05 */ PATH_CHAR_CLASS_INVALID, + /* 0x06 */ PATH_CHAR_CLASS_INVALID, /* 0x07 */ PATH_CHAR_CLASS_INVALID, + /* 0x08 */ PATH_CHAR_CLASS_INVALID, /* 0x09 */ PATH_CHAR_CLASS_INVALID, + /* 0x0a */ PATH_CHAR_CLASS_INVALID, /* 0x0b */ PATH_CHAR_CLASS_INVALID, + /* 0x0c */ PATH_CHAR_CLASS_INVALID, /* 0x0d */ PATH_CHAR_CLASS_INVALID, + /* 0x0e */ PATH_CHAR_CLASS_INVALID, /* 0x0f */ PATH_CHAR_CLASS_INVALID, + /* 0x10 */ PATH_CHAR_CLASS_INVALID, /* 0x11 */ PATH_CHAR_CLASS_INVALID, + /* 0x12 */ PATH_CHAR_CLASS_INVALID, /* 0x13 */ PATH_CHAR_CLASS_INVALID, + /* 0x14 */ PATH_CHAR_CLASS_INVALID, /* 0x15 */ PATH_CHAR_CLASS_INVALID, + /* 0x16 */ PATH_CHAR_CLASS_INVALID, /* 0x17 */ PATH_CHAR_CLASS_INVALID, + /* 0x18 */ PATH_CHAR_CLASS_INVALID, /* 0x19 */ PATH_CHAR_CLASS_INVALID, + /* 0x1a */ PATH_CHAR_CLASS_INVALID, /* 0x1b */ PATH_CHAR_CLASS_INVALID, + /* 0x1c */ PATH_CHAR_CLASS_INVALID, /* 0x1d */ PATH_CHAR_CLASS_INVALID, + /* 0x1e */ PATH_CHAR_CLASS_INVALID, /* 0x1f */ PATH_CHAR_CLASS_INVALID, + /* ' ' */ PATH_CHAR_CLASS_SPACE, /* '!' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '"' */ PATH_CHAR_CLASS_DOUBLEQUOTE, /* '#' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '$' */ PATH_CHAR_CLASS_OTHER_VALID, /* '%' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '&' */ PATH_CHAR_CLASS_OTHER_VALID, /* '\'' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '(' */ PATH_CHAR_CLASS_OTHER_VALID, /* ')' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '*' */ PATH_CHAR_CLASS_ASTERIX, /* '+' */ PATH_CHAR_CLASS_OTHER_VALID, + /* ',' */ PATH_CHAR_CLASS_COMMA, /* '-' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '.' */ PATH_CHAR_CLASS_DOT, /* '/' */ PATH_CHAR_CLASS_INVALID, + /* '0' */ PATH_CHAR_CLASS_OTHER_VALID, /* '1' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '2' */ PATH_CHAR_CLASS_OTHER_VALID, /* '3' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '4' */ PATH_CHAR_CLASS_OTHER_VALID, /* '5' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '6' */ PATH_CHAR_CLASS_OTHER_VALID, /* '7' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '8' */ PATH_CHAR_CLASS_OTHER_VALID, /* '9' */ PATH_CHAR_CLASS_OTHER_VALID, + /* ':' */ PATH_CHAR_CLASS_COLON, /* ';' */ PATH_CHAR_CLASS_SEMICOLON, + /* '<' */ PATH_CHAR_CLASS_INVALID, /* '=' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '>' */ PATH_CHAR_CLASS_INVALID, /* '?' */ PATH_CHAR_CLASS_LETTER, + /* '@' */ PATH_CHAR_CLASS_OTHER_VALID, /* 'A' */ PATH_CHAR_CLASS_ANY, + /* 'B' */ PATH_CHAR_CLASS_ANY, /* 'C' */ PATH_CHAR_CLASS_ANY, + /* 'D' */ PATH_CHAR_CLASS_ANY, /* 'E' */ PATH_CHAR_CLASS_ANY, + /* 'F' */ PATH_CHAR_CLASS_ANY, /* 'G' */ PATH_CHAR_CLASS_ANY, + /* 'H' */ PATH_CHAR_CLASS_ANY, /* 'I' */ PATH_CHAR_CLASS_ANY, + /* 'J' */ PATH_CHAR_CLASS_ANY, /* 'K' */ PATH_CHAR_CLASS_ANY, + /* 'L' */ PATH_CHAR_CLASS_ANY, /* 'M' */ PATH_CHAR_CLASS_ANY, + /* 'N' */ PATH_CHAR_CLASS_ANY, /* 'O' */ PATH_CHAR_CLASS_ANY, + /* 'P' */ PATH_CHAR_CLASS_ANY, /* 'Q' */ PATH_CHAR_CLASS_ANY, + /* 'R' */ PATH_CHAR_CLASS_ANY, /* 'S' */ PATH_CHAR_CLASS_ANY, + /* 'T' */ PATH_CHAR_CLASS_ANY, /* 'U' */ PATH_CHAR_CLASS_ANY, + /* 'V' */ PATH_CHAR_CLASS_ANY, /* 'W' */ PATH_CHAR_CLASS_ANY, + /* 'X' */ PATH_CHAR_CLASS_ANY, /* 'Y' */ PATH_CHAR_CLASS_ANY, + /* 'Z' */ PATH_CHAR_CLASS_ANY, /* '[' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '\\' */ PATH_CHAR_CLASS_BACKSLASH, /* ']' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '^' */ PATH_CHAR_CLASS_OTHER_VALID, /* '_' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '`' */ PATH_CHAR_CLASS_OTHER_VALID, /* 'a' */ PATH_CHAR_CLASS_ANY, + /* 'b' */ PATH_CHAR_CLASS_ANY, /* 'c' */ PATH_CHAR_CLASS_ANY, + /* 'd' */ PATH_CHAR_CLASS_ANY, /* 'e' */ PATH_CHAR_CLASS_ANY, + /* 'f' */ PATH_CHAR_CLASS_ANY, /* 'g' */ PATH_CHAR_CLASS_ANY, + /* 'h' */ PATH_CHAR_CLASS_ANY, /* 'i' */ PATH_CHAR_CLASS_ANY, + /* 'j' */ PATH_CHAR_CLASS_ANY, /* 'k' */ PATH_CHAR_CLASS_ANY, + /* 'l' */ PATH_CHAR_CLASS_ANY, /* 'm' */ PATH_CHAR_CLASS_ANY, + /* 'n' */ PATH_CHAR_CLASS_ANY, /* 'o' */ PATH_CHAR_CLASS_ANY, + /* 'p' */ PATH_CHAR_CLASS_ANY, /* 'q' */ PATH_CHAR_CLASS_ANY, + /* 'r' */ PATH_CHAR_CLASS_ANY, /* 's' */ PATH_CHAR_CLASS_ANY, + /* 't' */ PATH_CHAR_CLASS_ANY, /* 'u' */ PATH_CHAR_CLASS_ANY, + /* 'v' */ PATH_CHAR_CLASS_ANY, /* 'w' */ PATH_CHAR_CLASS_ANY, + /* 'x' */ PATH_CHAR_CLASS_ANY, /* 'y' */ PATH_CHAR_CLASS_ANY, + /* 'z' */ PATH_CHAR_CLASS_ANY, /* '{' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '|' */ PATH_CHAR_CLASS_INVALID, /* '}' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '~' */ PATH_CHAR_CLASS_OTHER_VALID +}; + +BOOL WINAPI PathIsValidCharA(char c, DWORD class) +{ + if ((unsigned)c > 0x7e) + return class & PATH_CHAR_CLASS_OTHER_VALID; + + return class & path_charclass[(unsigned)c]; +} + +BOOL WINAPI PathIsValidCharW(WCHAR c, DWORD class) +{ + if (c > 0x7e) + return class & PATH_CHAR_CLASS_OTHER_VALID; + + return class & path_charclass[c]; +} + +char * WINAPI PathFindNextComponentA(const char *path) +{ + char *slash; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path || !*path) + return NULL; + + if ((slash = StrChrA(path, '\\'))) + { + if (slash[1] == '\\') + slash++; + return slash + 1; + } + + return (char *)path + strlen(path); +} + +WCHAR * WINAPI PathFindNextComponentW(const WCHAR *path) +{ + WCHAR *slash; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path || !*path) + return NULL; + + if ((slash = StrChrW(path, '\\'))) + { + if (slash[1] == '\\') + slash++; + return slash + 1; + } + + return (WCHAR *)path + lstrlenW(path); +} + +char * WINAPI PathSkipRootA(const char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path || !*path) + return NULL; + + if (*path == '\\' && path[1] == '\\') + { + /* Network share: skip share server and mount point */ + path += 2; + if ((path = StrChrA(path, '\\')) && (path = StrChrA(path + 1, '\\'))) + path++; + return (char *)path; + } + + if (IsDBCSLeadByte(*path)) + return NULL; + + /* Check x:\ */ + if (path[0] && path[1] == ':' && path[2] == '\\') + return (char *)path + 3; + + return NULL; +} + +WCHAR * WINAPI PathSkipRootW(const WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path || !*path) + return NULL; + + if (*path == '\\' && path[1] == '\\') + { + /* Network share: skip share server and mount point */ + path += 2; + if ((path = StrChrW(path, '\\')) && (path = StrChrW(path + 1, '\\'))) + path++; + return (WCHAR *)path; + } + + /* Check x:\ */ + if (path[0] && path[1] == ':' && path[2] == '\\') + return (WCHAR *)path + 3; + + return NULL; +} + +void WINAPI PathStripPathA(char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + if (path) + { + char *filename = PathFindFileNameA(path); + if (filename != path) + RtlMoveMemory(path, filename, strlen(filename) + 1); + } +} + +void WINAPI PathStripPathW(WCHAR *path) +{ + WCHAR *filename; + + TRACE("%s\n", wine_dbgstr_w(path)); + filename = PathFindFileNameW(path); + if (filename != path) + RtlMoveMemory(path, filename, (lstrlenW(filename) + 1) * sizeof(WCHAR)); +} + +BOOL WINAPI PathSearchAndQualifyA(const char *path, char *buffer, UINT length) +{ + TRACE("%s, %p, %u\n", wine_dbgstr_a(path), buffer, length); + + if (SearchPathA(NULL, path, NULL, length, buffer, NULL)) + return TRUE; + + return !!GetFullPathNameA(path, length, buffer, NULL); +} + +BOOL WINAPI PathSearchAndQualifyW(const WCHAR *path, WCHAR *buffer, UINT length) +{ + TRACE("%s, %p, %u\n", wine_dbgstr_w(path), buffer, length); + + if (SearchPathW(NULL, path, NULL, length, buffer, NULL)) + return TRUE; + return !!GetFullPathNameW(path, length, buffer, NULL); +} + +BOOL WINAPI PathRelativePathToA(char *path, const char *from, DWORD attributes_from, const char *to, + DWORD attributes_to) +{ + WCHAR pathW[MAX_PATH], fromW[MAX_PATH], toW[MAX_PATH]; + BOOL ret; + + TRACE("%p, %s, %#lx, %s, %#lx\n", path, wine_dbgstr_a(from), attributes_from, wine_dbgstr_a(to), attributes_to); + + if (!path || !from || !to) + return FALSE; + + MultiByteToWideChar(CP_ACP, 0, from, -1, fromW, ARRAY_SIZE(fromW)); + MultiByteToWideChar(CP_ACP, 0, to, -1, toW, ARRAY_SIZE(toW)); + ret = PathRelativePathToW(pathW, fromW, attributes_from, toW, attributes_to); + WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, MAX_PATH, 0, 0); + + return ret; +} + +BOOL WINAPI PathRelativePathToW(WCHAR *path, const WCHAR *from, DWORD attributes_from, const WCHAR *to, + DWORD attributes_to) +{ + WCHAR fromW[MAX_PATH], toW[MAX_PATH]; + DWORD len; + + TRACE("%p, %s, %#lx, %s, %#lx\n", path, wine_dbgstr_w(from), attributes_from, wine_dbgstr_w(to), attributes_to); + + if (!path || !from || !to) + return FALSE; + + *path = '\0'; + lstrcpynW(fromW, from, ARRAY_SIZE(fromW)); + lstrcpynW(toW, to, ARRAY_SIZE(toW)); + + if (!(attributes_from & FILE_ATTRIBUTE_DIRECTORY)) + PathRemoveFileSpecW(fromW); + if (!(attributes_to & FILE_ATTRIBUTE_DIRECTORY)) + PathRemoveFileSpecW(toW); + + /* Paths can only be relative if they have a common root */ + if (!(len = PathCommonPrefixW(fromW, toW, 0))) + return FALSE; + + /* Strip off 'from' components to the root, by adding "..\" */ + from = fromW + len; + if (!*from) + { + path[0] = '.'; + path[1] = '\0'; + } + if (*from == '\\') + from++; + + while (*from) + { + from = PathFindNextComponentW(from); + lstrcatW(path, *from ? L"..\\" : L".."); + } + + /* From the root add the components of 'to' */ + to += len; + /* We check to[-1] to avoid skipping end of string. See the notes for this function. */ + if (*to && to[-1]) + { + if (*to != '\\') + to--; + len = lstrlenW(path); + if (len + lstrlenW(to) >= MAX_PATH) + { + *path = '\0'; + return FALSE; + } + lstrcpyW(path + len, to); + } + + return TRUE; +} + +HRESULT WINAPI PathMatchSpecExA(const char *path, const char *mask, DWORD flags) +{ + WCHAR *pathW, *maskW; + HRESULT ret; + + TRACE("%s, %s\n", wine_dbgstr_a(path), wine_dbgstr_a(mask)); + + if (flags) + FIXME("Ignoring flags %#lx.\n", flags); + + if (!lstrcmpA(mask, "*.*")) + return S_OK; /* Matches every path */ + + pathW = heap_strdupAtoW( path ); + maskW = heap_strdupAtoW( mask ); + ret = PathMatchSpecExW( pathW, maskW, flags ); + heap_free( pathW ); + heap_free( maskW ); + return ret; +} + +BOOL WINAPI PathMatchSpecA(const char *path, const char *mask) +{ + return PathMatchSpecExA(path, mask, 0) == S_OK; +} + +static BOOL path_match_maskW(const WCHAR *name, const WCHAR *mask) +{ + while (*name && *mask && *mask != ';') + { + if (*mask == '*') + { + do + { + if (path_match_maskW(name, mask + 1)) + return TRUE; /* try substrings */ + } while (*name++); + return FALSE; + } + + if (towupper(*mask) != towupper(*name) && *mask != '?') + return FALSE; + + name++; + mask++; + } + + if (!*name) + { + while (*mask == '*') + mask++; + if (!*mask || *mask == ';') + return TRUE; + } + + return FALSE; +} + +HRESULT WINAPI PathMatchSpecExW(const WCHAR *path, const WCHAR *mask, DWORD flags) +{ + TRACE("%s, %s\n", wine_dbgstr_w(path), wine_dbgstr_w(mask)); + + if (flags) + FIXME("Ignoring flags %#lx.\n", flags); + + if (!lstrcmpW(mask, L"*.*")) + return S_OK; /* Matches every path */ + + while (*mask) + { + while (*mask == ' ') + mask++; /* Eat leading spaces */ + + if (path_match_maskW(path, mask)) + return S_OK; /* Matches the current path */ + + while (*mask && *mask != ';') + mask++; /* masks separated by ';' */ + + if (*mask == ';') + mask++; + } + + return S_FALSE; +} + +BOOL WINAPI PathMatchSpecW(const WCHAR *path, const WCHAR *mask) +{ + return PathMatchSpecExW(path, mask, 0) == S_OK; +} + +void WINAPI PathQuoteSpacesA(char *path) +{ + TRACE("%s\n", wine_dbgstr_a(path)); + + if (path && StrChrA(path, ' ')) + { + size_t len = strlen(path) + 1; + + if (len + 2 < MAX_PATH) + { + memmove(path + 1, path, len); + path[0] = '"'; + path[len] = '"'; + path[len + 1] = '\0'; + } + } +} + +void WINAPI PathQuoteSpacesW(WCHAR *path) +{ + TRACE("%s\n", wine_dbgstr_w(path)); + + if (path && StrChrW(path, ' ')) + { + int len = lstrlenW(path) + 1; + + if (len + 2 < MAX_PATH) + { + memmove(path + 1, path, len * sizeof(WCHAR)); + path[0] = '"'; + path[len] = '"'; + path[len + 1] = '\0'; + } + } +} + +BOOL WINAPI PathIsSameRootA(const char *path1, const char *path2) +{ + const char *start; + int len; + + TRACE("%s, %s\n", wine_dbgstr_a(path1), wine_dbgstr_a(path2)); + + if (!path1 || !path2 || !(start = PathSkipRootA(path1))) + return FALSE; + + len = PathCommonPrefixA(path1, path2, NULL) + 1; + return start - path1 <= len; +} + +BOOL WINAPI PathIsSameRootW(const WCHAR *path1, const WCHAR *path2) +{ + const WCHAR *start; + int len; + + TRACE("%s, %s\n", wine_dbgstr_w(path1), wine_dbgstr_w(path2)); + + if (!path1 || !path2 || !(start = PathSkipRootW(path1))) + return FALSE; + + len = PathCommonPrefixW(path1, path2, NULL) + 1; + return start - path1 <= len; +} + +BOOL WINAPI PathFileExistsA(const char *path) +{ + UINT prev_mode; + DWORD attrs; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path) + return FALSE; + + /* Prevent a dialog box if path is on a disk that has been ejected. */ + prev_mode = SetErrorMode(SEM_FAILCRITICALERRORS); + attrs = GetFileAttributesA(path); + SetErrorMode(prev_mode); + return attrs != INVALID_FILE_ATTRIBUTES; +} + +BOOL WINAPI PathFileExistsW(const WCHAR *path) +{ + UINT prev_mode; + DWORD attrs; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path) + return FALSE; + + prev_mode = SetErrorMode(SEM_FAILCRITICALERRORS); + attrs = GetFileAttributesW(path); + SetErrorMode(prev_mode); + return attrs != INVALID_FILE_ATTRIBUTES; +} + +int WINAPI PathParseIconLocationA(char *path) +{ + int ret = 0; + char *comma; + + TRACE("%s\n", debugstr_a(path)); + + if (!path) + return 0; + + if ((comma = strchr(path, ','))) + { + *comma++ = '\0'; + ret = StrToIntA(comma); + } + PathUnquoteSpacesA(path); + PathRemoveBlanksA(path); + + return ret; +} + +int WINAPI PathParseIconLocationW(WCHAR *path) +{ + WCHAR *comma; + int ret = 0; + + TRACE("%s\n", debugstr_w(path)); + + if (!path) + return 0; + + if ((comma = StrChrW(path, ','))) + { + *comma++ = '\0'; + ret = StrToIntW(comma); + } + PathUnquoteSpacesW(path); + PathRemoveBlanksW(path); + + return ret; +} + +BOOL WINAPI PathUnExpandEnvStringsA(const char *path, char *buffer, UINT buf_len) +{ + WCHAR bufferW[MAX_PATH], *pathW; + DWORD len; + BOOL ret; + + TRACE("%s, %p, %d\n", debugstr_a(path), buffer, buf_len); + + pathW = heap_strdupAtoW(path); + if (!pathW) return FALSE; + + ret = PathUnExpandEnvStringsW(pathW, bufferW, MAX_PATH); + HeapFree(GetProcessHeap(), 0, pathW); + if (!ret) return FALSE; + + len = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); + if (buf_len < len + 1) return FALSE; + + WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buf_len, NULL, NULL); + return TRUE; +} + +struct envvars_map +{ + const WCHAR *var; + WCHAR path[MAX_PATH]; + DWORD len; +}; + +static void init_envvars_map(struct envvars_map *map) +{ + while (map->var) + { + map->len = ExpandEnvironmentStringsW(map->var, map->path, ARRAY_SIZE(map->path)); + /* exclude null from length */ + if (map->len) map->len--; + map++; + } +} + +BOOL WINAPI PathUnExpandEnvStringsW(const WCHAR *path, WCHAR *buffer, UINT buf_len) +{ + static struct envvars_map null_var = {L"", {0}, 0}; + struct envvars_map *match = &null_var, *cur; + struct envvars_map envvars[] = + { + { L"%ALLUSERSPROFILE%" }, + { L"%APPDATA%" }, + { L"%ProgramFiles%" }, + { L"%SystemRoot%" }, + { L"%SystemDrive%" }, + { L"%USERPROFILE%" }, + { NULL } + }; + DWORD pathlen; + UINT needed; + + TRACE("%s, %p, %d\n", debugstr_w(path), buffer, buf_len); + + pathlen = lstrlenW(path); + init_envvars_map(envvars); + cur = envvars; + while (cur->var) + { + /* path can't contain expanded value or value wasn't retrieved */ + if (cur->len == 0 || cur->len > pathlen || + CompareStringOrdinal( cur->path, cur->len, path, cur->len, TRUE ) != CSTR_EQUAL) + { + cur++; + continue; + } + + if (cur->len > match->len) + match = cur; + cur++; + } + + needed = lstrlenW(match->var) + 1 + pathlen - match->len; + if (match->len == 0 || needed > buf_len) return FALSE; + + lstrcpyW(buffer, match->var); + lstrcatW(buffer, &path[match->len]); + TRACE("ret %s\n", debugstr_w(buffer)); + + return TRUE; +} + +static const struct +{ + URL_SCHEME scheme_number; + const WCHAR *scheme_name; +} +url_schemes[] = +{ + { URL_SCHEME_FTP, L"ftp"}, + { URL_SCHEME_HTTP, L"http"}, + { URL_SCHEME_GOPHER, L"gopher"}, + { URL_SCHEME_MAILTO, L"mailto"}, + { URL_SCHEME_NEWS, L"news"}, + { URL_SCHEME_NNTP, L"nntp"}, + { URL_SCHEME_TELNET, L"telnet"}, + { URL_SCHEME_WAIS, L"wais"}, + { URL_SCHEME_FILE, L"file"}, + { URL_SCHEME_MK, L"mk"}, + { URL_SCHEME_HTTPS, L"https"}, + { URL_SCHEME_SHELL, L"shell"}, + { URL_SCHEME_SNEWS, L"snews"}, + { URL_SCHEME_LOCAL, L"local"}, + { URL_SCHEME_JAVASCRIPT, L"javascript"}, + { URL_SCHEME_VBSCRIPT, L"vbscript"}, + { URL_SCHEME_ABOUT, L"about"}, + { URL_SCHEME_RES, L"res"}, +}; + +static const WCHAR *parse_scheme( const WCHAR *p ) +{ + while (*p <= 0x7f && (iswalnum( *p ) || *p == '+' || *p == '-' || *p == '.')) + ++p; + return p; +} + +static DWORD get_scheme_code(const WCHAR *scheme, DWORD scheme_len) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(url_schemes); ++i) + { + if (scheme_len == lstrlenW(url_schemes[i].scheme_name) + && !wcsnicmp(scheme, url_schemes[i].scheme_name, scheme_len)) + return url_schemes[i].scheme_number; + } + + return URL_SCHEME_UNKNOWN; +} + +HRESULT WINAPI ParseURLA(const char *url, PARSEDURLA *result) +{ + WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH]; + const char *ptr = url; + int len; + + TRACE("%s, %p\n", wine_dbgstr_a(url), result); + + if (result->cbSize != sizeof(*result)) + return E_INVALIDARG; + + while (*ptr && (isalnum( *ptr ) || *ptr == '-' || *ptr == '+' || *ptr == '.')) + ptr++; + + if (*ptr != ':' || ptr <= url + 1) + { + result->pszProtocol = NULL; + return URL_E_INVALID_SYNTAX; + } + + result->pszProtocol = url; + result->cchProtocol = ptr - url; + result->pszSuffix = ptr + 1; + result->cchSuffix = strlen(result->pszSuffix); + + len = MultiByteToWideChar(CP_ACP, 0, url, ptr - url, scheme, ARRAY_SIZE(scheme)); + result->nScheme = get_scheme_code(scheme, len); + + return S_OK; +} + +HRESULT WINAPI ParseURLW(const WCHAR *url, PARSEDURLW *result) +{ + const WCHAR *ptr = url; + + TRACE("%s, %p\n", wine_dbgstr_w(url), result); + + if (result->cbSize != sizeof(*result)) + return E_INVALIDARG; + + while (*ptr && (iswalnum(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.')) + ptr++; + + if (*ptr != ':' || ptr <= url + 1) + { + result->pszProtocol = NULL; + return URL_E_INVALID_SYNTAX; + } + + result->pszProtocol = url; + result->cchProtocol = ptr - url; + result->pszSuffix = ptr + 1; + result->cchSuffix = lstrlenW(result->pszSuffix); + result->nScheme = get_scheme_code(url, ptr - url); + + return S_OK; +} + +HRESULT WINAPI UrlUnescapeA(char *url, char *unescaped, DWORD *unescaped_len, DWORD flags) +{ + BOOL stop_unescaping = FALSE; + const char *src; + char *dst, next; + DWORD needed; + HRESULT hr; + + TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_a(url), unescaped, unescaped_len, flags); + + if (!url) + return E_INVALIDARG; + + if (flags & URL_UNESCAPE_INPLACE) + dst = url; + else + { + if (!unescaped || !unescaped_len) return E_INVALIDARG; + dst = unescaped; + } + + for (src = url, needed = 0; *src; src++, needed++) + { + if (flags & URL_DONT_UNESCAPE_EXTRA_INFO && (*src == '#' || *src == '?')) + { + stop_unescaping = TRUE; + next = *src; + } + else if (*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2)) && !stop_unescaping) + { + INT ih; + char buf[3]; + memcpy(buf, src + 1, 2); + buf[2] = '\0'; + ih = strtol(buf, NULL, 16); + next = (CHAR) ih; + src += 2; /* Advance to end of escape */ + } + else + next = *src; + + if (flags & URL_UNESCAPE_INPLACE || needed < *unescaped_len) + *dst++ = next; + } + + if (flags & URL_UNESCAPE_INPLACE || needed < *unescaped_len) + { + *dst = '\0'; + hr = S_OK; + } + else + { + needed++; /* add one for the '\0' */ + hr = E_POINTER; + } + + if (!(flags & URL_UNESCAPE_INPLACE)) + *unescaped_len = needed; + + if (hr == S_OK) + TRACE("result %s\n", flags & URL_UNESCAPE_INPLACE ? wine_dbgstr_a(url) : wine_dbgstr_a(unescaped)); + + return hr; +} + +static int get_utf8_len(unsigned char code) +{ + if (code < 0x80) + return 1; + else if ((code & 0xe0) == 0xc0) + return 2; + else if ((code & 0xf0) == 0xe0) + return 3; + else if ((code & 0xf8) == 0xf0) + return 4; + return 0; +} + +HRESULT WINAPI UrlUnescapeW(WCHAR *url, WCHAR *unescaped, DWORD *unescaped_len, DWORD flags) +{ + WCHAR *dst, next, utf16_buf[4]; + BOOL stop_unescaping = FALSE; + int utf8_len, utf16_len, i; + const WCHAR *src; + char utf8_buf[4]; + DWORD needed; + HRESULT hr; + + TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_w(url), unescaped, unescaped_len, flags); + + if (!url) + return E_INVALIDARG; + + if (flags & URL_UNESCAPE_INPLACE) + dst = url; + else + { + if (!unescaped || !unescaped_len) return E_INVALIDARG; + dst = unescaped; + } + + for (src = url, needed = 0; *src; src++, needed++) + { + utf16_len = 0; + if (flags & URL_DONT_UNESCAPE_EXTRA_INFO && (*src == '#' || *src == '?')) + { + stop_unescaping = TRUE; + next = *src; + } + else if (*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2)) && !stop_unescaping) + { + INT ih; + WCHAR buf[5] = L"0x"; + + memcpy(buf + 2, src + 1, 2*sizeof(WCHAR)); + buf[4] = 0; + StrToIntExW(buf, STIF_SUPPORT_HEX, &ih); + src += 2; /* Advance to end of escape */ + + if (flags & URL_UNESCAPE_AS_UTF8) + { + utf8_buf[0] = ih; + utf8_len = get_utf8_len(ih); + for (i = 1; i < utf8_len && *(src + 1) == '%' && *(src + 2) && *(src + 3); i++) + { + memcpy(buf + 2, src + 2, 2 * sizeof(WCHAR)); + StrToIntExW(buf, STIF_SUPPORT_HEX, &ih); + /* Check if it is a valid continuation byte. */ + if ((ih & 0xc0) == 0x80) + { + utf8_buf[i] = ih; + src += 3; + } + else + break; + } + + utf16_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + utf8_buf, i, utf16_buf, ARRAYSIZE(utf16_buf)); + if (utf16_len) + needed += utf16_len - 1; + else + next = 0xfffd; + } + else + next = (WCHAR) ih; + } + else + next = *src; + + if (flags & URL_UNESCAPE_INPLACE || needed < *unescaped_len) + { + if (utf16_len) + { + memcpy(dst, utf16_buf, utf16_len * sizeof(*utf16_buf)); + dst += utf16_len; + } + else + *dst++ = next; + } + } + + if (flags & URL_UNESCAPE_INPLACE || needed < *unescaped_len) + { + *dst = '\0'; + hr = S_OK; + } + else + { + needed++; /* add one for the '\0' */ + hr = E_POINTER; + } + + if (!(flags & URL_UNESCAPE_INPLACE)) + *unescaped_len = needed; + + if (hr == S_OK) + TRACE("result %s\n", flags & URL_UNESCAPE_INPLACE ? wine_dbgstr_w(url) : wine_dbgstr_w(unescaped)); + + return hr; +} + +HRESULT WINAPI PathCreateFromUrlA(const char *pszUrl, char *pszPath, DWORD *pcchPath, DWORD dwReserved) +{ + WCHAR bufW[MAX_PATH]; + WCHAR *pathW = bufW; + UNICODE_STRING urlW; + HRESULT ret; + DWORD lenW = ARRAY_SIZE(bufW), lenA; + + if (!pszUrl || !pszPath || !pcchPath || !*pcchPath) + return E_INVALIDARG; + + if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl)) + return E_INVALIDARG; + if((ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved)) == E_POINTER) { + pathW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); + ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved); + } + if(ret == S_OK) { + RtlUnicodeToMultiByteSize(&lenA, pathW, lenW * sizeof(WCHAR)); + if(*pcchPath > lenA) { + RtlUnicodeToMultiByteN(pszPath, *pcchPath - 1, &lenA, pathW, lenW * sizeof(WCHAR)); + pszPath[lenA] = 0; + *pcchPath = lenA; + } else { + *pcchPath = lenA + 1; + ret = E_POINTER; + } + } + if(pathW != bufW) HeapFree(GetProcessHeap(), 0, pathW); + RtlFreeUnicodeString(&urlW); + return ret; +} + +HRESULT WINAPI PathCreateFromUrlW(const WCHAR *url, WCHAR *path, DWORD *pcchPath, DWORD dwReserved) +{ + DWORD nslashes, unescape, len; + const WCHAR *src; + WCHAR *tpath, *dst; + HRESULT hr = S_OK; + + TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_w(url), path, pcchPath, dwReserved); + + if (!url || !path || !pcchPath || !*pcchPath) + return E_INVALIDARG; + + if (wcsnicmp( url, L"file:", 5)) + return E_INVALIDARG; + + url += 5; + + src = url; + nslashes = 0; + while (*src == '/' || *src == '\\') + { + nslashes++; + src++; + } + + /* We need a temporary buffer so we can compute what size to ask for. + * We know that the final string won't be longer than the current pszUrl + * plus at most two backslashes. All the other transformations make it + * shorter. + */ + len = 2 + lstrlenW(url) + 1; + if (*pcchPath < len) + tpath = heap_alloc(len * sizeof(WCHAR)); + else + tpath = path; + + len = 0; + dst = tpath; + unescape = 1; + switch (nslashes) + { + case 0: + /* 'file:' + escaped DOS path */ + break; + case 1: + /* 'file:/' + escaped DOS path */ + /* fall through */ + case 3: + /* 'file:///' (implied localhost) + escaped DOS path */ + if (!is_escaped_drive_spec( src )) + src -= 1; + break; + case 2: + if (lstrlenW(src) >= 10 && !wcsnicmp( src, L"localhost", 9) && (src[9] == '/' || src[9] == '\\')) + { + /* 'file://localhost/' + escaped DOS path */ + src += 10; + } + else if (is_escaped_drive_spec( src )) + { + /* 'file://' + unescaped DOS path */ + unescape = 0; + } + else + { + /* 'file://hostname:port/path' (where path is escaped) + * or 'file:' + escaped UNC path (\\server\share\path) + * The second form is clearly specific to Windows and it might + * even be doing a network lookup to try to figure it out. + */ + while (*src && *src != '/' && *src != '\\') + src++; + len = src - url; + StrCpyNW(dst, url, len + 1); + dst += len; + if (*src && is_escaped_drive_spec( src + 1 )) + { + /* 'Forget' to add a trailing '/', just like Windows */ + src++; + } + } + break; + case 4: + /* 'file://' + unescaped UNC path (\\server\share\path) */ + unescape = 0; + if (is_escaped_drive_spec( src )) + break; + /* fall through */ + default: + /* 'file:/...' + escaped UNC path (\\server\share\path) */ + src -= 2; + } + + /* Copy the remainder of the path */ + len += lstrlenW(src); + lstrcpyW(dst, src); + + /* First do the Windows-specific path conversions */ + for (dst = tpath; *dst; dst++) + if (*dst == '/') *dst = '\\'; + if (is_escaped_drive_spec( tpath )) + tpath[1] = ':'; /* c| -> c: */ + + /* And only then unescape the path (i.e. escaped slashes are left as is) */ + if (unescape) + { + hr = UrlUnescapeW(tpath, NULL, &len, URL_UNESCAPE_INPLACE); + if (hr == S_OK) + { + /* When working in-place UrlUnescapeW() does not set len */ + len = lstrlenW(tpath); + } + } + + if (*pcchPath < len + 1) + { + hr = E_POINTER; + *pcchPath = len + 1; + } + else + { + *pcchPath = len; + if (tpath != path) + lstrcpyW(path, tpath); + } + if (tpath != path) + heap_free(tpath); + + TRACE("Returning (%lu) %s\n", *pcchPath, wine_dbgstr_w(path)); + return hr; +} + +HRESULT WINAPI PathCreateFromUrlAlloc(const WCHAR *url, WCHAR **path, DWORD reserved) +{ + WCHAR pathW[MAX_PATH]; + DWORD size; + HRESULT hr; + + size = MAX_PATH; + hr = PathCreateFromUrlW(url, pathW, &size, reserved); + if (SUCCEEDED(hr)) + { + /* Yes, this is supposed to crash if 'path' is NULL */ + *path = StrDupW(pathW); + } + + return hr; +} + +BOOL WINAPI PathIsURLA(const char *path) +{ + PARSEDURLA base; + HRESULT hr; + + TRACE("%s\n", wine_dbgstr_a(path)); + + if (!path || !*path) + return FALSE; + + /* get protocol */ + base.cbSize = sizeof(base); + hr = ParseURLA(path, &base); + return hr == S_OK && (base.nScheme != URL_SCHEME_INVALID); +} + +BOOL WINAPI PathIsURLW(const WCHAR *path) +{ + PARSEDURLW base; + HRESULT hr; + + TRACE("%s\n", wine_dbgstr_w(path)); + + if (!path || !*path) + return FALSE; + + /* get protocol */ + base.cbSize = sizeof(base); + hr = ParseURLW(path, &base); + return hr == S_OK && (base.nScheme != URL_SCHEME_INVALID); +} + +#define WINE_URL_BASH_AS_SLASH 0x01 +#define WINE_URL_COLLAPSE_SLASHES 0x02 +#define WINE_URL_ESCAPE_SLASH 0x04 +#define WINE_URL_ESCAPE_HASH 0x08 +#define WINE_URL_ESCAPE_QUESTION 0x10 +#define WINE_URL_STOP_ON_HASH 0x20 +#define WINE_URL_STOP_ON_QUESTION 0x40 + +static BOOL url_needs_escape(WCHAR ch, DWORD flags, DWORD int_flags) +{ + if (flags & URL_ESCAPE_SPACES_ONLY) + return ch == ' '; + + if ((flags & URL_ESCAPE_PERCENT) && (ch == '%')) + return TRUE; + + if ((flags & URL_ESCAPE_AS_UTF8) && (ch >= 0x80)) + return TRUE; + + if (ch <= 31 || (ch >= 127 && ch <= 255) ) + return TRUE; + + if (iswalnum(ch)) + return FALSE; + + switch (ch) { + case ' ': + case '<': + case '>': + case '\"': + case '{': + case '}': + case '|': + case '\\': + case '^': + case ']': + case '[': + case '`': + case '&': + return TRUE; + case '/': + return !!(int_flags & WINE_URL_ESCAPE_SLASH); + case '?': + return !!(int_flags & WINE_URL_ESCAPE_QUESTION); + case '#': + return !!(int_flags & WINE_URL_ESCAPE_HASH); + default: + return FALSE; + } +} + +HRESULT WINAPI UrlEscapeA(const char *url, char *escaped, DWORD *escaped_len, DWORD flags) +{ + WCHAR bufW[INTERNET_MAX_URL_LENGTH]; + WCHAR *escapedW = bufW; + UNICODE_STRING urlW; + HRESULT hr; + DWORD lenW = ARRAY_SIZE(bufW), lenA; + + if (!escaped || !escaped_len || !*escaped_len) + return E_INVALIDARG; + + if (!RtlCreateUnicodeStringFromAsciiz(&urlW, url)) + return E_INVALIDARG; + + if (flags & URL_ESCAPE_AS_UTF8) + { + RtlFreeUnicodeString(&urlW); + return E_NOTIMPL; + } + + if ((hr = UrlEscapeW(urlW.Buffer, escapedW, &lenW, flags)) == E_POINTER) + { + escapedW = heap_alloc(lenW * sizeof(WCHAR)); + hr = UrlEscapeW(urlW.Buffer, escapedW, &lenW, flags); + } + + if (hr == S_OK) + { + RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR)); + if (*escaped_len > lenA) + { + RtlUnicodeToMultiByteN(escaped, *escaped_len - 1, &lenA, escapedW, lenW * sizeof(WCHAR)); + escaped[lenA] = 0; + *escaped_len = lenA; + } + else + { + *escaped_len = lenA + 1; + hr = E_POINTER; + } + } + if (escapedW != bufW) + heap_free(escapedW); + RtlFreeUnicodeString(&urlW); + return hr; +} + +HRESULT WINAPI UrlEscapeW(const WCHAR *url, WCHAR *escaped, DWORD *escaped_len, DWORD flags) +{ + DWORD needed = 0, slashes = 0, int_flags; + WCHAR next[12], *dst, *dst_ptr; + BOOL stop_escaping = FALSE; + PARSEDURLW parsed_url; + const WCHAR *src; + INT i, len; + HRESULT hr; + + TRACE("%p, %s, %p, %p, %#lx\n", url, wine_dbgstr_w(url), escaped, escaped_len, flags); + + if (!url || !escaped_len || !escaped || *escaped_len == 0) + return E_INVALIDARG; + + if (flags & ~(URL_ESCAPE_SPACES_ONLY | URL_ESCAPE_SEGMENT_ONLY | URL_DONT_ESCAPE_EXTRA_INFO | + URL_ESCAPE_PERCENT | URL_ESCAPE_AS_UTF8)) + { + FIXME("Unimplemented flags: %08lx\n", flags); + } + + dst_ptr = dst = heap_alloc(*escaped_len * sizeof(WCHAR)); + if (!dst_ptr) + return E_OUTOFMEMORY; + + /* fix up flags */ + if (flags & URL_ESCAPE_SPACES_ONLY) + /* if SPACES_ONLY specified, reset the other controls */ + flags &= ~(URL_DONT_ESCAPE_EXTRA_INFO | URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY); + else + /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */ + flags |= URL_DONT_ESCAPE_EXTRA_INFO; + + int_flags = 0; + if (flags & URL_ESCAPE_SEGMENT_ONLY) + int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH; + else + { + parsed_url.cbSize = sizeof(parsed_url); + if (ParseURLW(url, &parsed_url) != S_OK) + parsed_url.nScheme = URL_SCHEME_INVALID; + + TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol)); + + if (flags & URL_DONT_ESCAPE_EXTRA_INFO) + int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION; + + switch(parsed_url.nScheme) { + case URL_SCHEME_FILE: + int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH; + int_flags &= ~WINE_URL_STOP_ON_HASH; + break; + + case URL_SCHEME_HTTP: + case URL_SCHEME_HTTPS: + int_flags |= WINE_URL_BASH_AS_SLASH; + if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\') + int_flags |= WINE_URL_ESCAPE_SLASH; + break; + + case URL_SCHEME_MAILTO: + int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH; + int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH); + break; + + case URL_SCHEME_INVALID: + break; + + case URL_SCHEME_FTP: + default: + if(parsed_url.pszSuffix[0] != '/') + int_flags |= WINE_URL_ESCAPE_SLASH; + break; + } + } + + for (src = url; *src; ) + { + WCHAR cur = *src; + len = 0; + + if ((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == url + parsed_url.cchProtocol + 1) + { + while (cur == '/' || cur == '\\') + { + slashes++; + cur = *++src; + } + if (slashes == 2 && !wcsnicmp(src, L"localhost", 9)) { /* file://localhost/ -> file:/// */ + if(src[9] == '/' || src[9] == '\\') src += 10; + slashes = 3; + } + + switch (slashes) + { + case 1: + case 3: + next[0] = next[1] = next[2] = '/'; + len = 3; + break; + case 0: + len = 0; + break; + default: + next[0] = next[1] = '/'; + len = 2; + break; + } + } + if (len == 0) + { + if (cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH)) + stop_escaping = TRUE; + + if (cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION)) + stop_escaping = TRUE; + + if (cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/'; + + if (url_needs_escape(cur, flags, int_flags) && !stop_escaping) + { + if (flags & URL_ESCAPE_AS_UTF8) + { + char utf[16]; + + if ((cur >= 0xd800 && cur <= 0xdfff) && (src[1] >= 0xdc00 && src[1] <= 0xdfff)) + { + len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, 2, utf, sizeof(utf), NULL, NULL); + src++; + } + else + len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &cur, 1, utf, sizeof(utf), NULL, NULL); + + if (!len) + { + utf[0] = 0xef; + utf[1] = 0xbf; + utf[2] = 0xbd; + len = 3; + } + + for (i = 0; i < len; ++i) + { + next[i*3+0] = '%'; + next[i*3+1] = hexDigits[(utf[i] >> 4) & 0xf]; + next[i*3+2] = hexDigits[utf[i] & 0xf]; + } + len *= 3; + } + else + { + next[0] = '%'; + next[1] = hexDigits[(cur >> 4) & 0xf]; + next[2] = hexDigits[cur & 0xf]; + len = 3; + } + } + else + { + next[0] = cur; + len = 1; + } + src++; + } + + if (needed + len <= *escaped_len) + { + memcpy(dst, next, len*sizeof(WCHAR)); + dst += len; + } + needed += len; + } + + if (needed < *escaped_len) + { + *dst = '\0'; + memcpy(escaped, dst_ptr, (needed+1)*sizeof(WCHAR)); + hr = S_OK; + } + else + { + needed++; /* add one for the '\0' */ + hr = E_POINTER; + } + *escaped_len = needed; + + heap_free(dst_ptr); + return hr; +} + +HRESULT WINAPI UrlCanonicalizeA(const char *src_url, char *canonicalized, DWORD *canonicalized_len, DWORD flags) +{ + LPWSTR url, canonical; + HRESULT hr; + + TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_a(src_url), canonicalized, canonicalized_len, flags); + + if (!src_url || !canonicalized || !canonicalized_len || !*canonicalized_len) + return E_INVALIDARG; + + url = heap_strdupAtoW(src_url); + canonical = heap_alloc(*canonicalized_len * sizeof(WCHAR)); + if (!url || !canonical) + { + heap_free(url); + heap_free(canonical); + return E_OUTOFMEMORY; + } + + hr = UrlCanonicalizeW(url, canonical, canonicalized_len, flags); + if (hr == S_OK) + WideCharToMultiByte(CP_ACP, 0, canonical, -1, canonicalized, *canonicalized_len + 1, NULL, NULL); + + heap_free(url); + heap_free(canonical); + return hr; +} + +static bool scheme_is_opaque( URL_SCHEME scheme ) +{ + switch (scheme) + { + case URL_SCHEME_ABOUT: + case URL_SCHEME_JAVASCRIPT: + case URL_SCHEME_MAILTO: + case URL_SCHEME_SHELL: + case URL_SCHEME_VBSCRIPT: + return true; + + default: + return false; + } +} + +static bool scheme_preserves_backslashes( URL_SCHEME scheme ) +{ + switch (scheme) + { + case URL_SCHEME_FTP: + case URL_SCHEME_INVALID: + case URL_SCHEME_LOCAL: + case URL_SCHEME_MK: + case URL_SCHEME_RES: + case URL_SCHEME_UNKNOWN: + case URL_SCHEME_WAIS: + return true; + + default: + return false; + } +} + +static bool scheme_uses_hostname( URL_SCHEME scheme ) +{ + switch (scheme) + { + case URL_SCHEME_ABOUT: + case URL_SCHEME_JAVASCRIPT: + case URL_SCHEME_MAILTO: + case URL_SCHEME_MK: + case URL_SCHEME_SHELL: + case URL_SCHEME_VBSCRIPT: + return false; + + default: + return true; + } +} + +static bool scheme_char_is_separator( URL_SCHEME scheme, WCHAR c ) +{ + if (c == '/') + return true; + if (c == '\\' && scheme != URL_SCHEME_INVALID && scheme != URL_SCHEME_UNKNOWN) + return true; + return false; +} + +static bool scheme_char_is_hostname_separator( URL_SCHEME scheme, DWORD flags, WCHAR c ) +{ + switch (c) + { + case 0: + case '/': + return true; + case '\\': + return !scheme_preserves_backslashes( scheme ); + case '?': + return scheme != URL_SCHEME_FILE || (flags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL)); + case '#': + return scheme != URL_SCHEME_FILE; + default: + return false; + } +} + +static bool scheme_char_is_dot_separator( URL_SCHEME scheme, DWORD flags, WCHAR c ) +{ + switch (c) + { + case 0: + case '/': + case '?': + return true; + case '#': + return (scheme != URL_SCHEME_FILE || !(flags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL))); + case '\\': + return (scheme != URL_SCHEME_INVALID && scheme != URL_SCHEME_UNKNOWN && scheme != URL_SCHEME_MK); + default: + return false; + } +} + +/* There are essentially two types of behaviour concerning dot simplification, + * not counting opaque schemes: + * + * 1) Simplify dots if and only if the first element is not a single or double + * dot. If a double dot would rewind past the root, ignore it. For example: + * + * http://hostname/a/../../b/. -> http://hostname/b/ + * http://hostname/./../../b/. -> http://hostname/./../../b/. + * + * 2) Effectively treat all paths as relative. Always simplify, except if a + * double dot would rewind past the root, in which case emit it verbatim. + * For example: + * + * wine://hostname/a/../../b/. -> wine://hostname/../b/ + * wine://hostname/./../../b/. -> wine://hostname/../b/ + * + * For unclear reasons, this behaviour also correlates with whether a final + * slash is always emitted after a single or double dot (e.g. if + * URL_DONT_SIMPLIFY is specified). The former type does not emit a slash; the + * latter does. + */ +static bool scheme_is_always_relative( URL_SCHEME scheme, DWORD flags ) +{ + switch (scheme) + { + case URL_SCHEME_INVALID: + case URL_SCHEME_UNKNOWN: + return true; + + case URL_SCHEME_FILE: + return flags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL); + + default: + return false; + } +} + +struct string_buffer +{ + WCHAR *string; + size_t len, capacity; +}; + +static void append_string( struct string_buffer *buffer, const WCHAR *str, size_t len ) +{ + array_reserve( (void **)&buffer->string, &buffer->capacity, buffer->len + len, sizeof(WCHAR) ); + memcpy( buffer->string + buffer->len, str, len * sizeof(WCHAR) ); + buffer->len += len; +} + +static void append_char( struct string_buffer *buffer, WCHAR c ) +{ + append_string( buffer, &c, 1 ); +} + +static char get_slash_dir( URL_SCHEME scheme, DWORD flags, char src, const struct string_buffer *dst ) +{ + if (src && scheme_preserves_backslashes( scheme )) + return src; + + if (scheme == URL_SCHEME_FILE && (flags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY)) + && !wmemchr( dst->string, '#', dst->len )) + return '\\'; + + return '/'; +} + +static void rewrite_url( struct string_buffer *dst, const WCHAR *url, DWORD *flags_ptr ) +{ + DWORD flags = *flags_ptr; + bool pathurl = (flags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY)); + bool is_relative = false, has_hostname = false, has_initial_slash = false; + const WCHAR *query = NULL, *hash = NULL; + URL_SCHEME scheme = URL_SCHEME_INVALID; + size_t query_len = 0, hash_len = 0; + const WCHAR *scheme_end, *src_end; + const WCHAR *hostname = NULL; + size_t hostname_len = 0; + const WCHAR *src = url; + size_t root_offset; + + /* Determine the scheme. */ + + scheme_end = parse_scheme( url ); + + if (*scheme_end == ':' && scheme_end >= url + 2) + { + size_t scheme_len = scheme_end + 1 - url; + + scheme = get_scheme_code( url, scheme_len - 1 ); + + for (size_t i = 0; i < scheme_len; ++i) + append_char( dst, tolower( *src++ )); + } + else if (url[0] == '\\' && url[1] == '\\') + { + append_string( dst, L"file:", 5 ); + if (!pathurl && !(flags & URL_UNESCAPE)) + flags |= URL_ESCAPE_UNSAFE | URL_ESCAPE_PERCENT; + scheme = URL_SCHEME_FILE; + + has_hostname = true; + } + + if (is_escaped_drive_spec( url )) + { + append_string( dst, L"file://", 7 ); + if (!pathurl && !(flags & URL_UNESCAPE)) + flags |= URL_ESCAPE_UNSAFE | URL_ESCAPE_PERCENT; + scheme = URL_SCHEME_FILE; + + hostname_len = 0; + has_hostname = true; + } + else if (scheme == URL_SCHEME_MK) + { + if (src[0] == '@') + { + while (*src && *src != '/') + append_char( dst, *src++ ); + if (*src == '/') + append_char( dst, *src++ ); + else + append_char( dst, '/' ); + + if ((src[0] == '.' && scheme_char_is_dot_separator( scheme, flags, src[1] )) || + (src[0] == '.' && src[1] == '.' && scheme_char_is_dot_separator( scheme, flags, src[2] ))) + is_relative = true; + } + } + else if (scheme_uses_hostname( scheme ) && scheme_char_is_separator( scheme, src[0] ) + && scheme_char_is_separator( scheme, src[1] )) + { + append_char( dst, scheme_preserves_backslashes( scheme ) ? src[0] : '/' ); + append_char( dst, scheme_preserves_backslashes( scheme ) ? src[1] : '/' ); + src += 2; + if (scheme == URL_SCHEME_FILE && is_slash( src[0] ) && is_slash( src[1] )) + { + while (is_slash( *src )) + ++src; + } + + hostname = src; + + while (!scheme_char_is_hostname_separator( scheme, flags, *src )) + ++src; + hostname_len = src - hostname; + has_hostname = true; + has_initial_slash = true; + } + else if (scheme_char_is_separator( scheme, src[0] )) + { + has_initial_slash = true; + + if (scheme == URL_SCHEME_UNKNOWN || scheme == URL_SCHEME_INVALID) + { + /* Special case: an unknown scheme starting with a single slash + * considers the "root" to be the single slash. + * Most other schemes treat it as an empty path segment instead. */ + append_char( dst, *src++ ); + + if (*src == '\\') + ++src; + } + else if (scheme == URL_SCHEME_FILE) + { + src++; + + append_string( dst, L"//", 2 ); + + hostname_len = 0; + has_hostname = true; + } + } + else + { + if (scheme == URL_SCHEME_FILE) + { + if (is_escaped_drive_spec( src )) + { + append_string( dst, L"//", 2 ); + hostname_len = 0; + has_hostname = true; + } + else + { + if (flags & URL_FILE_USE_PATHURL) + append_string( dst, L"//", 2 ); + } + } + } + + if (scheme == URL_SCHEME_FILE && (flags & URL_FILE_USE_PATHURL)) + flags |= URL_UNESCAPE; + + *flags_ptr = flags; + + if (has_hostname) + { + if (scheme == URL_SCHEME_FILE) + { + bool is_drive = false; + + if (is_slash( *src )) + ++src; + + if (hostname_len >= 2 && is_escaped_drive_spec( hostname )) + { + hostname_len = 0; + src = hostname; + is_drive = true; + } + else if (is_escaped_drive_spec( src )) + { + is_drive = true; + } + + if (pathurl) + { + if (hostname_len == 9 && !wcsnicmp( hostname, L"localhost", 9 )) + { + hostname_len = 0; + if (is_slash( *src )) + ++src; + if (is_escaped_drive_spec( src )) + is_drive = true; + } + + if (!is_drive) + { + if (hostname_len) + { + append_string( dst, L"\\\\", 2 ); + append_string( dst, hostname, hostname_len ); + } + + if ((*src && *src != '?') || (flags & URL_WININET_COMPATIBILITY)) + append_char( dst, get_slash_dir( scheme, flags, 0, dst )); + } + } + else + { + if (hostname_len) + append_string( dst, hostname, hostname_len ); + append_char( dst, '/' ); + } + + if (is_drive) + { + /* Root starts after the first slash when file flags are in use, + * but directly after the drive specification if not. */ + if (pathurl) + { + while (!scheme_char_is_hostname_separator( scheme, flags, *src )) + append_char( dst, *src++ ); + if (is_slash( *src )) + { + append_char( dst, '\\' ); + src++; + } + } + else + { + append_char( dst, *src++ ); + append_char( dst, *src++ ); + if (is_slash( *src )) + { + append_char( dst, '/' ); + src++; + } + } + } + } + else + { + for (size_t i = 0; i < hostname_len; ++i) + { + if (scheme == URL_SCHEME_UNKNOWN || scheme == URL_SCHEME_INVALID) + append_char( dst, hostname[i] ); + else + append_char( dst, tolower( hostname[i] )); + } + + if (*src == '/' || *src == '\\') + { + append_char( dst, scheme_preserves_backslashes( scheme ) ? *src : '/' ); + src++; + } + else + { + append_char( dst, '/' ); + } + } + + if ((src[0] == '.' && scheme_char_is_dot_separator( scheme, flags, src[1] )) || + (src[0] == '.' && src[1] == '.' && scheme_char_is_dot_separator( scheme, flags, src[2] ))) + { + if (!scheme_is_always_relative( scheme, flags )) + is_relative = true; + } + } + + /* root_offset now points to the point past which we will not rewind. + * If there is a hostname, it points to the character after the closing + * slash. */ + + root_offset = dst->len; + + /* Break up the rest of the URL into the body, query, and hash parts. */ + + src_end = src + wcslen( src ); + + if (scheme_is_opaque( scheme )) + { + /* +1 for null terminator */ + append_string( dst, src, src_end + 1 - src ); + return; + } + + if (scheme == URL_SCHEME_FILE) + { + if (!pathurl) + { + if (src[0] == '#') + hash = src; + else if (is_slash( src[0] ) && src[1] == '#') + hash = src + 1; + + if (src[0] == '?') + query = src; + else if (is_slash( src[0] ) && src[1] == '?') + query = src + 1; + } + else + { + query = wcschr( src, '?' ); + } + + if (!hash) + { + for (const WCHAR *p = src; p < src_end; ++p) + { + if (!wcsnicmp( p, L".htm#" , 5)) + hash = p + 4; + else if (!wcsnicmp( p, L".html#", 6 )) + hash = p + 5; + } + } + } + else + { + query = wcschr( src, '?' ); + hash = wcschr( src, '#' ); + } + + if (query) + query_len = ((hash && hash > query) ? hash : src_end) - query; + if (hash) + hash_len = ((query && query > hash) ? query : src_end) - hash; + + if (query) + src_end = query; + if (hash && hash < src_end) + src_end = hash; + + if (scheme == URL_SCHEME_UNKNOWN && !has_initial_slash) + { + if (!(flags & URL_DONT_SIMPLIFY) && src[0] == '.' && src_end == src + 1) + src++; + flags |= URL_DONT_SIMPLIFY; + } + + while (src < src_end) + { + bool is_dots = false; + size_t len; + + for (len = 0; src + len < src_end && !scheme_char_is_separator( scheme, src[len] ); ++len) + ; + + if (src[0] == '.' && scheme_char_is_dot_separator( scheme, flags, src[1] )) + { + if (!is_relative) + { + if (flags & URL_DONT_SIMPLIFY) + { + is_dots = true; + } + else + { + ++src; + if (*src == '/' || *src == '\\') + ++src; + continue; + } + } + } + else if (src[0] == '.' && src[1] == '.' && scheme_char_is_dot_separator( scheme, flags, src[2] )) + { + if (!is_relative) + { + if (flags & URL_DONT_SIMPLIFY) + { + is_dots = true; + } + else if (dst->len == root_offset && scheme_is_always_relative( scheme, flags )) + { + /* We could also use is_dots here, except that we need to + * update root afterwards. */ + + append_char( dst, *src++ ); + append_char( dst, *src++ ); + if (*src == '/' || *src == '\\') + append_char( dst, get_slash_dir( scheme, flags, *src++, dst )); + else + append_char( dst, get_slash_dir( scheme, flags, 0, dst )); + root_offset = dst->len; + continue; + } + else + { + if (dst->len > root_offset) + --dst->len; /* rewind past the last slash */ + + while (dst->len > root_offset && !scheme_char_is_separator( scheme, dst->string[dst->len - 1] )) + --dst->len; + + src += 2; + if (*src == '/' || *src == '\\') + ++src; + continue; + } + } + } + + if (len) + { + append_string( dst, src, len ); + src += len; + } + + if (*src == '?' || *src == '#' || !*src) + { + if (scheme == URL_SCHEME_UNKNOWN && !has_initial_slash) + is_dots = false; + + if (is_dots && scheme_is_always_relative( scheme, flags )) + append_char( dst, get_slash_dir( scheme, flags, 0, dst )); + } + else /* slash */ + { + append_char( dst, get_slash_dir( scheme, flags, *src++, dst )); + } + } + + /* If the source was non-empty but collapsed to an empty string, output a + * single slash. */ + if (!dst->len && src_end != url) + append_char( dst, '/' ); + + /* UNKNOWN and FILE schemes usually reorder the ? before the #, but others + * emit them in the original order. */ + if (query && hash && scheme != URL_SCHEME_FILE && scheme != URL_SCHEME_INVALID && scheme != URL_SCHEME_UNKNOWN) + { + if (query < hash) + { + append_string( dst, query, query_len ); + append_string( dst, hash, hash_len ); + } + else + { + append_string( dst, hash, hash_len ); + append_string( dst, query, query_len ); + } + } + else if (!(scheme == URL_SCHEME_FILE && (flags & URL_FILE_USE_PATHURL))) + { + if (query) + append_string( dst, query, query_len ); + + if (hash) + append_string( dst, hash, hash_len ); + } + + append_char( dst, 0 ); +} + +HRESULT WINAPI UrlCanonicalizeW(const WCHAR *src_url, WCHAR *canonicalized, DWORD *canonicalized_len, DWORD flags) +{ + struct string_buffer rewritten = {0}; + DWORD escape_flags; + HRESULT hr = S_OK; + const WCHAR *src; + WCHAR *url, *dst; + DWORD len; + + TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_w(src_url), canonicalized, canonicalized_len, flags); + + if (!src_url || !canonicalized || !canonicalized_len || !*canonicalized_len) + return E_INVALIDARG; + + if (!*src_url) + { + *canonicalized = 0; + return S_OK; + } + + /* PATHURL takes precedence. */ + if (flags & URL_FILE_USE_PATHURL) + flags &= ~URL_WININET_COMPATIBILITY; + + /* strip initial and final C0 control characters and space */ + src = src_url; + while (*src > 0 && *src <= 0x20) + ++src; + len = wcslen( src ); + while (len && src[len - 1] > 0 && src[len - 1] <= 0x20) + --len; + + if (!(url = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) + return E_OUTOFMEMORY; + + dst = url; + for (size_t i = 0; i < len; ++i) + { + if (src[i] != '\t' && src[i] != '\n' && src[i] != '\r') + *dst++ = src[i]; + } + *dst++ = 0; + + rewrite_url( &rewritten, url, &flags ); + + if (flags & URL_UNESCAPE) + { + len = rewritten.len; + UrlUnescapeW( rewritten.string, NULL, &len, URL_UNESCAPE_INPLACE); + rewritten.len = wcslen( rewritten.string ) + 1; + } + + /* URL_ESCAPE_SEGMENT_ONLY seems to be ignored. */ + escape_flags = flags & (URL_ESCAPE_UNSAFE | URL_ESCAPE_SPACES_ONLY | URL_ESCAPE_PERCENT | + URL_DONT_ESCAPE_EXTRA_INFO); + + if (escape_flags) + { + escape_flags &= ~URL_ESCAPE_UNSAFE; + hr = UrlEscapeW( rewritten.string, canonicalized, canonicalized_len, escape_flags ); + } + else + { + /* No escaping needed, just copy the string */ + if (rewritten.len <= *canonicalized_len) + { + memcpy( canonicalized, rewritten.string, rewritten.len * sizeof(WCHAR) ); + *canonicalized_len = rewritten.len - 1; + } + else + { + hr = E_POINTER; + *canonicalized_len = rewritten.len; + } + } + + heap_free( rewritten.string ); + heap_free( url ); + + if (hr == S_OK) + TRACE("result %s\n", wine_dbgstr_w(canonicalized)); + + return hr; +} + +HRESULT WINAPI UrlApplySchemeA(const char *url, char *out, DWORD *out_len, DWORD flags) +{ + LPWSTR inW, outW; + HRESULT hr; + DWORD len; + + TRACE("%s, %p, %p:out size %ld, %#lx\n", wine_dbgstr_a(url), out, out_len, out_len ? *out_len : 0, flags); + + if (!url || !out || !out_len) + return E_INVALIDARG; + + inW = heap_alloc(2 * INTERNET_MAX_URL_LENGTH * sizeof(WCHAR)); + outW = inW + INTERNET_MAX_URL_LENGTH; + + MultiByteToWideChar(CP_ACP, 0, url, -1, inW, INTERNET_MAX_URL_LENGTH); + len = INTERNET_MAX_URL_LENGTH; + + hr = UrlApplySchemeW(inW, outW, &len, flags); + if (hr != S_OK) + { + heap_free(inW); + return hr; + } + + len = WideCharToMultiByte(CP_ACP, 0, outW, -1, NULL, 0, NULL, NULL); + if (len > *out_len) + { + hr = E_POINTER; + goto cleanup; + } + + WideCharToMultiByte(CP_ACP, 0, outW, -1, out, *out_len, NULL, NULL); + len--; + +cleanup: + *out_len = len; + heap_free(inW); + return hr; +} + +static HRESULT url_guess_scheme(const WCHAR *url, WCHAR *out, DWORD *out_len) +{ + WCHAR reg_path[MAX_PATH], value[MAX_PATH], data[MAX_PATH]; + DWORD value_len, data_len, dwType, i; + WCHAR Wxx, Wyy; + HKEY newkey; + INT index; + BOOL j; + + MultiByteToWideChar(CP_ACP, 0, + "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes", -1, reg_path, MAX_PATH); + RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey); + index = 0; + while (value_len = data_len = MAX_PATH, + RegEnumValueW(newkey, index, value, &value_len, 0, &dwType, (LPVOID)data, &data_len) == 0) + { + TRACE("guess %d %s is %s\n", index, wine_dbgstr_w(value), wine_dbgstr_w(data)); + + j = FALSE; + for (i = 0; i < value_len; ++i) + { + Wxx = url[i]; + Wyy = value[i]; + /* remember that TRUE is not-equal */ + j = ChrCmpIW(Wxx, Wyy); + if (j) break; + } + if ((i == value_len) && !j) + { + if (lstrlenW(data) + lstrlenW(url) + 1 > *out_len) + { + *out_len = lstrlenW(data) + lstrlenW(url) + 1; + RegCloseKey(newkey); + return E_POINTER; + } + lstrcpyW(out, data); + lstrcatW(out, url); + *out_len = lstrlenW(out); + TRACE("matched and set to %s\n", wine_dbgstr_w(out)); + RegCloseKey(newkey); + return S_OK; + } + index++; + } + RegCloseKey(newkey); + return E_FAIL; +} + +static HRESULT url_create_from_path(const WCHAR *path, WCHAR *url, DWORD *url_len) +{ + PARSEDURLW parsed_url; + WCHAR *new_url; + DWORD needed; + HRESULT hr; + + parsed_url.cbSize = sizeof(parsed_url); + if (ParseURLW(path, &parsed_url) == S_OK) + { + if (parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) + { + needed = lstrlenW(path); + if (needed >= *url_len) + { + *url_len = needed + 1; + return E_POINTER; + } + else + { + *url_len = needed; + return S_FALSE; + } + } + } + + new_url = heap_alloc((lstrlenW(path) + 9) * sizeof(WCHAR)); /* "file:///" + path length + 1 */ + lstrcpyW(new_url, L"file:"); + if (is_drive_spec( path )) lstrcatW(new_url, L"///"); + lstrcatW(new_url, path); + hr = UrlEscapeW(new_url, url, url_len, URL_ESCAPE_PERCENT); + heap_free(new_url); + return hr; +} + +static HRESULT url_apply_default_scheme(const WCHAR *url, WCHAR *out, DWORD *length) +{ + DWORD data_len, dwType; + WCHAR data[MAX_PATH]; + HKEY newkey; + + /* get and prepend default */ + RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix", + 0, 1, &newkey); + data_len = sizeof(data); + RegQueryValueExW(newkey, NULL, 0, &dwType, (BYTE *)data, &data_len); + RegCloseKey(newkey); + if (lstrlenW(data) + lstrlenW(url) + 1 > *length) + { + *length = lstrlenW(data) + lstrlenW(url) + 1; + return E_POINTER; + } + lstrcpyW(out, data); + lstrcatW(out, url); + *length = lstrlenW(out); + TRACE("used default %s\n", wine_dbgstr_w(out)); + return S_OK; +} + +HRESULT WINAPI UrlApplySchemeW(const WCHAR *url, WCHAR *out, DWORD *length, DWORD flags) +{ + PARSEDURLW in_scheme; + DWORD res1; + HRESULT hr; + + TRACE("%s, %p, %p:out size %ld, %#lx\n", wine_dbgstr_w(url), out, length, length ? *length : 0, flags); + + if (!url || !out || !length) + return E_INVALIDARG; + + if (flags & URL_APPLY_GUESSFILE) + { + if ((*length > 1 && ':' == url[1]) || PathIsUNCW(url)) + { + res1 = *length; + hr = url_create_from_path(url, out, &res1); + if (hr == S_OK || hr == E_POINTER) + { + *length = res1; + return hr; + } + else if (hr == S_FALSE) + { + return hr; + } + } + } + + in_scheme.cbSize = sizeof(in_scheme); + /* See if the base has a scheme */ + res1 = ParseURLW(url, &in_scheme); + if (res1) + { + /* no scheme in input, need to see if we need to guess */ + if (flags & URL_APPLY_GUESSSCHEME) + { + if ((hr = url_guess_scheme(url, out, length)) != E_FAIL) + return hr; + } + } + + /* If we are here, then either invalid scheme, + * or no scheme and can't/failed guess. + */ + if ((((res1 == 0) && (flags & URL_APPLY_FORCEAPPLY)) || ((res1 != 0)) ) && (flags & URL_APPLY_DEFAULT)) + return url_apply_default_scheme(url, out, length); + + return S_FALSE; +} + +INT WINAPI UrlCompareA(const char *url1, const char *url2, BOOL ignore_slash) +{ + INT ret, len, len1, len2; + + if (!ignore_slash) + return strcmp(url1, url2); + len1 = strlen(url1); + if (url1[len1-1] == '/') len1--; + len2 = strlen(url2); + if (url2[len2-1] == '/') len2--; + if (len1 == len2) + return strncmp(url1, url2, len1); + len = min(len1, len2); + ret = strncmp(url1, url2, len); + if (ret) return ret; + if (len1 > len2) return 1; + return -1; +} + +INT WINAPI UrlCompareW(const WCHAR *url1, const WCHAR *url2, BOOL ignore_slash) +{ + size_t len, len1, len2; + INT ret; + + if (!ignore_slash) + return lstrcmpW(url1, url2); + len1 = lstrlenW(url1); + if (url1[len1-1] == '/') len1--; + len2 = lstrlenW(url2); + if (url2[len2-1] == '/') len2--; + if (len1 == len2) + return wcsncmp(url1, url2, len1); + len = min(len1, len2); + ret = wcsncmp(url1, url2, len); + if (ret) return ret; + if (len1 > len2) return 1; + return -1; +} + +HRESULT WINAPI UrlFixupW(const WCHAR *url, WCHAR *translatedUrl, DWORD maxChars) +{ + DWORD srcLen; + + FIXME("%s, %p, %ld stub\n", wine_dbgstr_w(url), translatedUrl, maxChars); + + if (!url) + return E_FAIL; + + srcLen = lstrlenW(url) + 1; + + /* For now just copy the URL directly */ + lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen); + + return S_OK; +} + +const char * WINAPI UrlGetLocationA(const char *url) +{ + PARSEDURLA base; + + base.cbSize = sizeof(base); + if (ParseURLA(url, &base) != S_OK) return NULL; /* invalid scheme */ + + /* if scheme is file: then never return pointer */ + if (!strncmp(base.pszProtocol, "file", min(4, base.cchProtocol))) + return NULL; + + /* Look for '#' and return its addr */ + return strchr(base.pszSuffix, '#'); +} + +const WCHAR * WINAPI UrlGetLocationW(const WCHAR *url) +{ + PARSEDURLW base; + + base.cbSize = sizeof(base); + if (ParseURLW(url, &base) != S_OK) return NULL; /* invalid scheme */ + + /* if scheme is file: then never return pointer */ + if (!wcsncmp(base.pszProtocol, L"file", min(4, base.cchProtocol))) + return NULL; + + /* Look for '#' and return its addr */ + return wcschr(base.pszSuffix, '#'); +} + +HRESULT WINAPI UrlGetPartA(const char *url, char *out, DWORD *out_len, DWORD part, DWORD flags) +{ + LPWSTR inW, outW; + DWORD len, len2; + HRESULT hr; + + if (!url || !out || !out_len || !*out_len) + return E_INVALIDARG; + + inW = heap_alloc(2 * INTERNET_MAX_URL_LENGTH * sizeof(WCHAR)); + outW = inW + INTERNET_MAX_URL_LENGTH; + + MultiByteToWideChar(CP_ACP, 0, url, -1, inW, INTERNET_MAX_URL_LENGTH); + + len = INTERNET_MAX_URL_LENGTH; + hr = UrlGetPartW(inW, outW, &len, part, flags); + if (FAILED(hr)) + { + heap_free(inW); + return hr; + } + + len2 = WideCharToMultiByte(CP_ACP, 0, outW, len + 1, NULL, 0, NULL, NULL); + if (len2 > *out_len) + { + *out_len = len2; + heap_free(inW); + return E_POINTER; + } + len2 = WideCharToMultiByte(CP_ACP, 0, outW, len + 1, out, *out_len, NULL, NULL); + *out_len = len2 - 1; + heap_free(inW); + if (hr == S_OK && !*out_len) hr = S_FALSE; + return hr; +} + +static const WCHAR *parse_url_element( const WCHAR *url, const WCHAR *separators ) +{ + const WCHAR *p; + + if ((p = wcspbrk( url, separators ))) + return p; + return url + wcslen( url ); +} + +static void parse_url( const WCHAR *url, struct parsed_url *pl ) +{ + const WCHAR *work; + + memset(pl, 0, sizeof(*pl)); + pl->scheme = url; + work = parse_scheme( pl->scheme ); + if (work < url + 2 || *work != ':') return; + pl->scheme_len = work - pl->scheme; + work++; + pl->scheme_number = get_scheme_code(pl->scheme, pl->scheme_len); + if (!is_slash( work[0] ) || !is_slash( work[1] )) + { + if (pl->scheme_number != URL_SCHEME_FILE) + pl->scheme_number = URL_SCHEME_UNKNOWN; + return; + } + work += 2; + + if (pl->scheme_number != URL_SCHEME_FILE) + { + pl->username = work; + work = parse_url_element( pl->username, L":@/\\?#" ); + pl->username_len = work - pl->username; + if (*work == ':') + { + pl->password = work + 1; + work = parse_url_element( pl->password, L"@/\\?#" ); + pl->password_len = work - pl->password; + if (*work == '@') + { + work++; + } + else + { + /* what we just parsed must be the hostname and port + * so reset pointers and clear then let it parse */ + pl->username_len = pl->password_len = 0; + work = pl->username; + pl->username = pl->password = 0; + } + } + else if (*work == '@') + { + /* no password */ + pl->password_len = 0; + pl->password = 0; + work++; + } + else + { + /* what was parsed was hostname, so reset pointers and let it parse */ + pl->username_len = pl->password_len = 0; + work = pl->username; + pl->username = pl->password = 0; + } + } + + pl->hostname = work; + if (pl->scheme_number == URL_SCHEME_FILE) + { + work = parse_url_element( pl->hostname, L"/\\?#" ); + pl->hostname_len = work - pl->hostname; + if (pl->hostname_len >= 2 && pl->hostname[1] == ':') + pl->hostname_len = 0; + } + else + { + work = parse_url_element( pl->hostname, L":/\\?#" ); + pl->hostname_len = work - pl->hostname; + + if (*work == ':') + { + pl->port = work + 1; + work = parse_url_element( pl->port, L"/\\?#" ); + pl->port_len = work - pl->port; + } + } + + if ((pl->query = wcschr( work, '?' ))) + { + ++pl->query; + pl->query_len = lstrlenW(pl->query); + } +} + +HRESULT WINAPI UrlGetPartW(const WCHAR *url, WCHAR *out, DWORD *out_len, DWORD part, DWORD flags) +{ + LPCWSTR addr, schaddr; + struct parsed_url pl; + DWORD size, schsize; + + TRACE("%s, %p, %p(%ld), %#lx, %#lx\n", wine_dbgstr_w(url), out, out_len, *out_len, part, flags); + + if (!url || !out || !out_len || !*out_len) + return E_INVALIDARG; + + parse_url(url, &pl); + + switch (pl.scheme_number) + { + case URL_SCHEME_FTP: + case URL_SCHEME_GOPHER: + case URL_SCHEME_HTTP: + case URL_SCHEME_HTTPS: + case URL_SCHEME_TELNET: + case URL_SCHEME_NEWS: + case URL_SCHEME_NNTP: + case URL_SCHEME_SNEWS: + break; + + case URL_SCHEME_FILE: + if (part != URL_PART_SCHEME && part != URL_PART_QUERY && part != URL_PART_HOSTNAME) + return E_FAIL; + break; + + default: + if (part != URL_PART_SCHEME && part != URL_PART_QUERY) + return E_FAIL; + } + + switch (part) + { + case URL_PART_SCHEME: + flags &= ~URL_PARTFLAG_KEEPSCHEME; + addr = pl.scheme; + size = pl.scheme_len; + break; + + case URL_PART_HOSTNAME: + addr = pl.hostname; + size = pl.hostname_len; + break; + + case URL_PART_USERNAME: + if (!pl.username) + return E_INVALIDARG; + addr = pl.username; + size = pl.username_len; + break; + + case URL_PART_PASSWORD: + if (!pl.password) + return E_INVALIDARG; + addr = pl.password; + size = pl.password_len; + break; + + case URL_PART_PORT: + if (!pl.port) + return E_INVALIDARG; + addr = pl.port; + size = pl.port_len; + break; + + case URL_PART_QUERY: + flags &= ~URL_PARTFLAG_KEEPSCHEME; + addr = pl.query; + size = pl.query_len; + break; + + default: + return E_INVALIDARG; + } + + if (flags == URL_PARTFLAG_KEEPSCHEME && pl.scheme_number != URL_SCHEME_FILE) + { + if (!pl.scheme || !pl.scheme_len) + return E_FAIL; + schaddr = pl.scheme; + schsize = pl.scheme_len; + if (*out_len < schsize + size + 2) + { + *out_len = schsize + size + 2; + return E_POINTER; + } + memcpy(out, schaddr, schsize*sizeof(WCHAR)); + out[schsize] = ':'; + memcpy(out + schsize+1, addr, size*sizeof(WCHAR)); + out[schsize+1+size] = 0; + *out_len = schsize + 1 + size; + } + else + { + if (*out_len < size + 1) + { + *out_len = size + 1; + return E_POINTER; + } + + if (part == URL_PART_SCHEME) + { + unsigned int i; + + for (i = 0; i < size; ++i) + out[i] = tolower( addr[i] ); + } + else + { + memcpy( out, addr, size * sizeof(WCHAR) ); + } + out[size] = 0; + *out_len = size; + } + TRACE("len=%ld %s\n", *out_len, wine_dbgstr_w(out)); + + return S_OK; +} + +BOOL WINAPI UrlIsA(const char *url, URLIS Urlis) +{ + const char *last; + PARSEDURLA base; + + TRACE("%s, %d\n", debugstr_a(url), Urlis); + + if (!url) + return FALSE; + + switch (Urlis) { + + case URLIS_OPAQUE: + base.cbSize = sizeof(base); + if (ParseURLA(url, &base) != S_OK) return FALSE; /* invalid scheme */ + return scheme_is_opaque( base.nScheme ); + + case URLIS_FILEURL: + return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, url, 5, "file:", 5) == CSTR_EQUAL); + + case URLIS_DIRECTORY: + last = url + strlen(url) - 1; + return (last >= url && (*last == '/' || *last == '\\' )); + + case URLIS_URL: + return PathIsURLA(url); + + case URLIS_NOHISTORY: + case URLIS_APPLIABLE: + case URLIS_HASQUERY: + default: + FIXME("(%s %d): stub\n", debugstr_a(url), Urlis); + } + + return FALSE; +} + +BOOL WINAPI UrlIsW(const WCHAR *url, URLIS Urlis) +{ + const WCHAR *last; + PARSEDURLW base; + + TRACE("%s, %d\n", debugstr_w(url), Urlis); + + if (!url) + return FALSE; + + switch (Urlis) + { + case URLIS_OPAQUE: + base.cbSize = sizeof(base); + if (ParseURLW(url, &base) != S_OK) return FALSE; /* invalid scheme */ + switch (base.nScheme) + { + case URL_SCHEME_MAILTO: + case URL_SCHEME_SHELL: + case URL_SCHEME_JAVASCRIPT: + case URL_SCHEME_VBSCRIPT: + case URL_SCHEME_ABOUT: + return TRUE; + } + return FALSE; + + case URLIS_FILEURL: + return !wcsnicmp( url, L"file:", 5 ); + + case URLIS_DIRECTORY: + last = url + lstrlenW(url) - 1; + return (last >= url && (*last == '/' || *last == '\\')); + + case URLIS_URL: + return PathIsURLW(url); + + case URLIS_NOHISTORY: + case URLIS_APPLIABLE: + case URLIS_HASQUERY: + default: + FIXME("(%s %d): stub\n", debugstr_w(url), Urlis); + } + + return FALSE; +} + +BOOL WINAPI UrlIsOpaqueA(const char *url) +{ + return UrlIsA(url, URLIS_OPAQUE); +} + +BOOL WINAPI UrlIsOpaqueW(const WCHAR *url) +{ + return UrlIsW(url, URLIS_OPAQUE); +} + +BOOL WINAPI UrlIsNoHistoryA(const char *url) +{ + return UrlIsA(url, URLIS_NOHISTORY); +} + +BOOL WINAPI UrlIsNoHistoryW(const WCHAR *url) +{ + return UrlIsW(url, URLIS_NOHISTORY); +} + +HRESULT WINAPI UrlCreateFromPathA(const char *path, char *url, DWORD *url_len, DWORD reserved) +{ + WCHAR bufW[INTERNET_MAX_URL_LENGTH]; + DWORD lenW = ARRAY_SIZE(bufW), lenA; + UNICODE_STRING pathW; + WCHAR *urlW = bufW; + HRESULT hr; + + if (!RtlCreateUnicodeStringFromAsciiz(&pathW, path)) + return E_INVALIDARG; + + if ((hr = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, reserved)) == E_POINTER) + { + urlW = heap_alloc(lenW * sizeof(WCHAR)); + hr = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, reserved); + } + + if (SUCCEEDED(hr)) + { + RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR)); + if (*url_len > lenA) + { + RtlUnicodeToMultiByteN(url, *url_len - 1, &lenA, urlW, lenW * sizeof(WCHAR)); + url[lenA] = 0; + *url_len = lenA; + } + else + { + *url_len = lenA + 1; + hr = E_POINTER; + } + } + if (urlW != bufW) + heap_free(urlW); + RtlFreeUnicodeString(&pathW); + return hr; +} + +HRESULT WINAPI UrlCreateFromPathW(const WCHAR *path, WCHAR *url, DWORD *url_len, DWORD reserved) +{ + HRESULT hr; + + TRACE("%s, %p, %p, %#lx\n", debugstr_w(path), url, url_len, reserved); + + if (reserved || !url || !url_len) + return E_INVALIDARG; + + hr = url_create_from_path(path, url, url_len); + if (hr == S_FALSE) + lstrcpyW(url, path); + + return hr; +} + +HRESULT WINAPI UrlCombineA(const char *base, const char *relative, char *combined, DWORD *combined_len, DWORD flags) +{ + WCHAR *baseW, *relativeW, *combinedW; + DWORD len, len2; + HRESULT hr; + + TRACE("%s, %s, %ld, %#lx\n", debugstr_a(base), debugstr_a(relative), combined_len ? *combined_len : 0, flags); + + if (!base || !relative || !combined_len) + return E_INVALIDARG; + + baseW = heap_alloc(3 * INTERNET_MAX_URL_LENGTH * sizeof(WCHAR)); + relativeW = baseW + INTERNET_MAX_URL_LENGTH; + combinedW = relativeW + INTERNET_MAX_URL_LENGTH; + + MultiByteToWideChar(CP_ACP, 0, base, -1, baseW, INTERNET_MAX_URL_LENGTH); + MultiByteToWideChar(CP_ACP, 0, relative, -1, relativeW, INTERNET_MAX_URL_LENGTH); + len = *combined_len; + + hr = UrlCombineW(baseW, relativeW, combined ? combinedW : NULL, &len, flags); + if (hr != S_OK) + { + *combined_len = len; + heap_free(baseW); + return hr; + } + + len2 = WideCharToMultiByte(CP_ACP, 0, combinedW, len, NULL, 0, NULL, NULL); + if (len2 > *combined_len) + { + *combined_len = len2; + heap_free(baseW); + return E_POINTER; + } + WideCharToMultiByte(CP_ACP, 0, combinedW, len+1, combined, *combined_len + 1, NULL, NULL); + *combined_len = len2; + heap_free(baseW); + return S_OK; +} + +HRESULT WINAPI UrlCombineW(const WCHAR *baseW, const WCHAR *relativeW, WCHAR *combined, DWORD *combined_len, DWORD flags) +{ + DWORD i, len, process_case = 0, myflags, sizeloc = 0; + LPWSTR work, preliminary, mbase, canonicalized; + PARSEDURLW base, relative; + HRESULT hr; + + TRACE("%s, %s, %ld, %#lx\n", debugstr_w(baseW), debugstr_w(relativeW), combined_len ? *combined_len : 0, flags); + + if (!baseW || !relativeW || !combined_len) + return E_INVALIDARG; + + base.cbSize = sizeof(base); + relative.cbSize = sizeof(relative); + + /* Get space for duplicates of the input and the output */ + preliminary = heap_alloc(3 * INTERNET_MAX_URL_LENGTH * sizeof(WCHAR)); + mbase = preliminary + INTERNET_MAX_URL_LENGTH; + canonicalized = mbase + INTERNET_MAX_URL_LENGTH; + *preliminary = '\0'; + + /* Canonicalize the base input prior to looking for the scheme */ + myflags = flags & (URL_DONT_SIMPLIFY | URL_UNESCAPE); + len = INTERNET_MAX_URL_LENGTH; + UrlCanonicalizeW(baseW, mbase, &len, myflags); + + /* See if the base has a scheme */ + if (ParseURLW(mbase, &base) != S_OK) + { + /* If base has no scheme return relative. */ + TRACE("no scheme detected in Base\n"); + process_case = 1; + } + else do + { + BOOL manual_search = FALSE; + + work = (LPWSTR)base.pszProtocol; + for (i = 0; i < base.cchProtocol; ++i) + work[i] = RtlDowncaseUnicodeChar(work[i]); + + /* mk is a special case */ + if (base.nScheme == URL_SCHEME_MK) + { + WCHAR *ptr = wcsstr(base.pszSuffix, L"::"); + if (ptr) + { + int delta; + + ptr += 2; + delta = ptr-base.pszSuffix; + base.cchProtocol += delta; + base.pszSuffix += delta; + base.cchSuffix -= delta; + } + } + else + { + /* get size of location field (if it exists) */ + work = (LPWSTR)base.pszSuffix; + sizeloc = 0; + if (*work++ == '/') + { + if (*work++ == '/') + { + /* At this point have start of location and + * it ends at next '/' or end of string. + */ + while (*work && (*work != '/')) work++; + sizeloc = (DWORD)(work - base.pszSuffix); + } + } + } + + /* If there is a '?', then the remaining part can only contain a + * query string or fragment, so start looking for the last leaf + * from the '?'. Otherwise, if there is a '#' and the characters + * immediately preceding it are ".htm[l]", then begin looking for + * the last leaf starting from the '#'. Otherwise the '#' is not + * meaningful and just start looking from the end. */ + if ((work = wcspbrk(base.pszSuffix + sizeloc, L"#?"))) + { + if (*work == '?' || base.nScheme == URL_SCHEME_HTTP || base.nScheme == URL_SCHEME_HTTPS) + manual_search = TRUE; + else if (work - base.pszSuffix > 4) + { + if (!wcsnicmp(work - 4, L".htm", 4)) manual_search = TRUE; + } + + if (!manual_search && work - base.pszSuffix > 5) + { + if (!wcsnicmp(work - 5, L".html", 5)) manual_search = TRUE; + } + } + + if (manual_search) + { + /* search backwards starting from the current position */ + while (*work != '/' && work > base.pszSuffix + sizeloc) + --work; + base.cchSuffix = work - base.pszSuffix + 1; + } + else + { + /* search backwards starting from the end of the string */ + work = wcsrchr((base.pszSuffix+sizeloc), '/'); + if (work) + { + len = (DWORD)(work - base.pszSuffix + 1); + base.cchSuffix = len; + } + else + base.cchSuffix = sizeloc; + } + + /* + * At this point: + * .pszSuffix points to location (starting with '//') + * .cchSuffix length of location (above) and rest less the last + * leaf (if any) + * sizeloc length of location (above) up to but not including + * the last '/' + */ + + if (ParseURLW(relativeW, &relative) != S_OK) + { + /* No scheme in relative */ + TRACE("no scheme detected in Relative\n"); + relative.pszSuffix = relativeW; /* case 3,4,5 depends on this */ + relative.cchSuffix = lstrlenW( relativeW ); + if (*relativeW == ':') + { + /* Case that is either left alone or uses base. */ + if (flags & URL_PLUGGABLE_PROTOCOL) + { + process_case = 5; + break; + } + process_case = 1; + break; + } + if (is_drive_spec( relativeW )) + { + /* case that becomes "file:///" */ + lstrcpyW(preliminary, L"file:///"); + process_case = 1; + break; + } + if (relativeW[0] == '/' && relativeW[1] == '/') + { + /* Relative has location and the rest. */ + process_case = 3; + break; + } + if (*relativeW == '/') + { + /* Relative is root to location. */ + process_case = 4; + break; + } + if (*relativeW == '#') + { + if (!(work = wcschr(base.pszSuffix+base.cchSuffix, '#'))) + work = (LPWSTR)base.pszSuffix + lstrlenW(base.pszSuffix); + + memcpy(preliminary, base.pszProtocol, (work-base.pszProtocol)*sizeof(WCHAR)); + preliminary[work-base.pszProtocol] = '\0'; + process_case = 1; + break; + } + process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3; + break; + } + else + { + work = (LPWSTR)relative.pszProtocol; + for (i = 0; i < relative.cchProtocol; ++i) + work[i] = RtlDowncaseUnicodeChar(work[i]); + } + + /* Handle cases where relative has scheme. */ + if ((base.cchProtocol == relative.cchProtocol) && !wcsncmp(base.pszProtocol, relative.pszProtocol, base.cchProtocol)) + { + /* since the schemes are the same */ + if (*relative.pszSuffix == '/' && *(relative.pszSuffix+1) == '/') + { + /* Relative replaces location and what follows. */ + process_case = 3; + break; + } + if (*relative.pszSuffix == '/') + { + /* Relative is root to location */ + process_case = 4; + break; + } + /* replace either just location if base's location starts with a + * slash or otherwise everything */ + process_case = (*base.pszSuffix == '/') ? 5 : 1; + break; + } + + if (*relative.pszSuffix == '/' && *(relative.pszSuffix+1) == '/') + { + /* Relative replaces scheme, location, and following and handles PLUGGABLE */ + process_case = 2; + break; + } + process_case = 1; + break; + } while (FALSE); /* a little trick to allow easy exit from nested if's */ + + hr = S_OK; + switch (process_case) + { + case 1: + /* Return relative appended to whatever is in combined (which may the string "file:///" */ + lstrcatW(preliminary, relativeW); + break; + + case 2: + /* Relative replaces scheme and location */ + lstrcpyW(preliminary, relativeW); + break; + + case 3: + /* Return the base scheme with relative. Basically keeps the scheme and replaces the domain and following. */ + memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR)); + work = preliminary + base.cchProtocol + 1; + lstrcpyW(work, relative.pszSuffix); + break; + + case 4: + /* Return the base scheme and location but everything after the location is relative. (Replace document from root on.) */ + memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR)); + work = preliminary + base.cchProtocol + 1 + sizeloc; + if (flags & URL_PLUGGABLE_PROTOCOL) + *(work++) = '/'; + lstrcpyW(work, relative.pszSuffix); + break; + + case 5: + /* Return the base without its document (if any) and append relative after its scheme. */ + memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1 + base.cchSuffix)*sizeof(WCHAR)); + work = preliminary + base.cchProtocol + 1 + base.cchSuffix - 1; + if (*work++ != '/') + *(work++) = '/'; + lstrcpyW(work, relative.pszSuffix); + break; + + default: + FIXME("Unexpected case %ld.\n", process_case); + hr = E_INVALIDARG; + } + + if (hr == S_OK) + { + if (*combined_len == 0) + *combined_len = 1; + hr = UrlCanonicalizeW(preliminary, canonicalized, combined_len, flags & ~URL_FILE_USE_PATHURL); + if (SUCCEEDED(hr) && combined) + lstrcpyW( combined, canonicalized ); + + TRACE("return-%ld len=%ld, %s\n", process_case, *combined_len, debugstr_w(combined)); + } + + heap_free(preliminary); + return hr; +} + +HRESULT WINAPI HashData(const unsigned char *src, DWORD src_len, unsigned char *dest, DWORD dest_len) +{ + INT src_count = src_len - 1, dest_count = dest_len - 1; + + if (!src || !dest) + return E_INVALIDARG; + + while (dest_count >= 0) + { + dest[dest_count] = (dest_count & 0xff); + dest_count--; + } + + while (src_count >= 0) + { + dest_count = dest_len - 1; + while (dest_count >= 0) + { + dest[dest_count] = hashdata_lookup[src[src_count] ^ dest[dest_count]]; + dest_count--; + } + src_count--; + } + + return S_OK; +} + +HRESULT WINAPI UrlHashA(const char *url, unsigned char *dest, DWORD dest_len) +{ + __TRY + { + HashData((const BYTE *)url, (int)strlen(url), dest, dest_len); + } + __EXCEPT_PAGE_FAULT + { + return E_INVALIDARG; + } + __ENDTRY + return S_OK; +} + +HRESULT WINAPI UrlHashW(const WCHAR *url, unsigned char *dest, DWORD dest_len) +{ + char urlA[MAX_PATH]; + + TRACE("%s, %p, %ld\n", debugstr_w(url), dest, dest_len); + + __TRY + { + WideCharToMultiByte(CP_ACP, 0, url, -1, urlA, MAX_PATH, NULL, NULL); + HashData((const BYTE *)urlA, (int)strlen(urlA), dest, dest_len); + } + __EXCEPT_PAGE_FAULT + { + return E_INVALIDARG; + } + __ENDTRY + return S_OK; +} + +BOOL WINAPI IsInternetESCEnabled(void) +{ + FIXME(": stub\n"); + return FALSE; +} diff --git a/dll/win32/KernelBase/wine/process.c b/dll/win32/KernelBase/wine/process.c new file mode 100644 index 0000000000000..2d08481cd3526 --- /dev/null +++ b/dll/win32/KernelBase/wine/process.c @@ -0,0 +1,1854 @@ +/* + * Win32 processes + * + * Copyright 1996, 1998 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "wincontypes.h" +#include "winternl.h" + +#include "kernelbase.h" +#include "wine/debug.h" +#include "wine/condrv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(process); + +static DWORD shutdown_flags = 0; +static DWORD shutdown_priority = 0x280; + +/*********************************************************************** + * Processes + ***********************************************************************/ + + +/*********************************************************************** + * find_exe_file + */ +static BOOL find_exe_file( const WCHAR *name, WCHAR *buffer, DWORD buflen ) +{ + WCHAR *load_path; + BOOL ret; + + if (!set_ntstatus( RtlGetExePath( name, &load_path ))) return FALSE; + + TRACE( "looking for %s in %s\n", debugstr_w(name), debugstr_w(load_path) ); + + ret = (SearchPathW( load_path, name, L".exe", buflen, buffer, NULL ) || + /* not found, try without extension in case it is a Unix app */ + SearchPathW( load_path, name, NULL, buflen, buffer, NULL )); + + if (ret) /* make sure it can be opened, SearchPathW also returns directories */ + { + HANDLE handle = CreateFileW( buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, 0 ); + if ((ret = (handle != INVALID_HANDLE_VALUE))) CloseHandle( handle ); + } + RtlReleasePath( load_path ); + return ret; +} + + +/************************************************************************* + * get_file_name + * + * Helper for CreateProcess: retrieve the file name to load from the + * app name and command line. Store the file name in buffer, and + * return a possibly modified command line. + */ +static WCHAR *get_file_name( WCHAR *cmdline, WCHAR *buffer, DWORD buflen ) +{ + WCHAR *name, *pos, *first_space, *ret = NULL; + const WCHAR *p; + + /* first check for a quoted file name */ + + if (cmdline[0] == '"' && (p = wcschr( cmdline + 1, '"' ))) + { + int len = p - cmdline - 1; + /* extract the quoted portion as file name */ + if (!(name = RtlAllocateHeap( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) return NULL; + memcpy( name, cmdline + 1, len * sizeof(WCHAR) ); + name[len] = 0; + + if (!find_exe_file( name, buffer, buflen )) goto done; + ret = cmdline; /* no change necessary */ + goto done; + } + + /* now try the command-line word by word */ + + if (!(name = RtlAllocateHeap( GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR) ))) + return NULL; + pos = name; + p = cmdline; + first_space = NULL; + + for (;;) + { + while (*p && *p != ' ' && *p != '\t') *pos++ = *p++; + *pos = 0; + if (find_exe_file( name, buffer, buflen )) + { + ret = cmdline; + break; + } + if (!first_space) first_space = pos; + if (!(*pos++ = *p++)) break; + } + + if (!ret) + { + SetLastError( ERROR_FILE_NOT_FOUND ); + } + else if (first_space) /* build a new command-line with quotes */ + { + if (!(ret = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(cmdline) + 3) * sizeof(WCHAR) ))) + goto done; + swprintf( ret, lstrlenW(cmdline) + 3, L"\"%s\"%s", name, p ); + } + + done: + RtlFreeHeap( GetProcessHeap(), 0, name ); + return ret; +} + + +/*********************************************************************** + * create_process_params + */ +static RTL_USER_PROCESS_PARAMETERS *create_process_params( const WCHAR *filename, const WCHAR *cmdline, + const WCHAR *cur_dir, void *env, DWORD flags, + const STARTUPINFOW *startup ) +{ + RTL_USER_PROCESS_PARAMETERS *params; + UNICODE_STRING imageW, curdirW, cmdlineW, titleW, desktopW, runtimeW, newdirW; + WCHAR imagepath[MAX_PATH]; + WCHAR *envW = env; + + if (!GetLongPathNameW( filename, imagepath, MAX_PATH )) lstrcpynW( imagepath, filename, MAX_PATH ); + if (!GetFullPathNameW( imagepath, MAX_PATH, imagepath, NULL )) lstrcpynW( imagepath, filename, MAX_PATH ); + + if (env && !(flags & CREATE_UNICODE_ENVIRONMENT)) /* convert environment to unicode */ + { + char *e = env; + DWORD lenW; + + while (*e) e += strlen(e) + 1; + e++; /* final null */ + lenW = MultiByteToWideChar( CP_ACP, 0, env, e - (char *)env, NULL, 0 ); + if ((envW = RtlAllocateHeap( GetProcessHeap(), 0, lenW * sizeof(WCHAR) ))) + MultiByteToWideChar( CP_ACP, 0, env, e - (char *)env, envW, lenW ); + } + + newdirW.Buffer = NULL; + if (cur_dir) + { + if (RtlDosPathNameToNtPathName_U( cur_dir, &newdirW, NULL, NULL )) + cur_dir = newdirW.Buffer + 4; /* skip \??\ prefix */ + else + cur_dir = NULL; + } + RtlInitUnicodeString( &imageW, imagepath ); + RtlInitUnicodeString( &curdirW, cur_dir ); + RtlInitUnicodeString( &cmdlineW, cmdline ); + RtlInitUnicodeString( &titleW, startup->lpTitle ? startup->lpTitle : imagepath ); + RtlInitUnicodeString( &desktopW, startup->lpDesktop ); + runtimeW.Buffer = (WCHAR *)startup->lpReserved2; + runtimeW.Length = runtimeW.MaximumLength = startup->cbReserved2; + if (RtlCreateProcessParametersEx( ¶ms, &imageW, NULL, cur_dir ? &curdirW : NULL, + &cmdlineW, envW, &titleW, &desktopW, + NULL, &runtimeW, PROCESS_PARAMS_FLAG_NORMALIZED )) + { + RtlFreeUnicodeString( &newdirW ); + if (envW != env) RtlFreeHeap( GetProcessHeap(), 0, envW ); + return NULL; + } + RtlFreeUnicodeString( &newdirW ); + + if (!(flags & CREATE_NEW_PROCESS_GROUP)) + params->ProcessGroupId = NtCurrentTeb()->Peb->ProcessParameters->ProcessGroupId; + else if (!(flags & CREATE_NEW_CONSOLE)) + params->ConsoleFlags = 1; + + if (flags & CREATE_NEW_CONSOLE) params->ConsoleHandle = CONSOLE_HANDLE_ALLOC; + else if (!(flags & DETACHED_PROCESS)) + { + if (flags & CREATE_NO_WINDOW) params->ConsoleHandle = CONSOLE_HANDLE_ALLOC_NO_WINDOW; + else + { + params->ConsoleHandle = NtCurrentTeb()->Peb->ProcessParameters->ConsoleHandle; + if (!params->ConsoleHandle) params->ConsoleHandle = CONSOLE_HANDLE_ALLOC; + } + } + + if (startup->dwFlags & STARTF_USESTDHANDLES) + { + params->hStdInput = startup->hStdInput; + params->hStdOutput = startup->hStdOutput; + params->hStdError = startup->hStdError; + } + else if (!(flags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE))) + { + params->hStdInput = NtCurrentTeb()->Peb->ProcessParameters->hStdInput; + params->hStdOutput = NtCurrentTeb()->Peb->ProcessParameters->hStdOutput; + params->hStdError = NtCurrentTeb()->Peb->ProcessParameters->hStdError; + } + + if (params->hStdInput == INVALID_HANDLE_VALUE) params->hStdInput = NULL; + if (params->hStdOutput == INVALID_HANDLE_VALUE) params->hStdOutput = NULL; + if (params->hStdError == INVALID_HANDLE_VALUE) params->hStdError = NULL; + + params->dwX = startup->dwX; + params->dwY = startup->dwY; + params->dwXSize = startup->dwXSize; + params->dwYSize = startup->dwYSize; + params->dwXCountChars = startup->dwXCountChars; + params->dwYCountChars = startup->dwYCountChars; + params->dwFillAttribute = startup->dwFillAttribute; + params->dwFlags = startup->dwFlags; + params->wShowWindow = startup->wShowWindow; + + if (envW != env) RtlFreeHeap( GetProcessHeap(), 0, envW ); + return params; +} + +struct proc_thread_attr +{ + DWORD_PTR attr; + SIZE_T size; + void *value; +}; + +struct _PROC_THREAD_ATTRIBUTE_LIST +{ + DWORD mask; /* bitmask of items in list */ + DWORD size; /* max number of items in list */ + DWORD count; /* number of items in list */ + DWORD pad; + DWORD_PTR unk; + struct proc_thread_attr attrs[1]; +}; + +/*********************************************************************** + * create_nt_process + */ +static NTSTATUS create_nt_process( HANDLE token, HANDLE debug, SECURITY_ATTRIBUTES *psa, + SECURITY_ATTRIBUTES *tsa, DWORD process_flags, + RTL_USER_PROCESS_PARAMETERS *params, + RTL_USER_PROCESS_INFORMATION *info, + HANDLE parent, USHORT machine, + const struct proc_thread_attr *handle_list, + const struct proc_thread_attr *job_list) +{ + OBJECT_ATTRIBUTES process_attr, thread_attr; + PS_CREATE_INFO create_info; + ULONG_PTR buffer[offsetof( PS_ATTRIBUTE_LIST, Attributes[9] ) / sizeof(ULONG_PTR)]; + PS_ATTRIBUTE_LIST *attr = (PS_ATTRIBUTE_LIST *)buffer; + UNICODE_STRING nameW; + NTSTATUS status; + UINT pos = 0; + + if (!params->ImagePathName.Buffer[0]) return STATUS_OBJECT_PATH_NOT_FOUND; + status = RtlDosPathNameToNtPathName_U_WithStatus( params->ImagePathName.Buffer, &nameW, NULL, NULL ); + if (!status) + { + RtlNormalizeProcessParams( params ); + + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_IMAGE_NAME; + attr->Attributes[pos].Size = nameW.Length; + attr->Attributes[pos].ValuePtr = nameW.Buffer; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_CLIENT_ID; + attr->Attributes[pos].Size = sizeof(info->ClientId); + attr->Attributes[pos].ValuePtr = &info->ClientId; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_IMAGE_INFO; + attr->Attributes[pos].Size = sizeof(info->ImageInformation); + attr->Attributes[pos].ValuePtr = &info->ImageInformation; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + if (parent) + { + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_PARENT_PROCESS; + attr->Attributes[pos].Size = sizeof(parent); + attr->Attributes[pos].ValuePtr = parent; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + } + if ((process_flags & PROCESS_CREATE_FLAGS_INHERIT_HANDLES) && handle_list) + { + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_HANDLE_LIST; + attr->Attributes[pos].Size = handle_list->size; + attr->Attributes[pos].ValuePtr = handle_list->value; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + } + if (token) + { + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_TOKEN; + attr->Attributes[pos].Size = sizeof(token); + attr->Attributes[pos].ValuePtr = token; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + } + if (debug) + { + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_DEBUG_PORT; + attr->Attributes[pos].Size = sizeof(debug); + attr->Attributes[pos].ValuePtr = debug; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + } + if (job_list) + { + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_JOB_LIST; + attr->Attributes[pos].Size = job_list->size; + attr->Attributes[pos].ValuePtr = job_list->value; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + } + if (machine) + { + attr->Attributes[pos].Attribute = PS_ATTRIBUTE_MACHINE_TYPE; + attr->Attributes[pos].Size = sizeof(machine); + attr->Attributes[pos].Value = machine; + attr->Attributes[pos].ReturnLength = NULL; + pos++; + } + attr->TotalLength = offsetof( PS_ATTRIBUTE_LIST, Attributes[pos] ); + + InitializeObjectAttributes( &process_attr, NULL, 0, NULL, psa ? psa->lpSecurityDescriptor : NULL ); + InitializeObjectAttributes( &thread_attr, NULL, 0, NULL, tsa ? tsa->lpSecurityDescriptor : NULL ); + + status = NtCreateUserProcess( &info->Process, &info->Thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, + &process_attr, &thread_attr, process_flags, + THREAD_CREATE_FLAGS_CREATE_SUSPENDED, params, + &create_info, attr ); + + RtlFreeUnicodeString( &nameW ); + } + return status; +} + + +/*********************************************************************** + * create_vdm_process + */ +static NTSTATUS create_vdm_process( HANDLE token, HANDLE debug, SECURITY_ATTRIBUTES *psa, + SECURITY_ATTRIBUTES *tsa, DWORD flags, + RTL_USER_PROCESS_PARAMETERS *params, + RTL_USER_PROCESS_INFORMATION *info ) +{ + const WCHAR *winevdm = (is_win64 || is_wow64 ? + L"C:\\windows\\syswow64\\winevdm.exe" : + L"C:\\windows\\system32\\winevdm.exe"); + WCHAR *newcmdline; + NTSTATUS status; + UINT len; + + len = (lstrlenW(params->ImagePathName.Buffer) + lstrlenW(params->CommandLine.Buffer) + + lstrlenW(winevdm) + 16); + + if (!(newcmdline = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + + swprintf( newcmdline, len, L"%s --app-name \"%s\" %s", + winevdm, params->ImagePathName.Buffer, params->CommandLine.Buffer ); + RtlInitUnicodeString( ¶ms->ImagePathName, winevdm ); + RtlInitUnicodeString( ¶ms->CommandLine, newcmdline ); + status = create_nt_process( token, debug, psa, tsa, flags, params, info, 0, 0, NULL, NULL ); + HeapFree( GetProcessHeap(), 0, newcmdline ); + return status; +} + + +/*********************************************************************** + * create_cmd_process + */ +static NTSTATUS create_cmd_process( HANDLE token, HANDLE debug, SECURITY_ATTRIBUTES *psa, + SECURITY_ATTRIBUTES *tsa, DWORD flags, + RTL_USER_PROCESS_PARAMETERS *params, + RTL_USER_PROCESS_INFORMATION *info ) +{ + WCHAR comspec[MAX_PATH]; + WCHAR *newcmdline; + NTSTATUS status; + UINT len; + + if (!GetEnvironmentVariableW( L"COMSPEC", comspec, ARRAY_SIZE( comspec ))) + lstrcpyW( comspec, L"C:\\windows\\system32\\cmd.exe" ); + + len = lstrlenW(comspec) + 7 + lstrlenW(params->CommandLine.Buffer) + 2; + if (!(newcmdline = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + return STATUS_NO_MEMORY; + + swprintf( newcmdline, len, L"%s /s/c \"%s\"", comspec, params->CommandLine.Buffer ); + RtlInitUnicodeString( ¶ms->ImagePathName, comspec ); + RtlInitUnicodeString( ¶ms->CommandLine, newcmdline ); + status = create_nt_process( token, debug, psa, tsa, flags, params, info, 0, 0, NULL, NULL ); + RtlFreeHeap( GetProcessHeap(), 0, newcmdline ); + return status; +} + + +/********************************************************************* + * CloseHandle (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CloseHandle( HANDLE handle ) +{ + if (handle == (HANDLE)STD_INPUT_HANDLE) + handle = InterlockedExchangePointer( &NtCurrentTeb()->Peb->ProcessParameters->hStdInput, 0 ); + else if (handle == (HANDLE)STD_OUTPUT_HANDLE) + handle = InterlockedExchangePointer( &NtCurrentTeb()->Peb->ProcessParameters->hStdOutput, 0 ); + else if (handle == (HANDLE)STD_ERROR_HANDLE) + handle = InterlockedExchangePointer( &NtCurrentTeb()->Peb->ProcessParameters->hStdError, 0 ); + + return set_ntstatus( NtClose( handle )); +} + + +/********************************************************************** + * CreateProcessAsUserA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessAsUserA( HANDLE token, const char *app_name, char *cmd_line, + SECURITY_ATTRIBUTES *process_attr, + SECURITY_ATTRIBUTES *thread_attr, + BOOL inherit, DWORD flags, void *env, + const char *cur_dir, STARTUPINFOA *startup_info, + PROCESS_INFORMATION *info ) +{ + return CreateProcessInternalA( token, app_name, cmd_line, process_attr, thread_attr, + inherit, flags, env, cur_dir, startup_info, info, NULL ); +} + + +/********************************************************************** + * CreateProcessAsUserW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessAsUserW( HANDLE token, const WCHAR *app_name, WCHAR *cmd_line, + SECURITY_ATTRIBUTES *process_attr, + SECURITY_ATTRIBUTES *thread_attr, + BOOL inherit, DWORD flags, void *env, + const WCHAR *cur_dir, STARTUPINFOW *startup_info, + PROCESS_INFORMATION *info ) +{ + return CreateProcessInternalW( token, app_name, cmd_line, process_attr, thread_attr, + inherit, flags, env, cur_dir, startup_info, info, NULL ); +} + +/********************************************************************** + * CreateProcessInternalA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalA( HANDLE token, const char *app_name, char *cmd_line, + SECURITY_ATTRIBUTES *process_attr, + SECURITY_ATTRIBUTES *thread_attr, + BOOL inherit, DWORD flags, void *env, + const char *cur_dir, STARTUPINFOA *startup_info, + PROCESS_INFORMATION *info, HANDLE *new_token ) +{ + BOOL ret = FALSE; + WCHAR *app_nameW = NULL, *cmd_lineW = NULL, *cur_dirW = NULL; + UNICODE_STRING desktopW, titleW; + STARTUPINFOEXW infoW; + + desktopW.Buffer = NULL; + titleW.Buffer = NULL; + if (app_name && !(app_nameW = file_name_AtoW( app_name, TRUE ))) goto done; + if (cmd_line && !(cmd_lineW = file_name_AtoW( cmd_line, TRUE ))) goto done; + if (cur_dir && !(cur_dirW = file_name_AtoW( cur_dir, TRUE ))) goto done; + + if (startup_info->lpDesktop) RtlCreateUnicodeStringFromAsciiz( &desktopW, startup_info->lpDesktop ); + if (startup_info->lpTitle) RtlCreateUnicodeStringFromAsciiz( &titleW, startup_info->lpTitle ); + + memcpy( &infoW.StartupInfo, startup_info, sizeof(infoW.StartupInfo) ); + infoW.StartupInfo.lpDesktop = desktopW.Buffer; + infoW.StartupInfo.lpTitle = titleW.Buffer; + + if (flags & EXTENDED_STARTUPINFO_PRESENT) + infoW.lpAttributeList = ((STARTUPINFOEXW *)startup_info)->lpAttributeList; + + ret = CreateProcessInternalW( token, app_nameW, cmd_lineW, process_attr, thread_attr, + inherit, flags, env, cur_dirW, (STARTUPINFOW *)&infoW, info, new_token ); +done: + RtlFreeHeap( GetProcessHeap(), 0, app_nameW ); + RtlFreeHeap( GetProcessHeap(), 0, cmd_lineW ); + RtlFreeHeap( GetProcessHeap(), 0, cur_dirW ); + RtlFreeUnicodeString( &desktopW ); + RtlFreeUnicodeString( &titleW ); + return ret; +} + +/********************************************************************** + * CreateProcessInternalW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR *app_name, WCHAR *cmd_line, + SECURITY_ATTRIBUTES *process_attr, + SECURITY_ATTRIBUTES *thread_attr, + BOOL inherit, DWORD flags, void *env, + const WCHAR *cur_dir, STARTUPINFOW *startup_info, + PROCESS_INFORMATION *info, HANDLE *new_token ) +{ + const struct proc_thread_attr *handle_list = NULL, *job_list = NULL; + WCHAR name[MAX_PATH]; + WCHAR *p, *tidy_cmdline = cmd_line; + RTL_USER_PROCESS_PARAMETERS *params = NULL; + RTL_USER_PROCESS_INFORMATION rtl_info; + HANDLE parent = 0, debug = 0; + ULONG nt_flags = 0; + USHORT machine = 0; + NTSTATUS status; + + /* Process the AppName and/or CmdLine to get module name and path */ + + TRACE( "app %s cmdline %s\n", debugstr_w(app_name), debugstr_w(cmd_line) ); + + if (new_token) FIXME( "No support for returning created process token\n" ); + + if (app_name) + { + if (!cmd_line || !cmd_line[0]) /* no command-line, create one */ + { + if (!(tidy_cmdline = RtlAllocateHeap( GetProcessHeap(), 0, (lstrlenW(app_name)+3) * sizeof(WCHAR) ))) + return FALSE; + swprintf( tidy_cmdline, lstrlenW(app_name) + 3, L"\"%s\"", app_name ); + } + } + else + { + if (!(tidy_cmdline = get_file_name( cmd_line, name, ARRAY_SIZE(name) ))) return FALSE; + app_name = name; + } + + /* Warn if unsupported features are used */ + + if (flags & (IDLE_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | REALTIME_PRIORITY_CLASS | + CREATE_DEFAULT_ERROR_MODE | PROFILE_USER | PROFILE_KERNEL | PROFILE_SERVER)) + WARN( "(%s,...): ignoring some flags in %lx\n", debugstr_w(app_name), flags ); + + if (cur_dir) + { + DWORD attr = GetFileAttributesW( cur_dir ); + if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) + { + status = STATUS_NOT_A_DIRECTORY; + goto done; + } + } + + info->hThread = info->hProcess = 0; + info->dwProcessId = info->dwThreadId = 0; + + if (!(params = create_process_params( app_name, tidy_cmdline, cur_dir, env, flags, startup_info ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + + if (flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) + { + if ((status = DbgUiConnectToDbg())) goto done; + debug = DbgUiGetThreadDebugObject(); + } + + if (flags & EXTENDED_STARTUPINFO_PRESENT) + { + struct _PROC_THREAD_ATTRIBUTE_LIST *attrs = + (struct _PROC_THREAD_ATTRIBUTE_LIST *)((STARTUPINFOEXW *)startup_info)->lpAttributeList; + unsigned int i; + + if (attrs) + { + for (i = 0; i < attrs->count; ++i) + { + switch(attrs->attrs[i].attr) + { + case PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: + parent = *(HANDLE *)attrs->attrs[i].value; + TRACE("PROC_THREAD_ATTRIBUTE_PARENT_PROCESS parent %p.\n", parent); + if (!parent) + { + status = STATUS_INVALID_HANDLE; + goto done; + } + break; + case PROC_THREAD_ATTRIBUTE_HANDLE_LIST: + handle_list = &attrs->attrs[i]; + TRACE("PROC_THREAD_ATTRIBUTE_HANDLE_LIST handle count %Iu.\n", attrs->attrs[i].size / sizeof(HANDLE)); + break; + case PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: + { + struct pseudo_console *console = attrs->attrs[i].value; + TRACE( "PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE %p reference %p\n", + console, console->reference ); + params->ConsoleHandle = console->reference; + break; + } + case PROC_THREAD_ATTRIBUTE_JOB_LIST: + job_list = &attrs->attrs[i]; + TRACE( "PROC_THREAD_ATTRIBUTE_JOB_LIST handle count %Iu.\n", + attrs->attrs[i].size / sizeof(HANDLE) ); + break; + case PROC_THREAD_ATTRIBUTE_MACHINE_TYPE: + machine = *(USHORT *)attrs->attrs[i].value; + TRACE( "PROC_THREAD_ATTRIBUTE_MACHINE %x.\n", machine ); + break; + default: + FIXME("Unsupported attribute %#Ix.\n", attrs->attrs[i].attr); + break; + } + } + } + } + + if (inherit) nt_flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES; + if (flags & DEBUG_ONLY_THIS_PROCESS) nt_flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT; + if (flags & CREATE_BREAKAWAY_FROM_JOB) nt_flags |= PROCESS_CREATE_FLAGS_BREAKAWAY; + if (flags & CREATE_SUSPENDED) nt_flags |= PROCESS_CREATE_FLAGS_SUSPENDED; + + status = create_nt_process( token, debug, process_attr, thread_attr, + nt_flags, params, &rtl_info, parent, machine, handle_list, job_list ); + switch (status) + { + case STATUS_SUCCESS: + break; + case STATUS_INVALID_IMAGE_WIN_16: + case STATUS_INVALID_IMAGE_NE_FORMAT: + case STATUS_INVALID_IMAGE_PROTECT: + TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(app_name) ); + status = create_vdm_process( token, debug, process_attr, thread_attr, + nt_flags, params, &rtl_info ); + break; + case STATUS_INVALID_IMAGE_NOT_MZ: + /* check for .com or .bat extension */ + if (!(p = wcsrchr( app_name, '.' ))) break; + if (!wcsicmp( p, L".com" ) || !wcsicmp( p, L".pif" )) + { + TRACE( "starting %s as DOS binary\n", debugstr_w(app_name) ); + status = create_vdm_process( token, debug, process_attr, thread_attr, + nt_flags, params, &rtl_info ); + } + else if (!wcsicmp( p, L".bat" ) || !wcsicmp( p, L".cmd" )) + { + TRACE( "starting %s as batch binary\n", debugstr_w(app_name) ); + status = create_cmd_process( token, debug, process_attr, thread_attr, + nt_flags, params, &rtl_info ); + } + break; + } + + if (!status) + { + info->hProcess = rtl_info.Process; + info->hThread = rtl_info.Thread; + info->dwProcessId = HandleToUlong( rtl_info.ClientId.UniqueProcess ); + info->dwThreadId = HandleToUlong( rtl_info.ClientId.UniqueThread ); + if (!(flags & CREATE_SUSPENDED)) NtResumeThread( rtl_info.Thread, NULL ); + TRACE( "started process pid %04lx tid %04lx\n", info->dwProcessId, info->dwThreadId ); + } + + done: + RtlDestroyProcessParameters( params ); + if (tidy_cmdline != cmd_line) HeapFree( GetProcessHeap(), 0, tidy_cmdline ); + return set_ntstatus( status ); +} + + +/********************************************************************** + * CreateProcessA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessA( const char *app_name, char *cmd_line, + SECURITY_ATTRIBUTES *process_attr, + SECURITY_ATTRIBUTES *thread_attr, BOOL inherit, + DWORD flags, void *env, const char *cur_dir, + STARTUPINFOA *startup_info, PROCESS_INFORMATION *info ) +{ + return CreateProcessInternalA( NULL, app_name, cmd_line, process_attr, thread_attr, + inherit, flags, env, cur_dir, startup_info, info, NULL ); +} + + +/********************************************************************** + * CreateProcessW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessW( const WCHAR *app_name, WCHAR *cmd_line, + SECURITY_ATTRIBUTES *process_attr, + SECURITY_ATTRIBUTES *thread_attr, BOOL inherit, DWORD flags, + void *env, const WCHAR *cur_dir, STARTUPINFOW *startup_info, + PROCESS_INFORMATION *info ) +{ + return CreateProcessInternalW( NULL, app_name, cmd_line, process_attr, thread_attr, + inherit, flags, env, cur_dir, startup_info, info, NULL ); +} + + +/********************************************************************** + * SetProcessInformation (kernelbase.@) + */ +BOOL WINAPI SetProcessInformation( HANDLE process, PROCESS_INFORMATION_CLASS info_class, void *info, DWORD size ) +{ + switch (info_class) + { + case ProcessMemoryPriority: + return set_ntstatus( NtSetInformationProcess( process, ProcessPagePriority, info, size )); + case ProcessPowerThrottling: + return set_ntstatus( NtSetInformationProcess( process, ProcessPowerThrottlingState, info, size )); + case ProcessLeapSecondInfo: + return set_ntstatus( NtSetInformationProcess( process, ProcessLeapSecondInformation, info, size )); + default: + FIXME("Unrecognized information class %d.\n", info_class); + return FALSE; + } +} + + +/********************************************************************* + * DuplicateHandle (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DuplicateHandle( HANDLE source_process, HANDLE source, + HANDLE dest_process, HANDLE *dest, + DWORD access, BOOL inherit, DWORD options ) +{ + return set_ntstatus( NtDuplicateObject( source_process, source, dest_process, dest, + access, inherit ? OBJ_INHERIT : 0, options )); +} + + +/*********************************************************************** + * GetApplicationRestartSettings (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ GetApplicationRestartSettings( HANDLE process, WCHAR *cmdline, + DWORD *size, DWORD *flags ) +{ + FIXME( "%p, %p, %p, %p)\n", process, cmdline, size, flags ); + return E_NOTIMPL; +} + + +/*********************************************************************** + * GetCurrentProcess (kernelbase.@) + */ +HANDLE WINAPI kernelbase_GetCurrentProcess(void) +{ + return (HANDLE)~(ULONG_PTR)0; +} + + +/*********************************************************************** + * GetCurrentProcessId (kernelbase.@) + */ +DWORD WINAPI kernelbase_GetCurrentProcessId(void) +{ + return HandleToULong( NtCurrentTeb()->ClientId.UniqueProcess ); +} + + +/*********************************************************************** + * GetErrorMode (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetErrorMode(void) +{ + UINT mode; + + NtQueryInformationProcess( GetCurrentProcess(), ProcessDefaultHardErrorMode, + &mode, sizeof(mode), NULL ); + return mode; +} + + +/*********************************************************************** + * GetExitCodeProcess (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetExitCodeProcess( HANDLE process, LPDWORD exit_code ) +{ + NTSTATUS status; + PROCESS_BASIC_INFORMATION pbi; + + status = NtQueryInformationProcess( process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL ); + if (!status && exit_code) *exit_code = pbi.ExitStatus; + return set_ntstatus( status ); +} + + +/********************************************************************* + * GetHandleInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetHandleInformation( HANDLE handle, DWORD *flags ) +{ + OBJECT_HANDLE_FLAG_INFORMATION info; + + if (!set_ntstatus( NtQueryObject( handle, ObjectHandleFlagInformation, &info, sizeof(info), NULL ))) + return FALSE; + + if (flags) + { + *flags = 0; + if (info.Inherit) *flags |= HANDLE_FLAG_INHERIT; + if (info.ProtectFromClose) *flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE; + } + return TRUE; +} + + +/*********************************************************************** + * GetPriorityClass (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetPriorityClass( HANDLE process ) +{ + PROCESS_BASIC_INFORMATION pbi; + + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessBasicInformation, + &pbi, sizeof(pbi), NULL ))) + return 0; + + switch (pbi.BasePriority) + { + case PROCESS_PRIOCLASS_IDLE: return IDLE_PRIORITY_CLASS; + case PROCESS_PRIOCLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS; + case PROCESS_PRIOCLASS_NORMAL: return NORMAL_PRIORITY_CLASS; + case PROCESS_PRIOCLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS; + case PROCESS_PRIOCLASS_HIGH: return HIGH_PRIORITY_CLASS; + case PROCESS_PRIOCLASS_REALTIME: return REALTIME_PRIORITY_CLASS; + default: return 0; + } +} + + +/*********************************************************************** + * GetProcessGroupAffinity (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProcessGroupAffinity( HANDLE process, USHORT *count, USHORT *array ) +{ + FIXME( "(%p,%p,%p): stub\n", process, count, array ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/****************************************************************** + * GetProcessHandleCount (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProcessHandleCount( HANDLE process, DWORD *count ) +{ + return set_ntstatus( NtQueryInformationProcess( process, ProcessHandleCount, + count, sizeof(*count), NULL )); +} + + +/*********************************************************************** + * GetProcessHeap (kernelbase.@) + */ +HANDLE WINAPI kernelbase_GetProcessHeap(void) +{ + return NtCurrentTeb()->Peb->ProcessHeap; +} + + +/********************************************************************* + * GetProcessId (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetProcessId( HANDLE process ) +{ + PROCESS_BASIC_INFORMATION pbi; + + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessBasicInformation, + &pbi, sizeof(pbi), NULL ))) + return 0; + return pbi.UniqueProcessId; +} + + +/********************************************************************** + * GetProcessMitigationPolicy (kernelbase.@) + */ +BOOL WINAPI /* DECLSPEC_HOTPATCH */ GetProcessMitigationPolicy( HANDLE process, PROCESS_MITIGATION_POLICY policy, + void *buffer, SIZE_T length ) +{ + FIXME( "(%p, %u, %p, %Iu): stub\n", process, policy, buffer, length ); + return TRUE; +} + + +/*********************************************************************** + * GetProcessPriorityBoost (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProcessPriorityBoost( HANDLE process, PBOOL disable ) +{ + FIXME( "(%p,%p): semi-stub\n", process, disable ); + *disable = FALSE; /* report that no boost is present */ + return TRUE; +} + + +/*********************************************************************** + * GetProcessShutdownParameters (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProcessShutdownParameters( LPDWORD level, LPDWORD flags ) +{ + *level = shutdown_priority; + *flags = shutdown_flags; + return TRUE; +} + + +/********************************************************************* + * GetProcessTimes (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProcessTimes( HANDLE process, FILETIME *create, FILETIME *exit, + FILETIME *kernel, FILETIME *user ) +{ + KERNEL_USER_TIMES time; + + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessTimes, &time, sizeof(time), NULL ))) + return FALSE; + + create->dwLowDateTime = time.CreateTime.u.LowPart; + create->dwHighDateTime = time.CreateTime.u.HighPart; + exit->dwLowDateTime = time.ExitTime.u.LowPart; + exit->dwHighDateTime = time.ExitTime.u.HighPart; + kernel->dwLowDateTime = time.KernelTime.u.LowPart; + kernel->dwHighDateTime = time.KernelTime.u.HighPart; + user->dwLowDateTime = time.UserTime.u.LowPart; + user->dwHighDateTime = time.UserTime.u.HighPart; + return TRUE; +} + + +/*********************************************************************** + * GetProcessVersion (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetProcessVersion( DWORD pid ) +{ + SECTION_IMAGE_INFORMATION info; + NTSTATUS status; + HANDLE process; + + if (pid && pid != GetCurrentProcessId()) + { + if (!(process = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pid ))) return 0; + status = NtQueryInformationProcess( process, ProcessImageInformation, &info, sizeof(info), NULL ); + CloseHandle( process ); + } + else status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageInformation, + &info, sizeof(info), NULL ); + + if (!set_ntstatus( status )) return 0; + return MAKELONG( info.MinorSubsystemVersion, info.MajorSubsystemVersion ); +} + + +/*********************************************************************** + * GetProcessWorkingSetSizeEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProcessWorkingSetSizeEx( HANDLE process, SIZE_T *minset, + SIZE_T *maxset, DWORD *flags) +{ + FIXME( "(%p,%p,%p,%p): stub\n", process, minset, maxset, flags ); + /* 32 MB working set size */ + if (minset) *minset = 32*1024*1024; + if (maxset) *maxset = 32*1024*1024; + if (flags) *flags = QUOTA_LIMITS_HARDWS_MIN_DISABLE | QUOTA_LIMITS_HARDWS_MAX_DISABLE; + return TRUE; +} + + +/****************************************************************************** + * IsProcessInJob (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsProcessInJob( HANDLE process, HANDLE job, BOOL *result ) +{ + NTSTATUS status = NtIsProcessInJob( process, job ); + + switch (status) + { + case STATUS_PROCESS_IN_JOB: + *result = TRUE; + return TRUE; + case STATUS_PROCESS_NOT_IN_JOB: + *result = FALSE; + return TRUE; + default: + return set_ntstatus( status ); + } +} + + +/*********************************************************************** + * IsProcessorFeaturePresent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsProcessorFeaturePresent ( DWORD feature ) +{ + return RtlIsProcessorFeaturePresent( feature ); +} + + +/********************************************************************** + * IsWow64Process2 (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsWow64Process2( HANDLE process, USHORT *machine, USHORT *native_machine ) +{ + return set_ntstatus( RtlWow64GetProcessMachines( process, machine, native_machine )); +} + + +/********************************************************************** + * IsWow64Process (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsWow64Process( HANDLE process, PBOOL wow64 ) +{ + ULONG_PTR pbi; + NTSTATUS status; + + status = NtQueryInformationProcess( process, ProcessWow64Information, &pbi, sizeof(pbi), NULL ); + if (!status) *wow64 = !!pbi; + return set_ntstatus( status ); +} + +/********************************************************************* + * GetProcessInformation (kernelbase.@) + */ +BOOL WINAPI GetProcessInformation( HANDLE process, PROCESS_INFORMATION_CLASS info_class, void *data, DWORD size ) +{ + switch (info_class) + { + case ProcessMachineTypeInfo: + { + PROCESS_MACHINE_INFORMATION *mi = data; + SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION machines[8]; + NTSTATUS status; + ULONG i; + + if (size != sizeof(*mi)) + { + SetLastError(ERROR_BAD_LENGTH); + return FALSE; + } + + status = NtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, &process, sizeof(process), + machines, sizeof(machines), NULL ); + if (status) return set_ntstatus( status ); + + for (i = 0; machines[i].Machine; i++) + { + if (machines[i].Process) + { + mi->ProcessMachine = machines[i].Machine; + mi->Res0 = 0; + mi->MachineAttributes = 0; + if (machines[i].KernelMode) + mi->MachineAttributes |= KernelEnabled; + if (machines[i].UserMode) + mi->MachineAttributes |= UserEnabled; + if (machines[i].WoW64Container) + mi->MachineAttributes |= Wow64Container; + + return TRUE; + } + } + + break; + } + default: + FIXME("Unsupported information class %d.\n", info_class); + } + + return FALSE; +} + + +/********************************************************************* + * OpenProcess (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenProcess( DWORD access, BOOL inherit, DWORD id ) +{ + HANDLE handle; + OBJECT_ATTRIBUTES attr; + CLIENT_ID cid; + + if (GetVersion() & 0x80000000) access = PROCESS_ALL_ACCESS; + + attr.Length = sizeof(OBJECT_ATTRIBUTES); + attr.RootDirectory = 0; + attr.Attributes = inherit ? OBJ_INHERIT : 0; + attr.ObjectName = NULL; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + cid.UniqueProcess = ULongToHandle(id); + cid.UniqueThread = 0; + + if (!set_ntstatus( NtOpenProcess( &handle, access, &attr, &cid ))) return NULL; + return handle; +} + + +/*********************************************************************** + * ProcessIdToSessionId (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ProcessIdToSessionId( DWORD pid, DWORD *id ) +{ + HANDLE process; + NTSTATUS status; + + if (pid == GetCurrentProcessId()) + { + *id = NtCurrentTeb()->Peb->SessionId; + return TRUE; + } + if (!(process = OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid ))) return FALSE; + status = NtQueryInformationProcess( process, ProcessSessionInformation, id, sizeof(*id), NULL ); + CloseHandle( process ); + return set_ntstatus( status ); +} + + +/*********************************************************************** + * QueryProcessCycleTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryProcessCycleTime( HANDLE process, ULONG64 *cycle ) +{ + PROCESS_CYCLE_TIME_INFORMATION time; + + if (!set_ntstatus( NtQueryInformationProcess( process, ProcessCycleTime, &time, sizeof(time), NULL ) )) + return FALSE; + + *cycle = time.AccumulatedCycles; + return TRUE; +} + + +/*********************************************************************** + * SetErrorMode (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH SetErrorMode( UINT mode ) +{ + UINT old = GetErrorMode(); + + NtSetInformationProcess( GetCurrentProcess(), ProcessDefaultHardErrorMode, + &mode, sizeof(mode) ); + return old; +} + + +/************************************************************************* + * SetHandleCount (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH SetHandleCount( UINT count ) +{ + return count; +} + + +/********************************************************************* + * SetHandleInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetHandleInformation( HANDLE handle, DWORD mask, DWORD flags ) +{ + OBJECT_HANDLE_FLAG_INFORMATION info; + + /* if not setting both fields, retrieve current value first */ + if ((mask & (HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE)) != + (HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE)) + { + if (!set_ntstatus( NtQueryObject( handle, ObjectHandleFlagInformation, &info, sizeof(info), NULL ))) + return FALSE; + } + if (mask & HANDLE_FLAG_INHERIT) + info.Inherit = (flags & HANDLE_FLAG_INHERIT) != 0; + if (mask & HANDLE_FLAG_PROTECT_FROM_CLOSE) + info.ProtectFromClose = (flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0; + + return set_ntstatus( NtSetInformationObject( handle, ObjectHandleFlagInformation, &info, sizeof(info) )); +} + + +/*********************************************************************** + * SetPriorityClass (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetPriorityClass( HANDLE process, DWORD class ) +{ + PROCESS_PRIORITY_CLASS ppc; + + ppc.Foreground = FALSE; + switch (class) + { + case IDLE_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_IDLE; break; + case BELOW_NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_BELOW_NORMAL; break; + case NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_NORMAL; break; + case ABOVE_NORMAL_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_ABOVE_NORMAL; break; + case HIGH_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_HIGH; break; + case REALTIME_PRIORITY_CLASS: ppc.PriorityClass = PROCESS_PRIOCLASS_REALTIME; break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return set_ntstatus( NtSetInformationProcess( process, ProcessPriorityClass, &ppc, sizeof(ppc) )); +} + + +/*********************************************************************** + * SetProcessAffinityUpdateMode (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetProcessAffinityUpdateMode( HANDLE process, DWORD flags ) +{ + FIXME( "(%p,0x%08lx): stub\n", process, flags ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * SetProcessGroupAffinity (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetProcessGroupAffinity( HANDLE process, const GROUP_AFFINITY *new, + GROUP_AFFINITY *old ) +{ + FIXME( "(%p,%p,%p): stub\n", process, new, old ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/********************************************************************** + * SetProcessMitigationPolicy (kernelbase.@) + */ +BOOL WINAPI /* DECLSPEC_HOTPATCH */ SetProcessMitigationPolicy( PROCESS_MITIGATION_POLICY policy, + void *buffer, SIZE_T length ) +{ + FIXME( "(%d, %p, %Iu): stub\n", policy, buffer, length ); + return TRUE; +} + + +/*********************************************************************** + * SetProcessPriorityBoost (kernelbase.@) + */ +BOOL WINAPI /* DECLSPEC_HOTPATCH */ SetProcessPriorityBoost( HANDLE process, BOOL disable ) +{ + FIXME( "(%p,%d): stub\n", process, disable ); + return TRUE; +} + + +/*********************************************************************** + * SetProcessShutdownParameters (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetProcessShutdownParameters( DWORD level, DWORD flags ) +{ + FIXME( "(%08lx, %08lx): partial stub.\n", level, flags ); + shutdown_flags = flags; + shutdown_priority = level; + return TRUE; +} + + +/*********************************************************************** + * SetProcessWorkingSetSizeEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetProcessWorkingSetSizeEx( HANDLE process, SIZE_T minset, + SIZE_T maxset, DWORD flags ) +{ + return TRUE; +} + + +/****************************************************************************** + * TerminateProcess (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH TerminateProcess( HANDLE handle, DWORD exit_code ) +{ + if (!handle) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + return set_ntstatus( NtTerminateProcess( handle, exit_code )); +} + + +/*********************************************************************** + * Process startup information + ***********************************************************************/ + + +static char *command_lineA; +static WCHAR *command_lineW; + +/****************************************************************** + * init_startup_info + */ +void init_startup_info( RTL_USER_PROCESS_PARAMETERS *params ) +{ + ANSI_STRING ansi; + + command_lineW = params->CommandLine.Buffer; + if (!RtlUnicodeStringToAnsiString( &ansi, ¶ms->CommandLine, TRUE )) command_lineA = ansi.Buffer; +} + + +/********************************************************************** + * BaseFlushAppcompatCache (kernelbase.@) + */ +BOOL WINAPI BaseFlushAppcompatCache(void) +{ + FIXME( "stub\n" ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/*********************************************************************** + * GetCommandLineA (kernelbase.@) + */ +LPSTR WINAPI GetCommandLineA(void) +{ + return command_lineA; +} + + +/*********************************************************************** + * GetCommandLineW (kernelbase.@) + */ +LPWSTR WINAPI GetCommandLineW(void) +{ + return command_lineW; +} + + +/*********************************************************************** + * GetStartupInfoW (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH GetStartupInfoW( STARTUPINFOW *info ) +{ + RTL_USER_PROCESS_PARAMETERS *params; + + RtlAcquirePebLock(); + + params = RtlGetCurrentPeb()->ProcessParameters; + + info->cb = sizeof(*info); + info->lpReserved = NULL; + info->lpDesktop = params->Desktop.Buffer; + info->lpTitle = params->WindowTitle.Buffer; + info->dwX = params->dwX; + info->dwY = params->dwY; + info->dwXSize = params->dwXSize; + info->dwYSize = params->dwYSize; + info->dwXCountChars = params->dwXCountChars; + info->dwYCountChars = params->dwYCountChars; + info->dwFillAttribute = params->dwFillAttribute; + info->dwFlags = params->dwFlags; + info->wShowWindow = params->wShowWindow; + info->cbReserved2 = params->RuntimeInfo.MaximumLength; + info->lpReserved2 = params->RuntimeInfo.MaximumLength ? (void *)params->RuntimeInfo.Buffer : NULL; + if (params->dwFlags & STARTF_USESTDHANDLES) + { + info->hStdInput = params->hStdInput; + info->hStdOutput = params->hStdOutput; + info->hStdError = params->hStdError; + } + RtlReleasePebLock(); +} + + +/*********************************************************************** + * GetStdHandle (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH GetStdHandle( DWORD std_handle ) +{ + switch (std_handle) + { + case STD_INPUT_HANDLE: return NtCurrentTeb()->Peb->ProcessParameters->hStdInput; + case STD_OUTPUT_HANDLE: return NtCurrentTeb()->Peb->ProcessParameters->hStdOutput; + case STD_ERROR_HANDLE: return NtCurrentTeb()->Peb->ProcessParameters->hStdError; + } + SetLastError( ERROR_INVALID_HANDLE ); + return INVALID_HANDLE_VALUE; +} + + +/*********************************************************************** + * SetStdHandle (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetStdHandle( DWORD std_handle, HANDLE handle ) +{ + switch (std_handle) + { + case STD_INPUT_HANDLE: NtCurrentTeb()->Peb->ProcessParameters->hStdInput = handle; return TRUE; + case STD_OUTPUT_HANDLE: NtCurrentTeb()->Peb->ProcessParameters->hStdOutput = handle; return TRUE; + case STD_ERROR_HANDLE: NtCurrentTeb()->Peb->ProcessParameters->hStdError = handle; return TRUE; + } + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; +} + + +/*********************************************************************** + * SetStdHandleEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetStdHandleEx( DWORD std_handle, HANDLE handle, HANDLE *prev ) +{ + HANDLE *ptr; + + switch (std_handle) + { + case STD_INPUT_HANDLE: ptr = &NtCurrentTeb()->Peb->ProcessParameters->hStdInput; break; + case STD_OUTPUT_HANDLE: ptr = &NtCurrentTeb()->Peb->ProcessParameters->hStdOutput; break; + case STD_ERROR_HANDLE: ptr = &NtCurrentTeb()->Peb->ProcessParameters->hStdError; break; + default: + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + if (prev) *prev = *ptr; + *ptr = handle; + return TRUE; +} + + +/*********************************************************************** + * Process environment + ***********************************************************************/ + + +static inline SIZE_T get_env_length( const WCHAR *env ) +{ + const WCHAR *end = env; + while (*end) end += lstrlenW(end) + 1; + return end + 1 - env; +} + +/*********************************************************************** + * ExpandEnvironmentStringsA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count ) +{ + UNICODE_STRING us_src; + PWSTR dstW = NULL; + DWORD ret; + + RtlCreateUnicodeStringFromAsciiz( &us_src, src ); + if (count) + { + if (!(dstW = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)))) return 0; + ret = ExpandEnvironmentStringsW( us_src.Buffer, dstW, count); + if (ret) WideCharToMultiByte( CP_ACP, 0, dstW, ret, dst, count, NULL, NULL ); + } + else ret = ExpandEnvironmentStringsW( us_src.Buffer, NULL, 0 ); + + RtlFreeUnicodeString( &us_src ); + HeapFree( GetProcessHeap(), 0, dstW ); + return ret; +} + + +/*********************************************************************** + * ExpandEnvironmentStringsW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len ) +{ + UNICODE_STRING us_src, us_dst; + NTSTATUS status; + DWORD res; + + TRACE( "(%s %p %lu)\n", debugstr_w(src), dst, len ); + + RtlInitUnicodeString( &us_src, src ); + + /* make sure we don't overflow the maximum UNICODE_STRING size */ + len = min( len, UNICODE_STRING_MAX_CHARS ); + + us_dst.Length = 0; + us_dst.MaximumLength = len * sizeof(WCHAR); + us_dst.Buffer = dst; + + res = 0; + status = RtlExpandEnvironmentStrings_U( NULL, &us_src, &us_dst, &res ); + res /= sizeof(WCHAR); + if (!set_ntstatus( status )) + { + if (status != STATUS_BUFFER_TOO_SMALL) return 0; + if (len && dst) dst[len - 1] = 0; + } + return res; +} + + +/*********************************************************************** + * GetEnvironmentStrings (kernelbase.@) + * GetEnvironmentStringsA (kernelbase.@) + */ +LPSTR WINAPI DECLSPEC_HOTPATCH GetEnvironmentStringsA(void) +{ + LPWSTR env; + LPSTR ret; + SIZE_T lenA, lenW; + + RtlAcquirePebLock(); + env = NtCurrentTeb()->Peb->ProcessParameters->Environment; + lenW = get_env_length( env ); + lenA = WideCharToMultiByte( CP_ACP, 0, env, lenW, NULL, 0, NULL, NULL ); + if ((ret = HeapAlloc( GetProcessHeap(), 0, lenA ))) + WideCharToMultiByte( CP_ACP, 0, env, lenW, ret, lenA, NULL, NULL ); + RtlReleasePebLock(); + return ret; +} + + +/*********************************************************************** + * GetEnvironmentStringsW (kernelbase.@) + */ +LPWSTR WINAPI DECLSPEC_HOTPATCH GetEnvironmentStringsW(void) +{ + LPWSTR ret; + SIZE_T len; + + RtlAcquirePebLock(); + len = get_env_length( NtCurrentTeb()->Peb->ProcessParameters->Environment ) * sizeof(WCHAR); + if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) + memcpy( ret, NtCurrentTeb()->Peb->ProcessParameters->Environment, len ); + RtlReleasePebLock(); + return ret; +} + + +/*********************************************************************** + * SetEnvironmentStringsA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentStringsA( char *env ) +{ + WCHAR *envW; + const char *p = env; + DWORD len; + BOOL ret; + + for (p = env; *p; p += strlen( p ) + 1); + + len = MultiByteToWideChar( CP_ACP, 0, env, p - env, NULL, 0 ); + if (!(envW = HeapAlloc( GetProcessHeap(), 0, len ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + MultiByteToWideChar( CP_ACP, 0, env, p - env, envW, len ); + ret = SetEnvironmentStringsW( envW ); + HeapFree( GetProcessHeap(), 0, envW ); + return ret; +} + + +/*********************************************************************** + * SetEnvironmentStringsW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentStringsW( WCHAR *env ) +{ + WCHAR *p; + WCHAR *new_env; + NTSTATUS status; + + for (p = env; *p; p += wcslen( p ) + 1) + { + const WCHAR *eq = wcschr( p, '=' ); + if (!eq || eq == p) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + } + + if ((status = RtlCreateEnvironment( FALSE, &new_env ))) + return set_ntstatus( status ); + + for (p = env; *p; p += wcslen( p ) + 1) + { + const WCHAR *eq = wcschr( p, '=' ); + UNICODE_STRING var, value; + var.Buffer = p; + var.Length = (eq - p) * sizeof(WCHAR); + RtlInitUnicodeString( &value, eq + 1 ); + if ((status = RtlSetEnvironmentVariable( &new_env, &var, &value ))) + { + RtlDestroyEnvironment( new_env ); + return set_ntstatus( status ); + } + } + + RtlSetCurrentEnvironment( new_env, NULL ); + return TRUE; +} + + +/*********************************************************************** + * GetEnvironmentVariableA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size ) +{ + UNICODE_STRING us_name, us_value; + PWSTR valueW; + NTSTATUS status; + DWORD len, ret; + + /* limit the size to sane values */ + size = min( size, 32767 ); + if (!(valueW = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return 0; + + RtlCreateUnicodeStringFromAsciiz( &us_name, name ); + us_value.Length = 0; + us_value.MaximumLength = (size ? size - 1 : 0) * sizeof(WCHAR); + us_value.Buffer = valueW; + + status = RtlQueryEnvironmentVariable_U( NULL, &us_name, &us_value ); + len = us_value.Length / sizeof(WCHAR); + if (status == STATUS_BUFFER_TOO_SMALL) ret = len + 1; + else if (!set_ntstatus( status )) ret = 0; + else if (!size) ret = len + 1; + else + { + if (len) WideCharToMultiByte( CP_ACP, 0, valueW, len + 1, value, size, NULL, NULL ); + value[len] = 0; + ret = len; + } + + RtlFreeUnicodeString( &us_name ); + HeapFree( GetProcessHeap(), 0, valueW ); + return ret; +} + + +/*********************************************************************** + * GetEnvironmentVariableW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetEnvironmentVariableW( LPCWSTR name, LPWSTR val, DWORD size ) +{ + UNICODE_STRING us_name, us_value; + NTSTATUS status; + DWORD len; + + TRACE( "(%s %p %lu)\n", debugstr_w(name), val, size ); + + RtlInitUnicodeString( &us_name, name ); + us_value.Length = 0; + us_value.MaximumLength = (size ? size - 1 : 0) * sizeof(WCHAR); + us_value.Buffer = val; + + status = RtlQueryEnvironmentVariable_U( NULL, &us_name, &us_value ); + len = us_value.Length / sizeof(WCHAR); + if (status == STATUS_BUFFER_TOO_SMALL) return len + 1; + if (!set_ntstatus( status )) return 0; + if (!size) return len + 1; + val[len] = 0; + return len; +} + + +/*********************************************************************** + * FreeEnvironmentStringsA (kernelbase.@) + * FreeEnvironmentStringsW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FreeEnvironmentStringsW( LPWSTR ptr ) +{ + return HeapFree( GetProcessHeap(), 0, ptr ); +} + + +/*********************************************************************** + * SetEnvironmentVariableA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentVariableA( LPCSTR name, LPCSTR value ) +{ + UNICODE_STRING us_name, us_value; + BOOL ret; + + if (!name) + { + SetLastError( ERROR_ENVVAR_NOT_FOUND ); + return FALSE; + } + + RtlCreateUnicodeStringFromAsciiz( &us_name, name ); + if (value) + { + RtlCreateUnicodeStringFromAsciiz( &us_value, value ); + ret = SetEnvironmentVariableW( us_name.Buffer, us_value.Buffer ); + RtlFreeUnicodeString( &us_value ); + } + else ret = SetEnvironmentVariableW( us_name.Buffer, NULL ); + RtlFreeUnicodeString( &us_name ); + return ret; +} + + +/*********************************************************************** + * SetEnvironmentVariableW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value ) +{ + UNICODE_STRING us_name, us_value; + NTSTATUS status; + + TRACE( "(%s %s)\n", debugstr_w(name), debugstr_w(value) ); + + if (!name) + { + SetLastError( ERROR_ENVVAR_NOT_FOUND ); + return FALSE; + } + + RtlInitUnicodeString( &us_name, name ); + if (value) + { + RtlInitUnicodeString( &us_value, value ); + status = RtlSetEnvironmentVariable( NULL, &us_name, &us_value ); + } + else status = RtlSetEnvironmentVariable( NULL, &us_name, NULL ); + + return set_ntstatus( status ); +} + + +/*********************************************************************** + * Process/thread attribute lists + ***********************************************************************/ + +/*********************************************************************** + * InitializeProcThreadAttributeList (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH InitializeProcThreadAttributeList( struct _PROC_THREAD_ATTRIBUTE_LIST *list, + DWORD count, DWORD flags, SIZE_T *size ) +{ + SIZE_T needed; + BOOL ret = FALSE; + + TRACE( "(%p %ld %lx %p)\n", list, count, flags, size ); + + needed = FIELD_OFFSET( struct _PROC_THREAD_ATTRIBUTE_LIST, attrs[count] ); + if (list && *size >= needed) + { + list->mask = 0; + list->size = count; + list->count = 0; + list->unk = 0; + ret = TRUE; + } + else SetLastError( ERROR_INSUFFICIENT_BUFFER ); + + *size = needed; + return ret; +} + + +static inline DWORD validate_proc_thread_attribute( DWORD_PTR attr, SIZE_T size ) +{ + switch (attr) + { + case PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: + if (size != sizeof(HANDLE)) return ERROR_BAD_LENGTH; + break; + case PROC_THREAD_ATTRIBUTE_HANDLE_LIST: + if ((size / sizeof(HANDLE)) * sizeof(HANDLE) != size) return ERROR_BAD_LENGTH; + break; + case PROC_THREAD_ATTRIBUTE_IDEAL_PROCESSOR: + if (size != sizeof(PROCESSOR_NUMBER)) return ERROR_BAD_LENGTH; + break; + case PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY: + if (size != sizeof(DWORD) && size != sizeof(DWORD64)) return ERROR_BAD_LENGTH; + break; + case PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY: + if (size != sizeof(DWORD) && size != sizeof(DWORD64) && size != sizeof(DWORD64) * 2) + return ERROR_BAD_LENGTH; + break; + case PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: + if (size != sizeof(HPCON)) return ERROR_BAD_LENGTH; + break; + case PROC_THREAD_ATTRIBUTE_JOB_LIST: + if ((size / sizeof(HANDLE)) * sizeof(HANDLE) != size) return ERROR_BAD_LENGTH; + break; + case PROC_THREAD_ATTRIBUTE_MACHINE_TYPE: + if (size != sizeof(USHORT)) return ERROR_BAD_LENGTH; + break; + default: + FIXME( "Unhandled attribute %Iu\n", attr & PROC_THREAD_ATTRIBUTE_NUMBER ); + return ERROR_NOT_SUPPORTED; + } + return 0; +} + + +/*********************************************************************** + * UpdateProcThreadAttribute (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH UpdateProcThreadAttribute( struct _PROC_THREAD_ATTRIBUTE_LIST *list, + DWORD flags, DWORD_PTR attr, void *value, + SIZE_T size, void *prev_ret, SIZE_T *size_ret ) +{ + DWORD mask, err; + struct proc_thread_attr *entry; + + TRACE( "(%p %lx %08Ix %p %Id %p %p)\n", list, flags, attr, value, size, prev_ret, size_ret ); + + if (list->count >= list->size) + { + SetLastError( ERROR_GEN_FAILURE ); + return FALSE; + } + if ((err = validate_proc_thread_attribute( attr, size ))) + { + SetLastError( err ); + return FALSE; + } + + mask = 1 << (attr & PROC_THREAD_ATTRIBUTE_NUMBER); + if (list->mask & mask) + { + SetLastError( ERROR_OBJECT_NAME_EXISTS ); + return FALSE; + } + list->mask |= mask; + + entry = list->attrs + list->count; + entry->attr = attr; + entry->size = size; + entry->value = value; + list->count++; + return TRUE; +} + + +/*********************************************************************** + * DeleteProcThreadAttributeList (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH DeleteProcThreadAttributeList( struct _PROC_THREAD_ATTRIBUTE_LIST *list ) +{ + return; +} + + +/*********************************************************************** + * CompareObjectHandles (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CompareObjectHandles( HANDLE first, HANDLE second ) +{ + return set_ntstatus( NtCompareObjects( first, second )); +} diff --git a/dll/win32/KernelBase/wine/registry.c b/dll/win32/KernelBase/wine/registry.c new file mode 100644 index 0000000000000..8471376bf96b7 --- /dev/null +++ b/dll/win32/KernelBase/wine/registry.c @@ -0,0 +1,4177 @@ +/* + * Registry management + * + * Copyright 1996 Marcus Meissner + * Copyright 1998 Matthew Becker + * Copyright 1999 Sylvain St-Germain + * Copyright 1999 Alexandre Julliard + * Copyright 2017 Dmitry Timoshkov + * Copyright 2019 Nikolay Sivov for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winerror.h" +#include "winternl.h" +#include "winperf.h" +#include "winuser.h" +#include "shlwapi.h" +#include "sddl.h" + +#include "kernelbase.h" +#include "wine/debug.h" +#include "wine/exception.h" +#include "wine/heap.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(reg); + +#define HKEY_SPECIAL_ROOT_FIRST HKEY_CLASSES_ROOT +#define HKEY_SPECIAL_ROOT_LAST HKEY_DYN_DATA + +static const WCHAR * const root_key_names[] = +{ + L"\\Registry\\Machine\\Software\\Classes", + NULL, /* HKEY_CURRENT_USER is determined dynamically */ + L"\\Registry\\Machine", + L"\\Registry\\User", + NULL, /* HKEY_PERFORMANCE_DATA is not a real key */ + L"\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\Current", + L"\\Registry\\DynData" +}; + +static HKEY special_root_keys[ARRAY_SIZE(root_key_names)]; +static BOOL cache_disabled[ARRAY_SIZE(root_key_names)]; + +static CRITICAL_SECTION reg_mui_cs; +static CRITICAL_SECTION_DEBUG reg_mui_cs_debug = +{ + 0, 0, ®_mui_cs, + { ®_mui_cs_debug.ProcessLocksList, + ®_mui_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": reg_mui_cs") } +}; +static CRITICAL_SECTION reg_mui_cs = { ®_mui_cs_debug, -1, 0, 0, 0, 0 }; +struct mui_cache_entry { + struct list entry; + WCHAR *file_name; /* full path name */ + DWORD index; + LCID locale; + WCHAR *text; +}; +static struct list reg_mui_cache = LIST_INIT(reg_mui_cache); /* MRU */ +static unsigned int reg_mui_cache_count; +#define REG_MUI_CACHE_SIZE 8 + +#define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1') + +/* check if value type needs string conversion (Ansi<->Unicode) */ +static inline BOOL is_string( DWORD type ) +{ + return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ); +} + +/* check if current version is NT or Win95 */ +static inline BOOL is_version_nt(void) +{ + return !(GetVersion() & 0x80000000); +} + +static BOOL is_wow6432node( const UNICODE_STRING *name ) +{ + DWORD len = name->Length / sizeof(WCHAR); + return (len >= 11 && !wcsnicmp( name->Buffer, L"Wow6432Node\\", min( len, 12 ) )); +} + +static BOOL is_classes_root( const UNICODE_STRING *name ) +{ + static const WCHAR classes_root[] = L"\\Registry\\Machine\\Software\\Classes\\"; + DWORD classes_root_len = ARRAY_SIZE( classes_root ) - 1; + DWORD len = name->Length / sizeof(WCHAR); + return (len >= classes_root_len - 1 && !wcsnicmp( name->Buffer, classes_root, min( len, classes_root_len ) )); +} + +static BOOL is_classes_wow6432node( HKEY key ) +{ + char buffer[256], *buf_ptr = buffer; + KEY_NAME_INFORMATION *info = (KEY_NAME_INFORMATION *)buffer; + DWORD len = sizeof(buffer); + UNICODE_STRING name; + NTSTATUS status; + BOOL ret = FALSE; + + /* Obtain the name of the root key */ + status = NtQueryKey( key, KeyNameInformation, info, len, &len ); + if (status && status != STATUS_BUFFER_OVERFLOW) return FALSE; + + /* Retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( len ))) return FALSE; + info = (KEY_NAME_INFORMATION *)buf_ptr; + status = NtQueryKey( key, KeyNameInformation, info, len, &len ); + } + + /* Check if the key ends in Wow6432Node and if the root is the Classes key*/ + if (!status && info->NameLength / sizeof(WCHAR) >= 11) + { + name.Buffer = info->Name + info->NameLength / sizeof(WCHAR) - 11; + name.Length = 11 * sizeof(WCHAR); + if (is_wow6432node( &name )) + { + name.Buffer = info->Name; + name.Length = info->NameLength; + ret = is_classes_root( &name ); + } + } + + if (buf_ptr != buffer) heap_free( buf_ptr ); + + return ret; +} + +/* Open the Wow6432Node subkey of the specified key */ +static HANDLE open_wow6432node( HANDLE key ) +{ + UNICODE_STRING nameW = RTL_CONSTANT_STRING( L"Wow6432Node" ); + OBJECT_ATTRIBUTES attr; + HANDLE ret; + + attr.Length = sizeof(attr); + attr.RootDirectory = key; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED | KEY_WOW64_64KEY, &attr, 0 )) return key; + return ret; +} + +/* Open HKCR, which should already exist because it's used when we're in its Wow6432Node child */ +static HANDLE open_classes_root( void ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + HANDLE ret = 0; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, root_key_names[0] ); + NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 ); + return ret; +} + +static HKEY get_perflib_key( HANDLE key ) +{ + static const WCHAR performance_text[] = + L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"; + char buffer[200]; + OBJECT_NAME_INFORMATION *info = (OBJECT_NAME_INFORMATION *)buffer; + + if (!NtQueryObject( key, ObjectNameInformation, buffer, sizeof(buffer), NULL )) + { + if (!wcsicmp( info->Name.Buffer, performance_text )) + { + NtClose( key ); + return HKEY_PERFORMANCE_TEXT; + } + } + + return key; +} + +static NTSTATUS open_key( HKEY *retkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access, BOOL create ); + +static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access ) +{ + BOOL is_wow64_key = (is_win64 && (access & KEY_WOW64_32KEY)) || (is_wow64 && !(access & KEY_WOW64_64KEY)); + ACCESS_MASK access_64 = (access & ~KEY_WOW64_32KEY) | KEY_WOW64_64KEY; + DWORD i = 0, len = name->Length / sizeof(WCHAR); + WCHAR *buffer = name->Buffer; + UNICODE_STRING str; + NTSTATUS status; + + if (!root && len > 10 && !wcsnicmp( buffer, L"\\Registry\\", 10 )) i += 10; + if (i < len && buffer[i] == '\\') return STATUS_OBJECT_PATH_INVALID; + while (i < len && buffer[i] != '\\') i++; + + str.Buffer = name->Buffer; + str.Length = i * sizeof(WCHAR); + + if (i < len) + options &= ~REG_OPTION_OPEN_LINK; + + status = open_key( subkey, root, &str, options, access_64, FALSE ); + if (status == STATUS_OBJECT_NAME_NOT_FOUND && root && is_wow64_key) + { + /* Try to open the shared parent if we can't find the key in the Wow6432Node */ + if (!is_classes_wow6432node( root )) + return STATUS_OBJECT_NAME_NOT_FOUND; + + root = open_classes_root(); + status = open_key( subkey, root, &str, options, access_64, FALSE ); + + if (!status) + NtClose( root ); + else + *subkey = root; + } + + if (!status) + { + while (i < len && buffer[i] == '\\') i++; + + name->Buffer += i; + name->Length -= i * sizeof(WCHAR); + + if (is_wow64_key && !is_wow6432node( name )) + { + HKEY wow6432node = open_wow6432node( *subkey ); + if (wow6432node != *subkey) + { + NtClose( *subkey ); + *subkey = wow6432node; + } + } + } + + return status; +} + +static NTSTATUS open_wow6432node_parent( HKEY *retkey, HKEY root, DWORD options, ACCESS_MASK access ) +{ + char buffer[256], *buf_ptr = buffer; + KEY_NAME_INFORMATION *info = (KEY_NAME_INFORMATION *)buffer; + DWORD len = sizeof(buffer); + UNICODE_STRING name; + NTSTATUS status; + + /* Obtain the name of the root key */ + status = NtQueryKey( root, KeyNameInformation, info, len, &len ); + if (status && status != STATUS_BUFFER_OVERFLOW) return status; + + /* Retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( len ))) + return STATUS_NO_MEMORY; + info = (KEY_NAME_INFORMATION *)buf_ptr; + status = NtQueryKey( root, KeyNameInformation, info, len, &len ); + } + + if (status) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + return status; + } + + name.Buffer = info->Name; + name.Length = info->NameLength; + root = 0; + + /* Obtain the parent Wow6432Node if it exists */ + while (!status && name.Length) + { + status = open_subkey( retkey, root, &name, options & ~REG_OPTION_OPEN_LINK, access ); + if (root) NtClose( root ); + root = *retkey; + } + + if (buf_ptr != buffer) heap_free( buf_ptr ); + + return status; +} + +/* wrapper for NtOpenKeyEx to handle Wow6432 nodes */ +static NTSTATUS open_key( HKEY *retkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access, BOOL create ) +{ + BOOL is_wow64_key = (is_win64 && (access & KEY_WOW64_32KEY)) || (is_wow64 && !(access & KEY_WOW64_64KEY)); + HKEY subkey = 0, subkey_root = root; + NTSTATUS status = STATUS_SUCCESS; + BOOL was_wow6432node = TRUE; + + *retkey = NULL; + + if (!(is_win64 && (access & KEY_WOW64_32KEY)) && !create) + { + OBJECT_ATTRIBUTES attr; + + attr.Length = sizeof(attr); + attr.RootDirectory = root; + attr.ObjectName = name; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + if (options & REG_OPTION_OPEN_LINK) attr.Attributes |= OBJ_OPENLINK; + status = NtOpenKeyEx( (HANDLE *)retkey, access, &attr, options ); + if (status == STATUS_PREDEFINED_HANDLE) + { + *retkey = get_perflib_key( *retkey ); + status = STATUS_SUCCESS; + } + return status; + } + + if (root && (access & KEY_WOW64_32KEY) && !is_wow6432node( name )) + status = open_wow6432node_parent( &subkey_root, root, options, access ); + else if (root && is_wow64 && !(access & KEY_WOW64_64KEY) && !is_wow6432node( name )) + { + subkey_root = open_wow6432node( root ); + if (!is_classes_wow6432node( subkey_root ) && subkey_root != root) + { + NtClose( subkey_root ); + subkey_root = root; + } + } + + while (!status && (name->Length || !subkey)) + { + was_wow6432node = is_wow6432node( name ); + status = open_subkey( &subkey, subkey_root, name, options, access ); + if (subkey && subkey_root && subkey_root != root) NtClose( subkey_root ); + if (subkey) subkey_root = subkey; + } + + /* Return the shared parent if we didn't explicitly look for the Wow6432Node */ + if (!status && !was_wow6432node && is_wow64_key && is_classes_wow6432node( subkey_root )) + { + if (subkey_root && subkey_root != root) NtClose( subkey_root ); + subkey_root = open_classes_root(); + } + + if (!status || (status == STATUS_OBJECT_NAME_NOT_FOUND && create)) + *retkey = subkey_root; + else if (subkey_root && subkey_root != root) + NtClose( subkey_root ); + + return status; +} + +static NTSTATUS create_key( HKEY *retkey, HKEY root, UNICODE_STRING name, ULONG options, ACCESS_MASK access, + const UNICODE_STRING *class, PULONG dispos ); + +static NTSTATUS create_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access, + const UNICODE_STRING *class, PULONG dispos ) +{ + ACCESS_MASK access_64 = access & ~KEY_WOW64_32KEY; + DWORD i = 0, len = name->Length / sizeof(WCHAR); + WCHAR *buffer = name->Buffer; + UNICODE_STRING str; + NTSTATUS status; + + if (i < len && buffer[i] == '\\') return STATUS_OBJECT_PATH_INVALID; + while (i < len && buffer[i] != '\\') i++; + + str.Buffer = name->Buffer; + str.Length = i * sizeof(WCHAR); + + if (i < len) + options &= ~REG_OPTION_CREATE_LINK; + + status = create_key( subkey, root, str, options, access_64, class, dispos ); + if (!status) + { + while (i < len && buffer[i] == '\\') i++; + + name->Buffer += i; + name->Length -= i * sizeof(WCHAR); + } + + return status; +} + +/* wrapper for NtCreateKey that creates the key recursively if necessary */ +static NTSTATUS create_key( HKEY *retkey, HKEY root, UNICODE_STRING name, ULONG options, ACCESS_MASK access, + const UNICODE_STRING *class, PULONG dispos ) +{ + NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND; + HKEY subkey, subkey_root = root; + + *retkey = NULL; + + if (!(is_win64 && (access & KEY_WOW64_32KEY))) + { + OBJECT_ATTRIBUTES attr; + + attr.Length = sizeof(attr); + attr.RootDirectory = root; + attr.ObjectName = &name; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + if (options & REG_OPTION_OPEN_LINK) attr.Attributes |= OBJ_OPENLINK; + + status = NtCreateKey( (HANDLE *)retkey, access, &attr, 0, class, options, dispos ); + if (status == STATUS_PREDEFINED_HANDLE) + { + *retkey = get_perflib_key( *retkey ); + status = STATUS_SUCCESS; + } + + if (!status || status != STATUS_OBJECT_NAME_NOT_FOUND) + return status; + } + + status = open_key( &subkey_root, root, &name, options & REG_OPTION_OPEN_LINK, access, TRUE ); + if (!status && (options & REG_OPTION_CREATE_LINK)) + { + NtClose( subkey_root ); + status = STATUS_OBJECT_NAME_COLLISION; + } + + if (!status) + if (dispos) *dispos = REG_OPENED_EXISTING_KEY; + + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + { + status = STATUS_SUCCESS; + while (!status && name.Length) + { + status = create_subkey( &subkey, subkey_root, &name, options, access, class, dispos ); + if (subkey_root && subkey_root != root) NtClose( subkey_root ); + subkey_root = subkey; + } + } + + if (!status) + *retkey = subkey_root; + + return status; +} + +/* create one of the HKEY_* special root keys */ +static HKEY create_special_root_hkey( HKEY hkey, DWORD access ) +{ + HKEY ret = 0; + int idx = HandleToUlong(hkey) - HandleToUlong(HKEY_SPECIAL_ROOT_FIRST); + + if (HandleToUlong(hkey) == HandleToUlong(HKEY_CURRENT_USER)) + { + if (RtlOpenCurrentUser( access, (HANDLE *)&hkey )) return 0; + TRACE( "HKEY_CURRENT_USER -> %p\n", hkey ); + } + else + { + UNICODE_STRING name; + + RtlInitUnicodeString( &name, root_key_names[idx] ); + if (create_key( &hkey, 0, name, 0, access, NULL, NULL )) return 0; + TRACE( "%s -> %p\n", debugstr_w(name.Buffer), hkey ); + } + + if (!cache_disabled[idx]) + { + if (!(ret = InterlockedCompareExchangePointer( (void **)&special_root_keys[idx], hkey, 0 ))) + ret = hkey; + else + NtClose( hkey ); /* somebody beat us to it */ + } + else + ret = hkey; + return ret; +} + +/* map the hkey from special root to normal key if necessary */ +static inline HKEY get_special_root_hkey( HKEY hkey ) +{ + unsigned int index = HandleToUlong(hkey) - HandleToUlong(HKEY_SPECIAL_ROOT_FIRST); + + switch (HandleToUlong(hkey)) + { + case (LONG)(LONG_PTR)HKEY_CLASSES_ROOT: + case (LONG)(LONG_PTR)HKEY_CURRENT_USER: + case (LONG)(LONG_PTR)HKEY_LOCAL_MACHINE: + case (LONG)(LONG_PTR)HKEY_USERS: + case (LONG)(LONG_PTR)HKEY_CURRENT_CONFIG: + case (LONG)(LONG_PTR)HKEY_DYN_DATA: + if (special_root_keys[index]) + return special_root_keys[index]; + return create_special_root_hkey( hkey, MAXIMUM_ALLOWED ); + + default: + return hkey; + } +} + +static BOOL is_perf_key( HKEY key ) +{ + return HandleToUlong(key) == HandleToUlong(HKEY_PERFORMANCE_DATA) + || HandleToUlong(key) == HandleToUlong(HKEY_PERFORMANCE_TEXT) + || HandleToUlong(key) == HandleToUlong(HKEY_PERFORMANCE_NLSTEXT); +} + + +/****************************************************************************** + * RemapPredefinedHandleInternal (kernelbase.@) + */ +NTSTATUS WINAPI RemapPredefinedHandleInternal( HKEY hkey, HKEY override ) +{ + HKEY old_key; + int idx; + + TRACE("(%p %p)\n", hkey, override); + + if ((HandleToUlong(hkey) < HandleToUlong(HKEY_SPECIAL_ROOT_FIRST)) + || (HandleToUlong(hkey) > HandleToUlong(HKEY_SPECIAL_ROOT_LAST))) + return STATUS_INVALID_HANDLE; + idx = HandleToUlong(hkey) - HandleToUlong(HKEY_SPECIAL_ROOT_FIRST); + + if (override) + { + NTSTATUS status = NtDuplicateObject( GetCurrentProcess(), override, + GetCurrentProcess(), (HANDLE *)&override, + 0, 0, DUPLICATE_SAME_ACCESS ); + if (status) return status; + } + + old_key = InterlockedExchangePointer( (void **)&special_root_keys[idx], override ); + if (old_key) NtClose( old_key ); + return STATUS_SUCCESS; +} + + +/****************************************************************************** + * DisablePredefinedHandleTableInternal (kernelbase.@) + */ +NTSTATUS WINAPI DisablePredefinedHandleTableInternal( HKEY hkey ) +{ + HKEY old_key; + int idx; + + TRACE("(%p)\n", hkey); + + if ((HandleToUlong(hkey) < HandleToUlong(HKEY_SPECIAL_ROOT_FIRST)) + || (HandleToUlong(hkey) > HandleToUlong(HKEY_SPECIAL_ROOT_LAST))) + return STATUS_INVALID_HANDLE; + idx = HandleToUlong(hkey) - HandleToUlong(HKEY_SPECIAL_ROOT_FIRST); + + cache_disabled[idx] = TRUE; + + old_key = InterlockedExchangePointer( (void **)&special_root_keys[idx], NULL ); + if (old_key) NtClose( old_key ); + return STATUS_SUCCESS; +} + + +/****************************************************************************** + * RegCreateKeyExW (kernelbase.@) + * + * See RegCreateKeyExA. + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegCreateKeyExW( HKEY hkey, LPCWSTR name, DWORD reserved, LPWSTR class, + DWORD options, REGSAM access, SECURITY_ATTRIBUTES *sa, + PHKEY retkey, LPDWORD dispos ) +{ + UNICODE_STRING nameW, classW; + + if (!retkey) return ERROR_BADKEY; + if (reserved) return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + RtlInitUnicodeString( &nameW, name ); + RtlInitUnicodeString( &classW, class ); + + return RtlNtStatusToDosError( create_key( retkey, hkey, nameW, options, access, &classW, dispos ) ); +} + + +/****************************************************************************** + * RegCreateKeyExA (kernelbase.@) + * + * Open a registry key, creating it if it doesn't exist. + * + * PARAMS + * hkey [I] Handle of the parent registry key + * name [I] Name of the new key to open or create + * reserved [I] Reserved, pass 0 + * class [I] The object type of the new key + * options [I] Flags controlling the key creation (REG_OPTION_* flags from "winnt.h") + * access [I] Access level desired + * sa [I] Security attributes for the key + * retkey [O] Destination for the resulting handle + * dispos [O] Receives REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY + * + * RETURNS + * Success: ERROR_SUCCESS. + * Failure: A standard Win32 error code. retkey remains untouched. + * + * FIXME + * MAXIMUM_ALLOWED in access mask not supported by server + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegCreateKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, LPSTR class, + DWORD options, REGSAM access, SECURITY_ATTRIBUTES *sa, + PHKEY retkey, LPDWORD dispos ) +{ + UNICODE_STRING classW; + ANSI_STRING nameA, classA; + NTSTATUS status; + + if (!retkey) return ERROR_BADKEY; + if (reserved) return ERROR_INVALID_PARAMETER; + if (!is_version_nt()) + { + access = MAXIMUM_ALLOWED; /* Win95 ignores the access mask */ + if (name && *name == '\\') name++; /* win9x,ME ignores one (and only one) beginning backslash */ + } + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + RtlInitAnsiString( &nameA, name ); + RtlInitAnsiString( &classA, class ); + + if (!(status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString, + &nameA, FALSE ))) + { + if (!(status = RtlAnsiStringToUnicodeString( &classW, &classA, TRUE ))) + { + status = create_key( retkey, hkey, NtCurrentTeb()->StaticUnicodeString, options, access, &classW, dispos ); + RtlFreeUnicodeString( &classW ); + } + } + return RtlNtStatusToDosError( status ); +} + + +/****************************************************************************** + * RegOpenKeyExW (kernelbase.@) + * + * See RegOpenKeyExA. + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegOpenKeyExW( HKEY hkey, LPCWSTR name, DWORD options, REGSAM access, PHKEY retkey ) +{ + UNICODE_STRING nameW; + + if (retkey && (!name || !name[0]) && + (HandleToUlong(hkey) >= HandleToUlong(HKEY_SPECIAL_ROOT_FIRST)) && + (HandleToUlong(hkey) <= HandleToUlong(HKEY_SPECIAL_ROOT_LAST))) + { + *retkey = hkey; + return ERROR_SUCCESS; + } + + /* NT+ allows beginning backslash for HKEY_CLASSES_ROOT */ + if (HandleToUlong(hkey) == HandleToUlong(HKEY_CLASSES_ROOT) && name && *name == '\\') name++; + + if (!retkey) return ERROR_INVALID_PARAMETER; + *retkey = NULL; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + RtlInitUnicodeString( &nameW, name ); + return RtlNtStatusToDosError( open_key( retkey, hkey, &nameW, options, access, FALSE ) ); +} + + +/****************************************************************************** + * RegOpenKeyExA (kernelbase.@) + * + * Open a registry key. + * + * PARAMS + * hkey [I] Handle of open key + * name [I] Name of subkey to open + * options [I] Open options (can be set to REG_OPTION_OPEN_LINK) + * access [I] Security access mask + * retkey [O] Handle to open key + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: A standard Win32 error code. retkey is set to 0. + * + * NOTES + * Unlike RegCreateKeyExA(), this function will not create the key if it + * does not exist. + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD options, REGSAM access, PHKEY retkey ) +{ + STRING nameA; + NTSTATUS status; + + if (retkey && (!name || !name[0]) && + (HandleToUlong(hkey) >= HandleToUlong(HKEY_SPECIAL_ROOT_FIRST)) && + (HandleToUlong(hkey) <= HandleToUlong(HKEY_SPECIAL_ROOT_LAST))) + { + *retkey = hkey; + return ERROR_SUCCESS; + } + + if (!is_version_nt()) access = MAXIMUM_ALLOWED; /* Win95 ignores the access mask */ + else + { + /* NT+ allows beginning backslash for HKEY_CLASSES_ROOT */ + if (HandleToUlong(hkey) == HandleToUlong(HKEY_CLASSES_ROOT) && name && *name == '\\') name++; + } + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + RtlInitAnsiString( &nameA, name ); + if (!(status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString, + &nameA, FALSE ))) + { + UNICODE_STRING nameW = NtCurrentTeb()->StaticUnicodeString; + status = open_key( retkey, hkey, &nameW, options, access, FALSE ); + } + return RtlNtStatusToDosError( status ); +} + + +/****************************************************************************** + * RegOpenCurrentUser (kernelbase.@) + * + * Get a handle to the HKEY_CURRENT_USER key for the user + * the current thread is impersonating. + * + * PARAMS + * access [I] Desired access rights to the key + * retkey [O] Handle to the opened key + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + * + * FIXME + * This function is supposed to retrieve a handle to the + * HKEY_CURRENT_USER for the user the current thread is impersonating. + * Since Wine does not currently allow threads to impersonate other users, + * this stub should work fine. + */ +LSTATUS WINAPI RegOpenCurrentUser( REGSAM access, PHKEY retkey ) +{ + void *data[20]; + TOKEN_USER *info = (TOKEN_USER *)data; + HANDLE token; + DWORD len = 0; + + /* get current user SID */ + if (OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, &token )) + { + len = sizeof(data); + if (!GetTokenInformation( token, TokenUser, info, len, &len )) len = 0; + CloseHandle( token ); + } + if (!len) + { + ImpersonateSelf(SecurityIdentification); + if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &token)) + { + len = sizeof(data); + if (!GetTokenInformation( token, TokenUser, info, len, &len )) len = 0; + CloseHandle( token ); + } + RevertToSelf(); + } + + if (len) + { + WCHAR buffer[200]; + UNICODE_STRING string = { 0, sizeof(buffer), buffer }; + + RtlConvertSidToUnicodeString( &string, info->User.Sid, FALSE ); + return RegOpenKeyExW( HKEY_USERS, string.Buffer, 0, access, retkey ); + } + + return RegOpenKeyExA( HKEY_CURRENT_USER, "", 0, access, retkey ); +} + + + +/****************************************************************************** + * RegEnumKeyExW (kernelbase.@) + * + * Enumerate subkeys of the specified open registry key. + * + * PARAMS + * hkey [I] Handle to key to enumerate + * index [I] Index of subkey to enumerate + * name [O] Buffer for subkey name + * name_len [O] Size of subkey buffer + * reserved [I] Reserved + * class [O] Buffer for class string + * class_len [O] Size of class buffer + * ft [O] Time key last written to + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: System error code. If there are no more subkeys available, the + * function returns ERROR_NO_MORE_ITEMS. + */ +LSTATUS WINAPI RegEnumKeyExW( HKEY hkey, DWORD index, LPWSTR name, LPDWORD name_len, + LPDWORD reserved, LPWSTR class, LPDWORD class_len, FILETIME *ft ) +{ + NTSTATUS status; + char buffer[256], *buf_ptr = buffer; + KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer; + DWORD total_size; + + TRACE( "(%p,%ld,%p,%p(%lu),%p,%p,%p,%p)\n", hkey, index, name, name_len, + name_len ? *name_len : 0, reserved, class, class_len, ft ); + + if (reserved) return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + status = NtEnumerateKey( hkey, index, KeyNodeInformation, + buffer, sizeof(buffer), &total_size ); + + while (status == STATUS_BUFFER_OVERFLOW) + { + /* retry with a dynamically allocated buffer */ + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( total_size ))) + return ERROR_NOT_ENOUGH_MEMORY; + info = (KEY_NODE_INFORMATION *)buf_ptr; + status = NtEnumerateKey( hkey, index, KeyNodeInformation, + buf_ptr, total_size, &total_size ); + } + + if (!status) + { + DWORD len = info->NameLength / sizeof(WCHAR); + DWORD cls_len = info->ClassLength / sizeof(WCHAR); + + if (ft) *ft = *(FILETIME *)&info->LastWriteTime; + + if (len >= *name_len || (class && class_len && (cls_len >= *class_len))) + status = STATUS_BUFFER_OVERFLOW; + else + { + *name_len = len; + memcpy( name, info->Name, info->NameLength ); + name[len] = 0; + if (class_len) + { + *class_len = cls_len; + if (class) + { + memcpy( class, buf_ptr + info->ClassOffset, info->ClassLength ); + class[cls_len] = 0; + } + } + } + } + + if (buf_ptr != buffer) heap_free( buf_ptr ); + return RtlNtStatusToDosError( status ); +} + + +/****************************************************************************** + * RegEnumKeyExA (kernelbase.@) + * + * See RegEnumKeyExW. + */ +LSTATUS WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_len, + LPDWORD reserved, LPSTR class, LPDWORD class_len, FILETIME *ft ) +{ + NTSTATUS status; + char buffer[256], *buf_ptr = buffer; + KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer; + DWORD total_size; + + TRACE( "(%p,%ld,%p,%p(%lu),%p,%p,%p,%p)\n", hkey, index, name, name_len, + name_len ? *name_len : 0, reserved, class, class_len, ft ); + + if (reserved) return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + status = NtEnumerateKey( hkey, index, KeyNodeInformation, + buffer, sizeof(buffer), &total_size ); + + while (status == STATUS_BUFFER_OVERFLOW) + { + /* retry with a dynamically allocated buffer */ + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( total_size ))) + return ERROR_NOT_ENOUGH_MEMORY; + info = (KEY_NODE_INFORMATION *)buf_ptr; + status = NtEnumerateKey( hkey, index, KeyNodeInformation, + buf_ptr, total_size, &total_size ); + } + + if (!status) + { + DWORD len, cls_len; + + RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength ); + RtlUnicodeToMultiByteSize( &cls_len, (WCHAR *)(buf_ptr + info->ClassOffset), + info->ClassLength ); + if (ft) *ft = *(FILETIME *)&info->LastWriteTime; + + if (len >= *name_len || (class && class_len && (cls_len >= *class_len))) + status = STATUS_BUFFER_OVERFLOW; + else + { + *name_len = len; + RtlUnicodeToMultiByteN( name, len, NULL, info->Name, info->NameLength ); + name[len] = 0; + if (class_len) + { + *class_len = cls_len; + if (class) + { + RtlUnicodeToMultiByteN( class, cls_len, NULL, + (WCHAR *)(buf_ptr + info->ClassOffset), + info->ClassLength ); + class[cls_len] = 0; + } + } + } + } + + if (buf_ptr != buffer) heap_free( buf_ptr ); + return RtlNtStatusToDosError( status ); +} + + +/****************************************************************************** + * RegQueryInfoKeyW (kernelbase.@) + * + * Retrieves information about the specified registry key. + * + * PARAMS + * hkey [I] Handle to key to query + * class [O] Buffer for class string + * class_len [O] Size of class string buffer + * reserved [I] Reserved + * subkeys [O] Buffer for number of subkeys + * max_subkey [O] Buffer for longest subkey name length + * max_class [O] Buffer for longest class string length + * values [O] Buffer for number of value entries + * max_value [O] Buffer for longest value name length + * max_data [O] Buffer for longest value data length + * security [O] Buffer for security descriptor length + * modif [O] Modification time + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: system error code. + * + * NOTES + * - win95 allows class to be valid and class_len to be NULL + * - winnt returns ERROR_INVALID_PARAMETER if class is valid and class_len is NULL + * - both allow class to be NULL and class_len to be NULL + * (it's hard to test validity, so test !NULL instead) + */ +LSTATUS WINAPI RegQueryInfoKeyW( HKEY hkey, LPWSTR class, LPDWORD class_len, LPDWORD reserved, + LPDWORD subkeys, LPDWORD max_subkey, LPDWORD max_class, + LPDWORD values, LPDWORD max_value, LPDWORD max_data, + LPDWORD security, FILETIME *modif ) +{ + NTSTATUS status; + char buffer[256], *buf_ptr = buffer; + KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer; + DWORD total_size; + + TRACE( "(%p,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey, class, class_len ? *class_len : 0, + reserved, subkeys, max_subkey, values, max_value, max_data, security, modif ); + + if (class && !class_len && is_version_nt()) return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size ); + if (status && status != STATUS_BUFFER_OVERFLOW) goto done; + + if (class && class_len && *class_len) + { + /* retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( total_size ))) + return ERROR_NOT_ENOUGH_MEMORY; + info = (KEY_FULL_INFORMATION *)buf_ptr; + status = NtQueryKey( hkey, KeyFullInformation, buf_ptr, total_size, &total_size ); + } + + if (status) goto done; + + if (class_len && (info->ClassLength/sizeof(WCHAR) + 1 > *class_len)) + { + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + memcpy( class, buf_ptr + info->ClassOffset, info->ClassLength ); + class[info->ClassLength/sizeof(WCHAR)] = 0; + } + } + else status = STATUS_SUCCESS; + + if (class_len) *class_len = info->ClassLength / sizeof(WCHAR); + if (subkeys) *subkeys = info->SubKeys; + if (max_subkey) *max_subkey = info->MaxNameLen / sizeof(WCHAR); + if (max_class) *max_class = info->MaxClassLen / sizeof(WCHAR); + if (values) *values = info->Values; + if (max_value) *max_value = info->MaxValueNameLen / sizeof(WCHAR); + if (max_data) *max_data = info->MaxValueDataLen; + if (modif) *modif = *(FILETIME *)&info->LastWriteTime; + + if (security) + { + FIXME( "security argument not supported.\n"); + *security = 0; + } + + done: + if (buf_ptr != buffer) heap_free( buf_ptr ); + return RtlNtStatusToDosError( status ); +} + + +/****************************************************************************** + * RegQueryInfoKeyA (kernelbase.@) + * + * Retrieves information about a registry key. + * + * PARAMS + * hKey [I] Handle to an open key. + * lpClass [O] Class string of the key. + * lpcClass [I/O] size of lpClass. + * lpReserved [I] Reserved; must be NULL. + * lpcSubKeys [O] Number of subkeys contained by the key. + * lpcMaxSubKeyLen [O] Size of the key's subkey with the longest name. + * lpcMaxClassLen [O] Size of the longest string specifying a subkey + * class in TCHARS. + * lpcValues [O] Number of values associated with the key. + * lpcMaxValueNameLen [O] Size of the key's longest value name in TCHARS. + * lpcMaxValueLen [O] Longest data component among the key's values + * lpcbSecurityDescriptor [O] Size of the key's security descriptor. + * lpftLastWriteTime [O] FILETIME structure that is the last write time. + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + */ +LSTATUS WINAPI RegQueryInfoKeyA( HKEY hkey, LPSTR class, LPDWORD class_len, LPDWORD reserved, + LPDWORD subkeys, LPDWORD max_subkey, LPDWORD max_class, + LPDWORD values, LPDWORD max_value, LPDWORD max_data, + LPDWORD security, FILETIME *modif ) +{ + NTSTATUS status; + char buffer[256], *buf_ptr = buffer; + KEY_FULL_INFORMATION *info = (KEY_FULL_INFORMATION *)buffer; + DWORD total_size; + + TRACE( "(%p,%p,%ld,%p,%p,%p,%p,%p,%p,%p,%p)\n", hkey, class, class_len ? *class_len : 0, + reserved, subkeys, max_subkey, values, max_value, max_data, security, modif ); + + if (class && !class_len && is_version_nt()) return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size ); + if (status && status != STATUS_BUFFER_OVERFLOW) goto done; + + if (class || class_len) + { + /* retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( total_size ))) + return ERROR_NOT_ENOUGH_MEMORY; + info = (KEY_FULL_INFORMATION *)buf_ptr; + status = NtQueryKey( hkey, KeyFullInformation, buf_ptr, total_size, &total_size ); + } + + if (status) goto done; + + if (class && class_len && *class_len) + { + DWORD len = *class_len; + RtlUnicodeToMultiByteN( class, len, class_len, + (WCHAR *)(buf_ptr + info->ClassOffset), info->ClassLength ); + if (*class_len == len) + { + status = STATUS_BUFFER_OVERFLOW; + *class_len -= 1; + } + class[*class_len] = 0; + } + else if (class_len) + RtlUnicodeToMultiByteSize( class_len, + (WCHAR *)(buf_ptr + info->ClassOffset), info->ClassLength ); + } + else status = STATUS_SUCCESS; + + if (subkeys) *subkeys = info->SubKeys; + if (max_subkey) *max_subkey = info->MaxNameLen / sizeof(WCHAR); + if (max_class) *max_class = info->MaxClassLen / sizeof(WCHAR); + if (values) *values = info->Values; + if (max_value) *max_value = info->MaxValueNameLen / sizeof(WCHAR); + if (max_data) *max_data = info->MaxValueDataLen; + if (modif) *modif = *(FILETIME *)&info->LastWriteTime; + + if (security) + { + FIXME( "security argument not supported.\n"); + *security = 0; + } + + done: + if (buf_ptr != buffer) heap_free( buf_ptr ); + return RtlNtStatusToDosError( status ); +} + +/****************************************************************************** + * RegCloseKey (kernelbase.@) + * + * Close an open registry key. + * + * PARAMS + * hkey [I] Handle of key to close + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: Error code + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegCloseKey( HKEY hkey ) +{ + if (!hkey) return ERROR_INVALID_HANDLE; + if (hkey >= (HKEY)0x80000000) return ERROR_SUCCESS; + return RtlNtStatusToDosError( NtClose( hkey ) ); +} + + +/****************************************************************************** + * RegDeleteKeyExW (kernelbase.@) + */ +LSTATUS WINAPI RegDeleteKeyExW( HKEY hkey, LPCWSTR name, REGSAM access, DWORD reserved ) +{ + DWORD ret; + HKEY tmp; + + if (!name) return ERROR_INVALID_PARAMETER; + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + access &= KEY_WOW64_64KEY | KEY_WOW64_32KEY; + if (!(ret = RegOpenKeyExW( hkey, name, 0, access | DELETE, &tmp ))) + { + ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) ); + RegCloseKey( tmp ); + } + TRACE("%s ret=%08lx\n", debugstr_w(name), ret); + return ret; +} + + +/****************************************************************************** + * RegDeleteKeyExA (kernelbase.@) + */ +LSTATUS WINAPI RegDeleteKeyExA( HKEY hkey, LPCSTR name, REGSAM access, DWORD reserved ) +{ + DWORD ret; + HKEY tmp; + + if (!name) return ERROR_INVALID_PARAMETER; + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + access &= KEY_WOW64_64KEY | KEY_WOW64_32KEY; + if (!(ret = RegOpenKeyExA( hkey, name, 0, access | DELETE, &tmp ))) + { + if (!is_version_nt()) /* win95 does recursive key deletes */ + { + CHAR sub[MAX_PATH]; + DWORD len = sizeof(sub); + while(!RegEnumKeyExA(tmp, 0, sub, &len, NULL, NULL, NULL, NULL)) + { + if(RegDeleteKeyExA(tmp, sub, access, reserved)) /* recurse */ + break; + } + } + ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) ); + RegCloseKey( tmp ); + } + TRACE("%s ret=%08lx\n", debugstr_a(name), ret); + return ret; +} + +/****************************************************************************** + * RegSetValueExW (kernelbase.@) + * + * Set the data and contents of a registry value. + * + * PARAMS + * hkey [I] Handle of key to set value for + * name [I] Name of value to set + * reserved [I] Reserved, must be zero + * type [I] Type of the value being set + * data [I] The new contents of the value to set + * count [I] Size of data + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: Error code + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegSetValueExW( HKEY hkey, LPCWSTR name, DWORD reserved, + DWORD type, const BYTE *data, DWORD count ) +{ + UNICODE_STRING nameW; + + /* no need for version check, not implemented on win9x anyway */ + + if ((data && ((ULONG_PTR)data >> 16) == 0) || (!data && count)) return ERROR_NOACCESS; + + if (count && is_string(type)) + { + LPCWSTR str = (LPCWSTR)data; + /* if user forgot to count terminating null, add it (yes NT does this) */ + if (str[count / sizeof(WCHAR) - 1] && !str[count / sizeof(WCHAR)]) + count += sizeof(WCHAR); + } + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + RtlInitUnicodeString( &nameW, name ); + return RtlNtStatusToDosError( NtSetValueKey( hkey, &nameW, 0, type, data, count ) ); +} + + +/****************************************************************************** + * RegSetValueExA (kernelbase.@) + * + * See RegSetValueExW. + * + * NOTES + * win95 does not care about count for REG_SZ and finds out the len by itself (js) + * NT does definitely care (aj) + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type, + const BYTE *data, DWORD count ) +{ + ANSI_STRING nameA; + UNICODE_STRING nameW; + WCHAR *dataW = NULL; + NTSTATUS status; + + if (!is_version_nt()) /* win95 */ + { + if (type == REG_SZ) + { + if (!data) return ERROR_INVALID_PARAMETER; + count = strlen((const char *)data) + 1; + } + } + else if (count && is_string(type)) + { + /* if user forgot to count terminating null, add it (yes NT does this) */ + if (data[count-1] && !data[count]) count++; + } + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + if (is_string( type )) /* need to convert to Unicode */ + { + DWORD lenW; + RtlMultiByteToUnicodeSize( &lenW, (const char *)data, count ); + if (!(dataW = heap_alloc( lenW ))) return ERROR_OUTOFMEMORY; + RtlMultiByteToUnicodeN( dataW, lenW, NULL, (const char *)data, count ); + count = lenW; + data = (BYTE *)dataW; + } + + RtlInitAnsiString( &nameA, name ); + if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE ))) + { + status = NtSetValueKey( hkey, &nameW, 0, type, data, count ); + RtlFreeUnicodeString( &nameW ); + } + heap_free( dataW ); + return RtlNtStatusToDosError( status ); +} + + +/****************************************************************************** + * RegSetKeyValueW (kernelbase.@) + */ +LONG WINAPI RegSetKeyValueW( HKEY hkey, LPCWSTR subkey, LPCWSTR name, DWORD type, const void *data, DWORD len ) +{ + HKEY hsubkey = NULL; + DWORD ret; + + TRACE("(%p,%s,%s,%ld,%p,%ld)\n", hkey, debugstr_w(subkey), debugstr_w(name), type, data, len ); + + if (subkey && subkey[0]) /* need to create the subkey */ + { + if ((ret = RegCreateKeyExW( hkey, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, NULL, &hsubkey, NULL )) != ERROR_SUCCESS) return ret; + hkey = hsubkey; + } + + ret = RegSetValueExW( hkey, name, 0, type, (const BYTE*)data, len ); + if (hsubkey) RegCloseKey( hsubkey ); + return ret; +} + +/****************************************************************************** + * RegSetKeyValueA (kernelbase.@) + */ +LONG WINAPI RegSetKeyValueA( HKEY hkey, LPCSTR subkey, LPCSTR name, DWORD type, const void *data, DWORD len ) +{ + HKEY hsubkey = NULL; + DWORD ret; + + TRACE("(%p,%s,%s,%ld,%p,%ld)\n", hkey, debugstr_a(subkey), debugstr_a(name), type, data, len ); + + if (subkey && subkey[0]) /* need to create the subkey */ + { + if ((ret = RegCreateKeyExA( hkey, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, NULL, &hsubkey, NULL )) != ERROR_SUCCESS) return ret; + hkey = hsubkey; + } + + ret = RegSetValueExA( hkey, name, 0, type, (const BYTE*)data, len ); + if (hsubkey) RegCloseKey( hsubkey ); + return ret; +} + +/* FIXME: we should read data from system32/perf009c.dat (or perf###c depending + * on locale) instead */ +static DWORD query_perf_names( DWORD *type, void *data, DWORD *ret_size, BOOL unicode ) +{ + static const WCHAR names[] = L"1\0" "1847\0" "1846\0End Marker\0"; + DWORD size = *ret_size; + + if (type) *type = REG_MULTI_SZ; + *ret_size = sizeof(names); + if (!unicode) *ret_size /= sizeof(WCHAR); + + if (!data) return ERROR_SUCCESS; + if (size < *ret_size) return ERROR_MORE_DATA; + + if (unicode) + memcpy( data, names, sizeof(names) ); + else + RtlUnicodeToMultiByteN( data, size, NULL, names, sizeof(names) ); + return ERROR_SUCCESS; +} + +/* FIXME: we should read data from system32/perf009h.dat (or perf###h depending + * on locale) instead */ +static DWORD query_perf_help( DWORD *type, void *data, DWORD *ret_size, BOOL unicode ) +{ + static const WCHAR names[] = L"1847\0End Marker\0"; + DWORD size = *ret_size; + + if (type) *type = REG_MULTI_SZ; + *ret_size = sizeof(names); + if (!unicode) *ret_size /= sizeof(WCHAR); + + if (!data) return ERROR_SUCCESS; + if (size < *ret_size) return ERROR_MORE_DATA; + + if (unicode) + memcpy( data, names, sizeof(names) ); + else + RtlUnicodeToMultiByteN( data, size, NULL, names, sizeof(names) ); + return ERROR_SUCCESS; +} + +struct perf_provider +{ + HMODULE perflib; + WCHAR linkage[MAX_PATH]; + WCHAR objects[MAX_PATH]; + PM_OPEN_PROC *pOpen; + PM_CLOSE_PROC *pClose; + PM_COLLECT_PROC *pCollect; +}; + +static void *get_provider_entry(HKEY perf, HMODULE perflib, const char *name) +{ + char buf[MAX_PATH]; + DWORD err, type, len; + + len = sizeof(buf) - 1; + err = RegQueryValueExA(perf, name, NULL, &type, (BYTE *)buf, &len); + if (err != ERROR_SUCCESS || type != REG_SZ) + return NULL; + + buf[len] = 0; + TRACE("Loading function pointer for %s: %s\n", name, debugstr_a(buf)); + + return GetProcAddress(perflib, buf); +} + +static BOOL load_provider(HKEY root, const WCHAR *name, struct perf_provider *provider) +{ + WCHAR buf[MAX_PATH], buf2[MAX_PATH]; + DWORD err, type, len; + HKEY service, perf; + + err = RegOpenKeyExW(root, name, 0, KEY_READ, &service); + if (err != ERROR_SUCCESS) + return FALSE; + + provider->linkage[0] = 0; + err = RegOpenKeyExW(service, L"Linkage", 0, KEY_READ, &perf); + if (err == ERROR_SUCCESS) + { + len = sizeof(buf) - sizeof(WCHAR); + err = RegQueryValueExW(perf, L"Export", NULL, &type, (BYTE *)buf, &len); + if (err == ERROR_SUCCESS && (type == REG_SZ || type == REG_MULTI_SZ)) + { + memcpy(provider->linkage, buf, len); + provider->linkage[len / sizeof(WCHAR)] = 0; + TRACE("Export: %s\n", debugstr_w(provider->linkage)); + } + RegCloseKey(perf); + } + + err = RegOpenKeyExW(service, L"Performance", 0, KEY_READ, &perf); + RegCloseKey(service); + if (err != ERROR_SUCCESS) + return FALSE; + + provider->objects[0] = 0; + len = sizeof(buf) - sizeof(WCHAR); + err = RegQueryValueExW(perf, L"Object List", NULL, &type, (BYTE *)buf, &len); + if (err == ERROR_SUCCESS && (type == REG_SZ || type == REG_MULTI_SZ)) + { + memcpy(provider->objects, buf, len); + provider->objects[len / sizeof(WCHAR)] = 0; + TRACE("Object List: %s\n", debugstr_w(provider->objects)); + } + + len = sizeof(buf) - sizeof(WCHAR); + err = RegQueryValueExW(perf, L"Library", NULL, &type, (BYTE *)buf, &len); + if (err != ERROR_SUCCESS || !(type == REG_SZ || type == REG_EXPAND_SZ)) + goto error; + + buf[len / sizeof(WCHAR)] = 0; + if (type == REG_EXPAND_SZ) + { + len = ExpandEnvironmentStringsW(buf, buf2, MAX_PATH); + if (!len || len > MAX_PATH) goto error; + lstrcpyW(buf, buf2); + } + + if (!(provider->perflib = LoadLibraryW(buf))) + { + WARN("Failed to load %s\n", debugstr_w(buf)); + goto error; + } + + GetModuleFileNameW(provider->perflib, buf, MAX_PATH); + TRACE("Loaded provider %s\n", wine_dbgstr_w(buf)); + + provider->pOpen = get_provider_entry(perf, provider->perflib, "Open"); + provider->pClose = get_provider_entry(perf, provider->perflib, "Close"); + provider->pCollect = get_provider_entry(perf, provider->perflib, "Collect"); + if (provider->pOpen && provider->pClose && provider->pCollect) + { + RegCloseKey(perf); + return TRUE; + } + + TRACE("Provider is missing required exports\n"); + FreeLibrary(provider->perflib); + +error: + RegCloseKey(perf); + return FALSE; +} + +static DWORD collect_data(struct perf_provider *provider, const WCHAR *query, void **data, DWORD *size, DWORD *obj_count) +{ + WCHAR *linkage = provider->linkage[0] ? provider->linkage : NULL; + DWORD err; + + if (!query || !query[0]) + query = L"Global"; + + err = provider->pOpen(linkage); + if (err != ERROR_SUCCESS) + { + TRACE("Open(%s) error %lu (%#lx)\n", debugstr_w(linkage), err, err); + return err; + } + + *obj_count = 0; + err = provider->pCollect((WCHAR *)query, data, size, obj_count); + if (err != ERROR_SUCCESS) + { + TRACE("Collect error %lu (%#lx)\n", err, err); + *obj_count = 0; + } + + provider->pClose(); + return err; +} + +#define MAX_SERVICE_NAME 260 + +static DWORD query_perf_data( const WCHAR *query, DWORD *type, void *data, DWORD *ret_size, BOOL unicode ) +{ + DWORD err, i, data_size; + HKEY root; + PERF_DATA_BLOCK *pdb; + + if (!ret_size) + return ERROR_INVALID_PARAMETER; + + if (!wcsnicmp( query, L"counter", 7 )) + return query_perf_names( type, data, ret_size, unicode ); + if (!wcsnicmp( query, L"help", 4 )) + return query_perf_help( type, data, ret_size, unicode ); + + data_size = *ret_size; + *ret_size = 0; + + if (type) + *type = REG_BINARY; + + if (!data || data_size < sizeof(*pdb)) + return ERROR_MORE_DATA; + + pdb = data; + + pdb->Signature[0] = 'P'; + pdb->Signature[1] = 'E'; + pdb->Signature[2] = 'R'; + pdb->Signature[3] = 'F'; +#ifdef WORDS_BIGENDIAN + pdb->LittleEndian = FALSE; +#else + pdb->LittleEndian = TRUE; +#endif + pdb->Version = PERF_DATA_VERSION; + pdb->Revision = PERF_DATA_REVISION; + pdb->TotalByteLength = 0; + pdb->HeaderLength = sizeof(*pdb); + pdb->NumObjectTypes = 0; + pdb->DefaultObject = 0; + NtQueryPerformanceCounter( &pdb->PerfTime, &pdb->PerfFreq ); + + data = pdb + 1; + pdb->SystemNameOffset = sizeof(*pdb); + pdb->SystemNameLength = (data_size - sizeof(*pdb)) / sizeof(WCHAR); + if (!GetComputerNameExW(ComputerNameNetBIOS, data, &pdb->SystemNameLength)) + return ERROR_MORE_DATA; + + pdb->SystemNameLength++; + pdb->SystemNameLength *= sizeof(WCHAR); + + pdb->HeaderLength += pdb->SystemNameLength; + + /* align to 8 bytes */ + if (pdb->SystemNameLength & 7) + pdb->HeaderLength += 8 - (pdb->SystemNameLength & 7); + + if (data_size < pdb->HeaderLength) + return ERROR_MORE_DATA; + + pdb->TotalByteLength = pdb->HeaderLength; + + data_size -= pdb->HeaderLength; + data = (char *)data + pdb->HeaderLength; + + err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services", 0, KEY_READ, &root); + if (err != ERROR_SUCCESS) + return err; + + i = 0; + for (;;) + { + DWORD collected_size = data_size, obj_count = 0; + struct perf_provider provider; + WCHAR name[MAX_SERVICE_NAME]; + DWORD len = ARRAY_SIZE( name ); + void *collected_data = data; + + err = RegEnumKeyExW(root, i++, name, &len, NULL, NULL, NULL, NULL); + if (err == ERROR_NO_MORE_ITEMS) + { + err = ERROR_SUCCESS; + break; + } + + if (err != ERROR_SUCCESS) + continue; + + if (!load_provider(root, name, &provider)) + continue; + + err = collect_data(&provider, query, &collected_data, &collected_size, &obj_count); + FreeLibrary(provider.perflib); + + if (err == ERROR_MORE_DATA) + break; + + if (err == ERROR_SUCCESS) + { + PERF_OBJECT_TYPE *obj = (PERF_OBJECT_TYPE *)data; + + TRACE("Collect: obj->TotalByteLength %lu, collected_size %lu\n", + obj->TotalByteLength, collected_size); + + data_size -= collected_size; + data = collected_data; + + pdb->TotalByteLength += collected_size; + pdb->NumObjectTypes += obj_count; + } + } + + RegCloseKey(root); + + if (err == ERROR_SUCCESS) + { + *ret_size = pdb->TotalByteLength; + + GetSystemTime(&pdb->SystemTime); + GetSystemTimeAsFileTime((FILETIME *)&pdb->PerfTime100nSec); + } + + return err; +} + +/****************************************************************************** + * RegQueryValueExW (kernelbase.@) + * + * See RegQueryValueExA. + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegQueryValueExW( HKEY hkey, LPCWSTR name, LPDWORD reserved, LPDWORD type, + LPBYTE data, LPDWORD count ) +{ + NTSTATUS status; + UNICODE_STRING name_str; + DWORD total_size; + char buffer[256], *buf_ptr = buffer; + KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data ); + + TRACE("(%p,%s,%p,%p,%p,%p=%ld)\n", + hkey, debugstr_w(name), reserved, type, data, count, + (count && data) ? *count : 0 ); + + if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER; + + if (is_perf_key( hkey )) + return query_perf_data( name, type, data, count, TRUE ); + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + RtlInitUnicodeString( &name_str, name ); + + if (data) total_size = min( sizeof(buffer), *count + info_size ); + else + { + total_size = info_size; + if (count) *count = 0; + } + + status = NtQueryValueKey( hkey, &name_str, KeyValuePartialInformation, + buffer, total_size, &total_size ); + if (status && status != STATUS_BUFFER_OVERFLOW) goto done; + + if (data) + { + /* retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW && total_size - info_size <= *count) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( total_size ))) + return ERROR_NOT_ENOUGH_MEMORY; + info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr; + status = NtQueryValueKey( hkey, &name_str, KeyValuePartialInformation, + buf_ptr, total_size, &total_size ); + } + + if (!status) + { + memcpy( data, buf_ptr + info_size, total_size - info_size ); + /* if the type is REG_SZ and data is not 0-terminated + * and there is enough space in the buffer NT appends a \0 */ + if (total_size - info_size <= *count-sizeof(WCHAR) && is_string(info->Type)) + { + WCHAR *ptr = (WCHAR *)(data + total_size - info_size); + if (ptr > (WCHAR *)data && ptr[-1]) *ptr = 0; + } + } + else if (status != STATUS_BUFFER_OVERFLOW) goto done; + } + else status = STATUS_SUCCESS; + + if (type) *type = info->Type; + if (count) *count = total_size - info_size; + + done: + if (buf_ptr != buffer) heap_free( buf_ptr ); + return RtlNtStatusToDosError(status); +} + + +/****************************************************************************** + * RegQueryValueExA (kernelbase.@) + * + * Get the type and contents of a specified value under with a key. + * + * PARAMS + * hkey [I] Handle of the key to query + * name [I] Name of value under hkey to query + * reserved [I] Reserved - must be NULL + * type [O] Destination for the value type, or NULL if not required + * data [O] Destination for the values contents, or NULL if not required + * count [I/O] Size of data, updated with the number of bytes returned + * + * RETURNS + * Success: ERROR_SUCCESS. *count is updated with the number of bytes copied to data. + * Failure: ERROR_INVALID_HANDLE, if hkey is invalid. + * ERROR_INVALID_PARAMETER, if any other parameter is invalid. + * ERROR_MORE_DATA, if on input *count is too small to hold the contents. + * + * NOTES + * MSDN states that if data is too small it is partially filled. In reality + * it remains untouched. + */ +LSTATUS WINAPI DECLSPEC_HOTPATCH RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, + LPDWORD type, LPBYTE data, LPDWORD count ) +{ + NTSTATUS status; + ANSI_STRING nameA; + UNICODE_STRING nameW; + DWORD total_size, datalen = 0; + char buffer[256], *buf_ptr = buffer; + KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data ); + + TRACE("(%p,%s,%p,%p,%p,%p=%ld)\n", + hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 ); + + if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) + return ERROR_INVALID_HANDLE; + + if (count) datalen = *count; + if (!data && count) *count = 0; + + /* this matches Win9x behaviour - NT sets *type to a random value */ + if (type) *type = REG_NONE; + + RtlInitAnsiString( &nameA, name ); + if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE ))) + return RtlNtStatusToDosError(status); + + if (is_perf_key( hkey )) + { + DWORD ret = query_perf_data( nameW.Buffer, type, data, count, FALSE ); + RtlFreeUnicodeString( &nameW ); + return ret; + } + + status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, + buffer, sizeof(buffer), &total_size ); + if (status && status != STATUS_BUFFER_OVERFLOW) goto done; + + /* we need to fetch the contents for a string type even if not requested, + * because we need to compute the length of the ANSI string. */ + if (data || is_string(info->Type)) + { + /* retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( total_size ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr; + status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, + buf_ptr, total_size, &total_size ); + } + + if (status) goto done; + + if (is_string(info->Type)) + { + DWORD len; + + RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info_size), + total_size - info_size ); + if (data && len) + { + if (len > datalen) status = STATUS_BUFFER_OVERFLOW; + else + { + RtlUnicodeToMultiByteN( (char*)data, len, NULL, (WCHAR *)(buf_ptr + info_size), + total_size - info_size ); + /* if the type is REG_SZ and data is not 0-terminated + * and there is enough space in the buffer NT appends a \0 */ + if (len < datalen && data[len-1]) data[len] = 0; + } + } + total_size = len + info_size; + } + else if (data) + { + if (total_size - info_size > datalen) status = STATUS_BUFFER_OVERFLOW; + else memcpy( data, buf_ptr + info_size, total_size - info_size ); + } + } + else status = STATUS_SUCCESS; + + if (type) *type = info->Type; + if (count) *count = total_size - info_size; + + done: + if (buf_ptr != buffer) heap_free( buf_ptr ); + RtlFreeUnicodeString( &nameW ); + return RtlNtStatusToDosError(status); +} + + +/****************************************************************************** + * apply_restrictions [internal] + * + * Helper function for RegGetValueA/W. + */ +static void apply_restrictions( DWORD dwFlags, DWORD dwType, DWORD cbData, PLONG ret ) +{ + /* Check if the type is restricted by the passed flags */ + if (*ret == ERROR_SUCCESS || *ret == ERROR_MORE_DATA) + { + DWORD dwMask = 0; + + switch (dwType) + { + case REG_NONE: dwMask = RRF_RT_REG_NONE; break; + case REG_SZ: dwMask = RRF_RT_REG_SZ; break; + case REG_EXPAND_SZ: dwMask = RRF_RT_REG_EXPAND_SZ; break; + case REG_MULTI_SZ: dwMask = RRF_RT_REG_MULTI_SZ; break; + case REG_BINARY: dwMask = RRF_RT_REG_BINARY; break; + case REG_DWORD: dwMask = RRF_RT_REG_DWORD; break; + case REG_QWORD: dwMask = RRF_RT_REG_QWORD; break; + } + + if (dwFlags & dwMask) + { + /* Type is not restricted, check for size mismatch */ + if (dwType == REG_BINARY) + { + DWORD cbExpect = 0; + + if ((dwFlags & RRF_RT_ANY) == RRF_RT_DWORD) + cbExpect = 4; + else if ((dwFlags & RRF_RT_ANY) == RRF_RT_QWORD) + cbExpect = 8; + + if (cbExpect && cbData != cbExpect) + *ret = ERROR_DATATYPE_MISMATCH; + } + } + else *ret = ERROR_UNSUPPORTED_TYPE; + } +} + + +/****************************************************************************** + * RegGetValueW (kernelbase.@) + * + * Retrieves the type and data for a value name associated with a key, + * optionally expanding its content and restricting its type. + * + * PARAMS + * hKey [I] Handle to an open key. + * pszSubKey [I] Name of the subkey of hKey. + * pszValue [I] Name of value under hKey/szSubKey to query. + * dwFlags [I] Flags restricting the value type to retrieve. + * pdwType [O] Destination for the values type, may be NULL. + * pvData [O] Destination for the values content, may be NULL. + * pcbData [I/O] Size of pvData, updated with the size in bytes required to + * retrieve the whole content, including the trailing '\0' + * for strings. + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + * + * NOTES + * - Unless RRF_NOEXPAND is specified, REG_EXPAND_SZ values are automatically + * expanded and pdwType is set to REG_SZ instead. + * - Restrictions are applied after expanding, using RRF_RT_REG_EXPAND_SZ + * without RRF_NOEXPAND is thus not allowed. + * An exception is the case where RRF_RT_ANY is specified, because then + * RRF_NOEXPAND is allowed. + */ +LSTATUS WINAPI RegGetValueW( HKEY hKey, LPCWSTR pszSubKey, LPCWSTR pszValue, + DWORD dwFlags, LPDWORD pdwType, PVOID pvData, + LPDWORD pcbData ) +{ + DWORD dwType, cbData = (pvData && pcbData) ? *pcbData : 0; + PVOID pvBuf = NULL; + LONG ret; + + TRACE("(%p,%s,%s,%ld,%p,%p,%p=%ld)\n", + hKey, debugstr_w(pszSubKey), debugstr_w(pszValue), dwFlags, pdwType, + pvData, pcbData, cbData); + + if (pvData && !pcbData) + return ERROR_INVALID_PARAMETER; + + if ((dwFlags & RRF_RT_REG_EXPAND_SZ) && !(dwFlags & RRF_NOEXPAND) && + ((dwFlags & RRF_RT_ANY) != RRF_RT_ANY)) + return ERROR_INVALID_PARAMETER; + + if ((dwFlags & RRF_WOW64_MASK) == RRF_WOW64_MASK) + return ERROR_INVALID_PARAMETER; + + if (pszSubKey && pszSubKey[0]) + { + REGSAM samDesired = KEY_QUERY_VALUE; + + if (dwFlags & RRF_WOW64_MASK) + samDesired |= (dwFlags & RRF_SUBKEY_WOW6432KEY) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY; + + ret = RegOpenKeyExW(hKey, pszSubKey, 0, samDesired, &hKey); + if (ret != ERROR_SUCCESS) return ret; + } + + ret = RegQueryValueExW(hKey, pszValue, NULL, &dwType, pvData, &cbData); + + /* If the value is a string, we need to read in the whole value to be able + * to know exactly how many bytes are needed after expanding the string and + * ensuring that it is null-terminated. */ + if (is_string(dwType) && + (ret == ERROR_MORE_DATA || + (ret == ERROR_SUCCESS && dwType == REG_EXPAND_SZ && !(dwFlags & RRF_NOEXPAND)) || + (ret == ERROR_SUCCESS && (cbData < sizeof(WCHAR) || (pvData && *((WCHAR *)pvData + cbData / sizeof(WCHAR) - 1)))))) + { + do { + heap_free(pvBuf); + + pvBuf = heap_alloc(cbData + sizeof(WCHAR)); + if (!pvBuf) + { + ret = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + if (ret == ERROR_MORE_DATA || !pvData) + ret = RegQueryValueExW(hKey, pszValue, NULL, + &dwType, pvBuf, &cbData); + else + { + /* Even if cbData was large enough we have to copy the + * string since ExpandEnvironmentStrings can't handle + * overlapping buffers. */ + CopyMemory(pvBuf, pvData, cbData); + } + } while (ret == ERROR_MORE_DATA); + + if (ret == ERROR_SUCCESS) + { + /* Ensure null termination */ + if (cbData < sizeof(WCHAR) || *((WCHAR *)pvBuf + cbData / sizeof(WCHAR) - 1)) + { + *((WCHAR *)pvBuf + cbData / sizeof(WCHAR)) = 0; + cbData += sizeof(WCHAR); + } + + /* Recheck dwType in case it changed since the first call */ + if (dwType == REG_EXPAND_SZ && !(dwFlags & RRF_NOEXPAND)) + { + cbData = ExpandEnvironmentStringsW(pvBuf, pvData, + pcbData ? *pcbData : 0) * sizeof(WCHAR); + dwType = REG_SZ; + if (pvData && cbData > *pcbData) + ret = ERROR_MORE_DATA; + } + else if (pvData) + { + if (cbData > *pcbData) + ret = ERROR_MORE_DATA; + else + CopyMemory(pvData, pvBuf, cbData); + } + } + + heap_free(pvBuf); + } + + if (pszSubKey && pszSubKey[0]) + RegCloseKey(hKey); + + apply_restrictions(dwFlags, dwType, cbData, &ret); + + if (pvData && ret != ERROR_SUCCESS && (dwFlags & RRF_ZEROONFAILURE)) + ZeroMemory(pvData, *pcbData); + + if (pdwType) *pdwType = dwType; + if (pcbData) *pcbData = cbData; + + return ret; +} + + +/****************************************************************************** + * RegGetValueA (kernelbase.@) + * + * See RegGetValueW. + */ +LSTATUS WINAPI RegGetValueA( HKEY hKey, LPCSTR pszSubKey, LPCSTR pszValue, + DWORD dwFlags, LPDWORD pdwType, PVOID pvData, + LPDWORD pcbData ) +{ + DWORD dwType, cbData = (pvData && pcbData) ? *pcbData : 0; + PVOID pvBuf = NULL; + LONG ret; + + TRACE("(%p,%s,%s,%ld,%p,%p,%p=%ld)\n", + hKey, debugstr_a(pszSubKey), debugstr_a(pszValue), dwFlags, + pdwType, pvData, pcbData, cbData); + + if (pvData && !pcbData) + return ERROR_INVALID_PARAMETER; + + if ((dwFlags & RRF_RT_REG_EXPAND_SZ) && !(dwFlags & RRF_NOEXPAND) && + ((dwFlags & RRF_RT_ANY) != RRF_RT_ANY)) + return ERROR_INVALID_PARAMETER; + + if ((dwFlags & RRF_WOW64_MASK) == RRF_WOW64_MASK) + return ERROR_INVALID_PARAMETER; + + if (pszSubKey && pszSubKey[0]) + { + REGSAM samDesired = KEY_QUERY_VALUE; + + if (dwFlags & RRF_WOW64_MASK) + samDesired |= (dwFlags & RRF_SUBKEY_WOW6432KEY) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY; + + ret = RegOpenKeyExA(hKey, pszSubKey, 0, samDesired, &hKey); + if (ret != ERROR_SUCCESS) return ret; + } + + ret = RegQueryValueExA(hKey, pszValue, NULL, &dwType, pvData, &cbData); + + /* If the value is a string, we need to read in the whole value to be able + * to know exactly how many bytes are needed after expanding the string and + * ensuring that it is null-terminated. */ + if (is_string(dwType) && + (ret == ERROR_MORE_DATA || + (ret == ERROR_SUCCESS && dwType == REG_EXPAND_SZ && !(dwFlags & RRF_NOEXPAND)) || + (ret == ERROR_SUCCESS && (!cbData || (pvData && *((char *)pvData + cbData - 1)))))) + { + do { + heap_free(pvBuf); + + pvBuf = heap_alloc(cbData + 1); + if (!pvBuf) + { + ret = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + if (ret == ERROR_MORE_DATA || !pvData) + ret = RegQueryValueExA(hKey, pszValue, NULL, + &dwType, pvBuf, &cbData); + else + { + /* Even if cbData was large enough we have to copy the + * string since ExpandEnvironmentStrings can't handle + * overlapping buffers. */ + CopyMemory(pvBuf, pvData, cbData); + } + } while (ret == ERROR_MORE_DATA); + + if (ret == ERROR_SUCCESS) + { + /* Ensure null termination */ + if (!cbData || *((char *)pvBuf + cbData - 1)) + { + *((char *)pvBuf + cbData) = 0; + cbData++; + } + + /* Recheck dwType in case it changed since the first call */ + if (dwType == REG_EXPAND_SZ && !(dwFlags & RRF_NOEXPAND)) + { + cbData = ExpandEnvironmentStringsA(pvBuf, pvData, + pcbData ? *pcbData : 0); + dwType = REG_SZ; + if (pvData && cbData > *pcbData) + ret = ERROR_MORE_DATA; + } + else if (pvData) + { + if (cbData > *pcbData) + ret = ERROR_MORE_DATA; + else + CopyMemory(pvData, pvBuf, cbData); + } + } + + heap_free(pvBuf); + } + + if (pszSubKey && pszSubKey[0]) + RegCloseKey(hKey); + + apply_restrictions(dwFlags, dwType, cbData, &ret); + + if (pvData && ret != ERROR_SUCCESS && (dwFlags & RRF_ZEROONFAILURE)) + ZeroMemory(pvData, *pcbData); + + if (pdwType) *pdwType = dwType; + if (pcbData) *pcbData = cbData; + + return ret; +} + + +/****************************************************************************** + * RegEnumValueW (kernelbase.@) + * + * Enumerates the values for the specified open registry key. + * + * PARAMS + * hkey [I] Handle to key to query + * index [I] Index of value to query + * value [O] Value string + * val_count [I/O] Size of value buffer (in wchars) + * reserved [I] Reserved + * type [O] Type code + * data [O] Value data + * count [I/O] Size of data buffer (in bytes) + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + */ +LSTATUS WINAPI RegEnumValueW( HKEY hkey, DWORD index, LPWSTR value, LPDWORD val_count, + LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count ) +{ + NTSTATUS status; + DWORD total_size; + char buffer[256], *buf_ptr = buffer; + KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer; + static const int info_size = offsetof( KEY_VALUE_FULL_INFORMATION, Name ); + + TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n", + hkey, index, value, val_count, reserved, type, data, count ); + + if ((data && !count) || reserved || !value || !val_count) + return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR); + if (data) total_size += *count; + total_size = min( sizeof(buffer), total_size ); + + status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation, + buffer, total_size, &total_size ); + + /* retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( total_size ))) + return ERROR_NOT_ENOUGH_MEMORY; + info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr; + status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation, + buf_ptr, total_size, &total_size ); + } + + if (status) goto done; + + if (info->NameLength/sizeof(WCHAR) >= *val_count) + { + status = STATUS_BUFFER_OVERFLOW; + goto overflow; + } + memcpy( value, info->Name, info->NameLength ); + *val_count = info->NameLength / sizeof(WCHAR); + value[*val_count] = 0; + + if (data) + { + if (total_size - info->DataOffset > *count) + { + status = STATUS_BUFFER_OVERFLOW; + goto overflow; + } + memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset ); + if (total_size - info->DataOffset <= *count-sizeof(WCHAR) && is_string(info->Type)) + { + /* if the type is REG_SZ and data is not 0-terminated + * and there is enough space in the buffer NT appends a \0 */ + WCHAR *ptr = (WCHAR *)(data + total_size - info->DataOffset); + if (ptr > (WCHAR *)data && ptr[-1]) *ptr = 0; + } + } + + overflow: + if (type) *type = info->Type; + if (count) *count = info->DataLength; + + done: + if (buf_ptr != buffer) heap_free( buf_ptr ); + return RtlNtStatusToDosError(status); +} + + +/****************************************************************************** + * RegEnumValueA (kernelbase.@) + * + * See RegEnumValueW. + */ +LSTATUS WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count, + LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count ) +{ + NTSTATUS status; + DWORD total_size; + char buffer[256], *buf_ptr = buffer; + KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer; + static const int info_size = offsetof( KEY_VALUE_FULL_INFORMATION, Name ); + + TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n", + hkey, index, value, val_count, reserved, type, data, count ); + + if ((data && !count) || reserved || !value || !val_count) + return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR); + if (data) total_size += *count; + total_size = min( sizeof(buffer), total_size ); + + status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation, + buffer, total_size, &total_size ); + + /* we need to fetch the contents for a string type even if not requested, + * because we need to compute the length of the ANSI string. */ + + /* retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( total_size ))) + return ERROR_NOT_ENOUGH_MEMORY; + info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr; + status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation, + buf_ptr, total_size, &total_size ); + } + + if (status) goto done; + + if (is_string(info->Type)) + { + DWORD len; + RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->DataOffset), + total_size - info->DataOffset ); + if (data && len) + { + if (len > *count) status = STATUS_BUFFER_OVERFLOW; + else + { + RtlUnicodeToMultiByteN( (char*)data, len, NULL, (WCHAR *)(buf_ptr + info->DataOffset), + total_size - info->DataOffset ); + /* if the type is REG_SZ and data is not 0-terminated + * and there is enough space in the buffer NT appends a \0 */ + if (len < *count && data[len-1]) data[len] = 0; + } + } + info->DataLength = len; + } + else if (data) + { + if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW; + else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset ); + } + + if (!status) + { + DWORD len; + + RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength ); + if (len >= *val_count) + { + status = STATUS_BUFFER_OVERFLOW; + if (*val_count) + { + len = *val_count - 1; + RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength ); + value[len] = 0; + } + } + else + { + RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength ); + value[len] = 0; + *val_count = len; + } + } + + if (type) *type = info->Type; + if (count) *count = info->DataLength; + + done: + if (buf_ptr != buffer) heap_free( buf_ptr ); + return RtlNtStatusToDosError(status); +} + +/****************************************************************************** + * RegDeleteValueW (kernelbase.@) + * + * See RegDeleteValueA. + */ +LSTATUS WINAPI RegDeleteValueW( HKEY hkey, LPCWSTR name ) +{ + return RegDeleteKeyValueW( hkey, NULL, name ); +} + +/****************************************************************************** + * RegDeleteValueA (kernelbase.@) + * + * Delete a value from the registry. + * + * PARAMS + * hkey [I] Registry handle of the key holding the value + * name [I] Name of the value under hkey to delete + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + */ +LSTATUS WINAPI RegDeleteValueA( HKEY hkey, LPCSTR name ) +{ + return RegDeleteKeyValueA( hkey, NULL, name ); +} + +/****************************************************************************** + * RegDeleteKeyValueW (kernelbase.@) + */ +LONG WINAPI RegDeleteKeyValueW( HKEY hkey, LPCWSTR subkey, LPCWSTR name ) +{ + UNICODE_STRING nameW; + HKEY hsubkey = 0; + LONG ret; + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + if (subkey) + { + if ((ret = RegOpenKeyExW( hkey, subkey, 0, KEY_SET_VALUE, &hsubkey ))) + return ret; + hkey = hsubkey; + } + + RtlInitUnicodeString( &nameW, name ); + ret = RtlNtStatusToDosError( NtDeleteValueKey( hkey, &nameW ) ); + if (hsubkey) RegCloseKey( hsubkey ); + return ret; +} + +/****************************************************************************** + * RegDeleteKeyValueA (kernelbase.@) + */ +LONG WINAPI RegDeleteKeyValueA( HKEY hkey, LPCSTR subkey, LPCSTR name ) +{ + UNICODE_STRING nameW; + HKEY hsubkey = 0; + ANSI_STRING nameA; + NTSTATUS status; + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + if (subkey) + { + LONG ret = RegOpenKeyExA( hkey, subkey, 0, KEY_SET_VALUE, &hsubkey ); + if (ret) + return ret; + hkey = hsubkey; + } + + RtlInitAnsiString( &nameA, name ); + if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE ))) + { + status = NtDeleteValueKey( hkey, &nameW ); + RtlFreeUnicodeString( &nameW ); + } + + if (hsubkey) RegCloseKey( hsubkey ); + return RtlNtStatusToDosError( status ); +} + +/****************************************************************************** + * RegLoadKeyW (kernelbase.@) + * + * Create a subkey under HKEY_USERS or HKEY_LOCAL_MACHINE and store + * registration information from a specified file into that subkey. + * + * PARAMS + * hkey [I] Handle of open key + * subkey [I] Address of name of subkey + * filename [I] Address of filename for registry information + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + */ +LSTATUS WINAPI RegLoadKeyW( HKEY hkey, LPCWSTR subkey, LPCWSTR filename ) +{ + OBJECT_ATTRIBUTES destkey, file; + UNICODE_STRING subkeyW, filenameW; + NTSTATUS status; + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + destkey.Length = sizeof(destkey); + destkey.RootDirectory = hkey; /* root key: HKLM or HKU */ + destkey.ObjectName = &subkeyW; /* name of the key */ + destkey.Attributes = 0; + destkey.SecurityDescriptor = NULL; + destkey.SecurityQualityOfService = NULL; + RtlInitUnicodeString(&subkeyW, subkey); + + file.Length = sizeof(file); + file.RootDirectory = NULL; + file.ObjectName = &filenameW; /* file containing the hive */ + file.Attributes = OBJ_CASE_INSENSITIVE; + file.SecurityDescriptor = NULL; + file.SecurityQualityOfService = NULL; + RtlDosPathNameToNtPathName_U(filename, &filenameW, NULL, NULL); + + status = NtLoadKey(&destkey, &file); + RtlFreeUnicodeString(&filenameW); + return RtlNtStatusToDosError( status ); +} + + +/****************************************************************************** + * RegLoadKeyA (kernelbase.@) + * + * See RegLoadKeyW. + */ +LSTATUS WINAPI RegLoadKeyA( HKEY hkey, LPCSTR subkey, LPCSTR filename ) +{ + UNICODE_STRING subkeyW = { 0, 0, NULL }, filenameW = { 0, 0, NULL }; + STRING subkeyA, filenameA; + NTSTATUS status; + LONG ret; + + RtlInitAnsiString(&subkeyA, subkey); + RtlInitAnsiString(&filenameA, filename); + + if (!(status = RtlAnsiStringToUnicodeString(&subkeyW, &subkeyA, TRUE)) && + !(status = RtlAnsiStringToUnicodeString(&filenameW, &filenameA, TRUE))) + { + ret = RegLoadKeyW(hkey, subkeyW.Buffer, filenameW.Buffer); + } + else ret = RtlNtStatusToDosError(status); + RtlFreeUnicodeString(&subkeyW); + RtlFreeUnicodeString(&filenameW); + return ret; +} + + +/****************************************************************************** + * RegSaveKeyExW (kernelbase.@) + */ +LSTATUS WINAPI RegSaveKeyExW( HKEY hkey, LPCWSTR file, SECURITY_ATTRIBUTES *sa, DWORD flags ) +{ + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + + TRACE( "(%p,%s,%p)\n", hkey, debugstr_w(file), sa ); + + if (!file || !*file) return ERROR_INVALID_PARAMETER; + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + if ((status = RtlDosPathNameToNtPathName_U_WithStatus( file, &nameW, NULL, NULL ))) + return RtlNtStatusToDosError( status ); + + InitializeObjectAttributes( &attr, &nameW, OBJ_CASE_INSENSITIVE, 0, sa ); + status = NtCreateFile( &handle, GENERIC_WRITE | SYNCHRONIZE, &attr, &io, NULL, FILE_NON_DIRECTORY_FILE, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OVERWRITE_IF, + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + RtlFreeUnicodeString( &nameW ); + if (!status) + { + status = NtSaveKey( hkey, handle ); + CloseHandle( handle ); + } + return RtlNtStatusToDosError( status ); +} + + +/****************************************************************************** + * RegSaveKeyExA (kernelbase.@) + */ +LSTATUS WINAPI RegSaveKeyExA( HKEY hkey, LPCSTR file, SECURITY_ATTRIBUTES *sa, DWORD flags ) +{ + UNICODE_STRING *fileW = &NtCurrentTeb()->StaticUnicodeString; + NTSTATUS status; + STRING fileA; + + RtlInitAnsiString(&fileA, file); + if ((status = RtlAnsiStringToUnicodeString(fileW, &fileA, FALSE))) + return RtlNtStatusToDosError( status ); + return RegSaveKeyExW(hkey, fileW->Buffer, sa, flags); +} + + +/****************************************************************************** + * RegRestoreKeyW (kernelbase.@) + * + * Read the registry information from a file and copy it over a key. + * + * PARAMS + * hkey [I] Handle of key where restore begins + * lpFile [I] Address of filename containing saved tree + * dwFlags [I] Optional flags + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + */ +LSTATUS WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags ) +{ + TRACE("(%p,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags); + + /* It seems to do this check before the hkey check */ + if (!lpFile || !*lpFile) + return ERROR_INVALID_PARAMETER; + + FIXME("(%p,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags); + + /* Check for file existence */ + + return ERROR_SUCCESS; +} + + +/****************************************************************************** + * RegRestoreKeyA (kernelbase.@) + * + * See RegRestoreKeyW. + */ +LSTATUS WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags ) +{ + UNICODE_STRING lpFileW; + LONG ret; + + RtlCreateUnicodeStringFromAsciiz( &lpFileW, lpFile ); + ret = RegRestoreKeyW( hkey, lpFileW.Buffer, dwFlags ); + RtlFreeUnicodeString( &lpFileW ); + return ret; +} + + +/****************************************************************************** + * RegUnLoadKeyW (kernelbase.@) + * + * Unload a registry key and its subkeys from the registry. + * + * PARAMS + * hkey [I] Handle of open key + * lpSubKey [I] Address of name of subkey to unload + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + */ +LSTATUS WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING subkey; + + TRACE("(%p,%s)\n",hkey, debugstr_w(lpSubKey)); + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + RtlInitUnicodeString(&subkey, lpSubKey); + InitializeObjectAttributes(&attr, &subkey, OBJ_CASE_INSENSITIVE, hkey, NULL); + return RtlNtStatusToDosError( NtUnloadKey(&attr) ); +} + + +/****************************************************************************** + * RegUnLoadKeyA (kernelbase.@) + * + * See RegUnLoadKeyW. + */ +LSTATUS WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey ) +{ + UNICODE_STRING lpSubKeyW; + LONG ret; + + RtlCreateUnicodeStringFromAsciiz( &lpSubKeyW, lpSubKey ); + ret = RegUnLoadKeyW( hkey, lpSubKeyW.Buffer ); + RtlFreeUnicodeString( &lpSubKeyW ); + return ret; +} + + +/****************************************************************************** + * RegSetKeySecurity (kernelbase.@) + * + * Set the security of an open registry key. + * + * PARAMS + * hkey [I] Open handle of key to set + * SecurityInfo [I] Descriptor contents + * pSecurityDesc [I] Address of descriptor for key + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + */ +LSTATUS WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo, + PSECURITY_DESCRIPTOR pSecurityDesc ) +{ + TRACE("(%p,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc); + + /* It seems to perform this check before the hkey check */ + if ((SecurityInfo & OWNER_SECURITY_INFORMATION) || + (SecurityInfo & GROUP_SECURITY_INFORMATION) || + (SecurityInfo & DACL_SECURITY_INFORMATION) || + (SecurityInfo & SACL_SECURITY_INFORMATION)) { + /* Param OK */ + } else + return ERROR_INVALID_PARAMETER; + + if (!pSecurityDesc) + return ERROR_INVALID_PARAMETER; + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + return RtlNtStatusToDosError( NtSetSecurityObject( hkey, SecurityInfo, pSecurityDesc ) ); +} + + +/****************************************************************************** + * RegGetKeySecurity (kernelbase.@) + * + * Get a copy of the security descriptor for a given registry key. + * + * PARAMS + * hkey [I] Open handle of key to set + * SecurityInformation [I] Descriptor contents + * pSecurityDescriptor [O] Address of descriptor for key + * lpcbSecurityDescriptor [I/O] Address of size of buffer and description + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: Error code + */ +LSTATUS WINAPI RegGetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, + LPDWORD lpcbSecurityDescriptor ) +{ + TRACE("(%p,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor, + *lpcbSecurityDescriptor); + + if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE; + + return RtlNtStatusToDosError( NtQuerySecurityObject( hkey, + SecurityInformation, pSecurityDescriptor, + *lpcbSecurityDescriptor, lpcbSecurityDescriptor ) ); +} + + +/****************************************************************************** + * RegFlushKey (kernelbase.@) + * + * Immediately write a registry key to registry. + * + * PARAMS + * hkey [I] Handle of key to write + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: Error code + */ +LSTATUS WINAPI RegFlushKey( HKEY hkey ) +{ + hkey = get_special_root_hkey( hkey ); + if (!hkey) return ERROR_INVALID_HANDLE; + + return RtlNtStatusToDosError( NtFlushKey( hkey ) ); +} + + +/****************************************************************************** + * RegNotifyChangeKeyValue (kernelbase.@) + * + * Notify the caller about changes to the attributes or contents of a registry key. + * + * PARAMS + * hkey [I] Handle of key to watch + * fWatchSubTree [I] Flag for subkey notification + * fdwNotifyFilter [I] Changes to be reported + * hEvent [I] Handle of signaled event + * fAsync [I] Flag for asynchronous reporting + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + */ +LSTATUS WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree, + DWORD fdwNotifyFilter, HANDLE hEvent, + BOOL fAsync ) +{ + NTSTATUS status; + IO_STATUS_BLOCK iosb; + + hkey = get_special_root_hkey( hkey ); + if (!hkey) return ERROR_INVALID_HANDLE; + + TRACE("(%p,%i,%ld,%p,%i)\n", hkey, fWatchSubTree, fdwNotifyFilter, + hEvent, fAsync); + + status = NtNotifyChangeKey( hkey, hEvent, NULL, NULL, &iosb, + fdwNotifyFilter, fWatchSubTree, NULL, 0, + fAsync); + + if (status && status != STATUS_PENDING) + return RtlNtStatusToDosError( status ); + + return ERROR_SUCCESS; +} + +/****************************************************************************** + * RegOpenUserClassesRoot (kernelbase.@) + * + * Open the HKEY_CLASSES_ROOT key for a user. + * + * PARAMS + * hToken [I] Handle of token representing the user + * dwOptions [I] Reserved, must be 0 + * samDesired [I] Desired access rights + * phkResult [O] Destination for the resulting key handle + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: nonzero error code from Winerror.h + * + * NOTES + * On Windows 2000 and upwards the HKEY_CLASSES_ROOT key is a view of the + * "HKEY_LOCAL_MACHINE\Software\Classes" and the + * "HKEY_CURRENT_USER\Software\Classes" keys merged together. + */ +LSTATUS WINAPI RegOpenUserClassesRoot( HANDLE hToken, DWORD dwOptions, REGSAM samDesired, PHKEY phkResult ) +{ + FIXME("(%p, 0x%lx, 0x%lx, %p) semi-stub\n", hToken, dwOptions, samDesired, phkResult); + + *phkResult = HKEY_CLASSES_ROOT; + return ERROR_SUCCESS; +} + + +static void dump_mui_cache(void) +{ + struct mui_cache_entry *ent; + + TRACE("---------- MUI Cache ----------\n"); + LIST_FOR_EACH_ENTRY( ent, ®_mui_cache, struct mui_cache_entry, entry ) + TRACE("entry=%p, %s,-%lu [%#lx] => %s\n", + ent, wine_dbgstr_w(ent->file_name), ent->index, ent->locale, wine_dbgstr_w(ent->text)); +} + +static inline void free_mui_cache_entry(struct mui_cache_entry *ent) +{ + heap_free(ent->file_name); + heap_free(ent->text); + heap_free(ent); +} + +/* critical section must be held */ +static int reg_mui_cache_get(const WCHAR *file_name, UINT index, WCHAR **buffer) +{ + struct mui_cache_entry *ent; + + TRACE("(%s %u %p)\n", wine_dbgstr_w(file_name), index, buffer); + + LIST_FOR_EACH_ENTRY(ent, ®_mui_cache, struct mui_cache_entry, entry) + { + if (ent->index == index && ent->locale == GetThreadLocale() && + !lstrcmpiW(ent->file_name, file_name)) + goto found; + } + return 0; + +found: + /* move to the list head */ + if (list_prev(®_mui_cache, &ent->entry)) { + list_remove(&ent->entry); + list_add_head(®_mui_cache, &ent->entry); + } + + TRACE("=> %s\n", wine_dbgstr_w(ent->text)); + *buffer = ent->text; + return lstrlenW(ent->text); +} + +/* critical section must be held */ +static void reg_mui_cache_put(const WCHAR *file_name, UINT index, const WCHAR *buffer, INT size) +{ + struct mui_cache_entry *ent; + TRACE("(%s %u %s %d)\n", wine_dbgstr_w(file_name), index, wine_dbgstr_wn(buffer, size), size); + + ent = heap_calloc(sizeof(*ent), 1); + if (!ent) + return; + ent->file_name = heap_alloc((lstrlenW(file_name) + 1) * sizeof(WCHAR)); + if (!ent->file_name) { + free_mui_cache_entry(ent); + return; + } + lstrcpyW(ent->file_name, file_name); + ent->index = index; + ent->locale = GetThreadLocale(); + ent->text = heap_alloc((size + 1) * sizeof(WCHAR)); + if (!ent->text) { + free_mui_cache_entry(ent); + return; + } + memcpy(ent->text, buffer, size * sizeof(WCHAR)); + ent->text[size] = '\0'; + + TRACE("add %p\n", ent); + list_add_head(®_mui_cache, &ent->entry); + if (reg_mui_cache_count > REG_MUI_CACHE_SIZE) { + ent = LIST_ENTRY( list_tail( ®_mui_cache ), struct mui_cache_entry, entry ); + TRACE("freeing %p\n", ent); + list_remove(&ent->entry); + free_mui_cache_entry(ent); + } + else + reg_mui_cache_count++; + + if (TRACE_ON(reg)) + dump_mui_cache(); + return; +} + +static LONG load_mui_string(const WCHAR *file_name, UINT res_id, WCHAR *buffer, INT max_chars, INT *req_chars, DWORD flags) +{ + HMODULE hModule = NULL; + WCHAR *string = NULL, *full_name; + int size; + LONG result; + + /* Verify the file existence. i.e. We don't rely on PATH variable */ + if (GetFileAttributesW(file_name) == INVALID_FILE_ATTRIBUTES) + return ERROR_FILE_NOT_FOUND; + + size = GetFullPathNameW(file_name, 0, NULL, NULL); + if (!size) + return GetLastError(); + full_name = heap_alloc(size * sizeof(WCHAR)); + if (!full_name) + return ERROR_NOT_ENOUGH_MEMORY; + GetFullPathNameW(file_name, size, full_name, NULL); + + RtlEnterCriticalSection(®_mui_cs); + size = reg_mui_cache_get(full_name, res_id, &string); + if (!size) { + RtlLeaveCriticalSection(®_mui_cs); + + /* Load the file */ + hModule = LoadLibraryExW(full_name, NULL, + LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); + if (!hModule) + return GetLastError(); + + size = LoadStringW(hModule, res_id, (WCHAR *)&string, 0); + if (!size) { + if (string) result = ERROR_NOT_FOUND; + else result = GetLastError(); + goto cleanup; + } + + RtlEnterCriticalSection(®_mui_cs); + reg_mui_cache_put(full_name, res_id, string, size); + RtlLeaveCriticalSection(®_mui_cs); + } + *req_chars = size + 1; + + /* If no buffer is given, skip copying. */ + if (!buffer) { + result = ERROR_MORE_DATA; + goto cleanup; + } + + /* Else copy over the string, respecting the buffer size. */ + if (size < max_chars) + max_chars = size; + else { + if (flags & REG_MUI_STRING_TRUNCATE) + max_chars--; + else { + result = ERROR_MORE_DATA; + goto cleanup; + } + } + if (max_chars >= 0) { + memcpy(buffer, string, max_chars * sizeof(WCHAR)); + buffer[max_chars] = '\0'; + } + + result = ERROR_SUCCESS; + +cleanup: + if (hModule) + FreeLibrary(hModule); + else + RtlLeaveCriticalSection(®_mui_cs); + heap_free(full_name); + return result; +} + +/****************************************************************************** + * RegLoadMUIStringW (kernelbase.@) + * + * Load the localized version of a string resource from some PE, respective + * id and path of which are given in the registry value in the format + * @[path]\dllname,-resourceId + * + * PARAMS + * hKey [I] Key, of which to load the string value from. + * pszValue [I] The value to be loaded (Has to be of REG_EXPAND_SZ or REG_SZ type). + * pszBuffer [O] Buffer to store the localized string in. + * cbBuffer [I] Size of the destination buffer in bytes. + * pcbData [O] Number of bytes written to pszBuffer (optional, may be NULL). + * dwFlags [I] Truncate output to fit the buffer if REG_MUI_STRING_TRUNCATE. + * pszBaseDir [I] Base directory of loading path. If NULL, use the current directory. + * + * RETURNS + * Success: ERROR_SUCCESS, + * Failure: nonzero error code from winerror.h + */ +LSTATUS WINAPI RegLoadMUIStringW(HKEY hKey, LPCWSTR pwszValue, LPWSTR pwszBuffer, DWORD cbBuffer, + LPDWORD pcbData, DWORD dwFlags, LPCWSTR pwszBaseDir) +{ + DWORD dwValueType, cbData; + LPWSTR pwszTempBuffer = NULL, pwszExpandedBuffer = NULL; + LONG result; + + TRACE("(hKey = %p, pwszValue = %s, pwszBuffer = %p, cbBuffer = %ld, pcbData = %p, " + "dwFlags = %lu, pwszBaseDir = %s)\n", hKey, debugstr_w(pwszValue), pwszBuffer, + cbBuffer, pcbData, dwFlags, debugstr_w(pwszBaseDir)); + + /* Parameter sanity checks. */ + if (!hKey || (!pwszBuffer && cbBuffer) || (cbBuffer % sizeof(WCHAR)) + || ((dwFlags & REG_MUI_STRING_TRUNCATE) && pcbData) + || (dwFlags & ~REG_MUI_STRING_TRUNCATE)) + return ERROR_INVALID_PARAMETER; + + /* Check for value existence and correctness of its type, allocate a buffer and load it. */ + result = RegQueryValueExW(hKey, pwszValue, NULL, &dwValueType, NULL, &cbData); + if (result != ERROR_SUCCESS) goto cleanup; + if (!(dwValueType == REG_SZ || dwValueType == REG_EXPAND_SZ) || !cbData) { + result = ERROR_FILE_NOT_FOUND; + goto cleanup; + } + pwszTempBuffer = heap_alloc(cbData); + if (!pwszTempBuffer) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + result = RegQueryValueExW(hKey, pwszValue, NULL, &dwValueType, (LPBYTE)pwszTempBuffer, &cbData); + if (result != ERROR_SUCCESS) goto cleanup; + + /* '@' is the prefix for resource based string entries. */ + if (*pwszTempBuffer != '@') { + result = ERROR_INVALID_DATA; + goto cleanup; + } + + /* Expand environment variables regardless of the type. */ + cbData = ExpandEnvironmentStringsW(pwszTempBuffer, NULL, 0) * sizeof(WCHAR); + if (!cbData) goto cleanup; + pwszExpandedBuffer = heap_alloc(cbData); + if (!pwszExpandedBuffer) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + ExpandEnvironmentStringsW(pwszTempBuffer, pwszExpandedBuffer, cbData / sizeof(WCHAR)); + + /* Parse the value and load the string. */ + { + WCHAR *pComma = wcsrchr(pwszExpandedBuffer, ','), *pNewBuffer; + UINT uiStringId; + DWORD baseDirLen; + int reqChars; + + /* Format of the expanded value is 'path_to_dll,-resId' */ + if (!pComma || pComma[1] != '-') { + result = ERROR_INVALID_DATA; + goto cleanup; + } + + uiStringId = wcstol(pComma+2, NULL, 10); + *pComma = '\0'; + + /* Build a resource dll path. */ + baseDirLen = pwszBaseDir ? lstrlenW(pwszBaseDir) : 0; + cbData = (baseDirLen + 1 + lstrlenW(pwszExpandedBuffer + 1) + 1) * sizeof(WCHAR); + pNewBuffer = heap_realloc(pwszTempBuffer, cbData); + if (!pNewBuffer) { + result = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + pwszTempBuffer = pNewBuffer; + pwszTempBuffer[0] = '\0'; + if (baseDirLen) { + lstrcpyW(pwszTempBuffer, pwszBaseDir); + if (pwszBaseDir[baseDirLen - 1] != '\\') + lstrcatW(pwszTempBuffer, L"\\"); + } + lstrcatW(pwszTempBuffer, pwszExpandedBuffer + 1); + + /* Load specified string from the file */ + reqChars = 0; + result = load_mui_string(pwszTempBuffer, uiStringId, pwszBuffer, cbBuffer/sizeof(WCHAR), &reqChars, dwFlags); + if (pcbData && (result == ERROR_SUCCESS || result == ERROR_MORE_DATA)) + *pcbData = reqChars * sizeof(WCHAR); + } + +cleanup: + heap_free(pwszTempBuffer); + heap_free(pwszExpandedBuffer); + return result; +} + +/****************************************************************************** + * RegLoadMUIStringA (kernelbase.@) + * + * Not implemented on native. + */ +LSTATUS WINAPI RegLoadMUIStringA(HKEY hKey, LPCSTR pszValue, LPSTR pszBuffer, DWORD cbBuffer, + LPDWORD pcbData, DWORD dwFlags, LPCSTR pszBaseDir) +{ + return ERROR_CALL_NOT_IMPLEMENTED; +} + + +/****************************************************************************** + * RegDeleteTreeW (kernelbase.@) + * + */ +LSTATUS WINAPI RegDeleteTreeW( HKEY hkey, const WCHAR *subkey ) +{ + DWORD name_size, max_name, max_subkey; + WCHAR *name_buf = NULL; + LONG ret; + + TRACE( "(%p, %s)\n", hkey, debugstr_w(subkey) ); + + if (subkey && *subkey) + { + ret = RegOpenKeyExW( hkey, subkey, 0, KEY_READ, &hkey ); + if (ret) return ret; + } + + ret = RegQueryInfoKeyW( hkey, NULL, NULL, NULL, NULL, &max_subkey, + NULL, NULL, &max_name, NULL, NULL, NULL ); + if (ret) + goto cleanup; + + max_name = max( max_subkey, max_name ) + 1; + if (!(name_buf = heap_alloc( max_name * sizeof(WCHAR) ))) + { + ret = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + + /* Recursively delete subkeys */ + for (;;) + { + name_size = max_name; + ret = RegEnumKeyExW( hkey, 0, name_buf, &name_size, NULL, NULL, NULL, NULL ); + if (ret == ERROR_NO_MORE_ITEMS) break; + if (ret) goto cleanup; + ret = RegDeleteTreeW( hkey, name_buf ); + if (ret) goto cleanup; + } + + /* Delete the key itself */ + if (subkey && *subkey) + { + ret = RegDeleteKeyExW( hkey, L"", 0, 0 ); + goto cleanup; + } + + /* Delete values */ + for (;;) + { + name_size = max_name; + ret = RegEnumValueW( hkey, 0, name_buf, &name_size, NULL, NULL, NULL, NULL ); + if (ret == ERROR_NO_MORE_ITEMS) break; + if (ret) goto cleanup; + ret = RegDeleteValueW( hkey, name_buf ); + if (ret) goto cleanup; + } + + ret = ERROR_SUCCESS; + +cleanup: + heap_free( name_buf ); + if (subkey && *subkey) + RegCloseKey( hkey ); + return ret; +} + + +/****************************************************************************** + * RegDeleteTreeA (kernelbase.@) + * + */ +LSTATUS WINAPI RegDeleteTreeA( HKEY hkey, const char *subkey ) +{ + UNICODE_STRING subkeyW; + LONG ret; + + if (subkey) RtlCreateUnicodeStringFromAsciiz( &subkeyW, subkey ); + else subkeyW.Buffer = NULL; + ret = RegDeleteTreeW( hkey, subkeyW.Buffer ); + RtlFreeUnicodeString( &subkeyW ); + return ret; +} + + +/****************************************************************************** + * RegCopyTreeW (kernelbase.@) + * + */ +LSTATUS WINAPI RegCopyTreeW( HKEY hsrc, const WCHAR *subkey, HKEY hdst ) +{ + DWORD name_size, max_name; + DWORD value_size, max_value; + DWORD max_subkey, i, type; + WCHAR *name_buf = NULL; + BYTE *value_buf = NULL; + HKEY hkey; + LONG ret; + + TRACE( "(%p, %s, %p)\n", hsrc, debugstr_w(subkey), hdst ); + + if (subkey) + { + ret = RegOpenKeyExW( hsrc, subkey, 0, KEY_READ, &hsrc ); + if (ret) return ret; + } + + ret = RegQueryInfoKeyW( hsrc, NULL, NULL, NULL, NULL, &max_subkey, + NULL, NULL, &max_name, &max_value, NULL, NULL ); + if (ret) + goto cleanup; + + max_name = max( max_subkey, max_name ) + 1; + if (!(name_buf = heap_alloc( max_name * sizeof(WCHAR) ))) + { + ret = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + + if (!(value_buf = heap_alloc( max_value ))) + { + ret = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + + /* Copy values */ + for (i = 0;; i++) + { + name_size = max_name; + value_size = max_value; + ret = RegEnumValueW( hsrc, i, name_buf, &name_size, NULL, &type, value_buf, &value_size ); + if (ret == ERROR_NO_MORE_ITEMS) break; + if (ret) goto cleanup; + ret = RegSetValueExW( hdst, name_buf, 0, type, value_buf, value_size ); + if (ret) goto cleanup; + } + + /* Recursively copy subkeys */ + for (i = 0;; i++) + { + name_size = max_name; + ret = RegEnumKeyExW( hsrc, i, name_buf, &name_size, NULL, NULL, NULL, NULL ); + if (ret == ERROR_NO_MORE_ITEMS) break; + if (ret) goto cleanup; + ret = RegCreateKeyExW( hdst, name_buf, 0, NULL, 0, KEY_WRITE, NULL, &hkey, NULL ); + if (ret) goto cleanup; + ret = RegCopyTreeW( hsrc, name_buf, hkey ); + RegCloseKey( hkey ); + if (ret) goto cleanup; + } + + ret = ERROR_SUCCESS; + +cleanup: + heap_free( name_buf ); + heap_free( value_buf ); + if (subkey) + RegCloseKey( hsrc ); + return ret; +} + + +/****************************************************************************** + * RegLoadAppKeyA (kernelbase.@) + * + */ +LSTATUS WINAPI RegLoadAppKeyA(const char *file, HKEY *result, REGSAM sam, DWORD options, DWORD reserved) +{ + FIXME("%s %p %lu %lu %lu: stub\n", wine_dbgstr_a(file), result, sam, options, reserved); + + if (!file || reserved) + return ERROR_INVALID_PARAMETER; + + *result = (HKEY)0xdeadbeef; + return ERROR_SUCCESS; +} + +/****************************************************************************** + * RegLoadAppKeyW (kernelbase.@) + * + */ +LSTATUS WINAPI RegLoadAppKeyW(const WCHAR *file, HKEY *result, REGSAM sam, DWORD options, DWORD reserved) +{ + FIXME("%s %p %lu %lu %lu: stub\n", wine_dbgstr_w(file), result, sam, options, reserved); + + if (!file || reserved) + return ERROR_INVALID_PARAMETER; + + *result = (HKEY)0xdeadbeef; + return ERROR_SUCCESS; +} + + +/*********************************************************************** + * DnsHostnameToComputerNameExW (kernelbase.@) + * + * FIXME: how is this different from the non-Ex function? + */ +BOOL WINAPI DECLSPEC_HOTPATCH DnsHostnameToComputerNameExW( const WCHAR *hostname, WCHAR *computername, + DWORD *size ) +{ + static const WCHAR allowed[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&')(-_{}"; + WCHAR buffer[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD i, len; + + lstrcpynW( buffer, hostname, MAX_COMPUTERNAME_LENGTH + 1 ); + len = lstrlenW( buffer ); + if (*size < len + 1) + { + *size = len; + SetLastError( ERROR_MORE_DATA ); + return FALSE; + } + *size = len; + if (!computername) return FALSE; + for (i = 0; i < len; i++) + { + if (buffer[i] >= 'a' && buffer[i] <= 'z') computername[i] = buffer[i] + 'A' - 'a'; + else computername[i] = wcschr( allowed, buffer[i] ) ? buffer[i] : '_'; + } + computername[len] = 0; + return TRUE; +} + + +/*********************************************************************** + * GetComputerNameExA (kernelbase.@) + */ +BOOL WINAPI GetComputerNameExA( COMPUTER_NAME_FORMAT type, char *name, DWORD *len ) +{ + BOOL ret = FALSE; + DWORD lenA, lenW = 0; + WCHAR *buffer; + + GetComputerNameExW( type, NULL, &lenW ); + if (GetLastError() != ERROR_MORE_DATA) return FALSE; + + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + if (GetComputerNameExW( type, buffer, &lenW )) + { + lenA = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL ); + if (lenA > *len) + { + *len = lenA; + SetLastError( ERROR_MORE_DATA ); + } + else + { + WideCharToMultiByte( CP_ACP, 0, buffer, -1, name, *len, NULL, NULL ); + *len = lenA - 1; + ret = TRUE; + } + } + HeapFree( GetProcessHeap(), 0, buffer ); + return ret; +} + + +/*********************************************************************** + * GetComputerNameExW (kernelbase.@) + */ +BOOL WINAPI GetComputerNameExW( COMPUTER_NAME_FORMAT type, WCHAR *name, DWORD *len ) +{ + const WCHAR *keyname, *valuename; + LRESULT ret; + HKEY key; + + switch (type) + { + case ComputerNameNetBIOS: + case ComputerNamePhysicalNetBIOS: + keyname = L"System\\CurrentControlSet\\Control\\ComputerName\\ActiveComputerName"; + valuename = L"ComputerName"; + break; + case ComputerNameDnsHostname: + case ComputerNamePhysicalDnsHostname: + keyname = L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters"; + valuename = L"Hostname"; + break; + case ComputerNameDnsDomain: + case ComputerNamePhysicalDnsDomain: + keyname = L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters"; + valuename = L"Domain"; + break; + case ComputerNameDnsFullyQualified: + case ComputerNamePhysicalDnsFullyQualified: + { + WCHAR *domain, buffer[256]; + DWORD size = ARRAY_SIZE(buffer) - 1; + + if (!GetComputerNameExW( ComputerNameDnsHostname, buffer, &size )) return FALSE; + domain = buffer + lstrlenW(buffer); + *domain++ = '.'; + size = ARRAY_SIZE(buffer) - (domain - buffer); + if (!GetComputerNameExW( ComputerNameDnsDomain, domain, &size )) return FALSE; + if (!*domain) domain[-1] = 0; + size = lstrlenW(buffer); + if (name && size < *len) + { + if (name) lstrcpyW( name, buffer ); + *len = size; + return TRUE; + } + *len = size + 1; + SetLastError( ERROR_MORE_DATA ); + return FALSE; + } + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (!(ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, keyname, 0, KEY_READ, &key ))) + { + DWORD size = *len * sizeof(WCHAR); + ret = RegQueryValueExW( key, valuename, NULL, NULL, (BYTE *)name, &size ); + if (!name) ret = ERROR_MORE_DATA; + else if (!ret) size -= sizeof(WCHAR); + *len = size / sizeof(WCHAR); + RegCloseKey( key ); + } + TRACE("-> %Iu %s\n", ret, debugstr_w(name) ); + if (ret) SetLastError( ret ); + return !ret; +} + + +/*********************************************************************** + * SetComputerNameA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetComputerNameA( const char *name ) +{ + BOOL ret; + DWORD len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 ); + WCHAR *nameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + + MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, len ); + ret = SetComputerNameExW( ComputerNamePhysicalNetBIOS, nameW ); + HeapFree( GetProcessHeap(), 0, nameW ); + return ret; +} + + +/*********************************************************************** + * SetComputerNameW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetComputerNameW( const WCHAR *name ) +{ + return SetComputerNameExW( ComputerNamePhysicalNetBIOS, name ); +} + + +/*********************************************************************** + * SetComputerNameExA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetComputerNameExA( COMPUTER_NAME_FORMAT type, const char *name ) +{ + BOOL ret; + DWORD len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 ); + WCHAR *nameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + + MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, len ); + ret = SetComputerNameExW( type, nameW ); + HeapFree( GetProcessHeap(), 0, nameW ); + return ret; +} + + +/*********************************************************************** + * SetComputerNameExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetComputerNameExW( COMPUTER_NAME_FORMAT type, const WCHAR *name ) +{ + WCHAR buffer[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD size; + HKEY key; + LRESULT ret; + + TRACE( "%u %s\n", type, debugstr_w( name )); + + switch (type) + { + case ComputerNameDnsHostname: + case ComputerNamePhysicalDnsHostname: + ret = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters", + 0, NULL, 0, KEY_ALL_ACCESS, NULL, &key, NULL ); + if (ret) break; + ret = RegSetValueExW( key, L"Hostname", 0, REG_SZ, + (BYTE *)name, (lstrlenW(name) + 1) * sizeof(WCHAR) ); + RegCloseKey( key ); + /* fall through */ + + case ComputerNameNetBIOS: + case ComputerNamePhysicalNetBIOS: + /* @@ Wine registry key: HKCU\Software\Wine\Network */ + if (!RegOpenKeyExW( HKEY_CURRENT_USER, L"Software\\Wine\\Network", 0, KEY_READ, &key )) + { + BOOL use_dns = TRUE; + size = sizeof(buffer); + if (!RegQueryValueExW( key, L"UseDnsComputerName", NULL, NULL, (BYTE *)buffer, &size )) + use_dns = IS_OPTION_TRUE( buffer[0] ); + RegCloseKey( key ); + if (!use_dns) + { + ret = ERROR_ACCESS_DENIED; + break; + } + } + size = ARRAY_SIZE( buffer ); + if (!DnsHostnameToComputerNameExW( name, buffer, &size )) return FALSE; + ret = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName", + 0, NULL, 0, KEY_ALL_ACCESS, NULL, &key, NULL ); + if (ret) break; + ret = RegSetValueExW( key, L"ComputerName", 0, REG_SZ, + (BYTE *)buffer, (lstrlenW(buffer) + 1) * sizeof(WCHAR) ); + RegCloseKey( key ); + break; + + case ComputerNameDnsDomain: + case ComputerNamePhysicalDnsDomain: + ret = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters", + 0, NULL, 0, KEY_ALL_ACCESS, NULL, &key, NULL ); + if (ret) break; + ret = RegSetValueExW( key, L"Domain", 0, REG_SZ, + (BYTE *)name, (lstrlenW(name) + 1) * sizeof(WCHAR) ); + RegCloseKey( key ); + break; + default: + ret = ERROR_INVALID_PARAMETER; + break; + } + if (ret) SetLastError( ret ); + return !ret; +} + +struct USKEY +{ + HKEY HKCUstart; /* Start key in CU hive */ + HKEY HKCUkey; /* Opened key in CU hive */ + HKEY HKLMstart; /* Start key in LM hive */ + HKEY HKLMkey; /* Opened key in LM hive */ + WCHAR path[MAX_PATH]; +}; + +LONG WINAPI SHRegCreateUSKeyA(LPCSTR path, REGSAM samDesired, HUSKEY relative_key, PHUSKEY new_uskey, DWORD flags) +{ + WCHAR *pathW; + LONG ret; + + TRACE("%s, %#lx, %p, %p, %#lx\n", debugstr_a(path), samDesired, relative_key, new_uskey, flags); + + if (path) + { + INT len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0); + pathW = heap_alloc(len * sizeof(WCHAR)); + if (!pathW) + return ERROR_NOT_ENOUGH_MEMORY; + MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len); + } + else + pathW = NULL; + + ret = SHRegCreateUSKeyW(pathW, samDesired, relative_key, new_uskey, flags); + HeapFree(GetProcessHeap(), 0, pathW); + return ret; +} + +static HKEY reg_duplicate_hkey(HKEY hKey) +{ + HKEY newKey = 0; + + RegOpenKeyExW(hKey, 0, 0, MAXIMUM_ALLOWED, &newKey); + return newKey; +} + +static HKEY reg_get_hkey_from_huskey(HUSKEY hUSKey, BOOL is_hkcu) +{ + struct USKEY *mihk = hUSKey; + HKEY test = hUSKey; + + if (test == HKEY_CLASSES_ROOT + || test == HKEY_CURRENT_CONFIG + || test == HKEY_CURRENT_USER + || test == HKEY_DYN_DATA + || test == HKEY_LOCAL_MACHINE + || test == HKEY_PERFORMANCE_DATA + || test == HKEY_USERS) +/* FIXME: need to define for Win2k, ME, XP + * (test == HKEY_PERFORMANCE_TEXT) || + * (test == HKEY_PERFORMANCE_NLSTEXT) || + */ + { + return test; + } + + return is_hkcu ? mihk->HKCUkey : mihk->HKLMkey; +} + +LONG WINAPI SHRegCreateUSKeyW(const WCHAR *path, REGSAM samDesired, HUSKEY relative_key, PHUSKEY new_uskey, DWORD flags) +{ + LONG ret = ERROR_CALL_NOT_IMPLEMENTED; + struct USKEY *ret_key; + + TRACE("%s, %#lx, %p, %p, %#lx\n", debugstr_w(path), samDesired, relative_key, new_uskey, flags); + + if (!new_uskey) + return ERROR_INVALID_PARAMETER; + + *new_uskey = NULL; + + if (flags & ~SHREGSET_FORCE_HKCU) + { + FIXME("unsupported flags 0x%08lx\n", flags); + return ERROR_SUCCESS; + } + + ret_key = heap_alloc_zero(sizeof(*ret_key)); + lstrcpynW(ret_key->path, path, ARRAY_SIZE(ret_key->path)); + + if (relative_key) + { + ret_key->HKCUstart = reg_duplicate_hkey(reg_get_hkey_from_huskey(relative_key, TRUE)); + ret_key->HKLMstart = reg_duplicate_hkey(reg_get_hkey_from_huskey(relative_key, FALSE)); + } + else + { + ret_key->HKCUstart = HKEY_CURRENT_USER; + ret_key->HKLMstart = HKEY_LOCAL_MACHINE; + } + + if (flags & SHREGSET_FORCE_HKCU) + { + ret = RegCreateKeyExW(ret_key->HKCUstart, path, 0, NULL, 0, samDesired, NULL, &ret_key->HKCUkey, NULL); + if (ret == ERROR_SUCCESS) + *new_uskey = ret_key; + else + heap_free(ret_key); + } + + return ret; +} + +LONG WINAPI SHRegCloseUSKey(HUSKEY hUSKey) +{ + struct USKEY *key = hUSKey; + LONG ret = ERROR_SUCCESS; + + if (!key) + return ERROR_INVALID_PARAMETER; + + if (key->HKCUkey) + ret = RegCloseKey(key->HKCUkey); + if (key->HKCUstart && key->HKCUstart != HKEY_CURRENT_USER) + ret = RegCloseKey(key->HKCUstart); + if (key->HKLMkey) + ret = RegCloseKey(key->HKLMkey); + if (key->HKLMstart && key->HKLMstart != HKEY_LOCAL_MACHINE) + ret = RegCloseKey(key->HKLMstart); + + heap_free(key); + return ret; +} + +LONG WINAPI SHRegDeleteEmptyUSKeyA(HUSKEY hUSKey, const char *value, SHREGDEL_FLAGS flags) +{ + FIXME("%p, %s, %#x\n", hUSKey, debugstr_a(value), flags); + return ERROR_SUCCESS; +} + +LONG WINAPI SHRegDeleteEmptyUSKeyW(HUSKEY hUSKey, const WCHAR *value, SHREGDEL_FLAGS flags) +{ + FIXME("%p, %s, %#x\n", hUSKey, debugstr_w(value), flags); + return ERROR_SUCCESS; +} + +LONG WINAPI SHRegDeleteUSValueA(HUSKEY hUSKey, const char *value, SHREGDEL_FLAGS flags) +{ + FIXME("%p, %s, %#x\n", hUSKey, debugstr_a(value), flags); + return ERROR_SUCCESS; +} + +LONG WINAPI SHRegDeleteUSValueW(HUSKEY hUSKey, const WCHAR *value, SHREGDEL_FLAGS flags) +{ + FIXME("%p, %s, %#x\n", hUSKey, debugstr_w(value), flags); + return ERROR_SUCCESS; +} + +LONG WINAPI SHRegEnumUSValueA(HUSKEY hUSKey, DWORD index, char *value_name, DWORD *value_name_len, DWORD *type, + void *data, DWORD *data_len, SHREGENUM_FLAGS flags) +{ + HKEY dokey; + + TRACE("%p, %#lx, %p, %p, %p, %p, %p, %#x\n", hUSKey, index, value_name, value_name_len, type, data, data_len, flags); + + if ((flags == SHREGENUM_HKCU || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, TRUE))) + return RegEnumValueA(dokey, index, value_name, value_name_len, NULL, type, data, data_len); + + if ((flags == SHREGENUM_HKLM || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, FALSE))) + return RegEnumValueA(dokey, index, value_name, value_name_len, NULL, type, data, data_len); + + FIXME("no support for SHREGENUM_BOTH\n"); + return ERROR_INVALID_FUNCTION; +} + +LONG WINAPI SHRegEnumUSValueW(HUSKEY hUSKey, DWORD index, WCHAR *value_name, DWORD *value_name_len, DWORD *type, + void *data, DWORD *data_len, SHREGENUM_FLAGS flags) +{ + HKEY dokey; + + TRACE("%p, %#lx, %p, %p, %p, %p, %p, %#x\n", hUSKey, index, value_name, value_name_len, type, data, data_len, flags); + + if ((flags == SHREGENUM_HKCU || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, TRUE))) + return RegEnumValueW(dokey, index, value_name, value_name_len, NULL, type, data, data_len); + + if ((flags == SHREGENUM_HKLM || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, FALSE))) + return RegEnumValueW(dokey, index, value_name, value_name_len, NULL, type, data, data_len); + + FIXME("no support for SHREGENUM_BOTH\n"); + return ERROR_INVALID_FUNCTION; +} + +LONG WINAPI SHRegEnumUSKeyA(HUSKEY hUSKey, DWORD index, char *name, DWORD *name_len, SHREGENUM_FLAGS flags) +{ + HKEY dokey; + + TRACE("%p, %ld, %p, %p(%ld), %d\n", hUSKey, index, name, name_len, *name_len, flags); + + if ((flags == SHREGENUM_HKCU || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, TRUE))) + return RegEnumKeyExA(dokey, index, name, name_len, 0, 0, 0, 0); + + if ((flags == SHREGENUM_HKLM || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, FALSE))) + return RegEnumKeyExA(dokey, index, name, name_len, 0, 0, 0, 0); + + FIXME("no support for SHREGENUM_BOTH\n"); + return ERROR_INVALID_FUNCTION; +} + +LONG WINAPI SHRegEnumUSKeyW(HUSKEY hUSKey, DWORD index, WCHAR *name, DWORD *name_len, SHREGENUM_FLAGS flags) +{ + HKEY dokey; + + TRACE("%p, %ld, %p, %p(%ld), %d\n", hUSKey, index, name, name_len, *name_len, flags); + + if ((flags == SHREGENUM_HKCU || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, TRUE))) + return RegEnumKeyExW(dokey, index, name, name_len, 0, 0, 0, 0); + + if ((flags == SHREGENUM_HKLM || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, FALSE))) + return RegEnumKeyExW(dokey, index, name, name_len, 0, 0, 0, 0); + + FIXME("no support for SHREGENUM_BOTH\n"); + return ERROR_INVALID_FUNCTION; +} + +LONG WINAPI SHRegOpenUSKeyA(const char *path, REGSAM access_mask, HUSKEY relative_key, HUSKEY *uskey, BOOL ignore_hkcu) +{ + WCHAR pathW[MAX_PATH]; + + if (path) + MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, ARRAY_SIZE(pathW)); + + return SHRegOpenUSKeyW(path ? pathW : NULL, access_mask, relative_key, uskey, ignore_hkcu); +} + +LONG WINAPI SHRegOpenUSKeyW(const WCHAR *path, REGSAM access_mask, HUSKEY relative_key, HUSKEY *uskey, BOOL ignore_hkcu) +{ + LONG ret2, ret1 = ~ERROR_SUCCESS; + struct USKEY *key; + + TRACE("%s, %#lx, %p, %p, %d\n", debugstr_w(path), access_mask, relative_key, uskey, ignore_hkcu); + + if (uskey) + *uskey = NULL; + + /* Create internal HUSKEY */ + key = heap_alloc_zero(sizeof(*key)); + lstrcpynW(key->path, path, ARRAY_SIZE(key->path)); + + if (relative_key) + { + key->HKCUstart = reg_duplicate_hkey(reg_get_hkey_from_huskey(relative_key, TRUE)); + key->HKLMstart = reg_duplicate_hkey(reg_get_hkey_from_huskey(relative_key, FALSE)); + + /* FIXME: if either of these keys is NULL, create the start key from + * the relative keys start+path + */ + } + else + { + key->HKCUstart = HKEY_CURRENT_USER; + key->HKLMstart = HKEY_LOCAL_MACHINE; + } + + if (!ignore_hkcu) + { + ret1 = RegOpenKeyExW(key->HKCUstart, key->path, 0, access_mask, &key->HKCUkey); + if (ret1) + key->HKCUkey = 0; + } + + ret2 = RegOpenKeyExW(key->HKLMstart, key->path, 0, access_mask, &key->HKLMkey); + if (ret2) + key->HKLMkey = 0; + + if (ret1 || ret2) + TRACE("one or more opens failed: HKCU=%ld HKLM=%ld\n", ret1, ret2); + + if (ret1 && ret2) + { + /* Neither open succeeded: fail */ + SHRegCloseUSKey(key); + return ret2; + } + + TRACE("HUSKEY=%p\n", key); + if (uskey) + *uskey = key; + + return ERROR_SUCCESS; +} + +LONG WINAPI SHRegWriteUSValueA(HUSKEY hUSKey, const char *value, DWORD type, void *data, DWORD data_len, DWORD flags) +{ + WCHAR valueW[MAX_PATH]; + + if (value) + MultiByteToWideChar(CP_ACP, 0, value, -1, valueW, ARRAY_SIZE(valueW)); + + return SHRegWriteUSValueW(hUSKey, value ? valueW : NULL, type, data, data_len, flags); +} + +LONG WINAPI SHRegWriteUSValueW(HUSKEY hUSKey, const WCHAR *value, DWORD type, void *data, DWORD data_len, DWORD flags) +{ + struct USKEY *hKey = hUSKey; + LONG ret = ERROR_SUCCESS; + DWORD dummy; + + TRACE("%p, %s, %ld, %p, %ld, %#lx\n", hUSKey, debugstr_w(value), type, data, data_len, flags); + + __TRY + { + dummy = hKey->HKCUkey || hKey->HKLMkey; + } + __EXCEPT_PAGE_FAULT + { + return ERROR_INVALID_PARAMETER; + } + __ENDTRY + if (!(flags & (SHREGSET_FORCE_HKCU|SHREGSET_FORCE_HKLM))) return ERROR_INVALID_PARAMETER; + + if (flags & (SHREGSET_FORCE_HKCU | SHREGSET_HKCU)) + { + if (!hKey->HKCUkey) + { + /* Create the key */ + ret = RegCreateKeyExW(hKey->HKCUstart, hKey->path, 0, NULL, REG_OPTION_NON_VOLATILE, + MAXIMUM_ALLOWED, NULL, &hKey->HKCUkey, NULL); + TRACE("Creating HKCU key, ret = %ld\n", ret); + if (ret && (flags & SHREGSET_FORCE_HKCU)) + { + hKey->HKCUkey = 0; + return ret; + } + } + + if (!ret) + { + if ((flags & SHREGSET_FORCE_HKCU) || RegQueryValueExW(hKey->HKCUkey, value, NULL, NULL, NULL, &dummy)) + { + /* Doesn't exist or we are forcing: Write value */ + ret = RegSetValueExW(hKey->HKCUkey, value, 0, type, data, data_len); + TRACE("Writing HKCU value, ret = %ld\n", ret); + } + } + } + + if (flags & (SHREGSET_FORCE_HKLM | SHREGSET_HKLM)) + { + if (!hKey->HKLMkey) + { + /* Create the key */ + ret = RegCreateKeyExW(hKey->HKLMstart, hKey->path, 0, NULL, REG_OPTION_NON_VOLATILE, + MAXIMUM_ALLOWED, NULL, &hKey->HKLMkey, NULL); + TRACE("Creating HKLM key, ret = %ld\n", ret); + if (ret && (flags & (SHREGSET_FORCE_HKLM))) + { + hKey->HKLMkey = 0; + return ret; + } + } + + if (!ret) + { + if ((flags & SHREGSET_FORCE_HKLM) || RegQueryValueExW(hKey->HKLMkey, value, NULL, NULL, NULL, &dummy)) + { + /* Doesn't exist or we are forcing: Write value */ + ret = RegSetValueExW(hKey->HKLMkey, value, 0, type, data, data_len); + TRACE("Writing HKLM value, ret = %ld\n", ret); + } + } + } + + return ret; +} + +LONG WINAPI SHRegSetUSValueA(const char *subkey, const char *value, DWORD type, void *data, DWORD data_len, + DWORD flags) +{ + BOOL ignore_hkcu; + HUSKEY hkey; + LONG ret; + + TRACE("%s, %s, %ld, %p, %ld, %#lx\n", debugstr_a(subkey), debugstr_a(value), type, data, data_len, flags); + + if (!data) + return ERROR_INVALID_FUNCTION; + + ignore_hkcu = !(flags & SHREGSET_HKCU || flags & SHREGSET_FORCE_HKCU); + + ret = SHRegOpenUSKeyA(subkey, KEY_ALL_ACCESS, 0, &hkey, ignore_hkcu); + if (ret == ERROR_SUCCESS) + { + ret = SHRegWriteUSValueA(hkey, value, type, data, data_len, flags); + SHRegCloseUSKey(hkey); + } + + return ret; +} + +LONG WINAPI SHRegSetUSValueW(const WCHAR *subkey, const WCHAR *value, DWORD type, void *data, DWORD data_len, + DWORD flags) +{ + BOOL ignore_hkcu; + HUSKEY hkey; + LONG ret; + + TRACE("%s, %s, %ld, %p, %ld, %#lx\n", debugstr_w(subkey), debugstr_w(value), type, data, data_len, flags); + + if (!data) + return ERROR_INVALID_FUNCTION; + + ignore_hkcu = !(flags & SHREGSET_HKCU || flags & SHREGSET_FORCE_HKCU); + + ret = SHRegOpenUSKeyW(subkey, KEY_ALL_ACCESS, 0, &hkey, ignore_hkcu); + if (ret == ERROR_SUCCESS) + { + ret = SHRegWriteUSValueW(hkey, value, type, data, data_len, flags); + SHRegCloseUSKey(hkey); + } + + return ret; +} + +LONG WINAPI SHRegQueryInfoUSKeyA(HUSKEY hUSKey, DWORD *subkeys, DWORD *max_subkey_len, DWORD *values, + DWORD *max_value_name_len, SHREGENUM_FLAGS flags) +{ + HKEY dokey; + LONG ret; + + TRACE("%p, %p, %p, %p, %p, %#x\n", hUSKey, subkeys, max_subkey_len, values, max_value_name_len, flags); + + if ((flags == SHREGENUM_HKCU || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, TRUE))) + { + ret = RegQueryInfoKeyA(dokey, 0, 0, 0, subkeys, max_subkey_len, 0, values, max_value_name_len, 0, 0, 0); + if (ret == ERROR_SUCCESS || flags == SHREGENUM_HKCU) + return ret; + } + + if ((flags == SHREGENUM_HKLM || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, FALSE))) + { + return RegQueryInfoKeyA(dokey, 0, 0, 0, subkeys, max_subkey_len, 0, values, max_value_name_len, 0, 0, 0); + } + + return ERROR_INVALID_FUNCTION; +} + +LONG WINAPI SHRegQueryInfoUSKeyW(HUSKEY hUSKey, DWORD *subkeys, DWORD *max_subkey_len, DWORD *values, + DWORD *max_value_name_len, SHREGENUM_FLAGS flags) +{ + HKEY dokey; + LONG ret; + + TRACE("%p, %p, %p, %p, %p, %#x\n", hUSKey, subkeys, max_subkey_len, values, max_value_name_len, flags); + + if ((flags == SHREGENUM_HKCU || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, TRUE))) + { + ret = RegQueryInfoKeyW(dokey, 0, 0, 0, subkeys, max_subkey_len, 0, values, max_value_name_len, 0, 0, 0); + if (ret == ERROR_SUCCESS || flags == SHREGENUM_HKCU) + return ret; + } + + if ((flags == SHREGENUM_HKLM || flags == SHREGENUM_DEFAULT) && (dokey = reg_get_hkey_from_huskey(hUSKey, FALSE))) + { + return RegQueryInfoKeyW(dokey, 0, 0, 0, subkeys, max_subkey_len, 0, values, max_value_name_len, 0, 0, 0); + } + + return ERROR_INVALID_FUNCTION; +} + +LONG WINAPI SHRegQueryUSValueA(HUSKEY hUSKey, const char *value, DWORD *type, void *data, DWORD *data_len, + BOOL ignore_hkcu, void *default_data, DWORD default_data_len) +{ + LONG ret = ~ERROR_SUCCESS; + DWORD move_len; + HKEY dokey; + + /* If user wants HKCU, and it exists, then try it */ + if (!ignore_hkcu && (dokey = reg_get_hkey_from_huskey(hUSKey, TRUE))) + { + ret = RegQueryValueExA(dokey, value, 0, type, data, data_len); + TRACE("HKCU RegQueryValue returned %ld\n", ret); + } + + /* If HKCU did not work and HKLM exists, then try it */ + if ((ret != ERROR_SUCCESS) && (dokey = reg_get_hkey_from_huskey(hUSKey, FALSE))) + { + ret = RegQueryValueExA(dokey, value, 0, type, data, data_len); + TRACE("HKLM RegQueryValue returned %ld\n", ret); + } + + /* If neither worked, and default data exists, then use it */ + if (ret != ERROR_SUCCESS) + { + if (default_data && default_data_len) + { + move_len = default_data_len >= *data_len ? *data_len : default_data_len; + memmove(data, default_data, move_len); + *data_len = move_len; + TRACE("setting default data\n"); + ret = ERROR_SUCCESS; + } + } + + return ret; +} + +LONG WINAPI SHRegQueryUSValueW(HUSKEY hUSKey, const WCHAR *value, DWORD *type, void *data, DWORD *data_len, + BOOL ignore_hkcu, void *default_data, DWORD default_data_len) +{ + LONG ret = ~ERROR_SUCCESS; + DWORD move_len; + HKEY dokey; + + /* If user wants HKCU, and it exists, then try it */ + if (!ignore_hkcu && (dokey = reg_get_hkey_from_huskey(hUSKey, TRUE))) + { + ret = RegQueryValueExW(dokey, value, 0, type, data, data_len); + TRACE("HKCU RegQueryValue returned %ld\n", ret); + } + + /* If HKCU did not work and HKLM exists, then try it */ + if ((ret != ERROR_SUCCESS) && (dokey = reg_get_hkey_from_huskey(hUSKey, FALSE))) + { + ret = RegQueryValueExW(dokey, value, 0, type, data, data_len); + TRACE("HKLM RegQueryValue returned %ld\n", ret); + } + + /* If neither worked, and default data exists, then use it */ + if (ret != ERROR_SUCCESS) + { + if (default_data && default_data_len) + { + move_len = default_data_len >= *data_len ? *data_len : default_data_len; + memmove(data, default_data, move_len); + *data_len = move_len; + TRACE("setting default data\n"); + ret = ERROR_SUCCESS; + } + } + + return ret; +} + +LONG WINAPI SHRegGetUSValueA(const char *subkey, const char *value, DWORD *type, void *data, DWORD *data_len, + BOOL ignore_hkcu, void *default_data, DWORD default_data_len) +{ + HUSKEY myhuskey; + LONG ret; + + if (!data || !data_len) + return ERROR_INVALID_FUNCTION; /* FIXME:wrong*/ + + TRACE("%s, %s, %ld\n", debugstr_a(subkey), debugstr_a(value), *data_len); + + ret = SHRegOpenUSKeyA(subkey, KEY_QUERY_VALUE, 0, &myhuskey, ignore_hkcu); + if (!ret) + { + ret = SHRegQueryUSValueA(myhuskey, value, type, data, data_len, ignore_hkcu, default_data, default_data_len); + SHRegCloseUSKey(myhuskey); + } + + return ret; +} + +LONG WINAPI SHRegGetUSValueW(const WCHAR *subkey, const WCHAR *value, DWORD *type, void *data, DWORD *data_len, + BOOL ignore_hkcu, void *default_data, DWORD default_data_len) +{ + HUSKEY myhuskey; + LONG ret; + + if (!data || !data_len) + return ERROR_INVALID_FUNCTION; /* FIXME:wrong*/ + + TRACE("%s, %s, %ld\n", debugstr_w(subkey), debugstr_w(value), *data_len); + + ret = SHRegOpenUSKeyW(subkey, KEY_QUERY_VALUE, 0, &myhuskey, ignore_hkcu); + if (!ret) + { + ret = SHRegQueryUSValueW(myhuskey, value, type, data, data_len, ignore_hkcu, default_data, default_data_len); + SHRegCloseUSKey(myhuskey); + } + + return ret; +} + +BOOL WINAPI SHRegGetBoolUSValueA(const char *subkey, const char *value, BOOL ignore_hkcu, BOOL default_value) +{ + BOOL ret = default_value; + DWORD type, datalen; + char data[10]; + + TRACE("%s, %s, %d\n", debugstr_a(subkey), debugstr_a(value), ignore_hkcu); + + datalen = ARRAY_SIZE(data) - 1; + if (!SHRegGetUSValueA(subkey, value, &type, data, &datalen, ignore_hkcu, 0, 0)) + { + switch (type) + { + case REG_SZ: + data[9] = '\0'; + if (!lstrcmpiA(data, "YES") || !lstrcmpiA(data, "TRUE")) + ret = TRUE; + else if (!lstrcmpiA(data, "NO") || !lstrcmpiA(data, "FALSE")) + ret = FALSE; + break; + case REG_DWORD: + ret = *(DWORD *)data != 0; + break; + case REG_BINARY: + if (datalen == 1) + { + ret = !!data[0]; + break; + } + default: + FIXME("Unsupported registry data type %ld\n", type); + ret = FALSE; + } + TRACE("got value (type=%ld), returning %d\n", type, ret); + } + else + TRACE("returning default value %d\n", ret); + + return ret; +} + +BOOL WINAPI SHRegGetBoolUSValueW(const WCHAR *subkey, const WCHAR *value, BOOL ignore_hkcu, BOOL default_value) +{ + BOOL ret = default_value; + DWORD type, datalen; + WCHAR data[10]; + + TRACE("%s, %s, %d\n", debugstr_w(subkey), debugstr_w(value), ignore_hkcu); + + datalen = (ARRAY_SIZE(data) - 1) * sizeof(WCHAR); + if (!SHRegGetUSValueW(subkey, value, &type, data, &datalen, ignore_hkcu, 0, 0)) + { + switch (type) + { + case REG_SZ: + data[9] = '\0'; + if (!lstrcmpiW(data, L"yes") || !lstrcmpiW(data, L"true")) + ret = TRUE; + else if (!lstrcmpiW(data, L"no") || !lstrcmpiW(data, L"false")) + ret = FALSE; + break; + case REG_DWORD: + ret = *(DWORD *)data != 0; + break; + case REG_BINARY: + if (datalen == 1) + { + ret = !!data[0]; + break; + } + default: + FIXME("Unsupported registry data type %ld\n", type); + ret = FALSE; + } + TRACE("got value (type=%ld), returning %d\n", type, ret); + } + else + TRACE("returning default value %d\n", ret); + + return ret; +} diff --git a/dll/win32/KernelBase/wine/security.c b/dll/win32/KernelBase/wine/security.c new file mode 100644 index 0000000000000..1dd975a7aade8 --- /dev/null +++ b/dll/win32/KernelBase/wine/security.c @@ -0,0 +1,1515 @@ +/* + * Copyright 1999, 2000 Juergen Schmied + * Copyright 2003 CodeWeavers Inc. (Ulrich Czekalla) + * Copyright 2006 Robert Reif + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winternl.h" +#include "winioctl.h" +#include "ddk/ntddk.h" + +#include "kernelbase.h" +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(security); + + +/****************************************************************************** + * SID functions + ******************************************************************************/ + +typedef struct _MAX_SID +{ + /* same fields as struct _SID */ + BYTE Revision; + BYTE SubAuthorityCount; + SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + DWORD SubAuthority[SID_MAX_SUB_AUTHORITIES]; +} MAX_SID; + +typedef struct WELLKNOWNSID +{ + WELL_KNOWN_SID_TYPE Type; + MAX_SID Sid; +} WELLKNOWNSID; + +static const WELLKNOWNSID WellKnownSids[] = +{ + { WinNullSid, { SID_REVISION, 1, { SECURITY_NULL_SID_AUTHORITY }, { SECURITY_NULL_RID } } }, + { WinWorldSid, { SID_REVISION, 1, { SECURITY_WORLD_SID_AUTHORITY }, { SECURITY_WORLD_RID } } }, + { WinLocalSid, { SID_REVISION, 1, { SECURITY_LOCAL_SID_AUTHORITY }, { SECURITY_LOCAL_RID } } }, + { WinCreatorOwnerSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_OWNER_RID } } }, + { WinCreatorGroupSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_GROUP_RID } } }, + { WinCreatorOwnerRightsSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_OWNER_RIGHTS_RID } } }, + { WinCreatorOwnerServerSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_OWNER_SERVER_RID } } }, + { WinCreatorGroupServerSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_GROUP_SERVER_RID } } }, + { WinNtAuthoritySid, { SID_REVISION, 0, { SECURITY_NT_AUTHORITY }, { SECURITY_NULL_RID } } }, + { WinDialupSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_DIALUP_RID } } }, + { WinNetworkSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_NETWORK_RID } } }, + { WinBatchSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_BATCH_RID } } }, + { WinInteractiveSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_INTERACTIVE_RID } } }, + { WinServiceSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_SERVICE_RID } } }, + { WinAnonymousSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_ANONYMOUS_LOGON_RID } } }, + { WinProxySid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_PROXY_RID } } }, + { WinEnterpriseControllersSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_ENTERPRISE_CONTROLLERS_RID } } }, + { WinSelfSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_PRINCIPAL_SELF_RID } } }, + { WinAuthenticatedUserSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_AUTHENTICATED_USER_RID } } }, + { WinRestrictedCodeSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_RESTRICTED_CODE_RID } } }, + { WinTerminalServerSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_TERMINAL_SERVER_RID } } }, + { WinRemoteLogonIdSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_REMOTE_LOGON_RID } } }, + { WinLogonIdsSid, { SID_REVISION, SECURITY_LOGON_IDS_RID_COUNT, { SECURITY_NT_AUTHORITY }, { SECURITY_LOGON_IDS_RID } } }, + { WinLocalSystemSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_LOCAL_SYSTEM_RID } } }, + { WinLocalServiceSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_LOCAL_SERVICE_RID } } }, + { WinNetworkServiceSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_NETWORK_SERVICE_RID } } }, + { WinBuiltinDomainSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID } } }, + { WinBuiltinAdministratorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS } } }, + { WinBuiltinUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS } } }, + { WinBuiltinGuestsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS } } }, + { WinBuiltinPowerUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS } } }, + { WinBuiltinAccountOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ACCOUNT_OPS } } }, + { WinBuiltinSystemOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_SYSTEM_OPS } } }, + { WinBuiltinPrintOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PRINT_OPS } } }, + { WinBuiltinBackupOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_BACKUP_OPS } } }, + { WinBuiltinReplicatorSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REPLICATOR } } }, + { WinBuiltinPreWindows2000CompatibleAccessSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PREW2KCOMPACCESS } } }, + { WinBuiltinRemoteDesktopUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS } } }, + { WinBuiltinNetworkConfigurationOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS } } }, + { WinNTLMAuthenticationSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_NTLM_RID } } }, + { WinDigestAuthenticationSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_DIGEST_RID } } }, + { WinSChannelAuthenticationSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_SCHANNEL_RID } } }, + { WinThisOrganizationSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_THIS_ORGANIZATION_RID } } }, + { WinOtherOrganizationSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_OTHER_ORGANIZATION_RID } } }, + { WinBuiltinIncomingForestTrustBuildersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_INCOMING_FOREST_TRUST_BUILDERS } } }, + { WinBuiltinPerfMonitoringUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_MONITORING_USERS } } }, + { WinBuiltinPerfLoggingUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_LOGGING_USERS } } }, + { WinBuiltinAuthorizationAccessSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_AUTHORIZATIONACCESS } } }, + { WinBuiltinTerminalServerLicenseServersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_TS_LICENSE_SERVERS } } }, + { WinBuiltinDCOMUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_DCOM_USERS } } }, + { WinLowLabelSid, { SID_REVISION, 1, { SECURITY_MANDATORY_LABEL_AUTHORITY}, { SECURITY_MANDATORY_LOW_RID} } }, + { WinMediumLabelSid, { SID_REVISION, 1, { SECURITY_MANDATORY_LABEL_AUTHORITY}, { SECURITY_MANDATORY_MEDIUM_RID } } }, + { WinHighLabelSid, { SID_REVISION, 1, { SECURITY_MANDATORY_LABEL_AUTHORITY}, { SECURITY_MANDATORY_HIGH_RID } } }, + { WinSystemLabelSid, { SID_REVISION, 1, { SECURITY_MANDATORY_LABEL_AUTHORITY}, { SECURITY_MANDATORY_SYSTEM_RID } } }, + { WinBuiltinAnyPackageSid, { SID_REVISION, 2, { SECURITY_APP_PACKAGE_AUTHORITY }, { SECURITY_APP_PACKAGE_BASE_RID, SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE } } }, +}; + +/* these SIDs must be constructed as relative to some domain - only the RID is well-known */ +typedef struct WELLKNOWNRID +{ + WELL_KNOWN_SID_TYPE Type; + DWORD Rid; +} WELLKNOWNRID; + +static const WELLKNOWNRID WellKnownRids[] = +{ + { WinAccountAdministratorSid, DOMAIN_USER_RID_ADMIN }, + { WinAccountGuestSid, DOMAIN_USER_RID_GUEST }, + { WinAccountKrbtgtSid, DOMAIN_USER_RID_KRBTGT }, + { WinAccountDomainAdminsSid, DOMAIN_GROUP_RID_ADMINS }, + { WinAccountDomainUsersSid, DOMAIN_GROUP_RID_USERS }, + { WinAccountDomainGuestsSid, DOMAIN_GROUP_RID_GUESTS }, + { WinAccountComputersSid, DOMAIN_GROUP_RID_COMPUTERS }, + { WinAccountControllersSid, DOMAIN_GROUP_RID_CONTROLLERS }, + { WinAccountCertAdminsSid, DOMAIN_GROUP_RID_CERT_ADMINS }, + { WinAccountSchemaAdminsSid, DOMAIN_GROUP_RID_SCHEMA_ADMINS }, + { WinAccountEnterpriseAdminsSid, DOMAIN_GROUP_RID_ENTERPRISE_ADMINS }, + { WinAccountPolicyAdminsSid, DOMAIN_GROUP_RID_POLICY_ADMINS }, + { WinAccountRasAndIasServersSid, DOMAIN_ALIAS_RID_RAS_SERVERS }, +}; + +static NTSTATUS open_file( LPCWSTR name, DWORD access, HANDLE *file ) +{ + UNICODE_STRING file_nameW; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + + if ((status = RtlDosPathNameToNtPathName_U_WithStatus( name, &file_nameW, NULL, NULL ))) return status; + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &file_nameW; + attr.SecurityDescriptor = NULL; + status = NtCreateFile( file, access|SYNCHRONIZE, &attr, &io, NULL, FILE_FLAG_BACKUP_SEMANTICS, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN, + FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0 ); + RtlFreeUnicodeString( &file_nameW ); + return status; +} + +static const char *debugstr_sid( PSID sid ) +{ + int auth; + SID * psid = sid; + + if (psid == NULL) return "(null)"; + + auth = psid->IdentifierAuthority.Value[5] + + (psid->IdentifierAuthority.Value[4] << 8) + + (psid->IdentifierAuthority.Value[3] << 16) + + (psid->IdentifierAuthority.Value[2] << 24); + + switch (psid->SubAuthorityCount) { + case 0: + return wine_dbg_sprintf("S-%d-%d", psid->Revision, auth); + case 1: + return wine_dbg_sprintf("S-%d-%d-%lu", psid->Revision, auth, + psid->SubAuthority[0]); + case 2: + return wine_dbg_sprintf("S-%d-%d-%lu-%lu", psid->Revision, auth, + psid->SubAuthority[0], psid->SubAuthority[1]); + case 3: + return wine_dbg_sprintf("S-%d-%d-%lu-%lu-%lu", psid->Revision, auth, + psid->SubAuthority[0], psid->SubAuthority[1], psid->SubAuthority[2]); + case 4: + return wine_dbg_sprintf("S-%d-%d-%lu-%lu-%lu-%lu", psid->Revision, auth, + psid->SubAuthority[0], psid->SubAuthority[1], psid->SubAuthority[2], + psid->SubAuthority[3]); + case 5: + return wine_dbg_sprintf("S-%d-%d-%lu-%lu-%lu-%lu-%lu", psid->Revision, auth, + psid->SubAuthority[0], psid->SubAuthority[1], psid->SubAuthority[2], + psid->SubAuthority[3], psid->SubAuthority[4]); + case 6: + return wine_dbg_sprintf("S-%d-%d-%lu-%lu-%lu-%lu-%lu-%lu", psid->Revision, auth, + psid->SubAuthority[3], psid->SubAuthority[1], psid->SubAuthority[2], + psid->SubAuthority[0], psid->SubAuthority[4], psid->SubAuthority[5]); + case 7: + return wine_dbg_sprintf("S-%d-%d-%lu-%lu-%lu-%lu-%lu-%lu-%lu", psid->Revision, auth, + psid->SubAuthority[0], psid->SubAuthority[1], psid->SubAuthority[2], + psid->SubAuthority[3], psid->SubAuthority[4], psid->SubAuthority[5], + psid->SubAuthority[6]); + case 8: + return wine_dbg_sprintf("S-%d-%d-%lu-%lu-%lu-%lu-%lu-%lu-%lu-%lu", psid->Revision, auth, + psid->SubAuthority[0], psid->SubAuthority[1], psid->SubAuthority[2], + psid->SubAuthority[3], psid->SubAuthority[4], psid->SubAuthority[5], + psid->SubAuthority[6], psid->SubAuthority[7]); + } + return "(too-big)"; +} + +/****************************************************************************** + * AllocateAndInitializeSid (kernelbase.@) + */ +BOOL WINAPI AllocateAndInitializeSid( PSID_IDENTIFIER_AUTHORITY auth, BYTE count, + DWORD auth0, DWORD auth1, DWORD auth2, DWORD auth3, + DWORD auth4, DWORD auth5, DWORD auth6, DWORD auth7, PSID *sid ) +{ + return set_ntstatus( RtlAllocateAndInitializeSid( auth, count, auth0, auth1, auth2, auth3, + auth4, auth5, auth6, auth7, sid )); +} + +/*********************************************************************** + * AllocateLocallyUniqueId (kernelbase.@) + */ +BOOL WINAPI AllocateLocallyUniqueId( PLUID luid ) +{ + return set_ntstatus( NtAllocateLocallyUniqueId( luid )); +} + +/****************************************************************************** + * CopySid (kernelbase.@) + */ +BOOL WINAPI CopySid( DWORD len, PSID dest, PSID source ) +{ + return RtlCopySid( len, dest, source ); +} + +/****************************************************************************** + * EqualPrefixSid (kernelbase.@) + */ +BOOL WINAPI EqualPrefixSid( PSID sid1, PSID sid2 ) +{ + return RtlEqualPrefixSid( sid1, sid2 ); +} + +/****************************************************************************** + * EqualSid (kernelbase.@) + */ +BOOL WINAPI EqualSid( PSID sid1, PSID sid2 ) +{ + BOOL ret = RtlEqualSid( sid1, sid2 ); + SetLastError(ERROR_SUCCESS); + return ret; +} + +/****************************************************************************** + * EqualDomainSid (kernelbase.@) + */ +BOOL WINAPI EqualDomainSid( PSID sid1, PSID sid2, BOOL *equal ) +{ + MAX_SID builtin_sid, domain_sid1, domain_sid2; + DWORD size; + + TRACE( "(%p,%p,%p)\n", sid1, sid2, equal ); + + if (!IsValidSid( sid1 ) || !IsValidSid( sid2 )) + { + SetLastError( ERROR_INVALID_SID ); + return FALSE; + } + + if (!equal) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + size = sizeof(domain_sid1); + if (GetWindowsAccountDomainSid( sid1, &domain_sid1, &size )) + { + size = sizeof(domain_sid2); + if (GetWindowsAccountDomainSid( sid2, &domain_sid2, &size )) + { + *equal = EqualSid( &domain_sid1, &domain_sid2 ); + SetLastError( 0 ); + return TRUE; + } + } + + size = sizeof(builtin_sid); + if (!CreateWellKnownSid( WinBuiltinDomainSid, NULL, &builtin_sid, &size )) + return FALSE; + + if (!memcmp(GetSidIdentifierAuthority( sid1 )->Value, builtin_sid.IdentifierAuthority.Value, sizeof(builtin_sid.IdentifierAuthority.Value)) && + !memcmp(GetSidIdentifierAuthority( sid2 )->Value, builtin_sid.IdentifierAuthority.Value, sizeof(builtin_sid.IdentifierAuthority.Value))) + { + if (*GetSidSubAuthorityCount( sid1 ) != 0 && *GetSidSubAuthorityCount( sid2 ) != 0 && + (*GetSidSubAuthority( sid1, 0 ) == SECURITY_BUILTIN_DOMAIN_RID || + *GetSidSubAuthority( sid2, 0 ) == SECURITY_BUILTIN_DOMAIN_RID)) + { + *equal = EqualSid( sid1, sid2 ); + SetLastError( 0 ); + return TRUE; + } + } + + SetLastError( ERROR_NON_DOMAIN_SID ); + return FALSE; +} + +/****************************************************************************** + * FreeSid (kernelbase.@) + */ +void * WINAPI FreeSid( PSID pSid ) +{ + RtlFreeSid(pSid); + return NULL; /* is documented like this */ +} + +/****************************************************************************** + * GetLengthSid (kernelbase.@) + */ +DWORD WINAPI GetLengthSid( PSID sid ) +{ + return RtlLengthSid( sid ); +} + +/****************************************************************************** + * GetSidIdentifierAuthority (kernelbase.@) + */ +PSID_IDENTIFIER_AUTHORITY WINAPI GetSidIdentifierAuthority( PSID sid ) +{ + SetLastError(ERROR_SUCCESS); + return RtlIdentifierAuthoritySid( sid ); +} + +/****************************************************************************** + * GetSidLengthRequired (kernelbase.@) + */ +DWORD WINAPI GetSidLengthRequired( BYTE count ) +{ + return RtlLengthRequiredSid( count ); +} + +/****************************************************************************** + * GetSidSubAuthority (kernelbase.@) + */ +PDWORD WINAPI GetSidSubAuthority( PSID sid, DWORD auth ) +{ + SetLastError(ERROR_SUCCESS); + return RtlSubAuthoritySid( sid, auth ); +} + +/****************************************************************************** + * GetSidSubAuthorityCount (kernelbase.@) + */ +PUCHAR WINAPI GetSidSubAuthorityCount( PSID sid ) +{ + SetLastError(ERROR_SUCCESS); + return RtlSubAuthorityCountSid( sid ); +} + +/****************************************************************************** + * GetWindowsAccountDomainSid (kernelbase.@) + */ +BOOL WINAPI GetWindowsAccountDomainSid( PSID sid, PSID domain_sid, DWORD *size ) +{ + SID_IDENTIFIER_AUTHORITY domain_ident = { SECURITY_NT_AUTHORITY }; + DWORD required_size; + int i; + + FIXME( "(%p %p %p): semi-stub\n", sid, domain_sid, size ); + + if (!sid || !IsValidSid( sid )) + { + SetLastError( ERROR_INVALID_SID ); + return FALSE; + } + + if (!size) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (*GetSidSubAuthorityCount( sid ) < 4) + { + SetLastError( ERROR_INVALID_SID ); + return FALSE; + } + + required_size = GetSidLengthRequired( 4 ); + if (*size < required_size || !domain_sid) + { + *size = required_size; + SetLastError( domain_sid ? ERROR_INSUFFICIENT_BUFFER : ERROR_INVALID_PARAMETER ); + return FALSE; + } + + InitializeSid( domain_sid, &domain_ident, 4 ); + for (i = 0; i < 4; i++) + *GetSidSubAuthority( domain_sid, i ) = *GetSidSubAuthority( sid, i ); + + *size = required_size; + return TRUE; +} + +/****************************************************************************** + * InitializeSid (kernelbase.@) + */ +BOOL WINAPI InitializeSid ( PSID sid, PSID_IDENTIFIER_AUTHORITY auth, BYTE count ) +{ + return set_ntstatus(RtlInitializeSid( sid, auth, count )); +} + +/****************************************************************************** + * IsValidSid (kernelbase.@) + */ +BOOL WINAPI IsValidSid( PSID sid ) +{ + return RtlValidSid( sid ); +} + +/****************************************************************************** + * CreateWellKnownSid (kernelbase.@) + */ +BOOL WINAPI CreateWellKnownSid( WELL_KNOWN_SID_TYPE type, PSID domain, PSID sid, DWORD *size ) +{ + unsigned int i; + + TRACE("(%d, %s, %p, %p)\n", type, debugstr_sid(domain), sid, size); + + if (size == NULL || (domain && !IsValidSid(domain))) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + for (i = 0; i < ARRAY_SIZE(WellKnownSids); i++) + { + if (WellKnownSids[i].Type == type) + { + DWORD length = GetSidLengthRequired(WellKnownSids[i].Sid.SubAuthorityCount); + + if (*size < length) + { + *size = length; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + if (!sid) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + CopyMemory(sid, &WellKnownSids[i].Sid.Revision, length); + *size = length; + return TRUE; + } + } + + if (domain == NULL || *GetSidSubAuthorityCount(domain) == SID_MAX_SUB_AUTHORITIES) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + for (i = 0; i < ARRAY_SIZE(WellKnownRids); i++) + { + if (WellKnownRids[i].Type == type) + { + UCHAR domain_subauth = *GetSidSubAuthorityCount(domain); + DWORD domain_sid_length = GetSidLengthRequired(domain_subauth); + DWORD output_sid_length = GetSidLengthRequired(domain_subauth + 1); + + if (*size < output_sid_length) + { + *size = output_sid_length; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + if (!sid) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + CopyMemory(sid, domain, domain_sid_length); + (*GetSidSubAuthorityCount(sid))++; + (*GetSidSubAuthority(sid, domain_subauth)) = WellKnownRids[i].Rid; + *size = output_sid_length; + return TRUE; + } + } + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; +} + +/****************************************************************************** + * IsWellKnownSid (kernelbase.@) + */ +BOOL WINAPI IsWellKnownSid( PSID sid, WELL_KNOWN_SID_TYPE type ) +{ + unsigned int i; + + TRACE("(%s, %d)\n", debugstr_sid(sid), type); + + for (i = 0; i < ARRAY_SIZE(WellKnownSids); i++) + if (WellKnownSids[i].Type == type) + if (EqualSid(sid, (PSID)&WellKnownSids[i].Sid.Revision)) + return TRUE; + + return FALSE; +} + + +/****************************************************************************** + * Token functions + ******************************************************************************/ + + +/****************************************************************************** + * AdjustTokenGroups (kernelbase.@) + */ +BOOL WINAPI AdjustTokenGroups( HANDLE token, BOOL reset, PTOKEN_GROUPS new, + DWORD len, PTOKEN_GROUPS prev, PDWORD ret_len ) +{ + return set_ntstatus( NtAdjustGroupsToken( token, reset, new, len, prev, ret_len )); +} + +/****************************************************************************** + * AdjustTokenPrivileges (kernelbase.@) + */ +BOOL WINAPI AdjustTokenPrivileges( HANDLE token, BOOL disable, PTOKEN_PRIVILEGES new, DWORD len, + PTOKEN_PRIVILEGES prev, PDWORD ret_len ) +{ + NTSTATUS status; + + TRACE("(%p %d %p %ld %p %p)\n", token, disable, new, len, prev, ret_len ); + + status = NtAdjustPrivilegesToken( token, disable, new, len, prev, ret_len ); + SetLastError( RtlNtStatusToDosError( status )); + return (status == STATUS_SUCCESS) || (status == STATUS_NOT_ALL_ASSIGNED); +} + +/****************************************************************************** + * CheckTokenMembership (kernelbase.@) + */ +BOOL WINAPI CheckTokenMembership( HANDLE token, PSID sid_to_check, PBOOL is_member ) +{ + PTOKEN_GROUPS token_groups = NULL; + HANDLE thread_token = NULL; + DWORD size, i; + BOOL ret; + + TRACE("(%p %s %p)\n", token, debugstr_sid(sid_to_check), is_member); + + *is_member = FALSE; + + if (!token) + { + if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &thread_token)) + { + HANDLE process_token; + ret = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &process_token); + if (!ret) + goto exit; + ret = DuplicateTokenEx(process_token, TOKEN_QUERY, NULL, SecurityImpersonation, + TokenImpersonation, &thread_token); + CloseHandle(process_token); + if (!ret) + goto exit; + } + token = thread_token; + } + else + { + TOKEN_TYPE type; + + ret = GetTokenInformation(token, TokenType, &type, sizeof(TOKEN_TYPE), &size); + if (!ret) goto exit; + + if (type == TokenPrimary) + { + SetLastError(ERROR_NO_IMPERSONATION_TOKEN); + return FALSE; + } + } + + ret = GetTokenInformation(token, TokenGroups, NULL, 0, &size); + if (!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto exit; + + token_groups = heap_alloc(size); + if (!token_groups) + { + ret = FALSE; + goto exit; + } + + ret = GetTokenInformation(token, TokenGroups, token_groups, size, &size); + if (!ret) + goto exit; + + for (i = 0; i < token_groups->GroupCount; i++) + { + TRACE("Groups[%ld]: {0x%lx, %s}\n", i, + token_groups->Groups[i].Attributes, + debugstr_sid(token_groups->Groups[i].Sid)); + if ((token_groups->Groups[i].Attributes & SE_GROUP_ENABLED) && + EqualSid(sid_to_check, token_groups->Groups[i].Sid)) + { + *is_member = TRUE; + TRACE("sid enabled and found in token\n"); + break; + } + } + +exit: + heap_free(token_groups); + if (thread_token != NULL) CloseHandle(thread_token); + return ret; +} + +/************************************************************************* + * CreateRestrictedToken (kernelbase.@) + */ +BOOL WINAPI CreateRestrictedToken( HANDLE token, DWORD flags, + DWORD disable_sid_count, SID_AND_ATTRIBUTES *disable_sids, + DWORD delete_priv_count, LUID_AND_ATTRIBUTES *delete_privs, + DWORD restrict_sid_count, SID_AND_ATTRIBUTES *restrict_sids, HANDLE *ret ) +{ + TOKEN_PRIVILEGES *nt_privs = NULL; + TOKEN_GROUPS *nt_disable_sids = NULL, *nt_restrict_sids = NULL; + NTSTATUS status = STATUS_NO_MEMORY; + + TRACE("token %p, flags %#lx, disable_sids %lu %p, delete_privs %lu %p, restrict_sids %lu %p, ret %p\n", + token, flags, disable_sid_count, disable_sids, delete_priv_count, delete_privs, + restrict_sid_count, restrict_sids, ret); + + if (disable_sid_count) + { + if (!(nt_disable_sids = heap_alloc( offsetof( TOKEN_GROUPS, Groups[disable_sid_count] ) ))) goto out; + nt_disable_sids->GroupCount = disable_sid_count; + memcpy( nt_disable_sids->Groups, disable_sids, disable_sid_count * sizeof(*disable_sids) ); + } + + if (delete_priv_count) + { + if (!(nt_privs = heap_alloc( offsetof( TOKEN_PRIVILEGES, Privileges[delete_priv_count] ) ))) goto out; + nt_privs->PrivilegeCount = delete_priv_count; + memcpy( nt_privs->Privileges, delete_privs, delete_priv_count * sizeof(*delete_privs) ); + } + + if (restrict_sid_count) + { + if (!(nt_restrict_sids = heap_alloc( offsetof( TOKEN_GROUPS, Groups[restrict_sid_count] ) ))) goto out; + nt_restrict_sids->GroupCount = restrict_sid_count; + memcpy( nt_restrict_sids->Groups, restrict_sids, restrict_sid_count * sizeof(*restrict_sids) ); + } + + status = NtFilterToken(token, flags, nt_disable_sids, nt_privs, nt_restrict_sids, ret); + +out: + heap_free(nt_disable_sids); + heap_free(nt_privs); + heap_free(nt_restrict_sids); + return set_ntstatus( status ); +} + +/****************************************************************************** + * DuplicateToken (kernelbase.@) + */ +BOOL WINAPI DuplicateToken( HANDLE token, SECURITY_IMPERSONATION_LEVEL level, PHANDLE ret ) +{ + return DuplicateTokenEx( token, TOKEN_IMPERSONATE|TOKEN_QUERY, NULL, level, TokenImpersonation, ret ); +} + +/****************************************************************************** + * DuplicateTokenEx (kernelbase.@) + */ +BOOL WINAPI DuplicateTokenEx( HANDLE token, DWORD access, LPSECURITY_ATTRIBUTES sa, + SECURITY_IMPERSONATION_LEVEL level, TOKEN_TYPE type, PHANDLE ret ) +{ + SECURITY_QUALITY_OF_SERVICE qos; + OBJECT_ATTRIBUTES attr; + + TRACE("%p 0x%08lx 0x%08x 0x%08x %p\n", token, access, level, type, ret ); + + qos.Length = sizeof(qos); + qos.ImpersonationLevel = level; + qos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + qos.EffectiveOnly = FALSE; + InitializeObjectAttributes( &attr, NULL, (sa && sa->bInheritHandle) ? OBJ_INHERIT : 0, + NULL, sa ? sa->lpSecurityDescriptor : NULL ); + attr.SecurityQualityOfService = &qos; + return set_ntstatus( NtDuplicateToken( token, access, &attr, FALSE, type, ret )); +} + +/****************************************************************************** + * GetTokenInformation (kernelbase.@) + */ +BOOL WINAPI GetTokenInformation( HANDLE token, TOKEN_INFORMATION_CLASS class, + LPVOID info, DWORD len, LPDWORD retlen ) +{ + TRACE("(%p, %s, %p, %ld, %p):\n", + token, + (class == TokenUser) ? "TokenUser" : + (class == TokenGroups) ? "TokenGroups" : + (class == TokenPrivileges) ? "TokenPrivileges" : + (class == TokenOwner) ? "TokenOwner" : + (class == TokenPrimaryGroup) ? "TokenPrimaryGroup" : + (class == TokenDefaultDacl) ? "TokenDefaultDacl" : + (class == TokenSource) ? "TokenSource" : + (class == TokenType) ? "TokenType" : + (class == TokenImpersonationLevel) ? "TokenImpersonationLevel" : + (class == TokenStatistics) ? "TokenStatistics" : + (class == TokenRestrictedSids) ? "TokenRestrictedSids" : + (class == TokenSessionId) ? "TokenSessionId" : + (class == TokenGroupsAndPrivileges) ? "TokenGroupsAndPrivileges" : + (class == TokenSessionReference) ? "TokenSessionReference" : + (class == TokenSandBoxInert) ? "TokenSandBoxInert" : + "Unknown", + info, len, retlen); + + return set_ntstatus( NtQueryInformationToken( token, class, info, len, retlen )); +} + +/****************************************************************************** + * ImpersonateAnonymousToken (kernelbase.@) + */ +BOOL WINAPI ImpersonateAnonymousToken( HANDLE thread ) +{ + TRACE("(%p)\n", thread); + return set_ntstatus( NtImpersonateAnonymousToken( thread ) ); +} + +/****************************************************************************** + * ImpersonateLoggedOnUser (kernelbase.@) + */ +BOOL WINAPI ImpersonateLoggedOnUser( HANDLE token ) +{ + DWORD size; + BOOL ret; + HANDLE dup; + TOKEN_TYPE type; + static BOOL warn = TRUE; + + if (warn) + { + FIXME( "(%p)\n", token ); + warn = FALSE; + } + if (!GetTokenInformation( token, TokenType, &type, sizeof(type), &size )) return FALSE; + + if (type == TokenPrimary) + { + if (!DuplicateToken( token, SecurityImpersonation, &dup )) return FALSE; + ret = SetThreadToken( NULL, dup ); + NtClose( dup ); + } + else ret = SetThreadToken( NULL, token ); + + return ret; +} + +/****************************************************************************** + * ImpersonateNamedPipeClient (kernelbase.@) + */ +BOOL WINAPI ImpersonateNamedPipeClient( HANDLE pipe ) +{ + IO_STATUS_BLOCK io_block; + + return set_ntstatus( NtFsControlFile( pipe, NULL, NULL, NULL, &io_block, + FSCTL_PIPE_IMPERSONATE, NULL, 0, NULL, 0 )); +} + +/****************************************************************************** + * ImpersonateSelf (kernelbase.@) + */ +BOOL WINAPI ImpersonateSelf( SECURITY_IMPERSONATION_LEVEL level ) +{ + return set_ntstatus( RtlImpersonateSelf( level ) ); +} + +/****************************************************************************** + * IsTokenRestricted (kernelbase.@) + */ +BOOL WINAPI IsTokenRestricted( HANDLE token ) +{ + TOKEN_GROUPS *groups; + DWORD size; + NTSTATUS status; + BOOL restricted; + + TRACE("(%p)\n", token); + + status = NtQueryInformationToken(token, TokenRestrictedSids, NULL, 0, &size); + if (status != STATUS_BUFFER_TOO_SMALL) return set_ntstatus(status); + + groups = heap_alloc(size); + if (!groups) + { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + status = NtQueryInformationToken(token, TokenRestrictedSids, groups, size, &size); + if (status != STATUS_SUCCESS) + { + heap_free(groups); + return set_ntstatus(status); + } + + restricted = groups->GroupCount > 0; + heap_free(groups); + + return restricted; +} + +/****************************************************************************** + * OpenProcessToken (kernelbase.@) + */ +BOOL WINAPI OpenProcessToken( HANDLE process, DWORD access, HANDLE *handle ) +{ + return set_ntstatus( NtOpenProcessToken( process, access, handle )); +} + +/****************************************************************************** + * OpenThreadToken (kernelbase.@) + */ +BOOL WINAPI OpenThreadToken( HANDLE thread, DWORD access, BOOL self, HANDLE *handle ) +{ + return set_ntstatus( NtOpenThreadToken( thread, access, self, handle )); +} + +/****************************************************************************** + * PrivilegeCheck (kernelbase.@) + */ +BOOL WINAPI PrivilegeCheck( HANDLE token, PPRIVILEGE_SET privs, LPBOOL result ) +{ + BOOLEAN res; + BOOL ret = set_ntstatus( NtPrivilegeCheck( token, privs, &res )); + if (ret) *result = res; + return ret; +} + +/****************************************************************************** + * RevertToSelf (kernelbase.@) + */ +BOOL WINAPI RevertToSelf(void) +{ + return SetThreadToken( NULL, 0 ); +} + +/************************************************************************* + * SetThreadToken (kernelbase.@) + */ +BOOL WINAPI SetThreadToken( PHANDLE thread, HANDLE token ) +{ + return set_ntstatus( NtSetInformationThread( thread ? *thread : GetCurrentThread(), + ThreadImpersonationToken, &token, sizeof(token) )); +} + +/****************************************************************************** + * SetTokenInformation (kernelbase.@) + */ +BOOL WINAPI SetTokenInformation( HANDLE token, TOKEN_INFORMATION_CLASS class, LPVOID info, DWORD len ) +{ + TRACE("(%p, %s, %p, %ld)\n", + token, + (class == TokenUser) ? "TokenUser" : + (class == TokenGroups) ? "TokenGroups" : + (class == TokenPrivileges) ? "TokenPrivileges" : + (class == TokenOwner) ? "TokenOwner" : + (class == TokenPrimaryGroup) ? "TokenPrimaryGroup" : + (class == TokenDefaultDacl) ? "TokenDefaultDacl" : + (class == TokenSource) ? "TokenSource" : + (class == TokenType) ? "TokenType" : + (class == TokenImpersonationLevel) ? "TokenImpersonationLevel" : + (class == TokenStatistics) ? "TokenStatistics" : + (class == TokenRestrictedSids) ? "TokenRestrictedSids" : + (class == TokenSessionId) ? "TokenSessionId" : + (class == TokenGroupsAndPrivileges) ? "TokenGroupsAndPrivileges" : + (class == TokenSessionReference) ? "TokenSessionReference" : + (class == TokenSandBoxInert) ? "TokenSandBoxInert" : + "Unknown", + info, len); + + return set_ntstatus( NtSetInformationToken( token, class, info, len )); +} + + +/****************************************************************************** + * Security descriptor functions + ******************************************************************************/ + + +/****************************************************************************** + * ConvertToAutoInheritPrivateObjectSecurity (kernelbase.@) + */ +BOOL WINAPI ConvertToAutoInheritPrivateObjectSecurity( PSECURITY_DESCRIPTOR parent, + PSECURITY_DESCRIPTOR current, + PSECURITY_DESCRIPTOR *descr, + GUID *type, BOOL is_dir, + PGENERIC_MAPPING mapping ) +{ + return set_ntstatus( RtlConvertToAutoInheritSecurityObject( parent, current, descr, type, is_dir, mapping )); +} + +/****************************************************************************** + * CreateBoundaryDescriptorW (kernelbase.@) + */ +HANDLE WINAPI CreateBoundaryDescriptorW( LPCWSTR name, ULONG flags ) +{ + FIXME("%s %lu - stub\n", debugstr_w(name), flags); + return NULL; +} + +/****************************************************************************** + * CreatePrivateObjectSecurity (kernelbase.@) + */ +BOOL WINAPI CreatePrivateObjectSecurity( PSECURITY_DESCRIPTOR parent, PSECURITY_DESCRIPTOR creator, + PSECURITY_DESCRIPTOR *descr, BOOL is_container, HANDLE token, + PGENERIC_MAPPING mapping ) +{ + return set_ntstatus( RtlNewSecurityObject( parent, creator, descr, is_container, token, mapping )); +} + +/****************************************************************************** + * CreatePrivateObjectSecurityEx (kernelbase.@) + */ +BOOL WINAPI CreatePrivateObjectSecurityEx( PSECURITY_DESCRIPTOR parent, PSECURITY_DESCRIPTOR creator, + PSECURITY_DESCRIPTOR *descr, GUID *type, BOOL is_container, + ULONG flags, HANDLE token, PGENERIC_MAPPING mapping ) +{ + return set_ntstatus( RtlNewSecurityObjectEx( parent, creator, descr, type, is_container, flags, token, mapping )); +} + +/****************************************************************************** + * CreatePrivateObjectSecurityWithMultipleInheritance (kernelbase.@) + */ +BOOL WINAPI CreatePrivateObjectSecurityWithMultipleInheritance( PSECURITY_DESCRIPTOR parent, + PSECURITY_DESCRIPTOR creator, + PSECURITY_DESCRIPTOR *descr, + GUID **types, ULONG count, + BOOL is_container, ULONG flags, + HANDLE token, PGENERIC_MAPPING mapping ) +{ + return set_ntstatus( RtlNewSecurityObjectWithMultipleInheritance( parent, creator, descr, types, count, + is_container, flags, token, mapping )); +} + +/****************************************************************************** + * DestroyPrivateObjectSecurity (kernelbase.@) + */ +BOOL WINAPI DestroyPrivateObjectSecurity( PSECURITY_DESCRIPTOR *descr ) +{ + return set_ntstatus( RtlDeleteSecurityObject( descr )); +} + +/****************************************************************************** + * GetFileSecurityW (kernelbase.@) + */ +BOOL WINAPI GetFileSecurityW( LPCWSTR name, SECURITY_INFORMATION info, + PSECURITY_DESCRIPTOR descr, DWORD len, LPDWORD ret_len ) +{ + HANDLE file; + NTSTATUS status; + DWORD access = 0; + + TRACE( "(%s,%ld,%p,%ld,%p)\n", debugstr_w(name), info, descr, len, ret_len ); + + if (info & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION)) + access |= READ_CONTROL; + if (info & SACL_SECURITY_INFORMATION) + access |= ACCESS_SYSTEM_SECURITY; + + if (!(status = open_file( name, access, &file ))) + { + status = NtQuerySecurityObject( file, info, descr, len, ret_len ); + NtClose( file ); + } + return set_ntstatus( status ); +} + +/****************************************************************************** + * GetKernelObjectSecurity (kernelbase.@) + */ +BOOL WINAPI GetKernelObjectSecurity( HANDLE handle, SECURITY_INFORMATION info, + PSECURITY_DESCRIPTOR descr, DWORD len, LPDWORD ret_len ) +{ + return set_ntstatus( NtQuerySecurityObject( handle, info, descr, len, ret_len )); +} + +/****************************************************************************** + * GetPrivateObjectSecurity (kernelbase.@) + */ +BOOL WINAPI GetPrivateObjectSecurity( PSECURITY_DESCRIPTOR obj_descr, SECURITY_INFORMATION info, + PSECURITY_DESCRIPTOR ret_descr, DWORD len, PDWORD ret_len ) +{ + SECURITY_DESCRIPTOR desc; + BOOL defaulted, present; + PACL pacl; + PSID psid; + + TRACE("(%p,0x%08lx,%p,0x%08lx,%p)\n", obj_descr, info, ret_descr, len, ret_len ); + + if (!InitializeSecurityDescriptor(&desc, SECURITY_DESCRIPTOR_REVISION)) return FALSE; + + if (info & OWNER_SECURITY_INFORMATION) + { + if (!GetSecurityDescriptorOwner(obj_descr, &psid, &defaulted)) return FALSE; + SetSecurityDescriptorOwner(&desc, psid, defaulted); + } + if (info & GROUP_SECURITY_INFORMATION) + { + if (!GetSecurityDescriptorGroup(obj_descr, &psid, &defaulted)) return FALSE; + SetSecurityDescriptorGroup(&desc, psid, defaulted); + } + if (info & DACL_SECURITY_INFORMATION) + { + if (!GetSecurityDescriptorDacl(obj_descr, &present, &pacl, &defaulted)) return FALSE; + SetSecurityDescriptorDacl(&desc, present, pacl, defaulted); + } + if (info & SACL_SECURITY_INFORMATION) + { + if (!GetSecurityDescriptorSacl(obj_descr, &present, &pacl, &defaulted)) return FALSE; + SetSecurityDescriptorSacl(&desc, present, pacl, defaulted); + } + + *ret_len = len; + return MakeSelfRelativeSD(&desc, ret_descr, ret_len); +} + +/****************************************************************************** + * GetSecurityDescriptorControl (kernelbase.@) + */ +BOOL WINAPI GetSecurityDescriptorControl( PSECURITY_DESCRIPTOR descr, PSECURITY_DESCRIPTOR_CONTROL control, + LPDWORD revision) +{ + return set_ntstatus( RtlGetControlSecurityDescriptor( descr, control, revision )); +} + +/****************************************************************************** + * GetSecurityDescriptorDacl (kernelbase.@) + */ +BOOL WINAPI GetSecurityDescriptorDacl( PSECURITY_DESCRIPTOR descr, LPBOOL dacl_present, PACL *dacl, + LPBOOL dacl_defaulted ) +{ + BOOLEAN present, defaulted; + BOOL ret = set_ntstatus( RtlGetDaclSecurityDescriptor( descr, &present, dacl, &defaulted )); + *dacl_present = present; + *dacl_defaulted = defaulted; + return ret; +} + +/****************************************************************************** + * GetSecurityDescriptorGroup (kernelbase.@) + */ +BOOL WINAPI GetSecurityDescriptorGroup( PSECURITY_DESCRIPTOR descr, PSID *group, LPBOOL group_defaulted ) +{ + BOOLEAN defaulted; + BOOL ret = set_ntstatus( RtlGetGroupSecurityDescriptor( descr, group, &defaulted )); + *group_defaulted = defaulted; + return ret; +} + +/****************************************************************************** + * GetSecurityDescriptorLength (kernelbase.@) + */ +DWORD WINAPI GetSecurityDescriptorLength( PSECURITY_DESCRIPTOR descr ) +{ + return RtlLengthSecurityDescriptor( descr ); +} + +/****************************************************************************** + * GetSecurityDescriptorOwner (kernelbase.@) + */ +BOOL WINAPI GetSecurityDescriptorOwner( PSECURITY_DESCRIPTOR descr, PSID *owner, LPBOOL owner_defaulted ) +{ + BOOLEAN defaulted; + BOOL ret = set_ntstatus( RtlGetOwnerSecurityDescriptor( descr, owner, &defaulted )); + *owner_defaulted = defaulted; + return ret; +} + +/****************************************************************************** + * GetSecurityDescriptorSacl (kernelbase.@) + */ +BOOL WINAPI GetSecurityDescriptorSacl( PSECURITY_DESCRIPTOR descr, LPBOOL sacl_present, PACL *sacl, + LPBOOL sacl_defaulted ) +{ + BOOLEAN present, defaulted; + BOOL ret = set_ntstatus( RtlGetSaclSecurityDescriptor( descr, &present, sacl, &defaulted )); + *sacl_present = present; + *sacl_defaulted = defaulted; + return ret; +} + +/****************************************************************************** + * InitializeSecurityDescriptor (kernelbase.@) + */ +BOOL WINAPI InitializeSecurityDescriptor( PSECURITY_DESCRIPTOR descr, DWORD revision ) +{ + return set_ntstatus( RtlCreateSecurityDescriptor( descr, revision )); +} + +/****************************************************************************** + * IsValidSecurityDescriptor (kernelbase.@) + */ +BOOL WINAPI IsValidSecurityDescriptor( PSECURITY_DESCRIPTOR descr ) +{ + if (!RtlValidSecurityDescriptor( descr )) + return set_ntstatus(STATUS_INVALID_SECURITY_DESCR); + + return TRUE; +} + +/****************************************************************************** + * MakeAbsoluteSD (kernelbase.@) + */ +BOOL WINAPI MakeAbsoluteSD ( PSECURITY_DESCRIPTOR rel_descr, PSECURITY_DESCRIPTOR abs_descr, + LPDWORD abs_size, PACL dacl, LPDWORD dacl_size, PACL sacl, LPDWORD sacl_size, + PSID owner, LPDWORD owner_size, PSID group, LPDWORD group_size ) +{ + return set_ntstatus( RtlSelfRelativeToAbsoluteSD( rel_descr, abs_descr, abs_size, + dacl, dacl_size, sacl, sacl_size, + owner, owner_size, group, group_size )); +} + +/****************************************************************************** + * MakeSelfRelativeSD (kernelbase.@) + */ +BOOL WINAPI MakeSelfRelativeSD( PSECURITY_DESCRIPTOR abs_descr, PSECURITY_DESCRIPTOR rel_descr, + LPDWORD len ) +{ + return set_ntstatus( RtlMakeSelfRelativeSD( abs_descr, rel_descr, len )); +} + +/****************************************************************************** + * SetFileSecurityW (kernelbase.@) + */ +BOOL WINAPI SetFileSecurityW( LPCWSTR name, SECURITY_INFORMATION info, PSECURITY_DESCRIPTOR descr ) +{ + HANDLE file; + DWORD access = 0; + NTSTATUS status; + + TRACE( "(%s, 0x%lx, %p)\n", debugstr_w(name), info, descr ); + + if (info & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) access |= WRITE_OWNER; + if (info & SACL_SECURITY_INFORMATION) access |= ACCESS_SYSTEM_SECURITY; + if (info & DACL_SECURITY_INFORMATION) access |= WRITE_DAC; + + if (!(status = open_file( name, access, &file ))) + { + status = NtSetSecurityObject( file, info, descr ); + NtClose( file ); + } + return set_ntstatus( status ); +} + +/************************************************************************* + * SetKernelObjectSecurity (kernelbase.@) + */ +BOOL WINAPI SetKernelObjectSecurity( HANDLE handle, SECURITY_INFORMATION info, PSECURITY_DESCRIPTOR descr ) +{ + return set_ntstatus( NtSetSecurityObject( handle, info, descr )); +} + +/************************************************************************* + * SetPrivateObjectSecurity (kernelbase.@) + */ +BOOL WINAPI SetPrivateObjectSecurity( SECURITY_INFORMATION info, PSECURITY_DESCRIPTOR descr, + PSECURITY_DESCRIPTOR *obj_descr, PGENERIC_MAPPING mapping, + HANDLE token ) +{ + FIXME( "0x%08lx %p %p %p %p - stub\n", info, descr, obj_descr, mapping, token ); + return TRUE; +} + +/************************************************************************* + * SetPrivateObjectSecurityEx (kernelbase.@) + */ +BOOL WINAPI SetPrivateObjectSecurityEx( SECURITY_INFORMATION info, PSECURITY_DESCRIPTOR descr, + PSECURITY_DESCRIPTOR *obj_descr, ULONG flags, + PGENERIC_MAPPING mapping, HANDLE token ) +{ + FIXME( "0x%08lx %p %p %lu %p %p - stub\n", info, descr, obj_descr, flags, mapping, token ); + return TRUE; +} + +/****************************************************************************** + * SetSecurityDescriptorControl (kernelbase.@) + */ +BOOL WINAPI SetSecurityDescriptorControl( PSECURITY_DESCRIPTOR descr, SECURITY_DESCRIPTOR_CONTROL mask, + SECURITY_DESCRIPTOR_CONTROL set ) +{ + return set_ntstatus( RtlSetControlSecurityDescriptor( descr, mask, set )); +} + +/****************************************************************************** + * SetSecurityDescriptorDacl (kernelbase.@) + */ +BOOL WINAPI SetSecurityDescriptorDacl( PSECURITY_DESCRIPTOR descr, BOOL present, PACL dacl, BOOL defaulted ) +{ + return set_ntstatus( RtlSetDaclSecurityDescriptor( descr, present, dacl, defaulted )); +} + +/****************************************************************************** + * SetSecurityDescriptorGroup (kernelbase.@) + */ +BOOL WINAPI SetSecurityDescriptorGroup( PSECURITY_DESCRIPTOR descr, PSID group, BOOL defaulted ) +{ + return set_ntstatus( RtlSetGroupSecurityDescriptor( descr, group, defaulted )); +} + +/****************************************************************************** + * SetSecurityDescriptorOwner (kernelbase.@) + */ +BOOL WINAPI SetSecurityDescriptorOwner( PSECURITY_DESCRIPTOR descr, PSID owner, BOOL defaulted ) +{ + return set_ntstatus( RtlSetOwnerSecurityDescriptor( descr, owner, defaulted )); +} + +/************************************************************************** + * SetSecurityDescriptorSacl (kernelbase.@) + */ +BOOL WINAPI SetSecurityDescriptorSacl ( PSECURITY_DESCRIPTOR descr, BOOL present, PACL sacl, BOOL defaulted ) +{ + return set_ntstatus( RtlSetSaclSecurityDescriptor( descr, present, sacl, defaulted )); +} + + +/****************************************************************************** + * Access control functions + ******************************************************************************/ + + +/****************************************************************************** + * AccessCheck (kernelbase.@) + */ +BOOL WINAPI AccessCheck( PSECURITY_DESCRIPTOR descr, HANDLE token, DWORD access, PGENERIC_MAPPING mapping, + PPRIVILEGE_SET priv, LPDWORD priv_len, LPDWORD granted, LPBOOL status ) +{ + NTSTATUS access_status; + BOOL ret = set_ntstatus( NtAccessCheck( descr, token, access, mapping, priv, priv_len, + granted, &access_status )); + if (ret) *status = set_ntstatus( access_status ); + return ret; +} + +/****************************************************************************** + * AccessCheckAndAuditAlarmW (kernelbase.@) + */ +BOOL WINAPI AccessCheckAndAuditAlarmW( LPCWSTR subsystem, LPVOID id, LPWSTR type_name, + LPWSTR name, PSECURITY_DESCRIPTOR descr, DWORD access, + PGENERIC_MAPPING mapping, BOOL creation, + LPDWORD granted, LPBOOL status, LPBOOL on_close ) +{ + FIXME( "stub (%s,%p,%s,%s,%p,%08lx,%p,%x,%p,%p,%p)\n", debugstr_w(subsystem), + id, debugstr_w(type_name), debugstr_w(name), descr, access, mapping, + creation, granted, status, on_close ); + return TRUE; +} + +/****************************************************************************** + * AccessCheckByType (kernelbase.@) + */ +BOOL WINAPI AccessCheckByType( PSECURITY_DESCRIPTOR descr, PSID sid, HANDLE token, DWORD access, + POBJECT_TYPE_LIST types, DWORD types_len, PGENERIC_MAPPING mapping, + PPRIVILEGE_SET priv, LPDWORD priv_len, LPDWORD granted, LPBOOL status ) +{ + FIXME("stub\n"); + *status = TRUE; + return !*status; +} + +/****************************************************************************** + * AddAccessAllowedAce (kernelbase.@) + */ +BOOL WINAPI AddAccessAllowedAce( PACL acl, DWORD rev, DWORD access, PSID sid ) +{ + return set_ntstatus( RtlAddAccessAllowedAce( acl, rev, access, sid )); +} + +/****************************************************************************** + * AddAccessAllowedAceEx (kernelbase.@) + */ +BOOL WINAPI AddAccessAllowedAceEx( PACL acl, DWORD rev, DWORD flags, DWORD access, PSID sid ) +{ + return set_ntstatus( RtlAddAccessAllowedAceEx( acl, rev, flags, access, sid )); +} + +/****************************************************************************** + * AddAccessAllowedObjectAce (kernelbase.@) + */ +BOOL WINAPI AddAccessAllowedObjectAce( PACL acl, DWORD rev, DWORD flags, DWORD access, + GUID *type, GUID *inherit, PSID sid ) +{ + return set_ntstatus( RtlAddAccessAllowedObjectAce( acl, rev, flags, access, type, inherit, sid )); +} + +/****************************************************************************** + * AddAccessDeniedAce (kernelbase.@) + */ +BOOL WINAPI AddAccessDeniedAce( PACL acl, DWORD rev, DWORD access, PSID sid ) +{ + return set_ntstatus( RtlAddAccessDeniedAce( acl, rev, access, sid )); +} + +/****************************************************************************** + * AddAccessDeniedAceEx (kernelbase.@) + */ +BOOL WINAPI AddAccessDeniedAceEx( PACL acl, DWORD rev, DWORD flags, DWORD access, PSID sid ) +{ + return set_ntstatus( RtlAddAccessDeniedAceEx( acl, rev, flags, access, sid )); +} + +/****************************************************************************** + * AddAccessDeniedObjectAce (kernelbase.@) + */ +BOOL WINAPI AddAccessDeniedObjectAce( PACL acl, DWORD rev, DWORD flags, DWORD access, + GUID *type, GUID *inherit, PSID sid ) +{ + return set_ntstatus( RtlAddAccessDeniedObjectAce( acl, rev, flags, access, type, inherit, sid )); +} + +/****************************************************************************** + * AddAce (kernelbase.@) + */ +BOOL WINAPI AddAce( PACL acl, DWORD rev, DWORD index, LPVOID list, DWORD len ) +{ + return set_ntstatus( RtlAddAce( acl, rev, index, list, len )); +} + +/****************************************************************************** + * AddAuditAccessAce (kernelbase.@) + */ +BOOL WINAPI AddAuditAccessAce( PACL acl, DWORD rev, DWORD access, PSID sid, BOOL success, BOOL failure ) +{ + return set_ntstatus( RtlAddAuditAccessAce( acl, rev, access, sid, success, failure )); +} + +/****************************************************************************** + * AddAuditAccessAceEx (kernelbase.@) + */ +BOOL WINAPI AddAuditAccessAceEx( PACL acl, DWORD rev, DWORD flags, DWORD access, + PSID sid, BOOL success, BOOL failure ) +{ + return set_ntstatus( RtlAddAuditAccessAceEx( acl, rev, flags, access, sid, success, failure )); +} + +/****************************************************************************** + * AddAuditAccessObjectAce (kernelbase.@) + */ +BOOL WINAPI AddAuditAccessObjectAce( PACL acl, DWORD rev, DWORD flags, DWORD access, + GUID *type, GUID *inherit, PSID sid, BOOL success, BOOL failure ) +{ + return set_ntstatus( RtlAddAuditAccessObjectAce( acl, rev, flags, access, + type, inherit, sid, success, failure )); +} + +/****************************************************************************** + * AddMandatoryAce (kernelbase.@) + */ +BOOL WINAPI AddMandatoryAce( PACL acl, DWORD rev, DWORD flags, DWORD policy, PSID sid ) +{ + return set_ntstatus( RtlAddMandatoryAce( acl, rev, flags, policy, + SYSTEM_MANDATORY_LABEL_ACE_TYPE, sid )); +} + +/****************************************************************************** + * AreAllAccessesGranted (kernelbase.@) + */ +BOOL WINAPI AreAllAccessesGranted( DWORD granted, DWORD desired ) +{ + return RtlAreAllAccessesGranted( granted, desired ); +} + +/****************************************************************************** + * AreAnyAccessesGranted (kernelbase.@) + */ +BOOL WINAPI AreAnyAccessesGranted( DWORD granted, DWORD desired ) +{ + return RtlAreAnyAccessesGranted( granted, desired ); +} + +/****************************************************************************** + * DeleteAce (kernelbase.@) + */ +BOOL WINAPI DeleteAce( PACL acl, DWORD index ) +{ + return set_ntstatus( RtlDeleteAce( acl, index )); +} + +/****************************************************************************** + * FindFirstFreeAce (kernelbase.@) + */ +BOOL WINAPI FindFirstFreeAce( PACL acl, LPVOID *ace) +{ + return RtlFirstFreeAce( acl, (PACE_HEADER *)ace ); +} + +/****************************************************************************** + * GetAce (kernelbase.@) + */ +BOOL WINAPI GetAce( PACL acl, DWORD index, LPVOID *ace ) +{ + return set_ntstatus( RtlGetAce( acl, index, ace )); +} + +/****************************************************************************** + * GetAclInformation (kernelbase.@) + */ +BOOL WINAPI GetAclInformation( PACL acl, LPVOID info, DWORD len, ACL_INFORMATION_CLASS class ) +{ + return set_ntstatus( RtlQueryInformationAcl( acl, info, len, class )); +} + +/************************************************************************* + * InitializeAcl (kernelbase.@) + */ +BOOL WINAPI InitializeAcl( PACL acl, DWORD size, DWORD rev ) +{ + return set_ntstatus( RtlCreateAcl( acl, size, rev )); +} + +/****************************************************************************** + * IsValidAcl (kernelbase.@) + */ +BOOL WINAPI IsValidAcl( PACL acl ) +{ + return RtlValidAcl( acl ); +} + +/****************************************************************************** + * MapGenericMask (kernelbase.@) + */ +void WINAPI MapGenericMask( PDWORD access, PGENERIC_MAPPING mapping ) +{ + RtlMapGenericMask( access, mapping ); +} + +/****************************************************************************** + * ObjectCloseAuditAlarmW (kernelbase.@) + */ +BOOL WINAPI ObjectCloseAuditAlarmW( LPCWSTR subsystem, LPVOID id, BOOL on_close ) +{ + FIXME( "stub (%s,%p,%x)\n", debugstr_w(subsystem), id, on_close ); + return TRUE; +} + +/****************************************************************************** + * ObjectDeleteAuditAlarmW (kernelbase.@) + */ +BOOL WINAPI ObjectDeleteAuditAlarmW( LPCWSTR subsystem, LPVOID id, BOOL on_close ) +{ + FIXME( "stub (%s,%p,%x)\n", debugstr_w(subsystem), id, on_close ); + return TRUE; +} + +/****************************************************************************** + * ObjectOpenAuditAlarmW (kernelbase.@) + */ +BOOL WINAPI ObjectOpenAuditAlarmW( LPCWSTR subsystem, LPVOID id, LPWSTR type, LPWSTR name, + PSECURITY_DESCRIPTOR descr, HANDLE token, DWORD desired, + DWORD granted, PPRIVILEGE_SET privs, BOOL creation, + BOOL access, LPBOOL on_close ) +{ + FIXME( "stub (%s,%p,%s,%s,%p,%p,0x%08lx,0x%08lx,%p,%x,%x,%p)\n", debugstr_w(subsystem), + id, debugstr_w(type), debugstr_w(name), descr, token, desired, granted, + privs, creation, access, on_close ); + return TRUE; +} + +/****************************************************************************** + * ObjectPrivilegeAuditAlarmW (kernelbase.@) + */ +BOOL WINAPI ObjectPrivilegeAuditAlarmW( LPCWSTR subsystem, LPVOID id, HANDLE token, + DWORD desired, PPRIVILEGE_SET privs, BOOL granted ) +{ + FIXME( "stub (%s,%p,%p,0x%08lx,%p,%x)\n", debugstr_w(subsystem), id, token, desired, privs, granted ); + return TRUE; +} + +/****************************************************************************** + * PrivilegedServiceAuditAlarmW (kernelbase.@) + */ +BOOL WINAPI PrivilegedServiceAuditAlarmW( LPCWSTR subsystem, LPCWSTR service, HANDLE token, + PPRIVILEGE_SET privs, BOOL granted ) +{ + FIXME( "stub %s,%s,%p,%p,%x)\n", debugstr_w(subsystem), debugstr_w(service), token, privs, granted ); + return TRUE; +} + +/****************************************************************************** + * SetAclInformation (kernelbase.@) + */ +BOOL WINAPI SetAclInformation( PACL acl, LPVOID info, DWORD len, ACL_INFORMATION_CLASS class ) +{ + FIXME( "%p %p 0x%08lx 0x%08x - stub\n", acl, info, len, class ); + return TRUE; +} + +/****************************************************************************** + * SetCachedSigningLevel (kernelbase.@) + */ +BOOL WINAPI SetCachedSigningLevel( PHANDLE source, ULONG count, ULONG flags, HANDLE file ) +{ + FIXME( "%p %lu %lu %p - stub\n", source, count, flags, file ); + return TRUE; +} diff --git a/dll/win32/KernelBase/wine/string.c b/dll/win32/KernelBase/wine/string.c new file mode 100644 index 0000000000000..f83ae35f19a74 --- /dev/null +++ b/dll/win32/KernelBase/wine/string.c @@ -0,0 +1,1501 @@ +/* + * Copyright 2019 Nikolay Sivov for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "shlwapi.h" +#include "winternl.h" + +#include "kernelbase.h" +#include "wine/debug.h" +#include "wine/exception.h" + +WINE_DEFAULT_DEBUG_CHANNEL(string); + +static BOOL char_compare(WORD ch1, WORD ch2, DWORD flags) +{ + char str1[3], str2[3]; + + str1[0] = LOBYTE(ch1); + if (IsDBCSLeadByte(str1[0])) + { + str1[1] = HIBYTE(ch1); + str1[2] = '\0'; + } + else + str1[1] = '\0'; + + str2[0] = LOBYTE(ch2); + if (IsDBCSLeadByte(str2[0])) + { + str2[1] = HIBYTE(ch2); + str2[2] = '\0'; + } + else + str2[1] = '\0'; + + return CompareStringA(GetThreadLocale(), flags, str1, -1, str2, -1) - CSTR_EQUAL; +} + +int WINAPI lstrcmpA( LPCSTR str1, LPCSTR str2 ) +{ + if (!str1 && !str2) return 0; + if (!str1) return -1; + if (!str2) return 1; + return CompareStringA( GetThreadLocale(), LOCALE_USE_CP_ACP, str1, -1, str2, -1 ) - 2; +} + +int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2) +{ + if (!str1 && !str2) return 0; + if (!str1) return -1; + if (!str2) return 1; + return CompareStringW( GetThreadLocale(), 0, str1, -1, str2, -1 ) - 2; +} + +int WINAPI lstrcmpiA(LPCSTR str1, LPCSTR str2) +{ + if (!str1 && !str2) return 0; + if (!str1) return -1; + if (!str2) return 1; + return CompareStringA( GetThreadLocale(), NORM_IGNORECASE|LOCALE_USE_CP_ACP, str1, -1, str2, -1 ) - 2; +} + +int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2) +{ + if (!str1 && !str2) return 0; + if (!str1) return -1; + if (!str2) return 1; + return CompareStringW( GetThreadLocale(), NORM_IGNORECASE, str1, -1, str2, -1 ) - 2; +} + +LPSTR WINAPI KERNELBASE_lstrcpynA( LPSTR dst, LPCSTR src, INT n ) +{ + /* Note: this function differs from the UNIX strncpy, it _always_ writes + * a terminating \0. + * + * Note: n is an INT but Windows treats it as unsigned, and will happily + * copy a gazillion chars if n is negative. + */ + __TRY + { + LPSTR d = dst; + LPCSTR s = src; + UINT count = n; + + while ((count > 1) && *s) + { + count--; + *d++ = *s++; + } + if (count) *d = 0; + } + __EXCEPT_PAGE_FAULT + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + __ENDTRY + return dst; +} + +LPWSTR WINAPI KERNELBASE_lstrcpynW( LPWSTR dst, LPCWSTR src, INT n ) +{ + /* Note: this function differs from the UNIX strncpy, it _always_ writes + * a terminating \0 + * + * Note: n is an INT but Windows treats it as unsigned, and will happily + * copy a gazillion chars if n is negative. + */ + __TRY + { + LPWSTR d = dst; + LPCWSTR s = src; + UINT count = n; + + while ((count > 1) && *s) + { + count--; + *d++ = *s++; + } + if (count) *d = 0; + } + __EXCEPT_PAGE_FAULT + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + __ENDTRY + return dst; +} + +INT WINAPI KERNELBASE_lstrlenA( LPCSTR str ) +{ + INT ret; + __TRY + { + ret = strlen(str); + } + __EXCEPT_PAGE_FAULT + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + __ENDTRY + return ret; +} + +INT WINAPI KERNELBASE_lstrlenW( LPCWSTR str ) +{ + INT ret; + __TRY + { + ret = wcslen(str); + } + __EXCEPT_PAGE_FAULT + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + __ENDTRY + return ret; +} + +DWORD WINAPI StrCmpCA(const char *str, const char *cmp) +{ + return lstrcmpA(str, cmp); +} + +DWORD WINAPI StrCmpCW(const WCHAR *str, const WCHAR *cmp) +{ + return lstrcmpW(str, cmp); +} + +DWORD WINAPI StrCmpICA(const char *str, const char *cmp) +{ + return lstrcmpiA(str, cmp); +} + +DWORD WINAPI StrCmpICW(const WCHAR *str, const WCHAR *cmp) +{ + return lstrcmpiW(str, cmp); +} + +DWORD WINAPI StrCmpNICA(const char *str, const char *cmp, DWORD len) +{ + return StrCmpNIA(str, cmp, len); +} + +DWORD WINAPI StrCmpNICW(const WCHAR *str, const WCHAR *cmp, DWORD len) +{ + return StrCmpNIW(str, cmp, len); +} + +char * WINAPI StrChrA(const char *str, WORD ch) +{ + TRACE("%s, %#x\n", wine_dbgstr_a(str), ch); + + if (!str) + return NULL; + + while (*str) + { + if (!char_compare(*str, ch, 0)) + return (char *)str; + str = CharNextA(str); + } + + return NULL; +} + +WCHAR * WINAPI StrChrW(const WCHAR *str, WCHAR ch) +{ + TRACE("%s, %#x\n", wine_dbgstr_w(str), ch); + + if (!str) + return NULL; + + return wcschr(str, ch); +} + +char * WINAPI StrChrIA(const char *str, WORD ch) +{ + TRACE("%s, %i\n", wine_dbgstr_a(str), ch); + + if (!str) + return NULL; + + while (*str) + { + if (!ChrCmpIA(*str, ch)) + return (char *)str; + str = CharNextA(str); + } + + return NULL; +} + +WCHAR * WINAPI StrChrIW(const WCHAR *str, WCHAR ch) +{ + TRACE("%s, %#x\n", wine_dbgstr_w(str), ch); + + if (!str) + return NULL; + + ch = towupper(ch); + while (*str) + { + if (towupper(*str) == ch) + return (WCHAR *)str; + str++; + } + str = NULL; + + return (WCHAR *)str; +} + +WCHAR * WINAPI StrChrNW(const WCHAR *str, WCHAR ch, UINT max_len) +{ + TRACE("%s, %#x, %u\n", wine_dbgstr_wn(str, max_len), ch, max_len); + + if (!str) + return NULL; + + while (*str && max_len-- > 0) + { + if (*str == ch) + return (WCHAR *)str; + str++; + } + + return NULL; +} + +char * WINAPI StrDupA(const char *str) +{ + unsigned int len; + char *ret; + + TRACE("%s\n", wine_dbgstr_a(str)); + + len = str ? strlen(str) + 1 : 1; + ret = LocalAlloc(LMEM_FIXED, len); + + if (ret) + { + if (str) + memcpy(ret, str, len); + else + *ret = '\0'; + } + + return ret; +} + +WCHAR * WINAPI StrDupW(const WCHAR *str) +{ + unsigned int len; + WCHAR *ret; + + TRACE("%s\n", wine_dbgstr_w(str)); + + len = (str ? lstrlenW(str) + 1 : 1) * sizeof(WCHAR); + ret = LocalAlloc(LMEM_FIXED, len); + + if (ret) + { + if (str) + memcpy(ret, str, len); + else + *ret = '\0'; + } + + return ret; +} + +BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2) +{ + TRACE("%#x, %#x\n", ch1, ch2); + + return char_compare(ch1, ch2, NORM_IGNORECASE); +} + +BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2) +{ + return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, &ch1, 1, &ch2, 1) - CSTR_EQUAL; +} + +char * WINAPI StrStrA(const char *str, const char *search) +{ + const char *end; + size_t len; + + TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(search)); + + if (!str || !search || !*search) return NULL; + + len = strlen(search); + end = str + strlen(str); + + while (str + len <= end) + { + if (!StrCmpNA(str, search, len)) return (char *)str; + str = CharNextA(str); + } + return NULL; +} + +WCHAR * WINAPI StrStrW(const WCHAR *str, const WCHAR *search) +{ + TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(search)); + + if (!str || !search || !*search) + return NULL; + + return wcsstr(str, search); +} + +WCHAR * WINAPI StrStrNW(const WCHAR *str, const WCHAR *search, UINT max_len) +{ + unsigned int i, len; + + TRACE("%s, %s, %u\n", wine_dbgstr_w(str), wine_dbgstr_w(search), max_len); + + if (!str || !search || !*search || !max_len) + return NULL; + + len = lstrlenW(search); + + for (i = max_len; *str && (i > 0); i--, str++) + { + if (!wcsncmp(str, search, len)) + return (WCHAR *)str; + } + + return NULL; +} + +int WINAPI StrCmpNIA(const char *str, const char *cmp, int len) +{ + TRACE("%s, %s, %i\n", wine_dbgstr_a(str), wine_dbgstr_a(cmp), len); + return CompareStringA(GetThreadLocale(), NORM_IGNORECASE, str, len, cmp, len) - CSTR_EQUAL; +} + +WCHAR * WINAPI StrStrNIW(const WCHAR *str, const WCHAR *search, UINT max_len) +{ + unsigned int i, len; + + TRACE("%s, %s, %u\n", wine_dbgstr_w(str), wine_dbgstr_w(search), max_len); + + if (!str || !search || !*search || !max_len) + return NULL; + + len = lstrlenW(search); + + for (i = max_len; *str && (i > 0); i--, str++) + { + if (!StrCmpNIW(str, search, len)) + return (WCHAR *)str; + } + + return NULL; +} + +int WINAPI StrCmpNA(const char *str, const char *comp, int len) +{ + TRACE("%s, %s, %i\n", wine_dbgstr_a(str), wine_dbgstr_a(comp), len); + return CompareStringA(GetThreadLocale(), 0, str, len, comp, len) - CSTR_EQUAL; +} + +int WINAPI StrCmpNW(const WCHAR *str, const WCHAR *comp, int len) +{ + TRACE("%s, %s, %i\n", wine_dbgstr_w(str), wine_dbgstr_w(comp), len); + return CompareStringW(GetThreadLocale(), 0, str, len, comp, len) - CSTR_EQUAL; +} + +DWORD WINAPI StrCmpNCA(const char *str, const char *comp, int len) +{ + return StrCmpNA(str, comp, len); +} + +DWORD WINAPI StrCmpNCW(const WCHAR *str, const WCHAR *comp, int len) +{ + return StrCmpNW(str, comp, len); +} + +int WINAPI StrCmpNIW(const WCHAR *str, const WCHAR *comp, int len) +{ + TRACE("%s, %s, %i\n", wine_dbgstr_w(str), wine_dbgstr_w(comp), len); + return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str, len, comp, len) - CSTR_EQUAL; +} + +int WINAPI StrCmpW(const WCHAR *str, const WCHAR *comp) +{ + TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(comp)); + return CompareStringW(GetThreadLocale(), 0, str, -1, comp, -1) - CSTR_EQUAL; +} + +int WINAPI StrCmpIW(const WCHAR *str, const WCHAR *comp) +{ + TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(comp)); + return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str, -1, comp, -1) - CSTR_EQUAL; +} + +WCHAR * WINAPI StrCpyNW(WCHAR *dst, const WCHAR *src, int count) +{ + const WCHAR *s = src; + WCHAR *d = dst; + + TRACE("%p, %s, %i\n", dst, wine_dbgstr_w(src), count); + + if (s) + { + while ((count > 1) && *s) + { + count--; + *d++ = *s++; + } + } + if (count) *d = 0; + + return dst; +} + +char * WINAPI StrStrIA(const char *str, const char *search) +{ + const char *end; + size_t len; + + TRACE("%s, %s\n", wine_dbgstr_a(str), debugstr_a(search)); + + if (!str || !search || !*search) return NULL; + + len = strlen(search); + end = str + strlen(str); + + while (str + len <= end) + { + if (!StrCmpNIA(str, search, len)) return (char *)str; + str = CharNextA(str); + } + return NULL; +} + +WCHAR * WINAPI StrStrIW(const WCHAR *str, const WCHAR *search) +{ + unsigned int len; + const WCHAR *end; + + TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(search)); + + if (!str || !search || !*search) + return NULL; + + len = lstrlenW(search); + end = str + lstrlenW(str); + + while (str + len <= end) + { + if (!StrCmpNIW(str, search, len)) + return (WCHAR *)str; + str++; + } + + return NULL; +} + +int WINAPI StrSpnA(const char *str, const char *match) +{ + const char *ptr = str; + + TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(match)); + + if (!str || !match) return 0; + + while (*ptr) + { + if (!StrChrA(match, *ptr)) break; + ptr = CharNextA(ptr); + } + return ptr - str; +} + +int WINAPI StrSpnW(const WCHAR *str, const WCHAR *match) +{ + if (!str || !match) return 0; + return wcsspn(str, match); +} + +int WINAPI StrCSpnA(const char *str, const char *match) +{ + const char *ptr = str; + + TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(match)); + + if (!str || !match) return 0; + + while (*ptr) + { + if (StrChrA(match, *ptr)) break; + ptr = CharNextA(ptr); + } + return ptr - str; +} + +int WINAPI StrCSpnW(const WCHAR *str, const WCHAR *match) +{ + if (!str || !match) + return 0; + + return wcscspn(str, match); +} + +int WINAPI StrCSpnIA(const char *str, const char *match) +{ + const char *ptr = str; + + TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(match)); + + if (!str || !match) return 0; + + while (*ptr) + { + if (StrChrIA(match, *ptr)) break; + ptr = CharNextA(ptr); + } + return ptr - str; +} + +int WINAPI StrCSpnIW(const WCHAR *str, const WCHAR *match) +{ + const WCHAR *ptr = str; + + TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(match)); + + if (!str || !*str || !match) + return 0; + + while (*ptr) + { + if (StrChrIW(match, *ptr)) break; + ptr++; + } + + return ptr - str; +} + +char * WINAPI StrRChrA(const char *str, const char *end, WORD ch) +{ + const char *ret = NULL; + + TRACE("%s, %s, %#x\n", wine_dbgstr_a(str), wine_dbgstr_a(end), ch); + + if (!str) return NULL; + if (!end) end = str + lstrlenA(str); + while (*str && str <= end) + { + WORD ch2 = IsDBCSLeadByte(*str) ? *str << 8 | str[1] : *str; + if (!char_compare(ch, ch2, 0)) ret = str; + str = CharNextA(str); + } + return (char *)ret; +} + +WCHAR * WINAPI StrRChrW(const WCHAR *str, const WCHAR *end, WORD ch) +{ + WCHAR *ret = NULL; + + if (!str) return NULL; + if (!end) end = str + lstrlenW(str); + while (str < end) + { + if (*str == ch) ret = (WCHAR *)str; + str++; + } + return ret; +} + +char * WINAPI StrRChrIA(const char *str, const char *end, WORD ch) +{ + const char *ret = NULL; + + TRACE("%s, %s, %#x\n", wine_dbgstr_a(str), wine_dbgstr_a(end), ch); + + if (!str) return NULL; + if (!end) end = str + lstrlenA(str); + + while (*str && str <= end) + { + WORD ch2 = IsDBCSLeadByte(*str) ? *str << 8 | str[1] : *str; + if (!ChrCmpIA(ch, ch2)) ret = str; + str = CharNextA(str); + } + return (char *)ret; +} + +WCHAR * WINAPI StrRChrIW(const WCHAR *str, const WCHAR *end, WORD ch) +{ + WCHAR *ret = NULL; + + if (!str) return NULL; + if (!end) end = str + lstrlenW(str); + while (str < end) + { + if (!ChrCmpIW(*str, ch)) ret = (WCHAR *)str; + str++; + } + return ret; +} + +char * WINAPI StrRStrIA(const char *str, const char *end, const char *search) +{ + char *ret = NULL; + WORD ch1, ch2; + int len; + + TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(search)); + + if (!str || !search || !*search) + return NULL; + + if (IsDBCSLeadByte(*search)) + ch1 = *search << 8 | (UCHAR)search[1]; + else + ch1 = *search; + len = lstrlenA(search); + + if (!end) + end = str + lstrlenA(str); + else /* reproduce the broken behaviour on Windows */ + end += min(len - 1, lstrlenA(end)); + + while (str + len <= end && *str) + { + ch2 = IsDBCSLeadByte(*str) ? *str << 8 | (UCHAR)str[1] : *str; + if (!ChrCmpIA(ch1, ch2)) + { + if (!StrCmpNIA(str, search, len)) + ret = (char *)str; + } + + str = CharNextA(str); + } + + return ret; +} + +WCHAR * WINAPI StrRStrIW(const WCHAR *str, const WCHAR *end, const WCHAR *search) +{ + WCHAR *ret = NULL; + int len; + + TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(search)); + + if (!str || !search || !*search) + return NULL; + + len = lstrlenW(search); + + if (!end) + end = str + lstrlenW(str); + else + end += min(len - 1, lstrlenW(end)); + + while (str + len <= end && *str) + { + if (!ChrCmpIW(*search, *str)) + { + if (!StrCmpNIW(str, search, len)) + ret = (WCHAR *)str; + } + str++; + } + + return ret; +} + +char * WINAPI StrPBrkA(const char *str, const char *match) +{ + TRACE("%s, %s\n", wine_dbgstr_a(str), wine_dbgstr_a(match)); + + if (!str || !match || !*match) + return NULL; + + while (*str) + { + if (StrChrA(match, *str)) + return (char *)str; + str = CharNextA(str); + } + + return NULL; +} + +WCHAR * WINAPI StrPBrkW(const WCHAR *str, const WCHAR *match) +{ + if (!str || !match) return NULL; + return wcspbrk(str, match); +} + +BOOL WINAPI StrTrimA(char *str, const char *trim) +{ + unsigned int len; + BOOL ret = FALSE; + char *ptr = str; + + TRACE("%s, %s\n", debugstr_a(str), debugstr_a(trim)); + + if (!str || !*str) + return FALSE; + + while (*ptr && StrChrA(trim, *ptr)) + ptr = CharNextA(ptr); /* Skip leading matches */ + + len = strlen(ptr); + + if (ptr != str) + { + memmove(str, ptr, len + 1); + ret = TRUE; + } + + if (len > 0) + { + ptr = str + len; + while (StrChrA(trim, ptr[-1])) + ptr = CharPrevA(str, ptr); /* Skip trailing matches */ + + if (ptr != str + len) + { + *ptr = '\0'; + ret = TRUE; + } + } + + return ret; +} + +BOOL WINAPI StrTrimW(WCHAR *str, const WCHAR *trim) +{ + unsigned int len; + WCHAR *ptr = str; + BOOL ret = FALSE; + + TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(trim)); + + if (!str || !*str) + return FALSE; + + while (*ptr && StrChrW(trim, *ptr)) + ptr++; + + len = lstrlenW(ptr); + + if (ptr != str) + { + memmove(str, ptr, (len + 1) * sizeof(WCHAR)); + ret = TRUE; + } + + if (len > 0) + { + ptr = str + len; + while (StrChrW(trim, ptr[-1])) + ptr--; /* Skip trailing matches */ + + if (ptr != str + len) + { + *ptr = '\0'; + ret = TRUE; + } + } + + return ret; +} + +BOOL WINAPI StrToInt64ExA(const char *str, DWORD flags, LONGLONG *ret) +{ + BOOL negative = FALSE; + LONGLONG value = 0; + + TRACE("%s, %#lx, %p\n", wine_dbgstr_a(str), flags, ret); + + if (!str || !ret) + return FALSE; + + if (flags > STIF_SUPPORT_HEX) + WARN("Unknown flags %#lx\n", flags); + + /* Skip leading space, '+', '-' */ + while (*str == ' ' || *str == '\t' || *str == '\n') str++; + + if (*str == '-') + { + negative = TRUE; + str++; + } + else if (*str == '+') + str++; + + if (flags & STIF_SUPPORT_HEX && *str == '0' && (str[1] == 'x' || str[1] == 'X')) + { + /* Read hex number */ + str += 2; + + if (!isxdigit(*str)) + return FALSE; + + while (isxdigit(*str)) + { + value *= 16; + if (*str >= '0' && *str <= '9') + value += (*str - '0'); + else if (*str >= 'A' && *str <= 'F') + value += 10 + *str - 'A'; + else + value += 10 + *str - 'a'; + str++; + } + + *ret = value; + return TRUE; + } + + /* Read decimal number */ + if (*str < '0' || *str > '9') + return FALSE; + + while (*str >= '0' && *str <= '9') + { + value *= 10; + value += (*str - '0'); + str++; + } + + *ret = negative ? -value : value; + return TRUE; +} + +BOOL WINAPI StrToInt64ExW(const WCHAR *str, DWORD flags, LONGLONG *ret) +{ + BOOL negative = FALSE; + LONGLONG value = 0; + + TRACE("%s, %#lx, %p\n", wine_dbgstr_w(str), flags, ret); + + if (!str || !ret) + return FALSE; + + if (flags > STIF_SUPPORT_HEX) + WARN("Unknown flags %#lx.\n", flags); + + /* Skip leading space, '+', '-' */ + while (*str == ' ' || *str == '\t' || *str == '\n') str++; + + if (*str == '-') + { + negative = TRUE; + str++; + } + else if (*str == '+') + str++; + + if (flags & STIF_SUPPORT_HEX && *str == '0' && (str[1] == 'x' || str[1] == 'X')) + { + /* Read hex number */ + str += 2; + + if (!isxdigit(*str)) + return FALSE; + + while (isxdigit(*str)) + { + value *= 16; + if (*str >= '0' && *str <= '9') + value += (*str - '0'); + else if (*str >= 'A' && *str <= 'Z') + value += 10 + (*str - 'A'); + else + value += 10 + (*str - 'a'); + str++; + } + + *ret = value; + return TRUE; + } + + /* Read decimal number */ + if (*str < '0' || *str > '9') + return FALSE; + + while (*str >= '0' && *str <= '9') + { + value *= 10; + value += (*str - '0'); + str++; + } + + *ret = negative ? -value : value; + return TRUE; +} + +BOOL WINAPI StrToIntExA(const char *str, DWORD flags, INT *ret) +{ + LONGLONG value; + BOOL res; + + TRACE("%s, %#lx, %p\n", wine_dbgstr_a(str), flags, ret); + + res = StrToInt64ExA(str, flags, &value); + if (res) *ret = value; + return res; +} + +BOOL WINAPI StrToIntExW(const WCHAR *str, DWORD flags, INT *ret) +{ + LONGLONG value; + BOOL res; + + TRACE("%s, %#lx, %p\n", wine_dbgstr_w(str), flags, ret); + + res = StrToInt64ExW(str, flags, &value); + if (res) *ret = value; + return res; +} + +int WINAPI StrToIntA(const char *str) +{ + int value = 0; + + TRACE("%s\n", wine_dbgstr_a(str)); + + if (!str) + return 0; + + if (*str == '-' || (*str >= '0' && *str <= '9')) + StrToIntExA(str, 0, &value); + + return value; +} + +int WINAPI StrToIntW(const WCHAR *str) +{ + int value = 0; + + TRACE("%s\n", wine_dbgstr_w(str)); + + if (!str) + return 0; + + if (*str == '-' || (*str >= '0' && *str <= '9')) + StrToIntExW(str, 0, &value); + return value; +} + +char * WINAPI StrCpyNXA(char *dst, const char *src, int len) +{ + TRACE("%p, %s, %i\n", dst, wine_dbgstr_a(src), len); + + if (dst && src && len > 0) + { + while ((len-- > 1) && *src) + *dst++ = *src++; + if (len >= 0) + *dst = '\0'; + } + + return dst; +} + +WCHAR * WINAPI StrCpyNXW(WCHAR *dst, const WCHAR *src, int len) +{ + TRACE("%p, %s, %i\n", dst, wine_dbgstr_w(src), len); + + if (dst && src && len > 0) + { + while ((len-- > 1) && *src) + *dst++ = *src++; + if (len >= 0) + *dst = '\0'; + } + + return dst; +} + +LPSTR WINAPI CharLowerA(char *str) +{ + if (IS_INTRESOURCE(str)) + { + char ch = LOWORD(str); + CharLowerBuffA( &ch, 1 ); + return (LPSTR)(UINT_PTR)(BYTE)ch; + } + + __TRY + { + CharLowerBuffA( str, strlen(str) ); + } + __EXCEPT_PAGE_FAULT + { + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + __ENDTRY + return str; +} + +DWORD WINAPI CharLowerBuffA(char *str, DWORD len) +{ + DWORD lenW; + WCHAR buffer[32]; + WCHAR *strW = buffer; + + if (!str) return 0; /* YES */ + + lenW = MultiByteToWideChar(CP_ACP, 0, str, len, NULL, 0); + if (lenW > ARRAY_SIZE(buffer)) + { + strW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); + if (!strW) return 0; + } + MultiByteToWideChar(CP_ACP, 0, str, len, strW, lenW); + CharLowerBuffW(strW, lenW); + len = WideCharToMultiByte(CP_ACP, 0, strW, lenW, str, len, NULL, NULL); + if (strW != buffer) HeapFree(GetProcessHeap(), 0, strW); + return len; +} + +DWORD WINAPI CharLowerBuffW(WCHAR *str, DWORD len) +{ + if (!str) return 0; /* YES */ + return LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, str, len, str, len); +} + +LPWSTR WINAPI CharLowerW(WCHAR *str) +{ + if (!IS_INTRESOURCE(str)) + { + CharLowerBuffW(str, lstrlenW(str)); + return str; + } + else + { + WCHAR ch = LOWORD(str); + CharLowerBuffW(&ch, 1); + return (LPWSTR)(UINT_PTR)ch; + } +} + +LPSTR WINAPI CharNextA(const char *ptr) +{ + if (!*ptr) return (LPSTR)ptr; + if (IsDBCSLeadByte( ptr[0] ) && ptr[1]) return (LPSTR)(ptr + 2); + return (LPSTR)(ptr + 1); +} + +LPSTR WINAPI CharNextExA(WORD codepage, const char *ptr, DWORD flags) +{ + if (!*ptr) return (LPSTR)ptr; + if (IsDBCSLeadByteEx( codepage, ptr[0] ) && ptr[1]) return (LPSTR)(ptr + 2); + return (LPSTR)(ptr + 1); +} + +LPWSTR WINAPI CharNextW(const WCHAR *x) +{ + if (*x) x++; + + return (WCHAR *)x; +} + +LPSTR WINAPI CharPrevA(const char *start, const char *ptr) +{ + while (*start && (start < ptr)) + { + LPCSTR next = CharNextA(start); + if (next >= ptr) break; + start = next; + } + return (LPSTR)start; +} + +LPSTR WINAPI CharPrevExA(WORD codepage, const char *start, const char *ptr, DWORD flags) +{ + while (*start && (start < ptr)) + { + LPCSTR next = CharNextExA(codepage, start, flags); + if (next >= ptr) break; + start = next; + } + return (LPSTR)start; +} + +LPWSTR WINAPI CharPrevW(const WCHAR *start, const WCHAR *x) +{ + if (x > start) return (LPWSTR)(x - 1); + else return (LPWSTR)x; +} + +LPSTR WINAPI CharUpperA(LPSTR str) +{ + if (IS_INTRESOURCE(str)) + { + char ch = LOWORD(str); + CharUpperBuffA(&ch, 1); + return (LPSTR)(UINT_PTR)(BYTE)ch; + } + + __TRY + { + CharUpperBuffA(str, strlen(str)); + } + __EXCEPT_PAGE_FAULT + { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + __ENDTRY + return str; +} + +DWORD WINAPI CharUpperBuffA(LPSTR str, DWORD len) +{ + DWORD lenW; + WCHAR buffer[32]; + WCHAR *strW = buffer; + + if (!str) return 0; /* YES */ + + lenW = MultiByteToWideChar(CP_ACP, 0, str, len, NULL, 0); + if (lenW > ARRAY_SIZE(buffer)) + { + strW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); + if (!strW) return 0; + } + MultiByteToWideChar(CP_ACP, 0, str, len, strW, lenW); + CharUpperBuffW(strW, lenW); + len = WideCharToMultiByte(CP_ACP, 0, strW, lenW, str, len, NULL, NULL); + if (strW != buffer) HeapFree(GetProcessHeap(), 0, strW); + return len; +} + +DWORD WINAPI CharUpperBuffW(WCHAR *str, DWORD len) +{ + if (!str) return 0; /* YES */ + return LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, str, len, str, len); +} + +LPWSTR WINAPI CharUpperW(WCHAR *str) +{ + if (!IS_INTRESOURCE(str)) + { + CharUpperBuffW(str, lstrlenW(str)); + return str; + } + else + { + WCHAR ch = LOWORD(str); + CharUpperBuffW(&ch, 1); + return (LPWSTR)(UINT_PTR)ch; + } +} + +INT WINAPI DECLSPEC_HOTPATCH LoadStringW(HINSTANCE instance, UINT resource_id, LPWSTR buffer, INT buflen) +{ + int string_num, i; + HGLOBAL hmem; + HRSRC hrsrc; + WCHAR *p; + + TRACE("instance = %p, id = %04x, buffer = %p, length = %d\n", instance, resource_id, buffer, buflen); + + if (!buffer) + return 0; + + if (!(hrsrc = FindResourceW(instance, MAKEINTRESOURCEW((LOWORD(resource_id) >> 4) + 1), (LPWSTR)RT_STRING)) || + !(hmem = LoadResource(instance, hrsrc))) + { + TRACE( "Failed to load string.\n" ); + if (buflen > 0) buffer[0] = 0; + return 0; + } + + p = LockResource(hmem); + string_num = resource_id & 0x000f; + for (i = 0; i < string_num; i++) + p += *p + 1; + + TRACE("strlen = %d\n", (int)*p ); + + /*if buflen == 0, then return a read-only pointer to the resource itself in buffer + it is assumed that buffer is actually a (LPWSTR *) */ + if (buflen == 0) + { + *((LPWSTR *)buffer) = p + 1; + return *p; + } + + i = min(buflen - 1, *p); + memcpy(buffer, p + 1, i * sizeof(WCHAR)); + buffer[i] = 0; + + TRACE("returning %s\n", debugstr_w(buffer)); + return i; +} + +INT WINAPI DECLSPEC_HOTPATCH LoadStringA(HINSTANCE instance, UINT resource_id, LPSTR buffer, INT buflen) +{ + DWORD retval = 0; + HGLOBAL hmem; + HRSRC hrsrc; + + TRACE("instance = %p, id = %04x, buffer = %p, length = %d\n", instance, resource_id, buffer, buflen); + + if (!buflen) return -1; + + /* Use loword (incremented by 1) as resourceid */ + if ((hrsrc = FindResourceW(instance, MAKEINTRESOURCEW((LOWORD(resource_id) >> 4) + 1), (LPWSTR)RT_STRING )) && + (hmem = LoadResource(instance, hrsrc))) + { + const WCHAR *p = LockResource(hmem); + unsigned int id = resource_id & 0x000f; + + while (id--) p += *p + 1; + + RtlUnicodeToMultiByteN(buffer, buflen - 1, &retval, p + 1, *p * sizeof(WCHAR)); + } + buffer[retval] = 0; + TRACE("returning %s\n", debugstr_a(buffer)); + return retval; +} + +int WINAPI StrCmpLogicalW(const WCHAR *str, const WCHAR *comp) +{ + TRACE("%s, %s\n", wine_dbgstr_w(str), wine_dbgstr_w(comp)); + + if (!str || !comp) + return 0; + + while (*str) + { + if (!*comp) + return 1; + else if (*str >= '0' && *str <= '9') + { + int str_value, comp_value; + + if (*comp < '0' || *comp > '9') + return -1; + + /* Compare the numbers */ + StrToIntExW(str, 0, &str_value); + StrToIntExW(comp, 0, &comp_value); + + if (str_value < comp_value) + return -1; + else if (str_value > comp_value) + return 1; + + /* Skip */ + while (*str >= '0' && *str <= '9') str++; + while (*comp >= '0' && *comp <= '9') comp++; + } + else if (*comp >= '0' && *comp <= '9') + return 1; + else + { + int diff = ChrCmpIW(*str, *comp); + if (diff > 0) + return 1; + else if (diff < 0) + return -1; + + str++; + comp++; + } + } + + if (*comp) + return -1; + + return 0; +} + +BOOL WINAPI StrIsIntlEqualA(BOOL case_sensitive, const char *str, const char *cmp, int len) +{ + DWORD flags; + + TRACE("%d, %s, %s, %d\n", case_sensitive, wine_dbgstr_a(str), wine_dbgstr_a(cmp), len); + + /* FIXME: This flag is undocumented and unknown by our CompareString. + * We need a define for it. + */ + flags = 0x10000000; + if (!case_sensitive) + flags |= NORM_IGNORECASE; + + return (CompareStringA(GetThreadLocale(), flags, str, len, cmp, len) == CSTR_EQUAL); +} + +BOOL WINAPI StrIsIntlEqualW(BOOL case_sensitive, const WCHAR *str, const WCHAR *cmp, int len) +{ + DWORD flags; + + TRACE("%d, %s, %s, %d\n", case_sensitive, debugstr_w(str), debugstr_w(cmp), len); + + /* FIXME: This flag is undocumented and unknown by our CompareString. + * We need a define for it. + */ + flags = 0x10000000; + if (!case_sensitive) + flags |= NORM_IGNORECASE; + + return (CompareStringW(GetThreadLocale(), flags, str, len, cmp, len) == CSTR_EQUAL); +} + +char * WINAPI StrCatBuffA(char *str, const char *cat, INT max_len) +{ + INT len; + + TRACE("%p, %s, %d\n", str, wine_dbgstr_a(cat), max_len); + + if (!str) + return NULL; + + len = strlen(str); + max_len -= len; + if (max_len > 0) + StrCpyNA(str + len, cat, max_len); + + return str; +} + +WCHAR * WINAPI StrCatBuffW(WCHAR *str, const WCHAR *cat, INT max_len) +{ + INT len; + + TRACE("%p, %s, %d\n", str, wine_dbgstr_w(cat), max_len); + + if (!str) + return NULL; + + len = lstrlenW(str); + max_len -= len; + if (max_len > 0) + StrCpyNW(str + len, cat, max_len); + + return str; +} + +DWORD WINAPI StrCatChainW(WCHAR *str, DWORD max_len, DWORD at, const WCHAR *cat) +{ + TRACE("%s, %lu, %ld, %s\n", wine_dbgstr_w(str), max_len, at, wine_dbgstr_w(cat)); + + if (at == -1) + at = lstrlenW(str); + + if (!max_len) + return at; + + if (at == max_len) + at--; + + if (cat && at < max_len) + { + str += at; + while (at < max_len - 1 && *cat) + { + *str++ = *cat++; + at++; + } + *str = 0; + } + + return at; +} + +DWORD WINAPI SHTruncateString(char *str, DWORD size) +{ + char *last_byte; + + if (!str || !size) + return 0; + + last_byte = str + size - 1; + + while (str < last_byte) + str += IsDBCSLeadByte(*str) ? 2 : 1; + + if (str == last_byte && IsDBCSLeadByte(*str)) + { + *str = '\0'; + size--; + } + + return size; +} + +HRESULT WINAPI SHLoadIndirectString(const WCHAR *src, WCHAR *dst, UINT dst_len, void **reserved) +{ + WCHAR *dllname = NULL; + HMODULE hmod = NULL; + HRESULT hr = E_FAIL; + + TRACE("%s, %p, %#x, %p\n", debugstr_w(src), dst, dst_len, reserved); + + if (src[0] == '@') + { + WCHAR *index_str; + int index; + + dst[0] = 0; + dllname = StrDupW(src + 1); + index_str = wcschr(dllname, ','); + + if(!index_str) goto end; + + *index_str = 0; + index_str++; + index = wcstol(index_str, NULL, 10); + + hmod = LoadLibraryW(dllname); + if (!hmod) goto end; + + if (index < 0) + { + if (LoadStringW(hmod, -index, dst, dst_len)) + hr = S_OK; + } + else + FIXME("can't handle non-negative indices (%d)\n", index); + } + else + { + if (dst != src) + lstrcpynW(dst, src, dst_len); + hr = S_OK; + } + + TRACE("returning %s\n", debugstr_w(dst)); +end: + if (hmod) FreeLibrary(hmod); + LocalFree(dllname); + return hr; +} diff --git a/dll/win32/KernelBase/wine/sync.c b/dll/win32/KernelBase/wine/sync.c new file mode 100644 index 0000000000000..f03a6c3150a69 --- /dev/null +++ b/dll/win32/KernelBase/wine/sync.c @@ -0,0 +1,1765 @@ +/* + * Kernel synchronization objects + * + * Copyright 1998 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "wincon.h" +#include "winerror.h" +#include "winnls.h" +#include "winternl.h" +#include "winioctl.h" +#include "ddk/wdm.h" + +#include "kernelbase.h" +#include "wine/asm.h" +#include "wine/exception.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(sync); + +static const struct _KUSER_SHARED_DATA *user_shared_data = (struct _KUSER_SHARED_DATA *)0x7ffe0000; + +/* check if current version is NT or Win95 */ +static inline BOOL is_version_nt(void) +{ + return !(GetVersion() & 0x80000000); +} + +/* helper for kernel32->ntdll timeout format conversion */ +static inline LARGE_INTEGER *get_nt_timeout( LARGE_INTEGER *time, DWORD timeout ) +{ + if (timeout == INFINITE) return NULL; + time->QuadPart = (ULONGLONG)timeout * -10000; + return time; +} + + +/*********************************************************************** + * BaseGetNamedObjectDirectory (kernelbase.@) + */ +NTSTATUS WINAPI BaseGetNamedObjectDirectory( HANDLE *dir ) +{ + static HANDLE handle; + WCHAR buffer[64]; + UNICODE_STRING str; + OBJECT_ATTRIBUTES attr; + NTSTATUS status = STATUS_SUCCESS; + + if (!handle) + { + HANDLE dir; + + swprintf( buffer, ARRAY_SIZE(buffer), L"\\Sessions\\%u\\BaseNamedObjects", + NtCurrentTeb()->Peb->SessionId ); + RtlInitUnicodeString( &str, buffer ); + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + status = NtOpenDirectoryObject( &dir, DIRECTORY_CREATE_OBJECT|DIRECTORY_TRAVERSE, &attr ); + if (!status && InterlockedCompareExchangePointer( &handle, dir, 0 ) != 0) + { + /* someone beat us here... */ + CloseHandle( dir ); + } + } + *dir = handle; + return status; +} + +static void get_create_object_attributes( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *nameW, + SECURITY_ATTRIBUTES *sa, const WCHAR *name ) +{ + attr->Length = sizeof(*attr); + attr->RootDirectory = 0; + attr->ObjectName = NULL; + attr->Attributes = OBJ_OPENIF | ((sa && sa->bInheritHandle) ? OBJ_INHERIT : 0); + attr->SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL; + attr->SecurityQualityOfService = NULL; + if (name) + { + RtlInitUnicodeString( nameW, name ); + attr->ObjectName = nameW; + BaseGetNamedObjectDirectory( &attr->RootDirectory ); + } +} + +static BOOL get_open_object_attributes( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *nameW, + BOOL inherit, const WCHAR *name ) +{ + HANDLE dir; + + if (!name) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + RtlInitUnicodeString( nameW, name ); + BaseGetNamedObjectDirectory( &dir ); + InitializeObjectAttributes( attr, nameW, inherit ? OBJ_INHERIT : 0, dir, NULL ); + return TRUE; +} + + +/*********************************************************************** + * Time functions + ***********************************************************************/ + + +/********************************************************************* + * GetSystemTimes (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetSystemTimes( FILETIME *idle, FILETIME *kernel, FILETIME *user ) +{ + LARGE_INTEGER idle_time, kernel_time, user_time; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *info; + ULONG ret_size; + DWORD i, cpus = NtCurrentTeb()->Peb->NumberOfProcessors; + + if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) * cpus ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + if (!set_ntstatus( NtQuerySystemInformation( SystemProcessorPerformanceInformation, info, + sizeof(*info) * cpus, &ret_size ))) + { + HeapFree( GetProcessHeap(), 0, info ); + return FALSE; + } + idle_time.QuadPart = 0; + kernel_time.QuadPart = 0; + user_time.QuadPart = 0; + for (i = 0; i < cpus; i++) + { + idle_time.QuadPart += info[i].IdleTime.QuadPart; + kernel_time.QuadPart += info[i].KernelTime.QuadPart; + user_time.QuadPart += info[i].UserTime.QuadPart; + } + if (idle) + { + idle->dwLowDateTime = idle_time.u.LowPart; + idle->dwHighDateTime = idle_time.u.HighPart; + } + if (kernel) + { + kernel->dwLowDateTime = kernel_time.u.LowPart; + kernel->dwHighDateTime = kernel_time.u.HighPart; + } + if (user) + { + user->dwLowDateTime = user_time.u.LowPart; + user->dwHighDateTime = user_time.u.HighPart; + } + HeapFree( GetProcessHeap(), 0, info ); + return TRUE; +} + + +/****************************************************************************** + * GetTickCount (kernelbase.@) + */ +ULONG WINAPI DECLSPEC_HOTPATCH GetTickCount(void) +{ + /* note: we ignore TickCountMultiplier */ + return user_shared_data->TickCount.LowPart; +} + + +/****************************************************************************** + * GetTickCount64 (kernelbase.@) + */ +ULONGLONG WINAPI DECLSPEC_HOTPATCH GetTickCount64(void) +{ + ULONG high, low; + + do + { + high = user_shared_data->TickCount.High1Time; + low = user_shared_data->TickCount.LowPart; + } + while (high != user_shared_data->TickCount.High2Time); + /* note: we ignore TickCountMultiplier */ + return (ULONGLONG)high << 32 | low; +} + + +/****************************************************************************** + * QueryInterruptTime (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH QueryInterruptTime( ULONGLONG *time ) +{ + ULONG high, low; + + do + { + high = user_shared_data->InterruptTime.High1Time; + low = user_shared_data->InterruptTime.LowPart; + } + while (high != user_shared_data->InterruptTime.High2Time); + *time = (ULONGLONG)high << 32 | low; +} + + +/****************************************************************************** + * QueryInterruptTimePrecise (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH QueryInterruptTimePrecise( ULONGLONG *time ) +{ + static int once; + if (!once++) FIXME( "(%p) semi-stub\n", time ); + + QueryInterruptTime( time ); +} + + +/*********************************************************************** + * QueryUnbiasedInterruptTimePrecise (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH QueryUnbiasedInterruptTimePrecise( ULONGLONG *time ) +{ + static int once; + if (!once++) FIXME( "(%p): semi-stub.\n", time ); + + RtlQueryUnbiasedInterruptTime( time ); +} + + +/*********************************************************************** + * QueryIdleProcessorCycleTime (kernelbase.@) + */ +BOOL WINAPI QueryIdleProcessorCycleTime( ULONG *size, ULONG64 *times ) +{ + ULONG ret_size; + NTSTATUS status = NtQuerySystemInformation( SystemProcessorIdleCycleTimeInformation, times, *size, &ret_size ); + + if (!*size || !status) *size = ret_size; + return TRUE; +} + + +/*********************************************************************** + * QueryIdleProcessorCycleTimeEx (kernelbase.@) + */ +BOOL WINAPI QueryIdleProcessorCycleTimeEx( USHORT group_id, ULONG *size, ULONG64 *times ) +{ + ULONG ret_size; + NTSTATUS status = NtQuerySystemInformationEx( SystemProcessorIdleCycleTimeInformation, &group_id, sizeof(group_id), + times, *size, &ret_size ); + if (!*size || !status) *size = ret_size; + return TRUE; +} + + +/*********************************************************************** + * Waits + ***********************************************************************/ + + +static HANDLE normalize_std_handle( HANDLE handle ) +{ + if ((handle == (HANDLE)STD_INPUT_HANDLE) || + (handle == (HANDLE)STD_OUTPUT_HANDLE) || + (handle == (HANDLE)STD_ERROR_HANDLE)) + return GetStdHandle( HandleToULong(handle) ); + + return handle; +} + + +/*********************************************************************** + * RegisterWaitForSingleObjectEx (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH RegisterWaitForSingleObjectEx( HANDLE handle, WAITORTIMERCALLBACK callback, + PVOID context, ULONG timeout, ULONG flags ) +{ + HANDLE ret; + + TRACE( "%p %p %p %ld %ld\n", handle, callback, context, timeout, flags ); + + handle = normalize_std_handle( handle ); + if (!set_ntstatus( RtlRegisterWait( &ret, handle, callback, context, timeout, flags ))) return NULL; + return ret; +} + + +/*********************************************************************** + * SignalObjectAndWait (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH SignalObjectAndWait( HANDLE signal, HANDLE wait, + DWORD timeout, BOOL alertable ) +{ + NTSTATUS status; + LARGE_INTEGER time; + + TRACE( "%p %p %ld %d\n", signal, wait, timeout, alertable ); + + status = NtSignalAndWaitForSingleObject( signal, wait, alertable, get_nt_timeout( &time, timeout ) ); + if (HIWORD(status)) + { + SetLastError( RtlNtStatusToDosError(status) ); + status = WAIT_FAILED; + } + return status; +} + + +/*********************************************************************** + * Sleep (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH Sleep( DWORD timeout ) +{ + LARGE_INTEGER time; + + NtDelayExecution( FALSE, get_nt_timeout( &time, timeout ) ); +} + + +/****************************************************************************** + * SleepEx (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH SleepEx( DWORD timeout, BOOL alertable ) +{ + NTSTATUS status; + LARGE_INTEGER time; + + status = NtDelayExecution( alertable, get_nt_timeout( &time, timeout ) ); + if (status == STATUS_USER_APC) return WAIT_IO_COMPLETION; + return 0; +} + + +/*********************************************************************** + * UnregisterWaitEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH UnregisterWaitEx( HANDLE handle, HANDLE event ) +{ + return set_ntstatus( RtlDeregisterWaitEx( handle, event )); +} + + +/*********************************************************************** + * WaitForSingleObject (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH WaitForSingleObject( HANDLE handle, DWORD timeout ) +{ + return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, FALSE ); +} + + +/*********************************************************************** + * WaitForSingleObjectEx (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH WaitForSingleObjectEx( HANDLE handle, DWORD timeout, BOOL alertable ) +{ + return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, alertable ); +} + + +/*********************************************************************** + * WaitForMultipleObjects (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH WaitForMultipleObjects( DWORD count, const HANDLE *handles, + BOOL wait_all, DWORD timeout ) +{ + return WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE ); +} + + +/*********************************************************************** + * WaitForMultipleObjectsEx (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles, + BOOL wait_all, DWORD timeout, BOOL alertable ) +{ + NTSTATUS status; + HANDLE hloc[MAXIMUM_WAIT_OBJECTS]; + LARGE_INTEGER time; + unsigned int i; + + if (count > MAXIMUM_WAIT_OBJECTS) + { + SetLastError(ERROR_INVALID_PARAMETER); + return WAIT_FAILED; + } + for (i = 0; i < count; i++) hloc[i] = normalize_std_handle( handles[i] ); + + status = NtWaitForMultipleObjects( count, hloc, !wait_all, alertable, + get_nt_timeout( &time, timeout ) ); + if (HIWORD(status)) /* is it an error code? */ + { + SetLastError( RtlNtStatusToDosError(status) ); + status = WAIT_FAILED; + } + return status; +} + + +/****************************************************************************** + * WaitForDebugEvent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WaitForDebugEvent( DEBUG_EVENT *event, DWORD timeout ) +{ + NTSTATUS status; + LARGE_INTEGER time; + DBGUI_WAIT_STATE_CHANGE state; + + for (;;) + { + status = DbgUiWaitStateChange( &state, get_nt_timeout( &time, timeout ) ); + switch (status) + { + case STATUS_SUCCESS: + /* continue on wide print exceptions to force resending an ANSI one. */ + if (state.NewState == DbgExceptionStateChange) + { + DBGKM_EXCEPTION *info = &state.StateInfo.Exception; + DWORD code = info->ExceptionRecord.ExceptionCode; + if (code == DBG_PRINTEXCEPTION_WIDE_C && info->ExceptionRecord.NumberParameters >= 2) + { + DbgUiContinue( &state.AppClientId, DBG_EXCEPTION_NOT_HANDLED ); + break; + } + } + DbgUiConvertStateChangeStructure( &state, event ); + return TRUE; + case STATUS_USER_APC: + continue; + case STATUS_TIMEOUT: + SetLastError( ERROR_SEM_TIMEOUT ); + return FALSE; + default: + return set_ntstatus( status ); + } + } +} + +/****************************************************************************** + * WaitForDebugEventEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WaitForDebugEventEx( DEBUG_EVENT *event, DWORD timeout ) +{ + NTSTATUS status; + LARGE_INTEGER time; + DBGUI_WAIT_STATE_CHANGE state; + + for (;;) + { + status = DbgUiWaitStateChange( &state, get_nt_timeout( &time, timeout ) ); + switch (status) + { + case STATUS_SUCCESS: + DbgUiConvertStateChangeStructure( &state, event ); + return TRUE; + case STATUS_USER_APC: + continue; + case STATUS_TIMEOUT: + SetLastError( ERROR_SEM_TIMEOUT ); + return FALSE; + default: + return set_ntstatus( status ); + } + } +} + + +/*********************************************************************** + * WaitOnAddress (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WaitOnAddress( volatile void *addr, void *cmp, SIZE_T size, DWORD timeout ) +{ + LARGE_INTEGER to; + + if (timeout != INFINITE) + { + to.QuadPart = -(LONGLONG)timeout * 10000; + return set_ntstatus( RtlWaitOnAddress( (const void *)addr, cmp, size, &to )); + } + return set_ntstatus( RtlWaitOnAddress( (const void *)addr, cmp, size, NULL )); +} + + +/*********************************************************************** + * Events + ***********************************************************************/ + + +/*********************************************************************** + * CreateEventA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateEventA( SECURITY_ATTRIBUTES *sa, BOOL manual_reset, + BOOL initial_state, LPCSTR name ) +{ + DWORD flags = 0; + + if (manual_reset) flags |= CREATE_EVENT_MANUAL_RESET; + if (initial_state) flags |= CREATE_EVENT_INITIAL_SET; + return CreateEventExA( sa, name, flags, EVENT_ALL_ACCESS ); +} + + +/*********************************************************************** + * CreateEventW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateEventW( SECURITY_ATTRIBUTES *sa, BOOL manual_reset, + BOOL initial_state, LPCWSTR name ) +{ + DWORD flags = 0; + + if (manual_reset) flags |= CREATE_EVENT_MANUAL_RESET; + if (initial_state) flags |= CREATE_EVENT_INITIAL_SET; + return CreateEventExW( sa, name, flags, EVENT_ALL_ACCESS ); +} + + +/*********************************************************************** + * CreateEventExA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateEventExA( SECURITY_ATTRIBUTES *sa, LPCSTR name, + DWORD flags, DWORD access ) +{ + WCHAR buffer[MAX_PATH]; + + if (!name) return CreateEventExW( sa, NULL, flags, access ); + + if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH )) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + return CreateEventExW( sa, buffer, flags, access ); +} + + +/*********************************************************************** + * CreateEventExW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateEventExW( SECURITY_ATTRIBUTES *sa, LPCWSTR name, + DWORD flags, DWORD access ) +{ + HANDLE ret = 0; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + + /* one buggy program needs this + * ("Van Dale Groot woordenboek der Nederlandse taal") + */ + __TRY + { + get_create_object_attributes( &attr, &nameW, sa, name ); + } + __EXCEPT_PAGE_FAULT + { + SetLastError( ERROR_INVALID_PARAMETER); + return 0; + } + __ENDTRY + + status = NtCreateEvent( &ret, access, &attr, + (flags & CREATE_EVENT_MANUAL_RESET) ? NotificationEvent : SynchronizationEvent, + (flags & CREATE_EVENT_INITIAL_SET) != 0 ); + if (status == STATUS_OBJECT_NAME_EXISTS) + SetLastError( ERROR_ALREADY_EXISTS ); + else + SetLastError( RtlNtStatusToDosError(status) ); + return ret; +} + + +/*********************************************************************** + * OpenEventA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenEventA( DWORD access, BOOL inherit, LPCSTR name ) +{ + WCHAR buffer[MAX_PATH]; + + if (!name) return OpenEventW( access, inherit, NULL ); + + if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH )) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + return OpenEventW( access, inherit, buffer ); +} + + +/*********************************************************************** + * OpenEventW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenEventW( DWORD access, BOOL inherit, LPCWSTR name ) +{ + HANDLE ret; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + + if (!is_version_nt()) access = EVENT_ALL_ACCESS; + + if (!get_open_object_attributes( &attr, &nameW, inherit, name )) return 0; + + if (!set_ntstatus( NtOpenEvent( &ret, access, &attr ))) return 0; + return ret; +} + +/*********************************************************************** + * PulseEvent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH PulseEvent( HANDLE handle ) +{ + return set_ntstatus( NtPulseEvent( handle, NULL )); +} + + +/*********************************************************************** + * SetEvent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetEvent( HANDLE handle ) +{ + return set_ntstatus( NtSetEvent( handle, NULL )); +} + + +/*********************************************************************** + * ResetEvent (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ResetEvent( HANDLE handle ) +{ + return set_ntstatus( NtResetEvent( handle, NULL )); +} + + +/*********************************************************************** + * Mutexes + ***********************************************************************/ + + +/*********************************************************************** + * CreateMutexA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateMutexA( SECURITY_ATTRIBUTES *sa, BOOL owner, LPCSTR name ) +{ + return CreateMutexExA( sa, name, owner ? CREATE_MUTEX_INITIAL_OWNER : 0, MUTEX_ALL_ACCESS ); +} + + +/*********************************************************************** + * CreateMutexW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateMutexW( SECURITY_ATTRIBUTES *sa, BOOL owner, LPCWSTR name ) +{ + return CreateMutexExW( sa, name, owner ? CREATE_MUTEX_INITIAL_OWNER : 0, MUTEX_ALL_ACCESS ); +} + + +/*********************************************************************** + * CreateMutexExA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateMutexExA( SECURITY_ATTRIBUTES *sa, LPCSTR name, + DWORD flags, DWORD access ) +{ + ANSI_STRING nameA; + NTSTATUS status; + + if (!name) return CreateMutexExW( sa, NULL, flags, access ); + + RtlInitAnsiString( &nameA, name ); + status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString, &nameA, FALSE ); + if (status != STATUS_SUCCESS) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return 0; + } + return CreateMutexExW( sa, NtCurrentTeb()->StaticUnicodeString.Buffer, flags, access ); +} + + +/*********************************************************************** + * CreateMutexExW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateMutexExW( SECURITY_ATTRIBUTES *sa, LPCWSTR name, + DWORD flags, DWORD access ) +{ + HANDLE ret = 0; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + + get_create_object_attributes( &attr, &nameW, sa, name ); + + status = NtCreateMutant( &ret, access, &attr, (flags & CREATE_MUTEX_INITIAL_OWNER) != 0 ); + if (status == STATUS_OBJECT_NAME_EXISTS) + SetLastError( ERROR_ALREADY_EXISTS ); + else + SetLastError( RtlNtStatusToDosError(status) ); + return ret; +} + + +/*********************************************************************** + * OpenMutexW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenMutexW( DWORD access, BOOL inherit, LPCWSTR name ) +{ + HANDLE ret; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + + if (!is_version_nt()) access = MUTEX_ALL_ACCESS; + + if (!get_open_object_attributes( &attr, &nameW, inherit, name )) return 0; + + if (!set_ntstatus( NtOpenMutant( &ret, access, &attr ))) return 0; + return ret; +} + + +/*********************************************************************** + * ReleaseMutex (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReleaseMutex( HANDLE handle ) +{ + return set_ntstatus( NtReleaseMutant( handle, NULL )); +} + + +/*********************************************************************** + * Semaphores + ***********************************************************************/ + + +/*********************************************************************** + * CreateSemaphoreW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateSemaphoreW( SECURITY_ATTRIBUTES *sa, LONG initial, + LONG max, LPCWSTR name ) +{ + return CreateSemaphoreExW( sa, initial, max, name, 0, SEMAPHORE_ALL_ACCESS ); +} + + +/*********************************************************************** + * CreateSemaphoreExW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateSemaphoreExW( SECURITY_ATTRIBUTES *sa, LONG initial, LONG max, + LPCWSTR name, DWORD flags, DWORD access ) +{ + HANDLE ret = 0; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + + get_create_object_attributes( &attr, &nameW, sa, name ); + + status = NtCreateSemaphore( &ret, access, &attr, initial, max ); + if (status == STATUS_OBJECT_NAME_EXISTS) + SetLastError( ERROR_ALREADY_EXISTS ); + else + SetLastError( RtlNtStatusToDosError(status) ); + return ret; +} + + +/*********************************************************************** + * OpenSemaphoreW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenSemaphoreW( DWORD access, BOOL inherit, LPCWSTR name ) +{ + HANDLE ret; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + + if (!is_version_nt()) access = SEMAPHORE_ALL_ACCESS; + + if (!get_open_object_attributes( &attr, &nameW, inherit, name )) return 0; + + if (!set_ntstatus( NtOpenSemaphore( &ret, access, &attr ))) return 0; + return ret; +} + + +/*********************************************************************** + * ReleaseSemaphore (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReleaseSemaphore( HANDLE handle, LONG count, LONG *previous ) +{ + return set_ntstatus( NtReleaseSemaphore( handle, count, (PULONG)previous )); +} + + +/*********************************************************************** + * Waitable timers + ***********************************************************************/ + + +/*********************************************************************** + * CreateWaitableTimerW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateWaitableTimerW( SECURITY_ATTRIBUTES *sa, BOOL manual, LPCWSTR name ) +{ + return CreateWaitableTimerExW( sa, name, manual ? CREATE_WAITABLE_TIMER_MANUAL_RESET : 0, + TIMER_ALL_ACCESS ); +} + + +/*********************************************************************** + * CreateWaitableTimerExW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateWaitableTimerExW( SECURITY_ATTRIBUTES *sa, LPCWSTR name, + DWORD flags, DWORD access ) +{ + HANDLE handle; + NTSTATUS status; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + + get_create_object_attributes( &attr, &nameW, sa, name ); + + status = NtCreateTimer( &handle, access, &attr, + (flags & CREATE_WAITABLE_TIMER_MANUAL_RESET) ? NotificationTimer : SynchronizationTimer ); + if (status == STATUS_OBJECT_NAME_EXISTS) + SetLastError( ERROR_ALREADY_EXISTS ); + else + SetLastError( RtlNtStatusToDosError(status) ); + return handle; +} + + +/*********************************************************************** + * OpenWaitableTimerW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenWaitableTimerW( DWORD access, BOOL inherit, LPCWSTR name ) +{ + HANDLE handle; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + + if (!is_version_nt()) access = TIMER_ALL_ACCESS; + + if (!get_open_object_attributes( &attr, &nameW, inherit, name )) return 0; + + if (!set_ntstatus( NtOpenTimer( &handle, access, &attr ))) return 0; + return handle; +} + + +/*********************************************************************** + * SetWaitableTimer (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetWaitableTimer( HANDLE handle, const LARGE_INTEGER *when, LONG period, + PTIMERAPCROUTINE callback, LPVOID arg, BOOL resume ) +{ + NTSTATUS status = NtSetTimer( handle, when, (PTIMER_APC_ROUTINE)callback, + arg, resume, period, NULL ); + return set_ntstatus( status ) || status == STATUS_TIMER_RESUME_IGNORED; +} + + +/*********************************************************************** + * SetWaitableTimerEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetWaitableTimerEx( HANDLE handle, const LARGE_INTEGER *when, LONG period, + PTIMERAPCROUTINE callback, LPVOID arg, + REASON_CONTEXT *context, ULONG tolerabledelay ) +{ + static int once; + if (!once++) FIXME( "(%p, %p, %ld, %p, %p, %p, %ld) semi-stub\n", + handle, when, period, callback, arg, context, tolerabledelay ); + + return SetWaitableTimer( handle, when, period, callback, arg, FALSE ); +} + + +/*********************************************************************** + * CancelWaitableTimer (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CancelWaitableTimer( HANDLE handle ) +{ + return set_ntstatus( NtCancelTimer( handle, NULL )); +} + + +/*********************************************************************** + * Timer queues + ***********************************************************************/ + + +/*********************************************************************** + * CreateTimerQueue (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateTimerQueue(void) +{ + HANDLE q; + + if (!set_ntstatus( RtlCreateTimerQueue( &q ))) return NULL; + return q; +} + + +/*********************************************************************** + * CreateTimerQueueTimer (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateTimerQueueTimer( PHANDLE timer, HANDLE queue, + WAITORTIMERCALLBACK callback, PVOID arg, + DWORD when, DWORD period, ULONG flags ) +{ + return set_ntstatus( RtlCreateTimer( queue, timer, callback, arg, when, period, flags )); +} + + +/*********************************************************************** + * ChangeTimerQueueTimer (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ChangeTimerQueueTimer( HANDLE queue, HANDLE timer, + ULONG when, ULONG period ) +{ + return set_ntstatus( RtlUpdateTimer( queue, timer, when, period )); +} + + +/*********************************************************************** + * DeleteTimerQueueEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DeleteTimerQueueEx( HANDLE queue, HANDLE event ) +{ + return set_ntstatus( RtlDeleteTimerQueueEx( queue, event )); +} + + +/*********************************************************************** + * DeleteTimerQueueTimer (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DeleteTimerQueueTimer( HANDLE queue, HANDLE timer, HANDLE event ) +{ + return set_ntstatus( RtlDeleteTimer( queue, timer, event )); +} + + +/*********************************************************************** + * Critical sections + ***********************************************************************/ + + +/*********************************************************************** + * InitializeCriticalSectionAndSpinCount (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH InitializeCriticalSectionAndSpinCount( CRITICAL_SECTION *crit, DWORD count ) +{ + return !RtlInitializeCriticalSectionAndSpinCount( crit, count ); +} + +/*********************************************************************** + * InitializeCriticalSectionEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH InitializeCriticalSectionEx( CRITICAL_SECTION *crit, DWORD spincount, + DWORD flags ) +{ + NTSTATUS ret = RtlInitializeCriticalSectionEx( crit, spincount, flags ); + if (ret) RtlRaiseStatus( ret ); + return !ret; +} + + +/*********************************************************************** + * File mappings + ***********************************************************************/ + +/*********************************************************************** + * CreateFileMappingW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileMappingW( HANDLE file, LPSECURITY_ATTRIBUTES sa, DWORD protect, + DWORD size_high, DWORD size_low, LPCWSTR name ) +{ + static const int sec_flags = (SEC_FILE | SEC_IMAGE | SEC_RESERVE | SEC_COMMIT | + SEC_NOCACHE | SEC_WRITECOMBINE | SEC_LARGE_PAGES); + HANDLE ret; + NTSTATUS status; + DWORD access, sec_type; + LARGE_INTEGER size; + UNICODE_STRING nameW; + OBJECT_ATTRIBUTES attr; + + sec_type = protect & sec_flags; + protect &= ~sec_flags; + if (!sec_type) sec_type = SEC_COMMIT; + + /* Win9x compatibility */ + if (!protect && !is_version_nt()) protect = PAGE_READONLY; + + switch(protect) + { + case PAGE_READONLY: + case PAGE_WRITECOPY: + access = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ; + break; + case PAGE_READWRITE: + access = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE; + break; + case PAGE_EXECUTE_READ: + case PAGE_EXECUTE_WRITECOPY: + access = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_EXECUTE; + break; + case PAGE_EXECUTE_READWRITE: + access = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE; + break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + size.u.LowPart = size_low; + size.u.HighPart = size_high; + + if (file == INVALID_HANDLE_VALUE) + { + file = 0; + if (!size.QuadPart) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + } + + get_create_object_attributes( &attr, &nameW, sa, name ); + + status = NtCreateSection( &ret, access, &attr, &size, protect, sec_type, file ); + if (status == STATUS_OBJECT_NAME_EXISTS) + SetLastError( ERROR_ALREADY_EXISTS ); + else + SetLastError( RtlNtStatusToDosError(status) ); + return ret; +} + + +/*********************************************************************** + * CreateFileMappingFromApp (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileMappingFromApp( HANDLE file, LPSECURITY_ATTRIBUTES sa, ULONG protect, + ULONG64 size, LPCWSTR name ) +{ + return CreateFileMappingW( file, sa, protect, size << 32, size, name ); +} + +/*********************************************************************** + * OpenFileMappingW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenFileMappingW( DWORD access, BOOL inherit, LPCWSTR name ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + HANDLE ret; + + if (!get_open_object_attributes( &attr, &nameW, inherit, name )) return 0; + + if (access == FILE_MAP_COPY) access = SECTION_MAP_READ; + + if (!is_version_nt()) + { + /* win9x doesn't do access checks, so try with full access first */ + if (!NtOpenSection( &ret, access | SECTION_MAP_READ | SECTION_MAP_WRITE, &attr )) return ret; + } + + if (!set_ntstatus( NtOpenSection( &ret, access, &attr ))) return 0; + return ret; +} + + +/*********************************************************************** + * OpenFileMappingFromApp (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenFileMappingFromApp( ULONG access, BOOL inherit, LPCWSTR name ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + HANDLE ret; + + if (!get_open_object_attributes( &attr, &nameW, inherit, name )) return 0; + + if (access == FILE_MAP_COPY) access = SECTION_MAP_READ; + + if (!set_ntstatus( NtOpenSection( &ret, access, &attr ))) return 0; + return ret; +} + + +/*********************************************************************** + * Condition variables + ***********************************************************************/ + + +/*********************************************************************** + * SleepConditionVariableCS (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SleepConditionVariableCS( CONDITION_VARIABLE *variable, + CRITICAL_SECTION *crit, DWORD timeout ) +{ + LARGE_INTEGER time; + + return set_ntstatus( RtlSleepConditionVariableCS( variable, crit, get_nt_timeout( &time, timeout ))); +} + + +/*********************************************************************** + * SleepConditionVariableSRW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, + RTL_SRWLOCK *lock, DWORD timeout, ULONG flags ) +{ + LARGE_INTEGER time; + + return set_ntstatus( RtlSleepConditionVariableSRW( variable, lock, + get_nt_timeout( &time, timeout ), flags )); +} + + +/*********************************************************************** + * I/O completions + ***********************************************************************/ + + +/****************************************************************************** + * CreateIoCompletionPort (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateIoCompletionPort( HANDLE handle, HANDLE port, + ULONG_PTR key, DWORD threads ) +{ + FILE_COMPLETION_INFORMATION info; + IO_STATUS_BLOCK iosb; + HANDLE ret = port; + + TRACE( "(%p, %p, %08Ix, %08lx)\n", handle, port, key, threads ); + + if (!port) + { + if (!set_ntstatus( NtCreateIoCompletion( &ret, IO_COMPLETION_ALL_ACCESS, NULL, threads ))) + return 0; + } + else if (handle == INVALID_HANDLE_VALUE) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + if (handle != INVALID_HANDLE_VALUE) + { + info.CompletionPort = ret; + info.CompletionKey = key; + if (!set_ntstatus( NtSetInformationFile( handle, &iosb, &info, sizeof(info), FileCompletionInformation ))) + { + if (!port) CloseHandle( ret ); + return 0; + } + } + return ret; +} + + +/****************************************************************************** + * GetQueuedCompletionStatus (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatus( HANDLE port, LPDWORD count, PULONG_PTR key, + LPOVERLAPPED *overlapped, DWORD timeout ) +{ + NTSTATUS status; + IO_STATUS_BLOCK iosb; + LARGE_INTEGER wait_time; + + TRACE( "(%p,%p,%p,%p,%ld)\n", port, count, key, overlapped, timeout ); + + *overlapped = NULL; + status = NtRemoveIoCompletion( port, key, (PULONG_PTR)overlapped, &iosb, + get_nt_timeout( &wait_time, timeout ) ); + if (status == STATUS_SUCCESS) + { + *count = iosb.Information; + if (iosb.Status >= 0) return TRUE; + SetLastError( RtlNtStatusToDosError(iosb.Status) ); + return FALSE; + } + + if (status == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (status == STATUS_ABANDONED) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; +} + +/****************************************************************************** + * GetQueuedCompletionStatusEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetQueuedCompletionStatusEx( HANDLE port, OVERLAPPED_ENTRY *entries, + ULONG count, ULONG *written, + DWORD timeout, BOOL alertable ) +{ + LARGE_INTEGER time; + NTSTATUS ret; + + TRACE( "%p %p %lu %p %lu %u\n", port, entries, count, written, timeout, alertable ); + + ret = NtRemoveIoCompletionEx( port, (FILE_IO_COMPLETION_INFORMATION *)entries, count, + written, get_nt_timeout( &time, timeout ), alertable ); + if (ret == STATUS_SUCCESS) return TRUE; + else if (ret == STATUS_TIMEOUT) SetLastError( WAIT_TIMEOUT ); + else if (ret == STATUS_USER_APC) SetLastError( WAIT_IO_COMPLETION ); + else if (ret == STATUS_ABANDONED) SetLastError( ERROR_ABANDONED_WAIT_0 ); + else SetLastError( RtlNtStatusToDosError(ret) ); + return FALSE; +} + + +/****************************************************************************** + * PostQueuedCompletionStatus (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH PostQueuedCompletionStatus( HANDLE port, DWORD count, + ULONG_PTR key, LPOVERLAPPED overlapped ) +{ + TRACE( "%p %ld %08Ix %p\n", port, count, key, overlapped ); + + return set_ntstatus( NtSetIoCompletion( port, key, (ULONG_PTR)overlapped, STATUS_SUCCESS, count )); +} + + +/*********************************************************************** + * Named pipes + ***********************************************************************/ + + +/*********************************************************************** + * CallNamedPipeW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CallNamedPipeW( LPCWSTR name, LPVOID input, DWORD in_size, + LPVOID output, DWORD out_size, + LPDWORD read_size, DWORD timeout ) +{ + HANDLE pipe; + BOOL ret; + DWORD mode; + + TRACE( "%s %p %ld %p %ld %p %ld\n", debugstr_w(name), + input, in_size, output, out_size, read_size, timeout ); + + pipe = CreateFileW( name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); + if (pipe == INVALID_HANDLE_VALUE) + { + if (!WaitNamedPipeW( name, timeout )) return FALSE; + pipe = CreateFileW( name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); + if (pipe == INVALID_HANDLE_VALUE) return FALSE; + } + + mode = PIPE_READMODE_MESSAGE; + ret = SetNamedPipeHandleState( pipe, &mode, NULL, NULL ); + if (ret) ret = TransactNamedPipe( pipe, input, in_size, output, out_size, read_size, NULL ); + CloseHandle( pipe ); + return ret; +} + + +/*********************************************************************** + * ConnectNamedPipe (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ConnectNamedPipe( HANDLE pipe, LPOVERLAPPED overlapped ) +{ + NTSTATUS status; + IO_STATUS_BLOCK status_block; + LPVOID cvalue = NULL; + + TRACE( "(%p,%p)\n", pipe, overlapped ); + + if (overlapped) + { + overlapped->Internal = STATUS_PENDING; + overlapped->InternalHigh = 0; + if (((ULONG_PTR)overlapped->hEvent & 1) == 0) cvalue = overlapped; + } + + status = NtFsControlFile( pipe, overlapped ? overlapped->hEvent : NULL, NULL, cvalue, + overlapped ? (IO_STATUS_BLOCK *)overlapped : &status_block, + FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0 ); + if (status == STATUS_PENDING && !overlapped) + { + WaitForSingleObject( pipe, INFINITE ); + status = status_block.Status; + } + return set_ntstatus( status ); +} + +/*********************************************************************** + * CreateNamedPipeW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateNamedPipeW( LPCWSTR name, DWORD open_mode, DWORD pipe_mode, + DWORD instances, DWORD out_buff, DWORD in_buff, + DWORD timeout, LPSECURITY_ATTRIBUTES sa ) +{ + HANDLE handle; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + DWORD access, options, sharing; + BOOLEAN pipe_type, read_mode, non_block; + NTSTATUS status; + IO_STATUS_BLOCK iosb; + LARGE_INTEGER time; + + TRACE( "(%s, %#08lx, %#08lx, %ld, %ld, %ld, %ld, %p)\n", debugstr_w(name), + open_mode, pipe_mode, instances, out_buff, in_buff, timeout, sa ); + + if (!RtlDosPathNameToNtPathName_U( name, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return INVALID_HANDLE_VALUE; + } + if (nt_name.Length >= MAX_PATH * sizeof(WCHAR) ) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + RtlFreeUnicodeString( &nt_name ); + return INVALID_HANDLE_VALUE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nt_name; + attr.Attributes = OBJ_CASE_INSENSITIVE | ((sa && sa->bInheritHandle) ? OBJ_INHERIT : 0); + attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL; + attr.SecurityQualityOfService = NULL; + + switch (open_mode & 3) + { + case PIPE_ACCESS_INBOUND: + sharing = FILE_SHARE_WRITE; + access = GENERIC_READ; + break; + case PIPE_ACCESS_OUTBOUND: + sharing = FILE_SHARE_READ; + access = GENERIC_WRITE; + break; + case PIPE_ACCESS_DUPLEX: + sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; + access = GENERIC_READ | GENERIC_WRITE; + break; + default: + SetLastError( ERROR_INVALID_PARAMETER ); + return INVALID_HANDLE_VALUE; + } + access |= SYNCHRONIZE; + options = 0; + if (open_mode & WRITE_DAC) access |= WRITE_DAC; + if (open_mode & WRITE_OWNER) access |= WRITE_OWNER; + if (open_mode & ACCESS_SYSTEM_SECURITY) access |= ACCESS_SYSTEM_SECURITY; + if (open_mode & FILE_FLAG_WRITE_THROUGH) options |= FILE_WRITE_THROUGH; + if (!(open_mode & FILE_FLAG_OVERLAPPED)) options |= FILE_SYNCHRONOUS_IO_NONALERT; + pipe_type = (pipe_mode & PIPE_TYPE_MESSAGE) != 0; + read_mode = (pipe_mode & PIPE_READMODE_MESSAGE) != 0; + non_block = (pipe_mode & PIPE_NOWAIT) != 0; + if (instances >= PIPE_UNLIMITED_INSTANCES) instances = ~0U; + + time.QuadPart = (ULONGLONG)timeout * -10000; + status = NtCreateNamedPipeFile( &handle, access, &attr, &iosb, sharing, + FILE_OPEN_IF, options, pipe_type, + read_mode, non_block, instances, in_buff, out_buff, &time ); + RtlFreeUnicodeString( &nt_name ); + if (!set_ntstatus( status )) return INVALID_HANDLE_VALUE; + SetLastError( iosb.Information == FILE_CREATED ? ERROR_SUCCESS : ERROR_ALREADY_EXISTS ); + return handle; +} + + +/****************************************************************** + * CreatePipe (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreatePipe( HANDLE *read_pipe, HANDLE *write_pipe, + SECURITY_ATTRIBUTES *sa, DWORD size ) +{ + static unsigned int index; + WCHAR name[64]; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK iosb; + LARGE_INTEGER timeout; + + *read_pipe = *write_pipe = INVALID_HANDLE_VALUE; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nt_name; + attr.Attributes = OBJ_CASE_INSENSITIVE | ((sa && sa->bInheritHandle) ? OBJ_INHERIT : 0); + attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL; + attr.SecurityQualityOfService = NULL; + + if (!size) size = 4096; + + timeout.QuadPart = (ULONGLONG)NMPWAIT_USE_DEFAULT_WAIT * -10000; + + /* generate a unique pipe name (system wide) */ + for (;;) + { + swprintf( name, ARRAY_SIZE(name), L"\\??\\pipe\\Win32.Pipes.%08lu.%08u", + GetCurrentProcessId(), ++index ); + RtlInitUnicodeString( &nt_name, name ); + if (!NtCreateNamedPipeFile( read_pipe, GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + &attr, &iosb, FILE_SHARE_WRITE, FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT, + FALSE, FALSE, FALSE, 1, size, size, &timeout )) + break; + } + if (!set_ntstatus( NtOpenFile( write_pipe, GENERIC_WRITE | FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, + &iosb, 0, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ))) + { + NtClose( *read_pipe ); + return FALSE; + } + return TRUE; +} + + +/*********************************************************************** + * DisconnectNamedPipe (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DisconnectNamedPipe( HANDLE pipe ) +{ + IO_STATUS_BLOCK io_block; + + TRACE( "(%p)\n", pipe ); + return set_ntstatus( NtFsControlFile( pipe, 0, NULL, NULL, &io_block, + FSCTL_PIPE_DISCONNECT, NULL, 0, NULL, 0 )); +} + + +/*********************************************************************** + * GetNamedPipeHandleStateW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetNamedPipeHandleStateW( HANDLE pipe, DWORD *state, DWORD *instances, + DWORD *max_count, DWORD *timeout, + WCHAR *user, DWORD size ) +{ + IO_STATUS_BLOCK io; + + FIXME( "%p %p %p %p %p %p %ld: semi-stub\n", pipe, state, instances, max_count, timeout, user, size ); + + if (max_count) *max_count = 0; + if (timeout) *timeout = 0; + if (user && size && !GetEnvironmentVariableW( L"WINEUSERNAME", user, size )) user[0] = 0; + + if (state) + { + FILE_PIPE_INFORMATION info; + + if (!set_ntstatus( NtQueryInformationFile( pipe, &io, &info, sizeof(info), FilePipeInformation ))) + return FALSE; + + *state = (info.ReadMode ? PIPE_READMODE_MESSAGE : PIPE_READMODE_BYTE) | + (info.CompletionMode ? PIPE_NOWAIT : PIPE_WAIT); + } + if (instances) + { + FILE_PIPE_LOCAL_INFORMATION info; + + if (!set_ntstatus( NtQueryInformationFile( pipe, &io, &info, sizeof(info), + FilePipeLocalInformation))) + return FALSE; + *instances = info.CurrentInstances; + } + return TRUE; +} + + +/*********************************************************************** + * GetNamedPipeInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetNamedPipeInfo( HANDLE pipe, LPDWORD flags, LPDWORD out_size, + LPDWORD in_size, LPDWORD instances ) +{ + FILE_PIPE_LOCAL_INFORMATION info; + IO_STATUS_BLOCK iosb; + + if (!set_ntstatus( NtQueryInformationFile( pipe, &iosb, &info, sizeof(info), FilePipeLocalInformation ))) + return FALSE; + + if (flags) + { + *flags = (info.NamedPipeEnd & FILE_PIPE_SERVER_END) ? PIPE_SERVER_END : PIPE_CLIENT_END; + *flags |= (info.NamedPipeType & FILE_PIPE_TYPE_MESSAGE) ? PIPE_TYPE_MESSAGE : PIPE_TYPE_BYTE; + } + if (out_size) *out_size = info.OutboundQuota; + if (in_size) *in_size = info.InboundQuota; + if (instances) *instances = info.MaximumInstances; + return TRUE; +} + + +/*********************************************************************** + * PeekNamedPipe (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH PeekNamedPipe( HANDLE pipe, LPVOID out_buffer, DWORD size, + LPDWORD read_size, LPDWORD avail, LPDWORD message ) +{ + FILE_PIPE_PEEK_BUFFER local_buffer; + FILE_PIPE_PEEK_BUFFER *buffer = &local_buffer; + IO_STATUS_BLOCK io; + NTSTATUS status; + + if (size && !(buffer = HeapAlloc( GetProcessHeap(), 0, + FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data[size] )))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + status = NtFsControlFile( pipe, 0, NULL, NULL, &io, FSCTL_PIPE_PEEK, NULL, 0, + buffer, FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data[size] ) ); + if (status == STATUS_BUFFER_OVERFLOW) status = STATUS_SUCCESS; + if (!status) + { + ULONG count = io.Information - FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data ); + if (avail) *avail = buffer->ReadDataAvailable; + if (read_size) *read_size = count; + if (message) *message = buffer->MessageLength - count; + if (out_buffer) memcpy( out_buffer, buffer->Data, count ); + } + else SetLastError( RtlNtStatusToDosError(status) ); + + if (buffer != &local_buffer) HeapFree( GetProcessHeap(), 0, buffer ); + return !status; +} + + +/*********************************************************************** + * SetNamedPipeHandleState (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetNamedPipeHandleState( HANDLE pipe, LPDWORD mode, + LPDWORD count, LPDWORD timeout ) +{ + FILE_PIPE_INFORMATION info; + IO_STATUS_BLOCK iosb; + NTSTATUS status = STATUS_SUCCESS; + + TRACE( "%p %p/%ld %p %p\n", pipe, mode, mode ? *mode : 0, count, timeout ); + if (count || timeout) FIXME( "Unsupported arguments\n" ); + + if (mode) + { + if (*mode & ~(PIPE_READMODE_MESSAGE | PIPE_NOWAIT)) status = STATUS_INVALID_PARAMETER; + else + { + info.CompletionMode = (*mode & PIPE_NOWAIT) ? + FILE_PIPE_COMPLETE_OPERATION : FILE_PIPE_QUEUE_OPERATION; + info.ReadMode = (*mode & PIPE_READMODE_MESSAGE) ? + FILE_PIPE_MESSAGE_MODE : FILE_PIPE_BYTE_STREAM_MODE; + status = NtSetInformationFile( pipe, &iosb, &info, sizeof(info), FilePipeInformation ); + } + } + return set_ntstatus( status ); +} + +/*********************************************************************** + * TransactNamedPipe (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH TransactNamedPipe( HANDLE handle, LPVOID write_buf, DWORD write_size, + LPVOID read_buf, DWORD read_size, LPDWORD bytes_read, + LPOVERLAPPED overlapped) +{ + IO_STATUS_BLOCK default_iosb, *iosb = &default_iosb; + HANDLE event = NULL; + void *cvalue = NULL; + NTSTATUS status; + + TRACE( "%p %p %lu %p %lu %p %p\n", handle, + write_buf, write_size, read_buf, read_size, bytes_read, overlapped ); + + if (overlapped) + { + event = overlapped->hEvent; + iosb = (IO_STATUS_BLOCK *)overlapped; + if (((ULONG_PTR)event & 1) == 0) cvalue = overlapped; + } + else + { + iosb->Information = 0; + } + + status = NtFsControlFile( handle, event, NULL, cvalue, iosb, FSCTL_PIPE_TRANSCEIVE, + write_buf, write_size, read_buf, read_size ); + if (status == STATUS_PENDING && !overlapped) + { + WaitForSingleObject(handle, INFINITE); + status = iosb->Status; + } + + if (bytes_read) *bytes_read = overlapped && status ? 0 : iosb->Information; + return set_ntstatus( status ); +} + + +/*********************************************************************** + * WaitNamedPipeW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH WaitNamedPipeW( LPCWSTR name, DWORD timeout ) +{ + static const int prefix_len = sizeof(L"\\??\\PIPE\\") - sizeof(WCHAR); + NTSTATUS status; + UNICODE_STRING nt_name, pipe_dev_name; + FILE_PIPE_WAIT_FOR_BUFFER *pipe_wait; + IO_STATUS_BLOCK iosb; + OBJECT_ATTRIBUTES attr; + ULONG wait_size; + HANDLE pipe_dev; + + TRACE( "%s 0x%08lx\n", debugstr_w(name), timeout ); + + if (!RtlDosPathNameToNtPathName_U( name, &nt_name, NULL, NULL )) return FALSE; + + if (nt_name.Length >= MAX_PATH * sizeof(WCHAR) || + nt_name.Length < prefix_len || + wcsnicmp( nt_name.Buffer, L"\\??\\PIPE\\", prefix_len / sizeof(WCHAR) )) + { + RtlFreeUnicodeString( &nt_name ); + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + + wait_size = offsetof( FILE_PIPE_WAIT_FOR_BUFFER, Name[(nt_name.Length - prefix_len) / sizeof(WCHAR)] ); + if (!(pipe_wait = HeapAlloc( GetProcessHeap(), 0, wait_size))) + { + RtlFreeUnicodeString( &nt_name ); + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + + pipe_dev_name.Buffer = nt_name.Buffer; + pipe_dev_name.Length = prefix_len; + pipe_dev_name.MaximumLength = prefix_len; + InitializeObjectAttributes( &attr,&pipe_dev_name, OBJ_CASE_INSENSITIVE, NULL, NULL ); + status = NtOpenFile( &pipe_dev, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, + &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT); + if (status != STATUS_SUCCESS) + { + HeapFree( GetProcessHeap(), 0, pipe_wait ); + RtlFreeUnicodeString( &nt_name ); + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + + pipe_wait->TimeoutSpecified = !(timeout == NMPWAIT_USE_DEFAULT_WAIT); + if (timeout == NMPWAIT_WAIT_FOREVER) + pipe_wait->Timeout.QuadPart = ((ULONGLONG)0x7fffffff << 32) | 0xffffffff; + else + pipe_wait->Timeout.QuadPart = (ULONGLONG)timeout * -10000; + pipe_wait->NameLength = nt_name.Length - prefix_len; + memcpy( pipe_wait->Name, nt_name.Buffer + prefix_len/sizeof(WCHAR), pipe_wait->NameLength ); + RtlFreeUnicodeString( &nt_name ); + + status = NtFsControlFile( pipe_dev, NULL, NULL, NULL, &iosb, FSCTL_PIPE_WAIT, + pipe_wait, wait_size, NULL, 0 ); + + HeapFree( GetProcessHeap(), 0, pipe_wait ); + NtClose( pipe_dev ); + return set_ntstatus( status ); +} + + + +/*********************************************************************** + * Interlocked functions + ***********************************************************************/ + + +/*********************************************************************** + * InitOnceBeginInitialize (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH InitOnceBeginInitialize( INIT_ONCE *once, DWORD flags, + BOOL *pending, void **context ) +{ + NTSTATUS status = RtlRunOnceBeginInitialize( once, flags, context ); + if (status >= 0) *pending = (status == STATUS_PENDING); + else SetLastError( RtlNtStatusToDosError(status) ); + return status >= 0; +} + + +/*********************************************************************** + * InitOnceComplete (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH InitOnceComplete( INIT_ONCE *once, DWORD flags, void *context ) +{ + return set_ntstatus( RtlRunOnceComplete( once, flags, context )); +} + + +/*********************************************************************** + * InitOnceExecuteOnce (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH InitOnceExecuteOnce( INIT_ONCE *once, PINIT_ONCE_FN func, + void *param, void **context ) +{ + return !RtlRunOnceExecuteOnce( once, (PRTL_RUN_ONCE_INIT_FN)func, param, context ); +} + +#ifdef __i386__ + +/*********************************************************************** + * InterlockedCompareExchange (kernelbase.@) + */ +__ASM_STDCALL_FUNC(InterlockedCompareExchange, 12, + "movl 12(%esp),%eax\n\t" + "movl 8(%esp),%ecx\n\t" + "movl 4(%esp),%edx\n\t" + "lock; cmpxchgl %ecx,(%edx)\n\t" + "ret $12") + +/*********************************************************************** + * InterlockedExchange (kernelbase.@) + */ +__ASM_STDCALL_FUNC(InterlockedExchange, 8, + "movl 8(%esp),%eax\n\t" + "movl 4(%esp),%edx\n\t" + "lock; xchgl %eax,(%edx)\n\t" + "ret $8") + +/*********************************************************************** + * InterlockedExchangeAdd (kernelbase.@) + */ +__ASM_STDCALL_FUNC(InterlockedExchangeAdd, 8, + "movl 8(%esp),%eax\n\t" + "movl 4(%esp),%edx\n\t" + "lock; xaddl %eax,(%edx)\n\t" + "ret $8") + +/*********************************************************************** + * InterlockedIncrement (kernelbase.@) + */ +__ASM_STDCALL_FUNC(InterlockedIncrement, 4, + "movl 4(%esp),%edx\n\t" + "movl $1,%eax\n\t" + "lock; xaddl %eax,(%edx)\n\t" + "incl %eax\n\t" + "ret $4") + +/*********************************************************************** + * InterlockedDecrement (kernelbase.@) + */ +__ASM_STDCALL_FUNC(InterlockedDecrement, 4, + "movl 4(%esp),%edx\n\t" + "movl $-1,%eax\n\t" + "lock; xaddl %eax,(%edx)\n\t" + "decl %eax\n\t" + "ret $4") + +#endif /* __i386__ */ diff --git a/dll/win32/KernelBase/wine/thread.c b/dll/win32/KernelBase/wine/thread.c new file mode 100644 index 0000000000000..12e1777262acb --- /dev/null +++ b/dll/win32/KernelBase/wine/thread.c @@ -0,0 +1,1367 @@ +/* + * Win32 threads + * + * Copyright 1996, 2002, 2019 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winternl.h" + +#include "kernelbase.h" +#include "wine/exception.h" +#include "wine/asm.h" +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(thread); + + +/*********************************************************************** + * Threads + ***********************************************************************/ + + +static DWORD rtlmode_to_win32mode( DWORD rtlmode ) +{ + DWORD win32mode = 0; + + if (rtlmode & 0x10) win32mode |= SEM_FAILCRITICALERRORS; + if (rtlmode & 0x20) win32mode |= SEM_NOGPFAULTERRORBOX; + if (rtlmode & 0x40) win32mode |= SEM_NOOPENFILEERRORBOX; + return win32mode; +} + + +/*************************************************************************** + * CreateRemoteThread (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateRemoteThread( HANDLE process, SECURITY_ATTRIBUTES *sa, SIZE_T stack, + LPTHREAD_START_ROUTINE start, LPVOID param, + DWORD flags, DWORD *id ) +{ + return CreateRemoteThreadEx( process, sa, stack, start, param, flags, NULL, id ); +} + + +/*************************************************************************** + * CreateRemoteThreadEx (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateRemoteThreadEx( HANDLE process, SECURITY_ATTRIBUTES *sa, + SIZE_T stack, LPTHREAD_START_ROUTINE start, + LPVOID param, DWORD flags, + LPPROC_THREAD_ATTRIBUTE_LIST attributes, DWORD *id ) +{ + HANDLE handle; + CLIENT_ID client_id; + SIZE_T stack_reserve = 0, stack_commit = 0; + + if (attributes) FIXME("thread attributes ignored\n"); + + if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack; + else stack_commit = stack; + + if (!set_ntstatus( RtlCreateUserThread( process, sa ? sa->lpSecurityDescriptor : NULL, TRUE, + 0, stack_reserve, stack_commit, + (PRTL_THREAD_START_ROUTINE)start, param, &handle, &client_id ))) + return 0; + + if (id) *id = HandleToULong( client_id.UniqueThread ); + if (sa && sa->nLength >= sizeof(*sa) && sa->bInheritHandle) + SetHandleInformation( handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT ); + if (!(flags & CREATE_SUSPENDED)) + { + ULONG ret; + if (NtResumeThread( handle, &ret )) + { + NtClose( handle ); + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + handle = 0; + } + } + return handle; +} + + +/*********************************************************************** + * CreateThread (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH CreateThread( SECURITY_ATTRIBUTES *sa, SIZE_T stack, + LPTHREAD_START_ROUTINE start, LPVOID param, + DWORD flags, LPDWORD id ) +{ + return CreateRemoteThread( GetCurrentProcess(), sa, stack, start, param, flags, id ); +} + + +/*********************************************************************** + * FreeLibraryAndExitThread (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH FreeLibraryAndExitThread( HINSTANCE module, DWORD exit_code ) +{ + FreeLibrary( module ); + RtlExitUserThread( exit_code ); +} + + +/*********************************************************************** + * GetCurrentThreadStackLimits (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH GetCurrentThreadStackLimits( ULONG_PTR *low, ULONG_PTR *high ) +{ + *low = (ULONG_PTR)NtCurrentTeb()->DeallocationStack; + *high = (ULONG_PTR)NtCurrentTeb()->Tib.StackBase; +} + + +/*********************************************************************** + * GetCurrentThread (kernelbase.@) + */ +HANDLE WINAPI kernelbase_GetCurrentThread(void) +{ + return (HANDLE)~(ULONG_PTR)1; +} + + +/*********************************************************************** + * GetCurrentThreadId (kernelbase.@) + */ +DWORD WINAPI kernelbase_GetCurrentThreadId(void) +{ + return HandleToULong( NtCurrentTeb()->ClientId.UniqueThread ); +} + + +/********************************************************************** + * GetExitCodeThread (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetExitCodeThread( HANDLE thread, LPDWORD exit_code ) +{ + THREAD_BASIC_INFORMATION info; + NTSTATUS status = NtQueryInformationThread( thread, ThreadBasicInformation, + &info, sizeof(info), NULL ); + if (!status && exit_code) *exit_code = info.ExitStatus; + return set_ntstatus( status ); +} + + +/********************************************************************** + * GetLastError (kernelbase.@) + */ +DWORD WINAPI kernelbase_GetLastError(void) +{ + return NtCurrentTeb()->LastErrorValue; +} + + +/********************************************************************** + * GetProcessIdOfThread (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetProcessIdOfThread( HANDLE thread ) +{ + THREAD_BASIC_INFORMATION tbi; + + if (!set_ntstatus( NtQueryInformationThread( thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL))) + return 0; + return HandleToULong( tbi.ClientId.UniqueProcess ); +} + + +/*********************************************************************** + * GetThreadContext (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetThreadContext( HANDLE thread, CONTEXT *context ) +{ + return set_ntstatus( NtGetContextThread( thread, context )); +} + + +/*********************************************************************** + * GetThreadErrorMode (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetThreadErrorMode(void) +{ + return rtlmode_to_win32mode( RtlGetThreadErrorMode() ); +} + + +/*********************************************************************** + * GetThreadGroupAffinity (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetThreadGroupAffinity( HANDLE thread, GROUP_AFFINITY *affinity ) +{ + if (!affinity) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + return set_ntstatus( NtQueryInformationThread( thread, ThreadGroupInformation, + affinity, sizeof(*affinity), NULL )); +} + + +/*********************************************************************** + * GetThreadIOPendingFlag (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetThreadIOPendingFlag( HANDLE thread, PBOOL pending ) +{ + return set_ntstatus( NtQueryInformationThread( thread, ThreadIsIoPending, + pending, sizeof(*pending), NULL )); +} + + +/********************************************************************** + * GetThreadId (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetThreadId( HANDLE thread ) +{ + THREAD_BASIC_INFORMATION tbi; + + if (!set_ntstatus( NtQueryInformationThread( thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL))) + return 0; + return HandleToULong( tbi.ClientId.UniqueThread ); +} + + +/*********************************************************************** + * GetThreadIdealProcessorEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetThreadIdealProcessorEx( HANDLE thread, PROCESSOR_NUMBER *ideal ) +{ + return set_ntstatus( NtQueryInformationThread( thread, ThreadIdealProcessorEx, ideal, sizeof(*ideal), NULL)); +} + + +/*********************************************************************** + * GetThreadLocale (kernelbase.@) + */ +LCID WINAPI /* DECLSPEC_HOTPATCH */ GetThreadLocale(void) +{ + LCID ret = NtCurrentTeb()->CurrentLocale; + if (!ret) NtCurrentTeb()->CurrentLocale = ret = GetUserDefaultLCID(); + return ret; +} + + +/********************************************************************** + * GetThreadPriority (kernelbase.@) + */ +INT WINAPI DECLSPEC_HOTPATCH GetThreadPriority( HANDLE thread ) +{ + THREAD_BASIC_INFORMATION info; + + if (!set_ntstatus( NtQueryInformationThread( thread, ThreadBasicInformation, + &info, sizeof(info), NULL ))) + return THREAD_PRIORITY_ERROR_RETURN; + return info.Priority; +} + + +/********************************************************************** + * GetThreadPriorityBoost (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetThreadPriorityBoost( HANDLE thread, BOOL *state ) +{ + return set_ntstatus( NtQueryInformationThread( thread, ThreadPriorityBoost, state, sizeof(*state), NULL )); +} + + +/********************************************************************** + * GetThreadTimes (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetThreadTimes( HANDLE thread, LPFILETIME creationtime, LPFILETIME exittime, + LPFILETIME kerneltime, LPFILETIME usertime ) +{ + KERNEL_USER_TIMES times; + + if (!set_ntstatus( NtQueryInformationThread( thread, ThreadTimes, ×, sizeof(times), NULL ))) + return FALSE; + + if (creationtime) + { + creationtime->dwLowDateTime = times.CreateTime.u.LowPart; + creationtime->dwHighDateTime = times.CreateTime.u.HighPart; + } + if (exittime) + { + exittime->dwLowDateTime = times.ExitTime.u.LowPart; + exittime->dwHighDateTime = times.ExitTime.u.HighPart; + } + if (kerneltime) + { + kerneltime->dwLowDateTime = times.KernelTime.u.LowPart; + kerneltime->dwHighDateTime = times.KernelTime.u.HighPart; + } + if (usertime) + { + usertime->dwLowDateTime = times.UserTime.u.LowPart; + usertime->dwHighDateTime = times.UserTime.u.HighPart; + } + return TRUE; +} + + +/*********************************************************************** + * GetThreadUILanguage (kernelbase.@) + */ +LANGID WINAPI DECLSPEC_HOTPATCH GetThreadUILanguage(void) +{ + LANGID lang; + + FIXME(": stub, returning default language.\n"); + NtQueryDefaultUILanguage( &lang ); + return lang; +} + + +/*********************************************************************** + * OpenThread (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH OpenThread( DWORD access, BOOL inherit, DWORD id ) +{ + HANDLE handle; + OBJECT_ATTRIBUTES attr; + CLIENT_ID cid; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = inherit ? OBJ_INHERIT : 0; + attr.ObjectName = NULL; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + cid.UniqueProcess = 0; + cid.UniqueThread = ULongToHandle( id ); + + if (!set_ntstatus( NtOpenThread( &handle, access, &attr, &cid ))) handle = 0; + return handle; +} + + +/* callback for QueueUserAPC */ +static void CALLBACK call_user_apc( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) +{ + PAPCFUNC func = (PAPCFUNC)arg1; + func( arg2 ); +} + +/*********************************************************************** + * QueueUserAPC (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH QueueUserAPC( PAPCFUNC func, HANDLE thread, ULONG_PTR data ) +{ + return set_ntstatus( NtQueueApcThread( thread, call_user_apc, (ULONG_PTR)func, data, 0 )); +} + + +/*********************************************************************** + * QueryThreadCycleTime (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryThreadCycleTime( HANDLE thread, ULONG64 *cycle ) +{ + static int once; + if (!once++) FIXME( "(%p,%p): stub!\n", thread, cycle ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/********************************************************************** + * ResumeThread (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH ResumeThread( HANDLE thread ) +{ + DWORD ret; + + if (!set_ntstatus( NtResumeThread( thread, &ret ))) ret = ~0U; + return ret; +} + + +/*********************************************************************** + * SetThreadContext (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadContext( HANDLE thread, const CONTEXT *context ) +{ + return set_ntstatus( NtSetContextThread( thread, context )); +} + + +/*********************************************************************** + * SetThreadDescription (kernelbase.@) + */ +HRESULT WINAPI DECLSPEC_HOTPATCH SetThreadDescription( HANDLE thread, PCWSTR description ) +{ + THREAD_NAME_INFORMATION info; + int length; + + TRACE( "(%p, %s)\n", thread, debugstr_w( description )); + + length = description ? lstrlenW( description ) * sizeof(WCHAR) : 0; + + if (length > USHRT_MAX) + return HRESULT_FROM_NT(STATUS_INVALID_PARAMETER); + + info.ThreadName.Length = info.ThreadName.MaximumLength = length; + info.ThreadName.Buffer = (WCHAR *)description; + + return HRESULT_FROM_NT(NtSetInformationThread( thread, ThreadNameInformation, &info, sizeof(info) )); +} + +/*********************************************************************** + * GetThreadDescription (kernelbase.@) + */ +HRESULT WINAPI DECLSPEC_HOTPATCH GetThreadDescription( HANDLE thread, WCHAR **description ) +{ + THREAD_NAME_INFORMATION *info; + NTSTATUS status; + ULONG length; + + TRACE( "(%p, %p)\n", thread, description ); + + *description = NULL; + + length = 0; + status = NtQueryInformationThread( thread, ThreadNameInformation, NULL, 0, &length ); + if (status != STATUS_BUFFER_TOO_SMALL) + return HRESULT_FROM_NT(status); + + if (!(info = heap_alloc( length ))) + return HRESULT_FROM_NT(STATUS_NO_MEMORY); + + status = NtQueryInformationThread( thread, ThreadNameInformation, info, length, &length ); + if (!status) + { + if (!(*description = LocalAlloc( 0, info->ThreadName.Length + sizeof(WCHAR)))) + status = STATUS_NO_MEMORY; + else + { + if (info->ThreadName.Length) + memcpy(*description, info->ThreadName.Buffer, info->ThreadName.Length); + (*description)[info->ThreadName.Length / sizeof(WCHAR)] = 0; + } + } + + heap_free(info); + + return HRESULT_FROM_NT(status); +} + +/*********************************************************************** + * SetThreadErrorMode (kernelbase.@) + */ +BOOL WINAPI SetThreadErrorMode( DWORD mode, DWORD *old ) +{ + NTSTATUS status; + DWORD new = 0; + + if (mode & ~(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (mode & SEM_FAILCRITICALERRORS) new |= 0x10; + if (mode & SEM_NOGPFAULTERRORBOX) new |= 0x20; + if (mode & SEM_NOOPENFILEERRORBOX) new |= 0x40; + + status = RtlSetThreadErrorMode( new, old ); + if (!status && old) *old = rtlmode_to_win32mode( *old ); + return set_ntstatus( status ); +} + + +/*********************************************************************** + * SetThreadGroupAffinity (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadGroupAffinity( HANDLE thread, const GROUP_AFFINITY *new, + GROUP_AFFINITY *old ) +{ + if (old && !GetThreadGroupAffinity( thread, old )) return FALSE; + return set_ntstatus( NtSetInformationThread( thread, ThreadGroupInformation, new, sizeof(*new) )); +} + + +/********************************************************************** + * SetThreadIdealProcessor (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH SetThreadIdealProcessor( HANDLE thread, DWORD proc ) +{ + NTSTATUS status; + + status = NtSetInformationThread( thread, ThreadIdealProcessor, &proc, sizeof(proc) ); + if (NT_SUCCESS(status)) return status; + + SetLastError( RtlNtStatusToDosError( status )); + return ~0u; +} + + +/*********************************************************************** + * SetThreadIdealProcessorEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadIdealProcessorEx( HANDLE thread, PROCESSOR_NUMBER *ideal, + PROCESSOR_NUMBER *previous ) +{ + FIXME( "(%p %p %p): stub\n", thread, ideal, previous ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; +} + + +/********************************************************************** + * SetThreadLocale (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadLocale( LCID lcid ) +{ + lcid = ConvertDefaultLocale( lcid ); + if (lcid != GetThreadLocale()) + { + if (!IsValidLocale( lcid, LCID_SUPPORTED )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + NtCurrentTeb()->CurrentLocale = lcid; + } + return TRUE; +} + + +/********************************************************************** + * SetThreadPriority (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPriority( HANDLE thread, INT priority ) +{ + DWORD prio = priority; + return set_ntstatus( NtSetInformationThread( thread, ThreadBasePriority, &prio, sizeof(prio) )); +} + + +/********************************************************************** + * SetThreadPriorityBoost (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadPriorityBoost( HANDLE thread, BOOL disable ) +{ + return set_ntstatus( NtSetInformationThread( thread, ThreadPriorityBoost, &disable, sizeof(disable) )); +} + + +/********************************************************************** + * SetThreadStackGuarantee (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadStackGuarantee( ULONG *size ) +{ + ULONG prev_size = NtCurrentTeb()->GuaranteedStackBytes; + ULONG new_size = (*size + 4095) & ~4095; + + /* at least 2 pages on 64-bit */ + if (sizeof(void *) > sizeof(int) && new_size) new_size = max( new_size, 8192 ); + + *size = prev_size; + if (new_size >= (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->DeallocationStack) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (new_size > prev_size) NtCurrentTeb()->GuaranteedStackBytes = (new_size + 4095) & ~4095; + return TRUE; +} + + +/********************************************************************** + * SetThreadUILanguage (kernelbase.@) + */ +LANGID WINAPI DECLSPEC_HOTPATCH SetThreadUILanguage( LANGID langid ) +{ + TRACE( "(0x%04x) stub - returning success\n", langid ); + + if (!langid) langid = GetThreadUILanguage(); + return langid; +} + + +/********************************************************************** + * SetThreadInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadInformation( HANDLE thread, THREAD_INFORMATION_CLASS info_class, + VOID *info, DWORD size ) +{ + switch (info_class) + { + case ThreadMemoryPriority: + return set_ntstatus( NtSetInformationThread( thread, ThreadPagePriority, info, size )); + case ThreadPowerThrottling: + return set_ntstatus( NtSetInformationThread( thread, ThreadPowerThrottlingState, info, size )); + default: + FIXME("Unsupported class %u.\n", info_class); + return FALSE; + } +} + + +/********************************************************************** + * SuspendThread (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH SuspendThread( HANDLE thread ) +{ + DWORD ret; + + if (!set_ntstatus( NtSuspendThread( thread, &ret ))) ret = ~0U; + return ret; +} + + +/*********************************************************************** + * SwitchToThread (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SwitchToThread(void) +{ + return (NtYieldExecution() != STATUS_NO_YIELD_PERFORMED); +} + + +/********************************************************************** + * TerminateThread (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH TerminateThread( HANDLE handle, DWORD exit_code ) +{ + return set_ntstatus( NtTerminateThread( handle, exit_code )); +} + + +/********************************************************************** + * TlsAlloc (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH TlsAlloc(void) +{ + DWORD index; + PEB * const peb = NtCurrentTeb()->Peb; + + RtlAcquirePebLock(); + index = RtlFindClearBitsAndSet( peb->TlsBitmap, 1, 1 ); + if (index != ~0U) NtCurrentTeb()->TlsSlots[index] = 0; /* clear the value */ + else + { + index = RtlFindClearBitsAndSet( peb->TlsExpansionBitmap, 1, 0 ); + if (index != ~0U) + { + if (!NtCurrentTeb()->TlsExpansionSlots && + !(NtCurrentTeb()->TlsExpansionSlots = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, + 8 * sizeof(peb->TlsExpansionBitmapBits) * sizeof(void*) ))) + { + RtlClearBits( peb->TlsExpansionBitmap, index, 1 ); + index = ~0U; + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + } + else + { + NtCurrentTeb()->TlsExpansionSlots[index] = 0; /* clear the value */ + index += TLS_MINIMUM_AVAILABLE; + } + } + else SetLastError( ERROR_NO_MORE_ITEMS ); + } + RtlReleasePebLock(); + return index; +} + + +/********************************************************************** + * TlsFree (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH TlsFree( DWORD index ) +{ + BOOL ret; + + RtlAcquirePebLock(); + if (index >= TLS_MINIMUM_AVAILABLE) + { + ret = RtlAreBitsSet( NtCurrentTeb()->Peb->TlsExpansionBitmap, index - TLS_MINIMUM_AVAILABLE, 1 ); + if (ret) RtlClearBits( NtCurrentTeb()->Peb->TlsExpansionBitmap, index - TLS_MINIMUM_AVAILABLE, 1 ); + } + else + { + ret = RtlAreBitsSet( NtCurrentTeb()->Peb->TlsBitmap, index, 1 ); + if (ret) RtlClearBits( NtCurrentTeb()->Peb->TlsBitmap, index, 1 ); + } + if (ret) NtSetInformationThread( GetCurrentThread(), ThreadZeroTlsCell, &index, sizeof(index) ); + else SetLastError( ERROR_INVALID_PARAMETER ); + RtlReleasePebLock(); + return ret; +} + + +/********************************************************************** + * TlsGetValue (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH TlsGetValue( DWORD index ) +{ + SetLastError( ERROR_SUCCESS ); + if (index < TLS_MINIMUM_AVAILABLE) return NtCurrentTeb()->TlsSlots[index]; + + index -= TLS_MINIMUM_AVAILABLE; + if (index >= 8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + if (!NtCurrentTeb()->TlsExpansionSlots) return NULL; + return NtCurrentTeb()->TlsExpansionSlots[index]; +} + + +/********************************************************************** + * TlsSetValue (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH TlsSetValue( DWORD index, LPVOID value ) +{ + if (index < TLS_MINIMUM_AVAILABLE) + { + NtCurrentTeb()->TlsSlots[index] = value; + } + else + { + index -= TLS_MINIMUM_AVAILABLE; + if (index >= 8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits)) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (!NtCurrentTeb()->TlsExpansionSlots && + !(NtCurrentTeb()->TlsExpansionSlots = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, + 8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits) * sizeof(void*) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + NtCurrentTeb()->TlsExpansionSlots[index] = value; + } + return TRUE; +} + + +/*********************************************************************** + * Wow64GetThreadContext (kernelbase.@) + */ +BOOL WINAPI Wow64GetThreadContext( HANDLE handle, WOW64_CONTEXT *context) +{ +#ifdef __i386__ + return set_ntstatus( NtGetContextThread( handle, (CONTEXT *)context )); +#elif defined(__x86_64__) + return set_ntstatus( RtlWow64GetThreadContext( handle, context )); +#else + return set_ntstatus( STATUS_NOT_IMPLEMENTED ); +#endif +} + + +/*********************************************************************** + * Wow64SetThreadContext (kernelbase.@) + */ +BOOL WINAPI Wow64SetThreadContext( HANDLE handle, const WOW64_CONTEXT *context) +{ +#ifdef __i386__ + return set_ntstatus( NtSetContextThread( handle, (const CONTEXT *)context )); +#elif defined(__x86_64__) + return set_ntstatus( RtlWow64SetThreadContext( handle, context )); +#else + return set_ntstatus( STATUS_NOT_IMPLEMENTED ); +#endif +} + + +/*********************************************************************** + * Fibers + ***********************************************************************/ + + +struct fiber_actctx +{ + ACTIVATION_CONTEXT_STACK stack_space; /* activation context stack space */ + ACTIVATION_CONTEXT_STACK *stack_ptr; /* last value of ActivationContextStackPointer */ +}; + +struct fiber_data +{ + LPVOID param; /* 00/00 fiber param */ + void *except; /* 04/08 saved exception handlers list */ + void *stack_base; /* 08/10 top of fiber stack */ + void *stack_limit; /* 0c/18 fiber stack low-water mark */ + void *stack_allocation; /* 10/20 base of the fiber stack allocation */ + CONTEXT context; /* 14/30 fiber context */ + DWORD flags; /* fiber flags */ + LPFIBER_START_ROUTINE start; /* start routine */ + void *fls_slots; /* fiber storage slots */ + struct fiber_actctx actctx; /* activation context state */ +}; + +#ifdef __i386__ +extern void WINAPI switch_fiber( CONTEXT *old, CONTEXT *new ); +__ASM_STDCALL_FUNC( switch_fiber, 8, + "movl 4(%esp),%ecx\n\t" /* old */ + "movl %edi,0x9c(%ecx)\n\t" /* old->Edi */ + "movl %esi,0xa0(%ecx)\n\t" /* old->Esi */ + "movl %ebx,0xa4(%ecx)\n\t" /* old->Ebx */ + "movl %ebp,0xb4(%ecx)\n\t" /* old->Ebp */ + "movl 0(%esp),%eax\n\t" + "movl %eax,0xb8(%ecx)\n\t" /* old->Eip */ + "leal 12(%esp),%eax\n\t" + "movl %eax,0xc4(%ecx)\n\t" /* old->Esp */ + "movl 8(%esp),%ecx\n\t" /* new */ + "movl 0x9c(%ecx),%edi\n\t" /* new->Edi */ + "movl 0xa0(%ecx),%esi\n\t" /* new->Esi */ + "movl 0xa4(%ecx),%ebx\n\t" /* new->Ebx */ + "movl 0xb4(%ecx),%ebp\n\t" /* new->Ebp */ + "movl 0xc4(%ecx),%esp\n\t" /* new->Esp */ + "jmp *0xb8(%ecx)" ) /* new->Eip */ +#elif defined(__arm64ec__) +static void __attribute__((naked)) WINAPI switch_fiber( CONTEXT *old, CONTEXT *new ) +{ + asm( "mov x2, sp\n\t" + "stp x27, x2, [x0, #0x90]\n\t" /* old->Rbx,Rsp */ + "str x29, [x0, #0xa0]\n\t" /* old->Rbp */ + "stp x25, x26, [x0, #0xa8]\n\t" /* old->Rsi,Rdi */ + "stp x19, x20, [x0, #0xd8]\n\t" /* old->R12,R13 */ + "stp x21, x22, [x0, #0xe8]\n\t" /* old->R14,R15 */ + "str x30, [x0, #0xf8]\n\t" /* old->Rip */ + "stp q8, q9, [x0, #0x220]\n\t" /* old->Xmm8,Xmm9 */ + "stp q10, q11, [x0, #0x240]\n\t" /* old->Xmm10,Xmm11 */ + "stp q12, q13, [x0, #0x260]\n\t" /* old->Xmm12,Xmm13 */ + "stp q14, q15, [x0, #0x280]\n\t" /* old->Xmm14,Xmm15 */ + /* FIXME: MxCsr */ + "ldp x27, x2, [x1, #0x90]\n\t" /* old->Rbx,Rsp */ + "ldr x29, [x1, #0xa0]\n\t" /* old->Rbp */ + "ldp x25, x26, [x1, #0xa8]\n\t" /* old->Rsi,Rdi */ + "ldp x19, x20, [x1, #0xd8]\n\t" /* old->R12,R13 */ + "ldp x21, x22, [x1, #0xe8]\n\t" /* old->R14,R15 */ + "ldr x30, [x1, #0xf8]\n\t" /* old->Rip */ + "ldp q8, q9, [x1, #0x220]\n\t" /* old->Xmm8,Xmm9 */ + "ldp q10, q11, [x1, #0x240]\n\t" /* old->Xmm10,Xmm11 */ + "ldp q12, q13, [x1, #0x260]\n\t" /* old->Xmm12,Xmm13 */ + "ldp q14, q15, [x1, #0x280]\n\t" /* old->Xmm14,Xmm15 */ + "mov sp, x2\n\t" + "ret" ); +} +#elif defined(__x86_64__) +extern void WINAPI switch_fiber( CONTEXT *old, CONTEXT *new ); +__ASM_GLOBAL_FUNC( switch_fiber, + "movq %rbx,0x90(%rcx)\n\t" /* old->Rbx */ + "leaq 0x8(%rsp),%rax\n\t" + "movq %rax,0x98(%rcx)\n\t" /* old->Rsp */ + "movq %rbp,0xa0(%rcx)\n\t" /* old->Rbp */ + "movq %rsi,0xa8(%rcx)\n\t" /* old->Rsi */ + "movq %rdi,0xb0(%rcx)\n\t" /* old->Rdi */ + "movq %r12,0xd8(%rcx)\n\t" /* old->R12 */ + "movq %r13,0xe0(%rcx)\n\t" /* old->R13 */ + "movq %r14,0xe8(%rcx)\n\t" /* old->R14 */ + "movq %r15,0xf0(%rcx)\n\t" /* old->R15 */ + "movq (%rsp),%rax\n\t" + "movq %rax,0xf8(%rcx)\n\t" /* old->Rip */ + "movdqa %xmm6,0x200(%rcx)\n\t" /* old->Xmm6 */ + "movdqa %xmm7,0x210(%rcx)\n\t" /* old->Xmm7 */ + "movdqa %xmm8,0x220(%rcx)\n\t" /* old->Xmm8 */ + "movdqa %xmm9,0x230(%rcx)\n\t" /* old->Xmm9 */ + "movdqa %xmm10,0x240(%rcx)\n\t" /* old->Xmm10 */ + "movdqa %xmm11,0x250(%rcx)\n\t" /* old->Xmm11 */ + "movdqa %xmm12,0x260(%rcx)\n\t" /* old->Xmm12 */ + "movdqa %xmm13,0x270(%rcx)\n\t" /* old->Xmm13 */ + "movdqa %xmm14,0x280(%rcx)\n\t" /* old->Xmm14 */ + "movdqa %xmm15,0x290(%rcx)\n\t" /* old->Xmm15 */ + "movq 0x90(%rdx),%rbx\n\t" /* new->Rbx */ + "movq 0xa0(%rdx),%rbp\n\t" /* new->Rbp */ + "movq 0xa8(%rdx),%rsi\n\t" /* new->Rsi */ + "movq 0xb0(%rdx),%rdi\n\t" /* new->Rdi */ + "movq 0xd8(%rdx),%r12\n\t" /* new->R12 */ + "movq 0xe0(%rdx),%r13\n\t" /* new->R13 */ + "movq 0xe8(%rdx),%r14\n\t" /* new->R14 */ + "movq 0xf0(%rdx),%r15\n\t" /* new->R15 */ + "movdqa 0x200(%rdx),%xmm6\n\t" /* new->Xmm6 */ + "movdqa 0x210(%rdx),%xmm7\n\t" /* new->Xmm7 */ + "movdqa 0x220(%rdx),%xmm8\n\t" /* new->Xmm8 */ + "movdqa 0x230(%rdx),%xmm9\n\t" /* new->Xmm9 */ + "movdqa 0x240(%rdx),%xmm10\n\t" /* new->Xmm10 */ + "movdqa 0x250(%rdx),%xmm11\n\t" /* new->Xmm11 */ + "movdqa 0x260(%rdx),%xmm12\n\t" /* new->Xmm12 */ + "movdqa 0x270(%rdx),%xmm13\n\t" /* new->Xmm13 */ + "movdqa 0x280(%rdx),%xmm14\n\t" /* new->Xmm14 */ + "movdqa 0x290(%rdx),%xmm15\n\t" /* new->Xmm15 */ + "movq 0x98(%rdx),%rsp\n\t" /* new->Rsp */ + "jmp *0xf8(%rdx)" ) /* new->Rip */ +#elif defined(__arm__) +extern void WINAPI switch_fiber( CONTEXT *old, CONTEXT *new ); +__ASM_GLOBAL_FUNC( switch_fiber, + "str r4, [r0, #0x14]\n\t" /* old->R4 */ + "str r5, [r0, #0x18]\n\t" /* old->R5 */ + "str r6, [r0, #0x1c]\n\t" /* old->R6 */ + "str r7, [r0, #0x20]\n\t" /* old->R7 */ + "str r8, [r0, #0x24]\n\t" /* old->R8 */ + "str r9, [r0, #0x28]\n\t" /* old->R9 */ + "str r10, [r0, #0x2c]\n\t" /* old->R10 */ + "str r11, [r0, #0x30]\n\t" /* old->R11 */ + "str sp, [r0, #0x38]\n\t" /* old->Sp */ + "str lr, [r0, #0x40]\n\t" /* old->Pc */ + "ldr r4, [r1, #0x14]\n\t" /* new->R4 */ + "ldr r5, [r1, #0x18]\n\t" /* new->R5 */ + "ldr r6, [r1, #0x1c]\n\t" /* new->R6 */ + "ldr r7, [r1, #0x20]\n\t" /* new->R7 */ + "ldr r8, [r1, #0x24]\n\t" /* new->R8 */ + "ldr r9, [r1, #0x28]\n\t" /* new->R9 */ + "ldr r10, [r1, #0x2c]\n\t" /* new->R10 */ + "ldr r11, [r1, #0x30]\n\t" /* new->R11 */ + "ldr sp, [r1, #0x38]\n\t" /* new->Sp */ + "ldr r2, [r1, #0x40]\n\t" /* new->Pc */ + "bx r2" ) +#elif defined(__aarch64__) +extern void WINAPI switch_fiber( CONTEXT *old, CONTEXT *new ); +__ASM_GLOBAL_FUNC( switch_fiber, + "stp x19, x20, [x0, #0xa0]\n\t" /* old->X19,X20 */ + "stp x21, x22, [x0, #0xb0]\n\t" /* old->X21,X22 */ + "stp x23, x24, [x0, #0xc0]\n\t" /* old->X23,X24 */ + "stp x25, x26, [x0, #0xd0]\n\t" /* old->X25,X26 */ + "stp x27, x28, [x0, #0xe0]\n\t" /* old->X27,X28 */ + "str x29, [x0, #0xf0]\n\t" /* old->Fp */ + "mov x2, sp\n\t" + "str x2, [x0, #0x100]\n\t" /* old->Sp */ + "str x30, [x0, #0x108]\n\t" /* old->Pc */ + "ldp x19, x20, [x1, #0xa0]\n\t" /* new->X19,X20 */ + "ldp x21, x22, [x1, #0xb0]\n\t" /* new->X21,X22 */ + "ldp x23, x24, [x1, #0xc0]\n\t" /* new->X23,X24 */ + "ldp x25, x26, [x1, #0xd0]\n\t" /* new->X25,X26 */ + "ldp x27, x28, [x1, #0xe0]\n\t" /* new->X27,X28 */ + "ldr x29, [x1, #0xf0]\n\t" /* new->Fp */ + "ldr x2, [x1, #0x100]\n\t" /* new->Sp */ + "ldr x30, [x1, #0x108]\n\t" /* new->Pc */ + "mov sp, x2\n\t" + "ret" ) +#else +static void WINAPI switch_fiber( CONTEXT *old, CONTEXT *new ) +{ + FIXME( "not implemented\n" ); + DbgBreakPoint(); +} +#endif + +/* call the fiber initial function once we have switched stack */ +static void CDECL start_fiber(void) +{ + struct fiber_data *fiber = NtCurrentTeb()->Tib.FiberData; + LPFIBER_START_ROUTINE start = fiber->start; + + __TRY + { + start( fiber->param ); + RtlExitUserThread( 1 ); + } + __EXCEPT(UnhandledExceptionFilter) + { + TerminateThread( GetCurrentThread(), GetExceptionCode() ); + } + __ENDTRY +} + +static void init_fiber_context( struct fiber_data *fiber ) +{ +#ifdef __i386__ + fiber->context.Esp = (ULONG_PTR)fiber->stack_base - 4; + fiber->context.Eip = (ULONG_PTR)start_fiber; +#elif defined(__arm64ec__) + fiber->context.Rsp = (ULONG_PTR)fiber->stack_base; + fiber->context.Rip = (ULONG_PTR)start_fiber; +#elif defined(__x86_64__) + fiber->context.Rsp = (ULONG_PTR)fiber->stack_base - 0x28; + fiber->context.Rip = (ULONG_PTR)start_fiber; +#elif defined(__arm__) + fiber->context.Sp = (ULONG_PTR)fiber->stack_base; + fiber->context.Pc = (ULONG_PTR)start_fiber; +#elif defined(__aarch64__) + fiber->context.Sp = (ULONG_PTR)fiber->stack_base; + fiber->context.Pc = (ULONG_PTR)start_fiber; +#endif +} + +static void move_list( LIST_ENTRY *dest, LIST_ENTRY *src ) +{ + LIST_ENTRY *head = src->Flink; + LIST_ENTRY *tail = src->Blink; + + if (src != head) + { + dest->Flink = head; + dest->Blink = tail; + head->Blink = dest; + tail->Flink = dest; + } + else InitializeListHead( dest ); +} + +static void relocate_thread_actctx_stack( ACTIVATION_CONTEXT_STACK *dest ) +{ + ACTIVATION_CONTEXT_STACK *src = NtCurrentTeb()->ActivationContextStackPointer; + + C_ASSERT(sizeof(*dest) == sizeof(dest->ActiveFrame) + sizeof(dest->FrameListCache) + + sizeof(dest->Flags) + sizeof(dest->NextCookieSequenceNumber) + + sizeof(dest->StackId)); + + dest->ActiveFrame = src->ActiveFrame; + move_list( &dest->FrameListCache, &src->FrameListCache ); + dest->Flags = src->Flags; + dest->NextCookieSequenceNumber = src->NextCookieSequenceNumber; + dest->StackId = src->StackId; + + NtCurrentTeb()->ActivationContextStackPointer = dest; +} + + +/*********************************************************************** + * CreateFiber (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH CreateFiber( SIZE_T stack, LPFIBER_START_ROUTINE start, LPVOID param ) +{ + return CreateFiberEx( stack, 0, 0, start, param ); +} + + +/*********************************************************************** + * CreateFiberEx (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH CreateFiberEx( SIZE_T stack_commit, SIZE_T stack_reserve, DWORD flags, + LPFIBER_START_ROUTINE start, LPVOID param ) +{ + struct fiber_data *fiber; + INITIAL_TEB stack; + + if (!(fiber = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*fiber) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return NULL; + } + + if (!set_ntstatus( RtlCreateUserStack( stack_commit, stack_reserve, 0, 1, 1, &stack ))) + { + HeapFree( GetProcessHeap(), 0, fiber ); + return NULL; + } + + fiber->stack_allocation = stack.DeallocationStack; + fiber->stack_base = stack.StackBase; + fiber->stack_limit = stack.StackLimit; + fiber->param = param; + fiber->except = (void *)-1; + fiber->start = start; + fiber->flags = flags; + InitializeListHead( &fiber->actctx.stack_space.FrameListCache ); + fiber->actctx.stack_ptr = &fiber->actctx.stack_space; + init_fiber_context( fiber ); + return fiber; +} + + +/*********************************************************************** + * ConvertFiberToThread (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ConvertFiberToThread(void) +{ + struct fiber_data *fiber = NtCurrentTeb()->Tib.FiberData; + + if (fiber) + { + relocate_thread_actctx_stack( &NtCurrentTeb()->ActivationContextStack ); + NtCurrentTeb()->Tib.FiberData = NULL; + HeapFree( GetProcessHeap(), 0, fiber ); + } + return TRUE; +} + + +/*********************************************************************** + * ConvertThreadToFiber (kernelbase.@) + */ +LPVOID WINAPI /* DECLSPEC_HOTPATCH */ ConvertThreadToFiber( LPVOID param ) +{ + return ConvertThreadToFiberEx( param, 0 ); +} + + +/*********************************************************************** + * ConvertThreadToFiberEx (kernelbase.@) + */ +LPVOID WINAPI DECLSPEC_HOTPATCH ConvertThreadToFiberEx( LPVOID param, DWORD flags ) +{ + struct fiber_data *fiber; + + if (NtCurrentTeb()->Tib.FiberData) + { + SetLastError( ERROR_ALREADY_FIBER ); + return NULL; + } + + if (!(fiber = HeapAlloc( GetProcessHeap(), 0, sizeof(*fiber) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return NULL; + } + fiber->param = param; + fiber->except = NtCurrentTeb()->Tib.ExceptionList; + fiber->stack_base = NtCurrentTeb()->Tib.StackBase; + fiber->stack_limit = NtCurrentTeb()->Tib.StackLimit; + fiber->stack_allocation = NtCurrentTeb()->DeallocationStack; + fiber->start = NULL; + fiber->flags = flags; + fiber->fls_slots = NtCurrentTeb()->FlsSlots; + relocate_thread_actctx_stack( &fiber->actctx.stack_space ); + NtCurrentTeb()->Tib.FiberData = fiber; + return fiber; +} + + +/*********************************************************************** + * DeleteFiber (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH DeleteFiber( LPVOID fiber_ptr ) +{ + struct fiber_data *fiber = fiber_ptr; + + if (!fiber) return; + if (fiber == NtCurrentTeb()->Tib.FiberData) + { + relocate_thread_actctx_stack( &NtCurrentTeb()->ActivationContextStack ); + HeapFree( GetProcessHeap(), 0, fiber ); + RtlExitUserThread( 1 ); + } + RtlFreeUserStack( fiber->stack_allocation ); + RtlProcessFlsData( fiber->fls_slots, 3 ); + RtlFreeActivationContextStack( &fiber->actctx.stack_space ); + HeapFree( GetProcessHeap(), 0, fiber ); +} + + +/*********************************************************************** + * IsThreadAFiber (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH IsThreadAFiber(void) +{ + return NtCurrentTeb()->Tib.FiberData != NULL; +} + + +/*********************************************************************** + * SwitchToFiber (kernelbase.@) + */ +void WINAPI DECLSPEC_HOTPATCH SwitchToFiber( LPVOID fiber ) +{ + struct fiber_data *new_fiber = fiber; + struct fiber_data *current_fiber = NtCurrentTeb()->Tib.FiberData; + + current_fiber->except = NtCurrentTeb()->Tib.ExceptionList; + current_fiber->stack_limit = NtCurrentTeb()->Tib.StackLimit; + current_fiber->fls_slots = NtCurrentTeb()->FlsSlots; + current_fiber->actctx.stack_ptr = NtCurrentTeb()->ActivationContextStackPointer; + /* stack_allocation and stack_base never change */ + + /* FIXME: should save floating point context if requested in fiber->flags */ + NtCurrentTeb()->Tib.FiberData = new_fiber; + NtCurrentTeb()->Tib.ExceptionList = new_fiber->except; + NtCurrentTeb()->Tib.StackBase = new_fiber->stack_base; + NtCurrentTeb()->Tib.StackLimit = new_fiber->stack_limit; + NtCurrentTeb()->DeallocationStack = new_fiber->stack_allocation; + NtCurrentTeb()->FlsSlots = new_fiber->fls_slots; + NtCurrentTeb()->ActivationContextStackPointer = new_fiber->actctx.stack_ptr; + switch_fiber( ¤t_fiber->context, &new_fiber->context ); +} + + +/*********************************************************************** + * FlsAlloc (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH FlsAlloc( PFLS_CALLBACK_FUNCTION callback ) +{ + DWORD index; + + if (!set_ntstatus( RtlFlsAlloc( callback, &index ))) return FLS_OUT_OF_INDEXES; + return index; +} + + +/*********************************************************************** + * FlsFree (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FlsFree( DWORD index ) +{ + return set_ntstatus( RtlFlsFree( index )); +} + + +/*********************************************************************** + * FlsGetValue (kernelbase.@) + */ +PVOID WINAPI DECLSPEC_HOTPATCH FlsGetValue( DWORD index ) +{ + void *data; + + if (!set_ntstatus( RtlFlsGetValue( index, &data ))) return NULL; + SetLastError( ERROR_SUCCESS ); + return data; +} + + +/*********************************************************************** + * FlsSetValue (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FlsSetValue( DWORD index, PVOID data ) +{ + return set_ntstatus( RtlFlsSetValue( index, data )); +} + + +/*********************************************************************** + * Thread pool + ***********************************************************************/ + + +/*********************************************************************** + * CallbackMayRunLong (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CallbackMayRunLong( TP_CALLBACK_INSTANCE *instance ) +{ + return set_ntstatus( TpCallbackMayRunLong( instance )); +} + + +/*********************************************************************** + * CreateThreadpool (kernelbase.@) + */ +PTP_POOL WINAPI DECLSPEC_HOTPATCH CreateThreadpool( void *reserved ) +{ + TP_POOL *pool; + + if (!set_ntstatus( TpAllocPool( &pool, reserved ))) pool = NULL; + return pool; +} + + +/*********************************************************************** + * CreateThreadpoolCleanupGroup (kernelbase.@) + */ +PTP_CLEANUP_GROUP WINAPI DECLSPEC_HOTPATCH CreateThreadpoolCleanupGroup(void) +{ + TP_CLEANUP_GROUP *group; + + if (!set_ntstatus( TpAllocCleanupGroup( &group ))) return NULL; + return group; +} + + +static void WINAPI tp_io_callback( TP_CALLBACK_INSTANCE *instance, void *userdata, void *cvalue, IO_STATUS_BLOCK *iosb, TP_IO *io ) +{ + PTP_WIN32_IO_CALLBACK callback = *(void **)io; + callback( instance, userdata, cvalue, RtlNtStatusToDosError( iosb->Status ), iosb->Information, io ); +} + + +/*********************************************************************** + * CreateThreadpoolIo (kernelbase.@) + */ +PTP_IO WINAPI DECLSPEC_HOTPATCH CreateThreadpoolIo( HANDLE handle, PTP_WIN32_IO_CALLBACK callback, + PVOID userdata, TP_CALLBACK_ENVIRON *environment ) +{ + TP_IO *io; + if (!set_ntstatus( TpAllocIoCompletion( &io, handle, tp_io_callback, userdata, environment ))) return NULL; + *(void **)io = callback; /* ntdll leaves us space to store our callback at the beginning of TP_IO struct */ + return io; +} + + +/*********************************************************************** + * CreateThreadpoolTimer (kernelbase.@) + */ +PTP_TIMER WINAPI DECLSPEC_HOTPATCH CreateThreadpoolTimer( PTP_TIMER_CALLBACK callback, PVOID userdata, + TP_CALLBACK_ENVIRON *environment ) +{ + TP_TIMER *timer; + + if (!set_ntstatus( TpAllocTimer( &timer, callback, userdata, environment ))) return NULL; + return timer; +} + + +/*********************************************************************** + * CreateThreadpoolWait (kernelbase.@) + */ +PTP_WAIT WINAPI DECLSPEC_HOTPATCH CreateThreadpoolWait( PTP_WAIT_CALLBACK callback, PVOID userdata, + TP_CALLBACK_ENVIRON *environment ) +{ + TP_WAIT *wait; + + if (!set_ntstatus( TpAllocWait( &wait, callback, userdata, environment ))) return NULL; + return wait; +} + + +/*********************************************************************** + * CreateThreadpoolWork (kernelbase.@) + */ +PTP_WORK WINAPI DECLSPEC_HOTPATCH CreateThreadpoolWork( PTP_WORK_CALLBACK callback, PVOID userdata, + TP_CALLBACK_ENVIRON *environment ) +{ + TP_WORK *work; + + if (!set_ntstatus( TpAllocWork( &work, callback, userdata, environment ))) return NULL; + return work; +} + + +/*********************************************************************** + * TrySubmitThreadpoolCallback (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH TrySubmitThreadpoolCallback( PTP_SIMPLE_CALLBACK callback, PVOID userdata, + TP_CALLBACK_ENVIRON *environment ) +{ + return set_ntstatus( TpSimpleTryPost( callback, userdata, environment )); +} + + +/*********************************************************************** + * QueueUserWorkItem (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueueUserWorkItem( LPTHREAD_START_ROUTINE func, PVOID context, ULONG flags ) +{ + return set_ntstatus( RtlQueueWorkItem( func, context, flags )); +} + +/*********************************************************************** + * SetThreadpoolStackInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadpoolStackInformation( PTP_POOL pool, PTP_POOL_STACK_INFORMATION stack_info ) +{ + return set_ntstatus( TpSetPoolStackInformation( pool, stack_info )); +} + +/*********************************************************************** + * QueryThreadpoolStackInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH QueryThreadpoolStackInformation( PTP_POOL pool, PTP_POOL_STACK_INFORMATION stack_info ) +{ + return set_ntstatus( TpQueryPoolStackInformation( pool, stack_info )); +} diff --git a/dll/win32/KernelBase/wine/version.c b/dll/win32/KernelBase/wine/version.c new file mode 100644 index 0000000000000..0636634badb27 --- /dev/null +++ b/dll/win32/KernelBase/wine/version.c @@ -0,0 +1,1746 @@ +/* + * Implementation of VERSION.DLL + * + * Copyright 1996,1997 Marcus Meissner + * Copyright 1997 David Cuthbert + * Copyright 1999 Ulrich Weigand + * Copyright 2005 Paul Vriens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include +#include +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winver.h" +#include "winuser.h" +#include "winnls.h" +#include "winternl.h" +#include "winerror.h" +#include "appmodel.h" + +#include "kernelbase.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ver); + +typedef struct +{ + WORD offset; + WORD length; + WORD flags; + WORD id; + WORD handle; + WORD usage; +} NE_NAMEINFO; + +typedef struct +{ + WORD type_id; + WORD count; + DWORD resloader; +} NE_TYPEINFO; + +struct version_info +{ + DWORD major; + DWORD minor; + DWORD build; +}; + +/*********************************************************************** + * Version Info Structure + */ + +typedef struct +{ + WORD wLength; + WORD wValueLength; + CHAR szKey[1]; +#if 0 /* variable length structure */ + /* DWORD aligned */ + BYTE Value[]; + /* DWORD aligned */ + VS_VERSION_INFO_STRUCT16 Children[]; +#endif +} VS_VERSION_INFO_STRUCT16; + +typedef struct +{ + WORD wLength; + WORD wValueLength; + WORD wType; /* 1:Text, 0:Binary */ + WCHAR szKey[1]; +#if 0 /* variable length structure */ + /* DWORD aligned */ + BYTE Value[]; + /* DWORD aligned */ + VS_VERSION_INFO_STRUCT32 Children[]; +#endif +} VS_VERSION_INFO_STRUCT32; + +#define VersionInfoIs16( ver ) \ + ( ((const VS_VERSION_INFO_STRUCT16 *)ver)->szKey[0] >= ' ' ) + +#define DWORD_ALIGN( base, ptr ) \ + ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) ) + +#define VersionInfo16_Value( ver ) \ + DWORD_ALIGN( (ver), (ver)->szKey + strlen((ver)->szKey) + 1 ) +#define VersionInfo32_Value( ver ) \ + DWORD_ALIGN( (ver), (ver)->szKey + lstrlenW((ver)->szKey) + 1 ) + +#define VersionInfo16_Children( ver ) \ + (const VS_VERSION_INFO_STRUCT16 *)( VersionInfo16_Value( ver ) + \ + ( ( (ver)->wValueLength + 3 ) & ~3 ) ) +#define VersionInfo32_Children( ver ) \ + (const VS_VERSION_INFO_STRUCT32 *)( VersionInfo32_Value( ver ) + \ + ( ( (ver)->wValueLength * \ + ((ver)->wType? 2 : 1) + 3 ) & ~3 ) ) + +#define VersionInfo16_Next( ver ) \ + (VS_VERSION_INFO_STRUCT16 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) ) +#define VersionInfo32_Next( ver ) \ + (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) ) + + +/*********************************************************************** + * Win8 info, reported if the app doesn't provide compat GUID in the manifest and + * doesn't have higher OS version in PE header. + */ +static const struct version_info windows8_version_info = { 6, 2, 9200 }; + +/*********************************************************************** + * Win8.1 info, reported if the app doesn't provide compat GUID in the manifest and + * OS version in PE header is 8.1 or higher but below 10. + */ +static const struct version_info windows8_1_version_info = { 6, 3, 9600 }; + + +/*********************************************************************** + * Windows versions that need compatibility GUID specified in manifest + * in order to be reported by the APIs. + */ +static const struct +{ + struct version_info info; + GUID guid; +} version_data[] = +{ + /* Windows 8.1 */ + { + { 6, 3, 9600 }, + {0x1f676c76,0x80e1,0x4239,{0x95,0xbb,0x83,0xd0,0xf6,0xd0,0xda,0x78}} + }, + /* Windows 10 */ + { + { 10, 0, 19043 }, + {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} + } +}; + + +/****************************************************************************** + * init_current_version + * + * Initialize the current_version variable. + * + * For compatibility, Windows 8.1 and later report Win8 version unless the app + * has a manifest or higher OS version in the PE optional header + * that confirms its compatibility with newer versions of Windows. + * + */ +static RTL_OSVERSIONINFOEXW current_version; + +static BOOL CALLBACK init_current_version(PINIT_ONCE init_once, PVOID parameter, PVOID *context) +{ + struct acci + { + DWORD ElementCount; + COMPATIBILITY_CONTEXT_ELEMENT Elements[1]; + } *acci; + BOOL have_os_compat_elements = FALSE; + const struct version_info *ver; + IMAGE_NT_HEADERS *nt; + SIZE_T req; + int idx; + + current_version.dwOSVersionInfoSize = sizeof(current_version); + if (!set_ntstatus( RtlGetVersion(¤t_version) )) return FALSE; + + for (idx = ARRAY_SIZE(version_data); idx--;) + if ( current_version.dwMajorVersion > version_data[idx].info.major || + (current_version.dwMajorVersion == version_data[idx].info.major && + current_version.dwMinorVersion >= version_data[idx].info.minor)) + break; + + if (idx < 0) return TRUE; + ver = &windows8_version_info; + + if (RtlQueryInformationActivationContext(0, NtCurrentTeb()->Peb->ActivationContextData, NULL, + CompatibilityInformationInActivationContext, NULL, 0, &req) != STATUS_BUFFER_TOO_SMALL + || !req) + goto done; + + if (!(acci = HeapAlloc(GetProcessHeap(), 0, req))) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + if (RtlQueryInformationActivationContext(0, NtCurrentTeb()->Peb->ActivationContextData, NULL, + CompatibilityInformationInActivationContext, acci, req, &req) == STATUS_SUCCESS) + { + do + { + DWORD i; + + for (i = 0; i < acci->ElementCount; i++) + { + if (acci->Elements[i].Type != ACTCTX_COMPATIBILITY_ELEMENT_TYPE_OS) + continue; + + have_os_compat_elements = TRUE; + + if (IsEqualGUID(&acci->Elements[i].Id, &version_data[idx].guid)) + { + ver = &version_data[idx].info; + + if (ver->major == current_version.dwMajorVersion && + ver->minor == current_version.dwMinorVersion) + ver = NULL; + + idx = 0; /* break from outer loop */ + break; + } + } + } while (idx--); + } + HeapFree(GetProcessHeap(), 0, acci); + +done: + if (!have_os_compat_elements && current_version.dwMajorVersion >= 10 + && (nt = RtlImageNtHeader(NtCurrentTeb()->Peb->ImageBaseAddress)) + && (nt->OptionalHeader.MajorOperatingSystemVersion > 6 + || (nt->OptionalHeader.MajorOperatingSystemVersion == 6 + && nt->OptionalHeader.MinorOperatingSystemVersion >= 3))) + { + if (current_version.dwMajorVersion > 10) + FIXME("Unsupported current_version.dwMajorVersion %lu.\n", current_version.dwMajorVersion); + + ver = nt->OptionalHeader.MajorOperatingSystemVersion >= 10 ? NULL : &windows8_1_version_info; + } + + if (ver) + { + current_version.dwMajorVersion = ver->major; + current_version.dwMinorVersion = ver->minor; + current_version.dwBuildNumber = ver->build; + } + return TRUE; +} + + +/********************************************************************** + * find_entry_by_id + * + * Find an entry by id in a resource directory + * Copied from loader/pe_resource.c + */ +static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir, + WORD id, const void *root, + DWORD root_size ) +{ + const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry; + int min, max, pos; + + entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1); + min = dir->NumberOfNamedEntries; + max = min + dir->NumberOfIdEntries - 1; + + if (max >= (root_size - ((INT_PTR)dir - (INT_PTR)root) - sizeof(*dir)) / sizeof(*entry)) + return NULL; + + while (min <= max) + { + pos = (min + max) / 2; + if (entry[pos].Id == id) + { + DWORD offset = entry[pos].OffsetToDirectory; + if (offset > root_size - sizeof(*dir)) return NULL; + return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + offset); + } + if (entry[pos].Id > id) max = pos - 1; + else min = pos + 1; + } + return NULL; +} + + +/********************************************************************** + * find_entry_default + * + * Find a default entry in a resource directory + * Copied from loader/pe_resource.c + */ +static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir, + const void *root ) +{ + const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry; + + entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1); + return (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + entry->OffsetToDirectory); +} + + +/********************************************************************** + * push_language + * + * push a language onto the list of languages to try + */ +static inline int push_language( WORD *list, int pos, WORD lang ) +{ + int i; + for (i = 0; i < pos; i++) if (list[i] == lang) return pos; + list[pos++] = lang; + return pos; +} + + +/********************************************************************** + * find_entry_language + */ +static const IMAGE_RESOURCE_DIRECTORY *find_entry_language( const IMAGE_RESOURCE_DIRECTORY *dir, + const void *root, DWORD root_size, + DWORD flags ) +{ + const IMAGE_RESOURCE_DIRECTORY *ret; + WORD list[9]; + int i, pos = 0; + + if (flags & FILE_VER_GET_LOCALISED) + { + /* cf. LdrFindResource_U */ + pos = push_language( list, pos, MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ) ); + pos = push_language( list, pos, LANGIDFROMLCID( NtCurrentTeb()->CurrentLocale ) ); + pos = push_language( list, pos, GetUserDefaultLangID() ); + pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_NEUTRAL )); + pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetUserDefaultLangID()), SUBLANG_DEFAULT )); + pos = push_language( list, pos, GetSystemDefaultLangID() ); + pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_NEUTRAL )); + pos = push_language( list, pos, MAKELANGID( PRIMARYLANGID(GetSystemDefaultLangID()), SUBLANG_DEFAULT )); + pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) ); + } + else + { + /* FIXME: resolve LN file here */ + pos = push_language( list, pos, MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) ); + } + + for (i = 0; i < pos; i++) if ((ret = find_entry_by_id( dir, list[i], root, root_size ))) return ret; + return find_entry_default( dir, root ); +} + + +static DWORD read_data( HANDLE handle, DWORD offset, void *data, DWORD len ) +{ + DWORD res; + + SetFilePointer( handle, offset, NULL, FILE_BEGIN ); + if (!ReadFile( handle, data, len, &res, NULL )) res = 0; + return res; +} + +/*********************************************************************** + * find_ne_resource [internal] + */ +static BOOL find_ne_resource( HANDLE handle, DWORD *resLen, DWORD *resOff ) +{ + const WORD typeid = VS_FILE_INFO | 0x8000; + const WORD resid = VS_VERSION_INFO | 0x8000; + IMAGE_OS2_HEADER nehd; + NE_TYPEINFO *typeInfo; + NE_NAMEINFO *nameInfo; + DWORD nehdoffset = *resOff; + LPBYTE resTab; + DWORD resTabSize; + int count; + + /* Read in NE header */ + if (read_data( handle, nehdoffset, &nehd, sizeof(nehd) ) != sizeof(nehd)) return FALSE; + + resTabSize = nehd.ne_restab - nehd.ne_rsrctab; + if ( !resTabSize ) + { + TRACE("No resources in NE dll\n" ); + return FALSE; + } + + /* Read in resource table */ + resTab = HeapAlloc( GetProcessHeap(), 0, resTabSize ); + if ( !resTab ) return FALSE; + + if (read_data( handle, nehd.ne_rsrctab + nehdoffset, resTab, resTabSize ) != resTabSize) + { + HeapFree( GetProcessHeap(), 0, resTab ); + return FALSE; + } + + /* Find resource */ + typeInfo = (NE_TYPEINFO *)(resTab + 2); + while (typeInfo->type_id) + { + if (typeInfo->type_id == typeid) goto found_type; + typeInfo = (NE_TYPEINFO *)((char *)(typeInfo + 1) + + typeInfo->count * sizeof(NE_NAMEINFO)); + } + TRACE("No typeid entry found\n" ); + HeapFree( GetProcessHeap(), 0, resTab ); + return FALSE; + + found_type: + nameInfo = (NE_NAMEINFO *)(typeInfo + 1); + + for (count = typeInfo->count; count > 0; count--, nameInfo++) + if (nameInfo->id == resid) goto found_name; + + TRACE("No resid entry found\n" ); + HeapFree( GetProcessHeap(), 0, resTab ); + return FALSE; + + found_name: + /* Return resource data */ + *resLen = nameInfo->length << *(WORD *)resTab; + *resOff = nameInfo->offset << *(WORD *)resTab; + + HeapFree( GetProcessHeap(), 0, resTab ); + return TRUE; +} + +/*********************************************************************** + * find_pe_resource [internal] + */ +static BOOL find_pe_resource( HANDLE handle, DWORD *resLen, DWORD *resOff, DWORD flags ) +{ + union + { + IMAGE_NT_HEADERS32 nt32; + IMAGE_NT_HEADERS64 nt64; + } pehd; + DWORD pehdoffset = *resOff; + PIMAGE_DATA_DIRECTORY resDataDir; + PIMAGE_SECTION_HEADER sections; + LPBYTE resSection; + DWORD len, section_size, data_size, resDirSize; + const void *resDir; + const IMAGE_RESOURCE_DIRECTORY *resPtr; + const IMAGE_RESOURCE_DATA_ENTRY *resData; + int i, nSections; + BOOL ret = FALSE; + + /* Read in PE header */ + len = read_data( handle, pehdoffset, &pehd, sizeof(pehd) ); + if (len < sizeof(pehd.nt32.FileHeader)) return FALSE; + if (len < sizeof(pehd)) memset( (char *)&pehd + len, 0, sizeof(pehd) - len ); + + switch (pehd.nt32.OptionalHeader.Magic) + { + case IMAGE_NT_OPTIONAL_HDR32_MAGIC: + resDataDir = pehd.nt32.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE; + break; + case IMAGE_NT_OPTIONAL_HDR64_MAGIC: + resDataDir = pehd.nt64.OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE; + break; + default: + return FALSE; + } + + if ( !resDataDir->Size ) + { + TRACE("No resources in PE dll\n" ); + return FALSE; + } + + /* Read in section table */ + nSections = pehd.nt32.FileHeader.NumberOfSections; + sections = HeapAlloc( GetProcessHeap(), 0, + nSections * sizeof(IMAGE_SECTION_HEADER) ); + if ( !sections ) return FALSE; + + len = FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) + pehd.nt32.FileHeader.SizeOfOptionalHeader; + if (read_data( handle, pehdoffset + len, sections, nSections * sizeof(IMAGE_SECTION_HEADER) ) != + nSections * sizeof(IMAGE_SECTION_HEADER)) + { + HeapFree( GetProcessHeap(), 0, sections ); + return FALSE; + } + + /* Find resource section */ + for ( i = 0; i < nSections; i++ ) + if ( resDataDir->VirtualAddress >= sections[i].VirtualAddress + && resDataDir->VirtualAddress < sections[i].VirtualAddress + + sections[i].SizeOfRawData ) + break; + + if ( i == nSections ) + { + HeapFree( GetProcessHeap(), 0, sections ); + TRACE("Couldn't find resource section\n" ); + return FALSE; + } + + /* Read in resource section */ + data_size = sections[i].SizeOfRawData; + section_size = max( data_size, sections[i].Misc.VirtualSize ); + resSection = HeapAlloc( GetProcessHeap(), 0, section_size ); + if ( !resSection ) + { + HeapFree( GetProcessHeap(), 0, sections ); + return FALSE; + } + + if (read_data( handle, sections[i].PointerToRawData, resSection, data_size ) != data_size) goto done; + if (data_size < section_size) memset( (char *)resSection + data_size, 0, section_size - data_size ); + + /* Find resource */ + resDir = resSection + (resDataDir->VirtualAddress - sections[i].VirtualAddress); + resDirSize = section_size - (resDataDir->VirtualAddress - sections[i].VirtualAddress); + + resPtr = resDir; + resPtr = find_entry_by_id( resPtr, VS_FILE_INFO, resDir, resDirSize ); + if ( !resPtr ) + { + TRACE("No typeid entry found\n" ); + goto done; + } + resPtr = find_entry_by_id( resPtr, VS_VERSION_INFO, resDir, resDirSize ); + if ( !resPtr ) + { + TRACE("No resid entry found\n" ); + goto done; + } + resPtr = find_entry_language( resPtr, resDir, resDirSize, flags ); + if ( !resPtr ) + { + TRACE("No default language entry found\n" ); + goto done; + } + + /* Find resource data section */ + resData = (const IMAGE_RESOURCE_DATA_ENTRY*)resPtr; + for ( i = 0; i < nSections; i++ ) + if ( resData->OffsetToData >= sections[i].VirtualAddress + && resData->OffsetToData < sections[i].VirtualAddress + + sections[i].SizeOfRawData ) + break; + + if ( i == nSections ) + { + TRACE("Couldn't find resource data section\n" ); + goto done; + } + + /* Return resource data */ + *resLen = resData->Size; + *resOff = resData->OffsetToData - sections[i].VirtualAddress + sections[i].PointerToRawData; + ret = TRUE; + + done: + HeapFree( GetProcessHeap(), 0, resSection ); + HeapFree( GetProcessHeap(), 0, sections ); + return ret; +} + + +/*********************************************************************** + * find_version_resource [internal] + */ +static DWORD find_version_resource( HANDLE handle, DWORD *reslen, DWORD *offset, DWORD flags ) +{ + IMAGE_DOS_HEADER mzh; + WORD magic; + + if (read_data( handle, 0, &mzh, sizeof(mzh) ) != sizeof(mzh)) return 0; + if (mzh.e_magic != IMAGE_DOS_SIGNATURE) return 0; + + if (read_data( handle, mzh.e_lfanew, &magic, sizeof(magic) ) != sizeof(magic)) return 0; + *offset = mzh.e_lfanew; + + switch (magic) + { + case IMAGE_OS2_SIGNATURE: + if (!find_ne_resource( handle, reslen, offset )) magic = 0; + break; + case IMAGE_NT_SIGNATURE: + if (!find_pe_resource( handle, reslen, offset, flags )) magic = 0; + break; + } + WARN( "Can't handle %04x files.\n", magic ); + return magic; +} + +/****************************************************************************** + * This function will print via standard TRACE, debug info regarding + * the file info structure vffi. + */ +static void print_vffi_debug(const VS_FIXEDFILEINFO *vffi) +{ + BOOL versioned_printer = FALSE; + + if((vffi->dwFileType == VFT_DLL) || (vffi->dwFileType == VFT_DRV)) + { + if(vffi->dwFileSubtype == VFT2_DRV_VERSIONED_PRINTER) + /* this is documented for newer w2k Drivers and up */ + versioned_printer = TRUE; + else if( (vffi->dwFileSubtype == VFT2_DRV_PRINTER) && + (vffi->dwFileVersionMS != vffi->dwProductVersionMS) && + (vffi->dwFileVersionMS > 0) && + (vffi->dwFileVersionMS <= 3) ) + /* found this on NT 3.51, NT4.0 and old w2k Drivers */ + versioned_printer = TRUE; + } + + TRACE("structversion=%u.%u, ", + HIWORD(vffi->dwStrucVersion),LOWORD(vffi->dwStrucVersion)); + if(versioned_printer) + { + WORD mode = LOWORD(vffi->dwFileVersionMS); + WORD ver_rev = HIWORD(vffi->dwFileVersionLS); + TRACE("fileversion=%lu.%u.%u.%u (%s.major.minor.release), ", + (vffi->dwFileVersionMS), + HIBYTE(ver_rev), LOBYTE(ver_rev), LOWORD(vffi->dwFileVersionLS), + (mode == 3) ? "Usermode" : ((mode <= 2) ? "Kernelmode" : "?") ); + } + else + { + TRACE("fileversion=%u.%u.%u.%u, ", + HIWORD(vffi->dwFileVersionMS),LOWORD(vffi->dwFileVersionMS), + HIWORD(vffi->dwFileVersionLS),LOWORD(vffi->dwFileVersionLS)); + } + TRACE("productversion=%u.%u.%u.%u\n", + HIWORD(vffi->dwProductVersionMS),LOWORD(vffi->dwProductVersionMS), + HIWORD(vffi->dwProductVersionLS),LOWORD(vffi->dwProductVersionLS)); + + TRACE("flagmask=0x%lx, flags=0x%lx %s%s%s%s%s%s\n", + vffi->dwFileFlagsMask, vffi->dwFileFlags, + (vffi->dwFileFlags & VS_FF_DEBUG) ? "DEBUG," : "", + (vffi->dwFileFlags & VS_FF_PRERELEASE) ? "PRERELEASE," : "", + (vffi->dwFileFlags & VS_FF_PATCHED) ? "PATCHED," : "", + (vffi->dwFileFlags & VS_FF_PRIVATEBUILD) ? "PRIVATEBUILD," : "", + (vffi->dwFileFlags & VS_FF_INFOINFERRED) ? "INFOINFERRED," : "", + (vffi->dwFileFlags & VS_FF_SPECIALBUILD) ? "SPECIALBUILD," : ""); + + TRACE("("); + + TRACE("OS=0x%x.0x%x ", HIWORD(vffi->dwFileOS), LOWORD(vffi->dwFileOS)); + + switch (vffi->dwFileOS&0xFFFF0000) + { + case VOS_DOS:TRACE("DOS,");break; + case VOS_OS216:TRACE("OS/2-16,");break; + case VOS_OS232:TRACE("OS/2-32,");break; + case VOS_NT:TRACE("NT,");break; + case VOS_UNKNOWN: + default: + TRACE("UNKNOWN(0x%lx),",vffi->dwFileOS&0xFFFF0000);break; + } + + switch (LOWORD(vffi->dwFileOS)) + { + case VOS__BASE:TRACE("BASE");break; + case VOS__WINDOWS16:TRACE("WIN16");break; + case VOS__WINDOWS32:TRACE("WIN32");break; + case VOS__PM16:TRACE("PM16");break; + case VOS__PM32:TRACE("PM32");break; + default: + TRACE("UNKNOWN(0x%x)",LOWORD(vffi->dwFileOS));break; + } + + TRACE(")\n"); + + switch (vffi->dwFileType) + { + case VFT_APP:TRACE("filetype=APP");break; + case VFT_DLL: + TRACE("filetype=DLL"); + if(vffi->dwFileSubtype != 0) + { + if(versioned_printer) /* NT3.x/NT4.0 or old w2k Driver */ + TRACE(",PRINTER"); + TRACE(" (subtype=0x%lx)", vffi->dwFileSubtype); + } + break; + case VFT_DRV: + TRACE("filetype=DRV,"); + switch(vffi->dwFileSubtype) + { + case VFT2_DRV_PRINTER:TRACE("PRINTER");break; + case VFT2_DRV_KEYBOARD:TRACE("KEYBOARD");break; + case VFT2_DRV_LANGUAGE:TRACE("LANGUAGE");break; + case VFT2_DRV_DISPLAY:TRACE("DISPLAY");break; + case VFT2_DRV_MOUSE:TRACE("MOUSE");break; + case VFT2_DRV_NETWORK:TRACE("NETWORK");break; + case VFT2_DRV_SYSTEM:TRACE("SYSTEM");break; + case VFT2_DRV_INSTALLABLE:TRACE("INSTALLABLE");break; + case VFT2_DRV_SOUND:TRACE("SOUND");break; + case VFT2_DRV_COMM:TRACE("COMM");break; + case VFT2_DRV_INPUTMETHOD:TRACE("INPUTMETHOD");break; + case VFT2_DRV_VERSIONED_PRINTER:TRACE("VERSIONED_PRINTER");break; + case VFT2_UNKNOWN: + default: + TRACE("UNKNOWN(0x%lx)",vffi->dwFileSubtype);break; + } + break; + case VFT_FONT: + TRACE("filetype=FONT,"); + switch (vffi->dwFileSubtype) + { + case VFT2_FONT_RASTER:TRACE("RASTER");break; + case VFT2_FONT_VECTOR:TRACE("VECTOR");break; + case VFT2_FONT_TRUETYPE:TRACE("TRUETYPE");break; + default:TRACE("UNKNOWN(0x%lx)",vffi->dwFileSubtype);break; + } + break; + case VFT_VXD:TRACE("filetype=VXD");break; + case VFT_STATIC_LIB:TRACE("filetype=STATIC_LIB");break; + case VFT_UNKNOWN: + default: + TRACE("filetype=Unknown(0x%lx)",vffi->dwFileType);break; + } + + TRACE("\n"); + TRACE("filedate=0x%lx.0x%lx\n",vffi->dwFileDateMS,vffi->dwFileDateLS); +} + +/*********************************************************************** + * GetFileVersionInfoSizeW (kernelbase.@) + */ +DWORD WINAPI GetFileVersionInfoSizeW( LPCWSTR filename, LPDWORD handle ) +{ + return GetFileVersionInfoSizeExW( FILE_VER_GET_LOCALISED, filename, handle ); +} + +/*********************************************************************** + * GetFileVersionInfoSizeA (kernelbase.@) + */ +DWORD WINAPI GetFileVersionInfoSizeA( LPCSTR filename, LPDWORD handle ) +{ + return GetFileVersionInfoSizeExA( FILE_VER_GET_LOCALISED, filename, handle ); +} + +/****************************************************************************** + * GetFileVersionInfoSizeExW (kernelbase.@) + */ +DWORD WINAPI GetFileVersionInfoSizeExW( DWORD flags, LPCWSTR filename, LPDWORD ret_handle ) +{ + DWORD len, offset, magic = 1; + HMODULE hModule; + + TRACE("(0x%lx,%s,%p)\n", flags, debugstr_w(filename), ret_handle ); + + if (ret_handle) *ret_handle = 0; + + if (!filename) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + if (!*filename) + { + SetLastError(ERROR_BAD_PATHNAME); + return 0; + } + if (flags & ~FILE_VER_GET_LOCALISED) + FIXME("flags 0x%lx ignored\n", flags & ~FILE_VER_GET_LOCALISED); + + if ((hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE ))) + { + HRSRC hRsrc = NULL; + if (!(flags & FILE_VER_GET_LOCALISED)) + { + LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ); + hRsrc = FindResourceExW( hModule, (LPWSTR)VS_FILE_INFO, + MAKEINTRESOURCEW(VS_VERSION_INFO), english ); + } + if (!hRsrc) + hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO), + (LPWSTR)VS_FILE_INFO ); + if (hRsrc) + { + magic = IMAGE_NT_SIGNATURE; + len = SizeofResource( hModule, hRsrc ); + } + FreeLibrary( hModule ); + } + else + { + HANDLE handle = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0 ); + if (handle == INVALID_HANDLE_VALUE) return 0; + magic = find_version_resource( handle, &len, &offset, flags ); + CloseHandle( handle ); + } + + switch (magic) + { + case IMAGE_OS2_SIGNATURE: + /* We have a 16bit resource. + * + * XP/W2K/W2K3 uses a buffer which is more than the actual needed space: + * + * (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 + * + * This extra buffer is used for ANSI to Unicode conversions in W-Calls. + * info->wLength should be the same as len. Currently it isn't but that + * doesn't seem to be a problem (len is bigger than info->wLength). + */ + SetLastError(0); + return (len - sizeof(VS_FIXEDFILEINFO)) * 4; + + case IMAGE_NT_SIGNATURE: + /* We have a 32bit resource. + * + * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X" + * This extra buffer is used for Unicode to ANSI conversions in A-Calls + */ + SetLastError(0); + return (len * 2) + 4; + + default: + if (GetVersion() & 0x80000000) /* Windows 95/98 */ + SetLastError(ERROR_FILE_NOT_FOUND); + else + SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); + return 0; + } +} + +/****************************************************************************** + * GetFileVersionInfoSizeExA (kernelbase.@) + */ +DWORD WINAPI GetFileVersionInfoSizeExA( DWORD flags, LPCSTR filename, LPDWORD handle ) +{ + UNICODE_STRING filenameW; + DWORD retval; + + TRACE("(0x%lx,%s,%p)\n", flags, debugstr_a(filename), handle ); + + if(filename) + RtlCreateUnicodeStringFromAsciiz(&filenameW, filename); + else + filenameW.Buffer = NULL; + + retval = GetFileVersionInfoSizeExW(flags, filenameW.Buffer, handle); + + RtlFreeUnicodeString(&filenameW); + + return retval; +} + +/*********************************************************************** + * GetFileVersionInfoExW (kernelbase.@) + */ +BOOL WINAPI GetFileVersionInfoExW( DWORD flags, LPCWSTR filename, DWORD ignored, DWORD datasize, LPVOID data ) +{ + static const char signature[4] = "FE2X"; + DWORD len, offset, magic = 1; + HMODULE hModule; + VS_VERSION_INFO_STRUCT32* vvis = data; + + TRACE("(0x%lx,%s,%ld,size=%ld,data=%p)\n", + flags, debugstr_w(filename), ignored, datasize, data ); + + if (!data) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + if (flags & ~FILE_VER_GET_LOCALISED) + FIXME("flags 0x%lx ignored\n", flags & ~FILE_VER_GET_LOCALISED); + + if ((hModule = LoadLibraryExW( filename, 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE ))) + { + HRSRC hRsrc = NULL; + if (!(flags & FILE_VER_GET_LOCALISED)) + { + LANGID english = MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ); + hRsrc = FindResourceExW( hModule, (LPWSTR)VS_FILE_INFO, + MAKEINTRESOURCEW(VS_VERSION_INFO), english ); + } + if (!hRsrc) + hRsrc = FindResourceW( hModule, MAKEINTRESOURCEW(VS_VERSION_INFO), + (LPWSTR)VS_FILE_INFO ); + if (hRsrc) + { + HGLOBAL hMem = LoadResource( hModule, hRsrc ); + magic = IMAGE_NT_SIGNATURE; + len = min( SizeofResource(hModule, hRsrc), datasize ); + memcpy( data, LockResource( hMem ), len ); + FreeResource( hMem ); + } + FreeLibrary( hModule ); + } + else + { + HANDLE handle = CreateFileW( filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0 ); + if (handle == INVALID_HANDLE_VALUE) return 0; + if ((magic = find_version_resource( handle, &len, &offset, flags ))) + len = read_data( handle, offset, data, min( len, datasize )); + CloseHandle( handle ); + } + + switch (magic) + { + case IMAGE_OS2_SIGNATURE: + /* We have a 16bit resource. */ + if (TRACE_ON(ver)) + print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo16_Value( (VS_VERSION_INFO_STRUCT16 *)data )); + SetLastError(0); + return TRUE; + + case IMAGE_NT_SIGNATURE: + /* We have a 32bit resource. + * + * XP/W2K/W2K3 uses a buffer which is 2 times the actual needed space + 4 bytes "FE2X" + * This extra buffer is used for Unicode to ANSI conversions in A-Calls + */ + len = vvis->wLength + sizeof(signature); + if (datasize >= len) memcpy( (char*)data + vvis->wLength, signature, sizeof(signature) ); + if (TRACE_ON(ver)) + print_vffi_debug( (VS_FIXEDFILEINFO *)VersionInfo32_Value( vvis )); + SetLastError(0); + return TRUE; + + default: + SetLastError( ERROR_RESOURCE_DATA_NOT_FOUND ); + return FALSE; + } +} + +/*********************************************************************** + * GetFileVersionInfoExA (kernelbase.@) + */ +BOOL WINAPI GetFileVersionInfoExA( DWORD flags, LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data ) +{ + UNICODE_STRING filenameW; + BOOL retval; + + TRACE("(0x%lx,%s,%ld,size=%ld,data=%p)\n", + flags, debugstr_a(filename), handle, datasize, data ); + + if(filename) + RtlCreateUnicodeStringFromAsciiz(&filenameW, filename); + else + filenameW.Buffer = NULL; + + retval = GetFileVersionInfoExW(flags, filenameW.Buffer, handle, datasize, data); + + RtlFreeUnicodeString(&filenameW); + + return retval; +} + +/*********************************************************************** + * GetFileVersionInfoW (kernelbase.@) + */ +BOOL WINAPI GetFileVersionInfoW( LPCWSTR filename, DWORD handle, DWORD datasize, LPVOID data ) +{ + return GetFileVersionInfoExW(FILE_VER_GET_LOCALISED, filename, handle, datasize, data); +} + +/*********************************************************************** + * GetFileVersionInfoA (kernelbase.@) + */ +BOOL WINAPI GetFileVersionInfoA( LPCSTR filename, DWORD handle, DWORD datasize, LPVOID data ) +{ + return GetFileVersionInfoExA(FILE_VER_GET_LOCALISED, filename, handle, datasize, data); +} + +/*********************************************************************** + * VersionInfo16_FindChild [internal] + */ +static const VS_VERSION_INFO_STRUCT16 *VersionInfo16_FindChild( const VS_VERSION_INFO_STRUCT16 *info, + LPCSTR key, UINT len ) +{ + const VS_VERSION_INFO_STRUCT16 *child = VersionInfo16_Children( info ); + + while ((char *)child < (char *)info + info->wLength ) + { + if (!strnicmp( child->szKey, key, len ) && !child->szKey[len]) + return child; + + if (!(child->wLength)) return NULL; + child = VersionInfo16_Next( child ); + } + + return NULL; +} + +/*********************************************************************** + * VersionInfo32_FindChild [internal] + */ +static const VS_VERSION_INFO_STRUCT32 *VersionInfo32_FindChild( const VS_VERSION_INFO_STRUCT32 *info, + LPCWSTR key, UINT len ) +{ + const VS_VERSION_INFO_STRUCT32 *child = VersionInfo32_Children( info ); + + while ((char *)child < (char *)info + info->wLength ) + { + if (!wcsnicmp( child->szKey, key, len ) && !child->szKey[len]) + return child; + + if (!(child->wLength)) return NULL; + child = VersionInfo32_Next( child ); + } + + return NULL; +} + +/*********************************************************************** + * VersionInfo16_QueryValue [internal] + * + * Gets a value from a 16-bit NE resource + */ +static BOOL VersionInfo16_QueryValue( const VS_VERSION_INFO_STRUCT16 *info, LPCSTR lpSubBlock, + LPVOID *lplpBuffer, UINT *puLen ) +{ + while ( *lpSubBlock ) + { + /* Find next path component */ + LPCSTR lpNextSlash; + for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ ) + if ( *lpNextSlash == '\\' ) + break; + + /* Skip empty components */ + if ( lpNextSlash == lpSubBlock ) + { + lpSubBlock++; + continue; + } + + /* We have a non-empty component: search info for key */ + info = VersionInfo16_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock ); + if ( !info ) + { + if (puLen) *puLen = 0 ; + SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND ); + return FALSE; + } + + /* Skip path component */ + lpSubBlock = lpNextSlash; + } + + /* Return value */ + *lplpBuffer = VersionInfo16_Value( info ); + if (puLen) + *puLen = info->wValueLength; + + return TRUE; +} + +/*********************************************************************** + * VersionInfo32_QueryValue [internal] + * + * Gets a value from a 32-bit PE resource + */ +static BOOL VersionInfo32_QueryValue( const VS_VERSION_INFO_STRUCT32 *info, LPCWSTR lpSubBlock, + LPVOID *lplpBuffer, UINT *puLen, BOOL *pbText ) +{ + TRACE("lpSubBlock : (%s)\n", debugstr_w(lpSubBlock)); + + while ( *lpSubBlock ) + { + /* Find next path component */ + LPCWSTR lpNextSlash; + for ( lpNextSlash = lpSubBlock; *lpNextSlash; lpNextSlash++ ) + if ( *lpNextSlash == '\\' ) + break; + + /* Skip empty components */ + if ( lpNextSlash == lpSubBlock ) + { + lpSubBlock++; + continue; + } + + /* We have a non-empty component: search info for key */ + info = VersionInfo32_FindChild( info, lpSubBlock, lpNextSlash-lpSubBlock ); + if ( !info ) + { + if (puLen) *puLen = 0 ; + SetLastError( ERROR_RESOURCE_TYPE_NOT_FOUND ); + return FALSE; + } + + /* Skip path component */ + lpSubBlock = lpNextSlash; + } + + /* Return value */ + *lplpBuffer = VersionInfo32_Value( info ); + if (puLen) + *puLen = info->wValueLength; + if (pbText) + *pbText = info->wType; + + return TRUE; +} + +/*********************************************************************** + * VerQueryValueA (kernelbase.@) + */ +BOOL WINAPI VerQueryValueA( LPCVOID pBlock, LPCSTR lpSubBlock, + LPVOID *lplpBuffer, PUINT puLen ) +{ + static const char rootA[] = "\\"; + const VS_VERSION_INFO_STRUCT16 *info = pBlock; + + TRACE("(%p,%s,%p,%p)\n", + pBlock, debugstr_a(lpSubBlock), lplpBuffer, puLen ); + + if (!pBlock) + return FALSE; + + if (lpSubBlock == NULL || lpSubBlock[0] == '\0') + lpSubBlock = rootA; + + if ( !VersionInfoIs16( info ) ) + { + BOOL ret, isText; + INT len; + LPWSTR lpSubBlockW; + UINT value_len; + + len = MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, NULL, 0); + lpSubBlockW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + + if (!lpSubBlockW) + return FALSE; + + MultiByteToWideChar(CP_ACP, 0, lpSubBlock, -1, lpSubBlockW, len); + + ret = VersionInfo32_QueryValue(pBlock, lpSubBlockW, lplpBuffer, &value_len, &isText); + if (puLen) *puLen = value_len; + + HeapFree(GetProcessHeap(), 0, lpSubBlockW); + + if (ret && isText) + { + /* Set lpBuffer so it points to the 'empty' area where we store + * the converted strings + */ + LPSTR lpBufferA = (LPSTR)pBlock + info->wLength + 4; + DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; + len = WideCharToMultiByte(CP_ACP, 0, *lplpBuffer, value_len, + lpBufferA + pos, info->wLength - pos, NULL, NULL); + *lplpBuffer = lpBufferA + pos; + if (puLen) *puLen = len; + } + return ret; + } + + return VersionInfo16_QueryValue(info, lpSubBlock, lplpBuffer, puLen); +} + +/*********************************************************************** + * VerQueryValueW (kernelbase.@) + */ +BOOL WINAPI VerQueryValueW( LPCVOID pBlock, LPCWSTR lpSubBlock, + LPVOID *lplpBuffer, PUINT puLen ) +{ + const VS_VERSION_INFO_STRUCT32 *info = pBlock; + + TRACE("(%p,%s,%p,%p)\n", + pBlock, debugstr_w(lpSubBlock), lplpBuffer, puLen ); + + if (!pBlock) + return FALSE; + + if (!lpSubBlock || !lpSubBlock[0]) + lpSubBlock = L"\\"; + + if ( VersionInfoIs16( info ) ) + { + BOOL ret; + int len; + LPSTR lpSubBlockA; + + len = WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, NULL, 0, NULL, NULL); + lpSubBlockA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char)); + + if (!lpSubBlockA) + return FALSE; + + WideCharToMultiByte(CP_ACP, 0, lpSubBlock, -1, lpSubBlockA, len, NULL, NULL); + + ret = VersionInfo16_QueryValue(pBlock, lpSubBlockA, lplpBuffer, puLen); + + HeapFree(GetProcessHeap(), 0, lpSubBlockA); + + if (ret && wcscmp( lpSubBlock, L"\\" ) && wcsicmp( lpSubBlock, L"\\VarFileInfo\\Translation" )) + { + /* Set lpBuffer so it points to the 'empty' area where we store + * the converted strings + */ + LPWSTR lpBufferW = (LPWSTR)((LPSTR)pBlock + info->wLength); + DWORD pos = (LPCSTR)*lplpBuffer - (LPCSTR)pBlock; + DWORD max = (info->wLength - sizeof(VS_FIXEDFILEINFO)) * 4 - info->wLength; + + len = MultiByteToWideChar(CP_ACP, 0, *lplpBuffer, -1, + lpBufferW + pos, max/sizeof(WCHAR) - pos ); + *lplpBuffer = lpBufferW + pos; + if (puLen) *puLen = len; + } + return ret; + } + + return VersionInfo32_QueryValue(info, lpSubBlock, lplpBuffer, puLen, NULL); +} + + +/****************************************************************************** + * file_existsA + */ +static BOOL file_existsA( char const * path, char const * file, BOOL excl ) +{ + DWORD sharing = excl ? 0 : FILE_SHARE_READ | FILE_SHARE_WRITE; + char filename[MAX_PATH]; + int len; + HANDLE handle; + + if (path) + { + strcpy( filename, path ); + len = strlen(filename); + if (len && filename[len - 1] != '\\') strcat( filename, "\\" ); + strcat( filename, file ); + } + else if (!SearchPathA( NULL, file, NULL, MAX_PATH, filename, NULL )) return FALSE; + + handle = CreateFileA( filename, 0, sharing, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ); + if (handle == INVALID_HANDLE_VALUE) return FALSE; + CloseHandle( handle ); + return TRUE; +} + +/****************************************************************************** + * file_existsW + */ +static BOOL file_existsW( const WCHAR *path, const WCHAR *file, BOOL excl ) +{ + DWORD sharing = excl ? 0 : FILE_SHARE_READ | FILE_SHARE_WRITE; + WCHAR filename[MAX_PATH]; + int len; + HANDLE handle; + + if (path) + { + lstrcpyW( filename, path ); + len = lstrlenW(filename); + if (len && filename[len - 1] != '\\') lstrcatW( filename, L"\\" ); + lstrcatW( filename, file ); + } + else if (!SearchPathW( NULL, file, NULL, MAX_PATH, filename, NULL )) return FALSE; + + handle = CreateFileW( filename, 0, sharing, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ); + if (handle == INVALID_HANDLE_VALUE) return FALSE; + CloseHandle( handle ); + return TRUE; +} + +/***************************************************************************** + * VerFindFileA (kernelbase.@) + * + * Determines where to install a file based on whether it locates another + * version of the file in the system. The values VerFindFile returns are + * used in a subsequent call to the VerInstallFile function. + */ +DWORD WINAPI VerFindFileA( DWORD flags, LPCSTR filename, LPCSTR win_dir, LPCSTR app_dir, + LPSTR cur_dir, PUINT curdir_len, LPSTR dest, PUINT dest_len ) +{ + DWORD retval = 0; + const char *curDir; + const char *destDir; + char winDir[MAX_PATH], systemDir[MAX_PATH]; + + TRACE("flags = %lx filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n", + flags, debugstr_a(filename), debugstr_a(win_dir), debugstr_a(app_dir), + curdir_len, curdir_len ? *curdir_len : 0, dest_len, dest_len ? *dest_len : 0 ); + + /* Figure out where the file should go; shared files default to the + system directory */ + + GetSystemDirectoryA(systemDir, sizeof(systemDir)); + curDir = ""; + + if(flags & VFFF_ISSHAREDFILE) + { + destDir = systemDir; + /* Were we given a filename? If so, try to find the file. */ + if(filename) + { + if(file_existsA(destDir, filename, FALSE)) curDir = destDir; + else if(app_dir && file_existsA(app_dir, filename, FALSE)) + curDir = app_dir; + + if(!file_existsA(systemDir, filename, FALSE)) + retval |= VFF_CURNEDEST; + } + } + else /* not a shared file */ + { + destDir = app_dir ? app_dir : ""; + if(filename) + { + GetWindowsDirectoryA( winDir, MAX_PATH ); + if(file_existsA(destDir, filename, FALSE)) curDir = destDir; + else if(file_existsA(winDir, filename, FALSE)) + curDir = winDir; + else if(file_existsA(systemDir, filename, FALSE)) + curDir = systemDir; + + if (app_dir && app_dir[0]) + { + if(!file_existsA(app_dir, filename, FALSE)) + retval |= VFF_CURNEDEST; + } + else if(file_existsA(NULL, filename, FALSE)) + retval |= VFF_CURNEDEST; + } + } + + /* Check to see if the file exists and is in use by another application */ + if (filename && file_existsA(curDir, filename, FALSE)) + { + if (filename && !file_existsA(curDir, filename, TRUE)) + retval |= VFF_FILEINUSE; + } + + if (dest_len && dest) + { + UINT len = strlen(destDir) + 1; + if (*dest_len < len) retval |= VFF_BUFFTOOSMALL; + lstrcpynA(dest, destDir, *dest_len); + *dest_len = len; + } + if (curdir_len && cur_dir) + { + UINT len = strlen(curDir) + 1; + if (*curdir_len < len) retval |= VFF_BUFFTOOSMALL; + lstrcpynA(cur_dir, curDir, *curdir_len); + *curdir_len = len; + } + + TRACE("ret = %lu (%s%s%s) curdir=%s destdir=%s\n", retval, + (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "", + (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "", + (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "", + debugstr_a(cur_dir), debugstr_a(dest)); + + return retval; +} + +/***************************************************************************** + * VerFindFileW (kernelbase.@) + */ +DWORD WINAPI VerFindFileW( DWORD flags, LPCWSTR filename, LPCWSTR win_dir, LPCWSTR app_dir, + LPWSTR cur_dir, PUINT curdir_len, LPWSTR dest, PUINT dest_len ) +{ + DWORD retval = 0; + const WCHAR *curDir; + const WCHAR *destDir; + + TRACE("flags = %lx filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n", + flags, debugstr_w(filename), debugstr_w(win_dir), debugstr_w(app_dir), + curdir_len, curdir_len ? *curdir_len : 0, dest_len, dest_len ? *dest_len : 0 ); + + /* Figure out where the file should go; shared files default to the + system directory */ + + curDir = L""; + + if(flags & VFFF_ISSHAREDFILE) + { + destDir = system_dir; + /* Were we given a filename? If so, try to find the file. */ + if(filename) + { + if(file_existsW(destDir, filename, FALSE)) curDir = destDir; + else if(app_dir && file_existsW(app_dir, filename, FALSE)) + { + curDir = app_dir; + retval |= VFF_CURNEDEST; + } + } + } + else /* not a shared file */ + { + destDir = app_dir ? app_dir : L""; + if(filename) + { + if(file_existsW(destDir, filename, FALSE)) curDir = destDir; + else if(file_existsW(windows_dir, filename, FALSE)) + { + curDir = windows_dir; + retval |= VFF_CURNEDEST; + } + else if (file_existsW(system_dir, filename, FALSE)) + { + curDir = system_dir; + retval |= VFF_CURNEDEST; + } + } + } + + if (filename && !file_existsW(curDir, filename, TRUE)) + retval |= VFF_FILEINUSE; + + if (dest_len && dest) + { + UINT len = lstrlenW(destDir) + 1; + if (*dest_len < len) retval |= VFF_BUFFTOOSMALL; + lstrcpynW(dest, destDir, *dest_len); + *dest_len = len; + } + if (curdir_len && cur_dir) + { + UINT len = lstrlenW(curDir) + 1; + if (*curdir_len < len) retval |= VFF_BUFFTOOSMALL; + lstrcpynW(cur_dir, curDir, *curdir_len); + *curdir_len = len; + } + + TRACE("ret = %lu (%s%s%s) curdir=%s destdir=%s\n", retval, + (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "", + (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "", + (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "", + debugstr_w(cur_dir), debugstr_w(dest)); + return retval; +} + + +/*********************************************************************** + * GetProductInfo (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetProductInfo( DWORD os_major, DWORD os_minor, + DWORD sp_major, DWORD sp_minor, DWORD *type ) +{ + return RtlGetProductInfo( os_major, os_minor, sp_major, sp_minor, type ); +} + + +/*********************************************************************** + * GetVersion (kernelbase.@) + */ +DWORD WINAPI GetVersion(void) +{ + OSVERSIONINFOEXW info; + DWORD result; + + info.dwOSVersionInfoSize = sizeof(info); + if (!GetVersionExW( (OSVERSIONINFOW *)&info )) return 0; + + result = MAKELONG( MAKEWORD( info.dwMajorVersion, info.dwMinorVersion ), + (info.dwPlatformId ^ 2) << 14 ); + + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) + result |= LOWORD(info.dwBuildNumber) << 16; + return result; +} + + +/*********************************************************************** + * GetVersionExA (kernelbase.@) + */ +BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info ) +{ + OSVERSIONINFOEXW infoW; + + if (info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA) && + info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXA)) + { + WARN( "wrong OSVERSIONINFO size from app (got: %ld)\n", info->dwOSVersionInfoSize ); + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + infoW.dwOSVersionInfoSize = sizeof(infoW); + if (!GetVersionExW( (OSVERSIONINFOW *)&infoW )) return FALSE; + + info->dwMajorVersion = infoW.dwMajorVersion; + info->dwMinorVersion = infoW.dwMinorVersion; + info->dwBuildNumber = infoW.dwBuildNumber; + info->dwPlatformId = infoW.dwPlatformId; + WideCharToMultiByte( CP_ACP, 0, infoW.szCSDVersion, -1, + info->szCSDVersion, sizeof(info->szCSDVersion), NULL, NULL ); + + if (info->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)) + { + OSVERSIONINFOEXA *vex = (OSVERSIONINFOEXA *)info; + vex->wServicePackMajor = infoW.wServicePackMajor; + vex->wServicePackMinor = infoW.wServicePackMinor; + vex->wSuiteMask = infoW.wSuiteMask; + vex->wProductType = infoW.wProductType; + } + return TRUE; +} + + +/*********************************************************************** + * GetVersionExW (kernelbase.@) + */ +BOOL WINAPI GetVersionExW( OSVERSIONINFOW *info ) +{ + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + + if (info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOW) && + info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXW)) + { + WARN( "wrong OSVERSIONINFO size from app (got: %ld)\n", info->dwOSVersionInfoSize ); + return FALSE; + } + + if (!InitOnceExecuteOnce(&init_once, init_current_version, NULL, NULL)) return FALSE; + + info->dwMajorVersion = current_version.dwMajorVersion; + info->dwMinorVersion = current_version.dwMinorVersion; + info->dwBuildNumber = current_version.dwBuildNumber; + info->dwPlatformId = current_version.dwPlatformId; + wcscpy( info->szCSDVersion, current_version.szCSDVersion ); + + if (info->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXW)) + { + OSVERSIONINFOEXW *vex = (OSVERSIONINFOEXW *)info; + vex->wServicePackMajor = current_version.wServicePackMajor; + vex->wServicePackMinor = current_version.wServicePackMinor; + vex->wSuiteMask = current_version.wSuiteMask; + vex->wProductType = current_version.wProductType; + } + return TRUE; +} + + +/*********************************************************************** + * GetCurrentPackageFamilyName (kernelbase.@) + */ +LONG WINAPI /* DECLSPEC_HOTPATCH */ GetCurrentPackageFamilyName( UINT32 *length, WCHAR *name ) +{ + FIXME( "(%p %p): stub\n", length, name ); + return APPMODEL_ERROR_NO_PACKAGE; +} + + +/*********************************************************************** + * GetCurrentPackageFullName (kernelbase.@) + */ +LONG WINAPI /* DECLSPEC_HOTPATCH */ GetCurrentPackageFullName( UINT32 *length, WCHAR *name ) +{ + FIXME( "(%p %p): stub\n", length, name ); + return APPMODEL_ERROR_NO_PACKAGE; +} + + +/*********************************************************************** + * GetCurrentPackageId (kernelbase.@) + */ +LONG WINAPI /* DECLSPEC_HOTPATCH */ GetCurrentPackageId( UINT32 *len, BYTE *buffer ) +{ + FIXME( "(%p %p): stub\n", len, buffer ); + return APPMODEL_ERROR_NO_PACKAGE; +} + + +/*********************************************************************** + * GetCurrentPackagePath (kernelbase.@) + */ +LONG WINAPI /* DECLSPEC_HOTPATCH */ GetCurrentPackagePath( UINT32 *length, WCHAR *path ) +{ + FIXME( "(%p %p): stub\n", length, path ); + return APPMODEL_ERROR_NO_PACKAGE; +} + + +/*********************************************************************** + * GetPackageFullName (kernelbase.@) + */ +LONG WINAPI /* DECLSPEC_HOTPATCH */ GetPackageFullName( HANDLE process, UINT32 *length, WCHAR *name ) +{ + FIXME( "(%p %p %p): stub\n", process, length, name ); + return APPMODEL_ERROR_NO_PACKAGE; +} + + +/*********************************************************************** + * GetPackageFamilyName (kernelbase.@) + */ +LONG WINAPI /* DECLSPEC_HOTPATCH */ GetPackageFamilyName( HANDLE process, UINT32 *length, WCHAR *name ) +{ + FIXME( "(%p %p %p): stub\n", process, length, name ); + return APPMODEL_ERROR_NO_PACKAGE; +} + +/*********************************************************************** + * GetPackagesByPackageFamily (kernelbase.@) + */ +LONG WINAPI DECLSPEC_HOTPATCH GetPackagesByPackageFamily(const WCHAR *family_name, UINT32 *count, + WCHAR *full_names, UINT32 *buffer_len, WCHAR *buffer) +{ + FIXME( "(%s %p %p %p %p): stub\n", debugstr_w(family_name), count, full_names, buffer_len, buffer ); + + if (!count || !buffer_len) + return ERROR_INVALID_PARAMETER; + + *count = 0; + *buffer_len = 0; + return ERROR_SUCCESS; +} + +/*********************************************************************** + * GetPackagePathByFullName (kernelbase.@) + */ +LONG WINAPI GetPackagePathByFullName(const WCHAR *name, UINT32 *len, WCHAR *path) +{ + if (!len || !name) + return ERROR_INVALID_PARAMETER; + + FIXME( "(%s %p %p): stub\n", debugstr_w(name), len, path ); + + return APPMODEL_ERROR_NO_PACKAGE; +} + +static const struct +{ + UINT32 code; + const WCHAR *name; +} +arch_names[] = +{ + {PROCESSOR_ARCHITECTURE_INTEL, L"x86"}, + {PROCESSOR_ARCHITECTURE_ARM, L"arm"}, + {PROCESSOR_ARCHITECTURE_AMD64, L"x64"}, + {PROCESSOR_ARCHITECTURE_NEUTRAL, L"neutral"}, + {PROCESSOR_ARCHITECTURE_ARM64, L"arm64"}, + {PROCESSOR_ARCHITECTURE_UNKNOWN, L"unknown"}, +}; + +static UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(arch_names); ++i) + if (lstrlenW(arch_names[i].name) == len && !wcsnicmp(str, arch_names[i].name, len)) + return arch_names[i].code; + return ~0u; +} + +/*********************************************************************** + * PackageIdFromFullName (kernelbase.@) + */ +LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer) +{ + const WCHAR *name, *version_str, *arch_str, *resource_id, *publisher_id, *s; + PACKAGE_ID *id = (PACKAGE_ID *)buffer; + UINT32 size, buffer_size, len; + + TRACE("full_name %s, flags %#x, buffer_length %p, buffer %p.\n", + debugstr_w(full_name), flags, buffer_length, buffer); + + if (flags) + FIXME("Flags %#x are not supported.\n", flags); + + if (!full_name || !buffer_length) + return ERROR_INVALID_PARAMETER; + + if (!buffer && *buffer_length) + return ERROR_INVALID_PARAMETER; + + name = full_name; + if (!(version_str = wcschr(name, L'_'))) + return ERROR_INVALID_PARAMETER; + ++version_str; + + if (!(arch_str = wcschr(version_str, L'_'))) + return ERROR_INVALID_PARAMETER; + ++arch_str; + + if (!(resource_id = wcschr(arch_str, L'_'))) + return ERROR_INVALID_PARAMETER; + ++resource_id; + + if (!(publisher_id = wcschr(resource_id, L'_'))) + return ERROR_INVALID_PARAMETER; + ++publisher_id; + + /* Publisher id length should be 13. */ + size = sizeof(*id) + sizeof(WCHAR) * ((version_str - name) + (publisher_id - resource_id) + 13 + 1); + buffer_size = *buffer_length; + *buffer_length = size; + if (buffer_size < size) + return ERROR_INSUFFICIENT_BUFFER; + + memset(id, 0, sizeof(*id)); + if ((id->processorArchitecture = processor_arch_from_string(arch_str, resource_id - arch_str - 1)) == ~0u) + { + FIXME("Unrecognized arch %s.\n", debugstr_w(arch_str)); + return ERROR_INVALID_PARAMETER; + } + buffer += sizeof(*id); + + id->version.Major = wcstol(version_str, NULL, 10); + if (!(s = wcschr(version_str, L'.'))) + return ERROR_INVALID_PARAMETER; + ++s; + id->version.Minor = wcstol(s, NULL, 10); + if (!(s = wcschr(s, L'.'))) + return ERROR_INVALID_PARAMETER; + ++s; + id->version.Build = wcstol(s, NULL, 10); + if (!(s = wcschr(s, L'.'))) + return ERROR_INVALID_PARAMETER; + ++s; + id->version.Revision = wcstol(s, NULL, 10); + + id->name = (WCHAR *)buffer; + len = version_str - name - 1; + memcpy(id->name, name, sizeof(*id->name) * len); + id->name[len] = 0; + buffer += sizeof(*id->name) * (len + 1); + + id->resourceId = (WCHAR *)buffer; + len = publisher_id - resource_id - 1; + memcpy(id->resourceId, resource_id, sizeof(*id->resourceId) * len); + id->resourceId[len] = 0; + buffer += sizeof(*id->resourceId) * (len + 1); + + id->publisherId = (WCHAR *)buffer; + len = lstrlenW(publisher_id); + if (len != 13) + return ERROR_INVALID_PARAMETER; + memcpy(id->publisherId, publisher_id, sizeof(*id->publisherId) * len); + id->publisherId[len] = 0; + + return ERROR_SUCCESS; +} diff --git a/dll/win32/KernelBase/wine/volume.c b/dll/win32/KernelBase/wine/volume.c new file mode 100644 index 0000000000000..d489522852566 --- /dev/null +++ b/dll/win32/KernelBase/wine/volume.c @@ -0,0 +1,1194 @@ +/* + * Volume management functions + * + * Copyright 1993 Erik Bos + * Copyright 1996, 2004 Alexandre Julliard + * Copyright 1999 Petr Tomasek + * Copyright 2000 Andreas Mohr + * Copyright 2003 Eric Pouech + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winternl.h" +#include "winioctl.h" +#include "ntddcdrm.h" +#define WINE_MOUNTMGR_EXTENSIONS +#include "ddk/mountmgr.h" +#include "ddk/wdm.h" +#include "kernelbase.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(volume); + +#define BLOCK_SIZE 2048 +#define SUPERBLOCK_SIZE BLOCK_SIZE +#define SYMBOLIC_LINK_QUERY 0x0001 + +#define CDFRAMES_PERSEC 75 +#define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60) +#define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3]) +#define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address) + +#define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)]) +#define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2)) + +enum fs_type +{ + FS_ERROR, /* error accessing the device */ + FS_UNKNOWN, /* unknown file system */ + FS_FAT1216, + FS_FAT32, + FS_ISO9660, + FS_UDF /* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */ +}; + +/* read the contents of an NT symlink object */ +static NTSTATUS read_nt_symlink( const WCHAR *name, WCHAR *target, DWORD size ) +{ + NTSTATUS status; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + HANDLE handle; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nameW; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, name ); + + if (!(status = NtOpenSymbolicLinkObject( &handle, SYMBOLIC_LINK_QUERY, &attr ))) + { + UNICODE_STRING targetW; + targetW.Buffer = target; + targetW.MaximumLength = (size - 1) * sizeof(WCHAR); + status = NtQuerySymbolicLinkObject( handle, &targetW, NULL ); + if (!status) target[targetW.Length / sizeof(WCHAR)] = 0; + NtClose( handle ); + } + return status; +} + +/* open a handle to a device root */ +static BOOL open_device_root( LPCWSTR root, HANDLE *handle ) +{ + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + + if (!root) root = L"\\"; + if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( handle, SYNCHRONIZE, &attr, &io, 0, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + return set_ntstatus( status ); +} + +/* query the type of a drive from the mount manager */ +static DWORD get_mountmgr_drive_type( LPCWSTR root ) +{ + HANDLE mgr; + struct mountmgr_unix_drive data; + DWORD br; + + memset( &data, 0, sizeof(data) ); + if (root) data.letter = root[0]; + else + { + WCHAR curdir[MAX_PATH]; + GetCurrentDirectoryW( MAX_PATH, curdir ); + if (curdir[1] != ':' || curdir[2] != '\\') return DRIVE_UNKNOWN; + data.letter = curdir[0]; + } + + mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); + if (mgr == INVALID_HANDLE_VALUE) return DRIVE_UNKNOWN; + + if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, &data, sizeof(data), &data, + sizeof(data), &br, NULL ) && GetLastError() != ERROR_MORE_DATA) + data.type = DRIVE_UNKNOWN; + + CloseHandle( mgr ); + return data.type; +} + + +/*********************************************************************** + * GetVolumeInformationW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len, + DWORD *serial, DWORD *filename_len, DWORD *flags, + LPWSTR fsname, DWORD fsname_len ) +{ + HANDLE handle; + NTSTATUS status; + UNICODE_STRING nt_name; + IO_STATUS_BLOCK io; + OBJECT_ATTRIBUTES attr; + unsigned int i; + BOOL ret = FALSE; + + if (!root) root = L"\\"; + if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + /* there must be exactly one backslash in the name, at the end */ + for (i = 4; i < nt_name.Length / sizeof(WCHAR); i++) if (nt_name.Buffer[i] == '\\') break; + if (i != nt_name.Length / sizeof(WCHAR) - 1) + { + /* check if root contains an explicit subdir */ + if (root[0] && root[1] == ':') root += 2; + while (*root == '\\') root++; + if (wcschr( root, '\\' )) + SetLastError( ERROR_DIR_NOT_ROOT ); + else + SetLastError( ERROR_INVALID_NAME ); + goto done; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + nt_name.Length -= sizeof(WCHAR); /* without trailing slash */ + status = NtOpenFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); + nt_name.Length += sizeof(WCHAR); + + if (status) + { + TRACE( "cannot open device %s: %lx\n", debugstr_w(nt_name.Buffer), status ); + status = NtOpenFile( &handle, SYNCHRONIZE, &attr, &io, 0, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); + } + + if (!set_ntstatus( status )) goto done; + + ret = GetVolumeInformationByHandleW( handle, label, label_len, serial, filename_len, flags, + fsname, fsname_len ); + NtClose( handle ); + +done: + RtlFreeUnicodeString( &nt_name ); + return ret; +} + + +/*********************************************************************** + * GetVolumeInformationA (kernelbase.@) + */ +BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label, + DWORD label_len, DWORD *serial, + DWORD *filename_len, DWORD *flags, + LPSTR fsname, DWORD fsname_len ) +{ + WCHAR *rootW = NULL; + LPWSTR labelW, fsnameW; + BOOL ret; + + if (root && !(rootW = file_name_AtoW( root, FALSE ))) return FALSE; + + labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL; + fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL; + + if ((ret = GetVolumeInformationW(rootW, labelW, label_len, serial, + filename_len, flags, fsnameW, fsname_len))) + { + if (label) file_name_WtoA( labelW, -1, label, label_len ); + if (fsname) file_name_WtoA( fsnameW, -1, fsname, fsname_len ); + } + + HeapFree( GetProcessHeap(), 0, labelW ); + HeapFree( GetProcessHeap(), 0, fsnameW ); + return ret; +} + + +/*********************************************************************** + * GetVolumeNameForVolumeMountPointW (kernelbase.@) + */ +BOOL WINAPI GetVolumeNameForVolumeMountPointW( LPCWSTR path, LPWSTR volume, DWORD size ) +{ + MOUNTMGR_MOUNT_POINT *input = NULL, *o1; + MOUNTMGR_MOUNT_POINTS *output = NULL; + WCHAR *p; + char *r; + DWORD i, i_size = 1024, o_size = 1024; + WCHAR *nonpersist_name; + WCHAR symlink_name[MAX_PATH]; + NTSTATUS status; + HANDLE mgr = INVALID_HANDLE_VALUE; + BOOL ret = FALSE; + DWORD br; + + TRACE("(%s, %p, %lx)\n", debugstr_w(path), volume, size); + if (path[lstrlenW(path)-1] != '\\') + { + SetLastError( ERROR_INVALID_NAME ); + return FALSE; + } + + if (size < 50) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return FALSE; + } + /* if length of input is > 3 then it must be a mounted folder */ + if (lstrlenW(path) > 3) + { + FIXME("Mounted Folders are not yet supported\n"); + SetLastError( ERROR_NOT_A_REPARSE_POINT ); + return FALSE; + } + + mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, 0 ); + if (mgr == INVALID_HANDLE_VALUE) return FALSE; + + if (!(input = HeapAlloc( GetProcessHeap(), 0, i_size ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto err_ret; + } + + if (!(output = HeapAlloc( GetProcessHeap(), 0, o_size ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto err_ret; + } + + /* construct the symlink name as "\DosDevices\C:" */ + lstrcpyW( symlink_name, L"\\DosDevices\\" ); + lstrcatW( symlink_name, path ); + symlink_name[lstrlenW(symlink_name)-1] = 0; + + /* Take the mount point and get the "nonpersistent name" */ + /* We will then take that and get the volume name */ + nonpersist_name = (WCHAR *)(input + 1); + status = read_nt_symlink( symlink_name, nonpersist_name, (i_size - sizeof(*input)) / sizeof(WCHAR) ); + TRACE("read_nt_symlink got stat=%lx, for %s, got <%s>\n", status, + debugstr_w(symlink_name), debugstr_w(nonpersist_name)); + if (status != STATUS_SUCCESS) + { + SetLastError( ERROR_FILE_NOT_FOUND ); + goto err_ret; + } + + /* Now take the "nonpersistent name" and ask the mountmgr */ + /* to give us all the mount points. One of them will be */ + /* the volume name (format of \??\Volume{). */ + memset( input, 0, sizeof(*input) ); /* clear all input parameters */ + input->DeviceNameOffset = sizeof(*input); + input->DeviceNameLength = lstrlenW( nonpersist_name) * sizeof(WCHAR); + i_size = input->DeviceNameOffset + input->DeviceNameLength; + + output->Size = o_size; + + /* now get the true volume name from the mountmgr */ + if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, i_size, + output, o_size, &br, NULL )) + goto err_ret; + + /* Verify and return the data, note string is not null terminated */ + TRACE("found %ld matching mount points\n", output->NumberOfMountPoints); + if (output->NumberOfMountPoints < 1) + { + SetLastError( ERROR_NO_VOLUME_ID ); + goto err_ret; + } + o1 = &output->MountPoints[0]; + + /* look for the volume name in returned values */ + for(i=0;iNumberOfMountPoints;i++) + { + p = (WCHAR*)((char *)output + o1->SymbolicLinkNameOffset); + r = (char *)output + o1->UniqueIdOffset; + TRACE("found symlink=%s, unique=%s, devname=%s\n", + debugstr_wn(p, o1->SymbolicLinkNameLength/sizeof(WCHAR)), + debugstr_an(r, o1->UniqueIdLength), + debugstr_wn((WCHAR*)((char *)output + o1->DeviceNameOffset), + o1->DeviceNameLength/sizeof(WCHAR))); + + if (!wcsncmp( p, L"\\??\\Volume{", wcslen(L"\\??\\Volume{") )) + { + /* is there space in the return variable ?? */ + if ((o1->SymbolicLinkNameLength/sizeof(WCHAR))+2 > size) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + goto err_ret; + } + memcpy( volume, p, o1->SymbolicLinkNameLength ); + volume[o1->SymbolicLinkNameLength / sizeof(WCHAR)] = 0; + lstrcatW( volume, L"\\" ); + /* change second char from '?' to '\' */ + volume[1] = '\\'; + ret = TRUE; + break; + } + o1++; + } + +err_ret: + HeapFree( GetProcessHeap(), 0, input ); + HeapFree( GetProcessHeap(), 0, output ); + CloseHandle( mgr ); + return ret; +} + + +/*********************************************************************** + * DefineDosDeviceW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH DefineDosDeviceW( DWORD flags, const WCHAR *device, const WCHAR *target ) +{ + WCHAR link_name[15] = L"\\DosDevices\\"; + UNICODE_STRING nt_name, nt_target; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + HANDLE handle; + + TRACE("%#lx, %s, %s\n", flags, debugstr_w(device), debugstr_w(target)); + + if (flags & ~(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION)) + FIXME("Ignoring flags %#lx.\n", flags & ~(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION)); + + lstrcatW( link_name, device ); + RtlInitUnicodeString( &nt_name, link_name ); + InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, 0, NULL ); + if (flags & DDD_REMOVE_DEFINITION) + { + if (!set_ntstatus( NtOpenSymbolicLinkObject( &handle, DELETE, &attr ) )) + return FALSE; + + status = NtMakeTemporaryObject( handle ); + NtClose( handle ); + + return set_ntstatus( status ); + } + + if (!(flags & DDD_RAW_TARGET_PATH)) + { + if (!RtlDosPathNameToNtPathName_U( target, &nt_target, NULL, NULL)) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + } + else + RtlInitUnicodeString( &nt_target, target ); + + if (!(status = NtCreateSymbolicLinkObject( &handle, SYMBOLIC_LINK_ALL_ACCESS, &attr, &nt_target ))) + NtClose( handle ); + return set_ntstatus( status ); +} + + +/*********************************************************************** + * QueryDosDeviceW (kernelbase.@) + * + * returns array of strings terminated by \0, terminated by \0 + */ +DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize ) +{ + NTSTATUS status; + + if (!bufsize) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return 0; + } + + if (devname) + { + WCHAR name[8]; + WCHAR *buffer; + DWORD dosdev, ret = 0; + + if ((dosdev = RtlIsDosDeviceName_U( devname ))) + { + memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) ); + name[LOWORD(dosdev)/sizeof(WCHAR)] = 0; + devname = name; + } + + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(L"\\DosDevices\\") + lstrlenW(devname)*sizeof(WCHAR) ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + return 0; + } + lstrcpyW( buffer, L"\\DosDevices\\" ); + lstrcatW( buffer, devname ); + status = read_nt_symlink( buffer, target, bufsize ); + HeapFree( GetProcessHeap(), 0, buffer ); + if (!set_ntstatus( status )) return 0; + ret = lstrlenW( target ) + 1; + if (ret < bufsize) target[ret++] = 0; /* add an extra null */ + return ret; + } + else /* return a list of all devices */ + { + UNICODE_STRING nt_name = RTL_CONSTANT_STRING( L"\\DosDevices" ); + OBJECT_ATTRIBUTES attr; + HANDLE handle; + WCHAR *p = target; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nt_name; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr ); + if (!status) + { + char data[1024]; + DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data; + ULONG ctx = 0, len; + + while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len )) + { + if (p + info->ObjectName.Length/sizeof(WCHAR) + 1 >= target + bufsize) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + NtClose( handle ); + return 0; + } + memcpy( p, info->ObjectName.Buffer, info->ObjectName.Length ); + p += info->ObjectName.Length/sizeof(WCHAR); + *p++ = 0; + } + NtClose( handle ); + } + + *p++ = 0; /* terminating null */ + return p - target; + } +} + + +/*********************************************************************** + * GetLogicalDrives (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetLogicalDrives(void) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nt_name = RTL_CONSTANT_STRING( L"\\DosDevices\\" ); + DWORD bitmask = 0; + NTSTATUS status; + HANDLE handle; + + nt_name.Length -= sizeof(WCHAR); /* without trailing slash */ + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nt_name; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr ); + if (!status) + { + char data[1024]; + DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data; + ULONG ctx = 0, len; + + while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len )) + if(info->ObjectName.Length == 2*sizeof(WCHAR) && info->ObjectName.Buffer[1] == ':') + bitmask |= 1 << (info->ObjectName.Buffer[0] - 'A'); + + NtClose( handle ); + } + + return bitmask; +} + + +/*********************************************************************** + * GetLogicalDriveStringsW (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetLogicalDriveStringsW( UINT len, LPWSTR buffer ) +{ + DWORD drives = GetLogicalDrives(); + UINT drive, count; + + for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++; + if ((count * 4) + 1 > len) return count * 4 + 1; + + for (drive = 0; drive < 26; drive++) + { + if (drives & (1 << drive)) + { + *buffer++ = 'A' + drive; + *buffer++ = ':'; + *buffer++ = '\\'; + *buffer++ = 0; + } + } + *buffer = 0; + return count * 4; +} + + +/*********************************************************************** + * GetDriveTypeW (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetDriveTypeW( LPCWSTR root ) +{ + FILE_FS_DEVICE_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + UINT ret; + + if (!open_device_root( root, &handle )) + { + /* CD ROM devices do not necessarily have a volume, but a drive type */ + ret = get_mountmgr_drive_type( root ); + if (ret == DRIVE_CDROM || ret == DRIVE_REMOVABLE) + return ret; + + return DRIVE_NO_ROOT_DIR; + } + + status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation ); + NtClose( handle ); + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError(status) ); + ret = DRIVE_UNKNOWN; + } + else + { + switch (info.DeviceType) + { + case FILE_DEVICE_CD_ROM_FILE_SYSTEM: ret = DRIVE_CDROM; break; + case FILE_DEVICE_VIRTUAL_DISK: ret = DRIVE_RAMDISK; break; + case FILE_DEVICE_NETWORK_FILE_SYSTEM: ret = DRIVE_REMOTE; break; + case FILE_DEVICE_DISK_FILE_SYSTEM: + if (info.Characteristics & FILE_REMOTE_DEVICE) ret = DRIVE_REMOTE; + else if (info.Characteristics & FILE_REMOVABLE_MEDIA) ret = DRIVE_REMOVABLE; + else if ((ret = get_mountmgr_drive_type( root )) == DRIVE_UNKNOWN) ret = DRIVE_FIXED; + break; + default: + ret = DRIVE_UNKNOWN; + break; + } + } + TRACE( "%s -> %d\n", debugstr_w(root), ret ); + return ret; +} + + +/*********************************************************************** + * GetDriveTypeA (kernelbase.@) + */ +UINT WINAPI DECLSPEC_HOTPATCH GetDriveTypeA( LPCSTR root ) +{ + WCHAR *rootW = NULL; + + if (root && !(rootW = file_name_AtoW( root, FALSE ))) return DRIVE_NO_ROOT_DIR; + return GetDriveTypeW( rootW ); +} + + +/*********************************************************************** + * GetDiskFreeSpaceExW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail, + PULARGE_INTEGER total, PULARGE_INTEGER totalfree ) +{ + FILE_FS_SIZE_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + UINT units; + + TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree ); + + if (!open_device_root( root, &handle )) return FALSE; + + status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation ); + NtClose( handle ); + if (!set_ntstatus( status )) return FALSE; + + units = info.SectorsPerAllocationUnit * info.BytesPerSector; + if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units; + if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units; + /* FIXME: this one should take quotas into account */ + if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units; + return TRUE; +} + + +/*********************************************************************** + * GetDiskFreeSpaceExA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail, + PULARGE_INTEGER total, PULARGE_INTEGER totalfree ) +{ + WCHAR *rootW = NULL; + + if (root && !(rootW = file_name_AtoW( root, FALSE ))) return FALSE; + return GetDiskFreeSpaceExW( rootW, avail, total, totalfree ); +} + + +/*********************************************************************** + * GetDiskFreeSpaceW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors, + LPDWORD sector_bytes, LPDWORD free_clusters, + LPDWORD total_clusters ) +{ + FILE_FS_SIZE_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + UINT units; + + TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root), + cluster_sectors, sector_bytes, free_clusters, total_clusters ); + + if (!open_device_root( root, &handle )) return FALSE; + + status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation ); + NtClose( handle ); + if (!set_ntstatus( status )) return FALSE; + + units = info.SectorsPerAllocationUnit * info.BytesPerSector; + + if( GetVersion() & 0x80000000) { /* win3.x, 9x, ME */ + /* cap the size and available at 2GB as per specs */ + if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff) { + info.TotalAllocationUnits.QuadPart = 0x7fffffff / units; + if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff) + info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units; + } + /* nr. of clusters is always <= 65335 */ + while( info.TotalAllocationUnits.QuadPart > 65535 ) { + info.TotalAllocationUnits.QuadPart /= 2; + info.AvailableAllocationUnits.QuadPart /= 2; + info.SectorsPerAllocationUnit *= 2; + } + } + + if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit; + if (sector_bytes) *sector_bytes = info.BytesPerSector; + if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart; + if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart; + TRACE("%#08lx, %#08lx, %#08lx, %#08lx\n", info.SectorsPerAllocationUnit, info.BytesPerSector, + info.AvailableAllocationUnits.u.LowPart, info.TotalAllocationUnits.u.LowPart); + return TRUE; +} + + +/*********************************************************************** + * GetDiskFreeSpaceA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors, + LPDWORD sector_bytes, LPDWORD free_clusters, + LPDWORD total_clusters ) +{ + WCHAR *rootW = NULL; + + if (root && !(rootW = file_name_AtoW( root, FALSE ))) return FALSE; + return GetDiskFreeSpaceW( rootW, cluster_sectors, sector_bytes, free_clusters, total_clusters ); +} + + +static BOOL is_dos_path( const UNICODE_STRING *path ) +{ + static const WCHAR global_prefix[4] = {'\\','?','?','\\'}; + return path->Length >= 7 * sizeof(WCHAR) + && !memcmp(path->Buffer, global_prefix, sizeof(global_prefix)) + && path->Buffer[5] == ':' && path->Buffer[6] == '\\'; +} + +/* resolve all symlinks in a path in-place; return FALSE if allocation failed */ +static BOOL resolve_symlink( UNICODE_STRING *path ) +{ + OBJECT_NAME_INFORMATION *info; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE file; + ULONG size; + + InitializeObjectAttributes( &attr, path, OBJ_CASE_INSENSITIVE, 0, NULL ); + if (NtOpenFile( &file, SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT )) + return TRUE; + + if (NtQueryObject( file, ObjectNameInformation, NULL, 0, &size ) != STATUS_INFO_LENGTH_MISMATCH) + { + NtClose( file ); + return TRUE; + } + + if (!(info = HeapAlloc( GetProcessHeap(), 0, size ))) + { + NtClose( file ); + return FALSE; + } + + status = NtQueryObject( file, ObjectNameInformation, info, size, NULL ); + NtClose( file ); + if (status) + return TRUE; + + RtlFreeUnicodeString( path ); + status = RtlDuplicateUnicodeString( 0, &info->Name, path ); + HeapFree( GetProcessHeap(), 0, info ); + return !status; +} + +/*********************************************************************** + * GetVolumePathNameW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetVolumePathNameW( const WCHAR *path, WCHAR *volume_path, DWORD length ) +{ + FILE_ATTRIBUTE_TAG_INFORMATION attr_info; + FILE_BASIC_INFORMATION basic_info; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nt_name; + NTSTATUS status; + + if (path && !wcsncmp(path, L"\\??\\", 4)) + { + WCHAR current_drive[MAX_PATH]; + + GetCurrentDirectoryW( ARRAY_SIZE(current_drive), current_drive ); + if (length >= 3) + { + WCHAR ret_path[4] = {current_drive[0], ':', '\\', 0}; + lstrcpynW( volume_path, ret_path, length ); + return TRUE; + } + + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return FALSE; + } + + if (!volume_path || !length || !RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + if (!is_dos_path( &nt_name )) + { + RtlFreeUnicodeString( &nt_name ); + WARN("invalid path %s\n", debugstr_w(path)); + SetLastError( ERROR_INVALID_NAME ); + return FALSE; + } + + InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL ); + + while (nt_name.Length > 7 * sizeof(WCHAR)) + { + IO_STATUS_BLOCK io; + HANDLE file; + + if (!NtQueryAttributesFile( &attr, &basic_info ) + && (basic_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + && (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + && !NtOpenFile( &file, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN_REPARSE_POINT | FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT )) + { + status = NtQueryInformationFile( file, &io, &attr_info, + sizeof(attr_info), FileAttributeTagInformation ); + NtClose( file ); + if (!status) + { + + if (attr_info.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) + break; + + if (!resolve_symlink( &nt_name )) + { + SetLastError( ERROR_OUTOFMEMORY ); + return FALSE; + } + } + } + + if (nt_name.Buffer[(nt_name.Length / sizeof(WCHAR)) - 1] == '\\') + nt_name.Length -= sizeof(WCHAR); + while (nt_name.Length && nt_name.Buffer[(nt_name.Length / sizeof(WCHAR)) - 1] != '\\') + nt_name.Length -= sizeof(WCHAR); + } + + nt_name.Buffer[nt_name.Length / sizeof(WCHAR)] = 0; + + if (NtQueryAttributesFile( &attr, &basic_info )) + { + RtlFreeUnicodeString( &nt_name ); + WARN("nonexistent path %s -> %s\n", debugstr_w(path), debugstr_w( nt_name.Buffer )); + SetLastError( ERROR_FILE_NOT_FOUND ); + return FALSE; + } + + if (!wcsncmp(path, L"\\\\.\\", 4) || !wcsncmp(path, L"\\\\?\\", 4)) + { + if (length >= nt_name.Length / sizeof(WCHAR)) + { + memcpy(volume_path, path, 4 * sizeof(WCHAR)); + lstrcpynW( volume_path + 4, nt_name.Buffer + 4, length - 4 ); + + TRACE("%s -> %s\n", debugstr_w(path), debugstr_w(volume_path)); + + RtlFreeUnicodeString( &nt_name ); + return TRUE; + } + } + else if (length >= (nt_name.Length / sizeof(WCHAR)) - 4) + { + lstrcpynW( volume_path, nt_name.Buffer + 4, length ); + volume_path[0] = towupper(volume_path[0]); + + TRACE("%s -> %s\n", debugstr_w(path), debugstr_w(volume_path)); + + RtlFreeUnicodeString( &nt_name ); + return TRUE; + } + + RtlFreeUnicodeString( &nt_name ); + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return FALSE; +} + + +static MOUNTMGR_MOUNT_POINTS *query_mount_points( HANDLE mgr, MOUNTMGR_MOUNT_POINT *input, DWORD insize ) +{ + MOUNTMGR_MOUNT_POINTS *output; + DWORD outsize = 1024; + DWORD br; + + for (;;) + { + if (!(output = HeapAlloc( GetProcessHeap(), 0, outsize ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return NULL; + } + if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, insize, output, outsize, &br, NULL )) break; + outsize = output->Size; + HeapFree( GetProcessHeap(), 0, output ); + if (GetLastError() != ERROR_MORE_DATA) return NULL; + } + return output; +} + +/*********************************************************************** + * GetVolumePathNamesForVolumeNameW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetVolumePathNamesForVolumeNameW( LPCWSTR volumename, LPWSTR volumepathname, + DWORD buflen, PDWORD returnlen ) +{ + static const WCHAR dosdevicesW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'}; + HANDLE mgr; + DWORD len, size; + MOUNTMGR_MOUNT_POINT *spec; + MOUNTMGR_MOUNT_POINTS *link, *target = NULL; + WCHAR *name, *path; + BOOL ret = FALSE; + UINT i, j; + + TRACE("%s, %p, %lu, %p\n", debugstr_w(volumename), volumepathname, buflen, returnlen); + + if (!volumename || (len = lstrlenW( volumename )) != 49) + { + SetLastError( ERROR_INVALID_NAME ); + return FALSE; + } + mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); + if (mgr == INVALID_HANDLE_VALUE) return FALSE; + + size = sizeof(*spec) + sizeof(WCHAR) * (len - 1); /* remove trailing backslash */ + if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done; + spec->SymbolicLinkNameOffset = sizeof(*spec); + spec->SymbolicLinkNameLength = size - sizeof(*spec); + name = (WCHAR *)((char *)spec + spec->SymbolicLinkNameOffset); + memcpy( name, volumename, size - sizeof(*spec) ); + name[1] = '?'; /* map \\?\ to \??\ */ + + target = query_mount_points( mgr, spec, size ); + HeapFree( GetProcessHeap(), 0, spec ); + if (!target) + { + goto done; + } + if (!target->NumberOfMountPoints) + { + SetLastError( ERROR_FILE_NOT_FOUND ); + goto done; + } + len = 0; + path = volumepathname; + for (i = 0; i < target->NumberOfMountPoints; i++) + { + link = NULL; + if (target->MountPoints[i].DeviceNameOffset) + { + const WCHAR *device = (const WCHAR *)((const char *)target + target->MountPoints[i].DeviceNameOffset); + USHORT device_len = target->MountPoints[i].DeviceNameLength; + + size = sizeof(*spec) + device_len; + if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done; + spec->DeviceNameOffset = sizeof(*spec); + spec->DeviceNameLength = device_len; + memcpy( (char *)spec + spec->DeviceNameOffset, device, device_len ); + + link = query_mount_points( mgr, spec, size ); + HeapFree( GetProcessHeap(), 0, spec ); + } + else if (target->MountPoints[i].UniqueIdOffset) + { + const WCHAR *id = (const WCHAR *)((const char *)target + target->MountPoints[i].UniqueIdOffset); + USHORT id_len = target->MountPoints[i].UniqueIdLength; + + size = sizeof(*spec) + id_len; + if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done; + spec->UniqueIdOffset = sizeof(*spec); + spec->UniqueIdLength = id_len; + memcpy( (char *)spec + spec->UniqueIdOffset, id, id_len ); + + link = query_mount_points( mgr, spec, size ); + HeapFree( GetProcessHeap(), 0, spec ); + } + if (!link) continue; + for (j = 0; j < link->NumberOfMountPoints; j++) + { + const WCHAR *linkname; + + if (!link->MountPoints[j].SymbolicLinkNameOffset) continue; + linkname = (const WCHAR *)((const char *)link + link->MountPoints[j].SymbolicLinkNameOffset); + + if (link->MountPoints[j].SymbolicLinkNameLength == sizeof(dosdevicesW) + 2 * sizeof(WCHAR) && + !wcsnicmp( linkname, dosdevicesW, ARRAY_SIZE( dosdevicesW ))) + { + len += 4; + if (volumepathname && len < buflen) + { + path[0] = linkname[ARRAY_SIZE( dosdevicesW )]; + path[1] = ':'; + path[2] = '\\'; + path[3] = 0; + path += 4; + } + } + } + HeapFree( GetProcessHeap(), 0, link ); + } + if (buflen <= len) SetLastError( ERROR_MORE_DATA ); + else if (volumepathname) + { + volumepathname[len] = 0; + ret = TRUE; + } + if (returnlen) *returnlen = len + 1; + +done: + HeapFree( GetProcessHeap(), 0, target ); + CloseHandle( mgr ); + return ret; +} + + +/*********************************************************************** + * FindFirstVolumeW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstVolumeW( LPWSTR volume, DWORD len ) +{ + DWORD size = 1024; + DWORD br; + HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0 ); + if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; + + for (;;) + { + MOUNTMGR_MOUNT_POINT input; + MOUNTMGR_MOUNT_POINTS *output; + + if (!(output = HeapAlloc( GetProcessHeap(), 0, size ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + break; + } + memset( &input, 0, sizeof(input) ); + + if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input), + output, size, &br, NULL )) + { + if (GetLastError() != ERROR_MORE_DATA) break; + size = output->Size; + HeapFree( GetProcessHeap(), 0, output ); + continue; + } + CloseHandle( mgr ); + /* abuse the Size field to store the current index */ + output->Size = 0; + if (!FindNextVolumeW( output, volume, len )) + { + HeapFree( GetProcessHeap(), 0, output ); + return INVALID_HANDLE_VALUE; + } + return output; + } + CloseHandle( mgr ); + return INVALID_HANDLE_VALUE; +} + + +/*********************************************************************** + * FindNextVolumeW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindNextVolumeW( HANDLE handle, LPWSTR volume, DWORD len ) +{ + MOUNTMGR_MOUNT_POINTS *data = handle; + + while (data->Size < data->NumberOfMountPoints) + { + static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',}; + WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset); + DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength; + data->Size++; + /* skip non-volumes */ + if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue; + if (size + sizeof(WCHAR) >= len * sizeof(WCHAR)) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return FALSE; + } + memcpy( volume, link, size ); + volume[1] = '\\'; /* map \??\ to \\?\ */ + volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */ + volume[size / sizeof(WCHAR) + 1] = 0; + TRACE( "returning entry %lu %s\n", data->Size - 1, debugstr_w(volume) ); + return TRUE; + } + SetLastError( ERROR_NO_MORE_FILES ); + return FALSE; +} + + +/*********************************************************************** + * FindVolumeClose (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindVolumeClose( HANDLE handle ) +{ + return HeapFree( GetProcessHeap(), 0, handle ); +} + + +/*********************************************************************** + * DeleteVolumeMountPointW (kernelbase.@) + */ +BOOL WINAPI /* DECLSPEC_HOTPATCH */ DeleteVolumeMountPointW( LPCWSTR mountpoint ) +{ + FIXME("(%s), stub!\n", debugstr_w(mountpoint)); + return FALSE; +} + + +/*********************************************************************** + * GetVolumeInformationByHandleW (kernelbase.@) + */ +BOOL WINAPI GetVolumeInformationByHandleW( HANDLE handle, WCHAR *label, DWORD label_len, + DWORD *serial, DWORD *filename_len, DWORD *flags, + WCHAR *fsname, DWORD fsname_len ) +{ + IO_STATUS_BLOCK io; + + TRACE( "%p\n", handle ); + + if (label || serial) + { + char buffer[sizeof(FILE_FS_VOLUME_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; + FILE_FS_VOLUME_INFORMATION *info = (FILE_FS_VOLUME_INFORMATION *)buffer; + + if (!set_ntstatus( NtQueryVolumeInformationFile( handle, &io, info, sizeof(buffer), + FileFsVolumeInformation ) )) + return FALSE; + + if (label) + { + if (label_len < info->VolumeLabelLength / sizeof(WCHAR) + 1) + { + SetLastError( ERROR_BAD_LENGTH ); + return FALSE; + } + memcpy( label, info->VolumeLabel, info->VolumeLabelLength ); + label[info->VolumeLabelLength / sizeof(WCHAR)] = 0; + } + if (serial) *serial = info->VolumeSerialNumber; + } + + if (filename_len || flags || fsname) + { + char buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; + FILE_FS_ATTRIBUTE_INFORMATION *info = (FILE_FS_ATTRIBUTE_INFORMATION *)buffer; + + if (!set_ntstatus( NtQueryVolumeInformationFile( handle, &io, info, sizeof(buffer), + FileFsAttributeInformation ) )) + return FALSE; + + if (fsname) + { + if (fsname_len < info->FileSystemNameLength / sizeof(WCHAR) + 1) + { + SetLastError( ERROR_BAD_LENGTH ); + return FALSE; + } + memcpy( fsname, info->FileSystemName, info->FileSystemNameLength ); + fsname[info->FileSystemNameLength / sizeof(WCHAR)] = 0; + } + if (filename_len) *filename_len = info->MaximumComponentNameLength; + if (flags) *flags = info->FileSystemAttributes; + } + + return TRUE; +} diff --git a/dll/win32/KernelBase/wine/winerror.mc b/dll/win32/KernelBase/wine/winerror.mc new file mode 100644 index 0000000000000..45ad7153463b7 --- /dev/null +++ b/dll/win32/KernelBase/wine/winerror.mc @@ -0,0 +1,3842 @@ +; +; Copyright 2000 Dave Pickles +; +; This library is free software; you can redistribute it and/or +; modify it under the terms of the GNU Lesser General Public +; License as published by the Free Software Foundation; either +; version 2.1 of the License, or (at your option) any later version. +; +; This library 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 +; Lesser General Public License for more details. +; +; You should have received a copy of the GNU Lesser General Public +; License along with this library; if not, write to the Free Software +; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +; + +LanguageNames=(ENU=0x409:winerr) +SeverityNames=(CoError=0x2:STATUS_SEVERITY_COERROR) +FacilityNames=(Trust=0xb:FACILITY_CERT) + +MessageId=0 +SymbolicName=ERROR_SUCCESS +Language=ENU +Success. +. +MessageId=1 +SymbolicName=ERROR_INVALID_FUNCTION +Language=ENU +Invalid function. +. +MessageId=2 +SymbolicName=ERROR_FILE_NOT_FOUND +Language=ENU +File not found. +. +MessageId=3 +SymbolicName=ERROR_PATH_NOT_FOUND +Language=ENU +Path not found. +. +MessageId=4 +SymbolicName=ERROR_TOO_MANY_OPEN_FILES +Language=ENU +Too many open files. +. +MessageId=5 +SymbolicName=ERROR_ACCESS_DENIED +Language=ENU +Access denied. +. +MessageId=6 +SymbolicName=ERROR_INVALID_HANDLE +Language=ENU +Invalid handle. +. +MessageId=7 +SymbolicName=ERROR_ARENA_TRASHED +Language=ENU +Memory trashed. +. +MessageId=8 +SymbolicName=ERROR_NOT_ENOUGH_MEMORY +Language=ENU +Not enough memory. +. +MessageId=9 +SymbolicName=ERROR_INVALID_BLOCK +Language=ENU +Invalid block. +. +MessageId=10 +SymbolicName=ERROR_BAD_ENVIRONMENT +Language=ENU +Bad environment. +. +MessageId=11 +SymbolicName=ERROR_BAD_FORMAT +Language=ENU +Bad format. +. +MessageId=12 +SymbolicName=ERROR_INVALID_ACCESS +Language=ENU +Invalid access. +. +MessageId=13 +SymbolicName=ERROR_INVALID_DATA +Language=ENU +Invalid data. +. +MessageId=14 +SymbolicName=ERROR_OUTOFMEMORY +Language=ENU +Out of memory. +. +MessageId=15 +SymbolicName=ERROR_INVALID_DRIVE +Language=ENU +Invalid drive. +. +MessageId=16 +SymbolicName=ERROR_CURRENT_DIRECTORY +Language=ENU +Can't delete current directory. +. +MessageId=17 +SymbolicName=ERROR_NOT_SAME_DEVICE +Language=ENU +Not same device. +. +MessageId=18 +SymbolicName=ERROR_NO_MORE_FILES +Language=ENU +No more files. +. +MessageId=19 +SymbolicName=ERROR_WRITE_PROTECT +Language=ENU +Write protected. +. +MessageId=20 +SymbolicName=ERROR_BAD_UNIT +Language=ENU +Bad unit. +. +MessageId=21 +SymbolicName=ERROR_NOT_READY +Language=ENU +Not ready. +. +MessageId=22 +SymbolicName=ERROR_BAD_COMMAND +Language=ENU +Bad command. +. +MessageId=23 +SymbolicName=ERROR_CRC +Language=ENU +CRC error. +. +MessageId=24 +SymbolicName=ERROR_BAD_LENGTH +Language=ENU +Bad length. +. +MessageId=25 +SymbolicName=ERROR_SEEK +Language=ENU +Seek error. +. +MessageId=26 +SymbolicName=ERROR_NOT_DOS_DISK +Language=ENU +Not DOS disk. +. +MessageId=27 +SymbolicName=ERROR_SECTOR_NOT_FOUND +Language=ENU +Sector not found. +. +MessageId=28 +SymbolicName=ERROR_OUT_OF_PAPER +Language=ENU +Out of paper. +. +MessageId=29 +SymbolicName=ERROR_WRITE_FAULT +Language=ENU +Write fault. +. +MessageId=30 +SymbolicName=ERROR_READ_FAULT +Language=ENU +Read fault. +. +MessageId=31 +SymbolicName=ERROR_GEN_FAILURE +Language=ENU +General failure. +. +MessageId=32 +SymbolicName=ERROR_SHARING_VIOLATION +Language=ENU +Sharing violation. +. +MessageId=33 +SymbolicName=ERROR_LOCK_VIOLATION +Language=ENU +Lock violation. +. +MessageId=34 +SymbolicName=ERROR_WRONG_DISK +Language=ENU +Wrong disk. +. +MessageId=36 +SymbolicName=ERROR_SHARING_BUFFER_EXCEEDED +Language=ENU +Sharing buffer exceeded. +. +MessageId=38 +SymbolicName=ERROR_HANDLE_EOF +Language=ENU +End of file. +. +MessageId=39 +SymbolicName=ERROR_HANDLE_DISK_FULL +Language=ENU +Disk full. +. +MessageId=50 +SymbolicName=ERROR_NOT_SUPPORTED +Language=ENU +Request not supported. +. +MessageId=51 +SymbolicName=ERROR_REM_NOT_LIST +Language=ENU +Remote machine not listening. +. +MessageId=52 +SymbolicName=ERROR_DUP_NAME +Language=ENU +Duplicate network name. +. +MessageId=53 +SymbolicName=ERROR_BAD_NETPATH +Language=ENU +Bad network path. +. +MessageId=54 +SymbolicName=ERROR_NETWORK_BUSY +Language=ENU +Network busy. +. +MessageId=55 +SymbolicName=ERROR_DEV_NOT_EXIST +Language=ENU +Device does not exist. +. +MessageId=56 +SymbolicName=ERROR_TOO_MANY_CMDS +Language=ENU +Too many commands. +. +MessageId=57 +SymbolicName=ERROR_ADAP_HDW_ERR +Language=ENU +Adapter hardware error. +. +MessageId=58 +SymbolicName=ERROR_BAD_NET_RESP +Language=ENU +Bad network response. +. +MessageId=59 +SymbolicName=ERROR_UNEXP_NET_ERR +Language=ENU +Unexpected network error. +. +MessageId=60 +SymbolicName=ERROR_BAD_REM_ADAP +Language=ENU +Bad remote adapter. +. +MessageId=61 +SymbolicName=ERROR_PRINTQ_FULL +Language=ENU +Print queue full. +. +MessageId=62 +SymbolicName=ERROR_NO_SPOOL_SPACE +Language=ENU +No spool space. +. +MessageId=63 +SymbolicName=ERROR_PRINT_CANCELLED +Language=ENU +Print canceled. +. +MessageId=64 +SymbolicName=ERROR_NETNAME_DELETED +Language=ENU +Network name deleted. +. +MessageId=65 +SymbolicName=ERROR_NETWORK_ACCESS_DENIED +Language=ENU +Network access denied. +. +MessageId=66 +SymbolicName=ERROR_BAD_DEV_TYPE +Language=ENU +Bad device type. +. +MessageId=67 +SymbolicName=ERROR_BAD_NET_NAME +Language=ENU +Bad network name. +. +MessageId=68 +SymbolicName=ERROR_TOO_MANY_NAMES +Language=ENU +Too many network names. +. +MessageId=69 +SymbolicName=ERROR_TOO_MANY_SESS +Language=ENU +Too many network sessions. +. +MessageId=70 +SymbolicName=ERROR_SHARING_PAUSED +Language=ENU +Sharing paused. +. +MessageId=71 +SymbolicName=ERROR_REQ_NOT_ACCEP +Language=ENU +Request not accepted. +. +MessageId=72 +SymbolicName=ERROR_REDIR_PAUSED +Language=ENU +Redirector paused. +. +MessageId=80 +SymbolicName=ERROR_FILE_EXISTS +Language=ENU +File exists. +. +MessageId=82 +SymbolicName=ERROR_CANNOT_MAKE +Language=ENU +Cannot create. +. +MessageId=83 +SymbolicName=ERROR_FAIL_I24 +Language=ENU +Int24 failure. +. +MessageId=84 +SymbolicName=ERROR_OUT_OF_STRUCTURES +Language=ENU +Out of structures. +. +MessageId=85 +SymbolicName=ERROR_ALREADY_ASSIGNED +Language=ENU +Already assigned. +. +MessageId=86 +SymbolicName=ERROR_INVALID_PASSWORD +Language=ENU +Invalid password. +. +MessageId=87 +SymbolicName=ERROR_INVALID_PARAMETER +Language=ENU +Invalid parameter. +. +MessageId=88 +SymbolicName=ERROR_NET_WRITE_FAULT +Language=ENU +Net write fault. +. +MessageId=89 +SymbolicName=ERROR_NO_PROC_SLOTS +Language=ENU +No process slots. +. +MessageId=100 +SymbolicName=ERROR_TOO_MANY_SEMAPHORES +Language=ENU +Too many semaphores. +. +MessageId=101 +SymbolicName=ERROR_EXCL_SEM_ALREADY_OWNED +Language=ENU +Exclusive semaphore already owned. +. +MessageId=102 +SymbolicName=ERROR_SEM_IS_SET +Language=ENU +Semaphore is set. +. +MessageId=103 +SymbolicName=ERROR_TOO_MANY_SEM_REQUESTS +Language=ENU +Too many semaphore requests. +. +MessageId=104 +SymbolicName=ERROR_INVALID_AT_INTERRUPT_TIME +Language=ENU +Invalid at interrupt time. +. +MessageId=105 +SymbolicName=ERROR_SEM_OWNER_DIED +Language=ENU +Semaphore owner died. +. +MessageId=106 +SymbolicName=ERROR_SEM_USER_LIMIT +Language=ENU +Semaphore user limit. +. +MessageId=107 +SymbolicName=ERROR_DISK_CHANGE +Language=ENU +Insert disk for drive %1. +. +MessageId=108 +SymbolicName=ERROR_DRIVE_LOCKED +Language=ENU +Drive locked. +. +MessageId=109 +SymbolicName=ERROR_BROKEN_PIPE +Language=ENU +Broken pipe. +. +MessageId=110 +SymbolicName=ERROR_OPEN_FAILED +Language=ENU +Open failed. +. +MessageId=111 +SymbolicName=ERROR_BUFFER_OVERFLOW +Language=ENU +Buffer overflow. +. +MessageId=112 +SymbolicName=ERROR_DISK_FULL +Language=ENU +Disk full. +. +MessageId=113 +SymbolicName=ERROR_NO_MORE_SEARCH_HANDLES +Language=ENU +No more search handles. +. +MessageId=114 +SymbolicName=ERROR_INVALID_TARGET_HANDLE +Language=ENU +Invalid target handle. +. +MessageId=117 +SymbolicName=ERROR_INVALID_CATEGORY +Language=ENU +Invalid IOCTL. +. +MessageId=118 +SymbolicName=ERROR_INVALID_VERIFY_SWITCH +Language=ENU +Invalid verify switch. +. +MessageId=119 +SymbolicName=ERROR_BAD_DRIVER_LEVEL +Language=ENU +Bad driver level. +. +MessageId=120 +SymbolicName=ERROR_CALL_NOT_IMPLEMENTED +Language=ENU +Call not implemented. +. +MessageId=121 +SymbolicName=ERROR_SEM_TIMEOUT +Language=ENU +Semaphore timeout. +. +MessageId=122 +SymbolicName=ERROR_INSUFFICIENT_BUFFER +Language=ENU +Insufficient buffer. +. +MessageId=123 +SymbolicName=ERROR_INVALID_NAME +Language=ENU +Invalid name. +. +MessageId=124 +SymbolicName=ERROR_INVALID_LEVEL +Language=ENU +Invalid level. +. +MessageId=125 +SymbolicName=ERROR_NO_VOLUME_LABEL +Language=ENU +No volume label. +. +MessageId=126 +SymbolicName=ERROR_MOD_NOT_FOUND +Language=ENU +Module not found. +. +MessageId=127 +SymbolicName=ERROR_PROC_NOT_FOUND +Language=ENU +Procedure not found. +. +MessageId=128 +SymbolicName=ERROR_WAIT_NO_CHILDREN +Language=ENU +No children to wait for. +. +MessageId=129 +SymbolicName=ERROR_CHILD_NOT_COMPLETE +Language=ENU +Child process has not completed. +. +MessageId=130 +SymbolicName=ERROR_DIRECT_ACCESS_HANDLE +Language=ENU +Invalid use of direct access handle. +. +MessageId=131 +SymbolicName=ERROR_NEGATIVE_SEEK +Language=ENU +Negative seek. +. +MessageId=132 +SymbolicName=ERROR_SEEK_ON_DEVICE +Language=ENU +Seek error. +. +MessageId=133 +SymbolicName=ERROR_IS_JOIN_TARGET +Language=ENU +Drive is a JOIN target. +. +MessageId=134 +SymbolicName=ERROR_IS_JOINED +Language=ENU +Drive is already JOINed. +. +MessageId=135 +SymbolicName=ERROR_IS_SUBSTED +Language=ENU +Drive is already SUBSTed. +. +MessageId=136 +SymbolicName=ERROR_NOT_JOINED +Language=ENU +Drive is not JOINed. +. +MessageId=137 +SymbolicName=ERROR_NOT_SUBSTED +Language=ENU +Drive is not SUBSTed. +. +MessageId=138 +SymbolicName=ERROR_JOIN_TO_JOIN +Language=ENU +Attempt to JOIN onto a JOINed drive. +. +MessageId=139 +SymbolicName=ERROR_SUBST_TO_SUBST +Language=ENU +Attempt to SUBST onto a SUBSTed drive. +. +MessageId=140 +SymbolicName=ERROR_JOIN_TO_SUBST +Language=ENU +Attempt to JOIN to a SUBSTed drive. +. +MessageId=141 +SymbolicName=ERROR_SUBST_TO_JOIN +Language=ENU +Attempt to SUBST to a JOINed drive. +. +MessageId=142 +SymbolicName=ERROR_BUSY_DRIVE +Language=ENU +Drive is busy. +. +MessageId=143 +SymbolicName=ERROR_SAME_DRIVE +Language=ENU +Same drive. +. +MessageId=144 +SymbolicName=ERROR_DIR_NOT_ROOT +Language=ENU +Not top-level directory. +. +MessageId=145 +SymbolicName=ERROR_DIR_NOT_EMPTY +Language=ENU +Directory is not empty. +. +MessageId=146 +SymbolicName=ERROR_IS_SUBST_PATH +Language=ENU +Path is in use as a SUBST. +. +MessageId=147 +SymbolicName=ERROR_IS_JOIN_PATH +Language=ENU +Path is in use as a JOIN. +. +MessageId=148 +SymbolicName=ERROR_PATH_BUSY +Language=ENU +Path is busy. +. +MessageId=149 +SymbolicName=ERROR_IS_SUBST_TARGET +Language=ENU +Already a SUBST target. +. +MessageId=150 +SymbolicName=ERROR_SYSTEM_TRACE +Language=ENU +System trace not specified or disallowed. +. +MessageId=151 +SymbolicName=ERROR_INVALID_EVENT_COUNT +Language=ENU +Event count for DosMuxSemWait incorrect. +. +MessageId=152 +SymbolicName=ERROR_TOO_MANY_MUXWAITERS +Language=ENU +Too many waiters for DosMuxSemWait. +. +MessageId=153 +SymbolicName=ERROR_INVALID_LIST_FORMAT +Language=ENU +DosSemMuxWait list invalid. +. +MessageId=154 +SymbolicName=ERROR_LABEL_TOO_LONG +Language=ENU +Volume label too long. +. +MessageId=155 +SymbolicName=ERROR_TOO_MANY_TCBS +Language=ENU +Too many TCBs. +. +MessageId=156 +SymbolicName=ERROR_SIGNAL_REFUSED +Language=ENU +Signal refused. +. +MessageId=157 +SymbolicName=ERROR_DISCARDED +Language=ENU +Segment discarded. +. +MessageId=158 +SymbolicName=ERROR_NOT_LOCKED +Language=ENU +Segment not locked. +. +MessageId=159 +SymbolicName=ERROR_BAD_THREADID_ADDR +Language=ENU +Bad thread ID address. +. +MessageId=160 +SymbolicName=ERROR_BAD_ARGUMENTS +Language=ENU +Bad arguments to DosExecPgm. +. +MessageId=161 +SymbolicName=ERROR_BAD_PATHNAME +Language=ENU +Path is invalid. +. +MessageId=162 +SymbolicName=ERROR_SIGNAL_PENDING +Language=ENU +Signal pending. +. +MessageId=164 +SymbolicName=ERROR_MAX_THRDS_REACHED +Language=ENU +Max system-wide thread count reached. +. +MessageId=167 +SymbolicName=ERROR_LOCK_FAILED +Language=ENU +Lock failed. +. +MessageId=170 +SymbolicName=ERROR_BUSY +Language=ENU +Resource in use. +. +MessageId=173 +SymbolicName=ERROR_CANCEL_VIOLATION +Language=ENU +Cancel violation. +. +MessageId=174 +SymbolicName=ERROR_ATOMIC_LOCKS_NOT_SUPPORTED +Language=ENU +Atomic locks not supported. +. +MessageId=180 +SymbolicName=ERROR_INVALID_SEGMENT_NUMBER +Language=ENU +Invalid segment number. +. +MessageId=182 +SymbolicName=ERROR_INVALID_ORDINAL +Language=ENU +Invalid ordinal for %1. +. +MessageId=183 +SymbolicName=ERROR_ALREADY_EXISTS +Language=ENU +File already exists. +. +MessageId=186 +SymbolicName=ERROR_INVALID_FLAG_NUMBER +Language=ENU +Invalid flag number. +. +MessageId=187 +SymbolicName=ERROR_SEM_NOT_FOUND +Language=ENU +Semaphore name not found. +. +MessageId=188 +SymbolicName=ERROR_INVALID_STARTING_CODESEG +Language=ENU +Invalid starting code segment for %1. +. +MessageId=189 +SymbolicName=ERROR_INVALID_STACKSEG +Language=ENU +Invalid starting stack segment for %1. +. +MessageId=190 +SymbolicName=ERROR_INVALID_MODULETYPE +Language=ENU +Invalid module type for %1. +. +MessageId=191 +SymbolicName=ERROR_INVALID_EXE_SIGNATURE +Language=ENU +Invalid EXE signature in %1. +. +MessageId=192 +SymbolicName=ERROR_EXE_MARKED_INVALID +Language=ENU +EXE %1 is marked invalid. +. +MessageId=193 +SymbolicName=ERROR_BAD_EXE_FORMAT +Language=ENU +Bad EXE format for %1. +. +MessageId=194 +SymbolicName=ERROR_ITERATED_DATA_EXCEEDS_64k +Language=ENU +Iterated data exceeds 64k in %1. +. +MessageId=195 +SymbolicName=ERROR_INVALID_MINALLOCSIZE +Language=ENU +Invalid MinAllocSize in %1. +. +MessageId=196 +SymbolicName=ERROR_DYNLINK_FROM_INVALID_RING +Language=ENU +Dynlink from invalid ring. +. +MessageId=197 +SymbolicName=ERROR_IOPL_NOT_ENABLED +Language=ENU +IOPL not enabled. +. +MessageId=198 +SymbolicName=ERROR_INVALID_SEGDPL +Language=ENU +Invalid SEGDPL in %1. +. +MessageId=199 +SymbolicName=ERROR_AUTODATASEG_EXCEEDS_64k +Language=ENU +Auto data segment exceeds 64k. +. +MessageId=200 +SymbolicName=ERROR_RING2SEG_MUST_BE_MOVABLE +Language=ENU +Ring 2 segment must be movable. +. +MessageId=201 +SymbolicName=ERROR_RELOC_CHAIN_XEEDS_SEGLIM +Language=ENU +Relocation chain exceeds segment limit in %1. +. +MessageId=202 +SymbolicName=ERROR_INFLOOP_IN_RELOC_CHAIN +Language=ENU +Infinite loop in relocation chain in %1. +. +MessageId=203 +SymbolicName=ERROR_ENVVAR_NOT_FOUND +Language=ENU +Environment variable not found. +. +MessageId=205 +SymbolicName=ERROR_NO_SIGNAL_SENT +Language=ENU +No signal sent. +. +MessageId=206 +SymbolicName=ERROR_FILENAME_EXCED_RANGE +Language=ENU +File name is too long. +. +MessageId=207 +SymbolicName=ERROR_RING2_STACK_IN_USE +Language=ENU +Ring 2 stack in use. +. +MessageId=208 +SymbolicName=ERROR_META_EXPANSION_TOO_LONG +Language=ENU +Error in use of filename wildcards. +. +MessageId=209 +SymbolicName=ERROR_INVALID_SIGNAL_NUMBER +Language=ENU +Invalid signal number. +. +MessageId=210 +SymbolicName=ERROR_THREAD_1_INACTIVE +Language=ENU +Error setting signal handler. +. +MessageId=212 +SymbolicName=ERROR_LOCKED +Language=ENU +Segment locked. +. +MessageId=214 +SymbolicName=ERROR_TOO_MANY_MODULES +Language=ENU +Too many modules. +. +MessageId=215 +SymbolicName=ERROR_NESTING_NOT_ALLOWED +Language=ENU +Nesting LoadModule calls not allowed. +. +MessageId=216 +SymbolicName=ERROR_EXE_MACHINE_TYPE_MISMATCH +Language=ENU +Machine type mismatch. +. +MessageId=230 +SymbolicName=ERROR_BAD_PIPE +Language=ENU +Bad pipe. +. +MessageId=231 +SymbolicName=ERROR_PIPE_BUSY +Language=ENU +Pipe busy. +. +MessageId=232 +SymbolicName=ERROR_NO_DATA +Language=ENU +Pipe closed. +. +MessageId=233 +SymbolicName=ERROR_PIPE_NOT_CONNECTED +Language=ENU +Pipe not connected. +. +MessageId=234 +SymbolicName=ERROR_MORE_DATA +Language=ENU +More data available. +. +MessageId=240 +SymbolicName=ERROR_VC_DISCONNECTED +Language=ENU +Session canceled. +. +MessageId=254 +SymbolicName=ERROR_INVALID_EA_NAME +Language=ENU +Invalid extended attribute name. +. +MessageId=255 +SymbolicName=ERROR_EA_LIST_INCONSISTENT +Language=ENU +Extended attribute list inconsistent. +. +MessageId=259 +SymbolicName=ERROR_NO_MORE_ITEMS +Language=ENU +No more data available. +. +MessageId=266 +SymbolicName=ERROR_CANNOT_COPY +Language=ENU +Cannot use Copy API. +. +MessageId=267 +SymbolicName=ERROR_DIRECTORY +Language=ENU +Directory name invalid. +. +MessageId=275 +SymbolicName=ERROR_EAS_DIDNT_FIT +Language=ENU +Extended attributes didn't fit. +. +MessageId=276 +SymbolicName=ERROR_EA_FILE_CORRUPT +Language=ENU +Extended attribute file corrupt. +. +MessageId=277 +SymbolicName=ERROR_EA_TABLE_FULL +Language=ENU +Extended attribute table full. +. +MessageId=278 +SymbolicName=ERROR_INVALID_EA_HANDLE +Language=ENU +Invalid extended attribute handle. +. +MessageId=282 +SymbolicName=ERROR_EAS_NOT_SUPPORTED +Language=ENU +Extended attributes not supported. +. +MessageId=288 +SymbolicName=ERROR_NOT_OWNER +Language=ENU +Mutex not owned by caller. +. +MessageId=298 +SymbolicName=ERROR_TOO_MANY_POSTS +Language=ENU +Too many posts to semaphore. +. +MessageId=299 +SymbolicName=ERROR_PARTIAL_COPY +Language=ENU +Read/WriteProcessMemory partially completed. +. +MessageId=300 +SymbolicName=ERROR_OPLOCK_NOT_GRANTED +Language=ENU +The oplock wasn't granted. +. +MessageId=301 +SymbolicName=ERROR_INVALID_OPLOCK_PROTOCOL +Language=ENU +Invalid oplock message received. +. +MessageId=317 +SymbolicName=ERROR_MR_MID_NOT_FOUND +Language=ENU +Message 0x%1 not found in file %2. +. +MessageId=487 +SymbolicName=ERROR_INVALID_ADDRESS +Language=ENU +Invalid address. +. +MessageId=534 +SymbolicName=ERROR_ARITHMETIC_OVERFLOW +Language=ENU +Arithmetic overflow. +. +MessageId=535 +SymbolicName=ERROR_PIPE_CONNECTED +Language=ENU +Pipe connected. +. +MessageId=536 +SymbolicName=ERROR_PIPE_LISTENING +Language=ENU +Pipe listening. +. +MessageId=994 +SymbolicName=ERROR_EA_ACCESS_DENIED +Language=ENU +Extended attribute access denied. +. +MessageId=995 +SymbolicName=ERROR_OPERATION_ABORTED +Language=ENU +I/O operation aborted. +. +MessageId=996 +SymbolicName=ERROR_IO_INCOMPLETE +Language=ENU +Overlapped I/O incomplete. +. +MessageId=997 +SymbolicName=ERROR_IO_PENDING +Language=ENU +Overlapped I/O pending. +. +MessageId=998 +SymbolicName=ERROR_NOACCESS +Language=ENU +No access to memory location. +. +MessageId=999 +SymbolicName=ERROR_SWAPERROR +Language=ENU +Swap error. +. +MessageId=1001 +SymbolicName=ERROR_STACK_OVERFLOW +Language=ENU +Stack overflow. +. +MessageId=1002 +SymbolicName=ERROR_INVALID_MESSAGE +Language=ENU +Invalid message. +. +MessageId=1003 +SymbolicName=ERROR_CAN_NOT_COMPLETE +Language=ENU +Cannot complete. +. +MessageId=1004 +SymbolicName=ERROR_INVALID_FLAGS +Language=ENU +Invalid flags. +. +MessageId=1005 +SymbolicName=ERROR_UNRECOGNIZED_VOLUME +Language=ENU +Unrecognized volume. +. +MessageId=1006 +SymbolicName=ERROR_FILE_INVALID +Language=ENU +File invalid. +. +MessageId=1007 +SymbolicName=ERROR_FULLSCREEN_MODE +Language=ENU +Cannot run full-screen. +. +MessageId=1008 +SymbolicName=ERROR_NO_TOKEN +Language=ENU +Nonexistent token. +. +MessageId=1009 +SymbolicName=ERROR_BADDB +Language=ENU +Registry corrupt. +. +MessageId=1010 +SymbolicName=ERROR_BADKEY +Language=ENU +Invalid key. +. +MessageId=1011 +SymbolicName=ERROR_CANTOPEN +Language=ENU +Can't open registry key. +. +MessageId=1012 +SymbolicName=ERROR_CANTREAD +Language=ENU +Can't read registry key. +. +MessageId=1013 +SymbolicName=ERROR_CANTWRITE +Language=ENU +Can't write registry key. +. +MessageId=1014 +SymbolicName=ERROR_REGISTRY_RECOVERED +Language=ENU +Registry has been recovered. +. +MessageId=1015 +SymbolicName=ERROR_REGISTRY_CORRUPT +Language=ENU +Registry is corrupt. +. +MessageId=1016 +SymbolicName=ERROR_REGISTRY_IO_FAILED +Language=ENU +I/O to registry failed. +. +MessageId=1017 +SymbolicName=ERROR_NOT_REGISTRY_FILE +Language=ENU +Not registry file. +. +MessageId=1018 +SymbolicName=ERROR_KEY_DELETED +Language=ENU +Key deleted. +. +MessageId=1019 +SymbolicName=ERROR_NO_LOG_SPACE +Language=ENU +No registry log space. +. +MessageId=1020 +SymbolicName=ERROR_KEY_HAS_CHILDREN +Language=ENU +Registry key has subkeys. +. +MessageId=1021 +SymbolicName=ERROR_CHILD_MUST_BE_VOLATILE +Language=ENU +Subkey must be volatile. +. +MessageId=1022 +SymbolicName=ERROR_NOTIFY_ENUM_DIR +Language=ENU +Notify change request in progress. +. +MessageId=1051 +SymbolicName=ERROR_DEPENDENT_SERVICES_RUNNING +Language=ENU +Dependent services are running. +. +MessageId=1052 +SymbolicName=ERROR_INVALID_SERVICE_CONTROL +Language=ENU +Invalid service control. +. +MessageId=1053 +SymbolicName=ERROR_SERVICE_REQUEST_TIMEOUT +Language=ENU +Service request timeout. +. +MessageId=1054 +SymbolicName=ERROR_SERVICE_NO_THREAD +Language=ENU +Cannot create service thread. +. +MessageId=1055 +SymbolicName=ERROR_SERVICE_DATABASE_LOCKED +Language=ENU +Service database locked. +. +MessageId=1056 +SymbolicName=ERROR_SERVICE_ALREADY_RUNNING +Language=ENU +Service already running. +. +MessageId=1057 +SymbolicName=ERROR_INVALID_SERVICE_ACCOUNT +Language=ENU +Invalid service account. +. +MessageId=1058 +SymbolicName=ERROR_SERVICE_DISABLED +Language=ENU +Service is disabled. +. +MessageId=1059 +SymbolicName=ERROR_CIRCULAR_DEPENDENCY +Language=ENU +Circular dependency. +. +MessageId=1060 +SymbolicName=ERROR_SERVICE_DOES_NOT_EXIST +Language=ENU +Service does not exist. +. +MessageId=1061 +SymbolicName=ERROR_SERVICE_CANNOT_ACCEPT_CTRL +Language=ENU +Service cannot accept control message. +. +MessageId=1062 +SymbolicName=ERROR_SERVICE_NOT_ACTIVE +Language=ENU +Service not active. +. +MessageId=1063 +SymbolicName=ERROR_FAILED_SERVICE_CONTROLLER_CONNECT +Language=ENU +Service controller connect failed. +. +MessageId=1064 +SymbolicName=ERROR_EXCEPTION_IN_SERVICE +Language=ENU +Exception in service. +. +MessageId=1065 +SymbolicName=ERROR_DATABASE_DOES_NOT_EXIST +Language=ENU +Database does not exist. +. +MessageId=1066 +SymbolicName=ERROR_SERVICE_SPECIFIC_ERROR +Language=ENU +Service-specific error. +. +MessageId=1067 +SymbolicName=ERROR_PROCESS_ABORTED +Language=ENU +Process aborted. +. +MessageId=1068 +SymbolicName=ERROR_SERVICE_DEPENDENCY_FAIL +Language=ENU +Service dependency failed. +. +MessageId=1069 +SymbolicName=ERROR_SERVICE_LOGON_FAILED +Language=ENU +Service login failed. +. +MessageId=1070 +SymbolicName=ERROR_SERVICE_START_HANG +Language=ENU +Service start-hang. +. +MessageId=1071 +SymbolicName=ERROR_INVALID_SERVICE_LOCK +Language=ENU +Invalid service lock. +. +MessageId=1072 +SymbolicName=ERROR_SERVICE_MARKED_FOR_DELETE +Language=ENU +Service marked for delete. +. +MessageId=1073 +SymbolicName=ERROR_SERVICE_EXISTS +Language=ENU +Service exists. +. +MessageId=1074 +SymbolicName=ERROR_ALREADY_RUNNING_LKG +Language=ENU +System running last-known-good config. +. +MessageId=1075 +SymbolicName=ERROR_SERVICE_DEPENDENCY_DELETED +Language=ENU +Service dependency deleted. +. +MessageId=1076 +SymbolicName=ERROR_BOOT_ALREADY_ACCEPTED +Language=ENU +Boot already accepted as last-good config. +. +MessageId=1077 +SymbolicName=ERROR_SERVICE_NEVER_STARTED +Language=ENU +Service not started since last boot. +. +MessageId=1078 +SymbolicName=ERROR_DUPLICATE_SERVICE_NAME +Language=ENU +Duplicate service name. +. +MessageId=1079 +SymbolicName=ERROR_DIFFERENT_SERVICE_ACCOUNT +Language=ENU +Different service account. +. +MessageId=1080 +SymbolicName=ERROR_CANNOT_DETECT_DRIVER_FAILURE +Language=ENU +Driver failure cannot be detected. +. +MessageId=1081 +SymbolicName=ERROR_CANNOT_DETECT_PROCESS_ABORT +Language=ENU +Process abort cannot be detected. +. +MessageId=1082 +SymbolicName=ERROR_NO_RECOVERY_PROGRAM +Language=ENU +No recovery program for service. +. +MessageId=1083 +SymbolicName=ERROR_SERVICE_NOT_IN_EXE +Language=ENU +Service not implemented by exe. +. +MessageId=1100 +SymbolicName=ERROR_END_OF_MEDIA +Language=ENU +End of media. +. +MessageId=1101 +SymbolicName=ERROR_FILEMARK_DETECTED +Language=ENU +Filemark detected. +. +MessageId=1102 +SymbolicName=ERROR_BEGINNING_OF_MEDIA +Language=ENU +Beginning of media. +. +MessageId=1103 +SymbolicName=ERROR_SETMARK_DETECTED +Language=ENU +Setmark detected. +. +MessageId=1104 +SymbolicName=ERROR_NO_DATA_DETECTED +Language=ENU +No data detected. +. +MessageId=1105 +SymbolicName=ERROR_PARTITION_FAILURE +Language=ENU +Partition failure. +. +MessageId=1106 +SymbolicName=ERROR_INVALID_BLOCK_LENGTH +Language=ENU +Invalid block length. +. +MessageId=1107 +SymbolicName=ERROR_DEVICE_NOT_PARTITIONED +Language=ENU +Device not partitioned. +. +MessageId=1108 +SymbolicName=ERROR_UNABLE_TO_LOCK_MEDIA +Language=ENU +Unable to lock media. +. +MessageId=1109 +SymbolicName=ERROR_UNABLE_TO_UNLOAD_MEDIA +Language=ENU +Unable to unload media. +. +MessageId=1110 +SymbolicName=ERROR_MEDIA_CHANGED +Language=ENU +Media changed. +. +MessageId=1111 +SymbolicName=ERROR_BUS_RESET +Language=ENU +I/O bus reset. +. +MessageId=1112 +SymbolicName=ERROR_NO_MEDIA_IN_DRIVE +Language=ENU +No media in drive. +. +MessageId=1113 +SymbolicName=ERROR_NO_UNICODE_TRANSLATION +Language=ENU +No Unicode translation. +. +MessageId=1114 +SymbolicName=ERROR_DLL_INIT_FAILED +Language=ENU +DLL initialization failed. +. +MessageId=1115 +SymbolicName=ERROR_SHUTDOWN_IN_PROGRESS +Language=ENU +Shutdown in progress. +. +MessageId=1116 +SymbolicName=ERROR_NO_SHUTDOWN_IN_PROGRESS +Language=ENU +No shutdown in progress. +. +MessageId=1117 +SymbolicName=ERROR_IO_DEVICE +Language=ENU +I/O device error. +. +MessageId=1118 +SymbolicName=ERROR_SERIAL_NO_DEVICE +Language=ENU +No serial devices found. +. +MessageId=1119 +SymbolicName=ERROR_IRQ_BUSY +Language=ENU +Shared IRQ busy. +. +MessageId=1120 +SymbolicName=ERROR_MORE_WRITES +Language=ENU +Serial I/O completed. +. +MessageId=1121 +SymbolicName=ERROR_COUNTER_TIMEOUT +Language=ENU +Serial I/O counter timeout. +. +MessageId=1122 +SymbolicName=ERROR_FLOPPY_ID_MARK_NOT_FOUND +Language=ENU +Floppy ID address mark not found. +. +MessageId=1123 +SymbolicName=ERROR_FLOPPY_WRONG_CYLINDER +Language=ENU +Floppy reports wrong cylinder. +. +MessageId=1124 +SymbolicName=ERROR_FLOPPY_UNKNOWN_ERROR +Language=ENU +Unknown floppy error. +. +MessageId=1125 +SymbolicName=ERROR_FLOPPY_BAD_REGISTERS +Language=ENU +Floppy registers inconsistent. +. +MessageId=1126 +SymbolicName=ERROR_DISK_RECALIBRATE_FAILED +Language=ENU +Hard disk recalibrate failed. +. +MessageId=1127 +SymbolicName=ERROR_DISK_OPERATION_FAILED +Language=ENU +Hard disk operation failed. +. +MessageId=1128 +SymbolicName=ERROR_DISK_RESET_FAILED +Language=ENU +Hard disk reset failed. +. +MessageId=1129 +SymbolicName=ERROR_EOM_OVERFLOW +Language=ENU +End of tape media. +. +MessageId=1130 +SymbolicName=ERROR_NOT_ENOUGH_SERVER_MEMORY +Language=ENU +Not enough server memory. +. +MessageId=1131 +SymbolicName=ERROR_POSSIBLE_DEADLOCK +Language=ENU +Possible deadlock. +. +MessageId=1132 +SymbolicName=ERROR_MAPPED_ALIGNMENT +Language=ENU +Incorrect alignment. +. +MessageId=1140 +SymbolicName=ERROR_SET_POWER_STATE_VETOED +Language=ENU +Set-power-state vetoed. +. +MessageId=1141 +SymbolicName=ERROR_SET_POWER_STATE_FAILED +Language=ENU +Set-power-state failed. +. +MessageId=1142 +SymbolicName=ERROR_TOO_MANY_LINKS +Language=ENU +Too many links. +. +MessageId=1150 +SymbolicName=ERROR_OLD_WIN_VERSION +Language=ENU +Newer Windows version needed. +. +MessageId=1151 +SymbolicName=ERROR_APP_WRONG_OS +Language=ENU +Wrong operating system. +. +MessageId=1152 +SymbolicName=ERROR_SINGLE_INSTANCE_APP +Language=ENU +Single-instance application. +. +MessageId=1153 +SymbolicName=ERROR_RMODE_APP +Language=ENU +Real-mode application. +. +MessageId=1154 +SymbolicName=ERROR_INVALID_DLL +Language=ENU +Invalid DLL. +. +MessageId=1155 +SymbolicName=ERROR_NO_ASSOCIATION +Language=ENU +No associated application. +. +MessageId=1156 +SymbolicName=ERROR_DDE_FAIL +Language=ENU +DDE failure. +. +MessageId=1157 +SymbolicName=ERROR_DLL_NOT_FOUND +Language=ENU +DLL not found. +. +MessageId=1158 +SymbolicName=ERROR_NO_MORE_USER_HANDLES +Language=ENU +Out of user handles. +. +MessageId=1159 +SymbolicName=ERROR_MESSAGE_SYNC_ONLY +Language=ENU +Message can only be used in synchronous calls. +. +MessageId=1160 +SymbolicName=ERROR_SOURCE_ELEMENT_EMPTY +Language=ENU +The source element is empty. +. +MessageId=1161 +SymbolicName=ERROR_DESTINATION_ELEMENT_FULL +Language=ENU +The destination element is full. +. +MessageId=1162 +SymbolicName=ERROR_ILLEGAL_ELEMENT_ADDRESS +Language=ENU +The element address is invalid. +. +MessageId=1163 +SymbolicName=ERROR_MAGAZINE_NOT_PRESENT +Language=ENU +The magazine is not present. +. +MessageId=1164 +SymbolicName=ERROR_DEVICE_REINITIALIZATION_NEEDED +Language=ENU +The device needs reinitialization. +. +MessageId=1165 +SymbolicName=ERROR_DEVICE_REQUIRES_CLEANING +Language=ENU +The device requires cleaning. +. +MessageId=1166 +SymbolicName=ERROR_DEVICE_DOOR_OPEN +Language=ENU +The device door is open. +. +MessageId=1167 +SymbolicName=ERROR_DEVICE_NOT_CONNECTED +Language=ENU +The device is not connected. +. +MessageId=1168 +SymbolicName=ERROR_NOT_FOUND +Language=ENU +Element not found. +. +MessageId=1169 +SymbolicName=ERROR_NO_MATCH +Language=ENU +No match found. +. +MessageId=1170 +SymbolicName=ERROR_SET_NOT_FOUND +Language=ENU +Property set not found. +. +MessageId=1171 +SymbolicName=ERROR_POINT_NOT_FOUND +Language=ENU +Point not found. +. +MessageId=1172 +SymbolicName=ERROR_NO_TRACKING_SERVICE +Language=ENU +No running tracking service. +. +MessageId=1173 +SymbolicName=ERROR_NO_VOLUME_ID +Language=ENU +No such volume ID. +. +MessageId=1175 +SymbolicName=ERROR_UNABLE_TO_REMOVE_REPLACED +Language=ENU +Unable to remove the file to be replaced. +. +MessageId=1176 +SymbolicName=ERROR_UNABLE_TO_MOVE_REPLACEMENT +Language=ENU +Unable to move the replacement file into place. +. +MessageId=1177 +SymbolicName=ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 +Language=ENU +Moving the replacement file failed. +. +MessageId=1178 +SymbolicName=ERROR_JOURNAL_DELETE_IN_PROGRESS +Language=ENU +The journal is being deleted. +. +MessageId=1179 +SymbolicName=ERROR_JOURNAL_NOT_ACTIVE +Language=ENU +The journal is not active. +. +MessageId=1180 +SymbolicName=ERROR_POTENTIAL_FILE_FOUND +Language=ENU +Potential matching file found. +. +MessageId=1181 +SymbolicName=ERROR_JOURNAL_ENTRY_DELETED +Language=ENU +The journal entry was deleted. +. +MessageId=1200 +SymbolicName=ERROR_BAD_DEVICE +Language=ENU +Invalid device name. +. +MessageId=1201 +SymbolicName=ERROR_CONNECTION_UNAVAIL +Language=ENU +Connection unavailable. +. +MessageId=1202 +SymbolicName=ERROR_DEVICE_ALREADY_REMEMBERED +Language=ENU +Device already remembered. +. +MessageId=1203 +SymbolicName=ERROR_NO_NET_OR_BAD_PATH +Language=ENU +No network or bad path. +. +MessageId=1204 +SymbolicName=ERROR_BAD_PROVIDER +Language=ENU +Invalid network provider name. +. +MessageId=1205 +SymbolicName=ERROR_CANNOT_OPEN_PROFILE +Language=ENU +Cannot open network connection profile. +. +MessageId=1206 +SymbolicName=ERROR_BAD_PROFILE +Language=ENU +Corrupt network connection profile. +. +MessageId=1207 +SymbolicName=ERROR_NOT_CONTAINER +Language=ENU +Not a container. +. +MessageId=1208 +SymbolicName=ERROR_EXTENDED_ERROR +Language=ENU +Extended error. +. +MessageId=1209 +SymbolicName=ERROR_INVALID_GROUPNAME +Language=ENU +Invalid group name. +. +MessageId=1210 +SymbolicName=ERROR_INVALID_COMPUTERNAME +Language=ENU +Invalid computer name. +. +MessageId=1211 +SymbolicName=ERROR_INVALID_EVENTNAME +Language=ENU +Invalid event name. +. +MessageId=1212 +SymbolicName=ERROR_INVALID_DOMAINNAME +Language=ENU +Invalid domain name. +. +MessageId=1213 +SymbolicName=ERROR_INVALID_SERVICENAME +Language=ENU +Invalid service name. +. +MessageId=1214 +SymbolicName=ERROR_INVALID_NETNAME +Language=ENU +Invalid network name. +. +MessageId=1215 +SymbolicName=ERROR_INVALID_SHARENAME +Language=ENU +Invalid share name. +. +MessageId=1216 +SymbolicName=ERROR_INVALID_PASSWORDNAME +Language=ENU +Invalid password. +. +MessageId=1217 +SymbolicName=ERROR_INVALID_MESSAGENAME +Language=ENU +Invalid message name. +. +MessageId=1218 +SymbolicName=ERROR_INVALID_MESSAGEDEST +Language=ENU +Invalid message destination. +. +MessageId=1219 +SymbolicName=ERROR_SESSION_CREDENTIAL_CONFLICT +Language=ENU +Session credential conflict. +. +MessageId=1220 +SymbolicName=ERROR_REMOTE_SESSION_LIMIT_EXCEEDED +Language=ENU +Remote session limit exceeded. +. +MessageId=1221 +SymbolicName=ERROR_DUP_DOMAINNAME +Language=ENU +Duplicate domain or workgroup name. +. +MessageId=1222 +SymbolicName=ERROR_NO_NETWORK +Language=ENU +No network. +. +MessageId=1223 +SymbolicName=ERROR_CANCELLED +Language=ENU +Operation canceled by user. +. +MessageId=1224 +SymbolicName=ERROR_USER_MAPPED_FILE +Language=ENU +File has a user-mapped section. +. +MessageId=1225 +SymbolicName=ERROR_CONNECTION_REFUSED +Language=ENU +Connection refused. +. +MessageId=1226 +SymbolicName=ERROR_GRACEFUL_DISCONNECT +Language=ENU +Connection gracefully closed. +. +MessageId=1227 +SymbolicName=ERROR_ADDRESS_ALREADY_ASSOCIATED +Language=ENU +Address already associated with transport endpoint. +. +MessageId=1228 +SymbolicName=ERROR_ADDRESS_NOT_ASSOCIATED +Language=ENU +Address not associated with transport endpoint. +. +MessageId=1229 +SymbolicName=ERROR_CONNECTION_INVALID +Language=ENU +Connection invalid. +. +MessageId=1230 +SymbolicName=ERROR_CONNECTION_ACTIVE +Language=ENU +Connection is active. +. +MessageId=1231 +SymbolicName=ERROR_NETWORK_UNREACHABLE +Language=ENU +Network unreachable. +. +MessageId=1232 +SymbolicName=ERROR_HOST_UNREACHABLE +Language=ENU +Host unreachable. +. +MessageId=1233 +SymbolicName=ERROR_PROTOCOL_UNREACHABLE +Language=ENU +Protocol unreachable. +. +MessageId=1234 +SymbolicName=ERROR_PORT_UNREACHABLE +Language=ENU +Port unreachable. +. +MessageId=1235 +SymbolicName=ERROR_REQUEST_ABORTED +Language=ENU +Request aborted. +. +MessageId=1236 +SymbolicName=ERROR_CONNECTION_ABORTED +Language=ENU +Connection aborted. +. +MessageId=1237 +SymbolicName=ERROR_RETRY +Language=ENU +Please retry operation. +. +MessageId=1238 +SymbolicName=ERROR_CONNECTION_COUNT_LIMIT +Language=ENU +Connection count limit reached. +. +MessageId=1239 +SymbolicName=ERROR_LOGIN_TIME_RESTRICTION +Language=ENU +Login time restriction. +. +MessageId=1240 +SymbolicName=ERROR_LOGIN_WKSTA_RESTRICTION +Language=ENU +Login workstation restriction. +. +MessageId=1241 +SymbolicName=ERROR_INCORRECT_ADDRESS +Language=ENU +Incorrect network address. +. +MessageId=1242 +SymbolicName=ERROR_ALREADY_REGISTERED +Language=ENU +Service already registered. +. +MessageId=1243 +SymbolicName=ERROR_SERVICE_NOT_FOUND +Language=ENU +Service not found. +. +MessageId=1244 +SymbolicName=ERROR_NOT_AUTHENTICATED +Language=ENU +User not authenticated. +. +MessageId=1245 +SymbolicName=ERROR_NOT_LOGGED_ON +Language=ENU +User not logged on. +. +MessageId=1246 +SymbolicName=ERROR_CONTINUE +Language=ENU +Continue work in progress. +. +MessageId=1247 +SymbolicName=ERROR_ALREADY_INITIALIZED +Language=ENU +Already initialized. +. +MessageId=1248 +SymbolicName=ERROR_NO_MORE_DEVICES +Language=ENU +No more local devices. +. +MessageId=1249 +SymbolicName=ERROR_NO_SUCH_SITE +Language=ENU +The site does not exist. +. +MessageId=1250 +SymbolicName=ERROR_DOMAIN_CONTROLLER_EXISTS +Language=ENU +The domain controller already exists. +. +MessageId=1251 +SymbolicName=ERROR_ONLY_IF_CONNECTED +Language=ENU +Supported only when connected. +. +MessageId=1252 +SymbolicName=ERROR_OVERRIDE_NOCHANGES +Language=ENU +Perform operation even when nothing changed. +. +MessageId=1253 +SymbolicName=ERROR_BAD_USER_PROFILE +Language=ENU +The user profile is invalid. +. +MessageId=1254 +SymbolicName=ERROR_NOT_SUPPORTED_ON_SBS +Language=ENU +Not supported on Small Business Server. +. +MessageId=1300 +SymbolicName=ERROR_NOT_ALL_ASSIGNED +Language=ENU +Not all privileges assigned. +. +MessageId=1301 +SymbolicName=ERROR_SOME_NOT_MAPPED +Language=ENU +Some security IDs not mapped. +. +MessageId=1302 +SymbolicName=ERROR_NO_QUOTAS_FOR_ACCOUNT +Language=ENU +No quotas for account. +. +MessageId=1303 +SymbolicName=ERROR_LOCAL_USER_SESSION_KEY +Language=ENU +Local user session key. +. +MessageId=1304 +SymbolicName=ERROR_NULL_LM_PASSWORD +Language=ENU +Password too complex for LM. +. +MessageId=1305 +SymbolicName=ERROR_UNKNOWN_REVISION +Language=ENU +Unknown revision. +. +MessageId=1306 +SymbolicName=ERROR_REVISION_MISMATCH +Language=ENU +Incompatible revision levels. +. +MessageId=1307 +SymbolicName=ERROR_INVALID_OWNER +Language=ENU +Invalid owner. +. +MessageId=1308 +SymbolicName=ERROR_INVALID_PRIMARY_GROUP +Language=ENU +Invalid primary group. +. +MessageId=1309 +SymbolicName=ERROR_NO_IMPERSONATION_TOKEN +Language=ENU +No impersonation token. +. +MessageId=1310 +SymbolicName=ERROR_CANT_DISABLE_MANDATORY +Language=ENU +Can't disable mandatory group. +. +MessageId=1311 +SymbolicName=ERROR_NO_LOGON_SERVERS +Language=ENU +No logon servers available. +. +MessageId=1312 +SymbolicName=ERROR_NO_SUCH_LOGON_SESSION +Language=ENU +No such logon session. +. +MessageId=1313 +SymbolicName=ERROR_NO_SUCH_PRIVILEGE +Language=ENU +No such privilege. +. +MessageId=1314 +SymbolicName=ERROR_PRIVILEGE_NOT_HELD +Language=ENU +Privilege not held. +. +MessageId=1315 +SymbolicName=ERROR_INVALID_ACCOUNT_NAME +Language=ENU +Invalid account name. +. +MessageId=1316 +SymbolicName=ERROR_USER_EXISTS +Language=ENU +User already exists. +. +MessageId=1317 +SymbolicName=ERROR_NO_SUCH_USER +Language=ENU +No such user. +. +MessageId=1318 +SymbolicName=ERROR_GROUP_EXISTS +Language=ENU +Group already exists. +. +MessageId=1319 +SymbolicName=ERROR_NO_SUCH_GROUP +Language=ENU +No such group. +. +MessageId=1320 +SymbolicName=ERROR_MEMBER_IN_GROUP +Language=ENU +User already in group. +. +MessageId=1321 +SymbolicName=ERROR_MEMBER_NOT_IN_GROUP +Language=ENU +User not in group. +. +MessageId=1322 +SymbolicName=ERROR_LAST_ADMIN +Language=ENU +Can't delete last admin user. +. +MessageId=1323 +SymbolicName=ERROR_WRONG_PASSWORD +Language=ENU +Wrong password. +. +MessageId=1324 +SymbolicName=ERROR_ILL_FORMED_PASSWORD +Language=ENU +Ill-formed password. +. +MessageId=1325 +SymbolicName=ERROR_PASSWORD_RESTRICTION +Language=ENU +Password restriction. +. +MessageId=1326 +SymbolicName=ERROR_LOGON_FAILURE +Language=ENU +Logon failure. +. +MessageId=1327 +SymbolicName=ERROR_ACCOUNT_RESTRICTION +Language=ENU +Account restriction. +. +MessageId=1328 +SymbolicName=ERROR_INVALID_LOGON_HOURS +Language=ENU +Invalid logon hours. +. +MessageId=1329 +SymbolicName=ERROR_INVALID_WORKSTATION +Language=ENU +Invalid workstation. +. +MessageId=1330 +SymbolicName=ERROR_PASSWORD_EXPIRED +Language=ENU +Password expired. +. +MessageId=1331 +SymbolicName=ERROR_ACCOUNT_DISABLED +Language=ENU +Account disabled. +. +MessageId=1332 +SymbolicName=ERROR_NONE_MAPPED +Language=ENU +No security ID mapped. +. +MessageId=1333 +SymbolicName=ERROR_TOO_MANY_LUIDS_REQUESTED +Language=ENU +Too many LUIDs requested. +. +MessageId=1334 +SymbolicName=ERROR_LUIDS_EXHAUSTED +Language=ENU +LUIDs exhausted. +. +MessageId=1335 +SymbolicName=ERROR_INVALID_SUB_AUTHORITY +Language=ENU +Invalid sub authority. +. +MessageId=1336 +SymbolicName=ERROR_INVALID_ACL +Language=ENU +Invalid ACL. +. +MessageId=1337 +SymbolicName=ERROR_INVALID_SID +Language=ENU +Invalid SID. +. +MessageId=1338 +SymbolicName=ERROR_INVALID_SECURITY_DESCR +Language=ENU +Invalid security descriptor. +. +MessageId=1340 +SymbolicName=ERROR_BAD_INHERITANCE_ACL +Language=ENU +Bad inherited ACL. +. +MessageId=1341 +SymbolicName=ERROR_SERVER_DISABLED +Language=ENU +Server disabled. +. +MessageId=1342 +SymbolicName=ERROR_SERVER_NOT_DISABLED +Language=ENU +Server not disabled. +. +MessageId=1343 +SymbolicName=ERROR_INVALID_ID_AUTHORITY +Language=ENU +Invalid ID authority. +. +MessageId=1344 +SymbolicName=ERROR_ALLOTTED_SPACE_EXCEEDED +Language=ENU +Allotted space exceeded. +. +MessageId=1345 +SymbolicName=ERROR_INVALID_GROUP_ATTRIBUTES +Language=ENU +Invalid group attributes. +. +MessageId=1346 +SymbolicName=ERROR_BAD_IMPERSONATION_LEVEL +Language=ENU +Bad impersonation level. +. +MessageId=1347 +SymbolicName=ERROR_CANT_OPEN_ANONYMOUS +Language=ENU +Can't open anonymous security token. +. +MessageId=1348 +SymbolicName=ERROR_BAD_VALIDATION_CLASS +Language=ENU +Bad validation class. +. +MessageId=1349 +SymbolicName=ERROR_BAD_TOKEN_TYPE +Language=ENU +Bad token type. +. +MessageId=1350 +SymbolicName=ERROR_NO_SECURITY_ON_OBJECT +Language=ENU +No security on object. +. +MessageId=1351 +SymbolicName=ERROR_CANT_ACCESS_DOMAIN_INFO +Language=ENU +Can't access domain information. +. +MessageId=1352 +SymbolicName=ERROR_INVALID_SERVER_STATE +Language=ENU +Invalid server state. +. +MessageId=1353 +SymbolicName=ERROR_INVALID_DOMAIN_STATE +Language=ENU +Invalid domain state. +. +MessageId=1354 +SymbolicName=ERROR_INVALID_DOMAIN_ROLE +Language=ENU +Invalid domain role. +. +MessageId=1355 +SymbolicName=ERROR_NO_SUCH_DOMAIN +Language=ENU +No such domain. +. +MessageId=1356 +SymbolicName=ERROR_DOMAIN_EXISTS +Language=ENU +Domain already exists. +. +MessageId=1357 +SymbolicName=ERROR_DOMAIN_LIMIT_EXCEEDED +Language=ENU +Domain limit exceeded. +. +MessageId=1358 +SymbolicName=ERROR_INTERNAL_DB_CORRUPTION +Language=ENU +Internal database corruption. +. +MessageId=1359 +SymbolicName=ERROR_INTERNAL_ERROR +Language=ENU +Internal error. +. +MessageId=1360 +SymbolicName=ERROR_GENERIC_NOT_MAPPED +Language=ENU +Generic access types not mapped. +. +MessageId=1361 +SymbolicName=ERROR_BAD_DESCRIPTOR_FORMAT +Language=ENU +Bad descriptor format. +. +MessageId=1362 +SymbolicName=ERROR_NOT_LOGON_PROCESS +Language=ENU +Not a logon process. +. +MessageId=1363 +SymbolicName=ERROR_LOGON_SESSION_EXISTS +Language=ENU +Logon session ID exists. +. +MessageId=1364 +SymbolicName=ERROR_NO_SUCH_PACKAGE +Language=ENU +Unknown authentication package. +. +MessageId=1365 +SymbolicName=ERROR_BAD_LOGON_SESSION_STATE +Language=ENU +Bad logon session state. +. +MessageId=1366 +SymbolicName=ERROR_LOGON_SESSION_COLLISION +Language=ENU +Logon session ID collision. +. +MessageId=1367 +SymbolicName=ERROR_INVALID_LOGON_TYPE +Language=ENU +Invalid logon type. +. +MessageId=1368 +SymbolicName=ERROR_CANNOT_IMPERSONATE +Language=ENU +Cannot impersonate. +. +MessageId=1369 +SymbolicName=ERROR_RXACT_INVALID_STATE +Language=ENU +Invalid transaction state. +. +MessageId=1370 +SymbolicName=ERROR_RXACT_COMMIT_FAILURE +Language=ENU +Security DB commit failure. +. +MessageId=1371 +SymbolicName=ERROR_SPECIAL_ACCOUNT +Language=ENU +Account is built-in. +. +MessageId=1372 +SymbolicName=ERROR_SPECIAL_GROUP +Language=ENU +Group is built-in. +. +MessageId=1373 +SymbolicName=ERROR_SPECIAL_USER +Language=ENU +User is built-in. +. +MessageId=1374 +SymbolicName=ERROR_MEMBERS_PRIMARY_GROUP +Language=ENU +Group is primary for user. +. +MessageId=1375 +SymbolicName=ERROR_TOKEN_ALREADY_IN_USE +Language=ENU +Token already in use. +. +MessageId=1376 +SymbolicName=ERROR_NO_SUCH_ALIAS +Language=ENU +No such local group. +. +MessageId=1377 +SymbolicName=ERROR_MEMBER_NOT_IN_ALIAS +Language=ENU +User not in local group. +. +MessageId=1378 +SymbolicName=ERROR_MEMBER_IN_ALIAS +Language=ENU +User already in local group. +. +MessageId=1379 +SymbolicName=ERROR_ALIAS_EXISTS +Language=ENU +Local group already exists. +. +MessageId=1380 +SymbolicName=ERROR_LOGON_NOT_GRANTED +Language=ENU +Logon type not granted. +. +MessageId=1381 +SymbolicName=ERROR_TOO_MANY_SECRETS +Language=ENU +Too many secrets. +. +MessageId=1382 +SymbolicName=ERROR_SECRET_TOO_LONG +Language=ENU +Secret too long. +. +MessageId=1383 +SymbolicName=ERROR_INTERNAL_DB_ERROR +Language=ENU +Internal security DB error. +. +MessageId=1384 +SymbolicName=ERROR_TOO_MANY_CONTEXT_IDS +Language=ENU +Too many context IDs. +. +MessageId=1385 +SymbolicName=ERROR_LOGON_TYPE_NOT_GRANTED +Language=ENU +Logon type not granted. +. +MessageId=1386 +SymbolicName=ERROR_NT_CROSS_ENCRYPTION_REQUIRED +Language=ENU +Cross-encrypted NT password required. +. +MessageId=1387 +SymbolicName=ERROR_NO_SUCH_MEMBER +Language=ENU +No such member. +. +MessageId=1388 +SymbolicName=ERROR_INVALID_MEMBER +Language=ENU +Invalid member. +. +MessageId=1389 +SymbolicName=ERROR_TOO_MANY_SIDS +Language=ENU +Too many SIDs. +. +MessageId=1390 +SymbolicName=ERROR_LM_CROSS_ENCRYPTION_REQUIRED +Language=ENU +Cross-encrypted LM password required. +. +MessageId=1391 +SymbolicName=ERROR_NO_INHERITANCE +Language=ENU +No inheritable components. +. +MessageId=1392 +SymbolicName=ERROR_FILE_CORRUPT +Language=ENU +File or directory corrupt. +. +MessageId=1393 +SymbolicName=ERROR_DISK_CORRUPT +Language=ENU +Disk is corrupt. +. +MessageId=1394 +SymbolicName=ERROR_NO_USER_SESSION_KEY +Language=ENU +No user session key. +. +MessageId=1395 +SymbolicName=ERROR_LICENSE_QUOTA_EXCEEDED +Language=ENU +License quota exceeded. +. +MessageId=1396 +SymbolicName=ERROR_WRONG_TARGET_NAME +Language=ENU +Wrong target name. +. +MessageId=1397 +SymbolicName=ERROR_MUTUAL_AUTH_FAILED +Language=ENU +Mutual authentication failed. +. +MessageId=1398 +SymbolicName=ERROR_TIME_SKEW +Language=ENU +Time skew between client and server. +. +MessageId=1400 +SymbolicName=ERROR_INVALID_WINDOW_HANDLE +Language=ENU +Invalid window handle. +. +MessageId=1401 +SymbolicName=ERROR_INVALID_MENU_HANDLE +Language=ENU +Invalid menu handle. +. +MessageId=1402 +SymbolicName=ERROR_INVALID_CURSOR_HANDLE +Language=ENU +Invalid cursor handle. +. +MessageId=1403 +SymbolicName=ERROR_INVALID_ACCEL_HANDLE +Language=ENU +Invalid accelerator table handle. +. +MessageId=1404 +SymbolicName=ERROR_INVALID_HOOK_HANDLE +Language=ENU +Invalid hook handle. +. +MessageId=1405 +SymbolicName=ERROR_INVALID_DWP_HANDLE +Language=ENU +Invalid DWP handle. +. +MessageId=1406 +SymbolicName=ERROR_TLW_WITH_WSCHILD +Language=ENU +Can't create top-level child window. +. +MessageId=1407 +SymbolicName=ERROR_CANNOT_FIND_WND_CLASS +Language=ENU +Can't find window class. +. +MessageId=1408 +SymbolicName=ERROR_WINDOW_OF_OTHER_THREAD +Language=ENU +Window owned by another thread. +. +MessageId=1409 +SymbolicName=ERROR_HOTKEY_ALREADY_REGISTERED +Language=ENU +Hotkey already registered. +. +MessageId=1410 +SymbolicName=ERROR_CLASS_ALREADY_EXISTS +Language=ENU +Class already exists. +. +MessageId=1411 +SymbolicName=ERROR_CLASS_DOES_NOT_EXIST +Language=ENU +Class does not exist. +. +MessageId=1412 +SymbolicName=ERROR_CLASS_HAS_WINDOWS +Language=ENU +Class has open windows. +. +MessageId=1413 +SymbolicName=ERROR_INVALID_INDEX +Language=ENU +Invalid index. +. +MessageId=1414 +SymbolicName=ERROR_INVALID_ICON_HANDLE +Language=ENU +Invalid icon handle. +. +MessageId=1415 +SymbolicName=ERROR_PRIVATE_DIALOG_INDEX +Language=ENU +Private dialog index. +. +MessageId=1416 +SymbolicName=ERROR_LISTBOX_ID_NOT_FOUND +Language=ENU +List box ID not found. +. +MessageId=1417 +SymbolicName=ERROR_NO_WILDCARD_CHARACTERS +Language=ENU +No wildcard characters. +. +MessageId=1418 +SymbolicName=ERROR_CLIPBOARD_NOT_OPEN +Language=ENU +Clipboard not open. +. +MessageId=1419 +SymbolicName=ERROR_HOTKEY_NOT_REGISTERED +Language=ENU +Hotkey not registered. +. +MessageId=1420 +SymbolicName=ERROR_WINDOW_NOT_DIALOG +Language=ENU +Not a dialog window. +. +MessageId=1421 +SymbolicName=ERROR_CONTROL_ID_NOT_FOUND +Language=ENU +Control ID not found. +. +MessageId=1422 +SymbolicName=ERROR_INVALID_COMBOBOX_MESSAGE +Language=ENU +Invalid combo box message. +. +MessageId=1423 +SymbolicName=ERROR_WINDOW_NOT_COMBOBOX +Language=ENU +Not a combo box window. +. +MessageId=1424 +SymbolicName=ERROR_INVALID_EDIT_HEIGHT +Language=ENU +Invalid edit height. +. +MessageId=1425 +SymbolicName=ERROR_DC_NOT_FOUND +Language=ENU +DC not found. +. +MessageId=1426 +SymbolicName=ERROR_INVALID_HOOK_FILTER +Language=ENU +Invalid hook filter. +. +MessageId=1427 +SymbolicName=ERROR_INVALID_FILTER_PROC +Language=ENU +Invalid filter procedure. +. +MessageId=1428 +SymbolicName=ERROR_HOOK_NEEDS_HMOD +Language=ENU +Hook procedure needs module handle. +. +MessageId=1429 +SymbolicName=ERROR_GLOBAL_ONLY_HOOK +Language=ENU +Global-only hook procedure. +. +MessageId=1430 +SymbolicName=ERROR_JOURNAL_HOOK_SET +Language=ENU +Journal hook already set. +. +MessageId=1431 +SymbolicName=ERROR_HOOK_NOT_INSTALLED +Language=ENU +Hook procedure not installed. +. +MessageId=1432 +SymbolicName=ERROR_INVALID_LB_MESSAGE +Language=ENU +Invalid list box message. +. +MessageId=1433 +SymbolicName=ERROR_SETCOUNT_ON_BAD_LB +Language=ENU +Invalid LB_SETCOUNT sent. +. +MessageId=1434 +SymbolicName=ERROR_LB_WITHOUT_TABSTOPS +Language=ENU +No tab stops on this list box. +. +MessageId=1435 +SymbolicName=ERROR_DESTROY_OBJECT_OF_OTHER_THREAD +Language=ENU +Can't destroy object owned by another thread. +. +MessageId=1436 +SymbolicName=ERROR_CHILD_WINDOW_MENU +Language=ENU +Child window menus not allowed. +. +MessageId=1437 +SymbolicName=ERROR_NO_SYSTEM_MENU +Language=ENU +Window has no system menu. +. +MessageId=1438 +SymbolicName=ERROR_INVALID_MSGBOX_STYLE +Language=ENU +Invalid message box style. +. +MessageId=1439 +SymbolicName=ERROR_INVALID_SPI_VALUE +Language=ENU +Invalid SPI parameter. +. +MessageId=1440 +SymbolicName=ERROR_SCREEN_ALREADY_LOCKED +Language=ENU +Screen already locked. +. +MessageId=1441 +SymbolicName=ERROR_HWNDS_HAVE_DIFF_PARENT +Language=ENU +Window handles have different parents. +. +MessageId=1442 +SymbolicName=ERROR_NOT_CHILD_WINDOW +Language=ENU +Not a child window. +. +MessageId=1443 +SymbolicName=ERROR_INVALID_GW_COMMAND +Language=ENU +Invalid GW command. +. +MessageId=1444 +SymbolicName=ERROR_INVALID_THREAD_ID +Language=ENU +Invalid thread ID. +. +MessageId=1445 +SymbolicName=ERROR_NON_MDICHILD_WINDOW +Language=ENU +Not an MDI child window. +. +MessageId=1446 +SymbolicName=ERROR_POPUP_ALREADY_ACTIVE +Language=ENU +Popup menu already active. +. +MessageId=1447 +SymbolicName=ERROR_NO_SCROLLBARS +Language=ENU +No scrollbars. +. +MessageId=1448 +SymbolicName=ERROR_INVALID_SCROLLBAR_RANGE +Language=ENU +Invalid scrollbar range. +. +MessageId=1449 +SymbolicName=ERROR_INVALID_SHOWWIN_COMMAND +Language=ENU +Invalid ShowWin command. +. +MessageId=1450 +SymbolicName=ERROR_NO_SYSTEM_RESOURCES +Language=ENU +No system resources. +. +MessageId=1451 +SymbolicName=ERROR_NONPAGED_SYSTEM_RESOURCES +Language=ENU +No non-paged system resources. +. +MessageId=1452 +SymbolicName=ERROR_PAGED_SYSTEM_RESOURCES +Language=ENU +No paged system resources. +. +MessageId=1453 +SymbolicName=ERROR_WORKING_SET_QUOTA +Language=ENU +No working set quota. +. +MessageId=1454 +SymbolicName=ERROR_PAGEFILE_QUOTA +Language=ENU +No page file quota. +. +MessageId=1455 +SymbolicName=ERROR_COMMITMENT_LIMIT +Language=ENU +Exceeded commitment limit. +. +MessageId=1456 +SymbolicName=ERROR_MENU_ITEM_NOT_FOUND +Language=ENU +Menu item not found. +. +MessageId=1457 +SymbolicName=ERROR_INVALID_KEYBOARD_HANDLE +Language=ENU +Invalid keyboard handle. +. +MessageId=1458 +SymbolicName=ERROR_HOOK_TYPE_NOT_ALLOWED +Language=ENU +Hook type not allowed. +. +MessageId=1459 +SymbolicName=ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION +Language=ENU +Interactive window station required. +. +MessageId=1460 +SymbolicName=ERROR_TIMEOUT +Language=ENU +Timeout. +. +MessageId=1461 +SymbolicName=ERROR_INVALID_MONITOR_HANDLE +Language=ENU +Invalid monitor handle. +. +MessageId=1500 +SymbolicName=ERROR_EVENTLOG_FILE_CORRUPT +Language=ENU +Event log file corrupt. +. +MessageId=1501 +SymbolicName=ERROR_EVENTLOG_CANT_START +Language=ENU +Event log can't start. +. +MessageId=1502 +SymbolicName=ERROR_LOG_FILE_FULL +Language=ENU +Event log file full. +. +MessageId=1503 +SymbolicName=ERROR_EVENTLOG_FILE_CHANGED +Language=ENU +Event log file changed. +. +MessageId=1601 +SymbolicName=ERROR_INSTALL_SERVICE_FAILURE +Language=ENU +Installer service failed. +. +MessageId=1602 +SymbolicName=ERROR_INSTALL_USEREXIT +Language=ENU +Installation aborted by user. +. +MessageId=1603 +SymbolicName=ERROR_INSTALL_FAILURE +Language=ENU +Installation failure. +. +MessageId=1604 +SymbolicName=ERROR_INSTALL_SUSPEND +Language=ENU +Installation suspended. +. +MessageId=1605 +SymbolicName=ERROR_UNKNOWN_PRODUCT +Language=ENU +Unknown product. +. +MessageId=1606 +SymbolicName=ERROR_UNKNOWN_FEATURE +Language=ENU +Unknown feature. +. +MessageId=1607 +SymbolicName=ERROR_UNKNOWN_COMPONENT +Language=ENU +Unknown component. +. +MessageId=1608 +SymbolicName=ERROR_UNKNOWN_PROPERTY +Language=ENU +Unknown property. +. +MessageId=1609 +SymbolicName=ERROR_INVALID_HANDLE_STATE +Language=ENU +Invalid handle state. +. +MessageId=1610 +SymbolicName=ERROR_BAD_CONFIGURATION +Language=ENU +Bad configuration. +. +MessageId=1611 +SymbolicName=ERROR_INDEX_ABSENT +Language=ENU +Index is missing. +. +MessageId=1612 +SymbolicName=ERROR_INSTALL_SOURCE_ABSENT +Language=ENU +Installation source is missing. +. +MessageId=1613 +SymbolicName=ERROR_INSTALL_PACKAGE_VERSION +Language=ENU +Wrong installation package version. +. +MessageId=1614 +SymbolicName=ERROR_PRODUCT_UNINSTALLED +Language=ENU +Product uninstalled. +. +MessageId=1615 +SymbolicName=ERROR_BAD_QUERY_SYNTAX +Language=ENU +Invalid query syntax. +. +MessageId=1616 +SymbolicName=ERROR_INVALID_FIELD +Language=ENU +Invalid field. +. +MessageId=1617 +SymbolicName=ERROR_DEVICE_REMOVED +Language=ENU +Device removed. +. +MessageId=1618 +SymbolicName=ERROR_INSTALL_ALREADY_RUNNING +Language=ENU +Installation already running. +. +MessageId=1619 +SymbolicName=ERROR_INSTALL_PACKAGE_OPEN_FAILED +Language=ENU +Installation package failed to open. +. +MessageId=1620 +SymbolicName=ERROR_INSTALL_PACKAGE_INVALID +Language=ENU +Installation package is invalid. +. +MessageId=1621 +SymbolicName=ERROR_INSTALL_UI_FAILURE +Language=ENU +Installer user interface failed. +. +MessageId=1622 +SymbolicName=ERROR_INSTALL_LOG_FAILURE +Language=ENU +Failed to open installation log file. +. +MessageId=1623 +SymbolicName=ERROR_INSTALL_LANGUAGE_UNSUPPORTED +Language=ENU +Installation language not supported. +. +MessageId=1624 +SymbolicName=ERROR_INSTALL_TRANSFORM_FAILURE +Language=ENU +Installation transform failed to apply. +. +MessageId=1625 +SymbolicName=ERROR_INSTALL_PACKAGE_REJECTED +Language=ENU +Installation package rejected. +. +MessageId=1626 +SymbolicName=ERROR_FUNCTION_NOT_CALLED +Language=ENU +Function could not be called. +. +MessageId=1627 +SymbolicName=ERROR_FUNCTION_FAILED +Language=ENU +Function failed. +. +MessageId=1628 +SymbolicName=ERROR_INVALID_TABLE +Language=ENU +Invalid table. +. +MessageId=1629 +SymbolicName=ERROR_DATATYPE_MISMATCH +Language=ENU +Data type mismatch. +. +MessageId=1630 +SymbolicName=ERROR_UNSUPPORTED_TYPE +Language=ENU +Unsupported type. +. +MessageId=1631 +SymbolicName=ERROR_CREATE_FAILED +Language=ENU +Creation failed. +. +MessageId=1632 +SymbolicName=ERROR_INSTALL_TEMP_UNWRITABLE +Language=ENU +Temporary directory not writable. +. +MessageId=1633 +SymbolicName=ERROR_INSTALL_PLATFORM_UNSUPPORTED +Language=ENU +Installation platform not supported. +. +MessageId=1634 +SymbolicName=ERROR_INSTALL_NOTUSED +Language=ENU +Installer not used. +. +MessageId=1635 +SymbolicName=ERROR_PATCH_PACKAGE_OPEN_FAILED +Language=ENU +Failed to open the patch package. +. +MessageId=1636 +SymbolicName=ERROR_PATCH_PACKAGE_INVALID +Language=ENU +Invalid patch package. +. +MessageId=1637 +SymbolicName=ERROR_PATCH_PACKAGE_UNSUPPORTED +Language=ENU +Unsupported patch package. +. +MessageId=1638 +SymbolicName=ERROR_PRODUCT_VERSION +Language=ENU +Another version is installed. +. +MessageId=1639 +SymbolicName=ERROR_INVALID_COMMAND_LINE +Language=ENU +Invalid command line. +. +MessageId=1640 +SymbolicName=ERROR_INSTALL_REMOTE_DISALLOWED +Language=ENU +Remote installation not allowed. +. +MessageId=1641 +SymbolicName=ERROR_SUCCESS_REBOOT_INITIATED +Language=ENU +Reboot initiated after successful install. +. +MessageId=1700 +SymbolicName=RPC_S_INVALID_STRING_BINDING +Language=ENU +Invalid string binding. +. +MessageId=1701 +SymbolicName=RPC_S_WRONG_KIND_OF_BINDING +Language=ENU +Wrong kind of binding. +. +MessageId=1702 +SymbolicName=RPC_S_INVALID_BINDING +Language=ENU +Invalid binding. +. +MessageId=1703 +SymbolicName=RPC_S_PROTSEQ_NOT_SUPPORTED +Language=ENU +RPC protocol sequence not supported. +. +MessageId=1704 +SymbolicName=RPC_S_INVALID_RPC_PROTSEQ +Language=ENU +Invalid RPC protocol sequence. +. +MessageId=1705 +SymbolicName=RPC_S_INVALID_STRING_UUID +Language=ENU +Invalid string UUID. +. +MessageId=1706 +SymbolicName=RPC_S_INVALID_ENDPOINT_FORMAT +Language=ENU +Invalid endpoint format. +. +MessageId=1707 +SymbolicName=RPC_S_INVALID_NET_ADDR +Language=ENU +Invalid network address. +. +MessageId=1708 +SymbolicName=RPC_S_NO_ENDPOINT_FOUND +Language=ENU +No endpoint found. +. +MessageId=1709 +SymbolicName=RPC_S_INVALID_TIMEOUT +Language=ENU +Invalid timeout value. +. +MessageId=1710 +SymbolicName=RPC_S_OBJECT_NOT_FOUND +Language=ENU +Object UUID not found. +. +MessageId=1711 +SymbolicName=RPC_S_ALREADY_REGISTERED +Language=ENU +UUID already registered. +. +MessageId=1712 +SymbolicName=RPC_S_TYPE_ALREADY_REGISTERED +Language=ENU +UUID type already registered. +. +MessageId=1713 +SymbolicName=RPC_S_ALREADY_LISTENING +Language=ENU +Server already listening. +. +MessageId=1714 +SymbolicName=RPC_S_NO_PROTSEQS_REGISTERED +Language=ENU +No protocol sequences registered. +. +MessageId=1715 +SymbolicName=RPC_S_NOT_LISTENING +Language=ENU +RPC server not listening. +. +MessageId=1716 +SymbolicName=RPC_S_UNKNOWN_MGR_TYPE +Language=ENU +Unknown manager type. +. +MessageId=1717 +SymbolicName=RPC_S_UNKNOWN_IF +Language=ENU +Unknown interface. +. +MessageId=1718 +SymbolicName=RPC_S_NO_BINDINGS +Language=ENU +No bindings. +. +MessageId=1719 +SymbolicName=RPC_S_NO_PROTSEQS +Language=ENU +No protocol sequences. +. +MessageId=1720 +SymbolicName=RPC_S_CANT_CREATE_ENDPOINT +Language=ENU +Can't create endpoint. +. +MessageId=1721 +SymbolicName=RPC_S_OUT_OF_RESOURCES +Language=ENU +Out of resources. +. +MessageId=1722 +SymbolicName=RPC_S_SERVER_UNAVAILABLE +Language=ENU +RPC server unavailable. +. +MessageId=1723 +SymbolicName=RPC_S_SERVER_TOO_BUSY +Language=ENU +RPC server too busy. +. +MessageId=1724 +SymbolicName=RPC_S_INVALID_NETWORK_OPTIONS +Language=ENU +Invalid network options. +. +MessageId=1725 +SymbolicName=RPC_S_NO_CALL_ACTIVE +Language=ENU +No RPC call active. +. +MessageId=1726 +SymbolicName=RPC_S_CALL_FAILED +Language=ENU +RPC call failed. +. +MessageId=1727 +SymbolicName=RPC_S_CALL_FAILED_DNE +Language=ENU +RPC call failed and didn't execute. +. +MessageId=1728 +SymbolicName=RPC_S_PROTOCOL_ERROR +Language=ENU +RPC protocol error. +. +MessageId=1730 +SymbolicName=RPC_S_UNSUPPORTED_TRANS_SYN +Language=ENU +Unsupported transfer syntax. +. +MessageId=1732 +SymbolicName=RPC_S_UNSUPPORTED_TYPE +Language=ENU +Unsupported type. +. +MessageId=1733 +SymbolicName=RPC_S_INVALID_TAG +Language=ENU +Invalid tag. +. +MessageId=1734 +SymbolicName=RPC_S_INVALID_BOUND +Language=ENU +Invalid array bounds. +. +MessageId=1735 +SymbolicName=RPC_S_NO_ENTRY_NAME +Language=ENU +No entry name. +. +MessageId=1736 +SymbolicName=RPC_S_INVALID_NAME_SYNTAX +Language=ENU +Invalid name syntax. +. +MessageId=1737 +SymbolicName=RPC_S_UNSUPPORTED_NAME_SYNTAX +Language=ENU +Unsupported name syntax. +. +MessageId=1739 +SymbolicName=RPC_S_UUID_NO_ADDRESS +Language=ENU +No network address. +. +MessageId=1740 +SymbolicName=RPC_S_DUPLICATE_ENDPOINT +Language=ENU +Duplicate endpoint. +. +MessageId=1741 +SymbolicName=RPC_S_UNKNOWN_AUTHN_TYPE +Language=ENU +Unknown authentication type. +. +MessageId=1742 +SymbolicName=RPC_S_MAX_CALLS_TOO_SMALL +Language=ENU +Maximum calls too low. +. +MessageId=1743 +SymbolicName=RPC_S_STRING_TOO_LONG +Language=ENU +String too long. +. +MessageId=1744 +SymbolicName=RPC_S_PROTSEQ_NOT_FOUND +Language=ENU +Protocol sequence not found. +. +MessageId=1745 +SymbolicName=RPC_S_PROCNUM_OUT_OF_RANGE +Language=ENU +Procedure number out of range. +. +MessageId=1746 +SymbolicName=RPC_S_BINDING_HAS_NO_AUTH +Language=ENU +Binding has no authentication data. +. +MessageId=1747 +SymbolicName=RPC_S_UNKNOWN_AUTHN_SERVICE +Language=ENU +Unknown authentication service. +. +MessageId=1748 +SymbolicName=RPC_S_UNKNOWN_AUTHN_LEVEL +Language=ENU +Unknown authentication level. +. +MessageId=1749 +SymbolicName=RPC_S_INVALID_AUTH_IDENTITY +Language=ENU +Invalid authentication identity. +. +MessageId=1750 +SymbolicName=RPC_S_UNKNOWN_AUTHZ_SERVICE +Language=ENU +Unknown authorization service. +. +MessageId=1751 +SymbolicName=EPT_S_INVALID_ENTRY +Language=ENU +Invalid entry. +. +MessageId=1752 +SymbolicName=EPT_S_CANT_PERFORM_OP +Language=ENU +Can't perform operation. +. +MessageId=1753 +SymbolicName=EPT_S_NOT_REGISTERED +Language=ENU +Endpoints not registered. +. +MessageId=1754 +SymbolicName=RPC_S_NOTHING_TO_EXPORT +Language=ENU +Nothing to export. +. +MessageId=1755 +SymbolicName=RPC_S_INCOMPLETE_NAME +Language=ENU +Incomplete name. +. +MessageId=1756 +SymbolicName=RPC_S_INVALID_VERS_OPTION +Language=ENU +Invalid version option. +. +MessageId=1757 +SymbolicName=RPC_S_NO_MORE_MEMBERS +Language=ENU +No more members. +. +MessageId=1758 +SymbolicName=RPC_S_NOT_ALL_OBJS_UNEXPORTED +Language=ENU +Not all objects unexported. +. +MessageId=1759 +SymbolicName=RPC_S_INTERFACE_NOT_FOUND +Language=ENU +Interface not found. +. +MessageId=1760 +SymbolicName=RPC_S_ENTRY_ALREADY_EXISTS +Language=ENU +Entry already exists. +. +MessageId=1761 +SymbolicName=RPC_S_ENTRY_NOT_FOUND +Language=ENU +Entry not found. +. +MessageId=1762 +SymbolicName=RPC_S_NAME_SERVICE_UNAVAILABLE +Language=ENU +Name service unavailable. +. +MessageId=1763 +SymbolicName=RPC_S_INVALID_NAF_ID +Language=ENU +Invalid network address family. +. +MessageId=1764 +SymbolicName=RPC_S_CANNOT_SUPPORT +Language=ENU +Operation not supported. +. +MessageId=1765 +SymbolicName=RPC_S_NO_CONTEXT_AVAILABLE +Language=ENU +No security context available. +. +MessageId=1766 +SymbolicName=RPC_S_INTERNAL_ERROR +Language=ENU +RPCInternal error. +. +MessageId=1767 +SymbolicName=RPC_S_ZERO_DIVIDE +Language=ENU +RPC divide-by-zero. +. +MessageId=1768 +SymbolicName=RPC_S_ADDRESS_ERROR +Language=ENU +Address error. +. +MessageId=1769 +SymbolicName=RPC_S_FP_DIV_ZERO +Language=ENU +Floating-point divide-by-zero. +. +MessageId=1770 +SymbolicName=RPC_S_FP_UNDERFLOW +Language=ENU +Floating-point underflow. +. +MessageId=1771 +SymbolicName=RPC_S_FP_OVERFLOW +Language=ENU +Floating-point overflow. +. +MessageId=1772 +SymbolicName=RPC_X_NO_MORE_ENTRIES +Language=ENU +No more entries. +. +MessageId=1773 +SymbolicName=RPC_X_SS_CHAR_TRANS_OPEN_FAIL +Language=ENU +Character translation table open failed. +. +MessageId=1774 +SymbolicName=RPC_X_SS_CHAR_TRANS_SHORT_FILE +Language=ENU +Character translation table file too small. +. +MessageId=1775 +SymbolicName=RPC_X_SS_IN_NULL_CONTEXT +Language=ENU +Null context handle. +. +MessageId=1777 +SymbolicName=RPC_X_SS_CONTEXT_DAMAGED +Language=ENU +Context handle damaged. +. +MessageId=1778 +SymbolicName=RPC_X_SS_HANDLES_MISMATCH +Language=ENU +Binding handle mismatch. +. +MessageId=1779 +SymbolicName=RPC_X_SS_CANNOT_GET_CALL_HANDLE +Language=ENU +Cannot get call handle. +. +MessageId=1780 +SymbolicName=RPC_X_NULL_REF_POINTER +Language=ENU +Null reference pointer. +. +MessageId=1781 +SymbolicName=RPC_X_ENUM_VALUE_OUT_OF_RANGE +Language=ENU +Enumeration value out of range. +. +MessageId=1782 +SymbolicName=RPC_X_BYTE_COUNT_TOO_SMALL +Language=ENU +Byte count too small. +. +MessageId=1783 +SymbolicName=RPC_X_BAD_STUB_DATA +Language=ENU +Bad stub data. +. +MessageId=1784 +SymbolicName=ERROR_INVALID_USER_BUFFER +Language=ENU +Invalid user buffer. +. +MessageId=1785 +SymbolicName=ERROR_UNRECOGNIZED_MEDIA +Language=ENU +Unrecognized media. +. +MessageId=1786 +SymbolicName=ERROR_NO_TRUST_LSA_SECRET +Language=ENU +No trust secret. +. +MessageId=1787 +SymbolicName=ERROR_NO_TRUST_SAM_ACCOUNT +Language=ENU +No trust SAM account. +. +MessageId=1788 +SymbolicName=ERROR_TRUSTED_DOMAIN_FAILURE +Language=ENU +Trusted domain failure. +. +MessageId=1789 +SymbolicName=ERROR_TRUSTED_RELATIONSHIP_FAILURE +Language=ENU +Trusted relationship failure. +. +MessageId=1790 +SymbolicName=ERROR_TRUST_FAILURE +Language=ENU +Trust logon failure. +. +MessageId=1791 +SymbolicName=RPC_S_CALL_IN_PROGRESS +Language=ENU +RPC call already in progress. +. +MessageId=1792 +SymbolicName=ERROR_NETLOGON_NOT_STARTED +Language=ENU +NETLOGON is not started. +. +MessageId=1793 +SymbolicName=ERROR_ACCOUNT_EXPIRED +Language=ENU +Account expired. +. +MessageId=1794 +SymbolicName=ERROR_REDIRECTOR_HAS_OPEN_HANDLES +Language=ENU +Redirector has open handles. +. +MessageId=1795 +SymbolicName=ERROR_PRINTER_DRIVER_ALREADY_INSTALLED +Language=ENU +Printer driver already installed. +. +MessageId=1796 +SymbolicName=ERROR_UNKNOWN_PORT +Language=ENU +Unknown port. +. +MessageId=1797 +SymbolicName=ERROR_UNKNOWN_PRINTER_DRIVER +Language=ENU +Unknown printer driver. +. +MessageId=1798 +SymbolicName=ERROR_UNKNOWN_PRINTPROCESSOR +Language=ENU +Unknown print processor. +. +MessageId=1799 +SymbolicName=ERROR_INVALID_SEPARATOR_FILE +Language=ENU +Invalid separator file. +. +MessageId=1800 +SymbolicName=ERROR_INVALID_PRIORITY +Language=ENU +Invalid priority. +. +MessageId=1801 +SymbolicName=ERROR_INVALID_PRINTER_NAME +Language=ENU +Invalid printer name. +. +MessageId=1802 +SymbolicName=ERROR_PRINTER_ALREADY_EXISTS +Language=ENU +Printer already exists. +. +MessageId=1803 +SymbolicName=ERROR_INVALID_PRINTER_COMMAND +Language=ENU +Invalid printer command. +. +MessageId=1804 +SymbolicName=ERROR_INVALID_DATATYPE +Language=ENU +Invalid data type. +. +MessageId=1805 +SymbolicName=ERROR_INVALID_ENVIRONMENT +Language=ENU +Invalid environment. +. +MessageId=1806 +SymbolicName=RPC_S_NO_MORE_BINDINGS +Language=ENU +No more bindings. +. +MessageId=1807 +SymbolicName=ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT +Language=ENU +Can't log on with inter-domain trust account. +. +MessageId=1808 +SymbolicName=ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT +Language=ENU +Can't log on with workstation trust account. +. +MessageId=1809 +SymbolicName=ERROR_NOLOGON_SERVER_TRUST_ACCOUNT +Language=ENU +Can't log on with server trust account. +. +MessageId=1810 +SymbolicName=ERROR_DOMAIN_TRUST_INCONSISTENT +Language=ENU +Domain trust information inconsistent. +. +MessageId=1811 +SymbolicName=ERROR_SERVER_HAS_OPEN_HANDLES +Language=ENU +Server has open handles. +. +MessageId=1812 +SymbolicName=ERROR_RESOURCE_DATA_NOT_FOUND +Language=ENU +Resource data not found. +. +MessageId=1813 +SymbolicName=ERROR_RESOURCE_TYPE_NOT_FOUND +Language=ENU +Resource type not found. +. +MessageId=1814 +SymbolicName=ERROR_RESOURCE_NAME_NOT_FOUND +Language=ENU +Resource name not found. +. +MessageId=1815 +SymbolicName=ERROR_RESOURCE_LANG_NOT_FOUND +Language=ENU +Resource language not found. +. +MessageId=1816 +SymbolicName=ERROR_NOT_ENOUGH_QUOTA +Language=ENU +Not enough quota. +. +MessageId=1817 +SymbolicName=RPC_S_NO_INTERFACES +Language=ENU +No interfaces. +. +MessageId=1818 +SymbolicName=RPC_S_CALL_CANCELLED +Language=ENU +RPC call canceled. +. +MessageId=1819 +SymbolicName=RPC_S_BINDING_INCOMPLETE +Language=ENU +Binding incomplete. +. +MessageId=1820 +SymbolicName=RPC_S_COMM_FAILURE +Language=ENU +RPC comm failure. +. +MessageId=1821 +SymbolicName=RPC_S_UNSUPPORTED_AUTHN_LEVEL +Language=ENU +Unsupported authorization level. +. +MessageId=1822 +SymbolicName=RPC_S_NO_PRINC_NAME +Language=ENU +No principal name registered. +. +MessageId=1823 +SymbolicName=RPC_S_NOT_RPC_ERROR +Language=ENU +Not an RPC error. +. +MessageId=1824 +SymbolicName=RPC_S_UUID_LOCAL_ONLY +Language=ENU +UUID is local only. +. +MessageId=1825 +SymbolicName=RPC_S_SEC_PKG_ERROR +Language=ENU +Security package error. +. +MessageId=1826 +SymbolicName=RPC_S_NOT_CANCELLED +Language=ENU +Thread not canceled. +. +MessageId=1827 +SymbolicName=RPC_X_INVALID_ES_ACTION +Language=ENU +Invalid handle operation. +. +MessageId=1828 +SymbolicName=RPC_X_WRONG_ES_VERSION +Language=ENU +Wrong serializing package version. +. +MessageId=1829 +SymbolicName=RPC_X_WRONG_STUB_VERSION +Language=ENU +Wrong stub version. +. +MessageId=1830 +SymbolicName=RPC_X_INVALID_PIPE_OBJECT +Language=ENU +Invalid pipe object. +. +MessageId=1831 +SymbolicName=RPC_X_WRONG_PIPE_ORDER +Language=ENU +Wrong pipe order. +. +MessageId=1832 +SymbolicName=RPC_X_WRONG_PIPE_VERSION +Language=ENU +Wrong pipe version. +. +MessageId=1898 +SymbolicName=RPC_S_GROUP_MEMBER_NOT_FOUND +Language=ENU +Group member not found. +. +MessageId=1899 +SymbolicName=EPT_S_CANT_CREATE +Language=ENU +Can't create endpoint mapper DB. +. +MessageId=1900 +SymbolicName=RPC_S_INVALID_OBJECT +Language=ENU +Invalid object. +. +MessageId=1901 +SymbolicName=ERROR_INVALID_TIME +Language=ENU +Invalid time. +. +MessageId=1902 +SymbolicName=ERROR_INVALID_FORM_NAME +Language=ENU +Invalid form name. +. +MessageId=1903 +SymbolicName=ERROR_INVALID_FORM_SIZE +Language=ENU +Invalid form size. +. +MessageId=1904 +SymbolicName=ERROR_ALREADY_WAITING +Language=ENU +Already awaiting printer handle. +. +MessageId=1905 +SymbolicName=ERROR_PRINTER_DELETED +Language=ENU +Printer deleted. +. +MessageId=1906 +SymbolicName=ERROR_INVALID_PRINTER_STATE +Language=ENU +Invalid printer state. +. +MessageId=1907 +SymbolicName=ERROR_PASSWORD_MUST_CHANGE +Language=ENU +User must change password. +. +MessageId=1908 +SymbolicName=ERROR_DOMAIN_CONTROLLER_NOT_FOUND +Language=ENU +Domain controller not found. +. +MessageId=1909 +SymbolicName=ERROR_ACCOUNT_LOCKED_OUT +Language=ENU +Account locked out. +. +MessageId=1910 +SymbolicName=OR_INVALID_OXID +Language=ENU +Invalid pixel format. +. +MessageId=1911 +SymbolicName=OR_INVALID_OID +Language=ENU +Invalid driver. +. +MessageId=1912 +SymbolicName=OR_INVALID_SET +Language=ENU +Invalid object resolver set. +. +MessageId=1913 +SymbolicName=RPC_S_SEND_INCOMPLETE +Language=ENU +Incomplete RPC send. +. +MessageId=1914 +SymbolicName=RPC_S_INVALID_ASYNC_HANDLE +Language=ENU +Invalid asynchronous RPC handle. +. +MessageId=1915 +SymbolicName=RPC_S_INVALID_ASYNC_CALL +Language=ENU +Invalid asynchronous RPC call. +. +MessageId=1916 +SymbolicName=RPC_X_PIPE_CLOSED +Language=ENU +RPC pipe closed. +. +MessageId=1917 +SymbolicName=RPC_X_PIPE_DISCIPLINE_ERROR +Language=ENU +Discipline error on RPC pipe. +. +MessageId=1918 +SymbolicName=RPC_X_PIPE_EMPTY +Language=ENU +No data on RPC pipe. +. +MessageId=1919 +SymbolicName=ERROR_NO_SITENAME +Language=ENU +No site name available. +. +MessageId=1920 +SymbolicName=ERROR_CANT_ACCESS_FILE +Language=ENU +The file cannot be accessed. +. +MessageId=1921 +SymbolicName=ERROR_CANT_RESOLVE_FILENAME +Language=ENU +The filename cannot be resolved. +. +MessageId=1922 +SymbolicName=RPC_S_ENTRY_TYPE_MISMATCH +Language=ENU +RPC entry type mismatch. +. +MessageId=1923 +SymbolicName=RPC_S_NOT_ALL_OBJS_EXPORTED +Language=ENU +Not all objects could be exported. +. +MessageId=1924 +SymbolicName=RPC_S_INTERFACE_NOT_EXPORTED +Language=ENU +The interface could not be exported. +. +MessageId=1925 +SymbolicName=RPC_S_PROFILE_NOT_ADDED +Language=ENU +The profile could not be added. +. +MessageId=1926 +SymbolicName=RPC_S_PRF_ELT_NOT_ADDED +Language=ENU +The profile element could not be added. +. +MessageId=1927 +SymbolicName=RPC_S_PRF_ELT_NOT_REMOVED +Language=ENU +The profile element could not be removed. +. +MessageId=1928 +SymbolicName=RPC_S_GRP_ELT_NOT_ADDED +Language=ENU +The group element could not be added. +. +MessageId=1929 +SymbolicName=RPC_S_GRP_ELT_NOT_REMOVED +Language=ENU +The group element could not be removed. +. +MessageId=2221 +SymbolicName=NERR_UserNotFound +Language=ENU +The username could not be found. +. +MessageId=2250 +SymbolicName=ERROR_NOT_CONNECTED +Language=ENU +This network connection does not exist. +. +MessageId=10004 +SymbolicName=WSAEINTR +Language=ENU +Call interrupted. +. +MessageId=10009 +SymbolicName=WSAEBADF +Language=ENU +Invalid file handle. +. +MessageId=10013 +SymbolicName=WSAEACCES +Language=ENU +Access denied. +. +MessageId=10014 +SymbolicName=WSAEFAULT +Language=ENU +Invalid pointer address. +. +MessageId=10022 +SymbolicName=WSAEINVAL +Language=ENU +Invalid argument. +. +MessageId=10024 +SymbolicName=WSAEMFILE +Language=ENU +Too many open files. +. +MessageId=10049 +SymbolicName=WSAEADDRNOTAVAIL +Language=ENU +Cannot assign requested address. +. +MessageId=10054 +SymbolicName=WSAECONNRESET +Language=ENU +Connection reset by peer. +. +MessageId=10061 +SymbolicName=WSAECONNREFUSED +Language=ENU +Connection refused. +. +MessageId=11001 +SymbolicName=WSAHOST_NOT_FOUND +Language=ENU +Host not found. +. +MessageId=11002 +SymbolicName=WSATRY_AGAIN +Language=ENU +Nonauthoritative host not found. +. +MessageId=11003 +SymbolicName=WSANO_RECOVERY +Language=ENU +Nonrecoverable error. +. +MessageId=11004 +SymbolicName=WSANO_DATA +Language=ENU +Name valid, no data record. +. +MessageId=0x100 +Severity=CoError +Facility=Trust +SymbolicName=TRUST_E_NOSIGNATURE +Language=ENU +No Signature found in file. +. +MessageId=0x4001 +Severity=CoError +Facility=Null +SymbolicName=E_NOTIMPL +Language=ENU +Not implemented. +. +MessageId=0x1 +Severity=CoError +Facility=Dxgi +SymbolicName=DXGI_ERROR_INVALID_CALL +Language=ENU +Invalid call. +. +MessageId=0x22 +Severity=CoError +Facility=Dxgi +SymbolicName=DXGI_ERROR_NOT_CURRENTLY_AVAILABLE +Language=ENU +Resource is not currently available. +. +MessageId=0x4005 +Severity=CoError +Facility=Null +SymbolicName=E_FAIL +Language=ENU +Call failed. +. diff --git a/sdk/include/psdk/ioringapi.h b/sdk/include/psdk/ioringapi.h new file mode 100644 index 0000000000000..3ccbfc8065455 --- /dev/null +++ b/sdk/include/psdk/ioringapi.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Paul Gofman for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __IORINGAPI_H_ +#define __IORINGAPI_H_ + +#include + +struct IORING_CAPABILITIES +{ + IORING_VERSION MaxVersion; + UINT32 MaxSubmissionQueueSize; + UINT32 MaxCompletionQueueSize; + IORING_FEATURE_FLAGS FeatureFlags; +}; +typedef struct IORING_CAPABILITIES IORING_CAPABILITIES; + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT WINAPI QueryIoRingCapabilities(IORING_CAPABILITIES *caps); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/sdk/include/psdk/ntioring_x.h b/sdk/include/psdk/ntioring_x.h new file mode 100644 index 0000000000000..bf16d9368d4c1 --- /dev/null +++ b/sdk/include/psdk/ntioring_x.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Paul Gofman for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __NTIORING_X_H_ +#define __NTIORING_X_H_ + +enum IORING_VERSION +{ + IORING_VERSION_INVALID = 0, + IORING_VERSION_1 = 1, + IORING_VERSION_2 = 2, + IORING_VERSION_3 = 300, +}; +typedef enum IORING_VERSION IORING_VERSION; + +enum IORING_FEATURE_FLAGS +{ + IORING_FEATURE_FLAGS_NONE = 0, + IORING_FEATURE_UM_EMULATION = 0x00000001, + IORING_FEATURE_SET_COMPLETION_EVENT = 0x00000002, +}; +typedef enum IORING_FEATURE_FLAGS IORING_FEATURE_FLAGS; + +#endif