Skip to content

Commit

Permalink
v1.4: fixed settings issue, implemented popup on data reload, blocked…
Browse files Browse the repository at this point in the history
… store review
  • Loading branch information
whoeevee committed Apr 29, 2024
1 parent 3f60185 commit f11e0cb
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 58 deletions.
12 changes: 12 additions & 0 deletions Sources/EeveeSpotify/BlockStoreReviews.x.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Orion
import UIKit
import StoreKit

// uncomment if you're building for jailbreak, cause ipa crashes:
// Fatal error: Error in tweak EeveeSpotify: Failed to hook method -[SKStoreReviewController requestReview:] (Could not hook method)
// Fatal error: Error in tweak EeveeSpotify: Failed to hook method -[SKStoreReviewController requestReviewInScene:] (Could not hook method)

// class SKStoreReviewControllerHook: ClassHook<SKStoreReviewController> {
// func requestReview() { }
// func requestReviewInScene(_ scene: UIWindowScene) { }
// }
63 changes: 63 additions & 0 deletions Sources/EeveeSpotify/Helpers/OfflineHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import Foundation

class OfflineHelper {

static let persistentCachePath = FileManager.default.urls(
for: .applicationSupportDirectory, in: .userDomainMask
)
.first!
.appendingPathComponent("PersistentCache")

//

static var offlineBnkPath: URL {
persistentCachePath.appendingPathComponent("offline.bnk")
}

static var eeveeBnkPath: URL {
persistentCachePath.appendingPathComponent("eevee.bnk")
}

static var offlineBnkData: Data {
get throws { try Data(contentsOf: offlineBnkPath) }
}

static var eeveeBnkData: Data {
get throws { try Data(contentsOf: eeveeBnkPath) }
}

//

private static func writeOfflineBnkData(_ data: Data) throws {
try data.write(to: offlineBnkPath)
}

private static func writeEeveeBnkData(_ data: Data) throws {
try data.write(to: eeveeBnkPath)
}

//

static func restoreFromEeveeBnk() throws {
try writeOfflineBnkData(try eeveeBnkData)
}

static func backupToEeveeBnk() throws {
try writeEeveeBnkData(try offlineBnkData)
}

static func patchOfflineBnk() throws {

let fileData = try offlineBnkData

let usernameLength = Int(fileData[8])
let username = Data(fileData[9..<9 + usernameLength])

var blankData = try BundleHelper.shared.premiumBlankData()

blankData.insert(UInt8(usernameLength), at: 8)
blankData.insert(contentsOf: username, at: 9)

try writeOfflineBnkData(blankData)
}
}
65 changes: 40 additions & 25 deletions Sources/EeveeSpotify/Helpers/PopUpHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,50 @@ class PopUpHelper {
.shared()

static func showPopUp(
delayed: Bool = false,
message: String,
buttonText: String
buttonText: String,
secondButtonText: String? = nil,
onPrimaryClick: (() -> Void)? = nil,
onSecondaryClick: (() -> Void)? = nil
) {

if isPopUpShowing {
return
}
DispatchQueue.main.asyncAfter(deadline: delayed ? .now() + 3.0 : .now()) {

if isPopUpShowing {
return
}

let model = Dynamic.SPTEncorePopUpDialogModel
.alloc(interface: SPTEncorePopUpDialogModel.self)
.initWithTitle(
"EeveeSpotify",
description: message,
image: nil,
primaryButtonTitle: buttonText,
secondaryButtonTitle: nil
)

let dialog = Dynamic.SPTEncorePopUpDialog
.alloc(interface: SPTEncorePopUpDialog.self)
.`init`()

dialog.update(model)
dialog.setEventHandler({
sharedPresenter.dismissPopupWithAnimate(true, clearQueue: false, completion: nil)
isPopUpShowing.toggle()
})
let model = Dynamic.SPTEncorePopUpDialogModel
.alloc(interface: SPTEncorePopUpDialogModel.self)
.initWithTitle(
"EeveeSpotify",
description: message,
image: nil,
primaryButtonTitle: buttonText,
secondaryButtonTitle: secondButtonText
)

let dialog = Dynamic.SPTEncorePopUpDialog
.alloc(interface: SPTEncorePopUpDialog.self)
.`init`()

dialog.update(model)
dialog.setEventHandler({ state in

switch (state) {

case .primary: onPrimaryClick?()
case .secondary: onSecondaryClick?()

}

sharedPresenter.presentPopUp(dialog)
isPopUpShowing.toggle()
sharedPresenter.dismissPopupWithAnimate(true, clearQueue: false, completion: nil)
isPopUpShowing.toggle()
})

isPopUpShowing.toggle()
sharedPresenter.presentPopUp(dialog)
}
}
}
19 changes: 19 additions & 0 deletions Sources/EeveeSpotify/HookedInstances.x.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Orion

class HookedInstances {
static var productState: SPTCoreProductState?
}

class SPTCoreProductStateInstanceHook: ClassHook<NSObject> {

static let targetName = "SPTCoreProductState"

func stringForKey(_ key: String) -> String {

HookedInstances.productState = Dynamic.convert(
target,
to: SPTCoreProductState.self
)
return orig.stringForKey(key)
}
}
5 changes: 5 additions & 0 deletions Sources/EeveeSpotify/Models/SPTCoreProductState.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

@objc protocol SPTCoreProductState {
func stringForKey(_ key: String) -> String
}
7 changes: 6 additions & 1 deletion Sources/EeveeSpotify/Models/SPTEncorePopUpDialog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@ import Foundation
@objc protocol SPTEncorePopUpDialog {
func `init`() -> SPTEncorePopUpDialog
func update(_ popUpModel: SPTEncorePopUpDialogModel)
func setEventHandler(_ handler: @escaping () -> Void)
func setEventHandler(_ handler: @escaping (_ state: ClickState) -> Void)
}

@objc enum ClickState: Int {
case primary
case secondary
}
52 changes: 52 additions & 0 deletions Sources/EeveeSpotify/OfflineObserver.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import Foundation
import UIKit

func exitApplication() {

UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)
Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
exit(EXIT_SUCCESS)
}
}

class OfflineObserver: NSObject, NSFilePresenter {

var presentedItemURL: URL?
var presentedItemOperationQueue: OperationQueue

override init() {
presentedItemURL = OfflineHelper.offlineBnkPath
presentedItemOperationQueue = .main
}

func presentedItemDidChange() {

let productState = HookedInstances.productState!

if productState.stringForKey("player-license") == "premium" {

do {
try OfflineHelper.backupToEeveeBnk()
NSLog("[EeveeSpotify] Settings has changed, updated eevee.bnk")
}
catch {
NSLog("[EeveeSpotify] Unable to update eevee.bnk: \(error)")
}

return
}

PopUpHelper.showPopUp(
message: "Spotify has just reloaded user data, and you've been switched to the Free plan. It's fine; simply restart the app, and the tweak will patch the data again. If this doesn't work, there might be a problem with the saved data. You can reset it and restart the app. Note: after resetting, you need to restart the app twice.",
buttonText: "Restart App",
secondButtonText: "Reset Data and Restart App",
onPrimaryClick: {
exitApplication()
},
onSecondaryClick: {
try! FileManager.default.removeItem(at: OfflineHelper.persistentCachePath)
exitApplication()
}
)
}
}
59 changes: 28 additions & 31 deletions Sources/EeveeSpotify/Tweak.x.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,52 +27,49 @@ struct EeveeSpotify: Tweak {

do {

let filePath = FileManager.default.urls(
for: .applicationSupportDirectory, in: .userDomainMask
)
.first!
.appendingPathComponent("PersistentCache")
.appendingPathComponent("offline.bnk")

if !FileManager.default.fileExists(atPath: filePath.path) {

DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
defer {
NSFileCoordinator.addFilePresenter(OfflineObserver())
}

PopUpHelper.showPopUp(
message: "Please log in and restart the app to get Premium.",
buttonText: "Okay!"
)
}
do {
try OfflineHelper.restoreFromEeveeBnk()
NSLog("[EeveeSpotify] Restored from eevee.bnk")

NSLog("[EeveeSpotify] Not activating due to nonexistent file: \(filePath)")
return
}

let fileData = try Data(contentsOf: filePath)

let usernameLength = Int(fileData[8])
let username = Data(fileData[9..<9 + usernameLength])

var blankData = try BundleHelper.shared.premiumBlankData()
catch CocoaError.fileReadNoSuchFile {
NSLog("[EeveeSpotify] Not restoring from eevee.bnk: doesn't exist")
}

blankData.insert(UInt8(usernameLength), at: 8)
blankData.insert(contentsOf: username, at: 9)
//

try blankData.write(to: filePath)
NSLog("[EeveeSpotify] Successfully applied")
}
do {
try OfflineHelper.patchOfflineBnk()
try OfflineHelper.backupToEeveeBnk()
}

catch {
catch CocoaError.fileReadNoSuchFile {

DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
NSLog("[EeveeSpotify] Not activating: offline.bnk doesn't exist")

PopUpHelper.showPopUp(
message: "Unable to apply tweak: \(error)",
buttonText: "OK"
delayed: true,
message: "Please log in and restart the app to get Premium.",
buttonText: "Okay!"
)
}
}

catch {

NSLog("[EeveeSpotify] Unable to apply tweak: \(error)")

PopUpHelper.showPopUp(
delayed: true,
message: "Unable to apply tweak: \(error)",
buttonText: "OK"
)
}
}
}
2 changes: 1 addition & 1 deletion control
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: com.eevee.spotify
Name: EeveeSpotify
Version: 1.3
Version: 1.4
Architecture: iphoneos-arm
Description: A tweak to get Spotify Premium for free, just like Spotilife
Maintainer: Eevee
Expand Down

0 comments on commit f11e0cb

Please sign in to comment.