Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ISSUE-001

虚幻引擎文件路径错误

常见表现

加载进度卡住,加载过程中提示存储空间不足,加载过程中提示文件操作失败,加载过程中闪退。

相关源码

虚幻引擎 IOSPlatformFile.cpp

FString FIOSPlatformFile::ConvertToPlatformPath(const FString& Filename, bool bForWrite, bool bIsPublicWrite)
{
	FString Result = Filename;
	if (Result.StartsWith(TEXT("/var/")))
	{
		return Result;
	}
	
	if (bForWrite)
	{
		static FString PublicWritePathBase = FString([NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]) + TEXT("/");
		static FString PrivateWritePathBase = FString([NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]) + TEXT("/");
		return (bIsPublicWrite ? PublicWritePathBase : PrivateWritePathBase) + Result;
	}
	else
	{
		static FString ReadPathBase = FString([[NSBundle mainBundle] bundlePath]) + TEXT("/cookeddata/");
		return ReadPathBase + Result.ToLower();
	}
}

原因分析

源码中根据字符串是否以/var/开头,来判断字符串是否绝对路径。如果不是绝对路径,则会在字符串前面附加Documents目录或Library目录的路径。

在iOS上,绝对路径的格式为:/var/mobile/Containers/Data/Application/{GUID}/Documents

在macOS上,绝对路径的格式为:/Users/$USER/Containers/{BUNDLE_ID}/Data/Documents

macOS的绝对路径并不是以/var/开头的,调用ConvertToPlatformPath()后会生成错误的路径。

如果所有文件操作都经过一遍ConvertToPlatformPath()处理的话,是不会导致报错的,只有在读写操作不一致时才会出问题。

例如通过虚幻引擎API写入文件,间接调用了ConvertToPlatformPath(),但在读取文件时却直接使用NSFileManager之类的系统API,就会出现路径不一致,从而导致各种报错。

解决方法

文件链接法

BUNDLE_ID=com.companyname.appname

rm -r /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Documents/Users/$USER/Library/Containers/$BUNDLE_ID/Data && ln -sf /Users/$USER/Library/Containers/$BUNDLE_ID/Data /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Documents/Users/$USER/Library/Containers/$BUNDLE_ID/Data

rm -r /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Library/Users/$USER/Library/Containers/$BUNDLE_ID/Data && ln -sf /Users/$USER/Library/Containers/$BUNDLE_ID/Data /Users/$USER/Library/Containers/$BUNDLE_ID/Data/Library/Users/$USER/Library/Containers/$BUNDLE_ID/Data

补丁法

修改常量字符串/var//User(保持字符串长度不变)

BUNDLE_ID=com.companyname.appname
BUNDLE_PATH=~/Library/Containers/io.playcover.PlayCover/Applications/$BUNDLE_ID.app
EXECUTABLE=$BUNDLE_PATH/$(/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" $BUNDLE_PATH/Info.plist)
perl -e 'open F, "+<:raw", $ARGV[0] or die $!; local $/; my $d = <F>; my $i = index($d, "\x2F\x00\x76\x00\x61\x00\x72\x00\x2F\x00\x00\x00"); exit 1 if $i < 0; seek F, $i, 0; print F "\x2F\x00\x55\x00\x73\x00\x65\x00\x72\x00\x00\x00"; close F;' $EXECUTABLE
codesign -fs- $EXECUTABLE --deep --preserve-metadata=entitlements