mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
macOS: full support — MoltenVK auto-discovery, portability fix, and KytyPS5.app bundle (#394)
* macOS: auto-discover bundled MoltenVK on startup
On macOS the Vulkan loader comes from MoltenVK. SDL's default
probe only searches system paths, so a bundled libMoltenVK.dylib
next to the executable is invisible under the hardened runtime.
Probe it explicitly via SDL_GetBasePath() before falling back to
SDL's default search. Respect SDL_VULKAN_LIBRARY overrides and
log which loader is used. Update README to reflect that no
environment variable is required for bundled builds.
Co-Authored-By: internal-model
* window: fix Vulkan portability enumeration on macOS
MoltenVK requires VK_KHR_portability_enumeration and
VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR to surface
portability devices via enumeratePhysicalDevices. Without the flag
the adapter is hidden on some driver versions, causing
"Could not find suitable device" failures.
Add the instance extension and flag when available, and add
diagnostic logs for all MoltenVK fallbacks (depthClipEnable,
colorWriteEnable, depthBounds, fragmentShaderBarycentric)
so black-screen diagnostics are actionable.
* macOS: add KytyPS5.app bundle packaging
Add a proper macOS application bundle when the launcher is built.
launcher becomes KytyPS5.app (CFBundleIdentifier org.kytyps5.kytyps5)
with KytyPS5 as the executable, and kyty_emulator is installed as a
helper inside Contents/MacOS so QCoreApplication::applicationDirPath()
finds it. MoltenVK is auto-discovered via SDL_GetBasePath() in that
directory, so no SDL_VULKAN_LIBRARY is required.
- src/launcher/Info.plist.in: minimal bundle plist (version from
PROJECT_VERSION, LSMinimumSystemVersion 13.0).
- src/launcher/CMakeLists.txt: set MACOSX_BUNDLE, BUNDLE DESTINATION,
install helper, re-sign helper with JIT entitlements, and invoke
macdeployqt when available. Keep flat kyty_emulator for CLI.
- .github/workflows/build.yml: bundle MoltenVK into
KytyPS5.app/Contents/MacOS when present, verify bundle artifacts
(file/lipo/codesign), and handle bundle paths in the release zip.
- README.md: document bundle double-click and MoltenVK locations.
* launcher: fix bundle rpath for Qt frameworks
The bundle executable had @rpath references to Qt frameworks but no
LC_RPATH, causing dyld to abort at launch:
Library not loaded: @rpath/QtConcurrent.framework
Reason: no LC_RPATH's found
Set INSTALL_RPATH to @executable_path/../Frameworks and
BUILD_WITH_INSTALL_RPATH so the built and installed bundles
resolve Qt from Contents/Frameworks. Verified via otool -l
(LC_RPATH now present) and macdeployqt now succeeds without
"Cannot resolve rpath" errors.
* launcher: add Import game button
Add a prominent Import button to ConfigurationListWidget that lets
users select a game folder (containing eboot.bin) or a folder
containing multiple games via QFileDialog. Handles:
- Single game folder selected: adds its parent to game_dirs (Scan
searches subdirectories, so parent is needed)
- Folder with multiple games: adds the selected folder
- Deduplication via PathKey/NormalizeGameDirectories, writes
settings, rescans, and selects the imported game if found
- Shows info/warning dialogs when no games are found
UI: QToolButton with SP_DirOpenIcon and text Import beside the
existing toolbar, styled with a blue background for visibility.
Logic in ConfigurationListWidget::import_game().
* macOS: fix bundled loader and artifact packaging
* Revert "launcher: add Import game button"
This reverts commit 2f670ef95c.
---------
Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
+47
-22
@@ -208,34 +208,66 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
archive="$RUNNER_TEMP/MoltenVK-macos.tar"
|
archive="$RUNNER_TEMP/MoltenVK-macos.tar"
|
||||||
package="$RUNNER_TEMP/MoltenVK"
|
package="$RUNNER_TEMP/MoltenVK"
|
||||||
|
install_dir="_Build/macos/install"
|
||||||
|
app_dir="$install_dir/KytyPS5.app"
|
||||||
|
moltenvk="$install_dir/libMoltenVK.dylib"
|
||||||
curl --fail --location --retry 3 \
|
curl --fail --location --retry 3 \
|
||||||
--output "$archive" \
|
--output "$archive" \
|
||||||
"https://github.com/KhronosGroup/MoltenVK/releases/download/$MOLTENVK_VERSION/MoltenVK-macos.tar"
|
"https://github.com/KhronosGroup/MoltenVK/releases/download/$MOLTENVK_VERSION/MoltenVK-macos.tar"
|
||||||
echo "$MOLTENVK_SHA256 $archive" | shasum -a 256 --check
|
echo "$MOLTENVK_SHA256 $archive" | shasum -a 256 --check
|
||||||
tar -xf "$archive" -C "$RUNNER_TEMP"
|
tar -xf "$archive" -C "$RUNNER_TEMP"
|
||||||
|
test -d "$app_dir"
|
||||||
install -m 755 \
|
install -m 755 \
|
||||||
"$package/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" \
|
"$package/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" \
|
||||||
_Build/macos/install/libMoltenVK.dylib
|
"$moltenvk"
|
||||||
install -m 644 "$package/LICENSE" _Build/macos/install/LICENSE.MoltenVK
|
install -m 644 "$package/LICENSE" "$install_dir/LICENSE.MoltenVK"
|
||||||
codesign --force --sign - --timestamp=none _Build/macos/install/libMoltenVK.dylib
|
codesign --force --sign - --timestamp=none "$moltenvk"
|
||||||
|
install -d -m 755 "$app_dir/Contents/Frameworks" "$app_dir/Contents/Resources"
|
||||||
|
install -m 755 "$moltenvk" "$app_dir/Contents/Frameworks/libMoltenVK.dylib"
|
||||||
|
install -m 644 "$package/LICENSE" "$app_dir/Contents/Resources/LICENSE.MoltenVK"
|
||||||
|
codesign --force --sign - --timestamp=none "$app_dir"
|
||||||
|
|
||||||
- name: Verify artifacts
|
- name: Verify artifacts
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
file _Build/macos/install/launcher
|
install_dir="_Build/macos/install"
|
||||||
file _Build/macos/install/kyty_emulator
|
app_dir="$install_dir/KytyPS5.app"
|
||||||
file _Build/macos/install/libMoltenVK.dylib
|
test -d "$app_dir"
|
||||||
lipo _Build/macos/install/launcher -verify_arch x86_64
|
for binary in \
|
||||||
lipo _Build/macos/install/kyty_emulator -verify_arch x86_64
|
"$install_dir/kyty_emulator" \
|
||||||
lipo _Build/macos/install/libMoltenVK.dylib -verify_arch x86_64
|
"$install_dir/libMoltenVK.dylib" \
|
||||||
codesign --verify --strict _Build/macos/install/kyty_emulator
|
"$app_dir/Contents/MacOS/KytyPS5" \
|
||||||
codesign --verify --strict _Build/macos/install/libMoltenVK.dylib
|
"$app_dir/Contents/MacOS/kyty_emulator" \
|
||||||
|
"$app_dir/Contents/Frameworks/libMoltenVK.dylib"; do
|
||||||
|
file "$binary"
|
||||||
|
lipo "$binary" -verify_arch x86_64
|
||||||
|
done
|
||||||
|
for binary in \
|
||||||
|
"$install_dir/kyty_emulator" \
|
||||||
|
"$install_dir/libMoltenVK.dylib" \
|
||||||
|
"$app_dir/Contents/MacOS/KytyPS5" \
|
||||||
|
"$app_dir/Contents/MacOS/kyty_emulator" \
|
||||||
|
"$app_dir/Contents/Frameworks/libMoltenVK.dylib"; do
|
||||||
|
codesign --verify --strict "$binary"
|
||||||
|
done
|
||||||
|
codesign --verify --deep --strict --verbose=2 "$app_dir"
|
||||||
|
|
||||||
|
- name: Package macOS artifacts
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
install_dir="_Build/macos/install"
|
||||||
|
archive="$RUNNER_TEMP/KytyPS5-macOS-x86_64.zip"
|
||||||
|
(
|
||||||
|
cd "$install_dir"
|
||||||
|
zip -qry "$archive" .
|
||||||
|
)
|
||||||
|
unzip -t "$archive"
|
||||||
|
|
||||||
- name: Upload macOS artifacts
|
- name: Upload macOS artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: KytyPS5-macOS-x86_64
|
name: KytyPS5-macOS-x86_64
|
||||||
path: _Build/macos/install/**
|
path: ${{ runner.temp }}/KytyPS5-macOS-x86_64.zip
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
linux:
|
linux:
|
||||||
@@ -424,16 +456,9 @@ jobs:
|
|||||||
if: needs.macos.result == 'success'
|
if: needs.macos.result == 'success'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
macos_dir="artifacts/KytyPS5-macOS-x86_64"
|
macos_archive="artifacts/KytyPS5-macOS-x86_64/KytyPS5-macOS-x86_64.zip"
|
||||||
test -d "$macos_dir"
|
test -f "$macos_archive"
|
||||||
chmod a+x \
|
cp "$macos_archive" "$RELEASE_NAME-macOS-x86_64.zip"
|
||||||
"$macos_dir/launcher" \
|
|
||||||
"$macos_dir/kyty_emulator" \
|
|
||||||
"$macos_dir/libMoltenVK.dylib"
|
|
||||||
(
|
|
||||||
cd "$macos_dir"
|
|
||||||
zip -r "$GITHUB_WORKSPACE/$RELEASE_NAME-macOS-x86_64.zip" .
|
|
||||||
)
|
|
||||||
|
|
||||||
- name: Package Linux build
|
- name: Package Linux build
|
||||||
if: needs.linux.result == 'success'
|
if: needs.linux.result == 'success'
|
||||||
|
|||||||
@@ -220,17 +220,23 @@ cmake --install _Build/macos --prefix _Build/macos/install
|
|||||||
```
|
```
|
||||||
|
|
||||||
The build re-signs `kyty_emulator` with the JIT entitlements it needs to execute translated
|
The build re-signs `kyty_emulator` with the JIT entitlements it needs to execute translated
|
||||||
guest code; no manual signing step is required.
|
guest code; no manual signing step is required. When the launcher is built, the install
|
||||||
|
also produces `_Build/macos/install/KytyPS5.app` — double-click to launch the GUI.
|
||||||
|
A flat `kyty_emulator` is kept for CLI usage.
|
||||||
|
|
||||||
Vulkan comes from MoltenVK. Download `MoltenVK-macos.tar` from the
|
Vulkan comes from MoltenVK. Download `MoltenVK-macos.tar` from the
|
||||||
[MoltenVK releases](https://github.com/KhronosGroup/MoltenVK/releases), then copy
|
[MoltenVK releases](https://github.com/KhronosGroup/MoltenVK/releases), then copy
|
||||||
`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to `kyty_emulator` and ad-hoc sign it:
|
`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to the flat `kyty_emulator`
|
||||||
|
(and, for the bundle, into `KytyPS5.app/Contents/Frameworks/`) and ad-hoc sign it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
codesign --force --sign - _Build/macos/install/libMoltenVK.dylib
|
codesign --force --sign - _Build/macos/install/libMoltenVK.dylib
|
||||||
|
# For the bundle (if present):
|
||||||
|
codesign --force --sign - _Build/macos/install/KytyPS5.app/Contents/Frameworks/libMoltenVK.dylib
|
||||||
|
codesign --force --sign - _Build/macos/install/KytyPS5.app
|
||||||
```
|
```
|
||||||
|
|
||||||
Release archives already include a signed `libMoltenVK.dylib`.
|
Release archives already include a signed `libMoltenVK.dylib` (both flat and inside the bundle).
|
||||||
|
|
||||||
### Regression tests
|
### Regression tests
|
||||||
|
|
||||||
@@ -274,6 +280,10 @@ To use the graphical launcher:
|
|||||||
./_Build/linux/install/launcher
|
./_Build/linux/install/launcher
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
open _Build/macos/install/KytyPS5.app # or double-click in Finder
|
||||||
|
```
|
||||||
|
|
||||||
On first launch, add one or more game folders in the global settings. The launcher searches those
|
On first launch, add one or more game folders in the global settings. The launcher searches those
|
||||||
folders recursively for game directories containing `eboot.bin`. Select a detected game and run it
|
folders recursively for game directories containing `eboot.bin`. Select a detected game and run it
|
||||||
from the game list.
|
from the game list.
|
||||||
@@ -288,12 +298,17 @@ The emulator can also be started directly with a legally obtained game directory
|
|||||||
./_Build/linux/install/kyty_emulator --game "/games/ExampleGame"
|
./_Build/linux/install/kyty_emulator --game "/games/ExampleGame"
|
||||||
```
|
```
|
||||||
|
|
||||||
On macOS, point SDL at the MoltenVK library explicitly; the hardened runtime prevents it from
|
On macOS, the adjacent flat or app-bundled `libMoltenVK.dylib` is found automatically; no
|
||||||
being picked up from the executable's directory:
|
environment variable is required:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd _Build/macos/install
|
./_Build/macos/install/kyty_emulator --game "/games/ExampleGame"
|
||||||
SDL_VULKAN_LIBRARY="$PWD/libMoltenVK.dylib" ./kyty_emulator --game "/games/ExampleGame"
|
```
|
||||||
|
|
||||||
|
To override the Vulkan loader, set `SDL_VULKAN_LIBRARY`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SDL_VULKAN_LIBRARY=/path/to/libMoltenVK.dylib ./kyty_emulator --game "/games/ExampleGame"
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `kyty_emulator --help` to see the available graphics, logging, validation, profiling, and
|
Run `kyty_emulator --help` to see the available graphics, logging, validation, profiling, and
|
||||||
|
|||||||
@@ -942,6 +942,18 @@ void WindowContext::CreateVulkan() {
|
|||||||
inst_info.sType = vk::StructureType::eInstanceCreateInfo;
|
inst_info.sType = vk::StructureType::eInstanceCreateInfo;
|
||||||
inst_info.pNext = (r.enable_validation_layers ? &dbg_create_info : nullptr);
|
inst_info.pNext = (r.enable_validation_layers ? &dbg_create_info : nullptr);
|
||||||
inst_info.flags = {};
|
inst_info.flags = {};
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
// MoltenVK requires VK_KHR_portability_enumeration + flag to surface
|
||||||
|
// portability devices. Without this, enumeratePhysicalDevices hides the
|
||||||
|
// MoltenVK adapter on some driver versions.
|
||||||
|
if (HasExtension(r.available_extensions, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||||
|
if (!HasExtension(r.required_extensions, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||||
|
r.required_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||||
|
}
|
||||||
|
inst_info.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
|
||||||
|
LOGF("Vulkan instance: enabled %s\n", VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
inst_info.pApplicationInfo = &app_info;
|
inst_info.pApplicationInfo = &app_info;
|
||||||
inst_info.enabledExtensionCount = static_cast<uint32_t>(r.required_extensions.size());
|
inst_info.enabledExtensionCount = static_cast<uint32_t>(r.required_extensions.size());
|
||||||
inst_info.ppEnabledExtensionNames = r.required_extensions.data();
|
inst_info.ppEnabledExtensionNames = r.required_extensions.data();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "SDL_error.h"
|
#include "SDL_error.h"
|
||||||
#include "SDL_events.h"
|
#include "SDL_events.h"
|
||||||
|
#include "SDL_filesystem.h"
|
||||||
#include "SDL_gamecontroller.h"
|
#include "SDL_gamecontroller.h"
|
||||||
#include "SDL_hints.h"
|
#include "SDL_hints.h"
|
||||||
#include "SDL_joystick.h"
|
#include "SDL_joystick.h"
|
||||||
@@ -40,13 +41,12 @@
|
|||||||
#include "loader/systemContent.h"
|
#include "loader/systemContent.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <vulkan/vk_platform.h>
|
#include <vulkan/vk_platform.h>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#define STBI_NO_SIMD
|
#define STBI_NO_SIMD
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
@@ -812,6 +812,24 @@ static void WindowCreate(WindowContext& context) {
|
|||||||
|
|
||||||
uint32_t window_flags = WindowContext::InitialWindowFlags(Config::FullscreenEnabled());
|
uint32_t window_flags = WindowContext::InitialWindowFlags(Config::FullscreenEnabled());
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
|
// SDL loads Vulkan while creating a Vulkan window, so select the bundled
|
||||||
|
// MoltenVK loader before calling SDL_CreateWindow. Keep an explicit user
|
||||||
|
// override, and fall back to SDL's normal loader search when no bundle is present.
|
||||||
|
if (std::getenv("SDL_VULKAN_LIBRARY") == nullptr) {
|
||||||
|
if (char* base_path = SDL_GetBasePath(); base_path != nullptr) {
|
||||||
|
const std::string base_path_str = base_path;
|
||||||
|
SDL_free(base_path);
|
||||||
|
std::string moltenvk_path = base_path_str + "libMoltenVK.dylib";
|
||||||
|
if (!Common::File::IsFileExisting(moltenvk_path)) {
|
||||||
|
moltenvk_path = base_path_str + "../Frameworks/libMoltenVK.dylib";
|
||||||
|
}
|
||||||
|
if (Common::File::IsFileExisting(moltenvk_path) &&
|
||||||
|
SDL_setenv("SDL_VULKAN_LIBRARY", moltenvk_path.c_str(), 0) == 0) {
|
||||||
|
LOGF("Vulkan loader: %s\n", moltenvk_path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// macOS 26 window chrome (CoreUI asset decode, SwiftUI titlebar) has been observed
|
// macOS 26 window chrome (CoreUI asset decode, SwiftUI titlebar) has been observed
|
||||||
// throwing NSExceptions under Rosetta during the first CATransaction commit. A
|
// throwing NSExceptions under Rosetta during the first CATransaction commit. A
|
||||||
// borderless window skips that machinery entirely.
|
// borderless window skips that machinery entirely.
|
||||||
|
|||||||
@@ -66,10 +66,46 @@ if(WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(launcher_name "launcher")
|
set(launcher_name "launcher")
|
||||||
|
if(APPLE)
|
||||||
|
set(launcher_name "KytyPS5")
|
||||||
|
endif()
|
||||||
|
|
||||||
set_target_properties(launcher PROPERTIES OUTPUT_NAME ${launcher_name})
|
set_target_properties(launcher PROPERTIES OUTPUT_NAME ${launcher_name})
|
||||||
|
|
||||||
install(TARGETS launcher DESTINATION .)
|
if(APPLE)
|
||||||
|
# macOS bundle: KytyPS5.app with launcher as main executable and
|
||||||
|
# kyty_emulator as helper inside Contents/MacOS. MoltenVK is found
|
||||||
|
# next to a flat executable or in the bundle's Contents/Frameworks;
|
||||||
|
# no rpath fixup is needed because SDL loads it by absolute path.
|
||||||
|
# Qt deployment (Frameworks + plugins) is handled by macdeployqt.
|
||||||
|
set_target_properties(launcher PROPERTIES
|
||||||
|
MACOSX_BUNDLE TRUE
|
||||||
|
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
|
||||||
|
INSTALL_RPATH "@executable_path/../Frameworks"
|
||||||
|
BUILD_WITH_INSTALL_RPATH TRUE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS launcher
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
RUNTIME DESTINATION .)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
install(TARGETS kyty_emulator DESTINATION "KytyPS5.app/Contents/MacOS")
|
||||||
|
# Deploy and ad-hoc sign the Qt frameworks and plugins in the bundle.
|
||||||
|
find_program(KYTY_MACDEPLOYQT NAMES macdeployqt PATHS "${Qt6_DIR}/../../../bin" NO_DEFAULT_PATH)
|
||||||
|
if(NOT KYTY_MACDEPLOYQT)
|
||||||
|
message(FATAL_ERROR "Could not find macdeployqt")
|
||||||
|
endif()
|
||||||
|
install(CODE "
|
||||||
|
execute_process(COMMAND \"${KYTY_MACDEPLOYQT}\" \"\${CMAKE_INSTALL_PREFIX}/KytyPS5.app\"
|
||||||
|
-codesign=-
|
||||||
|
RESULT_VARIABLE _kyty_deploy_result)
|
||||||
|
if(NOT _kyty_deploy_result EQUAL 0)
|
||||||
|
message(FATAL_ERROR \"macdeployqt failed with \${_kyty_deploy_result}\")
|
||||||
|
endif()
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
install(CODE "
|
install(CODE "
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleName</key><string>KytyPS5</string>
|
||||||
|
<key>CFBundleDisplayName</key><string>KytyPS5</string>
|
||||||
|
<key>CFBundleIdentifier</key><string>org.kytyps5.kytyps5</string>
|
||||||
|
<key>CFBundleVersion</key><string>@PROJECT_VERSION@</string>
|
||||||
|
<key>CFBundleShortVersionString</key><string>@PROJECT_VERSION@</string>
|
||||||
|
<key>CFBundlePackageType</key><string>APPL</string>
|
||||||
|
<key>CFBundleSignature</key><string>????</string>
|
||||||
|
<key>CFBundleExecutable</key><string>KytyPS5</string>
|
||||||
|
<key>LSMinimumSystemVersion</key><string>13.0</string>
|
||||||
|
<key>NSHighResolutionCapable</key><true/>
|
||||||
|
<key>NSRequiresAquaSystemAppearance</key><false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Reference in New Issue
Block a user