Skip to content

Commit 21a77e1

Browse files
committed
improve address search, update README, version 1.0.3
1 parent 996f22b commit 21a77e1

File tree

7 files changed

+87
-37
lines changed

7 files changed

+87
-37
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ repositories {
1414
}
1515
1616
dependencies {
17-
implementation 'com.github.LabyStudio:java-spotify-api:1.0.2:all'
17+
implementation 'com.github.LabyStudio:java-spotify-api:1.0.3:all'
1818
}
1919
```
2020

2121
## Example
2222
Create the API and get the current playing song and position:
2323
```java
2424
// Create a new SpotifyAPI for your operating system
25-
SpotifyAPI api = SpotifyAPIFactory.create();
25+
SpotifyAPI api = SpotifyAPIFactory.createInitialized();
2626

2727
// It has no track until the song started playing once
2828
if (api.hasTrack()) {

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group 'de.labystudio'
7-
version '1.0.2'
7+
version '1.0.3'
88

99
compileJava {
1010
sourceCompatibility = '1.8'

src/main/java/de/labystudio/spotifyapi/platform/windows/WinSpotifyAPI.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private void onTick() {
7272
String trackId = this.process.getTrackId();
7373

7474
// Update playback status and check if it is valid
75-
if (!playback.update() || !this.isTrackIdValid(trackId)) {
75+
if (!playback.update() || !this.process.isTrackIdValid(trackId)) {
7676
throw new IllegalStateException("Could not update playback");
7777
}
7878

@@ -173,20 +173,4 @@ public void stop() {
173173
}
174174
}
175175

176-
/**
177-
* Checks if the given track ID is valid.
178-
* A track ID is valid if there are no characters with a value of zero.
179-
*
180-
* @param trackId The track ID to check.
181-
* @return True if the track ID is valid, false otherwise.
182-
*/
183-
private boolean isTrackIdValid(String trackId) {
184-
for (char c : trackId.toCharArray()) {
185-
if (c == 0) {
186-
return false;
187-
}
188-
}
189-
return true;
190-
}
191-
192176
}

src/main/java/de/labystudio/spotifyapi/platform/windows/api/WinApi.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
import java.util.ArrayList;
1313
import java.util.Arrays;
14+
import java.util.HashMap;
1415
import java.util.List;
16+
import java.util.Map;
1517
import java.util.concurrent.atomic.AtomicReference;
1618

1719
/**
@@ -96,6 +98,25 @@ default String getWindowTitle(WinDef.HWND window) {
9698
return Native.toString(Arrays.copyOf(buffer, length));
9799
}
98100

101+
default Map<Long, Long> getModules(int pid) {
102+
Map<Long, Long> map = new HashMap<>();
103+
104+
WinNT.HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(
105+
Tlhelp32.TH32CS_SNAPMODULE,
106+
new WinDef.DWORD(pid)
107+
);
108+
if (snapshot == null) {
109+
return map;
110+
}
111+
112+
Tlhelp32.MODULEENTRY32W moduleEntry = new Tlhelp32.MODULEENTRY32W.ByReference();
113+
while (Kernel32.INSTANCE.Module32NextW(snapshot, moduleEntry)) {
114+
map.put(Pointer.nativeValue(moduleEntry.modBaseAddr), moduleEntry.modBaseSize.longValue());
115+
}
116+
Kernel32.INSTANCE.CloseHandle(snapshot);
117+
return map;
118+
}
119+
99120
default long getModuleAddress(int pid, String moduleName) {
100121
WinNT.HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(
101122
Tlhelp32.TH32CS_SNAPMODULE,

src/main/java/de/labystudio/spotifyapi/platform/windows/api/WinProcess.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.sun.jna.platform.win32.WinNT;
88
import com.sun.jna.ptr.IntByReference;
99

10+
import java.util.Map;
11+
1012
/**
1113
* Windows Process API
1214
* Used to connect to a Windows process and read its memory.
@@ -156,6 +158,20 @@ public long findInMemory(long address, long size, byte[] searchBytes, SearchCond
156158
return -1;
157159
}
158160

161+
/**
162+
* Find the highest process address.
163+
* The highest process address is determined by the highest module address + module size.
164+
*
165+
* @return The highest process address.
166+
*/
167+
public long getMaxProcessAddress() {
168+
long maxAddress = 0;
169+
for (Map.Entry<Long, Long> module : this.getModules(this.processId).entrySet()) {
170+
maxAddress = Math.max(maxAddress, module.getKey() + module.getValue());
171+
}
172+
return maxAddress;
173+
}
174+
159175
/**
160176
* Get the title of the window as a string.
161177
*

src/main/java/de/labystudio/spotifyapi/platform/windows/api/spotify/SpotifyProcess.java

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,7 @@
1111
public class SpotifyProcess extends WinProcess {
1212

1313
private static final byte[] PREFIX_TRACK_ID = "spotify:track:".getBytes();
14-
private static final long CHROME_ELF_ADDRESS = 0x5f710000L;
15-
private static final long CHROME_ELF_SIZE = 0x5f710000L;
16-
17-
private static final byte[] PREFIX_CONTEXT = new byte[]{0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x00};
18-
private static final long CONTEXT_ADDRESS = 0x0794E42C;
19-
private static final long CONTEXT_SIZE = CHROME_ELF_ADDRESS;
14+
private static final byte[] PREFIX_CONTEXT = new byte[]{0x63, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74};
2015

2116
private final long addressTrackId;
2217
private final long addressPlayBack;
@@ -34,25 +29,33 @@ public class SpotifyProcess extends WinProcess {
3429
public SpotifyProcess() {
3530
super("Spotify.exe");
3631

32+
long highestAddress = this.getMaxProcessAddress();
33+
34+
// Find addresses of playback states (Located in the chrome_elf.dll module)
35+
this.addressTrackId = this.findInMemory(
36+
0,
37+
highestAddress,
38+
PREFIX_TRACK_ID,
39+
address -> this.isTrackIdValid(this.readTrackId(address))
40+
);
41+
// Check if we have found the addresses
42+
if (this.addressTrackId == -1) {
43+
throw new IllegalStateException("Could not find track id in memory");
44+
}
45+
3746
// Check if the song is currently playing using the title bar
3847
boolean isPlaying = this.isPlayingUsingTitle();
3948

40-
// Find addresses of track id and playback states
41-
this.addressTrackId = this.findInMemory(CHROME_ELF_ADDRESS, CHROME_ELF_SIZE, PREFIX_TRACK_ID);
49+
// Find addresses of track id
4250
this.addressPlayBack = this.findInMemory(
43-
CONTEXT_ADDRESS,
44-
CONTEXT_SIZE,
51+
0,
52+
highestAddress,
4553
PREFIX_CONTEXT,
4654
address -> {
4755
PlaybackAccessor accessor = new PlaybackAccessor(this, address);
4856
return accessor.isValid() && accessor.isPlaying() == isPlaying; // Check if address is valid
4957
}
5058
);
51-
52-
// Check if we have found the addresses
53-
if (this.addressTrackId == -1) {
54-
throw new IllegalStateException("Could not find track id in memory");
55-
}
5659
if (this.addressPlayBack == -1) {
5760
throw new IllegalStateException("Could not find playback in memory");
5861
}
@@ -61,13 +64,23 @@ public SpotifyProcess() {
6164
this.playbackAccessor = new PlaybackAccessor(this, this.addressPlayBack);
6265
}
6366

67+
/**
68+
* Read the track id from the memory.
69+
*
70+
* @param address The address where the prefix "spotify:track:" starts
71+
* @return the track id without the prefix "spotify:track:"
72+
*/
73+
private String readTrackId(long address) {
74+
return this.readString(address + 14, 22);
75+
}
76+
6477
/**
6578
* Read the track id from the memory.
6679
*
6780
* @return the track id without the prefix "spotify:track:"
6881
*/
6982
public String getTrackId() {
70-
return new String(this.readBytes(this.addressTrackId + 14, 22));
83+
return this.readTrackId(this.addressTrackId);
7184
}
7285

7386
/**
@@ -106,4 +119,20 @@ public long getAddressTrackId() {
106119
public long getAddressPlayBack() {
107120
return this.addressPlayBack;
108121
}
122+
123+
/**
124+
* Checks if the given track ID is valid.
125+
* A track ID is valid if there are no characters with a value of zero.
126+
*
127+
* @param trackId The track ID to check.
128+
* @return True if the track ID is valid, false otherwise.
129+
*/
130+
public boolean isTrackIdValid(String trackId) {
131+
for (char c : trackId.toCharArray()) {
132+
if (c == 0) {
133+
return false;
134+
}
135+
}
136+
return true;
137+
}
109138
}

src/test/java/SpotifyTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
public class SpotifyTest {
1010

1111
public static void main(String[] args) throws Exception {
12-
SpotifyAPI api = SpotifyAPIFactory.create();
12+
SpotifyAPI api = SpotifyAPIFactory.createInitialized();
1313

1414
// It has no track until the song started playing once
1515
if (api.hasTrack()) {

0 commit comments

Comments
 (0)