2014-06-14 19:32:11 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2016-01-10 06:05:23 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2014-06-14 19:32:11 +00:00
|
|
|
*/
|
2017-03-04 10:23:56 +00:00
|
|
|
#include "platform/mac/specific_mac.h"
|
2014-05-30 08:53:19 +00:00
|
|
|
|
2017-04-13 08:27:10 +00:00
|
|
|
#include "lang/lang_keys.h"
|
2014-05-30 08:53:19 +00:00
|
|
|
#include "mainwidget.h"
|
2017-06-29 10:27:09 +00:00
|
|
|
#include "history/history_widget.h"
|
2017-12-11 14:45:29 +00:00
|
|
|
#include "core/crash_reports.h"
|
2019-04-07 06:59:37 +00:00
|
|
|
#include "core/sandbox.h"
|
2017-03-04 10:23:56 +00:00
|
|
|
#include "storage/localstorage.h"
|
2017-02-25 17:46:21 +00:00
|
|
|
#include "mainwindow.h"
|
2016-09-28 21:33:05 +00:00
|
|
|
#include "history/history_location_manager.h"
|
2017-06-06 10:40:15 +00:00
|
|
|
#include "platform/mac/mac_utilities.h"
|
2015-03-02 12:34:16 +00:00
|
|
|
|
2019-09-04 07:19:15 +00:00
|
|
|
#include <QtGui/QDesktopServices>
|
|
|
|
|
2017-12-11 14:45:29 +00:00
|
|
|
#include <cstdlib>
|
2016-01-30 18:24:18 +00:00
|
|
|
#include <execinfo.h>
|
2019-01-29 10:09:37 +00:00
|
|
|
#include <sys/xattr.h>
|
2016-01-30 18:24:18 +00:00
|
|
|
|
2017-06-06 10:40:15 +00:00
|
|
|
#include <Cocoa/Cocoa.h>
|
|
|
|
#include <CoreFoundation/CFURL.h>
|
|
|
|
#include <IOKit/IOKitLib.h>
|
|
|
|
#include <IOKit/hidsystem/ev_keymap.h>
|
|
|
|
#include <SPMediaKeyTap.h>
|
2017-08-15 17:12:57 +00:00
|
|
|
#include <mach-o/dyld.h>
|
2018-09-30 15:42:50 +00:00
|
|
|
#include <AVFoundation/AVFoundation.h>
|
2017-06-06 10:40:15 +00:00
|
|
|
|
2019-07-04 13:33:11 +00:00
|
|
|
extern "C" {
|
|
|
|
void _dispatch_main_queue_callback_4CF(mach_msg_header_t *msg);
|
|
|
|
} // extern "C"
|
|
|
|
|
2014-05-30 08:53:19 +00:00
|
|
|
namespace {
|
2015-01-18 09:23:03 +00:00
|
|
|
|
2017-02-23 10:59:19 +00:00
|
|
|
QStringList _initLogs;
|
2014-05-30 08:53:19 +00:00
|
|
|
|
2014-06-14 19:32:11 +00:00
|
|
|
};
|
2014-05-30 08:53:19 +00:00
|
|
|
|
2014-06-14 19:32:11 +00:00
|
|
|
namespace {
|
2016-12-01 19:20:33 +00:00
|
|
|
|
|
|
|
QRect _monitorRect;
|
2019-02-19 06:57:53 +00:00
|
|
|
crl::time _monitorLastGot = 0;
|
2016-12-01 19:20:33 +00:00
|
|
|
|
|
|
|
} // namespace
|
2014-05-30 08:53:19 +00:00
|
|
|
|
2014-07-06 03:32:21 +00:00
|
|
|
QRect psDesktopRect() {
|
2019-02-19 06:57:53 +00:00
|
|
|
auto tnow = crl::now();
|
2014-07-06 03:32:21 +00:00
|
|
|
if (tnow > _monitorLastGot + 1000 || tnow < _monitorLastGot) {
|
|
|
|
_monitorLastGot = tnow;
|
|
|
|
_monitorRect = QApplication::desktop()->availableGeometry(App::wnd());
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
2014-07-06 03:32:21 +00:00
|
|
|
return _monitorRect;
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
2014-10-17 12:57:14 +00:00
|
|
|
void psShowOverAll(QWidget *w, bool canFocus) {
|
|
|
|
objc_showOverAll(w->winId(), canFocus);
|
|
|
|
}
|
|
|
|
|
|
|
|
void psBringToBack(QWidget *w) {
|
|
|
|
objc_bringToBack(w->winId());
|
|
|
|
}
|
|
|
|
|
2016-01-30 18:24:18 +00:00
|
|
|
void psWriteDump() {
|
2017-12-12 20:15:58 +00:00
|
|
|
#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
|
2016-01-30 18:24:18 +00:00
|
|
|
double v = objc_appkitVersion();
|
2017-12-11 14:45:29 +00:00
|
|
|
CrashReports::dump() << "OS-Version: " << v;
|
2017-12-12 20:15:58 +00:00
|
|
|
#endif // TDESKTOP_DISABLE_CRASH_REPORTS
|
2016-01-30 18:24:18 +00:00
|
|
|
}
|
|
|
|
|
2015-06-03 18:13:01 +00:00
|
|
|
void psDeleteDir(const QString &dir) {
|
2014-06-25 07:25:55 +00:00
|
|
|
objc_deleteDir(dir);
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
2015-01-18 09:23:03 +00:00
|
|
|
QStringList psInitLogs() {
|
2017-08-15 17:12:57 +00:00
|
|
|
return _initLogs;
|
2015-01-18 09:23:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void psClearInitLogs() {
|
2017-08-15 17:12:57 +00:00
|
|
|
_initLogs = QStringList();
|
2015-01-16 16:46:01 +00:00
|
|
|
}
|
|
|
|
|
2014-05-30 08:53:19 +00:00
|
|
|
void psActivateProcess(uint64 pid) {
|
2015-08-12 18:02:20 +00:00
|
|
|
if (!pid) {
|
|
|
|
objc_activateProgram(App::wnd() ? App::wnd()->winId() : 0);
|
|
|
|
}
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString psAppDataPath() {
|
2014-06-25 07:25:55 +00:00
|
|
|
return objc_appDataPath();
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void psDoCleanup() {
|
|
|
|
try {
|
|
|
|
psAutoStart(false, true);
|
2014-07-18 10:37:34 +00:00
|
|
|
psSendToMenu(false, true);
|
2014-05-30 08:53:19 +00:00
|
|
|
} catch (...) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int psCleanup() {
|
2014-06-25 07:25:55 +00:00
|
|
|
psDoCleanup();
|
2014-05-30 08:53:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void psDoFixPrevious() {
|
|
|
|
}
|
|
|
|
|
|
|
|
int psFixPrevious() {
|
2014-06-25 07:25:55 +00:00
|
|
|
psDoFixPrevious();
|
2014-05-30 08:53:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-16 12:59:54 +00:00
|
|
|
namespace Platform {
|
2016-01-11 15:43:29 +00:00
|
|
|
|
2016-06-16 12:59:54 +00:00
|
|
|
void start() {
|
|
|
|
objc_start();
|
|
|
|
}
|
2016-01-11 15:43:29 +00:00
|
|
|
|
2016-06-16 12:59:54 +00:00
|
|
|
void finish() {
|
|
|
|
objc_finish();
|
|
|
|
}
|
2014-09-20 21:31:03 +00:00
|
|
|
|
2017-05-02 09:08:08 +00:00
|
|
|
void StartTranslucentPaint(QPainter &p, QPaintEvent *e) {
|
|
|
|
#ifdef OS_MAC_OLD
|
|
|
|
p.setCompositionMode(QPainter::CompositionMode_Source);
|
|
|
|
p.fillRect(e->rect(), Qt::transparent);
|
|
|
|
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
|
|
|
#endif // OS_MAC_OLD
|
2016-11-11 07:51:53 +00:00
|
|
|
}
|
|
|
|
|
2017-08-15 17:12:57 +00:00
|
|
|
QString CurrentExecutablePath(int argc, char *argv[]) {
|
|
|
|
return NS2QString([[NSBundle mainBundle] bundlePath]);
|
|
|
|
}
|
|
|
|
|
2019-01-29 10:09:37 +00:00
|
|
|
void RemoveQuarantine(const QString &path) {
|
|
|
|
const auto kQuarantineAttribute = "com.apple.quarantine";
|
|
|
|
|
|
|
|
DEBUG_LOG(("Removing quarantine attribute: %1").arg(path));
|
|
|
|
const auto local = QFile::encodeName(path);
|
|
|
|
removexattr(local.data(), kQuarantineAttribute, 0);
|
|
|
|
}
|
|
|
|
|
2019-07-04 13:33:11 +00:00
|
|
|
void DrainMainQueue() {
|
|
|
|
_dispatch_main_queue_callback_4CF(nullptr);
|
|
|
|
}
|
|
|
|
|
2018-03-18 08:51:14 +00:00
|
|
|
void RegisterCustomScheme() {
|
|
|
|
#ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
|
|
|
|
OSStatus result = LSSetDefaultHandlerForURLScheme(CFSTR("tg"), (CFStringRef)[[NSBundle mainBundle] bundleIdentifier]);
|
|
|
|
DEBUG_LOG(("App Info: set default handler for 'tg' scheme result: %1").arg(result));
|
|
|
|
#endif // !TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME
|
|
|
|
}
|
|
|
|
|
2018-09-30 15:42:50 +00:00
|
|
|
// I do check for availability, just not in the exact way clang is content with
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
|
|
|
PermissionStatus GetPermissionStatus(PermissionType type) {
|
2018-10-17 06:09:59 +00:00
|
|
|
#ifndef OS_MAC_OLD
|
|
|
|
switch (type) {
|
2018-09-30 15:42:50 +00:00
|
|
|
case PermissionType::Microphone:
|
|
|
|
if([AVCaptureDevice respondsToSelector: @selector(authorizationStatusForMediaType:)]) { // Available starting with 10.14
|
|
|
|
switch([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]) {
|
|
|
|
case AVAuthorizationStatusNotDetermined:
|
|
|
|
return PermissionStatus::CanRequest;
|
|
|
|
case AVAuthorizationStatusAuthorized:
|
|
|
|
return PermissionStatus::Granted;
|
|
|
|
case AVAuthorizationStatusDenied:
|
|
|
|
case AVAuthorizationStatusRestricted:
|
|
|
|
return PermissionStatus::Denied;
|
|
|
|
}
|
|
|
|
}
|
2018-10-17 06:09:59 +00:00
|
|
|
break;
|
2018-09-30 15:42:50 +00:00
|
|
|
}
|
2018-10-17 06:09:59 +00:00
|
|
|
#endif // OS_MAC_OLD
|
2018-09-30 15:42:50 +00:00
|
|
|
return PermissionStatus::Granted;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestPermission(PermissionType type, Fn<void(PermissionStatus)> resultCallback) {
|
2018-10-17 06:09:59 +00:00
|
|
|
#ifndef OS_MAC_OLD
|
|
|
|
switch (type) {
|
2018-09-30 15:42:50 +00:00
|
|
|
case PermissionType::Microphone:
|
2018-10-17 06:09:59 +00:00
|
|
|
if ([AVCaptureDevice respondsToSelector: @selector(requestAccessForMediaType:completionHandler:)]) { // Available starting with 10.14
|
|
|
|
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
|
|
|
|
crl::on_main([=] {
|
|
|
|
resultCallback(granted ? PermissionStatus::Granted : PermissionStatus::Denied);
|
|
|
|
});
|
|
|
|
}];
|
2018-09-30 15:42:50 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-10-17 06:09:59 +00:00
|
|
|
#endif // OS_MAC_OLD
|
|
|
|
resultCallback(PermissionStatus::Granted);
|
2018-09-30 15:42:50 +00:00
|
|
|
}
|
|
|
|
#pragma clang diagnostic pop // -Wunguarded-availability
|
2018-10-17 06:09:59 +00:00
|
|
|
|
2018-09-30 15:42:50 +00:00
|
|
|
void OpenSystemSettingsForPermission(PermissionType type) {
|
2018-10-17 06:09:59 +00:00
|
|
|
#ifndef OS_MAC_OLD
|
|
|
|
switch (type) {
|
2018-09-30 15:42:50 +00:00
|
|
|
case PermissionType::Microphone:
|
|
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"]];
|
|
|
|
break;
|
|
|
|
}
|
2018-10-17 06:09:59 +00:00
|
|
|
#endif // OS_MAC_OLD
|
2018-09-30 15:42:50 +00:00
|
|
|
}
|
|
|
|
|
2019-01-05 11:08:02 +00:00
|
|
|
bool OpenSystemSettings(SystemSettingsType type) {
|
|
|
|
switch (type) {
|
2019-01-11 10:07:56 +00:00
|
|
|
case SystemSettingsType::Audio:
|
|
|
|
[[NSWorkspace sharedWorkspace] openFile:@"/System/Library/PreferencePanes/Sound.prefPane"];
|
|
|
|
break;
|
2019-01-05 11:08:02 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-14 17:59:10 +00:00
|
|
|
// Taken from https://github.com/trueinteractions/tint/issues/53.
|
|
|
|
std::optional<crl::time> LastUserInputTime() {
|
|
|
|
CFMutableDictionaryRef properties = 0;
|
|
|
|
CFTypeRef obj;
|
|
|
|
mach_port_t masterPort;
|
|
|
|
io_iterator_t iter;
|
|
|
|
io_registry_entry_t curObj;
|
|
|
|
|
|
|
|
IOMasterPort(MACH_PORT_NULL, &masterPort);
|
|
|
|
|
|
|
|
/* Get IOHIDSystem */
|
|
|
|
IOServiceGetMatchingServices(masterPort, IOServiceMatching("IOHIDSystem"), &iter);
|
|
|
|
if (iter == 0) {
|
|
|
|
return std::nullopt;
|
|
|
|
} else {
|
|
|
|
curObj = IOIteratorNext(iter);
|
|
|
|
}
|
|
|
|
if (IORegistryEntryCreateCFProperties(curObj, &properties, kCFAllocatorDefault, 0) == KERN_SUCCESS && properties != NULL) {
|
|
|
|
obj = CFDictionaryGetValue(properties, CFSTR("HIDIdleTime"));
|
|
|
|
CFRetain(obj);
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64 err = ~0L, idleTime = err;
|
|
|
|
if (obj) {
|
|
|
|
CFTypeID type = CFGetTypeID(obj);
|
|
|
|
|
|
|
|
if (type == CFDataGetTypeID()) {
|
|
|
|
CFDataGetBytes((CFDataRef) obj, CFRangeMake(0, sizeof(idleTime)), (UInt8*)&idleTime);
|
|
|
|
} else if (type == CFNumberGetTypeID()) {
|
|
|
|
CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt64Type, &idleTime);
|
|
|
|
} else {
|
|
|
|
// error
|
|
|
|
}
|
|
|
|
|
|
|
|
CFRelease(obj);
|
|
|
|
|
|
|
|
if (idleTime != err) {
|
|
|
|
idleTime /= 1000000; // return as ms
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// error
|
|
|
|
}
|
|
|
|
|
|
|
|
CFRelease((CFTypeRef)properties);
|
|
|
|
IOObjectRelease(curObj);
|
|
|
|
IOObjectRelease(iter);
|
|
|
|
if (idleTime == err) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return (crl::now() - static_cast<crl::time>(idleTime));
|
2019-03-09 17:10:51 +00:00
|
|
|
}
|
|
|
|
|
2016-06-16 12:59:54 +00:00
|
|
|
} // namespace Platform
|
|
|
|
|
2015-08-13 15:11:07 +00:00
|
|
|
void psNewVersion() {
|
2018-03-18 08:51:14 +00:00
|
|
|
Platform::RegisterCustomScheme();
|
2014-12-03 13:10:32 +00:00
|
|
|
}
|
|
|
|
|
2014-05-30 08:53:19 +00:00
|
|
|
void psAutoStart(bool start, bool silent) {
|
|
|
|
}
|
2014-07-18 10:37:34 +00:00
|
|
|
|
|
|
|
void psSendToMenu(bool send, bool silent) {
|
|
|
|
}
|
2014-09-29 02:47:30 +00:00
|
|
|
|
|
|
|
void psUpdateOverlayed(QWidget *widget) {
|
|
|
|
}
|
2014-09-30 14:13:47 +00:00
|
|
|
|
2015-11-26 17:34:52 +00:00
|
|
|
void psDownloadPathEnableAccess() {
|
2016-08-28 20:23:32 +00:00
|
|
|
objc_downloadPathEnableAccess(Global::DownloadPathBookmark());
|
2015-11-26 17:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray psDownloadPathBookmark(const QString &path) {
|
|
|
|
return objc_downloadPathBookmark(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray psPathBookmark(const QString &path) {
|
|
|
|
return objc_pathBookmark(path);
|
|
|
|
}
|
|
|
|
|
2019-06-21 12:27:46 +00:00
|
|
|
bool psLaunchMaps(const Data::LocationPoint &point) {
|
|
|
|
return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(point.latAsString()).arg(point.lonAsString()));
|
2016-02-17 17:14:09 +00:00
|
|
|
}
|
|
|
|
|
2014-11-25 12:16:36 +00:00
|
|
|
QString strNotificationAboutThemeChange() {
|
2017-11-15 15:02:50 +00:00
|
|
|
const uint32 letters[] = { 0x75E86256, 0xD03E11B1, 0x4D92201D, 0xA2144987, 0x99D5B34F, 0x037589C3, 0x38ED2A7C, 0xD2371ABC, 0xDC98BB02, 0x27964E1B, 0x01748AED, 0xE06679F8, 0x761C9580, 0x4F2595BF, 0x6B5FCBF4, 0xE4D9C24E, 0xBA2F6AB5, 0xE6E3FA71, 0xF2CFC255, 0x56A50C19, 0x43AE1239, 0x77CA4254, 0x7D189A89, 0xEA7663EE, 0x84CEB554, 0xA0ADF236, 0x886512D4, 0x7D3FBDAF, 0x85C4BE4F, 0x12C8255E, 0x9AD8BD41, 0xAC154683, 0xB117598B, 0xDFD9F947, 0x63F06C7B, 0x6340DCD6, 0x3AAE6B3E, 0x26CB125A };
|
|
|
|
return Platform::MakeFromLetters(letters);
|
2014-11-25 12:16:36 +00:00
|
|
|
}
|
|
|
|
|
2016-07-07 16:15:34 +00:00
|
|
|
QString strNotificationAboutScreenLocked() {
|
2017-11-15 15:02:50 +00:00
|
|
|
const uint32 letters[] = { 0x34B47F28, 0x47E95179, 0x73D05C42, 0xB4E2A933, 0x924F22D1, 0x4265D8EA, 0x9E4D2CC2, 0x02E8157B, 0x35BF7525, 0x75901A41, 0xB0400FCC, 0xE801169D, 0x4E04B589, 0xC1CEF054, 0xAB2A7EB0, 0x5C67C4F6, 0xA4E2B954, 0xB35E12D2, 0xD598B22B, 0x4E3B8AAB, 0xBEA5E439, 0xFDA8AA3C, 0x1632DBA8, 0x88FE8965 };
|
|
|
|
return Platform::MakeFromLetters(letters);
|
2016-07-07 16:15:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString strNotificationAboutScreenUnlocked() {
|
2017-11-15 15:02:50 +00:00
|
|
|
const uint32 letters[] = { 0xF897900B, 0x19A04630, 0x144DA6DF, 0x643CA7ED, 0x81DDA343, 0x88C6B149, 0x5F9A3A15, 0x31804E13, 0xDF2202B8, 0x9BD1B500, 0x61B92735, 0x7DDF5D43, 0xB74E06C3, 0x16FF1665, 0x9098F702, 0x4461DAF0, 0xA3134FA5, 0x52B01D3C, 0x6BC35769, 0xA7CC945D, 0x8B5327C0, 0x7630B9A0, 0x4E52E3CE, 0xED7765E3, 0xCEB7862D, 0xA06B34F0 };
|
|
|
|
return Platform::MakeFromLetters(letters);
|
2016-07-07 16:15:34 +00:00
|
|
|
}
|
|
|
|
|
2014-11-25 12:16:36 +00:00
|
|
|
QString strStyleOfInterface() {
|
2017-11-15 15:02:50 +00:00
|
|
|
const uint32 letters[] = { 0x3BBB7F05, 0xED4C5EC3, 0xC62C15A3, 0x5D10B283, 0x1BB35729, 0x63FB674D, 0xDBE5C174, 0x401EA195, 0x87B0C82A, 0x311BD596, 0x7063ECFA, 0x4AB90C27, 0xDA587DC4, 0x0B6296F8, 0xAA5603FA, 0xE1140A9F, 0x3D12D094, 0x339B5708, 0x712BA5B1 };
|
|
|
|
return Platform::MakeFromLetters(letters);
|
2014-11-25 12:16:36 +00:00
|
|
|
}
|
2017-11-15 12:46:34 +00:00
|
|
|
|
|
|
|
QString strTitleWrapClass() {
|
2017-11-15 15:02:50 +00:00
|
|
|
const uint32 letters[] = { 0x066C95DD, 0xA289D425, 0x000EF1A5, 0xB53C76AA, 0x5096391D, 0x212BF5B8, 0xE6BCA526, 0x2A5B8EC6, 0xC1457BDB, 0xA1BEE033, 0xA8ADFA11, 0xFF151585, 0x36EC257D, 0x4D96241D, 0xD0341BAA, 0xDE2908BF, 0xFE7978E8, 0x26875E1D, 0x70DA5557, 0x14C02B69, 0x7EFF7E69, 0x008D7217, 0x5EB01138 };
|
|
|
|
return Platform::MakeFromLetters(letters);
|
2017-11-15 12:46:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString strTitleClass() {
|
2017-11-15 15:02:50 +00:00
|
|
|
const uint32 letters[] = { 0x1054BBE5, 0xA39FC333, 0x54B51E1E, 0x24895213, 0x50B71830, 0xBF07478C, 0x10BA5503, 0x5C70D3E6, 0x65079D9D, 0xACAAF939, 0x6A56C3CD };
|
|
|
|
return Platform::MakeFromLetters(letters);
|
2017-11-15 12:46:34 +00:00
|
|
|
}
|