From 90e903957aa6e4dc96de5b410748d41dde74225d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Fri, 25 Oct 2013 14:59:38 -0400 Subject: [PATCH 001/149] Fixed warmings (string formats) --- libfreerdp/core/gateway/tsg.c | 2 +- libfreerdp/utils/profiler.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libfreerdp/core/gateway/tsg.c b/libfreerdp/core/gateway/tsg.c index e10e164c8..a1a477be6 100644 --- a/libfreerdp/core/gateway/tsg.c +++ b/libfreerdp/core/gateway/tsg.c @@ -355,7 +355,7 @@ BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu) offset += 8; // UnicodeString Offset, Length } if(MsgBytes > TSG_MESSAGING_MAX_MESSAGE_LENGTH) { - fprintf(stderr, "Out of Spec Message Length %d"); + fprintf(stderr, "Out of Spec Message Length %d", MsgBytes); return FALSE; } offset += MsgBytes; diff --git a/libfreerdp/utils/profiler.c b/libfreerdp/utils/profiler.c index 794f3e7f1..d735dba77 100644 --- a/libfreerdp/utils/profiler.c +++ b/libfreerdp/utils/profiler.c @@ -70,7 +70,7 @@ void profiler_print(PROFILER* profiler) double elapsed_sec = stopwatch_get_elapsed_time_in_seconds(profiler->stopwatch); double avg_sec = elapsed_sec / (double) profiler->stopwatch->count; - fprintf(stderr, "| %-30.30s| %10lu | %9f | %9f |\n", + fprintf(stderr, "| %-30.30s| %10du | %9f | %9f |\n", profiler->name, profiler->stopwatch->count, elapsed_sec, avg_sec); } From ebb71062b84671946b35100d934b1d7027b08526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Mon, 28 Oct 2013 13:44:17 -0400 Subject: [PATCH 002/149] fixed incorrect printf format --- libfreerdp/crypto/tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index e5c496b40..52bdaf712 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -99,7 +99,7 @@ SecPkgContext_Bindings* tls_get_channel_bindings(X509* cert) static void tls_ssl_info_callback(const SSL* ssl, int type, int val) { - printf("tls_ssl_info_callback: type: %d val: %d\n"); + printf("tls_ssl_info_callback: type: %d val: %d\n", type, val); if (type & SSL_CB_HANDSHAKE_START) { From 993f5f18b81a052ead96ce9e12d9a07fa08ebce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Mon, 4 Nov 2013 09:10:05 -0500 Subject: [PATCH 003/149] added const to buffer parameters in functions --- client/common/client.c | 2 +- client/common/file.c | 6 +++--- include/freerdp/client.h | 2 +- include/freerdp/client/file.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/common/client.c b/client/common/client.c index 926ea9889..b3b5a3617 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -133,7 +133,7 @@ int freerdp_client_settings_parse_connection_file(rdpSettings* settings, const c return 0; } -int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, BYTE* buffer, size_t size) +int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, const BYTE* buffer, size_t size) { rdpFile* file; int status = -1; diff --git a/client/common/file.c b/client/common/file.c index 35b0b1341..153325bb6 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -323,7 +323,7 @@ void freerdp_client_parse_rdp_file_option_ascii(rdpFile* file, char* option) freerdp_client_add_option(file, option); } -BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, BYTE* buffer, size_t size) +BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffer, size_t size) { int length; char* line; @@ -395,7 +395,7 @@ next_line: return TRUE; } -BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, BYTE* buffer, size_t size) +BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buffer, size_t size) { int length; WCHAR* line; @@ -468,7 +468,7 @@ next_line: return TRUE; } -BOOL freerdp_client_parse_rdp_file_buffer(rdpFile* file, BYTE* buffer, size_t size) +BOOL freerdp_client_parse_rdp_file_buffer(rdpFile* file, const BYTE* buffer, size_t size) { if (size < 2) return FALSE; diff --git a/include/freerdp/client.h b/include/freerdp/client.h index 2878ffa2e..60ada2772 100644 --- a/include/freerdp/client.h +++ b/include/freerdp/client.h @@ -87,7 +87,7 @@ FREERDP_API HANDLE freerdp_client_get_thread(rdpContext* context); FREERDP_API int freerdp_client_settings_parse_command_line(rdpSettings* settings, int argc, char** argv); FREERDP_API int freerdp_client_settings_parse_connection_file(rdpSettings* settings, const char* filename); -FREERDP_API int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, BYTE* buffer, size_t size); +FREERDP_API int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, const BYTE* buffer, size_t size); FREERDP_API int freerdp_client_settings_write_connection_file(rdpSettings* settings, const char* filename, BOOL unicode); #ifdef __cplusplus diff --git a/include/freerdp/client/file.h b/include/freerdp/client/file.h index bf9dcb4d6..8268f286d 100644 --- a/include/freerdp/client/file.h +++ b/include/freerdp/client/file.h @@ -142,7 +142,7 @@ extern "C" { #endif FREERDP_API BOOL freerdp_client_parse_rdp_file(rdpFile* file, const char* name); -FREERDP_API BOOL freerdp_client_parse_rdp_file_buffer(rdpFile* file, BYTE* buffer, size_t size); +FREERDP_API BOOL freerdp_client_parse_rdp_file_buffer(rdpFile* file, const BYTE* buffer, size_t size); FREERDP_API BOOL freerdp_client_populate_settings_from_rdp_file(rdpFile* file, rdpSettings* settings); FREERDP_API BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, rdpSettings* settings); From cc881678a67e65cc3063e2d6d83809ba04997484 Mon Sep 17 00:00:00 2001 From: Richard Markiewicz Date: Mon, 4 Nov 2013 11:35:25 -0500 Subject: [PATCH 004/149] Update AppDelegate.m stringWithFormat returns an autoreleased object, so manually releasing message results in a crash --- client/Mac/cli/AppDelegate.m | 1 - 1 file changed, 1 deletion(-) diff --git a/client/Mac/cli/AppDelegate.m b/client/Mac/cli/AppDelegate.m index 9c5636079..612205bae 100644 --- a/client/Mac/cli/AppDelegate.m +++ b/client/Mac/cli/AppDelegate.m @@ -199,7 +199,6 @@ void AppDelegate_ConnectionResultEventHandler(void* ctx, ConnectionResultEventAr // Making sure this should be invoked on the main UI thread. [_singleDelegate performSelectorOnMainThread:@selector(rdpConnectError:) withObject:message waitUntilDone:FALSE]; - [message release]; } } } From a04fd2c65f16cbb70740a670b73199c8be285456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 6 Nov 2013 16:25:59 -0500 Subject: [PATCH 005/149] libfreerdp-core: added missing error info values --- include/freerdp/error.h | 18 +++++++++++ libfreerdp/core/errinfo.c | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/include/freerdp/error.h b/include/freerdp/error.h index 3c00b5c13..91ea67f7e 100755 --- a/include/freerdp/error.h +++ b/include/freerdp/error.h @@ -45,6 +45,19 @@ extern "C" { #define ERRINFO_RPC_INITIATED_DISCONNECT_BY_USER 0x0000000B #define ERRINFO_LOGOFF_BY_USER 0x0000000C +/* Protocol-independent codes generated by the Connection Broker */ +#define ERRINFO_CB_DESTINATION_NOT_FOUND 0x0000400 +#define ERRINFO_CB_LOADING_DESTINATION 0x0000402 +#define ERRINFO_CB_REDIRECTING_TO_DESTINATION 0x0000404 +#define ERRINFO_CB_SESSION_ONLINE_VM_WAKE 0x0000405 +#define ERRINFO_CB_SESSION_ONLINE_VM_BOOT 0x0000406 +#define ERRINFO_CB_SESSION_ONLINE_VM_NO_DNS 0x0000407 +#define ERRINFO_CB_DESTINATION_POOL_NOT_FREE 0x0000408 +#define ERRINFO_CB_CONNECTION_CANCELLED 0x0000409 +#define ERRINFO_CB_CONNECTION_ERROR_INVALID_SETTINGS 0x0000410 +#define ERRINFO_CB_SESSION_ONLINE_VM_BOOT_TIMEOUT 0x0000411 +#define ERRINFO_CB_SESSION_ONLINE_VM_SESSMON_FAILED 0x0000412 + /* Protocol-independent licensing codes */ #define ERRINFO_LICENSE_INTERNAL 0x00000100 #define ERRINFO_LICENSE_NO_LICENSE_SERVER 0x00000101 @@ -123,6 +136,11 @@ extern "C" { #define ERRINFO_VC_DATA_TOO_LONG 0x0000112B #define ERRINFO_GRAPHICS_MODE_NOT_SUPPORTED 0x0000112D #define ERRINFO_GRAPHICS_SUBSYSTEM_RESET_FAILED 0x0000112E +#define ERRINFO_GRAPHICS_SUBSYSTEM_FAILED 0x0000112F +#define ERRINFO_TIMEZONE_KEY_NAME_LENGTH_TOO_SHORT 0x00001130 +#define ERRINFO_TIMEZONE_KEY_NAME_LENGTH_TOO_LONG 0x00001131 +#define ERRINFO_DYNAMIC_DST_DISABLED_FIELD_MISSING 0x00001132 +#define ERRINFO_VC_DECODING_ERROR 0x00001133 #define ERRINFO_UPDATE_SESSION_KEY_FAILED 0x00001191 #define ERRINFO_DECRYPT_FAILED 0x00001192 #define ERRINFO_ENCRYPT_FAILED 0x00001193 diff --git a/libfreerdp/core/errinfo.c b/libfreerdp/core/errinfo.c index da028318a..ab8550c7d 100644 --- a/libfreerdp/core/errinfo.c +++ b/libfreerdp/core/errinfo.c @@ -62,6 +62,41 @@ int connectErrorCode; #define ERRINFO_LOGOFF_BY_USER_STRING \ "The disconnection was initiated by the user logging off his or her session on the server." +/* Protocol-independent codes generated by the Connection Broker */ + +#define ERRINFO_CB_DESTINATION_NOT_FOUND_STRING \ + "The target endpoint could not be found." + +#define ERRINFO_CB_LOADING_DESTINATION_STRING \ + "The target endpoint to which the client is being redirected is disconnecting from the Connection Broker." + +#define ERRINFO_CB_REDIRECTING_TO_DESTINATION_STRING \ + "An error occurred while the connection was being redirected to the target endpoint." + +#define ERRINFO_CB_SESSION_ONLINE_VM_WAKE_STRING \ + "An error occurred while the target endpoint (a virtual machine) was being awakened." + +#define ERRINFO_CB_SESSION_ONLINE_VM_BOOT_STRING \ + "An error occurred while the target endpoint (a virtual machine) was being started." + +#define ERRINFO_CB_SESSION_ONLINE_VM_NO_DNS_STRING \ + "The IP address of the target endpoint (a virtual machine) cannot be determined." + +#define ERRINFO_CB_DESTINATION_POOL_NOT_FREE_STRING \ + "There are no available endpoints in the pool managed by the Connection Broker." + +#define ERRINFO_CB_CONNECTION_CANCELLED_STRING \ + "Processing of the connection has been cancelled." + +#define ERRINFO_CB_CONNECTION_ERROR_INVALID_SETTINGS_STRING \ + "The settings contained in the routingToken field of the X.224 Connection Request PDU (section 2.2.1.1) cannot be validated." + +#define ERRINFO_CB_SESSION_ONLINE_VM_BOOT_TIMEOUT_STRING \ + "A time-out occurred while the target endpoint (a virtual machine) was being started." + +#define ERRINFO_CB_SESSION_ONLINE_VM_SESSMON_FAILED_STRING \ + "A session monitoring error occurred while the target endpoint (a virtual machine) was being started." + /* Protocol-independent licensing codes */ #define ERRINFO_LICENSE_INTERNAL_STRING \ @@ -319,6 +354,21 @@ int connectErrorCode; #define ERRINFO_GRAPHICS_SUBSYSTEM_RESET_FAILED_STRING \ "The server-side graphics subsystem failed to reset." +#define ERRINFO_GRAPHICS_SUBSYSTEM_FAILED_STRING \ + "The server-side graphics subsystem is in an error state and unable to continue graphics encoding." + +#define ERRINFO_TIMEZONE_KEY_NAME_LENGTH_TOO_SHORT_STRING \ + "There is not enough data to read the cbDynamicDSTTimeZoneKeyName field in the Extended Info Packet (section 2.2.1.11.1.1.1)." + +#define ERRINFO_TIMEZONE_KEY_NAME_LENGTH_TOO_LONG_STRING \ + "The length reported in the cbDynamicDSTTimeZoneKeyName field of the Extended Info Packet (section 2.2.1.11.1.1.1) is too long." + +#define ERRINFO_DYNAMIC_DST_DISABLED_FIELD_MISSING_STRING \ + "The dynamicDaylightTimeDisabled field is not present in the Extended Info Packet (section 2.2.1.11.1.1.1)." + +#define ERRINFO_VC_DECODING_ERROR_STRING \ + "An error occurred when processing dynamic virtual channel data ([MS-RDPEDYC] section 3.3.5)." + #define ERRINFO_UPDATE_SESSION_KEY_FAILED_STRING \ "An attempt to update the session keys while using Standard RDP Security mechanisms (section 5.3.7) failed." @@ -356,6 +406,19 @@ static const ERRINFO ERRINFO_CODES[] = ERRINFO_DEFINE(RPC_INITIATED_DISCONNECT_BY_USER), ERRINFO_DEFINE(LOGOFF_BY_USER), + /* Protocol-independent codes generated by the Connection Broker */ + ERRINFO_DEFINE(CB_DESTINATION_NOT_FOUND), + ERRINFO_DEFINE(CB_LOADING_DESTINATION), + ERRINFO_DEFINE(CB_REDIRECTING_TO_DESTINATION), + ERRINFO_DEFINE(CB_SESSION_ONLINE_VM_WAKE), + ERRINFO_DEFINE(CB_SESSION_ONLINE_VM_BOOT), + ERRINFO_DEFINE(CB_SESSION_ONLINE_VM_NO_DNS), + ERRINFO_DEFINE(CB_DESTINATION_POOL_NOT_FREE), + ERRINFO_DEFINE(CB_CONNECTION_CANCELLED), + ERRINFO_DEFINE(CB_CONNECTION_ERROR_INVALID_SETTINGS), + ERRINFO_DEFINE(CB_SESSION_ONLINE_VM_BOOT_TIMEOUT), + ERRINFO_DEFINE(CB_SESSION_ONLINE_VM_SESSMON_FAILED), + /* Protocol-independent licensing codes */ ERRINFO_DEFINE(LICENSE_INTERNAL), ERRINFO_DEFINE(LICENSE_NO_LICENSE_SERVER), @@ -434,6 +497,11 @@ static const ERRINFO ERRINFO_CODES[] = ERRINFO_DEFINE(VC_DATA_TOO_LONG), ERRINFO_DEFINE(GRAPHICS_MODE_NOT_SUPPORTED), ERRINFO_DEFINE(GRAPHICS_SUBSYSTEM_RESET_FAILED), + ERRINFO_DEFINE(GRAPHICS_SUBSYSTEM_FAILED), + ERRINFO_DEFINE(TIMEZONE_KEY_NAME_LENGTH_TOO_SHORT), + ERRINFO_DEFINE(TIMEZONE_KEY_NAME_LENGTH_TOO_LONG), + ERRINFO_DEFINE(DYNAMIC_DST_DISABLED_FIELD_MISSING), + ERRINFO_DEFINE(VC_DECODING_ERROR), ERRINFO_DEFINE(UPDATE_SESSION_KEY_FAILED), ERRINFO_DEFINE(DECRYPT_FAILED), ERRINFO_DEFINE(ENCRYPT_FAILED), From 08da7211aa05c182e31bf4a2a912f7c79d223e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Wed, 6 Nov 2013 20:07:42 -0500 Subject: [PATCH 006/149] Fixed crash caused by string value not being copied when parsing an ASCII file. --- client/common/file.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/common/file.c b/client/common/file.c index 341c1222d..fd4729828 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -305,7 +305,9 @@ void freerdp_client_parse_rdp_file_string_unicode(rdpFile* file, WCHAR* name, WC void freerdp_client_parse_rdp_file_string_ascii(rdpFile* file, char* name, char* value) { - freerdp_client_rdp_file_set_string(file, name, value); + char* valueA = _strdup(value); + if (!freerdp_client_rdp_file_set_string(file, name, valueA)) + free(valueA); } void freerdp_client_parse_rdp_file_option_unicode(rdpFile* file, WCHAR* option) @@ -325,6 +327,7 @@ void freerdp_client_parse_rdp_file_option_ascii(rdpFile* file, char* option) BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffer, size_t size) { + fprintf(stderr, "freerdp_client_parse_rdp_file_buffer_ascii"); int length; char* line; char* type; @@ -397,6 +400,7 @@ next_line: BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buffer, size_t size) { + fprintf(stderr, "freerdp_client_parse_rdp_file_buffer_unicode\n"); int length; WCHAR* line; WCHAR* type; From b50969e05df438087d629cdfd8b8c2cf8eb2347f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Wed, 6 Nov 2013 20:15:14 -0500 Subject: [PATCH 007/149] Removed logs --- client/common/file.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/common/file.c b/client/common/file.c index fd4729828..e979d4c9a 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -327,7 +327,6 @@ void freerdp_client_parse_rdp_file_option_ascii(rdpFile* file, char* option) BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffer, size_t size) { - fprintf(stderr, "freerdp_client_parse_rdp_file_buffer_ascii"); int length; char* line; char* type; @@ -400,7 +399,6 @@ next_line: BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buffer, size_t size) { - fprintf(stderr, "freerdp_client_parse_rdp_file_buffer_unicode\n"); int length; WCHAR* line; WCHAR* type; From d754e4f9a8071a6ccb2146271af3908ed0e77288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 7 Nov 2013 10:37:46 -0500 Subject: [PATCH 008/149] Fixed memory corruption that occured when writing rdpFile to disk --- client/common/client.c | 2 +- client/common/file.c | 31 ++++++++++++++++--------------- include/freerdp/client.h | 2 +- include/freerdp/client/file.h | 6 +++--- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/client/common/client.c b/client/common/client.c index f563f198d..2724408e0 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -151,7 +151,7 @@ int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, return status; } -int freerdp_client_settings_write_connection_file(rdpSettings* settings, const char* filename, BOOL unicode) +int freerdp_client_settings_write_connection_file(const rdpSettings* settings, const char* filename, BOOL unicode) { rdpFile* file; int status = -1; diff --git a/client/common/file.c b/client/common/file.c index 4ca74fea6..a85dc62da 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -532,13 +532,14 @@ BOOL freerdp_client_parse_rdp_file(rdpFile* file, const char* name) #define WRITE_ALL_SETTINGS TRUE #define SETTING_MODIFIED(_settings, _field) (WRITE_ALL_SETTINGS || _settings->SettingsModified[FreeRDP_##_field]) #define SETTING_MODIFIED_SET(_target, _settings, _field) if SETTING_MODIFIED(_settings, _field) _target = _settings->_field +#define SETTING_MODIFIED_SET_STRING(_target, _settings, _field) if SETTING_MODIFIED(_settings, _field) _target = _strdup(_settings->_field) -BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, rdpSettings* settings) +BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, const rdpSettings* settings) { - SETTING_MODIFIED_SET(file->Domain, settings, Domain); - SETTING_MODIFIED_SET(file->Username, settings, Username); + SETTING_MODIFIED_SET_STRING(file->Domain, settings, Domain); + SETTING_MODIFIED_SET_STRING(file->Username, settings, Username); SETTING_MODIFIED_SET(file->ServerPort, settings, ServerPort); - SETTING_MODIFIED_SET(file->FullAddress, settings, ServerHostname); + SETTING_MODIFIED_SET_STRING(file->FullAddress, settings, ServerHostname); SETTING_MODIFIED_SET(file->DesktopWidth, settings, DesktopWidth); SETTING_MODIFIED_SET(file->DesktopHeight, settings, DesktopHeight); SETTING_MODIFIED_SET(file->SessionBpp, settings, ColorDepth); @@ -546,8 +547,8 @@ BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, rdpSettings* SETTING_MODIFIED_SET(file->AdministrativeSession, settings, ConsoleSession); SETTING_MODIFIED_SET(file->NegotiateSecurityLayer, settings, NegotiateSecurityLayer); SETTING_MODIFIED_SET(file->EnableCredSSPSupport, settings, NlaSecurity); - SETTING_MODIFIED_SET(file->AlternateShell, settings, AlternateShell); - SETTING_MODIFIED_SET(file->ShellWorkingDirectory, settings, ShellWorkingDirectory); + SETTING_MODIFIED_SET_STRING(file->AlternateShell, settings, AlternateShell); + SETTING_MODIFIED_SET_STRING(file->ShellWorkingDirectory, settings, ShellWorkingDirectory); SETTING_MODIFIED_SET(file->ConnectionType, settings, ConnectionType); if (SETTING_MODIFIED(settings, AudioPlayback) || SETTING_MODIFIED(settings, RemoteConsoleAudio)) @@ -560,17 +561,17 @@ BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, rdpSettings* file->AudioMode = AUDIO_MODE_NONE; } - SETTING_MODIFIED_SET(file->GatewayHostname, settings, GatewayHostname); + SETTING_MODIFIED_SET_STRING(file->GatewayHostname, settings, GatewayHostname); SETTING_MODIFIED_SET(file->GatewayUsageMethod, settings, GatewayUsageMethod); SETTING_MODIFIED_SET(file->PromptCredentialOnce, settings, GatewayUseSameCredentials); SETTING_MODIFIED_SET(file->RemoteApplicationMode, settings, RemoteApplicationMode); - SETTING_MODIFIED_SET(file->RemoteApplicationProgram, settings, RemoteApplicationProgram); - SETTING_MODIFIED_SET(file->RemoteApplicationName, settings, RemoteApplicationName); - SETTING_MODIFIED_SET(file->RemoteApplicationIcon, settings, RemoteApplicationIcon); - SETTING_MODIFIED_SET(file->RemoteApplicationFile, settings, RemoteApplicationFile); - SETTING_MODIFIED_SET(file->RemoteApplicationGuid, settings, RemoteApplicationGuid); - SETTING_MODIFIED_SET(file->RemoteApplicationCmdLine, settings, RemoteApplicationCmdLine); + SETTING_MODIFIED_SET_STRING(file->RemoteApplicationProgram, settings, RemoteApplicationProgram); + SETTING_MODIFIED_SET_STRING(file->RemoteApplicationName, settings, RemoteApplicationName); + SETTING_MODIFIED_SET_STRING(file->RemoteApplicationIcon, settings, RemoteApplicationIcon); + SETTING_MODIFIED_SET_STRING(file->RemoteApplicationFile, settings, RemoteApplicationFile); + SETTING_MODIFIED_SET_STRING(file->RemoteApplicationGuid, settings, RemoteApplicationGuid); + SETTING_MODIFIED_SET_STRING(file->RemoteApplicationCmdLine, settings, RemoteApplicationCmdLine); SETTING_MODIFIED_SET(file->SpanMonitors, settings, SpanMonitors); SETTING_MODIFIED_SET(file->UseMultiMon, settings, UseMultimon); @@ -578,7 +579,7 @@ BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, rdpSettings* return TRUE; } -BOOL freerdp_client_write_rdp_file(rdpFile* file, const char* name, BOOL unicode) +BOOL freerdp_client_write_rdp_file(const rdpFile* file, const char* name, BOOL unicode) { int rc = 0; char* buffer; @@ -656,7 +657,7 @@ if (~__rdpFile->_field) \ #define WRITE_RDP_FILE_VALUE_RETURN \ return __required_size; -size_t freerdp_client_write_rdp_file_buffer(rdpFile* file, char* buffer, size_t size) +size_t freerdp_client_write_rdp_file_buffer(const rdpFile* file, char* buffer, size_t size) { WRITE_RDP_FILE_DECLARE(file, buffer, size) diff --git a/include/freerdp/client.h b/include/freerdp/client.h index 2878ffa2e..d76a3224f 100644 --- a/include/freerdp/client.h +++ b/include/freerdp/client.h @@ -88,7 +88,7 @@ FREERDP_API HANDLE freerdp_client_get_thread(rdpContext* context); FREERDP_API int freerdp_client_settings_parse_command_line(rdpSettings* settings, int argc, char** argv); FREERDP_API int freerdp_client_settings_parse_connection_file(rdpSettings* settings, const char* filename); FREERDP_API int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, BYTE* buffer, size_t size); -FREERDP_API int freerdp_client_settings_write_connection_file(rdpSettings* settings, const char* filename, BOOL unicode); +FREERDP_API int freerdp_client_settings_write_connection_file(const rdpSettings* settings, const char* filename, BOOL unicode); #ifdef __cplusplus } diff --git a/include/freerdp/client/file.h b/include/freerdp/client/file.h index bf9dcb4d6..4f3153017 100644 --- a/include/freerdp/client/file.h +++ b/include/freerdp/client/file.h @@ -145,9 +145,9 @@ FREERDP_API BOOL freerdp_client_parse_rdp_file(rdpFile* file, const char* name); FREERDP_API BOOL freerdp_client_parse_rdp_file_buffer(rdpFile* file, BYTE* buffer, size_t size); FREERDP_API BOOL freerdp_client_populate_settings_from_rdp_file(rdpFile* file, rdpSettings* settings); -FREERDP_API BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, rdpSettings* settings); -FREERDP_API BOOL freerdp_client_write_rdp_file(rdpFile* file, const char* name, BOOL unicode); -FREERDP_API size_t freerdp_client_write_rdp_file_buffer(rdpFile* file, char* buffer, size_t size); +FREERDP_API BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, const rdpSettings* settings); +FREERDP_API BOOL freerdp_client_write_rdp_file(const rdpFile* file, const char* name, BOOL unicode); +FREERDP_API size_t freerdp_client_write_rdp_file_buffer(const rdpFile* file, char* buffer, size_t size); FREERDP_API rdpFile* freerdp_client_rdp_file_new(void); FREERDP_API void freerdp_client_rdp_file_free(rdpFile* file); From f4d0371480c85aa945b126203604cffcf3a9ed5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Thu, 7 Nov 2013 10:48:49 -0500 Subject: [PATCH 009/149] Removed const warning --- client/common/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/common/file.c b/client/common/file.c index 11352d515..40240892d 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -633,7 +633,7 @@ BOOL freerdp_client_write_rdp_file(const rdpFile* file, const char* name, BOOL u } #define WRITE_RDP_FILE_DECLARE(_file, _buffer, _size) \ - rdpFile* __rdpFile = file; \ + const rdpFile* __rdpFile = file; \ char* __buffer = _buffer; \ size_t __size = _size; \ size_t __required_size = 0; \ From c4dea17af33be0ff8d24fe12536cca59f29b14ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20LeBlanc?= Date: Thu, 7 Nov 2013 13:44:18 -0500 Subject: [PATCH 010/149] added _scprintf macro to winpr because snprintf does not count characters on Windows. Modified write_rdp_file_ macros for windows compatibility --- client/common/file.c | 12 ++++++++++-- winpr/include/winpr/string.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/client/common/file.c b/client/common/file.c index 40240892d..b7787d096 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -643,7 +643,11 @@ BOOL freerdp_client_write_rdp_file(const rdpFile* file, const char* name, BOOL u #define WRITE_RDP_FILE_VALUE_INTEGER(_format, _field) \ if (~__rdpFile->_field) \ { \ - __count = sprintf_s(__buffer == NULL ? NULL : __buffer + __current, __buffer == NULL ? 0 : __size - __required_size, _format, (int) __rdpFile->_field); \ + if (__buffer) \ + __count = sprintf_s(__buffer + __current, __size - __required_size, _format, (int) __rdpFile->_field); \ + else \ + __count = _scprintf(_format, (int) __rdpFile->_field); \ + \ __required_size += __count; \ __current += __count; \ } @@ -651,7 +655,11 @@ if (~__rdpFile->_field) \ #define WRITE_RDP_FILE_VALUE_STRING(_format, _field) \ if (~((size_t) __rdpFile->_field) && __rdpFile->_field != NULL) \ { \ - __count = sprintf_s(__buffer == NULL ? NULL : __buffer + __current, __buffer == NULL ? 0 : __size - __required_size, _format, __rdpFile->_field); \ + if (buffer) \ + __count = sprintf_s(__buffer + __current, __size - __required_size, _format, __rdpFile->_field); \ + else \ + __count = _scprintf(_format, __rdpFile->_field); \ + \ __required_size += __count; \ __current += __count; \ } diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h index 8cb2466f0..65cdc5549 100644 --- a/winpr/include/winpr/string.h +++ b/winpr/include/winpr/string.h @@ -153,6 +153,7 @@ WINPR_API int lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2); #endif #define sprintf_s snprintf +#define _scprintf(_fmt, …) snprintf(NULL, 0, _fmt, ## __VAR_ARGS__) /* Unicode Conversion */ From 02fe384ecb4d2ced6ca70fb3fb77fd1eca17fa3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20LeBlanc?= Date: Thu, 7 Nov 2013 13:45:16 -0500 Subject: [PATCH 011/149] typo --- client/common/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/common/file.c b/client/common/file.c index b7787d096..0d993345e 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -655,7 +655,7 @@ if (~__rdpFile->_field) \ #define WRITE_RDP_FILE_VALUE_STRING(_format, _field) \ if (~((size_t) __rdpFile->_field) && __rdpFile->_field != NULL) \ { \ - if (buffer) \ + if (__buffer) \ __count = sprintf_s(__buffer + __current, __size - __required_size, _format, __rdpFile->_field); \ else \ __count = _scprintf(_format, __rdpFile->_field); \ From dfd39b6d0a8c16c658f5ccfbc6055b08126104b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Thu, 7 Nov 2013 13:55:06 -0500 Subject: [PATCH 012/149] Fixed macro declaration --- winpr/include/winpr/string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h index 65cdc5549..d35728b1d 100644 --- a/winpr/include/winpr/string.h +++ b/winpr/include/winpr/string.h @@ -153,7 +153,7 @@ WINPR_API int lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2); #endif #define sprintf_s snprintf -#define _scprintf(_fmt, …) snprintf(NULL, 0, _fmt, ## __VAR_ARGS__) +#define _scprintf(_fmt, ...) snprintf(NULL, 0, _fmt, ## __VA_ARGS__) /* Unicode Conversion */ From 8cbb7f42da5f3d1e8c7d0bce3738ab79c29e645d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 7 Nov 2013 13:57:05 -0500 Subject: [PATCH 013/149] libwinpr-library: add GetModuleFileName implementation for Linux --- winpr/include/winpr/library.h | 13 ++-- winpr/include/winpr/string.h | 2 + winpr/libwinpr/library/library.c | 59 +++++++++++++++++++ winpr/libwinpr/library/test/CMakeLists.txt | 3 +- .../test/TestLibraryGetModuleFileName.c | 18 ++++++ 5 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 winpr/libwinpr/library/test/TestLibraryGetModuleFileName.c diff --git a/winpr/include/winpr/library.h b/winpr/include/winpr/library.h index 721d2b70e..ae1906b10 100644 --- a/winpr/include/winpr/library.h +++ b/winpr/include/winpr/library.h @@ -46,12 +46,17 @@ WINPR_API HMODULE LoadLibraryW(LPCWSTR lpLibFileName); WINPR_API HMODULE LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); WINPR_API HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); +WINPR_API DWORD GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize); +WINPR_API DWORD GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize); + #ifdef UNICODE -#define LoadLibrary LoadLibraryW -#define LoadLibraryEx LoadLibraryExW +#define LoadLibrary LoadLibraryW +#define LoadLibraryEx LoadLibraryExW +#define GetModuleFileName GetModuleFileNameW #else -#define LoadLibrary LoadLibraryA -#define LoadLibraryEx LoadLibraryExA +#define LoadLibrary LoadLibraryA +#define LoadLibraryEx LoadLibraryExA +#define GetModuleFileName GetModuleFileNameA #endif WINPR_API FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName); diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h index 8cb2466f0..9321b488c 100644 --- a/winpr/include/winpr/string.h +++ b/winpr/include/winpr/string.h @@ -154,6 +154,8 @@ WINPR_API int lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2); #define sprintf_s snprintf +#define _scprintf(_fmt, ...) snprintf(NULL, 0, _fmt, ## __VA_ARGS__) + /* Unicode Conversion */ WINPR_API int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, diff --git a/winpr/libwinpr/library/library.c b/winpr/libwinpr/library/library.c index 54c8b223b..9a0419192 100644 --- a/winpr/libwinpr/library/library.c +++ b/winpr/libwinpr/library/library.c @@ -21,6 +21,9 @@ #include "config.h" #endif +#include +#include + #include /** @@ -62,7 +65,10 @@ #include #include +#include #include +#include +#include DLL_DIRECTORY_COOKIE AddDllDirectory(PCWSTR NewDirectory) { @@ -146,5 +152,58 @@ BOOL FreeLibrary(HMODULE hLibModule) return TRUE; } +/** + * GetModuleFileName: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms683197/ + * + * Finding current executable's path without /proc/self/exe: + * http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe + */ + +DWORD GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize) +{ + return 0; +} + +DWORD GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) +{ +#ifdef __linux__ + int status; + int length; + char path[64]; + + if (!hModule) + { + char buffer[4096]; + + sprintf(path, "/proc/%d/exe", getpid()); + + status = readlink(path, buffer, sizeof(buffer)); + + if (status < 0) + return 0; + + buffer[status] = '\0'; + + length = strlen(buffer); + + if (length < nSize) + { + CopyMemory(lpFilename, buffer, length); + lpFilename[length] = '\0'; + } + else + { + CopyMemory(lpFilename, buffer, nSize - 1); + lpFilename[nSize - 1] = '\0'; + } + + return 0; + } +#endif + + return 0; +} + #endif diff --git a/winpr/libwinpr/library/test/CMakeLists.txt b/winpr/libwinpr/library/test/CMakeLists.txt index 9d85509f8..f49956c98 100644 --- a/winpr/libwinpr/library/test/CMakeLists.txt +++ b/winpr/libwinpr/library/test/CMakeLists.txt @@ -10,7 +10,8 @@ set(${MODULE_PREFIX}_TESTS TestLibrarySetDefaultDllDirectories.c TestLibraryLoadLibrary.c TestLibraryFreeLibrary.c - TestLibraryGetProcAddress.c) + TestLibraryGetProcAddress.c + TestLibraryGetModuleFileName.c) create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} diff --git a/winpr/libwinpr/library/test/TestLibraryGetModuleFileName.c b/winpr/libwinpr/library/test/TestLibraryGetModuleFileName.c new file mode 100644 index 000000000..df9fa16c1 --- /dev/null +++ b/winpr/libwinpr/library/test/TestLibraryGetModuleFileName.c @@ -0,0 +1,18 @@ + +#include +#include +#include +#include +#include +#include + +int TestLibraryGetModuleFileName(int argc, char* argv[]) +{ + char ModuleFileName[4096]; + + GetModuleFileNameA(NULL, ModuleFileName, sizeof(ModuleFileName)); + + printf("GetModuleFileNameA: %s\n", ModuleFileName); + + return 0; +} From 61f95fbe16c19b9f0d10411f4b9d9c7af88a1cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 7 Nov 2013 15:14:59 -0500 Subject: [PATCH 014/149] libfreerdp-core: transport code style cleanup --- libfreerdp/core/tcp.c | 2 +- libfreerdp/core/transport.c | 72 +++++++++---------- libfreerdp/core/transport.h | 4 +- libfreerdp/utils/tcp.c | 15 ++-- winpr/libwinpr/utils/collections/StreamPool.c | 2 +- 5 files changed, 50 insertions(+), 45 deletions(-) diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c index 34cf4cc35..f57af4f52 100644 --- a/libfreerdp/core/tcp.c +++ b/libfreerdp/core/tcp.c @@ -89,7 +89,7 @@ void tcp_get_ip_address(rdpTcp * tcp) tcp->settings->ClientAddress = _strdup(tcp->ip_address); } -void tcp_get_mac_address(rdpTcp * tcp) +void tcp_get_mac_address(rdpTcp* tcp) { #ifdef LINUX BYTE* mac; diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 70ff6c069..020f2117b 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -77,7 +77,7 @@ BOOL transport_disconnect(rdpTransport* transport) if (transport->layer == TRANSPORT_LAYER_TLS) status &= tls_disconnect(transport->TlsIn); - if (transport->layer == TRANSPORT_LAYER_TSG || transport->layer == TRANSPORT_LAYER_TSG_TLS) + if ((transport->layer == TRANSPORT_LAYER_TSG) || (transport->layer == TRANSPORT_LAYER_TSG_TLS)) { tsg_disconnect(transport->tsg); } @@ -276,7 +276,7 @@ BOOL transport_connect_nla(rdpTransport* transport) settings = transport->settings; instance = (freerdp*) settings->instance; - if (transport->credssp == NULL) + if (!transport->credssp) transport->credssp = credssp_new(instance, transport, settings); if (credssp_authenticate(transport->credssp) < 0) @@ -305,12 +305,12 @@ BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 transport->tsg = tsg; transport->SplitInputOutput = TRUE; - if (transport->TlsIn == NULL) + if (!transport->TlsIn) transport->TlsIn = tls_new(transport->settings); transport->TlsIn->sockfd = transport->TcpIn->sockfd; - if (transport->TlsOut == NULL) + if (!transport->TlsOut) transport->TlsOut = tls_new(transport->settings); transport->TlsOut->sockfd = transport->TcpOut->sockfd; @@ -378,10 +378,10 @@ BOOL transport_accept_rdp(rdpTransport* transport) BOOL transport_accept_tls(rdpTransport* transport) { - if (transport->TlsIn == NULL) + if (!transport->TlsIn) transport->TlsIn = tls_new(transport->settings); - if (transport->TlsOut == NULL) + if (!transport->TlsOut) transport->TlsOut = transport->TlsIn; transport->layer = TRANSPORT_LAYER_TLS; @@ -398,10 +398,10 @@ BOOL transport_accept_nla(rdpTransport* transport) freerdp* instance; rdpSettings* settings; - if (transport->TlsIn == NULL) + if (!transport->TlsIn) transport->TlsIn = tls_new(transport->settings); - if (transport->TlsOut == NULL) + if (!transport->TlsOut) transport->TlsOut = transport->TlsIn; transport->layer = TRANSPORT_LAYER_TLS; @@ -436,7 +436,7 @@ BOOL transport_accept_nla(rdpTransport* transport) BOOL nla_verify_header(wStream* s) { - if ((s->pointer[0] == 0x30) && (s->pointer[1] & 0x80)) + if ((Stream_Pointer(s)[0] == 0x30) && (Stream_Pointer(s)[1] & 0x80)) return TRUE; return FALSE; @@ -446,17 +446,17 @@ UINT32 nla_read_header(wStream* s) { UINT32 length = 0; - if (s->pointer[1] & 0x80) + if (Stream_Pointer(s)[1] & 0x80) { - if ((s->pointer[1] & ~(0x80)) == 1) + if ((Stream_Pointer(s)[1] & ~(0x80)) == 1) { - length = s->pointer[2]; + length = Stream_Pointer(s)[2]; length += 3; Stream_Seek(s, 3); } - else if ((s->pointer[1] & ~(0x80)) == 2) + else if ((Stream_Pointer(s)[1] & ~(0x80)) == 2) { - length = (s->pointer[2] << 8) | s->pointer[3]; + length = (Stream_Pointer(s)[2] << 8) | Stream_Pointer(s)[3]; length += 4; Stream_Seek(s, 4); } @@ -467,7 +467,7 @@ UINT32 nla_read_header(wStream* s) } else { - length = s->pointer[1]; + length = Stream_Pointer(s)[1]; length += 2; Stream_Seek(s, 2); } @@ -479,11 +479,11 @@ UINT32 nla_header_length(wStream* s) { UINT32 length = 0; - if (s->pointer[1] & 0x80) + if (Stream_Pointer(s)[1] & 0x80) { - if ((s->pointer[1] & ~(0x80)) == 1) + if ((Stream_Pointer(s)[1] & ~(0x80)) == 1) length = 3; - else if ((s->pointer[1] & ~(0x80)) == 2) + else if ((Stream_Pointer(s)[1] & ~(0x80)) == 2) length = 4; else fprintf(stderr, "Error reading TSRequest!\n"); @@ -571,26 +571,26 @@ int transport_read(rdpTransport* transport, wStream* s) } /* if header is present, read in exactly one PDU */ - if (s->buffer[0] == 0x03) + if (Stream_Buffer(s)[0] == 0x03) { /* TPKT header */ - pduLength = (s->buffer[2] << 8) | s->buffer[3]; + pduLength = (Stream_Buffer(s)[2] << 8) | Stream_Buffer(s)[3]; } - else if (s->buffer[0] == 0x30) + else if (Stream_Buffer(s)[0] == 0x30) { /* TSRequest (NLA) */ - if (s->buffer[1] & 0x80) + if (Stream_Buffer(s)[1] & 0x80) { - if ((s->buffer[1] & ~(0x80)) == 1) + if ((Stream_Buffer(s)[1] & ~(0x80)) == 1) { - pduLength = s->buffer[2]; + pduLength = Stream_Buffer(s)[2]; pduLength += 3; } - else if ((s->buffer[1] & ~(0x80)) == 2) + else if ((Stream_Buffer(s)[1] & ~(0x80)) == 2) { - pduLength = (s->buffer[2] << 8) | s->buffer[3]; + pduLength = (Stream_Buffer(s)[2] << 8) | Stream_Buffer(s)[3]; pduLength += 4; } else @@ -600,7 +600,7 @@ int transport_read(rdpTransport* transport, wStream* s) } else { - pduLength = s->buffer[1]; + pduLength = Stream_Buffer(s)[1]; pduLength += 2; } } @@ -608,10 +608,10 @@ int transport_read(rdpTransport* transport, wStream* s) { /* Fast-Path Header */ - if (s->buffer[1] & 0x80) - pduLength = ((s->buffer[1] & 0x7F) << 8) | s->buffer[2]; + if (Stream_Buffer(s)[1] & 0x80) + pduLength = ((Stream_Buffer(s)[1] & 0x7F) << 8) | Stream_Buffer(s)[2]; else - pduLength = s->buffer[1]; + pduLength = Stream_Buffer(s)[1]; } status = transport_read_layer(transport, Stream_Buffer(s) + streamPosition, pduLength - streamPosition); @@ -657,7 +657,7 @@ int transport_write(rdpTransport* transport, wStream* s) int length; int status = -1; - WaitForSingleObject(transport->WriteMutex, INFINITE); + EnterCriticalSection(&(transport->WriteLock)); length = Stream_GetPosition(s); Stream_SetPosition(s, 0); @@ -722,7 +722,7 @@ int transport_write(rdpTransport* transport, wStream* s) if (s->pool) Stream_Release(s); - ReleaseMutex(transport->WriteMutex); + LeaveCriticalSection(&(transport->WriteLock)); return status; } @@ -1038,8 +1038,8 @@ rdpTransport* transport_new(rdpSettings* settings) transport->blocking = TRUE; - transport->ReadMutex = CreateMutex(NULL, FALSE, NULL); - transport->WriteMutex = CreateMutex(NULL, FALSE, NULL); + InitializeCriticalSectionAndSpinCount(&(transport->ReadLock), 4000); + InitializeCriticalSectionAndSpinCount(&(transport->WriteLock), 4000); transport->layer = TRANSPORT_LAYER_TCP; } @@ -1080,8 +1080,8 @@ void transport_free(rdpTransport* transport) tsg_free(transport->tsg); transport->tsg = NULL; - CloseHandle(transport->ReadMutex); - CloseHandle(transport->WriteMutex); + DeleteCriticalSection(&(transport->ReadLock)); + DeleteCriticalSection(&(transport->WriteLock)); free(transport); } diff --git a/libfreerdp/core/transport.h b/libfreerdp/core/transport.h index 5d9e70302..1525b19ee 100644 --- a/libfreerdp/core/transport.h +++ b/libfreerdp/core/transport.h @@ -75,8 +75,8 @@ struct rdp_transport HANDLE stopEvent; HANDLE thread; BOOL async; - HANDLE ReadMutex; - HANDLE WriteMutex; + CRITICAL_SECTION ReadLock; + CRITICAL_SECTION WriteLock; wLog* log; }; diff --git a/libfreerdp/utils/tcp.c b/libfreerdp/utils/tcp.c index 9adad8f59..0f938729e 100644 --- a/libfreerdp/utils/tcp.c +++ b/libfreerdp/utils/tcp.c @@ -71,16 +71,21 @@ int freerdp_tcp_connect(const char* hostname, int port) { int status; int sockfd; - char servname[10]; - struct addrinfo* ai; - struct addrinfo* res; - struct addrinfo hints = { 0 }; + char servname[32]; + struct addrinfo hints; + struct addrinfo* ai = NULL; + struct addrinfo* res = NULL; ZeroMemory(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = 0; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; - sprintf_s(servname, sizeof(servname), "%d", port); + sprintf_s(servname, 32, "%d", port); status = getaddrinfo(hostname, servname, &hints, &res); if (status != 0) diff --git a/winpr/libwinpr/utils/collections/StreamPool.c b/winpr/libwinpr/utils/collections/StreamPool.c index dab11c0e2..1804e1023 100644 --- a/winpr/libwinpr/utils/collections/StreamPool.c +++ b/winpr/libwinpr/utils/collections/StreamPool.c @@ -145,8 +145,8 @@ wStream* StreamPool_Take(wStreamPool* pool, size_t size) { StreamPool_ShiftAvailable(pool, foundIndex, -1); + Stream_SetPosition(s, 0); Stream_EnsureCapacity(s, size); - Stream_Pointer(s) = Stream_Buffer(s); } s->pool = pool; From 5536033a8ae6cdf5e8e99a393c465beb3eb132e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 7 Nov 2013 17:37:58 -0500 Subject: [PATCH 015/149] libfreerdp-core: transport refactoring --- docs/valgrind.supp | 21 +++++ libfreerdp/core/nego.c | 1 + libfreerdp/core/tcp.c | 16 ++-- libfreerdp/core/tcp.h | 4 +- libfreerdp/core/transport.c | 86 ++++++++++--------- winpr/libwinpr/utils/collections/StreamPool.c | 20 +++-- 6 files changed, 90 insertions(+), 58 deletions(-) create mode 100644 docs/valgrind.supp diff --git a/docs/valgrind.supp b/docs/valgrind.supp new file mode 100644 index 000000000..ba4f8aa61 --- /dev/null +++ b/docs/valgrind.supp @@ -0,0 +1,21 @@ + +{ + glibc_getaddrinfo + Memcheck:Param + sendmsg(mmsg[0].msg_hdr) + fun:sendmmsg + fun:__libc_res_nsend + fun:__libc_res_nquery + fun:__libc_res_nsearch + fun:_nss_dns_gethostbyname4_r + fun:gaih_inet + fun:getaddrinfo + fun:freerdp_tcp_connect + fun:tcp_connect + fun:transport_connect + fun:nego_tcp_connect + fun:nego_transport_connect +} + + + diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index 9c4f91327..a35f43e9a 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -491,6 +491,7 @@ BOOL nego_recv_response(rdpNego* nego) status = nego_recv(nego->transport, s, nego); Stream_Free(s, TRUE); + if (status < 0) return FALSE; diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c index f57af4f52..24e0de1bc 100644 --- a/libfreerdp/core/tcp.c +++ b/libfreerdp/core/tcp.c @@ -57,13 +57,14 @@ #include "tcp.h" -void tcp_get_ip_address(rdpTcp * tcp) +void tcp_get_ip_address(rdpTcp* tcp) { BYTE* ip; socklen_t length; struct sockaddr_in sockaddr; length = sizeof(sockaddr); + ZeroMemory(&sockaddr, length); if (getsockname(tcp->sockfd, (struct sockaddr*) &sockaddr, &length) == 0) { @@ -73,19 +74,12 @@ void tcp_get_ip_address(rdpTcp * tcp) } else { - strncpy(tcp->ip_address, "127.0.0.1", sizeof(tcp->ip_address)); + strcpy(tcp->ip_address, "127.0.0.1"); } - tcp->ip_address[sizeof(tcp->ip_address) - 1] = 0; - tcp->settings->IPv6Enabled = 0; - if (tcp->settings->ClientAddress) - { - free(tcp->settings->ClientAddress); - tcp->settings->ClientAddress = NULL; - } - + free(tcp->settings->ClientAddress); tcp->settings->ClientAddress = _strdup(tcp->ip_address); } @@ -122,7 +116,7 @@ void tcp_get_mac_address(rdpTcp* tcp) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); */ } -BOOL tcp_connect(rdpTcp* tcp, const char* hostname, UINT16 port) +BOOL tcp_connect(rdpTcp* tcp, const char* hostname, int port) { UINT32 option_value; socklen_t option_len; diff --git a/libfreerdp/core/tcp.h b/libfreerdp/core/tcp.h index c22e5da05..3ae94da34 100644 --- a/libfreerdp/core/tcp.h +++ b/libfreerdp/core/tcp.h @@ -41,14 +41,14 @@ struct rdp_tcp int sockfd; char ip_address[32]; BYTE mac_address[6]; - struct rdp_settings* settings; + rdpSettings* settings; #ifdef _WIN32 WSAEVENT wsa_event; #endif HANDLE event; }; -BOOL tcp_connect(rdpTcp* tcp, const char* hostname, UINT16 port); +BOOL tcp_connect(rdpTcp* tcp, const char* hostname, int port); BOOL tcp_disconnect(rdpTcp* tcp); int tcp_read(rdpTcp* tcp, BYTE* data, int length); int tcp_write(rdpTcp* tcp, BYTE* data, int length); diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 020f2117b..d95d78f57 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -219,7 +219,7 @@ BOOL transport_connect_tls(rdpTransport* transport) transport->layer = TRANSPORT_LAYER_TSG_TLS; - if (tls_connect(transport->TsgTls) != TRUE) + if (!tls_connect(transport->TsgTls)) { if (!connectErrorCode) connectErrorCode = TLSCONNECTERROR; @@ -233,16 +233,16 @@ BOOL transport_connect_tls(rdpTransport* transport) return TRUE; } - if (transport->TlsIn == NULL) + if (!transport->TlsIn) transport->TlsIn = tls_new(transport->settings); - if (transport->TlsOut == NULL) + if (!transport->TlsOut) transport->TlsOut = transport->TlsIn; transport->layer = TRANSPORT_LAYER_TLS; transport->TlsIn->sockfd = transport->TcpIn->sockfd; - if (tls_connect(transport->TlsIn) != TRUE) + if (!tls_connect(transport->TlsIn)) { if (!connectErrorCode) connectErrorCode = TLSCONNECTERROR; @@ -265,17 +265,17 @@ BOOL transport_connect_nla(rdpTransport* transport) freerdp* instance; rdpSettings* settings; + settings = transport->settings; + instance = (freerdp*) settings->instance; + if (!transport_connect_tls(transport)) return FALSE; /* Network Level Authentication */ - if (transport->settings->Authentication != TRUE) + if (!settings->Authentication) return TRUE; - settings = transport->settings; - instance = (freerdp*) settings->instance; - if (!transport->credssp) transport->credssp = credssp_new(instance, transport, settings); @@ -293,6 +293,7 @@ BOOL transport_connect_nla(rdpTransport* transport) } credssp_free(transport->credssp); + transport->credssp = NULL; return TRUE; } @@ -315,10 +316,10 @@ BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 transport->TlsOut->sockfd = transport->TcpOut->sockfd; - if (tls_connect(transport->TlsIn) != TRUE) + if (!tls_connect(transport->TlsIn)) return FALSE; - if (tls_connect(transport->TlsOut) != TRUE) + if (!tls_connect(transport->TlsOut)) return FALSE; if (!tsg_connect(tsg, hostname, port)) @@ -387,7 +388,7 @@ BOOL transport_accept_tls(rdpTransport* transport) transport->layer = TRANSPORT_LAYER_TLS; transport->TlsIn->sockfd = transport->TcpIn->sockfd; - if (tls_accept(transport->TlsIn, transport->settings->CertificateFile, transport->settings->PrivateKeyFile) != TRUE) + if (!tls_accept(transport->TlsIn, transport->settings->CertificateFile, transport->settings->PrivateKeyFile)) return FALSE; return TRUE; @@ -398,6 +399,9 @@ BOOL transport_accept_nla(rdpTransport* transport) freerdp* instance; rdpSettings* settings; + settings = transport->settings; + instance = (freerdp*) settings->instance; + if (!transport->TlsIn) transport->TlsIn = tls_new(transport->settings); @@ -407,18 +411,15 @@ BOOL transport_accept_nla(rdpTransport* transport) transport->layer = TRANSPORT_LAYER_TLS; transport->TlsIn->sockfd = transport->TcpIn->sockfd; - if (tls_accept(transport->TlsIn, transport->settings->CertificateFile, transport->settings->PrivateKeyFile) != TRUE) + if (!tls_accept(transport->TlsIn, transport->settings->CertificateFile, transport->settings->PrivateKeyFile)) return FALSE; /* Network Level Authentication */ - if (transport->settings->Authentication != TRUE) + if (!settings->Authentication) return TRUE; - settings = transport->settings; - instance = (freerdp*) settings->instance; - - if (transport->credssp == NULL) + if (!transport->credssp) transport->credssp = credssp_new(instance, transport, settings); if (credssp_authenticate(transport->credssp) < 0) @@ -496,7 +497,7 @@ UINT32 nla_header_length(wStream* s) return length; } -int transport_read_layer(rdpTransport* transport, UINT8* data, int bytes) +int transport_read_layer(rdpTransport* transport, BYTE* data, int bytes) { int read = 0; int status = -1; @@ -539,10 +540,12 @@ int transport_read_layer(rdpTransport* transport, UINT8* data, int bytes) int transport_read(rdpTransport* transport, wStream* s) { int status; + int position; int pduLength; - int streamPosition; + BYTE header[4]; int transport_status; + position = 0; pduLength = 0; transport_status = 0; @@ -553,54 +556,57 @@ int transport_read(rdpTransport* transport, wStream* s) return -1; /* first check if we have header */ - streamPosition = Stream_GetPosition(s); + position = Stream_GetPosition(s); - if (streamPosition < 4) + if (position < 4) { - status = transport_read_layer(transport, Stream_Buffer(s) + streamPosition, 4 - streamPosition); + status = transport_read_layer(transport, Stream_Buffer(s) + position, 4 - position); if (status < 0) return status; transport_status += status; - if ((status + streamPosition) < 4) + if ((status + position) < 4) return transport_status; - streamPosition += status; + position += status; } + Stream_Peek(s, header, 4); /* peek at first 4 bytes */ + /* if header is present, read in exactly one PDU */ - if (Stream_Buffer(s)[0] == 0x03) + if (header[0] == 0x03) { /* TPKT header */ - pduLength = (Stream_Buffer(s)[2] << 8) | Stream_Buffer(s)[3]; + pduLength = (header[2] << 8) | header[3]; } - else if (Stream_Buffer(s)[0] == 0x30) + else if (header[0] == 0x30) { /* TSRequest (NLA) */ - if (Stream_Buffer(s)[1] & 0x80) + if (header[1] & 0x80) { - if ((Stream_Buffer(s)[1] & ~(0x80)) == 1) + if ((header[1] & ~(0x80)) == 1) { - pduLength = Stream_Buffer(s)[2]; + pduLength = header[2]; pduLength += 3; } - else if ((Stream_Buffer(s)[1] & ~(0x80)) == 2) + else if ((header[1] & ~(0x80)) == 2) { - pduLength = (Stream_Buffer(s)[2] << 8) | Stream_Buffer(s)[3]; + pduLength = (header[2] << 8) | header[3]; pduLength += 4; } else { fprintf(stderr, "Error reading TSRequest!\n"); + return -1; } } else { - pduLength = Stream_Buffer(s)[1]; + pduLength = header[1]; pduLength += 2; } } @@ -608,13 +614,13 @@ int transport_read(rdpTransport* transport, wStream* s) { /* Fast-Path Header */ - if (Stream_Buffer(s)[1] & 0x80) - pduLength = ((Stream_Buffer(s)[1] & 0x7F) << 8) | Stream_Buffer(s)[2]; + if (header[1] & 0x80) + pduLength = ((header[1] & 0x7F) << 8) | header[2]; else - pduLength = Stream_Buffer(s)[1]; + pduLength = header[1]; } - status = transport_read_layer(transport, Stream_Buffer(s) + streamPosition, pduLength - streamPosition); + status = transport_read_layer(transport, Stream_Buffer(s) + position, pduLength - position); if (status < 0) return status; @@ -623,14 +629,14 @@ int transport_read(rdpTransport* transport, wStream* s) #ifdef WITH_DEBUG_TRANSPORT /* dump when whole PDU is read */ - if (streamPosition + status >= pduLength) + if (position + status >= pduLength) { fprintf(stderr, "Local < Remote\n"); winpr_HexDump(Stream_Buffer(s), pduLength); } #endif - if (streamPosition + status >= pduLength) + if (position + status >= pduLength) { WLog_Packet(transport->log, WLOG_TRACE, Stream_Buffer(s), pduLength, WLOG_PACKET_INBOUND); } @@ -799,7 +805,7 @@ int transport_check_fds(rdpTransport* transport) { int pos; int status; - UINT16 length; + int length; int recv_status; wStream* received; diff --git a/winpr/libwinpr/utils/collections/StreamPool.c b/winpr/libwinpr/utils/collections/StreamPool.c index 1804e1023..8bb40f373 100644 --- a/winpr/libwinpr/utils/collections/StreamPool.c +++ b/winpr/libwinpr/utils/collections/StreamPool.c @@ -45,7 +45,11 @@ void StreamPool_ShiftUsed(wStreamPool* pool, int index, int count) else if (count < 0) { if (pool->uSize - index + count > 0) - MoveMemory(&pool->uArray[index], &pool->uArray[index - count], (pool->uSize - index + count) * sizeof(wStream*)); + { + MoveMemory(&pool->uArray[index], &pool->uArray[index - count], + (pool->uSize - index + count) * sizeof(wStream*)); + } + pool->uSize += count; } } @@ -103,7 +107,11 @@ void StreamPool_ShiftAvailable(wStreamPool* pool, int index, int count) else if (count < 0) { if (pool->aSize - index + count > 0) - MoveMemory(&pool->aArray[index], &pool->aArray[index - count], (pool->aSize - index + count) * sizeof(wStream*)); + { + MoveMemory(&pool->aArray[index], &pool->aArray[index - count], + (pool->aSize - index + count) * sizeof(wStream*)); + } + pool->aSize += count; } } @@ -117,7 +125,6 @@ wStream* StreamPool_Take(wStreamPool* pool, size_t size) int index; int foundIndex; wStream* s = NULL; - BOOL found = FALSE; if (pool->synchronized) EnterCriticalSection(&pool->lock); @@ -125,6 +132,8 @@ wStream* StreamPool_Take(wStreamPool* pool, size_t size) if (size == 0) size = pool->defaultSize; + foundIndex = -1; + for (index = 0; index < pool->aSize; index++) { s = pool->aArray[index]; @@ -132,12 +141,11 @@ wStream* StreamPool_Take(wStreamPool* pool, size_t size) if (Stream_Capacity(s) >= size) { foundIndex = index; - found = TRUE; break; } } - if (!found) + if (foundIndex < 0) { s = Stream_New(NULL, size); } @@ -330,10 +338,12 @@ wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize) pool->aSize = 0; pool->aCapacity = 32; pool->aArray = (wStream**) malloc(sizeof(wStream*) * pool->aCapacity); + ZeroMemory(pool->aArray, sizeof(wStream*) * pool->aCapacity); pool->uSize = 0; pool->uCapacity = 32; pool->uArray = (wStream**) malloc(sizeof(wStream*) * pool->uCapacity); + ZeroMemory(pool->uArray, sizeof(wStream*) * pool->uCapacity); } return pool; From 639f6cee696c0bc9e3b146eb10ef69979483291a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 7 Nov 2013 23:12:31 -0500 Subject: [PATCH 016/149] docs: improve valgrind suppressions file --- docs/valgrind.supp | 64 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/docs/valgrind.supp b/docs/valgrind.supp index ba4f8aa61..440d92c6b 100644 --- a/docs/valgrind.supp +++ b/docs/valgrind.supp @@ -1,6 +1,6 @@ { - glibc_getaddrinfo + ignore glibc getaddrinfo Memcheck:Param sendmsg(mmsg[0].msg_hdr) fun:sendmmsg @@ -17,5 +17,67 @@ fun:nego_transport_connect } +{ + ignore openssl malloc + Memcheck:Leak + fun:malloc + fun:CRYPTO_malloc + ... + obj:*libcrypto* +} + +{ + ignore openssl realloc + Memcheck:Leak + fun:realloc + fun:CRYPTO_realloc + ... + obj:*libcrypto* +} +{ + ignore libssl cond + Memcheck:Cond + obj:*libssl* +} +{ + ignore libssl value + Memcheck:Value4 + obj:*libssl* +} + +{ + ignore ssl3_read_bytes tls1_enc + Memcheck:Cond + fun:tls1_enc + fun:ssl3_read_bytes + obj:*libssl* +} + +{ + ignore ssl3_read_bytes memcpy + Memcheck:Cond + fun:memcpy@@GLIBC_2.14 + fun:ssl3_read_bytes + obj:*libssl* +} + +{ + ignore ssl3_read_bytes value8 + Memcheck:Value8 + fun:memcpy@@GLIBC_2.14 + fun:ssl3_read_bytes + obj:*libssl* +} + +{ + ignore write buf BIO_write + Memcheck:Param + write(buf) + obj:*libpthread* + obj:*libcrypto* + fun:BIO_write + fun:ssl3_write_pending + fun:ssl3_write_bytes +} From 70aed3fa855dc39afc83da26a9a482eedaf7177c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 8 Nov 2013 13:57:41 -0500 Subject: [PATCH 017/149] libfreerdp-core: minor style cleanup --- libfreerdp/core/tpdu.c | 8 ++++---- libfreerdp/core/tpdu.h | 8 ++++---- libfreerdp/core/transport.c | 2 +- winpr/libwinpr/utils/stream.c | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libfreerdp/core/tpdu.c b/libfreerdp/core/tpdu.c index 30a649c22..e9b3ba35b 100644 --- a/libfreerdp/core/tpdu.c +++ b/libfreerdp/core/tpdu.c @@ -121,11 +121,11 @@ void tpdu_write_header(wStream* s, UINT16 length, BYTE code) * @return length indicator (LI) */ -BOOL tpdu_read_connection_request(wStream* s, BYTE *li) +BOOL tpdu_read_connection_request(wStream* s, BYTE* li) { BYTE code; - if(!tpdu_read_header(s, &code, li)) + if (!tpdu_read_header(s, &code, li)) return FALSE; if (code != X224_TPDU_CONNECTION_REQUEST) @@ -154,11 +154,11 @@ void tpdu_write_connection_request(wStream* s, UINT16 length) * @return length indicator (LI) */ -BOOL tpdu_read_connection_confirm(wStream* s, BYTE *li) +BOOL tpdu_read_connection_confirm(wStream* s, BYTE* li) { BYTE code; - if(!tpdu_read_header(s, &code, li)) + if (!tpdu_read_header(s, &code, li)) return FALSE; if (code != X224_TPDU_CONNECTION_CONFIRM) diff --git a/libfreerdp/core/tpdu.h b/libfreerdp/core/tpdu.h index dbd0fe53d..87799f206 100644 --- a/libfreerdp/core/tpdu.h +++ b/libfreerdp/core/tpdu.h @@ -41,14 +41,14 @@ enum X224_TPDU_TYPE #define TPDU_CONNECTION_CONFIRM_LENGTH (TPKT_HEADER_LENGTH + TPDU_CONNECTION_CONFIRM_HEADER_LENGTH) #define TPDU_DISCONNECT_REQUEST_LENGTH (TPKT_HEADER_LENGTH + TPDU_DISCONNECT_REQUEST_HEADER_LENGTH) -BOOL tpdu_read_header(wStream* s, BYTE* code, BYTE *li); +BOOL tpdu_read_header(wStream* s, BYTE* code, BYTE* li); void tpdu_write_header(wStream* s, UINT16 length, BYTE code); -BOOL tpdu_read_connection_request(wStream* s, BYTE *li); +BOOL tpdu_read_connection_request(wStream* s, BYTE* li); void tpdu_write_connection_request(wStream* s, UINT16 length); -BOOL tpdu_read_connection_confirm(wStream* s, BYTE *li); +BOOL tpdu_read_connection_confirm(wStream* s, BYTE* li); void tpdu_write_connection_confirm(wStream* s, UINT16 length); void tpdu_write_disconnect_request(wStream* s, UINT16 length); -BOOL tpdu_read_data(wStream* s, UINT16 *li); +BOOL tpdu_read_data(wStream* s, UINT16* li); void tpdu_write_data(wStream* s); #endif /* __TPDU_H */ diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index d95d78f57..fd7b1b2dc 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -573,7 +573,7 @@ int transport_read(rdpTransport* transport, wStream* s) position += status; } - Stream_Peek(s, header, 4); /* peek at first 4 bytes */ + CopyMemory(header, Stream_Buffer(s), 4); /* peek at first 4 bytes */ /* if header is present, read in exactly one PDU */ if (header[0] == 0x03) diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c index cae3b401e..bce25f541 100644 --- a/winpr/libwinpr/utils/stream.c +++ b/winpr/libwinpr/utils/stream.c @@ -67,7 +67,7 @@ wStream* Stream_New(BYTE* buffer, size_t size) s = malloc(sizeof(wStream)); - if (s != NULL) + if (s) { if (buffer) s->buffer = buffer; @@ -87,11 +87,11 @@ wStream* Stream_New(BYTE* buffer, size_t size) void Stream_Free(wStream* s, BOOL bFreeBuffer) { - if (s != NULL) + if (s) { if (bFreeBuffer) { - if (s->buffer != NULL) + if (s->buffer) free(s->buffer); } From 0e662a060e3deb1066e4306dfb606f08ee937e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 9 Nov 2013 17:51:09 -0500 Subject: [PATCH 018/149] freerdp: improve pkgconfig .pc file generation --- CMakeLists.txt | 6 ------ libfreerdp/CMakeLists.txt | 4 ++++ freerdp.pc.in => libfreerdp/freerdp.pc.in | 6 +++--- winpr/libwinpr/CMakeLists.txt | 3 +++ winpr/libwinpr/winpr.pc | 12 ++++++++++++ winpr/libwinpr/winpr.pc.in | 12 ++++++++++++ 6 files changed, 34 insertions(+), 9 deletions(-) rename freerdp.pc.in => libfreerdp/freerdp.pc.in (51%) create mode 100644 winpr/libwinpr/winpr.pc create mode 100644 winpr/libwinpr/winpr.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e05052be..887435cf1 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -508,12 +508,6 @@ include_directories("${CMAKE_BINARY_DIR}/winpr/include") add_subdirectory(winpr) -# Generate pkg-config -if(NOT MSVC) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/freerdp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc @ONLY) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -endif() - if(WITH_CUNIT) message(FATAL_ERROR "cunit (WITH_CUNIT) is deprecated please use BUILD_TESTING to build ctest tests. The cunit directory contains the old tests and is kept until all tests are converted.") diff --git a/libfreerdp/CMakeLists.txt b/libfreerdp/CMakeLists.txt index f2063e8ea..cea42c1c5 100644 --- a/libfreerdp/CMakeLists.txt +++ b/libfreerdp/CMakeLists.txt @@ -65,3 +65,7 @@ if(MONOLITHIC_BUILD) set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/libfreerdp") endif() +set(FREERDP_PC_LIBS "-lfreerdp -lwinpr") + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/freerdp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/freerdp.pc.in b/libfreerdp/freerdp.pc.in similarity index 51% rename from freerdp.pc.in rename to libfreerdp/freerdp.pc.in index 59e2b446c..3561100d8 100644 --- a/freerdp.pc.in +++ b/libfreerdp/freerdp.pc.in @@ -2,12 +2,12 @@ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_PREFIX@/include +libs=@FREERDP_PC_LIBS@ Name: FreeRDP -Description: A free remote desktop protocol client +Description: FreeRDP: A Remote Desktop Protocol Implementation URL: http://www.freerdp.com/ Version: @FREERDP_VERSION_FULL@ Requires: -Libs: -L${libdir} -lfreerdp-cache -lfreerdp-codec -lfreerdp-core -lfreerdp-crypto -lfreerdp-gdi -lfreerdp-locale -lfreerdp-rail -lfreerdp-utils -lwinpr-sspi -lwinpr-rpc -lwinpr-utils +Libs: -L${libdir} ${libs} Cflags: -I${includedir} - diff --git a/winpr/libwinpr/CMakeLists.txt b/winpr/libwinpr/CMakeLists.txt index 5915ad00a..f35bcb1ec 100644 --- a/winpr/libwinpr/CMakeLists.txt +++ b/winpr/libwinpr/CMakeLists.txt @@ -56,6 +56,9 @@ if(MONOLITHIC_BUILD) target_link_libraries(${MODULE_NAME} ${WINPR_LIBS}) install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries EXPORT WinPRTargets) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/winpr.pc.in ${CMAKE_CURRENT_BINARY_DIR}/winpr.pc @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/winpr.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/libwinpr") endif() diff --git a/winpr/libwinpr/winpr.pc b/winpr/libwinpr/winpr.pc new file mode 100644 index 000000000..5950cce30 --- /dev/null +++ b/winpr/libwinpr/winpr.pc @@ -0,0 +1,12 @@ +prefix=/opt/freerds +exec_prefix=/opt/freerds +libdir=/opt/freerds/lib64 +includedir=/opt/freerds/include + +Name: WinPR +Description: WinPR: Windows Portable Runtime +URL: http://www.freerdp.com/ +Version: 1.1.0 +Requires: +Libs: -L${libdir} -lwinpr +Cflags: -I${includedir} diff --git a/winpr/libwinpr/winpr.pc.in b/winpr/libwinpr/winpr.pc.in new file mode 100644 index 000000000..a593ede7b --- /dev/null +++ b/winpr/libwinpr/winpr.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_PREFIX@/include + +Name: WinPR +Description: WinPR: Windows Portable Runtime +URL: http://www.freerdp.com/ +Version: @WINPR_VERSION_FULL@ +Requires: +Libs: -L${libdir} -lwinpr +Cflags: -I${includedir} From 4898868e9daffd597545843d4e65dd11cdeb7a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 9 Nov 2013 23:38:22 -0500 Subject: [PATCH 019/149] libwinpr-path: extend custom portability functions --- winpr/include/winpr/path.h | 7 ++++++- winpr/libwinpr/path/path.c | 38 +++++++++++++++++++++++++++++++++++++ winpr/libwinpr/path/shell.c | 2 +- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/winpr/include/winpr/path.h b/winpr/include/winpr/path.h index 9c3ec4c01..811cfdb07 100644 --- a/winpr/include/winpr/path.h +++ b/winpr/include/winpr/path.h @@ -242,14 +242,19 @@ WINPR_API HRESULT NativePathAllocCombineW(PCWSTR pszPathIn, PCWSTR pszMore, unsi WINPR_API HRESULT PathCchConvertStyleA(PSTR pszPath, size_t cchPath, unsigned long dwFlags); WINPR_API HRESULT PathCchConvertStyleW(PWSTR pszPath, size_t cchPath, unsigned long dwFlags); +WINPR_API char PathGetSeparatorA(unsigned long dwFlags); +WINPR_API WCHAR PathGetSeparatorW(unsigned long dwFlags); + WINPR_API PCSTR PathGetSharedLibraryExtensionA(unsigned long dwFlags); WINPR_API PCWSTR PathGetSharedLibraryExtensionW(unsigned long dwFlags); #ifdef UNICODE #define PathCchConvertStyle PathCchConvertStyleW +#define PathGetSeparator PathGetSeparatorW #define PathGetSharedLibraryExtension PathGetSharedLibraryExtensionW #else #define PathCchConvertStyle PathCchConvertStyleA +#define PathGetSeparator PathGetSeparatorW #define PathGetSharedLibraryExtension PathGetSharedLibraryExtensionA #endif @@ -276,7 +281,7 @@ extern "C" { WINPR_API char* GetKnownPath(int id); WINPR_API char* GetKnownSubPath(int id, char* path); -WINPR_API char* GetCombinedPath(char* basePath, char* subPath); +WINPR_API char* GetCombinedPath(const char* basePath, const char* subPath); //#ifndef _WIN32 diff --git a/winpr/libwinpr/path/path.c b/winpr/libwinpr/path/path.c index 22f8a1365..45e9fb129 100644 --- a/winpr/libwinpr/path/path.c +++ b/winpr/libwinpr/path/path.c @@ -829,6 +829,44 @@ HRESULT PathCchConvertStyleW(PWSTR pszPath, size_t cchPath, unsigned long dwFlag return S_OK; } +/** + * PathGetSeparator + */ + +char PathGetSeparatorA(unsigned long dwFlags) +{ + char separator = PATH_SEPARATOR_CHR; + + if (!dwFlags) + dwFlags = PATH_STYLE_NATIVE; + + if (dwFlags == PATH_STYLE_WINDOWS) + separator = PATH_SEPARATOR_CHR; + else if (dwFlags == PATH_STYLE_UNIX) + separator = PATH_SEPARATOR_CHR; + else if (dwFlags == PATH_STYLE_NATIVE) + separator = PATH_SEPARATOR_CHR; + + return separator; +} + +WCHAR PathGetSeparatorW(unsigned long dwFlags) +{ + WCHAR separator = PATH_SEPARATOR_CHR; + + if (!dwFlags) + dwFlags = PATH_STYLE_NATIVE; + + if (dwFlags == PATH_STYLE_WINDOWS) + separator = PATH_SEPARATOR_CHR; + else if (dwFlags == PATH_STYLE_UNIX) + separator = PATH_SEPARATOR_CHR; + else if (dwFlags == PATH_STYLE_NATIVE) + separator = PATH_SEPARATOR_CHR; + + return separator; +} + /** * PathGetSharedLibraryExtension */ diff --git a/winpr/libwinpr/path/shell.c b/winpr/libwinpr/path/shell.c index 573ee0320..c7ead6871 100644 --- a/winpr/libwinpr/path/shell.c +++ b/winpr/libwinpr/path/shell.c @@ -271,7 +271,7 @@ char* GetKnownSubPath(int id, char* path) return subPath; } -char* GetCombinedPath(char* basePath, char* subPath) +char* GetCombinedPath(const char* basePath, const char* subPath) { int length; HRESULT status; From fb23f08388441acaadacbc17adced95db2ae4e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 10 Nov 2013 13:29:20 -0500 Subject: [PATCH 020/149] libfreerdp-client: fix possible infinite loop with .rdp file parsing containing freerdp options --- client/common/cmdline.c | 3 +-- client/common/compatibility.c | 16 +++++++++++++--- client/common/file.c | 4 ++++ winpr/include/winpr/cmdline.h | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 2b75bbe13..baf4e6681 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -989,7 +989,7 @@ BOOL freerdp_client_detect_command_line(int argc, char** argv, DWORD* flags) *flags |= COMMAND_LINE_SIGIL_DASH | COMMAND_LINE_SIGIL_DOUBLE_DASH; *flags |= COMMAND_LINE_SIGIL_ENABLE_DISABLE; - if (windows_cli_count > posix_cli_count) + if (windows_cli_count >= posix_cli_count) { *flags = COMMAND_LINE_SEPARATOR_COLON; *flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS; @@ -1098,7 +1098,6 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, return status; } - arg = CommandLineFindArgumentA(args, "v"); arg = args; diff --git a/client/common/compatibility.c b/client/common/compatibility.c index 279706722..d0fcff5d5 100644 --- a/client/common/compatibility.c +++ b/client/common/compatibility.c @@ -199,7 +199,12 @@ int freerdp_client_old_command_line_pre_filter(void* context, int index, int arg { return -1; } - freerdp_client_old_parse_hostname((char*) argv[index], &settings->ServerHostname, &settings->ServerPort); + + if (settings) + { + freerdp_client_old_parse_hostname((char*) argv[index], + &settings->ServerHostname, &settings->ServerPort); + } } else { @@ -280,14 +285,19 @@ int freerdp_client_old_command_line_pre_filter(void* context, int index, int arg index++; i++; } - } else { + } + else + { + if (settings) + { if (settings->instance) { freerdp_client_old_process_plugin(settings, args); } + } } - for (i=0; iargc; i++) + for (i = 0; i < args->argc; i++) free(args->argv[i]); free(args->argv); free(args); diff --git a/client/common/file.c b/client/common/file.c index 0d993345e..de29527bf 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -976,7 +976,11 @@ BOOL freerdp_client_populate_settings_from_rdp_file(rdpFile* file, rdpSettings* if (file->argc > 1) { + char* ConnectionFile = settings->ConnectionFile; + + settings->ConnectionFile = NULL; freerdp_client_settings_parse_command_line(settings, file->argc, file->argv); + settings->ConnectionFile = ConnectionFile; } return TRUE; diff --git a/winpr/include/winpr/cmdline.h b/winpr/include/winpr/cmdline.h index ca7ae7292..1570ce9b4 100644 --- a/winpr/include/winpr/cmdline.h +++ b/winpr/include/winpr/cmdline.h @@ -53,7 +53,7 @@ #define COMMAND_LINE_SIGIL_DOUBLE_DASH 0x00000008 #define COMMAND_LINE_SIGIL_PLUS_MINUS 0x00000010 #define COMMAND_LINE_SIGIL_ENABLE_DISABLE 0x00000020 -#define COMMAND_LINE_SIGIL_NOT_ESCAPED 0x00000040 +#define COMMAND_LINE_SIGIL_NOT_ESCAPED 0x00000040 #define COMMAND_LINE_SEPARATOR_COLON 0x00000100 #define COMMAND_LINE_SEPARATOR_EQUAL 0x00000200 From 226cad3e0368085d25b5fae59ffdcb4abc71dd08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 10 Nov 2013 17:54:41 -0500 Subject: [PATCH 021/149] winpr: improve winpr.pc generation --- .gitignore | 2 +- winpr/libwinpr/CMakeLists.txt | 13 +++++++++++++ winpr/libwinpr/winpr.pc | 12 ------------ winpr/libwinpr/winpr.pc.in | 3 ++- 4 files changed, 16 insertions(+), 14 deletions(-) delete mode 100644 winpr/libwinpr/winpr.pc diff --git a/.gitignore b/.gitignore index 904fbefbd..5e3941bc4 100755 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ CMakeCache.txt config.h install_manifest*.txt CTestTestfile.cmake -freerdp.pc +*.pc Makefile Testing cmake_install.cmake diff --git a/winpr/libwinpr/CMakeLists.txt b/winpr/libwinpr/CMakeLists.txt index f35bcb1ec..5b210c2ad 100644 --- a/winpr/libwinpr/CMakeLists.txt +++ b/winpr/libwinpr/CMakeLists.txt @@ -53,10 +53,23 @@ if(MONOLITHIC_BUILD) set_target_properties(${MODULE_NAME} PROPERTIES LINKER_LANGUAGE C) set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION_FULL} SOVERSION ${WINPR_VERSION} PREFIX "lib") + list(REMOVE_DUPLICATES WINPR_LIBS) target_link_libraries(${MODULE_NAME} ${WINPR_LIBS}) install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries EXPORT WinPRTargets) + set(WINPR_PC_LIBS "-lwinpr") + + foreach(WINPR_LIB ${WINPR_LIBS}) + if(${WINPR_LIB} MATCHES "^-l.*") + set(WINPR_PC_LIBS "${WINPR_PC_LIBS} ${WINPR_LIB}") + elseif(${WINPR_LIB} MATCHES "^/.*") + + else() + set(WINPR_PC_LIBS "${WINPR_PC_LIBS} -l${WINPR_LIB}") + endif() + endforeach() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/winpr.pc.in ${CMAKE_CURRENT_BINARY_DIR}/winpr.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/winpr.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/winpr/libwinpr/winpr.pc b/winpr/libwinpr/winpr.pc deleted file mode 100644 index 5950cce30..000000000 --- a/winpr/libwinpr/winpr.pc +++ /dev/null @@ -1,12 +0,0 @@ -prefix=/opt/freerds -exec_prefix=/opt/freerds -libdir=/opt/freerds/lib64 -includedir=/opt/freerds/include - -Name: WinPR -Description: WinPR: Windows Portable Runtime -URL: http://www.freerdp.com/ -Version: 1.1.0 -Requires: -Libs: -L${libdir} -lwinpr -Cflags: -I${includedir} diff --git a/winpr/libwinpr/winpr.pc.in b/winpr/libwinpr/winpr.pc.in index a593ede7b..538cbf0ed 100644 --- a/winpr/libwinpr/winpr.pc.in +++ b/winpr/libwinpr/winpr.pc.in @@ -2,11 +2,12 @@ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_PREFIX@/include +libs=@WINPR_PC_LIBS@ Name: WinPR Description: WinPR: Windows Portable Runtime URL: http://www.freerdp.com/ Version: @WINPR_VERSION_FULL@ Requires: -Libs: -L${libdir} -lwinpr +Libs: -L${libdir} ${libs} Cflags: -I${includedir} From f17a36f84cced265ea9d30e464e0ddb78c014448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 11 Nov 2013 17:25:59 -0500 Subject: [PATCH 022/149] libwinpr-library: port GetModuleFileName to Mac OS X --- winpr/libwinpr/library/library.c | 48 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/winpr/libwinpr/library/library.c b/winpr/libwinpr/library/library.c index 9a0419192..6a5285005 100644 --- a/winpr/libwinpr/library/library.c +++ b/winpr/libwinpr/library/library.c @@ -70,6 +70,10 @@ #include #include +#ifdef __MACOSX__ +#include +#endif + DLL_DIRECTORY_COOKIE AddDllDirectory(PCWSTR NewDirectory) { return NULL; @@ -166,8 +170,8 @@ DWORD GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize) } DWORD GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) -{ -#ifdef __linux__ +{ +#if defined(__linux__) int status; int length; char path[64]; @@ -198,6 +202,46 @@ DWORD GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) lpFilename[nSize - 1] = '\0'; } + return 0; + } +#elif defined(__MACOSX__) + int status; + int length; + + if (!hModule) + { + char path[4096]; + char buffer[4096]; + uint32_t size = sizeof(path); + + status = _NSGetExecutablePath(path, &size); + + if (status != 0) + { + /* path too small */ + return 0; + } + + /* + * _NSGetExecutablePath may not return the canonical path, + * so use realpath to find the absolute, canonical path. + */ + + realpath(path, buffer); + + length = strlen(buffer); + + if (length < nSize) + { + CopyMemory(lpFilename, buffer, length); + lpFilename[length] = '\0'; + } + else + { + CopyMemory(lpFilename, buffer, nSize - 1); + lpFilename[nSize - 1] = '\0'; + } + return 0; } #endif From fa12414a4b92cd44805de96fffdc4054139c4e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 14 Nov 2013 23:05:29 -0500 Subject: [PATCH 023/149] libfreerdp-core: fix parsing of MCS Disconnect Provider Ultimatum, workaround for 2008 R2 lack of error info pdu on user logoff --- libfreerdp/core/mcs.c | 48 ++++++++++++++++++++++++++++++++++++++ libfreerdp/core/mcs.h | 15 ++++++++++-- libfreerdp/core/rdp.c | 54 +++++++++++++++++++++++++++++++------------ 3 files changed, 100 insertions(+), 17 deletions(-) diff --git a/libfreerdp/core/mcs.c b/libfreerdp/core/mcs.c index 43a7a480c..ba34f98b1 100644 --- a/libfreerdp/core/mcs.c +++ b/libfreerdp/core/mcs.c @@ -818,6 +818,54 @@ BOOL mcs_send_channel_join_confirm(rdpMcs* mcs, UINT16 channel_id) return (status < 0) ? FALSE : TRUE; } +/** + * Receive MCS Disconnect Provider Ultimatum PDU.\n + * @param mcs mcs module + */ + +BOOL mcs_recv_disconnect_provider_ultimatum(rdpMcs* mcs, wStream* s, int* reason) +{ + BYTE b1, b2; + + /* + * http://msdn.microsoft.com/en-us/library/cc240872.aspx: + * + * PER encoded (ALIGNED variant of BASIC-PER) PDU contents: + * 21 80 + * + * 0x21: + * 0 - --\ + * 0 - | + * 1 - | CHOICE: From DomainMCSPDU select disconnectProviderUltimatum (8) + * 0 - | of type DisconnectProviderUltimatum + * 0 - | + * 0 - --/ + * 0 - --\ + * 1 - | + * | DisconnectProviderUltimatum::reason = rn-user-requested (3) + * 0x80: | + * 1 - --/ + * 0 - padding + * 0 - padding + * 0 - padding + * 0 - padding + * 0 - padding + * 0 - padding + * 0 - padding + */ + + if (Stream_GetRemainingLength(s) < 1) + return FALSE; + + Stream_Rewind_UINT8(s); + Stream_Read_UINT8(s, b1); + Stream_Read_UINT8(s, b2); + + *reason = ((b1 & 0x01) << 1) | (b2 >> 7); + + return TRUE; +} + /** * Send MCS Disconnect Provider Ultimatum PDU.\n * @param mcs mcs module diff --git a/libfreerdp/core/mcs.h b/libfreerdp/core/mcs.h index 1006231a7..9901f5d95 100644 --- a/libfreerdp/core/mcs.h +++ b/libfreerdp/core/mcs.h @@ -20,6 +20,8 @@ #ifndef __MCS_H #define __MCS_H +typedef struct rdp_mcs rdpMcs; + #include "transport.h" #include @@ -52,6 +54,15 @@ enum MCS_Result MCS_Result_enum_length = 16 }; +enum MCS_Reason +{ + MCS_Reason_domain_disconnected = 0, + MCS_Reason_provider_initiated = 1, + MCS_Reason_token_purged = 2, + MCS_Reason_user_requested = 3, + MCS_Reason_channel_purged = 4 +}; + enum DomainMCSPDU { DomainMCSPDU_PlumbDomainIndication = 0, @@ -115,7 +126,7 @@ typedef struct struct rdp_mcs { UINT16 user_id; - struct rdp_transport* transport; + rdpTransport* transport; DomainParameters domainParameters; DomainParameters targetParameters; DomainParameters minimumParameters; @@ -124,7 +135,6 @@ struct rdp_mcs BOOL user_channel_joined; BOOL global_channel_joined; }; -typedef struct rdp_mcs rdpMcs; #define MCS_SEND_DATA_HEADER_MAX_LENGTH 8 @@ -148,6 +158,7 @@ BOOL mcs_recv_channel_join_request(rdpMcs* mcs, wStream* s, UINT16* channel_id); BOOL mcs_send_channel_join_request(rdpMcs* mcs, UINT16 channel_id); BOOL mcs_recv_channel_join_confirm(rdpMcs* mcs, wStream* s, UINT16* channel_id); BOOL mcs_send_channel_join_confirm(rdpMcs* mcs, UINT16 channel_id); +BOOL mcs_recv_disconnect_provider_ultimatum(rdpMcs* mcs, wStream* s, int* reason); BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs); BOOL mcs_read_domain_mcspdu_header(wStream* s, enum DomainMCSPDU* domainMCSPDU, UINT16* length); void mcs_write_domain_mcspdu_header(wStream* s, enum DomainMCSPDU domainMCSPDU, UINT16 length, BYTE options); diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 69e964e18..589f1dda2 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -229,6 +229,25 @@ wStream* rdp_data_pdu_init(rdpRdp* rdp) return s; } +BOOL rdp_set_error_info(rdpRdp* rdp, UINT32 errorInfo) +{ + rdp->errorInfo = errorInfo; + + if (rdp->errorInfo != ERRINFO_SUCCESS) + { + ErrorInfoEventArgs e; + rdpContext* context = rdp->instance->context; + + rdp_print_errinfo(rdp->errorInfo); + + EventArgsInit(&e, "freerdp"); + e.code = rdp->errorInfo; + PubSub_OnErrorInfo(context->pubSub, context, &e); + } + + return TRUE; +} + /** * Read an RDP packet header.\n * @param rdp rdp module @@ -256,12 +275,25 @@ BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId) if (MCSPDU == DomainMCSPDU_DisconnectProviderUltimatum) { - BYTE reason; + int reason = 0; TerminateEventArgs e; rdpContext* context = rdp->instance->context; - (void) per_read_enumerated(s, &reason, 0); - DEBUG_RDP("DisconnectProviderUltimatum from server, reason code 0x%02x\n", reason); + if (!mcs_recv_disconnect_provider_ultimatum(rdp->mcs, s, &reason)) + return FALSE; + + if (rdp->errorInfo == ERRINFO_SUCCESS) + { + /** + * Some servers like Windows Server 2008 R2 do not send the error info pdu + * when the user logs off like they should. Map DisconnectProviderUltimatum + * to a ERRINFO_LOGOFF_BY_USER when the errinfo code is ERRINFO_SUCCESS. + */ + + rdp_set_error_info(rdp, ERRINFO_LOGOFF_BY_USER); + } + + fprintf(stderr, "DisconnectProviderUltimatum: reason: %d\n", reason); rdp->disconnect = TRUE; @@ -503,22 +535,14 @@ BOOL rdp_send_data_pdu(rdpRdp* rdp, wStream* s, BYTE type, UINT16 channel_id) BOOL rdp_recv_set_error_info_data_pdu(rdpRdp* rdp, wStream* s) { + UINT32 errorInfo; + if (Stream_GetRemainingLength(s) < 4) return FALSE; - Stream_Read_UINT32(s, rdp->errorInfo); /* errorInfo (4 bytes) */ + Stream_Read_UINT32(s, errorInfo); /* errorInfo (4 bytes) */ - if (rdp->errorInfo != ERRINFO_SUCCESS) - { - ErrorInfoEventArgs e; - rdpContext* context = rdp->instance->context; - - rdp_print_errinfo(rdp->errorInfo); - - EventArgsInit(&e, "freerdp"); - e.code = rdp->errorInfo; - PubSub_OnErrorInfo(context->pubSub, context, &e); - } + rdp_set_error_info(rdp, errorInfo); return TRUE; } From 498227eb1bc7453600ecfb670059817123659e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 14 Nov 2013 23:41:46 -0500 Subject: [PATCH 024/149] libfreerdp-core: map more Disconnect Provider Ultimatum PDU reason codes to error info pdu --- libfreerdp/core/rdp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 589f1dda2..01423bdb7 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -290,7 +290,12 @@ BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId) * to a ERRINFO_LOGOFF_BY_USER when the errinfo code is ERRINFO_SUCCESS. */ - rdp_set_error_info(rdp, ERRINFO_LOGOFF_BY_USER); + if (reason == MCS_Reason_provider_initiated) + rdp_set_error_info(rdp, ERRINFO_RPC_INITIATED_DISCONNECT); + else if (reason == MCS_Reason_user_requested) + rdp_set_error_info(rdp, ERRINFO_LOGOFF_BY_USER); + else + rdp_set_error_info(rdp, ERRINFO_RPC_INITIATED_DISCONNECT); } fprintf(stderr, "DisconnectProviderUltimatum: reason: %d\n", reason); From c4de5a4e4b1e9b4c46f1f3262d0fc2430de64850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20LeBlanc?= Date: Fri, 15 Nov 2013 08:21:36 -0500 Subject: [PATCH 025/149] Fixed compilation on windows (code was not standard C) --- libfreerdp/core/input.c | 3 ++- libfreerdp/core/update.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libfreerdp/core/input.c b/libfreerdp/core/input.c index fc5b5f4ee..7b505310b 100644 --- a/libfreerdp/core/input.c +++ b/libfreerdp/core/input.c @@ -494,9 +494,10 @@ static void input_free_queued_message(void *obj) rdpInput* input_new(rdpRdp* rdp) { - const wObject cb = { .fnObjectFree = input_free_queued_message }; + wObject cb; rdpInput* input; + cb.fnObjectFree = input_free_queued_message ; input = (rdpInput*) malloc(sizeof(rdpInput)); if (input != NULL) diff --git a/libfreerdp/core/update.c b/libfreerdp/core/update.c index a4cebdf08..1296a7b02 100644 --- a/libfreerdp/core/update.c +++ b/libfreerdp/core/update.c @@ -1553,9 +1553,10 @@ static void update_free_queued_message(void *obj) rdpUpdate* update_new(rdpRdp* rdp) { - const wObject cb = { .fnObjectFree = update_free_queued_message }; + wObject cb; rdpUpdate* update; + cb.fnObjectFree = update_free_queued_message; update = (rdpUpdate*) malloc(sizeof(rdpUpdate)); if (update) From 6e581daebb87f34d8b19d5d8c4f17ca0a8da9826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Mon, 18 Nov 2013 11:34:24 -0500 Subject: [PATCH 026/149] Removed compilation warning --- winpr/include/winpr/cmdline.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winpr/include/winpr/cmdline.h b/winpr/include/winpr/cmdline.h index 1570ce9b4..e38f8fec4 100644 --- a/winpr/include/winpr/cmdline.h +++ b/winpr/include/winpr/cmdline.h @@ -92,7 +92,7 @@ struct _COMMAND_LINE_ARGUMENT_A { LPCSTR Name; DWORD Flags; - LPSTR Format; + LPCSTR Format; LPSTR Default; LPSTR Value; LONG Index; @@ -104,7 +104,7 @@ struct _COMMAND_LINE_ARGUMENT_W { LPCWSTR Name; DWORD Flags; - LPSTR Format; + LPCSTR Format; LPWSTR Default; LPWSTR Value; LONG Index; From 13322f5ba2d276835af5a6441804b774dc440133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Mon, 18 Nov 2013 11:59:53 -0500 Subject: [PATCH 027/149] Removed warning --- winpr/libwinpr/synch/wait.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/winpr/libwinpr/synch/wait.c b/winpr/libwinpr/synch/wait.c index 15840916a..8c09bd47d 100644 --- a/winpr/libwinpr/synch/wait.c +++ b/winpr/libwinpr/synch/wait.c @@ -87,13 +87,13 @@ int clock_gettime(int clk_id, struct timespec *t) #if !defined(HAVE_PTHREAD_GNU_EXT) #include -static long long ts_difftime(struct timespec *o, - struct timespec *n) +static long long ts_difftime(const struct timespec *o, + const struct timespec *n) { - long long old = o->tv_sec * 1000000000LL + o->tv_nsec; - long long new = n->tv_sec * 1000000000LL + n->tv_nsec; + long long oldValue = o->tv_sec * 1000000000LL + o->tv_nsec; + long long newValue = n->tv_sec * 1000000000LL + n->tv_nsec; - return new - old; + return newValue - oldValue; } static int pthread_timedjoin_np(pthread_t td, void **res, From b0369cf284dd40b13e7d51c341008244d7bf0554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 18 Nov 2013 13:54:33 -0500 Subject: [PATCH 028/149] libfreerdp-core: add external certificate management, pass X509 PEM certificate through client callback --- include/freerdp/freerdp.h | 9 ++++--- include/freerdp/settings.h | 4 ++- libfreerdp/common/settings.c | 8 ++++++ libfreerdp/core/settings.c | 1 + libfreerdp/crypto/tls.c | 52 ++++++++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 4 deletions(-) diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index a7a4182f0..28064c27e 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -61,6 +61,7 @@ typedef void (*pPostDisconnect)(freerdp* instance); typedef BOOL (*pAuthenticate)(freerdp* instance, char** username, char** password, char** domain); typedef BOOL (*pVerifyCertificate)(freerdp* instance, char* subject, char* issuer, char* fingerprint); typedef BOOL (*pVerifyChangedCertificate)(freerdp* instance, char* subject, char* issuer, char* new_fingerprint, char* old_fingerprint); +typedef int (*pVerifyX509Certificate)(freerdp* instance, BYTE* data, int length, DWORD flags); typedef int (*pLogonErrorInfo)(freerdp* instance, UINT32 data, UINT32 type); @@ -195,13 +196,15 @@ struct rdp_freerdp Used when a certificate differs from stored fingerprint. If returns TRUE, the new fingerprint will be trusted and old thrown out. */ - ALIGN64 pLogonErrorInfo LogonErrorInfo; /**< (offset 53) Callback for logon error info, important for logon system messages with RemoteApp */ + ALIGN64 pVerifyX509Certificate VerifyX509Certificate; /**< (offset 53) Callback for X509 certificate verification (PEM format) */ - ALIGN64 pPostDisconnect PostDisconnect; /**< (offset 54) + ALIGN64 pLogonErrorInfo LogonErrorInfo; /**< (offset 54) Callback for logon error info, important for logon system messages with RemoteApp */ + + ALIGN64 pPostDisconnect PostDisconnect; /**< (offset 55) Callback for cleaning up resources allocated by connect callbacks. */ - UINT64 paddingD[64 - 55]; /* 55 */ + UINT64 paddingD[64 - 56]; /* 56 */ ALIGN64 pSendChannelData SendChannelData; /* (offset 64) Callback for sending data to a channel. diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 59481d9dd..eaad54584 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -594,6 +594,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_RdpKeyFile 1412 #define FreeRDP_RdpServerRsaKey 1413 #define FreeRDP_RdpServerCertificate 1414 +#define FreeRDP_ExternalCertificateManagement 1415 #define FreeRDP_Workarea 1536 #define FreeRDP_Fullscreen 1537 #define FreeRDP_PercentScreen 1538 @@ -960,7 +961,8 @@ struct rdp_settings ALIGN64 char* RdpKeyFile; /* 1412 */ ALIGN64 rdpRsaKey* RdpServerRsaKey; /* 1413 */ ALIGN64 rdpCertificate* RdpServerCertificate; /* 1414 */ - UINT64 padding1472[1472 - 1350]; /* 1415 */ + ALIGN64 BOOL ExternalCertificateManagement; /* 1415 */ + UINT64 padding1472[1472 - 1416]; /* 1416 */ UINT64 padding1536[1536 - 1472]; /* 1472 */ /** diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index ba0bea378..ef4493f4e 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -665,6 +665,10 @@ BOOL freerdp_get_param_bool(rdpSettings* settings, int id) return settings->IgnoreCertificate; break; + case FreeRDP_ExternalCertificateManagement: + return settings->ExternalCertificateManagement; + break; + case FreeRDP_Workarea: return settings->Workarea; break; @@ -1129,6 +1133,10 @@ int freerdp_set_param_bool(rdpSettings* settings, int id, BOOL param) settings->IgnoreCertificate = param; break; + case FreeRDP_ExternalCertificateManagement: + settings->ExternalCertificateManagement = param; + break; + case FreeRDP_Workarea: settings->Workarea = param; break; diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 5870e49b5..0edabb5c9 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -620,6 +620,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) _settings->MstscCookieMode = settings->MstscCookieMode; /* 1152 */ _settings->SendPreconnectionPdu = settings->SendPreconnectionPdu; /* 1156 */ _settings->IgnoreCertificate = settings->IgnoreCertificate; /* 1408 */ + _settings->ExternalCertificateManagement = settings->ExternalCertificateManagement; /* 1415 */ _settings->Workarea = settings->Workarea; /* 1536 */ _settings->Fullscreen = settings->Fullscreen; /* 1537 */ _settings->GrabKeyboard = settings->GrabKeyboard; /* 1539 */ diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 3b97486b5..b7cda06df 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -582,6 +582,58 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname) BOOL verification_status = FALSE; rdpCertificateData* certificate_data; + if (tls->settings->ExternalCertificateManagement) + { + BIO* bio; + int status; + int length; + int offset; + BYTE* pemCert; + freerdp* instance = (freerdp*) tls->settings->instance; + + /** + * Don't manage certificates internally, leave it up entirely to the external client implementation + */ + + bio = BIO_new(BIO_s_mem()); + + status = PEM_write_bio_X509(bio, cert->px509); + + offset = 0; + length = 2048; + pemCert = (BYTE*) malloc(length + 1); + + status = BIO_read(bio, pemCert, length); + offset += status; + + while (offset >= length) + { + length *= 2; + pemCert = (BYTE*) realloc(pemCert, length + 1); + + status = BIO_read(bio, &pemCert[offset], length); + + if (status < 0) + break; + + offset += status; + } + + length = offset; + pemCert[length] = '\0'; + + status = -1; + + if (instance->VerifyX509Certificate) + { + status = instance->VerifyX509Certificate(instance, pemCert, length, 0); + } + + free(pemCert); + + return (status < 0) ? FALSE : TRUE; + } + /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->IgnoreCertificate) return TRUE; /* success! */ From c03a2e54972a12c1d2d8da2d19f914390c918b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 19 Nov 2013 14:29:31 -0500 Subject: [PATCH 029/149] channels/rdpsnd: add option for configuring audio quality mode --- channels/rdpsnd/client/rdpsnd_main.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c index 1fc4c9e25..aedf8bf59 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -65,6 +65,7 @@ struct rdpsnd_plugin HANDLE ScheduleThread; BYTE cBlockNo; + UINT16 wQualityMode; int wCurrentFormatNo; AUDIO_FORMAT* ServerFormats; @@ -139,7 +140,7 @@ void rdpsnd_send_quality_mode_pdu(rdpsndPlugin* rdpsnd) Stream_Write_UINT8(pdu, SNDC_QUALITYMODE); /* msgType */ Stream_Write_UINT8(pdu, 0); /* bPad */ Stream_Write_UINT16(pdu, 4); /* BodySize */ - Stream_Write_UINT16(pdu, HIGH_QUALITY); /* wQualityMode */ + Stream_Write_UINT16(pdu, rdpsnd->wQualityMode); /* wQualityMode */ Stream_Write_UINT16(pdu, 0); /* Reserved */ rdpsnd_virtual_channel_write(rdpsnd, pdu); @@ -597,6 +598,7 @@ COMMAND_LINE_ARGUMENT_A rdpsnd_args[] = { "rate", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "rate" }, { "channel", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "channel" }, { "latency", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "latency" }, + { "quality", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "quality mode" }, { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } }; @@ -606,10 +608,13 @@ static void rdpsnd_process_addin_args(rdpsndPlugin* rdpsnd, ADDIN_ARGV* args) DWORD flags; COMMAND_LINE_ARGUMENT_A* arg; + rdpsnd->wQualityMode = HIGH_QUALITY; /* default quality mode */ + flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON; status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, rdpsnd_args, flags, rdpsnd, NULL, NULL); + if (status < 0) return; @@ -646,6 +651,24 @@ static void rdpsnd_process_addin_args(rdpsndPlugin* rdpsnd, ADDIN_ARGV* args) { rdpsnd->latency = atoi(arg->Value); } + CommandLineSwitchCase(arg, "quality") + { + int wQualityMode = DYNAMIC_QUALITY; + + if (_stricmp(arg->Value, "dynamic") == 0) + wQualityMode = DYNAMIC_QUALITY; + else if (_stricmp(arg->Value, "medium") == 0) + wQualityMode = MEDIUM_QUALITY; + else if (_stricmp(arg->Value, "high") == 0) + wQualityMode = HIGH_QUALITY; + else + wQualityMode = atoi(arg->Value); + + if ((wQualityMode < 0) || (wQualityMode > 2)) + wQualityMode = DYNAMIC_QUALITY; + + rdpsnd->wQualityMode = (UINT16) wQualityMode; + } CommandLineSwitchDefault(arg) { From c1fa455c30b9ee484a16a14347846c080b0f4895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 19 Nov 2013 15:31:38 -0500 Subject: [PATCH 030/149] channels/rdpsnd: add wlog debug output --- channels/rdpsnd/client/rdpsnd_main.c | 42 ++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c index aedf8bf59..fad04b364 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,7 @@ struct rdpsnd_plugin UINT32 OpenHandle; wMessagePipe* MsgPipe; + wLog* log; HANDLE ScheduleThread; BYTE cBlockNo; @@ -92,7 +94,7 @@ struct rdpsnd_plugin rdpsndDevicePlugin* device; }; -static void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE cConfirmedBlockNo); +static void rdpsnd_confirm_wave(rdpsndPlugin* rdpsnd, RDPSND_WAVE* wave); static void* rdpsnd_schedule_thread(void* arg) { @@ -124,9 +126,10 @@ static void* rdpsnd_schedule_thread(void* arg) Sleep(wTimeDiff); } - rdpsnd_send_wave_confirm_pdu(rdpsnd, wave->wTimeStampB, wave->cBlockNo); - free(wave); + rdpsnd_confirm_wave(rdpsnd, wave); + message.wParam = NULL; + free(wave); } return NULL; @@ -143,6 +146,8 @@ void rdpsnd_send_quality_mode_pdu(rdpsndPlugin* rdpsnd) Stream_Write_UINT16(pdu, rdpsnd->wQualityMode); /* wQualityMode */ Stream_Write_UINT16(pdu, 0); /* Reserved */ + WLog_Print(rdpsnd->log, WLOG_DEBUG, "QualityMode: %d", rdpsnd->wQualityMode); + rdpsnd_virtual_channel_write(rdpsnd, pdu); } @@ -259,6 +264,8 @@ void rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd) Stream_Write(pdu, clientFormat->data, clientFormat->cbSize); } + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Client Audio Formats"); + rdpsnd_virtual_channel_write(rdpsnd, pdu); } @@ -311,6 +318,8 @@ void rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s) rdpsnd_select_supported_audio_formats(rdpsnd); + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Server Audio Formats"); + rdpsnd_send_client_audio_formats(rdpsnd); if (wVersion >= 6) @@ -331,6 +340,9 @@ void rdpsnd_send_training_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, U Stream_Write_UINT16(pdu, wTimeStamp); Stream_Write_UINT16(pdu, wPackSize); + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Training Response: wTimeStamp: %d wPackSize: %d", + wTimeStamp, wPackSize); + rdpsnd_virtual_channel_write(rdpsnd, pdu); } @@ -342,6 +354,9 @@ static void rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, wStream* s) Stream_Read_UINT16(s, wTimeStamp); Stream_Read_UINT16(s, wPackSize); + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Training Request: wTimeStamp: %d wPackSize: %d", + wTimeStamp, wPackSize); + rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize); } @@ -362,6 +377,9 @@ static void rdpsnd_recv_wave_info_pdu(rdpsndPlugin* rdpsnd, wStream* s, UINT16 B format = &rdpsnd->ClientFormats[wFormatNo]; + WLog_Print(rdpsnd->log, WLOG_DEBUG, "WaveInfo: cBlockNo: %d wFormatNo: %d", + rdpsnd->cBlockNo, wFormatNo); + if (!rdpsnd->isOpen) { rdpsnd->isOpen = TRUE; @@ -400,6 +418,14 @@ void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE rdpsnd_virtual_channel_write(rdpsnd, pdu); } +void rdpsnd_confirm_wave(rdpsndPlugin* rdpsnd, RDPSND_WAVE* wave) +{ + WLog_Print(rdpsnd->log, WLOG_DEBUG, "WaveConfirm: cBlockNo: %d wTimeStamp: %d wTimeDiff: %d", + wave->cBlockNo, wave->wTimeStampB, wave->wTimeStampB - wave->wTimeStampA); + + rdpsnd_send_wave_confirm_pdu(rdpsnd, wave->wTimeStampB, wave->cBlockNo); +} + static void rdpsnd_device_send_wave_confirm_pdu(rdpsndDevicePlugin* device, RDPSND_WAVE* wave) { MessageQueue_Post(device->rdpsnd->MsgPipe->Out, NULL, 0, (void*) wave, NULL); @@ -439,6 +465,9 @@ static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s) format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo]; wave->wAudioLength = rdpsnd_compute_audio_time_length(format, size); + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Wave: cBlockNo: %d wTimeStamp: %d", + wave->cBlockNo, wave->wTimeStampA); + if (!rdpsnd->device) { free(wave); @@ -469,6 +498,8 @@ static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s) static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd) { + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Close"); + if (rdpsnd->device) { IFCALL(rdpsnd->device->Close, rdpsnd->device); @@ -483,6 +514,8 @@ static void rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s) Stream_Read_UINT32(s, dwVolume); + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Volume: 0x%04X", dwVolume); + if (rdpsnd->device) { IFCALL(rdpsnd->device->SetVolume, rdpsnd->device, dwVolume); @@ -1041,6 +1074,9 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints, pEntryPoints->cbSize); + rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client"); + //WLog_SetLogLevel(rdpsnd->log, WLOG_TRACE); + rdpsnd->channelEntryPoints.pVirtualChannelInit(&rdpsnd->InitHandle, &rdpsnd->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, rdpsnd_virtual_channel_init_event); From 690a6b624db56bdc3fc80a58dd90d6df8aedf2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 20 Nov 2013 15:21:29 -0500 Subject: [PATCH 031/149] libfreerdp-crypto: don't report SSL_ERROR_SYSCALL with errno value 0 as error --- libfreerdp/crypto/tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index b7cda06df..4d5e8c4ca 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -400,7 +400,7 @@ int tls_read(rdpTls* tls, BYTE* data, int length) break; case SSL_ERROR_SYSCALL: - if (errno == EAGAIN) + if ((errno == EAGAIN) || (errno == 0)) { status = 0; } From 76c842285d21086c0b6d491af973da845690c3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 22 Nov 2013 12:11:39 -0500 Subject: [PATCH 032/149] channels/rdpsnd: initial attempt at adding GSM610 support --- CMakeLists.txt | 5 + channels/rdpsnd/client/pulse/CMakeLists.txt | 6 +- channels/rdpsnd/client/pulse/rdpsnd_pulse.c | 141 +++++++++++++++----- cmake/FindGSM.cmake | 13 ++ config.h.in | 1 + libfreerdp/codec/audio.c | 32 ++++- 6 files changed, 164 insertions(+), 34 deletions(-) create mode 100644 cmake/FindGSM.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e57790cba..c2316d5d2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,6 +381,10 @@ set(JPEG_FEATURE_TYPE "OPTIONAL") set(JPEG_FEATURE_PURPOSE "codec") set(JPEG_FEATURE_DESCRIPTION "use JPEG library") +set(GSM_FEATURE_TYPE "OPTIONAL") +set(GSM_FEATURE_PURPOSE "codec") +set(GSM_FEATURE_DESCRIPTION "GSM audio codec library") + if(WIN32) set(X11_FEATURE_TYPE "DISABLED") set(ZLIB_FEATURE_TYPE "DISABLED") @@ -445,6 +449,7 @@ find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FE find_feature(Gstreamer ${GSTREAMER_FEATURE_TYPE} ${GSTREAMER_FEATURE_PURPOSE} ${GSTREAMER_FEATURE_DESCRIPTION}) find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION}) +find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION}) if(TARGET_ARCH MATCHES "x86|x64") if (NOT APPLE) diff --git a/channels/rdpsnd/client/pulse/CMakeLists.txt b/channels/rdpsnd/client/pulse/CMakeLists.txt index d652ba234..6b6de1ecc 100644 --- a/channels/rdpsnd/client/pulse/CMakeLists.txt +++ b/channels/rdpsnd/client/pulse/CMakeLists.txt @@ -32,7 +32,11 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MODULE freerdp MODULES freerdp-codec freerdp-utils) -set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${PULSE_LIBRARY}) +list(APPEND ${MODULE_PREFIX}_LIBS ${PULSE_LIBRARY}) + +if(GSM_FOUND) + list(APPEND ${MODULE_PREFIX}_LIBS ${GSM_LIBRARIES}) +endif() target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) diff --git a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c index cb0bea63d..e20cce9be 100644 --- a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c +++ b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c @@ -26,10 +26,15 @@ #include #include +#include #include #include +#ifdef WITH_GSM +#include +#endif + #include #include #include @@ -52,6 +57,11 @@ struct rdpsnd_pulse_plugin int latency; FREERDP_DSP_CONTEXT* dsp_context; + +#ifdef WITH_GSM + gsm gsm_context; + wStream* gsmBuffer; +#endif }; static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userdata) @@ -241,6 +251,7 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT* break; case WAVE_FORMAT_GSM610: + sample_spec.format = PA_SAMPLE_S16LE; break; } @@ -331,6 +342,14 @@ static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, if (state == PA_STREAM_READY) { freerdp_dsp_context_reset_adpcm(pulse->dsp_context); + +#ifdef WITH_GSM + if (pulse->gsm_context) + gsm_destroy(pulse->gsm_context); + + pulse->gsm_context = gsm_create(); +#endif + DEBUG_SVC("connected"); } else @@ -410,7 +429,18 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, AUDIO_FORM return TRUE; } break; + +#ifdef WITH_GSM + case WAVE_FORMAT_GSM610: + if ((format->nSamplesPerSec <= PA_RATE_MAX) && + (format->nBlockAlign == 65) && (format->nChannels == 1)) + { + return TRUE; + } + break; +#endif } + return FALSE; } @@ -459,63 +489,108 @@ static void rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value) pa_threaded_mainloop_unlock(pulse->mainloop); } +static BYTE* rdpsnd_pulse_convert_audio(rdpsndDevicePlugin* device, BYTE* data, int* size) +{ + BYTE* pcmData = NULL; + rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device; + + if (pulse->format == WAVE_FORMAT_ADPCM) + { + pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context, + data, *size, pulse->sample_spec.channels, pulse->block_size); + + *size = pulse->dsp_context->adpcm_size; + pcmData = pulse->dsp_context->adpcm_buffer; + } + else if (pulse->format == WAVE_FORMAT_DVI_ADPCM) + { + pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context, + data, *size, pulse->sample_spec.channels, pulse->block_size); + + *size = pulse->dsp_context->adpcm_size; + pcmData = pulse->dsp_context->adpcm_buffer; + } +#ifdef WITH_GSM + else if (pulse->format == WAVE_FORMAT_GSM610) + { + int inPos = 0; + int inSize = *size; + UINT16 gsmBlockBuffer[160]; + + Stream_SetPosition(pulse->gsmBuffer, 0); + + while (inSize) + { + ZeroMemory(gsmBlockBuffer, sizeof(gsmBlockBuffer)); + gsm_decode(pulse->gsm_context, (gsm_byte*) &data[inPos], (gsm_signal*) gsmBlockBuffer); + + if ((inPos % 65) == 0) + { + inPos += 33; + inSize -= 33; + } + else + { + inPos += 32; + inSize -= 32; + } + + Stream_EnsureRemainingCapacity(pulse->gsmBuffer, 160 * 2); + Stream_Write(pulse->gsmBuffer, (void*) gsmBlockBuffer, 160 * 2); + } + + Stream_SealLength(pulse->gsmBuffer); + + pcmData = Stream_Buffer(pulse->gsmBuffer); + *size = Stream_Length(pulse->gsmBuffer); + } +#endif + else + { + pcmData = data; + } + + return pcmData; +} + static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, BYTE* data, int size) { - int len; - int ret; - BYTE* src; + int length; + int status; + BYTE* pcmData; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device; if (!pulse->stream) return; - if (pulse->format == WAVE_FORMAT_ADPCM) - { - pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context, - data, size, pulse->sample_spec.channels, pulse->block_size); - - size = pulse->dsp_context->adpcm_size; - src = pulse->dsp_context->adpcm_buffer; - } - else if (pulse->format == WAVE_FORMAT_DVI_ADPCM) - { - pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context, - data, size, pulse->sample_spec.channels, pulse->block_size); - - size = pulse->dsp_context->adpcm_size; - src = pulse->dsp_context->adpcm_buffer; - } - else - { - src = data; - } + pcmData = rdpsnd_pulse_convert_audio(device, data, &size); pa_threaded_mainloop_lock(pulse->mainloop); while (size > 0) { - while ((len = pa_stream_writable_size(pulse->stream)) == 0) + while ((length = pa_stream_writable_size(pulse->stream)) == 0) { pa_threaded_mainloop_wait(pulse->mainloop); } - if (len < 0) + if (length < 0) break; - if (len > size) - len = size; + if (length > size) + length = size; - ret = pa_stream_write(pulse->stream, src, len, NULL, 0LL, PA_SEEK_RELATIVE); + status = pa_stream_write(pulse->stream, pcmData, length, NULL, 0LL, PA_SEEK_RELATIVE); - if (ret < 0) + if (status < 0) { DEBUG_WARN("pa_stream_write failed (%d)", pa_context_errno(pulse->context)); break; } - src += len; - size -= len; + pcmData += length; + size -= length; } pa_threaded_mainloop_unlock(pulse->mainloop); @@ -595,6 +670,10 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE pulse->dsp_context = freerdp_dsp_context_new(); +#ifdef WITH_GSM + pulse->gsmBuffer = Stream_New(NULL, 4096); +#endif + pulse->mainloop = pa_threaded_mainloop_new(); if (!pulse->mainloop) diff --git a/cmake/FindGSM.cmake b/cmake/FindGSM.cmake new file mode 100644 index 000000000..366fd6764 --- /dev/null +++ b/cmake/FindGSM.cmake @@ -0,0 +1,13 @@ + +find_path(GSM_INCLUDE_DIR gsm/gsm.h) + +find_library(GSM_LIBRARY gsm) + +find_package_handle_standard_args(GSM DEFAULT_MSG GSM_INCLUDE_DIR GSM_LIBRARY) + +if(GSM_FOUND) + set(GSM_LIBRARIES ${GSM_LIBRARY}) + set(GSM_INCLUDE_DIRS ${GSM_INCLUDE_DIR}) +endif() + +mark_as_advanced(GSM_INCLUDE_DIR GSM_LIBRARY) diff --git a/config.h.in b/config.h.in index 043592ca6..aed1f6f09 100755 --- a/config.h.in +++ b/config.h.in @@ -44,6 +44,7 @@ #cmakedefine WITH_PULSE #cmakedefine WITH_IOSAUDIO #cmakedefine WITH_OPENSLES +#cmakedefine WITH_GSM /* Plugins */ #cmakedefine STATIC_CHANNELS diff --git a/libfreerdp/codec/audio.c b/libfreerdp/codec/audio.c index 1e45989d3..751885e78 100644 --- a/libfreerdp/codec/audio.c +++ b/libfreerdp/codec/audio.c @@ -35,8 +35,36 @@ UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size) * http://msdn.microsoft.com/en-us/library/ms713497.aspx */ - wSamples = (size * 8) / format->wBitsPerSample; - mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + if (format->wBitsPerSample) + { + wSamples = (size * 8) / format->wBitsPerSample; + mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + } + else + { + mstime = 0; + + if (format->wFormatTag == WAVE_FORMAT_GSM610) + { + UINT16 nSamplesPerBlock; + + if ((format->cbSize == 2) && (format->data)) + { + nSamplesPerBlock = *((UINT16*) format->data); + + wSamples = (size / format->nBlockAlign) * nSamplesPerBlock; + mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + } + else + { + fprintf(stderr, "rdpsnd_compute_audio_time_length: invalid WAVE_FORMAT_GSM610 format\n"); + } + } + else + { + fprintf(stderr, "rdpsnd_compute_audio_time_length: unknown format %d\n", format->wFormatTag); + } + } return mstime; } From d734bde4201db1217950d3eadee0688798c4afb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Fri, 22 Nov 2013 13:37:28 -0500 Subject: [PATCH 033/149] =?UTF-8?q?Fix=20leak:=20free=20gdi=20object=20(if?= =?UTF-8?q?=20it=20wasn=E2=80=99t=20freed=20before)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libfreerdp/core/freerdp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 57ea44526..6e0f8948b 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -40,6 +40,7 @@ #include #include #include +#include /* connectErrorCode is 'extern' in error.h. See comment there.*/ @@ -447,6 +448,8 @@ void freerdp_context_free(freerdp* instance) graphics_free(instance->context->graphics); instance->context->graphics = NULL; + gdi_free(instance); + PubSub_Free(instance->context->pubSub); free(instance->context); From fce1880cae47dd0cd43fc33fbf69a4808ead9f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Fri, 22 Nov 2013 13:38:42 -0500 Subject: [PATCH 034/149] Fix leak (native mac) : free gdi --- client/Mac/MRDPView.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/Mac/MRDPView.m b/client/Mac/MRDPView.m index 64da92414..27341189e 100644 --- a/client/Mac/MRDPView.m +++ b/client/Mac/MRDPView.m @@ -660,6 +660,8 @@ DWORD mac_client_thread(void* param) if (!is_connected) return; + gdi_free(context->instance); + freerdp_channels_global_uninit(); if (pixel_data) From 21ec46036ba2d7d5e2a7a476661cf5d6530736ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 23 Nov 2013 21:35:44 -0500 Subject: [PATCH 035/149] channels/cliprdr: start implementing clean callback interface --- channels/cliprdr/client/cliprdr_format.c | 249 +++++++++++++++-------- channels/cliprdr/client/cliprdr_main.c | 181 +++++++++++++--- channels/cliprdr/client/cliprdr_main.h | 2 + client/Windows/wf_cliprdr.c | 6 +- include/freerdp/channels/cliprdr.h | 141 +++++++++++++ include/freerdp/client/cliprdr.h | 75 ++----- include/freerdp/message.h | 4 +- 7 files changed, 492 insertions(+), 166 deletions(-) create mode 100644 include/freerdp/channels/cliprdr.h diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index a32ce8e49..e7a5866ff 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -213,108 +213,193 @@ void cliprdr_process_long_format_names(cliprdrPlugin* cliprdr, wStream* s, UINT3 void cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags) { - int i; - UINT32 format; - BOOL supported; - CLIPRDR_FORMAT_NAME* format_name; - RDP_CB_FORMAT_LIST_EVENT* cb_event; + CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); - cb_event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(CliprdrChannel_Class, - CliprdrChannel_FormatList, NULL, NULL); - - if (dataLen > 0) + if (context->custom) { - cb_event->raw_format_data = (BYTE*) malloc(dataLen); - memcpy(cb_event->raw_format_data, Stream_Pointer(s), dataLen); - cb_event->raw_format_data_size = dataLen; - cb_event->raw_format_unicode = (msgFlags & CB_ASCII_NAMES) ? FALSE : TRUE; - } + int index; + int formatNameLength; + CLIPRDR_FORMAT* formats; + CLIPRDR_FORMAT_LIST formatList; - if (cliprdr->use_long_format_names) - cliprdr_process_long_format_names(cliprdr, s, dataLen, msgFlags); - else - cliprdr_process_short_format_names(cliprdr, s, dataLen, msgFlags); + formatList.msgType = CB_FORMAT_LIST; + formatList.msgFlags = msgFlags; + formatList.dataLen = dataLen; - if (cliprdr->num_format_names > 0) - cb_event->formats = (UINT32*) malloc(sizeof(UINT32) * cliprdr->num_format_names); + formatList.cFormats = 0; - cb_event->num_formats = 0; - - for (i = 0; i < cliprdr->num_format_names; i++) - { - supported = TRUE; - format_name = &cliprdr->format_names[i]; - format = format_name->id; - - switch (format) + while (dataLen) { - case CB_FORMAT_TEXT: - case CB_FORMAT_DIB: - case CB_FORMAT_UNICODETEXT: - break; + Stream_Seek(s, 4); /* formatId */ + dataLen -= 4; - default: - - if (format_name->length > 0) - { - DEBUG_CLIPRDR("format: %s", format_name->name); - - if (strcmp(format_name->name, "HTML Format") == 0) - { - format = CB_FORMAT_HTML; - break; - } - if (strcmp(format_name->name, "PNG") == 0) - { - format = CB_FORMAT_PNG; - break; - } - if (strcmp(format_name->name, "JFIF") == 0) - { - format = CB_FORMAT_JPEG; - break; - } - if (strcmp(format_name->name, "GIF") == 0) - { - format = CB_FORMAT_GIF; - break; - } - } - else - { - supported = FALSE; - } - - break; + formatNameLength = _wcslen((WCHAR*) Stream_Pointer(s)); + Stream_Seek(s, (formatNameLength + 1) * 2); + dataLen -= ((formatNameLength + 1) * 2); + formatList.cFormats++; } - if (supported) - cb_event->formats[cb_event->num_formats++] = format; + index = 0; + dataLen = formatList.dataLen; + Stream_Rewind(s, dataLen); - if (format_name->length > 0) - free(format_name->name); + formats = (CLIPRDR_FORMAT*) malloc(sizeof(CLIPRDR_FORMAT) * formatList.cFormats); + formatList.formats = formats; + + while (dataLen) + { + Stream_Read_UINT32(s, formats[index].formatId); /* formatId */ + dataLen -= 4; + + formats[index].formatName = NULL; + formatNameLength = _wcslen((WCHAR*) Stream_Pointer(s)); + + if (formatNameLength) + { + formatNameLength = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), + -1, &(formats[index].formatName), 0, NULL, NULL); + + Stream_Seek(s, formatNameLength * 2); + dataLen -= (formatNameLength * 2); + } + else + { + Stream_Seek(s, 2); + dataLen -= 2; + } + + index++; + } + + if (context->ServerFormatList) + context->ServerFormatList(context, &formatList); + + for (index = 0; index < formatList.cFormats; index++) + free(formats[index].formatName); + + free(formats); } + else + { + int i; + UINT32 format; + BOOL supported; + CLIPRDR_FORMAT_NAME* format_name; + RDP_CB_FORMAT_LIST_EVENT* cb_event; - free(cliprdr->format_names); - cliprdr->format_names = NULL; + cb_event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(CliprdrChannel_Class, + CliprdrChannel_FormatList, NULL, NULL); - cliprdr->num_format_names = 0; + if (dataLen > 0) + { + cb_event->raw_format_data = (BYTE*) malloc(dataLen); + memcpy(cb_event->raw_format_data, Stream_Pointer(s), dataLen); + cb_event->raw_format_data_size = dataLen; + cb_event->raw_format_unicode = (msgFlags & CB_ASCII_NAMES) ? FALSE : TRUE; + } - svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event); - cliprdr_send_format_list_response(cliprdr); + if (cliprdr->use_long_format_names) + cliprdr_process_long_format_names(cliprdr, s, dataLen, msgFlags); + else + cliprdr_process_short_format_names(cliprdr, s, dataLen, msgFlags); + + if (cliprdr->num_format_names > 0) + cb_event->formats = (UINT32*) malloc(sizeof(UINT32) * cliprdr->num_format_names); + + cb_event->num_formats = 0; + + for (i = 0; i < cliprdr->num_format_names; i++) + { + supported = TRUE; + format_name = &cliprdr->format_names[i]; + format = format_name->id; + + switch (format) + { + case CB_FORMAT_TEXT: + case CB_FORMAT_DIB: + case CB_FORMAT_UNICODETEXT: + break; + + default: + + if (format_name->length > 0) + { + DEBUG_CLIPRDR("format: %s", format_name->name); + + if (strcmp(format_name->name, "HTML Format") == 0) + { + format = CB_FORMAT_HTML; + break; + } + if (strcmp(format_name->name, "PNG") == 0) + { + format = CB_FORMAT_PNG; + break; + } + if (strcmp(format_name->name, "JFIF") == 0) + { + format = CB_FORMAT_JPEG; + break; + } + if (strcmp(format_name->name, "GIF") == 0) + { + format = CB_FORMAT_GIF; + break; + } + } + else + { + supported = FALSE; + } + + break; + } + + if (supported) + cb_event->formats[cb_event->num_formats++] = format; + + if (format_name->length > 0) + free(format_name->name); + } + + free(cliprdr->format_names); + cliprdr->format_names = NULL; + + cliprdr->num_format_names = 0; + + svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event); + cliprdr_send_format_list_response(cliprdr); + } } void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags) { - /* http://msdn.microsoft.com/en-us/library/hh872154.aspx */ - wMessage* event; + CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); - if ((msgFlags & CB_RESPONSE_FAIL) != 0) + /* http://msdn.microsoft.com/en-us/library/hh872154.aspx */ + + if (context->custom) { - /* In case of an error the clipboard will not be synchronized with the server. - * Post this event to restart format negociation and data transfer. */ - event = freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL); - svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event); + CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse; + + formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE; + formatListResponse.msgFlags = msgFlags; + formatListResponse.dataLen = dataLen; + + if (context->ServerFormatListResponse) + context->ServerFormatListResponse(context, &formatListResponse); + } + else + { + if ((msgFlags & CB_RESPONSE_FAIL) != 0) + { + /* In case of an error the clipboard will not be synchronized with the server. + * Post this event to restart format negociation and data transfer. */ + + wMessage* event = freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL); + svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event); + } } } diff --git a/channels/cliprdr/client/cliprdr_main.c b/channels/cliprdr/client/cliprdr_main.c index 16520be9a..7905e0bf0 100644 --- a/channels/cliprdr/client/cliprdr_main.c +++ b/channels/cliprdr/client/cliprdr_main.c @@ -53,6 +53,14 @@ static const char* const CB_MSG_TYPE_STRINGS[] = "CB_UNLOCK_CLIPDATA" }; +CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr) +{ + CliprdrClientContext* pInterface; + rdpSvcPlugin* plugin = (rdpSvcPlugin*) cliprdr; + pInterface = (CliprdrClientContext*) *(plugin->channel_entry_points.ppInterface); + return pInterface; +} + wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen) { wStream* s; @@ -110,7 +118,9 @@ static void cliprdr_process_general_capability(cliprdrPlugin* cliprdr, wStream* { UINT32 version; UINT32 generalFlags; - RDP_CB_CLIP_CAPS *caps_event; + CliprdrClientContext* context; + + context = cliprdr_get_client_interface(cliprdr); Stream_Read_UINT32(s, version); /* version (4 bytes) */ Stream_Read_UINT32(s, generalFlags); /* generalFlags (4 bytes) */ @@ -121,9 +131,6 @@ static void cliprdr_process_general_capability(cliprdrPlugin* cliprdr, wStream* cliprdr_print_general_capability_flags(generalFlags); #endif - caps_event = (RDP_CB_CLIP_CAPS *)freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_ClipCaps, NULL, NULL); - caps_event->capabilities = generalFlags; - if (generalFlags & CB_USE_LONG_FORMAT_NAMES) cliprdr->use_long_format_names = TRUE; @@ -138,8 +145,32 @@ static void cliprdr_process_general_capability(cliprdrPlugin* cliprdr, wStream* cliprdr->received_caps = TRUE; - svc_plugin_send_event((rdpSvcPlugin *)cliprdr, (wMessage *)caps_event); + if (context->custom) + { + CLIPRDR_CAPABILITIES capabilities; + CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet; + capabilities.cCapabilitiesSets = 1; + capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*) &(generalCapabilitySet); + + generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL; + generalCapabilitySet.capabilitySetLength = 12; + + generalCapabilitySet.version = version; + generalCapabilitySet.generalFlags = generalFlags; + + if (context->ServerCapabilities) + context->ServerCapabilities(context, &capabilities); + } + else + { + RDP_CB_CLIP_CAPS* caps_event; + + caps_event = (RDP_CB_CLIP_CAPS*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_ClipCaps, NULL, NULL); + caps_event->capabilities = generalFlags; + + svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) caps_event); + } } static void cliprdr_process_clip_caps(cliprdrPlugin* cliprdr, wStream* s, UINT16 length, UINT16 flags) @@ -195,14 +226,30 @@ static void cliprdr_send_clip_caps(cliprdrPlugin* cliprdr) static void cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr, wStream* s, UINT16 length, UINT16 flags) { - RDP_CB_MONITOR_READY_EVENT* event; + CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); - if (cliprdr->received_caps) - cliprdr_send_clip_caps(cliprdr); + if (context->custom) + { + CLIPRDR_MONITOR_READY monitorReady; - event = (RDP_CB_MONITOR_READY_EVENT *)freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL); + monitorReady.msgType = CB_MONITOR_READY; + monitorReady.msgFlags = flags; + monitorReady.dataLen = length; - svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage *)event); + if (context->MonitorReady) + context->MonitorReady(context, &monitorReady); + } + else + { + RDP_CB_MONITOR_READY_EVENT* event; + + if (cliprdr->received_caps) + cliprdr_send_clip_caps(cliprdr); + + event = (RDP_CB_MONITOR_READY_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL); + + svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) event); + } } static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s) @@ -290,13 +337,95 @@ static void cliprdr_process_terminate(rdpSvcPlugin* plugin) * Callback Interface */ -int cliprdr_monitor_ready(CliprdrClientContext* context) +int cliprdr_client_capabilities(CliprdrClientContext* context, CLIPRDR_CAPABILITIES* capabilities) { + wStream* s; + CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet; + cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + + s = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN); + + Stream_Write_UINT16(s, 1); /* cCapabilitiesSets */ + Stream_Write_UINT16(s, 0); /* pad1 */ + + generalCapabilitySet = (CLIPRDR_GENERAL_CAPABILITY_SET*) capabilities->capabilitySets; + + Stream_Write_UINT16(s, generalCapabilitySet->capabilitySetType); /* capabilitySetType */ + Stream_Write_UINT16(s, generalCapabilitySet->capabilitySetLength); /* lengthCapability */ + Stream_Write_UINT32(s, generalCapabilitySet->version); /* version */ + Stream_Write_UINT32(s, generalCapabilitySet->generalFlags); /* generalFlags */ + + cliprdr_packet_send(cliprdr, s); + return 0; } -int cliprdr_format_list(CliprdrClientContext* context) +int cliprdr_client_format_list(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST* formatList) { + int index; + wStream* s; + int length = 0; + int formatNameSize; + CLIPRDR_FORMAT* format; + cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + + for (index = 0; index < formatList->cFormats; index++) + { + format = (CLIPRDR_FORMAT*) &(formatList->formats[index]); + + length += 4; + + formatNameSize = 2; + + if (format->formatName) + formatNameSize = MultiByteToWideChar(CP_UTF8, 0, format->formatName, -1, NULL, 0) * 2; + + length += formatNameSize; + } + + s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length); + + for (index = 0; index < formatList->cFormats; index++) + { + format = (CLIPRDR_FORMAT*) &(formatList->formats[index]); + + Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */ + + if (format->formatName) + { + int cchWideChar; + LPWSTR lpWideCharStr; + + lpWideCharStr = (LPWSTR) Stream_Pointer(s); + cchWideChar = (Stream_Capacity(s) - Stream_GetPosition(s)) / 2; + + formatNameSize = MultiByteToWideChar(CP_UTF8, 0, + format->formatName, -1, lpWideCharStr, cchWideChar) * 2; + + Stream_Seek(s, formatNameSize); + } + else + { + Stream_Write_UINT16(s, 0); + } + } + + cliprdr_packet_send(cliprdr, s); + + return 0; +} + +int cliprdr_client_format_list_response(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse) +{ + wStream* s; + cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + + formatListResponse->msgType = CB_FORMAT_LIST_RESPONSE; + formatListResponse->dataLen = 0; + + s = cliprdr_packet_new(formatListResponse->msgType, formatListResponse->msgFlags, formatListResponse->dataLen); + cliprdr_packet_send(cliprdr, s); + return 0; } @@ -315,25 +444,25 @@ int cliprdr_data_response(CliprdrClientContext* context) int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) { - cliprdrPlugin* _p; + cliprdrPlugin* cliprdr; CliprdrClientContext* context; CHANNEL_ENTRY_POINTS_EX* pEntryPointsEx; - _p = (cliprdrPlugin*) malloc(sizeof(cliprdrPlugin)); - ZeroMemory(_p, sizeof(cliprdrPlugin)); + cliprdr = (cliprdrPlugin*) malloc(sizeof(cliprdrPlugin)); + ZeroMemory(cliprdr, sizeof(cliprdrPlugin)); - _p->plugin.channel_def.options = + cliprdr->plugin.channel_def.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL; - strcpy(_p->plugin.channel_def.name, "cliprdr"); + strcpy(cliprdr->plugin.channel_def.name, "cliprdr"); - _p->plugin.connect_callback = cliprdr_process_connect; - _p->plugin.receive_callback = cliprdr_process_receive; - _p->plugin.event_callback = cliprdr_process_event; - _p->plugin.terminate_callback = cliprdr_process_terminate; + cliprdr->plugin.connect_callback = cliprdr_process_connect; + cliprdr->plugin.receive_callback = cliprdr_process_receive; + cliprdr->plugin.event_callback = cliprdr_process_event; + cliprdr->plugin.terminate_callback = cliprdr_process_terminate; pEntryPointsEx = (CHANNEL_ENTRY_POINTS_EX*) pEntryPoints; @@ -341,16 +470,20 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER)) { context = (CliprdrClientContext*) malloc(sizeof(CliprdrClientContext)); + ZeroMemory(context, sizeof(CliprdrClientContext)); - context->MonitorReady = cliprdr_monitor_ready; - context->FormatList = cliprdr_format_list; + context->handle = (void*) cliprdr; + + context->ClientCapabilities = cliprdr_client_capabilities; + context->ClientFormatList = cliprdr_client_format_list; + context->ClientFormatListResponse = cliprdr_client_format_list_response; context->DataRequest = cliprdr_data_request; context->DataResponse = cliprdr_data_response; *(pEntryPointsEx->ppInterface) = (void*) context; } - svc_plugin_init((rdpSvcPlugin*) _p, pEntryPoints); + svc_plugin_init((rdpSvcPlugin*) cliprdr, pEntryPoints); return 1; } diff --git a/channels/cliprdr/client/cliprdr_main.h b/channels/cliprdr/client/cliprdr_main.h index 38d361031..b3eabcb9d 100644 --- a/channels/cliprdr/client/cliprdr_main.h +++ b/channels/cliprdr/client/cliprdr_main.h @@ -41,6 +41,8 @@ typedef struct cliprdr_plugin cliprdrPlugin; wStream* cliprdr_packet_new(UINT16 msgType, UINT16 msgFlags, UINT32 dataLen); void cliprdr_packet_send(cliprdrPlugin* cliprdr, wStream* data_out); +CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr); + #ifdef WITH_DEBUG_CLIPRDR #define DEBUG_CLIPRDR(fmt, ...) DEBUG_CLASS(CLIPRDR, fmt, ## __VA_ARGS__) #else diff --git a/client/Windows/wf_cliprdr.c b/client/Windows/wf_cliprdr.c index 216332f95..031b53a1b 100644 --- a/client/Windows/wf_cliprdr.c +++ b/client/Windows/wf_cliprdr.c @@ -54,7 +54,7 @@ static UINT32 get_local_format_id_by_name(cliprdrContext *cliprdr, void *format_ for (i = 0; i < cliprdr->map_size; i++) { map = &cliprdr->format_mappings[i]; - if ((cliprdr->capabilities & CAPS_USE_LONG_FORMAT_NAMES) != 0) + if ((cliprdr->capabilities & CB_USE_LONG_FORMAT_NAMES) != 0) { if (map->name) { @@ -144,7 +144,7 @@ static void cliprdr_send_format_list(cliprdrContext *cliprdr) { Write_UINT32(format_data + len, format); len += 4; - if ((cliprdr->capabilities & CAPS_USE_LONG_FORMAT_NAMES) != 0) + if ((cliprdr->capabilities & CB_USE_LONG_FORMAT_NAMES) != 0) { if (format >= CF_MAX) { @@ -518,7 +518,7 @@ static void wf_cliprdr_process_cb_format_list_event(wfContext *wfc, RDP_CB_FORMA clear_format_map(cliprdr); - if ((cliprdr->capabilities & CAPS_USE_LONG_FORMAT_NAMES) != 0) + if ((cliprdr->capabilities & CB_USE_LONG_FORMAT_NAMES) != 0) { while (left_size >= 6) { diff --git a/include/freerdp/channels/cliprdr.h b/include/freerdp/channels/cliprdr.h new file mode 100644 index 000000000..31d16d92e --- /dev/null +++ b/include/freerdp/channels/cliprdr.h @@ -0,0 +1,141 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Clipboard Virtual Channel Extension + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_CLIPRDR_H +#define FREERDP_CHANNEL_CLIPRDR_H + +#include +#include + +#define CLIPRDR_SVC_CHANNEL_NAME "cliprdr" + +/** + * Clipboard Formats + */ + +#define CB_FORMAT_RAW 0x0000 +#define CB_FORMAT_TEXT 0x0001 +#define CB_FORMAT_DIB 0x0008 +#define CB_FORMAT_UNICODETEXT 0x000D +#define CB_FORMAT_HTML 0xD010 +#define CB_FORMAT_PNG 0xD011 +#define CB_FORMAT_JPEG 0xD012 +#define CB_FORMAT_GIF 0xD013 + +/* CLIPRDR_HEADER.msgType */ +#define CB_MONITOR_READY 0x0001 +#define CB_FORMAT_LIST 0x0002 +#define CB_FORMAT_LIST_RESPONSE 0x0003 +#define CB_FORMAT_DATA_REQUEST 0x0004 +#define CB_FORMAT_DATA_RESPONSE 0x0005 +#define CB_TEMP_DIRECTORY 0x0006 +#define CB_CLIP_CAPS 0x0007 +#define CB_FILECONTENTS_REQUEST 0x0008 +#define CB_FILECONTENTS_RESPONSE 0x0009 +#define CB_LOCK_CLIPDATA 0x000A +#define CB_UNLOCK_CLIPDATA 0x000B + +/* CLIPRDR_HEADER.msgFlags */ +#define CB_RESPONSE_OK 0x0001 +#define CB_RESPONSE_FAIL 0x0002 +#define CB_ASCII_NAMES 0x0004 + +/* CLIPRDR_CAPS_SET.capabilitySetType */ +#define CB_CAPSTYPE_GENERAL 0x0001 + +/* CLIPRDR_GENERAL_CAPABILITY.lengthCapability */ +#define CB_CAPSTYPE_GENERAL_LEN 12 + +/* CLIPRDR_GENERAL_CAPABILITY.version */ +#define CB_CAPS_VERSION_1 0x00000001 +#define CB_CAPS_VERSION_2 0x00000002 + +/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */ +#define CB_USE_LONG_FORMAT_NAMES 0x00000002 +#define CB_STREAM_FILECLIP_ENABLED 0x00000004 +#define CB_FILECLIP_NO_FILE_PATHS 0x00000008 +#define CB_CAN_LOCK_CLIPDATA 0x00000010 + +#define DEFINE_CLIPRDR_HEADER_COMMON() \ + UINT16 msgType; \ + UINT16 msgFlags; \ + UINT32 dataLen + +struct _CLIPRDR_HEADER +{ + DEFINE_CLIPRDR_HEADER_COMMON(); +}; +typedef struct _CLIPRDR_HEADER CLIPRDR_HEADER; + +struct _CLIPRDR_CAPABILITY_SET +{ + UINT16 capabilitySetType; + UINT16 capabilitySetLength; +}; +typedef struct _CLIPRDR_CAPABILITY_SET CLIPRDR_CAPABILITY_SET; + +struct _CLIPRDR_GENERAL_CAPABILITY_SET +{ + UINT16 capabilitySetType; + UINT16 capabilitySetLength; + + UINT32 version; + UINT32 generalFlags; +}; +typedef struct _CLIPRDR_GENERAL_CAPABILITY_SET CLIPRDR_GENERAL_CAPABILITY_SET; + +struct _CLIPRDR_CAPABILITIES +{ + DEFINE_CLIPRDR_HEADER_COMMON(); + + UINT32 cCapabilitiesSets; + CLIPRDR_CAPABILITY_SET* capabilitySets; +}; +typedef struct _CLIPRDR_CAPABILITIES CLIPRDR_CAPABILITIES; + +struct _CLIPRDR_MONITOR_READY +{ + DEFINE_CLIPRDR_HEADER_COMMON(); +}; +typedef struct _CLIPRDR_MONITOR_READY CLIPRDR_MONITOR_READY; + +struct _CLIPRDR_FORMAT +{ + UINT32 formatId; + char* formatName; +}; +typedef struct _CLIPRDR_FORMAT CLIPRDR_FORMAT; + +struct _CLIPRDR_FORMAT_LIST +{ + DEFINE_CLIPRDR_HEADER_COMMON(); + + UINT32 cFormats; + CLIPRDR_FORMAT* formats; +}; +typedef struct _CLIPRDR_FORMAT_LIST CLIPRDR_FORMAT_LIST; + +struct _CLIPRDR_FORMAT_LIST_RESPONSE +{ + DEFINE_CLIPRDR_HEADER_COMMON(); +}; +typedef struct _CLIPRDR_FORMAT_LIST_RESPONSE CLIPRDR_FORMAT_LIST_RESPONSE; + +#endif /* FREERDP_CHANNEL_CLIPRDR_H */ + diff --git a/include/freerdp/client/cliprdr.h b/include/freerdp/client/cliprdr.h index e7aa94195..641f1eb3a 100644 --- a/include/freerdp/client/cliprdr.h +++ b/include/freerdp/client/cliprdr.h @@ -1,6 +1,6 @@ /** * FreeRDP: A Remote Desktop Protocol Implementation - * Clipboard Virtual Channel Types + * Clipboard Virtual Channel Extension * * Copyright 2011 Vic Lee * @@ -22,72 +22,41 @@ #include +#include +#include + /** * Client Interface */ typedef struct _cliprdr_client_context CliprdrClientContext; -typedef int (*pcCliprdrMonitorReady)(CliprdrClientContext* context); -typedef int (*pcCliprdrFormatList)(CliprdrClientContext* context); +typedef int (*pcCliprdrServerCapabilities)(CliprdrClientContext* context, CLIPRDR_CAPABILITIES* capabilities); +typedef int (*pcCliprdrClientCapabilities)(CliprdrClientContext* context, CLIPRDR_CAPABILITIES* capabilities); +typedef int (*pcCliprdrMonitorReady)(CliprdrClientContext* context, CLIPRDR_MONITOR_READY* monitorReady); +typedef int (*pcCliprdrClientFormatList)(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST* formatList); +typedef int (*pcCliprdrServerFormatList)(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST* formatList); +typedef int (*pcCliprdrClientFormatListResponse)(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse); +typedef int (*pcCliprdrServerFormatListResponse)(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse); typedef int (*pcCliprdrDataRequest)(CliprdrClientContext* context); typedef int (*pcCliprdrDataResponse)(CliprdrClientContext* context); struct _cliprdr_client_context { + void* handle; + void* custom; + + pcCliprdrServerCapabilities ServerCapabilities; + pcCliprdrClientCapabilities ClientCapabilities; pcCliprdrMonitorReady MonitorReady; - pcCliprdrFormatList FormatList; + pcCliprdrClientFormatList ClientFormatList; + pcCliprdrServerFormatList ServerFormatList; + pcCliprdrClientFormatListResponse ClientFormatListResponse; + pcCliprdrServerFormatListResponse ServerFormatListResponse; pcCliprdrDataRequest DataRequest; pcCliprdrDataResponse DataResponse; }; -/** - * Clipboard Formats - */ - -#define CB_FORMAT_RAW 0x0000 -#define CB_FORMAT_TEXT 0x0001 -#define CB_FORMAT_DIB 0x0008 -#define CB_FORMAT_UNICODETEXT 0x000D -#define CB_FORMAT_HTML 0xD010 -#define CB_FORMAT_PNG 0xD011 -#define CB_FORMAT_JPEG 0xD012 -#define CB_FORMAT_GIF 0xD013 - -/* CLIPRDR_HEADER.msgType */ -#define CB_MONITOR_READY 0x0001 -#define CB_FORMAT_LIST 0x0002 -#define CB_FORMAT_LIST_RESPONSE 0x0003 -#define CB_FORMAT_DATA_REQUEST 0x0004 -#define CB_FORMAT_DATA_RESPONSE 0x0005 -#define CB_TEMP_DIRECTORY 0x0006 -#define CB_CLIP_CAPS 0x0007 -#define CB_FILECONTENTS_REQUEST 0x0008 -#define CB_FILECONTENTS_RESPONSE 0x0009 -#define CB_LOCK_CLIPDATA 0x000A -#define CB_UNLOCK_CLIPDATA 0x000B - -/* CLIPRDR_HEADER.msgFlags */ -#define CB_RESPONSE_OK 0x0001 -#define CB_RESPONSE_FAIL 0x0002 -#define CB_ASCII_NAMES 0x0004 - -/* CLIPRDR_CAPS_SET.capabilitySetType */ -#define CB_CAPSTYPE_GENERAL 0x0001 - -/* CLIPRDR_GENERAL_CAPABILITY.lengthCapability */ -#define CB_CAPSTYPE_GENERAL_LEN 12 - -/* CLIPRDR_GENERAL_CAPABILITY.version */ -#define CB_CAPS_VERSION_1 0x00000001 -#define CB_CAPS_VERSION_2 0x00000002 - -/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */ -#define CB_USE_LONG_FORMAT_NAMES 0x00000002 -#define CB_STREAM_FILECLIP_ENABLED 0x00000004 -#define CB_FILECLIP_NO_FILE_PATHS 0x00000008 -#define CB_CAN_LOCK_CLIPDATA 0x00000010 - struct _CLIPRDR_FORMAT_NAME { UINT32 id; @@ -99,10 +68,6 @@ typedef struct _CLIPRDR_FORMAT_NAME CLIPRDR_FORMAT_NAME; /** * Clipboard Events */ -#define CAPS_USE_LONG_FORMAT_NAMES 0x00000002 -#define CAPS_STREAM_FILECLIP_ENABLED 0x00000004 -#define CAPS_FILECLIP_NO_FILE_PATH 0x00000008 -#define CAPS_CAN_LOCK_CLIPDATA 0x00000010 struct _RDP_CB_CLIP_CAPS { diff --git a/include/freerdp/message.h b/include/freerdp/message.h index e5e66dbf2..5369bf7a8 100644 --- a/include/freerdp/message.h +++ b/include/freerdp/message.h @@ -263,10 +263,10 @@ #define CliprdrChannel_ClipCaps 5 #define FREERDP_CLIPRDR_CHANNEL_MONITOR_READY MakeMessageId(CliprdrChannel, MonitorReady) -#define FREERDP_CLIPRDR_CHANNEL_FORMAT_LIST MakeMessageId(CliprdrChannel, FormatList) +#define FREERDP_CLIPRDR_CHANNEL_FORMAT_LIST MakeMessageId(CliprdrChannel, FormatList) #define FREERDP_CLIPRDR_CHANNEL_DATA_REQUEST MakeMessageId(CliprdrChannel, DataRequest) #define FREERDP_CLIPRDR_CHANNEL_DATA_RESPONSE MakeMessageId(CliprdrChannel, DataResponse) -#define FREERDP_CLIPRDR_CHANNEL_CLIP_CAPS MakeMessageId(CliprdrChannel, ClipCaps) +#define FREERDP_CLIPRDR_CHANNEL_CLIP_CAPS MakeMessageId(CliprdrChannel, ClipCaps) /** * Multimedia Redirection Channel From d7379cd4ff29bad9590961e8602ceae4b2814c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 23 Nov 2013 23:45:31 -0500 Subject: [PATCH 036/149] channels/cliprdr: implement more of the callback interface --- channels/cliprdr/client/cliprdr_format.c | 28 ++++++++++++++---- channels/cliprdr/client/cliprdr_main.c | 29 ++++++++++++++++--- include/freerdp/channels/cliprdr.h | 36 ++++++++++++++++++++++++ include/freerdp/client/cliprdr.h | 12 +++++--- 4 files changed, 92 insertions(+), 13 deletions(-) diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index e7a5866ff..2125438fc 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -405,13 +405,31 @@ void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UI void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags) { - RDP_CB_DATA_REQUEST_EVENT* cb_event; + CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); - cb_event = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class, - CliprdrChannel_DataRequest, NULL, NULL); + if (context->custom) + { + CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest; - Stream_Read_UINT32(s, cb_event->format); - svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event); + formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST; + formatDataRequest.msgFlags = msgFlags; + formatDataRequest.dataLen = dataLen; + + Stream_Read_UINT32(s, formatDataRequest.requestedFormatId); /* requestedFormatId (4 bytes) */ + + if (context->ServerFormatDataRequest) + context->ServerFormatDataRequest(context, &formatDataRequest); + } + else + { + RDP_CB_DATA_REQUEST_EVENT* cb_event; + + cb_event = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(CliprdrChannel_Class, + CliprdrChannel_DataRequest, NULL, NULL); + + Stream_Read_UINT32(s, cb_event->format); + svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event); + } } void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_RESPONSE_EVENT* cb_event) diff --git a/channels/cliprdr/client/cliprdr_main.c b/channels/cliprdr/client/cliprdr_main.c index 7905e0bf0..6ffdcf1c5 100644 --- a/channels/cliprdr/client/cliprdr_main.c +++ b/channels/cliprdr/client/cliprdr_main.c @@ -429,13 +429,34 @@ int cliprdr_client_format_list_response(CliprdrClientContext* context, CLIPRDR_F return 0; } -int cliprdr_data_request(CliprdrClientContext* context) +int cliprdr_client_format_data_request(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest) { + wStream* s; + cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + + formatDataRequest->msgType = CB_FORMAT_DATA_REQUEST; + formatDataRequest->dataLen = 4; + + s = cliprdr_packet_new(formatDataRequest->msgType, formatDataRequest->msgFlags, formatDataRequest->dataLen); + Stream_Write_UINT32(s, formatDataRequest->requestedFormatId); /* requestedFormatId (4 bytes) */ + + cliprdr_packet_send(cliprdr, s); + return 0; } -int cliprdr_data_response(CliprdrClientContext* context) +int cliprdr_client_format_data_response(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse) { + wStream* s; + cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; + + formatDataResponse->msgType = CB_FORMAT_DATA_RESPONSE; + + s = cliprdr_packet_new(formatDataResponse->msgType, formatDataResponse->msgFlags, formatDataResponse->dataLen); + Stream_Write(s, formatDataResponse->requestedFormatData, formatDataResponse->dataLen); + + cliprdr_packet_send(cliprdr, s); + return 0; } @@ -477,8 +498,8 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) context->ClientCapabilities = cliprdr_client_capabilities; context->ClientFormatList = cliprdr_client_format_list; context->ClientFormatListResponse = cliprdr_client_format_list_response; - context->DataRequest = cliprdr_data_request; - context->DataResponse = cliprdr_data_response; + context->ClientFormatDataRequest = cliprdr_client_format_data_request; + context->ClientFormatDataResponse = cliprdr_client_format_data_response; *(pEntryPointsEx->ppInterface) = (void*) context; } diff --git a/include/freerdp/channels/cliprdr.h b/include/freerdp/channels/cliprdr.h index 31d16d92e..adb1ffb31 100644 --- a/include/freerdp/channels/cliprdr.h +++ b/include/freerdp/channels/cliprdr.h @@ -29,6 +29,26 @@ * Clipboard Formats */ +#define CLIPRDR_FORMAT_RAW 0 +#define CLIPRDR_FORMAT_TEXT 1 /* "Plain Text" */ +#define CLIPRDR_FORMAT_BITMAP 2 /* "Bitmap" */ +#define CLIPRDR_FORMAT_METAFILEPICT 3 /* "Windows Metafile" */ +#define CLIPRDR_FORMAT_SYLK 4 +#define CLIPRDR_FORMAT_DIF 5 +#define CLIPRDR_FORMAT_TIFF 6 +#define CLIPRDR_FORMAT_OEMTEXT 7 /* "OEM Text" */ +#define CLIPRDR_FORMAT_DIB 8 /* "Device Independent Bitmap (DIB)" */ +#define CLIPRDR_FORMAT_PALETTE 9 +#define CLIPRDR_FORMAT_PENDATA 10 +#define CLIPRDR_FORMAT_RIFF 11 +#define CLIPRDR_FORMAT_WAVE 12 +#define CLIPRDR_FORMAT_UNICODETEXT 13 /* "Unicode Text" */ +#define CLIPRDR_FORMAT_ENHMETAFILE 14 /* "Enhanced Metafile" */ +#define CLIPRDR_FORMAT_HDROP 15 /* "File List" */ +#define CLIPRDR_FORMAT_LOCALE 16 /* "Locale Identifier" */ +#define CLIPRDR_FORMAT_DIBV5 17 +#define CLIPRDR_FORMAT_MAX 18 + #define CB_FORMAT_RAW 0x0000 #define CB_FORMAT_TEXT 0x0001 #define CB_FORMAT_DIB 0x0008 @@ -137,5 +157,21 @@ struct _CLIPRDR_FORMAT_LIST_RESPONSE }; typedef struct _CLIPRDR_FORMAT_LIST_RESPONSE CLIPRDR_FORMAT_LIST_RESPONSE; +struct _CLIPRDR_FORMAT_DATA_REQUEST +{ + DEFINE_CLIPRDR_HEADER_COMMON(); + + UINT32 requestedFormatId; +}; +typedef struct _CLIPRDR_FORMAT_DATA_REQUEST CLIPRDR_FORMAT_DATA_REQUEST; + +struct _CLIPRDR_FORMAT_DATA_RESPONSE +{ + DEFINE_CLIPRDR_HEADER_COMMON(); + + BYTE* requestedFormatData; +}; +typedef struct _CLIPRDR_FORMAT_DATA_RESPONSE CLIPRDR_FORMAT_DATA_RESPONSE; + #endif /* FREERDP_CHANNEL_CLIPRDR_H */ diff --git a/include/freerdp/client/cliprdr.h b/include/freerdp/client/cliprdr.h index 641f1eb3a..b3a0542fa 100644 --- a/include/freerdp/client/cliprdr.h +++ b/include/freerdp/client/cliprdr.h @@ -38,8 +38,10 @@ typedef int (*pcCliprdrClientFormatList)(CliprdrClientContext* context, CLIPRDR_ typedef int (*pcCliprdrServerFormatList)(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST* formatList); typedef int (*pcCliprdrClientFormatListResponse)(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse); typedef int (*pcCliprdrServerFormatListResponse)(CliprdrClientContext* context, CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse); -typedef int (*pcCliprdrDataRequest)(CliprdrClientContext* context); -typedef int (*pcCliprdrDataResponse)(CliprdrClientContext* context); +typedef int (*pcCliprdrClientFormatDataRequest)(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest); +typedef int (*pcCliprdrServerFormatDataRequest)(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest); +typedef int (*pcCliprdrClientFormatDataResponse)(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse); +typedef int (*pcCliprdrServerFormatDataResponse)(CliprdrClientContext* context, CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse); struct _cliprdr_client_context { @@ -53,8 +55,10 @@ struct _cliprdr_client_context pcCliprdrServerFormatList ServerFormatList; pcCliprdrClientFormatListResponse ClientFormatListResponse; pcCliprdrServerFormatListResponse ServerFormatListResponse; - pcCliprdrDataRequest DataRequest; - pcCliprdrDataResponse DataResponse; + pcCliprdrClientFormatDataRequest ClientFormatDataRequest; + pcCliprdrServerFormatDataRequest ServerFormatDataRequest; + pcCliprdrClientFormatDataResponse ClientFormatDataResponse; + pcCliprdrServerFormatDataResponse ServerFormatDataResponse; }; struct _CLIPRDR_FORMAT_NAME From 4fbbc03ac95751c46102f303942f2a3c105fe861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 24 Nov 2013 15:35:26 -0500 Subject: [PATCH 037/149] channels/cliprdr: fix conflict with CLIPRDR_HEADER --- channels/cliprdr/server/cliprdr_main.h | 8 -------- include/freerdp/server/cliprdr.h | 2 ++ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/channels/cliprdr/server/cliprdr_main.h b/channels/cliprdr/server/cliprdr_main.h index 3e201eeb3..71165045b 100644 --- a/channels/cliprdr/server/cliprdr_main.h +++ b/channels/cliprdr/server/cliprdr_main.h @@ -28,14 +28,6 @@ #define CLIPRDR_HEADER_LENGTH 8 -struct _CLIPRDR_HEADER -{ - UINT16 msgType; - UINT16 msgFlags; - UINT32 dataLen; -}; -typedef struct _CLIPRDR_HEADER CLIPRDR_HEADER; - struct _cliprdr_server_private { HANDLE Thread; diff --git a/include/freerdp/server/cliprdr.h b/include/freerdp/server/cliprdr.h index 946eb7652..256e736ae 100644 --- a/include/freerdp/server/cliprdr.h +++ b/include/freerdp/server/cliprdr.h @@ -23,6 +23,8 @@ #include #include #include + +#include #include /** From 8fa7008435133305281fbf515e39e712ef7faf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 24 Nov 2013 20:46:56 -0500 Subject: [PATCH 038/149] channels/cliprdr: add callback for data request response --- channels/cliprdr/client/cliprdr_format.c | 45 ++++++++++++++++++------ channels/cliprdr/client/cliprdr_main.c | 2 +- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index 2125438fc..bcbec672d 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -464,17 +464,42 @@ void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, RDP_CB_DA void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags) { - RDP_CB_DATA_RESPONSE_EVENT* cb_event; - - cb_event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class, - CliprdrChannel_DataResponse, NULL, NULL); - - if (dataLen > 0) + CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr); + + if (context->custom) { - cb_event->size = dataLen; - cb_event->data = (BYTE*) malloc(dataLen); - CopyMemory(cb_event->data, Stream_Pointer(s), dataLen); + CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse; + + formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE; + formatDataResponse.msgFlags = msgFlags; + formatDataResponse.dataLen = dataLen; + formatDataResponse.requestedFormatData = NULL; + + if (dataLen) + { + formatDataResponse.requestedFormatData = (BYTE*) malloc(dataLen); + Stream_Read(s, formatDataResponse.requestedFormatData, dataLen); + } + + if (context->ClientFormatDataResponse) + context->ClientFormatDataResponse(context, &formatDataResponse); + + free(formatDataResponse.requestedFormatData); } + else + { + RDP_CB_DATA_RESPONSE_EVENT* cb_event; - svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event); + cb_event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(CliprdrChannel_Class, + CliprdrChannel_DataResponse, NULL, NULL); + + if (dataLen > 0) + { + cb_event->size = dataLen; + cb_event->data = (BYTE*) malloc(dataLen); + CopyMemory(cb_event->data, Stream_Pointer(s), dataLen); + } + + svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (wMessage*) cb_event); + } } diff --git a/channels/cliprdr/client/cliprdr_main.c b/channels/cliprdr/client/cliprdr_main.c index 6ffdcf1c5..e3fe0b607 100644 --- a/channels/cliprdr/client/cliprdr_main.c +++ b/channels/cliprdr/client/cliprdr_main.c @@ -265,7 +265,7 @@ static void cliprdr_process_receive(rdpSvcPlugin* plugin, wStream* s) DEBUG_CLIPRDR("msgType: %s (%d), msgFlags: %d dataLen: %d", CB_MSG_TYPE_STRINGS[msgType], msgType, msgFlags, dataLen); - + #ifdef WITH_DEBUG_CLIPRDR winpr_HexDump(Stream_Buffer(s), dataLen + 8); #endif From 128fb72ec6291166dd6051a3b3f7079bd75ce40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 25 Nov 2013 00:25:16 -0500 Subject: [PATCH 039/149] mfreerdp: fix possible crash on gdi termination --- channels/cliprdr/client/cliprdr_format.c | 2 +- channels/cliprdr/client/cliprdr_main.c | 1 + client/Mac/MRDPView.m | 14 ++++++++++---- libfreerdp/core/freerdp.c | 3 --- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index bcbec672d..e01cb656d 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -395,7 +395,7 @@ void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UI if ((msgFlags & CB_RESPONSE_FAIL) != 0) { /* In case of an error the clipboard will not be synchronized with the server. - * Post this event to restart format negociation and data transfer. */ + * Post this event to restart format negotiation and data transfer. */ wMessage* event = freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_MonitorReady, NULL, NULL); svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event); diff --git a/channels/cliprdr/client/cliprdr_main.c b/channels/cliprdr/client/cliprdr_main.c index e3fe0b607..24c9e8b07 100644 --- a/channels/cliprdr/client/cliprdr_main.c +++ b/channels/cliprdr/client/cliprdr_main.c @@ -435,6 +435,7 @@ int cliprdr_client_format_data_request(CliprdrClientContext* context, CLIPRDR_FO cliprdrPlugin* cliprdr = (cliprdrPlugin*) context->handle; formatDataRequest->msgType = CB_FORMAT_DATA_REQUEST; + formatDataRequest->msgFlags = 0; formatDataRequest->dataLen = 4; s = cliprdr_packet_new(formatDataRequest->msgType, formatDataRequest->msgFlags, formatDataRequest->dataLen); diff --git a/client/Mac/MRDPView.m b/client/Mac/MRDPView.m index 27341189e..b213ea9d8 100644 --- a/client/Mac/MRDPView.m +++ b/client/Mac/MRDPView.m @@ -1051,6 +1051,10 @@ void mac_bitmap_update(rdpContext* context, BITMAP_UPDATE* bitmap) void mac_begin_paint(rdpContext* context) { rdpGdi* gdi = context->gdi; + + if (!gdi) + return; + gdi->primary->hdc->hwnd->invalid->null = 1; } @@ -1063,11 +1067,15 @@ void mac_end_paint(rdpContext* context) int i; rdpGdi* gdi; NSRect drawRect; + int ww, wh, dw, dh; mfContext* mfc = (mfContext*) context; MRDPView* view = (MRDPView*) mfc->view; - - int ww, wh, dw, dh; + gdi = context->gdi; + + if (!gdi) + return; + ww = mfc->client_width; wh = mfc->client_height; dw = mfc->context.settings->DesktopWidth; @@ -1081,8 +1089,6 @@ void mac_end_paint(rdpContext* context) if (context->gdi->drawing != context->gdi->primary) return; - - gdi = context->gdi; for (i = 0; i < gdi->primary->hdc->hwnd->ninvalid; i++) { diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 6e0f8948b..57ea44526 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -40,7 +40,6 @@ #include #include #include -#include /* connectErrorCode is 'extern' in error.h. See comment there.*/ @@ -448,8 +447,6 @@ void freerdp_context_free(freerdp* instance) graphics_free(instance->context->graphics); instance->context->graphics = NULL; - gdi_free(instance); - PubSub_Free(instance->context->pubSub); free(instance->context); From 4987f2b0e1bd10fe68db2f897014971786a2fc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 25 Nov 2013 12:08:58 -0500 Subject: [PATCH 040/149] libfreerdp-crypto: add robustness checks for VerifyX509Certificate --- libfreerdp/crypto/tls.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 4d5e8c4ca..19dda77c6 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -596,14 +596,33 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname) */ bio = BIO_new(BIO_s_mem()); + + if (!bio) + { + fprintf(stderr, "tls_verify_certificate: BIO_new() failure\n"); + return FALSE; + } status = PEM_write_bio_X509(bio, cert->px509); + if (status < 0) + { + fprintf(stderr, "tls_verify_certificate: PEM_write_bio_X509 failure: %d\n", status); + return FALSE; + } + offset = 0; length = 2048; pemCert = (BYTE*) malloc(length + 1); status = BIO_read(bio, pemCert, length); + + if (status < 0) + { + fprintf(stderr, "tls_verify_certificate: failed to read certificate\n"); + return FALSE; + } + offset += status; while (offset >= length) @@ -619,17 +638,27 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname) offset += status; } + if (status < 0) + { + fprintf(stderr, "tls_verify_certificate: failed to read certificate\n"); + return FALSE; + } + length = offset; pemCert[length] = '\0'; status = -1; - + if (instance->VerifyX509Certificate) { status = instance->VerifyX509Certificate(instance, pemCert, length, 0); } + + fprintf(stderr, "VerifyX509Certificate: (length = %d) status: %d\n%s\n", + length, status, pemCert); free(pemCert); + BIO_free(bio); return (status < 0) ? FALSE : TRUE; } From 56c517170fabf001f81f4e5eb42222d8336112b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Mon, 25 Nov 2013 14:30:43 -0500 Subject: [PATCH 041/149] Added hostname and port to callback function for SSL certification verification. --- include/freerdp/crypto/tls.h | 2 +- include/freerdp/freerdp.h | 2 +- libfreerdp/crypto/tls.c | 13 ++++++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/freerdp/crypto/tls.h b/include/freerdp/crypto/tls.h index 09ff7a3a0..4c1be79bf 100644 --- a/include/freerdp/crypto/tls.h +++ b/include/freerdp/crypto/tls.h @@ -64,7 +64,7 @@ FREERDP_API int tls_wait_read(rdpTls* tls); FREERDP_API int tls_wait_write(rdpTls* tls); FREERDP_API BOOL tls_match_hostname(char *pattern, int pattern_length, char *hostname); -FREERDP_API BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname); +FREERDP_API BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port); FREERDP_API void tls_print_certificate_error(char* hostname, char* fingerprint, char* hosts_file); FREERDP_API void tls_print_certificate_name_mismatch_error(char* hostname, char* common_name, char** alt_names, int alt_names_count); diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 28064c27e..1f769af95 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -61,7 +61,7 @@ typedef void (*pPostDisconnect)(freerdp* instance); typedef BOOL (*pAuthenticate)(freerdp* instance, char** username, char** password, char** domain); typedef BOOL (*pVerifyCertificate)(freerdp* instance, char* subject, char* issuer, char* fingerprint); typedef BOOL (*pVerifyChangedCertificate)(freerdp* instance, char* subject, char* issuer, char* new_fingerprint, char* old_fingerprint); -typedef int (*pVerifyX509Certificate)(freerdp* instance, BYTE* data, int length, DWORD flags); +typedef int (*pVerifyX509Certificate)(freerdp* instance, BYTE* data, int length, const char* hostname, int port, DWORD flags); typedef int (*pLogonErrorInfo)(freerdp* instance, UINT32 data, UINT32 type); diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index b7cda06df..06e5d53b3 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -111,6 +111,7 @@ BOOL tls_connect(rdpTls* tls) long options = 0; int connection_status; char *hostname; + int port; tls->ctx = SSL_CTX_new(TLSv1_client_method()); @@ -214,11 +215,17 @@ BOOL tls_connect(rdpTls* tls) } if (tls->settings->GatewayEnabled) + { hostname = tls->settings->GatewayHostname; + port = tls->settings->GatewayPort; + } else + { hostname = tls->settings->ServerHostname; + port = tls->settings->ServerPort; + } - if (!tls_verify_certificate(tls, cert, hostname)) + if (!tls_verify_certificate(tls, cert, hostname, port)) { fprintf(stderr, "tls_connect: certificate not trusted, aborting.\n"); tls_disconnect(tls); @@ -568,7 +575,7 @@ BOOL tls_match_hostname(char *pattern, int pattern_length, char *hostname) return FALSE; } -BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname) +BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port) { int match; int index; @@ -626,7 +633,7 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname) if (instance->VerifyX509Certificate) { - status = instance->VerifyX509Certificate(instance, pemCert, length, 0); + status = instance->VerifyX509Certificate(instance, pemCert, length, hostname, port, 0); } free(pemCert); From 7446c6f02b8836513c66977b6e976a78acddf902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 25 Nov 2013 18:58:01 -0500 Subject: [PATCH 042/149] libfreerdp-codec: start unit tests for RDP6 planar compressor --- libfreerdp/codec/CMakeLists.txt | 7 + libfreerdp/codec/bitmap_encode.c | 150 -------------- libfreerdp/codec/planar.c | 49 +++++ libfreerdp/codec/planar.h | 60 ++++++ libfreerdp/codec/test/.gitignore | 3 + libfreerdp/codec/test/CMakeLists.txt | 31 +++ .../codec/test/TestFreeRDPCodecPlanar.c | 188 ++++++++++++++++++ libfreerdp/codec/test/test01.bmp | Bin 0 -> 4150 bytes 8 files changed, 338 insertions(+), 150 deletions(-) create mode 100644 libfreerdp/codec/planar.c create mode 100644 libfreerdp/codec/planar.h create mode 100644 libfreerdp/codec/test/.gitignore create mode 100644 libfreerdp/codec/test/CMakeLists.txt create mode 100644 libfreerdp/codec/test/TestFreeRDPCodecPlanar.c create mode 100644 libfreerdp/codec/test/test01.bmp diff --git a/libfreerdp/codec/CMakeLists.txt b/libfreerdp/codec/CMakeLists.txt index 2c2363e84..d017b67df 100644 --- a/libfreerdp/codec/CMakeLists.txt +++ b/libfreerdp/codec/CMakeLists.txt @@ -22,6 +22,8 @@ set(${MODULE_PREFIX}_SRCS dsp.c color.c audio.c + planar.c + planar.h bitmap_decode.c bitmap_encode.c rfx_bitstream.h @@ -106,3 +108,8 @@ else() endif() set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/libfreerdp") + +if(BUILD_TESTING) + add_subdirectory(test) +endif() + diff --git a/libfreerdp/codec/bitmap_encode.c b/libfreerdp/codec/bitmap_encode.c index 84ab3b14a..5cdca0b93 100644 --- a/libfreerdp/codec/bitmap_encode.c +++ b/libfreerdp/codec/bitmap_encode.c @@ -1572,153 +1572,3 @@ int freerdp_bitmap_compress(char* in_data, int width, int height, return lines_sent; } - -/** - * RDP6 Bitmap Test Case ([MS-RDPEGDI]) - */ - -const BYTE TEST_RDP6_COMPRESSED_BITMAP[220] = - "\x85\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x06\x8B\x99\xD6\x99" - "\xD6\x99\xD6\x10\x84\x08\x42\x08\x42\x10\x84\x99\xD6\x99\xD6\x99" - "\xD6\x99\xD6\x06\x84\x99\xD6\x99\xD6\x99\xD6\xFF\xFF\x16\x69\x99" - "\xD6\x06\x69\x99\xD6\x04\xCC\x89\x52\x03\x6E\xFF\xFF\x02\x6E\x08" - "\x42\x01\x70\x08\x42\x71\xFF\xFF\xCE\x18\xC6\x01\x81\x08\x42\xCE" - "\x66\x29\x02\xCD\x89\x52\x03\x88\x10\x84\x99\xD6\x99\xD6\x99\xD6" - "\x00\x00\x00\x00\x00\x00\x00\x00\xD8\x99\xD6\x03\xF8\x01\x00\x00" - "\x00\x00\xF0\x66\x99\xD6\x05\x6A\x99\xD6\x00\xC4\xCC\x89\x52\x03" - "\x6E\xFF\xFF\x02\x6E\x08\x42\x01\x70\x08\x42\x71\xFF\xFF\xCE\x18" - "\xC6\x01\x81\x08\x42\xCE\x66\x29\x02\xCD\x89\x52\x03\x00\x04\xD6" - "\x99\xD6\xC3\x80\x61\x00\xA5\x80\x40\xEC\x52\x00\x5A\x00\x2D\x00" - "\x24\x00\x12\x00\x24\x00\x12\x00\x5A\x00\x2D\x00\xA5\x80\x52\x00" - "\xC3\x80\x61\x00\x00\x00\x00\x00\xCC\x89\x52\x03\x6E\xFF\xFF\x02" - "\xCB\x18\xC6\x84\x08\x42\x08\x42\x08\x42\xFF\xFF"; - -const BYTE TEST_RDP6_UNCOMPRESSED_BITMAP[2048] = - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" - "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x99\xD6\xFF\xFF" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x42" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\xFF\xFF" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - "\xFF\xFF\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" - "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x08\x42" - "\x08\x42\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" - "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x99\xD6\xFF\xFF" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x42" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\xFF\xFF" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - "\xFF\xFF\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" - "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x08\x42" - "\x08\x42\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" - "\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6" - "\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00" - "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" - "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00" - "\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00" - "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" - "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" - "\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6" - "\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" - "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" - "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" - "\x08\x42\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" - "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x99\xD6\xFF\xFF" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x42" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" - "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\xFF\xFF"; diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c new file mode 100644 index 000000000..dc337bf5f --- /dev/null +++ b/libfreerdp/codec/planar.c @@ -0,0 +1,49 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDP6 Planar Codec + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "planar.h" + +int freerdp_split_color_planes(BYTE* data, int width, int height, int scanline, BYTE* planes[4]) +{ + BYTE* srcp; + int i, j, k; + + k = 0; + srcp = data; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + planes[0][k] = data[(j * 4) + 0]; + planes[1][k] = data[(j * 4) + 1]; + planes[2][k] = data[(j * 4) + 2]; + planes[3][k] = data[(j * 4) + 3]; + k++; + } + + srcp += scanline; + } + + return 0; +} diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h new file mode 100644 index 000000000..665cd014b --- /dev/null +++ b/libfreerdp/codec/planar.h @@ -0,0 +1,60 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDP6 Planar Codec + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CODEC_PLANAR_PRIVATE_H +#define FREERDP_CODEC_PLANAR_PRIVATE_H + +#include + +struct _RDP6_RLE_SEGMENT +{ + /** + * controlByte: + * [0-3]: nRunLength + * [4-7]: cRawBytes + */ + BYTE controlByte; + BYTE* rawValues; +}; +typedef struct _RDP6_RLE_SEGMENT RDP6_RLE_SEGMENT; + +struct _RDP6_RLE_SEGMENTS +{ + UINT32 cSegments; + RDP6_RLE_SEGMENT* segments; +}; +typedef struct _RDP6_RLE_SEGMENTS RDP6_RLE_SEGMENTS; + +struct _RDP6_BITMAP_STREAM +{ + /** + * formatHeader: + * [0-2]: CCL + * [3] : CS + * [4] : RLE + * [5] : NA + * [6-7]: Reserved + */ + BYTE formatHeader; +}; +typedef struct _RDP6_BITMAP_STREAM RDP6_BITMAP_STREAM; + +int freerdp_split_color_planes(BYTE* data, int width, int height, int scanline, BYTE* planes[4]); + +#endif /* FREERDP_CODEC_PLANAR_PRIVATE_H */ diff --git a/libfreerdp/codec/test/.gitignore b/libfreerdp/codec/test/.gitignore new file mode 100644 index 000000000..9fe3a139c --- /dev/null +++ b/libfreerdp/codec/test/.gitignore @@ -0,0 +1,3 @@ +TestFreeRDPCodec +TestFreeRDPCodec.c + diff --git a/libfreerdp/codec/test/CMakeLists.txt b/libfreerdp/codec/test/CMakeLists.txt new file mode 100644 index 000000000..e1bced145 --- /dev/null +++ b/libfreerdp/codec/test/CMakeLists.txt @@ -0,0 +1,31 @@ + +set(MODULE_NAME "TestFreeRDPCodec") +set(MODULE_PREFIX "TEST_FREERDP_CODEC") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestFreeRDPCodecPlanar.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE freerdp + MODULES freerdp-codec) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Test") + diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c new file mode 100644 index 000000000..3be743c13 --- /dev/null +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -0,0 +1,188 @@ + +#include +#include + +/** + * [MS-RDPEGDI] Test Bitmap 32x32 (16bpp) + */ + +const BYTE TEST_RLE_UNCOMPRESSED_BITMAP_16BPP[2048] = + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" + "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x99\xD6\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x42" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\xFF\xFF" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" + "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x08\x42" + "\x08\x42\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" + "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x99\xD6\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x42" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\xFF\xFF" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" + "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x08\x42" + "\x08\x42\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" + "\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6" + "\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00" + "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" + "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00" + "\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00" + "\x00\x00\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" + "\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00" + "\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6" + "\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x00\x00\x00\x00\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\xFF\xFF" + "\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6" + "\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x10\x84\x08\x42" + "\x08\x42\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84" + "\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x10\x84\x99\xD6\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x42" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42" + "\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\x08\x42\xFF\xFF"; + +/** + * [MS-RDPEGDI] Test Bitmap 32x32 (RLE Encoded, not RDP6) + */ + +const BYTE TEST_RLE_COMPRESSED_BITMAP[220] = + "\x85\xFF\xFF\x99\xD6\x99\xD6\x99\xD6\x99\xD6\x06\x8B\x99\xD6\x99" + "\xD6\x99\xD6\x10\x84\x08\x42\x08\x42\x10\x84\x99\xD6\x99\xD6\x99" + "\xD6\x99\xD6\x06\x84\x99\xD6\x99\xD6\x99\xD6\xFF\xFF\x16\x69\x99" + "\xD6\x06\x69\x99\xD6\x04\xCC\x89\x52\x03\x6E\xFF\xFF\x02\x6E\x08" + "\x42\x01\x70\x08\x42\x71\xFF\xFF\xCE\x18\xC6\x01\x81\x08\x42\xCE" + "\x66\x29\x02\xCD\x89\x52\x03\x88\x10\x84\x99\xD6\x99\xD6\x99\xD6" + "\x00\x00\x00\x00\x00\x00\x00\x00\xD8\x99\xD6\x03\xF8\x01\x00\x00" + "\x00\x00\xF0\x66\x99\xD6\x05\x6A\x99\xD6\x00\xC4\xCC\x89\x52\x03" + "\x6E\xFF\xFF\x02\x6E\x08\x42\x01\x70\x08\x42\x71\xFF\xFF\xCE\x18" + "\xC6\x01\x81\x08\x42\xCE\x66\x29\x02\xCD\x89\x52\x03\x00\x04\xD6" + "\x99\xD6\xC3\x80\x61\x00\xA5\x80\x40\xEC\x52\x00\x5A\x00\x2D\x00" + "\x24\x00\x12\x00\x24\x00\x12\x00\x5A\x00\x2D\x00\xA5\x80\x52\x00" + "\xC3\x80\x61\x00\x00\x00\x00\x00\xCC\x89\x52\x03\x6E\xFF\xFF\x02" + "\xCB\x18\xC6\x84\x08\x42\x08\x42\x08\x42\xFF\xFF"; + +#include "../planar.h" + +int TestFreeRDPCodecPlanar(int argc, char* argv[]) +{ + BYTE* planes[4]; + HCLRCONV clrconv; + BYTE planeA[1024]; + BYTE planeR[1024]; + BYTE planeG[1024]; + BYTE planeB[1024]; + BYTE* srcBitmap32; + BYTE* srcBitmap16; + + clrconv = freerdp_clrconv_new(0); + srcBitmap16 = (BYTE*) TEST_RLE_UNCOMPRESSED_BITMAP_16BPP; + + srcBitmap32 = freerdp_image_convert(srcBitmap16, NULL, 32, 32, 16, 32, clrconv); + + planes[0] = planeA; + planes[1] = planeR; + planes[2] = planeG; + planes[3] = planeB; + + freerdp_split_color_planes(srcBitmap32, 32, 32, 32 * 4, planes); + + freerdp_clrconv_free(clrconv); + free(srcBitmap32); + + return 0; +} diff --git a/libfreerdp/codec/test/test01.bmp b/libfreerdp/codec/test/test01.bmp new file mode 100644 index 0000000000000000000000000000000000000000..fea498f3da2fccfdbf9dde0be18f9fb0db647707 GIT binary patch literal 4150 zcmeHIL23gr4BY&nPv|i($h~{-r%K+JOQ7feWb0WNm=2b_76d|xL6I!YcqC`DSwAmt z%f2q{^W5yc+0V9Zr#)($R!0uT)ym85{{6>TbFaAT*L7Jx*4+?m4RN#-2kl4ct4A8Kf-!Go8Pg~{-xi4|3>$v`Pco&zkj`_EdEc=AD)L9!@Y3+s^|N+>Y-cZ zl2`3rc{BH>SoV-TV_A(oWSFRiKB$j4!e#kK>GMC-wLRa{cMQ+){Ahm1`yS`^^pWR^ zw2>d_>p6ZN&yDntaF6KgIn-32>5sTa^f@=ZY9r3{$)R`dNZ)zB&-5MZdvG)T9v&Pn l&eEITF~oX4((8c-er61FaQ>?2`?u<$`wNb>^Tz-H literal 0 HcmV?d00001 From d30656d4417738bbd7cfb199cf74e0176899a1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 25 Nov 2013 22:26:08 -0500 Subject: [PATCH 043/149] libfreerdp-codec: start implementing uncompressed RDP6 planar codec --- include/freerdp/codec/bitmap.h | 5 + include/freerdp/codec/color.h | 14 ++ libfreerdp/codec/planar.c | 130 ++++++++++++++++-- libfreerdp/codec/planar.h | 16 ++- .../codec/test/TestFreeRDPCodecPlanar.c | 17 +-- 5 files changed, 153 insertions(+), 29 deletions(-) diff --git a/include/freerdp/codec/bitmap.h b/include/freerdp/codec/bitmap.h index c652a921b..1dc7c25d9 100644 --- a/include/freerdp/codec/bitmap.h +++ b/include/freerdp/codec/bitmap.h @@ -23,6 +23,8 @@ #include #include +#include + #include #include @@ -31,4 +33,7 @@ FREERDP_API BOOL bitmap_decompress(BYTE* srcData, BYTE* dstData, int width, int FREERDP_API int freerdp_bitmap_compress(char* in_data, int width, int height, wStream* s, int bpp, int byte_limit, int start_line, wStream* temp_s, int e); +FREERDP_API BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int height, + int scanline, BYTE* dstData, int* dstSize); + #endif /* FREERDP_CODEC_BITMAP_H */ diff --git a/include/freerdp/codec/color.h b/include/freerdp/codec/color.h index e73af8d3b..858db3779 100644 --- a/include/freerdp/codec/color.h +++ b/include/freerdp/codec/color.h @@ -23,6 +23,20 @@ #include #include +#define FREERDP_PIXEL_FORMAT_TYPE_ARGB 1 +#define FREERDP_PIXEL_FORMAT_TYPE_ABGR 2 + +#define FREERDP_PIXEL_FLIP_NONE 0 +#define FREERDP_PIXEL_FLIP_VERTICAL 1 +#define FREERDP_PIXEL_FLIP_HORIZONTAL 2 + +#define FREERDP_PIXEL_FORMAT(_bpp, _type, _flip) \ + ((_bpp << 24) | (_type << 16) | (_flip << 8)) + +#define FREERDP_PIXEL_FORMAT_BPP(_format) (((_format) >> 24) & 0xFF) +#define FREERDP_PIXEL_FORMAT_TYPE(_format) (((_format) >> 16) & 0xFF) +#define FREERDP_PIXEL_FORMAT_FLIP(_format) (((_format) >> 8) & 0xFF) + #ifdef __cplusplus extern "C" { #endif diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index dc337bf5f..5f1dea38a 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -21,29 +21,135 @@ #include "config.h" #endif +#include +#include + +#include + #include "planar.h" -int freerdp_split_color_planes(BYTE* data, int width, int height, int scanline, BYTE* planes[4]) +int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]) { + int bpp; BYTE* srcp; int i, j, k; k = 0; srcp = data; - for (i = 0; i < height; i++) - { - for (j = 0; j < width; j++) - { - planes[0][k] = data[(j * 4) + 0]; - planes[1][k] = data[(j * 4) + 1]; - planes[2][k] = data[(j * 4) + 2]; - planes[3][k] = data[(j * 4) + 3]; - k++; - } + bpp = FREERDP_PIXEL_FORMAT_BPP(format); - srcp += scanline; + if (bpp == 32) + { + UINT32 pixel; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + pixel = *((UINT32*) srcp); + GetARGB32(planes[0][k], planes[0][k], planes[2][k], planes[3][k], pixel); + k++; + } + + srcp += scanline; + } + } + else if (bpp == 24) + { + UINT32 pixel; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + pixel = *((UINT32*) srcp); + GetRGB32(planes[0][k], planes[2][k], planes[3][k], pixel); + planes[0][k] = 0; /* A */ + k++; + } + + srcp += scanline; + } } return 0; } + +BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* dstData, int* dstSize) +{ + int size; + BYTE* dstp; + int planeSize; + BYTE* planes[4]; + BYTE FormatHeader; + BYTE* planesBuffer; + + FormatHeader = 0; + FormatHeader |= PLANAR_FORMAT_HEADER_NA; + + planeSize = width; + planesBuffer = malloc(planeSize * 4); + planes[0] = &planesBuffer[planeSize * 0]; + planes[1] = &planesBuffer[planeSize * 1]; + planes[2] = &planesBuffer[planeSize * 2]; + planes[3] = &planesBuffer[planeSize * 3]; + + freerdp_split_color_planes(data, format, width, height, scanline, planes); + + if (!dstData) + { + size = 2; + + if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA)) + size += planeSize; + + size += (planeSize * 3); + + dstData = malloc(size); + *dstSize = size; + } + + dstp = dstData; + + *dstp = FormatHeader; /* FormatHeader */ + dstp++; + + /* AlphaPlane */ + + if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA)) + { + CopyMemory(dstp, planes[0], planeSize); /* Alpha */ + dstp += planeSize; + } + + /* LumaOrRedPlane */ + + CopyMemory(dstp, planes[1], planeSize); /* Red */ + dstp += planeSize; + + /* OrangeChromaOrGreenPlane */ + + CopyMemory(dstp, planes[2], planeSize); /* Green */ + dstp += planeSize; + + /* GreenChromeOrBluePlane */ + + CopyMemory(dstp, planes[3], planeSize); /* Blue */ + dstp += planeSize; + + /* Pad1 (1 byte) */ + + if (!(FormatHeader & PLANAR_FORMAT_HEADER_RLE)) + { + *dstp = 0; + dstp++; + } + + size = (dstp - dstData); + *dstSize = size; + + free(planesBuffer); + + return dstData; +} diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h index 665cd014b..cc4a378e7 100644 --- a/libfreerdp/codec/planar.h +++ b/libfreerdp/codec/planar.h @@ -22,6 +22,8 @@ #include +#include + struct _RDP6_RLE_SEGMENT { /** @@ -41,20 +43,24 @@ struct _RDP6_RLE_SEGMENTS }; typedef struct _RDP6_RLE_SEGMENTS RDP6_RLE_SEGMENTS; +#define PLANAR_FORMAT_HEADER_CS (1 << 3) +#define PLANAR_FORMAT_HEADER_RLE (1 << 4) +#define PLANAR_FORMAT_HEADER_NA (1 << 5) + struct _RDP6_BITMAP_STREAM { /** * formatHeader: - * [0-2]: CCL - * [3] : CS - * [4] : RLE - * [5] : NA + * [0-2]: Color Loss Level (CLL) + * [3] : Chroma Subsampling (CS) + * [4] : Run Length Encoding (RLE) + * [5] : No Alpha (NA) * [6-7]: Reserved */ BYTE formatHeader; }; typedef struct _RDP6_BITMAP_STREAM RDP6_BITMAP_STREAM; -int freerdp_split_color_planes(BYTE* data, int width, int height, int scanline, BYTE* planes[4]); +int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]); #endif /* FREERDP_CODEC_PLANAR_PRIVATE_H */ diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 3be743c13..abda7e6f2 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -1,6 +1,7 @@ #include #include +#include /** * [MS-RDPEGDI] Test Bitmap 32x32 (16bpp) @@ -156,16 +157,11 @@ const BYTE TEST_RLE_COMPRESSED_BITMAP[220] = "\xC3\x80\x61\x00\x00\x00\x00\x00\xCC\x89\x52\x03\x6E\xFF\xFF\x02" "\xCB\x18\xC6\x84\x08\x42\x08\x42\x08\x42\xFF\xFF"; -#include "../planar.h" - int TestFreeRDPCodecPlanar(int argc, char* argv[]) { - BYTE* planes[4]; + int dstSize; + UINT32 format; HCLRCONV clrconv; - BYTE planeA[1024]; - BYTE planeR[1024]; - BYTE planeG[1024]; - BYTE planeB[1024]; BYTE* srcBitmap32; BYTE* srcBitmap16; @@ -174,12 +170,9 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) srcBitmap32 = freerdp_image_convert(srcBitmap16, NULL, 32, 32, 16, 32, clrconv); - planes[0] = planeA; - planes[1] = planeR; - planes[2] = planeG; - planes[3] = planeB; + format = FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_ARGB, FREERDP_PIXEL_FLIP_NONE); - freerdp_split_color_planes(srcBitmap32, 32, 32, 32 * 4, planes); + freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); freerdp_clrconv_free(clrconv); free(srcBitmap32); From c8c75fd4e02428ea9539a7a5ca4b6e7f3efb212b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 25 Nov 2013 23:29:20 -0500 Subject: [PATCH 044/149] libfreerdp-codec: implement working uncompressed RDP6 bitmap planar codec --- libfreerdp/codec/planar.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 5f1dea38a..1bf9f626b 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -31,45 +31,42 @@ int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]) { int bpp; - BYTE* srcp; int i, j, k; k = 0; - srcp = data; - bpp = FREERDP_PIXEL_FORMAT_BPP(format); if (bpp == 32) { - UINT32 pixel; + UINT32* pixel; - for (i = 0; i < height; i++) + for (i = height - 1; i >= 0; i--) { + pixel = (UINT32*) &data[scanline * i]; + for (j = 0; j < width; j++) { - pixel = *((UINT32*) srcp); - GetARGB32(planes[0][k], planes[0][k], planes[2][k], planes[3][k], pixel); + GetARGB32(planes[0][k], planes[0][k], planes[2][k], planes[3][k], *pixel); + pixel++; k++; } - - srcp += scanline; } } else if (bpp == 24) { - UINT32 pixel; + UINT32* pixel; - for (i = 0; i < height; i++) + for (i = height - 1; i >= 0; i--) { + pixel = (UINT32*) &data[scanline * i]; + for (j = 0; j < width; j++) { - pixel = *((UINT32*) srcp); - GetRGB32(planes[0][k], planes[2][k], planes[3][k], pixel); - planes[0][k] = 0; /* A */ + GetRGB32(planes[1][k], planes[2][k], planes[3][k], *pixel); + planes[0][k] = 0xFF; /* A */ + pixel++; k++; } - - srcp += scanline; } } @@ -88,7 +85,7 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h FormatHeader = 0; FormatHeader |= PLANAR_FORMAT_HEADER_NA; - planeSize = width; + planeSize = width * height; planesBuffer = malloc(planeSize * 4); planes[0] = &planesBuffer[planeSize * 0]; planes[1] = &planesBuffer[planeSize * 1]; From f429b909a9a0785ae46c0268432547753c2b9d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 26 Nov 2013 11:30:44 -0500 Subject: [PATCH 045/149] libfreerdp-core: fix transport_free in cases where transport thread was not started --- libfreerdp/core/transport.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index efd29faba..bd3e75b6a 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -1062,8 +1062,17 @@ void transport_free(rdpTransport* transport) { if (transport->async) { - assert(!transport->thread); - assert(!transport->stopEvent); + if (transport->stopEvent) + { + SetEvent(transport->stopEvent); + WaitForSingleObject(transport->thread, INFINITE); + + CloseHandle(transport->thread); + CloseHandle(transport->stopEvent); + + transport->thread = NULL; + transport->stopEvent = NULL; + } } if (transport->ReceiveBuffer) From 021ef0533eb6429934d307152ab3ff7d0ec39e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 26 Nov 2013 15:16:40 -0500 Subject: [PATCH 046/149] libfreerdp-codec: implement planar codec RLE scanline encoding --- libfreerdp/codec/planar.c | 74 +++++++++++++++++++ libfreerdp/codec/planar.h | 1 + .../codec/test/TestFreeRDPCodecPlanar.c | 7 ++ 3 files changed, 82 insertions(+) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 1bf9f626b..bf2985e7b 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -73,6 +73,80 @@ int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, return 0; } +int freerdp_bitmap_compress_planar_rle_plane_scanline(BYTE* plane, int size) +{ + int i, j; + BYTE symbol; + int nRunLength; + int cRawBytes; + BYTE* rawValues; + int nSequenceLength; + + cRawBytes = 0; + nRunLength = 0; + rawValues = plane; + + nSequenceLength = 0; + + for (i = 0; i <= size; i++) + { + if ((!nSequenceLength) && (i != size)) + { + symbol = plane[i]; + nSequenceLength = 1; + } + else + { + if ((i != size) && (plane[i] == symbol)) + { + nSequenceLength++; + } + else + { + if (nSequenceLength > 3) + { + cRawBytes += 1; + nRunLength = nSequenceLength - 1; + + printf("RAW["); + + for (j = 0; j < cRawBytes; j++) + printf("%c", rawValues[j]); + + printf("] RUN[%d]\n", nRunLength); + + rawValues = &plane[i]; + cRawBytes = 0; + } + else + { + cRawBytes += nSequenceLength; + + if (i == size) + { + nRunLength = 0; + + printf("RAW["); + + for (j = 0; j < cRawBytes; j++) + printf("%c", rawValues[j]); + + printf("] RUN[%d]\n", nRunLength); + } + } + + if (i != size) + { + symbol = plane[i]; + nSequenceLength = 1; + } + } + } + } + + return 0; +} + BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* dstData, int* dstSize) { int size; diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h index cc4a378e7..7d6f1bffc 100644 --- a/libfreerdp/codec/planar.h +++ b/libfreerdp/codec/planar.h @@ -62,5 +62,6 @@ struct _RDP6_BITMAP_STREAM typedef struct _RDP6_BITMAP_STREAM RDP6_BITMAP_STREAM; int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]); +int freerdp_bitmap_compress_planar_rle_plane_scanline(BYTE* plane, int size); #endif /* FREERDP_CODEC_PLANAR_PRIVATE_H */ diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index abda7e6f2..0e28b10e5 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -157,6 +157,11 @@ const BYTE TEST_RLE_COMPRESSED_BITMAP[220] = "\xC3\x80\x61\x00\x00\x00\x00\x00\xCC\x89\x52\x03\x6E\xFF\xFF\x02" "\xCB\x18\xC6\x84\x08\x42\x08\x42\x08\x42\xFF\xFF"; +const BYTE TEST_RLE_SCANLINE_UNCOMPRESSED[12] = + "AAAABBCCCCCD"; + +#include "../planar.h" + int TestFreeRDPCodecPlanar(int argc, char* argv[]) { int dstSize; @@ -174,6 +179,8 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); + freerdp_bitmap_compress_planar_rle_plane_scanline((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12); + freerdp_clrconv_free(clrconv); free(srcBitmap32); From 52a1b328f2f101f37e5fc6a008a7bc8f9ef846c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 26 Nov 2013 16:07:55 -0500 Subject: [PATCH 047/149] libfreerdp-codec: start implementing delta-encoding of planar scanlines --- libfreerdp/codec/planar.c | 88 +++++++++++++++++++ libfreerdp/codec/planar.h | 1 + .../codec/test/TestFreeRDPCodecPlanar.c | 51 +++++++++++ 3 files changed, 140 insertions(+) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index bf2985e7b..b7bb78bbd 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -147,6 +147,94 @@ int freerdp_bitmap_compress_planar_rle_plane_scanline(BYTE* plane, int size) return 0; } +int freerdp_bitmap_planar_delta_encode_scanlines(BYTE* plane, int width, int height) +{ + char s2c; + int delta; + int i, j, k; + + k = 0; + + for (i = 0; i < height; i++) + { + printf("{ "); + + for (j = 0; j < width; j++) + { + printf("%4d%s", plane[k], + (j + 1 == width) ? " }\n" : ", "); + + k++; + } + } + + printf("\n"); + + k = 0; + + for (i = 0; i < height; i++) + { + printf("{ "); + + for (j = 0; j < width; j++) + { + if (i < 1) + { + delta = plane[j]; + + printf("%4d%s", delta, + (j + 1 == width) ? " }\n" : ", "); + } + else + { + delta = plane[(i * width) + j] - plane[((i - 1) * width) + j]; + + printf("%4d%s", (int) delta, + (j + 1 == width) ? " }\n" : ", "); + } + + k++; + } + } + + printf("\n"); + + k = 0; + + for (i = 0; i < height; i++) + { + printf("{ "); + + for (j = 0; j < width; j++) + { + if (i < 1) + { + delta = plane[j]; + + s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); + + printf("%4d%s", s2c, + (j + 1 == width) ? " }\n" : ", "); + } + else + { + delta = plane[(i * width) + j] - plane[((i - 1) * width) + j]; + + s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); + + printf("%4d%s", s2c, + (j + 1 == width) ? " }\n" : ", "); + } + + k++; + } + } + + printf("\n"); + + return 0; +} + BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* dstData, int* dstSize) { int size; diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h index 7d6f1bffc..dc919f453 100644 --- a/libfreerdp/codec/planar.h +++ b/libfreerdp/codec/planar.h @@ -63,5 +63,6 @@ typedef struct _RDP6_BITMAP_STREAM RDP6_BITMAP_STREAM; int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]); int freerdp_bitmap_compress_planar_rle_plane_scanline(BYTE* plane, int size); +int freerdp_bitmap_planar_delta_encode_scanlines(BYTE* plane, int width, int height); #endif /* FREERDP_CODEC_PLANAR_PRIVATE_H */ diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 0e28b10e5..f0596a697 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -160,6 +160,55 @@ const BYTE TEST_RLE_COMPRESSED_BITMAP[220] = const BYTE TEST_RLE_SCANLINE_UNCOMPRESSED[12] = "AAAABBCCCCCD"; +/** + * [MS-RDPEGDI] 3.1.9.2.1 Encoding Run-Length Sequences + */ + +/* Scanline Absolute Values */ + +const BYTE TEST_RDP6_SCANLINES_ABSOLUTE[3][6] = +{ + { 255, 255, 255, 255, 254, 253 }, + { 254, 192, 132, 96, 75, 25 }, + { 253, 140, 62, 14, 135, 193 } +}; + +/* Scanline Delta Values */ + +const int TEST_RDP6_SCANLINES_DELTA[3][6] = +{ + { 255, 255, 255, 255, 254, 253 }, + { -1, -63, -123, -159, -179, -228 }, + { -1, -52, -70, -82, 60, 168 } +}; + +/* Scanline Delta Values (1-byte two's complement) */ + +const char TEST_RDP6_SCANLINES_DELTA_2C[3][6] = +{ + { -1, -1, -1, -1, -2, -3 }, + { -1, -63, -123, 97, 77, 28 }, + { -1, -52, -70, -82, 60, -88 } +}; + +/* Scanline Delta Values (1-byte two's complement, encoded) */ + +const char TEST_RDP6_SCANLINES_DELTA_2C_ENCODED[3][6] = +{ + { -1, -1, -1, -1, -2, -3 }, + { -1, 125, 11, -62, -102, 56 }, + { 1, 103, -117, -93, 120, -81 } +}; + +/* Scanline Delta Values (1-byte two's complement, encoded, unsigned) */ + +const BYTE TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED[3][6] = +{ + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD }, + { 0x01, 0x7D, 0xF5, 0xC2, 0x9A, 0x38 }, + { 0x01, 0x67, 0x8B, 0xA3, 0x78, 0xAF } +}; + #include "../planar.h" int TestFreeRDPCodecPlanar(int argc, char* argv[]) @@ -181,6 +230,8 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_bitmap_compress_planar_rle_plane_scanline((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12); + freerdp_bitmap_planar_delta_encode_scanlines((BYTE*) TEST_RDP6_SCANLINES_ABSOLUTE, 6, 3); + freerdp_clrconv_free(clrconv); free(srcBitmap32); From c64e10444b638af67d707be0f662afcea7ee37b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 26 Nov 2013 16:30:43 -0500 Subject: [PATCH 048/149] libfreerdp-codec: get all steps of planar scanline delta encoding tested --- libfreerdp/codec/planar.c | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index b7bb78bbd..2336ae480 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -150,6 +150,7 @@ int freerdp_bitmap_compress_planar_rle_plane_scanline(BYTE* plane, int size) int freerdp_bitmap_planar_delta_encode_scanlines(BYTE* plane, int width, int height) { char s2c; + BYTE u2c; int delta; int i, j, k; @@ -232,6 +233,72 @@ int freerdp_bitmap_planar_delta_encode_scanlines(BYTE* plane, int width, int hei printf("\n"); + k = 0; + + for (i = 0; i < height; i++) + { + printf("{ "); + + for (j = 0; j < width; j++) + { + if (i < 1) + { + delta = plane[j]; + + s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); + } + else + { + delta = plane[(i * width) + j] - plane[((i - 1) * width) + j]; + + s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); + + s2c = (s2c >= 0) ? (s2c << 1) : (char) (((~((BYTE) s2c) + 1) << 1) - 1); + } + + printf("%4d%s", s2c, + (j + 1 == width) ? " }\n" : ", "); + + k++; + } + } + + printf("\n"); + + k = 0; + + for (i = 0; i < height; i++) + { + printf("{ "); + + for (j = 0; j < width; j++) + { + if (i < 1) + { + delta = plane[j]; + + s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); + } + else + { + delta = plane[(i * width) + j] - plane[((i - 1) * width) + j]; + + s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); + + s2c = (s2c >= 0) ? (s2c << 1) : (char) (((~((BYTE) s2c) + 1) << 1) - 1); + } + + u2c = (BYTE) s2c; + + printf("0x%02X%s", u2c, + (j + 1 == width) ? " }\n" : ", "); + + k++; + } + } + + printf("\n"); + return 0; } From 919b6c666a623480b4a0ad907c963060bfbe3a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 26 Nov 2013 18:04:29 -0500 Subject: [PATCH 049/149] libfreerdp-codec: compress and output RDP6 RLE planes --- libfreerdp/codec/planar.c | 318 +++++++++--------- libfreerdp/codec/planar.h | 13 +- .../codec/test/TestFreeRDPCodecPlanar.c | 8 +- 3 files changed, 172 insertions(+), 167 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 2336ae480..9bfb1e899 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -28,7 +28,7 @@ #include "planar.h" -int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]) +int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]) { int bpp; int i, j, k; @@ -73,215 +73,179 @@ int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, return 0; } -int freerdp_bitmap_compress_planar_rle_plane_scanline(BYTE* plane, int size) +BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane) { - int i, j; + BYTE* dstp; + BYTE* segp; BYTE symbol; + int i, j, k; + int cSegments; int nRunLength; int cRawBytes; BYTE* rawValues; + int outPlaneSize; int nSequenceLength; - cRawBytes = 0; - nRunLength = 0; - rawValues = plane; + cSegments = 0; + outPlaneSize = width * height; - nSequenceLength = 0; + if (!outPlane) + outPlane = malloc(outPlaneSize); - for (i = 0; i <= size; i++) + dstp = outPlane; + + for (i = 0; i < height; i++) { - if ((!nSequenceLength) && (i != size)) + cRawBytes = 0; + nRunLength = 0; + nSequenceLength = 0; + rawValues = &inPlane[i * width]; + + for (j = 0; j <= width; j++) { - symbol = plane[i]; - nSequenceLength = 1; - } - else - { - if ((i != size) && (plane[i] == symbol)) + if ((!nSequenceLength) && (j != width)) { - nSequenceLength++; + symbol = inPlane[(i * width) + j]; + nSequenceLength = 1; } else { - if (nSequenceLength > 3) + if ((j != width) && (inPlane[(i * width) + j] == symbol)) { - cRawBytes += 1; - nRunLength = nSequenceLength - 1; - - printf("RAW["); - - for (j = 0; j < cRawBytes; j++) - printf("%c", rawValues[j]); - - printf("] RUN[%d]\n", nRunLength); - - rawValues = &plane[i]; - cRawBytes = 0; + nSequenceLength++; } else { - cRawBytes += nSequenceLength; - - if (i == size) + if (nSequenceLength > 3) { - nRunLength = 0; + cRawBytes += 1; + nRunLength = nSequenceLength - 1; printf("RAW["); - for (j = 0; j < cRawBytes; j++) - printf("%c", rawValues[j]); + for (k = 0; k < cRawBytes; k++) + { + printf("0x%02X%s", rawValues[k], + ((k + 1) == cRawBytes) ? "" : ", "); + } printf("] RUN[%d]\n", nRunLength); - } - } - if (i != size) - { - symbol = plane[i]; - nSequenceLength = 1; + segp = dstp; + + if (((segp - outPlane) + cRawBytes + 1) > outPlaneSize) + { + printf("overflow: %d > %d\n", + ((dstp - outPlane) + cRawBytes + 1), + outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + printf("Segment %d\n", ++cSegments); + winpr_HexDump(segp, dstp - segp); + + rawValues = &inPlane[(i * width) + j]; + cRawBytes = 0; + } + else + { + cRawBytes += nSequenceLength; + + if (j == width) + { + nRunLength = 0; + + printf("RAW["); + + for (k = 0; k < cRawBytes; k++) + { + printf("0x%02X%s", rawValues[k], + ((k + 1) == cRawBytes) ? "" : ", "); + } + + printf("] RUN[%d]\n", nRunLength); + + segp = dstp; + + if (((segp - outPlane) + cRawBytes + 1) > outPlaneSize) + { + printf("overflow: %d > %d\n", + ((dstp - outPlane) + cRawBytes + 1), + outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + printf("Segment %d\n", ++cSegments); + winpr_HexDump(segp, dstp - segp); + } + } + + if (j != width) + { + symbol = inPlane[(i * width) + j]; + nSequenceLength = 1; + } } } } + + printf("---\n"); } + printf("\n"); + + return outPlane; +} + +int freerdp_bitmap_planar_compress_scanlines_rle(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5]) +{ + freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes[0]); + freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes[1]); + freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes[2]); + freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes[3]); + return 0; } -int freerdp_bitmap_planar_delta_encode_scanlines(BYTE* plane, int width, int height) +BYTE* freerdp_bitmap_planar_delta_encode_scanlines(BYTE* inPlane, int width, int height, BYTE* outPlane) { char s2c; BYTE u2c; int delta; int i, j, k; - k = 0; - - for (i = 0; i < height; i++) + if (!outPlane) { - printf("{ "); - - for (j = 0; j < width; j++) - { - printf("%4d%s", plane[k], - (j + 1 == width) ? " }\n" : ", "); - - k++; - } + outPlane = (BYTE*) malloc(width * height); } - printf("\n"); - k = 0; for (i = 0; i < height; i++) { - printf("{ "); - for (j = 0; j < width; j++) { if (i < 1) { - delta = plane[j]; - - printf("%4d%s", delta, - (j + 1 == width) ? " }\n" : ", "); - } - else - { - delta = plane[(i * width) + j] - plane[((i - 1) * width) + j]; - - printf("%4d%s", (int) delta, - (j + 1 == width) ? " }\n" : ", "); - } - - k++; - } - } - - printf("\n"); - - k = 0; - - for (i = 0; i < height; i++) - { - printf("{ "); - - for (j = 0; j < width; j++) - { - if (i < 1) - { - delta = plane[j]; - - s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); - - printf("%4d%s", s2c, - (j + 1 == width) ? " }\n" : ", "); - } - else - { - delta = plane[(i * width) + j] - plane[((i - 1) * width) + j]; - - s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); - - printf("%4d%s", s2c, - (j + 1 == width) ? " }\n" : ", "); - } - - k++; - } - } - - printf("\n"); - - k = 0; - - for (i = 0; i < height; i++) - { - printf("{ "); - - for (j = 0; j < width; j++) - { - if (i < 1) - { - delta = plane[j]; + delta = inPlane[j]; s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); } else { - delta = plane[(i * width) + j] - plane[((i - 1) * width) + j]; - - s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); - - s2c = (s2c >= 0) ? (s2c << 1) : (char) (((~((BYTE) s2c) + 1) << 1) - 1); - } - - printf("%4d%s", s2c, - (j + 1 == width) ? " }\n" : ", "); - - k++; - } - } - - printf("\n"); - - k = 0; - - for (i = 0; i < height; i++) - { - printf("{ "); - - for (j = 0; j < width; j++) - { - if (i < 1) - { - delta = plane[j]; - - s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); - } - else - { - delta = plane[(i * width) + j] - plane[((i - 1) * width) + j]; + delta = inPlane[(i * width) + j] - inPlane[((i - 1) * width) + j]; s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); @@ -290,14 +254,21 @@ int freerdp_bitmap_planar_delta_encode_scanlines(BYTE* plane, int width, int hei u2c = (BYTE) s2c; - printf("0x%02X%s", u2c, - (j + 1 == width) ? " }\n" : ", "); + outPlane[(i * width) + j] = u2c; k++; } } - printf("\n"); + return outPlane; +} + +int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5]) +{ + freerdp_bitmap_planar_delta_encode_scanlines(inPlanes[0], width, height, outPlanes[0]); + freerdp_bitmap_planar_delta_encode_scanlines(inPlanes[1], width, height, outPlanes[1]); + freerdp_bitmap_planar_delta_encode_scanlines(inPlanes[2], width, height, outPlanes[2]); + freerdp_bitmap_planar_delta_encode_scanlines(inPlanes[3], width, height, outPlanes[3]); return 0; } @@ -307,22 +278,45 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h int size; BYTE* dstp; int planeSize; - BYTE* planes[4]; - BYTE FormatHeader; + BYTE* planes[5]; BYTE* planesBuffer; + BYTE* deltaPlanes[5]; + BYTE* deltaPlanesBuffer; + BYTE* rlePlanes[5]; + BYTE* rlePlanesBuffer; + BYTE FormatHeader = 0; - FormatHeader = 0; FormatHeader |= PLANAR_FORMAT_HEADER_NA; planeSize = width * height; - planesBuffer = malloc(planeSize * 4); + + planesBuffer = malloc(planeSize * 5); planes[0] = &planesBuffer[planeSize * 0]; planes[1] = &planesBuffer[planeSize * 1]; planes[2] = &planesBuffer[planeSize * 2]; planes[3] = &planesBuffer[planeSize * 3]; + planes[4] = &planesBuffer[planeSize * 4]; + + deltaPlanesBuffer = malloc(planeSize * 5); + deltaPlanes[0] = &deltaPlanesBuffer[planeSize * 0]; + deltaPlanes[1] = &deltaPlanesBuffer[planeSize * 1]; + deltaPlanes[2] = &deltaPlanesBuffer[planeSize * 2]; + deltaPlanes[3] = &deltaPlanesBuffer[planeSize * 3]; + deltaPlanes[4] = &deltaPlanesBuffer[planeSize * 4]; + + rlePlanesBuffer = malloc(planeSize * 5); + rlePlanes[0] = &rlePlanesBuffer[planeSize * 0]; + rlePlanes[1] = &rlePlanesBuffer[planeSize * 1]; + rlePlanes[2] = &rlePlanesBuffer[planeSize * 2]; + rlePlanes[3] = &rlePlanesBuffer[planeSize * 3]; + rlePlanes[4] = &rlePlanesBuffer[planeSize * 4]; freerdp_split_color_planes(data, format, width, height, scanline, planes); + freerdp_bitmap_planar_delta_encode_planes(planes, width, height, deltaPlanes); + + freerdp_bitmap_planar_compress_scanlines_rle(deltaPlanes, width, height, rlePlanes); + if (!dstData) { size = 2; @@ -375,6 +369,8 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h size = (dstp - dstData); *dstSize = size; + free(rlePlanesBuffer); + free(deltaPlanesBuffer); free(planesBuffer); return dstData; diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h index dc919f453..bc5e7060e 100644 --- a/libfreerdp/codec/planar.h +++ b/libfreerdp/codec/planar.h @@ -24,6 +24,12 @@ #include +#define PLANAR_CONTROL_BYTE(_nRunLength, _cRawBytes) \ + (_nRunLength & 0x0F) | ((_cRawBytes & 0x0F) << 4) + +#define PLANAR_CONTROL_BYTE_RUN_LENGTH(_controlByte) (_controlByte & 0x0F) +#define PLANAR_CONTROL_BYTE_RAW_BYTES(_controlByte) ((_controlByte >> 4) & 0x0F) + struct _RDP6_RLE_SEGMENT { /** @@ -61,8 +67,9 @@ struct _RDP6_BITMAP_STREAM }; typedef struct _RDP6_BITMAP_STREAM RDP6_BITMAP_STREAM; -int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]); -int freerdp_bitmap_compress_planar_rle_plane_scanline(BYTE* plane, int size); -int freerdp_bitmap_planar_delta_encode_scanlines(BYTE* plane, int width, int height); +int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]); +BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* plane, int width, int height, BYTE* outPlane); +BYTE* freerdp_bitmap_planar_delta_encode_scanlines(BYTE* inPlane, int width, int height, BYTE* outPlane); +int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5]); #endif /* FREERDP_CODEC_PLANAR_PRIVATE_H */ diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index f0596a697..f20fc885b 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -226,11 +226,13 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) format = FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_ARGB, FREERDP_PIXEL_FLIP_NONE); - freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); + //freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); - freerdp_bitmap_compress_planar_rle_plane_scanline((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12); + //freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12, 1, NULL); - freerdp_bitmap_planar_delta_encode_scanlines((BYTE*) TEST_RDP6_SCANLINES_ABSOLUTE, 6, 3); + //freerdp_bitmap_planar_delta_encode_scanlines((BYTE*) TEST_RDP6_SCANLINES_ABSOLUTE, 6, 3, NULL); + + freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL); freerdp_clrconv_free(clrconv); free(srcBitmap32); From 5bcdab95ff5f6ae3e2b4e9c794ae39adaf001c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 26 Nov 2013 22:16:31 -0500 Subject: [PATCH 050/149] libfreerdp-codec: partial planar RLE compression support --- client/X11/xf_graphics.c | 2 +- libfreerdp/codec/bitmap_decode.c | 47 ++- libfreerdp/codec/planar.c | 277 +++++++++++++++--- libfreerdp/codec/planar.h | 4 +- .../codec/test/TestFreeRDPCodecPlanar.c | 8 +- 5 files changed, 267 insertions(+), 71 deletions(-) diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c index b3e296966..2c281d33d 100644 --- a/client/X11/xf_graphics.c +++ b/client/X11/xf_graphics.c @@ -182,7 +182,7 @@ void xf_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, { status = bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp); - if (status == FALSE) + if (!status) { fprintf(stderr, "xf_Bitmap_Decompress: Bitmap Decompression Failed\n"); } diff --git a/libfreerdp/codec/bitmap_decode.c b/libfreerdp/codec/bitmap_decode.c index 431a213a8..96c48a520 100644 --- a/libfreerdp/codec/bitmap_decode.c +++ b/libfreerdp/codec/bitmap_decode.c @@ -388,53 +388,52 @@ static BOOL bitmap_decompress4(BYTE* srcData, BYTE* dstData, int width, int heig int RLE; int code; int NoAlpha; - int bytes_processed; - int total_processed; + int bytesProcessed; + int totalProcessed; code = IN_UINT8_MV(srcData); RLE = code & 0x10; - total_processed = 1; + totalProcessed = 1; NoAlpha = code & 0x20; if (NoAlpha == 0) { - bytes_processed = process_rle_plane(srcData, width, height, dstData + 3, size - total_processed); - total_processed += bytes_processed; - srcData += bytes_processed; + bytesProcessed = process_rle_plane(srcData, width, height, dstData + 3, size - totalProcessed); + totalProcessed += bytesProcessed; + srcData += bytesProcessed; } if (RLE != 0) { - bytes_processed = process_rle_plane(srcData, width, height, dstData + 2, size - total_processed); - total_processed += bytes_processed; - srcData += bytes_processed; + bytesProcessed = process_rle_plane(srcData, width, height, dstData + 2, size - totalProcessed); + totalProcessed += bytesProcessed; + srcData += bytesProcessed; - bytes_processed = process_rle_plane(srcData, width, height, dstData + 1, size - total_processed); - total_processed += bytes_processed; - srcData += bytes_processed; + bytesProcessed = process_rle_plane(srcData, width, height, dstData + 1, size - totalProcessed); + totalProcessed += bytesProcessed; + srcData += bytesProcessed; - bytes_processed = process_rle_plane(srcData, width, height, dstData + 0, size - total_processed); - total_processed += bytes_processed; + bytesProcessed = process_rle_plane(srcData, width, height, dstData + 0, size - totalProcessed); + totalProcessed += bytesProcessed; } else { - bytes_processed = process_raw_plane(srcData, width, height, dstData + 2, size - total_processed); - total_processed += bytes_processed; - srcData += bytes_processed; + bytesProcessed = process_raw_plane(srcData, width, height, dstData + 2, size - totalProcessed); + totalProcessed += bytesProcessed; + srcData += bytesProcessed; - bytes_processed = process_raw_plane(srcData, width, height, dstData + 1, size - total_processed); - total_processed += bytes_processed; - srcData += bytes_processed; + bytesProcessed = process_raw_plane(srcData, width, height, dstData + 1, size - totalProcessed); + totalProcessed += bytesProcessed; + srcData += bytesProcessed; - bytes_processed = process_raw_plane(srcData, width, height, dstData + 0, size - total_processed); - total_processed += bytes_processed + 1; + bytesProcessed = process_raw_plane(srcData, width, height, dstData + 0, size - totalProcessed); + totalProcessed += bytesProcessed + 1; } - return (size == total_processed) ? TRUE : FALSE; + return (size == totalProcessed) ? TRUE : FALSE; } - /** * bitmap decompression routine */ diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 9bfb1e899..2fc1a913e 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -73,17 +73,19 @@ int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, return 0; } -BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane) +BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int* dstSize) { + int i, j; BYTE* dstp; BYTE* segp; BYTE symbol; - int i, j, k; int cSegments; int nRunLength; int cRawBytes; BYTE* rawValues; int outPlaneSize; + int outSegmentSize; + int nControlBytes; int nSequenceLength; cSegments = 0; @@ -121,6 +123,7 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei cRawBytes += 1; nRunLength = nSequenceLength - 1; +#if 0 printf("RAW["); for (k = 0; k < cRawBytes; k++) @@ -130,25 +133,149 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei } printf("] RUN[%d]\n", nRunLength); +#endif segp = dstp; + outSegmentSize = cRawBytes + 1; - if (((segp - outPlane) + cRawBytes + 1) > outPlaneSize) + if (cRawBytes > 15) { - printf("overflow: %d > %d\n", - ((dstp - outPlane) + cRawBytes + 1), - outPlaneSize); + //printf("cRawBytes > 15 unhandled\n"); return NULL; } - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; + if (nRunLength > 47) + { + printf("nRunLength > 47 unhandled\n"); + //return NULL; - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; + nControlBytes = (((nRunLength - 15) + 46) / 47) + 1; - printf("Segment %d\n", ++cSegments); - winpr_HexDump(segp, dstp - segp); + outSegmentSize = cRawBytes + nControlBytes; + + if (((segp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", + ((dstp - outPlane) + outSegmentSize), + outPlaneSize); + return NULL; + } + + printf(">47 nRunLength: %d nControlBytes: %d\n", nRunLength, nControlBytes); + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + printf("ControlByte nRunLength: %d cRawBytes: %d\n", 15, cRawBytes); + nRunLength -= 15; + nControlBytes--; + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + while (nControlBytes--) + { + printf("...nRunLength: %d\n", nRunLength); + + if (nRunLength > 47) + { + *dstp = PLANAR_CONTROL_BYTE(2, (47 - 32)); + printf("ControlByte nRunLength: %d cRawBytes: %d\n", 47, 0); + nRunLength -= 47; + dstp++; + } + else if (nRunLength > 31) + { + *dstp = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); + printf("ControlByte nRunLength: %d cRawBytes: %d\n", nRunLength, 0); + nRunLength = 0; + dstp++; + } + else if (nRunLength > 15) + { + *dstp = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); + printf("ControlByte nRunLength: %d cRawBytes: %d\n", nRunLength, 0); + nRunLength = 0; + dstp++; + } + else + { + *dstp = PLANAR_CONTROL_BYTE(0, nRunLength); + printf("ControlByte nRunLength: %d cRawBytes: %d\n", nRunLength, 0); + nRunLength = 0; + dstp++; + } + } + + return NULL; + } + else if (nRunLength > 31) + { + nControlBytes = 2; + outSegmentSize = cRawBytes + nControlBytes; + + if (((segp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", + ((dstp - outPlane) + outSegmentSize), + outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + nRunLength -= 32; + *dstp = PLANAR_CONTROL_BYTE(2, nRunLength); + dstp++; + } + else if (nRunLength > 15) + { + nControlBytes = 2; + outSegmentSize = cRawBytes + nControlBytes; + + if (((segp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", + ((dstp - outPlane) + outSegmentSize), + outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + nRunLength -= 16; + *dstp = PLANAR_CONTROL_BYTE(1, nRunLength); + dstp++; + } + else + { + nControlBytes = 1; + outSegmentSize = cRawBytes + nControlBytes; + + if (((segp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", + ((dstp - outPlane) + cRawBytes + 1), + outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + } + + //printf("Segment %d\n", ++cSegments); + //winpr_HexDump(segp, dstp - segp); rawValues = &inPlane[(i * width) + j]; cRawBytes = 0; @@ -161,6 +288,13 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei { nRunLength = 0; + if (cRawBytes > 15) + { + //printf("cRawBytes > 15 unhandled\n"); + return NULL; + } + +#if 0 printf("RAW["); for (k = 0; k < cRawBytes; k++) @@ -170,10 +304,12 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei } printf("] RUN[%d]\n", nRunLength); +#endif segp = dstp; + outSegmentSize = cRawBytes + 1; - if (((segp - outPlane) + cRawBytes + 1) > outPlaneSize) + if (((segp - outPlane) + outSegmentSize) > outPlaneSize) { printf("overflow: %d > %d\n", ((dstp - outPlane) + cRawBytes + 1), @@ -187,8 +323,8 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei CopyMemory(dstp, rawValues, cRawBytes); dstp += cRawBytes; - printf("Segment %d\n", ++cSegments); - winpr_HexDump(segp, dstp - segp); + //printf("Segment %d\n", ++cSegments); + //winpr_HexDump(segp, dstp - segp); } } @@ -201,25 +337,34 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei } } - printf("---\n"); + //printf("---\n"); } - printf("\n"); + //printf("\n"); + + *dstSize = (dstp - outPlane); return outPlane; } -int freerdp_bitmap_planar_compress_scanlines_rle(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5]) +int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5], int* dstSizes) { - freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes[0]); - freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes[1]); - freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes[2]); - freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes[3]); + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes[0], &dstSizes[0])) + return 0; - return 0; + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes[1], &dstSizes[1])) + return 0; + + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes[2], &dstSizes[2])) + return 0; + + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes[3], &dstSizes[3])) + return 0; + + return 1; } -BYTE* freerdp_bitmap_planar_delta_encode_scanlines(BYTE* inPlane, int width, int height, BYTE* outPlane) +BYTE* freerdp_bitmap_planar_delta_encode_plane(BYTE* inPlane, int width, int height, BYTE* outPlane) { char s2c; BYTE u2c; @@ -265,10 +410,10 @@ BYTE* freerdp_bitmap_planar_delta_encode_scanlines(BYTE* inPlane, int width, int int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5]) { - freerdp_bitmap_planar_delta_encode_scanlines(inPlanes[0], width, height, outPlanes[0]); - freerdp_bitmap_planar_delta_encode_scanlines(inPlanes[1], width, height, outPlanes[1]); - freerdp_bitmap_planar_delta_encode_scanlines(inPlanes[2], width, height, outPlanes[2]); - freerdp_bitmap_planar_delta_encode_scanlines(inPlanes[3], width, height, outPlanes[3]); + freerdp_bitmap_planar_delta_encode_plane(inPlanes[0], width, height, outPlanes[0]); + freerdp_bitmap_planar_delta_encode_plane(inPlanes[1], width, height, outPlanes[1]); + freerdp_bitmap_planar_delta_encode_plane(inPlanes[2], width, height, outPlanes[2]); + freerdp_bitmap_planar_delta_encode_plane(inPlanes[3], width, height, outPlanes[3]); return 0; } @@ -278,6 +423,7 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h int size; BYTE* dstp; int planeSize; + int dstSizes[5]; BYTE* planes[5]; BYTE* planesBuffer; BYTE* deltaPlanes[5]; @@ -315,16 +461,35 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h freerdp_bitmap_planar_delta_encode_planes(planes, width, height, deltaPlanes); - freerdp_bitmap_planar_compress_scanlines_rle(deltaPlanes, width, height, rlePlanes); + if (freerdp_bitmap_planar_compress_planes_rle(deltaPlanes, width, height, rlePlanes, (int*) &dstSizes) > 0) + { + FormatHeader |= PLANAR_FORMAT_HEADER_RLE; + + printf("R: [%d/%d] G: [%d/%d] B: [%d/%d]\n", + dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize); + + printf("RDP6 planar compression size: %d width: %d height: %d\n", size, width, height); + } if (!dstData) { - size = 2; + size = 1; if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA)) - size += planeSize; + { + if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) + size += dstSizes[0]; + else + size += planeSize; + } - size += (planeSize * 3); + if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) + size += (dstSizes[1] + dstSizes[2] + dstSizes[3]); + else + size += (planeSize * 3); + + if (!(FormatHeader & PLANAR_FORMAT_HEADER_RLE)) + size++; dstData = malloc(size); *dstSize = size; @@ -339,24 +504,56 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA)) { - CopyMemory(dstp, planes[0], planeSize); /* Alpha */ - dstp += planeSize; + if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) + { + CopyMemory(dstp, rlePlanes[0], dstSizes[0]); /* Alpha */ + dstp += dstSizes[0]; + } + else + { + CopyMemory(dstp, planes[0], planeSize); /* Alpha */ + dstp += planeSize; + } } /* LumaOrRedPlane */ - CopyMemory(dstp, planes[1], planeSize); /* Red */ - dstp += planeSize; + if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) + { + CopyMemory(dstp, rlePlanes[1], dstSizes[1]); /* Red */ + dstp += dstSizes[1]; + } + else + { + CopyMemory(dstp, planes[1], planeSize); /* Red */ + dstp += planeSize; + } /* OrangeChromaOrGreenPlane */ - CopyMemory(dstp, planes[2], planeSize); /* Green */ - dstp += planeSize; + if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) + { + CopyMemory(dstp, rlePlanes[2], dstSizes[2]); /* Green */ + dstp += dstSizes[2]; + } + else + { + CopyMemory(dstp, planes[2], planeSize); /* Green */ + dstp += planeSize; + } /* GreenChromeOrBluePlane */ - CopyMemory(dstp, planes[3], planeSize); /* Blue */ - dstp += planeSize; + if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) + { + CopyMemory(dstp, rlePlanes[3], dstSizes[3]); /* Blue */ + dstp += dstSizes[3]; + } + else + { + CopyMemory(dstp, planes[3], planeSize); /* Blue */ + dstp += planeSize; + } /* Pad1 (1 byte) */ diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h index bc5e7060e..0315a14e9 100644 --- a/libfreerdp/codec/planar.h +++ b/libfreerdp/codec/planar.h @@ -68,8 +68,8 @@ struct _RDP6_BITMAP_STREAM typedef struct _RDP6_BITMAP_STREAM RDP6_BITMAP_STREAM; int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]); -BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* plane, int width, int height, BYTE* outPlane); -BYTE* freerdp_bitmap_planar_delta_encode_scanlines(BYTE* inPlane, int width, int height, BYTE* outPlane); +BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* plane, int width, int height, BYTE* outPlane, int* dstSize); +BYTE* freerdp_bitmap_planar_delta_encode_plane(BYTE* inPlane, int width, int height, BYTE* outPlane); int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5]); #endif /* FREERDP_CODEC_PLANAR_PRIVATE_H */ diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index f20fc885b..a0d58ff4f 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -226,13 +226,13 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) format = FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_ARGB, FREERDP_PIXEL_FLIP_NONE); - //freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); + freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); - //freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12, 1, NULL); + freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12, 1, NULL, &dstSize); - //freerdp_bitmap_planar_delta_encode_scanlines((BYTE*) TEST_RDP6_SCANLINES_ABSOLUTE, 6, 3, NULL); + freerdp_bitmap_planar_delta_encode_plane((BYTE*) TEST_RDP6_SCANLINES_ABSOLUTE, 6, 3, NULL); - freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL); + freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL, &dstSize); freerdp_clrconv_free(clrconv); free(srcBitmap32); From 7d3ce08e96d90094323291dcd683085b3ed563fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 27 Nov 2013 14:46:38 -0500 Subject: [PATCH 051/149] libfreerdp-codec: simply planar RLE code --- libfreerdp/codec/planar.c | 383 ++++++++++++++++++-------------------- 1 file changed, 181 insertions(+), 202 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 2fc1a913e..2fe044df4 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -77,7 +77,6 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei { int i, j; BYTE* dstp; - BYTE* segp; BYTE symbol; int cSegments; int nRunLength; @@ -86,7 +85,6 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei int outPlaneSize; int outSegmentSize; int nControlBytes; - int nSequenceLength; cSegments = 0; outPlaneSize = width * height; @@ -99,29 +97,190 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei for (i = 0; i < height; i++) { cRawBytes = 0; - nRunLength = 0; - nSequenceLength = 0; + nRunLength = -1; rawValues = &inPlane[i * width]; for (j = 0; j <= width; j++) { - if ((!nSequenceLength) && (j != width)) + if ((nRunLength < 0) && (j != width)) { symbol = inPlane[(i * width) + j]; - nSequenceLength = 1; + nRunLength = 0; + continue; + } + + if ((j != width) && (inPlane[(i * width) + j] == symbol)) + { + nRunLength++; } else { - if ((j != width) && (inPlane[(i * width) + j] == symbol)) + if (nRunLength >= 3) { - nSequenceLength++; + cRawBytes += 1; + +#if 0 + printf("RAW["); + + for (k = 0; k < cRawBytes; k++) + { + printf("0x%02X%s", rawValues[k], + ((k + 1) == cRawBytes) ? "" : ", "); + } + + printf("] RUN[%d]\n", nRunLength); +#endif + + while (cRawBytes > 15) + { + printf("handling cRawBytes > 15\n"); + + nControlBytes = 1; + outSegmentSize = 15 + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(0, 15); + dstp++; + + CopyMemory(dstp, rawValues, 15); + cRawBytes -= 15; + rawValues += 15; + dstp += 15; + + return NULL; + } + + if (nRunLength > 47) + { + nControlBytes = (((nRunLength - 15) + 46) / 47) + 1; + + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + nRunLength -= 15; + nControlBytes--; + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + while (nControlBytes--) + { + if (nRunLength > 47) + { + *dstp = PLANAR_CONTROL_BYTE(2, (47 - 32)); + nRunLength -= 47; + dstp++; + } + else if (nRunLength > 31) + { + *dstp = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); + nRunLength = 0; + dstp++; + } + else if (nRunLength > 15) + { + *dstp = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); + nRunLength = 0; + dstp++; + } + else + { + *dstp = PLANAR_CONTROL_BYTE(0, nRunLength); + nRunLength = 0; + dstp++; + } + } + + return NULL; + } + else if (nRunLength > 31) + { + nControlBytes = 2; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + nRunLength -= 32; + *dstp = PLANAR_CONTROL_BYTE(2, nRunLength); + dstp++; + } + else if (nRunLength > 15) + { + nControlBytes = 2; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + nRunLength -= 16; + *dstp = PLANAR_CONTROL_BYTE(1, nRunLength); + dstp++; + } + else + { + nControlBytes = 1; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + } + + rawValues = &inPlane[(i * width) + j]; + cRawBytes = 0; } else { - if (nSequenceLength > 3) + cRawBytes += (nRunLength + 1); + + if (j == width) { - cRawBytes += 1; - nRunLength = nSequenceLength - 1; + nRunLength = 0; + + if (cRawBytes > 15) + { + printf("cRawBytes > 15 unhandled\n"); + return NULL; + } #if 0 printf("RAW["); @@ -135,204 +294,26 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei printf("] RUN[%d]\n", nRunLength); #endif - segp = dstp; outSegmentSize = cRawBytes + 1; - if (cRawBytes > 15) + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) { - //printf("cRawBytes > 15 unhandled\n"); + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); return NULL; } - if (nRunLength > 47) - { - printf("nRunLength > 47 unhandled\n"); - //return NULL; + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; - nControlBytes = (((nRunLength - 15) + 46) / 47) + 1; - - outSegmentSize = cRawBytes + nControlBytes; - - if (((segp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", - ((dstp - outPlane) + outSegmentSize), - outPlaneSize); - return NULL; - } - - printf(">47 nRunLength: %d nControlBytes: %d\n", nRunLength, nControlBytes); - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - printf("ControlByte nRunLength: %d cRawBytes: %d\n", 15, cRawBytes); - nRunLength -= 15; - nControlBytes--; - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - while (nControlBytes--) - { - printf("...nRunLength: %d\n", nRunLength); - - if (nRunLength > 47) - { - *dstp = PLANAR_CONTROL_BYTE(2, (47 - 32)); - printf("ControlByte nRunLength: %d cRawBytes: %d\n", 47, 0); - nRunLength -= 47; - dstp++; - } - else if (nRunLength > 31) - { - *dstp = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); - printf("ControlByte nRunLength: %d cRawBytes: %d\n", nRunLength, 0); - nRunLength = 0; - dstp++; - } - else if (nRunLength > 15) - { - *dstp = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); - printf("ControlByte nRunLength: %d cRawBytes: %d\n", nRunLength, 0); - nRunLength = 0; - dstp++; - } - else - { - *dstp = PLANAR_CONTROL_BYTE(0, nRunLength); - printf("ControlByte nRunLength: %d cRawBytes: %d\n", nRunLength, 0); - nRunLength = 0; - dstp++; - } - } - - return NULL; - } - else if (nRunLength > 31) - { - nControlBytes = 2; - outSegmentSize = cRawBytes + nControlBytes; - - if (((segp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", - ((dstp - outPlane) + outSegmentSize), - outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - nRunLength -= 32; - *dstp = PLANAR_CONTROL_BYTE(2, nRunLength); - dstp++; - } - else if (nRunLength > 15) - { - nControlBytes = 2; - outSegmentSize = cRawBytes + nControlBytes; - - if (((segp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", - ((dstp - outPlane) + outSegmentSize), - outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - nRunLength -= 16; - *dstp = PLANAR_CONTROL_BYTE(1, nRunLength); - dstp++; - } - else - { - nControlBytes = 1; - outSegmentSize = cRawBytes + nControlBytes; - - if (((segp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", - ((dstp - outPlane) + cRawBytes + 1), - outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - } - - //printf("Segment %d\n", ++cSegments); - //winpr_HexDump(segp, dstp - segp); - - rawValues = &inPlane[(i * width) + j]; - cRawBytes = 0; + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; } - else - { - cRawBytes += nSequenceLength; + } - if (j == width) - { - nRunLength = 0; - - if (cRawBytes > 15) - { - //printf("cRawBytes > 15 unhandled\n"); - return NULL; - } - -#if 0 - printf("RAW["); - - for (k = 0; k < cRawBytes; k++) - { - printf("0x%02X%s", rawValues[k], - ((k + 1) == cRawBytes) ? "" : ", "); - } - - printf("] RUN[%d]\n", nRunLength); -#endif - - segp = dstp; - outSegmentSize = cRawBytes + 1; - - if (((segp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", - ((dstp - outPlane) + cRawBytes + 1), - outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - //printf("Segment %d\n", ++cSegments); - //winpr_HexDump(segp, dstp - segp); - } - } - - if (j != width) - { - symbol = inPlane[(i * width) + j]; - nSequenceLength = 1; - } + if (j != width) + { + symbol = inPlane[(i * width) + j]; + nRunLength = 0; } } } @@ -467,8 +448,6 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h printf("R: [%d/%d] G: [%d/%d] B: [%d/%d]\n", dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize); - - printf("RDP6 planar compression size: %d width: %d height: %d\n", size, width, height); } if (!dstData) From a5c1d0bbaa767a3eb6a656e35fcadebcd2d4ac91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 27 Nov 2013 16:23:20 -0500 Subject: [PATCH 052/149] libfreerdp-codec: detect planar overflow per set of planes, not individual planes --- libfreerdp/codec/planar.c | 418 ++++++++++++++++++++------------------ 1 file changed, 222 insertions(+), 196 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 2fe044df4..a1f791582 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -87,10 +87,16 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei int nControlBytes; cSegments = 0; - outPlaneSize = width * height; if (!outPlane) + { + outPlaneSize = width * height * 2; outPlane = malloc(outPlaneSize); + } + else + { + outPlaneSize = *dstSize; + } dstp = outPlane; @@ -112,12 +118,178 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei if ((j != width) && (inPlane[(i * width) + j] == symbol)) { nRunLength++; + continue; + } + + if (nRunLength >= 3) + { + cRawBytes += 1; + +#if 0 + printf("RAW["); + + for (k = 0; k < cRawBytes; k++) + { + printf("0x%02X%s", rawValues[k], + ((k + 1) == cRawBytes) ? "" : ", "); + } + + printf("] RUN[%d]\n", nRunLength); +#endif + + while (cRawBytes > 15) + { + printf("handling cRawBytes > 15\n"); + + nControlBytes = 1; + outSegmentSize = 15 + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(0, 15); + dstp++; + + CopyMemory(dstp, rawValues, 15); + cRawBytes -= 15; + rawValues += 15; + dstp += 15; + } + + if (nRunLength > 47) + { + nControlBytes = (((nRunLength - 15) + 46) / 47) + 1; + + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + nRunLength -= 15; + nControlBytes--; + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; + + while (nControlBytes--) + { + if (nRunLength > 47) + { + *dstp = PLANAR_CONTROL_BYTE(2, (47 - 32)); + nRunLength -= 47; + dstp++; + } + else if (nRunLength > 31) + { + *dstp = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); + nRunLength = 0; + dstp++; + } + else if (nRunLength > 15) + { + *dstp = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); + nRunLength = 0; + dstp++; + } + else + { + *dstp = PLANAR_CONTROL_BYTE(0, nRunLength); + nRunLength = 0; + dstp++; + } + } + + return NULL; + } + else if (nRunLength > 31) + { + nControlBytes = 2; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + rawValues += cRawBytes; + dstp += cRawBytes; + cRawBytes = 0; + + nRunLength -= 32; + *dstp = PLANAR_CONTROL_BYTE(2, nRunLength); + dstp++; + } + else if (nRunLength > 15) + { + nControlBytes = 2; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + rawValues += cRawBytes; + dstp += cRawBytes; + cRawBytes = 0; + + nRunLength -= 16; + *dstp = PLANAR_CONTROL_BYTE(1, nRunLength); + dstp++; + } + else + { + nControlBytes = 1; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + rawValues += cRawBytes; + dstp += cRawBytes; + cRawBytes = 0; + } } else { - if (nRunLength >= 3) + cRawBytes += (nRunLength + 1); + + if (j == width) { - cRawBytes += 1; + printf("end of scanline: cRawBytes: %d nRunLength: %d\n", cRawBytes, nRunLength); + + nRunLength = 0; + + if (cRawBytes > 15) + { + printf("cRawBytes > 15 unhandled\n"); + return NULL; + } #if 0 printf("RAW["); @@ -131,193 +303,28 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei printf("] RUN[%d]\n", nRunLength); #endif - while (cRawBytes > 15) + outSegmentSize = cRawBytes + 1; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) { - printf("handling cRawBytes > 15\n"); - - nControlBytes = 1; - outSegmentSize = 15 + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(0, 15); - dstp++; - - CopyMemory(dstp, rawValues, 15); - cRawBytes -= 15; - rawValues += 15; - dstp += 15; - + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); return NULL; } - if (nRunLength > 47) - { - nControlBytes = (((nRunLength - 15) + 46) / 47) + 1; + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - nRunLength -= 15; - nControlBytes--; - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - while (nControlBytes--) - { - if (nRunLength > 47) - { - *dstp = PLANAR_CONTROL_BYTE(2, (47 - 32)); - nRunLength -= 47; - dstp++; - } - else if (nRunLength > 31) - { - *dstp = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); - nRunLength = 0; - dstp++; - } - else if (nRunLength > 15) - { - *dstp = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); - nRunLength = 0; - dstp++; - } - else - { - *dstp = PLANAR_CONTROL_BYTE(0, nRunLength); - nRunLength = 0; - dstp++; - } - } - - return NULL; - } - else if (nRunLength > 31) - { - nControlBytes = 2; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - nRunLength -= 32; - *dstp = PLANAR_CONTROL_BYTE(2, nRunLength); - dstp++; - } - else if (nRunLength > 15) - { - nControlBytes = 2; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - nRunLength -= 16; - *dstp = PLANAR_CONTROL_BYTE(1, nRunLength); - dstp++; - } - else - { - nControlBytes = 1; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - } - - rawValues = &inPlane[(i * width) + j]; - cRawBytes = 0; - } - else - { - cRawBytes += (nRunLength + 1); - - if (j == width) - { - nRunLength = 0; - - if (cRawBytes > 15) - { - printf("cRawBytes > 15 unhandled\n"); - return NULL; - } - -#if 0 - printf("RAW["); - - for (k = 0; k < cRawBytes; k++) - { - printf("0x%02X%s", rawValues[k], - ((k + 1) == cRawBytes) ? "" : ", "); - } - - printf("] RUN[%d]\n", nRunLength); -#endif - - outSegmentSize = cRawBytes + 1; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - } - } - - if (j != width) - { - symbol = inPlane[(i * width) + j]; - nRunLength = 0; + CopyMemory(dstp, rawValues, cRawBytes); + dstp += cRawBytes; } } - } + if (j != width) + { + symbol = inPlane[(i * width) + j]; + nRunLength = 0; + } + } //printf("---\n"); } @@ -328,20 +335,39 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei return outPlane; } -int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5], int* dstSizes) +int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes, int* dstSizes) { - if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes[0], &dstSizes[0])) + int outPlanesSize = width * height; + + dstSizes[0] = outPlanesSize; + + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes, &dstSizes[0])) return 0; - if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes[1], &dstSizes[1])) + outPlanes += dstSizes[0]; + outPlanesSize -= dstSizes[0]; + dstSizes[1] = outPlanesSize; + + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes, &dstSizes[1])) return 0; - if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes[2], &dstSizes[2])) + outPlanes += dstSizes[1]; + outPlanesSize -= dstSizes[1]; + dstSizes[2] = outPlanesSize; + + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes, &dstSizes[2])) return 0; - if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes[3], &dstSizes[3])) + outPlanes += dstSizes[2]; + outPlanesSize -= dstSizes[2]; + dstSizes[3] = outPlanesSize; + + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes, &dstSizes[3])) return 0; + outPlanes += dstSizes[3]; + outPlanesSize -= dstSizes[3]; + return 1; } @@ -404,12 +430,12 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h int size; BYTE* dstp; int planeSize; - int dstSizes[5]; + int dstSizes[4]; BYTE* planes[5]; BYTE* planesBuffer; BYTE* deltaPlanes[5]; BYTE* deltaPlanesBuffer; - BYTE* rlePlanes[5]; + BYTE* rlePlanes[4]; BYTE* rlePlanesBuffer; BYTE FormatHeader = 0; @@ -431,21 +457,21 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h deltaPlanes[3] = &deltaPlanesBuffer[planeSize * 3]; deltaPlanes[4] = &deltaPlanesBuffer[planeSize * 4]; - rlePlanesBuffer = malloc(planeSize * 5); - rlePlanes[0] = &rlePlanesBuffer[planeSize * 0]; - rlePlanes[1] = &rlePlanesBuffer[planeSize * 1]; - rlePlanes[2] = &rlePlanesBuffer[planeSize * 2]; - rlePlanes[3] = &rlePlanesBuffer[planeSize * 3]; - rlePlanes[4] = &rlePlanesBuffer[planeSize * 4]; + rlePlanesBuffer = malloc(planeSize * 4); freerdp_split_color_planes(data, format, width, height, scanline, planes); freerdp_bitmap_planar_delta_encode_planes(planes, width, height, deltaPlanes); - if (freerdp_bitmap_planar_compress_planes_rle(deltaPlanes, width, height, rlePlanes, (int*) &dstSizes) > 0) + if (freerdp_bitmap_planar_compress_planes_rle(deltaPlanes, width, height, rlePlanesBuffer, (int*) &dstSizes) > 0) { FormatHeader |= PLANAR_FORMAT_HEADER_RLE; + rlePlanes[0] = &rlePlanesBuffer[0]; + rlePlanes[1] = &rlePlanesBuffer[dstSizes[0]]; + rlePlanes[2] = &rlePlanesBuffer[dstSizes[0] + dstSizes[1]]; + rlePlanes[3] = &rlePlanesBuffer[dstSizes[0] + dstSizes[1] + dstSizes[2]]; + printf("R: [%d/%d] G: [%d/%d] B: [%d/%d]\n", dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize); } From fcafbfe626f67665d7c24f23e0f5b706c68ee07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 27 Nov 2013 18:21:05 -0500 Subject: [PATCH 053/149] libfreerdp-codec: improve planar RLE compression --- libfreerdp/codec/planar.c | 45 ++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index a1f791582..c685594ea 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -139,8 +139,6 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei while (cRawBytes > 15) { - printf("handling cRawBytes > 15\n"); - nControlBytes = 1; outSegmentSize = 15 + nControlBytes; @@ -281,14 +279,26 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei if (j == width) { - printf("end of scanline: cRawBytes: %d nRunLength: %d\n", cRawBytes, nRunLength); - nRunLength = 0; - if (cRawBytes > 15) + while (cRawBytes > 15) { - printf("cRawBytes > 15 unhandled\n"); - return NULL; + nControlBytes = 1; + outSegmentSize = 15 + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(0, 15); + dstp++; + + CopyMemory(dstp, rawValues, 15); + cRawBytes -= 15; + rawValues += 15; + dstp += 15; } #if 0 @@ -315,7 +325,9 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei dstp++; CopyMemory(dstp, rawValues, cRawBytes); + rawValues += cRawBytes; dstp += cRawBytes; + cRawBytes = 0; } } @@ -337,7 +349,7 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes, int* dstSizes) { - int outPlanesSize = width * height; + int outPlanesSize = width * height * 4; dstSizes[0] = outPlanesSize; @@ -465,12 +477,21 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h if (freerdp_bitmap_planar_compress_planes_rle(deltaPlanes, width, height, rlePlanesBuffer, (int*) &dstSizes) > 0) { + int offset = 0; + FormatHeader |= PLANAR_FORMAT_HEADER_RLE; - rlePlanes[0] = &rlePlanesBuffer[0]; - rlePlanes[1] = &rlePlanesBuffer[dstSizes[0]]; - rlePlanes[2] = &rlePlanesBuffer[dstSizes[0] + dstSizes[1]]; - rlePlanes[3] = &rlePlanesBuffer[dstSizes[0] + dstSizes[1] + dstSizes[2]]; + rlePlanes[0] = &rlePlanesBuffer[offset]; + offset += dstSizes[0]; + + rlePlanes[1] = &rlePlanesBuffer[offset]; + offset += dstSizes[1]; + + rlePlanes[2] = &rlePlanesBuffer[offset]; + offset += dstSizes[2]; + + rlePlanes[3] = &rlePlanesBuffer[offset]; + offset += dstSizes[3]; printf("R: [%d/%d] G: [%d/%d] B: [%d/%d]\n", dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize); From cca020a7115c164ffce5e012414a117184de5723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 28 Nov 2013 15:49:22 -0500 Subject: [PATCH 054/149] libfreerdp-codec: reorganize planar RLE encoding logic --- libfreerdp/codec/planar.c | 65 ++++++++--------- .../codec/test/TestFreeRDPCodecPlanar.c | 72 +++++++++++++++++++ 2 files changed, 101 insertions(+), 36 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index c685594ea..ce6f1feb2 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -75,13 +75,13 @@ int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int* dstSize) { - int i, j; + int i, j, k; BYTE* dstp; - BYTE symbol; int cSegments; int nRunLength; int cRawBytes; BYTE* rawValues; + BYTE* rawScanline; int outPlaneSize; int outSegmentSize; int nControlBytes; @@ -102,31 +102,28 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei for (i = 0; i < height; i++) { - cRawBytes = 0; - nRunLength = -1; - rawValues = &inPlane[i * width]; + cRawBytes = 1; + nRunLength = 0; + rawScanline = &inPlane[i * width]; + rawValues = rawScanline; - for (j = 0; j <= width; j++) + for (j = 1; j <= width; j++) { - if ((nRunLength < 0) && (j != width)) + if (j != width) { - symbol = inPlane[(i * width) + j]; - nRunLength = 0; - continue; - } - - if ((j != width) && (inPlane[(i * width) + j] == symbol)) - { - nRunLength++; - continue; + if (rawScanline[j] == rawValues[cRawBytes - 1]) + { + nRunLength++; + continue; + } } if (nRunLength >= 3) { - cRawBytes += 1; + printf("nRunLength: %d cRawBytes: %d\n", nRunLength, cRawBytes); -#if 0 - printf("RAW["); +#if 1 + printf("B RAW["); for (k = 0; k < cRawBytes; k++) { @@ -204,8 +201,6 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei dstp++; } } - - return NULL; } else if (nRunLength > 31) { @@ -268,16 +263,23 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei dstp++; CopyMemory(dstp, rawValues, cRawBytes); - rawValues += cRawBytes; dstp += cRawBytes; + + rawValues += (cRawBytes + nRunLength); + nRunLength = 0; cRawBytes = 0; + + j--; + continue; } } else { - cRawBytes += (nRunLength + 1); - - if (j == width) + if (j != width) + { + cRawBytes++; + } + else { nRunLength = 0; @@ -301,8 +303,8 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei dstp += 15; } -#if 0 - printf("RAW["); +#if 1 + printf("E RAW["); for (k = 0; k < cRawBytes; k++) { @@ -330,18 +332,9 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei cRawBytes = 0; } } - - if (j != width) - { - symbol = inPlane[(i * width) + j]; - nRunLength = 0; - } } - //printf("---\n"); } - //printf("\n"); - *dstSize = (dstp - outPlane); return outPlane; diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index a0d58ff4f..1302b4402 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -1,4 +1,6 @@ +#include + #include #include #include @@ -213,11 +215,17 @@ const BYTE TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED[3][6] = int TestFreeRDPCodecPlanar(int argc, char* argv[]) { + int i; int dstSize; UINT32 format; HCLRCONV clrconv; BYTE* srcBitmap32; BYTE* srcBitmap16; + int width, height; + BYTE* blackBitmap; + BYTE* whiteBitmap; + BYTE* compressedBitmap; + BYTE* decompressedBitmap; clrconv = freerdp_clrconv_new(0); srcBitmap16 = (BYTE*) TEST_RLE_UNCOMPRESSED_BITMAP_16BPP; @@ -234,6 +242,70 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL, &dstSize); + return 0; + + for (i = 17; i < 64; i++) + { + width = i; + height = i; + + if ((width > 17) && (width < 65)) + continue; + + whiteBitmap = (BYTE*) malloc(width * height * 4); + FillMemory(whiteBitmap, width * height * 4, 0xFF); + + compressedBitmap = freerdp_bitmap_compress_planar(whiteBitmap, format, width, height, width * 4, NULL, &dstSize); + + decompressedBitmap = (BYTE*) malloc(width * height * 4); + ZeroMemory(decompressedBitmap, width * height * 4); + + if (!bitmap_decompress(compressedBitmap, decompressedBitmap, width, height, dstSize, 32, 32)) + { + printf("failed to decompress bitmap: width: %d height: %d\n", width, height); + //return -1; + } + else + { + printf("success decompressing bitmap: width: %d height: %d\n", width, height); + } + + free(compressedBitmap); + free(decompressedBitmap); + } + + return 0; + + for (i = 17; i < 64; i++) + { + width = i; + height = i; + + if ((width > 17) && (width < 65)) + continue; + + blackBitmap = (BYTE*) malloc(width * height * 4); + ZeroMemory(blackBitmap, width * height * 4); + + compressedBitmap = freerdp_bitmap_compress_planar(blackBitmap, format, width, height, width * 4, NULL, &dstSize); + + decompressedBitmap = (BYTE*) malloc(width * height * 4); + ZeroMemory(decompressedBitmap, width * height * 4); + + if (!bitmap_decompress(compressedBitmap, decompressedBitmap, width, height, dstSize, 32, 32)) + { + printf("failed to decompress bitmap: width: %d height: %d\n", width, height); + //return -1; + } + else + { + printf("success decompressing bitmap: width: %d height: %d\n", width, height); + } + + free(compressedBitmap); + free(decompressedBitmap); + } + freerdp_clrconv_free(clrconv); free(srcBitmap32); From 1d6a07d2d585f6620c96a09777c7f273290be9a4 Mon Sep 17 00:00:00 2001 From: Hardening Date: Thu, 28 Nov 2013 23:17:21 +0100 Subject: [PATCH 055/149] More security in gcc.c As strange as it looks, i have found more errors that could lead to security issues in gcc.c. --- libfreerdp/core/gcc.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/libfreerdp/core/gcc.c b/libfreerdp/core/gcc.c index ee039a993..d51f4c004 100644 --- a/libfreerdp/core/gcc.c +++ b/libfreerdp/core/gcc.c @@ -232,36 +232,46 @@ BOOL gcc_read_conference_create_response(wStream* s, rdpSettings* settings) BYTE number; /* ConnectData */ - per_read_choice(s, &choice); - per_read_object_identifier(s, t124_02_98_oid); + if (!per_read_choice(s, &choice) || + !per_read_object_identifier(s, t124_02_98_oid)) + return FALSE; /* ConnectData::connectPDU (OCTET_STRING) */ - per_read_length(s, &length); + if (!per_read_length(s, &length)) + return FALSE; /* ConnectGCCPDU */ - per_read_choice(s, &choice); + if (!per_read_choice(s, &choice)) + return FALSE; /* ConferenceCreateResponse::nodeID (UserID) */ - per_read_integer16(s, &nodeID, 1001); + if (!per_read_integer16(s, &nodeID, 1001)) + return FALSE; /* ConferenceCreateResponse::tag (INTEGER) */ - per_read_integer(s, &tag); + if (!per_read_integer(s, &tag)) + return FALSE; /* ConferenceCreateResponse::result (ENUMERATED) */ - per_read_enumerated(s, &result, MCS_Result_enum_length); + if (!per_read_enumerated(s, &result, MCS_Result_enum_length)) + return FALSE; /* number of UserData sets */ - per_read_number_of_sets(s, &number); + if (!per_read_number_of_sets(s, &number)) + return FALSE; /* UserData::value present + select h221NonStandard (1) */ - per_read_choice(s, &choice); + if (!per_read_choice(s, &choice)) + return FALSE; /* h221NonStandard */ if (!per_read_octet_string(s, h221_sc_key, 4, 4)) /* h221NonStandard, server-to-client H.221 key, "McDn" */ return FALSE; /* userData (OCTET_STRING) */ - per_read_length(s, &length); + if (!per_read_length(s, &length)) + return FALSE; + if (!gcc_read_server_data_blocks(s, settings, length)) { fprintf(stderr, "gcc_read_conference_create_response: gcc_read_server_data_blocks failed\n"); @@ -1125,7 +1135,7 @@ BOOL gcc_read_server_network_data(wStream* s, rdpSettings* settings) { int i; UINT16 MCSChannelId; - UINT16 channelCount; + UINT16 channelCount, channelsToTreat; UINT16 channelId; if(Stream_GetRemainingLength(s) < 4) @@ -1133,16 +1143,21 @@ BOOL gcc_read_server_network_data(wStream* s, rdpSettings* settings) Stream_Read_UINT16(s, MCSChannelId); /* MCSChannelId */ Stream_Read_UINT16(s, channelCount); /* channelCount */ + channelsToTreat = channelCount; if (channelCount != settings->ChannelCount) { fprintf(stderr, "requested %d channels, got %d instead\n", settings->ChannelCount, channelCount); + + // we ensure that the response is not bigger than the request + if (channelCount > settings->ChannelCount) + channelsToTreat = settings->ChannelCount; } if(Stream_GetRemainingLength(s) < channelCount * 2) return FALSE; - for (i = 0; i < channelCount; i++) + for (i = 0; i < channelsToTreat; i++) { Stream_Read_UINT16(s, channelId); /* channelId */ settings->ChannelDefArray[i].ChannelId = channelId; From 338d809e3af952aed5f720bb802fd761485dcf08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 28 Nov 2013 19:51:29 -0500 Subject: [PATCH 056/149] libfreerdp-codec: extend planar codec unit tests --- libfreerdp/codec/planar.c | 333 +++++++++--------- .../codec/test/TestFreeRDPCodecPlanar.c | 68 +++- 2 files changed, 217 insertions(+), 184 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index ce6f1feb2..c591aa4c9 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -46,7 +46,7 @@ int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, for (j = 0; j < width; j++) { - GetARGB32(planes[0][k], planes[0][k], planes[2][k], planes[3][k], *pixel); + GetARGB32(planes[0][k], planes[1][k], planes[2][k], planes[3][k], *pixel); pixel++; k++; } @@ -118,12 +118,25 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei } } - if (nRunLength >= 3) + if (nRunLength < 3) { - printf("nRunLength: %d cRawBytes: %d\n", nRunLength, cRawBytes); + if (j != width) + { + cRawBytes++; + continue; + } + else + { + cRawBytes += nRunLength; + nRunLength = 0; + } + } -#if 1 - printf("B RAW["); + { +#if 0 + printf("scanline %d nRunLength: %d cRawBytes: %d\n", i, nRunLength, cRawBytes); + + printf("RAW["); for (k = 0; k < cRawBytes; k++) { @@ -134,6 +147,12 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei printf("] RUN[%d]\n", nRunLength); #endif + if ((rawValues - rawScanline) > width) + { + printf("rawValues overflow! %d\n", rawValues - rawScanline); + return NULL; + } + while (cRawBytes > 15) { nControlBytes = 1; @@ -154,139 +173,12 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei dstp += 15; } - if (nRunLength > 47) + if (cRawBytes == 0) { - nControlBytes = (((nRunLength - 15) + 46) / 47) + 1; - - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - nRunLength -= 15; - nControlBytes--; - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - while (nControlBytes--) - { - if (nRunLength > 47) - { - *dstp = PLANAR_CONTROL_BYTE(2, (47 - 32)); - nRunLength -= 47; - dstp++; - } - else if (nRunLength > 31) - { - *dstp = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); - nRunLength = 0; - dstp++; - } - else if (nRunLength > 15) - { - *dstp = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); - nRunLength = 0; - dstp++; - } - else - { - *dstp = PLANAR_CONTROL_BYTE(0, nRunLength); - nRunLength = 0; - dstp++; - } - } - } - else if (nRunLength > 31) - { - nControlBytes = 2; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - rawValues += cRawBytes; - dstp += cRawBytes; - cRawBytes = 0; - - nRunLength -= 32; - *dstp = PLANAR_CONTROL_BYTE(2, nRunLength); - dstp++; - } - else if (nRunLength > 15) - { - nControlBytes = 2; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - rawValues += cRawBytes; - dstp += cRawBytes; - cRawBytes = 0; - - nRunLength -= 16; - *dstp = PLANAR_CONTROL_BYTE(1, nRunLength); - dstp++; - } - else - { - nControlBytes = 1; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - dstp += cRawBytes; - - rawValues += (cRawBytes + nRunLength); - nRunLength = 0; - cRawBytes = 0; - - j--; - continue; - } - } - else - { - if (j != width) - { - cRawBytes++; - } - else - { - nRunLength = 0; - - while (cRawBytes > 15) + if (nRunLength > 47) { nControlBytes = 1; - outSegmentSize = 15 + nControlBytes; + outSegmentSize = nControlBytes; if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) { @@ -294,42 +186,153 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei return NULL; } - *dstp = PLANAR_CONTROL_BYTE(0, 15); + *dstp = PLANAR_CONTROL_BYTE(2, 15); + nRunLength -= 47; dstp++; - CopyMemory(dstp, rawValues, 15); - cRawBytes -= 15; - rawValues += 15; - dstp += 15; - } - -#if 1 - printf("E RAW["); - - for (k = 0; k < cRawBytes; k++) - { - printf("0x%02X%s", rawValues[k], - ((k + 1) == cRawBytes) ? "" : ", "); - } - - printf("] RUN[%d]\n", nRunLength); -#endif - - outSegmentSize = cRawBytes + 1; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); return NULL; + + //if (j != width) + { + j--; + continue; + } } + else if (nRunLength > 31) + { + nControlBytes = 1; + outSegmentSize = nControlBytes; - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } - CopyMemory(dstp, rawValues, cRawBytes); - rawValues += cRawBytes; - dstp += cRawBytes; - cRawBytes = 0; + *dstp = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); + nRunLength -= 32; + dstp++; + + //if (j != width) + { + j--; + continue; + } + } + else if (nRunLength > 15) + { + nControlBytes = 1; + outSegmentSize = nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); + nRunLength -= 16; + dstp++; + + //if (j != width) + { + j--; + continue; + } + } + else + { + nControlBytes = 1; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + rawValues += (cRawBytes + nRunLength); + dstp += cRawBytes; + cRawBytes = 0; + + nRunLength = 0; + + if (j != width) + { + j--; + continue; + } + } + } + else if (cRawBytes < 16) + { + if (nRunLength > 15) + { + nControlBytes = 2; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + if ((!rawValues[cRawBytes - 1]) && (cRawBytes == 1)) + { + rawValues += (cRawBytes + nRunLength); + cRawBytes = 0; + } + + *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); + dstp++; + + if (cRawBytes) + { + CopyMemory(dstp, rawValues, cRawBytes); + rawValues += (cRawBytes + nRunLength); + dstp += cRawBytes; + cRawBytes = 0; + } + + nRunLength -= 15; + + { + cRawBytes = 1; + j--; + continue; + } + } + else + { + nControlBytes = 1; + outSegmentSize = cRawBytes + nControlBytes; + + if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) + { + printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); + return NULL; + } + + *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + dstp++; + + CopyMemory(dstp, rawValues, cRawBytes); + rawValues += (cRawBytes + nRunLength); + dstp += cRawBytes; + cRawBytes = 0; + + nRunLength = 0; + + if (j != width) + { + j--; + continue; + } + } } } } diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 1302b4402..6af27eb98 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -213,9 +213,17 @@ const BYTE TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED[3][6] = #include "../planar.h" +static unsigned long next = 1; + +static int simple_rand(void) +{ + next = next * 1103515245 + 12345; + return ((unsigned int) (next / 65536) % 32768); +} + int TestFreeRDPCodecPlanar(int argc, char* argv[]) { - int i; + int i, j; int dstSize; UINT32 format; HCLRCONV clrconv; @@ -224,6 +232,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) int width, height; BYTE* blackBitmap; BYTE* whiteBitmap; + BYTE* randomBitmap; BYTE* compressedBitmap; BYTE* decompressedBitmap; @@ -242,16 +251,11 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL, &dstSize); - return 0; - - for (i = 17; i < 64; i++) + for (i = 4; i < 64; i += 4) { width = i; height = i; - if ((width > 17) && (width < 65)) - continue; - whiteBitmap = (BYTE*) malloc(width * height * 4); FillMemory(whiteBitmap, width * height * 4, 0xFF); @@ -262,28 +266,23 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) if (!bitmap_decompress(compressedBitmap, decompressedBitmap, width, height, dstSize, 32, 32)) { - printf("failed to decompress bitmap: width: %d height: %d\n", width, height); - //return -1; + printf("failed to decompress white bitmap: width: %d height: %d\n", width, height); + return -1; } else { - printf("success decompressing bitmap: width: %d height: %d\n", width, height); + printf("success decompressing white bitmap: width: %d height: %d\n", width, height); } free(compressedBitmap); free(decompressedBitmap); } - return 0; - - for (i = 17; i < 64; i++) + for (i = 4; i < 64; i += 4) { width = i; height = i; - if ((width > 17) && (width < 65)) - continue; - blackBitmap = (BYTE*) malloc(width * height * 4); ZeroMemory(blackBitmap, width * height * 4); @@ -294,12 +293,43 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) if (!bitmap_decompress(compressedBitmap, decompressedBitmap, width, height, dstSize, 32, 32)) { - printf("failed to decompress bitmap: width: %d height: %d\n", width, height); - //return -1; + printf("failed to decompress black bitmap: width: %d height: %d\n", width, height); + return -1; } else { - printf("success decompressing bitmap: width: %d height: %d\n", width, height); + printf("success decompressing black bitmap: width: %d height: %d\n", width, height); + } + + free(compressedBitmap); + free(decompressedBitmap); + } + + for (i = 4; i < 64; i += 4) + { + width = i; + height = i; + + randomBitmap = (BYTE*) malloc(width * height * 4); + + for (j = 0; j < width * height * 4; j++) + { + randomBitmap[j] = (BYTE) (simple_rand() % 256); + } + + compressedBitmap = freerdp_bitmap_compress_planar(randomBitmap, format, width, height, width * 4, NULL, &dstSize); + + decompressedBitmap = (BYTE*) malloc(width * height * 4); + ZeroMemory(decompressedBitmap, width * height * 4); + + if (!bitmap_decompress(compressedBitmap, decompressedBitmap, width, height, dstSize, 32, 32)) + { + printf("failed to decompress random bitmap: width: %d height: %d\n", width, height); + return -1; + } + else + { + printf("success decompressing random bitmap: width: %d height: %d\n", width, height); } free(compressedBitmap); From d9e1c0abaaf8138aa56fca374f66f9ea74195711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 29 Nov 2013 02:16:16 -0500 Subject: [PATCH 057/149] libfreerdp-codec: add more planar unit tests --- libfreerdp/codec/bitmap_decode.c | 191 +-- libfreerdp/codec/planar.c | 692 ++++++---- libfreerdp/codec/planar.h | 5 +- .../codec/test/TestFreeRDPCodecPlanar.c | 1171 +++++++++++++++++ winpr/include/winpr/print.h | 1 + winpr/libwinpr/utils/print.c | 26 + 6 files changed, 1667 insertions(+), 419 deletions(-) diff --git a/libfreerdp/codec/bitmap_decode.c b/libfreerdp/codec/bitmap_decode.c index 96c48a520..72386fa99 100644 --- a/libfreerdp/codec/bitmap_decode.c +++ b/libfreerdp/codec/bitmap_decode.c @@ -21,7 +21,11 @@ #include "config.h" #endif +#include #include + +#include "planar.h" + #include #include @@ -251,195 +255,12 @@ static UINT32 ExtractRunLength(UINT32 code, BYTE* pbOrderHdr, UINT32* advance) #define RLEEXTRA #include "include/bitmap.c" -#define IN_UINT8_MV(_p) (*((_p)++)) - -/** - * decompress an RLE color plane - * RDP6_BITMAP_STREAM - */ -static int process_rle_plane(BYTE* in, int width, int height, BYTE* out, int size) -{ - int indexw; - int indexh; - int code; - int collen; - int replen; - int color; - int x; - int revcode; - BYTE* last_line; - BYTE* this_line; - BYTE* org_in; - BYTE* org_out; - - org_in = in; - org_out = out; - last_line = 0; - indexh = 0; - while (indexh < height) - { - out = (org_out + width * height * 4) - ((indexh + 1) * width * 4); - color = 0; - this_line = out; - indexw = 0; - if (last_line == 0) - { - while (indexw < width) - { - code = IN_UINT8_MV(in); - replen = code & 0xf; - collen = (code >> 4) & 0xf; - revcode = (replen << 4) | collen; - if ((revcode <= 47) && (revcode >= 16)) - { - replen = revcode; - collen = 0; - } - while (collen > 0) - { - color = IN_UINT8_MV(in); - *out = color; - out += 4; - indexw++; - collen--; - } - while (replen > 0) - { - *out = color; - out += 4; - indexw++; - replen--; - } - } - } - else - { - while (indexw < width) - { - code = IN_UINT8_MV(in); - replen = code & 0xf; - collen = (code >> 4) & 0xf; - revcode = (replen << 4) | collen; - if ((revcode <= 47) && (revcode >= 16)) - { - replen = revcode; - collen = 0; - } - while (collen > 0) - { - x = IN_UINT8_MV(in); - if (x & 1) - { - x = x >> 1; - x = x + 1; - color = -x; - } - else - { - x = x >> 1; - color = x; - } - x = last_line[indexw * 4] + color; - *out = x; - out += 4; - indexw++; - collen--; - } - while (replen > 0) - { - x = last_line[indexw * 4] + color; - *out = x; - out += 4; - indexw++; - replen--; - } - } - } - indexh++; - last_line = this_line; - } - return (int) (in - org_in); -} - -/** - * process a raw color plane - */ -static int process_raw_plane(BYTE* srcData, int width, int height, BYTE* dstData, int size) -{ - int x, y; - - for (y = 0; y < height; y++) - { - for (x = 0; x < width; x++) - { - dstData[(((height - y - 1) * width) + x) * 4] = srcData[((y * width) + x)]; - } - } - - return (width * height); -} - -/** - * 4 byte bitmap decompress - * RDP6_BITMAP_STREAM - */ -static BOOL bitmap_decompress4(BYTE* srcData, BYTE* dstData, int width, int height, int size) -{ - int RLE; - int code; - int NoAlpha; - int bytesProcessed; - int totalProcessed; - - code = IN_UINT8_MV(srcData); - RLE = code & 0x10; - - totalProcessed = 1; - NoAlpha = code & 0x20; - - if (NoAlpha == 0) - { - bytesProcessed = process_rle_plane(srcData, width, height, dstData + 3, size - totalProcessed); - totalProcessed += bytesProcessed; - srcData += bytesProcessed; - } - - if (RLE != 0) - { - bytesProcessed = process_rle_plane(srcData, width, height, dstData + 2, size - totalProcessed); - totalProcessed += bytesProcessed; - srcData += bytesProcessed; - - bytesProcessed = process_rle_plane(srcData, width, height, dstData + 1, size - totalProcessed); - totalProcessed += bytesProcessed; - srcData += bytesProcessed; - - bytesProcessed = process_rle_plane(srcData, width, height, dstData + 0, size - totalProcessed); - totalProcessed += bytesProcessed; - } - else - { - bytesProcessed = process_raw_plane(srcData, width, height, dstData + 2, size - totalProcessed); - totalProcessed += bytesProcessed; - srcData += bytesProcessed; - - bytesProcessed = process_raw_plane(srcData, width, height, dstData + 1, size - totalProcessed); - totalProcessed += bytesProcessed; - srcData += bytesProcessed; - - bytesProcessed = process_raw_plane(srcData, width, height, dstData + 0, size - totalProcessed); - totalProcessed += bytesProcessed + 1; - } - - return (size == totalProcessed) ? TRUE : FALSE; -} - /** * bitmap decompression routine */ BOOL bitmap_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int size, int srcBpp, int dstBpp) { - BYTE * TmpBfr; + BYTE* TmpBfr; if (srcBpp == 16 && dstBpp == 16) { @@ -450,7 +271,7 @@ BOOL bitmap_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int } else if (srcBpp == 32 && dstBpp == 32) { - if (!bitmap_decompress4(srcData, dstData, width, height, size)) + if (!freerdp_bitmap_planar_decompress(srcData, dstData, width, height, size)) return FALSE; } else if (srcBpp == 15 && dstBpp == 15) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index c591aa4c9..235e21e82 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -28,6 +28,202 @@ #include "planar.h" +#define IN_UINT8_MV(_p) (*((_p)++)) + +static int freerdp_bitmap_planar_decompress_plane_rle(BYTE* in, int width, int height, BYTE* out, int size) +{ + int indexw; + int indexh; + int code; + int collen; + int replen; + int color; + int x; + int revcode; + BYTE* last_line; + BYTE* this_line; + BYTE* org_in; + BYTE* org_out; + + org_in = in; + org_out = out; + last_line = 0; + indexh = 0; + + while (indexh < height) + { + out = (org_out + width * height * 4) - ((indexh + 1) * width * 4); + color = 0; + this_line = out; + indexw = 0; + + if (last_line == 0) + { + while (indexw < width) + { + code = IN_UINT8_MV(in); + + replen = code & 0xf; + collen = (code >> 4) & 0xf; + revcode = (replen << 4) | collen; + + if ((revcode <= 47) && (revcode >= 16)) + { + replen = revcode; + collen = 0; + } + + while (collen > 0) + { + color = IN_UINT8_MV(in); + *out = color; + out += 4; + indexw++; + collen--; + } + + while (replen > 0) + { + *out = color; + out += 4; + indexw++; + replen--; + } + } + } + else + { + while (indexw < width) + { + code = IN_UINT8_MV(in); + + replen = code & 0xf; + collen = (code >> 4) & 0xf; + revcode = (replen << 4) | collen; + + if ((revcode <= 47) && (revcode >= 16)) + { + replen = revcode; + collen = 0; + } + + while (collen > 0) + { + x = IN_UINT8_MV(in); + + if (x & 1) + { + x = x >> 1; + x = x + 1; + color = -x; + } + else + { + x = x >> 1; + color = x; + } + + x = last_line[indexw * 4] + color; + *out = x; + out += 4; + indexw++; + collen--; + } + + while (replen > 0) + { + x = last_line[indexw * 4] + color; + *out = x; + out += 4; + indexw++; + replen--; + } + } + } + + indexh++; + last_line = this_line; + } + + return (int) (in - org_in); +} + +static int freerdp_bitmap_planar_decompress_plane_raw(BYTE* srcData, int width, int height, BYTE* dstData, int size) +{ + int x, y; + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + dstData[(((height - y - 1) * width) + x) * 4] = srcData[((y * width) + x)]; + } + } + + return (width * height); +} + +int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int size) +{ + BYTE* srcp; + BYTE FormatHeader; + int bytesProcessed; + int totalProcessed; + + srcp = srcData; + + FormatHeader = *srcp; + totalProcessed = 1; + srcp++; + + if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA)) + { + if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) + { + bytesProcessed = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 3, size - totalProcessed); + totalProcessed += bytesProcessed; + srcp += bytesProcessed; + } + else + { + bytesProcessed = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 3, size - totalProcessed); + totalProcessed += bytesProcessed; + srcp += bytesProcessed; + } + } + + if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) + { + bytesProcessed = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 2, size - totalProcessed); + totalProcessed += bytesProcessed; + srcp += bytesProcessed; + + bytesProcessed = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 1, size - totalProcessed); + totalProcessed += bytesProcessed; + srcp += bytesProcessed; + + bytesProcessed = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 0, size - totalProcessed); + totalProcessed += bytesProcessed; + srcp += bytesProcessed; + } + else + { + bytesProcessed = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 2, size - totalProcessed); + totalProcessed += bytesProcessed; + srcp += bytesProcessed; + + bytesProcessed = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 1, size - totalProcessed); + totalProcessed += bytesProcessed; + srcp += bytesProcessed; + + bytesProcessed = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 0, size - totalProcessed); + totalProcessed += bytesProcessed + 1; + srcp += bytesProcessed; + } + + return (size == totalProcessed) ? 1 : 0; +} + int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]) { int bpp; @@ -73,272 +269,292 @@ int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, return 0; } -BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int* dstSize) +struct _PLANAR_RLE_CONTEXT { - int i, j, k; - BYTE* dstp; - int cSegments; + int width; + int height; + BYTE* output; int nRunLength; int cRawBytes; BYTE* rawValues; BYTE* rawScanline; + BYTE* outPlane; int outPlaneSize; int outSegmentSize; int nControlBytes; +}; +typedef struct _PLANAR_RLE_CONTEXT PLANAR_RLE_CONTEXT; + +int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle, int position) +{ + int k; + + if (position < 0) + printf("B"); + else if (position == 0) + printf("M"); + else + printf("E"); + + printf(" RAW["); + + for (k = 0; k < rle->cRawBytes; k++) + { + printf("0x%02X%s", rle->rawValues[k], + ((k + 1) == rle->cRawBytes) ? "" : ", "); + } + + printf("] RUN[%d]\n", rle->nRunLength); + + while ((rle->cRawBytes != 0) || (rle->nRunLength != 0)) + { + printf("cRawBytes: %d nRunLength: %d\n", rle->cRawBytes, rle->nRunLength); + + if (rle->nRunLength < 3) + { + rle->cRawBytes += rle->nRunLength; + rle->nRunLength = 0; + } + + if ((rle->rawValues - rle->rawScanline) > rle->width) + { + printf("rawValues overflow! %d\n", rle->rawValues - rle->rawScanline); + return 0; + } + + if (rle->cRawBytes > 15) + { + rle->nControlBytes = 1; + rle->outSegmentSize = 15 + rle->nControlBytes; + + if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + { + printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); + return -1; + } + + *rle->output = PLANAR_CONTROL_BYTE(0, 15); + rle->output++; + + CopyMemory(rle->output, rle->rawValues, 15); + rle->cRawBytes -= 15; + rle->rawValues += 15; + rle->output += 15; + + /* continue */ + } + else if (rle->cRawBytes == 0) + { + if (rle->nRunLength > 47) + { + rle->nControlBytes = 1; + rle->outSegmentSize = rle->nControlBytes; + + if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + { + printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); + return -1; + } + + *rle->output = PLANAR_CONTROL_BYTE(2, 15); + rle->nRunLength -= 47; + rle->output++; + + /* continue */ + } + else if (rle->nRunLength > 31) + { + rle->nControlBytes = 1; + rle->outSegmentSize = rle->nControlBytes; + + if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + { + printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); + return -1; + } + + *rle->output = PLANAR_CONTROL_BYTE(2, (rle->nRunLength - 32)); + rle->nRunLength -= 32; + rle->output++; + + return 0; /* finish */ + } + else if (rle->nRunLength > 15) + { + rle->nControlBytes = 1; + rle->outSegmentSize = rle->nControlBytes; + + if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + { + printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); + return -1; + } + + *rle->output = PLANAR_CONTROL_BYTE(1, (rle->nRunLength - 16)); + rle->nRunLength -= 16; + rle->output++; + + return 0; /* finish */ + } + else + { + rle->nControlBytes = 1; + rle->outSegmentSize = rle->cRawBytes + rle->nControlBytes; + + if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + { + printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); + return -1; + } + + *rle->output = PLANAR_CONTROL_BYTE(rle->nRunLength, rle->cRawBytes); + rle->output++; + + if (rle->cRawBytes) + { + CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); + rle->rawValues += (rle->cRawBytes + rle->nRunLength); + rle->output += rle->cRawBytes; + } + + rle->cRawBytes = 0; + rle->nRunLength = 0; + + return 0; /* finish */ + } + } + else if (rle->cRawBytes < 16) + { + if (rle->nRunLength > 15) + { + rle->nControlBytes = 2; + rle->outSegmentSize = rle->cRawBytes + rle->nControlBytes; + + if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + { + printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); + return -1; + } + + if ((!rle->rawValues[rle->cRawBytes - 1]) && (rle->cRawBytes == 1)) + { + //rle->rawValues += (rle->cRawBytes + rle->nRunLength); + //rle->cRawBytes = 0; + } + + *rle->output = PLANAR_CONTROL_BYTE(15, rle->cRawBytes); + rle->output++; + + if (rle->cRawBytes) + { + CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); + rle->rawValues += (rle->cRawBytes + rle->nRunLength); + rle->output += rle->cRawBytes; + rle->cRawBytes = 0; + } + + rle->nRunLength -= 15; + rle->cRawBytes = 0; + + /* continue */ + } + else + { + rle->nControlBytes = 1; + rle->outSegmentSize = rle->cRawBytes + rle->nControlBytes; + + if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + { + printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); + return -1; + } + + if ((!rle->rawValues[rle->cRawBytes - 1]) && (rle->cRawBytes == 1)) + { + //rle->rawValues += (rle->cRawBytes + rle->nRunLength); + //rle->cRawBytes = 0; + } + + *rle->output = PLANAR_CONTROL_BYTE(rle->nRunLength, rle->cRawBytes); + rle->output++; + + if (rle->cRawBytes) + { + CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); + rle->rawValues += (rle->cRawBytes + rle->nRunLength); + rle->output += rle->cRawBytes; + } + + rle->cRawBytes = 0; + rle->nRunLength = 0; + + return 0; /* finish */ + } + } + } + + return 0; +} + +BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int* dstSize) +{ + int i, j; + int cSegments; + PLANAR_RLE_CONTEXT rle_s; + PLANAR_RLE_CONTEXT* rle = &rle_s; cSegments = 0; if (!outPlane) { - outPlaneSize = width * height * 2; - outPlane = malloc(outPlaneSize); + rle->outPlaneSize = width * height * 2; + outPlane = malloc(rle->outPlaneSize); } else { - outPlaneSize = *dstSize; + rle->outPlaneSize = *dstSize; } - dstp = outPlane; + rle->output = outPlane; + rle->outPlane = outPlane; + rle->width = width; + rle->height = height; for (i = 0; i < height; i++) { - cRawBytes = 1; - nRunLength = 0; - rawScanline = &inPlane[i * width]; - rawValues = rawScanline; + rle->cRawBytes = 1; + rle->nRunLength = 0; + rle->rawScanline = &inPlane[i * width]; + rle->rawValues = rle->rawScanline; - for (j = 1; j <= width; j++) + for (j = 1; j < width; j++) { - if (j != width) + printf("j: %d cRawBytes: %d nRunLength: %d\n", j, rle->cRawBytes, rle->nRunLength); + + if (rle->rawScanline[j] == rle->rawValues[rle->cRawBytes - 1]) { - if (rawScanline[j] == rawValues[cRawBytes - 1]) - { - nRunLength++; - continue; - } + rle->nRunLength++; + continue; } - if (nRunLength < 3) + if (rle->nRunLength < 3) { - if (j != width) - { - cRawBytes++; - continue; - } - else - { - cRawBytes += nRunLength; - nRunLength = 0; - } + rle->cRawBytes++; + continue; } - { -#if 0 - printf("scanline %d nRunLength: %d cRawBytes: %d\n", i, nRunLength, cRawBytes); + if (freerdp_bitmap_planar_compress_plane_rle_segment(rle, (j == 1) ? -1 : 0) < 0) + return NULL; + } - printf("RAW["); - - for (k = 0; k < cRawBytes; k++) - { - printf("0x%02X%s", rawValues[k], - ((k + 1) == cRawBytes) ? "" : ", "); - } - - printf("] RUN[%d]\n", nRunLength); -#endif - - if ((rawValues - rawScanline) > width) - { - printf("rawValues overflow! %d\n", rawValues - rawScanline); - return NULL; - } - - while (cRawBytes > 15) - { - nControlBytes = 1; - outSegmentSize = 15 + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(0, 15); - dstp++; - - CopyMemory(dstp, rawValues, 15); - cRawBytes -= 15; - rawValues += 15; - dstp += 15; - } - - if (cRawBytes == 0) - { - if (nRunLength > 47) - { - nControlBytes = 1; - outSegmentSize = nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(2, 15); - nRunLength -= 47; - dstp++; - - return NULL; - - //if (j != width) - { - j--; - continue; - } - } - else if (nRunLength > 31) - { - nControlBytes = 1; - outSegmentSize = nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); - nRunLength -= 32; - dstp++; - - //if (j != width) - { - j--; - continue; - } - } - else if (nRunLength > 15) - { - nControlBytes = 1; - outSegmentSize = nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); - nRunLength -= 16; - dstp++; - - //if (j != width) - { - j--; - continue; - } - } - else - { - nControlBytes = 1; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - rawValues += (cRawBytes + nRunLength); - dstp += cRawBytes; - cRawBytes = 0; - - nRunLength = 0; - - if (j != width) - { - j--; - continue; - } - } - } - else if (cRawBytes < 16) - { - if (nRunLength > 15) - { - nControlBytes = 2; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - if ((!rawValues[cRawBytes - 1]) && (cRawBytes == 1)) - { - rawValues += (cRawBytes + nRunLength); - cRawBytes = 0; - } - - *dstp = PLANAR_CONTROL_BYTE(15, cRawBytes); - dstp++; - - if (cRawBytes) - { - CopyMemory(dstp, rawValues, cRawBytes); - rawValues += (cRawBytes + nRunLength); - dstp += cRawBytes; - cRawBytes = 0; - } - - nRunLength -= 15; - - { - cRawBytes = 1; - j--; - continue; - } - } - else - { - nControlBytes = 1; - outSegmentSize = cRawBytes + nControlBytes; - - if (((dstp - outPlane) + outSegmentSize) > outPlaneSize) - { - printf("overflow: %d > %d\n", ((dstp - outPlane) + outSegmentSize), outPlaneSize); - return NULL; - } - - *dstp = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); - dstp++; - - CopyMemory(dstp, rawValues, cRawBytes); - rawValues += (cRawBytes + nRunLength); - dstp += cRawBytes; - cRawBytes = 0; - - nRunLength = 0; - - if (j != width) - { - j--; - continue; - } - } - } - } + if ((rle->cRawBytes != 0) || (rle->nRunLength != 0)) + { + if (freerdp_bitmap_planar_compress_plane_rle_segment(rle, 1) < 0) + return NULL; } } - *dstSize = (dstp - outPlane); + *dstSize = (rle->output - outPlane); return outPlane; } @@ -347,6 +563,7 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int { int outPlanesSize = width * height * 4; +#if 0 dstSizes[0] = outPlanesSize; if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes, &dstSizes[0])) @@ -354,8 +571,13 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int outPlanes += dstSizes[0]; outPlanesSize -= dstSizes[0]; +#else + dstSizes[0] = 0; +#endif dstSizes[1] = outPlanesSize; + printf("PlaneR\n"); + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes, &dstSizes[1])) return 0; @@ -363,6 +585,8 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int outPlanesSize -= dstSizes[1]; dstSizes[2] = outPlanesSize; + printf("PlaneG\n"); + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes, &dstSizes[2])) return 0; @@ -370,6 +594,8 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int outPlanesSize -= dstSizes[2]; dstSizes[3] = outPlanesSize; + printf("PlaneB\n"); + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes, &dstSizes[3])) return 0; diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h index 0315a14e9..b6a34660f 100644 --- a/libfreerdp/codec/planar.h +++ b/libfreerdp/codec/planar.h @@ -25,7 +25,8 @@ #include #define PLANAR_CONTROL_BYTE(_nRunLength, _cRawBytes) \ - (_nRunLength & 0x0F) | ((_cRawBytes & 0x0F) << 4) + ((_nRunLength & 0x0F) | ((_cRawBytes & 0x0F) << 4)) + \ + (printf("CONTROL_BYTE(%d, %d)\n", _cRawBytes, _nRunLength) * 0) #define PLANAR_CONTROL_BYTE_RUN_LENGTH(_controlByte) (_controlByte & 0x0F) #define PLANAR_CONTROL_BYTE_RAW_BYTES(_controlByte) ((_controlByte >> 4) & 0x0F) @@ -67,6 +68,8 @@ struct _RDP6_BITMAP_STREAM }; typedef struct _RDP6_BITMAP_STREAM RDP6_BITMAP_STREAM; +int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int size); + int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]); BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* plane, int width, int height, BYTE* outPlane, int* dstSize); BYTE* freerdp_bitmap_planar_delta_encode_plane(BYTE* inPlane, int width, int height, BYTE* outPlane); diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 6af27eb98..274a649eb 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -1,10 +1,1048 @@ #include +#include #include #include #include +/** + * Experimental Case 01: 64x64 (32bpp) + */ + +const BYTE TEST_RLE_BITMAP_EXPERIMENTAL_01[16384] = + "\x1B\x1A\x16\xFF\x1C\x1A\x16\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x18\x16\x12\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF" + "\x1D\x1C\x17\xFF\x1D\x1B\x17\xFF\x1C\x1B\x17\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x18\x17\x13\xFF\x1A\x19\x15\xFF" + "\x1B\x1A\x16\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x18\x16\x14\xFF\x1C\x1A\x16\xFF\x1A\x18\x14\xFF\x1B\x1A\x16\xFF\x19\x17\x13\xFF" + "\x1B\x19\x16\xFF\x1A\x18\x15\xFF\x1A\x18\x13\xFF\x19\x17\x13\xFF\x1B\x19\x15\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x18\x16\x12\xFF" + "\x1A\x19\x15\xFF\x1C\x1B\x16\xFF\x1C\x1B\x17\xFF\x1D\x1C\x17\xFF\x1B\x1B\x16\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x1B\x19\x14\xFF" + "\x1A\x19\x15\xFF\x1A\x1A\x15\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x1C\x1B\x17\xFF\x1D\x1C\x17\xFF\x1B\x1A\x15\xFF\x18\x17\x12\xFF" + "\x1B\x1A\x16\xFF\x18\x17\x13\xFF\x1A\x19\x15\xFF\x1E\x1D\x18\xFF\x19\x18\x14\xFF\x1C\x1B\x17\xFF\x1C\x1A\x16\xFF\x1B\x1A\x18\xFF" + "\x1D\x1B\x16\xFF\x1E\x1B\x17\xFF\x1D\x1C\x18\xFF\x1B\x1A\x16\xFF\x1A\x1A\x14\xFF\x1B\x1B\x15\xFF\x1E\x1C\x17\xFF\x1D\x1B\x17\xFF" + "\x1C\x19\x15\xFF\x1D\x1C\x17\xFF\x1A\x19\x14\xFF\x1C\x1B\x17\xFF\x1E\x1D\x19\xFF\x1C\x1C\x17\xFF\x1B\x1C\x16\xFF\x1D\x1C\x18\xFF" + "\x1C\x1C\x16\xFF\x1D\x1C\x17\xFF\x1F\x1E\x19\xFF\x1A\x18\x14\xFF\x1B\x1A\x16\xFF\x1D\x1C\x18\xFF\x1D\x1C\x17\xFF\x1E\x1B\x19\xFF" + "\x1F\x1C\x18\xFF\x1D\x1C\x18\xFF\x20\x1F\x19\xFF\x1F\x1E\x19\xFF\x1B\x1A\x16\xFF\x1F\x1E\x1A\xFF\x1D\x1C\x17\xFF\x1F\x1C\x18\xFF" + "\x1F\x1D\x18\xFF\x1E\x1C\x18\xFF\x1D\x1C\x17\xFF\x1C\x1C\x16\xFF\x1D\x1C\x18\xFF\x1F\x1F\x19\xFF\x1D\x1C\x18\xFF\x1B\x1B\x16\xFF" + "\x1D\x1D\x17\xFF\x1F\x1E\x19\xFF\x1D\x1C\x16\xFF\x1B\x1A\x15\xFF\x1F\x1D\x19\xFF\x1E\x1C\x18\xFF\x1F\x1E\x1A\xFF\x22\x20\x1C\xFF" + "\x22\x20\x1B\xFF\x20\x1F\x1A\xFF\x1F\x1F\x1A\xFF\x1E\x1E\x1A\xFF\x1D\x1C\x18\xFF\x1F\x1D\x19\xFF\x20\x20\x1A\xFF\x22\x21\x1C\xFF" + "\x20\x1F\x1A\xFF\x1D\x1D\x18\xFF\x1E\x1D\x18\xFF\x22\x20\x1B\xFF\x20\x20\x1A\xFF\x1E\x1D\x18\xFF\x20\x20\x1B\xFF\x20\x1F\x1B\xFF" + "\x1F\x1D\x18\xFF\x1E\x1E\x19\xFF\x1E\x1E\x19\xFF\x1D\x1B\x18\xFF\x1D\x1B\x16\xFF\x1E\x1C\x17\xFF\x1B\x1A\x16\xFF\x1F\x1D\x18\xFF" + "\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x19\x18\x15\xFF\x1B\x19\x15\xFF\x1B\x19\x15\xFF\x18\x18\x14\xFF\x17\x17\x13\xFF\x19\x18\x15\xFF" + "\x15\x15\x11\xFF\x14\x13\x10\xFF\x4E\x4A\x3D\xFF\x21\x20\x1A\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1F\xFF" + "\x21\x20\x1A\xFF\x24\x23\x1F\xFF\x21\x20\x1A\xFF\x2A\x29\x23\xFF\x21\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x26\x25\x21\xFF\x25\x24\x20\xFF\x21\x20\x1A\xFF\x22\x20\x1B\xFF\x26\x25\x20\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x21\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x22\x21\x1C\xFF\x26\x25\x21\xFF\x23\x21\x1C\xFF" + "\x22\x20\x1A\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x22\x22\x1C\xFF\x23\x22\x1D\xFF\x24\x23\x1E\xFF\x24\x24\x1E\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x29\x28\x23\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1E\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x25\x24\x20\xFF\x23\x23\x1E\xFF\x24\x23\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x25\x24\x20\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x22\x1D\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x24\x22\x1D\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x26\x24\x20\xFF\x24\x23\x1E\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1D\xFF\x21\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x21\x1C\xFF" + "\x22\x21\x1B\xFF\x28\x26\x21\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x28\x26\x21\xFF" + "\x28\x26\x21\xFF\x22\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x28\x26\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x21\xFF" + "\x24\x23\x1E\xFF\x26\x25\x21\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x23\x22\x1E\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x25\x24\x20\xFF\x25\x25\x20\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x2A\x29\x23\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x26\x26\x21\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x27\x25\x20\xFF" + "\x22\x21\x1B\xFF\x26\x26\x20\xFF\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x27\x25\x20\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x27\x26\x21\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x25\x24\x1F\xFF\x25\x24\x1F\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x29\x28\x23\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x29\x26\x21\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1E\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x26\x25\x20\xFF\x23\x23\x1E\xFF\x24\x23\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x21\xFF\x29\x26\x21\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x27\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x26\x24\x20\xFF\x23\x22\x1C\xFF\x26\x25\x21\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x23\x1E\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1E\xFF\x22\x21\x1B\xFF\x27\x25\x20\xFF\x23\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1E\xFF\x29\x26\x21\xFF\x23\x22\x1C\xFF" + "\x23\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x27\x26\x21\xFF\x24\x23\x1E\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x29\x26\x21\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x27\x26\x21\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x28\x26\x21\xFF\x23\x22\x1C\xFF\x26\x25\x21\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x25\x24\x1F\xFF\x28\x26\x21\xFF" + "\x28\x26\x21\xFF\x22\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x4E\x49\x3C\xFF\x1A\x18\x14\xFF\x1A\x18\x15\xFF" + "\x1B\x1B\x16\xFF\x1D\x1B\x17\xFF\x1F\x1D\x19\xFF\x20\x1D\x19\xFF\x1E\x1D\x19\xFF\x1F\x1D\x19\xFF\x21\x20\x1B\xFF\x23\x21\x1C\xFF" + "\x22\x21\x1C\xFF\x25\x22\x1D\xFF\x25\x22\x1E\xFF\x25\x23\x1E\xFF\x25\x22\x1D\xFF\x25\x23\x1E\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF" + "\x26\x23\x1E\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x25\x22\x1D\xFF\x26\x25\x1F\xFF\x27\x26\x20\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF" + "\x27\x25\x20\xFF\x29\x26\x21\xFF\x26\x23\x1F\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF" + "\x27\x24\x1F\xFF\x28\x25\x20\xFF\x27\x26\x1F\xFF\x27\x24\x1F\xFF\x26\x23\x1F\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x2A\x27\x22\xFF" + "\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x26\x25\x20\xFF\x26\x24\x1E\xFF\x26\x23\x1D\xFF\x27\x24\x1F\xFF" + "\x26\x23\x1E\xFF\x26\x23\x1F\xFF\x24\x23\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x1E\xFF\x27\x24\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x28\x25\x20\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x24\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF" + "\x28\x26\x21\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF\x28\x26\x20\xFF\x25\x24\x1F\xFF\x27\x24\x1F\xFF\x29\x26\x21\xFF\x27\x24\x1F\xFF" + "\x25\x22\x1D\xFF\x25\x24\x1E\xFF\x29\x27\x20\xFF\x29\x26\x20\xFF\x28\x26\x20\xFF\x27\x26\x1F\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF" + "\x26\x25\x1F\xFF\x26\x25\x20\xFF\x27\x24\x1F\xFF\x25\x22\x1E\xFF\x24\x23\x1D\xFF\x23\x22\x1D\xFF\x24\x21\x1C\xFF\x26\x23\x1E\xFF" + "\x27\x24\x1E\xFF\x28\x27\x20\xFF\x27\x25\x1F\xFF\x27\x25\x20\xFF\x25\x22\x1D\xFF\x26\x24\x1E\xFF\x22\x20\x1A\xFF\x24\x24\x1D\xFF" + "\x28\x25\x20\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x26\x23\x1E\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF\x26\x25\x1F\xFF" + "\x27\x25\x20\xFF\x28\x27\x21\xFF\x27\x25\x20\xFF\x26\x25\x1F\xFF\x26\x23\x1E\xFF\x22\x21\x1C\xFF\x25\x24\x1E\xFF\x26\x23\x1E\xFF" + "\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x25\x24\x1D\xFF\x23\x23\x1D\xFF\x25\x23\x1E\xFF\x27\x25\x20\xFF\x26\x24\x1E\xFF" + "\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x27\x26\x20\xFF\x29\x2F\x26\xFF\x3C\x70\x55\xFF\x54\xC5\x92\xFF\x57\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF" + "\x56\xCC\x97\xFF\x4B\xA4\x7B\xFF\x2F\x47\x37\xFF\x26\x26\x20\xFF\x25\x23\x1D\xFF\x24\x23\x1D\xFF\x24\x22\x1D\xFF\x24\x22\x1D\xFF" + "\x26\x23\x1E\xFF\x27\x25\x20\xFF\x25\x22\x1D\xFF\x25\x22\x1D\xFF\x26\x24\x1F\xFF\x24\x23\x1D\xFF\x26\x24\x1F\xFF\x25\x25\x1D\xFF" + "\x27\x2D\x26\xFF\x39\x68\x50\xFF\x54\xC0\x8F\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x97\xFF\x56\xCD\x97\xFF\x49\x9F\x76\xFF\x2D\x42\x33\xFF\x24\x25\x20\xFF\x23\x22\x1D\xFF\x25\x22\x1D\xFF\x24\x21\x1C\xFF" + "\x22\x21\x1C\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF\x22\x20\x1B\xFF\x23\x22\x1C\xFF\x25\x25\x1F\xFF\x26\x24\x1E\xFF" + "\x24\x23\x1D\xFF\x24\x23\x1D\xFF\x26\x23\x1F\xFF\x23\x20\x1C\xFF\x28\x26\x1F\xFF\x25\x24\x1F\xFF\x25\x22\x1D\xFF\x24\x21\x1C\xFF" + "\x23\x23\x1D\xFF\x25\x22\x1E\xFF\x24\x22\x1E\xFF\x27\x24\x1F\xFF\x23\x21\x1B\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1E\xFF" + "\x26\x23\x1E\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF\x24\x21\x1C\xFF\x27\x26\x21\xFF\x25\x22\x1D\xFF\x22\x20\x1C\xFF\x25\x22\x1D\xFF" + "\x26\x24\x1E\xFF\x24\x23\x1D\xFF\x26\x25\x1F\xFF\x24\x22\x1E\xFF\x23\x21\x1C\xFF\x23\x22\x1D\xFF\x24\x22\x1D\xFF\x23\x21\x1C\xFF" + "\x24\x22\x1D\xFF\x24\x22\x1D\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x22\x1D\xFF\x25\x23\x1E\xFF\x23\x20\x1B\xFF\x25\x22\x1D\xFF" + "\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x25\x22\x1E\xFF\x24\x21\x1D\xFF\x25\x23\x1E\xFF\x23\x22\x1C\xFF\x22\x21\x1C\xFF\x23\x1F\x1B\xFF" + "\x21\x21\x1B\xFF\x24\x22\x1D\xFF\x24\x23\x1D\xFF\x25\x23\x1E\xFF\x23\x20\x1C\xFF\x26\x24\x1E\xFF\x24\x22\x1D\xFF\x24\x22\x1D\xFF" + "\x21\x21\x1A\xFF\x21\x21\x1C\xFF\x20\x20\x1A\xFF\x22\x21\x1C\xFF\x26\x23\x1E\xFF\x21\x21\x1C\xFF\x22\x21\x1C\xFF\x23\x22\x1D\xFF" + "\x25\x23\x1C\xFF\x26\x23\x1E\xFF\x24\x22\x1D\xFF\x22\x22\x1C\xFF\x22\x22\x1C\xFF\x24\x22\x1D\xFF\x24\x23\x1D\xFF\x25\x22\x1D\xFF" + "\x22\x21\x1D\xFF\x21\x21\x1C\xFF\x23\x22\x1D\xFF\x23\x20\x1C\xFF\x21\x20\x1B\xFF\x24\x23\x1D\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x22\x22\x1C\xFF\x24\x22\x1D\xFF\x23\x21\x1B\xFF\x22\x21\x1C\xFF\x24\x23\x1D\xFF\x23\x21\x1C\xFF\x21\x21\x1B\xFF\x24\x24\x1E\xFF" + "\x24\x22\x1D\xFF\x23\x22\x1D\xFF\x23\x22\x1D\xFF\x21\x20\x1B\xFF\x23\x21\x1C\xFF\x25\x24\x1E\xFF\x24\x21\x1C\xFF\x23\x21\x1C\xFF" + "\x21\x20\x1B\xFF\x22\x20\x1B\xFF\x21\x20\x1B\xFF\x23\x22\x1C\xFF\x23\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF\x22\x22\x1D\xFF" + "\x24\x21\x1C\xFF\x21\x1F\x1B\xFF\x21\x1F\x1A\xFF\x20\x20\x19\xFF\x24\x22\x1B\xFF\x23\x22\x1C\xFF\x27\x25\x1E\xFF\x25\x24\x1E\xFF" + "\x26\x29\x23\xFF\x2F\x45\x37\xFF\x2B\x3E\x2F\xFF\x27\x26\x20\xFF\x21\x1F\x1A\xFF\x21\x20\x1C\xFF\x21\x20\x1B\xFF\x21\x21\x1B\xFF" + "\x20\x1F\x1A\xFF\x25\x23\x1E\xFF\x24\x23\x1D\xFF\x1F\x1E\x1A\xFF\x24\x22\x1C\xFF\x27\x23\x1F\xFF\x24\x22\x1E\xFF\x21\x20\x1C\xFF" + "\x1F\x1E\x19\xFF\x21\x1F\x1B\xFF\x21\x20\x1B\xFF\x22\x21\x1B\xFF\x25\x22\x1D\xFF\x21\x1F\x1A\xFF\x21\x1F\x1C\xFF\x21\x20\x1B\xFF" + "\x24\x21\x1C\xFF\x1F\x1D\x19\xFF\x22\x20\x1A\xFF\x22\x22\x1C\xFF\x22\x20\x1B\xFF\x21\x21\x1A\xFF\x20\x20\x1A\xFF\x21\x20\x1C\xFF" + "\x25\x24\x1E\xFF\x24\x22\x1D\xFF\x21\x20\x1B\xFF\x22\x1F\x1B\xFF\x22\x21\x1C\xFF\x23\x21\x1D\xFF\x20\x1F\x1C\xFF\x22\x21\x1C\xFF" + "\x15\x14\x11\xFF\x15\x13\x10\xFF\x15\x14\x10\xFF\x18\x17\x13\xFF\x16\x15\x11\xFF\x14\x13\x10\xFF\x15\x14\x10\xFF\x14\x13\x0F\xFF" + "\x12\x10\x0E\xFF\x11\x10\x0E\xFF\x10\x0F\x0D\xFF\x14\x12\x10\xFF\x14\x12\x0E\xFF\x14\x13\x0F\xFF\x15\x13\x10\xFF\x12\x10\x0D\xFF" + "\x12\x11\x0E\xFF\x12\x11\x0E\xFF\x17\x16\x12\xFF\x17\x15\x11\xFF\x18\x17\x13\xFF\x15\x13\x10\xFF\x16\x15\x12\xFF\x13\x12\x0E\xFF" + "\x12\x11\x0E\xFF\x14\x13\x0F\xFF\x13\x12\x0F\xFF\x12\x12\x0F\xFF\x15\x15\x11\xFF\x13\x12\x0F\xFF\x13\x11\x0E\xFF\x13\x12\x0F\xFF" + "\x13\x11\x0F\xFF\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF\x13\x11\x0F\xFF\x12\x12\x0F\xFF\x14\x13\x10\xFF\x14\x13\x0F\xFF" + "\x17\x16\x12\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x14\x13\x0F\xFF\x14\x13\x0F\xFF\x15\x14\x0F\xFF\x14\x13\x10\xFF\x14\x12\x10\xFF" + "\x15\x14\x10\xFF\x15\x13\x10\xFF\x15\x14\x10\xFF\x15\x13\x10\xFF\x11\x10\x0D\xFF\x14\x13\x10\xFF\x13\x12\x0E\xFF\x14\x12\x0F\xFF" + "\x16\x15\x10\xFF\x14\x13\x0F\xFF\x12\x10\x0E\xFF\x13\x11\x0F\xFF\x12\x10\x0F\xFF\x15\x14\x10\xFF\x15\x14\x11\xFF\x15\x13\x11\xFF" + "\x16\x15\x11\xFF\x17\x16\x12\xFF\x13\x12\x0E\xFF\x12\x11\x0D\xFF\x14\x13\x10\xFF\x17\x16\x12\xFF\x18\x17\x14\xFF\x17\x16\x13\xFF" + "\x15\x14\x10\xFF\x14\x13\x0F\xFF\x14\x13\x0F\xFF\x14\x13\x0F\xFF\x16\x14\x11\xFF\x16\x14\x12\xFF\x14\x13\x0F\xFF\x14\x12\x10\xFF" + "\x14\x13\x10\xFF\x15\x14\x12\xFF\x16\x14\x11\xFF\x15\x13\x0F\xFF\x14\x13\x10\xFF\x15\x13\x10\xFF\x18\x17\x13\xFF\x15\x13\x11\xFF" + "\x14\x13\x0F\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x15\x14\x11\xFF\x13\x12\x0F\xFF\x17\x15\x12\xFF\x15\x13\x11\xFF\x13\x12\x0E\xFF" + "\x17\x16\x13\xFF\x19\x18\x14\xFF\x18\x17\x14\xFF\x17\x16\x13\xFF\x15\x14\x10\xFF\x15\x14\x12\xFF\x14\x13\x0F\xFF\x15\x14\x11\xFF" + "\x16\x15\x11\xFF\x19\x18\x14\xFF\x18\x16\x13\xFF\x14\x13\x10\xFF\x14\x13\x0F\xFF\x15\x13\x11\xFF\x15\x14\x11\xFF\x14\x13\x10\xFF" + "\x15\x14\x11\xFF\x18\x17\x14\xFF\x17\x15\x12\xFF\x16\x15\x11\xFF\x18\x17\x13\xFF\x1B\x1A\x16\xFF\x1A\x19\x16\xFF\x16\x15\x11\xFF" + "\x16\x14\x12\xFF\x16\x15\x11\xFF\x17\x16\x13\xFF\x14\x13\x11\xFF\x17\x16\x12\xFF\x19\x19\x14\xFF\x16\x15\x12\xFF\x19\x18\x14\xFF" + "\x18\x17\x14\xFF\x18\x18\x13\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x17\x15\x13\xFF\x18\x17\x14\xFF\x17\x16\x13\xFF\x16\x15\x11\xFF" + "\x14\x13\x0F\xFF\x16\x15\x11\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x18\x17\x14\xFF\x15\x13\x12\xFF\x16\x15\x11\xFF\x18\x16\x12\xFF" + "\x14\x13\x0F\xFF\x15\x14\x10\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF\x16\x15\x11\xFF" + "\x15\x14\x10\xFF\x17\x16\x12\xFF\x19\x18\x13\xFF\x19\x18\x13\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x1B\x18\x15\xFF" + "\x17\x16\x12\xFF\x17\x16\x12\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x1A\x19\x15\xFF\x1B\x19\x15\xFF\x1A\x19\x14\xFF" + "\x16\x15\x11\xFF\x1A\x19\x15\xFF\x1A\x18\x15\xFF\x17\x16\x12\xFF\x19\x19\x14\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x18\x16\x12\xFF" + "\x19\x18\x14\xFF\x19\x18\x14\xFF\x1B\x1A\x16\xFF\x1C\x1A\x16\xFF\x17\x15\x11\xFF\x19\x19\x14\xFF\x19\x19\x14\xFF\x19\x18\x14\xFF" + "\x1D\x1B\x17\xFF\x1A\x19\x14\xFF\x1A\x19\x15\xFF\x17\x16\x12\xFF\x19\x18\x14\xFF\x19\x17\x14\xFF\x19\x18\x14\xFF\x19\x18\x13\xFF" + "\x1B\x1A\x16\xFF\x1B\x1A\x16\xFF\x19\x17\x13\xFF\x18\x17\x13\xFF\x1A\x18\x14\xFF\x1C\x1A\x16\xFF\x1D\x1C\x17\xFF\x1F\x1E\x19\xFF" + "\x19\x18\x14\xFF\x18\x17\x12\xFF\x18\x17\x12\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF" + "\x1B\x19\x15\xFF\x1B\x19\x15\xFF\x1A\x18\x14\xFF\x17\x16\x12\xFF\x16\x16\x11\xFF\x1A\x18\x14\xFF\x1B\x19\x15\xFF\x1E\x1C\x18\xFF" + "\x1B\x1A\x16\xFF\x1C\x1A\x15\xFF\x19\x18\x14\xFF\x1A\x18\x14\xFF\x1C\x1A\x16\xFF\x1C\x1A\x17\xFF\x1C\x1B\x17\xFF\x1A\x18\x14\xFF" + "\x1B\x19\x15\xFF\x1A\x18\x14\xFF\x1A\x19\x15\xFF\x1B\x1A\x16\xFF\x1B\x1A\x16\xFF\x1B\x1A\x15\xFF\x1C\x1C\x17\xFF\x1C\x1C\x17\xFF" + "\x1B\x19\x15\xFF\x1A\x19\x15\xFF\x1B\x19\x15\xFF\x1D\x1C\x17\xFF\x1C\x1A\x16\xFF\x1C\x1C\x17\xFF\x1C\x1B\x16\xFF\x1D\x1C\x17\xFF" + "\x1D\x1B\x17\xFF\x19\x19\x14\xFF\x1B\x1A\x16\xFF\x1F\x1D\x18\xFF\x1E\x1E\x18\xFF\x1E\x1D\x18\xFF\x1D\x1B\x18\xFF\x1D\x1B\x17\xFF" + "\x1B\x1A\x15\xFF\x1A\x1A\x15\xFF\x1C\x1B\x16\xFF\x1E\x1C\x18\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x1D\x1C\x17\xFF\x1C\x19\x15\xFF" + "\x1D\x1B\x17\xFF\x1B\x1A\x15\xFF\x1A\x1A\x15\xFF\x1C\x1B\x16\xFF\x22\x20\x1B\xFF\x1E\x1D\x18\xFF\x1D\x1C\x18\xFF\x1C\x1C\x17\xFF" + "\x1B\x1B\x15\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x1D\x1C\x18\xFF\x1B\x19\x15\xFF\x1D\x1B\x17\xFF\x1B\x1B\x16\xFF" + "\x1F\x1C\x18\xFF\x1E\x1C\x18\xFF\x1F\x1C\x18\xFF\x1E\x1C\x18\xFF\x1C\x1C\x18\xFF\x1D\x1C\x18\xFF\x1D\x1B\x17\xFF\x1E\x1B\x17\xFF" + "\x1D\x1B\x17\xFF\x1D\x1C\x17\xFF\x1E\x1E\x19\xFF\x1E\x1D\x19\xFF\x1C\x1C\x18\xFF\x20\x1F\x19\xFF\x1F\x1D\x1A\xFF\x1F\x1E\x19\xFF" + "\x1D\x1B\x17\xFF\x1C\x1B\x17\xFF\x1B\x1A\x16\xFF\x1B\x1A\x16\xFF\x1E\x1C\x17\xFF\x1A\x19\x15\xFF\x20\x1F\x1A\xFF\x1E\x1D\x18\xFF" + "\x1E\x1D\x18\xFF\x1F\x1D\x19\xFF\x1F\x1E\x1A\xFF\x1F\x1D\x19\xFF\x1D\x1C\x18\xFF\x1E\x1C\x18\xFF\x1D\x1C\x18\xFF\x21\x20\x1B\xFF" + "\x1E\x1E\x19\xFF\x20\x1F\x1A\xFF\x20\x1E\x1A\xFF\x20\x1F\x1A\xFF\x20\x20\x1A\xFF\x21\x20\x1A\xFF\x23\x21\x1B\xFF\x21\x21\x1B\xFF" + "\x1E\x1D\x19\xFF\x1E\x1D\x18\xFF\x1F\x1E\x1A\xFF\x1C\x1B\x18\xFF\x1D\x1C\x17\xFF\x1E\x1C\x17\xFF\x1C\x1B\x17\xFF\x1E\x1D\x18\xFF" + "\x1B\x1A\x16\xFF\x1B\x1B\x16\xFF\x1A\x1A\x15\xFF\x19\x19\x15\xFF\x18\x18\x14\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x1A\x19\x15\xFF" + "\x15\x14\x11\xFF\x15\x14\x11\xFF\x51\x4E\x41\xFF\x21\x20\x1A\xFF\x26\x25\x20\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF" + "\x21\x20\x1A\xFF\x22\x21\x1C\xFF\x21\x20\x1A\xFF\x21\x20\x1A\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF" + "\x28\x26\x21\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1D\xFF\x24\x23\x1E\xFF\x21\x20\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x24\x23\x1E\xFF\x24\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x22\x21\x1B\xFF\x28\x26\x21\xFF\x23\x21\x1C\xFF\x22\x21\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x24\x23\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x21\x1C\xFF" + "\x22\x21\x1B\xFF\x25\x23\x1E\xFF\x25\x24\x20\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x24\x23\x1D\xFF\x25\x23\x1F\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x21\x1C\xFF\x21\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x25\x24\x20\xFF\x26\x25\x20\xFF\x25\x24\x20\xFF\x23\x23\x1E\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF" + "\x25\x24\x20\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x25\x25\x20\xFF\x22\x21\x1B\xFF" + "\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x29\x28\x23\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x25\x25\x20\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x29\x28\x23\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x23\x21\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x27\x25\x20\xFF\x22\x21\x1B\xFF\x27\x25\x21\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1D\xFF\x23\x22\x1D\xFF\x25\x24\x1F\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF" + "\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x28\x26\x21\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x28\x26\x21\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF" + "\x28\x26\x21\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x23\x22\x1E\xFF\x25\x23\x1E\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x23\x1E\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF" + "\x23\x22\x1C\xFF\x28\x26\x21\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x23\x1E\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x27\x25\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x26\x25\x20\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x25\x24\x1F\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x26\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x27\x25\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x26\x25\x21\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x26\x25\x20\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF" + "\x26\x25\x21\xFF\x29\x27\x21\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x26\x26\x20\xFF\x22\x21\x1B\xFF" + "\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x29\x28\x23\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x26\x26\x21\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x29\x28\x23\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x27\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x27\x25\x20\xFF\x22\x21\x1B\xFF\x27\x25\x21\xFF\x4F\x4A\x3D\xFF\x19\x18\x15\xFF\x1A\x19\x15\xFF" + "\x1D\x1B\x17\xFF\x1D\x1B\x17\xFF\x1E\x1C\x18\xFF\x1F\x1D\x18\xFF\x1D\x1B\x17\xFF\x1F\x1D\x19\xFF\x21\x1E\x1A\xFF\x22\x21\x1C\xFF" + "\x24\x22\x1D\xFF\x25\x22\x1C\xFF\x23\x22\x1D\xFF\x25\x22\x1D\xFF\x27\x24\x1F\xFF\x26\x23\x1F\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF" + "\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x24\x23\x1D\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1E\xFF" + "\x25\x24\x1E\xFF\x29\x26\x21\xFF\x28\x25\x1F\xFF\x27\x25\x1F\xFF\x28\x26\x21\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x28\x27\x21\xFF" + "\x28\x25\x20\xFF\x28\x25\x1F\xFF\x27\x24\x1F\xFF\x28\x26\x1F\xFF\x25\x24\x1E\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x28\x26\x1F\xFF" + "\x27\x24\x1F\xFF\x29\x26\x20\xFF\x27\x25\x1F\xFF\x25\x25\x1F\xFF\x27\x25\x1F\xFF\x27\x25\x1F\xFF\x27\x25\x1F\xFF\x27\x25\x20\xFF" + "\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x24\x24\x1E\xFF\x29\x26\x21\xFF\x28\x26\x1F\xFF\x26\x25\x1F\xFF\x25\x23\x1E\xFF\x26\x25\x1D\xFF" + "\x26\x23\x1E\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x27\x25\x20\xFF\x25\x24\x1E\xFF\x25\x24\x1E\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF" + "\x26\x24\x1F\xFF\x28\x25\x20\xFF\x2A\x26\x21\xFF\x27\x26\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x26\x20\xFF\x28\x25\x20\xFF" + "\x25\x23\x1E\xFF\x27\x25\x1F\xFF\x29\x26\x20\xFF\x24\x22\x1D\xFF\x26\x25\x20\xFF\x25\x24\x1F\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF" + "\x26\x24\x1F\xFF\x26\x25\x20\xFF\x26\x25\x20\xFF\x25\x23\x1E\xFF\x27\x26\x1F\xFF\x26\x24\x1E\xFF\x24\x22\x1D\xFF\x26\x23\x1E\xFF" + "\x26\x24\x1E\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF" + "\x26\x22\x1D\xFF\x24\x21\x1C\xFF\x25\x23\x1E\xFF\x29\x28\x22\xFF\x25\x22\x1C\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x27\x23\x1E\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x25\x23\x1D\xFF\x25\x22\x1D\xFF\x24\x22\x1D\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x29\x26\x20\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF" + "\x26\x25\x1F\xFF\x28\x25\x20\xFF\x25\x23\x1E\xFF\x25\x23\x1D\xFF\x28\x2B\x23\xFF\x35\x57\x43\xFF\x4E\xB1\x84\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x51\xBB\x8A\xFF\x36\x60\x49\xFF\x25\x2A\x23\xFF\x27\x25\x1F\xFF\x26\x23\x1E\xFF\x25\x22\x1D\xFF\x25\x23\x1E\xFF" + "\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x24\x21\x1D\xFF\x27\x24\x1F\xFF\x25\x25\x1F\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x26\x24\x1E\xFF" + "\x28\x2A\x23\xFF\x33\x50\x3D\xFF\x4E\xAC\x80\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x52\xBF\x8D\xFF\x34\x58\x43\xFF\x26\x28\x21\xFF\x24\x23\x1E\xFF\x25\x23\x1E\xFF\x27\x24\x1F\xFF" + "\x22\x22\x1C\xFF\x22\x22\x1B\xFF\x25\x22\x1E\xFF\x27\x24\x1F\xFF\x24\x23\x1D\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF" + "\x25\x22\x1D\xFF\x25\x23\x1E\xFF\x24\x22\x1E\xFF\x22\x21\x1B\xFF\x25\x22\x1D\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x24\x21\x1C\xFF" + "\x24\x23\x1D\xFF\x23\x21\x1C\xFF\x25\x23\x1E\xFF\x27\x24\x1F\xFF\x22\x22\x1C\xFF\x23\x23\x1D\xFF\x24\x23\x1E\xFF\x25\x23\x1E\xFF" + "\x23\x21\x1C\xFF\x25\x23\x1E\xFF\x25\x22\x1D\xFF\x23\x22\x1C\xFF\x26\x25\x1F\xFF\x23\x22\x1D\xFF\x21\x21\x1C\xFF\x25\x23\x1E\xFF" + "\x26\x25\x1F\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x25\x22\x1D\xFF\x24\x23\x1D\xFF\x22\x21\x1C\xFF\x25\x22\x1D\xFF\x23\x21\x1B\xFF" + "\x25\x22\x1D\xFF\x24\x21\x1C\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x24\x22\x1D\xFF\x24\x23\x1D\xFF\x23\x22\x1C\xFF\x23\x22\x1D\xFF" + "\x24\x22\x1D\xFF\x25\x23\x1E\xFF\x22\x21\x1C\xFF\x23\x22\x1D\xFF\x25\x22\x1D\xFF\x24\x23\x1D\xFF\x23\x22\x1C\xFF\x24\x22\x1D\xFF" + "\x24\x23\x1D\xFF\x24\x23\x1D\xFF\x25\x22\x1D\xFF\x23\x22\x1D\xFF\x25\x23\x1E\xFF\x25\x24\x1D\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF" + "\x22\x22\x1D\xFF\x23\x22\x1D\xFF\x24\x23\x1D\xFF\x24\x22\x1E\xFF\x24\x23\x1E\xFF\x22\x22\x1C\xFF\x24\x22\x1D\xFF\x23\x21\x1C\xFF" + "\x26\x23\x1E\xFF\x24\x23\x1E\xFF\x23\x21\x1C\xFF\x23\x22\x1C\xFF\x24\x22\x1D\xFF\x25\x22\x1D\xFF\x25\x23\x1D\xFF\x25\x23\x1E\xFF" + "\x25\x23\x1E\xFF\x24\x22\x1E\xFF\x24\x23\x1E\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF" + "\x25\x23\x1D\xFF\x25\x23\x1D\xFF\x23\x22\x1C\xFF\x25\x22\x1D\xFF\x24\x23\x1D\xFF\x24\x22\x1C\xFF\x22\x20\x1B\xFF\x23\x21\x1C\xFF" + "\x22\x22\x1C\xFF\x24\x21\x1D\xFF\x23\x22\x1D\xFF\x25\x22\x1D\xFF\x23\x22\x1C\xFF\x25\x23\x1D\xFF\x24\x21\x1C\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1C\xFF\x26\x23\x1E\xFF\x22\x21\x1C\xFF\x22\x20\x1B\xFF\x24\x21\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x22\x1D\xFF" + "\x24\x21\x1C\xFF\x24\x21\x1C\xFF\x23\x20\x1B\xFF\x20\x20\x19\xFF\x26\x23\x1E\xFF\x25\x23\x1C\xFF\x23\x22\x1C\xFF\x21\x22\x1B\xFF" + "\x22\x22\x1D\xFF\x27\x2C\x24\xFF\x26\x2A\x23\xFF\x23\x24\x1C\xFF\x23\x24\x1E\xFF\x23\x22\x1D\xFF\x24\x23\x1D\xFF\x22\x22\x1B\xFF" + "\x24\x22\x1D\xFF\x21\x1F\x1A\xFF\x22\x20\x1B\xFF\x21\x20\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x1E\xFF\x23\x22\x1D\xFF\x1F\x1F\x19\xFF" + "\x22\x21\x1B\xFF\x23\x20\x1B\xFF\x24\x22\x1D\xFF\x24\x23\x1D\xFF\x28\x25\x1E\xFF\x25\x22\x1D\xFF\x23\x22\x1D\xFF\x1F\x1E\x19\xFF" + "\x22\x22\x1C\xFF\x20\x1F\x1B\xFF\x22\x20\x1C\xFF\x20\x20\x1B\xFF\x21\x1F\x1B\xFF\x22\x21\x1C\xFF\x21\x20\x1B\xFF\x24\x22\x1D\xFF" + "\x26\x23\x1E\xFF\x26\x23\x1F\xFF\x24\x22\x1D\xFF\x22\x21\x1B\xFF\x24\x23\x1D\xFF\x23\x22\x1D\xFF\x23\x22\x1C\xFF\x25\x24\x1D\xFF" + "\x14\x12\x0F\xFF\x14\x13\x0F\xFF\x13\x12\x0F\xFF\x18\x17\x13\xFF\x13\x12\x0E\xFF\x14\x12\x10\xFF\x16\x13\x10\xFF\x14\x14\x0E\xFF" + "\x12\x10\x0E\xFF\x0F\x0E\x0B\xFF\x11\x10\x0D\xFF\x15\x13\x11\xFF\x16\x14\x10\xFF\x14\x12\x10\xFF\x15\x14\x10\xFF\x14\x13\x10\xFF" + "\x14\x13\x10\xFF\x11\x10\x0F\xFF\x14\x14\x10\xFF\x15\x14\x10\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x19\x18\x14\xFF\x15\x14\x10\xFF" + "\x12\x10\x0E\xFF\x10\x10\x0C\xFF\x10\x0F\x0D\xFF\x13\x12\x0F\xFF\x16\x15\x11\xFF\x14\x12\x10\xFF\x14\x13\x10\xFF\x14\x13\x10\xFF" + "\x12\x10\x0F\xFF\x13\x12\x0F\xFF\x15\x13\x11\xFF\x14\x13\x0F\xFF\x14\x13\x10\xFF\x14\x13\x10\xFF\x13\x11\x0F\xFF\x15\x13\x11\xFF" + "\x15\x14\x11\xFF\x13\x12\x0E\xFF\x12\x11\x0E\xFF\x12\x10\x0E\xFF\x12\x11\x0D\xFF\x14\x13\x0F\xFF\x14\x14\x0F\xFF\x12\x11\x0D\xFF" + "\x12\x11\x0E\xFF\x15\x13\x11\xFF\x15\x14\x11\xFF\x15\x14\x11\xFF\x12\x12\x0F\xFF\x14\x14\x0F\xFF\x14\x13\x11\xFF\x14\x13\x10\xFF" + "\x17\x16\x12\xFF\x15\x14\x10\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x14\x12\x10\xFF\x14\x12\x10\xFF\x15\x14\x10\xFF\x13\x12\x0E\xFF" + "\x14\x13\x0F\xFF\x13\x12\x0E\xFF\x11\x10\x0D\xFF\x11\x10\x0C\xFF\x15\x14\x10\xFF\x17\x16\x12\xFF\x16\x14\x11\xFF\x18\x17\x13\xFF" + "\x13\x11\x10\xFF\x14\x12\x10\xFF\x16\x15\x11\xFF\x14\x13\x0F\xFF\x12\x12\x10\xFF\x15\x13\x10\xFF\x12\x12\x0F\xFF\x0F\x0F\x0D\xFF" + "\x13\x11\x10\xFF\x14\x13\x10\xFF\x17\x14\x11\xFF\x15\x14\x10\xFF\x13\x12\x0E\xFF\x12\x11\x0F\xFF\x14\x13\x0F\xFF\x13\x11\x10\xFF" + "\x15\x14\x11\xFF\x16\x15\x11\xFF\x19\x18\x14\xFF\x15\x14\x11\xFF\x13\x11\x0E\xFF\x13\x11\x10\xFF\x14\x13\x11\xFF\x12\x11\x0F\xFF" + "\x17\x16\x12\xFF\x19\x18\x14\xFF\x18\x16\x12\xFF\x18\x16\x13\xFF\x14\x13\x11\xFF\x14\x13\x10\xFF\x17\x15\x14\xFF\x15\x14\x10\xFF" + "\x18\x17\x13\xFF\x18\x17\x12\xFF\x18\x17\x13\xFF\x1A\x19\x15\xFF\x14\x13\x0F\xFF\x16\x14\x12\xFF\x17\x16\x12\xFF\x14\x13\x0F\xFF" + "\x14\x12\x11\xFF\x18\x17\x13\xFF\x15\x14\x11\xFF\x15\x13\x11\xFF\x14\x13\x0F\xFF\x19\x16\x12\xFF\x1A\x19\x15\xFF\x17\x16\x12\xFF" + "\x15\x13\x11\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF\x14\x13\x10\xFF\x18\x17\x12\xFF\x15\x14\x11\xFF\x16\x14\x11\xFF\x17\x16\x12\xFF" + "\x19\x17\x13\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x19\x18\x13\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x18\x18\x14\xFF\x17\x16\x12\xFF" + "\x15\x14\x10\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x1B\x1A\x16\xFF\x1B\x1A\x16\xFF\x18\x17\x14\xFF\x18\x16\x13\xFF\x1B\x19\x15\xFF" + "\x17\x15\x11\xFF\x14\x15\x11\xFF\x15\x15\x11\xFF\x15\x14\x10\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x1B\x1A\x16\xFF" + "\x19\x18\x14\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x18\x17\x12\xFF\x14\x13\x0F\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x17\x15\x11\xFF" + "\x19\x18\x14\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x16\x14\x11\xFF\x1B\x1A\x16\xFF" + "\x1A\x19\x15\xFF\x1A\x19\x16\xFF\x19\x19\x15\xFF\x17\x15\x13\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x16\x15\x11\xFF\x1A\x19\x15\xFF" + "\x1F\x1D\x19\xFF\x19\x18\x14\xFF\x1B\x19\x15\xFF\x1B\x1A\x15\xFF\x18\x17\x13\xFF\x1A\x18\x14\xFF\x19\x18\x14\xFF\x19\x17\x13\xFF" + "\x1B\x1A\x16\xFF\x19\x18\x14\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x1D\x1C\x18\xFF\x19\x18\x14\xFF\x18\x17\x14\xFF\x18\x16\x13\xFF" + "\x19\x17\x13\xFF\x1D\x1A\x16\xFF\x1A\x19\x15\xFF\x17\x16\x12\xFF\x1A\x18\x14\xFF\x19\x18\x14\xFF\x18\x17\x14\xFF\x1A\x19\x15\xFF" + "\x19\x18\x14\xFF\x19\x18\x13\xFF\x19\x17\x13\xFF\x19\x17\x13\xFF\x1C\x1A\x16\xFF\x1B\x19\x15\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF" + "\x1A\x18\x14\xFF\x1C\x1A\x16\xFF\x19\x17\x13\xFF\x17\x16\x12\xFF\x1B\x1A\x16\xFF\x1E\x1B\x17\xFF\x1A\x17\x14\xFF\x19\x18\x14\xFF" + "\x1B\x1A\x16\xFF\x19\x18\x14\xFF\x1B\x19\x15\xFF\x1D\x1A\x16\xFF\x1C\x1B\x17\xFF\x1B\x1A\x16\xFF\x1C\x1B\x17\xFF\x1C\x1B\x16\xFF" + "\x1D\x1C\x18\xFF\x18\x16\x12\xFF\x19\x17\x15\xFF\x19\x18\x15\xFF\x1A\x19\x15\xFF\x1C\x1A\x16\xFF\x1E\x1C\x18\xFF\x20\x1F\x1A\xFF" + "\x18\x17\x13\xFF\x18\x16\x12\xFF\x1B\x19\x14\xFF\x1D\x1D\x17\xFF\x19\x1A\x15\xFF\x1F\x1E\x19\xFF\x1F\x1D\x19\xFF\x20\x1E\x19\xFF" + "\x1A\x19\x14\xFF\x1A\x18\x14\xFF\x1A\x19\x15\xFF\x1D\x1B\x17\xFF\x20\x1D\x1A\xFF\x1F\x1F\x19\xFF\x1E\x1D\x19\xFF\x1B\x19\x16\xFF" + "\x1C\x1B\x16\xFF\x1B\x1B\x16\xFF\x1D\x1C\x17\xFF\x1F\x1D\x18\xFF\x1B\x1B\x16\xFF\x1A\x19\x15\xFF\x1C\x1A\x16\xFF\x1D\x1B\x17\xFF" + "\x1D\x1C\x16\xFF\x19\x18\x14\xFF\x1A\x1A\x15\xFF\x1D\x1D\x17\xFF\x21\x1E\x1A\xFF\x1D\x1D\x18\xFF\x1F\x1E\x19\xFF\x1B\x1A\x16\xFF" + "\x1C\x1B\x16\xFF\x1D\x1C\x18\xFF\x1C\x1B\x16\xFF\x1A\x19\x15\xFF\x1A\x1A\x15\xFF\x1D\x1C\x18\xFF\x1F\x1D\x19\xFF\x1F\x1C\x18\xFF" + "\x1D\x1C\x16\xFF\x1E\x1D\x19\xFF\x1D\x1C\x18\xFF\x1D\x1A\x16\xFF\x1E\x1F\x19\xFF\x1F\x1E\x1A\xFF\x1E\x1C\x19\xFF\x20\x1E\x1A\xFF" + "\x1E\x1D\x18\xFF\x1E\x1C\x16\xFF\x1C\x1C\x17\xFF\x1D\x1D\x17\xFF\x1B\x1A\x16\xFF\x1C\x1A\x16\xFF\x1E\x1C\x18\xFF\x22\x20\x1B\xFF" + "\x1F\x1D\x18\xFF\x1D\x1C\x18\xFF\x1F\x1E\x19\xFF\x20\x1E\x1A\xFF\x1E\x1E\x18\xFF\x1F\x1D\x19\xFF\x1E\x1D\x18\xFF\x1D\x1C\x17\xFF" + "\x1F\x1E\x1A\xFF\x22\x21\x1C\xFF\x21\x20\x1A\xFF\x1E\x1E\x18\xFF\x1C\x1A\x16\xFF\x1F\x1D\x19\xFF\x21\x1F\x1A\xFF\x23\x20\x1B\xFF" + "\x1F\x1F\x1A\xFF\x22\x21\x1C\xFF\x21\x20\x1B\xFF\x22\x22\x1C\xFF\x1C\x1C\x18\xFF\x1D\x1D\x19\xFF\x22\x1F\x1A\xFF\x1F\x1E\x19\xFF" + "\x1A\x1A\x15\xFF\x1C\x1B\x16\xFF\x1D\x1A\x17\xFF\x1C\x1B\x18\xFF\x1F\x1E\x19\xFF\x1C\x1B\x16\xFF\x1B\x1A\x16\xFF\x1E\x1B\x17\xFF" + "\x1A\x1B\x17\xFF\x1D\x1B\x16\xFF\x1C\x1B\x17\xFF\x19\x18\x15\xFF\x17\x16\x13\xFF\x17\x16\x13\xFF\x19\x17\x14\xFF\x19\x19\x15\xFF" + "\x16\x16\x12\xFF\x15\x15\x11\xFF\x50\x4C\x40\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x21\x20\x1A\xFF\x21\x20\x1B\xFF\x24\x22\x1D\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x28\x27\x21\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x23\x22\x1D\xFF\x21\x20\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x23\x21\x1C\xFF\x22\x22\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x21\x20\x1B\xFF\x22\x20\x1B\xFF\x23\x21\x1C\xFF\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x21\x20\x1A\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1D\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1D\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x22\x21\x1B\xFF\x21\x20\x1A\xFF" + "\x26\x25\x20\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x23\x21\x1C\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x21\x21\x1B\xFF" + "\x25\x24\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x23\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x23\x21\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x23\x1E\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF" + "\x24\x22\x1E\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x26\x25\x20\xFF\x22\x20\x1B\xFF\x26\x25\x20\xFF\x29\x28\x23\xFF\x26\x25\x20\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x22\x21\x1B\xFF" + "\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1D\xFF\x22\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF" + "\x24\x23\x1E\xFF\x22\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x24\x23\x1D\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF\x24\x22\x1D\xFF" + "\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x21\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF" + "\x26\x24\x20\xFF\x27\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF" + "\x25\x24\x20\xFF\x25\x24\x1F\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x28\x26\x21\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x25\x25\x20\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x25\x24\x1F\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x2A\x29\x23\xFF\x26\x25\x20\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF" + "\x23\x22\x1C\xFF\x27\x25\x21\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x26\x24\x20\xFF" + "\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x27\x25\x20\xFF\x25\x24\x20\xFF" + "\x27\x25\x20\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x23\x1E\xFF\x22\x21\x1B\xFF\x29\x28\x22\xFF\x27\x25\x20\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1E\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x27\x25\x20\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x27\x25\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x26\x24\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x23\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x25\x24\x1E\xFF\x27\x26\x20\xFF\x22\x21\x1B\xFF\x27\x25\x20\xFF\x29\x28\x23\xFF\x26\x25\x21\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x24\x23\x1E\xFF\x22\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x29\x26\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF" + "\x26\x25\x20\xFF\x26\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x26\x25\x20\xFF\x25\x24\x1F\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x28\x26\x21\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x25\x24\x1F\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x2A\x29\x23\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x27\x25\x21\xFF\x4F\x4A\x3D\xFF\x19\x18\x15\xFF\x1B\x1A\x16\xFF" + "\x1D\x1B\x16\xFF\x1D\x1D\x17\xFF\x1D\x1C\x18\xFF\x1E\x1E\x19\xFF\x20\x1D\x19\xFF\x22\x1F\x1B\xFF\x22\x20\x1B\xFF\x24\x21\x1D\xFF" + "\x22\x1F\x1B\xFF\x24\x22\x1D\xFF\x25\x22\x1E\xFF\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x23\x23\x1D\xFF\x25\x22\x1D\xFF\x27\x23\x1E\xFF" + "\x26\x23\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x25\x1F\xFF\x28\x27\x20\xFF\x28\x26\x21\xFF\x27\x24\x1F\xFF" + "\x25\x24\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x28\x26\x21\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF" + "\x27\x26\x20\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF\x24\x23\x1E\xFF\x26\x24\x1E\xFF" + "\x25\x23\x1E\xFF\x27\x26\x20\xFF\x27\x25\x1F\xFF\x29\x27\x20\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF" + "\x28\x25\x20\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x24\x23\x1E\xFF\x25\x23\x1E\xFF\x27\x25\x1F\xFF" + "\x27\x25\x1E\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x25\x26\x1E\xFF\x26\x25\x1F\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF" + "\x28\x26\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x23\x22\x1D\xFF\x28\x25\x20\xFF\x29\x27\x20\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF" + "\x23\x22\x1C\xFF\x28\x25\x1F\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x25\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF" + "\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x25\x20\xFF\x26\x24\x1F\xFF\x23\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x24\x23\x1E\xFF\x25\x24\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1E\xFF\x27\x26\x1F\xFF\x26\x23\x1E\xFF\x25\x22\x1D\xFF" + "\x26\x23\x1E\xFF\x26\x24\x1E\xFF\x27\x26\x20\xFF\x28\x26\x21\xFF\x25\x22\x1D\xFF\x27\x26\x1F\xFF\x27\x25\x1E\xFF\x24\x23\x1D\xFF" + "\x24\x22\x1E\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x25\x26\x1D\xFF\x29\x26\x21\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x25\x1E\xFF" + "\x27\x24\x1F\xFF\x25\x23\x1D\xFF\x27\x25\x1E\xFF\x27\x27\x21\xFF\x27\x24\x21\xFF\x28\x26\x1F\xFF\x26\x23\x1E\xFF\x24\x23\x1E\xFF" + "\x24\x23\x1E\xFF\x24\x22\x1D\xFF\x23\x21\x1D\xFF\x26\x23\x1E\xFF\x29\x29\x22\xFF\x2E\x3E\x31\xFF\x48\x9C\x74\xFF\x56\xCC\x97\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x56\xCA\x96\xFF\x3F\x7F\x5F\xFF\x28\x33\x28\xFF\x27\x27\x20\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x24\x23\x1E\xFF" + "\x25\x22\x1D\xFF\x23\x21\x1C\xFF\x23\x21\x1C\xFF\x27\x24\x1F\xFF\x25\x24\x1E\xFF\x24\x24\x1E\xFF\x23\x22\x1E\xFF\x24\x23\x1F\xFF" + "\x25\x27\x1F\xFF\x2B\x38\x2C\xFF\x46\x94\x6F\xFF\x57\xCB\x97\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCC\x97\xFF\x3E\x7D\x5E\xFF\x28\x2E\x26\xFF\x24\x24\x1F\xFF\x23\x22\x1D\xFF\x26\x23\x1E\xFF" + "\x27\x25\x20\xFF\x26\x24\x1E\xFF\x24\x23\x1D\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x22\x20\x1C\xFF\x24\x22\x1D\xFF\x24\x24\x1E\xFF" + "\x25\x22\x1E\xFF\x26\x23\x1E\xFF\x25\x22\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x22\x21\x1B\xFF\x27\x24\x1F\xFF" + "\x27\x24\x1F\xFF\x24\x21\x1D\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x24\x22\x1C\xFF\x24\x23\x1D\xFF\x23\x23\x1D\xFF\x25\x22\x1D\xFF" + "\x21\x21\x1B\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x24\x21\x1D\xFF\x23\x22\x1C\xFF\x25\x23\x1E\xFF\x23\x22\x1C\xFF\x25\x22\x1D\xFF" + "\x26\x25\x1F\xFF\x26\x23\x1F\xFF\x23\x22\x1D\xFF\x24\x21\x1C\xFF\x26\x24\x1F\xFF\x23\x22\x1C\xFF\x22\x21\x1C\xFF\x22\x22\x1C\xFF" + "\x24\x22\x1D\xFF\x23\x21\x1C\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x22\x22\x1B\xFF\x25\x23\x1C\xFF\x26\x24\x1E\xFF\x22\x21\x1C\xFF" + "\x24\x22\x1D\xFF\x23\x22\x1C\xFF\x21\x21\x1C\xFF\x24\x22\x1D\xFF\x23\x22\x1C\xFF\x25\x25\x1E\xFF\x24\x23\x1E\xFF\x26\x24\x1D\xFF" + "\x23\x20\x1C\xFF\x23\x21\x1C\xFF\x24\x21\x1C\xFF\x25\x22\x1E\xFF\x23\x23\x1D\xFF\x26\x24\x1E\xFF\x24\x23\x1D\xFF\x24\x22\x1E\xFF" + "\x23\x23\x1D\xFF\x25\x22\x1D\xFF\x25\x22\x1D\xFF\x23\x22\x1D\xFF\x20\x1F\x1A\xFF\x21\x20\x1B\xFF\x25\x21\x1D\xFF\x25\x22\x1E\xFF" + "\x21\x20\x1B\xFF\x22\x20\x1C\xFF\x24\x22\x1D\xFF\x23\x21\x1C\xFF\x23\x20\x1C\xFF\x25\x22\x1D\xFF\x23\x21\x1C\xFF\x21\x22\x1A\xFF" + "\x24\x21\x1C\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x25\x22\x1D\xFF\x24\x23\x1D\xFF\x25\x24\x1E\xFF\x25\x24\x1E\xFF\x25\x22\x1D\xFF" + "\x27\x24\x1F\xFF\x25\x22\x1D\xFF\x23\x22\x1C\xFF\x24\x23\x1D\xFF\x22\x22\x1A\xFF\x25\x22\x1D\xFF\x26\x22\x1D\xFF\x25\x22\x1D\xFF" + "\x23\x21\x1C\xFF\x22\x21\x1C\xFF\x24\x21\x1C\xFF\x24\x21\x1C\xFF\x22\x22\x1C\xFF\x22\x20\x1B\xFF\x22\x20\x1B\xFF\x23\x23\x1D\xFF" + "\x26\x24\x1F\xFF\x22\x22\x1C\xFF\x21\x20\x1B\xFF\x22\x1F\x1A\xFF\x21\x20\x1B\xFF\x21\x20\x1B\xFF\x22\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x24\x21\x1C\xFF\x23\x20\x1B\xFF\x24\x21\x1C\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x25\x23\x1D\xFF" + "\x21\x21\x1B\xFF\x24\x23\x1D\xFF\x26\x23\x1E\xFF\x26\x24\x1C\xFF\x26\x26\x20\xFF\x28\x25\x20\xFF\x23\x22\x1D\xFF\x20\x20\x1B\xFF" + "\x23\x22\x1D\xFF\x23\x21\x1C\xFF\x21\x21\x1B\xFF\x21\x21\x1C\xFF\x25\x23\x1D\xFF\x24\x22\x1D\xFF\x23\x21\x1C\xFF\x20\x20\x1A\xFF" + "\x22\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x23\x1D\xFF\x22\x21\x1C\xFF\x25\x23\x1D\xFF\x23\x21\x1D\xFF\x22\x20\x1B\xFF\x22\x22\x1D\xFF" + "\x25\x22\x1D\xFF\x22\x21\x1C\xFF\x21\x20\x1B\xFF\x20\x20\x1A\xFF\x21\x21\x1C\xFF\x21\x20\x1B\xFF\x20\x20\x1B\xFF\x25\x22\x1D\xFF" + "\x22\x20\x1C\xFF\x26\x24\x20\xFF\x23\x23\x1D\xFF\x24\x23\x1D\xFF\x22\x1F\x1B\xFF\x24\x22\x1E\xFF\x25\x22\x1E\xFF\x24\x23\x1D\xFF" + "\x0F\x0F\x0D\xFF\x13\x12\x0F\xFF\x12\x11\x0E\xFF\x12\x10\x0E\xFF\x11\x10\x0D\xFF\x12\x11\x0E\xFF\x13\x12\x0F\xFF\x11\x0F\x0E\xFF" + "\x12\x11\x0E\xFF\x12\x11\x0F\xFF\x13\x11\x0E\xFF\x12\x11\x0D\xFF\x19\x18\x14\xFF\x16\x15\x12\xFF\x12\x11\x0E\xFF\x11\x10\x0F\xFF" + "\x14\x13\x0F\xFF\x16\x14\x10\xFF\x13\x12\x0E\xFF\x16\x15\x11\xFF\x17\x15\x11\xFF\x13\x13\x0F\xFF\x12\x12\x0E\xFF\x13\x11\x0F\xFF" + "\x13\x12\x0F\xFF\x13\x11\x0F\xFF\x12\x10\x0E\xFF\x16\x14\x11\xFF\x18\x19\x12\xFF\x14\x12\x0F\xFF\x14\x10\x0F\xFF\x12\x10\x0F\xFF" + "\x12\x10\x0F\xFF\x14\x12\x11\xFF\x15\x14\x11\xFF\x18\x18\x12\xFF\x14\x13\x0F\xFF\x13\x12\x0E\xFF\x15\x14\x11\xFF\x14\x12\x11\xFF" + "\x13\x12\x0E\xFF\x13\x12\x0E\xFF\x13\x12\x0E\xFF\x13\x12\x10\xFF\x12\x11\x0F\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF\x19\x19\x14\xFF" + "\x14\x14\x0F\xFF\x12\x12\x0D\xFF\x12\x10\x0E\xFF\x14\x13\x0F\xFF\x11\x10\x0E\xFF\x13\x13\x10\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF" + "\x10\x0F\x0B\xFF\x11\x10\x0E\xFF\x15\x14\x10\xFF\x14\x12\x10\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF\x14\x13\x0F\xFF\x16\x15\x11\xFF" + "\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x15\x13\x10\xFF\x15\x13\x10\xFF\x14\x13\x0F\xFF\x14\x13\x10\xFF\x13\x12\x0F\xFF\x11\x10\x0C\xFF" + "\x14\x13\x0F\xFF\x14\x13\x10\xFF\x11\x11\x0F\xFF\x12\x11\x0D\xFF\x14\x11\x0D\xFF\x16\x13\x0F\xFF\x15\x13\x12\xFF\x16\x15\x11\xFF" + "\x13\x13\x0F\xFF\x13\x12\x0F\xFF\x16\x15\x12\xFF\x16\x15\x11\xFF\x12\x10\x0F\xFF\x14\x13\x0F\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF" + "\x15\x14\x11\xFF\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x14\x13\x0F\xFF\x14\x13\x10\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x14\x13\x10\xFF" + "\x15\x14\x11\xFF\x16\x15\x13\xFF\x16\x14\x11\xFF\x17\x14\x10\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x15\x14\x11\xFF" + "\x17\x16\x13\xFF\x11\x10\x0E\xFF\x13\x12\x0E\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF" + "\x13\x11\x0F\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x16\x15\x12\xFF\x15\x14\x10\xFF\x14\x13\x0E\xFF\x17\x16\x11\xFF\x18\x17\x12\xFF" + "\x16\x15\x11\xFF\x16\x15\x11\xFF\x19\x18\x14\xFF\x16\x15\x11\xFF\x14\x12\x10\xFF\x14\x13\x0F\xFF\x14\x13\x0E\xFF\x14\x13\x0F\xFF" + "\x18\x17\x13\xFF\x16\x16\x12\xFF\x12\x12\x0F\xFF\x14\x13\x0F\xFF\x16\x14\x11\xFF\x15\x13\x11\xFF\x17\x15\x12\xFF\x18\x17\x13\xFF" + "\x18\x17\x13\xFF\x15\x14\x10\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x16\x15\x11\xFF\x13\x13\x0E\xFF\x16\x15\x11\xFF\x16\x14\x10\xFF" + "\x15\x13\x11\xFF\x18\x17\x14\xFF\x1C\x1A\x16\xFF\x17\x15\x11\xFF\x14\x13\x0F\xFF\x18\x16\x12\xFF\x1B\x18\x14\xFF\x17\x15\x11\xFF" + "\x14\x13\x0F\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x19\x18\x14\xFF\x1B\x1A\x16\xFF\x1B\x1A\x16\xFF" + "\x17\x16\x13\xFF\x15\x14\x10\xFF\x16\x15\x10\xFF\x1B\x1A\x16\xFF\x17\x16\x12\xFF\x1B\x1A\x16\xFF\x18\x17\x13\xFF\x18\x18\x12\xFF" + "\x1A\x17\x14\xFF\x1A\x18\x14\xFF\x1B\x1A\x15\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x1B\x1A\x16\xFF" + "\x19\x17\x13\xFF\x18\x17\x13\xFF\x1A\x18\x14\xFF\x1B\x19\x16\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x1B\x1A\x16\xFF\x1B\x1B\x16\xFF" + "\x1B\x19\x15\xFF\x18\x16\x12\xFF\x16\x15\x11\xFF\x18\x17\x15\xFF\x1E\x1E\x18\xFF\x1D\x1C\x18\xFF\x1A\x19\x14\xFF\x19\x18\x13\xFF" + "\x1A\x18\x14\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x1A\x19\x15\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF" + "\x19\x18\x14\xFF\x1A\x19\x14\xFF\x1C\x1A\x15\xFF\x1A\x19\x14\xFF\x19\x1A\x15\xFF\x1B\x19\x15\xFF\x1D\x1A\x16\xFF\x1E\x1C\x18\xFF" + "\x1F\x1D\x19\xFF\x1A\x18\x14\xFF\x1A\x18\x14\xFF\x1A\x19\x15\xFF\x1B\x19\x17\xFF\x1A\x19\x14\xFF\x1B\x19\x15\xFF\x1A\x18\x14\xFF" + "\x1B\x1A\x16\xFF\x1B\x1A\x15\xFF\x1D\x1C\x17\xFF\x1F\x1E\x19\xFF\x1E\x1C\x17\xFF\x1B\x1B\x15\xFF\x1C\x1B\x17\xFF\x1B\x19\x15\xFF" + "\x1B\x1A\x16\xFF\x1B\x19\x15\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x1B\x1A\x16\xFF\x1C\x1A\x17\xFF\x1D\x1B\x18\xFF\x1B\x1B\x15\xFF" + "\x20\x1E\x19\xFF\x1D\x1B\x17\xFF\x1D\x1C\x18\xFF\x1B\x1A\x16\xFF\x1D\x1B\x17\xFF\x1C\x1B\x17\xFF\x1E\x1D\x19\xFF\x1A\x19\x15\xFF" + "\x19\x18\x14\xFF\x19\x19\x15\xFF\x19\x17\x13\xFF\x19\x18\x14\xFF\x1F\x1E\x19\xFF\x1E\x1C\x18\xFF\x1F\x1D\x19\xFF\x1F\x1F\x1A\xFF" + "\x1D\x1B\x17\xFF\x1C\x1A\x16\xFF\x1A\x18\x16\xFF\x18\x17\x13\xFF\x19\x19\x14\xFF\x1E\x1C\x17\xFF\x1B\x19\x15\xFF\x1C\x1B\x17\xFF" + "\x1F\x1C\x18\xFF\x1B\x1A\x16\xFF\x1D\x1C\x18\xFF\x1A\x19\x16\xFF\x1E\x1B\x17\xFF\x20\x1E\x1A\xFF\x1F\x1E\x1A\xFF\x1C\x1B\x17\xFF" + "\x1C\x19\x15\xFF\x20\x1E\x1A\xFF\x22\x20\x1B\xFF\x1F\x1C\x17\xFF\x1F\x1C\x18\xFF\x1E\x1C\x18\xFF\x1C\x1B\x17\xFF\x1A\x19\x15\xFF" + "\x1C\x1B\x17\xFF\x1E\x1C\x18\xFF\x1C\x1B\x18\xFF\x1C\x1C\x17\xFF\x1F\x1E\x1A\xFF\x1D\x1B\x17\xFF\x1E\x1E\x19\xFF\x1D\x1B\x17\xFF" + "\x20\x1F\x1A\xFF\x1D\x1C\x17\xFF\x1D\x1C\x17\xFF\x20\x1D\x19\xFF\x1F\x1E\x19\xFF\x21\x21\x1A\xFF\x1F\x1D\x18\xFF\x1C\x1C\x18\xFF" + "\x1D\x1D\x18\xFF\x20\x20\x1A\xFF\x21\x21\x1B\xFF\x22\x20\x1B\xFF\x1E\x1E\x19\xFF\x1E\x1E\x19\xFF\x1E\x1D\x18\xFF\x1D\x1C\x18\xFF" + "\x20\x1F\x1A\xFF\x22\x20\x1C\xFF\x21\x1F\x1B\xFF\x1F\x1F\x19\xFF\x1E\x1E\x18\xFF\x1C\x1B\x17\xFF\x1F\x1D\x19\xFF\x1E\x1C\x18\xFF" + "\x1D\x1C\x18\xFF\x1C\x1B\x16\xFF\x1D\x1D\x17\xFF\x1F\x1E\x1A\xFF\x1E\x1F\x17\xFF\x1E\x1D\x18\xFF\x1F\x1E\x19\xFF\x20\x20\x1A\xFF" + "\x1F\x1E\x19\xFF\x1F\x1D\x18\xFF\x1F\x1D\x19\xFF\x1F\x1D\x1A\xFF\x1E\x1C\x17\xFF\x1D\x1B\x17\xFF\x1D\x1C\x18\xFF\x1F\x1E\x19\xFF" + "\x1E\x1C\x18\xFF\x1B\x1B\x16\xFF\x1A\x19\x15\xFF\x1C\x1B\x18\xFF\x1A\x18\x15\xFF\x18\x18\x14\xFF\x17\x17\x13\xFF\x17\x17\x13\xFF" + "\x16\x16\x12\xFF\x15\x14\x11\xFF\x4E\x4A\x3D\xFF\x23\x22\x1D\xFF\x21\x20\x1A\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF" + "\x22\x21\x1A\xFF\x26\x25\x20\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x21\x21\x1B\xFF" + "\x28\x27\x21\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x28\x26\x22\xFF\x26\x25\x21\xFF\x28\x26\x21\xFF\x21\x20\x1A\xFF" + "\x28\x26\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x21\x20\x1B\xFF\x22\x21\x1C\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF\x21\x20\x1A\xFF" + "\x25\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x23\x1E\xFF\x25\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF" + "\x25\x24\x20\xFF\x28\x26\x21\xFF\x23\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x25\x24\x20\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1F\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x26\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x20\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF" + "\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x23\x22\x1C\xFF\x22\x22\x1C\xFF\x2A\x29\x23\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x26\x25\x21\xFF\x23\x22\x1C\xFF\x27\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x28\x26\x21\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x22\x1C\xFF" + "\x24\x23\x1E\xFF\x29\x28\x22\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x22\x1C\xFF\x23\x22\x1C\xFF\x28\x26\x22\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x21\x20\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x25\x23\x1E\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x28\x26\x21\xFF\x27\x25\x20\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x25\x23\x1E\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x29\x28\x23\xFF\x26\x25\x20\xFF" + "\x25\x24\x20\xFF\x26\x25\x20\xFF\x29\x28\x22\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x2A\x29\x23\xFF\x25\x24\x20\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x2B\x2A\x24\xFF" + "\x26\x25\x21\xFF\x23\x23\x1E\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x27\x26\x20\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF" + "\x26\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x29\x28\x22\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x28\x26\x22\xFF\x26\x25\x21\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x28\x26\x21\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x25\x24\x20\xFF\x28\x26\x21\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x25\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x27\x25\x20\xFF\x26\x24\x20\xFF\x23\x22\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF" + "\x25\x24\x20\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x26\x25\x21\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x27\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x26\x25\x20\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x22\x22\x1C\xFF\x2A\x29\x23\xFF\x22\x21\x1B\xFF\x27\x25\x20\xFF" + "\x27\x25\x21\xFF\x23\x22\x1C\xFF\x27\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x28\x26\x21\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x24\x23\x1E\xFF\x29\x28\x22\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x22\x1C\xFF\x23\x22\x1C\xFF\x29\x27\x22\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x28\x26\x21\xFF\x27\x26\x21\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x27\x26\x21\xFF" + "\x25\x24\x1F\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x29\x28\x23\xFF\x26\x25\x20\xFF" + "\x26\x24\x20\xFF\x27\x25\x20\xFF\x29\x28\x22\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x4F\x4A\x3D\xFF\x1A\x19\x15\xFF\x19\x19\x15\xFF" + "\x1B\x1A\x16\xFF\x1D\x1B\x17\xFF\x1D\x1C\x18\xFF\x1D\x1C\x18\xFF\x21\x1E\x19\xFF\x21\x1F\x1B\xFF\x21\x20\x1B\xFF\x21\x21\x1B\xFF" + "\x23\x20\x1C\xFF\x22\x22\x1C\xFF\x21\x21\x1C\xFF\x25\x22\x1D\xFF\x24\x24\x1D\xFF\x25\x24\x1F\xFF\x27\x25\x1F\xFF\x24\x23\x1E\xFF" + "\x24\x23\x1D\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x25\x24\x1F\xFF\x25\x24\x1E\xFF\x23\x23\x1D\xFF\x27\x25\x1F\xFF" + "\x28\x25\x20\xFF\x29\x26\x21\xFF\x25\x24\x1E\xFF\x24\x22\x1D\xFF\x28\x26\x20\xFF\x27\x25\x1F\xFF\x26\x23\x1E\xFF\x27\x24\x1E\xFF" + "\x27\x24\x1F\xFF\x24\x23\x1E\xFF\x26\x24\x1F\xFF\x28\x28\x22\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF" + "\x26\x23\x1E\xFF\x29\x27\x20\xFF\x2A\x27\x22\xFF\x29\x26\x1F\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF" + "\x28\x25\x20\xFF\x27\x25\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x26\x25\x1F\xFF\x26\x24\x1F\xFF\x28\x24\x1F\xFF\x29\x25\x20\xFF" + "\x27\x26\x20\xFF\x23\x23\x1D\xFF\x26\x24\x1E\xFF\x29\x26\x21\xFF\x25\x24\x1E\xFF\x24\x24\x1E\xFF\x23\x24\x1E\xFF\x28\x28\x21\xFF" + "\x28\x25\x20\xFF\x27\x24\x1F\xFF\x25\x23\x1D\xFF\x25\x24\x1E\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x26\x23\x1E\xFF\x27\x25\x20\xFF" + "\x25\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x24\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x25\x20\xFF\x28\x26\x21\xFF\x28\x26\x21\xFF" + "\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x26\x25\x1F\xFF\x27\x25\x20\xFF\x25\x25\x1F\xFF\x24\x24\x1D\xFF\x27\x26\x1E\xFF\x27\x26\x20\xFF" + "\x25\x25\x1F\xFF\x26\x24\x1F\xFF\x25\x22\x1E\xFF\x26\x23\x1E\xFF\x24\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x28\x26\x20\xFF" + "\x25\x22\x1E\xFF\x25\x23\x1E\xFF\x27\x25\x20\xFF\x28\x27\x20\xFF\x26\x26\x1E\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x25\x25\x1F\xFF" + "\x28\x26\x1F\xFF\x27\x25\x1F\xFF\x26\x24\x1F\xFF\x23\x23\x1D\xFF\x25\x24\x20\xFF\x26\x25\x20\xFF\x29\x25\x20\xFF\x26\x24\x1F\xFF" + "\x24\x22\x1D\xFF\x23\x23\x1D\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x24\x23\x1D\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF" + "\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x25\x26\x1F\xFF\x28\x2F\x25\xFF\x3E\x78\x5B\xFF\x54\xC4\x91\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x4C\xA5\x7B\xFF\x30\x46\x37\xFF\x27\x27\x21\xFF\x27\x25\x1F\xFF\x25\x24\x1E\xFF\x26\x23\x1E\xFF" + "\x24\x23\x1C\xFF\x26\x23\x1E\xFF\x24\x22\x1D\xFF\x27\x24\x1F\xFF\x25\x24\x1E\xFF\x29\x26\x21\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF" + "\x27\x26\x21\xFF\x28\x2C\x26\xFF\x39\x6C\x52\xFF\x53\xC1\x8F\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCC\x97\xFF\x4F\xB5\x85\xFF\x29\x38\x2D\xFF\x27\x28\x21\xFF\x24\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x24\x24\x1E\xFF\x24\x22\x1D\xFF\x24\x23\x1D\xFF\x25\x25\x1F\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x24\x22\x1D\xFF\x22\x21\x1C\xFF" + "\x25\x22\x1D\xFF\x25\x24\x1F\xFF\x24\x23\x1D\xFF\x26\x24\x1E\xFF\x27\x25\x1F\xFF\x25\x23\x1E\xFF\x24\x22\x1E\xFF\x25\x25\x1E\xFF" + "\x26\x24\x1F\xFF\x23\x22\x1D\xFF\x25\x23\x1D\xFF\x25\x22\x1D\xFF\x25\x24\x1E\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF" + "\x23\x23\x1D\xFF\x25\x25\x1F\xFF\x27\x26\x21\xFF\x26\x24\x1E\xFF\x24\x23\x1E\xFF\x24\x22\x1C\xFF\x22\x21\x1C\xFF\x26\x23\x1E\xFF" + "\x25\x22\x1D\xFF\x24\x22\x1D\xFF\x26\x24\x1E\xFF\x24\x23\x1D\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x24\x22\x1D\xFF\x23\x22\x1D\xFF" + "\x24\x21\x1C\xFF\x23\x22\x1C\xFF\x25\x24\x1E\xFF\x25\x22\x1D\xFF\x25\x22\x1D\xFF\x24\x21\x1C\xFF\x26\x24\x1F\xFF\x27\x27\x1E\xFF" + "\x25\x24\x1E\xFF\x26\x23\x1E\xFF\x24\x23\x1D\xFF\x22\x23\x1E\xFF\x22\x21\x1B\xFF\x21\x20\x1C\xFF\x23\x22\x1D\xFF\x24\x22\x1D\xFF" + "\x20\x20\x1A\xFF\x21\x20\x1B\xFF\x24\x22\x1D\xFF\x26\x23\x1E\xFF\x24\x24\x1E\xFF\x25\x25\x1E\xFF\x23\x21\x1C\xFF\x21\x20\x1A\xFF" + "\x21\x20\x1B\xFF\x23\x21\x1C\xFF\x24\x22\x1D\xFF\x24\x21\x1D\xFF\x22\x22\x1C\xFF\x22\x21\x1C\xFF\x22\x21\x1C\xFF\x22\x21\x1C\xFF" + "\x24\x21\x1C\xFF\x21\x20\x1B\xFF\x22\x20\x1B\xFF\x26\x24\x1F\xFF\x22\x20\x1B\xFF\x22\x22\x1C\xFF\x24\x23\x1E\xFF\x21\x20\x1B\xFF" + "\x25\x22\x1D\xFF\x25\x22\x1D\xFF\x23\x22\x1D\xFF\x22\x23\x1E\xFF\x22\x22\x1C\xFF\x24\x23\x1D\xFF\x27\x24\x1F\xFF\x25\x22\x1D\xFF" + "\x25\x21\x1C\xFF\x25\x22\x1D\xFF\x25\x22\x1D\xFF\x24\x21\x1D\xFF\x22\x1E\x1A\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x25\x22\x1D\xFF" + "\x22\x22\x1C\xFF\x22\x22\x1D\xFF\x21\x21\x1B\xFF\x23\x23\x1C\xFF\x22\x20\x1B\xFF\x23\x22\x1C\xFF\x25\x23\x1D\xFF\x24\x22\x1D\xFF" + "\x26\x23\x1E\xFF\x24\x22\x1D\xFF\x23\x20\x1B\xFF\x21\x1F\x1A\xFF\x23\x23\x1D\xFF\x22\x22\x1C\xFF\x26\x23\x1E\xFF\x26\x25\x1F\xFF" + "\x27\x24\x1F\xFF\x21\x21\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x23\x21\x1C\xFF\x21\x20\x1A\xFF\x22\x20\x1B\xFF\x20\x20\x1B\xFF" + "\x22\x22\x1C\xFF\x23\x20\x1B\xFF\x25\x22\x1D\xFF\x23\x22\x1C\xFF\x22\x22\x1C\xFF\x21\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x20\x1B\xFF" + "\x22\x20\x1B\xFF\x24\x22\x1D\xFF\x23\x22\x1D\xFF\x20\x20\x1A\xFF\x25\x21\x1C\xFF\x24\x23\x1D\xFF\x20\x21\x1B\xFF\x22\x22\x1C\xFF" + "\x22\x21\x1C\xFF\x1F\x1F\x19\xFF\x20\x1F\x1A\xFF\x1F\x20\x1A\xFF\x23\x20\x1C\xFF\x22\x21\x1C\xFF\x23\x22\x1C\xFF\x23\x21\x1C\xFF" + "\x1F\x1F\x1B\xFF\x21\x22\x1D\xFF\x20\x1F\x1B\xFF\x21\x21\x1B\xFF\x21\x1E\x1A\xFF\x22\x20\x1C\xFF\x23\x21\x1C\xFF\x21\x20\x1B\xFF" + "\x23\x22\x1D\xFF\x25\x21\x1D\xFF\x21\x20\x1B\xFF\x20\x1F\x1B\xFF\x22\x20\x1B\xFF\x25\x23\x1D\xFF\x24\x23\x1E\xFF\x24\x23\x1C\xFF" + "\x14\x13\x10\xFF\x12\x11\x0E\xFF\x12\x12\x0E\xFF\x12\x11\x0E\xFF\x10\x10\x0D\xFF\x10\x10\x0C\xFF\x10\x0F\x0B\xFF\x14\x13\x10\xFF" + "\x11\x0F\x0E\xFF\x12\x11\x0E\xFF\x14\x12\x10\xFF\x13\x11\x10\xFF\x14\x13\x0F\xFF\x11\x10\x0E\xFF\x12\x11\x0F\xFF\x10\x0F\x0E\xFF" + "\x15\x14\x0F\xFF\x12\x11\x0D\xFF\x13\x11\x0E\xFF\x16\x15\x11\xFF\x15\x15\x11\xFF\x16\x15\x11\xFF\x13\x12\x10\xFF\x13\x11\x0F\xFF" + "\x0F\x0F\x0D\xFF\x0F\x0E\x0C\xFF\x10\x0E\x0C\xFF\x14\x11\x10\xFF\x14\x13\x0E\xFF\x13\x12\x0E\xFF\x14\x12\x11\xFF\x12\x10\x0E\xFF" + "\x0F\x0E\x0D\xFF\x13\x11\x10\xFF\x13\x12\x0F\xFF\x14\x12\x0F\xFF\x12\x11\x0D\xFF\x14\x12\x0F\xFF\x15\x14\x10\xFF\x13\x11\x10\xFF" + "\x14\x12\x0E\xFF\x13\x12\x0F\xFF\x14\x13\x0F\xFF\x14\x13\x0E\xFF\x13\x11\x0F\xFF\x14\x13\x10\xFF\x16\x14\x10\xFF\x15\x14\x10\xFF" + "\x13\x11\x0F\xFF\x12\x11\x0D\xFF\x13\x12\x0F\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x14\x13\x10\xFF\x15\x13\x10\xFF\x13\x11\x0F\xFF" + "\x12\x12\x0E\xFF\x10\x0F\x0D\xFF\x14\x13\x0F\xFF\x15\x13\x11\xFF\x13\x12\x0F\xFF\x15\x13\x10\xFF\x16\x15\x11\xFF\x15\x13\x10\xFF" + "\x15\x14\x10\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF\x14\x13\x0F\xFF\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF" + "\x16\x15\x11\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF\x14\x13\x0F\xFF\x14\x13\x0F\xFF\x16\x15\x10\xFF\x15\x14\x11\xFF\x15\x14\x10\xFF" + "\x11\x10\x0F\xFF\x14\x12\x0F\xFF\x13\x11\x0F\xFF\x13\x11\x0F\xFF\x15\x13\x10\xFF\x15\x14\x10\xFF\x15\x14\x11\xFF\x16\x15\x11\xFF" + "\x15\x14\x11\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x16\x14\x10\xFF\x16\x15\x13\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF" + "\x12\x10\x0E\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x15\x14\x11\xFF\x15\x14\x10\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x15\x13\x10\xFF" + "\x17\x16\x13\xFF\x13\x11\x10\xFF\x16\x14\x11\xFF\x15\x14\x10\xFF\x13\x11\x10\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF" + "\x16\x15\x11\xFF\x16\x15\x10\xFF\x16\x15\x10\xFF\x15\x13\x11\xFF\x16\x15\x11\xFF\x17\x15\x12\xFF\x15\x14\x11\xFF\x12\x12\x0E\xFF" + "\x15\x14\x10\xFF\x16\x15\x11\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF\x18\x17\x13\xFF\x15\x14\x11\xFF" + "\x17\x15\x11\xFF\x17\x16\x12\xFF\x18\x16\x12\xFF\x18\x16\x12\xFF\x14\x13\x10\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x1A\x19\x15\xFF" + "\x19\x18\x14\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x16\x15\x11\xFF\x16\x15\x12\xFF\x17\x16\x13\xFF\x18\x17\x13\xFF\x17\x15\x11\xFF" + "\x18\x16\x12\xFF\x16\x15\x12\xFF\x16\x14\x12\xFF\x18\x16\x13\xFF\x15\x14\x10\xFF\x17\x15\x11\xFF\x17\x15\x11\xFF\x14\x13\x0F\xFF" + "\x16\x15\x11\xFF\x16\x15\x11\xFF\x16\x15\x12\xFF\x1B\x1A\x16\xFF\x16\x15\x11\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x1C\x1C\x17\xFF" + "\x17\x16\x12\xFF\x18\x17\x12\xFF\x18\x17\x13\xFF\x17\x15\x13\xFF\x16\x15\x12\xFF\x1A\x19\x14\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF" + "\x19\x18\x15\xFF\x19\x17\x13\xFF\x19\x17\x13\xFF\x15\x14\x11\xFF\x15\x14\x10\xFF\x17\x16\x12\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF" + "\x18\x17\x13\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x18\x17\x14\xFF\x1B\x1A\x16\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x1B\x1A\x15\xFF" + "\x17\x16\x12\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x1E\x1E\x19\xFF\x1D\x1D\x18\xFF\x1A\x19\x16\xFF\x19\x18\x14\xFF"; + + +/** + * Experimental Case 02: 64x64 (32bpp) + */ + +const BYTE TEST_RLE_BITMAP_EXPERIMENTAL_02[16384] = + "\x1C\x1C\x17\xFF\x1D\x1B\x18\xFF\x1B\x19\x15\xFF\x19\x18\x13\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x17\x17\x13\xFF\x19\x17\x14\xFF" + "\x15\x14\x11\xFF\x13\x13\x10\xFF\x4F\x4B\x3E\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x22\x21\x1B\xFF\x21\x21\x1A\xFF" + "\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x21\x20\x1A\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x22\x20\x1A\xFF\x21\x20\x1A\xFF" + "\x21\x20\x1B\xFF\x24\x23\x1E\xFF\x21\x20\x1B\xFF\x22\x22\x1C\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF" + "\x26\x25\x21\xFF\x21\x20\x1B\xFF\x29\x28\x22\xFF\x21\x20\x1A\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF\x26\x25\x20\xFF" + "\x23\x23\x1D\xFF\x2A\x29\x23\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x23\x22\x1D\xFF\x24\x23\x1E\xFF\x21\x20\x1B\xFF" + "\x26\x25\x20\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x25\x24\x20\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1D\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x28\x26\x21\xFF\x24\x22\x1D\xFF" + "\x23\x22\x1E\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x21\x20\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x2A\x29\x24\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x22\x1D\xFF\x26\x25\x20\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x26\x25\x20\xFF\x21\x20\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x28\x26\x21\xFF\x25\x24\x20\xFF" + "\x24\x23\x1D\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x23\x1E\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x21\x20\x1B\xFF\x29\x29\x23\xFF\x21\x20\x1A\xFF\x25\x24\x20\xFF\x26\x24\x20\xFF\x21\x21\x1B\xFF" + "\x21\x20\x1B\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x25\x20\xFF" + "\x29\x28\x22\xFF\x23\x23\x1E\xFF\x23\x22\x1C\xFF\x22\x22\x1C\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF" + "\x25\x25\x20\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF\x23\x22\x1E\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x28\x26\x21\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF" + "\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF\x24\x23\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x25\x24\x1F\xFF\x25\x24\x20\xFF\x28\x26\x21\xFF\x25\x24\x20\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x25\x24\x20\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF" + "\x28\x26\x21\xFF\x23\x22\x1C\xFF\x25\x23\x1F\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x29\x28\x23\xFF" + "\x27\x25\x21\xFF\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x26\x25\x20\xFF" + "\x24\x23\x1E\xFF\x2A\x29\x23\xFF\x25\x23\x1E\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x23\x23\x1E\xFF\x24\x24\x1E\xFF\x22\x21\x1B\xFF" + "\x26\x25\x20\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x26\x25\x21\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1E\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x28\x26\x21\xFF\x24\x23\x1E\xFF" + "\x23\x23\x1E\xFF\x27\x25\x20\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x25\x24\x1F\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x2A\x29\x24\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1E\xFF\x27\x25\x20\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x27\x25\x20\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x28\x26\x21\xFF\x25\x24\x20\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x23\x1E\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x2A\x29\x23\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x26\x25\x21\xFF\x22\x23\x1C\xFF" + "\x23\x23\x1C\xFF\x24\x25\x1E\xFF\x27\x2B\x25\xFF\x2B\x2F\x27\xFF\x25\x2C\x23\xFF\x26\x2E\x24\xFF\x26\x2F\x25\xFF\x2A\x35\x2B\xFF" + "\x2E\x39\x2E\xFF\x28\x34\x2A\xFF\x28\x33\x28\xFF\x28\x33\x28\xFF\x2A\x35\x2C\xFF\x27\x32\x27\xFF\x2A\x35\x2C\xFF\x27\x32\x27\xFF" + "\x2A\x36\x2C\xFF\x2B\x36\x2D\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF" + "\x27\x32\x27\xFF\x27\x32\x27\xFF\x2A\x35\x2C\xFF\x2E\x39\x2E\xFF\x28\x33\x28\xFF\x28\x33\x2A\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF" + "\x27\x32\x27\xFF\x28\x33\x28\xFF\x2D\x37\x2D\xFF\x27\x32\x27\xFF\x2A\x35\x2B\xFF\x54\x51\x43\xFF\x37\x82\x60\xFF\x36\x7A\x5A\xFF" + "\x34\x71\x54\xFF\x32\x68\x4D\xFF\x30\x5D\x46\xFF\x2D\x53\x3F\xFF\x2B\x45\x35\xFF\x26\x35\x29\xFF\x25\x2D\x23\xFF\x26\x2C\x25\xFF" + "\x27\x2A\x23\xFF\x23\x25\x1F\xFF\x23\x23\x1E\xFF\x24\x23\x1D\xFF\x25\x23\x1D\xFF\x25\x23\x1E\xFF\x25\x23\x1D\xFF\x25\x24\x1F\xFF" + "\x28\x2B\x23\xFF\x37\x60\x4A\xFF\x4A\xA2\x78\xFF\x47\x97\x71\xFF\x41\x84\x64\xFF\x3D\x75\x58\xFF\x38\x62\x4B\xFF\x33\x50\x3E\xFF" + "\x2E\x3C\x30\xFF\x2A\x34\x29\xFF\x28\x30\x26\xFF\x27\x2C\x24\xFF\x26\x29\x22\xFF\x27\x28\x21\xFF\x27\x26\x20\xFF\x27\x25\x20\xFF" + "\x25\x25\x20\xFF\x24\x23\x1D\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x26\x24\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF" + "\x26\x25\x1F\xFF\x28\x25\x20\xFF\x28\x26\x21\xFF\x2A\x27\x22\xFF\x26\x25\x1E\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x28\x25\x1F\xFF" + "\x27\x27\x1F\xFF\x27\x24\x1F\xFF\x25\x24\x1F\xFF\x26\x24\x1F\xFF\x24\x24\x1E\xFF\x25\x24\x1D\xFF\x27\x25\x1F\xFF\x28\x25\x20\xFF" + "\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x27\x26\x1F\xFF\x27\x25\x1E\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x20\xFF" + "\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1E\xFF\x25\x22\x1D\xFF\x26\x23\x1E\xFF\x23\x21\x1C\xFF" + "\x26\x24\x1F\xFF\x28\x25\x20\xFF\x28\x26\x1F\xFF\x28\x25\x1F\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF" + "\x26\x24\x1F\xFF\x25\x25\x1E\xFF\x27\x25\x1F\xFF\x28\x24\x1F\xFF\x29\x27\x1F\xFF\x25\x24\x1E\xFF\x24\x24\x1E\xFF\x25\x24\x1E\xFF" + "\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x25\x24\x1E\xFF\x27\x24\x1F\xFF\x2A\x27\x22\xFF\x2B\x28\x22\xFF\x2B\x28\x21\xFF\x25\x24\x1E\xFF" + "\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x27\x23\x1E\xFF\x28\x25\x20\xFF\x25\x22\x1E\xFF\x25\x23\x1E\xFF\x23\x22\x1C\xFF\x25\x25\x1E\xFF" + "\x26\x26\x20\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x27\x25\x1E\xFF\x25\x23\x1C\xFF" + "\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x24\x23\x1E\xFF\x24\x21\x1C\xFF\x25\x22\x1D\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF" + "\x25\x22\x1E\xFF\x25\x22\x1E\xFF\x25\x22\x1D\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x26\x20\xFF\x26\x26\x1F\xFF\x27\x25\x1F\xFF" + "\x27\x26\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1E\xFF\x23\x22\x1C\xFF\x25\x23\x1D\xFF\x25\x22\x1E\xFF\x26\x24\x1E\xFF\x24\x23\x1E\xFF" + "\x26\x24\x1F\xFF\x27\x26\x20\xFF\x29\x27\x21\xFF\x2A\x26\x21\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x28\x29\x23\xFF" + "\x2F\x46\x36\xFF\x4B\xA5\x7B\xFF\x57\xCD\x97\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x4D\xAC\x7F\xFF\x32\x50\x3D\xFF\x26\x29\x22\xFF" + "\x23\x21\x1C\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x25\x24\x1F\xFF" + "\x22\x21\x1C\xFF\x24\x23\x1D\xFF\x24\x24\x1D\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x25\x27\x21\xFF\x2B\x36\x2B\xFF" + "\x4B\xA7\x7D\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x50\xB9\x89\xFF\x32\x54\x40\xFF\x27\x2A\x23\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x25\x22\x1D\xFF\x25\x23\x1D\xFF\x25\x22\x1D\xFF" + "\x24\x23\x1D\xFF\x25\x23\x1E\xFF\x24\x22\x1D\xFF\x25\x22\x1D\xFF\x23\x22\x1C\xFF\x24\x23\x1D\xFF\x27\x25\x1F\xFF\x28\x25\x1F\xFF" + "\x25\x23\x1E\xFF\x23\x22\x1D\xFF\x24\x22\x1C\xFF\x24\x23\x1D\xFF\x24\x22\x1D\xFF\x24\x21\x1C\xFF\x25\x24\x1E\xFF\x23\x22\x1C\xFF" + "\x24\x21\x1C\xFF\x25\x23\x1E\xFF\x27\x25\x1F\xFF\x25\x25\x1F\xFF\x24\x22\x1D\xFF\x24\x21\x1C\xFF\x25\x23\x1E\xFF\x22\x21\x1C\xFF" + "\x26\x23\x1F\xFF\x24\x21\x1D\xFF\x24\x22\x1D\xFF\x23\x22\x1C\xFF\x23\x21\x1C\xFF\x24\x21\x1C\xFF\x26\x23\x1E\xFF\x23\x22\x1D\xFF" + "\x22\x21\x1C\xFF\x26\x25\x1F\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x23\x23\x1D\xFF\x25\x24\x1E\xFF\x26\x25\x1E\xFF\x26\x23\x1E\xFF" + "\x24\x22\x1D\xFF\x24\x22\x1E\xFF\x24\x22\x1D\xFF\x25\x22\x1D\xFF\x23\x22\x1D\xFF\x24\x22\x1D\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF" + "\x24\x21\x1D\xFF\x23\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x23\x1E\xFF\x24\x22\x1D\xFF\x21\x1F\x1B\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF" + "\x25\x24\x1E\xFF\x24\x24\x1D\xFF\x24\x23\x1D\xFF\x25\x23\x1E\xFF\x23\x22\x1D\xFF\x23\x22\x1D\xFF\x23\x21\x1C\xFF\x22\x21\x1C\xFF" + "\x20\x20\x1B\xFF\x25\x25\x1E\xFF\x24\x22\x1D\xFF\x24\x22\x1D\xFF\x26\x23\x1E\xFF\x23\x22\x1D\xFF\x24\x22\x1D\xFF\x25\x22\x1D\xFF" + "\x24\x22\x1D\xFF\x24\x22\x1D\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x24\x23\x1D\xFF\x25\x24\x1E\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF" + "\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x22\x22\x1C\xFF\x22\x20\x1B\xFF\x20\x20\x1A\xFF\x22\x20\x1B\xFF\x21\x20\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1C\xFF\x20\x1F\x1A\xFF\x20\x1F\x1A\xFF\x24\x21\x1C\xFF\x25\x24\x1E\xFF\x25\x23\x1E\xFF\x24\x22\x1D\xFF" + "\x26\x23\x1E\xFF\x24\x22\x1D\xFF\x23\x20\x1B\xFF\x24\x22\x1C\xFF\x25\x23\x1D\xFF\x23\x22\x1D\xFF\x20\x20\x1A\xFF\x20\x1F\x19\xFF" + "\x1F\x1F\x1A\xFF\x23\x23\x1D\xFF\x23\x21\x1C\xFF\x22\x1F\x1B\xFF\x21\x21\x1A\xFF\x22\x22\x1C\xFF\x23\x22\x1D\xFF\x21\x21\x1B\xFF" + "\x21\x21\x1B\xFF\x23\x21\x1C\xFF\x23\x20\x1B\xFF\x20\x20\x1A\xFF\x21\x1F\x1B\xFF\x24\x21\x1C\xFF\x25\x22\x1D\xFF\x22\x22\x1C\xFF" + "\x23\x21\x1C\xFF\x24\x22\x1D\xFF\x23\x21\x1C\xFF\x21\x1E\x19\xFF\x21\x1F\x1B\xFF\x22\x20\x1B\xFF\x23\x21\x1C\xFF\x22\x20\x1B\xFF" + "\x22\x21\x1C\xFF\x20\x20\x1C\xFF\x1F\x1E\x1A\xFF\x22\x21\x1C\xFF\x22\x21\x1C\xFF\x21\x21\x1B\xFF\x21\x21\x1B\xFF\x24\x22\x1F\xFF" + "\x12\x11\x0F\xFF\x11\x10\x0C\xFF\x11\x10\x0D\xFF\x13\x12\x0F\xFF\x12\x11\x0D\xFF\x12\x11\x0E\xFF\x12\x11\x0F\xFF\x16\x15\x11\xFF" + "\x17\x15\x12\xFF\x12\x10\x0E\xFF\x14\x13\x0F\xFF\x14\x13\x10\xFF\x11\x11\x0D\xFF\x10\x10\x0D\xFF\x13\x13\x0F\xFF\x13\x11\x0F\xFF" + "\x13\x12\x0F\xFF\x15\x13\x11\xFF\x13\x12\x0F\xFF\x10\x0E\x0E\xFF\x11\x0F\x0E\xFF\x13\x11\x10\xFF\x13\x11\x10\xFF\x13\x12\x0F\xFF" + "\x15\x13\x12\xFF\x12\x11\x0E\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x14\x12\x0F\xFF\x15\x13\x11\xFF\x17\x16\x12\xFF\x14\x13\x0F\xFF" + "\x14\x13\x11\xFF\x13\x13\x10\xFF\x14\x13\x10\xFF\x16\x16\x12\xFF\x0F\x0F\x0D\xFF\x11\x10\x0D\xFF\x11\x10\x0D\xFF\x12\x10\x0D\xFF" + "\x12\x10\x0E\xFF\x14\x12\x0F\xFF\x15\x13\x11\xFF\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x17\x15\x12\xFF\x15\x14\x12\xFF\x16\x15\x11\xFF" + "\x15\x14\x10\xFF\x18\x17\x13\xFF\x16\x16\x11\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF\x15\x14\x11\xFF\x14\x12\x11\xFF\x13\x12\x10\xFF" + "\x15\x13\x12\xFF\x18\x17\x13\xFF\x16\x15\x11\xFF\x11\x10\x0D\xFF\x14\x13\x10\xFF\x17\x16\x12\xFF\x14\x13\x0F\xFF\x14\x12\x10\xFF" + "\x16\x15\x11\xFF\x19\x17\x13\xFF\x16\x15\x11\xFF\x14\x12\x10\xFF\x14\x12\x10\xFF\x14\x13\x10\xFF\x15\x13\x10\xFF\x13\x11\x10\xFF" + "\x13\x12\x0F\xFF\x13\x11\x0F\xFF\x12\x10\x0F\xFF\x16\x14\x11\xFF\x12\x11\x0F\xFF\x14\x13\x10\xFF\x17\x17\x12\xFF\x18\x18\x12\xFF" + "\x18\x17\x13\xFF\x15\x14\x11\xFF\x16\x15\x11\xFF\x16\x14\x12\xFF\x15\x13\x10\xFF\x15\x13\x11\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF" + "\x16\x14\x11\xFF\x14\x13\x10\xFF\x14\x13\x0F\xFF\x13\x12\x0E\xFF\x15\x13\x11\xFF\x14\x13\x10\xFF\x15\x14\x10\xFF\x14\x13\x0F\xFF" + "\x15\x14\x10\xFF\x15\x14\x11\xFF\x16\x15\x11\xFF\x16\x14\x12\xFF\x16\x16\x10\xFF\x16\x15\x11\xFF\x15\x14\x11\xFF\x14\x13\x10\xFF" + "\x15\x14\x10\xFF\x16\x14\x11\xFF\x14\x13\x10\xFF\x14\x13\x10\xFF\x17\x16\x12\xFF\x17\x15\x14\xFF\x13\x12\x0F\xFF\x17\x15\x12\xFF" + "\x1A\x18\x14\xFF\x18\x16\x13\xFF\x18\x16\x13\xFF\x18\x16\x12\xFF\x15\x14\x10\xFF\x12\x11\x0E\xFF\x15\x13\x12\xFF\x17\x16\x13\xFF" + "\x15\x14\x12\xFF\x18\x17\x13\xFF\x18\x16\x14\xFF\x12\x11\x10\xFF\x15\x14\x10\xFF\x18\x16\x13\xFF\x17\x15\x12\xFF\x19\x18\x15\xFF" + "\x17\x16\x12\xFF\x17\x16\x12\xFF\x19\x17\x14\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x17\x15\x11\xFF" + "\x16\x15\x11\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF\x18\x16\x12\xFF\x16\x15\x11\xFF\x16\x15\x12\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF" + "\x18\x16\x13\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x14\x13\x0F\xFF\x17\x16\x13\xFF\x18\x17\x13\xFF\x18\x16\x14\xFF\x18\x17\x14\xFF" + "\x16\x15\x11\xFF\x1C\x1A\x16\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x1A\x18\x14\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF" + "\x16\x15\x11\xFF\x15\x14\x10\xFF\x17\x16\x12\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF\x16\x15\x11\xFF\x16\x14\x11\xFF\x16\x15\x11\xFF" + "\x1A\x19\x15\xFF\x15\x14\x11\xFF\x19\x17\x13\xFF\x1E\x1E\x19\xFF\x19\x18\x14\xFF\x1B\x19\x15\xFF\x1C\x1A\x16\xFF\x17\x16\x13\xFF" + "\x16\x14\x12\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x19\x18\x14\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x14\x13\x0F\xFF" + "\x16\x15\x11\xFF\x1A\x18\x14\xFF\x18\x16\x12\xFF\x13\x12\x0E\xFF\x16\x16\x12\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x1A\x19\x15\xFF" + "\x1C\x1A\x16\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x18\x15\x11\xFF\x19\x18\x14\xFF\x1A\x19\x15\xFF\x1B\x1A\x15\xFF\x22\x22\x1D\xFF" + "\x1F\x1D\x18\xFF\x1B\x18\x14\xFF\x1C\x1B\x16\xFF\x19\x18\x14\xFF\x1B\x1A\x16\xFF\x1B\x1A\x16\xFF\x1B\x1A\x15\xFF\x1A\x19\x15\xFF" + "\x1A\x18\x14\xFF\x1C\x1B\x16\xFF\x1C\x1A\x16\xFF\x1C\x19\x15\xFF\x1B\x19\x15\xFF\x19\x18\x14\xFF\x1E\x1C\x18\xFF\x1D\x1A\x16\xFF" + "\x17\x16\x11\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF\x1C\x1B\x17\xFF\x1D\x1C\x17\xFF\x1A\x18\x14\xFF\x19\x17\x14\xFF\x19\x19\x15\xFF" + "\x1B\x1A\x16\xFF\x1C\x1B\x17\xFF\x1E\x1D\x18\xFF\x1C\x1A\x16\xFF\x1A\x18\x14\xFF\x1B\x1A\x16\xFF\x1D\x1C\x17\xFF\x1F\x1E\x19\xFF" + "\x1B\x1A\x16\xFF\x1B\x1A\x16\xFF\x1D\x1C\x17\xFF\x1D\x1B\x17\xFF\x1B\x1A\x16\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x1D\x1C\x18\xFF" + "\x1B\x19\x15\xFF\x1C\x1B\x17\xFF\x1C\x1B\x18\xFF\x1A\x1A\x16\xFF\x1E\x1C\x18\xFF\x1E\x1C\x18\xFF\x1F\x1C\x18\xFF\x1F\x1E\x19\xFF" + "\x1C\x1B\x17\xFF\x1B\x1B\x17\xFF\x1A\x19\x15\xFF\x1E\x1C\x18\xFF\x1F\x1F\x19\xFF\x1C\x1C\x17\xFF\x1C\x1A\x18\xFF\x1F\x1C\x19\xFF" + "\x1F\x1D\x18\xFF\x1D\x1C\x18\xFF\x1D\x1B\x17\xFF\x1E\x1D\x19\xFF\x20\x1D\x19\xFF\x1E\x1C\x18\xFF\x1F\x1E\x19\xFF\x1D\x1C\x18\xFF" + "\x1D\x1C\x18\xFF\x22\x21\x1C\xFF\x20\x1F\x1A\xFF\x1F\x1D\x18\xFF\x1C\x1B\x17\xFF\x20\x1F\x19\xFF\x1B\x1B\x16\xFF\x1A\x19\x15\xFF" + "\x1C\x1A\x16\xFF\x1E\x1B\x17\xFF\x1F\x1D\x18\xFF\x1F\x1E\x19\xFF\x21\x1E\x1A\xFF\x1D\x1B\x17\xFF\x1D\x1D\x18\xFF\x1E\x1E\x18\xFF" + "\x1E\x1E\x18\xFF\x1F\x1D\x19\xFF\x1E\x1C\x17\xFF\x1E\x1D\x18\xFF\x1E\x1C\x18\xFF\x1F\x1E\x19\xFF\x1D\x1C\x18\xFF\x1F\x1D\x1A\xFF" + "\x1F\x1E\x18\xFF\x1E\x1D\x18\xFF\x1F\x1E\x19\xFF\x22\x22\x1C\xFF\x1D\x1D\x18\xFF\x1D\x1C\x18\xFF\x20\x1F\x1C\xFF\x1E\x1E\x17\xFF" + "\x1D\x1C\x18\xFF\x1D\x1D\x18\xFF\x20\x21\x1B\xFF\x21\x20\x1B\xFF\x1D\x1C\x18\xFF\x1B\x19\x15\xFF\x1D\x1B\x17\xFF\x20\x1D\x1B\xFF" + "\x1F\x1E\x19\xFF\x1E\x1D\x18\xFF\x20\x1F\x1A\xFF\x1F\x1F\x19\xFF\x22\x20\x1B\xFF\x1F\x1E\x19\xFF\x1E\x1D\x19\xFF\x22\x1F\x1B\xFF" + "\x1E\x1E\x17\xFF\x1C\x1C\x16\xFF\x1B\x1C\x15\xFF\x1D\x1B\x18\xFF\x1E\x1C\x19\xFF\x20\x1E\x19\xFF\x21\x1E\x1A\xFF\x1F\x1D\x18\xFF" + "\x1A\x1A\x15\xFF\x19\x17\x14\xFF\x1B\x1A\x16\xFF\x1B\x19\x15\xFF\x1B\x19\x15\xFF\x18\x18\x14\xFF\x18\x17\x14\xFF\x18\x18\x14\xFF" + "\x19\x17\x14\xFF\x15\x16\x12\xFF\x52\x4E\x42\xFF\x22\x21\x1B\xFF\x22\x20\x1A\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x21\x20\x1B\xFF\x25\x24\x20\xFF\x26\x25\x21\xFF\x23\x22\x1D\xFF\x24\x23\x1E\xFF" + "\x21\x20\x1A\xFF\x26\x25\x20\xFF\x23\x22\x1D\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x20\x1B\xFF\x21\x20\x1A\xFF\x21\x21\x1B\xFF" + "\x26\x25\x20\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x25\x20\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x21\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x28\x26\x22\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x21\x20\x1B\xFF\x26\x25\x21\xFF\x21\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x23\x22\x1D\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x24\x22\x1D\xFF\x24\x23\x1F\xFF\x25\x24\x20\xFF" + "\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x26\x25\x20\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x24\x23\x1E\xFF\x29\x28\x23\xFF\x25\x24\x20\xFF" + "\x24\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x21\x1C\xFF\x23\x21\x1C\xFF" + "\x24\x23\x1E\xFF\x22\x20\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x22\x1C\xFF\x26\x25\x20\xFF\x22\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x21\x21\x1B\xFF\x26\x25\x20\xFF" + "\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF\x26\x25\x21\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x21\x1C\xFF\x23\x22\x1C\xFF\x26\x25\x21\xFF\x25\x24\x20\xFF\x25\x24\x1F\xFF\x24\x22\x1D\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x25\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x23\x21\x1C\xFF\x23\x23\x1E\xFF\x23\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF" + "\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x25\x23\x1F\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x23\x1E\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x26\x25\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x25\x24\x1F\xFF\x26\x24\x20\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x21\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x26\x25\x21\xFF\x24\x23\x1E\xFF\x25\x24\x1F\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x26\x25\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x25\x20\xFF" + "\x23\x22\x1C\xFF\x27\x25\x20\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x28\x26\x22\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x25\x24\x1F\xFF\x26\x24\x20\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x27\x25\x20\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x27\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x26\x20\xFF" + "\x28\x26\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x29\x28\x23\xFF\x25\x25\x20\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x22\x1C\xFF\x27\x26\x20\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x22\x22\x1C\xFF\x26\x25\x21\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x27\x26\x22\xFF\x26\x26\x22\xFF\x26\x27\x21\xFF\x26\x2A\x23\xFF" + "\x26\x2E\x26\xFF\x26\x2F\x25\xFF\x27\x31\x27\xFF\x27\x31\x27\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x29\x34\x2A\xFF" + "\x27\x32\x27\xFF\x2A\x35\x2B\xFF\x2A\x36\x2C\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF\x2A\x35\x2C\xFF\x27\x32\x27\xFF\x2D\x37\x2D\xFF" + "\x28\x33\x28\xFF\x28\x34\x2A\xFF\x28\x34\x2A\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x2B\x36\x2C\xFF\x27\x32\x27\xFF" + "\x28\x33\x28\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x2D\x37\x2D\xFF\x2A\x35\x2C\xFF\x27\x32\x27\xFF" + "\x27\x32\x27\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF\x29\x34\x2A\xFF\x4F\x4D\x3F\xFF\x3A\x8B\x67\xFF\x3B\x8C\x68\xFF" + "\x3E\x93\x6D\xFF\x40\x98\x71\xFF\x42\x9A\x72\xFF\x42\x9A\x72\xFF\x42\x97\x70\xFF\x41\x93\x6D\xFF\x3E\x87\x64\xFF\x3B\x75\x56\xFF" + "\x32\x51\x3F\xFF\x29\x37\x2C\xFF\x29\x31\x28\xFF\x27\x2C\x24\xFF\x26\x29\x21\xFF\x28\x28\x22\xFF\x27\x27\x20\xFF\x27\x27\x21\xFF" + "\x29\x2F\x25\xFF\x3C\x73\x56\xFF\x55\xC9\x94\xFF\x56\xCC\x96\xFF\x56\xCB\x97\xFF\x54\xC5\x92\xFF\x52\xBD\x8C\xFF\x50\xB5\x86\xFF" + "\x4D\xAA\x7F\xFF\x48\x99\x71\xFF\x40\x80\x61\xFF\x37\x66\x4D\xFF\x2F\x48\x38\xFF\x2C\x38\x2C\xFF\x2B\x33\x29\xFF\x2A\x2E\x26\xFF" + "\x27\x29\x23\xFF\x27\x27\x21\xFF\x27\x27\x21\xFF\x27\x26\x20\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x27\x26\x20\xFF\x28\x26\x20\xFF\x28\x25\x20\xFF\x28\x27\x20\xFF\x27\x25\x1F\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x28\x26\x20\xFF" + "\x25\x23\x1E\xFF\x26\x24\x1E\xFF\x25\x23\x1D\xFF\x27\x25\x1F\xFF\x26\x24\x1E\xFF\x26\x23\x1E\xFF\x29\x26\x21\xFF\x28\x25\x20\xFF" + "\x26\x25\x1F\xFF\x27\x25\x1F\xFF\x25\x25\x1F\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x25\x1F\xFF\x25\x23\x1D\xFF\x27\x24\x1F\xFF" + "\x25\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x27\x24\x1E\xFF\x26\x26\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x29\x26\x20\xFF\x28\x25\x20\xFF\x27\x26\x1E\xFF\x26\x24\x1E\xFF\x28\x26\x21\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x28\x25\x20\xFF\x27\x24\x1F\xFF\x27\x26\x20\xFF\x27\x25\x1F\xFF\x28\x26\x20\xFF\x26\x23\x1E\xFF\x25\x24\x1D\xFF\x26\x24\x1E\xFF" + "\x26\x24\x1F\xFF\x22\x21\x1B\xFF\x23\x23\x1D\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF\x27\x24\x1E\xFF\x27\x24\x1F\xFF" + "\x27\x25\x1E\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x26\x23\x1F\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x24\x23\x1D\xFF\x21\x21\x1C\xFF" + "\x26\x23\x1F\xFF\x26\x23\x1F\xFF\x26\x23\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1E\xFF\x26\x24\x1E\xFF\x24\x24\x1D\xFF" + "\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x27\x26\x1F\xFF\x25\x22\x1D\xFF\x25\x22\x1D\xFF\x26\x24\x1E\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF" + "\x26\x24\x1F\xFF\x24\x21\x1C\xFF\x23\x22\x1C\xFF\x23\x23\x1D\xFF\x27\x25\x20\xFF\x26\x25\x1F\xFF\x27\x26\x20\xFF\x27\x27\x1F\xFF" + "\x29\x26\x21\xFF\x26\x25\x1F\xFF\x23\x23\x1D\xFF\x24\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x21\x1D\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF" + "\x25\x24\x1F\xFF\x27\x25\x21\xFF\x25\x24\x1F\xFF\x28\x25\x20\xFF\x27\x26\x1F\xFF\x26\x23\x1E\xFF\x25\x22\x1D\xFF\x26\x26\x20\xFF" + "\x2A\x37\x2D\xFF\x42\x87\x66\xFF\x56\xCD\x98\xFF\x56\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x52\xBF\x8E\xFF\x37\x66\x4D\xFF\x28\x2C\x25\xFF" + "\x26\x26\x21\xFF\x27\x24\x1F\xFF\x24\x23\x1E\xFF\x24\x22\x1D\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x25\x22\x1D\xFF" + "\x22\x22\x1C\xFF\x23\x21\x1C\xFF\x26\x23\x1E\xFF\x24\x23\x1D\xFF\x25\x22\x1C\xFF\x23\x21\x1C\xFF\x28\x27\x20\xFF\x2B\x31\x27\xFF" + "\x3E\x79\x5B\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCC\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x54\xC4\x91\xFF\x3E\x79\x5B\xFF\x29\x30\x28\xFF\x25\x25\x1F\xFF\x25\x24\x1E\xFF\x27\x24\x1E\xFF\x24\x23\x1D\xFF\x25\x23\x1F\xFF" + "\x25\x22\x1D\xFF\x26\x24\x1E\xFF\x24\x22\x1D\xFF\x22\x22\x1C\xFF\x22\x1F\x1A\xFF\x23\x21\x1C\xFF\x23\x21\x1C\xFF\x27\x24\x1F\xFF" + "\x27\x26\x21\xFF\x27\x24\x1F\xFF\x24\x21\x1C\xFF\x22\x21\x1C\xFF\x24\x23\x1D\xFF\x23\x20\x1B\xFF\x24\x23\x1D\xFF\x24\x23\x1E\xFF" + "\x25\x24\x1E\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x28\x26\x21\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF" + "\x26\x23\x1E\xFF\x24\x21\x1C\xFF\x24\x23\x1D\xFF\x22\x22\x1C\xFF\x22\x20\x1A\xFF\x22\x21\x1B\xFF\x24\x24\x1D\xFF\x25\x23\x1E\xFF" + "\x24\x23\x1E\xFF\x25\x23\x1E\xFF\x23\x22\x1D\xFF\x21\x20\x1B\xFF\x25\x23\x1E\xFF\x24\x23\x1D\xFF\x23\x22\x1C\xFF\x25\x22\x1D\xFF" + "\x23\x21\x1C\xFF\x22\x22\x1D\xFF\x23\x22\x1D\xFF\x27\x25\x20\xFF\x23\x22\x1D\xFF\x26\x22\x1F\xFF\x23\x20\x1D\xFF\x24\x21\x1D\xFF" + "\x25\x22\x1D\xFF\x24\x23\x1E\xFF\x20\x1F\x1A\xFF\x20\x1E\x19\xFF\x23\x22\x1D\xFF\x23\x22\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x24\x22\x1D\xFF\x24\x23\x1D\xFF\x24\x23\x1D\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x25\x22\x1E\xFF\x21\x20\x1B\xFF\x21\x21\x1B\xFF" + "\x20\x20\x1A\xFF\x24\x23\x1D\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x22\x1F\x1A\xFF\x24\x21\x1C\xFF\x23\x20\x1C\xFF\x20\x1F\x1A\xFF" + "\x24\x22\x1D\xFF\x23\x21\x1C\xFF\x24\x22\x1C\xFF\x24\x21\x1C\xFF\x22\x22\x1C\xFF\x22\x22\x1C\xFF\x24\x21\x1C\xFF\x23\x21\x1C\xFF" + "\x23\x22\x1C\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x25\x22\x1D\xFF\x25\x22\x1D\xFF\x24\x21\x1C\xFF\x22\x20\x1B\xFF\x21\x21\x1B\xFF" + "\x23\x21\x1C\xFF\x23\x21\x1C\xFF\x20\x1E\x19\xFF\x21\x1E\x1A\xFF\x23\x22\x1D\xFF\x26\x24\x1E\xFF\x23\x23\x1C\xFF\x23\x21\x1C\xFF" + "\x22\x20\x1C\xFF\x22\x21\x1C\xFF\x23\x20\x1C\xFF\x24\x22\x1D\xFF\x24\x22\x1C\xFF\x22\x20\x1C\xFF\x22\x20\x1B\xFF\x22\x22\x1C\xFF" + "\x22\x20\x1B\xFF\x22\x20\x1B\xFF\x25\x23\x1D\xFF\x26\x23\x1E\xFF\x21\x21\x1C\xFF\x20\x1F\x1A\xFF\x20\x1F\x1B\xFF\x1F\x1E\x1A\xFF" + "\x22\x1F\x1A\xFF\x27\x25\x1F\xFF\x23\x22\x1D\xFF\x22\x23\x1B\xFF\x25\x22\x1D\xFF\x27\x24\x1F\xFF\x24\x22\x1D\xFF\x23\x23\x1D\xFF" + "\x25\x23\x1D\xFF\x23\x22\x1C\xFF\x24\x22\x1D\xFF\x26\x23\x1C\xFF\x24\x21\x1D\xFF\x22\x20\x1B\xFF\x20\x1E\x1B\xFF\x1D\x1C\x18\xFF" + "\x20\x20\x1A\xFF\x20\x1F\x1A\xFF\x1F\x1D\x19\xFF\x20\x1F\x1A\xFF\x23\x22\x1D\xFF\x23\x22\x1D\xFF\x23\x22\x1D\xFF\x23\x22\x1C\xFF" + "\x13\x12\x0F\xFF\x13\x12\x0E\xFF\x13\x12\x0F\xFF\x12\x10\x0E\xFF\x12\x11\x0F\xFF\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x13\x12\x0E\xFF" + "\x14\x12\x0F\xFF\x12\x11\x0E\xFF\x12\x10\x0E\xFF\x12\x11\x0E\xFF\x13\x11\x0E\xFF\x17\x13\x10\xFF\x13\x12\x0E\xFF\x0E\x0D\x0A\xFF" + "\x0E\x0E\x0C\xFF\x0F\x0F\x0D\xFF\x11\x10\x0D\xFF\x13\x11\x0F\xFF\x0F\x10\x0E\xFF\x11\x10\x0F\xFF\x13\x11\x10\xFF\x13\x12\x10\xFF" + "\x11\x10\x0D\xFF\x12\x11\x0E\xFF\x14\x12\x10\xFF\x14\x12\x10\xFF\x10\x0F\x0D\xFF\x13\x11\x0F\xFF\x14\x13\x10\xFF\x13\x12\x10\xFF" + "\x15\x13\x0F\xFF\x14\x12\x0F\xFF\x12\x10\x0E\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x14\x12\x10\xFF\x13\x11\x0E\xFF\x12\x11\x0F\xFF" + "\x11\x10\x0C\xFF\x14\x13\x0F\xFF\x14\x13\x0F\xFF\x14\x12\x10\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF\x15\x13\x10\xFF\x14\x13\x0F\xFF" + "\x12\x11\x0E\xFF\x13\x12\x0E\xFF\x15\x13\x10\xFF\x12\x11\x0D\xFF\x17\x16\x12\xFF\x17\x16\x13\xFF\x12\x10\x0F\xFF\x14\x13\x0F\xFF" + "\x13\x13\x0F\xFF\x14\x14\x11\xFF\x14\x14\x11\xFF\x13\x12\x0E\xFF\x15\x13\x12\xFF\x14\x13\x10\xFF\x13\x12\x0F\xFF\x15\x13\x11\xFF" + "\x18\x17\x13\xFF\x14\x13\x10\xFF\x17\x16\x13\xFF\x17\x16\x12\xFF\x16\x15\x12\xFF\x14\x12\x10\xFF\x15\x15\x11\xFF\x12\x11\x0D\xFF" + "\x13\x10\x0F\xFF\x13\x12\x0F\xFF\x12\x11\x0D\xFF\x12\x13\x10\xFF\x11\x0F\x0D\xFF\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x12\x11\x0E\xFF" + "\x12\x11\x0D\xFF\x13\x12\x0F\xFF\x14\x12\x0F\xFF\x11\x10\x0D\xFF\x14\x13\x10\xFF\x13\x11\x10\xFF\x11\x10\x0D\xFF\x15\x14\x10\xFF" + "\x18\x16\x14\xFF\x17\x17\x12\xFF\x14\x13\x0F\xFF\x15\x14\x11\xFF\x19\x18\x14\xFF\x16\x15\x11\xFF\x17\x16\x11\xFF\x15\x14\x10\xFF" + "\x19\x17\x15\xFF\x1B\x1B\x16\xFF\x19\x18\x13\xFF\x16\x15\x13\xFF\x14\x13\x0F\xFF\x15\x14\x10\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF" + "\x16\x14\x12\xFF\x15\x14\x10\xFF\x15\x13\x11\xFF\x14\x13\x0F\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF" + "\x17\x16\x12\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF\x14\x13\x0F\xFF\x16\x15\x11\xFF" + "\x19\x17\x14\xFF\x16\x15\x12\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF\x17\x16\x12\xFF\x17\x15\x13\xFF\x18\x17\x12\xFF\x1B\x1A\x16\xFF" + "\x18\x15\x11\xFF\x14\x13\x0F\xFF\x17\x15\x11\xFF\x15\x13\x10\xFF\x15\x14\x10\xFF\x17\x15\x11\xFF\x14\x13\x0F\xFF\x17\x16\x12\xFF" + "\x18\x17\x13\xFF\x16\x14\x12\xFF\x15\x14\x11\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x16\x15\x12\xFF\x18\x17\x14\xFF\x16\x15\x11\xFF" + "\x17\x16\x12\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x15\x13\x11\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x1B\x1A\x16\xFF\x19\x18\x14\xFF" + "\x19\x18\x14\xFF\x1A\x19\x15\xFF\x17\x16\x12\xFF\x13\x12\x0E\xFF\x1C\x1A\x16\xFF\x1B\x19\x15\xFF\x1A\x19\x15\xFF\x16\x14\x10\xFF" + "\x14\x13\x10\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x19\x18\x14\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF" + "\x16\x15\x11\xFF\x16\x15\x11\xFF\x1B\x19\x15\xFF\x1B\x19\x15\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF" + "\x1A\x19\x15\xFF\x19\x18\x14\xFF\x1B\x18\x16\xFF\x1C\x19\x16\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x19\x18\x13\xFF\x19\x17\x13\xFF" + "\x1B\x18\x14\xFF\x19\x16\x14\xFF\x16\x15\x11\xFF\x17\x16\x13\xFF\x19\x17\x13\xFF\x19\x18\x13\xFF\x18\x17\x13\xFF\x17\x15\x11\xFF" + "\x16\x15\x12\xFF\x16\x16\x12\xFF\x18\x18\x13\xFF\x1B\x1A\x16\xFF\x1D\x1C\x17\xFF\x1A\x17\x13\xFF\x1B\x19\x15\xFF\x1A\x18\x14\xFF" + "\x1A\x19\x15\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x19\x17\x13\xFF" + "\x19\x18\x14\xFF\x1D\x1B\x17\xFF\x1C\x1B\x17\xFF\x1C\x1A\x16\xFF\x17\x16\x14\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x1D\x1B\x17\xFF" + "\x1C\x1B\x17\xFF\x1C\x1A\x16\xFF\x1C\x1A\x16\xFF\x1C\x1A\x16\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x1A\x19\x15\xFF" + "\x1D\x1C\x17\xFF\x1B\x1A\x16\xFF\x1D\x1C\x18\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x1A\x18\x14\xFF\x1C\x1B\x17\xFF\x1A\x19\x15\xFF" + "\x1A\x1A\x16\xFF\x1C\x1B\x16\xFF\x1D\x1C\x18\xFF\x1B\x1A\x17\xFF\x1F\x1F\x19\xFF\x1C\x1C\x17\xFF\x1D\x1B\x17\xFF\x1A\x19\x15\xFF" + "\x18\x19\x14\xFF\x1B\x19\x16\xFF\x1A\x19\x15\xFF\x1A\x1A\x16\xFF\x1C\x1B\x17\xFF\x1C\x1C\x17\xFF\x1D\x1C\x16\xFF\x1E\x1D\x18\xFF" + "\x20\x1E\x19\xFF\x1E\x1B\x17\xFF\x1B\x19\x14\xFF\x1D\x1B\x17\xFF\x1F\x1E\x1A\xFF\x1B\x1A\x16\xFF\x1D\x1C\x17\xFF\x1D\x1D\x17\xFF" + "\x1B\x1A\x16\xFF\x1C\x1B\x16\xFF\x1B\x1A\x15\xFF\x1F\x1F\x1A\xFF\x20\x1D\x19\xFF\x1E\x1C\x18\xFF\x1E\x1C\x17\xFF\x1B\x1A\x16\xFF" + "\x1C\x1B\x17\xFF\x1C\x1C\x17\xFF\x1E\x1F\x19\xFF\x1F\x1D\x18\xFF\x1D\x1B\x19\xFF\x1D\x1C\x17\xFF\x1D\x1C\x17\xFF\x1D\x1B\x17\xFF" + "\x1B\x1A\x17\xFF\x1D\x1D\x18\xFF\x1D\x1C\x17\xFF\x1E\x1D\x18\xFF\x1C\x1C\x17\xFF\x1E\x1C\x18\xFF\x20\x1E\x1A\xFF\x1F\x1E\x19\xFF" + "\x1F\x1E\x19\xFF\x1D\x1C\x18\xFF\x1E\x1C\x18\xFF\x1F\x1D\x19\xFF\x1A\x19\x15\xFF\x1C\x1B\x16\xFF\x1E\x1D\x19\xFF\x22\x21\x1B\xFF" + "\x20\x1F\x19\xFF\x1E\x1E\x18\xFF\x1E\x1E\x18\xFF\x1E\x1E\x18\xFF\x20\x20\x1A\xFF\x1C\x1A\x16\xFF\x1D\x1B\x17\xFF\x1F\x1E\x19\xFF" + "\x20\x1F\x1A\xFF\x1F\x1C\x18\xFF\x20\x1E\x19\xFF\x21\x21\x1B\xFF\x1F\x1E\x19\xFF\x1D\x1D\x18\xFF\x1C\x1B\x16\xFF\x1E\x1D\x18\xFF" + "\x1D\x1C\x18\xFF\x20\x20\x1A\xFF\x1E\x1D\x18\xFF\x1E\x1E\x19\xFF\x22\x20\x1C\xFF\x22\x1F\x1A\xFF\x1E\x1C\x17\xFF\x20\x1D\x18\xFF" + "\x20\x1E\x1A\xFF\x1F\x1D\x19\xFF\x1F\x1E\x19\xFF\x20\x1F\x1B\xFF\x1F\x1E\x18\xFF\x1F\x1C\x17\xFF\x1A\x19\x15\xFF\x1C\x1B\x16\xFF" + "\x1D\x1B\x18\xFF\x1C\x1B\x18\xFF\x1C\x1A\x16\xFF\x1A\x18\x15\xFF\x1C\x19\x15\xFF\x1B\x1B\x17\xFF\x1A\x19\x15\xFF\x16\x16\x13\xFF" + "\x18\x15\x12\xFF\x14\x13\x10\xFF\x4F\x4B\x3F\xFF\x23\x22\x1D\xFF\x26\x25\x21\xFF\x21\x21\x1B\xFF\x26\x25\x20\xFF\x22\x20\x1A\xFF" + "\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x28\x26\x21\xFF\x21\x20\x1A\xFF\x23\x22\x1D\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x25\x24\x20\xFF" + "\x29\x28\x22\xFF\x25\x24\x20\xFF\x22\x20\x1B\xFF\x23\x22\x1C\xFF\x21\x20\x1B\xFF\x21\x20\x1B\xFF\x21\x20\x1B\xFF\x21\x20\x1B\xFF" + "\x22\x21\x1B\xFF\x29\x28\x22\xFF\x21\x20\x1B\xFF\x21\x21\x1B\xFF\x21\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x2A\x29\x23\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x28\x26\x22\xFF\x21\x20\x1A\xFF\x21\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x25\x24\x20\xFF\x26\x25\x20\xFF\x25\x25\x20\xFF\x21\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x28\x26\x21\xFF" + "\x28\x26\x21\xFF\x25\x25\x20\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x25\x23\x1E\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x26\x25\x20\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x22\x21\x1B\xFF" + "\x28\x26\x21\xFF\x21\x21\x1B\xFF\x25\x24\x20\xFF\x24\x23\x1E\xFF\x27\x25\x21\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x26\x25\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x25\x24\x20\xFF" + "\x23\x22\x1C\xFF\x25\x24\x20\xFF\x24\x23\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x20\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x26\x25\x21\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x25\x24\x20\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x24\x23\x1E\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x24\x24\x1E\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF" + "\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x25\x23\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x26\x25\x20\xFF\x25\x24\x1F\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x25\x24\x1E\xFF\x29\x28\x22\xFF\x24\x23\x1E\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF" + "\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x28\x26\x21\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1F\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x24\x22\x1E\xFF\x26\x25\x20\xFF\x25\x24\x1F\xFF\x26\x25\x21\xFF" + "\x29\x28\x22\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x2A\x29\x23\xFF" + "\x24\x22\x1D\xFF\x23\x22\x1C\xFF\x28\x26\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x25\x24\x20\xFF\x26\x25\x20\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x29\x27\x22\xFF" + "\x29\x26\x21\xFF\x26\x26\x21\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF" + "\x27\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x28\x26\x21\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x24\x23\x1E\xFF\x26\x25\x21\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF" + "\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x26\x25\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x21\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x29\x28\x23\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x26\x25\x20\xFF" + "\x23\x22\x1C\xFF\x26\x25\x21\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x27\x26\x22\xFF\x22\x22\x1C\xFF\x29\x29\x23\xFF\x23\x26\x1F\xFF\x29\x2F\x27\xFF\x26\x2F\x25\xFF\x27\x31\x27\xFF\x27\x32\x27\xFF" + "\x2A\x35\x2C\xFF\x27\x32\x27\xFF\x2A\x35\x2C\xFF\x29\x34\x2A\xFF\x2D\x37\x2D\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF" + "\x27\x32\x27\xFF\x27\x32\x27\xFF\x2A\x35\x2C\xFF\x27\x32\x27\xFF\x2A\x35\x2C\xFF\x29\x35\x2B\xFF\x2A\x35\x2C\xFF\x29\x34\x2B\xFF" + "\x29\x34\x2A\xFF\x29\x34\x2A\xFF\x29\x35\x2B\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF\x29\x35\x2B\xFF" + "\x2B\x36\x2C\xFF\x2A\x35\x2B\xFF\x2A\x35\x2C\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x29\x34\x2A\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF" + "\x2A\x35\x2B\xFF\x2E\x39\x2E\xFF\x2A\x35\x2B\xFF\x29\x34\x2A\xFF\x28\x33\x28\xFF\x4F\x4D\x3F\xFF\x3B\x8B\x67\xFF\x3B\x8C\x68\xFF" + "\x3E\x93\x6D\xFF\x41\x99\x71\xFF\x43\x9F\x76\xFF\x45\xA4\x7A\xFF\x48\xAA\x7E\xFF\x49\xAE\x81\xFF\x4B\xB2\x84\xFF\x4C\xB6\x87\xFF" + "\x4E\xBA\x89\xFF\x4D\xB3\x83\xFF\x45\x97\x70\xFF\x3D\x79\x5B\xFF\x35\x58\x44\xFF\x30\x48\x39\xFF\x2D\x3A\x2F\xFF\x29\x31\x27\xFF" + "\x2B\x37\x2C\xFF\x3E\x79\x5B\xFF\x56\xCA\x95\xFF\x56\xCC\x96\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x52\xBC\x8B\xFF\x49\x9D\x76\xFF\x3E\x7A\x5C\xFF" + "\x36\x5C\x47\xFF\x31\x4C\x3B\xFF\x2D\x3D\x2F\xFF\x29\x30\x28\xFF\x27\x2B\x23\xFF\x27\x29\x22\xFF\x27\x28\x21\xFF\x28\x27\x21\xFF" + "\x27\x25\x20\xFF\x27\x24\x1F\xFF\x24\x24\x1E\xFF\x25\x23\x1F\xFF\x28\x25\x20\xFF\x29\x26\x20\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF" + "\x26\x23\x1F\xFF\x27\x24\x1F\xFF\x29\x27\x21\xFF\x2A\x27\x21\xFF\x27\x24\x1F\xFF\x25\x24\x1E\xFF\x24\x24\x1F\xFF\x26\x24\x1F\xFF" + "\x28\x25\x1F\xFF\x27\x25\x1E\xFF\x27\x25\x20\xFF\x25\x25\x1F\xFF\x29\x26\x21\xFF\x27\x27\x20\xFF\x26\x25\x1F\xFF\x25\x23\x1E\xFF" + "\x26\x23\x1E\xFF\x23\x23\x1D\xFF\x26\x25\x1E\xFF\x27\x24\x1E\xFF\x25\x22\x1E\xFF\x27\x24\x1F\xFF\x29\x26\x21\xFF\x26\x26\x1E\xFF" + "\x28\x27\x21\xFF\x28\x26\x21\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x25\x23\x1E\xFF\x25\x23\x1F\xFF\x27\x24\x1F\xFF" + "\x27\x24\x1D\xFF\x29\x26\x20\xFF\x27\x26\x20\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x25\x24\x1D\xFF\x26\x26\x20\xFF" + "\x25\x22\x1D\xFF\x26\x25\x1E\xFF\x27\x24\x1F\xFF\x25\x22\x1D\xFF\x27\x25\x1F\xFF\x27\x24\x1D\xFF\x24\x22\x1D\xFF\x24\x22\x1D\xFF" + "\x26\x24\x1E\xFF\x25\x24\x1E\xFF\x27\x25\x1F\xFF\x28\x24\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x29\x28\x20\xFF" + "\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x24\x23\x1D\xFF\x25\x24\x1E\xFF\x25\x22\x1D\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x24\x1E\xFF" + "\x22\x23\x1D\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x25\x24\x1F\xFF\x24\x23\x1E\xFF\x26\x23\x1E\xFF\x27\x25\x20\xFF\x25\x24\x1E\xFF" + "\x27\x25\x20\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x24\x21\x1C\xFF\x25\x24\x1E\xFF\x24\x24\x1E\xFF\x23\x23\x1D\xFF\x25\x23\x1E\xFF" + "\x26\x25\x1F\xFF\x23\x21\x1C\xFF\x24\x23\x1D\xFF\x26\x24\x1F\xFF\x23\x22\x1D\xFF\x23\x21\x1C\xFF\x25\x23\x1E\xFF\x28\x25\x20\xFF" + "\x28\x26\x1F\xFF\x28\x25\x1E\xFF\x25\x24\x1D\xFF\x26\x25\x20\xFF\x24\x22\x1E\xFF\x25\x23\x1E\xFF\x24\x21\x1D\xFF\x26\x23\x1E\xFF" + "\x27\x2D\x26\xFF\x39\x6A\x50\xFF\x53\xC1\x90\xFF\x58\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x41\x85\x65\xFF\x2A\x35\x2C\xFF" + "\x27\x27\x21\xFF\x26\x25\x1F\xFF\x24\x23\x1E\xFF\x25\x22\x1D\xFF\x26\x22\x1F\xFF\x24\x23\x1E\xFF\x24\x24\x1E\xFF\x22\x21\x1C\xFF" + "\x20\x20\x1A\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x29\x26\x21\xFF\x27\x24\x1F\xFF\x26\x25\x1E\xFF\x27\x27\x1F\xFF\x27\x2A\x24\xFF" + "\x31\x52\x3E\xFF\x51\xBA\x8A\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x48\x9C\x75\xFF\x2D\x3E\x31\xFF\x27\x28\x22\xFF\x25\x22\x1E\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x27\x24\x1F\xFF" + "\x28\x26\x20\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x24\x23\x1E\xFF\x26\x23\x1E\xFF\x25\x24\x1E\xFF\x25\x22\x1E\xFF\x25\x22\x1E\xFF" + "\x27\x24\x1F\xFF\x25\x22\x1D\xFF\x24\x22\x1E\xFF\x22\x22\x1D\xFF\x24\x23\x1D\xFF\x21\x21\x1B\xFF\x1F\x1E\x19\xFF\x23\x20\x1B\xFF" + "\x20\x1F\x1A\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x24\x21\x1D\xFF\x23\x23\x1D\xFF\x24\x23\x1D\xFF\x23\x21\x1C\xFF\x24\x21\x1C\xFF" + "\x27\x24\x1F\xFF\x24\x22\x1D\xFF\x22\x21\x1B\xFF\x26\x23\x1E\xFF\x26\x25\x1E\xFF\x23\x23\x1D\xFF\x25\x22\x1D\xFF\x28\x24\x20\xFF" + "\x24\x21\x1F\xFF\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x23\x23\x1D\xFF\x23\x21\x1C\xFF\x24\x21\x1C\xFF\x24\x21\x1C\xFF\x21\x20\x1A\xFF" + "\x22\x21\x1B\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF\x23\x21\x1F\xFF\x23\x21\x1C\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x23\x21\x1C\xFF" + "\x24\x24\x1E\xFF\x23\x23\x1D\xFF\x21\x21\x1B\xFF\x24\x23\x1D\xFF\x22\x22\x1C\xFF\x25\x23\x1E\xFF\x24\x21\x1C\xFF\x21\x1D\x1A\xFF" + "\x23\x22\x1D\xFF\x25\x22\x1D\xFF\x21\x20\x1B\xFF\x21\x1F\x1B\xFF\x21\x1F\x1B\xFF\x25\x23\x1E\xFF\x24\x21\x1D\xFF\x24\x23\x1D\xFF" + "\x26\x24\x1E\xFF\x22\x21\x1C\xFF\x23\x21\x1C\xFF\x22\x22\x1C\xFF\x22\x20\x1B\xFF\x24\x24\x1E\xFF\x25\x23\x1E\xFF\x23\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x21\x21\x1C\xFF\x22\x21\x1C\xFF\x23\x22\x1C\xFF\x21\x1F\x1B\xFF\x23\x20\x1C\xFF\x24\x23\x1D\xFF\x21\x21\x1B\xFF" + "\x22\x22\x1D\xFF\x25\x24\x1E\xFF\x26\x23\x1E\xFF\x25\x22\x1E\xFF\x22\x22\x1C\xFF\x23\x23\x1D\xFF\x23\x23\x1D\xFF\x24\x21\x1C\xFF" + "\x1F\x1E\x1A\xFF\x21\x20\x1A\xFF\x1F\x1D\x18\xFF\x1E\x1D\x19\xFF\x22\x22\x1C\xFF\x21\x21\x1B\xFF\x21\x20\x1B\xFF\x20\x1F\x1A\xFF" + "\x24\x23\x1D\xFF\x21\x20\x1A\xFF\x21\x1F\x1A\xFF\x22\x21\x1C\xFF\x25\x23\x1E\xFF\x23\x22\x1D\xFF\x23\x21\x1D\xFF\x24\x21\x1C\xFF" + "\x23\x21\x1C\xFF\x25\x23\x1E\xFF\x23\x21\x1C\xFF\x22\x22\x1C\xFF\x21\x20\x1B\xFF\x21\x21\x1B\xFF\x23\x21\x1B\xFF\x24\x21\x1C\xFF" + "\x23\x23\x1D\xFF\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x20\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1D\xFF\x20\x1F\x1B\xFF" + "\x25\x23\x1E\xFF\x23\x23\x1E\xFF\x23\x21\x1C\xFF\x28\x25\x20\xFF\x21\x1F\x1B\xFF\x20\x1E\x1A\xFF\x23\x21\x1C\xFF\x23\x21\x1C\xFF" + "\x22\x20\x1B\xFF\x21\x20\x1B\xFF\x23\x22\x1D\xFF\x23\x22\x1C\xFF\x23\x21\x1C\xFF\x24\x23\x1D\xFF\x24\x22\x1C\xFF\x23\x21\x1D\xFF" + "\x12\x11\x0E\xFF\x13\x11\x0F\xFF\x13\x11\x0F\xFF\x12\x11\x0D\xFF\x15\x13\x12\xFF\x15\x14\x11\xFF\x15\x15\x10\xFF\x11\x0F\x0E\xFF" + "\x11\x0E\x0D\xFF\x11\x10\x0E\xFF\x12\x10\x0E\xFF\x11\x0E\x0D\xFF\x10\x0E\x0D\xFF\x12\x10\x0E\xFF\x11\x0F\x0D\xFF\x11\x10\x0E\xFF" + "\x13\x12\x0F\xFF\x13\x12\x0E\xFF\x13\x12\x0F\xFF\x13\x12\x0F\xFF\x10\x10\x0C\xFF\x12\x11\x0E\xFF\x13\x12\x0F\xFF\x12\x12\x0F\xFF" + "\x11\x0F\x0E\xFF\x14\x13\x0F\xFF\x13\x12\x0F\xFF\x14\x13\x0F\xFF\x13\x12\x0F\xFF\x13\x11\x10\xFF\x12\x11\x0F\xFF\x11\x11\x0E\xFF" + "\x11\x11\x0E\xFF\x16\x14\x11\xFF\x13\x11\x0F\xFF\x13\x12\x10\xFF\x16\x14\x10\xFF\x15\x13\x10\xFF\x15\x14\x11\xFF\x12\x12\x0E\xFF" + "\x14\x13\x0F\xFF\x16\x14\x11\xFF\x14\x12\x0F\xFF\x15\x15\x11\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x13\x12\x0F\xFF\x12\x11\x0E\xFF" + "\x13\x11\x0F\xFF\x14\x12\x10\xFF\x14\x12\x11\xFF\x13\x13\x10\xFF\x16\x15\x11\xFF\x15\x15\x10\xFF\x13\x13\x0E\xFF\x17\x15\x11\xFF" + "\x10\x10\x0D\xFF\x14\x13\x0F\xFF\x15\x13\x10\xFF\x13\x12\x0F\xFF\x15\x14\x11\xFF\x15\x14\x10\xFF\x11\x10\x0D\xFF\x14\x13\x0F\xFF" + "\x16\x14\x10\xFF\x12\x11\x0E\xFF\x11\x10\x0D\xFF\x14\x12\x10\xFF\x14\x13\x10\xFF\x14\x13\x10\xFF\x13\x12\x0F\xFF\x13\x11\x0E\xFF" + "\x17\x15\x11\xFF\x18\x16\x13\xFF\x18\x17\x13\xFF\x12\x11\x0E\xFF\x15\x14\x10\xFF\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x14\x12\x0F\xFF" + "\x18\x17\x13\xFF\x19\x18\x14\xFF\x16\x15\x11\xFF\x12\x11\x0D\xFF\x14\x13\x10\xFF\x15\x14\x11\xFF\x14\x13\x10\xFF\x13\x13\x0E\xFF" + "\x15\x14\x11\xFF\x18\x17\x13\xFF\x14\x12\x0F\xFF\x13\x12\x0F\xFF\x13\x12\x0E\xFF\x14\x13\x10\xFF\x16\x15\x11\xFF\x18\x16\x13\xFF" + "\x16\x14\x12\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x15\x14\x10\xFF\x17\x16\x12\xFF\x15\x14\x11\xFF\x17\x15\x13\xFF\x15\x14\x11\xFF" + "\x16\x14\x10\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x14\x13\x10\xFF\x16\x16\x12\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x14\x13\x0F\xFF" + "\x17\x16\x12\xFF\x17\x16\x12\xFF\x15\x14\x10\xFF\x14\x13\x0F\xFF\x16\x15\x11\xFF\x13\x12\x0F\xFF\x13\x12\x0E\xFF\x13\x11\x0F\xFF" + "\x15\x14\x11\xFF\x15\x14\x11\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x1A\x19\x15\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF" + "\x13\x13\x0F\xFF\x17\x15\x11\xFF\x18\x16\x14\xFF\x15\x14\x11\xFF\x15\x14\x10\xFF\x19\x17\x13\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF" + "\x1A\x19\x15\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF\x16\x15\x11\xFF" + "\x19\x18\x14\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x16\x14\x11\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF" + "\x1B\x1A\x16\xFF\x16\x16\x12\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF" + "\x15\x14\x10\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x1A\x18\x14\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF\x19\x17\x13\xFF" + "\x19\x18\x14\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF" + "\x1A\x18\x14\xFF\x1A\x17\x14\xFF\x1A\x18\x14\xFF\x13\x13\x11\xFF\x19\x19\x15\xFF\x19\x18\x14\xFF\x19\x19\x14\xFF\x1B\x1A\x16\xFF" + "\x1A\x18\x14\xFF\x18\x17\x13\xFF\x15\x14\x11\xFF\x19\x18\x14\xFF\x1A\x18\x14\xFF\x1B\x1C\x16\xFF\x1A\x19\x15\xFF\x18\x16\x12\xFF" + "\x18\x17\x13\xFF\x1A\x19\x14\xFF\x19\x18\x13\xFF\x1A\x19\x15\xFF\x19\x18\x15\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF\x1A\x1A\x15\xFF" + "\x1A\x18\x14\xFF\x19\x19\x15\xFF\x1A\x1A\x16\xFF\x1A\x19\x15\xFF\x17\x17\x13\xFF\x19\x19\x15\xFF\x1B\x19\x15\xFF\x19\x17\x13\xFF" + "\x1A\x1A\x15\xFF\x1A\x19\x14\xFF\x1D\x1C\x18\xFF\x1E\x1C\x18\xFF\x19\x17\x14\xFF\x19\x17\x13\xFF\x1A\x19\x15\xFF\x1C\x1A\x16\xFF" + "\x1B\x19\x15\xFF\x18\x16\x13\xFF\x18\x16\x13\xFF\x19\x18\x14\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x1A\x19\x14\xFF\x1C\x1B\x16\xFF" + "\x1E\x1B\x17\xFF\x1A\x1A\x15\xFF\x1C\x1A\x16\xFF\x1B\x1A\x15\xFF\x1C\x1B\x17\xFF\x1B\x1B\x16\xFF\x1E\x1C\x18\xFF\x1D\x1B\x17\xFF" + "\x1D\x1B\x16\xFF\x1C\x1B\x16\xFF\x1B\x1A\x17\xFF\x1A\x18\x16\xFF\x1B\x19\x15\xFF\x1B\x1B\x16\xFF\x1A\x19\x15\xFF\x1B\x1A\x16\xFF" + "\x20\x1F\x19\xFF\x1B\x1A\x17\xFF\x1B\x1A\x16\xFF\x19\x19\x15\xFF\x1D\x1C\x18\xFF\x20\x20\x19\xFF\x1F\x1F\x18\xFF\x1D\x1C\x18\xFF" + "\x1E\x1E\x18\xFF\x1C\x1A\x16\xFF\x1A\x19\x14\xFF\x19\x18\x14\xFF\x1C\x1A\x17\xFF\x1B\x1A\x16\xFF\x1E\x1E\x19\xFF\x1F\x1E\x18\xFF" + "\x1C\x1B\x17\xFF\x1B\x1A\x16\xFF\x1C\x1C\x17\xFF\x1F\x1E\x19\xFF\x1F\x1F\x19\xFF\x1F\x1C\x18\xFF\x1E\x1C\x17\xFF\x1C\x1C\x17\xFF" + "\x1D\x1B\x16\xFF\x1F\x1D\x19\xFF\x1F\x1E\x19\xFF\x1C\x1B\x16\xFF\x1E\x1C\x17\xFF\x1B\x1A\x16\xFF\x1D\x1B\x18\xFF\x1E\x1C\x18\xFF" + "\x1C\x1C\x17\xFF\x1F\x1F\x19\xFF\x1D\x1C\x17\xFF\x1E\x1B\x17\xFF\x1F\x1D\x18\xFF\x1D\x1C\x18\xFF\x1D\x1D\x18\xFF\x21\x20\x1B\xFF" + "\x21\x1F\x1C\xFF\x1E\x1D\x19\xFF\x1E\x1C\x18\xFF\x1F\x1D\x19\xFF\x1D\x1C\x18\xFF\x1E\x1C\x17\xFF\x1F\x1D\x18\xFF\x20\x1F\x19\xFF" + "\x20\x1D\x19\xFF\x1C\x1B\x16\xFF\x20\x1F\x1A\xFF\x22\x1F\x1B\xFF\x21\x1F\x1A\xFF\x1E\x1C\x18\xFF\x1D\x1B\x17\xFF\x1E\x1C\x18\xFF" + "\x1D\x1C\x18\xFF\x1E\x1C\x18\xFF\x1F\x1D\x19\xFF\x20\x1F\x1B\xFF\x1F\x1E\x1A\xFF\x1F\x1E\x1A\xFF\x21\x1F\x1A\xFF\x1E\x1D\x19\xFF" + "\x20\x1F\x1B\xFF\x1E\x1E\x18\xFF\x1D\x1D\x18\xFF\x20\x1E\x1A\xFF\x20\x1E\x1A\xFF\x20\x1E\x19\xFF\x1D\x1C\x17\xFF\x1C\x1C\x16\xFF" + "\x1C\x1B\x17\xFF\x1F\x1C\x18\xFF\x20\x1E\x19\xFF\x20\x1E\x1A\xFF\x1D\x1B\x16\xFF\x1C\x1C\x16\xFF\x1C\x1D\x18\xFF\x1D\x1D\x18\xFF" + "\x1B\x19\x16\xFF\x1B\x1B\x15\xFF\x1A\x19\x15\xFF\x1B\x19\x16\xFF\x1C\x1A\x16\xFF\x1D\x1B\x18\xFF\x17\x16\x13\xFF\x16\x15\x11\xFF" + "\x15\x13\x10\xFF\x14\x13\x10\xFF\x4F\x4B\x3E\xFF\x28\x26\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x26\x25\x20\xFF" + "\x29\x28\x23\xFF\x22\x22\x1D\xFF\x26\x25\x20\xFF\x22\x21\x1C\xFF\x24\x23\x1E\xFF\x25\x24\x20\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x22\x20\x1B\xFF\x25\x25\x20\xFF\x26\x25\x20\xFF\x24\x22\x1D\xFF\x28\x26\x21\xFF\x23\x22\x1D\xFF\x24\x23\x1E\xFF\x28\x26\x21\xFF" + "\x21\x20\x1A\xFF\x28\x26\x21\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1D\xFF" + "\x23\x22\x1C\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF\x25\x24\x20\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x21\x20\x1B\xFF\x22\x20\x1B\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x23\x21\x1C\xFF\x23\x22\x1D\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x23\x1E\xFF\x28\x26\x21\xFF\x23\x22\x1E\xFF\x24\x22\x1D\xFF\x22\x21\x1B\xFF\x24\x23\x1D\xFF" + "\x28\x26\x21\xFF\x24\x22\x1D\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x20\x1B\xFF\x23\x21\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x25\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1E\xFF" + "\x21\x21\x1B\xFF\x26\x25\x20\xFF\x25\x24\x20\xFF\x22\x22\x1C\xFF\x25\x23\x1E\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF" + "\x24\x22\x1D\xFF\x23\x22\x1D\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1F\xFF" + "\x26\x25\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x23\x1F\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x20\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF" + "\x22\x22\x1C\xFF\x28\x26\x22\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x23\x22\x1D\xFF\x22\x21\x1B\xFF\x26\x25\x21\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x26\x24\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x26\x25\x21\xFF\x23\x22\x1C\xFF\x24\x24\x1E\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF" + "\x24\x23\x1E\xFF\x26\x25\x20\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF" + "\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF" + "\x28\x26\x21\xFF\x23\x22\x1C\xFF\x23\x23\x1E\xFF\x25\x23\x1E\xFF\x24\x23\x1E\xFF\x29\x28\x22\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x28\x26\x22\xFF\x22\x21\x1B\xFF\x24\x22\x1D\xFF" + "\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x26\x24\x20\xFF\x29\x28\x22\xFF\x22\x21\x1C\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x28\x26\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x28\x26\x21\xFF\x26\x25\x20\xFF" + "\x29\x28\x23\xFF\x23\x23\x1E\xFF\x27\x25\x20\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x25\x24\x20\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x25\x25\x20\xFF\x27\x25\x20\xFF\x24\x22\x1D\xFF\x28\x26\x21\xFF\x24\x23\x1E\xFF\x25\x23\x1E\xFF\x28\x26\x21\xFF" + "\x22\x21\x1B\xFF\x28\x26\x21\xFF\x24\x22\x1D\xFF\x23\x22\x1C\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x25\x24\x20\xFF\x29\x28\x22\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x27\x26\x21\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x24\x1F\xFF\x28\x26\x21\xFF\x23\x22\x1E\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF" + "\x28\x26\x21\xFF\x24\x23\x1E\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF" + "\x23\x22\x1C\xFF\x25\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x26\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1E\xFF" + "\x22\x21\x1B\xFF\x26\x25\x20\xFF\x26\x24\x20\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x23\x22\x1C\xFF\x28\x26\x21\xFF\x22\x21\x1B\xFF" + "\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x23\x22\x1C\xFF" + "\x24\x22\x1D\xFF\x24\x23\x1E\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x26\x25\x20\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF" + "\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x29\x28\x22\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x22\x21\x1B\xFF\x25\x24\x20\xFF" + "\x27\x26\x21\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x25\x24\x1F\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x23\x22\x1C\xFF\x26\x25\x20\xFF" + "\x22\x21\x1B\xFF\x27\x25\x20\xFF\x22\x21\x1B\xFF\x25\x24\x1F\xFF\x22\x21\x1B\xFF\x28\x27\x21\xFF\x22\x22\x1B\xFF\x24\x24\x1F\xFF" + "\x23\x26\x1F\xFF\x2A\x2E\x28\xFF\x25\x2D\x24\xFF\x27\x31\x27\xFF\x29\x34\x2A\xFF\x27\x32\x27\xFF\x2B\x36\x2D\xFF\x27\x32\x27\xFF" + "\x29\x34\x2A\xFF\x2A\x35\x2C\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF" + "\x27\x32\x27\xFF\x2B\x36\x2D\xFF\x28\x33\x28\xFF\x2A\x35\x2B\xFF\x27\x32\x27\xFF\x29\x34\x2A\xFF\x28\x33\x28\xFF\x28\x33\x28\xFF" + "\x2A\x35\x2B\xFF\x2A\x35\x2C\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF" + "\x28\x33\x28\xFF\x2B\x36\x2C\xFF\x27\x32\x27\xFF\x2A\x35\x2B\xFF\x27\x32\x27\xFF\x27\x32\x27\xFF\x28\x33\x28\xFF\x27\x32\x27\xFF" + "\x29\x34\x2A\xFF\x28\x33\x28\xFF\x29\x34\x2A\xFF\x28\x33\x28\xFF\x2A\x35\x2B\xFF\x4F\x4D\x3F\xFF\x3B\x8B\x67\xFF\x3B\x8C\x68\xFF" + "\x3E\x93\x6D\xFF\x41\x99\x71\xFF\x43\x9F\x76\xFF\x45\xA4\x7A\xFF\x48\xAA\x7E\xFF\x4A\xAF\x81\xFF\x4C\xB3\x84\xFF\x4D\xB7\x87\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x50\xC0\x8E\xFF\x51\xC2\x8F\xFF\x52\xBC\x8B\xFF\x4A\xA7\x7C\xFF\x44\x90\x6B\xFF\x3E\x78\x5B\xFF" + "\x36\x63\x4B\xFF\x41\x87\x66\xFF\x55\xCB\x96\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x53\xC2\x90\xFF\x4D\xAD\x81\xFF\x47\x96\x70\xFF\x40\x7E\x5F\xFF\x39\x63\x4C\xFF\x31\x48\x39\xFF\x2D\x36\x2B\xFF\x27\x2C\x24\xFF" + "\x25\x27\x21\xFF\x27\x27\x21\xFF\x26\x24\x1E\xFF\x25\x24\x1E\xFF\x28\x25\x1F\xFF\x26\x24\x1E\xFF\x25\x25\x20\xFF\x26\x24\x1F\xFF" + "\x25\x22\x1E\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x29\x26\x21\xFF\x25\x23\x1D\xFF\x25\x23\x1D\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x2A\x27\x21\xFF\x29\x26\x21\xFF\x28\x25\x20\xFF\x26\x26\x20\xFF\x29\x26\x21\xFF\x29\x26\x21\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x25\x23\x1E\xFF\x24\x22\x1E\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x25\x24\x1F\xFF" + "\x28\x27\x1F\xFF\x27\x25\x1F\xFF\x25\x25\x1F\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF\x26\x23\x1F\xFF\x25\x23\x1E\xFF\x28\x25\x20\xFF" + "\x28\x25\x20\xFF\x28\x26\x20\xFF\x26\x24\x1F\xFF\x26\x25\x1E\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x20\xFF" + "\x25\x23\x1E\xFF\x27\x25\x1E\xFF\x27\x26\x1F\xFF\x26\x24\x1D\xFF\x25\x24\x1D\xFF\x28\x26\x1F\xFF\x28\x27\x21\xFF\x26\x25\x1F\xFF" + "\x25\x23\x1E\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x1E\xFF\x24\x23\x1E\xFF\x27\x25\x1F\xFF\x27\x25\x20\xFF\x27\x26\x1F\xFF" + "\x29\x26\x20\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x26\x23\x1D\xFF\x23\x22\x1C\xFF\x26\x25\x1F\xFF\x25\x25\x1E\xFF\x25\x24\x1E\xFF" + "\x25\x24\x1E\xFF\x24\x23\x1D\xFF\x24\x23\x1E\xFF\x25\x25\x1F\xFF\x25\x24\x1E\xFF\x25\x23\x1E\xFF\x23\x23\x1E\xFF\x25\x24\x1F\xFF" + "\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x23\x1F\xFF\x25\x22\x1D\xFF\x27\x24\x1E\xFF\x27\x25\x1F\xFF\x25\x24\x1E\xFF\x25\x23\x1E\xFF" + "\x26\x24\x1F\xFF\x23\x22\x1D\xFF\x26\x24\x1E\xFF\x24\x24\x1E\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x26\x24\x1E\xFF" + "\x26\x24\x1E\xFF\x27\x25\x1E\xFF\x25\x23\x1E\xFF\x28\x25\x20\xFF\x24\x21\x1E\xFF\x25\x23\x1E\xFF\x26\x23\x1F\xFF\x25\x22\x1C\xFF" + "\x26\x29\x22\xFF\x32\x53\x3F\xFF\x4E\xAF\x82\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x4A\xA4\x7A\xFF\x2C\x43\x35\xFF" + "\x28\x28\x22\xFF\x26\x25\x20\xFF\x26\x24\x1F\xFF\x25\x23\x1F\xFF\x25\x23\x1C\xFF\x25\x22\x1D\xFF\x25\x24\x1D\xFF\x23\x23\x1D\xFF" + "\x23\x23\x1D\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x27\x25\x20\xFF\x23\x22\x1C\xFF\x24\x22\x1D\xFF\x24\x23\x1D\xFF\x24\x25\x1F\xFF" + "\x2C\x40\x30\xFF\x47\x98\x71\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x56\xCC\x98\xFF\x56\xCC\x97\xFF\x57\xCC\x97\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x4E\xB2\x84\xFF\x32\x56\x42\xFF\x25\x2A\x22\xFF\x25\x25\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x22\x1D\xFF" + "\x24\x24\x1E\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x27\x24\x1E\xFF\x25\x23\x1D\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x25\x22\x1D\xFF\x24\x23\x1E\xFF\x25\x23\x1E\xFF\x23\x22\x1E\xFF\x24\x22\x1E\xFF\x23\x20\x1B\xFF\x23\x22\x1C\xFF\x26\x23\x1E\xFF" + "\x25\x23\x1E\xFF\x23\x22\x1C\xFF\x24\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x27\x26\x1F\xFF\x25\x24\x1E\xFF\x22\x20\x1B\xFF" + "\x23\x20\x1B\xFF\x23\x21\x1C\xFF\x23\x21\x1C\xFF\x24\x22\x1D\xFF\x23\x21\x1C\xFF\x23\x22\x1C\xFF\x26\x24\x1E\xFF\x25\x23\x1E\xFF" + "\x23\x21\x1C\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x22\x21\x1B\xFF\x22\x22\x1C\xFF\x24\x22\x1C\xFF\x24\x21\x1C\xFF\x23\x21\x1C\xFF" + "\x25\x23\x1E\xFF\x26\x22\x1D\xFF\x25\x22\x1D\xFF\x23\x21\x1D\xFF\x24\x22\x1D\xFF\x21\x21\x1B\xFF\x23\x22\x1D\xFF\x24\x23\x1D\xFF" + "\x23\x22\x1D\xFF\x22\x21\x1C\xFF\x23\x20\x1C\xFF\x23\x21\x1D\xFF\x21\x21\x1C\xFF\x25\x23\x1E\xFF\x28\x25\x20\xFF\x23\x20\x1B\xFF" + "\x23\x21\x1C\xFF\x25\x22\x1D\xFF\x26\x23\x1E\xFF\x24\x23\x1E\xFF\x22\x21\x1B\xFF\x22\x20\x1C\xFF\x21\x20\x1B\xFF\x23\x22\x1D\xFF" + "\x26\x24\x1E\xFF\x24\x21\x1D\xFF\x25\x22\x1E\xFF\x24\x22\x1D\xFF\x24\x23\x1E\xFF\x23\x22\x1D\xFF\x23\x22\x1D\xFF\x25\x24\x1E\xFF" + "\x24\x21\x1D\xFF\x27\x24\x20\xFF\x25\x22\x1D\xFF\x26\x24\x1E\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x24\x21\x1D\xFF\x25\x23\x1F\xFF" + "\x26\x24\x1F\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x22\x1D\xFF\x23\x21\x1D\xFF\x24\x22\x1D\xFF\x22\x1F\x1A\xFF\x22\x21\x1B\xFF" + "\x20\x1E\x1A\xFF\x21\x20\x1B\xFF\x22\x20\x1B\xFF\x21\x20\x1A\xFF\x22\x22\x1C\xFF\x23\x21\x1C\xFF\x23\x21\x1C\xFF\x21\x20\x1B\xFF" + "\x23\x20\x1C\xFF\x21\x20\x1B\xFF\x22\x20\x1B\xFF\x21\x1F\x1B\xFF\x23\x21\x1C\xFF\x23\x21\x1C\xFF\x24\x22\x1D\xFF\x21\x20\x1B\xFF" + "\x20\x1F\x1A\xFF\x26\x23\x1E\xFF\x25\x22\x1D\xFF\x23\x22\x1B\xFF\x21\x1F\x1B\xFF\x22\x1F\x1B\xFF\x24\x21\x1D\xFF\x25\x23\x1D\xFF" + "\x25\x22\x1D\xFF\x21\x21\x1B\xFF\x21\x20\x1B\xFF\x22\x20\x1C\xFF\x22\x21\x1B\xFF\x1F\x1E\x19\xFF\x22\x20\x1B\xFF\x21\x20\x1B\xFF" + "\x25\x23\x1E\xFF\x21\x21\x1C\xFF\x20\x1F\x1B\xFF\x21\x20\x1B\xFF\x24\x23\x1E\xFF\x22\x22\x1C\xFF\x21\x20\x1B\xFF\x20\x1F\x1A\xFF" + "\x23\x21\x1C\xFF\x24\x22\x1D\xFF\x24\x21\x1D\xFF\x25\x24\x1E\xFF\x22\x21\x1C\xFF\x23\x22\x1C\xFF\x24\x22\x1E\xFF\x22\x21\x1D\xFF" + "\x18\x18\x13\xFF\x15\x14\x10\xFF\x15\x13\x10\xFF\x13\x12\x0E\xFF\x12\x10\x0D\xFF\x12\x11\x0D\xFF\x16\x15\x10\xFF\x14\x12\x10\xFF" + "\x12\x10\x10\xFF\x12\x10\x0E\xFF\x12\x11\x0E\xFF\x11\x10\x0D\xFF\x14\x13\x0F\xFF\x10\x0F\x0C\xFF\x10\x0E\x0D\xFF\x10\x0E\x0D\xFF" + "\x13\x12\x0E\xFF\x10\x0F\x0D\xFF\x10\x0F\x0C\xFF\x13\x11\x0F\xFF\x13\x12\x0E\xFF\x13\x12\x0E\xFF\x14\x13\x0F\xFF\x12\x11\x0F\xFF" + "\x12\x10\x0E\xFF\x13\x12\x0E\xFF\x11\x10\x0D\xFF\x14\x13\x0F\xFF\x14\x13\x10\xFF\x12\x10\x0E\xFF\x13\x11\x0F\xFF\x14\x12\x10\xFF" + "\x13\x10\x0D\xFF\x17\x15\x12\xFF\x17\x17\x13\xFF\x12\x11\x0E\xFF\x16\x15\x11\xFF\x14\x13\x0F\xFF\x13\x11\x0F\xFF\x13\x12\x0E\xFF" + "\x16\x15\x11\xFF\x14\x13\x0F\xFF\x14\x13\x0F\xFF\x1E\x1E\x18\xFF\x17\x15\x13\xFF\x16\x15\x11\xFF\x13\x12\x0E\xFF\x12\x11\x0D\xFF" + "\x14\x12\x10\xFF\x13\x12\x0F\xFF\x14\x13\x0F\xFF\x13\x12\x0F\xFF\x14\x12\x10\xFF\x15\x14\x10\xFF\x14\x14\x10\xFF\x15\x13\x0F\xFF" + "\x15\x14\x10\xFF\x12\x11\x0E\xFF\x12\x10\x0D\xFF\x14\x13\x0F\xFF\x14\x12\x11\xFF\x15\x14\x11\xFF\x12\x11\x0E\xFF\x16\x14\x0F\xFF" + "\x15\x14\x10\xFF\x11\x10\x0C\xFF\x12\x11\x0D\xFF\x16\x15\x12\xFF\x15\x14\x10\xFF\x14\x13\x0F\xFF\x14\x13\x0F\xFF\x12\x11\x0F\xFF" + "\x13\x12\x10\xFF\x14\x12\x11\xFF\x14\x12\x0F\xFF\x12\x11\x0E\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x15\x14\x11\xFF\x15\x15\x0F\xFF" + "\x17\x16\x12\xFF\x1A\x18\x15\xFF\x17\x16\x13\xFF\x14\x13\x0F\xFF\x11\x0F\x0E\xFF\x12\x11\x0F\xFF\x14\x13\x10\xFF\x16\x15\x11\xFF" + "\x16\x15\x11\xFF\x16\x15\x11\xFF\x18\x17\x13\xFF\x15\x13\x10\xFF\x12\x11\x0E\xFF\x16\x15\x11\xFF\x17\x16\x12\xFF\x15\x13\x11\xFF" + "\x16\x15\x12\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x14\x12\x0F\xFF" + "\x16\x15\x11\xFF\x15\x14\x10\xFF\x15\x14\x10\xFF\x13\x11\x0F\xFF\x15\x13\x11\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x17\x16\x12\xFF" + "\x16\x15\x11\xFF\x17\x16\x12\xFF\x18\x17\x13\xFF\x1C\x1B\x17\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x18\x17\x13\xFF" + "\x14\x13\x0F\xFF\x15\x13\x11\xFF\x14\x12\x0F\xFF\x14\x13\x0F\xFF\x19\x18\x15\xFF\x17\x16\x13\xFF\x15\x14\x11\xFF\x1B\x1A\x16\xFF" + "\x13\x12\x0E\xFF\x17\x15\x11\xFF\x16\x15\x11\xFF\x13\x12\x0F\xFF\x16\x15\x11\xFF\x18\x16\x13\xFF\x18\x16\x13\xFF\x17\x16\x12\xFF" + "\x17\x16\x12\xFF\x16\x15\x11\xFF\x16\x15\x11\xFF\x1B\x1A\x16\xFF\x17\x16\x12\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x15\x14\x10\xFF" + "\x17\x16\x12\xFF\x1A\x1A\x15\xFF\x18\x17\x13\xFF\x18\x17\x14\xFF\x16\x15\x11\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x16\x15\x11\xFF" + "\x19\x18\x14\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x1C\x1B\x17\xFF\x1D\x1B\x17\xFF\x18\x17\x13\xFF\x1C\x1A\x16\xFF\x19\x16\x12\xFF" + "\x16\x15\x11\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF\x1A\x18\x14\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x1A\x19\x15\xFF" + "\x19\x18\x14\xFF\x18\x16\x12\xFF\x1A\x18\x14\xFF\x18\x16\x12\xFF\x15\x14\x10\xFF\x17\x16\x12\xFF\x16\x15\x11\xFF\x18\x16\x12\xFF" + "\x17\x17\x13\xFF\x18\x17\x13\xFF\x1B\x1A\x15\xFF\x1B\x1B\x16\xFF\x18\x18\x14\xFF\x18\x18\x14\xFF\x1A\x19\x15\xFF\x19\x18\x14\xFF" + "\x1B\x19\x15\xFF\x1B\x1A\x16\xFF\x19\x18\x14\xFF\x1C\x1A\x16\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x18\x17\x13\xFF\x18\x17\x13\xFF" + "\x1A\x19\x15\xFF\x19\x18\x14\xFF\x19\x19\x15\xFF\x1A\x18\x14\xFF\x1D\x1C\x18\xFF\x1B\x1A\x16\xFF\x1E\x1C\x18\xFF\x1D\x1B\x17\xFF" + "\x1B\x18\x14\xFF\x1B\x1A\x16\xFF\x1B\x19\x15\xFF\x1B\x1A\x15\xFF\x18\x16\x12\xFF\x19\x18\x13\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF" + "\x1B\x1A\x16\xFF\x20\x1D\x19\xFF\x1D\x1B\x17\xFF\x18\x18\x13\xFF\x1E\x1B\x17\xFF\x1B\x19\x15\xFF\x1B\x1A\x16\xFF\x1A\x19\x14\xFF" + "\x19\x17\x14\xFF\x1C\x19\x16\xFF\x1B\x19\x15\xFF\x19\x17\x15\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x19\x19\x14\xFF\x1A\x1A\x15\xFF" + "\x1B\x1A\x15\xFF\x1E\x1C\x17\xFF\x1B\x1A\x15\xFF\x1B\x1A\x15\xFF\x1D\x1D\x17\xFF\x1E\x1D\x17\xFF\x1C\x1B\x16\xFF\x17\x15\x11\xFF" + "\x19\x19\x15\xFF\x1C\x19\x15\xFF\x1D\x1B\x17\xFF\x1D\x1B\x18\xFF\x19\x18\x14\xFF\x1B\x1A\x16\xFF\x1C\x1B\x17\xFF\x18\x17\x13\xFF" + "\x1B\x1A\x16\xFF\x1D\x1C\x17\xFF\x1B\x1A\x15\xFF\x17\x17\x12\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x17\x17\x13\xFF\x18\x17\x13\xFF" + "\x1D\x1D\x17\xFF\x1F\x1E\x19\xFF\x1A\x19\x14\xFF\x1A\x18\x16\xFF\x1D\x1A\x16\xFF\x1C\x1A\x16\xFF\x1A\x1A\x16\xFF\x1D\x1C\x17\xFF" + "\x1B\x1A\x16\xFF\x1C\x1A\x16\xFF\x1D\x1B\x17\xFF\x1B\x1A\x16\xFF\x1C\x18\x14\xFF\x1D\x1B\x17\xFF\x1C\x1B\x17\xFF\x1E\x1C\x18\xFF" + "\x1F\x1D\x19\xFF\x1E\x1E\x19\xFF\x1F\x1F\x1A\xFF\x1C\x1B\x17\xFF\x1E\x1E\x18\xFF\x1D\x1B\x18\xFF\x1D\x1C\x17\xFF\x1D\x1C\x18\xFF" + "\x1A\x1A\x18\xFF\x1D\x1D\x18\xFF\x1D\x1C\x18\xFF\x1D\x1D\x17\xFF\x1E\x1D\x19\xFF\x1E\x1D\x18\xFF\x1B\x1A\x15\xFF\x1F\x1E\x19\xFF" + "\x1B\x1A\x16\xFF\x1C\x1B\x17\xFF\x1B\x1A\x16\xFF\x1E\x1D\x18\xFF\x1E\x1D\x19\xFF\x1C\x1B\x16\xFF\x1D\x1B\x17\xFF\x1F\x1E\x19\xFF" + "\x1D\x1A\x17\xFF\x1F\x1D\x19\xFF\x22\x21\x1B\xFF\x21\x20\x1B\xFF\x1C\x1B\x17\xFF\x1E\x1D\x18\xFF\x1C\x1B\x17\xFF\x1D\x1D\x17\xFF" + "\x1D\x1C\x16\xFF\x1F\x1D\x18\xFF\x1C\x1B\x16\xFF\x1C\x1B\x17\xFF\x1E\x1C\x18\xFF\x20\x1F\x1A\xFF\x24\x21\x1D\xFF\x21\x21\x1B\xFF" + "\x24\x22\x1D\xFF\x1E\x1D\x18\xFF\x1D\x1C\x18\xFF\x1D\x1C\x17\xFF\x20\x1F\x1B\xFF\x1F\x1E\x19\xFF\x1B\x1A\x16\xFF\x1E\x1C\x17\xFF" + "\x1E\x1D\x19\xFF\x1E\x1D\x18\xFF\x1F\x1D\x18\xFF\x1E\x1B\x18\xFF\x1D\x1C\x17\xFF\x1D\x1C\x17\xFF\x1D\x1D\x19\xFF\x1E\x1E\x18\xFF"; + /** * [MS-RDPEGDI] Test Bitmap 32x32 (16bpp) */ @@ -221,6 +1259,21 @@ static int simple_rand(void) return ((unsigned int) (next / 65536) % 32768); } +static void fill_bitmap_alpha_channel(BYTE* data, int width, int height, BYTE value) +{ + int i, j; + UINT32* pixel; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + pixel = (UINT32*) &data[((i * width) + j) * 4]; + *pixel = ((*pixel & 0x00FFFFFF) | (value << 24)); + } + } +} + int TestFreeRDPCodecPlanar(int argc, char* argv[]) { int i, j; @@ -243,6 +1296,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) format = FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_ARGB, FREERDP_PIXEL_FLIP_NONE); +#if 1 freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12, 1, NULL, &dstSize); @@ -250,6 +1304,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_bitmap_planar_delta_encode_plane((BYTE*) TEST_RDP6_SCANLINES_ABSOLUTE, 6, 3, NULL); freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL, &dstSize); +#endif for (i = 4; i < 64; i += 4) { @@ -258,6 +1313,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) whiteBitmap = (BYTE*) malloc(width * height * 4); FillMemory(whiteBitmap, width * height * 4, 0xFF); + fill_bitmap_alpha_channel(whiteBitmap, width, height, 0x00); compressedBitmap = freerdp_bitmap_compress_planar(whiteBitmap, format, width, height, width * 4, NULL, &dstSize); @@ -274,10 +1330,23 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) printf("success decompressing white bitmap: width: %d height: %d\n", width, height); } + if (memcmp(decompressedBitmap, whiteBitmap, width * height * 4) != 0) + { + printf("white bitmap\n"); + winpr_HexDump(whiteBitmap, width * height * 4); + + printf("decompressed bitmap\n"); + winpr_HexDump(decompressedBitmap, width * height * 4); + + printf("error decompressed white bitmap corrupted: width: %d height: %d\n", width, height); + return -1; + } + free(compressedBitmap); free(decompressedBitmap); } +#if 1 for (i = 4; i < 64; i += 4) { width = i; @@ -285,6 +1354,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) blackBitmap = (BYTE*) malloc(width * height * 4); ZeroMemory(blackBitmap, width * height * 4); + fill_bitmap_alpha_channel(blackBitmap, width, height, 0x00); compressedBitmap = freerdp_bitmap_compress_planar(blackBitmap, format, width, height, width * 4, NULL, &dstSize); @@ -301,6 +1371,18 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) printf("success decompressing black bitmap: width: %d height: %d\n", width, height); } + if (memcmp(decompressedBitmap, blackBitmap, width * height * 4) != 0) + { + printf("black bitmap\n"); + winpr_HexDump(blackBitmap, width * height * 4); + + printf("decompressed bitmap\n"); + winpr_HexDump(decompressedBitmap, width * height * 4); + + printf("error decompressed black bitmap corrupted: width: %d height: %d\n", width, height); + return -1; + } + free(compressedBitmap); free(decompressedBitmap); } @@ -317,6 +1399,8 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) randomBitmap[j] = (BYTE) (simple_rand() % 256); } + fill_bitmap_alpha_channel(randomBitmap, width, height, 0x00); + compressedBitmap = freerdp_bitmap_compress_planar(randomBitmap, format, width, height, width * 4, NULL, &dstSize); decompressedBitmap = (BYTE*) malloc(width * height * 4); @@ -332,9 +1416,96 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) printf("success decompressing random bitmap: width: %d height: %d\n", width, height); } + if (memcmp(decompressedBitmap, randomBitmap, width * height * 4) != 0) + { + printf("random bitmap\n"); + winpr_HexDump(randomBitmap, width * height * 4); + + printf("decompressed bitmap\n"); + winpr_HexDump(decompressedBitmap, width * height * 4); + + printf("error decompressed random bitmap corrupted: width: %d height: %d\n", width, height); + return -1; + } + free(compressedBitmap); free(decompressedBitmap); } +#endif + + /* Experimental Case 01 */ + + width = 64; + height = 64; + + compressedBitmap = freerdp_bitmap_compress_planar((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, + format, width, height, width * 4, NULL, &dstSize); + + decompressedBitmap = (BYTE*) malloc(width * height * 4); + ZeroMemory(decompressedBitmap, width * height * 4); + + if (!bitmap_decompress(compressedBitmap, decompressedBitmap, width, height, dstSize, 32, 32)) + { + printf("failed to decompress experimental bitmap 01: width: %d height: %d\n", width, height); + return -1; + } + else + { + printf("success decompressing experimental bitmap 01: width: %d height: %d\n", width, height); + } + + fill_bitmap_alpha_channel(decompressedBitmap, width, height, 0xFF); + + if (memcmp(decompressedBitmap, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, width * height * 4) != 0) + { + //printf("experimental bitmap 01\n"); + //winpr_HexDump((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, width * height * 4); + + //printf("decompressed bitmap\n"); + //winpr_HexDump(decompressedBitmap, width * height * 4); + + printf("error: decompressed experimental bitmap 01 is corrupted\n"); + return -1; + } + + free(compressedBitmap); + free(decompressedBitmap); + + /* Experimental Case 02 */ + + width = 64; + height = 64; + + compressedBitmap = freerdp_bitmap_compress_planar((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, + format, width, height, width * 4, NULL, &dstSize); + + decompressedBitmap = (BYTE*) malloc(width * height * 4); + ZeroMemory(decompressedBitmap, width * height * 4); + + if (!bitmap_decompress(compressedBitmap, decompressedBitmap, width, height, dstSize, 32, 32)) + { + printf("failed to decompress experimental bitmap 02: width: %d height: %d\n", width, height); + return -1; + } + else + { + printf("success decompressing experimental bitmap 02: width: %d height: %d\n", width, height); + } + + if (memcmp(decompressedBitmap, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, width * height * 4) != 0) + { + printf("experimental bitmap 02\n"); + winpr_HexDump((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, width * height * 4); + + printf("decompressed bitmap\n"); + winpr_HexDump(decompressedBitmap, width * height * 4); + + printf("error: decompressed experimental bitmap 02 is corrupted\n"); + return -1; + } + + free(compressedBitmap); + free(decompressedBitmap); freerdp_clrconv_free(clrconv); free(srcBitmap32); diff --git a/winpr/include/winpr/print.h b/winpr/include/winpr/print.h index b85f176f9..bcb3f7642 100644 --- a/winpr/include/winpr/print.h +++ b/winpr/include/winpr/print.h @@ -34,6 +34,7 @@ extern "C" { #endif WINPR_API void winpr_HexDump(BYTE* data, int length); +WINPR_API void winpr_CArrayDump(BYTE* data, int length, int width); WINPR_API int wprintfx(const char *fmt, ...); WINPR_API int wvprintfx(const char *fmt, va_list args); diff --git a/winpr/libwinpr/utils/print.c b/winpr/libwinpr/utils/print.c index 8d98b100f..2b0bae91a 100644 --- a/winpr/libwinpr/utils/print.c +++ b/winpr/libwinpr/utils/print.c @@ -60,6 +60,32 @@ void winpr_HexDump(BYTE* data, int length) } } +void winpr_CArrayDump(BYTE* data, int length, int width) +{ + BYTE* p = data; + int i, line, offset = 0; + + while (offset < length) + { + line = length - offset; + + if (line > width) + line = width; + + printf("\t\""); + + for (i = 0; i < line; i++) + printf("\\x%02X", p[i]); + + printf("\"\n"); + + offset += line; + p += line; + } + + printf("\n"); +} + int wvprintfx(const char *fmt, va_list args) { return trio_vprintf(fmt, args); From 82b12621af9e47352aa5c282e193ba93addb0c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 29 Nov 2013 03:06:39 -0500 Subject: [PATCH 058/149] libfreerdp-codec: refactor planar decompression --- libfreerdp/codec/planar.c | 212 ++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 112 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 235e21e82..42bbe3fdb 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -28,124 +28,121 @@ #include "planar.h" -#define IN_UINT8_MV(_p) (*((_p)++)) - -static int freerdp_bitmap_planar_decompress_plane_rle(BYTE* in, int width, int height, BYTE* out, int size) +static int freerdp_bitmap_planar_decompress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int size) { - int indexw; - int indexh; - int code; - int collen; - int replen; - int color; - int x; - int revcode; - BYTE* last_line; - BYTE* this_line; - BYTE* org_in; - BYTE* org_out; + int x, y; + BYTE* srcp; + BYTE* dstp; + UINT32 pixel; + int scanline; + int cRawBytes; + int nRunLength; + int deltaValue; + BYTE controlByte; + BYTE* currentScanline; + BYTE* previousScanline; - org_in = in; - org_out = out; - last_line = 0; - indexh = 0; + srcp = inPlane; + dstp = outPlane; + scanline = width * 4; + previousScanline = NULL; - while (indexh < height) + y = 0; + + while (y < height) { - out = (org_out + width * height * 4) - ((indexh + 1) * width * 4); - color = 0; - this_line = out; - indexw = 0; + pixel = 0; + dstp = (outPlane + height * scanline) - ((y + 1) * scanline); + currentScanline = dstp; - if (last_line == 0) + x = 0; + + while (x < width) { - while (indexw < width) + controlByte = *srcp; + srcp++; + + nRunLength = PLANAR_CONTROL_BYTE_RUN_LENGTH(controlByte); + cRawBytes = PLANAR_CONTROL_BYTE_RAW_BYTES(controlByte); + + if (nRunLength == 1) { - code = IN_UINT8_MV(in); + nRunLength = cRawBytes + 16; + cRawBytes = 0; + } + else if (nRunLength == 2) + { + nRunLength = cRawBytes + 32; + cRawBytes = 0; + } - replen = code & 0xf; - collen = (code >> 4) & 0xf; - revcode = (replen << 4) | collen; + if (!previousScanline) + { + /* first scanline, absolute values */ - if ((revcode <= 47) && (revcode >= 16)) + while (cRawBytes > 0) { - replen = revcode; - collen = 0; + pixel = *srcp; + srcp++; + + *dstp = pixel; + dstp += 4; + x++; + cRawBytes--; } - while (collen > 0) + while (nRunLength > 0) { - color = IN_UINT8_MV(in); - *out = color; - out += 4; - indexw++; - collen--; - } - - while (replen > 0) - { - *out = color; - out += 4; - indexw++; - replen--; + *dstp = pixel; + dstp += 4; + x++; + nRunLength--; } } - } - else - { - while (indexw < width) + else { - code = IN_UINT8_MV(in); + /* delta values relative to previous scanline */ - replen = code & 0xf; - collen = (code >> 4) & 0xf; - revcode = (replen << 4) | collen; - - if ((revcode <= 47) && (revcode >= 16)) + while (cRawBytes > 0) { - replen = revcode; - collen = 0; - } + deltaValue = *srcp; + srcp++; - while (collen > 0) - { - x = IN_UINT8_MV(in); - - if (x & 1) + if (deltaValue & 1) { - x = x >> 1; - x = x + 1; - color = -x; + deltaValue = deltaValue >> 1; + deltaValue = deltaValue + 1; + pixel = -deltaValue; } else { - x = x >> 1; - color = x; + deltaValue = deltaValue >> 1; + pixel = deltaValue; } - x = last_line[indexw * 4] + color; - *out = x; - out += 4; - indexw++; - collen--; + deltaValue = previousScanline[x * 4] + pixel; + *dstp = deltaValue; + dstp += 4; + x++; + cRawBytes--; } - while (replen > 0) + while (nRunLength > 0) { - x = last_line[indexw * 4] + color; - *out = x; - out += 4; - indexw++; - replen--; + deltaValue = previousScanline[x * 4] + pixel; + *dstp = deltaValue; + dstp += 4; + x++; + nRunLength--; } } } - indexh++; - last_line = this_line; + previousScanline = currentScanline; + y++; } - return (int) (in - org_in); + return (int) (srcp - inPlane); } static int freerdp_bitmap_planar_decompress_plane_raw(BYTE* srcData, int width, int height, BYTE* dstData, int size) @@ -166,62 +163,53 @@ static int freerdp_bitmap_planar_decompress_plane_raw(BYTE* srcData, int width, int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int size) { BYTE* srcp; + int dstSize; BYTE FormatHeader; - int bytesProcessed; - int totalProcessed; srcp = srcData; FormatHeader = *srcp; - totalProcessed = 1; srcp++; if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA)) { if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { - bytesProcessed = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 3, size - totalProcessed); - totalProcessed += bytesProcessed; - srcp += bytesProcessed; + dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 3, size - (srcp - srcData)); + srcp += dstSize; } else { - bytesProcessed = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 3, size - totalProcessed); - totalProcessed += bytesProcessed; - srcp += bytesProcessed; + dstSize = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 3, size - (srcp - srcData)); + srcp += dstSize; } } if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { - bytesProcessed = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 2, size - totalProcessed); - totalProcessed += bytesProcessed; - srcp += bytesProcessed; + dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 2, size - (srcp - srcData)); + srcp += dstSize; - bytesProcessed = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 1, size - totalProcessed); - totalProcessed += bytesProcessed; - srcp += bytesProcessed; + dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 1, size - (srcp - srcData)); + srcp += dstSize; - bytesProcessed = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 0, size - totalProcessed); - totalProcessed += bytesProcessed; - srcp += bytesProcessed; + dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 0, size - (srcp - srcData)); + srcp += dstSize; } else { - bytesProcessed = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 2, size - totalProcessed); - totalProcessed += bytesProcessed; - srcp += bytesProcessed; + dstSize = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 2, size - (srcp - srcData)); + srcp += dstSize; - bytesProcessed = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 1, size - totalProcessed); - totalProcessed += bytesProcessed; - srcp += bytesProcessed; + dstSize = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 1, size - (srcp - srcData)); + srcp += dstSize; - bytesProcessed = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 0, size - totalProcessed); - totalProcessed += bytesProcessed + 1; - srcp += bytesProcessed; + dstSize = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 0, size - (srcp - srcData)); + srcp += dstSize; + srcp++; } - return (size == totalProcessed) ? 1 : 0; + return (size == (srcp - srcData)) ? 1 : 0; } int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]) From 9d1c4c10a6349d3ae2fdd65ecba7fc19538467c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 29 Nov 2013 04:12:59 -0500 Subject: [PATCH 059/149] libfreerdp-codec: make planar decoder more robust --- libfreerdp/codec/bitmap_decode.c | 2 +- libfreerdp/codec/planar.c | 65 ++++++++++++++++--- .../codec/test/TestFreeRDPCodecPlanar.c | 2 + 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/libfreerdp/codec/bitmap_decode.c b/libfreerdp/codec/bitmap_decode.c index 72386fa99..2330872c9 100644 --- a/libfreerdp/codec/bitmap_decode.c +++ b/libfreerdp/codec/bitmap_decode.c @@ -271,7 +271,7 @@ BOOL bitmap_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int } else if (srcBpp == 32 && dstBpp == 32) { - if (!freerdp_bitmap_planar_decompress(srcData, dstData, width, height, size)) + if (freerdp_bitmap_planar_decompress(srcData, dstData, width, height, size) < 0) return FALSE; } else if (srcBpp == 15 && dstBpp == 15) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 42bbe3fdb..dbcf61f29 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -28,8 +28,9 @@ #include "planar.h" -static int freerdp_bitmap_planar_decompress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int size) +static int freerdp_bitmap_planar_decompress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int srcSize) { + int k; int x, y; BYTE* srcp; BYTE* dstp; @@ -42,6 +43,8 @@ static int freerdp_bitmap_planar_decompress_plane_rle(BYTE* inPlane, int width, BYTE* currentScanline; BYTE* previousScanline; + k = 0; + srcp = inPlane; dstp = outPlane; scanline = width * 4; @@ -62,9 +65,17 @@ static int freerdp_bitmap_planar_decompress_plane_rle(BYTE* inPlane, int width, controlByte = *srcp; srcp++; + if ((srcp - inPlane) > srcSize) + { + printf("freerdp_bitmap_planar_decompress_plane_rle: error reading input buffer\n"); + return -1; + } + nRunLength = PLANAR_CONTROL_BYTE_RUN_LENGTH(controlByte); cRawBytes = PLANAR_CONTROL_BYTE_RAW_BYTES(controlByte); + //printf("CONTROL(%d, %d)\n", cRawBytes, nRunLength); + if (nRunLength == 1) { nRunLength = cRawBytes + 16; @@ -76,6 +87,26 @@ static int freerdp_bitmap_planar_decompress_plane_rle(BYTE* inPlane, int width, cRawBytes = 0; } +#if 0 + printf("y: %d cRawBytes: %d nRunLength: %d\n", y, cRawBytes, nRunLength); + + printf("RAW["); + + for (k = 0; k < cRawBytes; k++) + { + printf("0x%02X%s", srcp[k], + ((k + 1) == cRawBytes) ? "" : ", "); + } + + printf("] RUN[%d]\n", nRunLength); +#endif + + if (((dstp + (cRawBytes + nRunLength)) - currentScanline) > width * 4) + { + printf("freerdp_bitmap_planar_decompress_plane_rle: too many pixels in scanline\n"); + return -1; + } + if (!previousScanline) { /* first scanline, absolute values */ @@ -176,6 +207,10 @@ int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, in if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 3, size - (srcp - srcData)); + + if (dstSize < 0) + return -1; + srcp += dstSize; } else @@ -188,12 +223,24 @@ int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, in if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 2, size - (srcp - srcData)); + + if (dstSize < 0) + return -1; + srcp += dstSize; dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 1, size - (srcp - srcData)); + + if (dstSize < 0) + return -1; + srcp += dstSize; dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 0, size - (srcp - srcData)); + + if (dstSize < 0) + return -1; + srcp += dstSize; } else @@ -209,7 +256,7 @@ int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, in srcp++; } - return (size == (srcp - srcData)) ? 1 : 0; + return (size == (srcp - srcData)) ? 0 : -1; } int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]) @@ -525,21 +572,23 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei continue; } + rle->cRawBytes++; + if (rle->nRunLength < 3) - { - rle->cRawBytes++; continue; - } if (freerdp_bitmap_planar_compress_plane_rle_segment(rle, (j == 1) ? -1 : 0) < 0) return NULL; } - if ((rle->cRawBytes != 0) || (rle->nRunLength != 0)) + if (rle->nRunLength < 3) { - if (freerdp_bitmap_planar_compress_plane_rle_segment(rle, 1) < 0) - return NULL; + rle->cRawBytes += rle->nRunLength; + rle->nRunLength = 0; } + + if (freerdp_bitmap_planar_compress_plane_rle_segment(rle, 1) < 0) + return NULL; } *dstSize = (rle->output - outPlane); diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 274a649eb..81276eada 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -1306,6 +1306,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL, &dstSize); #endif +#if 1 for (i = 4; i < 64; i += 4) { width = i; @@ -1345,6 +1346,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); } +#endif #if 1 for (i = 4; i < 64; i += 4) From d5f18e88ccdb8affa0237ac6cf8347bb42a43af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 3 Dec 2013 00:52:02 -0500 Subject: [PATCH 060/149] libwinpr-environment: fix usage of environ on OS X --- winpr/libwinpr/environment/environment.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/winpr/libwinpr/environment/environment.c b/winpr/libwinpr/environment/environment.c index 62b883311..272aabdc4 100644 --- a/winpr/libwinpr/environment/environment.c +++ b/winpr/libwinpr/environment/environment.c @@ -31,11 +31,17 @@ #define strnicmp strncasecmp #include +#include #ifdef HAVE_UNISTD_H #include #endif +#ifdef __MACOSX__ +#include +#define environ (*_NSGetEnviron()) +#endif + DWORD GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer) { char* cwd; From 8f310980cac95d1ec39caf61a15d6bc6413e244e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 3 Dec 2013 12:10:12 -0500 Subject: [PATCH 061/149] cmake: fix Android toolchain on 64-bit hosts --- cmake/AndroidToolchain.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/AndroidToolchain.cmake b/cmake/AndroidToolchain.cmake index 71353e553..d31eaaccd 100644 --- a/cmake/AndroidToolchain.cmake +++ b/cmake/AndroidToolchain.cmake @@ -470,6 +470,7 @@ if( ANDROID_FORBID_SYGWIN ) endif() endif() +# FIXME: properly detect 64-bit host, currently reported as 32-bit # detect current host platform if( NOT DEFINED ANDROID_NDK_HOST_X64 AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64") @@ -493,7 +494,7 @@ else() endif() if( NOT ANDROID_NDK_HOST_X64 ) - set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) + #set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) endif() # see if we have path to Android NDK From cea8c08328e6c650c43851813497589647fb6b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 3 Dec 2013 15:19:58 -0500 Subject: [PATCH 062/149] ifreerdp: fix iOS/OSX platform detection --- client/iOS/.gitignore | 2 ++ winpr/include/winpr/platform.h | 23 ++++++++++++++++------- winpr/libwinpr/environment/environment.c | 4 +++- winpr/libwinpr/sysinfo/sysinfo.c | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/client/iOS/.gitignore b/client/iOS/.gitignore index 8bcaf57e4..20d3653e6 100644 --- a/client/iOS/.gitignore +++ b/client/iOS/.gitignore @@ -6,3 +6,5 @@ bin/ build/ project.pbxproj !iFreeRDP.xcodeproj/ +iFreeRDP.app/ + diff --git a/winpr/include/winpr/platform.h b/winpr/include/winpr/platform.h index ad2f6187c..6722fe368 100644 --- a/winpr/include/winpr/platform.h +++ b/winpr/include/winpr/platform.h @@ -154,19 +154,28 @@ /* GNU/Linux (__gnu_linux__) */ -/* Mac OS X (__MACOSX__) */ +/* Apple Platforms (iOS, Mac OS X) */ #if (defined(__APPLE__) && defined(__MACH__)) + +#include + +#if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) + +/* iOS (__IOS__) */ + +#ifndef __IOS__ +#define __IOS__ 1 +#endif + +#elif (TARGET_OS_MAC == 1) + +/* Mac OS X (__MACOSX__) */ + #ifndef __MACOSX__ #define __MACOSX__ 1 #endif -#endif -/* iOS (__IOS__)*/ - -#if (defined(__APPLE__) && defined(TARGET_OS_IPHONE)) -#ifndef __IOS__ -#define __IOS__ 1 #endif #endif diff --git a/winpr/libwinpr/environment/environment.c b/winpr/libwinpr/environment/environment.c index 272aabdc4..d67d33ea0 100644 --- a/winpr/libwinpr/environment/environment.c +++ b/winpr/libwinpr/environment/environment.c @@ -37,7 +37,9 @@ #include #endif -#ifdef __MACOSX__ +#if defined(__IOS__) + +#elif defined(__MACOSX__) #include #define environ (*_NSGetEnviron()) #endif diff --git a/winpr/libwinpr/sysinfo/sysinfo.c b/winpr/libwinpr/sysinfo/sysinfo.c index 9091c5e84..6161043cd 100644 --- a/winpr/libwinpr/sysinfo/sysinfo.c +++ b/winpr/libwinpr/sysinfo/sysinfo.c @@ -84,7 +84,7 @@ #include #include -#if defined(__MACOSX__) || \ +#if defined(__MACOSX__) || defined(__IOS__) || \ defined(__FreeBSD__) || defined(__NetBSD__) || \ defined(__OpenBSD__) || defined(__DragonFly__) #include From 9096bd3b61374a579d4d246befc307cf36b90358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 3 Dec 2013 18:50:22 -0500 Subject: [PATCH 063/149] libfreerdp-codec: make planar codec bitmap 01 pass the test --- libfreerdp/codec/planar.c | 113 +++++++++++------- libfreerdp/codec/planar.h | 3 +- .../codec/test/TestFreeRDPCodecPlanar.c | 84 +++++++++++-- 3 files changed, 143 insertions(+), 57 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index dbcf61f29..ff58ddf45 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -320,30 +320,30 @@ struct _PLANAR_RLE_CONTEXT }; typedef struct _PLANAR_RLE_CONTEXT PLANAR_RLE_CONTEXT; -int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle, int position) +int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) { - int k; +#if 0 + if (rle->nRunLength >= 3) + printf("### cRawBytes: %d nRunLength: %d\n", rle->cRawBytes, rle->nRunLength); - if (position < 0) - printf("B"); - else if (position == 0) - printf("M"); - else - printf("E"); - - printf(" RAW["); - - for (k = 0; k < rle->cRawBytes; k++) { - printf("0x%02X%s", rle->rawValues[k], - ((k + 1) == rle->cRawBytes) ? "" : ", "); - } + int k; - printf("] RUN[%d]\n", rle->nRunLength); + printf("RAW["); + + for (k = 0; k < rle->cRawBytes; k++) + { + printf("0x%02X%s", rle->rawValues[k], + ((k + 1) == rle->cRawBytes) ? "" : ", "); + } + + printf("] RUN[%d]\n", rle->nRunLength); + } +#endif while ((rle->cRawBytes != 0) || (rle->nRunLength != 0)) { - printf("cRawBytes: %d nRunLength: %d\n", rle->cRawBytes, rle->nRunLength); + //printf("|| cRawBytes: %d nRunLength: %d\n", rle->cRawBytes, rle->nRunLength); if (rle->nRunLength < 3) { @@ -485,7 +485,6 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle, in CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); rle->rawValues += (rle->cRawBytes + rle->nRunLength); rle->output += rle->cRawBytes; - rle->cRawBytes = 0; } rle->nRunLength -= 15; @@ -534,12 +533,11 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle, in BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int* dstSize) { int i, j; - int cSegments; + int bSymbolMatch; + int bSequenceEnd; PLANAR_RLE_CONTEXT rle_s; PLANAR_RLE_CONTEXT* rle = &rle_s; - cSegments = 0; - if (!outPlane) { rle->outPlaneSize = width * height * 2; @@ -557,38 +555,69 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei for (i = 0; i < height; i++) { - rle->cRawBytes = 1; rle->nRunLength = 0; + rle->cRawBytes = 1; + rle->rawScanline = &inPlane[i * width]; rle->rawValues = rle->rawScanline; for (j = 1; j < width; j++) { - printf("j: %d cRawBytes: %d nRunLength: %d\n", j, rle->cRawBytes, rle->nRunLength); + bSymbolMatch = FALSE; + bSequenceEnd = ((j + 1) == width) ? TRUE : FALSE; if (rle->rawScanline[j] == rle->rawValues[rle->cRawBytes - 1]) { - rle->nRunLength++; - continue; + bSymbolMatch = TRUE; + } + else + { + //printf("mismatch: nRunLength: %d cRawBytes: %d\n", rle->nRunLength, rle->cRawBytes); + + if (bSequenceEnd) + rle->cRawBytes++; + + if (rle->nRunLength < 3) + { + rle->cRawBytes += rle->nRunLength; + rle->nRunLength = 0; + } + else + { + bSequenceEnd = TRUE; + } } - rle->cRawBytes++; + if (bSymbolMatch) + { + rle->nRunLength++; + } - if (rle->nRunLength < 3) - continue; + //printf("j: %d [0x%02X] cRawBytes: %d nRunLength: %d bSymbolMatch: %d bSequenceEnd: %d\n", + // j, rle->rawScanline[j], rle->cRawBytes, rle->nRunLength, bSymbolMatch, bSequenceEnd); - if (freerdp_bitmap_planar_compress_plane_rle_segment(rle, (j == 1) ? -1 : 0) < 0) - return NULL; + if (bSequenceEnd) + { + if (rle->nRunLength < 3) + { + rle->cRawBytes += rle->nRunLength; + rle->nRunLength = 0; + } + + if (freerdp_bitmap_planar_compress_plane_rle_segment(rle) < 0) + return NULL; + + rle->nRunLength = 0; + rle->cRawBytes = 1; + } + else + { + if (!bSymbolMatch) + { + rle->cRawBytes++; + } + } } - - if (rle->nRunLength < 3) - { - rle->cRawBytes += rle->nRunLength; - rle->nRunLength = 0; - } - - if (freerdp_bitmap_planar_compress_plane_rle_segment(rle, 1) < 0) - return NULL; } *dstSize = (rle->output - outPlane); @@ -613,8 +642,6 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int #endif dstSizes[1] = outPlanesSize; - printf("PlaneR\n"); - if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes, &dstSizes[1])) return 0; @@ -622,8 +649,6 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int outPlanesSize -= dstSizes[1]; dstSizes[2] = outPlanesSize; - printf("PlaneG\n"); - if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes, &dstSizes[2])) return 0; @@ -631,8 +656,6 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int outPlanesSize -= dstSizes[2]; dstSizes[3] = outPlanesSize; - printf("PlaneB\n"); - if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes, &dstSizes[3])) return 0; diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h index b6a34660f..8931f94b2 100644 --- a/libfreerdp/codec/planar.h +++ b/libfreerdp/codec/planar.h @@ -25,8 +25,7 @@ #include #define PLANAR_CONTROL_BYTE(_nRunLength, _cRawBytes) \ - ((_nRunLength & 0x0F) | ((_cRawBytes & 0x0F) << 4)) + \ - (printf("CONTROL_BYTE(%d, %d)\n", _cRawBytes, _nRunLength) * 0) + (_nRunLength & 0x0F) | ((_cRawBytes & 0x0F) << 4) #define PLANAR_CONTROL_BYTE_RUN_LENGTH(_controlByte) (_controlByte & 0x0F) #define PLANAR_CONTROL_BYTE_RAW_BYTES(_controlByte) ((_controlByte >> 4) & 0x0F) diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 81276eada..2bb41134f 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -10,7 +10,7 @@ * Experimental Case 01: 64x64 (32bpp) */ -const BYTE TEST_RLE_BITMAP_EXPERIMENTAL_01[16384] = +static BYTE TEST_RLE_BITMAP_EXPERIMENTAL_01[16384] = "\x1B\x1A\x16\xFF\x1C\x1A\x16\xFF\x18\x17\x13\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x18\x16\x12\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF" "\x1D\x1C\x17\xFF\x1D\x1B\x17\xFF\x1C\x1B\x17\xFF\x1B\x1A\x16\xFF\x1A\x19\x15\xFF\x1A\x19\x15\xFF\x18\x17\x13\xFF\x1A\x19\x15\xFF" "\x1B\x1A\x16\xFF\x19\x18\x14\xFF\x19\x18\x14\xFF\x18\x16\x14\xFF\x1C\x1A\x16\xFF\x1A\x18\x14\xFF\x1B\x1A\x16\xFF\x19\x17\x13\xFF" @@ -529,7 +529,7 @@ const BYTE TEST_RLE_BITMAP_EXPERIMENTAL_01[16384] = * Experimental Case 02: 64x64 (32bpp) */ -const BYTE TEST_RLE_BITMAP_EXPERIMENTAL_02[16384] = +static BYTE TEST_RLE_BITMAP_EXPERIMENTAL_02[16384] = "\x1C\x1C\x17\xFF\x1D\x1B\x18\xFF\x1B\x19\x15\xFF\x19\x18\x13\xFF\x19\x18\x14\xFF\x17\x16\x12\xFF\x17\x17\x13\xFF\x19\x17\x14\xFF" "\x15\x14\x11\xFF\x13\x13\x10\xFF\x4F\x4B\x3E\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x22\x21\x1C\xFF\x22\x21\x1B\xFF\x21\x21\x1A\xFF" "\x22\x21\x1B\xFF\x21\x20\x1A\xFF\x21\x20\x1A\xFF\x21\x20\x1A\xFF\x22\x21\x1B\xFF\x23\x22\x1D\xFF\x22\x20\x1A\xFF\x21\x20\x1A\xFF" @@ -1274,6 +1274,66 @@ static void fill_bitmap_alpha_channel(BYTE* data, int width, int height, BYTE va } } +void fill_bitmap_red_channel(BYTE* data, int width, int height, BYTE value) +{ + int i, j; + UINT32* pixel; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + pixel = (UINT32*) &data[((i * width) + j) * 4]; + *pixel = ((*pixel & 0xFF00FFFF) | (value << 16)); + } + } +} + +void fill_bitmap_green_channel(BYTE* data, int width, int height, BYTE value) +{ + int i, j; + UINT32* pixel; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + pixel = (UINT32*) &data[((i * width) + j) * 4]; + *pixel = ((*pixel & 0xFFFF00FF) | (value << 8)); + } + } +} + +void fill_bitmap_blue_channel(BYTE* data, int width, int height, BYTE value) +{ + int i, j; + UINT32* pixel; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + pixel = (UINT32*) &data[((i * width) + j) * 4]; + *pixel = ((*pixel & 0xFFFFFF00) | (value)); + } + } +} + +void dump_color_channel(BYTE* data, int width, int height) +{ + int i, j; + + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + printf("%02X%s", *data, + ((j + 1) == width)? "\n" : " "); + data += 4; + } + } +} + int TestFreeRDPCodecPlanar(int argc, char* argv[]) { int i, j; @@ -1304,9 +1364,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_bitmap_planar_delta_encode_plane((BYTE*) TEST_RDP6_SCANLINES_ABSOLUTE, 6, 3, NULL); freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL, &dstSize); -#endif -#if 1 for (i = 4; i < 64; i += 4) { width = i; @@ -1346,9 +1404,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); } -#endif -#if 1 for (i = 4; i < 64; i += 4) { width = i; @@ -1457,14 +1513,17 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) } fill_bitmap_alpha_channel(decompressedBitmap, width, height, 0xFF); + fill_bitmap_alpha_channel((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, width, height, 0xFF); if (memcmp(decompressedBitmap, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, width * height * 4) != 0) { - //printf("experimental bitmap 01\n"); - //winpr_HexDump((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, width * height * 4); +#if 0 + printf("experimental bitmap 01\n"); + winpr_HexDump((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, width * height * 4); - //printf("decompressed bitmap\n"); - //winpr_HexDump(decompressedBitmap, width * height * 4); + printf("decompressed bitmap\n"); + winpr_HexDump(decompressedBitmap, width * height * 4); +#endif printf("error: decompressed experimental bitmap 01 is corrupted\n"); return -1; @@ -1473,6 +1532,8 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); + return 0; + /* Experimental Case 02 */ width = 64; @@ -1494,6 +1555,9 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) printf("success decompressing experimental bitmap 02: width: %d height: %d\n", width, height); } + fill_bitmap_alpha_channel(decompressedBitmap, width, height, 0xFF); + fill_bitmap_alpha_channel((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, width, height, 0xFF); + if (memcmp(decompressedBitmap, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, width * height * 4) != 0) { printf("experimental bitmap 02\n"); From 2114fbb8f921e7cd1edbe8e54eeeaafb0a793fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 3 Dec 2013 20:14:07 -0500 Subject: [PATCH 064/149] libfreerdp-codec: add new planar codec test case --- libfreerdp/codec/planar.c | 28 + .../codec/test/TestFreeRDPCodecPlanar.c | 569 +++++++++++++++++- 2 files changed, 594 insertions(+), 3 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index ff58ddf45..6829bd120 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -191,12 +191,16 @@ static int freerdp_bitmap_planar_decompress_plane_raw(BYTE* srcData, int width, return (width * height); } +//int g_DecompressCount = 0; + int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int size) { BYTE* srcp; int dstSize; BYTE FormatHeader; + //printf("freerdp_bitmap_planar_decompress: %d\n", ++g_DecompressCount); + srcp = srcData; FormatHeader = *srcp; @@ -719,6 +723,8 @@ int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[5], int width, int return 0; } +//static int g_Count = 0; + BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* dstData, int* dstSize) { int size; @@ -757,6 +763,28 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h freerdp_bitmap_planar_delta_encode_planes(planes, width, height, deltaPlanes); +#if 0 + { + g_Count++; + BYTE* bitmap; + int bitmapLength; + + bitmapLength = width * height * 4; + bitmap = malloc(width * height * 4); + ZeroMemory(bitmap, bitmapLength); + + freerdp_bitmap_planar_decompress_plane_raw(planes[0], width, height, bitmap + 3, planeSize); /* A */ + freerdp_bitmap_planar_decompress_plane_raw(planes[1], width, height, bitmap + 2, planeSize); /* R */ + freerdp_bitmap_planar_decompress_plane_raw(planes[2], width, height, bitmap + 1, planeSize); /* G */ + freerdp_bitmap_planar_decompress_plane_raw(planes[3], width, height, bitmap + 0, planeSize); /* B */ + + printf("Bitmap %02d\n", g_Count); + winpr_CArrayDump(bitmap, bitmapLength, 32); + + free(bitmap); + } +#endif + if (freerdp_bitmap_planar_compress_planes_rle(deltaPlanes, width, height, rlePlanesBuffer, (int*) &dstSizes) > 0) { int offset = 0; diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 2bb41134f..a00278d4e 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -1043,6 +1043,524 @@ static BYTE TEST_RLE_BITMAP_EXPERIMENTAL_02[16384] = "\x24\x22\x1D\xFF\x1E\x1D\x18\xFF\x1D\x1C\x18\xFF\x1D\x1C\x17\xFF\x20\x1F\x1B\xFF\x1F\x1E\x19\xFF\x1B\x1A\x16\xFF\x1E\x1C\x17\xFF" "\x1E\x1D\x19\xFF\x1E\x1D\x18\xFF\x1F\x1D\x18\xFF\x1E\x1B\x18\xFF\x1D\x1C\x17\xFF\x1D\x1C\x17\xFF\x1D\x1D\x19\xFF\x1E\x1E\x18\xFF"; +/** + * Experimental Case 03: 64x64 (32bpp) + */ + +static BYTE TEST_RLE_BITMAP_EXPERIMENTAL_03[16384] = + "\x27\x2A\x23\xFF\x23\x25\x1F\xFF\x23\x23\x1E\xFF\x24\x23\x1D\xFF\x25\x23\x1D\xFF\x25\x23\x1E\xFF\x25\x23\x1D\xFF\x25\x24\x1F\xFF" + "\x28\x2B\x23\xFF\x37\x60\x4A\xFF\x4A\xA2\x78\xFF\x47\x97\x71\xFF\x41\x84\x64\xFF\x3D\x75\x58\xFF\x38\x62\x4B\xFF\x33\x50\x3E\xFF" + "\x2E\x3C\x30\xFF\x2A\x34\x29\xFF\x28\x30\x26\xFF\x27\x2C\x24\xFF\x26\x29\x22\xFF\x27\x28\x21\xFF\x27\x26\x20\xFF\x27\x25\x20\xFF" + "\x25\x25\x20\xFF\x24\x23\x1D\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x26\x24\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF" + "\x26\x25\x1F\xFF\x28\x25\x20\xFF\x28\x26\x21\xFF\x2A\x27\x22\xFF\x26\x25\x1E\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x28\x25\x1F\xFF" + "\x27\x27\x1F\xFF\x27\x24\x1F\xFF\x25\x24\x1F\xFF\x26\x24\x1F\xFF\x24\x24\x1E\xFF\x25\x24\x1D\xFF\x27\x25\x1F\xFF\x28\x25\x20\xFF" + "\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x27\x26\x1F\xFF\x27\x25\x1E\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x20\xFF" + "\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1E\xFF\x25\x22\x1D\xFF\x26\x23\x1E\xFF\x23\x21\x1C\xFF" + "\x32\x51\x3F\xFF\x29\x37\x2C\xFF\x29\x31\x28\xFF\x27\x2C\x24\xFF\x26\x29\x21\xFF\x28\x28\x22\xFF\x27\x27\x20\xFF\x27\x27\x21\xFF" + "\x29\x2F\x25\xFF\x3C\x73\x56\xFF\x55\xC9\x94\xFF\x56\xCC\x96\xFF\x56\xCB\x97\xFF\x54\xC5\x92\xFF\x52\xBD\x8C\xFF\x50\xB5\x86\xFF" + "\x4D\xAA\x7F\xFF\x48\x99\x71\xFF\x40\x80\x61\xFF\x37\x66\x4D\xFF\x2F\x48\x38\xFF\x2C\x38\x2C\xFF\x2B\x33\x29\xFF\x2A\x2E\x26\xFF" + "\x27\x29\x23\xFF\x27\x27\x21\xFF\x27\x27\x21\xFF\x27\x26\x20\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x27\x26\x20\xFF\x28\x26\x20\xFF\x28\x25\x20\xFF\x28\x27\x20\xFF\x27\x25\x1F\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x28\x26\x20\xFF" + "\x25\x23\x1E\xFF\x26\x24\x1E\xFF\x25\x23\x1D\xFF\x27\x25\x1F\xFF\x26\x24\x1E\xFF\x26\x23\x1E\xFF\x29\x26\x21\xFF\x28\x25\x20\xFF" + "\x26\x25\x1F\xFF\x27\x25\x1F\xFF\x25\x25\x1F\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x25\x1F\xFF\x25\x23\x1D\xFF\x27\x24\x1F\xFF" + "\x25\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x27\x24\x1E\xFF\x26\x26\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x4E\xBA\x89\xFF\x4D\xB3\x83\xFF\x45\x97\x70\xFF\x3D\x79\x5B\xFF\x35\x58\x44\xFF\x30\x48\x39\xFF\x2D\x3A\x2F\xFF\x29\x31\x27\xFF" + "\x2B\x37\x2C\xFF\x3E\x79\x5B\xFF\x56\xCA\x95\xFF\x56\xCC\x96\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x52\xBC\x8B\xFF\x49\x9D\x76\xFF\x3E\x7A\x5C\xFF" + "\x36\x5C\x47\xFF\x31\x4C\x3B\xFF\x2D\x3D\x2F\xFF\x29\x30\x28\xFF\x27\x2B\x23\xFF\x27\x29\x22\xFF\x27\x28\x21\xFF\x28\x27\x21\xFF" + "\x27\x25\x20\xFF\x27\x24\x1F\xFF\x24\x24\x1E\xFF\x25\x23\x1F\xFF\x28\x25\x20\xFF\x29\x26\x20\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF" + "\x26\x23\x1F\xFF\x27\x24\x1F\xFF\x29\x27\x21\xFF\x2A\x27\x21\xFF\x27\x24\x1F\xFF\x25\x24\x1E\xFF\x24\x24\x1F\xFF\x26\x24\x1F\xFF" + "\x28\x25\x1F\xFF\x27\x25\x1E\xFF\x27\x25\x20\xFF\x25\x25\x1F\xFF\x29\x26\x21\xFF\x27\x27\x20\xFF\x26\x25\x1F\xFF\x25\x23\x1E\xFF" + "\x26\x23\x1E\xFF\x23\x23\x1D\xFF\x26\x25\x1E\xFF\x27\x24\x1E\xFF\x25\x22\x1E\xFF\x27\x24\x1F\xFF\x29\x26\x21\xFF\x26\x26\x1E\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x50\xC0\x8E\xFF\x51\xC2\x8F\xFF\x52\xBC\x8B\xFF\x4A\xA7\x7C\xFF\x44\x90\x6B\xFF\x3E\x78\x5B\xFF" + "\x36\x63\x4B\xFF\x41\x87\x66\xFF\x55\xCB\x96\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x53\xC2\x90\xFF\x4D\xAD\x81\xFF\x47\x96\x70\xFF\x40\x7E\x5F\xFF\x39\x63\x4C\xFF\x31\x48\x39\xFF\x2D\x36\x2B\xFF\x27\x2C\x24\xFF" + "\x25\x27\x21\xFF\x27\x27\x21\xFF\x26\x24\x1E\xFF\x25\x24\x1E\xFF\x28\x25\x1F\xFF\x26\x24\x1E\xFF\x25\x25\x20\xFF\x26\x24\x1F\xFF" + "\x25\x22\x1E\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x29\x26\x21\xFF\x25\x23\x1D\xFF\x25\x23\x1D\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x2A\x27\x21\xFF\x29\x26\x21\xFF\x28\x25\x20\xFF\x26\x26\x20\xFF\x29\x26\x21\xFF\x29\x26\x21\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x25\x23\x1E\xFF\x24\x22\x1E\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x25\x24\x1F\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x50\xC0\x8E\xFF\x51\xC2\x8F\xFF\x53\xC5\x91\xFF\x54\xC7\x92\xFF\x55\xC8\x95\xFF\x53\xC3\x92\xFF" + "\x50\xBA\x89\xFF\x51\xBD\x8C\xFF\x56\xCC\x97\xFF\x57\xCC\x97\xFF\x57\xCD\x99\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x58\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x55\xC8\x95\xFF\x52\xBD\x8D\xFF\x4E\xB0\x82\xFF\x47\x93\x6E\xFF\x38\x65\x4C\xFF" + "\x2D\x3A\x2F\xFF\x2C\x33\x2A\xFF\x26\x2A\x23\xFF\x27\x28\x20\xFF\x28\x27\x21\xFF\x27\x24\x1F\xFF\x26\x23\x1F\xFF\x28\x25\x20\xFF" + "\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x24\x23\x1E\xFF\x26\x24\x1D\xFF\x24\x24\x1D\xFF\x24\x22\x1D\xFF\x24\x21\x1D\xFF\x27\x24\x1F\xFF" + "\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x27\x26\x20\xFF\x27\x25\x1E\xFF\x25\x25\x1E\xFF\x26\x23\x1E\xFF" + "\x26\x23\x1E\xFF\x27\x25\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x20\xFF\x27\x26\x20\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x24\x24\x1E\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x52\xC4\x91\xFF\x54\xC7\x92\xFF\x55\xC8\x95\xFF\x54\xC8\x94\xFF" + "\x55\xCB\x96\xFF\x55\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCC\x97\xFF\x57\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x52\xBE\x8D\xFF\x45\x90\x6C\xFF\x38\x62\x4B\xFF\x2F\x42\x34\xFF\x29\x30\x27\xFF\x27\x28\x22\xFF\x28\x27\x21\xFF\x25\x22\x1E\xFF" + "\x23\x23\x1D\xFF\x25\x24\x1F\xFF\x28\x25\x20\xFF\x26\x25\x1F\xFF\x27\x25\x1F\xFF\x27\x25\x1E\xFF\x25\x24\x1E\xFF\x27\x24\x20\xFF" + "\x27\x25\x1F\xFF\x26\x25\x1F\xFF\x25\x24\x1E\xFF\x28\x26\x20\xFF\x25\x24\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x27\x23\x1F\xFF" + "\x27\x25\x1E\xFF\x26\x25\x1F\xFF\x2A\x27\x21\xFF\x2C\x29\x24\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x29\x26\x20\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x53\xC7\x93\xFF\x54\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x55\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x56\xCD\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCC\x97\xFF" + "\x56\xCD\x97\xFF\x56\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x56\xCC\x98\xFF\x52\xC0\x8E\xFF\x49\x9D\x76\xFF\x3A\x6A\x51\xFF\x2E\x3F\x31\xFF\x29\x2E\x24\xFF\x28\x27\x21\xFF" + "\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1E\xFF\x25\x24\x1E\xFF\x25\x25\x1F\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x27\x25\x20\xFF\x27\x24\x1E\xFF\x27\x25\x20\xFF\x27\x27\x21\xFF\x25\x24\x1E\xFF\x26\x25\x1F\xFF\x27\x25\x1F\xFF\x25\x24\x1E\xFF" + "\x25\x25\x1F\xFF\x28\x25\x20\xFF\x28\x26\x20\xFF\x28\x25\x20\xFF\x25\x23\x1D\xFF\x25\x23\x1E\xFF\x26\x25\x1F\xFF\x27\x26\x20\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x52\xC4\x91\xFF\x53\xC7\x93\xFF\x54\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x55\xCB\x95\xFF\x55\xCA\x95\xFF\x56\xCC\x96\xFF\x56\xCB\x96\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x58\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x96\xFF\x56\xCC\x97\xFF" + "\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x52\xC1\x8E\xFF\x4C\xA8\x7C\xFF\x45\x92\x6D\xFF\x42\x86\x64\xFF\x47\x99\x72\xFF" + "\x51\xB9\x89\xFF\x56\xCB\x96\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x53\xBF\x8E\xFF\x45\x8F\x6B\xFF\x30\x47\x38\xFF\x28\x2E\x25\xFF" + "\x27\x26\x1E\xFF\x27\x25\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x29\x26\x20\xFF\x28\x25\x1F\xFF\x26\x25\x1F\xFF" + "\x25\x24\x1F\xFF\x26\x25\x1F\xFF\x28\x25\x1F\xFF\x26\x24\x1E\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x29\x26\x21\xFF\x26\x24\x1F\xFF" + "\x27\x25\x1F\xFF\x29\x27\x20\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x26\x24\x1D\xFF\x26\x23\x1E\xFF\x25\x23\x1D\xFF\x26\x24\x1E\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x52\xC4\x91\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x55\xCA\x95\xFF\x55\xCA\x95\xFF\x56\xCC\x96\xFF\x56\xCB\x96\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x99\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x56\xCD\x98\xFF" + "\x56\xCA\x96\xFF\x50\xB6\x87\xFF\x42\x83\x62\xFF\x30\x48\x39\xFF\x2A\x3B\x2E\xFF\x2B\x39\x2E\xFF\x2D\x3B\x2F\xFF\x2D\x3B\x2F\xFF" + "\x2E\x40\x32\xFF\x35\x58\x45\xFF\x48\x9B\x74\xFF\x54\xC5\x92\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x4C\xA9\x7D\xFF\x34\x52\x3E\xFF" + "\x27\x2B\x22\xFF\x27\x26\x20\xFF\x29\x26\x21\xFF\x2A\x27\x22\xFF\x28\x25\x1E\xFF\x26\x23\x1E\xFF\x29\x26\x21\xFF\x26\x24\x1E\xFF" + "\x25\x24\x1F\xFF\x26\x25\x1F\xFF\x28\x25\x20\xFF\x25\x23\x1E\xFF\x27\x25\x20\xFF\x25\x23\x1E\xFF\x25\x22\x1D\xFF\x25\x23\x1E\xFF" + "\x27\x25\x1F\xFF\x25\x24\x1E\xFF\x27\x24\x1E\xFF\x25\x24\x1F\xFF\x26\x24\x1E\xFF\x25\x22\x1E\xFF\x25\x23\x1D\xFF\x26\x25\x1E\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCD\x98\xFF" + "\x4C\xA8\x7D\xFF\x37\x5D\x47\xFF\x2D\x3D\x31\xFF\x2E\x3D\x30\xFF\x34\x5F\x49\xFF\x3B\x71\x55\xFF\x3D\x79\x5B\xFF\x3A\x6C\x52\xFF" + "\x33\x50\x3D\xFF\x2F\x3D\x30\xFF\x2F\x47\x37\xFF\x3E\x7A\x5D\xFF\x54\xC5\x92\xFF\x56\xCD\x98\xFF\x56\xCC\x97\xFF\x48\x98\x72\xFF" + "\x2C\x37\x2C\xFF\x29\x2A\x23\xFF\x27\x26\x1F\xFF\x25\x24\x1E\xFF\x27\x25\x20\xFF\x26\x25\x1F\xFF\x29\x27\x21\xFF\x25\x25\x1F\xFF" + "\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x28\x26\x21\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF\x24\x23\x1D\xFF\x25\x22\x1D\xFF" + "\x25\x23\x1E\xFF\x24\x23\x1E\xFF\x29\x26\x21\xFF\x28\x26\x20\xFF\x25\x24\x1F\xFF\x25\x24\x1F\xFF\x25\x24\x1E\xFF\x24\x23\x1D\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x53\xC3\x91\xFF" + "\x35\x59\x45\xFF\x2D\x3B\x2F\xFF\x32\x50\x3E\xFF\x46\x92\x6D\xFF\x51\xBB\x8B\xFF\x54\xC4\x91\xFF\x55\xC7\x93\xFF\x53\xC1\x8F\xFF" + "\x4E\xAE\x81\xFF\x3F\x75\x59\xFF\x2D\x40\x32\xFF\x2D\x3D\x31\xFF\x43\x8C\x68\xFF\x56\xCC\x97\xFF\x56\xCD\x97\xFF\x56\xC8\x94\xFF" + "\x39\x65\x4D\xFF\x2A\x30\x26\xFF\x28\x28\x22\xFF\x27\x25\x1E\xFF\x2A\x27\x22\xFF\x28\x26\x21\xFF\x29\x26\x21\xFF\x26\x25\x1E\xFF" + "\x27\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x26\x24\x1E\xFF\x26\x25\x20\xFF\x27\x25\x1E\xFF\x28\x25\x1F\xFF" + "\x27\x24\x20\xFF\x26\x23\x1E\xFF\x25\x24\x1E\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x97\xFF\x55\xC7\x93\xFF\x3B\x70\x56\xFF" + "\x2E\x3B\x2F\xFF\x36\x59\x44\xFF\x4E\xAE\x81\xFF\x57\xCC\x97\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x55\xC9\x94\xFF\x54\xC4\x91\xFF" + "\x56\xCB\x96\xFF\x56\xCA\x96\xFF\x43\x8A\x69\xFF\x2E\x41\x33\xFF\x31\x4C\x3A\xFF\x4A\xA1\x79\xFF\x56\xCC\x97\xFF\x57\xCD\x97\xFF" + "\x4E\xB0\x81\xFF\x2F\x45\x37\xFF\x28\x2B\x24\xFF\x25\x23\x1E\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x28\x26\x20\xFF\x27\x24\x20\xFF" + "\x26\x23\x1E\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x25\x1F\xFF\x26\x24\x1E\xFF\x26\x23\x1E\xFF" + "\x28\x26\x21\xFF\x25\x25\x1E\xFF\x26\x23\x1E\xFF\x26\x24\x20\xFF\x27\x24\x1F\xFF\x27\x24\x20\xFF\x26\x23\x1F\xFF\x27\x24\x1F\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x4E\xAF\x82\xFF\x32\x50\x3E\xFF" + "\x2F\x42\x35\xFF\x45\x90\x6C\xFF\x56\xCA\x96\xFF\x57\xCD\x98\xFF\x56\xCB\x96\xFF\x4E\xB0\x82\xFF\x42\x84\x63\xFF\x3D\x74\x57\xFF" + "\x44\x8B\x69\xFF\x50\xB7\x88\xFF\x53\xC0\x8F\xFF\x36\x64\x4C\xFF\x2D\x3C\x2F\xFF\x3E\x76\x59\xFF\x54\xC7\x92\xFF\x57\xCD\x97\xFF" + "\x54\xC3\x91\xFF\x3D\x74\x58\xFF\x2A\x31\x28\xFF\x27\x26\x1F\xFF\x27\x25\x20\xFF\x26\x25\x1F\xFF\x27\x26\x20\xFF\x27\x26\x20\xFF" + "\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x27\x25\x1F\xFF\x26\x25\x20\xFF\x27\x24\x1F\xFF\x27\x26\x1F\xFF\x27\x26\x1F\xFF\x23\x23\x1D\xFF" + "\x25\x23\x1E\xFF\x27\x26\x1F\xFF\x27\x24\x1E\xFF\x29\x26\x20\xFF\x26\x25\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x49\x98\x72\xFF\x2E\x44\x36\xFF" + "\x33\x51\x3F\xFF\x4F\xB4\x86\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x53\xC0\x8F\xFF\x3D\x73\x57\xFF\x2F\x3E\x32\xFF\x2C\x38\x2C\xFF" + "\x2F\x42\x35\xFF\x41\x83\x62\xFF\x55\xC8\x94\xFF\x43\x8F\x6A\xFF\x2E\x3A\x2F\xFF\x39\x66\x4E\xFF\x52\xBB\x8B\xFF\x56\xCD\x97\xFF" + "\x56\xCB\x97\xFF\x48\x9C\x75\xFF\x2E\x42\x34\xFF\x28\x29\x22\xFF\x28\x27\x21\xFF\x28\x26\x1F\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF" + "\x28\x26\x20\xFF\x2A\x27\x21\xFF\x27\x26\x20\xFF\x25\x23\x1E\xFF\x29\x26\x21\xFF\x29\x28\x22\xFF\x26\x25\x1F\xFF\x25\x22\x1D\xFF" + "\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x27\x24\x1E\xFF\x27\x26\x1F\xFF\x27\x25\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCD\x97\xFF\x57\xCD\x98\xFF\x41\x80\x61\xFF\x2B\x39\x2E\xFF" + "\x33\x58\x44\xFF\x54\xC7\x93\xFF\x56\xCD\x98\xFF\x56\xCC\x97\xFF\x55\xC9\x94\xFF\x46\x96\x70\xFF\x34\x5B\x46\xFF\x32\x51\x3E\xFF" + "\x38\x67\x4D\xFF\x49\xA3\x79\xFF\x56\xCD\x97\xFF\x4C\xAC\x80\xFF\x2F\x3D\x31\xFF\x36\x58\x44\xFF\x4E\xAF\x82\xFF\x56\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x52\xBF\x8E\xFF\x39\x69\x50\xFF\x2A\x30\x26\xFF\x26\x25\x1F\xFF\x26\x23\x1E\xFF\x27\x25\x1E\xFF\x28\x25\x20\xFF" + "\x28\x26\x21\xFF\x28\x25\x20\xFF\x26\x25\x1E\xFF\x27\x25\x1F\xFF\x28\x26\x21\xFF\x28\x26\x20\xFF\x28\x26\x1F\xFF\x25\x24\x1D\xFF" + "\x25\x24\x1E\xFF\x25\x25\x1F\xFF\x24\x23\x1D\xFF\x26\x24\x1F\xFF\x25\x22\x1D\xFF\x23\x23\x1D\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x56\xCC\x98\xFF\x56\xCC\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x44\x8C\x69\xFF\x2D\x40\x31\xFF" + "\x33\x55\x41\xFF\x52\xBE\x8D\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x55\xC9\x95\xFF\x50\xBA\x8A\xFF\x4D\xAD\x80\xFF" + "\x52\xC0\x8D\xFF\x56\xCC\x97\xFF\x57\xCD\x97\xFF\x4A\xA5\x7A\xFF\x2E\x3B\x2F\xFF\x36\x5A\x45\xFF\x4F\xB1\x83\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x42\x8A\x68\xFF\x2B\x3B\x2E\xFF\x28\x28\x20\xFF\x27\x24\x1E\xFF\x28\x25\x1F\xFF\x28\x25\x1F\xFF" + "\x24\x23\x1E\xFF\x25\x24\x1E\xFF\x27\x25\x1F\xFF\x28\x26\x20\xFF\x26\x25\x1F\xFF\x27\x26\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF" + "\x27\x25\x1F\xFF\x26\x24\x1E\xFF\x25\x24\x1D\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x99\xFF\x56\xCD\x97\xFF\x56\xCD\x98\xFF\x49\x9E\x77\xFF\x31\x47\x38\xFF" + "\x2F\x4A\x39\xFF\x4B\xA9\x7E\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x58\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x57\xCE\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x40\x80\x61\xFF\x2E\x3B\x2F\xFF\x38\x6A\x50\xFF\x52\xBF\x8E\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x4D\xAC\x80\xFF\x2F\x4B\x3A\xFF\x28\x2A\x21\xFF\x27\x26\x1F\xFF\x28\x25\x1F\xFF\x29\x26\x21\xFF" + "\x26\x23\x1F\xFF\x27\x26\x20\xFF\x27\x25\x1F\xFF\x27\x25\x20\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x28\x25\x20\xFF" + "\x27\x25\x20\xFF\x26\x25\x1F\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x26\x24\x1E\xFF\x24\x23\x1D\xFF\x27\x24\x1F\xFF\x24\x22\x1D\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCA\x96\xFF\x53\xC1\x8F\xFF\x57\xC7\x94\xFF\x57\xCC\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x56\xCC\x97\xFF\x56\xCD\x97\xFF\x51\xBA\x89\xFF\x37\x5E\x47\xFF" + "\x2D\x39\x2E\xFF\x39\x6B\x52\xFF\x52\xBE\x8D\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCB\x97\xFF\x4C\xA8\x7C\xFF\x31\x4D\x3B\xFF\x2F\x3F\x31\xFF\x41\x86\x65\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF" + "\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xC9\x96\xFF\x39\x63\x4D\xFF\x27\x2D\x26\xFF\x26\x27\x1F\xFF\x27\x26\x20\xFF\x24\x23\x1D\xFF" + "\x24\x22\x1D\xFF\x26\x24\x1D\xFF\x27\x26\x20\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x26\x25\x1F\xFF\x26\x23\x1F\xFF\x25\x23\x1E\xFF" + "\x25\x24\x1F\xFF\x26\x23\x1E\xFF\x28\x26\x20\xFF\x28\x26\x21\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x26\x23\x1E\xFF\x27\x24\x20\xFF" + "\x4F\xBA\x8A\xFF\x50\xBD\x8C\xFF\x51\xC0\x8F\xFF\x52\xC2\x90\xFF\x53\xC4\x92\xFF\x54\xC6\x93\xFF\x54\xC8\x94\xFF\x55\xC8\x95\xFF" + "\x55\xCA\x96\xFF\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x55\xC8\x94\xFF\x49\x9E\x76\xFF\x43\x8B\x68\xFF\x50\xB5\x87\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCD\x97\xFF\x48\x9A\x72\xFF" + "\x2C\x3B\x2F\xFF\x30\x42\x34\xFF\x3F\x7C\x5D\xFF\x51\xBB\x8B\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x56\xC9\x95\xFF\x4C\xA9\x7D\xFF\x37\x5F\x49\xFF\x2C\x3A\x2E\xFF\x36\x5D\x47\xFF\x50\xB4\x86\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCC\x99\xFF\x57\xCC\x98\xFF\x57\xCD\x98\xFF\x44\x8C\x69\xFF\x29\x30\x27\xFF\x27\x27\x20\xFF\x27\x26\x1F\xFF\x25\x23\x1E\xFF" + "\x23\x22\x1C\xFF\x26\x23\x1E\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x27\x25\x1F\xFF\x26\x23\x1D\xFF" + "\x27\x24\x1F\xFF\x26\x24\x1E\xFF\x26\x25\x1F\xFF\x25\x23\x1E\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC4\x92\xFF\x54\xC6\x93\xFF\x54\xC8\x94\xFF\x55\xC9\x95\xFF" + "\x55\xCA\x96\xFF\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x4C\xAA\x7F\xFF\x35\x5E\x49\xFF\x35\x59\x45\xFF" + "\x46\x99\x72\xFF\x55\xC8\x94\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x55\xC8\x94\xFF" + "\x3B\x6C\x51\xFF\x2C\x3A\x2E\xFF\x2E\x3F\x31\xFF\x38\x5F\x49\xFF\x46\x94\x6F\xFF\x4E\xB1\x83\xFF\x50\xB9\x88\xFF\x4C\xAA\x7F\xFF" + "\x41\x80\x61\xFF\x32\x50\x3E\xFF\x2C\x3A\x2E\xFF\x2B\x40\x32\xFF\x4B\xA7\x7C\xFF\x57\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x4F\xB6\x86\xFF\x2D\x38\x2D\xFF\x29\x2A\x23\xFF\x27\x25\x1F\xFF\x26\x23\x1E\xFF" + "\x26\x24\x1F\xFF\x27\x26\x20\xFF\x29\x26\x21\xFF\x27\x25\x20\xFF\x28\x26\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x22\x1D\xFF" + "\x25\x24\x1E\xFF\x25\x24\x1E\xFF\x25\x24\x1F\xFF\x24\x23\x1D\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF" + "\x4F\xBB\x8A\xFF\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x54\xC8\x94\xFF\x55\xC9\x95\xFF" + "\x55\xCA\x96\xFF\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x55\xC7\x94\xFF\x40\x7B\x5D\xFF\x2C\x3B\x2E\xFF" + "\x2B\x37\x2C\xFF\x35\x5B\x46\xFF\x48\x9E\x76\xFF\x52\xBF\x8D\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x54\xC4\x91\xFF\x44\x89\x67\xFF\x31\x4D\x3C\xFF\x29\x37\x2C\xFF\x2C\x39\x2D\xFF\x2C\x3A\x2E\xFF\x2D\x3C\x2E\xFF\x2C\x3B\x2E\xFF" + "\x2C\x39\x2D\xFF\x2E\x41\x32\xFF\x38\x60\x4B\xFF\x4B\xA2\x79\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCC\x97\xFF\x33\x52\x3F\xFF\x28\x2C\x23\xFF\x28\x27\x1F\xFF\x27\x24\x1F\xFF" + "\x23\x22\x1D\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF\x24\x23\x1E\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x26\x25\x1F\xFF\x26\x24\x1F\xFF" + "\x25\x22\x1D\xFF\x27\x24\x1F\xFF\x27\x26\x21\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x26\x23\x1F\xFF\x26\x23\x1F\xFF\x27\x24\x1F\xFF" + "\x4F\xBC\x8B\xFF\x51\xBF\x8D\xFF\x52\xC1\x8F\xFF\x53\xC4\x91\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x54\xC8\x94\xFF\x55\xC9\x95\xFF" + "\x55\xCA\x96\xFF\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x4A\xA4\x7A\xFF\x30\x4B\x39\xFF" + "\x2A\x30\x28\xFF\x2A\x32\x28\xFF\x2E\x40\x32\xFF\x38\x67\x4E\xFF\x48\x9B\x74\xFF\x53\xC0\x8F\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x55\xC8\x94\xFF\x4A\xA5\x7A\xFF\x3F\x7D\x5E\xFF\x37\x62\x4C\xFF\x31\x4C\x3A\xFF\x31\x49\x39\xFF\x35\x57\x42\xFF" + "\x3A\x6B\x52\xFF\x44\x8D\x6A\xFF\x50\xBB\x8B\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCA\x96\xFF\x4F\xAC\x80\xFF\x2F\x4A\x39\xFF\x28\x2A\x23\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF" + "\x25\x23\x1E\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x29\x26\x21\xFF\x28\x26\x20\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF" + "\x28\x25\x20\xFF\x26\x25\x1F\xFF\x25\x24\x1F\xFF\x27\x25\x1F\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x27\x25\x1F\xFF\x26\x25\x20\xFF" + "\x50\xBC\x8C\xFF\x51\xBF\x8D\xFF\x52\xC1\x8F\xFF\x53\xC4\x91\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x55\xCA\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x54\xC6\x93\xFF\x41\x82\x62\xFF" + "\x2D\x3C\x30\xFF\x2B\x34\x29\xFF\x29\x2F\x26\xFF\x27\x30\x27\xFF\x2F\x42\x34\xFF\x38\x63\x4C\xFF\x48\x98\x72\xFF\x55\xC6\x93\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x55\xC7\x94\xFF\x52\xBC\x8C\xFF\x4F\xB2\x84\xFF\x4E\xB0\x83\xFF\x53\xB7\x88\xFF" + "\x53\xC0\x8E\xFF\x55\xC9\x95\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCD\x97\xFF\x52\xBC\x8C\xFF\x40\x81\x62\xFF\x32\x4E\x3D\xFF\x29\x2C\x25\xFF\x29\x27\x21\xFF\x29\x27\x20\xFF\x27\x25\x1F\xFF" + "\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x26\x20\xFF\x26\x25\x20\xFF\x26\x25\x1F\xFF\x28\x26\x20\xFF\x27\x25\x20\xFF\x29\x26\x20\xFF" + "\x27\x25\x1F\xFF\x28\x25\x20\xFF\x25\x23\x1E\xFF\x26\x24\x1E\xFF\x26\x25\x20\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x27\x25\x1F\xFF" + "\x50\xBD\x8C\xFF\x51\xC0\x8E\xFF\x52\xC2\x90\xFF\x53\xC4\x91\xFF\x54\xC6\x93\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x55\xCA\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x56\xCC\x97\xFF" + "\x52\xBA\x8B\xFF\x42\x85\x64\xFF\x36\x59\x46\xFF\x2F\x40\x33\xFF\x2B\x31\x28\xFF\x2B\x31\x27\xFF\x2C\x36\x2B\xFF\x31\x4B\x3A\xFF" + "\x41\x82\x61\xFF\x4C\xA8\x7E\xFF\x52\xBC\x8C\xFF\x55\xC9\x95\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x97\xFF\x54\xC8\x93\xFF\x51\xB8\x89\xFF" + "\x3F\x82\x62\xFF\x2F\x47\x38\xFF\x2A\x34\x29\xFF\x26\x2B\x22\xFF\x27\x27\x21\xFF\x26\x25\x1F\xFF\x26\x26\x1F\xFF\x26\x25\x1E\xFF" + "\x27\x24\x20\xFF\x27\x24\x20\xFF\x28\x26\x20\xFF\x28\x26\x21\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF" + "\x26\x23\x1E\xFF\x27\x25\x20\xFF\x26\x25\x1F\xFF\x28\x27\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x26\x1F\xFF" + "\x50\xBE\x8D\xFF\x51\xC0\x8F\xFF\x52\xC3\x90\xFF\x53\xC4\x92\xFF\x54\xC6\x93\xFF\x54\xC8\x94\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF" + "\x55\xCA\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x56\xCB\x96\xFF\x50\xBA\x8A\xFF\x46\x94\x6F\xFF\x38\x64\x4B\xFF\x2D\x3F\x32\xFF\x29\x33\x29\xFF\x29\x32\x27\xFF" + "\x2A\x35\x29\xFF\x30\x43\x34\xFF\x38\x61\x4A\xFF\x40\x81\x61\xFF\x49\x9F\x78\xFF\x4E\xB0\x83\xFF\x52\xC0\x8E\xFF\x56\xCB\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xC9\x95\xFF\x4F\xB7\x86\xFF\x48\x9B\x74\xFF\x3F\x7E\x5F\xFF\x35\x5A\x44\xFF" + "\x2A\x36\x2B\xFF\x2B\x34\x2A\xFF\x2B\x37\x2C\xFF\x2D\x3D\x30\xFF\x28\x29\x21\xFF\x25\x25\x1E\xFF\x26\x25\x20\xFF\x26\x26\x1E\xFF" + "\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x24\x23\x1D\xFF\x28\x24\x1F\xFF\x25\x22\x1E\xFF" + "\x25\x22\x1E\xFF\x24\x25\x1F\xFF\x27\x25\x20\xFF\x29\x27\x21\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x26\x25\x1E\xFF" + "\x51\xBF\x8D\xFF\x52\xC1\x8F\xFF\x52\xC3\x90\xFF\x53\xC5\x92\xFF\x54\xC6\x93\xFF\x54\xC8\x94\xFF\x55\xC8\x95\xFF\x55\xCA\x96\xFF" + "\x55\xCA\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x51\xBD\x8C\xFF\x49\xA2\x79\xFF\x3D\x72\x57\xFF\x31\x47\x37\xFF" + "\x2D\x36\x2C\xFF\x2B\x34\x29\xFF\x2A\x33\x28\xFF\x29\x34\x29\xFF\x2F\x43\x35\xFF\x31\x4D\x3C\xFF\x35\x5A\x45\xFF\x3A\x67\x50\xFF" + "\x3D\x73\x57\xFF\x3B\x6B\x52\xFF\x39\x66\x4D\xFF\x37\x61\x4A\xFF\x33\x53\x3F\xFF\x2E\x42\x34\xFF\x29\x35\x2A\xFF\x2A\x34\x2A\xFF" + "\x2B\x3B\x2F\xFF\x37\x5F\x49\xFF\x46\x95\x6F\xFF\x47\x97\x70\xFF\x28\x2E\x25\xFF\x25\x25\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF" + "\x28\x25\x20\xFF\x26\x25\x1F\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x25\x23\x1E\xFF" + "\x26\x23\x1E\xFF\x24\x24\x1E\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x25\x22\x1D\xFF\x28\x25\x1F\xFF\x26\x25\x1F\xFF\x25\x25\x1F\xFF" + "\x51\xC0\x8E\xFF\x52\xC2\x90\xFF\x53\xC4\x91\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x54\xC8\x94\xFF\x55\xC9\x95\xFF\x55\xCA\x96\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCD\x98\xFF\x57\xCC\x97\xFF\x57\xCD\x98\xFF\x54\xC7\x94\xFF" + "\x4A\x9E\x77\xFF\x3E\x75\x58\xFF\x35\x57\x42\xFF\x2E\x40\x32\xFF\x28\x33\x28\xFF\x29\x33\x28\xFF\x28\x32\x27\xFF\x29\x32\x28\xFF" + "\x29\x31\x27\xFF\x2A\x32\x2A\xFF\x2C\x34\x2A\xFF\x2B\x34\x2A\xFF\x2B\x37\x2C\xFF\x2F\x42\x35\xFF\x35\x55\x42\xFF\x3E\x74\x58\xFF" + "\x4E\xB2\x85\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x45\x90\x6B\xFF\x2B\x32\x28\xFF\x25\x25\x20\xFF\x27\x24\x20\xFF\x29\x25\x20\xFF" + "\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x27\x26\x1F\xFF\x25\x23\x1D\xFF\x25\x25\x1F\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF" + "\x26\x24\x1F\xFF\x22\x21\x1C\xFF\x25\x21\x1D\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF\x27\x25\x1F\xFF\x25\x25\x1F\xFF\x27\x26\x20\xFF" + "\x51\xC0\x8F\xFF\x52\xC2\x90\xFF\x53\xC4\x92\xFF\x54\xC6\x93\xFF\x54\xC7\x93\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF\x55\xCA\x96\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x56\xCA\x96\xFF\x50\xB8\x89\xFF\x48\x9C\x74\xFF\x41\x83\x62\xFF\x3E\x76\x59\xFF\x38\x66\x4D\xFF\x34\x56\x42\xFF" + "\x31\x4A\x38\xFF\x33\x51\x3F\xFF\x38\x5E\x48\xFF\x3B\x6B\x51\xFF\x41\x83\x62\xFF\x48\x9B\x74\xFF\x4F\xB5\x86\xFF\x55\xC9\x95\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x55\xC5\x92\xFF\x38\x66\x4E\xFF\x2A\x2E\x26\xFF\x28\x26\x21\xFF\x28\x25\x20\xFF\x27\x25\x1F\xFF" + "\x25\x25\x1E\xFF\x26\x25\x1F\xFF\x26\x25\x20\xFF\x22\x23\x1D\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x26\x24\x1F\xFF" + "\x25\x23\x1D\xFF\x25\x22\x1D\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF\x27\x26\x20\xFF\x27\x27\x1F\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF" + "\x52\xC1\x8F\xFF\x52\xC3\x90\xFF\x53\xC4\x92\xFF\x54\xC6\x93\xFF\x54\xC8\x94\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF\x55\xCA\x96\xFF" + "\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCB\x96\xFF\x54\xC5\x92\xFF\x52\xBE\x8D\xFF\x51\xB7\x88\xFF" + "\x4F\xB2\x84\xFF\x4F\xB5\x87\xFF\x52\xBB\x8B\xFF\x53\xC1\x8F\xFF\x55\xCA\x95\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x49\x9C\x75\xFF\x2F\x44\x35\xFF\x26\x27\x23\xFF\x26\x25\x1E\xFF\x29\x26\x20\xFF\x26\x26\x1E\xFF" + "\x26\x22\x1D\xFF\x26\x23\x1F\xFF\x27\x25\x20\xFF\x27\x24\x20\xFF\x27\x24\x1F\xFF\x25\x23\x1D\xFF\x26\x25\x1E\xFF\x28\x25\x20\xFF" + "\x26\x24\x1D\xFF\x27\x24\x1F\xFF\x27\x25\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x1E\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF" + "\x52\xC2\x90\xFF\x53\xC4\x91\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x54\xC8\x94\xFF\x55\xC9\x95\xFF\x55\xCA\x96\xFF\x55\xCA\x96\xFF" + "\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF" + "\x51\xBC\x8B\xFF\x43\x8C\x69\xFF\x30\x48\x39\xFF\x2A\x2D\x27\xFF\x24\x25\x20\xFF\x24\x24\x1E\xFF\x25\x24\x1E\xFF\x25\x22\x1D\xFF" + "\x27\x25\x1F\xFF\x28\x26\x20\xFF\x25\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x25\x24\x1E\xFF" + "\x25\x22\x1D\xFF\x27\x25\x1F\xFF\x27\x26\x20\xFF\x26\x24\x1E\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x29\x26\x21\xFF\x26\x26\x20\xFF" + "\x52\xC3\x90\xFF\x53\xC4\x92\xFF\x54\xC6\x93\xFF\x54\xC8\x94\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF\x55\xCA\x96\xFF\x56\xCB\x96\xFF" + "\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x52\xBE\x8D\xFF\x48\x9A\x73\xFF" + "\x38\x63\x4D\xFF\x2C\x39\x2E\xFF\x27\x2B\x24\xFF\x27\x26\x21\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x25\x24\x1D\xFF\x25\x26\x1D\xFF" + "\x25\x24\x1F\xFF\x27\x25\x1E\xFF\x26\x25\x1E\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x26\x25\x20\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF" + "\x27\x25\x1F\xFF\x26\x25\x1E\xFF\x25\x24\x1D\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x27\x24\x20\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF" + "\x53\xC4\x91\xFF\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x54\xC8\x94\xFF\x55\xC8\x95\xFF\x55\xC9\x95\xFF\x55\xCA\x96\xFF\x56\xCB\x96\xFF" + "\x56\xCB\x96\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x58\xCC\x99\xFF\x57\xCD\x98\xFF\x57\xCD\x99\xFF\x57\xCD\x98\xFF\x52\xBE\x8D\xFF\x43\x8C\x68\xFF\x35\x5D\x47\xFF\x2E\x40\x32\xFF" + "\x27\x2C\x26\xFF\x28\x2A\x23\xFF\x29\x29\x22\xFF\x27\x26\x20\xFF\x23\x22\x1C\xFF\x25\x23\x1D\xFF\x26\x24\x1E\xFF\x27\x25\x20\xFF" + "\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x26\x1E\xFF\x28\x25\x20\xFF\x26\x24\x1D\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x24\x23\x1E\xFF" + "\x25\x22\x1C\xFF\x25\x23\x1E\xFF\x25\x23\x1D\xFF\x25\x24\x1E\xFF\x26\x23\x1F\xFF\x28\x25\x20\xFF\x27\x24\x1E\xFF\x27\x24\x1E\xFF" + "\x53\xC4\x92\xFF\x54\xC6\x93\xFF\x54\xC7\x92\xFF\x55\xC8\x94\xFF\x55\xC9\x95\xFF\x54\xC9\x95\xFF\x54\xC9\x95\xFF\x55\xCA\x95\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x56\xCD\x99\xFF\x56\xCD\x97\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCE\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x99\xFF" + "\x56\xCD\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x58\xCD\x98\xFF\x56\xCD\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x54\xC7\x93\xFF" + "\x51\xBC\x8B\xFF\x4E\xAF\x83\xFF\x46\x92\x6D\xFF\x39\x67\x4E\xFF\x2D\x3B\x30\xFF\x2B\x32\x29\xFF\x29\x2D\x24\xFF\x28\x28\x22\xFF" + "\x27\x26\x20\xFF\x27\x25\x1F\xFF\x28\x26\x20\xFF\x28\x25\x20\xFF\x26\x25\x1F\xFF\x26\x26\x1F\xFF\x26\x25\x1E\xFF\x28\x25\x20\xFF" + "\x2A\x27\x22\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x26\x23\x1C\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF" + "\x26\x24\x1F\xFF\x28\x25\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x25\x23\x1E\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x25\x24\x1E\xFF" + "\x53\xC5\x92\xFF\x54\xC7\x93\xFF\x53\xC8\x93\xFF\x54\xC8\x94\xFF\x55\xC8\x94\xFF\x52\xBD\x8C\xFF\x4C\xAD\x80\xFF\x4B\xA7\x7D\xFF" + "\x48\xA1\x77\xFF\x48\xA0\x77\xFF\x4B\xA7\x7D\xFF\x4F\xB0\x83\xFF\x51\xB9\x8A\xFF\x52\xC0\x8E\xFF\x55\xC6\x92\xFF\x56\xCB\x96\xFF" + "\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x56\xCB\x97\xFF\x54\xC4\x91\xFF\x50\xBA\x8B\xFF\x4E\xAF\x81\xFF\x49\x9F\x77\xFF\x43\x8B\x68\xFF\x3F\x78\x5B\xFF" + "\x37\x61\x4A\xFF\x30\x48\x38\xFF\x2A\x35\x2B\xFF\x2A\x30\x27\xFF\x28\x2B\x22\xFF\x27\x27\x20\xFF\x28\x26\x20\xFF\x28\x25\x20\xFF" + "\x27\x24\x1F\xFF\x25\x24\x1D\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x27\x26\x20\xFF" + "\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x27\x24\x20\xFF\x28\x27\x20\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x20\xFF" + "\x26\x24\x1E\xFF\x27\x24\x1E\xFF\x25\x22\x1D\xFF\x26\x24\x1E\xFF\x26\x25\x1F\xFF\x27\x25\x1F\xFF\x27\x26\x1F\xFF\x26\x24\x1E\xFF" + "\x54\xC6\x92\xFF\x54\xC8\x94\xFF\x54\xC8\x94\xFF\x54\xC8\x94\xFF\x44\x8C\x69\xFF\x37\x5E\x49\xFF\x32\x4D\x3C\xFF\x31\x49\x38\xFF" + "\x2D\x42\x33\xFF\x2D\x44\x33\xFF\x2E\x47\x37\xFF\x32\x4E\x3C\xFF\x35\x54\x42\xFF\x34\x58\x43\xFF\x34\x5C\x45\xFF\x38\x63\x4C\xFF" + "\x3C\x6F\x55\xFF\x40\x7B\x5C\xFF\x43\x87\x65\xFF\x45\x91\x6D\xFF\x44\x8E\x6B\xFF\x43\x8A\x68\xFF\x43\x86\x65\xFF\x41\x81\x61\xFF" + "\x3D\x75\x59\xFF\x38\x67\x4E\xFF\x36\x5C\x47\xFF\x33\x55\x42\xFF\x33\x4E\x3C\xFF\x2F\x41\x34\xFF\x2A\x34\x2A\xFF\x2B\x31\x27\xFF" + "\x27\x2C\x23\xFF\x29\x2B\x23\xFF\x28\x29\x22\xFF\x26\x25\x20\xFF\x29\x28\x22\xFF\x27\x26\x20\xFF\x27\x25\x20\xFF\x27\x24\x20\xFF" + "\x29\x26\x21\xFF\x27\x25\x1F\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x25\x25\x1F\xFF\x26\x25\x1E\xFF\x28\x26\x21\xFF" + "\x27\x25\x20\xFF\x25\x25\x1E\xFF\x25\x24\x1F\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x26\x24\x20\xFF\x25\x24\x1F\xFF" + "\x26\x25\x1F\xFF\x26\x23\x1E\xFF\x23\x21\x1C\xFF\x25\x23\x1C\xFF\x28\x25\x20\xFF\x26\x25\x1F\xFF\x28\x27\x1F\xFF\x27\x25\x1F\xFF" + "\x53\xC7\x92\xFF\x53\xC8\x94\xFF\x53\xC3\x91\xFF\x3E\x7B\x5D\xFF\x2B\x35\x2D\xFF\x25\x2B\x22\xFF\x23\x26\x1E\xFF\x25\x26\x20\xFF" + "\x24\x24\x1E\xFF\x27\x27\x1F\xFF\x28\x28\x22\xFF\x28\x29\x23\xFF\x27\x29\x23\xFF\x27\x29\x23\xFF\x2A\x2C\x25\xFF\x2A\x2D\x25\xFF" + "\x27\x2D\x25\xFF\x27\x2D\x25\xFF\x27\x2D\x24\xFF\x29\x30\x27\xFF\x29\x2F\x28\xFF\x28\x2F\x26\xFF\x29\x30\x27\xFF\x29\x2F\x26\xFF" + "\x28\x2C\x23\xFF\x2A\x2D\x24\xFF\x29\x2B\x23\xFF\x28\x29\x22\xFF\x27\x27\x21\xFF\x26\x26\x21\xFF\x27\x26\x1F\xFF\x29\x26\x21\xFF" + "\x26\x26\x20\xFF\x26\x24\x1F\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1E\xFF\x26\x25\x1F\xFF" + "\x28\x25\x20\xFF\x28\x25\x20\xFF\x29\x27\x21\xFF\x29\x26\x21\xFF\x27\x24\x20\xFF\x25\x24\x1F\xFF\x25\x24\x1F\xFF\x28\x25\x21\xFF" + "\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x29\x26\x20\xFF\x26\x25\x20\xFF\x29\x26\x21\xFF\x26\x23\x1F\xFF\x27\x26\x20\xFF\x27\x24\x1F\xFF" + "\x27\x24\x1F\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x26\x25\x1F\xFF\x25\x22\x1D\xFF\x25\x23\x1F\xFF\x26\x25\x20\xFF\x26\x25\x1F\xFF" + "\x53\xC8\x94\xFF\x54\xC8\x95\xFF\x49\xA4\x7A\xFF\x30\x49\x39\xFF\x26\x29\x23\xFF\x24\x25\x1F\xFF\x23\x22\x1D\xFF\x25\x22\x1D\xFF" + "\x25\x24\x1D\xFF\x28\x25\x1F\xFF\x28\x26\x20\xFF\x26\x25\x1F\xFF\x25\x24\x1F\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x25\x24\x1F\xFF" + "\x26\x25\x1E\xFF\x28\x26\x20\xFF\x28\x27\x20\xFF\x26\x25\x1F\xFF\x27\x26\x20\xFF\x28\x26\x21\xFF\x27\x26\x20\xFF\x26\x25\x20\xFF" + "\x27\x25\x20\xFF\x26\x25\x1F\xFF\x26\x26\x20\xFF\x27\x26\x1F\xFF\x25\x25\x20\xFF\x24\x23\x1D\xFF\x25\x24\x1D\xFF\x27\x25\x1F\xFF" + "\x25\x25\x1E\xFF\x26\x23\x1F\xFF\x27\x25\x1F\xFF\x27\x26\x21\xFF\x29\x27\x22\xFF\x27\x25\x20\xFF\x26\x24\x1D\xFF\x26\x25\x1E\xFF" + "\x27\x26\x20\xFF\x2A\x27\x21\xFF\x29\x27\x21\xFF\x27\x25\x1F\xFF\x25\x22\x1F\xFF\x24\x22\x1D\xFF\x25\x23\x1D\xFF\x28\x26\x21\xFF" + "\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x26\x24\x1F\xFF\x27\x26\x20\xFF\x26\x25\x1F\xFF" + "\x26\x23\x1F\xFF\x28\x25\x21\xFF\x28\x25\x20\xFF\x26\x25\x1E\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x26\x26\x1F\xFF\x26\x24\x1F\xFF" + "\x55\xC8\x94\xFF\x54\xC8\x94\xFF\x40\x80\x60\xFF\x29\x36\x2A\xFF\x26\x26\x20\xFF\x24\x23\x1D\xFF\x24\x22\x1D\xFF\x27\x23\x1F\xFF" + "\x25\x23\x1D\xFF\x26\x24\x1F\xFF\x27\x27\x20\xFF\x29\x27\x21\xFF\x26\x24\x1F\xFF\x24\x24\x1E\xFF\x26\x25\x1F\xFF\x26\x24\x1F\xFF" + "\x27\x24\x1F\xFF\x29\x27\x20\xFF\x29\x26\x20\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x26\x24\x1D\xFF" + "\x28\x25\x20\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x25\x24\x1F\xFF\x28\x25\x1F\xFF\x27\x25\x1E\xFF\x27\x24\x1F\xFF" + "\x27\x25\x1F\xFF\x27\x25\x1F\xFF\x25\x25\x1F\xFF\x25\x24\x1E\xFF\x26\x23\x1F\xFF\x27\x24\x20\xFF\x27\x25\x1F\xFF\x26\x24\x1F\xFF" + "\x25\x24\x1E\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF\x24\x23\x1E\xFF\x27\x24\x1F\xFF\x25\x25\x1F\xFF\x26\x24\x1F\xFF\x27\x25\x20\xFF" + "\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x27\x27\x20\xFF\x28\x27\x21\xFF\x24\x24\x1E\xFF\x27\x24\x1F\xFF\x27\x26\x20\xFF\x28\x26\x20\xFF" + "\x26\x24\x1F\xFF\x26\x26\x1F\xFF\x29\x27\x21\xFF\x26\x25\x1F\xFF\x25\x25\x1F\xFF\x26\x25\x1F\xFF\x26\x24\x1F\xFF\x26\x26\x1D\xFF" + "\x54\xC9\x94\xFF\x52\xC1\x8E\xFF\x3A\x6C\x52\xFF\x29\x30\x26\xFF\x25\x25\x1F\xFF\x24\x22\x1D\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x26\x23\x1E\xFF\x25\x24\x1F\xFF\x26\x25\x1F\xFF\x26\x26\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x28\x26\x20\xFF" + "\x29\x26\x22\xFF\x2A\x27\x21\xFF\x28\x26\x20\xFF\x29\x26\x20\xFF\x27\x24\x1F\xFF\x26\x24\x20\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF" + "\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x26\x20\xFF\x29\x26\x21\xFF\x28\x25\x21\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF" + "\x27\x24\x1F\xFF\x28\x26\x21\xFF\x25\x23\x1E\xFF\x23\x23\x1D\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x26\x25\x1E\xFF" + "\x23\x23\x1D\xFF\x25\x24\x1F\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x28\x25\x1F\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF" + "\x26\x25\x1F\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x24\x24\x1E\xFF\x24\x23\x1D\xFF\x26\x24\x1E\xFF\x27\x25\x20\xFF" + "\x27\x26\x20\xFF\x27\x25\x1F\xFF\x28\x25\x1F\xFF\x28\x25\x1E\xFF\x27\x24\x1F\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x55\xC9\x94\xFF\x52\xBE\x8C\xFF\x38\x68\x4F\xFF\x29\x2E\x25\xFF\x24\x24\x1D\xFF\x26\x23\x1F\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF" + "\x25\x23\x1D\xFF\x25\x24\x1E\xFF\x27\x24\x1E\xFF\x27\x24\x1E\xFF\x26\x24\x1F\xFF\x26\x25\x1F\xFF\x27\x25\x20\xFF\x26\x26\x20\xFF" + "\x29\x27\x21\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x27\x24\x20\xFF" + "\x27\x25\x1E\xFF\x26\x25\x1E\xFF\x27\x26\x20\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x27\x25\x1F\xFF\x28\x26\x20\xFF\x28\x26\x20\xFF" + "\x27\x25\x20\xFF\x27\x24\x1F\xFF\x24\x22\x1D\xFF\x25\x23\x1E\xFF\x28\x26\x20\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x26\x25\x20\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF\x26\x25\x1E\xFF" + "\x28\x25\x1F\xFF\x27\x26\x20\xFF\x28\x25\x20\xFF\x27\x25\x1F\xFF\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x25\x23\x1E\xFF\x26\x24\x1E\xFF\x26\x23\x1E\xFF\x26\x25\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x24\x24\x1E\xFF" + "\x55\xCA\x95\xFF\x51\xBB\x8A\xFF\x38\x66\x4D\xFF\x28\x2D\x25\xFF\x24\x22\x1C\xFF\x26\x23\x1D\xFF\x25\x23\x1D\xFF\x25\x24\x1E\xFF" + "\x26\x23\x1E\xFF\x25\x24\x1E\xFF\x25\x25\x1F\xFF\x27\x25\x1F\xFF\x29\x27\x20\xFF\x28\x26\x21\xFF\x27\x24\x1F\xFF\x24\x23\x1D\xFF" + "\x27\x24\x1F\xFF\x28\x25\x20\xFF\x28\x27\x21\xFF\x26\x24\x1F\xFF\x23\x22\x1C\xFF\x25\x24\x1E\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF" + "\x25\x25\x1E\xFF\x28\x27\x20\xFF\x27\x25\x1F\xFF\x26\x24\x20\xFF\x29\x26\x21\xFF\x28\x26\x20\xFF\x27\x26\x20\xFF\x2A\x28\x22\xFF" + "\x28\x25\x20\xFF\x28\x25\x20\xFF\x26\x25\x20\xFF\x27\x25\x20\xFF\x26\x23\x1E\xFF\x27\x25\x20\xFF\x27\x26\x1F\xFF\x27\x25\x1F\xFF" + "\x28\x25\x20\xFF\x26\x24\x1E\xFF\x26\x24\x1E\xFF\x24\x21\x1C\xFF\x23\x22\x1C\xFF\x25\x23\x1E\xFF\x25\x25\x1F\xFF\x27\x25\x1E\xFF" + "\x27\x26\x20\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x29\x27\x20\xFF\x27\x24\x20\xFF\x27\x26\x1F\xFF\x27\x25\x1F\xFF\x29\x26\x21\xFF" + "\x25\x24\x1E\xFF\x28\x25\x20\xFF\x26\x24\x1E\xFF\x24\x24\x1E\xFF\x26\x25\x1E\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x24\x23\x1E\xFF" + "\x56\xCB\x96\xFF\x53\xC0\x8F\xFF\x39\x6A\x50\xFF\x28\x2D\x25\xFF\x28\x25\x20\xFF\x28\x25\x1F\xFF\x28\x26\x1F\xFF\x27\x24\x1F\xFF" + "\x28\x25\x20\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x2A\x27\x21\xFF\x2A\x27\x22\xFF\x29\x26\x21\xFF\x29\x26\x21\xFF" + "\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x27\x25\x1F\xFF\x26\x25\x1F\xFF\x25\x23\x1E\xFF\x26\x25\x1F\xFF\x27\x25\x20\xFF\x28\x26\x1F\xFF" + "\x28\x26\x21\xFF\x26\x25\x20\xFF\x27\x25\x20\xFF\x28\x26\x21\xFF\x26\x24\x1F\xFF\x27\x26\x20\xFF\x28\x25\x20\xFF\x27\x25\x1F\xFF" + "\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF\x24\x23\x1D\xFF\x26\x23\x1E\xFF\x26\x25\x1F\xFF\x27\x24\x20\xFF" + "\x25\x25\x1F\xFF\x25\x23\x1E\xFF\x23\x23\x1D\xFF\x25\x22\x1D\xFF\x27\x25\x1D\xFF\x26\x25\x1E\xFF\x28\x27\x20\xFF\x27\x24\x1F\xFF" + "\x27\x24\x1F\xFF\x27\x26\x1F\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x29\x26\x21\xFF\x27\x24\x1F\xFF\x27\x26\x1F\xFF\x27\x25\x1F\xFF" + "\x28\x24\x1F\xFF\x26\x23\x1F\xFF\x26\x23\x1E\xFF\x25\x24\x1E\xFF\x26\x25\x1E\xFF\x25\x23\x1E\xFF\x26\x25\x1F\xFF\x26\x26\x20\xFF" + "\x56\xCB\x96\xFF\x55\xC6\x92\xFF\x3B\x71\x56\xFF\x27\x2F\x26\xFF\x26\x25\x1F\xFF\x24\x23\x1D\xFF\x26\x25\x1F\xFF\x27\x25\x20\xFF" + "\x24\x23\x1D\xFF\x26\x24\x1F\xFF\x27\x25\x20\xFF\x28\x26\x21\xFF\x25\x24\x1E\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF" + "\x29\x26\x20\xFF\x27\x24\x1F\xFF\x26\x24\x1E\xFF\x26\x23\x1E\xFF\x27\x25\x1F\xFF\x26\x25\x1E\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF" + "\x28\x26\x20\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x28\x26\x21\xFF\x25\x24\x1F\xFF\x28\x26\x21\xFF\x27\x24\x1F\xFF\x26\x25\x1F\xFF" + "\x26\x25\x1F\xFF\x25\x23\x1D\xFF\x26\x24\x1F\xFF\x27\x25\x1F\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x27\x25\x1F\xFF\x28\x26\x21\xFF" + "\x27\x25\x20\xFF\x25\x24\x1F\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x28\x26\x20\xFF\x26\x26\x1F\xFF\x26\x25\x1E\xFF\x28\x26\x1F\xFF" + "\x26\x22\x1E\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x27\x25\x20\xFF\x26\x23\x1E\xFF\x26\x24\x1E\xFF\x27\x25\x1F\xFF\x26\x25\x1E\xFF" + "\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x28\x25\x1F\xFF\x27\x25\x1F\xFF\x27\x24\x1E\xFF\x27\x25\x20\xFF\x26\x25\x1F\xFF\x25\x24\x1E\xFF" + "\x56\xCB\x96\xFF\x56\xCB\x96\xFF\x43\x87\x66\xFF\x2C\x39\x2D\xFF\x25\x25\x1D\xFF\x25\x22\x1D\xFF\x27\x24\x1F\xFF\x29\x26\x21\xFF" + "\x27\x25\x1F\xFF\x27\x24\x20\xFF\x28\x24\x20\xFF\x28\x27\x22\xFF\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x27\x24\x1F\xFF\x23\x20\x1B\xFF" + "\x23\x23\x1C\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x28\x26\x21\xFF\x27\x24\x1E\xFF\x26\x24\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x28\x25\x20\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x26\x25\x1F\xFF\x2A\x28\x21\xFF\x29\x26\x20\xFF\x27\x26\x20\xFF" + "\x29\x27\x21\xFF\x29\x27\x20\xFF\x29\x26\x21\xFF\x27\x25\x1F\xFF\x28\x25\x20\xFF\x28\x26\x20\xFF\x29\x26\x20\xFF\x27\x25\x1E\xFF" + "\x28\x23\x20\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1E\xFF\x25\x24\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x1E\xFF" + "\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x25\x1D\xFF\x25\x22\x1E\xFF\x27\x24\x1F\xFF\x29\x27\x20\xFF\x26\x24\x1E\xFF" + "\x25\x24\x1F\xFF\x28\x26\x20\xFF\x28\x25\x1E\xFF\x25\x24\x1E\xFF\x28\x26\x1E\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF" + "\x55\xCC\x96\xFF\x56\xCC\x97\xFF\x4B\xA6\x7A\xFF\x2E\x47\x36\xFF\x25\x25\x20\xFF\x28\x26\x21\xFF\x29\x26\x21\xFF\x28\x25\x20\xFF" + "\x25\x25\x1F\xFF\x25\x22\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x25\x24\x1E\xFF\x25\x25\x1F\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF" + "\x23\x23\x1D\xFF\x24\x22\x1D\xFF\x25\x23\x1E\xFF\x26\x26\x20\xFF\x26\x24\x1F\xFF\x26\x23\x1F\xFF\x26\x25\x1F\xFF\x24\x23\x1E\xFF" + "\x25\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x25\x23\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x24\x23\x1E\xFF" + "\x26\x23\x1F\xFF\x29\x26\x21\xFF\x26\x23\x1E\xFF\x23\x22\x1D\xFF\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x27\x25\x20\xFF\x28\x26\x20\xFF" + "\x26\x24\x1E\xFF\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x26\x23\x1E\xFF\x27\x25\x1F\xFF\x25\x23\x1E\xFF" + "\x28\x26\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x1F\xFF\x26\x24\x1F\xFF\x29\x26\x21\xFF\x28\x25\x20\xFF\x27\x24\x20\xFF\x24\x22\x1D\xFF" + "\x29\x26\x21\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x27\x25\x20\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x28\x25\x1F\xFF\x25\x23\x1E\xFF" + "\x55\xCC\x97\xFF\x56\xCC\x97\xFF\x51\xBE\x8C\xFF\x32\x52\x3F\xFF\x27\x2A\x22\xFF\x28\x26\x21\xFF\x27\x24\x20\xFF\x27\x25\x20\xFF" + "\x26\x24\x1F\xFF\x29\x26\x21\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x27\x25\x1F\xFF" + "\x26\x25\x20\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x29\x29\x22\xFF\x25\x25\x1F\xFF\x27\x24\x20\xFF\x29\x26\x21\xFF\x26\x24\x1F\xFF" + "\x26\x24\x1E\xFF\x27\x25\x1F\xFF\x29\x26\x21\xFF\x27\x24\x1F\xFF\x26\x25\x1F\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF" + "\x25\x22\x1D\xFF\x25\x22\x1D\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x27\x25\x1F\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF" + "\x26\x25\x1E\xFF\x26\x24\x1F\xFF\x28\x26\x20\xFF\x2A\x27\x22\xFF\x29\x26\x21\xFF\x25\x24\x1E\xFF\x24\x22\x1C\xFF\x24\x23\x1D\xFF" + "\x26\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x28\x25\x1F\xFF\x26\x24\x1F\xFF\x26\x25\x1F\xFF\x26\x25\x1E\xFF\x25\x23\x1D\xFF" + "\x26\x24\x1E\xFF\x24\x23\x1E\xFF\x26\x23\x1E\xFF\x27\x26\x20\xFF\x26\x23\x1E\xFF\x27\x25\x1E\xFF\x28\x25\x1F\xFF\x27\x25\x1F\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x55\xCA\x95\xFF\x39\x6C\x51\xFF\x29\x2F\x26\xFF\x29\x28\x22\xFF\x28\x25\x20\xFF\x29\x27\x22\xFF" + "\x28\x25\x20\xFF\x26\x25\x1F\xFF\x26\x24\x1F\xFF\x27\x25\x20\xFF\x26\x25\x1E\xFF\x26\x24\x1F\xFF\x25\x22\x1E\xFF\x27\x24\x1F\xFF" + "\x25\x25\x1F\xFF\x25\x24\x1F\xFF\x26\x25\x1F\xFF\x25\x23\x1E\xFF\x26\x24\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF" + "\x27\x24\x1F\xFF\x26\x25\x1E\xFF\x28\x26\x20\xFF\x27\x25\x20\xFF\x26\x24\x1E\xFF\x27\x26\x1F\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF" + "\x26\x25\x1F\xFF\x27\x24\x20\xFF\x27\x26\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF" + "\x29\x27\x21\xFF\x28\x25\x20\xFF\x26\x25\x1F\xFF\x29\x26\x21\xFF\x29\x26\x21\xFF\x28\x26\x20\xFF\x27\x25\x1E\xFF\x28\x25\x1E\xFF" + "\x27\x26\x21\xFF\x29\x26\x21\xFF\x27\x24\x1F\xFF\x26\x26\x1E\xFF\x25\x24\x1E\xFF\x26\x24\x1E\xFF\x25\x24\x1E\xFF\x26\x25\x1F\xFF" + "\x24\x23\x1D\xFF\x25\x24\x1D\xFF\x26\x24\x1E\xFF\x27\x24\x1F\xFF\x26\x24\x1E\xFF\x25\x23\x1D\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF" + "\x57\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x4A\xA3\x79\xFF\x29\x35\x2B\xFF\x27\x29\x23\xFF\x27\x25\x1F\xFF\x27\x25\x1E\xFF" + "\x27\x26\x1F\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x29\x27\x20\xFF\x27\x25\x20\xFF\x29\x26\x21\xFF\x25\x23\x1E\xFF\x25\x22\x1D\xFF" + "\x25\x25\x1F\xFF\x25\x24\x1E\xFF\x27\x24\x1F\xFF\x29\x26\x21\xFF\x26\x26\x20\xFF\x26\x24\x1F\xFF\x27\x25\x1F\xFF\x26\x25\x1F\xFF" + "\x28\x25\x20\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x25\x23\x1E\xFF\x29\x26\x21\xFF\x29\x27\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x25\x22\x1D\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF\x24\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF" + "\x26\x23\x1F\xFF\x28\x26\x20\xFF\x27\x26\x20\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x26\x24\x1E\xFF\x27\x25\x1F\xFF\x27\x25\x1E\xFF" + "\x27\x24\x1F\xFF\x28\x27\x21\xFF\x27\x25\x20\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1E\xFF\x2A\x26\x21\xFF" + "\x28\x25\x20\xFF\x26\x24\x1E\xFF\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x23\x1D\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x55\xC8\x93\xFF\x32\x53\x40\xFF\x28\x2E\x25\xFF\x25\x25\x20\xFF\x24\x23\x1E\xFF" + "\x28\x25\x20\xFF\x28\x26\x21\xFF\x27\x26\x1F\xFF\x27\x26\x20\xFF\x27\x24\x1F\xFF\x27\x26\x1F\xFF\x27\x27\x21\xFF\x26\x25\x20\xFF" + "\x26\x25\x20\xFF\x27\x25\x1F\xFF\x28\x25\x1F\xFF\x26\x24\x1F\xFF\x25\x24\x1E\xFF\x27\x25\x20\xFF\x29\x27\x22\xFF\x27\x25\x20\xFF" + "\x27\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x24\x23\x1D\xFF\x29\x26\x21\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x26\x23\x1E\xFF" + "\x25\x23\x1E\xFF\x28\x25\x20\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x29\x25\x20\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x25\x1F\xFF" + "\x26\x23\x1E\xFF\x29\x26\x21\xFF\x28\x25\x20\xFF\x25\x25\x1F\xFF\x25\x24\x1E\xFF\x25\x22\x1D\xFF\x27\x25\x1F\xFF\x28\x26\x21\xFF" + "\x26\x24\x1F\xFF\x26\x25\x1F\xFF\x26\x25\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x25\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x26\x20\xFF" + "\x28\x25\x20\xFF\x28\x26\x20\xFF\x28\x25\x1F\xFF\x24\x23\x1D\xFF\x26\x24\x1F\xFF\x26\x25\x1F\xFF\x25\x24\x1E\xFF\x25\x23\x1C\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x97\xFF\x43\x8E\x6A\xFF\x2B\x3C\x2F\xFF\x29\x32\x28\xFF\x28\x30\x26\xFF" + "\x2A\x2F\x28\xFF\x29\x2F\x26\xFF\x29\x2D\x25\xFF\x2A\x2E\x26\xFF\x28\x2B\x23\xFF\x27\x2A\x24\xFF\x28\x2B\x25\xFF\x27\x2A\x23\xFF" + "\x27\x29\x21\xFF\x27\x29\x22\xFF\x27\x28\x20\xFF\x29\x27\x22\xFF\x26\x25\x1F\xFF\x2A\x28\x23\xFF\x28\x28\x21\xFF\x24\x24\x1D\xFF" + "\x26\x23\x1E\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x25\x23\x1E\xFF\x24\x22\x1E\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x26\x25\x1F\xFF" + "\x26\x24\x1F\xFF\x28\x25\x20\xFF\x28\x26\x21\xFF\x27\x25\x20\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x25\x24\x1F\xFF\x25\x23\x1E\xFF" + "\x26\x23\x1E\xFF\x27\x24\x1F\xFF\x27\x25\x20\xFF\x23\x23\x1D\xFF\x24\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x25\x1F\xFF\x26\x24\x1F\xFF" + "\x28\x25\x20\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x27\x24\x20\xFF\x26\x25\x20\xFF\x26\x23\x1E\xFF\x28\x25\x20\xFF\x25\x22\x1D\xFF" + "\x27\x24\x1F\xFF\x27\x25\x20\xFF\x28\x25\x1F\xFF\x26\x24\x1E\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x25\x24\x1E\xFF" + "\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x56\xCD\x97\xFF\x54\xC5\x93\xFF\x49\xA2\x78\xFF\x47\x94\x6F\xFF\x44\x8D\x6A\xFF" + "\x42\x87\x64\xFF\x40\x7F\x5F\xFF\x3F\x79\x5B\xFF\x3D\x71\x57\xFF\x37\x68\x4F\xFF\x39\x65\x4D\xFF\x38\x60\x4B\xFF\x36\x5E\x49\xFF" + "\x37\x5A\x45\xFF\x32\x51\x3E\xFF\x30\x48\x38\xFF\x2F\x41\x33\xFF\x2A\x35\x2A\xFF\x2C\x31\x2A\xFF\x2B\x31\x29\xFF\x29\x2E\x26\xFF" + "\x2B\x2D\x27\xFF\x28\x2A\x23\xFF\x28\x2A\x23\xFF\x26\x27\x21\xFF\x26\x26\x20\xFF\x26\x25\x1F\xFF\x27\x24\x1E\xFF\x28\x27\x1F\xFF" + "\x27\x26\x1D\xFF\x25\x24\x1F\xFF\x26\x25\x1F\xFF\x26\x24\x1D\xFF\x24\x23\x1D\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x26\x23\x1E\xFF" + "\x29\x26\x21\xFF\x27\x24\x20\xFF\x28\x27\x20\xFF\x27\x26\x1E\xFF\x28\x26\x21\xFF\x26\x24\x1E\xFF\x27\x25\x1F\xFF\x26\x24\x1F\xFF" + "\x26\x26\x1E\xFF\x28\x26\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x27\x25\x20\xFF\x25\x24\x1E\xFF" + "\x28\x25\x20\xFF\x26\x23\x1E\xFF\x29\x26\x21\xFF\x27\x25\x20\xFF\x27\x26\x20\xFF\x26\x25\x1F\xFF\x27\x26\x1F\xFF\x28\x26\x20\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x55\xC9\x95\xFF\x54\xC6\x93\xFF" + "\x53\xBF\x8D\xFF\x4F\xB4\x85\xFF\x4C\xA8\x7D\xFF\x48\x9C\x74\xFF\x44\x8D\x6A\xFF\x41\x82\x62\xFF\x3D\x76\x59\xFF\x3B\x6A\x51\xFF" + "\x36\x5A\x46\xFF\x30\x4B\x3B\xFF\x2C\x3D\x30\xFF\x2B\x35\x2B\xFF\x2B\x32\x2A\xFF\x2A\x2F\x27\xFF\x2A\x2C\x24\xFF\x2A\x2B\x24\xFF" + "\x28\x29\x22\xFF\x27\x26\x20\xFF\x26\x25\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x1E\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x27\x26\x1F\xFF" + "\x28\x25\x20\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x26\x25\x20\xFF\x25\x24\x1E\xFF\x25\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x20\xFF" + "\x27\x24\x1F\xFF\x25\x25\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x27\x24\x1F\xFF\x28\x26\x1F\xFF\x28\x26\x1F\xFF" + "\x28\x26\x20\xFF\x28\x26\x1F\xFF\x27\x25\x20\xFF\x27\x26\x20\xFF\x27\x26\x1F\xFF\x28\x26\x20\xFF\x29\x25\x20\xFF\x27\x25\x20\xFF" + "\x56\xCC\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCA\x96\xFF\x54\xC5\x92\xFF\x53\xC0\x8F\xFF" + "\x51\xB9\x89\xFF\x50\xB3\x85\xFF\x4D\xAC\x80\xFF\x49\xA1\x78\xFF\x43\x8E\x6A\xFF\x3D\x76\x59\xFF\x34\x56\x42\xFF\x2E\x3D\x30\xFF" + "\x2D\x36\x2C\xFF\x2A\x31\x28\xFF\x26\x2A\x23\xFF\x25\x28\x21\xFF\x28\x29\x22\xFF\x27\x26\x1F\xFF\x28\x27\x20\xFF\x26\x26\x1E\xFF" + "\x28\x25\x20\xFF\x29\x26\x21\xFF\x27\x24\x1F\xFF\x25\x25\x1F\xFF\x26\x24\x1F\xFF\x27\x25\x20\xFF\x27\x26\x20\xFF\x27\x24\x1F\xFF" + "\x24\x25\x1F\xFF\x26\x25\x1F\xFF\x28\x26\x20\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x25\x23\x1D\xFF\x29\x26\x20\xFF" + "\x28\x26\x1F\xFF\x29\x26\x21\xFF\x27\x26\x20\xFF\x26\x24\x1E\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF" + "\x56\xCE\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x97\xFF\x58\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCC\x97\xFF\x56\xC6\x91\xFF" + "\x4B\xA9\x7D\xFF\x42\x8C\x69\xFF\x3A\x6D\x54\xFF\x36\x5B\x46\xFF\x2F\x49\x38\xFF\x2C\x3D\x2F\xFF\x29\x32\x27\xFF\x28\x2E\x26\xFF" + "\x27\x2B\x23\xFF\x27\x28\x22\xFF\x24\x25\x1E\xFF\x23\x23\x1D\xFF\x26\x24\x1F\xFF\x27\x24\x1E\xFF\x26\x23\x1E\xFF\x28\x26\x20\xFF" + "\x27\x26\x20\xFF\x27\x25\x20\xFF\x26\x24\x1E\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x24\x23\x1D\xFF\x25\x24\x1E\xFF\x27\x24\x1F\xFF" + "\x27\x26\x1E\xFF\x27\x26\x20\xFF\x28\x26\x21\xFF\x28\x25\x20\xFF\x29\x26\x21\xFF\x25\x24\x1E\xFF\x22\x21\x1C\xFF\x26\x24\x1F\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x56\xCC\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x56\xCB\x96\xFF\x52\xC0\x8E\xFF\x4C\xAB\x7E\xFF\x46\x97\x70\xFF\x41\x82\x62\xFF\x3C\x6E\x53\xFF" + "\x34\x54\x41\xFF\x2C\x3C\x31\xFF\x28\x30\x27\xFF\x27\x2C\x23\xFF\x27\x29\x22\xFF\x28\x27\x21\xFF\x26\x25\x1F\xFF\x27\x27\x20\xFF" + "\x28\x25\x20\xFF\x26\x25\x20\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x25\x23\x1E\xFF\x25\x24\x1E\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x28\x26\x21\xFF\x26\x24\x1E\xFF\x25\x25\x1F\xFF\x26\x24\x1F\xFF\x25\x23\x1E\xFF\x27\x24\x1F\xFF\x28\x25\x20\xFF\x26\x24\x1F\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x56\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCD\x97\xFF\x57\xCD\x97\xFF\x55\xCA\x95\xFF\x53\xC2\x90\xFF" + "\x50\xB6\x88\xFF\x4B\xA7\x7C\xFF\x41\x86\x64\xFF\x35\x5A\x45\xFF\x2B\x38\x2D\xFF\x29\x31\x28\xFF\x28\x2C\x25\xFF\x26\x28\x22\xFF" + "\x26\x26\x21\xFF\x27\x25\x20\xFF\x26\x24\x1F\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x28\x25\x1F\xFF\x29\x26\x20\xFF\x29\x26\x21\xFF" + "\x2B\x29\x22\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x28\x26\x20\xFF\x29\x27\x20\xFF\x28\x25\x20\xFF\x28\x25\x1F\xFF\x28\x26\x20\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCC\x97\xFF\x56\xCD\x97\xFF\x57\xCC\x97\xFF\x52\xBB\x89\xFF\x46\x91\x6C\xFF\x38\x68\x4F\xFF\x31\x4F\x3D\xFF" + "\x2C\x3A\x30\xFF\x2A\x30\x28\xFF\x27\x2A\x24\xFF\x26\x28\x21\xFF\x25\x26\x1E\xFF\x27\x25\x1F\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF" + "\x25\x24\x1D\xFF\x24\x24\x1D\xFF\x27\x25\x1F\xFF\x28\x26\x1F\xFF\x23\x23\x1D\xFF\x26\x23\x1E\xFF\x28\x26\x20\xFF\x27\x25\x1F\xFF" + "\x56\xCB\x96\xFF\x55\xCA\x95\xFF\x55\xC9\x94\xFF\x56\xCB\x96\xFF\x58\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x55\xC8\x94\xFF\x4F\xB4\x86\xFF" + "\x46\x93\x6E\xFF\x3F\x76\x59\xFF\x34\x57\x42\xFF\x2B\x3B\x2E\xFF\x29\x30\x27\xFF\x2B\x2D\x24\xFF\x28\x28\x22\xFF\x26\x25\x1F\xFF" + "\x25\x25\x1E\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x27\x25\x1E\xFF\x24\x22\x1D\xFF\x26\x23\x1E\xFF\x28\x24\x1F\xFF\x25\x25\x1E\xFF" + "\x39\x64\x4C\xFF\x38\x62\x4B\xFF\x37\x61\x4A\xFF\x37\x62\x4B\xFF\x39\x68\x4F\xFF\x3B\x71\x55\xFF\x3F\x7B\x5B\xFF\x40\x81\x60\xFF" + "\x44\x8C\x69\xFF\x47\x94\x6F\xFF\x49\x9C\x75\xFF\x4A\xA3\x7A\xFF\x4C\xAB\x7F\xFF\x50\xB5\x86\xFF\x53\xC3\x90\xFF\x56\xCC\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x58\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x58\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x54\xC5\x92\xFF\x50\xB8\x88\xFF\x4B\xA5\x7A\xFF\x3F\x79\x5B\xFF\x31\x4C\x3C\xFF\x29\x34\x29\xFF\x27\x2E\x25\xFF" + "\x26\x26\x22\xFF\x26\x25\x20\xFF\x26\x25\x20\xFF\x27\x27\x1F\xFF\x23\x21\x1C\xFF\x29\x26\x20\xFF\x27\x26\x21\xFF\x27\x25\x1F\xFF" + "\x2A\x2C\x24\xFF\x29\x2B\x24\xFF\x26\x28\x23\xFF\x2A\x2C\x25\xFF\x28\x2C\x24\xFF\x27\x2B\x23\xFF\x26\x2B\x23\xFF\x28\x2F\x25\xFF" + "\x27\x2E\x25\xFF\x2A\x31\x28\xFF\x29\x33\x2A\xFF\x29\x34\x2A\xFF\x2D\x37\x2C\xFF\x2D\x38\x2D\xFF\x2C\x3B\x2E\xFF\x2F\x43\x35\xFF" + "\x35\x5B\x47\xFF\x3C\x70\x55\xFF\x41\x83\x62\xFF\x47\x95\x6E\xFF\x4B\xA7\x7B\xFF\x4E\xAF\x82\xFF\x50\xB6\x87\xFF\x52\xBC\x8C\xFF" + "\x54\xC5\x92\xFF\x56\xCB\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x97\xFF\x56\xCC\x97\xFF\x54\xC6\x92\xFF\x4A\xA1\x78\xFF\x3D\x6E\x54\xFF" + "\x32\x4A\x39\xFF\x2E\x35\x2B\xFF\x27\x29\x22\xFF\x24\x24\x1E\xFF\x28\x27\x1F\xFF\x26\x26\x20\xFF\x26\x24\x1F\xFF\x25\x24\x1E\xFF" + "\x26\x26\x20\xFF\x25\x25\x1F\xFF\x24\x23\x1E\xFF\x24\x23\x1E\xFF\x27\x26\x20\xFF\x26\x25\x1F\xFF\x26\x26\x1F\xFF\x25\x25\x1F\xFF" + "\x26\x25\x1F\xFF\x24\x24\x1F\xFF\x28\x28\x21\xFF\x2A\x2A\x23\xFF\x26\x26\x20\xFF\x26\x28\x21\xFF\x26\x29\x21\xFF\x2A\x2C\x24\xFF" + "\x27\x2B\x24\xFF\x29\x2E\x25\xFF\x29\x30\x27\xFF\x2B\x33\x2A\xFF\x2B\x37\x2D\xFF\x31\x44\x35\xFF\x34\x53\x40\xFF\x38\x61\x4A\xFF" + "\x3C\x74\x58\xFF\x43\x88\x66\xFF\x4A\x9D\x76\xFF\x4E\xB1\x83\xFF\x54\xC4\x91\xFF\x56\xCD\x97\xFF\x57\xCD\x97\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF" + "\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xC9\x94\xFF" + "\x4C\xA7\x7C\xFF\x41\x81\x61\xFF\x35\x58\x44\xFF\x2D\x39\x2F\xFF\x2B\x30\x28\xFF\x28\x2A\x22\xFF\x27\x26\x20\xFF\x27\x25\x20\xFF" + "\x27\x25\x1F\xFF\x26\x24\x1D\xFF\x28\x26\x1F\xFF\x28\x26\x20\xFF\x25\x24\x1F\xFF\x25\x24\x1E\xFF\x25\x23\x1E\xFF\x26\x24\x1F\xFF" + "\x26\x23\x1E\xFF\x25\x24\x1E\xFF\x29\x26\x21\xFF\x2A\x28\x20\xFF\x28\x25\x20\xFF\x28\x26\x20\xFF\x27\x25\x1E\xFF\x26\x26\x1F\xFF" + "\x26\x27\x1F\xFF\x27\x25\x1F\xFF\x25\x26\x1E\xFF\x26\x27\x21\xFF\x27\x28\x22\xFF\x29\x28\x22\xFF\x28\x2A\x23\xFF\x29\x2C\x24\xFF" + "\x2A\x31\x27\xFF\x2A\x35\x2A\xFF\x2F\x42\x35\xFF\x34\x50\x3E\xFF\x38\x61\x4A\xFF\x3C\x74\x56\xFF\x46\x92\x6D\xFF\x4D\xAF\x82\xFF" + "\x53\xC9\x94\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCE\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x55\xC8\x94\xFF\x51\xB8\x89\xFF\x48\x9B\x74\xFF\x38\x5E\x48\xFF\x2C\x39\x2D\xFF\x28\x2F\x26\xFF\x27\x2A\x22\xFF" + "\x27\x24\x1F\xFF\x25\x23\x1E\xFF\x25\x23\x1E\xFF\x27\x24\x1F\xFF\x26\x23\x1E\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x26\x26\x20\xFF" + "\x27\x25\x1F\xFF\x26\x25\x1F\xFF\x25\x25\x1F\xFF\x25\x25\x1F\xFF\x26\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x25\x20\xFF\x25\x24\x1E\xFF" + "\x26\x24\x1F\xFF\x25\x24\x1D\xFF\x25\x22\x1C\xFF\x26\x25\x1F\xFF\x27\x25\x20\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x29\x27\x22\xFF\x27\x27\x21\xFF\x28\x28\x22\xFF\x2B\x2C\x26\xFF\x29\x2B\x23\xFF\x28\x2C\x25\xFF\x29\x31\x27\xFF\x2B\x36\x2C\xFF" + "\x2D\x3F\x31\xFF\x34\x5A\x46\xFF\x3F\x7D\x5E\xFF\x4A\xA0\x77\xFF\x51\xB8\x88\xFF\x55\xC5\x92\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCC\x97\xFF\x57\xCC\x97\xFF\x57\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCD\x98\xFF\x56\xCD\x97\xFF\x56\xCD\x98\xFF\x56\xCC\x97\xFF\x56\xCA\x96\xFF\x4E\xB2\x85\xFF\x3F\x7D\x5E\xFF\x33\x55\x42\xFF" + "\x25\x22\x1E\xFF\x26\x23\x1E\xFF\x27\x25\x1F\xFF\x28\x25\x20\xFF\x27\x25\x20\xFF\x27\x25\x20\xFF\x27\x25\x1F\xFF\x25\x24\x1D\xFF" + "\x27\x24\x1E\xFF\x27\x24\x1F\xFF\x25\x23\x1D\xFF\x27\x24\x1F\xFF\x25\x25\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF\x27\x24\x1F\xFF" + "\x27\x25\x20\xFF\x29\x26\x21\xFF\x28\x26\x21\xFF\x28\x25\x20\xFF\x25\x24\x1F\xFF\x28\x25\x20\xFF\x28\x25\x20\xFF\x26\x25\x1E\xFF" + "\x27\x24\x1F\xFF\x26\x24\x1F\xFF\x28\x25\x20\xFF\x27\x26\x20\xFF\x26\x25\x20\xFF\x28\x26\x20\xFF\x27\x27\x20\xFF\x27\x29\x21\xFF" + "\x28\x2A\x24\xFF\x28\x2C\x25\xFF\x2A\x31\x29\xFF\x2C\x38\x2D\xFF\x35\x57\x43\xFF\x3E\x74\x59\xFF\x46\x91\x6E\xFF\x4E\xAD\x81\xFF" + "\x55\xC7\x93\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" + "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCB\x96\xFF\x51\xB7\x88\xFF"; + /** * [MS-RDPEGDI] Test Bitmap 32x32 (16bpp) */ @@ -1356,7 +1874,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) format = FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_ARGB, FREERDP_PIXEL_FLIP_NONE); -#if 1 +#if 0 freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12, 1, NULL, &dstSize); @@ -1491,6 +2009,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) } #endif +#if 0 /* Experimental Case 01 */ width = 64; @@ -1531,9 +2050,9 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); +#endif - return 0; - +#if 0 /* Experimental Case 02 */ width = 64; @@ -1560,11 +2079,13 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) if (memcmp(decompressedBitmap, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, width * height * 4) != 0) { +#if 0 printf("experimental bitmap 02\n"); winpr_HexDump((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, width * height * 4); printf("decompressed bitmap\n"); winpr_HexDump(decompressedBitmap, width * height * 4); +#endif printf("error: decompressed experimental bitmap 02 is corrupted\n"); return -1; @@ -1572,6 +2093,48 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); +#endif + + /* Experimental Case 03 */ + + width = 64; + height = 64; + + compressedBitmap = freerdp_bitmap_compress_planar((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_03, + format, width, height, width * 4, NULL, &dstSize); + + decompressedBitmap = (BYTE*) malloc(width * height * 4); + ZeroMemory(decompressedBitmap, width * height * 4); + + if (!bitmap_decompress(compressedBitmap, decompressedBitmap, width, height, dstSize, 32, 32)) + { + printf("failed to decompress experimental bitmap 03: width: %d height: %d\n", width, height); + return -1; + } + else + { + printf("success decompressing experimental bitmap 03: width: %d height: %d\n", width, height); + } + + fill_bitmap_alpha_channel(decompressedBitmap, width, height, 0xFF); + fill_bitmap_alpha_channel((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_03, width, height, 0xFF); + + if (memcmp(decompressedBitmap, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_03, width * height * 4) != 0) + { +#if 0 + printf("experimental bitmap 03\n"); + winpr_HexDump((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_03, width * height * 4); + + printf("decompressed bitmap\n"); + winpr_HexDump(decompressedBitmap, width * height * 4); +#endif + + printf("error: decompressed experimental bitmap 03 is corrupted\n"); + return -1; + } + + free(compressedBitmap); + free(decompressedBitmap); freerdp_clrconv_free(clrconv); free(srcBitmap32); From 0ffc6a93ae3ab16b4b7cf5776e48072f533dd8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 4 Dec 2013 15:37:42 -0500 Subject: [PATCH 065/149] libfreerdp-client: start extending .rdp file parsing capabilities --- client/common/file.c | 135 ++++++++++++++++++++----- client/common/test/TestClientRdpFile.c | 27 ++++- include/freerdp/client/file.h | 23 +++++ 3 files changed, 160 insertions(+), 25 deletions(-) diff --git a/client/common/file.c b/client/common/file.c index cc71ae604..84b19fdfe 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -49,8 +49,10 @@ static WCHAR CR_LF_STR_W[] = { '\r', '\n', '\0' }; #define INVALID_INTEGER_VALUE 0xFFFFFFFF -BOOL freerdp_client_rdp_file_set_integer(rdpFile* file, char* name, int value) +BOOL freerdp_client_rdp_file_set_integer(rdpFile* file, char* name, int value, int index) { + BOOL bStandard = TRUE; + #ifdef DEBUG_CLIENT_FILE fprintf(stderr, "%s:i:%d\n", name, value); #endif @@ -184,12 +186,26 @@ BOOL freerdp_client_rdp_file_set_integer(rdpFile* file, char* name, int value) else if (_stricmp(name, "rdgiskdcproxy") == 0) file->RdgIsKdcProxy = value; else - return FALSE; + bStandard = FALSE; - return TRUE; + if (index >= 0) + { + file->lines[index].name = _strdup(name); + file->lines[index].iValue = (DWORD) value; + + file->lines[index].flags = RDP_FILE_LINE_FLAG_FORMATTED; + file->lines[index].flags |= RDP_FILE_LINE_FLAG_TYPE_INTEGER; + + if (bStandard) + file->lines[index].flags |= RDP_FILE_LINE_FLAG_STANDARD; + + file->lines[index].valueLength = 0; + } + + return bStandard; } -void freerdp_client_parse_rdp_file_integer_unicode(rdpFile* file, WCHAR* name, WCHAR* value) +void freerdp_client_parse_rdp_file_integer_unicode(rdpFile* file, WCHAR* name, WCHAR* value, int index) { int length; int ivalue; @@ -207,20 +223,22 @@ void freerdp_client_parse_rdp_file_integer_unicode(rdpFile* file, WCHAR* name, W valueA[length] = '\0'; ivalue = atoi(valueA); - freerdp_client_rdp_file_set_integer(file, nameA, ivalue); + freerdp_client_rdp_file_set_integer(file, nameA, ivalue, index); free(nameA); free(valueA); } -void freerdp_client_parse_rdp_file_integer_ascii(rdpFile* file, char* name, char* value) +void freerdp_client_parse_rdp_file_integer_ascii(rdpFile* file, char* name, char* value, int index) { int ivalue = atoi(value); - freerdp_client_rdp_file_set_integer(file, name, ivalue); + freerdp_client_rdp_file_set_integer(file, name, ivalue, index); } -BOOL freerdp_client_rdp_file_set_string(rdpFile* file, char* name, char* value) +BOOL freerdp_client_rdp_file_set_string(rdpFile* file, char* name, char* value, int index) { + BOOL bStandard = TRUE; + #ifdef DEBUG_CLIENT_FILE fprintf(stderr, "%s:s:%s\n", name, value); #endif @@ -264,9 +282,23 @@ BOOL freerdp_client_rdp_file_set_string(rdpFile* file, char* name, char* value) else if (_stricmp(name, "winposstr") == 0) file->WinPosStr = _strdup(value); else - return FALSE; + bStandard = FALSE; - return TRUE; + if (index >= 0) + { + file->lines[index].name = _strdup(name); + file->lines[index].sValue = _strdup(value); + + file->lines[index].flags = RDP_FILE_LINE_FLAG_FORMATTED; + file->lines[index].flags |= RDP_FILE_LINE_FLAG_TYPE_STRING; + + if (bStandard) + file->lines[index].flags |= RDP_FILE_LINE_FLAG_STANDARD; + + file->lines[index].valueLength = strlen(file->lines[index].sValue); + } + + return bStandard; } void freerdp_client_add_option(rdpFile* file, char* option) @@ -281,7 +313,36 @@ void freerdp_client_add_option(rdpFile* file, char* option) (file->argc)++; } -void freerdp_client_parse_rdp_file_string_unicode(rdpFile* file, WCHAR* name, WCHAR* value) +void freerdp_client_parse_rdp_file_add_line(rdpFile* file, char* line, int index) +{ + while ((file->lineCount + 1) > file->lineSize) + { + file->lineSize *= 2; + file->lines = (rdpFileLine*) realloc(file->lines, file->lineSize * sizeof(rdpFileLine)); + } + + ZeroMemory(&(file->lines[file->lineCount]), sizeof(rdpFileLine)); + file->lines[file->lineCount].text = _strdup(line); + file->lines[file->lineCount].index = index; + (file->lineCount)++; +} + +void freerdp_client_parse_rdp_file_add_line_unicode(rdpFile* file, WCHAR* line, int index) +{ + char* lineA = NULL; + + ConvertFromUnicode(CP_UTF8, 0, line, -1, &lineA, 0, NULL, NULL); + freerdp_client_parse_rdp_file_add_line(file, lineA, index); + + free(lineA); +} + +void freerdp_client_parse_rdp_file_add_line_ascii(rdpFile* file, char* line, int index) +{ + freerdp_client_parse_rdp_file_add_line(file, line, index); +} + +void freerdp_client_parse_rdp_file_string_unicode(rdpFile* file, WCHAR* name, WCHAR* value, int index) { int length; char* nameA; @@ -297,20 +358,20 @@ void freerdp_client_parse_rdp_file_string_unicode(rdpFile* file, WCHAR* name, WC WideCharToMultiByte(CP_UTF8, 0, value, length, valueA, length, NULL, NULL); valueA[length] = '\0'; - freerdp_client_rdp_file_set_string(file, nameA, valueA); + freerdp_client_rdp_file_set_string(file, nameA, valueA, index); free(nameA); free(valueA); } -void freerdp_client_parse_rdp_file_string_ascii(rdpFile* file, char* name, char* value) +void freerdp_client_parse_rdp_file_string_ascii(rdpFile* file, char* name, char* value, int index) { char* valueA = _strdup(value); - if (!freerdp_client_rdp_file_set_string(file, name, valueA)) - free(valueA); + freerdp_client_rdp_file_set_string(file, name, valueA, index); + free(valueA); } -void freerdp_client_parse_rdp_file_option_unicode(rdpFile* file, WCHAR* option) +void freerdp_client_parse_rdp_file_option_unicode(rdpFile* file, WCHAR* option, int index) { char* optionA = NULL; @@ -320,13 +381,14 @@ void freerdp_client_parse_rdp_file_option_unicode(rdpFile* file, WCHAR* option) free(optionA); } -void freerdp_client_parse_rdp_file_option_ascii(rdpFile* file, char* option) +void freerdp_client_parse_rdp_file_option_ascii(rdpFile* file, char* option, int index) { freerdp_client_add_option(file, option); } BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffer, size_t size) { + int index; int length; char* line; char* type; @@ -335,6 +397,7 @@ BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffe char *beg, *end; char *name, *value; + index = 0; line = strtok_s((char*) buffer, "\r\n", &context); while (line) @@ -346,9 +409,11 @@ BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffe beg = line; end = &line[length - 1]; + freerdp_client_parse_rdp_file_add_line_ascii(file, line, index); + if (beg[0] == '/') { - freerdp_client_parse_rdp_file_option_ascii(file, line); + freerdp_client_parse_rdp_file_option_ascii(file, line, index); goto next_line; /* FreeRDP option */ } @@ -377,12 +442,12 @@ BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffe if (*type == 'i') { /* integer type */ - freerdp_client_parse_rdp_file_integer_ascii(file, name, value); + freerdp_client_parse_rdp_file_integer_ascii(file, name, value, index); } else if (*type == 's') { /* string type */ - freerdp_client_parse_rdp_file_string_ascii(file, name, value); + freerdp_client_parse_rdp_file_string_ascii(file, name, value, index); } else if (*type == 'b') { @@ -392,6 +457,7 @@ BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffe next_line: line = strtok_s(NULL, "\r\n", &context); + index++; } return TRUE; @@ -399,6 +465,7 @@ next_line: BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buffer, size_t size) { + int index; int length; WCHAR* line; WCHAR* type; @@ -407,6 +474,7 @@ BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buf WCHAR *beg, *end; WCHAR *name, *value; + index = 0; line = wcstok_s((WCHAR*) buffer, CR_LF_STR_W, &context); while (line != NULL) @@ -418,10 +486,12 @@ BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buf beg = line; end = &line[length - 1]; + freerdp_client_parse_rdp_file_add_line_unicode(file, line, index); + if (beg[0] == '/') { /* FreeRDP option */ - freerdp_client_parse_rdp_file_option_unicode(file, line); + freerdp_client_parse_rdp_file_option_unicode(file, line, index); goto next_line; } @@ -450,12 +520,12 @@ BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buf if (*type == 'i') { /* integer type */ - freerdp_client_parse_rdp_file_integer_unicode(file, name, value); + freerdp_client_parse_rdp_file_integer_unicode(file, name, value, index); } else if (*type == 's') { /* string type */ - freerdp_client_parse_rdp_file_string_unicode(file, name, value); + freerdp_client_parse_rdp_file_string_unicode(file, name, value, index); } else if (*type == 'b') { @@ -465,6 +535,7 @@ BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buf next_line: line = wcstok_s(NULL, CR_LF_STR_W, &context); + index++; } return TRUE; @@ -610,7 +681,7 @@ BOOL freerdp_client_write_rdp_file(const rdpFile* file, const char* name, BOOL u { ConvertToUnicode(CP_UTF8, 0, buffer, len, &unicodestr, 0); - // Write multi-byte header + /* Write multi-byte header */ fwrite(BOM_UTF16_LE, sizeof(BYTE), 2, fp); fwrite(unicodestr, 2, len, fp); @@ -1002,6 +1073,10 @@ rdpFile* freerdp_client_rdp_file_new() { FillMemory(file, sizeof(rdpFile), 0xFF); + file->lineCount = 0; + file->lineSize = 32; + file->lines = (rdpFileLine*) malloc(file->lineSize * sizeof(rdpFileLine)); + file->argc = 0; file->argSize = 32; file->argv = (char**) malloc(file->argSize * sizeof(char*)); @@ -1018,6 +1093,18 @@ void freerdp_client_rdp_file_free(rdpFile* file) if (file) { + if (file->lineCount) + { + for (i = 0; i < file->lineCount; i++) + { + free(file->lines[i].text); + free(file->lines[i].name); + free(file->lines[i].sValue); + } + + free(file->lines); + } + if (file->argv) { for (i = 0; i < file->argc; i++) diff --git a/client/common/test/TestClientRdpFile.c b/client/common/test/TestClientRdpFile.c index 2f391630a..322917290 100644 --- a/client/common/test/TestClientRdpFile.c +++ b/client/common/test/TestClientRdpFile.c @@ -256,11 +256,15 @@ static char testRdpFileUTF8[] = "rdgiskdcproxy:i:0\n" "kdcproxyname:s:\n" "drivestoredirect:s:*\n" - "username:s:LAB1\\JohnDoe\n"; + "username:s:LAB1\\JohnDoe\n" + "vendor integer:i:123\n" + "vendor string:s:microsoft\n"; int TestClientRdpFile(int argc, char* argv[]) { + int index; rdpFile* file; + rdpFileLine* line; /* Unicode */ @@ -324,6 +328,27 @@ int TestClientRdpFile(int argc, char* argv[]) return -1; } + for (index = 0; index < file->lineCount; index++) + { + line = &(file->lines[index]); + + if (line->flags & RDP_FILE_LINE_FLAG_FORMATTED) + { + if (line->flags & RDP_FILE_LINE_FLAG_TYPE_STRING) + { + printf("line %02d: name: %s value: %s, %s\n", + line->index, line->name, line->sValue, + (line->flags & RDP_FILE_LINE_FLAG_STANDARD) ? "standard" : "non-standard"); + } + else if (line->flags & RDP_FILE_LINE_FLAG_TYPE_INTEGER) + { + printf("line %02d: name: %s value: %d, %s\n", + line->index, line->name, line->iValue, + (line->flags & RDP_FILE_LINE_FLAG_STANDARD) ? "standard" : "non-standard"); + } + } + } + freerdp_client_rdp_file_free(file); return 0; diff --git a/include/freerdp/client/file.h b/include/freerdp/client/file.h index e9f6d332b..8a22db288 100644 --- a/include/freerdp/client/file.h +++ b/include/freerdp/client/file.h @@ -23,6 +23,25 @@ #include #include +#define RDP_FILE_LINE_FLAG_FORMATTED 0x00000001 +#define RDP_FILE_LINE_FLAG_STANDARD 0x00000002 +#define RDP_FILE_LINE_FLAG_TYPE_STRING 0x00000010 +#define RDP_FILE_LINE_FLAG_TYPE_INTEGER 0x00000020 +#define RDP_FILE_LINE_FLAG_TYPE_BINARY 0x00000040 + +struct rdp_file_line +{ + int index; + char* text; + DWORD flags; + char* name; + LPSTR sValue; + DWORD iValue; + PBYTE bValue; + int valueLength; +}; +typedef struct rdp_file_line rdpFileLine; + struct rdp_file { DWORD UseMultiMon; /* use multimon */ @@ -130,6 +149,10 @@ struct rdp_file LPSTR DevicesToRedirect; /* devicestoredirect */ LPSTR WinPosStr; /* winposstr */ + int lineCount; + int lineSize; + rdpFileLine* lines; + int argc; char** argv; int argSize; From 4b341daa3d9fe25c3d3ca2c061988bafe95fac47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 4 Dec 2013 16:29:45 -0500 Subject: [PATCH 066/149] libfreerdp-client: add functions for getting/setting rdp file options --- client/common/file.c | 140 ++++++++++++++++++++++++- client/common/test/TestClientRdpFile.c | 13 +++ include/freerdp/client/file.h | 6 ++ 3 files changed, 157 insertions(+), 2 deletions(-) diff --git a/client/common/file.c b/client/common/file.c index 84b19fdfe..2cf88597d 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -295,7 +295,7 @@ BOOL freerdp_client_rdp_file_set_string(rdpFile* file, char* name, char* value, if (bStandard) file->lines[index].flags |= RDP_FILE_LINE_FLAG_STANDARD; - file->lines[index].valueLength = strlen(file->lines[index].sValue); + file->lines[index].valueLength = 0; } return bStandard; @@ -313,8 +313,11 @@ void freerdp_client_add_option(rdpFile* file, char* option) (file->argc)++; } -void freerdp_client_parse_rdp_file_add_line(rdpFile* file, char* line, int index) +int freerdp_client_parse_rdp_file_add_line(rdpFile* file, char* line, int index) { + if (index < 0) + index = file->lineCount; + while ((file->lineCount + 1) > file->lineSize) { file->lineSize *= 2; @@ -325,6 +328,8 @@ void freerdp_client_parse_rdp_file_add_line(rdpFile* file, char* line, int index file->lines[file->lineCount].text = _strdup(line); file->lines[file->lineCount].index = index; (file->lineCount)++; + + return index; } void freerdp_client_parse_rdp_file_add_line_unicode(rdpFile* file, WCHAR* line, int index) @@ -1057,6 +1062,137 @@ BOOL freerdp_client_populate_settings_from_rdp_file(rdpFile* file, rdpSettings* return TRUE; } +rdpFileLine* freerdp_client_rdp_file_find_line_index(rdpFile* file, int index) +{ + rdpFileLine* line; + + line = &(file->lines[index]); + + return line; +} + +rdpFileLine* freerdp_client_rdp_file_find_line_by_name(rdpFile* file, const char* name) +{ + int index; + BOOL bFound = FALSE; + rdpFileLine* line = NULL; + + for (index = 0; index < file->lineCount; index++) + { + line = &(file->lines[index]); + + if (line->flags & RDP_FILE_LINE_FLAG_FORMATTED) + { + if (strcmp(name, line->name) == 0) + { + bFound = TRUE; + break; + } + } + } + + return (bFound) ? line : NULL; +} + +int freerdp_client_rdp_file_set_string_option(rdpFile* file, const char* name, const char* value) +{ + int index; + int length; + char* text; + rdpFileLine* line; + + line = freerdp_client_rdp_file_find_line_by_name(file, name); + + length = _scprintf("%s:s:%s", name, value); + text = (char*) malloc(length + 1); + sprintf_s(text, length, "%s:s:%s", name, value); + text[length] = '\0'; + + if (line) + { + free(line->sValue); + line->sValue = _strdup(value); + + free(line->text); + line->text = text; + } + else + { + index = freerdp_client_parse_rdp_file_add_line(file, text, -1); + line = freerdp_client_rdp_file_find_line_index(file, index); + + freerdp_client_rdp_file_set_string(file, (char*) name, (char*) value, index); + + free(text); + } + + return 0; +} + +char* freerdp_client_rdp_file_get_string_option(rdpFile* file, const char* name) +{ + rdpFileLine* line; + + line = freerdp_client_rdp_file_find_line_by_name(file, name); + + if (!line) + return NULL; + + if (!(line->flags & RDP_FILE_LINE_FLAG_TYPE_STRING)) + return NULL; + + return line->sValue; +} + +int freerdp_client_rdp_file_set_integer_option(rdpFile* file, const char* name, int value) +{ + int index; + int length; + char* text; + rdpFileLine* line; + + line = freerdp_client_rdp_file_find_line_by_name(file, name); + + length = _scprintf("%s:i:%d", name, value); + text = (char*) malloc(length + 1); + sprintf_s(text, length, "%s:i:%d", name, value); + text[length] = '\0'; + + if (line) + { + line->iValue = value; + + free(line->text); + line->text = text; + } + else + { + index = freerdp_client_parse_rdp_file_add_line(file, text, -1); + line = freerdp_client_rdp_file_find_line_index(file, index); + + freerdp_client_rdp_file_set_integer(file, (char*) name, value, index); + + free(text); + } + + return 0; +} + +int freerdp_client_rdp_file_get_integer_option(rdpFile* file, const char* name) +{ + rdpFileLine* line; + + line = freerdp_client_rdp_file_find_line_by_name(file, name); + + if (!line) + return -1; + + if (!(line->flags & RDP_FILE_LINE_FLAG_TYPE_INTEGER)) + return -1; + + return line->iValue; +} + void freerdp_client_file_string_check_free(LPSTR str) { if (~((size_t) str)) diff --git a/client/common/test/TestClientRdpFile.c b/client/common/test/TestClientRdpFile.c index 322917290..39b188624 100644 --- a/client/common/test/TestClientRdpFile.c +++ b/client/common/test/TestClientRdpFile.c @@ -263,6 +263,8 @@ static char testRdpFileUTF8[] = int TestClientRdpFile(int argc, char* argv[]) { int index; + int iValue; + char* sValue; rdpFile* file; rdpFileLine* line; @@ -328,6 +330,17 @@ int TestClientRdpFile(int argc, char* argv[]) return -1; } + iValue = freerdp_client_rdp_file_get_integer_option(file, "vendor integer"); + freerdp_client_rdp_file_set_integer_option(file, "vendor integer", 456); + iValue = freerdp_client_rdp_file_get_integer_option(file, "vendor integer"); + + sValue = freerdp_client_rdp_file_get_string_option(file, "vendor string"); + freerdp_client_rdp_file_set_string_option(file, "vendor string", "apple"); + sValue = freerdp_client_rdp_file_get_string_option(file, "vendor string"); + + freerdp_client_rdp_file_set_string_option(file, "fruits", "banana,oranges"); + freerdp_client_rdp_file_set_integer_option(file, "numbers", 123456789); + for (index = 0; index < file->lineCount; index++) { line = &(file->lines[index]); diff --git a/include/freerdp/client/file.h b/include/freerdp/client/file.h index 8a22db288..2336b7dd1 100644 --- a/include/freerdp/client/file.h +++ b/include/freerdp/client/file.h @@ -172,6 +172,12 @@ FREERDP_API BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, c FREERDP_API BOOL freerdp_client_write_rdp_file(const rdpFile* file, const char* name, BOOL unicode); FREERDP_API size_t freerdp_client_write_rdp_file_buffer(const rdpFile* file, char* buffer, size_t size); +FREERDP_API int freerdp_client_rdp_file_set_string_option(rdpFile* file, const char* name, const char* value); +FREERDP_API char* freerdp_client_rdp_file_get_string_option(rdpFile* file, const char* name); + +FREERDP_API int freerdp_client_rdp_file_set_integer_option(rdpFile* file, const char* name, int value); +FREERDP_API int freerdp_client_rdp_file_get_integer_option(rdpFile* file, const char* name); + FREERDP_API rdpFile* freerdp_client_rdp_file_new(void); FREERDP_API void freerdp_client_rdp_file_free(rdpFile* file); From 73196c3777d5ea8ec145f217d41bf0af413ab054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 4 Dec 2013 18:25:55 -0500 Subject: [PATCH 067/149] libfreerdp-common: add fine grained control over .rdp file writing functions --- client/Windows/wf_interface.c | 31 ------ client/common/client.c | 33 +++++- client/common/file.c | 190 ++++++++++++---------------------- 3 files changed, 97 insertions(+), 157 deletions(-) diff --git a/client/Windows/wf_interface.c b/client/Windows/wf_interface.c index fa967fec2..8a1d016ee 100644 --- a/client/Windows/wf_interface.c +++ b/client/Windows/wf_interface.c @@ -862,37 +862,6 @@ int freerdp_client_load_settings_from_rdp_file(wfContext* wfc, char* filename) return 0; } -int freerdp_client_save_settings_to_rdp_file(wfContext* wfc, char* filename) -{ - if (!filename) - return 1; - - if (wfc->instance->settings->ConnectionFile) - { - free(wfc->instance->settings->ConnectionFile); - } - - wfc->instance->settings->ConnectionFile = _strdup(filename); - - // Reuse existing rdpFile structure if available, to preserve unsupported settings when saving to disk. - if (wfc->connectionRdpFile == NULL) - { - wfc->connectionRdpFile = freerdp_client_rdp_file_new(); - } - - if (!freerdp_client_populate_rdp_file_from_settings(wfc->connectionRdpFile, wfc->instance->settings)) - { - return 1; - } - - if (!freerdp_client_write_rdp_file(wfc->connectionRdpFile, filename, UNICODE)); - { - return 2; - } - - return 0; -} - void wf_size_scrollbars(wfContext* wfc, int client_width, int client_height) { BOOL rc; diff --git a/client/common/client.c b/client/common/client.c index d531ed2ff..4dc46008c 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -154,19 +154,42 @@ int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, int freerdp_client_settings_write_connection_file(const rdpSettings* settings, const char* filename, BOOL unicode) { rdpFile* file; - int status = -1; file = freerdp_client_rdp_file_new(); - if (freerdp_client_populate_rdp_file_from_settings(file, settings)) + if (!freerdp_client_populate_rdp_file_from_settings(file, settings)) + return -1; + { - if (freerdp_client_write_rdp_file(file, filename, unicode)) + int index; + rdpFileLine* line; + + for (index = 0; index < file->lineCount; index++) { - status = 0; + line = &(file->lines[index]); + + if (line->flags & RDP_FILE_LINE_FLAG_FORMATTED) + { + if (line->flags & RDP_FILE_LINE_FLAG_TYPE_STRING) + { + printf("line %02d: name: %s value: %s, %s\n", + line->index, line->name, line->sValue, + (line->flags & RDP_FILE_LINE_FLAG_STANDARD) ? "standard" : "non-standard"); + } + else if (line->flags & RDP_FILE_LINE_FLAG_TYPE_INTEGER) + { + printf("line %02d: name: %s value: %d, %s\n", + line->index, line->name, line->iValue, + (line->flags & RDP_FILE_LINE_FLAG_STANDARD) ? "standard" : "non-standard"); + } + } } } + if (!freerdp_client_write_rdp_file(file, filename, unicode)) + return -1; + freerdp_client_rdp_file_free(file); - return status; + return 0; } diff --git a/client/common/file.c b/client/common/file.c index 2cf88597d..6e93f3a95 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -659,144 +659,92 @@ BOOL freerdp_client_populate_rdp_file_from_settings(rdpFile* file, const rdpSett BOOL freerdp_client_write_rdp_file(const rdpFile* file, const char* name, BOOL unicode) { - int rc = 0; + FILE* fp; + int length; char* buffer; - int len, len2; - FILE* fp = NULL; + int status = 0; WCHAR* unicodestr = NULL; - len = freerdp_client_write_rdp_file_buffer(file, NULL, 0); + length = freerdp_client_write_rdp_file_buffer(file, NULL, 0); - if (len <= 0) + if (length < 0) { - fprintf(stderr, "freerdp_client_write_rdp_file: Error determining buffer size.\n"); + fprintf(stderr, "freerdp_client_write_rdp_file: error determining buffer size.\n"); return FALSE; } - buffer = (char*) malloc((len + 1) * sizeof(char)); - len2 = freerdp_client_write_rdp_file_buffer(file, buffer, len + 1); + buffer = (char*) malloc((length + 1) * sizeof(char)); - if (len2 == len) + if (freerdp_client_write_rdp_file_buffer(file, buffer, length + 1) != length) { - fp = fopen(name, "w+b"); - - if (fp != NULL) - { - if (unicode) - { - ConvertToUnicode(CP_UTF8, 0, buffer, len, &unicodestr, 0); - - /* Write multi-byte header */ - fwrite(BOM_UTF16_LE, sizeof(BYTE), 2, fp); - fwrite(unicodestr, 2, len, fp); - - free(unicodestr); - } - else - { - fwrite(buffer, 1, len, fp); - } - - rc = fflush(fp); - rc = fclose(fp); - } + fprintf(stderr, "freerdp_client_write_rdp_file: error writing to output buffer\n"); + return FALSE; } - if (buffer != NULL) + fp = fopen(name, "w+b"); + + if (fp) + { + if (unicode) + { + ConvertToUnicode(CP_UTF8, 0, buffer, length, &unicodestr, 0); + + /* Write multi-byte header */ + fwrite(BOM_UTF16_LE, sizeof(BYTE), 2, fp); + fwrite(unicodestr, 2, length, fp); + + free(unicodestr); + } + else + { + fwrite(buffer, 1, length, fp); + } + + status = fflush(fp); + status = fclose(fp); + } + + if (buffer) free(buffer); - return (rc == 0); + return (status == 0) ? TRUE : FALSE; } -#define WRITE_RDP_FILE_DECLARE(_file, _buffer, _size) \ - const rdpFile* __rdpFile = file; \ - char* __buffer = _buffer; \ - size_t __size = _size; \ - size_t __required_size = 0; \ - int __current = 0; \ - int __count = 0; - -#define WRITE_RDP_FILE_VALUE_INTEGER(_format, _field) \ -if (~__rdpFile->_field) \ - { \ - if (__buffer) \ - __count = sprintf_s(__buffer + __current, __size - __required_size, _format, (int) __rdpFile->_field); \ - else \ - __count = _scprintf(_format, (int) __rdpFile->_field); \ - \ - __required_size += __count; \ - __current += __count; \ - } - -#define WRITE_RDP_FILE_VALUE_STRING(_format, _field) \ - if (~((size_t) __rdpFile->_field) && __rdpFile->_field != NULL) \ - { \ - if (__buffer) \ - __count = sprintf_s(__buffer + __current, __size - __required_size, _format, __rdpFile->_field); \ - else \ - __count = _scprintf(_format, __rdpFile->_field); \ - \ - __required_size += __count; \ - __current += __count; \ - } - -#define WRITE_RDP_FILE_VALUE_RETURN \ - return __required_size; - size_t freerdp_client_write_rdp_file_buffer(const rdpFile* file, char* buffer, size_t size) { - WRITE_RDP_FILE_DECLARE(file, buffer, size) + int index; + int length; + char* output; + rdpFileLine* line; - WRITE_RDP_FILE_VALUE_INTEGER("screen mode id:i:%d\n", ScreenModeId); - WRITE_RDP_FILE_VALUE_INTEGER("use multimon:i:%d\n", UseMultiMon); - WRITE_RDP_FILE_VALUE_INTEGER("desktopwidth:i:%d\n", DesktopWidth); - WRITE_RDP_FILE_VALUE_INTEGER("desktopheight:i:%d\n", DesktopHeight); - WRITE_RDP_FILE_VALUE_INTEGER("session bpp:i:%d\n", SessionBpp); - WRITE_RDP_FILE_VALUE_STRING("winposstr:s:%s\n", WinPosStr); - WRITE_RDP_FILE_VALUE_INTEGER("compression:i:%d\n", Compression); - WRITE_RDP_FILE_VALUE_INTEGER("keyboardhook:i:%d\n", KeyboardHook); - WRITE_RDP_FILE_VALUE_INTEGER("audiocapturemode:i:%d\n", AudioCaptureMode); - WRITE_RDP_FILE_VALUE_INTEGER("videoplaybackmode:i:%d\n", VideoPlaybackMode); - WRITE_RDP_FILE_VALUE_INTEGER("connection type:i:%d\n", ConnectionType); - WRITE_RDP_FILE_VALUE_INTEGER("networkautodetect:i:%d\n", NetworkAutoDetect); - WRITE_RDP_FILE_VALUE_INTEGER("bandwidthautodetect:i:%d\n", BandwidthAutoDetect); - WRITE_RDP_FILE_VALUE_INTEGER("displayconnectionbar:i:%d\n", DisplayConnectionBar); - WRITE_RDP_FILE_VALUE_INTEGER("enableworkspacereconnect:i:%d\n", EnableWorkspaceReconnect); - WRITE_RDP_FILE_VALUE_INTEGER("disable wallpaper:i:%d\n", DisableWallpaper); - WRITE_RDP_FILE_VALUE_INTEGER("allow font smoothing:i:%d\n", AllowFontSmoothing); - WRITE_RDP_FILE_VALUE_INTEGER("allow desktop composition:i:%d\n", AllowDesktopComposition); - WRITE_RDP_FILE_VALUE_INTEGER("disable full window drag:i:%d\n", DisableFullWindowDrag); - WRITE_RDP_FILE_VALUE_INTEGER("disable menu anims:i:%d\n", DisableMenuAnims); - WRITE_RDP_FILE_VALUE_INTEGER("disable themes:i:%d\n", DisableThemes); - WRITE_RDP_FILE_VALUE_INTEGER("disable cursor setting:i:%d\n", DisableCursorSetting); - WRITE_RDP_FILE_VALUE_INTEGER("bitmapcachepersistenable:i:%d\n", BitmapCachePersistEnable); - WRITE_RDP_FILE_VALUE_STRING("full address:s:%s\n", FullAddress); - WRITE_RDP_FILE_VALUE_INTEGER("audiomode:i:%d\n", AudioMode); - WRITE_RDP_FILE_VALUE_INTEGER("redirectprinters:i:%d\n", RedirectPrinters); - WRITE_RDP_FILE_VALUE_INTEGER("redirectcomports:i:%d\n", RedirectComPorts); - WRITE_RDP_FILE_VALUE_INTEGER("redirectsmartcards:i:%d\n", RedirectSmartCards); - WRITE_RDP_FILE_VALUE_INTEGER("redirectclipboard:i:%d\n", RedirectClipboard); - WRITE_RDP_FILE_VALUE_INTEGER("redirectposdevices:i:%d\n", RedirectPosDevices); - WRITE_RDP_FILE_VALUE_INTEGER("autoreconnection enabled:i:%d\n", AutoReconnectionEnabled); - WRITE_RDP_FILE_VALUE_INTEGER("authentication level:i:%d\n", AuthenticationLevel); - WRITE_RDP_FILE_VALUE_INTEGER("prompt for credentials:i:%d\n", PromptForCredentials); - WRITE_RDP_FILE_VALUE_INTEGER("negotiate security layer:i:%d\n", NegotiateSecurityLayer); - WRITE_RDP_FILE_VALUE_INTEGER("remoteapplicationmode:i:%d\n", RemoteApplicationMode); - WRITE_RDP_FILE_VALUE_STRING("alternate shell:s:%s\n", AlternateShell); - WRITE_RDP_FILE_VALUE_STRING("shell working directory:s:%s\n", ShellWorkingDirectory); - WRITE_RDP_FILE_VALUE_STRING("gatewayhostname:s:%s\n", GatewayHostname); - WRITE_RDP_FILE_VALUE_INTEGER("gatewayusagemethod:i:%d\n", GatewayUsageMethod); - WRITE_RDP_FILE_VALUE_INTEGER("gatewaycredentialssource:i:%d\n", GatewayCredentialsSource); - WRITE_RDP_FILE_VALUE_INTEGER("gatewayprofileusagemethod:i:%d\n", GatewayProfileUsageMethod); - WRITE_RDP_FILE_VALUE_INTEGER("promptcredentialonce:i:%d\n", PromptCredentialOnce); - WRITE_RDP_FILE_VALUE_INTEGER("use redirection server name:i:%d\n", UseRedirectionServerName); - WRITE_RDP_FILE_VALUE_INTEGER("rdgiskdcproxy:i:%d\n", RdgIsKdcProxy); - WRITE_RDP_FILE_VALUE_STRING("kdcproxyname:s:%s\n", KdcProxyName); - WRITE_RDP_FILE_VALUE_STRING("drivestoredirect:s:%s\n", DrivesToRedirect); - WRITE_RDP_FILE_VALUE_STRING("username:s:%s\n", Username); - WRITE_RDP_FILE_VALUE_STRING("domain:s:%s\n", Domain); + if (!buffer) + size = 0; - WRITE_RDP_FILE_VALUE_RETURN + output = buffer; + + for (index = 0; index < file->lineCount; index++) + { + line = &(file->lines[index]); + + length = strlen(line->text); + + if (!buffer) + { + size += length + 1; + } + else + { + CopyMemory(output, line->text, length); + output += length; + *output = '\n'; + output++; + } + } + + if (buffer) + size = (output - buffer); + + return size; } BOOL freerdp_client_populate_settings_from_rdp_file(rdpFile* file, rdpSettings* settings) @@ -966,7 +914,7 @@ BOOL freerdp_client_populate_settings_from_rdp_file(rdpFile* file, rdpSettings* if (~file->DisableThemes) freerdp_set_param_bool(settings, FreeRDP_DisableThemes, file->DisableThemes); if (~file->AllowDesktopComposition) - freerdp_set_param_bool(settings, FreeRDP_DisableCursorShadow, file->AllowDesktopComposition); + freerdp_set_param_bool(settings, FreeRDP_AllowDesktopComposition, file->AllowDesktopComposition); if (~file->BitmapCachePersistEnable) freerdp_set_param_bool(settings, FreeRDP_BitmapCachePersistEnabled, file->BitmapCachePersistEnable); @@ -1105,7 +1053,7 @@ int freerdp_client_rdp_file_set_string_option(rdpFile* file, const char* name, c length = _scprintf("%s:s:%s", name, value); text = (char*) malloc(length + 1); - sprintf_s(text, length, "%s:s:%s", name, value); + sprintf_s(text, length + 1, "%s:s:%s", name, value ? value : ""); text[length] = '\0'; if (line) @@ -1155,7 +1103,7 @@ int freerdp_client_rdp_file_set_integer_option(rdpFile* file, const char* name, length = _scprintf("%s:i:%d", name, value); text = (char*) malloc(length + 1); - sprintf_s(text, length, "%s:i:%d", name, value); + sprintf_s(text, length + 1, "%s:i:%d", name, value); text[length] = '\0'; if (line) From 84137cb76d9fd64fdb7202b49c8459543c4f31ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 4 Dec 2013 23:46:58 -0500 Subject: [PATCH 068/149] libwinpr-utils: create wlog output directory --- winpr/libwinpr/utils/wlog/Message.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/winpr/libwinpr/utils/wlog/Message.c b/winpr/libwinpr/utils/wlog/Message.c index b5c4c111b..4f313c4a4 100644 --- a/winpr/libwinpr/utils/wlog/Message.c +++ b/winpr/libwinpr/utils/wlog/Message.c @@ -39,6 +39,9 @@ char* WLog_Message_GetOutputFileName(int id, const char* ext) FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog"); + if (!PathFileExistsA(FilePath)) + CreateDirectoryA(FilePath, NULL); + FileName = (char*) malloc(256); if (id >= 0) From 978527576946ab0dddb0c0ff6821a86e4da8f519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Thu, 5 Dec 2013 12:12:55 -0500 Subject: [PATCH 069/149] Fix warnings --- CMakeLists.txt | 2 ++ winpr/libwinpr/utils/wlog/Message.c | 1 + 2 files changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa71dd2cd..4785ca99a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,6 +144,7 @@ if(CMAKE_COMPILER_IS_GNUCC) CHECK_C_COMPILER_FLAG(-Wno-deprecated-declarations Wno-deprecated-declarations) if(Wno-deprecated-declarations) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") endif() if(NOT EXPORT_ALL_SYMBOLS) message(STATUS "GCC default symbol visibility: hidden") @@ -176,6 +177,7 @@ if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") CHECK_C_COMPILER_FLAG(-Wno-deprecated-declarations Wno-deprecated-declarations) if(Wno-deprecated-declarations) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") endif() endif() diff --git a/winpr/libwinpr/utils/wlog/Message.c b/winpr/libwinpr/utils/wlog/Message.c index 4f313c4a4..bb2fa5595 100644 --- a/winpr/libwinpr/utils/wlog/Message.c +++ b/winpr/libwinpr/utils/wlog/Message.c @@ -23,6 +23,7 @@ #include #include +#include #include From f963491ebe558b4d6931c1dc933349b1424dc5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 5 Dec 2013 12:35:31 -0500 Subject: [PATCH 070/149] libfreerdp-client: cleanup .rdp file parsing --- client/common/client.c | 26 -------------------------- client/common/file.c | 2 -- winpr/libwinpr/utils/wlog/Message.c | 1 + 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/client/common/client.c b/client/common/client.c index 4dc46008c..1fc565f9a 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -160,32 +160,6 @@ int freerdp_client_settings_write_connection_file(const rdpSettings* settings, c if (!freerdp_client_populate_rdp_file_from_settings(file, settings)) return -1; - { - int index; - rdpFileLine* line; - - for (index = 0; index < file->lineCount; index++) - { - line = &(file->lines[index]); - - if (line->flags & RDP_FILE_LINE_FLAG_FORMATTED) - { - if (line->flags & RDP_FILE_LINE_FLAG_TYPE_STRING) - { - printf("line %02d: name: %s value: %s, %s\n", - line->index, line->name, line->sValue, - (line->flags & RDP_FILE_LINE_FLAG_STANDARD) ? "standard" : "non-standard"); - } - else if (line->flags & RDP_FILE_LINE_FLAG_TYPE_INTEGER) - { - printf("line %02d: name: %s value: %d, %s\n", - line->index, line->name, line->iValue, - (line->flags & RDP_FILE_LINE_FLAG_STANDARD) ? "standard" : "non-standard"); - } - } - } - } - if (!freerdp_client_write_rdp_file(file, filename, unicode)) return -1; diff --git a/client/common/file.c b/client/common/file.c index 6e93f3a95..3cf052f14 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -893,8 +893,6 @@ BOOL freerdp_client_populate_settings_from_rdp_file(rdpFile* file, rdpSettings* freerdp_set_param_string(settings, FreeRDP_RemoteApplicationIcon, file->RemoteApplicationIcon); if (~((size_t) file->RemoteApplicationFile)) freerdp_set_param_string(settings, FreeRDP_RemoteApplicationGuid, file->RemoteApplicationGuid); - if (~((size_t) file->RemoteApplicationGuid)) - freerdp_set_param_string(settings, FreeRDP_RemoteApplicationGuid, file->RemoteApplicationGuid); if (~((size_t) file->RemoteApplicationCmdLine)) freerdp_set_param_string(settings, FreeRDP_RemoteApplicationCmdLine, file->RemoteApplicationCmdLine); diff --git a/winpr/libwinpr/utils/wlog/Message.c b/winpr/libwinpr/utils/wlog/Message.c index 4f313c4a4..bb2fa5595 100644 --- a/winpr/libwinpr/utils/wlog/Message.c +++ b/winpr/libwinpr/utils/wlog/Message.c @@ -23,6 +23,7 @@ #include #include +#include #include From 4d6f3b6de46420c6670d523d88683b48118d7954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 5 Dec 2013 16:55:28 -0500 Subject: [PATCH 071/149] libfreerdp-client: fix bug in pInterface channel registration --- channels/cliprdr/client/cliprdr_main.c | 2 +- channels/rail/client/rail_main.c | 28 ++--- include/freerdp/svc.h | 3 +- include/freerdp/utils/svc_plugin.h | 4 +- libfreerdp/utils/svc_plugin.c | 136 +++++++++++-------------- 5 files changed, 79 insertions(+), 94 deletions(-) diff --git a/channels/cliprdr/client/cliprdr_main.c b/channels/cliprdr/client/cliprdr_main.c index 24c9e8b07..99a1b7b1f 100644 --- a/channels/cliprdr/client/cliprdr_main.c +++ b/channels/cliprdr/client/cliprdr_main.c @@ -57,7 +57,7 @@ CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr) { CliprdrClientContext* pInterface; rdpSvcPlugin* plugin = (rdpSvcPlugin*) cliprdr; - pInterface = (CliprdrClientContext*) *(plugin->channel_entry_points.ppInterface); + pInterface = (CliprdrClientContext*) plugin->channel_entry_points.pInterface; return pInterface; } diff --git a/channels/rail/client/rail_main.c b/channels/rail/client/rail_main.c index 19c7e5e7f..7dd557fec 100644 --- a/channels/rail/client/rail_main.c +++ b/channels/rail/client/rail_main.c @@ -42,7 +42,7 @@ RailClientContext* rail_get_client_interface(void* railObject) { RailClientContext* pInterface; rdpSvcPlugin* plugin = (rdpSvcPlugin*) railObject; - pInterface = (RailClientContext*) *(plugin->channel_entry_points.ppInterface); + pInterface = (RailClientContext*) plugin->channel_entry_points.pInterface; return pInterface; } @@ -503,25 +503,25 @@ int rail_server_get_appid_response(RailClientContext* context, RAIL_GET_APPID_RE int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) { - railPlugin* _p; + railPlugin* rail; RailClientContext* context; CHANNEL_ENTRY_POINTS_EX* pEntryPointsEx; - _p = (railPlugin*) malloc(sizeof(railPlugin)); - ZeroMemory(_p, sizeof(railPlugin)); + rail = (railPlugin*) malloc(sizeof(railPlugin)); + ZeroMemory(rail, sizeof(railPlugin)); - _p->plugin.channel_def.options = + rail->plugin.channel_def.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL; - strcpy(_p->plugin.channel_def.name, "rail"); + strcpy(rail->plugin.channel_def.name, "rail"); - _p->plugin.connect_callback = rail_process_connect; - _p->plugin.receive_callback = rail_process_receive; - _p->plugin.event_callback = rail_process_event; - _p->plugin.terminate_callback = rail_process_terminate; + rail->plugin.connect_callback = rail_process_connect; + rail->plugin.receive_callback = rail_process_receive; + rail->plugin.event_callback = rail_process_event; + rail->plugin.terminate_callback = rail_process_terminate; pEntryPointsEx = (CHANNEL_ENTRY_POINTS_EX*) pEntryPoints; @@ -530,7 +530,7 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) { context = (RailClientContext*) malloc(sizeof(RailClientContext)); - context->handle = (void*) _p; + context->handle = (void*) rail; context->ClientExecute = rail_client_execute; context->ClientActivate = rail_client_activate; @@ -557,11 +557,11 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) } WLog_Init(); - _p->log = WLog_Get("com.freerdp.channels.rail.client"); + rail->log = WLog_Get("com.freerdp.channels.rail.client"); - WLog_Print(_p->log, WLOG_DEBUG, "VirtualChannelEntry"); + WLog_Print(rail->log, WLOG_DEBUG, "VirtualChannelEntry"); - svc_plugin_init((rdpSvcPlugin*) _p, pEntryPoints); + svc_plugin_init((rdpSvcPlugin*) rail, pEntryPoints); return 1; } diff --git a/include/freerdp/svc.h b/include/freerdp/svc.h index 519bd9e40..36213e9d8 100644 --- a/include/freerdp/svc.h +++ b/include/freerdp/svc.h @@ -109,7 +109,8 @@ struct _CHANNEL_ENTRY_POINTS_EX /* Extended Fields */ UINT32 MagicNumber; /* identifies FreeRDP */ void* pExtendedData; /* extended initial data */ - void** ppInterface; /* channel callback interface */ + void* pInterface; /* channel callback interface, use after initialization */ + void** ppInterface; /* channel callback interface, use for initialization */ PVIRTUALCHANNELEVENTPUSH pVirtualChannelEventPush; }; typedef struct _CHANNEL_ENTRY_POINTS_EX CHANNEL_ENTRY_POINTS_EX; diff --git a/include/freerdp/utils/svc_plugin.h b/include/freerdp/utils/svc_plugin.h index 7feeb7f7f..4b8376733 100644 --- a/include/freerdp/utils/svc_plugin.h +++ b/include/freerdp/utils/svc_plugin.h @@ -50,8 +50,8 @@ struct rdp_svc_plugin HANDLE thread; wStream* data_in; - void* init_handle; - UINT32 open_handle; + void* InitHandle; + UINT32 OpenHandle; wMessagePipe* MsgPipe; }; diff --git a/libfreerdp/utils/svc_plugin.c b/libfreerdp/utils/svc_plugin.c index f1fb44146..884b6c237 100644 --- a/libfreerdp/utils/svc_plugin.c +++ b/libfreerdp/utils/svc_plugin.c @@ -37,79 +37,57 @@ #include #include -static wArrayList* g_AddinList = NULL; +static wListDictionary* g_InitHandles; +static wListDictionary* g_OpenHandles; -rdpSvcPlugin* svc_plugin_find_by_init_handle(void* init_handle) +void svc_plugin_add_init_handle_data(void* pInitHandle, void* pUserData) { - int index; - BOOL found = FALSE; - rdpSvcPlugin* plugin; + if (!g_InitHandles) + g_InitHandles = ListDictionary_New(TRUE); - ArrayList_Lock(g_AddinList); - - index = 0; - plugin = (rdpSvcPlugin*) ArrayList_GetItem(g_AddinList, index++); - - while (plugin) - { - if (plugin->init_handle == init_handle) - { - found = TRUE; - break; - } - - plugin = (rdpSvcPlugin*) ArrayList_GetItem(g_AddinList, index++); - } - - ArrayList_Unlock(g_AddinList); - - return (found) ? plugin : NULL; + ListDictionary_Add(g_InitHandles, pInitHandle, pUserData); } -rdpSvcPlugin* svc_plugin_find_by_open_handle(UINT32 open_handle) +void* svc_plugin_get_init_handle_data(void* pInitHandle) { - int index; - BOOL found = FALSE; - rdpSvcPlugin* plugin; - - ArrayList_Lock(g_AddinList); - - index = 0; - plugin = (rdpSvcPlugin*) ArrayList_GetItem(g_AddinList, index++); - - while (plugin) - { - if (plugin->open_handle == open_handle) - { - found = TRUE; - break; - } - - plugin = (rdpSvcPlugin*) ArrayList_GetItem(g_AddinList, index++); - } - - ArrayList_Unlock(g_AddinList); - - return (found) ? plugin : NULL; + void* pUserData = NULL; + pUserData = ListDictionary_GetItemValue(g_InitHandles, pInitHandle); + return pUserData; } -void svc_plugin_add(rdpSvcPlugin* plugin) +void svc_plugin_remove_init_handle_data(void* pInitHandle) { - if (!g_AddinList) - g_AddinList = ArrayList_New(TRUE); - - ArrayList_Add(g_AddinList, (void*) plugin); + ListDictionary_Remove(g_InitHandles, pInitHandle); } -void svc_plugin_remove(rdpSvcPlugin* plugin) +void svc_plugin_add_open_handle_data(DWORD openHandle, void* pUserData) { - ArrayList_Remove(g_AddinList, (void*) plugin); + void* pOpenHandle = (void*) (size_t) openHandle; + + if (!g_OpenHandles) + g_OpenHandles = ListDictionary_New(TRUE); + + ListDictionary_Add(g_OpenHandles, pOpenHandle, pUserData); +} + +void* svc_plugin_get_open_handle_data(DWORD openHandle) +{ + void* pUserData = NULL; + void* pOpenHandle = (void*) (size_t) openHandle; + pUserData = ListDictionary_GetItemValue(g_OpenHandles, pOpenHandle); + return pUserData; +} + +void svc_plugin_remove_open_handle_data(DWORD openHandle) +{ + void* pOpenHandle = (void*) (size_t) openHandle; + ListDictionary_Remove(g_OpenHandles, pOpenHandle); } static void svc_plugin_process_received(rdpSvcPlugin* plugin, void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags) { - wStream* data_in; + wStream* s; if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME)) { @@ -130,22 +108,22 @@ static void svc_plugin_process_received(rdpSvcPlugin* plugin, void* pData, UINT3 plugin->data_in = Stream_New(NULL, totalLength); } - data_in = plugin->data_in; - Stream_EnsureRemainingCapacity(data_in, (int) dataLength); - Stream_Write(data_in, pData, dataLength); + s = plugin->data_in; + Stream_EnsureRemainingCapacity(s, (int) dataLength); + Stream_Write(s, pData, dataLength); if (dataFlags & CHANNEL_FLAG_LAST) { - if (Stream_Capacity(data_in) != Stream_GetPosition(data_in)) + if (Stream_Capacity(s) != Stream_GetPosition(s)) { fprintf(stderr, "svc_plugin_process_received: read error\n"); } plugin->data_in = NULL; - Stream_SealLength(data_in); - Stream_SetPosition(data_in, 0); + Stream_SealLength(s); + Stream_SetPosition(s, 0); - MessageQueue_Post(plugin->MsgPipe->In, NULL, 0, (void*) data_in, NULL); + MessageQueue_Post(plugin->MsgPipe->In, NULL, 0, (void*) s, NULL); } } @@ -162,7 +140,7 @@ static void svc_plugin_open_event(UINT32 openHandle, UINT32 event, void* pData, DEBUG_SVC("openHandle %d event %d dataLength %d totalLength %d dataFlags %d", openHandle, event, dataLength, totalLength, dataFlags); - plugin = (rdpSvcPlugin*) svc_plugin_find_by_open_handle(openHandle); + plugin = (rdpSvcPlugin*) svc_plugin_get_open_handle_data(openHandle); if (!plugin) { @@ -233,8 +211,8 @@ static void svc_plugin_process_connected(rdpSvcPlugin* plugin, void* pData, UINT { UINT32 status; - status = plugin->channel_entry_points.pVirtualChannelOpen(plugin->init_handle, - &plugin->open_handle, plugin->channel_def.name, svc_plugin_open_event); + status = plugin->channel_entry_points.pVirtualChannelOpen(plugin->InitHandle, + &(plugin->OpenHandle), plugin->channel_def.name, svc_plugin_open_event); if (status != CHANNEL_RC_OK) { @@ -242,6 +220,8 @@ static void svc_plugin_process_connected(rdpSvcPlugin* plugin, void* pData, UINT return; } + svc_plugin_add_open_handle_data(plugin->OpenHandle, plugin); + plugin->MsgPipe = MessagePipe_New(); plugin->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) svc_plugin_thread_func, (void*) plugin, 0, NULL); @@ -255,9 +235,7 @@ static void svc_plugin_process_terminated(rdpSvcPlugin* plugin) MessagePipe_Free(plugin->MsgPipe); CloseHandle(plugin->thread); - plugin->channel_entry_points.pVirtualChannelClose(plugin->open_handle); - - svc_plugin_remove(plugin); + plugin->channel_entry_points.pVirtualChannelClose(plugin->OpenHandle); if (plugin->data_in) { @@ -266,6 +244,9 @@ static void svc_plugin_process_terminated(rdpSvcPlugin* plugin) } IFCALL(plugin->terminate_callback, plugin); + + svc_plugin_remove_open_handle_data(plugin->OpenHandle); + svc_plugin_remove_init_handle_data(plugin->InitHandle); } static void svc_plugin_init_event(void* pInitHandle, UINT32 event, void* pData, UINT32 dataLength) @@ -274,7 +255,7 @@ static void svc_plugin_init_event(void* pInitHandle, UINT32 event, void* pData, DEBUG_SVC("event %d", event); - plugin = (rdpSvcPlugin*) svc_plugin_find_by_init_handle(pInitHandle); + plugin = (rdpSvcPlugin*) svc_plugin_get_init_handle_data(pInitHandle); if (!plugin) { @@ -304,12 +285,15 @@ void svc_plugin_init(rdpSvcPlugin* plugin, CHANNEL_ENTRY_POINTS* pEntryPoints) * VirtualChannelInit at a time. So this should be safe. */ - CopyMemory(&plugin->channel_entry_points, pEntryPoints, pEntryPoints->cbSize); + CopyMemory(&(plugin->channel_entry_points), pEntryPoints, pEntryPoints->cbSize); - svc_plugin_add(plugin); + plugin->channel_entry_points.pVirtualChannelInit(&(plugin->InitHandle), + &(plugin->channel_def), 1, VIRTUAL_CHANNEL_VERSION_WIN2000, svc_plugin_init_event); - plugin->channel_entry_points.pVirtualChannelInit(&plugin->init_handle, - &plugin->channel_def, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, svc_plugin_init_event); + plugin->channel_entry_points.pInterface = *(plugin->channel_entry_points.ppInterface); + plugin->channel_entry_points.ppInterface = &(plugin->channel_entry_points.pInterface); + + svc_plugin_add_init_handle_data(plugin->InitHandle, plugin); } int svc_plugin_send(rdpSvcPlugin* plugin, wStream* data_out) @@ -321,7 +305,7 @@ int svc_plugin_send(rdpSvcPlugin* plugin, wStream* data_out) if (!plugin) status = CHANNEL_RC_BAD_INIT_HANDLE; else - status = plugin->channel_entry_points.pVirtualChannelWrite(plugin->open_handle, + status = plugin->channel_entry_points.pVirtualChannelWrite(plugin->OpenHandle, Stream_Buffer(data_out), Stream_GetPosition(data_out), data_out); if (status != CHANNEL_RC_OK) @@ -340,7 +324,7 @@ int svc_plugin_send_event(rdpSvcPlugin* plugin, wMessage* event) DEBUG_SVC("event class: %d type: %d", GetMessageClass(event->id), GetMessageType(event->id)); - status = plugin->channel_entry_points.pVirtualChannelEventPush(plugin->open_handle, event); + status = plugin->channel_entry_points.pVirtualChannelEventPush(plugin->OpenHandle, event); if (status != CHANNEL_RC_OK) fprintf(stderr, "svc_plugin_send_event: VirtualChannelEventPush failed %d\n", status); From 85b7ad90fe8d932cd6bbf53d51b0b1b4795e656b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 5 Dec 2013 17:31:33 -0500 Subject: [PATCH 072/149] libfreerdp-core: fix possible crash on unauthorized TS Gateway error --- libfreerdp/core/gateway/rpc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libfreerdp/core/gateway/rpc.c b/libfreerdp/core/gateway/rpc.c index ae024cd9e..5a3760f35 100644 --- a/libfreerdp/core/gateway/rpc.c +++ b/libfreerdp/core/gateway/rpc.c @@ -360,6 +360,12 @@ int rpc_write(rdpRpc* rpc, BYTE* data, int length, UINT16 opnum) ntlm = rpc->ntlm; + if ((!ntlm) || (!ntlm->table)) + { + fprintf(stderr, "rpc_write: invalid ntlm context\n"); + return -1; + } + if (ntlm->table->QueryContextAttributes(&ntlm->context, SECPKG_ATTR_SIZES, &ntlm->ContextSizes) != SEC_E_OK) { fprintf(stderr, "QueryContextAttributes SECPKG_ATTR_SIZES failure\n"); From a86168fe8f6d7bd32ba6f54a0fadd1f156119fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Fri, 6 Dec 2013 09:14:55 -0500 Subject: [PATCH 073/149] added const to freerdp_client_get_string_option --- client/common/file.c | 2 +- include/freerdp/client/file.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/common/file.c b/client/common/file.c index 6e93f3a95..56f26ecf5 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -1077,7 +1077,7 @@ int freerdp_client_rdp_file_set_string_option(rdpFile* file, const char* name, c return 0; } -char* freerdp_client_rdp_file_get_string_option(rdpFile* file, const char* name) +const char* freerdp_client_rdp_file_get_string_option(rdpFile* file, const char* name) { rdpFileLine* line; diff --git a/include/freerdp/client/file.h b/include/freerdp/client/file.h index 2336b7dd1..adf535dcc 100644 --- a/include/freerdp/client/file.h +++ b/include/freerdp/client/file.h @@ -173,7 +173,7 @@ FREERDP_API BOOL freerdp_client_write_rdp_file(const rdpFile* file, const char* FREERDP_API size_t freerdp_client_write_rdp_file_buffer(const rdpFile* file, char* buffer, size_t size); FREERDP_API int freerdp_client_rdp_file_set_string_option(rdpFile* file, const char* name, const char* value); -FREERDP_API char* freerdp_client_rdp_file_get_string_option(rdpFile* file, const char* name); +FREERDP_API const char* freerdp_client_rdp_file_get_string_option(rdpFile* file, const char* name); FREERDP_API int freerdp_client_rdp_file_set_integer_option(rdpFile* file, const char* name, int value); FREERDP_API int freerdp_client_rdp_file_get_integer_option(rdpFile* file, const char* name); From d7b7fcc688a9d6dda78ab5f62d3fd7766a76b9a2 Mon Sep 17 00:00:00 2001 From: Hardening Date: Fri, 6 Dec 2013 23:25:31 +0100 Subject: [PATCH 074/149] Parse the Surface frame marker flag This patch adds the parsing for the surface framemarker flag, so that we can test it later. --- libfreerdp/core/capabilities.c | 9 ++++++--- libfreerdp/core/settings.c | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/libfreerdp/core/capabilities.c b/libfreerdp/core/capabilities.c index 4da339677..606500964 100644 --- a/libfreerdp/core/capabilities.c +++ b/libfreerdp/core/capabilities.c @@ -2387,13 +2387,15 @@ BOOL rdp_print_large_pointer_capability_set(wStream* s, UINT16 length) BOOL rdp_read_surface_commands_capability_set(wStream* s, UINT16 length, rdpSettings* settings) { + UINT32 cmdFlags; if (length < 12) return FALSE; - Stream_Seek_UINT32(s); /* cmdFlags (4 bytes) */ + Stream_Read_UINT32(s, cmdFlags); /* cmdFlags (4 bytes) */ Stream_Seek_UINT32(s); /* reserved (4 bytes) */ settings->SurfaceCommandsEnabled = TRUE; + settings->SurfaceFrameMarkerEnabled = (cmdFlags & SURFCMDS_FRAME_MARKER); return TRUE; } @@ -2414,9 +2416,10 @@ void rdp_write_surface_commands_capability_set(wStream* s, rdpSettings* settings header = rdp_capability_set_start(s); - cmdFlags = SURFCMDS_FRAME_MARKER | - SURFCMDS_SET_SURFACE_BITS | + cmdFlags = SURFCMDS_SET_SURFACE_BITS | SURFCMDS_STREAM_SURFACE_BITS; + if (settings->SurfaceFrameMarkerEnabled) + cmdFlags |= SURFCMDS_FRAME_MARKER; Stream_Write_UINT32(s, cmdFlags); /* cmdFlags (4 bytes) */ Stream_Write_UINT32(s, 0); /* reserved (4 bytes) */ diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 0edabb5c9..69a1bbf98 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -313,6 +313,7 @@ rdpSettings* freerdp_settings_new(DWORD flags) settings->DrawGdiPlusEnabled = FALSE; settings->FrameMarkerCommandEnabled = FALSE; + settings->SurfaceCommandsEnabled = FALSE; settings->BitmapCacheV3Enabled = FALSE; settings->BitmapCacheEnabled = TRUE; @@ -668,6 +669,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) _settings->SoundBeepsEnabled = settings->SoundBeepsEnabled; /* 2944 */ _settings->SurfaceCommandsEnabled = settings->SurfaceCommandsEnabled; /* 3520 */ _settings->FrameMarkerCommandEnabled = settings->FrameMarkerCommandEnabled; /* 3521 */ + _settings->SurfaceFrameMarkerEnabled = settings->SurfaceFrameMarkerEnabled; /* 3522 */ _settings->RemoteFxOnly = settings->RemoteFxOnly; /* 3648 */ _settings->RemoteFxCodec = settings->RemoteFxCodec; /* 3649 */ _settings->RemoteFxImageCodec = settings->RemoteFxImageCodec; /* 3652 */ From 8c1f836ac89f9cc93bc4d0ae51be86f4c865d51e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Fri, 6 Dec 2013 22:15:45 -0500 Subject: [PATCH 075/149] =?UTF-8?q?-=20SSL=20verification=20callback:=20se?= =?UTF-8?q?nd=20correct=20hostname=20and=20port=20-=20Gateway=20Authentica?= =?UTF-8?q?tion=20callback.=20-=20Handling=20=E2=80=9Cuse=20same=20credent?= =?UTF-8?q?ials=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/freerdp/crypto/tls.h | 2 ++ include/freerdp/freerdp.h | 6 ++++- libfreerdp/core/gateway/ncacn_http.c | 40 +++++++++++++++++++++++----- libfreerdp/core/gateway/ntlm.c | 15 ++++++++--- libfreerdp/core/gateway/rpc_bind.c | 27 +++++++++++++++++++ libfreerdp/core/transport.c | 22 +++++++++++++++ libfreerdp/crypto/tls.c | 13 +-------- 7 files changed, 103 insertions(+), 22 deletions(-) diff --git a/include/freerdp/crypto/tls.h b/include/freerdp/crypto/tls.h index 4c1be79bf..712f82a87 100644 --- a/include/freerdp/crypto/tls.h +++ b/include/freerdp/crypto/tls.h @@ -49,6 +49,8 @@ struct rdp_tls rdpSettings* settings; SecPkgContext_Bindings* Bindings; rdpCertificateStore* certificate_store; + char* hostname; + int port; }; FREERDP_API BOOL tls_connect(rdpTls* tls); diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 1f769af95..9ab42bb35 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -204,7 +204,11 @@ struct rdp_freerdp Callback for cleaning up resources allocated by connect callbacks. */ - UINT64 paddingD[64 - 56]; /* 56 */ + ALIGN64 pAuthenticate GatewayAuthenticate; /**< (offset 56) + Callback for gateway authentication. + It is used to get the username/password when it was not provided at connection time. */ + + UINT64 paddingD[64 - 57]; /* 57 */ ALIGN64 pSendChannelData SendChannelData; /* (offset 64) Callback for sending data to a channel. diff --git a/libfreerdp/core/gateway/ncacn_http.c b/libfreerdp/core/gateway/ncacn_http.c index 4e3489d81..57ba66776 100644 --- a/libfreerdp/core/gateway/ncacn_http.c +++ b/libfreerdp/core/gateway/ncacn_http.c @@ -113,15 +113,40 @@ int rpc_ncacn_http_recv_in_channel_response(rdpRpc* rpc) int rpc_ncacn_http_ntlm_init(rdpRpc* rpc, TSG_CHANNEL channel) { rdpNtlm* ntlm = NULL; - rdpSettings* settings; - - settings = rpc->settings; + rdpSettings* settings = rpc->settings; + freerdp* instance = (freerdp*) rpc->settings->instance; + BOOL promptPassword = FALSE; if (channel == TSG_CHANNEL_IN) ntlm = rpc->NtlmHttpIn->ntlm; else if (channel == TSG_CHANNEL_OUT) ntlm = rpc->NtlmHttpOut->ntlm; + if ((!settings->GatewayPassword) || (!settings->GatewayUsername) + || (!strlen(settings->GatewayPassword)) || (!strlen(settings->GatewayUsername))) + { + promptPassword = TRUE; + } + + if (promptPassword) + { + if (instance->GatewayAuthenticate) + { + BOOL proceed = instance->GatewayAuthenticate(instance, + &settings->GatewayUsername, &settings->GatewayPassword, &settings->GatewayDomain); + + if (!proceed) + return 0; + + if (settings->GatewayUseSameCredentials) + { + settings->Username = _strdup(settings->GatewayUsername); + settings->Domain = _strdup(settings->GatewayDomain); + settings->Password = _strdup(settings->GatewayPassword); + } + } + } + ntlm_client_init(ntlm, TRUE, settings->GatewayUsername, settings->GatewayDomain, settings->GatewayPassword, rpc->TlsIn->Bindings); @@ -180,7 +205,7 @@ int rpc_ncacn_http_send_out_channel_request(rdpRpc* rpc) int rpc_ncacn_http_recv_out_channel_response(rdpRpc* rpc) { - int ntlm_token_length; + int ntlm_token_length = 0; BYTE* ntlm_token_data; HttpResponse* http_response; rdpNtlm* ntlm = rpc->NtlmHttpOut->ntlm; @@ -188,8 +213,11 @@ int rpc_ncacn_http_recv_out_channel_response(rdpRpc* rpc) http_response = http_response_recv(rpc->TlsOut); ntlm_token_data = NULL; - crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam), - &ntlm_token_data, &ntlm_token_length); + if (http_response && http_response->AuthParam) + { + crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam), + &ntlm_token_data, &ntlm_token_length); + } ntlm->inputBuffer[0].pvBuffer = ntlm_token_data; ntlm->inputBuffer[0].cbBuffer = ntlm_token_length; diff --git a/libfreerdp/core/gateway/ntlm.c b/libfreerdp/core/gateway/ntlm.c index 8ff4d2057..5764194d1 100644 --- a/libfreerdp/core/gateway/ntlm.c +++ b/libfreerdp/core/gateway/ntlm.c @@ -227,6 +227,12 @@ BOOL ntlm_authenticate(rdpNtlm* ntlm) } } + if ((!ntlm) || (!ntlm->table)) + { + fprintf(stderr, "rpc_write: invalid ntlm context\n"); + return FALSE; + } + status = ntlm->table->InitializeSecurityContext(&ntlm->credentials, (ntlm->haveContext) ? &ntlm->context : NULL, (ntlm->ServicePrincipalName) ? ntlm->ServicePrincipalName : NULL, @@ -270,9 +276,12 @@ void ntlm_client_uninit(rdpNtlm* ntlm) free(ntlm->identity.Password); free(ntlm->ServicePrincipalName); - ntlm->table->FreeCredentialsHandle(&ntlm->credentials); - ntlm->table->FreeContextBuffer(ntlm->pPackageInfo); - ntlm->table->DeleteSecurityContext(&ntlm->context); + if (ntlm->table) + { + ntlm->table->FreeCredentialsHandle(&ntlm->credentials); + ntlm->table->FreeContextBuffer(ntlm->pPackageInfo); + ntlm->table->DeleteSecurityContext(&ntlm->context); + } } rdpNtlm* ntlm_new() diff --git a/libfreerdp/core/gateway/rpc_bind.c b/libfreerdp/core/gateway/rpc_bind.c index 4bc93f57e..7c2041758 100644 --- a/libfreerdp/core/gateway/rpc_bind.c +++ b/libfreerdp/core/gateway/rpc_bind.c @@ -97,11 +97,38 @@ int rpc_send_bind_pdu(rdpRpc* rpc) p_cont_elem_t* p_cont_elem; rpcconn_bind_hdr_t* bind_pdu; rdpSettings* settings = rpc->settings; + BOOL promptPassword = FALSE; + freerdp* instance = (freerdp*) settings->instance; DEBUG_RPC("Sending bind PDU"); rpc->ntlm = ntlm_new(); + if ((!settings->GatewayPassword) || (!settings->GatewayUsername) + || (!strlen(settings->GatewayPassword)) || (!strlen(settings->GatewayUsername))) + { + promptPassword = TRUE; + } + + if (promptPassword) + { + if (instance->GatewayAuthenticate) + { + BOOL proceed = instance->GatewayAuthenticate(instance, + &settings->GatewayUsername, &settings->GatewayPassword, &settings->GatewayDomain); + + if (!proceed) + return 0; + + if (settings->GatewayUseSameCredentials) + { + settings->Username = _strdup(settings->GatewayUsername); + settings->Domain = _strdup(settings->GatewayDomain); + settings->Password = _strdup(settings->GatewayPassword); + } + } + } + ntlm_client_init(rpc->ntlm, FALSE, settings->GatewayUsername, settings->GatewayDomain, settings->GatewayPassword, NULL); ntlm_client_make_spn(rpc->ntlm, NULL, settings->GatewayHostname); diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index bd3e75b6a..55ba1596b 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -222,6 +222,12 @@ BOOL transport_connect_tls(rdpTransport* transport) transport->layer = TRANSPORT_LAYER_TSG_TLS; + transport->TsgTls->hostname = transport->settings->ServerHostname; + transport->TsgTls->port = transport->settings->ServerPort; + + if (transport->TsgTls->port == 0) + transport->TsgTls->port = 3389; + if (!tls_connect(transport->TsgTls)) { if (!connectErrorCode) @@ -245,6 +251,12 @@ BOOL transport_connect_tls(rdpTransport* transport) transport->layer = TRANSPORT_LAYER_TLS; transport->TlsIn->sockfd = transport->TcpIn->sockfd; + transport->TlsIn->hostname = transport->settings->ServerHostname; + transport->TlsIn->port = transport->settings->ServerPort; + + if (transport->TlsIn->port == 0) + transport->TlsIn->port = 3389; + if (!tls_connect(transport->TlsIn)) { if (!connectErrorCode) @@ -313,11 +325,21 @@ BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 transport->TlsIn = tls_new(transport->settings); transport->TlsIn->sockfd = transport->TcpIn->sockfd; + transport->TlsIn->hostname = transport->settings->GatewayHostname; + transport->TlsIn->port = transport->settings->GatewayPort; + + if (transport->TlsIn->port == 0) + transport->TlsIn->port = 443; if (!transport->TlsOut) transport->TlsOut = tls_new(transport->settings); transport->TlsOut->sockfd = transport->TcpOut->sockfd; + transport->TlsOut->hostname = transport->settings->GatewayHostname; + transport->TlsOut->port = transport->settings->GatewayPort; + + if (transport->TlsOut->port == 0) + transport->TlsOut->port = 443; if (!tls_connect(transport->TlsIn)) return FALSE; diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 2d62e9386..166c259ad 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -214,18 +214,7 @@ BOOL tls_connect(rdpTls* tls) return FALSE; } - if (tls->settings->GatewayEnabled) - { - hostname = tls->settings->GatewayHostname; - port = tls->settings->GatewayPort; - } - else - { - hostname = tls->settings->ServerHostname; - port = tls->settings->ServerPort; - } - - if (!tls_verify_certificate(tls, cert, hostname, port)) + if (!tls_verify_certificate(tls, cert, tls->hostname, tls->port)) { fprintf(stderr, "tls_connect: certificate not trusted, aborting.\n"); tls_disconnect(tls); From 98245b817766071bb8843a3018b4ab62479b5d39 Mon Sep 17 00:00:00 2001 From: Hardening Date: Sun, 8 Dec 2013 11:22:36 +0100 Subject: [PATCH 076/149] Fixed typo in the initialization --- libfreerdp/core/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 69a1bbf98..982432f43 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -313,7 +313,7 @@ rdpSettings* freerdp_settings_new(DWORD flags) settings->DrawGdiPlusEnabled = FALSE; settings->FrameMarkerCommandEnabled = FALSE; - settings->SurfaceCommandsEnabled = FALSE; + settings->SurfaceFrameMarkerEnabled = TRUE; settings->BitmapCacheV3Enabled = FALSE; settings->BitmapCacheEnabled = TRUE; From b3ea5806165298fb375a4f3fbbc2601a6a86ed92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 8 Dec 2013 11:17:16 -0500 Subject: [PATCH 077/149] freerdp: add missing SurfaceFrameMarkerEnabled setting --- include/freerdp/settings.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index eaad54584..21a218e2f 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -699,6 +699,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_CompDeskSupportLevel 3456 #define FreeRDP_SurfaceCommandsEnabled 3520 #define FreeRDP_FrameMarkerCommandEnabled 3521 +#define FreeRDP_SurfaceFrameMarkerEnabled 3522 #define FreeRDP_RemoteFxOnly 3648 #define FreeRDP_RemoteFxCodec 3649 #define FreeRDP_RemoteFxCodecId 3650 @@ -1175,7 +1176,8 @@ struct rdp_settings /* Surface Commands Capabilities */ ALIGN64 BOOL SurfaceCommandsEnabled; /* 3520 */ ALIGN64 BOOL FrameMarkerCommandEnabled; /* 3521 */ - UINT64 padding3584[3584 - 3522]; /* 3522 */ + ALIGN64 BOOL SurfaceFrameMarkerEnabled; /* 3522 */ + UINT64 padding3584[3584 - 3523]; /* 3523 */ UINT64 padding3648[3648 - 3584]; /* 3584 */ /* From 6dfdc286ec2b10d0a5faaa3204c5e125369bc15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 8 Dec 2013 14:43:11 -0500 Subject: [PATCH 078/149] libfreerdp-core: disconnect client when accept fails --- libfreerdp/core/activation.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libfreerdp/core/activation.c b/libfreerdp/core/activation.c index edc31a295..eb5bb38dc 100644 --- a/libfreerdp/core/activation.c +++ b/libfreerdp/core/activation.c @@ -368,7 +368,8 @@ BOOL rdp_server_accept_client_font_list_pdu(rdpRdp* rdp, wStream* s) if (!rdp_send_server_font_map_pdu(rdp)) return FALSE; - rdp_server_transition_to_state(rdp, CONNECTION_STATE_ACTIVE); + if (rdp_server_transition_to_state(rdp, CONNECTION_STATE_ACTIVE) < 0) + return FALSE; return TRUE; } From 1add4e628f8b014b6c4d50618324332585ab1fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 8 Dec 2013 17:06:59 -0500 Subject: [PATCH 079/149] libfreerdp-codec: make planar codec encoder context reusable --- include/freerdp/codec/bitmap.h | 14 +- libfreerdp/codec/planar.c | 169 ++++++++++-------- libfreerdp/codec/planar.h | 32 +++- .../codec/test/TestFreeRDPCodecPlanar.c | 29 +-- 4 files changed, 148 insertions(+), 96 deletions(-) diff --git a/include/freerdp/codec/bitmap.h b/include/freerdp/codec/bitmap.h index 1dc7c25d9..6e09f2d8c 100644 --- a/include/freerdp/codec/bitmap.h +++ b/include/freerdp/codec/bitmap.h @@ -33,7 +33,17 @@ FREERDP_API BOOL bitmap_decompress(BYTE* srcData, BYTE* dstData, int width, int FREERDP_API int freerdp_bitmap_compress(char* in_data, int width, int height, wStream* s, int bpp, int byte_limit, int start_line, wStream* temp_s, int e); -FREERDP_API BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int height, - int scanline, BYTE* dstData, int* dstSize); +#define PLANAR_FORMAT_HEADER_CS (1 << 3) +#define PLANAR_FORMAT_HEADER_RLE (1 << 4) +#define PLANAR_FORMAT_HEADER_NA (1 << 5) +#define PLANAR_FORMAT_HEADER_CLL_MASK 0x07 + +typedef struct _BITMAP_PLANAR_CONTEXT BITMAP_PLANAR_CONTEXT; + +FREERDP_API BYTE* freerdp_bitmap_compress_planar(BITMAP_PLANAR_CONTEXT* context, BYTE* data, UINT32 format, + int width, int height, int scanline, BYTE* dstData, int* dstSize); + +FREERDP_API BITMAP_PLANAR_CONTEXT* freerdp_bitmap_planar_context_new(DWORD flags, int maxWidth, int maxHeight); +FREERDP_API void freerdp_bitmap_planar_context_free(BITMAP_PLANAR_CONTEXT* context); #endif /* FREERDP_CODEC_BITMAP_H */ diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 6829bd120..4a66f87f1 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -263,7 +263,7 @@ int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, in return (size == (srcp - srcData)) ? 0 : -1; } -int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]) +int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]) { int bpp; int i, j, k; @@ -629,7 +629,7 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei return outPlane; } -int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes, int* dstSizes) +int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[4], int width, int height, BYTE* outPlanes, int* dstSizes) { int outPlanesSize = width * height * 4; @@ -713,7 +713,7 @@ BYTE* freerdp_bitmap_planar_delta_encode_plane(BYTE* inPlane, int width, int hei return outPlane; } -int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5]) +int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[4], int width, int height, BYTE* outPlanes[4]) { freerdp_bitmap_planar_delta_encode_plane(inPlanes[0], width, height, outPlanes[0]); freerdp_bitmap_planar_delta_encode_plane(inPlanes[1], width, height, outPlanes[1]); @@ -723,88 +723,47 @@ int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[5], int width, int return 0; } -//static int g_Count = 0; - -BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* dstData, int* dstSize) +BYTE* freerdp_bitmap_compress_planar(BITMAP_PLANAR_CONTEXT* context, BYTE* data, UINT32 format, + int width, int height, int scanline, BYTE* dstData, int* dstSize) { int size; BYTE* dstp; int planeSize; int dstSizes[4]; - BYTE* planes[5]; - BYTE* planesBuffer; - BYTE* deltaPlanes[5]; - BYTE* deltaPlanesBuffer; - BYTE* rlePlanes[4]; - BYTE* rlePlanesBuffer; BYTE FormatHeader = 0; - FormatHeader |= PLANAR_FORMAT_HEADER_NA; + if (context->AllowSkipAlpha) + FormatHeader |= PLANAR_FORMAT_HEADER_NA; planeSize = width * height; - planesBuffer = malloc(planeSize * 5); - planes[0] = &planesBuffer[planeSize * 0]; - planes[1] = &planesBuffer[planeSize * 1]; - planes[2] = &planesBuffer[planeSize * 2]; - planes[3] = &planesBuffer[planeSize * 3]; - planes[4] = &planesBuffer[planeSize * 4]; + freerdp_split_color_planes(data, format, width, height, scanline, context->planes); - deltaPlanesBuffer = malloc(planeSize * 5); - deltaPlanes[0] = &deltaPlanesBuffer[planeSize * 0]; - deltaPlanes[1] = &deltaPlanesBuffer[planeSize * 1]; - deltaPlanes[2] = &deltaPlanesBuffer[planeSize * 2]; - deltaPlanes[3] = &deltaPlanesBuffer[planeSize * 3]; - deltaPlanes[4] = &deltaPlanesBuffer[planeSize * 4]; - - rlePlanesBuffer = malloc(planeSize * 4); - - freerdp_split_color_planes(data, format, width, height, scanline, planes); - - freerdp_bitmap_planar_delta_encode_planes(planes, width, height, deltaPlanes); - -#if 0 + if (context->AllowRunLengthEncoding) { - g_Count++; - BYTE* bitmap; - int bitmapLength; + freerdp_bitmap_planar_delta_encode_planes(context->planes, width, height, context->deltaPlanes); - bitmapLength = width * height * 4; - bitmap = malloc(width * height * 4); - ZeroMemory(bitmap, bitmapLength); + if (freerdp_bitmap_planar_compress_planes_rle(context->deltaPlanes, width, height, context->rlePlanesBuffer, (int*) &dstSizes) > 0) + { + int offset = 0; - freerdp_bitmap_planar_decompress_plane_raw(planes[0], width, height, bitmap + 3, planeSize); /* A */ - freerdp_bitmap_planar_decompress_plane_raw(planes[1], width, height, bitmap + 2, planeSize); /* R */ - freerdp_bitmap_planar_decompress_plane_raw(planes[2], width, height, bitmap + 1, planeSize); /* G */ - freerdp_bitmap_planar_decompress_plane_raw(planes[3], width, height, bitmap + 0, planeSize); /* B */ + FormatHeader |= PLANAR_FORMAT_HEADER_RLE; - printf("Bitmap %02d\n", g_Count); - winpr_CArrayDump(bitmap, bitmapLength, 32); + context->rlePlanes[0] = &context->rlePlanesBuffer[offset]; + offset += dstSizes[0]; - free(bitmap); - } -#endif + context->rlePlanes[1] = &context->rlePlanesBuffer[offset]; + offset += dstSizes[1]; - if (freerdp_bitmap_planar_compress_planes_rle(deltaPlanes, width, height, rlePlanesBuffer, (int*) &dstSizes) > 0) - { - int offset = 0; + context->rlePlanes[2] = &context->rlePlanesBuffer[offset]; + offset += dstSizes[2]; - FormatHeader |= PLANAR_FORMAT_HEADER_RLE; + context->rlePlanes[3] = &context->rlePlanesBuffer[offset]; + offset += dstSizes[3]; - rlePlanes[0] = &rlePlanesBuffer[offset]; - offset += dstSizes[0]; - - rlePlanes[1] = &rlePlanesBuffer[offset]; - offset += dstSizes[1]; - - rlePlanes[2] = &rlePlanesBuffer[offset]; - offset += dstSizes[2]; - - rlePlanes[3] = &rlePlanesBuffer[offset]; - offset += dstSizes[3]; - - printf("R: [%d/%d] G: [%d/%d] B: [%d/%d]\n", - dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize); + printf("R: [%d/%d] G: [%d/%d] B: [%d/%d]\n", + dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize); + } } if (!dstData) @@ -842,12 +801,12 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h { if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { - CopyMemory(dstp, rlePlanes[0], dstSizes[0]); /* Alpha */ + CopyMemory(dstp, context->rlePlanes[0], dstSizes[0]); /* Alpha */ dstp += dstSizes[0]; } else { - CopyMemory(dstp, planes[0], planeSize); /* Alpha */ + CopyMemory(dstp, context->planes[0], planeSize); /* Alpha */ dstp += planeSize; } } @@ -856,12 +815,12 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { - CopyMemory(dstp, rlePlanes[1], dstSizes[1]); /* Red */ + CopyMemory(dstp, context->rlePlanes[1], dstSizes[1]); /* Red */ dstp += dstSizes[1]; } else { - CopyMemory(dstp, planes[1], planeSize); /* Red */ + CopyMemory(dstp, context->planes[1], planeSize); /* Red */ dstp += planeSize; } @@ -869,12 +828,12 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { - CopyMemory(dstp, rlePlanes[2], dstSizes[2]); /* Green */ + CopyMemory(dstp, context->rlePlanes[2], dstSizes[2]); /* Green */ dstp += dstSizes[2]; } else { - CopyMemory(dstp, planes[2], planeSize); /* Green */ + CopyMemory(dstp, context->planes[2], planeSize); /* Green */ dstp += planeSize; } @@ -882,12 +841,12 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { - CopyMemory(dstp, rlePlanes[3], dstSizes[3]); /* Blue */ + CopyMemory(dstp, context->rlePlanes[3], dstSizes[3]); /* Blue */ dstp += dstSizes[3]; } else { - CopyMemory(dstp, planes[3], planeSize); /* Blue */ + CopyMemory(dstp, context->planes[3], planeSize); /* Blue */ dstp += planeSize; } @@ -902,9 +861,63 @@ BYTE* freerdp_bitmap_compress_planar(BYTE* data, UINT32 format, int width, int h size = (dstp - dstData); *dstSize = size; - free(rlePlanesBuffer); - free(deltaPlanesBuffer); - free(planesBuffer); - return dstData; } + +BITMAP_PLANAR_CONTEXT* freerdp_bitmap_planar_context_new(DWORD flags, int maxWidth, int maxHeight) +{ + BITMAP_PLANAR_CONTEXT* context; + + context = (BITMAP_PLANAR_CONTEXT*) malloc(sizeof(BITMAP_PLANAR_CONTEXT)); + + if (!context) + return NULL; + + ZeroMemory(context, sizeof(BITMAP_PLANAR_CONTEXT)); + + if (flags & PLANAR_FORMAT_HEADER_NA) + context->AllowSkipAlpha = TRUE; + + if (flags & PLANAR_FORMAT_HEADER_RLE) + context->AllowRunLengthEncoding = TRUE; + + if (flags & PLANAR_FORMAT_HEADER_CS) + context->AllowColorSubsampling = TRUE; + + context->ColorLossLevel = flags & PLANAR_FORMAT_HEADER_CLL_MASK; + + if (context->ColorLossLevel) + context->AllowDynamicColorFidelity = TRUE; + + context->maxWidth = maxWidth; + context->maxHeight = maxHeight; + context->maxPlaneSize = context->maxWidth * context->maxHeight; + + context->planesBuffer = malloc(context->maxPlaneSize * 4); + context->planes[0] = &context->planesBuffer[context->maxPlaneSize * 0]; + context->planes[1] = &context->planesBuffer[context->maxPlaneSize * 1]; + context->planes[2] = &context->planesBuffer[context->maxPlaneSize * 2]; + context->planes[3] = &context->planesBuffer[context->maxPlaneSize * 3]; + + context->deltaPlanesBuffer = malloc(context->maxPlaneSize * 4); + context->deltaPlanes[0] = &context->deltaPlanesBuffer[context->maxPlaneSize * 0]; + context->deltaPlanes[1] = &context->deltaPlanesBuffer[context->maxPlaneSize * 1]; + context->deltaPlanes[2] = &context->deltaPlanesBuffer[context->maxPlaneSize * 2]; + context->deltaPlanes[3] = &context->deltaPlanesBuffer[context->maxPlaneSize * 3]; + + context->rlePlanesBuffer = malloc(context->maxPlaneSize * 4); + + return context; +} + +void freerdp_bitmap_planar_context_free(BITMAP_PLANAR_CONTEXT* context) +{ + if (!context) + return; + + free(context->planesBuffer); + free(context->deltaPlanesBuffer); + free(context->rlePlanesBuffer); + + free(context); +} diff --git a/libfreerdp/codec/planar.h b/libfreerdp/codec/planar.h index 8931f94b2..54a98e644 100644 --- a/libfreerdp/codec/planar.h +++ b/libfreerdp/codec/planar.h @@ -23,6 +23,7 @@ #include #include +#include #define PLANAR_CONTROL_BYTE(_nRunLength, _cRawBytes) \ (_nRunLength & 0x0F) | ((_cRawBytes & 0x0F) << 4) @@ -49,10 +50,6 @@ struct _RDP6_RLE_SEGMENTS }; typedef struct _RDP6_RLE_SEGMENTS RDP6_RLE_SEGMENTS; -#define PLANAR_FORMAT_HEADER_CS (1 << 3) -#define PLANAR_FORMAT_HEADER_RLE (1 << 4) -#define PLANAR_FORMAT_HEADER_NA (1 << 5) - struct _RDP6_BITMAP_STREAM { /** @@ -67,11 +64,34 @@ struct _RDP6_BITMAP_STREAM }; typedef struct _RDP6_BITMAP_STREAM RDP6_BITMAP_STREAM; +struct _BITMAP_PLANAR_CONTEXT +{ + int maxWidth; + int maxHeight; + int maxPlaneSize; + + BOOL AllowSkipAlpha; + BOOL AllowRunLengthEncoding; + BOOL AllowColorSubsampling; + BOOL AllowDynamicColorFidelity; + + int ColorLossLevel; + + BYTE* planes[4]; + BYTE* planesBuffer; + + BYTE* deltaPlanes[4]; + BYTE* deltaPlanesBuffer; + + BYTE* rlePlanes[4]; + BYTE* rlePlanesBuffer; +}; + int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int size); -int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[5]); +int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, int scanline, BYTE* planes[4]); BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* plane, int width, int height, BYTE* outPlane, int* dstSize); BYTE* freerdp_bitmap_planar_delta_encode_plane(BYTE* inPlane, int width, int height, BYTE* outPlane); -int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[5], int width, int height, BYTE* outPlanes[5]); +int freerdp_bitmap_planar_delta_encode_planes(BYTE* inPlanes[4], int width, int height, BYTE* outPlanes[4]); #endif /* FREERDP_CODEC_PLANAR_PRIVATE_H */ diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index a00278d4e..85678d63d 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -1858,6 +1858,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) int dstSize; UINT32 format; HCLRCONV clrconv; + DWORD planarFlags; BYTE* srcBitmap32; BYTE* srcBitmap16; int width, height; @@ -1866,6 +1867,12 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) BYTE* randomBitmap; BYTE* compressedBitmap; BYTE* decompressedBitmap; + BITMAP_PLANAR_CONTEXT* planar; + + planarFlags = PLANAR_FORMAT_HEADER_NA; + //planarFlags |= PLANAR_FORMAT_HEADER_RLE; + + planar = freerdp_bitmap_planar_context_new(planarFlags, 64, 64); clrconv = freerdp_clrconv_new(0); srcBitmap16 = (BYTE*) TEST_RLE_UNCOMPRESSED_BITMAP_16BPP; @@ -1874,8 +1881,8 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) format = FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_ARGB, FREERDP_PIXEL_FLIP_NONE); -#if 0 - freerdp_bitmap_compress_planar(srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); +#if 1 + freerdp_bitmap_compress_planar(planar, srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12, 1, NULL, &dstSize); @@ -1892,7 +1899,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) FillMemory(whiteBitmap, width * height * 4, 0xFF); fill_bitmap_alpha_channel(whiteBitmap, width, height, 0x00); - compressedBitmap = freerdp_bitmap_compress_planar(whiteBitmap, format, width, height, width * 4, NULL, &dstSize); + compressedBitmap = freerdp_bitmap_compress_planar(planar, whiteBitmap, format, width, height, width * 4, NULL, &dstSize); decompressedBitmap = (BYTE*) malloc(width * height * 4); ZeroMemory(decompressedBitmap, width * height * 4); @@ -1932,7 +1939,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) ZeroMemory(blackBitmap, width * height * 4); fill_bitmap_alpha_channel(blackBitmap, width, height, 0x00); - compressedBitmap = freerdp_bitmap_compress_planar(blackBitmap, format, width, height, width * 4, NULL, &dstSize); + compressedBitmap = freerdp_bitmap_compress_planar(planar, blackBitmap, format, width, height, width * 4, NULL, &dstSize); decompressedBitmap = (BYTE*) malloc(width * height * 4); ZeroMemory(decompressedBitmap, width * height * 4); @@ -1977,7 +1984,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) fill_bitmap_alpha_channel(randomBitmap, width, height, 0x00); - compressedBitmap = freerdp_bitmap_compress_planar(randomBitmap, format, width, height, width * 4, NULL, &dstSize); + compressedBitmap = freerdp_bitmap_compress_planar(planar, randomBitmap, format, width, height, width * 4, NULL, &dstSize); decompressedBitmap = (BYTE*) malloc(width * height * 4); ZeroMemory(decompressedBitmap, width * height * 4); @@ -2009,13 +2016,13 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) } #endif -#if 0 +#if 1 /* Experimental Case 01 */ width = 64; height = 64; - compressedBitmap = freerdp_bitmap_compress_planar((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, + compressedBitmap = freerdp_bitmap_compress_planar(planar, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_01, format, width, height, width * 4, NULL, &dstSize); decompressedBitmap = (BYTE*) malloc(width * height * 4); @@ -2052,13 +2059,13 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(decompressedBitmap); #endif -#if 0 +#if 1 /* Experimental Case 02 */ width = 64; height = 64; - compressedBitmap = freerdp_bitmap_compress_planar((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, + compressedBitmap = freerdp_bitmap_compress_planar(planar, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_02, format, width, height, width * 4, NULL, &dstSize); decompressedBitmap = (BYTE*) malloc(width * height * 4); @@ -2100,7 +2107,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) width = 64; height = 64; - compressedBitmap = freerdp_bitmap_compress_planar((BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_03, + compressedBitmap = freerdp_bitmap_compress_planar(planar, (BYTE*) TEST_RLE_BITMAP_EXPERIMENTAL_03, format, width, height, width * 4, NULL, &dstSize); decompressedBitmap = (BYTE*) malloc(width * height * 4); @@ -2139,5 +2146,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_clrconv_free(clrconv); free(srcBitmap32); + freerdp_bitmap_planar_context_free(planar); + return 0; } From 718a0d0bbcea96f9abe81b2e875eedc28386d5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 8 Dec 2013 23:17:24 -0500 Subject: [PATCH 080/149] libfreerdp-codec: avoid pixel overflow with planar codec RLE test cases --- libfreerdp/codec/planar.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 4a66f87f1..89bb17ddb 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -449,12 +449,9 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) *rle->output = PLANAR_CONTROL_BYTE(rle->nRunLength, rle->cRawBytes); rle->output++; - if (rle->cRawBytes) - { - CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); - rle->rawValues += (rle->cRawBytes + rle->nRunLength); - rle->output += rle->cRawBytes; - } + CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); + rle->rawValues += (rle->cRawBytes + rle->nRunLength); + rle->output += rle->cRawBytes; rle->cRawBytes = 0; rle->nRunLength = 0; @@ -466,7 +463,7 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) { if (rle->nRunLength > 15) { - rle->nControlBytes = 2; + rle->nControlBytes = 1; rle->outSegmentSize = rle->cRawBytes + rle->nControlBytes; if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) @@ -484,12 +481,9 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) *rle->output = PLANAR_CONTROL_BYTE(15, rle->cRawBytes); rle->output++; - if (rle->cRawBytes) - { - CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); - rle->rawValues += (rle->cRawBytes + rle->nRunLength); - rle->output += rle->cRawBytes; - } + CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); + rle->rawValues += (rle->cRawBytes + 15); + rle->output += rle->cRawBytes; rle->nRunLength -= 15; rle->cRawBytes = 0; @@ -516,12 +510,9 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) *rle->output = PLANAR_CONTROL_BYTE(rle->nRunLength, rle->cRawBytes); rle->output++; - if (rle->cRawBytes) - { - CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); - rle->rawValues += (rle->cRawBytes + rle->nRunLength); - rle->output += rle->cRawBytes; - } + CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); + rle->rawValues += (rle->cRawBytes + rle->nRunLength); + rle->output += rle->cRawBytes; rle->cRawBytes = 0; rle->nRunLength = 0; @@ -565,6 +556,8 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei rle->rawScanline = &inPlane[i * width]; rle->rawValues = rle->rawScanline; + //winpr_HexDump(rle->rawScanline, width); + for (j = 1; j < width; j++) { bSymbolMatch = FALSE; From f60a1e59f16386bf0a3355343a0ca8c1924430af Mon Sep 17 00:00:00 2001 From: Hardening Date: Mon, 9 Dec 2013 15:56:13 +0100 Subject: [PATCH 081/149] Update version number for master --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa71dd2cd..81b86d882 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ include(CMakePackageConfigHelpers) # Soname versioning set(FREERDP_VERSION_MAJOR "1") -set(FREERDP_VERSION_MINOR "1") +set(FREERDP_VERSION_MINOR "2") set(FREERDP_VERSION_REVISION "0") set(FREERDP_VERSION_SUFFIX "beta1") set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}") From 26f543cdf4e72c8453e8b3cf578245eecdd6067f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 9 Dec 2013 12:02:05 -0500 Subject: [PATCH 082/149] libfreerdp-codec: planar cleanup --- libfreerdp/codec/planar.c | 92 ++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 89bb17ddb..bcbd90838 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -191,21 +191,19 @@ static int freerdp_bitmap_planar_decompress_plane_raw(BYTE* srcData, int width, return (width * height); } -//int g_DecompressCount = 0; - int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, int height, int size) { BYTE* srcp; int dstSize; BYTE FormatHeader; - //printf("freerdp_bitmap_planar_decompress: %d\n", ++g_DecompressCount); - srcp = srcData; FormatHeader = *srcp; srcp++; + /* AlphaPlane */ + if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA)) { if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) @@ -226,6 +224,8 @@ int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, in if (FormatHeader & PLANAR_FORMAT_HEADER_RLE) { + /* LumaOrRedPlane */ + dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 2, size - (srcp - srcData)); if (dstSize < 0) @@ -233,6 +233,8 @@ int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, in srcp += dstSize; + /* OrangeChromaOrGreenPlane */ + dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 1, size - (srcp - srcData)); if (dstSize < 0) @@ -240,6 +242,8 @@ int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, in srcp += dstSize; + /* GreenChromeOrBluePlane */ + dstSize = freerdp_bitmap_planar_decompress_plane_rle(srcp, width, height, dstData + 0, size - (srcp - srcData)); if (dstSize < 0) @@ -249,12 +253,18 @@ int freerdp_bitmap_planar_decompress(BYTE* srcData, BYTE* dstData, int width, in } else { + /* LumaOrRedPlane */ + dstSize = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 2, size - (srcp - srcData)); srcp += dstSize; + /* OrangeChromaOrGreenPlane */ + dstSize = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 1, size - (srcp - srcData)); srcp += dstSize; + /* GreenChromeOrBluePlane */ + dstSize = freerdp_bitmap_planar_decompress_plane_raw(srcp, width, height, dstData + 0, size - (srcp - srcData)); srcp += dstSize; srcp++; @@ -320,16 +330,12 @@ struct _PLANAR_RLE_CONTEXT BYTE* outPlane; int outPlaneSize; int outSegmentSize; - int nControlBytes; }; typedef struct _PLANAR_RLE_CONTEXT PLANAR_RLE_CONTEXT; int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) { #if 0 - if (rle->nRunLength >= 3) - printf("### cRawBytes: %d nRunLength: %d\n", rle->cRawBytes, rle->nRunLength); - { int k; @@ -363,8 +369,7 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) if (rle->cRawBytes > 15) { - rle->nControlBytes = 1; - rle->outSegmentSize = 15 + rle->nControlBytes; + rle->outSegmentSize = 1 + 15; if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) { @@ -386,8 +391,7 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) { if (rle->nRunLength > 47) { - rle->nControlBytes = 1; - rle->outSegmentSize = rle->nControlBytes; + rle->outSegmentSize = 1; if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) { @@ -403,8 +407,7 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) } else if (rle->nRunLength > 31) { - rle->nControlBytes = 1; - rle->outSegmentSize = rle->nControlBytes; + rle->outSegmentSize = 1; if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) { @@ -420,8 +423,7 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) } else if (rle->nRunLength > 15) { - rle->nControlBytes = 1; - rle->outSegmentSize = rle->nControlBytes; + rle->outSegmentSize = 1; if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) { @@ -437,8 +439,7 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) } else { - rle->nControlBytes = 1; - rle->outSegmentSize = rle->cRawBytes + rle->nControlBytes; + rle->outSegmentSize = 1 + rle->cRawBytes; if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) { @@ -463,8 +464,7 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) { if (rle->nRunLength > 15) { - rle->nControlBytes = 1; - rle->outSegmentSize = rle->cRawBytes + rle->nControlBytes; + rle->outSegmentSize = 1 + rle->cRawBytes; if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) { @@ -472,12 +472,6 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) return -1; } - if ((!rle->rawValues[rle->cRawBytes - 1]) && (rle->cRawBytes == 1)) - { - //rle->rawValues += (rle->cRawBytes + rle->nRunLength); - //rle->cRawBytes = 0; - } - *rle->output = PLANAR_CONTROL_BYTE(15, rle->cRawBytes); rle->output++; @@ -492,8 +486,7 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) } else { - rle->nControlBytes = 1; - rle->outSegmentSize = rle->cRawBytes + rle->nControlBytes; + rle->outSegmentSize = 1 + rle->cRawBytes; if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) { @@ -501,12 +494,6 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) return -1; } - if ((!rle->rawValues[rle->cRawBytes - 1]) && (rle->cRawBytes == 1)) - { - //rle->rawValues += (rle->cRawBytes + rle->nRunLength); - //rle->cRawBytes = 0; - } - *rle->output = PLANAR_CONTROL_BYTE(rle->nRunLength, rle->cRawBytes); rle->output++; @@ -622,21 +609,29 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei return outPlane; } -int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[4], int width, int height, BYTE* outPlanes, int* dstSizes) +int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[4], int width, int height, BYTE* outPlanes, int* dstSizes, BOOL skipAlpha) { int outPlanesSize = width * height * 4; -#if 0 - dstSizes[0] = outPlanesSize; + /* AlphaPlane */ - if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes, &dstSizes[0])) - return 0; + if (skipAlpha) + { + dstSizes[0] = 0; + } + else + { + dstSizes[0] = outPlanesSize; + + if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes, &dstSizes[0])) + return 0; + + outPlanes += dstSizes[0]; + outPlanesSize -= dstSizes[0]; + } + + /* LumaOrRedPlane */ - outPlanes += dstSizes[0]; - outPlanesSize -= dstSizes[0]; -#else - dstSizes[0] = 0; -#endif dstSizes[1] = outPlanesSize; if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes, &dstSizes[1])) @@ -644,6 +639,9 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[4], int width, int outPlanes += dstSizes[1]; outPlanesSize -= dstSizes[1]; + + /* OrangeChromaOrGreenPlane */ + dstSizes[2] = outPlanesSize; if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes, &dstSizes[2])) @@ -651,6 +649,9 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[4], int width, int outPlanes += dstSizes[2]; outPlanesSize -= dstSizes[2]; + + /* GreenChromeOrBluePlane */ + dstSizes[3] = outPlanesSize; if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes, &dstSizes[3])) @@ -736,7 +737,8 @@ BYTE* freerdp_bitmap_compress_planar(BITMAP_PLANAR_CONTEXT* context, BYTE* data, { freerdp_bitmap_planar_delta_encode_planes(context->planes, width, height, context->deltaPlanes); - if (freerdp_bitmap_planar_compress_planes_rle(context->deltaPlanes, width, height, context->rlePlanesBuffer, (int*) &dstSizes) > 0) + if (freerdp_bitmap_planar_compress_planes_rle(context->deltaPlanes, width, height, + context->rlePlanesBuffer, (int*) &dstSizes, context->AllowSkipAlpha) > 0) { int offset = 0; From 065ba4f500b29f2c17f7ddc9864f0ec84f196c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Mon, 9 Dec 2013 14:28:32 -0500 Subject: [PATCH 083/149] Prevent crash in mac CLI application if command-line is not properly parsed (NULL argv causes segmentation fault when printing usage). --- client/Mac/cli/AppDelegate.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/Mac/cli/AppDelegate.m b/client/Mac/cli/AppDelegate.m index 612205bae..144ba081e 100644 --- a/client/Mac/cli/AppDelegate.m +++ b/client/Mac/cli/AppDelegate.m @@ -94,7 +94,13 @@ void mac_set_view_size(rdpContext* context, MRDPView* view); } status = freerdp_client_settings_parse_command_line(context->settings, argc, argv); - status = freerdp_client_settings_command_line_status_print(context->settings, status, context->argc, context->argv); + + if (context->argc && context->argv) + status = freerdp_client_settings_command_line_status_print(context->settings, status, context->argc, context->argv); + else + { + freerdp_client_print_command_line_help(argc, argv); + } return status; } From 2ea2a6937dd08377e393d8141ef48569a324c42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Mon, 9 Dec 2013 15:30:00 -0500 Subject: [PATCH 084/149] missing DEFINE on .h file --- client/Mac/MRDPView.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/Mac/MRDPView.h b/client/Mac/MRDPView.h index 4cd7c68d3..6e4c3a4b8 100755 --- a/client/Mac/MRDPView.h +++ b/client/Mac/MRDPView.h @@ -1,3 +1,6 @@ +#ifndef MRDPVIEW_H +#define MRDPVIEW_H + /** * FreeRDP: A Remote Desktop Protocol Implementation * MacFreeRDP @@ -97,3 +100,5 @@ BOOL mac_post_connect(freerdp* instance); BOOL mac_authenticate(freerdp* instance, char** username, char** password, char** domain); int mac_receive_channel_data(freerdp* instance, int chan_id, BYTE* data, int size, int flags, int total_size); DWORD mac_client_thread(void* param); + +#endif // MRDPVIEW_H From c230e872afffd042683608503ba6273a35f30d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 9 Dec 2013 16:02:42 -0500 Subject: [PATCH 085/149] channels/rdpsnd: start refactoring mac audio code --- channels/rdpsnd/client/mac/rdpsnd_mac.c | 338 +++++++++++++----------- channels/rdpsnd/client/rdpsnd_main.c | 2 +- 2 files changed, 185 insertions(+), 155 deletions(-) diff --git a/channels/rdpsnd/client/mac/rdpsnd_mac.c b/channels/rdpsnd/client/mac/rdpsnd_mac.c index c8e3bed84..2ed616866 100644 --- a/channels/rdpsnd/client/mac/rdpsnd_mac.c +++ b/channels/rdpsnd/client/mac/rdpsnd_mac.c @@ -17,10 +17,6 @@ * limitations under the License. */ -/** - * Use AudioQueue to implement audio redirection - */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -40,163 +36,203 @@ #include "rdpsnd_main.h" -#define AQ_NUM_BUFFERS 10 -#define AQ_BUF_SIZE (32 * 1024) +#define AQ_NUM_BUFFERS 10 +#define AQ_BUF_SIZE (32 * 1024) -static void aq_playback_cb(void *user_data, - AudioQueueRef aq_ref, - AudioQueueBufferRef aq_buf_ref - ); - -struct rdpsnd_audio_q_plugin +struct rdpsnd_mac_plugin { rdpsndDevicePlugin device; - /* audio queue player state */ - int is_open; // true when audio_q has been inited - char * device_name; - int is_playing; - int buf_index; + BOOL isOpen; + BOOL isPlaying; + + UINT32 latency; + AUDIO_FORMAT format; + int audioBufferIndex; - AudioStreamBasicDescription data_format; - AudioQueueRef aq_ref; - AudioQueueBufferRef buffers[AQ_NUM_BUFFERS]; + AudioQueueRef audioQueue; + AudioStreamBasicDescription audioFormat; + AudioQueueBufferRef audioBuffers[AQ_NUM_BUFFERS]; }; -typedef struct rdpsnd_audio_q_plugin rdpsndAudioQPlugin; +typedef struct rdpsnd_mac_plugin rdpsndMacPlugin; -static void rdpsnd_audio_close(rdpsndDevicePlugin* device) +static void mac_audio_queue_output_cb(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { - rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin*) device; - - AudioQueueStop(aq_plugin_p->aq_ref, 0); - aq_plugin_p->is_open = 0; + } -static void rdpsnd_audio_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency) -{ - int rv; - int i; - - rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device; - if (aq_plugin_p->is_open) { - return; - } - - aq_plugin_p->buf_index = 0; - - // setup AudioStreamBasicDescription - aq_plugin_p->data_format.mSampleRate = 44100; - aq_plugin_p->data_format.mFormatID = kAudioFormatLinearPCM; - aq_plugin_p->data_format.mFormatFlags = kAudioFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; - - // until we know better, assume that one packet = one frame - // one frame = bytes_per_sample x number_of_channels - aq_plugin_p->data_format.mBytesPerPacket = 4; - aq_plugin_p->data_format.mFramesPerPacket = 1; - aq_plugin_p->data_format.mBytesPerFrame = 4; - aq_plugin_p->data_format.mChannelsPerFrame = 2; - aq_plugin_p->data_format.mBitsPerChannel = 16; - - rv = AudioQueueNewOutput( - &aq_plugin_p->data_format, // audio stream basic desc - aq_playback_cb, // callback when more data is required - aq_plugin_p, // data to pass to callback - CFRunLoopGetCurrent(), // The current run loop, and the one on - // which the audio queue playback callback - // will be invoked - kCFRunLoopCommonModes, // run loop modes in which callbacks can - // be invoked - 0, // flags - reserved - &aq_plugin_p->aq_ref - ); - if (rv != 0) { - fprintf(stderr, "rdpsnd_audio_open: AudioQueueNewOutput() failed with error %d\n", rv); - aq_plugin_p->is_open = 1; - return; - } - - for (i = 0; i < AQ_NUM_BUFFERS; i++) - { - rv = AudioQueueAllocateBuffer(aq_plugin_p->aq_ref, AQ_BUF_SIZE, &aq_plugin_p->buffers[i]); - } - - aq_plugin_p->is_open = 1; -} - -static void rdpsnd_audio_free(rdpsndDevicePlugin* device) -{ -} - -static BOOL rdpsnd_audio_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format) +static void rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency) { + rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; + + mac->latency = (UINT32) latency; + CopyMemory(&(mac->format), format, sizeof(AUDIO_FORMAT)); + switch (format->wFormatTag) { - case 1: /* PCM */ - if (format->cbSize == 0 && - (format->nSamplesPerSec <= 48000) && - (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) && - (format->nChannels == 1 || format->nChannels == 2)) - { - return 1; - } - break; + case WAVE_FORMAT_ALAW: + mac->audioFormat.mFormatID = kAudioFormatALaw; + break; + + case WAVE_FORMAT_MULAW: + mac->audioFormat.mFormatID = kAudioFormatULaw; + break; + + case WAVE_FORMAT_PCM: + mac->audioFormat.mFormatID = kAudioFormatLinearPCM; + break; + + case WAVE_FORMAT_GSM610: + mac->audioFormat.mFormatID = kAudioFormatMicrosoftGSM; + break; + + default: + break; } - return 0; + + mac->audioFormat.mSampleRate = format->nSamplesPerSec; + mac->audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; + mac->audioFormat.mFramesPerPacket = 1; + mac->audioFormat.mChannelsPerFrame = format->nChannels; + mac->audioFormat.mBitsPerChannel = format->wBitsPerSample; + mac->audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8; + mac->audioFormat.mBytesPerPacket = format->nBlockAlign; + + rdpsnd_print_audio_format(format); } -static void rdpsnd_audio_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency) +static void rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency) { -} - -static void rdpsnd_audio_set_volume(rdpsndDevicePlugin* device, UINT32 value) -{ -} - -static void rdpsnd_audio_play(rdpsndDevicePlugin* device, BYTE* data, int size) -{ - rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device; - AudioQueueBufferRef aq_buf_ref; - int len; + int index; + OSStatus status; - if (!aq_plugin_p->is_open) { + rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; + + if (mac->isOpen) + return; + + mac->audioBufferIndex = 0; + + device->SetFormat(device, format, 0); + + status = AudioQueueNewOutput(&(mac->audioFormat), + mac_audio_queue_output_cb, mac, + NULL, NULL, 0, &(mac->audioQueue)); + + if (status != 0) + { + fprintf(stderr, "AudioQueueNewOutput failure\n"); return; } + + for (index = 0; index < AQ_NUM_BUFFERS; index++) + { + status = AudioQueueAllocateBuffer(mac->audioQueue, AQ_BUF_SIZE, &mac->audioBuffers[index]); + + if (status != 0) + { + fprintf(stderr, "AudioQueueAllocateBuffer failed\n"); + } + } + + mac->isOpen = TRUE; +} - /* get next empty buffer */ - aq_buf_ref = aq_plugin_p->buffers[aq_plugin_p->buf_index]; - - // fill aq_buf_ref with audio data - len = size > AQ_BUF_SIZE ? AQ_BUF_SIZE : size; - - memcpy(aq_buf_ref->mAudioData, (char *) data, len); - aq_buf_ref->mAudioDataByteSize = len; - - // add buffer to audioqueue - AudioQueueEnqueueBuffer(aq_plugin_p->aq_ref, aq_buf_ref, 0, 0); - - // update buf_index - aq_plugin_p->buf_index++; - if (aq_plugin_p->buf_index >= AQ_NUM_BUFFERS) { - aq_plugin_p->buf_index = 0; +static void rdpsnd_mac_close(rdpsndDevicePlugin* device) +{ + rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; + + if (mac->isOpen) + { + mac->isOpen = FALSE; + + AudioQueueStop(mac->audioQueue, true); + AudioQueueDispose(mac->audioQueue, true); + + mac->isPlaying = FALSE; } } -static void rdpsnd_audio_start(rdpsndDevicePlugin* device) +static void rdpsnd_mac_free(rdpsndDevicePlugin* device) { - rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device; - - AudioQueueStart(aq_plugin_p->aq_ref, NULL); + } -/** - * AudioQueue Playback callback - * - * our job here is to fill aq_buf_ref with audio data and enqueue it - */ - -static void aq_playback_cb(void* user_data, AudioQueueRef aq_ref, AudioQueueBufferRef aq_buf_ref) +static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format) { + if (format->wFormatTag == WAVE_FORMAT_PCM) + { + return TRUE; + } + else if (format->wFormatTag == WAVE_FORMAT_ALAW) + { + return FALSE; + } + else if (format->wFormatTag == WAVE_FORMAT_MULAW) + { + return FALSE; + } + else if (format->wFormatTag == WAVE_FORMAT_GSM610) + { + return FALSE; + } + + return FALSE; +} +static void rdpsnd_mac_set_volume(rdpsndDevicePlugin* device, UINT32 value) +{ + +} + +static void rdpsnd_mac_start(rdpsndDevicePlugin* device) +{ + rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; + + if (!mac->isPlaying) + { + OSStatus status; + + if (!mac->audioQueue) + return; + + status = AudioQueueStart(mac->audioQueue, NULL); + + if (status != 0) + { + printf("AudioQueueStart failed\n"); + } + + mac->isPlaying = TRUE; + } +} + +static void rdpsnd_mac_play(rdpsndDevicePlugin* device, BYTE* data, int size) +{ + int length; + AudioQueueBufferRef audioBuffer; + rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; + + fprintf(stderr, "WavePlay\n"); + + if (!mac->isOpen) + return; + + audioBuffer = mac->audioBuffers[mac->audioBufferIndex]; + + length = size > AQ_BUF_SIZE ? AQ_BUF_SIZE : size; + + CopyMemory(audioBuffer->mAudioData, data, length); + audioBuffer->mAudioDataByteSize = length; + + AudioQueueEnqueueBuffer(mac->audioQueue, audioBuffer, 0, 0); + + mac->audioBufferIndex++; + + if (mac->audioBufferIndex >= AQ_NUM_BUFFERS) + mac->audioBufferIndex = 0; + + device->Start(device); } #ifdef STATIC_CHANNELS @@ -205,31 +241,25 @@ static void aq_playback_cb(void* user_data, AudioQueueRef aq_ref, AudioQueueBuff int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints) { - fprintf(stderr, "freerdp_rdpsnd_client_subsystem_entry()\n\n"); + rdpsndMacPlugin* mac; - ADDIN_ARGV* args; - rdpsndAudioQPlugin* aqPlugin; - - aqPlugin = (rdpsndAudioQPlugin*) malloc(sizeof(rdpsndAudioQPlugin)); - ZeroMemory(aqPlugin, sizeof(rdpsndAudioQPlugin)); - - aqPlugin->device.Open = rdpsnd_audio_open; - aqPlugin->device.FormatSupported = rdpsnd_audio_format_supported; - aqPlugin->device.SetFormat = rdpsnd_audio_set_format; - aqPlugin->device.SetVolume = rdpsnd_audio_set_volume; - aqPlugin->device.Play = rdpsnd_audio_play; - aqPlugin->device.Start = rdpsnd_audio_start; - aqPlugin->device.Close = rdpsnd_audio_close; - aqPlugin->device.Free = rdpsnd_audio_free; - - args = pEntryPoints->args; - - if (args->argc > 2) + mac = (rdpsndMacPlugin*) malloc(sizeof(rdpsndMacPlugin)); + + if (mac) { - /* TODO: parse device name */ - } + ZeroMemory(mac, sizeof(rdpsndMacPlugin)); + + mac->device.Open = rdpsnd_mac_open; + mac->device.FormatSupported = rdpsnd_mac_format_supported; + mac->device.SetFormat = rdpsnd_mac_set_format; + mac->device.SetVolume = rdpsnd_mac_set_volume; + mac->device.Play = rdpsnd_mac_play; + mac->device.Start = rdpsnd_mac_start; + mac->device.Close = rdpsnd_mac_close; + mac->device.Free = rdpsnd_mac_free; - pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) aqPlugin); + pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) mac); + } return 0; } diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c index 1fc4c9e25..059db1637 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -718,7 +718,7 @@ static void rdpsnd_process_connect(rdpsndPlugin* rdpsnd) #if defined(WITH_MACAUDIO) if (!rdpsnd->device) { - rdpsnd_set_subsystem(rdpsnd, "macaudio"); + rdpsnd_set_subsystem(rdpsnd, "mac"); rdpsnd_set_device_name(rdpsnd, "default"); rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); } From 95a452e77fc9553e17d38240596df1fc1c4cd39f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 9 Dec 2013 17:34:23 -0500 Subject: [PATCH 086/149] channels/rdpsnd: start using AudioConverter --- channels/rdpsnd/client/mac/rdpsnd_mac.c | 118 +++++++++++++++++++----- 1 file changed, 94 insertions(+), 24 deletions(-) diff --git a/channels/rdpsnd/client/mac/rdpsnd_mac.c b/channels/rdpsnd/client/mac/rdpsnd_mac.c index 2ed616866..4a00bb3d0 100644 --- a/channels/rdpsnd/client/mac/rdpsnd_mac.c +++ b/channels/rdpsnd/client/mac/rdpsnd_mac.c @@ -36,8 +36,8 @@ #include "rdpsnd_main.h" -#define AQ_NUM_BUFFERS 10 -#define AQ_BUF_SIZE (32 * 1024) +#define MAC_AUDIO_QUEUE_NUM_BUFFERS 10 +#define MAC_AUDIO_QUEUE_BUFFER_SIZE 32768 struct rdpsnd_mac_plugin { @@ -51,8 +51,10 @@ struct rdpsnd_mac_plugin int audioBufferIndex; AudioQueueRef audioQueue; - AudioStreamBasicDescription audioFormat; - AudioQueueBufferRef audioBuffers[AQ_NUM_BUFFERS]; + AudioConverterRef audioConverter; + AudioStreamBasicDescription inputAudioFormat; + AudioStreamBasicDescription outputAudioFormat; + AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS]; }; typedef struct rdpsnd_mac_plugin rdpsndMacPlugin; @@ -63,6 +65,7 @@ static void mac_audio_queue_output_cb(void* inUserData, AudioQueueRef inAQ, Audi static void rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency) { + OSStatus status; rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; mac->latency = (UINT32) latency; @@ -71,32 +74,61 @@ static void rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* form switch (format->wFormatTag) { case WAVE_FORMAT_ALAW: - mac->audioFormat.mFormatID = kAudioFormatALaw; + mac->inputAudioFormat.mFormatID = kAudioFormatALaw; break; case WAVE_FORMAT_MULAW: - mac->audioFormat.mFormatID = kAudioFormatULaw; + mac->inputAudioFormat.mFormatID = kAudioFormatULaw; break; case WAVE_FORMAT_PCM: - mac->audioFormat.mFormatID = kAudioFormatLinearPCM; + mac->inputAudioFormat.mFormatID = kAudioFormatLinearPCM; break; case WAVE_FORMAT_GSM610: - mac->audioFormat.mFormatID = kAudioFormatMicrosoftGSM; + mac->inputAudioFormat.mFormatID = kAudioFormatMicrosoftGSM; break; default: break; } - mac->audioFormat.mSampleRate = format->nSamplesPerSec; - mac->audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; - mac->audioFormat.mFramesPerPacket = 1; - mac->audioFormat.mChannelsPerFrame = format->nChannels; - mac->audioFormat.mBitsPerChannel = format->wBitsPerSample; - mac->audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8; - mac->audioFormat.mBytesPerPacket = format->nBlockAlign; + mac->inputAudioFormat.mSampleRate = format->nSamplesPerSec; + mac->inputAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; + mac->inputAudioFormat.mFramesPerPacket = 1; + mac->inputAudioFormat.mChannelsPerFrame = format->nChannels; + mac->inputAudioFormat.mBitsPerChannel = format->wBitsPerSample; + mac->inputAudioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8; + mac->inputAudioFormat.mBytesPerPacket = format->nBlockAlign; + + mac->outputAudioFormat.mFormatID = kAudioFormatLinearPCM; + mac->outputAudioFormat.mSampleRate = 44100; + mac->outputAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; + mac->outputAudioFormat.mFramesPerPacket = 1; + mac->outputAudioFormat.mChannelsPerFrame = 2; + mac->outputAudioFormat.mBitsPerChannel = 16; + mac->outputAudioFormat.mBytesPerFrame = (16 * 2) / 8; + mac->outputAudioFormat.mBytesPerPacket = 4; + + status = AudioConverterNew(&(mac->inputAudioFormat), + &(mac->outputAudioFormat), &(mac->audioConverter)); + + if (status != 0) + { + fprintf(stderr, "AudioConverterNew failure\n"); + return; + } + + SInt32 channelMap[2] = { 0, 0 }; + + status = AudioConverterSetProperty(mac->audioConverter, kAudioConverterChannelMap, + sizeof(channelMap), channelMap); + + if (status != 0) + { + fprintf(stderr, "AudioConverterSetProperty kAudioConverterChannelMap failure\n"); + return; + } rdpsnd_print_audio_format(format); } @@ -115,7 +147,7 @@ static void rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in device->SetFormat(device, format, 0); - status = AudioQueueNewOutput(&(mac->audioFormat), + status = AudioQueueNewOutput(&(mac->inputAudioFormat), mac_audio_queue_output_cb, mac, NULL, NULL, 0, &(mac->audioQueue)); @@ -124,10 +156,23 @@ static void rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in fprintf(stderr, "AudioQueueNewOutput failure\n"); return; } - - for (index = 0; index < AQ_NUM_BUFFERS; index++) + + UInt32 DecodeBufferSizeFrames; + UInt32 propertySize = sizeof(DecodeBufferSizeFrames); + + AudioQueueGetProperty(mac->audioQueue, + kAudioQueueProperty_DecodeBufferSizeFrames, + &DecodeBufferSizeFrames, + &propertySize); + + if (status != 0) { - status = AudioQueueAllocateBuffer(mac->audioQueue, AQ_BUF_SIZE, &mac->audioBuffers[index]); + printf("AudioQueueGetProperty failure: kAudioQueueProperty_DecodeBufferSizeFrames\n"); + } + + for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++) + { + status = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE, &mac->audioBuffers[index]); if (status != 0) { @@ -147,7 +192,9 @@ static void rdpsnd_mac_close(rdpsndDevicePlugin* device) mac->isOpen = FALSE; AudioQueueStop(mac->audioQueue, true); + AudioQueueDispose(mac->audioQueue, true); + mac->audioQueue = NULL; mac->isPlaying = FALSE; } @@ -155,7 +202,11 @@ static void rdpsnd_mac_close(rdpsndDevicePlugin* device) static void rdpsnd_mac_free(rdpsndDevicePlugin* device) { + rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; + device->Close(device); + + free(mac); } static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format) @@ -182,7 +233,26 @@ static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT static void rdpsnd_mac_set_volume(rdpsndDevicePlugin* device, UINT32 value) { + OSStatus status; + Float32 fVolume; + UINT16 volumeLeft; + UINT16 volumeRight; + rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; + if (!mac->audioQueue) + return; + + volumeLeft = (value & 0xFFFF); + volumeRight = ((value >> 16) & 0xFFFF); + + fVolume = ((float) volumeLeft) / 65535.0; + + status = AudioQueueSetParameter(mac->audioQueue, kAudioQueueParam_Volume, fVolume); + + if (status != 0) + { + fprintf(stderr, "AudioQueueSetParameter kAudioQueueParam_Volume failed: %f\n", fVolume); + } } static void rdpsnd_mac_start(rdpsndDevicePlugin* device) @@ -200,7 +270,7 @@ static void rdpsnd_mac_start(rdpsndDevicePlugin* device) if (status != 0) { - printf("AudioQueueStart failed\n"); + fprintf(stderr, "AudioQueueStart failed\n"); } mac->isPlaying = TRUE; @@ -212,15 +282,13 @@ static void rdpsnd_mac_play(rdpsndDevicePlugin* device, BYTE* data, int size) int length; AudioQueueBufferRef audioBuffer; rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; - - fprintf(stderr, "WavePlay\n"); if (!mac->isOpen) return; audioBuffer = mac->audioBuffers[mac->audioBufferIndex]; - length = size > AQ_BUF_SIZE ? AQ_BUF_SIZE : size; + length = size > MAC_AUDIO_QUEUE_BUFFER_SIZE ? MAC_AUDIO_QUEUE_BUFFER_SIZE : size; CopyMemory(audioBuffer->mAudioData, data, length); audioBuffer->mAudioDataByteSize = length; @@ -229,8 +297,10 @@ static void rdpsnd_mac_play(rdpsndDevicePlugin* device, BYTE* data, int size) mac->audioBufferIndex++; - if (mac->audioBufferIndex >= AQ_NUM_BUFFERS) + if (mac->audioBufferIndex >= MAC_AUDIO_QUEUE_NUM_BUFFERS) + { mac->audioBufferIndex = 0; + } device->Start(device); } From eb20d0f7709c300470bec87daa364ceaa1369eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 10 Dec 2013 12:30:25 -0500 Subject: [PATCH 087/149] freerdp: fix order of OrderSupport initialization --- client/Mac/MRDPView.m | 29 ++++++++++++++++++++ client/Mac/mf_client.m | 32 ---------------------- client/X11/xf_client.c | 57 ++++++++++++++++++++------------------- client/X11/xf_gdi.c | 4 +-- client/common/cmdline.c | 4 +-- include/freerdp/gdi/gdi.h | 4 +++ 6 files changed, 66 insertions(+), 64 deletions(-) diff --git a/client/Mac/MRDPView.m b/client/Mac/MRDPView.m index b213ea9d8..451f5092a 100644 --- a/client/Mac/MRDPView.m +++ b/client/Mac/MRDPView.m @@ -762,6 +762,35 @@ BOOL mac_pre_connect(freerdp* instance) return -1; } + settings->OsMajorType = OSMAJORTYPE_MACINTOSH; + settings->OsMinorType = OSMINORTYPE_MACINTOSH; + + ZeroMemory(settings->OrderSupport, 32); + settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE; + settings->OrderSupport[NEG_PATBLT_INDEX] = TRUE; + settings->OrderSupport[NEG_SCRBLT_INDEX] = TRUE; + settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; + settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; + settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = TRUE; + settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; + settings->OrderSupport[NEG_LINETO_INDEX] = TRUE; + settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE; + settings->OrderSupport[NEG_MEMBLT_INDEX] = settings->BitmapCacheEnabled; + settings->OrderSupport[NEG_MEM3BLT_INDEX] = (settings->SoftwareGdi) ? TRUE : FALSE; + settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = settings->BitmapCacheEnabled; + settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE; + settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; + settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE; + settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE; + settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE; + settings->OrderSupport[NEG_POLYGON_SC_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; + settings->OrderSupport[NEG_POLYGON_CB_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; + settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; + settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; + freerdp_client_load_addins(instance->context->channels, instance->settings); settings = instance->settings; diff --git a/client/Mac/mf_client.m b/client/Mac/mf_client.m index 6354546fb..6bc679818 100755 --- a/client/Mac/mf_client.m +++ b/client/Mac/mf_client.m @@ -105,38 +105,6 @@ int mfreerdp_client_new(freerdp* instance, rdpContext* context) settings->AsyncChannels = TRUE; settings->AsyncTransport = TRUE; - settings->OsMajorType = OSMAJORTYPE_MACINTOSH; - settings->OsMinorType = OSMINORTYPE_MACINTOSH; - - settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_PATBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_SCRBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; - settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = TRUE; - settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; - settings->OrderSupport[NEG_LINETO_INDEX] = TRUE; - settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE; - settings->OrderSupport[NEG_MEMBLT_INDEX] = settings->BitmapCacheEnabled; - - settings->OrderSupport[NEG_MEM3BLT_INDEX] = (settings->SoftwareGdi) ? TRUE : FALSE; - - settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = settings->BitmapCacheEnabled; - settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE; - settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; - settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE; - settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE; - settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE; - - settings->OrderSupport[NEG_POLYGON_SC_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; - settings->OrderSupport[NEG_POLYGON_CB_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; - - settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; - settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; - return 0; } diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c index a29499339..ffbee746a 100644 --- a/client/X11/xf_client.c +++ b/client/X11/xf_client.c @@ -760,6 +760,35 @@ BOOL xf_pre_connect(freerdp* instance) settings = instance->settings; channels = instance->context->channels; + settings->OsMajorType = OSMAJORTYPE_UNIX; + settings->OsMinorType = OSMINORTYPE_NATIVE_XSERVER; + + ZeroMemory(settings->OrderSupport, 32); + settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE; + settings->OrderSupport[NEG_PATBLT_INDEX] = TRUE; + settings->OrderSupport[NEG_SCRBLT_INDEX] = TRUE; + settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; + settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; + settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = TRUE; + settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; + settings->OrderSupport[NEG_LINETO_INDEX] = TRUE; + settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE; + settings->OrderSupport[NEG_MEMBLT_INDEX] = settings->BitmapCacheEnabled; + settings->OrderSupport[NEG_MEM3BLT_INDEX] = (settings->SoftwareGdi) ? TRUE : FALSE; + settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = settings->BitmapCacheEnabled; + settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE; + settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; + settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE; + settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE; + settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE; + settings->OrderSupport[NEG_POLYGON_SC_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; + settings->OrderSupport[NEG_POLYGON_CB_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; + settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; + settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; + xfc->UseXThreads = TRUE; if (xfc->UseXThreads) @@ -1815,34 +1844,6 @@ static int xfreerdp_client_new(freerdp* instance, rdpContext* context) settings = instance->settings; xfc->settings = instance->context->settings; - settings->OsMajorType = OSMAJORTYPE_UNIX; - settings->OsMinorType = OSMINORTYPE_NATIVE_XSERVER; - - settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_PATBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_SCRBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; - settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = TRUE; - settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; - settings->OrderSupport[NEG_LINETO_INDEX] = TRUE; - settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE; - settings->OrderSupport[NEG_MEMBLT_INDEX] = settings->BitmapCacheEnabled; - settings->OrderSupport[NEG_MEM3BLT_INDEX] = (settings->SoftwareGdi) ? TRUE : FALSE; - settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = settings->BitmapCacheEnabled; - settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE; - settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; - settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE; - settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE; - settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE; - settings->OrderSupport[NEG_POLYGON_SC_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; - settings->OrderSupport[NEG_POLYGON_CB_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; - settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; - settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; - PubSub_SubscribeTerminate(context->pubSub, (pTerminateEventHandler) xf_TerminateEventHandler); PubSub_SubscribeParamChange(context->pubSub, (pParamChangeEventHandler) xf_ParamChangeEventHandler); PubSub_SubscribeScalingFactorChange(context->pubSub, (pScalingFactorChangeEventHandler) xf_ScalingFactorChangeEventHandler); diff --git a/client/X11/xf_gdi.c b/client/X11/xf_gdi.c index 5c2787f39..be63db5c2 100644 --- a/client/X11/xf_gdi.c +++ b/client/X11/xf_gdi.c @@ -830,11 +830,11 @@ void xf_gdi_polygon_cb(rdpContext* context, POLYGON_CB_ORDER* polygon_cb) switch (polygon_cb->fillMode) { - case 1: /* alternate */ + case GDI_FILL_ALTERNATE: /* alternate */ XSetFillRule(xfc->display, xfc->gc, EvenOddRule); break; - case 2: /* winding */ + case GDI_FILL_WINDING: /* winding */ XSetFillRule(xfc->display, xfc->gc, WindingRule); break; diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 0dfe6ef93..47a43011f 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -1483,9 +1483,9 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, } CommandLineSwitchCase(arg, "gdi") { - if (strcmp(arg->Value, "sw") == 0) + if (_stricmp(arg->Value, "sw") == 0) settings->SoftwareGdi = TRUE; - else if (strcmp(arg->Value, "hw") == 0) + else if (_stricmp(arg->Value, "hw") == 0) settings->SoftwareGdi = FALSE; } CommandLineSwitchCase(arg, "gfx") diff --git a/include/freerdp/gdi/gdi.h b/include/freerdp/gdi/gdi.h index a4172521e..9352278bd 100644 --- a/include/freerdp/gdi/gdi.h +++ b/include/freerdp/gdi/gdi.h @@ -113,6 +113,10 @@ #define GDI_OPAQUE 0x00000001 #define GDI_TRANSPARENT 0x00000002 +/* Fill Modes */ +#define GDI_FILL_ALTERNATE 0x01 +#define GDI_FILL_WINDING 0x02 + /* GDI Object Types */ #define GDIOBJECT_BITMAP 0x00 #define GDIOBJECT_PEN 0x01 From 5f96f50e0d4708227c4d976b27b8f3b5d9d3b4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 10 Dec 2013 16:35:46 -0500 Subject: [PATCH 088/149] libfreerdp-gdi: port old cunit gdi tests to ctest --- libfreerdp/gdi/test/CMakeLists.txt | 12 +- libfreerdp/gdi/test/TestGdiBitBlt.c | 1417 ++++++++++++++++++++++++++ libfreerdp/gdi/test/TestGdiClip.c | 349 +++++++ libfreerdp/gdi/test/TestGdiCreate.c | 419 ++++++++ libfreerdp/gdi/test/TestGdiEllipse.c | 127 +++ libfreerdp/gdi/test/TestGdiLine.c | 993 ++++++++++++++++++ libfreerdp/gdi/test/TestGdiRect.c | 149 +++ 7 files changed, 3463 insertions(+), 3 deletions(-) create mode 100644 libfreerdp/gdi/test/TestGdiBitBlt.c create mode 100644 libfreerdp/gdi/test/TestGdiClip.c create mode 100644 libfreerdp/gdi/test/TestGdiCreate.c create mode 100644 libfreerdp/gdi/test/TestGdiEllipse.c create mode 100644 libfreerdp/gdi/test/TestGdiLine.c create mode 100644 libfreerdp/gdi/test/TestGdiRect.c diff --git a/libfreerdp/gdi/test/CMakeLists.txt b/libfreerdp/gdi/test/CMakeLists.txt index fe12cc202..48e5a9125 100644 --- a/libfreerdp/gdi/test/CMakeLists.txt +++ b/libfreerdp/gdi/test/CMakeLists.txt @@ -5,7 +5,13 @@ set(MODULE_PREFIX "TEST_GDI") set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) set(${MODULE_PREFIX}_TESTS - TestGdiRop3.c) + TestGdiRop3.c + TestGdiLine.c + TestGdiRect.c + TestGdiBitBlt.c + TestGdiCreate.c + TestGdiEllipse.c + TestGdiClip.c) create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} @@ -15,7 +21,7 @@ include_directories(..) add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) -set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${CMOCKERY_LIBRARIES}) +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS}) set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MONOLITHIC ${MONOLITHIC_BUILD} @@ -36,5 +42,5 @@ foreach(test ${${MODULE_PREFIX}_TESTS}) add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) endforeach() -set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Test") diff --git a/libfreerdp/gdi/test/TestGdiBitBlt.c b/libfreerdp/gdi/test/TestGdiBitBlt.c new file mode 100644 index 000000000..b164bc61f --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiBitBlt.c @@ -0,0 +1,1417 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* BitBlt() Test Data */ + +/* source bitmap (16x16) */ +BYTE bmp_SRC[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* destination bitmap (16x16) */ +BYTE bmp_DST[256] = +{ + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* pattern bitmap (8x8) */ +BYTE bmp_PAT[64] = +{ + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +/* SRCCOPY (0x00CC0020) */ +BYTE bmp_SRCCOPY[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* BLACKNESS (0x00000042) */ +BYTE bmp_BLACKNESS[256] = +{ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* WHITENESS (0x00FF0062) */ +BYTE bmp_WHITENESS[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* SRCAND (0x008800C6) */ +BYTE bmp_SRCAND[256] = +{ + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* SRCPAINT (0x00EE0086) */ +BYTE bmp_SRCPAINT[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* SRCINVERT (0x00660046) */ +BYTE bmp_SRCINVERT[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* SRCERASE (0x00440328) */ +BYTE bmp_SRCERASE[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* NOTSRCCOPY (0x00330008) */ +BYTE bmp_NOTSRCCOPY[256] = +{ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* NOTSRCERASE (0x001100A6) */ +BYTE bmp_NOTSRCERASE[256] = +{ + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* DSTINVERT (0x00550009) */ +BYTE bmp_DSTINVERT[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* SPna (0x000C0324) */ +BYTE bmp_SPna[256] = +{ + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" +}; + +/* MERGEPAINT (0x00BB0226) */ +BYTE bmp_MERGEPAINT[256] = +{ + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* MERGECOPY (0x00C000CA) */ +BYTE bmp_MERGECOPY[256] = +{ + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +/* PATPAINT (0x00FB0A09) */ +BYTE bmp_PATPAINT[256] = +{ + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +/* PATCOPY (0x00F00021) */ +BYTE bmp_PATCOPY[256] = +{ + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +/* PATINVERT (0x005A0049) */ +BYTE bmp_PATINVERT[256] = +{ + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +int CompareBitmaps(HGDI_BITMAP hBmp1, HGDI_BITMAP hBmp2) +{ + int x, y; + BYTE *p1, *p2; + + int minw = (hBmp1->width < hBmp2->width) ? hBmp1->width : hBmp2->width; + int minh = (hBmp1->height < hBmp2->height) ? hBmp1->height : hBmp2->height; + + if (hBmp1->bitsPerPixel == hBmp2->bitsPerPixel) + { + p1 = hBmp1->data; + p2 = hBmp2->data; + int bpp = hBmp1->bitsPerPixel; + + if (bpp == 32) + { + for (y = 0; y < minh; y++) + { + for (x = 0; x < minw; x++) + { + if (*p1 != *p2) + return 0; + p1++; + p2++; + + if (*p1 != *p2) + return 0; + p1++; + p2++; + + if (*p1 != *p2) + return 0; + p1 += 2; + p2 += 2; + } + } + } + else if (bpp == 16) + { + for (y = 0; y < minh; y++) + { + for (x = 0; x < minw; x++) + { + if (*p1 != *p2) + return 0; + p1++; + p2++; + + if (*p1 != *p2) + return 0; + p1++; + p2++; + } + } + } + else if (bpp == 8) + { + for (y = 0; y < minh; y++) + { + for (x = 0; x < minw; x++) + { + if (*p1 != *p2) + return 0; + p1++; + p2++; + } + } + } + } + else + { + return 0; + } + + return 1; +} + +void test_dump_data(unsigned char* p, int len, int width, const char* name) +{ + unsigned char *line = p; + int i, thisline, offset = 0; + + printf("\n%s[%d][%d]:\n", name, len / width, width); + + while (offset < len) + { + printf("%04x ", offset); + thisline = len - offset; + if (thisline > width) + thisline = width; + + for (i = 0; i < thisline; i++) + printf("%02x ", line[i]); + + for (; i < width; i++) + printf(" "); + + printf("\n"); + offset += thisline; + line += thisline; + } + + printf("\n"); +} + +void test_dump_bitmap(HGDI_BITMAP hBmp, const char* name) +{ + test_dump_data(hBmp->data, hBmp->width * hBmp->height * hBmp->bytesPerPixel, hBmp->width * hBmp->bytesPerPixel, name); +} + +int test_assert_bitmaps_equal(HGDI_BITMAP hBmpActual, HGDI_BITMAP hBmpExpected, const char* name) +{ + int bitmapsEqual = CompareBitmaps(hBmpActual, hBmpExpected); + + if (bitmapsEqual != 1) + { + printf("\n%s\n", name); + test_dump_bitmap(hBmpActual, "Actual"); + test_dump_bitmap(hBmpExpected, "Expected"); + } + + if (bitmapsEqual != 1) + return -1; + + return 0; +} + +int test_gdi_BitBlt_32bpp(void) +{ + BYTE* data; + HGDI_DC hdcSrc; + HGDI_DC hdcDst; + HGDI_BRUSH hBrush; + HGDI_BITMAP hBmpSrc; + HGDI_BITMAP hBmpDst; + HGDI_BITMAP hBmpPat; + HGDI_BITMAP hBmp_SPna; + HGDI_BITMAP hBmp_BLACKNESS; + HGDI_BITMAP hBmp_WHITENESS; + HGDI_BITMAP hBmp_SRCCOPY; + HGDI_BITMAP hBmp_SRCAND; + HGDI_BITMAP hBmp_SRCPAINT; + HGDI_BITMAP hBmp_SRCINVERT; + HGDI_BITMAP hBmp_SRCERASE; + HGDI_BITMAP hBmp_NOTSRCCOPY; + HGDI_BITMAP hBmp_NOTSRCERASE; + HGDI_BITMAP hBmp_DSTINVERT; + HGDI_BITMAP hBmp_MERGECOPY; + HGDI_BITMAP hBmp_MERGEPAINT; + HGDI_BITMAP hBmp_PATCOPY; + HGDI_BITMAP hBmp_PATPAINT; + HGDI_BITMAP hBmp_PATINVERT; + HGDI_BITMAP hBmpDstOriginal; + rdpPalette* hPalette; + HCLRCONV clrconv; + + int bytesPerPixel = 4; + int bitsPerPixel = 32; + + hdcSrc = gdi_GetDC(); + hdcSrc->bytesPerPixel = bytesPerPixel; + hdcSrc->bitsPerPixel = bitsPerPixel; + + hdcDst = gdi_GetDC(); + hdcDst->bytesPerPixel = bytesPerPixel; + hdcDst->bitsPerPixel = bitsPerPixel; + + hPalette = (rdpPalette*) gdi_GetSystemPalette(); + + clrconv = (HCLRCONV) malloc(sizeof(CLRCONV)); + clrconv->alpha = 1; + clrconv->invert = 0; + clrconv->palette = hPalette; + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRC, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpSrc = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DST, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpDst = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DST, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpDstOriginal = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PAT, NULL, 8, 8, 8, bitsPerPixel, clrconv); + hBmpPat = gdi_CreateBitmap(8, 8, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SPna, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SPna = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_BLACKNESS, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_BLACKNESS = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_WHITENESS, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_WHITENESS = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCAND, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCAND = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCERASE, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCERASE = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_NOTSRCCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_NOTSRCCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_NOTSRCERASE, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_NOTSRCERASE = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DSTINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_DSTINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_MERGECOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_MERGECOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_MERGEPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_MERGEPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + gdi_SelectObject(hdcDst, (HGDIOBJECT) hBmpDst); + + /* SRCCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCCOPY, "SRCCOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* BLACKNESS */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_BLACKNESS); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_BLACKNESS, "BLACKNESS") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* WHITENESS */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_WHITENESS); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_WHITENESS, "WHITENESS") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCAND */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCAND); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCAND, "SRCAND") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCPAINT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCPAINT, "SRCPAINT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCINVERT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCINVERT, "SRCINVERT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCERASE */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCERASE); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCERASE, "SRCERASE") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* NOTSRCCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_NOTSRCCOPY); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_NOTSRCCOPY, "NOTSRCCOPY") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* NOTSRCERASE */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_NOTSRCERASE); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_NOTSRCERASE, "NOTSRCERASE") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* DSTINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_DSTINVERT); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_DSTINVERT, "DSTINVERT") < 0) + // return -1; + + /* select a brush for operations using a pattern */ + hBrush = gdi_CreatePatternBrush(hBmpPat); + gdi_SelectObject(hdcDst, (HGDIOBJECT) hBrush); + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* MERGECOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_MERGECOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_MERGECOPY, "MERGECOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* MERGEPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_MERGEPAINT); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_MERGEPAINT, "MERGEPAINT") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATCOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATCOPY, "PATCOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATINVERT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATINVERT, "PATINVERT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATPAINT); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATPAINT, "PATPAINT") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SPna */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SPna); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SPna, "SPna") < 0) + return -1; + + return 0; +} + +int test_gdi_BitBlt_16bpp(void) +{ + BYTE* data; + HGDI_DC hdcSrc; + HGDI_DC hdcDst; + HGDI_BRUSH hBrush; + HGDI_BITMAP hBmpSrc; + HGDI_BITMAP hBmpDst; + HGDI_BITMAP hBmpPat; + HGDI_BITMAP hBmp_SPna; + HGDI_BITMAP hBmp_BLACKNESS; + HGDI_BITMAP hBmp_WHITENESS; + HGDI_BITMAP hBmp_SRCCOPY; + HGDI_BITMAP hBmp_SRCAND; + HGDI_BITMAP hBmp_SRCPAINT; + HGDI_BITMAP hBmp_SRCINVERT; + HGDI_BITMAP hBmp_SRCERASE; + HGDI_BITMAP hBmp_NOTSRCCOPY; + HGDI_BITMAP hBmp_NOTSRCERASE; + HGDI_BITMAP hBmp_DSTINVERT; + HGDI_BITMAP hBmp_MERGECOPY; + HGDI_BITMAP hBmp_MERGEPAINT; + HGDI_BITMAP hBmp_PATCOPY; + HGDI_BITMAP hBmp_PATPAINT; + HGDI_BITMAP hBmp_PATINVERT; + HGDI_BITMAP hBmpDstOriginal; + rdpPalette* hPalette; + HCLRCONV clrconv; + + int bytesPerPixel = 2; + int bitsPerPixel = 16; + + hdcSrc = gdi_GetDC(); + hdcSrc->bytesPerPixel = bytesPerPixel; + hdcSrc->bitsPerPixel = bitsPerPixel; + + hdcDst = gdi_GetDC(); + hdcDst->bytesPerPixel = bytesPerPixel; + hdcDst->bitsPerPixel = bitsPerPixel; + + hPalette = (rdpPalette*) gdi_GetSystemPalette(); + + clrconv = (HCLRCONV) malloc(sizeof(CLRCONV)); + clrconv->alpha = 1; + clrconv->invert = 0; + clrconv->palette = hPalette; + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRC, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpSrc = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DST, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpDst = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DST, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpDstOriginal = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PAT, NULL, 8, 8, 8, bitsPerPixel, clrconv); + hBmpPat = gdi_CreateBitmap(8, 8, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SPna, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SPna = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_BLACKNESS, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_BLACKNESS = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_WHITENESS, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_WHITENESS = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCAND, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCAND = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCERASE, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCERASE = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_NOTSRCCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_NOTSRCCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_NOTSRCERASE, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_NOTSRCERASE = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DSTINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_DSTINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_MERGECOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_MERGECOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_MERGEPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_MERGEPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + gdi_SelectObject(hdcDst, (HGDIOBJECT) hBmpDst); + + /* SRCCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCCOPY, "SRCCOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* BLACKNESS */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_BLACKNESS); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_BLACKNESS, "BLACKNESS") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* WHITENESS */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_WHITENESS); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_WHITENESS, "WHITENESS") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCAND */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCAND); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCAND, "SRCAND") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCPAINT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCPAINT, "SRCPAINT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCINVERT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCINVERT, "SRCINVERT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCERASE */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCERASE); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCERASE, "SRCERASE") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* NOTSRCCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_NOTSRCCOPY); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_NOTSRCCOPY, "NOTSRCCOPY") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* NOTSRCERASE */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_NOTSRCERASE); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_NOTSRCERASE, "NOTSRCERASE") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* DSTINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_DSTINVERT); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_DSTINVERT, "DSTINVERT") < 0) + // return -1; + + /* select a brush for operations using a pattern */ + hBrush = gdi_CreatePatternBrush(hBmpPat); + gdi_SelectObject(hdcDst, (HGDIOBJECT) hBrush); + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* MERGECOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_MERGECOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_MERGECOPY, "MERGECOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* MERGEPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_MERGEPAINT); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_MERGEPAINT, "MERGEPAINT") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATCOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATCOPY, "PATCOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATINVERT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATINVERT, "PATINVERT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATPAINT); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATPAINT, "PATPAINT") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SPna */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SPna); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SPna, "SPna") < 0) + return -1; + + return 0; +} + +int test_gdi_BitBlt_8bpp(void) +{ + BYTE* data; + HGDI_DC hdcSrc; + HGDI_DC hdcDst; + HGDI_BRUSH hBrush; + HGDI_BITMAP hBmpSrc; + HGDI_BITMAP hBmpDst; + HGDI_BITMAP hBmpPat; + HGDI_BITMAP hBmp_SPna; + HGDI_BITMAP hBmp_BLACKNESS; + HGDI_BITMAP hBmp_WHITENESS; + HGDI_BITMAP hBmp_SRCCOPY; + HGDI_BITMAP hBmp_SRCAND; + HGDI_BITMAP hBmp_SRCPAINT; + HGDI_BITMAP hBmp_SRCINVERT; + HGDI_BITMAP hBmp_SRCERASE; + HGDI_BITMAP hBmp_NOTSRCCOPY; + HGDI_BITMAP hBmp_NOTSRCERASE; + HGDI_BITMAP hBmp_DSTINVERT; + HGDI_BITMAP hBmp_MERGECOPY; + HGDI_BITMAP hBmp_MERGEPAINT; + HGDI_BITMAP hBmp_PATCOPY; + HGDI_BITMAP hBmp_PATPAINT; + HGDI_BITMAP hBmp_PATINVERT; + HGDI_BITMAP hBmpDstOriginal; + rdpPalette* hPalette; + HCLRCONV clrconv; + + int bytesPerPixel = 1; + int bitsPerPixel = 8; + + hdcSrc = gdi_GetDC(); + hdcSrc->bytesPerPixel = bytesPerPixel; + hdcSrc->bitsPerPixel = bitsPerPixel; + + hdcDst = gdi_GetDC(); + hdcDst->bytesPerPixel = bytesPerPixel; + hdcDst->bitsPerPixel = bitsPerPixel; + + hPalette = (rdpPalette*) gdi_GetSystemPalette(); + + clrconv = (HCLRCONV) malloc(sizeof(CLRCONV)); + clrconv->alpha = 1; + clrconv->invert = 0; + clrconv->palette = hPalette; + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRC, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpSrc = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DST, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpDst = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DST, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmpDstOriginal = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PAT, NULL, 8, 8, 8, bitsPerPixel, clrconv); + hBmpPat = gdi_CreateBitmap(8, 8, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SPna, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SPna = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_BLACKNESS, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_BLACKNESS = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_WHITENESS, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_WHITENESS = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCAND, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCAND = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_SRCERASE, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_SRCERASE = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_NOTSRCCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_NOTSRCCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_NOTSRCERASE, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_NOTSRCERASE = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_DSTINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_DSTINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_MERGECOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_MERGECOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_MERGEPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_MERGEPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATCOPY, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATCOPY = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATPAINT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATPAINT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) bmp_PATINVERT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_PATINVERT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + gdi_SelectObject(hdcDst, (HGDIOBJECT) hBmpDst); + + /* SRCCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + + if (CompareBitmaps(hBmpDst, hBmp_SRCCOPY) != 1) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* BLACKNESS */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_BLACKNESS); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_BLACKNESS, "BLACKNESS") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* WHITENESS */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_WHITENESS); + + //if (test_assert_bitmaps_equal(hBmpDst, hBmp_WHITENESS, "WHITENESS") < 0) + // return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCAND */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCAND); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCAND, "SRCAND") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCPAINT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCPAINT, "SRCPAINT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCINVERT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCINVERT, "SRCINVERT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SRCERASE */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCERASE); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SRCERASE, "SRCERASE") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* NOTSRCCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_NOTSRCCOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_NOTSRCCOPY, "NOTSRCCOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* NOTSRCERASE */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_NOTSRCERASE); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_NOTSRCERASE, "NOTSRCERASE") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* DSTINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_DSTINVERT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_DSTINVERT, "DSTINVERT") < 0) + return -1; + + /* select a brush for operations using a pattern */ + hBrush = gdi_CreatePatternBrush(hBmpPat); + gdi_SelectObject(hdcDst, (HGDIOBJECT) hBrush); + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* MERGECOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_MERGECOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_MERGECOPY, "MERGECOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* MERGEPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_MERGEPAINT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_MERGEPAINT, "MERGEPAINT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATCOPY */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATCOPY); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATCOPY, "PATCOPY") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATINVERT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATINVERT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATINVERT, "PATINVERT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* PATPAINT */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_PATPAINT); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_PATPAINT, "PATPAINT") < 0) + return -1; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpDstOriginal); + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY); + gdi_SelectObject(hdcSrc, (HGDIOBJECT) hBmpSrc); + + /* SPna */ + gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SPna); + + if (test_assert_bitmaps_equal(hBmpDst, hBmp_SPna, "SPna") < 0) + return -1; + + return 0; +} + +int TestGdiBitBlt(int argc, char* argv[]) +{ + fprintf(stderr, "test_gdi_BitBlt_32bpp()\n"); + + if (test_gdi_BitBlt_32bpp() < 0) + return -1; + + fprintf(stderr, "test_gdi_BitBlt_16bpp()\n"); + + if (test_gdi_BitBlt_16bpp() < 0) + return -1; + + fprintf(stderr, "test_gdi_BitBlt_8bpp()\n"); + + if (test_gdi_BitBlt_8bpp() < 0) + return -1; + + return 0; +} diff --git a/libfreerdp/gdi/test/TestGdiClip.c b/libfreerdp/gdi/test/TestGdiClip.c new file mode 100644 index 000000000..fdf5fa39c --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiClip.c @@ -0,0 +1,349 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int test_gdi_ClipCoords(void) +{ + int draw; + HGDI_DC hdc; + HGDI_RGN rgn1; + HGDI_RGN rgn2; + HGDI_BITMAP bmp; + + hdc = gdi_GetDC(); + hdc->bytesPerPixel = 4; + hdc->bitsPerPixel = 32; + bmp = gdi_CreateBitmap(1024, 768, 4, NULL); + gdi_SelectObject(hdc, (HGDIOBJECT) bmp); + gdi_SetNullClipRgn(hdc); + + rgn1 = gdi_CreateRectRgn(0, 0, 0, 0); + rgn2 = gdi_CreateRectRgn(0, 0, 0, 0); + rgn1->null = 1; + rgn2->null = 1; + + /* null clipping region */ + gdi_SetNullClipRgn(hdc); + gdi_SetRgn(rgn1, 20, 20, 100, 100); + gdi_SetRgn(rgn2, 20, 20, 100, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + /* region all inside clipping region */ + gdi_SetClipRgn(hdc, 0, 0, 1024, 768); + gdi_SetRgn(rgn1, 20, 20, 100, 100); + gdi_SetRgn(rgn2, 20, 20, 100, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + /* region all outside clipping region, on the left */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 20, 20, 100, 100); + gdi_SetRgn(rgn2, 0, 0, 0, 0); + + draw = gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (draw != 0) + return -1; + + /* region all outside clipping region, on the right */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 420, 420, 100, 100); + gdi_SetRgn(rgn2, 0, 0, 0, 0); + + draw = gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (draw != 0) + return -1; + + /* region all outside clipping region, on top */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 20, 100, 100); + gdi_SetRgn(rgn2, 0, 0, 0, 0); + + draw = gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (draw != 0) + return -1; + + /* region all outside clipping region, at the bottom */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 420, 100, 100); + gdi_SetRgn(rgn2, 0, 0, 0, 0); + + draw = gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (draw != 0) + return -1; + + /* left outside, right = clip, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 300, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + /* left outside, right inside, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 250, 100); + gdi_SetRgn(rgn2, 300, 300, 50, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + /* left = clip, right outside, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 300, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + /* left inside, right outside, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 350, 300, 200, 100); + gdi_SetRgn(rgn2, 350, 300, 50, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + /* top outside, bottom = clip, left = clip, right = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 100, 300, 300); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + /* top = clip, bottom outside, left = clip, right = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 100, 200); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + /* top = clip, bottom = clip, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (gdi_EqualRgn(rgn1, rgn2) != 1) + return -1; + + return 0; +} + +int test_gdi_InvalidateRegion(void) +{ + HGDI_DC hdc; + HGDI_RGN rgn1; + HGDI_RGN rgn2; + HGDI_RGN invalid; + HGDI_BITMAP bmp; + + hdc = gdi_GetDC(); + hdc->bytesPerPixel = 4; + hdc->bitsPerPixel = 32; + bmp = gdi_CreateBitmap(1024, 768, 4, NULL); + gdi_SelectObject(hdc, (HGDIOBJECT) bmp); + gdi_SetNullClipRgn(hdc); + + hdc->hwnd = (HGDI_WND) malloc(sizeof(GDI_WND)); + hdc->hwnd->invalid = gdi_CreateRectRgn(0, 0, 0, 0); + hdc->hwnd->invalid->null = 1; + invalid = hdc->hwnd->invalid; + + hdc->hwnd->count = 16; + hdc->hwnd->cinvalid = (HGDI_RGN) malloc(sizeof(GDI_RGN) * hdc->hwnd->count); + + rgn1 = gdi_CreateRectRgn(0, 0, 0, 0); + rgn2 = gdi_CreateRectRgn(0, 0, 0, 0); + rgn1->null = 1; + rgn2->null = 1; + + /* no previous invalid region */ + invalid->null = 1; + gdi_SetRgn(rgn1, 300, 300, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* region same as invalid region */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* left outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 300, 100); + gdi_SetRgn(rgn2, 100, 300, 300, 100); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* right outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 300, 100); + gdi_SetRgn(rgn2, 300, 300, 300, 100); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* top outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 100, 100, 300); + gdi_SetRgn(rgn2, 300, 100, 100, 300); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* bottom outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 100, 300); + gdi_SetRgn(rgn2, 300, 300, 100, 300); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* left outside, right outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 600, 300); + gdi_SetRgn(rgn2, 100, 300, 600, 300); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* top outside, bottom outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 100, 100, 500); + gdi_SetRgn(rgn2, 300, 100, 100, 500); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* all outside, left */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 100, 100); + gdi_SetRgn(rgn2, 100, 300, 300, 100); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* all outside, right */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 700, 300, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 500, 100); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* all outside, top */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 100, 100, 100); + gdi_SetRgn(rgn2, 300, 100, 100, 300); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* all outside, bottom */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 500, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 300); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* all outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 100, 600, 600); + gdi_SetRgn(rgn2, 100, 100, 600, 600); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + /* everything */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 0, 0, 1024, 768); + gdi_SetRgn(rgn2, 0, 0, 1024, 768); + + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (gdi_EqualRgn(invalid, rgn2) != 1) + return -1; + + return 0; +} + +int TestGdiClip(int argc, char* argv[]) +{ + if (test_gdi_ClipCoords() < 0) + return -1; + + if (test_gdi_InvalidateRegion() < 0) + return -1; + + return 0; +} + diff --git a/libfreerdp/gdi/test/TestGdiCreate.c b/libfreerdp/gdi/test/TestGdiCreate.c new file mode 100644 index 000000000..ac319c123 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiCreate.c @@ -0,0 +1,419 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +int test_gdi_GetDC(void) +{ + HGDI_DC hdc = gdi_GetDC(); + + if (hdc->bytesPerPixel != 4) + return -1; + + if (hdc->bitsPerPixel != 32) + return -1; + + if (hdc->drawMode != GDI_R2_BLACK) + return -1; + + return 0; +} + +int test_gdi_CreateCompatibleDC(void) +{ + HGDI_DC hdc; + HGDI_DC chdc; + + hdc = gdi_GetDC(); + hdc->bytesPerPixel = 2; + hdc->bitsPerPixel = 16; + hdc->drawMode = GDI_R2_XORPEN; + + chdc = gdi_CreateCompatibleDC(hdc); + + if (chdc->bytesPerPixel != hdc->bytesPerPixel) + return -1; + + if (chdc->bitsPerPixel != hdc->bitsPerPixel) + return -1; + + if (chdc->drawMode != hdc->drawMode) + return -1; + + return 0; +} + +int test_gdi_CreateBitmap(void) +{ + int bpp; + int width; + int height; + BYTE* data; + HGDI_BITMAP hBitmap; + + bpp = 32; + width = 32; + height = 16; + data = (BYTE*) malloc(width * height * 4); + hBitmap = gdi_CreateBitmap(width, height, bpp, data); + + if (hBitmap->objectType != GDIOBJECT_BITMAP) + return -1; + + if (hBitmap->bitsPerPixel != bpp) + return -1; + + if (hBitmap->width != width) + return -1; + + if (hBitmap->height != height) + return -1; + + if (hBitmap->data != data) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hBitmap); + + return 0; +} + +int test_gdi_CreateCompatibleBitmap(void) +{ + HGDI_DC hdc; + int width; + int height; + HGDI_BITMAP hBitmap; + + hdc = gdi_GetDC(); + hdc->bytesPerPixel = 4; + hdc->bitsPerPixel = 32; + + width = 32; + height = 16; + hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height); + + if (hBitmap->objectType != GDIOBJECT_BITMAP) + return -1; + + if (hBitmap->bytesPerPixel != hdc->bytesPerPixel) + return -1; + + if (hBitmap->bitsPerPixel != hdc->bitsPerPixel) + return -1; + + if (hBitmap->width != width) + return -1; + + if (hBitmap->height != height) + return -1; + + if (!hBitmap->data) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hBitmap); + + return 0; +} + +int test_gdi_CreatePen(void) +{ + HGDI_PEN hPen = gdi_CreatePen(GDI_PS_SOLID, 8, 0xAABBCCDD); + + if (hPen->style != GDI_PS_SOLID) + return -1; + + if (hPen->width != 8) + return -1; + + if (hPen->color != 0xAABBCCDD) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hPen); + + return 0; +} + +int test_gdi_CreateSolidBrush(void) +{ + HGDI_BRUSH hBrush = gdi_CreateSolidBrush(0xAABBCCDD); + + if (hBrush->objectType != GDIOBJECT_BRUSH) + return -1; + + if (hBrush->style != GDI_BS_SOLID) + return -1; + + if (hBrush->color != 0xAABBCCDD) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hBrush); + + return 0; +} + +int test_gdi_CreatePatternBrush(void) +{ + HGDI_BRUSH hBrush; + HGDI_BITMAP hBitmap; + + hBitmap = gdi_CreateBitmap(64, 64, 32, NULL); + hBrush = gdi_CreatePatternBrush(hBitmap); + + if (hBrush->objectType != GDIOBJECT_BRUSH) + return -1; + + if (hBrush->style != GDI_BS_PATTERN) + return -1; + + if (hBrush->pattern != hBitmap) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hBitmap); + + return 0; +} + +int test_gdi_CreateRectRgn(void) +{ + int x1 = 32; + int y1 = 64; + int x2 = 128; + int y2 = 256; + + HGDI_RGN hRegion = gdi_CreateRectRgn(x1, y1, x2, y2); + + if (hRegion->objectType != GDIOBJECT_REGION) + return -1; + + if (hRegion->x != x1) + return -1; + + if (hRegion->y != y1) + return -1; + + if (hRegion->w != x2 - x1 + 1) + return -1; + + if (hRegion->h != y2 - y1 + 1) + return -1; + + if (hRegion->null) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hRegion); + + return 0; +} + +int test_gdi_CreateRect(void) +{ + int x1 = 32; + int y1 = 64; + int x2 = 128; + int y2 = 256; + + HGDI_RECT hRect = gdi_CreateRect(x1, y1, x2, y2); + + if (hRect->objectType != GDIOBJECT_RECT) + return -1; + + if (hRect->left != x1) + return -1; + + if (hRect->top != y1) + return -1; + + if (hRect->right != x2) + return -1; + + if (hRect->bottom != y2) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hRect); + + return 0; +} + +int test_gdi_GetPixel(void) +{ + HGDI_DC hdc; + int width = 128; + int height = 64; + HGDI_BITMAP hBitmap; + + hdc = gdi_GetDC(); + hdc->bytesPerPixel = 4; + hdc->bitsPerPixel = 32; + + hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height); + gdi_SelectObject(hdc, (HGDIOBJECT) hBitmap); + + hBitmap->data[(64 * width * 4) + 32 * 4 + 0] = 0xDD; + hBitmap->data[(64 * width * 4) + 32 * 4 + 1] = 0xCC; + hBitmap->data[(64 * width * 4) + 32 * 4 + 2] = 0xBB; + hBitmap->data[(64 * width * 4) + 32 * 4 + 3] = 0xAA; + + if (gdi_GetPixel(hdc, 32, 64) != 0xAABBCCDD) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hBitmap); + + return 0; +} + +int test_gdi_SetPixel(void) +{ + HGDI_DC hdc; + int width = 128; + int height = 64; + HGDI_BITMAP hBitmap; + + hdc = gdi_GetDC(); + hdc->bytesPerPixel = 4; + hdc->bitsPerPixel = 32; + + hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height); + gdi_SelectObject(hdc, (HGDIOBJECT) hBitmap); + + gdi_SetPixel(hdc, 32, 64, 0xAABBCCDD); + + if (gdi_GetPixel(hdc, 32, 64) != 0xAABBCCDD) + return -1; + + gdi_SetPixel(hdc, width - 1, height - 1, 0xAABBCCDD); + + if (gdi_GetPixel(hdc, width - 1, height - 1) != 0xAABBCCDD) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hBitmap); + + return 0; +} + +int test_gdi_SetROP2(void) +{ + HGDI_DC hdc = gdi_GetDC(); + + gdi_SetROP2(hdc, GDI_R2_BLACK); + + if (hdc->drawMode != GDI_R2_BLACK) + return -1; + + return 0; +} + +int test_gdi_MoveToEx(void) +{ + HGDI_DC hdc; + HGDI_PEN hPen; + HGDI_POINT prevPoint; + + hdc = gdi_GetDC(); + hPen = gdi_CreatePen(GDI_PS_SOLID, 8, 0xAABBCCDD); + gdi_SelectObject(hdc, (HGDIOBJECT) hPen); + gdi_MoveToEx(hdc, 128, 256, NULL); + + if (hdc->pen->posX != 128) + return -1; + + if (hdc->pen->posY != 256) + return -1; + + prevPoint = (HGDI_POINT) malloc(sizeof(GDI_POINT)); + ZeroMemory(prevPoint, sizeof(GDI_POINT)); + + gdi_MoveToEx(hdc, 64, 128, prevPoint); + + if (prevPoint->x != 128) + return -1; + + if (prevPoint->y != 256) + return -1; + + if (hdc->pen->posX != 64) + return -1; + + if (hdc->pen->posY != 128) + return -1; + + return 0; +} + +int TestGdiCreate(int argc, char* argv[]) +{ + fprintf(stderr, "test_gdi_GetDC()\n"); + + if (test_gdi_GetDC() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateCompatibleDC()\n"); + + if (test_gdi_CreateCompatibleDC() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateBitmap()\n"); + + if (test_gdi_CreateBitmap() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateCompatibleBitmap()\n"); + + if (test_gdi_CreateCompatibleBitmap() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreatePen()\n"); + + if (test_gdi_CreatePen() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateSolidBrush()\n"); + + if (test_gdi_CreateSolidBrush() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreatePatternBrush()\n"); + + if (test_gdi_CreatePatternBrush() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateRectRgn()\n"); + + if (test_gdi_CreateRectRgn() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateRect()\n"); + + if (test_gdi_CreateRect() < 0) + return -1; + + fprintf(stderr, "test_gdi_GetPixel()\n"); + + if (test_gdi_GetPixel() < 0) + return -1; + + fprintf(stderr, "test_gdi_SetPixel()\n"); + + if (test_gdi_SetPixel() < 0) + return -1; + + fprintf(stderr, "test_gdi_SetROP2()\n"); + + if (test_gdi_SetROP2() < 0) + return -1; + + fprintf(stderr, "test_gdi_MoveToEx()\n"); + + if (test_gdi_MoveToEx() < 0) + return -1; + + return 0; +} + diff --git a/libfreerdp/gdi/test/TestGdiEllipse.c b/libfreerdp/gdi/test/TestGdiEllipse.c new file mode 100644 index 000000000..9f2323ba0 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiEllipse.c @@ -0,0 +1,127 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Ellipse() Test Data */ + +BYTE ellipse_case_1[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE ellipse_case_2[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE ellipse_case_3[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +int TestGdiEllipse(int argc, char* argv[]) +{ + HGDI_DC hdc; + HGDI_PEN pen; + BYTE* data; + HGDI_BITMAP hBmp; + HGDI_BITMAP hBmp_Ellipse_1; + HGDI_BITMAP hBmp_Ellipse_2; + HGDI_BITMAP hBmp_Ellipse_3; + rdpPalette* hPalette; + HCLRCONV clrconv; + int bitsPerPixel = 8; + int bytesPerPixel = 1; + + hdc = gdi_GetDC(); + hdc->bitsPerPixel = bitsPerPixel; + hdc->bytesPerPixel = bytesPerPixel; + gdi_SetNullClipRgn(hdc); + + pen = gdi_CreatePen(1, 1, 0); + gdi_SelectObject(hdc, (HGDIOBJECT) pen); + + hBmp = gdi_CreateCompatibleBitmap(hdc, 16, 16); + gdi_SelectObject(hdc, (HGDIOBJECT) hBmp); + + hPalette = (rdpPalette*) gdi_GetSystemPalette(); + + clrconv = (HCLRCONV) malloc(sizeof(CLRCONV)); + clrconv->alpha = 1; + clrconv->invert = 0; + clrconv->palette = hPalette; + + data = (BYTE*) freerdp_image_convert((BYTE*) ellipse_case_1, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_Ellipse_1 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) ellipse_case_2, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_Ellipse_2 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) ellipse_case_3, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_Ellipse_3 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + /* Test Case 1: (0,0) -> (16, 16) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_Ellipse(hdc, 0, 0, 16, 16); + + return 0; +} + diff --git a/libfreerdp/gdi/test/TestGdiLine.c b/libfreerdp/gdi/test/TestGdiLine.c new file mode 100644 index 000000000..1c2c132a3 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiLine.c @@ -0,0 +1,993 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* LineTo() Test Data */ + +BYTE line_to_case_1[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_2[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_case_3[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_4[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_5[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_6[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_7[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_8[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_9[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_10[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_case_11[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_BLACK[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_NOTMERGEPEN[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_MASKNOTPEN[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_NOTCOPYPEN[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_MASKPENNOT[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_NOT[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_XORPEN[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_NOTMASKPEN[256] = +{ + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +BYTE line_to_R2_MASKPEN[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_R2_NOTXORPEN[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_R2_NOP[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_R2_MERGENOTPEN[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_R2_COPYPEN[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_R2_MERGEPENNOT[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_R2_MERGEPEN[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +BYTE line_to_R2_WHITE[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* PolylineTo() Test Data */ + +BYTE polyline_to_case_1[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +BYTE polyline_to_case_2[256] = +{ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +extern int test_assert_bitmaps_equal(HGDI_BITMAP hBmpActual, HGDI_BITMAP hBmpExpected, const char* name); + +int TestGdiLine(int argc, char* argv[]) +{ + HGDI_DC hdc; + HGDI_PEN pen; + BYTE* data; + HGDI_BITMAP hBmp; + HGDI_BITMAP hBmp_LineTo_1; + HGDI_BITMAP hBmp_LineTo_2; + HGDI_BITMAP hBmp_LineTo_3; + HGDI_BITMAP hBmp_LineTo_4; + HGDI_BITMAP hBmp_LineTo_5; + HGDI_BITMAP hBmp_LineTo_6; + HGDI_BITMAP hBmp_LineTo_7; + HGDI_BITMAP hBmp_LineTo_8; + HGDI_BITMAP hBmp_LineTo_9; + HGDI_BITMAP hBmp_LineTo_10; + HGDI_BITMAP hBmp_LineTo_11; + HGDI_BITMAP hBmp_LineTo_R2_BLACK; + HGDI_BITMAP hBmp_LineTo_R2_NOTMERGEPEN; + HGDI_BITMAP hBmp_LineTo_R2_MASKNOTPEN; + HGDI_BITMAP hBmp_LineTo_R2_NOTCOPYPEN; + HGDI_BITMAP hBmp_LineTo_R2_MASKPENNOT; + HGDI_BITMAP hBmp_LineTo_R2_NOT; + HGDI_BITMAP hBmp_LineTo_R2_XORPEN; + HGDI_BITMAP hBmp_LineTo_R2_NOTMASKPEN; + HGDI_BITMAP hBmp_LineTo_R2_MASKPEN; + HGDI_BITMAP hBmp_LineTo_R2_NOTXORPEN; + HGDI_BITMAP hBmp_LineTo_R2_NOP; + HGDI_BITMAP hBmp_LineTo_R2_MERGENOTPEN; + HGDI_BITMAP hBmp_LineTo_R2_COPYPEN; + HGDI_BITMAP hBmp_LineTo_R2_MERGEPENNOT; + HGDI_BITMAP hBmp_LineTo_R2_MERGEPEN; + HGDI_BITMAP hBmp_LineTo_R2_WHITE; + rdpPalette* hPalette; + HCLRCONV clrconv; + int bitsPerPixel = 8; + int bytesPerPixel = 1; + + hdc = gdi_GetDC(); + hdc->bitsPerPixel = bitsPerPixel; + hdc->bytesPerPixel = bytesPerPixel; + gdi_SetNullClipRgn(hdc); + + pen = gdi_CreatePen(1, 1, 0); + gdi_SelectObject(hdc, (HGDIOBJECT) pen); + + hBmp = gdi_CreateCompatibleBitmap(hdc, 16, 16); + gdi_SelectObject(hdc, (HGDIOBJECT) hBmp); + + hPalette = (rdpPalette*) gdi_GetSystemPalette(); + + clrconv = (HCLRCONV) malloc(sizeof(CLRCONV)); + clrconv->alpha = 1; + clrconv->invert = 0; + clrconv->palette = hPalette; + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_1, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_1 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_2, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_2 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_3, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_3 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_4, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_4 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_5, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_5 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_5, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_5 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_6, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_6 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_7, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_7 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_8, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_8 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_9, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_9 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_10, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_10 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_case_11, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_11 = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_BLACK, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_BLACK = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_NOTMERGEPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_NOTMERGEPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_MASKNOTPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_MASKNOTPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_NOTCOPYPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_NOTCOPYPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_MASKPENNOT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_MASKPENNOT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_NOT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_NOT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_XORPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_XORPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_NOTMASKPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_NOTMASKPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_MASKPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_MASKPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_NOTXORPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_NOTXORPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_NOP, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_NOP = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_MERGENOTPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_MERGENOTPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_COPYPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_COPYPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_MERGEPENNOT, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_MERGEPENNOT = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_MERGEPEN, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_MERGEPEN = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + data = (BYTE*) freerdp_image_convert((BYTE*) line_to_R2_WHITE, NULL, 16, 16, 8, bitsPerPixel, clrconv); + hBmp_LineTo_R2_WHITE = gdi_CreateBitmap(16, 16, bitsPerPixel, data); + + /* Test Case 1: (0,0) -> (15, 15) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_LineTo(hdc, 15, 15); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_1, "Case 1") < 0) + return -1; + + /* Test Case 2: (15,15) -> (0,0) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 15, 15, NULL); + gdi_LineTo(hdc, 0, 0); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_2, "Case 2") < 0) + return -1; + + /* Test Case 3: (15,0) -> (0,15) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 15, 0, NULL); + gdi_LineTo(hdc, 0, 15); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_3, "Case 3") < 0) + return -1; + + /* Test Case 4: (0,15) -> (15,0) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 0, 15, NULL); + gdi_LineTo(hdc, 15, 0); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_4, "Case 4") < 0) + return -1; + + /* Test Case 5: (0,8) -> (15,8) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 0, 8, NULL); + gdi_LineTo(hdc, 15, 8); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_5, "Case 5") < 0) + return -1; + + /* Test Case 6: (15,8) -> (0,8) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 15, 8, NULL); + gdi_LineTo(hdc, 0, 8); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_6, "Case 6") < 0) + return -1; + + /* Test Case 7: (8,0) -> (8,15) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 8, 0, NULL); + gdi_LineTo(hdc, 8, 15); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_7, "Case 7") < 0) + return -1; + + /* Test Case 8: (8,15) -> (8,0) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 8, 15, NULL); + gdi_LineTo(hdc, 8, 0); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_8, "Case 8") < 0) + return -1; + + /* Test Case 9: (4,4) -> (12,12) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 4, 4, NULL); + gdi_LineTo(hdc, 12, 12); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_9, "Case 9") < 0) + return -1; + + /* Test Case 10: (12,12) -> (4,4) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_MoveToEx(hdc, 12, 12, NULL); + gdi_LineTo(hdc, 4, 4); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_10, "Case 10") < 0) + return -1; + + /* Test Case 11: (0,0) -> (+10,+10) */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_LineTo(hdc, 16 + 10, 16 + 10); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_11, "Case 11") < 0) + return -1; + + /* Test Case 12: (0,0) -> (16,16), R2_BLACK */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_BLACK); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_BLACK, "Case 12") < 0) + return -1; + + /* Test Case 13: (0,0) -> (16,16), R2_NOTMERGEPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_NOTMERGEPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_NOTMERGEPEN, "Case 13") < 0) + return -1; + + /* Test Case 14: (0,0) -> (16,16), R2_MASKNOTPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_MASKNOTPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_MASKNOTPEN, "Case 14") < 0) + return -1; + + /* Test Case 15: (0,0) -> (16,16), R2_NOTCOPYPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_NOTCOPYPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_NOTCOPYPEN, "Case 15") < 0) + return -1; + + /* Test Case 16: (0,0) -> (16,16), R2_MASKPENNOT */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_MASKPENNOT); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_MASKPENNOT, "Case 16") < 0) + return -1; + + /* Test Case 17: (0,0) -> (16,16), R2_NOT */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_NOT); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_NOT, "Case 17") < 0) + return -1; + + /* Test Case 18: (0,0) -> (16,16), R2_XORPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_XORPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_XORPEN, "Case 18") < 0) + return -1; + + /* Test Case 19: (0,0) -> (16,16), R2_NOTMASKPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_NOTMASKPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_NOTMASKPEN, "Case 19") < 0) + return -1; + + /* Test Case 20: (0,0) -> (16,16), R2_MASKPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_MASKPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_MASKPEN, "Case 20") < 0) + return -1; + + /* Test Case 21: (0,0) -> (16,16), R2_NOTXORPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_NOTXORPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_NOTXORPEN, "Case 21") < 0) + return -1; + + /* Test Case 22: (0,0) -> (16,16), R2_NOP */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_NOP); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_NOP, "Case 22") < 0) + return -1; + + /* Test Case 23: (0,0) -> (16,16), R2_MERGENOTPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_MERGENOTPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_MERGENOTPEN, "Case 23") < 0) + return -1; + + /* Test Case 24: (0,0) -> (16,16), R2_COPYPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_COPYPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_COPYPEN, "Case 24") < 0) + return -1; + + /* Test Case 25: (0,0) -> (16,16), R2_MERGEPENNOT */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_MERGEPENNOT); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_MERGEPENNOT, "Case 25") < 0) + return -1; + + /* Test Case 26: (0,0) -> (16,16), R2_MERGEPEN */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_MERGEPEN); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_MERGEPEN, "Case 26") < 0) + return -1; + + /* Test Case 27: (0,0) -> (16,16), R2_WHITE */ + gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS); + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, GDI_R2_WHITE); + gdi_LineTo(hdc, 16, 16); + + if (test_assert_bitmaps_equal(hBmp, hBmp_LineTo_R2_WHITE, "Case 27") < 0) + return -1; + + return 0; +} diff --git a/libfreerdp/gdi/test/TestGdiRect.c b/libfreerdp/gdi/test/TestGdiRect.c new file mode 100644 index 000000000..e4a87bb1f --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiRect.c @@ -0,0 +1,149 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int test_gdi_PtInRect(void) +{ + HGDI_RECT hRect; + int left = 20; + int top = 40; + int right = 60; + int bottom = 80; + + hRect = gdi_CreateRect(left, top, right, bottom); + + if (gdi_PtInRect(hRect, 0, 0) != 0) + return -1; + + if (gdi_PtInRect(hRect, 500, 500) != 0) + return -1; + + if (gdi_PtInRect(hRect, 40, 100) != 0) + return -1; + + if (gdi_PtInRect(hRect, 10, 40) != 0) + return -1; + + if (gdi_PtInRect(hRect, 30, 50) != 1) + return -1; + + if (gdi_PtInRect(hRect, left, top) != 1) + return -1; + + if (gdi_PtInRect(hRect, right, bottom) != 1) + return -1; + + if (gdi_PtInRect(hRect, right, 60) != 1) + return -1; + + if (gdi_PtInRect(hRect, 40, bottom) != 1) + return -1; + + return 0; +} + +int test_gdi_FillRect(void) +{ + HGDI_DC hdc; + HGDI_RECT hRect; + HGDI_BRUSH hBrush; + HGDI_BITMAP hBitmap; + GDI_COLOR color; + GDI_COLOR pixel; + GDI_COLOR rawPixel; + + int x, y; + int badPixels; + int goodPixels; + int width = 200; + int height = 300; + + int left = 20; + int top = 40; + int right = 60; + int bottom = 80; + + hdc = gdi_GetDC(); + hdc->bytesPerPixel = 4; + hdc->bitsPerPixel = 32; + + hRect = gdi_CreateRect(left, top, right, bottom); + + hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height); + ZeroMemory(hBitmap->data, width * height * hdc->bytesPerPixel); + gdi_SelectObject(hdc, (HGDIOBJECT) hBitmap); + + color = (GDI_COLOR) ARGB32(0xFF, 0xAA, 0xBB, 0xCC); + hBrush = gdi_CreateSolidBrush(color); + + gdi_FillRect(hdc, hRect, hBrush); + + badPixels = 0; + goodPixels = 0; + + for (x = 0; x < width; x++) + { + for (y = 0; y < height; y++) + { + rawPixel = gdi_GetPixel(hdc, x, y); + pixel = gdi_get_color_32bpp(hdc, rawPixel); + + if (gdi_PtInRect(hRect, x, y)) + { + if (pixel == color) { + goodPixels++; + } + else { + printf("actual:%04X expected:%04X\n", gdi_GetPixel(hdc, x, y), color); + badPixels++; + } + } + else + { + if (pixel == color) { + badPixels++; + } + else { + goodPixels++; + } + } + } + } + + if (goodPixels != width * height) + return -1; + + if (badPixels != 0) + return -1; + + gdi_DeleteObject((HGDIOBJECT) hBrush); + gdi_DeleteObject((HGDIOBJECT) hBitmap); + + return 0; +} + +int TestGdiRect(int argc, char* argv[]) +{ + if (test_gdi_PtInRect() < 0) + return -1; + + if (test_gdi_FillRect() < 0) + return -1; + + return 0; +} + From 72f58b220d5aa01ad12b4c9fc8a9fd9ca3ed99b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Tue, 10 Dec 2013 17:37:08 -0500 Subject: [PATCH 089/149] Remove deprecated ParamChanged event --- include/freerdp/event.h | 4 ---- libfreerdp/core/freerdp.c | 1 - 2 files changed, 5 deletions(-) diff --git a/include/freerdp/event.h b/include/freerdp/event.h index 3f6d51196..633ea79d5 100644 --- a/include/freerdp/event.h +++ b/include/freerdp/event.h @@ -65,10 +65,6 @@ DEFINE_EVENT_BEGIN(ErrorInfo) UINT32 code; DEFINE_EVENT_END(ErrorInfo) -DEFINE_EVENT_BEGIN(ParamChange) - int id; -DEFINE_EVENT_END(ParamChange) - DEFINE_EVENT_BEGIN(Terminate) int code; DEFINE_EVENT_END(Terminate) diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 57ea44526..2910a0eff 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -364,7 +364,6 @@ static wEventType FreeRDP_Events[] = DEFINE_EVENT_ENTRY(PanningChange) DEFINE_EVENT_ENTRY(ScalingFactorChange) DEFINE_EVENT_ENTRY(ErrorInfo) - DEFINE_EVENT_ENTRY(ParamChange) DEFINE_EVENT_ENTRY(Terminate) DEFINE_EVENT_ENTRY(ConnectionResult) DEFINE_EVENT_ENTRY(ChannelConnected) From bf094ed99782e7a19a79952c49240a6bb3f3fcf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 11 Dec 2013 14:31:54 -0500 Subject: [PATCH 090/149] mfreerdp: code cleanup --- client/Mac/MRDPView.m | 103 +++++++++++------------------------ client/Mac/cli/AppDelegate.m | 45 ++++----------- 2 files changed, 44 insertions(+), 104 deletions(-) diff --git a/client/Mac/MRDPView.m b/client/Mac/MRDPView.m index 451f5092a..f08bcd590 100644 --- a/client/Mac/MRDPView.m +++ b/client/Mac/MRDPView.m @@ -59,6 +59,8 @@ #import "freerdp/types.h" #import "freerdp/channels/channels.h" #import "freerdp/gdi/gdi.h" +#import "freerdp/gdi/dc.h" +#import "freerdp/gdi/region.h" #import "freerdp/graphics.h" #import "freerdp/utils/event.h" #import "freerdp/client/cliprdr.h" @@ -745,7 +747,6 @@ DWORD mac_client_thread(void* param) BOOL mac_pre_connect(freerdp* instance) { rdpSettings* settings; - BOOL bitmap_cache; // setup callbacks instance->update->BeginPaint = mac_begin_paint; @@ -762,6 +763,9 @@ BOOL mac_pre_connect(freerdp* instance) return -1; } + settings->ColorDepth = 32; + settings->SoftwareGdi = TRUE; + settings->OsMajorType = OSMAJORTYPE_MACINTOSH; settings->OsMinorType = OSMINORTYPE_MACINTOSH; @@ -793,44 +797,6 @@ BOOL mac_pre_connect(freerdp* instance) freerdp_client_load_addins(instance->context->channels, instance->settings); - settings = instance->settings; - bitmap_cache = settings->BitmapCacheEnabled; - - instance->settings->ColorDepth = 32; - instance->settings->SoftwareGdi = TRUE; - - settings->OsMajorType = OSMAJORTYPE_UNIX; - settings->OsMinorType = OSMINORTYPE_NATIVE_XSERVER; - - settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_PATBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_SCRBLT_INDEX] = TRUE; - settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; - settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; - settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = TRUE; - settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; - settings->OrderSupport[NEG_LINETO_INDEX] = TRUE; - settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE; - settings->OrderSupport[NEG_MEMBLT_INDEX] = bitmap_cache; - - settings->OrderSupport[NEG_MEM3BLT_INDEX] = (settings->SoftwareGdi) ? TRUE : FALSE; - - settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = bitmap_cache; - settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE; - settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; - settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE; - settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE; - settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE; - - settings->OrderSupport[NEG_POLYGON_SC_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; - settings->OrderSupport[NEG_POLYGON_CB_INDEX] = (settings->SoftwareGdi) ? FALSE : TRUE; - - settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; - settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; - freerdp_channels_pre_connect(instance->context->channels, instance); return TRUE; @@ -886,7 +852,6 @@ BOOL mac_post_connect(freerdp* instance) return TRUE; } - BOOL mac_authenticate(freerdp* instance, char** username, char** password, char** domain) { PasswordDialog* dialog = [PasswordDialog new]; @@ -1093,9 +1058,9 @@ void mac_begin_paint(rdpContext* context) void mac_end_paint(rdpContext* context) { - int i; rdpGdi* gdi; - NSRect drawRect; + HGDI_RGN invalid; + NSRect newDrawRect; int ww, wh, dw, dh; mfContext* mfc = (mfContext*) context; MRDPView* view = (MRDPView*) mfc->view; @@ -1110,7 +1075,7 @@ void mac_end_paint(rdpContext* context) dw = mfc->context.settings->DesktopWidth; dh = mfc->context.settings->DesktopHeight; - if ((context == 0) || (context->gdi == 0)) + if ((!context) || (!context->gdi)) return; if (context->gdi->primary->hdc->hwnd->invalid->null) @@ -1119,36 +1084,32 @@ void mac_end_paint(rdpContext* context) if (context->gdi->drawing != context->gdi->primary) return; - for (i = 0; i < gdi->primary->hdc->hwnd->ninvalid; i++) + invalid = gdi->primary->hdc->hwnd->invalid; + + newDrawRect.origin.x = invalid->x; + newDrawRect.origin.y = invalid->y; + newDrawRect.size.width = invalid->w; + newDrawRect.size.height = invalid->h; + + if (mfc->context.settings->SmartSizing && (ww != dw || wh != dh)) { - drawRect.origin.x = gdi->primary->hdc->hwnd->cinvalid[i].x; - drawRect.origin.y = gdi->primary->hdc->hwnd->cinvalid[i].y; - drawRect.size.width = gdi->primary->hdc->hwnd->cinvalid[i].w; - drawRect.size.height = gdi->primary->hdc->hwnd->cinvalid[i].h; - - if (mfc->context.settings->SmartSizing && (ww != dw || wh != dh)) - { - drawRect.origin.y = drawRect.origin.y * wh / dh - 1; - drawRect.size.height = drawRect.size.height * wh / dh + 1; - drawRect.origin.x = drawRect.origin.x * ww / dw - 1; - drawRect.size.width = drawRect.size.width * ww / dw + 1; - } - else - { - drawRect.origin.y = drawRect.origin.y - 1; - drawRect.size.height = drawRect.size.height + 1; - drawRect.origin.x = drawRect.origin.x - 1; - drawRect.size.width = drawRect.size.width + 1; - } - - windows_to_apple_cords(mfc->view, &drawRect); - - // Note: The xCurrentScroll and yCurrentScroll values do not need to be taken into account - // because the current frame is always at full size, since the scrolling is handled by the external container. - - [view setNeedsDisplayInRect:drawRect]; + newDrawRect.origin.y = newDrawRect.origin.y * wh / dh - 1; + newDrawRect.size.height = newDrawRect.size.height * wh / dh + 1; + newDrawRect.origin.x = newDrawRect.origin.x * ww / dw - 1; + newDrawRect.size.width = newDrawRect.size.width * ww / dw + 1; } - + else + { + newDrawRect.origin.y = newDrawRect.origin.y - 1; + newDrawRect.size.height = newDrawRect.size.height + 1; + newDrawRect.origin.x = newDrawRect.origin.x - 1; + newDrawRect.size.width = newDrawRect.size.width + 1; + } + + windows_to_apple_cords(mfc->view, &newDrawRect); + + [view setNeedsDisplayInRect:newDrawRect]; + gdi->primary->hdc->hwnd->ninvalid = 0; } diff --git a/client/Mac/cli/AppDelegate.m b/client/Mac/cli/AppDelegate.m index 144ba081e..674a12e50 100644 --- a/client/Mac/cli/AppDelegate.m +++ b/client/Mac/cli/AppDelegate.m @@ -16,7 +16,6 @@ static AppDelegate* _singleDelegate = nil; void AppDelegate_EmbedWindowEventHandler(void* context, EmbedWindowEventArgs* e); void AppDelegate_ConnectionResultEventHandler(void* context, ConnectionResultEventArgs* e); void AppDelegate_ErrorInfoEventHandler(void* ctx, ErrorInfoEventArgs* e); -int mac_client_start(rdpContext* context); void mac_set_view_size(rdpContext* context, MRDPView* view); @implementation AppDelegate @@ -61,7 +60,7 @@ void mac_set_view_size(rdpContext* context, MRDPView* view); - (void) applicationWillTerminate:(NSNotification*)notification { [mrdpView releaseResources]; - _singleDelegate = nil; + _singleDelegate = nil; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender @@ -114,8 +113,6 @@ void mac_set_view_size(rdpContext* context, MRDPView* view); clientEntryPoints.Version = RDP_CLIENT_INTERFACE_VERSION; RdpClientEntry(&clientEntryPoints); - - clientEntryPoints.ClientStart = mac_client_start; context = freerdp_client_context_new(&clientEntryPoints); } @@ -170,19 +167,18 @@ void AppDelegate_EmbedWindowEventHandler(void* ctx, EmbedWindowEventArgs* e) { rdpContext* context = (rdpContext*) ctx; - if (_singleDelegate) - { - mfContext* mfc = (mfContext*) context; - _singleDelegate->mrdpView = mfc->view; - - if (_singleDelegate->window) - { - [[_singleDelegate->window contentView] addSubview:mfc->view]; - } - + if (_singleDelegate) + { + mfContext* mfc = (mfContext*) context; + _singleDelegate->mrdpView = mfc->view; + + if (_singleDelegate->window) + { + [[_singleDelegate->window contentView] addSubview:mfc->view]; + } mac_set_view_size(context, mfc->view); - } + } } /** ********************************************************************* @@ -228,7 +224,6 @@ void AppDelegate_ErrorInfoEventHandler(void* ctx, ErrorInfoEventArgs* e) } } - void mac_set_view_size(rdpContext* context, MRDPView* view) { // set client area to specified dimensions @@ -249,22 +244,6 @@ void mac_set_view_size(rdpContext* context, MRDPView* view) // set window to given area [[view window] setFrame:outerRect display:YES]; - - if(context->settings->Fullscreen) + if (context->settings->Fullscreen) [[view window] toggleFullScreen:nil]; } - -int mac_client_start(rdpContext* context) -{ - mfContext* mfc; - MRDPView* view; - - mfc = (mfContext*) context; - view = [[MRDPView alloc] initWithFrame : NSMakeRect(0, 0, context->settings->DesktopWidth, context->settings->DesktopHeight)]; - mfc->view = view; - - [view rdpStart:context]; - mac_set_view_size(context, view); - - return 0; -} From f890771871a6092d8a8c3229f7eee878fe80e8f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 11 Dec 2013 17:21:29 -0500 Subject: [PATCH 091/149] channels/rdpsnd: fix audio duration computation for GSM610 format --- channels/rdpsnd/client/mac/rdpsnd_mac.c | 65 +++++++------------------ libfreerdp/codec/audio.c | 14 +++++- 2 files changed, 29 insertions(+), 50 deletions(-) diff --git a/channels/rdpsnd/client/mac/rdpsnd_mac.c b/channels/rdpsnd/client/mac/rdpsnd_mac.c index 4a00bb3d0..1fe05242c 100644 --- a/channels/rdpsnd/client/mac/rdpsnd_mac.c +++ b/channels/rdpsnd/client/mac/rdpsnd_mac.c @@ -51,9 +51,7 @@ struct rdpsnd_mac_plugin int audioBufferIndex; AudioQueueRef audioQueue; - AudioConverterRef audioConverter; - AudioStreamBasicDescription inputAudioFormat; - AudioStreamBasicDescription outputAudioFormat; + AudioStreamBasicDescription audioFormat; AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS]; }; typedef struct rdpsnd_mac_plugin rdpsndMacPlugin; @@ -65,7 +63,6 @@ static void mac_audio_queue_output_cb(void* inUserData, AudioQueueRef inAQ, Audi static void rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency) { - OSStatus status; rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; mac->latency = (UINT32) latency; @@ -74,61 +71,33 @@ static void rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* form switch (format->wFormatTag) { case WAVE_FORMAT_ALAW: - mac->inputAudioFormat.mFormatID = kAudioFormatALaw; + mac->audioFormat.mFormatID = kAudioFormatALaw; break; case WAVE_FORMAT_MULAW: - mac->inputAudioFormat.mFormatID = kAudioFormatULaw; + mac->audioFormat.mFormatID = kAudioFormatULaw; break; case WAVE_FORMAT_PCM: - mac->inputAudioFormat.mFormatID = kAudioFormatLinearPCM; + mac->audioFormat.mFormatID = kAudioFormatLinearPCM; break; case WAVE_FORMAT_GSM610: - mac->inputAudioFormat.mFormatID = kAudioFormatMicrosoftGSM; + mac->audioFormat.mFormatID = kAudioFormatMicrosoftGSM; break; default: break; } - mac->inputAudioFormat.mSampleRate = format->nSamplesPerSec; - mac->inputAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; - mac->inputAudioFormat.mFramesPerPacket = 1; - mac->inputAudioFormat.mChannelsPerFrame = format->nChannels; - mac->inputAudioFormat.mBitsPerChannel = format->wBitsPerSample; - mac->inputAudioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8; - mac->inputAudioFormat.mBytesPerPacket = format->nBlockAlign; - - mac->outputAudioFormat.mFormatID = kAudioFormatLinearPCM; - mac->outputAudioFormat.mSampleRate = 44100; - mac->outputAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; - mac->outputAudioFormat.mFramesPerPacket = 1; - mac->outputAudioFormat.mChannelsPerFrame = 2; - mac->outputAudioFormat.mBitsPerChannel = 16; - mac->outputAudioFormat.mBytesPerFrame = (16 * 2) / 8; - mac->outputAudioFormat.mBytesPerPacket = 4; - - status = AudioConverterNew(&(mac->inputAudioFormat), - &(mac->outputAudioFormat), &(mac->audioConverter)); - - if (status != 0) - { - fprintf(stderr, "AudioConverterNew failure\n"); - return; - } - - SInt32 channelMap[2] = { 0, 0 }; - - status = AudioConverterSetProperty(mac->audioConverter, kAudioConverterChannelMap, - sizeof(channelMap), channelMap); - - if (status != 0) - { - fprintf(stderr, "AudioConverterSetProperty kAudioConverterChannelMap failure\n"); - return; - } + mac->audioFormat.mSampleRate = format->nSamplesPerSec; + mac->audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; + mac->audioFormat.mFramesPerPacket = 1; + mac->audioFormat.mChannelsPerFrame = format->nChannels; + mac->audioFormat.mBitsPerChannel = format->wBitsPerSample; + mac->audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8; + mac->audioFormat.mBytesPerPacket = format->nBlockAlign; + mac->audioFormat.mReserved = 0; rdpsnd_print_audio_format(format); } @@ -147,7 +116,7 @@ static void rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in device->SetFormat(device, format, 0); - status = AudioQueueNewOutput(&(mac->inputAudioFormat), + status = AudioQueueNewOutput(&(mac->audioFormat), mac_audio_queue_output_cb, mac, NULL, NULL, 0, &(mac->audioQueue)); @@ -217,11 +186,11 @@ static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT } else if (format->wFormatTag == WAVE_FORMAT_ALAW) { - return FALSE; + return TRUE; } else if (format->wFormatTag == WAVE_FORMAT_MULAW) { - return FALSE; + return TRUE; } else if (format->wFormatTag == WAVE_FORMAT_GSM610) { @@ -288,7 +257,7 @@ static void rdpsnd_mac_play(rdpsndDevicePlugin* device, BYTE* data, int size) audioBuffer = mac->audioBuffers[mac->audioBufferIndex]; - length = size > MAC_AUDIO_QUEUE_BUFFER_SIZE ? MAC_AUDIO_QUEUE_BUFFER_SIZE : size; + length = size > audioBuffer->mAudioDataBytesCapacity ? audioBuffer->mAudioDataBytesCapacity : size; CopyMemory(audioBuffer->mAudioData, data, length); audioBuffer->mAudioDataByteSize = length; diff --git a/libfreerdp/codec/audio.c b/libfreerdp/codec/audio.c index 1e45989d3..4140677e3 100644 --- a/libfreerdp/codec/audio.c +++ b/libfreerdp/codec/audio.c @@ -29,14 +29,24 @@ UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size) { UINT32 mstime; UINT32 wSamples; + UINT16 nSamplesPerBlock; /** * [MSDN-AUDIOFORMAT]: * http://msdn.microsoft.com/en-us/library/ms713497.aspx */ - wSamples = (size * 8) / format->wBitsPerSample; - mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + if (format->wFormatTag == WAVE_FORMAT_GSM610) + { + nSamplesPerBlock = *((UINT16*) format->data); + wSamples = (size / format->nBlockAlign) * nSamplesPerBlock; + mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + } + else + { + wSamples = (size * 8) / format->wBitsPerSample; + mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + } return mstime; } From e7974090bb4a4ec23c83b4ffd032ee8da318a34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 11 Dec 2013 17:29:09 -0500 Subject: [PATCH 092/149] freerdp: fix removal of OnParamChange event --- client/Windows/wf_interface.c | 35 ----------------------------------- client/X11/xf_client.c | 32 -------------------------------- 2 files changed, 67 deletions(-) diff --git a/client/Windows/wf_interface.c b/client/Windows/wf_interface.c index 8a1d016ee..6e1b341bd 100644 --- a/client/Windows/wf_interface.c +++ b/client/Windows/wf_interface.c @@ -798,39 +798,6 @@ int freerdp_client_set_window_size(wfContext* wfc, int width, int height) return 0; } -void wf_ParamChangeEventHandler(rdpContext* context, ParamChangeEventArgs* e) -{ - RECT rect; - HMENU hMenu; - wfContext* wfc = (wfContext*) context; - - // specific processing here - switch (e->id) - { - case FreeRDP_SmartSizing: - fprintf(stderr, "SmartSizing changed.\n"); - - if (!context->settings->SmartSizing && (wfc->client_width > context->settings->DesktopWidth || wfc->client_height > context->settings->DesktopHeight)) - { - GetWindowRect(wfc->hwnd, &rect); - SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, MIN(wfc->client_width + wfc->offset_x, rect.right - rect.left), MIN(wfc->client_height + wfc->offset_y, rect.bottom - rect.top), SWP_NOMOVE | SWP_FRAMECHANGED); - wf_update_canvas_diff(wfc); - } - - hMenu = GetSystemMenu(wfc->hwnd, FALSE); - CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING, context->settings->SmartSizing); - wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height); - GetClientRect(wfc->hwnd, &rect); - InvalidateRect(wfc->hwnd, &rect, TRUE); - break; - - case FreeRDP_ConnectionType: - fprintf(stderr, "ConnectionType changed.\n"); - freerdp_set_connection_type(wfc->instance->settings, wfc->instance->settings->ConnectionType); - break; - } -} - // TODO: Some of that code is a duplicate of wf_pre_connect. Refactor? int freerdp_client_load_settings_from_rdp_file(wfContext* wfc, char* filename) { @@ -1016,8 +983,6 @@ int wfreerdp_client_new(freerdp* instance, rdpContext* context) wfc->instance = instance; context->channels = freerdp_channels_new(); - - PubSub_SubscribeParamChange(context->pubSub, wf_ParamChangeEventHandler); return 0; } diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c index ffbee746a..62ab1df49 100644 --- a/client/X11/xf_client.c +++ b/client/X11/xf_client.c @@ -1702,37 +1702,6 @@ void xf_TerminateEventHandler(rdpContext* context, TerminateEventArgs* e) } } -void xf_ParamChangeEventHandler(rdpContext* context, ParamChangeEventArgs* e) -{ - - xfContext* xfc = (xfContext*) context; - - switch (e->id) - { - case FreeRDP_ScalingFactor: - - xfc->currentWidth = xfc->originalWidth * xfc->settings->ScalingFactor; - xfc->currentHeight = xfc->originalHeight * xfc->settings->ScalingFactor; - - xf_transform_window(xfc); - - { - ResizeWindowEventArgs e; - - EventArgsInit(&e, "xfreerdp"); - e.width = (int) xfc->originalWidth * xfc->settings->ScalingFactor; - e.height = (int) xfc->originalHeight * xfc->settings->ScalingFactor; - PubSub_OnResizeWindow(((rdpContext*) xfc)->pubSub, xfc, &e); - } - xf_draw_screen_scaled(xfc, 0, 0, 0, 0, FALSE); - - break; - - default: - break; - } -} - static void xf_ScalingFactorChangeEventHandler(rdpContext* context, ScalingFactorChangeEventArgs* e) { xfContext* xfc = (xfContext*) context; @@ -1845,7 +1814,6 @@ static int xfreerdp_client_new(freerdp* instance, rdpContext* context) xfc->settings = instance->context->settings; PubSub_SubscribeTerminate(context->pubSub, (pTerminateEventHandler) xf_TerminateEventHandler); - PubSub_SubscribeParamChange(context->pubSub, (pParamChangeEventHandler) xf_ParamChangeEventHandler); PubSub_SubscribeScalingFactorChange(context->pubSub, (pScalingFactorChangeEventHandler) xf_ScalingFactorChangeEventHandler); return 0; From 469303a545e1c04b4c7991c9d84ff4c4b6942cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Thu, 12 Dec 2013 11:32:36 -0500 Subject: [PATCH 093/149] Fix: PromptCredentialsOnce / GatewayUseCredentials were always set to true regardless of the value present in the rdp file. --- client/common/file.c | 2 +- libfreerdp/core/settings.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/common/file.c b/client/common/file.c index 4b42517cf..96fdfe446 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -881,7 +881,7 @@ BOOL freerdp_client_populate_settings_from_rdp_file(rdpFile* file, rdpSettings* } if (~file->PromptCredentialOnce) - freerdp_set_param_bool(settings, FreeRDP_GatewayUseSameCredentials, TRUE); + freerdp_set_param_bool(settings, FreeRDP_GatewayUseSameCredentials, file->PromptCredentialOnce); if (~file->RemoteApplicationMode) freerdp_set_param_bool(settings, FreeRDP_RemoteApplicationMode, file->RemoteApplicationMode); diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 982432f43..33dc4b2a1 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -380,7 +380,7 @@ rdpSettings* freerdp_settings_new(DWORD flags) settings->MultifragMaxRequestSize = 0xFFFF; - settings->GatewayUseSameCredentials = TRUE; + settings->GatewayUseSameCredentials = FALSE; settings->FastPathInput = TRUE; settings->FastPathOutput = TRUE; From 3adff0ec60d690b9c638077399e0e89bc26cc2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Fri, 13 Dec 2013 10:11:36 -0500 Subject: [PATCH 094/149] - added CANCELEDBYUSER error code. - AUTHENTICATIONERROR error code correclty set on gateway authentication (http error 401) - Better error handling on connection errors and user cancelation --- include/freerdp/error.h | 1 + libfreerdp/core/gateway/ncacn_http.c | 84 +++++++++++++++++++--------- libfreerdp/core/gateway/ntlm.c | 4 +- libfreerdp/core/gateway/rpc_bind.c | 3 + libfreerdp/core/nla.c | 4 ++ 5 files changed, 67 insertions(+), 29 deletions(-) diff --git a/include/freerdp/error.h b/include/freerdp/error.h index 91ea67f7e..a5733bb8a 100755 --- a/include/freerdp/error.h +++ b/include/freerdp/error.h @@ -173,6 +173,7 @@ FREERDP_API extern int connectErrorCode; #define TLSCONNECTERROR ERRORSTART + 8 #define AUTHENTICATIONERROR ERRORSTART + 9 #define INSUFFICIENTPRIVILEGESERROR ERRORSTART + 10 +#define CANCELEDBYUSER ERRORSTART + 11 #ifdef __cplusplus } diff --git a/libfreerdp/core/gateway/ncacn_http.c b/libfreerdp/core/gateway/ncacn_http.c index 57ba66776..e13591531 100644 --- a/libfreerdp/core/gateway/ncacn_http.c +++ b/libfreerdp/core/gateway/ncacn_http.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -98,12 +99,20 @@ int rpc_ncacn_http_recv_in_channel_response(rdpRpc* rpc) http_response = http_response_recv(rpc->TlsIn); - ntlm_token_data = NULL; - crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam), - &ntlm_token_data, &ntlm_token_length); + if (http_response->StatusCode == HTTP_STATUS_DENIED) + { + if (!connectErrorCode) + connectErrorCode = AUTHENTICATIONERROR; + } + else if (http_response->AuthParam) + { + ntlm_token_data = NULL; + crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam), + &ntlm_token_data, &ntlm_token_length); - ntlm->inputBuffer[0].pvBuffer = ntlm_token_data; - ntlm->inputBuffer[0].cbBuffer = ntlm_token_length; + ntlm->inputBuffer[0].pvBuffer = ntlm_token_data; + ntlm->inputBuffer[0].cbBuffer = ntlm_token_length; + } http_response_free(http_response); @@ -136,7 +145,10 @@ int rpc_ncacn_http_ntlm_init(rdpRpc* rpc, TSG_CHANNEL channel) &settings->GatewayUsername, &settings->GatewayPassword, &settings->GatewayDomain); if (!proceed) + { + connectErrorCode = CANCELEDBYUSER; return 0; + } if (settings->GatewayUseSameCredentials) { @@ -147,40 +159,51 @@ int rpc_ncacn_http_ntlm_init(rdpRpc* rpc, TSG_CHANNEL channel) } } - ntlm_client_init(ntlm, TRUE, settings->GatewayUsername, + if (!ntlm_client_init(ntlm, TRUE, settings->GatewayUsername, settings->GatewayDomain, settings->GatewayPassword, - rpc->TlsIn->Bindings); + rpc->TlsIn->Bindings)) + { + return 0; + } //ntlm_client_make_spn(ntlm, NULL, settings->GatewayHostname); - ntlm_client_make_spn(ntlm, _T("HTTP"), settings->GatewayHostname); + if (!ntlm_client_make_spn(ntlm, _T("HTTP"), settings->GatewayHostname)) + { + return 0; + } - return 0; + return 1; } BOOL rpc_ntlm_http_in_connect(rdpRpc* rpc) { rdpNtlm* ntlm = rpc->NtlmHttpIn->ntlm; + BOOL success = FALSE; - rpc_ncacn_http_ntlm_init(rpc, TSG_CHANNEL_IN); + if (rpc_ncacn_http_ntlm_init(rpc, TSG_CHANNEL_IN) == 1) + { + success = TRUE; - /* Send IN Channel Request */ + /* Send IN Channel Request */ - rpc_ncacn_http_send_in_channel_request(rpc); + rpc_ncacn_http_send_in_channel_request(rpc); - /* Receive IN Channel Response */ + /* Receive IN Channel Response */ - rpc_ncacn_http_recv_in_channel_response(rpc); + rpc_ncacn_http_recv_in_channel_response(rpc); - /* Send IN Channel Request */ + /* Send IN Channel Request */ - rpc_ncacn_http_send_in_channel_request(rpc); + rpc_ncacn_http_send_in_channel_request(rpc); + + ntlm_client_uninit(ntlm); + } - ntlm_client_uninit(ntlm); ntlm_free(ntlm); rpc->NtlmHttpIn->ntlm = NULL; - return TRUE; + return success; } int rpc_ncacn_http_send_out_channel_request(rdpRpc* rpc) @@ -230,25 +253,32 @@ int rpc_ncacn_http_recv_out_channel_response(rdpRpc* rpc) BOOL rpc_ntlm_http_out_connect(rdpRpc* rpc) { rdpNtlm* ntlm = rpc->NtlmHttpOut->ntlm; + BOOL success = FALSE; - rpc_ncacn_http_ntlm_init(rpc, TSG_CHANNEL_OUT); + if (rpc_ncacn_http_ntlm_init(rpc, TSG_CHANNEL_OUT) == 1) + { + success = TRUE; - /* Send OUT Channel Request */ + /* Send OUT Channel Request */ - rpc_ncacn_http_send_out_channel_request(rpc); + rpc_ncacn_http_send_out_channel_request(rpc); - /* Receive OUT Channel Response */ + /* Receive OUT Channel Response */ - rpc_ncacn_http_recv_out_channel_response(rpc); + rpc_ncacn_http_recv_out_channel_response(rpc); - /* Send OUT Channel Request */ + /* Send OUT Channel Request */ - rpc_ncacn_http_send_out_channel_request(rpc); + rpc_ncacn_http_send_out_channel_request(rpc); + + ntlm_client_uninit(ntlm); + } - ntlm_client_uninit(ntlm); ntlm_free(ntlm); - return TRUE; + rpc->NtlmHttpOut->ntlm = NULL; + + return success; } void rpc_ntlm_http_init_channel(rdpRpc* rpc, rdpNtlmHttp* ntlm_http, TSG_CHANNEL channel) diff --git a/libfreerdp/core/gateway/ntlm.c b/libfreerdp/core/gateway/ntlm.c index 5764194d1..0b9454b52 100644 --- a/libfreerdp/core/gateway/ntlm.c +++ b/libfreerdp/core/gateway/ntlm.c @@ -151,7 +151,7 @@ BOOL ntlm_client_make_spn(rdpNtlm* ntlm, LPCTSTR ServiceClass, char* hostname) status = DsMakeSpn(ServiceClass, hostnameX, NULL, 0, NULL, &SpnLength, ntlm->ServicePrincipalName); if (status != ERROR_SUCCESS) - return -1; + return FALSE; return TRUE; } @@ -229,7 +229,7 @@ BOOL ntlm_authenticate(rdpNtlm* ntlm) if ((!ntlm) || (!ntlm->table)) { - fprintf(stderr, "rpc_write: invalid ntlm context\n"); + fprintf(stderr, "ntlm_authenticate: invalid ntlm context\n"); return FALSE; } diff --git a/libfreerdp/core/gateway/rpc_bind.c b/libfreerdp/core/gateway/rpc_bind.c index 7c2041758..108d7ff14 100644 --- a/libfreerdp/core/gateway/rpc_bind.c +++ b/libfreerdp/core/gateway/rpc_bind.c @@ -118,7 +118,10 @@ int rpc_send_bind_pdu(rdpRpc* rpc) &settings->GatewayUsername, &settings->GatewayPassword, &settings->GatewayDomain); if (!proceed) + { + connectErrorCode = CANCELEDBYUSER; return 0; + } if (settings->GatewayUseSameCredentials) { diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index 5e1a2feec..5f5da67a6 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -147,7 +147,11 @@ int credssp_ntlm_client_init(rdpCredssp* credssp) &settings->Username, &settings->Password, &settings->Domain); if (!proceed) + { + connectErrorCode = CANCELEDBYUSER; return 0; + } + } } From 36a1323dceb0690d5422a809d51072d079cdc8f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Fri, 13 Dec 2013 16:58:46 -0500 Subject: [PATCH 095/149] fixed error parsing rdp file: lines without value were ignored before being marked as formatted, resulting in duplicate values being written to the RDP file afterwards. --- client/common/file.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/client/common/file.c b/client/common/file.c index 96fdfe446..acac04acb 100644 --- a/client/common/file.c +++ b/client/common/file.c @@ -49,7 +49,7 @@ static WCHAR CR_LF_STR_W[] = { '\r', '\n', '\0' }; #define INVALID_INTEGER_VALUE 0xFFFFFFFF -BOOL freerdp_client_rdp_file_set_integer(rdpFile* file, char* name, int value, int index) +BOOL freerdp_client_rdp_file_set_integer(rdpFile* file, const char* name, int value, int index) { BOOL bStandard = TRUE; @@ -229,13 +229,13 @@ void freerdp_client_parse_rdp_file_integer_unicode(rdpFile* file, WCHAR* name, W free(valueA); } -void freerdp_client_parse_rdp_file_integer_ascii(rdpFile* file, char* name, char* value, int index) +void freerdp_client_parse_rdp_file_integer_ascii(rdpFile* file, const char* name, const char* value, int index) { int ivalue = atoi(value); freerdp_client_rdp_file_set_integer(file, name, ivalue, index); } -BOOL freerdp_client_rdp_file_set_string(rdpFile* file, char* name, char* value, int index) +BOOL freerdp_client_rdp_file_set_string(rdpFile* file, const char* name, const char* value, int index) { BOOL bStandard = TRUE; @@ -436,8 +436,6 @@ BOOL freerdp_client_parse_rdp_file_buffer_ascii(rdpFile* file, const BYTE* buffe if ((d2 - d1) != 2) goto next_line; /* improper type length */ - if (d2 == end) - goto next_line; /* no value */ *d1 = 0; *d2 = 0; @@ -514,8 +512,6 @@ BOOL freerdp_client_parse_rdp_file_buffer_unicode(rdpFile* file, const BYTE* buf if ((d2 - d1) != 2) goto next_line; /* improper type length */ - if (d2 == end) - goto next_line; /* no value */ *d1 = 0; *d2 = 0; @@ -1067,7 +1063,7 @@ int freerdp_client_rdp_file_set_string_option(rdpFile* file, const char* name, c index = freerdp_client_parse_rdp_file_add_line(file, text, -1); line = freerdp_client_rdp_file_find_line_index(file, index); - freerdp_client_rdp_file_set_string(file, (char*) name, (char*) value, index); + freerdp_client_rdp_file_set_string(file, name, value, index); free(text); } From 6acd0ed84a87c55d66329f684ab336336af057fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 15 Dec 2013 15:59:51 -0500 Subject: [PATCH 096/149] channels/rdpsnd: improve winmm rdpsnd implementation --- channels/rdpsnd/client/rdpsnd_main.c | 29 ++- channels/rdpsnd/client/winmm/rdpsnd_winmm.c | 238 +++++++++++--------- include/freerdp/client/rdpsnd.h | 4 + 3 files changed, 162 insertions(+), 109 deletions(-) diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c index 30387d7a5..3c485cacd 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -428,7 +428,10 @@ void rdpsnd_confirm_wave(rdpsndPlugin* rdpsnd, RDPSND_WAVE* wave) static void rdpsnd_device_send_wave_confirm_pdu(rdpsndDevicePlugin* device, RDPSND_WAVE* wave) { - MessageQueue_Post(device->rdpsnd->MsgPipe->Out, NULL, 0, (void*) wave, NULL); + if (device->DisableConfirmThread) + rdpsnd_confirm_wave(device->rdpsnd, wave); + else + MessageQueue_Post(device->rdpsnd->MsgPipe->Out, NULL, 0, (void*) wave, NULL); } static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s) @@ -461,6 +464,7 @@ static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s) wave->data = data; wave->length = size; + wave->AutoConfirm = TRUE; format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo]; wave->wAudioLength = rdpsnd_compute_audio_time_length(format, size); @@ -470,6 +474,9 @@ static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s) if (!rdpsnd->device) { + wave->wLocalTimeB = wave->wLocalTimeA; + wave->wTimeStampB = wave->wTimeStampA; + rdpsnd_confirm_wave(rdpsnd, wave); free(wave); return; } @@ -493,7 +500,9 @@ static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s) wave->wTimeStampB = rdpsnd->wTimeStamp + wave->wAudioLength + TIME_DELAY_MS; wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + TIME_DELAY_MS; } - rdpsnd->device->WaveConfirm(rdpsnd->device, wave); + + if (wave->AutoConfirm) + rdpsnd->device->WaveConfirm(rdpsnd->device, wave); } static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd) @@ -718,10 +727,6 @@ static void rdpsnd_process_connect(rdpsndPlugin* rdpsnd) rdpsnd->latency = -1; - rdpsnd->ScheduleThread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread, - (void*) rdpsnd, 0, NULL); - args = (ADDIN_ARGV*) rdpsnd->channelEntryPoints.pExtendedData; if (args) @@ -730,7 +735,9 @@ static void rdpsnd_process_connect(rdpsndPlugin* rdpsnd) if (rdpsnd->subsystem) { if (strcmp(rdpsnd->subsystem, "fake") == 0) + { return; + } rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); } @@ -794,6 +801,13 @@ static void rdpsnd_process_connect(rdpsndPlugin* rdpsnd) DEBUG_WARN("no sound device."); return; } + + if (!rdpsnd->device->DisableConfirmThread) + { + rdpsnd->ScheduleThread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread, + (void*) rdpsnd, 0, NULL); + } } @@ -1075,7 +1089,8 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints, pEntryPoints->cbSize); rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client"); - //WLog_SetLogLevel(rdpsnd->log, WLOG_TRACE); + + WLog_SetLogLevel(rdpsnd->log, WLOG_TRACE); rdpsnd->channelEntryPoints.pVirtualChannelInit(&rdpsnd->InitHandle, &rdpsnd->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, rdpsnd_virtual_channel_init_event); diff --git a/channels/rdpsnd/client/winmm/rdpsnd_winmm.c b/channels/rdpsnd/client/winmm/rdpsnd_winmm.c index 564b2f63f..b19f114ba 100644 --- a/channels/rdpsnd/client/winmm/rdpsnd_winmm.c +++ b/channels/rdpsnd/client/winmm/rdpsnd_winmm.c @@ -38,28 +38,16 @@ #include "rdpsnd_main.h" -typedef struct rdpsnd_winmm_datablock rdpsndWinmmDatablock; - -struct rdpsnd_winmm_datablock -{ - WAVEHDR header; - rdpsndWinmmDatablock* next; -}; - typedef struct rdpsnd_winmm_plugin rdpsndWinmmPlugin; struct rdpsnd_winmm_plugin { rdpsndDevicePlugin device; - HWAVEOUT out_handle; + HWAVEOUT hWaveOut; WAVEFORMATEX format; int wformat; int block_size; - int latency; - HANDLE event; - rdpsndWinmmDatablock* datablock_head; - FREERDP_DSP_CONTEXT* dsp_context; }; @@ -92,28 +80,9 @@ static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* ou return result; } -static void rdpsnd_winmm_clear_datablocks(rdpsndWinmmPlugin* winmm, BOOL drain) -{ - rdpsndWinmmDatablock* datablock; - - while ((datablock = winmm->datablock_head) != NULL) - { - if (!drain && (datablock->header.dwFlags & WHDR_DONE) == 0) - break; - - while ((datablock->header.dwFlags & WHDR_DONE) == 0) - WaitForSingleObject(winmm->event, INFINITE); - - winmm->datablock_head = datablock->next; - - free(datablock->header.lpData); - free(datablock); - } -} - static void rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency) { - rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device; + rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; if (format) { @@ -122,57 +91,108 @@ static void rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* fo winmm->wformat = format->wFormatTag; winmm->block_size = format->nBlockAlign; } +} - winmm->latency = latency; +static void CALLBACK rdpsnd_winmm_callback_function(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) +{ + MMRESULT mmResult; + RDPSND_WAVE* wave; + LPWAVEHDR lpWaveHdr; + rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) dwInstance; + + switch (uMsg) + { + case MM_WOM_OPEN: + fprintf(stderr, "MM_WOM_OPEN\n"); + break; + + case MM_WOM_CLOSE: + fprintf(stderr, "MM_WOM_CLOSE\n"); + break; + + case MM_WOM_DONE: + { + UINT32 wTimeDelta; + lpWaveHdr = (LPWAVEHDR) dwParam1; + + if (!lpWaveHdr) + return; + + wave = (RDPSND_WAVE*) lpWaveHdr->dwUser; + + if (!wave) + return; + + fprintf(stderr, "MM_WOM_DONE: dwBufferLength: %d cBlockNo: %d\n", + lpWaveHdr->dwBufferLength, wave->cBlockNo); + + wave->wLocalTimeB = GetTickCount(); + wTimeDelta = wave->wLocalTimeB - wave->wLocalTimeA; + wave->wTimeStampB = wave->wTimeStampA + wTimeDelta; + + winmm->device.WaveConfirm(&(winmm->device), wave); + + if (lpWaveHdr->lpData) + free(lpWaveHdr->lpData); + + free(wave); + } + break; + } } static void rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency) { - MMRESULT result; + MMRESULT mmResult; rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; - if (winmm->out_handle != NULL) + if (winmm->hWaveOut) return; - DEBUG_SVC("opening"); - rdpsnd_winmm_set_format(device, format, latency); freerdp_dsp_context_reset_adpcm(winmm->dsp_context); - result = waveOutOpen(&winmm->out_handle, WAVE_MAPPER, &winmm->format, (DWORD_PTR) winmm->event, 0, CALLBACK_EVENT); + mmResult = waveOutOpen(&winmm->hWaveOut, WAVE_MAPPER, &winmm->format, + (DWORD_PTR) rdpsnd_winmm_callback_function, (DWORD_PTR) winmm, CALLBACK_FUNCTION); - if (result != MMSYSERR_NOERROR) + if (mmResult != MMSYSERR_NOERROR) { - DEBUG_WARN("waveOutOpen failed: %d", result); + fprintf(stderr, "waveOutOpen failed: %d\n", mmResult); } } static void rdpsnd_winmm_close(rdpsndDevicePlugin* device) { - rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device; + MMRESULT mmResult; + rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; - if (winmm->out_handle) + if (winmm->hWaveOut) { - DEBUG_SVC("close"); - rdpsnd_winmm_clear_datablocks(winmm, TRUE); + mmResult = waveOutReset(winmm->hWaveOut); - if (waveOutClose(winmm->out_handle) != MMSYSERR_NOERROR) + mmResult = waveOutClose(winmm->hWaveOut); + + if (mmResult != MMSYSERR_NOERROR) { - DEBUG_WARN("waveOutClose error"); + fprintf(stderr, "waveOutClose failure: %d\n", mmResult); } - winmm->out_handle = NULL; + winmm->hWaveOut = NULL; } } static void rdpsnd_winmm_free(rdpsndDevicePlugin* device) { - rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device; + rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; - rdpsnd_winmm_close(device); - freerdp_dsp_context_free(winmm->dsp_context); - CloseHandle(winmm->event); - free(winmm); + if (winmm) + { + rdpsnd_winmm_close(device); + + freerdp_dsp_context_free(winmm->dsp_context); + + free(winmm); + } } static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format) @@ -203,10 +223,10 @@ static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device) dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */ dwVolume = (dwVolumeLeft << 16) | dwVolumeRight; - if (!winmm->out_handle) + if (!winmm->hWaveOut) return dwVolume; - waveOutGetVolume(winmm->out_handle, &dwVolume); + waveOutGetVolume(winmm->hWaveOut, &dwVolume); return dwVolume; } @@ -215,82 +235,94 @@ static void rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value) { rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; - if (!winmm->out_handle) + if (!winmm->hWaveOut) return; - waveOutSetVolume(winmm->out_handle, value); + waveOutSetVolume(winmm->hWaveOut, value); } -static void rdpsnd_winmm_play(rdpsndDevicePlugin* device, BYTE* data, int size) +static void rdpsnd_winmm_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave) { - BYTE* src; - MMRESULT result; - rdpsndWinmmDatablock* last; - rdpsndWinmmDatablock* datablock; + int length; + BYTE* data; rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; - if (!winmm->out_handle) - return; - if (winmm->wformat == WAVE_FORMAT_ADPCM) { winmm->dsp_context->decode_ms_adpcm(winmm->dsp_context, - data, size, winmm->format.nChannels, winmm->block_size); - size = winmm->dsp_context->adpcm_size; - src = winmm->dsp_context->adpcm_buffer; + wave->data, wave->length, winmm->format.nChannels, winmm->block_size); + length = winmm->dsp_context->adpcm_size; + data = winmm->dsp_context->adpcm_buffer; } else if (winmm->wformat == WAVE_FORMAT_DVI_ADPCM) { winmm->dsp_context->decode_ima_adpcm(winmm->dsp_context, - data, size, winmm->format.nChannels, winmm->block_size); - size = winmm->dsp_context->adpcm_size; - src = winmm->dsp_context->adpcm_buffer; + wave->data, wave->length, winmm->format.nChannels, winmm->block_size); + length = winmm->dsp_context->adpcm_size; + data = winmm->dsp_context->adpcm_buffer; } else { - src = data; + length = wave->length; + data = wave->data; } - rdpsnd_winmm_clear_datablocks(winmm, FALSE); + wave->data = (BYTE*) malloc(length); + CopyMemory(wave->data, data, length); + wave->length = length; +} - for (last = winmm->datablock_head; last && last->next; last = last->next) +void rdpsnd_winmm_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave) +{ + MMRESULT mmResult; + LPWAVEHDR lpWaveHdr; + rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; + + if (!winmm->hWaveOut) + return; + + wave->AutoConfirm = FALSE; + + lpWaveHdr = (LPWAVEHDR) malloc(sizeof(WAVEHDR)); + + if (!lpWaveHdr) + return; + + ZeroMemory(lpWaveHdr, sizeof(WAVEHDR)); + + lpWaveHdr->dwFlags = 0; + lpWaveHdr->dwLoops = 0; + lpWaveHdr->lpData = (LPSTR) wave->data; + lpWaveHdr->dwBufferLength = wave->length; + lpWaveHdr->dwUser = (DWORD_PTR) wave; + lpWaveHdr->lpNext = NULL; + + mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); + + if (mmResult != MMSYSERR_NOERROR) { - - } - - datablock = (rdpsndWinmmDatablock*) malloc(sizeof(rdpsndWinmmDatablock)); - ZeroMemory(datablock, sizeof(rdpsndWinmmDatablock)); - - if (last) - last->next = datablock; - else - winmm->datablock_head = datablock; - - datablock->header.dwBufferLength = size; - datablock->header.lpData = (LPSTR) malloc(size); - CopyMemory(datablock->header.lpData, src, size); - - result = waveOutPrepareHeader(winmm->out_handle, &datablock->header, sizeof(datablock->header)); - - if (result != MMSYSERR_NOERROR) - { - DEBUG_WARN("waveOutPrepareHeader: %d", result); + fprintf(stderr, "waveOutPrepareHeader failure: %d\n", mmResult); + return; + } + + mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); + + if (mmResult != MMSYSERR_NOERROR) + { + fprintf(stderr, "waveOutWrite failure: %d\n", mmResult); + waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR)); return; } - - ResetEvent(winmm->event); - waveOutWrite(winmm->out_handle, &datablock->header, sizeof(datablock->header)); } static void rdpsnd_winmm_start(rdpsndDevicePlugin* device) { - rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; - - rdpsnd_winmm_clear_datablocks(winmm, FALSE); + //rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device; } static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args) { + } #ifdef STATIC_CHANNELS @@ -305,12 +337,15 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE winmm = (rdpsndWinmmPlugin*) malloc(sizeof(rdpsndWinmmPlugin)); ZeroMemory(winmm, sizeof(rdpsndWinmmPlugin)); + winmm->device.DisableConfirmThread = TRUE; + winmm->device.Open = rdpsnd_winmm_open; winmm->device.FormatSupported = rdpsnd_winmm_format_supported; winmm->device.SetFormat = rdpsnd_winmm_set_format; winmm->device.GetVolume = rdpsnd_winmm_get_volume; winmm->device.SetVolume = rdpsnd_winmm_set_volume; - winmm->device.Play = rdpsnd_winmm_play; + winmm->device.WaveDecode = rdpsnd_winmm_wave_decode; + winmm->device.WavePlay = rdpsnd_winmm_wave_play; winmm->device.Start = rdpsnd_winmm_start; winmm->device.Close = rdpsnd_winmm_close; winmm->device.Free = rdpsnd_winmm_free; @@ -319,7 +354,6 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*) winmm, args); winmm->dsp_context = freerdp_dsp_context_new(); - winmm->event = CreateEvent(NULL, TRUE, FALSE, NULL); pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) winmm); diff --git a/include/freerdp/client/rdpsnd.h b/include/freerdp/client/rdpsnd.h index 8c873c6ce..21debb1bb 100644 --- a/include/freerdp/client/rdpsnd.h +++ b/include/freerdp/client/rdpsnd.h @@ -43,6 +43,8 @@ struct _RDPSND_WAVE UINT32 wLocalTimeA; UINT32 wLocalTimeB; + + BOOL AutoConfirm; }; typedef struct _RDPSND_WAVE RDPSND_WAVE; @@ -81,6 +83,8 @@ struct rdpsnd_device_plugin pcWaveDecode WaveDecode; pcWavePlay WavePlay; pcWaveConfirm WaveConfirm; + + BOOL DisableConfirmThread; }; #define RDPSND_DEVICE_EXPORT_FUNC_NAME "freerdp_rdpsnd_client_subsystem_entry" From 652dbfd50df18ffd6009447a07284e08dad152fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 17 Dec 2013 11:51:13 -0500 Subject: [PATCH 097/149] libwinpr-sspi: fix NTLM TargetName bug --- winpr/libwinpr/sspi/NTLM/ntlm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.c b/winpr/libwinpr/sspi/NTLM/ntlm.c index 2848e4abd..13f36bc15 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm.c @@ -89,7 +89,7 @@ void ntlm_SetContextTargetName(NTLM_CONTEXT* context, char* TargetName) { GetComputerNameExA(ComputerNameDnsHostname, NULL, &nSize); name = malloc(nSize); - GetComputerNameExA(ComputerNameDnsHostname, TargetName, &nSize); + GetComputerNameExA(ComputerNameDnsHostname, name, &nSize); CharUpperA(TargetName); } From f4a0216c7610ef44b4507d2a5733c9f4dff13a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Tue, 17 Dec 2013 18:20:33 -0500 Subject: [PATCH 098/149] Fixed WaitForSingleObject and WaitForMultipleObjects (timeouts incorrectly sets). Added detailed error reporting, better timeout error handling. --- winpr/libwinpr/synch/wait.c | 108 +++++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 7 deletions(-) diff --git a/winpr/libwinpr/synch/wait.c b/winpr/libwinpr/synch/wait.c index 8c09bd47d..cfa6b6441 100644 --- a/winpr/libwinpr/synch/wait.c +++ b/winpr/libwinpr/synch/wait.c @@ -168,7 +168,10 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) PVOID Object; if (!winpr_Handle_GetInfo(hHandle, &Type, &Object)) + { + fprintf(stderr, "WaitForSingleObject failed: invalid hHandle.\n"); return WAIT_FAILED; + } if (Type == HANDLE_TYPE_THREAD) { @@ -218,6 +221,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) if (waitpid(process->pid, &(process->status), 0) != -1) { + fprintf(stderr, "WaitForSingleObject: waitpid failure [%d] %s\n", errno, strerror(errno)); return WAIT_FAILED; } @@ -260,14 +264,18 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0)) { - timeout.tv_usec = dwMilliseconds * 1000; + timeout.tv_sec = dwMilliseconds / 1000; + timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } status = select(event->pipe_fd[0] + 1, &rfds, NULL, NULL, (dwMilliseconds == INFINITE) ? NULL : &timeout); if (status < 0) + { + fprintf(stderr, "WaitForSingleObject: event select() failure [%d] %s\n", errno, strerror(errno)); return WAIT_FAILED; + } if (status != 1) return WAIT_TIMEOUT; @@ -292,14 +300,18 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0)) { - timeout.tv_usec = dwMilliseconds * 1000; + timeout.tv_sec = dwMilliseconds / 1000; + timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } status = select(semaphore->pipe_fd[0] + 1, &rfds, 0, 0, (dwMilliseconds == INFINITE) ? NULL : &timeout); if (status < 0) + { + fprintf(stderr, "WaitForSingleObject: semaphore select() failure [%d] %s\n", errno, strerror(errno)); return WAIT_FAILED; + } if (status != 1) return WAIT_TIMEOUT; @@ -307,7 +319,10 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) length = read(semaphore->pipe_fd[0], &length, 1); if (length != 1) + { + fprintf(stderr, "WaitForSingleObject: semaphore read failure [%d] %s\n", errno, strerror(errno)); return WAIT_FAILED; + } } #else @@ -339,28 +354,49 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0)) { - timeout.tv_usec = dwMilliseconds * 1000; + timeout.tv_sec = dwMilliseconds / 1000; + timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } status = select(timer->fd + 1, &rfds, 0, 0, (dwMilliseconds == INFINITE) ? NULL : &timeout); if (status < 0) + { + fprintf(stderr, "WaitForSingleObject: timer select() failure [%d] %s\n", errno, strerror(errno)); return WAIT_FAILED; + } if (status != 1) return WAIT_TIMEOUT; - status = read(timer->fd, (void*) &expirations, sizeof(UINT64)); + length = read(timer->fd, (void*) &expirations, sizeof(UINT64)); - if (status != 8) - return WAIT_TIMEOUT; + if (length != 8) + { + if (length == -1) + { + if (errno == ETIMEDOUT) + return WAIT_TIMEOUT; + + fprintf(stderr, "WaitForSingleObject: timer read() failure [%d] %s\n", errno, strerror(errno)); + } + else + { + fprintf(stderr, "WaitForSingleObject: timer read() failure - incorrect number of bytes read"); + } + + return WAIT_FAILED; + } } else { + fprintf(stderr, "WaitForSingleObject: invalid timer file descriptor\n"); return WAIT_FAILED; } + #else + fprintf(stderr, "WaitForSingleObject: file descriptors not supported\n"); return WAIT_FAILED; #endif } @@ -375,7 +411,11 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd; if (fd == -1) + { + fprintf(stderr, "WaitForSingleObject: invalid pipe file descriptor\n"); return WAIT_FAILED; + } + FD_ZERO(&rfds); FD_SET(fd, &rfds); @@ -383,17 +423,23 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0)) { - timeout.tv_usec = dwMilliseconds * 1000; + timeout.tv_sec = dwMilliseconds / 1000; + timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } status = select(fd + 1, &rfds, NULL, NULL, (dwMilliseconds == INFINITE) ? NULL : &timeout); if (status < 0) + { + fprintf(stderr, "WaitForSingleObject: named pipe select() failure [%d] %s\n", errno, strerror(errno)); return WAIT_FAILED; + } if (status != 1) + { return WAIT_TIMEOUT; + } } else { @@ -422,7 +468,11 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl struct timeval timeout; if (!nCount) + { + fprintf(stderr, "WaitForMultipleObjects: invalid handles count\n"); return WAIT_FAILED; + } + maxfd = 0; FD_ZERO(&fds); @@ -437,17 +487,27 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl for (index = 0; index < nCount; index++) { if (!winpr_Handle_GetInfo(lpHandles[index], &Type, &Object)) + { + fprintf(stderr, "WaitForMultipleObjects: invalid handle\n"); return WAIT_FAILED; + } if (Type == HANDLE_TYPE_EVENT) { fd = ((WINPR_EVENT*) Object)->pipe_fd[0]; + + if (fd == -1) + { + fprintf(stderr, "WaitForMultipleObjects: invalid event file descriptor\n"); + return WAIT_FAILED; + } } else if (Type == HANDLE_TYPE_SEMAPHORE) { #ifdef WINPR_PIPE_SEMAPHORE fd = ((WINPR_SEMAPHORE*) Object)->pipe_fd[0]; #else + fprintf(stderr, "WaitForMultipleObjects: semaphore not supported\n"); return WAIT_FAILED; #endif } @@ -455,6 +515,12 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl { WINPR_TIMER* timer = (WINPR_TIMER*) Object; fd = timer->fd; + + if (fd == -1) + { + fprintf(stderr, "WaitForMultipleObjects: invalid timer file descriptor\n"); + return WAIT_FAILED; + } } else if (Type == HANDLE_TYPE_NAMED_PIPE) { @@ -462,15 +528,22 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl fd = (pipe->ServerMode) ? pipe->serverfd : pipe->clientfd; if (fd == -1) + { + fprintf(stderr, "WaitForMultipleObjects: invalid timer file descriptor\n"); return WAIT_FAILED; + } } else { + fprintf(stderr, "WaitForMultipleObjects: unknown handle type %lu\n", Type); return WAIT_FAILED; } if (fd == -1) + { + fprintf(stderr, "WaitForMultipleObjects: invalid file descriptor\n"); return WAIT_FAILED; + } FD_SET(fd, &fds); @@ -487,7 +560,10 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl (dwMilliseconds == INFINITE) ? NULL : &timeout); if (status < 0) + { + fprintf(stderr, "WaitForMultipleObjects: select() failure [%d] %s\n", errno, strerror(errno)); return WAIT_FAILED; + } if (status == 0) return WAIT_TIMEOUT; @@ -524,7 +600,10 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl length = read(fd, &length, 1); if (length != 1) + { + fprintf(stderr, "WaitForMultipleObjects: semaphore read() failure [%d] %s\n", errno, strerror(errno)); return WAIT_FAILED; + } } else if (Type == HANDLE_TYPE_TIMER) { @@ -534,13 +613,28 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl length = read(fd, (void*) &expirations, sizeof(UINT64)); if (length != 8) + { + if (length == -1) + { + if (errno == ETIMEDOUT) + return WAIT_TIMEOUT; + + fprintf(stderr, "WaitForMultipleObjects: timer read() failure [%d] %s\n", errno, strerror(errno)); + } + else + { + fprintf(stderr, "WaitForMultipleObjects: timer read() failure - incorrect number of bytes read"); + } + return WAIT_FAILED; + } } return (WAIT_OBJECT_0 + index); } } + fprintf(stderr, "WaitForMultipleObjects: failed (unknown error)\n"); return WAIT_FAILED; } From 043b834ac2f9e2aee5283521f301929e26787190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Tue, 17 Dec 2013 18:21:12 -0500 Subject: [PATCH 099/149] Fixed WaitForSingleObject and WaitForMultipleObjects (timeouts incorrectly sets). Added detailed error reporting, better timeout error handling. --- winpr/libwinpr/synch/wait.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/winpr/libwinpr/synch/wait.c b/winpr/libwinpr/synch/wait.c index cfa6b6441..6bfd2e23a 100644 --- a/winpr/libwinpr/synch/wait.c +++ b/winpr/libwinpr/synch/wait.c @@ -553,7 +553,8 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0)) { - timeout.tv_usec = dwMilliseconds * 1000; + timeout.tv_sec = dwMilliseconds / 1000; + timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } status = select(maxfd + 1, &fds, 0, 0, From 5de7a4f082eb1fa9301bf2adcc24ef365d11ba21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Wed, 18 Dec 2013 12:44:40 -0500 Subject: [PATCH 100/149] Code cleanup --- client/Mac/MRDPView.m | 5 +---- libfreerdp/core/settings.c | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/client/Mac/MRDPView.m b/client/Mac/MRDPView.m index f08bcd590..da2992629 100644 --- a/client/Mac/MRDPView.m +++ b/client/Mac/MRDPView.m @@ -916,10 +916,7 @@ void mf_Pointer_New(rdpContext* context, rdpPointer* pointer) freerdp_alpha_cursor_convert(cursor_data, pointer->xorMaskData, pointer->andMaskData, pointer->width, pointer->height, pointer->xorBpp, context->gdi->clrconv); - - // TODO if xorBpp is > 24 need to call freerdp_image_swap_color_order - // see file df_graphics.c - + /* store cursor bitmap image in representation - required by NSImage */ bmiRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **) &cursor_data pixelsWide:rect.size.width diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 33dc4b2a1..b9ad028c8 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -248,8 +248,6 @@ rdpSettings* freerdp_settings_new(DWORD flags) settings->DisableThemes = FALSE; settings->ConnectionType = CONNECTION_TYPE_LAN; - settings->AutoReconnectionEnabled = TRUE; - settings->EncryptionMethods = ENCRYPTION_METHOD_NONE; settings->EncryptionLevel = ENCRYPTION_LEVEL_NONE; From 9245d364cc0e7d2507084637d2517b64c85c0446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Wed, 18 Dec 2013 12:59:53 -0500 Subject: [PATCH 101/149] Fixed handling gateway authentication error Handle NULL pdu --- libfreerdp/core/gateway/ncacn_http.c | 7 +------ libfreerdp/core/gateway/rts.c | 9 ++++++++- libfreerdp/core/gateway/tsg.c | 6 ++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/libfreerdp/core/gateway/ncacn_http.c b/libfreerdp/core/gateway/ncacn_http.c index e13591531..35525f5c7 100644 --- a/libfreerdp/core/gateway/ncacn_http.c +++ b/libfreerdp/core/gateway/ncacn_http.c @@ -99,12 +99,7 @@ int rpc_ncacn_http_recv_in_channel_response(rdpRpc* rpc) http_response = http_response_recv(rpc->TlsIn); - if (http_response->StatusCode == HTTP_STATUS_DENIED) - { - if (!connectErrorCode) - connectErrorCode = AUTHENTICATIONERROR; - } - else if (http_response->AuthParam) + if (http_response->AuthParam) { ntlm_token_data = NULL; crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam), diff --git a/libfreerdp/core/gateway/rts.c b/libfreerdp/core/gateway/rts.c index 2cd982f63..e5820cecd 100644 --- a/libfreerdp/core/gateway/rts.c +++ b/libfreerdp/core/gateway/rts.c @@ -22,6 +22,7 @@ #endif #include +#include #include "ncacn_http.h" #include "rpc_client.h" @@ -147,11 +148,17 @@ BOOL rts_connect(rdpRpc* rpc) http_response = http_response_recv(rpc->TlsOut); - if (http_response->StatusCode != 200) + if (http_response->StatusCode != HTTP_STATUS_OK) { fprintf(stderr, "rts_connect error! Status Code: %d\n", http_response->StatusCode); http_response_print(http_response); http_response_free(http_response); + + if (!connectErrorCode && http_response->StatusCode == HTTP_STATUS_DENIED) + { + connectErrorCode = AUTHENTICATIONERROR; + } + return FALSE; } diff --git a/libfreerdp/core/gateway/tsg.c b/libfreerdp/core/gateway/tsg.c index 53595cd03..b8bbb552c 100644 --- a/libfreerdp/core/gateway/tsg.c +++ b/libfreerdp/core/gateway/tsg.c @@ -1367,6 +1367,9 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port) pdu = rpc_recv_dequeue_pdu(rpc); + if (!pdu) + return FALSE; + call = rpc_client_call_find_by_id(rpc, pdu->CallId); if (call->OpNum == TsProxyMakeTunnelCallOpnum) @@ -1409,6 +1412,9 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port) #if 0 pdu = rpc_recv_dequeue_pdu(rpc); + if (!pdu) + return FALSE; + if (!TsProxySetupReceivePipeReadResponse(tsg, pdu)) { fprintf(stderr, "TsProxySetupReceivePipe: error reading response\n"); From d94122c07bc6b303e0ef608b2dd8d5dd837e70ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20LeBlanc?= Date: Wed, 18 Dec 2013 13:00:16 -0500 Subject: [PATCH 102/149] Fixed mac cli message box message --- client/Mac/cli/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/Mac/cli/AppDelegate.m b/client/Mac/cli/AppDelegate.m index 674a12e50..351111b97 100644 --- a/client/Mac/cli/AppDelegate.m +++ b/client/Mac/cli/AppDelegate.m @@ -195,7 +195,7 @@ void AppDelegate_ConnectionResultEventHandler(void* ctx, ConnectionResultEventAr NSString* message = nil; if (connectErrorCode == AUTHENTICATIONERROR) { - message = [NSString stringWithFormat:@"%@:\n%@", message, @"Authentication failure, check credentials."]; + message = [NSString stringWithFormat:@"%@", @"Authentication failure, check credentials."]; } From 51ad85e0ee2b921a6819b48218f370579973ac97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 18 Dec 2013 19:44:18 -0500 Subject: [PATCH 103/149] libfreerdp-core: send Access Denied TLS alert when server-side NLA fails --- include/freerdp/crypto/tls.h | 33 ++++++++++++++++++++++++++ libfreerdp/core/nla.c | 2 +- libfreerdp/core/transport.c | 3 +++ libfreerdp/crypto/tls.c | 46 +++++++++++++++++++++++++++++++++--- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/include/freerdp/crypto/tls.h b/include/freerdp/crypto/tls.h index 712f82a87..486db2bd6 100644 --- a/include/freerdp/crypto/tls.h +++ b/include/freerdp/crypto/tls.h @@ -34,6 +34,35 @@ #include +#define TLS_ALERT_LEVEL_WARNING 1 +#define TLS_ALERT_LEVEL_FATAL 2 + +#define TLS_ALERT_DESCRIPTION_CLOSE_NOTIFY 0 +#define TLS_ALERT_DESCRIPTION_UNEXPECTED_MESSAGE 10 +#define TLS_ALERT_DESCRIPTION_BAD_RECORD_MAC 20 +#define TLS_ALERT_DESCRIPTION_DECRYPTION_FAILED 21 +#define TLS_ALERT_DESCRIPTION_RECORD_OVERFLOW 22 +#define TLS_ALERT_DESCRIPTION_DECOMPRESSION_FAILURE 30 +#define TLS_ALERT_DESCRIPTION_HANSHAKE_FAILURE 40 +#define TLS_ALERT_DESCRIPTION_NO_CERTIFICATE 41 +#define TLS_ALERT_DESCRIPTION_BAD_CERTIFICATE 42 +#define TLS_ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE 43 +#define TLS_ALERT_DESCRIPTION_CERTIFICATE_REVOKED 44 +#define TLS_ALERT_DESCRIPTION_CERTIFICATE_EXPIRED 45 +#define TLS_ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN 46 +#define TLS_ALERT_DESCRIPTION_ILLEGAL_PARAMETER 47 +#define TLS_ALERT_DESCRIPTION_UNKNOWN_CA 48 +#define TLS_ALERT_DESCRIPTION_ACCESS_DENIED 49 +#define TLS_ALERT_DESCRIPTION_DECODE_ERROR 50 +#define TLS_ALERT_DESCRIPTION_DECRYPT_ERROR 51 +#define TLS_ALERT_DESCRIPTION_EXPORT_RESTRICTION 60 +#define TLS_ALERT_DESCRIPTION_PROTOCOL_VERSION 70 +#define TLS_ALERT_DESCRIPTION_INSUFFICIENT_SECURITY 71 +#define TLS_ALERT_DESCRIPTION_INTERNAL_ERROR 80 +#define TLS_ALERT_DESCRIPTION_USER_CANCELED 90 +#define TLS_ALERT_DESCRIPTION_NO_RENEGOTIATION 100 +#define TLS_ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION 110 + typedef struct rdp_tls rdpTls; struct rdp_tls @@ -51,6 +80,8 @@ struct rdp_tls rdpCertificateStore* certificate_store; char* hostname; int port; + int alertLevel; + int alertDescription; }; FREERDP_API BOOL tls_connect(rdpTls* tls); @@ -65,6 +96,8 @@ FREERDP_API int tls_write_all(rdpTls* tls, BYTE* data, int length); FREERDP_API int tls_wait_read(rdpTls* tls); FREERDP_API int tls_wait_write(rdpTls* tls); +FREERDP_API int tls_set_alert_code(rdpTls* tls, int level, int description); + FREERDP_API BOOL tls_match_hostname(char *pattern, int pattern_length, char *hostname); FREERDP_API BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port); FREERDP_API void tls_print_certificate_error(char* hostname, char* fingerprint, char* hosts_file); diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index 5e1a2feec..b78fbdc92 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -623,7 +623,7 @@ int credssp_server_authenticate(rdpCredssp* credssp) if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED)) { fprintf(stderr, "AcceptSecurityContext status: 0x%08X\n", status); - return -1; + return -1; /* Access Denied */ } /* send authentication token */ diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 55ba1596b..567fc169e 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -452,6 +452,9 @@ BOOL transport_accept_nla(rdpTransport* transport) fprintf(stderr, "client authentication failure\n"); credssp_free(transport->credssp); transport->credssp = NULL; + + tls_set_alert_code(transport->TlsIn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DESCRIPTION_ACCESS_DENIED); + return FALSE; } diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 75ac6e936..4fe93ee94 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -110,8 +110,6 @@ BOOL tls_connect(rdpTls* tls) CryptoCert cert; long options = 0; int connection_status; - char *hostname; - int port; tls->ctx = SSL_CTX_new(TLSv1_client_method()); @@ -360,7 +358,38 @@ BOOL tls_disconnect(rdpTls* tls) return FALSE; if (tls->ssl) - SSL_shutdown(tls->ssl); + { + if (tls->alertDescription != TLS_ALERT_DESCRIPTION_CLOSE_NOTIFY) + { + /** + * OpenSSL doesn't really expose an API for sending a TLS alert manually. + * + * The following code disables the sending of the default "close notify" + * and then proceeds to force sending a custom TLS alert before shutting down. + * + * Manually sending a TLS alert is necessary in certain cases, + * like when server-side NLA results in an authentication failure. + */ + + SSL_set_quiet_shutdown(tls->ssl, 1); + + if ((tls->alertLevel == TLS_ALERT_LEVEL_FATAL) && (tls->ssl->session)) + SSL_CTX_remove_session(tls->ssl->ctx, tls->ssl->session); + + tls->ssl->s3->alert_dispatch = 1; + tls->ssl->s3->send_alert[0] = tls->alertLevel; + tls->ssl->s3->send_alert[1] = tls->alertDescription; + + if (tls->ssl->s3->wbuf.left == 0) + tls->ssl->method->ssl_dispatch_alert(tls->ssl); + + SSL_shutdown(tls->ssl); + } + else + { + SSL_shutdown(tls->ssl); + } + } return TRUE; } @@ -549,6 +578,14 @@ BOOL tls_print_error(char* func, SSL* connection, int value) } } +int tls_set_alert_code(rdpTls* tls, int level, int description) +{ + tls->alertLevel = level; + tls->alertDescription = description; + + return 0; +} + BOOL tls_match_hostname(char *pattern, int pattern_length, char *hostname) { if (strlen(hostname) == pattern_length) @@ -868,6 +905,9 @@ rdpTls* tls_new(rdpSettings* settings) tls->settings = settings; tls->certificate_store = certificate_store_new(settings); + + tls->alertLevel = TLS_ALERT_LEVEL_WARNING; + tls->alertDescription = TLS_ALERT_DESCRIPTION_CLOSE_NOTIFY; } return tls; From 05947dd0b279b14ebf09aa5b78981bd73bc9fceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 18 Dec 2013 22:02:59 -0500 Subject: [PATCH 104/149] libfreerdp-core: fix linux build --- libfreerdp/core/tcp.h | 1 + winpr/libwinpr/synch/wait.c | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libfreerdp/core/tcp.h b/libfreerdp/core/tcp.h index 3ae94da34..b43fbaf1c 100644 --- a/libfreerdp/core/tcp.h +++ b/libfreerdp/core/tcp.h @@ -29,6 +29,7 @@ #include #include #include +#include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 diff --git a/winpr/libwinpr/synch/wait.c b/winpr/libwinpr/synch/wait.c index 6bfd2e23a..017d59353 100644 --- a/winpr/libwinpr/synch/wait.c +++ b/winpr/libwinpr/synch/wait.c @@ -370,11 +370,11 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) if (status != 1) return WAIT_TIMEOUT; - length = read(timer->fd, (void*) &expirations, sizeof(UINT64)); + status = read(timer->fd, (void*) &expirations, sizeof(UINT64)); - if (length != 8) + if (status != 8) { - if (length == -1) + if (status == -1) { if (errno == ETIMEDOUT) return WAIT_TIMEOUT; @@ -416,7 +416,6 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) return WAIT_FAILED; } - FD_ZERO(&rfds); FD_SET(fd, &rfds); ZeroMemory(&timeout, sizeof(timeout)); From 9c1dca14f721e9ab82d5880a2622c8eab6927cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 18 Dec 2013 22:53:34 -0500 Subject: [PATCH 105/149] libwinpr-input: minor code style cleanup --- winpr/libwinpr/input/scancode.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/winpr/libwinpr/input/scancode.c b/winpr/libwinpr/input/scancode.c index fe08882d5..578ed387e 100644 --- a/winpr/libwinpr/input/scancode.c +++ b/winpr/libwinpr/input/scancode.c @@ -567,10 +567,11 @@ static DWORD KBD7X[128] = DWORD GetVirtualKeyCodeFromVirtualScanCode(DWORD scancode, DWORD dwKeyboardType) { - DWORD code_index; + DWORD codeIndex; - code_index = scancode & 0xff; - if (code_index > 127) + codeIndex = scancode & 0xFF; + + if (codeIndex > 127) return VK_NONE; if ((dwKeyboardType != 4) && (dwKeyboardType != 7)) @@ -578,11 +579,11 @@ DWORD GetVirtualKeyCodeFromVirtualScanCode(DWORD scancode, DWORD dwKeyboardType) if (dwKeyboardType == 4) { - return (scancode & KBDEXT) ? KBD4X[code_index] : KBD4T[code_index]; + return (scancode & KBDEXT) ? KBD4X[codeIndex] : KBD4T[codeIndex]; } else if (dwKeyboardType == 7) { - return (scancode & KBDEXT) ? KBD7X[code_index] : KBD7T[code_index]; + return (scancode & KBDEXT) ? KBD7X[codeIndex] : KBD7T[codeIndex]; } return VK_NONE; @@ -592,8 +593,10 @@ DWORD GetVirtualScanCodeFromVirtualKeyCode(DWORD vkcode, DWORD dwKeyboardType) { int i; DWORD scancode; + DWORD codeIndex; scancode = 0; + codeIndex = vkcode & 0xFF; if ((dwKeyboardType != 4) && (dwKeyboardType != 7)) dwKeyboardType = 4; @@ -604,7 +607,7 @@ DWORD GetVirtualScanCodeFromVirtualKeyCode(DWORD vkcode, DWORD dwKeyboardType) { for (i = 0; i < 128; i++) { - if (KBD4X[i] == (vkcode & 0xFF)) + if (KBD4X[i] == codeIndex) { scancode = (i | KBDEXT); break; @@ -615,7 +618,7 @@ DWORD GetVirtualScanCodeFromVirtualKeyCode(DWORD vkcode, DWORD dwKeyboardType) { for (i = 0; i < 128; i++) { - if (KBD4T[i] == (vkcode & 0xFF)) + if (KBD4T[i] == codeIndex) { scancode = i; break; @@ -629,7 +632,7 @@ DWORD GetVirtualScanCodeFromVirtualKeyCode(DWORD vkcode, DWORD dwKeyboardType) { for (i = 0; i < 128; i++) { - if (KBD7X[i] == (vkcode & 0xFF)) + if (KBD7X[i] == codeIndex) { scancode = (i | KBDEXT); break; @@ -640,7 +643,7 @@ DWORD GetVirtualScanCodeFromVirtualKeyCode(DWORD vkcode, DWORD dwKeyboardType) { for (i = 0; i < 128; i++) { - if (KBD7T[i] == (vkcode & 0xFF)) + if (KBD7T[i] == codeIndex) { scancode = i; break; From 74a323270798ea9dd73a98ba8c0d01332d158e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 19 Dec 2013 19:56:58 -0500 Subject: [PATCH 106/149] libfreerdp-codec: add new planar codec test data --- .../codec/test/TestFreeRDPCodecPlanar.c | 1245 +++++++++++++++++ 1 file changed, 1245 insertions(+) diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 85678d63d..1f30869c6 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -1561,6 +1561,1103 @@ static BYTE TEST_RLE_BITMAP_EXPERIMENTAL_03[16384] = "\x56\xCC\x97\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF" "\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x57\xCD\x98\xFF\x56\xCB\x96\xFF\x51\xB7\x88\xFF"; +BYTE TEST_RLE_BITMAP_EXPERIMENTAL_03_RLE[11160] = + "\x30\xF0\x23\x1F\x1E\x1D\x1D\x1E\x1D\x1F\x23\x4A\x78\x71\x64\x58\x4B\xF0\x3E\x30\x29\x26\x24\x22\x21\x20\x20\x20\x1D\x1E\x20\x1E" + "\x1F\x86\x20\x20\x1F\x20\x21\x22\x1E\x1F\xF0\x1E\x1D\x1F\x20\x1F\x1F\x1F\x1E\x1E\x1F\x1F\x20\x1E\x1F\x1F\x50\x1F\x1E\x1D\x1E\x1C" + "\xF0\x38\x1A\x14\x0E\x08\x08\x06\x04\x04\x18\x38\x4A\x66\x74\x82\xF0\x90\x9E\x90\x76\x52\x2C\x16\x12\x0C\x06\x08\x06\x00\x02\x00" + "\xF0\x03\x03\x02\x00\x01\x03\x02\x02\x00\x02\x01\x01\x03\x00\x00\x33\x02\x04\x00\xD0\x02\x00\x00\x03\x01\x02\x00\x02\x01\x00\x04" + "\x02\x06\xF0\x94\xAE\x90\x6E\x46\x2E\x1E\x0C\x0E\x0A\x02\x00\x02\x0C\x18\xF0\x24\x30\x4C\x6C\x94\xBE\xBE\x9A\x6C\x48\x34\x1C\x10" + "\x08\x06\xF0\x06\x06\x00\x01\x03\x01\x02\x00\x02\x00\x02\x02\x08\x04\x02\xF0\x00\x03\x01\x00\x01\x02\x00\x06\x02\x04\x01\x01\x03" + "\x03\x00\x40\x00\x00\x04\x01\xD3\x02\x14\x3C\x68\x8E\x86\x78\x68\x3E\x16\x02\x02\x00\x14\x02\xF0\x1A\x44\x78\x92\x8C\x82\x6E\x52" + "\x2E\x14\x06\x02\x04\x00\x01\xF0\x01\x03\x00\x01\x01\x02\x00\x00\x03\x01\x01\x01\x04\x06\x00\xD0\x02\x00\x02\x00\x02\x02\x02\x00" + "\x00\x04\x00\x03\x02\x04\xAA\x0C\x2C\x54\x6E\x7C\x4C\x02\x00\x02\x00\xF0\x10\x2E\x50\x6C\x82\x92\x86\x50\x1C\x12\x0A\x04\x04\x02" + "\x01\xF0\x02\x02\x01\x05\x07\x00\x00\x01\x02\x03\x05\x03\x00\x01\x05\xA0\x01\x01\x01\x00\x02\x04\x00\x02\x00\x01\xE5\x00\x00\x02" + "\x02\x00\x00\x00\x04\x1A\x14\x00\x00\x01\x00\x35\x01\x01\x00\xF0\x06\x16\x2A\x52\x96\xBC\x84\x50\x28\x0C\x06\x04\x03\x03\x00\xF0" + "\x04\x04\x04\x02\x02\x02\x00\x02\x00\x00\x01\x02\x00\x02\x00\x70\x02\x04\x08\x01\x01\x02\x04\x04\x5C\x02\x02\x00\x02\x00\x14\x01" + "\xF0\x00\x00\x00\x02\x02\x02\x16\x58\x86\x84\x54\x1E\x06\x06\x04\xF0\x00\x03\x01\x00\x02\x02\x01\x02\x01\x04\x02\x01\x00\x02\x01" + "\x80\x02\x02\x01\x07\x03\x01\x01\x00\x04\x53\x01\x00\x00\x00\x01\x16\x00\xF0\x02\x00\x00\x01\x00\x02\x00\x01\x13\x37\x55\x67\x4B" + "\x1D\x03\xF0\x14\x44\x7A\x74\x28\x08\x01\x01\x02\x04\x00\x02\x00\x00\x01\xF0\x02\x01\x05\x00\x01\x04\x02\x00\x00\x00\x01\x00\x00" + "\x03\x03\x41\xF0\x04\x02\x02\x02\x03\x1F\x69\xA9\x9B\x7D\x69\x85\xAD\xA1\x47\xF0\x0B\x14\x5A\x8A\x32\x08\x04\x04\x04\x01\x03\x04" + "\x01\x00\x00\xE0\x02\x00\x04\x00\x07\x01\x00\x03\x03\x00\x02\x00\x00\x00\x04\x53\x02\x00\x00\x00\x02\x17\x00\xF0\x01\x00\x00\x00" + "\x31\x7F\x61\x11\x36\x4E\x58\x46\x16\x29\x79\xF0\x69\x0B\x00\x34\x68\x14\x06\x03\x07\x04\x02\x00\x02\x00\x00\xA3\x00\x06\x00\x00" + "\x00\x01\x01\x00\x06\x02\x10\x01\x71\xF0\x0D\x6F\x2F\x1A\x7A\x84\x78\x70\x7A\x88\x52\x09\x57\x53\x01\xF0\x00\x44\x42\x06\x06\x00" + "\x04\x04\x00\x01\x00\x02\x00\x00\x03\xB0\x04\x02\x04\x04\x00\x05\x00\x00\x00\x02\x02\x41\xF0\x01\x01\x07\x75\x2B\x2A\x86\x54\x1A" + "\x0C\x02\x04\x2A\x7A\x6E\xF0\x04\x5B\x3B\x00\x06\x68\x22\x04\x00\x03\x01\x01\x04\x01\x00\x83\x01\x03\x02\x01\x00\x01\x02\x00\x30" + "\x02\x00\x02\x51\xF0\x02\x21\x2F\x0C\x50\x2A\x02\x03\x29\x61\x73\x59\x1B\x4C\x32\xF0\x15\x3F\x09\x00\x20\x42\x08\x02\x00\x01\x00" + "\x00\x02\x01\x00\xD0\x02\x00\x00\x02\x01\x05\x02\x00\x00\x00\x01\x01\x01\x61\xF0\x1F\x0F\x14\x34\x04\x00\x0D\x55\x61\x55\x67\x4B" + "\x0A\x3C\x00\xF0\x15\x0D\x00\x0C\x3A\x18\x06\x02\x00\x01\x00\x02\x04\x02\x03\xC0\x04\x06\x00\x00\x02\x00\x00\x01\x00\x00\x02\x00" + "\x11\xF0\x01\x01\x01\x00\x00\x21\x0F\x0A\x1A\x00\x01\x0A\x32\x28\x24\xF0\x30\x2E\x06\x2C\x04\x13\x11\x02\x02\x32\x38\x08\x03\x01" + "\x01\xF0\x00\x02\x01\x03\x02\x00\x03\x00\x00\x01\x00\x01\x00\x03\x03\x20\x00\x02\x0E\xF0\x01\x00\x00\x02\x02\x02\x00\x00\x10\x06" + "\x05\x0B\x00\x00\x06\xF0\x4A\x88\x84\x80\x3C\x00\x0B\x03\x02\x02\x00\x00\x12\x30\x10\xF0\x02\x00\x02\x01\x05\x03\x02\x02\x03\x01" + "\x00\x06\x02\x01\x00\x50\x00\x04\x02\x00\x02\x0D\x24\x01\x00\xF0\x02\x00\x00\x1C\x0E\x0F\x1D\x00\x02\x02\x06\x1A\x2E\x16\x02\xF0" + "\x02\x31\x00\x16\x16\x00\x00\x02\x30\x18\x02\x02\x00\x04\x02\xF0\x04\x00\x00\x01\x00\x00\x00\x02\x02\x06\x04\x01\x01\x00\x05\x0C" + "\xF0\x03\x0F\x05\x01\x00\x00\x01\x03\x00\x01\x24\x1E\x15\x57\x15\xF0\x01\x00\x00\x00\x02\x00\x01\x37\x4B\x04\x2A\x12\x00\x00\x00" + "\xF0\x2C\x26\x0A\x00\x02\x07\x03\x05\x02\x00\x02\x00\x00\x03\x01\x70\x01\x00\x00\x02\x04\x01\x06\x33\x00\x01\x00\xF0\x01\x00\x00" + "\x00\x01\x00\x03\x31\x57\x1F\x00\x00\x02\x02\x00\x83\x00\x1C\x56\x02\x3B\x5F\x17\x00\xF0\x05\x33\x65\x19\x2C\x42\x02\x00\x02\x00" + "\x04\x38\x02\x02\x01\xF0\x02\x01\x02\x00\x00\x01\x01\x00\x01\x00\x00\x01\x05\x02\x02\x20\x02\x01\x39\x00\x02\x00\xF0\x06\x12\x3D" + "\x83\x4B\x07\x00\x00\x02\x02\x02\x44\x44\x0B\x57\xF0\x83\x51\x29\x1D\x31\x67\x7D\x35\x08\x6A\x22\x00\x00\x01\x00\xF0\x00\x3A\x0C" + "\x06\x00\x00\x06\x04\x02\x00\x02\x02\x01\x00\x01\x70\x00\x00\x01\x03\x01\x00\x00\x0C\xF0\x02\x2A\x28\x2D\x8B\x9B\x43\x15\x01\x00" + "\x00\x08\x80\x72\x16\xF0\x39\x83\xA9\xB3\xA1\x67\x17\x3A\x8E\x38\x02\x00\x00\x00\x01\xF0\x01\x22\x24\x00\x00\x02\x03\x01\x03\x03" + "\x00\x00\x02\x04\x01\x70\x02\x04\x06\x02\x00\x00\x00\x58\x02\x00\x00\x02\x00\xF0\x08\x3A\x16\x07\x3B\x87\x7D\x45\x11\x00\x00\x0E" + "\x5A\x7C\x64\x94\x3E\x18\x16\x28\x4A\x70\x80\x3C\x00\x83\x02\x01\x2D\x0B\x00\x02\x00\x02\xC0\x04\x02\x00\x00\x06\x00\x03\x01\x01" + "\x02\x00\x02\x24\x02\x00\xF0\x02\x00\x00\x00\x02\x00\x00\x00\x32\x52\x10\x02\x17\x4D\x7F\xF0\x85\x4B\x09\x00\x08\x3A\x6C\x80\x94" + "\x94\x8C\x78\x56\x1A\x02\x13\x00\xF0\x01\x17\x67\x85\x27\x03\x00\x00\x00\x01\x00\x02\x03\x00\x02\x90\x02\x01\x02\x01\x01\x04\x01" + "\x00\x01\x68\x00\x02\x02\x00\x02\x00\xF0\x08\x6A\xB6\x76\x40\x18\x17\x49\x8D\xB1\x6D\x33\x15\x02\x18\xF0\x28\x2A\x20\x12\x04\x01" + "\x00\x00\x01\x09\x1D\x69\xA7\x71\x35\xF0\x07\x03\x01\x01\x04\x02\x00\x02\x00\x01\x00\x03\x01\x00\x02\x50\x04\x01\x00\x00\x00\x77" + "\x02\x02\x00\x02\x00\x02\x00\xF0\x02\x02\x1A\x64\x88\x78\x46\x16\x03\x25\x6F\x93\x83\x67\x3F\xF0\x29\x13\x01\x02\x02\x00\x05\x23" + "\x45\x67\x89\x6D\x1B\x06\x1C\x53\x00\x01\x02\x00\x01\xC0\x00\x03\x01\x00\x00\x01\x02\x02\x01\x02\x02\x01\x07\x28\x02\x00\xF0\x04" + "\x1C\x50\x82\x8E\x5C\x20\x06\x15\x43\x6F\x85\x8D\x91\x8D\xF0\x81\x8B\x93\x95\x8D\x7F\x69\x33\x08\x3E\x86\x80\x08\x02\x01\xF0\x02" + "\x02\x00\x01\x01\x02\x04\x02\x00\x00\x01\x00\x00\x01\x01\x20\x01\x02\x4D\x02\x02\x02\x00\x03\xF0\x18\x3C\x82\xBA\x96\x5E\x34\x12" + "\x19\x27\x3B\x4F\x5F\x4F\x45\xF0\x3F\x25\x02\x30\x5C\xAC\x9C\x50\x09\x06\x02\x02\x02\x03\x00\xE0\x02\x03\x01\x00\x01\x02\x02\x03" + "\x05\x03\x02\x00\x00\x02\x7C\x02\x00\x02\x02\x00\x02\x00\xF0\x02\x00\x02\x00\x08\x42\x7C\x8E\x84\x74\x62\x4C\x34\x22\x2A\xF0\x3C" + "\x4E\x6C\x7E\x88\x7A\x26\x02\x09\x39\x03\x02\x00\x01\x00\xF0\x00\x02\x00\x02\x02\x00\x00\x03\x02\x02\x00\x04\x00\x02\x00\x04\x23" + "\x02\x00\x2E\x02\x00\xF0\x04\x1E\x48\x68\x72\x80\x8C\x98\x90\x86\x7C\x66\x48\x24\x06\xF0\x00\x00\x39\x31\x05\x05\x00\x01\x01\x00" + "\x00\x06\x01\x05\x01\x63\x02\x00\x04\x00\x00\x01\x33\x02\x02\x00\x2F\x02\x00\x05\xF0\x04\x0C\x14\x1E\x28\x22\x1A\x12\x06\x00\x01" + "\x00\x19\x5D\x77\xF0\x1B\x05\x00\x03\x01\x04\x02\x01\x01\x00\x04\x02\x03\x00\x00\x60\x04\x01\x02\x02\x04\x02\x23\x00\x02\x1F\x00" + "\x0F\xF0\x01\x01\x13\x49\x7B\x75\x29\x0B\x03\x00\x01\x00\x00\x03\x01\xD0\x01\x01\x02\x02\x04\x04\x01\x05\x02\x00\x02\x05\x01\x29" + "\x02\x00\x26\x02\x00\x23\x01\x00\x33\x01\x01\x00\xF0\x02\x02\x02\x00\x02\x00\x13\x5D\x8B\x81\x4D\x15\x03\x01\x03\xF0\x01\x02\x06" + "\x00\x02\x00\x04\x01\x03\x03\x03\x05\x00\x00\x01\x40\x01\x00\x00\x01\xF4\x02\x02\x01\x00\x00\x00\x01\x01\x02\x00\x00\x00\x02\x01" + "\x00\xF0\x02\x00\x00\x00\x02\x00\x00\x01\x00\x01\x01\x01\x09\x1B\x29\xF0\x57\x93\xB9\x7D\x45\x1F\x0B\x07\x03\x00\x06\x04\x00\x00" + "\x06\xF0\x02\x04\x00\x01\x00\x02\x00\x06\x02\x06\x04\x01\x00\x06\x00\xF0\x00\x00\x02\x00\x01\x11\x29\x2F\x3F\x3F\x33\x29\x1D\x11" + "\x0B\x26\x03\x00\xF0\x03\x02\x00\x0B\x19\x2B\x3F\x5D\x6F\x81\x95\x83\x4D\x1B\x11\xF0\x07\x03\x01\x03\x03\x01\x00\x00\x02\x00\x05" + "\x03\x00\x00\x06\xB0\x00\x00\x04\x01\x01\x05\x03\x02\x01\x03\x00\xF0\x00\x02\x02\x00\x55\x85\x87\x89\x87\x87\x8B\x8D\x8F\x95\x99" + "\xF0\x93\x85\x77\x65\x55\x59\x5F\x65\x6B\x7D\x91\x93\x91\x89\x85\x73\x7B\x67\x4D\x29\x11\x0D\x00\xF0\x04\x04\x04\x02\x00\x00\x01" + "\x02\x02\x00\x01\x00\x02\x02\x02\x90\x01\x02\x00\x01\x03\x02\x00\x00\x02\xF0\x00\x00\x05\x6D\x77\x4D\x3B\x2F\x29\x27\x29\x31\x3D" + "\x3F\x3F\xF0\x4D\x5F\x6D\x81\x8B\x85\x83\x7B\x75\x6B\x53\x47\x3F\x35\x25\xC3\x15\x0B\x05\x07\x07\x01\x05\x01\x03\x01\x01\x02\x93" + "\x00\x02\x00\x01\x02\x02\x00\x02\x00\x70\x04\x06\x06\x05\x00\x02\x00\xF0\x04\x02\x2D\x47\x13\x05\x01\x05\x01\x00\x03\x07\x07\x05" + "\x09\xF0\x0B\x0D\x09\x07\x0F\x0F\x09\x0D\x0B\x05\x09\x05\x05\x01\x07\xF0\x03\x03\x03\x00\x02\x04\x06\x02\x01\x01\x00\x02\x00\x03" + "\x01\x75\x03\x03\x00\x00\x00\x01\x00\x70\x02\x02\x01\x04\x00\x01\x00\xF0\x00\x01\x33\x1D\x05\x03\x00\x04\x00\x00\x00\x04\x00\x03" + "\x01\xF0\x00\x02\x00\x00\x01\x01\x01\x00\x05\x00\x00\x00\x02\x01\x04\xF0\x02\x00\x02\x00\x00\x05\x05\x00\x04\x02\x03\x01\x05\x01" + "\x00\xF0\x04\x04\x01\x00\x00\x02\x02\x05\x00\x00\x02\x00\x03\x02\x02\x40\x00\x00\x00\x03\xF0\x00\x0B\x1B\x07\x01\x00\x02\x01\x02" + "\x00\x01\x01\x00\x02\x00\xF0\x02\x06\x02\x00\x04\x00\x00\x00\x04\x01\x00\x00\x02\x04\x02\xF0\x04\x02\x00\x04\x01\x01\x02\x01\x02" + "\x01\x01\x01\x00\x02\x00\xF0\x01\x00\x03\x00\x00\x01\x03\x00\x03\x03\x00\x02\x00\x03\x01\x40\x00\x02\x00\x04\xF0\x00\x03\x05\x01" + "\x03\x04\x04\x00\x01\x01\x01\x03\x00\x00\x02\xF0\x00\x01\x01\x00\x01\x00\x03\x03\x02\x01\x01\x00\x01\x01\x01\xF0\x00\x00\x02\x03" + "\x01\x02\x00\x02\x01\x02\x06\x02\x04\x02\x00\xF3\x00\x01\x00\x00\x02\x02\x00\x02\x02\x00\x03\x03\x01\x01\x00\x10\x01\xF0\x02\x03" + "\x03\x00\x01\x03\x05\x00\x02\x00\x02\x02\x02\x04\x01\xF0\x05\x03\x00\x02\x00\x05\x00\x02\x01\x00\x04\x01\x00\x02\x02\x83\x00\x04" + "\x00\x02\x06\x04\x03\x00\xB3\x03\x03\x07\x05\x00\x02\x00\x02\x01\x01\x02\x90\x06\x00\x04\x00\x00\x01\x00\x02\x00\xF0\x02\x0A\x06" + "\x00\x08\x04\x04\x02\x04\x04\x02\x00\x02\x02\x04\xF0\x08\x00\x01\x03\x00\x04\x02\x02\x00\x06\x00\x02\x02\x03\x00\xF0\x00\x05\x01" + "\x03\x01\x03\x01\x03\x00\x02\x01\x00\x01\x02\x02\xF0\x00\x02\x02\x01\x00\x01\x03\x02\x00\x00\x03\x02\x01\x00\x00\x40\x00\x03\x01" + "\x04\xF0\x00\x06\x0C\x02\x01\x03\x00\x02\x05\x01\x00\x04\x05\x03\x03\xB3\x03\x02\x00\x01\x01\x02\x01\x01\x00\x01\x00\xB3\x02\x01" + "\x00\x00\x01\x00\x02\x02\x00\x00\x02\xF0\x04\x06\x02\x03\x00\x01\x01\x00\x04\x05\x01\x00\x01\x00\x00\x60\x02\x02\x00\x04\x00\x03" + "\xF0\x00\x08\x20\x0E\x03\x00\x00\x02\x04\x02\x00\x02\x02\x03\x00\x73\x07\x07\x02\x02\x06\x01\x00\xF0\x03\x01\x01\x00\x00\x02\x02" + "\x04\x06\x04\x00\x04\x04\x02\x05\xF0\x00\x00\x02\x01\x03\x00\x04\x01\x00\x02\x00\x05\x00\x02\x02\x63\x00\x00\x02\x01\x01\x00\xF0" + "\x00\x02\x28\x12\x06\x08\x04\x01\x00\x03\x01\x03\x01\x02\x02\xF0\x08\x02\x05\x01\x01\x02\x02\x00\x01\x01\x04\x02\x01\x00\x03\xF0" + "\x01\x03\x03\x02\x05\x03\x01\x01\x00\x04\x03\x01\x00\x02\x00\xF0\x01\x01\x00\x02\x00\x02\x04\x06\x02\x00\x01\x04\x00\x02\x04\x13" + "\x00\xF0\x02\x00\x24\x12\x04\x00\x01\x00\x00\x06\x00\x03\x02\x00\x00\xF0\x00\x06\x06\x04\x04\x00\x02\x04\x02\x01\x01\x02\x00\x00" + "\x02\xF0\x00\x00\x03\x07\x02\x04\x01\x00\x00\x01\x00\x02\x02\x06\x06\xF0\x00\x05\x01\x00\x02\x02\x00\x03\x01\x03\x00\x05\x03\x01" + "\x00\x40\x00\x03\x00\x02\xF0\x00\x00\x12\x24\x08\x02\x00\x04\x02\x03\x00\x04\x01\x00\x03\xF0\x00\x01\x01\x01\x07\x01\x01\x01\x00" + "\x02\x01\x01\x02\x01\x01\xF0\x02\x02\x04\x06\x02\x00\x02\x02\x00\x00\x06\x02\x01\x01\x00\x63\x04\x04\x02\x04\x02\x01\xA0\x00\x04" + "\x01\x01\x00\x01\x00\x01\x00\x02\xF0\x00\x02\x06\x50\x0A\x02\x01\x07\x01\x02\x04\x00\x04\x04\x00\xF0\x03\x00\x01\x00\x06\x04\x00" + "\x01\x00\x02\x04\x00\x03\x06\x02\xF0\x01\x00\x03\x03\x01\x00\x00\x01\x01\x01\x03\x00\x02\x01\x01\x63\x03\x02\x00\x03\x00\x02\xA0" + "\x00\x04\x06\x02\x02\x01\x04\x04\x00\x05\xF0\x02\x00\x00\x34\x2A\x04\x02\x00\x02\x02\x03\x00\x01\x03\x06\xF0\x06\x02\x02\x00\x03" + "\x03\x02\x06\x02\x01\x00\x00\x01\x00\x00\xF0\x00\x01\x02\x04\x01\x01\x02\x00\x00\x02\x01\x02\x00\x01\x03\xF0\x01\x00\x06\x00\x03" + "\x01\x02\x02\x00\x02\x01\x00\x04\x00\x01\x40\x01\x00\x01\x01\x03\xF0\x08\x54\x14\x10\x10\x10\x0A\x0C\x0C\x08\x0A\x08\x06\x02\x06" + "\xF0\x02\x06\x02\x06\x01\x05\x01\x00\x00\x02\x05\x03\x00\x02\x02\xF0\x00\x06\x04\x00\x00\x00\x01\x00\x03\x00\x03\x00\x02\x00\x03" + "\xF0\x02\x02\x00\x00\x00\x01\x02\x05\x01\x00\x00\x02\x00\x00\x00\x10\x04\x04\xF0\x52\x92\x8E\x88\x78\x72\x6C\x62\x58\x52\x4C\x4C" + "\x48\x38\x30\xF0\x22\x16\x0E\x10\x12\x12\x06\x06\x06\x04\x02\x01\x00\x03\x01\xF0\x03\x05\x05\x01\x01\x00\x06\x02\x00\x02\x06\x00" + "\x00\x00\x03\xF0\x00\x00\x01\x01\x02\x00\x02\x02\x03\x04\x04\x02\x00\x02\x04\x03\xF0\x02\x0A\x40\x52\x5C\x68\x72\x7A\x82\x92\x94" + "\x94\x94\x90\x8E\xF0\x8A\x82\x80\x70\x60\x56\x3E\x30\x1A\x14\x14\x10\x0C\x0A\x0A\xF0\x02\x00\x04\x02\x00\x02\x02\x01\x00\x00\x04" + "\x05\x02\x00\x02\xF0\x02\x01\x00\x00\x02\x00\x01\x02\x00\x02\x01\x00\x01\x02\x02\x10\x00\x39\x00\x01\x00\xF0\x01\x02\x06\x0A\x16" + "\x26\x34\x48\x5A\x68\x72\x7C\x86\x94\xA0\xF0\x9A\x80\x64\x3C\x18\x14\x10\x08\x04\x08\x02\x02\x01\x00\x02\xF0\x01\x01\x02\x02\x02" + "\x01\x00\x00\x02\x00\x01\x01\x03\x02\x01\x70\x04\x00\x03\x00\x01\x01\x01\x04\x5A\x01\x00\x00\x01\x00\xF0\x01\x02\x04\x0C\x10\x1E" + "\x26\x30\x3E\x5A\x7C\xAA\xC2\xA2\x82\xF0\x62\x4A\x2C\x20\x0E\x10\x06\x02\x01\x03\x00\x03\x03\x02\x02\xF0\x02\x03\x01\x01\x01\x02" + "\x01\x01\x01\x02\x04\x04\x01\x05\x00\x33\x00\x02\x00\x24\x01\x00\x29\x02\x00\x53\x02\x00\x00\x00\x02\xF0\x0E\x34\x5E\x84\x90\x8C" + "\x82\x76\x5A\x3C\x1E\x12\x0C\x06\x06\xF0\x02\x00\x00\x00\x02\x00\x00\x02\x02\x00\x06\x03\x03\x01\x05\x30\x02\x08\x00\x03\x3A\x01" + "\x02\x00\x53\x01\x00\x02\x02\x00\x28\x01\x00\xF0\x04\x12\x32\x4E\x66\x7A\x8E\x96\x7A\x44\x16\x0E\x0C\x04\x02\x33\x00\x00\x02\x90" + "\x04\x02\x04\x02\x02\x04\x02\x01\x02\x55\x00\x01\x01\x02\x00\xA8\x01\x01\x01\x00\x00\x01\x00\x01\x01\x00\xF0\x01\x01\x00\x00\x02" + "\x00\x00\x02\x02\x00\x04\x10\x20\x36\x66\xF0\xA4\xB8\x88\x54\x36\x1E\x10\x0A\x04\x01\x00\x01\x05\x09\x05\x60\x01\x01\x05\x03\x02" + "\x01\xF0\x03\x03\x05\x03\x00\x00\x02\x02\x00\x01\x02\x02\x02\x00\x00\x54\x02\x00\x00\x02\x00\x94\x02\x00\x00\x00\x02\x02\x00\x01" + "\x00\xF0\x02\x02\x00\x01\x02\x02\x02\x1E\x58\x8A\x92\x7C\x62\x3C\x1A\xC0\x12\x0A\x06\x02\x02\x06\x02\x01\x00\x00\x01\x01\xF0\x93" + "\x93\x93\x95\x91\x85\x79\x6F\x5D\x4F\x45\x3B\x31\x23\x0F\x8D\x01\x02\x02\x00\x00\x01\x01\x00\x13\x01\xF0\x00\x01\x01\x00\x00\x00" + "\x08\x24\x52\x72\x8C\x98\x68\x30\x0E\x90\x0C\x08\x00\x00\x02\x01\x04\x04\x02\xF0\x4F\x4D\x4D\x4B\x55\x63\x6F\x75\x87\x8D\x95\x9F" + "\xA5\xB1\xC3\xF0\xC3\xA1\x85\x6B\x53\x37\x29\x21\x17\x0B\x01\x00\x00\x00\x01\x49\x01\x02\x01\x00\x14\x01\xF0\x00\x0C\x20\x3A\x78" + "\xAC\x9E\x5E\x2E\x16\x04\x01\x06\x00\x03\x10\x01\xF0\x07\x09\x09\x0D\x07\x07\x07\x0B\x0B\x11\x11\x0D\x17\x17\x19\xF0\x21\x45\x5F" + "\x75\x87\x9B\x99\x8D\x83\x73\x61\x43\x29\x0D\x00\xC6\x00\x00\x02\x00\x00\x00\x02\x02\x00\x00\x02\x00\xF0\x02\x01\x00\x02\x02\x0C" + "\x40\x80\x86\x6C\x44\x22\x12\x04\x02\x10\x04\xF0\x01\x03\x02\x04\x01\x01\x01\x00\x01\x01\x00\x05\x00\x01\x05\xF0\x09\x09\x0B\x11" + "\x11\x15\x25\x39\x4B\x61\x77\x81\x89\x8D\x81\xE4\x53\x2B\x07\x00\x01\x01\x00\x00\x02\x02\x00\x00\x00\x02\x34\x00\x02\x00\x90\x08" + "\x36\x66\x8A\x8A\x40\x16\x0C\x04\xF0\x00\x02\x01\x01\x01\x02\x04\x02\x02\x02\x03\x01\x01\x01\x04\x33\x01\x00\x03\xF0\x05\x07\x09" + "\x09\x11\x25\x2F\x4D\x61\x8B\xAB\xC5\xA3\x71\x3F\x44\x1F\x0B\x01\x00\x66\x01\x01\x00\x00\x01\x00\x80\x02\x06\x1E\x46\x9C\xB0\x70" + "\x40\xF0\x01\x00\x02\x02\x04\x02\x01\x05\x01\x00\x03\x00\x00\x00\x01\xF0\x02\x02\x08\x0A\x02\x01\x02\x02\x01\x05\x03\x03\x0B\x05" + "\x09\xF0\x0D\x15\x19\x41\x69\x93\x89\x71\x51\x2D\x09\x02\x02\x00\x02\x2A\x02\x00\x70\x02\x00\x02\x04\x26\x70\x8C\x34\x2A\x25\x23" + "\xF0\x24\x2B\x60\xA2\x97\x84\x75\x62\x50\x3C\x34\x30\x2C\x29\x28\x93\x26\x25\x25\x23\x23\x25\x24\x24\x25\x84\x26\x27\x25\x24\x25" + "\x25\x27\x24\xC3\x25\x25\x24\x25\x26\x25\x23\x24\x24\x25\x23\x24\x30\x22\x23\x21\xF0\x4E\x24\x1C\x12\x0C\x0A\x08\x06\x08\x26\x4E" + "\x6A\x8E\xA0\xB6\xF0\xCA\xDC\xCA\xA0\x74\x3E\x20\x1A\x12\x08\x08\x08\x02\x02\x02\xF0\x03\x03\x02\x02\x01\x00\x00\x02\x00\x02\x07" + "\x00\x01\x02\x00\xF0\x01\x02\x00\x02\x00\x01\x01\x00\x02\x01\x01\x02\x00\x02\x00\x40\x04\x04\x02\x06\xF0\xD2\xF8\xCC\x9A\x5E\x40" + "\x26\x14\x10\x0C\x02\x00\x04\x10\x20\xF0\x30\x44\x66\x98\xCC\xF7\xF7\xD4\x98\x66\x4A\x2C\x14\x0C\x08\xF0\x0A\x08\x01\x03\x01\x07" + "\x00\x02\x00\x01\x00\x00\x08\x04\x00\xF0\x02\x03\x01\x00\x00\x00\x02\x06\x04\x04\x01\x01\x01\x00\x00\x40\x07\x00\x04\x04\xC4\x02" + "\x16\x52\x92\xC8\xBE\xAC\x8E\x58\x1C\x02\x00\x14\x02\xF0\x22\x60\xA6\xCC\xC2\xB2\x9C\x70\x3E\x1C\x0A\x04\x06\x00\x02\x75\x00\x03" + "\x00\x01\x01\x02\x01\xF0\x04\x02\x00\x02\x00\x01\x01\x02\x02\x02\x03\x03\x06\x00\x03\x10\x03\x04\x8C\x12\x40\x70\x96\xAE\x6C\x02" + "\x00\xF0\x16\x40\x6E\x94\xB4\xD0\xBA\x72\x26\x18\x0C\x08\x04\x00\x03\xF0\x02\x04\x01\x05\x03\x02\x01\x03\x02\x05\x05\x03\x01\x00" + "\x01\xA0\x02\x01\x01\x02\x02\x04\x02\x02\x00\x00\x03\x8D\x02\x01\x00\x00\x0A\x22\x1C\x00\x03\xF0\x0A\x20\x38\x72\xCE\xF7\xBA\x70" + "\x34\x12\x08\x08\x05\x01\x00\xF0\x04\x02\x02\x06\x06\x00\x02\x04\x02\x02\x03\x01\x03\x00\x04\x70\x00\x06\x0A\x03\x01\x02\x04\x04" + "\x5C\x02\x00\x00\x02\x00\x44\x01\x01\x01\x00\xF0\x02\x02\x02\x1E\x78\xBC\xB6\x74\x2E\x0E\x0A\x02\x00\x01\x01\xF0\x00\x01\x00\x00" + "\x00\x01\x02\x02\x00\x02\x04\x02\x00\x00\x01\x50\x07\x01\x01\x00\x00\x04\x23\x01\x00\x47\x01\x00\x01\x00\x23\x01\x00\xF0\x01\x01" + "\x17\x49\x75\x8D\x67\x27\x01\x1A\x60\xAA\xA0\x32\x0E\xF0\x04\x02\x00\x02\x01\x04\x02\x02\x01\x02\x00\x05\x01\x01\x02\x90\x00\x00" + "\x04\x01\x01\x02\x00\x03\x03\x08\x2A\x01\x00\x13\x02\xF0\x05\x2B\x91\xF1\xD9\xB1\x95\xBB\xF1\xE5\x63\x0F\x1C\x7C\xC4\xF0\x48\x0A" + "\x02\x04\x04\x02\x05\x02\x01\x00\x00\x00\x01\x04\x01\xA0\x07\x01\x00\x05\x01\x00\x00\x01\x00\x02\x04\x99\x02\x00\x00\x00\x02\x02" + "\x00\x02\x00\xF0\x01\x00\x43\xB1\x8B\x15\x48\x70\x7C\x62\x20\x35\xA7\x95\x0F\xF0\x00\x46\x8C\x18\x08\x00\x05\x00\x04\x02\x02\x00" + "\x01\x00\x06\xC0\x00\x00\x02\x01\x03\x01\x04\x04\x00\x04\x02\x03\x71\xF0\x13\x9D\x43\x26\xAA\xB8\xA6\x9C\xAA\xBC\x70\x0D\x79\x71" + "\x01\xF0\x02\x60\x5C\x0C\x04\x02\x04\x02\x01\x00\x00\x02\x00\x00\x01\x83\x04\x04\x06\x02\x00\x03\x01\x00\x41\xF0\x01\x00\x09\xA5" + "\x3B\x3C\xBC\x74\x24\x10\x04\x06\x3A\xAA\x94\xF0\x08\x7F\x55\x01\x0A\x96\x2A\x06\x03\x03\x01\x00\x01\x01\x00\xE0\x01\x03\x00\x00" + "\x01\x03\x04\x04\x01\x01\x00\x00\x01\x02\x61\xF0\x2F\x3F\x0E\x6E\x38\x02\x03\x37\x89\x9F\x7F\x25\x6C\x46\x1F\xF0\x55\x09\x00\x26" + "\x5E\x0C\x06\x00\x00\x00\x04\x02\x00\x02\x02\xC0\x00\x02\x04\x00\x05\x02\x02\x04\x02\x00\x00\x01\x41\xF0\x02\x00\x2D\x17\x1E\x48" + "\x06\x00\x15\x79\x8B\x77\x91\x67\x10\xF0\x56\x03\x1F\x17\x00\x10\x50\x22\x06\x04\x02\x03\x01\x04\x04\x93\x02\x03\x04\x04\x01\x01" + "\x02\x01\x00\x20\x02\x00\x21\xF0\x01\x01\x00\x00\x2F\x15\x0E\x26\x00\x01\x12\x46\x3A\x32\x4A\xF0\x40\x0A\x3A\x06\x1B\x17\x00\x04" + "\x46\x4E\x0E\x03\x05\x02\x00\xF0\x00\x03\x01\x04\x00\x03\x02\x04\x00\x00\x01\x03\x05\x01\x00\x10\x02\x11\xF0\x01\x00\x02\x01\x00" + "\x18\x0E\x05\x11\x00\x00\x06\x66\xBE\xB8\xF0\xB2\x52\x00\x0D\x03\x04\x04\x00\x00\x1A\x42\x16\x06\x02\x00\xF0\x00\x05\x01\x00\x02" + "\x01\x00\x03\x02\x02\x01\x02\x00\x04\x00\x20\x00\x02\x11\xF0\x02\x02\x00\x02\x00\x24\x0E\x15\x29\x00\x02\x02\x08\x24\x3E\xF0\x1C" + "\x02\x00\x49\x00\x20\x1C\x00\x00\x02\x44\x20\x04\x04\x00\xF0\x02\x00\x04\x00\x01\x03\x03\x02\x00\x00\x02\x02\x04\x00\x00\x20\x00" + "\x05\x0C\x53\x05\x17\x0B\x01\x00\xF0\x01\x00\x38\x2E\x21\x7B\x1D\x01\x00\x00\x02\x02\x01\x03\x49\xF0\x65\x08\x38\x1A\x00\x00\x00" + "\x3A\x30\x06\x02\x02\x05\x01\x03\xE0\x02\x00\x04\x02\x03\x03\x01\x03\x02\x00\x00\x04\x01\x04\xF0\x01\x01\x00\x01\x01\x01\x00\x01" + "\x01\x00\x01\x00\x03\x45\x77\x24\x2D\x00\x83\x01\x26\x78\x04\x51\x83\x21\x00\xE3\x07\x43\x91\x25\x3C\x5C\x02\x00\x01\x01\x08\x52" + "\x06\x00\xF0\x01\x01\x00\x03\x03\x04\x00\x00\x02\x01\x05\x02\x00\x02\x00\x93\x02\x02\x00\x02\x00\x00\x00\x02\x00\xF0\x08\x18\x59" + "\xB7\x67\x09\x00\x00\x02\x02\x00\x5C\x62\x0F\x79\xF0\xB7\x71\x37\x27\x45\x91\xB1\x49\x0C\x94\x30\x00\x00\x02\x02\xF0\x00\x54\x10" + "\x06\x01\x00\x04\x06\x02\x00\x06\x02\x03\x01\x00\x70\x00\x01\x00\x01\x01\x00\x00\x04\x35\x02\x02\x00\xF0\x02\x3A\x3A\x3B\xC3\xD9" + "\x5D\x1B\x01\x00\x00\x0A\xB0\x9E\x1C\xC4\x4F\xB5\xED\xF9\xDD\x8D\x1D\x4C\xC4\x4C\x02\x00\xF0\x2C\x34\x04\x04\x02\x03\x01\x03\x03" + "\x03\x02\x04\x04\x03\x00\x60\x04\x04\x00\x01\x01\x00\x13\x02\x18\x00\xF0\x0C\x52\x20\x0D\x51\xBB\xAF\x61\x19\x00\x00\x12\x7E\xB0" + "\x8C\x95\x52\x24\x1A\x38\x64\x98\xB6\x54\x00\xF0\x05\x3F\x0F\x03\x03\x02\x02\x00\x02\x04\x04\x02\x01\x02\x06\x70\x02\x03\x00\x01" + "\x04\x04\x02\x0A\xF0\x02\x00\x00\x00\x44\x6E\x18\x04\x21\x6D\xB1\xB9\x69\x0D\x00\xC4\x0A\x4E\x94\xB4\xCC\xCE\xC0\xAA\x78\x24\x02" + "\x00\xF0\x21\x91\xBB\x3B\x05\x04\x00\x00\x01\x02\x00\x01\x00\x02\x02\x80\x00\x00\x01\x01\x04\x01\x01\x00\x68\x02\x02\x02\x00\x02" + "\x00\xF0\x0E\x94\xFC\xA2\x54\x20\x21\x63\xC3\xF5\x95\x49\x1F\x04\x22\xF0\x36\x3A\x2C\x18\x06\x01\x00\x00\x00\x09\x29\x95\xE9\x99" + "\x45\xF0\x09\x03\x01\x00\x02\x00\x00\x02\x00\x01\x00\x05\x03\x00\x04\x50\x06\x01\x00\x00\x02\x78\x02\x00\x02\x00\x00\x02\x00\xF0" + "\x02\x26\x8C\xC2\xA8\x66\x1C\x05\x31\x99\xC9\xB5\x8F\x5B\x39\xF0\x19\x03\x02\x02\x00\x07\x2B\x63\x93\xBB\x97\x25\x06\x24\x04\xF0" + "\x00\x01\x02\x00\x02\x03\x01\x01\x03\x01\x01\x01\x00\x00\x00\x40\x01\x02\x02\x01\x98\x02\x02\x00\x02\x00\x00\x00\x02\x00\xF0\x04" + "\x26\x70\xB2\xC6\x7E\x2A\x02\x1D\x5B\x99\xB7\xC5\xCB\xC7\xF0\xB3\xC3\xCB\xCF\xC7\xB1\x91\x4B\x0A\x56\xBC\xB4\x0A\x00\x01\x64\x01" + "\x02\x00\x00\x01\x02\x70\x01\x00\x01\x01\x00\x00\x00\xAA\x02\x02\x02\x00\x02\x00\x02\x00\x02\x00\xF0\x20\x54\xB6\xFF\xD0\x82\x48" + "\x18\x1F\x33\x4F\x69\x83\x71\x63\xF0\x59\x37\x00\x40\x80\xEE\xDA\x6E\x0D\x08\x00\x00\x00\x03\x01\xE0\x04\x01\x00\x02\x00\x02\x02" + "\x05\x07\x03\x02\x00\x00\x02\x03\x2E\x02\x00\xF0\x02\x00\x02\x00\x0C\x5E\xAA\xC2\xB8\xA0\x86\x68\x48\x32\x3E\xF0\x54\x6E\x98\xB2" + "\xC0\xAA\x36\x02\x0D\x53\x07\x02\x02\x00\x04\x34\x02\x01\x00\x80\x01\x02\x06\x00\x06\x04\x00\x01\x63\x02\x02\x00\x00\x02\x00\x2E" + "\x02\x00\xF0\x06\x2A\x62\x90\x9E\xB0\xC2\xD0\xC8\xBA\xAC\x8E\x64\x30\x08\xF0\x00\x00\x51\x43\x0D\x01\x02\x02\x05\x03\x00\x02\x01" + "\x03\x00\x90\x02\x02\x04\x02\x00\x03\x03\x00\x01\x13\x02\x4F\x00\x02\x02\x00\x05\xF0\x04\x10\x1C\x2A\x36\x30\x24\x18\x06\x00\x01" + "\x00\x21\x81\xA7\xF0\x2D\x03\x01\x03\x07\x06\x06\x01\x00\x02\x02\x00\x01\x03\x02\x60\x02\x00\x02\x01\x02\x04\x9F\x02\x00\x02\x02" + "\x00\x00\x00\x02\x00\x0C\xF0\x01\x01\x1B\x65\xB1\xA5\x39\x0D\x03\x00\x00\x08\x01\x01\x02\xD0\x01\x03\x02\x00\x02\x06\x00\x03\x00" + "\x00\x00\x05\x03\x47\x02\x02\x02\x00\x2B\x02\x00\x33\x01\x01\x00\xF0\x02\x02\x01\x00\x00\x00\x1B\x7F\xC1\xB3\x6D\x1D\x03\x00\x01" + "\xF0\x01\x00\x01\x00\x01\x02\x04\x02\x03\x03\x03\x05\x03\x01\x00\x40\x03\x02\x02\x00\xA6\x00\x02\x00\x00\x02\x00\x01\x01\x02\x00" + "\x26\x02\x00\xF0\x02\x00\x01\x00\x00\x01\x01\x0B\x1F\x3B\x75\xCB\xFA\xB3\x5F\xF0\x2F\x0B\x09\x05\x01\x06\x06\x02\x00\x06\x02\x01" + "\x00\x01\x00\xA0\x02\x00\x04\x04\x04\x02\x00\x00\x04\x00\xF0\x02\x02\x02\x00\x01\x17\x37\x45\x55\x57\x49\x39\x27\x19\x0D\x37\x03" + "\x01\x00\xF0\x01\x0F\x25\x3B\x59\x81\x9D\xB5\xCD\xB9\x6D\x1F\x15\x0D\x05\xF0\x03\x01\x05\x01\x01\x03\x01\x02\x05\x03\x01\x04\x02" + "\x00\x00\x90\x02\x00\x01\x05\x01\x04\x00\x00\x00\xF0\x02\x02\x00\x00\x77\xBD\xBF\xBB\xBD\xB7\xBF\xC3\xC9\xCF\xD3\xF0\xCF\xBB\xA3" + "\x8B\x77\x7D\x85\x8D\x97\xAF\xC7\xCF\xC9\xC1\xBB\xF0\xAD\x8D\x69\x39\x17\x15\x05\x01\x01\x01\x04\x02\x04\x02\x00\xF0\x02\x02\x00" + "\x02\x04\x00\x03\x02\x02\x00\x00\x02\x01\x01\x01\x40\x00\x00\x02\x02\xF0\x02\x00\x09\x99\xAD\x65\x4D\x45\x3B\x39\x3D\x49\x55\x5D" + "\x5F\xF0\x6B\x83\x9B\xB3\xC1\xBD\xB5\xAB\xA3\x91\x73\x61\x57\x4D\x35\xF0\x1B\x15\x0B\x0D\x09\x01\x07\x03\x01\x02\x01\x00\x04\x02" + "\x00\x14\x01\xE0\x04\x00\x02\x01\x04\x00\x01\x04\x06\x04\x05\x03\x03\x00\xF0\x02\x00\x3D\x63\x17\x0B\x07\x07\x00\x03\x03\x07\x09" + "\x07\x0D\xF0\x11\x0F\x0D\x0B\x15\x11\x11\x13\x13\x0D\x0F\x09\x05\x03\x05\xF0\x03\x01\x01\x01\x02\x04\x06\x02\x00\x00\x02\x04\x00" + "\x01\x03\xF0\x03\x01\x02\x00\x00\x03\x00\x00\x02\x00\x02\x01\x00\x02\x00\x40\x06\x04\x02\x01\xF0\x00\x00\x47\x25\x05\x03\x00\x02" + "\x01\x01\x02\x04\x00\x01\x00\xB3\x00\x01\x02\x01\x01\x03\x01\x01\x01\x00\x01\xF0\x04\x02\x01\x00\x04\x00\x03\x07\x01\x02\x01\x03" + "\x03\x07\x03\xF0\x04\x06\x02\x01\x00\x02\x06\x04\x03\x00\x00\x02\x02\x02\x04\x50\x00\x00\x00\x03\x04\xF0\x02\x0D\x27\x0B\x01\x01" + "\x02\x00\x00\x00\x03\x01\x00\x00\x00\xF0\x04\x04\x00\x00\x04\x00\x01\x00\x00\x01\x00\x02\x02\x02\x00\xF0\x00\x02\x01\x02\x03\x01" + "\x04\x00\x00\x02\x01\x01\x00\x02\x02\xF0\x03\x00\x03\x02\x00\x05\x05\x00\x01\x03\x01\x04\x01\x03\x00\x40\x01\x00\x00\x03\xF0\x00" + "\x05\x07\x03\x01\x02\x04\x00\x00\x00\x01\x03\x00\x02\x00\xF0\x00\x02\x03\x01\x01\x00\x01\x03\x00\x02\x02\x00\x01\x00\x00\xF0\x02" + "\x02\x02\x03\x01\x00\x02\x02\x01\x01\x04\x02\x04\x02\x01\xF4\x00\x01\x04\x00\x02\x02\x02\x00\x02\x01\x03\x05\x01\x03\x00\xF0\x02" + "\x05\x03\x01\x03\x00\x03\x02\x00\x00\x02\x02\x06\x02\x01\xF0\x05\x05\x00\x04\x01\x03\x02\x04\x00\x00\x04\x01\x01\x02\x02\xF0\x00" + "\x04\x00\x02\x06\x04\x05\x00\x04\x02\x00\x01\x01\x07\x03\xF0\x00\x04\x00\x02\x03\x01\x04\x00\x04\x04\x06\x02\x02\x02\x01\x40\x02" + "\x00\x02\x01\xF0\x02\x0A\x08\x00\x06\x04\x06\x00\x04\x02\x00\x01\x00\x02\x04\xF0\x06\x00\x00\x03\x02\x02\x02\x00\x04\x02\x03\x00" + "\x04\x03\x00\xF0\x01\x05\x01\x03\x01\x03\x00\x03\x01\x01\x00\x01\x01\x02\x06\xF0\x04\x04\x01\x03\x04\x01\x07\x04\x03\x02\x01\x00" + "\x03\x01\x00\x40\x00\x03\x00\x06\xF0\x00\x0C\x0E\x04\x00\x03\x01\x02\x03\x01\x00\x04\x05\x03\x03\xA5\x01\x04\x01\x01\x03\x04\x00" + "\x00\x03\x00\xF0\x01\x00\x02\x00\x00\x04\x00\x00\x00\x04\x00\x02\x02\x04\x02\xF0\x02\x03\x04\x03\x05\x00\x04\x05\x00\x01\x00\x00" + "\x02\x04\x02\x40\x01\x04\x00\x03\xF0\x00\x0A\x2C\x14\x00\x01\x01\x02\x04\x00\x01\x02\x00\x01\x00\xF0\x09\x05\x02\x00\x06\x01\x01" + "\x01\x00\x01\x03\x01\x01\x02\x04\xF0\x04\x02\x04\x08\x04\x00\x04\x06\x02\x01\x03\x00\x00\x00\x03\xF0\x03\x00\x01\x02\x02\x00\x00" + "\x01\x00\x04\x01\x00\x04\x00\x01\x40\x04\x00\x01\x01\xF0\x02\x02\x3E\x1C\x00\x08\x04\x01\x00\x03\x00\x03\x00\x02\x02\xF0\x08\x00" + "\x05\x01\x00\x00\x01\x02\x01\x01\x04\x02\x01\x03\x07\xF0\x03\x05\x07\x01\x05\x05\x01\x01\x01\x02\x02\x01\x00\x00\x01\xF0\x01\x00" + "\x03\x06\x00\x04\x01\x08\x02\x05\x03\x04\x01\x01\x02\x40\x05\x00\x02\x00\xF0\x00\x00\x30\x16\x0A\x00\x03\x00\x01\x08\x00\x03\x00" + "\x01\x00\x63\x02\x04\x06\x04\x06\x02\xF0\x00\x00\x02\x00\x04\x02\x00\x00\x01\x07\x02\x04\x01\x00\x00\xF0\x03\x02\x02\x04\x06\x06" + "\x02\x05\x00\x03\x02\x00\x02\x03\x00\xA0\x02\x02\x03\x03\x01\x02\x00\x00\x00\x04\xF0\x00\x00\x18\x34\x0A\x04\x02\x04\x02\x01\x00" + "\x04\x02\x00\x05\x93\x01\x00\x01\x00\x0B\x01\x00\x01\x00\xF0\x02\x01\x02\x02\x02\x06\x04\x04\x00\x02\x00\x00\x00\x04\x02\xF0\x01" + "\x01\x00\x04\x06\x04\x04\x02\x01\x02\x00\x01\x01\x04\x01\x70\x02\x02\x03\x02\x03\x01\x00\xF0\x00\x02\x06\x6E\x0C\x02\x00\x03\x02" + "\x00\x04\x04\x00\x04\x02\xF0\x03\x00\x00\x01\x06\x04\x00\x00\x02\x02\x00\x01\x03\x04\x02\xF0\x01\x00\x05\x00\x03\x00\x00\x01\x01" + "\x01\x07\x02\x02\x01\x01\xF0\x03\x00\x00\x03\x02\x02\x01\x00\x00\x00\x02\x04\x00\x00\x00\x40\x02\x02\x00\x03\xF0\x02\x00\x00\x4A" + "\x3C\x0A\x00\x03\x01\x02\x00\x01\x01\x00\x08\xB3\x06\x00\x02\x02\x03\x03\x02\x04\x00\x01\x00\xF0\x03\x00\x01\x02\x02\x01\x01\x02" + "\x00\x00\x04\x00\x00\x01\x00\xA3\x01\x03\x00\x02\x00\x03\x00\x00\x02\x00\x70\x04\x02\x01\x01\x02\x00\x00\x03\xF0\x0A\x76\x1C\x1A" + "\x1A\x14\x12\x0E\x10\x0E\x08\x08\x0A\x08\x08\xF0\x06\x06\x02\x06\x02\x01\x01\x00\x00\x00\x07\x03\x00\x04\x02\xF0\x00\x06\x04\x00" + "\x00\x00\x03\x00\x03\x00\x03\x01\x02\x00\x03\xF0\x02\x00\x00\x01\x00\x01\x02\x07\x01\x01\x00\x02\x00\x01\x01\x10\x02\x04\xF0\x6E" + "\xCC\xC4\xBA\xB0\xA0\x98\x86\x7A\x76\x6A\x68\x62\x50\x40\xF0\x34\x20\x12\x12\x14\x14\x0A\x0A\x08\x08\x04\x00\x04\x04\x01\xF0\x01" + "\x01\x03\x01\x01\x00\x06\x00\x04\x06\x06\x02\x00\x00\x02\xF0\x02\x01\x00\x01\x02\x00\x04\x02\x03\x02\x02\x04\x02\x06\x04\x04\xF0" + "\x10\x56\x72\x80\x8C\x9C\xA8\xB8\xCA\xCE\xD2\xD0\xCA\xC6\xC0\xF0\xB6\xB0\xA2\x8A\x78\x5A\x42\x26\x1C\x18\x14\x10\x08\x06\x04\xF0" + "\x00\x00\x04\x00\x02\x06\x01\x02\x03\x01\x03\x00\x01\x00\x03\xF0\x01\x00\x00\x02\x00\x02\x04\x02\x06\x01\x02\x00\x02\x01\x01\x39" + "\x01\x01\x00\xF0\x01\x02\x08\x0E\x1C\x32\x4A\x62\x7E\x90\x9E\xAC\xBE\xD0\xDE\xF0\xD8\xB8\x8E\x54\x24\x1A\x16\x0A\x08\x08\x06\x06" + "\x00\x00\x02\xF0\x01\x00\x00\x02\x04\x00\x02\x00\x04\x00\x01\x01\x05\x00\x00\x70\x00\x02\x03\x01\x03\x01\x01\x6C\x04\x00\x00\x00" + "\x01\x00\xF0\x01\x01\x02\x06\x10\x1A\x28\x34\x42\x58\x7C\xAC\xEC\xED\xE6\xF0\xB6\x86\x66\x40\x2E\x16\x10\x0C\x04\x02\x03\x00\x01" + "\x05\x04\xF0\x02\x00\x03\x01\x00\x00\x02\x03\x00\x00\x00\x02\x02\x00\x05\x10\x00\x34\x01\x02\x00\x23\x01\x00\x2E\x02\x00\xF0\x02" + "\x02\x02\x0E\x46\x82\xBC\xCA\xC4\xB4\xA0\x80\x52\x28\x16\xF0\x12\x0A\x06\x04\x02\x01\x00\x00\x00\x01\x02\x00\x00\x00\x03\x60\x01" + "\x01\x05\x00\x08\x00\x03\x67\x01\x02\x00\x00\x02\x00\x53\x01\x00\x02\x02\x00\x28\x01\x00\xF0\x04\x18\x44\x6C\x90\xA8\xC4\xD6\xAC" + "\x5C\x1E\x14\x0E\x02\x02\xF0\x00\x00\x02\x02\x02\x04\x04\x06\x02\x00\x04\x08\x02\x00\x04\x03\x25\x02\x00\x25\x01\x00\x34\x01\x01" + "\x00\x26\x02\x00\xF0\x02\x00\x00\x02\x00\x00\x06\x16\x2E\x4A\x8E\xE4\xF9\xC0\x78\xF0\x4E\x28\x16\x0C\x08\x04\x00\x03\x05\x09\x01" + "\x00\x00\x07\x03\x20\x02\x01\x55\x03\x05\x07\x03\x00\x2F\x02\x00\x0D\xF0\x01\x02\x00\x02\x24\x78\xC0\xCA\xB2\x8C\x5A\x26\x14\x10" + "\x08\x90\x04\x02\x02\x00\x01\x01\x00\x03\x00\xF0\xCD\xCF\xCF\xD1\xC9\xB7\xA3\x97\x81\x71\x61\x53\x43\x2F\x13\x5D\x01\x02\x02\x02" + "\x00\x03\x13\x01\xF0\x00\x01\x01\x00\x00\x00\x0A\x32\x72\x9E\xC2\xD4\x92\x3E\x18\x90\x12\x02\x00\x00\x04\x01\x06\x04\x00\xF0\x6F" + "\x6D\x71\x6B\x77\x8B\x9F\xA3\xBB\xC5\xD1\xDD\xE7\xF9\xF0\xC5\xEE\xE3\xB9\x93\x6F\x4B\x3B\x2D\x21\x0F\x03\x00\x29\x01\x00\x14\x01" + "\xF0\x00\x10\x2A\x50\xA6\xF4\xDA\x80\x48\x20\x08\x05\x0C\x00\x03\x10\x01\xF0\x0B\x0B\x09\x11\x0B\x0B\x09\x13\x11\x19\x15\x13\x21" + "\x1F\x23\xF0\x2D\x5F\x83\xA5\xC3\xDF\xD5\xC5\xB5\xA1\x85\x5F\x37\x11\x00\xC6\x00\x00\x02\x00\x00\x00\x02\x02\x00\x00\x02\x00\xF0" + "\x02\x01\x00\x00\x02\x0E\x58\xB6\xBA\x98\x5E\x2A\x12\x08\x04\x10\x02\xF0\x01\x01\x06\x06\x03\x01\x05\x01\x03\x00\x03\x03\x01\x03" + "\x07\xF0\x0B\x07\x11\x13\x17\x1D\x37\x51\x69\x85\xA5\xB5\xC1\xC5\xB1\xE4\x75\x3B\x07\x00\x01\x01\x00\x00\x02\x02\x00\x00\x00\x02" + "\xF0\x00\x02\x00\x00\x02\x00\x00\x08\x4A\x8E\xC0\xC4\x5C\x1E\x12\x10\x0A\xF0\x01\x01\x05\x03\x01\x00\x04\x04\x04\x02\x01\x05\x01" + "\x03\x00\xF0\x03\x05\x01\x07\x03\x05\x07\x0B\x0F\x13\x1B\x33\x47\x6B\x8F\xA4\xC1\xF1\xEC\xE5\x9D\x57\x29\x0F\x01\x00\xF0\x01\x01" + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x0A\x2A\x50\x62\xD8\xF2\x9C\x56\xF0\x03\x00\x04\x02\x04\x02\x00\x03\x01\x01\x03\x01" + "\x02\x00\x01\xF0\x00\x02\x04\x08\x00\x01\x02\x02\x02\x05\x05\x05\x0B\x0B\x0B\xF0\x13\x19\x29\x5B\x97\xCF\xC1\xA1\x75\x3F\x0B\x02" + "\x02\x00\x02\x2C\x02\x00\x50\x02\x06\x36\x9C\xC4\x53\x27\x23\x23\x24\x25\xF0\x28\x37\x4A\x47\x41\x3D\x38\x33\x2E\x2A\x28\x27\x26" + "\x27\x27\xF0\x27\x25\x24\x26\x28\x26\x27\x28\x28\x26\x28\x28\x2A\x26\x27\xF0\x27\x28\x27\x27\x25\x26\x24\x25\x27\x28\x27\x28\x27" + "\x27\x25\xB0\x26\x27\x27\x26\x27\x27\x27\x26\x25\x26\x23\xF0\x16\x0C\x0C\x06\x02\x06\x04\x04\x02\x0A\x16\x1E\x2A\x2E\x34\xF0\x3A" + "\x3E\x3C\x30\x20\x12\x0A\x08\x06\x04\x06\x02\x01\x00\x01\xF0\x05\x03\x02\x00\x00\x03\x02\x00\x00\x00\x03\x01\x00\x02\x04\xF0\x02" + "\x04\x00\x01\x01\x03\x00\x00\x00\x03\x00\x01\x00\x02\x00\x40\x00\x04\x02\x08\xF0\x38\x48\x38\x2C\x1E\x10\x0C\x04\x04\x04\x02\x00" + "\x02\x06\x0A\xF0\x0E\x12\x1C\x2C\x3E\x4E\x4C\x3C\x28\x1E\x14\x0C\x04\x02\x02\xF0\x04\x04\x00\x01\x07\x05\x02\x04\x00\x00\x02\x02" + "\x08\x06\x02\xF0\x01\x09\x03\x04\x00\x04\x03\x08\x02\x02\x03\x02\x07\x03\x00\x40\x01\x00\x04\x01\xC4\x02\x06\x16\x28\x3A\x34\x2E" + "\x2A\x16\x06\x01\x00\x13\x02\xF0\x00\x0A\x1C\x32\x3A\x38\x34\x2E\x24\x14\x0C\x01\x03\x00\x04\xF0\x00\x00\x05\x03\x03\x01\x02\x00" + "\x01\x03\x00\x04\x00\x04\x04\xE0\x02\x02\x00\x04\x02\x04\x00\x04\x01\x05\x06\x00\x03\x01\x04\x96\x02\x14\x22\x2A\x34\x20\x02\x02" + "\x00\xF0\x01\x02\x00\x00\x00\x0A\x14\x20\x2A\x32\x3A\x34\x22\x10\x0A\xF0\x00\x04\x00\x02\x02\x04\x04\x03\x09\x05\x01\x01\x03\x02" + "\x05\xF0\x07\x03\x04\x03\x03\x03\x01\x00\x04\x02\x06\x01\x02\x00\x01\xDB\x00\x00\x02\x02\x01\x00\x00\x02\x0A\x08\x00\x01\x00\xF0" + "\x03\x01\x00\x04\x0A\x12\x20\x3C\x4A\x32\x24\x10\x02\x00\x04\xF0\x05\x07\x01\x08\x00\x06\x06\x02\x00\x00\x02\x01\x00\x03\x01\xA0" + "\x00\x02\x02\x01\x08\x0A\x00\x01\x02\x0A\x04\x78\x02\x01\x01\x02\x00\x02\x00\x57\x02\x01\x01\x01\x00\xF0\x02\x0A\x22\x34\x34\x22" + "\x0E\x02\x06\x08\x02\x03\x01\x03\x01\xF3\x04\x00\x00\x02\x04\x01\x00\x00\x04\x03\x03\x04\x03\x07\x03\x04\x23\x01\x00\x28\x01\x00" + "\xF0\x02\x00\x00\x00\x01\x02\x00\x01\x09\x15\x23\x29\x1F\x0B\x00\xF0\x0A\x1C\x32\x2E\x0E\x00\x00\x02\x02\x06\x04\x06\x02\x01\x03" + "\xF0\x01\x02\x01\x00\x01\x04\x02\x04\x02\x01\x03\x02\x02\x01\x01\x05\x3B\x02\x02\x00\xF0\x01\x02\x02\x00\x00\x01\x0B\x27\x43\x43" + "\x33\x29\x33\x45\x41\xE3\x1D\x05\x08\x24\x38\x18\x00\x00\x04\x04\x02\x05\x02\x00\xD0\x01\x04\x00\x07\x01\x00\x07\x00\x01\x00\x01" + "\x00\x00\x04\x7D\x02\x00\x00\x00\x02\x02\x00\xF0\x13\x31\x29\x03\x14\x20\x20\x1A\x0A\x0B\x31\x2B\x05\x01\x14\xF0\x28\x0A\x04\x03" + "\x09\x01\x00\x00\x01\x04\x00\x00\x06\x02\x02\xA0\x01\x00\x03\x01\x04\x06\x01\x00\x00\x03\x71\xF0\x05\x2D\x13\x0A\x30\x3A\x32\x30" + "\x32\x36\x20\x03\x21\x21\x00\xF0\x00\x1C\x1A\x02\x02\x04\x06\x04\x00\x02\x00\x04\x00\x02\x03\xB0\x00\x06\x06\x04\x04\x07\x00\x02" + "\x02\x04\x02\x41\xF0\x01\x00\x01\x2F\x0D\x12\x38\x22\x0C\x04\x00\x02\x10\x2E\x2C\xF0\x02\x23\x17\x00\x02\x2A\x0A\x00\x03\x05\x01" + "\x01\x02\x01\x01\xE0\x01\x05\x00\x00\x01\x03\x02\x01\x02\x03\x02\x02\x01\x04\x61\xF0\x0D\x11\x02\x1E\x10\x00\x01\x0F\x25\x2D\x23" + "\x0B\x20\x10\x07\xF0\x17\x03\x00\x0C\x1C\x04\x04\x00\x01\x01\x00\x02\x00\x00\x00\x93\x02\x02\x02\x05\x05\x04\x02\x06\x01\x41\xF0" + "\x02\x00\x09\x07\x08\x14\x02\x00\x05\x21\x25\x21\x29\x1D\x04\xF0\x1A\x02\x09\x03\x01\x04\x16\x08\x02\x02\x04\x01\x02\x02\x06\xE0" + "\x00\x01\x04\x04\x01\x04\x04\x02\x00\x03\x02\x00\x02\x01\x11\x13\x01\xF0\x00\x0F\x05\x00\x0A\x01\x01\x04\x12\x0A\x0C\x12\x10\x02" + "\x12\xF0\x02\x05\x07\x00\x02\x14\x16\x04\x03\x03\x02\x00\x00\x03\x01\xD0\x04\x01\x01\x04\x00\x03\x05\x05\x01\x03\x05\x00\x04\x31" + "\xF0\x02\x00\x00\x06\x04\x00\x03\x02\x00\x02\x1E\x38\x36\x34\x1A\xF0\x02\x03\x01\x00\x02\x02\x00\x08\x12\x02\x04\x02\x02\x00\x07" + "\xF0\x05\x02\x02\x03\x01\x01\x06\x04\x02\x02\x00\x02\x04\x02\x02\x11\xF0\x02\x02\x00\x00\x01\x0A\x08\x07\x0D\x00\x02\x04\x04\x0C" + "\x12\xF0\x0A\x02\x00\x13\x00\x04\x06\x00\x00\x02\x16\x08\x00\x00\x00\xF0\x02\x04\x04\x00\x01\x00\x00\x02\x00\x00\x00\x06\x06\x00" + "\x01\x20\x00\x07\x0C\x37\x01\x07\x00\xF0\x10\x0C\x03\x23\x09\x01\x01\x00\x02\x02\x00\x00\x15\x1D\x02\xF0\x12\x08\x00\x01\x01\x12" + "\x14\x01\x01\x01\x09\x03\x01\x00\x00\xC0\x02\x01\x03\x05\x03\x00\x00\x01\x02\x06\x01\x06\x06\xF0\x01\x00\x01\x00\x00\x00\x01\x13" + "\x27\x0D\x00\x00\x01\x01\x00\x83\x00\x0A\x22\x01\x11\x25\x09\x00\xF0\x01\x15\x29\x09\x0E\x1E\x02\x00\x02\x02\x02\x16\x04\x02\x00" + "\xF0\x02\x01\x00\x00\x00\x01\x00\x02\x02\x04\x00\x03\x05\x02\x00\x20\x02\x01\x0C\xF0\x02\x06\x1B\x35\x21\x03\x00\x00\x02\x02\x02" + "\x1A\x1E\x07\x21\xC4\x31\x21\x11\x0D\x15\x29\x33\x15\x01\x2A\x0E\x00\xF0\x16\x08\x04\x00\x02\x06\x02\x04\x00\x04\x00\x03\x01\x03" + "\x01\x60\x01\x01\x05\x01\x00\x00\x0C\xF0\x02\x12\x16\x11\x35\x3F\x1B\x07\x01\x00\x00\x04\x32\x30\x06\xB5\x1D\x33\x43\x45\x3F\x29" + "\x07\x18\x40\x18\x00\xF0\x10\x0C\x01\x02\x02\x05\x01\x03\x05\x01\x02\x02\x02\x00\x04\x60\x04\x08\x02\x00\x01\x02\x58\x00\x02\x02" + "\x02\x00\xF0\x04\x14\x08\x01\x15\x33\x33\x1B\x07\x00\x00\x06\x22\x32\x2C\xF0\x16\x0A\x08\x12\x1C\x2C\x30\x16\x00\x01\x01\x00\x01" + "\x00\x01\xF0\x0F\x07\x00\x01\x00\x04\x04\x00\x06\x04\x02\x02\x02\x06\x01\x60\x03\x01\x00\x04\x02\x01\x24\x02\x00\x26\x02\x00\xF0" + "\x14\x22\x06\x02\x09\x21\x31\x35\x1D\x03\x00\x04\x18\x2C\x36\x84\x3C\x3A\x3C\x32\x22\x0E\x02\x00\xF0\x09\x2B\x39\x0B\x02\x04\x00" + "\x00\x03\x01\x01\x05\x00\x00\x04\x80\x01\x04\x00\x01\x00\x01\x01\x02\x04\x28\x02\x00\xF0\x04\x2A\x4A\x2E\x1A\x10\x07\x19\x37\x47" + "\x2B\x15\x07\x00\x0A\xF0\x10\x12\x08\x06\x02\x01\x00\x01\x00\x03\x0B\x2D\x45\x2B\x17\xF0\x03\x05\x05\x01\x04\x02\x04\x04\x00\x03" + "\x02\x05\x01\x01\x02\x50\x04\x02\x00\x02\x02\x0E\xF0\x02\x00\x0A\x28\x34\x2E\x1A\x04\x05\x0F\x2D\x37\x33\x29\x1B\xF0\x11\x09\x01" + "\x02\x02\x00\x01\x0D\x1B\x29\x37\x29\x07\x02\x0E\x33\x02\x01\x00\xE0\x01\x01\x00\x03\x00\x01\x01\x05\x02\x02\x01\x02\x02\x03\x3E" + "\x02\x02\x00\xF0\x02\x0E\x20\x32\x38\x28\x10\x06\x09\x1B\x2D\x33\x39\x39\x37\xF0\x33\x37\x39\x3D\x37\x33\x2B\x15\x02\x18\x36\x34" + "\x00\x00\x02\xF0\x02\x02\x01\x03\x01\x04\x04\x00\x00\x02\x00\x02\x00\x01\x00\x20\x03\x01\x44\x00\x00\x02\x00\x25\x02\x00\x23\x02" + "\x00\xF0\x0A\x1C\x34\x46\x3A\x26\x16\x0A\x0D\x0F\x19\x21\x27\x21\x19\xF0\x17\x0F\x02\x18\x28\x46\x3E\x20\x03\x06\x00\x00\x04\x05" + "\x00\xE0\x04\x01\x05\x00\x03\x04\x00\x03\x05\x05\x02\x01\x01\x04\x03\x4C\x02\x00\x02\x00\xF0\x02\x02\x00\x00\x06\x1A\x30\x36\x34" + "\x32\x2A\x20\x16\x10\x12\xF0\x18\x20\x2C\x32\x34\x2E\x12\x02\x01\x19\x01\x06\x02\x03\x00\xF0\x00\x01\x05\x04\x02\x02\x01\x01\x06" + "\x02\x00\x02\x00\x04\x02\x2F\x02\x00\x08\xF0\x02\x0E\x1C\x2A\x2C\x34\x3A\x3C\x38\x34\x30\x28\x1C\x10\x04\xF0\x00\x00\x17\x11\x07" + "\x03\x02\x01\x02\x00\x02\x0A\x00\x03\x01\x90\x04\x02\x04\x02\x02\x00\x00\x00\x01\x3F\x00\x02\x00\x09\xF0\x02\x02\x06\x08\x0A\x10" + "\x0E\x08\x06\x02\x00\x01\x00\x0B\x27\xF0\x31\x09\x03\x03\x07\x01\x02\x04\x03\x01\x01\x04\x04\x05\x01\x70\x00\x00\x01\x00\x00\x04" + "\x01\x9F\x00\x00\x02\x00\x02\x00\x00\x02\x00\x09\x23\x02\x00\xF0\x07\x1D\x31\x2D\x11\x05\x02\x02\x00\x00\x03\x01\x02\x00\x01\xB0" + "\x01\x00\x04\x04\x01\x03\x00\x02\x00\x07\x00\x29\x02\x00\x2B\x02\x00\x33\x01\x01\x00\xF0\x02\x02\x02\x00\x02\x02\x07\x25\x39\x33" + "\x21\x07\x04\x00\x03\xF0\x00\x02\x04\x04\x00\x02\x04\x02\x01\x03\x05\x03\x01\x00\x01\x40\x03\x02\x04\x02\x93\x00\x02\x00\x02\x00" + "\x01\x01\x01\x00\xA4\x01\x01\x01\x00\x01\x01\x00\x00\x01\x00\xF0\x01\x02\x01\x01\x01\x05\x0D\x11\x21\x3B\x49\x2F\x17\x0B\x00\xF0" + "\x01\x01\x02\x06\x02\x00\x02\x06\x02\x02\x00\x00\x02\x00\x04\x80\x02\x06\x06\x06\x01\x00\x04\x03\xF0\x00\x00\x01\x01\x00\x03\x0F" + "\x13\x1B\x1B\x15\x0F\x09\x07\x01\x24\x01\x00\xF0\x01\x00\x01\x02\x00\x03\x0F\x0F\x19\x25\x29\x33\x3B\x37\x1D\xF0\x09\x07\x01\x00" + "\x00\x03\x03\x01\x00\x02\x00\x01\x05\x05\x01\xD0\x00\x02\x00\x00\x02\x00\x01\x05\x03\x02\x01\x03\x02\xF0\x02\x00\x02\x00\x21\x35" + "\x33\x33\x35\x35\x39\x39\x37\x3B\x41\xF0\x3B\x33\x2B\x27\x23\x23\x25\x27\x29\x33\x3B\x3B\x39\x35\x33\xF0\x31\x27\x1F\x0D\x03\x07" + "\x02\x00\x01\x01\x04\x04\x02\x02\x00\xF0\x03\x00\x02\x00\x00\x03\x01\x02\x00\x00\x03\x00\x01\x03\x01\x40\x04\x01\x02\x02\xF0\x01" + "\x01\x01\x2B\x31\x23\x1D\x17\x11\x0B\x0B\x13\x1B\x19\x13\xF0\x1B\x29\x31\x37\x37\x35\x35\x33\x2F\x29\x1B\x19\x15\x17\x11\x83\x05" + "\x03\x01\x05\x03\x00\x03\x01\xF0\x02\x04\x02\x02\x00\x01\x00\x00\x04\x08\x01\x02\x00\x02\x04\x80\x02\x02\x06\x02\x05\x01\x03\x01" + "\xF0\x00\x02\x13\x1B\x09\x01\x00\x00\x02\x02\x00\x03\x03\x00\x05\xE3\x09\x01\x02\x02\x05\x03\x00\x03\x05\x01\x07\x05\x01\x03\xF0" + "\x01\x00\x02\x02\x04\x02\x00\x00\x01\x04\x00\x03\x03\x01\x00\xF0\x00\x00\x01\x03\x04\x00\x00\x00\x01\x01\x02\x04\x00\x02\x02\x20" + "\x00\x00\xF0\x04\x00\x11\x0D\x00\x00\x02\x04\x00\x03\x01\x06\x02\x05\x01\x13\x02\xF0\x01\x01\x00\x02\x00\x02\x02\x04\x00\x00\x08" + "\x04\x00\x04\x02\xF0\x03\x03\x05\x00\x02\x00\x03\x03\x05\x05\x04\x02\x02\x01\x00\xF0\x02\x00\x00\x09\x02\x00\x04\x00\x03\x02\x00" + "\x01\x00\x00\x00\xF0\x01\x03\x0B\x00\x01\x00\x04\x01\x02\x01\x01\x05\x02\x06\x02\xF0\x04\x04\x02\x01\x08\x02\x03\x01\x02\x01\x00" + "\x00\x04\x06\x01\xF0\x00\x02\x00\x02\x00\x03\x02\x01\x02\x00\x03\x05\x00\x04\x02\xF0\x02\x00\x03\x01\x00\x00\x01\x00\x05\x01\x01" + "\x02\x02\x01\x04\x40\x04\x02\x00\x02\xF0\x02\x00\x03\x00\x01\x04\x04\x00\x01\x00\x02\x02\x01\x01\x00\xF0\x03\x00\x05\x01\x03\x00" + "\x01\x03\x00\x00\x01\x01\x01\x00\x00\xF0\x02\x00\x00\x01\x01\x04\x02\x02\x01\x02\x06\x04\x04\x02\x01\xF0\x01\x00\x02\x04\x00\x02" + "\x00\x04\x02\x01\x01\x03\x01\x03\x03\x40\x00\x02\x00\x05\xF0\x00\x01\x00\x01\x00\x00\x05\x01\x02\x00\x03\x00\x06\x04\x00\xF0\x03" + "\x03\x02\x02\x01\x07\x00\x04\x00\x03\x04\x00\x03\x02\x02\xF0\x01\x04\x02\x02\x04\x04\x03\x00\x00\x00\x04\x01\x03\x05\x07\xF0\x00" + "\x01\x02\x01\x01\x01\x04\x02\x04\x04\x06\x00\x04\x00\x03\x40\x01\x00\x04\x00\x83\x02\x04\x02\x00\x08\x04\x06\x04\xF0\x00\x02\x04" + "\x04\x0A\x00\x01\x01\x00\x04\x02\x00\x02\x06\x03\xF0\x00\x04\x05\x01\x02\x05\x03\x05\x00\x01\x03\x01\x01\x00\x05\xF0\x01\x05\x02" + "\x08\x02\x06\x00\x00\x02\x01\x05\x04\x00\x00\x03\x80\x06\x03\x00\x02\x00\x05\x03\x04\xF0\x00\x04\x04\x01\x03\x07\x03\x00\x07\x01" + "\x00\x02\x09\x05\x03\xF0\x03\x04\x00\x01\x00\x04\x00\x00\x01\x00\x04\x02\x00\x01\x02\xF0\x01\x01\x00\x00\x00\x02\x04\x01\x02\x02" + "\x04\x00\x04\x02\x02\xF0\x00\x03\x02\x01\x01\x00\x02\x05\x01\x00\x01\x01\x00\x04\x04\x40\x02\x04\x00\x01\xF0\x00\x02\x10\x0A\x01" + "\x02\x02\x04\x06\x02\x02\x00\x02\x03\x00\x64\x07\x0B\x00\x00\x04\x00\xF0\x03\x01\x00\x02\x04\x04\x02\x06\x08\x06\x00\x04\x06\x04" + "\x01\xF0\x02\x04\x02\x00\x05\x02\x04\x00\x00\x02\x01\x03\x01\x02\x04\x90\x00\x03\x04\x00\x03\x02\x00\x02\x02\xF0\x01\x00\x10\x04" + "\x00\x06\x04\x01\x03\x03\x01\x00\x01\x00\x00\xF0\x08\x00\x05\x01\x03\x01\x00\x01\x05\x05\x04\x02\x01\x01\x05\xF0\x03\x05\x05\x00" + "\x05\x07\x01\x00\x03\x02\x03\x01\x02\x02\x02\xF0\x01\x01\x05\x04\x00\x04\x02\x08\x02\x03\x03\x08\x00\x01\x04\x40\x03\x02\x02\x01" + "\xF0\x00\x00\x0C\x08\x04\x00\x03\x01\x02\x08\x01\x03\x02\x02\x02\xF0\x00\x06\x08\x06\x06\x01\x02\x06\x04\x02\x01\x02\x00\x02\x02" + "\xF0\x00\x02\x01\x07\x00\x08\x03\x01\x02\x03\x00\x00\x02\x06\x06\xF0\x01\x05\x01\x03\x02\x02\x04\x05\x03\x01\x02\x05\x07\x01\x00" + "\x40\x00\x01\x00\x04\xF0\x02\x00\x08\x0E\x04\x02\x02\x04\x04\x05\x00\x02\x00\x00\x05\xF0\x00\x01\x05\x03\x07\x02\x00\x01\x00\x02" + "\x01\x01\x00\x00\x01\xF0\x00\x02\x02\x04\x02\x00\x04\x02\x00\x00\x06\x04\x03\x01\x00\xF0\x06\x06\x08\x02\x02\x01\x03\x01\x00\x01" + "\x02\x03\x02\x00\x00\x40\x00\x03\x03\x02\xF0\x02\x02\x04\x22\x00\x03\x01\x03\x01\x04\x06\x04\x02\x06\x00\xF0\x03\x00\x00\x02\x08" + "\x00\x01\x01\x00\x02\x04\x01\x03\x06\x04\xF0\x00\x02\x01\x01\x01\x05\x00\x01\x03\x00\x05\x00\x02\x03\x03\xF0\x03\x00\x01\x00\x01" + "\x00\x00\x04\x00\x02\x08\x08\x02\x00\x03\x40\x04\x02\x02\x03\x03\xF0\x16\x12\x02\x03\x05\x02\x00\x03\x03\x00\x03\x04\x02\x02\x04" + "\xF0\x02\x05\x01\x02\x04\x02\x01\x00\x02\x01\x00\x03\x01\x01\x00\xF0\x04\x00\x02\x04\x01\x00\x00\x00\x02\x02\x03\x03\x01\x00\x02" + "\xF0\x01\x03\x01\x04\x02\x01\x02\x03\x00\x04\x04\x01\x03\x00\x03\x10\x01\xF0\x00\x00\x01\x02\x22\x06\x08\x08\x04\x02\x04\x06\x02" + "\x00\x02\xF0\x02\x02\x00\x01\x06\x02\x06\x01\x05\x01\x00\x00\x02\x09\x01\xF0\x00\x00\x02\x00\x04\x04\x01\x02\x01\x01\x00\x03\x01" + "\x03\x01\xF0\x00\x03\x03\x04\x02\x02\x01\x03\x02\x02\x05\x01\x01\x00\x04\x40\x02\x02\x02\x00\xF0\x01\x00\x00\x00\x22\x3C\x3C\x38" + "\x30\x2E\x2C\x26\x1E\x24\x20\xF0\x1E\x20\x16\x12\x0C\x08\x04\x06\x0A\x0A\x00\x00\x02\x04\x00\xF0\x02\x04\x02\x05\x03\x01\x07\x03" + "\x00\x02\x06\x00\x02\x08\x08\xF0\x02\x04\x00\x03\x02\x00\x00\x02\x00\x01\x00\x02\x01\x02\x02\x40\x00\x01\x02\x06\xF0\x02\x00\x02" + "\x02\x06\x1C\x20\x26\x2A\x2E\x30\x34\x40\x3A\x3A\xF0\x3C\x38\x3A\x38\x32\x34\x2A\x24\x24\x16\x10\x08\x0A\x0A\x08\xF0\x06\x04\x02" + "\x04\x00\x02\x08\x02\x02\x02\x01\x02\x00\x01\x05\xF0\x01\x00\x02\x02\x05\x01\x01\x02\x02\x02\x06\x00\x04\x03\x00\x40\x00\x04\x04" + "\x01\x66\x01\x01\x00\x00\x01\x00\xF0\x01\x02\x04\x06\x08\x10\x14\x1E\x24\x2A\x2E\x30\x36\x40\x42\xF0\x3C\x30\x26\x14\x08\x0A\x06" + "\x00\x03\x00\x02\x04\x01\x00\x02\xF0\x01\x01\x02\x04\x00\x00\x05\x02\x04\x00\x01\x01\x05\x02\x00\x70\x02\x00\x01\x00\x01\x05\x01" + "\x31\xF0\x01\x02\x02\x06\x08\x0C\x0E\x12\x1C\x2A\x32\x46\x50\x3C\x30\xF0\x28\x22\x0E\x0A\x02\x04\x01\x03\x05\x03\x00\x00\x01\x02" + "\x06\xF0\x02\x03\x01\x03\x03\x00\x03\x01\x03\x02\x04\x04\x03\x07\x00\x33\x02\x02\x00\x33\x01\x01\x00\x2E\x02\x00\xF0\x01\x02\x00" + "\x02\x16\x2A\x38\x38\x3A\x34\x30\x28\x1A\x0A\x08\xF0\x08\x02\x02\x00\x01\x02\x01\x02\x02\x00\x02\x02\x00\x02\x01\x60\x05\x03\x07" + "\x04\x0C\x00\x04\x2A\x02\x00\x53\x01\x00\x02\x02\x00\x46\x01\x00\x02\x00\xF0\x02\x08\x14\x22\x28\x2E\x38\x3E\x32\x1C\x08\x02\x04" + "\x01\x03\xF0\x02\x01\x00\x04\x06\x06\x04\x06\x04\x04\x04\x08\x02\x00\x04\x06\x74\x02\x02\x00\x00\x01\x01\x00\x37\x01\x01\x00\x23" + "\x01\x00\xF0\x02\x00\x00\x02\x02\x00\x04\x08\x0E\x18\x2A\x44\x4E\x3A\x20\xF0\x16\x0C\x06\x02\x00\x03\x01\x03\x05\x0B\x07\x00\x00" + "\x0B\x03\x20\x00\x01\x64\x01\x03\x03\x01\x02\x00\x3B\x02\x02\x00\x5B\x02\x00\x00\x02\x00\xF0\x01\x00\x02\x00\x0A\x22\x3A\x3C\x34" + "\x2A\x1A\x0A\x08\x08\x02\x90\x00\x00\x08\x02\x01\x02\x00\x00\x03\xF0\x39\x39\x3B\x3D\x3D\x37\x2F\x2D\x25\x1F\x1B\x19\x15\x0D\x07" + "\x23\x01\x02\x1F\x00\x13\x01\xF0\x00\x01\x01\x02\x01\x00\x04\x10\x20\x2A\x38\x40\x2C\x0C\x02\x90\x02\x02\x03\x03\x00\x01\x06\x01" + "\x04\xF0\x1D\x1D\x21\x19\x21\x27\x31\x2F\x39\x39\x3F\x41\x3D\x45\x4D\xC5\x4D\x43\x35\x2B\x21\x17\x11\x0D\x09\x05\x01\x00\x29\x01" + "\x00\xF0\x03\x00\x01\x01\x01\x00\x06\x0E\x16\x2E\x46\x42\x2C\x18\x10\x60\x02\x05\x0A\x05\x01\x03\xF0\x07\x07\x03\x0B\x01\x01\x00" + "\x05\x01\x0B\x01\x02\x0D\x0D\x0B\xF0\x09\x1B\x25\x2F\x37\x3F\x39\x37\x33\x2F\x25\x19\x11\x05\x01\xC6\x00\x00\x02\x00\x00\x00\x02" + "\x02\x00\x00\x02\x00\xF0\x02\x01\x01\x00\x02\x06\x1A\x32\x34\x26\x1C\x12\x06\x04\x02\x10\x04\xF0\x02\x02\x08\x08\x03\x01\x01\x02" + "\x00\x02\x02\x00\x04\x04\x02\xF0\x07\x01\x03\x07\x09\x07\x0F\x17\x1D\x23\x31\x35\x33\x37\x33\xE4\x21\x13\x07\x00\x01\x01\x00\x00" + "\x02\x02\x00\x00\x00\x02\xF0\x00\x02\x02\x02\x00\x00\x00\x02\x14\x28\x38\x36\x1A\x08\x02\x10\x00\xF0\x00\x01\x05\x01\x02\x02\x06" + "\x00\x02\x02\x07\x09\x03\x01\x00\xF0\x01\x00\x03\x00\x00\x00\x03\x01\x03\x01\x05\x0D\x11\x1D\x27\xF0\x39\x43\x4B\x45\x2D\x17\x0B" + "\x03\x01\x00\x00\x02\x02\x00\x01\x84\x01\x00\x00\x01\x00\x00\x01\x00\x70\x02\x0A\x1C\x3C\x44\x2E\x18\xF0\x03\x02\x04\x02\x02\x02" + "\x01\x01\x00\x02\x00\x04\x01\x00\x00\xF0\x04\x02\x08\x06\x04\x03\x02\x02\x01\x03\x01\x00\x07\x05\x00\xF0\x03\x07\x09\x17\x29\x3B" + "\x37\x2D\x1F\x11\x03\x00\x00\x00\x02\x24\x02\x00\x23\x02\x00\x80\x02\x02\x00\x02\x02\x12\x2E\x3C"; + +BYTE TEST_64X64_RED_PLANE[4096] = + "\x23\x1F\x1E\x1D\x1D\x1E\x1D\x1F\x23\x4A\x78\x71\x64\x58\x4B\x3E\x30\x29\x26\x24\x22\x21\x20\x20\x20\x1D\x1E\x20\x1E\x1F\x20\x20" + "\x1F\x20\x21\x22\x1E\x1F\x1F\x1F\x1F\x1F\x1F\x1F\x1E\x1D\x1F\x20\x1F\x1F\x1F\x1E\x1E\x1F\x1F\x20\x1E\x1F\x1F\x1F\x1E\x1D\x1E\x1C" + "\x3F\x2C\x28\x24\x21\x22\x20\x21\x25\x56\x94\x96\x97\x92\x8C\x86\x7F\x71\x61\x4D\x38\x2C\x29\x26\x23\x21\x21\x20\x1F\x1F\x1E\x1E" + "\x20\x20\x20\x20\x1F\x20\x1F\x20\x1E\x1E\x1D\x1F\x1E\x1E\x21\x20\x1F\x1F\x1F\x1F\x1E\x1F\x1D\x1F\x1F\x1F\x20\x1E\x1E\x1F\x1F\x1F" + "\x89\x83\x70\x5B\x44\x39\x2F\x27\x2C\x5B\x95\x96\x98\x98\x98\x98\x97\x97\x97\x97\x97\x8B\x76\x5C\x47\x3B\x2F\x28\x23\x22\x21\x21" + "\x20\x1F\x1E\x1F\x20\x20\x20\x20\x1F\x1F\x21\x21\x1F\x1E\x1F\x1F\x1F\x1E\x20\x1F\x21\x20\x1F\x1E\x1E\x1D\x1E\x1E\x1E\x1F\x21\x1E" + "\x8A\x8D\x8E\x8F\x8B\x7C\x6B\x5B\x4B\x66\x96\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x90\x81\x70\x5F\x4C\x39\x2B\x24" + "\x21\x21\x1E\x1E\x1F\x1E\x20\x1F\x1E\x20\x21\x21\x1D\x1D\x1E\x1E\x21\x21\x20\x20\x21\x21\x1F\x1F\x1F\x1E\x1E\x1E\x20\x1F\x1F\x1F" + "\x8A\x8D\x8E\x8F\x91\x92\x95\x92\x89\x8C\x97\x97\x99\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x95\x8D\x82\x6E\x4C" + "\x2F\x2A\x23\x20\x21\x1F\x1F\x20\x1F\x1F\x1E\x1D\x1D\x1D\x1D\x1F\x1F\x1E\x1E\x20\x20\x1E\x1E\x1E\x1E\x1E\x1F\x20\x20\x20\x1F\x1E" + "\x8A\x8D\x8F\x90\x91\x92\x95\x94\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x97\x97\x97" + "\x8D\x6C\x4B\x34\x27\x22\x21\x1E\x1D\x1F\x20\x1F\x1F\x1E\x1E\x20\x1F\x1F\x1E\x20\x1F\x1F\x1E\x1F\x1E\x1F\x21\x24\x1F\x1F\x20\x20" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x97\x97\x97\x97\x97\x97\x97\x98\x98\x98\x98\x98\x98" + "\x98\x98\x8E\x76\x51\x31\x24\x21\x1F\x1F\x1E\x1E\x1F\x1F\x1F\x1F\x20\x1E\x20\x21\x1E\x1F\x1F\x1E\x1F\x20\x20\x20\x1D\x1E\x1F\x20" + "\x8A\x8D\x8F\x90\x91\x93\x95\x95\x95\x95\x96\x96\x98\x98\x98\x98\x98\x98\x98\x98\x97\x97\x96\x97\x98\x97\x97\x8E\x7C\x6D\x64\x72" + "\x89\x96\x98\x98\x8E\x6B\x38\x25\x1E\x1E\x1F\x20\x1F\x20\x1F\x1F\x1F\x1F\x1F\x1E\x1E\x1E\x21\x1F\x1F\x20\x20\x1F\x1D\x1E\x1D\x1E" + "\x8A\x8D\x8F\x90\x91\x93\x95\x95\x95\x95\x96\x96\x98\x98\x98\x98\x98\x98\x98\x98\x99\x98\x97\x98\x96\x87\x62\x39\x2E\x2E\x2F\x2F" + "\x32\x45\x74\x92\x98\x98\x7D\x3E\x22\x20\x21\x22\x1E\x1E\x21\x1E\x1F\x1F\x20\x1E\x20\x1E\x1D\x1E\x1F\x1E\x1E\x1F\x1E\x1E\x1D\x1E" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97\x98\x7D\x47\x31\x30\x49\x55\x5B\x52" + "\x3D\x30\x37\x5D\x92\x98\x97\x72\x2C\x23\x1F\x1E\x20\x1F\x21\x1F\x1F\x1F\x20\x21\x20\x1E\x1D\x1D\x1E\x1E\x21\x20\x1F\x1F\x1E\x1D" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97\x91\x45\x2F\x3E\x6D\x8B\x91\x93\x8F" + "\x81\x59\x32\x31\x68\x97\x97\x94\x4D\x26\x22\x1E\x22\x21\x21\x1E\x1F\x20\x20\x21\x1E\x20\x1E\x1F\x20\x1E\x1E\x20\x1F\x1F\x1F\x1E" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x97\x97\x93\x56\x2F\x44\x81\x97\x98\x97\x94\x91" + "\x96\x96\x69\x33\x3A\x79\x97\x97\x81\x37\x24\x1E\x20\x20\x20\x20\x1E\x20\x1F\x1F\x1F\x1F\x1E\x1E\x21\x1E\x1E\x20\x1F\x20\x1F\x1F" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x97\x98\x82\x3E\x35\x6C\x96\x98\x96\x82\x63\x57" + "\x69\x88\x8F\x4C\x2F\x59\x92\x97\x91\x58\x28\x1F\x20\x1F\x20\x20\x1F\x1F\x1F\x20\x1F\x1F\x1F\x1D\x1E\x1F\x1E\x20\x1F\x1F\x1E\x1E" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x97\x98\x72\x36\x3F\x86\x98\x98\x8F\x57\x32\x2C" + "\x35\x62\x94\x6A\x2F\x4E\x8B\x97\x97\x75\x34\x22\x21\x1F\x1F\x20\x20\x21\x20\x1E\x21\x22\x1F\x1D\x1F\x1F\x1E\x1F\x1F\x1F\x1F\x1E" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x98\x98\x98\x97\x97\x97\x97\x98\x61\x2E\x44\x93\x98\x97\x94\x70\x46\x3E" + "\x4D\x79\x97\x80\x31\x44\x82\x98\x98\x8E\x50\x26\x1F\x1E\x1E\x20\x21\x20\x1E\x1F\x21\x20\x1F\x1D\x1E\x1F\x1D\x1F\x1D\x1D\x1F\x1F" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x97\x98\x98\x98\x98\x98\x97\x98\x69\x31\x41\x8D\x98\x97\x97\x95\x8A\x80" + "\x8D\x97\x97\x7A\x2F\x45\x83\x98\x98\x97\x68\x2E\x20\x1E\x1F\x1F\x1E\x1E\x1F\x20\x1F\x1F\x1F\x20\x1F\x1E\x1D\x1F\x1F\x1E\x1F\x20" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x98\x97\x97\x98\x98\x98\x98\x99\x97\x98\x77\x38\x39\x7E\x98\x98\x98\x98\x97\x97" + "\x98\x98\x98\x61\x2F\x50\x8E\x98\x98\x98\x80\x3A\x21\x1F\x1F\x21\x1F\x20\x1F\x20\x1E\x1F\x1F\x20\x20\x1F\x20\x21\x1E\x1D\x1F\x1D" + "\x8A\x8D\x8F\x90\x92\x93\x95\x95\x96\x96\x97\x97\x96\x8F\x94\x97\x98\x98\x97\x97\x97\x97\x89\x47\x2E\x52\x8D\x97\x98\x98\x97\x98" + "\x98\x97\x7C\x3B\x31\x65\x97\x98\x98\x98\x96\x4D\x26\x1F\x20\x1D\x1D\x1D\x20\x20\x1F\x1F\x1F\x1E\x1F\x1E\x20\x21\x1F\x1F\x1E\x20" + "\x8A\x8C\x8F\x90\x92\x93\x94\x95\x96\x96\x96\x97\x94\x76\x68\x87\x98\x98\x98\x98\x97\x97\x97\x72\x2F\x34\x5D\x8B\x98\x98\x97\x98" + "\x95\x7D\x49\x2E\x47\x86\x98\x98\x99\x98\x98\x69\x27\x20\x1F\x1E\x1C\x1E\x20\x20\x1E\x1E\x1F\x1D\x1F\x1E\x1F\x1E\x20\x20\x1F\x1F" + "\x8A\x8D\x8F\x90\x92\x93\x94\x95\x96\x96\x96\x97\x97\x7F\x49\x45\x72\x94\x98\x98\x98\x98\x98\x94\x51\x2E\x31\x49\x6F\x83\x88\x7F" + "\x61\x3E\x2E\x32\x7C\x97\x98\x98\x98\x98\x98\x86\x2D\x23\x1F\x1E\x1F\x20\x21\x20\x1F\x1F\x1E\x1D\x1E\x1E\x1F\x1D\x1E\x1F\x1F\x1F" + "\x8A\x8D\x8F\x90\x92\x93\x94\x95\x96\x96\x96\x97\x98\x94\x5D\x2E\x2C\x46\x76\x8D\x97\x98\x98\x98\x91\x67\x3C\x2C\x2D\x2E\x2E\x2E" + "\x2D\x32\x4B\x79\x98\x98\x98\x98\x98\x97\x97\x97\x3F\x23\x1F\x1F\x1D\x1F\x1F\x1E\x1F\x1F\x1F\x1F\x1D\x1F\x21\x20\x1F\x1F\x1F\x1F" + "\x8B\x8D\x8F\x91\x92\x93\x94\x95\x96\x96\x96\x97\x98\x98\x7A\x39\x28\x28\x32\x4E\x74\x8F\x98\x98\x98\x94\x7A\x5E\x4C\x3A\x39\x42" + "\x52\x6A\x8B\x97\x98\x98\x98\x98\x98\x98\x96\x80\x39\x23\x20\x1F\x1E\x20\x20\x1F\x21\x20\x1F\x1F\x20\x1F\x1F\x1F\x1E\x20\x1F\x20" + "\x8C\x8D\x8F\x91\x92\x93\x95\x95\x96\x96\x97\x97\x98\x98\x93\x62\x30\x29\x26\x27\x34\x4C\x72\x93\x98\x98\x97\x94\x8C\x84\x83\x88" + "\x8E\x95\x98\x98\x98\x98\x98\x98\x97\x8C\x62\x3D\x25\x21\x20\x1F\x1E\x1F\x20\x20\x1F\x20\x20\x20\x1F\x20\x1E\x1E\x20\x1F\x1F\x1F" + "\x8C\x8E\x90\x91\x93\x93\x95\x95\x96\x96\x97\x97\x98\x98\x97\x97\x8B\x64\x46\x33\x28\x27\x2B\x3A\x61\x7E\x8C\x95\x98\x98\x98\x98" + "\x97\x97\x97\x98\x98\x97\x93\x89\x62\x38\x29\x22\x21\x1F\x1F\x1E\x20\x20\x20\x21\x1F\x1F\x20\x1E\x1E\x20\x1F\x20\x1F\x1F\x1F\x1F" + "\x8D\x8F\x90\x92\x93\x94\x95\x95\x96\x96\x97\x97\x98\x98\x98\x98\x98\x96\x8A\x6F\x4B\x32\x29\x27\x29\x34\x4A\x61\x78\x83\x8E\x97" + "\x98\x98\x97\x95\x86\x74\x5F\x44\x2B\x2A\x2C\x30\x21\x1E\x20\x1E\x1F\x1F\x1F\x20\x1F\x1D\x1F\x1E\x1E\x1F\x20\x21\x1E\x20\x20\x1E" + "\x8D\x8F\x90\x92\x93\x94\x95\x96\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x97\x8C\x79\x57\x37\x2C\x29\x28\x29\x35\x3C\x45\x50" + "\x57\x52\x4D\x4A\x3F\x34\x2A\x2A\x2F\x49\x6F\x70\x25\x1F\x1F\x1F\x20\x1F\x1E\x1F\x20\x1F\x20\x1E\x1E\x1E\x20\x21\x1D\x1F\x1F\x1F" + "\x8E\x90\x91\x92\x93\x94\x95\x96\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x97\x98\x97\x98\x94\x77\x58\x42\x32\x28\x28\x27\x28" + "\x27\x2A\x2A\x2A\x2C\x35\x42\x58\x85\x97\x97\x6B\x28\x20\x20\x20\x1E\x1F\x1F\x1D\x1F\x1F\x1F\x1F\x1F\x1C\x1D\x1F\x1E\x1F\x1F\x20" + "\x8F\x90\x92\x93\x93\x95\x95\x96\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x96\x89\x74\x62\x59\x4D\x42" + "\x38\x3F\x48\x51\x62\x74\x86\x95\x98\x98\x92\x4E\x26\x21\x20\x1F\x1E\x1F\x20\x1D\x20\x20\x1F\x1F\x1D\x1D\x1E\x1F\x20\x1F\x20\x20" + "\x8F\x90\x92\x93\x94\x95\x95\x96\x96\x97\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x96\x92\x8D\x88" + "\x84\x87\x8B\x8F\x95\x98\x98\x98\x98\x98\x75\x35\x23\x1E\x20\x1E\x1D\x1F\x20\x20\x1F\x1D\x1E\x20\x1D\x1F\x1E\x1F\x1F\x1E\x1F\x1F" + "\x90\x91\x92\x93\x94\x95\x96\x96\x96\x97\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97\x97" + "\x98\x98\x98\x98\x98\x98\x97\x98\x8B\x69\x39\x27\x20\x1E\x1E\x1D\x1F\x20\x1F\x1F\x1F\x1F\x1F\x1E\x1D\x1F\x20\x1E\x20\x1F\x21\x20" + "\x90\x92\x93\x94\x95\x95\x96\x96\x96\x97\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97\x97" + "\x98\x98\x98\x98\x97\x97\x8D\x73\x4D\x2E\x24\x21\x1E\x1E\x1D\x1D\x1F\x1E\x1E\x1E\x1E\x20\x20\x20\x1F\x1E\x1D\x1F\x20\x20\x1E\x1F" + "\x91\x92\x93\x94\x95\x95\x96\x96\x96\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x97\x98\x98\x98\x98\x97\x97\x98\x98\x98\x98\x98\x98" + "\x99\x98\x99\x98\x8D\x68\x47\x32\x26\x23\x22\x20\x1C\x1D\x1E\x20\x1F\x1F\x1E\x20\x1D\x1E\x1E\x1E\x1C\x1E\x1D\x1E\x1F\x20\x1E\x1E" + "\x92\x93\x92\x94\x95\x95\x95\x95\x97\x97\x97\x98\x99\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x99\x97\x97\x97\x98\x97\x97\x97\x93" + "\x8B\x83\x6D\x4E\x30\x29\x24\x22\x20\x1F\x20\x20\x1F\x1F\x1E\x20\x22\x20\x20\x20\x1C\x1E\x1F\x1E\x1F\x1F\x20\x20\x1E\x20\x21\x1E" + "\x92\x93\x93\x94\x94\x8C\x80\x7D\x77\x77\x7D\x83\x8A\x8E\x92\x96\x98\x98\x98\x98\x98\x98\x98\x97\x98\x97\x91\x8B\x81\x77\x68\x5B" + "\x4A\x38\x2B\x27\x22\x20\x20\x20\x1F\x1D\x1E\x1F\x1F\x1F\x1F\x20\x1F\x1E\x20\x20\x1F\x1E\x1F\x20\x1E\x1E\x1D\x1E\x1F\x1F\x1F\x1E" + "\x92\x94\x94\x94\x69\x49\x3C\x38\x33\x33\x37\x3C\x42\x43\x45\x4C\x55\x5C\x65\x6D\x6B\x68\x65\x61\x59\x4E\x47\x42\x3C\x34\x2A\x27" + "\x23\x23\x22\x20\x22\x20\x20\x20\x21\x1F\x20\x20\x1F\x1F\x1E\x21\x20\x1E\x1F\x20\x20\x1F\x20\x1F\x1F\x1E\x1C\x1C\x20\x1F\x1F\x1F" + "\x92\x94\x91\x5D\x2D\x22\x1E\x20\x1E\x1F\x22\x23\x23\x23\x25\x25\x25\x25\x24\x27\x28\x26\x27\x26\x23\x24\x23\x22\x21\x21\x1F\x21" + "\x20\x1F\x1E\x1F\x1F\x1F\x1E\x1F\x20\x20\x21\x21\x20\x1F\x1F\x21\x1F\x1F\x20\x20\x21\x1F\x20\x1F\x1F\x20\x1F\x1F\x1D\x1F\x20\x1F" + "\x94\x95\x7A\x39\x23\x1F\x1D\x1D\x1D\x1F\x20\x1F\x1F\x20\x20\x1F\x1E\x20\x20\x1F\x20\x21\x20\x20\x20\x1F\x20\x1F\x20\x1D\x1D\x1F" + "\x1E\x1F\x1F\x21\x22\x20\x1D\x1E\x20\x21\x21\x1F\x1F\x1D\x1D\x21\x1F\x1F\x1F\x20\x21\x1F\x20\x1F\x1F\x21\x20\x1E\x1F\x1F\x1F\x1F" + "\x94\x94\x60\x2A\x20\x1D\x1D\x1F\x1D\x1F\x20\x21\x1F\x1E\x1F\x1F\x1F\x20\x20\x1E\x1F\x20\x20\x1D\x20\x1F\x20\x20\x1F\x1F\x1E\x1F" + "\x1F\x1F\x1F\x1E\x1F\x20\x1F\x1F\x1E\x20\x1E\x1E\x1F\x1F\x1F\x20\x1F\x1F\x20\x21\x1E\x1F\x20\x20\x1F\x1F\x21\x1F\x1F\x1F\x1F\x1D" + "\x94\x8E\x52\x26\x1F\x1D\x1E\x1E\x1E\x1F\x1F\x20\x1F\x1F\x1F\x20\x22\x21\x20\x20\x1F\x20\x20\x1F\x1F\x1F\x20\x21\x21\x20\x20\x20" + "\x1F\x21\x1E\x1D\x20\x1F\x20\x1E\x1D\x1F\x1E\x1F\x1F\x1E\x1F\x1E\x1F\x1F\x1F\x1F\x1E\x1D\x1E\x20\x20\x1F\x1F\x1E\x1F\x20\x1F\x1F" + "\x94\x8C\x4F\x25\x1D\x1F\x20\x1E\x1D\x1E\x1E\x1E\x1F\x1F\x20\x20\x21\x20\x20\x1F\x1F\x1E\x1E\x20\x1E\x1E\x20\x20\x20\x1F\x20\x20" + "\x20\x1F\x1D\x1E\x20\x20\x1F\x1F\x20\x20\x20\x20\x1F\x1E\x1E\x1E\x1F\x20\x20\x1F\x1F\x1E\x1E\x1E\x1E\x1E\x1E\x1E\x1F\x20\x1F\x1E" + "\x95\x8A\x4D\x25\x1C\x1D\x1D\x1E\x1E\x1E\x1F\x1F\x20\x21\x1F\x1D\x1F\x20\x21\x1F\x1C\x1E\x1F\x1F\x1E\x20\x1F\x20\x21\x20\x20\x22" + "\x20\x20\x20\x20\x1E\x20\x1F\x1F\x20\x1E\x1E\x1C\x1C\x1E\x1F\x1E\x20\x1F\x1F\x20\x20\x1F\x1F\x21\x1E\x20\x1E\x1E\x1E\x20\x20\x1E" + "\x96\x8F\x50\x25\x20\x1F\x1F\x1F\x20\x20\x20\x1F\x21\x22\x21\x21\x1F\x1F\x1F\x1F\x1E\x1F\x20\x1F\x21\x20\x20\x21\x1F\x20\x20\x1F" + "\x1F\x1E\x1F\x1E\x1D\x1E\x1F\x20\x1F\x1E\x1D\x1D\x1D\x1E\x20\x1F\x1F\x1F\x1E\x1E\x21\x1F\x1F\x1F\x1F\x1F\x1E\x1E\x1E\x1E\x1F\x20" + "\x96\x92\x56\x26\x1F\x1D\x1F\x20\x1D\x1F\x20\x21\x1E\x20\x1F\x1F\x20\x1F\x1E\x1E\x1F\x1E\x1F\x1F\x20\x20\x20\x21\x1F\x21\x1F\x1F" + "\x1F\x1D\x1F\x1F\x1E\x1E\x1F\x21\x20\x1F\x1E\x1F\x20\x1F\x1E\x1F\x1E\x1E\x1E\x20\x1E\x1E\x1F\x1E\x1F\x1F\x1F\x1F\x1E\x20\x1F\x1E" + "\x96\x96\x66\x2D\x1D\x1D\x1F\x21\x1F\x20\x20\x22\x1F\x1E\x1F\x1B\x1C\x20\x1F\x21\x1E\x1E\x1F\x1F\x20\x1E\x1F\x20\x1F\x21\x20\x20" + "\x21\x20\x21\x1F\x20\x20\x20\x1E\x20\x1F\x1F\x1E\x1E\x1F\x20\x1E\x1E\x1F\x1E\x1D\x1E\x1F\x20\x1E\x1F\x20\x1E\x1E\x1E\x20\x1F\x1E" + "\x96\x97\x7A\x36\x20\x21\x21\x20\x1F\x1E\x1F\x20\x1E\x1F\x20\x1F\x1D\x1D\x1E\x20\x1F\x1F\x1F\x1E\x1F\x20\x20\x1F\x1F\x1F\x1F\x1E" + "\x1F\x21\x1E\x1D\x1F\x1F\x20\x20\x1E\x1E\x1F\x1F\x1E\x1E\x1F\x1E\x1F\x1F\x1F\x1F\x21\x20\x20\x1D\x21\x20\x1F\x20\x1E\x20\x1F\x1E" + "\x97\x97\x8C\x3F\x22\x21\x20\x20\x1F\x21\x1F\x1E\x1F\x1F\x20\x1F\x20\x20\x20\x22\x1F\x20\x21\x1F\x1E\x1F\x21\x1F\x1F\x20\x1F\x1E" + "\x1D\x1D\x1F\x1F\x1E\x1F\x20\x1F\x1E\x1F\x20\x22\x21\x1E\x1C\x1D\x1F\x20\x20\x1F\x1F\x1F\x1E\x1D\x1E\x1E\x1E\x20\x1E\x1E\x1F\x1F" + "\x97\x97\x95\x51\x26\x22\x20\x22\x20\x1F\x1F\x20\x1E\x1F\x1E\x1F\x1F\x1F\x1F\x1E\x1E\x1F\x20\x1F\x1F\x1E\x20\x20\x1E\x1F\x20\x1F" + "\x1F\x20\x20\x1F\x1F\x20\x20\x1F\x21\x20\x1F\x21\x21\x20\x1E\x1E\x21\x21\x1F\x1E\x1E\x1E\x1E\x1F\x1D\x1D\x1E\x1F\x1E\x1D\x1F\x20" + "\x97\x98\x98\x79\x2B\x23\x1F\x1E\x1F\x20\x21\x20\x20\x21\x1E\x1D\x1F\x1E\x1F\x21\x20\x1F\x1F\x1F\x20\x20\x20\x1E\x21\x20\x1F\x1F" + "\x1D\x1E\x1F\x1F\x1F\x1F\x1F\x1E\x1F\x20\x20\x20\x20\x1E\x1F\x1E\x1F\x21\x20\x1F\x1F\x1F\x1E\x21\x20\x1E\x1F\x1E\x20\x1F\x1F\x1D" + "\x98\x98\x98\x93\x40\x25\x20\x1E\x20\x21\x1F\x20\x1F\x1F\x21\x20\x20\x1F\x1F\x1F\x1E\x20\x22\x20\x1F\x20\x20\x1D\x21\x20\x1F\x1E" + "\x1E\x20\x1E\x1E\x20\x1F\x1F\x1F\x1E\x21\x20\x1F\x1E\x1D\x1F\x21\x1F\x1F\x1F\x20\x20\x1F\x1F\x20\x20\x20\x1F\x1D\x1F\x1F\x1E\x1C" + "\x98\x98\x98\x97\x6A\x2F\x28\x26\x28\x26\x25\x26\x23\x24\x25\x23\x21\x22\x20\x22\x1F\x23\x21\x1D\x1E\x20\x20\x1E\x1E\x1E\x1F\x1F" + "\x1F\x20\x21\x20\x20\x1F\x1F\x1E\x1E\x1F\x20\x1D\x1E\x1E\x1F\x1F\x20\x20\x1F\x20\x20\x1E\x20\x1D\x1F\x20\x1F\x1E\x1F\x1F\x1E\x1E" + "\x98\x98\x98\x97\x93\x78\x6F\x6A\x64\x5F\x5B\x57\x4F\x4D\x4B\x49\x45\x3E\x38\x33\x2A\x2A\x29\x26\x27\x23\x23\x21\x20\x1F\x1E\x1F" + "\x1D\x1F\x1F\x1D\x1D\x1E\x1E\x1E\x21\x20\x20\x1E\x21\x1E\x1F\x1F\x1E\x20\x1F\x1F\x1F\x1F\x20\x1E\x20\x1E\x21\x20\x20\x1F\x1F\x20" + "\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97\x95\x93\x8D\x85\x7D\x74\x6A\x62\x59\x51\x46\x3B\x30\x2B\x2A\x27\x24\x24" + "\x22\x20\x1F\x1F\x1E\x1E\x1F\x1F\x20\x20\x20\x20\x1E\x1F\x1F\x20\x1F\x1F\x1F\x1F\x20\x1F\x1F\x1F\x20\x1F\x20\x20\x1F\x20\x20\x20" + "\x98\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97\x98\x98\x98\x98\x98\x97\x98\x97\x96\x92\x8F\x89\x85\x80\x78\x6A\x59\x42\x30" + "\x2C\x28\x23\x21\x22\x1F\x20\x1E\x20\x21\x1F\x1F\x1F\x20\x20\x1F\x1F\x1F\x20\x1F\x1F\x1E\x1D\x20\x1F\x21\x20\x1E\x1F\x1F\x1F\x1F" + "\x98\x97\x98\x98\x97\x98\x98\x97\x98\x98\x98\x98\x97\x98\x98\x98\x98\x98\x97\x97\x98\x98\x98\x97\x98\x98\x98\x97\x97\x97\x97\x91" + "\x7D\x69\x54\x46\x38\x2F\x27\x26\x23\x22\x1E\x1D\x1F\x1E\x1E\x20\x20\x20\x1E\x1E\x1E\x1D\x1E\x1F\x1E\x20\x21\x20\x21\x1E\x1C\x1F" + "\x98\x98\x98\x98\x97\x98\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98" + "\x97\x98\x96\x8E\x7E\x70\x62\x53\x41\x31\x27\x23\x22\x21\x1F\x20\x20\x20\x1F\x1E\x1E\x1E\x1F\x1F\x21\x1E\x1F\x1F\x1E\x1F\x20\x1F" + "\x98\x98\x98\x97\x98\x98\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x97\x98\x98\x98\x98\x98\x98\x98\x97\x98\x98\x98\x98\x98\x98\x98" + "\x97\x98\x98\x97\x97\x97\x95\x90\x88\x7C\x64\x45\x2D\x28\x25\x22\x21\x20\x1F\x1F\x1F\x1F\x20\x21\x22\x20\x20\x20\x20\x20\x1F\x20" + "\x98\x97\x97\x98\x98\x98\x97\x97\x98\x98\x97\x97\x97\x98\x98\x97\x97\x97\x97\x98\x98\x98\x98\x98\x97\x98\x98\x98\x97\x97\x98\x98" + "\x98\x98\x98\x98\x98\x97\x97\x98\x98\x97\x97\x97\x89\x6C\x4F\x3D\x30\x28\x24\x21\x1E\x1F\x1F\x1E\x1D\x1D\x1F\x1F\x1D\x1E\x20\x1F" + "\x96\x95\x94\x96\x98\x98\x98\x98\x98\x97\x98\x98\x98\x98\x98\x98\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97" + "\x98\x98\x98\x98\x98\x98\x98\x98\x97\x98\x98\x98\x98\x98\x94\x86\x6E\x59\x42\x2E\x27\x24\x22\x1F\x1E\x20\x20\x1E\x1D\x1E\x1F\x1E" + "\x4C\x4B\x4A\x4B\x4F\x55\x5B\x60\x69\x6F\x75\x7A\x7F\x86\x90\x97\x98\x98\x98\x98\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97" + "\x98\x98\x98\x98\x97\x97\x97\x97\x97\x97\x97\x98\x98\x98\x98\x98\x97\x92\x88\x7A\x5B\x3C\x29\x25\x22\x20\x20\x1F\x1C\x20\x21\x1F" + "\x24\x24\x23\x25\x24\x23\x23\x25\x25\x28\x2A\x2A\x2C\x2D\x2E\x35\x47\x55\x62\x6E\x7B\x82\x87\x8C\x92\x97\x98\x98\x98\x97\x97\x98" + "\x97\x98\x98\x98\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x97\x98\x98\x97\x97\x92\x78\x54\x39\x2B\x22\x1E\x1F\x20\x1F\x1E" + "\x20\x1F\x1E\x1E\x20\x1F\x1F\x1F\x1F\x1F\x21\x23\x20\x21\x21\x24\x24\x25\x27\x2A\x2D\x35\x40\x4A\x58\x66\x76\x83\x91\x97\x97\x98" + "\x98\x98\x98\x98\x98\x98\x97\x97\x98\x97\x97\x97\x97\x97\x97\x97\x98\x97\x98\x98\x98\x98\x98\x94\x7C\x61\x44\x2F\x28\x22\x20\x20" + "\x1F\x1D\x1F\x20\x1F\x1E\x1E\x1F\x1E\x1E\x21\x20\x20\x20\x1E\x1F\x1F\x1F\x1E\x21\x22\x22\x23\x24\x27\x2A\x35\x3E\x4A\x56\x6D\x82" + "\x94\x98\x97\x97\x98\x98\x98\x98\x98\x97\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x97\x94\x89\x74\x48\x2D\x26\x22" + "\x1F\x1E\x1E\x1F\x1E\x1F\x20\x20\x1F\x1F\x1F\x1F\x1F\x1F\x20\x1E\x1F\x1D\x1C\x1F\x20\x1F\x1F\x1F\x22\x21\x22\x26\x23\x25\x27\x2C" + "\x31\x46\x5E\x77\x88\x92\x97\x98\x98\x97\x97\x98\x97\x97\x98\x98\x97\x98\x98\x98\x98\x98\x98\x98\x98\x97\x98\x97\x96\x85\x5E\x42" + "\x1E\x1E\x1F\x20\x20\x20\x1F\x1D\x1E\x1F\x1D\x1F\x1F\x1F\x1F\x1F\x20\x21\x21\x20\x1F\x20\x20\x1E\x1F\x1F\x20\x20\x20\x20\x20\x21" + "\x24\x25\x29\x2D\x43\x59\x6E\x81\x93\x98\x98\x98\x98\x98\x98\x98\x97\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x98\x96\x88"; + +BYTE TEST_64X64_RED_PLANE_RLE[3739] = + "\xF0\x23\x1F\x1E\x1D\x1D\x1E\x1D\x1F\x23\x4A\x78\x71\x64\x58\x4B\xF0\x3E\x30\x29\x26\x24\x22\x21\x20\x20\x20\x1D\x1E\x20\x1E\x1F" + "\x86\x20\x20\x1F\x20\x21\x22\x1E\x1F\xF0\x1E\x1D\x1F\x20\x1F\x1F\x1F\x1E\x1E\x1F\x1F\x20\x1E\x1F\x1F\x50\x1F\x1E\x1D\x1E\x1C\xF0" + "\x38\x1A\x14\x0E\x08\x08\x06\x04\x04\x18\x38\x4A\x66\x74\x82\xF0\x90\x9E\x90\x76\x52\x2C\x16\x12\x0C\x06\x08\x06\x00\x02\x00\xF0" + "\x03\x03\x02\x00\x01\x03\x02\x02\x00\x02\x01\x01\x03\x00\x00\x33\x02\x04\x00\xD0\x02\x00\x00\x03\x01\x02\x00\x02\x01\x00\x04\x02" + "\x06\xF0\x94\xAE\x90\x6E\x46\x2E\x1E\x0C\x0E\x0A\x02\x00\x02\x0C\x18\xF0\x24\x30\x4C\x6C\x94\xBE\xBE\x9A\x6C\x48\x34\x1C\x10\x08" + "\x06\xF0\x06\x06\x00\x01\x03\x01\x02\x00\x02\x00\x02\x02\x08\x04\x02\xF0\x00\x03\x01\x00\x01\x02\x00\x06\x02\x04\x01\x01\x03\x03" + "\x00\x40\x00\x00\x04\x01\xD3\x02\x14\x3C\x68\x8E\x86\x78\x68\x3E\x16\x02\x02\x00\x14\x02\xF0\x1A\x44\x78\x92\x8C\x82\x6E\x52\x2E" + "\x14\x06\x02\x04\x00\x01\xF0\x01\x03\x00\x01\x01\x02\x00\x00\x03\x01\x01\x01\x04\x06\x00\xD0\x02\x00\x02\x00\x02\x02\x02\x00\x00" + "\x04\x00\x03\x02\x04\xAA\x0C\x2C\x54\x6E\x7C\x4C\x02\x00\x02\x00\xF0\x10\x2E\x50\x6C\x82\x92\x86\x50\x1C\x12\x0A\x04\x04\x02\x01" + "\xF0\x02\x02\x01\x05\x07\x00\x00\x01\x02\x03\x05\x03\x00\x01\x05\xA0\x01\x01\x01\x00\x02\x04\x00\x02\x00\x01\xE5\x00\x00\x02\x02" + "\x00\x00\x00\x04\x1A\x14\x00\x00\x01\x00\x35\x01\x01\x00\xF0\x06\x16\x2A\x52\x96\xBC\x84\x50\x28\x0C\x06\x04\x03\x03\x00\xF0\x04" + "\x04\x04\x02\x02\x02\x00\x02\x00\x00\x01\x02\x00\x02\x00\x70\x02\x04\x08\x01\x01\x02\x04\x04\x5C\x02\x02\x00\x02\x00\x14\x01\xF0" + "\x00\x00\x00\x02\x02\x02\x16\x58\x86\x84\x54\x1E\x06\x06\x04\xF0\x00\x03\x01\x00\x02\x02\x01\x02\x01\x04\x02\x01\x00\x02\x01\x80" + "\x02\x02\x01\x07\x03\x01\x01\x00\x04\x53\x01\x00\x00\x00\x01\x16\x00\xF0\x02\x00\x00\x01\x00\x02\x00\x01\x13\x37\x55\x67\x4B\x1D" + "\x03\xF0\x14\x44\x7A\x74\x28\x08\x01\x01\x02\x04\x00\x02\x00\x00\x01\xF0\x02\x01\x05\x00\x01\x04\x02\x00\x00\x00\x01\x00\x00\x03" + "\x03\x41\xF0\x04\x02\x02\x02\x03\x1F\x69\xA9\x9B\x7D\x69\x85\xAD\xA1\x47\xF0\x0B\x14\x5A\x8A\x32\x08\x04\x04\x04\x01\x03\x04\x01" + "\x00\x00\xE0\x02\x00\x04\x00\x07\x01\x00\x03\x03\x00\x02\x00\x00\x00\x04\x53\x02\x00\x00\x00\x02\x17\x00\xF0\x01\x00\x00\x00\x31" + "\x7F\x61\x11\x36\x4E\x58\x46\x16\x29\x79\xF0\x69\x0B\x00\x34\x68\x14\x06\x03\x07\x04\x02\x00\x02\x00\x00\xA3\x00\x06\x00\x00\x00" + "\x01\x01\x00\x06\x02\x10\x01\x71\xF0\x0D\x6F\x2F\x1A\x7A\x84\x78\x70\x7A\x88\x52\x09\x57\x53\x01\xF0\x00\x44\x42\x06\x06\x00\x04" + "\x04\x00\x01\x00\x02\x00\x00\x03\xB0\x04\x02\x04\x04\x00\x05\x00\x00\x00\x02\x02\x41\xF0\x01\x01\x07\x75\x2B\x2A\x86\x54\x1A\x0C" + "\x02\x04\x2A\x7A\x6E\xF0\x04\x5B\x3B\x00\x06\x68\x22\x04\x00\x03\x01\x01\x04\x01\x00\x83\x01\x03\x02\x01\x00\x01\x02\x00\x30\x02" + "\x00\x02\x51\xF0\x02\x21\x2F\x0C\x50\x2A\x02\x03\x29\x61\x73\x59\x1B\x4C\x32\xF0\x15\x3F\x09\x00\x20\x42\x08\x02\x00\x01\x00\x00" + "\x02\x01\x00\xD0\x02\x00\x00\x02\x01\x05\x02\x00\x00\x00\x01\x01\x01\x61\xF0\x1F\x0F\x14\x34\x04\x00\x0D\x55\x61\x55\x67\x4B\x0A" + "\x3C\x00\xF0\x15\x0D\x00\x0C\x3A\x18\x06\x02\x00\x01\x00\x02\x04\x02\x03\xC0\x04\x06\x00\x00\x02\x00\x00\x01\x00\x00\x02\x00\x11" + "\xF0\x01\x01\x01\x00\x00\x21\x0F\x0A\x1A\x00\x01\x0A\x32\x28\x24\xF0\x30\x2E\x06\x2C\x04\x13\x11\x02\x02\x32\x38\x08\x03\x01\x01" + "\xF0\x00\x02\x01\x03\x02\x00\x03\x00\x00\x01\x00\x01\x00\x03\x03\x20\x00\x02\x0E\xF0\x01\x00\x00\x02\x02\x02\x00\x00\x10\x06\x05" + "\x0B\x00\x00\x06\xF0\x4A\x88\x84\x80\x3C\x00\x0B\x03\x02\x02\x00\x00\x12\x30\x10\xF0\x02\x00\x02\x01\x05\x03\x02\x02\x03\x01\x00" + "\x06\x02\x01\x00\x50\x00\x04\x02\x00\x02\x0D\x24\x01\x00\xF0\x02\x00\x00\x1C\x0E\x0F\x1D\x00\x02\x02\x06\x1A\x2E\x16\x02\xF0\x02" + "\x31\x00\x16\x16\x00\x00\x02\x30\x18\x02\x02\x00\x04\x02\xF0\x04\x00\x00\x01\x00\x00\x00\x02\x02\x06\x04\x01\x01\x00\x05\x0C\xF0" + "\x03\x0F\x05\x01\x00\x00\x01\x03\x00\x01\x24\x1E\x15\x57\x15\xF0\x01\x00\x00\x00\x02\x00\x01\x37\x4B\x04\x2A\x12\x00\x00\x00\xF0" + "\x2C\x26\x0A\x00\x02\x07\x03\x05\x02\x00\x02\x00\x00\x03\x01\x70\x01\x00\x00\x02\x04\x01\x06\x33\x00\x01\x00\xF0\x01\x00\x00\x00" + "\x01\x00\x03\x31\x57\x1F\x00\x00\x02\x02\x00\x83\x00\x1C\x56\x02\x3B\x5F\x17\x00\xF0\x05\x33\x65\x19\x2C\x42\x02\x00\x02\x00\x04" + "\x38\x02\x02\x01\xF0\x02\x01\x02\x00\x00\x01\x01\x00\x01\x00\x00\x01\x05\x02\x02\x20\x02\x01\x39\x00\x02\x00\xF0\x06\x12\x3D\x83" + "\x4B\x07\x00\x00\x02\x02\x02\x44\x44\x0B\x57\xF0\x83\x51\x29\x1D\x31\x67\x7D\x35\x08\x6A\x22\x00\x00\x01\x00\xF0\x00\x3A\x0C\x06" + "\x00\x00\x06\x04\x02\x00\x02\x02\x01\x00\x01\x70\x00\x00\x01\x03\x01\x00\x00\x0C\xF0\x02\x2A\x28\x2D\x8B\x9B\x43\x15\x01\x00\x00" + "\x08\x80\x72\x16\xF0\x39\x83\xA9\xB3\xA1\x67\x17\x3A\x8E\x38\x02\x00\x00\x00\x01\xF0\x01\x22\x24\x00\x00\x02\x03\x01\x03\x03\x00" + "\x00\x02\x04\x01\x70\x02\x04\x06\x02\x00\x00\x00\x58\x02\x00\x00\x02\x00\xF0\x08\x3A\x16\x07\x3B\x87\x7D\x45\x11\x00\x00\x0E\x5A" + "\x7C\x64\x94\x3E\x18\x16\x28\x4A\x70\x80\x3C\x00\x83\x02\x01\x2D\x0B\x00\x02\x00\x02\xC0\x04\x02\x00\x00\x06\x00\x03\x01\x01\x02" + "\x00\x02\x24\x02\x00\xF0\x02\x00\x00\x00\x02\x00\x00\x00\x32\x52\x10\x02\x17\x4D\x7F\xF0\x85\x4B\x09\x00\x08\x3A\x6C\x80\x94\x94" + "\x8C\x78\x56\x1A\x02\x13\x00\xF0\x01\x17\x67\x85\x27\x03\x00\x00\x00\x01\x00\x02\x03\x00\x02\x90\x02\x01\x02\x01\x01\x04\x01\x00" + "\x01\x68\x00\x02\x02\x00\x02\x00\xF0\x08\x6A\xB6\x76\x40\x18\x17\x49\x8D\xB1\x6D\x33\x15\x02\x18\xF0\x28\x2A\x20\x12\x04\x01\x00" + "\x00\x01\x09\x1D\x69\xA7\x71\x35\xF0\x07\x03\x01\x01\x04\x02\x00\x02\x00\x01\x00\x03\x01\x00\x02\x50\x04\x01\x00\x00\x00\x77\x02" + "\x02\x00\x02\x00\x02\x00\xF0\x02\x02\x1A\x64\x88\x78\x46\x16\x03\x25\x6F\x93\x83\x67\x3F\xF0\x29\x13\x01\x02\x02\x00\x05\x23\x45" + "\x67\x89\x6D\x1B\x06\x1C\x53\x00\x01\x02\x00\x01\xC0\x00\x03\x01\x00\x00\x01\x02\x02\x01\x02\x02\x01\x07\x28\x02\x00\xF0\x04\x1C" + "\x50\x82\x8E\x5C\x20\x06\x15\x43\x6F\x85\x8D\x91\x8D\xF0\x81\x8B\x93\x95\x8D\x7F\x69\x33\x08\x3E\x86\x80\x08\x02\x01\xF0\x02\x02" + "\x00\x01\x01\x02\x04\x02\x00\x00\x01\x00\x00\x01\x01\x20\x01\x02\x4D\x02\x02\x02\x00\x03\xF0\x18\x3C\x82\xBA\x96\x5E\x34\x12\x19" + "\x27\x3B\x4F\x5F\x4F\x45\xF0\x3F\x25\x02\x30\x5C\xAC\x9C\x50\x09\x06\x02\x02\x02\x03\x00\xE0\x02\x03\x01\x00\x01\x02\x02\x03\x05" + "\x03\x02\x00\x00\x02\x7C\x02\x00\x02\x02\x00\x02\x00\xF0\x02\x00\x02\x00\x08\x42\x7C\x8E\x84\x74\x62\x4C\x34\x22\x2A\xF0\x3C\x4E" + "\x6C\x7E\x88\x7A\x26\x02\x09\x39\x03\x02\x00\x01\x00\xF0\x00\x02\x00\x02\x02\x00\x00\x03\x02\x02\x00\x04\x00\x02\x00\x04\x23\x02" + "\x00\x2E\x02\x00\xF0\x04\x1E\x48\x68\x72\x80\x8C\x98\x90\x86\x7C\x66\x48\x24\x06\xF0\x00\x00\x39\x31\x05\x05\x00\x01\x01\x00\x00" + "\x06\x01\x05\x01\x63\x02\x00\x04\x00\x00\x01\x33\x02\x02\x00\x2F\x02\x00\x05\xF0\x04\x0C\x14\x1E\x28\x22\x1A\x12\x06\x00\x01\x00" + "\x19\x5D\x77\xF0\x1B\x05\x00\x03\x01\x04\x02\x01\x01\x00\x04\x02\x03\x00\x00\x60\x04\x01\x02\x02\x04\x02\x23\x00\x02\x1F\x00\x0F" + "\xF0\x01\x01\x13\x49\x7B\x75\x29\x0B\x03\x00\x01\x00\x00\x03\x01\xD0\x01\x01\x02\x02\x04\x04\x01\x05\x02\x00\x02\x05\x01\x29\x02" + "\x00\x26\x02\x00\x23\x01\x00\x33\x01\x01\x00\xF0\x02\x02\x02\x00\x02\x00\x13\x5D\x8B\x81\x4D\x15\x03\x01\x03\xF0\x01\x02\x06\x00" + "\x02\x00\x04\x01\x03\x03\x03\x05\x00\x00\x01\x40\x01\x00\x00\x01\xF4\x02\x02\x01\x00\x00\x00\x01\x01\x02\x00\x00\x00\x02\x01\x00" + "\xF0\x02\x00\x00\x00\x02\x00\x00\x01\x00\x01\x01\x01\x09\x1B\x29\xF0\x57\x93\xB9\x7D\x45\x1F\x0B\x07\x03\x00\x06\x04\x00\x00\x06" + "\xF0\x02\x04\x00\x01\x00\x02\x00\x06\x02\x06\x04\x01\x00\x06\x00\xF0\x00\x00\x02\x00\x01\x11\x29\x2F\x3F\x3F\x33\x29\x1D\x11\x0B" + "\x26\x03\x00\xF0\x03\x02\x00\x0B\x19\x2B\x3F\x5D\x6F\x81\x95\x83\x4D\x1B\x11\xF0\x07\x03\x01\x03\x03\x01\x00\x00\x02\x00\x05\x03" + "\x00\x00\x06\xB0\x00\x00\x04\x01\x01\x05\x03\x02\x01\x03\x00\xF0\x00\x02\x02\x00\x55\x85\x87\x89\x87\x87\x8B\x8D\x8F\x95\x99\xF0" + "\x93\x85\x77\x65\x55\x59\x5F\x65\x6B\x7D\x91\x93\x91\x89\x85\x73\x7B\x67\x4D\x29\x11\x0D\x00\xF0\x04\x04\x04\x02\x00\x00\x01\x02" + "\x02\x00\x01\x00\x02\x02\x02\x90\x01\x02\x00\x01\x03\x02\x00\x00\x02\xF0\x00\x00\x05\x6D\x77\x4D\x3B\x2F\x29\x27\x29\x31\x3D\x3F" + "\x3F\xF0\x4D\x5F\x6D\x81\x8B\x85\x83\x7B\x75\x6B\x53\x47\x3F\x35\x25\xC3\x15\x0B\x05\x07\x07\x01\x05\x01\x03\x01\x01\x02\x93\x00" + "\x02\x00\x01\x02\x02\x00\x02\x00\x70\x04\x06\x06\x05\x00\x02\x00\xF0\x04\x02\x2D\x47\x13\x05\x01\x05\x01\x00\x03\x07\x07\x05\x09" + "\xF0\x0B\x0D\x09\x07\x0F\x0F\x09\x0D\x0B\x05\x09\x05\x05\x01\x07\xF0\x03\x03\x03\x00\x02\x04\x06\x02\x01\x01\x00\x02\x00\x03\x01" + "\x75\x03\x03\x00\x00\x00\x01\x00\x70\x02\x02\x01\x04\x00\x01\x00\xF0\x00\x01\x33\x1D\x05\x03\x00\x04\x00\x00\x00\x04\x00\x03\x01" + "\xF0\x00\x02\x00\x00\x01\x01\x01\x00\x05\x00\x00\x00\x02\x01\x04\xF0\x02\x00\x02\x00\x00\x05\x05\x00\x04\x02\x03\x01\x05\x01\x00" + "\xF0\x04\x04\x01\x00\x00\x02\x02\x05\x00\x00\x02\x00\x03\x02\x02\x40\x00\x00\x00\x03\xF0\x00\x0B\x1B\x07\x01\x00\x02\x01\x02\x00" + "\x01\x01\x00\x02\x00\xF0\x02\x06\x02\x00\x04\x00\x00\x00\x04\x01\x00\x00\x02\x04\x02\xF0\x04\x02\x00\x04\x01\x01\x02\x01\x02\x01" + "\x01\x01\x00\x02\x00\xF0\x01\x00\x03\x00\x00\x01\x03\x00\x03\x03\x00\x02\x00\x03\x01\x40\x00\x02\x00\x04\xF0\x00\x03\x05\x01\x03" + "\x04\x04\x00\x01\x01\x01\x03\x00\x00\x02\xF0\x00\x01\x01\x00\x01\x00\x03\x03\x02\x01\x01\x00\x01\x01\x01\xF0\x00\x00\x02\x03\x01" + "\x02\x00\x02\x01\x02\x06\x02\x04\x02\x00\xF3\x00\x01\x00\x00\x02\x02\x00\x02\x02\x00\x03\x03\x01\x01\x00\x10\x01\xF0\x02\x03\x03" + "\x00\x01\x03\x05\x00\x02\x00\x02\x02\x02\x04\x01\xF0\x05\x03\x00\x02\x00\x05\x00\x02\x01\x00\x04\x01\x00\x02\x02\x83\x00\x04\x00" + "\x02\x06\x04\x03\x00\xB3\x03\x03\x07\x05\x00\x02\x00\x02\x01\x01\x02\x90\x06\x00\x04\x00\x00\x01\x00\x02\x00\xF0\x02\x0A\x06\x00" + "\x08\x04\x04\x02\x04\x04\x02\x00\x02\x02\x04\xF0\x08\x00\x01\x03\x00\x04\x02\x02\x00\x06\x00\x02\x02\x03\x00\xF0\x00\x05\x01\x03" + "\x01\x03\x01\x03\x00\x02\x01\x00\x01\x02\x02\xF0\x00\x02\x02\x01\x00\x01\x03\x02\x00\x00\x03\x02\x01\x00\x00\x40\x00\x03\x01\x04" + "\xF0\x00\x06\x0C\x02\x01\x03\x00\x02\x05\x01\x00\x04\x05\x03\x03\xB3\x03\x02\x00\x01\x01\x02\x01\x01\x00\x01\x00\xB3\x02\x01\x00" + "\x00\x01\x00\x02\x02\x00\x00\x02\xF0\x04\x06\x02\x03\x00\x01\x01\x00\x04\x05\x01\x00\x01\x00\x00\x60\x02\x02\x00\x04\x00\x03\xF0" + "\x00\x08\x20\x0E\x03\x00\x00\x02\x04\x02\x00\x02\x02\x03\x00\x73\x07\x07\x02\x02\x06\x01\x00\xF0\x03\x01\x01\x00\x00\x02\x02\x04" + "\x06\x04\x00\x04\x04\x02\x05\xF0\x00\x00\x02\x01\x03\x00\x04\x01\x00\x02\x00\x05\x00\x02\x02\x63\x00\x00\x02\x01\x01\x00\xF0\x00" + "\x02\x28\x12\x06\x08\x04\x01\x00\x03\x01\x03\x01\x02\x02\xF0\x08\x02\x05\x01\x01\x02\x02\x00\x01\x01\x04\x02\x01\x00\x03\xF0\x01" + "\x03\x03\x02\x05\x03\x01\x01\x00\x04\x03\x01\x00\x02\x00\xF0\x01\x01\x00\x02\x00\x02\x04\x06\x02\x00\x01\x04\x00\x02\x04\x13\x00" + "\xF0\x02\x00\x24\x12\x04\x00\x01\x00\x00\x06\x00\x03\x02\x00\x00\xF0\x00\x06\x06\x04\x04\x00\x02\x04\x02\x01\x01\x02\x00\x00\x02" + "\xF0\x00\x00\x03\x07\x02\x04\x01\x00\x00\x01\x00\x02\x02\x06\x06\xF0\x00\x05\x01\x00\x02\x02\x00\x03\x01\x03\x00\x05\x03\x01\x00" + "\x40\x00\x03\x00\x02\xF0\x00\x00\x12\x24\x08\x02\x00\x04\x02\x03\x00\x04\x01\x00\x03\xF0\x00\x01\x01\x01\x07\x01\x01\x01\x00\x02" + "\x01\x01\x02\x01\x01\xF0\x02\x02\x04\x06\x02\x00\x02\x02\x00\x00\x06\x02\x01\x01\x00\x63\x04\x04\x02\x04\x02\x01\xA0\x00\x04\x01" + "\x01\x00\x01\x00\x01\x00\x02\xF0\x00\x02\x06\x50\x0A\x02\x01\x07\x01\x02\x04\x00\x04\x04\x00\xF0\x03\x00\x01\x00\x06\x04\x00\x01" + "\x00\x02\x04\x00\x03\x06\x02\xF0\x01\x00\x03\x03\x01\x00\x00\x01\x01\x01\x03\x00\x02\x01\x01\x63\x03\x02\x00\x03\x00\x02\xA0\x00" + "\x04\x06\x02\x02\x01\x04\x04\x00\x05\xF0\x02\x00\x00\x34\x2A\x04\x02\x00\x02\x02\x03\x00\x01\x03\x06\xF0\x06\x02\x02\x00\x03\x03" + "\x02\x06\x02\x01\x00\x00\x01\x00\x00\xF0\x00\x01\x02\x04\x01\x01\x02\x00\x00\x02\x01\x02\x00\x01\x03\xF0\x01\x00\x06\x00\x03\x01" + "\x02\x02\x00\x02\x01\x00\x04\x00\x01\x40\x01\x00\x01\x01\x03\xF0\x08\x54\x14\x10\x10\x10\x0A\x0C\x0C\x08\x0A\x08\x06\x02\x06\xF0" + "\x02\x06\x02\x06\x01\x05\x01\x00\x00\x02\x05\x03\x00\x02\x02\xF0\x00\x06\x04\x00\x00\x00\x01\x00\x03\x00\x03\x00\x02\x00\x03\xF0" + "\x02\x02\x00\x00\x00\x01\x02\x05\x01\x00\x00\x02\x00\x00\x00\x10\x04\x04\xF0\x52\x92\x8E\x88\x78\x72\x6C\x62\x58\x52\x4C\x4C\x48" + "\x38\x30\xF0\x22\x16\x0E\x10\x12\x12\x06\x06\x06\x04\x02\x01\x00\x03\x01\xF0\x03\x05\x05\x01\x01\x00\x06\x02\x00\x02\x06\x00\x00" + "\x00\x03\xF0\x00\x00\x01\x01\x02\x00\x02\x02\x03\x04\x04\x02\x00\x02\x04\x03\xF0\x02\x0A\x40\x52\x5C\x68\x72\x7A\x82\x92\x94\x94" + "\x94\x90\x8E\xF0\x8A\x82\x80\x70\x60\x56\x3E\x30\x1A\x14\x14\x10\x0C\x0A\x0A\xF0\x02\x00\x04\x02\x00\x02\x02\x01\x00\x00\x04\x05" + "\x02\x00\x02\xF0\x02\x01\x00\x00\x02\x00\x01\x02\x00\x02\x01\x00\x01\x02\x02\x10\x00\x39\x00\x01\x00\xF0\x01\x02\x06\x0A\x16\x26" + "\x34\x48\x5A\x68\x72\x7C\x86\x94\xA0\xF0\x9A\x80\x64\x3C\x18\x14\x10\x08\x04\x08\x02\x02\x01\x00\x02\xF0\x01\x01\x02\x02\x02\x01" + "\x00\x00\x02\x00\x01\x01\x03\x02\x01\x70\x04\x00\x03\x00\x01\x01\x01\x04\x5A\x01\x00\x00\x01\x00\xF0\x01\x02\x04\x0C\x10\x1E\x26" + "\x30\x3E\x5A\x7C\xAA\xC2\xA2\x82\xF0\x62\x4A\x2C\x20\x0E\x10\x06\x02\x01\x03\x00\x03\x03\x02\x02\xF0\x02\x03\x01\x01\x01\x02\x01" + "\x01\x01\x02\x04\x04\x01\x05\x00\x33\x00\x02\x00\x24\x01\x00\x29\x02\x00\x53\x02\x00\x00\x00\x02\xF0\x0E\x34\x5E\x84\x90\x8C\x82" + "\x76\x5A\x3C\x1E\x12\x0C\x06\x06\xF0\x02\x00\x00\x00\x02\x00\x00\x02\x02\x00\x06\x03\x03\x01\x05\x30\x02\x08\x00\x03\x3A\x01\x02" + "\x00\x53\x01\x00\x02\x02\x00\x28\x01\x00\xF0\x04\x12\x32\x4E\x66\x7A\x8E\x96\x7A\x44\x16\x0E\x0C\x04\x02\x33\x00\x00\x02\x90\x04" + "\x02\x04\x02\x02\x04\x02\x01\x02\x55\x00\x01\x01\x02\x00\xA8\x01\x01\x01\x00\x00\x01\x00\x01\x01\x00\xF0\x01\x01\x00\x00\x02\x00" + "\x00\x02\x02\x00\x04\x10\x20\x36\x66\xF0\xA4\xB8\x88\x54\x36\x1E\x10\x0A\x04\x01\x00\x01\x05\x09\x05\x60\x01\x01\x05\x03\x02\x01" + "\xF0\x03\x03\x05\x03\x00\x00\x02\x02\x00\x01\x02\x02\x02\x00\x00\x54\x02\x00\x00\x02\x00\x94\x02\x00\x00\x00\x02\x02\x00\x01\x00" + "\xF0\x02\x02\x00\x01\x02\x02\x02\x1E\x58\x8A\x92\x7C\x62\x3C\x1A\xC0\x12\x0A\x06\x02\x02\x06\x02\x01\x00\x00\x01\x01\xF0\x93\x93" + "\x93\x95\x91\x85\x79\x6F\x5D\x4F\x45\x3B\x31\x23\x0F\x8D\x01\x02\x02\x00\x00\x01\x01\x00\x13\x01\xF0\x00\x01\x01\x00\x00\x00\x08" + "\x24\x52\x72\x8C\x98\x68\x30\x0E\x90\x0C\x08\x00\x00\x02\x01\x04\x04\x02\xF0\x4F\x4D\x4D\x4B\x55\x63\x6F\x75\x87\x8D\x95\x9F\xA5" + "\xB1\xC3\xF0\xC3\xA1\x85\x6B\x53\x37\x29\x21\x17\x0B\x01\x00\x00\x00\x01\x49\x01\x02\x01\x00\x14\x01\xF0\x00\x0C\x20\x3A\x78\xAC" + "\x9E\x5E\x2E\x16\x04\x01\x06\x00\x03\x10\x01\xF0\x07\x09\x09\x0D\x07\x07\x07\x0B\x0B\x11\x11\x0D\x17\x17\x19\xF0\x21\x45\x5F\x75" + "\x87\x9B\x99\x8D\x83\x73\x61\x43\x29\x0D\x00\xC6\x00\x00\x02\x00\x00\x00\x02\x02\x00\x00\x02\x00\xF0\x02\x01\x00\x02\x02\x0C\x40" + "\x80\x86\x6C\x44\x22\x12\x04\x02\x10\x04\xF0\x01\x03\x02\x04\x01\x01\x01\x00\x01\x01\x00\x05\x00\x01\x05\xF0\x09\x09\x0B\x11\x11" + "\x15\x25\x39\x4B\x61\x77\x81\x89\x8D\x81\xE4\x53\x2B\x07\x00\x01\x01\x00\x00\x02\x02\x00\x00\x00\x02\x34\x00\x02\x00\x90\x08\x36" + "\x66\x8A\x8A\x40\x16\x0C\x04\xF0\x00\x02\x01\x01\x01\x02\x04\x02\x02\x02\x03\x01\x01\x01\x04\x33\x01\x00\x03\xF0\x05\x07\x09\x09" + "\x11\x25\x2F\x4D\x61\x8B\xAB\xC5\xA3\x71\x3F\x44\x1F\x0B\x01\x00\x66\x01\x01\x00\x00\x01\x00\x80\x02\x06\x1E\x46\x9C\xB0\x70\x40" + "\xF0\x01\x00\x02\x02\x04\x02\x01\x05\x01\x00\x03\x00\x00\x00\x01\xF0\x02\x02\x08\x0A\x02\x01\x02\x02\x01\x05\x03\x03\x0B\x05\x09" + "\xF0\x0D\x15\x19\x41\x69\x93\x89\x71\x51\x2D\x09\x02\x02\x00\x02\x2A\x02\x00\x70\x02\x00\x02\x04\x26\x70\x8C"; + +BYTE TEST_64X64_GREEN_PLANE[4096] = + "\x2A\x25\x23\x23\x23\x23\x23\x24\x2B\x60\xA2\x97\x84\x75\x62\x50\x3C\x34\x30\x2C\x29\x28\x26\x25\x25\x23\x23\x25\x24\x24\x25\x25" + "\x25\x25\x26\x27\x25\x24\x25\x25\x27\x24\x24\x24\x24\x24\x25\x25\x24\x25\x26\x25\x23\x24\x24\x25\x23\x24\x24\x24\x24\x22\x23\x21" + "\x51\x37\x31\x2C\x29\x28\x27\x27\x2F\x73\xC9\xCC\xCB\xC5\xBD\xB5\xAA\x99\x80\x66\x48\x38\x33\x2E\x29\x27\x27\x26\x25\x25\x23\x23" + "\x26\x26\x25\x27\x25\x25\x25\x26\x23\x24\x23\x25\x24\x23\x26\x25\x25\x25\x25\x24\x23\x25\x23\x24\x24\x24\x25\x24\x26\x24\x24\x24" + "\xBA\xB3\x97\x79\x58\x48\x3A\x31\x37\x79\xCA\xCC\xCD\xCD\xCD\xCD\xCC\xCC\xCC\xCC\xCC\xBC\x9D\x7A\x5C\x4C\x3D\x30\x2B\x29\x28\x27" + "\x25\x24\x24\x23\x25\x26\x25\x25\x23\x24\x27\x27\x24\x24\x24\x24\x25\x25\x25\x25\x26\x27\x25\x23\x23\x23\x25\x24\x22\x24\x26\x26" + "\xBB\xBE\xC0\xC2\xBC\xA7\x90\x78\x63\x87\xCB\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xC2\xAD\x96\x7E\x63\x48\x36\x2C" + "\x27\x27\x24\x24\x25\x24\x25\x24\x22\x25\x26\x26\x23\x23\x23\x23\x27\x26\x25\x26\x26\x26\x24\x24\x24\x24\x23\x22\x25\x24\x24\x24" + "\xBB\xBE\xC0\xC2\xC5\xC7\xC8\xC3\xBA\xBD\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xC8\xBD\xB0\x93\x65" + "\x3A\x33\x2A\x28\x27\x24\x23\x25\x24\x24\x23\x24\x24\x22\x21\x24\x24\x23\x23\x25\x26\x25\x25\x23\x23\x25\x24\x24\x26\x25\x24\x24" + "\xBB\xBE\xC0\xC3\xC4\xC7\xC8\xC8\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCC" + "\xBE\x90\x62\x42\x30\x28\x27\x22\x23\x24\x25\x25\x25\x25\x24\x24\x25\x25\x24\x26\x24\x24\x23\x23\x25\x25\x27\x29\x24\x24\x25\x26" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCC\xC0\x9D\x6A\x3F\x2E\x27\x24\x24\x24\x24\x25\x24\x24\x24\x25\x24\x25\x27\x24\x25\x25\x24\x25\x25\x26\x25\x23\x23\x25\x26" + "\xBB\xBE\xC0\xC3\xC4\xC7\xC8\xC9\xCB\xCA\xCC\xCB\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCC\xCC\xCD\xCC\xCC\xC1\xA8\x92\x86\x99" + "\xB9\xCB\xCD\xCD\xBF\x8F\x47\x2E\x26\x25\x24\x25\x24\x26\x25\x25\x24\x25\x25\x24\x23\x24\x26\x24\x25\x27\x25\x24\x24\x23\x23\x24" + "\xBB\xBE\xC0\xC3\xC4\xC7\xC8\xC9\xCA\xCA\xCC\xCB\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCA\xB6\x83\x48\x3B\x39\x3B\x3B" + "\x40\x58\x9B\xC5\xCD\xCD\xA9\x52\x2B\x26\x26\x27\x25\x23\x26\x24\x24\x25\x25\x23\x25\x23\x22\x23\x25\x24\x24\x24\x24\x22\x23\x25" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xA8\x5D\x3D\x3D\x5F\x71\x79\x6C" + "\x50\x3D\x47\x7A\xC5\xCD\xCC\x98\x37\x2A\x26\x24\x25\x25\x27\x25\x24\x24\x25\x26\x25\x23\x23\x22\x23\x23\x26\x26\x24\x24\x24\x23" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xC3\x59\x3B\x50\x92\xBB\xC4\xC7\xC1" + "\xAE\x75\x40\x3D\x8C\xCC\xCD\xC8\x65\x30\x28\x25\x27\x26\x26\x25\x24\x25\x25\x26\x24\x25\x25\x25\x24\x23\x24\x25\x24\x24\x24\x23" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xC7\x70\x3B\x59\xAE\xCC\xCD\xCC\xC9\xC4" + "\xCB\xCA\x8A\x41\x4C\xA1\xCC\xCD\xB0\x45\x2B\x23\x25\x25\x26\x24\x23\x25\x24\x24\x24\x25\x24\x23\x26\x25\x23\x24\x24\x24\x23\x24" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xAF\x50\x42\x90\xCA\xCD\xCB\xB0\x84\x74" + "\x8B\xB7\xC0\x64\x3C\x76\xC7\xCD\xC3\x74\x31\x26\x25\x25\x26\x26\x24\x25\x25\x25\x24\x26\x26\x23\x23\x26\x24\x26\x25\x24\x23\x23" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\x98\x44\x51\xB4\xCD\xCD\xC0\x73\x3E\x38" + "\x42\x83\xC8\x8F\x3A\x66\xBB\xCD\xCB\x9C\x42\x29\x27\x26\x24\x25\x26\x27\x26\x23\x26\x28\x25\x22\x24\x25\x24\x26\x25\x24\x24\x23" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCD\xCD\x80\x39\x58\xC7\xCD\xCC\xC9\x96\x5B\x51" + "\x67\xA3\xCD\xAC\x3D\x58\xAF\xCD\xCD\xBF\x69\x30\x25\x23\x25\x25\x26\x25\x25\x25\x26\x26\x26\x24\x24\x25\x23\x24\x22\x23\x24\x24" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCD\xCC\xCD\x8C\x40\x55\xBE\xCD\xCC\xCC\xC9\xBA\xAD" + "\xC0\xCC\xCD\xA5\x3B\x5A\xB1\xCD\xCD\xCC\x8A\x3B\x28\x24\x25\x25\x23\x24\x25\x26\x25\x26\x24\x25\x25\x24\x24\x24\x24\x23\x24\x25" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\x9E\x47\x4A\xA9\xCD\xCD\xCD\xCD\xCC\xCC" + "\xCE\xCD\xCD\x80\x3B\x6A\xBF\xCD\xCD\xCD\xAC\x4B\x2A\x26\x25\x26\x23\x26\x25\x25\x23\x24\x25\x25\x25\x25\x25\x26\x24\x23\x24\x22" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCB\xCB\xCC\xCC\xCA\xC1\xC7\xCC\xCD\xCD\xCD\xCD\xCC\xCD\xBA\x5E\x39\x6B\xBE\xCC\xCD\xCD\xCD\xCD" + "\xCD\xCB\xA8\x4D\x3F\x86\xCC\xCD\xCD\xCD\xC9\x63\x2D\x27\x26\x23\x22\x24\x26\x25\x25\x25\x23\x23\x24\x23\x26\x26\x24\x25\x23\x24" + "\xBA\xBD\xC0\xC2\xC4\xC6\xC8\xC8\xCA\xCB\xCB\xCC\xC8\x9E\x8B\xB5\xCD\xCD\xCD\xCD\xCC\xCC\xCD\x9A\x3B\x42\x7C\xBB\xCD\xCD\xCD\xCD" + "\xC9\xA9\x5F\x3A\x5D\xB4\xCD\xCD\xCC\xCC\xCD\x8C\x30\x27\x26\x23\x22\x23\x25\x25\x23\x23\x25\x23\x24\x24\x25\x23\x25\x25\x24\x24" + "\xBB\xBE\xC0\xC3\xC4\xC6\xC8\xC9\xCA\xCB\xCB\xCC\xCC\xAA\x5E\x59\x99\xC8\xCD\xCD\xCD\xCD\xCD\xC8\x6C\x3A\x3F\x5F\x94\xB1\xB9\xAA" + "\x80\x50\x3A\x40\xA7\xCC\xCD\xCD\xCD\xCD\xCD\xB6\x38\x2A\x25\x23\x24\x26\x26\x25\x26\x24\x23\x22\x24\x24\x24\x23\x24\x24\x24\x24" + "\xBB\xBE\xC0\xC3\xC5\xC7\xC8\xC9\xCA\xCB\xCB\xCC\xCD\xC7\x7B\x3B\x37\x5B\x9E\xBF\xCC\xCD\xCD\xCD\xC4\x89\x4D\x37\x39\x3A\x3C\x3B" + "\x39\x41\x60\xA2\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\x52\x2C\x27\x24\x22\x25\x24\x23\x24\x25\x25\x24\x22\x24\x26\x25\x24\x23\x23\x24" + "\xBC\xBF\xC1\xC4\xC5\xC7\xC8\xC9\xCA\xCB\xCB\xCC\xCD\xCD\xA4\x4B\x30\x32\x40\x67\x9B\xC0\xCD\xCD\xCD\xC8\xA5\x7D\x62\x4C\x49\x57" + "\x6B\x8D\xBB\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCA\xAC\x4A\x2A\x25\x25\x23\x25\x25\x25\x26\x26\x24\x25\x25\x25\x24\x25\x23\x25\x25\x25" + "\xBC\xBF\xC1\xC4\xC5\xC7\xC8\xC9\xCA\xCB\xCC\xCC\xCD\xCD\xC6\x82\x3C\x34\x2F\x30\x42\x63\x98\xC6\xCD\xCD\xCC\xC7\xBC\xB2\xB0\xB7" + "\xC0\xC9\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xBC\x81\x4E\x2C\x27\x27\x25\x23\x24\x26\x25\x25\x26\x25\x26\x25\x25\x23\x24\x25\x24\x24\x25" + "\xBD\xC0\xC2\xC4\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCC\xCD\xCD\xCD\xCC\xBA\x85\x59\x40\x31\x31\x36\x4B\x82\xA8\xBC\xC9\xCD\xCD\xCD\xCD" + "\xCC\xCC\xCC\xCD\xCD\xCD\xC8\xB8\x82\x47\x34\x2B\x27\x25\x26\x25\x24\x24\x26\x26\x25\x25\x25\x23\x23\x25\x25\x27\x24\x24\x24\x26" + "\xBE\xC0\xC3\xC4\xC6\xC8\xC8\xC9\xCA\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCB\xBA\x94\x64\x3F\x33\x32\x35\x43\x61\x81\x9F\xB0\xC0\xCB" + "\xCD\xCD\xCC\xC9\xB7\x9B\x7E\x5A\x36\x34\x37\x3D\x29\x25\x25\x26\x24\x25\x24\x25\x24\x23\x24\x22\x22\x25\x25\x27\x23\x25\x25\x25" + "\xBF\xC1\xC3\xC5\xC6\xC8\xC8\xCA\xCA\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xBD\xA2\x72\x47\x36\x34\x33\x34\x43\x4D\x5A\x67" + "\x73\x6B\x66\x61\x53\x42\x35\x34\x3B\x5F\x95\x97\x2E\x25\x24\x25\x25\x25\x24\x24\x25\x24\x25\x23\x23\x24\x25\x26\x22\x25\x25\x25" + "\xC0\xC2\xC4\xC5\xC7\xC8\xC9\xCA\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCC\xCD\xC7\x9E\x75\x57\x40\x33\x33\x32\x32" + "\x31\x32\x34\x34\x37\x42\x55\x74\xB2\xCC\xCC\x90\x32\x25\x24\x25\x23\x24\x26\x23\x25\x25\x25\x24\x24\x21\x21\x24\x23\x25\x25\x26" + "\xC0\xC2\xC4\xC6\xC7\xC8\xC9\xCA\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCA\xB8\x9C\x83\x76\x66\x56" + "\x4A\x51\x5E\x6B\x83\x9B\xB5\xC9\xCD\xCD\xC5\x66\x2E\x26\x25\x25\x25\x25\x25\x23\x25\x25\x25\x24\x23\x22\x24\x24\x26\x27\x25\x25" + "\xC1\xC3\xC4\xC6\xC8\xC8\xC9\xCA\xCB\xCC\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCB\xC5\xBE\xB7" + "\xB2\xB5\xBB\xC1\xCA\xCD\xCD\xCD\xCD\xCD\x9C\x44\x27\x25\x26\x26\x22\x23\x25\x24\x24\x23\x25\x25\x24\x24\x25\x24\x24\x25\x25\x24" + "\xC2\xC4\xC5\xC7\xC8\xC9\xCA\xCA\xCB\xCC\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xBC\x8C\x48\x2D\x25\x24\x24\x22\x25\x26\x24\x24\x25\x24\x25\x24\x22\x25\x26\x24\x25\x24\x26\x26" + "\xC3\xC4\xC6\xC8\xC8\xC9\xCA\xCB\xCB\xCC\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC" + "\xCD\xCD\xCD\xCD\xCC\xCC\xBE\x9A\x63\x39\x2B\x26\x23\x24\x24\x26\x24\x25\x25\x23\x23\x25\x25\x25\x25\x25\x24\x24\x25\x24\x23\x24" + "\xC4\xC5\xC7\xC8\xC8\xC9\xCA\xCB\xCB\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCC\xCD\xCD\xCD\xBE\x8C\x5D\x40\x2C\x2A\x29\x26\x22\x23\x24\x25\x24\x24\x26\x25\x24\x23\x23\x23\x22\x23\x23\x24\x23\x25\x24\x24" + "\xC4\xC6\xC7\xC8\xC9\xC9\xC9\xCA\xCC\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCE\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCD\xCD\xCC\xCC\xC7" + "\xBC\xAF\x92\x67\x3B\x32\x2D\x28\x26\x25\x26\x25\x25\x26\x25\x25\x27\x25\x25\x25\x23\x23\x24\x23\x24\x25\x25\x25\x23\x25\x26\x24" + "\xC5\xC7\xC8\xC8\xC8\xBD\xAD\xA7\xA1\xA0\xA7\xB0\xB9\xC0\xC6\xCB\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCB\xC4\xBA\xAF\x9F\x8B\x78" + "\x61\x48\x35\x30\x2B\x27\x26\x25\x24\x24\x23\x24\x24\x24\x24\x26\x24\x23\x24\x27\x24\x23\x24\x24\x24\x24\x22\x24\x25\x25\x26\x24" + "\xC6\xC8\xC8\xC8\x8C\x5E\x4D\x49\x42\x44\x47\x4E\x54\x58\x5C\x63\x6F\x7B\x87\x91\x8E\x8A\x86\x81\x75\x67\x5C\x55\x4E\x41\x34\x31" + "\x2C\x2B\x29\x25\x28\x26\x25\x24\x26\x25\x25\x25\x24\x25\x25\x26\x25\x25\x24\x25\x25\x24\x24\x24\x25\x23\x21\x23\x25\x25\x27\x25" + "\xC7\xC8\xC3\x7B\x35\x2B\x26\x26\x24\x27\x28\x29\x29\x29\x2C\x2D\x2D\x2D\x2D\x30\x2F\x2F\x30\x2F\x2C\x2D\x2B\x29\x27\x26\x26\x26" + "\x26\x24\x24\x24\x24\x24\x24\x25\x25\x25\x27\x26\x24\x24\x24\x25\x24\x24\x26\x25\x26\x23\x26\x24\x24\x25\x24\x25\x22\x23\x25\x25" + "\xC8\xC8\xA4\x49\x29\x25\x22\x22\x24\x25\x26\x25\x24\x25\x25\x24\x25\x26\x27\x25\x26\x26\x26\x25\x25\x25\x26\x26\x25\x23\x24\x25" + "\x25\x23\x25\x26\x27\x25\x24\x25\x26\x27\x27\x25\x22\x22\x23\x26\x24\x24\x24\x25\x26\x24\x26\x25\x23\x25\x25\x25\x25\x25\x26\x24" + "\xC8\xC8\x80\x36\x26\x23\x22\x23\x23\x24\x27\x27\x24\x24\x25\x24\x24\x27\x26\x24\x24\x25\x25\x24\x25\x24\x25\x25\x24\x25\x25\x24" + "\x25\x25\x25\x24\x23\x24\x25\x24\x24\x25\x23\x23\x24\x25\x24\x25\x24\x25\x27\x27\x24\x24\x26\x26\x24\x26\x27\x25\x25\x25\x24\x26" + "\xC9\xC1\x6C\x30\x25\x22\x23\x23\x23\x24\x25\x26\x24\x24\x25\x26\x26\x27\x26\x26\x24\x24\x25\x24\x24\x24\x26\x26\x25\x25\x25\x25" + "\x24\x26\x23\x23\x25\x24\x25\x25\x23\x24\x23\x24\x25\x23\x24\x23\x25\x25\x24\x24\x24\x23\x24\x25\x26\x25\x25\x25\x24\x25\x24\x24" + "\xC9\xBE\x68\x2E\x24\x23\x25\x23\x23\x24\x24\x24\x24\x25\x25\x26\x27\x25\x25\x25\x24\x23\x23\x24\x25\x25\x26\x25\x25\x25\x26\x26" + "\x25\x24\x22\x23\x26\x25\x24\x24\x25\x25\x25\x25\x24\x23\x23\x25\x25\x26\x25\x25\x24\x24\x23\x23\x23\x24\x23\x25\x24\x25\x24\x24" + "\xCA\xBB\x66\x2D\x22\x23\x23\x24\x23\x24\x25\x25\x27\x26\x24\x23\x24\x25\x27\x24\x22\x24\x25\x24\x25\x27\x25\x24\x26\x26\x26\x28" + "\x25\x25\x25\x25\x23\x25\x26\x25\x25\x24\x24\x21\x22\x23\x25\x25\x26\x24\x24\x27\x24\x26\x25\x26\x24\x25\x24\x24\x25\x25\x25\x23" + "\xCB\xC0\x6A\x2D\x25\x25\x26\x24\x25\x25\x25\x24\x27\x27\x26\x26\x24\x25\x25\x25\x23\x25\x25\x26\x26\x25\x25\x26\x24\x26\x25\x25" + "\x24\x23\x24\x23\x23\x23\x25\x24\x25\x23\x23\x22\x25\x25\x27\x24\x24\x26\x23\x23\x26\x24\x26\x25\x24\x23\x23\x24\x25\x23\x25\x26" + "\xCB\xC6\x71\x2F\x25\x23\x25\x25\x23\x24\x25\x26\x24\x25\x24\x25\x26\x24\x24\x23\x25\x25\x25\x24\x26\x25\x25\x26\x24\x26\x24\x25" + "\x25\x23\x24\x25\x23\x23\x25\x26\x25\x24\x24\x24\x26\x26\x25\x26\x22\x23\x23\x25\x23\x24\x25\x25\x24\x24\x25\x25\x24\x25\x25\x24" + "\xCB\xCB\x87\x39\x25\x22\x24\x26\x25\x24\x24\x27\x24\x24\x24\x20\x23\x25\x24\x26\x24\x24\x24\x24\x25\x23\x24\x25\x25\x28\x26\x26" + "\x27\x27\x26\x25\x25\x26\x26\x25\x23\x24\x24\x24\x24\x24\x25\x25\x23\x24\x23\x25\x22\x24\x27\x24\x24\x26\x25\x24\x26\x25\x24\x23" + "\xCC\xCC\xA6\x47\x25\x26\x26\x25\x25\x22\x24\x25\x24\x25\x25\x24\x23\x22\x23\x26\x24\x23\x25\x23\x24\x25\x25\x24\x23\x24\x24\x23" + "\x23\x26\x23\x22\x24\x25\x25\x26\x24\x23\x24\x24\x23\x23\x25\x23\x26\x24\x25\x24\x26\x25\x24\x22\x26\x25\x24\x25\x23\x25\x25\x23" + "\xCC\xCC\xBE\x52\x2A\x26\x24\x25\x24\x26\x24\x23\x24\x24\x25\x25\x25\x25\x25\x29\x25\x24\x26\x24\x24\x25\x26\x24\x25\x25\x24\x23" + "\x22\x22\x24\x24\x23\x25\x25\x24\x25\x24\x26\x27\x26\x24\x22\x23\x24\x25\x25\x25\x24\x25\x25\x23\x24\x23\x23\x26\x23\x25\x25\x25" + "\xCC\xCC\xCA\x6C\x2F\x28\x25\x27\x25\x25\x24\x25\x25\x24\x22\x24\x25\x24\x25\x23\x24\x24\x25\x24\x24\x25\x26\x25\x24\x26\x25\x24" + "\x25\x24\x26\x24\x24\x25\x25\x24\x27\x25\x25\x26\x26\x26\x25\x25\x26\x26\x24\x26\x24\x24\x24\x25\x23\x24\x24\x24\x24\x23\x24\x25" + "\xCC\xCD\xCD\xA3\x35\x29\x25\x25\x26\x25\x26\x27\x25\x26\x23\x22\x25\x24\x24\x26\x26\x24\x25\x25\x25\x25\x25\x23\x26\x27\x24\x24" + "\x22\x24\x24\x24\x24\x24\x24\x23\x23\x26\x26\x25\x25\x24\x25\x25\x24\x27\x25\x25\x24\x24\x24\x26\x25\x24\x24\x24\x25\x24\x24\x23" + "\xCD\xCD\xCD\xC8\x53\x2E\x25\x23\x25\x26\x26\x26\x24\x26\x27\x25\x25\x25\x25\x24\x24\x25\x27\x25\x24\x25\x25\x23\x26\x25\x24\x23" + "\x23\x25\x23\x23\x25\x24\x24\x25\x23\x26\x25\x25\x24\x22\x25\x26\x24\x25\x25\x25\x25\x24\x24\x26\x25\x26\x25\x23\x24\x25\x24\x23" + "\xCD\xCD\xCD\xCD\x8E\x3C\x32\x30\x2F\x2F\x2D\x2E\x2B\x2A\x2B\x2A\x29\x29\x28\x27\x25\x28\x28\x24\x23\x25\x25\x23\x22\x23\x24\x25" + "\x24\x25\x26\x25\x25\x24\x24\x23\x23\x24\x25\x23\x23\x23\x25\x24\x25\x25\x25\x24\x25\x23\x25\x22\x24\x25\x25\x24\x24\x24\x23\x24" + "\xCD\xCD\xCD\xCD\xC5\xA2\x94\x8D\x87\x7F\x79\x71\x68\x65\x60\x5E\x5A\x51\x48\x41\x35\x31\x31\x2E\x2D\x2A\x2A\x27\x26\x25\x24\x27" + "\x26\x24\x25\x24\x23\x23\x23\x23\x26\x24\x27\x26\x26\x24\x25\x24\x26\x26\x24\x24\x24\x24\x25\x24\x25\x23\x26\x25\x26\x25\x26\x26" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xC9\xC6\xBF\xB4\xA8\x9C\x8D\x82\x76\x6A\x5A\x4B\x3D\x35\x32\x2F\x2C\x2B" + "\x29\x26\x25\x24\x25\x23\x24\x26\x25\x25\x25\x25\x24\x24\x24\x24\x24\x25\x24\x24\x25\x24\x26\x26\x26\x26\x25\x26\x26\x26\x25\x25" + "\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCA\xC5\xC0\xB9\xB3\xAC\xA1\x8E\x76\x56\x3D" + "\x36\x31\x2A\x28\x29\x26\x27\x26\x25\x26\x24\x25\x24\x25\x26\x24\x25\x25\x26\x24\x24\x23\x23\x26\x26\x26\x26\x24\x25\x24\x24\x24" + "\xCE\xCC\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCC\xC6" + "\xA9\x8C\x6D\x5B\x49\x3D\x32\x2E\x2B\x28\x25\x23\x24\x24\x23\x26\x26\x25\x24\x23\x24\x23\x24\x24\x26\x26\x26\x25\x26\x24\x21\x24" + "\xCD\xCD\xCD\xCD\xCC\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCC\xCD\xCB\xC0\xAB\x97\x82\x6E\x54\x3C\x30\x2C\x29\x27\x25\x27\x25\x25\x24\x23\x23\x24\x24\x24\x26\x24\x25\x24\x23\x24\x25\x24" + "\xCD\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCC\xCD\xCD\xCC\xCD\xCD\xCA\xC2\xB6\xA7\x86\x5A\x38\x31\x2C\x28\x26\x25\x24\x24\x24\x25\x26\x26\x29\x25\x25\x26\x27\x25\x25\x26" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCC\xBB\x91\x68\x4F\x3A\x30\x2A\x28\x26\x25\x24\x23\x24\x24\x25\x26\x23\x23\x26\x25" + "\xCB\xCA\xC9\xCB\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xC8\xB4\x93\x76\x57\x3B\x30\x2D\x28\x25\x25\x25\x25\x25\x22\x23\x24\x25" + "\x64\x62\x61\x62\x68\x71\x7B\x81\x8C\x94\x9C\xA3\xAB\xB5\xC3\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCC\xC5\xB8\xA5\x79\x4C\x34\x2E\x26\x25\x25\x27\x21\x26\x26\x25" + "\x2C\x2B\x28\x2C\x2C\x2B\x2B\x2F\x2E\x31\x33\x34\x37\x38\x3B\x43\x5B\x70\x83\x95\xA7\xAF\xB6\xBC\xC5\xCB\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCC\xCD\xCD\xCD\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCD\xCD\xCD\xCC\xC6\xA1\x6E\x4A\x35\x29\x24\x27\x26\x24\x24" + "\x26\x25\x23\x23\x26\x25\x26\x25\x25\x24\x28\x2A\x26\x28\x29\x2C\x2B\x2E\x30\x33\x37\x44\x53\x61\x74\x88\x9D\xB1\xC4\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCD\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xC9\xA7\x81\x58\x39\x30\x2A\x26\x25" + "\x25\x24\x26\x26\x24\x24\x23\x24\x23\x24\x26\x28\x25\x26\x25\x26\x27\x25\x26\x27\x28\x28\x2A\x2C\x31\x35\x42\x50\x61\x74\x92\xAF" + "\xC9\xCD\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCC\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCE\xCD\xCD\xCD\xCC\xC8\xB8\x9B\x5E\x39\x2F\x2A" + "\x24\x23\x23\x24\x23\x24\x25\x26\x25\x25\x25\x25\x24\x24\x25\x24\x24\x24\x22\x25\x25\x24\x24\x24\x27\x27\x28\x2C\x2B\x2C\x31\x36" + "\x3F\x5A\x7D\xA0\xB8\xC5\xCC\xCD\xCD\xCC\xCC\xCD\xCC\xCC\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCA\xB2\x7D\x55" + "\x22\x23\x25\x25\x25\x25\x25\x24\x24\x24\x23\x24\x25\x24\x24\x24\x25\x26\x26\x25\x24\x25\x25\x25\x24\x24\x25\x26\x25\x26\x27\x29" + "\x2A\x2C\x31\x38\x57\x74\x91\xAD\xC7\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCC\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCB\xB7"; + +BYTE TEST_64X64_GREEN_PLANE_RLE[3696] = + "\x34\x2A\x25\x23\xF0\x24\x2B\x60\xA2\x97\x84\x75\x62\x50\x3C\x34\x30\x2C\x29\x28\x93\x26\x25\x25\x23\x23\x25\x24\x24\x25\x84\x26" + "\x27\x25\x24\x25\x25\x27\x24\xC3\x25\x25\x24\x25\x26\x25\x23\x24\x24\x25\x23\x24\x30\x22\x23\x21\xF0\x4E\x24\x1C\x12\x0C\x0A\x08" + "\x06\x08\x26\x4E\x6A\x8E\xA0\xB6\xF0\xCA\xDC\xCA\xA0\x74\x3E\x20\x1A\x12\x08\x08\x08\x02\x02\x02\xF0\x03\x03\x02\x02\x01\x00\x00" + "\x02\x00\x02\x07\x00\x01\x02\x00\xF0\x01\x02\x00\x02\x00\x01\x01\x00\x02\x01\x01\x02\x00\x02\x00\x40\x04\x04\x02\x06\xF0\xD2\xF8" + "\xCC\x9A\x5E\x40\x26\x14\x10\x0C\x02\x00\x04\x10\x20\xF0\x30\x44\x66\x98\xCC\xF7\xF7\xD4\x98\x66\x4A\x2C\x14\x0C\x08\xF0\x0A\x08" + "\x01\x03\x01\x07\x00\x02\x00\x01\x00\x00\x08\x04\x00\xF0\x02\x03\x01\x00\x00\x00\x02\x06\x04\x04\x01\x01\x01\x00\x00\x40\x07\x00" + "\x04\x04\xC4\x02\x16\x52\x92\xC8\xBE\xAC\x8E\x58\x1C\x02\x00\x14\x02\xF0\x22\x60\xA6\xCC\xC2\xB2\x9C\x70\x3E\x1C\x0A\x04\x06\x00" + "\x02\x75\x00\x03\x00\x01\x01\x02\x01\xF0\x04\x02\x00\x02\x00\x01\x01\x02\x02\x02\x03\x03\x06\x00\x03\x10\x03\x04\x8C\x12\x40\x70" + "\x96\xAE\x6C\x02\x00\xF0\x16\x40\x6E\x94\xB4\xD0\xBA\x72\x26\x18\x0C\x08\x04\x00\x03\xF0\x02\x04\x01\x05\x03\x02\x01\x03\x02\x05" + "\x05\x03\x01\x00\x01\xA0\x02\x01\x01\x02\x02\x04\x02\x02\x00\x00\x03\x8D\x02\x01\x00\x00\x0A\x22\x1C\x00\x03\xF0\x0A\x20\x38\x72" + "\xCE\xF7\xBA\x70\x34\x12\x08\x08\x05\x01\x00\xF0\x04\x02\x02\x06\x06\x00\x02\x04\x02\x02\x03\x01\x03\x00\x04\x70\x00\x06\x0A\x03" + "\x01\x02\x04\x04\x5C\x02\x00\x00\x02\x00\x44\x01\x01\x01\x00\xF0\x02\x02\x02\x1E\x78\xBC\xB6\x74\x2E\x0E\x0A\x02\x00\x01\x01\xF0" + "\x00\x01\x00\x00\x00\x01\x02\x02\x00\x02\x04\x02\x00\x00\x01\x50\x07\x01\x01\x00\x00\x04\x23\x01\x00\x47\x01\x00\x01\x00\x23\x01" + "\x00\xF0\x01\x01\x17\x49\x75\x8D\x67\x27\x01\x1A\x60\xAA\xA0\x32\x0E\xF0\x04\x02\x00\x02\x01\x04\x02\x02\x01\x02\x00\x05\x01\x01" + "\x02\x90\x00\x00\x04\x01\x01\x02\x00\x03\x03\x08\x2A\x01\x00\x13\x02\xF0\x05\x2B\x91\xF1\xD9\xB1\x95\xBB\xF1\xE5\x63\x0F\x1C\x7C" + "\xC4\xF0\x48\x0A\x02\x04\x04\x02\x05\x02\x01\x00\x00\x00\x01\x04\x01\xA0\x07\x01\x00\x05\x01\x00\x00\x01\x00\x02\x04\x99\x02\x00" + "\x00\x00\x02\x02\x00\x02\x00\xF0\x01\x00\x43\xB1\x8B\x15\x48\x70\x7C\x62\x20\x35\xA7\x95\x0F\xF0\x00\x46\x8C\x18\x08\x00\x05\x00" + "\x04\x02\x02\x00\x01\x00\x06\xC0\x00\x00\x02\x01\x03\x01\x04\x04\x00\x04\x02\x03\x71\xF0\x13\x9D\x43\x26\xAA\xB8\xA6\x9C\xAA\xBC" + "\x70\x0D\x79\x71\x01\xF0\x02\x60\x5C\x0C\x04\x02\x04\x02\x01\x00\x00\x02\x00\x00\x01\x83\x04\x04\x06\x02\x00\x03\x01\x00\x41\xF0" + "\x01\x00\x09\xA5\x3B\x3C\xBC\x74\x24\x10\x04\x06\x3A\xAA\x94\xF0\x08\x7F\x55\x01\x0A\x96\x2A\x06\x03\x03\x01\x00\x01\x01\x00\xE0" + "\x01\x03\x00\x00\x01\x03\x04\x04\x01\x01\x00\x00\x01\x02\x61\xF0\x2F\x3F\x0E\x6E\x38\x02\x03\x37\x89\x9F\x7F\x25\x6C\x46\x1F\xF0" + "\x55\x09\x00\x26\x5E\x0C\x06\x00\x00\x00\x04\x02\x00\x02\x02\xC0\x00\x02\x04\x00\x05\x02\x02\x04\x02\x00\x00\x01\x41\xF0\x02\x00" + "\x2D\x17\x1E\x48\x06\x00\x15\x79\x8B\x77\x91\x67\x10\xF0\x56\x03\x1F\x17\x00\x10\x50\x22\x06\x04\x02\x03\x01\x04\x04\x93\x02\x03" + "\x04\x04\x01\x01\x02\x01\x00\x20\x02\x00\x21\xF0\x01\x01\x00\x00\x2F\x15\x0E\x26\x00\x01\x12\x46\x3A\x32\x4A\xF0\x40\x0A\x3A\x06" + "\x1B\x17\x00\x04\x46\x4E\x0E\x03\x05\x02\x00\xF0\x00\x03\x01\x04\x00\x03\x02\x04\x00\x00\x01\x03\x05\x01\x00\x10\x02\x11\xF0\x01" + "\x00\x02\x01\x00\x18\x0E\x05\x11\x00\x00\x06\x66\xBE\xB8\xF0\xB2\x52\x00\x0D\x03\x04\x04\x00\x00\x1A\x42\x16\x06\x02\x00\xF0\x00" + "\x05\x01\x00\x02\x01\x00\x03\x02\x02\x01\x02\x00\x04\x00\x20\x00\x02\x11\xF0\x02\x02\x00\x02\x00\x24\x0E\x15\x29\x00\x02\x02\x08" + "\x24\x3E\xF0\x1C\x02\x00\x49\x00\x20\x1C\x00\x00\x02\x44\x20\x04\x04\x00\xF0\x02\x00\x04\x00\x01\x03\x03\x02\x00\x00\x02\x02\x04" + "\x00\x00\x20\x00\x05\x0C\x53\x05\x17\x0B\x01\x00\xF0\x01\x00\x38\x2E\x21\x7B\x1D\x01\x00\x00\x02\x02\x01\x03\x49\xF0\x65\x08\x38" + "\x1A\x00\x00\x00\x3A\x30\x06\x02\x02\x05\x01\x03\xE0\x02\x00\x04\x02\x03\x03\x01\x03\x02\x00\x00\x04\x01\x04\xF0\x01\x01\x00\x01" + "\x01\x01\x00\x01\x01\x00\x01\x00\x03\x45\x77\x24\x2D\x00\x83\x01\x26\x78\x04\x51\x83\x21\x00\xE3\x07\x43\x91\x25\x3C\x5C\x02\x00" + "\x01\x01\x08\x52\x06\x00\xF0\x01\x01\x00\x03\x03\x04\x00\x00\x02\x01\x05\x02\x00\x02\x00\x93\x02\x02\x00\x02\x00\x00\x00\x02\x00" + "\xF0\x08\x18\x59\xB7\x67\x09\x00\x00\x02\x02\x00\x5C\x62\x0F\x79\xF0\xB7\x71\x37\x27\x45\x91\xB1\x49\x0C\x94\x30\x00\x00\x02\x02" + "\xF0\x00\x54\x10\x06\x01\x00\x04\x06\x02\x00\x06\x02\x03\x01\x00\x70\x00\x01\x00\x01\x01\x00\x00\x04\x35\x02\x02\x00\xF0\x02\x3A" + "\x3A\x3B\xC3\xD9\x5D\x1B\x01\x00\x00\x0A\xB0\x9E\x1C\xC4\x4F\xB5\xED\xF9\xDD\x8D\x1D\x4C\xC4\x4C\x02\x00\xF0\x2C\x34\x04\x04\x02" + "\x03\x01\x03\x03\x03\x02\x04\x04\x03\x00\x60\x04\x04\x00\x01\x01\x00\x13\x02\x18\x00\xF0\x0C\x52\x20\x0D\x51\xBB\xAF\x61\x19\x00" + "\x00\x12\x7E\xB0\x8C\x95\x52\x24\x1A\x38\x64\x98\xB6\x54\x00\xF0\x05\x3F\x0F\x03\x03\x02\x02\x00\x02\x04\x04\x02\x01\x02\x06\x70" + "\x02\x03\x00\x01\x04\x04\x02\x0A\xF0\x02\x00\x00\x00\x44\x6E\x18\x04\x21\x6D\xB1\xB9\x69\x0D\x00\xC4\x0A\x4E\x94\xB4\xCC\xCE\xC0" + "\xAA\x78\x24\x02\x00\xF0\x21\x91\xBB\x3B\x05\x04\x00\x00\x01\x02\x00\x01\x00\x02\x02\x80\x00\x00\x01\x01\x04\x01\x01\x00\x68\x02" + "\x02\x02\x00\x02\x00\xF0\x0E\x94\xFC\xA2\x54\x20\x21\x63\xC3\xF5\x95\x49\x1F\x04\x22\xF0\x36\x3A\x2C\x18\x06\x01\x00\x00\x00\x09" + "\x29\x95\xE9\x99\x45\xF0\x09\x03\x01\x00\x02\x00\x00\x02\x00\x01\x00\x05\x03\x00\x04\x50\x06\x01\x00\x00\x02\x78\x02\x00\x02\x00" + "\x00\x02\x00\xF0\x02\x26\x8C\xC2\xA8\x66\x1C\x05\x31\x99\xC9\xB5\x8F\x5B\x39\xF0\x19\x03\x02\x02\x00\x07\x2B\x63\x93\xBB\x97\x25" + "\x06\x24\x04\xF0\x00\x01\x02\x00\x02\x03\x01\x01\x03\x01\x01\x01\x00\x00\x00\x40\x01\x02\x02\x01\x98\x02\x02\x00\x02\x00\x00\x00" + "\x02\x00\xF0\x04\x26\x70\xB2\xC6\x7E\x2A\x02\x1D\x5B\x99\xB7\xC5\xCB\xC7\xF0\xB3\xC3\xCB\xCF\xC7\xB1\x91\x4B\x0A\x56\xBC\xB4\x0A" + "\x00\x01\x64\x01\x02\x00\x00\x01\x02\x70\x01\x00\x01\x01\x00\x00\x00\xAA\x02\x02\x02\x00\x02\x00\x02\x00\x02\x00\xF0\x20\x54\xB6" + "\xFF\xD0\x82\x48\x18\x1F\x33\x4F\x69\x83\x71\x63\xF0\x59\x37\x00\x40\x80\xEE\xDA\x6E\x0D\x08\x00\x00\x00\x03\x01\xE0\x04\x01\x00" + "\x02\x00\x02\x02\x05\x07\x03\x02\x00\x00\x02\x03\x2E\x02\x00\xF0\x02\x00\x02\x00\x0C\x5E\xAA\xC2\xB8\xA0\x86\x68\x48\x32\x3E\xF0" + "\x54\x6E\x98\xB2\xC0\xAA\x36\x02\x0D\x53\x07\x02\x02\x00\x04\x34\x02\x01\x00\x80\x01\x02\x06\x00\x06\x04\x00\x01\x63\x02\x02\x00" + "\x00\x02\x00\x2E\x02\x00\xF0\x06\x2A\x62\x90\x9E\xB0\xC2\xD0\xC8\xBA\xAC\x8E\x64\x30\x08\xF0\x00\x00\x51\x43\x0D\x01\x02\x02\x05" + "\x03\x00\x02\x01\x03\x00\x90\x02\x02\x04\x02\x00\x03\x03\x00\x01\x13\x02\x4F\x00\x02\x02\x00\x05\xF0\x04\x10\x1C\x2A\x36\x30\x24" + "\x18\x06\x00\x01\x00\x21\x81\xA7\xF0\x2D\x03\x01\x03\x07\x06\x06\x01\x00\x02\x02\x00\x01\x03\x02\x60\x02\x00\x02\x01\x02\x04\x9F" + "\x02\x00\x02\x02\x00\x00\x00\x02\x00\x0C\xF0\x01\x01\x1B\x65\xB1\xA5\x39\x0D\x03\x00\x00\x08\x01\x01\x02\xD0\x01\x03\x02\x00\x02" + "\x06\x00\x03\x00\x00\x00\x05\x03\x47\x02\x02\x02\x00\x2B\x02\x00\x33\x01\x01\x00\xF0\x02\x02\x01\x00\x00\x00\x1B\x7F\xC1\xB3\x6D" + "\x1D\x03\x00\x01\xF0\x01\x00\x01\x00\x01\x02\x04\x02\x03\x03\x03\x05\x03\x01\x00\x40\x03\x02\x02\x00\xA6\x00\x02\x00\x00\x02\x00" + "\x01\x01\x02\x00\x26\x02\x00\xF0\x02\x00\x01\x00\x00\x01\x01\x0B\x1F\x3B\x75\xCB\xFA\xB3\x5F\xF0\x2F\x0B\x09\x05\x01\x06\x06\x02" + "\x00\x06\x02\x01\x00\x01\x00\xA0\x02\x00\x04\x04\x04\x02\x00\x00\x04\x00\xF0\x02\x02\x02\x00\x01\x17\x37\x45\x55\x57\x49\x39\x27" + "\x19\x0D\x37\x03\x01\x00\xF0\x01\x0F\x25\x3B\x59\x81\x9D\xB5\xCD\xB9\x6D\x1F\x15\x0D\x05\xF0\x03\x01\x05\x01\x01\x03\x01\x02\x05" + "\x03\x01\x04\x02\x00\x00\x90\x02\x00\x01\x05\x01\x04\x00\x00\x00\xF0\x02\x02\x00\x00\x77\xBD\xBF\xBB\xBD\xB7\xBF\xC3\xC9\xCF\xD3" + "\xF0\xCF\xBB\xA3\x8B\x77\x7D\x85\x8D\x97\xAF\xC7\xCF\xC9\xC1\xBB\xF0\xAD\x8D\x69\x39\x17\x15\x05\x01\x01\x01\x04\x02\x04\x02\x00" + "\xF0\x02\x02\x00\x02\x04\x00\x03\x02\x02\x00\x00\x02\x01\x01\x01\x40\x00\x00\x02\x02\xF0\x02\x00\x09\x99\xAD\x65\x4D\x45\x3B\x39" + "\x3D\x49\x55\x5D\x5F\xF0\x6B\x83\x9B\xB3\xC1\xBD\xB5\xAB\xA3\x91\x73\x61\x57\x4D\x35\xF0\x1B\x15\x0B\x0D\x09\x01\x07\x03\x01\x02" + "\x01\x00\x04\x02\x00\x14\x01\xE0\x04\x00\x02\x01\x04\x00\x01\x04\x06\x04\x05\x03\x03\x00\xF0\x02\x00\x3D\x63\x17\x0B\x07\x07\x00" + "\x03\x03\x07\x09\x07\x0D\xF0\x11\x0F\x0D\x0B\x15\x11\x11\x13\x13\x0D\x0F\x09\x05\x03\x05\xF0\x03\x01\x01\x01\x02\x04\x06\x02\x00" + "\x00\x02\x04\x00\x01\x03\xF0\x03\x01\x02\x00\x00\x03\x00\x00\x02\x00\x02\x01\x00\x02\x00\x40\x06\x04\x02\x01\xF0\x00\x00\x47\x25" + "\x05\x03\x00\x02\x01\x01\x02\x04\x00\x01\x00\xB3\x00\x01\x02\x01\x01\x03\x01\x01\x01\x00\x01\xF0\x04\x02\x01\x00\x04\x00\x03\x07" + "\x01\x02\x01\x03\x03\x07\x03\xF0\x04\x06\x02\x01\x00\x02\x06\x04\x03\x00\x00\x02\x02\x02\x04\x50\x00\x00\x00\x03\x04\xF0\x02\x0D" + "\x27\x0B\x01\x01\x02\x00\x00\x00\x03\x01\x00\x00\x00\xF0\x04\x04\x00\x00\x04\x00\x01\x00\x00\x01\x00\x02\x02\x02\x00\xF0\x00\x02" + "\x01\x02\x03\x01\x04\x00\x00\x02\x01\x01\x00\x02\x02\xF0\x03\x00\x03\x02\x00\x05\x05\x00\x01\x03\x01\x04\x01\x03\x00\x40\x01\x00" + "\x00\x03\xF0\x00\x05\x07\x03\x01\x02\x04\x00\x00\x00\x01\x03\x00\x02\x00\xF0\x00\x02\x03\x01\x01\x00\x01\x03\x00\x02\x02\x00\x01" + "\x00\x00\xF0\x02\x02\x02\x03\x01\x00\x02\x02\x01\x01\x04\x02\x04\x02\x01\xF4\x00\x01\x04\x00\x02\x02\x02\x00\x02\x01\x03\x05\x01" + "\x03\x00\xF0\x02\x05\x03\x01\x03\x00\x03\x02\x00\x00\x02\x02\x06\x02\x01\xF0\x05\x05\x00\x04\x01\x03\x02\x04\x00\x00\x04\x01\x01" + "\x02\x02\xF0\x00\x04\x00\x02\x06\x04\x05\x00\x04\x02\x00\x01\x01\x07\x03\xF0\x00\x04\x00\x02\x03\x01\x04\x00\x04\x04\x06\x02\x02" + "\x02\x01\x40\x02\x00\x02\x01\xF0\x02\x0A\x08\x00\x06\x04\x06\x00\x04\x02\x00\x01\x00\x02\x04\xF0\x06\x00\x00\x03\x02\x02\x02\x00" + "\x04\x02\x03\x00\x04\x03\x00\xF0\x01\x05\x01\x03\x01\x03\x00\x03\x01\x01\x00\x01\x01\x02\x06\xF0\x04\x04\x01\x03\x04\x01\x07\x04" + "\x03\x02\x01\x00\x03\x01\x00\x40\x00\x03\x00\x06\xF0\x00\x0C\x0E\x04\x00\x03\x01\x02\x03\x01\x00\x04\x05\x03\x03\xA5\x01\x04\x01" + "\x01\x03\x04\x00\x00\x03\x00\xF0\x01\x00\x02\x00\x00\x04\x00\x00\x00\x04\x00\x02\x02\x04\x02\xF0\x02\x03\x04\x03\x05\x00\x04\x05" + "\x00\x01\x00\x00\x02\x04\x02\x40\x01\x04\x00\x03\xF0\x00\x0A\x2C\x14\x00\x01\x01\x02\x04\x00\x01\x02\x00\x01\x00\xF0\x09\x05\x02" + "\x00\x06\x01\x01\x01\x00\x01\x03\x01\x01\x02\x04\xF0\x04\x02\x04\x08\x04\x00\x04\x06\x02\x01\x03\x00\x00\x00\x03\xF0\x03\x00\x01" + "\x02\x02\x00\x00\x01\x00\x04\x01\x00\x04\x00\x01\x40\x04\x00\x01\x01\xF0\x02\x02\x3E\x1C\x00\x08\x04\x01\x00\x03\x00\x03\x00\x02" + "\x02\xF0\x08\x00\x05\x01\x00\x00\x01\x02\x01\x01\x04\x02\x01\x03\x07\xF0\x03\x05\x07\x01\x05\x05\x01\x01\x01\x02\x02\x01\x00\x00" + "\x01\xF0\x01\x00\x03\x06\x00\x04\x01\x08\x02\x05\x03\x04\x01\x01\x02\x40\x05\x00\x02\x00\xF0\x00\x00\x30\x16\x0A\x00\x03\x00\x01" + "\x08\x00\x03\x00\x01\x00\x63\x02\x04\x06\x04\x06\x02\xF0\x00\x00\x02\x00\x04\x02\x00\x00\x01\x07\x02\x04\x01\x00\x00\xF0\x03\x02" + "\x02\x04\x06\x06\x02\x05\x00\x03\x02\x00\x02\x03\x00\xA0\x02\x02\x03\x03\x01\x02\x00\x00\x00\x04\xF0\x00\x00\x18\x34\x0A\x04\x02" + "\x04\x02\x01\x00\x04\x02\x00\x05\x93\x01\x00\x01\x00\x0B\x01\x00\x01\x00\xF0\x02\x01\x02\x02\x02\x06\x04\x04\x00\x02\x00\x00\x00" + "\x04\x02\xF0\x01\x01\x00\x04\x06\x04\x04\x02\x01\x02\x00\x01\x01\x04\x01\x70\x02\x02\x03\x02\x03\x01\x00\xF0\x00\x02\x06\x6E\x0C" + "\x02\x00\x03\x02\x00\x04\x04\x00\x04\x02\xF0\x03\x00\x00\x01\x06\x04\x00\x00\x02\x02\x00\x01\x03\x04\x02\xF0\x01\x00\x05\x00\x03" + "\x00\x00\x01\x01\x01\x07\x02\x02\x01\x01\xF0\x03\x00\x00\x03\x02\x02\x01\x00\x00\x00\x02\x04\x00\x00\x00\x40\x02\x02\x00\x03\xF0" + "\x02\x00\x00\x4A\x3C\x0A\x00\x03\x01\x02\x00\x01\x01\x00\x08\xB3\x06\x00\x02\x02\x03\x03\x02\x04\x00\x01\x00\xF0\x03\x00\x01\x02" + "\x02\x01\x01\x02\x00\x00\x04\x00\x00\x01\x00\xA3\x01\x03\x00\x02\x00\x03\x00\x00\x02\x00\x70\x04\x02\x01\x01\x02\x00\x00\x03\xF0" + "\x0A\x76\x1C\x1A\x1A\x14\x12\x0E\x10\x0E\x08\x08\x0A\x08\x08\xF0\x06\x06\x02\x06\x02\x01\x01\x00\x00\x00\x07\x03\x00\x04\x02\xF0" + "\x00\x06\x04\x00\x00\x00\x03\x00\x03\x00\x03\x01\x02\x00\x03\xF0\x02\x00\x00\x01\x00\x01\x02\x07\x01\x01\x00\x02\x00\x01\x01\x10" + "\x02\x04\xF0\x6E\xCC\xC4\xBA\xB0\xA0\x98\x86\x7A\x76\x6A\x68\x62\x50\x40\xF0\x34\x20\x12\x12\x14\x14\x0A\x0A\x08\x08\x04\x00\x04" + "\x04\x01\xF0\x01\x01\x03\x01\x01\x00\x06\x00\x04\x06\x06\x02\x00\x00\x02\xF0\x02\x01\x00\x01\x02\x00\x04\x02\x03\x02\x02\x04\x02" + "\x06\x04\x04\xF0\x10\x56\x72\x80\x8C\x9C\xA8\xB8\xCA\xCE\xD2\xD0\xCA\xC6\xC0\xF0\xB6\xB0\xA2\x8A\x78\x5A\x42\x26\x1C\x18\x14\x10" + "\x08\x06\x04\xF0\x00\x00\x04\x00\x02\x06\x01\x02\x03\x01\x03\x00\x01\x00\x03\xF0\x01\x00\x00\x02\x00\x02\x04\x02\x06\x01\x02\x00" + "\x02\x01\x01\x39\x01\x01\x00\xF0\x01\x02\x08\x0E\x1C\x32\x4A\x62\x7E\x90\x9E\xAC\xBE\xD0\xDE\xF0\xD8\xB8\x8E\x54\x24\x1A\x16\x0A" + "\x08\x08\x06\x06\x00\x00\x02\xF0\x01\x00\x00\x02\x04\x00\x02\x00\x04\x00\x01\x01\x05\x00\x00\x70\x00\x02\x03\x01\x03\x01\x01\x6C" + "\x04\x00\x00\x00\x01\x00\xF0\x01\x01\x02\x06\x10\x1A\x28\x34\x42\x58\x7C\xAC\xEC\xED\xE6\xF0\xB6\x86\x66\x40\x2E\x16\x10\x0C\x04" + "\x02\x03\x00\x01\x05\x04\xF0\x02\x00\x03\x01\x00\x00\x02\x03\x00\x00\x00\x02\x02\x00\x05\x10\x00\x34\x01\x02\x00\x23\x01\x00\x2E" + "\x02\x00\xF0\x02\x02\x02\x0E\x46\x82\xBC\xCA\xC4\xB4\xA0\x80\x52\x28\x16\xF0\x12\x0A\x06\x04\x02\x01\x00\x00\x00\x01\x02\x00\x00" + "\x00\x03\x60\x01\x01\x05\x00\x08\x00\x03\x67\x01\x02\x00\x00\x02\x00\x53\x01\x00\x02\x02\x00\x28\x01\x00\xF0\x04\x18\x44\x6C\x90" + "\xA8\xC4\xD6\xAC\x5C\x1E\x14\x0E\x02\x02\xF0\x00\x00\x02\x02\x02\x04\x04\x06\x02\x00\x04\x08\x02\x00\x04\x03\x25\x02\x00\x25\x01" + "\x00\x34\x01\x01\x00\x26\x02\x00\xF0\x02\x00\x00\x02\x00\x00\x06\x16\x2E\x4A\x8E\xE4\xF9\xC0\x78\xF0\x4E\x28\x16\x0C\x08\x04\x00" + "\x03\x05\x09\x01\x00\x00\x07\x03\x20\x02\x01\x55\x03\x05\x07\x03\x00\x2F\x02\x00\x0D\xF0\x01\x02\x00\x02\x24\x78\xC0\xCA\xB2\x8C" + "\x5A\x26\x14\x10\x08\x90\x04\x02\x02\x00\x01\x01\x00\x03\x00\xF0\xCD\xCF\xCF\xD1\xC9\xB7\xA3\x97\x81\x71\x61\x53\x43\x2F\x13\x5D" + "\x01\x02\x02\x02\x00\x03\x13\x01\xF0\x00\x01\x01\x00\x00\x00\x0A\x32\x72\x9E\xC2\xD4\x92\x3E\x18\x90\x12\x02\x00\x00\x04\x01\x06" + "\x04\x00\xF0\x6F\x6D\x71\x6B\x77\x8B\x9F\xA3\xBB\xC5\xD1\xDD\xE7\xF9\xF0\xC5\xEE\xE3\xB9\x93\x6F\x4B\x3B\x2D\x21\x0F\x03\x00\x29" + "\x01\x00\x14\x01\xF0\x00\x10\x2A\x50\xA6\xF4\xDA\x80\x48\x20\x08\x05\x0C\x00\x03\x10\x01\xF0\x0B\x0B\x09\x11\x0B\x0B\x09\x13\x11" + "\x19\x15\x13\x21\x1F\x23\xF0\x2D\x5F\x83\xA5\xC3\xDF\xD5\xC5\xB5\xA1\x85\x5F\x37\x11\x00\xC6\x00\x00\x02\x00\x00\x00\x02\x02\x00" + "\x00\x02\x00\xF0\x02\x01\x00\x00\x02\x0E\x58\xB6\xBA\x98\x5E\x2A\x12\x08\x04\x10\x02\xF0\x01\x01\x06\x06\x03\x01\x05\x01\x03\x00" + "\x03\x03\x01\x03\x07\xF0\x0B\x07\x11\x13\x17\x1D\x37\x51\x69\x85\xA5\xB5\xC1\xC5\xB1\xE4\x75\x3B\x07\x00\x01\x01\x00\x00\x02\x02" + "\x00\x00\x00\x02\xF0\x00\x02\x00\x00\x02\x00\x00\x08\x4A\x8E\xC0\xC4\x5C\x1E\x12\x10\x0A\xF0\x01\x01\x05\x03\x01\x00\x04\x04\x04" + "\x02\x01\x05\x01\x03\x00\xF0\x03\x05\x01\x07\x03\x05\x07\x0B\x0F\x13\x1B\x33\x47\x6B\x8F\xA4\xC1\xF1\xEC\xE5\x9D\x57\x29\x0F\x01" + "\x00\xF0\x01\x01\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x0A\x2A\x50\x62\xD8\xF2\x9C\x56\xF0\x03\x00\x04\x02\x04\x02\x00\x03" + "\x01\x01\x03\x01\x02\x00\x01\xF0\x00\x02\x04\x08\x00\x01\x02\x02\x02\x05\x05\x05\x0B\x0B\x0B\xF0\x13\x19\x29\x5B\x97\xCF\xC1\xA1" + "\x75\x3F\x0B\x02\x02\x00\x02\x2C\x02\x00\x50\x02\x06\x36\x9C\xC4"; + +BYTE TEST_64X64_BLUE_PLANE[4096] = + "\x27\x23\x23\x24\x25\x25\x25\x25\x28\x37\x4A\x47\x41\x3D\x38\x33\x2E\x2A\x28\x27\x26\x27\x27\x27\x25\x24\x26\x28\x26\x27\x28\x28" + "\x26\x28\x28\x2A\x26\x27\x27\x28\x27\x27\x25\x26\x24\x25\x27\x28\x27\x28\x27\x27\x25\x26\x27\x27\x26\x27\x27\x27\x26\x25\x26\x23" + "\x32\x29\x29\x27\x26\x28\x27\x27\x29\x3C\x55\x56\x56\x54\x52\x50\x4D\x48\x40\x37\x2F\x2C\x2B\x2A\x27\x27\x27\x27\x26\x26\x25\x26" + "\x27\x28\x28\x28\x27\x27\x27\x28\x25\x26\x25\x27\x26\x26\x29\x28\x26\x27\x25\x27\x25\x26\x25\x27\x25\x27\x28\x27\x26\x27\x27\x27" + "\x4E\x4D\x45\x3D\x35\x30\x2D\x29\x2B\x3E\x56\x56\x57\x57\x57\x57\x56\x56\x56\x56\x56\x52\x49\x3E\x36\x31\x2D\x29\x27\x27\x27\x28" + "\x27\x27\x24\x25\x28\x29\x27\x28\x26\x27\x29\x2A\x27\x25\x24\x26\x28\x27\x27\x25\x29\x27\x26\x25\x26\x23\x26\x27\x25\x27\x29\x26" + "\x4F\x50\x50\x51\x52\x4A\x44\x3E\x36\x41\x55\x56\x57\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x53\x4D\x47\x40\x39\x31\x2D\x27" + "\x25\x27\x26\x25\x28\x26\x25\x26\x25\x28\x29\x29\x25\x25\x26\x26\x2A\x29\x28\x26\x29\x29\x27\x27\x26\x25\x25\x24\x28\x27\x27\x25" + "\x4F\x50\x50\x51\x53\x54\x55\x53\x50\x51\x56\x57\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x58\x57\x57\x55\x52\x4E\x47\x38" + "\x2D\x2C\x26\x27\x28\x27\x26\x28\x27\x26\x24\x26\x24\x24\x24\x27\x27\x25\x26\x28\x27\x27\x25\x26\x26\x27\x26\x27\x27\x28\x27\x24" + "\x4F\x50\x51\x52\x52\x54\x55\x54\x55\x55\x56\x56\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x56\x56\x57\x57\x57\x57\x57\x56" + "\x52\x45\x38\x2F\x29\x27\x28\x25\x23\x25\x28\x26\x27\x27\x25\x27\x27\x26\x25\x28\x25\x26\x25\x27\x27\x26\x2A\x2C\x27\x27\x28\x29" + "\x4F\x50\x51\x52\x53\x53\x54\x55\x55\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x56\x57\x56\x56\x57\x57\x57\x57\x57\x57" + "\x57\x56\x52\x49\x3A\x2E\x29\x28\x27\x26\x26\x25\x25\x26\x27\x27\x27\x27\x27\x27\x25\x26\x27\x25\x25\x28\x28\x28\x25\x25\x26\x27" + "\x4F\x50\x51\x52\x52\x53\x54\x55\x55\x55\x56\x56\x57\x57\x57\x57\x57\x57\x57\x58\x56\x56\x56\x56\x57\x56\x56\x52\x4C\x45\x42\x47" + "\x51\x56\x57\x57\x53\x45\x30\x28\x27\x27\x27\x28\x27\x29\x28\x26\x25\x26\x28\x26\x25\x25\x29\x26\x27\x29\x27\x26\x26\x26\x25\x26" + "\x4F\x50\x51\x52\x52\x54\x55\x55\x55\x55\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x56\x50\x42\x30\x2A\x2B\x2D\x2D" + "\x2E\x35\x48\x54\x57\x57\x4C\x34\x27\x27\x29\x2A\x28\x26\x29\x26\x25\x26\x28\x25\x27\x25\x25\x25\x27\x25\x27\x25\x26\x25\x25\x26" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x4C\x37\x2D\x2E\x34\x3B\x3D\x3A" + "\x33\x2F\x2F\x3E\x54\x56\x56\x48\x2C\x29\x27\x25\x27\x26\x29\x25\x27\x26\x28\x28\x28\x26\x24\x25\x25\x24\x29\x28\x25\x25\x25\x24" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x53\x35\x2D\x32\x46\x51\x54\x55\x53" + "\x4E\x3F\x2D\x2D\x43\x56\x56\x56\x39\x2A\x28\x27\x2A\x28\x29\x26\x27\x28\x28\x29\x26\x26\x27\x28\x27\x26\x25\x28\x26\x26\x27\x25" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x56\x57\x55\x3B\x2E\x36\x4E\x57\x57\x56\x55\x54" + "\x56\x56\x43\x2E\x31\x4A\x56\x57\x4E\x2F\x28\x25\x27\x27\x28\x27\x26\x27\x27\x26\x26\x26\x26\x26\x28\x25\x26\x26\x27\x27\x26\x27" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x56\x57\x4E\x32\x2F\x45\x56\x57\x56\x4E\x42\x3D" + "\x44\x50\x53\x36\x2D\x3E\x54\x57\x54\x3D\x2A\x27\x27\x26\x27\x27\x27\x27\x27\x26\x27\x27\x27\x23\x25\x27\x27\x29\x26\x26\x25\x26" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x49\x2E\x33\x4F\x57\x57\x53\x3D\x2F\x2C" + "\x2F\x41\x55\x43\x2E\x39\x52\x56\x56\x48\x2E\x28\x28\x28\x26\x28\x28\x2A\x27\x25\x29\x29\x26\x25\x27\x28\x27\x27\x27\x26\x26\x25" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x56\x56\x56\x56\x57\x41\x2B\x33\x54\x56\x56\x55\x46\x34\x32" + "\x38\x49\x56\x4C\x2F\x36\x4E\x56\x57\x52\x39\x2A\x26\x26\x27\x28\x28\x28\x26\x27\x28\x28\x28\x25\x25\x25\x24\x26\x25\x23\x26\x27" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x56\x56\x57\x56\x57\x44\x2D\x33\x52\x57\x56\x56\x55\x50\x4D" + "\x52\x56\x57\x4A\x2E\x36\x4F\x57\x57\x56\x42\x2B\x28\x27\x28\x28\x24\x25\x27\x28\x26\x27\x27\x28\x27\x26\x25\x26\x26\x25\x27\x28" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x49\x31\x2F\x4B\x57\x57\x58\x57\x56\x56" + "\x57\x57\x57\x40\x2E\x38\x52\x57\x57\x57\x4D\x2F\x28\x27\x28\x29\x26\x27\x27\x27\x26\x27\x28\x28\x27\x26\x28\x29\x26\x24\x27\x24" + "\x4F\x50\x51\x52\x53\x54\x55\x55\x56\x56\x56\x56\x56\x53\x57\x57\x57\x57\x57\x57\x56\x56\x51\x37\x2D\x39\x52\x56\x57\x57\x57\x57" + "\x57\x57\x4C\x31\x2F\x41\x56\x57\x56\x56\x56\x39\x27\x26\x27\x24\x24\x26\x27\x27\x27\x26\x26\x25\x25\x26\x28\x28\x27\x27\x26\x27" + "\x4F\x50\x51\x52\x53\x54\x54\x55\x55\x56\x56\x56\x55\x49\x43\x50\x57\x57\x56\x56\x56\x56\x56\x48\x2C\x30\x3F\x51\x57\x57\x57\x57" + "\x56\x4C\x37\x2C\x36\x50\x57\x57\x57\x57\x57\x44\x29\x27\x27\x25\x23\x26\x27\x27\x26\x26\x27\x26\x27\x26\x26\x25\x28\x27\x27\x26" + "\x4F\x50\x51\x52\x53\x54\x54\x55\x55\x56\x56\x56\x56\x4C\x35\x35\x46\x55\x56\x56\x57\x57\x57\x55\x3B\x2C\x2E\x38\x46\x4E\x50\x4C" + "\x41\x32\x2C\x2B\x4B\x57\x57\x57\x57\x57\x57\x4F\x2D\x29\x27\x26\x26\x27\x29\x27\x28\x26\x25\x25\x25\x25\x25\x24\x25\x26\x27\x26" + "\x4F\x50\x51\x52\x53\x54\x54\x55\x55\x56\x56\x56\x57\x55\x40\x2C\x2B\x35\x48\x52\x56\x57\x57\x57\x54\x44\x31\x29\x2C\x2C\x2D\x2C" + "\x2C\x2E\x38\x4B\x57\x57\x57\x57\x57\x57\x57\x57\x33\x28\x28\x27\x23\x26\x27\x24\x27\x27\x26\x26\x25\x27\x27\x28\x26\x26\x26\x27" + "\x4F\x51\x52\x53\x53\x54\x54\x55\x55\x56\x56\x56\x57\x57\x4A\x30\x2A\x2A\x2E\x38\x48\x53\x57\x57\x57\x55\x4A\x3F\x37\x31\x31\x35" + "\x3A\x44\x50\x56\x57\x56\x56\x57\x56\x57\x56\x4F\x2F\x28\x27\x27\x25\x28\x27\x27\x29\x28\x27\x27\x28\x26\x25\x27\x26\x28\x27\x26" + "\x50\x51\x52\x53\x53\x54\x55\x55\x55\x56\x56\x56\x57\x57\x54\x41\x2D\x2B\x29\x27\x2F\x38\x48\x55\x57\x57\x56\x55\x52\x4F\x4E\x53" + "\x53\x55\x57\x57\x57\x56\x56\x57\x56\x52\x40\x32\x29\x29\x29\x27\x25\x26\x26\x26\x26\x28\x27\x29\x27\x28\x25\x26\x26\x27\x26\x27" + "\x50\x51\x52\x53\x54\x54\x55\x55\x55\x56\x56\x56\x57\x57\x56\x56\x52\x42\x36\x2F\x2B\x2B\x2C\x31\x41\x4C\x52\x55\x57\x57\x57\x57" + "\x56\x56\x56\x57\x56\x56\x54\x51\x3F\x2F\x2A\x26\x27\x26\x26\x26\x27\x27\x28\x28\x26\x26\x28\x26\x26\x27\x26\x28\x27\x27\x27\x28" + "\x50\x51\x52\x53\x54\x54\x55\x55\x55\x56\x56\x56\x57\x57\x57\x56\x57\x56\x50\x46\x38\x2D\x29\x29\x2A\x30\x38\x40\x49\x4E\x52\x56" + "\x57\x57\x56\x56\x4F\x48\x3F\x35\x2A\x2B\x2B\x2D\x28\x25\x26\x26\x27\x27\x27\x27\x26\x24\x28\x25\x25\x24\x27\x29\x26\x28\x28\x26" + "\x51\x52\x52\x53\x54\x54\x55\x55\x55\x56\x56\x56\x57\x57\x57\x56\x57\x57\x57\x56\x51\x49\x3D\x31\x2D\x2B\x2A\x29\x2F\x31\x35\x3A" + "\x3D\x3B\x39\x37\x33\x2E\x29\x2A\x2B\x37\x46\x47\x28\x25\x27\x27\x28\x26\x25\x26\x28\x26\x28\x25\x26\x24\x28\x29\x25\x28\x26\x25" + "\x51\x52\x53\x53\x54\x54\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x56\x56\x57\x57\x54\x4A\x3E\x35\x2E\x28\x29\x28\x29" + "\x29\x2A\x2C\x2B\x2B\x2F\x35\x3E\x4E\x56\x56\x45\x2B\x25\x27\x29\x25\x26\x27\x25\x25\x26\x26\x27\x26\x22\x25\x26\x26\x27\x25\x27" + "\x51\x52\x53\x54\x54\x55\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x50\x48\x41\x3E\x38\x34" + "\x31\x33\x38\x3B\x41\x48\x4F\x55\x57\x57\x55\x38\x2A\x28\x28\x27\x25\x26\x26\x22\x27\x27\x27\x26\x25\x25\x26\x26\x27\x27\x27\x28" + "\x52\x52\x53\x54\x54\x55\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x54\x52\x51" + "\x4F\x4F\x52\x53\x55\x56\x57\x57\x57\x57\x49\x2F\x26\x26\x29\x26\x26\x26\x27\x27\x27\x25\x26\x28\x26\x27\x27\x27\x27\x27\x27\x27" + "\x52\x53\x53\x54\x54\x55\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56" + "\x57\x56\x56\x56\x56\x56\x56\x57\x51\x43\x30\x2A\x24\x24\x25\x25\x27\x28\x25\x26\x26\x27\x28\x25\x25\x27\x27\x26\x27\x27\x29\x26" + "\x52\x53\x54\x54\x55\x55\x55\x56\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56" + "\x57\x57\x56\x56\x56\x56\x52\x48\x38\x2C\x27\x27\x25\x25\x25\x25\x25\x27\x26\x26\x25\x26\x28\x27\x27\x26\x25\x26\x28\x27\x25\x26" + "\x53\x53\x54\x54\x55\x55\x55\x56\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x57\x57\x57\x57\x57\x57" + "\x58\x57\x57\x57\x52\x43\x35\x2E\x27\x28\x29\x27\x23\x25\x26\x27\x27\x27\x27\x28\x26\x25\x26\x24\x25\x25\x25\x25\x26\x28\x27\x27" + "\x53\x54\x54\x55\x55\x54\x54\x55\x56\x56\x56\x57\x56\x56\x56\x57\x56\x56\x57\x57\x56\x57\x57\x57\x56\x56\x56\x58\x56\x56\x56\x54" + "\x51\x4E\x46\x39\x2D\x2B\x29\x28\x27\x27\x28\x28\x26\x26\x26\x28\x2A\x28\x28\x28\x26\x26\x26\x26\x26\x28\x28\x28\x25\x28\x29\x25" + "\x53\x54\x53\x54\x55\x52\x4C\x4B\x48\x48\x4B\x4F\x51\x52\x55\x56\x56\x56\x57\x57\x56\x56\x57\x56\x57\x56\x54\x50\x4E\x49\x43\x3F" + "\x37\x30\x2A\x2A\x28\x27\x28\x28\x27\x25\x26\x27\x26\x27\x26\x27\x27\x25\x27\x28\x27\x26\x26\x27\x26\x27\x25\x26\x26\x27\x27\x26" + "\x54\x54\x54\x54\x44\x37\x32\x31\x2D\x2D\x2E\x32\x35\x34\x34\x38\x3C\x40\x43\x45\x44\x43\x43\x41\x3D\x38\x36\x33\x33\x2F\x2A\x2B" + "\x27\x29\x28\x26\x29\x27\x27\x27\x29\x27\x27\x28\x26\x25\x26\x28\x27\x25\x25\x27\x28\x26\x26\x25\x26\x26\x23\x25\x28\x26\x28\x27" + "\x53\x53\x53\x3E\x2B\x25\x23\x25\x24\x27\x28\x28\x27\x27\x2A\x2A\x27\x27\x27\x29\x29\x28\x29\x29\x28\x2A\x29\x28\x27\x26\x27\x29" + "\x26\x26\x26\x26\x27\x26\x26\x26\x28\x28\x29\x29\x27\x25\x25\x28\x27\x27\x29\x26\x29\x26\x27\x27\x27\x27\x26\x26\x25\x25\x26\x26" + "\x53\x54\x49\x30\x26\x24\x23\x25\x25\x28\x28\x26\x25\x27\x27\x25\x26\x28\x28\x26\x27\x28\x27\x26\x27\x26\x26\x27\x25\x24\x25\x27" + "\x25\x26\x27\x27\x29\x27\x26\x26\x27\x2A\x29\x27\x25\x24\x25\x28\x27\x26\x27\x28\x29\x26\x27\x26\x26\x28\x28\x26\x26\x26\x26\x26" + "\x55\x54\x40\x29\x26\x24\x24\x27\x25\x26\x27\x29\x26\x24\x26\x26\x27\x29\x29\x25\x26\x28\x28\x26\x28\x27\x28\x27\x25\x28\x27\x27" + "\x27\x27\x25\x25\x26\x27\x27\x26\x25\x28\x26\x24\x27\x25\x26\x27\x27\x27\x27\x28\x24\x27\x27\x28\x26\x26\x29\x26\x25\x26\x26\x26" + "\x54\x52\x3A\x29\x25\x24\x26\x26\x26\x25\x26\x26\x27\x27\x27\x28\x29\x2A\x28\x29\x27\x26\x27\x27\x27\x27\x28\x29\x28\x27\x27\x28" + "\x27\x28\x25\x23\x27\x26\x28\x26\x23\x25\x26\x26\x28\x26\x26\x25\x26\x27\x27\x27\x24\x24\x26\x27\x27\x27\x28\x28\x27\x27\x26\x27" + "\x55\x52\x38\x29\x24\x26\x28\x26\x25\x25\x27\x27\x26\x26\x27\x26\x29\x27\x27\x27\x27\x25\x25\x27\x27\x26\x27\x28\x28\x27\x28\x28" + "\x27\x27\x24\x25\x28\x27\x27\x27\x26\x27\x28\x27\x27\x25\x26\x26\x28\x27\x28\x27\x26\x25\x25\x26\x25\x26\x26\x26\x27\x28\x26\x24" + "\x55\x51\x38\x28\x24\x26\x25\x25\x26\x25\x25\x27\x29\x28\x27\x24\x27\x28\x28\x26\x23\x25\x27\x27\x25\x28\x27\x26\x29\x28\x27\x2A" + "\x28\x28\x26\x27\x26\x27\x27\x27\x28\x26\x26\x24\x23\x25\x25\x27\x27\x26\x27\x29\x27\x27\x27\x29\x25\x28\x26\x24\x26\x28\x28\x24" + "\x56\x53\x39\x28\x28\x28\x28\x27\x28\x27\x27\x27\x2A\x2A\x29\x29\x27\x27\x27\x26\x25\x26\x27\x28\x28\x26\x27\x28\x26\x27\x28\x27" + "\x26\x25\x26\x26\x24\x26\x26\x27\x25\x25\x23\x25\x27\x26\x28\x27\x27\x27\x26\x26\x29\x27\x27\x27\x28\x26\x26\x25\x26\x25\x26\x26" + "\x56\x55\x3B\x27\x26\x24\x26\x27\x24\x26\x27\x28\x25\x27\x27\x27\x29\x27\x26\x26\x27\x26\x27\x27\x28\x28\x28\x28\x25\x28\x27\x26" + "\x26\x25\x26\x27\x26\x25\x27\x28\x27\x25\x25\x26\x28\x26\x26\x28\x26\x26\x26\x27\x26\x26\x27\x26\x27\x26\x28\x27\x27\x27\x26\x25" + "\x56\x56\x43\x2C\x25\x25\x27\x29\x27\x27\x28\x28\x26\x25\x27\x23\x23\x27\x26\x28\x27\x26\x27\x27\x28\x26\x27\x28\x26\x2A\x29\x27" + "\x29\x29\x29\x27\x28\x28\x29\x27\x28\x27\x26\x26\x25\x27\x28\x28\x26\x27\x25\x25\x25\x27\x29\x26\x25\x28\x28\x25\x28\x27\x27\x26" + "\x55\x56\x4B\x2E\x25\x28\x29\x28\x25\x25\x27\x28\x25\x25\x27\x27\x23\x24\x25\x26\x26\x26\x26\x24\x25\x28\x28\x27\x25\x27\x27\x24" + "\x26\x29\x26\x23\x27\x28\x27\x28\x26\x26\x27\x27\x26\x26\x27\x25\x28\x27\x27\x26\x29\x28\x27\x24\x29\x28\x27\x27\x26\x28\x28\x25" + "\x55\x56\x51\x32\x27\x28\x27\x27\x26\x29\x26\x26\x26\x26\x28\x27\x26\x28\x28\x29\x25\x27\x29\x26\x26\x27\x29\x27\x26\x28\x27\x25" + "\x25\x25\x26\x27\x25\x27\x28\x26\x26\x26\x28\x2A\x29\x25\x24\x24\x26\x28\x28\x28\x26\x26\x26\x25\x26\x24\x26\x27\x26\x27\x28\x27" + "\x56\x56\x55\x39\x29\x29\x28\x29\x28\x26\x26\x27\x26\x26\x25\x27\x25\x25\x26\x25\x26\x27\x28\x26\x27\x26\x28\x27\x26\x27\x27\x26" + "\x26\x27\x27\x27\x27\x28\x28\x26\x29\x28\x26\x29\x29\x28\x27\x28\x27\x29\x27\x26\x25\x26\x25\x26\x24\x25\x26\x27\x26\x25\x26\x28" + "\x57\x57\x57\x4A\x29\x27\x27\x27\x27\x28\x29\x29\x27\x29\x25\x25\x25\x25\x27\x29\x26\x26\x27\x26\x28\x28\x27\x25\x29\x29\x27\x27" + "\x25\x26\x26\x24\x27\x27\x26\x26\x26\x28\x27\x27\x27\x26\x27\x27\x27\x28\x27\x26\x27\x26\x26\x2A\x28\x26\x26\x25\x28\x26\x27\x26" + "\x57\x57\x57\x55\x32\x28\x25\x24\x28\x28\x27\x27\x27\x27\x27\x26\x26\x27\x28\x26\x25\x27\x29\x27\x27\x28\x28\x24\x29\x27\x26\x26" + "\x25\x28\x26\x25\x29\x26\x26\x26\x26\x29\x28\x25\x25\x25\x27\x28\x26\x26\x26\x28\x28\x25\x27\x28\x28\x28\x28\x24\x26\x26\x25\x25" + "\x57\x57\x56\x56\x43\x2B\x29\x28\x2A\x29\x29\x2A\x28\x27\x28\x27\x27\x27\x27\x29\x26\x2A\x28\x24\x26\x28\x28\x25\x24\x26\x26\x26" + "\x26\x28\x28\x27\x28\x27\x25\x25\x26\x27\x27\x23\x24\x25\x25\x26\x28\x27\x27\x27\x26\x26\x28\x25\x27\x27\x28\x26\x27\x27\x26\x25" + "\x56\x57\x56\x56\x54\x49\x47\x44\x42\x40\x3F\x3D\x37\x39\x38\x36\x37\x32\x30\x2F\x2A\x2C\x2B\x29\x2B\x28\x28\x26\x26\x26\x27\x28" + "\x27\x25\x26\x26\x24\x25\x25\x26\x29\x27\x28\x27\x28\x26\x27\x26\x26\x28\x27\x27\x27\x26\x27\x25\x28\x26\x29\x27\x27\x26\x27\x28" + "\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x55\x54\x53\x4F\x4C\x48\x44\x41\x3D\x3B\x36\x30\x2C\x2B\x2B\x2A\x2A\x2A" + "\x28\x27\x26\x27\x28\x26\x26\x27\x28\x28\x28\x26\x25\x25\x27\x27\x27\x25\x26\x26\x28\x27\x28\x28\x28\x28\x27\x27\x27\x28\x29\x27" + "\x56\x56\x57\x57\x56\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x57\x56\x57\x56\x56\x54\x53\x51\x50\x4D\x49\x43\x3D\x34\x2E" + "\x2D\x2A\x26\x25\x28\x27\x28\x26\x28\x29\x27\x25\x26\x27\x27\x27\x24\x26\x28\x26\x27\x26\x25\x29\x28\x29\x27\x26\x27\x27\x26\x26" + "\x56\x56\x57\x57\x56\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x57\x56\x56\x57\x57\x57\x57\x57\x57\x56\x57\x58\x56\x57\x56" + "\x4B\x42\x3A\x36\x2F\x2C\x29\x28\x27\x27\x24\x23\x26\x27\x26\x28\x27\x27\x26\x25\x25\x24\x25\x27\x27\x27\x28\x28\x29\x25\x22\x26" + "\x57\x57\x57\x57\x56\x57\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x57" + "\x56\x57\x56\x52\x4C\x46\x41\x3C\x34\x2C\x28\x27\x27\x28\x26\x27\x28\x26\x27\x26\x25\x25\x26\x27\x28\x26\x25\x26\x25\x27\x28\x26" + "\x57\x57\x57\x57\x57\x57\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x57\x57\x57" + "\x56\x57\x57\x56\x56\x57\x55\x53\x50\x4B\x41\x35\x2B\x29\x28\x26\x26\x27\x26\x26\x27\x28\x29\x29\x2B\x28\x27\x28\x29\x28\x28\x28" + "\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x57\x57\x57\x57\x56\x56\x56\x57\x57\x57\x57\x57\x56\x57\x57\x56\x57\x57\x57\x57" + "\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x57\x52\x46\x38\x31\x2C\x2A\x27\x26\x25\x27\x27\x26\x25\x24\x27\x28\x23\x26\x28\x27" + "\x56\x55\x55\x56\x58\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57" + "\x57\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x57\x55\x4F\x46\x3F\x34\x2B\x29\x2B\x28\x26\x25\x28\x28\x27\x24\x26\x28\x25" + "\x39\x38\x37\x37\x39\x3B\x3F\x40\x44\x47\x49\x4A\x4C\x50\x53\x56\x57\x57\x57\x58\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57" + "\x57\x57\x57\x57\x56\x56\x56\x56\x56\x56\x56\x58\x56\x57\x57\x57\x56\x54\x50\x4B\x3F\x31\x29\x27\x26\x26\x26\x27\x23\x29\x27\x27" + "\x2A\x29\x26\x2A\x28\x27\x26\x28\x27\x2A\x29\x29\x2D\x2D\x2C\x2F\x35\x3C\x41\x47\x4B\x4E\x50\x52\x54\x56\x57\x57\x57\x57\x57\x57" + "\x56\x57\x57\x57\x56\x56\x56\x56\x56\x56\x56\x56\x56\x56\x56\x56\x56\x57\x57\x56\x56\x54\x4A\x3D\x32\x2E\x27\x24\x28\x26\x26\x25" + "\x26\x25\x24\x24\x27\x26\x26\x25\x26\x24\x28\x2A\x26\x26\x26\x2A\x27\x29\x29\x2B\x2B\x31\x34\x38\x3C\x43\x4A\x4E\x54\x56\x57\x57" + "\x57\x57\x57\x57\x57\x57\x56\x56\x57\x56\x56\x56\x56\x56\x56\x56\x57\x56\x56\x56\x57\x57\x57\x56\x4C\x41\x35\x2D\x2B\x28\x27\x27" + "\x27\x26\x28\x28\x25\x25\x25\x26\x26\x25\x29\x2A\x28\x28\x27\x26\x26\x27\x25\x26\x27\x29\x28\x29\x2A\x2A\x2F\x34\x38\x3C\x46\x4D" + "\x53\x57\x56\x56\x57\x57\x57\x57\x57\x56\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x55\x51\x48\x38\x2C\x28\x27" + "\x27\x25\x25\x27\x26\x26\x28\x26\x27\x26\x25\x25\x26\x27\x27\x25\x26\x25\x25\x26\x27\x27\x27\x27\x29\x27\x28\x2B\x29\x28\x29\x2B" + "\x2D\x34\x3F\x4A\x51\x55\x56\x57\x57\x57\x57\x57\x56\x56\x57\x57\x56\x57\x57\x56\x57\x57\x57\x57\x56\x56\x56\x56\x56\x4E\x3F\x33" + "\x25\x26\x27\x28\x27\x27\x27\x25\x27\x27\x25\x27\x25\x27\x27\x27\x27\x29\x28\x28\x25\x28\x28\x26\x27\x26\x28\x27\x26\x28\x27\x27" + "\x28\x28\x2A\x2C\x35\x3E\x46\x4E\x55\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x57\x57\x57\x57\x57\x57\x56\x57\x57\x57\x56\x51"; + +BYTE TEST_64X64_BLUE_PLANE_RLE[3724] = + "\x53\x27\x23\x23\x24\x25\xF0\x28\x37\x4A\x47\x41\x3D\x38\x33\x2E\x2A\x28\x27\x26\x27\x27\xF0\x27\x25\x24\x26\x28\x26\x27\x28\x28" + "\x26\x28\x28\x2A\x26\x27\xF0\x27\x28\x27\x27\x25\x26\x24\x25\x27\x28\x27\x28\x27\x27\x25\xB0\x26\x27\x27\x26\x27\x27\x27\x26\x25" + "\x26\x23\xF0\x16\x0C\x0C\x06\x02\x06\x04\x04\x02\x0A\x16\x1E\x2A\x2E\x34\xF0\x3A\x3E\x3C\x30\x20\x12\x0A\x08\x06\x04\x06\x02\x01" + "\x00\x01\xF0\x05\x03\x02\x00\x00\x03\x02\x00\x00\x00\x03\x01\x00\x02\x04\xF0\x02\x04\x00\x01\x01\x03\x00\x00\x00\x03\x00\x01\x00" + "\x02\x00\x40\x00\x04\x02\x08\xF0\x38\x48\x38\x2C\x1E\x10\x0C\x04\x04\x04\x02\x00\x02\x06\x0A\xF0\x0E\x12\x1C\x2C\x3E\x4E\x4C\x3C" + "\x28\x1E\x14\x0C\x04\x02\x02\xF0\x04\x04\x00\x01\x07\x05\x02\x04\x00\x00\x02\x02\x08\x06\x02\xF0\x01\x09\x03\x04\x00\x04\x03\x08" + "\x02\x02\x03\x02\x07\x03\x00\x40\x01\x00\x04\x01\xC4\x02\x06\x16\x28\x3A\x34\x2E\x2A\x16\x06\x01\x00\x13\x02\xF0\x00\x0A\x1C\x32" + "\x3A\x38\x34\x2E\x24\x14\x0C\x01\x03\x00\x04\xF0\x00\x00\x05\x03\x03\x01\x02\x00\x01\x03\x00\x04\x00\x04\x04\xE0\x02\x02\x00\x04" + "\x02\x04\x00\x04\x01\x05\x06\x00\x03\x01\x04\x96\x02\x14\x22\x2A\x34\x20\x02\x02\x00\xF0\x01\x02\x00\x00\x00\x0A\x14\x20\x2A\x32" + "\x3A\x34\x22\x10\x0A\xF0\x00\x04\x00\x02\x02\x04\x04\x03\x09\x05\x01\x01\x03\x02\x05\xF0\x07\x03\x04\x03\x03\x03\x01\x00\x04\x02" + "\x06\x01\x02\x00\x01\xDB\x00\x00\x02\x02\x01\x00\x00\x02\x0A\x08\x00\x01\x00\xF0\x03\x01\x00\x04\x0A\x12\x20\x3C\x4A\x32\x24\x10" + "\x02\x00\x04\xF0\x05\x07\x01\x08\x00\x06\x06\x02\x00\x00\x02\x01\x00\x03\x01\xA0\x00\x02\x02\x01\x08\x0A\x00\x01\x02\x0A\x04\x78" + "\x02\x01\x01\x02\x00\x02\x00\x57\x02\x01\x01\x01\x00\xF0\x02\x0A\x22\x34\x34\x22\x0E\x02\x06\x08\x02\x03\x01\x03\x01\xF3\x04\x00" + "\x00\x02\x04\x01\x00\x00\x04\x03\x03\x04\x03\x07\x03\x04\x23\x01\x00\x28\x01\x00\xF0\x02\x00\x00\x00\x01\x02\x00\x01\x09\x15\x23" + "\x29\x1F\x0B\x00\xF0\x0A\x1C\x32\x2E\x0E\x00\x00\x02\x02\x06\x04\x06\x02\x01\x03\xF0\x01\x02\x01\x00\x01\x04\x02\x04\x02\x01\x03" + "\x02\x02\x01\x01\x05\x3B\x02\x02\x00\xF0\x01\x02\x02\x00\x00\x01\x0B\x27\x43\x43\x33\x29\x33\x45\x41\xE3\x1D\x05\x08\x24\x38\x18" + "\x00\x00\x04\x04\x02\x05\x02\x00\xD0\x01\x04\x00\x07\x01\x00\x07\x00\x01\x00\x01\x00\x00\x04\x7D\x02\x00\x00\x00\x02\x02\x00\xF0" + "\x13\x31\x29\x03\x14\x20\x20\x1A\x0A\x0B\x31\x2B\x05\x01\x14\xF0\x28\x0A\x04\x03\x09\x01\x00\x00\x01\x04\x00\x00\x06\x02\x02\xA0" + "\x01\x00\x03\x01\x04\x06\x01\x00\x00\x03\x71\xF0\x05\x2D\x13\x0A\x30\x3A\x32\x30\x32\x36\x20\x03\x21\x21\x00\xF0\x00\x1C\x1A\x02" + "\x02\x04\x06\x04\x00\x02\x00\x04\x00\x02\x03\xB0\x00\x06\x06\x04\x04\x07\x00\x02\x02\x04\x02\x41\xF0\x01\x00\x01\x2F\x0D\x12\x38" + "\x22\x0C\x04\x00\x02\x10\x2E\x2C\xF0\x02\x23\x17\x00\x02\x2A\x0A\x00\x03\x05\x01\x01\x02\x01\x01\xE0\x01\x05\x00\x00\x01\x03\x02" + "\x01\x02\x03\x02\x02\x01\x04\x61\xF0\x0D\x11\x02\x1E\x10\x00\x01\x0F\x25\x2D\x23\x0B\x20\x10\x07\xF0\x17\x03\x00\x0C\x1C\x04\x04" + "\x00\x01\x01\x00\x02\x00\x00\x00\x93\x02\x02\x02\x05\x05\x04\x02\x06\x01\x41\xF0\x02\x00\x09\x07\x08\x14\x02\x00\x05\x21\x25\x21" + "\x29\x1D\x04\xF0\x1A\x02\x09\x03\x01\x04\x16\x08\x02\x02\x04\x01\x02\x02\x06\xE0\x00\x01\x04\x04\x01\x04\x04\x02\x00\x03\x02\x00" + "\x02\x01\x11\x13\x01\xF0\x00\x0F\x05\x00\x0A\x01\x01\x04\x12\x0A\x0C\x12\x10\x02\x12\xF0\x02\x05\x07\x00\x02\x14\x16\x04\x03\x03" + "\x02\x00\x00\x03\x01\xD0\x04\x01\x01\x04\x00\x03\x05\x05\x01\x03\x05\x00\x04\x31\xF0\x02\x00\x00\x06\x04\x00\x03\x02\x00\x02\x1E" + "\x38\x36\x34\x1A\xF0\x02\x03\x01\x00\x02\x02\x00\x08\x12\x02\x04\x02\x02\x00\x07\xF0\x05\x02\x02\x03\x01\x01\x06\x04\x02\x02\x00" + "\x02\x04\x02\x02\x11\xF0\x02\x02\x00\x00\x01\x0A\x08\x07\x0D\x00\x02\x04\x04\x0C\x12\xF0\x0A\x02\x00\x13\x00\x04\x06\x00\x00\x02" + "\x16\x08\x00\x00\x00\xF0\x02\x04\x04\x00\x01\x00\x00\x02\x00\x00\x00\x06\x06\x00\x01\x20\x00\x07\x0C\x37\x01\x07\x00\xF0\x10\x0C" + "\x03\x23\x09\x01\x01\x00\x02\x02\x00\x00\x15\x1D\x02\xF0\x12\x08\x00\x01\x01\x12\x14\x01\x01\x01\x09\x03\x01\x00\x00\xC0\x02\x01" + "\x03\x05\x03\x00\x00\x01\x02\x06\x01\x06\x06\xF0\x01\x00\x01\x00\x00\x00\x01\x13\x27\x0D\x00\x00\x01\x01\x00\x83\x00\x0A\x22\x01" + "\x11\x25\x09\x00\xF0\x01\x15\x29\x09\x0E\x1E\x02\x00\x02\x02\x02\x16\x04\x02\x00\xF0\x02\x01\x00\x00\x00\x01\x00\x02\x02\x04\x00" + "\x03\x05\x02\x00\x20\x02\x01\x0C\xF0\x02\x06\x1B\x35\x21\x03\x00\x00\x02\x02\x02\x1A\x1E\x07\x21\xC4\x31\x21\x11\x0D\x15\x29\x33" + "\x15\x01\x2A\x0E\x00\xF0\x16\x08\x04\x00\x02\x06\x02\x04\x00\x04\x00\x03\x01\x03\x01\x60\x01\x01\x05\x01\x00\x00\x0C\xF0\x02\x12" + "\x16\x11\x35\x3F\x1B\x07\x01\x00\x00\x04\x32\x30\x06\xB5\x1D\x33\x43\x45\x3F\x29\x07\x18\x40\x18\x00\xF0\x10\x0C\x01\x02\x02\x05" + "\x01\x03\x05\x01\x02\x02\x02\x00\x04\x60\x04\x08\x02\x00\x01\x02\x58\x00\x02\x02\x02\x00\xF0\x04\x14\x08\x01\x15\x33\x33\x1B\x07" + "\x00\x00\x06\x22\x32\x2C\xF0\x16\x0A\x08\x12\x1C\x2C\x30\x16\x00\x01\x01\x00\x01\x00\x01\xF0\x0F\x07\x00\x01\x00\x04\x04\x00\x06" + "\x04\x02\x02\x02\x06\x01\x60\x03\x01\x00\x04\x02\x01\x24\x02\x00\x26\x02\x00\xF0\x14\x22\x06\x02\x09\x21\x31\x35\x1D\x03\x00\x04" + "\x18\x2C\x36\x84\x3C\x3A\x3C\x32\x22\x0E\x02\x00\xF0\x09\x2B\x39\x0B\x02\x04\x00\x00\x03\x01\x01\x05\x00\x00\x04\x80\x01\x04\x00" + "\x01\x00\x01\x01\x02\x04\x28\x02\x00\xF0\x04\x2A\x4A\x2E\x1A\x10\x07\x19\x37\x47\x2B\x15\x07\x00\x0A\xF0\x10\x12\x08\x06\x02\x01" + "\x00\x01\x00\x03\x0B\x2D\x45\x2B\x17\xF0\x03\x05\x05\x01\x04\x02\x04\x04\x00\x03\x02\x05\x01\x01\x02\x50\x04\x02\x00\x02\x02\x0E" + "\xF0\x02\x00\x0A\x28\x34\x2E\x1A\x04\x05\x0F\x2D\x37\x33\x29\x1B\xF0\x11\x09\x01\x02\x02\x00\x01\x0D\x1B\x29\x37\x29\x07\x02\x0E" + "\x33\x02\x01\x00\xE0\x01\x01\x00\x03\x00\x01\x01\x05\x02\x02\x01\x02\x02\x03\x3E\x02\x02\x00\xF0\x02\x0E\x20\x32\x38\x28\x10\x06" + "\x09\x1B\x2D\x33\x39\x39\x37\xF0\x33\x37\x39\x3D\x37\x33\x2B\x15\x02\x18\x36\x34\x00\x00\x02\xF0\x02\x02\x01\x03\x01\x04\x04\x00" + "\x00\x02\x00\x02\x00\x01\x00\x20\x03\x01\x44\x00\x00\x02\x00\x25\x02\x00\x23\x02\x00\xF0\x0A\x1C\x34\x46\x3A\x26\x16\x0A\x0D\x0F" + "\x19\x21\x27\x21\x19\xF0\x17\x0F\x02\x18\x28\x46\x3E\x20\x03\x06\x00\x00\x04\x05\x00\xE0\x04\x01\x05\x00\x03\x04\x00\x03\x05\x05" + "\x02\x01\x01\x04\x03\x4C\x02\x00\x02\x00\xF0\x02\x02\x00\x00\x06\x1A\x30\x36\x34\x32\x2A\x20\x16\x10\x12\xF0\x18\x20\x2C\x32\x34" + "\x2E\x12\x02\x01\x19\x01\x06\x02\x03\x00\xF0\x00\x01\x05\x04\x02\x02\x01\x01\x06\x02\x00\x02\x00\x04\x02\x2F\x02\x00\x08\xF0\x02" + "\x0E\x1C\x2A\x2C\x34\x3A\x3C\x38\x34\x30\x28\x1C\x10\x04\xF0\x00\x00\x17\x11\x07\x03\x02\x01\x02\x00\x02\x0A\x00\x03\x01\x90\x04" + "\x02\x04\x02\x02\x00\x00\x00\x01\x3F\x00\x02\x00\x09\xF0\x02\x02\x06\x08\x0A\x10\x0E\x08\x06\x02\x00\x01\x00\x0B\x27\xF0\x31\x09" + "\x03\x03\x07\x01\x02\x04\x03\x01\x01\x04\x04\x05\x01\x70\x00\x00\x01\x00\x00\x04\x01\x9F\x00\x00\x02\x00\x02\x00\x00\x02\x00\x09" + "\x23\x02\x00\xF0\x07\x1D\x31\x2D\x11\x05\x02\x02\x00\x00\x03\x01\x02\x00\x01\xB0\x01\x00\x04\x04\x01\x03\x00\x02\x00\x07\x00\x29" + "\x02\x00\x2B\x02\x00\x33\x01\x01\x00\xF0\x02\x02\x02\x00\x02\x02\x07\x25\x39\x33\x21\x07\x04\x00\x03\xF0\x00\x02\x04\x04\x00\x02" + "\x04\x02\x01\x03\x05\x03\x01\x00\x01\x40\x03\x02\x04\x02\x93\x00\x02\x00\x02\x00\x01\x01\x01\x00\xA4\x01\x01\x01\x00\x01\x01\x00" + "\x00\x01\x00\xF0\x01\x02\x01\x01\x01\x05\x0D\x11\x21\x3B\x49\x2F\x17\x0B\x00\xF0\x01\x01\x02\x06\x02\x00\x02\x06\x02\x02\x00\x00" + "\x02\x00\x04\x80\x02\x06\x06\x06\x01\x00\x04\x03\xF0\x00\x00\x01\x01\x00\x03\x0F\x13\x1B\x1B\x15\x0F\x09\x07\x01\x24\x01\x00\xF0" + "\x01\x00\x01\x02\x00\x03\x0F\x0F\x19\x25\x29\x33\x3B\x37\x1D\xF0\x09\x07\x01\x00\x00\x03\x03\x01\x00\x02\x00\x01\x05\x05\x01\xD0" + "\x00\x02\x00\x00\x02\x00\x01\x05\x03\x02\x01\x03\x02\xF0\x02\x00\x02\x00\x21\x35\x33\x33\x35\x35\x39\x39\x37\x3B\x41\xF0\x3B\x33" + "\x2B\x27\x23\x23\x25\x27\x29\x33\x3B\x3B\x39\x35\x33\xF0\x31\x27\x1F\x0D\x03\x07\x02\x00\x01\x01\x04\x04\x02\x02\x00\xF0\x03\x00" + "\x02\x00\x00\x03\x01\x02\x00\x00\x03\x00\x01\x03\x01\x40\x04\x01\x02\x02\xF0\x01\x01\x01\x2B\x31\x23\x1D\x17\x11\x0B\x0B\x13\x1B" + "\x19\x13\xF0\x1B\x29\x31\x37\x37\x35\x35\x33\x2F\x29\x1B\x19\x15\x17\x11\x83\x05\x03\x01\x05\x03\x00\x03\x01\xF0\x02\x04\x02\x02" + "\x00\x01\x00\x00\x04\x08\x01\x02\x00\x02\x04\x80\x02\x02\x06\x02\x05\x01\x03\x01\xF0\x00\x02\x13\x1B\x09\x01\x00\x00\x02\x02\x00" + "\x03\x03\x00\x05\xE3\x09\x01\x02\x02\x05\x03\x00\x03\x05\x01\x07\x05\x01\x03\xF0\x01\x00\x02\x02\x04\x02\x00\x00\x01\x04\x00\x03" + "\x03\x01\x00\xF0\x00\x00\x01\x03\x04\x00\x00\x00\x01\x01\x02\x04\x00\x02\x02\x20\x00\x00\xF0\x04\x00\x11\x0D\x00\x00\x02\x04\x00" + "\x03\x01\x06\x02\x05\x01\x13\x02\xF0\x01\x01\x00\x02\x00\x02\x02\x04\x00\x00\x08\x04\x00\x04\x02\xF0\x03\x03\x05\x00\x02\x00\x03" + "\x03\x05\x05\x04\x02\x02\x01\x00\xF0\x02\x00\x00\x09\x02\x00\x04\x00\x03\x02\x00\x01\x00\x00\x00\xF0\x01\x03\x0B\x00\x01\x00\x04" + "\x01\x02\x01\x01\x05\x02\x06\x02\xF0\x04\x04\x02\x01\x08\x02\x03\x01\x02\x01\x00\x00\x04\x06\x01\xF0\x00\x02\x00\x02\x00\x03\x02" + "\x01\x02\x00\x03\x05\x00\x04\x02\xF0\x02\x00\x03\x01\x00\x00\x01\x00\x05\x01\x01\x02\x02\x01\x04\x40\x04\x02\x00\x02\xF0\x02\x00" + "\x03\x00\x01\x04\x04\x00\x01\x00\x02\x02\x01\x01\x00\xF0\x03\x00\x05\x01\x03\x00\x01\x03\x00\x00\x01\x01\x01\x00\x00\xF0\x02\x00" + "\x00\x01\x01\x04\x02\x02\x01\x02\x06\x04\x04\x02\x01\xF0\x01\x00\x02\x04\x00\x02\x00\x04\x02\x01\x01\x03\x01\x03\x03\x40\x00\x02" + "\x00\x05\xF0\x00\x01\x00\x01\x00\x00\x05\x01\x02\x00\x03\x00\x06\x04\x00\xF0\x03\x03\x02\x02\x01\x07\x00\x04\x00\x03\x04\x00\x03" + "\x02\x02\xF0\x01\x04\x02\x02\x04\x04\x03\x00\x00\x00\x04\x01\x03\x05\x07\xF0\x00\x01\x02\x01\x01\x01\x04\x02\x04\x04\x06\x00\x04" + "\x00\x03\x40\x01\x00\x04\x00\x83\x02\x04\x02\x00\x08\x04\x06\x04\xF0\x00\x02\x04\x04\x0A\x00\x01\x01\x00\x04\x02\x00\x02\x06\x03" + "\xF0\x00\x04\x05\x01\x02\x05\x03\x05\x00\x01\x03\x01\x01\x00\x05\xF0\x01\x05\x02\x08\x02\x06\x00\x00\x02\x01\x05\x04\x00\x00\x03" + "\x80\x06\x03\x00\x02\x00\x05\x03\x04\xF0\x00\x04\x04\x01\x03\x07\x03\x00\x07\x01\x00\x02\x09\x05\x03\xF0\x03\x04\x00\x01\x00\x04" + "\x00\x00\x01\x00\x04\x02\x00\x01\x02\xF0\x01\x01\x00\x00\x00\x02\x04\x01\x02\x02\x04\x00\x04\x02\x02\xF0\x00\x03\x02\x01\x01\x00" + "\x02\x05\x01\x00\x01\x01\x00\x04\x04\x40\x02\x04\x00\x01\xF0\x00\x02\x10\x0A\x01\x02\x02\x04\x06\x02\x02\x00\x02\x03\x00\x64\x07" + "\x0B\x00\x00\x04\x00\xF0\x03\x01\x00\x02\x04\x04\x02\x06\x08\x06\x00\x04\x06\x04\x01\xF0\x02\x04\x02\x00\x05\x02\x04\x00\x00\x02" + "\x01\x03\x01\x02\x04\x90\x00\x03\x04\x00\x03\x02\x00\x02\x02\xF0\x01\x00\x10\x04\x00\x06\x04\x01\x03\x03\x01\x00\x01\x00\x00\xF0" + "\x08\x00\x05\x01\x03\x01\x00\x01\x05\x05\x04\x02\x01\x01\x05\xF0\x03\x05\x05\x00\x05\x07\x01\x00\x03\x02\x03\x01\x02\x02\x02\xF0" + "\x01\x01\x05\x04\x00\x04\x02\x08\x02\x03\x03\x08\x00\x01\x04\x40\x03\x02\x02\x01\xF0\x00\x00\x0C\x08\x04\x00\x03\x01\x02\x08\x01" + "\x03\x02\x02\x02\xF0\x00\x06\x08\x06\x06\x01\x02\x06\x04\x02\x01\x02\x00\x02\x02\xF0\x00\x02\x01\x07\x00\x08\x03\x01\x02\x03\x00" + "\x00\x02\x06\x06\xF0\x01\x05\x01\x03\x02\x02\x04\x05\x03\x01\x02\x05\x07\x01\x00\x40\x00\x01\x00\x04\xF0\x02\x00\x08\x0E\x04\x02" + "\x02\x04\x04\x05\x00\x02\x00\x00\x05\xF0\x00\x01\x05\x03\x07\x02\x00\x01\x00\x02\x01\x01\x00\x00\x01\xF0\x00\x02\x02\x04\x02\x00" + "\x04\x02\x00\x00\x06\x04\x03\x01\x00\xF0\x06\x06\x08\x02\x02\x01\x03\x01\x00\x01\x02\x03\x02\x00\x00\x40\x00\x03\x03\x02\xF0\x02" + "\x02\x04\x22\x00\x03\x01\x03\x01\x04\x06\x04\x02\x06\x00\xF0\x03\x00\x00\x02\x08\x00\x01\x01\x00\x02\x04\x01\x03\x06\x04\xF0\x00" + "\x02\x01\x01\x01\x05\x00\x01\x03\x00\x05\x00\x02\x03\x03\xF0\x03\x00\x01\x00\x01\x00\x00\x04\x00\x02\x08\x08\x02\x00\x03\x40\x04" + "\x02\x02\x03\x03\xF0\x16\x12\x02\x03\x05\x02\x00\x03\x03\x00\x03\x04\x02\x02\x04\xF0\x02\x05\x01\x02\x04\x02\x01\x00\x02\x01\x00" + "\x03\x01\x01\x00\xF0\x04\x00\x02\x04\x01\x00\x00\x00\x02\x02\x03\x03\x01\x00\x02\xF0\x01\x03\x01\x04\x02\x01\x02\x03\x00\x04\x04" + "\x01\x03\x00\x03\x10\x01\xF0\x00\x00\x01\x02\x22\x06\x08\x08\x04\x02\x04\x06\x02\x00\x02\xF0\x02\x02\x00\x01\x06\x02\x06\x01\x05" + "\x01\x00\x00\x02\x09\x01\xF0\x00\x00\x02\x00\x04\x04\x01\x02\x01\x01\x00\x03\x01\x03\x01\xF0\x00\x03\x03\x04\x02\x02\x01\x03\x02" + "\x02\x05\x01\x01\x00\x04\x40\x02\x02\x02\x00\xF0\x01\x00\x00\x00\x22\x3C\x3C\x38\x30\x2E\x2C\x26\x1E\x24\x20\xF0\x1E\x20\x16\x12" + "\x0C\x08\x04\x06\x0A\x0A\x00\x00\x02\x04\x00\xF0\x02\x04\x02\x05\x03\x01\x07\x03\x00\x02\x06\x00\x02\x08\x08\xF0\x02\x04\x00\x03" + "\x02\x00\x00\x02\x00\x01\x00\x02\x01\x02\x02\x40\x00\x01\x02\x06\xF0\x02\x00\x02\x02\x06\x1C\x20\x26\x2A\x2E\x30\x34\x40\x3A\x3A" + "\xF0\x3C\x38\x3A\x38\x32\x34\x2A\x24\x24\x16\x10\x08\x0A\x0A\x08\xF0\x06\x04\x02\x04\x00\x02\x08\x02\x02\x02\x01\x02\x00\x01\x05" + "\xF0\x01\x00\x02\x02\x05\x01\x01\x02\x02\x02\x06\x00\x04\x03\x00\x40\x00\x04\x04\x01\x66\x01\x01\x00\x00\x01\x00\xF0\x01\x02\x04" + "\x06\x08\x10\x14\x1E\x24\x2A\x2E\x30\x36\x40\x42\xF0\x3C\x30\x26\x14\x08\x0A\x06\x00\x03\x00\x02\x04\x01\x00\x02\xF0\x01\x01\x02" + "\x04\x00\x00\x05\x02\x04\x00\x01\x01\x05\x02\x00\x70\x02\x00\x01\x00\x01\x05\x01\x31\xF0\x01\x02\x02\x06\x08\x0C\x0E\x12\x1C\x2A" + "\x32\x46\x50\x3C\x30\xF0\x28\x22\x0E\x0A\x02\x04\x01\x03\x05\x03\x00\x00\x01\x02\x06\xF0\x02\x03\x01\x03\x03\x00\x03\x01\x03\x02" + "\x04\x04\x03\x07\x00\x33\x02\x02\x00\x33\x01\x01\x00\x2E\x02\x00\xF0\x01\x02\x00\x02\x16\x2A\x38\x38\x3A\x34\x30\x28\x1A\x0A\x08" + "\xF0\x08\x02\x02\x00\x01\x02\x01\x02\x02\x00\x02\x02\x00\x02\x01\x60\x05\x03\x07\x04\x0C\x00\x04\x2A\x02\x00\x53\x01\x00\x02\x02" + "\x00\x46\x01\x00\x02\x00\xF0\x02\x08\x14\x22\x28\x2E\x38\x3E\x32\x1C\x08\x02\x04\x01\x03\xF0\x02\x01\x00\x04\x06\x06\x04\x06\x04" + "\x04\x04\x08\x02\x00\x04\x06\x74\x02\x02\x00\x00\x01\x01\x00\x37\x01\x01\x00\x23\x01\x00\xF0\x02\x00\x00\x02\x02\x00\x04\x08\x0E" + "\x18\x2A\x44\x4E\x3A\x20\xF0\x16\x0C\x06\x02\x00\x03\x01\x03\x05\x0B\x07\x00\x00\x0B\x03\x20\x00\x01\x64\x01\x03\x03\x01\x02\x00" + "\x3B\x02\x02\x00\x5B\x02\x00\x00\x02\x00\xF0\x01\x00\x02\x00\x0A\x22\x3A\x3C\x34\x2A\x1A\x0A\x08\x08\x02\x90\x00\x00\x08\x02\x01" + "\x02\x00\x00\x03\xF0\x39\x39\x3B\x3D\x3D\x37\x2F\x2D\x25\x1F\x1B\x19\x15\x0D\x07\x23\x01\x02\x1F\x00\x13\x01\xF0\x00\x01\x01\x02" + "\x01\x00\x04\x10\x20\x2A\x38\x40\x2C\x0C\x02\x90\x02\x02\x03\x03\x00\x01\x06\x01\x04\xF0\x1D\x1D\x21\x19\x21\x27\x31\x2F\x39\x39" + "\x3F\x41\x3D\x45\x4D\xC5\x4D\x43\x35\x2B\x21\x17\x11\x0D\x09\x05\x01\x00\x29\x01\x00\xF0\x03\x00\x01\x01\x01\x00\x06\x0E\x16\x2E" + "\x46\x42\x2C\x18\x10\x60\x02\x05\x0A\x05\x01\x03\xF0\x07\x07\x03\x0B\x01\x01\x00\x05\x01\x0B\x01\x02\x0D\x0D\x0B\xF0\x09\x1B\x25" + "\x2F\x37\x3F\x39\x37\x33\x2F\x25\x19\x11\x05\x01\xC6\x00\x00\x02\x00\x00\x00\x02\x02\x00\x00\x02\x00\xF0\x02\x01\x01\x00\x02\x06" + "\x1A\x32\x34\x26\x1C\x12\x06\x04\x02\x10\x04\xF0\x02\x02\x08\x08\x03\x01\x01\x02\x00\x02\x02\x00\x04\x04\x02\xF0\x07\x01\x03\x07" + "\x09\x07\x0F\x17\x1D\x23\x31\x35\x33\x37\x33\xE4\x21\x13\x07\x00\x01\x01\x00\x00\x02\x02\x00\x00\x00\x02\xF0\x00\x02\x02\x02\x00" + "\x00\x00\x02\x14\x28\x38\x36\x1A\x08\x02\x10\x00\xF0\x00\x01\x05\x01\x02\x02\x06\x00\x02\x02\x07\x09\x03\x01\x00\xF0\x01\x00\x03" + "\x00\x00\x00\x03\x01\x03\x01\x05\x0D\x11\x1D\x27\xF0\x39\x43\x4B\x45\x2D\x17\x0B\x03\x01\x00\x00\x02\x02\x00\x01\x84\x01\x00\x00" + "\x01\x00\x00\x01\x00\x70\x02\x0A\x1C\x3C\x44\x2E\x18\xF0\x03\x02\x04\x02\x02\x02\x01\x01\x00\x02\x00\x04\x01\x00\x00\xF0\x04\x02" + "\x08\x06\x04\x03\x02\x02\x01\x03\x01\x00\x07\x05\x00\xF0\x03\x07\x09\x17\x29\x3B\x37\x2D\x1F\x11\x03\x00\x00\x00\x02\x24\x02\x00" + "\x23\x02\x00\x80\x02\x02\x00\x02\x02\x12\x2E\x3C"; + /** * [MS-RDPEGDI] Test Bitmap 32x32 (16bpp) */ @@ -1852,6 +2949,150 @@ void dump_color_channel(BYTE* data, int width, int height) } } +int test_individual_planes_encoding_rle() +{ + int width; + int height; + BYTE* pOutput; + int planeSize; + int compareSize; + int dstSizes[4]; + int availableSize; + DWORD planarFlags; + BITMAP_PLANAR_CONTEXT* planar; + + planarFlags = PLANAR_FORMAT_HEADER_NA; + planarFlags |= PLANAR_FORMAT_HEADER_RLE; + + width = 64; + height = 64; + planeSize = width * height; + planar = freerdp_bitmap_planar_context_new(planarFlags, width, height); + + CopyMemory(planar->planes[1], (BYTE*) TEST_64X64_RED_PLANE, planeSize); /* Red */ + CopyMemory(planar->planes[2], (BYTE*) TEST_64X64_GREEN_PLANE, planeSize); /* Green */ + CopyMemory(planar->planes[3], (BYTE*) TEST_64X64_BLUE_PLANE, planeSize); /* Blue */ + + freerdp_bitmap_planar_delta_encode_plane(planar->planes[1], width, height, planar->deltaPlanes[1]); /* Red */ + freerdp_bitmap_planar_delta_encode_plane(planar->planes[2], width, height, planar->deltaPlanes[2]); /* Green */ + freerdp_bitmap_planar_delta_encode_plane(planar->planes[3], width, height, planar->deltaPlanes[3]); /* Blue */ + + pOutput = planar->rlePlanesBuffer; + availableSize = planeSize * 3; + + /* Red */ + + dstSizes[1] = availableSize; + + if (!freerdp_bitmap_planar_compress_plane_rle(planar->planes[1], width, height, pOutput, &dstSizes[1])) + { + printf("failed to encode red plane\n"); + return 0; + } + + planar->rlePlanes[1] = pOutput; + pOutput += dstSizes[1]; + availableSize -= dstSizes[1]; + + if (dstSizes[1] != sizeof(TEST_64X64_RED_PLANE_RLE)) + { + printf("RedPlaneRle unexpected size: actual: %d, expected: %d\n", + dstSizes[1], sizeof(TEST_64X64_RED_PLANE_RLE)); + //return -1; + } + + compareSize = (dstSizes[1] > sizeof(TEST_64X64_RED_PLANE_RLE)) ? sizeof(TEST_64X64_RED_PLANE_RLE) : dstSizes[1]; + + if (memcmp(planar->rlePlanes[1], (BYTE*) TEST_64X64_RED_PLANE_RLE, compareSize) != 0) + { + printf("RedPlaneRle doesn't match expected output\n"); + + printf("RedPlaneRle Expected (%d):\n", sizeof(TEST_64X64_RED_PLANE_RLE)); + winpr_HexDump((BYTE*) TEST_64X64_RED_PLANE_RLE, sizeof(TEST_64X64_RED_PLANE_RLE)); + + printf("RedPlaneRle Actual (%d):\n", dstSizes[1]); + winpr_HexDump(planar->rlePlanes[1], dstSizes[1]); + + //return -1; + } + + /* Green */ + + dstSizes[2] = availableSize; + + if (!freerdp_bitmap_planar_compress_plane_rle(planar->planes[2], width, height, pOutput, &dstSizes[2])) + { + printf("failed to encode green plane\n"); + return 0; + } + + planar->rlePlanes[2] = pOutput; + pOutput += dstSizes[2]; + availableSize -= dstSizes[2]; + + if (dstSizes[2] != sizeof(TEST_64X64_GREEN_PLANE_RLE)) + { + printf("GreenPlaneRle unexpected size: actual: %d, expected: %d\n", + dstSizes[1], sizeof(TEST_64X64_GREEN_PLANE_RLE)); + //return -1; + } + + compareSize = (dstSizes[2] > sizeof(TEST_64X64_GREEN_PLANE_RLE)) ? sizeof(TEST_64X64_GREEN_PLANE_RLE) : dstSizes[2]; + + if (memcmp(planar->rlePlanes[2], (BYTE*) TEST_64X64_GREEN_PLANE_RLE, compareSize) != 0) + { + printf("GreenPlaneRle doesn't match expected output\n"); + + printf("GreenPlaneRle Expected (%d):\n", sizeof(TEST_64X64_GREEN_PLANE_RLE)); + winpr_HexDump((BYTE*) TEST_64X64_GREEN_PLANE_RLE, sizeof(TEST_64X64_GREEN_PLANE_RLE)); + + printf("GreenPlaneRle Actual (%d):\n", dstSizes[2]); + winpr_HexDump(planar->rlePlanes[2], dstSizes[2]); + + //return -1; + } + + /* Blue */ + + dstSizes[3] = availableSize; + + if (!freerdp_bitmap_planar_compress_plane_rle(planar->planes[3], width, height, pOutput, &dstSizes[3])) + { + printf("failed to encode blue plane\n"); + return 0; + } + + planar->rlePlanes[3] = pOutput; + pOutput += dstSizes[3]; + availableSize -= dstSizes[3]; + + if (dstSizes[3] != sizeof(TEST_64X64_BLUE_PLANE_RLE)) + { + printf("BluePlaneRle unexpected size: actual: %d, expected: %d\n", + dstSizes[1], sizeof(TEST_64X64_BLUE_PLANE_RLE)); + //return -1; + } + + compareSize = (dstSizes[3] > sizeof(TEST_64X64_BLUE_PLANE_RLE)) ? sizeof(TEST_64X64_BLUE_PLANE_RLE) : dstSizes[3]; + + if (memcmp(planar->rlePlanes[3], (BYTE*) TEST_64X64_BLUE_PLANE_RLE, compareSize) != 0) + { + printf("BluePlaneRle doesn't match expected output\n"); + + printf("BluePlaneRle Expected (%d):\n", sizeof(TEST_64X64_BLUE_PLANE_RLE)); + winpr_HexDump((BYTE*) TEST_64X64_BLUE_PLANE_RLE, sizeof(TEST_64X64_BLUE_PLANE_RLE)); + + printf("BluePlaneRle Actual (%d):\n", dstSizes[3]); + winpr_HexDump(planar->rlePlanes[3], dstSizes[3]); + + //return -1; + } + + freerdp_bitmap_planar_context_free(planar); + + return 0; +} + int TestFreeRDPCodecPlanar(int argc, char* argv[]) { int i, j; @@ -2102,6 +3343,9 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(decompressedBitmap); #endif + //test_individual_planes_encoding_rle(); + +#if 1 /* Experimental Case 03 */ width = 64; @@ -2142,6 +3386,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); +#endif freerdp_clrconv_free(clrconv); free(srcBitmap32); From 4c6218e5944fcfd8c667ea68b9f1f11f89b5b423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 20 Dec 2013 02:41:25 -0500 Subject: [PATCH 107/149] libfreerdp-codec: make planar encoder pass compression/decompression tests --- libfreerdp/codec/planar.c | 75 +++++++++++++------ .../codec/test/TestFreeRDPCodecPlanar.c | 35 ++++----- 2 files changed, 66 insertions(+), 44 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index bcbd90838..6fa14fb59 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -330,6 +330,7 @@ struct _PLANAR_RLE_CONTEXT BYTE* outPlane; int outPlaneSize; int outSegmentSize; + int nRunLengthPrev; }; typedef struct _PLANAR_RLE_CONTEXT PLANAR_RLE_CONTEXT; @@ -339,12 +340,12 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) { int k; - printf("RAW["); + printf("RAW(%d)[", rle->cRawBytes); for (k = 0; k < rle->cRawBytes; k++) { - printf("0x%02X%s", rle->rawValues[k], - ((k + 1) == rle->cRawBytes) ? "" : ", "); + printf("%02x%s", rle->rawValues[k], + ((k + 1) == rle->cRawBytes) ? "" : " "); } printf("] RUN[%d]\n", rle->nRunLength); @@ -416,9 +417,14 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) } *rle->output = PLANAR_CONTROL_BYTE(2, (rle->nRunLength - 32)); - rle->nRunLength -= 32; rle->output++; + rle->rawValues += (rle->cRawBytes + rle->nRunLength); + rle->output += rle->cRawBytes; + + rle->cRawBytes = 0; + rle->nRunLength = 0; + return 0; /* finish */ } else if (rle->nRunLength > 15) @@ -432,9 +438,14 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) } *rle->output = PLANAR_CONTROL_BYTE(1, (rle->nRunLength - 16)); - rle->nRunLength -= 16; rle->output++; + rle->rawValues += (rle->cRawBytes + rle->nRunLength); + rle->output += rle->cRawBytes; + + rle->cRawBytes = 0; + rle->nRunLength = 0; + return 0; /* finish */ } else @@ -450,7 +461,6 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) *rle->output = PLANAR_CONTROL_BYTE(rle->nRunLength, rle->cRawBytes); rle->output++; - CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); rle->rawValues += (rle->cRawBytes + rle->nRunLength); rle->output += rle->cRawBytes; @@ -543,32 +553,34 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei rle->rawScanline = &inPlane[i * width]; rle->rawValues = rle->rawScanline; + rle->nRunLengthPrev = 0; + //winpr_HexDump(rle->rawScanline, width); - for (j = 1; j < width; j++) + for (j = 1; j <= width; j++) { bSymbolMatch = FALSE; - bSequenceEnd = ((j + 1) == width) ? TRUE : FALSE; + bSequenceEnd = (j == width) ? TRUE : FALSE; - if (rle->rawScanline[j] == rle->rawValues[rle->cRawBytes - 1]) + if (!bSequenceEnd) { - bSymbolMatch = TRUE; - } - else - { - //printf("mismatch: nRunLength: %d cRawBytes: %d\n", rle->nRunLength, rle->cRawBytes); - - if (bSequenceEnd) - rle->cRawBytes++; - - if (rle->nRunLength < 3) + if (rle->rawScanline[j] == rle->rawValues[rle->cRawBytes - 1]) { - rle->cRawBytes += rle->nRunLength; - rle->nRunLength = 0; + bSymbolMatch = TRUE; } else { - bSequenceEnd = TRUE; + //printf("mismatch: nRunLength: %d cRawBytes: %d\n", rle->nRunLength, rle->cRawBytes); + + if (rle->nRunLength < 3) + { + rle->cRawBytes += rle->nRunLength; + rle->nRunLength = 0; + } + else + { + bSequenceEnd = TRUE; + } } } @@ -582,15 +594,30 @@ BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int hei if (bSequenceEnd) { + int nRunLengthPrev; + if (rle->nRunLength < 3) { rle->cRawBytes += rle->nRunLength; rle->nRunLength = 0; } + if ((rle->cRawBytes == 1) && (*rle->rawValues == 0)) + { + if (!rle->nRunLengthPrev) + { + rle->cRawBytes = 0; + rle->nRunLength++; + } + } + + nRunLengthPrev = rle->nRunLength; + if (freerdp_bitmap_planar_compress_plane_rle_segment(rle) < 0) return NULL; + rle->nRunLengthPrev = nRunLengthPrev; + rle->nRunLength = 0; rle->cRawBytes = 1; } @@ -756,8 +783,8 @@ BYTE* freerdp_bitmap_compress_planar(BITMAP_PLANAR_CONTEXT* context, BYTE* data, context->rlePlanes[3] = &context->rlePlanesBuffer[offset]; offset += dstSizes[3]; - printf("R: [%d/%d] G: [%d/%d] B: [%d/%d]\n", - dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize); + //printf("R: [%d/%d] G: [%d/%d] B: [%d/%d]\n", + // dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize); } } diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index 1f30869c6..ffd97e252 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -2984,7 +2984,7 @@ int test_individual_planes_encoding_rle() dstSizes[1] = availableSize; - if (!freerdp_bitmap_planar_compress_plane_rle(planar->planes[1], width, height, pOutput, &dstSizes[1])) + if (!freerdp_bitmap_planar_compress_plane_rle(planar->deltaPlanes[1], width, height, pOutput, &dstSizes[1])) { printf("failed to encode red plane\n"); return 0; @@ -2998,7 +2998,7 @@ int test_individual_planes_encoding_rle() { printf("RedPlaneRle unexpected size: actual: %d, expected: %d\n", dstSizes[1], sizeof(TEST_64X64_RED_PLANE_RLE)); - //return -1; + return -1; } compareSize = (dstSizes[1] > sizeof(TEST_64X64_RED_PLANE_RLE)) ? sizeof(TEST_64X64_RED_PLANE_RLE) : dstSizes[1]; @@ -3008,19 +3008,19 @@ int test_individual_planes_encoding_rle() printf("RedPlaneRle doesn't match expected output\n"); printf("RedPlaneRle Expected (%d):\n", sizeof(TEST_64X64_RED_PLANE_RLE)); - winpr_HexDump((BYTE*) TEST_64X64_RED_PLANE_RLE, sizeof(TEST_64X64_RED_PLANE_RLE)); + //winpr_HexDump((BYTE*) TEST_64X64_RED_PLANE_RLE, sizeof(TEST_64X64_RED_PLANE_RLE)); printf("RedPlaneRle Actual (%d):\n", dstSizes[1]); winpr_HexDump(planar->rlePlanes[1], dstSizes[1]); - //return -1; + return -1; } /* Green */ dstSizes[2] = availableSize; - if (!freerdp_bitmap_planar_compress_plane_rle(planar->planes[2], width, height, pOutput, &dstSizes[2])) + if (!freerdp_bitmap_planar_compress_plane_rle(planar->deltaPlanes[2], width, height, pOutput, &dstSizes[2])) { printf("failed to encode green plane\n"); return 0; @@ -3034,7 +3034,7 @@ int test_individual_planes_encoding_rle() { printf("GreenPlaneRle unexpected size: actual: %d, expected: %d\n", dstSizes[1], sizeof(TEST_64X64_GREEN_PLANE_RLE)); - //return -1; + return -1; } compareSize = (dstSizes[2] > sizeof(TEST_64X64_GREEN_PLANE_RLE)) ? sizeof(TEST_64X64_GREEN_PLANE_RLE) : dstSizes[2]; @@ -3049,14 +3049,14 @@ int test_individual_planes_encoding_rle() printf("GreenPlaneRle Actual (%d):\n", dstSizes[2]); winpr_HexDump(planar->rlePlanes[2], dstSizes[2]); - //return -1; + return -1; } /* Blue */ dstSizes[3] = availableSize; - if (!freerdp_bitmap_planar_compress_plane_rle(planar->planes[3], width, height, pOutput, &dstSizes[3])) + if (!freerdp_bitmap_planar_compress_plane_rle(planar->deltaPlanes[3], width, height, pOutput, &dstSizes[3])) { printf("failed to encode blue plane\n"); return 0; @@ -3070,7 +3070,7 @@ int test_individual_planes_encoding_rle() { printf("BluePlaneRle unexpected size: actual: %d, expected: %d\n", dstSizes[1], sizeof(TEST_64X64_BLUE_PLANE_RLE)); - //return -1; + return -1; } compareSize = (dstSizes[3] > sizeof(TEST_64X64_BLUE_PLANE_RLE)) ? sizeof(TEST_64X64_BLUE_PLANE_RLE) : dstSizes[3]; @@ -3085,7 +3085,7 @@ int test_individual_planes_encoding_rle() printf("BluePlaneRle Actual (%d):\n", dstSizes[3]); winpr_HexDump(planar->rlePlanes[3], dstSizes[3]); - //return -1; + return -1; } freerdp_bitmap_planar_context_free(planar); @@ -3111,7 +3111,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) BITMAP_PLANAR_CONTEXT* planar; planarFlags = PLANAR_FORMAT_HEADER_NA; - //planarFlags |= PLANAR_FORMAT_HEADER_RLE; + planarFlags |= PLANAR_FORMAT_HEADER_RLE; planar = freerdp_bitmap_planar_context_new(planarFlags, 64, 64); @@ -3122,7 +3122,6 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) format = FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_ARGB, FREERDP_PIXEL_FLIP_NONE); -#if 1 freerdp_bitmap_compress_planar(planar, srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12, 1, NULL, &dstSize); @@ -3255,9 +3254,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); } -#endif -#if 1 /* Experimental Case 01 */ width = 64; @@ -3298,9 +3295,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); -#endif -#if 1 /* Experimental Case 02 */ width = 64; @@ -3341,11 +3336,12 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); -#endif - //test_individual_planes_encoding_rle(); + if (test_individual_planes_encoding_rle() < 0) + { + //return -1; + } -#if 1 /* Experimental Case 03 */ width = 64; @@ -3386,7 +3382,6 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); -#endif freerdp_clrconv_free(clrconv); free(srcBitmap32); From eb8f34ffef8b0b2055b95eb4144c1bd97049f581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 20 Dec 2013 16:02:20 -0500 Subject: [PATCH 108/149] libfreerdp-codec: reduce size of GDI bitmap encoder --- libfreerdp/codec/bitmap_encode.c | 492 +------------------------------ 1 file changed, 4 insertions(+), 488 deletions(-) diff --git a/libfreerdp/codec/bitmap_encode.c b/libfreerdp/codec/bitmap_encode.c index 5cdca0b93..9db6f1a14 100644 --- a/libfreerdp/codec/bitmap_encode.c +++ b/libfreerdp/codec/bitmap_encode.c @@ -23,27 +23,9 @@ #include -#define GETPIXEL8(d, x, y, w) (*(((unsigned char*)d) + ((y) * (w) + (x)))) #define GETPIXEL16(d, x, y, w) (*(((unsigned short*)d) + ((y) * (w) + (x)))) #define GETPIXEL32(d, x, y, w) (*(((unsigned int*)d) + ((y) * (w) + (x)))) -/*****************************************************************************/ -#define IN_PIXEL8(in_ptr, in_x, in_y, in_w, in_last_pixel, in_pixel); \ - { \ - if (in_ptr == 0) \ - { \ - in_pixel = 0; \ - } \ - else if (in_x < in_w) \ - { \ - in_pixel = GETPIXEL8(in_ptr, in_x, in_y, in_w); \ - } \ - else \ - { \ - in_pixel = in_last_pixel; \ - } \ - } - /*****************************************************************************/ #define IN_PIXEL16(in_ptr, in_x, in_y, in_w, in_last_pixel, in_pixel); \ { \ @@ -78,35 +60,6 @@ } \ } -/*****************************************************************************/ -/* color */ -#define OUT_COLOR_COUNT1(in_count, in_s, in_data) \ - { \ - if (in_count > 0) \ - { \ - if (in_count < 32) \ - { \ - temp = (0x3 << 5) | in_count; \ - Stream_Write_UINT8(in_s, temp); \ - Stream_Write_UINT8(in_s, in_data); \ - } \ - else if (in_count < 256 + 32) \ - { \ - Stream_Write_UINT8(in_s, 0x60); \ - temp = in_count - 32; \ - Stream_Write_UINT8(in_s, temp); \ - Stream_Write_UINT8(in_s, in_data); \ - } \ - else \ - { \ - Stream_Write_UINT8(in_s, 0xf3); \ - Stream_Write_UINT16(in_s, in_count); \ - Stream_Write_UINT8(in_s, in_data); \ - } \ - } \ - in_count = 0; \ - } - /*****************************************************************************/ /* color */ #define OUT_COLOR_COUNT2(in_count, in_s, in_data) \ @@ -171,36 +124,6 @@ in_count = 0; \ } -/*****************************************************************************/ -/* copy */ -#define OUT_COPY_COUNT1(in_count, in_s, in_data) \ - { \ - if (in_count > 0) \ - { \ - if (in_count < 32) \ - { \ - temp = (0x4 << 5) | in_count; \ - Stream_Write_UINT8(in_s, temp); \ - Stream_Write(in_s, Stream_Buffer(in_data), in_count); \ - } \ - else if (in_count < 256 + 32) \ - { \ - Stream_Write_UINT8(in_s, 0x80); \ - temp = in_count - 32; \ - Stream_Write_UINT8(in_s, temp); \ - Stream_Write(in_s, Stream_Buffer(in_data), in_count); \ - } \ - else \ - { \ - Stream_Write_UINT8(in_s, 0xf4); \ - Stream_Write_UINT16(in_s, in_count); \ - Stream_Write(in_s, Stream_Buffer(in_data), in_count); \ - } \ - } \ - in_count = 0; \ - Stream_SetPosition(in_data, 0); \ - } - /*****************************************************************************/ /* copy */ #define OUT_COPY_COUNT2(in_count, in_s, in_data) \ @@ -267,39 +190,6 @@ Stream_SetPosition(in_data, 0); \ } -/*****************************************************************************/ -/* bicolor */ -#define OUT_BICOLOR_COUNT1(in_count, in_s, in_color1, in_color2) \ - { \ - if (in_count > 0) \ - { \ - if (in_count / 2 < 16) \ - { \ - temp = (0xe << 4) | (in_count / 2); \ - Stream_Write_UINT8(in_s, temp); \ - Stream_Write_UINT8(in_s, in_color1); \ - Stream_Write_UINT8(in_s, in_color2); \ - } \ - else if (in_count / 2 < 256 + 16) \ - { \ - Stream_Write_UINT8(in_s, 0xe0); \ - temp = in_count / 2 - 16; \ - Stream_Write_UINT8(in_s, temp); \ - Stream_Write_UINT8(in_s, in_color1); \ - Stream_Write_UINT8(in_s, in_color2); \ - } \ - else \ - { \ - Stream_Write_UINT8(in_s, 0xf8); \ - temp = in_count / 2; \ - Stream_Write_UINT16(in_s, temp); \ - Stream_Write_UINT8(in_s, in_color1); \ - Stream_Write_UINT8(in_s, in_color2); \ - } \ - } \ - in_count = 0; \ - } - /*****************************************************************************/ /* bicolor */ #define OUT_BICOLOR_COUNT2(in_count, in_s, in_color1, in_color2) \ @@ -378,31 +268,6 @@ in_count = 0; \ } -/*****************************************************************************/ -/* fill */ -#define OUT_FILL_COUNT1(in_count, in_s) \ - { \ - if (in_count > 0) \ - { \ - if (in_count < 32) \ - { \ - Stream_Write_UINT8(in_s, in_count); \ - } \ - else if (in_count < 256 + 32) \ - { \ - Stream_Write_UINT8(in_s, 0x0); \ - temp = in_count - 32; \ - Stream_Write_UINT8(in_s, temp); \ - } \ - else \ - { \ - Stream_Write_UINT8(in_s, 0xf0); \ - Stream_Write_UINT16(in_s, in_count); \ - } \ - } \ - in_count = 0; \ - } - /*****************************************************************************/ /* fill */ #define OUT_FILL_COUNT2(in_count, in_s) \ @@ -453,32 +318,6 @@ in_count = 0; \ } -/*****************************************************************************/ -/* mix */ -#define OUT_MIX_COUNT1(in_count, in_s) \ - { \ - if (in_count > 0) \ - { \ - if (in_count < 32) \ - { \ - temp = (0x1 << 5) | in_count; \ - Stream_Write_UINT8(in_s, temp); \ - } \ - else if (in_count < 256 + 32) \ - { \ - Stream_Write_UINT8(in_s, 0x20); \ - temp = in_count - 32; \ - Stream_Write_UINT8(in_s, temp); \ - } \ - else \ - { \ - Stream_Write_UINT8(in_s, 0xf1); \ - Stream_Write_UINT16(in_s, in_count); \ - } \ - } \ - in_count = 0; \ - } - /*****************************************************************************/ /* mix */ #define OUT_MIX_COUNT2(in_count, in_s) \ @@ -531,35 +370,6 @@ in_count = 0; \ } -/*****************************************************************************/ -/* fom */ -#define OUT_FOM_COUNT1(in_count, in_s, in_mask, in_mask_len) \ - { \ - if (in_count > 0) \ - { \ - if ((in_count % 8) == 0 && in_count < 249) \ - { \ - temp = (0x2 << 5) | (in_count / 8); \ - Stream_Write_UINT8(in_s, temp); \ - Stream_Write(in_s, in_mask, in_mask_len); \ - } \ - else if (in_count < 256) \ - { \ - Stream_Write_UINT8(in_s, 0x40); \ - temp = in_count - 1; \ - Stream_Write_UINT8(in_s, temp); \ - Stream_Write(in_s, in_mask, in_mask_len); \ - } \ - else \ - { \ - Stream_Write_UINT8(in_s, 0xf2); \ - Stream_Write_UINT16(in_s, in_count); \ - Stream_Write(in_s, in_mask, in_mask_len); \ - } \ - } \ - in_count = 0; \ - } - /*****************************************************************************/ /* fom */ #define OUT_FOM_COUNT2(in_count, in_s, in_mask, in_mask_len) \ @@ -618,7 +428,6 @@ in_count = 0; \ } -/*****************************************************************************/ #define TEST_FILL \ ((last_line == 0 && pixel == 0) || \ (last_line != 0 && pixel == ypixel)) @@ -646,8 +455,7 @@ bicolor_spin = 0; \ } -/*****************************************************************************/ -int freerdp_bitmap_compress(char* in_data, int width, int height, +int freerdp_bitmap_compress(char* srcData, int width, int height, wStream* s, int bpp, int byte_limit, int start_line, wStream* temp_s, int e) { char *line; @@ -691,303 +499,11 @@ int freerdp_bitmap_compress(char* in_data, int width, int height, mix_count = 0; fom_count = 0; - if (bpp == 8) - { - mix = 0xFF; - out_count = end; - line = in_data + width * start_line; - - while (start_line >= 0 && out_count < 32768) - { - i = Stream_GetPosition(s) + count; - - if (i - color_count >= byte_limit && - i - bicolor_count >= byte_limit && - i - fill_count >= byte_limit && - i - mix_count >= byte_limit && - i - fom_count >= byte_limit) - { - break; - } - - out_count += end; - - for (i = 0; i < end; i++) - { - /* read next pixel */ - IN_PIXEL8(line, i, 0, width, last_pixel, pixel); - IN_PIXEL8(last_line, i, 0, width, last_ypixel, ypixel); - - if (!TEST_FILL) - { - if (fill_count > 3 && - fill_count >= color_count && - fill_count >= bicolor_count && - fill_count >= mix_count && - fill_count >= fom_count) - { - count -= fill_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_FILL_COUNT1(fill_count, s); - RESET_COUNTS; - } - - fill_count = 0; - } - - if (!TEST_MIX) - { - if (mix_count > 3 && - mix_count >= fill_count && - mix_count >= bicolor_count && - mix_count >= color_count && - mix_count >= fom_count) - { - count -= mix_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_MIX_COUNT1(mix_count, s); - RESET_COUNTS; - } - - mix_count = 0; - } - - if (!TEST_COLOR) - { - if (color_count > 3 && - color_count >= fill_count && - color_count >= bicolor_count && - color_count >= mix_count && - color_count >= fom_count) - { - count -= color_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_COLOR_COUNT1(color_count, s, last_pixel); - RESET_COUNTS; - } - - color_count = 0; - } - - if (!TEST_BICOLOR) - { - if (bicolor_count > 3 && - bicolor_count >= fill_count && - bicolor_count >= color_count && - bicolor_count >= mix_count && - bicolor_count >= fom_count) - { - if ((bicolor_count % 2) == 0) - { - count -= bicolor_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_BICOLOR_COUNT1(bicolor_count, s, bicolor1, bicolor2); - } - else - { - bicolor_count--; - count -= bicolor_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_BICOLOR_COUNT1(bicolor_count, s, bicolor2, bicolor1); - } - - RESET_COUNTS; - } - - bicolor_count = 0; - bicolor1 = last_pixel; - bicolor2 = pixel; - bicolor_spin = 0; - } - - if (!TEST_FOM) - { - if (fom_count > 3 && - fom_count >= fill_count && - fom_count >= color_count && - fom_count >= mix_count && - fom_count >= bicolor_count) - { - count -= fom_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_FOM_COUNT1(fom_count, s, fom_mask, fom_mask_len); - RESET_COUNTS; - } - - fom_count = 0; - fom_mask_len = 0; - } - - if (TEST_FILL) - { - fill_count++; - } - - if (TEST_MIX) - { - mix_count++; - } - - if (TEST_COLOR) - { - color_count++; - } - - if (TEST_BICOLOR) - { - bicolor_spin = !bicolor_spin; - bicolor_count++; - } - - if (TEST_FOM) - { - if ((fom_count % 8) == 0) - { - fom_mask[fom_mask_len] = 0; - fom_mask_len++; - } - - if (pixel == (ypixel ^ mix)) - { - fom_mask[fom_mask_len - 1] |= (1 << (fom_count % 8)); - } - - fom_count++; - } - - Stream_Write_UINT8(temp_s, pixel); - count++; - last_pixel = pixel; - last_ypixel = ypixel; - } - - /* can't take fix, mix, or fom past first line */ - if (last_line == 0) - { - if (fill_count > 3 && - fill_count >= color_count && - fill_count >= bicolor_count && - fill_count >= mix_count && - fill_count >= fom_count) - { - count -= fill_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_FILL_COUNT1(fill_count, s); - RESET_COUNTS; - } - - fill_count = 0; - - if (mix_count > 3 && - mix_count >= fill_count && - mix_count >= bicolor_count && - mix_count >= color_count && - mix_count >= fom_count) - { - count -= mix_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_MIX_COUNT1(mix_count, s); - RESET_COUNTS; - } - - mix_count = 0; - - if (fom_count > 3 && - fom_count >= fill_count && - fom_count >= color_count && - fom_count >= mix_count && - fom_count >= bicolor_count) - { - count -= fom_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_FOM_COUNT1(fom_count, s, fom_mask, fom_mask_len); - RESET_COUNTS; - } - - fom_count = 0; - fom_mask_len = 0; - } - - last_line = line; - line = line - width; - start_line--; - lines_sent++; - } - - if (fill_count > 3 && - fill_count >= color_count && - fill_count >= bicolor_count && - fill_count >= mix_count && - fill_count >= fom_count) - { - count -= fill_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_FILL_COUNT1(fill_count, s); - } - else if (mix_count > 3 && - mix_count >= color_count && - mix_count >= bicolor_count && - mix_count >= fill_count && - mix_count >= fom_count) - { - count -= mix_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_MIX_COUNT1(mix_count, s); - } - else if (color_count > 3 && - color_count >= mix_count && - color_count >= bicolor_count && - color_count >= fill_count && - color_count >= fom_count) - { - count -= color_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_COLOR_COUNT1(color_count, s, last_pixel); - } - else if (bicolor_count > 3 && - bicolor_count >= mix_count && - bicolor_count >= color_count && - bicolor_count >= fill_count && - bicolor_count >= fom_count) - { - if ((bicolor_count % 2) == 0) - { - count -= bicolor_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_BICOLOR_COUNT1(bicolor_count, s, bicolor1, bicolor2); - } - else - { - bicolor_count--; - count -= bicolor_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_BICOLOR_COUNT1(bicolor_count, s, bicolor2, bicolor1); - } - - count -= bicolor_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_BICOLOR_COUNT1(bicolor_count, s, bicolor1, bicolor2); - } - else if (fom_count > 3 && - fom_count >= mix_count && - fom_count >= color_count && - fom_count >= fill_count && - fom_count >= bicolor_count) - { - count -= fom_count; - OUT_COPY_COUNT1(count, s, temp_s); - OUT_FOM_COUNT1(fom_count, s, fom_mask, fom_mask_len); - } - else - { - OUT_COPY_COUNT1(count, s, temp_s); - } - } - else if ((bpp == 15) || (bpp == 16)) + if ((bpp == 15) || (bpp == 16)) { mix = (bpp == 15) ? 0xBA1F : 0xFFFF; out_count = end * 2; - line = in_data + width * start_line * 2; + line = srcData + width * start_line * 2; while (start_line >= 0 && out_count < 32768) { @@ -1279,7 +795,7 @@ int freerdp_bitmap_compress(char* in_data, int width, int height, { mix = 0xFFFFFF; out_count = end * 3; - line = in_data + width * start_line * 4; + line = srcData + width * start_line * 4; while (start_line >= 0 && out_count < 32768) { From 8d8ecbc042971c4ef6fab923bd37a4908b946651 Mon Sep 17 00:00:00 2001 From: Hardening Date: Wed, 18 Dec 2013 10:50:46 +0100 Subject: [PATCH 109/149] More security fixes for capabilities This patch fixes a security issue which would allow an attackant to set bytes to 1 at choosen places. It also fixes a warning when DEBUG_CAPABILITIES is set. --- libfreerdp/core/capabilities.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/libfreerdp/core/capabilities.c b/libfreerdp/core/capabilities.c index 606500964..1a7dfd3b2 100644 --- a/libfreerdp/core/capabilities.c +++ b/libfreerdp/core/capabilities.c @@ -3159,13 +3159,21 @@ BOOL rdp_read_capability_sets(wStream* s, rdpSettings* settings, UINT16 numberCa Stream_GetPointer(s, mark); count = numberCapabilities; - while (numberCapabilities > 0) + while (numberCapabilities > 0 && Stream_GetRemainingLength(s) >= 4) { Stream_GetPointer(s, bm); rdp_read_capability_set_header(s, &length, &type); - settings->ReceivedCapabilities[type] = TRUE; + if (type < 32) + { + settings->ReceivedCapabilities[type] = TRUE; + } + else + { + fprintf(stderr, "%s: not handling capability type %d yet\n", __FUNCTION__, type); + } + em = bm + length; if (Stream_GetRemainingLength(s) < length - 4) @@ -3336,6 +3344,12 @@ BOOL rdp_read_capability_sets(wStream* s, rdpSettings* settings, UINT16 numberCa numberCapabilities--; } + if (numberCapabilities) + { + fprintf(stderr, "%s: strange we haven't read the number of announced capacity sets, read=%d expected=%d\n", + __FUNCTION__, count-numberCapabilities, count); + } + #ifdef WITH_DEBUG_CAPABILITIES Stream_GetPointer(s, em); Stream_SetPointer(s, mark); From 19fb713b83ae7d9bc7071e6b18daafcf6a9d1050 Mon Sep 17 00:00:00 2001 From: Hardening Date: Tue, 7 Jan 2014 16:36:41 +0100 Subject: [PATCH 110/149] Handle EINTR nicely When we caught an EINTR during a select we should retry instead of returning an error --- winpr/libwinpr/synch/wait.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/winpr/libwinpr/synch/wait.c b/winpr/libwinpr/synch/wait.c index 017d59353..560fed572 100644 --- a/winpr/libwinpr/synch/wait.c +++ b/winpr/libwinpr/synch/wait.c @@ -426,8 +426,12 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } - status = select(fd + 1, &rfds, NULL, NULL, - (dwMilliseconds == INFINITE) ? NULL : &timeout); + do + { + status = select(fd + 1, &rfds, NULL, NULL, + (dwMilliseconds == INFINITE) ? NULL : &timeout); + } + while (status < 0 && errno == EINTR); if (status < 0) { @@ -556,8 +560,12 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } - status = select(maxfd + 1, &fds, 0, 0, - (dwMilliseconds == INFINITE) ? NULL : &timeout); + do + { + status = select(maxfd + 1, &fds, 0, 0, + (dwMilliseconds == INFINITE) ? NULL : &timeout); + } + while (status < 0 && errno == EINTR); if (status < 0) { From 47f3b879db40d49767b8caefb89a70941e71f85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 11 Jan 2014 22:54:08 -0500 Subject: [PATCH 111/149] libfreerdp-core: implement parsing of less frequent core rdp messages --- include/freerdp/settings.h | 19 +++++ libfreerdp/core/errinfo.c | 1 - libfreerdp/core/rdp.c | 158 ++++++++++++++++++++++++++++++------- 3 files changed, 147 insertions(+), 31 deletions(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 21a218e2f..361cfba08 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -240,6 +240,15 @@ typedef struct _TARGET_NET_ADDRESS TARGET_NET_ADDRESS; #define LOGON_FAILED_OTHER 0x00000002 #define LOGON_WARNING 0x00000003 +/* Server Status Info */ +#define STATUS_FINDING_DESTINATION 0x00000401 +#define STATUS_LOADING_DESTINATION 0x00000402 +#define STATUS_BRINGING_SESSION_ONLINE 0x00000403 +#define STATUS_REDIRECTING_TO_DESTINATION 0x00000404 +#define STATUS_VM_LOADING 0x00000501 +#define STATUS_VM_WAKING 0x00000502 +#define STATUS_VM_BOOTING 0x00000503 + /* SYSTEM_TIME */ typedef struct { @@ -389,6 +398,16 @@ struct rdp_monitor }; typedef struct rdp_monitor rdpMonitor; +struct _MONITOR_DEF +{ + INT32 left; + INT32 top; + INT32 right; + INT32 bottom; + UINT32 flags; +}; +typedef struct _MONITOR_DEF MONITOR_DEF; + /* Device Redirection */ #define RDPDR_DTYP_SERIAL 0x00000001 diff --git a/libfreerdp/core/errinfo.c b/libfreerdp/core/errinfo.c index ab8550c7d..44e3d301e 100644 --- a/libfreerdp/core/errinfo.c +++ b/libfreerdp/core/errinfo.c @@ -568,4 +568,3 @@ void rdp_print_errinfo(UINT32 code) fprintf(stderr, "ERRINFO_UNKNOWN 0x%08X: Unknown error.\n", code); } - diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 01423bdb7..c3f81adf6 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -538,6 +538,41 @@ BOOL rdp_send_data_pdu(rdpRdp* rdp, wStream* s, BYTE type, UINT16 channel_id) return TRUE; } +BOOL rdp_recv_server_shutdown_denied_pdu(rdpRdp* rdp, wStream* s) +{ + return TRUE; +} + +BOOL rdp_recv_server_set_keyboard_indicators_pdu(rdpRdp* rdp, wStream* s) +{ + UINT16 unitId; + UINT16 ledFlags; + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + + Stream_Read_UINT16(s, unitId); /* unitId (2 bytes) */ + Stream_Read_UINT16(s, ledFlags); /* ledFlags (2 bytes) */ + + return TRUE; +} + +BOOL rdp_recv_server_set_keyboard_ime_status_pdu(rdpRdp* rdp, wStream* s) +{ + UINT16 unitId; + UINT32 imeState; + UINT32 imeConvMode; + + if (Stream_GetRemainingLength(s) < 10) + return FALSE; + + Stream_Read_UINT16(s, unitId); /* unitId (2 bytes) */ + Stream_Read_UINT32(s, imeState); /* imeState (4 bytes) */ + Stream_Read_UINT32(s, imeConvMode); /* imeConvMode (4 bytes) */ + + return TRUE; +} + BOOL rdp_recv_set_error_info_data_pdu(rdpRdp* rdp, wStream* s) { UINT32 errorInfo; @@ -552,6 +587,87 @@ BOOL rdp_recv_set_error_info_data_pdu(rdpRdp* rdp, wStream* s) return TRUE; } +BOOL rdp_recv_server_auto_reconnect_status_pdu(rdpRdp* rdp, wStream* s) +{ + UINT32 arcStatus; + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + + Stream_Read_UINT32(s, arcStatus); /* arcStatus (4 bytes) */ + + return TRUE; +} + +BOOL rdp_recv_server_status_info_pdu(rdpRdp* rdp, wStream* s) +{ + UINT32 statusCode; + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + + Stream_Read_UINT32(s, statusCode); /* statusCode (4 bytes) */ + + return TRUE; +} + +BOOL rdp_recv_monitor_layout_pdu(rdpRdp* rdp, wStream* s) +{ + int index; + UINT32 monitorCount; + MONITOR_DEF* monitor; + MONITOR_DEF* monitorDefArray; + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + + Stream_Read_UINT32(s, monitorCount); /* monitorCount (4 bytes) */ + + if (Stream_GetRemainingLength(s) < (monitorCount * 20)) + return FALSE; + + monitorDefArray = (MONITOR_DEF*) malloc(sizeof(MONITOR_DEF) * monitorCount); + ZeroMemory(monitorDefArray, sizeof(MONITOR_DEF) * monitorCount); + + for (index = 0; index < monitorCount; index++) + { + monitor = &(monitorDefArray[index]); + + Stream_Read_UINT32(s, monitor->left); /* left (4 bytes) */ + Stream_Read_UINT32(s, monitor->top); /* top (4 bytes) */ + Stream_Read_UINT32(s, monitor->right); /* right (4 bytes) */ + Stream_Read_UINT32(s, monitor->bottom); /* bottom (4 bytes) */ + Stream_Read_UINT32(s, monitor->flags); /* flags (4 bytes) */ + } + + free(monitorDefArray); + + return TRUE; +} + +BOOL rdp_write_monitor_layout_pdu(wStream* s, UINT32 monitorCount, MONITOR_DEF* monitorDefArray) +{ + int index; + MONITOR_DEF* monitor; + + Stream_EnsureRemainingCapacity(s, 4 + (monitorCount * 20)); + + Stream_Write_UINT32(s, monitorCount); /* monitorCount (4 bytes) */ + + for (index = 0; index < monitorCount; index++) + { + monitor = &(monitorDefArray[index]); + + Stream_Write_UINT32(s, monitor->left); /* left (4 bytes) */ + Stream_Write_UINT32(s, monitor->top); /* top (4 bytes) */ + Stream_Write_UINT32(s, monitor->right); /* right (4 bytes) */ + Stream_Write_UINT32(s, monitor->bottom); /* bottom (4 bytes) */ + Stream_Write_UINT32(s, monitor->flags); /* flags (4 bytes) */ + } + + return TRUE; +} + int rdp_recv_data_pdu(rdpRdp* rdp, wStream* s) { BYTE type; @@ -618,29 +734,19 @@ int rdp_recv_data_pdu(rdpRdp* rdp, wStream* s) return -1; break; - case DATA_PDU_TYPE_INPUT: - break; - case DATA_PDU_TYPE_SYNCHRONIZE: if (!rdp_recv_synchronize_pdu(rdp, cs)) return -1; break; - case DATA_PDU_TYPE_REFRESH_RECT: - break; - case DATA_PDU_TYPE_PLAY_SOUND: if (!update_recv_play_sound(rdp->update, cs)) return -1; break; - case DATA_PDU_TYPE_SUPPRESS_OUTPUT: - break; - - case DATA_PDU_TYPE_SHUTDOWN_REQUEST: - break; - case DATA_PDU_TYPE_SHUTDOWN_DENIED: + if (!rdp_recv_server_shutdown_denied_pdu(rdp, cs)) + return -1; break; case DATA_PDU_TYPE_SAVE_SESSION_INFO: @@ -648,27 +754,19 @@ int rdp_recv_data_pdu(rdpRdp* rdp, wStream* s) return -1; break; - case DATA_PDU_TYPE_FONT_LIST: - break; - case DATA_PDU_TYPE_FONT_MAP: if (!rdp_recv_font_map_pdu(rdp, cs)) return -1; break; case DATA_PDU_TYPE_SET_KEYBOARD_INDICATORS: - break; - - case DATA_PDU_TYPE_BITMAP_CACHE_PERSISTENT_LIST: - break; - - case DATA_PDU_TYPE_BITMAP_CACHE_ERROR: + if (!rdp_recv_server_set_keyboard_indicators_pdu(rdp, cs)) + return -1; break; case DATA_PDU_TYPE_SET_KEYBOARD_IME_STATUS: - break; - - case DATA_PDU_TYPE_OFFSCREEN_CACHE_ERROR: + if (!rdp_recv_server_set_keyboard_ime_status_pdu(rdp, cs)) + return -1; break; case DATA_PDU_TYPE_SET_ERROR_INFO: @@ -676,19 +774,19 @@ int rdp_recv_data_pdu(rdpRdp* rdp, wStream* s) return -1; break; - case DATA_PDU_TYPE_DRAW_NINEGRID_ERROR: - break; - - case DATA_PDU_TYPE_DRAW_GDIPLUS_ERROR: - break; - case DATA_PDU_TYPE_ARC_STATUS: + if (!rdp_recv_server_auto_reconnect_status_pdu(rdp, cs)) + return -1; break; case DATA_PDU_TYPE_STATUS_INFO: + if (!rdp_recv_server_status_info_pdu(rdp, cs)) + return -1; break; case DATA_PDU_TYPE_MONITOR_LAYOUT: + if (!rdp_recv_monitor_layout_pdu(rdp, cs)) + return -1; break; default: From 7c6be746a62e924215fd77e24de0d62d2a188df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 12 Jan 2014 16:28:06 -0500 Subject: [PATCH 112/149] libfreerdp-codec: minor planar codec fix (undocumented behaviour for RLE) --- libfreerdp/codec/planar.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 6fa14fb59..a5b973ee6 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -348,7 +348,8 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) ((k + 1) == rle->cRawBytes) ? "" : " "); } - printf("] RUN[%d]\n", rle->nRunLength); + printf("] RUN[%d] offset: 0x%04x\n", rle->nRunLength, + rle->output - rle->outPlane); } #endif @@ -482,15 +483,30 @@ int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) return -1; } - *rle->output = PLANAR_CONTROL_BYTE(15, rle->cRawBytes); - rle->output++; + if (rle->nRunLength < 18) + { + *rle->output = PLANAR_CONTROL_BYTE(13, rle->cRawBytes); + rle->output++; - CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); - rle->rawValues += (rle->cRawBytes + 15); - rle->output += rle->cRawBytes; + CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); + rle->rawValues += (rle->cRawBytes + 13); + rle->output += rle->cRawBytes; - rle->nRunLength -= 15; - rle->cRawBytes = 0; + rle->nRunLength -= 13; + rle->cRawBytes = 0; + } + else + { + *rle->output = PLANAR_CONTROL_BYTE(15, rle->cRawBytes); + rle->output++; + + CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); + rle->rawValues += (rle->cRawBytes + 15); + rle->output += rle->cRawBytes; + + rle->nRunLength -= 15; + rle->cRawBytes = 0; + } /* continue */ } From 6dfaa8eefac3920d0e346306ac34b49899ce58b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 13 Jan 2014 22:16:10 -0500 Subject: [PATCH 113/149] libfreerdp-codec: got planar encoder to work 100% correctly --- libfreerdp/codec/planar.c | 491 +++++++----------- .../codec/test/TestFreeRDPCodecPlanar.c | 12 +- 2 files changed, 208 insertions(+), 295 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index a5b973ee6..bc0b71118 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -318,336 +318,245 @@ int freerdp_split_color_planes(BYTE* data, UINT32 format, int width, int height, return 0; } -struct _PLANAR_RLE_CONTEXT +int freerdp_bitmap_planar_write_rle_bytes(BYTE* pInBuffer, int cRawBytes, int nRunLength, BYTE* pOutBuffer, int outBufferSize) { - int width; - int height; - BYTE* output; - int nRunLength; - int cRawBytes; - BYTE* rawValues; - BYTE* rawScanline; - BYTE* outPlane; - int outPlaneSize; - int outSegmentSize; - int nRunLengthPrev; -}; -typedef struct _PLANAR_RLE_CONTEXT PLANAR_RLE_CONTEXT; + BYTE* pInput; + BYTE* pOutput; + BYTE controlByte; + int nBytesToWrite; -int freerdp_bitmap_planar_compress_plane_rle_segment(PLANAR_RLE_CONTEXT* rle) -{ -#if 0 + pInput = pInBuffer; + pOutput = pOutBuffer; + + if (!cRawBytes && !nRunLength) + return 0; + + if (nRunLength < 3) { - int k; - - printf("RAW(%d)[", rle->cRawBytes); - - for (k = 0; k < rle->cRawBytes; k++) - { - printf("%02x%s", rle->rawValues[k], - ((k + 1) == rle->cRawBytes) ? "" : " "); - } - - printf("] RUN[%d] offset: 0x%04x\n", rle->nRunLength, - rle->output - rle->outPlane); + cRawBytes += nRunLength; + nRunLength = 0; } -#endif - while ((rle->cRawBytes != 0) || (rle->nRunLength != 0)) + while (cRawBytes) { - //printf("|| cRawBytes: %d nRunLength: %d\n", rle->cRawBytes, rle->nRunLength); - - if (rle->nRunLength < 3) + if (cRawBytes < 16) { - rle->cRawBytes += rle->nRunLength; - rle->nRunLength = 0; - } - - if ((rle->rawValues - rle->rawScanline) > rle->width) - { - printf("rawValues overflow! %d\n", rle->rawValues - rle->rawScanline); - return 0; - } - - if (rle->cRawBytes > 15) - { - rle->outSegmentSize = 1 + 15; - - if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + if (nRunLength > 15) { - printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); - return -1; - } - - *rle->output = PLANAR_CONTROL_BYTE(0, 15); - rle->output++; - - CopyMemory(rle->output, rle->rawValues, 15); - rle->cRawBytes -= 15; - rle->rawValues += 15; - rle->output += 15; - - /* continue */ - } - else if (rle->cRawBytes == 0) - { - if (rle->nRunLength > 47) - { - rle->outSegmentSize = 1; - - if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) + if (nRunLength < 18) { - printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); - return -1; - } - - *rle->output = PLANAR_CONTROL_BYTE(2, 15); - rle->nRunLength -= 47; - rle->output++; - - /* continue */ - } - else if (rle->nRunLength > 31) - { - rle->outSegmentSize = 1; - - if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) - { - printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); - return -1; - } - - *rle->output = PLANAR_CONTROL_BYTE(2, (rle->nRunLength - 32)); - rle->output++; - - rle->rawValues += (rle->cRawBytes + rle->nRunLength); - rle->output += rle->cRawBytes; - - rle->cRawBytes = 0; - rle->nRunLength = 0; - - return 0; /* finish */ - } - else if (rle->nRunLength > 15) - { - rle->outSegmentSize = 1; - - if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) - { - printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); - return -1; - } - - *rle->output = PLANAR_CONTROL_BYTE(1, (rle->nRunLength - 16)); - rle->output++; - - rle->rawValues += (rle->cRawBytes + rle->nRunLength); - rle->output += rle->cRawBytes; - - rle->cRawBytes = 0; - rle->nRunLength = 0; - - return 0; /* finish */ - } - else - { - rle->outSegmentSize = 1 + rle->cRawBytes; - - if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) - { - printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); - return -1; - } - - *rle->output = PLANAR_CONTROL_BYTE(rle->nRunLength, rle->cRawBytes); - rle->output++; - - rle->rawValues += (rle->cRawBytes + rle->nRunLength); - rle->output += rle->cRawBytes; - - rle->cRawBytes = 0; - rle->nRunLength = 0; - - return 0; /* finish */ - } - } - else if (rle->cRawBytes < 16) - { - if (rle->nRunLength > 15) - { - rle->outSegmentSize = 1 + rle->cRawBytes; - - if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) - { - printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); - return -1; - } - - if (rle->nRunLength < 18) - { - *rle->output = PLANAR_CONTROL_BYTE(13, rle->cRawBytes); - rle->output++; - - CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); - rle->rawValues += (rle->cRawBytes + 13); - rle->output += rle->cRawBytes; - - rle->nRunLength -= 13; - rle->cRawBytes = 0; + controlByte = PLANAR_CONTROL_BYTE(13, cRawBytes); + nRunLength -= 13; + cRawBytes = 0; } else { - *rle->output = PLANAR_CONTROL_BYTE(15, rle->cRawBytes); - rle->output++; - - CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); - rle->rawValues += (rle->cRawBytes + 15); - rle->output += rle->cRawBytes; - - rle->nRunLength -= 15; - rle->cRawBytes = 0; + controlByte = PLANAR_CONTROL_BYTE(15, cRawBytes); + nRunLength -= 15; + cRawBytes = 0; } - - /* continue */ } else { - rle->outSegmentSize = 1 + rle->cRawBytes; - - if (((rle->output - rle->outPlane) + rle->outSegmentSize) > rle->outPlaneSize) - { - printf("overflow: %d > %d\n", ((rle->output - rle->outPlane) + rle->outSegmentSize), rle->outPlaneSize); - return -1; - } - - *rle->output = PLANAR_CONTROL_BYTE(rle->nRunLength, rle->cRawBytes); - rle->output++; - - CopyMemory(rle->output, rle->rawValues, rle->cRawBytes); - rle->rawValues += (rle->cRawBytes + rle->nRunLength); - rle->output += rle->cRawBytes; - - rle->cRawBytes = 0; - rle->nRunLength = 0; - - return 0; /* finish */ + controlByte = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes); + nRunLength = 0; + cRawBytes = 0; } } + else + { + controlByte = PLANAR_CONTROL_BYTE(0, 15); + cRawBytes -= 15; + } + + if (outBufferSize < 1) + return 0; + + outBufferSize--; + + *pOutput = controlByte; + pOutput++; + + nBytesToWrite = (int) (controlByte >> 4); + + if (nBytesToWrite) + { + if (outBufferSize < nBytesToWrite) + return 0; + + outBufferSize -= nBytesToWrite; + CopyMemory(pOutput, pInput, nBytesToWrite); + pOutput += nBytesToWrite; + pInput += nBytesToWrite; + } } - return 0; + while (nRunLength) + { + if (nRunLength > 47) + { + if (nRunLength < 50) + { + controlByte = PLANAR_CONTROL_BYTE(2, 13); + nRunLength -= 45; + } + else + { + controlByte = PLANAR_CONTROL_BYTE(2, 15); + nRunLength -= 47; + } + } + else if (nRunLength > 31) + { + controlByte = PLANAR_CONTROL_BYTE(2, (nRunLength - 32)); + nRunLength = 0; + } + else if (nRunLength > 15) + { + controlByte = PLANAR_CONTROL_BYTE(1, (nRunLength - 16)); + nRunLength = 0; + } + else + { + controlByte = PLANAR_CONTROL_BYTE(nRunLength, 0); + nRunLength = 0; + } + + if (outBufferSize < 1) + return 0; + + --outBufferSize; + *pOutput = controlByte; + pOutput++; + } + + return (pOutput - pOutBuffer); +} + +int freerdp_bitmap_planar_encode_rle_bytes(BYTE* pInBuffer, int inBufferSize, BYTE* pOutBuffer, int outBufferSize) +{ + BYTE symbol; + BYTE* pInput; + BYTE* pOutput; + BYTE* pBytes; + int cRawBytes; + int nRunLength; + int bSymbolMatch; + int nBytesWritten; + int nTotalBytesWritten; + + symbol = 0; + cRawBytes = 0; + nRunLength = 0; + pInput = pInBuffer; + pOutput = pOutBuffer; + nTotalBytesWritten = 0; + + if (!outBufferSize) + return 0; + + do + { + if (!inBufferSize) + break; + + bSymbolMatch = (symbol == *pInput) ? TRUE : FALSE; + symbol = *pInput; + pInput++; + inBufferSize--; + + if (nRunLength && !bSymbolMatch) + { + if (nRunLength < 3) + { + cRawBytes += nRunLength; + nRunLength = 0; + } + else + { + pBytes = pInput - (cRawBytes + nRunLength + 1); + + nBytesWritten = freerdp_bitmap_planar_write_rle_bytes(pBytes, + cRawBytes, nRunLength, pOutput, outBufferSize); + + nRunLength = 0; + + if (!nBytesWritten || (nBytesWritten > outBufferSize)) + return nRunLength; + + nTotalBytesWritten += nBytesWritten; + outBufferSize -= nBytesWritten; + pOutput += nBytesWritten; + cRawBytes = 0; + } + } + + nRunLength += bSymbolMatch; + cRawBytes += (!bSymbolMatch) ? TRUE : FALSE; + } + while (outBufferSize); + + if (cRawBytes || nRunLength) + { + pBytes = pInput - (cRawBytes + nRunLength); + + nBytesWritten = freerdp_bitmap_planar_write_rle_bytes(pBytes, + cRawBytes, nRunLength, pOutput, outBufferSize); + + if (!nBytesWritten) + return 0; + + nTotalBytesWritten += nBytesWritten; + } + + if (inBufferSize) + return 0; + + return nTotalBytesWritten; } BYTE* freerdp_bitmap_planar_compress_plane_rle(BYTE* inPlane, int width, int height, BYTE* outPlane, int* dstSize) { - int i, j; - int bSymbolMatch; - int bSequenceEnd; - PLANAR_RLE_CONTEXT rle_s; - PLANAR_RLE_CONTEXT* rle = &rle_s; + int index; + BYTE* pInput; + BYTE* pOutput; + int outBufferSize; + int nBytesWritten; + int nTotalBytesWritten; if (!outPlane) { - rle->outPlaneSize = width * height * 2; - outPlane = malloc(rle->outPlaneSize); + outBufferSize = width * height; + outPlane = malloc(outBufferSize); } else { - rle->outPlaneSize = *dstSize; + outBufferSize = *dstSize; } - rle->output = outPlane; - rle->outPlane = outPlane; - rle->width = width; - rle->height = height; + index = 0; + pInput = inPlane; + pOutput = outPlane; + nTotalBytesWritten = 0; - for (i = 0; i < height; i++) + while (outBufferSize) { - rle->nRunLength = 0; - rle->cRawBytes = 1; + nBytesWritten = freerdp_bitmap_planar_encode_rle_bytes(pInput, width, pOutput, outBufferSize); - rle->rawScanline = &inPlane[i * width]; - rle->rawValues = rle->rawScanline; + if ((!nBytesWritten) || (nBytesWritten > outBufferSize)) + return NULL; - rle->nRunLengthPrev = 0; + outBufferSize -= nBytesWritten; + nTotalBytesWritten += nBytesWritten; + pOutput += nBytesWritten; + pInput += width; + index++; - //winpr_HexDump(rle->rawScanline, width); - - for (j = 1; j <= width; j++) - { - bSymbolMatch = FALSE; - bSequenceEnd = (j == width) ? TRUE : FALSE; - - if (!bSequenceEnd) - { - if (rle->rawScanline[j] == rle->rawValues[rle->cRawBytes - 1]) - { - bSymbolMatch = TRUE; - } - else - { - //printf("mismatch: nRunLength: %d cRawBytes: %d\n", rle->nRunLength, rle->cRawBytes); - - if (rle->nRunLength < 3) - { - rle->cRawBytes += rle->nRunLength; - rle->nRunLength = 0; - } - else - { - bSequenceEnd = TRUE; - } - } - } - - if (bSymbolMatch) - { - rle->nRunLength++; - } - - //printf("j: %d [0x%02X] cRawBytes: %d nRunLength: %d bSymbolMatch: %d bSequenceEnd: %d\n", - // j, rle->rawScanline[j], rle->cRawBytes, rle->nRunLength, bSymbolMatch, bSequenceEnd); - - if (bSequenceEnd) - { - int nRunLengthPrev; - - if (rle->nRunLength < 3) - { - rle->cRawBytes += rle->nRunLength; - rle->nRunLength = 0; - } - - if ((rle->cRawBytes == 1) && (*rle->rawValues == 0)) - { - if (!rle->nRunLengthPrev) - { - rle->cRawBytes = 0; - rle->nRunLength++; - } - } - - nRunLengthPrev = rle->nRunLength; - - if (freerdp_bitmap_planar_compress_plane_rle_segment(rle) < 0) - return NULL; - - rle->nRunLengthPrev = nRunLengthPrev; - - rle->nRunLength = 0; - rle->cRawBytes = 1; - } - else - { - if (!bSymbolMatch) - { - rle->cRawBytes++; - } - } - } + if (index >= height) + break; } - *dstSize = (rle->output - outPlane); + *dstSize = nTotalBytesWritten; return outPlane; } diff --git a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c index ffd97e252..e4f096c76 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecPlanar.c @@ -2987,7 +2987,7 @@ int test_individual_planes_encoding_rle() if (!freerdp_bitmap_planar_compress_plane_rle(planar->deltaPlanes[1], width, height, pOutput, &dstSizes[1])) { printf("failed to encode red plane\n"); - return 0; + return -1; } planar->rlePlanes[1] = pOutput; @@ -2998,7 +2998,7 @@ int test_individual_planes_encoding_rle() { printf("RedPlaneRle unexpected size: actual: %d, expected: %d\n", dstSizes[1], sizeof(TEST_64X64_RED_PLANE_RLE)); - return -1; + //return -1; } compareSize = (dstSizes[1] > sizeof(TEST_64X64_RED_PLANE_RLE)) ? sizeof(TEST_64X64_RED_PLANE_RLE) : dstSizes[1]; @@ -3011,7 +3011,7 @@ int test_individual_planes_encoding_rle() //winpr_HexDump((BYTE*) TEST_64X64_RED_PLANE_RLE, sizeof(TEST_64X64_RED_PLANE_RLE)); printf("RedPlaneRle Actual (%d):\n", dstSizes[1]); - winpr_HexDump(planar->rlePlanes[1], dstSizes[1]); + //winpr_HexDump(planar->rlePlanes[1], dstSizes[1]); return -1; } @@ -3122,6 +3122,7 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) format = FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_ARGB, FREERDP_PIXEL_FLIP_NONE); +#if 0 freerdp_bitmap_compress_planar(planar, srcBitmap32, format, 32, 32, 32 * 4, NULL, &dstSize); freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RLE_SCANLINE_UNCOMPRESSED, 12, 1, NULL, &dstSize); @@ -3129,7 +3130,9 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) freerdp_bitmap_planar_delta_encode_plane((BYTE*) TEST_RDP6_SCANLINES_ABSOLUTE, 6, 3, NULL); freerdp_bitmap_planar_compress_plane_rle((BYTE*) TEST_RDP6_SCANLINES_DELTA_2C_ENCODED_UNSIGNED, 6, 3, NULL, &dstSize); +#endif +#if 1 for (i = 4; i < 64; i += 4) { width = i; @@ -3336,10 +3339,11 @@ int TestFreeRDPCodecPlanar(int argc, char* argv[]) free(compressedBitmap); free(decompressedBitmap); +#endif if (test_individual_planes_encoding_rle() < 0) { - //return -1; + return -1; } /* Experimental Case 03 */ From c1a6eda7405e1533ab7bba2fabf00b277ae7013c Mon Sep 17 00:00:00 2001 From: Hardening Date: Tue, 14 Jan 2014 09:44:38 +0100 Subject: [PATCH 114/149] Improve performance for delta_encoder This patch inline the case of the first line to drop the if() that was done at each loop. Some variable have been renamed for code clarity. --- libfreerdp/codec/planar.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index bc0b71118..a320da5a3 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -618,41 +618,31 @@ int freerdp_bitmap_planar_compress_planes_rle(BYTE* inPlanes[4], int width, int BYTE* freerdp_bitmap_planar_delta_encode_plane(BYTE* inPlane, int width, int height, BYTE* outPlane) { char s2c; - BYTE u2c; int delta; - int i, j, k; + int y, x; + BYTE *outPtr, *srcPtr, *prevLinePtr; if (!outPlane) - { outPlane = (BYTE*) malloc(width * height); - } - k = 0; + // first line is copied as is + CopyMemory(outPlane, inPlane, width); - for (i = 0; i < height; i++) + outPtr = outPlane + width; + srcPtr = inPlane + width; + prevLinePtr = inPlane; + + for (y = 1; y < height; y++) { - for (j = 0; j < width; j++) + for (x = 0; x < width; x++, outPtr++, srcPtr++, prevLinePtr++) { - if (i < 1) - { - delta = inPlane[j]; + delta = *srcPtr - *prevLinePtr; - s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); - } - else - { - delta = inPlane[(i * width) + j] - inPlane[((i - 1) * width) + j]; + s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (-delta)) + 1); - s2c = (delta >= 0) ? (char) delta : (char) (~((BYTE) (delta * -1)) + 1); + s2c = (s2c >= 0) ? (s2c << 1) : (char) (((~((BYTE) s2c) + 1) << 1) - 1); - s2c = (s2c >= 0) ? (s2c << 1) : (char) (((~((BYTE) s2c) + 1) << 1) - 1); - } - - u2c = (BYTE) s2c; - - outPlane[(i * width) + j] = u2c; - - k++; + *outPtr = (BYTE)s2c; } } From 38b125f33699caf1ff33dcd5832ed115938ad8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 23 Jan 2014 16:00:02 -0500 Subject: [PATCH 115/149] libfreerdp-core: fix and update parsing of server-side GCC client data blocks --- include/freerdp/settings.h | 13 +- libfreerdp/core/gcc.c | 237 +++++++++++++++++++++++++++++++------ libfreerdp/core/gcc.h | 46 +++---- 3 files changed, 238 insertions(+), 58 deletions(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 361cfba08..99e892b30 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -51,7 +51,8 @@ #define CS_CLUSTER 0xC004 #define CS_MONITOR 0xC005 #define CS_MCS_MSGCHANNEL 0xC006 -#define CS_MULTITRANSPORT 0xC008 +#define CS_MONITOR_EX 0xC008 +#define CS_MULTITRANSPORT 0xC00A /* Server to Client (SC) data blocks */ #define SC_CORE 0x0C01 @@ -408,6 +409,16 @@ struct _MONITOR_DEF }; typedef struct _MONITOR_DEF MONITOR_DEF; +struct _MONITOR_ATTRIBUTES +{ + UINT32 physicalWidth; + UINT32 physicalHeight; + UINT32 orientation; + UINT32 desktopScaleFactor; + UINT32 deviceScaleFactor; +}; +typedef struct _MONITOR_ATTRIBUTES MONITOR_ATTRIBUTES; + /* Device Redirection */ #define RDPDR_DTYP_SERIAL 0x00000001 diff --git a/libfreerdp/core/gcc.c b/libfreerdp/core/gcc.c index d51f4c004..d8d8ffae1 100644 --- a/libfreerdp/core/gcc.c +++ b/libfreerdp/core/gcc.c @@ -319,12 +319,16 @@ BOOL gcc_read_client_data_blocks(wStream* s, rdpSettings* settings, int length) { UINT16 type; UINT16 blockLength; - int pos; + int begPos, endPos; while (length > 0) { - pos = Stream_GetPosition(s); - if(!gcc_read_user_data_header(s, &type, &blockLength)) + begPos = Stream_GetPosition(s); + + if (!gcc_read_user_data_header(s, &type, &blockLength)) + return FALSE; + + if (Stream_GetRemainingLength(s) < (blockLength - 4)) return FALSE; switch (type) @@ -354,12 +358,37 @@ BOOL gcc_read_client_data_blocks(wStream* s, rdpSettings* settings, int length) return FALSE; break; + case CS_MCS_MSGCHANNEL: + if (!gcc_read_client_message_channel_data(s, settings, blockLength - 4)) + return FALSE; + break; + + case CS_MONITOR_EX: + if (!gcc_read_client_monitor_extended_data(s, settings, blockLength - 4)) + return FALSE; + break; + + case CS_MULTITRANSPORT: + if (!gcc_read_client_multitransport_channel_data(s, settings, blockLength - 4)) + return FALSE; + break; + default: + fprintf(stderr, "Unknown GCC client data block: 0x%04X\n", type); + Stream_Seek(s, blockLength - 4); break; } + endPos = Stream_GetPosition(s); + + if (endPos != (begPos + blockLength)) + { + fprintf(stderr, "Error parsing GCC client data block 0x%04X: Actual Offset: %d Expected Offset: %d\n", + type, endPos, begPos + blockLength); + } + length -= blockLength; - Stream_SetPosition(s, pos + blockLength); + Stream_SetPosition(s, begPos + blockLength); } return TRUE; @@ -506,20 +535,25 @@ BOOL gcc_read_client_core_data(wStream* s, rdpSettings* settings, UINT16 blockLe UINT16 highColorDepth = 0; UINT16 supportedColorDepths = 0; UINT32 serverSelectedProtocol = 0; + UINT32 desktopPhysicalWidth = 0; + UINT32 desktopPhysicalHeight = 0; + UINT16 desktopOrientation = 0; + UINT32 desktopScaleFactor = 0; + UINT32 deviceScaleFactor = 0; /* Length of all required fields, until imeFileName */ if (blockLength < 128) return FALSE; - Stream_Read_UINT32(s, version); /* version */ + Stream_Read_UINT32(s, version); /* version (4 bytes) */ settings->RdpVersion = (version == RDP_VERSION_4 ? 4 : 7); - Stream_Read_UINT16(s, settings->DesktopWidth); /* DesktopWidth */ - Stream_Read_UINT16(s, settings->DesktopHeight); /* DesktopHeight */ - Stream_Read_UINT16(s, colorDepth); /* ColorDepth */ - Stream_Seek_UINT16(s); /* SASSequence (Secure Access Sequence) */ - Stream_Read_UINT32(s, settings->KeyboardLayout); /* KeyboardLayout */ - Stream_Read_UINT32(s, settings->ClientBuild); /* ClientBuild */ + Stream_Read_UINT16(s, settings->DesktopWidth); /* DesktopWidth (2 bytes) */ + Stream_Read_UINT16(s, settings->DesktopHeight); /* DesktopHeight (2 bytes) */ + Stream_Read_UINT16(s, colorDepth); /* ColorDepth (2 bytes) */ + Stream_Seek_UINT16(s); /* SASSequence (Secure Access Sequence) (2 bytes) */ + Stream_Read_UINT32(s, settings->KeyboardLayout); /* KeyboardLayout (4 bytes) */ + Stream_Read_UINT32(s, settings->ClientBuild); /* ClientBuild (4 bytes) */ /* clientName (32 bytes, null-terminated unicode, truncated to 15 characters) */ ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), 32 / 2, &str, 0, NULL, NULL); @@ -529,11 +563,11 @@ BOOL gcc_read_client_core_data(wStream* s, rdpSettings* settings, UINT16 blockLe free(str); str = NULL; - Stream_Read_UINT32(s, settings->KeyboardType); /* KeyboardType */ - Stream_Read_UINT32(s, settings->KeyboardSubType); /* KeyboardSubType */ - Stream_Read_UINT32(s, settings->KeyboardFunctionKey); /* KeyboardFunctionKey */ + Stream_Read_UINT32(s, settings->KeyboardType); /* KeyboardType (4 bytes) */ + Stream_Read_UINT32(s, settings->KeyboardSubType); /* KeyboardSubType (4 bytes) */ + Stream_Read_UINT32(s, settings->KeyboardFunctionKey); /* KeyboardFunctionKey (4 bytes) */ - Stream_Seek(s, 64); /* imeFileName */ + Stream_Seek(s, 64); /* imeFileName (64 bytes) */ blockLength -= 128; @@ -548,56 +582,81 @@ BOOL gcc_read_client_core_data(wStream* s, rdpSettings* settings, UINT16 blockLe { if (blockLength < 2) break; - Stream_Read_UINT16(s, postBeta2ColorDepth); /* postBeta2ColorDepth */ + Stream_Read_UINT16(s, postBeta2ColorDepth); /* postBeta2ColorDepth (2 bytes) */ blockLength -= 2; if (blockLength < 2) break; - Stream_Seek_UINT16(s); /* clientProductID */ + Stream_Seek_UINT16(s); /* clientProductID (2 bytes) */ blockLength -= 2; if (blockLength < 4) break; - Stream_Seek_UINT32(s); /* serialNumber */ + Stream_Seek_UINT32(s); /* serialNumber (4 bytes) */ blockLength -= 4; if (blockLength < 2) break; - Stream_Read_UINT16(s, highColorDepth); /* highColorDepth */ + Stream_Read_UINT16(s, highColorDepth); /* highColorDepth (2 bytes) */ blockLength -= 2; if (blockLength < 2) break; - Stream_Read_UINT16(s, supportedColorDepths); /* supportedColorDepths */ + Stream_Read_UINT16(s, supportedColorDepths); /* supportedColorDepths (2 bytes) */ blockLength -= 2; if (blockLength < 2) break; - Stream_Read_UINT16(s, settings->EarlyCapabilityFlags); /* earlyCapabilityFlags */ + Stream_Read_UINT16(s, settings->EarlyCapabilityFlags); /* earlyCapabilityFlags (2 bytes) */ blockLength -= 2; if (blockLength < 64) break; ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), 64 / 2, &str, 0, NULL, NULL); - Stream_Seek(s, 64); + Stream_Seek(s, 64); /* clientDigProductId (64 bytes) */ sprintf_s(settings->ClientProductId, 32, "%s", str); free(str); blockLength -= 64; if (blockLength < 1) break; - Stream_Read_UINT8(s, settings->PerformanceFlags); /* connectionType */ + Stream_Read_UINT8(s, settings->PerformanceFlags); /* connectionType (1 byte) */ blockLength -= 1; if (blockLength < 1) break; - Stream_Seek_UINT8(s); /* pad1octet */ + Stream_Seek_UINT8(s); /* pad1octet (1 byte) */ blockLength -= 1; if (blockLength < 4) break; - Stream_Read_UINT32(s, serverSelectedProtocol); /* serverSelectedProtocol */ + Stream_Read_UINT32(s, serverSelectedProtocol); /* serverSelectedProtocol (4 bytes) */ + blockLength -= 4; + + if (blockLength < 4) + break; + Stream_Read_UINT32(s, desktopPhysicalWidth); /* desktopPhysicalWidth (4 bytes) */ + blockLength -= 4; + + if (blockLength < 4) + break; + Stream_Read_UINT32(s, desktopPhysicalHeight); /* desktopPhysicalHeight (4 bytes) */ + blockLength -= 4; + + if (blockLength < 2) + break; + Stream_Read_UINT16(s, desktopOrientation); /* desktopOrientation (2 bytes) */ + blockLength -= 2; + + if (blockLength < 4) + break; + Stream_Read_UINT32(s, desktopScaleFactor); /* desktopScaleFactor (4 bytes) */ + blockLength -= 4; + + if (blockLength < 4) + break; + Stream_Read_UINT32(s, deviceScaleFactor); /* deviceScaleFactor (4 bytes) */ blockLength -= 4; if (settings->SelectedProtocol != serverSelectedProtocol) @@ -772,7 +831,7 @@ BOOL gcc_read_server_core_data(wStream* s, rdpSettings* settings) UINT32 version; UINT32 clientRequestedProtocols; - if(Stream_GetRemainingLength(s) < 8) + if (Stream_GetRemainingLength(s) < 8) return FALSE; Stream_Read_UINT32(s, version); /* version */ Stream_Read_UINT32(s, clientRequestedProtocols); /* clientRequestedProtocols */ @@ -1138,23 +1197,25 @@ BOOL gcc_read_server_network_data(wStream* s, rdpSettings* settings) UINT16 channelCount, channelsToTreat; UINT16 channelId; - if(Stream_GetRemainingLength(s) < 4) + if (Stream_GetRemainingLength(s) < 4) return FALSE; + Stream_Read_UINT16(s, MCSChannelId); /* MCSChannelId */ Stream_Read_UINT16(s, channelCount); /* channelCount */ channelsToTreat = channelCount; + if (channelCount != settings->ChannelCount) { fprintf(stderr, "requested %d channels, got %d instead\n", settings->ChannelCount, channelCount); - // we ensure that the response is not bigger than the request + /* we ensure that the response is not bigger than the request */ if (channelCount > settings->ChannelCount) channelsToTreat = settings->ChannelCount; } - if(Stream_GetRemainingLength(s) < channelCount * 2) + if (Stream_GetRemainingLength(s) < channelCount * 2) return FALSE; for (i = 0; i < channelsToTreat; i++) @@ -1197,18 +1258,16 @@ void gcc_write_server_network_data(wStream* s, rdpSettings* settings) BOOL gcc_read_client_cluster_data(wStream* s, rdpSettings* settings, UINT16 blockLength) { UINT32 flags; + UINT32 redirectedSessionId; - if (blockLength < 4) + if (blockLength < 8) return FALSE; Stream_Read_UINT32(s, flags); /* flags */ + Stream_Read_UINT32(s, redirectedSessionId); /* redirectedSessionId */ - if ((flags & REDIRECTED_SESSIONID_FIELD_VALID)) - { - if(blockLength < 8) - return FALSE; - Stream_Read_UINT32(s, settings->RedirectedSessionId); /* redirectedSessionID */ - } + if (flags & REDIRECTED_SESSIONID_FIELD_VALID) + settings->RedirectedSessionId = redirectedSessionId; return TRUE; } @@ -1244,7 +1303,34 @@ void gcc_write_client_cluster_data(wStream* s, rdpSettings* settings) BOOL gcc_read_client_monitor_data(wStream* s, rdpSettings* settings, UINT16 blockLength) { - fprintf(stderr, "CS_MONITOR\n"); + UINT32 index; + UINT32 flags; + UINT32 monitorCount; + MONITOR_DEF* monitorDefArray; + + if (blockLength < 8) + return FALSE; + + Stream_Read_UINT32(s, flags); /* flags */ + Stream_Read_UINT32(s, monitorCount); /* monitorCount */ + + if (blockLength < (8 + (monitorCount * 20))) + return FALSE; + + monitorDefArray = (MONITOR_DEF*) malloc(sizeof(MONITOR_DEF) * monitorCount); + ZeroMemory(monitorDefArray, sizeof(MONITOR_DEF) * monitorCount); + + for (index = 0; index < monitorCount; index++) + { + Stream_Read_UINT32(s, monitorDefArray[index].left); /* left */ + Stream_Read_UINT32(s, monitorDefArray[index].top); /* top */ + Stream_Read_UINT32(s, monitorDefArray[index].right); /* right */ + Stream_Read_UINT32(s, monitorDefArray[index].bottom); /* bottom */ + Stream_Read_UINT32(s, monitorDefArray[index].flags); /* flags */ + } + + free(monitorDefArray); + return TRUE; } @@ -1285,3 +1371,80 @@ void gcc_write_client_monitor_data(wStream* s, rdpSettings* settings) } } } + +BOOL gcc_read_client_monitor_extended_data(wStream* s, rdpSettings* settings, UINT16 blockLength) +{ + UINT32 index; + UINT32 flags; + UINT32 monitorCount; + UINT32 monitorAttributeSize; + MONITOR_ATTRIBUTES* monitorAttributesArray; + + if (blockLength < 8) + return FALSE; + + Stream_Read_UINT32(s, flags); /* flags */ + Stream_Read_UINT32(s, monitorAttributeSize); /* monitorAttributeSize */ + Stream_Read_UINT32(s, monitorCount); /* monitorCount */ + + if (monitorAttributeSize != 20) + return FALSE; + + if (blockLength < (12 + (monitorCount * monitorAttributeSize))) + return FALSE; + + monitorAttributesArray = (MONITOR_ATTRIBUTES*) malloc(sizeof(MONITOR_ATTRIBUTES) * monitorCount); + ZeroMemory(monitorAttributesArray, sizeof(MONITOR_ATTRIBUTES) * monitorCount); + + for (index = 0; index < monitorCount; index++) + { + Stream_Read_UINT32(s, monitorAttributesArray[index].physicalWidth); /* physicalWidth */ + Stream_Read_UINT32(s, monitorAttributesArray[index].physicalHeight); /* physicalHeight */ + Stream_Read_UINT32(s, monitorAttributesArray[index].orientation); /* orientation */ + Stream_Read_UINT32(s, monitorAttributesArray[index].desktopScaleFactor); /* desktopScaleFactor */ + Stream_Read_UINT32(s, monitorAttributesArray[index].deviceScaleFactor); /* deviceScaleFactor */ + } + + free(monitorAttributesArray); + + return TRUE; +} + +void gcc_write_client_monitor_extended_data(wStream* s, rdpSettings* settings) +{ + +} + +BOOL gcc_read_client_message_channel_data(wStream* s, rdpSettings* settings, UINT16 blockLength) +{ + UINT32 flags; + + if (blockLength < 4) + return FALSE; + + Stream_Read_UINT32(s, flags); + + return TRUE; +} + +void gcc_write_client_message_channel_data(wStream* s, rdpSettings* settings) +{ + +} + +BOOL gcc_read_client_multitransport_channel_data(wStream* s, rdpSettings* settings, UINT16 blockLength) +{ + UINT32 flags; + + if (blockLength < 4) + return FALSE; + + Stream_Read_UINT32(s, flags); + + return TRUE; +} + +void gcc_write_client_multitransport_channel_data(wStream* s, rdpSettings* settings) +{ + +} diff --git a/libfreerdp/core/gcc.h b/libfreerdp/core/gcc.h index 3432656b8..38bffb218 100644 --- a/libfreerdp/core/gcc.h +++ b/libfreerdp/core/gcc.h @@ -33,27 +33,33 @@ BOOL gcc_read_conference_create_request(wStream* s, rdpSettings* settings); void gcc_write_conference_create_request(wStream* s, wStream* user_data); BOOL gcc_read_conference_create_response(wStream* s, rdpSettings* settings); void gcc_write_conference_create_response(wStream* s, wStream* user_data); -BOOL gcc_read_client_data_blocks(wStream* s, rdpSettings *settings, int length); -void gcc_write_client_data_blocks(wStream* s, rdpSettings *settings); -BOOL gcc_read_server_data_blocks(wStream* s, rdpSettings *settings, int length); -void gcc_write_server_data_blocks(wStream* s, rdpSettings *settings); +BOOL gcc_read_client_data_blocks(wStream* s, rdpSettings* settings, int length); +void gcc_write_client_data_blocks(wStream* s, rdpSettings* settings); +BOOL gcc_read_server_data_blocks(wStream* s, rdpSettings* settings, int length); +void gcc_write_server_data_blocks(wStream* s, rdpSettings* settings); BOOL gcc_read_user_data_header(wStream* s, UINT16* type, UINT16* length); void gcc_write_user_data_header(wStream* s, UINT16 type, UINT16 length); -BOOL gcc_read_client_core_data(wStream* s, rdpSettings *settings, UINT16 blockLength); -void gcc_write_client_core_data(wStream* s, rdpSettings *settings); -BOOL gcc_read_server_core_data(wStream* s, rdpSettings *settings); -void gcc_write_server_core_data(wStream* s, rdpSettings *settings); -BOOL gcc_read_client_security_data(wStream* s, rdpSettings *settings, UINT16 blockLength); -void gcc_write_client_security_data(wStream* s, rdpSettings *settings); -BOOL gcc_read_server_security_data(wStream* s, rdpSettings *settings); -void gcc_write_server_security_data(wStream* s, rdpSettings *settings); -BOOL gcc_read_client_network_data(wStream* s, rdpSettings *settings, UINT16 blockLength); -void gcc_write_client_network_data(wStream* s, rdpSettings *settings); -BOOL gcc_read_server_network_data(wStream* s, rdpSettings *settings); -void gcc_write_server_network_data(wStream* s, rdpSettings *settings); -BOOL gcc_read_client_cluster_data(wStream* s, rdpSettings *settings, UINT16 blockLength); -void gcc_write_client_cluster_data(wStream* s, rdpSettings *settings); -BOOL gcc_read_client_monitor_data(wStream* s, rdpSettings *settings, UINT16 blockLength); -void gcc_write_client_monitor_data(wStream* s, rdpSettings *settings); +BOOL gcc_read_client_core_data(wStream* s, rdpSettings* settings, UINT16 blockLength); +void gcc_write_client_core_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_server_core_data(wStream* s, rdpSettings* settings); +void gcc_write_server_core_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_client_security_data(wStream* s, rdpSettings* settings, UINT16 blockLength); +void gcc_write_client_security_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_server_security_data(wStream* s, rdpSettings* settings); +void gcc_write_server_security_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_client_network_data(wStream* s, rdpSettings* settings, UINT16 blockLength); +void gcc_write_client_network_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_server_network_data(wStream* s, rdpSettings* settings); +void gcc_write_server_network_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_client_cluster_data(wStream* s, rdpSettings* settings, UINT16 blockLength); +void gcc_write_client_cluster_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_client_monitor_data(wStream* s, rdpSettings* settings, UINT16 blockLength); +void gcc_write_client_monitor_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_client_monitor_extended_data(wStream* s, rdpSettings* settings, UINT16 blockLength); +void gcc_write_client_monitor_extended_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_client_message_channel_data(wStream* s, rdpSettings* settings, UINT16 blockLength); +void gcc_write_client_message_channel_data(wStream* s, rdpSettings* settings); +BOOL gcc_read_client_multitransport_channel_data(wStream* s, rdpSettings* settings, UINT16 blockLength); +void gcc_write_client_multitransport_channel_data(wStream* s, rdpSettings* settings); #endif /* FREERDP_CORE_GCC_H */ From 1c0e874b5be411a04efbc440716a5d009dc0338a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Thu, 23 Jan 2014 17:41:05 -0500 Subject: [PATCH 116/149] libfreerdp-core: more GCC and MCS fixes --- include/freerdp/settings.h | 3 ++- libfreerdp/core/gcc.c | 28 +++++++++++++++++++++++++--- libfreerdp/core/gcc.h | 2 ++ libfreerdp/core/mcs.c | 13 ++++++++++++- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 99e892b30..eeb106190 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -58,7 +58,8 @@ #define SC_CORE 0x0C01 #define SC_SECURITY 0x0C02 #define SC_NET 0x0C03 -#define SC_MULTITRANSPORT 0x0C06 +#define SC_MCS_MSGCHANNEL 0x0C04 +#define SC_MULTITRANSPORT 0x0C08 /* RDP version */ #define RDP_VERSION_4 0x00080001 diff --git a/libfreerdp/core/gcc.c b/libfreerdp/core/gcc.c index d8d8ffae1..be8ed750f 100644 --- a/libfreerdp/core/gcc.c +++ b/libfreerdp/core/gcc.c @@ -485,9 +485,13 @@ BOOL gcc_read_server_data_blocks(wStream* s, rdpSettings* settings, int length) void gcc_write_server_data_blocks(wStream* s, rdpSettings* settings) { - gcc_write_server_core_data(s, settings); - gcc_write_server_network_data(s, settings); - gcc_write_server_security_data(s, settings); + gcc_write_server_core_data(s, settings); /* serverCoreData */ + gcc_write_server_network_data(s, settings); /* serverNetworkData */ + gcc_write_server_security_data(s, settings); /* serverSecurityData */ + + /* TODO: Send these GCC data blocks only when the client sent them */ + //gcc_write_server_message_channel_data(s, settings); /* serverMessageChannelData */ + //gcc_write_server_multitransport_channel_data(s, settings); /* serverMultitransportChannelData */ } BOOL gcc_read_user_data_header(wStream* s, UINT16* type, UINT16* length) @@ -961,6 +965,24 @@ BOOL gcc_read_server_security_data(wStream* s, rdpSettings* settings) return TRUE; } +void gcc_write_server_message_channel_data(wStream* s, rdpSettings* settings) +{ + UINT16 mcsChannelId = 0; + + gcc_write_user_data_header(s, SC_MCS_MSGCHANNEL, 6); + + Stream_Write_UINT16(s, mcsChannelId); /* mcsChannelId (2 bytes) */ +} + +void gcc_write_server_multitransport_channel_data(wStream* s, rdpSettings* settings) +{ + UINT32 flags = 0; + + gcc_write_user_data_header(s, SC_MULTITRANSPORT, 8); + + Stream_Write_UINT32(s, flags); /* flags (4 bytes) */ +} + static const BYTE initial_signature[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, diff --git a/libfreerdp/core/gcc.h b/libfreerdp/core/gcc.h index 38bffb218..9b106309c 100644 --- a/libfreerdp/core/gcc.h +++ b/libfreerdp/core/gcc.h @@ -59,7 +59,9 @@ BOOL gcc_read_client_monitor_extended_data(wStream* s, rdpSettings* settings, UI void gcc_write_client_monitor_extended_data(wStream* s, rdpSettings* settings); BOOL gcc_read_client_message_channel_data(wStream* s, rdpSettings* settings, UINT16 blockLength); void gcc_write_client_message_channel_data(wStream* s, rdpSettings* settings); +void gcc_write_server_message_channel_data(wStream* s, rdpSettings* settings); BOOL gcc_read_client_multitransport_channel_data(wStream* s, rdpSettings* settings, UINT16 blockLength); void gcc_write_client_multitransport_channel_data(wStream* s, rdpSettings* settings); +void gcc_write_server_multitransport_channel_data(wStream* s, rdpSettings* settings); #endif /* FREERDP_CORE_GCC_H */ diff --git a/libfreerdp/core/mcs.c b/libfreerdp/core/mcs.c index ba34f98b1..eae664bf4 100644 --- a/libfreerdp/core/mcs.c +++ b/libfreerdp/core/mcs.c @@ -584,11 +584,22 @@ BOOL mcs_send_connect_response(rdpMcs* mcs) BOOL mcs_recv_erect_domain_request(rdpMcs* mcs, wStream* s) { UINT16 length; + UINT32 subHeight; + UINT32 subInterval; enum DomainMCSPDU MCSPDU; MCSPDU = DomainMCSPDU_ErectDomainRequest; - return mcs_read_domain_mcspdu_header(s, &MCSPDU, &length); + if (!mcs_read_domain_mcspdu_header(s, &MCSPDU, &length)) + return FALSE; + + if (!per_read_integer(s, &subHeight)) /* subHeight (INTEGER) */ + return FALSE; + + if (!per_read_integer(s, &subInterval)) /* subInterval (INTEGER) */ + return FALSE; + + return TRUE; } /** From 07083acc9767785473c926c8b0c540d2c11e0eb9 Mon Sep 17 00:00:00 2001 From: Mike McDonald Date: Thu, 23 Jan 2014 18:01:31 -0500 Subject: [PATCH 117/149] First cut at network characteristics auto-detect and multitransport protocol --- include/freerdp/settings.h | 15 ++-- libfreerdp/core/CMakeLists.txt | 4 ++ libfreerdp/core/gcc.c | 123 ++++++++++++++++++++++++++++++++- libfreerdp/core/gcc.h | 8 +++ libfreerdp/core/mcs.h | 2 + libfreerdp/core/rdp.c | 91 ++++++++++++++++++++---- libfreerdp/core/rdp.h | 13 ++++ 7 files changed, 237 insertions(+), 19 deletions(-) diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 59481d9dd..6555fc952 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -51,13 +51,14 @@ #define CS_CLUSTER 0xC004 #define CS_MONITOR 0xC005 #define CS_MCS_MSGCHANNEL 0xC006 -#define CS_MULTITRANSPORT 0xC008 +#define CS_MULTITRANSPORT 0xC00A /* Server to Client (SC) data blocks */ #define SC_CORE 0x0C01 #define SC_SECURITY 0x0C02 #define SC_NET 0x0C03 -#define SC_MULTITRANSPORT 0x0C06 +#define SC_MCS_MSGCHANNEL 0x0C04 +#define SC_MULTITRANSPORT 0x0C08 /* RDP version */ #define RDP_VERSION_4 0x00080001 @@ -493,6 +494,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_SupportMonitorLayoutPdu 141 #define FreeRDP_SupportGraphicsPipeline 142 #define FreeRDP_SupportDynamicTimeZone 143 +#define FreeRDP_SupportHeartbeatPdu 144 #define FreeRDP_DisableEncryption 192 #define FreeRDP_EncryptionMethods 193 #define FreeRDP_ExtEncryptionMethods 194 @@ -516,6 +518,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_DesktopPosX 390 #define FreeRDP_DesktopPosY 391 #define FreeRDP_MultitransportFlags 512 +#define FreeRDP_SupportMultitransport 513 #define FreeRDP_AlternateShell 640 #define FreeRDP_ShellWorkingDirectory 641 #define FreeRDP_AutoLogonEnabled 704 @@ -786,7 +789,8 @@ struct rdp_settings ALIGN64 BOOL SupportMonitorLayoutPdu; /* 141 */ ALIGN64 BOOL SupportGraphicsPipeline; /* 142 */ ALIGN64 BOOL SupportDynamicTimeZone; /* 143 */ - UINT64 padding0192[192 - 143]; /* 143 */ + ALIGN64 BOOL SupportHeartbeatPdu; /* 144 */ + UINT64 padding0192[192 - 145]; /* 145 */ /* Client/Server Security Data */ ALIGN64 BOOL DisableEncryption; /* 192 */ @@ -831,7 +835,8 @@ struct rdp_settings /* Client Multitransport Channel Data */ ALIGN64 UINT32 MultitransportFlags; /* 512 */ - UINT64 padding0576[576 - 513]; /* 513 */ + ALIGN64 BOOL SupportMultitransport; /* 513 */ + UINT64 padding0576[576 - 514]; /* 514 */ UINT64 padding0640[640 - 576]; /* 576 */ /* @@ -1291,7 +1296,7 @@ struct rdp_settings /* Extensions */ ALIGN64 int num_extensions; /* */ ALIGN64 struct rdp_ext_set extensions[16]; /* */ - + ALIGN64 BYTE* SettingsModified; /* byte array marking fields that have been modified from their default value */ }; typedef struct rdp_settings rdpSettings; diff --git a/libfreerdp/core/CMakeLists.txt b/libfreerdp/core/CMakeLists.txt index 89a7d4f9e..48f399fa2 100644 --- a/libfreerdp/core/CMakeLists.txt +++ b/libfreerdp/core/CMakeLists.txt @@ -83,6 +83,10 @@ set(${MODULE_PREFIX}_SRCS connection.h redirection.c redirection.h + autodetect.c + autodetect.h + multitransport.c + multitransport.h timezone.c timezone.h rdp.c diff --git a/libfreerdp/core/gcc.c b/libfreerdp/core/gcc.c index ee039a993..a65d4e248 100644 --- a/libfreerdp/core/gcc.c +++ b/libfreerdp/core/gcc.c @@ -366,10 +366,12 @@ void gcc_write_client_data_blocks(wStream* s, rdpSettings* settings) if (settings->NegotiationFlags & EXTENDED_CLIENT_DATA_SUPPORTED) { - if (!settings->SpanMonitors) + if (settings->SpanMonitors) { gcc_write_client_monitor_data(s, settings); } + gcc_write_client_message_channel_data(s, settings); + gcc_write_client_multitransport_channel_data(s, settings); } else { @@ -433,6 +435,22 @@ BOOL gcc_read_server_data_blocks(wStream* s, rdpSettings* settings, int length) } break; + case SC_MCS_MSGCHANNEL: + if (!gcc_read_server_message_channel_data(s, settings)) + { + fprintf(stderr, "gcc_read_server_data_blocks: gcc_read_server_message_channel_data failed\n"); + return FALSE; + } + break; + + case SC_MULTITRANSPORT: + if (!gcc_read_server_multitransport_channel_data(s, settings)) + { + fprintf(stderr, "gcc_read_server_data_blocks: gcc_read_server_multitransport_channel_data failed\n"); + return FALSE; + } + break; + default: fprintf(stderr, "gcc_read_server_data_blocks: ignoring type=%hu\n", type); break; @@ -730,6 +748,9 @@ void gcc_write_client_core_data(wStream* s, rdpSettings* settings) if (settings->NetworkAutoDetect) earlyCapabilityFlags |= RNS_UD_CS_SUPPORT_NETWORK_AUTODETECT; + if (settings->SupportHeartbeatPdu) + earlyCapabilityFlags |= RNS_UD_CS_SUPPORT_HEARTBEAT_PDU; + if (settings->SupportGraphicsPipeline) earlyCapabilityFlags |= RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL; @@ -1270,3 +1291,103 @@ void gcc_write_client_monitor_data(wStream* s, rdpSettings* settings) } } } + +/** + * Read a client message channel data block (TS_UD_CS_MCS_MSGCHANNEL).\n + * @msdn{jj217627} + * @param s stream + * @param settings rdp settings + */ + +BOOL gcc_read_client_message_channel_data(wStream* s, rdpSettings* settings, UINT16 blockLength) +{ + fprintf(stderr, "CS_MCS_MSGCHANNEL\n"); + return TRUE; +} + +/** + * Write a client message channel data block (TS_UD_CS_MCS_MSGCHANNEL).\n + * @msdn{jj217627} + * @param s stream + * @param settings rdp settings + */ + +void gcc_write_client_message_channel_data(wStream* s, rdpSettings* settings) +{ + if (settings->NetworkAutoDetect || + settings->SupportHeartbeatPdu || + settings->SupportMultitransport) + { + gcc_write_user_data_header(s, CS_MCS_MSGCHANNEL, 8); + + Stream_Write_UINT32(s, 0); /* flags */ + } +} + +BOOL gcc_read_server_message_channel_data(wStream* s, rdpSettings* settings) +{ + freerdp* instance; + UINT16 MCSChannelId; + + if (Stream_GetRemainingLength(s) < 2) + return FALSE; + Stream_Read_UINT16(s, MCSChannelId); /* MCSChannelId */ + + /* Save the MCS message channel id */ + instance = (freerdp*)settings->instance; + instance->context->rdp->mcs->message_channel_id = MCSChannelId; + + return TRUE; +} + +void gcc_write_server_message_channel_data(wStream* s, rdpSettings* settings) +{ + fprintf(stderr, "SC_MCS_MSGCHANNEL\n"); +} + +/** + * Read a client multitransport channel data block (TS_UD_CS_MULTITRANSPORT).\n + * @msdn{jj217498} + * @param s stream + * @param settings rdp settings + */ + +BOOL gcc_read_client_multitransport_channel_data(wStream* s, rdpSettings* settings, UINT16 blockLength) +{ + fprintf(stderr, "CS_MULTITRANSPORT\n"); + return TRUE; +} + +/** + * Write a client multitransport channel data block (TS_UD_CS_MULTITRANSPORT).\n + * @msdn{jj217498} + * @param s stream + * @param settings rdp settings + */ + +void gcc_write_client_multitransport_channel_data(wStream* s, rdpSettings* settings) +{ + if (settings->MultitransportFlags != 0) + { + gcc_write_user_data_header(s, CS_MULTITRANSPORT, 8); + + Stream_Write_UINT32(s, settings->MultitransportFlags); /* flags */ + } +} + +BOOL gcc_read_server_multitransport_channel_data(wStream* s, rdpSettings* settings) +{ + UINT32 flags; + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + Stream_Read_UINT32(s, flags); /* flags */ + + return TRUE; +} + +void gcc_write_server_multitransport_channel_data(wStream* s, rdpSettings* settings) +{ + fprintf(stderr, "SC_MULTITRANSPORT\n"); +} + diff --git a/libfreerdp/core/gcc.h b/libfreerdp/core/gcc.h index 3432656b8..04859612e 100644 --- a/libfreerdp/core/gcc.h +++ b/libfreerdp/core/gcc.h @@ -55,5 +55,13 @@ BOOL gcc_read_client_cluster_data(wStream* s, rdpSettings *settings, UINT16 bloc void gcc_write_client_cluster_data(wStream* s, rdpSettings *settings); BOOL gcc_read_client_monitor_data(wStream* s, rdpSettings *settings, UINT16 blockLength); void gcc_write_client_monitor_data(wStream* s, rdpSettings *settings); +BOOL gcc_read_client_message_channel_data(wStream* s, rdpSettings *settings, UINT16 blockLength); +void gcc_write_client_message_channel_data(wStream* s, rdpSettings *settings); +BOOL gcc_read_server_message_channel_data(wStream* s, rdpSettings *settings); +void gcc_write_server_message_channel_data(wStream* s, rdpSettings *settings); +BOOL gcc_read_client_multitransport_channel_data(wStream* s, rdpSettings *settings, UINT16 blockLength); +void gcc_write_client_multitransport_channel_data(wStream* s, rdpSettings *settings); +BOOL gcc_read_server_multitransport_channel_data(wStream* s, rdpSettings *settings); +void gcc_write_server_multitransport_channel_data(wStream* s, rdpSettings *settings); #endif /* FREERDP_CORE_GCC_H */ diff --git a/libfreerdp/core/mcs.h b/libfreerdp/core/mcs.h index 1006231a7..126e74b36 100644 --- a/libfreerdp/core/mcs.h +++ b/libfreerdp/core/mcs.h @@ -115,6 +115,7 @@ typedef struct struct rdp_mcs { UINT16 user_id; + UINT16 message_channel_id; struct rdp_transport* transport; DomainParameters domainParameters; DomainParameters targetParameters; @@ -123,6 +124,7 @@ struct rdp_mcs BOOL user_channel_joined; BOOL global_channel_joined; + BOOL message_channel_joined; }; typedef struct rdp_mcs rdpMcs; diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 69e964e18..d421caca4 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -229,6 +229,15 @@ wStream* rdp_data_pdu_init(rdpRdp* rdp) return s; } +wStream* rdp_message_channel_pdu_init(rdpRdp* rdp) +{ + wStream* s; + s = transport_send_stream_init(rdp->transport, 2048); + Stream_Seek(s, RDP_PACKET_HEADER_MAX_LENGTH); + rdp_security_stream_init(rdp, s); + return s; +} + /** * Read an RDP packet header.\n * @param rdp rdp module @@ -328,13 +337,12 @@ void rdp_write_header(rdpRdp* rdp, wStream* s, UINT16 length, UINT16 channelId) Stream_Write_UINT16_BE(s, length); /* userData (OCTET_STRING) */ } -static UINT32 rdp_security_stream_out(rdpRdp* rdp, wStream* s, int length) +static UINT32 rdp_security_stream_out(rdpRdp* rdp, wStream* s, int length, UINT32 sec_flags) { BYTE* data; - UINT32 sec_flags; UINT32 pad = 0; - sec_flags = rdp->sec_flags; + sec_flags |= rdp->sec_flags; if (sec_flags != 0) { @@ -431,7 +439,7 @@ BOOL rdp_send(rdpRdp* rdp, wStream* s, UINT16 channel_id) Stream_Seek(s, sec_bytes); Stream_SetPosition(s, secm); - length += rdp_security_stream_out(rdp, s, length); + length += rdp_security_stream_out(rdp, s, length, 0); Stream_SetPosition(s, length); Stream_SealLength(s); @@ -460,7 +468,7 @@ BOOL rdp_send_pdu(rdpRdp* rdp, wStream* s, UINT16 type, UINT16 channel_id) rdp_write_share_control_header(s, length - sec_bytes, type, channel_id); Stream_SetPosition(s, sec_hold); - length += rdp_security_stream_out(rdp, s, length); + length += rdp_security_stream_out(rdp, s, length, 0); Stream_SetPosition(s, length); Stream_SealLength(s); @@ -490,7 +498,34 @@ BOOL rdp_send_data_pdu(rdpRdp* rdp, wStream* s, BYTE type, UINT16 channel_id) rdp_write_share_data_header(s, length - sec_bytes, type, rdp->settings->ShareId); Stream_SetPosition(s, sec_hold); - length += rdp_security_stream_out(rdp, s, length); + length += rdp_security_stream_out(rdp, s, length, 0); + + Stream_SetPosition(s, length); + Stream_SealLength(s); + + if (transport_write(rdp->transport, s) < 0) + return FALSE; + + return TRUE; +} + +BOOL rdp_send_message_channel_pdu(rdpRdp* rdp, wStream* s, UINT16 sec_flags) +{ + UINT16 length; + UINT32 sec_bytes; + int sec_hold; + + length = Stream_GetPosition(s); + Stream_SetPosition(s, 0); + + rdp_write_header(rdp, s, length, rdp->mcs->message_channel_id); + + sec_bytes = rdp_get_sec_bytes(rdp); + sec_hold = Stream_GetPosition(s); + Stream_Seek(s, sec_bytes); + + Stream_SetPosition(s, sec_hold); + length += rdp_security_stream_out(rdp, s, length, sec_flags); Stream_SetPosition(s, length); Stream_SealLength(s); @@ -569,7 +604,7 @@ int rdp_recv_data_pdu(rdpRdp* rdp, wStream* s) #ifdef WITH_DEBUG_RDP printf("recv %s Data PDU (0x%02X), length: %d\n", - type < ARRAYSIZE(DATA_PDU_TYPE_STRINGS) ? DATA_PDU_TYPE_STRINGS[type] : "???", type, length); + type < ARRAYSIZE(DATA_PDU_TYPE_STRINGS) ? DATA_PDU_TYPE_STRINGS[type] : "???", type, length); #endif switch (type) @@ -672,6 +707,28 @@ int rdp_recv_data_pdu(rdpRdp* rdp, wStream* s) return 0; } +int rdp_recv_message_channel_pdu(rdpRdp* rdp, wStream* s) +{ + UINT16 securityFlags; + + if (!rdp_read_security_header(s, &securityFlags)) + return -1; + + if (securityFlags & SEC_AUTODETECT_REQ) + { + /* Server Auto-Detect Request PDU */ + return rdp_recv_autodetect_packet(rdp, s); + } + + if (securityFlags & SEC_TRANSPORT_REQ) + { + /* Initiate Multitransport Request PDU */ + return rdp_recv_multitransport_packet(rdp, s); + } + + return -1; +} + int rdp_recv_out_of_sequence_pdu(rdpRdp* rdp, wStream* s) { UINT16 type; @@ -820,12 +877,7 @@ static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s) } } - if (channelId != MCS_GLOBAL_CHANNEL_ID) - { - if (!freerdp_channel_process(rdp->instance, s, channelId)) - return -1; - } - else + if (channelId == MCS_GLOBAL_CHANNEL_ID) { while (Stream_GetRemainingLength(s) > 3) { @@ -865,6 +917,15 @@ static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s) Stream_SetPosition(s, nextPosition); } } + else if (channelId == rdp->mcs->message_channel_id) + { + return rdp_recv_message_channel_pdu(rdp, s); + } + else + { + if (!freerdp_channel_process(rdp->instance, s, channelId)) + return -1; + } return 0; } @@ -1020,6 +1081,8 @@ rdpRdp* rdp_new(rdpContext* context) rdp->nego = nego_new(rdp->transport); rdp->mcs = mcs_new(rdp->transport); rdp->redirection = redirection_new(); + rdp->autodetect = autodetect_new(); + rdp->multitransport = multitransport_new(); rdp->mppc_dec = mppc_dec_new(); rdp->mppc_enc = mppc_enc_new(PROTO_RDP_50); } @@ -1092,6 +1155,8 @@ void rdp_free(rdpRdp* rdp) nego_free(rdp->nego); mcs_free(rdp->mcs); redirection_free(rdp->redirection); + autodetect_free(rdp->autodetect); + multitransport_free(rdp->multitransport); mppc_dec_free(rdp->mppc_dec); mppc_enc_free(rdp->mppc_enc); free(rdp); diff --git a/libfreerdp/core/rdp.h b/libfreerdp/core/rdp.h index 9fece7007..aea34fab6 100644 --- a/libfreerdp/core/rdp.h +++ b/libfreerdp/core/rdp.h @@ -34,6 +34,8 @@ #include "license.h" #include "errinfo.h" #include "extension.h" +#include "autodetect.h" +#include "multitransport.h" #include "security.h" #include "transport.h" #include "connection.h" @@ -51,6 +53,8 @@ /* Security Header Flags */ #define SEC_EXCHANGE_PKT 0x0001 +#define SEC_TRANSPORT_REQ 0x0002 +#define SEC_TRANSPORT_RSP 0x0004 #define SEC_ENCRYPT 0x0008 #define SEC_RESET_SEQNO 0x0010 #define SEC_IGNORE_SEQNO 0x0020 @@ -60,6 +64,9 @@ #define SEC_LICENSE_ENCRYPT_SC 0x0200 #define SEC_REDIRECTION_PKT 0x0400 #define SEC_SECURE_CHECKSUM 0x0800 +#define SEC_AUTODETECT_REQ 0x1000 +#define SEC_AUTODETECT_RSP 0x2000 +#define SEC_HEARTBEAT 0x4000 #define SEC_FLAGSHI_VALID 0x8000 #define SEC_PKT_CS_MASK (SEC_EXCHANGE_PKT | SEC_INFO_PKT) @@ -131,6 +138,8 @@ struct rdp_rdp rdpSettings* settings; rdpTransport* transport; rdpExtension* extension; + rdpAutoDetect* autodetect; + rdpMultitransport* multitransport; struct rdp_mppc_dec* mppc_dec; struct rdp_mppc_enc* mppc_enc; struct crypto_rc4_struct* rc4_decrypt_key; @@ -191,6 +200,10 @@ BOOL rdp_send(rdpRdp* rdp, wStream* s, UINT16 channel_id); int rdp_send_channel_data(rdpRdp* rdp, int channel_id, BYTE* data, int size); +wStream* rdp_message_channel_pdu_init(rdpRdp* rdp); +BOOL rdp_send_message_channel_pdu(rdpRdp* rdp, wStream* s, UINT16 sec_flags); +int rdp_recv_message_channel_pdu(rdpRdp* rdp, wStream* s); + int rdp_recv_out_of_sequence_pdu(rdpRdp* rdp, wStream* s); void rdp_set_blocking_mode(rdpRdp* rdp, BOOL blocking); From ac8fe6ad61a6cfe9d1c0122cc9c1fa83c4877edc Mon Sep 17 00:00:00 2001 From: Mike McDonald Date: Thu, 23 Jan 2014 22:23:47 -0500 Subject: [PATCH 118/149] Added heartbeat code --- libfreerdp/core/CMakeLists.txt | 2 + libfreerdp/core/autodetect.c | 283 +++++++++++++++++++++++++++++++ libfreerdp/core/autodetect.h | 58 +++++++ libfreerdp/core/heartbeat.c | 62 +++++++ libfreerdp/core/heartbeat.h | 47 +++++ libfreerdp/core/multitransport.c | 58 +++++++ libfreerdp/core/multitransport.h | 41 +++++ libfreerdp/core/rdp.c | 6 + libfreerdp/core/rdp.h | 1 + 9 files changed, 558 insertions(+) create mode 100644 libfreerdp/core/autodetect.c create mode 100644 libfreerdp/core/autodetect.h create mode 100644 libfreerdp/core/heartbeat.c create mode 100644 libfreerdp/core/heartbeat.h create mode 100644 libfreerdp/core/multitransport.c create mode 100644 libfreerdp/core/multitransport.h diff --git a/libfreerdp/core/CMakeLists.txt b/libfreerdp/core/CMakeLists.txt index 48f399fa2..2bb80cf13 100644 --- a/libfreerdp/core/CMakeLists.txt +++ b/libfreerdp/core/CMakeLists.txt @@ -85,6 +85,8 @@ set(${MODULE_PREFIX}_SRCS redirection.h autodetect.c autodetect.h + heartbeat.c + heartbeat.h multitransport.c multitransport.h timezone.c diff --git a/libfreerdp/core/autodetect.c b/libfreerdp/core/autodetect.c new file mode 100644 index 000000000..f0cc6bccc --- /dev/null +++ b/libfreerdp/core/autodetect.c @@ -0,0 +1,283 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Auto-Detect PDUs + * + * Copyright 2014 Dell Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define WITH_DEBUG_AUTODETECT + +#include "autodetect.h" + +typedef struct +{ + UINT8 headerLength; + UINT8 headerTypeId; + UINT16 sequenceNumber; + UINT16 requestType; +} AUTODETECT_REQ_PDU; + +static BOOL autodetect_send_rtt_measure_response(rdpRdp* rdp, UINT16 sequenceNumber) +{ + wStream* s; + + /* Send the response PDU to the server */ + s = rdp_message_channel_pdu_init(rdp); + if (s == NULL) return FALSE; + + DEBUG_AUTODETECT("sending RTT Measure Response PDU"); + + Stream_Write_UINT8(s, 0x06); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_RESPONSE); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, 0x0000); /* responseType (1 byte) */ + + return rdp_send_message_channel_pdu(rdp, s, SEC_AUTODETECT_RSP); +} + +static BOOL autodetect_send_bandwidth_measure_results(rdpRdp* rdp, UINT16 responseType, UINT16 sequenceNumber) +{ + UINT32 timeDelta; + wStream* s; + + /* Compute the total time */ + timeDelta = GetTickCount() - rdp->autodetect->bandwidthMeasureStartTime; + + /* Send the result PDU to the server */ + s = rdp_message_channel_pdu_init(rdp); + if (s == NULL) return FALSE; + + DEBUG_AUTODETECT("sending Bandwidth Measure Results PDU -> timeDelta=%u, byteCount=%u", timeDelta, rdp->autodetect->bandwidthMeasureByteCount); + + Stream_Write_UINT8(s, 0x0E); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_RESPONSE); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, responseType); /* responseType (1 byte) */ + Stream_Write_UINT32(s, timeDelta); /* timeDelta (4 bytes) */ + Stream_Write_UINT32(s, rdp->autodetect->bandwidthMeasureByteCount); /* byteCount (4 bytes) */ + + return rdp_send_message_channel_pdu(rdp, s, SEC_AUTODETECT_RSP); +} + +static BOOL autodetect_send_netchar_sync(rdpRdp* rdp, UINT16 sequenceNumber) +{ + wStream* s; + + /* Send the response PDU to the server */ + s = rdp_message_channel_pdu_init(rdp); + if (s == NULL) return FALSE; + + DEBUG_AUTODETECT("sending Network Characteristics Sync PDU -> bandwidth=%u, rtt=%u", rdp->autodetect->netCharBandwidth, rdp->autodetect->netCharAverageRTT); + + Stream_Write_UINT8(s, 0x0E); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_RESPONSE); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, 0x0018); /* responseType (1 byte) */ + Stream_Write_UINT32(s, rdp->autodetect->netCharBandwidth); /* bandwidth (4 bytes) */ + Stream_Write_UINT32(s, rdp->autodetect->netCharAverageRTT); /* rtt (4 bytes) */ + + return rdp_send_message_channel_pdu(rdp, s, SEC_AUTODETECT_RSP); +} + +static BOOL autodetect_recv_rtt_measure_request(rdpRdp* rdp, wStream* s, AUTODETECT_REQ_PDU* autodetectReqPdu) +{ + if (autodetectReqPdu->headerLength != 0x06) + return FALSE; + + DEBUG_AUTODETECT("received RTT Measure Request PDU"); + + /* Send a response to the server */ + return autodetect_send_rtt_measure_response(rdp, autodetectReqPdu->sequenceNumber); +} + +static BOOL autodetect_recv_bandwidth_measure_start(rdpRdp* rdp, wStream* s, AUTODETECT_REQ_PDU* autodetectReqPdu) +{ + if (autodetectReqPdu->headerLength != 0x06) + return FALSE; + + DEBUG_AUTODETECT("received Bandwidth Measure Start PDU"); + + /* Initialize bandwidth measurement parameters */ + rdp->autodetect->bandwidthMeasureStartTime = GetTickCount(); + rdp->autodetect->bandwidthMeasureByteCount = 0; + + return TRUE; +} + +static BOOL autodetect_recv_bandwidth_measure_payload(rdpRdp* rdp, wStream* s, AUTODETECT_REQ_PDU* autodetectReqPdu) +{ + UINT16 payloadLength; + + if (autodetectReqPdu->headerLength != 0x08) + return FALSE; + + if (Stream_GetRemainingLength(s) < 2) + return FALSE; + + Stream_Read_UINT16(s, payloadLength); /* payloadLength (2 bytes) */ + + DEBUG_AUTODETECT("received Bandwidth Measure Payload PDU -> payloadLength=%u", payloadLength); + + /* Add the payload length to the bandwidth measurement parameters */ + rdp->autodetect->bandwidthMeasureByteCount += payloadLength; + + return TRUE; +} + +static BOOL autodetect_recv_bandwidth_measure_stop(rdpRdp* rdp, wStream* s, AUTODETECT_REQ_PDU* autodetectReqPdu) +{ + UINT16 payloadLength; + UINT16 responseType; + + if (autodetectReqPdu->requestType == 0x002B) + { + if (autodetectReqPdu->headerLength != 0x08) + return FALSE; + + if (Stream_GetRemainingLength(s) < 2) + return FALSE; + + Stream_Read_UINT16(s, payloadLength); /* payloadLength (2 bytes) */ + } + else + { + if (autodetectReqPdu->headerLength != 0x06) + return FALSE; + + payloadLength = 0; + } + + DEBUG_AUTODETECT("received Bandwidth Measure Stop PDU -> payloadLength=%u", payloadLength); + + /* Add the payload length to the bandwidth measurement parameters */ + rdp->autodetect->bandwidthMeasureByteCount += payloadLength; + + /* Send a response the server */ + responseType = autodetectReqPdu->requestType == 0x002B ? 0x0003 : 0x000B; + + return autodetect_send_bandwidth_measure_results(rdp, responseType, autodetectReqPdu->sequenceNumber); +} + +static BOOL autodetect_recv_netchar_result(rdpRdp* rdp, wStream* s, AUTODETECT_REQ_PDU* autodetectReqPdu) +{ + switch (autodetectReqPdu->requestType) + { + case 0x0840: + /* baseRTT and averageRTT fields are present (bandwidth field is not) */ + if ((autodetectReqPdu->headerLength != 0x0E) || (Stream_GetRemainingLength(s) < 8)) + return FALSE; + Stream_Read_UINT32(s, rdp->autodetect->netCharBaseRTT); /* baseRTT (4 bytes) */ + Stream_Read_UINT32(s, rdp->autodetect->netCharAverageRTT); /* averageRTT (4 bytes) */ + break; + + case 0x0880: + /* bandwidth and averageRTT fields are present (baseRTT field is not) */ + if ((autodetectReqPdu->headerLength != 0x0E) || (Stream_GetRemainingLength(s) < 8)) + return FALSE; + Stream_Read_UINT32(s, rdp->autodetect->netCharBandwidth); /* bandwidth (4 bytes) */ + Stream_Read_UINT32(s, rdp->autodetect->netCharAverageRTT); /* averageRTT (4 bytes) */ + break; + + case 0x08C0: + /* baseRTT, bandwidth, and averageRTT fields are present */ + if ((autodetectReqPdu->headerLength != 0x12) || (Stream_GetRemainingLength(s) < 12)) + return FALSE; + Stream_Read_UINT32(s, rdp->autodetect->netCharBaseRTT); /* baseRTT (4 bytes) */ + Stream_Read_UINT32(s, rdp->autodetect->netCharBandwidth); /* bandwidth (4 bytes) */ + Stream_Read_UINT32(s, rdp->autodetect->netCharAverageRTT); /* averageRTT (4 bytes) */ + break; + } + + DEBUG_AUTODETECT("received Network Characteristics Result PDU -> baseRTT=%u, bandwidth=%u, averageRTT=%u", rdp->autodetect->netCharBaseRTT, rdp->autodetect->netCharBandwidth, rdp->autodetect->netCharAverageRTT); + + return TRUE; +} + +int rdp_recv_autodetect_packet(rdpRdp* rdp, wStream* s) +{ + AUTODETECT_REQ_PDU autodetectReqPdu; + BOOL success = FALSE; + + if (Stream_GetRemainingLength(s) < 6) + return -1; + + Stream_Read_UINT8(s, autodetectReqPdu.headerLength); /* headerLength (1 byte) */ + Stream_Read_UINT8(s, autodetectReqPdu.headerTypeId); /* headerTypeId (1 byte) */ + Stream_Read_UINT16(s, autodetectReqPdu.sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Read_UINT16(s, autodetectReqPdu.requestType); /* requestType (2 bytes) */ + + if (autodetectReqPdu.headerTypeId != TYPE_ID_AUTODETECT_REQUEST) + return -1; + + switch (autodetectReqPdu.requestType) + { + case 0x0001: + case 0x1001: + /* RTT Measure Request (RDP_RTT_REQUEST) - MS-RDPBCGR 2.2.14.1.1 */ + success = autodetect_recv_rtt_measure_request(rdp, s, &autodetectReqPdu); + break; + + case 0x0014: + case 0x0114: + case 0x1014: + /* Bandwidth Measure Start (RDP_BW_START) - MS-RDPBCGR 2.2.14.1.2 */ + success = autodetect_recv_bandwidth_measure_start(rdp, s, &autodetectReqPdu); + break; + + case 0x0002: + /* Bandwidth Measure Payload (RDP_BW_PAYLOAD) - MS-RDPBCGR 2.2.14.1.3 */ + success = autodetect_recv_bandwidth_measure_payload(rdp, s, &autodetectReqPdu); + break; + + case 0x002B: + case 0x0429: + case 0x0629: + /* Bandwidth Measure Stop (RDP_BW_STOP) - MS-RDPBCGR 2.2.14.1.4 */ + success = autodetect_recv_bandwidth_measure_stop(rdp, s, &autodetectReqPdu); + break; + + case 0x0840: + case 0x0880: + case 0x08C0: + /* Network Characteristics Result (RDP_NETCHAR_RESULT) - MS-RDPBCGR 2.2.14.1.5 */ + success = autodetect_recv_netchar_result(rdp, s, &autodetectReqPdu); + break; + + default: + break; + } + + return success ? 0 : -1; +} + +rdpAutoDetect* autodetect_new(void) +{ + rdpAutoDetect* autodetect = (rdpAutoDetect*)malloc(sizeof(rdpAutoDetect)); + if (autodetect) + { + memset(autodetect, 0, sizeof(rdpAutoDetect)); + } + + return autodetect; +} + +void autodetect_free(rdpAutoDetect* autodetect) +{ + free(autodetect); +} diff --git a/libfreerdp/core/autodetect.h b/libfreerdp/core/autodetect.h new file mode 100644 index 000000000..b5823c122 --- /dev/null +++ b/libfreerdp/core/autodetect.h @@ -0,0 +1,58 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Auto-Detect PDUs + * + * Copyright 2014 Dell Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __AUTODETECT_H +#define __AUTODETECT_H + +typedef struct rdp_autodetect rdpAutoDetect; + +#include "rdp.h" + +#include + +#include +#include + +#define TYPE_ID_AUTODETECT_REQUEST 0x00 +#define TYPE_ID_AUTODETECT_RESPONSE 0x01 + +struct rdp_autodetect +{ + /* Bandwidth measurement */ + UINT32 bandwidthMeasureStartTime; + UINT32 bandwidthMeasureByteCount; + + /* Network characteristics (as reported by server) */ + UINT32 netCharBandwidth; + UINT32 netCharBaseRTT; + UINT32 netCharAverageRTT; +}; + +int rdp_recv_autodetect_packet(rdpRdp* rdp, wStream* s); + +rdpAutoDetect* autodetect_new(void); +void autodetect_free(rdpAutoDetect* autodetect); + +#ifdef WITH_DEBUG_AUTODETECT +#define DEBUG_AUTODETECT(fmt, ...) DEBUG_CLASS(AUTODETECT, fmt, ## __VA_ARGS__) +#else +#define DEBUG_AUTODETECT(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) +#endif + +#endif /* __AUTODETECT_H */ diff --git a/libfreerdp/core/heartbeat.c b/libfreerdp/core/heartbeat.c new file mode 100644 index 000000000..3989ae390 --- /dev/null +++ b/libfreerdp/core/heartbeat.c @@ -0,0 +1,62 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Heartbeat PDUs + * + * Copyright 2014 Dell Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define WITH_DEBUG_HEARTBEAT + +#include "heartbeat.h" + +int rdp_recv_heartbeat_packet(rdpRdp* rdp, wStream* s) +{ + BYTE reserved; + BYTE period; + BYTE count1; + BYTE count2; + + if (Stream_GetRemainingLength(s) < 4) + return -1; + + Stream_Read_UINT8(s, reserved); /* reserved (1 byte) */ + Stream_Read_UINT8(s, period); /* period (1 byte) */ + Stream_Read_UINT8(s, count1); /* count1 (1 byte) */ + Stream_Read_UINT8(s, count2); /* count2 (1 byte) */ + + DEBUG_HEARTBEAT("received Heartbeat PDU -> period=%u, count1=%u, count2=%u", period, count1, count2); + + return 0; +} + +rdpHeartbeat* heartbeat_new(void) +{ + rdpHeartbeat* heartbeat = (rdpHeartbeat*)malloc(sizeof(rdpHeartbeat)); + if (heartbeat) + { + ZeroMemory(heartbeat, sizeof(rdpHeartbeat)); + } + + return heartbeat; +} + +void heartbeat_free(rdpHeartbeat* heartbeat) +{ + free(heartbeat); +} diff --git a/libfreerdp/core/heartbeat.h b/libfreerdp/core/heartbeat.h new file mode 100644 index 000000000..80e3f46a3 --- /dev/null +++ b/libfreerdp/core/heartbeat.h @@ -0,0 +1,47 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Heartbeat PDUs + * + * Copyright 2014 Dell Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HEARTBEAT_H +#define __HEARTBEAT_H + +typedef struct rdp_heartbeat rdpHeartbeat; + +#include "rdp.h" + +#include + +#include + +struct rdp_heartbeat +{ + UINT32 placeholder; +}; + +int rdp_recv_heartbeat_packet(rdpRdp* rdp, wStream* s); + +rdpHeartbeat* heartbeat_new(void); +void heartbeat_free(rdpHeartbeat* heartbeat); + +#ifdef WITH_DEBUG_HEARTBEAT +#define DEBUG_HEARTBEAT(fmt, ...) DEBUG_CLASS(HEARTBEAT, fmt, ## __VA_ARGS__) +#else +#define DEBUG_HEARTBEAT(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) +#endif + +#endif /* __HEARTBEAT_H */ diff --git a/libfreerdp/core/multitransport.c b/libfreerdp/core/multitransport.c new file mode 100644 index 000000000..36d86f741 --- /dev/null +++ b/libfreerdp/core/multitransport.c @@ -0,0 +1,58 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * MULTITRANSPORT PDUs + * + * Copyright 2014 Dell Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "multitransport.h" + +int rdp_recv_multitransport_packet(rdpRdp* rdp, wStream* s) +{ + UINT32 requestId; + UINT16 requestedProtocol; + UINT16 reserved; + BYTE securityCookie[16]; + + if (Stream_GetRemainingLength(s) < 24) + return -1; + + Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */ + Stream_Read_UINT16(s, requestedProtocol); /* requestedProtocol (2 bytes) */ + Stream_Read_UINT16(s, reserved); /* reserved (2 bytes) */ + Stream_Read(s, securityCookie, 16); /* securityCookie (16 bytes) */ + + return 0; +} + +rdpMultitransport* multitransport_new(void) +{ + rdpMultitransport* multitransport = (rdpMultitransport*)malloc(sizeof(rdpMultitransport)); + if (multitransport) + { + memset(multitransport, 0, sizeof(rdpMultitransport)); + } + + return multitransport; +} + +void multitransport_free(rdpMultitransport* multitransport) +{ + free(multitransport); +} diff --git a/libfreerdp/core/multitransport.h b/libfreerdp/core/multitransport.h new file mode 100644 index 000000000..1f07bdbc6 --- /dev/null +++ b/libfreerdp/core/multitransport.h @@ -0,0 +1,41 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Multitransport PDUs + * + * Copyright 2014 Dell Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MULTITRANSPORT_H +#define __MULTITRANSPORT_H + +typedef struct rdp_multitransport rdpMultitransport; + +#include "rdp.h" + +#include + +#include + +struct rdp_multitransport +{ + UINT32 placeholder; +}; + +int rdp_recv_multitransport_packet(rdpRdp* rdp, wStream* s); + +rdpMultitransport* multitransport_new(void); +void multitransport_free(rdpMultitransport* multitransport); + +#endif /* __MULTITRANSPORT_H */ diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index d421caca4..8024a1889 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -720,6 +720,12 @@ int rdp_recv_message_channel_pdu(rdpRdp* rdp, wStream* s) return rdp_recv_autodetect_packet(rdp, s); } + if (securityFlags & SEC_HEARTBEAT) + { + /* Heartbeat PDU */ + return rdp_recv_heartbeat_packet(rdp, s); + } + if (securityFlags & SEC_TRANSPORT_REQ) { /* Initiate Multitransport Request PDU */ diff --git a/libfreerdp/core/rdp.h b/libfreerdp/core/rdp.h index aea34fab6..de9ae1159 100644 --- a/libfreerdp/core/rdp.h +++ b/libfreerdp/core/rdp.h @@ -35,6 +35,7 @@ #include "errinfo.h" #include "extension.h" #include "autodetect.h" +#include "heartbeat.h" #include "multitransport.h" #include "security.h" #include "transport.h" From 63f6947872b6dc3f42f38909735559b5e7c7f249 Mon Sep 17 00:00:00 2001 From: Mike McDonald Date: Fri, 24 Jan 2014 08:38:28 -0500 Subject: [PATCH 119/149] Added heartbeat to rdpRdp struct and added calls to heartbeat constructor and destructor --- libfreerdp/core/rdp.c | 2 ++ libfreerdp/core/rdp.h | 1 + 2 files changed, 3 insertions(+) diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 8024a1889..e7c4e3d61 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -1088,6 +1088,7 @@ rdpRdp* rdp_new(rdpContext* context) rdp->mcs = mcs_new(rdp->transport); rdp->redirection = redirection_new(); rdp->autodetect = autodetect_new(); + rdp->heartbeat = heartbeat_new(); rdp->multitransport = multitransport_new(); rdp->mppc_dec = mppc_dec_new(); rdp->mppc_enc = mppc_enc_new(PROTO_RDP_50); @@ -1162,6 +1163,7 @@ void rdp_free(rdpRdp* rdp) mcs_free(rdp->mcs); redirection_free(rdp->redirection); autodetect_free(rdp->autodetect); + heartbeat_free(rdp->heartbeat); multitransport_free(rdp->multitransport); mppc_dec_free(rdp->mppc_dec); mppc_enc_free(rdp->mppc_enc); diff --git a/libfreerdp/core/rdp.h b/libfreerdp/core/rdp.h index de9ae1159..fbd73e45c 100644 --- a/libfreerdp/core/rdp.h +++ b/libfreerdp/core/rdp.h @@ -140,6 +140,7 @@ struct rdp_rdp rdpTransport* transport; rdpExtension* extension; rdpAutoDetect* autodetect; + rdpHeartbeat* heartbeat; rdpMultitransport* multitransport; struct rdp_mppc_dec* mppc_dec; struct rdp_mppc_enc* mppc_enc; From 21a259927a02bfa5020099748b7ce3627271dcf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 24 Jan 2014 13:02:45 -0500 Subject: [PATCH 120/149] libwinpr-sspi: fix encoding of server-side NTLM challenge message --- winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c index 810bc2223..a9dbf9885 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c @@ -188,7 +188,7 @@ void ntlm_get_target_computer_name(PUNICODE_STRING pName, COMPUTER_NAME_FORMAT t length = ConvertToUnicode(CP_UTF8, 0, name, -1, &pName->Buffer, 0); - pName->Length = (length - 1) / 2; + pName->Length = (length - 1) * 2; pName->MaximumLength = pName->Length; free(name); From 95634f3e4c8fb986dc1a2ae6fee6181e18b495a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 24 Jan 2014 13:03:37 -0500 Subject: [PATCH 121/149] libfreerdp-core: improve encoding and decoding of GCC core data block --- libfreerdp/core/gcc.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/libfreerdp/core/gcc.c b/libfreerdp/core/gcc.c index be8ed750f..f2b6e3d8c 100644 --- a/libfreerdp/core/gcc.c +++ b/libfreerdp/core/gcc.c @@ -834,26 +834,38 @@ BOOL gcc_read_server_core_data(wStream* s, rdpSettings* settings) { UINT32 version; UINT32 clientRequestedProtocols; + UINT32 earlyCapabilityFlags; - if (Stream_GetRemainingLength(s) < 8) + if (Stream_GetRemainingLength(s) < 4) return FALSE; + Stream_Read_UINT32(s, version); /* version */ - Stream_Read_UINT32(s, clientRequestedProtocols); /* clientRequestedProtocols */ if (version == RDP_VERSION_4 && settings->RdpVersion > 4) settings->RdpVersion = 4; else if (version == RDP_VERSION_5_PLUS && settings->RdpVersion < 5) settings->RdpVersion = 7; + if (Stream_GetRemainingLength(s) >= 4) + { + Stream_Read_UINT32(s, clientRequestedProtocols); /* clientRequestedProtocols */ + } + + if (Stream_GetRemainingLength(s) >= 4) + { + Stream_Read_UINT32(s, earlyCapabilityFlags); /* earlyCapabilityFlags */ + } + return TRUE; } void gcc_write_server_core_data(wStream* s, rdpSettings* settings) { - gcc_write_user_data_header(s, SC_CORE, 12); + gcc_write_user_data_header(s, SC_CORE, 16); Stream_Write_UINT32(s, settings->RdpVersion == 4 ? RDP_VERSION_4 : RDP_VERSION_5_PLUS); Stream_Write_UINT32(s, settings->RequestedProtocols); /* clientRequestedProtocols */ + Stream_Write_UINT32(s, settings->EarlyCapabilityFlags); /* earlyCapabilityFlags */ } /** From fab0cd27796359a639d28fd972775f754b7991b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 24 Jan 2014 17:48:55 -0500 Subject: [PATCH 122/149] libwinpr-synch: stub timer queues --- client/common/test/TestClientRdpFile.c | 4 +- winpr/include/winpr/synch.h | 26 +++++ winpr/libwinpr/handle/handle.h | 2 + winpr/libwinpr/synch/synch.h | 12 +++ winpr/libwinpr/synch/test/CMakeLists.txt | 1 + .../libwinpr/synch/test/TestSynchTimerQueue.c | 58 +++++++++++ winpr/libwinpr/synch/timer.c | 98 +++++++++++++++++++ 7 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 winpr/libwinpr/synch/test/TestSynchTimerQueue.c diff --git a/client/common/test/TestClientRdpFile.c b/client/common/test/TestClientRdpFile.c index 39b188624..24d138deb 100644 --- a/client/common/test/TestClientRdpFile.c +++ b/client/common/test/TestClientRdpFile.c @@ -334,9 +334,9 @@ int TestClientRdpFile(int argc, char* argv[]) freerdp_client_rdp_file_set_integer_option(file, "vendor integer", 456); iValue = freerdp_client_rdp_file_get_integer_option(file, "vendor integer"); - sValue = freerdp_client_rdp_file_get_string_option(file, "vendor string"); + sValue = (char*) freerdp_client_rdp_file_get_string_option(file, "vendor string"); freerdp_client_rdp_file_set_string_option(file, "vendor string", "apple"); - sValue = freerdp_client_rdp_file_get_string_option(file, "vendor string"); + sValue = (char*) freerdp_client_rdp_file_get_string_option(file, "vendor string"); freerdp_client_rdp_file_set_string_option(file, "fruits", "banana,oranges"); freerdp_client_rdp_file_set_integer_option(file, "numbers", 123456789); diff --git a/winpr/include/winpr/synch.h b/winpr/include/winpr/synch.h index 28d15fa23..10647ff30 100644 --- a/winpr/include/winpr/synch.h +++ b/winpr/include/winpr/synch.h @@ -269,6 +269,32 @@ WINPR_API BOOL CancelWaitableTimer(HANDLE hTimer); #define OpenWaitableTimer OpenWaitableTimerA #endif +/** + * Timer-Queue Timer + */ + +#define WT_EXECUTEDEFAULT 0x00000000 +#define WT_EXECUTEINIOTHREAD 0x00000001 +#define WT_EXECUTEINUITHREAD 0x00000002 +#define WT_EXECUTEINWAITTHREAD 0x00000004 +#define WT_EXECUTEONLYONCE 0x00000008 +#define WT_EXECUTELONGFUNCTION 0x00000010 +#define WT_EXECUTEINTIMERTHREAD 0x00000020 +#define WT_EXECUTEINPERSISTENTIOTHREAD 0x00000040 +#define WT_EXECUTEINPERSISTENTTHREAD 0x00000080 +#define WT_TRANSFER_IMPERSONATION 0x00000100 + +typedef VOID (*WAITORTIMERCALLBACK)(PVOID lpParameter, BOOLEAN TimerOrWaitFired); + +WINPR_API HANDLE CreateTimerQueue(void); +WINPR_API BOOL DeleteTimerQueue(HANDLE TimerQueue); +WINPR_API BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent); + +WINPR_API BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, + WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags); +WINPR_API BOOL ChangeTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG Period); +WINPR_API BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent); + #endif #if ((_WIN32) && (_WIN32_WINNT < 0x0600)) diff --git a/winpr/libwinpr/handle/handle.h b/winpr/libwinpr/handle/handle.h index 201493a51..f9075e114 100644 --- a/winpr/libwinpr/handle/handle.h +++ b/winpr/libwinpr/handle/handle.h @@ -33,6 +33,8 @@ #define HANDLE_TYPE_ANONYMOUS_PIPE 8 #define HANDLE_TYPE_ACCESS_TOKEN 9 #define HANDLE_TYPE_FILE 10 +#define HANDLE_TYPE_TIMER_QUEUE 11 +#define HANDLE_TYPE_TIMER_QUEUE_TIMER 12 #define WINPR_HANDLE_DEF() \ ULONG Type diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index f3593c536..6dbe50b69 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -97,6 +97,18 @@ struct winpr_timer }; typedef struct winpr_timer WINPR_TIMER; +struct winpr_timer_queue +{ + WINPR_HANDLE_DEF(); +}; +typedef struct winpr_timer_queue WINPR_TIMER_QUEUE; + +struct winpr_timer_queue_timer +{ + WINPR_HANDLE_DEF(); +}; +typedef struct winpr_timer_queue_timer WINPR_TIMER_QUEUE_TIMER; + #endif #endif /* WINPR_SYNCH_PRIVATE_H */ diff --git a/winpr/libwinpr/synch/test/CMakeLists.txt b/winpr/libwinpr/synch/test/CMakeLists.txt index b3a774ce6..cdda383da 100644 --- a/winpr/libwinpr/synch/test/CMakeLists.txt +++ b/winpr/libwinpr/synch/test/CMakeLists.txt @@ -10,6 +10,7 @@ set(${MODULE_PREFIX}_TESTS TestSynchCritical.c TestSynchSemaphore.c TestSynchThread.c + TestSynchTimerQueue.c TestSynchWaitableTimer.c) create_test_sourcelist(${MODULE_PREFIX}_SRCS diff --git a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c new file mode 100644 index 000000000..07efd29f3 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c @@ -0,0 +1,58 @@ + +#include +#include + +HANDLE gDoneEvent; + +VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) +{ + int param; + + if (!lpParam) + return; + + param = *((int*) lpParam); + + printf("TimerRoutine: Param: %d TimerOrWaitFired: %d\n", param, TimerOrWaitFired); + + SetEvent(gDoneEvent); +} + +int TestSynchTimerQueue(int argc, char* argv[]) +{ + int arg = 123; + HANDLE hTimer = NULL; + HANDLE hTimerQueue = NULL; + + gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + hTimerQueue = CreateTimerQueue(); + + if (!hTimerQueue) + { + printf("CreateTimerQueue failed (%d)\n", GetLastError()); + return -1; + } + + if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine, &arg , 1000, 0, 0)) + { + printf("CreateTimerQueueTimer failed (%d)\n", GetLastError()); + return -1; + } + + if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0) + { + printf("WaitForSingleObject failed (%d)\n", GetLastError()); + return -1; + } + + CloseHandle(gDoneEvent); + + if (!DeleteTimerQueue(hTimerQueue)) + { + printf("DeleteTimerQueue failed (%d)\n", GetLastError()); + return -1; + } + + return 0; +} diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index 32b38a042..8ff9f333c 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -31,6 +31,10 @@ #include "../handle/handle.h" +/** + * Waitable Timer + */ + HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, LPCSTR lpTimerName) { HANDLE handle = NULL; @@ -205,4 +209,98 @@ BOOL CancelWaitableTimer(HANDLE hTimer) return TRUE; } +/** + * Timer-Queue Timer + */ + +HANDLE CreateTimerQueue(void) +{ + HANDLE handle = NULL; + WINPR_TIMER_QUEUE* timerQueue; + + timerQueue = (WINPR_TIMER_QUEUE*) malloc(sizeof(WINPR_TIMER_QUEUE)); + + if (timerQueue) + { + WINPR_HANDLE_SET_TYPE(timerQueue, HANDLE_TYPE_TIMER_QUEUE); + handle = (HANDLE) timerQueue; + } + + return handle; +} + +BOOL DeleteTimerQueue(HANDLE TimerQueue) +{ + WINPR_TIMER_QUEUE* timerQueue; + + if (!TimerQueue) + return FALSE; + + timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; + + free(timerQueue); + + return TRUE; +} + +BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) +{ + WINPR_TIMER_QUEUE* timerQueue; + + if (!TimerQueue) + return FALSE; + + timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; + + free(timerQueue); + + return TRUE; +} + +BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, + WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags) +{ + WINPR_TIMER_QUEUE_TIMER* timer; + + timer = (WINPR_TIMER_QUEUE_TIMER*) malloc(sizeof(WINPR_TIMER_QUEUE_TIMER)); + + if (!timer) + return FALSE; + + WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER); + *((UINT_PTR*) phNewTimer) = (UINT_PTR) (HANDLE) timer; + + return TRUE; +} + +BOOL ChangeTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG Period) +{ + WINPR_TIMER_QUEUE* timerQueue; + WINPR_TIMER_QUEUE_TIMER* timer; + + if (!TimerQueue || !Timer) + return FALSE; + + timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; + timer = (WINPR_TIMER_QUEUE_TIMER*) Timer; + + return TRUE; +} + +BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent) +{ + WINPR_TIMER_QUEUE* timerQueue; + WINPR_TIMER_QUEUE_TIMER* timer; + + if (!TimerQueue || !Timer) + return FALSE; + + timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; + timer = (WINPR_TIMER_QUEUE_TIMER*) Timer; + + free(timer); + + return TRUE; +} + #endif From 9a4fb396dabe23f7fb9a6165b55981191ee38bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 24 Jan 2014 18:08:06 -0500 Subject: [PATCH 123/149] libwinpr-synch: add unit test for waitable timer asynchronous procedure calls --- client/X11/generate_argument_docbook.c | 2 +- winpr/libwinpr/synch/test/CMakeLists.txt | 3 +- .../synch/test/TestSynchWaitableTimerAPC.c | 55 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c diff --git a/client/X11/generate_argument_docbook.c b/client/X11/generate_argument_docbook.c index 75bef5f04..dd7b9d937 100644 --- a/client/X11/generate_argument_docbook.c +++ b/client/X11/generate_argument_docbook.c @@ -11,7 +11,7 @@ LPSTR tmp = NULL; -LPSTR tr_esc_str(LPSTR arg) +LPSTR tr_esc_str(LPCSTR arg) { size_t cs = 0, x, ds; size_t s; diff --git a/winpr/libwinpr/synch/test/CMakeLists.txt b/winpr/libwinpr/synch/test/CMakeLists.txt index cdda383da..1d21af0e4 100644 --- a/winpr/libwinpr/synch/test/CMakeLists.txt +++ b/winpr/libwinpr/synch/test/CMakeLists.txt @@ -11,7 +11,8 @@ set(${MODULE_PREFIX}_TESTS TestSynchSemaphore.c TestSynchThread.c TestSynchTimerQueue.c - TestSynchWaitableTimer.c) + TestSynchWaitableTimer.c + TestSynchWaitableTimerAPC.c) create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} diff --git a/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c b/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c new file mode 100644 index 000000000..ed34b3ec7 --- /dev/null +++ b/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c @@ -0,0 +1,55 @@ + +#include +#include + +HANDLE gDoneEvent; + +VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue) +{ + int param; + + if (!lpArg) + return; + + param = *((int*) lpArg); + + printf("TimerAPCProc: %d\n", param); + + SetEvent(gDoneEvent); +} + +int TestSynchWaitableTimerAPC(int argc, char* argv[]) +{ + int arg = 123; + HANDLE hTimer; + BOOL bSuccess; + INT64 qwDueTime; + LARGE_INTEGER liDueTime; + + hTimer = CreateWaitableTimer(NULL, FALSE, NULL); + + if (!hTimer) + return -1; + + qwDueTime = -5 * 10000000; + liDueTime.LowPart = (DWORD) (qwDueTime & 0xFFFFFFFF); + liDueTime.HighPart = (LONG) (qwDueTime >> 32); + + bSuccess = SetWaitableTimer(hTimer, &liDueTime, 2000, TimerAPCProc, &arg, FALSE); + + if (!bSuccess) + return -1; + + if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0) + { + printf("WaitForSingleObject failed (%d)\n", GetLastError()); + return -1; + } + + CloseHandle(gDoneEvent); + + CloseHandle(hTimer); + + return 0; +} + From d2405a25f83c02df4ad8f86976b9b54e60df2a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 24 Jan 2014 22:44:23 -0500 Subject: [PATCH 124/149] libwinpr-synch: initial waitable timer asynchronous procedure callback support --- winpr/libwinpr/synch/synch.h | 8 + .../synch/test/TestSynchWaitableTimerAPC.c | 44 +++-- winpr/libwinpr/synch/timer.c | 157 +++++++++++++++--- winpr/libwinpr/synch/wait.c | 29 +++- 4 files changed, 188 insertions(+), 50 deletions(-) diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index 6dbe50b69..d41f8283e 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -86,6 +86,8 @@ struct winpr_timer WINPR_HANDLE_DEF(); int fd; + BOOL bInit; + timer_t tid; LONG lPeriod; BOOL bManualReset; PTIMERAPCROUTINE pfnCompletionRoutine; @@ -106,6 +108,12 @@ typedef struct winpr_timer_queue WINPR_TIMER_QUEUE; struct winpr_timer_queue_timer { WINPR_HANDLE_DEF(); + + ULONG Flags; + DWORD DueTime; + DWORD Period; + PVOID Parameter; + WAITORTIMERCALLBACK Callback; }; typedef struct winpr_timer_queue_timer WINPR_TIMER_QUEUE_TIMER; diff --git a/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c b/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c index ed34b3ec7..238472e75 100644 --- a/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c +++ b/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c @@ -1,54 +1,68 @@ #include #include +#include -HANDLE gDoneEvent; +static int g_Count = 0; +static HANDLE g_Event = NULL; + +struct apc_data +{ + UINT32 StartTime; +}; +typedef struct apc_data APC_DATA; VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue) { - int param; + APC_DATA* apcData; + UINT32 CurrentTime = GetTickCount(); if (!lpArg) return; - param = *((int*) lpArg); + apcData = (APC_DATA*) lpArg; - printf("TimerAPCProc: %d\n", param); + printf("TimerAPCProc: time: %d\n", CurrentTime - apcData->StartTime); - SetEvent(gDoneEvent); + g_Count++; + + if (g_Count >= 5) + { + SetEvent(g_Event); + } } int TestSynchWaitableTimerAPC(int argc, char* argv[]) { - int arg = 123; HANDLE hTimer; BOOL bSuccess; - INT64 qwDueTime; - LARGE_INTEGER liDueTime; + LARGE_INTEGER due; + APC_DATA* apcData; + apcData = (APC_DATA*) malloc(sizeof(APC_DATA)); + g_Event = CreateEvent(NULL, TRUE, FALSE, NULL); hTimer = CreateWaitableTimer(NULL, FALSE, NULL); if (!hTimer) return -1; - qwDueTime = -5 * 10000000; - liDueTime.LowPart = (DWORD) (qwDueTime & 0xFFFFFFFF); - liDueTime.HighPart = (LONG) (qwDueTime >> 32); + due.QuadPart = -15000000LL; /* 1.5 seconds */ - bSuccess = SetWaitableTimer(hTimer, &liDueTime, 2000, TimerAPCProc, &arg, FALSE); + apcData->StartTime = GetTickCount(); + bSuccess = SetWaitableTimer(hTimer, &due, 2000, TimerAPCProc, apcData, FALSE); if (!bSuccess) return -1; - if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0) + if (WaitForSingleObject(g_Event, INFINITE) != WAIT_OBJECT_0) { printf("WaitForSingleObject failed (%d)\n", GetLastError()); return -1; } - CloseHandle(gDoneEvent); - CloseHandle(hTimer); + CloseHandle(g_Event); + free(apcData); return 0; } diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index 8ff9f333c..34aa4189b 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -25,12 +25,111 @@ #include +#ifndef _WIN32 +#include +#include +#endif + #include "synch.h" #ifndef _WIN32 #include "../handle/handle.h" +static BOOL g_WaitableTimerSignalHandlerInstalled = FALSE; + +void WaitableTimerSignalHandler(int signum, siginfo_t* siginfo, void* arg) +{ + WINPR_TIMER* timer = siginfo->si_value.sival_ptr; + + if (!timer || (signum != SIGALRM)) + return; + + if (timer->pfnCompletionRoutine) + { + timer->pfnCompletionRoutine(timer->lpArgToCompletionRoutine, 0, 0); + + if (timer->lPeriod) + { + timer->timeout.it_interval.tv_sec = (timer->lPeriod / 1000); /* seconds */ + timer->timeout.it_interval.tv_nsec = ((timer->lPeriod % 1000) * 1000000); /* nanoseconds */ + + if ((timer_settime(timer->tid, 0, &(timer->timeout), NULL)) != 0) + { + perror("timer_settime"); + } + } + } +} + +int InstallWaitableTimerSignalHandler() +{ + if (!g_WaitableTimerSignalHandlerInstalled) + { + struct sigaction action; + + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGALRM); + + action.sa_flags = SA_RESTART | SA_SIGINFO; + action.sa_sigaction = (void*) &WaitableTimerSignalHandler; + + sigaction(SIGALRM, &action, NULL); + + g_WaitableTimerSignalHandlerInstalled = TRUE; + } + + return 0; +} + +int InitializeWaitableTimer(WINPR_TIMER* timer) +{ + int status; + + if (!timer->lpArgToCompletionRoutine) + { +#ifdef HAVE_TIMERFD_H + timer->fd = timerfd_create(CLOCK_MONOTONIC, 0); + + if (timer->fd <= 0) + { + free(timer); + return -1; + } + + status = fcntl(timer->fd, F_SETFL, O_NONBLOCK); + + if (status) + { + close(timer->fd); + return -1; + } +#endif + } + else + { + struct sigevent sigev; + + InstallWaitableTimerSignalHandler(); + + ZeroMemory(&sigev, sizeof(struct sigevent)); + + sigev.sigev_notify = SIGEV_SIGNAL; + sigev.sigev_signo = SIGALRM; + sigev.sigev_value.sival_ptr = (void*) timer; + + if ((timer_create(CLOCK_MONOTONIC, &sigev, &(timer->tid))) != 0) + { + perror("timer_create"); + return -1; + } + } + + timer->bInit = TRUE; + + return 0; +} + /** * Waitable Timer */ @@ -44,8 +143,6 @@ HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManua if (timer) { - int status = 0; - WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER); handle = (HANDLE) timer; @@ -54,24 +151,7 @@ HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManua timer->bManualReset = bManualReset; timer->pfnCompletionRoutine = NULL; timer->lpArgToCompletionRoutine = NULL; - -#ifdef HAVE_TIMERFD_H - timer->fd = timerfd_create(CLOCK_MONOTONIC, 0); - if (timer->fd <= 0) - { - free(timer); - return NULL; - } - - status = fcntl(timer->fd, F_SETFL, O_NONBLOCK); - - if (status) - { - close(timer->fd); - free(timer); - return NULL; - } -#endif + timer->bInit = FALSE; } return handle; @@ -124,7 +204,12 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio timer->pfnCompletionRoutine = pfnCompletionRoutine; timer->lpArgToCompletionRoutine = lpArgToCompletionRoutine; -#ifdef HAVE_TIMERFD_H + if (!timer->bInit) + { + if (InitializeWaitableTimer(timer) < 0) + return FALSE; + } + ZeroMemory(&(timer->timeout), sizeof(struct itimerspec)); if (lpDueTime->QuadPart < 0) @@ -163,14 +248,26 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio timer->timeout.it_value.tv_nsec = timer->timeout.it_interval.tv_nsec; /* nanoseconds */ } - status = timerfd_settime(timer->fd, 0, &(timer->timeout), NULL); - - if (status) + if (!timer->pfnCompletionRoutine) { - printf("SetWaitableTimer timerfd_settime failure: %d\n", status); - return FALSE; - } +#ifdef HAVE_TIMERFD_H + status = timerfd_settime(timer->fd, 0, &(timer->timeout), NULL); + + if (status) + { + printf("SetWaitableTimer timerfd_settime failure: %d\n", status); + return FALSE; + } #endif + } + else + { + if ((timer_settime(timer->tid, 0, &(timer->timeout), NULL)) != 0) + { + perror("timer_settime"); + return FALSE; + } + } return TRUE; } @@ -270,6 +367,12 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER); *((UINT_PTR*) phNewTimer) = (UINT_PTR) (HANDLE) timer; + timer->Flags = Flags; + timer->DueTime = DueTime; + timer->Period = Period; + timer->Callback = Callback; + timer->Parameter = Parameter; + return TRUE; } diff --git a/winpr/libwinpr/synch/wait.c b/winpr/libwinpr/synch/wait.c index 560fed572..eb2268c11 100644 --- a/winpr/libwinpr/synch/wait.c +++ b/winpr/libwinpr/synch/wait.c @@ -247,7 +247,9 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) return WAIT_TIMEOUT; } else + { pthread_mutex_lock(&mutex->mutex); + } } else if (Type == HANDLE_TYPE_EVENT) { @@ -268,8 +270,12 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } - status = select(event->pipe_fd[0] + 1, &rfds, NULL, NULL, - (dwMilliseconds == INFINITE) ? NULL : &timeout); + do + { + status = select(event->pipe_fd[0] + 1, &rfds, NULL, NULL, + (dwMilliseconds == INFINITE) ? NULL : &timeout); + } + while (status < 0 && (errno == EINTR)); if (status < 0) { @@ -304,8 +310,12 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } - status = select(semaphore->pipe_fd[0] + 1, &rfds, 0, 0, - (dwMilliseconds == INFINITE) ? NULL : &timeout); + do + { + status = select(semaphore->pipe_fd[0] + 1, &rfds, 0, 0, + (dwMilliseconds == INFINITE) ? NULL : &timeout); + } + while (status < 0 && (errno == EINTR)); if (status < 0) { @@ -358,8 +368,12 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) timeout.tv_usec = (dwMilliseconds % 1000) * 1000; } - status = select(timer->fd + 1, &rfds, 0, 0, - (dwMilliseconds == INFINITE) ? NULL : &timeout); + do + { + status = select(timer->fd + 1, &rfds, 0, 0, + (dwMilliseconds == INFINITE) ? NULL : &timeout); + } + while (status < 0 && (errno == EINTR)); if (status < 0) { @@ -431,7 +445,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) status = select(fd + 1, &rfds, NULL, NULL, (dwMilliseconds == INFINITE) ? NULL : &timeout); } - while (status < 0 && errno == EINTR); + while (status < 0 && (errno == EINTR)); if (status < 0) { @@ -476,7 +490,6 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAl return WAIT_FAILED; } - maxfd = 0; FD_ZERO(&fds); ZeroMemory(&timeout, sizeof(timeout)); From d5fcd78b65fe191d4f8345728bd992a91635345d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 26 Jan 2014 00:06:27 -0500 Subject: [PATCH 125/149] libwinpr-synch: fix build on OS X --- winpr/libwinpr/synch/synch.h | 10 +++++++++- winpr/libwinpr/synch/timer.c | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index d41f8283e..9be3cc08e 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -28,6 +28,10 @@ #include +#ifdef __linux__ +#define WITH_POSIX_TIMER 1 +#endif + #ifndef _WIN32 #include "../handle/handle.h" @@ -36,6 +40,7 @@ #if defined __APPLE__ #include +#include #include #include #include @@ -87,11 +92,14 @@ struct winpr_timer int fd; BOOL bInit; - timer_t tid; LONG lPeriod; BOOL bManualReset; PTIMERAPCROUTINE pfnCompletionRoutine; LPVOID lpArgToCompletionRoutine; + +#ifdef WITH_POSIX_TIMER + timer_t tid; +#endif #ifdef HAVE_TIMERFD_H struct itimerspec timeout; diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index 34aa4189b..ec5bdb928 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -36,6 +36,8 @@ #include "../handle/handle.h" +#ifdef WITH_POSIX_TIMER + static BOOL g_WaitableTimerSignalHandlerInstalled = FALSE; void WaitableTimerSignalHandler(int signum, siginfo_t* siginfo, void* arg) @@ -82,13 +84,15 @@ int InstallWaitableTimerSignalHandler() return 0; } +#endif + int InitializeWaitableTimer(WINPR_TIMER* timer) { - int status; - if (!timer->lpArgToCompletionRoutine) { #ifdef HAVE_TIMERFD_H + int status; + timer->fd = timerfd_create(CLOCK_MONOTONIC, 0); if (timer->fd <= 0) @@ -108,6 +112,7 @@ int InitializeWaitableTimer(WINPR_TIMER* timer) } else { +#ifdef WITH_POSIX_TIMER struct sigevent sigev; InstallWaitableTimerSignalHandler(); @@ -123,6 +128,7 @@ int InitializeWaitableTimer(WINPR_TIMER* timer) perror("timer_create"); return -1; } +#endif } timer->bInit = TRUE; @@ -210,6 +216,8 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio return FALSE; } +#ifdef WITH_POSIX_TIMER + ZeroMemory(&(timer->timeout), sizeof(struct itimerspec)); if (lpDueTime->QuadPart < 0) @@ -269,6 +277,8 @@ BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER* lpDueTime, LONG lPerio } } +#endif + return TRUE; } From 656766ca2e31e0fa272b435d5f90f59d44eb2508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 26 Jan 2014 12:37:32 -0500 Subject: [PATCH 126/149] libwinpr-synch: start implementing timer queue --- winpr/libwinpr/synch/synch.h | 16 ++++- .../libwinpr/synch/test/TestSynchTimerQueue.c | 6 ++ winpr/libwinpr/synch/timer.c | 61 ++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index 9be3cc08e..282d3857f 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -107,9 +107,19 @@ struct winpr_timer }; typedef struct winpr_timer WINPR_TIMER; +typedef struct winpr_timer_queue_timer WINPR_TIMER_QUEUE_TIMER; + struct winpr_timer_queue { WINPR_HANDLE_DEF(); + + pthread_t thread; + pthread_attr_t attr; + pthread_cond_t cond; + pthread_mutex_t mutex; + struct sched_param param; + + WINPR_TIMER_QUEUE_TIMER* head; }; typedef struct winpr_timer_queue WINPR_TIMER_QUEUE; @@ -122,8 +132,12 @@ struct winpr_timer_queue_timer DWORD Period; PVOID Parameter; WAITORTIMERCALLBACK Callback; + + WINPR_TIMER_QUEUE* timerQueue; + + WINPR_TIMER_QUEUE_TIMER* prev; + WINPR_TIMER_QUEUE_TIMER* next; }; -typedef struct winpr_timer_queue_timer WINPR_TIMER_QUEUE_TIMER; #endif diff --git a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c index 07efd29f3..3400a863c 100644 --- a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c +++ b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c @@ -48,6 +48,12 @@ int TestSynchTimerQueue(int argc, char* argv[]) CloseHandle(gDoneEvent); + if (!DeleteTimerQueueTimer(hTimerQueue, hTimer, NULL)) + { + printf("DeleteTimerQueueTimer failed (%d)\n", GetLastError()); + return -1; + } + if (!DeleteTimerQueue(hTimerQueue)) { printf("DeleteTimerQueue failed (%d)\n", GetLastError()); diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index ec5bdb928..e1afedcba 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -320,6 +320,59 @@ BOOL CancelWaitableTimer(HANDLE hTimer) * Timer-Queue Timer */ +/** + * Design, Performance, and Optimization of Timer Strategies for Real-time ORBs: + * http://www.cs.wustl.edu/~schmidt/Timer_Queue.html + */ + +static void* TimerQueueThread(void* arg) +{ + //WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*) arg; + + return NULL; +} + +int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue) +{ + pthread_cond_init(&(timerQueue->cond), NULL); + pthread_mutex_init(&(timerQueue->mutex), NULL); + + pthread_attr_init(&(timerQueue->attr)); + timerQueue->param.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_setschedparam(&(timerQueue->attr), &(timerQueue->param)); + pthread_attr_setschedpolicy(&(timerQueue->attr), SCHED_FIFO); + pthread_create(&(timerQueue->thread), &(timerQueue->attr), TimerQueueThread, timerQueue); + + return 0; +} + +int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER* timer) +{ + WINPR_TIMER_QUEUE_TIMER* node; + + if (!timerQueue->head) + { + timerQueue->head = timer; + timer->prev = NULL; + timer->next = NULL; + return 0; + } + + node = timerQueue->head; + + do + { + node = node->next; + } + while (node->next); + + node->next = timer; + timer->prev = node; + timer->next = NULL; + + return 0; +} + HANDLE CreateTimerQueue(void) { HANDLE handle = NULL; @@ -331,6 +384,8 @@ HANDLE CreateTimerQueue(void) { WINPR_HANDLE_SET_TYPE(timerQueue, HANDLE_TYPE_TIMER_QUEUE); handle = (HANDLE) timerQueue; + + StartTimerQueueThread(timerQueue); } return handle; @@ -367,11 +422,13 @@ BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags) { + WINPR_TIMER_QUEUE* timerQueue; WINPR_TIMER_QUEUE_TIMER* timer; + timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; timer = (WINPR_TIMER_QUEUE_TIMER*) malloc(sizeof(WINPR_TIMER_QUEUE_TIMER)); - if (!timer) + if (timer || !TimerQueue) return FALSE; WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER); @@ -382,6 +439,8 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, timer->Period = Period; timer->Callback = Callback; timer->Parameter = Parameter; + + timer->timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; return TRUE; } From 2e45ad143f011dade70ab2905ca44ae079c9a134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 26 Jan 2014 17:21:12 -0500 Subject: [PATCH 127/149] libwinpr-synch: improve timer queue implementation --- winpr/libwinpr/synch/synch.h | 6 + .../libwinpr/synch/test/TestSynchTimerQueue.c | 62 ++++++++-- winpr/libwinpr/synch/timer.c | 117 ++++++++++++------ 3 files changed, 134 insertions(+), 51 deletions(-) diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index 282d3857f..557aa4a05 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -119,6 +119,8 @@ struct winpr_timer_queue pthread_mutex_t mutex; struct sched_param param; + int resolution; + WINPR_TIMER_QUEUE_TIMER* head; }; typedef struct winpr_timer_queue WINPR_TIMER_QUEUE; @@ -133,6 +135,10 @@ struct winpr_timer_queue_timer PVOID Parameter; WAITORTIMERCALLBACK Callback; + int FireCount; + UINT64 StartTime; + UINT64 ExpirationTime; + WINPR_TIMER_QUEUE* timerQueue; WINPR_TIMER_QUEUE_TIMER* prev; diff --git a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c index 3400a863c..96859c0f5 100644 --- a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c +++ b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c @@ -1,30 +1,56 @@ #include +#include + #include -HANDLE gDoneEvent; +#define FIRE_COUNT 5 +#define TIMER_COUNT 4 + +static int g_Count = 0; +static HANDLE g_Event = NULL; + +struct apc_data +{ + int TimerId; + UINT32 StartTime; +}; +typedef struct apc_data APC_DATA; VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) { - int param; + UINT32 TimerTime; + APC_DATA* apcData; + UINT32 CurrentTime = GetTickCount(); if (!lpParam) return; - param = *((int*) lpParam); + apcData = (APC_DATA*) lpParam; - printf("TimerRoutine: Param: %d TimerOrWaitFired: %d\n", param, TimerOrWaitFired); + TimerTime = CurrentTime - apcData->StartTime; - SetEvent(gDoneEvent); + printf("TimerRoutine: TimerId: %d Time: %d Discrepancy: %d\n", + apcData->TimerId, TimerTime, TimerTime % 100); + + g_Count++; + + if (g_Count >= (TIMER_COUNT * FIRE_COUNT)) + { + SetEvent(g_Event); + } } int TestSynchTimerQueue(int argc, char* argv[]) { - int arg = 123; + int index; + DWORD DueTime; + DWORD Period; HANDLE hTimer = NULL; HANDLE hTimerQueue = NULL; + APC_DATA apcData[TIMER_COUNT]; - gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + g_Event = CreateEvent(NULL, TRUE, FALSE, NULL); hTimerQueue = CreateTimerQueue(); @@ -34,20 +60,28 @@ int TestSynchTimerQueue(int argc, char* argv[]) return -1; } - if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine, &arg , 1000, 0, 0)) + for (index = 0; index < TIMER_COUNT; index++) { - printf("CreateTimerQueueTimer failed (%d)\n", GetLastError()); - return -1; + apcData[index].TimerId = index; + apcData[index].StartTime = GetTickCount(); + + DueTime = (index * 100) + 500; + Period = 1000; + + if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine, + &apcData[index], DueTime, Period, 0)) + { + printf("CreateTimerQueueTimer failed (%d)\n", GetLastError()); + return -1; + } } - if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0) + if (WaitForSingleObject(g_Event, INFINITE) != WAIT_OBJECT_0) { printf("WaitForSingleObject failed (%d)\n", GetLastError()); return -1; } - CloseHandle(gDoneEvent); - if (!DeleteTimerQueueTimer(hTimerQueue, hTimer, NULL)) { printf("DeleteTimerQueueTimer failed (%d)\n", GetLastError()); @@ -60,5 +94,7 @@ int TestSynchTimerQueue(int argc, char* argv[]) return -1; } + CloseHandle(g_Event); + return 0; } diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index e1afedcba..85b5fc2a6 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -22,6 +22,7 @@ #endif #include +#include #include @@ -325,27 +326,6 @@ BOOL CancelWaitableTimer(HANDLE hTimer) * http://www.cs.wustl.edu/~schmidt/Timer_Queue.html */ -static void* TimerQueueThread(void* arg) -{ - //WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*) arg; - - return NULL; -} - -int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue) -{ - pthread_cond_init(&(timerQueue->cond), NULL); - pthread_mutex_init(&(timerQueue->mutex), NULL); - - pthread_attr_init(&(timerQueue->attr)); - timerQueue->param.sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_attr_setschedparam(&(timerQueue->attr), &(timerQueue->param)); - pthread_attr_setschedpolicy(&(timerQueue->attr), SCHED_FIFO); - pthread_create(&(timerQueue->thread), &(timerQueue->attr), TimerQueueThread, timerQueue); - - return 0; -} - int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER* timer) { WINPR_TIMER_QUEUE_TIMER* node; @@ -360,11 +340,10 @@ int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER node = timerQueue->head; - do + while (node->next) { node = node->next; } - while (node->next); node->next = timer; timer->prev = node; @@ -373,6 +352,64 @@ int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER return 0; } +int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) +{ + UINT64 currentTime; + WINPR_TIMER_QUEUE_TIMER* node; + + if (!timerQueue->head) + return 0; + + currentTime = GetTickCount64(); + + node = timerQueue->head; + + while (node) + { + if (currentTime >= node->ExpirationTime) + { + node->Callback(node->Parameter, TRUE); + node->FireCount++; + + if (node->Period) + { + node->ExpirationTime = node->StartTime + (node->FireCount * node->Period); + } + } + + node = node->next; + } + + return 0; +} + +static void* TimerQueueThread(void* arg) +{ + WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*) arg; + + while (1) + { + FireExpiredTimerQueueTimers(timerQueue); + Sleep(timerQueue->resolution); + } + + return NULL; +} + +int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue) +{ + pthread_cond_init(&(timerQueue->cond), NULL); + pthread_mutex_init(&(timerQueue->mutex), NULL); + + pthread_attr_init(&(timerQueue->attr)); + timerQueue->param.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_setschedparam(&(timerQueue->attr), &(timerQueue->param)); + pthread_attr_setschedpolicy(&(timerQueue->attr), SCHED_FIFO); + pthread_create(&(timerQueue->thread), &(timerQueue->attr), TimerQueueThread, timerQueue); + + return 0; +} + HANDLE CreateTimerQueue(void) { HANDLE handle = NULL; @@ -385,26 +422,14 @@ HANDLE CreateTimerQueue(void) WINPR_HANDLE_SET_TYPE(timerQueue, HANDLE_TYPE_TIMER_QUEUE); handle = (HANDLE) timerQueue; + timerQueue->resolution = 5; + StartTimerQueueThread(timerQueue); } return handle; } -BOOL DeleteTimerQueue(HANDLE TimerQueue) -{ - WINPR_TIMER_QUEUE* timerQueue; - - if (!TimerQueue) - return FALSE; - - timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; - - free(timerQueue); - - return TRUE; -} - BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) { WINPR_TIMER_QUEUE* timerQueue; @@ -414,21 +439,31 @@ BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; + pthread_cond_destroy(&(timerQueue->cond)); + pthread_mutex_destroy(&(timerQueue->mutex)); + free(timerQueue); return TRUE; } +BOOL DeleteTimerQueue(HANDLE TimerQueue) +{ + return DeleteTimerQueueEx(TimerQueue, NULL); +} + BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags) { + UINT64 currentTime; WINPR_TIMER_QUEUE* timerQueue; WINPR_TIMER_QUEUE_TIMER* timer; + currentTime = GetTickCount64(); timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; timer = (WINPR_TIMER_QUEUE_TIMER*) malloc(sizeof(WINPR_TIMER_QUEUE_TIMER)); - if (timer || !TimerQueue) + if (!timer || !TimerQueue) return FALSE; WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER); @@ -442,6 +477,12 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, timer->timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; + timer->FireCount = 0; + timer->StartTime = currentTime + DueTime; + timer->ExpirationTime = timer->StartTime; + + InsertTimerQueueTimer(timerQueue, timer); + return TRUE; } From 3616b192904b782eb6bbed655613793bd97b6701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 26 Jan 2014 20:39:13 -0500 Subject: [PATCH 128/149] libwinpr-synch: make use of timespec for timer queue --- winpr/libwinpr/synch/synch.h | 12 +-- .../libwinpr/synch/test/TestSynchTimerQueue.c | 22 ++-- winpr/libwinpr/synch/timer.c | 100 +++++++++++++----- 3 files changed, 91 insertions(+), 43 deletions(-) diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index 557aa4a05..ceea862fd 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -115,11 +115,10 @@ struct winpr_timer_queue pthread_t thread; pthread_attr_t attr; - pthread_cond_t cond; pthread_mutex_t mutex; + pthread_cond_t cond; + pthread_mutex_t cond_mutex; struct sched_param param; - - int resolution; WINPR_TIMER_QUEUE_TIMER* head; }; @@ -136,12 +135,11 @@ struct winpr_timer_queue_timer WAITORTIMERCALLBACK Callback; int FireCount; - UINT64 StartTime; - UINT64 ExpirationTime; + + struct timespec StartTime; + struct timespec ExpirationTime; WINPR_TIMER_QUEUE* timerQueue; - - WINPR_TIMER_QUEUE_TIMER* prev; WINPR_TIMER_QUEUE_TIMER* next; }; diff --git a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c index 96859c0f5..1c9ad7c58 100644 --- a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c +++ b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c @@ -5,7 +5,7 @@ #include #define FIRE_COUNT 5 -#define TIMER_COUNT 4 +#define TIMER_COUNT 5 static int g_Count = 0; static HANDLE g_Event = NULL; @@ -13,6 +13,9 @@ static HANDLE g_Event = NULL; struct apc_data { int TimerId; + int FireCount; + DWORD DueTime; + DWORD Period; UINT32 StartTime; }; typedef struct apc_data APC_DATA; @@ -21,6 +24,7 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) { UINT32 TimerTime; APC_DATA* apcData; + UINT32 expectedTime; UINT32 CurrentTime = GetTickCount(); if (!lpParam) @@ -29,10 +33,12 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) apcData = (APC_DATA*) lpParam; TimerTime = CurrentTime - apcData->StartTime; + expectedTime = apcData->DueTime + (apcData->Period * apcData->FireCount); - printf("TimerRoutine: TimerId: %d Time: %d Discrepancy: %d\n", - apcData->TimerId, TimerTime, TimerTime % 100); + printf("TimerRoutine: TimerId: %d ActualTime: %d ExpectedTime: %d Discrepancy: %d\n", + apcData->TimerId, TimerTime, expectedTime, TimerTime - expectedTime); + apcData->FireCount++; g_Count++; if (g_Count >= (TIMER_COUNT * FIRE_COUNT)) @@ -44,8 +50,6 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) int TestSynchTimerQueue(int argc, char* argv[]) { int index; - DWORD DueTime; - DWORD Period; HANDLE hTimer = NULL; HANDLE hTimerQueue = NULL; APC_DATA apcData[TIMER_COUNT]; @@ -64,12 +68,12 @@ int TestSynchTimerQueue(int argc, char* argv[]) { apcData[index].TimerId = index; apcData[index].StartTime = GetTickCount(); - - DueTime = (index * 100) + 500; - Period = 1000; + apcData[index].DueTime = (index * 100) + 500; + apcData[index].Period = 1000; + apcData[index].FireCount = 0; if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine, - &apcData[index], DueTime, Period, 0)) + &apcData[index], apcData[index].DueTime, apcData[index].Period, 0)) { printf("CreateTimerQueueTimer failed (%d)\n", GetLastError()); return -1; diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index 85b5fc2a6..60e904e45 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -326,54 +326,86 @@ BOOL CancelWaitableTimer(HANDLE hTimer) * http://www.cs.wustl.edu/~schmidt/Timer_Queue.html */ -int InsertTimerQueueTimer(WINPR_TIMER_QUEUE* timerQueue, WINPR_TIMER_QUEUE_TIMER* timer) +static void timespec_add_ms(struct timespec* tspec, UINT32 ms) +{ + UINT64 ns = tspec->tv_nsec + (ms * 1000000); + tspec->tv_sec += (ns / 1000000000); + tspec->tv_nsec = (ns % 1000000000); +} + +static void timespec_gettimeofday(struct timespec* tspec) +{ + struct timeval tval; + gettimeofday(&tval, NULL); + tspec->tv_sec = tval.tv_sec; + tspec->tv_nsec = tval.tv_usec * 1000; +} + +static int timespec_compare(const struct timespec* tspec1, const struct timespec* tspec2) +{ + if (tspec1->tv_sec < tspec2->tv_sec) + return -1; + + if (tspec1->tv_sec > tspec2->tv_sec) + return 1; + + return tspec1->tv_nsec - tspec2->tv_nsec; +} + +static void timespec_copy(struct timespec* dst, struct timespec* src) +{ + dst->tv_sec = src->tv_sec; + dst->tv_nsec = src->tv_nsec; +} + +void InsertTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer) { WINPR_TIMER_QUEUE_TIMER* node; - - if (!timerQueue->head) + + if (!(*pHead)) { - timerQueue->head = timer; - timer->prev = NULL; - timer->next = NULL; - return 0; + *pHead = timer; + return; } - - node = timerQueue->head; - + + node = *pHead; + while (node->next) { + if (timespec_compare(&(timer->ExpirationTime), &(node->ExpirationTime)) < 0) + break; + node = node->next; } - + + if (node->next) + timer->next = node->next->next; + node->next = timer; - timer->prev = node; - timer->next = NULL; - - return 0; } int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) { - UINT64 currentTime; + struct timespec CurrentTime; WINPR_TIMER_QUEUE_TIMER* node; if (!timerQueue->head) return 0; - currentTime = GetTickCount64(); + timespec_gettimeofday(&CurrentTime); node = timerQueue->head; while (node) { - if (currentTime >= node->ExpirationTime) + if (timespec_compare(&CurrentTime, &(node->ExpirationTime)) >= 0) { node->Callback(node->Parameter, TRUE); node->FireCount++; if (node->Period) { - node->ExpirationTime = node->StartTime + (node->FireCount * node->Period); + timespec_add_ms(&(node->ExpirationTime), node->Period); } } @@ -385,12 +417,22 @@ int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) static void* TimerQueueThread(void* arg) { + int status; + struct timespec tspec; WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*) arg; while (1) { + pthread_mutex_lock(&(timerQueue->cond_mutex)); + + timespec_gettimeofday(&tspec); + timespec_add_ms(&tspec, 23); + + status = pthread_cond_timedwait(&(timerQueue->cond), &(timerQueue->cond_mutex), &tspec); + FireExpiredTimerQueueTimers(timerQueue); - Sleep(timerQueue->resolution); + + pthread_mutex_unlock(&(timerQueue->cond_mutex)); } return NULL; @@ -399,6 +441,8 @@ static void* TimerQueueThread(void* arg) int StartTimerQueueThread(WINPR_TIMER_QUEUE* timerQueue) { pthread_cond_init(&(timerQueue->cond), NULL); + pthread_mutex_init(&(timerQueue->cond_mutex), NULL); + pthread_mutex_init(&(timerQueue->mutex), NULL); pthread_attr_init(&(timerQueue->attr)); @@ -421,8 +465,6 @@ HANDLE CreateTimerQueue(void) { WINPR_HANDLE_SET_TYPE(timerQueue, HANDLE_TYPE_TIMER_QUEUE); handle = (HANDLE) timerQueue; - - timerQueue->resolution = 5; StartTimerQueueThread(timerQueue); } @@ -440,8 +482,12 @@ BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; pthread_cond_destroy(&(timerQueue->cond)); + pthread_mutex_destroy(&(timerQueue->cond_mutex)); + pthread_mutex_destroy(&(timerQueue->mutex)); + pthread_attr_destroy(&(timerQueue->attr)); + free(timerQueue); return TRUE; @@ -455,11 +501,9 @@ BOOL DeleteTimerQueue(HANDLE TimerQueue) BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags) { - UINT64 currentTime; WINPR_TIMER_QUEUE* timerQueue; WINPR_TIMER_QUEUE_TIMER* timer; - currentTime = GetTickCount64(); timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; timer = (WINPR_TIMER_QUEUE_TIMER*) malloc(sizeof(WINPR_TIMER_QUEUE_TIMER)); @@ -469,6 +513,10 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER); *((UINT_PTR*) phNewTimer) = (UINT_PTR) (HANDLE) timer; + timespec_gettimeofday(&(timer->StartTime)); + timespec_add_ms(&(timer->StartTime), DueTime); + timespec_copy(&(timer->ExpirationTime), &(timer->StartTime)); + timer->Flags = Flags; timer->DueTime = DueTime; timer->Period = Period; @@ -478,10 +526,8 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, timer->timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; timer->FireCount = 0; - timer->StartTime = currentTime + DueTime; - timer->ExpirationTime = timer->StartTime; - InsertTimerQueueTimer(timerQueue, timer); + InsertTimerQueueTimer(&(timerQueue->head), timer); return TRUE; } From 1f394eb81d1a429b806b41d3f239328ae93eb3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 26 Jan 2014 20:49:48 -0500 Subject: [PATCH 129/149] libwinpr-synch: make use of head's expiration time in timer queue --- winpr/libwinpr/synch/timer.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index 60e904e45..bc95ee063 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -406,10 +406,18 @@ int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) if (node->Period) { timespec_add_ms(&(node->ExpirationTime), node->Period); + + timerQueue->head = node->next; + node->next = NULL; + + InsertTimerQueueTimer(&(timerQueue->head), node); + node = timerQueue->head; } } - - node = node->next; + else + { + break; + } } return 0; @@ -418,17 +426,24 @@ int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) static void* TimerQueueThread(void* arg) { int status; - struct timespec tspec; + struct timespec timeout; WINPR_TIMER_QUEUE* timerQueue = (WINPR_TIMER_QUEUE*) arg; while (1) { pthread_mutex_lock(&(timerQueue->cond_mutex)); - timespec_gettimeofday(&tspec); - timespec_add_ms(&tspec, 23); + if (!timerQueue->head) + { + timespec_gettimeofday(&timeout); + timespec_add_ms(&timeout, 20); + } + else + { + timespec_copy(&timeout, &(timerQueue->head->ExpirationTime)); + } - status = pthread_cond_timedwait(&(timerQueue->cond), &(timerQueue->cond_mutex), &tspec); + status = pthread_cond_timedwait(&(timerQueue->cond), &(timerQueue->cond_mutex), &timeout); FireExpiredTimerQueueTimers(timerQueue); From 159f539ef246adf8025d45f5da5f537ea6f2cfad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 26 Jan 2014 21:56:07 -0500 Subject: [PATCH 130/149] libwinpr-synch: improve timer queue implementation --- winpr/libwinpr/synch/synch.h | 1 + .../libwinpr/synch/test/TestSynchTimerQueue.c | 21 +-- winpr/libwinpr/synch/timer.c | 124 +++++++++++++++++- 3 files changed, 133 insertions(+), 13 deletions(-) diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index ceea862fd..d0f283f44 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -120,6 +120,7 @@ struct winpr_timer_queue pthread_mutex_t cond_mutex; struct sched_param param; + BOOL bCancelled; WINPR_TIMER_QUEUE_TIMER* head; }; typedef struct winpr_timer_queue WINPR_TIMER_QUEUE; diff --git a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c index 1c9ad7c58..67a20db2a 100644 --- a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c +++ b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c @@ -35,12 +35,12 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) TimerTime = CurrentTime - apcData->StartTime; expectedTime = apcData->DueTime + (apcData->Period * apcData->FireCount); - printf("TimerRoutine: TimerId: %d ActualTime: %d ExpectedTime: %d Discrepancy: %d\n", - apcData->TimerId, TimerTime, expectedTime, TimerTime - expectedTime); - apcData->FireCount++; g_Count++; + printf("TimerRoutine: TimerId: %d FireCount: %d ActualTime: %d ExpectedTime: %d Discrepancy: %d\n", + apcData->TimerId, apcData->FireCount, TimerTime, expectedTime, TimerTime - expectedTime); + if (g_Count >= (TIMER_COUNT * FIRE_COUNT)) { SetEvent(g_Event); @@ -50,8 +50,8 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) int TestSynchTimerQueue(int argc, char* argv[]) { int index; - HANDLE hTimer = NULL; - HANDLE hTimerQueue = NULL; + HANDLE hTimerQueue; + HANDLE hTimers[TIMER_COUNT]; APC_DATA apcData[TIMER_COUNT]; g_Event = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -72,7 +72,7 @@ int TestSynchTimerQueue(int argc, char* argv[]) apcData[index].Period = 1000; apcData[index].FireCount = 0; - if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine, + if (!CreateTimerQueueTimer(&hTimers[index], hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine, &apcData[index], apcData[index].DueTime, apcData[index].Period, 0)) { printf("CreateTimerQueueTimer failed (%d)\n", GetLastError()); @@ -86,10 +86,13 @@ int TestSynchTimerQueue(int argc, char* argv[]) return -1; } - if (!DeleteTimerQueueTimer(hTimerQueue, hTimer, NULL)) + for (index = 0; index < TIMER_COUNT; index++) { - printf("DeleteTimerQueueTimer failed (%d)\n", GetLastError()); - return -1; + if (!DeleteTimerQueueTimer(hTimerQueue, hTimers[index], NULL)) + { + printf("DeleteTimerQueueTimer failed (%d)\n", GetLastError()); + return -1; + } } if (!DeleteTimerQueue(hTimerQueue)) diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index bc95ee063..511c1795b 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -22,11 +22,13 @@ #endif #include +#include #include #include #ifndef _WIN32 +#include #include #include #endif @@ -384,6 +386,33 @@ void InsertTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TI node->next = timer; } +void RemoveTimerQueueTimer(WINPR_TIMER_QUEUE_TIMER** pHead, WINPR_TIMER_QUEUE_TIMER* timer) +{ + WINPR_TIMER_QUEUE_TIMER* node; + WINPR_TIMER_QUEUE_TIMER* prevNode; + + if (timer == *pHead) + { + *pHead = timer->next; + return; + } + + node = *pHead; + prevNode = NULL; + + while (node) + { + if (node == timer) + break; + + prevNode = node; + node = node->next; + } + + prevNode->next = timer->next; + timer->next = NULL; +} + int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) { struct timespec CurrentTime; @@ -403,13 +432,13 @@ int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) node->Callback(node->Parameter, TRUE); node->FireCount++; + timerQueue->head = node->next; + node->next = NULL; + if (node->Period) { timespec_add_ms(&(node->ExpirationTime), node->Period); - timerQueue->head = node->next; - node->next = NULL; - InsertTimerQueueTimer(&(timerQueue->head), node); node = timerQueue->head; } @@ -436,7 +465,7 @@ static void* TimerQueueThread(void* arg) if (!timerQueue->head) { timespec_gettimeofday(&timeout); - timespec_add_ms(&timeout, 20); + timespec_add_ms(&timeout, 100); } else { @@ -448,6 +477,9 @@ static void* TimerQueueThread(void* arg) FireExpiredTimerQueueTimers(timerQueue); pthread_mutex_unlock(&(timerQueue->cond_mutex)); + + if (timerQueue->bCancelled) + break; } return NULL; @@ -481,6 +513,9 @@ HANDLE CreateTimerQueue(void) WINPR_HANDLE_SET_TYPE(timerQueue, HANDLE_TYPE_TIMER_QUEUE); handle = (HANDLE) timerQueue; + timerQueue->head = NULL; + timerQueue->bCancelled = FALSE; + StartTimerQueueThread(timerQueue); } @@ -489,13 +524,51 @@ HANDLE CreateTimerQueue(void) BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) { + void* rvalue; WINPR_TIMER_QUEUE* timerQueue; + WINPR_TIMER_QUEUE_TIMER* node; + WINPR_TIMER_QUEUE_TIMER* nextNode; if (!TimerQueue) return FALSE; timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; + /* Cancel and delete timer queue timers */ + + pthread_mutex_lock(&(timerQueue->cond_mutex)); + + timerQueue->bCancelled = TRUE; + + pthread_cond_signal(&(timerQueue->cond)); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + + pthread_join(timerQueue->thread, &rvalue); + + if (CompletionEvent == INVALID_HANDLE_VALUE) + { + /* Wait for all callback functions to complete before returning */ + } + else + { + /* Cancel all timers and return immediately */ + + node = timerQueue->head; + + while (node) + { + nextNode = node->next; + + free(node); + + node = nextNode; + } + + timerQueue->head = NULL; + } + + /* Delete timer queue */ + pthread_cond_destroy(&(timerQueue->cond)); pthread_mutex_destroy(&(timerQueue->cond_mutex)); @@ -505,6 +578,9 @@ BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) free(timerQueue); + if (CompletionEvent && (CompletionEvent != INVALID_HANDLE_VALUE)) + SetEvent(CompletionEvent); + return TRUE; } @@ -542,8 +618,13 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, timer->FireCount = 0; + pthread_mutex_lock(&(timerQueue->cond_mutex)); + InsertTimerQueueTimer(&(timerQueue->head), timer); + pthread_cond_signal(&(timerQueue->cond)); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + return TRUE; } @@ -558,6 +639,22 @@ BOOL ChangeTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; timer = (WINPR_TIMER_QUEUE_TIMER*) Timer; + pthread_mutex_lock(&(timerQueue->cond_mutex)); + + RemoveTimerQueueTimer(&(timerQueue->head), timer); + + timer->DueTime = DueTime; + timer->Period = Period; + + timespec_gettimeofday(&(timer->StartTime)); + timespec_add_ms(&(timer->StartTime), DueTime); + timespec_copy(&(timer->ExpirationTime), &(timer->StartTime)); + + InsertTimerQueueTimer(&(timerQueue->head), timer); + + pthread_cond_signal(&(timerQueue->cond)); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + return TRUE; } @@ -572,8 +669,27 @@ BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEve timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; timer = (WINPR_TIMER_QUEUE_TIMER*) Timer; + pthread_mutex_lock(&(timerQueue->cond_mutex)); + + if (CompletionEvent == INVALID_HANDLE_VALUE) + { + /* Wait for all callback functions to complete before returning */ + } + else + { + /* Cancel timer and return immediately */ + + RemoveTimerQueueTimer(&(timerQueue->head), timer); + } + + pthread_cond_signal(&(timerQueue->cond)); + pthread_mutex_unlock(&(timerQueue->cond_mutex)); + free(timer); + if (CompletionEvent && (CompletionEvent != INVALID_HANDLE_VALUE)) + SetEvent(CompletionEvent); + return TRUE; } From eb38b9f1e1fa3d8961b03295ccfb2f6dd64eba15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 27 Jan 2014 10:37:38 -0500 Subject: [PATCH 131/149] libwinpr-synch: add active/inactive linked list for timer queue --- winpr/libwinpr/synch/synch.h | 3 +- .../libwinpr/synch/test/TestSynchTimerQueue.c | 12 ++-- winpr/libwinpr/synch/timer.c | 62 ++++++++++++++----- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index d0f283f44..2b326ad9b 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -121,7 +121,8 @@ struct winpr_timer_queue struct sched_param param; BOOL bCancelled; - WINPR_TIMER_QUEUE_TIMER* head; + WINPR_TIMER_QUEUE_TIMER* activeHead; + WINPR_TIMER_QUEUE_TIMER* inactiveHead; }; typedef struct winpr_timer_queue WINPR_TIMER_QUEUE; diff --git a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c index 67a20db2a..db1261aa9 100644 --- a/winpr/libwinpr/synch/test/TestSynchTimerQueue.c +++ b/winpr/libwinpr/synch/test/TestSynchTimerQueue.c @@ -45,6 +45,8 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) { SetEvent(g_Event); } + + Sleep(50); } int TestSynchTimerQueue(int argc, char* argv[]) @@ -60,7 +62,7 @@ int TestSynchTimerQueue(int argc, char* argv[]) if (!hTimerQueue) { - printf("CreateTimerQueue failed (%d)\n", GetLastError()); + printf("CreateTimerQueue failed (%d)\n", (int) GetLastError()); return -1; } @@ -75,14 +77,14 @@ int TestSynchTimerQueue(int argc, char* argv[]) if (!CreateTimerQueueTimer(&hTimers[index], hTimerQueue, (WAITORTIMERCALLBACK) TimerRoutine, &apcData[index], apcData[index].DueTime, apcData[index].Period, 0)) { - printf("CreateTimerQueueTimer failed (%d)\n", GetLastError()); + printf("CreateTimerQueueTimer failed (%d)\n", (int) GetLastError()); return -1; } } if (WaitForSingleObject(g_Event, INFINITE) != WAIT_OBJECT_0) { - printf("WaitForSingleObject failed (%d)\n", GetLastError()); + printf("WaitForSingleObject failed (%d)\n", (int) GetLastError()); return -1; } @@ -90,14 +92,14 @@ int TestSynchTimerQueue(int argc, char* argv[]) { if (!DeleteTimerQueueTimer(hTimerQueue, hTimers[index], NULL)) { - printf("DeleteTimerQueueTimer failed (%d)\n", GetLastError()); + printf("DeleteTimerQueueTimer failed (%d)\n", (int) GetLastError()); return -1; } } if (!DeleteTimerQueue(hTimerQueue)) { - printf("DeleteTimerQueue failed (%d)\n", GetLastError()); + printf("DeleteTimerQueue failed (%d)\n", (int) GetLastError()); return -1; } diff --git a/winpr/libwinpr/synch/timer.c b/winpr/libwinpr/synch/timer.c index 511c1795b..fa72fbb40 100644 --- a/winpr/libwinpr/synch/timer.c +++ b/winpr/libwinpr/synch/timer.c @@ -418,12 +418,12 @@ int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) struct timespec CurrentTime; WINPR_TIMER_QUEUE_TIMER* node; - if (!timerQueue->head) + if (!timerQueue->activeHead) return 0; timespec_gettimeofday(&CurrentTime); - node = timerQueue->head; + node = timerQueue->activeHead; while (node) { @@ -432,15 +432,19 @@ int FireExpiredTimerQueueTimers(WINPR_TIMER_QUEUE* timerQueue) node->Callback(node->Parameter, TRUE); node->FireCount++; - timerQueue->head = node->next; + timerQueue->activeHead = node->next; node->next = NULL; if (node->Period) { timespec_add_ms(&(node->ExpirationTime), node->Period); - InsertTimerQueueTimer(&(timerQueue->head), node); - node = timerQueue->head; + InsertTimerQueueTimer(&(timerQueue->activeHead), node); + node = timerQueue->activeHead; + } + else + { + InsertTimerQueueTimer(&(timerQueue->inactiveHead), node); } } else @@ -462,14 +466,18 @@ static void* TimerQueueThread(void* arg) { pthread_mutex_lock(&(timerQueue->cond_mutex)); - if (!timerQueue->head) + timespec_gettimeofday(&timeout); + + if (!timerQueue->activeHead) { - timespec_gettimeofday(&timeout); timespec_add_ms(&timeout, 100); } else { - timespec_copy(&timeout, &(timerQueue->head->ExpirationTime)); + if (timespec_compare(&timeout, &(timerQueue->activeHead->ExpirationTime)) < 0) + { + timespec_copy(&timeout, &(timerQueue->activeHead->ExpirationTime)); + } } status = pthread_cond_timedwait(&(timerQueue->cond), &(timerQueue->cond_mutex), &timeout); @@ -513,7 +521,7 @@ HANDLE CreateTimerQueue(void) WINPR_HANDLE_SET_TYPE(timerQueue, HANDLE_TYPE_TIMER_QUEUE); handle = (HANDLE) timerQueue; - timerQueue->head = NULL; + timerQueue->activeHead = NULL; timerQueue->bCancelled = FALSE; StartTimerQueueThread(timerQueue); @@ -553,7 +561,21 @@ BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) { /* Cancel all timers and return immediately */ - node = timerQueue->head; + /* Move all active timers to the inactive timer list */ + + node = timerQueue->activeHead; + + while (node) + { + InsertTimerQueueTimer(&(timerQueue->inactiveHead), node); + node = node->next; + } + + timerQueue->activeHead = NULL; + + /* Once all timers are inactive, free them */ + + node = timerQueue->inactiveHead; while (node) { @@ -564,7 +586,7 @@ BOOL DeleteTimerQueueEx(HANDLE TimerQueue, HANDLE CompletionEvent) node = nextNode; } - timerQueue->head = NULL; + timerQueue->inactiveHead = NULL; } /* Delete timer queue */ @@ -592,9 +614,12 @@ BOOL DeleteTimerQueue(HANDLE TimerQueue) BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags) { + struct timespec CurrentTime; WINPR_TIMER_QUEUE* timerQueue; WINPR_TIMER_QUEUE_TIMER* timer; + timespec_gettimeofday(&CurrentTime); + timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; timer = (WINPR_TIMER_QUEUE_TIMER*) malloc(sizeof(WINPR_TIMER_QUEUE_TIMER)); @@ -604,7 +629,7 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WINPR_HANDLE_SET_TYPE(timer, HANDLE_TYPE_TIMER_QUEUE_TIMER); *((UINT_PTR*) phNewTimer) = (UINT_PTR) (HANDLE) timer; - timespec_gettimeofday(&(timer->StartTime)); + timespec_copy(&(timer->StartTime), &CurrentTime); timespec_add_ms(&(timer->StartTime), DueTime); timespec_copy(&(timer->ExpirationTime), &(timer->StartTime)); @@ -620,7 +645,7 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, pthread_mutex_lock(&(timerQueue->cond_mutex)); - InsertTimerQueueTimer(&(timerQueue->head), timer); + InsertTimerQueueTimer(&(timerQueue->activeHead), timer); pthread_cond_signal(&(timerQueue->cond)); pthread_mutex_unlock(&(timerQueue->cond_mutex)); @@ -630,27 +655,30 @@ BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, BOOL ChangeTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG Period) { + struct timespec CurrentTime; WINPR_TIMER_QUEUE* timerQueue; WINPR_TIMER_QUEUE_TIMER* timer; if (!TimerQueue || !Timer) return FALSE; + timespec_gettimeofday(&CurrentTime); + timerQueue = (WINPR_TIMER_QUEUE*) TimerQueue; timer = (WINPR_TIMER_QUEUE_TIMER*) Timer; pthread_mutex_lock(&(timerQueue->cond_mutex)); - RemoveTimerQueueTimer(&(timerQueue->head), timer); + RemoveTimerQueueTimer(&(timerQueue->activeHead), timer); timer->DueTime = DueTime; timer->Period = Period; - timespec_gettimeofday(&(timer->StartTime)); + timespec_copy(&(timer->StartTime), &CurrentTime); timespec_add_ms(&(timer->StartTime), DueTime); timespec_copy(&(timer->ExpirationTime), &(timer->StartTime)); - InsertTimerQueueTimer(&(timerQueue->head), timer); + InsertTimerQueueTimer(&(timerQueue->activeHead), timer); pthread_cond_signal(&(timerQueue->cond)); pthread_mutex_unlock(&(timerQueue->cond_mutex)); @@ -679,7 +707,7 @@ BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEve { /* Cancel timer and return immediately */ - RemoveTimerQueueTimer(&(timerQueue->head), timer); + RemoveTimerQueueTimer(&(timerQueue->activeHead), timer); } pthread_cond_signal(&(timerQueue->cond)); From 4d289ee0658d81cb33504db67f783c5165adc1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 28 Jan 2014 16:23:42 -0500 Subject: [PATCH 132/149] mfreerdp: fix hungarian keyboard '0' and 'i' key inversion --- client/Mac/MRDPView.m | 82 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/client/Mac/MRDPView.m b/client/Mac/MRDPView.m index da2992629..2d496812b 100644 --- a/client/Mac/MRDPView.m +++ b/client/Mac/MRDPView.m @@ -498,32 +498,84 @@ DWORD mac_client_thread(void* param) mf_scale_mouse_event(context, instance->input, PTR_FLAGS_MOVE, x, y); } +DWORD fixKeyCode(DWORD keyCode, unichar keyChar) +{ + /** + * In 99% of cases, the given key code is truly keyboard independent. + * This function handles the remaining 1% of edge cases. + * + * Hungarian Keyboard: This is 'QWERTZ' and not 'QWERTY'. + * The '0' key is on the left of the '1' key, where '~' is on a US keyboard. + * A special 'i' letter key with acute is found on the right of the left shift key. + * On the hungarian keyboard, the 'i' key is at the left of the 'Y' key + * Some international keyboards have a corresponding key which would be at + * the left of the 'Z' key when using a QWERTY layout. + * + * The Apple Hungarian keyboard sends inverted key codes for the '0' and 'i' keys. + * When using the US keyboard layout, key codes are left as-is (inverted). + * When using the Hungarian keyboard layout, key codes are swapped (non-inverted). + * This means that when using the Hungarian keyboard layout with a US keyboard, + * the keys corresponding to '0' and 'i' will effectively be inverted. + * + * To fix the '0' and 'i' key inversion, we use the corresponding output character + * provided by OS X and check for a character to key code mismatch: for instance, + * when the output character is '0' for the key code corresponding to the 'i' key. + */ + + switch (keyChar) + { + case '0': + case 0x00A7: /* section sign */ + if (keyCode == APPLE_VK_ISO_Section) + keyCode = APPLE_VK_ANSI_Grave; + break; + + case 0x00ED: /* latin small letter i with acute */ + case 0x00CD: /* latin capital letter i with acute */ + if (keyCode == APPLE_VK_ANSI_Grave) + keyCode = APPLE_VK_ISO_Section; + break; + } + + return keyCode; +} + /** ********************************************************************* * called when a key is pressed ***********************************************************************/ - (void) keyDown:(NSEvent *) event { - int key; + DWORD keyCode; DWORD keyFlags; DWORD vkcode; DWORD scancode; + unichar keyChar; + NSString* characters; if (!is_connected) return; keyFlags = KBD_FLAGS_DOWN; - key = [event keyCode] + 8; + keyCode = [event keyCode]; - vkcode = GetVirtualKeyCodeFromKeycode(key, KEYCODE_TYPE_APPLE); + characters = [event charactersIgnoringModifiers]; + + if ([characters length] > 0) + { + keyChar = [characters characterAtIndex:0]; + keyCode = fixKeyCode(keyCode, keyChar); + } + + vkcode = GetVirtualKeyCodeFromKeycode(keyCode + 8, KEYCODE_TYPE_APPLE); scancode = GetVirtualScanCodeFromVirtualKeyCode(vkcode, 4); keyFlags |= (scancode & KBDEXT) ? KBDEXT : 0; scancode &= 0xFF; vkcode &= 0xFF; - + #if 0 - fprintf(stderr, "keyDown: key: 0x%04X scancode: 0x%04X vkcode: 0x%04X keyFlags: %d name: %s\n", - key - 8, scancode, vkcode, keyFlags, GetVirtualKeyName(vkcode)); + fprintf(stderr, "keyDown: keyCode: 0x%04X scancode: 0x%04X vkcode: 0x%04X keyFlags: %d name: %s\n", + keyCode, scancode, vkcode, keyFlags, GetVirtualKeyName(vkcode)); #endif freerdp_input_send_keyboard_event(instance->input, keyFlags, scancode); @@ -535,18 +587,28 @@ DWORD mac_client_thread(void* param) - (void) keyUp:(NSEvent *) event { - int key; + DWORD keyCode; DWORD keyFlags; DWORD vkcode; DWORD scancode; + unichar keyChar; + NSString* characters; if (!is_connected) return; - key = [event keyCode] + 8; keyFlags = KBD_FLAGS_RELEASE; + keyCode = [event keyCode]; + + characters = [event charactersIgnoringModifiers]; + + if ([characters length] > 0) + { + keyChar = [characters characterAtIndex:0]; + keyCode = fixKeyCode(keyCode, keyChar); + } - vkcode = GetVirtualKeyCodeFromKeycode(key, KEYCODE_TYPE_APPLE); + vkcode = GetVirtualKeyCodeFromKeycode(keyCode + 8, KEYCODE_TYPE_APPLE); scancode = GetVirtualScanCodeFromVirtualKeyCode(vkcode, 4); keyFlags |= (scancode & KBDEXT) ? KBDEXT : 0; scancode &= 0xFF; @@ -554,7 +616,7 @@ DWORD mac_client_thread(void* param) #if 0 fprintf(stderr, "keyUp: key: 0x%04X scancode: 0x%04X vkcode: 0x%04X keyFlags: %d name: %s\n", - key - 8, scancode, vkcode, keyFlags, GetVirtualKeyName(vkcode)); + keyCode, scancode, vkcode, keyFlags, GetVirtualKeyName(vkcode)); #endif freerdp_input_send_keyboard_event(instance->input, keyFlags, scancode); From 97dd904a36e5e7574c43276737a96de7db3e7934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 28 Jan 2014 21:46:47 -0500 Subject: [PATCH 133/149] libfreerdp-gdi: fix invalidation of region with line drawing --- libfreerdp/gdi/gdi.c | 3 ++- libfreerdp/gdi/include/line.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libfreerdp/gdi/gdi.c b/libfreerdp/gdi/gdi.c index baa503edb..03039aa92 100644 --- a/libfreerdp/gdi/gdi.c +++ b/libfreerdp/gdi/gdi.c @@ -313,7 +313,8 @@ static const UINT32 rop3_code_table[] = }; /* Hatch Patterns as monochrome data */ -static BYTE GDI_BS_HACHTED_PATTERNS[] = { +static BYTE GDI_BS_HACHTED_PATTERNS[] = +{ 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, /* HS_HORIZONTAL */ 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, /* HS_VERTICAL */ 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F, /* HS_FDIAGONAL */ diff --git a/libfreerdp/gdi/include/line.c b/libfreerdp/gdi/include/line.c index d8a56b7ea..8088d797d 100644 --- a/libfreerdp/gdi/include/line.c +++ b/libfreerdp/gdi/include/line.c @@ -70,6 +70,8 @@ int LINE_TO(HGDI_DC hdc, int nXEnd, int nYEnd) by1 = MAX(by1, 0); bx2 = MIN(bx2, bmp->width - 1); by2 = MIN(by2, bmp->height - 1); + + gdi_InvalidateRegion(hdc, bx1, by1, bx2 - bx1 + 1, by2 - by1 + 1); pen = GDI_GET_PEN_COLOR(hdc->pen); From c73c558d1fc11e83d35d8cfb34036fbd0bf51582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 28 Jan 2014 22:41:11 -0500 Subject: [PATCH 134/149] winpr-hash: fix building against openssl in static mode --- winpr/tools/hash/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/winpr/tools/hash/CMakeLists.txt b/winpr/tools/hash/CMakeLists.txt index 3ede0e0b9..e9b307173 100644 --- a/winpr/tools/hash/CMakeLists.txt +++ b/winpr/tools/hash/CMakeLists.txt @@ -18,11 +18,18 @@ set(MODULE_NAME "winpr-hash") set(MODULE_PREFIX "WINPR_TOOLS_HASH") +include_directories(${ZLIB_INCLUDE_DIRS}) +include_directories(${OPENSSL_INCLUDE_DIR}) + set(${MODULE_PREFIX}_SRCS hash.c) add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) +set(${MODULE_PREFIX}_LIBS + ${ZLIB_LIBRARIES} + ${OPENSSL_LIBRARIES}) + set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MONOLITHIC ${MONOLITHIC_BUILD} MODULE winpr From 839bcb42ae8cedebc4819f8050f88ea32edb880c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 29 Jan 2014 15:27:43 -0500 Subject: [PATCH 135/149] wfreerdp: fix build against static openssl library --- cmake/FindOpenSSL.cmake | 2 +- winpr/tools/makecert/makecert.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/cmake/FindOpenSSL.cmake b/cmake/FindOpenSSL.cmake index 9ae015828..450a6329d 100644 --- a/cmake/FindOpenSSL.cmake +++ b/cmake/FindOpenSSL.cmake @@ -83,7 +83,7 @@ IF(WIN32 AND NOT CYGWIN) # * MTd for static-debug # Implementation details: - # We are using the libraries located in the VC subdir instead of the parent directory eventhough : + # We are using the libraries located in the VC subdir instead of the parent directory even though : # libeay32MD.lib is identical to ../libeay32.lib, and # ssleay32MD.lib is identical to ../ssleay32.lib diff --git a/winpr/tools/makecert/makecert.c b/winpr/tools/makecert/makecert.c index 5017b1d50..8430d851c 100644 --- a/winpr/tools/makecert/makecert.c +++ b/winpr/tools/makecert/makecert.c @@ -31,10 +31,6 @@ #include #include -#ifdef _WIN32 -#include -#endif - #include "makecert.h" struct _MAKECERT_CONTEXT From 02c9d07bcf69177507879c8260c443f590392360 Mon Sep 17 00:00:00 2001 From: Mike McDonald Date: Wed, 29 Jan 2014 22:53:32 -0500 Subject: [PATCH 136/149] Fixes to process new command line options (autodetect, heartbeat, multitransport), join the MCS message channel and process auto-detect PDUs during the connection sequence. --- client/common/cmdline.c | 18 ++++++++++++ libfreerdp/core/autodetect.c | 9 ++++-- libfreerdp/core/capabilities.c | 9 ++++-- libfreerdp/core/connection.c | 52 ++++++++++++++++++++++++++++++++++ libfreerdp/core/connection.h | 1 + libfreerdp/core/rdp.c | 13 +++++++++ 6 files changed, 98 insertions(+), 4 deletions(-) diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 47a43011f..c506eea57 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -148,6 +148,8 @@ COMMAND_LINE_ARGUMENT_A args[] = { "auth-only", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Authenticate only." }, { "reconnect-cookie", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Pass base64 reconnect cookie to the connection" }, { "print-reconnect-cookie", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Print base64 reconnect cookie after connecting" }, + { "heartbeat", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Support heartbeat PDUs" }, + { "multitransport", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Support multitransport protocol" }, { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } }; @@ -719,6 +721,15 @@ int freerdp_client_command_line_post_filter(void* context, COMMAND_LINE_ARGUMENT freerdp_client_add_dynamic_channel(settings, count, p); } } + CommandLineSwitchCase(arg, "heartbeat") + { + settings->SupportHeartbeatPdu = TRUE; + } + CommandLineSwitchCase(arg, "multitransport") + { + settings->SupportMultitransport = TRUE; + settings->MultitransportFlags = (TRANSPORT_TYPE_UDP_FECR | TRANSPORT_TYPE_UDP_FECL | TRANSPORT_TYPE_UDP_PREFERRED); + } CommandLineSwitchEnd(arg) @@ -1796,6 +1807,13 @@ int freerdp_client_load_addins(rdpChannels* channels, rdpSettings* settings) settings->AudioCapture = TRUE; } + if (settings->NetworkAutoDetect || + settings->SupportHeartbeatPdu || + settings->SupportMultitransport) + { + settings->DeviceRedirection = TRUE; /* these RDP 8 features require rdpdr to be registered */ + } + if (settings->RedirectDrives) { settings->DeviceRedirection = TRUE; diff --git a/libfreerdp/core/autodetect.c b/libfreerdp/core/autodetect.c index f0cc6bccc..a8077ef4f 100644 --- a/libfreerdp/core/autodetect.c +++ b/libfreerdp/core/autodetect.c @@ -21,7 +21,7 @@ #include "config.h" #endif -#define WITH_DEBUG_AUTODETECT +//#define WITH_DEBUG_AUTODETECT #include "autodetect.h" @@ -111,7 +111,7 @@ static BOOL autodetect_recv_bandwidth_measure_start(rdpRdp* rdp, wStream* s, AUT if (autodetectReqPdu->headerLength != 0x06) return FALSE; - DEBUG_AUTODETECT("received Bandwidth Measure Start PDU"); + DEBUG_AUTODETECT("received Bandwidth Measure Start PDU - time=%lu", GetTickCount()); /* Initialize bandwidth measurement parameters */ rdp->autodetect->bandwidthMeasureStartTime = GetTickCount(); @@ -222,6 +222,11 @@ int rdp_recv_autodetect_packet(rdpRdp* rdp, wStream* s) Stream_Read_UINT16(s, autodetectReqPdu.sequenceNumber); /* sequenceNumber (2 bytes) */ Stream_Read_UINT16(s, autodetectReqPdu.requestType); /* requestType (2 bytes) */ + DEBUG_AUTODETECT( + "rdp_recv_autodetect_packet: headerLength=%u, headerTypeId=%u, sequenceNumber=%u, requestType=%04x", + autodetectReqPdu.headerLength, autodetectReqPdu.headerTypeId, + autodetectReqPdu.sequenceNumber, autodetectReqPdu.requestType); + if (autodetectReqPdu.headerTypeId != TYPE_ID_AUTODETECT_REQUEST) return -1; diff --git a/libfreerdp/core/capabilities.c b/libfreerdp/core/capabilities.c index 1a7dfd3b2..39bdd66d1 100644 --- a/libfreerdp/core/capabilities.c +++ b/libfreerdp/core/capabilities.c @@ -3389,8 +3389,13 @@ BOOL rdp_recv_get_active_header(rdpRdp* rdp, wStream* s, UINT16* pChannelId) if (*pChannelId != MCS_GLOBAL_CHANNEL_ID) { - fprintf(stderr, "expected MCS_GLOBAL_CHANNEL_ID %04x, got %04x\n", MCS_GLOBAL_CHANNEL_ID, *pChannelId); - return FALSE; + UINT16 mcsMessageChannelId = rdp->mcs->message_channel_id; + + if ((mcsMessageChannelId == 0) || (*pChannelId != mcsMessageChannelId)) + { + fprintf(stderr, "unexpected MCS channel id %04x received\n", *pChannelId); + return FALSE; + } } return TRUE; diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index d170a5479..bf2bae764 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -581,6 +581,31 @@ BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s) rdp->mcs->global_channel_joined = TRUE; + if (rdp->mcs->message_channel_id != 0) + { + if (!mcs_send_channel_join_request(rdp->mcs, rdp->mcs->message_channel_id)) + return FALSE; + + all_joined = FALSE; + } + else + { + if (rdp->settings->ChannelCount > 0) + { + if (!mcs_send_channel_join_request(rdp->mcs, rdp->settings->ChannelDefArray[0].ChannelId)) + return FALSE; + + all_joined = FALSE; + } + } + } + else if ((rdp->mcs->message_channel_id != 0) && !rdp->mcs->message_channel_joined) + { + if (channel_id != rdp->mcs->message_channel_id) + return FALSE; + + rdp->mcs->message_channel_joined = TRUE; + if (rdp->settings->ChannelCount > 0) { if (!mcs_send_channel_join_request(rdp->mcs, rdp->settings->ChannelDefArray[0].ChannelId)) @@ -626,6 +651,33 @@ BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s) return TRUE; } +BOOL rdp_client_connect_auto_detect(rdpRdp* rdp, wStream *s) +{ + BYTE* mark; + UINT16 length; + UINT16 channelId; + + /* If the MCS message channel has been joined... */ + if (rdp->mcs->message_channel_id != 0) + { + /* Process any MCS message channel PDUs. */ + Stream_GetPointer(s, mark); + + if (rdp_read_header(rdp, s, &length, &channelId)) + { + if (channelId == rdp->mcs->message_channel_id) + { + if (rdp_recv_message_channel_pdu(rdp, s) == 0) + return TRUE; + } + } + + Stream_SetPointer(s, mark); + } + + return FALSE; +} + int rdp_client_connect_license(rdpRdp* rdp, wStream* s) { int status; diff --git a/libfreerdp/core/connection.h b/libfreerdp/core/connection.h index 65708ae75..ebc39b19f 100644 --- a/libfreerdp/core/connection.h +++ b/libfreerdp/core/connection.h @@ -53,6 +53,7 @@ BOOL rdp_client_reconnect(rdpRdp* rdp); BOOL rdp_client_connect_mcs_connect_response(rdpRdp* rdp, wStream* s); BOOL rdp_client_connect_mcs_attach_user_confirm(rdpRdp* rdp, wStream* s); BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s); +BOOL rdp_client_connect_auto_detect(rdpRdp* rdp, wStream* s); int rdp_client_connect_license(rdpRdp* rdp, wStream* s); int rdp_client_connect_demand_active(rdpRdp* rdp, wStream* s); int rdp_client_connect_finalize(rdpRdp* rdp); diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 9a7ee0305..a874ff183 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -1103,6 +1103,19 @@ static int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) int status = 0; rdpRdp* rdp = (rdpRdp*) extra; + /* + * At any point in the connection sequence between when all + * MCS channels have been joined and when the RDP connection + * enters the active state, an auto-detect PDU can be received + * on the MCS message channel. + */ + if ((rdp->state > CONNECTION_STATE_MCS_CHANNEL_JOIN) && + (rdp->state < CONNECTION_STATE_ACTIVE)) + { + if (rdp_client_connect_auto_detect(rdp, s)) + return 0; + } + switch (rdp->state) { case CONNECTION_STATE_NEGO: From aca384992f5ba3c2e4a7cdbf5ce4049d9f0cc45a Mon Sep 17 00:00:00 2001 From: vworkspace Date: Fri, 31 Jan 2014 13:52:37 -0500 Subject: [PATCH 137/149] Increased size of stream on GCC conference create request from 512 to 1024. For large numbers of static virtual channels, the stream was being overflowed and was causing crashes. --- libfreerdp/core/mcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfreerdp/core/mcs.c b/libfreerdp/core/mcs.c index eae664bf4..1fd38d86e 100644 --- a/libfreerdp/core/mcs.c +++ b/libfreerdp/core/mcs.c @@ -464,7 +464,7 @@ BOOL mcs_send_connect_initial(rdpMcs* mcs) client_data = Stream_New(NULL, 512); gcc_write_client_data_blocks(client_data, mcs->transport->settings); - gcc_CCrq = Stream_New(NULL, 512); + gcc_CCrq = Stream_New(NULL, 1024); gcc_write_conference_create_request(gcc_CCrq, client_data); length = Stream_GetPosition(gcc_CCrq) + 7; From 80449cd2c584eb4dff5644cd102249609c62e1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 31 Jan 2014 18:27:58 -0500 Subject: [PATCH 138/149] libwinpr-memory: start stubbing --- winpr/include/winpr/crt.h | 21 ++- winpr/include/winpr/file.h | 14 ++ winpr/include/winpr/memory.h | 41 ++++-- winpr/include/winpr/security.h | 32 +++++ winpr/include/winpr/wtypes.h | 2 + winpr/libwinpr/memory/CMakeLists.txt | 51 +++++++ winpr/libwinpr/memory/ModuleOptions.cmake | 9 ++ winpr/libwinpr/memory/memory.c | 126 ++++++++++++++++++ winpr/libwinpr/memory/memory.h | 34 +++++ winpr/libwinpr/memory/module.def | 3 + winpr/libwinpr/memory/test/.gitignore | 2 + winpr/libwinpr/memory/test/CMakeLists.txt | 30 +++++ .../memory/test/TestMemoryCreateFileMapping.c | 9 ++ winpr/libwinpr/security/security.c | 92 ++++++++++--- .../utils/collections/ListDictionary.c | 3 +- 15 files changed, 440 insertions(+), 29 deletions(-) create mode 100644 winpr/libwinpr/memory/CMakeLists.txt create mode 100644 winpr/libwinpr/memory/ModuleOptions.cmake create mode 100644 winpr/libwinpr/memory/memory.c create mode 100644 winpr/libwinpr/memory/memory.h create mode 100644 winpr/libwinpr/memory/module.def create mode 100644 winpr/libwinpr/memory/test/.gitignore create mode 100644 winpr/libwinpr/memory/test/CMakeLists.txt create mode 100644 winpr/libwinpr/memory/test/TestMemoryCreateFileMapping.c diff --git a/winpr/include/winpr/crt.h b/winpr/include/winpr/crt.h index 70ea058e5..694544a43 100644 --- a/winpr/include/winpr/crt.h +++ b/winpr/include/winpr/crt.h @@ -27,12 +27,27 @@ #include #include -#include - -/* Data Alignment */ +#include #ifndef _WIN32 +#define CopyMemory(Destination, Source, Length) memcpy((Destination), (Source), (Length)) +#define MoveMemory(Destination, Source, Length) memmove((Destination), (Source), (Length)) +#define FillMemory(Destination, Length, Fill) memset((Destination), (Fill), (Length)) +#define ZeroMemory(Destination, Length) memset((Destination), 0, (Length)) + +#ifdef __cplusplus +extern "C" { +#endif + +WINPR_API PVOID SecureZeroMemory(PVOID ptr, SIZE_T cnt); + +#ifdef __cplusplus +} +#endif + +/* Data Alignment */ + #ifndef _ERRNO_T_DEFINED #define _ERRNO_T_DEFINED typedef int errno_t; diff --git a/winpr/include/winpr/file.h b/winpr/include/winpr/file.h index 12208dae4..9570ec380 100644 --- a/winpr/include/winpr/file.h +++ b/winpr/include/winpr/file.h @@ -102,6 +102,20 @@ #define FILE_FLAG_OPEN_NO_RECALL 0x00100000 #define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 +#define SECTION_MAP_EXECUTE_EXPLICIT 0x00020 +#define SECTION_EXTEND_SIZE 0x00010 +#define SECTION_MAP_READ 0x00004 +#define SECTION_MAP_WRITE 0x00002 +#define SECTION_QUERY 0x00001 +#define SECTION_MAP_EXECUTE 0x00008 +#define SECTION_ALL_ACCESS 0xF001F + +#define FILE_MAP_COPY SECTION_QUERY +#define FILE_MAP_WRITE SECTION_MAP_WRITE +#define FILE_MAP_READ SECTION_MAP_READ +#define FILE_MAP_ALL_ACCESS SECTION_ALL_ACCESS +#define FILE_MAP_EXECUTE SECTION_MAP_EXECUTE_EXPLICIT + #define CREATE_NEW 1 #define CREATE_ALWAYS 2 #define OPEN_EXISTING 3 diff --git a/winpr/include/winpr/memory.h b/winpr/include/winpr/memory.h index 3a99685a8..1f64342d8 100644 --- a/winpr/include/winpr/memory.h +++ b/winpr/include/winpr/memory.h @@ -17,27 +17,48 @@ * limitations under the License. */ -#ifndef WINPR_CRT_MEMORY_H -#define WINPR_CRT_MEMORY_H +#ifndef WINPR_MEMORY_H +#define WINPR_MEMORY_H #include #include #include + #include #include +#include +#include + #ifndef _WIN32 -#define CopyMemory(Destination, Source, Length) memcpy((Destination), (Source), (Length)) -#define MoveMemory(Destination, Source, Length) memmove((Destination), (Source), (Length)) -#define FillMemory(Destination, Length, Fill) memset((Destination), (Fill), (Length)) -#define ZeroMemory(Destination, Length) memset((Destination), 0, (Length)) +#ifdef __cplusplus +extern "C" { +#endif -WINPR_API PVOID SecureZeroMemory(PVOID ptr, SIZE_T cnt); +WINPR_API HANDLE CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName); +WINPR_API HANDLE CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName); + +WINPR_API HANDLE OpenFileMappingA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName); +WINPR_API HANDLE OpenFileMappingW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName); + +WINPR_API LPVOID MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap); + +WINPR_API LPVOID MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap, LPVOID lpBaseAddress); + +WINPR_API BOOL FlushViewOfFile(LPCVOID lpBaseAddress, SIZE_T dwNumberOfBytesToFlush); + +WINPR_API BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); + +#ifdef __cplusplus +} +#endif #endif -#include - -#endif /* WINPR_CRT_MEMORY_H */ +#endif /* WINPR_MEMORY_H */ diff --git a/winpr/include/winpr/security.h b/winpr/include/winpr/security.h index 4ad7d4d4f..f36099f89 100644 --- a/winpr/include/winpr/security.h +++ b/winpr/include/winpr/security.h @@ -371,6 +371,38 @@ typedef struct _TOKEN_APPCONTAINER_INFORMATION PSID TokenAppContainer; } TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION; +#ifdef __cplusplus +extern "C" { +#endif + +WINPR_API BOOL InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD dwRevision); +WINPR_API DWORD GetSecurityDescriptorLength(PSECURITY_DESCRIPTOR pSecurityDescriptor); +WINPR_API BOOL IsValidSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor); + +WINPR_API BOOL GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSECURITY_DESCRIPTOR_CONTROL pControl, LPDWORD lpdwRevision); +WINPR_API BOOL SetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet); + +WINPR_API BOOL GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL lpbDaclPresent, PACL* pDacl, LPBOOL lpbDaclDefaulted); +WINPR_API BOOL SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, BOOL bDaclPresent, PACL pDacl, BOOL bDaclDefaulted); + +WINPR_API BOOL GetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID* pGroup, LPBOOL lpbGroupDefaulted); +WINPR_API BOOL SetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pGroup, BOOL bGroupDefaulted); + +WINPR_API BOOL GetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID* pOwner, LPBOOL lpbOwnerDefaulted); +WINPR_API BOOL SetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pOwner, BOOL bOwnerDefaulted); + +WINPR_API DWORD GetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor, PUCHAR RMControl); +WINPR_API DWORD SetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor, PUCHAR RMControl); + +WINPR_API BOOL GetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL lpbSaclPresent, PACL* pSacl, LPBOOL lpbSaclDefaulted); +WINPR_API BOOL SetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, BOOL bSaclPresent, PACL pSacl, BOOL bSaclDefaulted); + +#ifdef __cplusplus +} +#endif + #endif #endif /* WINPR_SECURITY_H */ diff --git a/winpr/include/winpr/wtypes.h b/winpr/include/winpr/wtypes.h index 83458f531..f3f1ce64d 100644 --- a/winpr/include/winpr/wtypes.h +++ b/winpr/include/winpr/wtypes.h @@ -268,6 +268,8 @@ typedef struct _SECURITY_DESCRIPTOR PACL Dacl; } SECURITY_DESCRIPTOR, *PSECURITY_DESCRIPTOR; +typedef WORD SECURITY_DESCRIPTOR_CONTROL, *PSECURITY_DESCRIPTOR_CONTROL; + typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; diff --git a/winpr/libwinpr/memory/CMakeLists.txt b/winpr/libwinpr/memory/CMakeLists.txt new file mode 100644 index 000000000..4c7e8e8ac --- /dev/null +++ b/winpr/libwinpr/memory/CMakeLists.txt @@ -0,0 +1,51 @@ +# WinPR: Windows Portable Runtime +# libwinpr-memory cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(MODULE_NAME "winpr-memory") +set(MODULE_PREFIX "WINPR_MEMORY") + +set(${MODULE_PREFIX}_SRCS + memory.c + memory.h) + +if(MSVC AND (NOT MONOLITHIC_BUILD)) + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} module.def) +endif() + +add_complex_library(MODULE ${MODULE_NAME} TYPE "OBJECT" + MONOLITHIC ${MONOLITHIC_BUILD} + SOURCES ${${MODULE_PREFIX}_SRCS}) + +set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION_FULL} SOVERSION ${WINPR_VERSION} PREFIX "lib") + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL + MODULE winpr + MODULES winpr-crt) + +if(MONOLITHIC_BUILD) + +else() + target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT WinPRTargets) +endif() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR") + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/winpr/libwinpr/memory/ModuleOptions.cmake b/winpr/libwinpr/memory/ModuleOptions.cmake new file mode 100644 index 000000000..704ddca19 --- /dev/null +++ b/winpr/libwinpr/memory/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "1") +set(MINWIN_MINOR_VERSION "2") +set(MINWIN_SHORT_NAME "memory") +set(MINWIN_LONG_NAME "Memory Functions") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/memory/memory.c b/winpr/libwinpr/memory/memory.c new file mode 100644 index 000000000..fc05a57a4 --- /dev/null +++ b/winpr/libwinpr/memory/memory.c @@ -0,0 +1,126 @@ +/** + * WinPR: Windows Portable Runtime + * Memory Functions + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +/** + * api-ms-win-core-memory-l1-1-2.dll: + * + * AllocateUserPhysicalPages + * AllocateUserPhysicalPagesNuma + * CreateFileMappingFromApp + * CreateFileMappingNumaW + * CreateFileMappingW + * CreateMemoryResourceNotification + * FlushViewOfFile + * FreeUserPhysicalPages + * GetLargePageMinimum + * GetMemoryErrorHandlingCapabilities + * GetProcessWorkingSetSizeEx + * GetSystemFileCacheSize + * GetWriteWatch + * MapUserPhysicalPages + * MapViewOfFile + * MapViewOfFileEx + * MapViewOfFileFromApp + * OpenFileMappingW + * PrefetchVirtualMemory + * QueryMemoryResourceNotification + * ReadProcessMemory + * RegisterBadMemoryNotification + * ResetWriteWatch + * SetProcessWorkingSetSizeEx + * SetSystemFileCacheSize + * UnmapViewOfFile + * UnmapViewOfFileEx + * UnregisterBadMemoryNotification + * VirtualAlloc + * VirtualAllocEx + * VirtualAllocExNuma + * VirtualFree + * VirtualFreeEx + * VirtualLock + * VirtualProtect + * VirtualProtectEx + * VirtualQuery + * VirtualQueryEx + * VirtualUnlock + * WriteProcessMemory + */ + +#ifndef _WIN32 + +#include "memory.h" + +HANDLE CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName) +{ + return NULL; +} + +HANDLE CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName) +{ + return NULL; +} + +HANDLE OpenFileMappingA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName) +{ + return NULL; +} + +HANDLE OpenFileMappingW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName) +{ + return NULL; +} + +LPVOID MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap) +{ + return NULL; +} + +LPVOID MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap, LPVOID lpBaseAddress) +{ + return NULL; +} + +BOOL FlushViewOfFile(LPCVOID lpBaseAddress, SIZE_T dwNumberOfBytesToFlush) +{ + return TRUE; +} + +BOOL UnmapViewOfFile(LPCVOID lpBaseAddress) +{ + return TRUE; +} + +#endif + diff --git a/winpr/libwinpr/memory/memory.h b/winpr/libwinpr/memory/memory.h new file mode 100644 index 000000000..8071088f4 --- /dev/null +++ b/winpr/libwinpr/memory/memory.h @@ -0,0 +1,34 @@ +/** + * WinPR: Windows Portable Runtime + * Memory Functions + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_MEMORY_PRIVATE_H +#define WINPR_MEMORY_PRIVATE_H + +#ifndef _WIN32 + +#include +#include + + + +#endif + +#endif /* WINPR_MEMORY_PRIVATE_H */ + + diff --git a/winpr/libwinpr/memory/module.def b/winpr/libwinpr/memory/module.def new file mode 100644 index 000000000..14a9402ee --- /dev/null +++ b/winpr/libwinpr/memory/module.def @@ -0,0 +1,3 @@ +LIBRARY "libwinpr-memory" +EXPORTS + diff --git a/winpr/libwinpr/memory/test/.gitignore b/winpr/libwinpr/memory/test/.gitignore new file mode 100644 index 000000000..34af35fe4 --- /dev/null +++ b/winpr/libwinpr/memory/test/.gitignore @@ -0,0 +1,2 @@ +TestMemory +TestMemory.c diff --git a/winpr/libwinpr/memory/test/CMakeLists.txt b/winpr/libwinpr/memory/test/CMakeLists.txt new file mode 100644 index 000000000..b143d3f82 --- /dev/null +++ b/winpr/libwinpr/memory/test/CMakeLists.txt @@ -0,0 +1,30 @@ + +set(MODULE_NAME "TestMemory") +set(MODULE_PREFIX "TEST_MEMORY") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestMemoryCreateFileMapping.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE winpr + MODULES winpr-crt) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/memory/test/TestMemoryCreateFileMapping.c b/winpr/libwinpr/memory/test/TestMemoryCreateFileMapping.c new file mode 100644 index 000000000..918b4c468 --- /dev/null +++ b/winpr/libwinpr/memory/test/TestMemoryCreateFileMapping.c @@ -0,0 +1,9 @@ + +#include +#include + +int TestMemoryCreateFileMapping(int argc, char* argv[]) +{ + return 0; +} + diff --git a/winpr/libwinpr/security/security.c b/winpr/libwinpr/security/security.c index 51250e78b..b7f39d281 100644 --- a/winpr/libwinpr/security/security.c +++ b/winpr/libwinpr/security/security.c @@ -85,13 +85,6 @@ * GetKernelObjectSecurity * GetLengthSid * GetPrivateObjectSecurity - * GetSecurityDescriptorControl - * GetSecurityDescriptorDacl - * GetSecurityDescriptorGroup - * GetSecurityDescriptorLength - * GetSecurityDescriptorOwner - * GetSecurityDescriptorRMControl - * GetSecurityDescriptorSacl * GetSidIdentifierAuthority * GetSidLengthRequired * GetSidSubAuthority @@ -102,11 +95,9 @@ * ImpersonateLoggedOnUser * ImpersonateSelf * InitializeAcl - * InitializeSecurityDescriptor * InitializeSid * IsTokenRestricted * IsValidAcl - * IsValidSecurityDescriptor * IsValidSid * IsWellKnownSid * MakeAbsoluteSD @@ -127,12 +118,6 @@ * SetPrivateObjectSecurity * SetPrivateObjectSecurityEx * SetSecurityAccessMask - * SetSecurityDescriptorControl - * SetSecurityDescriptorDacl - * SetSecurityDescriptorGroup - * SetSecurityDescriptorOwner - * SetSecurityDescriptorRMControl - * SetSecurityDescriptorSacl * SetTokenInformation */ @@ -140,5 +125,82 @@ #include "security.h" +BOOL InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD dwRevision) +{ + return TRUE; +} + +DWORD GetSecurityDescriptorLength(PSECURITY_DESCRIPTOR pSecurityDescriptor) +{ + return 0; +} + +BOOL IsValidSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor) +{ + return TRUE; +} + +BOOL GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSECURITY_DESCRIPTOR_CONTROL pControl, LPDWORD lpdwRevision) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor, + SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet) +{ + return TRUE; +} + +BOOL GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL lpbDaclPresent, PACL* pDacl, LPBOOL lpbDaclDefaulted) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, BOOL bDaclPresent, PACL pDacl, BOOL bDaclDefaulted) +{ + return TRUE; +} + +BOOL GetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID* pGroup, LPBOOL lpbGroupDefaulted) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pGroup, BOOL bGroupDefaulted) +{ + return TRUE; +} + +BOOL GetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID* pOwner, LPBOOL lpbOwnerDefaulted) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pOwner, BOOL bOwnerDefaulted) +{ + return TRUE; +} + +DWORD GetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor, PUCHAR RMControl) +{ + return 0; +} + +DWORD SetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor, PUCHAR RMControl) +{ + return 0; +} + +BOOL GetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL lpbSaclPresent, PACL* pSacl, LPBOOL lpbSaclDefaulted) +{ + return TRUE; +} + +BOOL SetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, BOOL bSaclPresent, PACL pSacl, BOOL bSaclDefaulted) +{ + return TRUE; +} + #endif diff --git a/winpr/libwinpr/utils/collections/ListDictionary.c b/winpr/libwinpr/utils/collections/ListDictionary.c index bef4f0757..586e20a7a 100644 --- a/winpr/libwinpr/utils/collections/ListDictionary.c +++ b/winpr/libwinpr/utils/collections/ListDictionary.c @@ -21,8 +21,9 @@ #include "config.h" #endif +#include + #include -#include /** * C equivalent of the C# ListDictionary Class: From 6365880f4d06fee9d3f13325b751d60a687d8c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 31 Jan 2014 20:48:31 -0500 Subject: [PATCH 139/149] libwinpr-memory: add missing definitions --- winpr/include/winpr/file.h | 36 ++++++++++++++++++++++++++++++++++ winpr/include/winpr/memory.h | 8 ++++++++ winpr/libwinpr/memory/memory.c | 5 +++++ 3 files changed, 49 insertions(+) diff --git a/winpr/include/winpr/file.h b/winpr/include/winpr/file.h index 9570ec380..69e4eceed 100644 --- a/winpr/include/winpr/file.h +++ b/winpr/include/winpr/file.h @@ -102,6 +102,42 @@ #define FILE_FLAG_OPEN_NO_RECALL 0x00100000 #define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000 +#define PAGE_NOACCESS 0x00000001 +#define PAGE_READONLY 0x00000002 +#define PAGE_READWRITE 0x00000004 +#define PAGE_WRITECOPY 0x00000008 +#define PAGE_EXECUTE 0x00000010 +#define PAGE_EXECUTE_READ 0x00000020 +#define PAGE_EXECUTE_READWRITE 0x00000040 +#define PAGE_EXECUTE_WRITECOPY 0x00000080 +#define PAGE_GUARD 0x00000100 +#define PAGE_NOCACHE 0x00000200 +#define PAGE_WRITECOMBINE 0x00000400 + +#define MEM_COMMIT 0x00001000 +#define MEM_RESERVE 0x00002000 +#define MEM_DECOMMIT 0x00004000 +#define MEM_RELEASE 0x00008000 +#define MEM_FREE 0x00010000 +#define MEM_PRIVATE 0x00020000 +#define MEM_MAPPED 0x00040000 +#define MEM_RESET 0x00080000 +#define MEM_TOP_DOWN 0x00100000 +#define MEM_WRITE_WATCH 0x00200000 +#define MEM_PHYSICAL 0x00400000 +#define MEM_4MB_PAGES 0x80000000 +#define MEM_IMAGE SEC_IMAGE + +#define SEC_NO_CHANGE 0x00400000 +#define SEC_FILE 0x00800000 +#define SEC_IMAGE 0x01000000 +#define SEC_VLM 0x02000000 +#define SEC_RESERVE 0x04000000 +#define SEC_COMMIT 0x08000000 +#define SEC_NOCACHE 0x10000000 +#define SEC_WRITECOMBINE 0x40000000 +#define SEC_LARGE_PAGES 0x80000000 + #define SECTION_MAP_EXECUTE_EXPLICIT 0x00020 #define SECTION_EXTEND_SIZE 0x00010 #define SECTION_MAP_READ 0x00004 diff --git a/winpr/include/winpr/memory.h b/winpr/include/winpr/memory.h index 1f64342d8..0721734a1 100644 --- a/winpr/include/winpr/memory.h +++ b/winpr/include/winpr/memory.h @@ -58,6 +58,14 @@ WINPR_API BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); } #endif +#ifdef UNICODE +#define CreateFileMapping CreateFileMappingW +#define OpenFileMapping OpenFileMappingW +#else +#define CreateFileMapping CreateFileMappingA +#define OpenFileMapping OpenFileMappingA +#endif + #endif #endif /* WINPR_MEMORY_H */ diff --git a/winpr/libwinpr/memory/memory.c b/winpr/libwinpr/memory/memory.c index fc05a57a4..ec5cb854b 100644 --- a/winpr/libwinpr/memory/memory.c +++ b/winpr/libwinpr/memory/memory.c @@ -81,6 +81,11 @@ HANDLE CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName) { + if (hFile != INVALID_HANDLE_VALUE) + { + return NULL; /* not yet implemented */ + } + return NULL; } From 851ace73ff1adcd000ae45ea3a1d6c23068b686f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 1 Feb 2014 11:52:04 -0500 Subject: [PATCH 140/149] libwinpr-crypto: implement CryptProtectMemory/CryptUnprotectMemory --- winpr/include/winpr/crypto.h | 90 +++++++++++ winpr/libwinpr/credui/CMakeLists.txt | 5 + winpr/libwinpr/crypto/CMakeLists.txt | 12 ++ winpr/libwinpr/crypto/crypto.c | 142 +++++++++++++++++- winpr/libwinpr/crypto/crypto.h | 17 +++ winpr/libwinpr/crypto/test/CMakeLists.txt | 4 +- ...> TestCryptoCertEnumCertificatesInStore.c} | 2 +- .../crypto/test/TestCryptoProtectData.c | 11 ++ .../crypto/test/TestCryptoProtectMemory.c | 47 ++++++ 9 files changed, 322 insertions(+), 8 deletions(-) rename winpr/libwinpr/crypto/test/{TestCertEnumCertificatesInStore.c => TestCryptoCertEnumCertificatesInStore.c} (95%) create mode 100644 winpr/libwinpr/crypto/test/TestCryptoProtectData.c create mode 100644 winpr/libwinpr/crypto/test/TestCryptoProtectMemory.c diff --git a/winpr/include/winpr/crypto.h b/winpr/include/winpr/crypto.h index 27f1fd33f..67aa8d024 100644 --- a/winpr/include/winpr/crypto.h +++ b/winpr/include/winpr/crypto.h @@ -23,6 +23,8 @@ #include #include +#include + #ifdef _WIN32 #include @@ -492,6 +494,94 @@ DWORD CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType, #define CertGetNameString CertGetNameStringA #endif +/** + * Data Protection API (DPAPI) + */ + +#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 + +#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00000000 +#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x00000001 +#define CRYPTPROTECTMEMORY_SAME_LOGON 0x00000002 + +#define CRYPTPROTECT_PROMPT_ON_UNPROTECT 0x00000001 +#define CRYPTPROTECT_PROMPT_ON_PROTECT 0x00000002 +#define CRYPTPROTECT_PROMPT_RESERVED 0x00000004 +#define CRYPTPROTECT_PROMPT_STRONG 0x00000008 +#define CRYPTPROTECT_PROMPT_REQUIRE_STRONG 0x00000010 + +#define CRYPTPROTECT_UI_FORBIDDEN 0x1 +#define CRYPTPROTECT_LOCAL_MACHINE 0x4 +#define CRYPTPROTECT_CRED_SYNC 0x8 +#define CRYPTPROTECT_AUDIT 0x10 +#define CRYPTPROTECT_NO_RECOVERY 0x20 +#define CRYPTPROTECT_VERIFY_PROTECTION 0x40 +#define CRYPTPROTECT_CRED_REGENERATE 0x80 + +#define CRYPTPROTECT_FIRST_RESERVED_FLAGVAL 0x0FFFFFFF +#define CRYPTPROTECT_LAST_RESERVED_FLAGVAL 0xFFFFFFFF + +typedef struct _CRYPTPROTECT_PROMPTSTRUCT +{ + DWORD cbSize; + DWORD dwPromptFlags; + HWND hwndApp; + LPCWSTR szPrompt; +} CRYPTPROTECT_PROMPTSTRUCT, *PCRYPTPROTECT_PROMPTSTRUCT; + +#define CRYPTPROTECT_DEFAULT_PROVIDER { 0xdf9d8cd0, 0x1501, 0x11d1, { 0x8c, 0x7a, 0x00, 0xc0, 0x4f, 0xc2, 0x97, 0xeb } } + +#ifdef __cplusplus +extern "C" { +#endif + +WINPR_API BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags); +WINPR_API BOOL CryptUnprotectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags); + +WINPR_API BOOL CryptProtectData(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, DATA_BLOB* pDataOut); +WINPR_API BOOL CryptUnprotectData(DATA_BLOB* pDataIn, LPWSTR* ppszDataDescr, DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, DATA_BLOB* pDataOut); + +#ifdef __cplusplus +} +#endif + +#define CRYPT_STRING_BASE64HEADER 0x00000000 +#define CRYPT_STRING_BASE64 0x00000001 +#define CRYPT_STRING_BINARY 0x00000002 +#define CRYPT_STRING_BASE64REQUESTHEADER 0x00000003 +#define CRYPT_STRING_HEX 0x00000004 +#define CRYPT_STRING_HEXASCII 0x00000005 +#define CRYPT_STRING_BASE64_ANY 0x00000006 +#define CRYPT_STRING_ANY 0x00000007 +#define CRYPT_STRING_HEX_ANY 0x00000008 +#define CRYPT_STRING_BASE64X509CRLHEADER 0x00000009 +#define CRYPT_STRING_HEXADDR 0x0000000A +#define CRYPT_STRING_HEXASCIIADDR 0x0000000B +#define CRYPT_STRING_HEXRAW 0x0000000C + +#define CRYPT_STRING_HASHDATA 0x10000000 +#define CRYPT_STRING_STRICT 0x20000000 +#define CRYPT_STRING_NOCRLF 0x40000000 +#define CRYPT_STRING_NOCR 0x80000000 + +BOOL CryptStringToBinaryW(LPCWSTR pszString, DWORD cchString, DWORD dwFlags, BYTE* pbBinary, + DWORD* pcbBinary, DWORD* pdwSkip, DWORD* pdwFlags); +BOOL CryptStringToBinaryA(LPCSTR pszString, DWORD cchString, DWORD dwFlags, BYTE* pbBinary, + DWORD* pcbBinary, DWORD* pdwSkip, DWORD* pdwFlags); + +BOOL CryptBinaryToStringW(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, DWORD* pcchString); +BOOL CryptBinaryToStringA(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD* pcchString); + +#ifdef UNICODE +#define CryptStringToBinary CryptStringToBinaryW +#define CryptBinaryToString CryptBinaryToStringW +#else +#define CryptStringToBinary CryptStringToBinaryA +#define CryptBinaryToString CryptBinaryToStringA +#endif + #endif #endif /* WINPR_CRYPTO_H */ diff --git a/winpr/libwinpr/credui/CMakeLists.txt b/winpr/libwinpr/credui/CMakeLists.txt index 2f59aa252..de9e71b8e 100644 --- a/winpr/libwinpr/credui/CMakeLists.txt +++ b/winpr/libwinpr/credui/CMakeLists.txt @@ -35,6 +35,11 @@ if(WIN32) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} credui) endif() +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL + MODULE winpr + MODULES winpr-crt winpr-utils) + if(MONOLITHIC_BUILD) set(WINPR_LIBS ${WINPR_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE) else() diff --git a/winpr/libwinpr/crypto/CMakeLists.txt b/winpr/libwinpr/crypto/CMakeLists.txt index c1c5e822c..e63aefea4 100644 --- a/winpr/libwinpr/crypto/CMakeLists.txt +++ b/winpr/libwinpr/crypto/CMakeLists.txt @@ -30,9 +30,21 @@ endif() add_complex_library(MODULE ${MODULE_NAME} TYPE "OBJECT" MONOLITHIC ${MONOLITHIC_BUILD} SOURCES ${${MODULE_PREFIX}_SRCS}) + +include_directories(${ZLIB_INCLUDE_DIRS}) +include_directories(${OPENSSL_INCLUDE_DIR}) set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION_FULL} SOVERSION ${WINPR_VERSION} PREFIX "lib") +set(${MODULE_PREFIX}_LIBS + ${ZLIB_LIBRARIES} + ${OPENSSL_LIBRARIES}) + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL + MODULE winpr + MODULES winpr-crt winpr-utils) + if(WIN32) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} crypt32) endif() diff --git a/winpr/libwinpr/crypto/crypto.c b/winpr/libwinpr/crypto/crypto.c index 8556121e9..96a53d107 100644 --- a/winpr/libwinpr/crypto/crypto.c +++ b/winpr/libwinpr/crypto/crypto.c @@ -89,8 +89,6 @@ * CryptMsgUpdate * CryptMsgVerifyCountersignatureEncoded * CryptMsgVerifyCountersignatureEncodedEx - * CryptProtectData - * CryptProtectMemory * CryptQueryObject * CryptRegisterDefaultOIDFunction * CryptRegisterOIDFunction @@ -115,11 +113,7 @@ * CryptSIPRetrieveSubjectGuid * CryptSIPRetrieveSubjectGuidForCatalogFile * CryptSIPVerifyIndirectData - * CryptStringToBinaryA - * CryptStringToBinaryW * CryptUninstallDefaultContext - * CryptUnprotectData - * CryptUnprotectMemory * CryptUnregisterDefaultOIDFunction * CryptUnregisterOIDFunction * CryptUnregisterOIDInfo @@ -146,4 +140,140 @@ #include "crypto.h" +#include +#include + +static wListDictionary* g_ProtectedMemoryBlocks = NULL; + +BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags) +{ + BYTE* pCipherText; + int cbOut, cbFinal; + BYTE randomKey[256]; + WINPR_PROTECTED_MEMORY_BLOCK* pMemBlock; + + if (dwFlags != CRYPTPROTECTMEMORY_SAME_PROCESS) + return FALSE; + + if (!g_ProtectedMemoryBlocks) + g_ProtectedMemoryBlocks = ListDictionary_New(TRUE); + + pMemBlock = (WINPR_PROTECTED_MEMORY_BLOCK*) malloc(sizeof(WINPR_PROTECTED_MEMORY_BLOCK)); + ZeroMemory(pMemBlock, sizeof(WINPR_PROTECTED_MEMORY_BLOCK)); + + pMemBlock->pData = pData; + pMemBlock->cbData = cbData; + pMemBlock->dwFlags = dwFlags; + + /* AES Initialization */ + + RAND_bytes(pMemBlock->salt, 8); + RAND_bytes(randomKey, sizeof(randomKey)); + + EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), + pMemBlock->salt, + randomKey, sizeof(randomKey), + 4, pMemBlock->key, pMemBlock->iv); + + SecureZeroMemory(randomKey, sizeof(randomKey)); + + EVP_CIPHER_CTX_init(&(pMemBlock->enc)); + EVP_EncryptInit_ex(&(pMemBlock->enc), EVP_aes_256_cbc(), NULL, pMemBlock->key, pMemBlock->iv); + + EVP_CIPHER_CTX_init(&(pMemBlock->dec)); + EVP_DecryptInit_ex(&(pMemBlock->dec), EVP_aes_256_cbc(), NULL, pMemBlock->key, pMemBlock->iv); + + /* AES Encryption */ + + cbOut = pMemBlock->cbData + AES_BLOCK_SIZE - 1; + pCipherText = (BYTE*) malloc(cbOut); + + EVP_EncryptInit_ex(&(pMemBlock->enc), NULL, NULL, NULL, NULL); + EVP_EncryptUpdate(&(pMemBlock->enc), pCipherText, &cbOut, pMemBlock->pData, pMemBlock->cbData); + EVP_EncryptFinal_ex(&(pMemBlock->enc), pCipherText + cbOut, &cbFinal); + + CopyMemory(pMemBlock->pData, pCipherText, pMemBlock->cbData); + free(pCipherText); + + ListDictionary_Add(g_ProtectedMemoryBlocks, pData, pMemBlock); + + return TRUE; +} + +BOOL CryptUnprotectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags) +{ + BYTE* pPlainText; + int cbOut, cbFinal; + WINPR_PROTECTED_MEMORY_BLOCK* pMemBlock; + + if (dwFlags != CRYPTPROTECTMEMORY_SAME_PROCESS) + return FALSE; + + if (!g_ProtectedMemoryBlocks) + return FALSE; + + pMemBlock = (WINPR_PROTECTED_MEMORY_BLOCK*) ListDictionary_GetItemValue(g_ProtectedMemoryBlocks, pData); + + if (!pMemBlock) + return FALSE; + + /* AES Decryption */ + + cbOut = pMemBlock->cbData + AES_BLOCK_SIZE - 1; + pPlainText = (BYTE*) malloc(cbOut); + + EVP_DecryptInit_ex(&(pMemBlock->dec), NULL, NULL, NULL, NULL); + EVP_DecryptUpdate(&(pMemBlock->dec), pPlainText, &cbOut, pMemBlock->pData, pMemBlock->cbData); + EVP_DecryptFinal_ex(&(pMemBlock->dec), pPlainText + cbOut, &cbFinal); + + CopyMemory(pMemBlock->pData, pPlainText, pMemBlock->cbData); + SecureZeroMemory(pPlainText, pMemBlock->cbData); + free(pPlainText); + + ListDictionary_Remove(g_ProtectedMemoryBlocks, pData); + + /* AES Cleanup */ + + EVP_CIPHER_CTX_cleanup(&(pMemBlock->enc)); + EVP_CIPHER_CTX_cleanup(&(pMemBlock->dec)); + + free(pMemBlock); + + return TRUE; +} + +BOOL CryptProtectData(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, DATA_BLOB* pDataOut) +{ + return TRUE; +} + +BOOL CryptUnprotectData(DATA_BLOB* pDataIn, LPWSTR* ppszDataDescr, DATA_BLOB* pOptionalEntropy, + PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, DATA_BLOB* pDataOut) +{ + return TRUE; +} + +BOOL CryptStringToBinaryW(LPCWSTR pszString, DWORD cchString, DWORD dwFlags, BYTE* pbBinary, + DWORD* pcbBinary, DWORD* pdwSkip, DWORD* pdwFlags) +{ + return TRUE; +} + +BOOL CryptStringToBinaryA(LPCSTR pszString, DWORD cchString, DWORD dwFlags, BYTE* pbBinary, + DWORD* pcbBinary, DWORD* pdwSkip, DWORD* pdwFlags) +{ + return TRUE; +} + +BOOL CryptBinaryToStringW(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, DWORD* pcchString) +{ + return TRUE; +} + +BOOL CryptBinaryToStringA(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD* pcchString) +{ + return TRUE; +} + #endif diff --git a/winpr/libwinpr/crypto/crypto.h b/winpr/libwinpr/crypto/crypto.h index cd4fcdad5..5fbc74083 100644 --- a/winpr/libwinpr/crypto/crypto.h +++ b/winpr/libwinpr/crypto/crypto.h @@ -22,6 +22,10 @@ #ifndef _WIN32 +#include +#include +#include + struct _WINPR_CERTSTORE { LPCSTR lpszStoreProvider; @@ -29,6 +33,19 @@ struct _WINPR_CERTSTORE }; typedef struct _WINPR_CERTSTORE WINPR_CERTSTORE; +struct _WINPR_PROTECTED_MEMORY_BLOCK +{ + BYTE* pData; + DWORD cbData; + DWORD dwFlags; + BYTE key[32]; + BYTE iv[32]; + BYTE salt[8]; + EVP_CIPHER_CTX enc; + EVP_CIPHER_CTX dec; +}; +typedef struct _WINPR_PROTECTED_MEMORY_BLOCK WINPR_PROTECTED_MEMORY_BLOCK; + #endif #endif /* WINPR_CRYPTO_PRIVATE_H */ diff --git a/winpr/libwinpr/crypto/test/CMakeLists.txt b/winpr/libwinpr/crypto/test/CMakeLists.txt index 398cb9c8e..58a29cdb7 100644 --- a/winpr/libwinpr/crypto/test/CMakeLists.txt +++ b/winpr/libwinpr/crypto/test/CMakeLists.txt @@ -5,7 +5,9 @@ set(MODULE_PREFIX "TEST_CRYPTO") set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) set(${MODULE_PREFIX}_TESTS - TestCertEnumCertificatesInStore.c) + TestCryptoProtectData.c + TestCryptoProtectMemory.c + TestCryptoCertEnumCertificatesInStore.c) create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} diff --git a/winpr/libwinpr/crypto/test/TestCertEnumCertificatesInStore.c b/winpr/libwinpr/crypto/test/TestCryptoCertEnumCertificatesInStore.c similarity index 95% rename from winpr/libwinpr/crypto/test/TestCertEnumCertificatesInStore.c rename to winpr/libwinpr/crypto/test/TestCryptoCertEnumCertificatesInStore.c index d4a909e5a..d0eb67516 100644 --- a/winpr/libwinpr/crypto/test/TestCertEnumCertificatesInStore.c +++ b/winpr/libwinpr/crypto/test/TestCryptoCertEnumCertificatesInStore.c @@ -12,7 +12,7 @@ #include #endif -int TestCertEnumCertificatesInStore(int argc, char* argv[]) +int TestCryptoCertEnumCertificatesInStore(int argc, char* argv[]) { int index; DWORD status; diff --git a/winpr/libwinpr/crypto/test/TestCryptoProtectData.c b/winpr/libwinpr/crypto/test/TestCryptoProtectData.c new file mode 100644 index 000000000..faa24d3c5 --- /dev/null +++ b/winpr/libwinpr/crypto/test/TestCryptoProtectData.c @@ -0,0 +1,11 @@ + + +#include +#include +#include + +int TestCryptoProtectData(int argc, char* argv[]) +{ + return 0; +} + diff --git a/winpr/libwinpr/crypto/test/TestCryptoProtectMemory.c b/winpr/libwinpr/crypto/test/TestCryptoProtectMemory.c new file mode 100644 index 000000000..75b461adb --- /dev/null +++ b/winpr/libwinpr/crypto/test/TestCryptoProtectMemory.c @@ -0,0 +1,47 @@ + +#include +#include +#include + +static const char* SECRET_PASSWORD_TEST = "MySecretPassword123!"; + +int TestCryptoProtectMemory(int argc, char* argv[]) +{ + int cbPlainText; + int cbCipherText; + char* pPlainText; + BYTE* pCipherText; + + pPlainText = (char*) SECRET_PASSWORD_TEST; + cbPlainText = strlen(pPlainText) + 1; + + cbCipherText = cbPlainText + (CRYPTPROTECTMEMORY_BLOCK_SIZE - (cbPlainText % CRYPTPROTECTMEMORY_BLOCK_SIZE)); + printf("cbPlainText: %d cbCipherText: %d\n", cbPlainText, cbCipherText); + + pCipherText = (BYTE*) malloc(cbCipherText); + CopyMemory(pCipherText, pPlainText, cbPlainText); + ZeroMemory(&pCipherText[cbPlainText], (cbCipherText - cbPlainText)); + + if (!CryptProtectMemory(pCipherText, cbCipherText, CRYPTPROTECTMEMORY_SAME_PROCESS)) + { + printf("CryptProtectMemory failure\n"); + return -1; + } + + printf("PlainText: %s (cbPlainText = %d, cbCipherText = %d)\n", pPlainText, cbPlainText, cbCipherText); + + winpr_HexDump(pCipherText, cbCipherText); + + if (!CryptUnprotectMemory(pCipherText, cbCipherText, CRYPTPROTECTMEMORY_SAME_PROCESS)) + { + printf("CryptUnprotectMemory failure\n"); + return -1; + } + + printf("Decrypted CipherText: %s\n", pCipherText); + + SecureZeroMemory(pCipherText, cbCipherText); + free(pCipherText); + + return 0; +} From 9bdfbcd556fe8636d73301f05512aa200153d823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 1 Feb 2014 12:50:28 -0500 Subject: [PATCH 141/149] wfreerdp: fix test build issues --- libfreerdp/gdi/test/TestGdiBitBlt.c | 3 ++- winpr/libwinpr/sspi/test/CMakeLists.txt | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libfreerdp/gdi/test/TestGdiBitBlt.c b/libfreerdp/gdi/test/TestGdiBitBlt.c index b164bc61f..1738a1164 100644 --- a/libfreerdp/gdi/test/TestGdiBitBlt.c +++ b/libfreerdp/gdi/test/TestGdiBitBlt.c @@ -407,6 +407,7 @@ BYTE bmp_PATINVERT[256] = int CompareBitmaps(HGDI_BITMAP hBmp1, HGDI_BITMAP hBmp2) { + int bpp; int x, y; BYTE *p1, *p2; @@ -417,7 +418,7 @@ int CompareBitmaps(HGDI_BITMAP hBmp1, HGDI_BITMAP hBmp2) { p1 = hBmp1->data; p2 = hBmp2->data; - int bpp = hBmp1->bitsPerPixel; + bpp = hBmp1->bitsPerPixel; if (bpp == 32) { diff --git a/winpr/libwinpr/sspi/test/CMakeLists.txt b/winpr/libwinpr/sspi/test/CMakeLists.txt index 4ea3599d6..593b22c3d 100644 --- a/winpr/libwinpr/sspi/test/CMakeLists.txt +++ b/winpr/libwinpr/sspi/test/CMakeLists.txt @@ -16,8 +16,15 @@ create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS}) +include_directories(${ZLIB_INCLUDE_DIRS}) +include_directories(${OPENSSL_INCLUDE_DIR}) + add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) +set(${MODULE_PREFIX}_LIBS + ${ZLIB_LIBRARIES} + ${OPENSSL_LIBRARIES}) + if(WIN32) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} secur32 crypt32) endif() From b5bef07e50445fa596a9ce137a084be107835f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sat, 1 Feb 2014 19:53:45 -0500 Subject: [PATCH 142/149] wfreerdp: fix building against OpenSSL with MONOLITHIC_BUILD and shared libraries --- client/common/CMakeLists.txt | 11 +++++++---- cmake/FindOpenSSL.cmake | 14 +++++--------- winpr/CMakeLists.txt | 2 -- winpr/libwinpr/crypto/CMakeLists.txt | 4 +++- winpr/libwinpr/sspi/CMakeLists.txt | 2 -- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/client/common/CMakeLists.txt b/client/common/CMakeLists.txt index 3f1b29780..07f162386 100644 --- a/client/common/CMakeLists.txt +++ b/client/common/CMakeLists.txt @@ -18,9 +18,6 @@ set(MODULE_NAME "freerdp-client") set(MODULE_PREFIX "FREERDP_CLIENT") -include_directories(${OPENSSL_INCLUDE_DIR}) -include_directories(${ZLIB_INCLUDE_DIRS}) - set(${MODULE_PREFIX}_SRCS client.c cmdline.c @@ -40,6 +37,9 @@ endif() add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) +include_directories(${OPENSSL_INCLUDE_DIR}) +include_directories(${ZLIB_INCLUDE_DIRS}) + set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${FREERDP_VERSION} SOVERSION ${FREERDP_API_VERSION} PREFIX "lib") set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} @@ -53,7 +53,10 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MONOLITHIC ${MONOLITHI MODULE winpr MODULES winpr-crt winpr-utils) -set_target_properties(${MODULE_NAME} PROPERTIES LINK_INTERFACE_LIBRARIES "") +if(NOT WIN32) + set_target_properties(${MODULE_NAME} PROPERTIES LINK_INTERFACE_LIBRARIES "") + set_target_properties(${MODULE_NAME} PROPERTIES INTERFACE_LINK_LIBRARIES "") +endif() target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) diff --git a/cmake/FindOpenSSL.cmake b/cmake/FindOpenSSL.cmake index 450a6329d..ee446f6e1 100644 --- a/cmake/FindOpenSSL.cmake +++ b/cmake/FindOpenSSL.cmake @@ -143,14 +143,10 @@ IF(WIN32 AND NOT CYGWIN) "lib/VC" ) - if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) - set( OPENSSL_LIBRARIES - optimized ${SSL_EAY_RELEASE} debug ${SSL_EAY_DEBUG} - optimized ${LIB_EAY_RELEASE} debug ${LIB_EAY_DEBUG} - ) - else() - set( OPENSSL_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} ) - endif() + set( OPENSSL_DEBUG_LIBRARIES ${SSL_EAY_DEBUG} ${LIB_EAY_DEBUG} ) + set( OPENSSL_RELEASE_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} ) + set( OPENSSL_LIBRARIES ${OPENSSL_RELEASE_LIBRARIES} ) + MARK_AS_ADVANCED(SSL_EAY_DEBUG SSL_EAY_RELEASE) MARK_AS_ADVANCED(LIB_EAY_DEBUG LIB_EAY_RELEASE) ELSEIF(MINGW) @@ -322,4 +318,4 @@ else (OPENSSL_VERSION) ) endif (OPENSSL_VERSION) -MARK_AS_ADVANCED(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES) +MARK_AS_ADVANCED(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES OPENSSL_DEBUG_LIBRARIES OPENSSL_RELEASE_LIBRARIES) diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt index c20a307c6..7430d3570 100644 --- a/winpr/CMakeLists.txt +++ b/winpr/CMakeLists.txt @@ -91,8 +91,6 @@ if(${CMAKE_VERSION} VERSION_GREATER "2.8.10") set(WINPR_INCLUDE_DIR "include") set(WINPR_MONOLITHIC_BUILD ${MONOLITHIC_BUILD}) - message(STATUS "WINPR_VERSION: ${WINPR_VERSION}") - configure_package_config_file(WinPRConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/WinPRConfig.cmake INSTALL_DESTINATION ${WINPR_CMAKE_INSTALL_DIR} PATH_VARS WINPR_INCLUDE_DIR WINPR_MONOLITHIC_BUILD) diff --git a/winpr/libwinpr/crypto/CMakeLists.txt b/winpr/libwinpr/crypto/CMakeLists.txt index e63aefea4..7f0947f8d 100644 --- a/winpr/libwinpr/crypto/CMakeLists.txt +++ b/winpr/libwinpr/crypto/CMakeLists.txt @@ -45,12 +45,14 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MODULE winpr MODULES winpr-crt winpr-utils) +message(STATUS "OPENSSL_LIBRARIES: ${OPENSSL_LIBRARIES} ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}") + if(WIN32) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} crypt32) endif() if(MONOLITHIC_BUILD) - + set(WINPR_LIBS ${WINPR_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE) else() install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT WinPRTargets) endif() diff --git a/winpr/libwinpr/sspi/CMakeLists.txt b/winpr/libwinpr/sspi/CMakeLists.txt index 7bfe934f3..c9eac6a6d 100644 --- a/winpr/libwinpr/sspi/CMakeLists.txt +++ b/winpr/libwinpr/sspi/CMakeLists.txt @@ -74,8 +74,6 @@ set(${MODULE_PREFIX}_LIBS if(WIN32) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ws2_32) -else() - set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${ZLIB_LIBRARIES}) endif() set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS From 17665378a93d042c48cbf8273961b8462baf3121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 2 Feb 2014 22:37:54 -0500 Subject: [PATCH 143/149] wfreerdp: fix target exporting --- CMakeLists.txt | 47 ++++++++++++++-------------- client/Windows/CMakeLists.txt | 2 +- winpr/CMakeLists.txt | 7 +++-- winpr/libwinpr/crypto/CMakeLists.txt | 2 -- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c8ebb270d..92f60d453 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,30 +214,30 @@ if(IOS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -isysroot ${CMAKE_IOS_SDK_ROOT} -g") endif() -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINPR_EXPORTS") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFREERDP_EXPORTS") + # Include files if(NOT IOS) -check_include_files(fcntl.h HAVE_FCNTL_H) -check_include_files(unistd.h HAVE_UNISTD_H) -check_include_files(stdint.h HAVE_STDINT_H) -check_include_files(inttypes.h HAVE_INTTYPES_H) -check_include_files(sys/modem.h HAVE_SYS_MODEM_H) -check_include_files(sys/filio.h HAVE_SYS_FILIO_H) -check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H) -check_include_files(sys/select.h HAVE_SYS_SELECT_H) + check_include_files(fcntl.h HAVE_FCNTL_H) + check_include_files(unistd.h HAVE_UNISTD_H) + check_include_files(stdint.h HAVE_STDINT_H) + check_include_files(inttypes.h HAVE_INTTYPES_H) + check_include_files(sys/modem.h HAVE_SYS_MODEM_H) + check_include_files(sys/filio.h HAVE_SYS_FILIO_H) + check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H) + check_include_files(sys/select.h HAVE_SYS_SELECT_H) else() -set(HAVE_FCNTL_H 1) -set(HAVE_UNISTD_H 1) -set(HAVE_STDINT_H 1) -set(HAVE_INTTYPES_H 1) -set(HAVE_SYS_FILIO_H 1) + set(HAVE_FCNTL_H 1) + set(HAVE_UNISTD_H 1) + set(HAVE_STDINT_H 1) + set(HAVE_INTTYPES_H 1) + set(HAVE_SYS_FILIO_H 1) endif() if(NOT IOS) -check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF) + check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF) else() -set(HAVE_TM_GMTOFF 1) + set(HAVE_TM_GMTOFF 1) endif() # Mac OS X @@ -315,10 +315,11 @@ if(NOT IOS AND NOT ANDROID) find_package(Threads REQUIRED) endif() -list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) -check_library_exists(pthread pthread_tryjoin_np "" HAVE_PTHREAD_GNU_EXT) -list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) - +if(NOT WIN32) + list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + check_library_exists(pthread pthread_tryjoin_np "" HAVE_PTHREAD_GNU_EXT) + list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) +endif() if(UNIX OR CYGWIN) check_include_files(sys/eventfd.h HAVE_AIO_H) @@ -433,9 +434,7 @@ endif() find_feature(X11 ${X11_FEATURE_TYPE} ${X11_FEATURE_PURPOSE} ${X11_FEATURE_DESCRIPTION}) find_feature(DirectFB ${DIRECTFB_FEATURE_TYPE} ${DIRECTFB_FEATURE_PURPOSE} ${DIRECTFB_FEATURE_DESCRIPTION}) if (${WITH_DIRECTFB}) - message(WARNING " -DIRECTFB is orphaned and not maintained see docs/README.directfb for details -") + message(WARNING "DIRECTFB is orphaned and not maintained see docs/README.directfb for details") endif() find_feature(ZLIB ${ZLIB_FEATURE_TYPE} ${ZLIB_FEATURE_PURPOSE} ${ZLIB_FEATURE_DESCRIPTION}) @@ -463,7 +462,7 @@ if(TARGET_ARCH MATCHES "x86|x64") endif() # Installation Paths -if(WIN32) +if(WIN32 AND NOT FREERDP_SDK) set(CMAKE_INSTALL_BINDIR ".") set(CMAKE_INSTALL_LIBDIR ".") endif() diff --git a/client/Windows/CMakeLists.txt b/client/Windows/CMakeLists.txt index 5bc2d5eef..d01b6b07f 100644 --- a/client/Windows/CMakeLists.txt +++ b/client/Windows/CMakeLists.txt @@ -63,7 +63,7 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) if(WITH_CLIENT_INTERFACE) - install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries EXPORT WinPRTargets) + install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries EXPORT FreeRDPTargets) add_subdirectory(cli) else() install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client) diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt index 7430d3570..22ad623da 100644 --- a/winpr/CMakeLists.txt +++ b/winpr/CMakeLists.txt @@ -22,7 +22,7 @@ project(WinPR C) set(CMAKE_COLOR_MAKEFILE ON) if(FREERDP_VERSION) - set(FREERDP_BUILD) + set(FREERDP_BUILD 1) endif() # Include cmake modules @@ -37,12 +37,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/) # Check for cmake compatibility (enable/disable features) include(CheckCmakeCompat) - include(FindFeature) +include(ComplexLibrary) include(AutoVersioning) include(ConfigOptions) include(CheckCCompilerFlag) include(GNUInstallDirsWrapper) +include(CMakePackageConfigHelpers) # Soname versioning set(WINPR_VERSION_MAJOR "1") @@ -61,6 +62,8 @@ if(NOT DEFINED BUILD_SHARED_LIBS) set(BUILD_SHARED_LIBS ON) endif() +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINPR_EXPORTS") + if(FREERDP_BUILD) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE) include_directories(${CMAKE_CURRENT_BINARY_DIR}/include PARENT_SCOPE) diff --git a/winpr/libwinpr/crypto/CMakeLists.txt b/winpr/libwinpr/crypto/CMakeLists.txt index 7f0947f8d..e8fc30bda 100644 --- a/winpr/libwinpr/crypto/CMakeLists.txt +++ b/winpr/libwinpr/crypto/CMakeLists.txt @@ -45,8 +45,6 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MODULE winpr MODULES winpr-crt winpr-utils) -message(STATUS "OPENSSL_LIBRARIES: ${OPENSSL_LIBRARIES} ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}") - if(WIN32) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} crypt32) endif() From 7c7aa192ec8b25d21890d5cd859153534041f766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 3 Feb 2014 00:56:16 -0500 Subject: [PATCH 144/149] libwinpr-wtsapi: include wtsapi32.h on Windows --- winpr/include/winpr/wtsapi.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/winpr/include/winpr/wtsapi.h b/winpr/include/winpr/wtsapi.h index 42f2b0101..04e947fea 100644 --- a/winpr/include/winpr/wtsapi.h +++ b/winpr/include/winpr/wtsapi.h @@ -25,7 +25,11 @@ #include -#ifndef _WIN32 +#ifdef _WIN32 + +#include + +#else /** * Virtual Channel Protocol (Common) From fab61ba946acf134139f3e40389e8b26365d0ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 3 Feb 2014 16:03:43 -0500 Subject: [PATCH 145/149] afreerdp: fix build on Android --- client/Android/.gitignore | 4 ++++ client/Android/FreeRDPCore/.gitignore | 4 ++++ client/Android/aFreeRDP/.gitignore | 4 ++++ winpr/libwinpr/io/io.c | 2 +- winpr/libwinpr/synch/synch.h | 3 --- 5 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 client/Android/FreeRDPCore/.gitignore create mode 100644 client/Android/aFreeRDP/.gitignore diff --git a/client/Android/.gitignore b/client/Android/.gitignore index 1eee223a1..31b2e3c80 100644 --- a/client/Android/.gitignore +++ b/client/Android/.gitignore @@ -8,3 +8,7 @@ libs/armeabi* AndroidManifest.xml local.properties !.project + +FreeRDPCore/project.properties +FreeRDPCore/src/com/freerdp/freerdpcore/utils/BuildConfiguration.java +aFreeRDP/project.properties diff --git a/client/Android/FreeRDPCore/.gitignore b/client/Android/FreeRDPCore/.gitignore new file mode 100644 index 000000000..2d8df2acd --- /dev/null +++ b/client/Android/FreeRDPCore/.gitignore @@ -0,0 +1,4 @@ +ant.properties +build.xml +jni/Android.mk +jni/Application.mk diff --git a/client/Android/aFreeRDP/.gitignore b/client/Android/aFreeRDP/.gitignore new file mode 100644 index 000000000..2d8df2acd --- /dev/null +++ b/client/Android/aFreeRDP/.gitignore @@ -0,0 +1,4 @@ +ant.properties +build.xml +jni/Android.mk +jni/Application.mk diff --git a/winpr/libwinpr/io/io.c b/winpr/libwinpr/io/io.c index ad2c3930c..3fa4fc207 100644 --- a/winpr/libwinpr/io/io.c +++ b/winpr/libwinpr/io/io.c @@ -57,7 +57,7 @@ BOOL GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumb else if (Type == HANDLE_TYPE_NAMED_PIPE) { - int status; + int status = -1; DWORD request; PVOID lpBuffer; DWORD nNumberOfBytes; diff --git a/winpr/libwinpr/synch/synch.h b/winpr/libwinpr/synch/synch.h index 2b326ad9b..fbde1d4a8 100644 --- a/winpr/libwinpr/synch/synch.h +++ b/winpr/libwinpr/synch/synch.h @@ -99,9 +99,6 @@ struct winpr_timer #ifdef WITH_POSIX_TIMER timer_t tid; -#endif - -#ifdef HAVE_TIMERFD_H struct itimerspec timeout; #endif }; From 3bd5fda4a8d2cc4625d42c6567f61a842970414a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 5 Feb 2014 08:37:13 -0500 Subject: [PATCH 146/149] libfreerdp-codec: port MPPC unit tests --- include/freerdp/codec/mppc_dec.h | 1 + libfreerdp/codec/test/CMakeLists.txt | 1 + libfreerdp/codec/test/TestFreeRDPCodecMppc.c | 657 +++++++++++++++++++ 3 files changed, 659 insertions(+) create mode 100644 libfreerdp/codec/test/TestFreeRDPCodecMppc.c diff --git a/include/freerdp/codec/mppc_dec.h b/include/freerdp/codec/mppc_dec.h index b5b92f7b7..6d39299fd 100644 --- a/include/freerdp/codec/mppc_dec.h +++ b/include/freerdp/codec/mppc_dec.h @@ -50,6 +50,7 @@ FREERDP_API int decompress_rdp_4(struct rdp_mppc_dec* dec, BYTE* cbuf, int len, FREERDP_API int decompress_rdp_5(struct rdp_mppc_dec* dec, BYTE* cbuf, int len, int ctype, UINT32* roff, UINT32* rlen); FREERDP_API int decompress_rdp_6(struct rdp_mppc_dec* dec, BYTE* cbuf, int len, int ctype, UINT32* roff, UINT32* rlen); FREERDP_API int decompress_rdp_61(struct rdp_mppc_dec* dec, BYTE* cbuf, int len, int ctype, UINT32* roff, UINT32* rlen); + FREERDP_API struct rdp_mppc_dec* mppc_dec_new(void); FREERDP_API void mppc_dec_free(struct rdp_mppc_dec* dec); diff --git a/libfreerdp/codec/test/CMakeLists.txt b/libfreerdp/codec/test/CMakeLists.txt index e1bced145..3ccf9da72 100644 --- a/libfreerdp/codec/test/CMakeLists.txt +++ b/libfreerdp/codec/test/CMakeLists.txt @@ -5,6 +5,7 @@ set(MODULE_PREFIX "TEST_FREERDP_CODEC") set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) set(${MODULE_PREFIX}_TESTS + TestFreeRDPCodecMppc.c TestFreeRDPCodecPlanar.c) create_test_sourcelist(${MODULE_PREFIX}_SRCS diff --git a/libfreerdp/codec/test/TestFreeRDPCodecMppc.c b/libfreerdp/codec/test/TestFreeRDPCodecMppc.c new file mode 100644 index 000000000..b7b3a6977 --- /dev/null +++ b/libfreerdp/codec/test/TestFreeRDPCodecMppc.c @@ -0,0 +1,657 @@ +#include +#include + +#include +#include +#include + +static BYTE TEST_RDP5_COMPRESSED_DATA[] = +{ + 0x24, 0x02, 0x03, 0x09, 0x00, 0x20, 0x0c, 0x05, 0x10, 0x01, 0x40, 0x0a, 0xbf, 0xdf, 0xc3, 0x20, + 0x80, 0x00, 0x1f, 0x0a, 0x00, 0x00, 0x07, 0x43, 0x4e, 0x00, 0x68, 0x02, 0x00, 0x22, 0x00, 0x34, + 0xcb, 0xfb, 0xf8, 0x18, 0x40, 0x01, 0x00, 0x27, 0xe2, 0x90, 0x0f, 0xc3, 0x91, 0xa8, 0x00, 0x08, + 0x00, 0x00, 0x68, 0x50, 0x60, 0x65, 0xfc, 0x0e, 0xfe, 0x04, 0x00, 0x08, 0x00, 0x06, 0x0c, 0x00, + 0x01, 0x00, 0xf8, 0x40, 0x20, 0x00, 0x00, 0x90, 0x00, 0xcf, 0x95, 0x1f, 0x44, 0x90, 0x00, 0x6e, + 0x03, 0xf4, 0x40, 0x21, 0x9f, 0x26, 0x01, 0xbf, 0x88, 0x10, 0x90, 0x00, 0x08, 0x04, 0x00, 0x04, + 0x30, 0x03, 0xe4, 0xc7, 0xea, 0x05, 0x1e, 0x87, 0xf8, 0x20, 0x1c, 0x00, 0x10, 0x84, 0x22, 0x1f, + 0x71, 0x0d, 0x0e, 0xb9, 0x88, 0x9f, 0x5c, 0xee, 0x41, 0x97, 0xfb, 0xf8, 0x88, 0x68, 0x08, 0x6d, + 0xd0, 0x44, 0xfc, 0x34, 0x06, 0xe6, 0x16, 0x21, 0x04, 0x11, 0x0f, 0xb9, 0x85, 0x86, 0x5d, 0x44, + 0x4f, 0xae, 0xb7, 0x40, 0xa8, 0xcd, 0x5b, 0xed, 0x02, 0xee, 0xc2, 0x21, 0x40, 0x21, 0x21, 0x23, + 0x17, 0xb7, 0x00, 0x60, 0x00, 0x3b, 0xfd, 0xfc, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x34, 0x00, 0x33, + 0xc7, 0xe0, 0xc0, 0x0f, 0x07, 0x12, 0x42, 0x01, 0xe8, 0x6c, 0xc7, 0x83, 0x07, 0x8c, 0xd4, 0x30, + 0x07, 0x20, 0x01, 0x90, 0xa3, 0xf1, 0xdb, 0xf5, 0xd4, 0x13, 0xc2, 0x4f, 0x0f, 0xe5, 0xe2, 0xc7, + 0x87, 0xf2, 0xf0, 0x93, 0xc3, 0xf9, 0x78, 0xb0, 0x1a, 0x03, 0xe1, 0xf1, 0xd0, 0x08, 0x4c, 0x66, + 0xac, 0x32, 0x31, 0x70, 0x60, 0x11, 0x01, 0x11, 0x01, 0x01, 0x01, 0xf0, 0x36, 0x1f, 0xe5, 0xe0, + 0x6c, 0xbc, 0x26, 0xf0, 0x36, 0x5f, 0xe5, 0xe0, 0x6c, 0xbc, 0x26, 0xf0, 0x34, 0xf9, 0x94, 0x32, + 0x31, 0x74, 0x20, 0x10, 0x00, 0x00, 0x0f, 0xbf, 0x87, 0xdf, 0xef, 0xfe, 0x4b, 0xbf, 0x02, 0xfa, + 0xde, 0xa7, 0x79, 0x32, 0x44, 0x7c, 0x20, 0x82, 0x00, 0x5f, 0xef, 0xff, 0x09, 0xe1, 0x05, 0x74, + 0x32, 0xea, 0x09, 0xe1, 0x0f, 0x55, 0x83, 0x85, 0x2a, 0xa0, 0x1d, 0x50, 0x0e, 0x0e, 0x0b, 0x01, + 0x01, 0x43, 0x06, 0x02, 0xbe, 0x5f, 0x00, 0x00, 0x0c, 0x3d, 0x4d, 0x87, 0xa6, 0x5e, 0xa6, 0xcb, + 0xc3, 0xcf, 0x53, 0x65, 0xe9, 0x97, 0xa9, 0xb2, 0xf5, 0x9b, 0xd4, 0xd3, 0xee, 0xcd, 0xc0, 0x7c, + 0xae, 0xe0, 0x65, 0x1f, 0xe5, 0xe0, 0x6c, 0xbc, 0x26, 0xf0, 0x36, 0x5f, 0xe5, 0xe0, 0x6c, 0xbc, + 0x26, 0xf0, 0x34, 0xfb, 0xb3, 0xf2, 0x41, 0x30, 0x20, 0x04, 0xa0, 0x80, 0x93, 0xf3, 0xf2, 0x1b, + 0xed, 0xf6, 0x0f, 0x04, 0x82, 0x7b, 0xcc, 0x00, 0x65, 0xef, 0x4f, 0x86, 0x02, 0xf7, 0xa7, 0xe0, + 0x0a, 0x88, 0x1c, 0x34, 0x02, 0x02, 0x02, 0x60, 0x60, 0x49, 0x40, 0xc1, 0x2f, 0x14, 0xca, 0x60, + 0xc1, 0x81, 0x80, 0x07, 0xc3, 0x00, 0x00, 0x39, 0xfa, 0x86, 0x38, 0x93, 0x47, 0x08, 0x27, 0x08, + 0xfc, 0xb8, 0x4e, 0x38, 0x47, 0xe5, 0xc2, 0x09, 0xc2, 0x3f, 0x2e, 0x13, 0x8e, 0x11, 0xf3, 0xc3, + 0x57, 0x1a, 0x88, 0x7d, 0x44, 0x3c, 0x3c, 0x04, 0x0f, 0xd4, 0x3f, 0x83, 0x8d, 0x82, 0x00, 0x25, + 0x04, 0x84, 0xdf, 0xe0, 0x17, 0xf8, 0x04, 0x03, 0xe1, 0x47, 0xc4, 0xaf, 0x9c, 0x00, 0x00, 0x31, + 0xf5, 0x4c, 0x71, 0x78, 0x8f, 0x54, 0xfb, 0x1c, 0x97, 0xa4, 0x04, 0x13, 0xd5, 0x2f, 0x77, 0xc7, + 0xb8, 0x9e, 0xef, 0xcb, 0xc2, 0x6f, 0x77, 0xe5, 0xee, 0x27, 0xbb, 0xf2, 0xf7, 0xe3, 0xdd, 0xf3, + 0xc6, 0xfb, 0x2a, 0x78, 0x6d, 0x3c, 0x34, 0x37, 0xc0, 0xaf, 0x25, 0xc7, 0x81, 0x7d, 0x6e, 0x5d, + 0x5c, 0xd6, 0xe3, 0x43, 0xc0, 0x82, 0xd0, 0x95, 0x90, 0xd8, 0xbd, 0xfc, 0x00, 0x09, 0xc0, 0x34, + 0x39, 0x46, 0x84, 0x20, 0x40, 0x38, 0xa3, 0x42, 0x12, 0xb0, 0x55, 0xbe, 0x28, 0xc0, 0x70, 0x64, + 0x28, 0xc8, 0x48, 0x42, 0x08, 0xb2, 0x1b, 0x46, 0xa6, 0x09, 0x54, 0x2e, 0x5f, 0x73, 0x84, 0xfc, + 0x28, 0x4a, 0x73, 0x79, 0xf2, 0x6c, 0x5d, 0x82, 0x82, 0x6e, 0xc2, 0x27, 0xd7, 0x6b, 0xb8, 0x4f, + 0xa4, 0xa4, 0x22, 0xee, 0x22, 0x7e, 0x10, 0x03, 0x78, 0x08, 0xf4, 0x94, 0x5e, 0x02, 0x01, 0xef, + 0x02, 0x27, 0xd7, 0x8b, 0xc8, 0x3f, 0xa4, 0xa4, 0x1a, 0xf3, 0xd1, 0x84, 0x0c, 0x32, 0x31, 0x75, + 0x60, 0x05, 0xe2, 0x30, 0xb7, 0xad, 0x5b, 0x15, 0xd5, 0xc3, 0xc0, 0x00, 0x11, 0x81, 0x81, 0x69, + 0x8f, 0x06, 0x0f, 0x14, 0xcf, 0xa6, 0xe8, 0xb1, 0x22, 0x77, 0xeb, 0xd7, 0x45, 0x89, 0xf0, 0xb6, + 0x3e, 0x23, 0x06, 0x80, 0xf8, 0x5b, 0x0f, 0x04, 0x83, 0xfc, 0x2d, 0x8f, 0x88, 0xc1, 0xa0, 0x3e, + 0x16, 0x1d, 0x00, 0x83, 0x74, 0x58, 0xa0, 0xc0, 0x10, 0xce, 0x8b, 0x17, 0xe0, 0x68, 0xff, 0x20, + 0xff, 0x03, 0x63, 0xe5, 0xcf, 0x1f, 0xa0, 0x40, 0x00, 0x00, 0x2a, 0xff, 0xd6, 0xd1, 0xc0, 0xb9, + 0xe0, 0x5f, 0x6b, 0x81, 0x73, 0xc9, 0x93, 0xd1, 0x63, 0x50, 0xf0, 0x9b, 0xf0, 0x48, 0x4f, 0xaf, + 0xe0, 0x1b, 0xef, 0x82, 0x6f, 0xc2, 0x40, 0xe0, 0xe4, 0x60, 0xa0, 0x69, 0xa1, 0xa1, 0xbe, 0xba, + 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x42, 0x00, 0x44, 0x00, 0x88, 0x01, 0x10, 0x02, + 0x21, 0x02, 0x22, 0x04, 0x44, 0x08, 0x9c, 0x8f, 0xcd, 0xe0, 0x02, 0x20, 0x88, 0x02, 0x10, 0x40, + 0x01, 0xf0, 0x60, 0x44, 0xc0, 0xce, 0xb1, 0x8f, 0xd0, 0x30, 0x00, 0x60, 0x00, 0xa0, 0x00, 0xc4, + 0x00, 0xcc, 0x01, 0x98, 0x03, 0x28, 0x03, 0x31, 0x03, 0x33, 0x06, 0x66, 0x07, 0x0e, 0x2c, 0xe3, + 0x7b, 0x18, 0x85, 0xc7, 0xd6, 0x51, 0x71, 0x0f, 0x0e, 0xb8, 0x88, 0x9f, 0x5c, 0x6e, 0x41, 0xde, + 0xeb, 0x71, 0x20, 0x5c, 0xba, 0xf7, 0xc8, 0x6f, 0xba, 0xc1, 0xf7, 0x30, 0xd0, 0xce, 0xc1, 0x31, + 0x74, 0xec, 0x13, 0x41, 0x77, 0x41, 0x13, 0xa0, 0x10, 0xbf, 0x7c, 0x45, 0xd3, 0xa5, 0xbc, 0x55, + 0x84, 0xaa, 0x41, 0xc1, 0xc1, 0xe0, 0xe0, 0x29, 0x01, 0x20, 0x81, 0x00, 0x03, 0x80, 0x07, 0xc0, + 0x0f, 0xe0, 0x06, 0xbe, 0x16, 0x75, 0xe7, 0x9f, 0xfb, 0x1e, 0x17, 0x90, 0xef, 0x0b, 0xbb, 0x15, + 0x03, 0x7c, 0x2b, 0x7e, 0x22, 0x78, 0x56, 0x83, 0xae, 0x77, 0x40, 0xcf, 0xb0, 0xf0, 0x98, 0x28, + 0x04, 0x2f, 0xaf, 0x0e, 0x40, 0xfc, 0x01, 0x1c, 0x5c, 0xb1, 0xf2, 0xbf, 0xa5, 0xd7, 0x8f, 0x97, + 0xc0, 0xfe, 0x9f, 0x02, 0xe7, 0x24, 0x79, 0xe0, 0x9b, 0xa9, 0xfd, 0x74, 0x3b, 0xaf, 0x2d, 0xf8, + 0x4b, 0xd2, 0xf7, 0x84, 0x54, 0x04, 0x2a, 0x02, 0x02, 0x01, 0xe1, 0x1e, 0xf0, 0x87, 0xff, 0x77, + 0x07, 0x00, 0x02, 0x00, 0x0d, 0xbd, 0xe1, 0xf0, 0x01, 0x1e, 0xf0, 0xfd, 0x80, 0x4c, 0x24, 0x11, + 0x2c, 0x10, 0x24, 0x02, 0x01, 0x40, 0xb0, 0x5c, 0x2c, 0x14, 0x08, 0x07, 0x1b, 0x80, 0x01, 0xa7, + 0xbd, 0x3e, 0x00, 0x27, 0xde, 0x9f, 0xb0, 0x85, 0x01, 0xfb, 0xd2, 0x04, 0x0c, 0x1c, 0x2e, 0x0e, + 0x06, 0x18, 0x03, 0xd4, 0x00, 0x00, 0x67, 0xef, 0x4f, 0x80, 0x0a, 0xf7, 0xa7, 0xe3, 0x94, 0xe0, + 0xe0, 0x10, 0x1b, 0xfd, 0xfc, 0x74, 0x62, 0xe8, 0xc0, 0x1d, 0x62, 0x00, 0x0b, 0x00, 0xb7, 0x70, + 0xe6, 0x8a, 0x68, 0x75, 0x38, 0x3c, 0x3c, 0x4c, 0x2f, 0x87, 0xef, 0x01, 0xc7, 0xb2, 0x40, 0x21, + 0xa3, 0x23, 0x0a, 0x08, 0x01, 0xa1, 0xa1, 0xe1, 0x80, 0x69, 0x40, 0xe1, 0x00, 0x00, 0x40, 0xd0, + 0xea, 0xe5, 0xe1, 0xc0, 0x81, 0x87, 0xed, 0x68, 0x1a, 0x08, 0x94, 0x0c, 0x0c, 0xf1, 0x7c, 0xbe, + 0x5f, 0x2f, 0x8f, 0x00, 0x00, 0x0d, 0x1f, 0x68, 0x7a, 0x1a, 0x04, 0x05, 0xce, 0xe6, 0x2a, 0x0c, + 0x01, 0xc2, 0x00, 0x40, 0x42, 0x61, 0xc0, 0x49, 0x41, 0x60, 0xa0, 0x80, 0x01, 0xc0, 0x03, 0xe0, + 0x07, 0xf0, 0x07, 0xfa, 0x00, 0x07, 0x3b, 0x99, 0x01, 0x0f, 0x19, 0x18, 0x54, 0x40, 0xe0, 0x60, + 0xee, 0xd0, 0x0e, 0x19, 0x0a, 0x03, 0xa5, 0x7d, 0x05, 0xd0, 0x83, 0x98, 0x5a, 0x96, 0x21, 0x4b, + 0x10, 0x10, 0xe6, 0x17, 0xaf, 0xeb, 0xaf, 0x34, 0x3c, 0xc8, 0x0f, 0xf0, 0x64, 0x3f, 0xd0, 0x0f, + 0xe0, 0x03, 0xfe, 0x10, 0x02, 0x7d, 0x47, 0x2d, 0x58, 0xfc, 0x35, 0xe0, 0xca, 0x0f, 0x19, 0x0a, + 0xf9, 0xf1, 0xe0, 0xb9, 0xc0, 0x81, 0x10, 0x03, 0xe0, 0xbd, 0x4f, 0xea, 0x61, 0xf7, 0xeb, 0xf6, + 0x02, 0xd4, 0x7a, 0xf9, 0xff, 0x15, 0x30, 0xfa, 0x88, 0x68, 0x68, 0xd8, 0x80, 0x12, 0x60, 0x50, + 0x50, 0xf0, 0x03, 0xfc, 0x01, 0xfe, 0x01, 0x7f, 0xa0, 0x7c, 0x28, 0xbf, 0xd0, 0x3e, 0x64, 0x0f, + 0x00, 0x37, 0x00, 0x08, 0x80, 0x20, 0x0b, 0x88, 0x81, 0xa5, 0x04, 0x84, 0x60, 0x40, 0x36, 0x04, + 0x1b, 0x8f, 0x88, 0x01, 0x00, 0xa1, 0x80, 0x1e, 0x00, 0x36, 0xfd, 0xb9, 0x12, 0x02, 0x4c, 0x09, + 0x08, 0x1e, 0x00, 0x61, 0x80, 0x20, 0x60, 0x44, 0x17, 0xdc, 0x7c, 0x62, 0x00, 0x03, 0x67, 0xdb, + 0x81, 0xb1, 0x30, 0x34, 0xb0, 0xa0, 0xaf, 0xa0, 0x80, 0x75, 0x35, 0x20, 0x7c, 0x49, 0xfc, 0x0f, + 0xf5, 0x0d, 0x7f, 0x7e, 0x45, 0x00, 0x53, 0x42, 0x82, 0x83, 0xc0, 0x0c, 0x28, 0x1f, 0x72, 0x3e, + 0xd3, 0xf5, 0x62, 0xd4, 0x00, 0x22, 0xa8, 0x81, 0xec, 0x67, 0x96, 0x02, 0xa0, 0x49, 0x7d, 0xfd, + 0x6b, 0xbf, 0xcc, 0x7c, 0x4a, 0xf8, 0xd0, 0x00, 0x00, 0xcf, 0xd5, 0xd2, 0x23, 0x35, 0x60, 0x01, + 0xf1, 0x60, 0x14, 0xc0, 0xb0, 0xbe, 0xb3, 0x02, 0x0f, 0x89, 0x5f, 0x1b, 0x00, 0x02, 0x0b, 0xfd, + 0x80, 0x00, 0x01, 0x9b, 0xf3, 0x40, 0x42, 0x10, 0x00, 0xd8, 0xb8, 0x0f, 0xa8, 0x17, 0xfe, 0x59, + 0xef, 0x14, 0x61, 0xf2, 0x30, 0x65, 0xfc, 0x51, 0xe2, 0xc1, 0x18, 0xc0, 0x07, 0x5e, 0x68, 0x08, + 0xe8, 0x46, 0xf8, 0x95, 0xf1, 0xb0, 0xf9, 0x13, 0x7f, 0xbc, 0x00, 0x00, 0x32, 0x7e, 0xa8, 0xeb, + 0xcd, 0x03, 0x20, 0x09, 0xa1, 0x81, 0x97, 0xfb, 0x87, 0x80, 0xb0, 0xf9, 0x19, 0x7c, 0xa8, 0x63, + 0xf3, 0xe6, 0x20, 0x22, 0xbd, 0x85, 0x9e, 0x62, 0x00, 0x8b, 0x7c, 0x87, 0x91, 0x00, 0x22, 0xff, + 0x21, 0xe2, 0xa0, 0x08, 0xc7, 0xc8, 0x78, 0x20, 0x02, 0x33, 0xf2, 0x1c, 0x10, 0x41, 0xe3, 0x40, + 0x69, 0x7c, 0x45, 0x72, 0x62, 0xf0, 0x04, 0x7f, 0x60, 0x68, 0x6f, 0x80, 0x00, 0x08, 0x1f, 0xf7, + 0xad, 0x51, 0x03, 0xf3, 0xf8, 0xa0, 0x9d, 0xa8, 0x40, 0x00, 0x23, 0x42, 0x37, 0x46, 0x0f, 0xde, + 0xa6, 0x06, 0xd3, 0x3c, 0x33, 0xe1, 0x78, 0xd8, 0x34, 0x32, 0x14, 0x67, 0xdb, 0xd2, 0x38, 0xaf, + 0xc7, 0x9c, 0xdf, 0xd0, 0x21, 0xe6, 0xd7, 0x80, 0x40, 0x22, 0x3f, 0x21, 0xe8, 0xd8, 0x12, 0xf9, + 0x0f, 0xb4, 0x01, 0x13, 0xf9, 0x0f, 0x46, 0xc0, 0xa7, 0x13, 0x37, 0x1e, 0x67, 0x07, 0x8b, 0x01, + 0xfd, 0xfe, 0x0f, 0xf7, 0x7a, 0xf0, 0x16, 0x36, 0x0a, 0x92, 0x08, 0x08, 0xc1, 0x70, 0xb8, 0x30, + 0x34, 0xf1, 0xf3, 0x72, 0x27, 0x8f, 0x4b, 0x60, 0x21, 0xc4, 0xdd, 0xe2, 0xdf, 0x0b, 0xca, 0x4f, + 0x2e, 0x4f, 0x9c, 0xde, 0x59, 0xe9, 0xf1, 0x55, 0x00, 0x8d, 0xf2, 0x20, 0x53, 0x3c, 0xc4, 0xf6, + 0x46, 0x7e, 0x24, 0xee, 0xf2, 0x0c, 0x0d, 0x81, 0x83, 0xf9, 0x98, 0x0e, 0x00, 0x02, 0x10, 0x11, + 0x01, 0x08, 0x95, 0x2a, 0xfc, 0x28, 0x95, 0x2a, 0x84, 0x80, 0xbf, 0x81, 0x06, 0x80, 0x0d, 0x00, + 0x86, 0xe0, 0x6b, 0xa5, 0xc3, 0xd8, 0x8f, 0x22, 0xa0, 0x3e, 0xe9, 0x8f, 0x90, 0xf2, 0x6b, 0x85, + 0x77, 0x57, 0x99, 0x43, 0x5c, 0x66, 0x5f, 0x9e, 0x85, 0x7c, 0x3f, 0x1f, 0xb3, 0xce, 0xc0, 0x0e, + 0x64, 0x20, 0x0e, 0x20, 0xdc, 0x7e, 0x18, 0x81, 0x90, 0xa3, 0x13, 0x4e, 0x52, 0x71, 0x81, 0x03, + 0xa4, 0x30, 0x30, 0x6c, 0x73, 0x8f, 0xc4, 0x50, 0x60, 0x16, 0x38, 0x03, 0xbf, 0x6f, 0x89, 0x3e, + 0x00, 0x77, 0x00, 0xb1, 0xc0, 0x28, 0x3d, 0x73, 0x98, 0x06, 0xfe, 0x00, 0xe9, 0x81, 0xa3, 0xb8, + 0x1c, 0x85, 0x20, 0x45, 0x45, 0xe1, 0xa1, 0x23, 0x63, 0xa0, 0x29, 0x61, 0x41, 0x27, 0xf4, 0x03, + 0xfa, 0x01, 0x02, 0x05, 0xff, 0xe1, 0x20, 0x34, 0x08, 0x08, 0x04, 0x04, 0x02, 0xff, 0xeb, 0x96, + 0x05, 0x24, 0x8e, 0x0a, 0xb1, 0xce, 0xf2, 0x06, 0xc7, 0xb9, 0x01, 0xd7, 0x20, 0x52, 0x04, 0x03, + 0xe1, 0x47, 0xc4, 0xa4, 0x0b, 0xfd, 0x03, 0x01, 0xc0, 0x47, 0xe6, 0xc0, 0x2c, 0x7c, 0x09, 0x10, + 0x1c, 0x0a, 0xfd, 0x7e, 0xc0, 0xd2, 0x94, 0x7a, 0x1a, 0x06, 0x07, 0xcf, 0x12, 0x2a, 0x8c, 0x1e, + 0xe7, 0x07, 0x08, 0x81, 0x81, 0x91, 0x90, 0x72, 0x26, 0x9e, 0x55, 0x44, 0x0e, 0x4d, 0x21, 0x00, + 0x08, 0x40, 0x02, 0x20, 0x01, 0x17, 0x2c, 0xd4, 0x22, 0x00, 0x88, 0x80, 0x44, 0x40, 0x23, 0xcd, + 0xf8, 0xf1, 0xc8, 0x9b, 0x02, 0x10, 0x0c, 0x02, 0x99, 0x30, 0x00, 0x0a, 0x06, 0x01, 0x4b, 0x18, + 0x00, 0x46, 0x00, 0x29, 0x9c, 0xa3, 0x86, 0x60, 0x11, 0x98, 0x05, 0x32, 0x80, 0xcc, 0xc0, 0xf3, + 0xc3, 0xb8, 0x7a, 0x21, 0x7d, 0xbe, 0xfa, 0xce, 0x2a, 0x9d, 0xfa, 0xa0, 0x3c, 0x32, 0xfb, 0x7d, + 0x13, 0x22, 0x05, 0xeb, 0x0b, 0xbb, 0xb8, 0x00, 0x15, 0xfe, 0xfe, 0x1a, 0x14, 0x7e, 0x1c, 0x00, + 0x01, 0x82, 0x3a, 0xa7, 0xd2, 0x6c, 0x11, 0xdd, 0x00, 0x00, 0x00, 0xc0, 0x40, 0x18, 0x23, 0x5a, + 0x00, 0x80, 0xb0, 0x47, 0x84, 0x7c, 0xa8, 0x03, 0xa7, 0x82, 0x48, 0x83, 0x01, 0x50, 0x11, 0x2a, + 0x37, 0xfb, 0xfc, 0x03, 0x03, 0xd1, 0xa3, 0x35, 0x68, 0xcd, 0x58, 0x40, 0x03, 0xe3, 0x47, 0xc4, + 0xaf, 0x8d, 0x1f, 0x42, 0x84, 0x20, 0x81, 0x08, 0x57, 0xfb, 0xff, 0xd0, 0x98, 0x27, 0xc8, 0xaf, + 0x99, 0x1f, 0x12, 0x04, 0x3e, 0x84, 0xfe, 0x08, 0x1c, 0xc1, 0x31, 0x58, 0x80, 0x3a, 0xd1, 0x99, + 0x8a, 0x40, 0x02, 0x5a, 0x04, 0x00, 0x02, 0x1a, 0x38, 0xf3, 0x08, 0x00, 0x01, 0xda, 0xe3, 0x35, + 0x60, 0x5f, 0x88, 0x00, 0x03, 0x6e, 0xbf, 0xdf, 0xc0, 0xbe, 0x20, 0x00, 0x42, 0x80, 0x01, 0x77, + 0x9e, 0x80, 0xd0, 0x30, 0x4a, 0x32, 0x81, 0xe3, 0x94, 0x04, 0x21, 0x0a, 0x9c, 0xcc, 0x52, 0x03, + 0x7d, 0xa7, 0x0c, 0x51, 0x80, 0x6f, 0xa5, 0xc0, 0x3f, 0x3e, 0x80, 0xa0, 0x22, 0x10, 0x40, 0x68, + 0x17, 0x9f, 0x60, 0x1e, 0x9b, 0x09, 0x52, 0x03, 0x2d, 0x03, 0x81, 0x88, 0x41, 0x3c, 0x65, 0x14, + 0x98, 0xcd, 0x58, 0x6a, 0x04, 0x21, 0x80, 0x9b, 0x81, 0x45, 0x21, 0x24, 0xe1, 0x8c, 0xf1, 0x9a, + 0xb0, 0xa9, 0x38, 0xef, 0xe7, 0x90, 0xdf, 0x98, 0x00, 0x19, 0xa8, 0x18, 0x42, 0x6a, 0xc0, 0x7f, + 0xda, 0x00, 0x00, 0x2b, 0x1e, 0x36, 0x7c, 0xaa, 0xa0, 0x00, 0xc0, 0xf8, 0xa0, 0xbe, 0x60, 0x2e, + 0xb1, 0x09, 0xab, 0x60, 0x3e, 0x38, 0xf9, 0x6f, 0xa9, 0x3e, 0x08, 0x81, 0xa6, 0x8c, 0x13, 0xae, + 0x83, 0x7e, 0x0a, 0xfb, 0x0f, 0x60, 0x86, 0x3e, 0x90, 0x6d, 0xa2, 0x33, 0x56, 0x06, 0xfa, 0xcf, + 0xc5, 0x1f, 0x12, 0x38, 0x49, 0x3d, 0x04, 0x03, 0xa6, 0x42, 0x54, 0x82, 0x3e, 0xd3, 0xd1, 0xd0, + 0x08, 0x58, 0x06, 0xdc, 0x10, 0x85, 0xe8, 0xf8, 0xf8, 0x94, 0x10, 0x84, 0x21, 0xe7, 0xa3, 0x85, + 0xfe, 0xfe, 0xc1, 0xe9, 0x77, 0xa3, 0x27, 0xe7, 0xbd, 0x31, 0x98, 0x17, 0xa1, 0xe2, 0x13, 0xe8, + 0x5a, 0xf1, 0x44, 0x7c, 0x4a, 0x00, 0x00, 0x07, 0x2d, 0x03, 0x2d, 0x05, 0xa3, 0x46, 0x6a, 0xc1, + 0x9e, 0x9f, 0x9f, 0x51, 0xc0, 0x55, 0x1a, 0x13, 0x56, 0x0e, 0xf4, 0xa4, 0x85, 0xfd, 0x4c, 0x47, + 0x10, 0x0d, 0x70, 0x24, 0x9b, 0xfa, 0x45, 0x41, 0x3a, 0x33, 0xea, 0x28, 0x60, 0x00, 0x80, 0x00, + 0xbc, 0x00, 0x80, 0x7b, 0x2e, 0x43, 0x10, 0x0b, 0x00, 0xec, 0x1e, 0x98, 0x8a, 0xb4, 0x26, 0xac, + 0x5f, 0xf9, 0x20, 0x03, 0xf2, 0xc1, 0xdf, 0xca, 0x14, 0x40, 0x07, 0x40, 0x1e, 0x00, 0x3d, 0x10, + 0xe1, 0x37, 0x90, 0x64, 0x17, 0xec, 0x3d, 0x4c, 0xf5, 0x94, 0x20, 0x15, 0x80, 0xdc, 0x3e, 0x74, + 0x7f, 0x87, 0x87, 0xa9, 0xa6, 0x33, 0x56, 0x16, 0xfd, 0xcf, 0xa9, 0x1f, 0x12, 0x23, 0x35, 0x60, + 0xaf, 0xa4, 0x04, 0xf5, 0xb0, 0x1f, 0xe4, 0x3d, 0x75, 0x1c, 0x20, 0xeb, 0xd7, 0x19, 0x00, 0xb8, + 0x04, 0x21, 0x7a, 0xd3, 0xbe, 0x15, 0xeb, 0x4a, 0xf1, 0x84, 0x78, 0x52, 0x3e, 0x25, 0x03, 0x16, + 0x81, 0xc3, 0x7d, 0x59, 0x1f, 0x12, 0x30, 0x50, 0xe3, 0xe1, 0xcf, 0xc5, 0x8f, 0xa1, 0x1c, 0x0e, + 0x9e, 0xd0, 0x0d, 0x7b, 0x18, 0x14, 0xcc, 0x21, 0x04, 0x1b, 0x6a, 0x8c, 0xd5, 0x86, 0xe0, 0x31, + 0x9a, 0xb0, 0x4f, 0xc8, 0x0b, 0x7c, 0x40, 0x37, 0xc4, 0x5c, 0x22, 0x80, 0x3e, 0x54, 0x71, 0x10, + 0xbf, 0x26, 0xf9, 0xa2, 0x1c, 0x0b, 0x82, 0xf0, 0x8f, 0x22, 0x47, 0x8a, 0xab, 0xca, 0xd4, 0x31, + 0x08, 0xf1, 0xe6, 0x51, 0x9a, 0xb7, 0xcc, 0x80, 0x7f, 0xc9, 0xc2, 0x13, 0x08, 0xfd, 0x95, 0xfe, + 0x23, 0xc0, 0x14, 0x0f, 0x08, 0xe1, 0xb5, 0x5f, 0x4a, 0x38, 0x10, 0x47, 0x1b, 0x17, 0x0a, 0x07, + 0x1d, 0x38, 0xe3, 0xcb, 0x42, 0x10, 0x4f, 0x5d, 0x40, 0x3f, 0xf8, 0xe1, 0x0a, 0xe0, 0x45, 0xa8, + 0x47, 0xe0, 0x78, 0x23, 0x0f, 0x91, 0x5f, 0x4a, 0x7f, 0xe3, 0xc9, 0x11, 0xe0, 0x4a, 0x09, 0xfe, + 0x5a, 0xf0, 0xea, 0x8f, 0x21, 0x57, 0x82, 0xa3, 0xfa, 0x47, 0xc4, 0x8e, 0x0d, 0x8f, 0xcc, 0xfe, + 0x11, 0xf1, 0x22, 0x33, 0x56, 0xe1, 0xf9, 0x1f, 0x9a, 0x83, 0x79, 0x2d, 0xe3, 0xf5, 0x23, 0xf6, + 0x50, 0x64, 0x17, 0xce, 0x4f, 0x12, 0x58, 0x5f, 0xe0, 0xc4, 0x32, 0x0d, 0xfc, 0xab, 0xd5, 0x54, + 0x15, 0x04, 0xfd, 0x91, 0xf1, 0x20, 0x32, 0x0d, 0xe1, 0x48, 0xf8, 0x91, 0xe5, 0x48, 0x09, 0xfc, + 0xdb, 0x7b, 0xab, 0x84, 0x22, 0x0d, 0xfd, 0x23, 0xda, 0xd1, 0xf2, 0x20, 0x2a, 0x11, 0xfe, 0x23, + 0xe7, 0x4f, 0x8c, 0x2f, 0x80, 0xe7, 0x1f, 0x09, 0x40, 0x2f, 0x00, 0xee, 0x7f, 0xf5, 0x1f, 0x12, + 0x3c, 0x0d, 0x40, 0xff, 0xa9, 0xc3, 0x1b, 0x01, 0x42, 0xce, 0x18, 0x5b, 0x52, 0xd9, 0x8a, 0x79, + 0xa7, 0xbc, 0xc5, 0x01, 0x08, 0x41, 0x21, 0xb5, 0xfc, 0x1b, 0x93, 0x1e, 0x8f, 0x60, 0x02, 0x98, + 0xf8, 0xe0, 0x0c, 0x1c, 0x2e, 0x15, 0x00, 0xe7, 0x61, 0x08, 0x02, 0xfd, 0x16, 0x5c, 0xdb, 0xf2, + 0xb8, 0x4f, 0x03, 0xfd, 0x81, 0x8a, 0x88, 0x52, 0x05, 0x20, 0x0e, 0xe9, 0xf9, 0xaa, 0xed, 0x7f, + 0xbf, 0xd0, 0x0b, 0x0b, 0x42, 0x60, 0x85, 0xa1, 0x3f, 0x0a, 0x0b, 0x42, 0x40, 0x08, 0xa8, 0x02, + 0x04, 0xa9, 0x60, 0x46, 0x00, 0x45, 0x40, 0x5c, 0xa7, 0xa6, 0xfa, 0x5c, 0x07, 0xf0, 0xe0, 0xa4, + 0x0f, 0x94, 0xc4, 0x16, 0x82, 0x96, 0x82, 0x94, 0x83, 0x71, 0x76, 0x04, 0x94, 0x8f, 0xa1, 0xf3, + 0x40, 0x00, 0x93, 0x85, 0xa2, 0x50, 0xc0, 0x00, 0x28, 0x1c, 0xbb, 0x03, 0x09, 0x12, 0x5e, 0x91, + 0xaf, 0x21, 0x42, 0x05, 0x09, 0x6b, 0xe5, 0x59, 0x27, 0xcf, 0x8f, 0x88, 0x24, 0x00, 0x90, 0x7c, + 0x60, 0x00, 0x00, 0x17, 0x1a, 0x02, 0x40, 0x2c, 0x03, 0x94, 0x1a, 0xf8, 0x02, 0xa0, 0x80, 0xd2, + 0x15, 0xf5, 0x64, 0x00, 0xc0, 0x32, 0x01, 0x83, 0xa4, 0xc0, 0x5e, 0xb2, 0x0e, 0x70, 0x9a, 0x7b, + 0x12, 0x23, 0x35, 0x6f, 0x26, 0x43, 0x7f, 0x40, 0x6a, 0x04, 0xe8, 0x14, 0x04, 0xa4, 0xb3, 0x14, + 0x81, 0x30, 0x2f, 0x16, 0x84, 0xd0, 0x0c, 0x0b, 0x42, 0x6e, 0x14, 0x00, 0x9a, 0x00, 0x87, 0x76, + 0x80, 0x07, 0x98, 0x2c, 0x03, 0x99, 0x9c, 0xf3, 0xbb, 0x7f, 0xb8, 0xa4, 0xdb, 0xde, 0xfc, 0x4a, + 0x00, 0x05, 0xa4, 0xc2, 0x6a, 0xc0, 0xed, 0x3d, 0x15, 0xc1, 0x04, 0xe1, 0x30, 0x2e, 0x2c, 0xf1, + 0x50, 0x69, 0x84, 0xa9, 0x0f, 0xf8, 0xc2, 0xbe, 0x35, 0xa8, 0x87, 0x50, 0x10, 0x0e, 0x00, 0xe5, + 0x1e, 0xc6, 0xa9, 0x55, 0xfe, 0xff, 0x48, 0xf5, 0xe0, 0x53, 0xdc, 0x78, 0x80, 0x10, 0x51, 0x89, + 0x52, 0xc0, 0x06, 0xab, 0x03, 0x14, 0x6f, 0xed, 0x85, 0xde, 0x80, 0x03, 0x09, 0x52, 0xe5, 0xff, + 0x5e, 0x02, 0xbf, 0x8f, 0x8f, 0xc9, 0xcf, 0xe5, 0xeb, 0xf3, 0x72, 0xbb, 0x80, 0x00, 0xc6, 0x6a, + 0xd8, 0x08, 0x95, 0xf4, 0xb2, 0xf9, 0x4f, 0xa1, 0xc1, 0xc2, 0x5a, 0xef, 0xf7, 0xfa, 0x81, 0xdd, + 0xbd, 0xef, 0xee, 0xe0, 0xd1, 0xe5, 0x72, 0xc5, 0xcd, 0xf0, 0x2c, 0x00, 0x03, 0xcb, 0x98, 0xf0, + 0x7f, 0x52, 0x00 +}; + +static BYTE TEST_RDP5_UNCOMPRESSED_DATA[] = +{ + 0x24, 0x02, 0x03, 0x09, 0x00, 0x20, 0x0c, 0x05, 0x10, 0x01, 0x40, 0x0a, 0xff, 0xff, 0x0c, 0x84, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x0d, 0x38, 0x01, 0xc0, 0x10, 0x01, 0x10, + 0x01, 0xcc, 0xff, 0x7f, 0x03, 0x08, 0x00, 0x20, 0x04, 0x05, 0x10, 0x01, 0x40, 0x0a, 0x00, 0x0c, + 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x01, 0x00, 0x00, 0x0d, 0x0a, + 0x0c, 0x0c, 0xff, 0x03, 0xff, 0x02, 0x00, 0x04, 0x00, 0x03, 0x06, 0x00, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x0c, 0x80, 0x00, 0x80, 0x00, 0x06, 0x00, 0x00, 0x48, 0x00, + 0x37, 0x01, 0x02, 0x00, 0x00, 0x01, 0x0c, 0x48, 0x00, 0x37, 0x01, 0x06, 0x01, 0x00, 0x00, 0x04, + 0x24, 0x00, 0x02, 0x01, 0x00, 0x01, 0x0c, 0x00, 0x04, 0x24, 0x00, 0x02, 0x00, 0x00, 0x09, 0x0a, + 0x3d, 0x0f, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, + 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, + 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, + 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x09, 0x18, 0xfb, 0x70, 0x06, 0x00, 0x03, + 0xff, 0xff, 0x00, 0x03, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x0c, 0x00, 0x00, 0x80, 0x0c, 0x00, 0x0f, + 0x00, 0x01, 0x49, 0x08, 0x07, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x72, 0x00, 0x19, + 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0xff, 0xff, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, + 0xf3, 0xf2, 0x0c, 0x08, 0x42, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, + 0x99, 0xd6, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x10, 0x84, 0x11, + 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x3a, 0x01, 0x09, 0x99, 0xd6, 0x19, 0x18, 0xf0, 0x60, 0x11, 0x01, + 0x11, 0x01, 0x01, 0x01, 0x00, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, + 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, 0xf4, + 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, + 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x19, 0x18, + 0xf4, 0x20, 0x10, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, + 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, + 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, + 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, + 0x0a, 0x01, 0x09, 0x19, 0x18, 0xf4, 0x20, 0xff, 0xff, 0x00, 0x11, 0x01, 0x11, 0x01, 0x01, 0x11, + 0xf4, 0x20, 0x10, 0x84, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0xdd, 0x0c, 0xf5, + 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, + 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, + 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, + 0x0a, 0x01, 0x09, 0x19, 0x18, 0xf4, 0x60, 0x00, 0x00, 0x00, 0xd0, 0x0e, 0xd0, 0x0e, 0x0e, 0x0b, + 0x01, 0x01, 0x43, 0x06, 0x02, 0xfc, 0xfc, 0x00, 0x00, 0x30, 0x00, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, + 0xf5, 0x04, 0xff, 0xff, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0x08, + 0x42, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x99, 0xd6, 0x11, 0x0f, + 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x10, 0x84, 0x11, 0x0d, 0x01, 0x0b, 0xf6, + 0x11, 0x3a, 0x01, 0x09, 0x99, 0xd6, 0x19, 0x18, 0xf0, 0x60, 0x11, 0x01, 0x11, 0x01, 0x01, 0x01, + 0x01, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, + 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, + 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x99, + 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x19, 0x18, 0xf4, 0x20, 0x10, 0x00, + 0x00, 0x0f, 0xff, 0x0f, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, + 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, + 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, + 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x19, + 0x18, 0xf4, 0x20, 0xff, 0xff, 0x00, 0x11, 0x01, 0x11, 0x01, 0x01, 0x11, 0xf4, 0x20, 0x10, 0x84, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0xdd, 0x0c, 0xf5, 0x04, 0x08, 0x42, 0x11, + 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, + 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, + 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x19, + 0x18, 0xf4, 0x60, 0x00, 0x00, 0x00, 0xd0, 0x0e, 0xd0, 0x0e, 0x0e, 0x13, 0x02, 0x00, 0x4a, 0x08, + 0x09, 0x3f, 0x3f, 0x21, 0xfd, 0xfd, 0x87, 0x84, 0x84, 0xfc, 0x00, 0x00, 0x00, 0x32, 0x00, 0x19, + 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0xff, 0xff, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, + 0xf3, 0xf2, 0x0c, 0x08, 0x42, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, + 0x99, 0xd6, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x10, 0x84, 0x11, + 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x3a, 0x01, 0x09, 0x99, 0xd6, 0x19, 0x18, 0xf0, 0x60, 0x11, 0x01, + 0x11, 0x01, 0x01, 0x01, 0x02, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, + 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, 0xf4, + 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, + 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x19, 0x18, + 0xf4, 0x20, 0x10, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, + 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, + 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, + 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, + 0x0a, 0x01, 0x09, 0x19, 0x18, 0x54, 0x40, 0x00, 0x00, 0x00, 0x10, 0x10, 0x13, 0x03, 0x02, 0x4a, + 0x06, 0x09, 0x78, 0xcc, 0xcc, 0x18, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x73, 0x00, + 0x19, 0x0a, 0x3f, 0xdd, 0x0c, 0xf5, 0x04, 0xff, 0xff, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, + 0x3e, 0xf3, 0xf2, 0x0c, 0x08, 0x42, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, + 0x0b, 0x99, 0xd6, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x10, 0x84, + 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x3a, 0x01, 0x09, 0x99, 0xd6, 0x19, 0x18, 0xf0, 0x60, 0xd1, + 0x0f, 0xd1, 0x0f, 0x0f, 0x01, 0x03, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, + 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, + 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, + 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x19, + 0x18, 0xf4, 0x20, 0x10, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, + 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, + 0xff, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, + 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, + 0x11, 0x0a, 0x01, 0x09, 0x19, 0x18, 0x54, 0x40, 0x00, 0x00, 0x00, 0x10, 0x10, 0x1b, 0x04, 0x00, + 0x4a, 0x09, 0x09, 0xff, 0x80, 0xff, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0xff, 0x80, 0x00, 0x00, 0x31, 0x00, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, + 0xff, 0xff, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0x08, 0x42, 0x11, + 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x99, 0xd6, 0x11, 0x0f, 0xf3, 0x0b, + 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x10, 0x84, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x3a, + 0x01, 0x09, 0x99, 0xd6, 0x19, 0x18, 0xf0, 0x60, 0x11, 0x01, 0x11, 0x01, 0x01, 0x01, 0x04, 0x19, + 0x0a, 0x3f, 0xdd, 0x0c, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0d, 0x0e, 0xf3, 0x11, 0x3e, + 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0b, + 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, 0xf4, 0x0a, 0x99, 0xd6, 0x11, + 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x19, 0x18, 0xf4, 0x20, 0x10, 0x00, 0x00, 0xcf, + 0x0d, 0xcf, 0x0d, 0x0d, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, + 0x0d, 0x0e, 0xf3, 0x11, 0x3e, 0xf3, 0xf2, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0d, 0xf4, 0x11, + 0x3f, 0x0d, 0x01, 0xf3, 0x0b, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0b, 0x0c, 0xf5, 0x11, 0x3e, 0xf5, + 0xf4, 0x0a, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0b, 0xf6, 0x11, 0x0a, 0x01, 0x09, 0x19, 0x18, 0xf4, + 0x20, 0xff, 0xff, 0x00, 0x11, 0x01, 0x11, 0x01, 0x01, 0x11, 0xf4, 0x20, 0x10, 0x84, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0xee, 0x34, 0x3c, 0x08, 0x2d, 0x09, 0x59, 0x0d, 0x97, + 0xff, 0x00, 0x02, 0x70, 0x0d, 0x0e, 0x51, 0xc2, 0x10, 0x20, 0x1c, 0x51, 0xc2, 0x12, 0xe0, 0xd6, + 0x51, 0xc2, 0x12, 0x30, 0x1c, 0x19, 0x0a, 0x32, 0x12, 0x10, 0x84, 0x59, 0x0d, 0xc6, 0xcc, 0x12, + 0xd0, 0xf2, 0x51, 0xc2, 0x10, 0x20, 0x1c, 0x51, 0xc2, 0x12, 0xe0, 0xd6, 0x51, 0xc2, 0x12, 0x30, + 0x1c, 0x19, 0x0a, 0x3f, 0x0a, 0x12, 0xb9, 0xf9, 0x08, 0x42, 0x11, 0x0f, 0xf6, 0x0a, 0x09, 0xf6, + 0x11, 0x3e, 0xf6, 0xf7, 0x09, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x08, 0xf7, 0x11, 0x3f, 0x08, 0x01, + 0xf8, 0x08, 0x10, 0x84, 0x11, 0x0f, 0xf8, 0x08, 0x07, 0xf8, 0x11, 0x3e, 0xf8, 0xf9, 0x07, 0x99, + 0xd6, 0x11, 0x0d, 0x01, 0x06, 0xf9, 0x11, 0x0a, 0x01, 0x06, 0x19, 0x18, 0xf5, 0x60, 0x05, 0x00, + 0x00, 0x00, 0xef, 0x5a, 0xec, 0x57, 0x57, 0x0f, 0x00, 0x00, 0x46, 0x06, 0x05, 0xcc, 0x78, 0x30, + 0x78, 0xcc, 0x00, 0x00, 0x00, 0x72, 0x00, 0x19, 0x0a, 0x3f, 0x13, 0xfe, 0xfa, 0x04, 0xff, 0xff, + 0x11, 0x0f, 0xf6, 0x0a, 0x09, 0xf6, 0x11, 0x3e, 0xf6, 0xf7, 0x09, 0x08, 0x42, 0x11, 0x0d, 0x01, + 0x08, 0xf7, 0x11, 0x3f, 0x08, 0x01, 0xf8, 0x08, 0x99, 0xd6, 0x11, 0x0f, 0xf8, 0x08, 0x07, 0xf8, + 0x11, 0x3e, 0xf8, 0xf9, 0x07, 0x10, 0x84, 0x11, 0x0d, 0x01, 0x06, 0xf9, 0x11, 0x3a, 0x01, 0x06, + 0x99, 0xd6, 0x19, 0x18, 0xf0, 0x60, 0x0c, 0x01, 0x0c, 0x01, 0x01, 0x01, 0x00, 0x19, 0x0a, 0x3f, + 0x13, 0xfe, 0xfa, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf6, 0x0a, 0x09, 0xf6, 0x11, 0x3e, 0xf6, 0xf7, + 0x09, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x08, 0xf7, 0x11, 0x3f, 0x08, 0x01, 0xf8, 0x08, 0x10, 0x84, + 0x11, 0x0f, 0xf8, 0x08, 0x07, 0xf8, 0x11, 0x3e, 0xf8, 0xf9, 0x07, 0x99, 0xd6, 0x11, 0x0d, 0x01, + 0x06, 0xf9, 0x11, 0x0a, 0x01, 0x06, 0x19, 0x18, 0xf4, 0x20, 0x10, 0x00, 0x00, 0x0a, 0xff, 0x0a, + 0xff, 0xff, 0x19, 0x0a, 0x3f, 0x13, 0xfe, 0xfa, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf6, 0x0a, 0x09, + 0xf6, 0x11, 0x3e, 0xf6, 0xf7, 0x09, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x08, 0xf7, 0x11, 0x3f, 0x08, + 0x01, 0xf8, 0x08, 0x10, 0x84, 0x11, 0x0f, 0xf8, 0x08, 0x07, 0xf8, 0x11, 0x3e, 0xf8, 0xf9, 0x07, + 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x06, 0xf9, 0x11, 0x0a, 0x01, 0x06, 0x19, 0x18, 0xf4, 0x20, 0xff, + 0xff, 0x00, 0x0c, 0x01, 0x0c, 0x01, 0x01, 0x11, 0xf4, 0x20, 0x10, 0x84, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x19, 0x0a, 0x0f, 0x09, 0xfe, 0x09, 0x09, 0x19, 0x18, 0xf5, 0x60, 0x06, 0xff, 0xff, + 0x00, 0x09, 0xfe, 0x12, 0x07, 0x07, 0x23, 0x05, 0x03, 0x4d, 0x0d, 0x0d, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x88, 0x01, 0x10, 0x02, 0x20, 0x04, 0x40, 0x08, 0x88, + 0x11, 0x10, 0x22, 0x20, 0x44, 0x40, 0x00, 0x00, 0x6f, 0x00, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, + 0x1f, 0x06, 0x04, 0x4c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x60, 0x00, 0xc0, + 0x01, 0x90, 0x03, 0x30, 0x06, 0x60, 0x0c, 0xc0, 0x19, 0x90, 0x33, 0x30, 0x66, 0x60, 0x70, 0x00, + 0x19, 0x0a, 0x37, 0xe3, 0x10, 0xf1, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, + 0xf1, 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, + 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, + 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, 0xf4, 0x60, 0x00, 0x00, + 0x00, 0xd6, 0x12, 0xd2, 0x0e, 0x0e, 0x0f, 0x07, 0x01, 0x48, 0x09, 0x04, 0x08, 0x00, 0x1c, 0x00, + 0x3e, 0x00, 0x7f, 0x00, 0x35, 0x00, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x10, 0x84, 0x11, + 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x0e, 0xf1, 0xf2, 0x0e, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, + 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x99, 0xd6, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x0e, 0xf3, + 0xf4, 0x0c, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x0a, 0x01, 0x0b, 0x19, 0x18, 0xf0, 0x60, 0x11, + 0x01, 0x11, 0x01, 0x01, 0x01, 0x07, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, + 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, 0xf1, 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, + 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, + 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, + 0xd6, 0x19, 0x18, 0xf4, 0x20, 0x10, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, 0xff, 0x19, 0x0a, 0x3f, + 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, 0xf1, 0xf2, + 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x10, 0x84, + 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, + 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, 0xf4, 0x20, 0xff, 0xff, 0x00, 0x11, + 0x01, 0x11, 0x01, 0x01, 0x11, 0xf4, 0x20, 0x10, 0x84, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19, + 0x0a, 0x3f, 0xdd, 0x0e, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, + 0xf1, 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, + 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, + 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, 0xf4, 0x60, 0x00, 0x00, + 0x00, 0xd0, 0x10, 0xd0, 0x10, 0x10, 0x0f, 0x08, 0x01, 0x48, 0x09, 0x04, 0x7f, 0x00, 0x3e, 0x00, + 0x1c, 0x00, 0x08, 0x00, 0x36, 0x00, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x10, 0x84, 0x11, + 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x0e, 0xf1, 0xf2, 0x0e, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, + 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x99, 0xd6, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x0e, 0xf3, + 0xf4, 0x0c, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x0a, 0x01, 0x0b, 0x19, 0x18, 0xf0, 0x60, 0x11, + 0x01, 0x11, 0x01, 0x01, 0x01, 0x08, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, + 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, 0xf1, 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, + 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, + 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, + 0xd6, 0x19, 0x18, 0xf4, 0x20, 0x10, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, 0xff, 0x19, 0x0a, 0x3f, + 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, 0xf1, 0xf2, + 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x10, 0x84, + 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, + 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, 0xf4, 0x20, 0xff, 0xff, 0x00, 0x11, + 0x01, 0x11, 0x01, 0x01, 0x11, 0xf4, 0x20, 0x10, 0x84, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19, + 0x0a, 0x3f, 0xdd, 0x0e, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, + 0xf1, 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, + 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, + 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, 0xf4, 0x60, 0x00, 0x00, + 0x00, 0xd0, 0x10, 0xd0, 0x10, 0x10, 0x13, 0x09, 0x04, 0x4b, 0x04, 0x09, 0x00, 0x80, 0xc0, 0xe0, + 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, + 0x04, 0x10, 0x84, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x0e, 0xf1, 0xf2, 0x0e, 0x11, 0x0d, + 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x99, 0xd6, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, + 0xf3, 0x11, 0x0e, 0xf3, 0xf4, 0x0c, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x0a, 0x01, 0x0b, 0x19, + 0x18, 0xf0, 0x60, 0x11, 0x01, 0x11, 0x01, 0x01, 0x01, 0x09, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, + 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, 0xf1, 0xf2, 0x0e, 0x99, 0xd6, + 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x10, 0x84, 0x11, 0x0f, 0xf3, + 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, + 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, 0xf4, 0x20, 0x10, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, + 0xff, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, + 0x11, 0x3e, 0xf1, 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, + 0xf3, 0x0d, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, 0x0c, 0xff, + 0xff, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, 0xf4, 0x20, + 0xff, 0xff, 0x00, 0x11, 0x01, 0x11, 0x01, 0x01, 0x11, 0xf4, 0x20, 0x10, 0x84, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0xdd, 0x0e, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, + 0x0e, 0xf1, 0x11, 0x3e, 0xf1, 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, + 0x0d, 0x01, 0xf3, 0x0d, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, + 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, + 0xf4, 0x60, 0x00, 0x00, 0x00, 0xd0, 0x10, 0xd0, 0x10, 0x10, 0x13, 0x0a, 0x03, 0x4b, 0x04, 0x09, + 0x00, 0x10, 0x30, 0x70, 0xf0, 0x70, 0x30, 0x10, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x19, 0x0a, + 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x10, 0x84, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x0e, 0xf1, + 0xf2, 0x0e, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x99, 0xd6, 0x11, + 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x0e, 0xf3, 0xf4, 0x0c, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, + 0x0a, 0x01, 0x0b, 0x19, 0x18, 0xf0, 0x60, 0x11, 0x01, 0x11, 0x01, 0x01, 0x01, 0x0a, 0x19, 0x0a, + 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, 0xf1, + 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x10, + 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, 0x0d, + 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, 0x19, 0x18, 0xf4, 0x20, 0x10, 0x00, 0x00, + 0x0f, 0xff, 0x0f, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0x1d, 0xfe, 0xf5, 0x04, 0x08, 0x42, 0x11, 0x0f, + 0xf1, 0x0f, 0x0e, 0xf1, 0x11, 0x3e, 0xf1, 0xf2, 0x0e, 0x99, 0xd6, 0x11, 0x0d, 0x01, 0x0d, 0xf2, + 0x11, 0x3f, 0x0d, 0x01, 0xf3, 0x0d, 0x10, 0x84, 0x11, 0x0f, 0xf3, 0x0d, 0x0c, 0xf3, 0x11, 0x3e, + 0xf3, 0xf4, 0x0c, 0xff, 0xff, 0x11, 0x0d, 0x01, 0x0b, 0xf4, 0x11, 0x3a, 0x01, 0x0b, 0x99, 0xd6, + 0x19, 0x18, 0xf4, 0x20, 0xff, 0xff, 0x00, 0x11, 0x01, 0x11, 0x01, 0x01, 0x11, 0xf4, 0x20, 0x10, + 0x84, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0xce, 0x0e, 0x01, 0x01, 0xff, 0xff, + 0x1d, 0x18, 0xf4, 0x60, 0x0e, 0xe2, 0x00, 0x0b, 0x00, 0xee, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x0e, + 0xce, 0x0f, 0x0f, 0x13, 0x0b, 0x04, 0x4b, 0x04, 0x09, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xe0, 0xc0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x19, 0x0a, 0x01, 0x0d, 0x19, 0x18, 0x50, 0x40, 0x0d, + 0x0d, 0x0f, 0x0c, 0x03, 0x4a, 0x07, 0x08, 0x00, 0x02, 0x06, 0x8e, 0xdc, 0xf8, 0x70, 0x20, 0x61, + 0x00, 0x19, 0x0a, 0x01, 0x0d, 0x19, 0x18, 0x50, 0x40, 0x0d, 0x0d, 0x0f, 0x0d, 0x04, 0x4a, 0x06, + 0x06, 0x78, 0xfc, 0xfc, 0xfc, 0xfc, 0x78, 0x00, 0x00, 0x68, 0x00, 0x19, 0x0a, 0x3d, 0x0d, 0x02, + 0x02, 0x99, 0xd6, 0x19, 0x18, 0xd0, 0x60, 0x0e, 0x10, 0x02, 0x02, 0x13, 0x0e, 0x02, 0x4a, 0x0b, + 0x05, 0x04, 0x00, 0x0e, 0x00, 0x1f, 0x00, 0x3f, 0x80, 0x7f, 0xc0, 0x00, 0x00, 0x35, 0x00, 0x19, + 0x0a, 0x01, 0x0f, 0x19, 0x18, 0x54, 0x40, 0x10, 0x00, 0x00, 0x0f, 0x0f, 0x01, 0x0e, 0x19, 0x0a, + 0x03, 0xca, 0x0f, 0x19, 0x18, 0xf4, 0x20, 0xff, 0xff, 0x00, 0xcb, 0x10, 0xcb, 0x10, 0x10, 0x11, + 0xf4, 0x20, 0x10, 0x84, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19, 0x0a, 0x01, 0x0f, 0x19, 0x18, + 0x54, 0x40, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x13, 0x0f, 0x02, 0x4a, 0x0b, 0x05, 0x7f, 0xc0, 0x3f, + 0x80, 0x1f, 0x00, 0x0e, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0x00, 0x19, 0x0a, 0x01, 0x0f, 0x19, + 0x18, 0x54, 0x40, 0x10, 0x00, 0x00, 0x0f, 0x0f, 0x01, 0x0f, 0x19, 0x0a, 0x01, 0x0f, 0x19, 0x18, + 0xf4, 0x20, 0xff, 0xff, 0x00, 0x10, 0x01, 0x10, 0x01, 0x01, 0x11, 0xf4, 0x20, 0x10, 0x84, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x19, 0x0a, 0x3f, 0xd3, 0x0f, 0xfe, 0xfe, 0xff, 0xff, 0x19, 0x18, + 0xf4, 0x60, 0x00, 0x00, 0x00, 0xd3, 0x0f, 0xd1, 0x0d, 0x0d, 0x1b, 0x10, 0x02, 0x4c, 0x0a, 0x0a, + 0x1e, 0x00, 0x7f, 0x80, 0x7f, 0x80, 0xff, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x7f, 0x80, + 0x7f, 0x80, 0x1e, 0x00, 0x6e, 0x00, 0x11, 0x00, 0x40, 0x17, 0x11, 0x03, 0x4a, 0x09, 0x08, 0x01, + 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0xc3, 0x00, 0x3c, 0x00, 0x6d, + 0x00, 0x11, 0x00, 0x40, 0x17, 0x12, 0x02, 0x4c, 0x09, 0x08, 0x1e, 0x00, 0x61, 0x80, 0x40, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x6c, 0x00, 0x11, 0x00, 0x40, 0x1b, + 0x13, 0x03, 0x4b, 0x0a, 0x0a, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x80, 0x00, 0x80, 0xc3, 0x00, 0x3c, 0x00, 0x6b, 0x00, 0x11, 0x00, 0x40, 0x1b, 0x14, + 0x01, 0x4d, 0x0a, 0x0a, 0x0f, 0x00, 0x30, 0xc0, 0x40, 0x00, 0x40, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x6a, 0x00, 0x11, 0x54, 0x40, 0xff, 0xff, 0x00, + 0x0d, 0x0d, 0x1b, 0x15, 0x02, 0x4b, 0x09, 0x09, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, + 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0x00, 0x00, 0x67, 0x00, 0x11, 0x04, + 0x40, 0x99, 0xd6, 0x00, 0x1f, 0x16, 0x01, 0x4c, 0x0b, 0x0b, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0xff, 0xe0, + 0x00, 0x00, 0x66, 0x00, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x1b, 0x17, 0x01, 0x4c, 0x0a, 0x0a, + 0xff, 0xc0, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x65, 0x00, 0x11, 0x04, 0x40, 0xff, 0xff, 0x00, 0x23, 0x18, 0x00, 0x4d, + 0x0d, 0x0d, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0xff, 0xf8, 0x00, 0x00, 0x64, 0x00, + 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x1f, 0x19, 0x00, 0x4d, 0x0c, 0x0c, 0xff, 0xf0, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x63, 0x00, 0x11, 0x54, 0x40, 0xff, 0xff, 0x00, 0x0d, 0x0d, 0x01, 0x15, + 0x11, 0x04, 0x40, 0x99, 0xd6, 0x00, 0x01, 0x16, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x01, 0x17, + 0x11, 0x04, 0x40, 0xff, 0xff, 0x00, 0x01, 0x18, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x01, 0x19, + 0x11, 0x04, 0x40, 0x00, 0x00, 0x00, 0x0f, 0x1a, 0x03, 0x4b, 0x07, 0x08, 0x00, 0x02, 0x06, 0x8e, + 0xdc, 0xf8, 0x70, 0x20, 0x62, 0x00, 0x11, 0x54, 0x40, 0x99, 0xd6, 0x00, 0x0d, 0x0d, 0x01, 0x15, + 0x11, 0x00, 0x40, 0x01, 0x16, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x01, 0x17, 0x11, 0x04, 0x40, + 0xff, 0xff, 0x00, 0x01, 0x18, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x01, 0x19, 0x11, 0x54, 0x40, + 0x99, 0xd6, 0x00, 0x0d, 0x0d, 0x01, 0x15, 0x11, 0x00, 0x40, 0x01, 0x16, 0x11, 0x04, 0x40, 0x08, + 0x42, 0x00, 0x01, 0x17, 0x11, 0x04, 0x40, 0xff, 0xff, 0x00, 0x01, 0x18, 0x11, 0x04, 0x40, 0x10, + 0x84, 0x00, 0x01, 0x19, 0x11, 0x04, 0x40, 0x00, 0x00, 0x00, 0x01, 0x1a, 0x11, 0xf4, 0x60, 0x99, + 0xd6, 0x00, 0xcc, 0x0d, 0xcc, 0x0d, 0x0d, 0x01, 0x15, 0x11, 0x00, 0x40, 0x01, 0x16, 0x11, 0x04, + 0x40, 0x08, 0x42, 0x00, 0x01, 0x17, 0x11, 0x04, 0x40, 0xff, 0xff, 0x00, 0x01, 0x18, 0x11, 0x04, + 0x40, 0x10, 0x84, 0x00, 0x01, 0x19, 0x11, 0x00, 0x40, 0x01, 0x1a, 0x19, 0x0a, 0x33, 0x0d, 0x0d, + 0x00, 0x00, 0x19, 0x18, 0x54, 0x40, 0xff, 0xff, 0x00, 0x0d, 0x0d, 0x01, 0x10, 0x11, 0x04, 0x40, + 0x99, 0xd6, 0x00, 0x01, 0x11, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x01, 0x12, 0x11, 0x04, 0x40, + 0xff, 0xff, 0x00, 0x01, 0x13, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x01, 0x14, 0x19, 0x0a, 0x01, + 0x0d, 0x19, 0x18, 0x54, 0x40, 0xff, 0xff, 0x00, 0x0d, 0x0d, 0x01, 0x10, 0x11, 0x04, 0x40, 0x99, + 0xd6, 0x00, 0x01, 0x11, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x01, 0x12, 0x11, 0x04, 0x40, 0xff, + 0xff, 0x00, 0x01, 0x13, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x01, 0x14, 0x11, 0x04, 0x40, 0x00, + 0x00, 0x00, 0x0b, 0x1b, 0x05, 0x49, 0x04, 0x04, 0x60, 0xf0, 0xf0, 0x60, 0x69, 0x00, 0x19, 0x0a, + 0x01, 0x0d, 0x19, 0x18, 0x54, 0x40, 0x99, 0xd6, 0x00, 0x0d, 0x0d, 0x01, 0x10, 0x11, 0x00, 0x40, + 0x01, 0x11, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x01, 0x12, 0x11, 0x04, 0x40, 0xff, 0xff, 0x00, + 0x01, 0x13, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x01, 0x14, 0x19, 0x0a, 0x01, 0x0d, 0x19, 0x18, + 0x54, 0x40, 0x99, 0xd6, 0x00, 0x0d, 0x0d, 0x01, 0x10, 0x11, 0x00, 0x40, 0x01, 0x11, 0x11, 0x04, + 0x40, 0x08, 0x42, 0x00, 0x01, 0x12, 0x11, 0x04, 0x40, 0xff, 0xff, 0x00, 0x01, 0x13, 0x11, 0x04, + 0x40, 0x10, 0x84, 0x00, 0x01, 0x14, 0x11, 0x04, 0x40, 0x00, 0x00, 0x00, 0x01, 0x1b, 0x19, 0x0a, + 0x03, 0xcc, 0x0d, 0x19, 0x18, 0xf4, 0x60, 0x99, 0xd6, 0x00, 0xcc, 0x0d, 0xcc, 0x0d, 0x0d, 0x01, + 0x10, 0x11, 0x00, 0x40, 0x01, 0x11, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x01, 0x12, 0x11, 0x04, + 0x40, 0xff, 0xff, 0x00, 0x01, 0x13, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x01, 0x14, 0x11, 0x00, + 0x40, 0x01, 0x1b, 0x03, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x08, 0x08, 0x81, 0x08, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0x09, 0x01, 0x7f, 0x02, 0x0d, 0x00, 0x1a, 0x01, 0x0d, + 0x00, 0x0d, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x99, 0xd6, 0x00, 0x81, 0x19, 0x18, 0x54, 0x40, 0x99, + 0xd6, 0x00, 0x0d, 0x0d, 0x01, 0x16, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x01, 0x17, 0x11, 0x04, + 0x40, 0xff, 0xff, 0x00, 0x01, 0x18, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x01, 0x19, 0x11, 0x00, + 0x40, 0x01, 0x1a, 0x11, 0x54, 0x40, 0x99, 0xd6, 0x00, 0x0d, 0x0d, 0x01, 0x15, 0x11, 0x00, 0x40, + 0x01, 0x16, 0x11, 0x04, 0x40, 0x08, 0x42, 0x00, 0x01, 0x17, 0x11, 0x04, 0x40, 0xff, 0xff, 0x00, + 0x01, 0x18, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x01, 0x19, 0x11, 0x00, 0x40, 0x01, 0x1a, 0x11, + 0x54, 0x40, 0x99, 0xd6, 0x00, 0x0d, 0x0d, 0x01, 0x15, 0x11, 0x00, 0x40, 0x01, 0x16, 0x11, 0x04, + 0x40, 0x08, 0x42, 0x00, 0x01, 0x17, 0x11, 0x04, 0x40, 0xff, 0xff, 0x00, 0x01, 0x18, 0x11, 0x04, + 0x40, 0x10, 0x84, 0x00, 0x01, 0x19, 0x11, 0x00, 0x40, 0x01, 0x1a, 0x19, 0x0a, 0x31, 0x34, 0xff, + 0xff, 0x19, 0x18, 0x54, 0x40, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x1b, 0x1c, 0x02, 0x4b, 0x09, 0x09, + 0xc1, 0x80, 0xe3, 0x80, 0x77, 0x00, 0x3e, 0x00, 0x1c, 0x00, 0x3e, 0x00, 0x77, 0x00, 0xe3, 0x80, + 0xc1, 0x80, 0x00, 0x00, 0x72, 0x00, 0x19, 0x0a, 0x03, 0xcc, 0x0d, 0x1d, 0x18, 0xf0, 0x60, 0xa0, + 0x45, 0x45, 0xcc, 0x0d, 0xcc, 0x0d, 0x0d, 0x1b, 0x1d, 0x01, 0x4b, 0x0a, 0x09, 0x3f, 0xc0, 0x3f, + 0xc0, 0x20, 0x40, 0xff, 0x40, 0xff, 0x40, 0x81, 0xc0, 0x81, 0x00, 0x81, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x32, 0x00, 0x19, 0x0a, 0x01, 0x0d, 0x19, 0x18, 0x50, 0x40, 0x0d, 0x0d, 0x1b, 0x1e, 0x01, + 0x4c, 0x0a, 0x0a, 0xff, 0xc0, 0xff, 0xc0, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, + 0x40, 0x80, 0x40, 0x80, 0x40, 0xff, 0xc0, 0x31, 0x00, 0x19, 0x0a, 0x01, 0x0d, 0x19, 0x18, 0x50, + 0x40, 0x0d, 0x0d, 0x0b, 0x1f, 0x02, 0x44, 0x07, 0x02, 0xfe, 0xfe, 0x00, 0x00, 0x30, 0x00, 0x19, + 0x0a, 0x3d, 0x0d, 0x03, 0x03, 0x99, 0xd6, 0x19, 0x18, 0xd4, 0x60, 0xff, 0xff, 0x00, 0x0e, 0x11, + 0x03, 0x03, 0x23, 0x20, 0x00, 0x4d, 0x0d, 0x0d, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, + 0x10, 0x00, 0x88, 0x00, 0x44, 0x00, 0x22, 0x00, 0x11, 0x00, 0x88, 0x80, 0x44, 0x40, 0x22, 0x20, + 0x11, 0x10, 0x00, 0x00, 0x78, 0x00, 0x11, 0x04, 0x40, 0x10, 0x84, 0x00, 0x1f, 0x21, 0x00, 0x4c, + 0x0c, 0x0c, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x98, 0x00, 0xcc, 0x00, + 0x66, 0x00, 0x33, 0x00, 0x99, 0x80, 0xcc, 0xc0, 0x66, 0x60, 0x79, 0x00, 0x19, 0x0a, 0x3d, 0x10, + 0xfd, 0xfd, 0xff, 0xff, 0x19, 0x18, 0xd4, 0x60, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0xfd, 0xfd, 0x13, + 0x22, 0x05, 0x4b, 0x04, 0x09, 0x00, 0x10, 0x30, 0x70, 0xf0, 0x70, 0x30, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x77, 0x00, 0x02, 0xff, 0xff, 0x0d, 0x0a, 0x3f, 0x0e, 0x00, 0x00, 0xff, 0x03, 0xff, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x48, 0x00, 0x37, + 0x01, 0x02, 0x02, 0x00, 0x09, 0x00, 0x0c, 0x48, 0x00, 0x37, 0x01, 0x03, 0xcf, 0x04, 0xa2, 0x0c, + 0x05, 0x40, 0x44, 0xd1, 0xff, 0xff, 0x80, 0x00, 0xff, 0xff, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x00, + 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x00, + 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x84, 0x08, 0x42, 0xff, 0xff, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x84, + 0xff, 0xff, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x84, + 0xff, 0xff, 0x99, 0xd6, 0x10, 0x84, 0x08, 0x42, 0x1c, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x75, 0xc6, 0x66, 0x29, 0x00, 0x09, 0x68, 0x10, 0x00, 0x08, 0x68, 0x10, 0x84, + 0x00, 0x20, 0x00, 0x07, 0x6b, 0x99, 0xd6, 0x05, 0x6b, 0x99, 0xd6, 0x00, 0x03, 0x6e, 0xff, 0xff, + 0x02, 0x6e, 0xff, 0xff, 0x00, 0x10, 0xc0, 0x00, 0xf7, 0xbd, 0x01, 0xc0, 0x00, 0x08, 0x42, 0xc0, + 0x00, 0xff, 0xff, 0x81, 0x08, 0x42, 0xce, 0x66, 0x29, 0x01, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0xfd, + 0x2e, 0x01, 0x81, 0x08, 0x42, 0xce, 0x66, 0x29, 0x02, 0x81, 0x10, 0x84, 0x06, 0x82, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xcd, 0x89, 0x52, 0x03, 0x2d, 0x03, 0x83, 0x10, 0x84, 0x99, 0xd6, 0x99, 0xd6, + 0xc9, 0x99, 0xd6, 0x1a, 0x82, 0x10, 0x00, 0x10, 0x00, 0x0a, 0x29, 0x09, 0x27, 0x0c, 0x67, 0x99, + 0xd6, 0x15, 0x27, 0x1d, 0x82, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x67, 0x99, 0xd6, 0x00, 0x19, 0xd0, + 0x30, 0x89, 0xd6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x99, 0xd6, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x83, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xd8, 0x89, + 0xd6, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x83, 0x99, 0xd6, 0x10, 0x00, 0x10, + 0x00, 0x1a, 0x68, 0x00, 0x00, 0x09, 0x86, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x99, 0xd6, 0x18, 0x68, 0x00, 0x00, 0x1b, 0x68, 0x99, 0xd6, 0x06, 0x86, 0x99, 0xd6, 0x10, + 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x99, 0xd6, 0x19, 0x6b, 0x99, 0xd6, 0x03, 0xcc, 0x89, + 0x52, 0x08, 0x68, 0x99, 0xd6, 0x05, 0x6b, 0x99, 0xd6, 0x04, 0x2c, 0x03, 0x6e, 0x08, 0x42, 0x02, + 0x6e, 0xff, 0xff, 0x02, 0x6e, 0xff, 0xff, 0x02, 0x6e, 0x08, 0x42, 0x10, 0x81, 0x08, 0x42, 0x70, + 0xff, 0xff, 0x60, 0x00, 0x08, 0x42, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0x81, 0x08, 0x42, 0xce, 0x66, + 0x29, 0x01, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0xfd, 0x2e, 0x02, 0xcd, 0x89, 0x52, 0x03, 0x89, 0x10, + 0x84, 0x99, 0xd6, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x2d, 0x03, 0x2d, 0x05, 0xc6, 0x99, 0xd6, 0x0c, 0x84, 0x99, 0xd6, 0x99, 0xd6, 0x99, + 0xd6, 0x99, 0xd6, 0x0a, 0xc6, 0x89, 0xd6, 0x0e, 0x82, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x84, 0x99, + 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x1c, 0x40, 0x35, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd0, 0x4e, 0x99, 0xd6, 0x03, 0x00, 0x00, 0x60, 0x00, 0x80, 0x01, 0x78, 0x01, 0x00, 0x82, + 0x10, 0x00, 0x10, 0x00, 0x0c, 0x40, 0x2c, 0x03, 0xe0, 0x05, 0x00, 0x00, 0x00, 0xd6, 0x89, 0xd6, + 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x4e, 0x99, 0xd6, 0x3b, 0x00, 0x00, 0x00, 0x28, 0x80, + 0x1d, 0x00, 0x78, 0x00, 0x86, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x99, + 0xd6, 0x0c, 0x85, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x00, 0x00, 0x40, 0x2b, 0x01, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x89, 0xd6, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x99, + 0xd6, 0x16, 0x86, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x0a, + 0x69, 0x99, 0xd6, 0x04, 0xcc, 0x89, 0x52, 0x07, 0x69, 0x99, 0xd6, 0x08, 0x68, 0x99, 0xd6, 0x03, + 0x6e, 0xff, 0xff, 0x02, 0x6e, 0x08, 0x42, 0x02, 0x6e, 0xff, 0xff, 0x02, 0x6e, 0xff, 0xff, 0x01, + 0x70, 0x08, 0x42, 0x70, 0xff, 0xff, 0x60, 0x00, 0x08, 0x42, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0x81, + 0x08, 0x42, 0xce, 0x66, 0x29, 0x01, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0xfd, 0x2e, 0x02, 0xcd, 0x89, + 0x52, 0x03, 0x8a, 0x10, 0x84, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x2d, 0x03, 0x8d, 0x99, 0xd6, 0x99, 0xd6, 0x99, + 0xd6, 0x99, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x99, + 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x06, 0xc6, 0x99, 0xd6, 0x1a, 0xc6, 0x89, 0xd6, 0x0a, 0x66, 0x10, + 0x84, 0x1b, 0x6a, 0x99, 0xd6, 0x1b, 0x81, 0x99, 0xd6, 0x09, 0x6a, 0x99, 0xd6, 0x16, 0x6a, 0x99, + 0xd6, 0x06, 0x6a, 0x99, 0xd6, 0xf0, 0x94, 0x01, 0xcc, 0x89, 0x52, 0x00, 0x03, 0x6e, 0xff, 0xff, + 0x02, 0x6e, 0x08, 0x42, 0x02, 0x6e, 0xff, 0xff, 0x02, 0x6e, 0xff, 0xff, 0x01, 0x70, 0x08, 0x42, + 0x70, 0xff, 0xff, 0x60, 0x00, 0x08, 0x42, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0x81, 0x08, 0x42, 0xce, + 0x66, 0x29, 0x01, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0xfd, 0x2e, 0x02, 0xcd, 0x89, 0x52, 0x03, 0x10, + 0x2d, 0x03, 0x2d, 0x17, 0x88, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, + 0xd6, 0x00, 0x00, 0x00, 0x00, 0x18, 0x88, 0xff, 0xff, 0xff, 0xff, 0x99, 0xd6, 0x99, 0xd6, 0x99, + 0xd6, 0x99, 0xd6, 0xff, 0xff, 0xff, 0xff, 0x07, 0x88, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x99, + 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x09, 0x88, 0x99, 0xd6, 0x00, 0x00, 0x00, + 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x07, 0x88, 0x10, 0x00, 0x10, + 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x00, 0x10, 0x00, 0x08, 0x89, 0x10, + 0x84, 0x10, 0x84, 0xff, 0xff, 0xff, 0xff, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x84, 0x10, 0x84, 0x99, + 0xd6, 0x07, 0x88, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x00, 0x00, 0x00, + 0x00, 0x99, 0xd6, 0x0a, 0x86, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, + 0xd6, 0x08, 0x88, 0x99, 0xd6, 0x10, 0x00, 0x10, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x00, 0x10, + 0x00, 0x99, 0xd6, 0x08, 0x88, 0x99, 0xd6, 0x10, 0x84, 0x10, 0x84, 0xff, 0xff, 0xff, 0xff, 0x10, + 0x84, 0x10, 0x84, 0x99, 0xd6, 0x09, 0x86, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x99, 0xd6, 0x0c, 0x84, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x0a, 0x86, 0x99, + 0xd6, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x99, 0xd6, 0x0a, 0x86, 0x99, 0xd6, 0x10, + 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x99, 0xd6, 0x0b, 0x84, 0x99, 0xd6, 0x00, 0x00, 0x00, + 0x00, 0x99, 0xd6, 0x0d, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x84, 0x99, + 0xd6, 0x10, 0x00, 0x10, 0x00, 0x99, 0xd6, 0x0c, 0x85, 0x99, 0xd6, 0x10, 0x84, 0x10, 0x84, 0xff, + 0xff, 0xff, 0xff, 0x0b, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x86, 0x00, + 0x00, 0x00, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x84, 0x10, 0x00, 0x10, + 0x00, 0x10, 0x00, 0x10, 0x00, 0x0c, 0x86, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0xff, + 0xff, 0xff, 0xff, 0x09, 0x86, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x88, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x86, 0x10, 0x00, 0x10, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x00, 0x10, + 0x00, 0x0a, 0x88, 0x10, 0x84, 0x10, 0x84, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x84, 0x10, 0x84, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x88, 0x00, 0x00, 0x00, 0x00, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, + 0xd6, 0x00, 0x00, 0x00, 0x00, 0x09, 0x6a, 0x99, 0xd6, 0x05, 0x88, 0x10, 0x00, 0x10, 0x00, 0x99, + 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x00, 0x10, 0x00, 0x08, 0x89, 0x10, 0x84, 0x10, + 0x84, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x10, 0x84, 0x10, 0x84, 0x99, 0xd6, 0x07, + 0x6a, 0x99, 0xd6, 0x16, 0x6a, 0x99, 0xd6, 0x06, 0x6a, 0x99, 0xd6, 0x14, 0x2c, 0x00, 0x03, 0x6e, + 0xff, 0xff, 0x02, 0x6e, 0x08, 0x42, 0x02, 0x6e, 0xff, 0xff, 0x02, 0xcb, 0x66, 0x29, 0x84, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x42, 0x09, 0x0d, 0xdf, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x03, 0x15, 0x00, 0xa2, 0x0c, + 0x05, 0x40, 0x40, 0x17, 0xff, 0xff, 0x00, 0x1c, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0xbc, 0x0f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x0a, 0x40, + 0xc8, 0x03, 0xf4, 0x00, 0xa2, 0x0c, 0x05, 0x40, 0x40, 0xf6, 0xff, 0xff, 0xc0, 0x2c, 0x2d, 0x09, + 0x84, 0x2d, 0x09, 0x2d, 0x09, 0x2d, 0x09, 0x2d, 0x09, 0x00, 0x22, 0xc0, 0x10, 0x25, 0x4b, 0x02, + 0x30, 0x02, 0x2a, 0x02, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0xfd, 0x2e, 0x03, 0xfd, 0x2e, 0x03, 0xfd, + 0x29, 0x03, 0xcd, 0x89, 0x52, 0x03, 0x2d, 0x05, 0x2d, 0x05, 0x29, 0x06, 0xc6, 0x99, 0xd6, 0x09, + 0x29, 0x1f, 0x43, 0x03, 0x00, 0x00, 0x01, 0x27, 0x0b, 0x44, 0xc3, 0x00, 0x00, 0xc0, 0x68, 0x99, + 0xd6, 0x18, 0x48, 0xa5, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xc2, 0x5a, 0x00, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x81, 0x99, 0xd6, 0x48, 0x05, 0x80, 0x05, 0x00, 0x00, 0xfc, + 0x01, 0x50, 0x40, 0x69, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x80, 0x06, 0x00, + 0x00, 0x02, 0x6a, 0x99, 0xd6, 0x1c, 0x86, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0x2d, + 0x09, 0x2d, 0x09, 0x6f, 0xff, 0xff, 0x02, 0x6e, 0xff, 0xff, 0x04, 0x6e, 0xff, 0xff, 0x04, 0xc9, + 0x66, 0x29, 0x02, 0x60, 0x5e, 0x2d, 0x09, 0xc0, 0x30, 0x2d, 0x09, 0xf0, 0xc0, 0x09, 0xc0, 0x10, + 0x08, 0x42, 0x00, 0x00, 0xfd, 0xce, 0x18, 0xc6, 0x01, 0xfd, 0x2e, 0x00, 0x02, 0xcd, 0x89, 0x52, + 0x03, 0x83, 0x99, 0xd6, 0x99, 0xd6, 0x99, 0xd6, 0xc9, 0xef, 0x7b, 0x81, 0x99, 0xd6, 0x00, 0x05, + 0xc9, 0x89, 0xd6, 0x07, 0x69, 0x10, 0x84, 0x00, 0x08, 0x27, 0x09, 0x82, 0xff, 0xff, 0x99, 0xd6, + 0xd0, 0x69, 0x89, 0x52, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x51, 0x0e, 0xc0, 0x40, 0x38, 0x03, 0xa8, 0x00, 0xa2, 0x0c, 0x05, 0x40, 0x40, 0xaa, + 0xff, 0xff, 0xc8, 0x2d, 0x09, 0x00, 0x14, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0xc6, 0x25, 0x4b, 0x00, 0x1a, 0xd8, 0x18, 0xc6, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf7, 0xc0, 0x01, 0x89, 0x52, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, + 0x00, 0x01, 0x99, 0xd6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd0, 0x3b, 0xef, + 0x7b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x2d, 0x09, 0x00, 0x58, 0xf3, 0x7c, + 0x0b, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x0a, 0x40, 0xc8, +}; + +int TestFreeRDPCodecMppc(int argc, char* argv[]) +{ + BOOL status; + UINT32 roff; + UINT32 rlen; + struct rdp_mppc_enc* enc; + struct rdp_mppc_dec* rmppc; + + /* Decompression */ + + rmppc = mppc_dec_new(); + + status = decompress_rdp_5(rmppc, TEST_RDP5_COMPRESSED_DATA, + sizeof(TEST_RDP5_COMPRESSED_DATA), PACKET_COMPRESSED, &roff, &rlen); + + if (!status) + { + printf("RDP5 decompression failure: %d\n", status); + return -1; + } + + if (memcmp(TEST_RDP5_UNCOMPRESSED_DATA, rmppc->history_buf, sizeof(TEST_RDP5_UNCOMPRESSED_DATA)) != 0) + { + printf("RDP5 decompression failure\n"); + return -1; + } + + mppc_dec_free(rmppc); + + /* Compression */ + + enc = mppc_enc_new(PROTO_RDP_50); + + status = compress_rdp(enc, TEST_RDP5_UNCOMPRESSED_DATA, sizeof(TEST_RDP5_UNCOMPRESSED_DATA)); + + if (!status) + { + printf("RDP5 decompression failure: %d\n", status); + return -1; + } + + if (enc->bytes_in_opb != sizeof(TEST_RDP5_UNCOMPRESSED_DATA)) + { + printf("RDP5 decompression failure: size mismatch: Actual: %d, Expected: %d\n", + enc->bytes_in_opb, sizeof(TEST_RDP5_UNCOMPRESSED_DATA)); + return -1; + } + + if (memcmp(TEST_RDP5_COMPRESSED_DATA, enc->outputBuffer, sizeof(TEST_RDP5_COMPRESSED_DATA)) != 0) + { + printf("RDP5 decompression failure\n"); + return -1; + } + + mppc_enc_free(enc); + + return 0; +} From 1ce1bcb5a370b67c7f4f908c969e4e7b1fa02eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 5 Feb 2014 08:54:10 -0500 Subject: [PATCH 147/149] libfreerdp-codec: add more checks in MPPC compression test --- libfreerdp/codec/test/TestFreeRDPCodecMppc.c | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libfreerdp/codec/test/TestFreeRDPCodecMppc.c b/libfreerdp/codec/test/TestFreeRDPCodecMppc.c index b7b3a6977..4f46e4bed 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecMppc.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecMppc.c @@ -625,6 +625,7 @@ int TestFreeRDPCodecMppc(int argc, char* argv[]) } mppc_dec_free(rmppc); + rmppc = mppc_dec_new(); /* Compression */ @@ -638,6 +639,31 @@ int TestFreeRDPCodecMppc(int argc, char* argv[]) return -1; } + if (enc->flags & PACKET_COMPRESSED) + { + status = decompress_rdp_5(rmppc, (BYTE*) enc->outputBuffer, + enc->bytes_in_opb, enc->flags, &roff, &rlen); + + if (!status) + { + printf("RDP5 compression/decompression failure: %d\n", status); + return -1; + } + + if (rlen != sizeof(TEST_RDP5_UNCOMPRESSED_DATA)) + { + printf("RDP5 compression/decompression failure: size mismatch: Actual: %d, Expected: %d\n", + rlen, sizeof(TEST_RDP5_UNCOMPRESSED_DATA)); + return -1; + } + + if (memcmp(TEST_RDP5_UNCOMPRESSED_DATA, &rmppc->history_buf[roff], rlen) != 0) + { + printf("RDP5 compression/decompression failure\n"); + return -1; + } + } + if (enc->bytes_in_opb != sizeof(TEST_RDP5_UNCOMPRESSED_DATA)) { printf("RDP5 decompression failure: size mismatch: Actual: %d, Expected: %d\n", @@ -652,6 +678,7 @@ int TestFreeRDPCodecMppc(int argc, char* argv[]) } mppc_enc_free(enc); + mppc_dec_free(rmppc); return 0; } From e8a9b7ff14e8696dd977af871389a9b1571ad102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 5 Feb 2014 11:54:42 -0500 Subject: [PATCH 148/149] libfreerdp-core: fix ignored port settings for TS Gateway and vmconnect --- client/common/cmdline.c | 11 +++++++---- libfreerdp/core/settings.c | 1 + libfreerdp/core/transport.c | 6 +++--- winpr/include/winpr/memory.h | 1 + 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/client/common/cmdline.c b/client/common/cmdline.c index c506eea57..0458db3d4 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -1149,10 +1149,6 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, settings->PreconnectionBlob = _strdup(arg->Value); } } - CommandLineSwitchCase(arg, "port") - { - settings->ServerPort = atoi(arg->Value); - } CommandLineSwitchCase(arg, "w") { settings->DesktopWidth = atoi(arg->Value); @@ -1755,6 +1751,13 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, } } + arg = CommandLineFindArgumentA(args, "port"); + + if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT) + { + settings->ServerPort = atoi(arg->Value); + } + arg = CommandLineFindArgumentA(args, "p"); if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT) diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index b9ad028c8..fb59a73a9 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -234,6 +234,7 @@ rdpSettings* freerdp_settings_new(DWORD flags) settings->DisableEncryption = FALSE; settings->SaltedChecksum = TRUE; settings->ServerPort = 3389; + settings->GatewayPort = 443; settings->DesktopResize = TRUE; settings->ToggleFullscreen = TRUE; settings->DesktopPosX = 0; diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 567fc169e..affd27f5c 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -29,10 +29,10 @@ #include #include #include +#include #include #include -#include #include #include @@ -365,10 +365,10 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 por transport->layer = TRANSPORT_LAYER_TSG; transport->TcpOut = tcp_new(settings); - status = tcp_connect(transport->TcpIn, settings->GatewayHostname, 443); + status = tcp_connect(transport->TcpIn, settings->GatewayHostname, settings->GatewayPort); if (status) - status = tcp_connect(transport->TcpOut, settings->GatewayHostname, 443); + status = tcp_connect(transport->TcpOut, settings->GatewayHostname, settings->GatewayPort); if (status) status = transport_tsg_connect(transport, hostname, port); diff --git a/winpr/include/winpr/memory.h b/winpr/include/winpr/memory.h index 0721734a1..28e740fc4 100644 --- a/winpr/include/winpr/memory.h +++ b/winpr/include/winpr/memory.h @@ -27,6 +27,7 @@ #include #include +#include #include #include From c799db68552fef817867d24a3d2d2247a2e70b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 5 Feb 2014 13:09:25 -0500 Subject: [PATCH 149/149] libwinpr-synch: fix linker error --- winpr/libwinpr/synch/CMakeLists.txt | 2 +- winpr/libwinpr/synch/test/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/winpr/libwinpr/synch/CMakeLists.txt b/winpr/libwinpr/synch/CMakeLists.txt index 7104df843..af1279489 100644 --- a/winpr/libwinpr/synch/CMakeLists.txt +++ b/winpr/libwinpr/synch/CMakeLists.txt @@ -57,7 +57,7 @@ endif() set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL MODULE winpr - MODULES winpr-handle winpr-interlocked winpr-thread winpr-sysinfo) + MODULES winpr-handle winpr-error winpr-interlocked winpr-thread winpr-sysinfo) if(MONOLITHIC_BUILD) set(WINPR_LIBS ${WINPR_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE) diff --git a/winpr/libwinpr/synch/test/CMakeLists.txt b/winpr/libwinpr/synch/test/CMakeLists.txt index 1d21af0e4..7805d1798 100644 --- a/winpr/libwinpr/synch/test/CMakeLists.txt +++ b/winpr/libwinpr/synch/test/CMakeLists.txt @@ -23,7 +23,7 @@ add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MONOLITHIC ${MONOLITHIC_BUILD} MODULE winpr - MODULES winpr-synch winpr-sysinfo) + MODULES winpr-synch winpr-sysinfo winpr-error) target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})