27 Commits
3.1.0 ... 3.6.0

Author SHA1 Message Date
HolographicHat
9cab7e8702 4.6 2024-04-24 23:26:24 +08:00
HolographicHat
1f080fe084 Merge pull request #98 from Anong0u0/patch 2024-04-22 14:27:29 +08:00
Anong0u0
c8497243c0 Fix GamePathRegex match to case insensitive 2024-04-22 13:44:20 +08:00
HolographicHat
9abdd123ee #93
remove appcenter
2024-04-02 15:06:05 +08:00
HolographicHat
e1429289ad refactor appcenter 2024-03-15 00:08:34 +08:00
HolographicHat
1f311ed987 4.5 2024-03-14 23:55:36 +08:00
HolographicHat
cc346915e3 Merge pull request #85 from BTMuli/master
️ 延长刷新间隔,Y/YES均可使用旧数据(大小写不敏感)
2024-02-06 23:27:16 +08:00
HolographicHat
cd0f49d83d shorten code 2024-02-06 23:19:41 +08:00
目棃
d0b7d15894 Merge branch 'master' into master 2024-02-06 22:54:51 +08:00
目棃
504c8a2a9a 👔 默认不使用旧数据 2024-02-06 22:46:37 +08:00
HolographicHat
b3162052da refactor 2024-02-06 21:31:14 +08:00
目棃
034d999d25 ️ 延长刷新间隔,回车/Y也能使用旧数据了 2024-02-02 19:30:03 +08:00
HolographicHat
45d5620e83 4.4 2024-01-31 12:46:31 +08:00
HolographicHat
fa13f9c8e5 fix 2023-12-25 00:21:48 +08:00
HolographicHat
2210a97d61 4.3 2023-12-24 23:59:24 +08:00
HolographicHat
feb7ac44da 4.2 2023-11-09 21:39:05 +08:00
HolographicHat
3924129560 Merge pull request #74 from Lightczx/master
Disable marshalling
2023-10-30 14:04:24 +08:00
Lightczx
4f7f0cdfd2 disable marshalling 2023-10-30 13:51:01 +08:00
HolographicHat
cf0753c676 Merge pull request #73 from BTMuli/master
🐛 修复 TeyvatGuide 链接错误
2023-10-16 13:42:22 +08:00
BTMuli
0b895d47ca 🐛 修复 TeyvatGuide 链接错误 2023-10-16 12:39:32 +08:00
HolographicHat
78d2722e20 Merge pull request #72 from BTMuli/master
更新文档
2023-10-15 01:16:19 +08:00
BTMuli
385c673323 🚨 revert,删除多余空格 2023-10-15 00:31:18 +08:00
BTMuli
50beb2cce7 🚨 修复 CI 报错 2023-10-15 00:18:39 +08:00
BTMuli
324a4153e0 📝 删除混入其中的表格文件 2023-10-14 23:56:24 +08:00
BTMuli
3de459aceb 📝 更新部分信息 2023-10-14 23:54:49 +08:00
BTMuli
295bb89177 📝 更新导出说明 2023-10-14 23:50:31 +08:00
HolographicHat
baaf4e8227 rollback 2023-10-13 21:22:54 +08:00
44 changed files with 194 additions and 830 deletions

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build

View File

@@ -9,9 +9,22 @@
- 支持导出所有类别的成就
- 支持官服,渠道服与国际服
- 支持导出至[椰羊](https://cocogoat.work/achievement)、[Snap·HuTao](https://github.com/DGP-Studio/Snap.HuTao)、[Paimon.moe](https://paimon.moe/achievement/)、[Seelie.me](https://seelie.me/achievements)、[寻空](https://github.com/xunkong/xunkong)和表格文件(csv)
- 没有窗口大小、游戏语言等要求
## 导出支持
> 按照数字键选择导出方式,<kbd>0</kbd> 为默认导出方式
0. [椰羊](https://cocogoat.work/achievement)
1. [胡桃工具箱](https://github.com/DGP-Studio/Snap.HuTao)
2. [Paimon.moe](https://paimon.moe/achievement/)
3. [Seelie.me](https://seelie.me/achievements)
4. 表格文件 `.csv`
5. [寻空](https://github.com/xunkong/xunkong)
6. [原魔工具箱](https://apps.apple.com/app/id1663989619)
7. [TeyvatGuide](https://github.com/BTMuli/TeyvatGuide)
8. [UIAF](https://uigf.org/standards/UIAF.html) JSON 文件
## 使用说明
→ [Tutorial.md](Tutorial.md)

View File

@@ -13,9 +13,22 @@
- Support for exporting all categories of achievements
- Supports all versions of Genshin Impact
- Support for exporting to [Cocogoat](https://cocogoat.work/achievement), [Snap·HuTao](https://github.com/DGP-Studio/Snap.HuTao), [Paimon.moe](https://paimon.moe/achievement/), [Seelie.me](https://seelie.me/achievements)、[XunKong](https://github.com/xunkong/xunkong) and form files (csv)
- There are no requirements for window size, game language, etc.
## Export support
> Select the export method according to the number keys, <kbd>0</kbd> is the default export method
0. [Cocogoat](https://cocogoat.work/achievement)
1. [Snap HuTao](https://github.com/DGP-Studio/Snap.HuTao)
2. [Paimon.moe](https://paimon.moe/achievement/)
3. [Seelie.me](https://seelie.me/achievements)
4. Form File `.csv`
5. [XunKong](https://github.com/xunkong/xunkong)
6. [YuanmoTools](https://apps.apple.com/app/id1663989619)
7. [TeyvatGuide](https://github.com/BTMuli/TeyvatGuide)
8. [UIAF](https://uigf.org/standards/UIAF.html) JSON file
## Instructions for Use:
→ [Tutorial_EN.md](Tutorial_EN.md)

View File

@@ -71,11 +71,11 @@
### 各种工具的介绍烦请移步至各工具的官方页面进行查看(下方序号对应导出序号)
0. [椰羊](https://cocogoat.work/achievement)
1. [Snap·HuTao](https://github.com/DGP-Studio/Snap.HuTao)
1. [胡桃工具箱](https://github.com/DGP-Studio/Snap.HuTao)
2. [Paimon.moe](https://paimon.moe/achievement/)
3. [Seelie.me](https://seelie.me/achievements)
4. [寻空](https://github.com/xunkong/xunkong)
4. ~~表格文件 `.csv`~~
5. [寻空](https://github.com/xunkong/xunkong)
6. [原魔工具箱](https://apps.apple.com/app/id1663989619)
7. [TeyvatGuide](https://github.com/BTMuli/TeyvatGuide)
8. [UIAF](https://uigf.org/standards/UIAF.html) JSON 文件

View File

@@ -65,9 +65,12 @@ At this time, you can select the Achievements in the left column to view the imp
### For the introduction of different tools, please visit the official page of each tool to see:
1. [Snap·HuTao](https://github.com/DGP-Studio/Snap.HuTao)
0. [Cocogoat](https://cocogoat.work/achievement)
1. [Snap HuTao](https://github.com/DGP-Studio/Snap.HuTao)
2. [Paimon.moe](https://paimon.moe/achievement/)
3. [Seelie.me](https://seelie.me/achievements)
4. ~~Form File `.csv`~~
5. [XunKong](https://github.com/xunkong/xunkong)
6. [YuanmoTools](https://apps.apple.com/app/id1663989619)
7. [Teyvat Guide](https://github.com/BTMuli/TeyvatGuide)
8. [UIAF](https://uigf.org/standards/UIAF.html) JSON file

View File

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

View File

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

View File

@@ -1,70 +1,47 @@
#include "pch.h"
// ReSharper disable CppCStyleCast
// ReSharper disable CppInconsistentNaming
// ReSharper disable CppClangTidyModernizeUseStdPrint
// ReSharper disable CppClangTidyClangDiagnosticCastAlign
// ReSharper disable CppClangTidyHicppMultiwayPathsCovered
// ReSharper disable CppDefaultCaseNotHandledInSwitchStatement
// ReSharper disable CppClangTidyClangDiagnosticCastFunctionTypeStrict
#include "pch.h"
#include "util.h"
#include "il2cpp-init.h"
using Genshin::ByteArray, Genshin::ClientKcpEvent, Genshin::KcpPacket, Genshin::KcpEventType;
using std::to_string;
using Genshin::ByteArray;
HWND unityWnd = nullptr;
HANDLE hPipe = nullptr;
// Allow Protocol: GetPlayerTokenRsp, PlayerLoginRsp, AchievementAllDataNotify, PingRsp
std::set<UINT16> PacketWhitelist = { 1347, 4424, 20342, 25731 };
bool OnPacket(KcpPacket* pkt) {
if (pkt->data == nullptr) return true;
auto len = pkt->length;
auto data = (ByteArray*)new BYTE[len + 32];
data->max_length = len;
memcpy(data->vector, pkt->data, len);
Genshin::XorEncrypt(&data, len, nullptr);
if (ReadMapped<UINT16>(data->vector, 0) != 0x4567) {
delete[] data;
return true;
}
if (!PacketWhitelist.contains(ReadMapped<UINT16>(data->vector, 2))) {
//ifdef _DEBUG
printf("Blocked cmdid: %d\n", ReadMapped<UINT16>(data->vector, 2));
//endif
delete[] data;
return false;
}
printf("Passed cmdid: %d\n", ReadMapped<UINT16>(data->vector, 2));
if (ReadMapped<UINT16>(data->vector, 2) == 20342) {
const auto headLength = ReadMapped<UINT16>(data->vector, 4);
const auto dataLength = ReadMapped<UINT32>(data->vector, 6);
const auto cStr = base64_encode(data->vector + 10 + headLength, dataLength) + "\n";
WriteFile(hPipe, cStr.c_str(), cStr.length(), nullptr, nullptr);
CloseHandle(hPipe);
auto manager = Genshin::GetSingletonInstance(Genshin::GetSingletonManager(), il2cpp_string_new("GameManager"));
Genshin::ForceQuit(manager);
}
delete[] data;
return true;
}
std::string checksum;
namespace Hook {
void SetVersion(void* obj, Il2CppString* value, void* method) {
const auto version = ToString(value);
value = string_new(version + " YaeAchievement");
CALL_ORIGIN(SetVersion, obj, value, method);
}
bool KcpRecv(void* client, ClientKcpEvent* evt, void* method) {
const auto result = CALL_ORIGIN(KcpRecv, client, evt, method);
if (result == 0 || evt->fields.type != KcpEventType::EventRecvMsg) {
return result;
ByteArray* UnityEngine_RecordUserData(const INT type) {
if (type == 0) {
const auto arr = new ByteArray {};
const auto len = checksum.length();
arr->max_length = len;
memcpy(&arr->vector[0], checksum.data(), len);
return arr;
}
return OnPacket(evt->fields.packet) ? result : false;
}
ByteArray* UnityEngine_RecordUserData(INT type) {
return new ByteArray {};
}
// 不再使用checksum(?
uint16_t BitConverter_ToUInt16(ByteArray* val, const int startIndex) {
const auto ret = CALL_ORIGIN(BitConverter_ToUInt16, val, startIndex);
if (ret == 0xAB89 && ReadMapped<UINT16>(val->vector, 2) == 1655) {
const auto headLength = ReadMapped<UINT16>(val->vector, 4);
const auto dataLength = ReadMapped<UINT32>(val->vector, 6);
const auto cStr = base64_encode(val->vector + 10 + headLength, dataLength) + "\n";
WriteFile(hPipe, cStr.c_str(), (DWORD) cStr.length(), nullptr, nullptr);
CloseHandle(hPipe);
ExitProcess(0);
}
return ret;
}
}
void Run(HMODULE* phModule) {
@@ -76,8 +53,11 @@ void Run(HMODULE* phModule) {
Sleep(5000);
DisableVMProtect();
InitIL2CPP();
HookManager::install(Genshin::KcpRecv, Hook::KcpRecv);
HookManager::install(Genshin::SetVersion, Hook::SetVersion);
for (int i = 0; i < 3; i++) {
const auto result = Genshin::RecordUserData(i);
checksum += string(reinterpret_cast<char*>(&result->vector[0]), result->max_length);
}
HookManager::install(Genshin::BitConverter_ToUInt16, Hook::BitConverter_ToUInt16);
HookManager::install(Genshin::UnityEngine_RecordUserData, Hook::UnityEngine_RecordUserData);
hPipe = CreateFile(R"(\\.\pipe\YaeAchievementPipe)", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (hPipe == INVALID_HANDLE_VALUE) {

View File

@@ -1 +0,0 @@
DO_API(0x9D1AC0, 0x9CADC0, Il2CppString*, il2cpp_string_new, (const char* str));

View File

@@ -1,20 +1,19 @@
// ReSharper disable CppClangTidyBugproneMacroParentheses
#pragma once
#include "il2cpp-types.h"
// IL2CPP APIs
#define DO_API(ca, oa, r, n, p) extern r (*n) p
#include "il2cpp-api-functions.h"
#undef DO_API
// Application-specific functions
#define DO_APP_FUNC(ca, oa, r, n, p) extern r (*n) p
#define DO_UNI_FUNC(ca, oa, r, n, p) extern r (*n) p
namespace Genshin {
#include "il2cpp-functions.h"
}
#undef DO_UNI_FUNC
#undef DO_APP_FUNC
#define DO_UNI_FUNC(ca, oa, r, n, p) extern r (*n) p
#define DO_TYPEDEF(ca, oa, n) extern n##__Class **n##__TypeInfo
namespace Genshin {
#include "il2cpp-unity-functions.h"
#include "il2cpp-types-ptr.h"
}
#undef DO_UNI_FUNC
#undef DO_TYPEDEF

View File

@@ -2,14 +2,10 @@ using namespace Genshin;
// DO_APP_FUNC(CN_OFFSET, OS_OFFSET, RETURN, FUNC_NAME, (ARGS...));
DO_APP_FUNC(0x258fd40, 0x2548e50, void, SetVersion, (void* obj, Il2CppString* value, void* method));
DO_APP_FUNC(0x57A390, 0x579D00, LPVOID, il2cpp_object_new, (LPVOID t));
DO_APP_FUNC(0x3220f00, 0x31c1650, void, XorEncrypt, (ByteArray** data, int length, void* method));
DO_APP_FUNC(0x07315A30, 0x0710F580, ByteArray*, RecordUserData, (int32_t nType));
DO_APP_FUNC(0x12f5df0, 0x12ddd80, bool, KcpRecv, (void* client, ClientKcpEvent* evt, void* method));
DO_APP_FUNC(0x0D257150, 0x0D244830, uint16_t, BitConverter_ToUInt16, (ByteArray* val, int startIndex));
DO_APP_FUNC(0x264ee90, 0x2606720, VOID, ForceQuit, (LPVOID obj));
DO_APP_FUNC(0x624d630, 0x61bd630, LPVOID, GetSingletonManager, ());
DO_APP_FUNC(0x624d360, 0x61bd360, LPVOID, GetSingletonInstance, (LPVOID obj, Il2CppString* value));
DO_UNI_FUNC(0x10CF80, 0x10CF80, ByteArray*, UnityEngine_RecordUserData, (int32_t nType));

View File

@@ -1,22 +1,22 @@
// ReSharper disable CppClangTidyBugproneMacroParentheses
#include "pch.h"
#include "il2cpp-init.h"
#define DO_API(ca, oa, r, n, p) r (*n) p
#include "il2cpp-api-functions.h"
#undef DO_API
#define DO_APP_FUNC(ca, oa, r, n, p) r (*n) p
#define DO_UNI_FUNC(ca, oa, r, n, p) r (*n) p
namespace Genshin {
#include "il2cpp-functions.h"
}
#undef DO_UNI_FUNC
#undef DO_APP_FUNC
#define DO_UNI_FUNC(ca, oa, r, n, p) r (*n) p
#define DO_TYPEDEF(ca, oa, n) n##__Class **n##__TypeInfo
namespace Genshin {
#include "il2cpp-unity-functions.h"
#include "il2cpp-types-ptr.h"
}
#undef DO_UNI_FUNC
#undef DO_TYPEDEF
using std::string;
@@ -27,13 +27,12 @@ void InitIL2CPP() {
auto hBase = GetModuleHandle("UserAssembly.dll");
auto bAddr = (UINT64)hBase;
auto cAddr = (UINT64)GetModuleHandle("UnityPlayer.dll");
#define DO_API(ca, oa, r, n, p) n = (r (*) p)(bAddr + (isCN ? ca : oa))
#include "il2cpp-api-functions.h"
#undef DO_API
#define DO_APP_FUNC(ca, oa, r, n, p) n = (r (*) p)(bAddr + (isCN ? ca : oa))
#include "il2cpp-functions.h"
#undef DO_APP_FUNC
#define DO_UNI_FUNC(ca, oa, r, n, p) n = (r (*) p)(cAddr + (isCN ? ca : oa))
#include "il2cpp-unity-functions.h"
#include "il2cpp-functions.h"
#undef DO_UNI_FUNC
#undef DO_APP_FUNC
#define DO_TYPEDEF(ca, oa, n) n##__TypeInfo = (n##__Class **)(bAddr + (isCN ? ca : oa))
#include "il2cpp-types-ptr.h"
#undef DO_TYPEDEF
}

View File

View File

@@ -1,3 +1,6 @@
// ReSharper disable CppClangTidyClangDiagnosticReservedIdentifier
// ReSharper disable CppClangTidyBugproneReservedIdentifier
#pragma once
#pragma region IL2CPPInternalTypes
@@ -36,30 +39,7 @@ namespace Genshin {
il2cpp_array_size_t max_length;
uint8_t vector[32];
};
struct KcpPacket {
BYTE* data;
UINT32 length;
struct CodedOutputStream__Class {
};
enum class KcpEventType : int {
EventNotSet = -1,
EventConnect = 0,
EventConnectFailed = 1,
EventDisconnect = 2,
EventRecvMsg = 3,
EventCount = 4,
};
struct KcpEvent_Fields {
KcpEventType type;
UINT32 token;
UINT32 data;
KcpPacket* packet;
};
struct ClientKcpEvent {
KcpEvent_Fields fields;
};
}

View File

@@ -1,3 +0,0 @@
using namespace Genshin;
DO_UNI_FUNC(0x103420, 0x103420, ByteArray*, UnityEngine_RecordUserData, (int32_t nType));

View File

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

View File

@@ -9,13 +9,13 @@ message Achievement {
FINISHED = 2;
REWARD_TAKEN = 3;
}
uint32 timestamp = 3;
uint32 current = 14;
uint32 total = 8;
uint32 timestamp = 13;
uint32 current = 6;
uint32 total = 9;
uint32 id = 1;
Status status = 13;
Status status = 2;
}
message AchievementAllDataNotify {
repeated Achievement list = 2;
repeated Achievement list = 11;
}

View File

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

View File

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

View File

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

View File

@@ -1,51 +0,0 @@
using System.Globalization;
using System.Reflection;
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
public class Device {
[JsonProperty(PropertyName = "sdkName")]
public string SdkName { get; set; } = "appcenter.wpf.netcore";
[JsonProperty(PropertyName = "sdkVersion")]
public string SdkVersion { get; set; } = "4.5.0";
[JsonProperty(PropertyName = "osName")]
public string OsName { get; set; } = "WINDOWS";
[JsonProperty(PropertyName = "osVersion")]
public string OsVersion { get; set; } = DeviceHelper.GetSystemVersion();
[JsonProperty(PropertyName = "osBuild")]
public string OsBuild { get; set; } = $"{DeviceHelper.GetSystemVersion()}.{DeviceHelper.GetSystemBuild()}";
[JsonProperty(PropertyName = "model")]
public string? Model { get; set; } = DeviceHelper.GetModel();
[JsonProperty(PropertyName = "oemName")]
public string? OemName { get; set; } = DeviceHelper.GetOem();
[JsonProperty(PropertyName = "screenSize")]
public string ScreenSize { get; set; } = DeviceHelper.GetScreenSize();
[JsonProperty(PropertyName = "carrierCountry")]
public string Country { get; set; } = DeviceHelper.GetCountry() ?? "CN";
[JsonProperty(PropertyName = "locale")]
public string Locale { get; set; } = CultureInfo.CurrentCulture.Name;
[JsonProperty(PropertyName = "timeZoneOffset")]
public int TimeZoneOffset { get; set; } = (int) TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes;
[JsonProperty(PropertyName = "appVersion")]
public string AppVersion { get; set; } = GlobalVars.AppVersionName;
[JsonProperty(PropertyName = "appBuild")]
public string AppBuild { get; set; } = GlobalVars.AppVersionCode.ToString();
[JsonProperty(PropertyName = "appNamespace")]
public string AppNamespace { get; set; } = Assembly.GetEntryAssembly()?.EntryPoint?.DeclaringType?.Namespace ?? string.Empty;
}

View File

@@ -1,20 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
[JsonObject(JsonIdentifier)]
public class EventLog : LogWithProperties {
public const string JsonIdentifier = "event";
public EventLog(string name) {
Name = name;
}
[JsonProperty(PropertyName = "id")]
private Guid Id { get; set; } = Guid.NewGuid();
[JsonProperty(PropertyName = "name")]
private string Name { get; set; }
}

View File

@@ -1,21 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
[JsonObject(JsonIdentifier)]
public class HandledErrorLog : LogWithProperties {
public const string JsonIdentifier = "handledError";
public HandledErrorLog(MException exception) {
Id = Guid.NewGuid();
Exception = exception;
}
[JsonProperty(PropertyName = "id")]
public Guid? Id { get; set; }
[JsonProperty(PropertyName = "exception")]
public MException Exception { get; set; }
}

View File

@@ -1,23 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
public abstract class Log {
[JsonProperty(PropertyName = "sid")]
private Guid? Session { get; set; } = AppCenter.SessionID;
[JsonProperty(PropertyName = "timestamp")]
private DateTime Timestamp { get; set; } = DateTime.UtcNow;
[JsonProperty(PropertyName = "device")]
private Device Device { get; set; } = AppCenter.DeviceInfo;
[JsonIgnore]
internal LogStatus Status = LogStatus.Pending;
}
public enum LogStatus {
Pending, Uploading, Uploaded
}

View File

@@ -1,14 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
public class LogContainer {
public LogContainer(IEnumerable<Log> logs) {
Logs = logs;
}
[JsonProperty(PropertyName = "logs")]
public IEnumerable<Log> Logs { get; set; }
}

View File

@@ -1,19 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
public class LogUploadResult {
[JsonProperty(PropertyName = "status")]
public string Status { get; set; } = null!;
[JsonProperty(PropertyName = "validDiagnosticsIds")]
public Guid[] ValidDiagnosticsIds { get; set; } = Array.Empty<Guid>();
[JsonProperty(PropertyName = "throttledDiagnosticsIds")]
public Guid[] ThrottledDiagnosticsIds { get; set; } = Array.Empty<Guid>();
[JsonProperty(PropertyName = "correlationId")]
public Guid CorrelationId { get; set; }
}

View File

@@ -1,10 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
public class LogWithProperties : Log {
[JsonProperty(PropertyName = "properties")]
public IDictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
}

View File

@@ -1,22 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
public class MException {
[JsonProperty(PropertyName = "type")]
public string Type { get; set; } = "UnknownType";
[JsonProperty(PropertyName = "message")]
public string? Message { get; set; }
[JsonProperty(PropertyName = "stackTrace")]
public string? StackTrace { get; set; }
[JsonProperty(PropertyName = "frames")]
public IList<StackFrame>? Frames { get; set; }
[JsonProperty(PropertyName = "innerExceptions")]
public IList<MException>? InnerExceptions { get; set; }
}

View File

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

View File

@@ -1,17 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
[JsonObject(JsonIdentifier)]
public class PageLog : LogWithProperties {
public const string JsonIdentifier = "page";
public PageLog(string name) {
Name = name;
}
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
}

View File

@@ -1,60 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
#pragma warning disable CS8604, CS8765
public class LogJsonConverter : JsonConverter {
private readonly Dictionary<string, Type> _logTypes = new ();
private readonly object _jsonConverterLock = new ();
private static readonly JsonSerializerSettings SerializationSettings;
static LogJsonConverter() {
SerializationSettings = new JsonSerializerSettings {
Formatting = Formatting.None,
NullValueHandling = NullValueHandling.Ignore,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
};
}
public void AddLogType(string typeName, Type type) {
lock (_jsonConverterLock) {
_logTypes[typeName] = type;
}
}
public override bool CanConvert(Type objectType) {
return typeof(Log).IsAssignableFrom(objectType);
}
public override object? ReadJson(JsonReader reader, Type t, object o, JsonSerializer s) {
Type logType;
var jsonObject = JObject.Load(reader);
var typeName = jsonObject.GetValue("type")?.ToString();
lock (_jsonConverterLock) {
if (typeName == null || !_logTypes.ContainsKey(typeName)) {
throw new JsonReaderException("Could not identify type of log");
}
logType = _logTypes[typeName];
jsonObject.Remove("type");
}
return jsonObject.ToObject(logType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
var info = value.GetType().GetTypeInfo();
if (info.GetCustomAttribute(typeof(JsonObjectAttribute)) is not JsonObjectAttribute attribute) {
throw new JsonWriterException("Log type is missing JsonObjectAttribute");
}
var jsonObject = JObject.FromObject(value, JsonSerializer.CreateDefault(SerializationSettings));
jsonObject.Add("type", JToken.FromObject(attribute.Id));
writer.WriteRawValue(jsonObject.ToString(Formatting.None));
}
}

View File

@@ -1,40 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
#pragma warning disable CS8604, CS8765, CS8603
public static class LogSerializer {
private static readonly JsonSerializerSettings SerializationSettings;
private static readonly LogJsonConverter Converter = new ();
static LogSerializer() {
SerializationSettings = new JsonSerializerSettings {
Formatting = Formatting.None,
NullValueHandling = NullValueHandling.Ignore,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
Converters = { Converter }
};
}
public static void AddLogType(string typeName, Type type) {
Converter.AddLogType(typeName, type);
}
public static string Serialize(LogContainer logContainer) {
return JsonConvert.SerializeObject(logContainer, SerializationSettings);
}
public static string Serialize(Log log) {
return JsonConvert.SerializeObject(log, SerializationSettings);
}
public static LogContainer? DeserializeLogs(string json) {
return JsonConvert.DeserializeObject<LogContainer>(json, SerializationSettings);
}
}

View File

@@ -1,34 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
public class StackFrame {
public StackFrame(string address, string code, string className, string methodName, int? lineNumber, string fileName) {
Address = address;
Code = code;
ClassName = className;
MethodName = methodName;
LineNumber = lineNumber;
FileName = fileName;
}
[JsonProperty(PropertyName = "address")]
public string Address { get; set; }
[JsonProperty(PropertyName = "code")]
public string Code { get; set; }
[JsonProperty(PropertyName = "className")]
public string ClassName { get; set; }
[JsonProperty(PropertyName = "methodName")]
public string MethodName { get; set; }
[JsonProperty(PropertyName = "lineNumber")]
public int? LineNumber { get; set; }
[JsonProperty(PropertyName = "fileName")]
public string FileName { get; set; }
}

View File

@@ -1,17 +0,0 @@
using Newtonsoft.Json;
namespace YaeAchievement.AppCenterSDK.Models;
[JsonObject(JsonIdentifier)]
public class StartServiceLog : Log {
public const string JsonIdentifier = "startService";
public StartServiceLog(params string[] services) {
Services = services;
}
[JsonProperty(PropertyName = "services")]
public string[] Services { get; set; }
}

View File

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

View File

@@ -3,7 +3,7 @@ using YaeAchievement.res;
namespace YaeAchievement;
public static class AppConfig {
public static partial class AppConfig {
public static string GamePath { get; private set; } = null!;
@@ -52,11 +52,15 @@ public static class AppConfig {
try {
File.Delete(copiedLogFilePath);
} catch (Exception) { /* ignore */}
var matchResult = Regex.Match(content, @"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)");
var matchResult = GamePathRegex().Match(content);
if (!matchResult.Success) {
return null;
}
var entryName = matchResult.Groups["1"].Value.Replace("_Data", ".exe");
return Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
}
[GeneratedRegex(@"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)", RegexOptions.IgnoreCase)]
private static partial Regex GamePathRegex();
}

View File

@@ -4,17 +4,13 @@ using Proto;
namespace YaeAchievement;
public class CacheFile {
public class CacheFile(string identifier) {
private readonly string _cacheName;
private readonly string _cacheName = Path.Combine(GlobalVars.CachePath, $"{identifier.MD5Hash()[..16]}.miko");
private CacheItem? _content;
public DateTime LastWriteTime => Exists() ? File.GetLastWriteTimeUtc(_cacheName) : DateTime.UnixEpoch;
public CacheFile(string identifier) {
_cacheName = Path.Combine(GlobalVars.CachePath, $"{identifier.MD5Hash()[..16]}.miko");
}
public bool Exists() => File.Exists(_cacheName);
public CacheItem Read() {

View File

@@ -6,7 +6,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Win32;
using Proto;
using YaeAchievement.AppCenterSDK;
using YaeAchievement.res;
using static Proto.Achievement.Types;
@@ -16,6 +15,10 @@ public static class Export {
public static uint ExportTo { get; set; } = uint.MaxValue;
private static readonly JsonSerializerOptions JsonOpts = new () {
WriteIndented = true
};
public static void Choose(AchievementAllDataNotify data) {
if (ExportTo == uint.MaxValue) {
Console.Write(App.ExportChoose);
@@ -40,16 +43,15 @@ public static class Export {
}
private class CocogoatResponse {
[JsonPropertyName("key")] public string Code { get; set; } = null!;
[JsonPropertyName("key")] public string Code { get; init; } = null!;
}
private static void ToCocogoat(AchievementAllDataNotify data) {
var result = JsonSerializer.Serialize(ExportToUIAFApp(data));
using var request = new HttpRequestMessage {
Method = HttpMethod.Post,
RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}"),
Content = new StringContent(result, Encoding.UTF8, "application/json")
};
using var request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}");
request.Content = new StringContent(result, Encoding.UTF8, "application/json");
using var response = Utils.CHttpClient.Send(request);
if (response.StatusCode != HttpStatusCode.Created) {
Console.WriteLine(App.ExportToCocogoatFail);
@@ -68,11 +70,10 @@ public static class Export {
{ "key", id },
{ "data", ExportToUIAFApp(data) }
});
using var request = new HttpRequestMessage {
Method = HttpMethod.Post,
RequestUri = new Uri("https://api.qyinter.com/achievementRedis"),
Content = new StringContent(result, Encoding.UTF8, "application/json")
};
using var request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.RequestUri = new Uri("https://api.qyinter.com/achievementRedis");
request.Content = new StringContent(result, Encoding.UTF8, "application/json");
using var response = Utils.CHttpClient.Send(request);
Console.WriteLine(App.ExportToWxApp1Success, id);
}
@@ -100,7 +101,7 @@ public static class Export {
}
private static void ToTeyvatGuide(AchievementAllDataNotify data) {
if (Process.GetProcessesByName("TeyvatGuide").Any()) {
if (Process.GetProcessesByName("TeyvatGuide").Length != 0) {
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
Utils.ShellOpen("teyvatguide://import_uigf?app=YaeAchievement");
Console.WriteLine(App.ExportToTauriSuccess);
@@ -171,10 +172,10 @@ public static class Export {
finishAt = DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
}
var current = ach.Status != Status.Unfinished ? ach.Current == 0 ? ach.Total : ach.Current : ach.Current;
outList.Add(new List<object> {
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
outList.Add([
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
achInfo.Description, current, ach.Total, finishAt
});
]);
}
var output = new List<string> { "ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间" };
output.AddRange(outList.OrderBy(v => v[2]).Select(item => {
@@ -190,9 +191,7 @@ public static class Export {
private static void ToRawJson(AchievementAllDataNotify data) {
var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-raw.json");
var text = JsonSerializer.Serialize(data, new JsonSerializerOptions {
WriteIndented = true
});
var text = JsonSerializer.Serialize(data, JsonOpts);
if (TryWriteToFile(path, text)) {
Console.WriteLine(App.ExportToFileSuccess, path);
}
@@ -220,6 +219,7 @@ public static class Export {
};
}
// ReSharper disable once InconsistentNaming
private static bool CheckWinUIAppScheme(string protocol) {
return (string?)Registry.ClassesRoot.OpenSubKey(protocol)?.GetValue("") == $"URL:{protocol}";
}
@@ -228,7 +228,7 @@ public static class Export {
return string.Join(separator, list);
}
private static readonly List<uint> UnusedAchievement = new() { 84517 };
private static readonly List<uint> UnusedAchievement = [ 84517 ];
private static string ToDesc(this Status status) {
return status switch {
@@ -248,7 +248,6 @@ public static class Export {
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
// ReSharper disable once LocalizableElement
Console.WriteLine($"{msg}: {ex.Message}");
AppCenter.TrackCrash(ex, false);
return ex.NativeErrorCode;
}

View File

@@ -20,8 +20,8 @@ public static class GlobalVars {
public static readonly string CachePath = Path.Combine(DataPath, "cache");
public static readonly string LibFilePath = Path.Combine(DataPath, "YaeAchievement.dll");
public const uint AppVersionCode = 41;
public const string AppVersionName = "3.1";
public const uint AppVersionCode = 46;
public const string AppVersionName = "3.6";
public const string PipeName = "YaeAchievementPipe";
public const string BucketHost = "https://cn-cd-1259389942.file.myqcloud.com";

View File

@@ -45,7 +45,7 @@ public static class Injector {
return new Win32Exception().PrintMsgAndReturnErrCode("WriteProcessMemory fail");
}
}
var lpStartAddress = pLoadLibrary.CreateDelegate<LPTHREAD_START_ROUTINE>();
var lpStartAddress = (delegate* unmanaged[Stdcall]<void*, uint>)pLoadLibrary.Value; //THREAD_START_ROUTINE
var hThread = Native.CreateRemoteThread(hProc, default, 0, lpStartAddress, pBase, 0);
if (hThread.IsNull) {
var e = new Win32Exception();

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"className": "Native",
"allowMarshaling": true,
"allowMarshaling": false,
"public": true
}

View File

@@ -1,21 +1,21 @@
CreateProcess
CloseClipboard
CreateProcess
CreateRemoteThread
EmptyClipboard
GetConsoleMode
GetDC
GetDeviceCaps
GetModuleHandle
GetProcAddress
VirtualAllocEx
WriteProcessMemory
CreateRemoteThread
VirtualFreeEx
WaitForSingleObject
OpenClipboard
EmptyClipboard
GlobalLock
SetClipboardData
GlobalUnlock
CloseClipboard
GetStdHandle
GetConsoleMode
GlobalLock
GlobalUnlock
OpenClipboard
ResumeThread
SetClipboardData
SetConsoleMode
TerminateProcess
ResumeThread
GetDeviceCaps
GetDC
VirtualAllocEx
VirtualFreeEx
WaitForSingleObject
WriteProcessMemory

View File

@@ -1,7 +1,5 @@
using Proto;
using YaeAchievement;
using YaeAchievement.AppCenterSDK;
using YaeAchievement.AppCenterSDK.Models;
using YaeAchievement.res;
using static YaeAchievement.Utils;
@@ -22,36 +20,27 @@ AppConfig.Load(args.GetOrNull(0) ?? "auto");
Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue;
CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2)));
AppCenter.Init();
new EventLog("AppInit") {
Properties = {
{ "AppVersion", GlobalVars.AppVersionName },
{ "SystemVersion", DeviceHelper.GetSystemVersion() }
}
}.Enqueue();
var usePreviousData = false;
var historyCache = new CacheFile("ExportData");
if (historyCache.LastWriteTime.AddMinutes(10) > DateTime.UtcNow) {
AchievementAllDataNotify? data = null;
try {
data = AchievementAllDataNotify.Parser.ParseFrom(historyCache.Read().Content);
} catch (Exception) { /* ignored */ }
if (historyCache.LastWriteTime.AddMinutes(60) > DateTime.UtcNow && data != null) {
Console.WriteLine(App.UsePreviousData);
usePreviousData = Console.ReadLine() == "yes";
}
Export:
if(usePreviousData) {
AchievementAllDataNotify data;
try {
data = AchievementAllDataNotify.Parser.ParseFrom(historyCache.Read().Content);
} catch (Exception) {
usePreviousData = false;
goto Export;
if (Console.ReadLine()?.ToUpper() is "Y" or "YES") {
Export.Choose(data);
return;
}
Export.Choose(data);
} else {
StartAndWaitResult(AppConfig.GamePath, str => {
GlobalVars.UnexpectedExit = false;
var data = Convert.FromBase64String(str);
var list = AchievementAllDataNotify.Parser.ParseFrom(data);
historyCache.Write(data);
Export.Choose(list);
return true;
});
}
}
StartAndWaitResult(AppConfig.GamePath, str => {
GlobalVars.UnexpectedExit = false;
var bytes = Convert.FromBase64String(str);
var list = AchievementAllDataNotify.Parser.ParseFrom(bytes);
historyCache.Write(bytes);
Export.Choose(list);
return true;
});

View File

@@ -11,7 +11,6 @@ using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Console;
using Proto;
using YaeAchievement.AppCenterSDK;
using YaeAchievement.res;
namespace YaeAchievement;
@@ -31,10 +30,9 @@ public static class Utils {
public static byte[] GetBucketFileAsByteArray(string path, bool cache = true) {
try {
using var msg = new HttpRequestMessage {
Method = HttpMethod.Get,
RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}")
};
using var msg = new HttpRequestMessage();
msg.Method = HttpMethod.Get;
msg.RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}");
var cacheFile = new CacheFile(path);
if (cache && cacheFile.Exists()) {
msg.Headers.TryAddWithoutValidation("If-None-Match", $"{cacheFile.Read().Etag}");
@@ -73,11 +71,11 @@ public static class Utils {
if (Native.OpenClipboard(HWND.Null))
{
Native.EmptyClipboard();
var hGlobal = (HANDLE) Marshal.AllocHGlobal((text.Length + 1) * 2);
var hGlobal = (HGLOBAL) Marshal.AllocHGlobal((text.Length + 1) * 2);
var hPtr = (nint) Native.GlobalLock(hGlobal);
Marshal.Copy(text.ToCharArray(), 0, hPtr, text.Length);
Native.GlobalUnlock(hPtr);
Native.SetClipboardData(13, hGlobal);
Native.GlobalUnlock((HGLOBAL) hPtr);
Native.SetClipboardData(13, new HANDLE(hPtr));
Marshal.FreeHGlobal(hGlobal);
Native.CloseClipboard();
}
@@ -201,9 +199,6 @@ public static class Utils {
break;
default:
Console.WriteLine(ex.ToString());
Console.WriteLine(App.UploadError);
AppCenter.TrackCrash((Exception) e.ExceptionObject);
AppCenter.Upload();
break;
}
Environment.Exit(-1);
@@ -312,12 +307,9 @@ public static class Utils {
}
await File.WriteAllBytesAsync(pkgPath, bytes);
Console.WriteLine(App.VcRuntimeInstalling);
using var process = new Process {
StartInfo = {
FileName = pkgPath,
Arguments = "/install /passive /norestart"
}
};
using var process = new Process();
process.StartInfo.FileName = pkgPath;
process.StartInfo.Arguments = "/install /passive /norestart";
process.Start();
await process.WaitForExitAsync();
File.Delete(pkgPath);