mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-08 23:48:12 +08:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
793ad075fe | ||
|
|
f737122247 | ||
|
|
520167ef85 | ||
|
|
faee6f6121 | ||
|
|
06c5468118 | ||
|
|
b7c2204f68 | ||
|
|
5dc5e646d6 | ||
|
|
9cab7e8702 | ||
|
|
1f080fe084 | ||
|
|
c8497243c0 | ||
|
|
9abdd123ee | ||
|
|
e1429289ad | ||
|
|
1f311ed987 | ||
|
|
cc346915e3 | ||
|
|
cd0f49d83d | ||
|
|
d0b7d15894 | ||
|
|
504c8a2a9a | ||
|
|
b3162052da | ||
|
|
034d999d25 | ||
|
|
45d5620e83 | ||
|
|
fa13f9c8e5 | ||
|
|
2210a97d61 | ||
|
|
feb7ac44da | ||
|
|
3924129560 | ||
|
|
4f7f0cdfd2 | ||
|
|
cf0753c676 | ||
|
|
0b895d47ca | ||
|
|
78d2722e20 | ||
|
|
385c673323 | ||
|
|
50beb2cce7 | ||
|
|
324a4153e0 | ||
|
|
3de459aceb | ||
|
|
295bb89177 | ||
|
|
baaf4e8227 | ||
|
|
f41fe6fb12 | ||
|
|
78bda3f49c | ||
|
|
a10dc22461 | ||
|
|
74dda750ef | ||
|
|
099270ad29 | ||
|
|
fe5b2c0c12 | ||
|
|
ed5d99745c | ||
|
|
7175cd7427 | ||
|
|
73747bcce5 | ||
|
|
b12c3209d7 | ||
|
|
5805070627 |
10
.github/workflows/dotnet.yml
vendored
10
.github/workflows/dotnet.yml
vendored
@@ -12,19 +12,19 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
run: dotnet build -c Release --no-restore
|
||||
- name: Publish
|
||||
run: dotnet publish --property:OutputPath=.\publish\
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Artifacts
|
||||
path: publish
|
||||
|
||||
64
.gitlab-ci.yml
Normal file
64
.gitlab-ci.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- release
|
||||
|
||||
Test:
|
||||
stage: test
|
||||
image: mcr.microsoft.com/windows/server
|
||||
tags:
|
||||
- windows
|
||||
script:
|
||||
- dotnet restore
|
||||
- dotnet build -c Release --no-restore
|
||||
- dotnet publish --property:OutputPath=.\publish\
|
||||
- Move-Item -Path .\publish\publish\*.exe -Destination ..\ -Force
|
||||
|
||||
Build:
|
||||
stage: build
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- windows
|
||||
needs:
|
||||
- job: Test
|
||||
script:
|
||||
- echo "This is build stage."
|
||||
- Move-Item -Path ..\YaeAchievement.exe .\ -Force
|
||||
after_script:
|
||||
- echo "Current Job ID is $CI_JOB_ID"
|
||||
- echo "THIS_JOB_ID=$CI_JOB_ID" >> build.env
|
||||
artifacts:
|
||||
paths:
|
||||
- .\*.exe
|
||||
expire_in: 90 days
|
||||
reports:
|
||||
dotenv: build.env
|
||||
|
||||
|
||||
release:
|
||||
stage: release
|
||||
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||
only:
|
||||
- tags
|
||||
needs:
|
||||
- job: Build
|
||||
artifacts: true
|
||||
variables:
|
||||
TAG: '$CI_COMMIT_TAG'
|
||||
script:
|
||||
- echo "Create Release $TAG"
|
||||
- echo "$THIS_JOB_ID"
|
||||
release:
|
||||
name: '$TAG'
|
||||
tag_name: '$TAG'
|
||||
ref: '$TAG'
|
||||
description: 'Release $TAG by CI'
|
||||
assets:
|
||||
links:
|
||||
- name: "YaeAchievement.exe"
|
||||
url: "https://$CI_SERVER_SHELL_SSH_HOST/$CI_PROJECT_PATH/-/jobs/$THIS_JOB_ID/artifacts/raw/YaeAchievement.exe?inline=false"
|
||||
link_type: package
|
||||
- name: ".NET 7.0 Desktop Runtime"
|
||||
url: "https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/runtime-desktop-7.0.11-windows-x64-installer"
|
||||
link_type: other
|
||||
17
README.md
17
README.md
@@ -9,9 +9,22 @@
|
||||
|
||||
- 支持导出所有类别的成就
|
||||
- 支持官服,渠道服与国际服
|
||||
- 支持导出至[椰羊](https://cocogoat.work/achievement)、[Snap·HuTao](https://github.com/DGP-Studio/Snap.HuTao)、[Paimon.moe](https://paimon.moe/achievement/)、[Seelie.me](https://seelie.me/achievements)、[寻空](https://github.com/xunkong/xunkong)和表格文件(csv)
|
||||
- 没有窗口大小、游戏语言等要求
|
||||
|
||||
## 导出支持
|
||||
|
||||
> 按照数字键选择导出方式,<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.md](Tutorial.md)
|
||||
|
||||
@@ -23,7 +36,7 @@
|
||||
|
||||
## 常见问题
|
||||
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)
|
||||
A: 不要把软件和原神主程序放一起
|
||||
|
||||
20
README_EN.md
20
README_EN.md
@@ -6,16 +6,26 @@
|
||||
|
||||
[简体中文](README.md) | English
|
||||
|
||||
**I18n support currently in [snapshot version](https://github.com/HolographicHat/YaeAchievement/actions/)**
|
||||
**Next release: 2022/09/28 (Genshin 3.1)**
|
||||
|
||||
</div>
|
||||
|
||||
- Support for exporting all categories of achievements
|
||||
- Supports all versions of Genshin Impact
|
||||
- Support for exporting to [Cocogoat](https://cocogoat.work/achievement), [Snap·HuTao](https://github.com/DGP-Studio/Snap.HuTao), [Paimon.moe](https://paimon.moe/achievement/), [Seelie.me](https://seelie.me/achievements)、[XunKong](https://github.com/xunkong/xunkong) and form files (csv)
|
||||
- There are no requirements for window size, game language, etc.
|
||||
|
||||
## Export support
|
||||
|
||||
> Select the export method according to the number keys, <kbd>0</kbd> is the default export method
|
||||
|
||||
0. [Cocogoat](https://cocogoat.work/achievement)
|
||||
1. [Snap HuTao](https://github.com/DGP-Studio/Snap.HuTao)
|
||||
2. [Paimon.moe](https://paimon.moe/achievement/)
|
||||
3. [Seelie.me](https://seelie.me/achievements)
|
||||
4. Form File `.csv`
|
||||
5. [XunKong](https://github.com/xunkong/xunkong)
|
||||
6. [YuanmoTools](https://apps.apple.com/app/id1663989619)
|
||||
7. [TeyvatGuide](https://github.com/BTMuli/TeyvatGuide)
|
||||
8. [UIAF](https://uigf.org/standards/UIAF.html) JSON file
|
||||
|
||||
## Instructions for Use:
|
||||
→ [Tutorial_EN.md](Tutorial_EN.md)
|
||||
|
||||
@@ -26,7 +36,7 @@
|
||||
|
||||
## Frequently asked questions
|
||||
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)
|
||||
|
||||
14
Tutorial.md
14
Tutorial.md
@@ -10,7 +10,7 @@
|
||||
|
||||
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 。
|
||||
|
||||
进入网页后浏览器会自动弹出下载,同样地,将文件保存在桌面或者其它易于寻找的文件夹内。
|
||||
|
||||
@@ -71,11 +71,11 @@
|
||||
### 各种工具的介绍烦请移步至各工具的官方页面进行查看(下方序号对应导出序号)
|
||||
|
||||
0. [椰羊](https://cocogoat.work/achievement)
|
||||
|
||||
1. [Snap·HuTao](https://github.com/DGP-Studio/Snap.HuTao)
|
||||
|
||||
1. [胡桃工具箱](https://github.com/DGP-Studio/Snap.HuTao)
|
||||
2. [Paimon.moe](https://paimon.moe/achievement/)
|
||||
|
||||
3. [Seelie.me](https://seelie.me/achievements)
|
||||
|
||||
4. [寻空](https://github.com/xunkong/xunkong)
|
||||
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 文件
|
||||
|
||||
@@ -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)
|
||||
|
||||
Click Here:https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-7.0.7-windows-x64-installer .
|
||||
Click Here:https://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.
|
||||
|
||||
@@ -65,9 +65,12 @@ At this time, you can select the Achievements in the left column to view the imp
|
||||
|
||||
### For the introduction of different tools, please visit the official page of each tool to see:
|
||||
|
||||
1. [Snap·HuTao](https://github.com/DGP-Studio/Snap.HuTao)
|
||||
|
||||
0. [Cocogoat](https://cocogoat.work/achievement)
|
||||
1. [Snap HuTao](https://github.com/DGP-Studio/Snap.HuTao)
|
||||
2. [Paimon.moe](https://paimon.moe/achievement/)
|
||||
|
||||
3. [Seelie.me](https://seelie.me/achievements)
|
||||
|
||||
4. ~~Form File `.csv`~~
|
||||
5. [XunKong](https://github.com/xunkong/xunkong)
|
||||
6. [YuanmoTools](https://apps.apple.com/app/id1663989619)
|
||||
7. [Teyvat Guide](https://github.com/BTMuli/TeyvatGuide)
|
||||
8. [UIAF](https://uigf.org/standards/UIAF.html) JSON file
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
@@ -21,16 +21,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.188-beta">
|
||||
<PackageReference Include="Google.Protobuf" Version="3.26.1" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Grpc.Tools" Version="2.53.0">
|
||||
<PackageReference Include="Grpc.Tools" Version="2.62.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -105,17 +105,15 @@
|
||||
<AdditionalDependencies>detours-x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<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>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\HookManager.h" />
|
||||
<ClInclude Include="src\il2cpp-api-functions.h" />
|
||||
<ClInclude Include="src\il2cpp-appdata.h" />
|
||||
<ClInclude Include="src\il2cpp-functions.h" />
|
||||
<ClInclude Include="src\il2cpp-types.h" />
|
||||
<ClInclude Include="src\il2cpp-init.h" />
|
||||
<ClInclude Include="src\il2cpp-unity-functions.h" />
|
||||
<ClInclude Include="src\pch.h" />
|
||||
<ClInclude Include="src\util.h" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,107 +1,65 @@
|
||||
#include "pch.h"
|
||||
// ReSharper disable CppCStyleCast
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
// ReSharper disable CppClangTidyModernizeUseStdPrint
|
||||
// ReSharper disable CppClangTidyClangDiagnosticCastAlign
|
||||
// ReSharper disable CppClangTidyHicppMultiwayPathsCovered
|
||||
// ReSharper disable CppDefaultCaseNotHandledInSwitchStatement
|
||||
// ReSharper disable CppClangTidyClangDiagnosticCastFunctionTypeStrict
|
||||
|
||||
#include "pch.h"
|
||||
#include "util.h"
|
||||
#include "il2cpp-init.h"
|
||||
|
||||
using Genshin::ByteArray, Genshin::ClientKcpEvent, Genshin::KcpPacket, Genshin::KcpEventType;
|
||||
using std::to_string;
|
||||
using Genshin::ByteArray;
|
||||
|
||||
HWND unityWnd = nullptr;
|
||||
HANDLE hPipe = nullptr;
|
||||
|
||||
// Allow Protocol: GetPlayerToken, PlayerLogin, AchievementAllDataNotify, Ping, PlayerForceExit
|
||||
std::set<UINT16> PacketWhitelist = { 21228, 2407, 25842, 29665, 27422, 5285, 8231, 1819 };
|
||||
|
||||
bool OnPacket(KcpPacket* pkt) {
|
||||
if (pkt->data == nullptr) return true;
|
||||
auto len = pkt->length;
|
||||
auto data = (ByteArray*)new BYTE[len + 32];
|
||||
data->max_length = len;
|
||||
memcpy(data->vector, pkt->data, len);
|
||||
Genshin::XorEncrypt(&data, len, nullptr);
|
||||
if (ReadMapped<UINT16>(data->vector, 0) != 0x4567) {
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
if (!PacketWhitelist.contains(ReadMapped<UINT16>(data->vector, 2))) {
|
||||
//ifdef _DEBUG
|
||||
printf("Blocked cmdid: %d\n", ReadMapped<UINT16>(data->vector, 2));
|
||||
//endif
|
||||
delete[] data;
|
||||
return false;
|
||||
}
|
||||
printf("Passed cmdid: %d\n", ReadMapped<UINT16>(data->vector, 2));
|
||||
if (ReadMapped<UINT16>(data->vector, 2) == 27422) {
|
||||
auto headLength = ReadMapped<UINT16>(data->vector, 4);
|
||||
auto dataLength = ReadMapped<UINT32>(data->vector, 6);
|
||||
auto iStr = Genshin::ToBase64String(data, 10 + headLength, dataLength, nullptr);
|
||||
auto cStr = ToString(iStr) + "\n";
|
||||
WriteFile(hPipe, cStr.c_str(), cStr.length(), nullptr, nullptr);
|
||||
CloseHandle(hPipe);
|
||||
auto manager = Genshin::GetSingletonInstance(Genshin::GetSingletonManager(), il2cpp_string_new("GameManager"));
|
||||
Genshin::ForceQuit(manager);
|
||||
}
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
|
||||
void* baClass;
|
||||
std::string checksum;
|
||||
|
||||
namespace Hook {
|
||||
|
||||
int KcpSend(void* client, KcpPacket* pkt, void* method) {
|
||||
return OnPacket(pkt) ? CALL_ORIGIN(KcpSend, client, pkt, method) : 0;
|
||||
}
|
||||
|
||||
void SetVersion(void* obj, Il2CppString* value, void* method) {
|
||||
auto version = ToString(value);
|
||||
value = string_new(version + " YaeAchievement");
|
||||
CALL_ORIGIN(SetVersion, obj, value, method);
|
||||
}
|
||||
|
||||
bool KcpRecv(void* client, ClientKcpEvent* evt, void* method) {
|
||||
auto result = CALL_ORIGIN(KcpRecv, client, evt, method);
|
||||
if (result == 0 || evt->fields.type != KcpEventType::EventRecvMsg) {
|
||||
return result;
|
||||
ByteArray* UnityEngine_RecordUserData(const INT type) {
|
||||
if (type == 0) {
|
||||
const auto len = checksum.length();
|
||||
const auto arr = Genshin::il2cpp_array_new_specific(baClass, len);
|
||||
memcpy(&arr->vector[0], checksum.data(), len);
|
||||
return arr;
|
||||
}
|
||||
return OnPacket(evt->fields.packet) ? result : false;
|
||||
return Genshin::il2cpp_array_new_specific(baClass, 0);
|
||||
}
|
||||
|
||||
ByteArray* UnityEngine_RecordUserData(INT type) {
|
||||
return Genshin::GetBytes(Genshin::GetDefaultEncoding(), il2cpp_string_new(""));
|
||||
}
|
||||
|
||||
VOID SetChecksum(LPVOID obj, Il2CppString* value) {
|
||||
CALL_ORIGIN(SetChecksum, obj, il2cpp_string_new(checksum.c_str()));
|
||||
}
|
||||
|
||||
VOID RequestLogin(LPVOID obj, LPVOID token, UINT32 uid) {
|
||||
HookManager::install(Genshin::SetChecksum, SetChecksum);
|
||||
CALL_ORIGIN(RequestLogin, obj, token, uid);
|
||||
HookManager::detach(SetChecksum);
|
||||
|
||||
uint16_t BitConverter_ToUInt16(ByteArray* val, const int startIndex) {
|
||||
const auto ret = CALL_ORIGIN(BitConverter_ToUInt16, val, startIndex);
|
||||
if (ret == 0xAB89 && ReadMapped<UINT16>(val->vector, 2) == 3199) {
|
||||
const auto headLength = ReadMapped<UINT16>(val->vector, 4);
|
||||
const auto dataLength = ReadMapped<UINT32>(val->vector, 6);
|
||||
const auto cStr = base64_encode(val->vector + 10 + headLength, dataLength) + "\n";
|
||||
WriteFile(hPipe, cStr.c_str(), (DWORD) cStr.length(), nullptr, nullptr);
|
||||
CloseHandle(hPipe);
|
||||
ExitProcess(0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void Run(HMODULE* phModule) {
|
||||
//AllocConsole();
|
||||
//freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
|
||||
while (
|
||||
GetModuleHandle("UserAssembly.dll") == nullptr ||
|
||||
(unityWnd = FindMainWindowByPID(GetCurrentProcessId())) == nullptr
|
||||
) {
|
||||
while ((unityWnd = FindMainWindowByPID(GetCurrentProcessId())) == nullptr) {
|
||||
Sleep(1000);
|
||||
}
|
||||
Sleep(5000);
|
||||
DisableVMProtect();
|
||||
InitIL2CPP();
|
||||
auto enc = Genshin::GetDefaultEncoding();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
checksum += ToString(Genshin::GetString(enc, Genshin::RecordUserData(i)));
|
||||
const auto result = Genshin::RecordUserData(i);
|
||||
checksum += string(reinterpret_cast<char*>(&result->vector[0]), result->max_length);
|
||||
baClass = result->klass;
|
||||
}
|
||||
HookManager::install(Genshin::KcpSend, Hook::KcpSend);
|
||||
HookManager::install(Genshin::KcpRecv, Hook::KcpRecv);
|
||||
HookManager::install(Genshin::SetVersion, Hook::SetVersion);
|
||||
HookManager::install(Genshin::RequestLogin, Hook::RequestLogin);
|
||||
HookManager::install(Genshin::UnityEngine_RecordUserData, Hook::UnityEngine_RecordUserData);
|
||||
HookManager::install(Genshin::RecordUserData, Hook::UnityEngine_RecordUserData);
|
||||
HookManager::install(Genshin::BitConverter_ToUInt16, Hook::BitConverter_ToUInt16);
|
||||
hPipe = CreateFile(R"(\\.\pipe\YaeAchievementPipe)", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (hPipe == INVALID_HANDLE_VALUE) {
|
||||
Win32ErrorDialog(1001);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
DO_API(0x9cc850, 0x9ca750, Il2CppString*, il2cpp_string_new, (const char* str));
|
||||
@@ -1,20 +1,11 @@
|
||||
// ReSharper disable CppClangTidyBugproneMacroParentheses
|
||||
|
||||
#pragma once
|
||||
#include "il2cpp-types.h"
|
||||
|
||||
// IL2CPP APIs
|
||||
#define DO_API(ca, oa, r, n, p) extern r (*n) p
|
||||
#include "il2cpp-api-functions.h"
|
||||
#undef DO_API
|
||||
|
||||
// Application-specific functions
|
||||
#define DO_APP_FUNC(ca, oa, r, n, p) extern r (*n) p
|
||||
namespace Genshin {
|
||||
#include "il2cpp-functions.h"
|
||||
}
|
||||
#undef DO_APP_FUNC
|
||||
|
||||
#define DO_UNI_FUNC(ca, oa, r, n, p) extern r (*n) p
|
||||
namespace Genshin {
|
||||
#include "il2cpp-unity-functions.h"
|
||||
}
|
||||
#undef DO_UNI_FUNC
|
||||
|
||||
@@ -2,30 +2,8 @@ using namespace Genshin;
|
||||
|
||||
// DO_APP_FUNC(CN_OFFSET, OS_OFFSET, RETURN, FUNC_NAME, (ARGS...));
|
||||
|
||||
DO_APP_FUNC(0x7974040, 0x792b560, Il2CppString*, ToBase64String, (ByteArray* value, int offset, int length, void* method));
|
||||
DO_APP_FUNC(0x0052A510, 0x0052ED10, ByteArray*, il2cpp_array_new_specific, (void* arrayTypeInfo, uint64_t length));
|
||||
|
||||
DO_APP_FUNC(0x21e5c10, 0x21bf5c0, void, SetVersion, (void* obj, Il2CppString* value, void* method));
|
||||
DO_APP_FUNC(0x01688250, 0x0168CB50, ByteArray*, RecordUserData, (int32_t nType));
|
||||
|
||||
DO_APP_FUNC(0x81d1a00, 0x818d250, ByteArray*, RecordUserData, (int32_t nType));
|
||||
|
||||
DO_APP_FUNC(0x3174320, 0x3133880, void, XorEncrypt, (ByteArray** data, int length, void* method));
|
||||
|
||||
DO_APP_FUNC(0x2d49720, 0x2d103a0, int, KcpSend, (void* client, KcpPacket* pkt, void* method));
|
||||
|
||||
DO_APP_FUNC(0x3d6a920, 0x3d11e00, bool, KcpRecv, (void* client, ClientKcpEvent* evt, void* method));
|
||||
|
||||
DO_APP_FUNC(0x7c9c9c0, 0x7c56400, LPVOID, GetDefaultEncoding, ());
|
||||
|
||||
DO_APP_FUNC(0x7c9c220, 0x7c55c60, Il2CppString*, GetString, (LPVOID encoding, LPVOID bytes));
|
||||
|
||||
DO_APP_FUNC(0x7c9b030, 0x7c54a20, ByteArray*, GetBytes, (LPVOID encoding, LPVOID str));
|
||||
|
||||
DO_APP_FUNC(0x1d2e480, 0x1d0db20, VOID, RequestLogin, (LPVOID obj, LPVOID token, UINT uid));
|
||||
|
||||
DO_APP_FUNC(0x31285b0, 0x32ee9b0, VOID, SetChecksum, (LPVOID obj, Il2CppString* value));
|
||||
|
||||
DO_APP_FUNC(0xfa2eb0, 0xf99150, VOID, ForceQuit, (LPVOID obj));
|
||||
|
||||
DO_APP_FUNC(0x7257b20, 0x72079f0, LPVOID, GetSingletonManager, ());
|
||||
|
||||
DO_APP_FUNC(0x7257850, 0x7207720, LPVOID, GetSingletonInstance, (LPVOID obj, Il2CppString* value));
|
||||
DO_APP_FUNC(0x0FC7D610, 0x0FCA7240, uint16_t, BitConverter_ToUInt16, (ByteArray* val, int startIndex));
|
||||
|
||||
@@ -1,39 +1,26 @@
|
||||
// ReSharper disable CppCStyleCast
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
// ReSharper disable CppClangTidyBugproneMacroParentheses
|
||||
// ReSharper disable CppClangTidyClangDiagnosticCastAlign
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "il2cpp-init.h"
|
||||
|
||||
#define DO_API(ca, oa, r, n, p) r (*n) p
|
||||
#include "il2cpp-api-functions.h"
|
||||
#undef DO_API
|
||||
|
||||
#define DO_APP_FUNC(ca, oa, r, n, p) r (*n) p
|
||||
namespace Genshin {
|
||||
#include "il2cpp-functions.h"
|
||||
}
|
||||
#undef DO_APP_FUNC
|
||||
|
||||
#define DO_UNI_FUNC(ca, oa, r, n, p) r (*n) p
|
||||
namespace Genshin {
|
||||
#include "il2cpp-unity-functions.h"
|
||||
}
|
||||
#undef DO_UNI_FUNC
|
||||
|
||||
using std::string;
|
||||
|
||||
void InitIL2CPP() {
|
||||
TCHAR szFileName[MAX_PATH];
|
||||
GetModuleFileName(NULL, szFileName, MAX_PATH);
|
||||
auto isCN = strstr(szFileName, "YuanShen.exe");//string(szFileName).contains();
|
||||
auto hBase = GetModuleHandle("UserAssembly.dll");
|
||||
auto bAddr = (UINT64)hBase;
|
||||
auto cAddr = (UINT64)GetModuleHandle("UnityPlayer.dll");
|
||||
#define DO_API(ca, oa, r, n, p) n = (r (*) p)(bAddr + (isCN ? ca : oa))
|
||||
#include "il2cpp-api-functions.h"
|
||||
#undef DO_API
|
||||
#define DO_APP_FUNC(ca, oa, r, n, p) n = (r (*) p)(bAddr + (isCN ? ca : oa))
|
||||
GetModuleFileName(nullptr, szFileName, MAX_PATH);
|
||||
const auto isCN = strstr(szFileName, "YuanShen.exe");
|
||||
const auto uBase = reinterpret_cast<uint64_t>(GetModuleHandle(isCN ? "YuanShen.exe" : "GenshinImpact.exe"));
|
||||
#define DO_APP_FUNC(ca, oa, r, n, p) n = (r (*) p)(uBase + (isCN ? ca : oa))
|
||||
#include "il2cpp-functions.h"
|
||||
#undef DO_APP_FUNC
|
||||
#define DO_UNI_FUNC(ca, oa, r, n, p) n = (r (*) p)(cAddr + (isCN ? ca : oa))
|
||||
#include "il2cpp-unity-functions.h"
|
||||
#undef DO_UNI_FUNC
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
using namespace Genshin;
|
||||
|
||||
DO_UNI_FUNC(0x102fa0, 0x102fa0, ByteArray*, UnityEngine_RecordUserData, (int32_t nType));
|
||||
@@ -14,12 +14,8 @@
|
||||
// 添加要在此处预编译的标头
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <codecvt>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <detours.h>
|
||||
#include "HookManager.h"
|
||||
#include "il2cpp-appdata.h"
|
||||
|
||||
@@ -12,24 +12,12 @@ VOID DisableVMProtect() {
|
||||
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
|
||||
|
||||
bool IsLittleEndian() {
|
||||
UINT i = 1;
|
||||
char* c = (char*)&i;
|
||||
return (*c);
|
||||
return *c;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
@@ -68,3 +56,38 @@ HWND FindMainWindowByPID(DWORD pid) {
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
std::string base64_encode(BYTE const* buf, unsigned int bufLen) {
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
BYTE char_array_3[3];
|
||||
BYTE char_array_4[4];
|
||||
while (bufLen--) {
|
||||
char_array_3[i++] = *buf++;
|
||||
if (i == 3) {
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
for (i = 0; (i < 4); i++)
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
if (i) {
|
||||
int j;
|
||||
for (j = i; j < 3; j++)
|
||||
char_array_3[j] = '\0';
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
for (j = 0; j < i + 1; j++)
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
while (i++ < 3)
|
||||
ret += '=';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,9 @@ using std::string;
|
||||
VOID DisableVMProtect();
|
||||
bool IsLittleEndian();
|
||||
HWND FindMainWindowByPID(DWORD pid);
|
||||
string ToString(Il2CppString* str, UINT codePage = CP_ACP);
|
||||
std::string base64_encode(BYTE const* buf, unsigned int bufLen);
|
||||
|
||||
#define cstring_new(str) il2cpp_string_new(str)
|
||||
#define string_new(str) cstring_new((str).c_str())
|
||||
|
||||
#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 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())
|
||||
|
||||
|
||||
22
res/App.Designer.cs
generated
22
res/App.Designer.cs
generated
@@ -121,7 +121,9 @@ namespace YaeAchievement.res {
|
||||
///[3] Seelie.me
|
||||
///[4] Csv file
|
||||
///[5] Xunkong
|
||||
///Input a number (0-5): .
|
||||
///[7] Teyvat Guide
|
||||
///[8] UIAF JSON File
|
||||
///Input a number (0-8): .
|
||||
/// </summary>
|
||||
internal static string ExportChoose {
|
||||
get {
|
||||
@@ -174,6 +176,24 @@ namespace YaeAchievement.res {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please launch/update Teyvat Guide and retry..
|
||||
/// </summary>
|
||||
internal static string ExportToTauriFail {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToTauriFail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Successfully exported to Teyvat Guide..
|
||||
/// </summary>
|
||||
internal static string ExportToTauriSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToTauriSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0}.
|
||||
/// </summary>
|
||||
|
||||
10
res/App.resx
10
res/App.resx
@@ -32,7 +32,9 @@
|
||||
[3] Seelie.me
|
||||
[4] Csv file
|
||||
[5] Xunkong
|
||||
Input a number (0-5): </value>
|
||||
[7] Teyvat Guide
|
||||
[8] UIAF JSON File
|
||||
Input a number (0-8): </value>
|
||||
</data>
|
||||
<data name="ExportToCocogoatSuccess" xml:space="preserve">
|
||||
<value>Successfully exported to cocogoat.</value>
|
||||
@@ -129,4 +131,10 @@ Input a number (0-5): </value>
|
||||
<data name="NoWritePermission" xml:space="preserve">
|
||||
<value>No write permission on {0}.</value>
|
||||
</data>
|
||||
<data name="ExportToTauriSuccess" xml:space="preserve">
|
||||
<value>Successfully exported to Teyvat Guide.</value>
|
||||
</data>
|
||||
<data name="ExportToTauriFail" xml:space="preserve">
|
||||
<value>Please launch/update Teyvat Guide and retry.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -26,8 +26,9 @@
|
||||
[4] 表格文件
|
||||
[5] 寻空
|
||||
[6] 原魔工具箱
|
||||
[7] UIAF JSON 文件
|
||||
输入一个数字 (0-7): </value>
|
||||
[7] Teyvat Guide
|
||||
[8] UIAF JSON 文件
|
||||
输入一个数字 (0-8): </value>
|
||||
</data>
|
||||
<data name="ExportToCocogoatSuccess" xml:space="preserve">
|
||||
<value>在浏览器内进行下一步操作</value>
|
||||
@@ -115,9 +116,15 @@
|
||||
<value>网络错误,请检查网络后重试 ({0}: {1})</value>
|
||||
</data>
|
||||
<data name="GenshinHashError" xml:space="preserve">
|
||||
<value>请将原神更新至最新版本后重试</value>
|
||||
<value>当前适配版本不匹配,请更新原神至最新版本后重试或等待工具更新。</value>
|
||||
</data>
|
||||
<data name="NoWritePermission" xml:space="preserve">
|
||||
<value>无法写入文件,请更换软件所在目录后重试</value>
|
||||
</data>
|
||||
<data name="ExportToTauriFail" xml:space="preserve">
|
||||
<value>启动 Teyvat Guide 或更新 Teyvat Guide 至最新版本后重试</value>
|
||||
</data>
|
||||
<data name="ExportToTauriSuccess" xml:space="preserve">
|
||||
<value>在 Teyvat Guide 进行下一步操作</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -9,13 +9,13 @@ message Achievement {
|
||||
FINISHED = 2;
|
||||
REWARD_TAKEN = 3;
|
||||
}
|
||||
uint32 timestamp = 15;
|
||||
uint32 current = 14;
|
||||
uint32 total = 4;
|
||||
uint32 id = 5;
|
||||
Status status = 10;
|
||||
uint32 timestamp = 7;
|
||||
uint32 current = 12;
|
||||
uint32 total = 5;
|
||||
uint32 id = 15;
|
||||
Status status = 8;
|
||||
}
|
||||
|
||||
message AchievementAllDataNotify {
|
||||
repeated Achievement list = 9;
|
||||
repeated Achievement list = 11;
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Net;
|
||||
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;
|
||||
};
|
||||
LogSerializer.AddLogType(PageLog.JsonIdentifier, typeof(PageLog));
|
||||
LogSerializer.AddLogType(EventLog.JsonIdentifier, typeof(EventLog));
|
||||
LogSerializer.AddLogType(HandledErrorLog.JsonIdentifier, typeof(HandledErrorLog));
|
||||
LogSerializer.AddLogType(ManagedErrorLog.JsonIdentifier, typeof(ManagedErrorLog));
|
||||
LogSerializer.AddLogType(StartServiceLog.JsonIdentifier, typeof(StartServiceLog));
|
||||
LogSerializer.AddLogType(StartSessionLog.JsonIdentifier, typeof(StartSessionLog));
|
||||
}
|
||||
|
||||
// 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.ReadAsStringAsync().Result;
|
||||
uploadStatus = JsonConvert.DeserializeObject<LogUploadResult>(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));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
public class Device {
|
||||
|
||||
[JsonProperty(PropertyName = "sdkName")]
|
||||
public string SdkName { get; set; } = "appcenter.wpf.netcore";
|
||||
|
||||
[JsonProperty(PropertyName = "sdkVersion")]
|
||||
public string SdkVersion { get; set; } = "4.5.0";
|
||||
|
||||
[JsonProperty(PropertyName = "osName")]
|
||||
public string OsName { get; set; } = "WINDOWS";
|
||||
|
||||
[JsonProperty(PropertyName = "osVersion")]
|
||||
public string OsVersion { get; set; } = DeviceHelper.GetSystemVersion();
|
||||
|
||||
[JsonProperty(PropertyName = "osBuild")]
|
||||
public string OsBuild { get; set; } = $"{DeviceHelper.GetSystemVersion()}.{DeviceHelper.GetSystemBuild()}";
|
||||
|
||||
[JsonProperty(PropertyName = "model")]
|
||||
public string? Model { get; set; } = DeviceHelper.GetModel();
|
||||
|
||||
[JsonProperty(PropertyName = "oemName")]
|
||||
public string? OemName { get; set; } = DeviceHelper.GetOem();
|
||||
|
||||
[JsonProperty(PropertyName = "screenSize")]
|
||||
public string ScreenSize { get; set; } = DeviceHelper.GetScreenSize();
|
||||
|
||||
[JsonProperty(PropertyName = "carrierCountry")]
|
||||
public string Country { get; set; } = DeviceHelper.GetCountry() ?? "CN";
|
||||
|
||||
[JsonProperty(PropertyName = "locale")]
|
||||
public string Locale { get; set; } = CultureInfo.CurrentCulture.Name;
|
||||
|
||||
[JsonProperty(PropertyName = "timeZoneOffset")]
|
||||
public int TimeZoneOffset { get; set; } = (int) TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes;
|
||||
|
||||
[JsonProperty(PropertyName = "appVersion")]
|
||||
public string AppVersion { get; set; } = GlobalVars.AppVersionName;
|
||||
|
||||
[JsonProperty(PropertyName = "appBuild")]
|
||||
public string AppBuild { get; set; } = GlobalVars.AppVersionCode.ToString();
|
||||
|
||||
[JsonProperty(PropertyName = "appNamespace")]
|
||||
public string AppNamespace { get; set; } = Assembly.GetEntryAssembly()?.EntryPoint?.DeclaringType?.Namespace ?? string.Empty;
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
[JsonObject(JsonIdentifier)]
|
||||
public class EventLog : LogWithProperties {
|
||||
|
||||
public const string JsonIdentifier = "event";
|
||||
|
||||
public EventLog(string name) {
|
||||
Name = name;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
private Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
[JsonProperty(PropertyName = "name")]
|
||||
private string Name { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
[JsonObject(JsonIdentifier)]
|
||||
public class HandledErrorLog : LogWithProperties {
|
||||
|
||||
public const string JsonIdentifier = "handledError";
|
||||
|
||||
public HandledErrorLog(MException exception) {
|
||||
Id = Guid.NewGuid();
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "exception")]
|
||||
public MException Exception { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
public abstract class Log {
|
||||
|
||||
[JsonProperty(PropertyName = "sid")]
|
||||
private Guid? Session { get; set; } = AppCenter.SessionID;
|
||||
|
||||
[JsonProperty(PropertyName = "timestamp")]
|
||||
private DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[JsonProperty(PropertyName = "device")]
|
||||
private Device Device { get; set; } = AppCenter.DeviceInfo;
|
||||
|
||||
[JsonIgnore]
|
||||
internal LogStatus Status = LogStatus.Pending;
|
||||
|
||||
}
|
||||
|
||||
public enum LogStatus {
|
||||
Pending, Uploading, Uploaded
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
public class LogContainer {
|
||||
|
||||
public LogContainer(IEnumerable<Log> logs) {
|
||||
Logs = logs;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "logs")]
|
||||
public IEnumerable<Log> Logs { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
public class LogUploadResult {
|
||||
|
||||
[JsonProperty(PropertyName = "status")]
|
||||
public string Status { get; set; } = null!;
|
||||
|
||||
[JsonProperty(PropertyName = "validDiagnosticsIds")]
|
||||
public Guid[] ValidDiagnosticsIds { get; set; } = Array.Empty<Guid>();
|
||||
|
||||
[JsonProperty(PropertyName = "throttledDiagnosticsIds")]
|
||||
public Guid[] ThrottledDiagnosticsIds { get; set; } = Array.Empty<Guid>();
|
||||
|
||||
[JsonProperty(PropertyName = "correlationId")]
|
||||
public Guid CorrelationId { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
public class LogWithProperties : Log {
|
||||
|
||||
[JsonProperty(PropertyName = "properties")]
|
||||
public IDictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
public class MException {
|
||||
|
||||
[JsonProperty(PropertyName = "type")]
|
||||
public string Type { get; set; } = "UnknownType";
|
||||
|
||||
[JsonProperty(PropertyName = "message")]
|
||||
public string? Message { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "stackTrace")]
|
||||
public string? StackTrace { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "frames")]
|
||||
public IList<StackFrame>? Frames { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "innerExceptions")]
|
||||
public IList<MException>? InnerExceptions { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
[JsonObject(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();
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "userId")]
|
||||
public string? UserId { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "processId")]
|
||||
public int ProcessId { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "processName")]
|
||||
public string ProcessName { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "fatal")]
|
||||
public bool Fatal { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "appLaunchTimestamp")]
|
||||
public DateTime? AppLaunchTimestamp { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "architecture")]
|
||||
public string? Architecture { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "exception")]
|
||||
public MException Exception { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
[JsonObject(JsonIdentifier)]
|
||||
public class PageLog : LogWithProperties {
|
||||
|
||||
public const string JsonIdentifier = "page";
|
||||
|
||||
public PageLog(string name) {
|
||||
Name = name;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
|
||||
|
||||
#pragma warning disable CS8604, CS8765
|
||||
public class LogJsonConverter : JsonConverter {
|
||||
|
||||
private readonly Dictionary<string, Type> _logTypes = new ();
|
||||
private readonly object _jsonConverterLock = new ();
|
||||
private static readonly JsonSerializerSettings SerializationSettings;
|
||||
|
||||
static LogJsonConverter() {
|
||||
SerializationSettings = new JsonSerializerSettings {
|
||||
Formatting = Formatting.None,
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
DateFormatHandling = DateFormatHandling.IsoDateFormat,
|
||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
|
||||
};
|
||||
}
|
||||
|
||||
public void AddLogType(string typeName, Type type) {
|
||||
lock (_jsonConverterLock) {
|
||||
_logTypes[typeName] = type;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) {
|
||||
return typeof(Log).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object? ReadJson(JsonReader reader, Type t, object o, JsonSerializer s) {
|
||||
Type logType;
|
||||
var jsonObject = JObject.Load(reader);
|
||||
var typeName = jsonObject.GetValue("type")?.ToString();
|
||||
lock (_jsonConverterLock) {
|
||||
if (typeName == null || !_logTypes.ContainsKey(typeName)) {
|
||||
throw new JsonReaderException("Could not identify type of log");
|
||||
}
|
||||
logType = _logTypes[typeName];
|
||||
jsonObject.Remove("type");
|
||||
}
|
||||
return jsonObject.ToObject(logType);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
|
||||
var info = value.GetType().GetTypeInfo();
|
||||
if (info.GetCustomAttribute(typeof(JsonObjectAttribute)) is not JsonObjectAttribute attribute) {
|
||||
throw new JsonWriterException("Log type is missing JsonObjectAttribute");
|
||||
}
|
||||
var jsonObject = JObject.FromObject(value, JsonSerializer.CreateDefault(SerializationSettings));
|
||||
jsonObject.Add("type", JToken.FromObject(attribute.Id));
|
||||
writer.WriteRawValue(jsonObject.ToString(Formatting.None));
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
|
||||
|
||||
#pragma warning disable CS8604, CS8765, CS8603
|
||||
public static class LogSerializer {
|
||||
|
||||
private static readonly JsonSerializerSettings SerializationSettings;
|
||||
private static readonly LogJsonConverter Converter = new ();
|
||||
|
||||
static LogSerializer() {
|
||||
SerializationSettings = new JsonSerializerSettings {
|
||||
Formatting = Formatting.None,
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
DateFormatHandling = DateFormatHandling.IsoDateFormat,
|
||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
|
||||
Converters = { Converter }
|
||||
};
|
||||
}
|
||||
|
||||
public static void AddLogType(string typeName, Type type) {
|
||||
Converter.AddLogType(typeName, type);
|
||||
}
|
||||
|
||||
public static string Serialize(LogContainer logContainer) {
|
||||
return JsonConvert.SerializeObject(logContainer, SerializationSettings);
|
||||
}
|
||||
|
||||
public static string Serialize(Log log) {
|
||||
return JsonConvert.SerializeObject(log, SerializationSettings);
|
||||
}
|
||||
|
||||
public static LogContainer? DeserializeLogs(string json) {
|
||||
return JsonConvert.DeserializeObject<LogContainer>(json, SerializationSettings);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "address")]
|
||||
public string Address { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "code")]
|
||||
public string Code { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "className")]
|
||||
public string ClassName { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "methodName")]
|
||||
public string MethodName { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "lineNumber")]
|
||||
public int? LineNumber { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "fileName")]
|
||||
public string FileName { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
[JsonObject(JsonIdentifier)]
|
||||
public class StartServiceLog : Log {
|
||||
|
||||
public const string JsonIdentifier = "startService";
|
||||
|
||||
public StartServiceLog(params string[] services) {
|
||||
Services = services;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "services")]
|
||||
public string[] Services { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK.Models;
|
||||
|
||||
[JsonObject(JsonIdentifier)]
|
||||
public class StartSessionLog : Log {
|
||||
public const string JsonIdentifier = "startSession";
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using YaeAchievement.res;
|
||||
|
||||
namespace YaeAchievement;
|
||||
|
||||
public static class AppConfig {
|
||||
public static partial class AppConfig {
|
||||
|
||||
public static string GamePath { get; private set; } = null!;
|
||||
|
||||
@@ -52,11 +52,15 @@ public static class AppConfig {
|
||||
try {
|
||||
File.Delete(copiedLogFilePath);
|
||||
} catch (Exception) { /* ignore */}
|
||||
var matchResult = Regex.Match(content, @"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)");
|
||||
var matchResult = GamePathRegex().Match(content);
|
||||
if (!matchResult.Success) {
|
||||
return null;
|
||||
}
|
||||
var entryName = matchResult.Groups["1"].Value.Replace("_Data", ".exe");
|
||||
return Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex GamePathRegex();
|
||||
|
||||
}
|
||||
|
||||
@@ -4,17 +4,13 @@ using Proto;
|
||||
|
||||
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;
|
||||
|
||||
public DateTime LastWriteTime => Exists() ? File.GetLastWriteTimeUtc(_cacheName) : DateTime.UnixEpoch;
|
||||
|
||||
public CacheFile(string identifier) {
|
||||
_cacheName = Path.Combine(GlobalVars.CachePath, $"{identifier.MD5Hash()[..16]}.miko");
|
||||
}
|
||||
|
||||
public bool Exists() => File.Exists(_cacheName);
|
||||
|
||||
public CacheItem Read() {
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Win32;
|
||||
using Proto;
|
||||
using YaeAchievement.AppCenterSDK;
|
||||
using YaeAchievement.res;
|
||||
using static Proto.Achievement.Types;
|
||||
|
||||
@@ -16,6 +15,10 @@ public static class Export {
|
||||
|
||||
public static uint ExportTo { get; set; } = uint.MaxValue;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOpts = new () {
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
public static void Choose(AchievementAllDataNotify data) {
|
||||
if (ExportTo == uint.MaxValue) {
|
||||
Console.Write(App.ExportChoose);
|
||||
@@ -32,23 +35,23 @@ public static class Export {
|
||||
4 => ToCSV,
|
||||
5 => ToXunkong,
|
||||
6 => ToWxApp1,
|
||||
7 => ToUIAFJson,
|
||||
8 => ToRawJson,
|
||||
7 => ToTeyvatGuide,
|
||||
8 => ToUIAFJson,
|
||||
9 => ToRawJson,
|
||||
_ => ToCocogoat
|
||||
})).Invoke(data);
|
||||
}
|
||||
|
||||
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) {
|
||||
var result = JsonSerializer.Serialize(ExportToUIAFApp(data));
|
||||
using var request = new HttpRequestMessage {
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}"),
|
||||
Content = new StringContent(result, Encoding.UTF8, "application/json")
|
||||
};
|
||||
using var request = new HttpRequestMessage();
|
||||
request.Method = HttpMethod.Post;
|
||||
request.RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}");
|
||||
request.Content = new StringContent(result, Encoding.UTF8, "application/json");
|
||||
using var response = Utils.CHttpClient.Send(request);
|
||||
if (response.StatusCode != HttpStatusCode.Created) {
|
||||
Console.WriteLine(App.ExportToCocogoatFail);
|
||||
@@ -67,29 +70,47 @@ public static class Export {
|
||||
{ "key", id },
|
||||
{ "data", ExportToUIAFApp(data) }
|
||||
});
|
||||
using var request = new HttpRequestMessage {
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri("https://api.qyinter.com/achievementRedis"),
|
||||
Content = new StringContent(result, Encoding.UTF8, "application/json")
|
||||
};
|
||||
using var request = new HttpRequestMessage();
|
||||
request.Method = HttpMethod.Post;
|
||||
request.RequestUri = new Uri("https://api.qyinter.com/achievementRedis");
|
||||
request.Content = new StringContent(result, Encoding.UTF8, "application/json");
|
||||
using var response = Utils.CHttpClient.Send(request);
|
||||
Console.WriteLine(App.ExportToWxApp1Success, id);
|
||||
}
|
||||
|
||||
private static void ToHuTao(AchievementAllDataNotify data) {
|
||||
if (CheckWinUIAppScheme("hutao"))
|
||||
{
|
||||
if (CheckWinUIAppScheme("hutao")) {
|
||||
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
|
||||
Utils.ShellOpen("hutao://achievement/import");
|
||||
Console.WriteLine(App.ExportToSnapGenshinSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Console.WriteLine(App.ExportToSnapGenshinNeedUpdate);
|
||||
Utils.ShellOpen("ms-windows-store://pdp/?productid=9PH4NXJ2JN52");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToXunkong(AchievementAllDataNotify data) {
|
||||
if (CheckWinUIAppScheme("xunkong")) {
|
||||
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
|
||||
Utils.ShellOpen("xunkong://import-achievement?caller=YaeAchievement&from=clipboard");
|
||||
Console.WriteLine(App.ExportToXunkongSuccess);
|
||||
} else {
|
||||
Console.WriteLine(App.ExportToXunkongNeedUpdate);
|
||||
Utils.ShellOpen("ms-windows-store://pdp/?productid=9N2SVG0JMT12");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToTeyvatGuide(AchievementAllDataNotify data) {
|
||||
if (Process.GetProcessesByName("TeyvatGuide").Length != 0) {
|
||||
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
|
||||
Utils.ShellOpen("teyvatguide://import_uigf?app=YaeAchievement");
|
||||
Console.WriteLine(App.ExportToTauriSuccess);
|
||||
} else {
|
||||
Console.WriteLine(App.ExportToTauriFail);
|
||||
Utils.ShellOpen("ms-windows-store://pdp/?productid=9NLBNNNBNSJN");
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static void ToUIAFJson(AchievementAllDataNotify data) {
|
||||
var path = Path.GetFullPath($"uiaf-{DateTime.Now:yyyyMMddHHmmss}.json");
|
||||
@@ -151,10 +172,10 @@ public static class Export {
|
||||
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;
|
||||
outList.Add(new List<object> {
|
||||
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
|
||||
outList.Add([
|
||||
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
|
||||
achInfo.Description, current, ach.Total, finishAt
|
||||
});
|
||||
]);
|
||||
}
|
||||
var output = new List<string> { "ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间" };
|
||||
output.AddRange(outList.OrderBy(v => v[2]).Select(item => {
|
||||
@@ -168,22 +189,9 @@ public static class Export {
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToXunkong(AchievementAllDataNotify data) {
|
||||
if (CheckWinUIAppScheme("xunkong")) {
|
||||
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
|
||||
Utils.ShellOpen("xunkong://import-achievement?caller=YaeAchievement&from=clipboard");
|
||||
Console.WriteLine(App.ExportToXunkongSuccess);
|
||||
} else {
|
||||
Console.WriteLine(App.ExportToXunkongNeedUpdate);
|
||||
Utils.ShellOpen("ms-windows-store://pdp/?productid=9N2SVG0JMT12");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToRawJson(AchievementAllDataNotify data) {
|
||||
var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-raw.json");
|
||||
var text = JsonSerializer.Serialize(data, new JsonSerializerOptions {
|
||||
WriteIndented = true
|
||||
});
|
||||
var text = JsonSerializer.Serialize(data, JsonOpts);
|
||||
if (TryWriteToFile(path, text)) {
|
||||
Console.WriteLine(App.ExportToFileSuccess, path);
|
||||
}
|
||||
@@ -211,6 +219,7 @@ public static class Export {
|
||||
};
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static bool CheckWinUIAppScheme(string protocol) {
|
||||
return (string?)Registry.ClassesRoot.OpenSubKey(protocol)?.GetValue("") == $"URL:{protocol}";
|
||||
}
|
||||
@@ -219,7 +228,7 @@ public static class Export {
|
||||
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) {
|
||||
return status switch {
|
||||
@@ -239,7 +248,6 @@ public static class Export {
|
||||
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
|
||||
// ReSharper disable once LocalizableElement
|
||||
Console.WriteLine($"{msg}: {ex.Message}");
|
||||
AppCenter.TrackCrash(ex, false);
|
||||
return ex.NativeErrorCode;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ public static class GlobalVars {
|
||||
public static readonly string CachePath = Path.Combine(DataPath, "cache");
|
||||
public static readonly string LibFilePath = Path.Combine(DataPath, "YaeAchievement.dll");
|
||||
|
||||
public const uint AppVersionCode = 40;
|
||||
public const string AppVersionName = "3.0";
|
||||
public const uint AppVersionCode = 48;
|
||||
public const string AppVersionName = "3.8";
|
||||
|
||||
public const string PipeName = "YaeAchievementPipe";
|
||||
public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com";
|
||||
|
||||
@@ -45,7 +45,7 @@ public static class Injector {
|
||||
return new Win32Exception().PrintMsgAndReturnErrCode("WriteProcessMemory fail");
|
||||
}
|
||||
}
|
||||
var lpStartAddress = pLoadLibrary.CreateDelegate<LPTHREAD_START_ROUTINE>();
|
||||
var lpStartAddress = (delegate* unmanaged[Stdcall]<void*, uint>)pLoadLibrary.Value; //THREAD_START_ROUTINE
|
||||
var hThread = Native.CreateRemoteThread(hProc, default, 0, lpStartAddress, pBase, 0);
|
||||
if (hThread.IsNull) {
|
||||
var e = new Win32Exception();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://aka.ms/CsWin32.schema.json",
|
||||
"className": "Native",
|
||||
"allowMarshaling": true,
|
||||
"allowMarshaling": false,
|
||||
"public": true
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
CreateProcess
|
||||
CloseClipboard
|
||||
CreateProcess
|
||||
CreateRemoteThread
|
||||
EmptyClipboard
|
||||
GetConsoleMode
|
||||
GetDC
|
||||
GetDeviceCaps
|
||||
GetModuleHandle
|
||||
GetProcAddress
|
||||
VirtualAllocEx
|
||||
WriteProcessMemory
|
||||
CreateRemoteThread
|
||||
VirtualFreeEx
|
||||
WaitForSingleObject
|
||||
OpenClipboard
|
||||
EmptyClipboard
|
||||
GlobalLock
|
||||
SetClipboardData
|
||||
GlobalUnlock
|
||||
CloseClipboard
|
||||
GetStdHandle
|
||||
GetConsoleMode
|
||||
GlobalLock
|
||||
GlobalUnlock
|
||||
OpenClipboard
|
||||
ResumeThread
|
||||
SetClipboardData
|
||||
SetConsoleMode
|
||||
TerminateProcess
|
||||
ResumeThread
|
||||
GetDeviceCaps
|
||||
GetDC
|
||||
VirtualAllocEx
|
||||
VirtualFreeEx
|
||||
WaitForSingleObject
|
||||
WriteProcessMemory
|
||||
@@ -1,10 +1,12 @@
|
||||
using Proto;
|
||||
using System.Text;
|
||||
using Proto;
|
||||
using YaeAchievement;
|
||||
using YaeAchievement.AppCenterSDK;
|
||||
using YaeAchievement.AppCenterSDK.Models;
|
||||
using YaeAchievement.res;
|
||||
using static YaeAchievement.Utils;
|
||||
|
||||
Console.InputEncoding = Encoding.UTF8;
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
|
||||
TryDisableQuickEdit();
|
||||
InstallExitHook();
|
||||
InstallExceptionHook();
|
||||
@@ -22,36 +24,27 @@ AppConfig.Load(args.GetOrNull(0) ?? "auto");
|
||||
Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue;
|
||||
|
||||
CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2)));
|
||||
AppCenter.Init();
|
||||
new EventLog("AppInit") {
|
||||
Properties = {
|
||||
{ "AppVersion", GlobalVars.AppVersionName },
|
||||
{ "SystemVersion", DeviceHelper.GetSystemVersion() }
|
||||
}
|
||||
}.Enqueue();
|
||||
var usePreviousData = false;
|
||||
|
||||
var historyCache = new CacheFile("ExportData");
|
||||
if (historyCache.LastWriteTime.AddMinutes(10) > DateTime.UtcNow) {
|
||||
|
||||
AchievementAllDataNotify? data = null;
|
||||
try {
|
||||
data = AchievementAllDataNotify.Parser.ParseFrom(historyCache.Read().Content);
|
||||
} catch (Exception) { /* ignored */ }
|
||||
|
||||
if (historyCache.LastWriteTime.AddMinutes(60) > DateTime.UtcNow && data != null) {
|
||||
Console.WriteLine(App.UsePreviousData);
|
||||
usePreviousData = Console.ReadLine() == "yes";
|
||||
}
|
||||
Export:
|
||||
if(usePreviousData) {
|
||||
AchievementAllDataNotify data;
|
||||
try {
|
||||
data = AchievementAllDataNotify.Parser.ParseFrom(historyCache.Read().Content);
|
||||
} catch (Exception) {
|
||||
usePreviousData = false;
|
||||
goto Export;
|
||||
if (Console.ReadLine()?.ToUpper() is "Y" or "YES") {
|
||||
Export.Choose(data);
|
||||
return;
|
||||
}
|
||||
Export.Choose(data);
|
||||
} else {
|
||||
StartAndWaitResult(AppConfig.GamePath, str => {
|
||||
GlobalVars.UnexpectedExit = false;
|
||||
var data = Convert.FromBase64String(str);
|
||||
var list = AchievementAllDataNotify.Parser.ParseFrom(data);
|
||||
historyCache.Write(data);
|
||||
Export.Choose(list);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
StartAndWaitResult(AppConfig.GamePath, str => {
|
||||
GlobalVars.UnexpectedExit = false;
|
||||
var bytes = Convert.FromBase64String(str);
|
||||
var list = AchievementAllDataNotify.Parser.ParseFrom(bytes);
|
||||
historyCache.Write(bytes);
|
||||
Export.Choose(list);
|
||||
return true;
|
||||
});
|
||||
|
||||
26
src/Utils.cs
26
src/Utils.cs
@@ -11,7 +11,6 @@ using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.System.Console;
|
||||
using Proto;
|
||||
using YaeAchievement.AppCenterSDK;
|
||||
using YaeAchievement.res;
|
||||
|
||||
namespace YaeAchievement;
|
||||
@@ -31,10 +30,9 @@ public static class Utils {
|
||||
|
||||
public static byte[] GetBucketFileAsByteArray(string path, bool cache = true) {
|
||||
try {
|
||||
using var msg = new HttpRequestMessage {
|
||||
Method = HttpMethod.Get,
|
||||
RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}")
|
||||
};
|
||||
using var msg = new HttpRequestMessage();
|
||||
msg.Method = HttpMethod.Get;
|
||||
msg.RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}");
|
||||
var cacheFile = new CacheFile(path);
|
||||
if (cache && cacheFile.Exists()) {
|
||||
msg.Headers.TryAddWithoutValidation("If-None-Match", $"{cacheFile.Read().Etag}");
|
||||
@@ -73,11 +71,11 @@ public static class Utils {
|
||||
if (Native.OpenClipboard(HWND.Null))
|
||||
{
|
||||
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);
|
||||
Marshal.Copy(text.ToCharArray(), 0, hPtr, text.Length);
|
||||
Native.GlobalUnlock(hPtr);
|
||||
Native.SetClipboardData(13, hGlobal);
|
||||
Native.GlobalUnlock((HGLOBAL) hPtr);
|
||||
Native.SetClipboardData(13, new HANDLE(hPtr));
|
||||
Marshal.FreeHGlobal(hGlobal);
|
||||
Native.CloseClipboard();
|
||||
}
|
||||
@@ -201,9 +199,6 @@ public static class Utils {
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine(ex.ToString());
|
||||
Console.WriteLine(App.UploadError);
|
||||
AppCenter.TrackCrash((Exception) e.ExceptionObject);
|
||||
AppCenter.Upload();
|
||||
break;
|
||||
}
|
||||
Environment.Exit(-1);
|
||||
@@ -312,12 +307,9 @@ public static class Utils {
|
||||
}
|
||||
await File.WriteAllBytesAsync(pkgPath, bytes);
|
||||
Console.WriteLine(App.VcRuntimeInstalling);
|
||||
using var process = new Process {
|
||||
StartInfo = {
|
||||
FileName = pkgPath,
|
||||
Arguments = "/install /passive /norestart"
|
||||
}
|
||||
};
|
||||
using var process = new Process();
|
||||
process.StartInfo.FileName = pkgPath;
|
||||
process.StartInfo.Arguments = "/install /passive /norestart";
|
||||
process.Start();
|
||||
await process.WaitForExitAsync();
|
||||
File.Delete(pkgPath);
|
||||
|
||||
Reference in New Issue
Block a user