├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ └── feature-request.yml └── workflows │ ├── build.yml │ └── update-repo.yml ├── .gitignore ├── .gitmodules ├── AltStoreTweak ├── Makefile └── Tweak.m ├── INSTALL.md ├── LICENSE ├── LIVECONTAINER-LICENSE.txt ├── MSColorPicker ├── LICENSE ├── MSColorPicker │ ├── MSColorComponentView.h │ ├── MSColorComponentView.m │ ├── MSColorPicker.h │ ├── MSColorSelectionView.h │ ├── MSColorSelectionView.m │ ├── MSColorSelectionViewController.h │ ├── MSColorSelectionViewController.m │ ├── MSColorUtils.h │ ├── MSColorUtils.m │ ├── MSColorView.h │ ├── MSColorWheelView.h │ ├── MSColorWheelView.m │ ├── MSHSBView.h │ ├── MSHSBView.m │ ├── MSRGBView.h │ ├── MSRGBView.m │ ├── MSSliderView.h │ ├── MSSliderView.m │ ├── MSThumbView.h │ ├── MSThumbView.m │ ├── UIControl+HitTestEdgeInsets.h │ └── UIControl+HitTestEdgeInsets.m └── README.md ├── Makefile ├── NOTICE.md ├── README.md ├── Resources ├── AppIcon20x20.png ├── AppIcon20x20@2x.png ├── AppIcon40x40.png ├── AppIcon40x40@2x.png ├── AppIcon40x40@3x.png ├── AppIcon60x60.png ├── AppIcon60x60@2x.png ├── AppIcon60x60@3x.png ├── AppIcon76x76.png ├── AppIcon76x76@2x.png ├── ColorWheel.png ├── Cydia.png ├── Icons │ ├── Contents.json │ ├── Default.png │ ├── Default.zip │ ├── Pride.png │ ├── Pride.zip │ └── PrideFixed.png ├── Info.plist ├── PrideIcon20x20.png ├── PrideIcon20x20@2x.png ├── PrideIcon40x40.png ├── PrideIcon40x40@2x.png ├── PrideIcon40x40@3x.png ├── PrideIcon60x60.png ├── PrideIcon60x60@2x.png ├── PrideIcon60x60@3x.png ├── PrideIcon76x76.png ├── PrideIcon76x76@2x.png ├── Sileo.png ├── Zebra.png ├── en.lproj │ └── Localizable.strings ├── geode_logo.pdf └── web │ ├── index.html │ ├── script.js │ └── styles.css ├── TestJITLess ├── Makefile └── TestJITLess.m ├── TweakLoader ├── FBSSerialQueue.m ├── Makefile ├── NSBundle+FixCydiaSubstrate.m ├── NSFileManager+GuestHooks.m ├── SecItem.m ├── TweakLoader.m ├── UIKit+GuestHooks.m ├── utils.h └── utils.m ├── VERSION ├── WebServerLib ├── Makefile └── WebServer.m ├── ZSign ├── Makefile ├── Utils.hpp ├── Utils.mm ├── archo.cpp ├── archo.h ├── bundle.cpp ├── bundle.h ├── common │ ├── base64.cpp │ ├── base64.h │ ├── common.cpp │ ├── common.h │ ├── json.cpp │ ├── json.h │ └── mach-o.h ├── macho.cpp ├── macho.h ├── openssl.cpp ├── openssl.h ├── signing.cpp ├── signing.h ├── zsign.hpp ├── zsign.mm ├── zsigner.h └── zsigner.m ├── control ├── download_openssl.sh ├── entitlements.xml ├── screenshots ├── StikJIT.conf ├── altstore.psd ├── install-1.png ├── install-2.png ├── install-3.png ├── install-4.png ├── install-altstore.png ├── install-trollstore.png ├── jitstreamer-manual.png ├── jitstreamer-manual.psd ├── stikdebug.png ├── stikdebug.psd ├── stosvpn.png ├── stosvpn.psd ├── thumbnail.png ├── thumbnail.psd ├── webserverguide.png └── webserverguide.psd ├── src ├── AppDelegate.h ├── AppDelegate.m ├── GeodeInstaller.h ├── GeodeInstaller.m ├── IconView.h ├── IconView.m ├── IntroVC.h ├── IntroVC.m ├── LCUtils │ ├── FoundationPrivate.h │ ├── GCSharedUtils.h │ ├── GCSharedUtils.m │ ├── LCAppInfo.h │ ├── LCAppInfo.m │ ├── LCAppModel.h │ ├── LCAppModel.m │ ├── LCMachOUtils.m │ ├── LCUtils.h │ ├── LCUtils.m │ ├── NSUserDefaults.m │ ├── Shared.h │ ├── Shared.m │ ├── TPRO.h │ ├── UIApplicationMain.m │ ├── UIKitPrivate.h │ ├── archive.h │ ├── archive_entry.h │ ├── dyld_bypass_validation.m │ ├── unarchive.h │ ├── unarchive.m │ ├── utils.h │ └── utils.m ├── Localization.h ├── Localization.m ├── LogsView.h ├── LogsView.m ├── RootViewController.h ├── RootViewController.m ├── SettingsVC.h ├── SettingsVC.m ├── Theming.h ├── Theming.m ├── Utils.h ├── Utils.m ├── VerifyInstall.h ├── VerifyInstall.m ├── WebServer.h ├── WebServer.m ├── components │ ├── LogUtils.h │ ├── LogUtils.m │ ├── ProgressBar.h │ └── ProgressBar.m └── main.m └── ts-entitlements.xml /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: "0" 3 | ColumnLimit: "180" 4 | IncludeIsMainRegex: "0" 5 | IndentWidth: "4" 6 | ObjCBlockIndentWidth: "4" 7 | PointerAlignment: Left 8 | SpaceBeforeAssignmentOperators: "true" 9 | SpaceBeforeParens: ControlStatements 10 | Cpp11BracedListStyle: false 11 | TabWidth: "4" 12 | UseTab: Always 13 | AllowShortBlocksOnASingleLine: Always 14 | IncludeBlocks: Preserve 15 | LineEnding: LF 16 | ObjCBreakBeforeNestedBlockParam: false 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report with the iOS launcher (not Geode Loader itself) where something is not working as expected. 3 | title: "[BUG] " 4 | labels: [ "bug" ] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for filing a bug report. 10 | 11 | ## Important 12 | - Please search existing issues to avoid creating duplicates. 13 | - Ensure you are running the latest version of the launcher, in case the bug had been fixed already. 14 | - For enhancement requests, please use the Feature Request template. 15 | - Please fill out the template below to the best of your ability. 16 | - type: checkboxes 17 | attributes: 18 | label: Launcher Issue 19 | description: | 20 | The Geode iOS Launcher repository is for issues of the *iOS Launcher*, not the ***Geode Loader itself*** or individual mods created by other developers. 21 | When submitting a bug report, please make sure that the bug is *actually* related to the ***iOS Launcher*** and not related to the Geode Loader, or a mod. 22 | Failing to do this will get your issue *closed without explanation*. 23 | options: 24 | - label: I confirm that this bug is NOT related to the geode loader and a mod but directly to the iOS Launcher itself. 25 | required: true 26 | - type: input 27 | id: version 28 | attributes: 29 | label: Version 30 | description: What version of the iOS Launcher were you using when this bug was encountered? If you do not know where to find this, open the settings and scroll to the "About" section. The version should be next to the "iOS Launcher" text. 31 | placeholder: "Example: v0.5.0" 32 | validations: 33 | required: true 34 | - type: dropdown 35 | id: variant 36 | attributes: 37 | label: Variant 38 | description: What variant of the iOS Launcher are you using when this bug occured? 39 | multiple: true 40 | options: 41 | - IPA (Non-Jailbroken) 42 | - TIPA (TrollStore + Jailbroken) 43 | validations: 44 | required: true 45 | - type: textarea 46 | id: what-happened 47 | attributes: 48 | label: What happened? 49 | description: Also tell us, what did you expect to happen? Attach screenshots here as necessary. 50 | placeholder: Please be specific and concice with describing the bug. 51 | validations: 52 | required: true 53 | - type: textarea 54 | id: reproduce 55 | attributes: 56 | label: How do you reproduce this bug? 57 | description: Please provide instructions that can be easily reproduced. Attach screenshots here as necessary. 58 | validations: 59 | required: true 60 | - type: textarea 61 | id: logs 62 | attributes: 63 | label: Relevant log output 64 | description: Please send any relevant app logs found in the "View Last App Logs" section of the Launcher. This will be automatically formatted into code, so no need for backticks. 65 | render: shell 66 | - type: textarea 67 | id: crash 68 | attributes: 69 | label: Relevant crash log 70 | description: If the launcher had crashed, please send any relevant crash logs. This can be found in **Settings > Privacy & Security > Analytics & Improvements > Analytics Data > GeodeLauncher_PleaseDoNotShortenTheExecutableNameBecauseItIsUsedToReserveSpaceForOverwritingThankYou** 71 | - type: textarea 72 | id: additional-info 73 | attributes: 74 | label: Additional Information 75 | description: Any additional information you wish to provide. Please add anything which did not fit into the other sections here. 76 | placeholder: "Example: This is likely caused by X because..." 77 | validations: 78 | required: false 79 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Request a new feature or a change to an existing one. 3 | labels: [ "enhancement" ] 4 | body: 5 | - type: textarea 6 | id: feature-description 7 | attributes: 8 | label: What feature would you like to see? 9 | description: Please describe the feature you would like to see. 10 | placeholder: I would like to see... 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: other-details 15 | attributes: 16 | label: Other details 17 | placeholder: Add any additional details about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-22.04 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Install Theos 18 | run: | 19 | # uncomment for swift 20 | #sudo apt-get install -y libtinfo6 21 | 22 | echo "installing theos" 23 | export THEOS=$(mktemp -d theos.XXXXX) 24 | echo "THEOS=$THEOS" >> $GITHUB_ENV 25 | 26 | # set architecture for the download urls below 27 | export ARCH=$(uname -m) 28 | 29 | # depth=1 for SPEED 30 | git clone --recursive https://github.com/theos/theos.git $THEOS --depth=1 31 | 32 | # uncomment for swift 33 | #curl -sL https://github.com/kabiroberai/swift-toolchain-linux/releases/download/v2.3.0/swift-5.8-ubuntu20.04.tar.xz | tar -xJvf - -C $THEOS/toolchain/ 34 | 35 | echo "downloading llvm toolchain" 36 | curl -sL https://github.com/L1ghtmann/llvm-project/releases/latest/download/iOSToolchain-$ARCH.tar.xz | tar -xJvf - -C $THEOS/toolchain/ 37 | 38 | # yoinked from theos install script 39 | if [[ -x $THEOS/toolchain/linux/iphone/bin/clang ]]; then 40 | echo "Successfully installed the toolchain!" 41 | else 42 | echo "Something appears to have gone wrong -- the toolchain is not accessible. Please try again." 43 | exit 7 44 | fi 45 | 46 | echo "installing sdk !!" 47 | $THEOS/bin/install-sdk iPhoneOS16.5 48 | 49 | - name: Replace Download Link 50 | run: | 51 | sed -i 's|__DOWNLOAD_LINK__|${{ secrets.GD_DOWNLOAD }}|' src/RootViewController.m 52 | sed -i 's|__KEY_PART1__|${{ secrets.KEYONE }}|' src/RootViewController.m 53 | sed -i 's|__KEY_PART2__|${{ secrets.KEYTWO }}|' src/RootViewController.m 54 | 55 | - name: Build 56 | run: | 57 | git submodule update --init --recursive 58 | make package FINALPACKAGE=1 STRIP=0 59 | make clean 60 | make package FINALPACKAGE=1 STRIP=0 TROLLSTORE=1 61 | 62 | - name: Upload artifact 63 | uses: actions/upload-artifact@main 64 | with: 65 | name: GeodeLauncher 66 | path: packages/*.* 67 | publish: 68 | name: Publish 69 | runs-on: ubuntu-latest 70 | needs: build 71 | if: github.ref == 'refs/heads/main' 72 | steps: 73 | - name: Checkout 74 | uses: actions/checkout@v4 75 | 76 | - name: Declare Version Variables 77 | id: ref 78 | run: | 79 | echo "version=$(cat VERSION | xargs)" >> $GITHUB_OUTPUT 80 | echo "hash=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_OUTPUT 81 | 82 | - name: Download Artifacts 83 | uses: actions/download-artifact@v4 84 | with: 85 | path: ${{ github.workspace }}/artifacts 86 | 87 | - name: Rename files 88 | run: | 89 | mv artifacts/GeodeLauncher/com.geode.launcher_${{ steps.ref.outputs.version }}.ipa artifacts/GeodeLauncher/com.geode.launcher_${{ steps.ref.outputs.hash }}.ipa 90 | mv artifacts/GeodeLauncher/com.geode.launcher_${{ steps.ref.outputs.version }}.tipa artifacts/GeodeLauncher/com.geode.launcher_${{ steps.ref.outputs.hash }}.tipa 91 | 92 | - name: Update Development Release 93 | uses: andelf/nightly-release@main 94 | env: 95 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 96 | with: 97 | tag_name: v999999.9.9 # thanks cvolton 98 | name: 'Development Release' 99 | body: Geode iOS development release for commit ${{ github.sha }}. 100 | files: | 101 | ./artifacts/GeodeLauncher/*.ipa 102 | ./artifacts/GeodeLauncher/*.tipa 103 | -------------------------------------------------------------------------------- /.github/workflows/update-repo.yml: -------------------------------------------------------------------------------- 1 | name: Update iOS Repository (ios-repo) 2 | 3 | on: 4 | release: 5 | types: [ published, unpublished, edited ] 6 | workflow_dispatch: 7 | # mainly for testing 8 | 9 | jobs: 10 | the-activation-of-the-action-of-all-time: 11 | name: Trigger Action 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/github-script@v7 15 | with: 16 | github-token: ${{ secrets.GITHUB_TOKEN }} 17 | script: | 18 | await github.rest.actions.createWorkflowDispatch({ 19 | owner: 'geode-sdk', 20 | repo: 'ios-repo', 21 | workflow_id: 'static.yml', 22 | ref: 'main' 23 | }) 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .theos/ 2 | packages/ 3 | .DS_Store 4 | .cache 5 | .clangd 6 | compile_commands.json 7 | old/ 8 | .cache/ 9 | build.sh 10 | build-ts.sh 11 | build-alt.sh 12 | build-without.sh 13 | build-debug.sh 14 | .git 15 | Resources/Frameworks/OpenSSL.framework 16 | /.vscode 17 | sec/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "fishhook"] 2 | path = fishhook 3 | url = https://github.com/khanhduytran0/fishhook 4 | [submodule "GCDWebServer"] 5 | path = GCDWebServer 6 | url = https://github.com/swisspol/GCDWebServer.git 7 | -------------------------------------------------------------------------------- /AltStoreTweak/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:latest:15.0 2 | ARCHS := arm64 3 | include $(THEOS)/makefiles/common.mk 4 | 5 | LIBRARY_NAME = AltStoreTweak 6 | 7 | AltStoreTweak_FILES = Tweak.m 8 | AltStoreTweak_CFLAGS = -fobjc-arc -DCONFIG_COMMIT=\"$(CONFIG_COMMIT)\" 9 | AltStoreTweak_INSTALL_PATH = /Applications/Geode.app/Frameworks 10 | 11 | include $(THEOS_MAKE_PATH)/library.mk 12 | -------------------------------------------------------------------------------- /AltStoreTweak/Tweak.m: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | @import Security; 3 | 4 | NSData* getKeyChainItemFromService(NSString* key, NSString* service) { 5 | NSDictionary *query = @{ 6 | (__bridge id)kSecAttrService: service, 7 | (__bridge id)kSecAttrAccount: key, 8 | (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, 9 | (__bridge id)kSecReturnData: @YES, 10 | (__bridge id)kSecAttrSynchronizable: (__bridge id)kSecAttrSynchronizableAny, 11 | (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne, 12 | }; 13 | 14 | CFTypeRef result = NULL; 15 | OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, &result); 16 | 17 | if (status == errSecSuccess) { 18 | NSData *data = (__bridge NSData *)result; 19 | return data; 20 | 21 | } else { 22 | NSLog(@"[LC] Error retrieving keychain items: %d", (int)status); 23 | return nil; 24 | } 25 | } 26 | 27 | BOOL synced = NO; 28 | __attribute__((constructor)) 29 | static void LCAltstoreHookInit(void) { 30 | if (synced) { 31 | return; 32 | } 33 | NSLog(@"[LC] LiveContainer AltStore Tweak build %s", CONFIG_COMMIT); 34 | NSString* bundleId = [NSBundle.mainBundle bundleIdentifier]; 35 | NSString* serviceName; 36 | NSArray* appGroups = [NSBundle.mainBundle.infoDictionary objectForKey:@"ALTAppGroups"]; 37 | if(appGroups == nil || [appGroups count] == 0) { 38 | NSLog(@"[LC] Invalid install method! Failed to find App Group ID."); 39 | return; 40 | } 41 | 42 | NSString* appGroupId = appGroups.firstObject; 43 | if([bundleId containsString:@"SideStore"]) { 44 | serviceName = @"com.SideStore.SideStore"; 45 | } else if ([bundleId containsString:@"AltStore"]) { 46 | serviceName = @"com.rileytestut.AltStore"; 47 | } else { 48 | NSLog(@"[LC] Failed to figure out which store this is!"); 49 | return; 50 | } 51 | NSData *certData = getKeyChainItemFromService(@"signingCertificate", serviceName); 52 | if(certData == nil) { 53 | NSLog(@"[LC] Failed to retrive certificate data!"); 54 | return; 55 | } 56 | NSData *certPassword = getKeyChainItemFromService(@"signingCertificatePassword", serviceName); 57 | if(certPassword == nil) { 58 | NSLog(@"[LC] Failed to retrive certificate password!"); 59 | return; 60 | } 61 | NSUserDefaults* appGroupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:appGroupId]; 62 | [appGroupUserDefaults setObject:certData forKey:@"LCCertificateData"]; 63 | [appGroupUserDefaults setObject:[NSString stringWithUTF8String:certPassword.bytes] forKey:@"LCCertificatePassword"]; 64 | [appGroupUserDefaults setObject:NSDate.now forKey:@"LCCertificateUpdateDate"]; 65 | NSLog(@"[LC] Successfully updated JIT-Less certificate!"); 66 | synced = YES; 67 | } 68 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation Guide 2 | > [!WARNING] 3 | > For this installation guide, it is **required** to have a computer with Administrator access, as this guide will require installing software on your computer to sideload Geode, and to obtain a pairing file for **JIT**. Additionally, **JIT** is a **__requirement__** if you want to run Geode without jailbreaking. 4 | 5 | > [!WARNING] 6 | > Do **not** use enterprise certificates in sideloaders **like ESign and Scarlet.** Those certificates **do not have the entitlements for enabling JIT** (`get-task-allow`). You **won't be able to enable JIT** if you use them. If you want to use ESign, buy a developer certificate. 7 | 8 | ## Prerequisites 9 | - iOS/iPadOS 14.0 or later 10 | - PC (Windows, Linux) or Mac OS 11 | - Apple ID (Secondary / Throwaway Recommended) 12 | - USB Cable to connect your device (Lightning / USB C) 13 | - An internet connection 14 | - Full version of Geometry Dash from the [App Store](https://apps.apple.com/us/app/geometry-dash/id625334537) 15 | - IPA / TIPA file of Geode launcher from [Releases](https://github.com/geode-sdk/ios-launcher/releases) (only get the TIPA file if you want to use the jailbreak tweak) 16 | 17 | ## Installing SideStore 18 | > [!TIP] 19 | > After installing **SideStore**, a PC is **not required** after the initial install. 20 | > You can skip this step if you are using Sideloadly or TrollStore, but you may still need to follow the first step, especially if you have never sideloaded an app before. 21 | 22 | 1. **Enabling Developer Mode (iOS 16+)** 23 | - If you are on iOS 16 or later, you will need to enable **Developer Mode** in order to launch third party apps like SideStore, otherwise you will encounter this error when attempting to sideload SideStore or any app: 24 | - ![](screenshots/install-1.png) 25 | - To enable **Developer Mode** on your iOS device, navigate to `Settings -> Privacy & Security -> Developer Mode`. Do note that this will require restarting your device. 26 | - ![](https://faq.altstore.io/~gitbook/image?url=https%3A%2F%2F2606795771-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FAfe8qEztjcTjsjjaMBY2%252Fuploads%252FWSvXhUTj8UZyGd1ex652%252FFcejvMRXgAE8k3R.jpg%3Falt%3Dmedia%26token%3D5e380cd0-be4e-406a-914b-8fa0519e1196&width=768&dpr=2&quality=100&sign=8860eb96&sv=2) 27 | - After your device restarts, you will be prompted to "Turn on Developer Mode", press "Turn On", and **Developer Mode** should be enabled! 28 | 29 | 2. **Installing SideStore** (Recommended) 30 | - Follow the steps provided here: https://sidestore.io/#get-started 31 | - SideStore is recommended if you do not want to refresh your apps while keeping your PC on. 32 | 33 | Now you can proceed with installing Geode! If you are not jailbroken, **install the IPA**. If you're jailbroken and plan to stay so, **install the TIPA** version. 34 | 35 | ## Installing Geode through SideStore 36 | > [!NOTE] 37 | > You will need to **refresh** both the store and Geode every week, otherwise you will not be able to run the app. 38 | 39 | Navigate to the **My Apps** tab, and tap the `+` button to add an app. Select the IPA for the Geode app, and the Geode app should appear on your home screen! 40 | 41 | ![](screenshots/install-altstore.png) 42 | 43 | ## Installing Geode through TrollStore 44 | Tap the `+` button and tap either **Install IPA File** or **Install From URL**, depending if you manually downloaded the TIPA file. After either selecting the TIPA file for the Geode app, or providing the URL, the Geode app should appear on your home screen! 45 | 46 | ![](screenshots/install-trollstore.png) 47 | 48 | ## Post Installation (IPA / Non-Jailbroken) 49 | > [!TIP] 50 | > You can skip this step if you installed the .tipa version of Geode, and are jailbroken. Simply follow the steps in the setup process in the app. 51 | 52 | After going through the setup process, you may have seen the warning that **Just-In-Time** (JIT) compilation is required. This is true if you want to run Geode without being jailbroken, as by default, Apple restricts how apps can manage memory. 53 | 54 | > [!WARNING] 55 | > JIT also requires you to have **Wi-Fi** connection or **Airplane Mode** enabled on your iOS device if you're not jailbroken or don't have Trollstore. 56 | 57 | There are a few ways to launch Geode with JIT, depending on both iOS version, and your use case. 58 | 59 | ### For iOS 16.7 RC, iOS 17.0 and Below 60 | > Install **TrollStore** using this guide: https://ios.cfw.guide/installing-trollstore 61 | 62 | #### TrollStore 63 | 1. Enable the **URL Scheme** setting in TrollStore. 64 | 2. Go back to Geode and tap the **Launch** button in the Geode app. 65 | 3. Geode should launch in Geometry Dash! 66 | 67 | ### For iOS 17.4+ and Later 68 | #### StikDebug 69 | > [!NOTE] 70 | > For the first time setup, you will need a computer to get a Pairing File. If you installed SideStore, you likely already have a pairing profile, meaning there is no need to reinstall Jitterbug Pair. 71 | 72 | #### Steps for downloading Jitterbug Pair (Skippable if you already have a Pairing File) 73 | 1. Go to [Jitterbug Pair](https://github.com/osy/Jitterbug/releases) and download the version for your computer. 74 | 2. Run the program with your iOS device connected to your computer. It will save a file to your computer. 75 | 3. Use iCloud, Airdrop, or a website such as [Pairdrop](https://pairdrop.net/) to upload the pairing file to your iOS device. 76 | 77 | #### Downloading StikDebug 78 | 1. Download StikDebug from the App Store: https://apps.apple.com/us/app/stikdebug/id6744045754 79 | 2. Launch the app 80 | 3. It'll ask you to add "StikDebug" as a VPN Configuration. Click "Allow" and enter your passcode to add it. 81 | 4. Go back to StikDebug and click on "Select Pairing File". You actually will need **WiFi connection** on your phone for it to download DDI. 82 | 5. After DDI is mounted you won't need WiFi. Now you can go back to Geode and click on **Launch**. 83 | 6. Geode will now launch in Geometry Dash. 84 | ![](screenshots/stikdebug.png) 85 | > [!TIP] 86 | > StikDebug works without Wi-Fi connection, just by Airplane Mode. Unfortunately, this on-device VPN does not support cellular. However, you can use cellular data after launching an app with JIT. 87 | 88 | ## Conclusion 89 | You should now be able to run Geometry Dash with Geode! You can install mods by tapping the **Geode** button on the bottom of the menu, and browse for mods to install! 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /MSColorPicker/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Maksym Shcheglov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorComponentView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorComponentView.h 3 | // 4 | // Created by Maksym Shcheglov on 2014-02-12. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @class MSSliderView; 30 | 31 | /** 32 | * The view to edit a color component. 33 | */ 34 | @interface MSColorComponentView : UIControl 35 | 36 | /** 37 | * The title. 38 | */ 39 | @property (nonatomic, copy) NSString *title; 40 | 41 | /** 42 | * The current value. The default value is 0.0. 43 | */ 44 | @property (nonatomic, assign) CGFloat value; 45 | 46 | /** 47 | * The minimum value. The default value is 0.0. 48 | */ 49 | @property (nonatomic, assign) CGFloat minimumValue; 50 | 51 | /** 52 | * The maximum value. The default value is 255.0. 53 | */ 54 | @property (nonatomic, assign) CGFloat maximumValue; 55 | 56 | /** 57 | * The format string to use apply for textfield value. \c %.f by default. 58 | */ 59 | @property (nonatomic, copy) NSString *format; 60 | 61 | /** 62 | * Sets the array of CGColorRef objects defining the color of each gradient stop on a slider's track. 63 | * The location of each gradient stop is evaluated with formula: i * width_of_the_track / number_of_colors. 64 | * 65 | * @param colors An array of CGColorRef objects. 66 | */ 67 | - (void)setColors:(NSArray *)colors __attribute__((nonnull(1))); 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorPicker.h 3 | // 4 | // Created by Maksym Shcheglov on 2015-03-06. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "MSColorSelectionViewController.h" 28 | #import "MSColorUtils.h" 29 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorSelectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorSelectionView.h 3 | // 4 | // Created by Maksym Shcheglov on 2015-04-12. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | #import "MSColorView.h" 30 | 31 | /** 32 | * The enum to define the MSColorView's types. 33 | */ 34 | typedef NS_ENUM(NSUInteger, MSSelectedColorView) { 35 | /** 36 | * The RGB color view type. 37 | */ 38 | MSSelectedColorViewRGB, 39 | /** 40 | * The HSB color view type. 41 | */ 42 | MSSelectedColorViewHSB 43 | }; 44 | 45 | /** 46 | * The MSColorSelectionView aggregates views that should be used to edit color components. 47 | */ 48 | @interface MSColorSelectionView : UIView 49 | 50 | /** 51 | * The selected color view 52 | */ 53 | @property (nonatomic, assign, readonly) MSSelectedColorView selectedIndex; 54 | 55 | /** 56 | * Makes a color component view (rgb or hsb) visible according to the index. 57 | * 58 | * @param index This index define a view to show. 59 | * @param animated If YES, the view is being appeared using an animation. 60 | */ 61 | - (void)setSelectedIndex:(MSSelectedColorView)index animated:(BOOL)animated; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorSelectionView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorSelectionView.m 3 | // 4 | // Created by Maksym Shcheglov on 2015-04-12. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "MSColorSelectionView.h" 28 | #import "src/Theming.h" 29 | #import "MSRGBView.h" 30 | #import "MSHSBView.h" 31 | 32 | @interface MSColorSelectionView () 33 | 34 | @property (nonatomic, strong) UIView *rgbColorView; 35 | @property (nonatomic, strong) UIView *hsbColorView; 36 | @property (nonatomic, assign) MSSelectedColorView selectedIndex; 37 | 38 | @end 39 | 40 | @implementation MSColorSelectionView 41 | 42 | @synthesize color = _color; 43 | @synthesize delegate; 44 | 45 | - (instancetype)initWithFrame:(CGRect)frame 46 | { 47 | self = [super initWithFrame:frame]; 48 | 49 | if (self != nil) { 50 | [self ms_init]; 51 | } 52 | 53 | return self; 54 | } 55 | 56 | - (id)initWithCoder:(NSCoder *)aDecoder 57 | { 58 | self = [super initWithCoder:aDecoder]; 59 | 60 | if (self != nil) { 61 | [self ms_init]; 62 | } 63 | 64 | return self; 65 | } 66 | 67 | - (void)setColor:(UIColor *)color 68 | { 69 | _color = color; 70 | [[self selectedView] setColor:color]; 71 | } 72 | 73 | - (void)setSelectedIndex:(MSSelectedColorView)index animated:(BOOL)animated 74 | { 75 | self.selectedIndex = index; 76 | self.selectedView.color = self.color; 77 | [UIView animateWithDuration:animated ? .5 : 0.0 animations:^{ 78 | self.rgbColorView.alpha = index == 0 ? 1.0 : 0.0; 79 | self.hsbColorView.alpha = index == 1 ? 1.0 : 0.0; 80 | } completion:nil]; 81 | } 82 | 83 | - (UIView *)selectedView 84 | { 85 | return self.selectedIndex == 0 ? self.rgbColorView : self.hsbColorView; 86 | } 87 | 88 | - (void)addColorView:(UIView *)view 89 | { 90 | view.delegate = self; 91 | [self addSubview:view]; 92 | view.translatesAutoresizingMaskIntoConstraints = NO; 93 | NSDictionary *views = NSDictionaryOfVariableBindings(view); 94 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:views]]; 95 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:views]]; 96 | } 97 | 98 | - (void)updateConstraints 99 | { 100 | [self.rgbColorView setNeedsUpdateConstraints]; 101 | [self.hsbColorView setNeedsUpdateConstraints]; 102 | [super updateConstraints]; 103 | } 104 | 105 | #pragma mark - FBColorViewDelegate methods 106 | 107 | - (void)colorView:(id)colorView didChangeColor:(UIColor *)color 108 | { 109 | self.color = color; 110 | [self.delegate colorView:self didChangeColor:self.color]; 111 | } 112 | 113 | #pragma mark - Private 114 | 115 | - (void)ms_init 116 | { 117 | self.accessibilityLabel = @"color_selection_view"; 118 | 119 | //self.backgroundColor = [UIColor whiteColor]; 120 | self.backgroundColor = [Theming getBackgroundColor]; 121 | self.rgbColorView = [[MSRGBView alloc] init]; 122 | self.hsbColorView = [[MSHSBView alloc] init]; 123 | [self addColorView:self.rgbColorView]; 124 | [self addColorView:self.hsbColorView]; 125 | [self setSelectedIndex:0 animated:NO]; 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorSelectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorSelectionViewController.h 3 | // 4 | // Created by Maksym Shcheglov on 2015-04-12. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | NS_ASSUME_NONNULL_BEGIN 30 | 31 | @class MSColorSelectionViewController; 32 | 33 | /** 34 | * The delegate of a MSColorSelectionViewController object must adopt the MSColorSelectionViewController protocol. 35 | * Methods of the protocol allow the delegate to handle color value changes. 36 | */ 37 | @protocol MSColorSelectionViewControllerDelegate 38 | 39 | @required 40 | 41 | /** 42 | * Tells the data source to return the color components. 43 | * 44 | * @param colorViewCntroller The color view. 45 | * @param color The new color value. 46 | */ 47 | - (void)colorViewController:(MSColorSelectionViewController *)colorViewCntroller didChangeColor:(UIColor *)color; 48 | 49 | @end 50 | 51 | @interface MSColorSelectionViewController : UIViewController 52 | 53 | /** 54 | * The controller's delegate. Controller notifies a delegate on color change. 55 | */ 56 | @property (nonatomic, weak, nullable) id delegate; 57 | /** 58 | * The current color value. 59 | */ 60 | @property (nonatomic, strong) UIColor *color; 61 | 62 | @end 63 | 64 | NS_ASSUME_NONNULL_END 65 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorSelectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorSelectionViewController.m 3 | // 4 | // Created by Maksym Shcheglov on 2015-04-12. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "MSColorSelectionViewController.h" 28 | #import "MSColorSelectionView.h" 29 | 30 | #import "MSColorPicker.h" 31 | 32 | #import "src/Localization.h" 33 | 34 | @interface MSColorSelectionViewController () 35 | 36 | @end 37 | 38 | @implementation MSColorSelectionViewController 39 | 40 | - (void)loadView 41 | { 42 | MSColorSelectionView *colorSelectionView = [[MSColorSelectionView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 43 | 44 | self.view = colorSelectionView; 45 | } 46 | 47 | - (void)viewDidLoad 48 | { 49 | [super viewDidLoad]; 50 | 51 | UISegmentedControl *segmentControl = [[UISegmentedControl alloc] initWithItems:@[NSLocalizedString(@"color.rgb".loc, ), NSLocalizedString(@"color.hsb".loc, )]]; 52 | [segmentControl addTarget:self action:@selector(segmentControlDidChangeValue:) forControlEvents:UIControlEventValueChanged]; 53 | segmentControl.selectedSegmentIndex = 0; 54 | self.navigationItem.titleView = segmentControl; 55 | 56 | [self.colorSelectionView setSelectedIndex:0 animated:NO]; 57 | self.colorSelectionView.delegate = self; 58 | self.edgesForExtendedLayout = UIRectEdgeNone; 59 | } 60 | 61 | - (IBAction)segmentControlDidChangeValue:(UISegmentedControl *)segmentedControl 62 | { 63 | [self.colorSelectionView setSelectedIndex:segmentedControl.selectedSegmentIndex animated:YES]; 64 | } 65 | 66 | - (void)setColor:(UIColor *)color 67 | { 68 | self.colorSelectionView.color = color; 69 | } 70 | 71 | - (UIColor *)color 72 | { 73 | return self.colorSelectionView.color; 74 | } 75 | 76 | - (void)viewWillLayoutSubviews 77 | { 78 | [self.colorSelectionView setNeedsUpdateConstraints]; 79 | [self.colorSelectionView updateConstraintsIfNeeded]; 80 | } 81 | 82 | - (MSColorSelectionView *)colorSelectionView 83 | { 84 | return (MSColorSelectionView *)self.view; 85 | } 86 | 87 | #pragma mark - MSColorViewDelegate 88 | 89 | - (void)colorView:(id)colorView didChangeColor:(UIColor *)color 90 | { 91 | [self.delegate colorViewController:self didChangeColor:color]; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorUtils.h 3 | // 4 | // Created by Maksym Shcheglov on 2014-02-13. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | /** 30 | * The structure to represent a color in the Red-Green-Blue-Alpha color space. 31 | */ 32 | typedef struct { CGFloat red, green, blue, alpha; } RGB; 33 | /** 34 | * The structure to represent a color in the hue-saturation-brightness color space. 35 | */ 36 | typedef struct { CGFloat hue, saturation, brightness, alpha; } HSB; 37 | 38 | /** 39 | * The maximum value of the RGB color components. 40 | */ 41 | extern CGFloat const MSRGBColorComponentMaxValue; 42 | /** 43 | * The maximum value of the alpha component. 44 | */ 45 | extern CGFloat const MSAlphaComponentMaxValue; 46 | /** 47 | * The maximum value of the HSB color components. 48 | */ 49 | extern CGFloat const MSHSBColorComponentMaxValue; 50 | 51 | /** 52 | * Converts an RGB color value to HSV. 53 | * Assumes r, g, and b are contained in the set [0, 1] and 54 | * returns h, s, and b in the set [0, 1]. 55 | * 56 | * @param rgb The rgb color values 57 | * @return The hsb color values 58 | */ 59 | extern HSB MSRGB2HSB(RGB rgb); 60 | 61 | /** 62 | * Converts an HSB color value to RGB. 63 | * Assumes h, s, and b are contained in the set [0, 1] and 64 | * returns r, g, and b in the set [0, 255]. 65 | * 66 | * @return The hsb color values 67 | */ 68 | extern RGB MSHSB2RGB(HSB hsb); 69 | 70 | /** 71 | * Returns the rgb values of the color components. 72 | * 73 | * @param color The color value. 74 | * 75 | * @return The values of the color components (including alpha). 76 | */ 77 | extern RGB MSRGBColorComponents(UIColor *color); 78 | 79 | /** 80 | * Converts hex string to the UIColor representation. 81 | * 82 | * @param color The color value. 83 | * 84 | * @return The hex string color value. 85 | */ 86 | extern NSString * MSHexStringFromColor(UIColor *color); 87 | 88 | /** 89 | * Converts UIColor value to the hex string. 90 | * 91 | * @param hexString The hex string color value. 92 | * 93 | * @return The color value. 94 | */ 95 | extern UIColor * MSColorFromHexString(NSString *hexString); 96 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorUtils.m 3 | // 4 | // Created by Maksym Shcheglov on 2014-02-13. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "MSColorUtils.h" 28 | 29 | CGFloat const MSRGBColorComponentMaxValue = 255.0f; 30 | CGFloat const MSAlphaComponentMaxValue = 100.0f; 31 | CGFloat const MSHSBColorComponentMaxValue = 1.0f; 32 | 33 | extern HSB MSRGB2HSB(RGB rgb) 34 | { 35 | HSB hsb = { 0.0f, 0.0f, 0.0f, 0.0f }; 36 | double rd = (double)rgb.red; 37 | double gd = (double)rgb.green; 38 | double bd = (double)rgb.blue; 39 | double max = fmax(rd, fmax(gd, bd)); 40 | double min = fmin(rd, fmin(gd, bd)); 41 | double h = 0, s, b = max; 42 | 43 | double d = max - min; 44 | 45 | s = max == 0 ? 0 : d / max; 46 | 47 | if (max == min) { 48 | h = 0; // achromatic 49 | } else { 50 | if (max == rd) { 51 | h = (gd - bd) / d + (gd < bd ? 6 : 0); 52 | } else if (max == gd) { 53 | h = (bd - rd) / d + 2; 54 | } else if (max == bd) { 55 | h = (rd - gd) / d + 4; 56 | } 57 | 58 | h /= 6; 59 | } 60 | 61 | hsb.hue = h; 62 | hsb.saturation = s; 63 | hsb.brightness = b; 64 | hsb.alpha = rgb.alpha; 65 | return hsb; 66 | } 67 | 68 | extern RGB MSHSB2RGB(HSB hsb) 69 | { 70 | RGB rgb = { 0.0f, 0.0f, 0.0f, 0.0f }; 71 | double r = 0, g = 0, b = 0; 72 | 73 | int i = hsb.hue * 6; 74 | double f = hsb.hue * 6 - i; 75 | double p = hsb.brightness * (1 - hsb.saturation); 76 | double q = hsb.brightness * (1 - f * hsb.saturation); 77 | double t = hsb.brightness * (1 - (1 - f) * hsb.saturation); 78 | 79 | switch (i % 6) { 80 | case 0: r = hsb.brightness; g = t; b = p; break; 81 | 82 | case 1: r = q; g = hsb.brightness; b = p; break; 83 | 84 | case 2: r = p; g = hsb.brightness; b = t; break; 85 | 86 | case 3: r = p; g = q; b = hsb.brightness; break; 87 | 88 | case 4: r = t; g = p; b = hsb.brightness; break; 89 | 90 | case 5: r = hsb.brightness; g = p; b = q; break; 91 | } 92 | 93 | rgb.red = r; 94 | rgb.green = g; 95 | rgb.blue = b; 96 | rgb.alpha = hsb.alpha; 97 | return rgb; 98 | } 99 | 100 | extern RGB MSRGBColorComponents(UIColor *color) 101 | { 102 | RGB result = {0.0, 0.0, 0.0, 0.0}; 103 | CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor)); 104 | 105 | if (colorSpaceModel != kCGColorSpaceModelRGB && colorSpaceModel != kCGColorSpaceModelMonochrome) { 106 | return result; 107 | } 108 | 109 | const CGFloat *components = CGColorGetComponents(color.CGColor); 110 | 111 | if (colorSpaceModel == kCGColorSpaceModelMonochrome) { 112 | result.red = result.green = result.blue = components[0]; 113 | result.alpha = components[1]; 114 | } else { 115 | result.red = components[0]; 116 | result.green = components[1]; 117 | result.blue = components[2]; 118 | result.alpha = components[3]; 119 | } 120 | 121 | return result; 122 | } 123 | 124 | extern NSString * MSHexStringFromColor(UIColor *color) 125 | { 126 | CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor)); 127 | 128 | if (colorSpaceModel != kCGColorSpaceModelRGB && colorSpaceModel != kCGColorSpaceModelMonochrome) { 129 | return nil; 130 | } 131 | 132 | const CGFloat *components = CGColorGetComponents(color.CGColor); 133 | CGFloat red, green, blue, alpha; 134 | 135 | if (colorSpaceModel == kCGColorSpaceModelMonochrome) { 136 | red = green = blue = components[0]; 137 | alpha = components[1]; 138 | } else { 139 | red = components[0]; 140 | green = components[1]; 141 | blue = components[2]; 142 | alpha = components[3]; 143 | } 144 | 145 | NSString *hexColorString = [NSString stringWithFormat:@"#%02lX%02lX%02lX%02lX", 146 | (unsigned long)(red * MSRGBColorComponentMaxValue), 147 | (unsigned long)(green * MSRGBColorComponentMaxValue), 148 | (unsigned long)(blue * MSRGBColorComponentMaxValue), 149 | (unsigned long)(alpha * MSRGBColorComponentMaxValue)]; 150 | return hexColorString; 151 | } 152 | 153 | extern UIColor * MSColorFromHexString(NSString *hexColor) 154 | { 155 | if (![hexColor hasPrefix:@"#"]) { 156 | return nil; 157 | } 158 | 159 | NSScanner *scanner = [NSScanner scannerWithString:hexColor]; 160 | [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"#"]]; 161 | 162 | unsigned hexNum; 163 | 164 | if (![scanner scanHexInt:&hexNum]) return nil; 165 | 166 | int r = (hexNum >> 24) & 0xFF; 167 | int g = (hexNum >> 16) & 0xFF; 168 | int b = (hexNum >> 8) & 0xFF; 169 | int a = (hexNum) & 0xFF; 170 | 171 | return [UIColor colorWithRed:r / MSRGBColorComponentMaxValue 172 | green:g / MSRGBColorComponentMaxValue 173 | blue:b / MSRGBColorComponentMaxValue 174 | alpha:a / MSRGBColorComponentMaxValue]; 175 | } 176 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorView.h 3 | // 4 | // Created by Maksym Shcheglov on 2014-02-15. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | @protocol MSColorViewDelegate; 30 | 31 | /** 32 | * The \c MSColorView protocol declares a view's interface for displaying and editing color value. 33 | */ 34 | @protocol MSColorView 35 | 36 | @required 37 | 38 | /** 39 | * The object that acts as the delegate of the receiving color selection view. 40 | */ 41 | @property (nonatomic, weak) id delegate; 42 | /** 43 | * The current color. 44 | */ 45 | @property (nonatomic, strong) UIColor* color; 46 | 47 | @end 48 | 49 | /** 50 | * The delegate of a MSColorView object must adopt the MSColorViewDelegate protocol. 51 | * Methods of the protocol allow the delegate to handle color value changes. 52 | */ 53 | @protocol MSColorViewDelegate 54 | 55 | @required 56 | 57 | /** 58 | * Tells the data source to return the color components. 59 | * 60 | * @param colorView The color view. 61 | * @param color The new color value. 62 | */ 63 | - (void)colorView:(id)colorView didChangeColor:(UIColor *)color; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorWheelView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorWheelView.h 3 | // 4 | // Created by Maksym Shcheglov on 2014-02-04. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | #import 29 | 30 | /** 31 | * The color wheel view. 32 | */ 33 | @interface MSColorWheelView : UIControl 34 | 35 | /** 36 | * The hue value. 37 | */ 38 | @property (nonatomic, assign) CGFloat hue; 39 | 40 | /** 41 | * The saturation value. 42 | */ 43 | @property (nonatomic, assign) CGFloat saturation; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSColorWheelView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSColorWheelView.m 3 | // 4 | // Created by Maksym Shcheglov on 2014-02-04. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "MSColorWheelView.h" 28 | #import 29 | #import "src/Utils.h" 30 | #import "MSColorUtils.h" 31 | #import "src/Theming.h" 32 | 33 | @interface MSColorWheelView () 34 | { 35 | @private 36 | 37 | CALayer *_indicatorLayer; 38 | CGFloat _hue; 39 | CGFloat _saturation; 40 | } 41 | 42 | @end 43 | 44 | @implementation MSColorWheelView 45 | 46 | + (BOOL)requiresConstraintBasedLayout 47 | { 48 | return YES; 49 | } 50 | 51 | - (instancetype)initWithFrame:(CGRect)frame 52 | { 53 | self = [super initWithFrame:frame]; 54 | 55 | if (self) { 56 | _hue = 0.0f; 57 | _saturation = 0.0f; 58 | 59 | self.accessibilityLabel = @"color_wheel_view"; 60 | 61 | self.layer.delegate = self; 62 | [self.layer addSublayer:[self indicatorLayer]]; 63 | 64 | // [self setSelectedPoint:CGPointMake(dimension / 2, dimension / 2)]; 65 | } 66 | 67 | return self; 68 | } 69 | 70 | - (CALayer *)indicatorLayer 71 | { 72 | if (!_indicatorLayer) { 73 | CGFloat dimension = 33; 74 | UIColor *edgeColor = [UIColor colorWithWhite:0.9 alpha:0.8]; 75 | _indicatorLayer = [CALayer layer]; 76 | _indicatorLayer.cornerRadius = dimension / 2; 77 | _indicatorLayer.borderColor = edgeColor.CGColor; 78 | _indicatorLayer.borderWidth = 2; 79 | _indicatorLayer.backgroundColor = [Theming getBackgroundColor].CGColor; 80 | _indicatorLayer.bounds = CGRectMake(0, 0, dimension, dimension); 81 | _indicatorLayer.position = CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2); 82 | _indicatorLayer.shadowColor = [UIColor blackColor].CGColor; 83 | _indicatorLayer.shadowOffset = CGSizeZero; 84 | _indicatorLayer.shadowRadius = 1; 85 | _indicatorLayer.shadowOpacity = 0.5f; 86 | } 87 | 88 | return _indicatorLayer; 89 | } 90 | 91 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 92 | { 93 | CGPoint position = [[touches anyObject] locationInView:self]; 94 | 95 | [self onTouchEventWithPosition:position]; 96 | } 97 | 98 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 99 | { 100 | CGPoint position = [[touches anyObject] locationInView:self]; 101 | 102 | [self onTouchEventWithPosition:position]; 103 | } 104 | 105 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 106 | { 107 | CGPoint position = [[touches anyObject] locationInView:self]; 108 | 109 | [self onTouchEventWithPosition:position]; 110 | } 111 | 112 | - (void)onTouchEventWithPosition:(CGPoint)point 113 | { 114 | CGFloat radius = CGRectGetWidth(self.bounds) / 2; 115 | CGFloat dist = sqrtf((radius - point.x) * (radius - point.x) + (radius - point.y) * (radius - point.y)); 116 | 117 | if (dist <= radius) { 118 | [self ms_colorWheelValueWithPosition:point hue:&_hue saturation:&_saturation]; 119 | [self setSelectedPoint:point]; 120 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 121 | } 122 | } 123 | 124 | - (void)setSelectedPoint:(CGPoint)point 125 | { 126 | UIColor *selectedColor = [UIColor colorWithHue:_hue saturation:_saturation brightness:1.0f alpha:1.0f]; 127 | 128 | [CATransaction begin]; 129 | [CATransaction setValue:(id)kCFBooleanTrue 130 | forKey:kCATransactionDisableActions]; 131 | self.indicatorLayer.position = point; 132 | self.indicatorLayer.backgroundColor = selectedColor.CGColor; 133 | [CATransaction commit]; 134 | } 135 | 136 | - (void)setHue:(CGFloat)hue 137 | { 138 | _hue = hue; 139 | [self setSelectedPoint:[self ms_selectedPoint]]; 140 | [self setNeedsDisplay]; 141 | } 142 | 143 | - (void)setSaturation:(CGFloat)saturation 144 | { 145 | _saturation = saturation; 146 | [self setSelectedPoint:[self ms_selectedPoint]]; 147 | [self setNeedsDisplay]; 148 | } 149 | 150 | #pragma mark - CALayerDelegate methods 151 | 152 | - (void)displayLayer:(CALayer *)layer 153 | { 154 | UIImage *image = [UIImage imageNamed:@"ColorWheel.png"]; 155 | if (image) { 156 | self.layer.contents = (__bridge id)image.CGImage; 157 | } 158 | self.layer.contentsGravity = kCAGravityResizeAspect; 159 | } 160 | 161 | - (void)layoutSublayersOfLayer:(CALayer *)layer 162 | { 163 | if (layer == self.layer) { 164 | [self setSelectedPoint:[self ms_selectedPoint]]; 165 | [self.layer setNeedsDisplay]; 166 | } 167 | } 168 | 169 | #pragma mark - Private methods 170 | 171 | - (CGPoint)ms_selectedPoint 172 | { 173 | CGFloat dimension = MIN(CGRectGetWidth(self.frame), CGRectGetHeight(self.frame)); 174 | CGFloat radius = _saturation * dimension / 2; 175 | CGFloat x = dimension / 2 + radius * cosf(_hue * M_PI * 2.0f); 176 | CGFloat y = dimension / 2 - radius * sinf(_hue * M_PI * 2.0f); 177 | 178 | return CGPointMake(x, y); 179 | } 180 | 181 | - (void)ms_colorWheelValueWithPosition:(CGPoint)position hue:(out CGFloat *)hue saturation:(out CGFloat *)saturation 182 | { 183 | NSInteger c = CGRectGetWidth(self.bounds) / 2; 184 | CGFloat dx = (float)(position.x - c) / c; 185 | CGFloat dy = (float)(c - position.y) / c; 186 | CGFloat d = sqrtf((float)(dx * dx + dy * dy)); 187 | 188 | *saturation = d; 189 | 190 | if (d == 0) { 191 | *hue = 0; 192 | } else { 193 | *hue = acosf((float)dx / d) / M_PI / 2.0f; 194 | 195 | if (dy < 0) { 196 | *hue = 1.0 - *hue; 197 | } 198 | } 199 | } 200 | 201 | @end 202 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSHSBView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSHSBView.h 3 | // 4 | // Created by Maksym Shcheglov on 2014-02-17. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | #import "MSColorView.h" 30 | 31 | /** 32 | * The view to edit HSB color components. 33 | */ 34 | @interface MSHSBView : UIView 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSRGBView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSRGBView.h 3 | // 4 | // Created by Maksym Shcheglov on 2014-02-16. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | #import "MSColorView.h" 30 | 31 | /** 32 | * The view to edit RGBA color components. 33 | */ 34 | @interface MSRGBView : UIView 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSSliderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSSliderView.h 3 | // 4 | // Created by Maksym Shcheglov on 2014-01-31. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | /** 30 | * The slider with a gradient track. 31 | */ 32 | @interface MSSliderView : UIControl 33 | 34 | /** 35 | * The slider's current value. The default value is 0.0. 36 | */ 37 | @property (nonatomic, assign) CGFloat value; 38 | /** 39 | * The minimum value of the slider. The default value is 0.0. 40 | */ 41 | @property (nonatomic, assign) CGFloat minimumValue; 42 | /** 43 | * The maximum value of the slider. The default value is 1.0. 44 | */ 45 | @property (nonatomic, assign) CGFloat maximumValue; 46 | /** 47 | * Sets the array of CGColorRef objects defining the color of each gradient stop on the track. 48 | * The location of each gradient stop is evaluated with formula: i * width_of_the_track / number_of_colors. 49 | * 50 | * @param colors An array of CGColorRef objects. 51 | */ 52 | - (void)setColors:(NSArray *)colors __attribute__((nonnull(1))); 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSSliderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSSliderView.m 3 | // 4 | // Created by Maksym Shcheglov on 2014-01-31. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import "MSSliderView.h" 28 | #import "MSThumbView.h" 29 | #import "UIControl+HitTestEdgeInsets.h" 30 | 31 | static const CGFloat MSSliderViewHeight = 28.0f; 32 | static const CGFloat MSSliderViewMinWidth = 150.0f; 33 | static const CGFloat MSSliderViewTrackHeight = 3.0f; 34 | static const CGFloat MSThumbViewEdgeInset = -10.0f; 35 | 36 | @interface MSSliderView () { 37 | @private 38 | 39 | MSThumbView *_thumbView; 40 | CAGradientLayer *_trackLayer; 41 | } 42 | 43 | @end 44 | 45 | @implementation MSSliderView 46 | 47 | + (BOOL)requiresConstraintBasedLayout 48 | { 49 | return YES; 50 | } 51 | 52 | - (instancetype)initWithFrame:(CGRect)frame 53 | { 54 | self = [super initWithFrame:frame]; 55 | 56 | if (self) { 57 | self.accessibilityLabel = @"color_slider"; 58 | 59 | _minimumValue = 0.0f; 60 | _maximumValue = 1.0f; 61 | _value = 0.0f; 62 | 63 | self.layer.delegate = self; 64 | 65 | _trackLayer = [CAGradientLayer layer]; 66 | _trackLayer.cornerRadius = MSSliderViewTrackHeight / 2.0f; 67 | _trackLayer.startPoint = CGPointMake(0.0f, 0.5f); 68 | _trackLayer.endPoint = CGPointMake(1.0f, 0.5f); 69 | [self.layer addSublayer:_trackLayer]; 70 | 71 | _thumbView = [[MSThumbView alloc] init]; 72 | _thumbView.hitTestEdgeInsets = UIEdgeInsetsMake(MSThumbViewEdgeInset, MSThumbViewEdgeInset, MSThumbViewEdgeInset, MSThumbViewEdgeInset); 73 | [_thumbView.gestureRecognizer addTarget:self action:@selector(ms_didPanThumbView:)]; 74 | [self addSubview:_thumbView]; 75 | 76 | __attribute__((objc_precise_lifetime)) id color = (__bridge id)[UIColor blueColor].CGColor; 77 | [self setColors:@[color, color]]; 78 | } 79 | 80 | return self; 81 | } 82 | 83 | - (CGSize)intrinsicContentSize 84 | { 85 | return CGSizeMake(MSSliderViewMinWidth, MSSliderViewHeight); 86 | } 87 | 88 | - (void)setValue:(CGFloat)value 89 | { 90 | if (value < _minimumValue) { 91 | _value = _minimumValue; 92 | } else if (value > _maximumValue) { 93 | _value = _maximumValue; 94 | } else { 95 | _value = value; 96 | } 97 | 98 | [self ms_updateThumbPositionWithValue:_value]; 99 | } 100 | 101 | - (void)setColors:(NSArray *)colors 102 | { 103 | NSParameterAssert(colors); 104 | _trackLayer.colors = colors; 105 | [self ms_updateLocations]; 106 | } 107 | 108 | - (void)layoutSubviews 109 | { 110 | [self ms_updateThumbPositionWithValue:_value]; 111 | [self ms_updateTrackLayer]; 112 | } 113 | 114 | #pragma mark - UIControl touch tracking events 115 | 116 | - (void)ms_didPanThumbView:(UIPanGestureRecognizer *)gestureRecognizer 117 | { 118 | if (gestureRecognizer.state != UIGestureRecognizerStateBegan && gestureRecognizer.state != UIGestureRecognizerStateChanged) { 119 | return; 120 | } 121 | 122 | CGPoint translation = [gestureRecognizer translationInView:self]; 123 | [gestureRecognizer setTranslation:CGPointZero inView:self]; 124 | 125 | [self ms_setValueWithTranslation:translation.x]; 126 | } 127 | 128 | - (void)ms_updateTrackLayer 129 | { 130 | CGFloat height = MSSliderViewHeight; 131 | CGFloat width = CGRectGetWidth(self.bounds); 132 | 133 | [CATransaction begin]; 134 | [CATransaction setValue:(id)kCFBooleanTrue 135 | forKey:kCATransactionDisableActions]; 136 | _trackLayer.bounds = CGRectMake(0, 0, width, MSSliderViewTrackHeight); 137 | _trackLayer.position = CGPointMake(CGRectGetWidth(self.bounds) / 2, height / 2); 138 | [CATransaction commit]; 139 | } 140 | 141 | #pragma mark - Private methods 142 | 143 | - (void)ms_setValueWithTranslation:(CGFloat)translation 144 | { 145 | CGFloat width = CGRectGetWidth(self.bounds) - CGRectGetWidth(_thumbView.bounds); 146 | CGFloat valueRange = (_maximumValue - _minimumValue); 147 | CGFloat value = _value + valueRange * translation / width; 148 | 149 | [self setValue:value]; 150 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 151 | } 152 | 153 | - (void)ms_updateLocations 154 | { 155 | NSUInteger size = [_trackLayer.colors count]; 156 | 157 | if (size == [_trackLayer.locations count]) { 158 | return; 159 | } 160 | 161 | CGFloat step = 1.0f / (size - 1); 162 | NSMutableArray *locations = [NSMutableArray array]; 163 | [locations addObject:@(0.0f)]; 164 | 165 | for (NSUInteger i = 1; i < size - 1; ++i) { 166 | [locations addObject:@(i * step)]; 167 | } 168 | 169 | [locations addObject:@(1.0f)]; 170 | _trackLayer.locations = [locations copy]; 171 | } 172 | 173 | - (void)ms_updateThumbPositionWithValue:(CGFloat)value 174 | { 175 | CGFloat thumbWidth = CGRectGetWidth(_thumbView.bounds); 176 | CGFloat thumbHeight = CGRectGetHeight(_thumbView.bounds); 177 | CGFloat width = CGRectGetWidth(self.bounds) - thumbWidth; 178 | 179 | if (width == 0) { 180 | return; 181 | } 182 | 183 | CGFloat percentage = (value - _minimumValue) / (_maximumValue - _minimumValue); 184 | CGFloat position = width * percentage; 185 | _thumbView.frame = CGRectMake(position, 0, thumbWidth, thumbHeight); 186 | } 187 | 188 | @end 189 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSThumbView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSThumbView.h 3 | // 4 | // Created by Maksym Shcheglov on 2016-05-25. 5 | // Copyright (c) 2016 Maksym Shcheglov. 6 | // License: http://opensource.org/licenses/MIT 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface MSThumbView : UIControl 13 | @property (nonatomic, strong, readonly) UIGestureRecognizer *gestureRecognizer; 14 | @end 15 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/MSThumbView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSThumbView.m 3 | // 4 | // Created by Maksym Shcheglov on 2016-05-25. 5 | // Copyright (c) 2016 Maksym Shcheglov. 6 | // License: http://opensource.org/licenses/MIT 7 | // 8 | 9 | #import "MSThumbView.h" 10 | #import "src/Theming.h" 11 | 12 | static const CGFloat MSSliderViewThumbDimension = 28.0f; 13 | 14 | @interface MSThumbView () 15 | @property (nonatomic, strong) CALayer *thumbLayer; 16 | @property (nonatomic, strong) UIGestureRecognizer *gestureRecognizer; 17 | @end 18 | 19 | @implementation MSThumbView 20 | 21 | - (instancetype)initWithFrame:(CGRect)frame 22 | { 23 | self = [super initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, MSSliderViewThumbDimension, MSSliderViewThumbDimension)]; 24 | 25 | if (self) { 26 | self.thumbLayer = [CALayer layer]; 27 | 28 | self.thumbLayer.borderColor = [[UIColor lightGrayColor] colorWithAlphaComponent:.4].CGColor; 29 | self.thumbLayer.borderWidth = .5; 30 | self.thumbLayer.cornerRadius = MSSliderViewThumbDimension / 2; 31 | //self.thumbLayer.backgroundColor = [UIColor whiteColor].CGColor; 32 | self.thumbLayer.backgroundColor = [Theming getBackgroundColor].CGColor; 33 | self.thumbLayer.shadowColor = [UIColor blackColor].CGColor; 34 | self.thumbLayer.shadowOffset = CGSizeMake(0.0, 3.0); 35 | self.thumbLayer.shadowRadius = 2; 36 | self.thumbLayer.shadowOpacity = 0.3f; 37 | [self.layer addSublayer:self.thumbLayer]; 38 | self.gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:nil action:nil]; 39 | [self addGestureRecognizer:self.gestureRecognizer]; 40 | } 41 | 42 | return self; 43 | } 44 | 45 | - (void)layoutSublayersOfLayer:(CALayer *)layer 46 | { 47 | if (layer != self.layer) { 48 | return; 49 | } 50 | 51 | [CATransaction begin]; 52 | [CATransaction setValue:(id)kCFBooleanTrue 53 | forKey:kCATransactionDisableActions]; 54 | self.thumbLayer.bounds = CGRectMake(0, 0, MSSliderViewThumbDimension, MSSliderViewThumbDimension); 55 | self.thumbLayer.position = CGPointMake(MSSliderViewThumbDimension / 2, MSSliderViewThumbDimension / 2); 56 | [CATransaction commit]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/UIControl+HitTestEdgeInsets.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+HitTestEdgeInsets.h 3 | // 4 | // Created by Maksym Shcheglov on 18/05/16. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | @interface UIControl (HitTestEdgeInsets) 31 | 32 | /** 33 | * Edge inset values are applied to a view bounds to shrink or expand the touchable area. 34 | */ 35 | @property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /MSColorPicker/MSColorPicker/UIControl+HitTestEdgeInsets.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIControl+HitTestEdgeInsets.m 3 | // 4 | // Created by Maksym Shcheglov on 18/05/16. 5 | // 6 | // The MIT License (MIT) 7 | // Copyright (c) 2015 Maksym Shcheglov 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | // 27 | 28 | 29 | #import "UIControl+HitTestEdgeInsets.h" 30 | 31 | #import 32 | 33 | @implementation UIControl (HitTestEdgeInsets) 34 | 35 | - (void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets 36 | { 37 | NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:@encode(UIEdgeInsets)]; 38 | 39 | objc_setAssociatedObject(self, @selector(hitTestEdgeInsets), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 40 | } 41 | 42 | - (UIEdgeInsets)hitTestEdgeInsets 43 | { 44 | NSValue *value = objc_getAssociatedObject(self, @selector(hitTestEdgeInsets)); 45 | 46 | if (value) { 47 | UIEdgeInsets edgeInsets; 48 | [value getValue:&edgeInsets]; 49 | return edgeInsets; 50 | } 51 | 52 | return UIEdgeInsetsZero; 53 | } 54 | 55 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 56 | { 57 | if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden || !self.userInteractionEnabled || self.alpha == 0) return [super pointInside:point withEvent:event]; 58 | 59 | CGRect relativeFrame = self.bounds; 60 | CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets); 61 | 62 | return CGRectContainsPoint(hitFrame, point); 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /MSColorPicker/README.md: -------------------------------------------------------------------------------- 1 | MSColorPicker 2 | =============== 3 | [![Build Status](https://travis-ci.org/sgl0v/MSColorPicker.svg?branch=master)](https://travis-ci.org/sgl0v/MSColorPicker) 4 | [![Version](https://img.shields.io/cocoapods/v/MSColorPicker.svg?style=flat)](http://cocoadocs.org/docsets/MSColorPicker) 5 | [![License](https://img.shields.io/cocoapods/l/MSColorPicker.svg?style=flat)](http://cocoadocs.org/docsets/MSColorPicker) 6 | [![Platform](https://img.shields.io/cocoapods/p/MSColorPicker.svg?style=flat)](http://cocoadocs.org/docsets/MSColorPicker) 7 | 8 | [[Overview](#overview) • [Installation](#installation) • [Demo](#demo) • [Requirements](#requirements) • [Licence](#licence)] 9 | 10 |
11 | 12 | ![Alt text](https://raw.githubusercontent.com/sgl0v/MSColorPicker/master/screenshots/sample_iphone.gif) 13 | ![Alt text](https://raw.githubusercontent.com/sgl0v/MSColorPicker/master/screenshots/sample_ipad.gif) 14 | 15 | ## Overview 16 | 17 | Color picker component for iOS. It allows the user to select a color with color components. Key features: 18 | 19 | - iPhone & iPad support, 20 | - Adaptive User Interface, 21 | - Supports RGB and HSB color models, 22 | - Well-documented, 23 | - Compatible with iOS 8.0 (iPhone & iPad) and higher. 24 | 25 | ## Installation 26 | 27 | MSColorPicker is available through [CocoaPods](http://cocoapods.org). To install 28 | it, simply add the following line to your Podfile: 29 | 30 | ```ruby 31 | pod "MSColorPicker" 32 | ``` 33 | 34 | ## Demo 35 | 36 | Build and run the MSColorPickerDemo project in Xcode. The demo shows how to use and integrate the MSColorPicker into your project. 37 | 38 | ## Requirements 39 | 40 | - Requires iOS 8.0 or later 41 | - Requires Automatic Reference Counting (ARC) 42 | 43 | ## Licence 44 | 45 | `MSColorPicker` is MIT-licensed. See `LICENSE`. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export ARCHS := arm64 2 | PACKAGE_FORMAT = ipa 3 | TARGET := iphone:clang:latest:14.0:13.5 4 | #TARGET := iphone:clang:16.5:14.0 5 | INSTALL_TARGET_PROCESSES = Geode 6 | 7 | include $(THEOS)/makefiles/common.mk 8 | 9 | APPLICATION_NAME = Geode 10 | 11 | ifeq ($(TROLLSTORE),1) 12 | Geode_CODESIGN_FLAGS = -Sts-entitlements.xml 13 | THEOS_PACKAGE_NAME=trollstore 14 | else 15 | Geode_CODESIGN_FLAGS = -Sentitlements.xml 16 | endif 17 | 18 | Geode_FILES = $(wildcard src/*.m) $(wildcard src/views/*.m) $(wildcard src/components/*.m) $(wildcard src/LCUtils/*.m) fishhook/fishhook.c $(wildcard MSColorPicker/MSColorPicker/*.m) $(wildcard GCDWebServer/GCDWebServer/*/*.m) 19 | Geode_FRAMEWORKS = UIKit CoreGraphics Security 20 | Geode_CFLAGS = -fobjc-arc -IGCDWebServer/GCDWebServer/Core -IGCDWebServer/GCDWebServer/Requests -IGCDWebServer/GCDWebServer/Responses 21 | Geode_LIBRARIES = archive # thats dumb 22 | $(APPLICATION_NAME)_LDFLAGS = -e _GeodeMain -rpath @loader_path/Frameworks 23 | 24 | include $(THEOS_MAKE_PATH)/application.mk 25 | SUBPROJECTS += ZSign TweakLoader WebServerLib TestJITLess AltStoreTweak 26 | include $(THEOS_MAKE_PATH)/aggregate.mk 27 | 28 | 29 | before-package:: 30 | @mv $(THEOS_STAGING_DIR)/Applications/Geode.app/Geode $(THEOS_STAGING_DIR)/Applications/Geode.app/GeodeLauncher_PleaseDoNotShortenTheExecutableNameBecauseItIsUsedToReserveSpaceForOverwritingThankYou 31 | 32 | after-package:: 33 | ifeq ($(TROLLSTORE),1) 34 | @mv "$(THEOS_PACKAGE_DIR)/trollstore_$(THEOS_PACKAGE_BASE_VERSION).ipa" "$(THEOS_PACKAGE_DIR)/com.geode.launcher_$(THEOS_PACKAGE_BASE_VERSION).tipa" 35 | endif 36 | 37 | before-all:: 38 | @sh ./download_openssl.sh 39 | 40 | # make package FINALPACKAGE=1 STRIP=0 TROLLSTORE=1 41 | -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | # NOTICE 2 | This project includes derived work portions of code from [LiveContainer](https://github.com/khanhduytran0/LiveContainer). [LiveContainer](https://github.com/khanhduytran0/LiveContainer) is licensed under the Apache Version 2.0 license. 3 | 4 | The portions included from [LiveContainer](https://github.com/khanhduytran0/LiveContainer), originally implemented in Swift, were inspired from the work and rewritten to Objective C. 5 | 6 | The following files taken or rewritten from [LiveContainer](https://github.com/khanhduytran0/LiveContainer): 7 | ``` 8 | src/main.m 9 | src/LCUtils - All files included in the directory 10 | src/LCUtils/AltStoreCore - All files included in the directory 11 | ZSign - All files included in the directory 12 | AltStoreTweak - All files included in the directory 13 | TestJITLess - All files included in the directory 14 | TweakLoader - All files included in the directory 15 | ``` 16 | 17 | For details, please refer to the Apache License 2.0 text included in either this repository, or the [LiveContainer license](https://github.com/khanhduytran0/LiveContainer/blob/main/LICENSE) 18 | 19 | A copy of the Apache License 2.0 is included with this distribution in the file [LIVECONTAINER-LICENSE.txt](./LIVECONTAINER-LICENSE.txt). 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Geode iOS Launcher 2 | Manages installing and launching **Geometry Dash** with **Geode** for iOS. 3 | 4 |

5 | 6 |

7 | 8 | ## Requirements 9 | - iOS/iPadOS 14.0 or later 10 | - Full version of Geometry Dash installed 11 | - An internet connection 12 | 13 | ## Quick Start 14 | 1. Navigate to https://github.com/geode-sdk/ios-launcher/releases, if you are not **jailbroken**, download the latest **ipa** file. If you wish to use the tweak and have **TrollStore**, download the latest **tipa** file. 15 | 2. Install the launcher by following the [Installation Guide](./INSTALL.md), or reading the **INSTALL.md** file. 16 | 3. Enjoy using Geode! 17 | 18 | ## Support 19 | 20 | If you have any further questions, or need help, be sure to join [our Discord server](https://discord.gg/9e43WMKzhp)! 21 | 22 | ## Building / Development 23 | 24 | To build this project, you must have the following prerequisites installed: 25 | - [Theos](https://theos.dev/docs/) [WSL for Windows and GNU/Linux] 26 | - [make](https://formulae.brew.sh/formula/make) [Mac OS only] 27 | 28 | After installing these, you can compile the project by running: 29 | ```bash 30 | git clone https://github.com/geode-sdk/ios-launcher 31 | cd ios-launcher 32 | make package FINALPACKAGE=1 STRIP=0 33 | ``` 34 | 35 | ## Libraries 36 | - [LiveContainer](https://github.com/khanhduytran0/LiveContainer) - Made the launcher possible! 37 | - [MSColorPicker](https://github.com/sgl0v/MSColorPicker) - Helper for Color Picking 38 | - [GCDWebServer](https://github.com/swisspol/GCDWebServer) - For the web debug panel! 39 | 40 | ## License 41 | This project is licensed under the [Boost Software License 1.0](./LICENSE). Additionally, this project also uses code from [LiveContainer](https://github.com/khanhduytran0/LiveContainer). View the [NOTICE.md](./NOTICE.md) for more details. 42 | -------------------------------------------------------------------------------- /Resources/AppIcon20x20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon20x20.png -------------------------------------------------------------------------------- /Resources/AppIcon20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon20x20@2x.png -------------------------------------------------------------------------------- /Resources/AppIcon40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon40x40.png -------------------------------------------------------------------------------- /Resources/AppIcon40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon40x40@2x.png -------------------------------------------------------------------------------- /Resources/AppIcon40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon40x40@3x.png -------------------------------------------------------------------------------- /Resources/AppIcon60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon60x60.png -------------------------------------------------------------------------------- /Resources/AppIcon60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon60x60@2x.png -------------------------------------------------------------------------------- /Resources/AppIcon60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon60x60@3x.png -------------------------------------------------------------------------------- /Resources/AppIcon76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon76x76.png -------------------------------------------------------------------------------- /Resources/AppIcon76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/AppIcon76x76@2x.png -------------------------------------------------------------------------------- /Resources/ColorWheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/ColorWheel.png -------------------------------------------------------------------------------- /Resources/Cydia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/Cydia.png -------------------------------------------------------------------------------- /Resources/Icons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "size": "60x60", 5 | "filename": "AppIcon60x60@3x.png", 6 | "folder": "Assets.xcassets/AppIcon.appiconset/", 7 | "idiom": "iphone", 8 | "scale": "3x" 9 | }, 10 | { 11 | "size": "40x40", 12 | "filename": "AppIcon40x40@2x.png", 13 | "folder": "Assets.xcassets/AppIcon.appiconset/", 14 | "idiom": "iphone", 15 | "scale": "2x" 16 | }, 17 | { 18 | "size": "40x40", 19 | "filename": "AppIcon40x40@3x.png", 20 | "folder": "Assets.xcassets/AppIcon.appiconset/", 21 | "idiom": "iphone", 22 | "scale": "3x" 23 | }, 24 | { 25 | "size": "60x60", 26 | "filename": "AppIcon60x60@2x.png", 27 | "folder": "Assets.xcassets/AppIcon.appiconset/", 28 | "idiom": "iphone", 29 | "scale": "2x" 30 | }, 31 | { 32 | "size": "20x20", 33 | "filename": "AppIcon20x20@2x.png", 34 | "folder": "Assets.xcassets/AppIcon.appiconset/", 35 | "idiom": "iphone", 36 | "scale": "2x" 37 | }, 38 | { 39 | "size": "20x20", 40 | "filename": "AppIcon60x60.png", 41 | "folder": "Assets.xcassets/AppIcon.appiconset/", 42 | "idiom": "iphone", 43 | "scale": "3x" 44 | }, 45 | { 46 | "size": "1024x1024", 47 | "filename": "1024.png", 48 | "idiom": "ios-marketing", 49 | "folder": "Assets.xcassets/AppIcon.appiconset/", 50 | "scale": "1x" 51 | }, 52 | { 53 | "size": "40x40", 54 | "filename": "AppIcon40x40@2x.png", 55 | "folder": "Assets.xcassets/AppIcon.appiconset/", 56 | "idiom": "ipad", 57 | "scale": "2x" 58 | }, 59 | { 60 | "size": "72x72", 61 | "filename": "AppIcon72x72.png", 62 | "folder": "Assets.xcassets/AppIcon.appiconset/", 63 | "idiom": "ipad", 64 | "scale": "1x" 65 | }, 66 | { 67 | "size": "76x76", 68 | "filename": "AppIcon76x76@2x.png", 69 | "folder": "Assets.xcassets/AppIcon.appiconset/", 70 | "idiom": "ipad", 71 | "scale": "2x" 72 | }, 73 | { 74 | "size": "76x76", 75 | "filename": "AppIcon76x76.png", 76 | "folder": "Assets.xcassets/AppIcon.appiconset/", 77 | "idiom": "ipad", 78 | "scale": "1x" 79 | }, 80 | { 81 | "size": "72x72", 82 | "filename": "AppIcon72x72@2x.png", 83 | "folder": "Assets.xcassets/AppIcon.appiconset/", 84 | "idiom": "ipad", 85 | "scale": "2x" 86 | }, 87 | { 88 | "size": "40x40", 89 | "filename": "AppIcon40x40.png", 90 | "folder": "Assets.xcassets/AppIcon.appiconset/", 91 | "idiom": "ipad", 92 | "scale": "1x" 93 | }, 94 | { 95 | "size": "20x20", 96 | "filename": "AppIcon20x20.png", 97 | "folder": "Assets.xcassets/AppIcon.appiconset/", 98 | "idiom": "ipad", 99 | "scale": "1x" 100 | }, 101 | { 102 | "size": "20x20", 103 | "filename": "AppIcon20x20@2x.png", 104 | "folder": "Assets.xcassets/AppIcon.appiconset/", 105 | "idiom": "ipad", 106 | "scale": "2x" 107 | } 108 | ], 109 | "info": { 110 | "author": "xcode", 111 | "version": 1 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Resources/Icons/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/Icons/Default.png -------------------------------------------------------------------------------- /Resources/Icons/Default.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/Icons/Default.zip -------------------------------------------------------------------------------- /Resources/Icons/Pride.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/Icons/Pride.png -------------------------------------------------------------------------------- /Resources/Icons/Pride.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/Icons/Pride.zip -------------------------------------------------------------------------------- /Resources/Icons/PrideFixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/Icons/PrideFixed.png -------------------------------------------------------------------------------- /Resources/PrideIcon20x20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon20x20.png -------------------------------------------------------------------------------- /Resources/PrideIcon20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon20x20@2x.png -------------------------------------------------------------------------------- /Resources/PrideIcon40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon40x40.png -------------------------------------------------------------------------------- /Resources/PrideIcon40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon40x40@2x.png -------------------------------------------------------------------------------- /Resources/PrideIcon40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon40x40@3x.png -------------------------------------------------------------------------------- /Resources/PrideIcon60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon60x60.png -------------------------------------------------------------------------------- /Resources/PrideIcon60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon60x60@2x.png -------------------------------------------------------------------------------- /Resources/PrideIcon60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon60x60@3x.png -------------------------------------------------------------------------------- /Resources/PrideIcon76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon76x76.png -------------------------------------------------------------------------------- /Resources/PrideIcon76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/PrideIcon76x76@2x.png -------------------------------------------------------------------------------- /Resources/Sileo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/Sileo.png -------------------------------------------------------------------------------- /Resources/Zebra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/Zebra.png -------------------------------------------------------------------------------- /Resources/geode_logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/Resources/geode_logo.pdf -------------------------------------------------------------------------------- /Resources/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Geode 7 | 8 | 9 | 10 | 11 |
12 |
13 |

Geode

14 |
15 |
16 |

Upload Mod

17 |
18 |

Drag & Drop Geode or a Mod file here or

19 | 20 | 21 |
22 |
23 | 24 |
25 |

Controls

26 | 27 | 28 |
29 | 30 | 31 |
32 |

Logs

33 | 34 | 35 | 36 | 37 |
38 |
39 | 40 |
41 |

Launcher Information

42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
NameValue
Host%host%
Version%version%
Geode%geode%
Geometry Dash%gd%
Device%device%
Mods Installed%mods%
76 |
77 |
78 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /Resources/web/script.js: -------------------------------------------------------------------------------- 1 | const apiUrl = window.location.origin; 2 | 3 | function onBrowse() { 4 | document.getElementById("uploadManual").click(); 5 | } 6 | 7 | function handleRequest(url, success, errorMsg, body) { 8 | let opts = { 9 | method: 'POST' 10 | } 11 | if (body) { 12 | opts["body"] = body; 13 | } 14 | fetch(`${apiUrl}/${url}`, opts).then(async resp => { 15 | if (!resp.ok) { 16 | alert(`${errorMsg} Server responded with code: ${resp.status}. View app logs for more info.`); 17 | return; 18 | } 19 | alert(success.length > 0 ? success : await resp.text()); 20 | window.location.reload(); 21 | }).catch(err => { 22 | console.error(err); 23 | alert(`${errorMsg} Error: ${err.message}`); 24 | }) 25 | } 26 | 27 | function handleUpload(files) { 28 | const fileInput = document.getElementById("uploadManual"); 29 | const formData = new FormData(); 30 | for (const file of files) { 31 | formData.append("files", file); 32 | } 33 | handleRequest("upload", "", "Couldn't upload files.", formData); 34 | if (fileInput) { 35 | fileInput.value = ''; 36 | } 37 | } 38 | 39 | const uploadArea = document.getElementById("uploadArea"); 40 | const fileInput = document.getElementById("uploadManual"); 41 | if (uploadArea) { 42 | uploadArea.addEventListener("dragover", (e) => { 43 | e.preventDefault(); 44 | uploadArea.classList.add("drag"); 45 | }); 46 | uploadArea.addEventListener("dragleave", (e) => { 47 | e.preventDefault(); 48 | uploadArea.classList.remove("drag"); 49 | }); 50 | uploadArea.addEventListener("drop", (e) => { 51 | e.preventDefault(); 52 | uploadArea.classList.remove("drag"); 53 | if (e.dataTransfer.files.length > 0) { 54 | handleUpload(e.dataTransfer.files); 55 | } 56 | }); 57 | } 58 | if (fileInput) { 59 | fileInput.addEventListener("change", (e) => { 60 | handleUpload(e.target.files); 61 | }) 62 | } 63 | 64 | function onLaunch() { 65 | handleRequest("launch", "Launched!", "Couldn't launch game."); 66 | } 67 | 68 | function onStop() { 69 | handleRequest("stop", "Stopped!", "Couldn't stop HTTP server."); 70 | } 71 | let interval; 72 | function fetchLogs() { 73 | const logsContainer = document.getElementById('logs-container'); 74 | fetch(`${apiUrl}/logs`).then(resp => { 75 | if (!resp.ok) { 76 | alert(`Couldn't fetch logs: Server responded with code: ${resp.status}. View app logs for more info.`); 77 | clearInterval(interval) 78 | return ""; 79 | } 80 | return resp.text(); 81 | }).then(logData => { 82 | logsContainer.innerHTML = ''; 83 | const lines = logData.split("\n"); 84 | lines.forEach(line => { 85 | if (line.trim()) { 86 | // 00:00:00 INFO [Thread] [Geode]: Example 87 | const splitLog = line.split(' '); 88 | if (splitLog.length > 4) { 89 | const level = splitLog[1]; 90 | /*const thread = splitLog[2]; 91 | const mod = splitLog[3]; 92 | const message = splitLog.slice(4);*/ 93 | 94 | // elements 95 | // maybe dont create new classes 96 | const logLine = document.createElement('div'); 97 | logLine.className = 'log-line'; 98 | const logTimestamp = document.createElement('span'); 99 | logTimestamp.textContent = splitLog[0]; 100 | logTimestamp.style.marginRight = '8px'; 101 | logTimestamp.style.color = '#3498db'; 102 | const logLevel = document.createElement('span'); 103 | logLevel.textContent = splitLog[1]; 104 | logLevel.className = 'log-level'; 105 | logLevel.classList.add(level.toLowerCase()) 106 | /*const logThread = document.createElement('span'); 107 | const logMod = document.createElement('span');*/ 108 | const logMsg = document.createElement('span'); 109 | logMsg.textContent = splitLog.slice(2).join(" ") 110 | logTimestamp.className = 'message'; 111 | 112 | logLine.appendChild(logTimestamp); 113 | logLine.appendChild(logLevel); 114 | logLine.appendChild(logMsg); 115 | logsContainer.appendChild(logLine); 116 | 117 | logsContainer.scrollTop = logsContainer.scrollHeight; 118 | } 119 | } 120 | }) 121 | }).catch(err => { 122 | console.error(err); 123 | alert(`Error: ${err.message}`); 124 | if (interval) clearInterval(interval) 125 | }) 126 | 127 | } 128 | if (window.containerized) { 129 | fetchLogs(); 130 | function onStartLogs() { 131 | clearInterval(interval) 132 | interval = setInterval(() => { 133 | fetchLogs(); 134 | }, 1000) 135 | } 136 | function onStopLogs() { 137 | clearInterval(interval) 138 | } 139 | onStartLogs(); 140 | } 141 | -------------------------------------------------------------------------------- /Resources/web/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --accent: %accent%; 3 | --danger: #dc3545; 4 | --success: #27a745; 5 | } 6 | 7 | body { 8 | color: #344955; 9 | padding: 20px; 10 | margin: 0; 11 | box-sizing: border-box; 12 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif 13 | } 14 | 15 | button { 16 | background-color: var(--accent); 17 | color: white; 18 | border: none; 19 | padding: 10px 20px; 20 | border-radius: 5px; 21 | cursor: pointer; 22 | font-size: 14px; 23 | } 24 | 25 | .success-color { 26 | background-color: var(--success); 27 | } 28 | 29 | .danger-color { 30 | background-color: var(--danger); 31 | } 32 | 33 | .container { 34 | max-width: 100%; 35 | margin: 0; 36 | background-color: white; 37 | border-radius: 10px; 38 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); 39 | overflow: hidden; 40 | } 41 | 42 | .container header { 43 | font-size: 24px; 44 | font-weight: bold; 45 | text-align: center; 46 | } 47 | 48 | section h2 { 49 | font-size: 18px; 50 | margin-bottom: 15px; 51 | } 52 | 53 | .upload { 54 | border: 2px dashed #cccccc; 55 | padding: 30px; 56 | text-align: center; 57 | border-radius: 5px; 58 | margin-bottom: 10px; 59 | transition: border-color 0.3s ease-in-out; 60 | } 61 | 62 | .upload:hover, upload.drag { 63 | border-color: var(--accent); 64 | } 65 | 66 | .upload input { 67 | display: none; 68 | } 69 | 70 | table { 71 | width: 100%; 72 | border-collapse: collapse; 73 | margin-top: 15px; 74 | } 75 | 76 | th { 77 | background-color: #f9f9f9; 78 | font-size: 14px; 79 | } 80 | 81 | td { 82 | font-size: 14px; 83 | } 84 | 85 | th, td { 86 | padding: 12px 15px; 87 | text-align: left; 88 | border-bottom: 1px solid #eee; 89 | } 90 | 91 | tr:last-child td { 92 | border-bottom: none; 93 | } 94 | 95 | tr:hover td { 96 | background-color: #f5f5f5; 97 | } 98 | 99 | @media (max-width: 700px) { 100 | .container { 101 | border-radius: 0; 102 | } 103 | section { 104 | padding: 15px; 105 | } 106 | .upload { 107 | padding: 20px; 108 | } 109 | th, td { 110 | padding: 8px 10px; 111 | } 112 | 113 | } 114 | 115 | #logs-container { 116 | font-size: 14px; 117 | background-color: #2c2c2c; 118 | color: #ecf0f1; 119 | padding: 15px; 120 | margin-top: 15px; 121 | border-radius: 5px; 122 | height: 300px; 123 | overflow-y: auto 124 | } 125 | 126 | .log-line { 127 | margin-bottom: 5px; 128 | word-wrap: break-word; 129 | } 130 | 131 | .log-level { 132 | padding: 2px 6px; 133 | border-radius: 4px; 134 | margin-right: 4px; 135 | font-size: 12px; 136 | } 137 | .log-level.info { 138 | background-color: #0cbeff; 139 | } 140 | .log-level.debug { 141 | background-color: var(--success) 142 | } 143 | .log-level.warn { 144 | background-color: #4f4a1b; 145 | } 146 | .log-level.error { 147 | background-color: var(--danger) 148 | } 149 | -------------------------------------------------------------------------------- /TestJITLess/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | LIBRARY_NAME = TestJITLess 4 | 5 | TestJITLess_FILES = TestJITLess.m 6 | TestJITLess_CFLAGS = -fobjc-arc 7 | TestJITLess_INSTALL_PATH = /Applications/Geode.app/Frameworks 8 | 9 | include $(THEOS_MAKE_PATH)/library.mk 10 | -------------------------------------------------------------------------------- /TestJITLess/TestJITLess.m: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | __attribute__((constructor)) 4 | static void TestJITLessConstructor() { 5 | NSLog(@"JIT-less test succeed"); 6 | setenv("LC_JITLESS_TEST_LOADED", "1", 1); 7 | } 8 | -------------------------------------------------------------------------------- /TweakLoader/FBSSerialQueue.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "utils.h" 3 | #import 4 | 5 | @interface FBSSerialQueue1 : NSObject 6 | -(void)assertBarrierOnQueue1; 7 | -(void)assertBarrierOnQueue2; 8 | @end 9 | 10 | @implementation FBSSerialQueue1 11 | - (void)assertBarrierOnQueue1 { 12 | 13 | } 14 | - (void)assertBarrierOnQueue2 { 15 | 16 | } 17 | @end 18 | 19 | @implementation VSSubscriptionRegistrationCenter(GeodeHook) 20 | 21 | - (void)setCurrentSubscription:(id)sub { 22 | 23 | } 24 | 25 | @end 26 | 27 | __attribute__((constructor)) 28 | static void NSFMGuestHooksInit() { 29 | if(![NSUserDefaults.guestAppInfo[@"bypassAssertBarrierOnQueue"] boolValue]) { 30 | return; 31 | } 32 | 33 | // Use empty function to replace these functions so assertion will never fail 34 | method_exchangeImplementations(class_getInstanceMethod(NSClassFromString(@"FBSSerialQueue"), @selector(assertBarrierOnQueue)), class_getInstanceMethod(FBSSerialQueue1.class, @selector(assertBarrierOnQueue1))); 35 | 36 | method_exchangeImplementations(class_getInstanceMethod(NSClassFromString(@"FBSMainRunLoopSerialQueue"), @selector(assertBarrierOnQueue)), class_getInstanceMethod(FBSSerialQueue1.class, @selector(assertBarrierOnQueue2))); 37 | } 38 | -------------------------------------------------------------------------------- /TweakLoader/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS := arm64 2 | TARGET := iphone:clang:16.5:14.0 3 | PACKAGE_FORMAT = ipa 4 | INSTALL_TARGET_PROCESSES = Geode 5 | include $(THEOS)/makefiles/common.mk 6 | 7 | LIBRARY_NAME = TweakLoader 8 | 9 | TweakLoader_FILES = SecItem.m TweakLoader.m NSBundle+FixCydiaSubstrate.m NSFileManager+GuestHooks.m UIKit+GuestHooks.m utils.m FBSSerialQueue.m ../fishhook/fishhook.c 10 | TweakLoader_CFLAGS = -objc-arc 11 | TweakLoader_INSTALL_PATH = /Applications/Geode.app/Frameworks 12 | TweakLoader_PRIVATE_FRAMEWORKS = CoreServices FrontBoard RunningBoardServices 13 | 14 | include $(THEOS_MAKE_PATH)/library.mk 15 | -------------------------------------------------------------------------------- /TweakLoader/NSBundle+FixCydiaSubstrate.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @implementation NSBundle(FixCydiaSubstrate) 4 | 5 | - (NSString *)bundlePath { 6 | NSString *path = self.bundleURL.path; 7 | if ([path hasPrefix:@"/var"]) { 8 | return [@"/private" stringByAppendingPathComponent:path]; 9 | } 10 | return path; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /TweakLoader/NSFileManager+GuestHooks.m: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | #import "utils.h" 3 | #import "src/LCUtils/GCSharedUtils.h" 4 | 5 | BOOL isolateAppGroup = NO; 6 | __attribute__((constructor)) 7 | static void NSFMGuestHooksInit() { 8 | NSString* containerInfoPath = [[NSString stringWithUTF8String:getenv("HOME")] stringByAppendingPathComponent:@"LCContainerInfo.plist"]; 9 | NSDictionary* infoDict = [NSDictionary dictionaryWithContentsOfFile:containerInfoPath]; 10 | isolateAppGroup = [infoDict[@"isolateAppGroup"] boolValue]; 11 | swizzle(NSFileManager.class, @selector(containerURLForSecurityApplicationGroupIdentifier:), @selector(hook_containerURLForSecurityApplicationGroupIdentifier:)); 12 | } 13 | 14 | // NSFileManager simulate app group 15 | @implementation NSFileManager(GeodeHooks) 16 | - (nullable NSURL *)hook_containerURLForSecurityApplicationGroupIdentifier:(NSString *)groupIdentifier { 17 | if([groupIdentifier isEqualToString:[NSClassFromString(@"GCSharedUtils") appGroupID]]) { 18 | return [NSURL fileURLWithPath: NSUserDefaults.gcAppGroupPath]; 19 | } 20 | NSURL *result; 21 | if(isolateAppGroup) { 22 | result = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%s/LCAppGroup/%@", getenv("HOME"), groupIdentifier]]; 23 | } else { 24 | result = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/Geode/Data/AppGroup/%@", NSUserDefaults.gcAppGroupPath, groupIdentifier]]; 25 | } 26 | [NSFileManager.defaultManager createDirectoryAtURL:result withIntermediateDirectories:YES attributes:nil error:nil]; 27 | return result; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /TweakLoader/SecItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecItem.m 3 | // LiveContainer 4 | // 5 | // Created by s s on 2024/11/29. 6 | // 7 | #import 8 | #import 9 | #import "../fishhook/fishhook.h" 10 | #import "utils.h" 11 | #import 12 | 13 | OSStatus (*orig_SecItemAdd)(CFDictionaryRef attributes, CFTypeRef *result); 14 | OSStatus (*orig_SecItemCopyMatching)(CFDictionaryRef query, CFTypeRef *result); 15 | OSStatus (*orig_SecItemUpdate)(CFDictionaryRef query, CFDictionaryRef attributesToUpdate); 16 | OSStatus (*orig_SecItemDelete)(CFDictionaryRef query); 17 | 18 | NSString* accessGroup = nil; 19 | NSString* containerId = nil; 20 | 21 | OSStatus new_SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result) { 22 | NSMutableDictionary *attributesCopy = ((__bridge NSDictionary *)attributes).mutableCopy; 23 | attributesCopy[(__bridge id)kSecAttrAccessGroup] = accessGroup; 24 | // for keychain deletion in LCUI 25 | attributesCopy[@"alis"] = containerId; 26 | 27 | OSStatus status = orig_SecItemAdd((__bridge CFDictionaryRef)attributesCopy, result); 28 | if(status == errSecParam) { 29 | return orig_SecItemAdd(attributes, result); 30 | } 31 | 32 | return status; 33 | } 34 | 35 | OSStatus new_SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result) { 36 | NSMutableDictionary *queryCopy = ((__bridge NSDictionary *)query).mutableCopy; 37 | queryCopy[(__bridge id)kSecAttrAccessGroup] = accessGroup; 38 | OSStatus status = orig_SecItemCopyMatching((__bridge CFDictionaryRef)queryCopy, result); 39 | if(status == errSecParam) { 40 | // if this search don't support kSecAttrAccessGroup, we just use the original search 41 | return orig_SecItemCopyMatching(query, result); 42 | } 43 | 44 | return status; 45 | } 46 | 47 | OSStatus new_SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate) { 48 | NSMutableDictionary *queryCopy = ((__bridge NSDictionary *)query).mutableCopy; 49 | queryCopy[(__bridge id)kSecAttrAccessGroup] = accessGroup; 50 | 51 | NSMutableDictionary *attrCopy = ((__bridge NSDictionary *)attributesToUpdate).mutableCopy; 52 | attrCopy[(__bridge id)kSecAttrAccessGroup] = accessGroup; 53 | 54 | OSStatus status = orig_SecItemUpdate((__bridge CFDictionaryRef)queryCopy, (__bridge CFDictionaryRef)attrCopy); 55 | 56 | if(status == errSecParam) { 57 | return orig_SecItemUpdate(query, attributesToUpdate); 58 | } 59 | 60 | return status; 61 | } 62 | 63 | OSStatus new_SecItemDelete(CFDictionaryRef query){ 64 | NSMutableDictionary *queryCopy = ((__bridge NSDictionary *)query).mutableCopy; 65 | queryCopy[(__bridge id)kSecAttrAccessGroup] = accessGroup; 66 | OSStatus status = orig_SecItemDelete((__bridge CFDictionaryRef)queryCopy); 67 | if(status == errSecParam) { 68 | return new_SecItemDelete(query); 69 | } 70 | 71 | return status; 72 | } 73 | 74 | __attribute__((constructor)) 75 | static void SecItemGuestHooksInit() { 76 | //void SecItemGuestHooksInit() { 77 | containerId = [NSString stringWithUTF8String:getenv("HOME")].lastPathComponent; 78 | NSString* containerInfoPath = [[NSString stringWithUTF8String:getenv("HOME")] stringByAppendingPathComponent:@"LCContainerInfo.plist"]; 79 | NSDictionary* infoDict = [NSDictionary dictionaryWithContentsOfFile:containerInfoPath]; 80 | int keychainGroupId = [infoDict[@"keychainGroupId"] intValue]; 81 | //NSString* groupId = [[NSUserDefaults.gcMainBundle.bundleIdentifier componentsSeparatedByString:@"."] lastObject]; 82 | NSString* groupId; 83 | if([NSUserDefaults.gcUserDefaults boolForKey:@"LCCertificateImported"]) { 84 | groupId = [NSUserDefaults.gcUserDefaults stringForKey:@"LCCertificateTeamId"]; 85 | } else { 86 | groupId = [[NSUserDefaults.gcMainBundle.bundleIdentifier componentsSeparatedByString:@"."] lastObject]; 87 | } 88 | if(keychainGroupId == 0) { 89 | accessGroup = [NSString stringWithFormat:@"%@.com.kdt.livecontainer.shared", groupId]; 90 | } else { 91 | accessGroup = [NSString stringWithFormat:@"%@.com.kdt.livecontainer.shared.%d", groupId, keychainGroupId]; 92 | } 93 | 94 | struct rebinding rebindings[] = (struct rebinding[]){ 95 | {"SecItemAdd", (void *)new_SecItemAdd, (void **)&orig_SecItemAdd}, 96 | {"SecItemCopyMatching", (void *)new_SecItemCopyMatching, (void **)&orig_SecItemCopyMatching}, 97 | {"SecItemUpdate", (void *)new_SecItemUpdate, (void **)&orig_SecItemUpdate}, 98 | {"SecItemDelete", (void *)new_SecItemDelete, (void **)&orig_SecItemDelete} 99 | }; 100 | rebind_symbols(rebindings, sizeof(rebindings)/sizeof(struct rebinding)); 101 | } 102 | -------------------------------------------------------------------------------- /TweakLoader/TweakLoader.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #include 4 | #include 5 | #include "utils.h" 6 | 7 | static NSString *loadTweakAtURL(NSURL *url) { 8 | NSString *tweakPath = url.path; 9 | NSString *tweak = tweakPath.lastPathComponent; 10 | if (![tweakPath hasSuffix:@".dylib"]) { 11 | return nil; 12 | } 13 | void *handle = dlopen(tweakPath.UTF8String, RTLD_LAZY | RTLD_GLOBAL); 14 | const char *error = dlerror(); 15 | if (handle) { 16 | NSLog(@"Loaded tweak %@", tweak); 17 | return nil; 18 | } else if (error) { 19 | NSLog(@"Error: %s", error); 20 | return @(error); 21 | } else { 22 | NSLog(@"Error: dlopen(%@): Unknown error because dlerror() returns NULL", tweak); 23 | return [NSString stringWithFormat:@"dlopen(%@): unknown error, handle is NULL", tweakPath]; 24 | } 25 | } 26 | 27 | static void showDlerrAlert(NSString *error) { 28 | UIWindow *window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; 29 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Failed to load tweaks" message:error preferredStyle:UIAlertControllerStyleAlert]; 30 | UIAlertAction* okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { 31 | window.windowScene = nil; 32 | }]; 33 | [alert addAction:okAction]; 34 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Copy" style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { 35 | UIPasteboard.generalPasteboard.string = error; 36 | window.windowScene = nil; 37 | }]; 38 | [alert addAction:cancelAction]; 39 | window.rootViewController = [UIViewController new]; 40 | window.windowLevel = 1000; 41 | window.windowScene = (id)UIApplication.sharedApplication.connectedScenes.anyObject; 42 | [window makeKeyAndVisible]; 43 | [window.rootViewController presentViewController:alert animated:YES completion:nil]; 44 | objc_setAssociatedObject(alert, @"window", window, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 45 | } 46 | 47 | __attribute__((constructor)) 48 | static void TweakLoaderConstructor() { 49 | const char *tweakFolderC = getenv("GC_GLOBAL_TWEAKS_FOLDER"); 50 | NSString *globalTweakFolder = @(tweakFolderC); 51 | unsetenv("GC_GLOBAL_TWEAKS_FOLDER"); 52 | 53 | NSMutableArray *errors = [NSMutableArray new]; 54 | 55 | // Load CydiaSubstrate 56 | //dlopen("@loader_path/CydiaSubstrate.framework/CydiaSubstrate", RTLD_LAZY | RTLD_GLOBAL); 57 | const char *substrateError = dlerror(); 58 | if (substrateError) { 59 | [errors addObject:@(substrateError)]; 60 | } 61 | 62 | // Load global tweaks 63 | NSLog(@"Loading tweaks from the global folder"); 64 | NSArray *globalTweaks = [NSFileManager.defaultManager contentsOfDirectoryAtURL:[NSURL fileURLWithPath:globalTweakFolder] 65 | includingPropertiesForKeys:@[] options:0 error:nil]; 66 | for (NSURL *fileURL in globalTweaks) { 67 | NSString *error = loadTweakAtURL(fileURL); 68 | if (error) { 69 | [errors addObject:error]; 70 | } 71 | } 72 | 73 | // Load selected tweak folder, recursively 74 | NSString *tweakFolderName = NSUserDefaults.guestAppInfo[@"LCTweakFolder"]; 75 | if (tweakFolderName.length > 0) { 76 | NSLog(@"Loading tweaks from the selected folder"); 77 | NSString *tweakFolder = [globalTweakFolder stringByAppendingPathComponent:tweakFolderName]; 78 | NSURL *tweakFolderURL = [NSURL fileURLWithPath:tweakFolder]; 79 | NSDirectoryEnumerator *directoryEnumerator = [NSFileManager.defaultManager enumeratorAtURL:tweakFolderURL includingPropertiesForKeys:@[] options:0 errorHandler:^BOOL(NSURL *url, NSError *error) { 80 | NSLog(@"Error while enumerating tweak directory: %@", error); 81 | return YES; 82 | }]; 83 | for (NSURL *fileURL in directoryEnumerator) { 84 | NSString *error = loadTweakAtURL(fileURL); 85 | if (error) { 86 | [errors addObject:error]; 87 | } 88 | } 89 | } 90 | 91 | if (errors.count > 0) { 92 | dispatch_async(dispatch_get_main_queue(), ^{ 93 | NSString *error = [errors componentsJoinedByString:@"\n"]; 94 | showDlerrAlert(error); 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /TweakLoader/utils.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | @import ObjectiveC; 3 | 4 | void swizzle(Class class, SEL originalAction, SEL swizzledAction); 5 | void swizzleClassMethod(Class class, SEL originalAction, SEL swizzledAction); 6 | 7 | // Exported from the main executable 8 | @interface NSUserDefaults(Geode) 9 | + (instancetype)gcSharedDefaults; 10 | + (instancetype)gcUserDefaults; 11 | + (NSString *)gcAppUrlScheme; 12 | + (NSString *)gcAppGroupPath; 13 | + (NSBundle *)gcMainBundle; 14 | + (NSDictionary*)guestAppInfo; 15 | @end 16 | -------------------------------------------------------------------------------- /TweakLoader/utils.m: -------------------------------------------------------------------------------- 1 | #import "utils.h" 2 | 3 | void swizzle(Class class, SEL originalAction, SEL swizzledAction) { 4 | method_exchangeImplementations(class_getInstanceMethod(class, originalAction), class_getInstanceMethod(class, swizzledAction)); 5 | } 6 | 7 | void swizzleClassMethod(Class class, SEL originalAction, SEL swizzledAction) { 8 | method_exchangeImplementations(class_getClassMethod(class, originalAction), class_getClassMethod(class, swizzledAction)); 9 | } 10 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.0.1 2 | -------------------------------------------------------------------------------- /WebServerLib/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS := arm64 2 | TARGET := iphone:clang:16.5:14.0 3 | PACKAGE_FORMAT = ipa 4 | INSTALL_TARGET_PROCESSES = Geode 5 | include $(THEOS)/makefiles/common.mk 6 | 7 | LIBRARY_NAME = WebServer 8 | 9 | WebServer_FILES = WebServer.m 10 | WebServer_CFLAGS = -fobjc-arc 11 | WebServer_INSTALL_PATH = /Applications/Geode.app/Frameworks 12 | 13 | include $(THEOS_MAKE_PATH)/library.mk 14 | -------------------------------------------------------------------------------- /WebServerLib/WebServer.m: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | #import "src/components/LogUtils.h" 4 | #import "src/WebServer.h" 5 | 6 | __attribute__((constructor)) 7 | static void WebServerConstructor() { 8 | [NSClassFromString(@"LogUtils") log:@"WebServer Library Loaded!"]; 9 | [[NSClassFromString(@"WebServer") alloc] initServer]; 10 | } 11 | -------------------------------------------------------------------------------- /ZSign/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:latest:14.0:13.5 2 | ARCHS := arm64 3 | include $(THEOS)/makefiles/common.mk 4 | 5 | LIBRARY_NAME = ZSign 6 | 7 | ZSign_FILES = $(shell find . -name '*.cpp') $(shell find . -name '*.mm') zsigner.m 8 | ZSign_CFLAGS = -fobjc-arc -Wno-deprecated -Wno-unused-variable -Wno-module-import-in-extern-c 9 | ZSign_CCFLAGS = -std=c++11 10 | ZSign_FRAMEWORKS = OpenSSL 11 | ZSign_INSTALL_PATH = /Applications/Geode.app/Frameworks 12 | 13 | include $(THEOS_MAKE_PATH)/library.mk 14 | 15 | -------------------------------------------------------------------------------- /ZSign/Utils.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.hpp 3 | // feather 4 | // 5 | // Created by samara on 30.09.2024. 6 | // 7 | 8 | #ifndef Utils_hpp 9 | #define Utils_hpp 10 | 11 | #include 12 | 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | const char* getDocumentsDirectory(); 19 | void writeToNSLogFlood(const char* msg); 20 | void writeToNSLog(const char* msg); 21 | void refreshFile(const char* path); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | #endif /* zsign_hpp */ 28 | -------------------------------------------------------------------------------- /ZSign/Utils.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.cpp 3 | // feather 4 | // 5 | // Created by samara on 30.09.2024. 6 | // 7 | 8 | //#import "src/components/LogUtils.h" 9 | #include "Utils.hpp" 10 | #import 11 | 12 | extern "C" { 13 | 14 | const char* getDocumentsDirectory() { 15 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 16 | NSString *documentsDirectory = [paths firstObject]; 17 | const char *documentsPath = [documentsDirectory UTF8String]; 18 | return documentsPath; 19 | } 20 | 21 | void writeToNSLog(const char* msg) { 22 | NSLog(@"[LC] signer msg: %s", msg); 23 | //AppLog(@"signer msg: %s", msg); 24 | } 25 | void writeToNSLogFlood(const char* msg) { 26 | NSLog(@"[LC] signer msg: %s", msg); 27 | } 28 | 29 | // copy, remove and rename back the file to prevent crash due to kernel signature cache 30 | // see https://developer.apple.com/documentation/security/updating-mac-software 31 | void refreshFile(const char* path) { 32 | NSString* objcPath = @(path); 33 | if(![NSFileManager.defaultManager fileExistsAtPath:objcPath]) { 34 | return; 35 | } 36 | NSString* newPath = [NSString stringWithFormat:@"%s.tmp", path]; 37 | NSError* error; 38 | [NSFileManager.defaultManager copyItemAtPath:objcPath toPath:newPath error:&error]; 39 | [NSFileManager.defaultManager removeItemAtPath:objcPath error:&error]; 40 | [NSFileManager.defaultManager moveItemAtPath:newPath toPath:objcPath error:&error]; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /ZSign/archo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common/mach-o.h" 3 | #include "openssl.h" 4 | #include 5 | class ZArchO 6 | { 7 | public: 8 | ZArchO(); 9 | bool Init(uint8_t *pBase, uint32_t uLength); 10 | 11 | public: 12 | bool Sign(ZSignAsset *pSignAsset, bool bForce, const string &strBundleId, const string &strInfoPlistSHA1, const string &strInfoPlistSHA256, const string &strCodeResourcesData); 13 | void PrintInfo(); 14 | bool IsExecute(); 15 | bool InjectDyLib(bool bWeakInject, const char *szDyLibPath, bool &bCreate); 16 | uint32_t ReallocCodeSignSpace(const string &strNewFile); 17 | void uninstallDylibs(set dylibNames); 18 | bool ChangeDylibPath(const char *oldPath, const char *newPath); 19 | std::vector ListDylibs(); 20 | private: 21 | uint32_t BO(uint32_t uVal); 22 | const char *GetFileType(uint32_t uFileType); 23 | const char *GetArch(int cpuType, int cpuSubType); 24 | bool BuildCodeSignature(ZSignAsset *pSignAsset, bool bForce, const string &strBundleId, const string &strInfoPlistSHA1, const string &strInfoPlistSHA256, const string &strCodeResourcesSHA1, const string &strCodeResourcesSHA256, string &strOutput); 25 | 26 | public: 27 | uint8_t *m_pBase; 28 | uint32_t m_uLength; 29 | uint32_t m_uCodeLength; 30 | uint8_t *m_pSignBase; 31 | uint32_t m_uSignLength; 32 | string m_strInfoPlist; 33 | bool m_bEncrypted; 34 | bool m_b64; 35 | bool m_bBigEndian; 36 | bool m_bEnoughSpace; 37 | uint8_t *m_pCodeSignSegment; 38 | uint8_t *m_pLinkEditSegment; 39 | uint32_t m_uLoadCommandsFreeSpace; 40 | mach_header *m_pHeader; 41 | uint32_t m_uHeaderSize; 42 | }; 43 | -------------------------------------------------------------------------------- /ZSign/bundle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common/common.h" 3 | #include "common/json.h" 4 | #include "openssl.h" 5 | 6 | class ZAppBundle 7 | { 8 | public: 9 | ZAppBundle(); 10 | 11 | public: 12 | bool ConfigureFolderSign(ZSignAsset *pSignAsset, const string &strFolder, const string &strBundleID, const string &strBundleVersion, const string &strDisplayName, const string &strDyLibFile, bool bForce, bool bWeakInject, bool bEnableCache, bool dontGenerateEmbeddedMobileProvision); 13 | bool StartSign(bool enableCache); 14 | int GetSignCount(); 15 | private: 16 | bool SignNode(JValue &jvNode); 17 | void GetNodeChangedFiles(JValue &jvNode, bool dontGenerateEmbeddedMobileProvision); 18 | void GetChangedFiles(JValue &jvNode, vector &arrChangedFiles); 19 | void GetPlugIns(const string &strFolder, vector &arrPlugIns); 20 | int GetSignCount(JValue &jvNode); 21 | 22 | private: 23 | bool FindAppFolder(const string &strFolder, string &strAppFolder); 24 | bool GetObjectsToSign(const string &strFolder, JValue &jvInfo); 25 | bool GetSignFolderInfo(const string &strFolder, JValue &jvNode, bool bGetName = false); 26 | 27 | private: 28 | bool GenerateCodeResources(const string &strFolder, JValue &jvCodeRes); 29 | void GetFolderFiles(const string &strFolder, const string &strBaseFolder, set &setFiles); 30 | 31 | private: 32 | bool m_bForceSign; 33 | bool m_bWeakInject; 34 | string m_strDyLibPath; 35 | ZSignAsset *m_pSignAsset; 36 | string mainBundleIdentifier; 37 | JValue config; 38 | 39 | public: 40 | string m_strAppFolder; 41 | std::function progressHandler; 42 | string signFailedFiles; 43 | }; 44 | -------------------------------------------------------------------------------- /ZSign/common/base64.cpp: -------------------------------------------------------------------------------- 1 | #include "base64.h" 2 | #include 3 | 4 | #define B0(a) (a & 0xFF) 5 | #define B1(a) (a >> 8 & 0xFF) 6 | #define B2(a) (a >> 16 & 0xFF) 7 | #define B3(a) (a >> 24 & 0xFF) 8 | 9 | ZBase64::ZBase64(void) 10 | { 11 | } 12 | 13 | ZBase64::~ZBase64(void) 14 | { 15 | if (!m_arrEnc.empty()) 16 | { 17 | for (size_t i = 0; i < m_arrEnc.size(); i++) 18 | { 19 | delete[] m_arrEnc[i]; 20 | } 21 | m_arrEnc.clear(); 22 | } 23 | 24 | if (!m_arrDec.empty()) 25 | { 26 | for (size_t i = 0; i < m_arrDec.size(); i++) 27 | { 28 | delete[] m_arrDec[i]; 29 | } 30 | m_arrDec.clear(); 31 | } 32 | } 33 | 34 | char ZBase64::GetB64char(int nIndex) 35 | { 36 | static const char szTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 37 | if (nIndex >= 0 && nIndex < 64) 38 | { 39 | return szTable[nIndex]; 40 | } 41 | return '='; 42 | } 43 | 44 | int ZBase64::GetB64Index(char ch) 45 | { 46 | int index = -1; 47 | if (ch >= 'A' && ch <= 'Z') 48 | { 49 | index = ch - 'A'; 50 | } 51 | else if (ch >= 'a' && ch <= 'z') 52 | { 53 | index = ch - 'a' + 26; 54 | } 55 | else if (ch >= '0' && ch <= '9') 56 | { 57 | index = ch - '0' + 52; 58 | } 59 | else if (ch == '+') 60 | { 61 | index = 62; 62 | } 63 | else if (ch == '/') 64 | { 65 | index = 63; 66 | } 67 | return index; 68 | } 69 | 70 | const char *ZBase64::Encode(const char *szSrc, int nSrcLen) 71 | { 72 | if (0 == nSrcLen) 73 | { 74 | nSrcLen = (int)strlen(szSrc); 75 | } 76 | 77 | if (nSrcLen <= 0) 78 | { 79 | return ""; 80 | } 81 | 82 | char *szEnc = new char[nSrcLen * 3 + 128]; 83 | m_arrEnc.push_back(szEnc); 84 | 85 | int i = 0; 86 | int len = 0; 87 | printf("%i\n", len); // or else it errors?? 88 | unsigned char *psrc = (unsigned char *)szSrc; 89 | char *p64 = szEnc; 90 | for (i = 0; i < nSrcLen - 3; i += 3) 91 | { 92 | unsigned long ulTmp = *(unsigned long *)psrc; 93 | int b0 = GetB64char((B0(ulTmp) >> 2) & 0x3F); 94 | int b1 = GetB64char((B0(ulTmp) << 6 >> 2 | B1(ulTmp) >> 4) & 0x3F); 95 | int b2 = GetB64char((B1(ulTmp) << 4 >> 2 | B2(ulTmp) >> 6) & 0x3F); 96 | int b3 = GetB64char((B2(ulTmp) << 2 >> 2) & 0x3F); 97 | *((unsigned long *)p64) = b0 | b1 << 8 | b2 << 16 | b3 << 24; 98 | len += 4; 99 | p64 += 4; 100 | psrc += 3; 101 | } 102 | 103 | if (i < nSrcLen) 104 | { 105 | int rest = nSrcLen - i; 106 | unsigned long ulTmp = 0; 107 | for (int j = 0; j < rest; ++j) 108 | { 109 | *(((unsigned char *)&ulTmp) + j) = *psrc++; 110 | } 111 | p64[0] = GetB64char((B0(ulTmp) >> 2) & 0x3F); 112 | p64[1] = GetB64char((B0(ulTmp) << 6 >> 2 | B1(ulTmp) >> 4) & 0x3F); 113 | p64[2] = rest > 1 ? GetB64char((B1(ulTmp) << 4 >> 2 | B2(ulTmp) >> 6) & 0x3F) : '='; 114 | p64[3] = rest > 2 ? GetB64char((B2(ulTmp) << 2 >> 2) & 0x3F) : '='; 115 | p64 += 4; 116 | len += 4; 117 | } 118 | *p64 = '\0'; 119 | return szEnc; 120 | } 121 | 122 | const char *ZBase64::Encode(const string &strInput) 123 | { 124 | return Encode(strInput.data(), strInput.size()); 125 | } 126 | 127 | const char *ZBase64::Decode(const char *szSrc, int nSrcLen, int *pDecLen) 128 | { 129 | if (0 == nSrcLen) 130 | { 131 | nSrcLen = (int)strlen(szSrc); 132 | } 133 | 134 | if (nSrcLen <= 0) 135 | { 136 | return ""; 137 | } 138 | 139 | char *szDec = new char[nSrcLen]; 140 | m_arrDec.push_back(szDec); 141 | 142 | int i = 0; 143 | int len = 0; 144 | printf("%i\n", len); // or else it errors?? 145 | unsigned char *psrc = (unsigned char *)szSrc; 146 | char *pbuf = szDec; 147 | for (i = 0; i < nSrcLen - 4; i += 4) 148 | { 149 | unsigned long ulTmp = *(unsigned long *)psrc; 150 | 151 | int b0 = (GetB64Index((char)B0(ulTmp)) << 2 | GetB64Index((char)B1(ulTmp)) << 2 >> 6) & 0xFF; 152 | int b1 = (GetB64Index((char)B1(ulTmp)) << 4 | GetB64Index((char)B2(ulTmp)) << 2 >> 4) & 0xFF; 153 | int b2 = (GetB64Index((char)B2(ulTmp)) << 6 | GetB64Index((char)B3(ulTmp)) << 2 >> 2) & 0xFF; 154 | 155 | *((unsigned long *)pbuf) = b0 | b1 << 8 | b2 << 16; 156 | psrc += 4; 157 | pbuf += 3; 158 | len += 3; 159 | } 160 | 161 | if (i < nSrcLen) 162 | { 163 | int rest = nSrcLen - i; 164 | unsigned long ulTmp = 0; 165 | for (int j = 0; j < rest; ++j) 166 | { 167 | *(((unsigned char *)&ulTmp) + j) = *psrc++; 168 | } 169 | 170 | int b0 = (GetB64Index((char)B0(ulTmp)) << 2 | GetB64Index((char)B1(ulTmp)) << 2 >> 6) & 0xFF; 171 | *pbuf++ = b0; 172 | len++; 173 | 174 | if ('=' != B1(ulTmp) && '=' != B2(ulTmp)) 175 | { 176 | int b1 = (GetB64Index((char)B1(ulTmp)) << 4 | GetB64Index((char)B2(ulTmp)) << 2 >> 4) & 0xFF; 177 | *pbuf++ = b1; 178 | len++; 179 | } 180 | 181 | if ('=' != B2(ulTmp) && '=' != B3(ulTmp)) 182 | { 183 | int b2 = (GetB64Index((char)B2(ulTmp)) << 6 | GetB64Index((char)B3(ulTmp)) << 2 >> 2) & 0xFF; 184 | *pbuf++ = b2; 185 | len++; 186 | } 187 | } 188 | *pbuf = '\0'; 189 | 190 | if (NULL != pDecLen) 191 | { 192 | *pDecLen = (int)(pbuf - szDec); 193 | } 194 | 195 | return szDec; 196 | } 197 | 198 | const char *ZBase64::Decode(const char *szSrc, string &strOutput) 199 | { 200 | strOutput.clear(); 201 | int nDecLen = 0; 202 | const char *p = Decode(szSrc, 0, &nDecLen); 203 | strOutput.append(p, nDecLen); 204 | return strOutput.data(); 205 | } 206 | -------------------------------------------------------------------------------- /ZSign/common/base64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | class ZBase64 9 | { 10 | public: 11 | ZBase64(void); 12 | ~ZBase64(void); 13 | 14 | public: 15 | const char *Encode(const char *szSrc, int nSrcLen = 0); 16 | const char *Encode(const string &strInput); 17 | const char *Decode(const char *szSrc, int nSrcLen = 0, int *pDecLen = NULL); 18 | const char *Decode(const char *szSrc, string &strOutput); 19 | 20 | private: 21 | inline int GetB64Index(char ch); 22 | inline char GetB64char(int nIndex); 23 | 24 | private: 25 | vector m_arrDec; 26 | vector m_arrEnc; 27 | }; 28 | -------------------------------------------------------------------------------- /ZSign/common/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | using namespace std; 28 | 29 | #define LE(x) _Swap(x) 30 | #define BE(x) _Swap(x) 31 | 32 | #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) 33 | #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) 34 | #endif 35 | 36 | uint16_t _Swap(uint16_t value); 37 | uint32_t _Swap(uint32_t value); 38 | uint64_t _Swap(uint64_t value); 39 | 40 | bool ReadFile(const char *szFile, string &strData); 41 | bool ReadFile(string &strData, const char *szFormatPath, ...); 42 | bool WriteFile(const char *szFile, const string &strData); 43 | bool WriteFile(const char *szFile, const char *szData, size_t sLen); 44 | bool WriteFile(string &strData, const char *szFormatPath, ...); 45 | bool WriteFile(const char *szData, size_t sLen, const char *szFormatPath, ...); 46 | bool AppendFile(const char *szFile, const string &strData); 47 | bool AppendFile(const char *szFile, const char *szData, size_t sLen); 48 | bool AppendFile(const string &strData, const char *szFormatPath, ...); 49 | bool IsRegularFile(const char *szFile); 50 | bool IsFolder(const char *szFolder); 51 | bool IsFolderV(const char *szFormatPath, ...); 52 | bool CreateFolder(const char *szFolder); 53 | bool CreateFolderV(const char *szFormatPath, ...); 54 | bool RemoveFile(const char *szFile); 55 | bool RemoveFileV(const char *szFormatPath, ...); 56 | bool RemoveFolder(const char *szFolder); 57 | bool RemoveFolderV(const char *szFormatPath, ...); 58 | bool IsFileExists(const char *szFile); 59 | bool IsFileExistsV(const char *szFormatPath, ...); 60 | int64_t GetFileSize(int fd); 61 | int64_t GetFileSize(const char *szFile); 62 | int64_t GetFileSizeV(const char *szFormatPath, ...); 63 | string GetFileSizeString(const char *szFile); 64 | bool IsZipFile(const char *szFile); 65 | string GetCanonicalizePath(const char *szPath); 66 | void *MapFile(const char *path, size_t offset, size_t size, size_t *psize, bool ro); 67 | bool IsPathSuffix(const string &strPath, const char *suffix); 68 | 69 | const char *StringFormat(string &strFormat, const char *szFormatArgs, ...); 70 | string &StringReplace(string &context, const string &from, const string &to); 71 | void StringSplit(const string &src, const string &split, vector &dest); 72 | 73 | string FormatSize(int64_t size, int64_t base = 1024); 74 | time_t GetUnixStamp(); 75 | uint64_t GetMicroSecond(); 76 | bool SystemExec(const char *szFormatCmd, ...); 77 | uint32_t ByteAlign(uint32_t uValue, uint32_t uAlign); 78 | 79 | enum 80 | { 81 | E_SHASUM_TYPE_1 = 1, 82 | E_SHASUM_TYPE_256 = 2, 83 | }; 84 | 85 | bool SHASum(int nSumType, uint8_t *data, size_t size, string &strOutput); 86 | bool SHASum(int nSumType, const string &strData, string &strOutput); 87 | bool SHASum(const string &strData, string &strSHA1, string &strSHA256); 88 | bool SHA1Text(const string &strData, string &strOutput); 89 | bool SHASumFile(const char *szFile, string &strSHA1, string &strSHA256); 90 | bool SHASumBase64(const string &strData, string &strSHA1Base64, string &strSHA256Base64); 91 | bool SHASumBase64File(const char *szFile, string &strSHA1Base64, string &strSHA256Base64); 92 | void PrintSHASum(const char *prefix, const uint8_t *hash, uint32_t size, const char *suffix = "\n"); 93 | void PrintSHASum(const char *prefix, const string &strSHASum, const char *suffix = "\n"); 94 | void PrintDataSHASum(const char *prefix, int nSumType, const string &strData, const char *suffix = "\n"); 95 | void PrintDataSHASum(const char *prefix, int nSumType, uint8_t *data, size_t size, const char *suffix = "\n"); 96 | 97 | class ZBuffer 98 | { 99 | public: 100 | ZBuffer(); 101 | ~ZBuffer(); 102 | 103 | public: 104 | char *GetBuffer(uint32_t uSize); 105 | 106 | private: 107 | void Free(); 108 | 109 | private: 110 | char *m_pData; 111 | uint32_t m_uSize; 112 | }; 113 | 114 | class ZTimer 115 | { 116 | public: 117 | ZTimer(); 118 | 119 | public: 120 | uint64_t Reset(); 121 | uint64_t Print(const char *szFormatArgs, ...); 122 | uint64_t PrintResult(bool bSuccess, const char *szFormatArgs, ...); 123 | 124 | private: 125 | uint64_t m_uBeginTime; 126 | }; 127 | 128 | class ZLog 129 | { 130 | public: 131 | enum eLogType 132 | { 133 | E_NONE = 0, 134 | E_ERROR = 1, 135 | E_WARN = 2, 136 | E_INFO = 3, 137 | E_DEBUG = 4 138 | }; 139 | 140 | public: 141 | static bool IsDebug(); 142 | static void Print(const char *szLog); 143 | static void PrintV(const char *szFormatArgs, ...); 144 | static void Debug(const char *szLog); 145 | static void DebugV(const char *szFormatArgs, ...); 146 | static bool Warn(const char *szLog); 147 | static bool WarnV(const char *szFormatArgs, ...); 148 | static bool Error(const char *szLog); 149 | static bool ErrorV(const char *szFormatArgs, ...); 150 | static bool Success(const char *szLog); 151 | static bool SuccessV(const char *szFormatArgs, ...); 152 | static bool PrintResult(bool bSuccess, const char *szLog); 153 | static bool PrintResultV(bool bSuccess, const char *szFormatArgs, ...); 154 | static void Print(int nLevel, const char *szLog); 155 | static void PrintV(int nLevel, const char *szFormatArgs, ...); 156 | static void SetLogLever(int nLogLevel); 157 | static vector logs; 158 | 159 | private: 160 | static int g_nLogLevel; 161 | static void writeToLogFile(const std::string& message); 162 | 163 | }; 164 | -------------------------------------------------------------------------------- /ZSign/macho.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "archo.h" 3 | 4 | class ZMachO 5 | { 6 | public: 7 | ZMachO(); 8 | ~ZMachO(); 9 | 10 | public: 11 | bool Init(const char *szFile); 12 | bool InitV(const char *szFormatPath, ...); 13 | bool Free(); 14 | void PrintInfo(); 15 | bool Sign(ZSignAsset *pSignAsset, bool bForce, string strBundleId, string strInfoPlistSHA1, string strInfoPlistSHA256, const string &strCodeResourcesData); 16 | bool InjectDyLib(bool bWeakInject, const char *szDyLibPath, bool &bCreate); 17 | bool ChangeDylibPath(const char *oldPath, const char *newPath); 18 | std::vector ListDylibs(); 19 | bool RemoveDylib(const std::set &dylibNames); 20 | private: 21 | bool OpenFile(const char *szPath); 22 | bool CloseFile(); 23 | 24 | bool NewArchO(uint8_t *pBase, uint32_t uLength); 25 | void FreeArchOes(); 26 | bool ReallocCodeSignSpace(); 27 | 28 | private: 29 | size_t m_sSize; 30 | string m_strFile; 31 | uint8_t *m_pBase; 32 | bool m_bCSRealloced; 33 | vector m_arrArchOes; 34 | }; 35 | 36 | bool is_64bit_macho(const char *filepath); 37 | -------------------------------------------------------------------------------- /ZSign/openssl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common/json.h" 3 | 4 | extern const char *appleDevCACert; 5 | extern const char *appleDevCACertG3; 6 | extern const char *appleRootCACert; 7 | 8 | bool GetCertSubjectCN(const string &strCertData, string &strSubjectCN); 9 | bool GetCMSInfo(uint8_t *pCMSData, uint32_t uCMSLength, JValue &jvOutput); 10 | bool GetCMSContent(const string &strCMSDataInput, string &strContentOutput); 11 | bool GetCMSContent2(const void* strCMSDataInput, int size, string &strContentOutput); 12 | bool GenerateCMS(const string &strSignerCertData, const string &strSignerPKeyData, const string &strCDHashData, const string &strCDHashPlist, string &strCMSOutput); 13 | 14 | class ZSignAsset 15 | { 16 | public: 17 | ZSignAsset(); 18 | 19 | public: 20 | bool GenerateCMS(const string &strCDHashData, const string &strCDHashesPlist, const string &strCodeDirectorySlotSHA1, const string &strAltnateCodeDirectorySlot256, string &strCMSOutput); 21 | bool Init(const string &strSignerCertFile, const string &strSignerPKeyFile, const string &strProvisionFile, const string &strEntitlementsFile, const string &strPassword); 22 | bool InitSimple(const void* strSignerPKeyData, int strSignerPKeyDataSize, const void* strProvisionData, int strProvisionDataSize, const string &strPassword); 23 | 24 | public: 25 | string m_strTeamId; 26 | string m_strSubjectCN; 27 | string m_strProvisionData; 28 | string m_strEntitlementsData; 29 | time_t expirationDate; 30 | 31 | void *m_evpPKey; 32 | void *m_x509Cert; 33 | }; 34 | -------------------------------------------------------------------------------- /ZSign/signing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "openssl.h" 3 | 4 | bool ParseCodeSignature(uint8_t *pCSBase); 5 | bool SlotBuildEntitlements(const string &strEntitlements, string &strOutput); 6 | bool SlotBuildDerEntitlements(const string &strEntitlements, string &strOutput); 7 | bool SlotBuildRequirements(const string &strBundleID, const string &strSubjectCN, string &strOutput); 8 | bool GetCodeSignatureCodeSlotsData(uint8_t *pCSBase, uint8_t *&pCodeSlots1, uint32_t &uCodeSlots1Length, uint8_t *&pCodeSlots256, uint32_t &uCodeSlots256Length); 9 | bool SlotBuildCodeDirectory(bool bAlternate, 10 | uint8_t *pCodeBase, 11 | uint32_t uCodeLength, 12 | uint8_t *pCodeSlotsData, 13 | uint32_t uCodeSlotsDataLength, 14 | uint64_t execSegLimit, 15 | uint64_t execSegFlags, 16 | const string &strBundleId, 17 | const string &strTeamId, 18 | const string &strInfoPlistSHA, 19 | const string &strRequirementsSlotSHA, 20 | const string &strCodeResourcesSHA, 21 | const string &strEntitlementsSlotSHA, 22 | const string &strDerEntitlementsSlotSHA, 23 | bool isExecuteArch, 24 | string &strOutput); 25 | bool SlotBuildCMSSignature(ZSignAsset *pSignAsset, 26 | const string &strCodeDirectorySlot, 27 | const string &strAltnateCodeDirectorySlot, 28 | string &strOutput); 29 | bool GetCodeSignatureExistsCodeSlotsData(uint8_t *pCSBase, 30 | uint8_t *&pCodeSlots1Data, 31 | uint32_t &uCodeSlots1DataLength, 32 | uint8_t *&pCodeSlots256Data, 33 | uint32_t &uCodeSlots256DataLength); 34 | uint32_t GetCodeSignatureLength(uint8_t *pCSBase); 35 | -------------------------------------------------------------------------------- /ZSign/zsign.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // zsign.hpp 3 | // feather 4 | // 5 | // Created by HAHALOSAH on 5/22/24. 6 | // 7 | 8 | #ifndef zsign_hpp 9 | #define zsign_hpp 10 | 11 | #include 12 | #import 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | bool InjectDyLib(NSString *filePath, 19 | NSString *dylibPath, 20 | bool weakInject, 21 | bool bCreate); 22 | 23 | bool ChangeDylibPath(NSString *filePath, 24 | NSString *oldPath, 25 | NSString *newPath); 26 | 27 | bool ListDylibs(NSString *filePath, NSMutableArray *dylibPathsArray); 28 | bool UninstallDylibs(NSString *filePath, NSArray *dylibPathsArray); 29 | 30 | void zsign(NSString *appPath, 31 | NSData *prov, 32 | NSData *key, 33 | NSString *pass, 34 | NSProgress* progress, 35 | void(^completionHandler)(BOOL success, NSError *error) 36 | ); 37 | NSString* getTeamId(NSData *prov, 38 | NSData *key, 39 | NSString *pass); 40 | 41 | int checkCert(NSData *prov, 42 | NSData *key, 43 | NSString *pass, 44 | BOOL ocsp, 45 | void(^completionHandler)(int status, NSDate* expirationDate, NSString *error)); 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif /* zsign_hpp */ 51 | -------------------------------------------------------------------------------- /ZSign/zsigner.h: -------------------------------------------------------------------------------- 1 | // 2 | // zsigner.h 3 | // LiveContainer 4 | // 5 | // Created by s s on 2024/11/10. 6 | // 7 | #import 8 | 9 | @interface ZSigner : NSObject 10 | + (NSProgress*)signWithAppPath:(NSString *)appPath prov:(NSData *)prov key:(NSData *)key pass:(NSString *)pass completionHandler:(void (^)(BOOL success, NSError *error))completionHandler; 11 | // this method is used to get teamId for ADP/Enterprise certs ,don't use it in normal jitless 12 | + (NSString*)getTeamIdWithProv:(NSData *)prov key:(NSData *)key pass:(NSString *)pass; 13 | + (int)checkCertWithProv:(NSData *)prov key:(NSData *)key pass:(NSString *)pass ocsp:(BOOL)ocsp completionHandler:(void(^)(int status, NSDate* expirationDate, NSString *error))completionHandler; 14 | @end 15 | -------------------------------------------------------------------------------- /ZSign/zsigner.m: -------------------------------------------------------------------------------- 1 | // 2 | // zsigner.m 3 | // LiveContainer 4 | // 5 | // Created by s s on 2024/11/10. 6 | // 7 | 8 | #import "zsigner.h" 9 | #import "zsign.hpp" 10 | 11 | NSProgress* currentZSignProgress; 12 | 13 | @implementation ZSigner 14 | + (NSProgress*)signWithAppPath:(NSString *)appPath prov:(NSData *)prov key:(NSData *)key pass:(NSString *)pass 15 | completionHandler:(void (^)(BOOL success, NSError *error))completionHandler { 16 | NSProgress* ans = [NSProgress progressWithTotalUnitCount:1000]; 17 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 18 | zsign(appPath, prov, key, pass, ans, completionHandler); 19 | }); 20 | return ans; 21 | } 22 | // this method is used to get teamId for ADP/Enterprise certs ,don't use it in normal jitless 23 | + (NSString*)getTeamIdWithProv:(NSData *)prov key:(NSData *)key pass:(NSString *)pass { 24 | return getTeamId(prov, key, pass); 25 | } 26 | + (int)checkCertWithProv:(NSData *)prov key:(NSData *)key pass:(NSString *)pass ocsp:(BOOL)ocsp completionHandler:(void(^)(int status, NSDate* expirationDate, NSString *error))completionHandler { 27 | return checkCert(prov, key, pass, ocsp, completionHandler); 28 | } 29 | @end 30 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.geode.launcher 2 | Name: Geode 3 | Version: 1.0.1 4 | Architecture: iphoneos-arm 5 | Description: Geode launcher helper for iOS! 6 | Maintainer: Firee 7 | Author: Firee 8 | Section: Utilities 9 | -------------------------------------------------------------------------------- /download_openssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -d "$THEOS/lib/OpenSSL.framework" ]; then 3 | echo "OpenSSL.framework already exists in $THEOS/lib" 4 | else 5 | echo "OpenSSL.framework not found. Downloading..." 6 | 7 | curl -L "https://github.com/krzyzanowskim/OpenSSL/releases/download/3.3.3001/OpenSSL.xcframework.zip" -o "OpenSSL.xcframework.zip" 8 | 9 | unzip -q "OpenSSL.xcframework.zip" -d . 10 | 11 | mkdir -p "$THEOS/lib" 12 | mv "OpenSSL.xcframework/ios-arm64/OpenSSL.framework" "$THEOS/lib" 13 | 14 | rm -f "OpenSSL.xcframework.zip" 15 | rm -rf "OpenSSL.xcframework" 16 | 17 | echo "OpenSSL.framework has been installed to $THEOS/lib" 18 | fi 19 | 20 | if [ ! -d "./Resources/Frameworks/OpenSSL.framework" ]; then 21 | rsync -av --exclude 'Headers' "$THEOS/lib/OpenSSL.framework" "./Resources/Frameworks" 22 | fi 23 | -------------------------------------------------------------------------------- /entitlements.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | application-identifier 6 | com.geode.launcher 7 | com.apple.developer.kernel.extended-virtual-addressing 8 | 9 | com.apple.developer.kernel.increased-memory-limit 10 | 11 | com.apple.security.application-groups 12 | 13 | group.com.SideStore.SideStore 14 | group.com.rileytestut.AltStore 15 | 16 | get-task-allow 17 | 18 | com.apple.private.MobileContainerManager.allowed 19 | 20 | platform-application 21 | 22 | 23 | 24 | 25 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /screenshots/StikJIT.conf: -------------------------------------------------------------------------------- 1 | [Interface] 2 | PrivateKey = AIIeeUDvk3NeAFJ9BWCQvPJize/9WZibMnGJ/0rt5k4= 3 | Address = 10.7.0.10/24 4 | 5 | [Peer] 6 | PublicKey = kHDoekeYhBvfW9a9UQ+UCmpbG423eejTjcjW+DT+JF0= 7 | AllowedIPs = 10.7.0.1/32 8 | Endpoint = 127.0.0.1:51820 9 | PersistentKeepalive = 25 10 | -------------------------------------------------------------------------------- /screenshots/altstore.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/altstore.psd -------------------------------------------------------------------------------- /screenshots/install-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/install-1.png -------------------------------------------------------------------------------- /screenshots/install-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/install-2.png -------------------------------------------------------------------------------- /screenshots/install-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/install-3.png -------------------------------------------------------------------------------- /screenshots/install-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/install-4.png -------------------------------------------------------------------------------- /screenshots/install-altstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/install-altstore.png -------------------------------------------------------------------------------- /screenshots/install-trollstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/install-trollstore.png -------------------------------------------------------------------------------- /screenshots/jitstreamer-manual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/jitstreamer-manual.png -------------------------------------------------------------------------------- /screenshots/jitstreamer-manual.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/jitstreamer-manual.psd -------------------------------------------------------------------------------- /screenshots/stikdebug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/stikdebug.png -------------------------------------------------------------------------------- /screenshots/stikdebug.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/stikdebug.psd -------------------------------------------------------------------------------- /screenshots/stosvpn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/stosvpn.png -------------------------------------------------------------------------------- /screenshots/stosvpn.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/stosvpn.psd -------------------------------------------------------------------------------- /screenshots/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/thumbnail.png -------------------------------------------------------------------------------- /screenshots/thumbnail.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/thumbnail.psd -------------------------------------------------------------------------------- /screenshots/webserverguide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/webserverguide.png -------------------------------------------------------------------------------- /screenshots/webserverguide.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/ios-launcher/b9119f6b892d96982c7fef87790b394a46053afa/screenshots/webserverguide.psd -------------------------------------------------------------------------------- /src/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | typedef void (^ImportCertHandler)(NSData* certData, NSString* password); 4 | 5 | @interface AppDelegate : UIResponder 6 | @property(nonatomic, strong) UIWindow* window; 7 | @property(copy, nonatomic) void (^openUrlStrFunc)(NSString* urlStr); 8 | @property(copy, nonatomic) void (^launchAppFunc)(NSString* bundleId, NSString* container); 9 | @property(nonatomic, strong) NSString* urlStrToOpen; 10 | @property(nonatomic, strong) NSString* bundleToLaunch; 11 | @property(nonatomic, strong) NSString* containerToLaunch; 12 | 13 | + (void)setOpenUrlStrFunc:(void (^)(NSString* urlStr))handler; 14 | + (void)setLaunchAppFunc:(void (^)(NSString* bundleId, NSString* container))handler; 15 | + (void)openWebPage:(NSString*)urlStr; 16 | + (void)launchApp:(NSString*)bundleId container:(NSString*)container; 17 | + (void)setImportSideStoreCertFunc:(ImportCertHandler)handler; 18 | + (void)importSideStoreCert:(NSData*)certData password:(NSString*)password; 19 | @end 20 | -------------------------------------------------------------------------------- /src/GeodeInstaller.h: -------------------------------------------------------------------------------- 1 | #import "RootViewController.h" 2 | #import 3 | #import 4 | 5 | @interface GeodeInstaller : NSObject 6 | @property(nonatomic, strong) RootViewController* root; 7 | - (void)startInstall:(RootViewController*)root ignoreRoot:(BOOL)ignoreRoot; 8 | - (void)checkUpdates:(RootViewController*)root download:(BOOL)download; 9 | - (void)checkLauncherUpdates:(RootViewController*)root; 10 | - (void)verifyChecksum; 11 | - (void)cancelDownload; 12 | @end 13 | -------------------------------------------------------------------------------- /src/IconView.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | @interface IconViewController : UITableViewController 4 | @end 5 | -------------------------------------------------------------------------------- /src/IconView.m: -------------------------------------------------------------------------------- 1 | #import "IconView.h" 2 | #import "Utils.h" 3 | #import "components/LogUtils.h" 4 | #import "src/Theming.h" 5 | #import 6 | 7 | @interface IconViewController () 8 | @property(nonatomic, strong) NSArray* icons; 9 | @end 10 | 11 | @implementation IconViewController 12 | - (void)viewDidLoad { 13 | [super viewDidLoad]; 14 | //[self setTitle:@"general.change-icon".loc]; 15 | [[self tableView] setRowHeight:75]; 16 | self.icons = @[ 17 | @{ @"name" : @"Default", @"iconName" : @"AppIcon", @"iconImg" : @"AppIcon60x60" }, 18 | @{ @"name" : @"Pride", @"iconName" : @"Pride", @"iconImg" : @"PrideIcon60x60" }, 19 | ]; 20 | // https://github.com/reactwg/react-native-new-architecture/blob/76d8426c27c1bf30c235f653e425ef872554a33b/docs/fabric-native-components.md 21 | [NSLayoutConstraint activateConstraints:@[ 22 | [self.tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], 23 | [self.tableView.topAnchor constraintEqualToAnchor:self.view.topAnchor], 24 | [self.tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], 25 | [self.tableView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], 26 | ]]; 27 | [[self view] setBackgroundColor:[Theming getBackgroundColor]]; 28 | } 29 | - (void)viewDidLayoutSubviews { 30 | [super viewDidLayoutSubviews]; 31 | } 32 | 33 | #pragma mark - Table View Data Source 34 | 35 | - (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView { 36 | return 1; 37 | } 38 | 39 | - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section { 40 | return self.icons.count; 41 | } 42 | 43 | - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { 44 | UITableViewCell* cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil]; 45 | 46 | cell.backgroundColor = nil; 47 | 48 | UIImageView* iconView = [[UIImageView alloc] init]; 49 | iconView.image = [UIImage imageNamed:self.icons[indexPath.row][@"iconImg"]]; 50 | iconView.translatesAutoresizingMaskIntoConstraints = NO; 51 | iconView.layer.masksToBounds = YES; 52 | iconView.layer.cornerRadius = 15; 53 | [cell.contentView addSubview:iconView]; 54 | 55 | UILabel* name = [[UILabel alloc] init]; 56 | name.translatesAutoresizingMaskIntoConstraints = NO; 57 | name.font = [UIFont boldSystemFontOfSize:20]; 58 | name.text = self.icons[indexPath.row][@"name"]; 59 | [cell.contentView addSubview:name]; 60 | 61 | [NSLayoutConstraint activateConstraints:@[ 62 | [iconView.widthAnchor constraintEqualToConstant:60], 63 | [iconView.heightAnchor constraintEqualToConstant:60], // or itll be squished 64 | [iconView.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:20], 65 | [iconView.centerYAnchor constraintEqualToAnchor:cell.contentView.centerYAnchor], 66 | 67 | [name.leadingAnchor constraintEqualToAnchor:iconView.trailingAnchor constant:16], 68 | [name.centerYAnchor constraintEqualToAnchor:cell.contentView.centerYAnchor], 69 | ]]; 70 | 71 | NSString* currentIcon = [UIApplication sharedApplication].alternateIconName; 72 | BOOL isSelected = ([currentIcon isEqualToString:self.icons[indexPath.row][@"iconName"]]); 73 | if (currentIcon == nil) { 74 | isSelected = ([self.icons[indexPath.row][@"iconName"] isEqualToString:@"AppIcon"]); 75 | } 76 | cell.accessoryType = isSelected ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone; 77 | return cell; 78 | } 79 | 80 | - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath { 81 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 82 | NSString* iconKey = nil; 83 | if (![self.icons[indexPath.row][@"iconName"] isEqualToString:@"AppIcon"]) { 84 | iconKey = self.icons[indexPath.row][@"iconName"]; 85 | } 86 | [[UIApplication sharedApplication] setAlternateIconName:iconKey completionHandler:^(NSError* _Nullable error) { 87 | if (error) { 88 | AppLog(@"Failed to set alternate icon: %@", error); 89 | [Utils showError:self title:@"Couldn't set icon" error:error]; 90 | } else { 91 | AppLog(@"Icon set successfully."); 92 | } 93 | }]; 94 | [self.tableView reloadData]; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /src/IntroVC.h: -------------------------------------------------------------------------------- 1 | #import "MSColorPicker/MSColorPicker/MSColorSelectionViewController.h" 2 | #include 3 | 4 | typedef NS_ENUM(NSInteger, InstallStep) { 5 | InstallStepWelcome, 6 | InstallStepAccentColor, 7 | InstallStepInstallMethod, 8 | InstallStepJailbreakStore, 9 | InstallStepWarning, 10 | InstallStepLaunchMethod, 11 | InstallStepComplete 12 | }; 13 | 14 | @interface IntroVC : UIViewController 15 | #pragma mark - Color Temp 16 | @property(nonatomic, strong) UIButton* colorNextButton; 17 | @property(nonatomic, strong) UILabel* colorPreviewLabel; 18 | @property(nonatomic, strong) MSColorSelectionViewController* colorSelectionController; 19 | #pragma mark - Other 20 | @property(nonatomic, strong) UIColor* accentColor; 21 | @property(nonatomic, assign) BOOL skipColor; 22 | @property(nonatomic, assign) InstallStep currentStep; 23 | @property(nonatomic, strong) UIView* contentView; 24 | @property(nonatomic, strong) NSString* installMethod; 25 | @property(nonatomic, assign) BOOL useJITLess; 26 | @end 27 | -------------------------------------------------------------------------------- /src/LCUtils/FoundationPrivate.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | @interface NSBundle (private) 4 | - (id)_cfBundle; 5 | @end 6 | 7 | @interface NSUserDefaults (private) 8 | + (void)setStandardUserDefaults:(id)defaults; 9 | - (NSString*)_identifier; 10 | @end 11 | -------------------------------------------------------------------------------- /src/LCUtils/GCSharedUtils.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | @interface GCSharedUtils : NSObject 4 | + (NSString*)liveContainerBundleID; 5 | + (NSString*)teamIdentifier; 6 | + (NSString*)appGroupID; 7 | + (NSURL*)appGroupPath; 8 | + (NSString*)certificatePassword; 9 | + (BOOL)askForJIT; 10 | + (void)relaunchApp; 11 | + (BOOL)launchToGuestApp; 12 | + (void)setWebPageUrlForNextLaunch:(NSString*)urlString; 13 | + (NSString*)getContainerUsingLCSchemeWithFolderName:(NSString*)folderName; 14 | + (void)moveSharedAppFolderBack; 15 | + (NSBundle*)findBundleWithBundleId:(NSString*)bundleId; 16 | + (void)dumpPreferenceToPath:(NSString*)plistLocationTo dataUUID:(NSString*)dataUUID; 17 | + (NSString*)findDefaultContainerWithBundleId:(NSString*)bundleId; 18 | @end 19 | -------------------------------------------------------------------------------- /src/LCUtils/LCAppInfo.h: -------------------------------------------------------------------------------- 1 | #import "LCUtils.h" 2 | #import 3 | #import 4 | 5 | @interface LCAppInfo : NSObject { 6 | NSMutableDictionary* _info; 7 | NSMutableDictionary* _infoPlist; 8 | NSString* _bundlePath; 9 | } 10 | @property NSString* relativeBundlePath; 11 | @property bool isShared; 12 | @property bool doSymlinkInbox; 13 | @property bool ignoreDlopenError; 14 | @property NSString* dataUUID; 15 | @property NSArray* containerInfo; 16 | @property bool autoSaveDisabled; 17 | 18 | - (void)setBundlePath:(NSString*)newBundlePath; 19 | - (NSMutableDictionary*)info; 20 | - (NSString*)bundlePath; 21 | - (NSString*)bundleIdentifier; 22 | - (NSString*)version; 23 | - (NSMutableArray*)urlSchemes; 24 | - (instancetype)initWithBundlePath:(NSString*)bundlePath; 25 | - (void)save; 26 | - (void)patchExecAndSignIfNeedWithCompletionHandler:(void (^)(bool success, NSString* errorInfo))completetionHandler 27 | progressHandler:(void (^)(NSProgress* progress))progressHandler 28 | forceSign:(BOOL)forceSign; 29 | @end 30 | -------------------------------------------------------------------------------- /src/LCUtils/LCAppModel.h: -------------------------------------------------------------------------------- 1 | #import "src/LCUtils/LCAppInfo.h" 2 | #import "src/LCUtils/LCUtils.h" 3 | #import 4 | #import 5 | 6 | @protocol LCAppModelDelegate 7 | - (void)closeNavigationView; 8 | - (void)changeAppVisibility:(id)app; 9 | @end 10 | 11 | @interface LCAppModel : NSObject 12 | 13 | @property LCAppInfo* appInfo; 14 | 15 | @property BOOL isAppRunning; 16 | @property BOOL isSigningInProgress; 17 | @property CGFloat signProgress; 18 | @property BOOL uiIsJITNeeded; 19 | @property NSString* uiDefaultDataFolder; 20 | @property NSArray* uiContainers; 21 | @property NSString* uiTweakFolder; 22 | @property BOOL uiDoSymlinkInbox; 23 | @property BOOL uiBypassAssertBarrierOnQueue; 24 | 25 | @property id delegate; 26 | 27 | - (instancetype)initWithAppInfo:(LCAppInfo*)appInfo delegate:(id)delegate; 28 | 29 | - (BOOL)isEqual:(id)object; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /src/LCUtils/LCAppModel.m: -------------------------------------------------------------------------------- 1 | #import "LCAppModel.h" 2 | #import "src/LCUtils/LCUtils.h" 3 | 4 | @implementation LCAppModel 5 | 6 | @synthesize isAppRunning = _isAppRunning; 7 | @synthesize isSigningInProgress = _isSigningInProgress; 8 | 9 | - (instancetype)initWithAppInfo:(LCAppInfo*)appInfo delegate:(id)delegate { 10 | if (self = [super init]) { 11 | _appInfo = appInfo; 12 | _delegate = delegate; 13 | } 14 | return self; 15 | } 16 | 17 | - (BOOL)isEqual:(id)object { 18 | if (self == object) { 19 | return YES; 20 | } 21 | if (![object isKindOfClass:[LCAppModel class]]) { 22 | return NO; 23 | } 24 | return [self hash] == [object hash]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /src/LCUtils/LCUtils.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | typedef void (^LCParseMachOCallback)(const char* path, struct mach_header_64* header, int fd, void* filePtr); 4 | 5 | typedef NS_ENUM(NSInteger, Store) { SideStore, AltStore, Unknown }; 6 | 7 | NSString* LCParseMachO(const char* path, bool readOnly, LCParseMachOCallback callback); 8 | void LCPatchAddRPath(const char* path, struct mach_header_64* header); 9 | void LCPatchExecSlice(const char* path, struct mach_header_64* header); 10 | void LCPatchLibrary(const char* path, struct mach_header_64* header); 11 | void LCChangeExecUUID(struct mach_header_64* header); 12 | void LCPatchAltStore(const char* path, struct mach_header_64* header); 13 | bool checkCodeSignature(const char* path); 14 | void refreshFile(NSString* execPath); 15 | 16 | @interface PKZipArchiver : NSObject 17 | 18 | - (NSData*)zippedDataForURL:(NSURL*)url; 19 | 20 | @end 21 | 22 | @interface LCUtils : NSObject 23 | 24 | + (void)validateJITLessSetup:(void (^)(BOOL success, NSError* error))completionHandler; 25 | + (NSURL*)archiveTweakedAltStoreWithError:(NSError**)error; 26 | + (NSData*)certificateData; 27 | + (NSString*)certificatePassword; 28 | 29 | + (BOOL)askForJIT; 30 | + (BOOL)launchToGuestApp; 31 | 32 | + (NSProgress*)signAppBundleWithZSign:(NSURL*)path completionHandler:(void (^)(BOOL success, NSError* error))completionHandler; 33 | + (BOOL)isAppGroupAltStoreLike; 34 | + (NSString*)getCertTeamIdWithKeyData:(NSData*)keyData password:(NSString*)password; 35 | + (int)validateCertificate:(void (^)(int status, NSDate* expirationDate, NSString* error))completionHandler; 36 | + (Store)store; 37 | + (NSString*)teamIdentifier; 38 | + (NSString*)appGroupID; 39 | + (NSString*)appUrlScheme; 40 | + (NSURL*)appGroupPath; 41 | + (NSString*)storeInstallURLScheme; 42 | 43 | // ext 44 | + (NSUserDefaults*)appGroupUserDefault; 45 | + (NSString*)getStoreName; 46 | + (NSString*)getAppRunningLCScheme:(NSString*)bundleId; 47 | 48 | + (void)signFilesInFolder:(NSURL*)url onProgressCreated:(void (^)(NSProgress* progress))onProgressCreated completion:(void (^)(NSString* error))completion; 49 | + (void)signTweaks:(NSURL*)tweakFolderUrl force:(BOOL)force progressHandler:(void (^)(NSProgress* progress))progressHandler completion:(void (^)(NSError* error))completion; 50 | + (void)signMods:(NSURL*)geodeUrl force:(BOOL)force progressHandler:(void (^)(NSProgress* progress))progressHandler completion:(void (^)(NSError* error))completion; 51 | @end 52 | -------------------------------------------------------------------------------- /src/LCUtils/NSUserDefaults.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSUserDefaults.m 3 | // LiveContainer 4 | // 5 | // Created by s s on 2024/11/29. 6 | // 7 | 8 | #import "FoundationPrivate.h" 9 | #import "GCSharedUtils.h" 10 | #import "utils.h" 11 | 12 | @interface NSUserDefaults (Geode) 13 | + (instancetype)gcSharedDefaults; 14 | + (instancetype)gcUserDefaults; 15 | + (NSString*)gcAppUrlScheme; 16 | + (NSString*)gcAppGroupPath; 17 | @end 18 | 19 | NSMutableDictionary* LCPreferences = 0; 20 | 21 | void NUDGuestHooksInit() { 22 | LCPreferences = [[NSMutableDictionary alloc] init]; 23 | NSFileManager* fm = NSFileManager.defaultManager; 24 | NSURL* libraryPath = [fm URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask].lastObject; 25 | NSURL* preferenceFolderPath = [libraryPath URLByAppendingPathComponent:@"Preferences"]; 26 | if (![fm fileExistsAtPath:preferenceFolderPath.path]) { 27 | NSError* error; 28 | [fm createDirectoryAtPath:preferenceFolderPath.path withIntermediateDirectories:YES attributes:@{} error:&error]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/LCUtils/Shared.h: -------------------------------------------------------------------------------- 1 | #import "src/LCUtils/LCAppModel.h" 2 | #import 3 | 4 | @interface LCPath : NSObject 5 | 6 | + (NSURL*)docPath; 7 | + (NSURL*)bundlePath; 8 | + (NSURL*)dataPath; 9 | + (NSURL*)appGroupPath; 10 | + (NSURL*)tweakPath; 11 | + (NSURL*)lcGroupDocPath; 12 | + (NSURL*)lcGroupBundlePath; 13 | + (NSURL*)lcGroupDataPath; 14 | + (NSURL*)lcGroupAppGroupPath; 15 | + (NSURL*)lcGroupTweakPath; 16 | + (void)ensureAppGroupPaths:(NSError**)error; 17 | 18 | @end 19 | 20 | @interface SharedModel : NSObject 21 | @property BOOL isHiddenAppUnlocked; 22 | @property BOOL developerMode; 23 | // 0= not installed, 1= is installed, 2=current liveContainer is the second one 24 | 25 | @property NSMutableArray* apps; 26 | @property NSMutableArray* hiddenApps; 27 | 28 | - (BOOL)isPhone; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /src/LCUtils/Shared.m: -------------------------------------------------------------------------------- 1 | #import "LCUtils.h" 2 | #import "Shared.h" 3 | #import "src/Utils.h" 4 | #import 5 | 6 | @implementation LCPath 7 | 8 | + (NSURL*)docPath { 9 | return [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].lastObject; 10 | } 11 | 12 | + (NSURL*)bundlePath { 13 | if (![Utils isSandboxed]) { 14 | NSString* bundlePath = [Utils getGDBundlePath]; 15 | if (bundlePath) { 16 | return [NSURL URLWithString:bundlePath]; 17 | } 18 | } 19 | return [[self docPath] URLByAppendingPathComponent:@"Applications"]; 20 | } 21 | 22 | + (NSURL*)dataPath { 23 | return [[self docPath] URLByAppendingPathComponent:@"Data/Application"]; 24 | } 25 | 26 | + (NSURL*)appGroupPath { 27 | return [[self docPath] URLByAppendingPathComponent:@"Data/AppGroup"]; 28 | } 29 | 30 | + (NSURL*)tweakPath { 31 | return [[self docPath] URLByAppendingPathComponent:@"Tweaks"]; 32 | } 33 | 34 | + (NSURL*)lcGroupDocPath { 35 | NSFileManager* fm = [NSFileManager defaultManager]; 36 | NSURL* appGroupUrl = [fm containerURLForSecurityApplicationGroupIdentifier:@"group.com.SideStore.SideStore"]; 37 | if (appGroupUrl) { 38 | return [appGroupUrl URLByAppendingPathComponent:@"Geode"]; 39 | } else { 40 | return [self docPath]; 41 | } 42 | } 43 | 44 | + (NSURL*)lcGroupBundlePath { 45 | return [[self lcGroupDocPath] URLByAppendingPathComponent:@"Applications"]; 46 | } 47 | 48 | + (NSURL*)lcGroupDataPath { 49 | return [[self lcGroupDocPath] URLByAppendingPathComponent:@"Data/Application"]; 50 | } 51 | 52 | + (NSURL*)lcGroupAppGroupPath { 53 | return [[self lcGroupDocPath] URLByAppendingPathComponent:@"Data/AppGroup"]; 54 | } 55 | 56 | + (NSURL*)lcGroupTweakPath { 57 | return [[self lcGroupDocPath] URLByAppendingPathComponent:@"Tweaks"]; 58 | } 59 | 60 | + (void)ensureAppGroupPaths:(NSError**)error { 61 | NSFileManager* fm = [NSFileManager defaultManager]; 62 | /*NSArray *paths = @[ 63 | [self lcGroupBundlePath], 64 | [self lcGroupDataPath], 65 | [self lcGroupTweakPath] 66 | ];*/ 67 | NSArray* paths = @[ [self bundlePath], [self dataPath], [self tweakPath] ]; 68 | 69 | for (NSURL* url in paths) { 70 | NSString* path = url.path; 71 | if (![fm fileExistsAtPath:path]) { 72 | [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:error]; 73 | if (*error) { 74 | return; 75 | } 76 | } 77 | } 78 | } 79 | 80 | @end 81 | 82 | @implementation SharedModel 83 | 84 | - (BOOL)isPhone { 85 | return [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone; 86 | } 87 | 88 | - (instancetype)init { 89 | if (self = [super init]) 90 | return self; 91 | return self; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /src/LCUtils/TPRO.h: -------------------------------------------------------------------------------- 1 | // by khanhduytran0 2 | #define _COMM_PAGE_START_ADDRESS (0x0000000FFFFFC000ULL) 3 | // #define _COMM_PAGE_TPRO_SUPPORT (_COMM_PAGE_START_ADDRESS + ????) 4 | #define _COMM_PAGE_TPRO_WRITE_ENABLE (_COMM_PAGE_START_ADDRESS + 0x0D0) 5 | // #define _COMM_PAGE_TPRO_WRITE_DISABLE (_COMM_PAGE_START_ADDRESS + 0x0D8) 6 | 7 | static inline bool os_thread_self_restrict_tpro_to_rw() { 8 | if (!*(uint64_t*)_COMM_PAGE_TPRO_WRITE_ENABLE) { 9 | // Doesn't have TPRO, skip this 10 | return false; 11 | } 12 | __asm__ __volatile__("mov x0, %0\n" 13 | "ldr x0, [x0]\n" 14 | "msr s3_6_c15_c1_5, x0\n" 15 | "isb sy\n" ::"r"(_COMM_PAGE_TPRO_WRITE_ENABLE) 16 | : "memory", "x0"); 17 | return true; 18 | } 19 | 20 | /* 21 | inline uint64_t sprr_read() { 22 | uint64_t v; 23 | __asm__ __volatile__( 24 | "isb sy\n" 25 | "mrs %0, s3_6_c15_c1_5\n" 26 | : "=r"(v)::"memory"); 27 | return v; 28 | } 29 | */ 30 | -------------------------------------------------------------------------------- /src/LCUtils/UIApplicationMain.m: -------------------------------------------------------------------------------- 1 | #import "FoundationPrivate.h" 2 | #import "GCSharedUtils.h" 3 | #import "fishhook/fishhook.h" 4 | #import "utils.h" 5 | #include 6 | #include 7 | 8 | // TODO: this is not implemented fully yet 9 | 10 | static int (*orig_UIApplicationMain)(int, char*[], void*, NSString*) = NULL; 11 | 12 | int hook_UIApplicationMain(int argc, char* argv[], void* principalClass, NSString* delegateClass) { 13 | // delegateClass = @"AppDelegate"; // launcher 14 | // delegateClass = @"AppController"; // gd 15 | return orig_UIApplicationMain(argc, argv, principalClass, delegateClass); 16 | } 17 | 18 | void UIAGuestHooksInit() { 19 | void* uikitHandle = dlopen("/System/Library/Frameworks/UIKit.framework/UIKit", RTLD_GLOBAL); 20 | void* uiApplicationMainAddr = dlsym(uikitHandle, "UIApplicationMain"); 21 | if (uiApplicationMainAddr) { 22 | rebind_symbols((struct rebinding[1]){ { "UIApplicationMain", hook_UIApplicationMain, (void*)&orig_UIApplicationMain } }, 1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/LCUtils/UIKitPrivate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface UIImage (private) 4 | - (UIImage*)_imageWithSize:(CGSize)size; 5 | @end 6 | 7 | @interface UIAlertAction (private) 8 | @property(nonatomic, copy) id shouldDismissHandler; 9 | @end 10 | 11 | @interface UIActivityContinuationManager : UIResponder 12 | - (NSDictionary*)handleActivityContinuation:(NSDictionary*)activityDict isSuspended:(id)isSuspended; 13 | @end 14 | 15 | @interface UIApplication (private) 16 | - (void)suspend; 17 | - (UIActivityContinuationManager*)_getActivityContinuationManager; 18 | @end 19 | 20 | @interface UIContextMenuInteraction (private) 21 | - (void)_presentMenuAtLocation:(CGPoint)location; 22 | @end 23 | 24 | @interface _UIContextMenuStyle : NSObject 25 | @property(nonatomic) NSInteger preferredLayout; 26 | + (instancetype)defaultStyle; 27 | @end 28 | 29 | @interface UIOpenURLAction : NSObject 30 | - (NSURL*)url; 31 | - (instancetype)initWithURL:(NSURL*)arg1; 32 | @end 33 | 34 | @interface FBSSceneTransitionContext : NSObject 35 | @property(nonatomic, copy) NSSet* actions; 36 | @end 37 | 38 | @interface UIApplicationSceneTransitionContext : FBSSceneTransitionContext 39 | @property(nonatomic, retain) NSDictionary* payload; 40 | @end 41 | 42 | @interface UITableViewHeaderFooterView (private) 43 | - (void)setText:(NSString*)text; 44 | - (NSString*)text; 45 | @end 46 | 47 | @interface UIApplicationSceneSettings : NSObject 48 | @end 49 | 50 | @interface UIApplicationSceneClientSettings : NSObject 51 | @end 52 | 53 | @interface UIMutableApplicationSceneSettings : UIApplicationSceneSettings 54 | @property(assign, nonatomic) UIDeviceOrientation deviceOrientation; 55 | - (void)setInterfaceOrientation:(NSInteger)o; 56 | @end 57 | 58 | @interface UIMutableApplicationSceneClientSettings : UIApplicationSceneClientSettings 59 | @property(assign, nonatomic) UIDeviceOrientation deviceOrientation; 60 | @property(nonatomic, assign) NSInteger interfaceOrientation; 61 | @property(nonatomic, assign) NSInteger statusBarStyle; 62 | @end 63 | 64 | @interface FBSMutableSceneParameters : NSObject 65 | @property(nonatomic, copy) UIMutableApplicationSceneSettings* settings; 66 | @end 67 | 68 | @interface FBSSceneParameters : NSObject 69 | @property(nonatomic, copy) UIApplicationSceneSettings* settings; 70 | @property(nonatomic, copy) UIApplicationSceneClientSettings* clientSettings; 71 | - (instancetype)initWithXPCDictionary:(NSDictionary*)dict; 72 | @end 73 | 74 | @interface UIWindow (private) 75 | - (void)setAutorotates:(BOOL)autorotates forceUpdateInterfaceOrientation:(BOOL)force; 76 | @end 77 | 78 | @interface LSApplicationWorkspace : NSObject 79 | + (instancetype)defaultWorkspace; 80 | - (BOOL)openApplicationWithBundleID:(NSString*)arg1; 81 | @end 82 | -------------------------------------------------------------------------------- /src/LCUtils/dyld_bypass_validation.m: -------------------------------------------------------------------------------- 1 | // Based on: https://blog.xpnsec.com/restoring-dyld-memory-loading 2 | // https://github.com/xpn/DyldDeNeuralyzer/blob/main/DyldDeNeuralyzer/DyldPatch/dyldpatch.m 3 | 4 | #import "src/components/LogUtils.h" 5 | #import 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "utils.h" 18 | 19 | #define ASM(...) __asm__(#__VA_ARGS__) 20 | // ldr x8, value; br x8; value: .ascii "\x41\x42\x43\x44\x45\x46\x47\x48" 21 | static char patch[] = { 0x88, 0x00, 0x00, 0x58, 0x00, 0x01, 0x1f, 0xd6, 0x1f, 0x20, 0x03, 0xd5, 0x1f, 0x20, 0x03, 0xd5, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }; 22 | 23 | // Signatures to search for 24 | static char mmapSig[] = { 0xB0, 0x18, 0x80, 0xD2, 0x01, 0x10, 0x00, 0xD4 }; 25 | static char fcntlSig[] = { 0x90, 0x0B, 0x80, 0xD2, 0x01, 0x10, 0x00, 0xD4 }; 26 | static char syscallSig[] = { 0x01, 0x10, 0x00, 0xD4 }; 27 | 28 | static int (*dopamineFcntlHookAddr)(int fildes, int cmd, void* param) = 0; 29 | 30 | extern void* __mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset); 31 | extern int __fcntl(int fildes, int cmd, void* param); 32 | 33 | // Since we're patching libsystem_kernel, we must avoid calling to its functions 34 | static void builtin_memcpy(char* target, char* source, size_t size) { 35 | for (int i = 0; i < size; i++) { 36 | target[i] = source[i]; 37 | } 38 | } 39 | 40 | // Originated from _kernelrpc_mach_vm_protect_trap 41 | ASM(.global _builtin_vm_protect \n _builtin_vm_protect : \n mov x16, # - 0xe \n svc #0x80 \n ret); 42 | 43 | static bool redirectFunction(char* name, void* patchAddr, void* target) { 44 | kern_return_t kret = builtin_vm_protect(mach_task_self(), (vm_address_t)patchAddr, sizeof(patch), false, PROT_READ | PROT_WRITE | VM_PROT_COPY); 45 | if (kret != KERN_SUCCESS) { 46 | AppLog(@"vm_protect(RW) fails at line %d", __LINE__); 47 | return FALSE; 48 | } 49 | 50 | builtin_memcpy((char*)patchAddr, patch, sizeof(patch)); 51 | *(void**)((char*)patchAddr + 16) = target; 52 | 53 | kret = builtin_vm_protect(mach_task_self(), (vm_address_t)patchAddr, sizeof(patch), false, PROT_READ | PROT_EXEC); 54 | if (kret != KERN_SUCCESS) { 55 | AppLog(@"vm_protect(RX) fails at line %d", __LINE__); 56 | return FALSE; 57 | } 58 | 59 | AppLog(@"hook %s succeed!", name); 60 | return TRUE; 61 | } 62 | 63 | static bool searchAndPatch(char* name, char* base, char* signature, int length, void* target) { 64 | char* patchAddr = NULL; 65 | 66 | AppLog(@"searching for %s...", name, patchAddr); 67 | // TODO: maybe add a condition for if the user really has dopamine, considering that I may need to look further into the address space 68 | // but it crashes if I have it too big!? wacky 69 | // for(int i=0; i < 0x100000; i++) { 70 | for (int i = 0; i < 0x80000; i++) { // i+=4 71 | if (base[i] == signature[0] && memcmp(base + i, signature, length) == 0) { 72 | patchAddr = base + i; 73 | break; 74 | } 75 | } 76 | 77 | if (patchAddr == NULL) { 78 | AppLog(@"hook %s fails line %d", name, __LINE__); 79 | return FALSE; 80 | } 81 | 82 | AppLog(@"found %s at %p", name, patchAddr); 83 | return redirectFunction(name, patchAddr, target); 84 | } 85 | 86 | static struct dyld_all_image_infos* _alt_dyld_get_all_image_infos() { 87 | static struct dyld_all_image_infos* result; 88 | if (result) { 89 | return result; 90 | } 91 | struct task_dyld_info dyld_info; 92 | mach_vm_address_t image_infos; 93 | mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; 94 | kern_return_t ret; 95 | ret = task_info(mach_task_self_, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count); 96 | if (ret != KERN_SUCCESS) { 97 | return NULL; 98 | } 99 | image_infos = dyld_info.all_image_info_addr; 100 | result = (struct dyld_all_image_infos*)image_infos; 101 | return result; 102 | } 103 | 104 | static void* getDyldBase(void) { return (void*)_alt_dyld_get_all_image_infos()->dyldImageLoadAddress; } 105 | 106 | static void* hooked_mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) { 107 | void* map = __mmap(addr, len, prot, flags, fd, offset); 108 | if (map == MAP_FAILED && fd && (prot & PROT_EXEC)) { 109 | map = __mmap(addr, len, PROT_READ | PROT_WRITE, flags | MAP_PRIVATE | MAP_ANON, 0, 0); 110 | void* memoryLoadedFile = __mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, offset); 111 | memcpy(map, memoryLoadedFile, len); 112 | munmap(memoryLoadedFile, len); 113 | mprotect(map, len, prot); 114 | } 115 | return map; 116 | } 117 | 118 | static int hooked___fcntl(int fildes, int cmd, void* param) { 119 | if (cmd == F_ADDFILESIGS_RETURN) { 120 | char filePath[PATH_MAX]; 121 | bzero(filePath, PATH_MAX); 122 | 123 | // Check if the file is our "in-memory" file 124 | if (__fcntl(fildes, F_GETPATH, filePath) != -1) { 125 | const char* homeDir = getenv("GC_HOME_PATH"); 126 | if (!strncmp(filePath, homeDir, strlen(homeDir))) { 127 | fsignatures_t* fsig = (fsignatures_t*)param; 128 | // called to check that cert covers file.. so we'll make it cover everything ;) 129 | fsig->fs_file_start = 0xFFFFFFFF; 130 | return 0; 131 | } 132 | } 133 | } 134 | 135 | // Signature sanity check by dyld 136 | else if (cmd == F_CHECK_LV) { 137 | // Just say everything is fine 138 | return 0; 139 | } 140 | 141 | // If for another command or file, we pass through 142 | // return __fcntl(fildes, cmd, param); 143 | 144 | // dopamine already hooks fcntl?? so i guess we will call their func instead... 145 | if (dopamineFcntlHookAddr) { 146 | return dopamineFcntlHookAddr(fildes, cmd, param); 147 | } else { 148 | return __fcntl(fildes, cmd, param); 149 | } 150 | } 151 | 152 | void init_bypassDyldLibValidation() { 153 | static BOOL bypassed; 154 | if (bypassed) 155 | return; 156 | bypassed = YES; 157 | 158 | AppLog(@"init"); 159 | 160 | // Modifying exec page during execution may cause SIGBUS, so ignore it now 161 | // Only comment this out if only one thread (main) is running 162 | // signal(SIGBUS, SIG_IGN); 163 | char* dyldBase = getDyldBase(); 164 | // redirectFunction("mmap", mmap, hooked_mmap); 165 | // redirectFunction("fcntl", fcntl, hooked_fcntl); 166 | searchAndPatch("dyld_mmap", dyldBase, mmapSig, sizeof(mmapSig), hooked_mmap); 167 | bool ret = searchAndPatch("dyld_fcntl", dyldBase, fcntlSig, sizeof(fcntlSig), hooked___fcntl); 168 | 169 | // fix for dopamine giving that "oh this code isnt signed!", or specifically "not valid for use in process" issue 170 | if (!ret) { 171 | // this should ONLY RUN if the hook failed 172 | char* fcntlAddr = 0; 173 | for (int i = 0; i < 0x80000; i += 4) { 174 | if (dyldBase[i] == syscallSig[0] && memcmp(dyldBase + i, syscallSig, 4) == 0) { 175 | char* syscallAddr = dyldBase + i; 176 | uint32_t* prev = (uint32_t*)(syscallAddr - 4); 177 | if (*prev >> 26 == 0x5) { 178 | fcntlAddr = (char*)prev; 179 | break; 180 | } 181 | } 182 | } 183 | if (fcntlAddr) { 184 | uint32_t* inst = (uint32_t*)fcntlAddr; 185 | int32_t offset = ((int32_t)((*inst) << 6)) >> 4; 186 | AppLog(@"Dopamine hook offset = %x", offset); 187 | dopamineFcntlHookAddr = (void*)((char*)fcntlAddr + offset); 188 | redirectFunction("dyld_fcntl (Dopamine)", fcntlAddr, hooked___fcntl); 189 | } else { 190 | AppLog(@"Dopamine hook not found"); 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/LCUtils/unarchive.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | extern int extract(NSString* fileToExtract, NSString* extractionPath, NSProgress* progress); 4 | extern CGFloat getProgress(); 5 | -------------------------------------------------------------------------------- /src/LCUtils/unarchive.m: -------------------------------------------------------------------------------- 1 | #import "src/components/LogUtils.h" 2 | #import "unarchive.h" 3 | 4 | #include "archive.h" 5 | #include "archive_entry.h" 6 | 7 | CGFloat completedUnitCount = 0; 8 | CGFloat totalUnitCount = 0; 9 | 10 | static int copy_data(struct archive* ar, struct archive* aw, NSProgress* progress) { 11 | int r; 12 | const void* buff; 13 | size_t size; 14 | la_int64_t offset; 15 | 16 | for (;;) { 17 | r = archive_read_data_block(ar, &buff, &size, &offset); 18 | if (r == ARCHIVE_EOF) 19 | return (ARCHIVE_OK); 20 | if (r < ARCHIVE_OK) 21 | return (r); 22 | r = archive_write_data_block(aw, buff, size, offset); 23 | if (r < ARCHIVE_OK) { 24 | fprintf(stderr, "%s\n", archive_error_string(aw)); 25 | return (r); 26 | } 27 | progress.completedUnitCount += size; 28 | completedUnitCount += size; 29 | } 30 | } 31 | 32 | bool forceProgress = false; 33 | 34 | CGFloat getProgress() { 35 | if (forceProgress) { 36 | return 100; 37 | } 38 | CGFloat progress = ((completedUnitCount / totalUnitCount) * 100); 39 | if (!(progress >= 0)) { 40 | return 1; 41 | } else { 42 | return progress; 43 | } 44 | } 45 | 46 | int extract(NSString* fileToExtract, NSString* extractionPath, NSProgress* progress) { 47 | forceProgress = false; 48 | completedUnitCount = 0; 49 | totalUnitCount = 0; 50 | struct archive* a; 51 | struct archive* ext; 52 | struct archive_entry* entry; 53 | int flags; 54 | int r; 55 | 56 | /* Select which attributes we want to restore. */ 57 | flags = ARCHIVE_EXTRACT_TIME; 58 | flags |= ARCHIVE_EXTRACT_PERM; 59 | flags |= ARCHIVE_EXTRACT_ACL; 60 | flags |= ARCHIVE_EXTRACT_FFLAGS; 61 | 62 | // Calculate decompressed size 63 | a = archive_read_new(); 64 | archive_read_support_format_all(a); 65 | archive_read_support_filter_all(a); 66 | if ((r = archive_read_open_filename(a, fileToExtract.fileSystemRepresentation, 10240))) { 67 | archive_read_free(a); 68 | AppLog(@"Failed to open archive: %@", fileToExtract); 69 | forceProgress = true; 70 | return 1; 71 | } 72 | while ((r = archive_read_next_header(a, &entry)) != ARCHIVE_EOF) { 73 | if (r < ARCHIVE_OK) { 74 | fprintf(stderr, "%s\n", archive_error_string(a)); 75 | AppLog(@"Error reading archive header: %s", archive_error_string(a)); 76 | } 77 | if (r < ARCHIVE_WARN) { 78 | archive_read_close(a); 79 | archive_read_free(a); 80 | AppLog(@"Archive warning: %s", archive_error_string(a)); 81 | return 1; 82 | } 83 | totalUnitCount += archive_entry_size(entry); 84 | progress.totalUnitCount += archive_entry_size(entry); 85 | } 86 | archive_read_close(a); 87 | archive_read_free(a); 88 | 89 | // Re-open the archive and extract 90 | a = archive_read_new(); 91 | archive_read_support_format_all(a); 92 | archive_read_support_filter_all(a); 93 | if ((r = archive_read_open_filename(a, fileToExtract.fileSystemRepresentation, 10240))) { 94 | archive_read_free(a); 95 | AppLog(@"Failed to reopen archive for extraction: %@", fileToExtract); 96 | forceProgress = true; 97 | return 1; 98 | } 99 | ext = archive_write_disk_new(); 100 | archive_write_disk_set_options(ext, flags); 101 | archive_write_disk_set_standard_lookup(ext); 102 | 103 | while ((r = archive_read_next_header(a, &entry)) != ARCHIVE_EOF) { 104 | if (r == ARCHIVE_EOF) 105 | break; 106 | if (r < ARCHIVE_OK) { 107 | fprintf(stderr, "%s\n", archive_error_string(a)); 108 | AppLog(@"Error reading header: %s", archive_error_string(a)); 109 | } 110 | if (r < ARCHIVE_WARN) 111 | break; 112 | 113 | NSString* currentFile = [NSString stringWithUTF8String:archive_entry_pathname(entry)]; 114 | NSString* fullOutputPath = [extractionPath stringByAppendingPathComponent:currentFile]; 115 | // printf("extracting %@ to %@\n", currentFile, fullOutputPath); 116 | archive_entry_set_pathname(entry, fullOutputPath.fileSystemRepresentation); 117 | 118 | r = archive_write_header(ext, entry); 119 | if (r < ARCHIVE_OK) { 120 | fprintf(stderr, "%s\n", archive_error_string(ext)); 121 | AppLog(@"Error writing header: %s", archive_error_string(ext)); 122 | } else if (archive_entry_size(entry) > 0) { 123 | r = copy_data(a, ext, progress); 124 | if (r < ARCHIVE_OK) { 125 | fprintf(stderr, "%s\n", archive_error_string(ext)); 126 | AppLog(@"Error copying data: %s", archive_error_string(ext)); 127 | } 128 | if (r < ARCHIVE_WARN) 129 | break; 130 | } 131 | r = archive_write_finish_entry(ext); 132 | if (r < ARCHIVE_OK) { 133 | fprintf(stderr, "%s\n", archive_error_string(ext)); 134 | AppLog(@"Error finishing entry: %s", archive_error_string(ext)); 135 | } 136 | if (r < ARCHIVE_WARN) 137 | break; 138 | } 139 | archive_read_close(a); 140 | archive_read_free(a); 141 | archive_write_close(ext); 142 | archive_write_free(ext); 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /src/LCUtils/utils.h: -------------------------------------------------------------------------------- 1 | #import 2 | #include 3 | #include 4 | 5 | const char** _CFGetProgname(void); 6 | const char** _CFGetProcessPath(void); 7 | int _NSGetExecutablePath(char* buf, uint32_t* bufsize); 8 | 9 | #define CS_DEBUGGED 0x10000000 10 | int csops(pid_t pid, unsigned int ops, void* useraddr, size_t usersize); 11 | 12 | void init_bypassDyldLibValidation(); 13 | kern_return_t builtin_vm_protect(mach_port_name_t task, mach_vm_address_t address, mach_vm_size_t size, boolean_t set_max, vm_prot_t new_prot); 14 | 15 | uint64_t aarch64_get_tbnz_jump_address(uint32_t instruction, uint64_t pc); 16 | uint64_t aarch64_emulate_adrp(uint32_t instruction, uint64_t pc); 17 | bool aarch64_emulate_add_imm(uint32_t instruction, uint32_t* dst, uint32_t* src, uint32_t* imm); 18 | uint64_t aarch64_emulate_adrp_add(uint32_t instruction, uint32_t addInstruction, uint64_t pc); 19 | uint64_t aarch64_emulate_adrp_ldr(uint32_t instruction, uint32_t ldrInstruction, uint64_t pc); 20 | -------------------------------------------------------------------------------- /src/LCUtils/utils.m: -------------------------------------------------------------------------------- 1 | #import "utils.h" 2 | 3 | void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) { 4 | [NSException raise:NSInternalInconsistencyException format:@"Assertion failed: (%s), file %s, line %d.\n", failedexpr, file, line]; 5 | abort(); // silent compiler warning 6 | } 7 | 8 | uint64_t aarch64_get_tbnz_jump_address(uint32_t instruction, uint64_t pc) { 9 | // Check that this is a tbnz instruction 10 | if ((instruction & 0xFF000000) != 0x37000000) { 11 | return 0; 12 | } 13 | 14 | uint32_t imm = ((instruction >> 5) & 0xFFFF) * 4; 15 | return imm + pc; 16 | } 17 | 18 | // https://github.com/pinauten/PatchfinderUtils/blob/master/Sources/CFastFind/CFastFind.c 19 | // 20 | // CFastFind.c 21 | // CFastFind 22 | // 23 | // Created by Linus Henze on 2021-10-16. 24 | // Copyright © 2021 Linus Henze. All rights reserved. 25 | // 26 | 27 | /** 28 | * Emulate an adrp instruction at the given pc value 29 | * Returns adrp destination 30 | */ 31 | uint64_t aarch64_emulate_adrp(uint32_t instruction, uint64_t pc) { 32 | // Check that this is an adrp instruction 33 | if ((instruction & 0x9F000000) != 0x90000000) { 34 | return 0; 35 | } 36 | 37 | // Calculate imm from hi and lo 38 | int32_t imm_hi_lo = (instruction & 0xFFFFE0) >> 3; 39 | imm_hi_lo |= (instruction & 0x60000000) >> 29; 40 | if (instruction & 0x800000) { 41 | // Sign extend 42 | imm_hi_lo |= 0xFFE00000; 43 | } 44 | 45 | // Build real imm 46 | int64_t imm = ((int64_t)imm_hi_lo << 12); 47 | 48 | // Emulate 49 | return (pc & ~(0xFFFULL)) + imm; 50 | } 51 | 52 | bool aarch64_emulate_add_imm(uint32_t instruction, uint32_t* dst, uint32_t* src, uint32_t* imm) { 53 | // Check that this is an add instruction with immediate 54 | if ((instruction & 0xFF000000) != 0x91000000) { 55 | return 0; 56 | } 57 | 58 | int32_t imm12 = (instruction & 0x3FFC00) >> 10; 59 | 60 | uint8_t shift = (instruction & 0xC00000) >> 22; 61 | switch (shift) { 62 | case 0: 63 | *imm = imm12; 64 | break; 65 | 66 | case 1: 67 | *imm = imm12 << 12; 68 | break; 69 | 70 | default: 71 | return false; 72 | } 73 | 74 | *dst = instruction & 0x1F; 75 | *src = (instruction >> 5) & 0x1F; 76 | 77 | return true; 78 | } 79 | 80 | /** 81 | * Emulate an adrp and add instruction at the given pc value 82 | * Returns destination 83 | */ 84 | 85 | uint64_t aarch64_emulate_adrp_add(uint32_t instruction, uint32_t addInstruction, uint64_t pc) { 86 | uint64_t adrp_target = aarch64_emulate_adrp(instruction, pc); 87 | if (!adrp_target) { 88 | return 0; 89 | } 90 | 91 | uint32_t addDst; 92 | uint32_t addSrc; 93 | uint32_t addImm; 94 | if (!aarch64_emulate_add_imm(addInstruction, &addDst, &addSrc, &addImm)) { 95 | return 0; 96 | } 97 | 98 | if ((instruction & 0x1F) != addSrc) { 99 | return 0; 100 | } 101 | 102 | // Emulate 103 | return adrp_target + (uint64_t)addImm; 104 | } 105 | 106 | /** 107 | * Emulate an adrp and ldr instruction at the given pc value 108 | * Returns destination 109 | */ 110 | 111 | uint64_t aarch64_emulate_adrp_ldr(uint32_t instruction, uint32_t ldrInstruction, uint64_t pc) { 112 | uint64_t adrp_target = aarch64_emulate_adrp(instruction, pc); 113 | if (!adrp_target) { 114 | return 0; 115 | } 116 | 117 | if ((instruction & 0x1F) != ((ldrInstruction >> 5) & 0x1F)) { 118 | return 0; 119 | } 120 | 121 | if ((ldrInstruction & 0xFFC00000) != 0xF9400000) { 122 | return 0; 123 | } 124 | 125 | uint32_t imm12 = ((ldrInstruction >> 10) & 0xFFF) << 3; 126 | 127 | // Emulate 128 | return adrp_target + (uint64_t)imm12; 129 | } 130 | -------------------------------------------------------------------------------- /src/Localization.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NSString (Localization) 4 | @property(readonly, nonnull, getter=localized) NSString* loc; 5 | - (instancetype _Nonnull)localized; 6 | - (instancetype _Nonnull)localizeWithFormat:(NSString* _Nonnull)format, ...; 7 | @end 8 | -------------------------------------------------------------------------------- /src/Localization.m: -------------------------------------------------------------------------------- 1 | #import "Localization.h" 2 | 3 | @implementation NSString (Localization) 4 | - (NSString*)localized { 5 | NSString* str = NSLocalizedString(self, nil); 6 | if ([str isEqualToString:self]) { // no translations found! 7 | NSDictionary* enLocal = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@"en.lproj/Localizable.strings"]]; 8 | str = enLocal[self]; 9 | if (!str) { 10 | str = self; 11 | } 12 | } 13 | return str; 14 | } 15 | - (NSString*)localizeWithFormat:(NSString*)arg1, ... { 16 | va_list args; 17 | va_start(args, arg1); 18 | NSString* formattedString = [NSString localizedStringWithFormat:[self localized], arg1, args]; 19 | va_end(args); 20 | return formattedString; 21 | } 22 | @end 23 | -------------------------------------------------------------------------------- /src/LogsView.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | @interface LogsViewController : UIViewController 4 | - (instancetype)initWithFile:(NSURL*)fileURL; 5 | @end 6 | -------------------------------------------------------------------------------- /src/LogsView.m: -------------------------------------------------------------------------------- 1 | #import "LogsView.h" 2 | #import "Utils.h" 3 | #import "components/LogUtils.h" 4 | #import 5 | #import 6 | 7 | @interface LogsViewController () 8 | @property(nonatomic, strong) NSURL* fileURL; 9 | @property(nonatomic, strong) UITextView* textView; 10 | @end 11 | 12 | @implementation LogsViewController 13 | 14 | - (instancetype)initWithFile:(NSURL*)fileURL { 15 | self = [super initWithNibName:nil bundle:nil]; 16 | if (self) { 17 | _fileURL = fileURL; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | UIBarButtonItem* shareButton = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"square.and.arrow.up"] style:UIBarButtonItemStylePlain target:self 26 | action:@selector(shareLogs)]; 27 | self.navigationItem.rightBarButtonItem = shareButton; 28 | 29 | self.textView = [[UITextView alloc] initWithFrame:self.view.bounds]; 30 | self.textView.font = [UIFont systemFontOfSize:12 weight:UIFontWeightRegular]; 31 | self.textView.alwaysBounceVertical = YES; 32 | self.textView.contentSize = self.view.bounds.size; 33 | self.textView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; 34 | self.textView.translatesAutoresizingMaskIntoConstraints = NO; 35 | self.textView.editable = NO; 36 | self.textView.selectable = YES; 37 | self.textView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 38 | 39 | self.view.clipsToBounds = YES; 40 | self.view.autoresizesSubviews = YES; 41 | 42 | // fix invisible padding 43 | self.textView.contentInset = UIEdgeInsetsMake(60, 0, 0, 0); 44 | self.textView.textContainerInset = UIEdgeInsetsZero; 45 | 46 | [self.view addSubview:self.textView]; 47 | [NSLayoutConstraint activateConstraints:@[ 48 | [self.textView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], 49 | [self.textView.topAnchor constraintEqualToAnchor:self.view.topAnchor], 50 | [self.textView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], 51 | [self.textView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], 52 | ]]; 53 | NSError* error; 54 | 55 | if ([self.fileURL checkResourceIsReachableAndReturnError:&error]) { 56 | NSString* fileName = self.fileURL.lastPathComponent; 57 | if ([fileName isEqualToString:@"app.log"]) { 58 | fileName = [NSString stringWithFormat:@"app.log (Clearing in %lu launches)", (5 - [[Utils getPrefs] integerForKey:@"LAUNCH_COUNT"] % 5)]; 59 | } 60 | self.textView.text = [NSString 61 | stringWithFormat:@"%@\n============================\n%@", fileName, [NSString stringWithContentsOfURL:self.fileURL encoding:NSUTF8StringEncoding error:&error]]; 62 | } 63 | if (error) { 64 | AppLog(@"Error reading log file: %@", error); 65 | self.textView.text = [@"logs.error" localizeWithFormat:self.fileURL.lastPathComponent]; 66 | } 67 | } 68 | 69 | - (void)shareLogs { 70 | if (self.textView.text.length == 0) { 71 | [Utils showError:self title:@"logs.share-error".loc error:nil]; 72 | return; 73 | } 74 | UIActivityViewController* activityViewController = [[UIActivityViewController alloc] initWithActivityItems:[NSArray arrayWithObjects:self.fileURL, nil] 75 | applicationActivities:nil]; 76 | activityViewController.popoverPresentationController.sourceView = self.view; 77 | [self presentViewController:activityViewController animated:YES completion:nil]; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /src/RootViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RootViewController : UIViewController 4 | @property(nonatomic, strong) UIImageView* logoImageView; 5 | @property(nonatomic, strong) UILabel* titleLabel; 6 | @property(nonatomic, strong) UILabel* optionalTextLabel; 7 | @property(nonatomic, strong) UIButton* launchButton; 8 | @property(nonatomic, strong) UIButton* settingsButton; 9 | - (void)updateState; 10 | - (void)cancelDownload; 11 | 12 | // sorry i dont want to deal with dumb link errors 13 | - (BOOL)progressVisible; 14 | - (void)progressVisibility:(BOOL)hidden; 15 | - (void)progressCancelVisibility:(BOOL)hidden; 16 | - (void)progressText:(NSString*)text; 17 | - (void)barProgress:(CGFloat)value; 18 | - (void)signApp:(BOOL)force completionHandler:(void (^)(BOOL success, NSString* error))completionHandler; 19 | - (void)refreshTheme; 20 | @end 21 | -------------------------------------------------------------------------------- /src/SettingsVC.h: -------------------------------------------------------------------------------- 1 | #import "MSColorPicker/MSColorPicker/MSColorSelectionViewController.h" 2 | #import "RootViewController.h" 3 | #include 4 | 5 | @interface SettingsVC 6 | : UIViewController 7 | @property(nonatomic, strong) UITableView* tableView; 8 | @property(nonatomic, strong) RootViewController* root; 9 | @end 10 | -------------------------------------------------------------------------------- /src/Theming.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface Theming : NSObject 5 | + (BOOL)darkModeEnabled; 6 | + (UIColor*)getDarkColor; 7 | + (UIColor*)getBackgroundColor; 8 | + (UIColor*)getAccentColor; 9 | + (UIColor*)getTextColor:(UIColor*)color; 10 | + (void)saveAccentColor:(UIColor*)color; 11 | + (UIColor*)getWhiteColor; 12 | + (UIColor*)getFooterColor; 13 | @end 14 | -------------------------------------------------------------------------------- /src/Theming.m: -------------------------------------------------------------------------------- 1 | #import "Theming.h" 2 | #import "Utils.h" 3 | #import "components/LogUtils.h" 4 | 5 | @implementation Theming 6 | + (BOOL)darkModeEnabled { 7 | UIWindow* keyWindow = nil; 8 | for (UIWindow* window in [UIApplication sharedApplication].windows) { 9 | if (window.isKeyWindow) { 10 | keyWindow = window; 11 | break; 12 | } 13 | } 14 | if (!keyWindow) { 15 | keyWindow = [UIApplication sharedApplication].windows.firstObject; 16 | } 17 | if (!keyWindow) { 18 | return NO; 19 | } 20 | return (keyWindow.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark); 21 | } 22 | + (UIColor*)getDarkColor { 23 | if ([Theming darkModeEnabled]) { 24 | return [UIColor colorWithRed:0.15 green:0.15 blue:0.15 alpha:1.00]; 25 | } else { 26 | return [UIColor colorWithRed:0.85 green:0.85 blue:0.85 alpha:1.00]; 27 | } 28 | } 29 | + (UIColor*)getBackgroundColor { 30 | if ([Theming darkModeEnabled]) { 31 | return [UIColor colorWithRed:0.07 green:0.07 blue:0.09 alpha:1.00]; 32 | } else { 33 | return [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.00]; 34 | } 35 | } 36 | + (UIColor*)getWhiteColor { 37 | if ([Theming darkModeEnabled]) { 38 | return [UIColor whiteColor]; 39 | } else { 40 | return [UIColor blackColor]; 41 | } 42 | } 43 | + (UIColor*)getFooterColor { 44 | if ([Theming darkModeEnabled]) { 45 | return [UIColor lightGrayColor]; 46 | } else { 47 | return [UIColor darkGrayColor]; 48 | } 49 | } 50 | + (UIColor*)getAccentColor { 51 | NSData* colorData = [[Utils getPrefs] dataForKey:@"accentColor"]; 52 | NSError* error = nil; 53 | if (colorData) { 54 | UIColor* accentColor = [NSKeyedUnarchiver unarchivedObjectOfClass:[UIColor class] fromData:colorData error:&error]; 55 | if (accentColor) { 56 | return accentColor; 57 | } 58 | } 59 | if ([Theming darkModeEnabled]) { 60 | return [UIColor colorWithRed:0.70 green:0.77 blue:1.00 alpha:1.00]; 61 | } else { 62 | return [UIColor colorWithRed:0.4 green:0.55 blue:1.00 alpha:1.00]; 63 | } 64 | // apple loves blue 65 | // return [UIColor systemBlueColor]; 66 | } 67 | 68 | + (void)saveAccentColor:(UIColor*)color { 69 | NSUserDefaults* userDefaults = [Utils getPrefs]; 70 | NSError* error = nil; 71 | NSData* colorData = [NSKeyedArchiver archivedDataWithRootObject:color requiringSecureCoding:YES error:&error]; 72 | if (error) { 73 | AppLog(@"Couldn't archive accent color: %@", error); 74 | return; 75 | } 76 | [userDefaults setObject:colorData forKey:@"accentColor"]; 77 | } 78 | 79 | + (UIColor*)getTextColor:(UIColor*)color { 80 | CGFloat red, green, blue, alpha; 81 | [color getRed:&red green:&green blue:&blue alpha:&alpha]; 82 | CGFloat brightness = (0.299 * red) + (0.587 * green) + (0.114 * blue); 83 | return brightness > 0.5 ? [UIColor blackColor] : [UIColor whiteColor]; 84 | } 85 | @end 86 | -------------------------------------------------------------------------------- /src/Utils.h: -------------------------------------------------------------------------------- 1 | #import "Localization.h" 2 | #import 3 | #import 4 | 5 | @interface CompareSemVer : NSObject 6 | + (BOOL)isVersion:(NSString*)versionA greaterThanVersion:(NSString*)versionB; 7 | @end 8 | 9 | @interface Utils : NSObject 10 | + (NSString*)gdBundleName; 11 | + (NSString*)launcherBundleName; 12 | + (NSString*)getGeodeVersion; 13 | 14 | + (NSString*)docPath; 15 | 16 | + (void)updateGeodeVersion:(NSString*)newVer; 17 | + (BOOL)isJailbroken; 18 | + (NSString*)getGeodeReleaseURL; 19 | + (NSString*)getGeodeLauncherURL; 20 | + (NSString*)getGeodeLauncherRedirect; 21 | + (UIImageView*)imageViewFromPDF:(NSString*)pdfName; 22 | + (NSURL*)pathToMostRecentLogInDirectory:(NSString*)directoryPath; 23 | + (void)showErrorGlobal:(NSString*)title error:(NSError*)error; 24 | + (void)showError:(UIViewController*)root title:(NSString*)title error:(NSError*)error; 25 | + (void)showNoticeGlobal:(NSString*)title; 26 | + (void)showNotice:(UIViewController*)root title:(NSString*)title; 27 | + (NSString*)archName; 28 | + (void)toggleKey:(NSString*)key; 29 | + (NSString*)sha256sum:(NSString*)path; 30 | + (NSString*)sha256sumWithData:(NSData*)data; 31 | + (NSString*)getGDDocPath; 32 | + (NSString*)getGDBinaryPath; 33 | + (NSString*)getGDBundlePath; 34 | + (NSUserDefaults*)getPrefs; 35 | + (NSUserDefaults*)getPrefsGC; 36 | + (BOOL)isSandboxed; 37 | + (BOOL)isContainerized; 38 | + (const char*)getKillAllPath; 39 | + (void)increaseLaunchCount; 40 | + (void)tweakLaunch_withSafeMode:(BOOL)safemode; 41 | + (NSString*)colorToHex:(UIColor*)color; 42 | 43 | + (NSData*)encryptData:(NSData*)data withKey:(NSString*)key; 44 | + (NSData*)decryptData:(NSData*)data withKey:(NSString*)key; 45 | @end 46 | -------------------------------------------------------------------------------- /src/VerifyInstall.h: -------------------------------------------------------------------------------- 1 | #import "RootViewController.h" 2 | #import 3 | #import 4 | 5 | @interface VerifyInstall : NSObject 6 | + (BOOL)verifyGDAuthenticity; 7 | + (void)startVerifyGDAuth:(RootViewController*)root; 8 | + (BOOL)canLaunchAppWithBundleID:(NSString*)bundleID; 9 | + (BOOL)verifyGDInstalled; 10 | + (void)startGDInstall:(RootViewController*)root url:(NSURL*)url; 11 | + (BOOL)verifyGeodeInstalled; 12 | //+ (void)startGeodeInstall; 13 | + (BOOL)verifyAll; 14 | @end 15 | -------------------------------------------------------------------------------- /src/WebServer.h: -------------------------------------------------------------------------------- 1 | #import "GCDWebServer/GCDWebServer/Core/GCDWebServer.h" 2 | #import 3 | 4 | @interface WebServer : NSObject 5 | @property(nonatomic, strong) GCDWebServer* webServer; 6 | - (void)initServer; 7 | @end 8 | -------------------------------------------------------------------------------- /src/components/LogUtils.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | @interface LogUtils : NSObject 4 | 5 | + (void)log:(NSString*)format, ...; 6 | + (void)clearLogs; 7 | 8 | @end 9 | #define AppLog(x...) [LogUtils log:x]; 10 | -------------------------------------------------------------------------------- /src/components/LogUtils.m: -------------------------------------------------------------------------------- 1 | #import "LogUtils.h" 2 | #include "src/Utils.h" 3 | 4 | static const NSUInteger MAX_LOG_FILE_SIZE = 1024 * 1024; // 1 MB 5 | static dispatch_queue_t loggingQueue; 6 | 7 | @implementation LogUtils 8 | 9 | + (void)initialize { 10 | static dispatch_once_t onceToken; 11 | dispatch_once(&onceToken, ^{ loggingQueue = dispatch_queue_create("com.geode.logqueue", DISPATCH_QUEUE_SERIAL); }); 12 | } 13 | 14 | + (NSString*)logFilePath { 15 | NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 16 | if ([paths[0] hasSuffix:@"GeometryDash/Documents"]) { 17 | // a fix since im lazy to actually get the shared doc 18 | return [paths[0] stringByAppendingPathComponent:@"../../../../app.log"]; 19 | } else { 20 | return [paths[0] stringByAppendingPathComponent:@"app.log"]; 21 | } 22 | } 23 | 24 | + (void)log:(NSString*)format, ... { 25 | NSString* callSource = [[NSThread callStackSymbols] objectAtIndex:1]; 26 | if (!callSource) 27 | callSource = @"[Unknown]"; 28 | 29 | NSArray* parts = [callSource componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]]; 30 | NSString* srcFunc = @"Unknown"; 31 | 32 | if (parts.count > 1) 33 | srcFunc = [parts objectAtIndex:1]; 34 | 35 | NSString* prefix = [NSString stringWithFormat:@"[GeodeLauncher/%@] ", [srcFunc substringToIndex:MIN([srcFunc rangeOfString:@" "].location, srcFunc.length)]]; 36 | format = [prefix stringByAppendingString:format]; 37 | 38 | va_list args; 39 | va_start(args, format); 40 | NSString* message = [[NSString alloc] initWithFormat:format arguments:args]; 41 | va_end(args); 42 | NSLog(@"%@", message); 43 | dispatch_async(loggingQueue, ^{ 44 | [self checkLogFileSize]; 45 | NSFileHandle* fileHandle = [NSFileHandle fileHandleForWritingAtPath:[self logFilePath]]; 46 | if (fileHandle) { 47 | [fileHandle seekToEndOfFile]; 48 | [fileHandle writeData:[[message stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]]; 49 | [fileHandle closeFile]; 50 | } else { 51 | [[message stringByAppendingString:@"\n"] writeToFile:[self logFilePath] atomically:YES encoding:NSUTF8StringEncoding error:nil]; 52 | } 53 | }); 54 | } 55 | 56 | + (void)checkLogFileSize { 57 | NSFileManager* fileManager = [NSFileManager defaultManager]; 58 | NSString* logPath = [self logFilePath]; 59 | 60 | if ([fileManager fileExistsAtPath:logPath]) { 61 | NSError* error = nil; 62 | NSDictionary* attrs = [fileManager attributesOfItemAtPath:logPath error:&error]; 63 | if (!error && [attrs fileSize] > MAX_LOG_FILE_SIZE) { 64 | [@"" writeToFile:logPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; 65 | } 66 | } 67 | } 68 | 69 | + (void)clearLogs { 70 | if ([[Utils getPrefs] integerForKey:@"LAUNCH_COUNT"] % 5 == 0) { 71 | dispatch_sync(loggingQueue, ^{ 72 | NSError* error = nil; 73 | [@"" writeToFile:[self logFilePath] atomically:YES encoding:NSUTF8StringEncoding error:&error]; 74 | }); 75 | } 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /src/components/ProgressBar.h: -------------------------------------------------------------------------------- 1 | #import "../RootViewController.h" 2 | #import 3 | 4 | @interface ProgressBar : UIView 5 | 6 | @property(nonatomic, assign) CGFloat progress; 7 | @property(nonatomic, copy) NSString* progressText; 8 | @property(nonatomic, assign) BOOL showCancelButton; 9 | @property(nonatomic, strong) RootViewController* root; 10 | //@property (weak, nonatomic) id delegate; 11 | 12 | - (instancetype)initWithFrame:(CGRect)frame progressText:(NSString*)progressText showCancelButton:(BOOL)showCancelButton root:(RootViewController*)root; 13 | 14 | - (void)setProgress:(CGFloat)progress; 15 | - (void)setCancelHidden:(BOOL)hidden; 16 | @end 17 | -------------------------------------------------------------------------------- /src/components/ProgressBar.m: -------------------------------------------------------------------------------- 1 | #import "../Theming.h" 2 | #import "ProgressBar.h" 3 | 4 | @interface ProgressBar () 5 | 6 | @property(nonatomic, strong) UIView* progressView; 7 | @property(nonatomic, strong) UILabel* progressLabel; 8 | @property(nonatomic, strong) UIButton* cancelButton; 9 | 10 | @end 11 | 12 | @implementation ProgressBar 13 | 14 | - (instancetype)initWithFrame:(CGRect)frame progressText:(NSString*)progressText showCancelButton:(BOOL)showCancelButton root:(RootViewController*)root { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | self.progressText = progressText; 18 | self.showCancelButton = showCancelButton; 19 | self.backgroundColor = [UIColor clearColor]; 20 | self.root = root; 21 | 22 | [self setupViews]; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)setupViews { 28 | CGFloat barHeight = 10.0; // height of the bar 29 | CGFloat textHeight = 20.0; // height of the text 30 | CGFloat buttonHeight = 30.0; // height of the button 31 | CGFloat spacing = 8.0; // spacing between 32 | 33 | if (self.progressText != nil) { 34 | self.progressLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, textHeight)]; 35 | self.progressLabel.textAlignment = NSTextAlignmentCenter; 36 | self.progressLabel.textColor = [Theming getWhiteColor]; 37 | self.progressLabel.text = [self.progressText stringByReplacingOccurrencesOfString:@"{percent}" withString:@"0"]; // Initialize with 0% 38 | [self addSubview:self.progressLabel]; 39 | } 40 | 41 | CGRect barFrame = CGRectMake(0, self.progressText ? textHeight + spacing : 0, self.frame.size.width, barHeight); 42 | UIView* barBackgroundView = [[UIView alloc] initWithFrame:barFrame]; 43 | barBackgroundView.backgroundColor = [Theming getDarkColor]; 44 | barBackgroundView.layer.cornerRadius = barHeight / 2.0; 45 | barBackgroundView.clipsToBounds = YES; 46 | [self addSubview:barBackgroundView]; 47 | 48 | self.progressView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, barHeight)]; 49 | self.progressView.backgroundColor = [Theming getAccentColor]; 50 | self.progressView.layer.cornerRadius = barHeight / 2.0; 51 | self.progressView.clipsToBounds = YES; 52 | [barBackgroundView addSubview:self.progressView]; 53 | 54 | if (self.showCancelButton) { 55 | self.cancelButton = [UIButton buttonWithType:UIButtonTypeSystem]; 56 | [self.cancelButton setTitle:@"Cancel" forState:UIControlStateNormal]; 57 | [self.cancelButton setTitleColor:[Theming getAccentColor] forState:UIControlStateNormal]; 58 | self.cancelButton.frame = CGRectMake(0, barFrame.origin.y + barHeight + spacing, self.frame.size.width, buttonHeight); 59 | [self.cancelButton addTarget:self action:@selector(cancelButtonTapped) forControlEvents:UIControlEventTouchUpInside]; 60 | [self addSubview:self.cancelButton]; 61 | } 62 | } 63 | 64 | - (void)setProgress:(CGFloat)progress { 65 | if (progress < 0) 66 | progress = 0; 67 | if (progress > 100) 68 | progress = 100; 69 | _progress = progress; 70 | CGFloat barWidth = (self.frame.size.width * progress) / 100.0; 71 | self.progressView.frame = CGRectMake(0, 0, barWidth, self.progressView.frame.size.height); 72 | if (self.progressText) { 73 | NSString* progressText = [NSString stringWithFormat:@"%.0f", progress]; 74 | self.progressLabel.text = [self.progressText stringByReplacingOccurrencesOfString:@"{percent}" withString:progressText]; 75 | } 76 | } 77 | 78 | - (void)cancelButtonTapped { 79 | [_root cancelDownload]; 80 | } 81 | 82 | - (void)setCancelHidden:(BOOL)hidden { 83 | if (self.cancelButton != nil) { 84 | [self.cancelButton setHidden:hidden]; 85 | } 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /ts-entitlements.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | application-identifier 6 | com.geode.launcher 7 | com.apple.developer.kernel.extended-virtual-addressing 8 | 9 | com.apple.developer.kernel.increased-memory-limit 10 | 11 | com.apple.security.application-groups 12 | 13 | group.com.SideStore.SideStore 14 | group.com.rileytestut.AltStore 15 | 16 | get-task-allow 17 | 18 | com.apple.private.MobileContainerManager.allowed 19 | 20 | platform-application 21 | 22 | com.apple.private.security.no-sandbox 23 | 24 | com.apple.private.security.storage.AppDataContainers 25 | 26 | 27 | 28 | --------------------------------------------------------------------------------