diff --git a/Vanessa/Players/Tencent.cs b/Vanessa/Players/Tencent.cs
index 6a5f3ee..bff3a0d 100644
--- a/Vanessa/Players/Tencent.cs
+++ b/Vanessa/Players/Tencent.cs
@@ -1,16 +1,155 @@
using System;
+using System.Diagnostics;
+using System.Text;
using Kxnrl.Vanessa.Models;
+using Kxnrl.Vanessa.Win32Api;
namespace Kxnrl.Vanessa.Players;
internal sealed class Tencent : IMusicPlayer
{
+ private const string CurrentSOngInfoPattern
+ = "B9 ? ? ? ? C7 05 ? ? ? ? ? ? ? ? E8 ? ? ? ? B9";
+
+ private const string AlbumThumbnailPattern
+ = "A1 ? ? ? ? 6A ? 51 8B CC 51 89 01 8B C4 8D 4D ? C7 00 ? ? ? ? FF 15 ? ? ? ? A1 ? ? ? ? 6A ? 51 8B CC 51 89 01 8B C4 8D 4D ? C7 00 ? ? ? ? FF 15 ? ? ? ? 6A ? FF 35 ? ? ? ? 51 8B C4 8D 4D ? C7 00 ? ? ? ? FF 15 ? ? ? ? 6A ? FF 35 ? ? ? ? 51 8B C4 8D 4D ? C7 00 ? ? ? ? FF 15 ? ? ? ? 6A ? FF 35 ? ? ? ? 51 8B C4 C7 00";
+
+ private const string IsPausedPattern
+ = "0F 29 05 ? ? ? ? C7 05 ? ? ? ? ? ? ? ? C7 05 ? ? ? ? ? ? ? ? C7 05 ? ? ? ? ? ? ? ? E8";
+
+ private readonly nint _currentSongInfoAddress;
+ private readonly nint _isPausedAddress;
+ private readonly nint _albumThumbnailAddress;
+
+ private readonly int _pid;
+ private readonly ProcessMemory _process;
+
public Tencent(int pid)
- => throw new NotImplementedException();
+ {
+ _pid = pid;
+ using var p = Process.GetProcessById(pid);
+
+ foreach (ProcessModule module in p.Modules)
+ {
+ if (!"QQMusic.dll".Equals(module.ModuleName))
+ {
+ continue;
+ }
+
+ var process = new ProcessMemory(pid);
+ var address = module.BaseAddress;
+
+ if (Memory.FindPattern(CurrentSOngInfoPattern, pid, address, out var patternAddress))
+ {
+ var currentSongAddress = process.ReadInt32(patternAddress, 1);
+ _currentSongInfoAddress = currentSongAddress;
+ }
+
+ if (Memory.FindPattern(IsPausedPattern, pid, address, out patternAddress))
+ {
+ var isPausedAddress = process.ReadInt32(patternAddress, 3);
+ _isPausedAddress = isPausedAddress + 4;
+ }
+
+ if (Memory.FindPattern(AlbumThumbnailPattern, pid, address, out patternAddress))
+ {
+ var albumThumbnailAddress = process.ReadInt32(patternAddress, 1);
+ _albumThumbnailAddress = albumThumbnailAddress;
+ }
+
+ _process = process;
+
+ break;
+ }
+
+ if (_process is null)
+ {
+ throw new EntryPointNotFoundException("Failed to find process");
+ }
+
+ if (_currentSongInfoAddress == 0)
+ {
+ throw new EntryPointNotFoundException("_currentSongAddress is 0");
+ }
+ }
public bool Validate(int pid)
- => throw new System.NotImplementedException();
+ => _pid == pid;
public PlayerInfo? GetPlayerInfo()
- => throw new System.NotImplementedException();
+ {
+ var id = GetSongIdentity();
+
+ if (id == 0)
+ {
+ return null;
+ }
+
+ return new PlayerInfo
+ {
+ Identity = id.ToString(),
+ Title = GetSongName(),
+ Artists = GetArtistName(),
+ Album = GetAlbumName(),
+ Cover = GetAlbumThumbnailUrl(),
+ Schedule = GetSongSchedule() * 0.001,
+ Duration = GetSongDuration() * 0.001,
+ Pause = IsPaused(),
+
+ // lock
+ Url = $"https://y.qq.com/n/ryqq/songDetail/{id}",
+ };
+ }
+
+ private uint GetSongIdentity()
+ => _process.ReadUInt32(_currentSongInfoAddress);
+
+ private int GetSongDuration()
+ => _process.ReadInt32(_currentSongInfoAddress, 0x10);
+
+ private int GetSongSchedule()
+ => _process.ReadInt32(_currentSongInfoAddress, 0xC);
+
+ private string GetSongName()
+ {
+ var address = _process.ReadInt32(_currentSongInfoAddress);
+ var bytes = _process.ReadBytes(address, 512);
+
+ var result = Encoding.Unicode.GetString(bytes);
+
+ return result[..result.IndexOf('\0')];
+ }
+
+ private string GetArtistName()
+ {
+ var address = _process.ReadInt32(_currentSongInfoAddress, 4);
+ var bytes = _process.ReadBytes(address, 512);
+
+ var result = Encoding.Unicode.GetString(bytes);
+
+ return result[..result.IndexOf('\0')];
+ }
+
+ private string GetAlbumName()
+ {
+ var address = _process.ReadInt32(_currentSongInfoAddress, 8);
+ var bytes = _process.ReadBytes(address, 512);
+
+ var result = Encoding.Unicode.GetString(bytes);
+
+ return result[..result.IndexOf('\0')];
+ }
+
+ private string GetAlbumThumbnailUrl()
+ {
+ var address = _process.ReadInt32(_albumThumbnailAddress);
+ var bytes = _process.ReadBytes(address, 512);
+
+ var result = Encoding.Unicode.GetString(bytes);
+
+ return result[..result.IndexOf('\0')];
+ }
+
+ private bool IsPaused()
+ => _process.ReadInt32(_isPausedAddress) == 1;
}
diff --git a/Vanessa/Program.cs b/Vanessa/Program.cs
index 71306cd..7f6a110 100644
--- a/Vanessa/Program.cs
+++ b/Vanessa/Program.cs
@@ -156,7 +156,7 @@ private static async Task UpdateThread(DiscordRpcClient netEase, DiscordRpcClien
rpc.Assets = new Assets
{
LargeImageKey = info.Cover,
- LargeImageText = info.Album,
+ LargeImageText = $"💿 {info.Album}",
SmallImageKey = "timg",
SmallImageText = "NetEase CloudMusic",
};
diff --git a/Vanessa/Vanessa.csproj b/Vanessa/Vanessa.csproj
index 3c17e3e..84cb4dd 100644
--- a/Vanessa/Vanessa.csproj
+++ b/Vanessa/Vanessa.csproj
@@ -10,7 +10,7 @@
Kxnrl.Vanessa
1.0
1
- $(VersionPrefix).$(VersionSuffix)
+ 3.0
https://github.com/Kxnrl/NetEase-Cloud-Music-DiscordRPC
©2024 Kyle
https://github.com/Kxnrl/NetEase-Cloud-Music-DiscordRPC
diff --git a/Vanessa/Win32Api/Memory.cs b/Vanessa/Win32Api/Memory.cs
index babde9f..2d7a7dd 100644
--- a/Vanessa/Win32Api/Memory.cs
+++ b/Vanessa/Win32Api/Memory.cs
@@ -14,12 +14,13 @@ public static bool FindPattern(string pattern, int processId, nint address, out
var ntHeader = address + ntOffset;
// IMAGE
- var fileHeader = ntHeader + 4;
- var sections = memory.ReadInt16(ntHeader, 6);
+ var fileHeader = ntHeader + 4;
+ var sections = memory.ReadInt16(ntHeader, 6);
+ var sectionSize = memory.ReadInt16(fileHeader, 16);
// OPT HEADER
var optHeader = fileHeader + 20;
- var sectionHeader = optHeader + 240;
+ var sectionHeader = optHeader + sectionSize;
var cursor = sectionHeader;
@@ -167,6 +168,9 @@ public short ReadInt16(IntPtr address, int offset = 0)
public int ReadInt32(IntPtr address, int offset = 0)
=> BitConverter.ToInt32(ReadBytes(IntPtr.Add(address, offset), 4), 0);
+ public uint ReadUInt32(IntPtr address, int offset = 0)
+ => BitConverter.ToUInt32(ReadBytes(IntPtr.Add(address, offset), 4), 0);
+
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr pHandle, IntPtr address, byte[] buffer, int size, IntPtr bytesRead);