mirror of
https://github.com/HolographicHat/Yae.git
synced 2025-12-10 16:38:13 +08:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31b77a9fb3 | ||
|
|
d2d5bafcd6 | ||
|
|
e2f1f1e343 | ||
|
|
349e15fe25 | ||
|
|
9e0d18910b | ||
|
|
afee99fd3f | ||
|
|
4dae8c52b6 | ||
|
|
3294ac4b89 | ||
|
|
ccb19c832c | ||
|
|
1207dd70b3 | ||
|
|
204f211249 | ||
|
|
043a861030 | ||
|
|
09a9d4c22b | ||
|
|
9094b9c718 | ||
|
|
d7ac18587a | ||
|
|
82c5054002 | ||
|
|
553d67bff7 | ||
|
|
07a08f56d4 | ||
|
|
656589bc80 | ||
|
|
9aaefe4cd7 | ||
|
|
d84571ba1c | ||
|
|
9bc8d2473a | ||
|
|
7b3e22c84f | ||
|
|
faa583587a | ||
|
|
37bfb93fa9 | ||
|
|
b88858d2dc | ||
|
|
c2e3a3b13d | ||
|
|
78a29e9390 | ||
|
|
a76f03b035 | ||
|
|
d814ece2de | ||
|
|
9414ffbe12 | ||
|
|
251246fd74 | ||
|
|
76785c5179 | ||
|
|
e086e14e8d | ||
|
|
29aa4fea2d | ||
|
|
ffd75da2bf | ||
|
|
92e8cd8997 | ||
|
|
4b62901dbe | ||
|
|
2216610413 | ||
|
|
99b8db2a69 | ||
|
|
5b6e86459d | ||
|
|
bf46494b6b | ||
|
|
458a56a855 | ||
|
|
ef21274cd2 | ||
|
|
7528f4247b | ||
|
|
652b5afa80 | ||
|
|
59a042019a |
30
.github/workflows/dotnet.yml
vendored
Normal file
30
.github/workflows/dotnet.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: .NET Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
- name: Publish
|
||||
run: dotnet publish -o .\publish\
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
with:
|
||||
name: Artifacts
|
||||
path: publish
|
||||
@@ -1,20 +1,23 @@
|
||||
<div align="center">
|
||||
<div align="center"><img width="100" src="https://github.com/HolographicHat/YaeAchievement/blob/master/icon.ico">
|
||||
|
||||
# YaeAchievement
|
||||
|
||||
    
|
||||
|
||||
简体中文 | [English](README_EN.md)
|
||||
</div>
|
||||
|
||||
- 支持导出所有类别的成就
|
||||
- 支持官服,渠道服与国际服
|
||||
- 支持导出至[椰羊](https://cocogoat.work/achievement)、[SnapGenshin](https://github.com/DGP-Studio/Snap.Genshin)、[Paimon.moe](https://paimon.moe/achievement/)、[Seelie.me](https://seelie.me/achievements)、[寻空](https://github.com/xunkong/xunkong)和表格文件(csv)
|
||||
- 支持导出至[椰羊](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)
|
||||
- 没有窗口大小、游戏语言等要求
|
||||
|
||||
## 使用说明
|
||||
第一次打开需要先设置原神主程序(YuanShen.exe/GenshinImpact.exe)所在路径
|
||||

|
||||
设置完毕后,等待原神自动启动并退出
|
||||
设置完毕后,等待原神启动
|
||||
当你看到门时,点击进入游戏,游戏将自动退出
|
||||
游戏退出后,在程序内输入数字以选择导出到哪个网站/应用
|
||||
|
||||
## 下载地址
|
||||
[releases/latest](https://github.com/HolographicHat/YaeAchievement/releases/latest)
|
||||
|
||||
37
README_EN.md
Normal file
37
README_EN.md
Normal file
@@ -0,0 +1,37 @@
|
||||
<div align="center"><img width="100" src="https://github.com/HolographicHat/YaeAchievement/blob/master/icon.ico">
|
||||
|
||||
# YaeAchievement
|
||||
|
||||
    
|
||||
|
||||
[简体中文](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.
|
||||
|
||||
## How to use:
|
||||
The first time you open it, you need to set the path that contains the Genshin main program (YuanShen.exe / GenshinIimpact.exe)
|
||||

|
||||
After setting up, wait for the game to start.
|
||||
When you see the door, press in to enter the game, the game will automatically exit.
|
||||
After the game exits, enter a number inside the program to select the site/application to export to
|
||||
|
||||
## Download: [Here](https://github.com/HolographicHat/YaeAchievement/releases/latest)
|
||||
|
||||
## Feedback or Problem?
|
||||
[issues](https://github.com/HolographicHat/YaeAchievement/issues) or [QQ群: 913777414](https://qm.qq.com/cgi-bin/qm/qr?k=9UGz-chQVTjZa4b82RA_A41vIcBVNpms&jump_from=webapi)
|
||||
|
||||
## Frequently asked questions
|
||||
0. Q: Unable to start
|
||||
A: Download and install [.NET Runtime 6](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-6.0.8-windows-x64-installer)
|
||||
|
||||
1. Q: Error while Genshin started: Data Exception (31-4302)
|
||||
A: Don't place software in the directory containing Genshin Impact.
|
||||
|
||||
@@ -11,11 +11,37 @@
|
||||
<AssemblyVersion>2.0.0</AssemblyVersion>
|
||||
<FileVersion>2.0.0</FileVersion>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
|
||||
<DebugType>embedded</DebugType>
|
||||
<SelfContained>false</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.21.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta1" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.21.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="res\App.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>App.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Remove="res\Updater.exe" />
|
||||
<EmbeddedResource Include="res\Updater.exe">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="res\App.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>App.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<AdditionalDependencies>detours-x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy $(TargetPath) $(ProjectDir)..\bin\Debug\net6.0</Command>
|
||||
<Command>copy $(TargetPath) $(ProjectDir)..\bin\Debug\net6.0\win-x64\YaeAchievementLib.dll /y</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,7 +8,7 @@ using std::to_string;
|
||||
HWND unityWnd = 0;
|
||||
HANDLE hPipe = 0;
|
||||
|
||||
std::set<UINT16> PacketWhitelist = { 172, 198, 112, 2676, 7, 21, 135 }; // ping, token, loginreq
|
||||
std::set<UINT16> PacketWhitelist = { 179, 130, 156, 2692, 100, 43, 119 }; // ping, token, loginreq
|
||||
|
||||
bool OnPacket(KcpPacket* pkt) {
|
||||
if (pkt->data == nullptr) return true;
|
||||
@@ -16,7 +16,7 @@ bool OnPacket(KcpPacket* pkt) {
|
||||
auto data = (ByteArray*)new BYTE[len + 32];
|
||||
data->max_length = len;
|
||||
memcpy(data->vector, pkt->data, len);
|
||||
Genshin::Packet_Xor(&data, len, nullptr);
|
||||
Genshin::XorEncrypt(&data, len, nullptr);
|
||||
if (ReadMapped<UINT16>(data->vector, 0) != 0x4567) {
|
||||
delete[] data;
|
||||
return true;
|
||||
@@ -29,10 +29,10 @@ bool OnPacket(KcpPacket* pkt) {
|
||||
return false;
|
||||
}
|
||||
printf("Passed cmdid: %d\n", ReadMapped<UINT16>(data->vector, 2));
|
||||
if (ReadMapped<UINT16>(data->vector, 2) == 2676) {
|
||||
if (ReadMapped<UINT16>(data->vector, 2) == 2692) {
|
||||
auto headLength = ReadMapped<UINT16>(data->vector, 4);
|
||||
auto dataLength = ReadMapped<UINT32>(data->vector, 6);
|
||||
auto iStr = Genshin::Convert_ToBase64String(data, 10 + headLength, dataLength, nullptr);
|
||||
auto iStr = Genshin::ToBase64String(data, 10 + headLength, dataLength, nullptr);
|
||||
auto cStr = IlStringToString(iStr) + "\n";
|
||||
WriteFile(hPipe, cStr.c_str(), cStr.length(), nullptr, nullptr);
|
||||
CloseHandle(hPipe);
|
||||
@@ -42,35 +42,30 @@ bool OnPacket(KcpPacket* pkt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<INT, UINT> signatures;
|
||||
|
||||
namespace Hook {
|
||||
|
||||
int Kcp_Send(void* client, KcpPacket* pkt, void* method) {
|
||||
return OnPacket(pkt) ? CALL_ORIGIN(Kcp_Send, client, pkt, method) : 0;
|
||||
int KcpSend(void* client, KcpPacket* pkt, void* method) {
|
||||
return OnPacket(pkt) ? CALL_ORIGIN(KcpSend, client, pkt, method) : 0;
|
||||
}
|
||||
|
||||
void MonoLoginMainPage__set_version(void* obj, Il2CppString* value, void* method) {
|
||||
void SetVersion(void* obj, Il2CppString* value, void* method) {
|
||||
auto version = IlStringToString(value);
|
||||
value = string_new(version + " YaeAchievement");
|
||||
CALL_ORIGIN(MonoLoginMainPage__set_version, obj, value, method);
|
||||
CALL_ORIGIN(SetVersion, obj, value, method);
|
||||
}
|
||||
|
||||
bool Kcp_Recv(void* client, ClientKcpEvent* evt, void* method) {
|
||||
auto result = CALL_ORIGIN(Kcp_Recv, client, evt, 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;
|
||||
}
|
||||
return OnPacket(evt->fields.packet) ? result : false;
|
||||
}
|
||||
|
||||
std::map<INT, UINT> signatures;
|
||||
|
||||
ByteArray* UnityEngine_RecordUserData(INT type) {
|
||||
if (signatures.count(type)) {
|
||||
return GCHandle_GetObject<ByteArray>(signatures[type]);
|
||||
}
|
||||
auto result = CALL_ORIGIN(UnityEngine_RecordUserData, type);
|
||||
signatures[type] = GCHandle_New(result, true);
|
||||
return result;
|
||||
return GCHandle_GetObject<ByteArray>(signatures[type]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,14 +78,18 @@ void Run(HMODULE* phModule) {
|
||||
) {
|
||||
Sleep(1000);
|
||||
}
|
||||
Sleep(5000);
|
||||
DisableVMProtect();
|
||||
InitIL2CPP();
|
||||
HookManager::install(Genshin::UnityEngine_RecordUserData, Hook::UnityEngine_RecordUserData);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Genshin::Application_RecordUserData(i, nullptr);
|
||||
auto result = Genshin::RecordUserData(i, nullptr);
|
||||
signatures[i] = GCHandle_New(result, true);
|
||||
}
|
||||
HookManager::install(Genshin::Kcp_Send, Hook::Kcp_Send);
|
||||
HookManager::install(Genshin::Kcp_Recv, Hook::Kcp_Recv);
|
||||
HookManager::install(Genshin::MonoLoginMainPage__set_version, Hook::MonoLoginMainPage__set_version);
|
||||
signatures[3] = signatures[2];
|
||||
HookManager::install(Genshin::KcpSend, Hook::KcpSend);
|
||||
HookManager::install(Genshin::KcpRecv, Hook::KcpRecv);
|
||||
HookManager::install(Genshin::SetVersion, Hook::SetVersion);
|
||||
HookManager::install(Genshin::UnityEngine_RecordUserData, Hook::UnityEngine_RecordUserData);
|
||||
hPipe = CreateFile(R"(\\.\pipe\YaeAchievementPipe)", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (hPipe == INVALID_HANDLE_VALUE) {
|
||||
Win32ErrorDialog(1001);
|
||||
|
||||
@@ -1,310 +1,3 @@
|
||||
#ifndef DO_API_NO_RETURN
|
||||
#define DO_API_NO_RETURN(r, n, p) DO_API(r,n,p)
|
||||
#endif
|
||||
|
||||
#pragma region General
|
||||
DO_API(int, il2cpp_init, (const char* domain_name));
|
||||
DO_API(int, il2cpp_init_utf16, (const Il2CppChar* domain_name));
|
||||
DO_API(void, il2cpp_shutdown, ());
|
||||
DO_API(void, il2cpp_set_config_dir, (const char* config_path));
|
||||
DO_API(void, il2cpp_set_data_dir, (const char* data_path));
|
||||
DO_API(void, il2cpp_set_temp_dir, (const char* temp_path));
|
||||
DO_API(void, il2cpp_set_commandline_arguments, (int argc, const char* const argv[], const char* basedir));
|
||||
DO_API(void, il2cpp_set_commandline_arguments_utf16, (int argc, const Il2CppChar* const argv[], const char* basedir));
|
||||
DO_API(void, il2cpp_set_config_utf16, (const Il2CppChar* executablePath));
|
||||
DO_API(void, il2cpp_set_config, (const char* executablePath));
|
||||
DO_API(void, il2cpp_set_memory_callbacks, (Il2CppMemoryCallbacks* callbacks));
|
||||
DO_API(const Il2CppImage*, il2cpp_get_corlib, ());
|
||||
DO_API(void, il2cpp_add_internal_call, (const char* name, Il2CppMethodPointer method));
|
||||
DO_API(Il2CppMethodPointer, il2cpp_resolve_icall, (const char* name));
|
||||
DO_API(void*, il2cpp_alloc, (size_t size));
|
||||
DO_API(void, il2cpp_free, (void* ptr));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Array
|
||||
DO_API(Il2CppClass*, il2cpp_array_class_get, (Il2CppClass* element_class, uint32_t rank));
|
||||
DO_API(uint32_t, il2cpp_array_length, (Il2CppArray* array));
|
||||
DO_API(uint32_t, il2cpp_array_get_byte_length, (Il2CppArray* array));
|
||||
DO_API(Il2CppArray*, il2cpp_array_new, (Il2CppClass* elementTypeInfo, il2cpp_array_size_t length));
|
||||
DO_API(Il2CppArray*, il2cpp_array_new_specific, (Il2CppClass* arrayTypeInfo, il2cpp_array_size_t length));
|
||||
DO_API(Il2CppArray*, il2cpp_array_new_full, (Il2CppClass* array_class, il2cpp_array_size_t* lengths, il2cpp_array_size_t* lower_bounds));
|
||||
DO_API(Il2CppClass*, il2cpp_bounded_array_class_get, (Il2CppClass* element_class, uint32_t rank, bool bounded));
|
||||
DO_API(int, il2cpp_array_element_size, (const Il2CppClass* array_class));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Assembly
|
||||
DO_API(const Il2CppImage*, il2cpp_assembly_get_image, (const Il2CppAssembly* assembly));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Class
|
||||
DO_API(void, il2cpp_class_for_each, (void(*klassReportFunc)(Il2CppClass* klass, void* userData), void* userData));
|
||||
DO_API(const Il2CppType*, il2cpp_class_enum_basetype, (Il2CppClass* klass));
|
||||
DO_API(bool, il2cpp_class_is_generic, (const Il2CppClass* klass));
|
||||
DO_API(bool, il2cpp_class_is_inflated, (const Il2CppClass* klass));
|
||||
DO_API(bool, il2cpp_class_is_assignable_from, (Il2CppClass* klass, Il2CppClass* oklass));
|
||||
DO_API(bool, il2cpp_class_is_subclass_of, (Il2CppClass* klass, Il2CppClass* klassc, bool check_interfaces));
|
||||
DO_API(bool, il2cpp_class_has_parent, (Il2CppClass* klass, Il2CppClass* klassc));
|
||||
DO_API(Il2CppClass*, il2cpp_class_from_il2cpp_type, (const Il2CppType* type));
|
||||
DO_API(Il2CppClass*, il2cpp_class_from_name, (const Il2CppImage* image, const char* namespaze, const char* name));
|
||||
DO_API(Il2CppClass*, il2cpp_class_from_system_type, (Il2CppReflectionType* type));
|
||||
DO_API(Il2CppClass*, il2cpp_class_get_element_class, (Il2CppClass* klass));
|
||||
DO_API(const EventInfo*, il2cpp_class_get_events, (Il2CppClass* klass, void** iter));
|
||||
DO_API(FieldInfo*, il2cpp_class_get_fields, (Il2CppClass* klass, void** iter));
|
||||
DO_API(Il2CppClass*, il2cpp_class_get_nested_types, (Il2CppClass* klass, void** iter));
|
||||
DO_API(Il2CppClass*, il2cpp_class_get_interfaces, (Il2CppClass* klass, void** iter));
|
||||
DO_API(const PropertyInfo*, il2cpp_class_get_properties, (Il2CppClass* klass, void** iter));
|
||||
DO_API(const PropertyInfo*, il2cpp_class_get_property_from_name, (Il2CppClass* klass, const char* name));
|
||||
DO_API(FieldInfo*, il2cpp_class_get_field_from_name, (Il2CppClass* klass, const char* name));
|
||||
DO_API(const MethodInfo*, il2cpp_class_get_methods, (Il2CppClass* klass, void** iter));
|
||||
DO_API(const MethodInfo*, il2cpp_class_get_method_from_name, (Il2CppClass* klass, const char* name, int argsCount));
|
||||
DO_API(const char*, il2cpp_class_get_name, (Il2CppClass* klass));
|
||||
DO_API(void, il2cpp_type_get_name_chunked, (const Il2CppType* type, void(*chunkReportFunc)(void* data, void* userData), void* userData));
|
||||
DO_API(const char*, il2cpp_class_get_namespace, (Il2CppClass* klass));
|
||||
DO_API(Il2CppClass*, il2cpp_class_get_parent, (Il2CppClass* klass));
|
||||
DO_API(Il2CppClass*, il2cpp_class_get_declaring_type, (Il2CppClass* klass));
|
||||
DO_API(int32_t, il2cpp_class_instance_size, (Il2CppClass* klass));
|
||||
DO_API(size_t, il2cpp_class_num_fields, (const Il2CppClass* enumKlass));
|
||||
DO_API(bool, il2cpp_class_is_valuetype, (const Il2CppClass* klass));
|
||||
DO_API(int32_t, il2cpp_class_value_size, (Il2CppClass* klass, uint32_t* align));
|
||||
DO_API(bool, il2cpp_class_is_blittable, (const Il2CppClass* klass));
|
||||
DO_API(int, il2cpp_class_get_flags, (const Il2CppClass* klass));
|
||||
DO_API(bool, il2cpp_class_is_abstract, (const Il2CppClass* klass));
|
||||
DO_API(bool, il2cpp_class_is_interface, (const Il2CppClass* klass));
|
||||
DO_API(int, il2cpp_class_array_element_size, (const Il2CppClass* klass));
|
||||
DO_API(Il2CppClass*, il2cpp_class_from_type, (const Il2CppType* type));
|
||||
DO_API(const Il2CppType*, il2cpp_class_get_type, (Il2CppClass* klass));
|
||||
DO_API(uint32_t, il2cpp_class_get_type_token, (Il2CppClass* klass));
|
||||
DO_API(bool, il2cpp_class_has_attribute, (Il2CppClass* klass, Il2CppClass* attr_class));
|
||||
DO_API(bool, il2cpp_class_has_references, (Il2CppClass* klass));
|
||||
DO_API(bool, il2cpp_class_is_enum, (const Il2CppClass* klass));
|
||||
DO_API(const Il2CppImage*, il2cpp_class_get_image, (Il2CppClass* klass));
|
||||
DO_API(const char*, il2cpp_class_get_assemblyname, (const Il2CppClass* klass));
|
||||
DO_API(int, il2cpp_class_get_rank, (const Il2CppClass* klass));
|
||||
DO_API(uint32_t, il2cpp_class_get_data_size, (const Il2CppClass* klass));
|
||||
DO_API(void*, il2cpp_class_get_static_field_data, (const Il2CppClass* klass));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Stats
|
||||
DO_API(bool, il2cpp_stats_dump_to_file, (const char* path));
|
||||
DO_API(uint64_t, il2cpp_stats_get_value, (Il2CppStat stat));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Domain
|
||||
DO_API(Il2CppDomain*, il2cpp_domain_get, ());
|
||||
DO_API(const Il2CppAssembly*, il2cpp_domain_assembly_open, (Il2CppDomain* domain, const char* name));
|
||||
DO_API(const Il2CppAssembly**, il2cpp_domain_get_assemblies, (const Il2CppDomain* domain, size_t* size));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Exception
|
||||
DO_API_NO_RETURN(void, il2cpp_raise_exception, (Il2CppException*));
|
||||
DO_API(Il2CppException*, il2cpp_exception_from_name_msg, (const Il2CppImage* image, const char* name_space, const char* name, const char* msg));
|
||||
DO_API(Il2CppException*, il2cpp_get_exception_argument_null, (const char* arg));
|
||||
DO_API(void, il2cpp_format_exception, (const Il2CppException* ex, char* message, int message_size));
|
||||
DO_API(void, il2cpp_format_stack_trace, (const Il2CppException* ex, char* output, int output_size));
|
||||
DO_API(void, il2cpp_unhandled_exception, (Il2CppException*));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Field
|
||||
DO_API(int, il2cpp_field_get_flags, (FieldInfo* field));
|
||||
DO_API(const char*, il2cpp_field_get_name, (FieldInfo* field));
|
||||
DO_API(Il2CppClass*, il2cpp_field_get_parent, (FieldInfo* field));
|
||||
DO_API(size_t, il2cpp_field_get_offset, (FieldInfo* field));
|
||||
DO_API(const Il2CppType*, il2cpp_field_get_type, (FieldInfo* field));
|
||||
DO_API(void, il2cpp_field_get_value, (Il2CppObject* obj, FieldInfo* field, void* value));
|
||||
DO_API(Il2CppObject*, il2cpp_field_get_value_object, (FieldInfo* field, Il2CppObject* obj));
|
||||
DO_API(bool, il2cpp_field_has_attribute, (FieldInfo* field, Il2CppClass* attr_class));
|
||||
DO_API(void, il2cpp_field_set_value, (Il2CppObject* obj, FieldInfo* field, void* value));
|
||||
DO_API(void, il2cpp_field_static_get_value, (FieldInfo* field, void* value));
|
||||
DO_API(void, il2cpp_field_static_set_value, (FieldInfo* field, void* value));
|
||||
DO_API(void, il2cpp_field_set_value_object, (Il2CppObject* instance, FieldInfo* field, Il2CppObject* value));
|
||||
DO_API(bool, il2cpp_field_is_literal, (FieldInfo* field));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region GC
|
||||
DO_API(void, il2cpp_gc_collect, (int maxGenerations));
|
||||
DO_API(int32_t, il2cpp_gc_collect_a_little, ());
|
||||
DO_API(void, il2cpp_gc_disable, ());
|
||||
DO_API(void, il2cpp_gc_enable, ());
|
||||
DO_API(bool, il2cpp_gc_is_disabled, ());
|
||||
DO_API(int64_t, il2cpp_gc_get_max_time_slice_ns, ());
|
||||
DO_API(void, il2cpp_gc_set_max_time_slice_ns, (int64_t maxTimeSlice));
|
||||
DO_API(bool, il2cpp_gc_is_incremental, ());
|
||||
DO_API(int64_t, il2cpp_gc_get_used_size, ());
|
||||
DO_API(int64_t, il2cpp_gc_get_heap_size, ());
|
||||
DO_API(void, il2cpp_gc_wbarrier_set_field, (Il2CppObject* obj, void** targetAddress, void* object));
|
||||
DO_API(bool, il2cpp_gc_has_strict_wbarriers, ());
|
||||
DO_API(void, il2cpp_gc_set_external_allocation_tracker, (void(*func)(void*, size_t, int)));
|
||||
DO_API(void, il2cpp_gc_set_external_wbarrier_tracker, (void(*func)(void**)));
|
||||
DO_API(void, il2cpp_gc_foreach_heap, (void(*func)(void* data, void* userData), void* userData));
|
||||
DO_API(void, il2cpp_stop_gc_world, ());
|
||||
DO_API(void, il2cpp_start_gc_world, ());
|
||||
#pragma endregion
|
||||
|
||||
#pragma region GCHandle
|
||||
DO_API(uint32_t, il2cpp_gchandle_new, (Il2CppObject* obj, bool pinned));
|
||||
DO_API(uint32_t, il2cpp_gchandle_new_weakref, (Il2CppObject* obj, bool track_resurrection));
|
||||
DO_API(Il2CppObject*, il2cpp_gchandle_get_target, (uint32_t gchandle));
|
||||
DO_API(void, il2cpp_gchandle_free, (uint32_t gchandle));
|
||||
DO_API(void, il2cpp_gchandle_foreach_get_target, (void(*func)(void* data, void* userData), void* userData));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region VMRuntimeInfo
|
||||
DO_API(uint32_t, il2cpp_object_header_size, ());
|
||||
DO_API(uint32_t, il2cpp_array_object_header_size, ());
|
||||
DO_API(uint32_t, il2cpp_offset_of_array_length_in_array_object_header, ());
|
||||
DO_API(uint32_t, il2cpp_offset_of_array_bounds_in_array_object_header, ());
|
||||
DO_API(uint32_t, il2cpp_allocation_granularity, ());
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Liveness
|
||||
DO_API(void*, il2cpp_unity_liveness_calculation_begin, (Il2CppClass* filter, int max_object_count, il2cpp_register_object_callback callback, void* userdata, il2cpp_WorldChangedCallback onWorldStarted, il2cpp_WorldChangedCallback onWorldStopped));
|
||||
DO_API(void, il2cpp_unity_liveness_calculation_end, (void* state));
|
||||
DO_API(void, il2cpp_unity_liveness_calculation_from_root, (Il2CppObject* root, void* state));
|
||||
DO_API(void, il2cpp_unity_liveness_calculation_from_statics, (void* state));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Method
|
||||
DO_API(const Il2CppType*, il2cpp_method_get_return_type, (const MethodInfo* method));
|
||||
DO_API(Il2CppClass*, il2cpp_method_get_declaring_type, (const MethodInfo* method));
|
||||
DO_API(const char*, il2cpp_method_get_name, (const MethodInfo* method));
|
||||
DO_API(const MethodInfo*, il2cpp_method_get_from_reflection, (const Il2CppReflectionMethod* method));
|
||||
DO_API(Il2CppReflectionMethod*, il2cpp_method_get_object, (const MethodInfo* method, Il2CppClass* refclass));
|
||||
DO_API(bool, il2cpp_method_is_generic, (const MethodInfo* method));
|
||||
DO_API(bool, il2cpp_method_is_inflated, (const MethodInfo* method));
|
||||
DO_API(bool, il2cpp_method_is_instance, (const MethodInfo* method));
|
||||
DO_API(uint32_t, il2cpp_method_get_param_count, (const MethodInfo* method));
|
||||
DO_API(const Il2CppType*, il2cpp_method_get_param, (const MethodInfo* method, uint32_t index));
|
||||
DO_API(Il2CppClass*, il2cpp_method_get_class, (const MethodInfo* method));
|
||||
DO_API(bool, il2cpp_method_has_attribute, (const MethodInfo* method, Il2CppClass* attr_class));
|
||||
DO_API(uint32_t, il2cpp_method_get_flags, (const MethodInfo* method, uint32_t* iflags));
|
||||
DO_API(uint32_t, il2cpp_method_get_token, (const MethodInfo* method));
|
||||
DO_API(const char*, il2cpp_method_get_param_name, (const MethodInfo* method, uint32_t index));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Property
|
||||
DO_API(uint32_t, il2cpp_property_get_flags, (PropertyInfo* prop));
|
||||
DO_API(const MethodInfo*, il2cpp_property_get_get_method, (PropertyInfo* prop));
|
||||
DO_API(const MethodInfo*, il2cpp_property_get_set_method, (PropertyInfo* prop));
|
||||
DO_API(const char*, il2cpp_property_get_name, (PropertyInfo* prop));
|
||||
DO_API(Il2CppClass*, il2cpp_property_get_parent, (PropertyInfo* prop));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Object
|
||||
DO_API(Il2CppClass*, il2cpp_object_get_class, (Il2CppObject* obj));
|
||||
DO_API(uint32_t, il2cpp_object_get_size, (Il2CppObject* obj));
|
||||
DO_API(const MethodInfo*, il2cpp_object_get_virtual_method, (Il2CppObject* obj, const MethodInfo* method));
|
||||
DO_API(Il2CppObject*, il2cpp_object_new, (const Il2CppClass* klass));
|
||||
DO_API(void*, il2cpp_object_unbox, (Il2CppObject* obj));
|
||||
DO_API(Il2CppObject*, il2cpp_value_box, (Il2CppClass* klass, void* data));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Monitor
|
||||
DO_API(void, il2cpp_monitor_enter, (Il2CppObject* obj));
|
||||
DO_API(bool, il2cpp_monitor_try_enter, (Il2CppObject* obj, uint32_t timeout));
|
||||
DO_API(void, il2cpp_monitor_exit, (Il2CppObject* obj));
|
||||
DO_API(void, il2cpp_monitor_pulse, (Il2CppObject* obj));
|
||||
DO_API(void, il2cpp_monitor_pulse_all, (Il2CppObject* obj));
|
||||
DO_API(void, il2cpp_monitor_wait, (Il2CppObject* obj));
|
||||
DO_API(bool, il2cpp_monitor_try_wait, (Il2CppObject* obj, uint32_t timeout));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Runtime
|
||||
DO_API(Il2CppObject*, il2cpp_runtime_invoke, (const MethodInfo* method, void* obj, void** params, Il2CppException** exc));
|
||||
DO_API(Il2CppObject*, il2cpp_runtime_invoke_convert_args, (const MethodInfo* method, void* obj, Il2CppObject** params, int paramCount, Il2CppException** exc));
|
||||
DO_API(void, il2cpp_runtime_class_init, (Il2CppClass* klass));
|
||||
DO_API(void, il2cpp_runtime_object_init, (Il2CppObject* obj));
|
||||
DO_API(void, il2cpp_runtime_object_init_exception, (Il2CppObject* obj, Il2CppException** exc));
|
||||
DO_API(void, il2cpp_runtime_unhandled_exception_policy_set, (Il2CppRuntimeUnhandledExceptionPolicy value));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region String
|
||||
DO_API(int32_t, il2cpp_string_length, (Il2CppString* str));
|
||||
DO_API(Il2CppChar*, il2cpp_string_chars, (Il2CppString* str));
|
||||
DO_API(Il2CppString*, il2cpp_string_new, (const char* str));
|
||||
DO_API(Il2CppString*, il2cpp_string_new_len, (const char* str, uint32_t length));
|
||||
DO_API(Il2CppString*, il2cpp_string_new_utf16, (const Il2CppChar* text, int32_t len));
|
||||
DO_API(Il2CppString*, il2cpp_string_new_wrapper, (const char* str));
|
||||
DO_API(Il2CppString*, il2cpp_string_intern, (Il2CppString* str));
|
||||
DO_API(Il2CppString*, il2cpp_string_is_interned, (Il2CppString* str));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Thread
|
||||
DO_API(Il2CppThread*, il2cpp_thread_current, ());
|
||||
DO_API(Il2CppThread*, il2cpp_thread_attach, (Il2CppDomain* domain));
|
||||
DO_API(void, il2cpp_thread_detach, (Il2CppThread* thread));
|
||||
DO_API(Il2CppThread**, il2cpp_thread_get_all_attached_threads, (size_t* size));
|
||||
DO_API(bool, il2cpp_is_vm_thread, (Il2CppThread* thread));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region StackTrace
|
||||
DO_API(void, il2cpp_current_thread_walk_frame_stack, (Il2CppFrameWalkFunc func, void* user_data));
|
||||
DO_API(void, il2cpp_thread_walk_frame_stack, (Il2CppThread* thread, Il2CppFrameWalkFunc func, void* user_data));
|
||||
DO_API(bool, il2cpp_current_thread_get_top_frame, (Il2CppStackFrameInfo* frame));
|
||||
DO_API(bool, il2cpp_thread_get_top_frame, (Il2CppThread* thread, Il2CppStackFrameInfo* frame));
|
||||
DO_API(bool, il2cpp_current_thread_get_frame_at, (int32_t offset, Il2CppStackFrameInfo* frame));
|
||||
DO_API(bool, il2cpp_thread_get_frame_at, (Il2CppThread* thread, int32_t offset, Il2CppStackFrameInfo* frame));
|
||||
DO_API(int32_t, il2cpp_current_thread_get_stack_depth, ());
|
||||
DO_API(int32_t, il2cpp_thread_get_stack_depth, (Il2CppThread* thread));
|
||||
DO_API(void, il2cpp_override_stack_backtrace, (Il2CppBacktraceFunc stackBacktraceFunc));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Type
|
||||
DO_API(Il2CppObject*, il2cpp_type_get_object, (const Il2CppType* type));
|
||||
DO_API(int, il2cpp_type_get_type, (const Il2CppType* type));
|
||||
DO_API(Il2CppClass*, il2cpp_type_get_class_or_element_class, (const Il2CppType* type));
|
||||
DO_API(char*, il2cpp_type_get_name, (const Il2CppType* type));
|
||||
DO_API(bool, il2cpp_type_is_byref, (const Il2CppType* type));
|
||||
DO_API(uint32_t, il2cpp_type_get_attrs, (const Il2CppType* type));
|
||||
DO_API(bool, il2cpp_type_equals, (const Il2CppType* type, const Il2CppType* otherType));
|
||||
DO_API(char*, il2cpp_type_get_assembly_qualified_name, (const Il2CppType* type));
|
||||
DO_API(bool, il2cpp_type_is_static, (const Il2CppType* type));
|
||||
DO_API(bool, il2cpp_type_is_pointer_type, (const Il2CppType* type));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Image
|
||||
DO_API(const Il2CppAssembly*, il2cpp_image_get_assembly, (const Il2CppImage* image));
|
||||
DO_API(const char*, il2cpp_image_get_name, (const Il2CppImage* image));
|
||||
DO_API(const char*, il2cpp_image_get_filename, (const Il2CppImage* image));
|
||||
DO_API(const MethodInfo*, il2cpp_image_get_entry_point, (const Il2CppImage* image));
|
||||
DO_API(size_t, il2cpp_image_get_class_count, (const Il2CppImage* image));
|
||||
DO_API(const Il2CppClass*, il2cpp_image_get_class, (const Il2CppImage* image, size_t index));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region MemoryInformation
|
||||
DO_API(Il2CppManagedMemorySnapshot*, il2cpp_capture_memory_snapshot, ());
|
||||
DO_API(void, il2cpp_free_captured_memory_snapshot, (Il2CppManagedMemorySnapshot* snapshot));
|
||||
DO_API(void, il2cpp_set_find_plugin_callback, (Il2CppSetFindPlugInCallback method));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Logging
|
||||
DO_API(void, il2cpp_register_log_callback, (Il2CppLogCallback method));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Debugger
|
||||
DO_API(void, il2cpp_debugger_set_agent_options, (const char* options));
|
||||
DO_API(bool, il2cpp_is_debugger_attached, ());
|
||||
DO_API(void, il2cpp_register_debugger_agent_transport, (Il2CppDebuggerTransport* debuggerTransport));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region DebugMetadata
|
||||
DO_API(bool, il2cpp_debug_get_method_info, (const MethodInfo*, Il2CppMethodDebugInfo* methodDebugInfo));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region TLSModule
|
||||
DO_API(void, il2cpp_unity_install_unitytls_interface, (const void* unitytlsInterfaceStruct));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CustomAttributes
|
||||
DO_API(Il2CppCustomAttrInfo*, il2cpp_custom_attrs_from_class, (Il2CppClass* klass));
|
||||
DO_API(Il2CppCustomAttrInfo*, il2cpp_custom_attrs_from_method, (const MethodInfo* method));
|
||||
DO_API(Il2CppObject*, il2cpp_custom_attrs_get_attr, (Il2CppCustomAttrInfo* ainfo, Il2CppClass* attr_klass));
|
||||
DO_API(bool, il2cpp_custom_attrs_has_attr, (Il2CppCustomAttrInfo* ainfo, Il2CppClass* attr_klass));
|
||||
DO_API(Il2CppArray*, il2cpp_custom_attrs_construct, (Il2CppCustomAttrInfo* cinfo));
|
||||
DO_API(void, il2cpp_custom_attrs_free, (Il2CppCustomAttrInfo* ainfo));
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Il2CppClassUserDataForGetComponentOptimization
|
||||
DO_API(int, il2cpp_class_get_userdata_offset, ());
|
||||
DO_API(void, il2cpp_class_set_userdata, (Il2CppClass* klass, void* userdata));
|
||||
DO_API(void, il2cpp_set_default_thread_affinity, (int64_t affinity_mask));
|
||||
#pragma endregion
|
||||
DO_API(0x02974550, 0x02970540, uint32_t, il2cpp_gchandle_new, (Il2CppObject* obj, bool pinned));
|
||||
DO_API(0x02974260, 0x02970250, Il2CppObject*, il2cpp_gchandle_get_target, (uint32_t gchandle));
|
||||
DO_API(0x028BF7E0, 0x028BBE80, Il2CppString*, il2cpp_string_new, (const char* str));
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "il2cpp-types.h"
|
||||
|
||||
// IL2CPP APIs
|
||||
#define DO_API(r, n, p) extern r (*n) p
|
||||
#define DO_API(ca, oa, r, n, p) extern r (*n) p
|
||||
#include "il2cpp-api-functions.h"
|
||||
#undef DO_API
|
||||
|
||||
|
||||
@@ -1,10 +1,34 @@
|
||||
using namespace Genshin;
|
||||
|
||||
DO_APP_FUNC(0x05254960, 0x052544E0, Il2CppString*, Convert_ToBase64String, (ByteArray* value, int offset, int length, void* method));
|
||||
DO_APP_FUNC(0x020127B0, 0x02012D40, void, Packet_Xor, (ByteArray** data, int length, void* method));
|
||||
// DO_APP_FUNC(CN_OFFSET, OS_OFFSET, RETURN, FUNC_NAME, (ARGS...));
|
||||
|
||||
DO_APP_FUNC(0X01AD8E40, 0x01AD9740, void, MonoLoginMainPage__set_version, (void* obj, Il2CppString* value, void* method));
|
||||
DO_APP_FUNC(0x05C25AC0, 0x05C25E60, ByteArray*, Application_RecordUserData, (int32_t nType, void* method));
|
||||
// N: System.Convert$ToBase64String
|
||||
// L: mscorlib
|
||||
// S: Ref/E8 ?? ?? ?? ?? 48 8B D8 EB 23 E8
|
||||
DO_APP_FUNC(0x086B86C0, 0x086B6440, Il2CppString*, ToBase64String, (ByteArray* value, int offset, int length, void* method));
|
||||
|
||||
DO_APP_FUNC(0x015C19D0, 0x015C2150, int, Kcp_Send, (void* client, KcpPacket* pkt, void* method));
|
||||
DO_APP_FUNC(0x02CF31D0, 0x02CF33A0, bool, Kcp_Recv, (void* client, ClientKcpEvent* evt, void* method));
|
||||
// N: MoleMole.MonoLoginMainPage.version$set
|
||||
// L: Assembly-CSharp
|
||||
// S: 84 C0 74 35 B9 52 FA 00 00 E8 ?? ?? ?? ?? 84 C0 74 27 B9 52 FA 00 00 E8 ?? ?? ?? ?? 48 85 C0 74 52 4C 8B C7 48 8B D3 48 8B C8 48 8B 5C 24 30 48 83 C4 20 5F E9
|
||||
DO_APP_FUNC(0X04186660, 0x04180EC0, void, SetVersion, (void* obj, Il2CppString* value, void* method));
|
||||
|
||||
// N: UnityEngine.Application$RecordUserData
|
||||
// L: UnityEngine.CoreModule
|
||||
DO_APP_FUNC(0x090BEBC0, 0x090BD710, ByteArray*, RecordUserData, (int32_t nType, void* method));
|
||||
|
||||
// N: MoleMole.Packet$XorEncrypt [Obfuscated]
|
||||
// L: Assembly-CSharp
|
||||
DO_APP_FUNC(0x0423B270, 0x04235CE0, void, XorEncrypt, (ByteArray** data, int length, void* method));
|
||||
|
||||
// N: Kcp.KcpNative$kcp_client_send_packet [Obfuscated]
|
||||
// L: Assembly-CSharp
|
||||
DO_APP_FUNC(0x042281D0, 0x04222A60, int, KcpSend, (void* client, KcpPacket* pkt, void* method));
|
||||
|
||||
// N: MoleMole.KcpClient$TryDequeueEvent [Obfuscated]
|
||||
// L: Assembly-CSharp
|
||||
// S: Ref/public static extern Int32 [A-Z]{11}\(IntPtr [A-Z]{11}, [A-Z]{11}& [A-Z]{11}\)
|
||||
DO_APP_FUNC(0x02BAFFF0, 0x02BAC3D0, bool, KcpRecv, (void* client, ClientKcpEvent* evt, void* method));
|
||||
|
||||
DO_APP_FUNC(0x08A43710, 0x08A41130, LPVOID, GetDefaultEncoding, (void* method));
|
||||
|
||||
DO_APP_FUNC(0x08A42FB0, 0x08A409B0, Il2CppString*, GetString, (LPVOID encoding, LPVOID bytes, void* method));
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "il2cpp-init.h"
|
||||
|
||||
#define DO_API(r, n, p) r (*n) p
|
||||
#define DO_API(ca, oa, r, n, p) r (*n) p
|
||||
#include "il2cpp-api-functions.h"
|
||||
#undef DO_API
|
||||
|
||||
@@ -20,11 +20,6 @@ namespace Genshin {
|
||||
|
||||
using std::string;
|
||||
|
||||
UINT64 GetAddressByExports(HMODULE base, const char* name) {
|
||||
UINT64 funcAddr = reinterpret_cast<UINT64>(GetProcAddress(base, name));
|
||||
return funcAddr == 0 ? 0 : funcAddr;
|
||||
}
|
||||
|
||||
void InitIL2CPP() {
|
||||
TCHAR szFileName[MAX_PATH];
|
||||
GetModuleFileName(NULL, szFileName, MAX_PATH);
|
||||
@@ -32,7 +27,7 @@ void InitIL2CPP() {
|
||||
auto hBase = GetModuleHandle("UserAssembly.dll");
|
||||
auto bAddr = (UINT64)hBase;
|
||||
auto cAddr = (UINT64)GetModuleHandle("UnityPlayer.dll");
|
||||
#define DO_API(r, n, p) n = (r (*) p) GetAddressByExports(hBase, #n);
|
||||
#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))
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
using namespace Genshin;
|
||||
|
||||
DO_UNI_FUNC(0x00B9D710, 0x00B9D710, ByteArray*, UnityEngine_RecordUserData, (int32_t nType));
|
||||
DO_UNI_FUNC(0x00100300, 0x00100300, ByteArray*, UnityEngine_RecordUserData, (int32_t nType));
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
#include "pch.h"
|
||||
#include "util.h"
|
||||
|
||||
VOID DisableVMProtect() {
|
||||
DWORD oldProtect = 0;
|
||||
auto ntdll = GetModuleHandleA("ntdll.dll");
|
||||
BYTE callcode = ((BYTE*)GetProcAddress(ntdll, "NtQuerySection"))[4] - 1;
|
||||
BYTE restore[] = { 0x4C, 0x8B, 0xD1, 0xB8, callcode };
|
||||
auto nt_vp = (BYTE*)GetProcAddress(ntdll, "NtProtectVirtualMemory");
|
||||
VirtualProtect(nt_vp, sizeof(restore), PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
memcpy(nt_vp, restore, sizeof(restore));
|
||||
VirtualProtect(nt_vp, sizeof(restore), oldProtect, &oldProtect);
|
||||
}
|
||||
|
||||
#pragma region StringConvert
|
||||
|
||||
string IlStringToString(Il2CppString* str, UINT codePage) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
VOID DisableVMProtect();
|
||||
bool IsLittleEndian();
|
||||
HWND FindMainWindowByPID(DWORD pid);
|
||||
UINT32 GCHandle_New(LPVOID object, bool pinned);
|
||||
|
||||
368
res/App.Designer.cs
generated
Normal file
368
res/App.Designer.cs
generated
Normal file
@@ -0,0 +1,368 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace YaeAchievement.res {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class App {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal App() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("YaeAchievement.res.App", typeof(App).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to all achievement.
|
||||
/// </summary>
|
||||
internal static string AllAchievement {
|
||||
get {
|
||||
return ResourceManager.GetString("AllAchievement", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please close another instance..
|
||||
/// </summary>
|
||||
internal static string AnotherInstance {
|
||||
get {
|
||||
return ResourceManager.GetString("AnotherInstance", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to YaeAchievement ({0}).
|
||||
/// </summary>
|
||||
internal static string AppBanner {
|
||||
get {
|
||||
return ResourceManager.GetString("AppBanner", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You need to login genshin impact before exporting..
|
||||
/// </summary>
|
||||
internal static string ConfigNeedStartGenshin {
|
||||
get {
|
||||
return ResourceManager.GetString("ConfigNeedStartGenshin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Download: {0}.
|
||||
/// </summary>
|
||||
internal static string DownloadLink {
|
||||
get {
|
||||
return ResourceManager.GetString("DownloadLink", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Network error ({0}: {1}).
|
||||
/// </summary>
|
||||
internal static string ExceptionNetwork {
|
||||
get {
|
||||
return ResourceManager.GetString("ExceptionNetwork", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Export to:
|
||||
///[0] Cocogoat (https://cocogoat.work/achievement, Default)
|
||||
///[1] Snap.HuTao
|
||||
///[2] Paimon.moe
|
||||
///[3] Seelie.me
|
||||
///[4] Csv file
|
||||
///[5] Xunkong
|
||||
///Input a number (0-5): .
|
||||
/// </summary>
|
||||
internal static string ExportChoose {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportChoose", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fail, please contact developer to get help information.
|
||||
/// </summary>
|
||||
internal static string ExportToCocogoatFail {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToCocogoatFail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Successfully exported to cocogoat..
|
||||
/// </summary>
|
||||
internal static string ExportToCocogoatSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToCocogoatSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Successfully exported to {0}.
|
||||
/// </summary>
|
||||
internal static string ExportToFileSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToFileSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please update SnapGenshin and retry..
|
||||
/// </summary>
|
||||
internal static string ExportToSnapGenshinNeedUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToSnapGenshinNeedUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Successfully exported to snap genshin..
|
||||
/// </summary>
|
||||
internal static string ExportToSnapGenshinSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToSnapGenshinSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0}.
|
||||
/// </summary>
|
||||
internal static string ExportToWxApp1Success {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToWxApp1Success", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please update xunkong and retry..
|
||||
/// </summary>
|
||||
internal static string ExportToXunkongNeedUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToXunkongNeedUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Successfully exported to xunkong..
|
||||
/// </summary>
|
||||
internal static string ExportToXunkongSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("ExportToXunkongSuccess", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Game process start ({0}).
|
||||
/// </summary>
|
||||
internal static string GameLoading {
|
||||
get {
|
||||
return ResourceManager.GetString("GameLoading", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Game exited..
|
||||
/// </summary>
|
||||
internal static string GameProcessExit {
|
||||
get {
|
||||
return ResourceManager.GetString("GameProcessExit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please update genshin and retry..
|
||||
/// </summary>
|
||||
internal static string GenshinHashError {
|
||||
get {
|
||||
return ResourceManager.GetString("GenshinHashError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please close game before run this application. ({0}).
|
||||
/// </summary>
|
||||
internal static string GenshinIsRunning {
|
||||
get {
|
||||
return ResourceManager.GetString("GenshinIsRunning", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Network error:.
|
||||
/// </summary>
|
||||
internal static string NetworkError {
|
||||
get {
|
||||
return ResourceManager.GetString("NetworkError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Press any key to exit..
|
||||
/// </summary>
|
||||
internal static string PressKeyToExit {
|
||||
get {
|
||||
return ResourceManager.GetString("PressKeyToExit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reward not taken.
|
||||
/// </summary>
|
||||
internal static string StatusFinished {
|
||||
get {
|
||||
return ResourceManager.GetString("StatusFinished", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid.
|
||||
/// </summary>
|
||||
internal static string StatusInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("StatusInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Finished.
|
||||
/// </summary>
|
||||
internal static string StatusRewardTaken {
|
||||
get {
|
||||
return ResourceManager.GetString("StatusRewardTaken", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unfinished.
|
||||
/// </summary>
|
||||
internal static string StatusUnfinished {
|
||||
get {
|
||||
return ResourceManager.GetString("StatusUnfinished", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Description:
|
||||
///{0}.
|
||||
/// </summary>
|
||||
internal static string UpdateDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("UpdateDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Downloading update package....
|
||||
/// </summary>
|
||||
internal static string UpdateDownloading {
|
||||
get {
|
||||
return ResourceManager.GetString("UpdateDownloading", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Has update: {0} => {1}.
|
||||
/// </summary>
|
||||
internal static string UpdateNewVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("UpdateNewVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] Updater {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("Updater", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Upload error to appcenter....
|
||||
/// </summary>
|
||||
internal static string UploadError {
|
||||
get {
|
||||
return ResourceManager.GetString("UploadError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use previous fetched data? (yes|no).
|
||||
/// </summary>
|
||||
internal static string UsePreviousData {
|
||||
get {
|
||||
return ResourceManager.GetString("UsePreviousData", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Downloading Visual C++ Redistributable....
|
||||
/// </summary>
|
||||
internal static string VcRuntimeDownload {
|
||||
get {
|
||||
return ResourceManager.GetString("VcRuntimeDownload", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Installing Visual C++ Redistributable....
|
||||
/// </summary>
|
||||
internal static string VcRuntimeInstalling {
|
||||
get {
|
||||
return ResourceManager.GetString("VcRuntimeInstalling", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
res/App.resx
Normal file
129
res/App.resx
Normal file
@@ -0,0 +1,129 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<root>
|
||||
<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>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ExportToCocogoatFail" xml:space="preserve">
|
||||
<value>Fail, please contact developer to get help information</value>
|
||||
</data>
|
||||
<data name="AllAchievement" xml:space="preserve">
|
||||
<value>all achievement</value>
|
||||
</data>
|
||||
<data name="ExportChoose" xml:space="preserve">
|
||||
<value>Export to:
|
||||
[0] Cocogoat (https://cocogoat.work/achievement, Default)
|
||||
[1] Snap.HuTao
|
||||
[2] Paimon.moe
|
||||
[3] Seelie.me
|
||||
[4] Csv file
|
||||
[5] Xunkong
|
||||
Input a number (0-5): </value>
|
||||
</data>
|
||||
<data name="ExportToCocogoatSuccess" xml:space="preserve">
|
||||
<value>Successfully exported to cocogoat.</value>
|
||||
</data>
|
||||
<data name="ExportToWxApp1Success" xml:space="preserve">
|
||||
<value>{0}</value>
|
||||
</data>
|
||||
<data name="ExportToSnapGenshinSuccess" xml:space="preserve">
|
||||
<value>Successfully exported to snap genshin.</value>
|
||||
</data>
|
||||
<data name="ExportToSnapGenshinNeedUpdate" xml:space="preserve">
|
||||
<value>Please update SnapGenshin and retry.</value>
|
||||
</data>
|
||||
<data name="ExportToFileSuccess" xml:space="preserve">
|
||||
<value>Successfully exported to {0}</value>
|
||||
</data>
|
||||
<data name="ExportToXunkongSuccess" xml:space="preserve">
|
||||
<value>Successfully exported to xunkong.</value>
|
||||
</data>
|
||||
<data name="ExportToXunkongNeedUpdate" xml:space="preserve">
|
||||
<value>Please update xunkong and retry.</value>
|
||||
</data>
|
||||
<data name="StatusInvalid" xml:space="preserve">
|
||||
<value>Invalid</value>
|
||||
</data>
|
||||
<data name="StatusRewardTaken" xml:space="preserve">
|
||||
<value>Finished</value>
|
||||
</data>
|
||||
<data name="StatusUnfinished" xml:space="preserve">
|
||||
<value>Unfinished</value>
|
||||
</data>
|
||||
<data name="StatusFinished" xml:space="preserve">
|
||||
<value>Reward not taken</value>
|
||||
</data>
|
||||
<data name="ConfigNeedStartGenshin" xml:space="preserve">
|
||||
<value>You need to login genshin impact before exporting.</value>
|
||||
</data>
|
||||
<data name="DownloadLink" xml:space="preserve">
|
||||
<value>Download: {0}</value>
|
||||
</data>
|
||||
<data name="GameProcessExit" xml:space="preserve">
|
||||
<value>Game exited.</value>
|
||||
</data>
|
||||
<data name="GameLoading" xml:space="preserve">
|
||||
<value>Game process start ({0})</value>
|
||||
</data>
|
||||
<data name="UploadError" xml:space="preserve">
|
||||
<value>Upload error to appcenter...</value>
|
||||
</data>
|
||||
<data name="PressKeyToExit" xml:space="preserve">
|
||||
<value>Press any key to exit.</value>
|
||||
</data>
|
||||
<data name="GenshinIsRunning" xml:space="preserve">
|
||||
<value>Please close game before run this application. ({0})</value>
|
||||
</data>
|
||||
<data name="AnotherInstance" xml:space="preserve">
|
||||
<value>Please close another instance.</value>
|
||||
</data>
|
||||
<data name="UpdateNewVersion" xml:space="preserve">
|
||||
<value>Has update: {0} => {1}</value>
|
||||
</data>
|
||||
<data name="UpdateDescription" xml:space="preserve">
|
||||
<value>Description:
|
||||
{0}</value>
|
||||
</data>
|
||||
<data name="UpdateDownloading" xml:space="preserve">
|
||||
<value>Downloading update package...</value>
|
||||
</data>
|
||||
<data name="AppBanner" xml:space="preserve">
|
||||
<value>YaeAchievement ({0})</value>
|
||||
</data>
|
||||
<data name="UsePreviousData" xml:space="preserve">
|
||||
<value>Use previous fetched data? (yes|no)</value>
|
||||
</data>
|
||||
<data name="NetworkError" xml:space="preserve">
|
||||
<value>Network error:</value>
|
||||
</data>
|
||||
<data name="VcRuntimeDownload" xml:space="preserve">
|
||||
<value>Downloading Visual C++ Redistributable...</value>
|
||||
</data>
|
||||
<data name="VcRuntimeInstalling" xml:space="preserve">
|
||||
<value>Installing Visual C++ Redistributable...</value>
|
||||
</data>
|
||||
<data name="ExceptionNetwork" xml:space="preserve">
|
||||
<value>Network error ({0}: {1})</value>
|
||||
</data>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="Updater" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Updater.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="GenshinHashError" xml:space="preserve">
|
||||
<value>Please update genshin and retry.</value>
|
||||
</data>
|
||||
</root>
|
||||
119
res/App.zh.resx
Normal file
119
res/App.zh.resx
Normal file
@@ -0,0 +1,119 @@
|
||||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ExportToCocogoatFail" xml:space="preserve">
|
||||
<value>导出失败, 请联系开发者以获取帮助</value>
|
||||
</data>
|
||||
<data name="AllAchievement" xml:space="preserve">
|
||||
<value>全部成就</value>
|
||||
</data>
|
||||
<data name="ExportChoose" xml:space="preserve">
|
||||
<value>导出至:
|
||||
[0] 椰羊 (https://cocogoat.work/achievement, 默认)
|
||||
[1] Snap.HuTao
|
||||
[2] Paimon.moe
|
||||
[3] Seelie.me
|
||||
[4] 表格文件
|
||||
[5] 寻空
|
||||
[6] 原魔工具箱
|
||||
输入一个数字 (0-6): </value>
|
||||
</data>
|
||||
<data name="ExportToCocogoatSuccess" xml:space="preserve">
|
||||
<value>在浏览器内进行下一步操作</value>
|
||||
</data>
|
||||
<data name="ExportToWxApp1Success" xml:space="preserve">
|
||||
<value>在小程序导入页面输入以下代码: {0}</value>
|
||||
</data>
|
||||
<data name="ExportToSnapGenshinSuccess" xml:space="preserve">
|
||||
<value>在 SnapHutao 进行下一步操作</value>
|
||||
</data>
|
||||
<data name="ExportToSnapGenshinNeedUpdate" xml:space="preserve">
|
||||
<value>更新 SnapHutao 至最新版本后重试</value>
|
||||
</data>
|
||||
<data name="ExportToFileSuccess" xml:space="preserve">
|
||||
<value>成就数据已导出至 {0}</value>
|
||||
</data>
|
||||
<data name="ExportToXunkongSuccess" xml:space="preserve">
|
||||
<value>在寻空中进行下一步操作</value>
|
||||
</data>
|
||||
<data name="ExportToXunkongNeedUpdate" xml:space="preserve">
|
||||
<value>更新寻空至最新版本后重试</value>
|
||||
</data>
|
||||
<data name="StatusInvalid" xml:space="preserve">
|
||||
<value>未知</value>
|
||||
</data>
|
||||
<data name="StatusFinished" xml:space="preserve">
|
||||
<value>已完成但未领取奖励</value>
|
||||
</data>
|
||||
<data name="StatusUnfinished" xml:space="preserve">
|
||||
<value>未完成</value>
|
||||
</data>
|
||||
<data name="StatusRewardTaken" xml:space="preserve">
|
||||
<value>已完成</value>
|
||||
</data>
|
||||
<data name="ConfigNeedStartGenshin" xml:space="preserve">
|
||||
<value>在导出前你需要先完成一次登入流程.</value>
|
||||
</data>
|
||||
<data name="DownloadLink" xml:space="preserve">
|
||||
<value>下载地址: {0}</value>
|
||||
</data>
|
||||
<data name="GameProcessExit" xml:space="preserve">
|
||||
<value>游戏进程异常退出</value>
|
||||
</data>
|
||||
<data name="GameLoading" xml:space="preserve">
|
||||
<value>原神正在启动 ({0})</value>
|
||||
</data>
|
||||
<data name="UploadError" xml:space="preserve">
|
||||
<value>正在上报错误信息...</value>
|
||||
</data>
|
||||
<data name="PressKeyToExit" xml:space="preserve">
|
||||
<value>按任意键退出</value>
|
||||
</data>
|
||||
<data name="GenshinIsRunning" xml:space="preserve">
|
||||
<value>原神正在运行,请关闭后重试 ({0})</value>
|
||||
</data>
|
||||
<data name="AnotherInstance" xml:space="preserve">
|
||||
<value>另一个实例正在运行,请关闭后重试</value>
|
||||
</data>
|
||||
<data name="UpdateNewVersion" xml:space="preserve">
|
||||
<value>有可用更新: {0} => {1}</value>
|
||||
</data>
|
||||
<data name="UpdateDescription" xml:space="preserve">
|
||||
<value>更新内容:
|
||||
{0}</value>
|
||||
</data>
|
||||
<data name="UpdateDownloading" xml:space="preserve">
|
||||
<value>正在下载更新包...</value>
|
||||
</data>
|
||||
<data name="AppBanner" xml:space="preserve">
|
||||
<value>YaeAchievement - 原神成就导出工具 ({0})</value>
|
||||
</data>
|
||||
<data name="UsePreviousData" xml:space="preserve">
|
||||
<value>要使用上一次获取到的成就数据吗? (yes|no)</value>
|
||||
</data>
|
||||
<data name="NetworkError" xml:space="preserve">
|
||||
<value>网络错误: {0}</value>
|
||||
</data>
|
||||
<data name="VcRuntimeDownload" xml:space="preserve">
|
||||
<value>正在下载 Visual C++ Redistributable...</value>
|
||||
</data>
|
||||
<data name="VcRuntimeInstalling" xml:space="preserve">
|
||||
<value>正在安装 Visual C++ Redistributable...</value>
|
||||
</data>
|
||||
<data name="ExceptionNetwork" xml:space="preserve">
|
||||
<value>网络错误,请检查网络后重试 ({0}: {1})</value>
|
||||
</data>
|
||||
<data name="GenshinHashError" xml:space="preserve">
|
||||
<value>请将原神更新至最新版本后重试</value>
|
||||
</data>
|
||||
</root>
|
||||
BIN
res/Updater.exe
Normal file
BIN
res/Updater.exe
Normal file
Binary file not shown.
@@ -5,15 +5,13 @@ using YaeAchievement.AppCenterSDK.Models.Serialization;
|
||||
|
||||
namespace YaeAchievement.AppCenterSDK;
|
||||
|
||||
#pragma warning disable CA1416, CA2211
|
||||
public static class AppCenter {
|
||||
|
||||
private const string LogCache = "./cache/bf18159fb833715i.miko";
|
||||
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;
|
||||
public static Guid? SessionID { get; private set; }
|
||||
public static readonly string DeviceID;
|
||||
public static readonly Device DeviceInfo;
|
||||
private static List<Log> Queue;
|
||||
@@ -40,10 +38,6 @@ public static class AppCenter {
|
||||
});
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
||||
running = false;
|
||||
if (Queue.Count > 0) {
|
||||
Directory.CreateDirectory("cache");
|
||||
File.WriteAllText(LogCache, Queue.ToJson());
|
||||
}
|
||||
};
|
||||
LogSerializer.AddLogType(PageLog.JsonIdentifier, typeof(PageLog));
|
||||
LogSerializer.AddLogType(EventLog.JsonIdentifier, typeof(EventLog));
|
||||
@@ -51,13 +45,6 @@ public static class AppCenter {
|
||||
LogSerializer.AddLogType(ManagedErrorLog.JsonIdentifier, typeof(ManagedErrorLog));
|
||||
LogSerializer.AddLogType(StartServiceLog.JsonIdentifier, typeof(StartServiceLog));
|
||||
LogSerializer.AddLogType(StartSessionLog.JsonIdentifier, typeof(StartSessionLog));
|
||||
if (Directory.Exists("./cache") && File.Exists(LogCache)) {
|
||||
var list = File.ReadAllText(LogCache).FromJson()?.Logs;
|
||||
if (list != null) {
|
||||
Queue.AddRange(list);
|
||||
}
|
||||
File.Delete(LogCache);
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
@@ -98,9 +85,4 @@ public static class AppCenter {
|
||||
private static string ToJson(this IEnumerable<Log> queue) {
|
||||
return LogSerializer.Serialize(new LogContainer(queue));
|
||||
}
|
||||
|
||||
private static LogContainer? FromJson(this string text) {
|
||||
return LogSerializer.DeserializeLogs(text);
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1416, CA2211
|
||||
|
||||
@@ -1,41 +1,62 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using YaeAchievement.res;
|
||||
|
||||
namespace YaeAchievement;
|
||||
|
||||
public class AppConfig {
|
||||
public static class AppConfig {
|
||||
|
||||
[JsonProperty(PropertyName = "location")]
|
||||
public string? Location { get; set; }
|
||||
public static string GamePath { get; private set; } = null!;
|
||||
|
||||
private static AppConfig? _instance;
|
||||
|
||||
private static readonly string FileName = Path.Combine(GlobalVars.AppPath, "conf.json");
|
||||
|
||||
public static string GamePath => _instance!.Location!;
|
||||
|
||||
internal static void Load() {
|
||||
if (File.Exists(FileName)) {
|
||||
var text = File.ReadAllText(FileName);
|
||||
_instance = JsonConvert.DeserializeObject<AppConfig>(text)!;
|
||||
internal static void Load(string argumentPath) {
|
||||
if (argumentPath != "auto" && File.Exists(argumentPath)) {
|
||||
GamePath = argumentPath;
|
||||
return;
|
||||
}
|
||||
if (_instance?.Location == null || !Utils.CheckGamePathValid(_instance.Location)) {
|
||||
var gameInstallPath = Utils.FindGamePathFromRegistry();
|
||||
if (!string.IsNullOrEmpty(gameInstallPath)) {
|
||||
Console.WriteLine($"自动读取到游戏路径: {gameInstallPath}");
|
||||
Console.WriteLine($"如果确认路径无误,请按 Y ;若有误或需要自行选择,请按 N ");
|
||||
var key = Console.ReadKey().Key;
|
||||
gameInstallPath = key == ConsoleKey.Y ? gameInstallPath : Utils.SelectGameExecutable();
|
||||
} else {
|
||||
gameInstallPath = Utils.SelectGameExecutable();
|
||||
var pathCacheFile = new CacheFile("genshin_impact_game_path");
|
||||
if (pathCacheFile.Exists()) {
|
||||
var path = pathCacheFile.Read().Content.ToStringUtf8();
|
||||
if (path != null && File.Exists(path)) {
|
||||
GamePath = path;
|
||||
return;
|
||||
}
|
||||
_instance = new AppConfig {
|
||||
Location = gameInstallPath
|
||||
};
|
||||
Save();
|
||||
}
|
||||
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var cnLogPath = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\原神\output_log.txt");
|
||||
var osLogPath = Path.Combine(appDataPath, @"..\LocalLow\miHoYo\Genshin Impact\output_log.txt");
|
||||
if (!File.Exists(cnLogPath) && !File.Exists(osLogPath)) {
|
||||
throw new ApplicationException(App.ConfigNeedStartGenshin);
|
||||
}
|
||||
string finalLogPath;
|
||||
if (!File.Exists(osLogPath)) {
|
||||
finalLogPath = cnLogPath;
|
||||
} else if (!File.Exists(cnLogPath)) {
|
||||
finalLogPath = osLogPath;
|
||||
} else {
|
||||
var cnLastWriteTime = File.GetLastWriteTime(cnLogPath);
|
||||
var osLastWriteTime = File.GetLastWriteTime(osLogPath);
|
||||
finalLogPath = cnLastWriteTime > osLastWriteTime ? cnLogPath : osLogPath;
|
||||
}
|
||||
GamePath = GetGamePathFromLogFile(finalLogPath)
|
||||
?? GetGamePathFromLogFile($"{finalLogPath}.last")
|
||||
?? throw new ApplicationException(App.ConfigNeedStartGenshin);
|
||||
pathCacheFile.Write(GamePath);
|
||||
}
|
||||
|
||||
public static void Save() {
|
||||
File.WriteAllText(FileName, JsonConvert.SerializeObject(_instance!, Formatting.Indented));
|
||||
|
||||
private static string? GetGamePathFromLogFile(string path) {
|
||||
if (!File.Exists(path)) {
|
||||
return null;
|
||||
}
|
||||
var copiedLogFilePath = Path.GetTempFileName();
|
||||
File.Copy(path, copiedLogFilePath, true);
|
||||
var content = File.ReadAllText(copiedLogFilePath);
|
||||
try {
|
||||
File.Delete(copiedLogFilePath);
|
||||
} catch (Exception) { /* ignore */}
|
||||
var matchResult = Regex.Match(content, @"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)");
|
||||
if (!matchResult.Success) {
|
||||
return null;
|
||||
}
|
||||
var entryName = matchResult.Groups["1"].Value.Replace("_Data", ".exe");
|
||||
return Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ public class CacheFile {
|
||||
public DateTime LastWriteTime => Exists() ? File.GetLastWriteTimeUtc(_cacheName) : DateTime.UnixEpoch;
|
||||
|
||||
public CacheFile(string identifier) {
|
||||
Directory.CreateDirectory(Path.Combine(GlobalVars.AppPath, "cache"));
|
||||
_cacheName = Path.Combine(GlobalVars.AppPath, $"cache/{identifier.MD5Hash()[..16]}.miko");
|
||||
_cacheName = Path.Combine(GlobalVars.CachePath, $"{identifier.MD5Hash()[..16]}.miko");
|
||||
}
|
||||
|
||||
public bool Exists() => File.Exists(_cacheName);
|
||||
@@ -26,14 +25,18 @@ public class CacheFile {
|
||||
return _content;
|
||||
}
|
||||
|
||||
public void Write(byte[] data, string? etag = null) {
|
||||
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);
|
||||
|
||||
private void Write(ByteString data, string hash, string? etag) {
|
||||
using var fOut = File.OpenWrite(_cacheName);
|
||||
using var cOut = new GZipStream(fOut, CompressionLevel.SmallestSize);
|
||||
new CacheItem {
|
||||
Etag = etag ?? string.Empty,
|
||||
Version = 3,
|
||||
Checksum = data.MD5Hash(),
|
||||
Content = ByteString.CopyFrom(data)
|
||||
Checksum = hash,
|
||||
Content = data
|
||||
}.WriteTo(cOut);
|
||||
}
|
||||
}
|
||||
|
||||
122
src/Export.cs
122
src/Export.cs
@@ -1,60 +1,64 @@
|
||||
|
||||
using System.Net;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
using YaeAchievement.res;
|
||||
using static AchievementAllDataNotify.Types.Achievement.Types;
|
||||
|
||||
namespace YaeAchievement;
|
||||
|
||||
public static class Export {
|
||||
|
||||
|
||||
public static uint ExportTo { get; set; } = uint.MaxValue;
|
||||
|
||||
public static void Choose(AchievementAllDataNotify data) {
|
||||
Console.Write("""
|
||||
导出至:
|
||||
[0] 椰羊 (https://cocogoat.work/achievement, 默认)
|
||||
[1] SnapGenshin
|
||||
[2] Paimon.moe
|
||||
[3] Seelie.me
|
||||
[4] 表格文件
|
||||
[5] 原魔工具箱
|
||||
[6] 寻空
|
||||
输入一个数字(0-6):
|
||||
""");
|
||||
if (!int.TryParse(Console.ReadLine(), out var num)) num = 0;
|
||||
((Action<AchievementAllDataNotify>) (num switch {
|
||||
1 => ToSnapGenshin,
|
||||
if (ExportTo == uint.MaxValue) {
|
||||
Console.Write(App.ExportChoose);
|
||||
while (Console.KeyAvailable) {
|
||||
Console.ReadKey(false);
|
||||
}
|
||||
if (!uint.TryParse(Console.ReadLine(), out var num)) num = 0;
|
||||
ExportTo = num;
|
||||
}
|
||||
((Action<AchievementAllDataNotify>) (ExportTo switch {
|
||||
1 => ToHuTao,
|
||||
2 => ToPaimon,
|
||||
3 => ToSeelie,
|
||||
4 => ToCSV,
|
||||
5 => ToWxApp1,
|
||||
6 => ToXunkong,
|
||||
5 => ToXunkong,
|
||||
6 => ToWxApp1,
|
||||
7 => ToRawJson,
|
||||
_ => ToCocogoat
|
||||
})).Invoke(data);
|
||||
}
|
||||
|
||||
private class CocogoatResponse {
|
||||
[JsonPropertyName("key")] public string Code { get; set; } = null!;
|
||||
}
|
||||
|
||||
private static void ToCocogoat(AchievementAllDataNotify data) {
|
||||
var result = JsonConvert.SerializeObject(ExportToUIAFApp(data));
|
||||
var result = JsonSerializer.Serialize(ExportToUIAFApp(data));
|
||||
using var request = new HttpRequestMessage {
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri("https://77.cocogoat.work/v1/memo?source=全部成就"),
|
||||
RequestUri = new Uri($"https://77.cocogoat.work/v1/memo?source={App.AllAchievement}"),
|
||||
Content = new StringContent(result, Encoding.UTF8, "application/json")
|
||||
};
|
||||
using var response = Utils.CHttpClient.Value.Send(request);
|
||||
using var response = Utils.CHttpClient.Send(request);
|
||||
if (response.StatusCode != HttpStatusCode.Created) {
|
||||
Console.WriteLine("导出失败, 请联系开发者以获取帮助");
|
||||
Console.WriteLine(App.ExportToCocogoatFail);
|
||||
return;
|
||||
}
|
||||
dynamic memo = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result)!;
|
||||
Console.WriteLine(Utils.ShellOpen($"https://cocogoat.work/achievement?memo={memo.key}")
|
||||
? "在浏览器内进行下一步操作"
|
||||
: $"https://cocogoat.work/achievement?memo={memo.key}");
|
||||
var responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
var responseJson = JsonSerializer.Deserialize<CocogoatResponse>(responseText)!;
|
||||
Console.WriteLine(Utils.ShellOpen($"https://cocogoat.work/achievement?memo={responseJson.Code}")
|
||||
? App.ExportToCocogoatSuccess
|
||||
: $"https://cocogoat.work/achievement?memo={responseJson.Code}");
|
||||
}
|
||||
|
||||
private static void ToWxApp1(AchievementAllDataNotify data) {
|
||||
var id = Guid.NewGuid().ToString("N").Substring(20, 8);
|
||||
var result = JsonConvert.SerializeObject(new Dictionary<string, object> {
|
||||
var result = JsonSerializer.Serialize(new Dictionary<string, object> {
|
||||
{ "key", id },
|
||||
{ "data", ExportToUIAFApp(data) }
|
||||
});
|
||||
@@ -63,18 +67,14 @@ public static class Export {
|
||||
RequestUri = new Uri("https://api.qyinter.com/achievementRedis"),
|
||||
Content = new StringContent(result, Encoding.UTF8, "application/json")
|
||||
};
|
||||
using var response = Utils.CHttpClient.Value.Send(request);
|
||||
Console.WriteLine($"在小程序导入页面输入以下代码: {id}");
|
||||
using var response = Utils.CHttpClient.Send(request);
|
||||
Console.WriteLine(App.ExportToWxApp1Success, id);
|
||||
}
|
||||
|
||||
private static void ToSnapGenshin(AchievementAllDataNotify data) {
|
||||
if (CheckSnapScheme()) {
|
||||
Utils.CopyToClipboard(JsonConvert.SerializeObject(ExportToUIAFApp(data)));
|
||||
Utils.ShellOpen("snapgenshin://achievement/import/uiaf");
|
||||
Console.WriteLine("在 SnapGenshin 进行下一步操作");
|
||||
} else {
|
||||
Console.WriteLine("更新 SnapGenshin 至最新版本后重试");
|
||||
}
|
||||
private static void ToHuTao(AchievementAllDataNotify data) {
|
||||
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
|
||||
Utils.ShellOpen("hutao://achievement/import");
|
||||
Console.WriteLine(App.ExportToSnapGenshinSuccess);
|
||||
}
|
||||
|
||||
private static void ToPaimon(AchievementAllDataNotify data) {
|
||||
@@ -82,7 +82,7 @@ public static class Export {
|
||||
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.");
|
||||
Console.WriteLine($@"Unable to find {ach.Id} in metadata.");
|
||||
continue;
|
||||
}
|
||||
var map = output.GetValueOrDefault(achInfo.Group, new Dictionary<uint, bool>());
|
||||
@@ -93,8 +93,8 @@ public static class Export {
|
||||
["achievement"] = output.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value)
|
||||
};
|
||||
var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-paimon.json");
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(final));
|
||||
Console.WriteLine($"成就数据已导出至 {path}");
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(final));
|
||||
Console.WriteLine(App.ExportToFileSuccess, path);
|
||||
}
|
||||
|
||||
private static void ToSeelie(AchievementAllDataNotify data) {
|
||||
@@ -108,8 +108,8 @@ public static class Export {
|
||||
["achievements"] = output.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value)
|
||||
};
|
||||
var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-seelie.json");
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(final));
|
||||
Console.WriteLine($"成就数据已导出至 {path}");
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(final));
|
||||
Console.WriteLine(App.ExportToFileSuccess, path);
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
@@ -119,13 +119,13 @@ public static class Export {
|
||||
foreach (var ach in data.List.OrderBy(a => a.Id)) {
|
||||
if (UnusedAchievement.Contains(ach.Id)) continue;
|
||||
if (!info.Items.TryGetValue(ach.Id, out var achInfo) || achInfo == null) {
|
||||
Console.WriteLine($"Unable to find {ach.Id} in metadata.");
|
||||
Console.WriteLine($@"Unable to find {ach.Id} in metadata.");
|
||||
continue;
|
||||
}
|
||||
var finishAt = "";
|
||||
if (ach.Timestamp != 0) {
|
||||
var ts = Convert.ToInt64(ach.Timestamp);
|
||||
finishAt = DateTimeOffset.FromUnixTimeSeconds(ts).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;
|
||||
outList.Add(new List<object> {
|
||||
@@ -140,24 +140,26 @@ public static class Export {
|
||||
}));
|
||||
var path = Path.GetFullPath($"achievement-{DateTime.Now:yyyyMMddHHmmss}.csv");
|
||||
File.WriteAllText(path, $"\uFEFF{string.Join("\n", output)}");
|
||||
Console.WriteLine($"成就数据已导出至 {path}");
|
||||
Console.WriteLine(App.ExportToFileSuccess, path);
|
||||
}
|
||||
|
||||
private static void ToXunkong(AchievementAllDataNotify data) {
|
||||
if (CheckXunkongScheme()) {
|
||||
Utils.CopyToClipboard(JsonConvert.SerializeObject(ExportToUIAFApp(data)));
|
||||
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
|
||||
Utils.ShellOpen("xunkong://import-achievement?caller=YaeAchievement&from=clipboard");
|
||||
Console.WriteLine("在寻空中进行下一步操作");
|
||||
Console.WriteLine(App.ExportToXunkongSuccess);
|
||||
} else {
|
||||
Console.WriteLine("更新寻空至最新版本后重试");
|
||||
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");
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(data, Formatting.Indented));
|
||||
Console.WriteLine($"成就数据已导出至 {path}");
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(data, new JsonSerializerOptions {
|
||||
WriteIndented = true
|
||||
}));
|
||||
Console.WriteLine(App.ExportToFileSuccess, path);
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
@@ -168,13 +170,13 @@ public static class Export {
|
||||
["id"] = ach.Id,
|
||||
["status"] = (uint) ach.Status,
|
||||
["current"] = ach.Current,
|
||||
["timestamp"] = ach.Timestamp,
|
||||
["timestamp"] = ach.Timestamp
|
||||
})
|
||||
.ToList();
|
||||
return new Dictionary<string, object> {
|
||||
["info"] = new Dictionary<string, object> {
|
||||
["export_app"] = "YaeAchievement",
|
||||
["export_timestamp"] = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
|
||||
["export_timestamp"] = DateTimeOffset.Now.ToUnixTimeSeconds(),
|
||||
["export_app_version"] = GlobalVars.AppVersionName,
|
||||
["uiaf_version"] = "v1.1"
|
||||
},
|
||||
@@ -183,10 +185,6 @@ public static class Export {
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
private static bool CheckSnapScheme() {
|
||||
return (string?)Registry.ClassesRoot.OpenSubKey("snapgenshin")?.GetValue("") == "URL:snapgenshin";
|
||||
}
|
||||
|
||||
private static bool CheckXunkongScheme() {
|
||||
return (string?)Registry.ClassesRoot.OpenSubKey("xunkong")?.GetValue("") == "URL:xunkong";
|
||||
}
|
||||
@@ -200,10 +198,10 @@ public static class Export {
|
||||
|
||||
private static string ToDesc(this Status status) {
|
||||
return status switch {
|
||||
Status.Invalid => "未知",
|
||||
Status.Finished => "已完成但未领取奖励",
|
||||
Status.Unfinished => "未完成",
|
||||
Status.RewardTaken => "已完成",
|
||||
Status.Invalid => App.StatusInvalid,
|
||||
Status.Finished => App.StatusFinished,
|
||||
Status.Unfinished => App.StatusUnfinished,
|
||||
Status.RewardTaken => App.StatusRewardTaken,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,20 +5,29 @@ namespace YaeAchievement;
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable ConvertToConstant.Global
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
#pragma warning disable CA2211
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
|
||||
public static class GlobalVars {
|
||||
|
||||
public static bool DebugProxy = false;
|
||||
public static bool CheckGamePath = true;
|
||||
public static bool UnexpectedExit = true;
|
||||
public static Version AppVersion = Assembly.GetEntryAssembly()!.GetName().Version!;
|
||||
public static readonly string AppPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
public static bool DebugProxy => false;
|
||||
public static bool UnexpectedExit { get; set; } = true;
|
||||
public static bool PauseOnExit { get; set; } = true;
|
||||
public static Version AppVersion { get; } = Assembly.GetEntryAssembly()!.GetName().Version!;
|
||||
|
||||
public static readonly string AppPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
private static readonly string CommonData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
||||
public static readonly string DataPath = Path.Combine(CommonData, "Yae");
|
||||
public static readonly string CachePath = Path.Combine(DataPath, "cache");
|
||||
public static readonly string LibFilePath = Path.Combine(DataPath, "YaeAchievement.dll");
|
||||
|
||||
public const uint AppVersionCode = 34;
|
||||
public const string AppVersionName = "2.4.1";
|
||||
|
||||
public const uint AppVersionCode = 29;
|
||||
public const string AppVersionName = "2.1";
|
||||
public const string LibName = "YaeLib.dll";
|
||||
public const string PipeName = "YaeAchievementPipe";
|
||||
public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com";
|
||||
|
||||
|
||||
static GlobalVars() {
|
||||
Directory.CreateDirectory(DataPath);
|
||||
Directory.CreateDirectory(CachePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
using YaeAchievement;
|
||||
using YaeAchievement.AppCenterSDK;
|
||||
using YaeAchievement.AppCenterSDK.Models;
|
||||
using YaeAchievement.res;
|
||||
using static YaeAchievement.Utils;
|
||||
|
||||
InstallExitHook();
|
||||
InstallExceptionHook();
|
||||
|
||||
CheckVcRuntime();
|
||||
CheckIsTempDir();
|
||||
await CheckVcRuntime();
|
||||
CheckSelfIsRunning();
|
||||
TryDisableQuickEdit();
|
||||
InstallExceptionHook();
|
||||
CheckGenshinIsRunning();
|
||||
|
||||
Console.WriteLine("----------------------------------------------------");
|
||||
Console.WriteLine($"YaeAchievement - 原神成就导出工具 ({GlobalVars.AppVersionName})");
|
||||
Console.WriteLine("https://github.com/HolographicHat/YaeAchievement");
|
||||
Console.WriteLine("----------------------------------------------------");
|
||||
Console.WriteLine(@"----------------------------------------------------");
|
||||
Console.WriteLine(App.AppBanner, GlobalVars.AppVersionName);
|
||||
Console.WriteLine(@"https://github.com/HolographicHat/YaeAchievement");
|
||||
Console.WriteLine(@"----------------------------------------------------");
|
||||
|
||||
AppConfig.Load();
|
||||
CheckUpdate();
|
||||
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 = {
|
||||
@@ -26,11 +28,22 @@ new EventLog("AppInit") {
|
||||
{ "SystemVersion", DeviceHelper.GetSystemVersion() }
|
||||
}
|
||||
}.Enqueue();
|
||||
var usePreviousData = false;
|
||||
var historyCache = new CacheFile("ExportData");
|
||||
if (historyCache.LastWriteTime.AddMinutes(10) > DateTime.UtcNow) {
|
||||
Console.WriteLine("使用上一次获取到的成就数据");
|
||||
Console.WriteLine("要重新获取数据,手动删除 cache\\d1a8ef40a67a5929.miko 后重新启动 YaeAchievement");
|
||||
Export.Choose(AchievementAllDataNotify.Parser.ParseFrom(historyCache.Read().Content));
|
||||
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;
|
||||
}
|
||||
Export.Choose(data);
|
||||
} else {
|
||||
StartAndWaitResult(AppConfig.GamePath, str => {
|
||||
GlobalVars.UnexpectedExit = false;
|
||||
|
||||
@@ -22,18 +22,19 @@ public static partial class AchievementAllDataNotifyReflection {
|
||||
static AchievementAllDataNotifyReflection() {
|
||||
byte[] descriptorData = global::System.Convert.FromBase64String(
|
||||
string.Concat(
|
||||
"Ch5BY2hpZXZlbWVudEFsbERhdGFOb3RpZnkucHJvdG8iowIKGEFjaGlldmVt",
|
||||
"ZW50QWxsRGF0YU5vdGlmeRIzCgRsaXN0GAQgAygLMiUuQWNoaWV2ZW1lbnRB",
|
||||
"bGxEYXRhTm90aWZ5LkFjaGlldmVtZW50GtEBCgtBY2hpZXZlbWVudBIKCgJp",
|
||||
"ZBgOIAEoDRI8CgZzdGF0dXMYDSABKA4yLC5BY2hpZXZlbWVudEFsbERhdGFO",
|
||||
"b3RpZnkuQWNoaWV2ZW1lbnQuU3RhdHVzEg8KB2N1cnJlbnQYDCABKA0SDQoF",
|
||||
"dG90YWwYCCABKA0SEQoJdGltZXN0YW1wGAsgASgNIkUKBlN0YXR1cxILCgdJ",
|
||||
"TlZBTElEEAASDgoKVU5GSU5JU0hFRBABEgwKCEZJTklTSEVEEAISEAoMUkVX",
|
||||
"QVJEX1RBS0VOEANiBnByb3RvMw=="));
|
||||
"Ch5BY2hpZXZlbWVudEFsbERhdGFOb3RpZnkucHJvdG8ivwIKGEFjaGlldmVt",
|
||||
"ZW50QWxsRGF0YU5vdGlmeRIzCgRsaXN0GAggAygLMiUuQWNoaWV2ZW1lbnRB",
|
||||
"bGxEYXRhTm90aWZ5LkFjaGlldmVtZW50Gu0BCgtBY2hpZXZlbWVudBIRCgl0",
|
||||
"aW1lc3RhbXAYCCABKA0SDwoHY3VycmVudBgLIAEoDRINCgV0b3RhbBgGIAEo",
|
||||
"DRIKCgJpZBgOIAEoDRI8CgZzdGF0dXMYDyABKA4yLC5BY2hpZXZlbWVudEFs",
|
||||
"bERhdGFOb3RpZnkuQWNoaWV2ZW1lbnQuU3RhdHVzImEKBlN0YXR1cxISCg5T",
|
||||
"VEFUVVNfSU5WQUxJRBAAEhUKEVNUQVRVU19VTkZJTklTSEVEEAESEwoPU1RB",
|
||||
"VFVTX0ZJTklTSEVEEAISFwoTU1RBVFVTX1JFV0FSRF9UQUtFThADYgZwcm90",
|
||||
"bzM="));
|
||||
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
|
||||
new pbr::FileDescriptor[] { },
|
||||
new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::AchievementAllDataNotify), global::AchievementAllDataNotify.Parser, new[]{ "List" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::AchievementAllDataNotify.Types.Achievement), global::AchievementAllDataNotify.Types.Achievement.Parser, new[]{ "Id", "Status", "Current", "Total", "Timestamp" }, null, new[]{ typeof(global::AchievementAllDataNotify.Types.Achievement.Types.Status) }, null, null)})
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::AchievementAllDataNotify), global::AchievementAllDataNotify.Parser, new[]{ "List" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::AchievementAllDataNotify.Types.Achievement), global::AchievementAllDataNotify.Types.Achievement.Parser, new[]{ "Timestamp", "Current", "Total", "Id", "Status" }, null, new[]{ typeof(global::AchievementAllDataNotify.Types.Achievement.Types.Status) }, null, null)})
|
||||
}));
|
||||
}
|
||||
#endregion
|
||||
@@ -85,9 +86,9 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "list" field.</summary>
|
||||
public const int ListFieldNumber = 4;
|
||||
public const int ListFieldNumber = 8;
|
||||
private static readonly pb::FieldCodec<global::AchievementAllDataNotify.Types.Achievement> _repeated_list_codec
|
||||
= pb::FieldCodec.ForMessage(34, global::AchievementAllDataNotify.Types.Achievement.Parser);
|
||||
= pb::FieldCodec.ForMessage(66, global::AchievementAllDataNotify.Types.Achievement.Parser);
|
||||
private readonly pbc::RepeatedField<global::AchievementAllDataNotify.Types.Achievement> list_ = new pbc::RepeatedField<global::AchievementAllDataNotify.Types.Achievement>();
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
@@ -188,7 +189,7 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
|
||||
break;
|
||||
case 34: {
|
||||
case 66: {
|
||||
list_.AddEntriesFrom(input, _repeated_list_codec);
|
||||
break;
|
||||
}
|
||||
@@ -207,7 +208,7 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
|
||||
break;
|
||||
case 34: {
|
||||
case 66: {
|
||||
list_.AddEntriesFrom(ref input, _repeated_list_codec);
|
||||
break;
|
||||
}
|
||||
@@ -255,11 +256,11 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public Achievement(Achievement other) : this() {
|
||||
id_ = other.id_;
|
||||
status_ = other.status_;
|
||||
timestamp_ = other.timestamp_;
|
||||
current_ = other.current_;
|
||||
total_ = other.total_;
|
||||
timestamp_ = other.timestamp_;
|
||||
id_ = other.id_;
|
||||
status_ = other.status_;
|
||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
|
||||
}
|
||||
|
||||
@@ -269,6 +270,42 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
return new Achievement(this);
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "timestamp" field.</summary>
|
||||
public const int TimestampFieldNumber = 8;
|
||||
private uint timestamp_;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public uint Timestamp {
|
||||
get { return timestamp_; }
|
||||
set {
|
||||
timestamp_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "current" field.</summary>
|
||||
public const int CurrentFieldNumber = 11;
|
||||
private uint current_;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public uint Current {
|
||||
get { return current_; }
|
||||
set {
|
||||
current_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "total" field.</summary>
|
||||
public const int TotalFieldNumber = 6;
|
||||
private uint total_;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public uint Total {
|
||||
get { return total_; }
|
||||
set {
|
||||
total_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "id" field.</summary>
|
||||
public const int IdFieldNumber = 14;
|
||||
private uint id_;
|
||||
@@ -282,7 +319,7 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "status" field.</summary>
|
||||
public const int StatusFieldNumber = 13;
|
||||
public const int StatusFieldNumber = 15;
|
||||
private global::AchievementAllDataNotify.Types.Achievement.Types.Status status_ = global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
@@ -293,42 +330,6 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "current" field.</summary>
|
||||
public const int CurrentFieldNumber = 12;
|
||||
private uint current_;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public uint Current {
|
||||
get { return current_; }
|
||||
set {
|
||||
current_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "total" field.</summary>
|
||||
public const int TotalFieldNumber = 8;
|
||||
private uint total_;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public uint Total {
|
||||
get { return total_; }
|
||||
set {
|
||||
total_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "timestamp" field.</summary>
|
||||
public const int TimestampFieldNumber = 11;
|
||||
private uint timestamp_;
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public uint Timestamp {
|
||||
get { return timestamp_; }
|
||||
set {
|
||||
timestamp_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public override bool Equals(object other) {
|
||||
@@ -344,11 +345,11 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
if (ReferenceEquals(other, this)) {
|
||||
return true;
|
||||
}
|
||||
if (Id != other.Id) return false;
|
||||
if (Status != other.Status) return false;
|
||||
if (Timestamp != other.Timestamp) return false;
|
||||
if (Current != other.Current) return false;
|
||||
if (Total != other.Total) return false;
|
||||
if (Timestamp != other.Timestamp) return false;
|
||||
if (Id != other.Id) return false;
|
||||
if (Status != other.Status) return false;
|
||||
return Equals(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
@@ -356,11 +357,11 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public override int GetHashCode() {
|
||||
int hash = 1;
|
||||
if (Id != 0) hash ^= Id.GetHashCode();
|
||||
if (Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) hash ^= Status.GetHashCode();
|
||||
if (Timestamp != 0) hash ^= Timestamp.GetHashCode();
|
||||
if (Current != 0) hash ^= Current.GetHashCode();
|
||||
if (Total != 0) hash ^= Total.GetHashCode();
|
||||
if (Timestamp != 0) hash ^= Timestamp.GetHashCode();
|
||||
if (Id != 0) hash ^= Id.GetHashCode();
|
||||
if (Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) hash ^= Status.GetHashCode();
|
||||
if (_unknownFields != null) {
|
||||
hash ^= _unknownFields.GetHashCode();
|
||||
}
|
||||
@@ -380,25 +381,25 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
output.WriteRawMessage(this);
|
||||
#else
|
||||
if (Total != 0) {
|
||||
output.WriteRawTag(64);
|
||||
output.WriteRawTag(48);
|
||||
output.WriteUInt32(Total);
|
||||
}
|
||||
if (Timestamp != 0) {
|
||||
output.WriteRawTag(88);
|
||||
output.WriteRawTag(64);
|
||||
output.WriteUInt32(Timestamp);
|
||||
}
|
||||
if (Current != 0) {
|
||||
output.WriteRawTag(96);
|
||||
output.WriteRawTag(88);
|
||||
output.WriteUInt32(Current);
|
||||
}
|
||||
if (Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) {
|
||||
output.WriteRawTag(104);
|
||||
output.WriteEnum((int) Status);
|
||||
}
|
||||
if (Id != 0) {
|
||||
output.WriteRawTag(112);
|
||||
output.WriteUInt32(Id);
|
||||
}
|
||||
if (Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) {
|
||||
output.WriteRawTag(120);
|
||||
output.WriteEnum((int) Status);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(output);
|
||||
}
|
||||
@@ -410,25 +411,25 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
|
||||
if (Total != 0) {
|
||||
output.WriteRawTag(64);
|
||||
output.WriteRawTag(48);
|
||||
output.WriteUInt32(Total);
|
||||
}
|
||||
if (Timestamp != 0) {
|
||||
output.WriteRawTag(88);
|
||||
output.WriteRawTag(64);
|
||||
output.WriteUInt32(Timestamp);
|
||||
}
|
||||
if (Current != 0) {
|
||||
output.WriteRawTag(96);
|
||||
output.WriteRawTag(88);
|
||||
output.WriteUInt32(Current);
|
||||
}
|
||||
if (Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) {
|
||||
output.WriteRawTag(104);
|
||||
output.WriteEnum((int) Status);
|
||||
}
|
||||
if (Id != 0) {
|
||||
output.WriteRawTag(112);
|
||||
output.WriteUInt32(Id);
|
||||
}
|
||||
if (Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) {
|
||||
output.WriteRawTag(120);
|
||||
output.WriteEnum((int) Status);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(ref output);
|
||||
}
|
||||
@@ -439,11 +440,8 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public int CalculateSize() {
|
||||
int size = 0;
|
||||
if (Id != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Id);
|
||||
}
|
||||
if (Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Status);
|
||||
if (Timestamp != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Timestamp);
|
||||
}
|
||||
if (Current != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Current);
|
||||
@@ -451,8 +449,11 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
if (Total != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Total);
|
||||
}
|
||||
if (Timestamp != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Timestamp);
|
||||
if (Id != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeUInt32Size(Id);
|
||||
}
|
||||
if (Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Status);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
size += _unknownFields.CalculateSize();
|
||||
@@ -466,11 +467,8 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
if (other == null) {
|
||||
return;
|
||||
}
|
||||
if (other.Id != 0) {
|
||||
Id = other.Id;
|
||||
}
|
||||
if (other.Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) {
|
||||
Status = other.Status;
|
||||
if (other.Timestamp != 0) {
|
||||
Timestamp = other.Timestamp;
|
||||
}
|
||||
if (other.Current != 0) {
|
||||
Current = other.Current;
|
||||
@@ -478,8 +476,11 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
if (other.Total != 0) {
|
||||
Total = other.Total;
|
||||
}
|
||||
if (other.Timestamp != 0) {
|
||||
Timestamp = other.Timestamp;
|
||||
if (other.Id != 0) {
|
||||
Id = other.Id;
|
||||
}
|
||||
if (other.Status != global::AchievementAllDataNotify.Types.Achievement.Types.Status.Invalid) {
|
||||
Status = other.Status;
|
||||
}
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
|
||||
}
|
||||
@@ -496,26 +497,26 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
|
||||
break;
|
||||
case 64: {
|
||||
case 48: {
|
||||
Total = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 88: {
|
||||
case 64: {
|
||||
Timestamp = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 96: {
|
||||
case 88: {
|
||||
Current = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 104: {
|
||||
Status = (global::AchievementAllDataNotify.Types.Achievement.Types.Status) input.ReadEnum();
|
||||
break;
|
||||
}
|
||||
case 112: {
|
||||
Id = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 120: {
|
||||
Status = (global::AchievementAllDataNotify.Types.Achievement.Types.Status) input.ReadEnum();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -531,26 +532,26 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
default:
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
|
||||
break;
|
||||
case 64: {
|
||||
case 48: {
|
||||
Total = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 88: {
|
||||
case 64: {
|
||||
Timestamp = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 96: {
|
||||
case 88: {
|
||||
Current = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 104: {
|
||||
Status = (global::AchievementAllDataNotify.Types.Achievement.Types.Status) input.ReadEnum();
|
||||
break;
|
||||
}
|
||||
case 112: {
|
||||
Id = input.ReadUInt32();
|
||||
break;
|
||||
}
|
||||
case 120: {
|
||||
Status = (global::AchievementAllDataNotify.Types.Achievement.Types.Status) input.ReadEnum();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -562,10 +563,10 @@ public sealed partial class AchievementAllDataNotify : pb::IMessage<AchievementA
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public static partial class Types {
|
||||
public enum Status {
|
||||
[pbr::OriginalName("INVALID")] Invalid = 0,
|
||||
[pbr::OriginalName("UNFINISHED")] Unfinished = 1,
|
||||
[pbr::OriginalName("FINISHED")] Finished = 2,
|
||||
[pbr::OriginalName("REWARD_TAKEN")] RewardTaken = 3,
|
||||
[pbr::OriginalName("STATUS_INVALID")] Invalid = 0,
|
||||
[pbr::OriginalName("STATUS_UNFINISHED")] Unfinished = 1,
|
||||
[pbr::OriginalName("STATUS_FINISHED")] Finished = 2,
|
||||
[pbr::OriginalName("STATUS_REWARD_TAKEN")] RewardTaken = 3,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
296
src/Utils.cs
296
src/Utils.cs
@@ -3,52 +3,68 @@ using System.Diagnostics;
|
||||
using System.IO.Pipes;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32;
|
||||
using YaeAchievement.AppCenterSDK;
|
||||
using YaeAchievement.res;
|
||||
using YaeAchievement.Win32;
|
||||
using static YaeAchievement.Win32.OpenFileFlags;
|
||||
|
||||
namespace YaeAchievement;
|
||||
|
||||
public static class Utils {
|
||||
|
||||
public static readonly Lazy<HttpClient> CHttpClient = new (() => {
|
||||
var c = new HttpClient(new HttpClientHandler {
|
||||
Proxy = GlobalVars.DebugProxy ? new WebProxy("http://127.0.0.1:8888") : null,
|
||||
AutomaticDecompression = DecompressionMethods.Brotli | DecompressionMethods.GZip
|
||||
}) {
|
||||
DefaultRequestHeaders = {
|
||||
UserAgent = {
|
||||
new ProductInfoHeaderValue("YaeAchievement", GlobalVars.AppVersion.ToString(2))
|
||||
}
|
||||
public static readonly HttpClient CHttpClient = new (new HttpClientHandler {
|
||||
Proxy = GlobalVars.DebugProxy ? new WebProxy("http://127.0.0.1:8888") : null,
|
||||
AutomaticDecompression = DecompressionMethods.Brotli | DecompressionMethods.GZip
|
||||
}) {
|
||||
DefaultRequestHeaders = {
|
||||
UserAgent = {
|
||||
new ProductInfoHeaderValue("YaeAchievement", GlobalVars.AppVersion.ToString(2))
|
||||
}
|
||||
};
|
||||
return c;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public static byte[] GetBucketFileAsByteArray(string path, bool cache = true) {
|
||||
using var msg = new HttpRequestMessage {
|
||||
Method = HttpMethod.Get,
|
||||
RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}")
|
||||
};
|
||||
var cacheFile = new CacheFile(path);
|
||||
if (cache && cacheFile.Exists()) {
|
||||
msg.Headers.TryAddWithoutValidation("If-None-Match", $"{cacheFile.Read().Etag}");
|
||||
try {
|
||||
using var msg = new HttpRequestMessage {
|
||||
Method = HttpMethod.Get,
|
||||
RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}")
|
||||
};
|
||||
var cacheFile = new CacheFile(path);
|
||||
if (cache && cacheFile.Exists()) {
|
||||
msg.Headers.TryAddWithoutValidation("If-None-Match", $"{cacheFile.Read().Etag}");
|
||||
}
|
||||
using var response = CHttpClient.Send(msg);
|
||||
if (cache && response.StatusCode == HttpStatusCode.NotModified) {
|
||||
return cacheFile.Read().Content.ToByteArray();
|
||||
}
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseBytes = response.Content.ReadAsByteArrayAsync().Result;
|
||||
if (cache) {
|
||||
var etag = response.Headers.ETag!.Tag;
|
||||
cacheFile.Write(responseBytes, etag);
|
||||
}
|
||||
return responseBytes;
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine(App.NetworkError, e.Message);
|
||||
Environment.Exit(-1);
|
||||
return null!;
|
||||
}
|
||||
using var response = CHttpClient.Value.Send(msg);
|
||||
if (cache && response.StatusCode == HttpStatusCode.NotModified) {
|
||||
return cacheFile.Read().Content.ToByteArray();
|
||||
}
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseBytes = response.Content.ReadAsByteArrayAsync().Result;
|
||||
if (cache) {
|
||||
var etag = response.Headers.ETag!.Tag;
|
||||
cacheFile.Write(responseBytes, etag);
|
||||
}
|
||||
return responseBytes;
|
||||
}
|
||||
|
||||
public static T? GetOrNull<T>(this T[] array, uint index) where T : class {
|
||||
return array.Length > index ? array[index] : null;
|
||||
}
|
||||
|
||||
public static uint? ToUIntOrNull(string? value) {
|
||||
return value != null ? uint.TryParse(value, out var result) ? result : null : null;
|
||||
}
|
||||
|
||||
public static bool ToBooleanOrFalse(string? value) {
|
||||
return value != null && bool.TryParse(value, out var result) && result;
|
||||
}
|
||||
|
||||
public static void CopyToClipboard(string text) {
|
||||
if (Native.OpenClipboard(IntPtr.Zero)) {
|
||||
Native.EmptyClipboard();
|
||||
@@ -64,99 +80,66 @@ public static class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void CheckUpdate() {
|
||||
public static void CheckUpdate(bool useLocalLib) {
|
||||
var info = UpdateInfo.Parser.ParseFrom(GetBucketFileAsByteArray("schicksal/version"))!;
|
||||
if (GlobalVars.AppVersionCode != info.VersionCode) {
|
||||
Console.WriteLine($"有可用更新: {GlobalVars.AppVersionName} => {info.VersionName}");
|
||||
Console.WriteLine($"更新内容: \n{info.Description}");
|
||||
Console.WriteLine(App.UpdateNewVersion, GlobalVars.AppVersionName, info.VersionName);
|
||||
Console.WriteLine(App.UpdateDescription, info.Description);
|
||||
if (info.EnableAutoDownload) {
|
||||
Console.WriteLine("正在下载更新包...");
|
||||
var fullPath = Path.GetFullPath($"update.{Path.GetExtension(info.PackageLink)}");
|
||||
File.WriteAllBytes(fullPath, GetBucketFileAsByteArray(info.PackageLink));
|
||||
Console.WriteLine("关闭程序后, 将压缩包解压至当前目录即可完成更新.");
|
||||
ShellOpen(fullPath);
|
||||
Console.WriteLine(App.UpdateDownloading);
|
||||
var tmpPath = Path.GetTempFileName();
|
||||
File.WriteAllBytes(tmpPath, GetBucketFileAsByteArray(info.PackageLink));
|
||||
var updaterArgs = $"{Environment.ProcessId}|{Environment.ProcessPath}|{tmpPath}";
|
||||
var updaterPath = Path.Combine(GlobalVars.DataPath, "update.exe");
|
||||
var updaterHash = App.Updater.MD5Hash();
|
||||
if (!File.Exists(updaterPath) || File.ReadAllBytes(updaterPath).MD5Hash() != updaterHash) {
|
||||
File.WriteAllBytes(updaterPath, App.Updater);
|
||||
}
|
||||
ShellOpen(updaterPath, updaterArgs.ToBytes().ToBase64());
|
||||
GlobalVars.PauseOnExit = false;
|
||||
Environment.Exit(0);
|
||||
}
|
||||
Console.WriteLine($"下载地址: {info.PackageLink}");
|
||||
Console.WriteLine(App.DownloadLink, info.PackageLink);
|
||||
if (info.ForceUpdate) {
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
if (info.EnableLibDownload) {
|
||||
File.WriteAllBytes(GlobalVars.LibName, GetBucketFileAsByteArray("schicksal/lib.dll"));
|
||||
if (useLocalLib) {
|
||||
Console.WriteLine(@"[DEBUG] Use local native lib.");
|
||||
File.Copy(Path.Combine(GlobalVars.AppPath, "YaeAchievementLib.dll"), GlobalVars.LibFilePath, true);
|
||||
} else if (info.EnableLibDownload) {
|
||||
File.WriteAllBytes(GlobalVars.LibFilePath, GetBucketFileAsByteArray("schicksal/lib.dll"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void CheckSelfIsRunning() {
|
||||
Process.EnterDebugMode();
|
||||
var cur = Process.GetCurrentProcess();
|
||||
foreach (var process in Process.GetProcesses().Where(process => process.Id != cur.Id)) {
|
||||
if (process.ProcessName == cur.ProcessName) {
|
||||
Console.WriteLine("另一个实例正在运行,请关闭后重试");
|
||||
Console.WriteLine(App.AnotherInstance);
|
||||
Environment.Exit(302);
|
||||
}
|
||||
}
|
||||
Process.LeaveDebugMode();
|
||||
}
|
||||
|
||||
public static void CheckIsTempDir() {
|
||||
if (GlobalVars.AppPath.Contains(Path.GetTempPath())) {
|
||||
Console.WriteLine("请将程序完整解压后再运行");
|
||||
Environment.Exit(303);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShellOpen(string path) {
|
||||
return new Process {
|
||||
StartInfo = {
|
||||
|
||||
// ReSharper disable once UnusedMethodReturnValue.Global
|
||||
public static bool ShellOpen(string path, string? args = null) {
|
||||
try {
|
||||
var startInfo = new ProcessStartInfo {
|
||||
FileName = path,
|
||||
UseShellExecute = true
|
||||
};
|
||||
if (args != null) {
|
||||
startInfo.Arguments = args;
|
||||
}
|
||||
}.Start();
|
||||
}
|
||||
|
||||
public static bool CheckGamePathValid(string? path) {
|
||||
if (path == null) return false;
|
||||
var dir = Path.GetDirectoryName(path)!;
|
||||
return !GlobalVars.CheckGamePath || File.Exists(Path.Combine(dir, "UnityPlayer.dll"));
|
||||
}
|
||||
|
||||
public static string SelectGameExecutable() {
|
||||
var fnPtr = Marshal.AllocHGlobal(32768);
|
||||
Native.RtlZeroMemory(fnPtr, 32768);
|
||||
var ofn = new OpenFileName {
|
||||
file = fnPtr,
|
||||
size = Marshal.SizeOf<OpenFileName>(),
|
||||
owner = Native.GetConsoleWindow(),
|
||||
flags = Explorer | NoNetworkButton | FileMustExist | NoChangeDir,
|
||||
title = "选择主程序",
|
||||
filter = "国服/国际服主程序 (YuanShen/GenshinImpact.exe)\0YuanShen.exe;GenshinImpact.exe\0",
|
||||
maxFile = 32768
|
||||
};
|
||||
new Thread(() => {
|
||||
var handle = Native.FindWindow("#32770", "选择主程序");
|
||||
while (handle == IntPtr.Zero) {
|
||||
handle = Native.FindWindow("#32770", "选择主程序");
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
var currentThreadId = Native.GetCurrentThreadId();
|
||||
var foregroundThreadId = Native.GetWindowThreadProcessId(Native.GetForegroundWindow(), out _);
|
||||
Native.AttachThreadInput(currentThreadId, foregroundThreadId, true);
|
||||
Native.SetWindowPos(handle, new IntPtr(-1), 0, 0, 0, 0, 1 | 2);
|
||||
Native.SetForegroundWindow(handle);
|
||||
Native.AttachThreadInput(currentThreadId, foregroundThreadId, false);
|
||||
}).Start();
|
||||
if(!Native.GetOpenFileName(ofn)) {
|
||||
var err = Native.CommDlgExtendedError();
|
||||
if (err != 0) {
|
||||
throw new SystemException($"Dialog error: {err}");
|
||||
}
|
||||
Console.WriteLine("操作被取消");
|
||||
Environment.Exit(0);
|
||||
return new Process {
|
||||
StartInfo = startInfo
|
||||
}.Start();
|
||||
} catch (Exception) {
|
||||
return false;
|
||||
}
|
||||
var path = Marshal.PtrToStringAuto(fnPtr)!;
|
||||
Marshal.FreeHGlobal(fnPtr);
|
||||
return path;
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMethodReturnValue.Global
|
||||
@@ -168,8 +151,11 @@ public static class Utils {
|
||||
public static void CheckGenshinIsRunning() {
|
||||
Process.EnterDebugMode();
|
||||
foreach (var process in Process.GetProcesses()) {
|
||||
if (process.ProcessName is "GenshinImpact" or "YuanShen" && !process.HasExited) {
|
||||
Console.WriteLine($"原神正在运行,请关闭后重试 ({process.Id})");
|
||||
if (process.ProcessName is "GenshinImpact" or "YuanShen"
|
||||
&& !process.HasExited
|
||||
&& process.MainWindowHandle != IntPtr.Zero
|
||||
) {
|
||||
Console.WriteLine(App.GenshinIsRunning, process.Id);
|
||||
Environment.Exit(301);
|
||||
}
|
||||
}
|
||||
@@ -182,43 +168,73 @@ public static class Utils {
|
||||
public static void InstallExitHook() {
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
||||
proc?.Kill();
|
||||
Console.WriteLine("按任意键退出");
|
||||
Console.ReadKey();
|
||||
if (GlobalVars.PauseOnExit) {
|
||||
Console.WriteLine(App.PressKeyToExit);
|
||||
Console.ReadKey();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void InstallExceptionHook() {
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => {
|
||||
Console.WriteLine(e.ExceptionObject.ToString());
|
||||
Console.WriteLine("正在上报错误信息...");
|
||||
AppCenter.TrackCrash((Exception) e.ExceptionObject);
|
||||
AppCenter.Upload();
|
||||
var ex = e.ExceptionObject;
|
||||
switch (ex) {
|
||||
case ApplicationException ex1:
|
||||
Console.WriteLine(ex1.Message);
|
||||
break;
|
||||
case SocketException ex2:
|
||||
Console.WriteLine(App.ExceptionNetwork, nameof(SocketException), ex2.Message);
|
||||
break;
|
||||
case HttpRequestException ex3:
|
||||
Console.WriteLine(App.ExceptionNetwork, nameof(HttpRequestException), ex3.Message);
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine(ex.ToString());
|
||||
Console.WriteLine(App.UploadError);
|
||||
AppCenter.TrackCrash((Exception) e.ExceptionObject);
|
||||
AppCenter.Upload();
|
||||
break;
|
||||
}
|
||||
Environment.Exit(-1);
|
||||
};
|
||||
}
|
||||
|
||||
private static bool CheckGenshinIsLatestVersion(string path) {
|
||||
#if DEBUG
|
||||
return true;
|
||||
#else
|
||||
return File.Exists(path) && File.ReadAllBytes(path).MD5Hash()
|
||||
is "34433aa962523e55213c596d4e6b1f9c"
|
||||
or "1fa8e1445b8121d5d1b5c1e6a8daa905"; // TODO: Use api
|
||||
#endif
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMethodReturnValue.Global
|
||||
public static Thread StartAndWaitResult(string exePath, Func<string, bool> onReceive) {
|
||||
const string lib = "C:/ProgramData/yae.dll";
|
||||
File.Copy(Path.GetFullPath(GlobalVars.LibName), lib, true);
|
||||
if (!CheckGenshinIsLatestVersion(exePath)) {
|
||||
Console.WriteLine(App.GenshinHashError);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
||||
File.Delete(lib);
|
||||
try {
|
||||
File.Delete(GlobalVars.LibFilePath);
|
||||
} catch (Exception) { /* ignored */ }
|
||||
};
|
||||
if (!Injector.CreateProcess(exePath, out var hProcess, out var hThread, out var pid)) {
|
||||
Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("ICreateProcess fail"));
|
||||
}
|
||||
if (Injector.LoadLibraryAndInject(hProcess, lib) != 0) {
|
||||
if (Injector.LoadLibraryAndInject(hProcess, GlobalVars.LibFilePath) != 0) {
|
||||
if (!Native.TerminateProcess(hProcess, 0)) {
|
||||
Environment.Exit(new Win32Exception().PrintMsgAndReturnErrCode("TerminateProcess fail"));
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"原神正在启动 ({pid})");
|
||||
Console.WriteLine(App.GameLoading, pid);
|
||||
proc = Process.GetProcessById(Convert.ToInt32(pid));
|
||||
proc.EnableRaisingEvents = true;
|
||||
proc.Exited += (_, _) => {
|
||||
if (GlobalVars.UnexpectedExit) {
|
||||
proc = null;
|
||||
Console.WriteLine("游戏进程异常退出");
|
||||
Console.WriteLine(App.GameProcessExit);
|
||||
Environment.Exit(114514);
|
||||
}
|
||||
};
|
||||
@@ -253,45 +269,35 @@ public static class Utils {
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
/// <summary>
|
||||
/// 从注册表中寻找安装路径 暂时只支持国服
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string? FindGamePathFromRegistry() {
|
||||
try {
|
||||
using var root = Registry.LocalMachine;
|
||||
using var sub = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\原神");
|
||||
if (sub == null) {
|
||||
return null;
|
||||
}
|
||||
var installLocation = sub.GetValue("InstallPath")?.ToString();
|
||||
if (!string.IsNullOrEmpty(installLocation)) {
|
||||
var folder = Path.Combine(installLocation, "Genshin Impact Game\\");
|
||||
var exePath = Path.Combine(folder, "YuanShen.exe");
|
||||
if (File.Exists(Path.Combine(folder, "UnityPlayer.dll")) && File.Exists(exePath)) {
|
||||
return exePath;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void CheckVcRuntime() {
|
||||
public static async Task CheckVcRuntime() {
|
||||
using var root = Registry.LocalMachine;
|
||||
using var sub = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall")!;
|
||||
var installed = sub.GetSubKeyNames()
|
||||
.Select(subKeyName => sub.OpenSubKey(subKeyName))
|
||||
.Select(subKeyName => {
|
||||
try {
|
||||
return sub.OpenSubKey(subKeyName);
|
||||
} catch (Exception) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.Select(item => item?.GetValue("DisplayName") as string ?? string.Empty)
|
||||
.Any(name => name.Contains("Microsoft Visual C++ 2022 X64 "));
|
||||
if (!installed) {
|
||||
const string vcDownloadUrl = "https://aka.ms/vs/17/release/vc_redist.x64.exe";
|
||||
Console.WriteLine("未安装 VcRuntime");
|
||||
Console.WriteLine($"下载地址: {vcDownloadUrl}");
|
||||
Console.WriteLine("安装完成后,重新打开 YaeAchievement");
|
||||
ShellOpen(vcDownloadUrl);
|
||||
Environment.Exit(303);
|
||||
Console.WriteLine(App.VcRuntimeDownload);
|
||||
var pkgPath = Path.Combine(GlobalVars.DataPath, "vc_redist.x64.exe");
|
||||
var bytes = await CHttpClient.GetByteArrayAsync("https://aka.ms/vs/17/release/vc_redist.x64.exe");
|
||||
await File.WriteAllBytesAsync(pkgPath, bytes);
|
||||
Console.WriteLine(App.VcRuntimeInstalling);
|
||||
using var process = new Process {
|
||||
StartInfo = {
|
||||
FileName = pkgPath,
|
||||
Arguments = "/install /passive /norestart"
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
await process.WaitForExitAsync();
|
||||
File.Delete(pkgPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public static class Native {
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr CreateRemoteThread(
|
||||
IntPtr hProcess,
|
||||
IntPtr lpThreadAttributes,
|
||||
@@ -71,9 +71,6 @@ public static class Native {
|
||||
out IntPtr lpThreadId
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr GetConsoleWindow();
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private const int STD_INPUT_HANDLE = -10;
|
||||
|
||||
@@ -86,12 +83,6 @@ public static class Native {
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool SetConsoleMode(IntPtr handle, int ioMode);
|
||||
|
||||
[DllImport("comdlg32.dll", SetLastError = true)]
|
||||
public static extern int CommDlgExtendedError();
|
||||
|
||||
[DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool GetOpenFileName([In, Out] OpenFileName ofn);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GlobalLock(IntPtr mem);
|
||||
|
||||
@@ -110,43 +101,19 @@ public static class Native {
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr SetClipboardData(uint uFormat, IntPtr data);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool EmptyClipboard();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern void RtlZeroMemory(IntPtr dst, ulong length);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern uint GetCurrentThreadId();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
[DllImport("gdi32.dll", SetLastError = true)]
|
||||
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetDC(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hdc);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern uint WaitForSingleObject(IntPtr handle, ulong dwMilliseconds);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace YaeAchievement.Win32;
|
||||
|
||||
[Flags]
|
||||
public enum OpenFileFlags : uint {
|
||||
Explorer = 0x00080000,
|
||||
NoChangeDir = 0x00000008,
|
||||
FileMustExist = 0x00001000,
|
||||
NoNetworkButton = 0x00020000,
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable FieldCanBeMadeReadOnly.Global
|
||||
|
||||
namespace YaeAchievement.Win32;
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct OpenFileName {
|
||||
public int size;
|
||||
public IntPtr owner;
|
||||
public IntPtr instance;
|
||||
public string filter;
|
||||
public IntPtr customFilter;
|
||||
public int maxCustFilter;
|
||||
public int filterIndex;
|
||||
public IntPtr file;
|
||||
public int maxFile;
|
||||
public IntPtr fileTitle;
|
||||
public int maxFileTitle;
|
||||
public string initialDir;
|
||||
public string title;
|
||||
public OpenFileFlags flags;
|
||||
public short fileOffset;
|
||||
public short fileExtension;
|
||||
public string defExt;
|
||||
public IntPtr custData;
|
||||
public IntPtr hook;
|
||||
public string templateName;
|
||||
public IntPtr reservedPtr;
|
||||
public int reservedInt;
|
||||
public int flagsEx;
|
||||
}
|
||||
Reference in New Issue
Block a user