19 Commits
3.5.0 ... 4.1.0

Author SHA1 Message Date
HolographicHat
bf5525d2ea 5.1 2024-10-09 16:03:25 +08:00
HolographicHat
cf3749f887 fix #114 2024-09-12 23:47:01 +08:00
HolographicHat
21af4de1a6 5.0 2024-08-29 18:24:21 +08:00
HolographicHat
8e0fd2d27c fix #111 2024-08-05 18:11:51 +08:00
HolographicHat
0348cfa365 Merge pull request #107 from eltociear/add-japanese-readme-tutorial 2024-07-31 07:58:19 +08:00
Ikko Eltociear Ashimine
494eda32c2 docs: add Japanese README and tutorial 2024-07-23 00:47:25 +09:00
HolographicHat
975638c1ee Merge pull request #104 from canmengxian/master 2024-07-17 18:33:19 +08:00
HolographicHat
793ad075fe 4.8 2024-07-17 18:12:24 +08:00
残梦
c82a10353f Update Utils.cs 2024-06-23 23:16:13 +08:00
HolographicHat
f737122247 4.7 2024-06-06 02:07:56 +08:00
HolographicHat
520167ef85 fix #82 #101 2024-04-29 14:38:13 +08:00
HolographicHat
faee6f6121 Update README_EN.md 2024-04-25 21:50:15 +08:00
HolographicHat
06c5468118 update readme 2024-04-25 21:42:14 +08:00
HolographicHat
b7c2204f68 Update CI 2024-04-25 02:52:46 +08:00
HolographicHat
5dc5e646d6 fix #87 2024-04-25 02:45:30 +08:00
HolographicHat
9cab7e8702 4.6 2024-04-24 23:26:24 +08:00
HolographicHat
1f080fe084 Merge pull request #98 from Anong0u0/patch 2024-04-22 14:27:29 +08:00
Anong0u0
c8497243c0 Fix GamePathRegex match to case insensitive 2024-04-22 13:44:20 +08:00
HolographicHat
9abdd123ee #93
remove appcenter
2024-04-02 15:06:05 +08:00
49 changed files with 332 additions and 806 deletions

View File

@@ -12,11 +12,11 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 7.0.x dotnet-version: 8.0.x
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
- name: Build - name: Build
@@ -24,7 +24,7 @@ jobs:
- name: Publish - name: Publish
run: dotnet publish --property:OutputPath=.\publish\ run: dotnet publish --property:OutputPath=.\publish\
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: Artifacts name: Artifacts
path: publish path: publish

View File

@@ -1,42 +1,42 @@
<div align="center"><img width="100" src="https://github.com/HolographicHat/YaeAchievement/blob/master/icon.ico"> <div align="center"><img width="100" src="https://github.com/HolographicHat/YaeAchievement/blob/master/icon.ico">
# YaeAchievement # YaeAchievement
![GitHub](https://img.shields.io/badge/License-GPL--3.0-brightgreen?style=flat-square) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/HolographicHat/YaeAchievement?color=brightgreen&label=Release&style=flat-square) ![GitHub issues](https://img.shields.io/github/issues/HolographicHat/YaeAchievement?label=Issues&style=flat-square) ![Downloads](https://img.shields.io/github/downloads/HolographicHat/YaeAchievement/total?color=brightgreen&label=Downloads&style=flat-square) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square) ![GitHub](https://img.shields.io/badge/License-GPL--3.0-brightgreen?style=flat-square) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/HolographicHat/YaeAchievement?color=brightgreen&label=Release&style=flat-square) ![GitHub issues](https://img.shields.io/github/issues/HolographicHat/YaeAchievement?label=Issues&style=flat-square) ![Downloads](https://img.shields.io/github/downloads/HolographicHat/YaeAchievement/total?color=brightgreen&label=Downloads&style=flat-square) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)
简体中文 | [English](README_EN.md) 简体中文 | [English](README_EN.md) | [日本語](README_JP.md)
</div> </div>
- 支持导出所有类别的成就 - 支持导出所有类别的成就
- 支持官服,渠道服与国际服 - 支持官服,渠道服与国际服
- 没有窗口大小、游戏语言等要求 - 没有窗口大小、游戏语言等要求
## 导出支持 ## 导出支持
> 按照数字键选择导出方式,<kbd>0</kbd> 为默认导出方式 > 按照数字键选择导出方式,<kbd>0</kbd> 为默认导出方式
0. [椰羊](https://cocogoat.work/achievement) 0. [椰羊](https://cocogoat.work/achievement)
1. [胡桃工具箱](https://github.com/DGP-Studio/Snap.HuTao) 1. [胡桃工具箱](https://github.com/DGP-Studio/Snap.HuTao)
2. [Paimon.moe](https://paimon.moe/achievement/) 2. [Paimon.moe](https://paimon.moe/achievement/)
3. [Seelie.me](https://seelie.me/achievements) 3. [Seelie.me](https://seelie.me/achievements)
4. 表格文件 `.csv` 4. 表格文件 `.csv`
5. [寻空](https://github.com/xunkong/xunkong) 5. [寻空](https://github.com/xunkong/xunkong)
6. [原魔工具箱](https://apps.apple.com/app/id1663989619) 6. [原魔工具箱](https://apps.apple.com/app/id1663989619)
7. [TeyvatGuide](https://github.com/BTMuli/TeyvatGuide) 7. [TeyvatGuide](https://github.com/BTMuli/TeyvatGuide)
8. [UIAF](https://uigf.org/standards/UIAF.html) JSON 文件 8. [UIAF](https://uigf.org/standards/UIAF.html) JSON 文件
## 使用说明 ## 使用说明
→ [Tutorial.md](Tutorial.md) → [Tutorial.md](Tutorial.md)
## 下载地址 ## 下载地址
[releases/latest](https://github.com/HolographicHat/YaeAchievement/releases/latest) [releases/latest](https://github.com/HolographicHat/YaeAchievement/releases/latest)
## 问题反馈 ## 问题反馈
[issues](https://github.com/HolographicHat/YaeAchievement/issues)或[QQ群: 913777414](https://qm.qq.com/cgi-bin/qm/qr?k=9UGz-chQVTjZa4b82RA_A41vIcBVNpms&jump_from=webapi) [issues](https://github.com/HolographicHat/YaeAchievement/issues)或[QQ群: 913777414](https://qm.qq.com/cgi-bin/qm/qr?k=9UGz-chQVTjZa4b82RA_A41vIcBVNpms&jump_from=webapi)
## 常见问题 ## 常见问题
0. Q: 打不开 0. Q: 打不开
A: 安装 [.NET Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-7.0.3-windows-x64-installer) A: 安装 [.NET Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.4-windows-x64-installer)
1. Q: 原神启动时报错: 数据异常(31-4302) 1. Q: 原神启动时报错: 数据异常(31-4302)
A: 不要把软件和原神主程序放一起 A: 不要把软件和原神主程序放一起

View File

@@ -4,10 +4,7 @@
![GitHub](https://img.shields.io/badge/License-GPL--3.0-brightgreen?style=flat-square) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/HolographicHat/YaeAchievement?color=brightgreen&label=Release&style=flat-square) ![GitHub issues](https://img.shields.io/github/issues/HolographicHat/YaeAchievement?label=Issues&style=flat-square) ![Downloads](https://img.shields.io/github/downloads/HolographicHat/YaeAchievement/total?color=brightgreen&label=Downloads&style=flat-square) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square) ![GitHub](https://img.shields.io/badge/License-GPL--3.0-brightgreen?style=flat-square) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/HolographicHat/YaeAchievement?color=brightgreen&label=Release&style=flat-square) ![GitHub issues](https://img.shields.io/github/issues/HolographicHat/YaeAchievement?label=Issues&style=flat-square) ![Downloads](https://img.shields.io/github/downloads/HolographicHat/YaeAchievement/total?color=brightgreen&label=Downloads&style=flat-square) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)
[简体中文](README.md) | English [简体中文](README.md) | English | [日本語](README_JP.md)
**I18n support currently in [snapshot version](https://github.com/HolographicHat/YaeAchievement/actions/)**
**Next release: 2022/09/28 (Genshin 3.1)**
</div> </div>
@@ -39,7 +36,7 @@
## Frequently asked questions ## Frequently asked questions
0. Q: Unable to start 0. Q: Unable to start
A: Download and install [.NET Runtime 7](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-7.0.7-windows-x64-installer) or ` winget install Microsoft.DotNet.Runtime.7` A: Download and install [.NET Runtime 7](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.4-windows-x64-installer) or ` winget install Microsoft.DotNet.Runtime.8`
1. Q: Error while Genshin started: Data Exception (31-4302) 1. Q: Error while Genshin started: Data Exception (31-4302)

44
README_JP.md Normal file
View File

@@ -0,0 +1,44 @@
<div align="center"><img width="100" src="https://github.com/HolographicHat/YaeAchievement/blob/master/icon.ico">
# YaeAchievement
![GitHub](https://img.shields.io/badge/License-GPL--3.0-brightgreen?style=flat-square) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/HolographicHat/YaeAchievement?color=brightgreen&label=Release&style=flat-square) ![GitHub issues](https://img.shields.io/github/issues/HolographicHat/YaeAchievement?label=Issues&style=flat-square) ![Downloads](https://img.shields.io/github/downloads/HolographicHat/YaeAchievement/total?color=brightgreen&label=Downloads&style=flat-square) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)
[简体中文](README.md) | [English](README_EN.md) | 日本語
</div>
- すべてのカテゴリの実績のエクスポートをサポート
- すべてのバージョンの原神をサポート
- ウィンドウサイズ、ゲーム言語などの要件はありません
## エクスポートサポート
> 数字キーに従ってエクスポート方法を選択します。<kbd>0</kbd> はデフォルトのエクスポート方法です
0. [椰羊](https://cocogoat.work/achievement)
1. [胡桃ツールボックス](https://github.com/DGP-Studio/Snap.HuTao)
2. [Paimon.moe](https://paimon.moe/achievement/)
3. [Seelie.me](https://seelie.me/achievements)
4. フォームファイル `.csv`
5. [尋空](https://github.com/xunkong/xunkong)
6. [原魔ツールボックス](https://apps.apple.com/app/id1663989619)
7. [TeyvatGuide](https://github.com/BTMuli/TeyvatGuide)
8. [UIAF](https://uigf.org/standards/UIAF.html) JSONファイル
## 使用説明書:
→ [Tutorial_JP.md](Tutorial_JP.md)
## ダウンロード: [こちら](https://github.com/HolographicHat/YaeAchievement/releases/latest)
## フィードバックや問題?
[issues](https://github.com/HolographicHat/YaeAchievement/issues) または [QQ群: 913777414](https://qm.qq.com/cgi-bin/qm/qr?k=9UGz-chQVTjZa4b82RA_A41vIcBVNpms&jump_from=webapi)
## よくある質問
0. Q: 起動できない
A: [.NET Runtime 7](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.4-windows-x64-installer) をダウンロードしてインストールするか、`winget install Microsoft.DotNet.Runtime.8` を実行してください。
1. Q: 原神を起動中にエラーが発生しました: データ例外 (31-4302)
A: ソフトウェアを原神のディレクトリに配置しないでください。

View File

@@ -10,7 +10,7 @@
2.安装启动软件所需文件(若已安装该运行时可忽略此步骤) 2.安装启动软件所需文件(若已安装该运行时可忽略此步骤)
点击该网址https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-7.0.3-windows-x64-installer 。 点击该网址https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.4-windows-x64-installer 。
进入网页后浏览器会自动弹出下载,同样地,将文件保存在桌面或者其它易于寻找的文件夹内。 进入网页后浏览器会自动弹出下载,同样地,将文件保存在桌面或者其它易于寻找的文件夹内。

View File

@@ -11,7 +11,7 @@ Click on the file named "YaeAchievement.exe" in the red box to automatically pop
2.Install .NET Runtime 7 (this step can be ignored if the runtime is already installed) 2.Install .NET Runtime 7 (this step can be ignored if the runtime is already installed)
Click Herehttps://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-7.0.7-windows-x64-installer . Click Herehttps://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.4-windows-x64-installer .
Or `winget install Microsoft.DotNet.Runtime.7` if you use Windows 11 or have Winget installed. Or `winget install Microsoft.DotNet.Runtime.7` if you use Windows 11 or have Winget installed.

75
Tutorial_JP.md Normal file
View File

@@ -0,0 +1,75 @@
## 使用説明書
1. YaeAchievement最新バージョンをダウンロード
こちらをクリックhttps://github.com/HolographicHat/YaeAchievement/releases
赤枠で囲まれた「YaeAchievement.exe」という名前のファイルをクリックすると、自動的にポップアップしてダウンロードされます。このファイルをデスクトップや他の見やすいフォルダに保存することをお勧めします。
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide1.png)
2. .NET Runtime 7をインストールランタイムが既にインストールされている場合はこのステップを無視できます
こちらをクリックhttps://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.4-windows-x64-installer .
または、Windows 11を使用しているか、Wingetがインストールされている場合は、`winget install Microsoft.DotNet.Runtime.7`を実行します。
ウェブページにアクセスすると、ブラウザが自動的にポップアップしてダウンロードされます。ファイルをデスクトップや他の見やすいフォルダに保存します。
ダウンロードが完了したら、dotnet-runtime-x.x.x-win-x64.exeという名前のファイルを開くと、インストールウィンドウがポップアップします。以下の図のように表示されます。
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide2.png)
インストールをクリックするだけです。
3. メインプログラムを開くための操作と実績エクスポートのオプション
最初のステップでダウンロードした「YaeAchievement.exe」という名前のファイルをダブルクリックして開くと、原神が起動していることを示します。以下の図のように表示されます。
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide3.png)
原神の起動が完了したら、ゲームに入ります。
ゲームに入ると、ツールがどのツールにエクスポートするかを選択するように促します。以下の図のように表示されます。
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide4.png)
グローバルユーザーの場合、[3] Seelie.meまたは[4] csvファイルにエクスポートを選択する必要があります。
選択後、各ページは次のようにツールをエクスポートします:
#### Snap.Hutao
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide5.png)
#### Seelie.me
この時点で、Yae Achievementは実績データがエクスポートされたことを通知します。Yae Achievement保存ディレクトリにexport-20xxxxxxxxxxxx-seelie.jsonという名前のファイルを見つけてください。
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide6.png)
次に、URLhttps://seelie.me/settings をクリックし、ウェブサイトにアクセスしてインポートアカウントを選択します。以下の図のように表示されます。
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide7.png)
インポートをクリックした後、export-20xxxxxxxxxxxx-seelie.jsonという名前のファイルを選択します。以下の図のように表示されます。
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide8.png)
以下の図のようなプロンプトが表示されたら、インポートプロセスは成功です。
![image](https://github.com/prpjzz/yae-markdown-en/blob/main/image/Guide9.png)
この時点で、左側の列のAchievementsを選択して、インポートされた実績データを表示できます。
### 各ツールの紹介については、各ツールの公式ページをご覧ください:
0. [椰羊](https://cocogoat.work/achievement)
1. [胡桃ツールボックス](https://github.com/DGP-Studio/Snap.HuTao)
2. [Paimon.moe](https://paimon.moe/achievement/)
3. [Seelie.me](https://seelie.me/achievements)
4. ~~フォームファイル `.csv`~~
5. [尋空](https://github.com/xunkong/xunkong)
6. [原魔ツールボックス](https://apps.apple.com/app/id1663989619)
7. [TeyvatGuide](https://github.com/BTMuli/TeyvatGuide)
8. [UIAF](https://uigf.org/standards/UIAF.html) JSONファイル

View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
@@ -21,11 +21,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.22.1" /> <PackageReference Include="Google.Protobuf" Version="3.26.1" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.188-beta"> <PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Grpc.Tools" Version="2.53.0"> <PackageReference Include="Grpc.Tools" Version="2.62.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@@ -105,14 +105,13 @@
<AdditionalDependencies>detours-x64.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>detours-x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>copy $(TargetPath) $(ProjectDir)..\bin\Debug\net7.0-windows\win-x64\YaeAchievementLib.dll /y</Command> <Command>copy $(TargetPath) $(ProjectDir)..\bin\Debug\net8.0-windows\win-x64\YaeAchievementLib.dll /y</Command>
</PostBuildEvent> </PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="src\HookManager.h" /> <ClInclude Include="src\HookManager.h" />
<ClInclude Include="src\il2cpp-appdata.h" /> <ClInclude Include="src\il2cpp-appdata.h" />
<ClInclude Include="src\il2cpp-functions.h" /> <ClInclude Include="src\il2cpp-functions.h" />
<ClInclude Include="src\il2cpp-types-ptr.h" />
<ClInclude Include="src\il2cpp-types.h" /> <ClInclude Include="src\il2cpp-types.h" />
<ClInclude Include="src\il2cpp-init.h" /> <ClInclude Include="src\il2cpp-init.h" />
<ClInclude Include="src\pch.h" /> <ClInclude Include="src\pch.h" />

View File

@@ -15,24 +15,24 @@ using Genshin::ByteArray;
HWND unityWnd = nullptr; HWND unityWnd = nullptr;
HANDLE hPipe = nullptr; HANDLE hPipe = nullptr;
void* baClass;
std::string checksum; std::string checksum;
namespace Hook { namespace Hook {
ByteArray* UnityEngine_RecordUserData(const INT type) { ByteArray* UnityEngine_RecordUserData(const INT type) {
if (type == 0) { if (type == 0) {
const auto arr = new ByteArray {};
const auto len = checksum.length(); const auto len = checksum.length();
arr->max_length = len; const auto arr = Genshin::il2cpp_array_new_specific(baClass, len);
memcpy(&arr->vector[0], checksum.data(), len); memcpy(&arr->vector[0], checksum.data(), len);
return arr; return arr;
} }
return new ByteArray {}; return Genshin::il2cpp_array_new_specific(baClass, 0);
} }
uint16_t BitConverter_ToUInt16(ByteArray* val, const int startIndex) { uint16_t BitConverter_ToUInt16(ByteArray* val, const int startIndex) {
const auto ret = CALL_ORIGIN(BitConverter_ToUInt16, val, startIndex); const auto ret = CALL_ORIGIN(BitConverter_ToUInt16, val, startIndex);
if (ret == 0xAB89 && ReadMapped<UINT16>(val->vector, 2) == 20248) { if (ret == 0xAB89 && ReadMapped<UINT16>(val->vector, 2) == 24082) {
const auto headLength = ReadMapped<UINT16>(val->vector, 4); const auto headLength = ReadMapped<UINT16>(val->vector, 4);
const auto dataLength = ReadMapped<UINT32>(val->vector, 6); const auto dataLength = ReadMapped<UINT32>(val->vector, 6);
const auto cStr = base64_encode(val->vector + 10 + headLength, dataLength) + "\n"; const auto cStr = base64_encode(val->vector + 10 + headLength, dataLength) + "\n";
@@ -56,9 +56,10 @@ void Run(HMODULE* phModule) {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
const auto result = Genshin::RecordUserData(i); const auto result = Genshin::RecordUserData(i);
checksum += string(reinterpret_cast<char*>(&result->vector[0]), result->max_length); checksum += string(reinterpret_cast<char*>(&result->vector[0]), result->max_length);
baClass = result->klass;
} }
HookManager::install(Genshin::RecordUserData, Hook::UnityEngine_RecordUserData);
HookManager::install(Genshin::BitConverter_ToUInt16, Hook::BitConverter_ToUInt16); HookManager::install(Genshin::BitConverter_ToUInt16, Hook::BitConverter_ToUInt16);
HookManager::install(Genshin::UnityEngine_RecordUserData, Hook::UnityEngine_RecordUserData);
hPipe = CreateFile(R"(\\.\pipe\YaeAchievementPipe)", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); hPipe = CreateFile(R"(\\.\pipe\YaeAchievementPipe)", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (hPipe == INVALID_HANDLE_VALUE) { if (hPipe == INVALID_HANDLE_VALUE) {
Win32ErrorDialog(1001); Win32ErrorDialog(1001);

View File

@@ -5,15 +5,7 @@
// Application-specific functions // Application-specific functions
#define DO_APP_FUNC(ca, oa, r, n, p) extern r (*n) p #define DO_APP_FUNC(ca, oa, r, n, p) extern r (*n) p
#define DO_UNI_FUNC(ca, oa, r, n, p) extern r (*n) p
namespace Genshin { namespace Genshin {
#include "il2cpp-functions.h" #include "il2cpp-functions.h"
} }
#undef DO_UNI_FUNC
#undef DO_APP_FUNC #undef DO_APP_FUNC
#define DO_TYPEDEF(ca, oa, n) extern n##__Class **n##__TypeInfo
namespace Genshin {
#include "il2cpp-types-ptr.h"
}
#undef DO_TYPEDEF

View File

@@ -2,10 +2,8 @@ using namespace Genshin;
// DO_APP_FUNC(CN_OFFSET, OS_OFFSET, RETURN, FUNC_NAME, (ARGS...)); // DO_APP_FUNC(CN_OFFSET, OS_OFFSET, RETURN, FUNC_NAME, (ARGS...));
DO_APP_FUNC(0x569170, 0x568470, LPVOID, il2cpp_object_new, (LPVOID t)); DO_APP_FUNC(0x002ED400, 0x002ED390, ByteArray*, il2cpp_array_new_specific, (void* arrayTypeInfo, uint64_t length));
DO_APP_FUNC(0x06C43A80, 0x06775280, ByteArray*, RecordUserData, (int32_t nType)); DO_APP_FUNC(0x0113ADC0, 0x0113AFC0, ByteArray*, RecordUserData, (int32_t nType));
DO_APP_FUNC(0x0C87B240, 0x0C89EFB0, uint16_t, BitConverter_ToUInt16, (ByteArray* val, int startIndex)); DO_APP_FUNC(0x0F826CF0, 0x0F825F10, uint16_t, BitConverter_ToUInt16, (ByteArray* val, int startIndex));
DO_UNI_FUNC(0x105560, 0x105560, ByteArray*, UnityEngine_RecordUserData, (int32_t nType));

View File

@@ -1,38 +1,27 @@
// ReSharper disable CppCStyleCast
// ReSharper disable CppInconsistentNaming
// ReSharper disable CppClangTidyBugproneMacroParentheses // ReSharper disable CppClangTidyBugproneMacroParentheses
// ReSharper disable CppClangTidyClangDiagnosticCastAlign
#include "pch.h" #include "pch.h"
#include "il2cpp-init.h" #include "il2cpp-init.h"
#define DO_APP_FUNC(ca, oa, r, n, p) r (*n) p #define DO_APP_FUNC(ca, oa, r, n, p) r (*n) p
#define DO_UNI_FUNC(ca, oa, r, n, p) r (*n) p
namespace Genshin { namespace Genshin {
#include "il2cpp-functions.h" #include "il2cpp-functions.h"
} }
#undef DO_UNI_FUNC
#undef DO_APP_FUNC #undef DO_APP_FUNC
#define DO_TYPEDEF(ca, oa, n) n##__Class **n##__TypeInfo
namespace Genshin {
#include "il2cpp-types-ptr.h"
}
#undef DO_TYPEDEF
using std::string; using std::string;
void InitIL2CPP() { void InitIL2CPP() {
TCHAR szFileName[MAX_PATH]; TCHAR szFileName[MAX_PATH];
GetModuleFileName(NULL, szFileName, MAX_PATH); GetModuleFileName(nullptr, szFileName, MAX_PATH);
auto isCN = strstr(szFileName, "YuanShen.exe");//string(szFileName).contains(); _strupr_s(szFileName);
auto hBase = GetModuleHandle("UserAssembly.dll"); const auto isCN = strstr(szFileName, "YUANSHEN.EXE");
auto bAddr = (UINT64)hBase; const auto uBase = reinterpret_cast<uint64_t>(GetModuleHandle(isCN ? "YuanShen.exe" : "GenshinImpact.exe"));
auto cAddr = (UINT64)GetModuleHandle("UnityPlayer.dll"); #define DO_APP_FUNC(ca, oa, r, n, p) n = (r (*) p)(uBase + (isCN ? ca : oa))
#define DO_APP_FUNC(ca, oa, r, n, p) n = (r (*) p)(bAddr + (isCN ? ca : oa))
#define DO_UNI_FUNC(ca, oa, r, n, p) n = (r (*) p)(cAddr + (isCN ? ca : oa))
#include "il2cpp-functions.h" #include "il2cpp-functions.h"
#undef DO_UNI_FUNC
#undef DO_APP_FUNC #undef DO_APP_FUNC
#define DO_TYPEDEF(ca, oa, n) n##__TypeInfo = (n##__Class **)(bAddr + (isCN ? ca : oa))
#include "il2cpp-types-ptr.h"
#undef DO_TYPEDEF
} }

View File

@@ -3,43 +3,13 @@
#pragma once #pragma once
#pragma region IL2CPPInternalTypes
typedef uint16_t Il2CppChar;
typedef uintptr_t il2cpp_array_size_t;
typedef int32_t il2cpp_array_lower_bound_t;
typedef struct Il2CppObject {
union {
void* klass;
void* vtable;
} Il2CppClass;
void* monitor;
} Il2CppObject;
typedef struct Il2CppString {
Il2CppObject object;
int32_t length;
Il2CppChar chars[32];
} Il2CppString;
typedef struct Il2CppArrayBounds {
il2cpp_array_size_t length;
il2cpp_array_lower_bound_t lower_bound;
} Il2CppArrayBounds;
#pragma endregion
namespace Genshin { namespace Genshin {
struct ByteArray { struct ByteArray {
void* klass; void* klass;
void* monitor; void* monitor;
Il2CppArrayBounds* bounds; void* bounds;
il2cpp_array_size_t max_length; uint64_t max_length;
uint8_t vector[32]; uint8_t vector[32];
}; };
struct CodedOutputStream__Class {
};
} }

View File

@@ -14,12 +14,8 @@
// 添加要在此处预编译的标头 // 添加要在此处预编译的标头
#include <map> #include <map>
#include <set>
#include <string> #include <string>
#include <vector>
#include <codecvt>
#include <cstdint> #include <cstdint>
#include <iostream>
#include <detours.h> #include <detours.h>
#include "HookManager.h" #include "HookManager.h"
#include "il2cpp-appdata.h" #include "il2cpp-appdata.h"

View File

@@ -12,24 +12,12 @@ VOID DisableVMProtect() {
VirtualProtect(pNtProtectVirtualMemory, 1, old, &old); VirtualProtect(pNtProtectVirtualMemory, 1, old, &old);
} }
#pragma region StringConvert
string ToString(Il2CppString* str, UINT codePage) {
auto chars = reinterpret_cast<const wchar_t*>(str->chars);
auto len = WideCharToMultiByte(codePage, 0, chars, -1, nullptr, 0, nullptr, nullptr);
auto buffer = new char[len];
WideCharToMultiByte(codePage, 0, chars, -1, buffer, len, nullptr, nullptr);
return string(buffer);
}
#pragma endregion
#pragma region ByteUtils #pragma region ByteUtils
bool IsLittleEndian() { bool IsLittleEndian() {
UINT i = 1; UINT i = 1;
char* c = (char*)&i; char* c = (char*)&i;
return (*c); return *c;
} }
#pragma endregion #pragma endregion

View File

@@ -5,10 +5,9 @@ using std::string;
VOID DisableVMProtect(); VOID DisableVMProtect();
bool IsLittleEndian(); bool IsLittleEndian();
HWND FindMainWindowByPID(DWORD pid); HWND FindMainWindowByPID(DWORD pid);
string ToString(Il2CppString* str, UINT codePage = CP_ACP);
std::string base64_encode(BYTE const* buf, unsigned int bufLen); std::string base64_encode(BYTE const* buf, unsigned int bufLen);
#define ErrorDialogT(title, msg) MessageBox(unityWnd, msg, title, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); #define ErrorDialogT(title, msg) MessageBox(unityWnd, msg, title, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL)
#define ErrorDialog(msg) ErrorDialogT("YaeAchievement", msg) #define ErrorDialog(msg) ErrorDialogT("YaeAchievement", msg)
#define Win32ErrorDialog(code) ErrorDialogT("YaeAchievement", ("CRITICAL ERROR!\nError code: " + std::to_string(GetLastError()) + "-"#code"\n\nPlease take the screenshot and contact developer by GitHub Issue to solve this problem\nNOT MIHOYO/COGNOSPHERE CUSTOMER SERVICE!").c_str()) #define Win32ErrorDialog(code) ErrorDialogT("YaeAchievement", ("CRITICAL ERROR!\nError code: " + std::to_string(GetLastError()) + "-"#code"\n\nPlease take the screenshot and contact developer by GitHub Issue to solve this problem\nNOT MIHOYO/COGNOSPHERE CUSTOMER SERVICE!").c_str())

90
res/App.Designer.cs generated
View File

@@ -9,8 +9,8 @@
namespace YaeAchievement.res { namespace YaeAchievement.res {
using System; using System;
/// <summary> /// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc. /// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary> /// </summary>
@@ -22,15 +22,15 @@ namespace YaeAchievement.res {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class App { internal class App {
private static global::System.Resources.ResourceManager resourceMan; private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture; private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal App() { internal App() {
} }
/// <summary> /// <summary>
/// Returns the cached ResourceManager instance used by this class. /// Returns the cached ResourceManager instance used by this class.
/// </summary> /// </summary>
@@ -44,7 +44,7 @@ namespace YaeAchievement.res {
return resourceMan; return resourceMan;
} }
} }
/// <summary> /// <summary>
/// Overrides the current thread's CurrentUICulture property for all /// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class. /// resource lookups using this strongly typed resource class.
@@ -58,7 +58,7 @@ namespace YaeAchievement.res {
resourceCulture = value; resourceCulture = value;
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to all achievement. /// Looks up a localized string similar to all achievement.
/// </summary> /// </summary>
@@ -67,7 +67,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("AllAchievement", resourceCulture); return ResourceManager.GetString("AllAchievement", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Please close another instance.. /// Looks up a localized string similar to Please close another instance..
/// </summary> /// </summary>
@@ -76,7 +76,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("AnotherInstance", resourceCulture); return ResourceManager.GetString("AnotherInstance", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to YaeAchievement ({0}). /// Looks up a localized string similar to YaeAchievement ({0}).
/// </summary> /// </summary>
@@ -85,7 +85,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("AppBanner", resourceCulture); return ResourceManager.GetString("AppBanner", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to You need to login genshin impact before exporting.. /// Looks up a localized string similar to You need to login genshin impact before exporting..
/// </summary> /// </summary>
@@ -94,7 +94,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ConfigNeedStartGenshin", resourceCulture); return ResourceManager.GetString("ConfigNeedStartGenshin", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Download: {0}. /// Looks up a localized string similar to Download: {0}.
/// </summary> /// </summary>
@@ -103,7 +103,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("DownloadLink", resourceCulture); return ResourceManager.GetString("DownloadLink", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Network error ({0}: {1}). /// Looks up a localized string similar to Network error ({0}: {1}).
/// </summary> /// </summary>
@@ -112,9 +112,9 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExceptionNetwork", resourceCulture); return ResourceManager.GetString("ExceptionNetwork", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Export to: /// Looks up a localized string similar to Export to:
///[0] Cocogoat (https://cocogoat.work/achievement, Default) ///[0] Cocogoat (https://cocogoat.work/achievement, Default)
///[1] Snap.HuTao ///[1] Snap.HuTao
///[2] Paimon.moe ///[2] Paimon.moe
@@ -130,7 +130,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportChoose", resourceCulture); return ResourceManager.GetString("ExportChoose", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Fail, please contact developer to get help information. /// Looks up a localized string similar to Fail, please contact developer to get help information.
/// </summary> /// </summary>
@@ -139,7 +139,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToCocogoatFail", resourceCulture); return ResourceManager.GetString("ExportToCocogoatFail", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Successfully exported to cocogoat.. /// Looks up a localized string similar to Successfully exported to cocogoat..
/// </summary> /// </summary>
@@ -148,7 +148,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToCocogoatSuccess", resourceCulture); return ResourceManager.GetString("ExportToCocogoatSuccess", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Successfully exported to {0}. /// Looks up a localized string similar to Successfully exported to {0}.
/// </summary> /// </summary>
@@ -157,7 +157,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToFileSuccess", resourceCulture); return ResourceManager.GetString("ExportToFileSuccess", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Please update Snap Hutao and retry.. /// Looks up a localized string similar to Please update Snap Hutao and retry..
/// </summary> /// </summary>
@@ -166,7 +166,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToSnapGenshinNeedUpdate", resourceCulture); return ResourceManager.GetString("ExportToSnapGenshinNeedUpdate", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Successfully exported to Snap Hutao.. /// Looks up a localized string similar to Successfully exported to Snap Hutao..
/// </summary> /// </summary>
@@ -175,7 +175,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToSnapGenshinSuccess", resourceCulture); return ResourceManager.GetString("ExportToSnapGenshinSuccess", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Please launch/update Teyvat Guide and retry.. /// Looks up a localized string similar to Please launch/update Teyvat Guide and retry..
/// </summary> /// </summary>
@@ -184,7 +184,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToTauriFail", resourceCulture); return ResourceManager.GetString("ExportToTauriFail", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Successfully exported to Teyvat Guide.. /// Looks up a localized string similar to Successfully exported to Teyvat Guide..
/// </summary> /// </summary>
@@ -193,7 +193,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToTauriSuccess", resourceCulture); return ResourceManager.GetString("ExportToTauriSuccess", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to {0}. /// Looks up a localized string similar to {0}.
/// </summary> /// </summary>
@@ -202,7 +202,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToWxApp1Success", resourceCulture); return ResourceManager.GetString("ExportToWxApp1Success", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Please update xunkong and retry.. /// Looks up a localized string similar to Please update xunkong and retry..
/// </summary> /// </summary>
@@ -211,7 +211,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToXunkongNeedUpdate", resourceCulture); return ResourceManager.GetString("ExportToXunkongNeedUpdate", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Successfully exported to xunkong.. /// Looks up a localized string similar to Successfully exported to xunkong..
/// </summary> /// </summary>
@@ -220,7 +220,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("ExportToXunkongSuccess", resourceCulture); return ResourceManager.GetString("ExportToXunkongSuccess", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Game process start ({0}). /// Looks up a localized string similar to Game process start ({0}).
/// </summary> /// </summary>
@@ -229,7 +229,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("GameLoading", resourceCulture); return ResourceManager.GetString("GameLoading", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Game exited.. /// Looks up a localized string similar to Game exited..
/// </summary> /// </summary>
@@ -238,7 +238,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("GameProcessExit", resourceCulture); return ResourceManager.GetString("GameProcessExit", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Please update genshin and retry.. /// Looks up a localized string similar to Please update genshin and retry..
/// </summary> /// </summary>
@@ -247,7 +247,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("GenshinHashError", resourceCulture); return ResourceManager.GetString("GenshinHashError", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Please close game before run this application. ({0}). /// Looks up a localized string similar to Please close game before run this application. ({0}).
/// </summary> /// </summary>
@@ -256,7 +256,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("GenshinIsRunning", resourceCulture); return ResourceManager.GetString("GenshinIsRunning", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Network error:. /// Looks up a localized string similar to Network error:.
/// </summary> /// </summary>
@@ -265,7 +265,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("NetworkError", resourceCulture); return ResourceManager.GetString("NetworkError", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to No write permission on {0}.. /// Looks up a localized string similar to No write permission on {0}..
/// </summary> /// </summary>
@@ -274,7 +274,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("NoWritePermission", resourceCulture); return ResourceManager.GetString("NoWritePermission", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Press any key to exit.. /// Looks up a localized string similar to Press any key to exit..
/// </summary> /// </summary>
@@ -283,7 +283,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("PressKeyToExit", resourceCulture); return ResourceManager.GetString("PressKeyToExit", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Reward not taken. /// Looks up a localized string similar to Reward not taken.
/// </summary> /// </summary>
@@ -292,7 +292,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("StatusFinished", resourceCulture); return ResourceManager.GetString("StatusFinished", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Invalid. /// Looks up a localized string similar to Invalid.
/// </summary> /// </summary>
@@ -301,7 +301,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("StatusInvalid", resourceCulture); return ResourceManager.GetString("StatusInvalid", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Finished. /// Looks up a localized string similar to Finished.
/// </summary> /// </summary>
@@ -310,7 +310,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("StatusRewardTaken", resourceCulture); return ResourceManager.GetString("StatusRewardTaken", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Unfinished. /// Looks up a localized string similar to Unfinished.
/// </summary> /// </summary>
@@ -319,9 +319,9 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("StatusUnfinished", resourceCulture); return ResourceManager.GetString("StatusUnfinished", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Description: /// Looks up a localized string similar to Description:
///{0}. ///{0}.
/// </summary> /// </summary>
internal static string UpdateDescription { internal static string UpdateDescription {
@@ -329,7 +329,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("UpdateDescription", resourceCulture); return ResourceManager.GetString("UpdateDescription", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Downloading update package.... /// Looks up a localized string similar to Downloading update package....
/// </summary> /// </summary>
@@ -338,7 +338,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("UpdateDownloading", resourceCulture); return ResourceManager.GetString("UpdateDownloading", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Has update: {0} =&gt; {1}. /// Looks up a localized string similar to Has update: {0} =&gt; {1}.
/// </summary> /// </summary>
@@ -347,7 +347,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("UpdateNewVersion", resourceCulture); return ResourceManager.GetString("UpdateNewVersion", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Byte[]. /// Looks up a localized resource of type System.Byte[].
/// </summary> /// </summary>
@@ -357,7 +357,7 @@ namespace YaeAchievement.res {
return ((byte[])(obj)); return ((byte[])(obj));
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Upload error to appcenter.... /// Looks up a localized string similar to Upload error to appcenter....
/// </summary> /// </summary>
@@ -366,7 +366,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("UploadError", resourceCulture); return ResourceManager.GetString("UploadError", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Use previous fetched data? (yes|no). /// Looks up a localized string similar to Use previous fetched data? (yes|no).
/// </summary> /// </summary>
@@ -375,7 +375,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("UsePreviousData", resourceCulture); return ResourceManager.GetString("UsePreviousData", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Downloading Visual C++ Redistributable.... /// Looks up a localized string similar to Downloading Visual C++ Redistributable....
/// </summary> /// </summary>
@@ -384,7 +384,7 @@ namespace YaeAchievement.res {
return ResourceManager.GetString("VcRuntimeDownload", resourceCulture); return ResourceManager.GetString("VcRuntimeDownload", resourceCulture);
} }
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Installing Visual C++ Redistributable.... /// Looks up a localized string similar to Installing Visual C++ Redistributable....
/// </summary> /// </summary>

View File

@@ -3,7 +3,7 @@
<root> <root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true"> <xsd:element name="root" msdata:IsDataSet="true">
</xsd:element> </xsd:element>
</xsd:schema> </xsd:schema>
<resheader name="resmimetype"> <resheader name="resmimetype">

View File

@@ -49,7 +49,7 @@
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI无需 自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI无需
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应 选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。 在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation --> 将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <application xmlns="urn:schemas-microsoft-com:asm.v3">

View File

@@ -9,13 +9,13 @@ message Achievement {
FINISHED = 2; FINISHED = 2;
REWARD_TAKEN = 3; REWARD_TAKEN = 3;
} }
uint32 timestamp = 14; uint32 timestamp = 11;
uint32 current = 13; uint32 current = 5;
uint32 total = 9; uint32 total = 15;
uint32 id = 7; uint32 id = 2;
Status status = 10; Status status = 7;
} }
message AchievementAllDataNotify { message AchievementAllDataNotify {
repeated Achievement list = 15; repeated Achievement list = 4;
} }

View File

@@ -1,82 +0,0 @@
using System.Net;
using System.Net.Http.Json;
using YaeAchievement.AppCenterSDK.Models;
using YaeAchievement.AppCenterSDK.Models.Serialization;
namespace YaeAchievement.AppCenterSDK;
public static class AppCenter {
private const string AppSecret = "648b83bf-d439-49bd-97f4-e1e506bdfe39";
private const string ApiUrl = "https://in.appcenter.ms/logs?api-version=1.0.0";
// ReSharper disable InconsistentNaming
public static Guid? SessionID { get; private set; }
public static readonly string DeviceID;
public static readonly Device DeviceInfo;
private static List<Log> Queue;
private static readonly Lazy<HttpClient> httpClient = new(() => new HttpClient(new HttpClientHandler {
Proxy = GlobalVars.DebugProxy ? new WebProxy("http://127.0.0.1:8888") : null
}) {
DefaultRequestHeaders = {
{ "Install-ID", DeviceID },
{ "App-Secret", AppSecret }
}
});
static AppCenter() {
Queue = new List<Log>();
DeviceID = DeviceHelper.GetDeviceID();
DeviceInfo = new Device();
var running = true;
Task.Run(() => {
// ReSharper disable once LoopVariableIsNeverChangedInsideLoop
while (running) {
Upload();
Thread.Sleep(5 * 1000);
}
});
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
running = false;
};
}
// ReSharper restore InconsistentNaming
public static void Upload() {
if (Queue.Count == 0) return;
var uploadStatus = "";
do {
Queue = Queue.Select(log => {
log.Status = LogStatus.Uploading;
return log;
}).ToList();
using var uploadContent = new StringContent(Queue.ToJson());
try {
using var response = httpClient.Value.PostAsync(ApiUrl, uploadContent).Result;
var result = response.Content.ReadFromJsonAsync<LogUploadResult>().GetAwaiter().GetResult();
uploadStatus = result!.Status;
} catch (Exception) {
// ignored
}
} while (uploadStatus != "Success");
Queue.RemoveAll(log => log.Status == LogStatus.Uploading);
}
public static void Init() {
new StartServiceLog("Analytics", "Crashes").Enqueue();
SessionID = Guid.NewGuid();
new StartSessionLog().Enqueue();
}
public static void TrackCrash(Exception exception, bool fatal = true) {
new ManagedErrorLog(exception, fatal).Enqueue();
}
public static void Enqueue(this Log log) {
Queue.Add(log);
}
private static string ToJson(this IEnumerable<Log> queue) {
return LogSerializer.Serialize(new LogContainer(queue));
}
}

View File

@@ -1,70 +0,0 @@
using Microsoft.Win32;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
namespace YaeAchievement.AppCenterSDK;
#pragma warning disable CA1416
public static class DeviceHelper {
public static string? GetOem() {
using var root = Registry.LocalMachine;
using var sub = root.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\BIOS");
var oem = sub?.GetValue("SystemManufacturer") as string;
return oem == "System manufacturer" ? null : oem;
}
public static string? GetModel() {
using var root = Registry.LocalMachine;
using var sub = root.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\BIOS");
var model = sub?.GetValue("SystemProductName") as string;
return model == "System Product Name" ? null : model;
}
public static string GetScreenSize() {
var desktop = Native.GetDC(HWND.Null);
var size = $"{Native.GetDeviceCaps(desktop, GET_DEVICE_CAPS_INDEX.DESKTOPHORZRES)}x{Native.GetDeviceCaps(desktop, GET_DEVICE_CAPS_INDEX.DESKTOPVERTRES)}";
_ = Native.ReleaseDC(HWND.Null, desktop);
return size;
}
public static string? GetCountry() {
using var root = Registry.CurrentUser;
using var sub = root.OpenSubKey("Control Panel\\International\\Geo");
return sub?.GetValue("Name") as string;
}
public static string GetSystemVersion() {
using var root = Registry.LocalMachine;
using var sub = root.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
var majorVersion = sub?.GetValue("CurrentMajorVersionNumber");
if (majorVersion != null) {
var minorVersion = sub?.GetValue("CurrentMinorVersionNumber", "0");
var buildNumber = sub?.GetValue("CurrentBuildNumber", "0");
return $"{majorVersion}.{minorVersion}.{buildNumber}";
} else {
var version = sub?.GetValue("CurrentVersion", "0.0");
var buildNumber = sub?.GetValue("CurrentBuild", "0");
return $"{version}.{buildNumber}";
}
}
public static int GetSystemBuild() {
using var root = Registry.LocalMachine;
using var sub = root.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
return (int) (sub?.GetValue("UBR") ?? 0);
}
// ReSharper disable once InconsistentNaming
public static string GetDeviceID() {
using var sdk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\miHoYoSDK");
if (sdk?.GetValue("MIHOYOSDK_DEVICE_ID") is not string id) {
id = $"{Random.Shared.NextInt64().ToString().SHA1Hash().ToLower()}{DateTimeOffset.Now.ToUnixTimeMilliseconds()}";
sdk?.SetValue("MIHOYOSDK_DEVICE_ID", id);
}
id = id.MD5Hash().ToLower();
return $"{id[..8]}-{id[8..12]}-{id[12..16]}-{id[16..20]}-{id[20..]}";
}
}
#pragma warning restore CA1416

View File

@@ -1,25 +0,0 @@
using YaeAchievement.AppCenterSDK.Models;
namespace YaeAchievement.AppCenterSDK;
public static class ErrorLogHelper {
public static MException CreateModelExceptionAndBinaries(Exception exception) {
var modelException = new MException {
Type = exception.GetType().ToString(),
Message = exception.Message,
StackTrace = exception.StackTrace
};
if (exception is AggregateException aggregateException) {
if (aggregateException.InnerExceptions.Count != 0) {
modelException.InnerExceptions = new List<MException>();
foreach (var innerException in aggregateException.InnerExceptions) {
modelException.InnerExceptions.Add(CreateModelExceptionAndBinaries(innerException));
}
}
} else if (exception.InnerException != null) {
modelException.InnerExceptions ??= new List<MException>();
modelException.InnerExceptions.Add(CreateModelExceptionAndBinaries(exception.InnerException));
}
return modelException;
}
}

View File

@@ -1,36 +0,0 @@
using System.Globalization;
using System.Reflection;
namespace YaeAchievement.AppCenterSDK.Models;
public class Device {
public string SdkName { get; set; } = "appcenter.wpf.netcore";
public string SdkVersion { get; set; } = "4.5.0";
public string OsName { get; set; } = "WINDOWS";
public string OsVersion { get; set; } = DeviceHelper.GetSystemVersion();
public string OsBuild { get; set; } = $"{DeviceHelper.GetSystemVersion()}.{DeviceHelper.GetSystemBuild()}";
public string? Model { get; set; } = DeviceHelper.GetModel();
public string? OemName { get; set; } = DeviceHelper.GetOem();
public string ScreenSize { get; set; } = DeviceHelper.GetScreenSize();
public string CarrierCountry { get; set; } = DeviceHelper.GetCountry() ?? "CN";
public string Locale { get; set; } = CultureInfo.CurrentCulture.Name;
public int TimeZoneOffset { get; set; } = (int) TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes;
public string AppVersion { get; set; } = GlobalVars.AppVersionName;
public string AppBuild { get; set; } = GlobalVars.AppVersionCode.ToString();
public string AppNamespace { get; set; } = Assembly.GetEntryAssembly()?.EntryPoint?.DeclaringType?.Namespace ?? string.Empty;
}

View File

@@ -1,14 +0,0 @@
using YaeAchievement.AppCenterSDK.Models.Serialization;
namespace YaeAchievement.AppCenterSDK.Models;
[LogId(JsonIdentifier)]
public class EventLog(string name) : LogWithProperties {
public const string JsonIdentifier = "event";
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } = name;
}

View File

@@ -1,19 +0,0 @@
using YaeAchievement.AppCenterSDK.Models.Serialization;
namespace YaeAchievement.AppCenterSDK.Models;
[LogId(JsonIdentifier)]
public class HandledErrorLog : LogWithProperties {
public const string JsonIdentifier = "handledError";
public HandledErrorLog(MException exception) {
Id = Guid.NewGuid();
Exception = exception;
}
public Guid? Id { get; set; }
public MException Exception { get; set; }
}

View File

@@ -1,21 +0,0 @@
using System.Text.Json.Serialization;
namespace YaeAchievement.AppCenterSDK.Models;
public abstract class Log {
[JsonPropertyName("sid")]
public Guid? Session { get; set; } = AppCenter.SessionID;
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
public Device Device { get; set; } = AppCenter.DeviceInfo;
[JsonIgnore]
internal LogStatus Status = LogStatus.Pending;
}
public enum LogStatus {
Pending, Uploading, Uploaded
}

View File

@@ -1,11 +0,0 @@
namespace YaeAchievement.AppCenterSDK.Models;
public class LogContainer {
public LogContainer(IEnumerable<Log> logs) {
Logs = logs;
}
public IEnumerable<Log> Logs { get; set; }
}

View File

@@ -1,13 +0,0 @@
namespace YaeAchievement.AppCenterSDK.Models;
public class LogUploadResult {
public string Status { get; set; } = null!;
public Guid[] ValidDiagnosticsIds { get; set; } = Array.Empty<Guid>();
public Guid[] ThrottledDiagnosticsIds { get; set; } = Array.Empty<Guid>();
public Guid CorrelationId { get; set; }
}

View File

@@ -1,7 +0,0 @@
namespace YaeAchievement.AppCenterSDK.Models;
public class LogWithProperties : Log {
public IDictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
}

View File

@@ -1,15 +0,0 @@
namespace YaeAchievement.AppCenterSDK.Models;
public class MException {
public string Type { get; set; } = "UnknownType";
public string? Message { get; set; }
public string? StackTrace { get; set; }
public IList<StackFrame>? Frames { get; set; }
public IList<MException>? InnerExceptions { get; set; }
}

View File

@@ -1,42 +0,0 @@
using System.Diagnostics;
using YaeAchievement.AppCenterSDK.Models.Serialization;
namespace YaeAchievement.AppCenterSDK.Models;
[LogId(JsonIdentifier)]
public class ManagedErrorLog : Log {
public const string JsonIdentifier = "managedError";
public ManagedErrorLog(
Exception exception,
bool fatal = true
) {
var p = Process.GetCurrentProcess();
Id = Guid.NewGuid();
Fatal = fatal;
UserId = AppCenter.DeviceID;
ProcessId = p.Id;
Exception = ErrorLogHelper.CreateModelExceptionAndBinaries(exception);
ProcessName = p.ProcessName;
Architecture = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
AppLaunchTimestamp = p.StartTime.ToUniversalTime();
}
public Guid Id { get; set; }
public string? UserId { get; set; }
public int ProcessId { get; set; }
public string ProcessName { get; set; }
public bool Fatal { get; set; }
public DateTime? AppLaunchTimestamp { get; set; }
public string? Architecture { get; set; }
public MException Exception { get; set; }
}

View File

@@ -1,16 +0,0 @@
using YaeAchievement.AppCenterSDK.Models.Serialization;
namespace YaeAchievement.AppCenterSDK.Models;
[LogId(JsonIdentifier)]
public class PageLog : LogWithProperties {
public const string JsonIdentifier = "page";
public PageLog(string name) {
Name = name;
}
public string Name { get; set; }
}

View File

@@ -1,8 +0,0 @@
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
[AttributeUsage(AttributeTargets.Class)]
public sealed class LogIdAttribute(string id) : Attribute {
public string Id { get; } = id;
}

View File

@@ -1,49 +0,0 @@
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
public class GuidConverter : JsonConverter<Guid> {
public static GuidConverter Instance { get; } = new ();
public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
throw new NotSupportedException();
}
public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options) {
writer.WriteStringValue(value.ToString("D"));
}
}
public class LogJsonConverter : JsonConverter<Log> {
public static LogJsonConverter Instance { get; } = new ();
private static readonly JsonSerializerOptions SerializationSettings;
static LogJsonConverter() {
SerializationSettings = new JsonSerializerOptions {
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
};
}
public override bool CanConvert(Type objectType) => typeof(Log).IsAssignableFrom(objectType);
public override Log Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
throw new NotSupportedException();
}
public override void Write(Utf8JsonWriter writer, Log value, JsonSerializerOptions options) {
var attr = value.GetType().GetCustomAttribute<LogIdAttribute>();
if (attr == null) {
throw new JsonException("Log type is missing LogTypeAttribute");
}
var cNode = JsonSerializer.SerializeToNode((object) value, SerializationSettings)!;
cNode["type"] = attr.Id;
writer.WriteRawValue(cNode.ToString());
}
}

View File

@@ -1,25 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
public static class LogSerializer {
private static readonly JsonSerializerOptions SerializationSettings;
static LogSerializer() {
SerializationSettings = new JsonSerializerOptions {
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
Converters = {
GuidConverter.Instance,
LogJsonConverter.Instance,
}
};
}
public static string Serialize(LogContainer logContainer) {
return JsonSerializer.Serialize(logContainer, SerializationSettings);
}
}

View File

@@ -1,26 +0,0 @@
namespace YaeAchievement.AppCenterSDK.Models;
public class StackFrame {
public StackFrame(string address, string code, string className, string methodName, int? lineNumber, string fileName) {
Address = address;
Code = code;
ClassName = className;
MethodName = methodName;
LineNumber = lineNumber;
FileName = fileName;
}
public string Address { get; set; }
public string Code { get; set; }
public string ClassName { get; set; }
public string MethodName { get; set; }
public int? LineNumber { get; set; }
public string FileName { get; set; }
}

View File

@@ -1,16 +0,0 @@
using YaeAchievement.AppCenterSDK.Models.Serialization;
namespace YaeAchievement.AppCenterSDK.Models;
[LogId(JsonIdentifier)]
public class StartServiceLog : Log {
public const string JsonIdentifier = "startService";
public StartServiceLog(params string[] services) {
Services = services;
}
public string[] Services { get; set; }
}

View File

@@ -1,8 +0,0 @@
using YaeAchievement.AppCenterSDK.Models.Serialization;
namespace YaeAchievement.AppCenterSDK.Models;
[LogId(JsonIdentifier)]
public class StartSessionLog : Log {
public const string JsonIdentifier = "startSession";
}

View File

@@ -1,10 +1,10 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using YaeAchievement.res; using YaeAchievement.res;
namespace YaeAchievement; namespace YaeAchievement;
public static partial class AppConfig {
public static class AppConfig {
public static string GamePath { get; private set; } = null!; public static string GamePath { get; private set; } = null!;
internal static void Load(string argumentPath) { internal static void Load(string argumentPath) {
@@ -36,8 +36,8 @@ public static class AppConfig {
var osLastWriteTime = File.GetLastWriteTime(osLogPath); var osLastWriteTime = File.GetLastWriteTime(osLogPath);
finalLogPath = cnLastWriteTime > osLastWriteTime ? cnLogPath : osLogPath; finalLogPath = cnLastWriteTime > osLastWriteTime ? cnLogPath : osLogPath;
} }
GamePath = GetGamePathFromLogFile(finalLogPath) GamePath = GetGamePathFromLogFile(finalLogPath)
?? GetGamePathFromLogFile($"{finalLogPath}.last") ?? GetGamePathFromLogFile($"{finalLogPath}.last")
?? throw new ApplicationException(App.ConfigNeedStartGenshin); ?? throw new ApplicationException(App.ConfigNeedStartGenshin);
pathCacheFile.Write(GamePath); pathCacheFile.Write(GamePath);
} }
@@ -52,11 +52,15 @@ public static class AppConfig {
try { try {
File.Delete(copiedLogFilePath); File.Delete(copiedLogFilePath);
} catch (Exception) { /* ignore */} } catch (Exception) { /* ignore */}
var matchResult = Regex.Match(content, @"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)"); var matchResult = GamePathRegex().Match(content);
if (!matchResult.Success) { if (!matchResult.Success) {
return null; return null;
} }
var entryName = matchResult.Groups["1"].Value.Replace("_Data", ".exe"); var entryName = matchResult.Groups["1"].Value.Replace("_Data", ".exe");
return Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName)); return Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
} }
[GeneratedRegex(@"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)", RegexOptions.IgnoreCase)]
private static partial Regex GamePathRegex();
} }

View File

@@ -2,18 +2,14 @@
using Google.Protobuf; using Google.Protobuf;
using Proto; using Proto;
namespace YaeAchievement; namespace YaeAchievement;
public class CacheFile { public class CacheFile(string identifier) {
private readonly string _cacheName; private readonly string _cacheName = Path.Combine(GlobalVars.CachePath, $"{identifier.MD5Hash()[..16]}.miko");
private CacheItem? _content; private CacheItem? _content;
public DateTime LastWriteTime => Exists() ? File.GetLastWriteTimeUtc(_cacheName) : DateTime.UnixEpoch;
public CacheFile(string identifier) { public DateTime LastWriteTime => Exists() ? File.GetLastWriteTimeUtc(_cacheName) : DateTime.UnixEpoch;
_cacheName = Path.Combine(GlobalVars.CachePath, $"{identifier.MD5Hash()[..16]}.miko");
}
public bool Exists() => File.Exists(_cacheName); public bool Exists() => File.Exists(_cacheName);
@@ -29,7 +25,7 @@ public class CacheFile {
public void Write(string data, string? etag = null) => Write(ByteString.CopyFromUtf8(data), data.MD5Hash(), etag); public void Write(string data, string? etag = null) => Write(ByteString.CopyFromUtf8(data), data.MD5Hash(), etag);
public void Write(byte[] data, string? etag = null) => Write(ByteString.CopyFrom(data), data.MD5Hash(), etag); public void Write(byte[] data, string? etag = null) => Write(ByteString.CopyFrom(data), data.MD5Hash(), etag);
private void Write(ByteString data, string hash, string? etag) { private void Write(ByteString data, string hash, string? etag) {
using var fOut = File.OpenWrite(_cacheName); using var fOut = File.OpenWrite(_cacheName);
using var cOut = new GZipStream(fOut, CompressionLevel.SmallestSize); using var cOut = new GZipStream(fOut, CompressionLevel.SmallestSize);

View File

@@ -6,16 +6,19 @@ using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Microsoft.Win32; using Microsoft.Win32;
using Proto; using Proto;
using YaeAchievement.AppCenterSDK;
using YaeAchievement.res; using YaeAchievement.res;
using static Proto.Achievement.Types; using static Proto.Achievement.Types;
namespace YaeAchievement; namespace YaeAchievement;
public static class Export { public static class Export {
public static uint ExportTo { get; set; } = uint.MaxValue; public static uint ExportTo { get; set; } = uint.MaxValue;
private static readonly JsonSerializerOptions JsonOpts = new () {
WriteIndented = true
};
public static void Choose(AchievementAllDataNotify data) { public static void Choose(AchievementAllDataNotify data) {
if (ExportTo == uint.MaxValue) { if (ExportTo == uint.MaxValue) {
Console.Write(App.ExportChoose); Console.Write(App.ExportChoose);
@@ -40,16 +43,15 @@ public static class Export {
} }
private class CocogoatResponse { private class CocogoatResponse {
[JsonPropertyName("key")] public string Code { get; set; } = null!; [JsonPropertyName("key")] public string Code { get; init; } = null!;
} }
private static void ToCocogoat(AchievementAllDataNotify data) { private static void ToCocogoat(AchievementAllDataNotify data) {
var result = JsonSerializer.Serialize(ExportToUIAFApp(data)); var result = JsonSerializer.Serialize(ExportToUIAFApp(data));
using var request = new HttpRequestMessage { using var request = new HttpRequestMessage();
Method = HttpMethod.Post, request.Method = HttpMethod.Post;
RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}"), request.RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}");
Content = new StringContent(result, Encoding.UTF8, "application/json") request.Content = new StringContent(result, Encoding.UTF8, "application/json");
};
using var response = Utils.CHttpClient.Send(request); using var response = Utils.CHttpClient.Send(request);
if (response.StatusCode != HttpStatusCode.Created) { if (response.StatusCode != HttpStatusCode.Created) {
Console.WriteLine(App.ExportToCocogoatFail); Console.WriteLine(App.ExportToCocogoatFail);
@@ -61,18 +63,17 @@ public static class Export {
? App.ExportToCocogoatSuccess ? App.ExportToCocogoatSuccess
: $"https://cocogoat.work/achievement?memo={responseJson.Code}"); : $"https://cocogoat.work/achievement?memo={responseJson.Code}");
} }
private static void ToWxApp1(AchievementAllDataNotify data) { private static void ToWxApp1(AchievementAllDataNotify data) {
var id = Guid.NewGuid().ToString("N").Substring(20, 8); var id = Guid.NewGuid().ToString("N").Substring(20, 8);
var result = JsonSerializer.Serialize(new Dictionary<string, object> { var result = JsonSerializer.Serialize(new Dictionary<string, object> {
{ "key", id }, { "key", id },
{ "data", ExportToUIAFApp(data) } { "data", ExportToUIAFApp(data) }
}); });
using var request = new HttpRequestMessage { using var request = new HttpRequestMessage();
Method = HttpMethod.Post, request.Method = HttpMethod.Post;
RequestUri = new Uri("https://api.qyinter.com/achievementRedis"), request.RequestUri = new Uri("https://api.qyinter.com/achievementRedis");
Content = new StringContent(result, Encoding.UTF8, "application/json") request.Content = new StringContent(result, Encoding.UTF8, "application/json");
};
using var response = Utils.CHttpClient.Send(request); using var response = Utils.CHttpClient.Send(request);
Console.WriteLine(App.ExportToWxApp1Success, id); Console.WriteLine(App.ExportToWxApp1Success, id);
} }
@@ -87,7 +88,7 @@ public static class Export {
Utils.ShellOpen("ms-windows-store://pdp/?productid=9PH4NXJ2JN52"); Utils.ShellOpen("ms-windows-store://pdp/?productid=9PH4NXJ2JN52");
} }
} }
private static void ToXunkong(AchievementAllDataNotify data) { private static void ToXunkong(AchievementAllDataNotify data) {
if (CheckWinUIAppScheme("xunkong")) { if (CheckWinUIAppScheme("xunkong")) {
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data))); Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
@@ -100,7 +101,7 @@ public static class Export {
} }
private static void ToTeyvatGuide(AchievementAllDataNotify data) { private static void ToTeyvatGuide(AchievementAllDataNotify data) {
if (Process.GetProcessesByName("TeyvatGuide").Any()) { if (Process.GetProcessesByName("TeyvatGuide").Length != 0) {
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data))); Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
Utils.ShellOpen("teyvatguide://import_uigf?app=YaeAchievement"); Utils.ShellOpen("teyvatguide://import_uigf?app=YaeAchievement");
Console.WriteLine(App.ExportToTauriSuccess); Console.WriteLine(App.ExportToTauriSuccess);
@@ -119,30 +120,25 @@ public static class Export {
} }
private static void ToPaimon(AchievementAllDataNotify data) { private static void ToPaimon(AchievementAllDataNotify data) {
var info = LoadAchievementInfo(); var info = LoadAchievementInfo().Items.ToDictionary(pair => pair.Key, pair => pair.Value.Group);
var output = new Dictionary<uint, Dictionary<uint, bool>>();
foreach (var ach in data.List.Where(a => a.Status is Status.Finished or Status.RewardTaken)) {
if (!info.Items.TryGetValue(ach.Id, out var achInfo) || achInfo == null) {
Console.WriteLine($@"Unable to find {ach.Id} in metadata.");
continue;
}
var map = output.GetValueOrDefault(achInfo.Group, new Dictionary<uint, bool>());
map[ach.Id == 81222 ? 81219 : ach.Id] = true;
output[achInfo.Group] = map;
}
var final = new Dictionary<string, Dictionary<uint, Dictionary<uint, bool>>> { var final = new Dictionary<string, Dictionary<uint, Dictionary<uint, bool>>> {
["achievement"] = output.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value) ["achievement"] = data.List
.Where(achievement => achievement.Status is Status.Finished or Status.RewardTaken)
.Where(achievement => info.ContainsKey(achievement.Id))
.GroupBy(achievement => info[achievement.Id], achievement => achievement.Id)
.OrderBy(group => group.Key)
.ToDictionary(group => group.Key, group => group.ToDictionary(id => id, _ => true))
}; };
var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-paimon.json"); var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-paimon.json");
if (TryWriteToFile(path, JsonSerializer.Serialize(final))) { if (TryWriteToFile(path, JsonSerializer.Serialize(final))) {
Console.WriteLine(App.ExportToFileSuccess, path); Console.WriteLine(App.ExportToFileSuccess, path);
} }
} }
private static void ToSeelie(AchievementAllDataNotify data) { private static void ToSeelie(AchievementAllDataNotify data) {
var output = new Dictionary<uint, Dictionary<string, bool>>(); var output = new Dictionary<uint, Dictionary<string, bool>>();
foreach (var ach in data.List.Where(a => a.Status is Status.Finished or Status.RewardTaken)) { foreach (var ach in data.List.Where(a => a.Status is Status.Finished or Status.RewardTaken)) {
output[ach.Id == 81222 ? 81219 : ach.Id] = new Dictionary<string, bool> { output[ach.Id] = new Dictionary<string, bool> {
["done"] = true ["done"] = true
}; };
} }
@@ -171,10 +167,10 @@ public static class Export {
finishAt = DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss"); finishAt = DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
} }
var current = ach.Status != Status.Unfinished ? ach.Current == 0 ? ach.Total : ach.Current : ach.Current; var current = ach.Status != Status.Unfinished ? ach.Current == 0 ? ach.Total : ach.Current : ach.Current;
outList.Add(new List<object> { outList.Add([
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name, ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
achInfo.Description, current, ach.Total, finishAt achInfo.Description, current, ach.Total, finishAt
}); ]);
} }
var output = new List<string> { "ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间" }; var output = new List<string> { "ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间" };
output.AddRange(outList.OrderBy(v => v[2]).Select(item => { output.AddRange(outList.OrderBy(v => v[2]).Select(item => {
@@ -190,9 +186,7 @@ public static class Export {
private static void ToRawJson(AchievementAllDataNotify data) { private static void ToRawJson(AchievementAllDataNotify data) {
var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-raw.json"); var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-raw.json");
var text = JsonSerializer.Serialize(data, new JsonSerializerOptions { var text = JsonSerializer.Serialize(data, JsonOpts);
WriteIndented = true
});
if (TryWriteToFile(path, text)) { if (TryWriteToFile(path, text)) {
Console.WriteLine(App.ExportToFileSuccess, path); Console.WriteLine(App.ExportToFileSuccess, path);
} }
@@ -220,6 +214,7 @@ public static class Export {
}; };
} }
// ReSharper disable once InconsistentNaming
private static bool CheckWinUIAppScheme(string protocol) { private static bool CheckWinUIAppScheme(string protocol) {
return (string?)Registry.ClassesRoot.OpenSubKey(protocol)?.GetValue("") == $"URL:{protocol}"; return (string?)Registry.ClassesRoot.OpenSubKey(protocol)?.GetValue("") == $"URL:{protocol}";
} }
@@ -227,8 +222,8 @@ public static class Export {
private static string JoinToString(this IEnumerable<object> list, string separator) { private static string JoinToString(this IEnumerable<object> list, string separator) {
return string.Join(separator, list); return string.Join(separator, list);
} }
private static readonly List<uint> UnusedAchievement = new() { 84517 }; private static readonly List<uint> UnusedAchievement = [ 84517 ];
private static string ToDesc(this Status status) { private static string ToDesc(this Status status) {
return status switch { return status switch {
@@ -248,7 +243,6 @@ public static class Export {
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) { public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
// ReSharper disable once LocalizableElement // ReSharper disable once LocalizableElement
Console.WriteLine($"{msg}: {ex.Message}"); Console.WriteLine($"{msg}: {ex.Message}");
AppCenter.TrackCrash(ex, false);
return ex.NativeErrorCode; return ex.NativeErrorCode;
} }

View File

@@ -10,21 +10,21 @@ public static class Extensions {
private static readonly Lazy<MD5> md5 = new (MD5.Create); private static readonly Lazy<MD5> md5 = new (MD5.Create);
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
private static readonly Lazy<SHA1> sha1 = new (SHA1.Create); private static readonly Lazy<SHA1> sha1 = new (SHA1.Create);
public static byte[] ToBytes(this string text) { public static byte[] ToBytes(this string text) {
return Encoding.UTF8.GetBytes(text); return Encoding.UTF8.GetBytes(text);
} }
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
public static string MD5Hash(this string text) { public static string MD5Hash(this string text) {
return text.ToBytes().MD5Hash(); return text.ToBytes().MD5Hash();
} }
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
public static string MD5Hash(this byte[] data) { public static string MD5Hash(this byte[] data) {
return md5.Value.ComputeHash(data).ToHex().ToLower(); return md5.Value.ComputeHash(data).ToHex().ToLower();
} }
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
public static string SHA1Hash(this string text, bool base64 = true) { public static string SHA1Hash(this string text, bool base64 = true) {
var bytes = sha1.Value.ComputeHash(text.ToBytes()); var bytes = sha1.Value.ComputeHash(text.ToBytes());
@@ -34,7 +34,7 @@ public static class Extensions {
public static string ToHex(this byte[] bytes) { public static string ToHex(this byte[] bytes) {
return Convert.ToHexString(bytes); return Convert.ToHexString(bytes);
} }
public static string ToBase64(this byte[] bytes) { public static string ToBase64(this byte[] bytes) {
return Convert.ToBase64String(bytes); return Convert.ToBase64String(bytes);
} }

View File

@@ -1,6 +1,6 @@
using System.Reflection; using System.Reflection;
namespace YaeAchievement; namespace YaeAchievement;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
// ReSharper disable ConvertToConstant.Global // ReSharper disable ConvertToConstant.Global
@@ -8,21 +8,21 @@ namespace YaeAchievement;
// ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once MemberCanBePrivate.Global
public static class GlobalVars { public static class GlobalVars {
public static bool DebugProxy => false; public static bool DebugProxy => false;
public static bool UnexpectedExit { get; set; } = true; public static bool UnexpectedExit { get; set; } = true;
public static bool PauseOnExit { get; set; } = true; public static bool PauseOnExit { get; set; } = true;
public static Version AppVersion { get; } = Assembly.GetEntryAssembly()!.GetName().Version!; public static Version AppVersion { get; } = Assembly.GetEntryAssembly()!.GetName().Version!;
public static readonly string AppPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly string AppPath = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string CommonData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); private static readonly string CommonData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
public static readonly string DataPath = Path.Combine(CommonData, "Yae"); public static readonly string DataPath = Path.Combine(CommonData, "Yae");
public static readonly string CachePath = Path.Combine(DataPath, "cache"); public static readonly string CachePath = Path.Combine(DataPath, "cache");
public static readonly string LibFilePath = Path.Combine(DataPath, "YaeAchievement.dll"); public static readonly string LibFilePath = Path.Combine(DataPath, "YaeAchievement.dll");
public const uint AppVersionCode = 45; public const uint AppVersionCode = 51;
public const string AppVersionName = "3.5"; public const string AppVersionName = "4.1";
public const string PipeName = "YaeAchievementPipe"; public const string PipeName = "YaeAchievementPipe";
public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com"; public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com";

View File

@@ -7,7 +7,7 @@ using Windows.Win32.System.Threading;
namespace YaeAchievement; namespace YaeAchievement;
public static class Injector { public static class Injector {
public static unsafe bool CreateProcess(string path, out HANDLE hProc, out HANDLE hThread, out uint pid) { public static unsafe bool CreateProcess(string path, out HANDLE hProc, out HANDLE hThread, out uint pid) {
Span<char> cmdLines = stackalloc char[1]; // "\0" Span<char> cmdLines = stackalloc char[1]; // "\0"
var si = new STARTUPINFOW { var si = new STARTUPINFOW {

View File

@@ -1,10 +1,12 @@
using Proto; using System.Text;
using Proto;
using YaeAchievement; using YaeAchievement;
using YaeAchievement.AppCenterSDK;
using YaeAchievement.AppCenterSDK.Models;
using YaeAchievement.res; using YaeAchievement.res;
using static YaeAchievement.Utils; using static YaeAchievement.Utils;
Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;
TryDisableQuickEdit(); TryDisableQuickEdit();
InstallExitHook(); InstallExitHook();
InstallExceptionHook(); InstallExceptionHook();
@@ -22,13 +24,6 @@ AppConfig.Load(args.GetOrNull(0) ?? "auto");
Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue; Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue;
CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2))); CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2)));
AppCenter.Init();
new EventLog("AppInit") {
Properties = {
{ "AppVersion", GlobalVars.AppVersionName },
{ "SystemVersion", DeviceHelper.GetSystemVersion() }
}
}.Enqueue();
var historyCache = new CacheFile("ExportData"); var historyCache = new CacheFile("ExportData");

View File

@@ -11,7 +11,6 @@ using Windows.Win32;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
using Windows.Win32.System.Console; using Windows.Win32.System.Console;
using Proto; using Proto;
using YaeAchievement.AppCenterSDK;
using YaeAchievement.res; using YaeAchievement.res;
namespace YaeAchievement; namespace YaeAchievement;
@@ -31,10 +30,9 @@ public static class Utils {
public static byte[] GetBucketFileAsByteArray(string path, bool cache = true) { public static byte[] GetBucketFileAsByteArray(string path, bool cache = true) {
try { try {
using var msg = new HttpRequestMessage { using var msg = new HttpRequestMessage();
Method = HttpMethod.Get, msg.Method = HttpMethod.Get;
RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}") msg.RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}");
};
var cacheFile = new CacheFile(path); var cacheFile = new CacheFile(path);
if (cache && cacheFile.Exists()) { if (cache && cacheFile.Exists()) {
msg.Headers.TryAddWithoutValidation("If-None-Match", $"{cacheFile.Read().Etag}"); msg.Headers.TryAddWithoutValidation("If-None-Match", $"{cacheFile.Read().Etag}");
@@ -64,7 +62,7 @@ public static class Utils {
public static uint? ToUIntOrNull(string? value) { public static uint? ToUIntOrNull(string? value) {
return value != null ? uint.TryParse(value, out var result) ? result : null : null; return value != null ? uint.TryParse(value, out var result) ? result : null : null;
} }
public static bool ToBooleanOrFalse(string? value) { public static bool ToBooleanOrFalse(string? value) {
return value != null && bool.TryParse(value, out var result) && result; return value != null && bool.TryParse(value, out var result) && result;
} }
@@ -73,11 +71,11 @@ public static class Utils {
if (Native.OpenClipboard(HWND.Null)) if (Native.OpenClipboard(HWND.Null))
{ {
Native.EmptyClipboard(); Native.EmptyClipboard();
var hGlobal = (HANDLE) Marshal.AllocHGlobal((text.Length + 1) * 2); var hGlobal = (HGLOBAL) Marshal.AllocHGlobal((text.Length + 1) * 2);
var hPtr = (nint) Native.GlobalLock(hGlobal); var hPtr = (nint) Native.GlobalLock(hGlobal);
Marshal.Copy(text.ToCharArray(), 0, hPtr, text.Length); Marshal.Copy(text.ToCharArray(), 0, hPtr, text.Length);
Native.GlobalUnlock(hPtr); Native.GlobalUnlock((HGLOBAL) hPtr);
Native.SetClipboardData(13, hGlobal); Native.SetClipboardData(13, new HANDLE(hPtr));
Marshal.FreeHGlobal(hGlobal); Marshal.FreeHGlobal(hGlobal);
Native.CloseClipboard(); Native.CloseClipboard();
} }
@@ -89,7 +87,7 @@ public static class Utils {
// ReSharper disable once NotAccessedField.Local // ReSharper disable once NotAccessedField.Local
private static UpdateInfo _updateInfo = null!; private static UpdateInfo _updateInfo = null!;
public static void CheckUpdate(bool useLocalLib) { public static void CheckUpdate(bool useLocalLib) {
var info = UpdateInfo.Parser.ParseFrom(GetBucketFileAsByteArray("schicksal/version"))!; var info = UpdateInfo.Parser.ParseFrom(GetBucketFileAsByteArray("schicksal/version"))!;
if (GlobalVars.AppVersionCode < info.VersionCode) { if (GlobalVars.AppVersionCode < info.VersionCode) {
@@ -162,8 +160,8 @@ public static class Utils {
public static void CheckGenshinIsRunning() { public static void CheckGenshinIsRunning() {
Process.EnterDebugMode(); Process.EnterDebugMode();
foreach (var process in Process.GetProcesses()) { foreach (var process in Process.GetProcesses()) {
if (process.ProcessName is "GenshinImpact" or "YuanShen" if (process.ProcessName is "GenshinImpact" or "YuanShen"
&& !process.HasExited && !process.HasExited
&& process.MainWindowHandle != nint.Zero && process.MainWindowHandle != nint.Zero
) { ) {
Console.WriteLine(App.GenshinIsRunning, process.Id); Console.WriteLine(App.GenshinIsRunning, process.Id);
@@ -172,10 +170,10 @@ public static class Utils {
} }
Process.LeaveDebugMode(); Process.LeaveDebugMode();
} }
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
private static Process? proc; private static Process? proc;
public static void InstallExitHook() { public static void InstallExitHook() {
AppDomain.CurrentDomain.ProcessExit += (_, _) => { AppDomain.CurrentDomain.ProcessExit += (_, _) => {
proc?.Kill(); proc?.Kill();
@@ -201,9 +199,6 @@ public static class Utils {
break; break;
default: default:
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
Console.WriteLine(App.UploadError);
AppCenter.TrackCrash((Exception) e.ExceptionObject);
AppCenter.Upload();
break; break;
} }
Environment.Exit(-1); Environment.Exit(-1);
@@ -227,7 +222,7 @@ public static class Utils {
Environment.Exit(0); Environment.Exit(0);
} }
AppDomain.CurrentDomain.ProcessExit += (_, _) => { AppDomain.CurrentDomain.ProcessExit += (_, _) => {
try { try {
File.Delete(GlobalVars.LibFilePath); File.Delete(GlobalVars.LibFilePath);
} catch (Exception) { /* ignored */ } } catch (Exception) { /* ignored */ }
}; };
@@ -298,7 +293,7 @@ public static class Utils {
} }
}) })
.Select(item => item?.GetValue("DisplayName") as string ?? string.Empty) .Select(item => item?.GetValue("DisplayName") as string ?? string.Empty)
.Any(name => name.Contains("Microsoft Visual C++ 2022 X64 ")); .Any(name => name.Contains("Microsoft Visual C++ 2022 X64 ") || name.Contains("Microsoft Visual C++ 2015-2022 Redistributable (x64)"));
if (!installed) { if (!installed) {
Console.WriteLine(App.VcRuntimeDownload); Console.WriteLine(App.VcRuntimeDownload);
var pkgPath = Path.Combine(GlobalVars.DataPath, "vc_redist.x64.exe"); var pkgPath = Path.Combine(GlobalVars.DataPath, "vc_redist.x64.exe");
@@ -312,12 +307,9 @@ public static class Utils {
} }
await File.WriteAllBytesAsync(pkgPath, bytes); await File.WriteAllBytesAsync(pkgPath, bytes);
Console.WriteLine(App.VcRuntimeInstalling); Console.WriteLine(App.VcRuntimeInstalling);
using var process = new Process { using var process = new Process();
StartInfo = { process.StartInfo.FileName = pkgPath;
FileName = pkgPath, process.StartInfo.Arguments = "/install /passive /norestart";
Arguments = "/install /passive /norestart"
}
};
process.Start(); process.Start();
await process.WaitForExitAsync(); await process.WaitForExitAsync();
File.Delete(pkgPath); File.Delete(pkgPath);