app名称本地化自适应;分享图片接入
This commit is contained in:
parent
cbd1ce3313
commit
625e2b7934
@ -10,7 +10,7 @@ using UnityEngine;
|
||||
namespace Assets
|
||||
{
|
||||
[CustomEditor(typeof(RadarChart), true)]
|
||||
class RadarChartInspector : Editor
|
||||
class RadarChartInspector : UnityEditor.Editor
|
||||
{
|
||||
bool mCategories = false;
|
||||
bool mGroups = false;
|
||||
|
||||
8
Assets/Editor/Locale.meta
Normal file
8
Assets/Editor/Locale.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5024d7258021714e832782d73a13749
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Editor/Locale/en.lproj.meta
Normal file
8
Assets/Editor/Locale/en.lproj.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85d4f615e53b346478ff4dff569a317f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/Editor/Locale/en.lproj/InfoPlist.strings
Normal file
9
Assets/Editor/Locale/en.lproj/InfoPlist.strings
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
InfoPlist.strings
|
||||
IosToRN
|
||||
|
||||
Created by lishuo on 2021/2/24.
|
||||
|
||||
*/
|
||||
"CFBundleName"="PowerFun";
|
||||
"CFBundleDisplayName"="PowerFun";
|
||||
7
Assets/Editor/Locale/en.lproj/InfoPlist.strings.meta
Normal file
7
Assets/Editor/Locale/en.lproj/InfoPlist.strings.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4aa11356c3e11a41941fc03fae3a26b
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Editor/Locale/zh-Hans.lproj.meta
Normal file
8
Assets/Editor/Locale/zh-Hans.lproj.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cde742ac5daeb784ca7837d4e2622f26
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/Editor/Locale/zh-Hans.lproj/InfoPlist.strings
Normal file
9
Assets/Editor/Locale/zh-Hans.lproj/InfoPlist.strings
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
InfoPlist.strings
|
||||
IosToRN
|
||||
|
||||
Created by lishuo on 2021/2/24.
|
||||
|
||||
*/
|
||||
"CFBundleName"="运动地球";
|
||||
"CFBundleDisplayName"="运动地球";
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d407327a982cae2468c2e1c372b5dbd3
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
77
Assets/Editor/NativeLocale.cs
Normal file
77
Assets/Editor/NativeLocale.cs
Normal 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/Localizable.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/Editor/NativeLocale.cs.meta
Normal file
11
Assets/Editor/NativeLocale.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04d272fbec5a52a4a8a1bb6b201a3030
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -5,6 +5,7 @@ using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEditor.iOS.Xcode;
|
||||
|
||||
#endif
|
||||
|
||||
public static class XCodePostProcessBuild
|
||||
@ -25,6 +26,7 @@ public static class XCodePostProcessBuild
|
||||
SetFrameworksAndBuildSettings(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 SetFrameworksAndBuildSettings(string path)
|
||||
|
||||
8
Assets/Editor/Xcode.meta
Normal file
8
Assets/Editor/Xcode.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9b05453b42e5aa40b0b080cdec35b95
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
813
Assets/Editor/Xcode/AssetCatalog.cs
Normal file
813
Assets/Editor/Xcode/AssetCatalog.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/AssetCatalog.cs.meta
Normal file
12
Assets/Editor/Xcode/AssetCatalog.cs.meta
Normal 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:
|
||||
257
Assets/Editor/Xcode/JsonParser.cs
Normal file
257
Assets/Editor/Xcode/JsonParser.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/JsonParser.cs.meta
Normal file
12
Assets/Editor/Xcode/JsonParser.cs.meta
Normal 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:
|
||||
9
Assets/Editor/Xcode/PBX.meta
Normal file
9
Assets/Editor/Xcode/PBX.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7625017be5ff149a6b0f20ee645ce11e
|
||||
folderAsset: yes
|
||||
timeCreated: 1496741689
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
106
Assets/Editor/Xcode/PBX/Elements.cs
Normal file
106
Assets/Editor/Xcode/PBX/Elements.cs
Normal 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
|
||||
|
||||
12
Assets/Editor/Xcode/PBX/Elements.cs.meta
Normal file
12
Assets/Editor/Xcode/PBX/Elements.cs.meta
Normal 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:
|
||||
243
Assets/Editor/Xcode/PBX/Lexer.cs
Normal file
243
Assets/Editor/Xcode/PBX/Lexer.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/PBX/Lexer.cs.meta
Normal file
12
Assets/Editor/Xcode/PBX/Lexer.cs.meta
Normal 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:
|
||||
1007
Assets/Editor/Xcode/PBX/Objects.cs
Normal file
1007
Assets/Editor/Xcode/PBX/Objects.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/Editor/Xcode/PBX/Objects.cs.meta
Normal file
12
Assets/Editor/Xcode/PBX/Objects.cs.meta
Normal 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:
|
||||
171
Assets/Editor/Xcode/PBX/Parser.cs
Normal file
171
Assets/Editor/Xcode/PBX/Parser.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/PBX/Parser.cs.meta
Normal file
12
Assets/Editor/Xcode/PBX/Parser.cs.meta
Normal 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:
|
||||
122
Assets/Editor/Xcode/PBX/Sections.cs
Normal file
122
Assets/Editor/Xcode/PBX/Sections.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/PBX/Sections.cs.meta
Normal file
12
Assets/Editor/Xcode/PBX/Sections.cs.meta
Normal 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:
|
||||
259
Assets/Editor/Xcode/PBX/Serializer.cs
Normal file
259
Assets/Editor/Xcode/PBX/Serializer.cs
Normal 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
|
||||
|
||||
12
Assets/Editor/Xcode/PBX/Serializer.cs.meta
Normal file
12
Assets/Editor/Xcode/PBX/Serializer.cs.meta
Normal 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:
|
||||
300
Assets/Editor/Xcode/PBX/Utils.cs
Normal file
300
Assets/Editor/Xcode/PBX/Utils.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/PBX/Utils.cs.meta
Normal file
12
Assets/Editor/Xcode/PBX/Utils.cs.meta
Normal 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:
|
||||
125
Assets/Editor/Xcode/PBXCapabilityType.cs
Normal file
125
Assets/Editor/Xcode/PBXCapabilityType.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Editor/Xcode/PBXCapabilityType.cs.meta
Normal file
12
Assets/Editor/Xcode/PBXCapabilityType.cs.meta
Normal 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:
|
||||
106
Assets/Editor/Xcode/PBXPath.cs
Normal file
106
Assets/Editor/Xcode/PBXPath.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/PBXPath.cs.meta
Normal file
12
Assets/Editor/Xcode/PBXPath.cs.meta
Normal 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:
|
||||
1538
Assets/Editor/Xcode/PBXProject.cs
Normal file
1538
Assets/Editor/Xcode/PBXProject.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/Editor/Xcode/PBXProject.cs.meta
Normal file
12
Assets/Editor/Xcode/PBXProject.cs.meta
Normal 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:
|
||||
713
Assets/Editor/Xcode/PBXProjectData.cs
Normal file
713
Assets/Editor/Xcode/PBXProjectData.cs
Normal 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
|
||||
|
||||
12
Assets/Editor/Xcode/PBXProjectData.cs.meta
Normal file
12
Assets/Editor/Xcode/PBXProjectData.cs.meta
Normal 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:
|
||||
296
Assets/Editor/Xcode/PBXProjectExtensions.cs
Normal file
296
Assets/Editor/Xcode/PBXProjectExtensions.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/PBXProjectExtensions.cs.meta
Normal file
12
Assets/Editor/Xcode/PBXProjectExtensions.cs.meta
Normal 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:
|
||||
351
Assets/Editor/Xcode/PlistParser.cs
Normal file
351
Assets/Editor/Xcode/PlistParser.cs
Normal 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
|
||||
12
Assets/Editor/Xcode/PlistParser.cs.meta
Normal file
12
Assets/Editor/Xcode/PlistParser.cs.meta
Normal 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:
|
||||
578
Assets/Editor/Xcode/ProjectCapabilityManager.cs
Normal file
578
Assets/Editor/Xcode/ProjectCapabilityManager.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
12
Assets/Editor/Xcode/ProjectCapabilityManager.cs.meta
Normal file
12
Assets/Editor/Xcode/ProjectCapabilityManager.cs.meta
Normal 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:
|
||||
@ -20,10 +20,9 @@
|
||||
<data android:scheme="powerfunx" android:host="app" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:launchMode="singleInstance"
|
||||
android:name="com.unityplugins.imageselector.SelectActivity">
|
||||
</activity>
|
||||
<activity android:name="com.unityplugins.mapbox.GlobeActivity"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||
android:screenOrientation="landscape"></activity>
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
Binary file not shown.
BIN
Assets/Plugins/Android/PowerFunAndroidPlugin-release.aar
Normal file
BIN
Assets/Plugins/Android/PowerFunAndroidPlugin-release.aar
Normal file
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9722f205596f2fd499991ea299119cc8
|
||||
guid: c2b78b518d3971e4a89095c3e33ddc00
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@ -20,6 +20,19 @@ allprojects {
|
||||
repositories {**ARTIFACTORYREPOSITORY**
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url 'https://api.mapbox.com/downloads/v2/releases/maven'
|
||||
authentication {
|
||||
basic(BasicAuthentication)
|
||||
}
|
||||
credentials {
|
||||
// Do not change the username below.
|
||||
// This should always be `mapbox` (not your username).
|
||||
username = 'mapbox'
|
||||
// Use the secret token you stored in gradle.properties as the password
|
||||
password = "sk.eyJ1IjoidGFsZXNmYW4iLCJhIjoiY2t3ZDg4bmV4NDFubjJucm9nMDZsNnFyZyJ9._ukjbjSdzXFsF-4rZ1sPeA"
|
||||
}
|
||||
}
|
||||
flatDir {
|
||||
dirs "${project(':unityLibrary').projectDir}/libs"
|
||||
}
|
||||
|
||||
@ -4,20 +4,13 @@ apply plugin: 'com.android.library'
|
||||
**APPLY_PLUGINS**
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation(name: 'animated-vector-drawable-25.1.0', ext:'aar')
|
||||
implementation(name: 'appcompat-v7-25.1.0', ext:'aar')
|
||||
implementation(name: 'com.mapbox.android.unity-debug', ext:'aar')
|
||||
implementation(name: 'libcore-release', ext:'aar')
|
||||
implementation(name: 'libtelemetry-full-release', ext:'aar')
|
||||
implementation(name: 'support-compat-25.1.0', ext:'aar')
|
||||
implementation(name: 'support-core-ui-25.1.0', ext:'aar')
|
||||
implementation(name: 'support-core-utils-25.1.0', ext:'aar')
|
||||
implementation(name: 'support-media-compat-25.1.0', ext:'aar')
|
||||
implementation(name: 'support-v4-25.1.0', ext:'aar')
|
||||
implementation(name: 'support-vector-drawable-25.1.0', ext:'aar')
|
||||
implementation(name: 'UnityCallWechatShare-release', ext:'aar')
|
||||
implementation files ('libs/ImageSelector-release.aar')
|
||||
implementation files ("libs/unity-classes.jar")
|
||||
implementation files ("libs/unityandroidbluetoothlelib.jar")
|
||||
implementation files ('libs/PowerFunAndroidPlugin-release.aar')
|
||||
implementation ('com.mapbox.maps:android:10.2.0-beta.1'){
|
||||
exclude group: 'group_name', module: 'module_name'
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
@ -49,3 +42,12 @@ android {
|
||||
}**PACKAGING_OPTIONS**
|
||||
}**REPOSITORIES****SOURCE_BUILD_SETUP**
|
||||
**EXTERNAL_SOURCES**
|
||||
|
||||
task localizeAppName(type: Copy) {
|
||||
from("${project.rootDir}/unityLibrary/unity-android-resources/res/") {
|
||||
include "**/strings.xml"
|
||||
}
|
||||
into "${project.rootDir}/launcher/src/main/res"
|
||||
}
|
||||
|
||||
preBuild.dependsOn(localizeAppName)
|
||||
8
Assets/Plugins/Android/res.meta
Normal file
8
Assets/Plugins/Android/res.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 503cb92f2ff04994295fb1ef0fb6ff87
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/Android/res/values-zh.meta
Normal file
8
Assets/Plugins/Android/res/values-zh.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1a4f8ddbb19f5149a54b3952a1b1e1d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
5
Assets/Plugins/Android/res/values-zh/strings.xml
Normal file
5
Assets/Plugins/Android/res/values-zh/strings.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">运动地球</string>
|
||||
<string name="game_view_content_description">Game view</string>
|
||||
</resources>
|
||||
7
Assets/Plugins/Android/res/values-zh/strings.xml.meta
Normal file
7
Assets/Plugins/Android/res/values-zh/strings.xml.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae90b72fae98b7d409fef80a77ef58dd
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Plugins/Android/res/values.meta
Normal file
8
Assets/Plugins/Android/res/values.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ac3db33836fa754384a431594aaea4a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
5
Assets/Plugins/Android/res/values/strings.xml
Normal file
5
Assets/Plugins/Android/res/values/strings.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">PowerFun</string>
|
||||
<string name="game_view_content_description">Game view</string>
|
||||
</resources>
|
||||
7
Assets/Plugins/Android/res/values/strings.xml.meta
Normal file
7
Assets/Plugins/Android/res/values/strings.xml.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 269366f50793f6c479888c9eecf16ea1
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -30,12 +30,12 @@ void LaunchMiniProgram(const char* miniProgramID, const char* path){
|
||||
}
|
||||
|
||||
// 分享图片至微信
|
||||
void ShareImgToWX(int scene, UInt8 *msgByteArrayData, int arrayLength, const char* title, const char* description){
|
||||
void ShareImgToWX(int scene, UInt8 *msgByteArrayData, int arrayLength){
|
||||
WXImageObject *imageObject = [WXImageObject object];
|
||||
imageObject.imageData = [[NSData alloc] initWithBytes:msgByteArrayData length:arrayLength];
|
||||
WXMediaMessage *message = [WXMediaMessage message];
|
||||
message.title = [NSString stringWithUTF8String:title];
|
||||
message.description = [NSString stringWithUTF8String:description];
|
||||
// message.title = [NSString stringWithUTF8String:title];
|
||||
// message.description = [NSString stringWithUTF8String:description];
|
||||
// 用APP的Icon做缩略图
|
||||
NSDictionary *infoPlist = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString *icon = [[infoPlist valueForKeyPath:@"CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles"] lastObject];
|
||||
@ -67,7 +67,6 @@ void ShareUrlToWX(int scene, const char* url, const char* title, const char* des
|
||||
[WXApi sendReq:req completion:nil];
|
||||
}
|
||||
|
||||
|
||||
//判断是否安装微信
|
||||
bool IsWechatInstalled_iOS()
|
||||
{
|
||||
|
||||
@ -293,84 +293,6 @@ MonoBehaviour:
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text:
|
||||
--- !u!1 &437213691946889435
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2982792994629127324}
|
||||
- component: {fileID: 6892152824926409725}
|
||||
- component: {fileID: 7099515369382083389}
|
||||
- component: {fileID: 7234636846379503339}
|
||||
m_Layer: 5
|
||||
m_Name: Lines
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2982792994629127324
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 437213691946889435}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1662293206147616003}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &6892152824926409725
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 437213691946889435}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!222 &7099515369382083389
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 437213691946889435}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &7234636846379503339
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 437213691946889435}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2032ee9ddbfbfb74da66a209b05d468d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
Thickness: 4
|
||||
--- !u!1 &561507438981283975
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -8834,7 +8756,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &765892905500667676
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -25458,7 +25380,7 @@ RectTransform:
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 6705637700361051074}
|
||||
- {fileID: 287130843534808004}
|
||||
- {fileID: 5572785103298013575}
|
||||
m_Father: {fileID: 765892906030958096}
|
||||
m_RootOrder: 3
|
||||
@ -28401,6 +28323,84 @@ MonoBehaviour:
|
||||
m_ChildControlHeight: 0
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
--- !u!1 &4561237107004918660
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6368931264969813662}
|
||||
- component: {fileID: 1645432988067376672}
|
||||
- component: {fileID: 8985708638791463984}
|
||||
- component: {fileID: 3907800697780269774}
|
||||
m_Layer: 5
|
||||
m_Name: Lines
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6368931264969813662
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4561237107004918660}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4972958166963389385}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1645432988067376672
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4561237107004918660}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!222 &8985708638791463984
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4561237107004918660}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &3907800697780269774
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4561237107004918660}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2032ee9ddbfbfb74da66a209b05d468d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
Thickness: 2
|
||||
--- !u!1 &4805486912667576755
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -28853,109 +28853,6 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
radius: 30
|
||||
--- !u!1 &5219485531322787435
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1662293206147616003}
|
||||
- component: {fileID: 1339745422673456410}
|
||||
- component: {fileID: 996780360449557400}
|
||||
- component: {fileID: 5245709930834093469}
|
||||
- component: {fileID: 4133168441936445500}
|
||||
m_Layer: 5
|
||||
m_Name: RectMask(Clone)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1662293206147616003
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5219485531322787435}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 2982792994629127324}
|
||||
- {fileID: 1715951100218252328}
|
||||
m_Father: {fileID: 6705637700361051074}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 50}
|
||||
m_SizeDelta: {x: 500, y: 50}
|
||||
m_Pivot: {x: 0, y: 1}
|
||||
--- !u!114 &1339745422673456410
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5219485531322787435}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_ShowMaskGraphic: 0
|
||||
--- !u!222 &996780360449557400
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5219485531322787435}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &5245709930834093469
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5219485531322787435}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 10917, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &4133168441936445500
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5219485531322787435}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &5227404737329208808
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -29914,7 +29811,7 @@ MonoBehaviour:
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: 0
|
||||
--- !u!1 &6731657110001096442
|
||||
--- !u!1 &6761719125807787846
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@ -29922,10 +29819,73 @@ GameObject:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1715951100218252328}
|
||||
- component: {fileID: 2606228147842712072}
|
||||
- component: {fileID: 4489143448244481427}
|
||||
- component: {fileID: 9169757412572267114}
|
||||
- component: {fileID: 2921649382181662335}
|
||||
- component: {fileID: 4686157667057369641}
|
||||
- component: {fileID: 1501255716731572845}
|
||||
m_Layer: 5
|
||||
m_Name: textController
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2921649382181662335
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6761719125807787846}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 287130843534808004}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -250, y: -25}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &4686157667057369641
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6761719125807787846}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!114 &1501255716731572845
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6761719125807787846}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f8c8bf670921e114bbea10f451c86392, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Camera: {fileID: 0}
|
||||
PlaneDistance: 20
|
||||
--- !u!1 &6867828174471998518
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6669230693244627319}
|
||||
- component: {fileID: 6880249551563795014}
|
||||
- component: {fileID: 2688744985322922845}
|
||||
- component: {fileID: 1028417144058143333}
|
||||
m_Layer: 5
|
||||
m_Name: Lines
|
||||
m_TagString: Untagged
|
||||
@ -29933,52 +29893,52 @@ GameObject:
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1715951100218252328
|
||||
--- !u!224 &6669230693244627319
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6731657110001096442}
|
||||
m_GameObject: {fileID: 6867828174471998518}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1662293206147616003}
|
||||
m_RootOrder: 1
|
||||
m_Father: {fileID: 4972958166963389385}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &2606228147842712072
|
||||
--- !u!114 &6880249551563795014
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6731657110001096442}
|
||||
m_GameObject: {fileID: 6867828174471998518}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!222 &4489143448244481427
|
||||
--- !u!222 &2688744985322922845
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6731657110001096442}
|
||||
m_GameObject: {fileID: 6867828174471998518}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &9169757412572267114
|
||||
--- !u!114 &1028417144058143333
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6731657110001096442}
|
||||
m_GameObject: {fileID: 6867828174471998518}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2032ee9ddbfbfb74da66a209b05d468d, type: 3}
|
||||
@ -29991,57 +29951,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
Thickness: 2
|
||||
--- !u!1 &6790952765333462635
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6705637700361051074}
|
||||
- component: {fileID: 5625754829217035930}
|
||||
m_Layer: 0
|
||||
m_Name: New Game Object
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6705637700361051074
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6790952765333462635}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0.96, y: 0.96, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 1662293206147616003}
|
||||
- {fileID: 4455722854794813142}
|
||||
m_Father: {fileID: 2877296740073624297}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 500, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &5625754829217035930
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6790952765333462635}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Thickness: 4
|
||||
--- !u!1 &6885174058740936923
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -30135,69 +30045,6 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
m_HorizontalFit: 2
|
||||
m_VerticalFit: 0
|
||||
--- !u!1 &6905895147960617872
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4455722854794813142}
|
||||
- component: {fileID: 6000514500222502934}
|
||||
- component: {fileID: 5031116583493021538}
|
||||
m_Layer: 5
|
||||
m_Name: textController
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4455722854794813142
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6905895147960617872}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6705637700361051074}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: -250, y: -25}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &6000514500222502934
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6905895147960617872}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!114 &5031116583493021538
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6905895147960617872}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f8c8bf670921e114bbea10f451c86392, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Camera: {fileID: 0}
|
||||
PlaneDistance: 20
|
||||
--- !u!1 &7128831287110258232
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -32198,6 +32045,109 @@ MonoBehaviour:
|
||||
m_ChildControlHeight: 0
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
--- !u!1 &7743152534115465197
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4972958166963389385}
|
||||
- component: {fileID: 2462729567061685641}
|
||||
- component: {fileID: 6932560375282435178}
|
||||
- component: {fileID: 1204421178344973644}
|
||||
- component: {fileID: 7276889366372022131}
|
||||
m_Layer: 5
|
||||
m_Name: RectMask(Clone)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4972958166963389385
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7743152534115465197}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 6669230693244627319}
|
||||
- {fileID: 6368931264969813662}
|
||||
m_Father: {fileID: 287130843534808004}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 50}
|
||||
m_SizeDelta: {x: 500, y: 50}
|
||||
m_Pivot: {x: 0, y: 1}
|
||||
--- !u!114 &2462729567061685641
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7743152534115465197}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_ShowMaskGraphic: 0
|
||||
--- !u!222 &6932560375282435178
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7743152534115465197}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &1204421178344973644
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7743152534115465197}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 10917, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &7276889366372022131
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7743152534115465197}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &8084747437690518146
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -33473,6 +33423,56 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
key:
|
||||
--- !u!1 &8870311823578833747
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 287130843534808004}
|
||||
- component: {fileID: 3040018482647644442}
|
||||
m_Layer: 0
|
||||
m_Name: New Game Object
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &287130843534808004
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8870311823578833747}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0.96, y: 0.96, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 4972958166963389385}
|
||||
- {fileID: 2921649382181662335}
|
||||
m_Father: {fileID: 2877296740073624297}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 500, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &3040018482647644442
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8870311823578833747}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 55b5a59897c650342a9b23ff348a9992, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &9118781381026562979
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@ -18,7 +18,7 @@ public delegate void ChangeLanguageDelegate();
|
||||
|
||||
public static class App
|
||||
{
|
||||
public static string Host = "http://192.168.0.101:5084/";
|
||||
public static string Host = "http://192.168.0.101:5087/";
|
||||
|
||||
public static string AppVersion = Application.version;
|
||||
|
||||
|
||||
@ -27,6 +27,9 @@ public class WeChatController
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern bool checkLocation();
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void ShareImgToWX(int scene, byte[] msgByteArrayData, int arrayLength);
|
||||
#endif
|
||||
#endregion
|
||||
/// <summary>
|
||||
@ -128,6 +131,14 @@ public class WeChatController
|
||||
{
|
||||
mainActivityClass.CallStatic("ShareImageToWX", scene, ImageToBytes(image));
|
||||
}
|
||||
public void ShareImageToWX(int scene, byte[] image)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
mainActivityClass.CallStatic("ShareImageToWX", scene, image);
|
||||
#elif UNITY_IOS
|
||||
ShareImgToWX(scene,image,image.Length);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分享音乐至微信
|
||||
|
||||
@ -880,8 +880,11 @@ public class CyclingController : DeviceServiceMonoBase
|
||||
}
|
||||
cyclingController.recorderData.SaveWithLocalRecordAysnc(cyclingModel, selectParamModel, imageFileName, recordId, path);
|
||||
}
|
||||
|
||||
protected void CaptureCamera(Camera camera, Rect rect,string fileName)
|
||||
public byte[] CaptureCamera()
|
||||
{
|
||||
return CaptureCameraReturnByte(Camera.main, new Rect(Screen.width * 0f, Screen.height * 0f, Screen.width * 0.5f, Screen.height * 0.5f));
|
||||
}
|
||||
private byte[] CaptureCameraReturnByte(Camera camera, Rect rect)
|
||||
{
|
||||
// 创建一个RenderTexture对象
|
||||
RenderTexture rt = new RenderTexture((int)rect.width, (int)rect.height, 0);
|
||||
@ -905,8 +908,11 @@ public class CyclingController : DeviceServiceMonoBase
|
||||
RenderTexture.active = null; // JC: added to avoid errors
|
||||
GameObject.Destroy(rt);
|
||||
// 最后将这些纹理数据,成一个图片文件
|
||||
byte[] bytes = screenShot.EncodeToJPG();
|
||||
|
||||
return screenShot.EncodeToJPG();
|
||||
}
|
||||
protected void CaptureCamera(Camera camera, Rect rect,string fileName)
|
||||
{
|
||||
byte[] bytes = CaptureCameraReturnByte(camera, rect);
|
||||
//var path = Helper.GetDataDir("MapWorkoutRecords/images");
|
||||
//string filename = path + "/" + Guid.NewGuid().ToString() + ".png";
|
||||
System.IO.File.WriteAllBytes(fileName, bytes);
|
||||
|
||||
@ -39,6 +39,7 @@ namespace Assets.Scenes.Ride.Scripts
|
||||
#region 功能按钮
|
||||
GameObject goResultBtn;
|
||||
GameObject cancelBtn;
|
||||
GameObject shareWxBtn;
|
||||
#endregion
|
||||
|
||||
CyclingController cyclingController;
|
||||
@ -67,6 +68,8 @@ namespace Assets.Scenes.Ride.Scripts
|
||||
#endregion
|
||||
goResultBtn = transform.Find("ConFirmButton").gameObject;
|
||||
cancelBtn = transform.Find("CloseButton").gameObject;
|
||||
shareWxBtn = transform.Find("ToolBarPanel/WeChatButton").gameObject;
|
||||
|
||||
}
|
||||
|
||||
public void InjectController(CyclingController controller)
|
||||
@ -74,7 +77,15 @@ namespace Assets.Scenes.Ride.Scripts
|
||||
cyclingController = controller;
|
||||
cyclingController.AddEvent(goResultBtn, UnityEngine.EventSystems.EventTriggerType.PointerClick, GoResult);
|
||||
cyclingController.AddEvent(cancelBtn, UnityEngine.EventSystems.EventTriggerType.PointerClick, Cancel);
|
||||
cyclingController.AddEvent(shareWxBtn, EventTriggerType.PointerClick, shareToWx);
|
||||
}
|
||||
|
||||
private void shareToWx(BaseEventData b)
|
||||
{
|
||||
var bs = cyclingController.CaptureCamera();
|
||||
WeChatController.Instance.ShareImageToWX(0, bs);
|
||||
}
|
||||
|
||||
private void GoResult(BaseEventData baseEventData)
|
||||
{
|
||||
if (App.MainSceneParam.ContainsKey("Name"))
|
||||
|
||||
@ -38,6 +38,7 @@ GraphicsSettings:
|
||||
- {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0}
|
||||
- {fileID: 16003, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_PreloadedShaders: []
|
||||
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
|
||||
type: 0}
|
||||
|
||||
@ -256,7 +256,7 @@ PlayerSettings:
|
||||
clonedFromGUID: c0afd0d1d80e3634a9dac47e8a0426ea
|
||||
templatePackageId: com.unity.template.3d@4.2.8
|
||||
templateDefaultScene: Assets/Scenes/SampleScene.unity
|
||||
AndroidTargetArchitectures: 3
|
||||
AndroidTargetArchitectures: 1
|
||||
AndroidSplashScreenScale: 0
|
||||
androidSplashScreen: {fileID: 0}
|
||||
AndroidKeystoreName: '{inproject}: Assets/Plugins/Android/powerfun.keystore'
|
||||
@ -868,7 +868,7 @@ PlayerSettings:
|
||||
platformArchitecture:
|
||||
iPhone: 1
|
||||
scriptingBackend:
|
||||
Android: 1
|
||||
Android: 0
|
||||
Standalone: 0
|
||||
il2cppCompilerConfiguration:
|
||||
Standalone: 0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user