代码混淆

This commit is contained in:
lishuo 2023-04-14 16:15:29 +08:00
parent fb75ff5318
commit ce213460f9
55 changed files with 7663 additions and 4 deletions

33
Assets/Editor/CEditor.cs Normal file
View File

@ -0,0 +1,33 @@
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class CEditor : Editor
{
[MenuItem("GameObject/CopyHierarchyPath", false, 11)]
public static void CopyHierarchy()
{
GameObject go = Selection.activeGameObject;
string path = go.GetRPath();
path = path.Replace("Canvas (Environment)/", "");
GUIUtility.systemCopyBuffer = path;
}
}
public static class Ex
{
public static string GetRPath(this GameObject go)
{
GameObject current = go;
string path = current.name;
while (null != current.transform.parent)
{
current = current.transform.parent.gameObject;
path = current.name + "/" + path;
}
return path;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c50a36b17c46c9b4396813a8b0ec2e88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class ExportAssets : Editor
{
[@MenuItem("Juze/Build Asset Bundles")]
static void BuildAssetBundles()
{
BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, BuildAssetBundleOptions.UncompressedAssetBundle, BuildTarget.StandaloneWindows);
}
[MenuItem("GameObject/UI/PFButton", false, 100)]
static void CreatePFButtonCommand()
{
GameObject prefab = Resources.Load<GameObject>("UI/Control/PfUIButton");
GameObject game = Instantiate(prefab, Selection.activeTransform);
}
[MenuItem("GameObject/UI/PFUIInputField", false, 100)]
static void CreatePFUIInputFieldCommand()
{
GameObject prefab = Resources.Load<GameObject>("UI/Control/PFUIInputField");
GameObject game = Instantiate(prefab, Selection.activeTransform);
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9696ca9d085337943844a940a23e41f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a5024d7258021714e832782d73a13749
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 85d4f615e53b346478ff4dff569a317f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
/*
InfoPlist.strings
IosToRN
Created by lishuo on 2021/2/24.
*/
"CFBundleName"="PowerFun";
"CFBundleDisplayName"="PowerFun";

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a4aa11356c3e11a41941fc03fae3a26b
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cde742ac5daeb784ca7837d4e2622f26
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
/*
InfoPlist.strings
IosToRN
Created by lishuo on 2021/2/24.
*/
"CFBundleName"="运动地球";
"CFBundleDisplayName"="运动地球";

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d407327a982cae2468c2e1c372b5dbd3
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
public class MyBuildPostprocessor
{
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
{
//Debug.Log(pathToBuiltProject);
//System.IO.File.AppendAllText("D:\\test.txt",pathToBuiltProject);
#if !(UNITY_IOS || UNITY_ANDROID)
var dir = Path.GetDirectoryName(pathToBuiltProject);
var managedPath = dir + "\\PowerFun_Data\\Managed";
if (!Directory.Exists(managedPath))
{
Directory.CreateDirectory(managedPath);
}
File.Copy(dir + "\\PowerFun_Data\\Plugins\\x86_64\\ANT_WrappedLib.dll", dir + "\\PowerFun_Data\\Managed\\ANT_WrappedLib.dll");
File.Copy(dir + "\\PowerFun_Data\\Plugins\\x86_64\\DSI_CP210xManufacturing_3_1.dll", dir + "\\PowerFun_Data\\Managed\\DSI_CP210xManufacturing_3_1.dll");
File.Copy(dir + "\\PowerFun_Data\\Plugins\\x86_64\\DSI_SiUSBXp_3_1.dll", dir + "\\PowerFun_Data\\Managed\\DSI_SiUSBXp_3_1.dll");
#endif
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c69c286f97edb5d408caa387050a40c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,77 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using ChillyRoom.UnityEditor.iOS.Xcode;
namespace Assets
{
public class NativeLocale
{
public static void AddLocalizedStringsIOS(string projectPath, string localizedDirectoryPath)
{
DirectoryInfo dir = new DirectoryInfo(localizedDirectoryPath);
if (!dir.Exists)
return;
List<string> locales = new List<string>();
var localeDirs = dir.GetDirectories("*.lproj", SearchOption.TopDirectoryOnly);
foreach (var sub in localeDirs)
locales.Add(Path.GetFileNameWithoutExtension(sub.Name));
AddLocalizedStringsIOS(projectPath, localizedDirectoryPath, locales);
}
public static void AddLocalizedStringsIOS(string projectPath, string localizedDirectoryPath, List<string> validLocales)
{
string projPath = projectPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromFile(projPath);
foreach (var locale in validLocales)
{
// copy contents in the localization directory to project directory
string src = Path.Combine(localizedDirectoryPath, locale + ".lproj");
DirectoryCopy(src, Path.Combine(projectPath, "Unity-iPhone/" + locale + ".lproj"));
string fileRelatvePath = string.Format("Unity-iPhone/{0}.lproj/InfoPlist.strings", locale);
proj.AddLocalization("Localizable.strings", locale, fileRelatvePath);
}
proj.WriteToFile(projPath);
}
private static void DirectoryCopy(string sourceDirName, string destDirName)
{
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
if (!dir.Exists)
return;
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
// skip unity meta files
if (file.FullName.EndsWith(".meta"))
continue;
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, false);
}
DirectoryInfo[] dirs = dir.GetDirectories();
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 04d272fbec5a52a4a8a1bb6b201a3030
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cbfad999c612c244f9a6c609a97122ca
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a403a09e241a0480a957591ea60fb785, type: 3}
m_Name: settings
m_EditorClassIdentifier:
usesCleartextTraffic: 1
writeExternalStorage: 0
accessFineLocation: 0
addsKotlin: 1
addsAndroidBrowser: 1
enableJetifier: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 580a44b4404956543a86c106381eae18
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,141 @@

#if UNITY_IOS && UNITY_EDITOR
using UnityEditor;
using System.IO;
using UnityEngine;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using System;
#endif
public static class XCodePostProcessBuild
{
#if UNITY_IOS && UNITY_EDITOR
private static readonly string[] csAddFrameworks = new string[]{
"Security.framework","WebKit.framework", "CoreGraphics.framework"
};
[PostProcessBuild(11)]
public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuiltProject)
{
if (BuildTarget.iOS != buildTarget)
{
return;
}
string projectPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
SetFrameworksAndBuildSettings(projectPath);
AddGooglePlist(pathToBuiltProject,projectPath);
SetInfoList(pathToBuiltProject, "com.ZhiXingPai.PowerFun", "wxe3573a84e7e29902");
SetAssociatedDomains(projectPath, "wx.powerfun.com.cn");
Assets.NativeLocale.AddLocalizedStringsIOS(pathToBuiltProject, Path.Combine(Application.dataPath, "Editor/Locale"));
}
private static void AddGooglePlist(string pathToBuiltProject,string path)
{
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(path));
// This is the Xcode target in the generated project
string target = proj.GetUnityMainTargetGuid();
// Copy plist from the project folder to the build folder
FileUtil.CopyFileOrDirectory("Assets/Plugins/iOS/GoogleService-Info.plist", pathToBuiltProject + "/GoogleService-Info.plist");
proj.AddFileToBuild(target, proj.AddFile("GoogleService-Info.plist", "GoogleService-Info.plist"));
// Write PBXProject object back to the file
proj.WriteToFile(path);
}
private static void SetFrameworksAndBuildSettings(string path)
{
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(path));
string target = proj.GetUnityMainTargetGuid();
Debug.Log("Target Name is " + target);
// 设置 BuildSettings
proj.AddBuildProperty(target, "Other Linker Flags", "-Objc -all_load");
proj.SetBuildProperty(target, "ENABLE_BITCODE", "NO");
//根据微信SDK文档的要求加入相关的Frameworks
for (int i = 0; i < csAddFrameworks.Length; ++i)
{
if (!proj.ContainsFramework(target, csAddFrameworks[i]))
proj.AddFrameworkToProject(target, csAddFrameworks[i], false);
}
//苹果登录配置项
//proj.AddCapability(target, PBXCapabilityType.SignInWithApple);
proj.AddFrameworkToProject(proj.GetUnityFrameworkTargetGuid(), "AuthenticationServices.framework", false);
//移除无用配置项
proj.RemoveFrameworkFromProject(proj.GetUnityFrameworkTargetGuid(), "Chromium Embedded Framework.framework");
proj.SetBuildProperty(proj.ProjectGuid(), "ENABLE_BITCODE", "NO");
proj.SetBuildProperty(proj.ProjectGuid(), "CLANG_ENABLE_MODULES", "YES");
File.WriteAllText(path, proj.WriteToString());
}
public static void SetInfoList(string buildPath, string wxUrlName, string wxScheme)
{
string listPath = buildPath + "/Info.plist";
PlistDocument plist = new PlistDocument();
plist.ReadFromString(File.ReadAllText(listPath));
// 在“info”标签栏的“URL type“添加“URL scheme”,值为你在微信后台注册的应用程序的 AppID
PlistElementArray urlArray = plist.root.CreateArray("CFBundleURLTypes");
PlistElementDict dict = urlArray.AddDict();
dict.SetString("CFBundleTypeRole", "Editor");
dict.SetString("CFBundleURLName", wxUrlName);
PlistElementArray urlSchemes = dict.CreateArray("CFBundleURLSchemes");
urlSchemes.AddString(wxScheme);
PlistElementDict dictMine = urlArray.AddDict();
dictMine.SetString("CFBundleTypeRole", "Editor");
dictMine.SetString("CFBundleURLName", "powerfunx");
PlistElementArray urlSchemesMine = dictMine.CreateArray("CFBundleURLSchemes");
urlSchemesMine.AddString("powerfunx");
PlistElementDict dictGG = urlArray.AddDict();
dictGG.SetString("CFBundleTypeRole", "Editor");
dictGG.SetString("CFBundleURLName", "google");
PlistElementArray urlSchemesGG = dictGG.CreateArray("CFBundleURLSchemes");
urlSchemesGG.AddString("com.googleusercontent.apps.492440200766-spo9287smn7epmlarib2pek9kficsq86");
// 在 “info”标签栏的“LSApplicationQueriesSchemes“添加weixin wechat和weixinULAPI
PlistElementArray wxArray = plist.root.CreateArray("LSApplicationQueriesSchemes");
wxArray.AddString("weixin");
wxArray.AddString("wechat");
wxArray.AddString("weixinULAPI");
wxArray.AddString("powerfun");
// 设置转向
//var orientations = plist.root.CreateArray("UISupportedInterfaceOrientations");
//orientations.AddString("UIInterfaceOrientationLandscapeRight");
//orientations.AddString("UIInterfaceOrientationLandscapeLeft");
PlistElementDict qxLoc = plist.root.CreateDict("NSLocationWhenInUseUsageDescription");
qxLoc.SetBoolean("NSLocationWhenInUseUsageDescription", true);
plist.root.SetString("NSCameraUsageDescription","Use the camera to scan the QR code or take a photo or take a video to upload.");
plist.root.SetString("NSPhotoLibraryUsageDescription", "Need to access your image file to upload.");
plist.root.SetString("NSLocationWhenInUseUsageDescription", "Need to get your geographic location in order to push the route for you.");
plist.root.SetBoolean("ITSAppUsesNonExemptEncryption", false);
File.WriteAllText(listPath, plist.WriteToString());
}
// 设置Associated Domains
public static void SetAssociatedDomains(string pbxProjectPath, string domainUrl)
{
//默认 Target Name, 你自己的可能不一样
string targetName = "Unity-iPhone";
//Set the entitlements file name to what you want but make sure it has this extension
string entitlementsFileName = "my_app.entitlements";
var entitlements = new ProjectCapabilityManager(pbxProjectPath, entitlementsFileName, targetName);
entitlements.AddAssociatedDomains(new string[] { "applinks:" + domainUrl });
//苹果登录
entitlements.AddSignInWithApple();
entitlements.AddPushNotifications(false);
entitlements.WriteToFile();
}
#endif
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84ef3790c38d9564cb7770687283c42d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Editor/Xcode.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a9b05453b42e5aa40b0b080cdec35b95
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,813 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ChillyRoom.UnityEditor.iOS.Xcode
{
internal class DeviceTypeRequirement
{
public static readonly string Key = "idiom";
public static readonly string Any = "universal";
public static readonly string iPhone = "iphone";
public static readonly string iPad = "ipad";
public static readonly string Mac = "mac";
public static readonly string iWatch = "watch";
}
internal class MemoryRequirement
{
public static readonly string Key = "memory";
public static readonly string Any = "";
public static readonly string Mem1GB = "1GB";
public static readonly string Mem2GB = "2GB";
}
internal class GraphicsRequirement
{
public static readonly string Key = "graphics-feature-set";
public static readonly string Any = "";
public static readonly string Metal1v2 = "metal1v2";
public static readonly string Metal2v2 = "metal2v2";
}
// only used for image sets
internal class SizeClassRequirement
{
public static readonly string HeightKey = "height-class";
public static readonly string WidthKey = "width-class";
public static readonly string Any = "";
public static readonly string Compact = "compact";
public static readonly string Regular = "regular";
}
// only used for image sets
internal class ScaleRequirement
{
public static readonly string Key = "scale";
public static readonly string Any = ""; // vector image
public static readonly string X1 = "1x";
public static readonly string X2 = "2x";
public static readonly string X3 = "3x";
}
internal class DeviceRequirement
{
internal Dictionary<string, string> values = new Dictionary<string, string>();
public DeviceRequirement AddDevice(string device)
{
AddCustom(DeviceTypeRequirement.Key, device);
return this;
}
public DeviceRequirement AddMemory(string memory)
{
AddCustom(MemoryRequirement.Key, memory);
return this;
}
public DeviceRequirement AddGraphics(string graphics)
{
AddCustom(GraphicsRequirement.Key, graphics);
return this;
}
public DeviceRequirement AddWidthClass(string sizeClass)
{
AddCustom(SizeClassRequirement.WidthKey, sizeClass);
return this;
}
public DeviceRequirement AddHeightClass(string sizeClass)
{
AddCustom(SizeClassRequirement.HeightKey, sizeClass);
return this;
}
public DeviceRequirement AddScale(string scale)
{
AddCustom(ScaleRequirement.Key, scale);
return this;
}
public DeviceRequirement AddCustom(string key, string value)
{
if (values.ContainsKey(key))
values.Remove(key);
values.Add(key, value);
return this;
}
public DeviceRequirement()
{
values.Add("idiom", DeviceTypeRequirement.Any);
}
}
internal class AssetCatalog
{
AssetFolder m_Root;
public string path { get { return m_Root.path; } }
public AssetFolder root { get { return m_Root; } }
public AssetCatalog(string path, string authorId)
{
if (Path.GetExtension(path) != ".xcassets")
throw new Exception("Asset catalogs must have xcassets extension");
m_Root = new AssetFolder(path, null, authorId);
}
AssetFolder OpenFolderForResource(string relativePath)
{
var pathItems = PBXPath.Split(relativePath).ToList();
// remove path filename
pathItems.RemoveAt(pathItems.Count - 1);
AssetFolder folder = root;
foreach (var pathItem in pathItems)
folder = folder.OpenFolder(pathItem);
return folder;
}
// Checks if a dataset at the given path exists and returns it if it does.
// Otherwise, creates a new dataset. Parent folders are created if needed.
// Note: the path is filesystem path, not logical asset name formed
// only from names of the folders that have "provides namespace" attribute.
// If you want to put certain resources in folders with namespace, first
// manually create the folders and then set the providesNamespace attribute.
// OpenNamespacedFolder may help to do this.
public AssetDataSet OpenDataSet(string relativePath)
{
var folder = OpenFolderForResource(relativePath);
return folder.OpenDataSet(Path.GetFileName(relativePath));
}
public AssetImageSet OpenImageSet(string relativePath)
{
var folder = OpenFolderForResource(relativePath);
return folder.OpenImageSet(Path.GetFileName(relativePath));
}
public AssetImageStack OpenImageStack(string relativePath)
{
var folder = OpenFolderForResource(relativePath);
return folder.OpenImageStack(Path.GetFileName(relativePath));
}
public AssetBrandAssetGroup OpenBrandAssetGroup(string relativePath)
{
var folder = OpenFolderForResource(relativePath);
return folder.OpenBrandAssetGroup(Path.GetFileName(relativePath));
}
// Checks if a folder with given path exists and returns it if it does.
// Otherwise, creates a new folder. Parent folders are created if needed.
public AssetFolder OpenFolder(string relativePath)
{
if (relativePath == null)
return root;
var pathItems = PBXPath.Split(relativePath);
if (pathItems.Length == 0)
return root;
AssetFolder folder = root;
foreach (var pathItem in pathItems)
folder = folder.OpenFolder(pathItem);
return folder;
}
// Creates a directory structure with "provides namespace" attribute.
// First, retrieves or creates the directory at relativeBasePath, creating parent
// directories if needed. Effectively calls OpenFolder(relativeBasePath).
// Then, relative to this directory, creates namespacePath directories with "provides
// namespace" attribute set. Fails if the attribute can't be set.
public AssetFolder OpenNamespacedFolder(string relativeBasePath, string namespacePath)
{
var folder = OpenFolder(relativeBasePath);
var pathItems = PBXPath.Split(namespacePath);
foreach (var pathItem in pathItems)
{
folder = folder.OpenFolder(pathItem);
folder.providesNamespace = true;
}
return folder;
}
public void Write()
{
Write(null);
}
public void Write(List<string> warnings)
{
m_Root.Write(warnings);
}
}
internal abstract class AssetCatalogItem
{
public readonly string name;
public readonly string authorId;
public string path { get { return m_Path; } }
protected Dictionary<string, string> m_Properties = new Dictionary<string, string>();
protected string m_Path;
public AssetCatalogItem(string name, string authorId)
{
if (name != null && name.Contains("/"))
throw new Exception("Asset catalog item must not have slashes in name");
this.name = name;
this.authorId = authorId;
}
protected JsonElementDict WriteInfoToJson(JsonDocument doc)
{
var info = doc.root.CreateDict("info");
info.SetInteger("version", 1);
info.SetString("author", authorId);
return info;
}
public abstract void Write(List<string> warnings);
}
internal class AssetFolder : AssetCatalogItem
{
List<AssetCatalogItem> m_Items = new List<AssetCatalogItem>();
bool m_ProvidesNamespace = false;
public bool providesNamespace
{
get { return m_ProvidesNamespace; }
set {
if (m_Items.Count > 0 && value != m_ProvidesNamespace)
throw new Exception("Asset folder namespace providing status can't be "+
"changed after items have been added");
m_ProvidesNamespace = value;
}
}
internal AssetFolder(string parentPath, string name, string authorId) : base(name, authorId)
{
if (name != null)
m_Path = Path.Combine(parentPath, name);
else
m_Path = parentPath;
}
// Checks if a folder with given name exists and returns it if it does.
// Otherwise, creates a new folder.
public AssetFolder OpenFolder(string name)
{
var item = GetChild(name);
if (item != null)
{
if (item is AssetFolder)
return item as AssetFolder;
throw new Exception("The given path is already occupied with an asset");
}
var folder = new AssetFolder(m_Path, name, authorId);
m_Items.Add(folder);
return folder;
}
T GetExistingItemWithType<T>(string name) where T : class
{
var item = GetChild(name);
if (item != null)
{
if (item is T)
return item as T;
throw new Exception("The given path is already occupied with an asset");
}
return null;
}
// Checks if a dataset with given name exists and returns it if it does.
// Otherwise, creates a new data set.
public AssetDataSet OpenDataSet(string name)
{
var item = GetExistingItemWithType<AssetDataSet>(name);
if (item != null)
return item;
var dataset = new AssetDataSet(m_Path, name, authorId);
m_Items.Add(dataset);
return dataset;
}
// Checks if an imageset with given name exists and returns it if it does.
// Otherwise, creates a new image set.
public AssetImageSet OpenImageSet(string name)
{
var item = GetExistingItemWithType<AssetImageSet>(name);
if (item != null)
return item;
var imageset = new AssetImageSet(m_Path, name, authorId);
m_Items.Add(imageset);
return imageset;
}
// Checks if a image stack with given name exists and returns it if it does.
// Otherwise, creates a new image stack.
public AssetImageStack OpenImageStack(string name)
{
var item = GetExistingItemWithType<AssetImageStack>(name);
if (item != null)
return item;
var imageStack = new AssetImageStack(m_Path, name, authorId);
m_Items.Add(imageStack);
return imageStack;
}
// Checks if a brand asset with given name exists and returns it if it does.
// Otherwise, creates a new brand asset.
public AssetBrandAssetGroup OpenBrandAssetGroup(string name)
{
var item = GetExistingItemWithType<AssetBrandAssetGroup>(name);
if (item != null)
return item;
var brandAsset = new AssetBrandAssetGroup(m_Path, name, authorId);
m_Items.Add(brandAsset);
return brandAsset;
}
// Returns the requested item or null if not found
public AssetCatalogItem GetChild(string name)
{
foreach (var item in m_Items)
{
if (item.name == name)
return item;
}
return null;
}
void WriteJson()
{
if (!providesNamespace)
return; // json is optional when namespace is not provided
var doc = new JsonDocument();
WriteInfoToJson(doc);
var props = doc.root.CreateDict("properties");
props.SetBoolean("provides-namespace", providesNamespace);
doc.WriteToFile(Path.Combine(m_Path, "Contents.json"));
}
public override void Write(List<string> warnings)
{
if (Directory.Exists(m_Path))
Directory.Delete(m_Path, true); // ensure we start from clean state
Directory.CreateDirectory(m_Path);
WriteJson();
foreach (var item in m_Items)
item.Write(warnings);
}
}
abstract class AssetCatalogItemWithVariants : AssetCatalogItem
{
protected List<VariantData> m_Variants = new List<VariantData>();
protected List<string> m_ODRTags = new List<string>();
protected AssetCatalogItemWithVariants(string name, string authorId) :
base(name, authorId)
{
}
protected class VariantData
{
public DeviceRequirement requirement;
public string path;
public VariantData(DeviceRequirement requirement, string path)
{
this.requirement = requirement;
this.path = path;
}
}
public bool HasVariant(DeviceRequirement requirement)
{
foreach (var item in m_Variants)
{
if (item.requirement.values == requirement.values)
return true;
}
return false;
}
public void AddOnDemandResourceTag(string tag)
{
if (!m_ODRTags.Contains(tag))
m_ODRTags.Add(tag);
}
protected void AddVariant(VariantData newItem)
{
foreach (var item in m_Variants)
{
if (item.requirement.values == newItem.requirement.values)
throw new Exception("The given requirement has been already added");
if (Path.GetFileName(item.path) == Path.GetFileName(path))
throw new Exception("Two items within the same set must not have the same file name");
}
if (Path.GetFileName(newItem.path) == "Contents.json")
throw new Exception("The file name must not be equal to Contents.json");
m_Variants.Add(newItem);
}
protected void WriteODRTagsToJson(JsonElementDict info)
{
if (m_ODRTags.Count > 0)
{
var tags = info.CreateArray("on-demand-resource-tags");
foreach (var tag in m_ODRTags)
tags.AddString(tag);
}
}
protected void WriteRequirementsToJson(JsonElementDict item, DeviceRequirement req)
{
foreach (var kv in req.values)
{
if (kv.Value != null && kv.Value != "")
item.SetString(kv.Key, kv.Value);
}
}
}
internal class AssetDataSet : AssetCatalogItemWithVariants
{
class DataSetVariant : VariantData
{
public string id;
public DataSetVariant(DeviceRequirement requirement, string path, string id) : base(requirement, path)
{
this.id = id;
}
}
internal AssetDataSet(string parentPath, string name, string authorId) : base(name, authorId)
{
m_Path = Path.Combine(parentPath, name + ".dataset");
}
// an exception is thrown is two equivalent requirements are added.
// The same asset dataset must not have paths with equivalent filenames.
// The identifier allows to identify which data variant is actually loaded (use
// the typeIdentifer property of the NSDataAsset that was created from the data set)
public void AddVariant(DeviceRequirement requirement, string path, string typeIdentifier)
{
foreach (DataSetVariant item in m_Variants)
{
if (item.id != null && typeIdentifier != null && item.id == typeIdentifier)
throw new Exception("Two items within the same dataset must not have the same id");
}
AddVariant(new DataSetVariant(requirement, path, typeIdentifier));
}
public override void Write(List<string> warnings)
{
Directory.CreateDirectory(m_Path);
var doc = new JsonDocument();
var info = WriteInfoToJson(doc);
WriteODRTagsToJson(info);
var data = doc.root.CreateArray("data");
foreach (DataSetVariant item in m_Variants)
{
var filename = Path.GetFileName(item.path);
if (!File.Exists(item.path))
{
if (warnings != null)
warnings.Add("File not found: " + item.path);
}
else
File.Copy(item.path, Path.Combine(m_Path, filename));
var docItem = data.AddDict();
docItem.SetString("filename", filename);
WriteRequirementsToJson(docItem, item.requirement);
if (item.id != null)
docItem.SetString("universal-type-identifier", item.id);
}
doc.WriteToFile(Path.Combine(m_Path, "Contents.json"));
}
}
internal class ImageAlignment
{
public int left = 0, right = 0, top = 0, bottom = 0;
}
internal class ImageResizing
{
public enum SlicingType
{
Horizontal,
Vertical,
HorizontalAndVertical
}
public enum ResizeMode
{
Stretch,
Tile
}
public SlicingType type = SlicingType.HorizontalAndVertical;
public int left = 0; // only valid for horizontal slicing
public int right = 0; // only valid for horizontal slicing
public int top = 0; // only valid for vertical slicing
public int bottom = 0; // only valid for vertical slicing
public ResizeMode centerResizeMode = ResizeMode.Stretch;
public int centerWidth = 0; // only valid for vertical slicing
public int centerHeight = 0; // only valid for horizontal slicing
}
// TODO: rendering intent property
internal class AssetImageSet : AssetCatalogItemWithVariants
{
internal AssetImageSet(string assetCatalogPath, string name, string authorId) : base(name, authorId)
{
m_Path = Path.Combine(assetCatalogPath, name + ".imageset");
}
class ImageSetVariant : VariantData
{
public ImageAlignment alignment = null;
public ImageResizing resizing = null;
public ImageSetVariant(DeviceRequirement requirement, string path) : base(requirement, path)
{
}
}
public void AddVariant(DeviceRequirement requirement, string path)
{
AddVariant(new ImageSetVariant(requirement, path));
}
public void AddVariant(DeviceRequirement requirement, string path, ImageAlignment alignment, ImageResizing resizing)
{
var imageset = new ImageSetVariant(requirement, path);
imageset.alignment = alignment;
imageset.resizing = resizing;
AddVariant(imageset);
}
void WriteAlignmentToJson(JsonElementDict item, ImageAlignment alignment)
{
var docAlignment = item.CreateDict("alignment-insets");
docAlignment.SetInteger("top", alignment.top);
docAlignment.SetInteger("bottom", alignment.bottom);
docAlignment.SetInteger("left", alignment.left);
docAlignment.SetInteger("right", alignment.right);
}
static string GetSlicingMode(ImageResizing.SlicingType mode)
{
switch (mode)
{
case ImageResizing.SlicingType.Horizontal: return "3-part-horizontal";
case ImageResizing.SlicingType.Vertical: return "3-part-vertical";
case ImageResizing.SlicingType.HorizontalAndVertical: return "9-part";
}
return "";
}
static string GetCenterResizeMode(ImageResizing.ResizeMode mode)
{
switch (mode)
{
case ImageResizing.ResizeMode.Stretch: return "stretch";
case ImageResizing.ResizeMode.Tile: return "tile";
}
return "";
}
void WriteResizingToJson(JsonElementDict item, ImageResizing resizing)
{
var docResizing = item.CreateDict("resizing");
docResizing.SetString("mode", GetSlicingMode(resizing.type));
var docCenter = docResizing.CreateDict("center");
docCenter.SetString("mode", GetCenterResizeMode(resizing.centerResizeMode));
docCenter.SetInteger("width", resizing.centerWidth);
docCenter.SetInteger("height", resizing.centerHeight);
var docInsets = docResizing.CreateDict("cap-insets");
docInsets.SetInteger("top", resizing.top);
docInsets.SetInteger("bottom", resizing.bottom);
docInsets.SetInteger("left", resizing.left);
docInsets.SetInteger("right", resizing.right);
}
public override void Write(List<string> warnings)
{
Directory.CreateDirectory(m_Path);
var doc = new JsonDocument();
var info = WriteInfoToJson(doc);
WriteODRTagsToJson(info);
var images = doc.root.CreateArray("images");
foreach (ImageSetVariant item in m_Variants)
{
var filename = Path.GetFileName(item.path);
if (!File.Exists(item.path))
{
if (warnings != null)
warnings.Add("File not found: " + item.path);
}
else
File.Copy(item.path, Path.Combine(m_Path, filename));
var docItem = images.AddDict();
docItem.SetString("filename", filename);
WriteRequirementsToJson(docItem, item.requirement);
if (item.alignment != null)
WriteAlignmentToJson(docItem, item.alignment);
if (item.resizing != null)
WriteResizingToJson(docItem, item.resizing);
}
doc.WriteToFile(Path.Combine(m_Path, "Contents.json"));
}
}
/* A stack layer may either contain an image set or reference another imageset
*/
class AssetImageStackLayer : AssetCatalogItem
{
internal AssetImageStackLayer(string assetCatalogPath, string name, string authorId) : base(name, authorId)
{
m_Path = Path.Combine(assetCatalogPath, name + ".imagestacklayer");
m_Imageset = new AssetImageSet(m_Path, "Content", authorId);
}
AssetImageSet m_Imageset = null;
string m_ReferencedName = null;
public void SetReference(string name)
{
m_Imageset = null;
m_ReferencedName = name;
}
public string ReferencedName()
{
return m_ReferencedName;
}
public AssetImageSet GetImageSet()
{
return m_Imageset;
}
public override void Write(List<string> warnings)
{
Directory.CreateDirectory(m_Path);
var doc = new JsonDocument();
WriteInfoToJson(doc);
if (m_ReferencedName != null)
{
var props = doc.root.CreateDict("properties");
var reference = props.CreateDict("content-reference");
reference.SetString("type", "image-set");
reference.SetString("name", m_ReferencedName);
reference.SetString("matching-style", "fully-qualified-name");
}
if (m_Imageset != null)
m_Imageset.Write(warnings);
doc.WriteToFile(Path.Combine(m_Path, "Contents.json"));
}
}
class AssetImageStack : AssetCatalogItem
{
List<AssetImageStackLayer> m_Layers = new List<AssetImageStackLayer>();
internal AssetImageStack(string assetCatalogPath, string name, string authorId) : base(name, authorId)
{
m_Path = Path.Combine(assetCatalogPath, name + ".imagestack");
}
public AssetImageStackLayer AddLayer(string name)
{
foreach (var layer in m_Layers)
{
if (layer.name == name)
throw new Exception("A layer with given name already exists");
}
var newLayer = new AssetImageStackLayer(m_Path, name, authorId);
m_Layers.Add(newLayer);
return newLayer;
}
public override void Write(List<string> warnings)
{
Directory.CreateDirectory(m_Path);
var doc = new JsonDocument();
WriteInfoToJson(doc);
var docLayers = doc.root.CreateArray("layers");
foreach (var layer in m_Layers)
{
layer.Write(warnings);
var docLayer = docLayers.AddDict();
docLayer.SetString("filename", Path.GetFileName(layer.path));
}
doc.WriteToFile(Path.Combine(m_Path, "Contents.json"));
}
}
class AssetBrandAssetGroup : AssetCatalogItem
{
class AssetBrandAssetItem
{
internal string idiom = null;
internal string role = null;
internal int width, height;
internal AssetCatalogItem item = null;
}
List<AssetBrandAssetItem> m_Items = new List<AssetBrandAssetItem>();
internal AssetBrandAssetGroup(string assetCatalogPath, string name, string authorId) : base(name, authorId)
{
m_Path = Path.Combine(assetCatalogPath, name + ".brandassets");
}
void AddItem(AssetCatalogItem item, string idiom, string role, int width, int height)
{
foreach (var it in m_Items)
{
if (it.item.name == item.name)
throw new Exception("An item with given name already exists");
}
var newItem = new AssetBrandAssetItem();
newItem.item = item;
newItem.idiom = idiom;
newItem.role = role;
newItem.width = width;
newItem.height = height;
m_Items.Add(newItem);
}
public AssetImageSet OpenImageSet(string name, string idiom, string role, int width, int height)
{
var newItem = new AssetImageSet(m_Path, name, authorId);
AddItem(newItem, idiom, role, width, height);
return newItem;
}
public AssetImageStack OpenImageStack(string name, string idiom, string role, int width, int height)
{
var newItem = new AssetImageStack(m_Path, name, authorId);
AddItem(newItem, idiom, role, width, height);
return newItem;
}
public override void Write(List<string> warnings)
{
Directory.CreateDirectory(m_Path);
var doc = new JsonDocument();
WriteInfoToJson(doc);
var docAssets = doc.root.CreateArray("assets");
foreach (var item in m_Items)
{
var docAsset = docAssets.AddDict();
docAsset.SetString("size", String.Format("{0}x{1}", item.width, item.height));
docAsset.SetString("idiom", item.idiom);
docAsset.SetString("role", item.role);
docAsset.SetString("filename", Path.GetFileName(item.item.path));
item.item.Write(warnings);
}
doc.WriteToFile(Path.Combine(m_Path, "Contents.json"));
}
}
} // namespace UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 46dd33873affe4e5caf86cb784bd831a
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
namespace ChillyRoom.UnityEditor.iOS.Xcode
{
internal class JsonElement
{
protected JsonElement() {}
// convenience methods
public string AsString() { return ((JsonElementString)this).value; }
public int AsInteger() { return ((JsonElementInteger)this).value; }
public bool AsBoolean() { return ((JsonElementBoolean)this).value; }
public JsonElementArray AsArray() { return (JsonElementArray)this; }
public JsonElementDict AsDict() { return (JsonElementDict)this; }
public JsonElement this[string key]
{
get { return AsDict()[key]; }
set { AsDict()[key] = value; }
}
}
internal class JsonElementString : JsonElement
{
public JsonElementString(string v) { value = v; }
public string value;
}
internal class JsonElementInteger : JsonElement
{
public JsonElementInteger(int v) { value = v; }
public int value;
}
internal class JsonElementBoolean : JsonElement
{
public JsonElementBoolean(bool v) { value = v; }
public bool value;
}
internal class JsonElementDict : JsonElement
{
public JsonElementDict() : base() {}
private SortedDictionary<string, JsonElement> m_PrivateValue = new SortedDictionary<string, JsonElement>();
public IDictionary<string, JsonElement> values { get { return m_PrivateValue; }}
new public JsonElement this[string key]
{
get {
if (values.ContainsKey(key))
return values[key];
return null;
}
set { this.values[key] = value; }
}
public bool Contains(string key)
{
return values.ContainsKey(key);
}
public void Remove(string key)
{
values.Remove(key);
}
// convenience methods
public void SetInteger(string key, int val)
{
values[key] = new JsonElementInteger(val);
}
public void SetString(string key, string val)
{
values[key] = new JsonElementString(val);
}
public void SetBoolean(string key, bool val)
{
values[key] = new JsonElementBoolean(val);
}
public JsonElementArray CreateArray(string key)
{
var v = new JsonElementArray();
values[key] = v;
return v;
}
public JsonElementDict CreateDict(string key)
{
var v = new JsonElementDict();
values[key] = v;
return v;
}
}
internal class JsonElementArray : JsonElement
{
public JsonElementArray() : base() {}
public List<JsonElement> values = new List<JsonElement>();
// convenience methods
public void AddString(string val)
{
values.Add(new JsonElementString(val));
}
public void AddInteger(int val)
{
values.Add(new JsonElementInteger(val));
}
public void AddBoolean(bool val)
{
values.Add(new JsonElementBoolean(val));
}
public JsonElementArray AddArray()
{
var v = new JsonElementArray();
values.Add(v);
return v;
}
public JsonElementDict AddDict()
{
var v = new JsonElementDict();
values.Add(v);
return v;
}
}
internal class JsonDocument
{
public JsonElementDict root;
public string indentString = " ";
public JsonDocument()
{
root = new JsonElementDict();
}
void AppendIndent(StringBuilder sb, int indent)
{
for (int i = 0; i < indent; ++i)
sb.Append(indentString);
}
void WriteString(StringBuilder sb, string str)
{
// TODO: escape
sb.Append('"');
sb.Append(str);
sb.Append('"');
}
void WriteBoolean(StringBuilder sb, bool value)
{
sb.Append(value ? "true" : "false");
}
void WriteInteger(StringBuilder sb, int value)
{
sb.Append(value.ToString());
}
void WriteDictKeyValue(StringBuilder sb, string key, JsonElement value, int indent)
{
sb.Append("\n");
AppendIndent(sb, indent);
WriteString(sb, key);
sb.Append(" : ");
if (value is JsonElementString)
WriteString(sb, value.AsString());
else if (value is JsonElementInteger)
WriteInteger(sb, value.AsInteger());
else if (value is JsonElementBoolean)
WriteBoolean(sb, value.AsBoolean());
else if (value is JsonElementDict)
WriteDict(sb, value.AsDict(), indent);
else if (value is JsonElementArray)
WriteArray(sb, value.AsArray(), indent);
}
void WriteDict(StringBuilder sb, JsonElementDict el, int indent)
{
sb.Append("{");
bool hasElement = false;
foreach (var key in el.values.Keys)
{
if (hasElement)
sb.Append(","); // trailing commas not supported
WriteDictKeyValue(sb, key, el[key], indent+1);
hasElement = true;
}
sb.Append("\n");
AppendIndent(sb, indent);
sb.Append("}");
}
void WriteArray(StringBuilder sb, JsonElementArray el, int indent)
{
sb.Append("[");
bool hasElement = false;
foreach (var value in el.values)
{
if (hasElement)
sb.Append(","); // trailing commas not supported
sb.Append("\n");
AppendIndent(sb, indent+1);
if (value is JsonElementString)
WriteString(sb, value.AsString());
else if (value is JsonElementInteger)
WriteInteger(sb, value.AsInteger());
else if (value is JsonElementBoolean)
WriteBoolean(sb, value.AsBoolean());
else if (value is JsonElementDict)
WriteDict(sb, value.AsDict(), indent+1);
else if (value is JsonElementArray)
WriteArray(sb, value.AsArray(), indent+1);
hasElement = true;
}
sb.Append("\n");
AppendIndent(sb, indent);
sb.Append("]");
}
public void WriteToFile(string path)
{
File.WriteAllText(path, WriteToString());
}
public void WriteToStream(TextWriter tw)
{
tw.Write(WriteToString());
}
public string WriteToString()
{
var sb = new StringBuilder();
WriteDict(sb, root, 0);
return sb.ToString();
}
}
} // namespace UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 052ac8a8c9ae3466f94f7b3b6c5b61af
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7625017be5ff149a6b0f20ee645ce11e
folderAsset: yes
timeCreated: 1496741689
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
using System.Collections.Generic;
using System.Collections;
using System;
namespace ChillyRoom.UnityEditor.iOS.Xcode.PBX
{
class PBXElement
{
protected PBXElement() {}
// convenience methods
public string AsString() { return ((PBXElementString)this).value; }
public PBXElementArray AsArray() { return (PBXElementArray)this; }
public PBXElementDict AsDict() { return (PBXElementDict)this; }
public PBXElement this[string key]
{
get { return AsDict()[key]; }
set { AsDict()[key] = value; }
}
}
class PBXElementString : PBXElement
{
public PBXElementString(string v) { value = v; }
public string value;
}
class PBXElementDict : PBXElement
{
public PBXElementDict() : base() {}
private Dictionary<string, PBXElement> m_PrivateValue = new Dictionary<string, PBXElement>();
public IDictionary<string, PBXElement> values { get { return m_PrivateValue; }}
new public PBXElement this[string key]
{
get {
if (values.ContainsKey(key))
return values[key];
return null;
}
set { this.values[key] = value; }
}
public bool Contains(string key)
{
return values.ContainsKey(key);
}
public void Remove(string key)
{
values.Remove(key);
}
public void SetString(string key, string val)
{
values[key] = new PBXElementString(val);
}
public PBXElementArray CreateArray(string key)
{
var v = new PBXElementArray();
values[key] = v;
return v;
}
public PBXElementDict CreateDict(string key)
{
var v = new PBXElementDict();
values[key] = v;
return v;
}
}
class PBXElementArray : PBXElement
{
public PBXElementArray() : base() {}
public List<PBXElement> values = new List<PBXElement>();
// convenience methods
public void AddString(string val)
{
values.Add(new PBXElementString(val));
}
public PBXElementArray AddArray()
{
var v = new PBXElementArray();
values.Add(v);
return v;
}
public PBXElementDict AddDict()
{
var v = new PBXElementDict();
values.Add(v);
return v;
}
}
} // namespace UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 83cffec4be0e045258109e9714cdaa5f
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,243 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using System.Linq;
using System;
namespace ChillyRoom.UnityEditor.iOS.Xcode.PBX
{
enum TokenType
{
EOF,
Invalid,
String,
QuotedString,
Comment,
Semicolon, // ;
Comma, // ,
Eq, // =
LParen, // (
RParen, // )
LBrace, // {
RBrace, // }
}
class Token
{
public TokenType type;
// the line of the input stream the token starts in (0-based)
public int line;
// start and past-the-end positions of the token in the input stream
public int begin, end;
}
class TokenList : List<Token>
{
}
class Lexer
{
string text;
int pos;
int length;
int line;
public static TokenList Tokenize(string text)
{
var lexer = new Lexer();
lexer.SetText(text);
return lexer.ScanAll();
}
public void SetText(string text)
{
this.text = text + " "; // to prevent out-of-bounds access during look ahead
pos = 0;
length = text.Length;
line = 0;
}
public TokenList ScanAll()
{
var tokens = new TokenList();
while (true)
{
var tok = new Token();
ScanOne(tok);
tokens.Add(tok);
if (tok.type == TokenType.EOF)
break;
}
return tokens;
}
void UpdateNewlineStats(char ch)
{
if (ch == '\n')
line++;
}
// tokens list is modified in the case when we add BrokenLine token and need to remove already
// added tokens for the current line
void ScanOne(Token tok)
{
while (true)
{
while (pos < length && Char.IsWhiteSpace(text[pos]))
{
UpdateNewlineStats(text[pos]);
pos++;
}
if (pos >= length)
{
tok.type = TokenType.EOF;
break;
}
char ch = text[pos];
char ch2 = text[pos+1];
if (ch == '\"')
ScanQuotedString(tok);
else if (ch == '/' && ch2 == '*')
ScanMultilineComment(tok);
else if (ch == '/' && ch2 == '/')
ScanComment(tok);
else if (IsOperator(ch))
ScanOperator(tok);
else
ScanString(tok); // be more robust and accept whatever is left
return;
}
}
void ScanString(Token tok)
{
tok.type = TokenType.String;
tok.begin = pos;
while (pos < length)
{
char ch = text[pos];
char ch2 = text[pos+1];
if (Char.IsWhiteSpace(ch))
break;
else if (ch == '\"')
break;
else if (ch == '/' && ch2 == '*')
break;
else if (ch == '/' && ch2 == '/')
break;
else if (IsOperator(ch))
break;
pos++;
}
tok.end = pos;
tok.line = line;
}
void ScanQuotedString(Token tok)
{
tok.type = TokenType.QuotedString;
tok.begin = pos;
pos++;
while (pos < length)
{
// ignore escaped quotes
if (text[pos] == '\\' && text[pos+1] == '\"')
{
pos += 2;
continue;
}
// note that we close unclosed quotes
if (text[pos] == '\"')
break;
UpdateNewlineStats(text[pos]);
pos++;
}
pos++;
tok.end = pos;
tok.line = line;
}
void ScanMultilineComment(Token tok)
{
tok.type = TokenType.Comment;
tok.begin = pos;
pos += 2;
while (pos < length)
{
if (text[pos] == '*' && text[pos+1] == '/')
break;
// we support multiline comments
UpdateNewlineStats(text[pos]);
pos++;
}
pos += 2;
tok.end = pos;
tok.line = line;
}
void ScanComment(Token tok)
{
tok.type = TokenType.Comment;
tok.begin = pos;
pos += 2;
while (pos < length)
{
if (text[pos] == '\n')
break;
pos++;
}
UpdateNewlineStats(text[pos]);
pos++;
tok.end = pos;
tok.line = line;
}
bool IsOperator(char ch)
{
if (ch == ';' || ch == ',' || ch == '=' || ch == '(' || ch == ')' || ch == '{' || ch == '}')
return true;
return false;
}
void ScanOperator(Token tok)
{
switch (text[pos])
{
case ';': ScanOperatorSpecific(tok, TokenType.Semicolon); return;
case ',': ScanOperatorSpecific(tok, TokenType.Comma); return;
case '=': ScanOperatorSpecific(tok, TokenType.Eq); return;
case '(': ScanOperatorSpecific(tok, TokenType.LParen); return;
case ')': ScanOperatorSpecific(tok, TokenType.RParen); return;
case '{': ScanOperatorSpecific(tok, TokenType.LBrace); return;
case '}': ScanOperatorSpecific(tok, TokenType.RBrace); return;
default: return;
}
}
void ScanOperatorSpecific(Token tok, TokenType type)
{
tok.type = type;
tok.begin = pos;
pos++;
tok.end = pos;
tok.line = line;
}
}
} // namespace UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9766f6c4eb5ef42f99c0d5f86e34ba84
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 67cf10cf994604c719f706525399f867
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,171 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using System.Linq;
using System;
namespace ChillyRoom.UnityEditor.iOS.Xcode.PBX
{
class ValueAST {}
// IdentifierAST := <quoted string> \ <string>
class IdentifierAST : ValueAST
{
public int value = 0; // token id
}
// TreeAST := '{' KeyValuePairList '}'
// KeyValuePairList := KeyValuePair ',' KeyValuePairList
// KeyValuePair ','
// (empty)
class TreeAST : ValueAST
{
public List<KeyValueAST> values = new List<KeyValueAST>();
}
// ListAST := '(' ValueList ')'
// ValueList := ValueAST ',' ValueList
// ValueAST ','
// (empty)
class ArrayAST : ValueAST
{
public List<ValueAST> values = new List<ValueAST>();
}
// KeyValueAST := IdentifierAST '=' ValueAST ';'
// ValueAST := IdentifierAST | TreeAST | ListAST
class KeyValueAST
{
public IdentifierAST key = null;
public ValueAST value = null; // either IdentifierAST, TreeAST or ListAST
}
class Parser
{
TokenList tokens;
int currPos;
public Parser(TokenList tokens)
{
this.tokens = tokens;
currPos = SkipComments(0);
}
int SkipComments(int pos)
{
while (pos < tokens.Count && tokens[pos].type == TokenType.Comment)
{
pos++;
}
return pos;
}
// returns new position
int IncInternal(int pos)
{
if (pos >= tokens.Count)
return pos;
pos++;
return SkipComments(pos);
}
// Increments current pointer if not past the end, returns previous pos
int Inc()
{
int prev = currPos;
currPos = IncInternal(currPos);
return prev;
}
// Returns the token type of the current token
TokenType Tok()
{
if (currPos >= tokens.Count)
return TokenType.EOF;
return tokens[currPos].type;
}
void SkipIf(TokenType type)
{
if (Tok() == type)
Inc();
}
string GetErrorMsg()
{
return "Invalid PBX project (parsing line " + tokens[currPos].line + ")";
}
public IdentifierAST ParseIdentifier()
{
if (Tok() != TokenType.String && Tok() != TokenType.QuotedString)
throw new Exception(GetErrorMsg());
var ast = new IdentifierAST();
ast.value = Inc();
return ast;
}
public TreeAST ParseTree()
{
if (Tok() != TokenType.LBrace)
throw new Exception(GetErrorMsg());
Inc();
var ast = new TreeAST();
while (Tok() != TokenType.RBrace && Tok() != TokenType.EOF)
{
ast.values.Add(ParseKeyValue());
}
SkipIf(TokenType.RBrace);
return ast;
}
public ArrayAST ParseList()
{
if (Tok() != TokenType.LParen)
throw new Exception(GetErrorMsg());
Inc();
var ast = new ArrayAST();
while (Tok() != TokenType.RParen && Tok() != TokenType.EOF)
{
ast.values.Add(ParseValue());
SkipIf(TokenType.Comma);
}
SkipIf(TokenType.RParen);
return ast;
}
// throws on error
public KeyValueAST ParseKeyValue()
{
var ast = new KeyValueAST();
ast.key = ParseIdentifier();
if (Tok() != TokenType.Eq)
throw new Exception(GetErrorMsg());
Inc(); // skip '='
ast.value = ParseValue();
SkipIf(TokenType.Semicolon);
return ast;
}
// throws on error
public ValueAST ParseValue()
{
if (Tok() == TokenType.String || Tok() == TokenType.QuotedString)
return ParseIdentifier();
else if (Tok() == TokenType.LBrace)
return ParseTree();
else if (Tok() == TokenType.LParen)
return ParseList();
throw new Exception(GetErrorMsg());
}
}
} // namespace UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5ce5a244bc4e04a668ab097db4456665
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,122 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
// Basr classes for section handling
namespace ChillyRoom.UnityEditor.iOS.Xcode.PBX
{
// common base
internal abstract class SectionBase
{
public abstract void AddObject(string key, PBXElementDict value);
public abstract void WriteSection(StringBuilder sb, GUIDToCommentMap comments);
}
// known section: contains objects that we care about
internal class KnownSectionBase<T> : SectionBase where T : PBXObjectData, new()
{
private Dictionary<string, T> m_Entries = new Dictionary<string, T>();
private string m_Name;
public KnownSectionBase(string sectionName)
{
m_Name = sectionName;
}
public IEnumerable<KeyValuePair<string, T>> GetEntries()
{
return m_Entries;
}
public IEnumerable<string> GetGuids()
{
return m_Entries.Keys;
}
public IEnumerable<T> GetObjects()
{
return m_Entries.Values;
}
public override void AddObject(string key, PBXElementDict value)
{
T obj = new T();
obj.guid = key;
obj.SetPropertiesWhenSerializing(value);
obj.UpdateVars();
m_Entries[obj.guid] = obj;
}
public override void WriteSection(StringBuilder sb, GUIDToCommentMap comments)
{
if (m_Entries.Count == 0)
return; // do not write empty sections
sb.AppendFormat("\n\n/* Begin {0} section */", m_Name);
var keys = new List<string>(m_Entries.Keys);
keys.Sort(StringComparer.Ordinal);
foreach (string key in keys)
{
T obj = m_Entries[key];
obj.UpdateProps();
sb.Append("\n\t\t");
comments.WriteStringBuilder(sb, obj.guid);
sb.Append(" = ");
Serializer.WriteDict(sb, obj.GetPropertiesWhenSerializing(), 2,
obj.shouldCompact, obj.checker, comments);
sb.Append(";");
}
sb.AppendFormat("\n/* End {0} section */", m_Name);
}
// returns null if not found
public T this[string guid]
{
get {
if (m_Entries.ContainsKey(guid))
return m_Entries[guid];
return null;
}
}
public bool HasEntry(string guid)
{
return m_Entries.ContainsKey(guid);
}
public void AddEntry(T obj)
{
m_Entries[obj.guid] = obj;
}
public void RemoveEntry(string guid)
{
if (m_Entries.ContainsKey(guid))
m_Entries.Remove(guid);
}
}
// we assume there is only one PBXProject entry
internal class PBXProjectSection : KnownSectionBase<PBXProjectObjectData>
{
public PBXProjectSection() : base("PBXProject")
{
}
public PBXProjectObjectData project
{
get {
foreach (var kv in GetEntries())
return kv.Value;
return null;
}
}
}
} // UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 60639b138b83f4ce9b2448c38a6ba285
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,259 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Linq;
namespace ChillyRoom.UnityEditor.iOS.Xcode.PBX
{
class PropertyCommentChecker
{
private int m_Level;
private bool m_All;
private List<List<string>> m_Props;
/* The argument is an array of matcher strings each of which determine
whether a property with a certain path needs to be decorated with a
comment.
A path is a number of path elements concatenated by '/'. The last path
element is either:
the key if we're referring to a dict key
the value if we're referring to a value in an array
the value if we're referring to a value in a dict
All other path elements are either:
the key if the container is dict
'*' if the container is array
Path matcher has the same structure as a path, except that any of the
elements may be '*'. Matcher matches a path if both have the same number
of elements and for each pair matcher element is the same as path element
or is '*'.
a/b/c matches a/b/c but not a/b nor a/b/c/d
a/* /c matches a/d/c but not a/b nor a/b/c/d
* /* /* matches any path from three elements
*/
protected PropertyCommentChecker(int level, List<List<string>> props)
{
m_Level = level;
m_All = false;
m_Props = props;
}
public PropertyCommentChecker()
{
m_Level = 0;
m_All = false;
m_Props = new List<List<string>>();
}
public PropertyCommentChecker(IEnumerable<string> props)
{
m_Level = 0;
m_All = false;
m_Props = new List<List<string>>();
foreach (var prop in props)
{
m_Props.Add(new List<string>(prop.Split('/')));
}
}
bool CheckContained(string prop)
{
if (m_All)
return true;
foreach (var list in m_Props)
{
if (list.Count == m_Level+1)
{
if (list[m_Level] == prop)
return true;
if (list[m_Level] == "*")
{
m_All = true; // short-circuit all at this level
return true;
}
}
}
return false;
}
public bool CheckStringValueInArray(string value) { return CheckContained(value); }
public bool CheckKeyInDict(string key) { return CheckContained(key); }
public bool CheckStringValueInDict(string key, string value)
{
foreach (var list in m_Props)
{
if (list.Count == m_Level + 2)
{
if ((list[m_Level] == "*" || list[m_Level] == key) &&
list[m_Level+1] == "*" || list[m_Level+1] == value)
return true;
}
}
return false;
}
public PropertyCommentChecker NextLevel(string prop)
{
var newList = new List<List<string>>();
foreach (var list in m_Props)
{
if (list.Count <= m_Level+1)
continue;
if (list[m_Level] == "*" || list[m_Level] == prop)
newList.Add(list);
}
return new PropertyCommentChecker(m_Level + 1, newList);
}
}
class Serializer
{
public static PBXElementDict ParseTreeAST(TreeAST ast, TokenList tokens, string text)
{
var el = new PBXElementDict();
foreach (var kv in ast.values)
{
PBXElementString key = ParseIdentifierAST(kv.key, tokens, text);
PBXElement value = ParseValueAST(kv.value, tokens, text);
el[key.value] = value;
}
return el;
}
public static PBXElementArray ParseArrayAST(ArrayAST ast, TokenList tokens, string text)
{
var el = new PBXElementArray();
foreach (var v in ast.values)
{
el.values.Add(ParseValueAST(v, tokens, text));
}
return el;
}
public static PBXElement ParseValueAST(ValueAST ast, TokenList tokens, string text)
{
if (ast is TreeAST)
return ParseTreeAST((TreeAST)ast, tokens, text);
if (ast is ArrayAST)
return ParseArrayAST((ArrayAST)ast, tokens, text);
if (ast is IdentifierAST)
return ParseIdentifierAST((IdentifierAST)ast, tokens, text);
return null;
}
public static PBXElementString ParseIdentifierAST(IdentifierAST ast, TokenList tokens, string text)
{
Token tok = tokens[ast.value];
string value;
switch (tok.type)
{
case TokenType.String:
value = text.Substring(tok.begin, tok.end - tok.begin);
return new PBXElementString(value);
case TokenType.QuotedString:
value = text.Substring(tok.begin, tok.end - tok.begin);
value = PBXStream.UnquoteString(value);
return new PBXElementString(value);
default:
throw new Exception("Internal parser error");
}
}
static string k_Indent = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
static string GetIndent(int indent)
{
return k_Indent.Substring(0, indent);
}
static void WriteStringImpl(StringBuilder sb, string s, bool comment, GUIDToCommentMap comments)
{
if (comment)
comments.WriteStringBuilder(sb, s);
else
sb.Append(PBXStream.QuoteStringIfNeeded(s));
}
public static void WriteDictKeyValue(StringBuilder sb, string key, PBXElement value, int indent, bool compact,
PropertyCommentChecker checker, GUIDToCommentMap comments)
{
if (!compact)
{
sb.Append("\n");
sb.Append(GetIndent(indent));
}
WriteStringImpl(sb, key, checker.CheckKeyInDict(key), comments);
sb.Append(" = ");
if (value is PBXElementString)
WriteStringImpl(sb, value.AsString(), checker.CheckStringValueInDict(key, value.AsString()), comments);
else if (value is PBXElementDict)
WriteDict(sb, value.AsDict(), indent, compact, checker.NextLevel(key), comments);
else if (value is PBXElementArray)
WriteArray(sb, value.AsArray(), indent, compact, checker.NextLevel(key), comments);
sb.Append(";");
if (compact)
sb.Append(" ");
}
public static void WriteDict(StringBuilder sb, PBXElementDict el, int indent, bool compact,
PropertyCommentChecker checker, GUIDToCommentMap comments)
{
sb.Append("{");
if (el.Contains("isa"))
WriteDictKeyValue(sb, "isa", el["isa"], indent+1, compact, checker, comments);
var keys = new List<string>(el.values.Keys);
keys.Sort(StringComparer.Ordinal);
foreach (var key in keys)
{
if (key != "isa")
WriteDictKeyValue(sb, key, el[key], indent+1, compact, checker, comments);
}
if (!compact)
{
sb.Append("\n");
sb.Append(GetIndent(indent));
}
sb.Append("}");
}
public static void WriteArray(StringBuilder sb, PBXElementArray el, int indent, bool compact,
PropertyCommentChecker checker, GUIDToCommentMap comments)
{
sb.Append("(");
foreach (var value in el.values)
{
if (!compact)
{
sb.Append("\n");
sb.Append(GetIndent(indent+1));
}
if (value is PBXElementString)
WriteStringImpl(sb, value.AsString(), checker.CheckStringValueInArray(value.AsString()), comments);
else if (value is PBXElementDict)
WriteDict(sb, value.AsDict(), indent+1, compact, checker.NextLevel("*"), comments);
else if (value is PBXElementArray)
WriteArray(sb, value.AsArray(), indent+1, compact, checker.NextLevel("*"), comments);
sb.Append(",");
if (compact)
sb.Append(" ");
}
if (!compact)
{
sb.Append("\n");
sb.Append(GetIndent(indent));
}
sb.Append(")");
}
}
} // namespace UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: afe8316bf614347729fac3466d7c77f2
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,300 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace ChillyRoom.UnityEditor.iOS.Xcode.PBX
{
internal class GUIDToCommentMap
{
private Dictionary<string, string> m_Dict = new Dictionary<string, string>();
public string this[string guid]
{
get {
if (m_Dict.ContainsKey(guid))
return m_Dict[guid];
return null;
}
}
public void Add(string guid, string comment)
{
if (m_Dict.ContainsKey(guid))
return;
m_Dict.Add(guid, comment);
}
public void Remove(string guid)
{
m_Dict.Remove(guid);
}
public string Write(string guid)
{
string comment = this[guid];
if (comment == null)
return guid;
return String.Format("{0} /* {1} */", guid, comment);
}
public void WriteStringBuilder(StringBuilder sb, string guid)
{
string comment = this[guid];
if (comment == null)
sb.Append(guid);
else
{
// {0} /* {1} */
sb.Append(guid).Append(" /* ").Append(comment).Append(" */");
}
}
}
internal class PBXGUID
{
internal delegate string GuidGenerator();
// We allow changing Guid generator to make testing of PBXProject possible
private static GuidGenerator guidGenerator = DefaultGuidGenerator;
internal static string DefaultGuidGenerator()
{
return Guid.NewGuid().ToString("N").Substring(8).ToUpper();
}
internal static void SetGuidGenerator(GuidGenerator generator)
{
guidGenerator = generator;
}
// Generates a GUID.
public static string Generate()
{
return guidGenerator();
}
}
internal class PBXRegex
{
public static string GuidRegexString = "[A-Fa-f0-9]{24}";
}
internal class PBXStream
{
static bool DontNeedQuotes(string src)
{
// using a regex instead of explicit matching slows down common cases by 40%
if (src.Length == 0)
return false;
bool hasSlash = false;
for (int i = 0; i < src.Length; ++i)
{
char c = src[i];
if (Char.IsLetterOrDigit(c) || c == '.' || c == '*' || c == '_')
continue;
if (c == '/')
{
hasSlash = true;
continue;
}
return false;
}
if (hasSlash)
{
if (src.Contains("//") || src.Contains("/*") || src.Contains("*/"))
return false;
}
return true;
}
// Quotes the given string if it contains special characters. Note: if the string already
// contains quotes, then they are escaped and the entire string quoted again
public static string QuoteStringIfNeeded(string src)
{
if (DontNeedQuotes(src))
return src;
return "\"" + src.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n") + "\"";
}
// If the given string is quoted, removes the quotes and unescapes any quotes within the string
public static string UnquoteString(string src)
{
if (!src.StartsWith("\"") || !src.EndsWith("\""))
return src;
return src.Substring(1, src.Length - 2).Replace("\\\\", "\u569f").Replace("\\\"", "\"")
.Replace("\\n", "\n").Replace("\u569f", "\\"); // U+569f is a rarely used Chinese character
}
}
internal enum PBXFileType
{
NotBuildable,
Framework,
Source,
Resource,
CopyFile
}
internal class FileTypeUtils
{
internal class FileTypeDesc
{
public FileTypeDesc(string typeName, PBXFileType type)
{
this.name = typeName;
this.type = type;
this.isExplicit = false;
}
public FileTypeDesc(string typeName, PBXFileType type, bool isExplicit)
{
this.name = typeName;
this.type = type;
this.isExplicit = isExplicit;
}
public string name;
public PBXFileType type;
public bool isExplicit;
}
private static readonly Dictionary<string, FileTypeDesc> types =
new Dictionary<string, FileTypeDesc>
{
{ "a", new FileTypeDesc("archive.ar", PBXFileType.Framework) },
{ "app", new FileTypeDesc("wrapper.application", PBXFileType.NotBuildable, true) },
{ "appex", new FileTypeDesc("wrapper.app-extension", PBXFileType.CopyFile) },
{ "bin", new FileTypeDesc("archive.macbinary", PBXFileType.Resource) },
{ "s", new FileTypeDesc("sourcecode.asm", PBXFileType.Source) },
{ "c", new FileTypeDesc("sourcecode.c.c", PBXFileType.Source) },
{ "cc", new FileTypeDesc("sourcecode.cpp.cpp", PBXFileType.Source) },
{ "cpp", new FileTypeDesc("sourcecode.cpp.cpp", PBXFileType.Source) },
{ "swift", new FileTypeDesc("sourcecode.swift", PBXFileType.Source) },
{ "dll", new FileTypeDesc("file", PBXFileType.NotBuildable) },
{ "framework", new FileTypeDesc("wrapper.framework", PBXFileType.Framework) },
{ "h", new FileTypeDesc("sourcecode.c.h", PBXFileType.NotBuildable) },
{ "pch", new FileTypeDesc("sourcecode.c.h", PBXFileType.NotBuildable) },
{ "icns", new FileTypeDesc("image.icns", PBXFileType.Resource) },
{ "xcassets", new FileTypeDesc("folder.assetcatalog", PBXFileType.Resource) },
{ "inc", new FileTypeDesc("sourcecode.inc", PBXFileType.NotBuildable) },
{ "m", new FileTypeDesc("sourcecode.c.objc", PBXFileType.Source) },
{ "mm", new FileTypeDesc("sourcecode.cpp.objcpp", PBXFileType.Source ) },
{ "nib", new FileTypeDesc("wrapper.nib", PBXFileType.Resource) },
{ "plist", new FileTypeDesc("text.plist.xml", PBXFileType.Resource) },
{ "png", new FileTypeDesc("image.png", PBXFileType.Resource) },
{ "rtf", new FileTypeDesc("text.rtf", PBXFileType.Resource) },
{ "tiff", new FileTypeDesc("image.tiff", PBXFileType.Resource) },
{ "txt", new FileTypeDesc("text", PBXFileType.Resource) },
{ "json", new FileTypeDesc("text.json", PBXFileType.Resource) },
{ "xcodeproj", new FileTypeDesc("wrapper.pb-project", PBXFileType.NotBuildable) },
{ "xib", new FileTypeDesc("file.xib", PBXFileType.Resource) },
{ "strings", new FileTypeDesc("text.plist.strings", PBXFileType.Resource) },
{ "storyboard",new FileTypeDesc("file.storyboard", PBXFileType.Resource) },
{ "bundle", new FileTypeDesc("wrapper.plug-in", PBXFileType.Resource) },
{ "dylib", new FileTypeDesc("compiled.mach-o.dylib", PBXFileType.Framework) },
{ "tbd", new FileTypeDesc("sourcecode.text-based-dylib-definition", PBXFileType.Framework) }
};
public static string TrimExtension(string ext)
{
return ext.TrimStart('.');
}
public static bool IsKnownExtension(string ext)
{
ext = TrimExtension(ext);
return types.ContainsKey(ext);
}
internal static bool IsFileTypeExplicit(string ext)
{
ext = TrimExtension(ext);
if (types.ContainsKey(ext))
return types[ext].isExplicit;
return false;
}
public static PBXFileType GetFileType(string ext, bool isFolderRef)
{
ext = TrimExtension(ext);
if (isFolderRef)
return PBXFileType.Resource;
if (!types.ContainsKey(ext))
return PBXFileType.Resource;
return types[ext].type;
}
public static string GetTypeName(string ext)
{
ext = TrimExtension(ext);
if (types.ContainsKey(ext))
return types[ext].name;
// Xcode actually checks the file contents to determine the file type.
// Text files have "text" type and all other files have "file" type.
// Since we can't reasonably determine whether the file in question is
// a text file, we just take the safe route and return "file" type.
return "file";
}
public static bool IsBuildableFile(string ext)
{
ext = TrimExtension(ext);
if (!types.ContainsKey(ext))
return true;
if (types[ext].type != PBXFileType.NotBuildable)
return true;
return false;
}
public static bool IsBuildable(string ext, bool isFolderReference)
{
ext = TrimExtension(ext);
if (isFolderReference)
return true;
return IsBuildableFile(ext);
}
private static readonly Dictionary<PBXSourceTree, string> sourceTree = new Dictionary<PBXSourceTree, string>
{
{ PBXSourceTree.Absolute, "<absolute>" },
{ PBXSourceTree.Group, "<group>" },
{ PBXSourceTree.Build, "BUILT_PRODUCTS_DIR" },
{ PBXSourceTree.Developer, "DEVELOPER_DIR" },
{ PBXSourceTree.Sdk, "SDKROOT" },
{ PBXSourceTree.Source, "SOURCE_ROOT" },
};
private static readonly Dictionary<string, PBXSourceTree> stringToSourceTreeMap = new Dictionary<string, PBXSourceTree>
{
{ "<absolute>", PBXSourceTree.Absolute },
{ "<group>", PBXSourceTree.Group },
{ "BUILT_PRODUCTS_DIR", PBXSourceTree.Build },
{ "DEVELOPER_DIR", PBXSourceTree.Developer },
{ "SDKROOT", PBXSourceTree.Sdk },
{ "SOURCE_ROOT", PBXSourceTree.Source },
};
internal static string SourceTreeDesc(PBXSourceTree tree)
{
return sourceTree[tree];
}
// returns PBXSourceTree.Source on error
internal static PBXSourceTree ParseSourceTree(string tree)
{
if (stringToSourceTreeMap.ContainsKey(tree))
return stringToSourceTreeMap[tree];
return PBXSourceTree.Source;
}
internal static List<PBXSourceTree> AllAbsoluteSourceTrees()
{
return new List<PBXSourceTree>{PBXSourceTree.Absolute, PBXSourceTree.Build,
PBXSourceTree.Developer, PBXSourceTree.Sdk, PBXSourceTree.Source};
}
}
} // UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8eafbccf2ded14760af588ffcb1ca0f5
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,125 @@
namespace ChillyRoom.UnityEditor.iOS.Xcode
{
/// <summary>
/// List of all the capabilities available.
/// </summary>
public sealed class PBXCapabilityType
{
public static readonly PBXCapabilityType ApplePay = new PBXCapabilityType ("com.apple.ApplePay", true);
public static readonly PBXCapabilityType AppGroups = new PBXCapabilityType ("com.apple.ApplicationGroups.iOS", true);
public static readonly PBXCapabilityType AssociatedDomains = new PBXCapabilityType ("com.apple.SafariKeychain", true);
public static readonly PBXCapabilityType BackgroundModes = new PBXCapabilityType ("com.apple.BackgroundModes", false);
public static readonly PBXCapabilityType DataProtection = new PBXCapabilityType ("com.apple.DataProtection", true);
public static readonly PBXCapabilityType GameCenter = new PBXCapabilityType ("com.apple.GameCenter", false, "GameKit.framework");
public static readonly PBXCapabilityType HealthKit = new PBXCapabilityType ("com.apple.HealthKit", true, "HealthKit.framework");
public static readonly PBXCapabilityType HomeKit = new PBXCapabilityType ("com.apple.HomeKit", true, "HomeKit.framework");
public static readonly PBXCapabilityType iCloud = new PBXCapabilityType("com.apple.iCloud", true, "CloudKit.framework", true);
public static readonly PBXCapabilityType InAppPurchase = new PBXCapabilityType ("com.apple.InAppPurchase", false);
public static readonly PBXCapabilityType InterAppAudio = new PBXCapabilityType ("com.apple.InterAppAudio", true, "AudioToolbox.framework");
public static readonly PBXCapabilityType KeychainSharing = new PBXCapabilityType ("com.apple.KeychainSharing", true);
public static readonly PBXCapabilityType Maps = new PBXCapabilityType("com.apple.Maps.iOS", false, "MapKit.framework");
public static readonly PBXCapabilityType PersonalVPN = new PBXCapabilityType("com.apple.VPNLite", true, "NetworkExtension.framework");
public static readonly PBXCapabilityType PushNotifications = new PBXCapabilityType ("com.apple.Push", true);
public static readonly PBXCapabilityType Siri = new PBXCapabilityType ("com.apple.Siri", true);
public static readonly PBXCapabilityType Wallet = new PBXCapabilityType ("com.apple.Wallet", true, "PassKit.framework");
public static readonly PBXCapabilityType WirelessAccessoryConfiguration = new PBXCapabilityType("com.apple.WAC", true, "ExternalAccessory.framework");
private readonly string m_ID;
private readonly bool m_RequiresEntitlements;
private readonly string m_Framework;
private readonly bool m_OptionalFramework;
public bool optionalFramework
{
get { return m_OptionalFramework; }
}
public string framework
{
get { return m_Framework; }
}
public string id
{
get { return m_ID; }
}
public bool requiresEntitlements
{
get { return m_RequiresEntitlements; }
}
public struct TargetCapabilityPair
{
public string targetGuid;
public PBXCapabilityType capability;
public TargetCapabilityPair(string guid, PBXCapabilityType type)
{
targetGuid = guid;
capability = type;
}
}
/// <summary>
/// This private object represents what a capability changes in the PBXProject file
/// </summary>
/// <param name="id">The string used in the PBXProject file to identify the capability and mark it as enabled.</param>
/// <param name="requiresEntitlements">This capability requires an entitlements file therefore we need to add this entitlements file to the code signing entitlement.</param>
/// <param name="framework">Specify which framework need to be added to the project for this capability, if "" no framework are added.</param>
/// <param name="optionalFramework">Some capability (right now only iCloud) adds a framework, not all the time but just when some option are checked
/// this parameter indicates if one of them is checked.</param>
private PBXCapabilityType(string _id, bool _requiresEntitlements, string _framework = "", bool _optionalFramework = false)
{
m_ID = _id;
m_RequiresEntitlements = _requiresEntitlements;
m_Framework = _framework;
m_OptionalFramework = _optionalFramework;
}
public static PBXCapabilityType StringToPBXCapabilityType(string cap)
{
switch (cap)
{
case "com.apple.ApplePay":
return ApplePay;
case "com.apple.ApplicationGroups.iOS":
return AppGroups;
case "com.apple.SafariKeychain":
return AssociatedDomains;
case "com.apple.BackgroundModes":
return BackgroundModes;
case "com.apple.DataProtection":
return DataProtection;
case "com.apple.GameCenter":
return GameCenter;
case "com.apple.HealthKit":
return HealthKit;
case "com.apple.HomeKit":
return HomeKit;
case "com.apple.iCloud":
return iCloud;
case "com.apple.InAppPurchase":
return InAppPurchase;
case "com.apple.InterAppAudio":
return InterAppAudio;
case "com.apple.KeychainSharing":
return KeychainSharing;
case "com.apple.Maps.iOS":
return Maps;
case "com.apple.VPNLite":
return PersonalVPN;
case "com.apple.Push":
return PushNotifications;
case "com.apple.Siri":
return Siri;
case "com.apple.Wallet":
return Wallet;
case "WAC":
return WirelessAccessoryConfiguration;
default:
return null;
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 71f2dd6273cda4741b9b3bac41a9b765
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace ChillyRoom.UnityEditor.iOS.Xcode
{
internal class PBXPath
{
/// Replaces '\' with '/'. We need to apply this function to all paths that come from the user
/// of the API because we store paths to pbxproj and on windows we may get path with '\' slashes
/// instead of '/' slashes
public static string FixSlashes(string path)
{
if (path == null)
return null;
return path.Replace('\\', '/');
}
public static void Combine(string path1, PBXSourceTree tree1, string path2, PBXSourceTree tree2,
out string resPath, out PBXSourceTree resTree)
{
if (tree2 == PBXSourceTree.Group)
{
resPath = Combine(path1, path2);
resTree = tree1;
return;
}
resPath = path2;
resTree = tree2;
}
// Combines two paths
public static string Combine(string path1, string path2)
{
if (path2.StartsWith("/"))
return path2;
if (path1.EndsWith("/"))
return path1 + path2;
if (path1 == "")
return path2;
if (path2 == "")
return path1;
return path1 + "/" + path2;
}
public static string GetDirectory(string path)
{
path = path.TrimEnd('/');
int pos = path.LastIndexOf('/');
if (pos == -1)
return "";
else
return path.Substring(0, pos);
}
public static string GetCurrentDirectory()
{
if (Environment.OSVersion.Platform != PlatformID.MacOSX &&
Environment.OSVersion.Platform != PlatformID.Unix)
{
throw new Exception("PBX project compatible current directory can only obtained on OSX");
}
string path = Directory.GetCurrentDirectory();
path = FixSlashes(path);
if (!IsPathRooted(path))
return "/" + path;
return path;
}
public static string GetFilename(string path)
{
int pos = path.LastIndexOf('/');
if (pos == -1)
return path;
else
return path.Substring(pos + 1);
}
public static bool IsPathRooted(string path)
{
if (path == null || path.Length == 0)
return false;
return path[0] == '/';
}
public static string GetFullPath(string path)
{
if (IsPathRooted(path))
return path;
else
return Combine(GetCurrentDirectory(), path);
}
public static string[] Split(string path)
{
if (string.IsNullOrEmpty(path))
return new string[]{};
return path.Split(new[]{'/'}, StringSplitOptions.RemoveEmptyEntries);
}
}
} // UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 57be5de7dcd844631b3e835186497c25
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5923416dbf32e48428afbeee2b4eb48e
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,713 @@
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System;
using ChillyRoom.UnityEditor.iOS.Xcode.PBX;
namespace ChillyRoom.UnityEditor.iOS.Xcode
{
using PBXBuildFileSection = KnownSectionBase<PBXBuildFileData>;
using PBXFileReferenceSection = KnownSectionBase<PBXFileReferenceData>;
using PBXGroupSection = KnownSectionBase<PBXGroupData>;
using PBXContainerItemProxySection = KnownSectionBase<PBXContainerItemProxyData>;
using PBXReferenceProxySection = KnownSectionBase<PBXReferenceProxyData>;
using PBXSourcesBuildPhaseSection = KnownSectionBase<PBXSourcesBuildPhaseData>;
using PBXFrameworksBuildPhaseSection= KnownSectionBase<PBXFrameworksBuildPhaseData>;
using PBXResourcesBuildPhaseSection = KnownSectionBase<PBXResourcesBuildPhaseData>;
using PBXCopyFilesBuildPhaseSection = KnownSectionBase<PBXCopyFilesBuildPhaseData>;
using PBXShellScriptBuildPhaseSection = KnownSectionBase<PBXShellScriptBuildPhaseData>;
using PBXVariantGroupSection = KnownSectionBase<PBXVariantGroupData>;
using PBXNativeTargetSection = KnownSectionBase<PBXNativeTargetData>;
using PBXTargetDependencySection = KnownSectionBase<PBXTargetDependencyData>;
using XCBuildConfigurationSection = KnownSectionBase<XCBuildConfigurationData>;
using XCConfigurationListSection = KnownSectionBase<XCConfigurationListData>;
using UnknownSection = KnownSectionBase<PBXObjectData>;
internal class PBXProjectData
{
private Dictionary<string, SectionBase> m_Section = null;
private PBXElementDict m_RootElements = null;
private PBXElementDict m_UnknownObjects = null;
private string m_ObjectVersion = null;
private List<string> m_SectionOrder = null;
private Dictionary<string, UnknownSection> m_UnknownSections;
private PBXBuildFileSection buildFiles = null; // use BuildFiles* methods instead of manipulating directly
private PBXFileReferenceSection fileRefs = null; // use FileRefs* methods instead of manipulating directly
private PBXGroupSection groups = null; // use Groups* methods instead of manipulating directly
public PBXContainerItemProxySection containerItems = null;
public PBXReferenceProxySection references = null;
public PBXSourcesBuildPhaseSection sources = null;
public PBXFrameworksBuildPhaseSection frameworks = null;
public PBXResourcesBuildPhaseSection resources = null;
public PBXCopyFilesBuildPhaseSection copyFiles = null;
public PBXShellScriptBuildPhaseSection shellScripts = null;
public PBXNativeTargetSection nativeTargets = null;
public PBXTargetDependencySection targetDependencies = null;
public PBXVariantGroupSection variantGroups = null;
public XCBuildConfigurationSection buildConfigs = null;
public XCConfigurationListSection buildConfigLists = null;
public PBXProjectSection project = null;
// FIXME: create a separate PBXObject tree to represent these relationships
// A build file can be represented only once in all *BuildPhaseSection sections, thus
// we can simplify the cache by not caring about the file extension
private Dictionary<string, Dictionary<string, PBXBuildFileData>> m_FileGuidToBuildFileMap = null;
private Dictionary<string, PBXFileReferenceData> m_ProjectPathToFileRefMap = null;
private Dictionary<string, string> m_FileRefGuidToProjectPathMap = null;
private Dictionary<PBXSourceTree, Dictionary<string, PBXFileReferenceData>> m_RealPathToFileRefMap = null;
private Dictionary<string, PBXGroupData> m_ProjectPathToGroupMap = null;
private Dictionary<string, string> m_GroupGuidToProjectPathMap = null;
private Dictionary<string, PBXGroupData> m_GuidToParentGroupMap = null;
public PBXBuildFileData BuildFilesGet(string guid)
{
return buildFiles[guid];
}
// targetGuid is the guid of the target that contains the section that contains the buildFile
public void BuildFilesAdd(string targetGuid, PBXBuildFileData buildFile)
{
if (!m_FileGuidToBuildFileMap.ContainsKey(targetGuid))
m_FileGuidToBuildFileMap[targetGuid] = new Dictionary<string, PBXBuildFileData>();
m_FileGuidToBuildFileMap[targetGuid][buildFile.fileRef] = buildFile;
buildFiles.AddEntry(buildFile);
}
public void BuildFilesRemove(string targetGuid, string fileGuid)
{
var buildFile = BuildFilesGetForSourceFile(targetGuid, fileGuid);
if (buildFile != null)
{
m_FileGuidToBuildFileMap[targetGuid].Remove(buildFile.fileRef);
buildFiles.RemoveEntry(buildFile.guid);
}
}
public PBXBuildFileData BuildFilesGetForSourceFile(string targetGuid, string fileGuid)
{
if (!m_FileGuidToBuildFileMap.ContainsKey(targetGuid))
return null;
if (!m_FileGuidToBuildFileMap[targetGuid].ContainsKey(fileGuid))
return null;
return m_FileGuidToBuildFileMap[targetGuid][fileGuid];
}
public IEnumerable<PBXBuildFileData> BuildFilesGetAll()
{
return buildFiles.GetObjects();
}
public void FileRefsAdd(string realPath, string projectPath, PBXGroupData parent, PBXFileReferenceData fileRef)
{
fileRefs.AddEntry(fileRef);
m_ProjectPathToFileRefMap.Add(projectPath, fileRef);
m_FileRefGuidToProjectPathMap.Add(fileRef.guid, projectPath);
//m_RealPathToFileRefMap[fileRef.tree].Add(realPath, fileRef); // FIXME
if (m_RealPathToFileRefMap.ContainsKey (fileRef.tree))
m_RealPathToFileRefMap [fileRef.tree].Add (realPath, fileRef); // FIXME
m_GuidToParentGroupMap.Add(fileRef.guid, parent);
}
public IEnumerable<PBXFileReferenceData> FileRefsGetAll ()
{
return fileRefs.GetObjects();
}
public PBXFileReferenceData FileRefsGet(string guid)
{
return fileRefs[guid];
}
public PBXFileReferenceData FileRefsGetByRealPath(string path, PBXSourceTree sourceTree)
{
if (m_RealPathToFileRefMap[sourceTree].ContainsKey(path))
return m_RealPathToFileRefMap[sourceTree][path];
return null;
}
public PBXFileReferenceData FileRefsGetByProjectPath(string path)
{
if (m_ProjectPathToFileRefMap.ContainsKey(path))
return m_ProjectPathToFileRefMap[path];
return null;
}
public void FileRefsRemove(string guid)
{
PBXFileReferenceData fileRef = fileRefs[guid];
fileRefs.RemoveEntry(guid);
m_ProjectPathToFileRefMap.Remove(m_FileRefGuidToProjectPathMap[guid]);
m_FileRefGuidToProjectPathMap.Remove(guid);
foreach (var tree in FileTypeUtils.AllAbsoluteSourceTrees())
m_RealPathToFileRefMap[tree].Remove(fileRef.path);
m_GuidToParentGroupMap.Remove(guid);
}
public PBXGroupData GroupsGet(string guid)
{
return groups[guid];
}
public PBXGroupData GroupsGetByChild(string childGuid)
{
return m_GuidToParentGroupMap[childGuid];
}
public PBXGroupData GroupsGetMainGroup()
{
return groups[project.project.mainGroup];
}
/// Returns the source group identified by sourceGroup. If sourceGroup is empty or null,
/// root group is returned. If no group is found, null is returned.
public PBXGroupData GroupsGetByProjectPath(string sourceGroup)
{
if (m_ProjectPathToGroupMap.ContainsKey(sourceGroup))
return m_ProjectPathToGroupMap[sourceGroup];
return null;
}
public void GroupsAdd(string projectPath, PBXGroupData parent, PBXGroupData gr)
{
m_ProjectPathToGroupMap.Add(projectPath, gr);
m_GroupGuidToProjectPathMap.Add(gr.guid, projectPath);
m_GuidToParentGroupMap.Add(gr.guid, parent);
groups.AddEntry(gr);
}
public void GroupsAddDuplicate(PBXGroupData gr)
{
groups.AddEntry(gr);
}
public void GroupsRemove(string guid)
{
m_ProjectPathToGroupMap.Remove(m_GroupGuidToProjectPathMap[guid]);
m_GroupGuidToProjectPathMap.Remove(guid);
m_GuidToParentGroupMap.Remove(guid);
groups.RemoveEntry(guid);
}
public FileGUIDListBase BuildSectionAny(PBXNativeTargetData target, string path, bool isFolderRef)
{
string ext = Path.GetExtension(path);
var phase = FileTypeUtils.GetFileType(ext, isFolderRef);
switch (phase) {
case PBXFileType.Framework:
foreach (var guid in target.phases)
if (frameworks.HasEntry(guid))
return frameworks[guid];
break;
case PBXFileType.Resource:
foreach (var guid in target.phases)
if (resources.HasEntry(guid))
return resources[guid];
break;
case PBXFileType.Source:
foreach (var guid in target.phases)
if (sources.HasEntry(guid))
return sources[guid];
break;
case PBXFileType.CopyFile:
foreach (var guid in target.phases)
if (copyFiles.HasEntry(guid))
return copyFiles[guid];
break;
}
return null;
}
public FileGUIDListBase BuildSectionAny(string sectionGuid)
{
if (frameworks.HasEntry(sectionGuid))
return frameworks[sectionGuid];
if (resources.HasEntry(sectionGuid))
return resources[sectionGuid];
if (sources.HasEntry(sectionGuid))
return sources[sectionGuid];
if (copyFiles.HasEntry(sectionGuid))
return copyFiles[sectionGuid];
throw new Exception(String.Format("The given GUID {0} does not refer to a known build section", sectionGuid));
}
void RefreshBuildFilesMapForBuildFileGuidList(Dictionary<string, PBXBuildFileData> mapForTarget,
FileGUIDListBase list)
{
foreach (string guid in list.files)
{
var buildFile = buildFiles[guid];
mapForTarget[buildFile.fileRef] = buildFile;
}
}
void RefreshMapsForGroupChildren(string projectPath, string realPath, PBXSourceTree realPathTree, PBXGroupData parent)
{
var children = new List<string>(parent.children);
foreach (string guid in children)
{
PBXFileReferenceData fileRef = fileRefs[guid];
string pPath;
string rPath;
PBXSourceTree rTree;
if (fileRef != null)
{
pPath = PBXPath.Combine(projectPath, fileRef.name);
PBXPath.Combine(realPath, realPathTree, fileRef.path, fileRef.tree, out rPath, out rTree);
if (!m_ProjectPathToFileRefMap.ContainsKey(pPath))
{
m_ProjectPathToFileRefMap.Add(pPath, fileRef);
}
if (!m_FileRefGuidToProjectPathMap.ContainsKey(fileRef.guid))
{
m_FileRefGuidToProjectPathMap.Add(fileRef.guid, pPath);
}
if (!m_RealPathToFileRefMap[rTree].ContainsKey(rPath))
{
m_RealPathToFileRefMap[rTree].Add(rPath, fileRef);
}
if (!m_GuidToParentGroupMap.ContainsKey(guid))
{
m_GuidToParentGroupMap.Add(guid, parent);
}
continue;
}
PBXGroupData gr = groups[guid];
if (gr != null)
{
pPath = PBXPath.Combine(projectPath, gr.name);
PBXPath.Combine(realPath, realPathTree, gr.path, gr.tree, out rPath, out rTree);
if (!m_ProjectPathToGroupMap.ContainsKey(pPath))
{
m_ProjectPathToGroupMap.Add(pPath, gr);
}
if (!m_GroupGuidToProjectPathMap.ContainsKey(gr.guid))
{
m_GroupGuidToProjectPathMap.Add(gr.guid, pPath);
}
if (!m_GuidToParentGroupMap.ContainsKey(guid))
{
m_GuidToParentGroupMap.Add(guid, parent);
}
RefreshMapsForGroupChildren(pPath, rPath, rTree, gr);
}
}
}
void RefreshAuxMaps()
{
foreach (var targetEntry in nativeTargets.GetEntries())
{
var map = new Dictionary<string, PBXBuildFileData>();
foreach (string phaseGuid in targetEntry.Value.phases)
{
if (frameworks.HasEntry(phaseGuid))
RefreshBuildFilesMapForBuildFileGuidList(map, frameworks[phaseGuid]);
if (resources.HasEntry(phaseGuid))
RefreshBuildFilesMapForBuildFileGuidList(map, resources[phaseGuid]);
if (sources.HasEntry(phaseGuid))
RefreshBuildFilesMapForBuildFileGuidList(map, sources[phaseGuid]);
if (copyFiles.HasEntry(phaseGuid))
RefreshBuildFilesMapForBuildFileGuidList(map, copyFiles[phaseGuid]);
}
m_FileGuidToBuildFileMap[targetEntry.Key] = map;
}
RefreshMapsForGroupChildren("", "", PBXSourceTree.Source, GroupsGetMainGroup());
}
public void Clear()
{
buildFiles = new PBXBuildFileSection("PBXBuildFile");
fileRefs = new PBXFileReferenceSection("PBXFileReference");
groups = new PBXGroupSection("PBXGroup");
containerItems = new PBXContainerItemProxySection("PBXContainerItemProxy");
references = new PBXReferenceProxySection("PBXReferenceProxy");
sources = new PBXSourcesBuildPhaseSection("PBXSourcesBuildPhase");
frameworks = new PBXFrameworksBuildPhaseSection("PBXFrameworksBuildPhase");
resources = new PBXResourcesBuildPhaseSection("PBXResourcesBuildPhase");
copyFiles = new PBXCopyFilesBuildPhaseSection("PBXCopyFilesBuildPhase");
shellScripts = new PBXShellScriptBuildPhaseSection("PBXShellScriptBuildPhase");
nativeTargets = new PBXNativeTargetSection("PBXNativeTarget");
targetDependencies = new PBXTargetDependencySection("PBXTargetDependency");
variantGroups = new PBXVariantGroupSection("PBXVariantGroup");
buildConfigs = new XCBuildConfigurationSection("XCBuildConfiguration");
buildConfigLists = new XCConfigurationListSection("XCConfigurationList");
project = new PBXProjectSection();
m_UnknownSections = new Dictionary<string, UnknownSection>();
m_Section = new Dictionary<string, SectionBase>
{
{ "PBXBuildFile", buildFiles },
{ "PBXFileReference", fileRefs },
{ "PBXGroup", groups },
{ "PBXContainerItemProxy", containerItems },
{ "PBXReferenceProxy", references },
{ "PBXSourcesBuildPhase", sources },
{ "PBXFrameworksBuildPhase", frameworks },
{ "PBXResourcesBuildPhase", resources },
{ "PBXCopyFilesBuildPhase", copyFiles },
{ "PBXShellScriptBuildPhase", shellScripts },
{ "PBXNativeTarget", nativeTargets },
{ "PBXTargetDependency", targetDependencies },
{ "PBXVariantGroup", variantGroups },
{ "XCBuildConfiguration", buildConfigs },
{ "XCConfigurationList", buildConfigLists },
{ "PBXProject", project },
};
m_RootElements = new PBXElementDict();
m_UnknownObjects = new PBXElementDict();
m_ObjectVersion = null;
m_SectionOrder = new List<string>{
"PBXBuildFile", "PBXContainerItemProxy", "PBXCopyFilesBuildPhase", "PBXFileReference",
"PBXFrameworksBuildPhase", "PBXGroup", "PBXNativeTarget", "PBXProject", "PBXReferenceProxy",
"PBXResourcesBuildPhase", "PBXShellScriptBuildPhase", "PBXSourcesBuildPhase", "PBXTargetDependency",
"PBXVariantGroup", "XCBuildConfiguration", "XCConfigurationList"
};
m_FileGuidToBuildFileMap = new Dictionary<string, Dictionary<string, PBXBuildFileData>>();
m_ProjectPathToFileRefMap = new Dictionary<string, PBXFileReferenceData>();
m_FileRefGuidToProjectPathMap = new Dictionary<string, string>();
m_RealPathToFileRefMap = new Dictionary<PBXSourceTree, Dictionary<string, PBXFileReferenceData>>();
foreach (var tree in FileTypeUtils.AllAbsoluteSourceTrees())
m_RealPathToFileRefMap.Add(tree, new Dictionary<string, PBXFileReferenceData>());
m_ProjectPathToGroupMap = new Dictionary<string, PBXGroupData>();
m_GroupGuidToProjectPathMap = new Dictionary<string, string>();
m_GuidToParentGroupMap = new Dictionary<string, PBXGroupData>();
}
private void BuildCommentMapForBuildFiles(GUIDToCommentMap comments, List<string> guids, string sectName)
{
foreach (var guid in guids)
{
var buildFile = BuildFilesGet(guid);
if (buildFile != null)
{
var fileRef = FileRefsGet(buildFile.fileRef);
if (fileRef != null)
comments.Add(guid, String.Format("{0} in {1}", fileRef.name, sectName));
else
{
var reference = references[buildFile.fileRef];
if (reference != null)
comments.Add(guid, String.Format("{0} in {1}", reference.path, sectName));
}
}
}
}
private GUIDToCommentMap BuildCommentMap()
{
GUIDToCommentMap comments = new GUIDToCommentMap();
// buildFiles are handled below
// filerefs are handled below
foreach (var e in groups.GetObjects())
comments.Add(e.guid, e.name);
foreach (var e in containerItems.GetObjects())
comments.Add(e.guid, "PBXContainerItemProxy");
foreach (var e in references.GetObjects())
comments.Add(e.guid, e.path);
foreach (var e in sources.GetObjects())
{
comments.Add(e.guid, "Sources");
BuildCommentMapForBuildFiles(comments, e.files, "Sources");
}
foreach (var e in resources.GetObjects())
{
comments.Add(e.guid, "Resources");
BuildCommentMapForBuildFiles(comments, e.files, "Resources");
}
foreach (var e in frameworks.GetObjects())
{
comments.Add(e.guid, "Frameworks");
BuildCommentMapForBuildFiles(comments, e.files, "Frameworks");
}
foreach (var e in copyFiles.GetObjects())
{
string sectName = e.name;
if (sectName == null)
sectName = "CopyFiles";
comments.Add(e.guid, sectName);
BuildCommentMapForBuildFiles(comments, e.files, sectName);
}
foreach (var e in shellScripts.GetObjects())
comments.Add(e.guid, "ShellScript");
foreach (var e in targetDependencies.GetObjects())
comments.Add(e.guid, "PBXTargetDependency");
foreach (var e in nativeTargets.GetObjects())
{
comments.Add(e.guid, e.name);
comments.Add(e.buildConfigList, String.Format("Build configuration list for PBXNativeTarget \"{0}\"", e.name));
}
foreach (var e in variantGroups.GetObjects())
comments.Add(e.guid, e.name);
foreach (var e in buildConfigs.GetObjects())
comments.Add(e.guid, e.name);
foreach (var e in project.GetObjects())
{
comments.Add(e.guid, "Project object");
comments.Add(e.buildConfigList, "Build configuration list for PBXProject \"Unity-iPhone\""); // FIXME: project name is hardcoded
}
foreach (var e in fileRefs.GetObjects())
comments.Add(e.guid, e.name);
if (m_RootElements.Contains("rootObject") && m_RootElements["rootObject"] is PBXElementString)
comments.Add(m_RootElements["rootObject"].AsString(), "Project object");
return comments;
}
private static PBXElementDict ParseContent(string content)
{
TokenList tokens = Lexer.Tokenize(content);
var parser = new Parser(tokens);
TreeAST ast = parser.ParseTree();
return Serializer.ParseTreeAST(ast, tokens, content);
}
public void ReadFromStream(TextReader sr)
{
Clear();
m_RootElements = ParseContent(sr.ReadToEnd());
if (!m_RootElements.Contains("objects"))
throw new Exception("Invalid PBX project file: no objects element");
var objects = m_RootElements["objects"].AsDict();
m_RootElements.Remove("objects");
m_RootElements.SetString("objects", "OBJMARKER");
if (m_RootElements.Contains("objectVersion"))
{
m_ObjectVersion = m_RootElements["objectVersion"].AsString();
m_RootElements.Remove("objectVersion");
}
var allGuids = new List<string>();
string prevSectionName = null;
foreach (var kv in objects.values)
{
allGuids.Add(kv.Key);
var el = kv.Value;
if (!(el is PBXElementDict) || !el.AsDict().Contains("isa"))
{
m_UnknownObjects.values.Add(kv.Key, el);
continue;
}
var dict = el.AsDict();
var sectionName = dict["isa"].AsString();
if (m_Section.ContainsKey(sectionName))
{
var section = m_Section[sectionName];
section.AddObject(kv.Key, dict);
}
else
{
UnknownSection section;
if (m_UnknownSections.ContainsKey(sectionName))
section = m_UnknownSections[sectionName];
else
{
section = new UnknownSection(sectionName);
m_UnknownSections.Add(sectionName, section);
}
section.AddObject(kv.Key, dict);
// update section order
if (!m_SectionOrder.Contains(sectionName))
{
int pos = 0;
if (prevSectionName != null)
{
// this never fails, because we already added any previous unknown sections
// to m_SectionOrder
pos = m_SectionOrder.FindIndex(x => x == prevSectionName);
pos += 1;
}
m_SectionOrder.Insert(pos, sectionName);
}
}
prevSectionName = sectionName;
}
RepairStructure(allGuids);
RefreshAuxMaps();
}
public string WriteToString()
{
var commentMap = BuildCommentMap();
var emptyChecker = new PropertyCommentChecker();
var emptyCommentMap = new GUIDToCommentMap();
// since we need to add custom comments, the serialization is much more complex
StringBuilder objectsSb = new StringBuilder();
if (m_ObjectVersion != null) // objectVersion comes right before objects
objectsSb.AppendFormat("objectVersion = {0};\n\t", m_ObjectVersion);
objectsSb.Append("objects = {");
foreach (string sectionName in m_SectionOrder)
{
if (m_Section.ContainsKey(sectionName))
m_Section[sectionName].WriteSection(objectsSb, commentMap);
else if (m_UnknownSections.ContainsKey(sectionName))
m_UnknownSections[sectionName].WriteSection(objectsSb, commentMap);
}
foreach (var kv in m_UnknownObjects.values)
Serializer.WriteDictKeyValue(objectsSb, kv.Key, kv.Value, 2, false, emptyChecker, emptyCommentMap);
objectsSb.Append("\n\t};");
StringBuilder contentSb = new StringBuilder();
contentSb.Append("// !$*UTF8*$!\n");
Serializer.WriteDict(contentSb, m_RootElements, 0, false,
new PropertyCommentChecker(new string[]{"rootObject/*"}), commentMap);
contentSb.Append("\n");
string content = contentSb.ToString();
content = content.Replace("objects = OBJMARKER;", objectsSb.ToString());
return content;
}
// This method walks the project structure and removes invalid entries.
void RepairStructure(List<string> allGuids)
{
var guidSet = new Dictionary<string, bool>(); // emulate HashSet on .Net 2.0
foreach (var guid in allGuids)
guidSet.Add(guid, false);
while (RepairStructureImpl(guidSet) == true)
;
}
/* Iterates the given guid list and removes all guids that are not in allGuids dictionary.
*/
static void RemoveMissingGuidsFromGuidList(PBX.GUIDList guidList, Dictionary<string, bool> allGuids)
{
List<string> guidsToRemove = null;
foreach (var guid in guidList)
{
if (!allGuids.ContainsKey(guid))
{
if (guidsToRemove == null)
guidsToRemove = new List<string>();
guidsToRemove.Add(guid);
}
}
if (guidsToRemove != null)
{
foreach (var guid in guidsToRemove)
guidList.RemoveGUID(guid);
}
}
/* Removes objects from the given @a section for which @a checker returns true.
Also removes the guids of the removed elements from allGuids dictionary.
Returns true if any objects were removed.
*/
static bool RemoveObjectsFromSection<T>(KnownSectionBase<T> section,
Dictionary<string, bool> allGuids,
Func<T, bool> checker) where T : PBXObjectData, new()
{
List<string> guidsToRemove = null;
foreach (var kv in section.GetEntries())
{
if (checker(kv.Value))
{
if (guidsToRemove == null)
guidsToRemove = new List<string>();
guidsToRemove.Add(kv.Key);
}
}
if (guidsToRemove != null)
{
foreach (var guid in guidsToRemove)
{
section.RemoveEntry(guid);
allGuids.Remove(guid);
}
return true;
}
return false;
}
// Returns true if changes were done and one should call RepairStructureImpl again
bool RepairStructureImpl(Dictionary<string, bool> allGuids)
{
bool changed = false;
// PBXBuildFile
changed |= RemoveObjectsFromSection(buildFiles, allGuids,
o => (o.fileRef == null || !allGuids.ContainsKey(o.fileRef)));
// PBXFileReference / fileRefs not cleaned
// PBXGroup
changed |= RemoveObjectsFromSection(groups, allGuids, o => o.children == null);
foreach (var o in groups.GetObjects())
RemoveMissingGuidsFromGuidList(o.children, allGuids);
// PBXContainerItem / containerItems not cleaned
// PBXReferenceProxy / references not cleaned
// PBXSourcesBuildPhase
changed |= RemoveObjectsFromSection(sources, allGuids, o => o.files == null);
foreach (var o in sources.GetObjects())
RemoveMissingGuidsFromGuidList(o.files, allGuids);
// PBXFrameworksBuildPhase
changed |= RemoveObjectsFromSection(frameworks, allGuids, o => o.files == null);
foreach (var o in frameworks.GetObjects())
RemoveMissingGuidsFromGuidList(o.files, allGuids);
// PBXResourcesBuildPhase
changed |= RemoveObjectsFromSection(resources, allGuids, o => o.files == null);
foreach (var o in resources.GetObjects())
RemoveMissingGuidsFromGuidList(o.files, allGuids);
// PBXCopyFilesBuildPhase
changed |= RemoveObjectsFromSection(copyFiles, allGuids, o => o.files == null);
foreach (var o in copyFiles.GetObjects())
RemoveMissingGuidsFromGuidList(o.files, allGuids);
// PBXShellScriptsBuildPhase
changed |= RemoveObjectsFromSection(shellScripts, allGuids, o => o.files == null);
foreach (var o in shellScripts.GetObjects())
RemoveMissingGuidsFromGuidList(o.files, allGuids);
// PBXNativeTarget
changed |= RemoveObjectsFromSection(nativeTargets, allGuids, o => o.phases == null);
foreach (var o in nativeTargets.GetObjects())
RemoveMissingGuidsFromGuidList(o.phases, allGuids);
// PBXTargetDependency / targetDependencies not cleaned
// PBXVariantGroup
changed |= RemoveObjectsFromSection(variantGroups, allGuids, o => o.children == null);
foreach (var o in variantGroups.GetObjects())
RemoveMissingGuidsFromGuidList(o.children, allGuids);
// XCBuildConfiguration / buildConfigs not cleaned
// XCConfigurationList
changed |= RemoveObjectsFromSection(buildConfigLists, allGuids, o => o.buildConfigs == null);
foreach (var o in buildConfigLists.GetObjects())
RemoveMissingGuidsFromGuidList(o.buildConfigs, allGuids);
// PBXProject project not cleaned
return changed;
}
public PBXGroupData GroupsGetByName (string name)
{
foreach (var group in groups.GetEntries())
if (group.Value.name == name)
return group.Value;
return null;
}
}
} // namespace UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 54c86bc46b87645c78e5b211a0eeada5
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,296 @@
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System;
using ChillyRoom.UnityEditor.iOS.Xcode.PBX;
namespace ChillyRoom.UnityEditor.iOS.Xcode.Extensions
{
/* This class implements a number of static methods for performing common tasks
on xcode projects.
TODO: Make sure enough stuff is exposed so that it's possible to perform the tasks
without using internal APIs
*/
public static class PBXProjectExtensions
{
// Create a wrapper class so that collection initializers work and we can have a
// compact notation. Note that we can't use Dictionary because the keys may be duplicate
internal class FlagList : List<KeyValuePair<string, string>>
{
public void Add(string flag, string value)
{
Add(new KeyValuePair<string, string>(flag, value));
}
}
internal static FlagList appExtensionReleaseBuildFlags = new FlagList
{
// { "INFOPLIST_FILE", <path/to/info.plist> },
{ "LD_RUNPATH_SEARCH_PATHS", "$(inherited)" },
{ "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks" },
{ "LD_RUNPATH_SEARCH_PATHS", "@executable_path/../../Frameworks" },
// { "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
{ "PRODUCT_NAME", "$(TARGET_NAME)" },
{ "SKIP_INSTALL", "YES" },
};
internal static FlagList appExtensionDebugBuildFlags = new FlagList
{
// { "INFOPLIST_FILE", <path/to/info.plist> },
{ "LD_RUNPATH_SEARCH_PATHS", "$(inherited)" },
{ "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks" },
{ "LD_RUNPATH_SEARCH_PATHS", "@executable_path/../../Frameworks" },
// { "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
{ "PRODUCT_NAME", "$(TARGET_NAME)" },
{ "SKIP_INSTALL", "YES" },
};
internal static FlagList watchExtensionReleaseBuildFlags = new FlagList
{
{ "ASSETCATALOG_COMPILER_COMPLICATION_NAME", "Complication" },
{ "CLANG_ANALYZER_NONNULL", "YES" },
{ "CLANG_WARN_DOCUMENTATION_COMMENTS", "YES" },
{ "CLANG_WARN_INFINITE_RECURSION", "YES" },
{ "CLANG_WARN_SUSPICIOUS_MOVE", "YES" },
{ "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym" },
{ "GCC_NO_COMMON_BLOCKS", "YES" },
//{ "INFOPLIST_FILE", "<path/to/Info.plist>" },
{ "LD_RUNPATH_SEARCH_PATHS", "$(inherited)" },
{ "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks" },
{ "LD_RUNPATH_SEARCH_PATHS", "@executable_path/../../Frameworks" },
// { "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
{ "PRODUCT_NAME", "${TARGET_NAME}" },
{ "SDKROOT", "watchos" },
{ "SKIP_INSTALL", "YES" },
{ "TARGETED_DEVICE_FAMILY", "4" },
{ "WATCHOS_DEPLOYMENT_TARGET", "3.1" },
// the following are needed to override project settings in Unity Xcode project
{ "ARCHS", "$(ARCHS_STANDARD)" },
{ "SUPPORTED_PLATFORMS", "watchos" },
{ "SUPPORTED_PLATFORMS", "watchsimulator" },
};
internal static FlagList watchExtensionDebugBuildFlags = new FlagList
{
{ "ASSETCATALOG_COMPILER_COMPLICATION_NAME", "Complication" },
{ "CLANG_ANALYZER_NONNULL", "YES" },
{ "CLANG_WARN_DOCUMENTATION_COMMENTS", "YES" },
{ "CLANG_WARN_INFINITE_RECURSION", "YES" },
{ "CLANG_WARN_SUSPICIOUS_MOVE", "YES" },
{ "DEBUG_INFORMATION_FORMAT", "dwarf" },
{ "ENABLE_TESTABILITY", "YES" },
{ "GCC_NO_COMMON_BLOCKS", "YES" },
// { "INFOPLIST_FILE", "<path/to/Info.plist>" },
{ "LD_RUNPATH_SEARCH_PATHS", "$(inherited)" },
{ "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks" },
{ "LD_RUNPATH_SEARCH_PATHS", "@executable_path/../../Frameworks" },
// { "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
{ "PRODUCT_NAME", "${TARGET_NAME}" },
{ "SDKROOT", "watchos" },
{ "SKIP_INSTALL", "YES" },
{ "TARGETED_DEVICE_FAMILY", "4" },
{ "WATCHOS_DEPLOYMENT_TARGET", "3.1" },
// the following are needed to override project settings in Unity Xcode project
{ "ARCHS", "$(ARCHS_STANDARD)" },
{ "SUPPORTED_PLATFORMS", "watchos" },
{ "SUPPORTED_PLATFORMS", "watchsimulator" },
};
internal static FlagList watchAppReleaseBuildFlags = new FlagList
{
{ "ASSETCATALOG_COMPILER_APPICON_NAME", "AppIcon" },
{ "CLANG_ANALYZER_NONNULL", "YES" },
{ "CLANG_WARN_DOCUMENTATION_COMMENTS", "YES" },
{ "CLANG_WARN_INFINITE_RECURSION", "YES" },
{ "CLANG_WARN_SUSPICIOUS_MOVE", "YES" },
{ "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym" },
{ "GCC_NO_COMMON_BLOCKS", "YES" },
//{ "IBSC_MODULE", "the extension target name with ' ' replaced with '_'" },
//{ "INFOPLIST_FILE", "<path/to/Info.plist>" },
//{ "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
{ "PRODUCT_NAME", "$(TARGET_NAME)" },
{ "SDKROOT", "watchos" },
{ "SKIP_INSTALL", "YES" },
{ "TARGETED_DEVICE_FAMILY", "4" },
{ "WATCHOS_DEPLOYMENT_TARGET", "3.1" },
// the following are needed to override project settings in Unity Xcode project
{ "ARCHS", "$(ARCHS_STANDARD)" },
{ "SUPPORTED_PLATFORMS", "watchos" },
{ "SUPPORTED_PLATFORMS", "watchsimulator" },
};
internal static FlagList watchAppDebugBuildFlags = new FlagList
{
{ "ASSETCATALOG_COMPILER_APPICON_NAME", "AppIcon" },
{ "CLANG_ANALYZER_NONNULL", "YES" },
{ "CLANG_WARN_DOCUMENTATION_COMMENTS", "YES" },
{ "CLANG_WARN_INFINITE_RECURSION", "YES" },
{ "CLANG_WARN_SUSPICIOUS_MOVE", "YES" },
{ "DEBUG_INFORMATION_FORMAT", "dwarf" },
{ "ENABLE_TESTABILITY", "YES" },
{ "GCC_NO_COMMON_BLOCKS", "YES" },
//{ "IBSC_MODULE", "the extension target name with ' ' replaced with '_'" },
//{ "INFOPLIST_FILE", "<path/to/Info.plist>" },
//{ "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
{ "PRODUCT_NAME", "$(TARGET_NAME)" },
{ "SDKROOT", "watchos" },
{ "SKIP_INSTALL", "YES" },
{ "TARGETED_DEVICE_FAMILY", "4" },
{ "WATCHOS_DEPLOYMENT_TARGET", "3.1" },
// the following are needed to override project settings in Unity Xcode project
{ "ARCHS", "$(ARCHS_STANDARD)" },
{ "SUPPORTED_PLATFORMS", "watchos" },
{ "SUPPORTED_PLATFORMS", "watchsimulator" },
};
static void SetBuildFlagsFromDict(this PBXProject proj, string configGuid, IEnumerable<KeyValuePair<string, string>> data)
{
foreach (var kv in data)
proj.AddBuildPropertyForConfig(configGuid, kv.Key, kv.Value);
}
internal static void SetDefaultAppExtensionReleaseBuildFlags(this PBXProject proj, string configGuid)
{
SetBuildFlagsFromDict(proj, configGuid, appExtensionReleaseBuildFlags);
}
internal static void SetDefaultAppExtensionDebugBuildFlags(this PBXProject proj, string configGuid)
{
SetBuildFlagsFromDict(proj, configGuid, appExtensionDebugBuildFlags);
}
internal static void SetDefaultWatchExtensionReleaseBuildFlags(this PBXProject proj, string configGuid)
{
SetBuildFlagsFromDict(proj, configGuid, watchExtensionReleaseBuildFlags);
}
internal static void SetDefaultWatchExtensionDebugBuildFlags(this PBXProject proj, string configGuid)
{
SetBuildFlagsFromDict(proj, configGuid, watchExtensionDebugBuildFlags);
}
internal static void SetDefaultWatchAppReleaseBuildFlags(this PBXProject proj, string configGuid)
{
SetBuildFlagsFromDict(proj, configGuid, watchAppReleaseBuildFlags);
}
internal static void SetDefaultWatchAppDebugBuildFlags(this PBXProject proj, string configGuid)
{
SetBuildFlagsFromDict(proj, configGuid, watchAppDebugBuildFlags);
}
/// <summary>
/// Creates an app extension.
/// </summary>
/// <returns>The GUID of the new target.</returns>
/// <param name="proj">A project passed as this argument.</param>
/// <param name="mainTargetGuid">The GUID of the main target to link the app to.</param>
/// <param name="name">The name of the app extension.</param>
/// <param name="bundleId">The bundle ID of the app extension. The bundle ID must be
/// prefixed with the parent app bundle ID.</param>
/// <param name="infoPlistPath">Path to the app extension Info.plist document.</param>
public static string AddAppExtension(this PBXProject proj, string mainTargetGuid,
string name, string bundleId, string infoPlistPath)
{
string ext = ".appex";
var newTargetGuid = proj.AddTarget(name, ext, "com.apple.product-type.app-extension");
foreach (var configName in proj.BuildConfigNames())
{
var configGuid = proj.BuildConfigByName(newTargetGuid, configName);
if (configName.Contains("Debug"))
SetDefaultAppExtensionDebugBuildFlags(proj, configGuid);
else
SetDefaultAppExtensionReleaseBuildFlags(proj, configGuid);
proj.SetBuildPropertyForConfig(configGuid, "INFOPLIST_FILE", infoPlistPath);
proj.SetBuildPropertyForConfig(configGuid, "PRODUCT_BUNDLE_IDENTIFIER", bundleId);
}
proj.AddSourcesBuildPhase(newTargetGuid);
proj.AddResourcesBuildPhase(newTargetGuid);
proj.AddFrameworksBuildPhase(newTargetGuid);
string copyFilesPhaseGuid = proj.AddCopyFilesBuildPhase(mainTargetGuid, "Embed App Extensions", "", "13");
proj.AddFileToBuildSection(mainTargetGuid, copyFilesPhaseGuid, proj.GetTargetProductFileRef(newTargetGuid));
proj.AddTargetDependency(mainTargetGuid, newTargetGuid);
return newTargetGuid;
}
/// <summary>
/// Creates a watch application.
/// </summary>
/// <returns>The GUID of the new target.</returns>
/// <param name="proj">A project passed as this argument.</param>
/// <param name="mainTargetGuid">The GUID of the main target to link the watch app to.</param>
/// <param name="watchExtensionTargetGuid">The GUID of watch extension as returned by [[AddWatchExtension()]].</param>
/// <param name="name">The name of the watch app. It must the same as the name of the watch extension.</param>
/// <param name="bundleId">The bundle ID of the watch app.</param>
/// <param name="infoPlistPath">Path to the watch app Info.plist document.</param>
public static string AddWatchApp(this PBXProject proj, string mainTargetGuid, string watchExtensionTargetGuid,
string name, string bundleId, string infoPlistPath)
{
var newTargetGuid = proj.AddTarget(name, ".app", "com.apple.product-type.application.watchapp2");
var isbcModuleName = proj.nativeTargets[watchExtensionTargetGuid].name.Replace(" ", "_");
foreach (var configName in proj.BuildConfigNames())
{
var configGuid = proj.BuildConfigByName(newTargetGuid, configName);
if (configName.Contains("Debug"))
SetDefaultWatchAppDebugBuildFlags(proj, configGuid);
else
SetDefaultWatchAppReleaseBuildFlags(proj, configGuid);
proj.SetBuildPropertyForConfig(configGuid, "PRODUCT_BUNDLE_IDENTIFIER", bundleId);
proj.SetBuildPropertyForConfig(configGuid, "INFOPLIST_FILE", infoPlistPath);
proj.SetBuildPropertyForConfig(configGuid, "IBSC_MODULE", isbcModuleName);
}
proj.AddResourcesBuildPhase(newTargetGuid);
string copyFilesGuid = proj.AddCopyFilesBuildPhase(newTargetGuid, "Embed App Extensions", "", "13");
proj.AddFileToBuildSection(newTargetGuid, copyFilesGuid, proj.GetTargetProductFileRef(watchExtensionTargetGuid));
string copyWatchFilesGuid = proj.AddCopyFilesBuildPhase(mainTargetGuid, "Embed Watch Content", "$(CONTENTS_FOLDER_PATH)/Watch", "16");
proj.AddFileToBuildSection(mainTargetGuid, copyWatchFilesGuid, proj.GetTargetProductFileRef(newTargetGuid));
proj.AddTargetDependency(newTargetGuid, watchExtensionTargetGuid);
proj.AddTargetDependency(mainTargetGuid, newTargetGuid);
return newTargetGuid;
}
/// <summary>
/// Creates a watch extension.
/// </summary>
/// <returns>The GUID of the new target.</returns>
/// <param name="proj">A project passed as this argument.</param>
/// <param name="mainTarget">The GUID of the main target to link the watch extension to.</param>
/// <param name="name">The name of the watch extension.</param>
/// <param name="bundleId">The bundle ID of the watch extension. The bundle ID must be
/// prefixed with the parent watch app bundle ID.</param>
/// <param name="infoPlistPath">Path to the watch extension Info.plist document.</param>
public static string AddWatchExtension(this PBXProject proj, string mainTarget,
string name, string bundleId, string infoPlistPath)
{
var newTargetGuid = proj.AddTarget(name, ".appex", "com.apple.product-type.watchkit2-extension");
foreach (var configName in proj.BuildConfigNames())
{
var configGuid = proj.BuildConfigByName(newTargetGuid, configName);
if (configName.Contains("Debug"))
SetDefaultWatchExtensionDebugBuildFlags(proj, configGuid);
else
SetDefaultWatchExtensionReleaseBuildFlags(proj, configGuid);
proj.SetBuildPropertyForConfig(configGuid, "PRODUCT_BUNDLE_IDENTIFIER", bundleId);
proj.SetBuildPropertyForConfig(configGuid, "INFOPLIST_FILE", infoPlistPath);
}
proj.AddSourcesBuildPhase(newTargetGuid);
proj.AddResourcesBuildPhase(newTargetGuid);
proj.AddFrameworksBuildPhase(newTargetGuid);
return newTargetGuid;
}
}
} // namespace UnityEditor.iOS.Xcode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b84f17869f54f4d7a952c9f63e5744d6
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,351 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
namespace ChillyRoom.UnityEditor.iOS.Xcode
{
public class PlistElement
{
protected PlistElement() {}
// convenience methods
public string AsString() { return ((PlistElementString)this).value; }
public int AsInteger() { return ((PlistElementInteger)this).value; }
public bool AsBoolean() { return ((PlistElementBoolean)this).value; }
public PlistElementArray AsArray() { return (PlistElementArray)this; }
public PlistElementDict AsDict() { return (PlistElementDict)this; }
public PlistElement this[string key]
{
get { return AsDict()[key]; }
set { AsDict()[key] = value; }
}
}
public class PlistElementString : PlistElement
{
public PlistElementString(string v) { value = v; }
public string value;
}
public class PlistElementInteger : PlistElement
{
public PlistElementInteger(int v) { value = v; }
public int value;
}
public class PlistElementBoolean : PlistElement
{
public PlistElementBoolean(bool v) { value = v; }
public bool value;
}
public class PlistElementDict : PlistElement
{
public PlistElementDict() : base() {}
private SortedDictionary<string, PlistElement> m_PrivateValue = new SortedDictionary<string, PlistElement>();
public IDictionary<string, PlistElement> values { get { return m_PrivateValue; }}
new public PlistElement this[string key]
{
get {
if (values.ContainsKey(key))
return values[key];
return null;
}
set { this.values[key] = value; }
}
// convenience methods
public void SetInteger(string key, int val)
{
values[key] = new PlistElementInteger(val);
}
public void SetString(string key, string val)
{
values[key] = new PlistElementString(val);
}
public void SetBoolean(string key, bool val)
{
values[key] = new PlistElementBoolean(val);
}
public PlistElementArray CreateArray(string key)
{
var v = new PlistElementArray();
values[key] = v;
return v;
}
public PlistElementDict CreateDict(string key)
{
var v = new PlistElementDict();
values[key] = v;
return v;
}
}
public class PlistElementArray : PlistElement
{
public PlistElementArray() : base() {}
public List<PlistElement> values = new List<PlistElement>();
// convenience methods
public void AddString(string val)
{
values.Add(new PlistElementString(val));
}
public void AddInteger(int val)
{
values.Add(new PlistElementInteger(val));
}
public void AddBoolean(bool val)
{
values.Add(new PlistElementBoolean(val));
}
public PlistElementArray AddArray()
{
var v = new PlistElementArray();
values.Add(v);
return v;
}
public PlistElementDict AddDict()
{
var v = new PlistElementDict();
values.Add(v);
return v;
}
}
public class PlistDocument
{
public PlistElementDict root;
public string version;
public PlistDocument()
{
root = new PlistElementDict();
version = "1.0";
}
// Parses a string that contains a XML file. No validation is done.
internal static XDocument ParseXmlNoDtd(string text)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.ProhibitDtd = false;
settings.XmlResolver = null; // prevent DTD download
XmlReader xmlReader = XmlReader.Create(new StringReader(text), settings);
return XDocument.Load(xmlReader);
}
// LINQ serializes XML DTD declaration with an explicit empty 'internal subset'
// (a pair of square brackets at the end of Doctype declaration).
// Even though this is valid XML, XCode does not like it, hence this workaround.
internal static string CleanDtdToString(XDocument doc)
{
// LINQ does not support changing the DTD of existing XDocument instances,
// so we create a dummy document for printing of the Doctype declaration.
// A single dummy element is added to force LINQ not to omit the declaration.
// Also, utf-8 encoding is forced since this is the encoding we use when writing to file in UpdateInfoPlist.
if (doc.DocumentType != null)
{
XDocument tmpDoc =
new XDocument(new XDeclaration("1.0", "utf-8", null),
new XDocumentType(doc.DocumentType.Name, doc.DocumentType.PublicId, doc.DocumentType.SystemId, null),
new XElement(doc.Root.Name));
return "" + tmpDoc.Declaration + "\n" + tmpDoc.DocumentType + "\n" + doc.Root;
}
else
{
XDocument tmpDoc = new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement(doc.Root.Name));
return "" + tmpDoc.Declaration + Environment.NewLine + doc.Root;
}
}
private static string GetText(XElement xml)
{
return String.Join("", xml.Nodes().OfType<XText>().Select(x => x.Value).ToArray());
}
private static PlistElement ReadElement(XElement xml)
{
switch (xml.Name.LocalName)
{
case "dict":
{
List<XElement> children = xml.Elements().ToList();
var el = new PlistElementDict();
if (children.Count % 2 == 1)
throw new Exception("Malformed plist file");
for (int i = 0; i < children.Count - 1; i++)
{
if (children[i].Name != "key")
throw new Exception("Malformed plist file");
string key = GetText(children[i]).Trim();
var newChild = ReadElement(children[i+1]);
if (newChild != null)
{
i++;
el[key] = newChild;
}
}
return el;
}
case "array":
{
List<XElement> children = xml.Elements().ToList();
var el = new PlistElementArray();
foreach (var childXml in children)
{
var newChild = ReadElement(childXml);
if (newChild != null)
el.values.Add(newChild);
}
return el;
}
case "string":
return new PlistElementString(GetText(xml));
case "integer":
{
int r;
if (int.TryParse(GetText(xml), out r))
return new PlistElementInteger(r);
return null;
}
case "true":
return new PlistElementBoolean(true);
case "false":
return new PlistElementBoolean(false);
default:
return null;
}
}
public void Create()
{
const string doc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" +
"<plist version=\"1.0\">" +
"<dict>" +
"</dict>" +
"</plist>";
ReadFromString(doc);
}
public void ReadFromFile(string path)
{
ReadFromString(File.ReadAllText(path));
}
public void ReadFromStream(TextReader tr)
{
ReadFromString(tr.ReadToEnd());
}
public void ReadFromString(string text)
{
XDocument doc = ParseXmlNoDtd(text);
version = (string) doc.Root.Attribute("version");
XElement xml = doc.XPathSelectElement("plist/dict");
var dict = ReadElement(xml);
if (dict == null)
throw new Exception("Error parsing plist file");
root = dict as PlistElementDict;
if (root == null)
throw new Exception("Malformed plist file");
}
private static XElement WriteElement(PlistElement el)
{
if (el is PlistElementBoolean)
{
var realEl = el as PlistElementBoolean;
return new XElement(realEl.value ? "true" : "false");
}
if (el is PlistElementInteger)
{
var realEl = el as PlistElementInteger;
return new XElement("integer", realEl.value.ToString());
}
if (el is PlistElementString)
{
var realEl = el as PlistElementString;
return new XElement("string", realEl.value);
}
if (el is PlistElementDict)
{
var realEl = el as PlistElementDict;
var dictXml = new XElement("dict");
foreach (var kv in realEl.values)
{
var keyXml = new XElement("key", kv.Key);
var valueXml = WriteElement(kv.Value);
if (valueXml != null)
{
dictXml.Add(keyXml);
dictXml.Add(valueXml);
}
}
return dictXml;
}
if (el is PlistElementArray)
{
var realEl = el as PlistElementArray;
var arrayXml = new XElement("array");
foreach (var v in realEl.values)
{
var elXml = WriteElement(v);
if (elXml != null)
arrayXml.Add(elXml);
}
return arrayXml;
}
return null;
}
public void WriteToFile(string path)
{
System.Text.Encoding utf8WithoutBom = new System.Text.UTF8Encoding(false);
File.WriteAllText(path, WriteToString(), utf8WithoutBom);
}
public void WriteToStream(TextWriter tw)
{
tw.Write(WriteToString());
}
public string WriteToString()
{
var el = WriteElement(root);
var rootEl = new XElement("plist");
rootEl.Add(new XAttribute("version", version));
rootEl.Add(el);
var doc = new XDocument();
doc.Add(rootEl);
return CleanDtdToString(doc).Replace("\r\n", "\n");
}
}
} // namespace UnityEditor.iOS.XCode

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c2ed9743fe9634c808ea1dc01c20e01e
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,578 @@
using System;
using System.IO;
namespace ChillyRoom.UnityEditor.iOS.Xcode
{
// This class is here to help you add capabilities to your Xcode project.
// Because capabilities modify the PBXProject, the entitlements file and/or the Info.plist and not consistently,
// it can be tedious.
// Therefore this class open the PBXProject that is always modify by capabilities and open Entitlement and info.plist only when needed.
// For optimisation reasons, we write the file only in the close method.
// If you don't call it the file will not be written.
public class ProjectCapabilityManager
{
private readonly string m_BuildPath;
private readonly string m_TargetGuid;
private readonly string m_PBXProjectPath;
private readonly string m_EntitlementFilePath;
private PlistDocument m_Entitlements;
private PlistDocument m_InfoPlist;
protected internal PBXProject project;
// Create the manager with the required parameter to open files and set the properties in the write place.
public ProjectCapabilityManager(string pbxProjectPath, string entitlementFilePath, string targetName)
{
m_BuildPath = Directory.GetParent(Path.GetDirectoryName(pbxProjectPath)).FullName;
m_EntitlementFilePath = entitlementFilePath;
m_PBXProjectPath = pbxProjectPath;
project = new PBXProject();
project.ReadFromString(File.ReadAllText(m_PBXProjectPath));
m_TargetGuid = project.TargetGuidByName(targetName);
}
// Write the actual file to the disk.
// If you don't call this method nothing will change.
public void WriteToFile()
{
File.WriteAllText(m_PBXProjectPath, project.WriteToString());
if (m_Entitlements != null)
m_Entitlements.WriteToFile(PBXPath.Combine(m_BuildPath, m_EntitlementFilePath));
if (m_InfoPlist != null)
m_InfoPlist.WriteToFile(PBXPath.Combine(m_BuildPath, "Info.plist"));
}
// Add the iCloud capability with the desired options.
public void AddiCloud(bool keyValueStorage, bool iCloudDocument, string[] customContainers)
{
var ent = GetOrCreateEntitlementDoc();
var val = (ent.root[ICloudEntitlements.ContainerIdValue] = new PlistElementArray()) as PlistElementArray;
if (iCloudDocument)
{
val.values.Add(new PlistElementString(ICloudEntitlements.ContainerIdValue));
var ser = (ent.root[ICloudEntitlements.ServicesKey] = new PlistElementArray()) as PlistElementArray;
ser.values.Add(new PlistElementString(ICloudEntitlements.ServicesKitValue));
ser.values.Add(new PlistElementString(ICloudEntitlements.ServicesDocValue));
var ubiquity = (ent.root[ICloudEntitlements.UbiquityContainerIdKey] = new PlistElementArray()) as PlistElementArray;
ubiquity.values.Add(new PlistElementString(ICloudEntitlements.UbiquityContainerIdValue));
for (var i = 0; i < customContainers.Length; i++)
{
ser.values.Add(new PlistElementString(customContainers[i]));
}
}
if (keyValueStorage)
{
ent.root[ICloudEntitlements.KeyValueStoreKey] = new PlistElementString(ICloudEntitlements.KeyValueStoreValue);
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.iCloud, m_EntitlementFilePath, iCloudDocument);
}
// Add Push (or remote) Notifications capability to your project
public void AddPushNotifications(bool development)
{
GetOrCreateEntitlementDoc().root[PushNotificationEntitlements.Key] = new PlistElementString(development ? PushNotificationEntitlements.DevelopmentValue : PushNotificationEntitlements.ProductionValue);
project.AddCapability(m_TargetGuid, PBXCapabilityType.PushNotifications, m_EntitlementFilePath);
}
// Add GameCenter capability to the project.
public void AddGameCenter()
{
var arr = (GetOrCreateInfoDoc().root[GameCenterInfo.Key] ?? (GetOrCreateInfoDoc().root[GameCenterInfo.Key] = new PlistElementArray())) as PlistElementArray;
arr.values.Add(new PlistElementString(GameCenterInfo.Value));
project.AddCapability(m_TargetGuid, PBXCapabilityType.GameCenter);
}
// Add Wallet capability to the project.
public void AddWallet(string[] passSubset)
{
var arr = (GetOrCreateEntitlementDoc().root[WalletEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
if ((passSubset == null || passSubset.Length == 0) && arr != null)
{
arr.values.Add(new PlistElementString(WalletEntitlements.BaseValue + WalletEntitlements.BaseValue));
}
else
{
for (var i = 0; i < passSubset.Length; i++)
{
if (arr != null)
arr.values.Add(new PlistElementString(WalletEntitlements.BaseValue + passSubset[i]));
}
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.Wallet, m_EntitlementFilePath);
}
// Add Siri capability to the project.
public void AddSiri()
{
GetOrCreateEntitlementDoc().root[SiriEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.Siri, m_EntitlementFilePath);
}
// Add Apple Pay capability to the project.
public void AddApplePay(string[] merchants)
{
var arr = (GetOrCreateEntitlementDoc().root[ApplePayEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
for (var i = 0; i < merchants.Length; i++)
{
arr.values.Add(new PlistElementString(merchants[i]));
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.ApplePay, m_EntitlementFilePath);
}
// Add In App Purchase capability to the project.
public void AddInAppPurchase()
{
project.AddCapability(m_TargetGuid, PBXCapabilityType.InAppPurchase);
}
// Add Maps capability to the project.
public void AddMaps(MapsOptions options)
{
var bundleArr = (GetOrCreateInfoDoc().root[MapsInfo.BundleKey] ?? (GetOrCreateInfoDoc().root[MapsInfo.BundleKey] = new PlistElementArray())) as PlistElementArray;
bundleArr.values.Add(new PlistElementDict());
PlistElementDict bundleDic = GetOrCreateUniqueDictElementInArray(bundleArr);
bundleDic[MapsInfo.BundleNameKey] = new PlistElementString(MapsInfo.BundleNameValue);
var bundleTypeArr = (bundleDic[MapsInfo.BundleTypeKey] ?? (bundleDic[MapsInfo.BundleTypeKey] = new PlistElementArray())) as PlistElementArray;
GetOrCreateStringElementInArray(bundleTypeArr, MapsInfo.BundleTypeValue);
var optionArr = (GetOrCreateInfoDoc().root[MapsInfo.ModeKey] ??
(GetOrCreateInfoDoc().root[MapsInfo.ModeKey] = new PlistElementArray())) as PlistElementArray;
if ((options & MapsOptions.Airplane) == MapsOptions.Airplane)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModePlaneValue);
}
if ((options & MapsOptions.Bike) == MapsOptions.Bike)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeBikeValue);
}
if ((options & MapsOptions.Bus) == MapsOptions.Bus)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeBusValue);
}
if ((options & MapsOptions.Car) == MapsOptions.Car)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeCarValue);
}
if ((options & MapsOptions.Ferry) == MapsOptions.Ferry)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeFerryValue);
}
if ((options & MapsOptions.Other) == MapsOptions.Other)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeOtherValue);
}
if ((options & MapsOptions.Pedestrian) == MapsOptions.Pedestrian)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModePedestrianValue);
}
if ((options & MapsOptions.RideSharing) == MapsOptions.RideSharing)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeRideShareValue);
}
if ((options & MapsOptions.StreetCar) == MapsOptions.StreetCar)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeStreetCarValue);
}
if ((options & MapsOptions.Subway) == MapsOptions.Subway)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeSubwayValue);
}
if ((options & MapsOptions.Taxi) == MapsOptions.Taxi)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeTaxiValue);
}
if ((options & MapsOptions.Train) == MapsOptions.Train)
{
GetOrCreateStringElementInArray(optionArr, MapsInfo.ModeTrainValue);
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.Maps);
}
// Add Personal VPN capability to the project.
public void AddPersonalVPN()
{
var arr = (GetOrCreateEntitlementDoc().root[VPNEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
arr.values.Add(new PlistElementString(VPNEntitlements.Value));
project.AddCapability(m_TargetGuid, PBXCapabilityType.PersonalVPN, m_EntitlementFilePath);
}
// Add Background capability to the project with the options wanted.
public void AddBackgroundModes(BackgroundModesOptions options)
{
var optionArr = (GetOrCreateInfoDoc().root[BackgroundInfo.Key] ??
(GetOrCreateInfoDoc().root[BackgroundInfo.Key] = new PlistElementArray())) as PlistElementArray;
if ((options & BackgroundModesOptions.ActsAsABluetoothLEAccessory) == BackgroundModesOptions.ActsAsABluetoothLEAccessory)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeActsBluetoothValue);
}
if ((options & BackgroundModesOptions.AudioAirplayPiP) == BackgroundModesOptions.AudioAirplayPiP)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeAudioValue);
}
if ((options & BackgroundModesOptions.BackgroundFetch) == BackgroundModesOptions.BackgroundFetch)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeFetchValue);
}
if ((options & BackgroundModesOptions.ExternalAccessoryCommunication) == BackgroundModesOptions.ExternalAccessoryCommunication)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeExtAccessoryValue);
}
if ((options & BackgroundModesOptions.LocationUpdates) == BackgroundModesOptions.LocationUpdates)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeLocationValue);
}
if ((options & BackgroundModesOptions.NewsstandDownloads) == BackgroundModesOptions.NewsstandDownloads)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeNewsstandValue);
}
if ((options & BackgroundModesOptions.RemoteNotifications) == BackgroundModesOptions.RemoteNotifications)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModePushValue);
}
if ((options & BackgroundModesOptions.VoiceOverIP) == BackgroundModesOptions.VoiceOverIP)
{
GetOrCreateStringElementInArray(optionArr, BackgroundInfo.ModeVOIPValue);
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.BackgroundModes);
}
// Add Keychain Sharing capability to the project with a list of groups.
public void AddKeychainSharing(string[] accessGroups)
{
var arr = (GetOrCreateEntitlementDoc().root[KeyChainEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
if (accessGroups != null)
{
for (var i = 0; i < accessGroups.Length; i++)
{
arr.values.Add(new PlistElementString(accessGroups[i]));
}
}
else
{
arr.values.Add(new PlistElementString(KeyChainEntitlements.DefaultValue));
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.KeychainSharing, m_EntitlementFilePath);
}
// Add Inter App Audio capability to the project.
public void AddInterAppAudio()
{
GetOrCreateEntitlementDoc().root[AudioEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.InterAppAudio, m_EntitlementFilePath);
}
// Add Associated Domains capability to the project.
public void AddAssociatedDomains(string[] domains)
{
var arr = (GetOrCreateEntitlementDoc().root[AssociatedDomainsEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
for (var i = 0; i < domains.Length; i++)
{
arr.values.Add(new PlistElementString(domains[i]));
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.AssociatedDomains, m_EntitlementFilePath);
}
// Add App Groups capability to the project.
public void AddAppGroups(string[] groups)
{
var arr = (GetOrCreateEntitlementDoc().root[AppGroupsEntitlements.Key] = new PlistElementArray()) as PlistElementArray;
for (var i = 0; i < groups.Length; i++)
{
arr.values.Add(new PlistElementString(groups[i]));
}
project.AddCapability(m_TargetGuid, PBXCapabilityType.AppGroups, m_EntitlementFilePath);
}
// Add HomeKit capability to the project.
public void AddHomeKit()
{
GetOrCreateEntitlementDoc().root[HomeKitEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.HomeKit, m_EntitlementFilePath);
}
// Add Data Protection capability to the project.
public void AddDataProtection()
{
GetOrCreateEntitlementDoc().root[DataProtectionEntitlements.Key] = new PlistElementString(DataProtectionEntitlements.Value);
project.AddCapability(m_TargetGuid, PBXCapabilityType.DataProtection, m_EntitlementFilePath);
}
// Add HealthKit capability to the project.
public void AddHealthKit()
{
var capabilityArr = (GetOrCreateInfoDoc().root[HealthInfo.Key] ??
(GetOrCreateInfoDoc().root[HealthInfo.Key] = new PlistElementArray())) as PlistElementArray;
GetOrCreateStringElementInArray(capabilityArr, HealthInfo.Value);
GetOrCreateEntitlementDoc().root[HealthKitEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.HealthKit, m_EntitlementFilePath);
}
// Add Wireless Accessory Configuration capability to the project.
public void AddWirelessAccessoryConfiguration()
{
GetOrCreateEntitlementDoc().root[WirelessAccessoryConfigurationEntitlements.Key] = new PlistElementBoolean(true);
project.AddCapability(m_TargetGuid, PBXCapabilityType.WirelessAccessoryConfiguration, m_EntitlementFilePath);
}
private PlistDocument GetOrCreateEntitlementDoc()
{
if (m_Entitlements == null)
{
m_Entitlements = new PlistDocument();
string[] entitlementsFiles = Directory.GetFiles(m_BuildPath, m_EntitlementFilePath);
if (entitlementsFiles.Length > 0)
{
m_Entitlements.ReadFromFile(entitlementsFiles[0]);
}
else
{
m_Entitlements.Create();
}
}
return m_Entitlements;
}
private PlistDocument GetOrCreateInfoDoc()
{
if (m_InfoPlist == null)
{
m_InfoPlist = new PlistDocument();
string[] infoFiles = Directory.GetFiles(m_BuildPath + "/", "Info.plist");
if (infoFiles.Length > 0)
{
m_InfoPlist.ReadFromFile(infoFiles[0]);
}
else
{
m_InfoPlist.Create();
}
}
return m_InfoPlist;
}
private PlistElementString GetOrCreateStringElementInArray(PlistElementArray root, string value)
{
PlistElementString r = null;
var c = root.values.Count;
var exist = false;
for (var i = 0; i < c; i++)
{
if (root.values[i] is PlistElementString && (root.values[i] as PlistElementString).value == value)
{
r = root.values[i] as PlistElementString;
exist = true;
}
}
if (!exist)
{
r = new PlistElementString(value);
root.values.Add(r);
}
return r;
}
private PlistElementDict GetOrCreateUniqueDictElementInArray(PlistElementArray root)
{
PlistElementDict r;
if (root.values.Count == 0)
{
r = root.values[0] as PlistElementDict;
}
else
{
r = new PlistElementDict();
root.values.Add(r);
}
return r;
}
}
// The list of options available for Background Mode.
[Flags]
[Serializable]
public enum BackgroundModesOptions
{
None = 0,
AudioAirplayPiP = 1<<0,
LocationUpdates = 1<<1,
VoiceOverIP = 1<<2,
NewsstandDownloads = 1<<3,
ExternalAccessoryCommunication = 1<<4,
UsesBluetoothLEAccessory = 1<<5,
ActsAsABluetoothLEAccessory = 1<<6,
BackgroundFetch = 1<<7,
RemoteNotifications = 1<<8
}
// The list of options available for Maps.
[Serializable]
[Flags]
public enum MapsOptions
{
None = 0,
Airplane = 1<<0,
Bike = 1<<1,
Bus = 1<<2,
Car = 1<<3,
Ferry = 1<<4,
Pedestrian = 1<<5,
RideSharing = 1<<6,
StreetCar = 1<<7,
Subway = 1<<8,
Taxi = 1<<9,
Train = 1<<10,
Other = 1<<11
}
/* Follows the large quantity of string used as key and value all over the place in the info.plist or entitlements file. */
internal class GameCenterInfo
{
internal static readonly string Key = "UIRequiredDeviceCapabilities";
internal static readonly string Value = "gamekit";
}
internal class MapsInfo
{
internal static readonly string BundleKey = "CFBundleDocumentTypes";
internal static readonly string BundleNameKey = "CFBundleTypeName";
internal static readonly string BundleNameValue = "MKDirectionsRequest";
internal static readonly string BundleTypeKey = "LSItemContentTypes";
internal static readonly string BundleTypeValue = "com.apple.maps.directionsrequest";
internal static readonly string ModeKey = "MKDirectionsApplicationSupportedModes";
internal static readonly string ModePlaneValue = "MKDirectionsModePlane";
internal static readonly string ModeBikeValue = "MKDirectionsModeBike";
internal static readonly string ModeBusValue = "MKDirectionsModeBus";
internal static readonly string ModeCarValue = "MKDirectionsModeCar";
internal static readonly string ModeFerryValue = "MKDirectionsModeFerry";
internal static readonly string ModeOtherValue = "MKDirectionsModeOther";
internal static readonly string ModePedestrianValue = "MKDirectionsModePedestrian";
internal static readonly string ModeRideShareValue = "MKDirectionsModeRideShare";
internal static readonly string ModeStreetCarValue = "MKDirectionsModeStreetCar";
internal static readonly string ModeSubwayValue = "MKDirectionsModeSubway";
internal static readonly string ModeTaxiValue = "MKDirectionsModeTaxi";
internal static readonly string ModeTrainValue = "MKDirectionsModeTrain";
}
internal class BackgroundInfo
{
internal static readonly string Key = "UIBackgroundModes";
internal static readonly string ModeAudioValue = "audio";
internal static readonly string ModeBluetoothValue = "bluetooth-central";
internal static readonly string ModeActsBluetoothValue = "bluetooth-peripheral";
internal static readonly string ModeExtAccessoryValue = "external-accessory";
internal static readonly string ModeFetchValue = "fetch";
internal static readonly string ModeLocationValue = "location";
internal static readonly string ModeNewsstandValue = "newsstand-content";
internal static readonly string ModePushValue = "remote-notification";
internal static readonly string ModeVOIPValue = "voip";
}
internal class HealthInfo
{
internal static readonly string Key = "UIRequiredDeviceCapabilities";
internal static readonly string Value = "healthkit";
}
internal class ICloudEntitlements
{
internal static readonly string ContainerIdKey = "com.apple.developer.icloud-container-identifiers";
internal static readonly string UbiquityContainerIdKey = "com.apple.developer.ubiquity-container-identifiers";
internal static readonly string ContainerIdValue = "iCloud.$(CFBundleIdentifier)";
internal static readonly string UbiquityContainerIdValue = "iCloud.$(CFBundleIdentifier)";
internal static readonly string ServicesKey = "com.apple.developer.icloud-services";
internal static readonly string ServicesDocValue = "CloudDocuments";
internal static readonly string ServicesKitValue = "CloudKit";
internal static readonly string KeyValueStoreKey = "com.apple.developer.ubiquity-kvstore-identifier";
internal static readonly string KeyValueStoreValue = "$(TeamIdentifierPrefix)$(CFBundleIdentifier)";
}
internal class PushNotificationEntitlements
{
internal static readonly string Key = "aps-environment";
internal static readonly string DevelopmentValue = "development";
internal static readonly string ProductionValue = "production";
}
internal class WalletEntitlements
{
internal static readonly string Key = "com.apple.developer.pass-type-identifiers";
internal static readonly string BaseValue = "$(TeamIdentifierPrefix)";
internal static readonly string DefaultValue = "*";
}
internal class SiriEntitlements
{
internal static readonly string Key = "com.apple.developer.siri";
}
internal class ApplePayEntitlements
{
internal static readonly string Key = "com.apple.developer.in-app-payments";
}
internal class VPNEntitlements
{
internal static readonly string Key = "com.apple.developer.networking.vpn.api";
internal static readonly string Value = "allow-vpn";
}
internal class KeyChainEntitlements
{
internal static readonly string Key = "keychain-access-groups";
internal static readonly string DefaultValue = "$(AppIdentifierPrefix)$(CFBundleIdentifier)";
}
internal class AudioEntitlements
{
internal static readonly string Key = "inter-app-audio";
}
internal class AssociatedDomainsEntitlements
{
// value is an array of string of domains
internal static readonly string Key = "com.apple.developer.associated-domains";
}
internal class AppGroupsEntitlements
{
// value is an array of string of groups
internal static readonly string Key = "com.apple.security.application-groups";
}
internal class HomeKitEntitlements
{
// value is bool true.
internal static readonly string Key = "com.apple.developer.homekit";
}
internal class DataProtectionEntitlements
{
internal static readonly string Key = "com.apple.developer.default-data-protection";
internal static readonly string Value = "NSFileProtectionComplete";
}
internal class HealthKitEntitlements
{
// value is bool true.
internal static readonly string Key = "com.apple.developer.healthkit";
}
internal class WirelessAccessoryConfigurationEntitlements
{
// value is bool true.
internal static readonly string Key = "com.apple.external-accessory.wireless-configuration";
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2bfeebf480ee141979c8981b78c0a488
timeCreated: 1496741691
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -21,7 +21,7 @@ PluginImporter:
Exclude Linux64: 0
Exclude OSXUniversal: 0
Exclude Win: 0
Exclude Win64: 1
Exclude Win64: 0
Exclude WindowsStoreApps: 0
Exclude iOS: 0
- first:
@ -48,7 +48,7 @@ PluginImporter:
second:
enabled: 1
settings:
CPU: x86_64
CPU: AnyCPU
- first:
Standalone: OSXUniversal
second:
@ -64,9 +64,9 @@ PluginImporter:
- first:
Standalone: Win64
second:
enabled: 0
enabled: 1
settings:
CPU: None
CPU: x86_64
- first:
Windows Store Apps: WindowsStoreApps
second: