Merge branch 'develop' into Sidekit-jit-implementation

Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
This commit is contained in:
Spidy123222
2023-05-17 02:34:48 -07:00
committed by GitHub
32 changed files with 423 additions and 698 deletions

View File

@@ -14,7 +14,7 @@
<key>ALTPairingFile</key>
<string>&lt;insert pairing file here&gt;</string>
<key>ALTAnisetteURL</key>
<string>https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx</string>
<string>https://ani.sidestore.io</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDocumentTypes</key>

View File

@@ -14,6 +14,8 @@ import minimuxer
import AltStoreCore
import UniformTypeIdentifiers
let pairingFileName = "ALTPairingFile.mobiledevicepairing"
final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDelegate
{
private var didFinishLaunching = false
@@ -125,14 +127,11 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
}
// Save to a file for next launch
let filename = "ALTPairingFile.mobiledevicepairing"
let fm = FileManager.default
let documentsPath = fm.documentsDirectory.appendingPathComponent("/\(filename)")
try pairing_string?.write(to: documentsPath, atomically: true, encoding: String.Encoding.utf8)
let pairingFile = FileManager.default.documentsDirectory.appendingPathComponent("\(pairingFileName)")
try pairing_string?.write(to: pairingFile, atomically: true, encoding: String.Encoding.utf8)
// Start minimuxer now that we have a file
start_minimuxer_threads(pairing_string!)
} catch {
displayError("Unable to read pairing file")
}
@@ -148,22 +147,15 @@ final class LaunchViewController: RSTLaunchViewController, UIDocumentPickerDeleg
}
func start_minimuxer_threads(_ pairing_file: String) {
set_usbmuxd_socket()
#if false // Retries
var res = start_minimuxer(pairing_file: pairing_file)
var attempts = 10
while (attempts != 0 && res != 0) {
print("start_minimuxer `res` != 0, retry #\(attempts)")
res = start_minimuxer(pairing_file: pairing_file)
attempts -= 1
target_minimuxer_address()
let documentsDirectory = FileManager.default.documentsDirectory.absoluteString
do {
try start(pairing_file, documentsDirectory)
} catch {
try! FileManager.default.removeItem(at: FileManager.default.documentsDirectory.appendingPathComponent("\(pairingFileName)"))
displayError("minimuxer failed to start, please restart SideStore. \((error as? LocalizedError)?.failureReason ?? "UNKNOWN ERROR!!!!!! REPORT TO GITHUB ISSUES!")")
}
#else
let res = start_minimuxer(pairing_file: pairing_file)
#endif
if res != 0 {
displayError("minimuxer failed to start. Incorrect arguments were passed.")
}
auto_mount_dev_image()
start_auto_mounter(documentsDirectory)
}
}

View File

@@ -876,7 +876,9 @@ private extension AppManager
if app.certificateSerialNumber != group.context.certificate?.serialNumber ||
uti != nil ||
app.needsResign
app.needsResign ||
// We need to reinstall ourselves on refresh to ensure the new provisioning profile is used
app.bundleIdentifier == StoreApp.altstoreAppID
{
// Resign app instead of just refreshing profiles because either:
// * Refreshing using different certificate

View File

@@ -11,6 +11,7 @@ import CoreData
import AltStoreCore
import EmotionalDamage
import minimuxer
enum RefreshError: LocalizedError
{
@@ -97,6 +98,14 @@ final class BackgroundRefreshAppsOperation: ResultOperation<[String: Result<Inst
return
}
start_em_proxy(bind_addr: Consts.Proxy.serverURL)
target_minimuxer_address()
let documentsDirectory = FileManager.default.documentsDirectory.absoluteString
do {
try minimuxer.start(try String(contentsOf: FileManager.default.documentsDirectory.appendingPathComponent("\(pairingFileName)")), documentsDirectory)
} catch {
self.finish(.failure(error))
}
start_auto_mounter(documentsDirectory)
self.managedObjectContext.perform {
print("Apps to refresh:", self.installedApps.map(\.bundleIdentifier))

View File

@@ -44,14 +44,9 @@ final class DeactivateAppOperation: ResultOperation<InstalledApp>
for profile in allIdentifiers {
do {
let res = try remove_provisioning_profile(id: profile)
if case Uhoh.Bad(let code) = res {
self.finish(.failure(minimuxer_to_operation(code: code)))
}
} catch Uhoh.Bad(let code) {
self.finish(.failure(minimuxer_to_operation(code: code)))
try remove_provisioning_profile(profile)
} catch {
self.finish(.failure(ALTServerError(.unknownResponse)))
return self.finish(.failure(error))
}
}

View File

@@ -45,23 +45,13 @@ final class EnableJITOperation<Context: EnableJITContext>: ResultOperation<Void>
guard let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) }
installedApp.managedObjectContext?.perform {
let v = minimuxer_to_operation(code: 1)
do {
var x = try debug_app(app_id: installedApp.resignedBundleIdentifier)
switch x {
case .Good:
self.finish(.success(()))
case .Bad(let code):
self.finish(.failure(minimuxer_to_operation(code: code)))
}
} catch Uhoh.Bad(let code) {
self.finish(.failure(minimuxer_to_operation(code: code)))
try debug_app(installedApp.resignedBundleIdentifier)
} catch {
self.finish(.failure(OperationError.unknown))
return self.finish(.failure(error))
}
self.finish(.success(()))
}
}
}

View File

@@ -268,8 +268,17 @@ extension FetchProvisioningProfilesOperation
}
}
}
//App ID name must be ascii. If the name is not ascii, using bundleID instead
let appIDName: String
if !name.allSatisfy({ $0.isASCII }) {
//Contains non ASCII (Such as Chinese/Japanese...), using bundleID
appIDName = bundleIdentifier
}else {
//ASCII text, keep going as usual
appIDName = name
}
ALTAppleAPI.shared.addAppID(withName: name, bundleIdentifier: bundleIdentifier, team: team, session: session) { (appID, error) in
ALTAppleAPI.shared.addAppID(withName: appIDName, bundleIdentifier: bundleIdentifier, team: team, session: session) { (appID, error) in
do
{
do
@@ -384,19 +393,39 @@ extension FetchProvisioningProfilesOperation
if app.isAltStoreApp
{
print("Application groups before modifying for SideStore: \(applicationGroups)")
// Remove app groups that contain AltStore since they can be problematic (cause SideStore to expire early)
for (index, group) in applicationGroups.enumerated() {
if group.contains("AltStore") {
print("Removing application group: \(group)")
applicationGroups.remove(at: index)
}
}
// Make sure we add .AltWidget for the widget
var altStoreAppGroupID = Bundle.baseAltStoreAppGroupID
for (_, group) in applicationGroups.enumerated() {
if group.contains("AltWidget") {
altStoreAppGroupID += ".AltWidget"
break
}
}
// Potentially updating app groups for this specific AltStore.
// Find the (unique) AltStore app group, then replace it
// with the correct "base" app group ID.
// Otherwise, we may append a duplicate team identifier to the end.
if let index = applicationGroups.firstIndex(where: { $0.contains(Bundle.baseAltStoreAppGroupID) })
{
applicationGroups[index] = Bundle.baseAltStoreAppGroupID
applicationGroups[index] = altStoreAppGroupID
}
else
{
applicationGroups.append(Bundle.baseAltStoreAppGroupID)
applicationGroups.append(altStoreAppGroupID)
}
}
print("Application groups: \(applicationGroups)")
// Dispatch onto global queue to prevent appGroupsLock deadlock.
DispatchQueue.global().async {
@@ -478,10 +507,13 @@ extension FetchProvisioningProfilesOperation
ALTAppleAPI.shared.delete(profile, for: team, session: session) { (success, error) in
switch Result(success, error)
{
case .failure(let error): completionHandler(.failure(error))
case .success:
case .failure:
// As of March 20, 2023, the free provisioning profile is re-generated each fetch, and you can no longer delete it.
// So instead, we just return the fetched profile from above.
completionHandler(.success(profile))
// Fetch new provisiong profile
case .success:
// Fetch new provisioning profile
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in
completionHandler(Result(profile, error))
}

View File

@@ -11,6 +11,7 @@ import Network
import AltStoreCore
import AltSign
import Roxas
import minimuxer
@objc(InstallAppOperation)
final class InstallAppOperation: ResultOperation<InstalledApp>
@@ -148,17 +149,72 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
})
}
let ns_bundle = NSString(string: installedApp.bundleIdentifier)
let ns_bundle_ptr = UnsafeMutablePointer<CChar>(mutating: ns_bundle.utf8String)
let res = minimuxer_install_ipa(ns_bundle_ptr)
if res == 0 {
installedApp.refreshedDate = Date()
self.finish(.success(installedApp))
} else {
self.finish(.failure(minimuxer_to_operation(code: res)))
var installing = true
if installedApp.storeApp?.bundleIdentifier == Bundle.Info.appbundleIdentifier {
// Reinstalling ourself will hang until we leave the app, so we need to exit it without force closing
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
if UIApplication.shared.applicationState != .active {
print("We are not in the foreground, let's not do anything")
return
}
if !installing {
print("Installing finished")
return
}
print("We are still installing after 3 seconds")
UNUserNotificationCenter.current().getNotificationSettings { settings in
switch (settings.authorizationStatus) {
case .authorized, .ephemeral, .provisional:
print("Notifications are enabled")
let content = UNMutableNotificationContent()
content.title = "Refreshing..."
content.body = "To finish refreshing, SideStore must be moved to the background, which it does by opening Safari. Please reopen SideStore after it is done refreshing!"
let notification = UNNotificationRequest(identifier: Bundle.Info.appbundleIdentifier + ".FinishRefreshNotification", content: content, trigger: UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false))
UNUserNotificationCenter.current().add(notification)
DispatchQueue.main.async { UIApplication.shared.open(URL(string: "x-web-search://")!) }
break
default:
print("Notifications are not enabled")
let alert = UIAlertController(title: "Finish Refresh", message: "To finish refreshing, SideStore must be moved to the background. To do this, you can either go to the Home Screen or open Safari by pressing Continue. Please reopen SideStore after doing this.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("Continue", comment: ""), style: .default, handler: { _ in
print("Opening Safari")
DispatchQueue.main.async { UIApplication.shared.open(URL(string: "x-web-search://")!) }
}))
DispatchQueue.main.async {
let keyWindow = UIApplication.shared.windows.filter { $0.isKeyWindow }.first
if var topController = keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.present(alert, animated: true)
} else {
print("No key window? Let's just open Safari")
UIApplication.shared.open(URL(string: "x-web-search://")!)
}
}
break
}
}
}
}
do {
try install_ipa(installedApp.bundleIdentifier)
installing = false
} catch {
installing = false
return self.finish(.failure(error))
}
installedApp.refreshedDate = Date()
self.finish(.success(installedApp))
}
}
@@ -174,10 +230,11 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
do
{
try FileManager.default.removeItem(at: fileURL)
print("Removed refreshed IPA")
}
catch
{
print("Failed to remove refreshed .ipa:", error)
print("Failed to remove refreshed .ipa: \(error)")
}
}

View File

@@ -8,6 +8,7 @@
import Foundation
import AltSign
import minimuxer
enum OperationError: LocalizedError
{
@@ -33,20 +34,6 @@ enum OperationError: LocalizedError
case openAppFailed(name: String)
case missingAppGroup
case noDevice
case createService(name: String)
case getFromDevice(name: String)
case setArgument(name: String)
case afc
case install
case uninstall
case lookupApps
case detach
case functionArguments
case profileInstall
case noConnection
case attach
var failureReason: String? {
switch self {
case .unknown: return NSLocalizedString("An unknown error occured.", comment: "")
@@ -62,19 +49,6 @@ enum OperationError: LocalizedError
case .openAppFailed(let name): return String(format: NSLocalizedString("SideStore was denied permission to launch %@.", comment: ""), name)
case .missingAppGroup: return NSLocalizedString("SideStore's shared app group could not be found.", comment: "")
case .maximumAppIDLimitReached: return NSLocalizedString("Cannot register more than 10 App IDs.", comment: "")
case .noDevice: return NSLocalizedString("Cannot fetch the device from the muxer", comment: "")
case .createService(let name): return String(format: NSLocalizedString("Cannot start a %@ server on the device.", comment: ""), name)
case .getFromDevice(let name): return String(format: NSLocalizedString("Cannot fetch %@ from the device.", comment: ""), name)
case .setArgument(let name): return String(format: NSLocalizedString("Cannot set %@ on the device.", comment: ""), name)
case .afc: return NSLocalizedString("AFC was unable to manage files on the device", comment: "")
case .install: return NSLocalizedString("Unable to install the app from the staging directory", comment: "")
case .uninstall: return NSLocalizedString("Unable to uninstall the app", comment: "")
case .lookupApps: return NSLocalizedString("Unable to fetch apps from the device", comment: "")
case .detach: return NSLocalizedString("Unable to detach from the app's process", comment: "")
case .functionArguments: return NSLocalizedString("A function was passed invalid arguments", comment: "")
case .profileInstall: return NSLocalizedString("Unable to manage profiles on the device", comment: "")
case .noConnection: return NSLocalizedString("Unable to connect to the device, make sure Wireguard is enabled and you're connected to WiFi", comment: "")
case .attach: return NSLocalizedString("Unable to attach to the app's process", comment: "")
}
}
@@ -119,44 +93,6 @@ enum OperationError: LocalizedError
}
}
}
func minimuxer_to_operation(code: Int32) -> OperationError {
switch code {
case 1:
return OperationError.noDevice
case 2:
return OperationError.createService(name: "debug")
case 3:
return OperationError.createService(name: "instproxy")
case 4:
return OperationError.getFromDevice(name: "installed apps")
case 5:
return OperationError.getFromDevice(name: "path to the app")
case 6:
return OperationError.getFromDevice(name: "bundle path")
case 7:
return OperationError.setArgument(name: "max packet")
case 8:
return OperationError.setArgument(name: "working directory")
case 9:
return OperationError.setArgument(name: "argv")
case 10:
return OperationError.getFromDevice(name: "launch success")
case 11:
return OperationError.detach
case 12:
return OperationError.functionArguments
case 13:
return OperationError.createService(name: "AFC")
case 14:
return OperationError.afc
case 15:
return OperationError.install
case 16:
return OperationError.uninstall
case 17:
return OperationError.createService(name: "misagent")
case 18:
return OperationError.profileInstall
case 19:
return OperationError.profileInstall
@@ -166,5 +102,66 @@ func minimuxer_to_operation(code: Int32) -> OperationError {
return OperationError.attach
default:
return OperationError.unknown
extension MinimuxerError: LocalizedError {
public var failureReason: String? {
switch self {
case .NoDevice:
return NSLocalizedString("Cannot fetch the device from the muxer", comment: "")
case .NoConnection:
return NSLocalizedString("Unable to connect to the device, make sure Wireguard is enabled and you're connected to WiFi", comment: "")
case .PairingFile:
return NSLocalizedString("Invalid pairing file. Your pairing file either didn't have a UDID, or it wasn't a valid plist. Please use jitterbugpair to generate it", comment: "")
case .CreateDebug:
return self.createService(name: "debug")
case .LookupApps:
return self.getFromDevice(name: "installed apps")
case .FindApp:
return self.getFromDevice(name: "path to the app")
case .BundlePath:
return self.getFromDevice(name: "bundle path")
case .MaxPacket:
return self.setArgument(name: "max packet")
case .WorkingDirectory:
return self.setArgument(name: "working directory")
case .Argv:
return self.setArgument(name: "argv")
case .LaunchSuccess:
return self.getFromDevice(name: "launch success")
case .Detach:
return NSLocalizedString("Unable to detach from the app's process", comment: "")
case .Attach:
return NSLocalizedString("Unable to attach to the app's process", comment: "")
case .CreateInstproxy:
return self.createService(name: "instproxy")
case .CreateAfc:
return self.createService(name: "AFC")
case .RwAfc:
return NSLocalizedString("AFC was unable to manage files on the device", comment: "")
case .InstallApp:
return NSLocalizedString("Unable to install the app from the staging directory", comment: "")
case .UninstallApp:
return NSLocalizedString("Unable to uninstall the app", comment: "")
case .CreateMisagent:
return self.createService(name: "misagent")
case .ProfileInstall:
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
case .ProfileRemove:
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
}
}
fileprivate func createService(name: String) -> String {
return String(format: NSLocalizedString("Cannot start a %@ server on the device.", comment: ""), name)
}
fileprivate func getFromDevice(name: String) -> String {
return String(format: NSLocalizedString("Cannot fetch %@ from the device.", comment: ""), name)
}
fileprivate func setArgument(name: String) -> String {
return String(format: NSLocalizedString("Cannot set %@ on the device.", comment: ""), name)
}
}

View File

@@ -49,15 +49,12 @@ final class RefreshAppOperation: ResultOperation<InstalledApp>
for p in profiles {
do {
let x = try install_provisioning_profile(plist: p.value.data)
if case .Bad(let code) = x {
self.finish(.failure(minimuxer_to_operation(code: code)))
}
} catch Uhoh.Bad(let code) {
self.finish(.failure(minimuxer_to_operation(code: code)))
let bytes = p.value.data.toRustByteSlice()
try install_provisioning_profile(bytes.forRust())
} catch {
self.finish(.failure(OperationError.unknown))
return self.finish(.failure(error))
}
self.progress.completedUnitCount += 1
let predicate = NSPredicate(format: "%K == %@", #keyPath(InstalledApp.bundleIdentifier), app.bundleIdentifier)

View File

@@ -39,15 +39,11 @@ final class RemoveAppOperation: ResultOperation<InstalledApp>
let resignedBundleIdentifier = installedApp.resignedBundleIdentifier
do {
let res = try remove_app(app_id: resignedBundleIdentifier)
if case Uhoh.Bad(let code) = res {
self.finish(.failure(minimuxer_to_operation(code: code)))
}
} catch Uhoh.Bad(let code) {
self.finish(.failure(minimuxer_to_operation(code: code)))
try remove_app(resignedBundleIdentifier)
} catch {
self.finish(.failure(ALTServerError(.appDeletionFailed)))
return self.finish(.failure(error))
}
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
self.progress.completedUnitCount += 1

View File

@@ -61,6 +61,7 @@ final class ResignAppOperation: ResultOperation<ALTApplication>
{
let destinationURL = InstalledApp.refreshedIPAURL(for: app)
try FileManager.default.copyItem(at: resignedURL, to: destinationURL, shouldReplace: true)
print("Successfully resigned app to \(destinationURL.absoluteString)")
// Use appBundleURL since we need an app bundle, not .ipa.
guard let resignedApplication = ALTApplication(fileURL: appBundleURL) else { throw OperationError.invalidApp }
@@ -147,6 +148,14 @@ private extension ResignAppOperation
infoDictionary[Bundle.Info.exportedUTIs] = exportedUTIs
try (infoDictionary as NSDictionary).write(to: bundle.infoPlistURL)
// Remove _CodeSignature folder (if it exists) because it will be added when resigning and it may have files that aren't overwritten when resigning
// These files might be the cause of some ApplicationVerificationFailed errors
let codeSignaturePath = bundle.bundleURL.appendingPathComponent("_CodeSignature").absoluteString.replacingOccurrences(of: "file://", with: "")
if FileManager.default.fileExists(atPath: codeSignaturePath) {
try FileManager.default.removeItem(atPath: codeSignaturePath)
print("Removed _CodeSignature folder at \(codeSignaturePath)")
}
}
DispatchQueue.global().async {

View File

@@ -9,6 +9,7 @@ import Foundation
import Network
import AltStoreCore
import minimuxer
@objc(SendAppOperation)
final class SendAppOperation: ResultOperation<()>
@@ -44,24 +45,18 @@ final class SendAppOperation: ResultOperation<()>
print("AFC App `fileURL`: \(fileURL.absoluteString)")
let ns_bundle = NSString(string: app.bundleIdentifier)
let ns_bundle_ptr = UnsafeMutablePointer<CChar>(mutating: ns_bundle.utf8String)
if let data = NSData(contentsOf: fileURL) {
let pls = UnsafeMutablePointer<UInt8>.allocate(capacity: data.length)
for (index, data) in data.enumerated() {
pls[index] = data
}
let res = minimuxer_yeet_app_afc(ns_bundle_ptr, pls, UInt(data.length))
if res == 0 {
print("minimuxer_yeet_app_afc `res` == \(res)")
self.progress.completedUnitCount += 1
self.finish(.success(()))
} else {
self.finish(.failure(minimuxer_to_operation(code: res)))
do {
let bytes = Data(data).toRustByteSlice()
try yeet_app_afc(app.bundleIdentifier, bytes.forRust())
} catch {
return self.finish(.failure(error))
}
self.progress.completedUnitCount += 1
self.finish(.success(()))
} else {
print("IPA doesn't exist????")
self.finish(.failure(ALTServerError(.underlyingError)))
}
}

View File

@@ -3,18 +3,18 @@
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>A72ZC8AJ5X.com.SideStore.AltStore</string>
<string>XYZ0123456.com.SideStore.SideStore</string>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.siri</key>
<true/>
<key>com.apple.developer.team-identifier</key>
<string>A72ZC8AJ5X</string>
<string>XYZ0123456</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.SideStore.AltStore</string>
<string>group.com.SideStore.SideStore</string>
</array>
<key>get-task-allow</key>
<true/>
</dict>
</plist>
</plist>

View File

@@ -1,269 +0,0 @@
{
"name": "AltStore",
"identifier": "com.rileytestut.AltStore",
"sourceURL": "https://cdn.altstore.io/file/altstore/apps.json",
"apps": [
{
"name": "AltStore",
"bundleIdentifier": "com.rileytestut.AltStore",
"developerName": "Riley Testut",
"version": "1.5.1",
"versionDate": "2022-07-14T12:00:00-05:00",
"versionDescription": "This update fixes the following issues:\n\n• Using Apple IDs that contain capital letters\n• Using Apple IDs with 2FA enabled without any trusted devices\n• Repeatedly asking some users to sign in every refresh\n• \"Incorrect Apple ID or password\" error after changing Apple ID email address\n• “Application is missing application-identifier” error when sideloading or (de-)activating certain apps\n• Potential crash when receiving unknown error codes from AltServer",
"downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_5_1.ipa",
"localizedDescription": "AltStore is an alternative app store for non-jailbroken devices. \n\nThis version of AltStore allows you to install Delta, an all-in-one emulator for iOS, as well as sideload other .ipa files from the Files app.",
"iconURL": "https://user-images.githubusercontent.com/705880/65270980-1eb96f80-dad1-11e9-9367-78ccd25ceb02.png",
"tintColor": "018084",
"size": 5465976,
"screenshotURLs": [
"https://user-images.githubusercontent.com/705880/78942028-acf54300-7a6d-11ea-821c-5bb7a9b3e73a.PNG",
"https://user-images.githubusercontent.com/705880/78942222-0fe6da00-7a6e-11ea-9f2a-dda16157583c.PNG",
"https://user-images.githubusercontent.com/705880/65605577-332cba80-df5e-11e9-9f00-b369ce974f71.PNG"
],
"permissions": [
{
"type": "background-fetch",
"usageDescription": "AltStore periodically refreshes apps in the background to prevent them from expiring."
},
{
"type": "background-audio",
"usageDescription": "Allows AltStore to run longer than 30 seconds when refreshing apps in background."
}
]
},
{
"name": "AltStore",
"bundleIdentifier": "com.rileytestut.AltStore.Beta",
"developerName": "Riley Testut",
"subtitle": "An alternative App Store for iOS.",
"version": "1.6b2",
"versionDate": "2022-09-21T13:00:00-05:00",
"versionDescription": "• Fixed “error migrating persistent store” issue on launch\n\nPREVIOUS VERSION\n\nLock Screen Widget (iOS 16+)\n• Counts down days until AltStore expires\n• Comes in 2 different styles: “icon” and “text”\n\nError Log\n• View past errors in more detail\n• Tap an error to copy the error message or error code\n• Search for error code directly in AltStore FAQ",
"downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_6_b2.ipa",
"localizedDescription": "AltStore is an alternative app store for non-jailbroken devices. \n\nThis beta release of AltStore adds support for 3rd party sources, allowing you to download apps from other developers directly through AltStore.",
"iconURL": "https://user-images.githubusercontent.com/705880/65270980-1eb96f80-dad1-11e9-9367-78ccd25ceb02.png",
"tintColor": "018084",
"size": 5465933,
"beta": true,
"screenshotURLs": [
"https://user-images.githubusercontent.com/705880/78942028-acf54300-7a6d-11ea-821c-5bb7a9b3e73a.PNG",
"https://user-images.githubusercontent.com/705880/78942222-0fe6da00-7a6e-11ea-9f2a-dda16157583c.PNG",
"https://user-images.githubusercontent.com/705880/65605577-332cba80-df5e-11e9-9f00-b369ce974f71.PNG"
],
"permissions": [
{
"type": "background-fetch",
"usageDescription": "AltStore periodically refreshes apps in the background to prevent them from expiring."
},
{
"type": "background-audio",
"usageDescription": "Allows AltStore to run longer than 30 seconds when refreshing apps in background."
}
]
},
{
"name": "Delta",
"bundleIdentifier": "com.rileytestut.Delta",
"developerName": "Riley Testut",
"subtitle": "Classic games in your pocket.",
"version": "1.3.1",
"versionDate": "2021-12-02T13:30:00-08:00",
"versionDescription": "• Fixes game artwork not loading\n• Fixes using deprecated DeSmuME core over melonDS core for some users",
"downloadURL": "https://cdn.altstore.io/file/altstore/apps/delta/1_3_1.ipa",
"localizedDescription": "Delta is an all-in-one emulator for iOS. Delta builds upon the strengths of its predecessor, GBA4iOS, while expanding to include support for more game systems such as NES, SNES, and N64.\n\nFEATURES\n\nSupported Game Systems\n• Nintendo Entertainment System\n• Super Nintendo Entertainment System\n• Nintendo 64\n• Game Boy (Color)\n• Game Boy Advance\n• Nintendo DS\n• And plenty more to come!\n\nController Support\n• Supports PS4, PS5, Xbox One S, Xbox Series X, and MFi game controllers.\n• Supports bluetooth (and wired) keyboards, as well as the Apple Smart Keyboard.\n• Completely customize button mappings on a per-system, per-controller basis.\n• Map buttons to special “Quick Save”, “Quick Load,” and “Fast Forward” actions.\n\nSave States\n• Save and load save states for any game from the pause menu.\n• Lock save states to prevent them from being accidentally overwritten.\n• Automatically makes backup save states to ensure you never lose your progress.\n• Support for “Quick Saves,” save states that can be quickly saved/loaded with a single button press (requires external controller).\n\nCheats\n• Supports various types of cheat codes for each supported system:\n• NES: Game Genie\n• SNES: Game Genie, Pro Action Replay\n• N64: GameShark\n• GBC: Game Genie, GameShark\n• GBA: Action Replay, Code Breaker, GameShark\n• DS: Action Replay\n\nDelta Sync\n• Sync your games, game saves, save states, cheats, controller skins, and controller mappings between devices.\n• View version histories of everything you sync and optionally restore them to earlier versions.\n• Supports both Google Drive and Dropbox.\n\nCustom Controller Skins\n• Beautiful built-in controller skins for all systems.\n• Import controller skins made by others, or even make your own to share with the world!\n\nHold Button\n• Choose buttons for Delta to hold down on your behalf, freeing up your thumbs to press other buttons instead.\n• Perfect for games that typically require one button be held down constantly (ex: run button in Mario games, or the A button in Mario Kart).\n\nFast Forward\n• Speed through slower parts of games by running the game much faster than normal.\n• Easily enable or disable from the pause menu, or optionally with a mapped button on an external controller.\n\n3D/Haptic Touch\n• Use 3D or Haptic Touch to “peek” at games, save states, and cheat codes.\n• App icon shortcuts allow quick access to your most recently played games, or optionally customize the shortcuts to always include certain games.\n\nGame Artwork\n• Automatically displays appropriate box art for imported games.\n• Change a games artwork to anything you want, or select from the built-in game artwork database.\n\nMisc.\n• Gyroscope support (WarioWare: Twisted! only)\n• Microphone support (DS only)\n• Support for delta:// URL scheme to jump directly into a specific game.\n\n**Delta and AltStore LLC are in no way affiliated with Nintendo. The name \"Nintendo\" and all associated game console names are registered trademarks of Nintendo Co., Ltd.**",
"iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png",
"tintColor": "8A28F7",
"size": 19739373,
"permissions": [
{
"type": "photos",
"usageDescription": "Allows Delta to use images from your Photo Library as game artwork."
}
],
"screenshotURLs": [
"https://user-images.githubusercontent.com/705880/65600448-f7d9be00-df54-11e9-9e3e-d4c31296da94.PNG",
"https://user-images.githubusercontent.com/705880/65813009-f2ae8600-e183-11e9-9eb7-704effc11173.png",
"https://user-images.githubusercontent.com/705880/65601117-58b5c600-df56-11e9-9c19-9a5ba5da54cf.PNG",
"https://user-images.githubusercontent.com/705880/65601125-5b182000-df56-11e9-9e7e-261480e893c0.PNG"
]
},
{
"name": "Delta",
"bundleIdentifier": "com.rileytestut.Delta.Beta",
"developerName": "Riley Testut",
"subtitle": "Classic games in your pocket.",
"version": "1.4b2",
"versionDate": "2022-08-16T08:00:00-05:00",
"versionDescription": "NEW\n• Supports Split View and Stage Manager multitasking on iPad\n• Automatically pauses + resumes emulation when switching between foreground apps with Stage Manager\n• Optimized full screen-width controller skins when using Split View, Slide Over, or Stage Manager\n• Supports controller skins with new `placement` parameter\n• Supports controller skins with custom screens that dont have explicit `outputFrame`\n\nFIXED\n• Fixed not detecting keyboard presses when remapping inputs\n• Fixed potential crash rendering game screen after changing EAGLContext\n• Fixed incorrect game screen frame when software keyboard appears on iOS 16\n• Fixed software keyboard sometimes appearing when not emulating anything",
"downloadURL": "https://cdn.altstore.io/file/altstore/apps/delta/1_4_b2.ipa",
"localizedDescription": "The next consoles for Delta are coming: this beta version of Delta brings support for playing Nintendo DS and Sega Genesis games!\n\nPlease report any issues you find to support@altstore.io. Thanks!",
"iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png",
"tintColor": "8A28F7",
"size": 42968657,
"beta": true,
"permissions": [
{
"type": "photos",
"usageDescription": "Allows Delta to use images from your Photo Library as game artwork."
}
],
"screenshotURLs": [
"https://user-images.githubusercontent.com/705880/65600448-f7d9be00-df54-11e9-9e3e-d4c31296da94.PNG",
"https://user-images.githubusercontent.com/705880/65601942-e5ad4f00-df57-11e9-9255-1463e0296e46.PNG",
"https://user-images.githubusercontent.com/705880/65813009-f2ae8600-e183-11e9-9eb7-704effc11173.png",
"https://user-images.githubusercontent.com/705880/65601117-58b5c600-df56-11e9-9c19-9a5ba5da54cf.PNG"
]
},
{
"name": "Clip",
"bundleIdentifier": "com.rileytestut.Clip",
"subtitle": "Manage your clipboard history with ease.",
"developerName": "Riley Testut",
"version": "1.0",
"versionDate": "2020-06-17T12:30:00-07:00",
"versionDescription": "Initial version 🎉",
"downloadURL": "https://f000.backblazeb2.com/file/altstore/apps/clip/1_0.ipa",
"localizedDescription": "Clip is a simple clipboard manager for iOS. \n\nUnlike other clipboard managers, Clip can continue monitoring your clipboard while in the background. No longer do you need to remember to manually open or share to an app to save your clipboard; just copy and paste as you would normally do, and Clip will have your back.\n\nIn addition to background monitoring, Clip also has these features:\n\n• Save text, URLs, and images copied to the clipboard.\n• Copy, delete, or share any clippings saved to Clip.\n• Customizable history limit.\n\nDownload Clip today, and never worry about losing your clipboard again!",
"iconURL": "https://user-images.githubusercontent.com/705880/63391981-5326f800-c37a-11e9-99d8-760fd06bb601.png",
"tintColor": "EC008C",
"size": 445056,
"permissions": [
{
"type": "background-audio",
"usageDescription": "Allows Clip to continuously monitor your clipboard in the background."
}
],
"screenshotURLs": [
"https://user-images.githubusercontent.com/705880/63391950-34286600-c37a-11e9-965f-832efe3da507.png",
"https://user-images.githubusercontent.com/705880/70830209-8e738980-1da4-11ea-8b3b-6e5fbc78adff.png"
]
},
{
"name": "Clip",
"bundleIdentifier": "com.rileytestut.Clip.Beta",
"subtitle": "Manage your clipboard history with ease.",
"developerName": "Riley Testut",
"version": "1.1b1",
"versionDate": "2020-06-17T12:30:00-07:00",
"versionDescription": "This update adds a Custom Keyboard app extension for quick access to clippings when editing text.\n\nTo enable the keyboard, go to Settings > General > Keyboard > Keyboards > Add New Keyboard... and add \"ClipBoard\". Once added, make sure to then enable \"Allow Full Access\" for ClipBoard so it can access your clippings.",
"downloadURL": "https://f000.backblazeb2.com/file/altstore/apps/clip/1_1_b1.ipa",
"localizedDescription": "Clip is a simple clipboard manager for iOS. \n\nUnlike other clipboard managers, Clip can continue monitoring your clipboard while in the background. No longer do you need to remember to manually open or share to an app to save your clipboard; just copy and paste as you would normally do, and Clip will have your back.\n\nIn addition to background monitoring, Clip also has these features:\n\n• Save text, URLs, and images copied to the clipboard.\n• Copy, delete, or share any clippings saved to Clip.\n• Customizable history limit.\n\nDownload Clip today, and never worry about losing your clipboard again!",
"iconURL": "https://user-images.githubusercontent.com/705880/63391981-5326f800-c37a-11e9-99d8-760fd06bb601.png",
"tintColor": "EC008C",
"size": 462771,
"beta": true,
"permissions": [
{
"type": "background-audio",
"usageDescription": "Allows Clip to continuously monitor your clipboard in the background."
}
],
"screenshotURLs": [
"https://user-images.githubusercontent.com/705880/63391950-34286600-c37a-11e9-965f-832efe3da507.png",
"https://user-images.githubusercontent.com/705880/70830209-8e738980-1da4-11ea-8b3b-6e5fbc78adff.png",
"https://user-images.githubusercontent.com/705880/84842227-70a80b00-aff9-11ea-8b04-bedb1f49c4a7.PNG",
"https://user-images.githubusercontent.com/705880/84842231-7271ce80-aff9-11ea-9272-e128aeceb95b.PNG"
]
}
],
"news": [
{
"title": "Delta Gaining DS Support",
"identifier": "delta-ds-support",
"caption": "Available this Saturday for patrons, coming soon for everyone else.",
"tintColor": "8A28F7",
"imageURL": "https://user-images.githubusercontent.com/705880/65603159-0676a400-df5a-11e9-882e-dc5566f4d50a.png",
"date": "2019-09-25",
"notify": false
},
{
"title": "Delta Now Available",
"identifier": "delta-now-available",
"caption": "Finally, relive your favorite NES, SNES, GB(C), GBA, and N64 games.",
"tintColor": "8A28F7",
"imageURL": "https://user-images.githubusercontent.com/705880/65604130-c1ec0800-df5b-11e9-8150-7657c474e3c3.png",
"appID": "com.rileytestut.Delta",
"date": "2019-09-28",
"notify": true
},
{
"title": "Sideloading is Here!",
"identifier": "sideloading-is-here",
"caption": "Update to AltStore 1.3 to install any app directly from Files.",
"tintColor": "018084",
"imageURL": "https://user-images.githubusercontent.com/705880/79022069-02932380-7b32-11ea-8bad-49907cb97ece.png",
"date": "2020-04-10T13:00:00-07:00",
"notify": true
},
{
"title": "iOS 13.4 Fixes App Crashes",
"identifier": "ios-13-4-now-available",
"caption": "Update to iOS 13.4 to fix some sideloaded apps crashing on launch.",
"tintColor": "34C759",
"date": "2020-04-10T13:30:00-07:00",
"notify": false
},
{
"title": "Clip Now Available!",
"identifier": "clip-now-available",
"caption": "Finally, a clipboard manager that can run in the background — no jailbreak required.",
"tintColor": "EC008C",
"imageURL": "https://user-images.githubusercontent.com/705880/65606598-04afdf00-df60-11e9-8f93-af6345d39557.png",
"appID": "com.rileytestut.Clip",
"date": "2020-06-17",
"notify": true
},
{
"title": "Delta, Meet Nintendo DS",
"identifier": "delta-meet-ds",
"caption": "Update to Delta 1.3 to relive all your favorite Nintendo DS games.",
"tintColor": "8A28F7",
"imageURL": "https://user-images.githubusercontent.com/705880/115617602-6ce2b600-a2a6-11eb-984e-2197a30c71e2.png",
"appID": "com.rileytestut.Delta",
"date": "2021-04-21",
"notify": true
},
{
"title": "#StandWithUkraine",
"identifier": "support-ukraine",
"caption": "Find out how you can help support those impacted by the Russian invasion.",
"tintColor": "003e80",
"imageURL": "https://user-images.githubusercontent.com/705880/156053447-a158cac7-df5f-4497-8025-15c3c2e10b48.png",
"url": "https://linktr.ee/razomforukraine",
"date": "2022-03-01",
"notify": false
},
{
"title": "The Biggest AltServer Update Yet!",
"identifier": "altserver-1-5",
"caption": "Update to AltServer 1.5 to use AltJIT and other exciting new features.",
"tintColor": "018084",
"imageURL": "https://user-images.githubusercontent.com/705880/166509576-744be578-6868-4b7d-b4fd-b9418c084327.png",
"url": "https://faq.altstore.io/release-notes/altserver",
"date": "2022-05-03",
"notify": true
},
{
"title": "More Apps in AltStore!",
"identifier": "trusted-sources",
"caption": "Update to AltStore 1.5 to easily download some of our favorite apps.",
"tintColor": "00CAB3",
"imageURL": "https://user-images.githubusercontent.com/705880/167026375-ddcb004f-7160-405c-b3e3-87a6795d2f43.png",
"url": "https://faq.altstore.io/release-notes/altstore",
"date": "2022-05-05",
"notify": true
},
{
"title": "New to AltStore?",
"identifier": "updated-faq",
"caption": "Check out our updated guide to learn how to sideload apps!",
"tintColor": "018084",
"url": "https://faq.altstore.io",
"date": "2022-07-28",
"notify": false
}
],
"userInfo": {
"patreonAccessToken": "uqoDoTxH8dY1ImE8tK76wxrzKk67gjyjBAcK8sD3RLU"
}
}

View File

@@ -16,15 +16,13 @@
<key>Key</key>
<string>customAnisetteURL</string>
<key>DefaultValue</key>
<string>http://ani.sidestore.io</string>
<string>https://ani.sidestore.io</string>
<key>Titles</key>
<array>
<string>SideStore</string>
<string>Macley (US)</string>
<string>Macley (DE)</string>
<string>DrPudding</string>
<string>jkcoxson (AltServer)</string>
<string>jkcoxson (Provision)</string>
<string>Sideloadly</string>
<string>Nick</string>
<string>Jawshoeadan</string>
@@ -32,12 +30,10 @@
</array>
<key>Values</key>
<array>
<string>http://ani.sidestore.io</string>
<string>https://ani.sidestore.io</string>
<string>http://us1.sternserv.tech</string>
<string>http://de1.sternserv.tech</string>
<string>https://sign.rheaa.xyz</string>
<string>http://jkcoxson.com:2095</string>
<string>http://jkcoxson.com:2052</string>
<string>https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx</string>
<string>http://45.33.29.114</string>
<string>https://anisette.jawshoeadan.me</string>