Skip to content

Commit

Permalink
feat: qq music #12
Browse files Browse the repository at this point in the history
  • Loading branch information
Kxnrl committed Nov 16, 2024
1 parent 39236cb commit f5beb04
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 8 deletions.
145 changes: 142 additions & 3 deletions Vanessa/Players/Tencent.cs
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 1 addition & 1 deletion Vanessa/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
};
Expand Down
2 changes: 1 addition & 1 deletion Vanessa/Vanessa.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<RootNamespace>Kxnrl.Vanessa</RootNamespace>
<VersionPrefix>1.0</VersionPrefix>
<VersionSuffix>1</VersionSuffix>
<Version>$(VersionPrefix).$(VersionSuffix)</Version>
<Version>3.0</Version>
<PackageProjectUrl>https://github.com/Kxnrl/NetEase-Cloud-Music-DiscordRPC</PackageProjectUrl>
<Copyright>©2024 Kyle</Copyright>
<RepositoryUrl>https://github.com/Kxnrl/NetEase-Cloud-Music-DiscordRPC</RepositoryUrl>
Expand Down
10 changes: 7 additions & 3 deletions Vanessa/Win32Api/Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit f5beb04

Please sign in to comment.