Created
June 6, 2019 00:01
-
-
Save SamL98/0cd20b00951b9a5cca6b5c9380ec5642 to your computer and use it in GitHub Desktop.
Hooks and accompanying code for Skiptracing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MARK: SkipManager | |
#define NUM_SKIP_BYTES 10 | |
#define BYTES_PER_LINE 34 | |
#define SKIP_FILE_PATH "/Users/samlerner/Documents/Spotify/skipped.csv" | |
// SkipManager class to handle writing skips to files | |
class SkipManager { | |
public: | |
SkipManager(); | |
void push(char * _Nonnull); | |
void pop(); | |
private: | |
int bytesInHeader; | |
int numSkippedInSession; | |
int numSkipped; | |
void openSkipFile(); | |
int readNumSkipped(); | |
void writeNumSkipped(); | |
}; | |
FILE *_Nullable skipFile; | |
void close() | |
{ | |
if (skipFile) | |
fclose(skipFile); | |
} | |
SkipManager::SkipManager() | |
{ | |
this->bytesInHeader = 0; | |
this->numSkipped = 0; | |
this->numSkippedInSession = 0; | |
this->openSkipFile(); | |
// Read the number of songs previously skipped, or writer 0 if the file is empty | |
this->numSkipped = this->readNumSkipped(); | |
atexit(close); | |
} | |
void SkipManager::openSkipFile() | |
{ | |
skipFile = fopen(SKIP_FILE_PATH, "r+"); | |
if (!skipFile) | |
{ | |
skipFile = fopen(SKIP_FILE_PATH, "w+"); | |
if (!skipFile) | |
{ | |
perror("Could not open skip file"); | |
exit(-1); | |
} | |
} | |
} | |
int SkipManager::readNumSkipped() | |
{ | |
if (!skipFile) | |
return 0; | |
char numSkipStr[NUM_SKIP_BYTES]; | |
// If this read fails (the file is empty), then write 0 on the first line | |
if (!fgets(numSkipStr, NUM_SKIP_BYTES, skipFile)) | |
{ | |
this->writeNumSkipped(); | |
return 0; | |
} | |
this->bytesInHeader = strlen(numSkipStr) + 1; | |
return atoi(numSkipStr); | |
} | |
void SkipManager::writeNumSkipped() | |
{ | |
if (!skipFile) | |
return; | |
char skipStr[NUM_SKIP_BYTES]; | |
sprintf(skipStr, "%d\n", this->numSkipped); | |
this->bytesInHeader = strlen(skipStr); | |
// Write the total number of skips to the beginning of the file | |
fseek(skipFile, 0, SEEK_SET); | |
fputs(skipStr, skipFile); | |
fflush(skipFile); | |
} | |
void SkipManager::push(char * _Nonnull tid) | |
{ | |
if (!skipFile) | |
return; | |
// Increment both num skipped counters | |
++this->numSkippedInSession; | |
++this->numSkipped; | |
this->writeNumSkipped(); | |
char line[BYTES_PER_LINE]; | |
sprintf(line, "%s,%lu\n", tid, (unsigned long)time(NULL)); | |
fseek(skipFile, this->bytesInHeader + (this->numSkipped - 1) * BYTES_PER_LINE, SEEK_SET); | |
fputs(line, skipFile); | |
fflush(skipFile); | |
} | |
void SkipManager::pop() | |
{ | |
// Don't decrement the skipped counters if we haven't skipped any in this session | |
if (!skipFile || !this->numSkippedInSession) | |
return; | |
--this->numSkippedInSession; | |
--this->numSkipped; | |
this->writeNumSkipped(); | |
fflush(skipFile); | |
} | |
// Global skipmanager | |
SkipManager skipman; | |
// MARK: AppleScript Hooks | |
// Hook to get the Spotify URL of the track pointed to by track_ptr | |
NSString*(*sub_1000EC4F0_caller)(void *track_ptr) = NULL; | |
static NSString *Hooked_sub_1000EC4F0(void *track_ptr) | |
{ | |
NSString *url= sub_1000EC4F0_caller(track_ptr); | |
reset_hook(reinterpret_cast<void*>(Hooked_sub_1000EC4F0)); | |
return url; | |
} | |
// Hook to get the player position | |
NSNumber*(*sub_1000EB760_caller)() = NULL; | |
static NSNumber *Hooked_sub_1000EB760() | |
{ | |
NSNumber *position= sub_1000EB760_caller(); | |
reset_hook(reinterpret_cast<void*>(Hooked_sub_1000EB760)); | |
return position; | |
} | |
// Hook to get the duration of the current track | |
NSNumber*(*sub_1000EC430_caller)(void *track_ptr) = NULL; | |
static NSNumber *Hooked_sub_1000EC430(void *track_ptr) | |
{ | |
NSNumber *duration= sub_1000EC430_caller(track_ptr); | |
reset_hook(reinterpret_cast<void*>(Hooked_sub_1000EC430)); | |
return duration; | |
} | |
// Hook to get a pointer to the current track | |
void*(*sub_1000EB2B0_caller)() = NULL; | |
static void *Hooked_sub_1000EB2B0() | |
{ | |
void *track_ptr = sub_1000EB2B0_caller(); | |
reset_hook(reinterpret_cast<void*>(Hooked_sub_1000EB2B0)); | |
return track_ptr; | |
} | |
// MARK: Helper functions | |
void getTID(char *tid) { | |
void *track_ptr = Hooked_sub_1000EB2B0(); | |
NSString *track_url = Hooked_sub_1000EC4F0(track_ptr); | |
strncpy(tid, [track_url UTF8String] + 14, 22); | |
} | |
int getPlaybackPosition() { | |
NSNumber *position = Hooked_sub_1000EB760(); | |
reset_hook(reinterpret_cast<void*>(Hooked_sub_1000EB760)); | |
return [position intValue]; | |
} | |
int getDuration() { | |
void *track_ptr = Hooked_sub_1000EB2B0(); | |
NSNumber *durationMillis = Hooked_sub_1000EC430(track_ptr); | |
return (int)[durationMillis longValue]/1000; | |
} | |
BOOL shouldHandleMediaKey() { | |
int position = getPlaybackPosition(); | |
int duration = getDuration(); | |
return position <= duration/2; | |
} | |
// MARK: Playback hooks | |
// Next hook | |
void (*sub_100CC2E20_caller)(void *p1, void *p2, void *p3) = NULL; | |
static void Hooked_sub_100CC2E20(void *p1, void *p2, void *p3) | |
{ | |
char tid[23]; | |
getTID(tid); | |
BOOL shdSkip = shouldHandleMediaKey(); | |
sub_100CC2E20_caller(p1, p2, p3); | |
reset_hook(reinterpret_cast<void*>(Hooked_sub_100CC2E20)); | |
if (shdSkip && *tid) | |
skipman.push(tid); | |
} | |
// Prev hook | |
void (*sub_100CC2D20_caller)(void *p1, void *p2, void *p3) = NULL; | |
static void Hooked_sub_100CC2D20(void *p1, void *p2, void *p3) | |
{ | |
char oldTid[23]; | |
getTID(oldTid); | |
BOOL shdPop = shouldHandleMediaKey(); | |
sub_100CC2D20_caller(p1, p2, p3); | |
reset_hook(reinterpret_cast<void*>(Hooked_sub_100CC2D20)); | |
char newTid[23]; | |
getTID(newTid); | |
if (shdPop && strncmp(oldTid, newTid, 22)) | |
skipman.pop(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment