Skip to content

Commit

Permalink
perf: 使用 CNG 计算 MD5
Browse files Browse the repository at this point in the history
  • Loading branch information
Blinue committed Apr 15, 2024
1 parent 33afe96 commit f1f7a05
Show file tree
Hide file tree
Showing 37 changed files with 198 additions and 126 deletions.
2 changes: 1 addition & 1 deletion src/Effects/SMAA/SMAA.hlsli
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SMAA For Magpie
// 移植自 https://github.com/iryoku/smaa
// 根据 Magpie 的需求做了一些更改
// 根据 Magpie 的需求做了一些更改:
// 1. 将 VS 的计算移到 PS 中
// 2. 删除一些用不到的功能,如 predicated thresholding,temporal supersampling 等
// 3. 添加两个采样器的预定义 SMAA_LINEAR_SAMPLER 和 SMAA_POINT_SAMPLER
Expand Down
4 changes: 2 additions & 2 deletions src/Magpie.App/AppSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ bool AppSettings::Initialize() noexcept {
return false;
}

logger.Info(StrUtils::Concat("便携模式", _isPortableMode ? "" : ""));
logger.Info(StrUtils::Concat("便携模式: ", _isPortableMode ? "" : ""));

if (existingConfigPath.empty()) {
logger.Info("不存在配置文件");
Expand Down Expand Up @@ -258,7 +258,7 @@ bool AppSettings::Initialize() noexcept {
rapidjson::Document doc;
doc.ParseInsitu(configText.data());
if (doc.HasParseError()) {
Logger::Get().Error(fmt::format("解析配置失败\n\t错误码{}", (int)doc.GetParseError()));
Logger::Get().Error(fmt::format("解析配置失败\n\t错误码: {}", (int)doc.GetParseError()));
ResourceLoader resourceLoader =
ResourceLoader::GetForCurrentView(CommonSharedConstants::APP_RESOURCE_MAP_ID);
hstring title = resourceLoader.GetString(L"AppSettings_ErrorDialog_NotValidJson");
Expand Down
3 changes: 1 addition & 2 deletions src/Magpie.App/AppXReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,7 @@ class CandidateIcon {
return _isValid;
}

// 用以选择更合适的图标
// 规则:
// 用以选择更合适的图标。规则:
// 1. 匹配当前主题的优先
// 2. 没有边框的优先
// 3. 和 preferredSize 相同的优先
Expand Down
6 changes: 3 additions & 3 deletions src/Magpie.App/IconHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ static bool CopyPixelsOfHBmp(HBITMAP hBmp, LONG width, LONG height, void* data)
}

static SoftwareBitmap HIcon2SoftwareBitmap(HICON hIcon) {
// 单色图标不处理
// 彩色掩码图标忽略掩码
// 单色图标: 不处理
// 彩色掩码图标: 忽略掩码

ICONINFO iconInfo{};
if (!GetIconInfo(hIcon, &iconInfo)) {
Expand Down Expand Up @@ -184,7 +184,7 @@ SoftwareBitmap IconHelper::ExtractIconFromExe(const wchar_t* fileName, uint32_t
}
}

// 回落到 IShellItemImageFactory,该接口存在以下问题:
// 回落到 IShellItemImageFactory,此接口存在以下问题:
// 1. 速度较慢
// 2. SIIGBF_BIGGERSIZEOK 不起作用,在我的测试里它始终在内部执行低质量的 GDI 缩放
// 3. 不能可靠的并发使用,有时会得到错误的结果
Expand Down
4 changes: 2 additions & 2 deletions src/Magpie.App/ProfileService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace winrt::Magpie::App {

static std::wstring_view GetRealClassName(std::wstring_view className) {
// WPF 窗口类每次启动都会改变,格式为
// WPF 窗口类每次启动都会改变,格式为:
// HwndWrapper[{名称};;{GUID}]
// GUID 格式为 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
static const std::wregex wpfRegex(
Expand All @@ -21,7 +21,7 @@ static std::wstring_view GetRealClassName(std::wstring_view className) {
return { matchResults[1].first, matchResults[1].second };
}

// RPG Maker MZ 制作的游戏每次重新加载(快捷键 F5)窗口类名都会改变,格式为
// RPG Maker MZ 制作的游戏每次重新加载(快捷键 F5)窗口类名都会改变,格式为:
// Chrome_WidgetWin_{递增的数字}
// 这个类名似乎在基于 Chromium 的程序中很常见,大多数时候是 Chrome_WidgetWin_1
static const std::wregex rpgMakerMZRegex(LR"(^Chrome_WidgetWin_\d+$)", std::wregex::optimize);
Expand Down
2 changes: 1 addition & 1 deletion src/Magpie.App/ProfileViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ hstring ProfileViewModel::Name() const noexcept {
}

static void LaunchPackagedApp(const Profile& profile) noexcept {
// 关于启动打包应用的讨论
// 关于启动打包应用的讨论:
// https://github.com/microsoft/WindowsAppSDK/issues/2856#issuecomment-1224409948
// 使用 CLSCTX_LOCAL_SERVER 以在独立的进程中启动应用
// 见 https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-iapplicationactivationmanager
Expand Down
2 changes: 1 addition & 1 deletion src/Magpie.App/ProfileViewModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ struct ProfileViewModel : ProfileViewModelT<ProfileViewModel>,
SmallVector<std::wstring> _graphicsCards;

uint32_t _index = 0;
// 可以保存此指针的原因是用户停留在此页面时不会有缩放配置被创建或删除
// 可以保存此指针的原因是: 用户停留在此页面时不会有缩放配置被创建或删除
Profile* _data = nullptr;

RootPage::ActualThemeChanged_revoker _themeChangedRevoker;
Expand Down
4 changes: 2 additions & 2 deletions src/Magpie.App/Resources.language-ja.resw
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@
<value>終了</value>
</data>
<data name="AppSettings_ErrorDialog_ConfigLocation" xml:space="preserve">
<value>設定ファイルの場所
<value>設定ファイルの場所:
{}</value>
</data>
<data name="AppSettings_ErrorDialog_ParseFailed" xml:space="preserve">
Expand Down Expand Up @@ -608,7 +608,7 @@
<value>貢献ガイドライン</value>
</data>
<data name="ScalingConfiguration_ScalingModes_DeleteFlyout_Description.Text" xml:space="preserve">
<value>以下のプロファイルは、このスケーリングモードを使用しています</value>
<value>以下のプロファイルは、このスケーリングモードを使用しています:</value>
</data>
<data name="ScalingConfiguration_ScalingModes_MoreOptions.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>その他のオプション</value>
Expand Down
6 changes: 3 additions & 3 deletions src/Magpie.App/Resources.language-zh-Hans.resw
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
<value>清除</value>
</data>
<data name="Home_AutoRestore_CurWindow" xml:space="preserve">
<value>当前窗口</value>
<value>当前窗口:</value>
</data>
<data name="Home_AutoRestore_EmptyTitle" xml:space="preserve">
<value>标题为空</value>
Expand Down Expand Up @@ -364,7 +364,7 @@
<value>删除</value>
</data>
<data name="ScalingConfiguration_ScalingModes_DeleteFlyout_Description.Text" xml:space="preserve">
<value>以下配置文件正在使用这个缩放模式</value>
<value>以下配置文件正在使用这个缩放模式:</value>
</data>
<data name="ScalingConfiguration_ScalingModes_DeleteFlyout_Title.Text" xml:space="preserve">
<value>确定删除这个缩放模式?</value>
Expand Down Expand Up @@ -691,7 +691,7 @@
<value>退出</value>
</data>
<data name="AppSettings_ErrorDialog_ConfigLocation" xml:space="preserve">
<value>配置文件位置
<value>配置文件位置:
{}</value>
</data>
<data name="AppSettings_ErrorDialog_NotValidJson" xml:space="preserve">
Expand Down
6 changes: 3 additions & 3 deletions src/Magpie.App/Resources.language-zh-Hant.resw
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
<value>清除</value>
</data>
<data name="Home_AutoRestore_CurWindow" xml:space="preserve">
<value>當前視窗</value>
<value>當前視窗:</value>
</data>
<data name="Home_AutoRestore_EmptyTitle" xml:space="preserve">
<value>標題為空</value>
Expand Down Expand Up @@ -610,7 +610,7 @@
<value>退出</value>
</data>
<data name="AppSettings_ErrorDialog_ConfigLocation" xml:space="preserve">
<value>組態檔案位置
<value>組態檔案位置:
{}</value>
</data>
<data name="AppSettings_ErrorDialog_NotValidJson" xml:space="preserve">
Expand Down Expand Up @@ -659,7 +659,7 @@
<value>確定</value>
</data>
<data name="ScalingConfiguration_ScalingModes_DeleteFlyout_Description.Text" xml:space="preserve">
<value>以下設定檔正在使用這個縮放模式</value>
<value>以下設定檔正在使用這個縮放模式:</value>
</data>
<data name="ScalingConfiguration_ScalingModes_DragNotSupported.Text" xml:space="preserve">
<value>管理員身份下不支援拖曳排列</value>
Expand Down
4 changes: 2 additions & 2 deletions src/Magpie.App/RootPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ void RootPage::NavigateToAboutPage() {

fire_and_forget RootPage::ShowToast(const hstring& message) {
// !!! HACK !!!
// 重用 TeachingTip 有一个 bug前一个 Toast 正在消失时新的 Toast 不会显示。为了
// 重用 TeachingTip 有一个 bug: 前一个 Toast 正在消失时新的 Toast 不会显示。为了
// 规避它,我们每次都创建新的 TeachingTip,但要保留旧对象的引用,因为播放动画时销毁
// 会导致崩溃。oldToastTeachingTip 的生存期可确保动画播放完毕。
MUXC::TeachingTip oldToastTeachingTip = ToastTeachingTip();
Expand All @@ -231,7 +231,7 @@ fire_and_forget RootPage::ShowToast(const hstring& message) {
// !!! HACK !!!
// 我们不想要 IsLightDismissEnabled,因为它会阻止用户和其他控件交互,但我们也不想要关闭按钮,于是
// 手动隐藏它。我们必须在模板加载完成后再做这些,但 TeachingTip 没有 Opening 事件,于是有了又一个
// workaround监听 ToastTextBlock 的 LayoutUpdated 事件,它在 TeachingTip 显示前必然会被引发。
// workaround: 监听 ToastTextBlock 的 LayoutUpdated 事件,它在 TeachingTip 显示前必然会被引发。
ToastTextBlock().LayoutUpdated([weak(weak_ref(newTeachingTip))](IInspectable const&, IInspectable const&) {
auto toastTeachingTip = weak.get();
if (!toastTeachingTip) {
Expand Down
2 changes: 1 addition & 1 deletion src/Magpie.App/ScalingConfigurationViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ static bool ImportImpl(bool legacy) noexcept {
// 导入时放宽 json 格式限制
doc.ParseInsitu<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(json.data());
if (doc.HasParseError()) {
Logger::Get().Error(fmt::format("解析缩放模式失败\n\t错误码{}", (int)doc.GetParseError()));
Logger::Get().Error(fmt::format("解析缩放模式失败\n\t错误码: {}", (int)doc.GetParseError()));
return false;
}

Expand Down
8 changes: 4 additions & 4 deletions src/Magpie.App/ShortcutService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
//
// 实现快捷键功能
//
// Windows 上一般有两种实现热键的方法,它们各有限制
// 1. RegisterHotKey在某些游戏上不可靠
// 2. 键盘钩子如果前台窗口是管理员而 Magpie 不是,此方法无效
// 为了使热键最大程度的可用,这两种方法都被使用。采用下述措施防止它们被同时触发
// Windows 上一般有两种实现热键的方法,它们各有限制:
// 1. RegisterHotKey: 在某些游戏上不可靠
// 2. 键盘钩子: 如果前台窗口是管理员而 Magpie 不是,此方法无效
// 为了使热键最大程度的可用,这两种方法都被使用。采用下述措施防止它们被同时触发:
// 1. 键盘钩子会先被触发,然后吞下热键,防止触发 RegisterHotKey
// 2. 限制热键的触发频率
//
Expand Down
87 changes: 69 additions & 18 deletions src/Magpie.App/UpdateService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "CommonSharedConstants.h"
#include <zip/zip.h>
#include <winrt/Windows.Security.Cryptography.Core.h>
#include <bcrypt.h>
#include <wil/resource.h> // 再次包含以激活 CNG 相关包装器

using namespace winrt;
using namespace Windows::Security::Cryptography::Core;
Expand All @@ -28,6 +30,8 @@ static constexpr Version MAGPIE_VERSION(
#endif
);

static constexpr uint32_t MD5_HASH_LENGTH = 16;

void UpdateService::Initialize() noexcept {
#ifndef MAGPIE_VERSION_TAG
// 只有正式版本才能检查更新
Expand Down Expand Up @@ -70,7 +74,7 @@ fire_and_forget UpdateService::CheckForUpdatesAsync(bool isAutoUpdate) {
doc.Parse((const char*)buffer.data(), buffer.Length());
} catch (const hresult_error& e) {
Logger::Get().ComError(
StrUtils::Concat("检查更新失败", StrUtils::UTF16ToUTF8(e.message())),
StrUtils::Concat("检查更新失败: ", StrUtils::UTF16ToUTF8(e.message())),
e.code()
);

Expand Down Expand Up @@ -218,7 +222,7 @@ static std::wstring Md5ToHex(const uint8_t* data) {
std::wstring result(32, 0);
wchar_t* pResult = &result[0];

for (int i = 0; i < 16; ++i) {
for (uint32_t i = 0; i < MD5_HASH_LENGTH; ++i) {
uint8_t b = data[i];
*pResult++ = oct2Hex[(b >> 4) & 0xf];
*pResult++ = oct2Hex[b & 0xf];
Expand Down Expand Up @@ -284,9 +288,44 @@ fire_and_forget UpdateService::DownloadAndInstall() {
_Status(UpdateStatus::Available);
co_return;
}

// 用于计算 MD5
wil::unique_bcrypt_algorithm hAlg;
NTSTATUS status = BCryptOpenAlgorithmProvider(hAlg.put(), BCRYPT_MD5_ALGORITHM, nullptr, 0);
if (status != STATUS_SUCCESS) {
Logger::Get().NTError("BCryptOpenAlgorithmProvider 失败", status);
_Status(UpdateStatus::ErrorWhileDownloading);
co_return;
}

ULONG result;

#ifdef _DEBUG
uint32_t hashLen = 0;
BCryptGetProperty(hAlg.get(), BCRYPT_HASH_LENGTH,
(PUCHAR)&hashLen, sizeof(hashLen), &result, 0);
assert(hashLen == MD5_HASH_LENGTH);
#endif

uint32_t hashObjSize = 0;
status = BCryptGetProperty(hAlg.get(), BCRYPT_OBJECT_LENGTH,
(PUCHAR)&hashObjSize, sizeof(hashObjSize), &result, 0);
if (status != STATUS_SUCCESS) {
Logger::Get().NTError("BCryptGetProperty 失败", status);
_Status(UpdateStatus::ErrorWhileDownloading);
co_return;
}

HashAlgorithmProvider hashAlgProvider = HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Md5());
CryptographicHash hasher = hashAlgProvider.CreateHash();
std::unique_ptr<uint8_t[]> hashObj = std::make_unique<uint8_t[]>(hashObjSize);

wil::unique_bcrypt_hash hHash;
status = BCryptCreateHash(
hAlg.get(), hHash.put(), hashObj.get(), hashObjSize, NULL, 0, 0);
if (status != STATUS_SUCCESS) {
Logger::Get().NTError("BCryptCreateHash 失败", status);
_Status(UpdateStatus::ErrorWhileDownloading);
co_return;
}

Buffer buffer(64 * 1024);
uint32_t bytesReceived = 0;
Expand All @@ -310,9 +349,15 @@ fire_and_forget UpdateService::DownloadAndInstall() {
DownloadProgressChanged.Invoke(_downloadProgress);
}

hasher.Append(buffer);

const uint8_t* bufferData = resultBuffer.data();

status = BCryptHashData(hHash.get(), (PUCHAR)bufferData, bufferSize, 0);
if (status != STATUS_SUCCESS) {
Logger::Get().NTError("BCryptHashData 失败", status);
_Status(UpdateStatus::ErrorWhileDownloading);
co_return;
}

if (!WriteFile(updatePkg.get(), bufferData, bufferSize, nullptr, nullptr)) {
Logger::Get().Win32Error("WriteFile 失败");
_Status(UpdateStatus::ErrorWhileDownloading);
Expand All @@ -321,15 +366,21 @@ fire_and_forget UpdateService::DownloadAndInstall() {
}

// 检查哈希
IBuffer hash = hasher.GetValueAndReset();
assert(hash.Length() == 16);
std::array<uint8_t, MD5_HASH_LENGTH> hash{};
status = BCryptFinishHash(hHash.get(), hash.data(), (ULONG)hash.size(), 0);
if (status != STATUS_SUCCESS) {
Logger::Get().NTError("BCryptFinishHash 失败", status);
_Status(UpdateStatus::ErrorWhileDownloading);
co_return;
}

if (Md5ToHex(hash.data()) != _binaryHash) {
Logger::Get().Error("下载失败哈希不匹配");
Logger::Get().Error("下载失败: 哈希不匹配");
_Status(UpdateStatus::ErrorWhileDownloading);
co_return;
}
} catch (const hresult_error& e) {
Logger::Get().Error(StrUtils::Concat("下载失败", StrUtils::UTF16ToUTF8(e.message())));
Logger::Get().Error(StrUtils::Concat("下载失败: ", StrUtils::UTF16ToUTF8(e.message())));
_Status(UpdateStatus::ErrorWhileDownloading);
co_return;
}
Expand All @@ -348,7 +399,7 @@ fire_and_forget UpdateService::DownloadAndInstall() {
nullptr
);
if (ec < 0) {
Logger::Get().Error(fmt::format("解压失败,错误代码{}", ec));
Logger::Get().Error(fmt::format("解压失败,错误代码: {}", ec));
co_await dispatcher;
_Status(UpdateStatus::ErrorWhileDownloading);
co_return;
Expand Down Expand Up @@ -391,15 +442,15 @@ fire_and_forget UpdateService::DownloadAndInstall() {

MoveFileEx(updaterExePath.c_str(), L"Updater.exe", MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);

SHELLEXECUTEINFO execInfo{};
execInfo.cbSize = sizeof(execInfo);
execInfo.lpFile = L"Updater.exe";
std::wstring curVersion = MAGPIE_VERSION.ToString();
execInfo.lpParameters = curVersion.c_str();
execInfo.lpVerb = L"open";
// 调用 ShellExecuteEx 后立即退出,因此应该指定 SEE_MASK_NOASYNC
execInfo.fMask = SEE_MASK_NOASYNC;

SHELLEXECUTEINFO execInfo{
.cbSize = sizeof(execInfo),
.fMask = SEE_MASK_NOASYNC,
.lpVerb = L"open",
.lpFile = L"Updater.exe",
.lpParameters = curVersion.c_str()
};
if (!ShellExecuteEx(&execInfo)) {
Logger::Get().Win32Error("ShellExecuteEx 失败");
}
Expand Down
8 changes: 4 additions & 4 deletions src/Magpie.Core/CursorDrawer.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ class CursorDrawer {
enum class _CursorType {
// 彩色光标,此时纹理中 RGB 通道已预乘 A 通道(premultiplied alpha),A 通道已预先取反
// 这是为了减少着色器的计算量以及确保(可能进行的)双线性差值的准确性
// 计算公式FinalColor = ScreenColor * CursorColor.a + CursorColor
// 纹理格式DXGI_FORMAT_R8G8B8A8_UNORM
// 计算公式: FinalColor = ScreenColor * CursorColor.a + CursorColor
// 纹理格式: DXGI_FORMAT_R8G8B8A8_UNORM
Color = 0,
// 彩色掩码光标,此时 A 通道可能为 0 或 255
// 为 0 时表示 RGB 通道取代屏幕颜色,为 255 时表示 RGB 通道和屏幕颜色进行异或操作
// 纹理格式DXGI_FORMAT_R8G8B8A8_UNORM
// 纹理格式: DXGI_FORMAT_R8G8B8A8_UNORM
MaskedColor,
// 单色光标,此时 R 通道为 AND 掩码,G 通道为 XOR 掩码,其他通道不使用
// RG 通道的值只能是 0 或 255
// 纹理格式DXGI_FORMAT_R8G8_UNORM
// 纹理格式: DXGI_FORMAT_R8G8_UNORM
Monochrome
};

Expand Down
Loading

0 comments on commit f1f7a05

Please sign in to comment.