diff --git a/core_info.c b/core_info.c index b7fe2e0d45..faa4b5bde0 100644 --- a/core_info.c +++ b/core_info.c @@ -1552,6 +1552,15 @@ static bool core_info_get_file_id(const char *core_filename, /* > Remove extension */ strlcpy(core_file_id, core_filename, len); path_remove_extension(core_file_id); +#if IOS + /* iOS framework names, to quote Apple: + * "must contain only alphanumerics, dots, hyphens and must not end with a dot." + * + * Since core names include underscore, which is not allowed, but not dot, + * which is, we change underscore to dot. Here, we need to change it back. + */ + string_replace_all_chars(core_file_id, '.', '_'); +#endif /* > Remove suffix */ last_underscore = (char*)strrchr(core_file_id, '_'); diff --git a/pkg/apple/RetroArchWidgetExtension/RetroArchWidgetExtension.intentdefinition b/pkg/apple/RetroArchWidgetExtension/RetroArchWidgetExtension.intentdefinition index bdb404554a..b6d6b0d814 100644 --- a/pkg/apple/RetroArchWidgetExtension/RetroArchWidgetExtension.intentdefinition +++ b/pkg/apple/RetroArchWidgetExtension/RetroArchWidgetExtension.intentdefinition @@ -9,16 +9,18 @@ INIntentDefinitionNamespace 88xZPY INIntentDefinitionSystemVersion - 20A294 + 23E224 INIntentDefinitionToolsBuildVersion - 12A6144 + 15E204a INIntentDefinitionToolsVersion - 12.0 + 15.3 INIntents INIntentCategory information + INIntentDescription + RetroArch Launch Widget INIntentDescriptionID tVvJ9c INIntentEligibleForWidgets diff --git a/pkg/apple/RetroArch_iOS13.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_iOS13.xcodeproj/project.pbxproj index 5875a56634..b78be7686b 100644 --- a/pkg/apple/RetroArch_iOS13.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_iOS13.xcodeproj/project.pbxproj @@ -120,7 +120,6 @@ 92CC05B921FE3C1700FF79F0 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059B21FE3C1700FF79F0 /* GCDWebServerDataRequest.m */; }; 92CC05BA21FE3C1700FF79F0 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059C21FE3C1700FF79F0 /* GCDWebServerFileRequest.m */; }; 92CC05BB21FE3C1700FF79F0 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC059C21FE3C1700FF79F0 /* GCDWebServerFileRequest.m */; }; - 92CC05BC21FE3C1700FF79F0 /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 92CC059F21FE3C1700FF79F0 /* GCDWebUploader.bundle */; }; 92CC05BD21FE3C1700FF79F0 /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 92CC059F21FE3C1700FF79F0 /* GCDWebUploader.bundle */; }; 92CC05BE21FE3C1700FF79F0 /* GCDWebUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC05A121FE3C1700FF79F0 /* GCDWebUploader.m */; }; 92CC05BF21FE3C1700FF79F0 /* GCDWebUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC05A121FE3C1700FF79F0 /* GCDWebUploader.m */; }; @@ -1467,7 +1466,6 @@ 07F7FB022A2DA8B800037C04 /* filters in Resources */, 9222F2092315DAD50097C0FD /* Launch Screen.storyboard in Resources */, 9204BE231D319EF300BD49DB /* InfoPlist.strings in Resources */, - 92CC05BC21FE3C1700FF79F0 /* GCDWebUploader.bundle in Resources */, 9222F20B2315DD3D0097C0FD /* retroarch_logo.png in Resources */, 929784502200EEE400989A60 /* iOS/Resources/Icons.xcassets in Resources */, 9222F1FF2314BA7C0097C0FD /* assets.zip in Resources */, @@ -1755,7 +1753,9 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = RetroArchTopShelfExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = RetroArchTopShelfExtension; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.games"; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 RetroArch. All rights reserved."; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1822,7 +1822,9 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = RetroArchTopShelfExtension/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = RetroArchTopShelfExtension; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.games"; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 RetroArch. All rights reserved."; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/bootstrap.css b/pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/bootstrap.css index 7f36651961..fbd42848bc 100644 --- a/pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/bootstrap.css +++ b/pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/css/bootstrap.css @@ -2377,7 +2377,7 @@ input[type="button"].btn-block { font-family: 'Glyphicons Halflings'; src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } .glyphicon { position: relative; diff --git a/pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.woff b/pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.woff deleted file mode 100644 index 8c54182aa5..0000000000 Binary files a/pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.bundle/Contents/Resources/fonts/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/pkg/apple/iOS/Info.plist b/pkg/apple/iOS/Info.plist index 11d753a048..bbdf203c8f 100644 --- a/pkg/apple/iOS/Info.plist +++ b/pkg/apple/iOS/Info.plist @@ -15,6 +15,8 @@ CFBundleTypeName ROM + LSHandlerRank + Owner LSItemContentTypes com.libretro.rom @@ -23,6 +25,8 @@ CFBundleTypeName All Files + LSHandlerRank + Alternate LSItemContentTypes public.data @@ -48,6 +52,25 @@ $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) + GCSupportedGameControllers + + + ProfileName + ExtendedGamepad + + + ProfileName + DirectionalGamepad + + + ProfileName + MicroGamepad + + + GCSupportsControllerUserInteraction + + ITSAppUsesNonExemptEncryption + LSApplicationCategoryType public.app-category.games LSRequiresIPhoneOS @@ -61,8 +84,10 @@ NSLocalNetworkUsageDescription RetroArch uses the local network to find local Netplay participants. - UIApplicationExitsOnSuspend - + NSUserActivityTypes + + ConfigurationIntent + UIFileSharingEnabled UILaunchStoryboardName @@ -91,23 +116,6 @@ UIViewControllerBasedStatusBarAppearance - GCSupportedGameControllers - - - ProfileName - ExtendedGamepad - - - ProfileName - DirectionalGamepad - - - ProfileName - MicroGamepad - - - GCSupportsControllerUserInteraction - UTImportedTypeDeclarations diff --git a/pkg/apple/iOS/fw.tmpl b/pkg/apple/iOS/fw.tmpl index 509712694d..15d68100b3 100644 --- a/pkg/apple/iOS/fw.tmpl +++ b/pkg/apple/iOS/fw.tmpl @@ -5,7 +5,7 @@ CFBundleExecutable %CORE% CFBundleName - %CORE% + %BUNDLE% CFBundleIdentifier %IDENTIFIER% CFBundleShortVersionString @@ -13,7 +13,7 @@ CFBundleVersion 1.0.0 MinimumOSVersion - 1.0 + 14.2 CFBundlePackageType FMWK CFBundleInfoDictionaryVersion diff --git a/pkg/apple/make-frameworks.sh b/pkg/apple/make-frameworks.sh index 1e079b5056..44f4be3f93 100755 --- a/pkg/apple/make-frameworks.sh +++ b/pkg/apple/make-frameworks.sh @@ -29,15 +29,14 @@ mkdir -p "$OUTDIR" for dylib in $(find "$BASE_DIR"/modules -maxdepth 1 -type f -regex '.*libretro.*\.dylib$') ; do intermediate=$(basename "$dylib") intermediate="${intermediate/%.dylib/}" - identifier="${intermediate/%$SUFFIX/}" - intermediate="${identifier/%_libretro/}" - fwName="${intermediate}_libretro" + intermediate="${intermediate/%$SUFFIX/}" + fwName="${intermediate//_/.}" echo Making framework $fwName from $dylib fwDir="${OUTDIR}/${fwName}.framework" mkdir -p "$fwDir" lipo -create "$dylib" -output "$fwDir/$fwName" - sed -e "s,%CORE%,$fwName," -e "s,%IDENTIFIER%,$identifier," iOS/fw.tmpl > "$fwDir/Info.plist" + sed -e "s,%CORE%,$fwName," -e "s,%BUNDLE%,$fwName," -e "s,%IDENTIFIER%,$fwName," iOS/fw.tmpl > "$fwDir/Info.plist" echo "signing $fwName" - codesign --force --verbose --sign "${CODE_SIGN_IDENTITY_FOR_ITEMS}" --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der "$fwDir" + codesign --force --verbose --sign "${CODE_SIGN_IDENTITY_FOR_ITEMS}" "$fwDir" done diff --git a/pkg/apple/rebuild-assets.sh b/pkg/apple/rebuild-assets.sh index 6c5bf4f8f4..d054ba7974 100755 --- a/pkg/apple/rebuild-assets.sh +++ b/pkg/apple/rebuild-assets.sh @@ -2,36 +2,53 @@ WD=$(realpath $(dirname $0)) +include_autoconfig="" include_cheats="" +include_databases="" include_overlays="" include_shaders="" +include_xmb="" assets_zip="$WD/assets.zip" -args=`getopt achmos $*` +args=`getopt acdhimosx $*` set -- $args while :; do case "$1" in -a) + include_autoconfig=1 include_cheats=1 + include_databases=1 include_overlays=1 include_shaders=1 + include_xmb=1 shift ;; -c) include_cheats=1 shift ;; + -d) + include_databases=1 + shift + ;; -h) echo "$(basename $0) -- Rebuild assets.zip" echo "Meant to be used when building RetroArch yourself. The buildbot does not use this." echo + echo " -a Include all assets, cheats, databases, input autoconfig, overlays, and shaders" echo " -c Include cheats" + echo " -d Include databases" + echo " -i Include input autoconfig" echo " -o Include overlays" echo " -s Include shaders" - echo " -a Include cheats, overlays, and shaders" - echo " -m Build for macOS (places in OSX directory" + echo " -x Include XMB assets" + echo " -m Build for macOS (places in OSX directory)" exit 0 ;; + -i) + include_autoconfig=1 + shift + ;; -m) assets_zip="$WD/OSX/assets.zip" shift @@ -44,6 +61,10 @@ while :; do include_shaders=1 shift ;; + -x) + include_xmb=1 + shift + ;; --) shift break @@ -64,8 +85,12 @@ pushd "$WD" &>/dev/null rm -rf .media fetch_zip retroarch-assets -fetch_zip retroarch-joypad-autoconfig -fetch_zip libretro-database +if [ -n "$include_autoconfig" ] ; then + fetch_zip retroarch-joypad-autoconfig +fi +if [ -n "$include_cheats" -o -n "$include_databases" ] ; then + fetch_zip libretro-database +fi fetch_zip libretro-super if [ -n "$include_overlays" ] ; then fetch_zip common-overlays @@ -78,19 +103,28 @@ fi pushd .media &>/dev/null echo "Packaging assets" -mkdir assets ; mv retroarch-assets/{COPYING,glui,menu_widgets,ozone,pkg,rgui,sounds,xmb} assets ; rm -rf retroarch-assets +mkdir assets ; mv retroarch-assets/{COPYING,glui,menu_widgets,ozone,pkg,rgui,sounds} assets +if [ -n "$include_xmb" ] ; then + mv retroarch-assets/xmb assets +fi +rm -rf retroarch-assets rm -rf assets/pkg/wiiu -echo "Packaging autoconfig" -mv retroarch-joypad-autoconfig autoconfig -rm -rf autoconfig/{android,dinput,linuxraw,parport,qnx,sdl2,udev,x,xinput} +if [ -n "$include_autoconfig" ] ; then + echo "Packaging autoconfig" + mv retroarch-joypad-autoconfig autoconfig + rm -rf autoconfig/{android,dinput,linuxraw,parport,qnx,sdl2,udev,x,xinput} +fi if [ -n "$include_cheats" ] ; then echo "Packaging cheats" mv libretro-database/cht cht fi -echo "Packaging database" -mkdir database ; mv libretro-database/{cursors,rdb} database ; rm -rf libretro-database +if [ -n "$include_databases" ] ; then + echo "Packaging database" + mkdir database ; mv libretro-database/{cursors,rdb} database +fi +rm -rf libretro-database echo "Packaging info" mv libretro-super/info info ; rmdir libretro-super @@ -106,9 +140,9 @@ if [ -n "$include_shaders" ] ; then mkdir shaders ; mv glsl-shaders shaders/shaders_glsl ; mv slang-shaders shaders/shaders_slang fi -rm -f ../assets.zip +rm -f $assets_zip echo "Zipping final assets bundle..." -zip -qr ../assets.zip * +zip -qr $assets_zip * popd &>/dev/null diff --git a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json index 48ecb4fa43..370111ab31 100644 --- a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +++ b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -1,11 +1,12 @@ { "images" : [ { + "filename" : "retroarch_logo_back-1.png", "idiom" : "tv" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/retroarch_logo_back-1.png b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/retroarch_logo_back-1.png new file mode 100644 index 0000000000..26c974c4e8 Binary files /dev/null and b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/retroarch_logo_back-1.png differ diff --git a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json index 48ecb4fa43..86856d5b87 100644 --- a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +++ b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -1,11 +1,12 @@ { "images" : [ { + "filename" : "retroarch_logo_front-1.png", "idiom" : "tv" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/retroarch_logo_front-1.png b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/retroarch_logo_front-1.png new file mode 100644 index 0000000000..5e31e0d922 Binary files /dev/null and b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/retroarch_logo_front-1.png differ diff --git a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json index 48ecb4fa43..1025860f98 100644 --- a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +++ b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -1,11 +1,12 @@ { "images" : [ { + "filename" : "retroarch_logo_middle-1.png", "idiom" : "tv" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/retroarch_logo_middle-1.png b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/retroarch_logo_middle-1.png new file mode 100644 index 0000000000..54c979d3db Binary files /dev/null and b/pkg/apple/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/retroarch_logo_middle-1.png differ diff --git a/pkg/apple/tvOS/Info.plist b/pkg/apple/tvOS/Info.plist index ed1ecc42f6..4052eaf10f 100644 --- a/pkg/apple/tvOS/Info.plist +++ b/pkg/apple/tvOS/Info.plist @@ -2,17 +2,6 @@ - CFBundleURLTypes - - - CFBundleURLName - RetroArch URL - CFBundleURLSchemes - - retroarch - - - ALTBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) ALTDeviceID @@ -21,6 +10,30 @@ $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName RetroArch + CFBundleDocumentTypes + + + CFBundleTypeName + ROM + LSHandlerRank + Owner + LSItemContentTypes + + com.libretro.rom + + + + CFBundleTypeName + All Files + LSHandlerRank + Alternate + LSItemContentTypes + + public.data + public.content + + + CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -33,16 +46,19 @@ APPL CFBundleShortVersionString $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleURLName + RetroArch URL + CFBundleURLSchemes + + retroarch + + + CFBundleVersion $(CURRENT_PROJECT_VERSION) - LSApplicationCategoryType - public.app-category.games - LSRequiresIPhoneOS - - UIRequiredDeviceCapabilities - - arm64 - GCSupportedGameControllers @@ -60,25 +76,15 @@ GCSupportsControllerUserInteraction - CFBundleDocumentTypes + ITSAppUsesNonExemptEncryption + + LSApplicationCategoryType + public.app-category.games + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities - - CFBundleTypeName - ROM - LSItemContentTypes - - com.libretro.rom - - - - CFBundleTypeName - All Files - LSItemContentTypes - - public.data - public.content - - + arm64 UTImportedTypeDeclarations diff --git a/pkg/apple/update-cores.sh b/pkg/apple/update-cores.sh index 2606ab28c7..df0ae90787 100755 --- a/pkg/apple/update-cores.sh +++ b/pkg/apple/update-cores.sh @@ -154,7 +154,7 @@ else race quicknes smsplus - blastem + #blastem vice_x128 vice_x64 vice_x64sc @@ -202,7 +202,8 @@ else virtualxt geolith vircon32 - melondsds + #melondsds + 2048 ) for dylib in "${exports[@]}" ; do find_dylib $dylib diff --git a/playlist.c b/playlist.c index eb946f90a5..122cdbe78f 100644 --- a/playlist.c +++ b/playlist.c @@ -1131,11 +1131,23 @@ void playlist_resolve_path(enum playlist_file_mode mode, string_starts_with(path, ":/modules/") && string_ends_with(path, ".dylib")) { + /* iOS cores used to be packaged as .dylib files in the modules + * directory; App Store rules require turning them into Frameworks and + * putting them in the Frameworks directory. Because some playlists + * include the old core path, we'll translate it here. + */ path[string_index_last_occurance(path, '.')] = '\0'; if (string_ends_with(path, "_ios")) path[string_index_last_occurance(path, '_')] = '\0'; strlcpy(tmp, ":/Frameworks/", STRLEN_CONST(":/Frameworks/") + 1); strlcpy(tmp + STRLEN_CONST(":/Frameworks/"), path + STRLEN_CONST(":/modules/"), sizeof(tmp) - STRLEN_CONST(":/Frameworks/")); + /* iOS framework names, to quote Apple: + * "must contain only alphanumerics, dots, hyphens and must not end with a dot." + * + * Since core names include underscore, which is not allowed, but not dot, + * which is, we change underscore to dot. + */ + string_replace_all_chars(tmp, '_', '.'); strlcat(tmp, ".framework", sizeof(tmp)); fill_pathname_expand_special(path, tmp, len); }