diff --git a/Engine/Build/Android/Java/src/com/epicgames/ue4/GameActivity.java.template b/Engine/Build/Android/Java/src/com/epicgames/ue4/GameActivity.java.template
index 6b310cdd2b9a..aa900aa45496 100644
--- a/Engine/Build/Android/Java/src/com/epicgames/ue4/GameActivity.java.template
+++ b/Engine/Build/Android/Java/src/com/epicgames/ue4/GameActivity.java.template
@@ -41,6 +41,7 @@ import java.text.DecimalFormatSymbols;
import java.util.zip.Inflater;
import java.util.Timer;
import java.util.TimerTask;
+import java.lang.Runnable;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -75,6 +76,7 @@ import android.util.Log;
import android.os.Vibrator;
import android.os.SystemClock;
+import android.os.Looper;
import android.os.Handler;
import android.os.HandlerThread;
@@ -113,6 +115,8 @@ import android.provider.Settings;
import android.provider.Settings.Secure;
import android.content.pm.PackageInfo;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
import android.media.AudioManager;
import android.util.DisplayMetrics;
@@ -281,8 +285,9 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
static Bundle _bundle;
static Bundle _extrasBundle;
- private Timer memoryTimer;
- private TimerTask memoryTask;
+ private HandlerThread memoryHandlerThread;
+ private Handler memoryHandler;
+ private Runnable memoryRunnable;
public int UsedMemory;
public String InternalFilesDir;
@@ -2020,7 +2025,7 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
_activity = this;
// update memory stats every 10 seconds
- memoryTask = new TimerTask() {
+ memoryRunnable = new Runnable() {
@Override
public void run()
{
@@ -2048,11 +2053,17 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
synchronized(_activity)
{
_activity.UsedMemory = ProcessMemory;
+ if (_activity.memoryHandler != null)
+ {
+ _activity.memoryHandler.postDelayed(this, 10000);
+ }
}
}
};
- memoryTimer = new Timer();
- memoryTimer.schedule(memoryTask, 0, 10000);
+ memoryHandlerThread = new HandlerThread("MemoryUsageThread", android.os.Process.THREAD_PRIORITY_LOWEST);
+ memoryHandlerThread.start();
+ memoryHandler = new Handler(memoryHandlerThread.getLooper());
+ memoryHandler.postDelayed(memoryRunnable, 1);
// layout required by popups, e.g ads, native controls
MarginLayoutParams params = new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
@@ -2828,6 +2839,17 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
{
super.onResume();
+ // start memory reporter timer (runs every 10 seconds)
+ synchronized(this)
+ {
+ if (memoryHandler == null && memoryRunnable != null)
+ {
+ Log.debug("onResume: start memory reporter runnable");
+ memoryHandler = new Handler(memoryHandlerThread.getLooper());
+ memoryHandler.postDelayed(memoryRunnable, 1000);
+ }
+ }
+
// only do this on KitKat and above
if (ShouldHideUI)
{
@@ -2949,7 +2971,16 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
@Override
protected void onPause()
{
- super.onPause();
+ // stop memory reporter timer if running
+ synchronized(this)
+ {
+ if (memoryHandler != null)
+ {
+ Log.debug("onPause: stop memory reporter runnable");
+ memoryHandler.removeCallbacks(memoryRunnable);
+ memoryHandler = null;
+ }
+ }
if (bAllowIMU)
{
@@ -2988,6 +3019,7 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
});
}
//$${gameActivityOnPauseAdditions}$$
+ super.onPause();
Log.debug("==============> GameActive.onPause complete!");
}
@@ -3138,26 +3170,41 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
@Override
public void onStop()
{
- super.onStop();
-
if (consoleCmdReceiver != null)
{
unregisterReceiver(consoleCmdReceiver);
}
//$${gameActivityOnStopAdditions}$$
+ super.onStop();
Log.debug("==============> GameActive.onStop complete!");
}
@Override
public void onDestroy()
{
- super.onDestroy();
+ synchronized(this)
+ {
+ if (memoryHandler != null)
+ {
+ Log.debug("onDestroy: destroy memoryHandler");
+ memoryHandler.removeCallbacks(memoryRunnable);
+ memoryHandler = null;
+ }
+ if (memoryHandlerThread != null)
+ {
+ Log.debug("onDestroy: destroy memoryHandlerThread");
+ memoryHandlerThread.quit();
+ memoryHandlerThread = null;
+ }
+ }
+
if( IapStoreHelper != null )
{
IapStoreHelper.onDestroy();
}
//$${gameActivityOnDestroyAdditions}$$
+ super.onDestroy();
Log.debug("==============> GameActive.onDestroy complete!");
}
@@ -4458,7 +4505,10 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
new DeviceInfoData(0x045e, 0x028e, "Xbox Wired Controller"),
new DeviceInfoData(0x045e, 0x02e0, "Xbox Wireless Controller"),
new DeviceInfoData(0x0111, 0x1419, "SteelSeries Stratus XL"),
- new DeviceInfoData(0x054c, 0x05c4, "PS4 Wireless Controller")
+ new DeviceInfoData(0x054c, 0x05c4, "PS4 Wireless Controller"),
+ new DeviceInfoData(0x054c, 0x09cc, "PS4 Wireless Controller (v2)"),
+ new DeviceInfoData(0x05ac, 0x056a, "glap QXPGP001"),
+ new DeviceInfoData(0x0483, 0x5750, "STMicroelectronics Lenovo GamePad")
};
public class InputDeviceInfo {
@@ -5373,6 +5423,103 @@ public class GameActivity extends NativeActivity implements SurfaceHolder.Callba
return "";
}
+ @SuppressWarnings("deprecation")
+ public boolean AndroidThunkJava_CookieManager_SetCookie(String Url, String Value)
+ {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP)
+ {
+ CookieManager cookieManager = CookieManager.getInstance();
+ if (cookieManager != null)
+ {
+ cookieManager.setAcceptCookie(true);
+ cookieManager.setCookie(Url, Value); // could use callback for API 21+
+ cookieManager.flush();
+ return true;
+ }
+ }
+ else
+ {
+ Context context = getApplicationContext();
+ CookieSyncManager syncManager = CookieSyncManager.createInstance(context);
+ syncManager.sync();
+
+ CookieManager cookieManager = CookieManager.getInstance();
+ if (cookieManager != null)
+ {
+ cookieManager.setAcceptCookie(true);
+
+ cookieManager.setCookie(Url, Value);
+ syncManager.sync();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @SuppressWarnings("deprecation")
+ public String AndroidThunkJava_CookieManager_GetCookies(String Url)
+ {
+ if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP)
+ {
+ Context context = getApplicationContext();
+ CookieSyncManager syncManager = CookieSyncManager.createInstance(context);
+ syncManager.sync();
+ }
+
+ CookieManager cookieManager = CookieManager.getInstance();
+ if (cookieManager != null)
+ {
+ cookieManager.setAcceptCookie(true);
+ return cookieManager.getCookie(Url);
+ }
+ return null;
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean AndroidThunkJava_CookieManager_RemoveCookies(String Url)
+ {
+ if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP)
+ {
+ Context context = getApplicationContext();
+ CookieSyncManager syncManager = CookieSyncManager.createInstance(context);
+ syncManager.sync();
+
+ CookieManager cookieManager = CookieManager.getInstance();
+ if (cookieManager != null)
+ {
+ cookieManager.setAcceptCookie(true);
+ String cookies = cookieManager.getCookie(Url);
+ if (cookies == null)
+ {
+ return false;
+ }
+
+ cookieManager.setCookie(Url, "expires=Sat, 1 Jan 2000 00:00:01 UTC;");
+ cookieManager.removeExpiredCookie();
+ syncManager.sync();
+ return true;
+ }
+ return false;
+ }
+
+ CookieManager cookieManager = CookieManager.getInstance();
+ if (cookieManager != null)
+ {
+ cookieManager.setAcceptCookie(true);
+ String cookies = cookieManager.getCookie(Url);
+ if (cookies == null)
+ {
+ return false;
+ }
+
+ cookieManager.setCookie(Url, "expires=Sat, 1 Jan 2000 00:00:01 UTC;"); // could use callback for API 21+
+ cookieManager.flush();
+ return true;
+ }
+ return false;
+ }
+
public native int nativeGetCPUFamily();
public native boolean nativeSupportsNEON();
public native void nativeSetAffinityInfo(boolean bEnableAffinity, int bigCoreMask, int littleCoreMask);
diff --git a/Engine/Build/Android/Java/src/com/epicgames/ue4/MediaPlayer14.java b/Engine/Build/Android/Java/src/com/epicgames/ue4/MediaPlayer14.java
index a0dbd11f1315..3f951563a4d6 100644
--- a/Engine/Build/Android/Java/src/com/epicgames/ue4/MediaPlayer14.java
+++ b/Engine/Build/Android/Java/src/com/epicgames/ue4/MediaPlayer14.java
@@ -10,6 +10,7 @@ import android.os.Build;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
+import android.media.MediaDataSource;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaPlayer;
@@ -284,6 +285,112 @@ public class MediaPlayer14
return true;
}
+ public native int nativeReadAt(long identifier, long position, java.nio.ByteBuffer buffer, int offset, int size);
+
+ public class PakDataSource extends MediaDataSource {
+ java.nio.ByteBuffer fileBuffer;
+ long identifier;
+ long fileSize;
+
+ public PakDataSource(long inIdentifier, long inFileSize)
+ {
+ fileBuffer = java.nio.ByteBuffer.allocateDirect(65536);
+ identifier = inIdentifier;
+ fileSize = inFileSize;
+ }
+
+ @Override
+ public synchronized int readAt(long position, byte[] buffer, int offset, int size) throws IOException
+ {
+ synchronized(fileBuffer)
+ {
+ //GameActivity.Log.debug("PDS: readAt(" + position + ", " + offset + ", " + size + ") bufferLen=" + buffer.length + ", fileBuffer.length=" + fileSize);
+ if (position >= fileSize)
+ {
+ return -1; // EOF
+ }
+ if (position + size > fileSize)
+ {
+ size = (int)(fileSize - position);
+ }
+ if (size > 0)
+ {
+ int readBytes = nativeReadAt(identifier, position, fileBuffer, 0, size);
+ if (readBytes > 0)
+ {
+ System.arraycopy(fileBuffer.array(), fileBuffer.arrayOffset(), buffer, offset, readBytes);
+ }
+
+ return readBytes;
+ }
+ return 0;
+ }
+ }
+
+ @Override
+ public synchronized long getSize() throws IOException
+ {
+ //GameActivity.Log.debug("PDS: getSize() = " + fileSize);
+ return fileSize;
+ }
+
+ @Override
+ public synchronized void close() throws IOException
+ {
+ //GameActivity.Log.debug("PDS: close()");
+ }
+ }
+
+ public boolean setDataSourceArchive(
+ long identifier, long size)
+ throws IOException,
+ java.lang.InterruptedException,
+ java.util.concurrent.ExecutionException
+ {
+ synchronized(this)
+ {
+ Prepared = false;
+ Completed = false;
+ }
+ Looping = false;
+ AudioEnabled = true;
+ audioTracks.clear();
+ videoTracks.clear();
+
+ // Android 6.0 required for MediaDataSource
+ if (android.os.Build.VERSION.SDK_INT < 23)
+ {
+ return false;
+ }
+
+ try
+ {
+ PakDataSource dataSource = new PakDataSource(identifier, size);
+ setDataSource(dataSource);
+
+ releaseOESTextureRenderer();
+ releaseBitmapRenderer();
+
+ if (android.os.Build.VERSION.SDK_INT >= 16)
+ {
+ MediaExtractor extractor = new MediaExtractor();
+ if (extractor != null)
+ {
+ extractor.setDataSource(dataSource);
+ updateTrackInfo(extractor);
+ extractor.release();
+ extractor = null;
+ }
+ }
+ }
+ catch(IOException e)
+ {
+ GameActivity.Log.debug("setDataSource (archive): Exception = " + e);
+ return false;
+ }
+ return true;
+ }
+
public boolean setDataSource(
String moviePath, long offset, long size)
throws IOException,
diff --git a/Engine/Build/BatchFiles/Mac/Build.sh b/Engine/Build/BatchFiles/Mac/Build.sh
index b5b447cbcec5..0e7ad4a1275a 100755
--- a/Engine/Build/BatchFiles/Mac/Build.sh
+++ b/Engine/Build/BatchFiles/Mac/Build.sh
@@ -1,108 +1,17 @@
#!/bin/sh
-# This script gets called every time Xcode does a build or clean operation, even though it's called "Build.sh".
-# Values for $ACTION: "" = building, "clean" = cleaning
+cd "`dirname "$0"`/../../../.."
# Setup Mono
source Engine/Build/BatchFiles/Mac/SetupMono.sh Engine/Build/BatchFiles/Mac
-# override env if action is specified on command line
-if [ $1 == "clean" ]
- then
- ACTION="clean"
-fi
+if [ "$4" == "-buildscw" ] || [ "$5" == "-buildscw" ]; then
+ echo Building ShaderCompileWorker...
+ mono Engine/Binaries/DotNET/UnrealBuildTool.exe ShaderCompileWorker Mac Development
+fi
-
-case $ACTION in
- "")
- echo Building $1...
-
- Platform=""
- AdditionalFlags=""
-
- case $CLANG_STATIC_ANALYZER_MODE in
- "deep")
- AdditionalFlags+="-SkipActionHistory"
- ;;
- "shallow")
- AdditionalFlags+="-SkipActionHistory"
- ;;
- esac
-
- case $ENABLE_THREAD_SANITIZER in
- "YES"|"1")
- # Disable TSAN atomic->non-atomic race reporting as we aren't C++11 memory-model conformant so UHT will fail
- export TSAN_OPTIONS="suppress_equal_stacks=true suppress_equal_addresses=true report_atomic_races=false"
- ;;
- esac
-
- case $2 in
- "iphoneos"|"IOS")
- Platform="IOS"
- AdditionalFlags+=" -deploy "
- ;;
- "appletvos")
- Platform="TVOS"
- AdditionalFlags+=" -deploy "
- ;;
- "iphonesimulator")
- Platform="IOS"
- AdditionalFlags+=" -deploy -simulator"
- ;;
- "macosx")
- Platform="Mac"
- ;;
- "HTML5")
- Platform="HTML5"
- ;;
- *)
- Platform="$2"
- AdditionalFlags+=" -deploy "
- ;;
- esac
-
- BuildTasks=$(defaults read com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks)
- export NumUBTBuildTasks=$BuildTasks
-
- if [ "$4" == "-buildscw" ] || [ "$5" == "-buildscw" ]; then
- echo Building ShaderCompileWorker...
- mono Engine/Binaries/DotNET/UnrealBuildTool.exe ShaderCompileWorker Mac Development
- fi
-
- echo Running command : Engine/Binaries/DotNET/UnrealBuildTool.exe $1 $Platform $3 $AdditionalFlags "${@:4}"
- mono Engine/Binaries/DotNET/UnrealBuildTool.exe $1 $Platform $3 $AdditionalFlags "${@:4}"
- ;;
- "clean")
- echo "Cleaning $2 $3 $4..."
-
- Platform=""
- AdditionalFlags="-clean"
-
- case $3 in
- "iphoneos"|"IOS")
- Platform="IOS"
- AdditionalFlags+=" "
- ;;
- "iphonesimulator")
- Platform="IOS"
- AdditionalFlags+=" -simulator"
- AdditionalFlags+=" "
- ;;
- "macosx")
- Platform="Mac"
- ;;
- "HTML5")
- Platform="HTML5"
- ;;
- *)
- Platform="$3"
- ;;
-
- esac
- echo Running command: mono Engine/Binaries/DotNET/UnrealBuildTool.exe $2 $Platform $4 $AdditionalFlags "${@:5}"
- mono Engine/Binaries/DotNET/UnrealBuildTool.exe $2 $Platform $4 $AdditionalFlags "${@:5}"
- ;;
-esac
+echo Running command : Engine/Binaries/DotNET/UnrealBuildTool.exe "$@"
+mono Engine/Binaries/DotNET/UnrealBuildTool.exe "$@"
ExitCode=$?
if [ $ExitCode -eq 254 ] || [ $ExitCode -eq 255 ] || [ $ExitCode -eq 2 ]; then
diff --git a/Engine/Build/BatchFiles/Mac/XcodeBuild.sh b/Engine/Build/BatchFiles/Mac/XcodeBuild.sh
new file mode 100755
index 000000000000..b5b447cbcec5
--- /dev/null
+++ b/Engine/Build/BatchFiles/Mac/XcodeBuild.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+# This script gets called every time Xcode does a build or clean operation, even though it's called "Build.sh".
+# Values for $ACTION: "" = building, "clean" = cleaning
+
+# Setup Mono
+source Engine/Build/BatchFiles/Mac/SetupMono.sh Engine/Build/BatchFiles/Mac
+
+# override env if action is specified on command line
+if [ $1 == "clean" ]
+ then
+ ACTION="clean"
+fi
+
+
+case $ACTION in
+ "")
+ echo Building $1...
+
+ Platform=""
+ AdditionalFlags=""
+
+ case $CLANG_STATIC_ANALYZER_MODE in
+ "deep")
+ AdditionalFlags+="-SkipActionHistory"
+ ;;
+ "shallow")
+ AdditionalFlags+="-SkipActionHistory"
+ ;;
+ esac
+
+ case $ENABLE_THREAD_SANITIZER in
+ "YES"|"1")
+ # Disable TSAN atomic->non-atomic race reporting as we aren't C++11 memory-model conformant so UHT will fail
+ export TSAN_OPTIONS="suppress_equal_stacks=true suppress_equal_addresses=true report_atomic_races=false"
+ ;;
+ esac
+
+ case $2 in
+ "iphoneos"|"IOS")
+ Platform="IOS"
+ AdditionalFlags+=" -deploy "
+ ;;
+ "appletvos")
+ Platform="TVOS"
+ AdditionalFlags+=" -deploy "
+ ;;
+ "iphonesimulator")
+ Platform="IOS"
+ AdditionalFlags+=" -deploy -simulator"
+ ;;
+ "macosx")
+ Platform="Mac"
+ ;;
+ "HTML5")
+ Platform="HTML5"
+ ;;
+ *)
+ Platform="$2"
+ AdditionalFlags+=" -deploy "
+ ;;
+ esac
+
+ BuildTasks=$(defaults read com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks)
+ export NumUBTBuildTasks=$BuildTasks
+
+ if [ "$4" == "-buildscw" ] || [ "$5" == "-buildscw" ]; then
+ echo Building ShaderCompileWorker...
+ mono Engine/Binaries/DotNET/UnrealBuildTool.exe ShaderCompileWorker Mac Development
+ fi
+
+ echo Running command : Engine/Binaries/DotNET/UnrealBuildTool.exe $1 $Platform $3 $AdditionalFlags "${@:4}"
+ mono Engine/Binaries/DotNET/UnrealBuildTool.exe $1 $Platform $3 $AdditionalFlags "${@:4}"
+ ;;
+ "clean")
+ echo "Cleaning $2 $3 $4..."
+
+ Platform=""
+ AdditionalFlags="-clean"
+
+ case $3 in
+ "iphoneos"|"IOS")
+ Platform="IOS"
+ AdditionalFlags+=" "
+ ;;
+ "iphonesimulator")
+ Platform="IOS"
+ AdditionalFlags+=" -simulator"
+ AdditionalFlags+=" "
+ ;;
+ "macosx")
+ Platform="Mac"
+ ;;
+ "HTML5")
+ Platform="HTML5"
+ ;;
+ *)
+ Platform="$3"
+ ;;
+
+ esac
+ echo Running command: mono Engine/Binaries/DotNET/UnrealBuildTool.exe $2 $Platform $4 $AdditionalFlags "${@:5}"
+ mono Engine/Binaries/DotNET/UnrealBuildTool.exe $2 $Platform $4 $AdditionalFlags "${@:5}"
+ ;;
+esac
+
+ExitCode=$?
+if [ $ExitCode -eq 254 ] || [ $ExitCode -eq 255 ] || [ $ExitCode -eq 2 ]; then
+ exit 0
+else
+ exit $ExitCode
+fi
diff --git a/Engine/Build/Commit.gitdeps.xml b/Engine/Build/Commit.gitdeps.xml
index 92b8268c9759..2f24e202df52 100644
--- a/Engine/Build/Commit.gitdeps.xml
+++ b/Engine/Build/Commit.gitdeps.xml
@@ -5,11 +5,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -2432,23 +2432,23 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2456,135 +2456,135 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4306,6 +4306,8 @@
+
+
@@ -4603,6 +4605,7 @@
+
@@ -7750,9 +7753,9 @@
-
+
-
+
@@ -20221,7 +20224,7 @@
-
+
@@ -25731,6 +25734,15 @@
+
+
+
+
+
+
+
+
+
@@ -30857,12 +30869,7 @@
-
-
-
-
-
@@ -31398,14 +31405,21 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -31451,7 +31465,7 @@
-
+
@@ -31547,7 +31561,7 @@
-
+
@@ -33285,6 +33299,15 @@
+
+
+
+
+
+
+
+
+
@@ -33309,6 +33332,7 @@
+
@@ -34734,114 +34758,114 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -34893,57 +34917,57 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -34971,33 +34995,33 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -35025,297 +35049,297 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -38876,6 +38900,7 @@
+
@@ -38895,6 +38920,7 @@
+
@@ -38918,7 +38944,9 @@
+
+
@@ -38950,7 +38978,7 @@
-
+
@@ -38971,7 +38999,7 @@
-
+
@@ -38979,6 +39007,7 @@
+
@@ -39019,19 +39048,20 @@
-
-
+
+
-
+
+
-
+
@@ -39059,6 +39089,7 @@
+
@@ -39068,7 +39099,7 @@
-
+
@@ -39077,14 +39108,13 @@
-
+
-
-
+
@@ -39099,7 +39129,7 @@
-
+
@@ -39107,14 +39137,13 @@
-
-
+
@@ -39123,12 +39152,12 @@
-
+
@@ -39140,6 +39169,8 @@
+
+
@@ -39155,7 +39186,6 @@
-
@@ -39164,6 +39194,7 @@
+
@@ -39218,21 +39249,21 @@
-
+
-
+
-
+
-
+
@@ -39253,11 +39284,9 @@
-
-
@@ -39275,7 +39304,7 @@
-
+
@@ -39285,12 +39314,10 @@
-
-
@@ -39307,6 +39334,7 @@
+
@@ -39318,6 +39346,7 @@
+
@@ -39326,10 +39355,9 @@
-
-
+
@@ -39345,6 +39373,7 @@
+
@@ -39359,7 +39388,6 @@
-
@@ -39368,24 +39396,25 @@
+
+
+
-
-
-
+
-
+
@@ -39411,15 +39440,14 @@
-
-
+
@@ -39432,7 +39460,7 @@
-
+
@@ -39446,13 +39474,14 @@
-
+
+
-
+
@@ -39465,7 +39494,6 @@
-
@@ -39512,12 +39540,11 @@
-
-
+
@@ -39529,20 +39556,18 @@
-
+
-
-
@@ -39552,6 +39577,7 @@
+
@@ -39559,7 +39585,7 @@
-
+
@@ -39576,6 +39602,7 @@
+
@@ -39604,7 +39631,7 @@
-
+
@@ -39614,6 +39641,7 @@
+
@@ -39621,7 +39649,7 @@
-
+
@@ -39645,18 +39673,20 @@
+
-
+
+
@@ -39696,7 +39726,6 @@
-
@@ -39709,17 +39738,18 @@
-
+
-
+
-
+
+
@@ -39737,11 +39767,10 @@
-
-
+
@@ -39786,7 +39815,7 @@
-
+
@@ -39806,13 +39835,14 @@
-
+
-
+
+
@@ -39825,7 +39855,7 @@
-
+
@@ -39840,9 +39870,9 @@
-
+
-
+
@@ -39870,6 +39900,7 @@
+
@@ -39877,7 +39908,6 @@
-
@@ -39910,7 +39940,7 @@
-
+
@@ -39921,9 +39951,9 @@
-
+
@@ -39931,8 +39961,9 @@
+
-
+
@@ -39943,7 +39974,7 @@
-
+
@@ -39969,7 +40000,6 @@
-
@@ -40012,7 +40042,7 @@
-
+
@@ -40028,9 +40058,10 @@
+
-
+
@@ -40040,7 +40071,7 @@
-
+
@@ -40073,8 +40104,8 @@
+
-
@@ -40094,6 +40125,7 @@
+
@@ -40105,7 +40137,6 @@
-
@@ -40141,11 +40172,13 @@
-
+
+
+
@@ -40175,15 +40208,16 @@
-
+
+
-
+
@@ -40204,7 +40238,6 @@
-
@@ -40214,7 +40247,7 @@
-
+
@@ -40227,7 +40260,7 @@
-
+
@@ -40254,7 +40287,7 @@
-
+
@@ -40265,7 +40298,6 @@
-
@@ -40274,14 +40306,13 @@
-
+
-
@@ -40290,6 +40321,7 @@
+
@@ -40301,13 +40333,13 @@
-
+
@@ -40331,7 +40363,7 @@
-
+
@@ -40344,7 +40376,6 @@
-
@@ -40363,7 +40394,7 @@
-
+
@@ -40376,9 +40407,10 @@
-
+
+
@@ -40387,7 +40419,6 @@
-
@@ -40440,8 +40471,7 @@
-
-
+
@@ -40462,15 +40492,17 @@
-
+
+
+
@@ -40492,6 +40524,7 @@
+
@@ -40549,10 +40582,11 @@
-
+
+
@@ -40573,7 +40607,7 @@
-
+
@@ -40597,7 +40631,6 @@
-
@@ -40615,7 +40648,6 @@
-
@@ -40647,12 +40679,11 @@
-
-
+
@@ -40679,9 +40710,11 @@
-
+
+
+
@@ -40696,7 +40729,7 @@
-
+
@@ -40717,10 +40750,10 @@
-
+
-
-
+
+
@@ -40739,7 +40772,7 @@
-
+
@@ -40749,9 +40782,9 @@
+
-
@@ -40789,7 +40822,6 @@
-
@@ -40801,15 +40833,14 @@
-
-
-
+
+
-
+
@@ -40817,6 +40848,7 @@
+
@@ -40826,10 +40858,8 @@
-
-
@@ -40869,7 +40899,6 @@
-
@@ -40878,11 +40907,11 @@
-
+
-
+
@@ -40915,12 +40944,12 @@
-
+
-
+
@@ -40928,7 +40957,7 @@
-
+
@@ -40954,7 +40983,7 @@
-
+
@@ -40966,13 +40995,15 @@
+
-
+
+
@@ -40987,7 +41018,6 @@
-
@@ -41038,10 +41068,11 @@
+
-
+
@@ -41059,13 +41090,12 @@
-
-
+
@@ -41087,7 +41117,7 @@
-
+
@@ -41096,6 +41126,7 @@
+
@@ -41110,7 +41141,6 @@
-
@@ -41123,6 +41153,7 @@
+
@@ -41136,7 +41167,7 @@
-
+
@@ -41145,34 +41176,31 @@
-
-
-
+
+
+
-
-
-
-
+
@@ -41189,31 +41217,30 @@
-
-
-
+
-
+
+
-
-
+
+
-
+
@@ -41221,7 +41248,6 @@
-
@@ -41262,7 +41288,7 @@
-
+
@@ -41279,7 +41305,7 @@
-
+
@@ -41288,8 +41314,7 @@
-
-
+
@@ -41331,7 +41356,6 @@
-
@@ -41339,7 +41363,6 @@
-
@@ -41350,6 +41373,7 @@
+
@@ -41367,7 +41391,7 @@
-
+
@@ -41380,6 +41404,7 @@
+
@@ -41390,7 +41415,6 @@
-
@@ -41399,15 +41423,15 @@
-
+
-
+
-
+
@@ -41430,6 +41454,7 @@
+
@@ -41442,6 +41467,7 @@
+
@@ -41483,11 +41509,11 @@
+
-
@@ -41496,13 +41522,11 @@
-
-
@@ -41511,6 +41535,7 @@
+
@@ -41522,7 +41547,7 @@
-
+
@@ -41552,7 +41577,6 @@
-
@@ -41581,21 +41605,20 @@
-
-
-
+
+
-
+
@@ -41608,21 +41631,20 @@
+
-
-
+
-
@@ -41637,38 +41659,37 @@
-
+
-
+
-
+
+
-
+
-
+
-
-
+
-
@@ -41691,13 +41712,14 @@
+
-
+
@@ -41716,7 +41738,6 @@
-
@@ -41738,12 +41759,11 @@
-
+
-
@@ -41764,7 +41784,8 @@
-
+
+
@@ -41791,12 +41812,11 @@
-
-
+
@@ -41848,7 +41868,7 @@
-
+
@@ -41861,11 +41881,11 @@
+
-
@@ -41879,13 +41899,13 @@
-
+
-
+
@@ -41909,10 +41929,10 @@
+
-
@@ -41927,8 +41947,9 @@
-
+
+
@@ -41937,7 +41958,6 @@
-
@@ -41945,6 +41965,7 @@
+
@@ -41970,12 +41991,11 @@
-
+
-
@@ -41993,7 +42013,7 @@
-
+
@@ -42009,7 +42029,6 @@
-
@@ -42046,6 +42065,7 @@
+
@@ -42075,7 +42095,7 @@
-
+
@@ -42093,7 +42113,7 @@
-
+
@@ -42108,10 +42128,11 @@
+
-
+
@@ -42148,10 +42169,10 @@
-
+
-
+
@@ -42162,6 +42183,7 @@
+
@@ -42191,7 +42213,6 @@
-
@@ -42225,6 +42246,7 @@
+
@@ -42237,7 +42259,7 @@
-
+
@@ -42247,6 +42269,7 @@
+
@@ -42270,7 +42293,7 @@
-
+
@@ -42280,11 +42303,13 @@
-
-
+
+
+
+
@@ -42292,6 +42317,7 @@
+
@@ -42318,7 +42344,7 @@
-
+
@@ -42362,28 +42388,26 @@
-
+
-
+
-
+
-
-
@@ -42402,7 +42426,7 @@
-
+
@@ -42413,7 +42437,7 @@
-
+
@@ -42433,11 +42457,10 @@
-
+
-
@@ -42544,10 +42567,11 @@
-
+
+
@@ -42555,7 +42579,7 @@
-
+
@@ -42580,10 +42604,8 @@
-
-
+
-
@@ -42598,9 +42620,8 @@
-
+
-
@@ -42631,7 +42652,7 @@
-
+
@@ -42644,6 +42665,7 @@
+
@@ -42656,7 +42678,7 @@
-
+
@@ -42676,6 +42698,7 @@
+
@@ -42697,7 +42720,7 @@
-
+
@@ -42707,7 +42730,7 @@
-
+
@@ -42720,7 +42743,6 @@
-
@@ -42752,8 +42774,7 @@
-
-
+
@@ -42763,6 +42784,7 @@
+
@@ -42776,7 +42798,7 @@
-
+
@@ -42797,8 +42819,7 @@
-
-
+
@@ -42810,10 +42831,10 @@
-
+
+
-
@@ -42824,6 +42845,7 @@
+
@@ -42847,13 +42869,13 @@
-
+
+
-
@@ -42878,6 +42900,7 @@
+
@@ -42891,6 +42914,7 @@
+
@@ -42918,6 +42942,7 @@
+
@@ -42926,6 +42951,7 @@
+
@@ -42939,11 +42965,12 @@
-
+
+
@@ -42953,7 +42980,6 @@
-
@@ -42961,10 +42987,11 @@
-
+
+
@@ -42976,6 +43003,7 @@
+
@@ -42984,7 +43012,6 @@
-
@@ -43028,7 +43055,8 @@
-
+
+
@@ -43040,7 +43068,7 @@
-
+
@@ -43061,7 +43089,7 @@
-
+
@@ -43108,7 +43136,7 @@
-
+
@@ -43129,7 +43157,6 @@
-
@@ -43155,7 +43182,6 @@
-
@@ -43172,6 +43198,7 @@
+
@@ -43181,7 +43208,6 @@
-
@@ -43237,7 +43263,6 @@
-
@@ -43245,6 +43270,7 @@
+
@@ -43265,23 +43291,22 @@
-
+
-
+
-
-
+
@@ -43312,7 +43337,6 @@
-
@@ -43337,6 +43361,7 @@
+
@@ -43344,6 +43369,7 @@
+
@@ -43369,7 +43395,6 @@
-
@@ -43381,7 +43406,7 @@
-
+
@@ -43391,28 +43416,30 @@
+
-
+
-
+
-
+
-
+
+
@@ -43423,7 +43450,7 @@
-
+
@@ -43442,16 +43469,16 @@
-
+
-
+
@@ -43469,6 +43496,7 @@
+
@@ -43476,7 +43504,7 @@
-
+
@@ -43490,9 +43518,9 @@
+
-
@@ -43500,6 +43528,7 @@
+
@@ -43539,6 +43568,7 @@
+
@@ -43554,8 +43584,9 @@
-
+
+
@@ -43573,7 +43604,6 @@
-
@@ -43587,9 +43617,9 @@
-
+
@@ -43601,7 +43631,7 @@
-
+
@@ -43612,7 +43642,6 @@
-
@@ -43627,11 +43656,11 @@
+
-
@@ -43641,7 +43670,6 @@
-
@@ -43650,7 +43678,6 @@
-
@@ -43659,7 +43686,7 @@
-
+
@@ -43678,6 +43705,7 @@
+
@@ -43702,7 +43730,7 @@
-
+
@@ -43715,15 +43743,15 @@
-
+
-
+
@@ -43744,7 +43772,6 @@
-
@@ -43758,29 +43785,29 @@
-
+
-
+
-
+
-
+
-
+
@@ -43838,12 +43865,12 @@
-
+
-
+
@@ -43874,12 +43901,10 @@
-
-
@@ -43891,13 +43916,11 @@
-
-
@@ -43906,6 +43929,7 @@
+
@@ -43929,6 +43953,7 @@
+
@@ -43972,7 +43997,7 @@
-
+
@@ -43983,6 +44008,7 @@
+
@@ -43995,15 +44021,15 @@
-
+
-
+
@@ -44021,8 +44047,7 @@
-
-
+
@@ -44030,7 +44055,7 @@
-
+
@@ -44047,7 +44072,6 @@
-
@@ -44057,11 +44081,12 @@
+
-
+
-
+
@@ -44090,7 +44115,7 @@
-
+
@@ -44120,9 +44145,8 @@
-
-
+
@@ -44134,6 +44158,7 @@
+
@@ -44158,6 +44183,7 @@
+
@@ -44183,7 +44209,6 @@
-
@@ -44203,7 +44228,7 @@
-
+
@@ -44219,6 +44244,7 @@
+
@@ -44226,20 +44252,17 @@
-
-
-
@@ -44253,14 +44276,15 @@
+
-
+
-
+
@@ -44280,7 +44304,6 @@
-
@@ -44307,7 +44330,6 @@
-
@@ -44333,6 +44355,7 @@
+
@@ -44370,7 +44393,7 @@
-
+
@@ -44393,14 +44416,14 @@
-
+
-
+
@@ -44414,14 +44437,14 @@
-
-
+
+
@@ -44439,8 +44462,9 @@
-
+
+
@@ -44451,27 +44475,24 @@
-
-
-
+
-
+
-
@@ -44480,7 +44501,6 @@
-
@@ -44498,7 +44518,7 @@
-
+
@@ -44528,7 +44548,7 @@
-
+
@@ -44551,6 +44571,7 @@
+
@@ -44608,7 +44629,7 @@
-
+
@@ -44619,7 +44640,7 @@
-
+
@@ -44659,6 +44680,7 @@
+
@@ -44694,7 +44716,7 @@
-
+
@@ -44712,7 +44734,8 @@
-
+
+
@@ -44767,12 +44790,12 @@
-
+
-
+
@@ -44787,7 +44810,6 @@
-
@@ -44795,13 +44817,12 @@
-
+
-
@@ -44809,11 +44830,11 @@
-
+
-
+
@@ -44834,10 +44855,10 @@
+
-
@@ -44873,9 +44894,9 @@
+
-
@@ -44902,23 +44923,26 @@
+
+
+
-
+
-
+
@@ -44927,11 +44951,12 @@
+
+
-
@@ -44944,11 +44969,12 @@
-
-
+
+
+
@@ -44958,18 +44984,19 @@
+
-
-
+
+
-
+
@@ -44977,7 +45004,6 @@
-
@@ -44994,7 +45020,6 @@
-
@@ -45004,15 +45029,14 @@
-
+
-
@@ -45034,7 +45058,6 @@
-
@@ -45057,7 +45080,8 @@
-
+
+
@@ -45074,7 +45098,7 @@
-
+
@@ -45107,7 +45131,7 @@
-
+
@@ -45120,7 +45144,6 @@
-
@@ -45131,7 +45154,7 @@
-
+
@@ -45141,12 +45164,12 @@
-
+
@@ -45174,6 +45197,7 @@
+
@@ -45215,9 +45239,8 @@
-
-
+
@@ -45304,6 +45327,7 @@
+
@@ -45313,6 +45337,7 @@
+
@@ -45328,8 +45353,9 @@
-
+
+
@@ -45359,7 +45385,6 @@
-
@@ -45368,6 +45393,7 @@
+
@@ -45375,7 +45401,6 @@
-
@@ -45385,7 +45410,7 @@
-
+
@@ -45398,7 +45423,6 @@
-
@@ -45408,8 +45432,7 @@
-
-
+
@@ -45433,7 +45456,6 @@
-
@@ -45449,11 +45471,12 @@
-
+
+
@@ -45494,7 +45517,6 @@
-
@@ -45521,7 +45543,8 @@
-
+
+
@@ -45558,6 +45581,7 @@
+
@@ -45586,16 +45610,14 @@
-
+
-
-
@@ -45608,24 +45630,24 @@
-
+
-
-
+
+
@@ -45648,6 +45670,7 @@
+
@@ -45667,7 +45690,6 @@
-
@@ -45692,7 +45714,7 @@
-
+
@@ -45700,7 +45722,6 @@
-
@@ -45749,7 +45770,7 @@
-
+
@@ -45775,6 +45796,7 @@
+
@@ -45782,7 +45804,7 @@
-
+
@@ -45790,6 +45812,7 @@
+
@@ -45797,7 +45820,7 @@
-
+
@@ -45879,12 +45902,14 @@
+
+
@@ -45897,7 +45922,7 @@
-
+
@@ -45905,7 +45930,7 @@
-
+
@@ -45935,7 +45960,6 @@
-
@@ -45964,10 +45988,9 @@
-
-
+
@@ -45986,6 +46009,7 @@
+
@@ -45995,8 +46019,8 @@
+
-
@@ -46004,7 +46028,6 @@
-
@@ -46024,9 +46047,9 @@
-
+
-
+
@@ -46042,7 +46065,6 @@
-
@@ -46077,17 +46099,16 @@
-
+
-
-
+
@@ -46102,13 +46123,13 @@
-
+
@@ -46138,7 +46159,7 @@
-
+
@@ -46154,10 +46175,9 @@
-
-
+
@@ -46177,13 +46197,16 @@
+
+
+
@@ -46207,14 +46230,14 @@
-
+
-
+
@@ -46223,12 +46246,13 @@
+
-
+
@@ -46241,7 +46265,7 @@
-
+
@@ -46291,13 +46315,14 @@
-
+
+
@@ -46316,7 +46341,6 @@
-
@@ -46327,12 +46351,11 @@
-
+
-
@@ -46341,7 +46364,7 @@
-
+
@@ -46356,9 +46379,10 @@
+
-
+
@@ -46371,7 +46395,6 @@
-
@@ -46382,7 +46405,7 @@
-
+
@@ -46402,7 +46425,9 @@
+
+
@@ -46437,6 +46462,7 @@
+
@@ -46449,9 +46475,10 @@
+
-
-
+
+
@@ -46464,6 +46491,7 @@
+
@@ -46488,7 +46516,6 @@
-
@@ -46497,7 +46524,7 @@
-
+
@@ -46508,15 +46535,14 @@
-
-
+
-
+
@@ -46526,6 +46552,7 @@
+
@@ -46558,7 +46585,7 @@
-
+
@@ -46575,8 +46602,6 @@
-
-
@@ -46602,13 +46627,13 @@
-
+
@@ -46623,7 +46648,7 @@
-
+
@@ -46656,6 +46681,7 @@
+
@@ -46679,7 +46705,6 @@
-
@@ -46687,12 +46712,10 @@
-
-
@@ -46708,6 +46731,7 @@
+
@@ -46719,7 +46743,7 @@
-
+
@@ -46734,13 +46758,14 @@
+
+
-
-
+
@@ -46776,13 +46801,13 @@
-
+
-
+
+
-
@@ -46806,6 +46831,7 @@
+
@@ -46822,13 +46848,12 @@
-
-
+
+
-
@@ -46851,11 +46876,11 @@
-
+
@@ -46900,6 +46925,7 @@
+
@@ -46945,7 +46971,7 @@
-
+
@@ -46968,11 +46994,10 @@
-
-
+
@@ -46985,7 +47010,6 @@
-
@@ -46996,27 +47020,26 @@
-
+
-
+
-
+
-
-
+
@@ -47034,19 +47057,20 @@
+
-
-
+
+
-
+
@@ -47057,9 +47081,7 @@
-
-
@@ -47072,6 +47094,7 @@
+
@@ -47085,6 +47108,7 @@
+
@@ -47100,14 +47124,14 @@
-
+
-
+
@@ -47118,7 +47142,7 @@
-
+
@@ -47147,6 +47171,7 @@
+
@@ -47182,10 +47207,8 @@
-
-
@@ -47193,7 +47216,7 @@
-
+
@@ -47230,7 +47253,6 @@
-
@@ -47260,7 +47282,6 @@
-
@@ -47268,10 +47289,9 @@
-
+
-
-
+
@@ -47280,6 +47300,7 @@
+
@@ -47288,6 +47309,7 @@
+
@@ -47306,7 +47328,7 @@
-
+
@@ -47332,7 +47354,7 @@
-
+
@@ -47340,8 +47362,9 @@
-
+
+
@@ -47352,6 +47375,7 @@
+
@@ -47388,24 +47412,24 @@
-
+
-
+
-
+
-
+
@@ -47421,6 +47445,7 @@
+
@@ -47429,6 +47454,7 @@
+
@@ -47472,7 +47498,6 @@
-
@@ -47483,7 +47508,7 @@
-
+
@@ -47505,7 +47530,6 @@
-
@@ -47523,7 +47547,7 @@
-
+
@@ -47563,7 +47587,6 @@
-
@@ -47576,14 +47599,12 @@
-
-
-
+
@@ -47607,6 +47628,7 @@
+
@@ -47623,12 +47645,13 @@
-
+
+
@@ -47637,7 +47660,7 @@
-
+
@@ -47688,6 +47711,7 @@
+
@@ -47695,7 +47719,6 @@
-
@@ -47703,6 +47726,8 @@
+
+
@@ -47716,7 +47741,6 @@
-
@@ -47728,6 +47752,7 @@
+
@@ -47772,13 +47797,14 @@
+
-
+
-
-
+
+
@@ -47814,7 +47840,6 @@
-
@@ -47829,7 +47854,6 @@
-
@@ -47837,12 +47861,13 @@
-
+
+
+
-
@@ -47858,22 +47883,22 @@
-
-
+
-
+
+
-
+
@@ -47885,7 +47910,9 @@
-
+
+
+
@@ -47927,7 +47954,7 @@
-
+
@@ -47960,7 +47987,6 @@
-
@@ -47969,7 +47995,6 @@
-
@@ -48008,6 +48033,7 @@
+
@@ -48043,7 +48069,7 @@
-
+
@@ -48073,8 +48099,8 @@
-
-
+
+
@@ -48124,7 +48150,6 @@
-
@@ -48135,6 +48160,7 @@
+
@@ -48167,7 +48193,7 @@
-
+
@@ -48183,7 +48209,7 @@
-
+
@@ -48226,7 +48252,6 @@
-
@@ -48239,7 +48264,6 @@
-
@@ -48293,7 +48317,6 @@
-
@@ -48310,7 +48333,6 @@
-
@@ -48323,10 +48345,9 @@
-
+
-
@@ -48353,7 +48374,6 @@
-
@@ -48398,15 +48418,15 @@
-
+
+
-
@@ -48436,12 +48456,11 @@
-
+
-
@@ -48454,16 +48473,15 @@
-
+
-
@@ -48476,7 +48494,6 @@
-
@@ -48496,19 +48513,18 @@
-
-
+
+
-
@@ -48518,8 +48534,7 @@
-
-
+
@@ -48541,8 +48556,8 @@
-
-
+
+
@@ -48551,15 +48566,14 @@
-
-
+
-
+
@@ -48581,12 +48595,12 @@
-
-
+
+
@@ -48599,12 +48613,11 @@
-
+
-
@@ -48625,13 +48638,14 @@
+
-
+
@@ -48656,7 +48670,7 @@
-
+
@@ -48664,6 +48678,7 @@
+
@@ -48678,9 +48693,9 @@
-
+
@@ -48696,11 +48711,11 @@
-
-
+
+
-
+
@@ -48710,11 +48725,12 @@
-
+
+
@@ -48729,7 +48745,7 @@
-
+
@@ -48741,12 +48757,12 @@
-
+
@@ -48759,15 +48775,14 @@
-
+
-
-
+
@@ -48817,10 +48832,11 @@
-
+
+
@@ -48831,7 +48847,7 @@
-
+
@@ -48845,7 +48861,9 @@
-
+
+
+
@@ -48860,7 +48878,6 @@
-
@@ -48879,7 +48896,7 @@
-
+
@@ -48891,7 +48908,6 @@
-
@@ -48900,6 +48916,7 @@
+
@@ -48945,7 +48962,7 @@
-
+
@@ -48969,6 +48986,7 @@
+
@@ -48988,7 +49006,7 @@
-
+
@@ -48998,31 +49016,29 @@
-
+
-
-
+
-
-
+
@@ -49063,6 +49079,7 @@
+
@@ -49096,19 +49113,18 @@
-
-
+
-
+
-
+
@@ -49119,10 +49135,11 @@
+
-
+
@@ -49137,6 +49154,7 @@
+
@@ -49153,26 +49171,26 @@
-
+
-
+
-
+
-
+
-
+
@@ -49194,7 +49212,6 @@
-
@@ -49231,15 +49248,14 @@
-
-
+
@@ -49270,13 +49286,14 @@
-
+
+
@@ -49289,6 +49306,7 @@
+
@@ -49299,7 +49317,7 @@
-
+
@@ -49311,7 +49329,6 @@
-
@@ -49329,7 +49346,6 @@
-
@@ -49362,11 +49378,11 @@
-
+
-
+
@@ -49394,8 +49410,9 @@
+
-
+
@@ -49406,7 +49423,7 @@
-
+
@@ -49418,7 +49435,7 @@
-
+
@@ -49456,6 +49473,7 @@
+
@@ -49467,7 +49485,6 @@
-
@@ -49487,7 +49504,7 @@
-
+
@@ -49517,17 +49534,18 @@
+
-
+
-
+
@@ -49543,7 +49561,7 @@
-
+
@@ -49554,7 +49572,7 @@
-
+
@@ -49586,10 +49604,9 @@
-
-
+
@@ -49599,13 +49616,11 @@
-
-
@@ -49626,7 +49641,7 @@
-
+
@@ -49652,7 +49667,7 @@
-
+
@@ -49663,7 +49678,6 @@
-
@@ -49703,11 +49717,11 @@
-
-
+
+
-
+
@@ -49726,7 +49740,7 @@
-
+
@@ -49770,8 +49784,8 @@
-
+
@@ -49785,12 +49799,13 @@
-
+
+
@@ -49801,7 +49816,6 @@
-
@@ -49815,7 +49829,7 @@
-
+
@@ -49834,9 +49848,7 @@
-
-
@@ -49876,7 +49888,6 @@
-
@@ -49900,7 +49911,7 @@
-
+
@@ -49931,7 +49942,7 @@
-
+
@@ -49945,7 +49956,7 @@
-
+
@@ -49955,6 +49966,7 @@
+
@@ -49970,7 +49982,6 @@
-
@@ -49978,8 +49989,10 @@
+
+
@@ -49987,7 +50000,7 @@
-
+
@@ -50039,7 +50052,9 @@
+
+
@@ -50047,6 +50062,7 @@
+
@@ -50054,7 +50070,7 @@
-
+
@@ -50065,7 +50081,7 @@
-
+
@@ -50108,6 +50124,7 @@
+
@@ -50125,7 +50142,7 @@
-
+
@@ -50137,6 +50154,7 @@
+
@@ -50154,7 +50172,6 @@
-
@@ -50178,7 +50195,7 @@
-
+
@@ -50191,7 +50208,6 @@
-
@@ -50219,7 +50235,6 @@
-
@@ -50227,6 +50242,7 @@
+
@@ -50250,12 +50266,12 @@
-
+
-
+
@@ -50264,11 +50280,10 @@
-
-
+
@@ -50289,7 +50304,6 @@
-
@@ -50323,10 +50337,12 @@
+
+
@@ -50350,13 +50366,12 @@
-
+
-
@@ -50367,7 +50382,6 @@
-
@@ -50375,13 +50389,12 @@
-
-
+
@@ -50393,8 +50406,9 @@
+
-
+
@@ -50417,6 +50431,7 @@
+
@@ -50440,7 +50455,6 @@
-
@@ -50448,6 +50462,7 @@
+
@@ -50459,11 +50474,9 @@
-
-
@@ -50492,13 +50505,12 @@
-
-
-
+
+
-
+
@@ -50512,7 +50524,6 @@
-
@@ -50538,6 +50549,7 @@
+
@@ -50554,7 +50566,7 @@
-
+
@@ -50570,6 +50582,7 @@
+
@@ -50590,10 +50603,12 @@
+
+
@@ -50601,6 +50616,7 @@
+
@@ -50626,7 +50642,7 @@
-
+
@@ -50644,7 +50660,7 @@
-
+
@@ -50685,6 +50701,7 @@
+
@@ -50692,7 +50709,6 @@
-
@@ -50708,25 +50724,25 @@
-
+
+
-
+
-
+
-
@@ -50748,7 +50764,6 @@
-
@@ -50764,7 +50779,6 @@
-
@@ -50791,9 +50805,8 @@
-
-
+
@@ -50811,7 +50824,7 @@
-
+
@@ -50824,21 +50837,18 @@
-
-
+
-
-
@@ -50846,6 +50856,7 @@
+
@@ -50860,6 +50871,7 @@
+
@@ -50881,13 +50893,13 @@
+
-
@@ -50908,29 +50920,30 @@
+
+
-
-
+
-
+
@@ -50941,20 +50954,20 @@
+
-
+
-
+
-
@@ -50971,6 +50984,7 @@
+
@@ -50998,14 +51012,13 @@
-
+
+
-
-
@@ -51023,13 +51036,14 @@
+
-
+
@@ -51040,7 +51054,7 @@
-
+
@@ -51059,6 +51073,7 @@
+
@@ -51068,20 +51083,19 @@
-
-
+
-
+
@@ -51093,7 +51107,7 @@
-
+
@@ -51108,7 +51122,7 @@
-
+
@@ -51117,6 +51131,7 @@
+
@@ -51127,6 +51142,7 @@
+
@@ -51134,7 +51150,7 @@
-
+
@@ -51170,7 +51186,7 @@
-
+
@@ -51202,11 +51218,11 @@
-
+
-
+
@@ -51258,6 +51274,7 @@
+
@@ -51277,8 +51294,9 @@
+
-
+
@@ -51332,7 +51350,6 @@
-
@@ -51365,6 +51382,7 @@
+
@@ -51374,22 +51392,22 @@
+
-
-
+
-
+
-
+
@@ -51405,6 +51423,7 @@
+
@@ -51420,7 +51439,7 @@
-
+
@@ -51432,10 +51451,11 @@
+
-
+
@@ -51457,7 +51477,7 @@
-
+
@@ -51480,7 +51500,7 @@
-
+
@@ -51527,24 +51547,24 @@
-
-
+
-
+
+
@@ -51558,6 +51578,7 @@
+
@@ -51579,13 +51600,13 @@
-
+
-
+
@@ -51618,7 +51639,8 @@
-
+
+
@@ -51630,6 +51652,7 @@
+
@@ -51648,7 +51671,7 @@
-
+
@@ -51659,7 +51682,6 @@
-
@@ -51668,7 +51690,6 @@
-
@@ -51679,11 +51700,10 @@
-
-
+
@@ -51712,16 +51732,17 @@
-
+
-
+
-
+
+
@@ -51737,9 +51758,9 @@
-
+
-
+
@@ -51770,11 +51791,10 @@
-
+
-
@@ -51800,6 +51820,7 @@
+
@@ -51835,19 +51856,19 @@
-
+
-
+
@@ -51861,7 +51882,6 @@
-
@@ -51885,7 +51905,6 @@
-
@@ -51915,17 +51934,17 @@
-
+
+
-
@@ -51954,7 +51973,7 @@
-
+
@@ -51967,6 +51986,7 @@
+
@@ -51982,17 +52002,17 @@
-
-
+
+
@@ -52009,7 +52029,6 @@
-
@@ -52025,12 +52044,14 @@
+
+
@@ -52042,7 +52063,7 @@
-
+
@@ -52053,7 +52074,7 @@
-
+
@@ -52067,6 +52088,7 @@
+
@@ -52097,7 +52119,7 @@
-
+
@@ -52114,6 +52136,7 @@
+
@@ -52149,7 +52172,7 @@
-
+
@@ -52165,14 +52188,14 @@
+
-
+
-
@@ -52183,7 +52206,7 @@
-
+
@@ -52193,7 +52216,6 @@
-
@@ -52207,7 +52229,7 @@
-
+
@@ -52215,7 +52237,6 @@
-
@@ -52223,7 +52244,6 @@
-
@@ -52233,6 +52253,7 @@
+
@@ -52253,18 +52274,18 @@
-
+
-
+
@@ -52304,9 +52325,8 @@
-
-
+
@@ -52319,9 +52339,10 @@
-
+
+
@@ -52339,6 +52360,7 @@
+
@@ -52356,7 +52378,6 @@
-
@@ -52377,7 +52398,6 @@
-
@@ -52427,11 +52447,13 @@
+
+
@@ -52443,7 +52465,6 @@
-
@@ -52456,7 +52477,6 @@
-
@@ -52464,6 +52484,7 @@
+
@@ -52484,6 +52505,7 @@
+
@@ -52492,17 +52514,16 @@
-
+
-
+
-
@@ -52520,6 +52541,7 @@
+
@@ -52532,7 +52554,7 @@
-
+
@@ -52542,12 +52564,12 @@
-
+
-
+
@@ -52605,6 +52627,7 @@
+
@@ -52617,7 +52640,7 @@
-
+
@@ -52642,9 +52665,10 @@
-
+
+
@@ -52662,15 +52686,14 @@
-
+
-
+
-
@@ -52682,6 +52705,7 @@
+
@@ -52713,6 +52737,7 @@
+
@@ -52723,10 +52748,9 @@
-
-
+
@@ -52749,14 +52773,14 @@
-
+
-
+
@@ -52775,10 +52799,12 @@
+
+
@@ -52810,7 +52836,6 @@
-
@@ -52862,6 +52887,7 @@
+
@@ -52869,6 +52895,7 @@
+
@@ -52904,8 +52931,9 @@
+
-
+
@@ -52933,6 +52961,7 @@
+
@@ -52978,6 +53007,7 @@
+
@@ -52994,8 +53024,7 @@
-
-
+
@@ -53013,7 +53042,6 @@
-
@@ -53023,6 +53051,7 @@
+
@@ -53043,19 +53072,17 @@
-
-
-
+
-
+
@@ -53074,12 +53101,11 @@
-
+
-
@@ -53095,19 +53121,19 @@
-
-
+
+
-
+
@@ -53134,10 +53160,10 @@
-
-
+
+
@@ -53152,6 +53178,7 @@
+
@@ -53169,7 +53196,7 @@
-
+
@@ -53181,7 +53208,8 @@
-
+
+
@@ -53189,9 +53217,9 @@
-
+
-
+
@@ -53200,14 +53228,12 @@
-
-
-
+
@@ -53217,13 +53243,14 @@
+
-
+
@@ -53243,16 +53270,18 @@
-
+
+
+
@@ -53262,10 +53291,11 @@
-
+
+
@@ -53309,10 +53339,11 @@
-
-
+
+
+
@@ -53347,9 +53378,9 @@
-
-
+
+
@@ -53375,7 +53406,7 @@
-
+
@@ -53386,8 +53417,8 @@
-
-
+
+
@@ -53404,6 +53435,8 @@
+
+
@@ -53431,8 +53464,6 @@
-
-
@@ -53447,8 +53478,9 @@
+
-
+
@@ -53456,6 +53488,8 @@
+
+
@@ -53465,6 +53499,7 @@
+
@@ -53495,9 +53530,8 @@
-
-
+
@@ -53539,15 +53573,13 @@
-
-
-
+
@@ -53571,22 +53603,22 @@
-
+
+
-
+
-
@@ -53605,6 +53637,7 @@
+
@@ -53618,17 +53651,16 @@
+
-
-
@@ -53637,9 +53669,9 @@
-
+
-
+
@@ -53656,10 +53688,12 @@
+
+
@@ -53670,6 +53704,7 @@
+
@@ -53681,6 +53716,7 @@
+
@@ -53702,13 +53738,12 @@
-
-
+
-
+
@@ -53730,13 +53765,12 @@
-
+
-
@@ -53758,7 +53792,6 @@
-
@@ -53789,19 +53822,20 @@
-
+
-
+
-
+
+
@@ -53826,14 +53860,13 @@
-
+
-
@@ -53842,11 +53875,10 @@
-
+
-
@@ -53861,7 +53893,6 @@
-
@@ -53882,8 +53913,9 @@
-
+
+
@@ -53894,10 +53926,10 @@
-
+
@@ -53916,6 +53948,7 @@
+
@@ -53925,8 +53958,10 @@
+
+
@@ -53935,8 +53970,7 @@
-
-
+
@@ -53982,7 +54016,7 @@
-
+
@@ -53993,9 +54027,8 @@
-
+
-
@@ -54018,12 +54051,10 @@
-
-
@@ -54053,11 +54084,11 @@
-
+
-
+
@@ -54079,12 +54110,11 @@
-
+
-
@@ -54100,7 +54130,7 @@
-
+
@@ -54116,7 +54146,7 @@
-
+
@@ -54125,6 +54155,7 @@
+
@@ -54169,7 +54200,6 @@
-
@@ -54181,20 +54211,17 @@
-
-
-
-
+
@@ -54217,30 +54244,29 @@
+
-
-
-
+
-
+
-
+
@@ -54264,7 +54290,7 @@
-
+
@@ -54281,7 +54307,6 @@
-
@@ -54317,6 +54342,7 @@
+
@@ -54324,7 +54350,7 @@
-
+
@@ -54335,7 +54361,7 @@
-
+
@@ -54366,6 +54392,7 @@
+
@@ -54397,7 +54424,6 @@
-
@@ -54407,6 +54433,7 @@
+
@@ -54417,6 +54444,7 @@
+
@@ -54424,8 +54452,7 @@
-
-
+
@@ -54449,7 +54476,6 @@
-
@@ -54480,13 +54506,14 @@
-
+
+
@@ -54494,6 +54521,7 @@
+
@@ -54509,6 +54537,7 @@
+
@@ -54518,7 +54547,7 @@
-
+
@@ -54528,10 +54557,11 @@
-
+
+
@@ -54554,7 +54584,7 @@
-
+
@@ -54565,6 +54595,7 @@
+
@@ -54575,7 +54606,7 @@
-
+
@@ -54607,6 +54638,7 @@
+
@@ -54634,6 +54666,7 @@
+
@@ -54678,7 +54711,6 @@
-
@@ -54686,7 +54718,9 @@
+
+
@@ -54702,10 +54736,9 @@
-
-
+
@@ -54722,7 +54755,7 @@
-
+
@@ -54739,7 +54772,6 @@
-
@@ -54753,9 +54785,10 @@
+
-
+
@@ -54777,12 +54810,13 @@
-
+
+
@@ -54797,13 +54831,12 @@
-
+
-
@@ -54813,7 +54846,9 @@
+
+
@@ -54822,7 +54857,7 @@
-
+
@@ -54847,7 +54882,6 @@
-
@@ -54895,19 +54929,17 @@
-
-
+
-
@@ -54926,7 +54958,7 @@
-
+
@@ -54934,7 +54966,7 @@
-
+
@@ -54952,13 +54984,13 @@
+
-
@@ -54986,6 +55018,7 @@
+
@@ -55008,6 +55041,7 @@
+
@@ -55020,13 +55054,11 @@
-
+
-
-
-
+
@@ -55036,10 +55068,11 @@
-
+
+
@@ -55051,7 +55084,6 @@
-
@@ -55062,7 +55094,6 @@
-
@@ -55077,7 +55108,7 @@
-
+
@@ -55099,7 +55130,6 @@
-
@@ -55107,6 +55137,7 @@
+
@@ -55123,7 +55154,7 @@
-
+
@@ -55133,11 +55164,11 @@
+
-
@@ -55155,11 +55186,9 @@
-
-
-
+
@@ -55190,9 +55219,10 @@
-
+
-
+
+
@@ -55276,7 +55306,6 @@
-
@@ -55286,7 +55315,7 @@
-
+
@@ -55296,7 +55325,6 @@
-
@@ -55307,6 +55335,7 @@
+
@@ -55319,8 +55348,8 @@
-
-
+
+
@@ -55342,9 +55371,10 @@
+
-
+
@@ -55355,19 +55385,19 @@
-
+
+
-
@@ -55383,8 +55413,9 @@
+
-
+
@@ -55413,8 +55444,8 @@
-
-
+
+
@@ -55438,14 +55469,13 @@
-
-
+
@@ -55488,6 +55518,7 @@
+
@@ -55512,6 +55543,7 @@
+
@@ -55562,7 +55594,7 @@
-
+
@@ -55573,12 +55605,11 @@
-
-
+
@@ -55595,6 +55626,7 @@
+
@@ -55617,13 +55649,12 @@
-
-
+
@@ -55634,14 +55665,14 @@
-
+
+
-
@@ -55651,14 +55682,12 @@
-
-
@@ -55699,10 +55728,9 @@
-
-
+
@@ -55710,7 +55738,7 @@
-
+
@@ -55730,8 +55758,8 @@
+
-
@@ -55776,7 +55804,6 @@
-
@@ -55816,7 +55843,7 @@
-
+
@@ -55858,18 +55885,19 @@
-
+
+
+
-
@@ -55884,7 +55912,7 @@
-
+
@@ -55903,12 +55931,12 @@
+
-
@@ -55924,16 +55952,17 @@
-
-
+
+
-
+
+
@@ -55941,13 +55970,13 @@
+
-
@@ -55956,6 +55985,8 @@
+
+
@@ -55964,7 +55995,6 @@
-
@@ -55990,14 +56020,15 @@
+
-
+
@@ -56022,6 +56053,7 @@
+
@@ -56029,21 +56061,19 @@
-
-
-
+
-
+
@@ -56052,6 +56082,7 @@
+
@@ -56064,7 +56095,6 @@
-
@@ -56072,7 +56102,6 @@
-
@@ -56080,7 +56109,7 @@
-
+
@@ -56101,6 +56130,7 @@
+
@@ -56143,14 +56173,16 @@
-
+
-
+
-
+
+
+
@@ -56161,13 +56193,11 @@
-
-
@@ -56182,14 +56212,14 @@
-
+
-
+
@@ -56202,28 +56232,28 @@
-
-
+
+
+
-
-
+
@@ -56276,11 +56306,12 @@
+
-
+
@@ -56304,7 +56335,7 @@
-
+
@@ -56319,7 +56350,7 @@
-
+
@@ -56337,7 +56368,7 @@
-
+
@@ -56369,7 +56400,7 @@
-
+
@@ -56379,10 +56410,9 @@
-
-
+
@@ -56402,12 +56432,12 @@
-
+
-
+
@@ -56426,7 +56456,7 @@
-
+
@@ -56438,9 +56468,10 @@
-
+
+
@@ -56461,7 +56492,7 @@
-
+
@@ -56474,7 +56505,6 @@
-
@@ -56483,7 +56513,7 @@
-
+
@@ -56496,6 +56526,7 @@
+
@@ -56509,13 +56540,16 @@
+
+
-
+
+
@@ -56524,6 +56558,7 @@
+
@@ -56532,7 +56567,6 @@
-
@@ -56540,14 +56574,14 @@
-
-
+
+
@@ -56567,7 +56601,7 @@
-
+
@@ -56575,8 +56609,8 @@
+
-
@@ -56611,10 +56645,10 @@
-
+
@@ -56622,6 +56656,7 @@
+
@@ -56630,9 +56665,9 @@
+
-
@@ -56652,7 +56687,8 @@
-
+
+
@@ -56668,7 +56704,7 @@
-
+
@@ -56681,9 +56717,10 @@
-
+
+
@@ -56705,7 +56742,6 @@
-
@@ -56714,9 +56750,11 @@
+
-
-
+
+
+
@@ -56724,9 +56762,9 @@
+
-
@@ -56734,7 +56772,7 @@
-
+
@@ -56763,6 +56801,7 @@
+
@@ -56782,8 +56821,8 @@
-
-
+
+
@@ -56796,7 +56835,7 @@
-
+
@@ -56804,7 +56843,7 @@
-
+
@@ -56815,12 +56854,11 @@
-
+
-
@@ -56830,19 +56868,18 @@
-
+
-
-
+
-
+
@@ -56883,8 +56920,6 @@
-
-
@@ -56894,7 +56929,7 @@
-
+
@@ -56910,14 +56945,13 @@
-
+
-
+
-
@@ -56925,7 +56959,7 @@
-
+
@@ -56939,7 +56973,7 @@
-
+
@@ -56966,18 +57000,17 @@
-
+
-
+
-
@@ -56991,7 +57024,7 @@
-
+
@@ -57015,13 +57048,13 @@
-
+
@@ -57033,6 +57066,7 @@
+
@@ -57063,7 +57097,7 @@
-
+
@@ -57079,6 +57113,7 @@
+
@@ -57098,12 +57133,10 @@
-
-
@@ -57121,7 +57154,7 @@
-
+
@@ -57130,6 +57163,7 @@
+
@@ -57183,13 +57217,13 @@
+
-
@@ -57207,15 +57241,15 @@
-
+
+
-
@@ -57234,7 +57268,7 @@
-
+
@@ -57246,6 +57280,7 @@
+
@@ -57255,7 +57290,6 @@
-
@@ -57265,13 +57299,14 @@
+
-
+
@@ -57296,10 +57331,9 @@
-
+
-
@@ -57309,7 +57343,7 @@
-
+
@@ -57357,8 +57391,9 @@
+
-
+
@@ -57366,9 +57401,9 @@
-
+
@@ -57439,13 +57474,14 @@
+
-
+
@@ -57463,7 +57499,6 @@
-
@@ -57478,9 +57513,8 @@
-
+
-
@@ -57488,19 +57522,17 @@
-
-
+
+
-
-
@@ -57510,6 +57542,7 @@
+
@@ -57521,17 +57554,19 @@
-
+
-
+
+
+
@@ -57540,7 +57575,7 @@
-
+
@@ -57556,6 +57591,7 @@
+
@@ -57578,7 +57614,6 @@
-
@@ -57629,17 +57664,17 @@
+
-
+
-
+
-
@@ -57654,9 +57689,12 @@
+
+
+
@@ -57668,6 +57706,7 @@
+
@@ -57680,7 +57719,6 @@
-
@@ -57703,6 +57741,7 @@
+
@@ -57710,7 +57749,6 @@
-
@@ -57719,8 +57757,7 @@
-
-
+
@@ -57735,6 +57772,7 @@
+
@@ -57753,13 +57791,15 @@
-
+
+
+
@@ -57767,7 +57807,7 @@
-
+
@@ -57788,7 +57828,7 @@
-
+
@@ -57806,7 +57846,6 @@
-
@@ -57816,12 +57855,12 @@
-
+
-
+
@@ -57833,7 +57872,7 @@
-
+
@@ -57844,10 +57883,9 @@
-
-
+
-
+
@@ -57876,7 +57914,6 @@
-
@@ -57895,6 +57932,7 @@
+
@@ -57935,6 +57973,7 @@
+
@@ -57943,11 +57982,11 @@
-
+
-
+
@@ -57958,19 +57997,17 @@
-
+
-
-
@@ -58005,10 +58042,10 @@
-
+
-
+
@@ -58053,7 +58090,6 @@
-
@@ -58061,7 +58097,7 @@
-
+
@@ -58069,15 +58105,15 @@
-
+
-
+
@@ -58097,6 +58133,7 @@
+
@@ -58105,10 +58142,10 @@
-
+
@@ -58123,13 +58160,16 @@
+
+
+
@@ -58185,9 +58225,10 @@
-
+
+
@@ -58198,7 +58239,6 @@
-
@@ -58215,18 +58255,19 @@
-
+
+
-
+
-
+
@@ -58239,11 +58280,10 @@
-
+
-
@@ -58257,7 +58297,7 @@
-
+
@@ -58300,7 +58340,7 @@
-
+
@@ -58310,14 +58350,13 @@
-
-
+
-
+
@@ -58327,7 +58366,7 @@
-
+
@@ -58352,7 +58391,6 @@
-
@@ -58373,13 +58411,15 @@
+
+
-
+
@@ -58392,6 +58432,7 @@
+
@@ -58431,16 +58472,16 @@
+
-
-
+
@@ -58454,9 +58495,10 @@
-
+
+
@@ -58464,6 +58506,7 @@
+
@@ -58506,9 +58549,9 @@
+
-
@@ -58518,7 +58561,7 @@
-
+
@@ -58539,7 +58582,6 @@
-
@@ -58548,12 +58590,11 @@
-
+
-
@@ -58574,6 +58615,7 @@
+
@@ -58589,7 +58631,6 @@
-
@@ -58597,13 +58638,14 @@
+
-
+
@@ -58614,12 +58656,12 @@
-
+
-
+
@@ -58635,14 +58677,13 @@
-
-
+
@@ -58666,20 +58707,20 @@
-
+
-
+
-
-
+
+
@@ -58707,7 +58748,6 @@
-
@@ -58749,13 +58789,13 @@
+
-
@@ -58763,7 +58803,7 @@
-
+
@@ -58793,7 +58833,7 @@
-
+
@@ -58803,6 +58843,7 @@
+
@@ -58818,7 +58859,7 @@
-
+
@@ -58829,8 +58870,7 @@
-
-
+
@@ -58909,8 +58949,8 @@
+
-
@@ -58933,6 +58973,7 @@
+
@@ -58947,12 +58988,13 @@
-
+
+
@@ -58960,7 +59002,6 @@
-
@@ -58972,16 +59013,18 @@
-
+
+
-
+
+
-
+
@@ -59037,16 +59080,16 @@
-
+
-
+
-
+
@@ -59098,7 +59141,6 @@
-
@@ -59126,7 +59168,6 @@
-
@@ -59134,7 +59175,7 @@
-
+
@@ -59142,11 +59183,10 @@
-
-
-
+
+
@@ -59168,15 +59208,14 @@
-
-
+
@@ -59215,9 +59254,8 @@
-
+
-
@@ -59245,6 +59283,7 @@
+
@@ -59259,6 +59298,7 @@
+
@@ -59279,6 +59319,7 @@
+
@@ -59291,7 +59332,7 @@
-
+
@@ -59322,7 +59363,6 @@
-
@@ -59335,10 +59375,10 @@
-
+
-
+
@@ -59355,6 +59395,7 @@
+
@@ -59386,9 +59427,8 @@
-
+
-
@@ -59415,6 +59455,8 @@
+
+
@@ -59426,6 +59468,7 @@
+
@@ -59433,7 +59476,6 @@
-
@@ -59483,7 +59525,6 @@
-
@@ -59493,13 +59534,13 @@
-
+
-
+
-
+
@@ -59556,6 +59597,7 @@
+
@@ -59566,6 +59608,7 @@
+
@@ -59582,14 +59625,15 @@
-
+
+
-
+
@@ -59609,7 +59653,6 @@
-
@@ -59658,7 +59701,7 @@
-
+
@@ -59673,9 +59716,10 @@
-
+
+
@@ -59689,14 +59733,12 @@
-
+
-
-
@@ -59709,7 +59751,7 @@
-
+
@@ -59720,8 +59762,9 @@
+
-
+
@@ -59795,16 +59838,14 @@
-
+
-
-
@@ -59823,7 +59864,7 @@
-
+
@@ -59875,7 +59916,6 @@
-
@@ -59889,7 +59929,6 @@
-
@@ -59900,6 +59939,7 @@
+
@@ -59908,7 +59948,7 @@
-
+
@@ -59938,10 +59978,10 @@
-
+
@@ -59950,7 +59990,7 @@
-
+
@@ -59961,16 +60001,16 @@
-
+
+
-
@@ -59992,7 +60032,7 @@
-
+
@@ -60015,11 +60055,10 @@
-
+
-
@@ -60033,7 +60072,7 @@
-
+
@@ -60060,6 +60099,7 @@
+
@@ -60085,7 +60125,7 @@
-
+
@@ -60104,7 +60144,6 @@
-
@@ -60113,7 +60152,6 @@
-
@@ -60126,7 +60164,6 @@
-
@@ -60144,7 +60181,7 @@
-
+
@@ -60189,8 +60226,8 @@
-
-
+
+
@@ -60213,18 +60250,19 @@
-
+
+
-
+
-
+
@@ -60239,7 +60277,6 @@
-
@@ -60247,6 +60284,7 @@
+
@@ -60280,6 +60318,7 @@
+
@@ -60287,7 +60326,6 @@
-
@@ -60314,7 +60352,7 @@
-
+
@@ -60324,7 +60362,6 @@
-
@@ -60356,17 +60393,15 @@
-
-
+
-
@@ -60379,11 +60414,13 @@
+
+
@@ -60391,7 +60428,6 @@
-
@@ -60417,6 +60453,7 @@
+
@@ -60435,7 +60472,7 @@
-
+
@@ -60476,7 +60513,7 @@
-
+
@@ -60485,7 +60522,6 @@
-
@@ -60499,7 +60535,7 @@
-
+
@@ -60516,11 +60552,11 @@
-
-
+
+
@@ -60539,7 +60575,6 @@
-
@@ -60556,8 +60591,8 @@
-
-
+
+
@@ -60575,7 +60610,6 @@
-
@@ -60587,7 +60621,6 @@
-
@@ -60597,15 +60630,14 @@
-
+
-
+
-
@@ -60621,7 +60653,6 @@
-
@@ -60658,7 +60689,7 @@
-
+
@@ -60671,7 +60702,6 @@
-
@@ -60693,29 +60723,28 @@
-
-
+
-
-
+
+
@@ -60736,13 +60765,12 @@
-
+
-
-
+
@@ -60757,7 +60785,6 @@
-
@@ -60772,6 +60799,7 @@
+
@@ -60791,9 +60819,11 @@
+
+
@@ -60814,7 +60844,6 @@
-
@@ -60822,7 +60851,7 @@
-
+
@@ -60839,7 +60868,8 @@
-
+
+
@@ -60879,8 +60909,6 @@
-
-
@@ -60929,6 +60957,7 @@
+
@@ -60960,6 +60989,7 @@
+
@@ -60998,9 +61028,9 @@
-
+
@@ -61019,7 +61049,7 @@
-
+
@@ -61046,15 +61076,15 @@
-
-
+
+
-
+
-
+
@@ -61065,14 +61095,13 @@
-
-
+
-
+
@@ -61084,10 +61113,11 @@
+
-
+
@@ -61112,10 +61142,8 @@
-
-
@@ -61125,8 +61153,8 @@
+
-
@@ -61136,7 +61164,6 @@
-
@@ -61151,7 +61178,7 @@
-
+
@@ -61159,6 +61186,7 @@
+
@@ -61178,9 +61206,10 @@
-
+
+
@@ -61209,6 +61238,7 @@
+
@@ -61232,6 +61262,7 @@
+
@@ -61253,7 +61284,6 @@
-
@@ -61296,7 +61326,7 @@
-
+
@@ -61318,6 +61348,7 @@
+
@@ -61336,12 +61367,11 @@
-
+
-
-
+
@@ -61354,10 +61384,9 @@
-
+
-
@@ -61376,7 +61405,6 @@
-
@@ -61390,7 +61418,6 @@
-
@@ -61420,12 +61447,13 @@
+
-
+
@@ -61439,13 +61467,15 @@
-
+
+
+
@@ -61461,18 +61491,20 @@
+
-
+
+
@@ -61489,6 +61521,7 @@
+
@@ -61499,7 +61532,6 @@
-
@@ -61513,6 +61545,7 @@
+
@@ -61533,12 +61566,11 @@
-
+
-
@@ -61546,7 +61578,7 @@
-
+
@@ -61567,7 +61599,7 @@
-
+
@@ -61589,12 +61621,12 @@
+
-
@@ -61606,7 +61638,7 @@
-
+
@@ -61629,14 +61661,15 @@
-
+
+
-
+
@@ -61675,7 +61708,7 @@
-
+
@@ -61685,9 +61718,8 @@
-
-
+
@@ -61701,7 +61733,7 @@
-
+
@@ -61724,7 +61756,6 @@
-
@@ -61744,14 +61775,12 @@
-
-
@@ -61772,7 +61801,6 @@
-
@@ -61783,13 +61811,13 @@
-
+
-
+
@@ -61810,7 +61838,7 @@
-
+
@@ -61831,10 +61859,11 @@
-
+
+
@@ -61843,12 +61872,12 @@
+
-
@@ -61879,7 +61908,6 @@
-
@@ -61889,6 +61917,7 @@
+
@@ -61901,6 +61930,7 @@
+
@@ -61910,9 +61940,8 @@
-
-
+
@@ -61924,8 +61953,10 @@
+
+
@@ -61969,7 +62000,7 @@
-
+
@@ -61985,6 +62016,7 @@
+
@@ -62003,6 +62035,7 @@
+
@@ -62015,6 +62048,7 @@
+
@@ -62034,17 +62068,17 @@
-
+
-
+
@@ -62056,7 +62090,6 @@
-
@@ -62069,7 +62102,7 @@
-
+
@@ -62087,13 +62120,15 @@
+
+
-
+
-
+
@@ -62101,7 +62136,6 @@
-
@@ -62119,7 +62153,6 @@
-
@@ -62140,6 +62173,7 @@
+
@@ -62167,6 +62201,7 @@
+
@@ -62176,7 +62211,7 @@
-
+
@@ -62201,16 +62236,17 @@
-
+
-
+
+
@@ -62220,6 +62256,7 @@
+
@@ -62227,7 +62264,6 @@
-
@@ -62243,7 +62279,6 @@
-
@@ -62251,8 +62286,10 @@
+
+
@@ -62267,7 +62304,9 @@
+
+
@@ -62289,23 +62328,21 @@
-
-
+
-
+
-
-
+
@@ -62325,7 +62362,6 @@
-
@@ -62355,7 +62391,7 @@
-
+
@@ -62365,7 +62401,7 @@
-
+
@@ -62378,14 +62414,13 @@
-
+
-
+
-
@@ -62398,7 +62433,6 @@
-
@@ -62426,7 +62460,6 @@
-
@@ -62460,26 +62493,25 @@
-
+
-
+
-
-
+
@@ -62505,7 +62537,6 @@
-
@@ -62517,6 +62548,7 @@
+
@@ -62533,6 +62565,7 @@
+
@@ -62545,7 +62578,6 @@
-
@@ -62581,9 +62613,9 @@
-
+
@@ -62640,6 +62672,7 @@
+
@@ -62648,11 +62681,12 @@
+
-
+
@@ -62712,7 +62746,7 @@
-
+
@@ -62731,12 +62765,12 @@
-
-
+
+
@@ -62747,16 +62781,15 @@
-
-
+
-
+
@@ -62767,12 +62800,11 @@
-
-
+
@@ -62783,13 +62815,13 @@
-
+
-
+
-
+
@@ -62804,13 +62836,14 @@
-
+
-
+
+
@@ -62852,6 +62885,7 @@
+
@@ -62878,6 +62912,7 @@
+
@@ -62885,6 +62920,7 @@
+
@@ -62908,6 +62944,7 @@
+
@@ -62917,7 +62954,6 @@
-
@@ -62948,7 +62984,6 @@
-
@@ -62959,10 +62994,10 @@
-
+
-
+
@@ -62975,6 +63010,7 @@
+
@@ -62988,6 +63024,7 @@
+
@@ -63003,10 +63040,9 @@
-
-
+
@@ -63015,7 +63051,6 @@
-
@@ -63034,13 +63069,13 @@
-
-
+
+
@@ -63063,11 +63098,9 @@
-
-
@@ -63080,6 +63113,7 @@
+
@@ -63095,7 +63129,7 @@
-
+
@@ -63106,7 +63140,6 @@
-
@@ -63116,8 +63149,7 @@
-
-
+
@@ -63133,7 +63165,6 @@
-
@@ -63157,6 +63188,7 @@
+
@@ -63170,6 +63202,7 @@
+
@@ -63213,7 +63246,6 @@
-
@@ -63226,7 +63258,6 @@
-
@@ -63235,7 +63266,6 @@
-
@@ -63247,7 +63277,7 @@
-
+
@@ -63255,8 +63285,9 @@
-
+
+
@@ -63278,7 +63309,7 @@
-
+
@@ -63293,7 +63324,6 @@
-
@@ -63311,12 +63341,12 @@
+
-
@@ -63338,7 +63368,7 @@
-
+
@@ -63354,6 +63384,7 @@
+
@@ -63361,8 +63392,8 @@
-
-
+
+
@@ -63401,7 +63432,6 @@
-
@@ -63420,6 +63450,7 @@
+
@@ -63436,6 +63467,7 @@
+
@@ -63445,6 +63477,7 @@
+
@@ -63461,10 +63494,9 @@
-
-
-
+
+
@@ -63479,6 +63511,7 @@
+
@@ -63514,6 +63547,7 @@
+
@@ -63533,7 +63567,6 @@
-
@@ -63543,6 +63576,7 @@
+
@@ -63555,13 +63589,14 @@
+
-
+
-
+
@@ -63570,7 +63605,7 @@
-
+
@@ -63579,7 +63614,7 @@
-
+
@@ -63590,7 +63625,7 @@
-
+
@@ -63602,7 +63637,6 @@
-
@@ -63620,7 +63654,7 @@
-
+
@@ -63648,6 +63682,7 @@
+
@@ -63662,7 +63697,7 @@
-
+
@@ -63674,9 +63709,9 @@
-
+
-
+
@@ -63688,8 +63723,9 @@
-
+
+
@@ -63700,7 +63736,7 @@
-
+
@@ -63718,7 +63754,7 @@
-
+
@@ -63733,8 +63769,8 @@
-
+
@@ -63744,10 +63780,9 @@
-
+
-
@@ -63772,7 +63807,6 @@
-
@@ -63788,7 +63822,7 @@
-
+
@@ -63814,14 +63848,14 @@
-
+
-
+
-
+
@@ -63849,10 +63883,10 @@
+
-
@@ -63877,7 +63911,6 @@
-
@@ -63902,13 +63935,13 @@
+
-
@@ -63947,6 +63980,7 @@
+
@@ -63955,10 +63989,11 @@
+
-
+
@@ -63987,10 +64022,10 @@
+
-
@@ -63998,10 +64033,10 @@
-
+
-
+
@@ -64012,7 +64047,6 @@
-
@@ -64029,7 +64063,8 @@
-
+
+
@@ -64042,10 +64077,11 @@
-
+
+
@@ -64070,19 +64106,19 @@
-
+
-
+
@@ -64090,14 +64126,12 @@
-
-
-
+
@@ -64106,11 +64140,11 @@
-
-
+
+
@@ -64128,8 +64162,9 @@
-
+
+
@@ -64155,13 +64190,13 @@
-
+
-
+
@@ -64190,12 +64225,13 @@
+
-
+
@@ -64203,11 +64239,12 @@
+
-
+
@@ -64227,7 +64264,7 @@
-
+
@@ -64247,7 +64284,6 @@
-
@@ -64255,7 +64291,6 @@
-
@@ -64267,6 +64302,7 @@
+
@@ -64280,7 +64316,8 @@
-
+
+
@@ -64288,6 +64325,7 @@
+
@@ -64302,7 +64340,6 @@
-
@@ -64328,7 +64365,7 @@
-
+
@@ -64342,7 +64379,8 @@
-
+
+
@@ -64355,7 +64393,8 @@
-
+
+
@@ -64365,6 +64404,7 @@
+
@@ -64395,16 +64435,16 @@
+
-
+
-
@@ -64449,11 +64489,11 @@
+
-
@@ -64468,10 +64508,11 @@
+
-
-
+
+
@@ -64480,7 +64521,7 @@
-
+
@@ -64513,9 +64554,10 @@
-
+
+
@@ -64532,14 +64574,15 @@
-
+
+
-
+
@@ -64556,7 +64599,7 @@
-
+
@@ -64566,6 +64609,7 @@
+
@@ -64590,7 +64634,6 @@
-
@@ -64613,6 +64656,7 @@
+
@@ -64620,8 +64664,7 @@
-
-
+
@@ -64641,7 +64684,6 @@
-
@@ -64657,11 +64699,10 @@
-
-
+
@@ -64670,9 +64711,9 @@
+
-
@@ -64697,7 +64738,6 @@
-
@@ -64705,12 +64745,12 @@
+
-
@@ -64724,7 +64764,7 @@
-
+
@@ -64739,7 +64779,7 @@
-
+
@@ -64817,11 +64857,10 @@
-
+
-
@@ -64829,7 +64868,7 @@
-
+
@@ -64837,7 +64876,6 @@
-
@@ -64849,9 +64887,10 @@
+
-
+
@@ -64874,7 +64913,7 @@
-
+
@@ -64891,7 +64930,7 @@
-
+
@@ -64904,7 +64943,6 @@
-
@@ -64923,12 +64961,10 @@
-
-
-
+
@@ -64954,6 +64990,7 @@
+
@@ -64965,10 +65002,9 @@
-
+
-
@@ -64981,17 +65017,19 @@
+
+
-
+
@@ -65000,12 +65038,11 @@
-
-
+
@@ -65031,7 +65068,7 @@
-
+
@@ -65060,10 +65097,12 @@
+
-
+
+
@@ -65074,7 +65113,6 @@
-
@@ -65084,7 +65122,8 @@
-
+
+
@@ -65105,13 +65144,10 @@
-
-
-
@@ -65121,14 +65157,15 @@
-
+
+
@@ -65147,6 +65184,7 @@
+
@@ -65166,23 +65204,23 @@
-
+
+
-
+
-
@@ -65192,17 +65230,18 @@
-
+
+
+
-
@@ -65214,7 +65253,7 @@
-
+
@@ -65235,7 +65274,6 @@
-
@@ -65245,19 +65283,19 @@
-
+
-
+
-
+
@@ -65278,7 +65316,7 @@
-
+
@@ -65289,7 +65327,8 @@
-
+
+
@@ -65307,7 +65346,7 @@
-
+
@@ -65316,21 +65355,21 @@
-
-
+
+
-
+
@@ -65351,10 +65390,9 @@
-
+
-
@@ -65379,11 +65417,10 @@
-
-
+
@@ -65406,7 +65443,7 @@
-
+
@@ -65420,6 +65457,7 @@
+
@@ -65441,7 +65479,7 @@
-
+
@@ -65461,7 +65499,7 @@
-
+
@@ -65472,6 +65510,7 @@
+
@@ -65500,8 +65539,9 @@
+
-
+
@@ -65519,7 +65559,7 @@
-
+
@@ -65565,7 +65605,7 @@
-
+
@@ -65581,6 +65621,7 @@
+
@@ -65599,18 +65640,20 @@
-
+
+
-
+
+
@@ -65625,7 +65668,6 @@
-
@@ -65636,9 +65678,10 @@
-
+
+
@@ -65649,6 +65692,7 @@
+
@@ -65656,23 +65700,21 @@
-
+
-
+
-
-
@@ -65689,13 +65731,8 @@
-
-
-
-
-
@@ -65708,16 +65745,20 @@
-
+
+
+
+
+
@@ -65729,7 +65770,6 @@
-
@@ -65743,7 +65783,7 @@
-
+
@@ -65759,23 +65799,23 @@
-
+
+
-
-
+
@@ -65794,25 +65834,27 @@
+
-
+
+
+
-
-
+
-
+
@@ -65823,55 +65865,55 @@
-
+
+
-
-
+
-
+
+
+
+
-
-
+
-
+
-
-
-
-
+
+
@@ -65884,11 +65926,16 @@
+
+
-
+
+
+
+
@@ -65897,11 +65944,12 @@
-
+
+
@@ -65910,21 +65958,22 @@
-
+
-
+
+
@@ -65933,6 +65982,7 @@
+
@@ -65943,7 +65993,6 @@
-
@@ -65957,144 +66006,148 @@
-
+
+
+
-
-
+
+
-
-
+
-
+
-
-
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
-
-
-
+
-
-
-
+
+
+
-
+
-
+
+
+
+
+
+
+
+
-
-
+
+
+
@@ -66103,40 +66156,37 @@
-
+
-
-
+
+
+
+
-
+
-
-
-
-
-
@@ -66147,18 +66197,16 @@
-
-
+
-
-
+
@@ -66167,12 +66215,13 @@
-
+
+
-
+
@@ -66181,41 +66230,41 @@
-
+
+
-
-
-
+
-
+
+
@@ -66228,40 +66277,44 @@
+
+
+
-
+
+
+
-
+
-
-
+
@@ -66270,7 +66323,6 @@
-
@@ -66278,17 +66330,14 @@
-
-
-
-
+
@@ -66296,40 +66345,39 @@
+
+
-
+
+
-
-
-
+
+
+
-
-
-
-
@@ -66337,26 +66385,29 @@
+
+
-
+
+
-
+
-
+
@@ -66365,16 +66416,18 @@
+
-
+
-
+
+
@@ -66382,7 +66435,6 @@
-
@@ -66398,21 +66450,29 @@
+
+
+
+
+
+
+
+
-
+
@@ -66429,30 +66489,33 @@
+
-
-
+
+
+
+
-
+
@@ -66461,62 +66524,61 @@
-
+
-
-
+
+
+
-
-
-
+
+
+
-
-
+
-
-
-
+
+
-
+
@@ -66525,32 +66587,28 @@
-
+
-
-
-
-
+
+
-
-
@@ -66560,9 +66618,11 @@
+
+
-
+
@@ -66578,20 +66638,22 @@
-
-
+
+
+
+
+
-
@@ -66600,30 +66662,29 @@
-
-
+
-
-
+
+
+
+
-
-
@@ -66638,13 +66699,11 @@
-
-
@@ -66652,6 +66711,7 @@
+
@@ -66661,6 +66721,7 @@
+
@@ -66668,7 +66729,6 @@
-
@@ -66682,21 +66742,24 @@
-
+
+
+
+
+
-
@@ -66707,30 +66770,31 @@
-
+
+
-
-
+
-
-
+
+
+
@@ -66738,27 +66802,24 @@
+
-
-
+
-
+
-
-
-
@@ -66767,62 +66828,55 @@
-
+
-
-
+
-
-
+
+
-
+
-
-
-
-
-
+
-
-
@@ -66835,25 +66889,23 @@
+
-
-
+
-
-
@@ -66866,15 +66918,12 @@
-
-
-
-
+
@@ -66883,25 +66932,21 @@
-
-
-
+
-
-
+
-
@@ -66916,6 +66961,9 @@
+
+
+
@@ -66931,30 +66979,34 @@
+
-
+
+
+
-
+
+
+
-
-
+
-
+
@@ -66962,13 +67014,11 @@
-
-
-
+
@@ -66985,11 +67035,11 @@
+
-
@@ -67000,15 +67050,13 @@
-
-
+
-
@@ -67016,22 +67064,26 @@
-
-
+
+
-
+
+
+
+
+
@@ -67039,22 +67091,17 @@
-
-
+
+
-
-
-
-
-
@@ -67064,15 +67111,15 @@
-
+
-
+
@@ -67080,38 +67127,31 @@
+
-
+
-
+
-
-
-
-
-
-
-
-
@@ -67119,16 +67159,15 @@
-
-
+
@@ -67139,67 +67178,61 @@
-
-
-
+
+
+
-
-
-
+
-
+
-
-
-
-
-
+
+
-
-
+
@@ -67207,17 +67240,15 @@
-
-
-
-
+
+
@@ -67234,47 +67265,48 @@
+
-
+
-
+
+
+
-
+
-
-
-
+
+
-
+
-
-
+
+
-
@@ -67286,7 +67318,6 @@
-
@@ -67294,60 +67325,67 @@
+
+
+
+
-
+
+
+
+
+
-
-
-
-
-
+
+
-
+
+
+
@@ -67355,10 +67393,12 @@
+
+
@@ -67367,17 +67407,15 @@
+
-
+
-
-
-
@@ -67390,46 +67428,48 @@
+
+
+
+
-
-
-
+
+
+
-
-
-
+
@@ -67440,17 +67480,18 @@
+
-
+
+
-
@@ -67459,14 +67500,14 @@
+
+
-
-
@@ -67490,14 +67531,12 @@
-
-
@@ -67511,17 +67550,14 @@
+
-
-
-
-
@@ -67529,31 +67565,28 @@
-
-
-
+
-
+
+
+
-
-
+
-
-
@@ -67561,21 +67594,18 @@
-
-
-
+
+
+
-
-
+
-
-
+
-
-
+
@@ -67585,6 +67615,8 @@
+
+
@@ -67593,8 +67625,10 @@
+
+
@@ -67604,6 +67638,7 @@
+
@@ -67614,7 +67649,6 @@
-
@@ -67622,48 +67656,49 @@
-
+
-
+
-
-
+
-
+
-
+
+
+
-
+
@@ -67673,14 +67708,14 @@
-
+
+
-
@@ -67689,17 +67724,18 @@
+
-
+
-
+
@@ -67707,30 +67743,35 @@
-
+
+
-
+
+
+
+
+
-
+
-
+
@@ -67741,7 +67782,6 @@
-
@@ -67751,11 +67791,10 @@
+
+
-
-
-
@@ -67768,6 +67807,7 @@
+
@@ -67775,17 +67815,17 @@
-
-
+
+
-
+
-
+
@@ -67793,34 +67833,33 @@
+
-
-
-
-
+
+
+
-
-
+
+
-
-
+
@@ -67832,14 +67871,14 @@
+
-
-
+
@@ -67849,53 +67888,48 @@
+
-
-
-
+
-
-
+
+
+
+
-
-
+
-
+
-
-
-
-
-
@@ -67905,36 +67939,36 @@
-
-
+
-
+
+
+
-
+
-
+
-
@@ -67945,11 +67979,13 @@
+
+
@@ -67958,10 +67994,8 @@
-
-
@@ -67969,39 +68003,45 @@
-
+
+
-
-
+
-
-
+
+
+
+
-
+
-
+
+
+
+
+
+
-
@@ -68012,15 +68052,14 @@
+
-
-
@@ -68028,12 +68067,9 @@
-
-
-
@@ -68046,80 +68082,77 @@
-
+
-
-
+
-
+
+
+
-
-
+
+
+
-
+
+
-
+
-
-
-
-
-
-
+
-
-
+
@@ -68129,31 +68162,27 @@
-
+
-
-
-
-
+
-
@@ -68166,23 +68195,23 @@
+
+
+
-
-
+
-
-
@@ -68190,16 +68219,19 @@
+
+
+
+
-
-
+
@@ -68211,33 +68243,22 @@
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
@@ -68245,14 +68266,15 @@
+
+
+
-
-
@@ -68265,9 +68287,13 @@
+
+
+
+
@@ -68278,55 +68304,60 @@
+
+
-
+
-
-
-
+
+
+
-
+
+
+
+
+
-
-
+
-
+
@@ -68336,44 +68367,41 @@
-
-
-
+
+
-
+
+
+
-
-
-
-
-
+
@@ -68384,12 +68412,14 @@
-
+
+
+
@@ -68403,21 +68433,24 @@
-
+
+
+
-
+
+
@@ -68427,16 +68460,19 @@
+
+
+
@@ -68445,13 +68481,15 @@
-
+
+
+
+
-
@@ -68459,17 +68497,22 @@
-
+
+
+
-
+
+
+
+
@@ -68480,44 +68523,41 @@
-
-
+
+
+
-
+
+
-
+
-
-
-
-
+
+
-
-
-
@@ -68529,19 +68569,20 @@
-
-
+
+
+
diff --git a/Engine/Config/BaseDeviceProfiles.ini b/Engine/Config/BaseDeviceProfiles.ini
index b351f2e65d30..08a82a6bc9be 100644
--- a/Engine/Config/BaseDeviceProfiles.ini
+++ b/Engine/Config/BaseDeviceProfiles.ini
@@ -422,7 +422,7 @@ BaseProfileName=
+CVars=r.RefractionQuality=0
+CVars=r.ShadowQuality=2
+CVars=slate.AbsoluteIndices=1
-+CVars=r.Vulkan.DelayAcquireBackBuffer=1
++CVars=r.Vulkan.DelayAcquireBackBuffer=0
+CVars=r.Vulkan.RobustBufferAccess=1
+CVars=r.Vulkan.DescriptorSetLayoutMode=2
diff --git a/Engine/Config/BaseEngine.ini b/Engine/Config/BaseEngine.ini
index 52153d86b568..458fe5219f64 100644
--- a/Engine/Config/BaseEngine.ini
+++ b/Engine/Config/BaseEngine.ini
@@ -631,7 +631,8 @@ bUseStreamingPause=false
+ClassRedirects=(OldName="LevelSequenceDirectorGeneratedClass",NewName="/Script/Engine.BlueprintGeneratedClass")
; 4.22
-+FunctionRedirects=(OldName="UserWidget.PlayAnimation",NewName="UserWidget.PlayAnimationAtTime")
++FunctionRedirects=(OldName="UserWidget.PlayAnimationTo",NewName="UserWidget.PlayAnimationTimeRange")
++FunctionRedirects=(OldName="UserWidget.PlayAnimationAtTime",NewName="UserWidget.PlayAnimation")
+FunctionRedirects=(OldName="AddChildWrapBox", NewName="AddChildToWrapBox")
; Chaos
@@ -903,6 +904,9 @@ CooloffTime=10
NetConnectionClassName="/Script/Engine.DemoNetConnection"
DemoSpectatorClass=Engine.PlayerController
SpawnPrioritySeconds=60.0
+!ChannelDefinitions=CLEAR_ARRAY
++ChannelDefinitions=(ChannelName=Control, ClassName=/Script/Engine.ControlChannel, StaticChannelIndex=0, bTickOnCreate=true, bServerOpen=false, bClientOpen=true, bInitialServer=false, bInitialClient=true)
++ChannelDefinitions=(ChannelName=Actor, ClassName=/Script/Engine.ActorChannel, StaticChannelIndex=-1, bTickOnCreate=false, bServerOpen=true, bClientOpen=false, bInitialServer=false, bInitialClient=false)
[TextureStreaming]
NeverStreamOutTextures=False
@@ -1797,6 +1801,7 @@ DebugInfoStartY=60.0
[/Script/IOSRuntimeSettings.IOSRuntimeSettings]
bEnableGameCenterSupport=False
bSupportsPortraitOrientation=False
+bSupportsITunesFileSharing=False
bSupportsUpsideDownOrientation=False
bSupportsLandscapeLeftOrientation=True
bSupportsLandscapeRightOrientation=True
@@ -2180,4 +2185,4 @@ LiveLinkPublishingPort=11111
r.GPUCrashDebugging=false
[Messaging]
-bAllowDelayedMessaging=false
\ No newline at end of file
+bAllowDelayedMessaging=false
diff --git a/Engine/Config/BasePakFileRules.ini b/Engine/Config/BasePakFileRules.ini
new file mode 100644
index 000000000000..b86b72085df8
--- /dev/null
+++ b/Engine/Config/BasePakFileRules.ini
@@ -0,0 +1,18 @@
+; These rules are applied in order, the first rule that applies per file is taken and no others are evaluated
+; [SectionName]
+; bOverrideChunkManifest=false ; If true this allows overriding assignments from the cooker
+; bExcludeFromPaks=false ; If true this removes entirely, cannot coexist with overridepaks
+; OverridePaks="pakchunk1" ; If set this will override pak list, comma separated
+; Platforms="iOS,Android" ; If set this rule will only apply to these platforms
+; Targets="Shipping,Test" ; If set this rule will only apply to these configurations
+; +Files=".../*FileMask*.*" ; List of file masks to apply to, using the C# FileFilter class
+
+
+[ExcludeContentForMobile]
+; Exclude specific large textures on mobile platforms, this was moved from CopyBuildToStagingDirectory.cs
+; This can be added to in a game's DefaultPakFileRules.ini by using the same sections, and new sections can be added
+; To remove this rule, use !Files to clear the list of file paths
+Platforms="Android,iOS,tvOS,HTML5"
+bExcludeFromPaks=true
+bOverrideChunkManifest=true
++Files=".../Engine/Content/EngineMaterials/DefaultBloomKernel.*"
diff --git a/Engine/Extras/LLDBDataFormatters/UE4DataFormatters.py b/Engine/Extras/LLDBDataFormatters/UE4DataFormatters.py
index c2bc0fb20fa0..e0c6d182c7b5 100644
--- a/Engine/Extras/LLDBDataFormatters/UE4DataFormatters.py
+++ b/Engine/Extras/LLDBDataFormatters/UE4DataFormatters.py
@@ -59,7 +59,7 @@ def UE4FNameSummaryProvider(valobj,dict):
if not Index.IsValid():
Index = valobj.GetChildMemberWithName('ComparisonIndex')
IndexVal = Index.GetValueAsUnsigned(0)
- if IndexVal >= 1048576:
+ if IndexVal >= 4194304:
return 'name=Invalid'
else:
Expr = '(char*)(((FNameEntry***)GFNameTableForDebuggerVisualizers_MT)['+str(IndexVal)+' / 16384]['+str(IndexVal)+' % 16384])->AnsiName'
@@ -74,7 +74,7 @@ def UE4FNameSummaryProvider(valobj,dict):
def UE4FMinimalNameSummaryProvider(valobj,dict):
Index = valobj.GetChildMemberWithName('Index')
IndexVal = Index.GetValueAsUnsigned(0)
- if IndexVal >= 1048576:
+ if IndexVal >= 4194304:
return 'name=Invalid'
else:
Expr = '(char*)(((FNameEntry***)GFNameTableForDebuggerVisualizers_MT)['+str(IndexVal)+' / 16384]['+str(IndexVal)+' % 16384])->AnsiName'
diff --git a/Engine/Extras/LLDBDataFormatters/UE4DataFormatters_2ByteChars.py b/Engine/Extras/LLDBDataFormatters/UE4DataFormatters_2ByteChars.py
index bb6ea7ed6a43..44a353cbeff9 100644
--- a/Engine/Extras/LLDBDataFormatters/UE4DataFormatters_2ByteChars.py
+++ b/Engine/Extras/LLDBDataFormatters/UE4DataFormatters_2ByteChars.py
@@ -59,7 +59,7 @@ def UE4FNameSummaryProvider(valobj,dict):
if not Index.IsValid():
Index = valobj.GetChildMemberWithName('ComparisonIndex')
IndexVal = Index.GetValueAsUnsigned(0)
- if IndexVal >= 1048576:
+ if IndexVal >= 4194304:
return 'name=Invalid'
else:
Expr = '(char*)(((FNameEntry***)FName::GetNameTableForDebuggerVisualizers_MT())['+str(IndexVal)+' / 16384]['+str(IndexVal)+' % 16384])->AnsiName'
@@ -74,7 +74,7 @@ def UE4FNameSummaryProvider(valobj,dict):
def UE4FMinimalNameSummaryProvider(valobj,dict):
Index = valobj.GetChildMemberWithName('Index')
IndexVal = Index.GetValueAsUnsigned(0)
- if IndexVal >= 1048576:
+ if IndexVal >= 4194304:
return 'name=Invalid'
else:
Expr = '(char*)(((FNameEntry***)FName::GetNameTableForDebuggerVisualizers_MT())['+str(IndexVal)+' / 16384]['+str(IndexVal)+' % 16384])->AnsiName'
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/LICENSE b/Engine/Extras/ThirdPartyNotUE/ios-deploy/LICENSE
new file mode 100644
index 000000000000..f8e017ae89b9
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/LICENSE
@@ -0,0 +1,8 @@
+ios-deploy is available under the provisions of the GNU General Public License,
+version 3 (or later), available here: http://www.gnu.org/licenses/gpl-3.0.html
+
+
+Error codes used for error messages were taken from SDMMobileDevice framework,
+originally reverse engineered by Sam Marshall. SDMMobileDevice is distributed
+under BSD 3-Clause license and is available here:
+https://github.com/samdmarshall/SDMMobileDevice
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/README.md b/Engine/Extras/ThirdPartyNotUE/ios-deploy/README.md
new file mode 100644
index 000000000000..a5d4f2b41f8b
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/README.md
@@ -0,0 +1,142 @@
+[](https://travis-ci.org/phonegap/ios-deploy)
+
+ios-deploy
+==========
+Install and debug iOS apps from the command line. Designed to work on un-jailbroken devices.
+
+## Requirements
+
+* Mac OS X. Tested on 10.11 El Capitan, 10.12 Sierra, iOS 9.0 and iOS 10.0
+* You need to have a valid iOS Development certificate installed.
+* Xcode 7 or greater should be installed (**NOT** Command Line Tools!)
+
+## Roadmap
+
+See our [milestones](https://github.com/phonegap/ios-deploy/milestones).
+
+## Development
+
+The 1.x branch has been archived (renamed for now), all development is to be on the master branch for simplicity, since the planned 2.x development (break out commands into their own files) has been abandoned for now.
+
+## Installation
+=======
+
+ios-deploy installation is made simple using the node.js package manager. If you use [Homebrew](http://brew.sh/), install [node.js](https://nodejs.org):
+
+```
+brew install node
+```
+
+Now install ios-deploy with the [node.js](https://nodejs.org) package manager:
+
+```
+npm install -g ios-deploy
+```
+
+To build from source:
+
+```
+xcodebuild
+```
+
+This will build `ios-deploy` into the `build/Release` folder.
+
+## Testing
+
+Run:
+
+```
+npm install && npm test
+```
+
+### OS X 10.11 El Capitan or greater
+
+If you are *not* using a node version manager like [nvm](https://github.com/creationix/nvm) or [n](https://github.com/tj/n), you may have to do either of these three things below when under El Capitan:
+
+1. Add the `--unsafe-perm=true` flag when installing ios-deploy
+2. Add the `--allow-root` flag when installing ios-deploy
+3. Ensure the `nobody` user has write access to `/usr/local/lib/node_modules/ios-deploy/ios-deploy`
+
+## Usage
+
+ Usage: ios-deploy [OPTION]...
+ -d, --debug launch the app in lldb after installation
+ -i, --id the id of the device to connect to
+ -c, --detect only detect if the device is connected
+ -b, --bundle the path to the app bundle to be installed
+ -a, --args command line arguments to pass to the app when launching it
+ -t, --timeout number of seconds to wait for a device to be connected
+ -u, --unbuffered don't buffer stdout
+ -n, --nostart do not start the app when debugging
+ -I, --noninteractive start in non interactive mode (quit when app crashes or exits)
+ -L, --justlaunch just launch the app and exit lldb
+ -v, --verbose enable verbose output
+ -m, --noinstall directly start debugging without app install (-d not required)
+ -p, --port port used for device, default: dynamic
+ -r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared)
+ -9, --uninstall_only uninstall the app ONLY. Use only with -1
+ -1, --bundle_id specify bundle id for list and upload
+ -l, --list list files
+ -o, --upload upload file
+ -w, --download download app tree
+ -2, --to use together with up/download file/tree. specify target
+ -D, --mkdir make directory on device
+ -R, --rm remove file or directory on device (directories must be empty)
+ -V, --version print the executable version
+ -e, --exists check if the app with given bundle_id is installed or not
+ -B, --list_bundle_id list bundle_id
+ -W, --no-wifi ignore wifi devices
+ --detect_deadlocks start printing backtraces for all threads periodically after specific amount of seconds
+
+## Examples
+
+The commands below assume that you have an app called `my.app` with bundle id `bundle.id`. Substitute where necessary.
+
+ // deploy and debug your app to a connected device
+ ios-deploy --debug --bundle my.app
+
+ // deploy and debug your app to a connected device, skipping any wi-fi connection (use USB)
+ ios-deploy --debug --bundle my.app --no-wifi
+
+ // deploy and launch your app to a connected device, but quit the debugger after
+ ios-deploy --justlaunch --debug --bundle my.app
+
+ // deploy and launch your app to a connected device, quit when app crashes or exits
+ ios-deploy --noninteractive --debug --bundle my.app
+
+ // Upload a file to your app's Documents folder
+ ios-deploy --bundle_id 'bundle.id' --upload test.txt --to Documents/test.txt
+
+ // Download your app's Documents, Library and tmp folders
+ ios-deploy --bundle_id 'bundle.id' --download --to MyDestinationFolder
+
+ // List the contents of your app's Documents, Library and tmp folders
+ ios-deploy --bundle_id 'bundle.id' --list
+
+ // deploy and debug your app to a connected device, uninstall the app first
+ ios-deploy --uninstall --debug --bundle my.app
+
+ // check whether an app by bundle id exists on the device (check return code `echo $?`)
+ ios-deploy --exists --bundle_id com.apple.mobilemail
+
+ // Download the Documents directory of the app *only*
+ ios-deploy --download=/Documents --bundle_id my.app.id --to ./my_download_location
+
+ // List ids and names of connected devices
+ ios-deploy -c
+
+ // Uninstall an app
+ ios-deploy --uninstall_only --bundle_id my.bundle.id
+
+ // list all bundle ids of all apps on your device
+ ios-deploy --list_bundle_id
+
+## Demo
+
+The included demo.app represents the minimum required to get code running on iOS.
+
+* `make demo.app` will generate the demo.app executable. If it doesn't compile, modify `IOS_SDK_VERSION` in the Makefile.
+* `make debug` will install demo.app and launch a LLDB session.
+
+## Notes
+* `--detect_deadlocks` can help to identify an exact state of application's threads in case of a deadlock. It works like this: The user specifies the amount of time ios-deploy runs the app as usual. When the timeout is elapsed ios-deploy starts to print call-stacks of all threads every 5 seconds and the app keeps running. Comparing threads' call-stacks between each other helps to identify the threads which were stuck.
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/.gitignore b/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/.gitignore
new file mode 100644
index 000000000000..c2ef55db28e2
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/.gitignore
@@ -0,0 +1,5 @@
+demo
+demo.app
+demo.dSYM
+/.DS_Store
+*~
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/Makefile b/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/Makefile
new file mode 100644
index 000000000000..319e9f8878ce
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/Makefile
@@ -0,0 +1,24 @@
+IOS_SDK_VERSION = 9.1
+
+IOS_CC = gcc -ObjC
+IOS_SDK = $(shell xcode-select --print-path)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$(IOS_SDK_VERSION).sdk
+
+all: clean demo.app
+
+demo.app: demo Info.plist
+ mkdir -p demo.app
+ cp demo demo.app/
+ cp Info.plist ResourceRules.plist demo.app/
+ codesign -f -s "iPhone Developer" --entitlements Entitlements.plist demo.app
+
+demo: demo.c
+ $(IOS_CC) -g -arch armv7 -isysroot $(IOS_SDK) -framework CoreFoundation -o demo demo.c
+
+debug: all ios-deploy
+ @../build/Release/ios-deploy --debug --bundle demo.app
+
+clean:
+ @rm -rf *.app demo demo.dSYM
+
+ios-deploy:
+ @xcodebuild -project ../ios-deploy.xcodeproj
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/demo.c b/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/demo.c
new file mode 100644
index 000000000000..b366b145ab3b
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/demo/demo.c
@@ -0,0 +1,9 @@
+#include
+
+int main(int argc, const char* argv[]) {
+ int i;
+ for (i = 0; i < argc; i++) {
+ printf("argv[%d] = %s\n", i, argv[i]);
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/ios-deploy.tps b/Engine/Extras/ThirdPartyNotUE/ios-deploy/ios-deploy.tps
new file mode 100644
index 000000000000..031d694d1fe6
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/ios-deploy.tps
@@ -0,0 +1,11 @@
+
+
+ ios-deploy
+ Engine\Extras\ThirdPartyNotUE\ios-deploy
+ Used by Gauntlet automation to deploy and launch apps on iOS devices.
+ https://github.com/ios-control/ios-deploy/blob/master/LICENSE
+
+ P4
+
+ Engine\Extras\ThirdPartyNotUE\ios-deploy\ios-deploy_License.txt
+
\ No newline at end of file
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/ios-deploy_License.txt b/Engine/Extras/ThirdPartyNotUE/ios-deploy/ios-deploy_License.txt
new file mode 100644
index 000000000000..cf2d0fdab1ec
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/ios-deploy_License.txt
@@ -0,0 +1,10 @@
+This work was modified by Epic Games, Inc., 2018.
+
+ios-deploy is available under the provisions of the GNU General Public License,
+version 3 (or later), available here: http://www.gnu.org/licenses/gpl-3.0.html
+
+
+Error codes used for error messages were taken from SDMMobileDevice framework,
+originally reverse engineered by Sam Marshall. SDMMobileDevice is distributed
+under BSD 3-Clause license and is available here:
+https://github.com/samdmarshall/SDMMobileDevice
\ No newline at end of file
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/package.json b/Engine/Extras/ThirdPartyNotUE/ios-deploy/package.json
new file mode 100644
index 000000000000..f3d7b7c79e5e
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "ios-deploy",
+ "version": "1.9.4",
+ "os": [
+ "darwin"
+ ],
+ "description": "launch iOS apps iOS devices from the command line (Xcode 7)",
+ "main": "ios-deploy",
+ "bin": "./build/Release/ios-deploy",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ios-control/ios-deploy"
+ },
+ "devDependencies": {
+ "eslint": "~4.19.1",
+ "eslint-config-semistandard": "^12.0.1",
+ "eslint-config-standard": "^11.0.0",
+ "eslint-plugin-import": "^2.12.0",
+ "eslint-plugin-node": "^6.0.1",
+ "eslint-plugin-promise": "^3.8.0",
+ "eslint-plugin-standard": "^3.1.0"
+ },
+ "scripts": {
+ "preinstall": "./src/scripts/check_reqs.js && xcodebuild",
+ "build-test": "npm run pycompile && xcodebuild -target ios-deploy-lib && xcodebuild test -scheme ios-deploy-tests",
+ "eslint": "eslint src/scripts/*.js",
+ "test": "npm run eslint && npm run build-test",
+ "pycompile": "python -m py_compile src/scripts/*.py"
+ },
+ "keywords": [
+ "ios-deploy",
+ "deploy to iOS device"
+ ],
+ "bugs": {
+ "url": "https://github.com/phonegap/ios-deploy/issues"
+ },
+ "author": "Greg Hughes",
+ "license": "GPLv3"
+}
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-lib/libios_deploy.h b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-lib/libios_deploy.h
new file mode 100644
index 000000000000..78323bc9bd2f
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-lib/libios_deploy.h
@@ -0,0 +1,5 @@
+#import
+
+@interface ios_deploy_lib : NSObject
+
+@end
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-lib/libios_deploy.m b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-lib/libios_deploy.m
new file mode 100644
index 000000000000..5993a9b5d39b
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-lib/libios_deploy.m
@@ -0,0 +1,5 @@
+#import "libios_deploy.h"
+
+@implementation ios_deploy_lib
+
+@end
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-tests/ios_deploy_tests.m b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-tests/ios_deploy_tests.m
new file mode 100644
index 000000000000..f58e94935299
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy-tests/ios_deploy_tests.m
@@ -0,0 +1,31 @@
+#import
+
+@interface ios_deploy_tests : XCTestCase
+
+@end
+
+@implementation ios_deploy_tests
+
+- (void)setUp {
+ [super setUp];
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ [super tearDown];
+}
+
+- (void)testExample {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+}
+
+- (void)testPerformanceExample {
+ // This is an example of a performance test case.
+ [self measureBlock:^{
+ // Put the code you want to measure the time of here.
+ }];
+}
+
+@end
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/MobileDevice.h b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/MobileDevice.h
new file mode 100644
index 000000000000..89b7d5aaff1c
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/MobileDevice.h
@@ -0,0 +1,497 @@
+/* ----------------------------------------------------------------------------
+ * MobileDevice.h - interface to MobileDevice.framework
+ * $LastChangedDate: 2007-07-09 18:59:29 -0700 (Mon, 09 Jul 2007) $
+ *
+ * Copied from http://iphonesvn.halifrag.com/svn/iPhone/
+ * With modifications from Allen Porter and Scott Turner
+ *
+ * ------------------------------------------------------------------------- */
+
+#ifndef MOBILEDEVICE_H
+#define MOBILEDEVICE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(WIN32)
+#include
+typedef unsigned int mach_error_t;
+#elif defined(__APPLE__)
+#include
+#include
+#endif
+
+/* Error codes */
+#define MDERR_APPLE_MOBILE (err_system(0x3a))
+#define MDERR_IPHONE (err_sub(0))
+
+/* Apple Mobile (AM*) errors */
+#define MDERR_OK ERR_SUCCESS
+#define MDERR_SYSCALL (ERR_MOBILE_DEVICE | 0x01)
+#define MDERR_OUT_OF_MEMORY (ERR_MOBILE_DEVICE | 0x03)
+#define MDERR_QUERY_FAILED (ERR_MOBILE_DEVICE | 0x04)
+#define MDERR_INVALID_ARGUMENT (ERR_MOBILE_DEVICE | 0x0b)
+#define MDERR_DICT_NOT_LOADED (ERR_MOBILE_DEVICE | 0x25)
+
+/* Apple File Connection (AFC*) errors */
+#define MDERR_AFC_OUT_OF_MEMORY 0x03
+
+/* USBMux errors */
+#define MDERR_USBMUX_ARG_NULL 0x16
+#define MDERR_USBMUX_FAILED 0xffffffff
+
+/* Messages passed to device notification callbacks: passed as part of
+ * am_device_notification_callback_info. */
+#define ADNCI_MSG_CONNECTED 1
+#define ADNCI_MSG_DISCONNECTED 2
+#define ADNCI_MSG_UNKNOWN 3
+
+#define AMD_IPHONE_PRODUCT_ID 0x1290
+#define AMD_IPHONE_SERIAL "3391002d9c804d105e2c8c7d94fc35b6f3d214a3"
+
+/* Services, found in /System/Library/Lockdown/Services.plist */
+#define AMSVC_AFC CFSTR("com.apple.afc")
+#define AMSVC_BACKUP CFSTR("com.apple.mobilebackup")
+#define AMSVC_CRASH_REPORT_COPY CFSTR("com.apple.crashreportcopy")
+#define AMSVC_DEBUG_IMAGE_MOUNT CFSTR("com.apple.mobile.debug_image_mount")
+#define AMSVC_NOTIFICATION_PROXY CFSTR("com.apple.mobile.notification_proxy")
+#define AMSVC_PURPLE_TEST CFSTR("com.apple.purpletestr")
+#define AMSVC_SOFTWARE_UPDATE CFSTR("com.apple.mobile.software_update")
+#define AMSVC_SYNC CFSTR("com.apple.mobilesync")
+#define AMSVC_SCREENSHOT CFSTR("com.apple.screenshotr")
+#define AMSVC_SYSLOG_RELAY CFSTR("com.apple.syslog_relay")
+#define AMSVC_SYSTEM_PROFILER CFSTR("com.apple.mobile.system_profiler")
+
+typedef unsigned int afc_error_t;
+typedef unsigned int usbmux_error_t;
+typedef unsigned int service_conn_t;
+
+struct am_recovery_device;
+
+typedef struct am_device_notification_callback_info {
+ struct am_device *dev; /* 0 device */
+ unsigned int msg; /* 4 one of ADNCI_MSG_* */
+} __attribute__ ((packed)) am_device_notification_callback_info;
+
+/* The type of the device restore notification callback functions.
+ * TODO: change to correct type. */
+typedef void (*am_restore_device_notification_callback)(struct
+ am_recovery_device *);
+
+/* This is a CoreFoundation object of class AMRecoveryModeDevice. */
+typedef struct am_recovery_device {
+ unsigned char unknown0[8]; /* 0 */
+ am_restore_device_notification_callback callback; /* 8 */
+ void *user_info; /* 12 */
+ unsigned char unknown1[12]; /* 16 */
+ unsigned int readwrite_pipe; /* 28 */
+ unsigned char read_pipe; /* 32 */
+ unsigned char write_ctrl_pipe; /* 33 */
+ unsigned char read_unknown_pipe; /* 34 */
+ unsigned char write_file_pipe; /* 35 */
+ unsigned char write_input_pipe; /* 36 */
+} __attribute__ ((packed)) am_recovery_device;
+
+/* A CoreFoundation object of class AMRestoreModeDevice. */
+typedef struct am_restore_device {
+ unsigned char unknown[32];
+ int port;
+} __attribute__ ((packed)) am_restore_device;
+
+/* The type of the device notification callback function. */
+typedef void(*am_device_notification_callback)(struct
+ am_device_notification_callback_info *, void* arg);
+
+/* The type of the _AMDDeviceAttached function.
+ * TODO: change to correct type. */
+typedef void *amd_device_attached_callback;
+
+
+typedef struct am_device {
+ unsigned char unknown0[16]; /* 0 - zero */
+ unsigned int device_id; /* 16 */
+ unsigned int product_id; /* 20 - set to AMD_IPHONE_PRODUCT_ID */
+ char *serial; /* 24 - set to AMD_IPHONE_SERIAL */
+ unsigned int unknown1; /* 28 */
+ unsigned char unknown2[4]; /* 32 */
+ unsigned int lockdown_conn; /* 36 */
+ unsigned char unknown3[8]; /* 40 */
+} __attribute__ ((packed)) am_device;
+
+typedef struct am_device_notification {
+ unsigned int unknown0; /* 0 */
+ unsigned int unknown1; /* 4 */
+ unsigned int unknown2; /* 8 */
+ am_device_notification_callback callback; /* 12 */
+ unsigned int unknown3; /* 16 */
+} __attribute__ ((packed)) am_device_notification;
+
+typedef struct afc_connection {
+ unsigned int handle; /* 0 */
+ unsigned int unknown0; /* 4 */
+ unsigned char unknown1; /* 8 */
+ unsigned char padding[3]; /* 9 */
+ unsigned int unknown2; /* 12 */
+ unsigned int unknown3; /* 16 */
+ unsigned int unknown4; /* 20 */
+ unsigned int fs_block_size; /* 24 */
+ unsigned int sock_block_size; /* 28: always 0x3c */
+ unsigned int io_timeout; /* 32: from AFCConnectionOpen, usu. 0 */
+ void *afc_lock; /* 36 */
+ unsigned int context; /* 40 */
+} __attribute__ ((packed)) afc_connection;
+
+typedef struct afc_directory {
+ unsigned char unknown[0]; /* size unknown */
+} __attribute__ ((packed)) afc_directory;
+
+typedef struct afc_dictionary {
+ unsigned char unknown[0]; /* size unknown */
+} __attribute__ ((packed)) afc_dictionary;
+
+typedef unsigned long long afc_file_ref;
+
+typedef struct usbmux_listener_1 { /* offset value in iTunes */
+ unsigned int unknown0; /* 0 1 */
+ unsigned char *unknown1; /* 4 ptr, maybe device? */
+ amd_device_attached_callback callback; /* 8 _AMDDeviceAttached */
+ unsigned int unknown3; /* 12 */
+ unsigned int unknown4; /* 16 */
+ unsigned int unknown5; /* 20 */
+} __attribute__ ((packed)) usbmux_listener_1;
+
+typedef struct usbmux_listener_2 {
+ unsigned char unknown0[4144];
+} __attribute__ ((packed)) usbmux_listener_2;
+
+typedef struct am_bootloader_control_packet {
+ unsigned char opcode; /* 0 */
+ unsigned char length; /* 1 */
+ unsigned char magic[2]; /* 2: 0x34, 0x12 */
+ unsigned char payload[0]; /* 4 */
+} __attribute__ ((packed)) am_bootloader_control_packet;
+
+/* ----------------------------------------------------------------------------
+ * Public routines
+ * ------------------------------------------------------------------------- */
+
+void AMDSetLogLevel(int level);
+
+/* Registers a notification with the current run loop. The callback gets
+ * copied into the notification struct, as well as being registered with the
+ * current run loop. dn_unknown3 gets copied into unknown3 in the same.
+ * (Maybe dn_unknown3 is a user info parameter that gets passed as an arg to
+ * the callback?) unused0 and unused1 are both 0 when iTunes calls this.
+ * In iTunes the callback is located from $3db78e-$3dbbaf.
+ *
+ * Returns:
+ * MDERR_OK if successful
+ * MDERR_SYSCALL if CFRunLoopAddSource() failed
+ * MDERR_OUT_OF_MEMORY if we ran out of memory
+ */
+
+mach_error_t AMDeviceNotificationSubscribe(am_device_notification_callback
+ callback, unsigned int unused0, unsigned int unused1, void* //unsigned int
+ dn_unknown3, struct am_device_notification **notification);
+
+
+/* Connects to the iPhone. Pass in the am_device structure that the
+ * notification callback will give to you.
+ *
+ * Returns:
+ * MDERR_OK if successfully connected
+ * MDERR_SYSCALL if setsockopt() failed
+ * MDERR_QUERY_FAILED if the daemon query failed
+ * MDERR_INVALID_ARGUMENT if USBMuxConnectByPort returned 0xffffffff
+ */
+
+mach_error_t AMDeviceConnect(struct am_device *device);
+
+/* Calls PairingRecordPath() on the given device, than tests whether the path
+ * which that function returns exists. During the initial connect, the path
+ * returned by that function is '/', and so this returns 1.
+ *
+ * Returns:
+ * 0 if the path did not exist
+ * 1 if it did
+ */
+
+int AMDeviceIsPaired(struct am_device *device);
+
+/* iTunes calls this function immediately after testing whether the device is
+ * paired. It creates a pairing file and establishes a Lockdown connection.
+ *
+ * Returns:
+ * MDERR_OK if successful
+ * MDERR_INVALID_ARGUMENT if the supplied device is null
+ * MDERR_DICT_NOT_LOADED if the load_dict() call failed
+ */
+
+mach_error_t AMDeviceValidatePairing(struct am_device *device);
+
+/* Creates a Lockdown session and adjusts the device structure appropriately
+ * to indicate that the session has been started. iTunes calls this function
+ * after validating pairing.
+ *
+ * Returns:
+ * MDERR_OK if successful
+ * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established
+ * MDERR_DICT_NOT_LOADED if the load_dict() call failed
+ */
+
+mach_error_t AMDeviceStartSession(struct am_device *device);
+
+/* Starts a service and returns a handle that can be used in order to further
+ * access the service. You should stop the session and disconnect before using
+ * the service. iTunes calls this function after starting a session. It starts
+ * the service and the SSL connection. unknown may safely be
+ * NULL (it is when iTunes calls this), but if it is not, then it will be
+ * filled upon function exit. service_name should be one of the AMSVC_*
+ * constants. If the service is AFC (AMSVC_AFC), then the handle is the handle
+ * that will be used for further AFC* calls.
+ *
+ * Returns:
+ * MDERR_OK if successful
+ * MDERR_SYSCALL if the setsockopt() call failed
+ * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established
+ */
+
+mach_error_t AMDeviceStartService(struct am_device *device, CFStringRef
+ service_name, service_conn_t *handle, unsigned int *
+ unknown);
+
+mach_error_t AMDeviceStartHouseArrestService(struct am_device *device, CFStringRef identifier, void *unknown, service_conn_t *handle, unsigned int *what);
+
+/* Stops a session. You should do this before accessing services.
+ *
+ * Returns:
+ * MDERR_OK if successful
+ * MDERR_INVALID_ARGUMENT if the Lockdown conn has not been established
+ */
+
+mach_error_t AMDeviceStopSession(struct am_device *device);
+
+/* Opens an Apple File Connection. You must start the appropriate service
+ * first with AMDeviceStartService(). In iTunes, io_timeout is 0.
+ *
+ * Returns:
+ * MDERR_OK if successful
+ * MDERR_AFC_OUT_OF_MEMORY if malloc() failed
+ */
+
+afc_error_t AFCConnectionOpen(service_conn_t handle, unsigned int io_timeout,
+ struct afc_connection **conn);
+
+/* Pass in a pointer to an afc_device_info structure. It will be filled. */
+afc_error_t AFCDeviceInfoOpen(afc_connection *conn, struct
+ afc_dictionary **info);
+
+/* Turns debug mode on if the environment variable AFCDEBUG is set to a numeric
+ * value, or if the file '/AFCDEBUG' is present and contains a value. */
+ void AFCPlatformInit(void);
+
+/* Opens a directory on the iPhone. Pass in a pointer in dir to be filled in.
+ * Note that this normally only accesses the iTunes sandbox/partition as the
+ * root, which is /var/root/Media. Pathnames are specified with '/' delimiters
+ * as in Unix style.
+ *
+ * Returns:
+ * MDERR_OK if successful
+ */
+
+afc_error_t AFCDirectoryOpen(afc_connection *conn, const char *path,
+ struct afc_directory **dir);
+
+/* Acquires the next entry in a directory previously opened with
+ * AFCDirectoryOpen(). When dirent is filled with a NULL value, then the end
+ * of the directory has been reached. '.' and '..' will be returned as the
+ * first two entries in each directory except the root; you may want to skip
+ * over them.
+ *
+ * Returns:
+ * MDERR_OK if successful, even if no entries remain
+ */
+
+afc_error_t AFCDirectoryRead(afc_connection *conn/*unsigned int unused*/, struct afc_directory *dir,
+ char **dirent);
+
+afc_error_t AFCDirectoryClose(afc_connection *conn, struct afc_directory *dir);
+afc_error_t AFCDirectoryCreate(afc_connection *conn, const char *dirname);
+afc_error_t AFCRemovePath(afc_connection *conn, const char *dirname);
+afc_error_t AFCRenamePath(afc_connection *conn, const char *from, const char *to);
+afc_error_t AFCLinkPath(afc_connection *conn, long long int linktype, const char *target, const char *linkname);
+
+/* Returns the context field of the given AFC connection. */
+unsigned int AFCConnectionGetContext(afc_connection *conn);
+
+/* Returns the fs_block_size field of the given AFC connection. */
+unsigned int AFCConnectionGetFSBlockSize(afc_connection *conn);
+
+/* Returns the io_timeout field of the given AFC connection. In iTunes this is
+ * 0. */
+unsigned int AFCConnectionGetIOTimeout(afc_connection *conn);
+
+/* Returns the sock_block_size field of the given AFC connection. */
+unsigned int AFCConnectionGetSocketBlockSize(afc_connection *conn);
+
+/* Closes the given AFC connection. */
+afc_error_t AFCConnectionClose(afc_connection *conn);
+
+/* Registers for device notifications related to the restore process. unknown0
+ * is zero when iTunes calls this. In iTunes,
+ * the callbacks are located at:
+ * 1: $3ac68e-$3ac6b1, calls $3ac542(unknown1, arg, 0)
+ * 2: $3ac66a-$3ac68d, calls $3ac542(unknown1, 0, arg)
+ * 3: $3ac762-$3ac785, calls $3ac6b2(unknown1, arg, 0)
+ * 4: $3ac73e-$3ac761, calls $3ac6b2(unknown1, 0, arg)
+ */
+
+unsigned int AMRestoreRegisterForDeviceNotifications(
+ am_restore_device_notification_callback dfu_connect_callback,
+ am_restore_device_notification_callback recovery_connect_callback,
+ am_restore_device_notification_callback dfu_disconnect_callback,
+ am_restore_device_notification_callback recovery_disconnect_callback,
+ unsigned int unknown0,
+ void *user_info);
+
+/* Causes the restore functions to spit out (unhelpful) progress messages to
+ * the file specified by the given path. iTunes always calls this right before
+ * restoring with a path of
+ * "$HOME/Library/Logs/iPhone Updater Logs/iPhoneUpdater X.log", where X is an
+ * unused number.
+ */
+
+unsigned int AMRestoreEnableFileLogging(char *path);
+
+/* Initializes a new option dictionary to default values. Pass the constant
+ * kCFAllocatorDefault as the allocator. The option dictionary looks as
+ * follows:
+ * {
+ * NORImageType => 'production',
+ * AutoBootDelay => 0,
+ * KernelCacheType => 'Release',
+ * UpdateBaseband => true,
+ * DFUFileType => 'RELEASE',
+ * SystemImageType => 'User',
+ * CreateFilesystemPartitions => true,
+ * FlashNOR => true,
+ * RestoreBootArgs => 'rd=md0 nand-enable-reformat=1 -progress'
+ * BootImageType => 'User'
+ * }
+ *
+ * Returns:
+ * the option dictionary if successful
+ * NULL if out of memory
+ */
+
+CFMutableDictionaryRef AMRestoreCreateDefaultOptions(CFAllocatorRef allocator);
+
+/* ----------------------------------------------------------------------------
+ * Less-documented public routines
+ * ------------------------------------------------------------------------- */
+
+/* mode 2 = read, mode 3 = write */
+afc_error_t AFCFileRefOpen(afc_connection *conn, const char *path,
+ unsigned long long mode, afc_file_ref *ref);
+afc_error_t AFCFileRefSeek(afc_connection *conn, afc_file_ref ref,
+ unsigned long long offset1, unsigned long long offset2);
+afc_error_t AFCFileRefRead(afc_connection *conn, afc_file_ref ref,
+ void *buf, size_t *len);
+afc_error_t AFCFileRefSetFileSize(afc_connection *conn, afc_file_ref ref,
+ unsigned long long offset);
+afc_error_t AFCFileRefWrite(afc_connection *conn, afc_file_ref ref,
+ const void *buf, size_t len);
+afc_error_t AFCFileRefClose(afc_connection *conn, afc_file_ref ref);
+
+afc_error_t AFCFileInfoOpen(afc_connection *conn, const char *path, struct
+ afc_dictionary **info);
+afc_error_t AFCKeyValueRead(struct afc_dictionary *dict, char **key, char **
+ val);
+afc_error_t AFCKeyValueClose(struct afc_dictionary *dict);
+
+unsigned int AMRestorePerformRecoveryModeRestore(struct am_recovery_device *
+ rdev, CFDictionaryRef opts, void *callback, void *user_info);
+unsigned int AMRestorePerformRestoreModeRestore(struct am_restore_device *
+ rdev, CFDictionaryRef opts, void *callback, void *user_info);
+
+struct am_restore_device *AMRestoreModeDeviceCreate(unsigned int unknown0,
+ unsigned int connection_id, unsigned int unknown1);
+
+unsigned int AMRestoreCreatePathsForBundle(CFStringRef restore_bundle_path,
+ CFStringRef kernel_cache_type, CFStringRef boot_image_type, unsigned int
+ unknown0, CFStringRef *firmware_dir_path, CFStringRef *
+ kernelcache_restore_path, unsigned int unknown1, CFStringRef *
+ ramdisk_path);
+
+unsigned int AMDeviceGetConnectionID(struct am_device *device);
+mach_error_t AMDeviceEnterRecovery(struct am_device *device);
+mach_error_t AMDeviceDisconnect(struct am_device *device);
+mach_error_t AMDeviceRetain(struct am_device *device);
+mach_error_t AMDeviceRelease(struct am_device *device);
+CFStringRef AMDeviceCopyValue(struct am_device *device, unsigned int, CFStringRef cfstring);
+CFStringRef AMDeviceCopyDeviceIdentifier(struct am_device *device);
+
+typedef void (*notify_callback)(CFStringRef notification, void *data);
+
+mach_error_t AMDPostNotification(service_conn_t socket, CFStringRef notification, CFStringRef userinfo);
+mach_error_t AMDObserveNotification(void *socket, CFStringRef notification);
+mach_error_t AMDListenForNotifications(void *socket, notify_callback cb, void *data);
+mach_error_t AMDShutdownNotificationProxy(void *socket);
+
+/*edits by geohot*/
+mach_error_t AMDeviceDeactivate(struct am_device *device);
+mach_error_t AMDeviceActivate(struct am_device *device, CFMutableDictionaryRef);
+/*end*/
+
+void *AMDeviceSerialize(struct am_device *device);
+void AMDAddLogFileDescriptor(int fd);
+//kern_return_t AMDeviceSendMessage(service_conn_t socket, void *unused, CFPropertyListRef plist);
+//kern_return_t AMDeviceReceiveMessage(service_conn_t socket, CFDictionaryRef options, CFPropertyListRef * result);
+
+typedef int (*am_device_install_application_callback)(CFDictionaryRef, int);
+
+mach_error_t AMDeviceInstallApplication(service_conn_t socket, CFStringRef path, CFDictionaryRef options, am_device_install_application_callback callback, void *user);
+mach_error_t AMDeviceTransferApplication(service_conn_t socket, CFStringRef path, CFDictionaryRef options, am_device_install_application_callback callbackj, void *user);
+
+int AMDeviceSecureUninstallApplication(int unknown0, struct am_device *device, CFStringRef bundle_id, int unknown1, void *callback, int callback_arg);
+
+/* ----------------------------------------------------------------------------
+ * Semi-private routines
+ * ------------------------------------------------------------------------- */
+
+/* Pass in a usbmux_listener_1 structure and a usbmux_listener_2 structure
+ * pointer, which will be filled with the resulting usbmux_listener_2.
+ *
+ * Returns:
+ * MDERR_OK if completed successfully
+ * MDERR_USBMUX_ARG_NULL if one of the arguments was NULL
+ * MDERR_USBMUX_FAILED if the listener was not created successfully
+ */
+
+usbmux_error_t USBMuxListenerCreate(struct usbmux_listener_1 *esi_fp8, struct
+ usbmux_listener_2 **eax_fp12);
+
+/* ----------------------------------------------------------------------------
+ * Less-documented semi-private routines
+ * ------------------------------------------------------------------------- */
+
+usbmux_error_t USBMuxListenerHandleData(void *);
+
+/* ----------------------------------------------------------------------------
+ * Private routines - here be dragons
+ * ------------------------------------------------------------------------- */
+
+/* AMRestorePerformRestoreModeRestore() calls this function with a dictionary
+ * in order to perform certain special restore operations
+ * (RESTORED_OPERATION_*). It is thought that this function might enable
+ * significant access to the phone. */
+
+typedef unsigned int (*t_performOperation)(struct am_restore_device *rdev,
+ CFDictionaryRef op); // __attribute__ ((regparm(2)));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/device_db.h b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/device_db.h
new file mode 100644
index 000000000000..489cf36cdfc8
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/device_db.h
@@ -0,0 +1,118 @@
+//
+// devices.h
+// ios-deploy
+//
+// Created by Gusts Kaksis on 26/10/2016.
+// Copyright © 2016 PhoneGap. All rights reserved.
+//
+
+#import
+
+#define ADD_DEVICE(model, name, sdk, arch) {CFSTR(model), CFSTR(name), CFSTR(sdk), CFSTR(arch)}
+
+typedef struct {
+ CFStringRef model;
+ CFStringRef name;
+ CFStringRef sdk;
+ CFStringRef arch;
+} device_desc;
+
+#define UNKNOWN_DEVICE_IDX 0
+
+device_desc device_db[] = {
+ ADD_DEVICE("UNKN", "Unknown Device", "uknownos", "unkarch"),
+
+ // iPod Touch
+
+ ADD_DEVICE("N45AP", "iPod Touch", "iphoneos", "armv7"),
+ ADD_DEVICE("N72AP", "iPod Touch 2G", "iphoneos", "armv7"),
+ ADD_DEVICE("N18AP", "iPod Touch 3G", "iphoneos", "armv7"),
+ ADD_DEVICE("N81AP", "iPod Touch 4G", "iphoneos", "armv7"),
+ ADD_DEVICE("N78AP", "iPod Touch 5G", "iphoneos", "armv7"),
+ ADD_DEVICE("N78AAP", "iPod Touch 5G", "iphoneos", "armv7"),
+ ADD_DEVICE("N102AP", "iPod Touch 6G", "iphoneos", "arm64"),
+
+ // iPad
+
+ ADD_DEVICE("K48AP", "iPad", "iphoneos", "armv7"),
+ ADD_DEVICE("K93AP", "iPad 2", "iphoneos", "armv7"),
+ ADD_DEVICE("K94AP", "iPad 2 (GSM)", "iphoneos", "armv7"),
+ ADD_DEVICE("K95AP", "iPad 2 (CDMA)", "iphoneos", "armv7"),
+ ADD_DEVICE("K93AAP", "iPad 2 (Wi-Fi, revision A)", "iphoneos", "armv7"),
+ ADD_DEVICE("J1AP", "iPad 3", "iphoneos", "armv7"),
+ ADD_DEVICE("J2AP", "iPad 3 (GSM)", "iphoneos", "armv7"),
+ ADD_DEVICE("J2AAP", "iPad 3 (CDMA)", "iphoneos", "armv7"),
+ ADD_DEVICE("P101AP", "iPad 4", "iphoneos", "armv7s"),
+ ADD_DEVICE("P102AP", "iPad 4 (GSM)", "iphoneos", "armv7s"),
+ ADD_DEVICE("P103AP", "iPad 4 (CDMA)", "iphoneos", "armv7s"),
+ ADD_DEVICE("J71AP", "iPad Air", "iphoneos", "arm64"),
+ ADD_DEVICE("J72AP", "iPad Air (GSM)", "iphoneos", "arm64"),
+ ADD_DEVICE("J73AP", "iPad Air (CDMA)", "iphoneos", "arm64"),
+ ADD_DEVICE("J81AP", "iPad Air 2", "iphoneos", "arm64"),
+ ADD_DEVICE("J82AP", "iPad Air 2 (GSM)", "iphoneos", "arm64"),
+ ADD_DEVICE("J83AP", "iPad Air 2 (CDMA)", "iphoneos", "arm64"),
+ ADD_DEVICE("J71sAP", "iPad (2017)", "iphoneos", "arm64"),
+ ADD_DEVICE("J71tAP", "iPad (2017)", "iphoneos", "arm64"),
+ ADD_DEVICE("J72sAP", "iPad (2017)", "iphoneos", "arm64"),
+ ADD_DEVICE("J72tAP", "iPad (2017)", "iphoneos", "arm64"),
+
+ // iPad Pro
+
+ ADD_DEVICE("J98aAP", "iPad Pro (12.9\")", "iphoneos", "arm64"),
+ ADD_DEVICE("J99aAP", "iPad Pro (12.9\")", "iphoneos", "arm64"),
+ ADD_DEVICE("J120AP", "iPad Pro 2G (12.9\")", "iphoneos", "arm64"),
+ ADD_DEVICE("J121AP", "iPad Pro 2G (12.9\")", "iphoneos", "arm64"),
+ ADD_DEVICE("J127AP", "iPad Pro (9.7\")", "iphoneos", "arm64"),
+ ADD_DEVICE("J128AP", "iPad Pro (9.7\")", "iphoneos", "arm64"),
+ ADD_DEVICE("J207AP", "iPad Pro (10.5\")", "iphoneos", "arm64"),
+ ADD_DEVICE("J208AP", "iPad Pro (10.5\")", "iphoneos", "arm64"),
+
+ // iPad Mini
+
+ ADD_DEVICE("P105AP", "iPad mini", "iphoneos", "armv7"),
+ ADD_DEVICE("P106AP", "iPad mini (GSM)", "iphoneos", "armv7"),
+ ADD_DEVICE("P107AP", "iPad mini (CDMA)", "iphoneos", "armv7"),
+ ADD_DEVICE("J85AP", "iPad mini 2", "iphoneos", "arm64"),
+ ADD_DEVICE("J86AP", "iPad mini 2 (GSM)", "iphoneos", "arm64"),
+ ADD_DEVICE("J87AP", "iPad mini 2 (CDMA)", "iphoneos", "arm64"),
+ ADD_DEVICE("J85MAP", "iPad mini 3", "iphoneos", "arm64"),
+ ADD_DEVICE("J86MAP", "iPad mini 3 (GSM)", "iphoneos", "arm64"),
+ ADD_DEVICE("J87MAP", "iPad mini 3 (CDMA)", "iphoneos", "arm64"),
+ ADD_DEVICE("J96AP", "iPad mini 4", "iphoneos", "arm64"),
+ ADD_DEVICE("J97AP", "iPad mini 4 (GSM)", "iphoneos", "arm64"),
+
+ // iPhone
+
+ ADD_DEVICE("M68AP", "iPhone", "iphoneos", "armv7"),
+ ADD_DEVICE("N82AP", "iPhone 3G", "iphoneos", "armv7"),
+ ADD_DEVICE("N88AP", "iPhone 3GS", "iphoneos", "armv7"),
+ ADD_DEVICE("N90AP", "iPhone 4 (GSM)", "iphoneos", "armv7"),
+ ADD_DEVICE("N92AP", "iPhone 4 (CDMA)", "iphoneos", "armv7"),
+ ADD_DEVICE("N90BAP", "iPhone 4 (GSM, revision A)", "iphoneos", "armv7"),
+ ADD_DEVICE("N94AP", "iPhone 4S", "iphoneos", "armv7"),
+ ADD_DEVICE("N41AP", "iPhone 5 (GSM)", "iphoneos", "armv7s"),
+ ADD_DEVICE("N42AP", "iPhone 5 (Global/CDMA)", "iphoneos", "armv7s"),
+ ADD_DEVICE("N48AP", "iPhone 5c (GSM)", "iphoneos", "armv7s"),
+ ADD_DEVICE("N49AP", "iPhone 5c (Global/CDMA)", "iphoneos", "armv7s"),
+ ADD_DEVICE("N51AP", "iPhone 5s (GSM)", "iphoneos", "arm64"),
+ ADD_DEVICE("N53AP", "iPhone 5s (Global/CDMA)", "iphoneos", "arm64"),
+ ADD_DEVICE("N61AP", "iPhone 6 (GSM)", "iphoneos", "arm64"),
+ ADD_DEVICE("N56AP", "iPhone 6 Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("N71mAP", "iPhone 6s", "iphoneos", "arm64"),
+ ADD_DEVICE("N71AP", "iPhone 6s", "iphoneos", "arm64"),
+ ADD_DEVICE("N66AP", "iPhone 6s Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("N66mAP", "iPhone 6s Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("N69AP", "iPhone SE", "iphoneos", "arm64"),
+ ADD_DEVICE("N69uAP", "iPhone SE", "iphoneos", "arm64"),
+ ADD_DEVICE("D10AP", "iPhone 7", "iphoneos", "arm64"),
+ ADD_DEVICE("D101AP", "iPhone 7", "iphoneos", "arm64"),
+ ADD_DEVICE("D11AP", "iPhone 7 Plus", "iphoneos", "arm64"),
+ ADD_DEVICE("D111AP", "iPhone 7 Plus", "iphoneos", "arm64"),
+
+ // Apple TV
+
+ ADD_DEVICE("K66AP", "Apple TV 2G", "appletvos", "armv7"),
+ ADD_DEVICE("J33AP", "Apple TV 3G", "appletvos", "armv7"),
+ ADD_DEVICE("J33IAP", "Apple TV 3.1G", "appletvos", "armv7"),
+ ADD_DEVICE("J42dAP", "Apple TV 4G", "appletvos", "arm64"),
+ };
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/errors.h b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/errors.h
new file mode 100644
index 000000000000..717f81df8a65
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/errors.h
@@ -0,0 +1,531 @@
+
+typedef struct errorcode_to_id {
+ unsigned int error;
+ const char* id;
+} errorcode_to_id_t;
+
+typedef struct error_id_to_message {
+ const char* id;
+ const char* message;
+} error_id_to_message_t;
+
+// Parts of error code to localization id map is taken from SDMMobileDevice framework. Associated license is bellow.
+// https://github.com/samdmarshall/SDMMobileDevice/blob/master/Framework/MobileDevice/Error/SDMMD_Error.h
+//
+// Copyright (c) 2014, Sam Marshall
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+// following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the distribution.
+//
+// 3. Neither the name of Sam Marshall nor the names of its contributors may be used to endorse or promote products derived from this
+// software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+static errorcode_to_id_t errorcode_to_id[] = {
+ { 0x00000000, "kAMDSuccess" },
+ { 0xe8000001, "kAMDUndefinedError" },
+ { 0xe8000002, "kAMDBadHeaderError" },
+ { 0xe8000003, "kAMDNoResourcesError" },
+ { 0xe8000004, "kAMDReadError" },
+ { 0xe8000005, "kAMDWriteError" },
+ { 0xe8000006, "kAMDUnknownPacketError" },
+ { 0xe8000007, "kAMDInvalidArgumentError" },
+ { 0xe8000008, "kAMDNotFoundError" },
+ { 0xe8000009, "kAMDIsDirectoryError" },
+ { 0xe800000a, "kAMDPermissionError" },
+ { 0xe800000b, "kAMDNotConnectedError" },
+ { 0xe800000c, "kAMDTimeOutError" },
+ { 0xe800000d, "kAMDOverrunError" },
+ { 0xe800000e, "kAMDEOFError" },
+ { 0xe800000f, "kAMDUnsupportedError" },
+ { 0xe8000010, "kAMDFileExistsError" },
+ { 0xe8000011, "kAMDBusyError" },
+ { 0xe8000012, "kAMDCryptoError" },
+ { 0xe8000013, "kAMDInvalidResponseError" },
+ { 0xe8000014, "kAMDMissingKeyError" },
+ { 0xe8000015, "kAMDMissingValueError" },
+ { 0xe8000016, "kAMDGetProhibitedError" },
+ { 0xe8000017, "kAMDSetProhibitedError" },
+ { 0xe8000018, "kAMDRemoveProhibitedError" },
+ { 0xe8000019, "kAMDImmutableValueError" },
+ { 0xe800001a, "kAMDPasswordProtectedError" },
+ { 0xe800001b, "kAMDMissingHostIDError" },
+ { 0xe800001c, "kAMDInvalidHostIDError" },
+ { 0xe800001d, "kAMDSessionActiveError" },
+ { 0xe800001e, "kAMDSessionInactiveError" },
+ { 0xe800001f, "kAMDMissingSessionIDError" },
+ { 0xe8000020, "kAMDInvalidSessionIDError" },
+ { 0xe8000021, "kAMDMissingServiceError" },
+ { 0xe8000022, "kAMDInvalidServiceError" },
+ { 0xe8000023, "kAMDInvalidCheckinError" },
+ { 0xe8000024, "kAMDCheckinTimeoutError" },
+ { 0xe8000025, "kAMDMissingPairRecordError" },
+ { 0xe8000026, "kAMDInvalidActivationRecordError" },
+ { 0xe8000027, "kAMDMissingActivationRecordError" },
+ { 0xe8000028, "kAMDWrongDroidError" },
+ { 0xe8000029, "kAMDSUVerificationError" },
+ { 0xe800002a, "kAMDSUPatchError" },
+ { 0xe800002b, "kAMDSUFirmwareError" },
+ { 0xe800002c, "kAMDProvisioningProfileNotValid" },
+ { 0xe800002d, "kAMDSendMessageError" },
+ { 0xe800002e, "kAMDReceiveMessageError" },
+ { 0xe800002f, "kAMDMissingOptionsError" },
+ { 0xe8000030, "kAMDMissingImageTypeError" },
+ { 0xe8000031, "kAMDDigestFailedError" },
+ { 0xe8000032, "kAMDStartServiceError" },
+ { 0xe8000033, "kAMDInvalidDiskImageError" },
+ { 0xe8000034, "kAMDMissingDigestError" },
+ { 0xe8000035, "kAMDMuxError" },
+ { 0xe8000036, "kAMDApplicationAlreadyInstalledError" },
+ { 0xe8000037, "kAMDApplicationMoveFailedError" },
+ { 0xe8000038, "kAMDApplicationSINFCaptureFailedError" },
+ { 0xe8000039, "kAMDApplicationSandboxFailedError" },
+ { 0xe800003a, "kAMDApplicationVerificationFailedError" },
+ { 0xe800003b, "kAMDArchiveDestructionFailedError" },
+ { 0xe800003c, "kAMDBundleVerificationFailedError" },
+ { 0xe800003d, "kAMDCarrierBundleCopyFailedError" },
+ { 0xe800003e, "kAMDCarrierBundleDirectoryCreationFailedError" },
+ { 0xe800003f, "kAMDCarrierBundleMissingSupportedSIMsError" },
+ { 0xe8000040, "kAMDCommCenterNotificationFailedError" },
+ { 0xe8000041, "kAMDContainerCreationFailedError" },
+ { 0xe8000042, "kAMDContainerP0wnFailedError" },
+ { 0xe8000043, "kAMDContainerRemovalFailedError" },
+ { 0xe8000044, "kAMDEmbeddedProfileInstallFailedError" },
+ { 0xe8000045, "kAMDErrorError" },
+ { 0xe8000046, "kAMDExecutableTwiddleFailedError" },
+ { 0xe8000047, "kAMDExistenceCheckFailedError" },
+ { 0xe8000048, "kAMDInstallMapUpdateFailedError" },
+ { 0xe8000049, "kAMDManifestCaptureFailedError" },
+ { 0xe800004a, "kAMDMapGenerationFailedError" },
+ { 0xe800004b, "kAMDMissingBundleExecutableError" },
+ { 0xe800004c, "kAMDMissingBundleIdentifierError" },
+ { 0xe800004d, "kAMDMissingBundlePathError" },
+ { 0xe800004e, "kAMDMissingContainerError" },
+ { 0xe800004f, "kAMDNotificationFailedError" },
+ { 0xe8000050, "kAMDPackageExtractionFailedError" },
+ { 0xe8000051, "kAMDPackageInspectionFailedError" },
+ { 0xe8000052, "kAMDPackageMoveFailedError" },
+ { 0xe8000053, "kAMDPathConversionFailedError" },
+ { 0xe8000054, "kAMDRestoreContainerFailedError" },
+ { 0xe8000055, "kAMDSeatbeltProfileRemovalFailedError" },
+ { 0xe8000056, "kAMDStageCreationFailedError" },
+ { 0xe8000057, "kAMDSymlinkFailedError" },
+ { 0xe8000058, "kAMDiTunesArtworkCaptureFailedError" },
+ { 0xe8000059, "kAMDiTunesMetadataCaptureFailedError" },
+ { 0xe800005a, "kAMDAlreadyArchivedError" },
+ { 0xe800005b, "kAMDServiceLimitError" },
+ { 0xe800005c, "kAMDInvalidPairRecordError" },
+ { 0xe800005d, "kAMDServiceProhibitedError" },
+ { 0xe800005e, "kAMDCheckinSetupFailedError" },
+ { 0xe800005f, "kAMDCheckinConnectionFailedError" },
+ { 0xe8000060, "kAMDCheckinReceiveFailedError" },
+ { 0xe8000061, "kAMDCheckinResponseFailedError" },
+ { 0xe8000062, "kAMDCheckinSendFailedError" },
+ { 0xe8000063, "kAMDMuxCreateListenerError" },
+ { 0xe8000064, "kAMDMuxGetListenerError" },
+ { 0xe8000065, "kAMDMuxConnectError" },
+ { 0xe8000066, "kAMDUnknownCommandError" },
+ { 0xe8000067, "kAMDAPIInternalError" },
+ { 0xe8000068, "kAMDSavePairRecordFailedError" },
+ { 0xe8000069, "kAMDCheckinOutOfMemoryError" },
+ { 0xe800006a, "kAMDDeviceTooNewError" },
+ { 0xe800006b, "kAMDDeviceRefNoGood" },
+ { 0xe800006c, "kAMDCannotTranslateError" },
+ { 0xe800006d, "kAMDMobileImageMounterMissingImageSignature" },
+ { 0xe800006e, "kAMDMobileImageMounterResponseCreationFailed" },
+ { 0xe800006f, "kAMDMobileImageMounterMissingImageType" },
+ { 0xe8000070, "kAMDMobileImageMounterMissingImagePath" },
+ { 0xe8000071, "kAMDMobileImageMounterImageMapLoadFailed" },
+ { 0xe8000072, "kAMDMobileImageMounterAlreadyMounted" },
+ { 0xe8000073, "kAMDMobileImageMounterImageMoveFailed" },
+ { 0xe8000074, "kAMDMobileImageMounterMountPathMissing" },
+ { 0xe8000075, "kAMDMobileImageMounterMountPathNotEmpty" },
+ { 0xe8000076, "kAMDMobileImageMounterImageMountFailed" },
+ { 0xe8000077, "kAMDMobileImageMounterTrustCacheLoadFailed" },
+ { 0xe8000078, "kAMDMobileImageMounterDigestFailed" },
+ { 0xe8000079, "kAMDMobileImageMounterDigestCreationFailed" },
+ { 0xe800007a, "kAMDMobileImageMounterImageVerificationFailed" },
+ { 0xe800007b, "kAMDMobileImageMounterImageInfoCreationFailed" },
+ { 0xe800007c, "kAMDMobileImageMounterImageMapStoreFailed" },
+ { 0xe800007d, "kAMDBonjourSetupError" },
+ { 0xe800007e, "kAMDDeviceOSVersionTooLow" },
+ { 0xe800007f, "kAMDNoWifiSyncSupportError" },
+ { 0xe8000080, "kAMDDeviceFamilyNotSupported" },
+ { 0xe8000081, "kAMDEscrowLockedError" },
+ { 0xe8000082, "kAMDPairingProhibitedError" },
+ { 0xe8000083, "kAMDProhibitedBySupervision" },
+ { 0xe8000084, "kAMDDeviceDisconnectedError" },
+ { 0xe8000085, "kAMDTooBigError" },
+ { 0xe8000086, "kAMDPackagePatchFailedError" },
+ { 0xe8000087, "kAMDIncorrectArchitectureError" },
+ { 0xe8000088, "kAMDPluginCopyFailedError" },
+ { 0xe8000089, "kAMDBreadcrumbFailedError" },
+ { 0xe800008a, "kAMDBreadcrumbUnlockError" },
+ { 0xe800008b, "kAMDGeoJSONCaptureFailedError" },
+ { 0xe800008c, "kAMDNewsstandArtworkCaptureFailedError" },
+ { 0xe800008d, "kAMDMissingCommandError" },
+ { 0xe800008e, "kAMDNotEntitledError" },
+ { 0xe800008f, "kAMDMissingPackagePathError" },
+ { 0xe8000090, "kAMDMissingContainerPathError" },
+ { 0xe8000091, "kAMDMissingApplicationIdentifierError" },
+ { 0xe8000092, "kAMDMissingAttributeValueError" },
+ { 0xe8000093, "kAMDLookupFailedError" },
+ { 0xe8000094, "kAMDDictCreationFailedError" },
+ { 0xe8000095, "kAMDUserDeniedPairingError" },
+ { 0xe8000096, "kAMDPairingDialogResponsePendingError" },
+ { 0xe8000097, "kAMDInstallProhibitedError" },
+ { 0xe8000098, "kAMDUninstallProhibitedError" },
+ { 0xe8000099, "kAMDFMiPProtectedError" },
+ { 0xe800009a, "kAMDMCProtected" },
+ { 0xe800009b, "kAMDMCChallengeRequired" },
+ { 0xe800009c, "kAMDMissingBundleVersionError" },
+ { 0xe800009d, "kAMDAppBlacklistedError" },
+ { 0xe800009e, "This app contains an app extension with an illegal bundle identifier. App extension bundle identifiers must have a prefix consisting of their containing application's bundle identifier followed by a '.'." },
+ { 0xe800009f, "If an app extension defines the XPCService key in its Info.plist, it must have a dictionary value." },
+ { 0xe80000a0, "App extensions must define the NSExtension key with a dictionary value in their Info.plist." },
+ { 0xe80000a1, "If an app extension defines the CFBundlePackageType key in its Info.plist, it must have the value \"XPC!\"." },
+ { 0xe80000a2, "App extensions must define either NSExtensionMainStoryboard or NSExtensionPrincipalClass keys in the NSExtension dictionary in their Info.plist." },
+ { 0xe80000a3, "If an app extension defines the NSExtensionContextClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." },
+ { 0xe80000a4, "If an app extension defines the NSExtensionContextHostClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." },
+ { 0xe80000a5, "If an app extension defines the NSExtensionViewControllerHostClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." },
+ { 0xe80000a6, "This app contains an app extension that does not define the NSExtensionPointIdentifier key in its Info.plist. This key must have a reverse-DNS format string value." },
+ { 0xe80000a7, "This app contains an app extension that does not define the NSExtensionPointIdentifier key in its Info.plist with a valid reverse-DNS format string value." },
+ { 0xe80000a8, "If an app extension defines the NSExtensionAttributes key in the NSExtension dictionary in its Info.plist, it must have a dictionary value." },
+ { 0xe80000a9, "If an app extension defines the NSExtensionPointName key in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." },
+ { 0xe80000aa, "If an app extension defines the NSExtensionPointVersion key in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters." },
+ { 0xe80000ab, "This app or a bundle it contains does not define the CFBundleName key in its Info.plist with a string value containing one or more characters." },
+ { 0xe80000ac, "This app or a bundle it contains does not define the CFBundleDisplayName key in its Info.plist with a string value containing one or more characters." },
+ { 0xe80000ad, "This app or a bundle it contains defines the CFBundleShortVersionStringKey key in its Info.plist with a non-string value or a zero-length string value." },
+ { 0xe80000ae, "This app or a bundle it contains defines the RunLoopType key in the XPCService dictionary in its Info.plist with a non-string value or a zero-length string value." },
+ { 0xe80000af, "This app or a bundle it contains defines the ServiceType key in the XPCService dictionary in its Info.plist with a non-string value or a zero-length string value." },
+ { 0xe80000b0, "This application or a bundle it contains has the same bundle identifier as this application or another bundle that it contains. Bundle identifiers must be unique." },
+ { 0xe80000b1, "This app contains an app extension that specifies an extension point identifier that is not supported on this version of iOS for the value of the NSExtensionPointIdentifier key in its Info.plist." },
+ { 0xe80000b2, "This app contains multiple app extensions that are file providers. Apps are only allowed to contain at most a single file provider app extension." },
+ { 0xe80000b3, "kMobileHouseArrestMissingCommand" },
+ { 0xe80000b4, "kMobileHouseArrestUnknownCommand" },
+ { 0xe80000b5, "kMobileHouseArrestMissingIdentifier" },
+ { 0xe80000b6, "kMobileHouseArrestDictionaryFailed" },
+ { 0xe80000b7, "kMobileHouseArrestInstallationLookupFailed" },
+ { 0xe80000b8, "kMobileHouseArrestApplicationLookupFailed" },
+ { 0xe80000b9, "kMobileHouseArrestMissingContainer" },
+ // 0xe80000ba does not exist
+ { 0xe80000bb, "kMobileHouseArrestPathConversionFailed" },
+ { 0xe80000bc, "kMobileHouseArrestPathMissing" },
+ { 0xe80000bd, "kMobileHouseArrestInvalidPath" },
+ { 0xe80000be, "kAMDMismatchedApplicationIdentifierEntitlementError" },
+ { 0xe80000bf, "kAMDInvalidSymlinkError" },
+ { 0xe80000c0, "kAMDNoSpaceError" },
+ { 0xe80000c1, "The WatchKit app extension must have, in its Info.plist's NSExtension dictionary's NSExtensionAttributes dictionary, the key WKAppBundleIdentifier with a value equal to the associated WatchKit app's bundle identifier." },
+ { 0xe80000c2, "This app is not a valid AppleTV Stub App" },
+ { 0xe80000c3, "kAMDBundleiTunesMetadataVersionMismatchError" },
+ { 0xe80000c4, "kAMDInvalidiTunesMetadataPlistError" },
+ { 0xe80000c5, "kAMDMismatchedBundleIDSigningIdentifierError" },
+ { 0xe80000c6, "This app contains multiple WatchKit app extensions. Only a single WatchKit extension is allowed." },
+ { 0xe80000c7, "A WatchKit app within this app is not a valid bundle." },
+ { 0xe80000c8, "kAMDDeviceNotSupportedByThinningError" },
+ { 0xe80000c9, "The UISupportedDevices key in this app's Info.plist does not specify a valid set of supported devices." },
+ { 0xe80000ca, "This app contains an app extension with an illegal bundle identifier. App extension bundle identifiers must have a prefix consisting of their containing application's bundle identifier followed by a '.', with no further '.' characters after the prefix." },
+ { 0xe80000cb, "kAMDAppexBundleIDConflictWithOtherIdentifierError" },
+ { 0xe80000cc, "kAMDBundleIDConflictWithOtherIdentifierError" },
+ { 0xe80000cd, "This app contains multiple WatchKit 1.0 apps. Only a single WatchKit 1.0 app is allowed." },
+ { 0xe80000ce, "This app contains multiple WatchKit 2.0 apps. Only a single WatchKit 2.0 app is allowed." },
+ { 0xe80000cf, "The WatchKit app has an invalid stub executable." },
+ { 0xe80000d0, "The WatchKit app has multiple app extensions. Only a single WatchKit extension is allowed in a WatchKit app, and only if this is a WatchKit 2.0 app." },
+ { 0xe80000d1, "The WatchKit 2.0 app contains non-WatchKit app extensions. Only WatchKit app extensions are allowed in WatchKit apps." },
+ { 0xe80000d2, "The WatchKit app has one or more embedded frameworks. Frameworks are only allowed in WatchKit app extensions in WatchKit 2.0 apps." },
+ { 0xe80000d3, "This app contains a WatchKit 1.0 app with app extensions. This is not allowed." },
+ { 0xe80000d4, "This app contains a WatchKit 2.0 app without an app extension. WatchKit 2.0 apps must contain a WatchKit app extension." },
+ { 0xe80000d5, "The WatchKit app's Info.plist must have a WKCompanionAppBundleIdentifier key set to the bundle identifier of the companion app." },
+ { 0xe80000d6, "The WatchKit app's Info.plist contains a non-string key." },
+ { 0xe80000d7, "The WatchKit app's Info.plist contains a key that is not in the whitelist of allowed keys for a WatchKit app." },
+ { 0xe80000d8, "The WatchKit 1.0 and a WatchKit 2.0 apps within this app must have have the same bundle identifier." },
+ { 0xe80000d9, "This app contains a WatchKit app with an invalid bundle identifier. The bundle identifier of a WatchKit app must have a prefix consisting of the companion app's bundle identifier, followed by a '.'." },
+ { 0xe80000da, "This app contains a WatchKit app where the UIDeviceFamily key in its Info.plist does not specify the value 4 to indicate that it's compatible with the Apple Watch device type." },
+ { 0xe80000db, "The device is out of storage for apps. Please remove some apps from the device and try again." },
+ { 0xe80000dc, "This app or an app that it contains has a Siri Intents app extension that is missing the IntentsSupported array in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist." },
+ { 0xe80000dd, "This app or an app that it contains has a Siri Intents app extension that does not correctly define the IntentsRestrictedWhileLocked key in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist. The key's value must be an array of strings." },
+ { 0xe80000de, "This app or an app that it contains has a Siri Intents app extension that declares values in its IntentsRestrictedWhileLocked key's array value that are not in its IntentsSupported key's array value (in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist)." },
+ { 0xe80000df, "This app or an app that it contains declares multiple Siri Intents app extensions that declare one or more of the same values in the IntentsSupported array in the NSExtensionAttributes dictionary in the NSExtension dictionary in their Info.plist. IntentsSupported must be distinct among a given Siri Intents extension type within an app." },
+ { 0xe80000e0, "The WatchKit 2.0 app, which expects to be compatible with watchOS versions earlier than 3.0, contains a non-WatchKit extension in a location that's not compatible with watchOS versions earlier than 3.0." },
+ { 0xe80000e1, "The WatchKit 2.0 app, which expects to be compatible with watchOS versions earlier than 3.0, contains a framework in a location that's not compatible with watchOS versions earlier than 3.0." },
+ { 0xe80000e2, "kAMDMobileImageMounterDeviceLocked" },
+ { 0xe80000e3, "kAMDInvalidSINFError" },
+ { 0xe80000e4, "Multiple iMessage app extensions were found in this app. Only one is allowed." },
+ { 0xe80000e5, "This iMessage application is missing its required iMessage app extension." },
+ { 0xe80000e6, "This iMessage application contains an app extension type other than an iMessage app extension. iMessage applications may only contain one iMessage app extension and may not contain other types of app extensions." },
+ { 0xe80000e7, "This app contains a WatchKit app with one or more Siri Intents app extensions that declare IntentsSupported that are not declared in any of the companion app's Siri Intents app extensions. WatchKit Siri Intents extensions' IntentsSupported values must be a subset of the companion app's Siri Intents extensions' IntentsSupported values." },
+ { 0xe80000e8, "kAMDRequireCUPairingCodeError" },
+ { 0xe80000e9, "kAMDRequireCUPairingBackoffError" },
+ { 0xe80000ea, "kAMDCUPairingError" },
+ { 0xe80000eb, "kAMDCUPairingContinueError" },
+ { 0xe80000ec, "kAMDCUPairingResetError" },
+ { 0xe80000ed, "kAMDRequireCUPairingError" },
+ { 0xe80000ee, "kAMDPasswordRequiredError" },
+
+ // Errors without id->string mapping.
+ { 0xe8008001, "An unknown error has occurred." },
+ { 0xe8008002, "Attempted to modify an immutable provisioning profile." },
+ { 0xe8008003, "This provisioning profile is malformed." },
+ { 0xe8008004, "This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature)." },
+ { 0xe8008005, "This provisioning profile is malformed." },
+ { 0xe8008006, "This provisioning profile is malformed." },
+ { 0xe8008007, "This provisioning profile is malformed." },
+ { 0xe8008008, "This provisioning profile is malformed." },
+ { 0xe8008009, "The signature was not valid." },
+ { 0xe800800a, "Unable to allocate memory." },
+ { 0xe800800b, "A file operation failed." },
+ { 0xe800800c, "There was an error communicating with your device." },
+ { 0xe800800d, "There was an error communicating with your device." },
+ { 0xe800800e, "This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature)." },
+ { 0xe800800f, "The application's signature is valid but it does not match the expected hash." },
+ { 0xe8008010, "This provisioning profile is unsupported." },
+ { 0xe8008011, "This provisioning profile has expired." },
+ { 0xe8008012, "This provisioning profile cannot be installed on this device." },
+ { 0xe8008013, "This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature)." },
+ { 0xe8008014, "The executable contains an invalid signature." },
+ { 0xe8008015, "A valid provisioning profile for this executable was not found." },
+ { 0xe8008016, "The executable was signed with invalid entitlements." },
+ { 0xe8008017, "A signed resource has been added, modified, or deleted." },
+ { 0xe8008018, "The identity used to sign the executable is no longer valid." },
+ { 0xe8008019, "The application does not have a valid signature." },
+ { 0xe800801a, "This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature)." },
+ { 0xe800801b, "There was an error communicating with your device." },
+ { 0xe800801c, "No code signature found." },
+ { 0xe800801d, "Rejected by policy." },
+ { 0xe800801e, "The requested profile does not exist (it may have been removed)." },
+ { 0xe800801f, "Attempted to install a Beta profile without the proper entitlement." },
+ { 0xe8008020, "Attempted to install a Beta profile over lockdown connection." },
+ { 0xe8008021, "The maximum number of apps for free development profiles has been reached." },
+ { 0xe8008022, "An error occured while accessing the profile database." },
+ { 0xe8008023, "An error occured while communicating with the agent." },
+ { 0xe8008024, "The provisioning profile is banned." },
+ { 0xe8008025, "The user did not explicitly trust the provisioning profile." },
+ { 0xe8008026, "The provisioning profile requires online authorization." },
+ { 0xe8008027, "The cdhash is not in the trust cache." },
+ { 0xe8008028, "Invalid arguments or option combination." },
+};
+
+const int errorcode_to_id_count = sizeof(errorcode_to_id) / sizeof(errorcode_to_id_t);
+
+// Taken from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/Resources/English.lproj/Localizable.strings
+error_id_to_message_t error_id_to_message[] = {
+ { "kAMDAPIInternalError", "There was an internal API error." },
+ { "kAMDAlreadyArchivedError", "The application is already archived." },
+ { "kAMDAppBlacklistedError", "This app is not allowed to be installed on this device." },
+ { "kAMDAppexBundleIDConflictWithOtherIdentifierError", "This application contains an app extension with a bundle identifier that conflicts with the bundle identifier of another app or app extension already installed." },
+ { "kAMDApplicationAlreadyInstalledError", "A system application with the given bundle identifier is already installed on the device and cannot be replaced." },
+ { "kAMDApplicationMoveFailedError", "The application could not be moved into place on the device." },
+ { "kAMDApplicationSandboxFailedError", "The application could not be sandboxed." },
+ { "kAMDApplicationVerificationFailedError", "The application could not be verified." },
+ { "kAMDArchiveDestructionFailedError", "Could not remove the application archive." },
+ { "kAMDBadHeaderError", "Could not transfer file." },
+ { "kAMDBreadcrumbFailedError", "Could not write installation breadcrumb." },
+ { "kAMDBreadcrumbUnlockError", "Could not update installation breadcrumb." },
+ { "kAMDBundleIDConflictWithOtherIdentifierError", "This application's bundle identifier conflicts with the identifier of another app or app extension already installed." },
+ { "kAMDBundleVerificationFailedError", "The carrier bundle could not be verified." },
+ { "kAMDBundleiTunesMetadataVersionMismatchError", "This application's iTunesMetadata.plist specifies versions that do not match the versions listed for the app in its Info.plist" },
+ { "kAMDBusyError", "The device is busy." },
+ { "kAMDCUPairingContinueError", "Continue pairing process over the network." },
+ { "kAMDCUPairingError", "General failure while pairing over the network." },
+ { "kAMDCUPairingResetError", "Pairing was reset due to earlier issues, try again." },
+ { "kAMDCannotTranslateError", "Could not translate messages from device" },
+ { "kAMDCarrierBundleCopyFailedError", "Could not install the carrier bundle." },
+ { "kAMDCarrierBundleDirectoryCreationFailedError", "Could not create the carrier bundle directory." },
+ { "kAMDCarrierBundleMissingSupportedSIMsError", "There are no supported SIMs for this carrier bundle." },
+ { "kAMDCheckinConnectionFailedError", "The service did not start properly on the device." },
+ { "kAMDCheckinOutOfMemoryError", "The service did not start properly on the device." },
+ { "kAMDCheckinReceiveFailedError", "The service did not start properly on the device." },
+ { "kAMDCheckinResponseFailedError", "The service did not start properly on the device." },
+ { "kAMDCheckinSendFailedError", "The service did not start properly on the device." },
+ { "kAMDCheckinSetupFailedError", "Could not start service on device" },
+ { "kAMDCheckinTimeoutError", "The service did not start properly on the device." },
+ { "kAMDCommCenterNotificationFailedError", "Could not listen for notification from the baseband." },
+ { "kAMDContainerCreationFailedError", "Could not create application container." },
+ { "kAMDContainerP0wnFailedError", "Could not repair permissions on application container." },
+ { "kAMDContainerRemovalFailedError", "Could not remove the application container." },
+ { "kAMDCryptoError", "Could not establish a secure connection to the device." },
+ { "kAMDDeviceDisconnectedError", "This device is no longer connected." },
+ { "kAMDDeviceFamilyNotSupported", "This application does not support this kind of device." },
+ { "kAMDDeviceNotSupportedByThinningError", "This application is not built for this device." },
+ { "kAMDDeviceOSVersionTooLow", "The device OS version is too low." },
+ { "kAMDDeviceRefNoGood", "This device is no longer connected." },
+ { "kAMDDeviceTooNewError", "This application needs to be updated." },
+ { "kAMDDictCreationFailedError", "Could not extract capabilities from the request." },
+ { "kAMDDigestFailedError", "Could not read disk image." },
+ { "kAMDEOFError", "End of file." },
+ { "kAMDEmbeddedProfileInstallFailedError", "Could not install the embedded provisioning profile." },
+ { "kAMDErrorError", "An error occurred." },
+ { "kAMDEscrowLockedError", "Device is not available until first unlock after boot." },
+ { "kAMDExecutableTwiddleFailedError", "Could not change executable permissions on the application." },
+ { "kAMDExistenceCheckFailedError", "Could not check to see if the application already exists." },
+ { "kAMDFMiPProtectedError", "The device is in lost mode." },
+ { "kAMDFileExistsError", "The file already exists." },
+ { "kAMDGeoJSONCaptureFailedError", "Could not save the GeoJSON data." },
+ { "kAMDGetProhibitedError", "Cannot retrieve value from the passcode locked device." },
+ { "kAMDImmutableValueError", "This value cannot be changed." },
+ { "kAMDIncorrectArchitectureError", "This application does not support this device's CPU type." },
+ { "kAMDInstallMapUpdateFailedError", "Could not update the installed applications list." },
+ { "kAMDInstallProhibitedError", "Installation of apps is prohibited by a policy on the device." },
+ { "kAMDInvalidActivationRecordError", "The activation record is not valid." },
+ { "kAMDInvalidArgumentError", "The argument is invalid." },
+ { "kAMDInvalidCheckinError", "Could not start service on device" },
+ { "kAMDInvalidDiskImageError", "The disk image is invalid." },
+ { "kAMDInvalidHostIDError", "The device does not recognize this host." },
+ { "kAMDInvalidPairRecordError", "The host is no longer paired with the device." },
+ { "kAMDInvalidResponseError", "Received an unexpected response from the device." },
+ { "kAMDInvalidSINFError", "The encryption information included with this application is not valid so this application cannot be installed on this device." },
+ { "kAMDInvalidServiceError", "The service is invalid." },
+ { "kAMDInvalidSessionIDError", "The session ID is invalid." },
+ { "kAMDInvalidSymlinkError", "The bundle contained an invalid symlink." },
+ { "kAMDInvalidiTunesMetadataPlistError", "This application's iTunesMetadata.plist is not valid." },
+ { "kAMDIsDirectoryError", "The path is a directory." },
+ { "kAMDLookupFailedError", "Could not list installed applications." },
+ { "kAMDMCChallengeRequired", "A policy on the device requires secure pairing." },
+ { "kAMDMCProtected", "Pairing is prohibited by a policy on the device." },
+ { "kAMDManifestCaptureFailedError", "Could not save the application manifest." },
+ { "kAMDMapGenerationFailedError", "Could not generate the map." },
+ { "kAMDMismatchedApplicationIdentifierEntitlementError", "This application's application-identifier entitlement does not match that of the installed application. These values must match for an upgrade to be allowed." },
+ { "kAMDMismatchedBundleIDSigningIdentifierError", "This application's bundle identifier does not match its code signing identifier." },
+ { "kAMDMissingActivationRecordError", "The activation record could not be found." },
+ { "kAMDMissingApplicationIdentifierError", "Request was missing the application identifier." },
+ { "kAMDMissingAttributeValueError", "Request was missing a required value." },
+ { "kAMDMissingBundleExecutableError", "The application bundle does not contain an executable." },
+ { "kAMDMissingBundleIdentifierError", "The application bundle does not contain a valid identifier." },
+ { "kAMDMissingBundlePathError", "Could not determine the application bundle path." },
+ { "kAMDMissingBundleVersionError", "The bundle's Info.plist does not contain a CFBundleVersion key or its value is not a string." },
+ { "kAMDMissingCommandError", "The request did not contain a command." },
+ { "kAMDMissingContainerError", "Could not find the container for the installed application." },
+ { "kAMDMissingContainerPathError", "Request was missing the container path." },
+ { "kAMDMissingDigestError", "The digest is missing." },
+ { "kAMDMissingHostIDError", "The device does not recognize this host." },
+ { "kAMDMissingImageTypeError", "The image is missing." },
+ { "kAMDMissingKeyError", "The key is missing." },
+ { "kAMDMissingOptionsError", "The options are missing." },
+ { "kAMDMissingPackagePathError", "Request was missing the package path." },
+ { "kAMDMissingPairRecordError", "The host is not paired with the device." },
+ { "kAMDMissingServiceError", "The service is missing." },
+ { "kAMDMissingSessionIDError", "The session ID is missing." },
+ { "kAMDMissingValueError", "The value is missing." },
+ { "kAMDMobileImageMounterAlreadyMounted", "Image is already mounted." },
+ { "kAMDMobileImageMounterDeviceLocked", "The device is locked." },
+ { "kAMDMobileImageMounterDigestCreationFailed", "Could not support development." },
+ { "kAMDMobileImageMounterDigestFailed", "Could not support development." },
+ { "kAMDMobileImageMounterImageInfoCreationFailed", "Could not support development." },
+ { "kAMDMobileImageMounterImageMapLoadFailed", "Could not support development." },
+ { "kAMDMobileImageMounterImageMapStoreFailed", "Could not support development." },
+ { "kAMDMobileImageMounterImageMountFailed", "Could not support development." },
+ { "kAMDMobileImageMounterImageMoveFailed", "Could not support development." },
+ { "kAMDMobileImageMounterImageVerificationFailed", "Could not support development." },
+ { "kAMDMobileImageMounterMissingImagePath", "Could not support development." },
+ { "kAMDMobileImageMounterMissingImageSignature", "Could not support development." },
+ { "kAMDMobileImageMounterMissingImageType", "Could not support development." },
+ { "kAMDMobileImageMounterMountPathMissing", "Could not support development." },
+ { "kAMDMobileImageMounterMountPathNotEmpty", "Could not support development." },
+ { "kAMDMobileImageMounterResponseCreationFailed", "Could not support development." },
+ { "kAMDMobileImageMounterTrustCacheLoadFailed", "Could not support development." },
+ { "kAMDMuxConnectError", "Could not connect to the device." },
+ { "kAMDMuxCreateListenerError", "Could not listen for USB devices." },
+ { "kAMDMuxError", "There was an error with the USB device multiplexor." },
+ { "kAMDMuxGetListenerError", "Could not get the USB listener." },
+ { "kAMDNewsstandArtworkCaptureFailedError", "Could not save the Newsstand artwork." },
+ { "kAMDNoResourcesError", "Could not allocate a resource." },
+ { "kAMDNoSpaceError", "No space is available on the device." },
+ { "kAMDNoWifiSyncSupportError", "Device doesn't support wireless sync." },
+ { "kAMDNotConnectedError", "Not connected to the device." },
+ { "kAMDNotEntitledError", "The requesting application is not allowed to make this request." },
+ { "kAMDNotFoundError", "The file could not be found." },
+ { "kAMDNotificationFailedError", "Could not post a notification." },
+ { "kAMDOverrunError", "There was a buffer overrun." },
+ { "kAMDPackageExtractionFailedError", "Could not open the application package." },
+ { "kAMDPackageInspectionFailedError", "Could not inspect the application package." },
+ { "kAMDPackageMoveFailedError", "Could not move the application package into the staging location." },
+ { "kAMDPackagePatchFailedError", "Could not apply patch update to application." },
+ { "kAMDPairingDialogResponsePendingError", "The user has not yet responded to the pairing request." },
+ { "kAMDPairingProhibitedError", "Pairing only allowed over USB." },
+ { "kAMDPasswordProtectedError", "The device is passcode protected." },
+ { "kAMDPasswordRequiredError", "A passcode is required to be set on the device." },
+ { "kAMDPathConversionFailedError", "Could not convert the path." },
+ { "kAMDPermissionError", "You do not have permission." },
+ { "kAMDPluginCopyFailedError", "Could not copy VPN Plugin into app container." },
+ { "kAMDProhibitedBySupervision", "Operation prohibited on supervised devices." },
+ { "kAMDProvisioningProfileNotValid", "The provisioning profile is not valid." },
+ { "kAMDReadError", "Could not read from the device." },
+ { "kAMDReceiveMessageError", "Could not receive a message from the device." },
+ { "kAMDRemoveProhibitedError", "Cannot remove value on device." },
+ { "kAMDRequireCUPairingBackoffError", "Retry later." },
+ { "kAMDRequireCUPairingCodeError", "Invalid PIN code entered." },
+ { "kAMDRequireCUPairingError", "Cannot pair over network yet" },
+ { "kAMDRestoreContainerFailedError", "Could not restore the application container." },
+ { "kAMDSUFirmwareError", "Could not flash the firmware." },
+ { "kAMDSUPatchError", "Could not patch the file." },
+ { "kAMDSUVerificationError", "The software update package could not be verified." },
+ { "kAMDSavePairRecordFailedError", "Could not save the pairing record." },
+ { "kAMDSeatbeltProfileRemovalFailedError", "Could not remove the application seatbelt profile." },
+ { "kAMDSendMessageError", "Could not send a message to the device." },
+ { "kAMDServiceLimitError", "Too many instances of this service are already running." },
+ { "kAMDServiceProhibitedError", "The service could not be started on the device." },
+ { "kAMDSessionActiveError", "The session is active." },
+ { "kAMDSessionInactiveError", "The session is inactive." },
+ { "kAMDSetProhibitedError", "Cannot set value on device." },
+ { "kAMDStageCreationFailedError", "Could not create the staging directory." },
+ { "kAMDStartServiceError", "The service could not be started." },
+ { "kAMDSuccess", "There was no error." },
+ { "kAMDSymlinkFailedError", "Could not create the symlink." },
+ { "kAMDTimeOutError", "The operation timed out." },
+ { "kAMDTooBigError", "The message is too big." },
+ { "kAMDUndefinedError", "An unknown error occurred." },
+ { "kAMDUninstallProhibitedError", "Uninstallation of apps is prohibited by a policy on the device." },
+ { "kAMDUnknownCommandError", "The device does not recognize the command." },
+ { "kAMDUnknownPacketError", "The packet is unknown." },
+ { "kAMDUnsupportedError", "This operation is unsupported." },
+ { "kAMDUserDeniedPairingError", "The device rejected the pairing attempt." },
+ { "kAMDWriteError", "Could not write to the device." },
+ { "kAMDWrongDroidError", "The device is in recovery mode." },
+ { "kAMDiTunesArtworkCaptureFailedError", "Could not save the iTunes artwork." },
+ { "kAMDiTunesMetadataCaptureFailedError", "Could not save the iTunes metadata." },
+ { "kMobileHouseArrestApplicationLookupFailed", "The requested application is not a user application." },
+ { "kMobileHouseArrestDictionaryFailed", "The request contained an invalid request dictionary." },
+ { "kMobileHouseArrestInstallationLookupFailed", "Could not find the requested application." },
+ { "kMobileHouseArrestInvalidPath", "The requested application contained an invalid data container path." },
+ { "kMobileHouseArrestMissingCommand", "The request was missing a command." },
+ { "kMobileHouseArrestMissingContainer", "The requested application does not contain a valid data container." },
+ { "kMobileHouseArrestMissingIdentifier", "The request was missing an application identifier." },
+ { "kMobileHouseArrestPathConversionFailed", "Could not convert the requested application's data container path." },
+ { "kMobileHouseArrestPathMissing", "The requested application's data container path does not exist." },
+ { "kMobileHouseArrestUnknownCommand", "The request contained an invalid command." },
+};
+
+const int error_id_to_message_count = sizeof(error_id_to_message) / sizeof(error_id_to_message_t);
+
+const char* get_error_message(unsigned int error) {
+ const char* id = NULL;
+
+ // Lookup error localization id
+ for (int i = 0; i < errorcode_to_id_count; i++) {
+ if (errorcode_to_id[i].error == error) {
+ id = errorcode_to_id[i].id;
+ break;
+ }
+ }
+
+ // Lookup error message
+ if (id) {
+ for (int i = 0; i < error_id_to_message_count; i++)
+ if (strcmp(error_id_to_message[i].id, id) == 0)
+ return error_id_to_message[i].message;
+ }
+
+ // If message is not found, then at least return id if it was found, otherwise NULL
+ return id;
+};
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/ios-deploy.m b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/ios-deploy.m
new file mode 100644
index 000000000000..37407219ac41
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/ios-deploy.m
@@ -0,0 +1,2048 @@
+//TODO: don't copy/mount DeveloperDiskImage.dmg if it's already done - Xcode checks this somehow
+
+#import
+#import
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "MobileDevice.h"
+#import "errors.h"
+#import "device_db.h"
+
+#define PREP_CMDS_PATH @"/tmp/%@/fruitstrap-lldb-prep-cmds-"
+#define LLDB_SHELL @"lldb -s %@"
+/*
+ * Startup script passed to lldb.
+ * To see how xcode interacts with lldb, put this into .lldbinit:
+ * log enable -v -f /Users/vargaz/lldb.log lldb all
+ * log enable -v -f /Users/vargaz/gdb-remote.log gdb-remote all
+ */
+
+#define LLDB_PREP_CMDS CFSTR("\
+ platform select remote-ios --sysroot '{symbols_path}'\n\
+ target create \"{disk_app}\"\n\
+ {disk_symbol_path}\
+ script fruitstrap_device_app=\"{device_app}\"\n\
+ script fruitstrap_connect_url=\"connect://127.0.0.1:{device_port}\"\n\
+ target modules search-paths add {modules_search_paths_pairs}\n\
+ command script import \"{python_file_path}\"\n\
+ command script add -f {python_command}.connect_command connect\n\
+ command script add -s asynchronous -f {python_command}.run_command run\n\
+ command script add -s asynchronous -f {python_command}.autoexit_command autoexit\n\
+ command script add -s asynchronous -f {python_command}.safequit_command safequit\n\
+ connect\n\
+")
+
+const char* lldb_prep_no_cmds = "";
+
+const char* lldb_prep_interactive_cmds = "\
+ run\n\
+";
+
+const char* lldb_prep_noninteractive_justlaunch_cmds = "\
+ run\n\
+ safequit\n\
+";
+
+const char* lldb_prep_noninteractive_cmds = "\
+ run\n\
+ autoexit\n\
+";
+
+/*
+ * Some things do not seem to work when using the normal commands like process connect/launch, so we invoke them
+ * through the python interface. Also, Launch () doesn't seem to work when ran from init_module (), so we add
+ * a command which can be used by the user to run it.
+ */
+NSString* LLDB_FRUITSTRAP_MODULE = @
+ #include "lldb.py.h"
+;
+
+typedef void (*iter_callback) (struct afc_connection *, char const *, char *);
+void remove_path_recursively_conn(struct afc_connection *conn, char const *path);
+
+typedef struct am_device * AMDeviceRef;
+mach_error_t AMDeviceSecureStartService(struct am_device *device, CFStringRef service_name, unsigned int *unknown, service_conn_t *handle);
+int AMDeviceSecureTransferPath(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
+int AMDeviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, void *callback, int cbarg);
+int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg);
+mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result);
+int AMDeviceGetInterfaceType(struct am_device *device);
+
+bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, debugserver_only = false, detect_only = false, install = true, uninstall = false, no_wifi = false;
+bool command_only = false;
+char *command = NULL;
+char const*target_filename = NULL;
+char const*upload_pathname = NULL;
+char *bundle_id = NULL;
+bool interactive = true;
+bool justlaunch = false;
+char *app_path = NULL;
+char *disk_symbol_path = NULL;
+char *device_id = NULL;
+char *args = NULL;
+char *list_root = NULL;
+int _timeout = 0;
+int _detectDeadlockTimeout = 0;
+int port = 0; // 0 means "dynamically assigned"
+CFStringRef last_path = NULL;
+service_conn_t gdbfd;
+pid_t parent = 0;
+// PID of child process running lldb
+pid_t child = 0;
+// Signal sent from child to parent process when LLDB finishes.
+const int SIGLLDB = SIGUSR1;
+AMDeviceRef best_device_match = NULL;
+NSString* tmpUUID;
+struct am_device_notification *notify;
+
+// Error codes we report on different failures, so scripts can distinguish between user app exit
+// codes and our exit codes. For non app errors we use codes in reserved 128-255 range.
+const int exitcode_timeout = 252;
+const int exitcode_error = 253;
+const int exitcode_app_crash = 254;
+
+// Checks for MobileDevice.framework errors, tries to print them and exits.
+#define check_error(call) \
+ do { \
+ unsigned int err = (unsigned int)call; \
+ if (err != 0) \
+ { \
+ const char* msg = get_error_message(err); \
+ /*on_error("Error 0x%x: %s " #call, err, msg ? msg : "unknown.");*/ \
+ on_error(@"Error 0x%x: %@ " #call, err, msg ? [NSString stringWithUTF8String:msg] : @"unknown."); \
+ } \
+ } while (false);
+
+void on_error(NSString* format, ...)
+{
+ va_list valist;
+ va_start(valist, format);
+ NSString* str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease];
+ va_end(valist);
+
+ NSLog(@"[ !! ] %@", str);
+
+ exit(exitcode_error);
+}
+
+// Print error message getting last errno and exit
+void on_sys_error(NSString* format, ...) {
+ const char* errstr = strerror(errno);
+
+ va_list valist;
+ va_start(valist, format);
+ NSString* str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease];
+ va_end(valist);
+
+ on_error(@"%@ : %@", str, [NSString stringWithUTF8String:errstr]);
+}
+
+void __NSLogOut(NSString* format, va_list valist) {
+ NSString* str = [[[NSString alloc] initWithFormat:format arguments:valist] autorelease];
+ [[str stringByAppendingString:@"\n"] writeToFile:@"/dev/stdout" atomically:NO encoding:NSUTF8StringEncoding error:nil];
+}
+
+void NSLogOut(NSString* format, ...) {
+ va_list valist;
+ va_start(valist, format);
+ __NSLogOut(format, valist);
+ va_end(valist);
+}
+
+void NSLogVerbose(NSString* format, ...) {
+ if (verbose) {
+ va_list valist;
+ va_start(valist, format);
+ __NSLogOut(format, valist);
+ va_end(valist);
+ }
+}
+
+
+BOOL mkdirp(NSString* path) {
+ NSError* error = nil;
+ BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:path
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&error];
+ return success;
+}
+
+Boolean path_exists(CFTypeRef path) {
+ if (CFGetTypeID(path) == CFStringGetTypeID()) {
+ CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, true);
+ Boolean result = CFURLResourceIsReachable(url, NULL);
+ CFRelease(url);
+ return result;
+ } else if (CFGetTypeID(path) == CFURLGetTypeID()) {
+ return CFURLResourceIsReachable(path, NULL);
+ } else {
+ return false;
+ }
+}
+
+CFStringRef find_path(CFStringRef rootPath, CFStringRef namePattern) {
+ FILE *fpipe = NULL;
+ CFStringRef cf_command;
+
+ if( !path_exists(rootPath) )
+ return NULL;
+
+ if (CFStringFind(namePattern, CFSTR("*"), 0).location == kCFNotFound) {
+ //No wildcards. Let's speed up the search
+ CFStringRef path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), rootPath, namePattern);
+
+ if( path_exists(path) )
+ return path;
+
+ CFRelease(path);
+ return NULL;
+ }
+
+ if (CFStringFind(namePattern, CFSTR("/"), 0).location == kCFNotFound) {
+ cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find '%@' -name '%@' -maxdepth 1 2>/dev/null | sort | tail -n 1"), rootPath, namePattern);
+ } else {
+ cf_command = CFStringCreateWithFormat(NULL, NULL, CFSTR("find '%@' -path '%@/%@' 2>/dev/null | sort | tail -n 1"), rootPath, rootPath, namePattern);
+ }
+
+ char command[1024] = { '\0' };
+ CFStringGetCString(cf_command, command, sizeof(command), kCFStringEncodingUTF8);
+ CFRelease(cf_command);
+
+ if (!(fpipe = (FILE *)popen(command, "r")))
+ on_sys_error(@"Error encountered while opening pipe");
+
+ char buffer[256] = { '\0' };
+
+ fgets(buffer, sizeof(buffer), fpipe);
+ pclose(fpipe);
+
+ strtok(buffer, "\n");
+
+ CFStringRef path = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
+
+ if( CFStringGetLength(path) > 0 && path_exists(path) )
+ return path;
+
+ CFRelease(path);
+ return NULL;
+}
+
+CFStringRef copy_xcode_dev_path() {
+ static char xcode_dev_path[256] = { '\0' };
+ if (strlen(xcode_dev_path) == 0) {
+ FILE *fpipe = NULL;
+ char *command = "xcode-select -print-path";
+
+ if (!(fpipe = (FILE *)popen(command, "r")))
+ on_sys_error(@"Error encountered while opening pipe");
+
+ char buffer[256] = { '\0' };
+
+ fgets(buffer, sizeof(buffer), fpipe);
+ pclose(fpipe);
+
+ strtok(buffer, "\n");
+ strcpy(xcode_dev_path, buffer);
+ }
+ return CFStringCreateWithCString(NULL, xcode_dev_path, kCFStringEncodingUTF8);
+}
+
+const char *get_home() {
+ const char* home = getenv("HOME");
+ if (!home) {
+ struct passwd *pwd = getpwuid(getuid());
+ home = pwd->pw_dir;
+ }
+ return home;
+}
+
+CFStringRef copy_xcode_path_for_impl(CFStringRef rootPath, CFStringRef subPath, CFStringRef search) {
+ CFStringRef searchPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), rootPath, subPath );
+ CFStringRef res = find_path(searchPath, search);
+ CFRelease(searchPath);
+ return res;
+}
+
+CFStringRef copy_xcode_path_for(CFStringRef subPath, CFStringRef search) {
+ CFStringRef xcodeDevPath = copy_xcode_dev_path();
+ CFStringRef defaultXcodeDevPath = CFSTR("/Applications/Xcode.app/Contents/Developer");
+ CFStringRef path = NULL;
+ const char* home = get_home();
+
+ // Try using xcode-select --print-path
+ path = copy_xcode_path_for_impl(xcodeDevPath, subPath, search);
+
+ // If not look in the default xcode location (xcode-select is sometimes wrong)
+ if (path == NULL && CFStringCompare(xcodeDevPath, defaultXcodeDevPath, 0) != kCFCompareEqualTo )
+ path = copy_xcode_path_for_impl(defaultXcodeDevPath, subPath, search);
+
+ // If not look in the users home directory, Xcode can store device support stuff there
+ if (path == NULL) {
+ CFRelease(xcodeDevPath);
+ xcodeDevPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/Library/Developer/Xcode"), home );
+ path = copy_xcode_path_for_impl(xcodeDevPath, subPath, search);
+ }
+
+ CFRelease(xcodeDevPath);
+
+ return path;
+}
+
+device_desc get_device_desc(CFStringRef model) {
+ if (model != NULL) {
+ size_t sz = sizeof(device_db) / sizeof(device_desc);
+ for (size_t i = 0; i < sz; i ++) {
+ if (CFStringCompare(model, device_db[i].model, kCFCompareNonliteral | kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+ return device_db[i];
+ }
+ }
+ }
+
+ device_desc res = device_db[UNKNOWN_DEVICE_IDX];
+
+ res.model = model;
+ res.name = model;
+
+ return res;
+}
+
+char * MYCFStringCopyUTF8String(CFStringRef aString) {
+ if (aString == NULL) {
+ return NULL;
+ }
+
+ CFIndex length = CFStringGetLength(aString);
+ CFIndex maxSize =
+ CFStringGetMaximumSizeForEncoding(length,
+ kCFStringEncodingUTF8);
+ char *buffer = (char *)malloc(maxSize);
+ if (CFStringGetCString(aString, buffer, maxSize,
+ kCFStringEncodingUTF8)) {
+ return buffer;
+ }
+ return NULL;
+}
+
+CFStringRef get_device_full_name(const AMDeviceRef device) {
+ CFStringRef full_name = NULL,
+ device_udid = AMDeviceCopyDeviceIdentifier(device),
+ device_name = NULL,
+ model_name = NULL,
+ sdk_name = NULL,
+ arch_name = NULL;
+
+ AMDeviceConnect(device);
+
+ device_name = AMDeviceCopyValue(device, 0, CFSTR("DeviceName"));
+
+ // Please ensure that device is connected or the name will be unknown
+ CFStringRef model = AMDeviceCopyValue(device, 0, CFSTR("HardwareModel"));
+ device_desc dev;
+ if (model != NULL) {
+ dev = get_device_desc(model);
+ } else {
+ dev= device_db[UNKNOWN_DEVICE_IDX];
+ model = dev.model;
+ }
+ model_name = dev.name;
+ sdk_name = dev.sdk;
+ arch_name = dev.arch;
+
+ NSLogVerbose(@"Hardware Model: %@", model);
+ NSLogVerbose(@"Device Name: %@", device_name);
+ NSLogVerbose(@"Model Name: %@", model_name);
+ NSLogVerbose(@"SDK Name: %@", sdk_name);
+ NSLogVerbose(@"Architecture Name: %@", arch_name);
+
+ if (device_name != NULL) {
+ full_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@, %@, %@, %@) a.k.a. '%@'"), device_udid, model, model_name, sdk_name, arch_name, device_name);
+ } else {
+ full_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@, %@, %@, %@)"), device_udid, model, model_name, sdk_name, arch_name);
+ }
+
+ AMDeviceDisconnect(device);
+
+ if(device_udid != NULL)
+ CFRelease(device_udid);
+ if(device_name != NULL)
+ CFRelease(device_name);
+ if(model_name != NULL)
+ CFRelease(model_name);
+
+ return full_name;
+}
+
+CFStringRef get_device_interface_name(const AMDeviceRef device) {
+ // AMDeviceGetInterfaceType(device) 0=Unknown, 1 = Direct/USB, 2 = Indirect/WIFI
+ switch(AMDeviceGetInterfaceType(device)) {
+ case 1:
+ return CFSTR("USB");
+ case 2:
+ return CFSTR("WIFI");
+ default:
+ return CFSTR("Unknown Connection");
+ }
+}
+
+CFMutableArrayRef get_device_product_version_parts(AMDeviceRef device) {
+ CFStringRef version = AMDeviceCopyValue(device, 0, CFSTR("ProductVersion"));
+ CFArrayRef parts = CFStringCreateArrayBySeparatingStrings(NULL, version, CFSTR("."));
+ CFMutableArrayRef result = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(parts), parts);
+ CFRelease(version);
+ CFRelease(parts);
+ return result;
+}
+
+CFStringRef copy_device_support_path(AMDeviceRef device, CFStringRef suffix) {
+ time_t startTime, endTime;
+ time( &startTime );
+
+ CFStringRef version = NULL;
+ CFStringRef build = AMDeviceCopyValue(device, 0, CFSTR("BuildVersion"));
+ CFStringRef deviceClass = AMDeviceCopyValue(device, 0, CFSTR("DeviceClass"));
+ CFStringRef path = NULL;
+ CFMutableArrayRef version_parts = get_device_product_version_parts(device);
+
+ NSLogVerbose(@"Device Class: %@", deviceClass);
+ NSLogVerbose(@"build: %@", build);
+
+ CFStringRef deviceClassPath[2];
+
+ if (CFStringCompare(CFSTR("AppleTV"), deviceClass, 0) == kCFCompareEqualTo) {
+ deviceClassPath[0] = CFSTR("Platforms/AppleTVOS.platform/DeviceSupport");
+ deviceClassPath[1] = CFSTR("tvOS DeviceSupport");
+ } else {
+ deviceClassPath[0] = CFSTR("Platforms/iPhoneOS.platform/DeviceSupport");
+ deviceClassPath[1] = CFSTR("iOS DeviceSupport");
+ }
+ while (CFArrayGetCount(version_parts) > 0) {
+ version = CFStringCreateByCombiningStrings(NULL, version_parts, CFSTR("."));
+ NSLogVerbose(@"version: %@", version);
+
+ for( int i = 0; i < 2; ++i ) {
+ if (path == NULL) {
+ path = copy_xcode_path_for(deviceClassPath[i], CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%@)/%@"), version, build, suffix));
+ }
+
+ if (path == NULL) {
+ path = copy_xcode_path_for(deviceClassPath[i], CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (*)/%@"), version, suffix));
+ }
+
+ if (path == NULL) {
+ path = copy_xcode_path_for(deviceClassPath[i], CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), version, suffix));
+ }
+
+ if (path == NULL) {
+ path = copy_xcode_path_for(deviceClassPath[i], CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.*/%@"), version, suffix));
+ }
+ }
+
+ CFRelease(version);
+ if (path != NULL) {
+ break;
+ }
+ CFArrayRemoveValueAtIndex(version_parts, CFArrayGetCount(version_parts) - 1);
+ }
+
+ for( int i = 0; i < 2; ++i ) {
+ if (path == NULL) {
+ path = copy_xcode_path_for(deviceClassPath[i], CFStringCreateWithFormat(NULL, NULL, CFSTR("Latest/%@"), suffix));
+ }
+ }
+
+ CFRelease(version_parts);
+ CFRelease(build);
+ CFRelease(deviceClass);
+ if (path == NULL)
+ on_error([NSString stringWithFormat:@"Unable to locate DeviceSupport directory with suffix '%@'. This probably means you don't have Xcode installed, you will need to launch the app manually and logging output will not be shown!", suffix]);
+
+ time( &endTime );
+ NSLogVerbose(@"DeviceSupport directory '%@' was located. It took %.2f seconds", path, difftime(endTime,startTime));
+
+ return path;
+}
+
+void mount_callback(CFDictionaryRef dict, int arg) {
+ CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
+
+ if (CFEqual(status, CFSTR("LookingUpImage"))) {
+ NSLogOut(@"[ 0%%] Looking up developer disk image");
+ } else if (CFEqual(status, CFSTR("CopyingImage"))) {
+ NSLogOut(@"[ 30%%] Copying DeveloperDiskImage.dmg to device");
+ } else if (CFEqual(status, CFSTR("MountingImage"))) {
+ NSLogOut(@"[ 90%%] Mounting developer disk image");
+ }
+}
+
+void mount_developer_image(AMDeviceRef device) {
+ CFStringRef image_path = copy_device_support_path(device, CFSTR("DeveloperDiskImage.dmg"));
+ CFStringRef sig_path = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.signature"), image_path);
+
+ NSLogVerbose(@"Developer disk image: %@", image_path);
+
+ FILE* sig = fopen(CFStringGetCStringPtr(sig_path, kCFStringEncodingMacRoman), "rb");
+ void *sig_buf = malloc(128);
+ assert(fread(sig_buf, 1, 128, sig) == 128);
+ fclose(sig);
+ CFDataRef sig_data = CFDataCreateWithBytesNoCopy(NULL, sig_buf, 128, NULL);
+ CFRelease(sig_path);
+
+ CFTypeRef keys[] = { CFSTR("ImageSignature"), CFSTR("ImageType") };
+ CFTypeRef values[] = { sig_data, CFSTR("Developer") };
+ CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFRelease(sig_data);
+
+ int result = AMDeviceMountImage(device, image_path, options, &mount_callback, 0);
+ if (result == 0) {
+ NSLogOut(@"[ 95%%] Developer disk image mounted successfully");
+ } else if (result == 0xe8000076 /* already mounted */) {
+ NSLogOut(@"[ 95%%] Developer disk image already mounted");
+ } else {
+ on_error(@"Unable to mount developer disk image. (%x)", result);
+ }
+
+ CFRelease(image_path);
+ CFRelease(options);
+}
+
+mach_error_t transfer_callback(CFDictionaryRef dict, int arg) {
+ int percent;
+ CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
+ CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);
+
+ if (CFEqual(status, CFSTR("CopyingFile"))) {
+ CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path"));
+
+ if ((last_path == NULL || !CFEqual(path, last_path)) && !CFStringHasSuffix(path, CFSTR(".ipa"))) {
+ NSLogOut(@"[%3d%%] Copying %@ to device", percent / 2, path);
+ }
+
+ if (last_path != NULL) {
+ CFRelease(last_path);
+ }
+ last_path = CFStringCreateCopy(NULL, path);
+ }
+
+ return 0;
+}
+
+mach_error_t install_callback(CFDictionaryRef dict, int arg) {
+ int percent;
+ CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
+ CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);
+
+ NSLogOut(@"[%3d%%] %@", (percent / 2) + 50, status);
+ return 0;
+}
+
+CFURLRef copy_device_app_url(AMDeviceRef device, CFStringRef identifier) {
+ CFDictionaryRef result = nil;
+
+ NSArray *a = [NSArray arrayWithObjects:
+ @"CFBundleIdentifier", // absolute must
+ @"ApplicationDSID",
+ @"ApplicationType",
+ @"CFBundleExecutable",
+ @"CFBundleDisplayName",
+ @"CFBundleIconFile",
+ @"CFBundleName",
+ @"CFBundleShortVersionString",
+ @"CFBundleSupportedPlatforms",
+ @"CFBundleURLTypes",
+ @"CodeInfoIdentifier",
+ @"Container",
+ @"Entitlements",
+ @"HasSettingsBundle",
+ @"IsUpgradeable",
+ @"MinimumOSVersion",
+ @"Path",
+ @"SignerIdentity",
+ @"UIDeviceFamily",
+ @"UIFileSharingEnabled",
+ @"UIStatusBarHidden",
+ @"UISupportedInterfaceOrientations",
+ nil];
+
+ NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"];
+ CFDictionaryRef options = (CFDictionaryRef)optionsDict;
+
+ check_error(AMDeviceLookupApplications(device, options, &result));
+
+ CFDictionaryRef app_dict = CFDictionaryGetValue(result, identifier);
+ assert(app_dict != NULL);
+
+ CFStringRef app_path = CFDictionaryGetValue(app_dict, CFSTR("Path"));
+ assert(app_path != NULL);
+
+ CFURLRef url = CFURLCreateWithFileSystemPath(NULL, app_path, kCFURLPOSIXPathStyle, true);
+ CFRelease(result);
+ return url;
+}
+
+CFStringRef copy_disk_app_identifier(CFURLRef disk_app_url) {
+ CFURLRef plist_url = CFURLCreateCopyAppendingPathComponent(NULL, disk_app_url, CFSTR("Info.plist"), false);
+ CFReadStreamRef plist_stream = CFReadStreamCreateWithFile(NULL, plist_url);
+ if (!CFReadStreamOpen(plist_stream)) {
+ on_error(@"Cannot read Info.plist file: %@", plist_url);
+ }
+
+ CFPropertyListRef plist = CFPropertyListCreateWithStream(NULL, plist_stream, 0, kCFPropertyListImmutable, NULL, NULL);
+ CFStringRef bundle_identifier = CFRetain(CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier")));
+ CFReadStreamClose(plist_stream);
+
+ CFRelease(plist_url);
+ CFRelease(plist_stream);
+ CFRelease(plist);
+
+ return bundle_identifier;
+}
+
+CFStringRef copy_modules_search_paths_pairs(CFStringRef symbols_path, CFStringRef disk_container, CFStringRef device_container_private, CFStringRef device_container_noprivate )
+{
+ CFMutableStringRef res = CFStringCreateMutable(kCFAllocatorDefault, 0);
+ CFStringAppendFormat(res, NULL, CFSTR("/usr \"%@/usr\""), symbols_path);
+ CFStringAppendFormat(res, NULL, CFSTR(" /System \"%@/System\""), symbols_path);
+
+ CFStringAppendFormat(res, NULL, CFSTR(" \"%@\" \"%@\""), device_container_private, disk_container);
+ CFStringAppendFormat(res, NULL, CFSTR(" \"%@\" \"%@\""), device_container_noprivate, disk_container);
+ CFStringAppendFormat(res, NULL, CFSTR(" /Developer \"%@/Developer\""), symbols_path);
+
+ return res;
+}
+
+void write_lldb_prep_cmds(AMDeviceRef device, CFURLRef disk_app_url) {
+ CFStringRef symbols_path = copy_device_support_path(device, CFSTR("Symbols"));
+ CFMutableStringRef cmds = CFStringCreateMutableCopy(NULL, 0, LLDB_PREP_CMDS);
+ CFRange range = { 0, CFStringGetLength(cmds) };
+
+ CFStringFindAndReplace(cmds, CFSTR("{symbols_path}"), symbols_path, range, 0);
+ range.length = CFStringGetLength(cmds);
+
+ CFMutableStringRef pmodule = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)LLDB_FRUITSTRAP_MODULE);
+
+ CFRange rangeLLDB = { 0, CFStringGetLength(pmodule) };
+
+ CFStringRef exitcode_app_crash_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), exitcode_app_crash);
+ CFStringFindAndReplace(pmodule, CFSTR("{exitcode_app_crash}"), exitcode_app_crash_str, rangeLLDB, 0);
+ rangeLLDB.length = CFStringGetLength(pmodule);
+
+ CFStringRef detect_deadlock_timeout_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), _detectDeadlockTimeout);
+ CFStringFindAndReplace(pmodule, CFSTR("{detect_deadlock_timeout}"), detect_deadlock_timeout_str, rangeLLDB, 0);
+ rangeLLDB.length = CFStringGetLength(pmodule);
+
+ if (args) {
+ CFStringRef cf_args = CFStringCreateWithCString(NULL, args, kCFStringEncodingUTF8);
+ CFStringFindAndReplace(cmds, CFSTR("{args}"), cf_args, range, 0);
+ rangeLLDB.length = CFStringGetLength(pmodule);
+ CFStringFindAndReplace(pmodule, CFSTR("{args}"), cf_args, rangeLLDB, 0);
+
+ //printf("write_lldb_prep_cmds:args: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
+ // CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
+ CFRelease(cf_args);
+ } else {
+ CFStringFindAndReplace(cmds, CFSTR("{args}"), CFSTR(""), range, 0);
+ CFStringFindAndReplace(pmodule, CFSTR("{args}"), CFSTR(""), rangeLLDB, 0);
+ //printf("write_lldb_prep_cmds: [%s][%s]\n", CFStringGetCStringPtr (cmds,kCFStringEncodingMacRoman),
+ // CFStringGetCStringPtr(pmodule, kCFStringEncodingMacRoman));
+ }
+ range.length = CFStringGetLength(cmds);
+
+ CFStringRef bundle_identifier = copy_disk_app_identifier(disk_app_url);
+ CFURLRef device_app_url = copy_device_app_url(device, bundle_identifier);
+ CFStringRef device_app_path = CFURLCopyFileSystemPath(device_app_url, kCFURLPOSIXPathStyle);
+ CFStringFindAndReplace(cmds, CFSTR("{device_app}"), device_app_path, range, 0);
+ range.length = CFStringGetLength(cmds);
+
+ CFStringRef disk_app_path = CFURLCopyFileSystemPath(disk_app_url, kCFURLPOSIXPathStyle);
+ CFStringFindAndReplace(cmds, CFSTR("{disk_app}"), disk_app_path, range, 0);
+ range.length = CFStringGetLength(cmds);
+
+ CFStringRef cf_disk_symbol_path;
+ if (!disk_symbol_path)
+ {
+ cf_disk_symbol_path = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+
+ }
+ else
+ {
+ cf_disk_symbol_path = CFStringCreateWithFormat(NULL, NULL, CFSTR("add-dsym \"%s\"\n"), disk_symbol_path);
+ }
+
+ CFStringFindAndReplace(cmds, CFSTR("{disk_symbol_path}"), cf_disk_symbol_path, range, 0);
+ range.length = CFStringGetLength(cmds);
+
+ CFStringRef device_port = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), port);
+ CFStringFindAndReplace(cmds, CFSTR("{device_port}"), device_port, range, 0);
+ range.length = CFStringGetLength(cmds);
+
+ CFURLRef device_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, device_app_url);
+ CFStringRef device_container_path = CFURLCopyFileSystemPath(device_container_url, kCFURLPOSIXPathStyle);
+ CFMutableStringRef dcp_noprivate = CFStringCreateMutableCopy(NULL, 0, device_container_path);
+ range.length = CFStringGetLength(dcp_noprivate);
+ CFStringFindAndReplace(dcp_noprivate, CFSTR("/private/var/"), CFSTR("/var/"), range, 0);
+ range.length = CFStringGetLength(cmds);
+ CFStringFindAndReplace(cmds, CFSTR("{device_container}"), dcp_noprivate, range, 0);
+ range.length = CFStringGetLength(cmds);
+
+ CFURLRef disk_container_url = CFURLCreateCopyDeletingLastPathComponent(NULL, disk_app_url);
+ CFStringRef disk_container_path = CFURLCopyFileSystemPath(disk_container_url, kCFURLPOSIXPathStyle);
+ CFStringFindAndReplace(cmds, CFSTR("{disk_container}"), disk_container_path, range, 0);
+ range.length = CFStringGetLength(cmds);
+
+ CFStringRef search_paths_pairs = copy_modules_search_paths_pairs(symbols_path, disk_container_path, device_container_path, dcp_noprivate);
+ CFStringFindAndReplace(cmds, CFSTR("{modules_search_paths_pairs}"), search_paths_pairs, range, 0);
+ range.length = CFStringGetLength(cmds);
+ CFRelease(search_paths_pairs);
+
+ NSString* python_file_path = [NSString stringWithFormat:@"/tmp/%@/fruitstrap_", tmpUUID];
+ mkdirp(python_file_path);
+
+ NSString* python_command = @"fruitstrap_";
+ if(device_id != NULL) {
+ python_file_path = [python_file_path stringByAppendingString:[[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
+ python_command = [python_command stringByAppendingString:[[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
+ }
+ python_file_path = [python_file_path stringByAppendingString:@".py"];
+
+ CFStringFindAndReplace(cmds, CFSTR("{python_command}"), (CFStringRef)python_command, range, 0);
+ range.length = CFStringGetLength(cmds);
+ CFStringFindAndReplace(cmds, CFSTR("{python_file_path}"), (CFStringRef)python_file_path, range, 0);
+ range.length = CFStringGetLength(cmds);
+
+ CFDataRef cmds_data = CFStringCreateExternalRepresentation(NULL, cmds, kCFStringEncodingUTF8, 0);
+ NSString* prep_cmds_path = [NSString stringWithFormat:PREP_CMDS_PATH, tmpUUID];
+ if(device_id != NULL) {
+ prep_cmds_path = [prep_cmds_path stringByAppendingString:[[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
+ }
+ FILE *out = fopen([prep_cmds_path UTF8String], "w");
+ fwrite(CFDataGetBytePtr(cmds_data), CFDataGetLength(cmds_data), 1, out);
+ // Write additional commands based on mode we're running in
+ const char* extra_cmds;
+ if (!interactive)
+ {
+ if (justlaunch)
+ extra_cmds = lldb_prep_noninteractive_justlaunch_cmds;
+ else
+ extra_cmds = lldb_prep_noninteractive_cmds;
+ }
+ else if (nostart)
+ extra_cmds = lldb_prep_no_cmds;
+ else
+ extra_cmds = lldb_prep_interactive_cmds;
+ fwrite(extra_cmds, strlen(extra_cmds), 1, out);
+ fclose(out);
+
+ CFDataRef pmodule_data = CFStringCreateExternalRepresentation(NULL, pmodule, kCFStringEncodingUTF8, 0);
+
+ out = fopen([python_file_path UTF8String], "w");
+ fwrite(CFDataGetBytePtr(pmodule_data), CFDataGetLength(pmodule_data), 1, out);
+ fclose(out);
+
+ CFRelease(cmds);
+ CFRelease(symbols_path);
+ CFRelease(bundle_identifier);
+ CFRelease(device_app_url);
+ CFRelease(device_app_path);
+ CFRelease(cf_disk_symbol_path);
+ CFRelease(disk_app_path);
+ CFRelease(device_container_url);
+ CFRelease(device_container_path);
+ CFRelease(dcp_noprivate);
+ CFRelease(disk_container_url);
+ CFRelease(disk_container_path);
+ CFRelease(cmds_data);
+}
+
+CFSocketRef server_socket;
+CFSocketRef lldb_socket;
+CFWriteStreamRef serverWriteStream = NULL;
+CFWriteStreamRef lldbWriteStream = NULL;
+
+int kill_ptree(pid_t root, int signum);
+void
+server_callback (CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
+{
+ ssize_t res;
+
+ if (CFDataGetLength (data) == 0) {
+ // close the socket on which we've got end-of-file, the server_socket.
+ CFSocketInvalidate(s);
+ CFRelease(s);
+ return;
+ }
+ res = write (CFSocketGetNative (lldb_socket), CFDataGetBytePtr (data), CFDataGetLength (data));
+}
+
+void lldb_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
+{
+ //printf ("lldb: %s\n", CFDataGetBytePtr (data));
+
+ if (CFDataGetLength (data) == 0) {
+ // close the socket on which we've got end-of-file, the lldb_socket.
+ CFSocketInvalidate(s);
+ CFRelease(s);
+ return;
+ }
+ write (gdbfd, CFDataGetBytePtr (data), CFDataGetLength (data));
+}
+
+void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
+ CFSocketNativeHandle socket = (CFSocketNativeHandle)(*((CFSocketNativeHandle *)data));
+
+ assert (callbackType == kCFSocketAcceptCallBack);
+ //PRINT ("callback!\n");
+
+ lldb_socket = CFSocketCreateWithNative(NULL, socket, kCFSocketDataCallBack, &lldb_callback, NULL);
+ int flag = 1;
+ int res = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
+ assert(res == 0);
+ CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, lldb_socket, 0), kCFRunLoopCommonModes);
+
+ CFSocketInvalidate(s);
+ CFRelease(s);
+}
+
+void start_remote_debug_server(AMDeviceRef device) {
+
+ check_error(AMDeviceStartService(device, CFSTR("com.apple.debugserver"), &gdbfd, NULL));
+ assert(gdbfd > 0);
+
+ /*
+ * The debugserver connection is through a fd handle, while lldb requires a host/port to connect, so create an intermediate
+ * socket to transfer data.
+ */
+ server_socket = CFSocketCreateWithNative (NULL, gdbfd, kCFSocketDataCallBack, &server_callback, NULL);
+ CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, server_socket, 0), kCFRunLoopCommonModes);
+
+ struct sockaddr_in addr4;
+ memset(&addr4, 0, sizeof(addr4));
+ addr4.sin_len = sizeof(addr4);
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(port);
+ addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ CFSocketRef fdvendor = CFSocketCreate(NULL, PF_INET, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL);
+
+ if (port) {
+ int yes = 1;
+ setsockopt(CFSocketGetNative(fdvendor), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+ }
+
+ CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&addr4, sizeof(addr4));
+
+ CFSocketSetAddress(fdvendor, address_data);
+ CFRelease(address_data);
+ socklen_t addrlen = sizeof(addr4);
+ int res = getsockname(CFSocketGetNative(fdvendor),(struct sockaddr *)&addr4,&addrlen);
+ assert(res == 0);
+ port = ntohs(addr4.sin_port);
+
+ CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, fdvendor, 0), kCFRunLoopCommonModes);
+}
+
+void kill_ptree_inner(pid_t root, int signum, struct kinfo_proc *kp, int kp_len) {
+ int i;
+ for (i = 0; i < kp_len; i++) {
+ if (kp[i].kp_eproc.e_ppid == root) {
+ kill_ptree_inner(kp[i].kp_proc.p_pid, signum, kp, kp_len);
+ }
+ }
+ if (root != getpid()) {
+ kill(root, signum);
+ }
+}
+
+int kill_ptree(pid_t root, int signum) {
+ int mib[3];
+ size_t len;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_ALL;
+ if (sysctl(mib, 3, NULL, &len, NULL, 0) == -1) {
+ return -1;
+ }
+
+ struct kinfo_proc *kp = calloc(1, len);
+ if (!kp) {
+ return -1;
+ }
+
+ if (sysctl(mib, 3, kp, &len, NULL, 0) == -1) {
+ free(kp);
+ return -1;
+ }
+
+ kill_ptree_inner(root, signum, kp, (int)(len / sizeof(struct kinfo_proc)));
+
+ free(kp);
+ return 0;
+}
+
+void killed(int signum) {
+ // SIGKILL needed to kill lldb, probably a better way to do this.
+ kill(0, SIGKILL);
+ _exit(0);
+}
+
+void lldb_finished_handler(int signum)
+{
+ int status = 0;
+ if (waitpid(child, &status, 0) == -1)
+ perror("waitpid failed");
+ _exit(WEXITSTATUS(status));
+}
+
+void bring_process_to_foreground() {
+ if (setpgid(0, 0) == -1)
+ perror("setpgid failed");
+
+ signal(SIGTTOU, SIG_IGN);
+ if (tcsetpgrp(STDIN_FILENO, getpid()) == -1)
+ perror("tcsetpgrp failed");
+ signal(SIGTTOU, SIG_DFL);
+}
+
+void setup_dummy_pipe_on_stdin(int pfd[2]) {
+ if (pipe(pfd) == -1)
+ perror("pipe failed");
+ if (dup2(pfd[0], STDIN_FILENO) == -1)
+ perror("dup2 failed");
+}
+
+void setup_lldb(AMDeviceRef device, CFURLRef url) {
+ CFStringRef device_full_name = get_device_full_name(device),
+ device_interface_name = get_device_interface_name(device);
+
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+ NSLogOut(@"------ Debug phase ------");
+
+ if(AMDeviceGetInterfaceType(device) == 2)
+ {
+ NSLogOut(@"Cannot debug %@ over %@.", device_full_name, device_interface_name);
+ exit(0);
+ }
+
+ NSLogOut(@"Starting debug of %@ connected through %@...", device_full_name, device_interface_name);
+
+ mount_developer_image(device); // put debugserver on the device
+ start_remote_debug_server(device); // start debugserver
+ if (!debugserver_only)
+ write_lldb_prep_cmds(device, url); // dump the necessary lldb commands into a file
+
+ CFRelease(url);
+
+ NSLogOut(@"[100%%] Connecting to remote debug server");
+ NSLogOut(@"-------------------------");
+
+ setpgid(getpid(), 0);
+ signal(SIGHUP, killed);
+ signal(SIGINT, killed);
+ signal(SIGTERM, killed);
+ // Need this before fork to avoid race conditions. For child process we remove this right after fork.
+ signal(SIGLLDB, lldb_finished_handler);
+
+ parent = getpid();
+}
+
+void launch_debugger(AMDeviceRef device, CFURLRef url) {
+ setup_lldb(device, url);
+ int pid = fork();
+ if (pid == 0) {
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGLLDB, SIG_DFL);
+ child = getpid();
+
+ int pfd[2] = {-1, -1};
+ if (isatty(STDIN_FILENO))
+ // If we are running on a terminal, then we need to bring process to foreground for input
+ // to work correctly on lldb's end.
+ bring_process_to_foreground();
+ else
+ // If lldb is running in a non terminal environment, then it freaks out spamming "^D" and
+ // "quit". It seems this is caused by read() on stdin returning EOF in lldb. To hack around
+ // this we setup a dummy pipe on stdin, so read() would block expecting "user's" input.
+ setup_dummy_pipe_on_stdin(pfd);
+
+ NSString* lldb_shell;
+ NSString* prep_cmds = [NSString stringWithFormat:PREP_CMDS_PATH, tmpUUID];
+ lldb_shell = [NSString stringWithFormat:LLDB_SHELL, prep_cmds];
+
+ if(device_id != NULL) {
+ lldb_shell = [lldb_shell stringByAppendingString: [[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
+ }
+
+ int status = system([lldb_shell UTF8String]); // launch lldb
+ if (status == -1)
+ perror("failed launching lldb");
+
+ close(pfd[0]);
+ close(pfd[1]);
+
+ // Notify parent we're exiting
+ kill(parent, SIGLLDB);
+ // Pass lldb exit code
+ _exit(WEXITSTATUS(status));
+ } else if (pid > 0) {
+ child = pid;
+ } else {
+ on_sys_error(@"Fork failed");
+ }
+}
+
+void launch_debugger_and_exit(AMDeviceRef device, CFURLRef url) {
+ setup_lldb(device,url);
+ int pfd[2] = {-1, -1};
+ if (pipe(pfd) == -1)
+ perror("Pipe failed");
+ int pid = fork();
+ if (pid == 0) {
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGLLDB, SIG_DFL);
+ child = getpid();
+
+ if (dup2(pfd[0],STDIN_FILENO) == -1)
+ perror("dup2 failed");
+
+
+ NSString* prep_cmds = [NSString stringWithFormat:PREP_CMDS_PATH, tmpUUID];
+ NSString* lldb_shell = [NSString stringWithFormat:LLDB_SHELL, prep_cmds];
+ if(device_id != NULL) {
+ lldb_shell = [lldb_shell stringByAppendingString:[[NSString stringWithUTF8String:device_id] stringByReplacingOccurrencesOfString:@"-" withString:@"_"]];
+ }
+
+ int status = system([lldb_shell UTF8String]); // launch lldb
+ if (status == -1)
+ perror("failed launching lldb");
+
+ close(pfd[0]);
+
+ // Notify parent we're exiting
+ kill(parent, SIGLLDB);
+ // Pass lldb exit code
+ _exit(WEXITSTATUS(status));
+ } else if (pid > 0) {
+ child = pid;
+ NSLogVerbose(@"Waiting for child [Child: %d][Parent: %d]\n", child, parent);
+ } else {
+ on_sys_error(@"Fork failed");
+ }
+}
+
+void launch_debugserver_only(AMDeviceRef device, CFURLRef url)
+{
+ CFRetain(url);
+ setup_lldb(device,url);
+
+ CFStringRef bundle_identifier = copy_disk_app_identifier(url);
+ CFURLRef device_app_url = copy_device_app_url(device, bundle_identifier);
+ CFStringRef device_app_path = CFURLCopyFileSystemPath(device_app_url, kCFURLPOSIXPathStyle);
+ CFRelease(url);
+
+ NSLogOut(@"debugserver port: %d", port);
+ NSLogOut(@"App path: %@", device_app_path);
+}
+
+CFStringRef get_bundle_id(CFURLRef app_url)
+{
+ if (app_url == NULL)
+ return NULL;
+
+ CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, app_url, CFSTR("Info.plist"), false);
+
+ if (url == NULL)
+ return NULL;
+
+ CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, url);
+ CFRelease(url);
+
+ if (stream == NULL)
+ return NULL;
+
+ CFPropertyListRef plist = NULL;
+ if (CFReadStreamOpen(stream) == TRUE) {
+ plist = CFPropertyListCreateWithStream(NULL, stream, 0,
+ kCFPropertyListImmutable, NULL, NULL);
+ }
+ CFReadStreamClose(stream);
+ CFRelease(stream);
+
+ if (plist == NULL)
+ return NULL;
+
+ const void *value = CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier"));
+ CFStringRef bundle_id = NULL;
+ if (value != NULL)
+ bundle_id = CFRetain(value);
+
+ CFRelease(plist);
+ return bundle_id;
+}
+
+
+void read_dir(service_conn_t afcFd, afc_connection* afc_conn_p, const char* dir,
+ void(*callback)(afc_connection *conn,const char *dir,int file))
+{
+ char *dir_ent;
+
+ afc_connection afc_conn;
+ if (!afc_conn_p) {
+ afc_conn_p = &afc_conn;
+ AFCConnectionOpen(afcFd, 0, &afc_conn_p);
+ }
+
+ afc_dictionary* afc_dict_p;
+ char *key, *val;
+ int not_dir = 0;
+
+ unsigned int code = AFCFileInfoOpen(afc_conn_p, dir, &afc_dict_p);
+ if (code != 0) {
+ // there was a problem reading or opening the file to get info on it, abort
+ return;
+ }
+
+ while((AFCKeyValueRead(afc_dict_p,&key,&val) == 0) && key && val) {
+ if (strcmp(key,"st_ifmt")==0) {
+ not_dir = strcmp(val,"S_IFDIR");
+ break;
+ }
+ }
+ AFCKeyValueClose(afc_dict_p);
+
+ if (not_dir) {
+ NSLogOut(@"%@", [NSString stringWithUTF8String:dir]);
+ } else {
+ NSLogOut(@"%@/", [NSString stringWithUTF8String:dir]);
+ }
+
+ if (not_dir) {
+ if (callback) (*callback)(afc_conn_p, dir, not_dir);
+ return;
+ }
+
+ afc_directory* afc_dir_p;
+ afc_error_t err = AFCDirectoryOpen(afc_conn_p, dir, &afc_dir_p);
+
+ if (err != 0) {
+ // Couldn't open dir - was probably a file
+ return;
+ } else {
+ if (callback) (*callback)(afc_conn_p, dir, not_dir);
+ }
+
+ while(true) {
+ err = AFCDirectoryRead(afc_conn_p, afc_dir_p, &dir_ent);
+
+ if (err != 0 || !dir_ent)
+ break;
+
+ if (strcmp(dir_ent, ".") == 0 || strcmp(dir_ent, "..") == 0)
+ continue;
+
+ char* dir_joined = malloc(strlen(dir) + strlen(dir_ent) + 2);
+ strcpy(dir_joined, dir);
+ if (dir_joined[strlen(dir)-1] != '/')
+ strcat(dir_joined, "/");
+ strcat(dir_joined, dir_ent);
+ read_dir(afcFd, afc_conn_p, dir_joined, callback);
+ free(dir_joined);
+ }
+
+ AFCDirectoryClose(afc_conn_p, afc_dir_p);
+}
+
+
+// Used to send files to app-specific sandbox (Documents dir)
+service_conn_t start_house_arrest_service(AMDeviceRef device) {
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+ service_conn_t houseFd;
+
+ if (bundle_id == NULL) {
+ on_error(@"Bundle id is not specified");
+ }
+
+ CFStringRef cf_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
+ if (AMDeviceStartHouseArrestService(device, cf_bundle_id, 0, &houseFd, 0) != 0)
+ {
+ on_error(@"Unable to find bundle with id: %@", [NSString stringWithUTF8String:bundle_id]);
+ }
+
+ check_error(AMDeviceStopSession(device));
+ check_error(AMDeviceDisconnect(device));
+ CFRelease(cf_bundle_id);
+
+ return houseFd;
+}
+
+char const* get_filename_from_path(char const* path)
+{
+ char const*ptr = path + strlen(path);
+ while (ptr > path)
+ {
+ if (*ptr == '/')
+ break;
+ --ptr;
+ }
+ if (ptr+1 >= path+strlen(path))
+ return NULL;
+ if (ptr == path)
+ return ptr;
+ return ptr+1;
+}
+
+void* read_file_to_memory(char const * path, size_t* file_size)
+{
+ struct stat buf;
+ int err = stat(path, &buf);
+ if (err < 0)
+ {
+ return NULL;
+ }
+
+ *file_size = buf.st_size;
+ FILE* fd = fopen(path, "r");
+ char* content = malloc(*file_size);
+ if (*file_size != 0 && fread(content, *file_size, 1, fd) != 1)
+ {
+ fclose(fd);
+ return NULL;
+ }
+ fclose(fd);
+ return content;
+}
+
+void list_files(AMDeviceRef device)
+{
+ service_conn_t houseFd = start_house_arrest_service(device);
+
+ afc_connection* afc_conn_p;
+ if (AFCConnectionOpen(houseFd, 0, &afc_conn_p) == 0) {
+ read_dir(houseFd, afc_conn_p, list_root?list_root:"/", NULL);
+ AFCConnectionClose(afc_conn_p);
+ }
+}
+
+int app_exists(AMDeviceRef device)
+{
+ if (bundle_id == NULL) {
+ NSLogOut(@"Bundle id is not specified.");
+ return 1;
+ }
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+ CFStringRef cf_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
+
+ NSArray *a = [NSArray arrayWithObjects:@"CFBundleIdentifier", nil];
+ NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"];
+ CFDictionaryRef options = (CFDictionaryRef)optionsDict;
+ CFDictionaryRef result = nil;
+ check_error(AMDeviceLookupApplications(device, options, &result));
+
+ bool appExists = CFDictionaryContainsKey(result, cf_bundle_id);
+ NSLogOut(@"%@", appExists ? @"true" : @"false");
+ CFRelease(cf_bundle_id);
+
+ check_error(AMDeviceStopSession(device));
+ check_error(AMDeviceDisconnect(device));
+ if (appExists)
+ return 0;
+ return -1;
+}
+
+void list_bundle_id(AMDeviceRef device)
+{
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+ NSArray *a = [NSArray arrayWithObjects:@"CFBundleIdentifier", nil];
+ NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:a forKey:@"ReturnAttributes"];
+ CFDictionaryRef options = (CFDictionaryRef)optionsDict;
+ CFDictionaryRef result = nil;
+ check_error(AMDeviceLookupApplications(device, options, &result));
+
+ CFIndex count;
+ count = CFDictionaryGetCount(result);
+ const void *keys[count];
+ CFDictionaryGetKeysAndValues(result, keys, NULL);
+ for(int i = 0; i < count; ++i) {
+ NSLogOut(@"%@", (CFStringRef)keys[i]);
+ }
+
+ check_error(AMDeviceStopSession(device));
+ check_error(AMDeviceDisconnect(device));
+}
+
+void copy_file_callback(afc_connection* afc_conn_p, const char *name,int file)
+{
+ const char *local_name=name;
+
+ if (*local_name=='/') local_name++;
+
+ if (*local_name=='\0') return;
+
+ if (file) {
+ afc_file_ref fref;
+ int err = AFCFileRefOpen(afc_conn_p,name,1,&fref);
+
+ if (err) {
+ fprintf(stderr,"AFCFileRefOpen(\"%s\") failed: %d\n",name,err);
+ return;
+ }
+
+ FILE *fp = fopen(local_name,"w");
+
+ if (fp==NULL) {
+ fprintf(stderr,"fopen(\"%s\",\"w\") failer: %s\n",local_name,strerror(errno));
+ AFCFileRefClose(afc_conn_p,fref);
+ return;
+ }
+
+ char buf[4096];
+ size_t sz=sizeof(buf);
+
+ while (AFCFileRefRead(afc_conn_p,fref,buf,&sz)==0 && sz) {
+ fwrite(buf,sz,1,fp);
+ sz = sizeof(buf);
+ }
+
+ AFCFileRefClose(afc_conn_p,fref);
+ fclose(fp);
+ } else {
+ if (mkdir(local_name,0777) && errno!=EEXIST)
+ fprintf(stderr,"mkdir(\"%s\") failed: %s\n",local_name,strerror(errno));
+ }
+}
+
+void download_tree(AMDeviceRef device)
+{
+ service_conn_t houseFd = start_house_arrest_service(device);
+ afc_connection* afc_conn_p = NULL;
+ char *dirname = NULL;
+
+ list_root = list_root? list_root : "/";
+ target_filename = target_filename? target_filename : ".";
+
+ NSString* targetPath = [NSString pathWithComponents:@[ @(target_filename), @(list_root)] ];
+ mkdirp([targetPath stringByDeletingLastPathComponent]);
+
+ if (AFCConnectionOpen(houseFd, 0, &afc_conn_p) == 0) do {
+
+ if (target_filename) {
+ dirname = strdup(target_filename);
+ mkdirp(@(dirname));
+ if (mkdir(dirname,0777) && errno!=EEXIST) {
+ fprintf(stderr,"mkdir(\"%s\") failed: %s\n",dirname,strerror(errno));
+ break;
+ }
+ if (chdir(dirname)) {
+ fprintf(stderr,"chdir(\"%s\") failed: %s\n",dirname,strerror(errno));
+ break;
+ }
+ }
+
+ read_dir(houseFd, afc_conn_p, list_root, copy_file_callback);
+
+ } while(0);
+
+ if (dirname) free(dirname);
+ if (afc_conn_p) AFCConnectionClose(afc_conn_p);
+}
+
+void upload_dir(AMDeviceRef device, afc_connection* afc_conn_p, NSString* source, NSString* destination);
+void upload_single_file(AMDeviceRef device, afc_connection* afc_conn_p, NSString* sourcePath, NSString* destinationPath);
+
+void upload_file(AMDeviceRef device)
+{
+ service_conn_t houseFd = start_house_arrest_service(device);
+
+ afc_connection afc_conn;
+ afc_connection* afc_conn_p = &afc_conn;
+ AFCConnectionOpen(houseFd, 0, &afc_conn_p);
+
+ // read_dir(houseFd, NULL, "/", NULL);
+
+ if (!target_filename)
+ {
+ target_filename = get_filename_from_path(upload_pathname);
+ }
+
+ NSString* sourcePath = [NSString stringWithUTF8String: upload_pathname];
+ NSString* destinationPath = [NSString stringWithUTF8String: target_filename];
+
+ BOOL isDir;
+ bool exists = [[NSFileManager defaultManager] fileExistsAtPath: sourcePath isDirectory: &isDir];
+ if (!exists)
+ {
+ on_error(@"Could not find file: %s", upload_pathname);
+ }
+ else if (isDir)
+ {
+ upload_dir(device, afc_conn_p, sourcePath, destinationPath);
+ }
+ else
+ {
+ upload_single_file(device, afc_conn_p, sourcePath, destinationPath);
+ }
+ assert(AFCConnectionClose(afc_conn_p) == 0);
+}
+
+void upload_single_file(AMDeviceRef device, afc_connection* afc_conn_p, NSString* sourcePath, NSString* destinationPath) {
+
+ afc_file_ref file_ref;
+
+ // read_dir(houseFd, NULL, "/", NULL);
+
+ size_t file_size;
+ void* file_content = read_file_to_memory([sourcePath fileSystemRepresentation], &file_size);
+
+ if (!file_content)
+ {
+ on_error(@"Could not open file: %@", sourcePath);
+ }
+
+ // Make sure the directory was created
+ {
+ NSString *dirpath = [destinationPath stringByDeletingLastPathComponent];
+ check_error(AFCDirectoryCreate(afc_conn_p, [dirpath fileSystemRepresentation]));
+ }
+
+
+ int ret = AFCFileRefOpen(afc_conn_p, [destinationPath fileSystemRepresentation], 3, &file_ref);
+ if (ret == 0x000a) {
+ on_error(@"Cannot write to %@. Permission error.", destinationPath);
+ }
+ if (ret == 0x0009) {
+ on_error(@"Target %@ is a directory.", destinationPath);
+ }
+ assert(ret == 0);
+ assert(AFCFileRefWrite(afc_conn_p, file_ref, file_content, file_size) == 0);
+ assert(AFCFileRefClose(afc_conn_p, file_ref) == 0);
+
+ free(file_content);
+}
+
+void upload_dir(AMDeviceRef device, afc_connection* afc_conn_p, NSString* source, NSString* destination)
+{
+ check_error(AFCDirectoryCreate(afc_conn_p, [destination fileSystemRepresentation]));
+ destination = [destination copy];
+ for (NSString* item in [[NSFileManager defaultManager] contentsOfDirectoryAtPath: source error: nil])
+ {
+ NSString* sourcePath = [source stringByAppendingPathComponent: item];
+ NSString* destinationPath = [destination stringByAppendingPathComponent: item];
+ BOOL isDir;
+ [[NSFileManager defaultManager] fileExistsAtPath: sourcePath isDirectory: &isDir];
+ if (isDir)
+ {
+ upload_dir(device, afc_conn_p, sourcePath, destinationPath);
+ }
+ else
+ {
+ upload_single_file(device, afc_conn_p, sourcePath, destinationPath);
+ }
+ }
+}
+
+void make_directory(AMDeviceRef device) {
+ service_conn_t houseFd = start_house_arrest_service(device);
+
+ afc_connection afc_conn;
+ afc_connection* afc_conn_p = &afc_conn;
+ AFCConnectionOpen(houseFd, 0, &afc_conn_p);
+
+ assert(AFCDirectoryCreate(afc_conn_p, target_filename) == 0);
+ assert(AFCConnectionClose(afc_conn_p) == 0);
+}
+
+void remove_path(AMDeviceRef device) {
+ service_conn_t houseFd = start_house_arrest_service(device);
+
+ afc_connection afc_conn;
+ afc_connection* afc_conn_p = &afc_conn;
+ AFCConnectionOpen(houseFd, 0, &afc_conn_p);
+
+
+ assert(AFCRemovePath(afc_conn_p, target_filename) == 0);
+ assert(AFCConnectionClose(afc_conn_p) == 0);
+}
+
+void iter_dir(struct afc_connection *conn, char const *path, iter_callback callback) {
+ struct afc_directory *dir;
+ char *dirent;
+
+ if(AFCDirectoryOpen(conn, path, &dir))
+ return;
+
+ while(1) {
+ assert(AFCDirectoryRead(conn, dir, &dirent) == 0);
+
+ if (!dirent)
+ break;
+
+ if (strcmp(dirent, ".") == 0 || strcmp(dirent, "..") == 0)
+ continue;
+
+ callback(conn, path, dirent);
+ }
+}
+
+void remove_path_callback(struct afc_connection *conn, char const *path, char *dirent) {
+ char subdir[255];
+ snprintf(subdir, 255, "%s/%s", path, dirent);
+ remove_path_recursively_conn(conn, subdir);
+}
+
+void remove_path_recursively_conn(struct afc_connection *conn, char const *path) {
+ int ret = AFCRemovePath(conn, path);
+ if(ret == 0 || ret == 8) {
+ // Successfully removed (it was empty) or does not exist
+ NSLogVerbose(@"Deleted %s", path);
+ return;
+ }
+
+ iter_dir(conn, path, (iter_callback) remove_path_callback);
+
+ ret = AFCRemovePath(conn, path);
+
+ if (ret == 10)
+ {
+ // No permissions for deleting folder
+ NSLogVerbose(@"No permissions for delete %s", path);
+ return;
+ }
+
+ NSLogVerbose(@"Deleted %s", path);
+}
+
+void remove_path_recursively(AMDeviceRef device) {
+ service_conn_t houseFd = start_house_arrest_service(device);
+
+ afc_connection afc_conn;
+ afc_connection* afc_conn_p = &afc_conn;
+ AFCConnectionOpen(houseFd, 0, &afc_conn_p);
+
+ remove_path_recursively_conn(afc_conn_p, target_filename);
+
+ assert(AFCConnectionClose(afc_conn_p) == 0);
+}
+
+void uninstall_app(AMDeviceRef device) {
+ CFRetain(device); // don't know if this is necessary?
+
+ NSLogOut(@"------ Uninstall phase ------");
+
+ //Do we already have the bundle_id passed in via the command line? if so, use it.
+ CFStringRef cf_uninstall_bundle_id = NULL;
+ if (bundle_id != NULL)
+ {
+ cf_uninstall_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
+ } else {
+ on_error(@"Error: you need to pass in the bundle id, (i.e. --bundle_id com.my.app)");
+ }
+
+ if (cf_uninstall_bundle_id == NULL) {
+ on_error(@"Error: Unable to get bundle id from user command or package %@.\nUninstall failed.", [NSString stringWithUTF8String:app_path]);
+ } else {
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+ int code = AMDeviceSecureUninstallApplication(0, device, cf_uninstall_bundle_id, 0, NULL, 0);
+ if (code == 0) {
+ NSLogOut(@"[ OK ] Uninstalled package with bundle id %@", cf_uninstall_bundle_id);
+ } else {
+ on_error(@"[ ERROR ] Could not uninstall package with bundle id %@", cf_uninstall_bundle_id);
+ }
+ check_error(AMDeviceStopSession(device));
+ check_error(AMDeviceDisconnect(device));
+ }
+}
+
+void handle_device(AMDeviceRef device) {
+ NSLogVerbose(@"Already found device? %d", found_device);
+
+ CFStringRef found_device_id = AMDeviceCopyDeviceIdentifier(device),
+ device_full_name = get_device_full_name(device),
+ device_interface_name = get_device_interface_name(device);
+
+ if (detect_only) {
+ NSLogOut(@"[....] Found %@ connected through %@.", device_full_name, device_interface_name);
+ found_device = true;
+ return;
+ }
+ if (device_id != NULL) {
+ CFStringRef deviceCFSTR = CFStringCreateWithCString(NULL, device_id, kCFStringEncodingUTF8);
+ if (CFStringCompare(deviceCFSTR, found_device_id, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+ found_device = true;
+ CFRelease(deviceCFSTR);
+ } else {
+ NSLogOut(@"Skipping %@.", device_full_name);
+ return;
+ }
+ } else {
+ device_id = MYCFStringCopyUTF8String(found_device_id);
+ found_device = true;
+ }
+
+ NSLogOut(@"[....] Using %@.", device_full_name);
+
+ if (command_only) {
+ if (strcmp("list", command) == 0) {
+ list_files(device);
+ } else if (strcmp("upload", command) == 0) {
+ upload_file(device);
+ } else if (strcmp("download", command) == 0) {
+ download_tree(device);
+ } else if (strcmp("mkdir", command) == 0) {
+ make_directory(device);
+ } else if (strcmp("rm", command) == 0) {
+ remove_path(device);
+ } else if (strcmp("rm_r", command) == 0) {
+ remove_path_recursively(device);
+ } else if (strcmp("exists", command) == 0) {
+ exit(app_exists(device));
+ } else if (strcmp("uninstall_only", command) == 0) {
+ uninstall_app(device);
+ } else if (strcmp("list_bundle_id", command) == 0) {
+ list_bundle_id(device);
+ }
+ exit(0);
+ }
+
+
+ CFRetain(device); // don't know if this is necessary?
+
+ CFStringRef path = CFStringCreateWithCString(NULL, app_path, kCFStringEncodingUTF8);
+ CFURLRef relative_url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, false);
+ CFURLRef url = CFURLCopyAbsoluteURL(relative_url);
+
+ CFRelease(relative_url);
+
+ if (uninstall) {
+ NSLogOut(@"------ Uninstall phase ------");
+
+ //Do we already have the bundle_id passed in via the command line? if so, use it.
+ CFStringRef cf_uninstall_bundle_id = NULL;
+ if (bundle_id != NULL)
+ {
+ cf_uninstall_bundle_id = CFStringCreateWithCString(NULL, bundle_id, kCFStringEncodingUTF8);
+ } else {
+ cf_uninstall_bundle_id = get_bundle_id(url);
+ }
+
+ if (cf_uninstall_bundle_id == NULL) {
+ on_error(@"Error: Unable to get bundle id from user command or package %@.\nUninstall failed.", [NSString stringWithUTF8String:app_path]);
+ } else {
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+ int code = AMDeviceSecureUninstallApplication(0, device, cf_uninstall_bundle_id, 0, NULL, 0);
+ if (code == 0) {
+ NSLogOut(@"[ OK ] Uninstalled package with bundle id %@", cf_uninstall_bundle_id);
+ } else {
+ on_error(@"[ ERROR ] Could not uninstall package with bundle id %@", cf_uninstall_bundle_id);
+ }
+ check_error(AMDeviceStopSession(device));
+ check_error(AMDeviceDisconnect(device));
+ }
+ }
+
+ if(install) {
+ NSLogOut(@"------ Install phase ------");
+ NSLogOut(@"[ 0%%] Found %@ connected through %@, beginning install", device_full_name, device_interface_name);
+
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+
+ // NOTE: the secure version doesn't seem to require us to start the AFC service
+ service_conn_t afcFd;
+ check_error(AMDeviceSecureStartService(device, CFSTR("com.apple.afc"), NULL, &afcFd));
+ check_error(AMDeviceStopSession(device));
+ check_error(AMDeviceDisconnect(device));
+
+ CFStringRef keys[] = { CFSTR("PackageType") };
+ CFStringRef values[] = { CFSTR("Developer") };
+ CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ //assert(AMDeviceTransferApplication(afcFd, path, NULL, transfer_callback, NULL) == 0);
+ check_error(AMDeviceSecureTransferPath(0, device, url, options, transfer_callback, 0));
+
+ close(afcFd);
+
+
+
+ AMDeviceConnect(device);
+ assert(AMDeviceIsPaired(device));
+ check_error(AMDeviceValidatePairing(device));
+ check_error(AMDeviceStartSession(device));
+
+ // // NOTE: the secure version doesn't seem to require us to start the installation_proxy service
+ // // Although I can't find it right now, I in some code that the first param of AMDeviceSecureInstallApplication was a "dontStartInstallProxy"
+ // // implying this is done for us by iOS already
+
+ //service_conn_t installFd;
+ //assert(AMDeviceSecureStartService(device, CFSTR("com.apple.mobile.installation_proxy"), NULL, &installFd) == 0);
+
+ //mach_error_t result = AMDeviceInstallApplication(installFd, path, options, install_callback, NULL);
+ check_error(AMDeviceSecureInstallApplication(0, device, url, options, install_callback, 0));
+
+ // close(installFd);
+
+ check_error(AMDeviceStopSession(device));
+ check_error(AMDeviceDisconnect(device));
+
+ CFRelease(path);
+ CFRelease(options);
+
+ NSLogOut(@"[100%%] Installed package %@", [NSString stringWithUTF8String:app_path]);
+ }
+
+ if (!debug)
+ exit(0); // no debug phase
+
+ if (justlaunch) {
+ launch_debugger_and_exit(device, url);
+ } else if (debugserver_only) {
+ launch_debugserver_only(device, url);
+ } else {
+ launch_debugger(device, url);
+ }
+}
+
+void device_callback(struct am_device_notification_callback_info *info, void *arg) {
+ switch (info->msg) {
+ case ADNCI_MSG_CONNECTED:
+ if(device_id != NULL || !debug || AMDeviceGetInterfaceType(info->dev) != 2) {
+ if (no_wifi && AMDeviceGetInterfaceType(info->dev) == 2)
+ {
+ NSLogVerbose(@"Skipping wifi device (type: %d)", AMDeviceGetInterfaceType(info->dev));
+ }
+ else
+ {
+ NSLogVerbose(@"Handling device type: %d", AMDeviceGetInterfaceType(info->dev));
+ handle_device(info->dev);
+ }
+ } else if(best_device_match == NULL) {
+ NSLogVerbose(@"Best device match: %d", AMDeviceGetInterfaceType(info->dev));
+ best_device_match = info->dev;
+ CFRetain(best_device_match);
+ }
+ default:
+ break;
+ }
+}
+
+void timeout_callback(CFRunLoopTimerRef timer, void *info) {
+ if (found_device && (!detect_only)) {
+ // Don't need to exit in the justlaunch mode
+ if (justlaunch)
+ return;
+
+ // App running for too long
+ NSLog(@"[ !! ] App is running for too long");
+ exit(exitcode_timeout);
+ return;
+ } else if ((!found_device) && (!detect_only)) {
+ // Device not found timeout
+ if (best_device_match != NULL) {
+ NSLogVerbose(@"Handling best device match.");
+ handle_device(best_device_match);
+
+ CFRelease(best_device_match);
+ best_device_match = NULL;
+ }
+
+ if (!found_device)
+ on_error(@"Timed out waiting for device.");
+ }
+ else
+ {
+ // Device detection timeout
+ if (!debug) {
+ NSLogOut(@"[....] No more devices found.");
+ }
+
+ if (detect_only && !found_device) {
+ exit(exitcode_error);
+ return;
+ } else {
+ int mypid = getpid();
+ if ((parent != 0) && (parent == mypid) && (child != 0))
+ {
+ NSLogVerbose(@"Timeout. Killing child (%d) tree.", child);
+ kill_ptree(child, SIGHUP);
+ }
+ }
+ exit(0);
+ }
+}
+
+void usage(const char* app) {
+ NSLog(
+ @"Usage: %@ [OPTION]...\n"
+ @" -d, --debug launch the app in lldb after installation\n"
+ @" -i, --id the id of the device to connect to\n"
+ @" -c, --detect only detect if the device is connected\n"
+ @" -b, --bundle the path to the app bundle to be installed\n"
+ @" -s, --symbols the path to symbols\n"
+ @" -a, --args command line arguments to pass to the app when launching it\n"
+ @" -t, --timeout number of seconds to wait for a device to be connected\n"
+ @" -u, --unbuffered don't buffer stdout\n"
+ @" -n, --nostart do not start the app when debugging\n"
+ @" -N, --nolldb start debugserver only. do not run lldb\n"
+ @" -I, --noninteractive start in non interactive mode (quit when app crashes or exits)\n"
+ @" -L, --justlaunch just launch the app and exit lldb\n"
+ @" -v, --verbose enable verbose output\n"
+ @" -m, --noinstall directly start debugging without app install (-d not required)\n"
+ @" -p, --port port used for device, default: dynamic\n"
+ @" -r, --uninstall uninstall the app before install (do not use with -m; app cache and data are cleared) \n"
+ @" -9, --uninstall_only uninstall the app ONLY. Use only with -1 \n"
+ @" -1, --bundle_id specify bundle id for list and upload\n"
+ @" -l, --list list files\n"
+ @" -o, --upload upload file\n"
+ @" -w, --download download app tree\n"
+ @" -2, --to use together with up/download file/tree. specify target\n"
+ @" -D, --mkdir make directory on device\n"
+ @" -R, --rm remove file or directory on device (directories must be empty)\n"
+ @" -T, --rm_r remove file or directory recursively on device\n"
+ @" -V, --version print the executable version \n"
+ @" -e, --exists check if the app with given bundle_id is installed or not \n"
+ @" -B, --list_bundle_id list bundle_id \n"
+ @" -W, --no-wifi ignore wifi devices\n"
+ @" --detect_deadlocks start printing backtraces for all threads periodically after specific amount of seconds\n",
+ [NSString stringWithUTF8String:app]);
+}
+
+void show_version() {
+ NSLogOut(@"%@", @
+#include "version.h"
+ );
+}
+
+int main(int argc, char *argv[]) {
+
+ // create a UUID for tmp purposes
+ CFUUIDRef uuid = CFUUIDCreate(NULL);
+ CFStringRef str = CFUUIDCreateString(NULL, uuid);
+ CFRelease(uuid);
+ tmpUUID = [(NSString*)str autorelease];
+
+ static struct option longopts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "id", required_argument, NULL, 'i' },
+ { "bundle", required_argument, NULL, 'b' },
+ { "symbols", required_argument, NULL, 's' },
+ { "args", required_argument, NULL, 'a' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "timeout", required_argument, NULL, 't' },
+ { "unbuffered", no_argument, NULL, 'u' },
+ { "nostart", no_argument, NULL, 'n' },
+ { "nolldb", no_argument, NULL, 'N' },
+ { "noninteractive", no_argument, NULL, 'I' },
+ { "justlaunch", no_argument, NULL, 'L' },
+ { "detect", no_argument, NULL, 'c' },
+ { "version", no_argument, NULL, 'V' },
+ { "noinstall", no_argument, NULL, 'm' },
+ { "port", required_argument, NULL, 'p' },
+ { "uninstall", no_argument, NULL, 'r' },
+ { "uninstall_only", no_argument, NULL, '9'},
+ { "list", optional_argument, NULL, 'l' },
+ { "bundle_id", required_argument, NULL, '1'},
+ { "upload", required_argument, NULL, 'o'},
+ { "download", optional_argument, NULL, 'w'},
+ { "to", required_argument, NULL, '2'},
+ { "mkdir", required_argument, NULL, 'D'},
+ { "rm", required_argument, NULL, 'R'},
+ { "rm_r", required_argument, NULL, 'T'},
+ { "exists", no_argument, NULL, 'e'},
+ { "list_bundle_id", no_argument, NULL, 'B'},
+ { "no-wifi", no_argument, NULL, 'W'},
+ { "detect_deadlocks", required_argument, NULL, 1000 },
+ { NULL, 0, NULL, 0 },
+ };
+ int ch;
+
+ while ((ch = getopt_long(argc, argv, "VmcdvunNrILeD:R:i:b:s:a:t:g:x:p:1:2:o:l::w::9::B::W", longopts, NULL)) != -1)
+ {
+ switch (ch) {
+ case 'm':
+ install = false;
+ debug = true;
+ break;
+ case 'd':
+ debug = true;
+ break;
+ case 'i':
+ device_id = optarg;
+ break;
+ case 'b':
+ app_path = optarg;
+ break;
+ case 's':
+ disk_symbol_path = optarg;
+ break;
+ case 'a':
+ args = optarg;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 't':
+ _timeout = atoi(optarg);
+ break;
+ case 'u':
+ unbuffered = true;
+ break;
+ case 'n':
+ nostart = true;
+ break;
+ case 'N':
+ debugserver_only = true;
+ debug = true;
+ break;
+ case 'I':
+ interactive = false;
+ debug = true;
+ break;
+ case 'L':
+ interactive = false;
+ justlaunch = true;
+ debug = true;
+ break;
+ case 'c':
+ detect_only = true;
+ debug = true;
+ break;
+ case 'V':
+ show_version();
+ return 0;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'r':
+ uninstall = true;
+ break;
+ case '9':
+ command_only = true;
+ command = "uninstall_only";
+ break;
+ case '1':
+ bundle_id = optarg;
+ break;
+ case '2':
+ target_filename = optarg;
+ break;
+ case 'o':
+ command_only = true;
+ upload_pathname = optarg;
+ command = "upload";
+ break;
+ case 'l':
+ command_only = true;
+ command = "list";
+ list_root = optarg;
+ break;
+ case 'w':
+ command_only = true;
+ command = "download";
+ list_root = optarg;
+ break;
+ case 'D':
+ command_only = true;
+ target_filename = optarg;
+ command = "mkdir";
+ break;
+ case 'R':
+ command_only = true;
+ target_filename = optarg;
+ command = "rm";
+ break;
+ case 'T':
+ command_only = true;
+ target_filename = optarg;
+ command = "rm_r";
+ break;
+ case 'e':
+ command_only = true;
+ command = "exists";
+ break;
+ case 'B':
+ command_only = true;
+ command = "list_bundle_id";
+ break;
+ case 'W':
+ no_wifi = true;
+ break;
+ case 1000:
+ _detectDeadlockTimeout = atoi(optarg);
+ break;
+ default:
+ usage(argv[0]);
+ return exitcode_error;
+ }
+ }
+
+ if (!app_path && !detect_only && !command_only) {
+ usage(argv[0]);
+ on_error(@"One of -[b|c|o|l|w|D|R|e|9] is required to proceed!");
+ }
+
+ if (unbuffered) {
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+ }
+
+ if (detect_only && _timeout == 0) {
+ _timeout = 5;
+ }
+
+ if (app_path) {
+ if (access(app_path, F_OK) != 0) {
+ on_sys_error(@"Can't access app path '%@'", [NSString stringWithUTF8String:app_path]);
+ }
+ }
+
+ AMDSetLogLevel(5); // otherwise syslog gets flooded with crap
+ if (_timeout > 0)
+ {
+ CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + _timeout, 0, 0, 0, timeout_callback, NULL);
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
+ NSLogOut(@"[....] Waiting up to %d seconds for iOS device to be connected", _timeout);
+ }
+ else
+ {
+ NSLogOut(@"[....] Waiting for iOS device to be connected");
+ }
+
+ AMDeviceNotificationSubscribe(&device_callback, 0, 0, NULL, ¬ify);
+ CFRunLoopRun();
+}
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/version.h b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/version.h
new file mode 100644
index 000000000000..65be40a270ef
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/ios-deploy/version.h
@@ -0,0 +1 @@
+"1.9.4"
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/scripts/check_reqs.js b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/scripts/check_reqs.js
new file mode 100644
index 000000000000..38c886e67c73
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/scripts/check_reqs.js
@@ -0,0 +1,56 @@
+#!/usr/bin/env node
+
+var util = require('util');
+var os = require('os');
+var child_process = require('child_process');
+
+var XCODEBUILD_MIN_VERSION = 7.0;
+var XCODEBUILD_NOT_FOUND_MESSAGE = util.format('Please install Xcode version %s or greater from the Mac App Store.', XCODEBUILD_MIN_VERSION);
+var TOOL = 'xcodebuild';
+
+var xcode_version = child_process.spawn(TOOL, ['-version']),
+ version_string = '';
+
+xcode_version.stdout.on('data', function (data) {
+ version_string += data;
+});
+
+xcode_version.stderr.on('data', function (data) {
+ console.log('stderr: ' + data);
+});
+
+xcode_version.on('error', function (err) {
+ console.log(util.format('Tool %s was not found. %s', TOOL, XCODEBUILD_NOT_FOUND_MESSAGE));
+});
+
+xcode_version.on('close', function (code) {
+ if (code === 0) {
+ var arr = version_string.match(/^Xcode (\d+\.\d+)/);
+ var ver = arr[1];
+
+ if (os.release() >= '15.0.0' && ver < XCODEBUILD_MIN_VERSION) {
+ console.log(util.format('You need at least Xcode 7.0 when you are on OS X 10.11 El Capitan (you have version %s)', ver));
+ process.exit(1);
+ }
+
+ if (ver < XCODEBUILD_MIN_VERSION) {
+ console.log(util.format('%s : %s. (you have version %s)', TOOL, XCODEBUILD_NOT_FOUND_MESSAGE, ver));
+ }
+
+ if (os.release() >= '15.0.0') { // print the El Capitan warning
+ console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
+ console.log('!!!! WARNING: You are on OS X 10.11 El Capitan or greater, you may need to add the');
+ console.log('!!!! WARNING: `--unsafe-perm=true` flag when running `npm install`');
+ console.log('!!!! WARNING: or else it will fail.');
+ console.log('!!!! WARNING: link:');
+ console.log('!!!! WARNING: https://github.com/phonegap/ios-deploy#os-x-1011-el-capitan-or-greater');
+ console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
+ }
+
+ }
+ process.exit(code);
+});
+
+
+
+
diff --git a/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/scripts/lldb.py b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/scripts/lldb.py
new file mode 100644
index 000000000000..d4157d43feeb
--- /dev/null
+++ b/Engine/Extras/ThirdPartyNotUE/ios-deploy/src/scripts/lldb.py
@@ -0,0 +1,144 @@
+import time
+import os
+import sys
+import shlex
+import lldb
+
+listener = None
+
+def connect_command(debugger, command, result, internal_dict):
+ # These two are passed in by the script which loads us
+ connect_url = internal_dict['fruitstrap_connect_url']
+ error = lldb.SBError()
+
+ # We create a new listener here and will use it for both target and the process.
+ # It allows us to prevent data races when both our code and internal lldb code
+ # try to process STDOUT/STDERR messages
+ global listener
+ listener = lldb.SBListener('iosdeploy_listener')
+
+ listener.StartListeningForEventClass(debugger,
+ lldb.SBTarget.GetBroadcasterClassName(),
+ lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR)
+
+ process = lldb.target.ConnectRemote(listener, connect_url, None, error)
+
+ # Wait for connection to succeed
+ events = []
+ state = (process.GetState() or lldb.eStateInvalid)
+ while state != lldb.eStateConnected:
+ event = lldb.SBEvent()
+ if listener.WaitForEvent(1, event):
+ state = process.GetStateFromEvent(event)
+ events.append(event)
+ else:
+ state = lldb.eStateInvalid
+
+ # Add events back to queue, otherwise lldb freezes
+ for event in events:
+ listener.AddEvent(event)
+
+def run_command(debugger, command, result, internal_dict):
+ device_app = internal_dict['fruitstrap_device_app']
+ args = command.split('--',1)
+ error = lldb.SBError()
+ lldb.target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_app))
+ args_arr = []
+ if len(args) > 1:
+ args_arr = shlex.split(args[1])
+
+ # EPIC: Specify not to use posix to maintain quotes, newlines, etc in passed arguments
+ args_arr = args_arr + shlex.split('{args}', posix=False)
+
+ launchInfo = lldb.SBLaunchInfo(args_arr)
+ global listener
+ launchInfo.SetListener(listener)
+
+ #This env variable makes NSLog, CFLog and os_log messages get mirrored to stderr
+ #https://stackoverflow.com/a/39581193
+ launchInfo.SetEnvironmentEntries(['OS_ACTIVITY_DT_MODE=enable'], True)
+
+ lldb.target.Launch(launchInfo, error)
+ lockedstr = ': Locked'
+ if lockedstr in str(error):
+ print('\\nDevice Locked\\n')
+ os._exit(254)
+ else:
+ print(str(error))
+
+def safequit_command(debugger, command, result, internal_dict):
+ process = lldb.target.process
+ state = process.GetState()
+ if state == lldb.eStateRunning:
+ process.Detach()
+ os._exit(0)
+ elif state > lldb.eStateRunning:
+ os._exit(state)
+ else:
+ print('\\nApplication has not been launched\\n')
+ os._exit(1)
+
+def autoexit_command(debugger, command, result, internal_dict):
+ global listener
+ process = lldb.target.process
+
+ detectDeadlockTimeout = {detect_deadlock_timeout}
+ printBacktraceTime = time.time() + detectDeadlockTimeout if detectDeadlockTimeout > 0 else None
+
+ # This line prevents internal lldb listener from processing STDOUT/STDERR messages. Without it, an order of log writes is incorrect sometimes
+ debugger.GetListener().StopListeningForEvents(process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitSTDOUT | lldb.SBProcess.eBroadcastBitSTDERR )
+
+ event = lldb.SBEvent()
+
+ def ProcessSTDOUT():
+ stdout = process.GetSTDOUT(1024)
+ while stdout:
+ sys.stdout.write(stdout)
+ stdout = process.GetSTDOUT(1024)
+
+ def ProcessSTDERR():
+ stderr = process.GetSTDERR(1024)
+ while stderr:
+ sys.stdout.write(stderr)
+ stderr = process.GetSTDERR(1024)
+
+ while True:
+ if listener.WaitForEvent(1, event) and lldb.SBProcess.EventIsProcessEvent(event):
+ state = lldb.SBProcess.GetStateFromEvent(event)
+ type = event.GetType()
+
+ if type & lldb.SBProcess.eBroadcastBitSTDOUT:
+ ProcessSTDOUT()
+
+ if type & lldb.SBProcess.eBroadcastBitSTDERR:
+ ProcessSTDERR()
+
+ else:
+ state = process.GetState()
+
+ if state != lldb.eStateRunning:
+ # Let's make sure that we drained our streams before exit
+ ProcessSTDOUT()
+ ProcessSTDERR()
+
+ if state == lldb.eStateExited:
+ sys.stdout.write( '\\nPROCESS_EXITED\\n' )
+ os._exit(process.GetExitStatus())
+ elif printBacktraceTime is None and state == lldb.eStateStopped:
+ sys.stdout.write( '\\nPROCESS_STOPPED\\n' )
+ debugger.HandleCommand('thread backtrace all -c 50')
+ os._exit({exitcode_app_crash})
+ elif state == lldb.eStateCrashed:
+ sys.stdout.write( '\\nPROCESS_CRASHED\\n' )
+ debugger.HandleCommand('thread backtrace all -c 50')
+ os._exit({exitcode_app_crash})
+ elif state == lldb.eStateDetached:
+ sys.stdout.write( '\\nPROCESS_DETACHED\\n' )
+ os._exit({exitcode_app_crash})
+ elif printBacktraceTime is not None and time.time() >= printBacktraceTime:
+ printBacktraceTime = None
+ sys.stdout.write( '\\nPRINT_BACKTRACE_TIMEOUT\\n' )
+ debugger.HandleCommand('process interrupt')
+ debugger.HandleCommand('bt all')
+ debugger.HandleCommand('continue')
+ printBacktraceTime = time.time() + 5
diff --git a/Engine/Plugins/2D/Paper2D/Source/Paper2DEditor/Private/Atlasing/PaperAtlasGenerator.cpp b/Engine/Plugins/2D/Paper2D/Source/Paper2DEditor/Private/Atlasing/PaperAtlasGenerator.cpp
index 276013251374..c0c2e21ed709 100644
--- a/Engine/Plugins/2D/Paper2D/Source/Paper2DEditor/Private/Atlasing/PaperAtlasGenerator.cpp
+++ b/Engine/Plugins/2D/Paper2D/Source/Paper2DEditor/Private/Atlasing/PaperAtlasGenerator.cpp
@@ -69,11 +69,16 @@ void FPaperAtlasGenerator::HandleAssetChangedEvent(UPaperSpriteAtlas* Atlas)
bWasTextureRemoved = true;
}
}
+
if (bWasTextureRemoved)
{
MergeAdjacentRects(Atlas);
}
+ // Sort sprites by name first, so that we have a consistent sorting, before sorting by size, so that we have a repeatable
+ // atlas generation.
+ SpritesInNewAtlas.StableSort([](UPaperSprite& A, UPaperSprite& B) { return A.GetPathName() > B.GetPathName(); } );
+
// Sort new sprites by size
struct Local
{
@@ -84,7 +89,7 @@ void FPaperAtlasGenerator::HandleAssetChangedEvent(UPaperSpriteAtlas* Atlas)
return SpriteSize.X * 16384 + SpriteSize.Y; // Sort wider textures first
}
};
- SpritesInNewAtlas.Sort( [](UPaperSprite& A, UPaperSprite& B) { return Local::SpriteSortValue(A) > Local::SpriteSortValue(B); } );
+ SpritesInNewAtlas.StableSort( [](UPaperSprite& A, UPaperSprite& B) { return Local::SpriteSortValue(A) > Local::SpriteSortValue(B); } );
// Add new sprites
TArray ImprovementTestAtlas; // A second atlas to compare wastage
diff --git a/Engine/Plugins/Compositing/Composure/Source/Composure/Classes/ComposureBlueprintLibrary.h b/Engine/Plugins/Compositing/Composure/Source/Composure/Classes/ComposureBlueprintLibrary.h
index 3c34ff98e2a4..7cad969ff847 100644
--- a/Engine/Plugins/Compositing/Composure/Source/Composure/Classes/ComposureBlueprintLibrary.h
+++ b/Engine/Plugins/Compositing/Composure/Source/Composure/Classes/ComposureBlueprintLibrary.h
@@ -58,7 +58,7 @@ class UComposureBlueprintLibrary : public UBlueprintFunctionLibrary
/** Returns the red and green channel factors from percentage of chromatic aberration. */
UFUNCTION(BlueprintPure, Category = "Composure")
- static void GetRedGreenUVFactorsFromChromaticAberration(float ChromaticAberrationAmount, FVector2D& RedGreenUVFactors);
+ static void GetRedGreenUVFactorsFromChromaticAberration(float ChromaticAberrationAmount, FVector2D& RedGreenUVFactors);
/** Returns display gamma of a given player camera manager, or 0 if no scene viewport attached. */
UFUNCTION(BlueprintPure, Category = "Composure")
diff --git a/Engine/Plugins/Developer/OneSkyLocalizationService/Source/OneSkyLocalizationService/Private/OneSkyLocalizationServiceResponseTypes.h b/Engine/Plugins/Developer/OneSkyLocalizationService/Source/OneSkyLocalizationService/Private/OneSkyLocalizationServiceResponseTypes.h
index f35f77395a6b..74421cef5876 100644
--- a/Engine/Plugins/Developer/OneSkyLocalizationService/Source/OneSkyLocalizationService/Private/OneSkyLocalizationServiceResponseTypes.h
+++ b/Engine/Plugins/Developer/OneSkyLocalizationService/Source/OneSkyLocalizationService/Private/OneSkyLocalizationServiceResponseTypes.h
@@ -15,25 +15,25 @@ struct FOneSkyListProjectGroupsResponseMeta
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 status;
+ int32 status;
UPROPERTY()
- int32 record_count;
+ int32 record_count;
UPROPERTY()
- int32 page_count;
+ int32 page_count;
UPROPERTY()
- FString next_page;
+ FString next_page;
UPROPERTY()
- FString prev_page;
+ FString prev_page;
UPROPERTY()
- FString first_page;
+ FString first_page;
UPROPERTY()
- FString last_page;
+ FString last_page;
/** Default constructor. */
FOneSkyListProjectGroupsResponseMeta()
@@ -57,10 +57,10 @@ struct FOneSkyListProjectGroupsResponseDataItem
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FString id;
+ FString id;
UPROPERTY()
- FString name;
+ FString name;
/** Default constructor. */
FOneSkyListProjectGroupsResponseDataItem()
@@ -82,10 +82,10 @@ struct FOneSkyListProjectGroupsResponse
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FOneSkyListProjectGroupsResponseMeta meta;
+ FOneSkyListProjectGroupsResponseMeta meta;
UPROPERTY()
- TArray data;
+ TArray data;
/** Default constructor. */
FOneSkyListProjectGroupsResponse()
@@ -107,8 +107,8 @@ struct FOneSkyShowProjectGroupResponseMeta
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 status;
+ UPROPERTY()
+ int32 status;
/** Default constructor. */
FOneSkyShowProjectGroupResponseMeta()
@@ -125,16 +125,16 @@ struct FOneSkyShowProjectGroupResponseData
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FString id;
+ FString id;
UPROPERTY()
- FString name;
+ FString name;
UPROPERTY()
- int32 description;
+ int32 description;
UPROPERTY()
- FString project_count;
+ FString project_count;
/** Default constructor. */
FOneSkyShowProjectGroupResponseData()
@@ -157,11 +157,11 @@ struct FOneSkyShowProjectGroupResponse
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FOneSkyShowProjectGroupResponseMeta meta;
+ UPROPERTY()
+ FOneSkyShowProjectGroupResponseMeta meta;
UPROPERTY()
- FOneSkyShowProjectGroupResponseData data;
+ FOneSkyShowProjectGroupResponseData data;
/** Default constructor. */
FOneSkyShowProjectGroupResponse()
@@ -183,8 +183,8 @@ struct FOneSkyCreateProjectGroupResponseMeta
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 status;
+ UPROPERTY()
+ int32 status;
/** Default constructor. */
FOneSkyCreateProjectGroupResponseMeta()
@@ -200,20 +200,20 @@ struct FOneSkyCreateProjectGroupResponseBaseLanguage
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FString code;
+ UPROPERTY()
+ FString code;
UPROPERTY()
- FString english_name;
+ FString english_name;
UPROPERTY()
- FString local_name;
+ FString local_name;
UPROPERTY()
- FString locale;
+ FString locale;
UPROPERTY()
- FString region;
+ FString region;
/** Default constructor. */
FOneSkyCreateProjectGroupResponseBaseLanguage()
@@ -233,14 +233,14 @@ struct FOneSkyCreateProjectGroupResponseData
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 id;
+ UPROPERTY()
+ int32 id;
UPROPERTY()
- FString name;
+ FString name;
UPROPERTY()
- FOneSkyCreateProjectGroupResponseBaseLanguage base_language;
+ FOneSkyCreateProjectGroupResponseBaseLanguage base_language;
/** Default constructor. */
FOneSkyCreateProjectGroupResponseData()
@@ -262,11 +262,11 @@ struct FOneSkyCreateProjectGroupResponse
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FOneSkyCreateProjectGroupResponseMeta meta;
+ UPROPERTY()
+ FOneSkyCreateProjectGroupResponseMeta meta;
UPROPERTY()
- FOneSkyCreateProjectGroupResponseData data;
+ FOneSkyCreateProjectGroupResponseData data;
/** Default constructor. */
FOneSkyCreateProjectGroupResponse()
@@ -288,11 +288,11 @@ struct FOneSkyListProjectGroupLanguagesResponseMeta
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 status;
+ UPROPERTY()
+ int32 status;
UPROPERTY()
- int32 record_count;
+ int32 record_count;
/** Default constructor. */
FOneSkyListProjectGroupLanguagesResponseMeta()
@@ -309,23 +309,23 @@ struct FOneSkyListProjectGroupLanguagesResponseDataItem
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FString code;
+ UPROPERTY()
+ FString code;
UPROPERTY()
- FString english_name;
+ FString english_name;
UPROPERTY()
- FString local_name;
+ FString local_name;
UPROPERTY()
- FString locale;
+ FString locale;
UPROPERTY()
- FString region;
+ FString region;
UPROPERTY()
- bool is_base_language;
+ bool is_base_language;
/** Default constructor. */
FOneSkyListProjectGroupLanguagesResponseDataItem()
@@ -350,11 +350,11 @@ struct FOneSkyListProjectGroupLanguagesResponse
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FOneSkyListProjectGroupLanguagesResponseMeta meta;
+ UPROPERTY()
+ FOneSkyListProjectGroupLanguagesResponseMeta meta;
UPROPERTY()
- TArray data;
+ TArray data;
/** Default constructor. */
FOneSkyListProjectGroupLanguagesResponse()
@@ -376,11 +376,11 @@ struct FOneSkyListProjectsInGroupResponseMeta
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 status;
+ UPROPERTY()
+ int32 status;
UPROPERTY()
- int32 record_count;
+ int32 record_count;
/** Default constructor. */
FOneSkyListProjectsInGroupResponseMeta()
@@ -398,10 +398,10 @@ struct FOneSkyListProjectsInGroupResponseDataItem
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 id;
+ int32 id;
UPROPERTY()
- FString name;
+ FString name;
/** Default constructor. */
FOneSkyListProjectsInGroupResponseDataItem()
@@ -426,7 +426,7 @@ struct FOneSkyListProjectsInGroupResponse
FOneSkyListProjectsInGroupResponseMeta meta;
UPROPERTY()
- TArray data;
+ TArray data;
/** Default constructor. */
FOneSkyListProjectsInGroupResponse()
@@ -448,8 +448,8 @@ struct FOneSkyShowProjectResponseMeta
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 status;
+ UPROPERTY()
+ int32 status;
/** Default constructor. */
FOneSkyShowProjectResponseMeta()
@@ -466,11 +466,11 @@ struct FOneSkyShowProjectResponseProjectType
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FString code;
+ UPROPERTY()
+ FString code;
UPROPERTY()
- FString name;
+ FString name;
/** Default constructor. */
FOneSkyShowProjectResponseProjectType()
@@ -488,22 +488,22 @@ struct FOneSkyShowProjectResponseData
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 id;
+ int32 id;
UPROPERTY()
- FString name;
+ FString name;
UPROPERTY()
- FString description;
+ FString description;
UPROPERTY()
- FOneSkyShowProjectResponseProjectType project_type;
+ FOneSkyShowProjectResponseProjectType project_type;
UPROPERTY()
- int32 string_count;
+ int32 string_count;
UPROPERTY()
- int32 word_count;
+ int32 word_count;
/** Default constructor. */
FOneSkyShowProjectResponseData()
@@ -527,11 +527,11 @@ struct FOneSkyShowProjectResponse
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FOneSkyShowProjectResponseMeta meta;
+ UPROPERTY()
+ FOneSkyShowProjectResponseMeta meta;
UPROPERTY()
- FOneSkyShowProjectResponseData data;
+ FOneSkyShowProjectResponseData data;
/** Default constructor. */
FOneSkyShowProjectResponse()
@@ -554,7 +554,7 @@ struct FOneSkyCreateProjectResponseMeta
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 status;
+ int32 status;
/** Default constructor. */
FOneSkyCreateProjectResponseMeta()
@@ -572,10 +572,10 @@ struct FOneSkyCreateProjectResponseProjectType
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FString code;
+ FString code;
UPROPERTY()
- FString name;
+ FString name;
/** Default constructor. */
FOneSkyCreateProjectResponseProjectType()
@@ -593,16 +593,16 @@ struct FOneSkyCreateProjectResponseData
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 id;
+ int32 id;
UPROPERTY()
- FString name;
+ FString name;
UPROPERTY()
- FString description;
+ FString description;
UPROPERTY()
- FOneSkyShowProjectResponseProjectType project_type;
+ FOneSkyShowProjectResponseProjectType project_type;
/** Default constructor. */
FOneSkyCreateProjectResponseData()
@@ -651,10 +651,10 @@ struct FOneSkyListProjectLanguagesResponseMeta
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 status;
+ int32 status;
UPROPERTY()
- int32 record_count;
+ int32 record_count;
/** Default constructor. */
FOneSkyListProjectLanguagesResponseMeta()
@@ -671,29 +671,29 @@ struct FOneSkyListProjectLanguagesResponseDataItem
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FString code;
+ UPROPERTY()
+ FString code;
UPROPERTY()
- FString english_name;
+ FString english_name;
UPROPERTY()
- FString local_name;
+ FString local_name;
UPROPERTY()
- FString locale;
+ FString locale;
UPROPERTY()
- FString region;
+ FString region;
UPROPERTY()
- bool is_base_language;
+ bool is_base_language;
UPROPERTY()
- bool is_ready_to_publish;
+ bool is_ready_to_publish;
UPROPERTY()
- FString translation_progress;
+ FString translation_progress;
/** Default constructor. */
FOneSkyListProjectLanguagesResponseDataItem()
@@ -721,10 +721,10 @@ struct FOneSkyListProjectLanguagesResponse
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FOneSkyListProjectLanguagesResponseMeta meta;
+ FOneSkyListProjectLanguagesResponseMeta meta;
UPROPERTY()
- TArray data;
+ TArray data;
/** Default constructor. */
FOneSkyListProjectLanguagesResponse()
@@ -746,8 +746,8 @@ struct FOneSkyTranslationStatusResponseMeta
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 status;
+ UPROPERTY()
+ int32 status;
/** Default constructor. */
FOneSkyTranslationStatusResponseMeta()
@@ -765,19 +765,19 @@ struct FOneSkyTranslationStatusResponseLocale
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FString code;
+ FString code;
UPROPERTY()
- FString english_name;
+ FString english_name;
UPROPERTY()
- FString local_name;
+ FString local_name;
UPROPERTY()
- FString locale;
+ FString locale;
UPROPERTY()
- FString region;
+ FString region;
/** Default constructor. */
FOneSkyTranslationStatusResponseLocale()
@@ -798,19 +798,19 @@ struct FOneSkyTranslationStatusResponseData
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FString file_name;
+ FString file_name;
UPROPERTY()
- FOneSkyTranslationStatusResponseLocale locale;
+ FOneSkyTranslationStatusResponseLocale locale;
UPROPERTY()
- FString progress;
+ FString progress;
UPROPERTY()
- int32 string_count;
+ int32 string_count;
UPROPERTY()
- int32 word_count;
+ int32 word_count;
/** Default constructor. */
FOneSkyTranslationStatusResponseData()
@@ -835,10 +835,10 @@ struct FOneSkyTranslationStatusResponse
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FOneSkyTranslationStatusResponseMeta meta;
+ FOneSkyTranslationStatusResponseMeta meta;
UPROPERTY()
- FOneSkyTranslationStatusResponseData data;
+ FOneSkyTranslationStatusResponseData data;
/** Default constructor. */
FOneSkyTranslationStatusResponse()
@@ -860,26 +860,26 @@ struct FOneSkyListUploadedFilesResponseMeta
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 status;
+ UPROPERTY()
+ int32 status;
UPROPERTY()
- int32 record_count;
+ int32 record_count;
UPROPERTY()
- int32 page_count;
+ int32 page_count;
UPROPERTY()
- FString next_page;
+ FString next_page;
UPROPERTY()
- FString prev_page;
+ FString prev_page;
UPROPERTY()
- FString first_page;
+ FString first_page;
UPROPERTY()
- FString last_page;
+ FString last_page;
/** Default constructor. */
FOneSkyListUploadedFilesResponseMeta()
@@ -902,10 +902,10 @@ struct FOneSkyListUploadedFilesResponseLastImport
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 id;
+ int32 id;
UPROPERTY()
- FString status;
+ FString status;
/** Default constructor. */
FOneSkyListUploadedFilesResponseLastImport()
@@ -924,19 +924,19 @@ struct FOneSkyListUploadedFilesResponseDataItem
GENERATED_USTRUCT_BODY()
UPROPERTY()
- FString file_name;
+ FString file_name;
UPROPERTY()
- int32 string_count;
+ int32 string_count;
UPROPERTY()
- FOneSkyListUploadedFilesResponseLastImport last_import;
+ FOneSkyListUploadedFilesResponseLastImport last_import;
UPROPERTY()
- FString uploaded_at;
+ FString uploaded_at;
UPROPERTY()
- int32 uploaded_at_timestamp;
+ int32 uploaded_at_timestamp;
/** Default constructor. */
FOneSkyListUploadedFilesResponseDataItem()
@@ -960,11 +960,11 @@ struct FOneSkyListUploadedFilesResponse
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FOneSkyListUploadedFilesResponseMeta meta;
+ UPROPERTY()
+ FOneSkyListUploadedFilesResponseMeta meta;
UPROPERTY()
- TArray data;
+ TArray data;
/** Default constructor. */
FOneSkyListUploadedFilesResponse()
@@ -986,7 +986,7 @@ struct FOneSkyUploadFileResponseMeta
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 status;
+ int32 status;
/** Default constructor. */
FOneSkyUploadFileResponseMeta()
@@ -1003,20 +1003,20 @@ struct FOneSkyUploadFileResponseLanguage
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FString code;
+ UPROPERTY()
+ FString code;
UPROPERTY()
- FString english_name;
+ FString english_name;
UPROPERTY()
- FString local_name;
+ FString local_name;
UPROPERTY()
- FString locale;
+ FString locale;
UPROPERTY()
- FString region;
+ FString region;
/** Default constructor. */
FOneSkyUploadFileResponseLanguage()
@@ -1037,13 +1037,13 @@ struct FOneSkyUploadedFileResponseImport
GENERATED_USTRUCT_BODY()
UPROPERTY()
- int32 id;
+ int32 id;
UPROPERTY()
- FString created_at;
+ FString created_at;
UPROPERTY()
- int32 created_at_timestamp;
+ int32 created_at_timestamp;
/** Default constructor. */
FOneSkyUploadedFileResponseImport()
@@ -1061,17 +1061,17 @@ struct FOneSkyUploadFileResponseData
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FString name;
+ UPROPERTY()
+ FString name;
UPROPERTY()
- FString format;
+ FString format;
UPROPERTY()
- FOneSkyUploadFileResponseLanguage language;
+ FOneSkyUploadFileResponseLanguage language;
UPROPERTY()
- FOneSkyUploadedFileResponseImport import;
+ FOneSkyUploadedFileResponseImport import;
/** Default constructor. */
FOneSkyUploadFileResponseData()
@@ -1098,7 +1098,7 @@ struct FOneSkyUploadFileResponse
FOneSkyUploadFileResponseMeta meta;
UPROPERTY()
- FOneSkyUploadFileResponseData data;
+ FOneSkyUploadFileResponseData data;
/** Default constructor. */
FOneSkyUploadFileResponse()
@@ -1120,26 +1120,26 @@ struct FOneSkyListPhraseCollectionsResponseMeta
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- int32 status;
+ UPROPERTY()
+ int32 status;
UPROPERTY()
- int32 record_count;
+ int32 record_count;
UPROPERTY()
- int32 page_count;
+ int32 page_count;
UPROPERTY()
- FString next_page;
+ FString next_page;
UPROPERTY()
- FString prev_page;
+ FString prev_page;
UPROPERTY()
- FString first_page;
+ FString first_page;
UPROPERTY()
- FString last_page;
+ FString last_page;
/** Default constructor. */
FOneSkyListPhraseCollectionsResponseMeta()
@@ -1161,14 +1161,14 @@ struct FOneSkyListPhraseCollectionsResponseDataItem
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FString key;
+ UPROPERTY()
+ FString key;
UPROPERTY()
- FString created_at;
+ FString created_at;
UPROPERTY()
- int32 created_at_timestamp;
+ int32 created_at_timestamp;
/** Default constructor. */
FOneSkyListPhraseCollectionsResponseDataItem()
@@ -1190,11 +1190,11 @@ struct FOneSkyListPhraseCollectionsResponse
{
GENERATED_USTRUCT_BODY()
- UPROPERTY()
- FOneSkyListPhraseCollectionsResponseMeta meta;
+ UPROPERTY()
+ FOneSkyListPhraseCollectionsResponseMeta meta;
UPROPERTY()
- TArray data;
+ TArray data;
/** Default constructor. */
FOneSkyListPhraseCollectionsResponse()
@@ -1237,16 +1237,16 @@ struct FOneSkyShowImportTaskResponseLocale
FString code;
UPROPERTY()
- FString english_name;
+ FString english_name;
UPROPERTY()
- FString local_name;
+ FString local_name;
UPROPERTY()
- FString locale;
+ FString locale;
UPROPERTY()
- FString region;
+ FString region;
/** Default constructor. */
FOneSkyShowImportTaskResponseLocale()
diff --git a/Engine/Plugins/Developer/OneSkyLocalizationService/Source/OneSkyLocalizationService/Private/OneSkyLocalizationServiceSettings.h b/Engine/Plugins/Developer/OneSkyLocalizationService/Source/OneSkyLocalizationService/Private/OneSkyLocalizationServiceSettings.h
index 57cc6e30cc05..894451e31d94 100644
--- a/Engine/Plugins/Developer/OneSkyLocalizationService/Source/OneSkyLocalizationService/Private/OneSkyLocalizationServiceSettings.h
+++ b/Engine/Plugins/Developer/OneSkyLocalizationService/Source/OneSkyLocalizationService/Private/OneSkyLocalizationServiceSettings.h
@@ -23,19 +23,19 @@ struct FOneSkyLocalizationTargetSetting
* The GUID of the LocalizationTarget that these OneSky settings are for
*/
UPROPERTY()
- FGuid TargetGuid;
+ FGuid TargetGuid;
/**
* The id of the OneSky Project this target belongs to
*/
UPROPERTY()
- int32 OneSkyProjectId;
+ int32 OneSkyProjectId;
/**
* The name of the file that corresponds to this target
*/
UPROPERTY()
- FString OneSkyFileName;
+ FString OneSkyFileName;
};
diff --git a/Engine/Plugins/Developer/PerforceSourceControl/Source/PerforceSourceControl/Private/PerforceSourceControlOperations.cpp b/Engine/Plugins/Developer/PerforceSourceControl/Source/PerforceSourceControl/Private/PerforceSourceControlOperations.cpp
index 8530ffab085d..0ceeb63187f6 100644
--- a/Engine/Plugins/Developer/PerforceSourceControl/Source/PerforceSourceControl/Private/PerforceSourceControlOperations.cpp
+++ b/Engine/Plugins/Developer/PerforceSourceControl/Source/PerforceSourceControl/Private/PerforceSourceControlOperations.cpp
@@ -754,7 +754,10 @@ static void ParseUpdateStatusResults(const FP4RecordSet& InRecords, const TArray
const FString OtherOpenRecordKey = FString::Printf(TEXT("otherOpen%d"), OpenIdx);
const FString OtherOpenRecordValue = ClientRecord(OtherOpenRecordKey);
- BranchModification.OtherUserCheckedOut += OtherOpenRecordValue;
+ int32 AtIndex = OtherOpenRecordValue.Find(TEXT("@"));
+ FString OtherOpenUser = AtIndex == INDEX_NONE ? FString(TEXT("")) : OtherOpenRecordValue.Left(AtIndex);
+ BranchModification.OtherUserCheckedOut += OtherOpenUser + TEXT(" @ ") + BranchModification.BranchName;
+
if (OpenIdx < OtherOpenNum - 1)
{
BranchModification.OtherUserCheckedOut += TEXT(", ");
@@ -792,6 +795,15 @@ static void ParseUpdateStatusResults(const FP4RecordSet& InRecords, const TArray
FPerforceSourceControlState& State = OutStates.Last();
State.DepotFilename = DepotFileName;
+ FString Branch;
+ FString BranchFile;
+ if (DepotFileName.Split(ContentRoot, &Branch, &BranchFile))
+ {
+ // Sanitize
+ Branch.RemoveFromEnd(FString(TEXT("/")));
+ BranchFile.RemoveFromStart(FString(TEXT("/")));
+ }
+
State.State = EPerforceState::ReadOnly;
if (Action.Len() > 0 && Action == TEXT("add"))
{
@@ -821,7 +833,10 @@ static void ParseUpdateStatusResults(const FP4RecordSet& InRecords, const TArray
const FString OtherOpenRecordKey = FString::Printf(TEXT("otherOpen%d"), OpenIdx);
const FString OtherOpenRecordValue = ClientRecord(OtherOpenRecordKey);
- State.OtherUserCheckedOut += OtherOpenRecordValue;
+ int32 AtIndex = OtherOpenRecordValue.Find(TEXT("@"));
+ FString OtherOpenUser = AtIndex == INDEX_NONE ? FString(TEXT("")) : OtherOpenRecordValue.Left(AtIndex);
+ State.OtherUserCheckedOut += OtherOpenUser + TEXT(" @ ") + Branch;
+
if(OpenIdx < OtherOpenNum - 1)
{
State.OtherUserCheckedOut += TEXT(", ");
@@ -839,11 +854,6 @@ static void ParseUpdateStatusResults(const FP4RecordSet& InRecords, const TArray
State.State = EPerforceState::NotInDepot;
}
- // If checked out or modified in another branch, setup state
- FString BranchFile;
- FileName.Replace(TEXT("\\"), TEXT("/")).Split(ContentRoot, nullptr, &BranchFile);
- BranchFile.RemoveFromStart(TEXT("/"));
-
State.HeadBranch = TEXT("*CurrentBranch");
State.HeadAction = HeadAction;
State.HeadModTime = FCString::Atoi64(*ClientRecord(TEXT("headModTime")));
diff --git a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/AssetManagerEditorModule.cpp b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/AssetManagerEditorModule.cpp
index 84f1896c808d..fffd99af2296 100644
--- a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/AssetManagerEditorModule.cpp
+++ b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/AssetManagerEditorModule.cpp
@@ -33,6 +33,7 @@
#include "Widgets/SToolTip.h"
#include "PropertyCustomizationHelpers.h"
#include "Toolkits/AssetEditorToolkit.h"
+#include "Toolkits/AssetEditorManager.h"
#include "LevelEditor.h"
#include "GraphEditorModule.h"
#include "AssetData.h"
@@ -60,6 +61,7 @@
#include "Misc/MessageDialog.h"
#include "UObject/ConstructorHelpers.h"
#include "Misc/HotReloadInterface.h"
+#include "Misc/ScopedSlowTask.h"
#define LOCTEXT_NAMESPACE "AssetManagerEditor"
@@ -297,6 +299,7 @@ public:
void DumpAssetDependencies(const TArray& Args);
virtual void OpenAssetAuditUI(TArray SelectedAssets) override;
+ virtual void OpenAssetAuditUI(TArray SelectedIdentifiers) override;
virtual void OpenAssetAuditUI(TArray SelectedPackages) override;
virtual void OpenReferenceViewerUI(TArray SelectedIdentifiers) override;
virtual void OpenReferenceViewerUI(TArray SelectedPackages) override;
@@ -352,8 +355,6 @@ private:
FDelegateHandle ReferenceViewerDelegateHandle;
FDelegateHandle AssetEditorExtenderDelegateHandle;
FDelegateHandle LevelEditorExtenderDelegateHandle;
- FDelegateHandle HotReloadDelegateHandle;
- FDelegateHandle MarkPackageDirtyDelegateHandle;
TWeakPtr AssetAuditTab;
TWeakPtr ReferenceViewerTab;
@@ -379,6 +380,7 @@ private:
TSharedRef OnExtendLevelEditor(const TSharedRef CommandList, const TArray SelectedActors);
void OnHotReload(bool bWasTriggeredAutomatically);
void OnMarkPackageDirty(UPackage* Pkg, bool bWasDirty);
+ void OnEditAssetIdentifiers(TArray AssetIdentifiers);
TSharedRef SpawnAssetAuditTab(const FSpawnTabArgs& Args);
TSharedRef SpawnReferenceViewerTab(const FSpawnTabArgs& Args);
@@ -499,9 +501,15 @@ void FAssetManagerEditorModule::StartupModule()
// Register for hot reload and package dirty to invalidate data
IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked("HotReload");
- HotReloadDelegateHandle = HotReloadSupport.OnHotReload().AddRaw(this, &FAssetManagerEditorModule::OnHotReload);
+ HotReloadSupport.OnHotReload().AddRaw(this, &FAssetManagerEditorModule::OnHotReload);
- MarkPackageDirtyDelegateHandle = UPackage::PackageMarkedDirtyEvent.AddRaw(this, &FAssetManagerEditorModule::OnMarkPackageDirty);
+ UPackage::PackageMarkedDirtyEvent.AddRaw(this, &FAssetManagerEditorModule::OnMarkPackageDirty);
+
+ // Register view callbacks
+ FEditorDelegates::OnOpenReferenceViewer.AddRaw(this, &FAssetManagerEditorModule::OpenReferenceViewerUI);
+ FEditorDelegates::OnOpenSizeMap.AddRaw(this, &FAssetManagerEditorModule::OpenSizeMapUI);
+ FEditorDelegates::OnOpenAssetAudit.AddRaw(this, &FAssetManagerEditorModule::OpenAssetAuditUI);
+ FEditorDelegates::OnEditAssetIdentifiers.AddRaw(this, &FAssetManagerEditorModule::OnEditAssetIdentifiers);
}
}
@@ -584,10 +592,14 @@ void FAssetManagerEditorModule::ShutdownModule()
if (FModuleManager::Get().IsModuleLoaded("HotReload"))
{
IHotReloadInterface& HotReloadSupport = FModuleManager::GetModuleChecked("HotReload");
- HotReloadSupport.OnHotReload().Remove(HotReloadDelegateHandle);
+ HotReloadSupport.OnHotReload().RemoveAll(this);
}
- UPackage::PackageMarkedDirtyEvent.Remove(MarkPackageDirtyDelegateHandle);
+ UPackage::PackageMarkedDirtyEvent.RemoveAll(this);
+ FEditorDelegates::OnOpenReferenceViewer.RemoveAll(this);
+ FEditorDelegates::OnOpenSizeMap.RemoveAll(this);
+ FEditorDelegates::OnOpenAssetAudit.RemoveAll(this);
+ FEditorDelegates::OnEditAssetIdentifiers.RemoveAll(this);
}
}
@@ -640,6 +652,16 @@ void FAssetManagerEditorModule::OpenAssetAuditUI(TArray SelectedAsse
}
}
+void FAssetManagerEditorModule::OpenAssetAuditUI(TArray SelectedIdentifiers)
+{
+ FGlobalTabmanager::Get()->InvokeTab(AssetAuditTabName);
+
+ if (AssetAuditUI.IsValid())
+ {
+ AssetAuditUI.Pin()->AddAssetsToList(SelectedIdentifiers, false);
+ }
+}
+
void FAssetManagerEditorModule::OpenAssetAuditUI(TArray SelectedPackages)
{
FGlobalTabmanager::Get()->InvokeTab(AssetAuditTabName);
@@ -916,6 +938,36 @@ void FAssetManagerEditorModule::OnMarkPackageDirty(UPackage* Pkg, bool bWasDirty
}
}
+void FAssetManagerEditorModule::OnEditAssetIdentifiers(TArray AssetIdentifiers)
+{
+ // Handle default package behavior
+ TArray AssetsToLoad;
+ for (FAssetIdentifier AssetIdentifier : AssetIdentifiers)
+ {
+ if (AssetIdentifier.IsPackage())
+ {
+ TArray Assets;
+ AssetRegistry->GetAssetsByPackageName(AssetIdentifier.PackageName, AssetsToLoad);
+ }
+ }
+
+ if (AssetsToLoad.Num() > 0)
+ {
+ FScopedSlowTask SlowTask(0, LOCTEXT("LoadingSelectedObject", "Editing assets..."));
+ SlowTask.MakeDialogDelayed(.1f);
+ FAssetEditorManager& EditorManager = FAssetEditorManager::Get();
+
+ for (const FAssetData& AssetData : AssetsToLoad)
+ {
+ UObject* EditObject = AssetData.GetAsset();
+ if (EditObject)
+ {
+ EditorManager.OpenEditorForAsset(EditObject);
+ }
+ }
+ }
+}
+
bool FAssetManagerEditorModule::GetManagedPackageListForAssetData(const FAssetData& AssetData, TSet& ManagedPackageSet)
{
InitializeRegistrySources(true);
diff --git a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/ReferenceViewer/SReferenceViewer.cpp b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/ReferenceViewer/SReferenceViewer.cpp
index 2a13b5d157de..66204d912dee 100644
--- a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/ReferenceViewer/SReferenceViewer.cpp
+++ b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/ReferenceViewer/SReferenceViewer.cpp
@@ -25,7 +25,6 @@
#include "ICollectionManager.h"
#include "CollectionManagerModule.h"
#include "Editor.h"
-#include "Toolkits/AssetEditorManager.h"
#include "AssetManagerEditorCommands.h"
#include "EditorWidgetsModule.h"
#include "Toolkits/GlobalEditorCommonCommands.h"
@@ -907,7 +906,7 @@ void SReferenceViewer::RegisterActions()
ReferenceViewerActions->MapAction(
FAssetManagerEditorCommands::Get().OpenSelectedInAssetEditor,
FExecuteAction::CreateSP(this, &SReferenceViewer::OpenSelectedInAssetEditor),
- FCanExecuteAction::CreateSP(this, &SReferenceViewer::HasExactlyOnePackageNodeSelected));
+ FCanExecuteAction::CreateSP(this, &SReferenceViewer::HasAtLeastOneRealNodeSelected));
ReferenceViewerActions->MapAction(
FAssetManagerEditorCommands::Get().ReCenterGraph,
@@ -972,12 +971,12 @@ void SReferenceViewer::RegisterActions()
ReferenceViewerActions->MapAction(
FAssetManagerEditorCommands::Get().ViewSizeMap,
FExecuteAction::CreateSP(this, &SReferenceViewer::ViewSizeMap),
- FCanExecuteAction());
+ FCanExecuteAction::CreateSP(this, &SReferenceViewer::HasAtLeastOneRealNodeSelected));
ReferenceViewerActions->MapAction(
FAssetManagerEditorCommands::Get().ViewAssetAudit,
FExecuteAction::CreateSP(this, &SReferenceViewer::ViewAssetAudit),
- FCanExecuteAction());
+ FCanExecuteAction::CreateSP(this, &SReferenceViewer::HasAtLeastOneRealNodeSelected));
}
void SReferenceViewer::ShowSelectionInContentBrowser()
@@ -1005,29 +1004,22 @@ void SReferenceViewer::ShowSelectionInContentBrowser()
void SReferenceViewer::OpenSelectedInAssetEditor()
{
+ TArray IdentifiersToEdit;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry"));
TSet SelectedNodes = GraphEditorPtr->GetSelectedNodes();
for (FGraphPanelSelectionSet::TConstIterator It(SelectedNodes); It; ++It)
{
if (UEdGraphNode_Reference* ReferenceNode = Cast(*It))
{
- if (!ReferenceNode->IsPackage() && !ReferenceNode->IsCollapsed())
+ if (!ReferenceNode->IsCollapsed())
{
- if (AssetRegistryModule.Get().EditSearchableName(ReferenceNode->GetIdentifier()))
- {
- // Was handled by callback, otherwise fall back to default behavior
- return;
- }
+ ReferenceNode->GetAllIdentifiers(IdentifiersToEdit);
}
}
}
- UObject* SelectedObject = GetObjectFromSingleSelectedNode();
-
- if( SelectedObject )
- {
- FAssetEditorManager::Get().OpenEditorForAsset(SelectedObject);
- }
+ // This will handle packages as well as searchable names if other systems register
+ FEditorDelegates::OnEditAssetIdentifiers.Broadcast(IdentifiersToEdit);
}
void SReferenceViewer::ReCenterGraph()
@@ -1406,6 +1398,27 @@ bool SReferenceViewer::HasAtLeastOnePackageNodeSelected() const
return false;
}
+bool SReferenceViewer::HasAtLeastOneRealNodeSelected() const
+{
+ if (GraphEditorPtr.IsValid())
+ {
+ TSet SelectedNodes = GraphEditorPtr->GetSelectedNodes();
+ for (UObject* Node : SelectedNodes)
+ {
+ UEdGraphNode_Reference* ReferenceNode = Cast(Node);
+ if (ReferenceNode)
+ {
+ if (!ReferenceNode->IsCollapsed())
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
void SReferenceViewer::OnInitialAssetRegistrySearchComplete()
{
if ( GraphObj )
diff --git a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/ReferenceViewer/SReferenceViewer.h b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/ReferenceViewer/SReferenceViewer.h
index 0f85e681e6b1..a909a1991f60 100644
--- a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/ReferenceViewer/SReferenceViewer.h
+++ b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/ReferenceViewer/SReferenceViewer.h
@@ -141,6 +141,7 @@ private:
bool HasExactlyOneNodeSelected() const;
bool HasExactlyOnePackageNodeSelected() const;
bool HasAtLeastOnePackageNodeSelected() const;
+ bool HasAtLeastOneRealNodeSelected() const;
void OnInitialAssetRegistrySearchComplete();
EActiveTimerReturnType TriggerZoomToFit(double InCurrentTime, float InDeltaTime);
diff --git a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/SAssetAuditBrowser.cpp b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/SAssetAuditBrowser.cpp
index dc48d0464645..f077d9f3ffc4 100644
--- a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/SAssetAuditBrowser.cpp
+++ b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/SAssetAuditBrowser.cpp
@@ -564,6 +564,22 @@ void SAssetAuditBrowser::AddAssetsToList(const TArray& AssetsToView,
AddAssetsToList(AssetNames, bReplaceExisting);
}
+void SAssetAuditBrowser::AddAssetsToList(const TArray& AssetsToView, bool bReplaceExisting)
+{
+ TArray AssetNames;
+
+ for (const FAssetIdentifier& AssetToView : AssetsToView)
+ {
+ FName PackageName = AssetToView.PackageName;
+ if (!PackageName.IsNone())
+ {
+ AssetNames.AddUnique(PackageName);
+ }
+ }
+
+ AddAssetsToList(AssetNames, bReplaceExisting);
+}
+
void SAssetAuditBrowser::AddAssetsToList(const TArray& PackageNamesToView, bool bReplaceExisting)
{
AssetHistory.Insert(TSet(), ++CurrentAssetHistoryIndex);
diff --git a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/SAssetAuditBrowser.h b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/SAssetAuditBrowser.h
index 6c3fc52e84a3..b1558b2a50eb 100644
--- a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/SAssetAuditBrowser.h
+++ b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Private/SAssetAuditBrowser.h
@@ -33,6 +33,7 @@ public:
/** Adds assets to current management view */
void AddAssetsToList(const TArray& AssetsToView, bool bReplaceExisting);
void AddAssetsToList(const TArray& AssetsToView, bool bReplaceExisting);
+ void AddAssetsToList(const TArray& AssetsToView, bool bReplaceExisting);
void AddAssetsToList(const TArray& PackageNamesToView, bool bReplaceExisting);
/** Called when the current registry source changes */
diff --git a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Public/AssetManagerEditorModule.h b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Public/AssetManagerEditorModule.h
index ffcd36b99c63..72cfc35c92e6 100644
--- a/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Public/AssetManagerEditorModule.h
+++ b/Engine/Plugins/Editor/AssetManagerEditor/Source/AssetManagerEditor/Public/AssetManagerEditorModule.h
@@ -186,6 +186,7 @@ public:
/** Opens asset management UI, with selected assets. Pass as value so it can be used in delegates */
virtual void OpenAssetAuditUI(TArray SelectedAssets) = 0;
+ virtual void OpenAssetAuditUI(TArray SelectedIdentifiers) = 0;
virtual void OpenAssetAuditUI(TArray SelectedPackages) = 0;
/** Spawns reference viewer, showing selected packages or identifiers */
diff --git a/Engine/Plugins/Editor/BlueprintMaterialTextureNodes/Source/BlueprintMaterialTextureNodes/Public/BlueprintMaterialTextureNodesBPLibrary.h b/Engine/Plugins/Editor/BlueprintMaterialTextureNodes/Source/BlueprintMaterialTextureNodes/Public/BlueprintMaterialTextureNodesBPLibrary.h
index 6d108db6d405..db8cbc7e0921 100644
--- a/Engine/Plugins/Editor/BlueprintMaterialTextureNodes/Source/BlueprintMaterialTextureNodes/Public/BlueprintMaterialTextureNodesBPLibrary.h
+++ b/Engine/Plugins/Editor/BlueprintMaterialTextureNodes/Source/BlueprintMaterialTextureNodes/Public/BlueprintMaterialTextureNodesBPLibrary.h
@@ -33,21 +33,21 @@ class UBlueprintMaterialTextureNodesBPLibrary : public UBlueprintFunctionLibrary
* Samples a texel from a Texture 2D with VectorDisplacement Compression
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Texture2D Sample UV Editor Only", Keywords = "Read Texture UV"), Category = Rendering)
- static FLinearColor Texture2D_SampleUV_EditorOnly(UTexture2D* Texture, FVector2D UV);
+ static FLinearColor Texture2D_SampleUV_EditorOnly(UTexture2D* Texture, FVector2D UV);
/**
* Samples an array of values from a Texture Render Target 2D. Currently only 4 channel formats are supported.
* Only works in the editor
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Render Target Sample Rectangle Editor Only", Keywords = "Sample Render Target Rectangle"), Category = Rendering)
- static TArray RenderTarget_SampleRectangle_EditorOnly(UTextureRenderTarget2D* InRenderTarget, FLinearColor InRect);
+ static TArray RenderTarget_SampleRectangle_EditorOnly(UTextureRenderTarget2D* InRenderTarget, FLinearColor InRect);
/**
* Samples a value from a Texture Render Target 2D. Currently only 4 channel formats are supported.
* Only works in the editor
*/
UFUNCTION(BlueprintPure, meta = (DisplayName = "Render Target Sample UV Editor Only", Keywords = "Sample Render Target UV"), Category = Rendering)
- static FLinearColor RenderTarget_SampleUV_EditorOnly(UTextureRenderTarget2D* InRenderTarget, FVector2D UV);
+ static FLinearColor RenderTarget_SampleUV_EditorOnly(UTextureRenderTarget2D* InRenderTarget, FVector2D UV);
/**
@@ -55,57 +55,57 @@ class UBlueprintMaterialTextureNodesBPLibrary : public UBlueprintFunctionLibrary
* Only works in the editor
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Create MIC Editor Only", Keywords = "Create MIC", UnsafeDuringActorConstruction = "true"), Category = Rendering)
- static UMaterialInstanceConstant* CreateMIC_EditorOnly(UMaterialInterface* Material, FString Name = "MIC_");
+ static UMaterialInstanceConstant* CreateMIC_EditorOnly(UMaterialInterface* Material, FString Name = "MIC_");
UFUNCTION()
- static void UpdateMIC(UMaterialInstanceConstant* MIC);
+ static void UpdateMIC(UMaterialInstanceConstant* MIC);
/**
* Sets a Scalar Parameter value in a Material Instance Constant
* Only works in the editor
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set MIC Scalar Parameter Editor Only", Keywords = "Set MIC Scalar Parameter", UnsafeDuringActorConstruction = "true"), Category = Rendering)
- static bool SetMICScalarParam_EditorOnly(UMaterialInstanceConstant* Material, FString ParamName = "test", float Value = 0.0f);
+ static bool SetMICScalarParam_EditorOnly(UMaterialInstanceConstant* Material, FString ParamName = "test", float Value = 0.0f);
/**
* Sets a Vector Parameter value in a Material Instance Constant
* Only works in the editor
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set MIC Vector Parameter Editor Only", Keywords = "Set MIC Vector Parameter", UnsafeDuringActorConstruction = "true"), Category = Rendering)
- static bool SetMICVectorParam_EditorOnly(UMaterialInstanceConstant* Material, FString ParamName, FLinearColor Value = FLinearColor(0, 0, 0, 0));
+ static bool SetMICVectorParam_EditorOnly(UMaterialInstanceConstant* Material, FString ParamName, FLinearColor Value = FLinearColor(0, 0, 0, 0));
/**
* Sets a Texture Parameter value in a Material Instance Constant
* Only works in the editor
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set MIC Texture Parameter Editor Only", Keywords = "Set MIC Texture Parameter", UnsafeDuringActorConstruction = "true"), Category = Rendering)
- static bool SetMICTextureParam_EditorOnly(UMaterialInstanceConstant* Material, FString ParamName, UTexture2D* Texture = nullptr);
+ static bool SetMICTextureParam_EditorOnly(UMaterialInstanceConstant* Material, FString ParamName, UTexture2D* Texture = nullptr);
/**
* Overrides the Shading Model of a Material Instance Constant
* Only works in the editor
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set MIC Shading Model Editor Only", Keywords = "Set MIC Shading Model", UnsafeDuringActorConstruction = "true"), Category = Rendering)
- static bool SetMICShadingModel_EditorOnly(UMaterialInstanceConstant* Material, TEnumAsByte ShadingModel = MSM_DefaultLit);
+ static bool SetMICShadingModel_EditorOnly(UMaterialInstanceConstant* Material, TEnumAsByte ShadingModel = MSM_DefaultLit);
/**
* Overrides the Blend Mode of a Material Instance Constant
* Only works in the editor
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set MIC Blend Mode Editor Only", Keywords = "Set MIC Blend Mode", UnsafeDuringActorConstruction = "true"), Category = Rendering)
- static bool SetMICBlendMode_EditorOnly(UMaterialInstanceConstant* Material, TEnumAsByte BlendMode = BLEND_Opaque);
+ static bool SetMICBlendMode_EditorOnly(UMaterialInstanceConstant* Material, TEnumAsByte BlendMode = BLEND_Opaque);
/**
* Overrides the Two Sided setting of a Material Instance Constant
* Only works in the editor
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set MIC Two Sided Editor Only", Keywords = "Set MIC Two Sided", UnsafeDuringActorConstruction = "true"), Category = Rendering)
- static bool SetMICTwoSided_EditorOnly(UMaterialInstanceConstant* Material, bool TwoSided = false);
+ static bool SetMICTwoSided_EditorOnly(UMaterialInstanceConstant* Material, bool TwoSided = false);
/**
* Overrides the Blend Mode of a Material Instance Constant
* Only works in the editor
*/
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set MIC Dithered LOD Editor Only", Keywords = "Set MIC Dithered LOD Transition", UnsafeDuringActorConstruction = "true"), Category = Rendering)
- static bool SetMICDitheredLODTransition_EditorOnly(UMaterialInstanceConstant* Material, bool DitheredLODTransition = false);
+ static bool SetMICDitheredLODTransition_EditorOnly(UMaterialInstanceConstant* Material, bool DitheredLODTransition = false);
};
diff --git a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Private/EditorSkeletalMeshLibrary.cpp b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Private/EditorSkeletalMeshLibrary.cpp
index 8f61b421ad79..7c4b6cf651c1 100644
--- a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Private/EditorSkeletalMeshLibrary.cpp
+++ b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Private/EditorSkeletalMeshLibrary.cpp
@@ -8,7 +8,7 @@
#include "Engine/SkeletalMesh.h"
#include "LODUtilities.h"
-bool UEditorSkeletalMeshLibrary::RegenerateLOD(USkeletalMesh* SkeletalMesh, int32 NewLODCount /*= 0*/, bool bRegenerateEvenIfImported /*= false*/)
+bool UEditorSkeletalMeshLibrary::RegenerateLOD(USkeletalMesh* SkeletalMesh, int32 NewLODCount /*= 0*/, bool bRegenerateEvenIfImported /*= false*/, bool bGenerateBaseLOD /*= false*/)
{
TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true);
@@ -23,7 +23,7 @@ bool UEditorSkeletalMeshLibrary::RegenerateLOD(USkeletalMesh* SkeletalMesh, int3
return false;
}
- return FLODUtilities::RegenerateLOD(SkeletalMesh, NewLODCount, bRegenerateEvenIfImported);
+ return FLODUtilities::RegenerateLOD(SkeletalMesh, NewLODCount, bRegenerateEvenIfImported, bGenerateBaseLOD);
}
int32 UEditorSkeletalMeshLibrary::GetNumVerts(USkeletalMesh* SkeletalMesh, int32 LODIndex)
diff --git a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorFilterLibrary.h b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorFilterLibrary.h
index 28c04c36c71b..5272a605db0c 100644
--- a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorFilterLibrary.h
+++ b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorFilterLibrary.h
@@ -40,9 +40,9 @@ public:
* @return The filtered list.
*/
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Utilities | Filter", meta = (DisplayName="Filter by Class", DeterminesOutputType = "ObjectClass"))
- static TArray ByClass(const TArray& TargetArray
- , TSubclassOf ObjectClass
- , EEditorScriptingFilterType FilterType = EEditorScriptingFilterType::Include);
+ static TArray ByClass(const TArray& TargetArray
+ , TSubclassOf ObjectClass
+ , EEditorScriptingFilterType FilterType = EEditorScriptingFilterType::Include);
/**
* Filter the array based on the Object's ID name.
diff --git a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorSkeletalMeshLibrary.h b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorSkeletalMeshLibrary.h
index 95592a78e5c7..cecf439c297d 100644
--- a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorSkeletalMeshLibrary.h
+++ b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorSkeletalMeshLibrary.h
@@ -26,10 +26,11 @@ public:
* Otherwise, it will use the current LOD and regenerate
* @param bRegenerateEvenIfImported If this is true, it only regenerate even if this LOD was imported before
* If false, it will regenerate for only previously auto generated ones
+ * @param bGenerateBaseLOD If this is true and there is some reduction data, the base LOD will be reduce according to the settings
* @return true if succeed. If mesh reduction is not available this will return false.
*/
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | SkeletalMesh", meta = (ScriptMethod))
- static bool RegenerateLOD(USkeletalMesh* SkeletalMesh, int32 NewLODCount = 0, bool bRegenerateEvenIfImported = false);
+ static bool RegenerateLOD(USkeletalMesh* SkeletalMesh, int32 NewLODCount = 0, bool bRegenerateEvenIfImported = false, bool bGenerateBaseLOD = false);
/** Get number of mesh vertices for an LOD of a Skeletal Mesh
*
diff --git a/Engine/Plugins/Editor/FacialAnimation/Source/FacialAnimation/Private/AudioCurveSourceComponent.cpp b/Engine/Plugins/Editor/FacialAnimation/Source/FacialAnimation/Private/AudioCurveSourceComponent.cpp
index 4119cb65797c..ee90bd38904a 100644
--- a/Engine/Plugins/Editor/FacialAnimation/Source/FacialAnimation/Private/AudioCurveSourceComponent.cpp
+++ b/Engine/Plugins/Editor/FacialAnimation/Source/FacialAnimation/Private/AudioCurveSourceComponent.cpp
@@ -44,10 +44,9 @@ void UAudioCurveSourceComponent::CacheCurveData()
// cache audio sync curve
static const FName AudioSyncCurve(TEXT("Audio"));
- FRichCurve** RichCurvePtr = CurveData->RowMap.Find(AudioSyncCurve);
- if (RichCurvePtr != nullptr && *RichCurvePtr != nullptr)
+ if (FRealCurve* Curve = CurveData->FindCurve(AudioSyncCurve, FString(), false))
{
- CachedSyncPreRoll = (*RichCurvePtr)->GetFirstKey().Time;
+ CachedSyncPreRoll = Curve->GetKeyTime(Curve->GetFirstKeyHandle());
}
CachedDuration = Sound->Duration;
@@ -158,10 +157,9 @@ float UAudioCurveSourceComponent::GetCurveValue_Implementation(FName CurveName)
UCurveTable* CurveTable = CachedCurveTable.Get();
if (CurveTable)
{
- FRichCurve** Curve = CurveTable->RowMap.Find(CurveName);
- if (Curve != nullptr && *Curve != nullptr)
+ if (FRealCurve* Curve = CurveTable->FindCurve(CurveName, FString(), false))
{
- return (*Curve)->Eval(CachedCurveEvalTime);
+ return Curve->Eval(CachedCurveEvalTime);
}
}
}
@@ -176,15 +174,15 @@ void UAudioCurveSourceComponent::GetCurves_Implementation(TArrayRowMap.Num());
+ OutCurve.Reset(CurveTable->GetRowMap().Num());
if (bCachedLooping && CachedSyncPreRoll > 0.0f && Delay >= CachedSyncPreRoll && CachedCurveEvalTime >= CachedDuration - CachedSyncPreRoll)
{
// if we are looping and we have some preroll delay, we need to evaluate the curve twice
// as we need to incorporate the preroll in the loop
- for (auto Iter = CurveTable->RowMap.CreateConstIterator(); Iter; ++Iter)
+ for (auto Iter = CurveTable->GetRowMap().CreateConstIterator(); Iter; ++Iter)
{
- FRichCurve* Curve = Iter.Value();
+ FRealCurve* Curve = Iter.Value();
float StandardValue = Curve->Eval(CachedCurveEvalTime);
float LoopedValue = Curve->Eval(FMath::Fmod(CachedCurveEvalTime, CachedDuration));
@@ -194,7 +192,7 @@ void UAudioCurveSourceComponent::GetCurves_Implementation(TArrayRowMap.CreateConstIterator(); Iter; ++Iter)
+ for (auto Iter = CurveTable->GetRowMap().CreateConstIterator(); Iter; ++Iter)
{
OutCurve.Add({ Iter.Key(), Iter.Value()->Eval(CachedCurveEvalTime) });
}
diff --git a/Engine/Plugins/Editor/FacialAnimation/Source/FacialAnimationEditor/Private/FacialAnimationImportItem.cpp b/Engine/Plugins/Editor/FacialAnimation/Source/FacialAnimationEditor/Private/FacialAnimationImportItem.cpp
index 224627ff7808..4f056629c546 100644
--- a/Engine/Plugins/Editor/FacialAnimation/Source/FacialAnimationEditor/Private/FacialAnimationImportItem.cpp
+++ b/Engine/Plugins/Editor/FacialAnimation/Source/FacialAnimationEditor/Private/FacialAnimationImportItem.cpp
@@ -4,7 +4,7 @@
#include "FacialAnimationBulkImporterSettings.h"
#include "Factories/SoundFactory.h"
#include "Sound/SoundWave.h"
-#include "Curves/RichCurve.h"
+#include "Curves/SimpleCurve.h"
#include "Engine/CurveTable.h"
#include "FbxAnimUtils.h"
#include "DesktopPlatformModule.h"
@@ -89,8 +89,8 @@ bool FFacialAnimationImportItem::ImportCurvesEmbeddedInSoundWave()
if (FbxAnimUtils::ImportCurveTableFromNode(FbxFile, GetDefault()->CurveNodeName, NewCurveTable, PreRollTime))
{
// we will need to add a curve to tell us the time we want to start playing audio
- FRichCurve* AudioCurve = NewCurveTable->RowMap.Add(TEXT("Audio"), new FRichCurve());
- AudioCurve->AddKey(PreRollTime, 1.0f);
+ FSimpleCurve& AudioCurve = NewCurveTable->AddSimpleCurve(TEXT("Audio"));
+ AudioCurve.AddKey(PreRollTime, 1.0f);
return true;
}
diff --git a/Engine/Plugins/Editor/GameplayTagsEditor/GameplayTagsEditor.uplugin b/Engine/Plugins/Editor/GameplayTagsEditor/GameplayTagsEditor.uplugin
index 9907b36ef002..d35a1e8282d3 100644
--- a/Engine/Plugins/Editor/GameplayTagsEditor/GameplayTagsEditor.uplugin
+++ b/Engine/Plugins/Editor/GameplayTagsEditor/GameplayTagsEditor.uplugin
@@ -21,12 +21,5 @@
"Type" : "Developer",
"LoadingPhase" : "PreDefault"
}
- ],
- "Plugins":
- [
- {
- "Name" : "AssetManagerEditor",
- "Enabled" : true
- }
]
}
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/GameplayTagsEditor.Build.cs b/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/GameplayTagsEditor.Build.cs
index 2834e63469e0..b6e8d89ce58c 100644
--- a/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/GameplayTagsEditor.Build.cs
+++ b/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/GameplayTagsEditor.Build.cs
@@ -34,8 +34,7 @@ namespace UnrealBuildTool.Rules
"ContentBrowser",
"MainFrame",
"UnrealEd",
- "SourceControl",
- "AssetManagerEditor"
+ "SourceControl"
}
);
diff --git a/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/GameplayTagsEditorModule.cpp b/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/GameplayTagsEditorModule.cpp
index 5762277777b9..be5d316d0153 100644
--- a/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/GameplayTagsEditorModule.cpp
+++ b/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/GameplayTagsEditorModule.cpp
@@ -81,13 +81,11 @@ public:
GameplayTagPackageName = FGameplayTag::StaticStruct()->GetOutermost()->GetFName();
GameplayTagStructName = FGameplayTag::StaticStruct()->GetFName();
- FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry"));
- AssetRegistryModule.Get().OnEditSearchableName(GameplayTagPackageName, GameplayTagStructName).BindRaw(this, &FGameplayTagsEditorModule::OnEditGameplayTag);
-
// Hook into notifications for object re-imports so that the gameplay tag tree can be reconstructed if the table changes
if (GIsEditor)
{
FEditorDelegates::OnAssetPostImport.AddRaw(this, &FGameplayTagsEditorModule::OnObjectReimported);
+ FEditorDelegates::OnEditAssetIdentifiers.AddRaw(this, &FGameplayTagsEditorModule::OnEditGameplayTag);
IGameplayTagsModule::OnTagSettingsChanged.AddRaw(this, &FGameplayTagsEditorModule::OnEditorSettingsChanged);
UPackage::PackageSavedEvent.AddRaw(this, &FGameplayTagsEditorModule::OnPackageSaved);
}
@@ -116,14 +114,9 @@ public:
}
FEditorDelegates::OnAssetPostImport.RemoveAll(this);
+ FEditorDelegates::OnEditAssetIdentifiers.RemoveAll(this);
IGameplayTagsModule::OnTagSettingsChanged.RemoveAll(this);
UPackage::PackageSavedEvent.RemoveAll(this);
-
- FAssetRegistryModule* AssetRegistryModule = FModuleManager::FModuleManager::GetModulePtr("AssetRegistry");
- if (AssetRegistryModule)
- {
- AssetRegistryModule->Get().OnEditSearchableName(GameplayTagPackageName, GameplayTagStructName).Unbind();
- }
}
void OnEditorSettingsChanged()
@@ -166,15 +159,21 @@ public:
}
}
- bool OnEditGameplayTag(const FAssetIdentifier& AssetId)
+ void OnEditGameplayTag(TArray AssetIdentifierList)
{
- if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"))
+ // If any of these are gameplay tags, open up tag viewer
+ for (FAssetIdentifier Identifier : AssetIdentifierList)
{
- // TODO: Select tag maybe?
- SettingsModule->ShowViewer("Project", "Project", "GameplayTags");
+ if (Identifier.IsValue() && Identifier.PackageName == GameplayTagPackageName && Identifier.ObjectName == GameplayTagStructName)
+ {
+ if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"))
+ {
+ // TODO: Select tag maybe?
+ SettingsModule->ShowViewer("Project", "Project", "GameplayTags");
+ }
+ return;
+ }
}
-
- return true;
}
void ShowNotification(const FText& TextToDisplay, float TimeToDisplay)
diff --git a/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/SGameplayTagWidget.cpp b/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/SGameplayTagWidget.cpp
index 79e2d25a1803..76b4f7ae9509 100644
--- a/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/SGameplayTagWidget.cpp
+++ b/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/SGameplayTagWidget.cpp
@@ -29,7 +29,7 @@
#include "SAddNewRestrictedGameplayTagWidget.h"
#include "SRenameGameplayTagDialog.h"
#include "AssetData.h"
-#include "AssetManagerEditorModule.h"
+#include "Editor.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
@@ -710,6 +710,32 @@ ECheckBoxState SGameplayTagWidget::IsTagChecked(TSharedPtr Nod
}
}
+bool SGameplayTagWidget::IsExactTagInCollection(TSharedPtr Node) const
+{
+ if (Node.IsValid())
+ {
+ UGameplayTagsManager& TagsManager = UGameplayTagsManager::Get();
+
+ for (int32 ContainerIdx = 0; ContainerIdx < TagContainers.Num(); ++ContainerIdx)
+ {
+ FGameplayTagContainer* Container = TagContainers[ContainerIdx].TagContainer;
+ if (Container)
+ {
+ FGameplayTag GameplayTag = Node->GetCompleteTag();
+ if (GameplayTag.IsValid())
+ {
+ if (Container->HasTagExact(GameplayTag))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
void SGameplayTagWidget::OnAllowChildrenTagCheckStatusChanged(ECheckBoxState NewCheckState, TSharedPtr NodeChanged)
{
IGameplayTagsEditorModule& TagsEditor = IGameplayTagsEditorModule::Get();
@@ -877,13 +903,22 @@ TSharedRef SGameplayTagWidget::MakeTagActionsMenu(TSharedPtr InTagNode)
}
}
+void SGameplayTagWidget::OnAddTag(TSharedPtr InTagNode)
+{
+ if (InTagNode.IsValid())
+ {
+ for (int32 ContainerIdx = 0; ContainerIdx < TagContainers.Num(); ++ContainerIdx)
+ {
+ FGameplayTagContainer* Container = TagContainers[ContainerIdx].TagContainer;
+ Container->AddTag(InTagNode->GetCompleteTag());
+ }
+
+ OnTagChanged.ExecuteIfBound();
+ }
+}
+
+void SGameplayTagWidget::OnRemoveTag(TSharedPtr InTagNode)
+{
+ if (InTagNode.IsValid())
+ {
+ for (int32 ContainerIdx = 0; ContainerIdx < TagContainers.Num(); ++ContainerIdx)
+ {
+ FGameplayTagContainer* Container = TagContainers[ContainerIdx].TagContainer;
+ Container->RemoveTag(InTagNode->GetCompleteTag());
+ }
+
+ OnTagChanged.ExecuteIfBound();
+ }
+}
+
void SGameplayTagWidget::OnSearchForReferences(TSharedPtr InTagNode)
{
- if (InTagNode.IsValid() && IAssetManagerEditorModule::IsAvailable())
+ if (InTagNode.IsValid())
{
- IAssetManagerEditorModule& ManagerEditorModule = IAssetManagerEditorModule::Get();
-
TArray AssetIdentifiers;
AssetIdentifiers.Add(FAssetIdentifier(FGameplayTag::StaticStruct(), InTagNode->GetCompleteTagName()));
-
- ManagerEditorModule.OpenReferenceViewerUI(AssetIdentifiers);
+ FEditorDelegates::OnOpenReferenceViewer.Broadcast(AssetIdentifiers);
}
}
diff --git a/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/SGameplayTagWidget.h b/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/SGameplayTagWidget.h
index e889a9c2776c..cfd01e200f89 100644
--- a/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/SGameplayTagWidget.h
+++ b/Engine/Plugins/Editor/GameplayTagsEditor/Source/GameplayTagsEditor/Private/SGameplayTagWidget.h
@@ -203,6 +203,11 @@ private:
*/
ECheckBoxState IsTagChecked(TSharedPtr Node) const;
+ /**
+ * @return true if the exact Tag provided is included in any of the tag containers the widget is editing.
+ */
+ bool IsExactTagInCollection(TSharedPtr Node) const;
+
/**
* Called via delegate when the status of the allow non-restricted children check box in a row changes
*
@@ -332,6 +337,12 @@ private:
/** Attempts to delete the specified tag */
void OnDeleteTag(TSharedPtr InTagNode);
+ /** Attempts to add the exact specified tag*/
+ void OnAddTag(TSharedPtr InTagNode);
+
+ /** Attempts to remove the specified tag, but not the children */
+ void OnRemoveTag(TSharedPtr InTagNode);
+
/** Searches for all references for the selected tag */
void OnSearchForReferences(TSharedPtr InTagNode);
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/MaterialAnalyzer.uplugin b/Engine/Plugins/Editor/MaterialAnalyzer/MaterialAnalyzer.uplugin
new file mode 100644
index 000000000000..d7a9f733fda3
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/MaterialAnalyzer.uplugin
@@ -0,0 +1,30 @@
+{
+ "FileVersion": 1,
+ "Version": 1,
+ "VersionName": "1.0",
+ "FriendlyName": "Material Analyzer",
+ "Description": "Analyzer to discover possible memory savings in material shaders.",
+ "Category": "Editor",
+ "CreatedBy": "Epic Games, Inc.",
+ "CreatedByURL": "http://epicgames.com",
+ "DocsURL": "",
+ "MarketplaceURL": "",
+ "SupportURL": "",
+ "EnabledByDefault": true,
+ "CanContainContent": false,
+ "IsBetaVersion": false,
+ "Installed": false,
+ "Modules": [
+ {
+ "Name": "MaterialAnalyzer",
+ "Type": "Editor",
+ "LoadingPhase": "Default"
+ }
+ ],
+ "Plugins": [
+ {
+ "Name": "AssetManagerEditor",
+ "Enabled": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzer.Build.cs b/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzer.Build.cs
new file mode 100644
index 000000000000..49ab4a1e1eb7
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzer.Build.cs
@@ -0,0 +1,32 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+namespace UnrealBuildTool.Rules
+{
+ public class MaterialAnalyzer : ModuleRules
+ {
+ public MaterialAnalyzer(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PublicIncludePaths.AddRange(
+ new string[] {
+ "Editor/WorkspaceMenuStructure/Public"
+ }
+ );
+ PublicDependencyModuleNames.AddRange(new string[] {
+ "Core",
+ "CoreUObject",
+ "Engine",
+ "InputCore",
+ "Slate",
+ "SlateCore",
+ "EditorStyle",
+ "UnrealEd",
+ "PropertyEditor"
+ });
+
+ PrivateDependencyModuleNames.AddRange(new string[] {
+ "AssetManagerEditor"
+ });
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzerModule.cpp b/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzerModule.cpp
new file mode 100644
index 000000000000..6d6c62418ef0
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzerModule.cpp
@@ -0,0 +1,68 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#include "MaterialAnalyzerModule.h"
+#include "SMaterialAnalyzer.h"
+#include "EditorModeRegistry.h"
+#include "Modules/ModuleManager.h"
+#include "Framework/Docking/TabManager.h"
+#include "ISettingsModule.h"
+#include "Editor.h"
+#include "WorkspaceMenuStructure.h"
+#include "WorkspaceMenuStructureModule.h"
+#include "EditorStyleSet.h"
+#include "Widgets/Docking/SDockTab.h"
+
+#define LOCTEXT_NAMESPACE "MaterialAnalyzer"
+
+DEFINE_LOG_CATEGORY(MaterialAnalyzer);
+
+static const FName MaterialAnalyzerName("MaterialAnalyzer");
+
+class FMaterialAnalyzerModule : public IModuleInterface
+{
+public:
+ FMaterialAnalyzerModule()
+ {
+ }
+
+ // FModuleInterface overrides
+ virtual void StartupModule() override;
+ virtual void ShutdownModule() override {}
+ virtual bool SupportsDynamicReloading() override
+ {
+ return true;
+ }
+
+ TSharedRef SpawnMaterialAnalyzerTab(const FSpawnTabArgs& SpawnTabArgs);
+
+protected:
+};
+
+void FMaterialAnalyzerModule::StartupModule()
+{
+ FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
+ MaterialAnalyzerName,
+ FOnSpawnTab::CreateRaw(this, &FMaterialAnalyzerModule::SpawnMaterialAnalyzerTab))
+ .SetGroup(WorkspaceMenu::GetMenuStructure().GetDeveloperToolsMiscCategory())
+ .SetDisplayName(LOCTEXT("TabTitle", "Material Analyzer"))
+ .SetTooltipText(LOCTEXT("TooltipText", "Opens Material Analyzer tool."))
+ .SetIcon(FSlateIcon(FEditorStyle::GetStyleSetName(), "MaterialEditor.ToggleMaterialStats.Tab"));
+}
+
+TSharedRef FMaterialAnalyzerModule::SpawnMaterialAnalyzerTab(const FSpawnTabArgs& SpawnTabArgs)
+{
+ const TSharedRef MajorTab = SNew(SDockTab)
+ .TabRole(ETabRole::NomadTab);
+
+ TSharedPtr TabContent;
+
+ TabContent = SNew(SMaterialAnalyzer, MajorTab, SpawnTabArgs.GetOwnerWindow());
+
+ MajorTab->SetContent(TabContent.ToSharedRef());
+
+ return MajorTab;
+}
+
+IMPLEMENT_MODULE(FMaterialAnalyzerModule, MaterialAnalyzer)
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzerModule.h b/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzerModule.h
new file mode 100644
index 000000000000..7b6642006daf
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/Source/MaterialAnalyzerModule.h
@@ -0,0 +1,7 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "CoreMinimal.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(MaterialAnalyzer, Log, All);
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/AnalyzedMaterialNode.h b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/AnalyzedMaterialNode.h
new file mode 100644
index 000000000000..dc9cbfddd9cf
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/AnalyzedMaterialNode.h
@@ -0,0 +1,172 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "CoreMinimal.h"
+
+struct FBasePropertyOverrideNode
+{
+public:
+ FBasePropertyOverrideNode(FName InParameterName, FName InParameterID, float InParameterValue, bool bInOverride) :
+ ParameterName(InParameterName),
+ ParameterID(InParameterID),
+ ParameterValue(InParameterValue),
+ bOverride(bInOverride)
+ {
+
+ }
+ FName ParameterName;
+ FName ParameterID;
+ float ParameterValue;
+ bool bOverride;
+
+ TArray>* Children;
+};
+
+struct FStaticMaterialLayerParameterNode
+{
+public:
+ FStaticMaterialLayerParameterNode(FName InParameterName, FString InParameterValue, bool bInOverride):
+ ParameterName(InParameterName),
+ ParameterValue(InParameterValue),
+ bOverride(bInOverride)
+ {
+
+ }
+ FName ParameterName;
+ FString ParameterValue;
+ bool bOverride;
+};
+
+struct FStaticSwitchParameterNode
+{
+public:
+ FStaticSwitchParameterNode(FName InParameterName, bool InParameterValue, bool bInOverride) :
+ ParameterName(InParameterName),
+ ParameterValue(InParameterValue),
+ bOverride(bInOverride)
+ {
+
+ }
+ FName ParameterName;
+ bool ParameterValue;
+ bool bOverride;
+
+ TArray>* Children;
+};
+
+struct FStaticComponentMaskParameterNode
+{
+public:
+ FStaticComponentMaskParameterNode(FName InParameterName, bool InR, bool InG, bool InB, bool InA, bool bInOverride) :
+ ParameterName(InParameterName),
+ R(InR),
+ G(InG),
+ B(InB),
+ A(InA),
+ bOverride(bInOverride)
+ {
+
+ }
+ FName ParameterName;
+ bool R;
+ bool G;
+ bool B;
+ bool A;
+ bool bOverride;
+};
+
+typedef TSharedRef FBasePropertyOverrideNodeRef;
+
+typedef TSharedRef FStaticMaterialLayerParameterNodeRef;
+
+typedef TSharedRef FStaticSwitchParameterNodeRef;
+
+typedef TSharedRef FStaticComponentMaskParameterNodeRef;
+
+typedef TSharedRef FAnalyzedMaterialNodeRef;
+
+typedef TSharedPtr FAnalyzedMaterialNodePtr;
+
+struct FAnalyzedMaterialNode
+{
+public:
+ /**
+ * Add the given node to our list of children for this material (this node will keep a strong reference to the instance)
+ */
+ FAnalyzedMaterialNodeRef* AddChildNode(FAnalyzedMaterialNodeRef InChildNode)
+ {
+ ChildNodes.Add(InChildNode);
+ return &ChildNodes.Last();
+ }
+
+ /**
+ * @return The node entries for the material's children
+ */
+ TArray& GetChildNodes()
+ {
+ return ChildNodes;
+ }
+
+ TArray* GetChildNodesPtr()
+ {
+ return &ChildNodes;
+ }
+
+ int ActualNumberOfChildren() const
+ {
+ return ChildNodes.Num();
+ }
+
+ int TotalNumberOfChildren() const
+ {
+ int32 TotalChildren = 0;
+
+ for(int i = 0; i < ChildNodes.Num(); ++i)
+ {
+ TotalChildren += ChildNodes[i]->TotalNumberOfChildren();
+ }
+
+ return TotalChildren + ChildNodes.Num();
+ }
+
+ FBasePropertyOverrideNodeRef FindBasePropertyOverride(FName ParameterName)
+ {
+ FBasePropertyOverrideNodeRef* BasePropertyOverride = BasePropertyOverrides.FindByPredicate([&](const FBasePropertyOverrideNodeRef& Entry) { return Entry->ParameterName == ParameterName; });
+ check(BasePropertyOverride != nullptr);
+ return *BasePropertyOverride;
+ }
+
+ FStaticMaterialLayerParameterNodeRef FindMaterialLayerParameter(FName ParameterName)
+ {
+ FStaticMaterialLayerParameterNodeRef* MaterialLayerParameter = MaterialLayerParameters.FindByPredicate([&](const FStaticMaterialLayerParameterNodeRef& Entry) { return Entry->ParameterName == ParameterName; });
+ check(MaterialLayerParameter != nullptr);
+ return *MaterialLayerParameter;
+ }
+
+ FStaticSwitchParameterNodeRef FindStaticSwitchParameter(FName ParameterName)
+ {
+ FStaticSwitchParameterNodeRef* StaticSwitchParameter = StaticSwitchParameters.FindByPredicate([&](const FStaticSwitchParameterNodeRef& Entry) { return Entry->ParameterName == ParameterName; });
+ check(StaticSwitchParameter != nullptr);
+ return *StaticSwitchParameter;
+ }
+
+ FStaticComponentMaskParameterNodeRef FindStaticComponentMaskParameter(FName ParameterName)
+ {
+ FStaticComponentMaskParameterNodeRef* StaticComponentMaskParameter = StaticComponentMaskParameters.FindByPredicate([&](const FStaticComponentMaskParameterNodeRef& Entry) { return Entry->ParameterName == ParameterName; });
+ check(StaticComponentMaskParameter != nullptr);
+ return *StaticComponentMaskParameter;
+ }
+
+ FString Path;
+ FName ObjectPath;
+ FAnalyzedMaterialNodePtr Parent;
+
+ TArray BasePropertyOverrides;
+ TArray MaterialLayerParameters;
+ TArray StaticSwitchParameters;
+ TArray StaticComponentMaskParameters;
+
+protected:
+ TArray ChildNodes;
+};
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SAnalyzedMaterialNodeWidgetItem.cpp b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SAnalyzedMaterialNodeWidgetItem.cpp
new file mode 100644
index 000000000000..3f4041dda471
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SAnalyzedMaterialNodeWidgetItem.cpp
@@ -0,0 +1,396 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved
+
+#include "SAnalyzedMaterialNodeWidgetItem.h"
+#include "Widgets/Views/STreeView.h"
+#include "SlateOptMacros.h"
+#include "Widgets/Images/SImage.h"
+#include "Engine/EngineTypes.h"
+#include "MaterialShaderType.h"
+
+#define LOCTEXT_NAMESPACE "MaterialAnalyzer"
+
+FName SAnalyzedMaterialNodeWidgetItem::NAME_MaterialName(TEXT("MaterialName"));
+FName SAnalyzedMaterialNodeWidgetItem::NAME_NumberOfChildren(TEXT("MaterialChildren"));
+FName SAnalyzedMaterialNodeWidgetItem::NAME_BasePropertyOverrides(TEXT("BasePropertyOverrides"));
+FName SAnalyzedMaterialNodeWidgetItem::NAME_MaterialLayerParameters(TEXT("MaterialLayerParameters"));
+FName SAnalyzedMaterialNodeWidgetItem::NAME_StaticSwitchParameters(TEXT("StaticSwitchParameters"));
+FName SAnalyzedMaterialNodeWidgetItem::NAME_StaticComponentMaskParameters(TEXT("StaticComponentMaskParameters"));
+
+void SAnalyzedMaterialNodeWidgetItem::Construct(const FArguments& InArgs, const TSharedRef& InOwnerTableView)
+{
+ this->MaterialInfo = InArgs._MaterialInfoToVisualize;
+
+ this->SetPadding(0);
+
+ this->BasePropertyOverrideNodes = MaterialInfo->BasePropertyOverrides;
+ this->StaticSwitchNodes = MaterialInfo->StaticSwitchParameters;
+ this->StaticMaterialLayerNodes = MaterialInfo->MaterialLayerParameters;
+ this->StaticComponentMaskNodes = MaterialInfo->StaticComponentMaskParameters;
+
+ CachedMaterialName = FText::FromString(MaterialInfo->Path);
+ TotalNumberOfChildren = MaterialInfo->TotalNumberOfChildren();
+ NumberOfChildren = MaterialInfo->ActualNumberOfChildren();
+
+ SMultiColumnTableRow< FAnalyzedMaterialNodeRef >::Construct(SMultiColumnTableRow< FAnalyzedMaterialNodeRef >::FArguments().Padding(0), InOwnerTableView);
+}
+
+TSharedRef SAnalyzedMaterialNodeWidgetItem::GenerateWidgetForColumn(const FName& ColumnName)
+{
+ if(ColumnName == NAME_MaterialName)
+ {
+ return SNew(SHorizontalBox)
+
+ + SHorizontalBox::Slot()
+ .AutoWidth()
+ .VAlign(VAlign_Top)
+ [
+ SNew(SExpanderArrow, SharedThis(this))
+ .IndentAmount(16)
+ ]
+ +SHorizontalBox::Slot()
+ .AutoWidth()
+ .Padding(2.0f, 0.0f)
+ .VAlign(VAlign_Top)
+ [
+ SNew(STextBlock)
+ .Text(this, &SAnalyzedMaterialNodeWidgetItem::GetMaterialName)
+ ];
+ }
+ else if(ColumnName == NAME_NumberOfChildren)
+ {
+ return SNew(STextBlock)
+ .Text(this, &SAnalyzedMaterialNodeWidgetItem::GetNumberOfChildren)
+ .Justification(ETextJustify::Left);
+ }
+ else if (ColumnName == NAME_BasePropertyOverrides)
+ {
+ return SNew(SBasePropertyOverrideWidget)
+ .StaticInfos(BasePropertyOverrideNodes);
+ }
+ else if(ColumnName == NAME_MaterialLayerParameters)
+ {
+ return SNew(SStaticMaterialLayerParameterWidget)
+ .StaticInfos(StaticMaterialLayerNodes);
+ }
+ else if (ColumnName == NAME_StaticSwitchParameters)
+ {
+ return SNew(SStaticSwitchParameterWidget)
+ .StaticInfos(StaticSwitchNodes);
+ }
+ else if (ColumnName == NAME_StaticComponentMaskParameters)
+ {
+ return SNew(SStaticComponentMaskParameterWidget)
+ .StaticInfos(StaticComponentMaskNodes);
+ }
+
+ return SNullWidget::NullWidget;
+}
+
+
+
+template
+void SStaticParameterWidget::Construct(const FArguments& InArgs)
+{
+ StaticNodes = InArgs._StaticInfos;
+ StyleSet = InArgs._StyleSet;
+
+ DataVerticalBox = SNew(SVerticalBox).Visibility(EVisibility::Collapsed);
+
+ for (int i = 0; i < StaticNodes.Num(); ++i)
+ {
+ if (!StaticNodes[i]->bOverride)
+ {
+ continue;
+ }
+
+ DataVerticalBox->AddSlot()
+ [
+ CreateRowWidget(StaticNodes[i])
+ ];
+ }
+
+ ChildSlot
+ [
+ SNew(SVerticalBox)
+ + SVerticalBox::Slot()
+ .AutoHeight()
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .AutoWidth()
+ [
+ SAssignNew(ExpanderButton, SButton)
+ .ButtonStyle(FCoreStyle::Get(), "NoBorder")
+ .VAlign(VAlign_Center)
+ .HAlign(HAlign_Center)
+ .ClickMethod(EButtonClickMethod::MouseDown)
+ .OnClicked(this, &SStaticParameterWidget::DoExpand)
+ .ContentPadding(0.f)
+ .ForegroundColor(FSlateColor::UseForeground())
+ .IsFocusable(false)
+ [
+ SNew(SImage)
+ .Image(this, &SStaticParameterWidget ::GetExpanderImage)
+ .ColorAndOpacity(FSlateColor::UseForeground())
+ ]
+ ]
+ + SHorizontalBox::Slot()
+ [
+ SNew(STextBlock)
+ .Text(this, &SStaticParameterWidget::GetBaseText)
+ ]
+ ]
+ +SVerticalBox::Slot()
+ .AutoHeight()
+ [
+ DataVerticalBox.ToSharedRef()
+ ]
+ ];
+
+ ExpanderButton->SetVisibility(DataVerticalBox->NumSlots() > 0 ? EVisibility::Visible : EVisibility::Hidden);
+}
+
+template
+FText SStaticParameterWidget::GetBaseText() const
+{
+ return LOCTEXT("NotOverridenErrorMessage", "Override SStaticParameterWidget::GetBaseText");
+}
+
+
+FText SBasePropertyOverrideWidget::GetBaseText() const
+{
+ return FText::Format(FTextFormat(LOCTEXT("NumberOfBasePropertyOverrides", "{0} Base Property Overrides")), DataVerticalBox->NumSlots());
+}
+
+TSharedRef SBasePropertyOverrideWidget::CreateRowWidget(FBasePropertyOverrideNodeRef RowData)
+{
+ FText DisplayText = FText::GetEmpty();
+
+ if (RowData->ParameterID.IsEqual(TEXT("bOverride_OpacityMaskClipValue")))
+ {
+ DisplayText = FText::AsNumber(RowData->ParameterValue);
+ }
+ else if (RowData->ParameterID.IsEqual(TEXT("bOverride_BlendMode")))
+ {
+ int32 BlendID = (int32)RowData->ParameterValue;
+ DisplayText = FText::FromString(GetBlendModeString((EBlendMode)BlendID));
+ }
+ else if (RowData->ParameterID.IsEqual(TEXT("bOverride_ShadingModel")))
+ {
+ int32 BlendID = (int32)RowData->ParameterValue;
+ DisplayText = FText::FromString(GetShadingModelString((EMaterialShadingModel)BlendID));
+ }
+ else // bool values
+ {
+ DisplayText = RowData->ParameterValue ? LOCTEXT("True", "True") : LOCTEXT("False", "False");
+ }
+
+
+ return SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .Padding(24, 0, 0, 0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromName(RowData->ParameterName))
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(2, 0, 0, 0)
+ .AutoWidth()
+ .HAlign(HAlign_Right)
+ [
+ SNew(STextBlock)
+ .Text(DisplayText)
+ ];
+}
+
+FText SStaticSwitchParameterWidget::GetBaseText() const
+{
+ return FText::Format(FTextFormat(LOCTEXT("NumberOfStaticSwitchParameters", "{0} Static Switch Parameters")), DataVerticalBox->NumSlots());
+}
+
+TSharedRef SStaticSwitchParameterWidget::CreateRowWidget(FStaticSwitchParameterNodeRef RowData)
+{
+ return SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .Padding(24, 0, 0, 0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromName(RowData->ParameterName))
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(2, 0, 0, 0)
+ .AutoWidth()
+ .HAlign(HAlign_Right)
+ [
+ SNew(STextBlock)
+ .Text(RowData->ParameterValue ? LOCTEXT("True", "True") : LOCTEXT("False", "False"))
+ ];
+}
+
+FText SStaticComponentMaskParameterWidget::GetBaseText() const
+{
+ return FText::Format(FTextFormat(LOCTEXT("NumberOfStaticComponentMaskParameters", "{0} Static Component Mask Parameters")), DataVerticalBox->NumSlots());
+}
+
+
+TSharedRef SStaticComponentMaskParameterWidget::CreateRowWidget(FStaticComponentMaskParameterNodeRef RowData)
+{
+ return SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .AutoWidth()
+ .Padding(24, 0, 0, 0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromName(RowData->ParameterName))
+ ]
+ + SHorizontalBox::Slot()
+ .HAlign(HAlign_Right)
+ [
+ SNew(SVerticalBox)
+ +SVerticalBox::Slot()
+ [
+ SNew(SHorizontalBox)
+ +SHorizontalBox::Slot()
+ .AutoWidth()
+ .Padding(0, 0, 10, 0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromString(TEXT("R")))
+ ]
+ +SHorizontalBox::Slot()
+ .HAlign(HAlign_Right)
+ [
+ SNew(STextBlock)
+ .Text(RowData->R ? LOCTEXT("True", "True") : LOCTEXT("False", "False"))
+ ]
+ ]
+ + SVerticalBox::Slot()
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .AutoWidth()
+ .Padding(0, 0, 10, 0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromString(TEXT("G")))
+ ]
+ + SHorizontalBox::Slot()
+ .HAlign(HAlign_Right)
+ [
+ SNew(STextBlock)
+ .Text(RowData->G ? LOCTEXT("True", "True") : LOCTEXT("False", "False"))
+ ]
+ ]
+ + SVerticalBox::Slot()
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .AutoWidth()
+ .Padding(0, 0, 10, 0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromString(TEXT("B")))
+ ]
+ + SHorizontalBox::Slot()
+ .HAlign(HAlign_Right)
+ [
+ SNew(STextBlock)
+ .Text(RowData->B ? LOCTEXT("True", "True") : LOCTEXT("False", "False"))
+ ]
+ ]
+ + SVerticalBox::Slot()
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .AutoWidth()
+ .Padding(0,0,10,0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromString(TEXT("A")))
+ ]
+ + SHorizontalBox::Slot()
+ .HAlign(HAlign_Right)
+ [
+ SNew(STextBlock)
+ .Text(RowData->A ? LOCTEXT("True", "True") : LOCTEXT("False", "False"))
+ ]
+ ]
+ ];
+}
+
+FText SStaticMaterialLayerParameterWidget::GetBaseText() const
+{
+ return FText::Format(FTextFormat(LOCTEXT("NumberOfStaticMaterialLayerParameters", "{0} Static Material Layer Parameters")), DataVerticalBox->NumSlots());
+}
+
+TSharedRef SStaticMaterialLayerParameterWidget::CreateRowWidget(FStaticMaterialLayerParameterNodeRef RowData)
+{
+ return SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .Padding(24, 0, 0, 0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromName(RowData->ParameterName))
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(2, 0, 0, 0)
+ [
+ SNew(STextBlock)
+ .Text(FText::FromString(RowData->ParameterValue))
+ ];
+
+}
+
+template
+FReply SStaticParameterWidget::DoExpand()
+{
+ if(bIsExpanded)
+ {
+ DataVerticalBox->SetVisibility(EVisibility::Collapsed);
+ bIsExpanded = false;
+ }
+ else
+ {
+ DataVerticalBox->SetVisibility(EVisibility::Visible);
+ bIsExpanded = true;
+ }
+ return FReply::Handled();
+}
+
+/** @return the name of an image that should be shown as the expander arrow */
+template
+const FSlateBrush* SStaticParameterWidget::GetExpanderImage() const
+{
+ FName ResourceName;
+ if (bIsExpanded)
+ {
+ if (ExpanderButton->IsHovered())
+ {
+ static FName ExpandedHoveredName = "TreeArrow_Expanded_Hovered";
+ ResourceName = ExpandedHoveredName;
+ }
+ else
+ {
+ static FName ExpandedName = "TreeArrow_Expanded";
+ ResourceName = ExpandedName;
+ }
+ }
+ else
+ {
+ if (ExpanderButton->IsHovered())
+ {
+ static FName CollapsedHoveredName = "TreeArrow_Collapsed_Hovered";
+ ResourceName = CollapsedHoveredName;
+ }
+ else
+ {
+ static FName CollapsedName = "TreeArrow_Collapsed";
+ ResourceName = CollapsedName;
+ }
+ }
+
+ return StyleSet->GetBrush(ResourceName);
+}
+
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SAnalyzedMaterialNodeWidgetItem.h b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SAnalyzedMaterialNodeWidgetItem.h
new file mode 100644
index 000000000000..1bddbe9949e9
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SAnalyzedMaterialNodeWidgetItem.h
@@ -0,0 +1,146 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Styling/SlateColor.h"
+#include "Widgets/DeclarativeSyntaxSupport.h"
+#include "Widgets/SWidget.h"
+#include "Widgets/Views/STableViewBase.h"
+#include "Widgets/Views/STableRow.h"
+#include "Widgets/Views/SListView.h"
+#include "AnalyzedMaterialNode.h"
+#include "Widgets/Input/SButton.h"
+
+class SAnalyzedMaterialNodeWidgetItem
+ : public SMultiColumnTableRow
+{
+public:
+ static FName NAME_MaterialName;
+ static FName NAME_NumberOfChildren;
+ static FName NAME_BasePropertyOverrides;
+ static FName NAME_MaterialLayerParameters;
+ static FName NAME_StaticSwitchParameters;
+ static FName NAME_StaticComponentMaskParameters;
+
+ SLATE_BEGIN_ARGS(SAnalyzedMaterialNodeWidgetItem)
+ : _MaterialInfoToVisualize()
+ { }
+
+ SLATE_ARGUMENT(FAnalyzedMaterialNodePtr, MaterialInfoToVisualize)
+
+ SLATE_END_ARGS()
+
+
+public:
+
+ /**
+ * Construct child widgets that comprise this widget.
+ *
+ * @param InArgs Declaration from which to construct this widget.
+ */
+ void Construct(const FArguments& InArgs, const TSharedRef& InOwnerTableView);
+
+
+ // SMultiColumnTableRow overrides
+ virtual TSharedRef GenerateWidgetForColumn(const FName& ColumnName) override;
+
+ FText GetMaterialName() const
+ {
+ return CachedMaterialName;
+ }
+
+ FText GetNumberOfChildren() const
+ {
+ return FText::Format(FTextFormat::FromString(TEXT("{0}/{1}")), NumberOfChildren, TotalNumberOfChildren);
+ }
+
+protected:
+ /** The info about the widget that we are visualizing. */
+ FAnalyzedMaterialNodePtr MaterialInfo;
+
+ FText CachedMaterialName;
+ int TotalNumberOfChildren;
+ int NumberOfChildren;
+
+ TArray BasePropertyOverrideNodes;
+ TArray StaticSwitchNodes;
+ TArray StaticComponentMaskNodes;
+ TArray StaticMaterialLayerNodes;
+};
+
+template
+class SStaticParameterWidget
+ : public SCompoundWidget
+{
+ SLATE_BEGIN_ARGS(SStaticParameterWidget)
+ : _StyleSet(&FCoreStyle::Get()),
+ _StaticInfos()
+ {}
+
+ SLATE_ARGUMENT(const ISlateStyle*, StyleSet)
+ SLATE_ARGUMENT(TArray, StaticInfos)
+
+ SLATE_END_ARGS()
+public:
+ SStaticParameterWidget() :
+ bIsExpanded(false)
+ {
+
+ }
+
+ virtual TSharedRef CreateRowWidget(NodeType RowData)
+ {
+ return SNullWidget::NullWidget;
+ }
+
+ virtual FText GetBaseText() const;
+
+ void Construct(const FArguments& InArgs);
+
+ TSharedPtr DataVerticalBox;
+ TSharedPtr ExpanderButton;
+
+ TArray StaticNodes;
+
+ FReply DoExpand();
+ const FSlateBrush* GetExpanderImage() const;
+
+ /** The slate style to use */
+ const ISlateStyle* StyleSet;
+
+ bool bIsExpanded;
+
+};
+
+class SBasePropertyOverrideWidget
+ : public SStaticParameterWidget
+{
+ virtual TSharedRef CreateRowWidget(FBasePropertyOverrideNodeRef RowData) override;
+
+ virtual FText GetBaseText() const override;
+};
+
+class SStaticSwitchParameterWidget
+ : public SStaticParameterWidget
+{
+ virtual TSharedRef CreateRowWidget(FStaticSwitchParameterNodeRef RowData) override;
+
+ virtual FText GetBaseText() const override;
+};
+
+class SStaticComponentMaskParameterWidget
+ : public SStaticParameterWidget
+{
+ virtual TSharedRef CreateRowWidget(FStaticComponentMaskParameterNodeRef RowData) override;
+
+ virtual FText GetBaseText() const override;
+};
+
+class SStaticMaterialLayerParameterWidget
+ : public SStaticParameterWidget
+{
+public:
+ virtual TSharedRef CreateRowWidget(FStaticMaterialLayerParameterNodeRef RowData) override;
+ virtual FText GetBaseText() const override;
+};
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SMaterialAnalyzer.cpp b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SMaterialAnalyzer.cpp
new file mode 100644
index 000000000000..cf21f8e55c8d
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SMaterialAnalyzer.cpp
@@ -0,0 +1,929 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#include "SMaterialAnalyzer.h"
+#include "Widgets/Input/SSearchBox.h"
+#include "AssetRegistryModule.h"
+#include "Async/AsyncWork.h"
+#include "Framework/MultiBox/MultiBoxBuilder.h"
+#include "Styling/SlateIconFinder.h"
+#include "Framework/Commands/UIAction.h"
+#include "Materials/MaterialLayersFunctions.h"
+#include "Hash/CityHash.h"
+#include "EditorStyleSet.h"
+#include "PropertyCustomizationHelpers.h"
+#include "CollectionManagerTypes.h"
+#include "CollectionManagerModule.h"
+#include "ICollectionManager.h"
+#include "AssetManagerEditorModule.h"
+#include "Widgets/Images/SImage.h"
+
+#define LOCTEXT_NAMESPACE "MaterialAnalyzer"
+
+static TMap BasePropertyOverrideNames;
+
+SMaterialAnalyzer::SMaterialAnalyzer()
+ : BuildBaseMaterialTreeTask(nullptr)
+ , AnalyzeTreeTask(nullptr)
+ , AnalyzeForIdenticalPermutationsTask(nullptr)
+ , bRequestedTreeRefresh(false)
+ , bWaitingForAssetRegistryLoad(false)
+{
+ BasePropertyOverrideNames.Empty();
+ BasePropertyOverrideNames.Add(TEXT("bOverride_OpacityMaskClipValue"), TEXT("OpacityMaskClipValueOverride"));
+ BasePropertyOverrideNames.Add(TEXT("bOverride_BlendMode"), TEXT("BlendModeOverride"));
+ BasePropertyOverrideNames.Add(TEXT("bOverride_ShadingModel"), TEXT("ShadingModelOverride"));
+ BasePropertyOverrideNames.Add(TEXT("bOverride_DitheredLODTransition"), TEXT("DitheredLODTransitionOverride"));
+ BasePropertyOverrideNames.Add(TEXT("bOverride_CastDynamicShadowAsMasked"), TEXT("CastDynamicShadowAsMaskedOverride"));
+ BasePropertyOverrideNames.Add(TEXT("bOverride_TwoSided"), TEXT("TwoSidedOverride"));
+}
+
+SMaterialAnalyzer::~SMaterialAnalyzer()
+{
+
+}
+
+const FAssetData* FindParentAssetData(const FAssetData* InAssetData, const TArray& ArrayToSearch)
+{
+ check(InAssetData != nullptr);
+ static const FName NAME_Parent = TEXT("Parent");
+ FString ParentPath = InAssetData->GetTagValueRef(NAME_Parent);
+
+ int32 FirstCut = INDEX_NONE;
+ ParentPath.FindChar(L'\'', FirstCut);
+
+ FName ParentPathName = NAME_None;
+
+ if(FirstCut != INDEX_NONE)
+ {
+ ParentPathName = FName(*ParentPath.Mid(FirstCut + 1, ParentPath.Len() - FirstCut - 2));
+ }
+ else
+ {
+ ParentPathName = FName(*ParentPath);
+ }
+
+ if(ParentPathName.IsValid() && !ParentPathName.IsNone())
+ {
+ return ArrayToSearch.FindByPredicate(
+ [&](FAssetData& Entry)
+ {
+ return Entry.ObjectPath == ParentPathName;
+ }
+ );
+ }
+
+ return nullptr;
+}
+
+void SMaterialAnalyzer::Construct(const FArguments& InArgs, const TSharedRef& ConstructUnderMajorTab, const TSharedPtr& ConstructUnderWindow)
+{
+ TArray AllowedClasses;
+ AllowedClasses.Add(UMaterialInterface::StaticClass());
+
+ TSharedRef AssetPickerWidget = SNew(SObjectPropertyEntryBox)
+ .ObjectPath(this, &SMaterialAnalyzer::GetCurrentAssetPath)
+ .AllowedClass(UMaterialInterface::StaticClass())
+ .OnObjectChanged(this, &SMaterialAnalyzer::OnAssetSelected)
+ .AllowClear(false)
+ .DisplayUseSelected(true)
+ .DisplayBrowse(true)
+ .NewAssetFactories(TArray())
+ .IsEnabled(this, &SMaterialAnalyzer::IsMaterialSelectionAllowed);
+
+ this->ChildSlot
+ [
+ SNew(SBorder)
+ .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
+ .BorderBackgroundColor(FLinearColor::Gray) // Darken the outer border
+ [
+ SNew(SVerticalBox)
+ + SVerticalBox::Slot()
+ .AutoHeight()
+ .Padding(FMargin(5.0f, 5.0f, 5.0f, 5.0f))
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .AutoWidth()
+ .VAlign(VAlign_Center)
+ [
+ SNew(STextBlock)
+ .Text(LOCTEXT("MaterialToAnalyze", "Material To Analyze: "))
+ ]
+ + SHorizontalBox::Slot()
+ .FillWidth(0.5f)
+ [
+ AssetPickerWidget
+ ]
+ +SHorizontalBox::Slot()
+ .FillWidth(0.5f)
+ [
+ SNullWidget::NullWidget
+ ]
+ ]
+ + SVerticalBox::Slot()
+ .FillHeight(1.0f)
+ [
+ SNew(SSplitter)
+ .Orientation(EOrientation::Orient_Vertical)
+ + SSplitter::Slot()
+ [
+ SNew(SBorder)
+ .Padding(0)
+ .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
+ [
+ SAssignNew(MaterialTree, SAnalyzedMaterialTree)
+ .ItemHeight(24.0f)
+ .TreeItemsSource(&MaterialTreeRoot)
+ .OnGenerateRow(this, &SMaterialAnalyzer::HandleReflectorTreeGenerateRow)
+ .OnGetChildren(this, &SMaterialAnalyzer::HandleReflectorTreeGetChildren)
+ .HeaderRow
+ (
+ SNew(SHeaderRow)
+ + SHeaderRow::Column(SAnalyzedMaterialNodeWidgetItem::NAME_MaterialName)
+ .DefaultLabel(LOCTEXT("MaterialName", "Material Name"))
+ .FillWidth(0.80f)
+ + SHeaderRow::Column(SAnalyzedMaterialNodeWidgetItem::NAME_NumberOfChildren)
+ .DefaultLabel(LOCTEXT("NumberOfMaterialChildren", "Number of Children (Direct/Total)"))
+ + SHeaderRow::Column(SAnalyzedMaterialNodeWidgetItem::NAME_BasePropertyOverrides)
+ .DefaultLabel(LOCTEXT("BasePropertyOverrides", "Base Property Overrides"))
+ + SHeaderRow::Column(SAnalyzedMaterialNodeWidgetItem::NAME_MaterialLayerParameters)
+ .DefaultLabel(LOCTEXT("MaterialLayerParameters", "Material Layer Parameters"))
+ + SHeaderRow::Column(SAnalyzedMaterialNodeWidgetItem::NAME_StaticSwitchParameters)
+ .DefaultLabel(LOCTEXT("StaticSwitchParameters", "Static Switch Parameters"))
+ + SHeaderRow::Column(SAnalyzedMaterialNodeWidgetItem::NAME_StaticComponentMaskParameters)
+ .DefaultLabel(LOCTEXT("StaticComponenetMaskParameters", "Static Component Mask Parameters"))
+ )
+ ]
+ ]
+ + SSplitter::Slot()
+ [
+ SNew(SBorder)
+ .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
+ .BorderBackgroundColor(FLinearColor::Gray) // Darken the outer border
+ [
+ SNew(SVerticalBox)
+ + SVerticalBox::Slot()
+ .AutoHeight()
+ .VAlign(VAlign_Center)
+ [
+ SNew(STextBlock)
+ .Text(LOCTEXT("Suggestions", "Suggestions"))
+ ]
+ + SVerticalBox::Slot()
+ [
+ SAssignNew(SuggestionsBox, SScrollBox)
+ ]
+ ]
+ ]
+ ]
+ + SVerticalBox::Slot()
+ .AutoHeight()
+ .VAlign(EVerticalAlignment::VAlign_Bottom)
+ [
+ SNew(SHorizontalBox)
+ +SHorizontalBox::Slot()
+ .VAlign(VAlign_Center)
+ [
+ SAssignNew(StatusBox, STextBlock)
+ .Text(LOCTEXT("Done", "Done"))
+ ]
+ +SHorizontalBox::Slot()
+ .HAlign(HAlign_Right)
+ .Padding(0,0,4,0)
+ [
+ SAssignNew(StatusThrobber, SThrobber)
+ .Animate(SThrobber::EAnimation::None)
+ ]
+ ]
+ ]
+ ];
+
+ // Load the asset registry module to listen for updates
+ FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry"));
+ if(AssetRegistryModule.Get().IsLoadingAssets())
+ {
+ StartAsyncWork(LOCTEXT("WaitingForAssetRegistry", "Waiting for Asset Registry to finish loading"));
+ bWaitingForAssetRegistryLoad = true;
+ }
+ else
+ {
+ SetupAssetRegistryCallbacks();
+ BuildBasicMaterialTree();
+ }
+}
+
+void SMaterialAnalyzer::SetupAssetRegistryCallbacks()
+{
+ FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry"));
+ AssetRegistryModule.Get().OnAssetAdded().AddSP(this, &SMaterialAnalyzer::OnAssetAdded);
+}
+
+void SMaterialAnalyzer::OnAssetAdded(const FAssetData& InAssetData)
+{
+ if(InAssetData.GetClass()->IsChildOf())
+ {
+ RecentlyAddedAssetData.Add(InAssetData);
+ }
+}
+
+void SMaterialAnalyzer::OnAssetSelected(const FAssetData& AssetData)
+{
+ if(AnalyzeTreeTask == nullptr)
+ {
+ CurrentlySelectedAsset = AssetData;
+
+ const FAssetData* ParentAssetData = &AssetData;
+ const FAssetData* NextParentAssetData = FindParentAssetData(&AssetData, AssetDataArray);
+ // get the topmost parent
+ while (NextParentAssetData != nullptr)
+ {
+ ParentAssetData = NextParentAssetData;
+ NextParentAssetData = FindParentAssetData(ParentAssetData, AssetDataArray);
+ }
+
+ // empty the previous tree root
+ MaterialTreeRoot.Empty(1);
+ // Add the new tree root
+ FAnalyzedMaterialNodeRef* NewRoot = AllMaterialTreeRoots.FindByPredicate([&](FAnalyzedMaterialNodeRef& Entry)
+ {
+ return Entry->ObjectPath == ParentAssetData->ObjectPath;
+ });
+ check(NewRoot != nullptr);
+
+ MaterialTreeRoot.Add(*NewRoot);
+
+ MaterialTree->RequestTreeRefresh();
+
+ SuggestionsBox->ClearChildren();
+
+ AnalyzeTreeTask = new FAsyncTask(*NewRoot, AssetDataArray);
+
+ StartAsyncWork(FText::Format(LOCTEXT("AnalyzingMaterial", "Analyzing {0}"), FText::FromString(AnalyzeTreeTask->GetTask().CurrentMaterialNode->Path)));
+ AnalyzeTreeTask->StartBackgroundTask();
+ }
+}
+
+void SMaterialAnalyzer::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
+{
+ SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
+
+ if (bWaitingForAssetRegistryLoad)
+ {
+ FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry"));
+ if(!AssetRegistryModule.Get().IsLoadingAssets())
+ {
+ SetupAssetRegistryCallbacks();
+ BuildBasicMaterialTree();
+ bWaitingForAssetRegistryLoad = false;
+ }
+ }
+ else
+ {
+ if (BuildBaseMaterialTreeTask != nullptr && BuildBaseMaterialTreeTask->IsDone())
+ {
+ delete BuildBaseMaterialTreeTask;
+ BuildBaseMaterialTreeTask = nullptr;
+ AsyncWorkFinished(FText::Format(FTextFormat(LOCTEXT("MaterialAnalyzer_DoneWithMaterialInterfaces", "Done with {0} MaterialInterfaces")), GetTotalNumberOfMaterialNodes()));
+
+ }
+
+ if (BuildBaseMaterialTreeTask == nullptr && RecentlyAddedAssetData.Num() > 0)
+ {
+ // Need to make this append to the previously generated list instead of erase all of the old info
+ // Current problem is that if we only have a portion of the asset registry it will create duplicate
+ // nodes since it won't find all parents in the tree. Need to modify the async task to not create
+ // nodes that don't have parents until we can find their parent.
+ AssetDataArray.Append(RecentlyAddedAssetData);
+ RecentlyAddedAssetData.Empty();
+ AllMaterialTreeRoots.Empty(AllMaterialTreeRoots.Num());
+
+ BuildBaseMaterialTreeTask = new FAsyncTask(AllMaterialTreeRoots, AssetDataArray);
+ BuildBaseMaterialTreeTask->StartBackgroundTask();
+
+ StartAsyncWork(LOCTEXT("BuildingBasicTree", "Building Basic MaterialTree"));
+ }
+
+ if (AnalyzeTreeTask != nullptr && AnalyzeTreeTask->IsDone())
+ {
+ if (AnalyzeTreeTask->GetTask().LoadNextMaterial())
+ {
+ StartAsyncWork(FText::Format(LOCTEXT("AnalyzingMaterial", "Analyzing {0}"), FText::FromString(AnalyzeTreeTask->GetTask().CurrentMaterialNode->Path)));
+ AnalyzeTreeTask->StartBackgroundTask();
+ }
+ else
+ {
+ MaterialTree->RequestListRefresh();
+
+ // Kick off a check for identical permutations
+ // @todo make this a series of tests that users can choose to run
+ AnalyzeForIdenticalPermutationsTask = new FAsyncTask(AnalyzeTreeTask->GetTask().MaterialTreeRoot);
+ AnalyzeForIdenticalPermutationsTask->StartBackgroundTask();
+
+ delete AnalyzeTreeTask;
+ AnalyzeTreeTask = nullptr;
+
+ StartAsyncWork(LOCTEXT("AnalyzingTreeForIdenticalPermutations", "Analyzing material tree for identical permutations"));
+ }
+ }
+
+ if (AnalyzeForIdenticalPermutationsTask != nullptr && AnalyzeForIdenticalPermutationsTask->IsDone())
+ {
+ MaterialTree->RequestListRefresh();
+ AsyncWorkFinished(LOCTEXT("Done", "Done!"));
+
+ TMultiMap Suggestions = AnalyzeForIdenticalPermutationsTask->GetTask().GetSuggestions();
+
+ Suggestions.KeySort([](int32 A, int32 B) {
+ return A > B; // sort to show most improvement possibility first
+ });
+
+
+
+
+ int32 BackgroundColorCounter = 0;
+ for (auto It = Suggestions.CreateConstIterator(); It; ++It)
+ {
+ TSharedPtr SuggestionHeader = MakeShareable(new FPermutationSuggestionView());
+ SuggestionHeader->Header = It.Value().Header;
+ for (FString Material : It.Value().Materials)
+ {
+ TSharedPtr SuggestionChild = MakeShareable(new FPermutationSuggestionView());
+ SuggestionChild->Header = FText::FromString(Material);
+ SuggestionHeader->Children.Add(SuggestionChild);
+ }
+ SuggestionDataArray.Add(SuggestionHeader);
+ }
+
+ SuggestionsBox->AddSlot()
+ [
+ SAssignNew(SuggestionsTree, STreeView>)
+ .TreeItemsSource(&SuggestionDataArray)
+ .OnGenerateRow(this, &SMaterialAnalyzer::OnGenerateSuggestionRow)
+ .OnGetChildren(this, &SMaterialAnalyzer::OnGetSuggestionChildren)
+ ];
+
+
+
+
+ delete AnalyzeForIdenticalPermutationsTask;
+ AnalyzeForIdenticalPermutationsTask = nullptr;
+ }
+ }
+}
+
+TSharedRef< ITableRow > SMaterialAnalyzer::OnGenerateSuggestionRow(TSharedPtr Item, const TSharedRef< STableViewBase >& OwnerTable)
+{
+ if (Item->Children.Num() > 0)
+ {
+ return SNew(STableRow>, OwnerTable)
+ [
+ SNew(SVerticalBox)
+ +SVerticalBox::Slot()
+ .AutoHeight()
+ .VAlign(VAlign_Bottom)
+ [
+ SNew(SEditableText)
+ .IsReadOnly(true)
+ .Text(Item->Header)
+ ]
+ + SVerticalBox::Slot()
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ [
+ SNew(STextBlock)
+ .Visibility(this, &SMaterialAnalyzer::ShouldShowAdvancedRecommendations, Item)
+ .Text(LOCTEXT("PermutationRecommendation", "It is recommended that you reparent them in a way so only dynamic parameters differ."))
+ ]
+ + SHorizontalBox::Slot()
+ .FillWidth(1.0f)
+ [
+ SNullWidget::NullWidget
+ ]
+ +SHorizontalBox::Slot()
+ .AutoWidth()
+ [
+ SNew(SButton)
+ .ButtonStyle(FEditorStyle::Get(), "SimpleSharpButton")
+ .Visibility(this, &SMaterialAnalyzer::ShouldShowAdvancedRecommendations, Item)
+ .OnClicked(this, &SMaterialAnalyzer::CreateLocalSuggestionCollection, Item)
+ .ContentPadding(FMargin(2.0f))
+ .Content()
+ [
+ SNew(SHorizontalBox)
+ +SHorizontalBox::Slot()
+ .AutoWidth()
+ .Padding(2.0f)
+ [
+ SNew(SImage)
+ .Image(FEditorStyle::GetBrush("ContentBrowser.AddCollectionButtonIcon"))
+ .ColorAndOpacity(FSlateColor::UseForeground())
+ ]
+ +SHorizontalBox::Slot()
+ .AutoWidth()
+ .Padding(2.0f)
+ [
+ SNew(STextBlock)
+ .Text(LOCTEXT("CreateLocalCollection", "Create Local Collection"))
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+ return SNew(STableRow>, OwnerTable)
+ [
+ SNew(SEditableText)
+ .IsReadOnly(true)
+ .Text(Item->Header)
+ ];
+}
+
+EVisibility SMaterialAnalyzer::ShouldShowAdvancedRecommendations(TSharedPtr Item) const
+{
+ return SuggestionsTree->IsItemExpanded(Item) ? EVisibility::Visible : EVisibility::Collapsed;
+}
+
+void SMaterialAnalyzer::OnGetSuggestionChildren(TSharedPtr InParent, TArray< TSharedPtr >& OutChildren)
+{
+ OutChildren = InParent->Children;
+}
+
+FReply SMaterialAnalyzer::CreateLocalSuggestionCollection(TSharedPtr InSuggestion)
+{
+ TArray AllSelectedPackageNames;
+ ECollectionShareType::Type ShareType = ECollectionShareType::CST_Local;
+ for (TSharedPtr Child : InSuggestion->Children)
+ {
+ AllSelectedPackageNames.Add(Child->Header.ToString());
+ }
+
+ if (AllSelectedPackageNames.Num() > 0)
+ {
+ FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
+
+ FString FirstAssetString = CurrentlySelectedAsset.AssetName.ToString() + TEXT("_") + FString::FromInt(InSuggestion->Children.Num());
+ FName FirstAssetName = FName(*FirstAssetString);
+
+ CollectionManagerModule.Get().CreateUniqueCollectionName(FirstAssetName, ShareType, FirstAssetName);
+
+ FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry"));
+ TArray PackageNamesToAddToCollection;
+
+ TArray PackageNameSet;
+ for (FString PackageToAdd : AllSelectedPackageNames)
+ {
+ PackageNameSet.Add(FName(*FPaths::GetBaseFilename(*PackageToAdd, false)));
+ }
+
+ IAssetManagerEditorModule::Get().WriteCollection(FirstAssetName, ShareType, PackageNameSet, true);
+ }
+ return FReply::Handled();
+}
+
+void SMaterialAnalyzer::StartAsyncWork(const FText& WorkText)
+{
+ if(StatusBox.IsValid())
+ {
+ StatusBox->SetText(WorkText);
+ }
+
+ if(StatusThrobber.IsValid())
+ {
+ StatusThrobber->SetAnimate(SThrobber::Horizontal);
+ StatusThrobber->SetVisibility(EVisibility::SelfHitTestInvisible);
+ }
+
+ bAllowMaterialSelection = false;
+}
+
+void SMaterialAnalyzer::AsyncWorkFinished(const FText& CompleteText)
+{
+ if (StatusBox.IsValid())
+ {
+ StatusBox->SetText(CompleteText);
+ }
+
+ if (StatusThrobber.IsValid())
+ {
+ StatusThrobber->SetAnimate(SThrobber::None);
+ StatusThrobber->SetVisibility(EVisibility::Collapsed);
+ }
+
+ bAllowMaterialSelection = true;
+}
+
+int32 SMaterialAnalyzer::GetTotalNumberOfMaterialNodes()
+{
+ int32 NumMaterialNodes = AllMaterialTreeRoots.Num();
+
+ for(int i = 0; i < AllMaterialTreeRoots.Num(); ++i)
+ {
+ NumMaterialNodes += AllMaterialTreeRoots[i]->TotalNumberOfChildren();
+ }
+
+ return NumMaterialNodes;
+}
+
+FString SMaterialAnalyzer::GetCurrentAssetPath() const
+{
+ return CurrentlySelectedAsset.IsValid() ? CurrentlySelectedAsset.ObjectPath.ToString() : FString("");
+}
+
+void SMaterialAnalyzer::BuildBasicMaterialTree()
+{
+ static const FName AssetRegistryName(TEXT("AssetRegistry"));
+ FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(AssetRegistryName);
+
+ IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
+
+ AssetRegistry.GetAssetsByClass(UMaterial::StaticClass()->GetFName(), AssetDataArray, true);
+ AssetRegistry.GetAssetsByClass(UMaterialInstance::StaticClass()->GetFName(), AssetDataArray, true);
+
+ if (BuildBaseMaterialTreeTask == nullptr && AssetDataArray.Num() > 0)
+ {
+ AllMaterialTreeRoots.Empty(AllMaterialTreeRoots.Num());
+ BuildBaseMaterialTreeTask = new FAsyncTask(AllMaterialTreeRoots, AssetDataArray);
+ BuildBaseMaterialTreeTask->StartBackgroundTask();
+
+ StartAsyncWork(LOCTEXT("BuildingBasicTree", "Building basic material tree"));
+
+ if (StatusThrobber.IsValid())
+ {
+ StatusThrobber->SetAnimate(SThrobber::EAnimation::Horizontal);
+ }
+ }
+}
+
+TSharedRef SMaterialAnalyzer::HandleReflectorTreeGenerateRow(FAnalyzedMaterialNodeRef InMaterialNode, const TSharedRef& OwnerTable)
+{
+ TSharedPtr NewWidget = SNew(SAnalyzedMaterialNodeWidgetItem, OwnerTable)
+ .MaterialInfoToVisualize(InMaterialNode);
+
+ // if we're the base level we're going to expand right away
+ if(!InMaterialNode->Parent.IsValid())
+ {
+ MaterialTree->SetItemExpansion(InMaterialNode, true);
+ }
+
+ return NewWidget.ToSharedRef();
+}
+
+void SMaterialAnalyzer::HandleReflectorTreeGetChildren(FAnalyzedMaterialNodeRef InMaterialNode, TArray& OutChildren)
+{
+ OutChildren = InMaterialNode->GetChildNodes();
+}
+
+FAnalyzedMaterialNodePtr FBuildBasicMaterialTreeAsyncTask::FindOrMakeBranchNode(FAnalyzedMaterialNodePtr ParentNode, const FAssetData* ChildData)
+{
+ check(ChildData != nullptr);
+ FAnalyzedMaterialNodeRef* OutNode = nullptr;
+
+ FName ChildName = ChildData->ObjectPath;
+
+ TArray& NodesToSearch = ParentNode.IsValid() ? ParentNode->GetChildNodes() : MaterialTreeRoot;
+
+ OutNode = NodesToSearch.FindByPredicate([&](FAnalyzedMaterialNodeRef& Entry) { return Entry->ObjectPath == ChildName; });
+
+ if (OutNode == nullptr)
+ {
+ FAnalyzedMaterialNode ChildNode;
+ ChildNode.Path = ChildData->AssetName.ToString();
+ ChildNode.ObjectPath = ChildData->ObjectPath;
+ ChildNode.Parent = ParentNode;
+ NodesToSearch.Add(FAnalyzedMaterialNodeRef(new FAnalyzedMaterialNode(ChildNode)));
+ OutNode = &NodesToSearch[NodesToSearch.Num() - 1];
+ }
+
+ return FAnalyzedMaterialNodePtr (*OutNode);
+}
+
+void FBuildBasicMaterialTreeAsyncTask::DoWork()
+{
+ for(int i = 0; i < AssetDataToAnalyze.Num(); ++i)
+ {
+ const FAssetData& AssetData = AssetDataToAnalyze[i];
+
+ TArray FullBranch;
+
+ const FAssetData* CurrentBranchNode = &AssetData;
+ while(CurrentBranchNode)
+ {
+ FullBranch.Add(CurrentBranchNode);
+ CurrentBranchNode = FindParentAssetData(CurrentBranchNode, AssetDataToAnalyze);
+ }
+
+ FAnalyzedMaterialNodePtr ParentNode = nullptr;
+
+ for(int Depth = FullBranch.Num() - 1; Depth >= 0; --Depth)
+ {
+ ParentNode = FindOrMakeBranchNode(ParentNode, FullBranch[Depth]);
+ }
+ }
+}
+
+bool FAnalyzeMaterialTreeAsyncTask::LoadNextMaterial()
+{
+ if (CurrentMaterialQueueIndex < MaterialQueue.Num())
+ {
+ CurrentMaterialNode = MaterialQueue[CurrentMaterialQueueIndex];
+ check(CurrentMaterialNode->ObjectPath.IsValid());
+
+ CurrentMaterialInterface = FindObject(NULL, *CurrentMaterialNode->ObjectPath.ToString());
+ if (CurrentMaterialInterface == nullptr)
+ {
+ CurrentMaterialInterface = LoadObject(NULL, *CurrentMaterialNode->ObjectPath.ToString());
+ check(CurrentMaterialInterface);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void FAnalyzeMaterialTreeAsyncTask::DoWork()
+{
+ MaterialQueue.Append(CurrentMaterialNode->GetChildNodes());
+
+ TArray MaterialLayersParameterInfo;
+
+ check(CurrentMaterialInterface != nullptr);
+
+ TArray ParameterInfo;
+ TArray Guids;
+
+ UMaterial* CurrentMaterial = Cast(CurrentMaterialInterface);
+
+ bool bCanBeOverriden = CurrentMaterial != nullptr;
+
+ if(CurrentMaterial != nullptr)
+ {
+ CurrentMaterial->GetAllMaterialLayersParameterInfo(MaterialLayerParameterInfo, MaterialLayerGuids);
+
+ CurrentMaterial->GetAllStaticSwitchParameterInfo(StaticSwitchParameterInfo, StaticSwitchGuids);
+
+ CurrentMaterial->GetAllStaticComponentMaskParameterInfo(StaticMaskParameterInfo, StaticMaskGuids);
+ }
+
+ CurrentMaterialNode->BasePropertyOverrides.Empty(BasePropertyOverrideNames.Num());
+
+ for (const TPair& BasePropertyOverrideName : BasePropertyOverrideNames)
+ {
+ float TempValue = 0.0f;
+ bool bIsOverridden = false;
+
+ UMaterialInstance* CurrentMaterialInstance = Cast(CurrentMaterialInterface);
+
+ if (BasePropertyOverrideName.Key.IsEqual(TEXT("bOverride_OpacityMaskClipValue")))
+ {
+ TempValue = CurrentMaterialInterface->GetOpacityMaskClipValue();
+ if (CurrentMaterialInstance)
+ {
+ bIsOverridden = CurrentMaterialInstance->BasePropertyOverrides.bOverride_OpacityMaskClipValue;
+ }
+ }
+ else if (BasePropertyOverrideName.Key.IsEqual(TEXT("bOverride_BlendMode")))
+ {
+ TempValue = (float)CurrentMaterialInterface->GetBlendMode();
+ if (CurrentMaterialInstance)
+ {
+ bIsOverridden = CurrentMaterialInstance->BasePropertyOverrides.bOverride_BlendMode;
+ }
+ }
+ else if (BasePropertyOverrideName.Key.IsEqual(TEXT("bOverride_ShadingModel")))
+ {
+ TempValue = (float)CurrentMaterialInterface->GetShadingModel();
+ if (CurrentMaterialInstance)
+ {
+ bIsOverridden = CurrentMaterialInstance->BasePropertyOverrides.bOverride_ShadingModel;
+ }
+ }
+ else if (BasePropertyOverrideName.Key.IsEqual(TEXT("bOverride_DitheredLODTransition")))
+ {
+ TempValue = (float)CurrentMaterialInterface->IsDitheredLODTransition();
+ if (CurrentMaterialInstance)
+ {
+ bIsOverridden = CurrentMaterialInstance->BasePropertyOverrides.bOverride_DitheredLODTransition;
+ }
+ }
+ else if (BasePropertyOverrideName.Key.IsEqual(TEXT("bOverride_CastDynamicShadowAsMasked")))
+ {
+ TempValue = CurrentMaterialInterface->GetCastShadowAsMasked();
+ if (CurrentMaterialInstance)
+ {
+ bIsOverridden = CurrentMaterialInstance->BasePropertyOverrides.bOverride_CastDynamicShadowAsMasked;
+ }
+ }
+ else if (BasePropertyOverrideName.Key.IsEqual(TEXT("bOverride_TwoSided")))
+ {
+ TempValue = CurrentMaterialInterface->IsTwoSided();
+ if (CurrentMaterialInstance)
+ {
+ bIsOverridden = CurrentMaterialInstance->BasePropertyOverrides.bOverride_TwoSided;
+ }
+ }
+
+ // Check the parent for this variable
+ FAnalyzedMaterialNodePtr Parent = CurrentMaterialNode->Parent;
+ if (!bIsOverridden && Parent.IsValid())
+ {
+ // We shouldn't be able to get in here for the base Material
+ FBasePropertyOverrideNodeRef ParentParameter = Parent->FindBasePropertyOverride(BasePropertyOverrideName.Value);
+
+ CurrentMaterialNode->BasePropertyOverrides.Add(FBasePropertyOverrideNodeRef(
+ new FBasePropertyOverrideNode(ParentParameter->ParameterName,
+ ParentParameter->ParameterID,
+ ParentParameter->ParameterValue,
+ false)));
+ }
+ else
+ {
+ CurrentMaterialNode->BasePropertyOverrides.Add(FBasePropertyOverrideNodeRef(
+ new FBasePropertyOverrideNode(BasePropertyOverrideName.Value, BasePropertyOverrideName.Key, TempValue, bIsOverridden)));
+ }
+ }
+
+
+ CurrentMaterialNode->MaterialLayerParameters.Empty(MaterialLayerParameterInfo.Num());
+
+ for (int ParameterIndex = 0; ParameterIndex < MaterialLayerParameterInfo.Num(); ++ParameterIndex)
+ {
+ FMaterialLayersFunctions Functions;
+ bool bIsOverridden = CurrentMaterialInterface->GetMaterialLayersParameterValue(MaterialLayerParameterInfo[ParameterIndex], Functions, MaterialLayerGuids[ParameterIndex], false);
+
+ if (!bIsOverridden)
+ {
+ // Check the parent for this variable
+ FAnalyzedMaterialNodePtr Parent = CurrentMaterialNode->Parent;
+ // We shouldn't be able to get in here for the base Material
+ check(Parent.IsValid());
+
+ FStaticMaterialLayerParameterNodeRef ParentParameter = Parent->FindMaterialLayerParameter(MaterialLayerParameterInfo[ParameterIndex].Name);
+
+ CurrentMaterialNode->MaterialLayerParameters.Add(FStaticMaterialLayerParameterNodeRef(
+ new FStaticMaterialLayerParameterNode(ParentParameter->ParameterName,
+ ParentParameter->ParameterValue,
+ false)));
+ }
+ else
+ {
+ CurrentMaterialNode->MaterialLayerParameters.Add(FStaticMaterialLayerParameterNodeRef(
+ new FStaticMaterialLayerParameterNode(MaterialLayerParameterInfo[ParameterIndex].Name,
+ Functions.GetStaticPermutationString(),
+ true)));
+ }
+ }
+
+ CurrentMaterialNode->StaticSwitchParameters.Empty(StaticSwitchParameterInfo.Num());
+
+ for (int ParameterIndex = 0; ParameterIndex < StaticSwitchParameterInfo.Num(); ++ParameterIndex)
+ {
+ bool bStaticSwitchValue;
+ bool bIsOverridden = CurrentMaterialInterface->GetStaticSwitchParameterValue(StaticSwitchParameterInfo[ParameterIndex], bStaticSwitchValue, StaticSwitchGuids[ParameterIndex], false, false);
+
+ if (!bIsOverridden)
+ {
+ // Check the parent for this variable
+ FAnalyzedMaterialNodePtr Parent = CurrentMaterialNode->Parent;
+ // We shouldn't be able to get in here for the base Material
+ check(Parent.IsValid());
+
+ FStaticSwitchParameterNodeRef ParentParameter = Parent->FindStaticSwitchParameter(StaticSwitchParameterInfo[ParameterIndex].Name);
+
+ CurrentMaterialNode->StaticSwitchParameters.Add(FStaticSwitchParameterNodeRef(
+ new FStaticSwitchParameterNode(ParentParameter->ParameterName,
+ ParentParameter->ParameterValue,
+ false)));
+ }
+ else
+ {
+ CurrentMaterialNode->StaticSwitchParameters.Add(FStaticSwitchParameterNodeRef(
+ new FStaticSwitchParameterNode(StaticSwitchParameterInfo[ParameterIndex].Name, bStaticSwitchValue, true)));
+ }
+ }
+
+ CurrentMaterialNode->StaticComponentMaskParameters.Empty(StaticMaskParameterInfo.Num());
+
+ for (int ParameterIndex = 0; ParameterIndex < StaticMaskParameterInfo.Num(); ++ParameterIndex)
+ {
+ bool R, G, B, A;
+
+ bool bIsOverridden = CurrentMaterialInterface->GetStaticComponentMaskParameterValue(StaticMaskParameterInfo[ParameterIndex], R, G, B, A, StaticMaskGuids[ParameterIndex], false, false);
+
+ if(!bIsOverridden)
+ {
+ // Check the parent for this variable
+ FAnalyzedMaterialNodePtr Parent = CurrentMaterialNode->Parent;
+ // We shouldn't be able to get in here for the base Material
+ check(Parent.IsValid());
+
+ FStaticComponentMaskParameterNodeRef ParentParameter = Parent->FindStaticComponentMaskParameter(StaticMaskParameterInfo[ParameterIndex].Name);
+
+ CurrentMaterialNode->StaticComponentMaskParameters.Add(FStaticComponentMaskParameterNodeRef(
+ new FStaticComponentMaskParameterNode(ParentParameter->ParameterName,
+ ParentParameter->R,
+ ParentParameter->G,
+ ParentParameter->B,
+ ParentParameter->A,
+ false)));
+ }
+ else
+ {
+ CurrentMaterialNode->StaticComponentMaskParameters.Add(FStaticComponentMaskParameterNodeRef(
+ new FStaticComponentMaskParameterNode(StaticMaskParameterInfo[ParameterIndex].Name, R, G, B, A, true)));
+ }
+ }
+
+ CurrentMaterialQueueIndex++;
+}
+
+bool FAnalyzeForIdenticalPermutationsAsyncTask::CreateMaterialPermutationHashForNode(const FAnalyzedMaterialNodeRef& MaterialNode, uint32& OutHash)
+{
+ TArray ByteArray;
+
+ bool bAnyOverrides = false;
+
+ for (int ParameterIndex = 0; ParameterIndex < MaterialNode->BasePropertyOverrides.Num(); ++ParameterIndex)
+ {
+ FString FloatToHash = FString::SanitizeFloat(MaterialNode->BasePropertyOverrides[ParameterIndex]->ParameterValue);
+ ByteArray.Append(TCHAR_TO_ANSI(*FloatToHash), FloatToHash.Len());
+ bAnyOverrides = bAnyOverrides || MaterialNode->BasePropertyOverrides[ParameterIndex]->bOverride;
+ }
+
+ for (int ParameterIndex = 0; ParameterIndex < MaterialNode->MaterialLayerParameters.Num(); ++ParameterIndex)
+ {
+ ByteArray.Append(TCHAR_TO_ANSI(*MaterialNode->MaterialLayerParameters[ParameterIndex]->ParameterValue), MaterialNode->MaterialLayerParameters[ParameterIndex]->ParameterValue.Len());
+ bAnyOverrides = bAnyOverrides || MaterialNode->MaterialLayerParameters[ParameterIndex]->bOverride;
+ }
+
+ for(int ParameterIndex = 0; ParameterIndex < MaterialNode->StaticSwitchParameters.Num(); ++ParameterIndex)
+ {
+ ByteArray.Add(MaterialNode->StaticSwitchParameters[ParameterIndex]->ParameterValue ? 1 : 0);
+ bAnyOverrides = bAnyOverrides || MaterialNode->StaticSwitchParameters[ParameterIndex]->bOverride;
+ }
+
+ for(int ParameterIndex = 0; ParameterIndex < MaterialNode->StaticComponentMaskParameters.Num(); ++ParameterIndex)
+ {
+ FStaticComponentMaskParameterNodeRef NodeRef = MaterialNode->StaticComponentMaskParameters[ParameterIndex];
+ ByteArray.Add(NodeRef->R ? 1 : 0);
+ ByteArray.Add(NodeRef->G ? 1 : 0);
+ ByteArray.Add(NodeRef->B ? 1 : 0);
+ ByteArray.Add(NodeRef->A ? 1 : 0);
+ bAnyOverrides = bAnyOverrides || NodeRef->bOverride;
+ }
+
+ OutHash = CityHash32(ByteArray.GetData(), ByteArray.Num());
+
+ return bAnyOverrides;
+}
+
+void FAnalyzeForIdenticalPermutationsAsyncTask::DoWork()
+{
+ for(int i = 0; i < MaterialQueue.Num(); ++i)
+ {
+ FAnalyzedMaterialNodeRef CurrentMaterialNode = MaterialQueue[i];
+
+ MaterialQueue.Append(CurrentMaterialNode->GetChildNodes());
+
+ uint32 MaterialPermutationHash = 0;
+
+ if(CreateMaterialPermutationHashForNode(CurrentMaterialNode, MaterialPermutationHash))
+ {
+ MaterialPermutationHashToMaterialObjectPath.FindOrAdd(MaterialPermutationHash).Add(CurrentMaterialNode->ObjectPath);
+ }
+ }
+
+ GatherSuggestions();
+}
+
+void FAnalyzeForIdenticalPermutationsAsyncTask::GatherSuggestions()
+{
+ for (TPair>& IdenticalPermutations : MaterialPermutationHashToMaterialObjectPath)
+ {
+ if (IdenticalPermutations.Value.Num() > 1)
+ {
+ TArray AllNames;
+ AssetCount = IdenticalPermutations.Value.Num();
+ for (int i = 0; i < IdenticalPermutations.Value.Num(); ++i)
+ {
+ FString PermutationString = IdenticalPermutations.Value[i].ToString();
+ AllNames.Add(PermutationString);
+ }
+
+ FPermutationSuggestionData NewData = FPermutationSuggestionData(FText::Format(LOCTEXT("IdenticalPermutationSuggestions",
+ "The following {0} materials all have identical permutations."),
+ FText::AsNumber(AssetCount)),
+ AllNames);
+
+ Suggestions.Add
+ (
+ AssetCount,
+ NewData
+ );
+ }
+ }
+}
+
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SMaterialAnalyzer.h b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SMaterialAnalyzer.h
new file mode 100644
index 000000000000..4fb8b859162c
--- /dev/null
+++ b/Engine/Plugins/Editor/MaterialAnalyzer/Source/Private/SMaterialAnalyzer.h
@@ -0,0 +1,283 @@
+// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Containers/Queue.h"
+#include "Input/Reply.h"
+#include "Widgets/DeclarativeSyntaxSupport.h"
+#include "Engine/GameViewportClient.h"
+#include "Widgets/SCompoundWidget.h"
+#include "Framework/Commands/UICommandList.h"
+#include "Framework/Docking/TabManager.h"
+#include "Widgets/Views/STreeView.h"
+#include "AnalyzedMaterialNode.h"
+#include "SAnalyzedMaterialNodeWidgetItem.h"
+#include "Materials/Material.h"
+#include "Materials/MaterialInstance.h"
+#include "ContentBrowserModule.h"
+#include "Widgets/Input/SComboButton.h"
+#include "Widgets/Layout/SScrollBox.h"
+#include "Widgets/Images/SThrobber.h"
+
+struct FBuildBasicMaterialTreeAsyncTask : FNonAbandonableTask
+{
+public:
+ /** File data loaded for the async read */
+ TArray& MaterialTreeRoot;
+
+ TArray AssetDataToAnalyze;
+
+ /** Initializes the variables needed to load and verify the data */
+ FBuildBasicMaterialTreeAsyncTask(TArray& InMaterialTreeRoot, const TArray& InAssetDataToAnalyze):
+ MaterialTreeRoot(InMaterialTreeRoot),
+ AssetDataToAnalyze(InAssetDataToAnalyze)
+ {
+
+ }
+
+ FAnalyzedMaterialNodePtr FindOrMakeBranchNode(FAnalyzedMaterialNodePtr ParentNode, const FAssetData* ChildData);
+
+ /**
+ * Loads and hashes the file data. Empties the data if the hash check fails
+ */
+ void DoWork();
+
+ FORCEINLINE TStatId GetStatId() const
+ {
+ RETURN_QUICK_DECLARE_CYCLE_STAT(FBuildBasicMaterialTreeAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
+ }
+};
+
+struct FAnalyzeMaterialTreeAsyncTask
+{
+public:
+ FAnalyzedMaterialNodeRef MaterialTreeRoot;
+ const TArray& AssetDataToAnalyze;
+
+ TArray MaterialQueue;
+
+ int32 CurrentMaterialQueueIndex;
+
+ FAnalyzedMaterialNodeRef CurrentMaterialNode;
+ UMaterialInterface* CurrentMaterialInterface;
+
+ TArray BasePropertyOverrideInfo;
+
+ TArray MaterialLayerParameterInfo;
+ TArray MaterialLayerGuids;
+
+ TArray