mirror of
https://github.com/wibus-wee/InjectGUI.git
synced 2025-11-26 03:44:54 +08:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
595d395202 | ||
|
|
b09d07313f | ||
|
|
df3854b8c9 | ||
|
|
85abf256db | ||
|
|
ecf16b33ac | ||
|
|
4bf17a9bef | ||
|
|
d35cea9e75 | ||
|
|
80e55d3a4d | ||
|
|
60b8688b5f | ||
|
|
032a842c7b | ||
|
|
199453137c | ||
|
|
ca063ce96e | ||
|
|
069e9e0d13 | ||
|
|
a06052e005 | ||
|
|
aa0b27d219 | ||
|
|
c6d8616076 | ||
|
|
a9e7c98523 | ||
|
|
114205aa4d | ||
|
|
1cbf2b8ca0 | ||
|
|
0c0d06df66 | ||
|
|
dba45c1d79 | ||
|
|
0e447de050 | ||
|
|
4f9df79875 | ||
|
|
77cccc8416 | ||
|
|
b116fd5084 | ||
|
|
17a25e4783 | ||
|
|
0dec3ad00a | ||
|
|
f105a21510 | ||
|
|
e6c28a1c77 | ||
|
|
77e77b2ced | ||
|
|
1eaa3d6071 | ||
|
|
541e83c63b | ||
|
|
d427614b01 | ||
|
|
d513754977 |
82
.github/workflows/build.yml
vendored
82
.github/workflows/build.yml
vendored
@@ -2,7 +2,7 @@ name: CI - Build Application
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- 'LICENSE'
|
||||
- '**.md'
|
||||
@@ -17,7 +17,7 @@ permissions:
|
||||
jobs:
|
||||
build-mac:
|
||||
name: Build for macOS (Universal)
|
||||
runs-on: macos-13
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -54,8 +54,9 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
hdiutil create -volname "InjectGUI" -srcfolder "build/Build/Products/${{ env.APP_CONF }}/InjectGUI.app" -ov -format UDZO "InjectGUI.dmg"
|
||||
|
||||
cp "InjectGUI.dmg" "$RUNNER_TEMP/InjectGUI.dmg"
|
||||
- name: Upload DMG Artifact
|
||||
id: upload-artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: InjectGUI
|
||||
@@ -90,7 +91,7 @@ jobs:
|
||||
# RELEASE_NOTES_URL: "https://codeedit.app/whats-new/"
|
||||
# RELEASE_NOTES_PREFIX: "https://codeedit.app/sparkle/"
|
||||
run: |
|
||||
SPARKLE_BIN="$RUNNER_TEMP/DerivedData/SourcePackages/artifacts/sparkle/Sparkle/bin"
|
||||
SPARKLE_BIN="$RUNNER_TEMP/build/SourcePackages/artifacts/sparkle/Sparkle/bin"
|
||||
SPARKLE_ARCHIVE="$RUNNER_TEMP/Sparkle_Archive"
|
||||
echo -n "$SPARKLE_KEY" | tee "$RUNNER_TEMP/sparkle_key"
|
||||
mkdir "$SPARKLE_ARCHIVE"
|
||||
@@ -103,4 +104,75 @@ jobs:
|
||||
if: github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: "${{ RUNNER.TEMP }}/Sparkle_Archive/appcast.xml"
|
||||
files: "${{ RUNNER.TEMP }}/Sparkle_Archive/appcast.xml"
|
||||
|
||||
- name: Upload Sparkle Artifact
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: appcast.xml
|
||||
path: "${{ RUNNER.TEMP }}/Sparkle_Archive/appcast.xml"
|
||||
|
||||
- name: Find Comment
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: 'InjectGUI'
|
||||
|
||||
- name: Gen Previous Builds List
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
shas_and_links=$(echo "${{ steps.fc.outputs.comment-body }}" | sed -n 's/.*\[\([^!][^]]*\)\].*/\1/p')
|
||||
result_list=""
|
||||
IFS=',' # 设置内部字段分隔符为逗号
|
||||
for entry in $shas_and_links; do
|
||||
sha=$(echo "$entry" | cut -d';' -f1)
|
||||
link=$(echo "$entry" | cut -d';' -f2)
|
||||
result_list+="<sup><li><a href='${link}'>${sha}</a></li></sup>"
|
||||
done
|
||||
|
||||
echo "PREVIOUS_BUILDS=${result_list}" >> $GITHUB_ENV
|
||||
|
||||
- name: Add new build to Previous Builds List
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
new_sha="${{github.sha}}"
|
||||
new_link="${{ steps.upload-artifact.outputs.artifact-id }}"
|
||||
new_link="https://github.com/wibus-wee/InjectGUI/actions/runs/${{ github.run_id }}/artifacts/${new_link}"
|
||||
comment_body="${{ steps.fc.outputs.comment-body }}"
|
||||
|
||||
if [ -z "$new_link" ]; then
|
||||
echo "Artifact URL is empty. Skipping update."
|
||||
else
|
||||
shas_and_links=$(echo "$comment_body" | sed -n 's/.*\[\([^!][^]]*\)\].*/\1/p')
|
||||
shas_and_links="${shas_and_links},[${new_sha};${new_link}]"
|
||||
echo "NEW_COMMENT_LIST=${shas_and_links}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Create Pull Request Comment
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
with:
|
||||
edit-mode: replace
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
Hi, thanks for your contribution!
|
||||
|
||||
**InjectGUI** (${{ github.sha }}) has been built and is available for download to test. (Based on PR ${{ github.event.pull_request.number }})
|
||||
|
||||
You can download the latest artifact from here: https://github.com/wibus-wee/InjectGUI/actions/runs/${{ github.run_id }}/artifacts/${{ steps.upload-artifact.outputs.artifact-id }}
|
||||
|
||||
> [!WARNING]
|
||||
> This is a PR build and may not be stable. It's only for testing purposes.
|
||||
|
||||
---
|
||||
|
||||
<!--${{ env.NEW_COMMENT_LIST }}-->
|
||||
|
||||
<sup>Previous builds:</sup>
|
||||
|
||||
${{ env.PREVIOUS_BUILDS }}
|
||||
|
||||
11
.github/workflows/bump-build-number.yml
vendored
11
.github/workflows/bump-build-number.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
git add .
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git commit -m "bump build number to $APP_BUILD"
|
||||
git commit -m "chore: bump build number to $APP_BUILD"
|
||||
- name: Push Changes
|
||||
uses: ad-m/github-push-action@v0.6.0
|
||||
with:
|
||||
@@ -38,4 +38,11 @@ jobs:
|
||||
delete-branch: true
|
||||
base: main
|
||||
title: "chore: bump Build Number to ${{ env.APP_BUILD }}"
|
||||
body: Automatically bump build number of all targets to ${{ env.APP_BUILD }}
|
||||
body: |
|
||||
Automatically bump build number of all targets to ${{ env.APP_BUILD }}
|
||||
|
||||
cc @wibus-wee
|
||||
|
||||
---
|
||||
|
||||
<sup>This pull request was automatically created by a GitHub Action.</sup>
|
||||
@@ -13,7 +13,7 @@
|
||||
381027E02C5F784F00348460 /* Extension+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381027DF2C5F784F00348460 /* Extension+Scene.swift */; };
|
||||
381027E22C5F7C4800348460 /* Extension+Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381027E12C5F7C4800348460 /* Extension+Font.swift */; };
|
||||
381027E42C5F7E1100348460 /* Extension+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381027E32C5F7E1100348460 /* Extension+URL.swift */; };
|
||||
381027E72C5F816500348460 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 381027E92C5F816500348460 /* Localizable.strings */; };
|
||||
383943792C68458F00E5984E /* Inject in Frameworks */ = {isa = PBXBuildFile; productRef = 383943782C68458F00E5984E /* Inject */; };
|
||||
387CEA792C5BD9C400E3A5AC /* AdminPrivilegeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 387CEA782C5BD9C400E3A5AC /* AdminPrivilegeView.swift */; };
|
||||
387CEA7B2C5DEF0600E3A5AC /* Extension+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 387CEA7A2C5DEF0600E3A5AC /* Extension+String.swift */; };
|
||||
38877A1D2C4A6F83009F5910 /* InjectGUIApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38877A1C2C4A6F83009F5910 /* InjectGUIApp.swift */; };
|
||||
@@ -30,12 +30,13 @@
|
||||
38AD95EA2C58E70E0032E79F /* Injector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AD95E92C58E70E0032E79F /* Injector.swift */; };
|
||||
38AD95EE2C58F59C0032E79F /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AD95ED2C58F59C0032E79F /* StatusView.swift */; };
|
||||
38BC1F532C4B587A00C3B60E /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BC1F522C4B587900C3B60E /* SidebarView.swift */; };
|
||||
38BC1F552C4B622500C3B60E /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BC1F542C4B622500C3B60E /* WelcomeView.swift */; };
|
||||
38BC1F5A2C4B98A300C3B60E /* AppDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BC1F592C4B98A300C3B60E /* AppDetailView.swift */; };
|
||||
38BC1F5C2C4BB02200C3B60E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BC1F5B2C4BB02200C3B60E /* SettingsView.swift */; };
|
||||
38E944F22C5A761B00B252A3 /* Executor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E944F12C5A761B00B252A3 /* Executor.swift */; };
|
||||
38E944F52C5A7F6A00B252A3 /* Cache in Frameworks */ = {isa = PBXBuildFile; productRef = 38E944F42C5A7F6A00B252A3 /* Cache */; };
|
||||
38E944F72C5A85E200B252A3 /* Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E944F62C5A85E200B252A3 /* Cache.swift */; };
|
||||
96BC7ED02C65C5E200149818 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 96BC7ECF2C65C5E200149818 /* Localizable.xcstrings */; };
|
||||
96BC7ED62C65E7B100149818 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BC7ED52C65E7B100149818 /* AboutView.swift */; };
|
||||
96BC7ED82C65E8C200149818 /* GeneralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BC7ED72C65E8C200149818 /* GeneralView.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -45,8 +46,6 @@
|
||||
381027DF2C5F784F00348460 /* Extension+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Scene.swift"; sourceTree = "<group>"; };
|
||||
381027E12C5F7C4800348460 /* Extension+Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Font.swift"; sourceTree = "<group>"; };
|
||||
381027E32C5F7E1100348460 /* Extension+URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+URL.swift"; sourceTree = "<group>"; };
|
||||
381027E82C5F816500348460 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
381027EA2C5F817400348460 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
387CEA782C5BD9C400E3A5AC /* AdminPrivilegeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminPrivilegeView.swift; sourceTree = "<group>"; };
|
||||
387CEA7A2C5DEF0600E3A5AC /* Extension+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+String.swift"; sourceTree = "<group>"; };
|
||||
38877A192C4A6F83009F5910 /* InjectGUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InjectGUI.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -65,11 +64,13 @@
|
||||
38AD95E92C58E70E0032E79F /* Injector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Injector.swift; sourceTree = "<group>"; };
|
||||
38AD95ED2C58F59C0032E79F /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
|
||||
38BC1F522C4B587900C3B60E /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
|
||||
38BC1F542C4B622500C3B60E /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
||||
38BC1F592C4B98A300C3B60E /* AppDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetailView.swift; sourceTree = "<group>"; };
|
||||
38BC1F5B2C4BB02200C3B60E /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
38E944F12C5A761B00B252A3 /* Executor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Executor.swift; sourceTree = "<group>"; };
|
||||
38E944F62C5A85E200B252A3 /* Cache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cache.swift; sourceTree = "<group>"; };
|
||||
96BC7ECF2C65C5E200149818 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = InjectGUI/Localizables/Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
96BC7ED52C65E7B100149818 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
96BC7ED72C65E8C200149818 /* GeneralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -77,8 +78,8 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
38E944F52C5A7F6A00B252A3 /* Cache in Frameworks */,
|
||||
380D777B2C61EA56005F3150 /* Sparkle in Frameworks */,
|
||||
383943792C68458F00E5984E /* Inject in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -88,7 +89,7 @@
|
||||
38877A102C4A6F83009F5910 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
381027E92C5F816500348460 /* Localizable.strings */,
|
||||
96BC7ECF2C65C5E200149818 /* Localizable.xcstrings */,
|
||||
38877A1B2C4A6F83009F5910 /* InjectGUI */,
|
||||
38877A1A2C4A6F83009F5910 /* Products */,
|
||||
);
|
||||
@@ -143,12 +144,11 @@
|
||||
38877A2C2C4A6FD7009F5910 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
96BC7EDB2C65ED2800149818 /* Settings */,
|
||||
38877A1E2C4A6F83009F5910 /* ContentView.swift */,
|
||||
38BC1F522C4B587900C3B60E /* SidebarView.swift */,
|
||||
38BC1F542C4B622500C3B60E /* WelcomeView.swift */,
|
||||
38BC1F592C4B98A300C3B60E /* AppDetailView.swift */,
|
||||
38AD95ED2C58F59C0032E79F /* StatusView.swift */,
|
||||
38BC1F5B2C4BB02200C3B60E /* SettingsView.swift */,
|
||||
387CEA782C5BD9C400E3A5AC /* AdminPrivilegeView.swift */,
|
||||
380D777C2C61EAB9005F3150 /* CheckForUpdatesView.swift */,
|
||||
);
|
||||
@@ -185,6 +185,16 @@
|
||||
path = Util;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
96BC7EDB2C65ED2800149818 /* Settings */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
96BC7ED52C65E7B100149818 /* AboutView.swift */,
|
||||
96BC7ED72C65E8C200149818 /* GeneralView.swift */,
|
||||
38BC1F5B2C4BB02200C3B60E /* SettingsView.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -202,8 +212,8 @@
|
||||
);
|
||||
name = InjectGUI;
|
||||
packageProductDependencies = (
|
||||
38E944F42C5A7F6A00B252A3 /* Cache */,
|
||||
380D777A2C61EA56005F3150 /* Sparkle */,
|
||||
383943782C68458F00E5984E /* Inject */,
|
||||
);
|
||||
productName = InjectGUI;
|
||||
productReference = 38877A192C4A6F83009F5910 /* InjectGUI.app */;
|
||||
@@ -235,8 +245,8 @@
|
||||
);
|
||||
mainGroup = 38877A102C4A6F83009F5910;
|
||||
packageReferences = (
|
||||
38E944F32C5A7F6A00B252A3 /* XCRemoteSwiftPackageReference "Cache" */,
|
||||
380D77792C61EA56005F3150 /* XCRemoteSwiftPackageReference "Sparkle" */,
|
||||
383943772C68458F00E5984E /* XCRemoteSwiftPackageReference "Inject" */,
|
||||
);
|
||||
productRefGroup = 38877A1A2C4A6F83009F5910 /* Products */;
|
||||
projectDirPath = "";
|
||||
@@ -253,8 +263,8 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
38877A252C4A6F87009F5910 /* Preview Assets.xcassets in Resources */,
|
||||
96BC7ED02C65C5E200149818 /* Localizable.xcstrings in Resources */,
|
||||
380D77842C620464005F3150 /* config.json in Resources */,
|
||||
381027E72C5F816500348460 /* Localizable.strings in Resources */,
|
||||
38877A212C4A6F87009F5910 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -278,11 +288,12 @@
|
||||
38877A332C4A7222009F5910 /* PublishedStorage.swift in Sources */,
|
||||
381027E02C5F784F00348460 /* Extension+Scene.swift in Sources */,
|
||||
38E944F22C5A761B00B252A3 /* Executor.swift in Sources */,
|
||||
38BC1F552C4B622500C3B60E /* WelcomeView.swift in Sources */,
|
||||
38877A2E2C4A6FFA009F5910 /* InjectConfiguration.swift in Sources */,
|
||||
38BC1F532C4B587A00C3B60E /* SidebarView.swift in Sources */,
|
||||
38877A352C4A7254009F5910 /* ViewKit.swift in Sources */,
|
||||
96BC7ED62C65E7B100149818 /* AboutView.swift in Sources */,
|
||||
38877A3A2C4A730F009F5910 /* Constants.swift in Sources */,
|
||||
96BC7ED82C65E8C200149818 /* GeneralView.swift in Sources */,
|
||||
381027E42C5F7E1100348460 /* Extension+URL.swift in Sources */,
|
||||
38BC1F5C2C4BB02200C3B60E /* SettingsView.swift in Sources */,
|
||||
381027E22C5F7C4800348460 /* Extension+Font.swift in Sources */,
|
||||
@@ -294,19 +305,6 @@
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
381027E92C5F816500348460 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
381027E82C5F816500348460 /* en */,
|
||||
381027EA2C5F817400348460 /* zh-Hans */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
path = InjectGUI/Localizables;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
38877A262C4A6F87009F5910 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
@@ -359,10 +357,12 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -412,9 +412,11 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
@@ -426,7 +428,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = InjectGUI/InjectGUI.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"InjectGUI/Preview Content\"";
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -446,7 +448,12 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.2.0;
|
||||
MARKETING_VERSION = 1.2.3;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"-Xlinker",
|
||||
"-interposable",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "dev.wibus-wee.InjectGUI";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = auto;
|
||||
@@ -465,7 +472,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = InjectGUI/InjectGUI.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"InjectGUI/Preview Content\"";
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -485,7 +492,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.2.0;
|
||||
MARKETING_VERSION = 1.2.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "dev.wibus-wee.InjectGUI";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = auto;
|
||||
@@ -529,12 +536,12 @@
|
||||
minimumVersion = 2.0.0;
|
||||
};
|
||||
};
|
||||
38E944F32C5A7F6A00B252A3 /* XCRemoteSwiftPackageReference "Cache" */ = {
|
||||
383943772C68458F00E5984E /* XCRemoteSwiftPackageReference "Inject" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/hyperoslo/Cache";
|
||||
repositoryURL = "https://github.com/krzysztofzablocki/Inject.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 7.0.0;
|
||||
minimumVersion = 1.5.2;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
@@ -545,10 +552,10 @@
|
||||
package = 380D77792C61EA56005F3150 /* XCRemoteSwiftPackageReference "Sparkle" */;
|
||||
productName = Sparkle;
|
||||
};
|
||||
38E944F42C5A7F6A00B252A3 /* Cache */ = {
|
||||
383943782C68458F00E5984E /* Inject */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 38E944F32C5A7F6A00B252A3 /* XCRemoteSwiftPackageReference "Cache" */;
|
||||
productName = Cache;
|
||||
package = 383943772C68458F00E5984E /* XCRemoteSwiftPackageReference "Inject" */;
|
||||
productName = Inject;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1520"
|
||||
version = "1.8">
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -28,8 +28,6 @@
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
||||
@@ -14,6 +14,10 @@ let softwareManager = SoftwareManager.shared
|
||||
let injector = Injector.shared
|
||||
let executor = Executor.shared
|
||||
|
||||
#if DEBUG
|
||||
let injectIII = Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/macOSInjection.bundle")?.load()
|
||||
#endif
|
||||
|
||||
@main
|
||||
struct InjectGUIApp: App {
|
||||
@AppStorage("showAdminPrivilegeView") private var showAdminPrivilegeView: Bool = true
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
//
|
||||
// Created by wibus on 2024/7/31.
|
||||
//
|
||||
|
||||
import Cache
|
||||
|
||||
enum CacheKey {
|
||||
static let latestCommitHash = "latestCommitHash"
|
||||
}
|
||||
|
||||
// MARK: - Cache Configuration
|
||||
|
||||
// 存一个有关 main/latest commit hash 的缓存
|
||||
let diskConfig = DiskConfig(name: "InjectLib")
|
||||
let memoryConfig = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)
|
||||
let transformer = TransformerFactory.forCodable(ofType: String.self)
|
||||
let storage = try! Storage<String, String>(diskConfig: diskConfig, memoryConfig: memoryConfig, transformer: transformer)
|
||||
//
|
||||
//import Cache
|
||||
//
|
||||
//enum CacheKey {
|
||||
// static let latestCommitHash = "latestCommitHash"
|
||||
//}
|
||||
//
|
||||
//// MARK: - Cache Configuration
|
||||
//
|
||||
//// 存一个有关 main/latest commit hash 的缓存
|
||||
//let diskConfig = DiskConfig(name: "InjectLib")
|
||||
//let memoryConfig = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)
|
||||
//let transformer = TransformerFactory.forCodable(ofType: String.self)
|
||||
//let storage = try! Storage<String, String>(diskConfig: diskConfig, memoryConfig: memoryConfig, transformer: transformer)
|
||||
|
||||
@@ -88,7 +88,7 @@ class Executor: ObservableObject {
|
||||
task.arguments = ["-c", command]
|
||||
// task.arguments = ["-c", "echo 123"]
|
||||
#if DEBUG
|
||||
print("command: \(command)")
|
||||
print("[*] command: \(command)")
|
||||
#endif
|
||||
|
||||
let pipe = Pipe()
|
||||
@@ -134,8 +134,12 @@ class Executor: ObservableObject {
|
||||
task.standardOutput = outputPipe
|
||||
task.standardError = errorPipe
|
||||
|
||||
let escapedCommand = command.replacingOccurrences(of: "\"", with: "\\\"")
|
||||
task.arguments = ["-c", "echo \(self.password) | sudo -S bash -c \"\(escapedCommand)\""]
|
||||
let escapedCommand = command
|
||||
.replacingOccurrences(of: "\"", with: "\\\"")
|
||||
#if DEBUG
|
||||
print("[*] Escaped command: \(escapedCommand)")
|
||||
#endif
|
||||
task.arguments = ["-c", "echo \(self.password) | base64 --decode | sudo -S bash -c \"\(escapedCommand)\""]
|
||||
task.executableURL = URL(fileURLWithPath: "/bin/bash")
|
||||
|
||||
do {
|
||||
|
||||
@@ -329,8 +329,43 @@ class InjectConfiguration: ObservableObject {
|
||||
guard let conf = remoteConf else {
|
||||
return false
|
||||
}
|
||||
let package = conf.appList.first { $0.packageName.allStrings.contains(package) }
|
||||
guard package != nil else {
|
||||
let packageDetail = conf.appList.first { $0.packageName.allStrings.contains(package) }
|
||||
guard packageDetail != nil else {
|
||||
// 这种情况适用于软件存在,但是 Bundle ID 改变了,而导致无法注入
|
||||
// 这部分纯纯的为了兼容好目前的 config.json
|
||||
// FIXME: 未来会移除,因为这种情况不应该存在. 应该在 packageName 中使用数组来支持多个 Bundle ID,而不是单个字符串
|
||||
let localPackageInfo = softwareManager.appListCache[package]
|
||||
if localPackageInfo != nil {
|
||||
let app = conf.appList.first { $0.appBaseLocate == localPackageInfo?.path.replacingOccurrences(of: "/Contents", with: "") }
|
||||
if app != nil {
|
||||
print("[*] Found \(package) in local cache, try to add to supported list...")
|
||||
let newApp = AppList(
|
||||
packageName: .string(package),
|
||||
appBaseLocate: app?.appBaseLocate,
|
||||
bridgeFile: app?.bridgeFile,
|
||||
injectFile: app?.injectFile,
|
||||
needCopyToAppDir: app?.needCopyToAppDir,
|
||||
noSignTarget: app?.noSignTarget,
|
||||
autoHandleHelper: app?.autoHandleHelper,
|
||||
helperFile: app?.helperFile,
|
||||
tccutil: app?.tccutil,
|
||||
forQiuChenly: app?.forQiuChenly,
|
||||
onlysh: app?.onlysh,
|
||||
extraShell: app?.extraShell,
|
||||
smExtra: app?.smExtra,
|
||||
componentApp: app?.componentApp,
|
||||
deepSignApp: app?.deepSignApp,
|
||||
noDeep: app?.noDeep,
|
||||
entitlements: app?.entitlements,
|
||||
useOptool: app?.useOptool,
|
||||
autoHandleSetapp: app?.autoHandleSetapp,
|
||||
keygen: app?.keygen
|
||||
)
|
||||
remoteConf!.appList.append(newApp)
|
||||
print("[*] Added \(package) to supported list.")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -354,7 +389,7 @@ struct InjectConfigurationModel: Codable, Equatable {
|
||||
let project, author: String
|
||||
let version: Double
|
||||
let basePublicConfig: BasePublicConfig
|
||||
let appList: [AppList]
|
||||
var appList: [AppList]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case project
|
||||
|
||||
@@ -33,25 +33,25 @@ extension InjectStage {
|
||||
var description: String {
|
||||
switch self {
|
||||
case .start:
|
||||
return "Start Injecting"
|
||||
return String(localized: "Start Injecting")
|
||||
case .copyExecutableFileAsBackup:
|
||||
return "Copying Executable File as Backup"
|
||||
return String(localized: "Copying Executable File as Backup")
|
||||
case .checkPermissionAndRun:
|
||||
return "Checking Permission and Run"
|
||||
return String(localized: "Checking Permission and Run")
|
||||
case .handleKeygen:
|
||||
return "Handling Keygen"
|
||||
return String(localized: "Handling Keygen")
|
||||
case .handleDeepCodeSign:
|
||||
return "Handling Deep Code Sign"
|
||||
return String(localized: "Handling Deep Code Sign")
|
||||
case .handleAutoHandleHelper:
|
||||
return "Handling Auto Handle Helper"
|
||||
return String(localized: "Handling Auto Handle Helper")
|
||||
case .handleTccutil:
|
||||
return "Handling Tccutil"
|
||||
return String(localized: "Handling Tccutil")
|
||||
case .handleExtraShell:
|
||||
return "Handling Extra Shell"
|
||||
return String(localized: "Handling Extra Shell")
|
||||
case .handleInjectLibInject:
|
||||
return "Handling Inject Lib Inject"
|
||||
return String(localized: "Handling InjectLib Inject")
|
||||
case .end:
|
||||
return "Injecting Finished"
|
||||
return String(localized: "Injecting Finished")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,19 +94,20 @@ class Injector: ObservableObject {
|
||||
|
||||
func startInjectApp(package: String) {
|
||||
// MARK: - 拦截 Setapp 旗下软件
|
||||
|
||||
if package.contains("com.setapp") {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Please read the Setapp inject document first"
|
||||
alert.informativeText = "It's important to read the Setapp inject document first before using the tool. Please"
|
||||
alert.messageText = String(localized: "Please read the Setapp inject document first")
|
||||
alert.informativeText = String(localized: "It's important to read the Setapp inject document first before using the tool. Please")
|
||||
alert.alertStyle = .informational
|
||||
alert.addButton(withTitle: "I have read the document")
|
||||
alert.addButton(withTitle: "Read the document")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: String(localized: "I have read the document"))
|
||||
alert.addButton(withTitle: String(localized: "Read the document"))
|
||||
alert.addButton(withTitle: String(localized: "Cancel"))
|
||||
let response = alert.runModal()
|
||||
if response == .alertFirstButtonReturn {
|
||||
// Continue
|
||||
} else if response == .alertSecondButtonReturn {
|
||||
let url = URL(string: "https://qiuchenlyopensource.github.io/Documentaions/setapp%E6%BF%80%E6%B4%BB%E5%BF%85%E8%AF%BB.html")!
|
||||
let url = URL(string: "https://qiuchenlyopensource.github.io/Documentaions/setapp.html")!
|
||||
NSWorkspace.shared.open(url)
|
||||
return
|
||||
} else {
|
||||
@@ -118,10 +119,10 @@ class Injector: ObservableObject {
|
||||
}
|
||||
if injectConfiguration.allToolsExist() == false {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Inject Tools Not Found"
|
||||
alert.informativeText = "Inject tools not found, it may be caused by network issues or the tools are not available. Please try again later."
|
||||
alert.messageText = String(localized: "Inject Tools Not Found")
|
||||
alert.informativeText = String(localized: "Inject tools not found, it may be caused by network issues or the tools are not available. Please try again later.")
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.addButton(withTitle: String(localized: "OK"))
|
||||
alert.runModal()
|
||||
return
|
||||
}
|
||||
@@ -131,6 +132,8 @@ class Injector: ObservableObject {
|
||||
guard let injectDetail = injectConfiguration.injectDetail(package: package) else {
|
||||
return
|
||||
}
|
||||
print("----------------------------")
|
||||
print("[*] Start inject \(package)")
|
||||
self.injectDetail = injectDetail
|
||||
self.appDetail = appDetail
|
||||
self.shouldShowStatusSheet = true
|
||||
@@ -161,8 +164,8 @@ class Injector: ObservableObject {
|
||||
.sink(receiveCompletion: { completion in
|
||||
if case .failure(let error) = completion {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Command Execution Error"
|
||||
|
||||
alert.messageText = String(localized: "Command Execution Error")
|
||||
|
||||
// Extracting the AppleScript error message
|
||||
var errorMessage = error.localizedDescription
|
||||
if let appleScriptError = error as NSError? {
|
||||
@@ -171,11 +174,37 @@ class Injector: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
alert.informativeText = "\(errorMessage) \n\nPlease check your application integrity and try again.\n\n(Stage: \(stage.description))"
|
||||
alert.informativeText = String(localized: "\(errorMessage) \n\nPlease check your application integrity and try again.\n\n(Stage: \(stage.description))")
|
||||
alert.alertStyle = .critical
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.runModal()
|
||||
// 加一个 Open an issue 按钮,点击后打开 GitHub Issues
|
||||
alert.addButton(withTitle: String(localized: "OK"))
|
||||
alert.addButton(withTitle: String(localized: "Report an issue"))
|
||||
let response = alert.runModal()
|
||||
if response == .alertSecondButtonReturn {
|
||||
// 构建一个 issue 链接
|
||||
let title = "[Bug] Error when injecting \(self.appDetail?.name ?? "")"
|
||||
let body = """
|
||||
### Error Message
|
||||
|
||||
```
|
||||
\(errorMessage)
|
||||
```
|
||||
|
||||
### Info
|
||||
|
||||
- Name: \(self.appDetail?.name ?? "")
|
||||
- Identifier: \(self.appDetail?.identifier ?? "")
|
||||
- \(self.appDetail?.name ?? "") Version: \(self.appDetail?.version ?? "")
|
||||
- Stage: \(stage.description)
|
||||
- InjectGUI version: \(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "")
|
||||
|
||||
"""
|
||||
let url = URL(string: "\(Constants.projectUrl)/issues/new?assignees=&labels=bug&title=\(title.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")&body=\(body.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")")!
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
self.updateInjectStage(stage: stage, message: "Error: \(errorMessage)", progress: 1, status: .error, error: InjectRunningError(error: errorMessage, stage: stage))
|
||||
self.isRunning = false
|
||||
self.emergencyStop = true
|
||||
} else {
|
||||
self.updateInjectStage(stage: stage, message: stage.description, progress: 1, status: .finished)
|
||||
self.executeNextStage(stages: stages, index: index + 1)
|
||||
@@ -249,45 +278,57 @@ class Injector: ObservableObject {
|
||||
case appleScript
|
||||
case bash
|
||||
}
|
||||
|
||||
|
||||
private func getBridgeDir(executable: Bool? = nil) -> String {
|
||||
if (executable ?? false) || self.injectDetail?.autoHandleSetapp == true {
|
||||
return "/MacOS/"
|
||||
self.getBridgeDir(executable: executable, injectDetail: self.injectDetail)
|
||||
}
|
||||
|
||||
private func getBridgeDir(executable: Bool? = nil, injectDetail: AppList?) -> String {
|
||||
if (executable ?? false) || injectDetail?.autoHandleSetapp == true {
|
||||
return "/MacOS/"
|
||||
} else {
|
||||
return self.injectDetail?.bridgeFile?.replacingOccurrences(of: "/Contents", with: "") ?? "/Frameworks/"
|
||||
return injectDetail?.bridgeFile?.replacingOccurrences(of: "/Contents", with: "") ?? "/Frameworks/"
|
||||
}
|
||||
}
|
||||
|
||||
private func genSourcePath(for type: GenScriptType) -> String {
|
||||
func genSourcePath(for type: GenScriptType) -> String {
|
||||
let bridgeDir = self.getBridgeDir()
|
||||
let source = (self.appDetail?.path ?? "") + bridgeDir + (self.injectDetail?.injectFile ?? self.appDetail?.executable ?? "")
|
||||
return self.transformPath(path: source, to: type)
|
||||
}
|
||||
|
||||
private func genSourcePath(for type: GenScriptType, executable: Bool? = nil) -> String {
|
||||
|
||||
private func genSourcePath(for type: GenScriptType, executable: Bool) -> String {
|
||||
let bridgeDir = self.getBridgeDir(executable: executable)
|
||||
let source = (self.appDetail?.path ?? "") + bridgeDir + (executable ?? true ? (self.appDetail?.executable ?? "") : (self.injectDetail?.injectFile ?? ""))
|
||||
let source = (self.appDetail?.path ?? "") + bridgeDir + (executable ? (self.appDetail?.executable ?? "") : (self.injectDetail?.injectFile ?? ""))
|
||||
return self.transformPath(path: source, to: type)
|
||||
}
|
||||
|
||||
private func genSourcePath(for type: GenScriptType, file: String? = nil) -> String {
|
||||
let bridgeDir = self.getBridgeDir()
|
||||
let source = (self.appDetail?.path ?? "") + bridgeDir + (file ?? (self.injectDetail?.injectFile ?? self.appDetail?.executable ?? ""))
|
||||
func genSourcePath(for type: GenScriptType, file: String?) -> String {
|
||||
self.genSourcePath(for: type, appList: self.injectDetail, file: file)
|
||||
}
|
||||
|
||||
func genSourcePath(for type: GenScriptType, appList: AppList?, file: String?) -> String {
|
||||
let bridgeDir = self.getBridgeDir(injectDetail: appList)
|
||||
let source = (self.appDetail?.path ?? "") + bridgeDir + (file ?? "")
|
||||
return self.transformPath(path: source, to: type)
|
||||
}
|
||||
|
||||
private func genSourcePath(for type: GenScriptType, path: String? = nil) -> String {
|
||||
let bridgeDir = self.getBridgeDir()
|
||||
let source = path ?? ((self.appDetail?.path ?? "") + bridgeDir + (self.injectDetail?.injectFile ?? self.appDetail?.executable ?? ""))
|
||||
func genSourcePath(for type: GenScriptType, appList: AppList) -> String {
|
||||
let bridgeDir = self.getBridgeDir(injectDetail: appList)
|
||||
let source = (self.appDetail?.path ?? "") + bridgeDir + (self.injectDetail?.injectFile ?? self.appDetail?.executable ?? "")
|
||||
return self.transformPath(path: source, to: type)
|
||||
}
|
||||
|
||||
private func genSourcePath(for type: GenScriptType, path: String?) -> String {
|
||||
return self.transformPath(path: path ?? "", to: type)
|
||||
}
|
||||
|
||||
private func transformPath(path: String, to type: GenScriptType) -> String {
|
||||
switch type {
|
||||
case .none:
|
||||
return path.replacingOccurrences(of: "%20", with: " ")
|
||||
case .appleScript:
|
||||
return path.replacingOccurrences(of: "%20", with: " ").replacingOccurrences(of: " ", with: "\\\\ ")
|
||||
return path.replacingOccurrences(of: "%20", with: " ").replacingOccurrences(of: " ", with: "\\\\ ")
|
||||
case .bash:
|
||||
return path.replacingOccurrences(of: "%20", with: " ").replacingOccurrences(of: " ", with: "\\ ")
|
||||
}
|
||||
@@ -300,15 +341,15 @@ class Injector: ObservableObject {
|
||||
let destination = source.appending(".backup")
|
||||
|
||||
if !FileManager.default.fileExists(atPath: source) {
|
||||
print("Source file not found: \(source)")
|
||||
return [("echo Source file not found: \(source.transformTo(to: .appleScript)) && exit 1", true)] // 借用一下 AppleScript 来弹窗
|
||||
print("[*] Source file not found: \(source)")
|
||||
return [("echo Source file not found: \(source.transformTo(to: .bash)) && exit 1", true)] // 借用一下 AppleScript 来弹窗
|
||||
}
|
||||
if FileManager.default.fileExists(atPath: destination) {
|
||||
print("Destination file already exists: \(destination)")
|
||||
print("[*] Destination file already exists: \(destination)")
|
||||
return []
|
||||
}
|
||||
return [
|
||||
("sudo cp \(source.transformTo(to: .appleScript)) \(destination.transformTo(to: .appleScript))", true)
|
||||
("sudo cp \(source.transformTo(to: .bash)) \(destination.transformTo(to: .bash))", true)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -316,14 +357,14 @@ class Injector: ObservableObject {
|
||||
|
||||
func checkPermissionAndRunCommands() -> [(command: String, isAdmin: Bool)] {
|
||||
var shells: [(command: String, isAdmin: Bool)] = []
|
||||
let source = self.genSourcePath(for: .appleScript)
|
||||
let source = self.genSourcePath(for: .bash)
|
||||
shells.append(("sudo xattr -cr \(source)", true))
|
||||
shells.append(("sudo chmod -R 777 \(source)", true))
|
||||
|
||||
// 检查是否运行中, 如果运行中则杀掉进程
|
||||
let isRunning = NSRunningApplication.runningApplications(withBundleIdentifier: self.appDetail?.identifier ?? "").count > 0
|
||||
if isRunning {
|
||||
shells.append(("sudo pkill -f \(self.genSourcePath(for: .appleScript, executable: true))", true))
|
||||
shells.append(("sudo pkill -f \(self.genSourcePath(for: .bash, executable: true))", true))
|
||||
}
|
||||
return shells
|
||||
}
|
||||
@@ -352,14 +393,17 @@ class Injector: ObservableObject {
|
||||
|
||||
if insert_dylib_URL == nil || QiuchenlyDylib_URL == nil {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Inject Tools Path Not Found"
|
||||
alert.informativeText = "This should not happen here, please report to the developer (Area: MainInject)"
|
||||
alert.messageText = String(localized: "Inject Tools Path Not Found")
|
||||
alert.informativeText = String(localized: "This should not happen here, please report to the developer (Area: MainInject)")
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.addButton(withTitle: String(localized: "OK"))
|
||||
alert.runModal()
|
||||
print("[*] Inject Tools Path Not Found.")
|
||||
return [("echo Inject Tools Path Not Found && exit 1", true)]
|
||||
}
|
||||
|
||||
if self.injectDetail?.needCopyToAppDir == true {
|
||||
print("[*] Copying 91Qiuchenly.dylib to app dir")
|
||||
// let copyedQiuchenly_URL = (self.appDetail?.path ?? "") + bridgeDir + "91Qiuchenly.dylib"
|
||||
let copyedQiuchenly_URL = self.genSourcePath(for: .none, file: "91Qiuchenly.dylib")
|
||||
let softLink = ("sudo ln -f -s '\(QiuchenlyDylib_URL!)' '\(copyedQiuchenly_URL)'", true) // 为了防止原神更新后导致的插件失效,这里使用软链接
|
||||
@@ -371,12 +415,12 @@ class Injector: ObservableObject {
|
||||
let componentApp = componentAppList.map { appBaseLocate + $0 }
|
||||
let componentAppExecutable = componentApp.map { $0 + "/Contents/MacOS/" + (self.readExecutableFile(app: URL(fileURLWithPath: $0)) ?? "") }
|
||||
let desireAppList = desireApp + componentAppExecutable
|
||||
let insert_dylib_commands = desireAppList.map { "sudo \(self.genSourcePath(for: .appleScript, path: insert_dylib_URL!)) '\(copyedQiuchenly_URL)' '\(destination.transformTo(to: .none))' '\($0)'" }
|
||||
let insert_dylib_commands = desireAppList.map { "sudo \(self.genSourcePath(for: .bash, path: insert_dylib_URL!)) '\(copyedQiuchenly_URL)' '\(destination.transformTo(to: .none))' '\($0)'" }
|
||||
|
||||
return [softLink] + insert_dylib_commands.map { ($0, true) }
|
||||
}
|
||||
|
||||
return [("sudo \(self.genSourcePath(for: .appleScript, path: insert_dylib_URL!)) '\(QiuchenlyDylib_URL!)' '\(source.transformTo(to: .none))' '\(destination.transformTo(to: .none))'", true)]
|
||||
return [("sudo \(self.genSourcePath(for: .bash, path: insert_dylib_URL!)) '\(QiuchenlyDylib_URL!)' '\(source.transformTo(to: .none))' '\(destination.transformTo(to: .none))'", true)]
|
||||
}
|
||||
|
||||
// MARK: - 注入原神之 DeepCodeSign
|
||||
@@ -392,23 +436,42 @@ class Injector: ObservableObject {
|
||||
}
|
||||
|
||||
let entitlements = self.injectDetail?.entitlements
|
||||
if let entitlements = entitlements {
|
||||
if let entitlements {
|
||||
let entitlementDownloadURL = injectConfiguration.generateInjectToolDownloadURL(name: entitlements)
|
||||
let downloadIntoTmpPath = try? FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: URL(fileURLWithPath: "/"), create: true)
|
||||
let entitlementsPath = downloadIntoTmpPath?.appendingPathComponent(entitlements).path
|
||||
let downloadCommand = "curl -L -o \(entitlementsPath!) \(entitlementDownloadURL!)"
|
||||
shells.append((downloadCommand, false))
|
||||
// let downloadCommand = "curl -L -o \(entitlementsPath!) \(entitlementDownloadURL!)"
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
|
||||
let task = URLSession.shared.downloadTask(with: entitlementDownloadURL!) { (location, response, error) in
|
||||
if let location = location {
|
||||
try? FileManager.default.moveItem(at: location, to: URL(fileURLWithPath: entitlementsPath!))
|
||||
print("[*] Download Entitlements Success: \(entitlementDownloadURL!)")
|
||||
} else {
|
||||
print("[*] Download Entitlements Failed: \(entitlementDownloadURL!)")
|
||||
shells.append(("echo Download Entitlements Failed: \(entitlementDownloadURL!) && exit 1", false))
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
task.resume()
|
||||
semaphore.wait()
|
||||
|
||||
sign_prefix_with_deep += " --entitlements \(entitlementsPath!)"
|
||||
}
|
||||
|
||||
let dest = self.genSourcePath(for: .bash)
|
||||
let dest = self.genSourcePath(for: .none)
|
||||
|
||||
shells.append((sign_prefix_with_deep + " '\(dest)'", false))
|
||||
if !(injectDetail?.noSignTarget ?? false) {
|
||||
shells.append((sign_prefix_with_deep + " '\(dest)'", true))
|
||||
}
|
||||
// shells.append((sign_prefix_with_deep + " '\(dest)'", false))
|
||||
|
||||
let deepSignApp = self.injectDetail?.deepSignApp // Bool
|
||||
if deepSignApp == true {
|
||||
let deepSignAppPath = self.genSourcePath(for: .bash, path: (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: ""))
|
||||
shells.append((sign_prefix_with_deep + " '\(deepSignAppPath)'", false))
|
||||
let deepSignAppPath = self.genSourcePath(for: .none, path: (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: ""))
|
||||
shells.append((sign_prefix_with_deep + " '\(deepSignAppPath)'", true))
|
||||
}
|
||||
|
||||
// let disableLibraryValidate = self.injectDetail?.dis
|
||||
@@ -439,42 +502,60 @@ class Injector: ObservableObject {
|
||||
try? FileManager.default.createDirectory(at: extraShellDir, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
let downloadPath = downloadIntoTmpPath.appendingPathComponent(extraShell).path
|
||||
let downloadCommand = "curl -L -o \(downloadPath) \(getToolDownloadURL)"
|
||||
// let downloadCommand = "curl -L -o \(downloadPath) \(getToolDownloadURL)"
|
||||
// 创建信号量,等待下载完成
|
||||
|
||||
let dest = self.genSourcePath(for: .appleScript)
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
|
||||
let task = URLSession.shared.downloadTask(with: getToolDownloadURL) { (location, response, error) in
|
||||
if let location = location {
|
||||
try? FileManager.default.moveItem(at: location, to: URL(fileURLWithPath: downloadPath))
|
||||
print("[*] Download Extra Shell Success: \(getToolDownloadURL)")
|
||||
} else {
|
||||
print("[*] Download Extra Shell Failed: \(getToolDownloadURL)")
|
||||
shells.append(("echo Download Extra Shell Failed: \(getToolDownloadURL) && exit 1", false))
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
task.resume()
|
||||
semaphore.wait()
|
||||
|
||||
let dest = self.genSourcePath(for: .bash)
|
||||
|
||||
// MARK: - Sub: 对某些 shell 脚本进行内容替换
|
||||
|
||||
var replaceSpecialShell: [(String, String)] = [] // (from, to)
|
||||
|
||||
// tool/optool
|
||||
let optoolPath = self.genSourcePath(for: .appleScript, path: injectConfiguration.getInjecToolPath(name: "optool")?.pathWithFallback())
|
||||
let optoolPath = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "optool")?.pathWithFallback())
|
||||
replaceSpecialShell.append(("tool/optool", optoolPath))
|
||||
replaceSpecialShell.append(("./tool/optool", optoolPath))
|
||||
|
||||
// tool/insert_dylib
|
||||
let insert_dylibPath = self.genSourcePath(for: .appleScript, path: injectConfiguration.getInjecToolPath(name: "insert_dylib")?.pathWithFallback())
|
||||
let insert_dylibPath = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "insert_dylib")?.pathWithFallback())
|
||||
replaceSpecialShell.append(("tool/insert_dylib", insert_dylibPath))
|
||||
replaceSpecialShell.append(("./tool/insert_dylib", insert_dylibPath))
|
||||
|
||||
// tool/91QiuChenly.dylib
|
||||
let dylibPath = self.genSourcePath(for: .appleScript, path: injectConfiguration.getInjecToolPath(name: "91Qiuchenly.dylib")?.pathWithFallback())
|
||||
let dylibPath = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "91Qiuchenly.dylib")?.pathWithFallback())
|
||||
replaceSpecialShell.append(("tool/91QiuChenly.dylib", dylibPath))
|
||||
replaceSpecialShell.append(("./tool/91QiuChenly.dylib", dylibPath))
|
||||
|
||||
// tool/GenShineImpactStarter
|
||||
let genShineImpactStarterPath = self.genSourcePath(for: .appleScript, path: injectConfiguration.getInjecToolPath(name: "GenShineImpactStarter")?.pathWithFallback())
|
||||
let genShineImpactStarterPath = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "GenShineImpactStarter")?.pathWithFallback())
|
||||
replaceSpecialShell.append(("tool/GenShineImpactStarter", genShineImpactStarterPath))
|
||||
replaceSpecialShell.append(("./tool/GenShineImpactStarter", genShineImpactStarterPath))
|
||||
|
||||
// 把 [0] 替换为 [1] 的内容
|
||||
let replaceCommands = replaceSpecialShell.map { from, to in
|
||||
"sed -i '' 's|\(from)|\(to)|g' \(downloadPath)"
|
||||
"sed -i '' 's|\(from)|\"\(to)\"|g' \(downloadPath)"
|
||||
}
|
||||
|
||||
shells.append((downloadCommand, false))
|
||||
shells.append(("chmod +x \(downloadPath)", false))
|
||||
if replaceCommands.count > 0 {
|
||||
// shells.append((downloadCommand, false))
|
||||
shells.append(("sudo chmod -R 777 \(downloadPath)", true))
|
||||
shells.append(("chmod +x \(downloadPath)", true))
|
||||
if !replaceCommands.isEmpty {
|
||||
shells.append(contentsOf: replaceCommands.map { ($0, false) })
|
||||
}
|
||||
shells.append(("sudo sh \(downloadPath)", true))
|
||||
@@ -488,7 +569,7 @@ class Injector: ObservableObject {
|
||||
var shells: [(command: String, isAdmin: Bool)] = []
|
||||
let helperFile = self.injectDetail?.helperFile?.allStrings // [String]?
|
||||
let autoHandleHelper = self.injectDetail?.autoHandleHelper // Bool?
|
||||
if let helperFile = helperFile, let autoHandleHelper = autoHandleHelper {
|
||||
if let helperFile, let autoHandleHelper {
|
||||
var helpers: [String] = []
|
||||
if autoHandleHelper {
|
||||
helpers = helperFile
|
||||
@@ -496,10 +577,10 @@ class Injector: ObservableObject {
|
||||
for helper in helpers {
|
||||
let genShineImpactStarterURL = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "GenShineImpactStarter")?.pathWithFallback())
|
||||
var targetHelper = (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: "") + helper
|
||||
let bridgeFile = (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: "") + (self.injectDetail?.bridgeFile ?? "")
|
||||
let bridgeFile = (self.appDetail?.path ?? "") + (self.getBridgeDir())
|
||||
let insertDylibURL = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "insert_dylib")?.pathWithFallback())
|
||||
let helperName = targetHelper.split(separator: "/").last
|
||||
let target = self.genSourcePath(for: .appleScript, path: "/Library/LaunchDaemons/\(helperName!).plist")
|
||||
let target = self.genSourcePath(for: .bash, path: "/Library/LaunchDaemons/\(helperName!).plist")
|
||||
|
||||
var srcInfo = [(self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: "") + "/Contents/Info.plist"]
|
||||
if let componentApps = self.injectDetail?.componentApp {
|
||||
@@ -519,7 +600,7 @@ class Injector: ObservableObject {
|
||||
let rmCommand = ("sudo /bin/rm \(target)", true)
|
||||
let rmPrivilegedHelper = "sudo /bin/rm /Library/PrivilegedHelperTools/\(helperName!)"
|
||||
let xattrCommand = "sudo xattr -c '\((self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: ""))'"
|
||||
|
||||
|
||||
let codeSignHelperCommand = "/usr/bin/codesign -f -s - --all-architectures --deep '\(targetHelper)'"
|
||||
let codeSignAppCommand = "/usr/bin/codesign -f -s - --all-architectures --deep '\((self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: ""))'"
|
||||
|
||||
@@ -551,7 +632,7 @@ class Injector: ObservableObject {
|
||||
|
||||
func handleTccutilCommands() -> [(command: String, isAdmin: Bool)] {
|
||||
let tccutil = self.injectDetail?.tccutil?.allStrings // [String]?
|
||||
if let tccutil = tccutil {
|
||||
if let tccutil {
|
||||
var ids = [self.appDetail?.identifier]
|
||||
if let componentApp = self.injectDetail?.componentApp {
|
||||
ids.append(contentsOf: componentApp.map { self.readBundleID(app: URL(fileURLWithPath: (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: "") + $0)) })
|
||||
|
||||
@@ -15,29 +15,32 @@ struct AppDetail {
|
||||
let path: String // -> path
|
||||
let executable: String // -> CFBundleExecutable
|
||||
let icon: NSImage
|
||||
var isInjected: Bool = false
|
||||
}
|
||||
|
||||
class SoftwareManager: ObservableObject {
|
||||
static let shared = SoftwareManager()
|
||||
|
||||
@Published var appListCache: [String: AppDetail] = [:]
|
||||
@Published var isLoading = false
|
||||
@Published var isLoading = false
|
||||
|
||||
private init() {
|
||||
refreshAppList()
|
||||
}
|
||||
|
||||
private init() {
|
||||
refreshAppList()
|
||||
}
|
||||
func refreshAppList() {
|
||||
DispatchQueue.main.async {
|
||||
self.isLoading = true
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
||||
self?.getList()
|
||||
DispatchQueue.main.async {
|
||||
self?.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.isLoading = true
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
||||
self?.getList()
|
||||
DispatchQueue.main.async {
|
||||
self?.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadAppInfo(
|
||||
from plistPath: String
|
||||
) -> AppDetail? {
|
||||
@@ -64,16 +67,7 @@ class SoftwareManager: ObservableObject {
|
||||
let iconFileRaw = plist["CFBundleIconFile"] as? String ?? plist["CFBundleIconName"] as? String
|
||||
|
||||
// 检查文件名并添加扩展名(如果需要)
|
||||
let iconFile: String?
|
||||
if let iconFileRaw = iconFileRaw {
|
||||
iconFile = iconFileRaw.hasSuffix(
|
||||
".icns"
|
||||
) ? iconFileRaw : iconFileRaw.appending(
|
||||
".icns"
|
||||
)
|
||||
} else {
|
||||
iconFile = nil
|
||||
}
|
||||
let iconFile: String? = iconFileRaw?.hasSuffix(".icns") ?? false ? iconFileRaw : iconFileRaw?.appending(".icns")
|
||||
|
||||
// 检查 iconFile 是否为 nil
|
||||
guard let finalIconFile = iconFile else {
|
||||
@@ -102,38 +96,51 @@ class SoftwareManager: ObservableObject {
|
||||
version: bundleVersion,
|
||||
path: path,
|
||||
executable: bundleExecutable,
|
||||
icon: icon ?? NSImage()
|
||||
icon: icon ?? NSImage(),
|
||||
isInjected: checkForInjection(appPath: path, package: bundleIdentifier)
|
||||
)
|
||||
}
|
||||
|
||||
func getList() {
|
||||
print("[*] Getting app list...")
|
||||
let applicationDirectories = [
|
||||
"/Applications",
|
||||
"/Applications/Setapp",
|
||||
]
|
||||
let fileManager = FileManager.default
|
||||
print("[*] Getting app list...")
|
||||
let applicationDirectories = [
|
||||
"/Applications",
|
||||
"/Applications/Setapp",
|
||||
]
|
||||
let fileManager = FileManager.default
|
||||
|
||||
var newAppListCache: [String: AppDetail] = [:]
|
||||
var newAppListCache: [String: AppDetail] = [:]
|
||||
|
||||
for directory in applicationDirectories {
|
||||
guard let appPaths = try? fileManager.contentsOfDirectory(atPath: directory) else {
|
||||
continue
|
||||
}
|
||||
for directory in applicationDirectories {
|
||||
guard let appPaths = try? fileManager.contentsOfDirectory(atPath: directory) else {
|
||||
continue
|
||||
}
|
||||
|
||||
for appPath in appPaths {
|
||||
let fullPath = "\(directory)/\(appPath)"
|
||||
let infoPlistPath = "\(fullPath)/Contents/Info.plist"
|
||||
if let appInfo = loadAppInfo(from: infoPlistPath) {
|
||||
newAppListCache[appInfo.identifier] = appInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
for appPath in appPaths {
|
||||
let fullPath = "\(directory)/\(appPath)"
|
||||
let infoPlistPath = "\(fullPath)/Contents/Info.plist"
|
||||
// MARK: - 针对 Adobe 系列软件的特殊处理
|
||||
if appPath.hasPrefix("Adobe") {
|
||||
let adobeAppPaths = try? fileManager.contentsOfDirectory(atPath: fullPath)
|
||||
for adobeAppPath in adobeAppPaths ?? [] { // Adobe 系列软件的子目录
|
||||
let adobeFullPath = "\(fullPath)/\(adobeAppPath)"
|
||||
let adobeInfoPlistPath = "\(adobeFullPath)/Contents/Info.plist"
|
||||
if let appInfo = loadAppInfo(from: adobeInfoPlistPath) {
|
||||
newAppListCache[appInfo.identifier] = appInfo
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let appInfo = loadAppInfo(from: infoPlistPath) {
|
||||
newAppListCache[appInfo.identifier] = appInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.appListCache = newAppListCache
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.appListCache = newAppListCache
|
||||
}
|
||||
}
|
||||
|
||||
func addAnMaybeExistAppToList(appBaseLocate: String) {
|
||||
// print("[*] try to add \(appBaseLocate) to list...")
|
||||
@@ -150,4 +157,19 @@ class SoftwareManager: ObservableObject {
|
||||
print("[*] Checking if \(package) is installed...")
|
||||
return appListCache[package] != nil
|
||||
}
|
||||
|
||||
private func checkForInjection(appPath: String, package: String) -> Bool {
|
||||
let injector = Injector.shared
|
||||
|
||||
guard let appList = InjectConfiguration.shared.injectDetail(package: package) else {
|
||||
return false
|
||||
}
|
||||
let paths = [
|
||||
injector.genSourcePath(for: .none, appList: appList, file: "91Qiuchenly.dylib"),
|
||||
injector.genSourcePath(for: .none, appList: appList).appending("_backup"),
|
||||
injector.genSourcePath(for: .none, appList: appList).appending(".backup"),
|
||||
]
|
||||
|
||||
return paths.contains { FileManager.default.fileExists(atPath: appPath + $0) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,11 @@ extension String {
|
||||
return original.replacingOccurrences(of: " ", with: "\\ ")
|
||||
}
|
||||
}
|
||||
|
||||
func encode() -> String {
|
||||
if let data = self.data(using: .utf8) {
|
||||
return data.base64EncodedString()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ enum ViewKit {
|
||||
|
||||
static func useSheet(
|
||||
title: String,
|
||||
secondaryButton: String = "Cancel",
|
||||
primaryButton: String = "OK",
|
||||
secondaryButton: String = String(localized: "Cancel"),
|
||||
primaryButton: String = String(localized: "OK"),
|
||||
toolbar: (() -> (AnyView))? = nil,
|
||||
content: @escaping () -> AnyView,
|
||||
action: @escaping (IsClickPrimaryButton) -> Void?
|
||||
@@ -97,8 +97,8 @@ enum ViewKit {
|
||||
static func useAlert(
|
||||
title: String,
|
||||
message: String,
|
||||
primaryButton: String = "OK",
|
||||
secondaryButton: String = "Cancel",
|
||||
primaryButton: String = String(localized: "OK"),
|
||||
secondaryButton: String = String(localized: "Cancel"),
|
||||
action: @escaping (IsClickPrimaryButton) -> Void?
|
||||
) {
|
||||
let alert = NSAlert()
|
||||
@@ -127,8 +127,8 @@ enum ViewKit {
|
||||
static func useFilePanel(
|
||||
title: String,
|
||||
message: String,
|
||||
primaryButton: String = "OK",
|
||||
secondaryButton: String = "Cancel",
|
||||
primaryButton: String = String(localized: "OK"),
|
||||
secondaryButton: String = String(localized: "Cancel"),
|
||||
action: @escaping (URL?) -> Void
|
||||
) {
|
||||
let panel = NSOpenPanel()
|
||||
@@ -147,8 +147,8 @@ enum ViewKit {
|
||||
static func useDirectoryPanel(
|
||||
title: String,
|
||||
message: String,
|
||||
primaryButton: String = "OK",
|
||||
secondaryButton: String = "Cancel",
|
||||
primaryButton: String = String(localized: "OK"),
|
||||
secondaryButton: String = String(localized: "Cancel"),
|
||||
action: @escaping (URL?) -> Void
|
||||
) {
|
||||
let panel = NSOpenPanel()
|
||||
@@ -167,8 +167,8 @@ enum ViewKit {
|
||||
static func saveFilePanel(
|
||||
title: String,
|
||||
message: String,
|
||||
primaryButton: String = "OK",
|
||||
secondaryButton: String = "Cancel",
|
||||
primaryButton: String = String(localized: "OK"),
|
||||
secondaryButton: String = String(localized: "Cancel"),
|
||||
action: @escaping (URL?) -> Void
|
||||
) {
|
||||
let panel = NSSavePanel()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://github.com/wibus-wee/InjectGUI/releases/download/latest/appcast.xml</string>
|
||||
<string>https://github.com/wibus-wee/InjectGUI/releases/latest/download/appcast.xml</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
<string>uj8dRzY60RpQhFt88Lm4vwy7a5TEceTqRWmBeZpb4e0=</string>
|
||||
</dict>
|
||||
|
||||
947
InjectGUI/Localizables/Localizable.xcstrings
Normal file
947
InjectGUI/Localizables/Localizable.xcstrings
Normal file
@@ -0,0 +1,947 @@
|
||||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"shouldTranslate" : false
|
||||
},
|
||||
"%@" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "%@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%@ \n\nPlease check your application integrity and try again.\n\n(Stage: %@)" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "%1$@ \n\nPlease check your application integrity and try again.\n\n(Stage: %2$@)"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "%1$@ \n\n请检查您的应用程序完整性并重试。\n\n(阶段: %2$@)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%lld%%" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "%lld%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"About" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "关于"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Are you sure you want to stop injecting?" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "你确定要停止注入吗?"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Branch" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "分支"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"By wibus. Made with ❤️" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "由 wibus 制作. Made with ❤️"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Cancel" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "取消"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Check for Updates…" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "检查更新…"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Checking Permission and Run" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在检查权限并运行"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Click here to close this view." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "点击以关闭此视图"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Command Execution Error" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "命令执行错误"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Commit" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "提交"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Compatible Version" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "兼容的版本"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Copy Bundle ID" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "复制 Bundle ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Copying Executable File as Backup" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在复制可执行文件作为备份"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Enter password again" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "请再次输入密码"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Error: %@" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "错误:%@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Finished. Close" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "完成. 关闭"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"General" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "通用"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Handling Auto Handle Helper" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在处理自动处理助手"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Handling Deep Code Sign" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在处理 Deep Code SIgn"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Handling Extra Shell" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在处理额外的Shell"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Handling InjectLib Inject" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "处理 InjectLib 注入"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Handling Keygen" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在处理 Keygen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Handling Tccutil" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在处理 Tccutil"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"I have read the document" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "我已阅读该文件"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Incorrect password" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "密码错误"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Information About InjectLib" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "关于 InjectLib 的信息"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Inject" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "注入"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Inject Configurations" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "注入配置"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Inject is running" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "注入已经在运行"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Inject Tools Not Found" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "未找到注入工具"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Inject tools not found, it may be caused by network issues or the tools are not available. Please try again later." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "未找到注入工具,可能是网络问题或工具不可用造成的,请稍后重试"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Inject Tools Path Not Found" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "未找到注入工具路径"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Injected" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "已注入"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"InjectGUI" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "InjectGUI"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"InjectGUI requires your password to perform administrative tasks." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "InjectGUI 需要您的密码来执行任务。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Injecting Finished" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "注入完成"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"InjectLib" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "InjectLib"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"It will only show when the injector is running." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "这仅在注入器运行时显示。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"It's a abnormal situation, it shouldn't be running, please report to developer." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "这是异常情况,它不应该运行,请向开发人员报告。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"It's important to read the Setapp inject document first before using the tool. Please" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "在使用该工具之前,首先阅读 Setapp 注入文档非常重要!"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Local Mode" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "本地模式"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"No apps found." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "找不到软件。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"OK" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "好的"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Open in Finder" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "在访达中打开"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Open Support Folder" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "打开应用支持文件夹"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Password" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "密码"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Please contact the developer if you see this view without running the injector." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "如果您在未运行注入器的情况下看到此视图,请联系开发人员。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Please enter your password to continue." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "请输入密码以继续"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Please read the Setapp inject document first" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "请先阅读 Setapp 注入文档"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Please try again." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "请重试"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Progress: %lld%%" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "进度:%lld%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Read the document" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "阅读文件"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Refresh app list" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "刷新软件列表"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Released under GPLv3. Based on QiuChenly/InjectLib." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "根据 GPLv3 发布. 基于 QiuChenly/InjectLib."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Remote InjectLib Branch" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "指定 InjectLib 远程分支"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Remote InjectLib Commit" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "指定 InjectLib 提交 Commit"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Remote InjectLib Git URL" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "指定 InjectLib 远程 Git URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Remote Mode" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "远程模式"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Report an issue" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "提交 issue"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Restarting the app will require your password again." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "重新启动应用程序将需要再次输入您的密码。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Run App" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "启动 App"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Scanning Apps..." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "扫描所有 App"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Search all local app" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "搜索所有本地软件"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Settings" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "设置"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Settings About InjectGUI" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "关于 InjectGUI 的设置"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Start Injecting" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "开始注入"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Status" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "状态"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Stop" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "停止"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Stop Injecting" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "停止注入"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Submit" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "提交"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"The app may not work properly if you stop injecting." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "如果您停止注入,该应用程序可能无法正常工作。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This app can use keygen to generate key" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此应用程序可以使用keygen生成密钥"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This app does not have extraShell" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此软件没有 extraShell"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This app has extraShell" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此软件有extraShell"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This app has sub app" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此软件有附属软件"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This app is supported by InjectLib." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此应用程序由 InjectLib 支持。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This app need auto handle helper" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此应用程序需要自动处理Helper"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This app need deep codesign" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此软件需要 deep codesign"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This app needs to use tccutil to reset" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "此软件需要使用 tccutil 来重置"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This is a status view for the injector." : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "这是注入器的状态视图。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This should not happen here, please report to the developer (Area: MainInject)" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "这里不应该发生这种情况,请向开发人员报告(区域:MainInject)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Toggle Sidebar" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "切换侧边栏"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Universal Version (? Maybe)" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "通用版本(?应该是吧)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Update InjectLib & Tools" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "更新 InjectLib & 相关工具"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"URL" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "链接"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Version: %@" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "版本:%@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Welcome to InjectGUI" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "欢迎使用 InjectGUI"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Why you are seeing this?" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "你怎么看到的?"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"You're in" : {
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "您处于"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
InjectGUI
|
||||
|
||||
Created by wibus on 2024/8/4.
|
||||
|
||||
*/
|
||||
/* No comment provided by engineer. */
|
||||
"" = "";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@" = "%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld%%" = "%lld%%";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"About" = "About";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Branch" = "Branch";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"By wibus. Made with ❤️" = "By wibus. Made with ❤️";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Click here to close this view." = "Click here to close this view.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Commit" = "Commit";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Compatible Version" = "Compatible Version";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Copy Bundle ID" = "Copy Bundle ID";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter password again" = "Enter password again";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error: %@" = "Error: %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Finished. Close" = "Finished. Close";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"General" = "General";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Information About InjectLib" = "Information About InjectLib";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Inject" = "Inject";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Inject Configurations" = "Inject Configurations";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"InjectGUI" = "InjectGUI";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"InjectGUI requires your password to perform administrative tasks." = "InjectGUI requires your password to perform administrative tasks.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"InjectLib" = "InjectLib";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"It will only show when the injector is running." = "It will only show when the injector is running.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No apps found." = "No apps found.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open in Finder" = "Open in Finder";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open Support Folder" = "Open Support Folder";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Password" = "Password";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Please contact the developer if you see this view without running the injector." = "Please contact the developer if you see this view without running the injector.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Please enter your password to continue." = "Please enter your password to continue.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Progress: %lld%%" = "Progress: %lld%%";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Refresh app list" = "Refresh app list";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Released under GPLv3. Based on QiuChenly/InjectLib." = "Released under GPLv3. Based on QiuChenly/InjectLib.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Restarting the app will require your password again." = "Restarting the app will require your password again.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Search all local app" = "Search all local app";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Settings" = "Settings";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Settings About InjectGUI" = "Settings About InjectGUI";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Status" = "Status";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Stop Injecting" = "Stop Injecting";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Submit" = "Submit";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app can use keygen to generate key" = "This app can use keygen to generate key";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app does not have extraShell" = "This app does not have extraShell";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app has extraShell" = "This app has extraShell";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app has sub app" = "This app has sub app";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app is supported by InjectLib." = "This app is supported by InjectLib.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app need auto handle helper" = "This app need auto handle helper";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app need deep codesign" = "This app need deep codesign";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app needs to use tccutil to reset" = "This app needs to use tccutil to reset";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This is a status view for the injector." = "This is a status view for the injector.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Toggle Sidebar" = "Toggle Sidebar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Universal Version (? Maybe)" = "Universal Version (? Maybe)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update InjectLib & Tools" = "Update InjectLib & Tools";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"URL" = "URL";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Version: %@" = "Version: %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Welcome to InjectGUI" = "Welcome to InjectGUI";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Why you are seeing this?" = "Why you are seeing this?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Remote InjectLib Git URL" = "Remote InjectLib Git URL";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Remote InjectLib Branch" = "Remote InjectLib Branch";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Remote InjectLib Commit" = "Remote InjectLib Commit";
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
InjectGUI
|
||||
|
||||
Created by JerryXU on 2024/8/4.
|
||||
|
||||
*/
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"" = "";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@" = "%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld%%" = "%lld%%";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"About" = "关于";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Branch" = "分支";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"By wibus. Made with ❤️" = "由 wibus 制作. Made with ❤️";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Click here to close this view." = "点击以关闭此视图";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Commit" = "提交";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Compatible Version" = "兼容的版本";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Copy Bundle ID" = "复制 Bundle ID";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter password again" = "请再次输入密码";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error: %@" = "错误:%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Finished. Close" = "完成. 关闭";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"General" = "通用";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Information About InjectLib" = "关于 InjectLib 的信息";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Inject" = "注入";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Inject Configurations" = "注入配置";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"InjectGUI" = "InjectGUI";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"InjectGUI requires your password to perform administrative tasks." = "InjectGUI 需要您的密码来执行任务。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"InjectLib" = "InjectLib";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"It will only show when the injector is running." = "这仅在注入器运行时显示。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No apps found." = "找不到软件。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open in Finder" = "在访达中打开";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open Support Folder" = "打开应用支持文件夹";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Password" = "密码";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Please contact the developer if you see this view without running the injector." = "如果您在未运行注入器的情况下看到此视图,请联系开发人员。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Please enter your password to continue." = "请输入密码以继续";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Progress: %lld%%" = "进度:%lld%%";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Refresh app list" = "刷新软件列表";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Released under GPLv3. Based on QiuChenly/InjectLib." = "根据 GPLv3 发布. 基于 QiuChenly/InjectLib。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Restarting the app will require your password again." = "重新启动应用程序将需要再次输入您的密码。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Search all local app" = "搜索所有本地软件";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Settings" = "设置";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Settings About InjectGUI" = "关于 InjectGUI 的设置";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Status" = "状态";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Stop Injecting" = "停止注入";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Submit" = "提交";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app can use keygen to generate key" = "此应用程序可以使用keygen生成密钥";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app does not have extraShell" = "此软件没有 extraShell";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app has extraShell" = "此软件有extraShell";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app has sub app" = "此软件有附属软件";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app is supported by InjectLib." = "此应用程序由 InjectLib 支持。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app need auto handle helper" = "此应用程序需要自动处理Helper";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app need deep codesign" = "此软件需要 deep codesign";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This app needs to use tccutil to reset" = "此软件需要使用 tccutil 来重置";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This is a status view for the injector." = "这是注入器的状态视图。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Toggle Sidebar" = "切换侧边栏";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Universal Version (? Maybe)" = "通用版本(?应该是吧)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Update InjectLib & Tools" = "更新 InjectLib & 相关工具";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"URL" = "链接";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Version: %@" = "版本:%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Welcome to InjectGUI" = "欢迎使用 InjectGUI";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Why you are seeing this?" = "你怎么看到的?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Remote InjectLib Git URL" = "指定 InjectLib 远程 Git URL";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Remote InjectLib Branch" = "指定 InjectLib 远程分支";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Remote InjectLib Commit" = "指定 InjectLib 提交 Commit";
|
||||
@@ -55,15 +55,15 @@ struct AdminPrivilegeView: View {
|
||||
}
|
||||
|
||||
func checkAndSavePassword(password: String) {
|
||||
executor.password = password
|
||||
executor.password = password.encode()
|
||||
executor.executeAdminCommand("sudo -v")
|
||||
.sink(receiveCompletion: { completion in
|
||||
if case .failure = completion {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Incorrect password"
|
||||
alert.informativeText = "Please try again."
|
||||
alert.messageText = String(localized: "Incorrect password")
|
||||
alert.informativeText = String(localized: "Please try again.")
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.addButton(withTitle: String(localized: "OK"))
|
||||
alert.runModal()
|
||||
}
|
||||
}, receiveValue: { _ in
|
||||
|
||||
@@ -35,6 +35,7 @@ struct AppDetailView: View {
|
||||
if getAppDetailFromSoftwareManager != nil {
|
||||
self.appDetail = SoftwareManager.shared.appListCache[appId]!
|
||||
} else {
|
||||
print("[*] Can't find app detail in SoftwareManager, it's a abnormal situation, please report to developer. appId: \(appId)")
|
||||
self.appDetail = AppDetail(name: appInjectConfDetail?.packageName.allStrings.first ?? "", identifier: appInjectConfDetail?.packageName.allStrings.first ?? "", version: "", path: "", executable: "", icon: NSImage())
|
||||
}
|
||||
// self._appDetail = State(wrappedValue: SoftwareManager.shared.appListCache[appId] ?? AppDetail(name: "", identifier: "", version: "", path: "", icon: NSImage()))
|
||||
@@ -54,7 +55,7 @@ struct AppDetailView: View {
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.onAppear {
|
||||
let inInjectLibList = injectConfiguration.checkPackageIsSupported(package: appId)
|
||||
self.compatibility = Compatibility(
|
||||
compatibility = Compatibility(
|
||||
id: appId,
|
||||
inInjectLibList: inInjectLibList
|
||||
)
|
||||
@@ -70,8 +71,15 @@ struct AppDetailView: View {
|
||||
.frame(width: 64, height: 64)
|
||||
.cornerRadius(4)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(appDetail.name)
|
||||
.font(.headline)
|
||||
HStack {
|
||||
Text(appDetail.name)
|
||||
.font(.headline)
|
||||
|
||||
Label("Injected", systemImage: "checkmark.circle.fill")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.green)
|
||||
.opacity(appDetail.isInjected ? 1 : 0)
|
||||
}
|
||||
Text(appDetail.identifier)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
@@ -87,10 +95,10 @@ struct AppDetailView: View {
|
||||
injector.startInjectApp(package: appId)
|
||||
} else {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Inject is running"
|
||||
alert.informativeText = "It's a abnormal situation, it shouldn't be running, please report to developer."
|
||||
alert.messageText = String(localized: "Inject is running")
|
||||
alert.informativeText = String(localized: "It's a abnormal situation, it shouldn't be running, please report to developer.")
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.addButton(withTitle: String(localized: "OK"))
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
@@ -145,8 +153,8 @@ struct AppDetailView: View {
|
||||
}
|
||||
|
||||
HStack {
|
||||
Image(systemName: (appInjectConfDetail?.componentApp ?? []).count > 0 ? CompatibilityIcon.compatible.rawValue : CompatibilityIcon.incompatible.rawValue)
|
||||
.foregroundColor((appInjectConfDetail?.componentApp ?? []).count > 0 ? .green : .red)
|
||||
Image(systemName: !(appInjectConfDetail?.componentApp ?? []).isEmpty ? CompatibilityIcon.compatible.rawValue : CompatibilityIcon.incompatible.rawValue)
|
||||
.foregroundColor(!(appInjectConfDetail?.componentApp ?? []).isEmpty ? .green : .red)
|
||||
|
||||
HStack {
|
||||
Text("This app has sub app")
|
||||
@@ -165,8 +173,8 @@ struct AppDetailView: View {
|
||||
}
|
||||
|
||||
HStack {
|
||||
Image(systemName: (appInjectConfDetail?.tccutil?.allStrings ?? []).count > 0 ? CompatibilityIcon.compatible.rawValue : CompatibilityIcon.incompatible.rawValue)
|
||||
.foregroundColor((appInjectConfDetail?.tccutil?.allStrings ?? []).count > 0 ? .green : .red)
|
||||
Image(systemName: !(appInjectConfDetail?.tccutil?.allStrings ?? []).isEmpty ? CompatibilityIcon.compatible.rawValue : CompatibilityIcon.incompatible.rawValue)
|
||||
.foregroundColor(!(appInjectConfDetail?.tccutil?.allStrings ?? []).isEmpty ? .green : .red)
|
||||
|
||||
HStack {
|
||||
Text("This app needs to use tccutil to reset")
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
// Created by wibus on 2024/7/19.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@@ -18,7 +19,7 @@ struct ContentView: View {
|
||||
}
|
||||
.navigationTitle(Constants.appName)
|
||||
|
||||
WelcomeView()
|
||||
AboutView()
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigation) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
//
|
||||
// WelcomeView.swift
|
||||
// AboutView.swift
|
||||
// InjectGUI
|
||||
//
|
||||
// Created by wibus on 2024/7/20.
|
||||
// Created by Licardo on 2024/8/9.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct WelcomeView: View {
|
||||
struct AboutView: View {
|
||||
var version: String {
|
||||
var ret = "Version: " +
|
||||
(Constants.appVersion)
|
||||
@@ -20,7 +20,8 @@ struct WelcomeView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
VStack {
|
||||
Spacer()
|
||||
VStack(spacing: 4) {
|
||||
Image("Avatar")
|
||||
.antialiased(true)
|
||||
@@ -43,25 +44,30 @@ struct WelcomeView: View {
|
||||
Text("By wibus. Made with ❤️")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
|
||||
Spacer().frame(height: 24)
|
||||
}
|
||||
Spacer()
|
||||
VStack {
|
||||
Spacer()
|
||||
Text(version)
|
||||
Text("Released under GPLv3. Based on QiuChenly/InjectLib.")
|
||||
.font(.system(size: 12, weight: .semibold, design: .rounded))
|
||||
.opacity(0.5)
|
||||
.onTapGesture {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "InjectGUI"
|
||||
alert.informativeText = "You're in \(injectConfiguration.mode == injectConfigurationMode.local ? "Local Mode" : "Remote Mode")."
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "InjectGUI"
|
||||
alert.informativeText = String(localized: "You're in") + (injectConfiguration.mode == injectConfigurationMode.local ? String(localized: "Local Mode") : String(localized: "Remote Mode"))
|
||||
alert.addButton(withTitle: String(localized: "OK"))
|
||||
alert.runModal()
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.tabItem {
|
||||
Label("About", systemImage: "info.circle")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
AboutView()
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
//
|
||||
// SettingsView.swift
|
||||
// GeneralView.swift
|
||||
// InjectGUI
|
||||
//
|
||||
// Created by wibus on 2024/7/20.
|
||||
// Created by Licardo on 2024/8/9.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
struct GeneralView: View {
|
||||
@StateObject var configuration = Configuration.shared
|
||||
|
||||
|
||||
var version: String {
|
||||
var ret = "Version: " +
|
||||
(Constants.appVersion)
|
||||
@@ -21,34 +20,8 @@ struct SettingsView: View {
|
||||
#endif
|
||||
return ret
|
||||
}
|
||||
|
||||
private enum Tabs: Hashable {
|
||||
case general
|
||||
case about
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
return TabView {
|
||||
generalView
|
||||
aboutView
|
||||
}
|
||||
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
|
||||
func SettingItemView(_ title: String, @ViewBuilder content: @escaping () -> some View) -> some View {
|
||||
HStack {
|
||||
Text(title)
|
||||
Spacer()
|
||||
content()
|
||||
}
|
||||
.padding(10)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(.secondary.opacity(0.1))
|
||||
.cornerRadius(6)
|
||||
}
|
||||
|
||||
var generalView: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
// Project info
|
||||
@@ -181,47 +154,17 @@ struct SettingsView: View {
|
||||
.tabItem {
|
||||
Label("General", systemImage: "gear")
|
||||
}
|
||||
.tag(Tabs.general)
|
||||
}
|
||||
|
||||
var aboutView: some View {
|
||||
ZStack {
|
||||
VStack(spacing: 4) {
|
||||
Image("Avatar")
|
||||
.antialiased(true)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 128, height: 128)
|
||||
|
||||
Spacer().frame(height: 16)
|
||||
|
||||
if #available(macOS 13.0, *) {
|
||||
Text("InjectGUI")
|
||||
.font(.system(.title2, design: .rounded, weight: .bold))
|
||||
Text("By wibus. Made with ❤️")
|
||||
.font(.system(.body, design: .rounded, weight: .bold))
|
||||
.foregroundColor(.secondary)
|
||||
} else {
|
||||
Text("Welcome to InjectGUI")
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
Text("By wibus. Made with ❤️")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer().frame(height: 24)
|
||||
}
|
||||
VStack {
|
||||
Spacer()
|
||||
Text("Released under GPLv3. Based on QiuChenly/InjectLib.")
|
||||
.font(.system(size: 12, weight: .semibold, design: .rounded))
|
||||
.opacity(0.5)
|
||||
}
|
||||
.padding()
|
||||
|
||||
func SettingItemView(_ title: String, @ViewBuilder content: @escaping () -> some View) -> some View {
|
||||
HStack {
|
||||
Text(title)
|
||||
Spacer()
|
||||
content()
|
||||
}
|
||||
.tabItem {
|
||||
Label("About", systemImage: "info.circle")
|
||||
}
|
||||
.tag(Tabs.about)
|
||||
.padding(10)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(.secondary.opacity(0.1))
|
||||
.cornerRadius(6)
|
||||
}
|
||||
}
|
||||
25
InjectGUI/View/Settings/SettingsView.swift
Normal file
25
InjectGUI/View/Settings/SettingsView.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// SettingsView.swift
|
||||
// InjectGUI
|
||||
//
|
||||
// Created by wibus on 2024/7/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
private enum Tabs: Hashable {
|
||||
case general
|
||||
case about
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
return TabView {
|
||||
GeneralView().tag(Tabs.general)
|
||||
AboutView().tag(Tabs.about)
|
||||
}
|
||||
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ struct SidebarView: View {
|
||||
// 应用列表或状态信息
|
||||
ZStack {
|
||||
if softwareManager.isLoading {
|
||||
ProgressView("Scanning apps...")
|
||||
ProgressView("Scanning Apps...")
|
||||
} else if filteredApps.isEmpty {
|
||||
Text("No apps found.")
|
||||
.font(.headline)
|
||||
@@ -55,13 +55,11 @@ struct SidebarView: View {
|
||||
}
|
||||
.onChange(of: injectConfiguration.remoteConf) { _ in
|
||||
updateFilteredApps()
|
||||
softwareManager.refreshAppList()
|
||||
}
|
||||
.onChange(of: searchText) { _ in
|
||||
updateFilteredApps()
|
||||
}
|
||||
.onAppear {
|
||||
softwareManager.refreshAppList()
|
||||
}
|
||||
}
|
||||
|
||||
private var appList: some View {
|
||||
@@ -75,8 +73,17 @@ struct SidebarView: View {
|
||||
.frame(width: 32, height: 32)
|
||||
.cornerRadius(4)
|
||||
VStack(alignment: .leading) {
|
||||
Text(app.detail.name)
|
||||
.font(.headline)
|
||||
HStack {
|
||||
Text(app.detail.name)
|
||||
.font(.headline)
|
||||
|
||||
Label("Injected", systemImage: "checkmark.circle.fill")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.green)
|
||||
.labelStyle(.iconOnly)
|
||||
.opacity(app.detail.isInjected ? 1 : 0)
|
||||
}
|
||||
|
||||
Text(app.detail.identifier)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
@@ -88,6 +95,9 @@ struct SidebarView: View {
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
.contextMenu {
|
||||
Button("Run App") {
|
||||
NSWorkspace.shared.openApplication(at: URL(fileURLWithPath: app.detail.path).deletingLastPathComponent(), configuration: NSWorkspace.OpenConfiguration())
|
||||
}
|
||||
Button("Open in Finder") {
|
||||
NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: app.detail.path)
|
||||
}
|
||||
@@ -109,9 +119,9 @@ struct SidebarView: View {
|
||||
return apps
|
||||
.filter { app in
|
||||
injectConfiguration.checkPackageIsSupported(package: app.detail.identifier) &&
|
||||
(searchText.isEmpty ||
|
||||
app.detail.name.lowercased().contains(searchText.lowercased()) ||
|
||||
app.detail.identifier.lowercased().contains(searchText.lowercased()))
|
||||
(searchText.isEmpty ||
|
||||
app.detail.name.lowercased().contains(searchText.lowercased()) ||
|
||||
app.detail.identifier.lowercased().contains(searchText.lowercased()))
|
||||
}
|
||||
.sorted { $0.detail.name < $1.detail.name }
|
||||
}
|
||||
|
||||
@@ -12,11 +12,10 @@ struct StatusView: View {
|
||||
@Environment(\.dismiss) var dismiss // dismiss the sheet
|
||||
|
||||
@StateObject var injector = Injector.shared
|
||||
@State var appDetail: AppDetail = .init(name: "", identifier: "", version: "", path: "", executable: "", icon: NSImage())
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
if appDetail.name.isEmpty {
|
||||
if self.injector.appDetail?.name == nil {
|
||||
Text("Why you are seeing this?")
|
||||
.font(.title)
|
||||
.bold()
|
||||
@@ -45,18 +44,18 @@ struct StatusView: View {
|
||||
// MARK: - App Info Display Box
|
||||
|
||||
HStack(spacing: 20) {
|
||||
Image(nsImage: appDetail.icon)
|
||||
Image(nsImage: self.injector.appDetail!.icon)
|
||||
.resizable()
|
||||
.frame(width: 64, height: 64)
|
||||
.cornerRadius(4)
|
||||
// Spacer()
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(appDetail.name)
|
||||
Text(self.injector.appDetail!.name)
|
||||
.font(.headline)
|
||||
Text(appDetail.identifier)
|
||||
Text(self.injector.appDetail!.identifier)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
Text("Version: \(appDetail.version)")
|
||||
Text("Version: \(self.injector.appDetail!.version)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
@@ -138,10 +137,10 @@ struct StatusView: View {
|
||||
return
|
||||
}
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Are you sure you want to stop injecting?"
|
||||
alert.informativeText = "The app may not work properly if you stop injecting."
|
||||
alert.addButton(withTitle: "Stop")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.messageText = String(localized: "Are you sure you want to stop injecting?")
|
||||
alert.informativeText = String(localized: "The app may not work properly if you stop injecting.")
|
||||
alert.addButton(withTitle: String(localized: "Stop"))
|
||||
alert.addButton(withTitle: String(localized: "Cancel"))
|
||||
alert.beginSheetModal(for: NSApp.keyWindow!) { response in
|
||||
if response == .alertFirstButtonReturn {
|
||||
injector.stopInjectApp()
|
||||
@@ -155,20 +154,15 @@ struct StatusView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.onChange(of: injector.stage.appId) { appId in
|
||||
guard let appDetail = softwareManager.appListCache[appId] else {
|
||||
return
|
||||
}
|
||||
self.appDetail = appDetail
|
||||
}
|
||||
.frame(minWidth: 350, minHeight: appDetail.name.isEmpty ? 200 : 400)
|
||||
// .onChange(of: injector.isRunning) { _ in
|
||||
// let appId = injector.stage.appId
|
||||
// guard let self.injector.appDetail = softwareManager.appListCache[appId] else {
|
||||
// return
|
||||
// }
|
||||
// self.self.injector.appDetail = self.injector.appDetail
|
||||
// }
|
||||
.frame(minWidth: 350, minHeight: 400)
|
||||
.padding()
|
||||
.onAppear {
|
||||
guard let appDetail = softwareManager.appListCache[injector.stage.appId] else {
|
||||
return
|
||||
}
|
||||
self.appDetail = appDetail
|
||||
}
|
||||
.background(Color(.windowBackgroundColor))
|
||||
}
|
||||
}
|
||||
|
||||
23
README.md
23
README.md
@@ -1,9 +1,17 @@
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> InjectGUI is about to complete its mission and reach the end of its development.
|
||||
>
|
||||
> Where is the next generation? Stay tuned!
|
||||
|
||||
<p align="center">
|
||||
<p align="center">
|
||||
<img width="150" height="150" src="/InjectGUI/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png" alt="Logo">
|
||||
</p>
|
||||
<h1 align="center"><b>InjectGUI</b></h1>
|
||||
<p align="center">
|
||||
<b><small><code>STOP MAINTENANCE</code></small><b>
|
||||
<br>
|
||||
macOS Integrated Injection Framework (GUI version)
|
||||
<br />
|
||||
<br />
|
||||
@@ -13,23 +21,24 @@
|
||||
</p>
|
||||
</p>
|
||||
|
||||
## Stop maintenance
|
||||
|
||||
> I know that InjectGUI currently has a lot of injection problems on some systems and models, but it takes a lot of time to solve it. Maybe I don't have so much time, QiuChenly will launch more powerful software.
|
||||
>
|
||||
> So far, thanks QiuChenlyOpenSource.
|
||||
|
||||
## ⚠️ Warning
|
||||
|
||||
_**New to Swift. Poor code quality.**_
|
||||
|
||||
## 🌍 Localization
|
||||
|
||||
The localization of InjectGUI is in progress, and some content has not been translated yet. PRs are welcome.
|
||||
|
||||
- [x] English
|
||||
- [ ] Chinese (Simplified) (Not yet)
|
||||
Once you meet any problems with *InjectGUI*, You should back to the [InjectLib](https://github.com/QiuChenly/InjectLib) project to continue your operation.
|
||||
|
||||
## 🌟 Features
|
||||
|
||||
- [x] Basic Functionality
|
||||
- [x] Backup Executable File
|
||||
- [x] Check Permission and kill process
|
||||
- [x] Handle Keygen
|
||||
- [ ] Handle Keygen *(Coming soon)*
|
||||
- [x] Handle Deep Code Sign
|
||||
- [x] Handle Tccutil
|
||||
- [x] Handle Extra Shell
|
||||
|
||||
Reference in New Issue
Block a user