Commit 354e08ac authored by 王雪伟's avatar 王雪伟

加入bugly

parent cda73a19
fileFormatVersion: 2
guid: 177a1d071db62497eaefdad1e71ed57b
folderAsset: yes
DefaultImporter:
userData:
Copyright (c) 2012 Wei Wang @onevcat
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
fileFormatVersion: 2
guid: 7d871c3f471cb423c8254b5a352ad529
DefaultImporter:
userData:
fileFormatVersion: 2
guid: 9a2ac820893b640c083f6f6c6116b420
folderAsset: yes
DefaultImporter:
userData:
namespace XUPorterJSON {
using System;
using System.Collections;
using System.Text;
using System.Collections.Generic;
/* Based on the JSON parser from
* http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
*
* I simplified it so that it doesn't throw exceptions
* and can be used in Unity iPhone with maximum code stripping.
*/
/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
/// All numbers are parsed to doubles.
/// </summary>
public class MiniJSON
{
private const int TOKEN_NONE = 0;
private const int TOKEN_CURLY_OPEN = 1;
private const int TOKEN_CURLY_CLOSE = 2;
private const int TOKEN_SQUARED_OPEN = 3;
private const int TOKEN_SQUARED_CLOSE = 4;
private const int TOKEN_COLON = 5;
private const int TOKEN_COMMA = 6;
private const int TOKEN_STRING = 7;
private const int TOKEN_NUMBER = 8;
private const int TOKEN_TRUE = 9;
private const int TOKEN_FALSE = 10;
private const int TOKEN_NULL = 11;
private const int BUILDER_CAPACITY = 2000;
/// <summary>
/// On decoding, this value holds the position at which the parse failed (-1 = no error).
/// </summary>
protected static int lastErrorIndex = -1;
protected static string lastDecode = "";
/// <summary>
/// Parses the string json into a value
/// </summary>
/// <param name="json">A JSON string.</param>
/// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
public static object jsonDecode( string json )
{
// save the string for debug information
MiniJSON.lastDecode = json;
if( json != null )
{
char[] charArray = json.ToCharArray();
int index = 0;
bool success = true;
object value = MiniJSON.parseValue( charArray, ref index, ref success );
if( success )
MiniJSON.lastErrorIndex = -1;
else
MiniJSON.lastErrorIndex = index;
return value;
}
else
{
return null;
}
}
/// <summary>
/// Converts a Hashtable / ArrayList / Dictionary(string,string) object into a JSON string
/// </summary>
/// <param name="json">A Hashtable / ArrayList</param>
/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
public static string jsonEncode( object json )
{
var builder = new StringBuilder( BUILDER_CAPACITY );
var success = MiniJSON.serializeValue( json, builder );
return ( success ? builder.ToString() : null );
}
/// <summary>
/// On decoding, this function returns the position at which the parse failed (-1 = no error).
/// </summary>
/// <returns></returns>
public static bool lastDecodeSuccessful()
{
return ( MiniJSON.lastErrorIndex == -1 );
}
/// <summary>
/// On decoding, this function returns the position at which the parse failed (-1 = no error).
/// </summary>
/// <returns></returns>
public static int getLastErrorIndex()
{
return MiniJSON.lastErrorIndex;
}
/// <summary>
/// If a decoding error occurred, this function returns a piece of the JSON string
/// at which the error took place. To ease debugging.
/// </summary>
/// <returns></returns>
public static string getLastErrorSnippet()
{
if( MiniJSON.lastErrorIndex == -1 )
{
return "";
}
else
{
int startIndex = MiniJSON.lastErrorIndex - 5;
int endIndex = MiniJSON.lastErrorIndex + 15;
if( startIndex < 0 )
startIndex = 0;
if( endIndex >= MiniJSON.lastDecode.Length )
endIndex = MiniJSON.lastDecode.Length - 1;
return MiniJSON.lastDecode.Substring( startIndex, endIndex - startIndex + 1 );
}
}
#region Parsing
protected static Hashtable parseObject( char[] json, ref int index )
{
Hashtable table = new Hashtable();
int token;
// {
nextToken( json, ref index );
bool done = false;
while( !done )
{
token = lookAhead( json, index );
if( token == MiniJSON.TOKEN_NONE )
{
return null;
}
else if( token == MiniJSON.TOKEN_COMMA )
{
nextToken( json, ref index );
}
else if( token == MiniJSON.TOKEN_CURLY_CLOSE )
{
nextToken( json, ref index );
return table;
}
else
{
// name
string name = parseString( json, ref index );
if( name == null )
{
return null;
}
// :
token = nextToken( json, ref index );
if( token != MiniJSON.TOKEN_COLON )
return null;
// value
bool success = true;
object value = parseValue( json, ref index, ref success );
if( !success )
return null;
table[name] = value;
}
}
return table;
}
protected static ArrayList parseArray( char[] json, ref int index )
{
ArrayList array = new ArrayList();
// [
nextToken( json, ref index );
bool done = false;
while( !done )
{
int token = lookAhead( json, index );
if( token == MiniJSON.TOKEN_NONE )
{
return null;
}
else if( token == MiniJSON.TOKEN_COMMA )
{
nextToken( json, ref index );
}
else if( token == MiniJSON.TOKEN_SQUARED_CLOSE )
{
nextToken( json, ref index );
break;
}
else
{
bool success = true;
object value = parseValue( json, ref index, ref success );
if( !success )
return null;
array.Add( value );
}
}
return array;
}
protected static object parseValue( char[] json, ref int index, ref bool success )
{
switch( lookAhead( json, index ) )
{
case MiniJSON.TOKEN_STRING:
return parseString( json, ref index );
case MiniJSON.TOKEN_NUMBER:
return parseNumber( json, ref index );
case MiniJSON.TOKEN_CURLY_OPEN:
return parseObject( json, ref index );
case MiniJSON.TOKEN_SQUARED_OPEN:
return parseArray( json, ref index );
case MiniJSON.TOKEN_TRUE:
nextToken( json, ref index );
return Boolean.Parse( "TRUE" );
case MiniJSON.TOKEN_FALSE:
nextToken( json, ref index );
return Boolean.Parse( "FALSE" );
case MiniJSON.TOKEN_NULL:
nextToken( json, ref index );
return null;
case MiniJSON.TOKEN_NONE:
break;
}
success = false;
return null;
}
protected static string parseString( char[] json, ref int index )
{
string s = "";
char c;
eatWhitespace( json, ref index );
// "
c = json[index++];
bool complete = false;
while( !complete )
{
if( index == json.Length )
break;
c = json[index++];
if( c == '"' )
{
complete = true;
break;
}
else if( c == '\\' )
{
if( index == json.Length )
break;
c = json[index++];
if( c == '"' )
{
s += '"';
}
else if( c == '\\' )
{
s += '\\';
}
else if( c == '/' )
{
s += '/';
}
else if( c == 'b' )
{
s += '\b';
}
else if( c == 'f' )
{
s += '\f';
}
else if( c == 'n' )
{
s += '\n';
}
else if( c == 'r' )
{
s += '\r';
}
else if( c == 't' )
{
s += '\t';
}
else if( c == 'u' )
{
int remainingLength = json.Length - index;
if( remainingLength >= 4 )
{
char[] unicodeCharArray = new char[4];
Array.Copy( json, index, unicodeCharArray, 0, 4 );
// Drop in the HTML markup for the unicode character
s += "&#x" + new string( unicodeCharArray ) + ";";
/*
uint codePoint = UInt32.Parse(new string(unicodeCharArray), NumberStyles.HexNumber);
// convert the integer codepoint to a unicode char and add to string
s += Char.ConvertFromUtf32((int)codePoint);
*/
// skip 4 chars
index += 4;
}
else
{
break;
}
}
}
else
{
s += c;
}
}
if( !complete )
return null;
return s;
}
protected static double parseNumber( char[] json, ref int index )
{
eatWhitespace( json, ref index );
int lastIndex = getLastIndexOfNumber( json, index );
int charLength = ( lastIndex - index ) + 1;
char[] numberCharArray = new char[charLength];
Array.Copy( json, index, numberCharArray, 0, charLength );
index = lastIndex + 1;
return Double.Parse( new string( numberCharArray ) ); // , CultureInfo.InvariantCulture);
}
protected static int getLastIndexOfNumber( char[] json, int index )
{
int lastIndex;
for( lastIndex = index; lastIndex < json.Length; lastIndex++ )
if( "0123456789+-.eE".IndexOf( json[lastIndex] ) == -1 )
{
break;
}
return lastIndex - 1;
}
protected static void eatWhitespace( char[] json, ref int index )
{
for( ; index < json.Length; index++ )
if( " \t\n\r".IndexOf( json[index] ) == -1 )
{
break;
}
}
protected static int lookAhead( char[] json, int index )
{
int saveIndex = index;
return nextToken( json, ref saveIndex );
}
protected static int nextToken( char[] json, ref int index )
{
eatWhitespace( json, ref index );
if( index == json.Length )
{
return MiniJSON.TOKEN_NONE;
}
char c = json[index];
index++;
switch( c )
{
case '{':
return MiniJSON.TOKEN_CURLY_OPEN;
case '}':
return MiniJSON.TOKEN_CURLY_CLOSE;
case '[':
return MiniJSON.TOKEN_SQUARED_OPEN;
case ']':
return MiniJSON.TOKEN_SQUARED_CLOSE;
case ',':
return MiniJSON.TOKEN_COMMA;
case '"':
return MiniJSON.TOKEN_STRING;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return MiniJSON.TOKEN_NUMBER;
case ':':
return MiniJSON.TOKEN_COLON;
}
index--;
int remainingLength = json.Length - index;
// false
if( remainingLength >= 5 )
{
if( json[index] == 'f' &&
json[index + 1] == 'a' &&
json[index + 2] == 'l' &&
json[index + 3] == 's' &&
json[index + 4] == 'e' )
{
index += 5;
return MiniJSON.TOKEN_FALSE;
}
}
// true
if( remainingLength >= 4 )
{
if( json[index] == 't' &&
json[index + 1] == 'r' &&
json[index + 2] == 'u' &&
json[index + 3] == 'e' )
{
index += 4;
return MiniJSON.TOKEN_TRUE;
}
}
// null
if( remainingLength >= 4 )
{
if( json[index] == 'n' &&
json[index + 1] == 'u' &&
json[index + 2] == 'l' &&
json[index + 3] == 'l' )
{
index += 4;
return MiniJSON.TOKEN_NULL;
}
}
return MiniJSON.TOKEN_NONE;
}
#endregion
#region Serialization
protected static bool serializeObjectOrArray( object objectOrArray, StringBuilder builder )
{
if( objectOrArray is Hashtable )
{
return serializeObject( (Hashtable)objectOrArray, builder );
}
else if( objectOrArray is ArrayList )
{
return serializeArray( (ArrayList)objectOrArray, builder );
}
else
{
return false;
}
}
protected static bool serializeObject( Hashtable anObject, StringBuilder builder )
{
builder.Append( "{" );
IDictionaryEnumerator e = anObject.GetEnumerator();
bool first = true;
while( e.MoveNext() )
{
string key = e.Key.ToString();
object value = e.Value;
if( !first )
{
builder.Append( ", " );
}
serializeString( key, builder );
builder.Append( ":" );
if( !serializeValue( value, builder ) )
{
return false;
}
first = false;
}
builder.Append( "}" );
return true;
}
protected static bool serializeDictionary( Dictionary<string,string> dict, StringBuilder builder )
{
builder.Append( "{" );
bool first = true;
foreach( var kv in dict )
{
if( !first )
builder.Append( ", " );
serializeString( kv.Key, builder );
builder.Append( ":" );
serializeString( kv.Value, builder );
first = false;
}
builder.Append( "}" );
return true;
}
protected static bool serializeArray( ArrayList anArray, StringBuilder builder )
{
builder.Append( "[" );
bool first = true;
for( int i = 0; i < anArray.Count; i++ )
{
object value = anArray[i];
if( !first )
{
builder.Append( ", " );
}
if( !serializeValue( value, builder ) )
{
return false;
}
first = false;
}
builder.Append( "]" );
return true;
}
protected static bool serializeValue( object value, StringBuilder builder )
{
// Type t = value.GetType();
// Debug.Log("type: " + t.ToString() + " isArray: " + t.IsArray);
if( value == null )
{
builder.Append( "null" );
}
else if( value.GetType().IsArray )
{
serializeArray( new ArrayList( (ICollection)value ), builder );
}
else if( value is string )
{
serializeString( (string)value, builder );
}
else if( value is Char )
{
serializeString( Convert.ToString( (char)value ), builder );
}
else if( value is Hashtable )
{
serializeObject( (Hashtable)value, builder );
}
else if( value is Dictionary<string,string> )
{
serializeDictionary( (Dictionary<string,string>)value, builder );
}
else if( value is ArrayList )
{
serializeArray( (ArrayList)value, builder );
}
else if( ( value is Boolean ) && ( (Boolean)value == true ) )
{
builder.Append( "true" );
}
else if( ( value is Boolean ) && ( (Boolean)value == false ) )
{
builder.Append( "false" );
}
else if( value.GetType().IsPrimitive )
{
serializeNumber( Convert.ToDouble( value ), builder );
}
else
{
return false;
}
return true;
}
protected static void serializeString( string aString, StringBuilder builder )
{
builder.Append( "\"" );
char[] charArray = aString.ToCharArray();
for( int i = 0; i < charArray.Length; i++ )
{
char c = charArray[i];
if( c == '"' )
{
builder.Append( "\\\"" );
}
else if( c == '\\' )
{
builder.Append( "\\\\" );
}
else if( c == '\b' )
{
builder.Append( "\\b" );
}
else if( c == '\f' )
{
builder.Append( "\\f" );
}
else if( c == '\n' )
{
builder.Append( "\\n" );
}
else if( c == '\r' )
{
builder.Append( "\\r" );
}
else if( c == '\t' )
{
builder.Append( "\\t" );
}
else
{
int codepoint = Convert.ToInt32( c );
if( ( codepoint >= 32 ) && ( codepoint <= 126 ) )
{
builder.Append( c );
}
else
{
builder.Append( "\\u" + Convert.ToString( codepoint, 16 ).PadLeft( 4, '0' ) );
}
}
}
builder.Append( "\"" );
}
protected static void serializeNumber( double number, StringBuilder builder )
{
builder.Append( Convert.ToString( number ) ); // , CultureInfo.InvariantCulture));
}
#endregion
}
#region Extension methods
public static class MiniJsonExtensions
{
public static string toJson( this Hashtable obj )
{
return MiniJSON.jsonEncode( obj );
}
public static string toJson( this Dictionary<string,string> obj )
{
return MiniJSON.jsonEncode( obj );
}
public static ArrayList arrayListFromJson( this string json )
{
return MiniJSON.jsonDecode( json ) as ArrayList;
}
public static Hashtable hashtableFromJson( this string json )
{
return MiniJSON.jsonDecode( json ) as Hashtable;
}
}
#endregion
}
\ No newline at end of file
fileFormatVersion: 2
guid: a9dbf5a8118504261a2be5785b0eca30
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
fileFormatVersion: 2
guid: 841b334890ac942dabe8737fbfcf46db
folderAsset: yes
DefaultImporter:
userData:
fileFormatVersion: 2
guid: 9b2756cd90b6c46a6b28d9d972aee86b
folderAsset: yes
DefaultImporter:
userData:
{
"group": "Bugly",
"libs": [
"libz.dylib",
"libc++.dylib"
],
"frameworks": [
"SystemConfiguration.framework",
"Security.framework",
"JavaScriptCore.framework"
],
"headerpaths": [],
"files": [
"Bugly/Bugly.framework",
"Bugly/BuglyBridge/libBuglyBridge.a",
"Bugly/BuglyBridge/BuglyBridge.h"
],
"folders": [],
"excludes": ["^.*.meta$", "^.*.mdown$", "^.*.pdf$"],
"compiler_flags": [],
"linker_flags": [
"-ObjC"
]
}
\ No newline at end of file
fileFormatVersion: 2
guid: af9acc5fe01ef4dac8882dea364ffb92
DefaultImporter:
userData:
fileFormatVersion: 2
guid: 75b10080bdc104b83beb6663fc9985fb
folderAsset: yes
DefaultImporter:
userData:
fileFormatVersion: 2
guid: 0dd2027a6278544d4acc46c208b9f5f5
timeCreated: 1497948394
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 2e39c573403bc479cb19650e7436097c
folderAsset: yes
timeCreated: 1497947584
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
//
// Bugly.h
// Bugly
//
// Version: 2.4(8)
//
// Copyright (c) 2016年 Bugly. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "BuglyConfig.h"
#import "BuglyLog.h"
BLY_START_NONNULL
@interface Bugly : NSObject
/**
* 初始化Bugly,使用默认BuglyConfig
*
* @param appId 注册Bugly分配的应用唯一标识
*/
+ (void)startWithAppId:(NSString * BLY_NULLABLE)appId;
/**
* 使用指定配置初始化Bugly
*
* @param appId 注册Bugly分配的应用唯一标识
* @param config 传入配置的 BuglyConfig
*/
+ (void)startWithAppId:(NSString * BLY_NULLABLE)appId
config:(BuglyConfig * BLY_NULLABLE)config;
/**
* 使用指定配置初始化Bugly
*
* @param appId 注册Bugly分配的应用唯一标识
* @param development 是否开发设备
* @param config 传入配置的 BuglyConfig
*/
+ (void)startWithAppId:(NSString * BLY_NULLABLE)appId
developmentDevice:(BOOL)development
config:(BuglyConfig * BLY_NULLABLE)config;
/**
* 设置用户标识
*
* @param userId 用户标识
*/
+ (void)setUserIdentifier:(NSString *)userId;
/**
* 更新版本信息
*
* @param version 应用版本信息
*/
+ (void)updateAppVersion:(NSString *)version;
/**
* 设置关键数据,随崩溃信息上报
*
* @param value KEY
* @param key VALUE
*/
+ (void)setUserValue:(NSString *)value
forKey:(NSString *)key;
/**
* 获取关键数据
*
* @return 关键数据
*/
+ (NSDictionary * BLY_NULLABLE)allUserValues;
/**
* 设置标签
*
* @param tag 标签ID,可在网站生成
*/
+ (void)setTag:(NSUInteger)tag;
/**
* 获取当前设置标签
*
* @return 当前标签ID
*/
+ (NSUInteger)currentTag;
/**
* 获取设备ID
*
* @return 设备ID
*/
+ (NSString *)buglyDeviceId;
/**
* 上报自定义Objective-C异常
*
* @param exception 异常信息
*/
+ (void)reportException:(NSException *)exception;
/**
* 上报错误
*
* @param error 错误信息
*/
+ (void)reportError:(NSError *)error;
/**
* @brief 上报自定义错误
*
* @param category 类型(Cocoa=3,CSharp=4,JS=5,Lua=6)
* @param aName 名称
* @param aReason 错误原因
* @param aStackArray 堆栈
* @param info 附加数据
* @param terminate 上报后是否退出应用进程
*/
+ (void)reportExceptionWithCategory:(NSUInteger)category name:(NSString *)aName reason:(NSString *)aReason callStack:(NSArray *)aStackArray extraInfo:(NSDictionary *)info terminateApp:(BOOL)terminate;
/**
* SDK 版本信息
*
* @return SDK版本号
*/
+ (NSString *)sdkVersion;
/**
* App 是否发生了连续闪退
* 如果启动SDK 且 5秒内 闪退,且次数达到 3次 则判定为连续闪退
*
* @return 是否连续闪退
*/
+ (BOOL)isAppCrashedOnStartUpExceedTheLimit;
+ (void)setComponentIdentifier:(NSString *)componentId version:(NSString *)version;
BLY_END_NONNULL
@end
fileFormatVersion: 2
guid: 841e5a51a12834159aeebd4cbad0d2ce
timeCreated: 1497948394
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
//
// BuglyConfig.h
//
//
// Copyright (c) 2016年 Tencent. All rights reserved.
//
#pragma once
#define BLY_UNAVAILABLE(x) __attribute__((unavailable(x)))
#if __has_feature(nullability)
#define BLY_NONNULL __nonnull
#define BLY_NULLABLE __nullable
#define BLY_START_NONNULL _Pragma("clang assume_nonnull begin")
#define BLY_END_NONNULL _Pragma("clang assume_nonnull end")
#else
#define BLY_NONNULL
#define BLY_NULLABLE
#define BLY_START_NONNULL
#define BLY_END_NONNULL
#endif
#import <Foundation/Foundation.h>
#import "BuglyLog.h"
BLY_START_NONNULL
@protocol BuglyDelegate <NSObject>
@optional
/**
* 发生异常时回调
*
* @param exception 异常信息
*
* @return 返回需上报记录,随异常上报一起上报
*/
- (NSString * BLY_NULLABLE)attachmentForException:(NSException * BLY_NULLABLE)exception;
@end
@interface BuglyConfig : NSObject
/**
* SDK Debug信息开关, 默认关闭
*/
@property (nonatomic, assign) BOOL debugMode;
/**
* 设置自定义渠道标识
*/
@property (nonatomic, copy) NSString *channel;
/**
* 设置自定义版本号
*/
@property (nonatomic, copy) NSString *version;
/**
* 设置自定义设备唯一标识
*/
@property (nonatomic, copy) NSString *deviceIdentifier;
/**
* 卡顿监控开关,默认关闭
*/
@property (nonatomic) BOOL blockMonitorEnable;
/**
* 卡顿监控判断间隔,单位为秒
*/
@property (nonatomic) NSTimeInterval blockMonitorTimeout;
/**
* 设置 App Groups Id (如有使用 Bugly iOS Extension SDK,请设置该值)
*/
@property (nonatomic, copy) NSString *applicationGroupIdentifier;
/**
* 进程内还原开关,默认开启
*/
@property (nonatomic) BOOL symbolicateInProcessEnable;
/**
* 非正常退出事件记录开关,默认关闭
*/
@property (nonatomic) BOOL unexpectedTerminatingDetectionEnable;
/**
* 页面信息记录开关,默认开启
*/
@property (nonatomic) BOOL viewControllerTrackingEnable;
/**
* Bugly Delegate
*/
@property (nonatomic, assign) id<BuglyDelegate> delegate;
/**
* 控制自定义日志上报,默认值为BuglyLogLevelSilent,即关闭日志记录功能。
* 如果设置为BuglyLogLevelWarn,则在崩溃时会上报Warn、Error接口打印的日志
*/
@property (nonatomic, assign) BuglyLogLevel reportLogLevel;
/**
* 崩溃数据过滤器,如果崩溃堆栈的模块名包含过滤器中设置的关键字,则崩溃数据不会进行上报
* 例如,过滤崩溃堆栈中包含搜狗输入法的数据,可以添加过滤器关键字SogouInputIPhone.dylib等
*/
@property (nonatomic, copy) NSArray *excludeModuleFilter;
/**
* 控制台日志上报开关,默认开启
*/
@property (nonatomic, assign) BOOL consolelogEnable;
@end
BLY_END_NONNULL
fileFormatVersion: 2
guid: dca72c35ab06f4c9abf2877ad9a9f718
timeCreated: 1497948394
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
//
// BuglyLog.h
//
// Copyright © 2017 tencent.com. All rights reserved.
//
#import <Foundation/Foundation.h>
// Log level for Bugly Log
typedef NS_ENUM(NSUInteger, BuglyLogLevel) {
BuglyLogLevelSilent = 0,
BuglyLogLevelError = 1,
BuglyLogLevelWarn = 2,
BuglyLogLevelInfo = 3,
BuglyLogLevelDebug = 4,
BuglyLogLevelVerbose = 5,
};
#pragma mark -
OBJC_EXTERN void BLYLog(BuglyLogLevel level, NSString *format, ...) NS_FORMAT_FUNCTION(2, 3);
OBJC_EXTERN void BLYLogv(BuglyLogLevel level, NSString *format, va_list args) NS_FORMAT_FUNCTION(2, 0);
#pragma mark -
#define BUGLY_LOG_MACRO(_level, fmt, ...) [BuglyLog level:_level tag:nil log:fmt, ##__VA_ARGS__]
#define BLYLogError(fmt, ...) BUGLY_LOG_MACRO(BuglyLogLevelError, fmt, ##__VA_ARGS__)
#define BLYLogWarn(fmt, ...) BUGLY_LOG_MACRO(BuglyLogLevelWarn, fmt, ##__VA_ARGS__)
#define BLYLogInfo(fmt, ...) BUGLY_LOG_MACRO(BuglyLogLevelInfo, fmt, ##__VA_ARGS__)
#define BLYLogDebug(fmt, ...) BUGLY_LOG_MACRO(BuglyLogLevelDebug, fmt, ##__VA_ARGS__)
#define BLYLogVerbose(fmt, ...) BUGLY_LOG_MACRO(BuglyLogLevelVerbose, fmt, ##__VA_ARGS__)
#pragma mark - Interface
@interface BuglyLog : NSObject
/**
* @brief 初始化日志模块
*
* @param level 设置默认日志级别,默认BLYLogLevelSilent
*
* @param printConsole 是否打印到控制台,默认NO
*/
+ (void)initLogger:(BuglyLogLevel) level consolePrint:(BOOL)printConsole;
/**
* @brief 打印BLYLogLevelInfo日志
*
* @param format 日志内容 总日志大小限制为:字符串长度30k,条数200
*/
+ (void)log:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
/**
* @brief 打印日志
*
* @param level 日志级别
* @param message 日志内容 总日志大小限制为:字符串长度30k,条数200
*/
+ (void)level:(BuglyLogLevel) level logs:(NSString *)message;
/**
* @brief 打印日志
*
* @param level 日志级别
* @param format 日志内容 总日志大小限制为:字符串长度30k,条数200
*/
+ (void)level:(BuglyLogLevel) level log:(NSString *)format, ... NS_FORMAT_FUNCTION(2, 3);
/**
* @brief 打印日志
*
* @param level 日志级别
* @param tag 日志模块分类
* @param format 日志内容 总日志大小限制为:字符串长度30k,条数200
*/
+ (void)level:(BuglyLogLevel) level tag:(NSString *) tag log:(NSString *)format, ... NS_FORMAT_FUNCTION(3, 4);
@end
fileFormatVersion: 2
guid: 52d616a4d69ce46469563ec4166a5da7
timeCreated: 1497948394
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 877eab29b0d76443883ae4dbb398cb69
folderAsset: yes
timeCreated: 1497948394
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
framework module Bugly {
umbrella header "Bugly.h"
export *
module * { export * }
link framework "Foundation"
link framework "Security"
link framework "SystemConfiguration"
link "c++"
link "z"
}
fileFormatVersion: 2
guid: 01fcf8f3340eb42758a274ba09bbfa74
timeCreated: 1497948394
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: d6b25bfcf65a44ec0a0e6973f612af2a
folderAsset: yes
DefaultImporter:
userData:
//
// BuglyBridge.h
// BuglyAgent
//
// Created by Yeelik on 15/11/25.
// Copyright © 2015年 Bugly. All rights reserved.
//
// Version: 1.3.3
//
#import <Foundation/Foundation.h>
#pragma mark - Interface for Bridge
#ifdef __cplusplus
extern "C"{
#endif
/**
* @brief 初始化
*
* @param appId 应用标识
* @param debug 是否开启debug模式,开启后会在控制台打印调试信息,默认为NO
* @param level 自定义日志上报级别,使用SDK接口打印的日志会跟崩溃信息一起上报,默认为Info(即Info、Warning、Error级别的日志都会上报)
* Debug=4,Info=3,Warnning=2,Error=1,Off=0
*/
void _BuglyInit(const char * appId, bool debug, int level);
/**
* @brief 设置用户唯一标识
*
* @param userId
*/
void _BuglySetUserId(const char * userId);
/**
* @brief 设置自定义标签
*
* @param tag
*/
void _BuglySetTag(int tag);
/**
* @brief 设置自定义键值对数据
*
* @param key
* @param value
*/
void _BuglySetKeyValue(const char * key, const char * value);
/**
* @brief 自定义异常数据上报
*
* @param type
* @param name 异常类型
* @param reason 异常原因
* @param stackTrace 异常堆栈
* @param extras 附加数据
* @param quit 上报后是否退出应用
*/
void _BuglyReportException(int type, const char * name, const char * reason, const char * stackTrace, const char * extras, bool quit);
/**
* @brief 设置默认的应用配置,在初始化之前调用
*
* @param channel 渠道
* @param version 应用版本
* @param user 用户
* @param deviceId 设备唯一标识
*/
void _BuglyDefaultConfig(const char * channel, const char * version, const char *user, const char * deviceId);
/**
* @brief 自定义日志打印接口
*
* @param level 日志级别, 1=Error、2=Warning、3=Info、4=Debug
* @param tag 日志标签
* @param log 日志内容
*/
void _BuglyLogMessage(int level, const char * tag, const char * log);
/**
* @brief 设置崩溃上报组件的类别
*
* @param type 0=Default、1=Bugly、2=MSDK、3=IMSDK
*/
void _BuglyConfigCrashReporterType(int type);
/**
* @brief 设置额外的配置信息
*
* @param key
* @param value
*/
void _BuglySetExtraConfig(const char *key, const char * value);
#ifdef __cplusplus
} // extern "C"
#endif
#pragma mark -
fileFormatVersion: 2
guid: aded946ff2bdf40ff9ac3e5c10f8fa5d
timeCreated: 1497947595
licenseType: Free
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 8e4622a66135549e18a59de3c9348ba7
timeCreated: 1497947594
licenseType: Free
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 50e42c3e8d4df4693be96cca1b0333f6
folderAsset: yes
DefaultImporter:
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXBuildFile : PBXObject
{
private const string FILE_REF_KEY = "fileRef";
private const string SETTINGS_KEY = "settings";
private const string ATTRIBUTES_KEY = "ATTRIBUTES";
private const string WEAK_VALUE = "Weak";
private const string COMPILER_FLAGS_KEY = "COMPILER_FLAGS";
public PBXBuildFile( PBXFileReference fileRef, bool weak = false ) : base()
{
this.Add( FILE_REF_KEY, fileRef.guid );
SetWeakLink( weak );
if (!string.IsNullOrEmpty(fileRef.compilerFlags))
{
foreach (var flag in fileRef.compilerFlags.Split(','))
AddCompilerFlag(flag);
}
}
public PBXBuildFile( string guid, PBXDictionary dictionary ) : base ( guid, dictionary )
{
}
public string fileRef
{
get {
return (string)_data[ FILE_REF_KEY ];
}
}
public bool SetWeakLink( bool weak = false )
{
PBXDictionary settings = null;
PBXList attributes = null;
if( !_data.ContainsKey( SETTINGS_KEY ) ) {
if( weak ) {
attributes = new PBXList();
attributes.Add( WEAK_VALUE );
settings = new PBXDictionary();
settings.Add( ATTRIBUTES_KEY, attributes );
_data.Add( SETTINGS_KEY, settings );
}
return true;
}
settings = _data[ SETTINGS_KEY ] as PBXDictionary;
if( !settings.ContainsKey( ATTRIBUTES_KEY ) ) {
if( weak ) {
attributes = new PBXList();
attributes.Add( WEAK_VALUE );
settings.Add( ATTRIBUTES_KEY, attributes );
return true;
}
else {
return false;
}
}
else {
attributes = settings[ ATTRIBUTES_KEY ] as PBXList;
}
if( weak ) {
attributes.Add( WEAK_VALUE );
}
else {
attributes.Remove( WEAK_VALUE );
}
settings.Add( ATTRIBUTES_KEY, attributes );
this.Add( SETTINGS_KEY, settings );
return true;
}
//CodeSignOnCopy
public bool AddCodeSignOnCopy()
{
if( !_data.ContainsKey( SETTINGS_KEY ) )
_data[ SETTINGS_KEY ] = new PBXDictionary();
var settings = _data[ SETTINGS_KEY ] as PBXDictionary;
if( !settings.ContainsKey( ATTRIBUTES_KEY ) ) {
var attributes = new PBXList();
attributes.Add( "CodeSignOnCopy" );
attributes.Add( "RemoveHeadersOnCopy" );
settings.Add( ATTRIBUTES_KEY, attributes );
}
else {
var attributes = settings[ ATTRIBUTES_KEY ] as PBXList;
attributes.Add( "CodeSignOnCopy" );
attributes.Add( "RemoveHeadersOnCopy" );
}
return true;
}
public bool AddCompilerFlag( string flag )
{
if( !_data.ContainsKey( SETTINGS_KEY ) )
_data[ SETTINGS_KEY ] = new PBXDictionary();
if( !((PBXDictionary)_data[ SETTINGS_KEY ]).ContainsKey( COMPILER_FLAGS_KEY ) ) {
((PBXDictionary)_data[ SETTINGS_KEY ]).Add( COMPILER_FLAGS_KEY, flag );
return true;
}
string[] flags = ((string)((PBXDictionary)_data[ SETTINGS_KEY ])[ COMPILER_FLAGS_KEY ]).Split( ' ' );
foreach( string item in flags ) {
if( item.CompareTo( flag ) == 0 )
return false;
}
((PBXDictionary)_data[ SETTINGS_KEY ])[ COMPILER_FLAGS_KEY ] = ( string.Join( " ", flags ) + " " + flag );
return true;
}
}
}
fileFormatVersion: 2
guid: 9992e69c513ea481ebe3119a011e16cd
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXBuildPhase : PBXObject
{
protected const string FILES_KEY = "files";
public PBXBuildPhase() :base()
{
}
public PBXBuildPhase( string guid, PBXDictionary dictionary ) : base ( guid, dictionary )
{
}
public bool AddBuildFile( PBXBuildFile file )
{
if( !ContainsKey( FILES_KEY ) ){
this.Add( FILES_KEY, new PBXList() );
}
((PBXList)_data[ FILES_KEY ]).Add( file.guid );
return true;
}
public void RemoveBuildFile( string id )
{
if( !ContainsKey( FILES_KEY ) ) {
this.Add( FILES_KEY, new PBXList() );
return;
}
((PBXList)_data[ FILES_KEY ]).Remove( id );
}
public bool HasBuildFile( string id )
{
if( !ContainsKey( FILES_KEY ) ) {
this.Add( FILES_KEY, new PBXList() );
return false;
}
if( !IsGuid( id ) )
return false;
return ((PBXList)_data[ FILES_KEY ]).Contains( id );
}
public PBXList files {
get {
if( !ContainsKey( FILES_KEY ) ) {
this.Add( FILES_KEY, new PBXList() );
}
return (PBXList)_data[ FILES_KEY ];
}
}
}
public class PBXFrameworksBuildPhase : PBXBuildPhase
{
public PBXFrameworksBuildPhase( string guid, PBXDictionary dictionary ) : base ( guid, dictionary )
{
}
}
public class PBXResourcesBuildPhase : PBXBuildPhase
{
public PBXResourcesBuildPhase( string guid, PBXDictionary dictionary ) : base ( guid, dictionary )
{
}
}
public class PBXShellScriptBuildPhase : PBXBuildPhase
{
public PBXShellScriptBuildPhase( string guid, PBXDictionary dictionary ) : base ( guid, dictionary )
{
}
}
public class PBXSourcesBuildPhase : PBXBuildPhase
{
public PBXSourcesBuildPhase( string guid, PBXDictionary dictionary ) : base ( guid, dictionary )
{
}
}
public class PBXCopyFilesBuildPhase : PBXBuildPhase
{
//Embed Frameworks PBXCopyFilesBuildPhase constructor
//to make sure "isa" = "PBXCopyFilesBuildPhase"
public PBXCopyFilesBuildPhase( int buildActionMask ) :base()
{
this.Add("buildActionMask", buildActionMask);
this.Add("dstPath", "");
this.Add("dstSubfolderSpec", 10);
this.Add("name", "Embed Frameworks");
this.Add("runOnlyForDeploymentPostprocessing", 0);
}
public PBXCopyFilesBuildPhase( string guid, PBXDictionary dictionary ) : base ( guid, dictionary )
{
}
}
}
fileFormatVersion: 2
guid: dbd728e6c046340b98e080ac6bc58670
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXDictionary : Dictionary<string, object>
{
public void Append( PBXDictionary dictionary )
{
foreach( var item in dictionary) {
this.Add( item.Key, item.Value );
}
}
public void Append<T>( PBXDictionary<T> dictionary ) where T : PBXObject
{
foreach( var item in dictionary) {
this.Add( item.Key, item.Value );
}
}
public void Append( PBXSortedDictionary dictionary )
{
foreach( var item in dictionary) {
this.Add( item.Key, item.Value );
}
}
public void Append<T>( PBXSortedDictionary<T> dictionary ) where T : PBXObject
{
foreach( var item in dictionary) {
this.Add( item.Key, item.Value );
}
}
/// <summary>
/// This allows us to use the form:
/// "if (x)" or "if (!x)"
/// </summary>
public static implicit operator bool( PBXDictionary x ) {
//if null or empty, treat us as false/null
return (x == null) ? false : (x.Count == 0);
}
/// <summary>
/// I find this handy. return our fields as comma-separated values
/// </summary>
public string ToCSV() {
// TODO use a char sep argument to allow specifying separator
string ret = string.Empty;
foreach (KeyValuePair<string, object> item in this) {
ret += "<";
ret += item.Key;
ret += ", ";
ret += item.Value;
ret += ">, ";
}
return ret;
}
/// <summary>
/// Concatenate and format so appears as "{,,,}"
/// </summary>
public override string ToString() {
return "{" + this.ToCSV() + "}";
}
}
public class PBXDictionary<T> : Dictionary<string, T> where T : PBXObject
{
public PBXDictionary()
{
}
public PBXDictionary( PBXDictionary genericDictionary )
{
foreach( KeyValuePair<string, object> currentItem in genericDictionary ) {
if( ((string)((PBXDictionary)currentItem.Value)[ "isa" ]).CompareTo( typeof(T).Name ) == 0 ) {
T instance = (T)System.Activator.CreateInstance( typeof(T), currentItem.Key, (PBXDictionary)currentItem.Value );
this.Add( currentItem.Key, instance );
}
}
}
public PBXDictionary( PBXSortedDictionary genericDictionary )
{
foreach( KeyValuePair<string, object> currentItem in genericDictionary ) {
if( ((string)((PBXDictionary)currentItem.Value)[ "isa" ]).CompareTo( typeof(T).Name ) == 0 ) {
T instance = (T)System.Activator.CreateInstance( typeof(T), currentItem.Key, (PBXDictionary)currentItem.Value );
this.Add( currentItem.Key, instance );
}
}
}
public void Add( T newObject )
{
this.Add( newObject.guid, newObject );
}
public void Append( PBXDictionary<T> dictionary )
{
foreach( KeyValuePair<string, T> item in dictionary) {
this.Add( item.Key, (T)item.Value );
}
}
}
}
fileFormatVersion: 2
guid: 80ec175e8a0a4490597beec8a365dcb3
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXFileReference : PBXObject
{
protected const string PATH_KEY = "path";
protected const string NAME_KEY = "name";
protected const string SOURCETREE_KEY = "sourceTree";
protected const string EXPLICIT_FILE_TYPE_KEY = "explicitFileType";
protected const string LASTKNOWN_FILE_TYPE_KEY = "lastKnownFileType";
protected const string ENCODING_KEY = "fileEncoding";
public string compilerFlags;
public string buildPhase;
public readonly Dictionary<TreeEnum, string> trees = new Dictionary<TreeEnum, string> {
{ TreeEnum.ABSOLUTE, "<absolute>" },
{ TreeEnum.GROUP, "<group>" },
{ TreeEnum.BUILT_PRODUCTS_DIR, "BUILT_PRODUCTS_DIR" },
{ TreeEnum.DEVELOPER_DIR, "DEVELOPER_DIR" },
{ TreeEnum.SDKROOT, "SDKROOT" },
{ TreeEnum.SOURCE_ROOT, "SOURCE_ROOT" }
};
public static readonly Dictionary<string, string> typeNames = new Dictionary<string, string> {
{ ".a", "archive.ar" },
{ ".app", "wrapper.application" },
{ ".s", "sourcecode.asm" },
{ ".c", "sourcecode.c.c" },
{ ".cpp", "sourcecode.cpp.cpp" },
{ ".framework", "wrapper.framework" },
{ ".h", "sourcecode.c.h" },
{ ".pch", "sourcecode.c.h" },
{ ".icns", "image.icns" },
{ ".m", "sourcecode.c.objc" },
{ ".mm", "sourcecode.cpp.objcpp" },
{ ".nib", "wrapper.nib" },
{ ".plist", "text.plist.xml" },
{ ".png", "image.png" },
{ ".rtf", "text.rtf" },
{ ".tiff", "image.tiff" },
{ ".txt", "text" },
{ ".xcodeproj", "wrapper.pb-project" },
{ ".xib", "file.xib" },
{ ".strings", "text.plist.strings" },
{ ".bundle", "wrapper.plug-in" },
{ ".dylib", "compiled.mach-o.dylib" },
{ ".tbd", "sourcecode.text-based-dylib-definition" },
{ ".json", "text.json" }
};
public static readonly Dictionary<string, string> typePhases = new Dictionary<string, string> {
{ ".a", "PBXFrameworksBuildPhase" },
{ ".app", null },
{ ".s", "PBXSourcesBuildPhase" },
{ ".c", "PBXSourcesBuildPhase" },
{ ".cpp", "PBXSourcesBuildPhase" },
{ ".framework", "PBXFrameworksBuildPhase" },
{ ".h", null },
{ ".pch", null },
{ ".icns", "PBXResourcesBuildPhase" },
{ ".m", "PBXSourcesBuildPhase" },
{ ".mm", "PBXSourcesBuildPhase" },
{ ".nib", "PBXResourcesBuildPhase" },
{ ".plist", "PBXResourcesBuildPhase" },
{ ".png", "PBXResourcesBuildPhase" },
{ ".rtf", "PBXResourcesBuildPhase" },
{ ".tiff", "PBXResourcesBuildPhase" },
{ ".txt", "PBXResourcesBuildPhase" },
{ ".json", "PBXResourcesBuildPhase" },
{ ".xcodeproj", null },
{ ".xib", "PBXResourcesBuildPhase" },
{ ".strings", "PBXResourcesBuildPhase" },
{ ".bundle", "PBXResourcesBuildPhase" },
{ ".dylib", "PBXFrameworksBuildPhase" },
{ ".tbd", "PBXFrameworksBuildPhase" }
};
public PBXFileReference( string guid, PBXDictionary dictionary ) : base( guid, dictionary )
{
}
//TODO see if XCode has a preference for ordering these attributes
public PBXFileReference( string filePath, TreeEnum tree = TreeEnum.SOURCE_ROOT ) : base()
{
this.Add( PATH_KEY, filePath );
this.Add( NAME_KEY, System.IO.Path.GetFileName( filePath ) );
this.Add( SOURCETREE_KEY, (string)( System.IO.Path.IsPathRooted( filePath ) ? trees[TreeEnum.ABSOLUTE] : trees[tree] ) );
this.GuessFileType();
}
public string name {
get {
if( !ContainsKey( NAME_KEY ) ) {
return null;
}
return (string)_data[NAME_KEY];
}
}
public string path {
get {
if( !ContainsKey( PATH_KEY ) ) {
return null;
}
return (string)_data[PATH_KEY];
}
}
private void GuessFileType()
{
this.Remove( EXPLICIT_FILE_TYPE_KEY );
this.Remove( LASTKNOWN_FILE_TYPE_KEY );
string extension = System.IO.Path.GetExtension( (string)_data[ PATH_KEY ] );
if( !PBXFileReference.typeNames.ContainsKey( extension ) ){
Debug.LogWarning( "Unknown file extension: " + extension + "\nPlease add extension and Xcode type to PBXFileReference.types" );
return;
}
this.Add( LASTKNOWN_FILE_TYPE_KEY, PBXFileReference.typeNames[ extension ] );
this.buildPhase = PBXFileReference.typePhases[ extension ];
}
private void SetFileType( string fileType )
{
this.Remove( EXPLICIT_FILE_TYPE_KEY );
this.Remove( LASTKNOWN_FILE_TYPE_KEY );
this.Add( EXPLICIT_FILE_TYPE_KEY, fileType );
}
}
public enum TreeEnum {
ABSOLUTE,
GROUP,
BUILT_PRODUCTS_DIR,
DEVELOPER_DIR,
SDKROOT,
SOURCE_ROOT
}
}
fileFormatVersion: 2
guid: 9e85cd63a1f5947d9bc04aa70c0a211e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXGroup : PBXObject
{
protected const string NAME_KEY = "name";
protected const string CHILDREN_KEY = "children";
protected const string PATH_KEY = "path";
protected const string SOURCETREE_KEY = "sourceTree";
#region Constructor
public PBXGroup( string name, string path = null, string tree = "SOURCE_ROOT" ) : base()
{
this.Add( CHILDREN_KEY, new PBXList() );
this.Add( NAME_KEY, name );
if( path != null ) {
this.Add( PATH_KEY, path );
this.Add( SOURCETREE_KEY, tree );
}
else {
this.Add( SOURCETREE_KEY, "<group>" );
}
}
public PBXGroup( string guid, PBXDictionary dictionary ) : base( guid, dictionary )
{
}
#endregion
#region Properties
public PBXList children {
get {
if( !ContainsKey( CHILDREN_KEY ) ) {
this.Add( CHILDREN_KEY, new PBXList() );
}
return (PBXList)_data[CHILDREN_KEY];
}
}
public string name {
get {
if( !ContainsKey( NAME_KEY ) ) {
return null;
}
return (string)_data[NAME_KEY];
}
}
public string path {
get {
if( !ContainsKey( PATH_KEY ) ) {
return null;
}
return (string)_data[PATH_KEY];
}
}
public string sourceTree {
get {
return (string)_data[SOURCETREE_KEY];
}
}
#endregion
public string AddChild( PBXObject child )
{
if( child is PBXFileReference || child is PBXGroup ) {
children.Add( child.guid );
return child.guid;
}
return null;
}
public void RemoveChild( string id )
{
if( !IsGuid( id ) )
return;
children.Remove( id );
}
public bool HasChild( string id )
{
if( !ContainsKey( CHILDREN_KEY ) ) {
this.Add( CHILDREN_KEY, new PBXList() );
return false;
}
if( !IsGuid( id ) )
return false;
return ((PBXList)_data[ CHILDREN_KEY ]).Contains( id );
}
public string GetName()
{
return (string)_data[ NAME_KEY ];
}
}
}
fileFormatVersion: 2
guid: 7244995014f654f3d8749bb32782ab95
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXList : ArrayList
{
public PBXList()
{
}
public PBXList( object firstValue )
{
this.Add( firstValue );
}
/// <summary>
/// This allows us to use the form:
/// "if (x)" or "if (!x)"
/// </summary>
public static implicit operator bool( PBXList x ) {
//if null or empty, treat us as false/null
return (x == null) ? false : (x.Count == 0);
}
/// <summary>
/// I find this handy. return our fields as comma-separated values
/// </summary>
public string ToCSV() {
// TODO use a char sep argument to allow specifying separator
string ret = string.Empty;
foreach (string item in this) {
ret += "\"";
ret += item;
ret += "\", ";
}
return ret;
}
/// <summary>
/// Concatenate and format so appears as "{,,,}"
/// </summary>
public override string ToString() {
return "{" + this.ToCSV() + "} ";
}
}
}
fileFormatVersion: 2
guid: 3f75b6962285f4b0fae8024b6fac0947
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXObject
{
protected const string ISA_KEY = "isa";
protected string _guid;
protected PBXDictionary _data;
#region Properties
public string guid {
get {
if( string.IsNullOrEmpty( _guid ) )
_guid = GenerateGuid();
return _guid;
}
}
public PBXDictionary data {
get {
if( _data == null )
_data = new PBXDictionary();
return _data;
}
}
#endregion
#region Constructors
public PBXObject()
{
_data = new PBXDictionary();
_data[ ISA_KEY ] = this.GetType().Name;
_guid = GenerateGuid();
}
public PBXObject( string guid ) : this()
{
if( IsGuid( guid ) )
_guid = guid;
}
public PBXObject( string guid, PBXDictionary dictionary ) : this( guid )
{
if( !dictionary.ContainsKey( ISA_KEY ) || ((string)dictionary[ ISA_KEY ]).CompareTo( this.GetType().Name ) != 0 )
Debug.LogError( "PBXDictionary is not a valid ISA object" );
foreach( KeyValuePair<string, object> item in dictionary ) {
_data[ item.Key ] = item.Value;
}
}
#endregion
#region Static methods
public static bool IsGuid( string aString )
{
// Note: Unity3d generates mixed-case GUIDs, Xcode use uppercase GUIDs only.
return System.Text.RegularExpressions.Regex.IsMatch( aString, @"^[A-Fa-f0-9]{24}$" );
}
public static string GenerateGuid()
{
return System.Guid.NewGuid().ToString("N").Substring( 8 ).ToUpper();
}
#endregion
#region Data manipulation
public void Add( string key, object obj )
{
_data.Add( key, obj );
}
public bool Remove( string key )
{
return _data.Remove( key );
}
public bool ContainsKey( string key )
{
return _data.ContainsKey( key );
}
#endregion
#region syntactic sugar
/// <summary>
/// This allows us to use the form:
/// "if (x)" or "if (!x)"
/// </summary>
public static implicit operator bool( PBXObject x ) {
//if null or no data, treat us as false/null
return (x == null) ? false : (x.data.Count == 0);
}
/// <summary>
/// I find this handy. return our fields as comma-separated values
/// </summary>
public string ToCSV() {
return "\"" + data + "\", ";
}
/// <summary>
/// Concatenate and format so appears as "{,,,}"
/// </summary>
public override string ToString() {
return "{" + this.ToCSV() + "} ";
}
#endregion
}
public class PBXNativeTarget : PBXObject
{
public PBXNativeTarget() : base() {
}
public PBXNativeTarget( string guid, PBXDictionary dictionary ) : base( guid, dictionary ) {
}
}
public class PBXContainerItemProxy : PBXObject
{
public PBXContainerItemProxy() : base() {
}
public PBXContainerItemProxy( string guid, PBXDictionary dictionary ) : base( guid, dictionary ) {
}
}
public class PBXReferenceProxy : PBXObject
{
public PBXReferenceProxy() : base() {
}
public PBXReferenceProxy( string guid, PBXDictionary dictionary ) : base( guid, dictionary ) {
}
}
}
fileFormatVersion: 2
guid: c2821fff09674488a8ed27548cee3074
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace UnityEditor.XCodeEditor
{
public class PBXResolver
{
private class PBXResolverReverseIndex : Dictionary<string, string> {}
private PBXDictionary objects;
private string rootObject;
private PBXResolverReverseIndex index;
public PBXResolver( PBXDictionary pbxData ) {
this.objects = (PBXDictionary)pbxData[ "objects" ];
this.index = new PBXResolverReverseIndex();
this.rootObject = (string)pbxData[ "rootObject" ];
BuildReverseIndex();
}
private void BuildReverseIndex()
{
foreach( KeyValuePair<string, object> pair in this.objects )
{
if( pair.Value is PBXBuildPhase )
{
foreach( string guid in ((PBXBuildPhase)pair.Value).files )
{
index[ guid ] = pair.Key;
}
}
else if( pair.Value is PBXGroup )
{
foreach( string guid in ((PBXGroup)pair.Value).children )
{
index[ guid ] = pair.Key;
}
}
}
}
public string ResolveName( string guid )
{
if (!this.objects.ContainsKey(guid)) {
Debug.LogWarning(this + " ResolveName could not resolve " + guid);
return string.Empty; //"UNRESOLVED GUID:" + guid;
}
object entity = this.objects[ guid ];
if( entity is PBXBuildFile )
{
return ResolveName( ((PBXBuildFile)entity).fileRef );
}
else if( entity is PBXFileReference )
{
PBXFileReference casted = (PBXFileReference)entity;
return casted.name != null ? casted.name : casted.path;
}
else if( entity is PBXGroup )
{
PBXGroup casted = (PBXGroup)entity;
return casted.name != null ? casted.name : casted.path;
}
else if( entity is PBXProject || guid == this.rootObject )
{
return "Project object";
}
else if( entity is PBXFrameworksBuildPhase )
{
return "Frameworks";
}
else if( entity is PBXResourcesBuildPhase )
{
return "Resources";
}
else if( entity is PBXShellScriptBuildPhase )
{
return "ShellScript";
}
else if( entity is PBXSourcesBuildPhase )
{
return "Sources";
}
else if( entity is PBXCopyFilesBuildPhase )
{
return "CopyFiles";
}
else if( entity is XCConfigurationList )
{
XCConfigurationList casted = (XCConfigurationList)entity;
//Debug.LogWarning ("XCConfigurationList " + guid + " " + casted.ToString());
if( casted.data.ContainsKey( "defaultConfigurationName" ) ) {
//Debug.Log ("XCConfigurationList " + (string)casted.data[ "defaultConfigurationName" ] + " " + guid);
return (string)casted.data[ "defaultConfigurationName" ];
}
return null;
}
else if( entity is PBXNativeTarget )
{
PBXNativeTarget obj = (PBXNativeTarget)entity;
//Debug.LogWarning ("PBXNativeTarget " + guid + " " + obj.ToString());
if( obj.data.ContainsKey( "name" ) ) {
//Debug.Log ("PBXNativeTarget " + (string)obj.data[ "name" ] + " " + guid);
return (string)obj.data[ "name" ];
}
return null;
}
else if( entity is XCBuildConfiguration )
{
XCBuildConfiguration obj = (XCBuildConfiguration)entity;
//Debug.LogWarning ("XCBuildConfiguration UNRESOLVED GUID:" + guid + " " + (obj==null?"":obj.ToString()));
if( obj.data.ContainsKey( "name" ) ) {
//Debug.Log ("XCBuildConfiguration " + (string)obj.data[ "name" ] + " " + guid + " " + (obj==null?"":obj.ToString()));
return (string)obj.data[ "name" ];
}
}
else if( entity is PBXObject )
{
PBXObject obj = (PBXObject)entity;
if( obj.data.ContainsKey( "name" ) )
Debug.Log ("PBXObject " + (string)obj.data[ "name" ] + " " + guid + " " + (obj==null?"":obj.ToString()));
return (string)obj.data[ "name" ];
}
//return "UNRESOLVED GUID:" + guid;
Debug.LogWarning ("UNRESOLVED GUID:" + guid);
return null;
}
public string ResolveBuildPhaseNameForFile( string guid )
{
if( this.objects.ContainsKey( guid ) )
{
object obj = this.objects[ guid ];
if( obj is PBXObject )
{
PBXObject entity = (PBXObject)obj;
if( this.index.ContainsKey( entity.guid ) )
{
string parent_guid = this.index[ entity.guid ];
if( this.objects.ContainsKey( parent_guid ) )
{
object parent = this.objects[ parent_guid ];
if( parent is PBXBuildPhase ) {
string ret = ResolveName( ((PBXBuildPhase)parent).guid );
//Debug.Log ("ResolveBuildPhaseNameForFile = " + ret);
return ret;
}
}
}
}
}
return null;
}
}
public class PBXParser
{
public const string PBX_HEADER_TOKEN = "// !$*UTF8*$!\n";
public const char WHITESPACE_SPACE = ' ';
public const char WHITESPACE_TAB = '\t';
public const char WHITESPACE_NEWLINE = '\n';
public const char WHITESPACE_CARRIAGE_RETURN = '\r';
public const char ARRAY_BEGIN_TOKEN = '(';
public const char ARRAY_END_TOKEN = ')';
public const char ARRAY_ITEM_DELIMITER_TOKEN = ',';
public const char DICTIONARY_BEGIN_TOKEN = '{';
public const char DICTIONARY_END_TOKEN = '}';
public const char DICTIONARY_ASSIGN_TOKEN = '=';
public const char DICTIONARY_ITEM_DELIMITER_TOKEN = ';';
public const char QUOTEDSTRING_BEGIN_TOKEN = '"';
public const char QUOTEDSTRING_END_TOKEN = '"';
public const char QUOTEDSTRING_ESCAPE_TOKEN = '\\';
public const char END_OF_FILE = (char)0x1A;
public const string COMMENT_BEGIN_TOKEN = "/*";
public const string COMMENT_END_TOKEN = "*/";
public const string COMMENT_LINE_TOKEN = "//";
private const int BUILDER_CAPACITY = 20000;
private char[] data;
private int index;
private PBXResolver resolver;
public PBXDictionary Decode( string data )
{
if( !data.StartsWith( PBX_HEADER_TOKEN ) ) {
Debug.Log( "Wrong file format." );
return null;
}
data = data.Substring( 13 );
this.data = data.ToCharArray();
index = 0;
return (PBXDictionary)ParseValue();
}
public string Encode( PBXDictionary pbxData, bool readable = false )
{
this.resolver = new PBXResolver( pbxData );
StringBuilder builder = new StringBuilder( PBX_HEADER_TOKEN, BUILDER_CAPACITY );
bool success = SerializeValue( pbxData, builder, readable );
this.resolver = null;
// Xcode adds newline at the end of file
builder.Append( "\n" );
return ( success ? builder.ToString() : null );
}
#region Pretty Print
private void Indent( StringBuilder builder, int deep )
{
builder.Append( "".PadLeft( deep, '\t' ) );
}
private void Endline( StringBuilder builder, bool useSpace = false )
{
builder.Append( useSpace ? " " : "\n" );
}
private string marker = null;
private void MarkSection( StringBuilder builder, string name )
{
if( marker == null && name == null ) return;
if( marker != null && name != marker )
{
builder.Append( String.Format( "/* End {0} section */\n", marker ) );
}
if( name != null && name != marker )
{
builder.Append( String.Format( "\n/* Begin {0} section */\n", name ) );
}
marker = name;
}
private bool GUIDComment( string guid, StringBuilder builder )
{
string filename = this.resolver.ResolveName( guid );
string location = this.resolver.ResolveBuildPhaseNameForFile( guid );
//Debug.Log( "RESOLVE " + guid + ": " + filename + " in " + location );
if( filename != null ) {
if( location != null ) {
//Debug.Log( "GUIDComment " + guid + " " + String.Format( " /* {0} in {1} */", filename, location ) );
builder.Append( String.Format( " /* {0} in {1} */", filename, location ) );
} else {
//Debug.Log( "GUIDComment " + guid + " " + String.Format( " /* {0} */", filename) );
builder.Append( String.Format( " /* {0} */", filename) );
}
return true;
} else {
//string other = this.resolver.ResolveConfigurationNameForFile( guid );
Debug.Log ("GUIDComment " + guid + " [no filename]");
}
return false;
}
#endregion
#region Move
private char NextToken()
{
SkipWhitespaces();
return StepForeward();
}
private string Peek( int step = 1 )
{
string sneak = string.Empty;
for( int i = 1; i <= step; i++ ) {
if( data.Length - 1 < index + i ) {
break;
}
sneak += data[ index + i ];
}
return sneak;
}
private bool SkipWhitespaces()
{
bool whitespace = false;
while( Regex.IsMatch( StepForeward().ToString(), @"\s" ) )
whitespace = true;
StepBackward();
if( SkipComments() ) {
whitespace = true;
SkipWhitespaces();
}
return whitespace;
}
private bool SkipComments()
{
string s = string.Empty;
string tag = Peek( 2 );
switch( tag ) {
case COMMENT_BEGIN_TOKEN: {
while( Peek( 2 ).CompareTo( COMMENT_END_TOKEN ) != 0 ) {
s += StepForeward();
}
s += StepForeward( 2 );
break;
}
case COMMENT_LINE_TOKEN: {
while( !Regex.IsMatch( StepForeward().ToString(), @"\n" ) )
continue;
break;
}
default:
return false;
}
return true;
}
private char StepForeward( int step = 1 )
{
index = Math.Min( data.Length, index + step );
return data[ index ];
}
private char StepBackward( int step = 1 )
{
index = Math.Max( 0, index - step );
return data[ index ];
}
#endregion
#region Parse
private object ParseValue()
{
switch( NextToken() ) {
case END_OF_FILE:
Debug.Log( "End of file" );
return null;
case DICTIONARY_BEGIN_TOKEN:
return ParseDictionary();
case ARRAY_BEGIN_TOKEN:
return ParseArray();
case QUOTEDSTRING_BEGIN_TOKEN:
return ParseString();
default:
StepBackward();
return ParseEntity();
}
}
private PBXDictionary ParseDictionary()
{
SkipWhitespaces();
PBXDictionary dictionary = new PBXDictionary();
string keyString = string.Empty;
object valueObject = null;
bool complete = false;
while( !complete ) {
switch( NextToken() ) {
case END_OF_FILE:
Debug.Log( "Error: reached end of file inside a dictionary: " + index );
complete = true;
break;
case DICTIONARY_ITEM_DELIMITER_TOKEN:
keyString = string.Empty;
valueObject = null;
break;
case DICTIONARY_END_TOKEN:
keyString = string.Empty;
valueObject = null;
complete = true;
break;
case DICTIONARY_ASSIGN_TOKEN:
valueObject = ParseValue();
if (!dictionary.ContainsKey(keyString)) {
dictionary.Add( keyString, valueObject );
}
break;
default:
StepBackward();
keyString = ParseValue() as string;
break;
}
}
return dictionary;
}
private PBXList ParseArray()
{
PBXList list = new PBXList();
bool complete = false;
while( !complete ) {
switch( NextToken() ) {
case END_OF_FILE:
Debug.Log( "Error: Reached end of file inside a list: " + list );
complete = true;
break;
case ARRAY_END_TOKEN:
complete = true;
break;
case ARRAY_ITEM_DELIMITER_TOKEN:
break;
default:
StepBackward();
list.Add( ParseValue() );
break;
}
}
return list;
}
private object ParseString()
{
string s = string.Empty;
char c = StepForeward();
while( c != QUOTEDSTRING_END_TOKEN ) {
s += c;
if( c == QUOTEDSTRING_ESCAPE_TOKEN )
s += StepForeward();
c = StepForeward();
}
return s;
}
private object ParseEntity()
{
string word = string.Empty;
while( !Regex.IsMatch( Peek(), @"[;,\s=]" ) ) {
word += StepForeward();
}
if( word.Length != 24 && Regex.IsMatch( word, @"^\d+$" ) ) {
return Int32.Parse( word );
}
return word;
}
#endregion
#region Serialize
private bool SerializeValue( object value, StringBuilder builder, bool readable = false, int indent = 0 )
{
if( value == null ) {
builder.Append( "null" );
}
else if( value is PBXObject ) {
SerializeDictionary( ((PBXObject)value).data, builder, readable, indent );
}
else if( value is Dictionary<string, object> ) {
SerializeDictionary( (Dictionary<string, object>)value, builder, readable, indent );
}
else if( value.GetType().IsArray ) {
SerializeArray( new ArrayList( (ICollection)value ), builder, readable, indent );
}
else if( value is ArrayList ) {
SerializeArray( (ArrayList)value, builder, readable, indent );
}
else if( value is string ) {
SerializeString( (string)value, builder, false, readable );
}
else if( value is Char ) {
SerializeString( Convert.ToString( (char)value ), builder, false, readable );
}
else if( value is bool ) {
builder.Append( Convert.ToInt32( value ).ToString() );
}
else if( value.GetType().IsPrimitive ) {
builder.Append( Convert.ToString( value ) );
}
else {
Debug.LogWarning( "Error: unknown object of type " + value.GetType().Name );
return false;
}
return true;
}
private bool SerializeDictionary( Dictionary<string, object> dictionary, StringBuilder builder, bool readable = false, int indent = 0 )
{
builder.Append( DICTIONARY_BEGIN_TOKEN );
if( readable ) Endline( builder );
foreach( KeyValuePair<string, object> pair in dictionary )
{
// output section banner if necessary
if( readable && indent == 1 ) MarkSection( builder, pair.Value.GetType().Name );
// indent KEY
if( readable ) Indent( builder, indent + 1 );
// KEY
SerializeString( pair.Key, builder, false, readable );
// =
// FIX ME: cannot resolve mode because readable = false for PBXBuildFile/Reference sections
builder.Append( String.Format( " {0} ", DICTIONARY_ASSIGN_TOKEN ) );
// VALUE
// do not pretty-print PBXBuildFile or PBXFileReference as Xcode does
//Debug.Log ("about to serialize " + pair.Value.GetType () + " " + pair.Value);
SerializeValue( pair.Value, builder, ( readable &&
( pair.Value.GetType() != typeof( PBXBuildFile ) ) &&
( pair.Value.GetType() != typeof( PBXFileReference ) )
), indent + 1 );
// end statement
builder.Append( DICTIONARY_ITEM_DELIMITER_TOKEN );
// FIX ME: negative readable in favor of nice output for PBXBuildFile/Reference sections
Endline( builder, !readable );
}
// output last section banner
if( readable && indent == 1 ) MarkSection( builder, null );
// indent }
if( readable ) Indent( builder, indent );
builder.Append( DICTIONARY_END_TOKEN );
return true;
}
private bool SerializeArray( ArrayList anArray, StringBuilder builder, bool readable = false, int indent = 0 )
{
builder.Append( ARRAY_BEGIN_TOKEN );
if( readable ) Endline( builder );
for( int i = 0; i < anArray.Count; i++ )
{
object value = anArray[i];
if( readable ) Indent( builder, indent + 1 );
if( !SerializeValue( value, builder, readable, indent + 1 ) )
{
return false;
}
builder.Append( ARRAY_ITEM_DELIMITER_TOKEN );
// FIX ME: negative readable in favor of nice output for PBXBuildFile/Reference sections
Endline( builder, !readable );
}
if( readable ) Indent( builder, indent );
builder.Append( ARRAY_END_TOKEN );
return true;
}
private bool SerializeString( string aString, StringBuilder builder, bool useQuotes = false, bool readable = false )
{
//Debug.Log ("SerializeString " + aString);
// Is a GUID?
// Note: Unity3d generates mixed-case GUIDs, Xcode use uppercase GUIDs only.
if( Regex.IsMatch( aString, @"^[A-Fa-f0-9]{24}$" ) ) {
builder.Append( aString );
GUIDComment( aString, builder );
return true;
}
// Is an empty string?
if( string.IsNullOrEmpty( aString ) ) {
builder.Append( QUOTEDSTRING_BEGIN_TOKEN );
builder.Append( QUOTEDSTRING_END_TOKEN );
return true;
}
// FIX ME: Original regexp was: @"^[A-Za-z0-9_.]+$", we use modified regexp with '/-' allowed
// to workaround Unity bug when all PNGs had "Libraries/" (group name) added to their paths after append
if( !Regex.IsMatch( aString, @"^[A-Za-z0-9_./-]+$" ) ) {
useQuotes = true;
}
if( useQuotes )
builder.Append( QUOTEDSTRING_BEGIN_TOKEN );
builder.Append( aString );
if( useQuotes )
builder.Append( QUOTEDSTRING_END_TOKEN );
return true;
}
#endregion
}
}
fileFormatVersion: 2
guid: cfba1f9c564074681a0f55c9eaada210
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXProject : PBXObject
{
protected string MAINGROUP_KEY = "mainGroup";
protected string KNOWN_REGIONS_KEY = "knownRegions";
protected bool _clearedLoc = false;
public PBXProject() : base() {
}
public PBXProject( string guid, PBXDictionary dictionary ) : base( guid, dictionary ) {
}
public string mainGroupID {
get {
return (string)_data[ MAINGROUP_KEY ];
}
}
public PBXList knownRegions {
get {
return (PBXList)_data[ KNOWN_REGIONS_KEY ];
}
}
public void AddRegion(string region) {
if (!_clearedLoc)
{
// Only include localizations we explicitly specify
knownRegions.Clear();
_clearedLoc = true;
}
knownRegions.Add(region);
}
}
}
fileFormatVersion: 2
guid: 8f0018d37205a4b0abb08efa4a0afd3c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXSortedDictionary : SortedDictionary<string, object>
{
public void Append( PBXDictionary dictionary )
{
foreach( var item in dictionary) {
this.Add( item.Key, item.Value );
}
}
public void Append<T>( PBXDictionary<T> dictionary ) where T : PBXObject
{
foreach( var item in dictionary) {
this.Add( item.Key, item.Value );
}
}
}
public class PBXSortedDictionary<T> : SortedDictionary<string, T> where T : PBXObject
{
public PBXSortedDictionary()
{
}
public PBXSortedDictionary( PBXDictionary genericDictionary )
{
foreach( KeyValuePair<string, object> currentItem in genericDictionary ) {
if( ((string)((PBXDictionary)currentItem.Value)[ "isa" ]).CompareTo( typeof(T).Name ) == 0 ) {
T instance = (T)System.Activator.CreateInstance( typeof(T), currentItem.Key, (PBXDictionary)currentItem.Value );
this.Add( currentItem.Key, instance );
}
}
}
public void Add( T newObject )
{
this.Add( newObject.guid, newObject );
}
public void Append( PBXDictionary<T> dictionary )
{
foreach( KeyValuePair<string, T> item in dictionary) {
this.Add( item.Key, (T)item.Value );
}
}
}
}
fileFormatVersion: 2
guid: f18f43a4db7a840d28721bc57cd91137
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class PBXVariantGroup : PBXGroup
{
#region Constructor
public PBXVariantGroup( string name, string path = null, string tree = "SOURCE_ROOT" )
: base(name, path, tree)
{
}
public PBXVariantGroup( string guid, PBXDictionary dictionary ) : base( guid, dictionary )
{
}
#endregion
}
}
fileFormatVersion: 2
guid: eff306ff024494b8cb41d590dd4e6da9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
fileFormatVersion: 2
guid: 964405d7aabe24c63b8f9719682db466
folderAsset: yes
DefaultImporter:
userData:
//
// PlistCS Property List (plist) serialization and parsing library.
//
// https://github.com/animetrics/PlistCS
//
// Copyright (c) 2011 Animetrics Inc. (marc@animetrics.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
namespace PlistCS
{
public static class Plist
{
private static List<int> offsetTable = new List<int>();
private static List<byte> objectTable = new List<byte>();
private static int refCount;
private static int objRefSize;
private static int offsetByteSize;
private static long offsetTableOffset;
#region Public Functions
public static object readPlist(string path)
{
using (FileStream f = new FileStream(path, FileMode.Open, FileAccess.Read))
{
return readPlist(f, plistType.Auto);
}
}
public static object readPlistSource(string source)
{
return readPlist(System.Text.Encoding.UTF8.GetBytes(source));
}
public static object readPlist(byte[] data)
{
return readPlist(new MemoryStream(data), plistType.Auto);
}
public static plistType getPlistType(Stream stream)
{
byte[] magicHeader = new byte[8];
stream.Read(magicHeader, 0, 8);
if (BitConverter.ToInt64(magicHeader, 0) == 3472403351741427810)
{
return plistType.Binary;
}
else
{
return plistType.Xml;
}
}
public static object readPlist(Stream stream, plistType type)
{
if (type == plistType.Auto)
{
type = getPlistType(stream);
stream.Seek(0, SeekOrigin.Begin);
}
if (type == plistType.Binary)
{
using (BinaryReader reader = new BinaryReader(stream))
{
byte[] data = reader.ReadBytes((int) reader.BaseStream.Length);
return readBinary(data);
}
}
else
{
XmlDocument xml = new XmlDocument();
xml.XmlResolver = null;
xml.Load(stream);
return readXml(xml);
}
}
public static void writeXml(object value, string path)
{
using (StreamWriter writer = new StreamWriter(path))
{
writer.Write(writeXml(value));
}
}
public static void writeXml(object value, Stream stream)
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(writeXml(value));
}
}
public static string writeXml(object value)
{
using (MemoryStream ms = new MemoryStream())
{
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = new System.Text.UTF8Encoding(false);
xmlWriterSettings.ConformanceLevel = ConformanceLevel.Document;
xmlWriterSettings.Indent = true;
using (XmlWriter xmlWriter = XmlWriter.Create(ms, xmlWriterSettings))
{
xmlWriter.WriteStartDocument();
//xmlWriter.WriteComment("DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
//xmlWriter.WriteDocType("plist", "-//Apple Computer//DTD PLIST 1.0//EN", "http://www.apple.com/DTDs/PropertyList-1.0.dtd", null);
xmlWriter.WriteStartElement("plist");
xmlWriter.WriteAttributeString("version", "1.0");
compose(value, xmlWriter);
xmlWriter.WriteEndElement();
xmlWriter.WriteEndDocument();
xmlWriter.Flush();
xmlWriter.Close();
return System.Text.Encoding.UTF8.GetString(ms.ToArray());
}
}
}
public static void writeBinary(object value, string path)
{
using (BinaryWriter writer = new BinaryWriter(new FileStream(path, FileMode.Create)))
{
writer.Write(writeBinary(value));
}
}
public static void writeBinary(object value, Stream stream)
{
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(writeBinary(value));
}
}
public static byte[] writeBinary(object value)
{
offsetTable.Clear();
objectTable.Clear();
refCount = 0;
objRefSize = 0;
offsetByteSize = 0;
offsetTableOffset = 0;
//Do not count the root node, subtract by 1
int totalRefs = countObject(value) - 1;
refCount = totalRefs;
objRefSize = RegulateNullBytes(BitConverter.GetBytes(refCount)).Length;
composeBinary(value);
writeBinaryString("bplist00", false);
offsetTableOffset = (long)objectTable.Count;
offsetTable.Add(objectTable.Count - 8);
offsetByteSize = RegulateNullBytes(BitConverter.GetBytes(offsetTable[offsetTable.Count-1])).Length;
List<byte> offsetBytes = new List<byte>();
offsetTable.Reverse();
for (int i = 0; i < offsetTable.Count; i++)
{
offsetTable[i] = objectTable.Count - offsetTable[i];
byte[] buffer = RegulateNullBytes(BitConverter.GetBytes(offsetTable[i]), offsetByteSize);
Array.Reverse(buffer);
offsetBytes.AddRange(buffer);
}
objectTable.AddRange(offsetBytes);
objectTable.AddRange(new byte[6]);
objectTable.Add(Convert.ToByte(offsetByteSize));
objectTable.Add(Convert.ToByte(objRefSize));
var a = BitConverter.GetBytes((long) totalRefs + 1);
Array.Reverse(a);
objectTable.AddRange(a);
objectTable.AddRange(BitConverter.GetBytes((long)0));
a = BitConverter.GetBytes(offsetTableOffset);
Array.Reverse(a);
objectTable.AddRange(a);
return objectTable.ToArray();
}
#endregion
#region Private Functions
private static object readXml(XmlDocument xml)
{
XmlNode rootNode = xml.DocumentElement.ChildNodes[0];
return parse(rootNode);
}
private static object readBinary(byte[] data)
{
offsetTable.Clear();
List<byte> offsetTableBytes = new List<byte>();
objectTable.Clear();
refCount = 0;
objRefSize = 0;
offsetByteSize = 0;
offsetTableOffset = 0;
List<byte> bList = new List<byte>(data);
List<byte> trailer = bList.GetRange(bList.Count - 32, 32);
parseTrailer(trailer);
objectTable = bList.GetRange(0, (int)offsetTableOffset);
offsetTableBytes = bList.GetRange((int)offsetTableOffset, bList.Count - (int)offsetTableOffset - 32);
parseOffsetTable(offsetTableBytes);
return parseBinary(0);
}
private static Dictionary<string, object> parseDictionary(XmlNode node)
{
XmlNodeList children = node.ChildNodes;
if (children.Count % 2 != 0)
{
throw new DataMisalignedException("Dictionary elements must have an even number of child nodes");
}
Dictionary<string, object> dict = new Dictionary<string, object>();
for (int i = 0; i < children.Count; i += 2)
{
XmlNode keynode = children[i];
XmlNode valnode = children[i + 1];
if (keynode.Name != "key")
{
throw new ApplicationException("expected a key node");
}
object result = parse(valnode);
if (result != null)
{
dict.Add(keynode.InnerText, result);
}
}
return dict;
}
private static List<object> parseArray(XmlNode node)
{
List<object> array = new List<object>();
foreach (XmlNode child in node.ChildNodes)
{
object result = parse(child);
if (result != null)
{
array.Add(result);
}
}
return array;
}
private static void composeArray(List<object> value, XmlWriter writer)
{
writer.WriteStartElement("array");
foreach (object obj in value)
{
compose(obj, writer);
}
writer.WriteEndElement();
}
private static object parse(XmlNode node)
{
switch (node.Name)
{
case "dict":
return parseDictionary(node);
case "array":
return parseArray(node);
case "string":
return node.InnerText;
case "integer":
// int result;
//int.TryParse(node.InnerText, System.Globalization.NumberFormatInfo.InvariantInfo, out result);
return Convert.ToInt32(node.InnerText, System.Globalization.NumberFormatInfo.InvariantInfo);
case "real":
return Convert.ToDouble(node.InnerText,System.Globalization.NumberFormatInfo.InvariantInfo);
case "false":
return false;
case "true":
return true;
case "null":
return null;
case "date":
return XmlConvert.ToDateTime(node.InnerText, XmlDateTimeSerializationMode.Utc);
case "data":
return Convert.FromBase64String(node.InnerText);
}
throw new ApplicationException(String.Format("Plist Node `{0}' is not supported", node.Name));
}
private static void compose(object value, XmlWriter writer)
{
if (value == null || value is string)
{
writer.WriteElementString("string", value as string);
}
else if (value is int || value is long)
{
writer.WriteElementString("integer", ((int)value).ToString(System.Globalization.NumberFormatInfo.InvariantInfo));
}
else if (value is System.Collections.Generic.Dictionary<string, object> ||
value.GetType().ToString().StartsWith("System.Collections.Generic.Dictionary`2[System.String"))
{
//Convert to Dictionary<string, object>
Dictionary<string, object> dic = value as Dictionary<string, object>;
if (dic == null)
{
dic = new Dictionary<string, object>();
IDictionary idic = (IDictionary)value;
foreach (var key in idic.Keys)
{
dic.Add(key.ToString(), idic[key]);
}
}
writeDictionaryValues(dic, writer);
}
else if (value is List<object>)
{
composeArray((List<object>)value, writer);
}
else if (value is byte[])
{
writer.WriteElementString("data", Convert.ToBase64String((Byte[])value));
}
else if (value is float || value is double)
{
writer.WriteElementString("real", ((double)value).ToString(System.Globalization.NumberFormatInfo.InvariantInfo));
}
else if (value is DateTime)
{
DateTime time = (DateTime)value;
string theString = XmlConvert.ToString(time, XmlDateTimeSerializationMode.Utc);
writer.WriteElementString("date", theString);//, "yyyy-MM-ddTHH:mm:ssZ"));
}
else if (value is bool)
{
writer.WriteElementString(value.ToString().ToLower(), "");
}
else
{
throw new Exception(String.Format("Value type '{0}' is unhandled", value.GetType().ToString()));
}
}
private static void writeDictionaryValues(Dictionary<string, object> dictionary, XmlWriter writer)
{
writer.WriteStartElement("dict");
foreach (string key in dictionary.Keys)
{
object value = dictionary[key];
writer.WriteElementString("key", key);
compose(value, writer);
}
writer.WriteEndElement();
}
private static int countObject(object value)
{
int count = 0;
switch (value.GetType().ToString())
{
case "System.Collections.Generic.Dictionary`2[System.String,System.Object]":
Dictionary<string, object> dict = (Dictionary<string, object>)value;
foreach (string key in dict.Keys)
{
count += countObject(dict[key]);
}
count += dict.Keys.Count;
count++;
break;
case "System.Collections.Generic.List`1[System.Object]":
List<object> list = (List<object>)value;
foreach (object obj in list)
{
count += countObject(obj);
}
count++;
break;
default:
count++;
break;
}
return count;
}
private static byte[] writeBinaryDictionary(Dictionary<string, object> dictionary)
{
List<byte> buffer = new List<byte>();
List<byte> header = new List<byte>();
List<int> refs = new List<int>();
for (int i = dictionary.Count - 1; i >= 0; i--)
{
var o = new object[dictionary.Count];
dictionary.Values.CopyTo(o, 0);
composeBinary(o[i]);
offsetTable.Add(objectTable.Count);
refs.Add(refCount);
refCount--;
}
for (int i = dictionary.Count - 1; i >= 0; i--)
{
var o = new string[dictionary.Count];
dictionary.Keys.CopyTo(o, 0);
composeBinary(o[i]);//);
offsetTable.Add(objectTable.Count);
refs.Add(refCount);
refCount--;
}
if (dictionary.Count < 15)
{
header.Add(Convert.ToByte(0xD0 | Convert.ToByte(dictionary.Count)));
}
else
{
header.Add(0xD0 | 0xf);
header.AddRange(writeBinaryInteger(dictionary.Count, false));
}
foreach (int val in refs)
{
byte[] refBuffer = RegulateNullBytes(BitConverter.GetBytes(val), objRefSize);
Array.Reverse(refBuffer);
buffer.InsertRange(0, refBuffer);
}
buffer.InsertRange(0, header);
objectTable.InsertRange(0, buffer);
return buffer.ToArray();
}
private static byte[] composeBinaryArray(List<object> objects)
{
List<byte> buffer = new List<byte>();
List<byte> header = new List<byte>();
List<int> refs = new List<int>();
for (int i = objects.Count - 1; i >= 0; i--)
{
composeBinary(objects[i]);
offsetTable.Add(objectTable.Count);
refs.Add(refCount);
refCount--;
}
if (objects.Count < 15)
{
header.Add(Convert.ToByte(0xA0 | Convert.ToByte(objects.Count)));
}
else
{
header.Add(0xA0 | 0xf);
header.AddRange(writeBinaryInteger(objects.Count, false));
}
foreach (int val in refs)
{
byte[] refBuffer = RegulateNullBytes(BitConverter.GetBytes(val), objRefSize);
Array.Reverse(refBuffer);
buffer.InsertRange(0, refBuffer);
}
buffer.InsertRange(0, header);
objectTable.InsertRange(0, buffer);
return buffer.ToArray();
}
private static byte[] composeBinary(object obj)
{
byte[] value;
switch (obj.GetType().ToString())
{
case "System.Collections.Generic.Dictionary`2[System.String,System.Object]":
value = writeBinaryDictionary((Dictionary<string, object>)obj);
return value;
case "System.Collections.Generic.List`1[System.Object]":
value = composeBinaryArray((List<object>)obj);
return value;
case "System.Byte[]":
value = writeBinaryByteArray((byte[])obj);
return value;
case "System.Double":
value = writeBinaryDouble((double)obj);
return value;
case "System.Int32":
value = writeBinaryInteger((int)obj, true);
return value;
case "System.String":
value = writeBinaryString((string)obj, true);
return value;
case "System.DateTime":
value = writeBinaryDate((DateTime)obj);
return value;
case "System.Boolean":
value = writeBinaryBool((bool)obj);
return value;
default:
return new byte[0];
}
}
public static byte[] writeBinaryDate(DateTime obj)
{
List<byte> buffer =new List<byte>(RegulateNullBytes(BitConverter.GetBytes(PlistDateConverter.ConvertToAppleTimeStamp(obj)), 8));
buffer.Reverse();
buffer.Insert(0, 0x33);
objectTable.InsertRange(0, buffer);
return buffer.ToArray();
}
public static byte[] writeBinaryBool(bool obj)
{
List<byte> buffer = new List<byte>(new byte[1] { (bool)obj ? (byte)9 : (byte)8 });
objectTable.InsertRange(0, buffer);
return buffer.ToArray();
}
private static byte[] writeBinaryInteger(int value, bool write)
{
List<byte> buffer = new List<byte>(BitConverter.GetBytes((long) value));
buffer =new List<byte>(RegulateNullBytes(buffer.ToArray()));
while (buffer.Count != Math.Pow(2, Math.Log(buffer.Count) / Math.Log(2)))
buffer.Add(0);
int header = 0x10 | (int)(Math.Log(buffer.Count) / Math.Log(2));
buffer.Reverse();
buffer.Insert(0, Convert.ToByte(header));
if (write)
objectTable.InsertRange(0, buffer);
return buffer.ToArray();
}
private static byte[] writeBinaryDouble(double value)
{
List<byte> buffer =new List<byte>(RegulateNullBytes(BitConverter.GetBytes(value), 4));
while (buffer.Count != Math.Pow(2, Math.Log(buffer.Count) / Math.Log(2)))
buffer.Add(0);
int header = 0x20 | (int)(Math.Log(buffer.Count) / Math.Log(2));
buffer.Reverse();
buffer.Insert(0, Convert.ToByte(header));
objectTable.InsertRange(0, buffer);
return buffer.ToArray();
}
private static byte[] writeBinaryByteArray(byte[] value)
{
List<byte> buffer = new List<byte>(value);
List<byte> header = new List<byte>();
if (value.Length < 15)
{
header.Add(Convert.ToByte(0x40 | Convert.ToByte(value.Length)));
}
else
{
header.Add(0x40 | 0xf);
header.AddRange(writeBinaryInteger(buffer.Count, false));
}
buffer.InsertRange(0, header);
objectTable.InsertRange(0, buffer);
return buffer.ToArray();
}
private static byte[] writeBinaryString(string value, bool head)
{
List<byte> buffer = new List<byte>();
List<byte> header = new List<byte>();
foreach (char chr in value.ToCharArray())
buffer.Add(Convert.ToByte(chr));
if (head)
{
if (value.Length < 15)
{
header.Add(Convert.ToByte(0x50 | Convert.ToByte(value.Length)));
}
else
{
header.Add(0x50 | 0xf);
header.AddRange(writeBinaryInteger(buffer.Count, false));
}
}
buffer.InsertRange(0, header);
objectTable.InsertRange(0, buffer);
return buffer.ToArray();
}
private static byte[] RegulateNullBytes(byte[] value)
{
return RegulateNullBytes(value, 1);
}
private static byte[] RegulateNullBytes(byte[] value, int minBytes)
{
Array.Reverse(value);
List<byte> bytes = new List<byte>(value);
for (int i = 0; i < bytes.Count; i++)
{
if (bytes[i] == 0 && bytes.Count > minBytes)
{
bytes.Remove(bytes[i]);
i--;
}
else
break;
}
if (bytes.Count < minBytes)
{
int dist = minBytes - bytes.Count;
for (int i = 0; i < dist; i++)
bytes.Insert(0, 0);
}
value = bytes.ToArray();
Array.Reverse(value);
return value;
}
private static void parseTrailer(List<byte> trailer)
{
offsetByteSize = BitConverter.ToInt32(RegulateNullBytes(trailer.GetRange(6, 1).ToArray(), 4), 0);
objRefSize = BitConverter.ToInt32(RegulateNullBytes(trailer.GetRange(7, 1).ToArray(), 4), 0);
byte[] refCountBytes = trailer.GetRange(12, 4).ToArray();
Array.Reverse(refCountBytes);
refCount = BitConverter.ToInt32(refCountBytes, 0);
byte[] offsetTableOffsetBytes = trailer.GetRange(24, 8).ToArray();
Array.Reverse(offsetTableOffsetBytes);
offsetTableOffset = BitConverter.ToInt64(offsetTableOffsetBytes, 0);
}
private static void parseOffsetTable(List<byte> offsetTableBytes)
{
for (int i = 0; i < offsetTableBytes.Count; i += offsetByteSize)
{
byte[] buffer = offsetTableBytes.GetRange(i, offsetByteSize).ToArray();
Array.Reverse(buffer);
offsetTable.Add(BitConverter.ToInt32(RegulateNullBytes(buffer, 4), 0));
}
}
private static object parseBinaryDictionary(int objRef)
{
Dictionary<string, object> buffer = new Dictionary<string, object>();
List<int> refs = new List<int>();
int refCount = 0;
int refStartPosition;
refCount = getCount(offsetTable[objRef], out refStartPosition);
if (refCount < 15)
refStartPosition = offsetTable[objRef] + 1;
else
refStartPosition = offsetTable[objRef] + 2 + RegulateNullBytes(BitConverter.GetBytes(refCount), 1).Length;
for (int i = refStartPosition; i < refStartPosition + refCount * 2 * objRefSize; i += objRefSize)
{
byte[] refBuffer = objectTable.GetRange(i, objRefSize).ToArray();
Array.Reverse(refBuffer);
refs.Add(BitConverter.ToInt32(RegulateNullBytes(refBuffer, 4), 0));
}
for (int i = 0; i < refCount; i++)
{
buffer.Add((string)parseBinary(refs[i]), parseBinary(refs[i + refCount]));
}
return buffer;
}
private static object parseBinaryArray(int objRef)
{
List<object> buffer = new List<object>();
List<int> refs = new List<int>();
int refCount = 0;
int refStartPosition;
refCount = getCount(offsetTable[objRef], out refStartPosition);
if (refCount < 15)
refStartPosition = offsetTable[objRef] + 1;
else
//The following integer has a header aswell so we increase the refStartPosition by two to account for that.
refStartPosition = offsetTable[objRef] + 2 + RegulateNullBytes(BitConverter.GetBytes(refCount), 1).Length;
for (int i = refStartPosition; i < refStartPosition + refCount * objRefSize; i += objRefSize)
{
byte[] refBuffer = objectTable.GetRange(i, objRefSize).ToArray();
Array.Reverse(refBuffer);
refs.Add(BitConverter.ToInt32(RegulateNullBytes(refBuffer, 4), 0));
}
for (int i = 0; i < refCount; i++)
{
buffer.Add(parseBinary(refs[i]));
}
return buffer;
}
private static int getCount(int bytePosition, out int newBytePosition)
{
byte headerByte = objectTable[bytePosition];
byte headerByteTrail = Convert.ToByte(headerByte & 0xf);
int count;
if (headerByteTrail < 15)
{
count = headerByteTrail;
newBytePosition = bytePosition + 1;
}
else
count = (int)parseBinaryInt(bytePosition + 1, out newBytePosition);
return count;
}
private static object parseBinary(int objRef)
{
byte header = objectTable[offsetTable[objRef]];
switch (header & 0xF0)
{
case 0:
{
//If the byte is
//0 return null
//9 return true
//8 return false
return (objectTable[offsetTable[objRef]] == 0) ? (object)null : ((objectTable[offsetTable[objRef]] == 9) ? true : false);
}
case 0x10:
{
return parseBinaryInt(offsetTable[objRef]);
}
case 0x20:
{
return parseBinaryReal(offsetTable[objRef]);
}
case 0x30:
{
return parseBinaryDate(offsetTable[objRef]);
}
case 0x40:
{
return parseBinaryByteArray(offsetTable[objRef]);
}
case 0x50://String ASCII
{
return parseBinaryAsciiString(offsetTable[objRef]);
}
case 0x60://String Unicode
{
return parseBinaryUnicodeString(offsetTable[objRef]);
}
case 0xD0:
{
return parseBinaryDictionary(objRef);
}
case 0xA0:
{
return parseBinaryArray(objRef);
}
}
throw new Exception("This type is not supported");
}
public static object parseBinaryDate(int headerPosition)
{
byte[] buffer = objectTable.GetRange(headerPosition + 1, 8).ToArray();
Array.Reverse(buffer);
double appleTime = BitConverter.ToDouble(buffer, 0);
DateTime result = PlistDateConverter.ConvertFromAppleTimeStamp(appleTime);
return result;
}
private static object parseBinaryInt(int headerPosition)
{
int output;
return parseBinaryInt(headerPosition, out output);
}
private static object parseBinaryInt(int headerPosition, out int newHeaderPosition)
{
byte header = objectTable[headerPosition];
int byteCount = (int)Math.Pow(2, header & 0xf);
byte[] buffer = objectTable.GetRange(headerPosition + 1, byteCount).ToArray();
Array.Reverse(buffer);
//Add one to account for the header byte
newHeaderPosition = headerPosition + byteCount + 1;
return BitConverter.ToInt32(RegulateNullBytes(buffer, 4), 0);
}
private static object parseBinaryReal(int headerPosition)
{
byte header = objectTable[headerPosition];
int byteCount = (int)Math.Pow(2, header & 0xf);
byte[] buffer = objectTable.GetRange(headerPosition + 1, byteCount).ToArray();
Array.Reverse(buffer);
return BitConverter.ToDouble(RegulateNullBytes(buffer, 8), 0);
}
private static object parseBinaryAsciiString(int headerPosition)
{
int charStartPosition;
int charCount = getCount(headerPosition, out charStartPosition);
var buffer = objectTable.GetRange(charStartPosition, charCount);
return buffer.Count > 0 ? Encoding.ASCII.GetString(buffer.ToArray()) : string.Empty;
}
private static object parseBinaryUnicodeString(int headerPosition)
{
int charStartPosition;
int charCount = getCount(headerPosition, out charStartPosition);
charCount = charCount * 2;
byte[] buffer = new byte[charCount];
byte one, two;
for (int i = 0; i < charCount; i+=2)
{
one = objectTable.GetRange(charStartPosition+i,1)[0];
two = objectTable.GetRange(charStartPosition + i+1, 1)[0];
if (BitConverter.IsLittleEndian)
{
buffer[i] = two;
buffer[i + 1] = one;
}
else
{
buffer[i] = one;
buffer[i + 1] = two;
}
}
return Encoding.Unicode.GetString(buffer);
}
private static object parseBinaryByteArray(int headerPosition)
{
int byteStartPosition;
int byteCount = getCount(headerPosition, out byteStartPosition);
return objectTable.GetRange(byteStartPosition, byteCount).ToArray();
}
#endregion
}
public enum plistType
{
Auto, Binary, Xml
}
public static class PlistDateConverter
{
public static long timeDifference = 978307200;
public static long GetAppleTime(long unixTime)
{
return unixTime - timeDifference;
}
public static long GetUnixTime(long appleTime)
{
return appleTime + timeDifference;
}
public static DateTime ConvertFromAppleTimeStamp(double timestamp)
{
DateTime origin = new DateTime(2001, 1, 1, 0, 0, 0, 0);
return origin.AddSeconds(timestamp);
}
public static double ConvertToAppleTimeStamp(DateTime date)
{
DateTime begin = new DateTime(2001, 1, 1, 0, 0, 0, 0);
TimeSpan diff = date - begin;
return Math.Floor(diff.TotalSeconds);
}
}
}
fileFormatVersion: 2
guid: aa641998e0e1d4b8594cfce2d1b0bb6d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
#XUPorter
## Overview
`XUPorter` can help you to add files and frameworks to your Xcode 4 project after it is generated by Unity 3D automatically and dramatically. Lot of changes from [XCodeEditor-for-Unity](https://github.com/dcariola/XCodeEditor-for-Unity).
中文版的说明请看[这里](http://www.onevcat.com/2012/12/xuporter/)
## Installation
Add all files of the repo to your Unity project's Editor Folder: {$ProjectFolder}/Assets/Editor/XUPorter or you can download [this unity package](https://www.dropbox.com/s/lo2wu7gur64py7a/XUPorter.unitypackage) and import to your project.
Some demos are contained in /Mods folder, they are just a tutorial to help you getting start. Please feel free to delete or substitute all files in that folder as soon as you know how to use `XUPorter`. [A simplified MiniJSON](https://github.com/prime31/UIToolkit/blob/master/Assets/Plugins/MiniJSON.cs) is now used in `XUPorter`, but I put it into a namespace, so there is no worry of conflicting, even if you are already using a same JSON wrapper.
`XUPorter` require Unity 3.5 or higher and Xcode 4 to work properly. I have tested for Xcode 5 DP and it can work well now too.
## Usage
After the Unity's building phase, OnPostProcessBuild in XCodePostProcess.cs will be called and Xcode project file will be modified. All .projmods (which is in JSON) will be treated as Xcode patch setting files (In the demos, all .projmods files are in /Mods folder). `XUPorter` will find and read all projmods files in /Assets and modify Xcode project file as settings.
In these setting files, specify the fields using a json pattern.
* group:The group name in Xcode to which files and folders will added by this projmods file
* libs:The name of libs should be added in Xcode Build Phases, libz.dylib for example. If you want to import a .a lib to Xcode, add it as a file(in "files")
* frameworks:The name of framework should be added in Xcode Build Phases, Security.framework for example. If you want to add a third party framework, add it using a relative path to `files` section instead of here.
* headerpaths:Header Search Paths in Build Setting of Xcode
* files:Files which should be added
* folders:Folders which should be added. All file and folders(recursively) will be added
* excludes:Regular expression pattern. Matched files will not be added.
* compiler_flags: Compiler flags which should be added, e.g. "-Wno-vexing-parse"
* linker_flags: Linker flags which should be added, e.g. "-ObjC"
* embed_binaries: Optional fields, support XCode 6+ `Embed Framework` feature. Notice: The frameworks must added already in `frameworks` or `files` fields.
* plist: edit the Info.plist file, only the urltype is supported currently. in url type settings, `name` and `schemes` are required, while `role` is optional and is `Editor` by default.
Use : for folders and files to add compiler flags to those files. For example: `path/to/my/folder:-fobjc-arc` or `path/to/my/file.m:-fobjc-arc`. You can add multiple compiler flags by using , to seperate them, too.
One example, the following file will add all files in /iOS/KKKeychain to the Xcode group `KKKeychain` and add the `Security.framework` to Xcode project.
```
{
"group": "KKKeychain",
"libs": [],
"frameworks": ["Security.framework"],
"headerpaths": [],
"files": [],
"folders": ["iOS/KKKeychain/"],
"excludes": ["^.*.meta$", "^.*.mdown$", "^.*.pdf$"],
"compiler_flags": [],
"linker_flags": [],
"embed_binaries": [],
"plist": {
"urltype" : [
{
"role": "Editor",
"name" : "wechat",
"schemes":["wxyz123456789"]
},
{
"name" : "twitter",
"schemes":["www1234566"]
}
],
},
}
```
### Contributor
Thanks for all contributors of this project. Especially [@pronebird](https://github.com/pronebird) and [@MatthewMaker](https://github.com/MatthewMaker)'s great effort!
## LICENSE
This code is distributed under the terms and conditions of the MIT license.
Copyright (c) 2012 Wei Wang @onevcat
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
fileFormatVersion: 2
guid: e85372c76e7b241e3b5378850b220436
DefaultImporter:
userData:
using UnityEngine;
using System.Collections;
namespace UnityEditor.XCodeEditor
{
public class XCBuildConfiguration : PBXObject
{
protected const string BUILDSETTINGS_KEY = "buildSettings";
protected const string HEADER_SEARCH_PATHS_KEY = "HEADER_SEARCH_PATHS";
protected const string LIBRARY_SEARCH_PATHS_KEY = "LIBRARY_SEARCH_PATHS";
protected const string FRAMEWORK_SEARCH_PATHS_KEY = "FRAMEWORK_SEARCH_PATHS";
protected const string OTHER_C_FLAGS_KEY = "OTHER_CFLAGS";
protected const string OTHER_LDFLAGS_KEY = "OTHER_LDFLAGS";
public XCBuildConfiguration( string guid, PBXDictionary dictionary ) : base( guid, dictionary )
{
}
public PBXSortedDictionary buildSettings {
get {
if( ContainsKey( BUILDSETTINGS_KEY ) ) {
if (_data[BUILDSETTINGS_KEY].GetType() == typeof(PBXDictionary)) {
PBXSortedDictionary ret = new PBXSortedDictionary();
ret.Append((PBXDictionary)_data[BUILDSETTINGS_KEY]);
return ret;
}
return (PBXSortedDictionary)_data[BUILDSETTINGS_KEY];
}
return null;
}
}
protected bool AddSearchPaths( string path, string key, bool recursive = true )
{
PBXList paths = new PBXList();
paths.Add( path );
return AddSearchPaths( paths, key, recursive );
}
protected bool AddSearchPaths( PBXList paths, string key, bool recursive = true, bool quoted = false ) //we want no quoting whenever we can get away with it
{
//Debug.Log ("AddSearchPaths " + paths + key + (recursive?" recursive":"") + " " + (quoted?" quoted":""));
bool modified = false;
if( !ContainsKey( BUILDSETTINGS_KEY ) )
this.Add( BUILDSETTINGS_KEY, new PBXSortedDictionary() );
foreach( string path in paths ) {
string currentPath = path;
//Debug.Log ("path " + currentPath);
if( !((PBXDictionary)_data[BUILDSETTINGS_KEY]).ContainsKey( key ) ) {
((PBXDictionary)_data[BUILDSETTINGS_KEY]).Add( key, new PBXList() );
}
else if( ((PBXDictionary)_data[BUILDSETTINGS_KEY])[key] is string ) {
PBXList list = new PBXList();
list.Add( ((PBXDictionary)_data[BUILDSETTINGS_KEY])[key] );
((PBXDictionary)_data[BUILDSETTINGS_KEY])[key] = list;
}
//Xcode uses space as the delimiter here, so if there's a space in the filename, we *must* quote. Escaping with slash may work when you are in the Xcode UI, in some situations, but it doesn't work here.
if (currentPath.Contains(@" ")) quoted = true;
if (quoted) {
//if it ends in "/**", it wants to be recursive, and the "/**" needs to be _outside_ the quotes
if (currentPath.EndsWith("/**")) {
currentPath = "\\\"" + currentPath.Replace("/**", "\\\"/**");
} else {
currentPath = "\\\"" + currentPath + "\\\"";
}
}
//Debug.Log ("currentPath = " + currentPath);
if( !((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[key]).Contains( currentPath ) ) {
((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[key]).Add( currentPath );
modified = true;
}
}
return modified;
}
public bool AddHeaderSearchPaths( PBXList paths, bool recursive = true )
{
return this.AddSearchPaths( paths, HEADER_SEARCH_PATHS_KEY, recursive );
}
public bool AddLibrarySearchPaths( PBXList paths, bool recursive = true )
{
Debug.Log ("AddLibrarySearchPaths " + paths);
return this.AddSearchPaths( paths, LIBRARY_SEARCH_PATHS_KEY, recursive );
}
public bool AddFrameworkSearchPaths( PBXList paths, bool recursive = true )
{
return this.AddSearchPaths( paths, FRAMEWORK_SEARCH_PATHS_KEY, recursive );
}
public bool AddOtherCFlags( string flag )
{
//Debug.Log( "INIZIO 1" );
PBXList flags = new PBXList();
flags.Add( flag );
return AddOtherCFlags( flags );
}
public bool AddOtherCFlags( PBXList flags )
{
//Debug.Log( "INIZIO 2" );
bool modified = false;
if( !ContainsKey( BUILDSETTINGS_KEY ) )
this.Add( BUILDSETTINGS_KEY, new PBXSortedDictionary() );
foreach( string flag in flags ) {
if( !((PBXDictionary)_data[BUILDSETTINGS_KEY]).ContainsKey( OTHER_C_FLAGS_KEY ) ) {
((PBXDictionary)_data[BUILDSETTINGS_KEY]).Add( OTHER_C_FLAGS_KEY, new PBXList() );
}
else if ( ((PBXDictionary)_data[BUILDSETTINGS_KEY])[ OTHER_C_FLAGS_KEY ] is string ) {
string tempString = (string)((PBXDictionary)_data[BUILDSETTINGS_KEY])[OTHER_C_FLAGS_KEY];
((PBXDictionary)_data[BUILDSETTINGS_KEY])[ OTHER_C_FLAGS_KEY ] = new PBXList();
((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[OTHER_C_FLAGS_KEY]).Add( tempString );
}
if( !((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[OTHER_C_FLAGS_KEY]).Contains( flag ) ) {
((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[OTHER_C_FLAGS_KEY]).Add( flag );
modified = true;
}
}
return modified;
}
public bool AddOtherLinkerFlags( string flag )
{
PBXList flags = new PBXList();
flags.Add( flag );
return AddOtherLinkerFlags( flags );
}
public bool AddOtherLinkerFlags( PBXList flags )
{
bool modified = false;
if( !ContainsKey( BUILDSETTINGS_KEY ) )
this.Add( BUILDSETTINGS_KEY, new PBXSortedDictionary() );
foreach( string flag in flags ) {
if( !((PBXDictionary)_data[BUILDSETTINGS_KEY]).ContainsKey( OTHER_LDFLAGS_KEY ) ) {
((PBXDictionary)_data[BUILDSETTINGS_KEY]).Add( OTHER_LDFLAGS_KEY, new PBXList() );
}
else if ( ((PBXDictionary)_data[BUILDSETTINGS_KEY])[ OTHER_LDFLAGS_KEY ] is string ) {
string tempString = (string)((PBXDictionary)_data[BUILDSETTINGS_KEY])[OTHER_LDFLAGS_KEY];
((PBXDictionary)_data[BUILDSETTINGS_KEY])[ OTHER_LDFLAGS_KEY ] = new PBXList();
if( !string.IsNullOrEmpty(tempString) ) {
((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[OTHER_LDFLAGS_KEY]).Add( tempString );
}
}
if( !((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[OTHER_LDFLAGS_KEY]).Contains( flag ) ) {
((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[OTHER_LDFLAGS_KEY]).Add( flag );
modified = true;
}
}
return modified;
}
public bool overwriteBuildSetting(string settingName, string settingValue) {
Debug.Log ("overwriteBuildSetting " + settingName + " " + settingValue);
bool modified = false;
if( !ContainsKey( BUILDSETTINGS_KEY ) ) {
Debug.Log ("creating key " + BUILDSETTINGS_KEY);
this.Add( BUILDSETTINGS_KEY, new PBXSortedDictionary() );
}
if( !((PBXDictionary)_data[BUILDSETTINGS_KEY]).ContainsKey( settingName ) ) {
Debug.Log("adding key " + settingName);
((PBXDictionary)_data[BUILDSETTINGS_KEY]).Add( settingName, new PBXList() );
}
else if ( ((PBXDictionary)_data[BUILDSETTINGS_KEY])[ settingName ] is string ) {
//Debug.Log("key is string:" + settingName);
//string tempString = (string)((PBXDictionary)_data[BUILDSETTINGS_KEY])[settingName];
((PBXDictionary)_data[BUILDSETTINGS_KEY])[ settingName ] = new PBXList();
//((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[settingName]).Add( tempString );
}
if( !((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[settingName]).Contains( settingValue ) ) {
Debug.Log("setting " + settingName + " to " + settingValue);
((PBXList)((PBXDictionary)_data[BUILDSETTINGS_KEY])[settingName]).Add( settingValue );
modified = true;
}
return modified;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 189232aeaab4d4a90b9fe88d7259b672
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace UnityEditor.XCodeEditor
{
public class XCConfigurationList : PBXObject
{
// XCBuildConfigurationList buildConfigurations;
// bool defaultConfigurationIsVisible = false;
// string defaultConfigurationName;
public XCConfigurationList( string guid, PBXDictionary dictionary ) : base( guid, dictionary ) {
}
}
}
fileFormatVersion: 2
guid: 4e44773a3df3045898aeb2f515f4dfa7
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.IO;
namespace UnityEditor.XCodeEditor
{
public class XCMod
{
private Hashtable _datastore = new Hashtable();
private ArrayList _libs = null;
public string name { get; private set; }
public string path { get; private set; }
public string group {
get {
if (_datastore != null && _datastore.Contains("group"))
return (string)_datastore["group"];
return string.Empty;
}
}
public ArrayList patches {
get {
return (ArrayList)_datastore["patches"];
}
}
public ArrayList libs {
get {
if( _libs == null ) {
_libs = new ArrayList( ((ArrayList)_datastore["libs"]).Count );
foreach( string fileRef in (ArrayList)_datastore["libs"] ) {
Debug.Log("Adding to Libs: "+fileRef);
_libs.Add( new XCModFile( fileRef ) );
}
}
return _libs;
}
}
public ArrayList frameworks {
get {
return (ArrayList)_datastore["frameworks"];
}
}
public ArrayList headerpaths {
get {
return (ArrayList)_datastore["headerpaths"];
}
}
public ArrayList files {
get {
return (ArrayList)_datastore["files"];
}
}
public ArrayList folders {
get {
return (ArrayList)_datastore["folders"];
}
}
public ArrayList excludes {
get {
return (ArrayList)_datastore["excludes"];
}
}
public ArrayList compiler_flags {
get {
return (ArrayList)_datastore["compiler_flags"];
}
}
public ArrayList linker_flags {
get {
return (ArrayList)_datastore["linker_flags"];
}
}
public ArrayList embed_binaries {
get {
return (ArrayList)_datastore["embed_binaries"];
}
}
public Hashtable plist {
get {
return (Hashtable)_datastore["plist"];
}
}
public XCMod( string filename )
{
FileInfo projectFileInfo = new FileInfo( filename );
if( !projectFileInfo.Exists ) {
Debug.LogWarning( "File does not exist." );
}
name = System.IO.Path.GetFileNameWithoutExtension( filename );
path = System.IO.Path.GetDirectoryName( filename );
string contents = projectFileInfo.OpenText().ReadToEnd();
Debug.Log (contents);
_datastore = (Hashtable)XUPorterJSON.MiniJSON.jsonDecode( contents );
if (_datastore == null || _datastore.Count == 0) {
Debug.Log (contents);
throw new UnityException("Parse error in file " + System.IO.Path.GetFileName(filename) + "! Check for typos such as unbalanced quotation marks, etc.");
}
}
}
public class XCModFile
{
public string filePath { get; private set; }
public bool isWeak { get; private set; }
public XCModFile( string inputString )
{
isWeak = false;
if( inputString.Contains( ":" ) ) {
string[] parts = inputString.Split( ':' );
filePath = parts[0];
isWeak = ( parts[1].CompareTo( "weak" ) == 0 );
}
else {
filePath = inputString;
}
}
}
}
fileFormatVersion: 2
guid: d3f1c78985b914f54b218977a3d51933
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace UnityEditor.XCodeEditor
{
public class XCPlist
{
string plistPath;
bool plistModified;
// URLTypes constant --- plist
const string BundleUrlTypes = "CFBundleURLTypes";
const string BundleTypeRole = "CFBundleTypeRole";
const string BundleUrlName = "CFBundleURLName";
const string BundleUrlSchemes = "CFBundleURLSchemes";
// URLTypes constant --- projmods
const string PlistUrlType = "urltype";
const string PlistRole = "role";
const string PlistEditor = "Editor";
const string PlistName = "name";
const string PlistSchemes = "schemes";
public XCPlist(string plistPath)
{
this.plistPath = plistPath;
}
public void Process(Hashtable plist)
{
if (plist == null) return;
Dictionary<string, object> dict = (Dictionary<string, object>)PlistCS.Plist.readPlist(plistPath);
foreach( DictionaryEntry entry in plist)
{
this.AddPlistItems((string)entry.Key, entry.Value, dict);
}
if (plistModified)
{
PlistCS.Plist.writeXml(dict, plistPath);
}
}
// http://stackoverflow.com/questions/20618809/hashtable-to-dictionary
public static Dictionary<K,V> HashtableToDictionary<K,V> (Hashtable table)
{
Dictionary<K,V> dict = new Dictionary<K,V>();
foreach(DictionaryEntry kvp in table)
dict.Add((K)kvp.Key, (V)kvp.Value);
return dict;
}
public void AddPlistItems(string key, object value, Dictionary<string, object> dict)
{
Debug.Log ("AddPlistItems: key=" + key);
if (key.CompareTo(PlistUrlType) == 0)
{
processUrlTypes((ArrayList)value, dict);
}
else
{
dict[key] = HashtableToDictionary<string, object>((Hashtable)value);
plistModified = true;
}
}
private void processUrlTypes(ArrayList urltypes, Dictionary<string, object> dict)
{
List<object> bundleUrlTypes;
if (dict.ContainsKey(BundleUrlTypes))
{
bundleUrlTypes = (List<object>)dict[BundleUrlTypes];
}
else
{
bundleUrlTypes = new List<object>();
}
foreach(Hashtable table in urltypes)
{
string role = (string)table[PlistRole];
if (string.IsNullOrEmpty(role))
{
role = PlistEditor;
}
string name = (string)table[PlistName];
ArrayList shcemes = (ArrayList)table[PlistSchemes];
// new schemes
List<object> urlTypeSchemes = new List<object>();
foreach(string s in shcemes)
{
urlTypeSchemes.Add(s);
}
Dictionary<string, object> urlTypeDict = this.findUrlTypeByName(bundleUrlTypes, name);
if (urlTypeDict == null)
{
urlTypeDict = new Dictionary<string, object>();
urlTypeDict[BundleTypeRole] = role;
urlTypeDict[BundleUrlName] = name;
urlTypeDict[BundleUrlSchemes] = urlTypeSchemes;
bundleUrlTypes.Add(urlTypeDict);
}
else
{
urlTypeDict[BundleTypeRole] = role;
urlTypeDict[BundleUrlSchemes] = urlTypeSchemes;
}
plistModified = true;
}
dict[BundleUrlTypes] = bundleUrlTypes;
}
private Dictionary<string, object> findUrlTypeByName(List<object> bundleUrlTypes, string name)
{
if ((bundleUrlTypes == null) || (bundleUrlTypes.Count == 0))
return null;
foreach(Dictionary<string, object> dict in bundleUrlTypes)
{
string _n = (string)dict[BundleUrlName];
if (string.Compare(_n, name) == 0)
{
return dict;
}
}
return null;
}
}
}
fileFormatVersion: 2
guid: 1d139ac4b68374224a37243fb4dd87fc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace UnityEditor.XCodeEditor
{
public partial class XCProject : System.IDisposable
{
private PBXDictionary _datastore;
public PBXDictionary _objects;
//private PBXDictionary _configurations;
private PBXGroup _rootGroup;
//private string _defaultConfigurationName;
private string _rootObjectKey;
public string projectRootPath { get; private set; }
private FileInfo projectFileInfo;
public string filePath { get; private set; }
//private string sourcePathRoot;
private bool modified = false;
#region Data
// Objects
private PBXSortedDictionary<PBXBuildFile> _buildFiles;
private PBXSortedDictionary<PBXGroup> _groups;
private PBXSortedDictionary<PBXFileReference> _fileReferences;
private PBXDictionary<PBXNativeTarget> _nativeTargets;
private PBXDictionary<PBXFrameworksBuildPhase> _frameworkBuildPhases;
private PBXDictionary<PBXResourcesBuildPhase> _resourcesBuildPhases;
private PBXDictionary<PBXShellScriptBuildPhase> _shellScriptBuildPhases;
private PBXDictionary<PBXSourcesBuildPhase> _sourcesBuildPhases;
private PBXDictionary<PBXCopyFilesBuildPhase> _copyBuildPhases;
private PBXDictionary<PBXVariantGroup> _variantGroups;
private PBXDictionary<XCBuildConfiguration> _buildConfigurations;
private PBXSortedDictionary<XCConfigurationList> _configurationLists;
private PBXProject _project;
#endregion
#region Constructor
public XCProject()
{
}
public XCProject( string filePath ) : this()
{
if( !System.IO.Directory.Exists( filePath ) ) {
Debug.LogWarning( "XCode project path does not exist: " + filePath );
return;
}
if( filePath.EndsWith( ".xcodeproj" ) ) {
Debug.Log( "Opening project " + filePath );
this.projectRootPath = Path.GetDirectoryName( filePath );
this.filePath = filePath;
} else {
Debug.Log( "Looking for xcodeproj files in " + filePath );
string[] projects = System.IO.Directory.GetDirectories( filePath, "*.xcodeproj" );
if( projects.Length == 0 ) {
Debug.LogWarning( "Error: missing xcodeproj file" );
return;
}
this.projectRootPath = filePath;
//if the path is relative to the project, we need to make it absolute
if (!System.IO.Path.IsPathRooted(projectRootPath))
projectRootPath = Application.dataPath.Replace("Assets", "") + projectRootPath;
//Debug.Log ("projectRootPath adjusted to " + projectRootPath);
this.filePath = projects[ 0 ];
}
projectFileInfo = new FileInfo( Path.Combine( this.filePath, "project.pbxproj" ) );
string contents = projectFileInfo.OpenText().ReadToEnd();
PBXParser parser = new PBXParser();
_datastore = parser.Decode( contents );
if( _datastore == null ) {
throw new System.Exception( "Project file not found at file path " + filePath );
}
if( !_datastore.ContainsKey( "objects" ) ) {
Debug.Log( "Errore " + _datastore.Count );
return;
}
_objects = (PBXDictionary)_datastore["objects"];
modified = false;
_rootObjectKey = (string)_datastore["rootObject"];
if( !string.IsNullOrEmpty( _rootObjectKey ) ) {
_project = new PBXProject( _rootObjectKey, (PBXDictionary)_objects[ _rootObjectKey ] );
_rootGroup = new PBXGroup( _rootObjectKey, (PBXDictionary)_objects[ _project.mainGroupID ] );
}
else {
Debug.LogWarning( "error: project has no root object" );
_project = null;
_rootGroup = null;
}
}
#endregion
#region Properties
public PBXProject project {
get {
return _project;
}
}
public PBXGroup rootGroup {
get {
return _rootGroup;
}
}
public PBXSortedDictionary<PBXBuildFile> buildFiles {
get {
if( _buildFiles == null ) {
_buildFiles = new PBXSortedDictionary<PBXBuildFile>( _objects );
}
return _buildFiles;
}
}
public PBXSortedDictionary<PBXGroup> groups {
get {
if( _groups == null ) {
_groups = new PBXSortedDictionary<PBXGroup>( _objects );
}
return _groups;
}
}
public PBXSortedDictionary<PBXFileReference> fileReferences {
get {
if( _fileReferences == null ) {
_fileReferences = new PBXSortedDictionary<PBXFileReference>( _objects );
}
return _fileReferences;
}
}
public PBXDictionary<PBXVariantGroup> variantGroups {
get {
if( _variantGroups == null ) {
_variantGroups = new PBXDictionary<PBXVariantGroup>( _objects );
}
return _variantGroups;
}
}
public PBXDictionary<PBXNativeTarget> nativeTargets {
get {
if( _nativeTargets == null ) {
_nativeTargets = new PBXDictionary<PBXNativeTarget>( _objects );
}
return _nativeTargets;
}
}
public PBXDictionary<XCBuildConfiguration> buildConfigurations {
get {
if( _buildConfigurations == null ) {
_buildConfigurations = new PBXDictionary<XCBuildConfiguration>( _objects );
}
return _buildConfigurations;
}
}
public PBXSortedDictionary<XCConfigurationList> configurationLists {
get {
if( _configurationLists == null ) {
_configurationLists = new PBXSortedDictionary<XCConfigurationList>( _objects );
}
return _configurationLists;
}
}
public PBXDictionary<PBXFrameworksBuildPhase> frameworkBuildPhases {
get {
if( _frameworkBuildPhases == null ) {
_frameworkBuildPhases = new PBXDictionary<PBXFrameworksBuildPhase>( _objects );
}
return _frameworkBuildPhases;
}
}
public PBXDictionary<PBXResourcesBuildPhase> resourcesBuildPhases {
get {
if( _resourcesBuildPhases == null ) {
_resourcesBuildPhases = new PBXDictionary<PBXResourcesBuildPhase>( _objects );
}
return _resourcesBuildPhases;
}
}
public PBXDictionary<PBXShellScriptBuildPhase> shellScriptBuildPhases {
get {
if( _shellScriptBuildPhases == null ) {
_shellScriptBuildPhases = new PBXDictionary<PBXShellScriptBuildPhase>( _objects );
}
return _shellScriptBuildPhases;
}
}
public PBXDictionary<PBXSourcesBuildPhase> sourcesBuildPhases {
get {
if( _sourcesBuildPhases == null ) {
_sourcesBuildPhases = new PBXDictionary<PBXSourcesBuildPhase>( _objects );
}
return _sourcesBuildPhases;
}
}
public PBXDictionary<PBXCopyFilesBuildPhase> copyBuildPhases {
get {
if( _copyBuildPhases == null ) {
_copyBuildPhases = new PBXDictionary<PBXCopyFilesBuildPhase>( _objects );
}
return _copyBuildPhases;
}
}
#endregion
#region PBXMOD
public bool AddOtherCFlags( string flag )
{
return AddOtherCFlags( new PBXList( flag ) );
}
public bool AddOtherCFlags( PBXList flags )
{
foreach( KeyValuePair<string, XCBuildConfiguration> buildConfig in buildConfigurations ) {
buildConfig.Value.AddOtherCFlags( flags );
}
modified = true;
return modified;
}
public bool AddOtherLinkerFlagsWithForceLoad(string absoluteFilePath){
if (!File.Exists(absoluteFilePath)) {
return false;
}
AddOtherLinkerFlags ("-force_load");
System.Uri fileURI = new System.Uri( absoluteFilePath );
System.Uri rootURI = new System.Uri( ( projectRootPath + "/." ) );
string relativeFilePath = rootURI.MakeRelativeUri( fileURI ).ToString();
return AddOtherLinkerFlags (relativeFilePath);
}
public bool AddOtherLinkerFlags( string flag )
{
return AddOtherLinkerFlags( new PBXList( flag ) );
}
public bool AddOtherLinkerFlags( PBXList flags )
{
foreach( KeyValuePair<string, XCBuildConfiguration> buildConfig in buildConfigurations ) {
buildConfig.Value.AddOtherLinkerFlags( flags );
}
modified = true;
return modified;
}
public bool overwriteBuildSetting( string settingName, string newValue, string buildConfigName = "all") {
Debug.Log("overwriteBuildSetting " + settingName + " " + newValue + " " + buildConfigName);
foreach( KeyValuePair<string, XCBuildConfiguration> buildConfig in buildConfigurations ) {
//Debug.Log ("build config " + buildConfig);
XCBuildConfiguration b = buildConfig.Value;
if ( (string)b.data["name"] == buildConfigName || (string)buildConfigName == "all") {
//Debug.Log ("found " + b.data["name"] + " config");
buildConfig.Value.overwriteBuildSetting(settingName, newValue);
modified = true;
} else {
//Debug.LogWarning ("skipping " + buildConfigName + " config " + (string)b.data["name"]);
}
}
return modified;
}
public bool AddHeaderSearchPaths( string path )
{
return AddHeaderSearchPaths( new PBXList( path ) );
}
public bool AddHeaderSearchPaths( PBXList paths )
{
Debug.Log ("AddHeaderSearchPaths " + paths);
foreach( KeyValuePair<string, XCBuildConfiguration> buildConfig in buildConfigurations ) {
buildConfig.Value.AddHeaderSearchPaths( paths );
}
modified = true;
return modified;
}
public bool AddLibrarySearchPaths( string path )
{
return AddLibrarySearchPaths( new PBXList( path ) );
}
public bool AddLibrarySearchPaths( PBXList paths )
{
Debug.Log ("AddLibrarySearchPaths " + paths);
foreach( KeyValuePair<string, XCBuildConfiguration> buildConfig in buildConfigurations ) {
buildConfig.Value.AddLibrarySearchPaths( paths );
}
modified = true;
return modified;
}
public bool AddFrameworkSearchPaths( string path )
{
return AddFrameworkSearchPaths( new PBXList( path ) );
}
public bool AddFrameworkSearchPaths( PBXList paths )
{
foreach( KeyValuePair<string, XCBuildConfiguration> buildConfig in buildConfigurations ) {
buildConfig.Value.AddFrameworkSearchPaths( paths );
}
modified = true;
return modified;
}
public object GetObject( string guid )
{
return _objects[guid];
}
public PBXDictionary AddFile( string filePath, PBXGroup parent = null, string tree = "SOURCE_ROOT", bool createBuildFiles = true, bool weak = false )
{
//Debug.Log("AddFile " + filePath + ", " + parent + ", " + tree + ", " + (createBuildFiles? "TRUE":"FALSE") + ", " + (weak? "TRUE":"FALSE") );
PBXDictionary results = new PBXDictionary();
if (filePath == null) {
Debug.LogError ("AddFile called with null filePath");
return results;
}
string absPath = string.Empty;
if( Path.IsPathRooted( filePath ) ) {
Debug.Log( "Path is Rooted" );
absPath = filePath;
}
else if( tree.CompareTo( "SDKROOT" ) != 0) {
absPath = Path.Combine( Application.dataPath, filePath );
}
if( !( File.Exists( absPath ) || Directory.Exists( absPath ) ) && tree.CompareTo( "SDKROOT" ) != 0 ) {
Debug.Log( "Missing file: " + filePath );
return results;
}
else if( tree.CompareTo( "SOURCE_ROOT" ) == 0 ) {
Debug.Log( "Source Root File" );
System.Uri fileURI = new System.Uri( absPath );
System.Uri rootURI = new System.Uri( ( projectRootPath + "/." ) );
filePath = rootURI.MakeRelativeUri( fileURI ).ToString();
}
else if( tree.CompareTo("GROUP") == 0) {
Debug.Log( "Group File" );
filePath = System.IO.Path.GetFileName( filePath );
}
if( parent == null ) {
parent = _rootGroup;
}
//Check if there is already a file
PBXFileReference fileReference = GetFile( System.IO.Path.GetFileName( filePath ) );
if( fileReference != null ) {
Debug.Log("File already exists: " + filePath); //not a warning, because this is normal for most builds!
return null;
}
fileReference = new PBXFileReference( filePath, (TreeEnum)System.Enum.Parse( typeof(TreeEnum), tree ) );
parent.AddChild( fileReference );
fileReferences.Add( fileReference );
results.Add( fileReference.guid, fileReference );
//Create a build file for reference
if( !string.IsNullOrEmpty( fileReference.buildPhase ) && createBuildFiles ) {
switch( fileReference.buildPhase ) {
case "PBXFrameworksBuildPhase":
foreach( KeyValuePair<string, PBXFrameworksBuildPhase> currentObject in frameworkBuildPhases ) {
BuildAddFile(fileReference,currentObject,weak);
}
if ( !string.IsNullOrEmpty( absPath ) && ( tree.CompareTo( "SOURCE_ROOT" ) == 0 )) {
string libraryPath = Path.Combine( "$(SRCROOT)", Path.GetDirectoryName( filePath ) );
if (File.Exists(absPath)) {
this.AddLibrarySearchPaths( new PBXList( libraryPath ) );
} else {
this.AddFrameworkSearchPaths( new PBXList( libraryPath ) );
}
}
break;
case "PBXResourcesBuildPhase":
foreach( KeyValuePair<string, PBXResourcesBuildPhase> currentObject in resourcesBuildPhases ) {
Debug.Log( "Adding Resources Build File" );
BuildAddFile(fileReference,currentObject,weak);
}
break;
case "PBXShellScriptBuildPhase":
foreach( KeyValuePair<string, PBXShellScriptBuildPhase> currentObject in shellScriptBuildPhases ) {
Debug.Log( "Adding Script Build File" );
BuildAddFile(fileReference,currentObject,weak);
}
break;
case "PBXSourcesBuildPhase":
foreach( KeyValuePair<string, PBXSourcesBuildPhase> currentObject in sourcesBuildPhases ) {
Debug.Log( "Adding Source Build File" );
BuildAddFile(fileReference,currentObject,weak);
}
break;
case "PBXCopyFilesBuildPhase":
foreach( KeyValuePair<string, PBXCopyFilesBuildPhase> currentObject in copyBuildPhases ) {
Debug.Log( "Adding Copy Files Build Phase" );
BuildAddFile(fileReference,currentObject,weak);
}
break;
case null:
Debug.LogWarning( "File Not Supported: " + filePath );
break;
default:
Debug.LogWarning( "File Not Supported." );
return null;
}
}
return results;
}
public PBXNativeTarget GetNativeTarget( string name )
{
PBXNativeTarget naviTarget = null;
foreach( KeyValuePair<string, PBXNativeTarget> currentObject in nativeTargets ) {
string targetName = (string)currentObject.Value.data["name"];
if (targetName == name) {
naviTarget = currentObject.Value;
break;
}
}
return naviTarget;
}
public int GetBuildActionMask()
{
int buildActionMask = 0;
foreach( var currentObject in copyBuildPhases )
{
buildActionMask = (int)currentObject.Value.data["buildActionMask"];
break;
}
return buildActionMask;
}
public PBXCopyFilesBuildPhase AddEmbedFrameworkBuildPhase()
{
PBXCopyFilesBuildPhase phase = null;
PBXNativeTarget naviTarget = GetNativeTarget("Unity-iPhone");
if (naviTarget == null)
{
Debug.Log("Not found Correct NativeTarget.");
return phase;
}
//check if embed framework buildPhase exist
foreach( var currentObject in copyBuildPhases )
{
object nameObj = null;
if (currentObject.Value.data.TryGetValue("name", out nameObj))
{
string name = (string)nameObj;
if (name == "Embed Frameworks")
return currentObject.Value;
}
}
int buildActionMask = this.GetBuildActionMask();
phase = new PBXCopyFilesBuildPhase(buildActionMask);
var buildPhases = (ArrayList)naviTarget.data["buildPhases"];
buildPhases.Add(phase.guid);//add build phase
copyBuildPhases.Add(phase);
return phase;
}
public void AddEmbedFramework( string fileName)
{
Debug.Log( "Add Embed Framework: " + fileName );
//Check if there is already a file
PBXFileReference fileReference = GetFile( System.IO.Path.GetFileName( fileName ) );
if( fileReference == null ) {
Debug.Log("Embed Framework must added already: " + fileName);
return;
}
var embedPhase = this.AddEmbedFrameworkBuildPhase();
if (embedPhase == null)
{
Debug.Log("AddEmbedFrameworkBuildPhase Failed.");
return;
}
//create a build file
PBXBuildFile buildFile = new PBXBuildFile( fileReference );
buildFile.AddCodeSignOnCopy();
buildFiles.Add( buildFile );
embedPhase.AddBuildFile(buildFile);
}
private void BuildAddFile (PBXFileReference fileReference, KeyValuePair<string, PBXFrameworksBuildPhase> currentObject,bool weak)
{
PBXBuildFile buildFile = new PBXBuildFile( fileReference, weak );
buildFiles.Add( buildFile );
currentObject.Value.AddBuildFile( buildFile );
}
private void BuildAddFile (PBXFileReference fileReference, KeyValuePair<string, PBXResourcesBuildPhase> currentObject,bool weak)
{
PBXBuildFile buildFile = new PBXBuildFile( fileReference, weak );
buildFiles.Add( buildFile );
currentObject.Value.AddBuildFile( buildFile );
}
private void BuildAddFile (PBXFileReference fileReference, KeyValuePair<string, PBXShellScriptBuildPhase> currentObject,bool weak)
{
PBXBuildFile buildFile = new PBXBuildFile( fileReference, weak );
buildFiles.Add( buildFile );
currentObject.Value.AddBuildFile( buildFile );
}
private void BuildAddFile (PBXFileReference fileReference, KeyValuePair<string, PBXSourcesBuildPhase> currentObject,bool weak)
{
PBXBuildFile buildFile = new PBXBuildFile( fileReference, weak );
buildFiles.Add( buildFile );
currentObject.Value.AddBuildFile( buildFile );
}
private void BuildAddFile (PBXFileReference fileReference, KeyValuePair<string, PBXCopyFilesBuildPhase> currentObject,bool weak)
{
PBXBuildFile buildFile = new PBXBuildFile( fileReference, weak );
buildFiles.Add( buildFile );
currentObject.Value.AddBuildFile( buildFile );
}
public bool AddFolder( string folderPath, PBXGroup parent = null, string[] exclude = null, bool recursive = true, bool createBuildFile = true )
{
Debug.Log("Folder PATH: "+folderPath);
if( !Directory.Exists( folderPath ) ){
Debug.Log("Directory doesn't exist?");
return false;
}
if (folderPath.EndsWith(".lproj")){
Debug.Log("Ended with .lproj");
return AddLocFolder(folderPath, parent, exclude, createBuildFile);
}
DirectoryInfo sourceDirectoryInfo = new DirectoryInfo( folderPath );
if( exclude == null ){
Debug.Log("Exclude was null");
exclude = new string[] {};
}
if( parent == null ){
Debug.Log("Parent was null");
parent = rootGroup;
}
// Create group
PBXGroup newGroup = GetGroup( sourceDirectoryInfo.Name, null /*relative path*/, parent );
Debug.Log("New Group created");
foreach( string directory in Directory.GetDirectories( folderPath ) ) {
Debug.Log( "DIR: " + directory );
if( directory.EndsWith( ".bundle" ) ) {
// Treat it like a file and copy even if not recursive
// TODO also for .xcdatamodeld?
Debug.LogWarning( "This is a special folder: " + directory );
AddFile( directory, newGroup, "SOURCE_ROOT", createBuildFile );
continue;
}
if( recursive ) {
Debug.Log( "recursive" );
AddFolder( directory, newGroup, exclude, recursive, createBuildFile );
}
}
// Adding files.
string regexExclude = string.Format( @"{0}", string.Join( "|", exclude ) );
foreach( string file in Directory.GetFiles( folderPath ) ) {
if( Regex.IsMatch( file, regexExclude ) ) {
continue;
}
Debug.Log("Adding Files for Folder");
AddFile( file, newGroup, "SOURCE_ROOT", createBuildFile );
}
modified = true;
return modified;
}
// We support neither recursing into nor bundles contained inside loc folders
public bool AddLocFolder( string folderPath, PBXGroup parent = null, string[] exclude = null, bool createBuildFile = true)
{
DirectoryInfo sourceDirectoryInfo = new DirectoryInfo( folderPath );
if( exclude == null )
exclude = new string[] {};
if( parent == null )
parent = rootGroup;
// Create group as needed
System.Uri projectFolderURI = new System.Uri( projectFileInfo.DirectoryName );
System.Uri locFolderURI = new System.Uri( folderPath );
var relativePath = projectFolderURI.MakeRelativeUri( locFolderURI ).ToString();
PBXGroup newGroup = GetGroup( sourceDirectoryInfo.Name, relativePath, parent );
// Add loc region to project
string nom = sourceDirectoryInfo.Name;
string region = nom.Substring(0, nom.Length - ".lproj".Length);
project.AddRegion(region);
// Adding files.
string regexExclude = string.Format( @"{0}", string.Join( "|", exclude ) );
foreach( string file in Directory.GetFiles( folderPath ) ) {
if( Regex.IsMatch( file, regexExclude ) ) {
continue;
}
// Add a variant group for the language as well
var variant = new PBXVariantGroup(System.IO.Path.GetFileName( file ), null, "GROUP");
variantGroups.Add(variant);
// The group gets a reference to the variant, not to the file itself
newGroup.AddChild(variant);
AddFile( file, variant, "GROUP", createBuildFile );
}
modified = true;
return modified;
}
#endregion
#region Getters
public PBXFileReference GetFile( string name )
{
if( string.IsNullOrEmpty( name ) ) {
return null;
}
foreach( KeyValuePair<string, PBXFileReference> current in fileReferences ) {
if( !string.IsNullOrEmpty( current.Value.name ) && current.Value.name.CompareTo( name ) == 0 ) {
return current.Value;
}
}
return null;
}
public PBXGroup GetGroup( string name, string path = null, PBXGroup parent = null )
{
if( string.IsNullOrEmpty( name ) )
return null;
if( parent == null ) parent = rootGroup;
foreach( KeyValuePair<string, PBXGroup> current in groups ) {
if( string.IsNullOrEmpty( current.Value.name ) ) {
if( current.Value.path.CompareTo( name ) == 0 && parent.HasChild( current.Key ) ) {
return current.Value;
}
} else if( current.Value.name.CompareTo( name ) == 0 && parent.HasChild( current.Key ) ) {
return current.Value;
}
}
PBXGroup result = new PBXGroup( name, path );
groups.Add( result );
parent.AddChild( result );
modified = true;
return result;
}
#endregion
#region Mods
public void ApplyMod( string pbxmod )
{
XCMod mod = new XCMod( pbxmod );
foreach(var lib in mod.libs){
Debug.Log("Library: "+lib);
}
ApplyMod( mod );
}
public void ApplyMod( XCMod mod )
{
PBXGroup modGroup = this.GetGroup( mod.group );
Debug.Log( "Adding libraries..." );
foreach( XCModFile libRef in mod.libs ) {
string completeLibPath = System.IO.Path.Combine( "usr/lib", libRef.filePath );
Debug.Log ("Adding library " + completeLibPath);
this.AddFile( completeLibPath, modGroup, "SDKROOT", true, libRef.isWeak );
}
Debug.Log( "Adding frameworks..." );
PBXGroup frameworkGroup = this.GetGroup( "Frameworks" );
foreach( string framework in mod.frameworks ) {
string[] filename = framework.Split( ':' );
bool isWeak = ( filename.Length > 1 ) ? true : false;
string completePath = System.IO.Path.Combine( "System/Library/Frameworks", filename[0] );
this.AddFile( completePath, frameworkGroup, "SDKROOT", true, isWeak );
}
Debug.Log( "Adding files..." );
foreach( string filePath in mod.files ) {
string absoluteFilePath = System.IO.Path.Combine( mod.path, filePath );
this.AddFile( absoluteFilePath, modGroup );
}
Debug.Log( "Adding embed binaries..." );
if (mod.embed_binaries != null)
{
//1. Add LD_RUNPATH_SEARCH_PATHS for embed framework
this.overwriteBuildSetting("LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks", "Release");
this.overwriteBuildSetting("LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks", "Debug");
foreach( string binary in mod.embed_binaries ) {
string absoluteFilePath = System.IO.Path.Combine( mod.path, binary );
this.AddEmbedFramework(absoluteFilePath);
}
}
Debug.Log( "Adding folders..." );
foreach( string folderPath in mod.folders ) {
string absoluteFolderPath = System.IO.Path.Combine( Application.dataPath, folderPath );
Debug.Log ("Adding folder " + absoluteFolderPath);
this.AddFolder( absoluteFolderPath, modGroup, (string[])mod.excludes.ToArray( typeof(string) ) );
}
Debug.Log( "Adding headerpaths..." );
foreach( string headerpath in mod.headerpaths ) {
if (headerpath.Contains("$(inherited)")) {
Debug.Log ("not prepending a path to " + headerpath);
this.AddHeaderSearchPaths( headerpath );
} else {
string absoluteHeaderPath = System.IO.Path.Combine( mod.path, headerpath );
this.AddHeaderSearchPaths( absoluteHeaderPath );
}
}
Debug.Log( "Adding compiler flags..." );
foreach( string flag in mod.compiler_flags ) {
this.AddOtherCFlags( flag );
}
Debug.Log( "Adding linker flags..." );
foreach( string flag in mod.linker_flags ) {
this.AddOtherLinkerFlags( flag );
}
Debug.Log ("Adding plist items...");
string plistPath = this.projectRootPath + "/Info.plist";
XCPlist plist = new XCPlist (plistPath);
plist.Process(mod.plist);
this.Consolidate();
}
#endregion
#region Savings
public void Consolidate()
{
PBXDictionary consolidated = new PBXDictionary();
consolidated.Append<PBXBuildFile>( this.buildFiles );//sort!
consolidated.Append<PBXCopyFilesBuildPhase>( this.copyBuildPhases );
consolidated.Append<PBXFileReference>( this.fileReferences );//sort!
consolidated.Append<PBXFrameworksBuildPhase>( this.frameworkBuildPhases );
consolidated.Append<PBXGroup>( this.groups );//sort!
consolidated.Append<PBXNativeTarget>( this.nativeTargets );
consolidated.Add( project.guid, project.data );//TODO this should be named PBXProject?
consolidated.Append<PBXResourcesBuildPhase>( this.resourcesBuildPhases );
consolidated.Append<PBXShellScriptBuildPhase>( this.shellScriptBuildPhases );
consolidated.Append<PBXSourcesBuildPhase>( this.sourcesBuildPhases );
consolidated.Append<PBXVariantGroup>( this.variantGroups );
consolidated.Append<XCBuildConfiguration>( this.buildConfigurations );
consolidated.Append<XCConfigurationList>( this.configurationLists );
_objects = consolidated;
consolidated = null;
}
public void Backup()
{
string backupPath = Path.Combine( this.filePath, "project.backup.pbxproj" );
// Delete previous backup file
if( File.Exists( backupPath ) )
File.Delete( backupPath );
// Backup original pbxproj file first
File.Copy( System.IO.Path.Combine( this.filePath, "project.pbxproj" ), backupPath );
}
private void DeleteExisting(string path)
{
// Delete old project file
if( File.Exists( path ))
File.Delete( path );
}
private void CreateNewProject(PBXDictionary result, string path)
{
PBXParser parser = new PBXParser();
StreamWriter saveFile = File.CreateText( path );
saveFile.Write( parser.Encode( result, true ) );
saveFile.Close();
}
/// <summary>
/// Saves a project after editing.
/// </summary>
public void Save()
{
PBXDictionary result = new PBXDictionary();
result.Add( "archiveVersion", 1 );
result.Add( "classes", new PBXDictionary() );
result.Add( "objectVersion", 46 );
Consolidate();
result.Add( "objects", _objects );
result.Add( "rootObject", _rootObjectKey );
string projectPath = Path.Combine( this.filePath, "project.pbxproj" );
// Delete old project file, in case of an IOException 'Sharing violation on path Error'
DeleteExisting(projectPath);
// Parse result object directly into file
CreateNewProject(result,projectPath);
}
/**
* Raw project data.
*/
public Dictionary<string, object> objects {
get {
return null;
}
}
#endregion
public void Dispose()
{
}
}
}
fileFormatVersion: 2
guid: 386979592dd7e47b9a195f851501f676
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace UnityEditor.XCodeEditor
{
public partial class XClass : System.IDisposable
{
private string filePath;
public XClass(string fPath)
{
filePath = fPath;
if( !System.IO.File.Exists( filePath ) ) {
Debug.LogError( filePath +"路径下文件不存在" );
return;
}
}
public void WriteBelow(string below, string text)
{
StreamReader streamReader = new StreamReader(filePath);
string text_all = streamReader.ReadToEnd();
streamReader.Close();
int beginIndex = text_all.IndexOf(below);
if(beginIndex == -1){
Debug.LogError(filePath +"中没有找到标致"+below);
return;
}
int endIndex = text_all.LastIndexOf("\n", beginIndex + below.Length);
text_all = text_all.Substring(0, endIndex) + "\n"+text+"\n" + text_all.Substring(endIndex);
StreamWriter streamWriter = new StreamWriter(filePath);
streamWriter.Write(text_all);
streamWriter.Close();
}
public void Replace(string below, string newText)
{
StreamReader streamReader = new StreamReader(filePath);
string text_all = streamReader.ReadToEnd();
streamReader.Close();
int beginIndex = text_all.IndexOf(below);
if(beginIndex == -1){
Debug.LogError(filePath +"中没有找到标致"+below);
return;
}
text_all = text_all.Replace(below,newText);
StreamWriter streamWriter = new StreamWriter(filePath);
streamWriter.Write(text_all);
streamWriter.Close();
}
public void Dispose()
{
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: eb7e547716f574410a007ce72ca068c4
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.XCodeEditor;
#endif
using System.IO;
public static class XCodePostProcess
{
#if UNITY_EDITOR
[PostProcessBuild(999)]
public static void OnPostProcessBuild( BuildTarget target, string pathToBuiltProject )
{
#if UNITY_5
if (target != BuildTarget.iOS) {
#else
if (target != BuildTarget.iOS) {
#endif
Debug.LogWarning("Target is not iPhone. XCodePostProcess will not run");
return;
}
// Create a new project object from build target
XCProject project = new XCProject( pathToBuiltProject );
// Find and run through all projmods files to patch the project.
// Please pay attention that ALL projmods files in your project folder will be excuted!
string[] files = Directory.GetFiles( Application.dataPath, "*.projmods", SearchOption.AllDirectories );
foreach( string file in files ) {
UnityEngine.Debug.Log("ProjMod File: "+file);
project.ApplyMod( file );
}
//TODO disable the bitcode for iOS 9
project.overwriteBuildSetting("ENABLE_BITCODE", "NO", "Release");
project.overwriteBuildSetting("ENABLE_BITCODE", "NO", "Debug");
//TODO implement generic settings as a module option
// project.overwriteBuildSetting("CODE_SIGN_IDENTITY[sdk=iphoneos*]", "iPhone Distribution", "Release");
// Finally save the xcode project
project.Save();
}
#endif
public static void Log(string message)
{
UnityEngine.Debug.Log("PostProcess: "+message);
}
}
fileFormatVersion: 2
guid: 02d512b8c59ab43e1ad091cdb5510314
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
......@@ -35,7 +35,7 @@ public final class UnionApplication extends Application {
mApplcation = this;
AppliContext.init(this);
UMConfigure.setLogEnabled(true);
UMConfigure.init(this, "60bb4765799cce47f93624d9", "toutiao", UMConfigure.DEVICE_TYPE_PHONE,"");
UMConfigure.init(this, "608669265844f15425eb7953", "toutiao", UMConfigure.DEVICE_TYPE_PHONE,"");
}
......
fileFormatVersion: 2
guid: 5cef439187fbb41ab879da2b95ac4291
folderAsset: yes
DefaultImporter:
userData:
fileFormatVersion: 2
guid: 694411f45e4e64facb88eebfc2e1df1c
folderAsset: yes
DefaultImporter:
userData:
fileFormatVersion: 2
guid: e8fc61bed8a2640c7a2c6d5a51574621
guid: 644a9f5710e62403c94066ef9b61e775
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 9b3f9e305d4804719b5f4505912accf2
guid: fb7b442d8e32443e5856838741007f70
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 432060a129574479db0cfd441cdf3d69
timeCreated: 1497948394
licenseType: Free
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Android:
enabled: 1
settings:
CPU: ARMv7
Any:
enabled: 0
settings: {}
Editor:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 70c2f5016d12841a58b22885903c1170
timeCreated: 1481808880
licenseType: Free
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Android:
enabled: 1
settings: {}
Any:
enabled: 0
settings: {}
Editor:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 1db231dca0f72420cb880590f799d7d5
timeCreated: 1481808880
licenseType: Free
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Android:
enabled: 1
settings: {}
Any:
enabled: 0
settings: {}
Editor:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: a75f8ac9bc3f8414cb9b1aca48735084
guid: 79531ba82725e4071861c982307805c3
folderAsset: yes
timeCreated: 1443426231
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 16eaf0ec67588418783d6f5311aa71ce
timeCreated: 1497948394
licenseType: Free
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Android:
enabled: 1
settings:
CPU: x86
Any:
enabled: 0
settings: {}
Editor:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:
// ----------------------------------------
//
// BuglyAgent.cs
//
// Author:
// Yeelik, <bugly@tencent.com>
//
// Copyright (c) 2015 Bugly, Tencent. All rights reserved.
//
// ----------------------------------------
//
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
// We dont use the LogType enum in Unity as the numerical order doesnt suit our purposes
/// <summary>
/// Log severity.
/// { Log, LogDebug, LogInfo, LogWarning, LogAssert, LogError, LogException }
/// </summary>
public enum LogSeverity
{
Log,
LogDebug,
LogInfo,
LogWarning,
LogAssert,
LogError,
LogException
}
/// <summary>
/// Bugly agent.
/// </summary>
public sealed class BuglyAgent
{
// Define delegate support multicasting to replace the 'Application.LogCallback'
public delegate void LogCallbackDelegate (string condition,string stackTrace,LogType type);
/// <summary>
/// Configs the type of the crash reporter and customized log level to upload
/// </summary>
/// <param name="type">Type. Default=0, 1=Bugly v2.x MSDK=2</param>
/// <param name="logLevel">Log level. Off=0,Error=1,Warn=2,Info=3,Debug=4</param>
public static void ConfigCrashReporter(int type, int logLevel){
_SetCrashReporterType (type);
_SetCrashReporterLogLevel (logLevel);
}
/// <summary>
/// Init sdk with the specified appId.
/// <para>This will initialize sdk to report native exception such as obj-c, c/c++, java exceptions, and also enable c# exception handler to report c# exception logs</para>
/// </summary>
/// <param name="appId">App identifier.</param>
public static void InitWithAppId (string appId)
{
if (IsInitialized) {
DebugLog (null, "BuglyAgent has already been initialized.");
return;
}
if (string.IsNullOrEmpty (appId)) {
return;
}
// init the sdk with app id
InitBuglyAgent (appId);
DebugLog (null, "Initialized with app id: {0}", appId);
// Register the LogCallbackHandler by Application.RegisterLogCallback(Application.LogCallback)
_RegisterExceptionHandler ();
}
/// <summary>
/// Only Enable the C# exception handler.
///
/// <para>
/// You can call it when you do not call the 'InitWithAppId(string)', but you must make sure initialized the sdk in elsewhere,
/// such as the native code in associated Android or iOS project.
/// </para>
///
/// <para>
/// Default Level is <c>LogError</c>, so the LogError, LogException will auto report.
/// </para>
///
/// <para>
/// You can call the method <code>BuglyAgent.ConfigAutoReportLogLevel(LogSeverity)</code>
/// to change the level to auto report if you known what are you doing.
/// </para>
///
/// </summary>
public static void EnableExceptionHandler ()
{
if (IsInitialized) {
DebugLog (null, "BuglyAgent has already been initialized.");
return;
}
DebugLog (null, "Only enable the exception handler, please make sure you has initialized the sdk in the native code in associated Android or iOS project.");
// Register the LogCallbackHandler by Application.RegisterLogCallback(Application.LogCallback)
_RegisterExceptionHandler ();
}
/// <summary>
/// Registers the log callback handler.
///
/// If you need register logcallback using Application.RegisterLogCallback(LogCallback),
/// you can call this method to replace it.
///
/// <para></para>
/// </summary>
/// <param name="handler">Handler.</param>
public static void RegisterLogCallback (LogCallbackDelegate handler)
{
if (handler != null) {
DebugLog (null, "Add log callback handler: {0}", handler);
_LogCallbackEventHandler += handler;
}
}
/// <summary>
/// Sets the log callback extras handler.
/// </summary>
/// <param name="handler">Handler.</param>
public static void SetLogCallbackExtrasHandler(Func<Dictionary<string, string>> handler){
if (handler != null) {
_LogCallbackExtrasHandler = handler;
DebugLog(null, "Add log callback extra data handler : {0}", handler);
}
}
/// <summary>
/// Reports the exception.
/// </summary>
/// <param name="e">E.</param>
/// <param name="message">Message.</param>
public static void ReportException (System.Exception e, string message)
{
if (!IsInitialized) {
return;
}
DebugLog (null, "Report exception: {0}\n------------\n{1}\n------------", message, e);
_HandleException (e, message, false);
}
/// <summary>
/// Reports the exception.
/// </summary>
/// <param name="name">Name.</param>
/// <param name="message">Message.</param>
/// <param name="stackTrace">Stack trace.</param>
public static void ReportException (string name, string message, string stackTrace)
{
if (!IsInitialized) {
return;
}
DebugLog (null, "Report exception: {0} {1} \n{2}", name, message, stackTrace);
_HandleException (LogSeverity.LogException, name, message, stackTrace, false);
}
/// <summary>
/// Unregisters the log callback.
/// </summary>
/// <param name="handler">Handler.</param>
public static void UnregisterLogCallback (LogCallbackDelegate handler)
{
if (handler != null) {
DebugLog (null, "Remove log callback handler");
_LogCallbackEventHandler -= handler;
}
}
/// <summary>
/// Sets the user identifier.
/// </summary>
/// <param name="userId">User identifier.</param>
public static void SetUserId (string userId)
{
if (!IsInitialized) {
return;
}
DebugLog (null, "Set user id: {0}", userId);
SetUserInfo (userId);
}
/// <summary>
/// Sets the scene.
/// </summary>
/// <param name="sceneId">Scene identifier.</param>
public static void SetScene (int sceneId)
{
if (!IsInitialized) {
return;
}
DebugLog (null, "Set scene: {0}", sceneId);
SetCurrentScene (sceneId);
}
/// <summary>
/// Adds the scene data.
/// </summary>
/// <param name="key">Key.</param>
/// <param name="value">Value.</param>
public static void AddSceneData (string key, string value)
{
if (!IsInitialized) {
return;
}
DebugLog (null, "Add scene data: [{0}, {1}]", key, value);
AddKeyAndValueInScene (key, value);
}
/// <summary>
/// Configs the debug mode.
/// </summary>
/// <param name="enable">If set to <c>true</c> debug mode.</param>
public static void ConfigDebugMode (bool enable)
{
EnableDebugMode (enable);
DebugLog (null, "{0} the log message print to console", enable ? "Enable" : "Disable");
}
/// <summary>
/// Configs the auto quit application.
/// </summary>
/// <param name="autoQuit">If set to <c>true</c> auto quit.</param>
public static void ConfigAutoQuitApplication (bool autoQuit)
{
_autoQuitApplicationAfterReport = autoQuit;
}
/// <summary>
/// Configs the auto report log level. Default is LogSeverity.LogError.
/// <example>
/// LogSeverity { Log, LogDebug, LogInfo, LogWarning, LogAssert, LogError, LogException }
/// </example>
/// </summary>
///
/// <param name="level">Level.</param>
public static void ConfigAutoReportLogLevel (LogSeverity level)
{
_autoReportLogLevel = level;
}
/// <summary>
/// Configs the default.
/// </summary>
/// <param name="channel">Channel.</param>
/// <param name="version">Version.</param>
/// <param name="user">User.</param>
/// <param name="delay">Delay.</param>
public static void ConfigDefault (string channel, string version, string user, long delay)
{
DebugLog (null, "Config default channel:{0}, version:{1}, user:{2}, delay:{3}", channel, version, user, delay);
ConfigDefaultBeforeInit (channel, version, user, delay);
}
/// <summary>
/// Logs the debug.
/// </summary>
/// <param name="tag">Tag.</param>
/// <param name="format">Format.</param>
/// <param name="args">Arguments.</param>
public static void DebugLog (string tag, string format, params object[] args)
{
if(!_debugMode) {
return;
}
if (string.IsNullOrEmpty (format)) {
return;
}
Console.WriteLine ("[BuglyAgent] <Debug> - {0} : {1}", tag, string.Format (format, args));
}
/// <summary>
/// Prints the log.
/// </summary>
/// <param name="level">Level.</param>
/// <param name="format">Format.</param>
/// <param name="args">Arguments.</param>
public static void PrintLog (LogSeverity level, string format, params object[] args)
{
if (string.IsNullOrEmpty (format)) {
return;
}
LogRecord (level, string.Format (format, args));
}
#if UNITY_EDITOR || UNITY_STANDALONE
#region Interface(Empty) in Editor
private static void InitBuglyAgent (string appId)
{
}
private static void ConfigDefaultBeforeInit(string channel, string version, string user, long delay){
}
private static void EnableDebugMode(bool enable){
}
private static void SetUserInfo(string userInfo){
}
private static void ReportException (int type,string name, string message, string stackTrace, bool quitProgram)
{
}
private static void SetCurrentScene(int sceneId) {
}
private static void AddKeyAndValueInScene(string key, string value){
}
private static void AddExtraDataWithException(string key, string value) {
// only impl for iOS
}
private static void LogRecord(LogSeverity level, string message){
}
private static void SetUnityVersion(){
}
#endregion
#elif UNITY_ANDROID
// #if UNITY_ANDROID
#region Interface for Android
private static readonly string GAME_AGENT_CLASS = "com.tencent.bugly.agent.GameAgent";
private static readonly int TYPE_U3D_CRASH = 4;
private static readonly int GAME_TYPE_UNITY = 2;
private static bool hasSetGameType = false;
private static AndroidJavaClass _gameAgentClass = null;
public static AndroidJavaClass GameAgent {
get {
if (_gameAgentClass == null) {
_gameAgentClass = new AndroidJavaClass(GAME_AGENT_CLASS);
// using (AndroidJavaClass clazz = new AndroidJavaClass(CLASS_UNITYAGENT)) {
// _gameAgentClass = clazz.CallStatic<AndroidJavaObject> ("getInstance");
// }
}
if (!hasSetGameType) {
// set game type: unity(2).
_gameAgentClass.CallStatic ("setGameType", GAME_TYPE_UNITY);
hasSetGameType = true;
}
return _gameAgentClass;
}
}
private static string _configChannel;
private static string _configVersion;
private static string _configUser;
private static long _configDelayTime;
private static void ConfigDefaultBeforeInit(string channel, string version, string user, long delay){
_configChannel = channel;
_configVersion = version;
_configUser = user;
_configDelayTime = delay;
}
private static bool _configCrashReporterPackage = false;
private static void ConfigCrashReporterPackage(){
if (!_configCrashReporterPackage) {
try {
GameAgent.CallStatic("setSdkPackageName", _crashReporterPackage);
_configCrashReporterPackage = true;
} catch {
}
}
}
private static void InitBuglyAgent(string appId)
{
if (IsInitialized) {
return;
}
ConfigCrashReporterPackage();
try {
GameAgent.CallStatic("initCrashReport", appId, _configChannel, _configVersion, _configUser, _configDelayTime);
_isInitialized = true;
} catch {
}
}
private static void EnableDebugMode(bool enable){
_debugMode = enable;
ConfigCrashReporterPackage();
try {
GameAgent.CallStatic("setLogEnable", enable);
} catch {
}
}
private static void SetUserInfo(string userInfo){
ConfigCrashReporterPackage();
try {
GameAgent.CallStatic("setUserId", userInfo);
} catch {
}
}
private static void ReportException (int type, string name, string reason, string stackTrace, bool quitProgram)
{
ConfigCrashReporterPackage();
try {
GameAgent.CallStatic("postException", TYPE_U3D_CRASH, name, reason, stackTrace, quitProgram);
} catch {
}
}
private static void SetCurrentScene(int sceneId) {
ConfigCrashReporterPackage();
try {
GameAgent.CallStatic("setUserSceneTag", sceneId);
} catch {
}
}
private static void SetUnityVersion(){
ConfigCrashReporterPackage();
try {
GameAgent.CallStatic("setSdkConfig", "UnityVersion", Application.unityVersion);
} catch {
}
}
private static void AddKeyAndValueInScene(string key, string value){
ConfigCrashReporterPackage();
try {
GameAgent.CallStatic("putUserData", key, value);
} catch {
}
}
private static void AddExtraDataWithException(string key, string value) {
// no impl
}
private static void LogRecord(LogSeverity level, string message){
if (level < LogSeverity.LogWarning) {
DebugLog (level.ToString (), message);
}
ConfigCrashReporterPackage();
try {
GameAgent.CallStatic("printLog", string.Format ("<{0}> - {1}", level.ToString (), message));
} catch {
}
}
#endregion
#elif UNITY_IPHONE || UNITY_IOS
#region Interface for iOS
private static bool _crashReporterTypeConfiged = false;
private static void ConfigCrashReporterType(){
if (!_crashReporterTypeConfiged) {
try {
_BuglyConfigCrashReporterType(_crashReporterType);
_crashReporterTypeConfiged = true;
} catch {
}
}
}
private static void ConfigDefaultBeforeInit(string channel, string version, string user, long delay){
ConfigCrashReporterType();
try {
_BuglyDefaultConfig(channel, version, user, null);
} catch {
}
}
private static void EnableDebugMode(bool enable){
_debugMode = enable;
}
private static void InitBuglyAgent (string appId)
{
ConfigCrashReporterType();
if(!string.IsNullOrEmpty(appId)) {
_BuglyInit(appId, _debugMode, _crashReproterCustomizedLogLevel); // Log level
}
}
private static void SetUnityVersion(){
ConfigCrashReporterType();
_BuglySetExtraConfig("UnityVersion", Application.unityVersion);
}
private static void SetUserInfo(string userInfo){
if(!string.IsNullOrEmpty(userInfo)) {
ConfigCrashReporterType();
_BuglySetUserId(userInfo);
}
}
private static void ReportException (int type, string name, string reason, string stackTrace, bool quitProgram)
{
ConfigCrashReporterType();
string extraInfo = "";
Dictionary<string, string> extras = null;
if (_LogCallbackExtrasHandler != null) {
extras = _LogCallbackExtrasHandler();
}
if (extras == null || extras.Count == 0) {
extras = new Dictionary<string, string> ();
extras.Add ("UnityVersion", Application.unityVersion);
}
if (extras != null && extras.Count > 0) {
if (!extras.ContainsKey("UnityVersion")) {
extras.Add ("UnityVersion", Application.unityVersion);
}
StringBuilder builder = new StringBuilder();
foreach(KeyValuePair<string,string> kvp in extras){
builder.Append(string.Format("\"{0}\" : \"{1}\"", kvp.Key, kvp.Value)).Append(" , ");
}
extraInfo = string.Format("{{ {0} }}", builder.ToString().TrimEnd(" , ".ToCharArray()));
}
// 4 is C# exception
_BuglyReportException(4, name, reason, stackTrace, extraInfo, quitProgram);
}
private static void SetCurrentScene(int sceneId) {
ConfigCrashReporterType();
_BuglySetTag(sceneId);
}
private static void AddKeyAndValueInScene(string key, string value){
ConfigCrashReporterType();
_BuglySetKeyValue(key, value);
}
private static void AddExtraDataWithException(string key, string value) {
}
private static void LogRecord(LogSeverity level, string message){
if (level < LogSeverity.LogWarning) {
DebugLog (level.ToString (), message);
}
ConfigCrashReporterType();
_BuglyLogMessage(LogSeverityToInt(level), null, message);
}
private static int LogSeverityToInt(LogSeverity logLevel){
int level = 5;
switch(logLevel) {
case LogSeverity.Log:
level = 5;
break;
case LogSeverity.LogDebug:
level = 4;
break;
case LogSeverity.LogInfo:
level = 3;
break;
case LogSeverity.LogWarning:
case LogSeverity.LogAssert:
level = 2;
break;
case LogSeverity.LogError:
case LogSeverity.LogException:
level = 1;
break;
default:
level = 0;
break;
}
return level;
}
// --- dllimport start ---
[DllImport("__Internal")]
private static extern void _BuglyInit(string appId, bool debug, int level);
[DllImport("__Internal")]
private static extern void _BuglySetUserId(string userId);
[DllImport("__Internal")]
private static extern void _BuglySetTag(int tag);
[DllImport("__Internal")]
private static extern void _BuglySetKeyValue(string key, string value);
[DllImport("__Internal")]
private static extern void _BuglyReportException(int type, string name, string reason, string stackTrace, string extras, bool quit);
[DllImport("__Internal")]
private static extern void _BuglyDefaultConfig(string channel, string version, string user, string deviceId);
[DllImport("__Internal")]
private static extern void _BuglyLogMessage(int level, string tag, string log);
[DllImport("__Internal")]
private static extern void _BuglyConfigCrashReporterType(int type);
[DllImport("__Internal")]
private static extern void _BuglySetExtraConfig(string key, string value);
// dllimport end
#endregion
#endif
#region Privated Fields and Methods
private static event LogCallbackDelegate _LogCallbackEventHandler;
private static bool _isInitialized = false;
private static LogSeverity _autoReportLogLevel = LogSeverity.LogError;
private static int _crashReporterType = 1; // Default=0,1=Bugly-V2,MSDKBugly=2, IMSDKBugly=3
#if UNITY_ANDROID
// The crash reporter package name, default is 'com.tencent.bugly'
private static string _crashReporterPackage = "com.tencent.bugly";
#endif
#if UNITY_IPHONE || UNITY_IOS
private static int _crashReproterCustomizedLogLevel = 2; // Off=0,Error=1,Warn=2,Info=3,Debug=4
#endif
#pragma warning disable 414
private static bool _debugMode = false;
private static bool _autoQuitApplicationAfterReport = false;
private static readonly int EXCEPTION_TYPE_UNCAUGHT = 1;
private static readonly int EXCEPTION_TYPE_CAUGHT = 2;
private static readonly string _pluginVersion = "1.5.1";
private static Func<Dictionary<string, string>> _LogCallbackExtrasHandler;
public static string PluginVersion {
get { return _pluginVersion; }
}
public static bool IsInitialized {
get { return _isInitialized; }
}
public static bool AutoQuitApplicationAfterReport {
get { return _autoQuitApplicationAfterReport; }
}
private static void _SetCrashReporterType(int type){
_crashReporterType = type;
if (_crashReporterType == 2) {
#if UNITY_ANDROID
_crashReporterPackage = "com.tencent.bugly.msdk";
#endif
}
}
private static void _SetCrashReporterLogLevel(int logLevel){
#if UNITY_IPHONE || UNITY_IOS
_crashReproterCustomizedLogLevel = logLevel;
#endif
}
private static void _RegisterExceptionHandler ()
{
try {
// hold only one instance
#if UNITY_5
Application.logMessageReceived += _OnLogCallbackHandler;
#else
Application.RegisterLogCallback (_OnLogCallbackHandler);
#endif
AppDomain.CurrentDomain.UnhandledException += _OnUncaughtExceptionHandler;
_isInitialized = true;
DebugLog (null, "Register the log callback in Unity {0}", Application.unityVersion);
} catch {
}
SetUnityVersion ();
}
private static void _UnregisterExceptionHandler ()
{
try {
#if UNITY_5
Application.logMessageReceived -= _OnLogCallbackHandler;
#else
Application.RegisterLogCallback (null);
#endif
System.AppDomain.CurrentDomain.UnhandledException -= _OnUncaughtExceptionHandler;
DebugLog (null, "Unregister the log callback in unity {0}", Application.unityVersion);
} catch {
}
}
private static void _OnLogCallbackHandler (string condition, string stackTrace, LogType type)
{
if (_LogCallbackEventHandler != null) {
_LogCallbackEventHandler (condition, stackTrace, type);
}
if (!IsInitialized) {
return;
}
if (!string.IsNullOrEmpty (condition) && condition.Contains ("[BuglyAgent] <Log>")) {
return;
}
if (_uncaughtAutoReportOnce) {
return;
}
// convert the log level
LogSeverity logLevel = LogSeverity.Log;
switch (type) {
case LogType.Exception:
logLevel = LogSeverity.LogException;
break;
case LogType.Error:
logLevel = LogSeverity.LogError;
break;
case LogType.Assert:
logLevel = LogSeverity.LogAssert;
break;
case LogType.Warning:
logLevel = LogSeverity.LogWarning;
break;
case LogType.Log:
logLevel = LogSeverity.LogDebug;
break;
default:
break;
}
if (LogSeverity.Log == logLevel) {
return;
}
_HandleException (logLevel, null, condition, stackTrace, true);
}
private static void _OnUncaughtExceptionHandler (object sender, System.UnhandledExceptionEventArgs args)
{
if (args == null || args.ExceptionObject == null) {
return;
}
try {
if (args.ExceptionObject.GetType () != typeof(System.Exception)) {
return;
}
} catch {
if (UnityEngine.Debug.isDebugBuild == true) {
UnityEngine.Debug.Log ("BuglyAgent: Failed to report uncaught exception");
}
return;
}
if (!IsInitialized) {
return;
}
if (_uncaughtAutoReportOnce) {
return;
}
_HandleException ((System.Exception)args.ExceptionObject, null, true);
}
private static void _HandleException (System.Exception e, string message, bool uncaught)
{
if (e == null) {
return;
}
if (!IsInitialized) {
return;
}
string name = e.GetType ().Name;
string reason = e.Message;
if (!string.IsNullOrEmpty (message)) {
reason = string.Format ("{0}{1}***{2}", reason, Environment.NewLine, message);
}
StringBuilder stackTraceBuilder = new StringBuilder ("");
StackTrace stackTrace = new StackTrace (e, true);
int count = stackTrace.FrameCount;
for (int i = 0; i < count; i++) {
StackFrame frame = stackTrace.GetFrame (i);
stackTraceBuilder.AppendFormat ("{0}.{1}", frame.GetMethod ().DeclaringType.Name, frame.GetMethod ().Name);
ParameterInfo[] parameters = frame.GetMethod ().GetParameters ();
if (parameters == null || parameters.Length == 0) {
stackTraceBuilder.Append (" () ");
} else {
stackTraceBuilder.Append (" (");
int pcount = parameters.Length;
ParameterInfo param = null;
for (int p = 0; p < pcount; p++) {
param = parameters [p];
stackTraceBuilder.AppendFormat ("{0} {1}", param.ParameterType.Name, param.Name);
if (p != pcount - 1) {
stackTraceBuilder.Append (", ");
}
}
param = null;
stackTraceBuilder.Append (") ");
}
string fileName = frame.GetFileName ();
if (!string.IsNullOrEmpty (fileName) && !fileName.ToLower ().Equals ("unknown")) {
fileName = fileName.Replace ("\\", "/");
int loc = fileName.ToLower ().IndexOf ("/assets/");
if (loc < 0) {
loc = fileName.ToLower ().IndexOf ("assets/");
}
if (loc > 0) {
fileName = fileName.Substring (loc);
}
stackTraceBuilder.AppendFormat ("(at {0}:{1})", fileName, frame.GetFileLineNumber ());
}
stackTraceBuilder.AppendLine ();
}
// report
_reportException (uncaught, name, reason, stackTraceBuilder.ToString ());
}
private static void _reportException (bool uncaught, string name, string reason, string stackTrace)
{
if (string.IsNullOrEmpty (name)) {
return;
}
if (string.IsNullOrEmpty (stackTrace)) {
stackTrace = StackTraceUtility.ExtractStackTrace ();
}
if (string.IsNullOrEmpty (stackTrace)) {
stackTrace = "Empty";
} else {
try {
string[] frames = stackTrace.Split ('\n');
if (frames != null && frames.Length > 0) {
StringBuilder trimFrameBuilder = new StringBuilder ();
string frame = null;
int count = frames.Length;
for (int i = 0; i < count; i++) {
frame = frames [i];
if (string.IsNullOrEmpty (frame) || string.IsNullOrEmpty (frame.Trim ())) {
continue;
}
frame = frame.Trim ();
// System.Collections.Generic
if (frame.StartsWith ("System.Collections.Generic.") || frame.StartsWith ("ShimEnumerator")) {
continue;
}
if (frame.StartsWith ("Bugly")) {
continue;
}
if (frame.Contains ("..ctor")) {
continue;
}
int start = frame.ToLower ().IndexOf ("(at");
int end = frame.ToLower ().IndexOf ("/assets/");
if (start > 0 && end > 0) {
trimFrameBuilder.AppendFormat ("{0}(at {1}", frame.Substring (0, start).Replace (":", "."), frame.Substring (end));
} else {
trimFrameBuilder.Append (frame.Replace (":", "."));
}
trimFrameBuilder.AppendLine ();
}
stackTrace = trimFrameBuilder.ToString ();
}
} catch {
PrintLog(LogSeverity.LogWarning,"{0}", "Error to parse the stack trace");
}
}
PrintLog (LogSeverity.LogError, "ReportException: {0} {1}\n*********\n{2}\n*********", name, reason, stackTrace);
_uncaughtAutoReportOnce = uncaught && _autoQuitApplicationAfterReport;
ReportException (uncaught ? EXCEPTION_TYPE_UNCAUGHT : EXCEPTION_TYPE_CAUGHT, name, reason, stackTrace, uncaught && _autoQuitApplicationAfterReport);
}
private static void _HandleException (LogSeverity logLevel, string name, string message, string stackTrace, bool uncaught)
{
if (!IsInitialized) {
DebugLog (null, "It has not been initialized.");
return;
}
if (logLevel == LogSeverity.Log) {
return;
}
if ((uncaught && logLevel < _autoReportLogLevel)) {
DebugLog (null, "Not report exception for level {0}", logLevel.ToString ());
return;
}
string type = null;
string reason = null;
if (!string.IsNullOrEmpty (message)) {
try {
if ((LogSeverity.LogException == logLevel) && message.Contains ("Exception")) {
Match match = new Regex (@"^(?<errorType>\S+):\s*(?<errorMessage>.*)", RegexOptions.Singleline).Match (message);
if (match.Success) {
type = match.Groups ["errorType"].Value.Trim();
reason = match.Groups ["errorMessage"].Value.Trim ();
}
} else if ((LogSeverity.LogError == logLevel) && message.StartsWith ("Unhandled Exception:")) {
Match match = new Regex (@"^Unhandled\s+Exception:\s*(?<exceptionName>\S+):\s*(?<exceptionDetail>.*)", RegexOptions.Singleline).Match(message);
if (match.Success) {
string exceptionName = match.Groups ["exceptionName"].Value.Trim();
string exceptionDetail = match.Groups ["exceptionDetail"].Value.Trim ();
//
int dotLocation = exceptionName.LastIndexOf(".");
if (dotLocation > 0 && dotLocation != exceptionName.Length) {
type = exceptionName.Substring(dotLocation + 1);
} else {
type = exceptionName;
}
int stackLocation = exceptionDetail.IndexOf(" at ");
if (stackLocation > 0) {
//
reason = exceptionDetail.Substring(0, stackLocation);
// substring after " at "
string callStacks = exceptionDetail.Substring(stackLocation + 3).Replace(" at ", "\n").Replace("in <filename unknown>:0","").Replace("[0x00000]","");
//
stackTrace = string.Format("{0}\n{1}", stackTrace, callStacks.Trim());
} else {
reason = exceptionDetail;
}
// for LuaScriptException
if(type.Equals("LuaScriptException") && exceptionDetail.Contains(".lua") && exceptionDetail.Contains("stack traceback:")) {
stackLocation = exceptionDetail.IndexOf("stack traceback:");
if(stackLocation > 0) {
reason = exceptionDetail.Substring(0, stackLocation);
// substring after "stack traceback:"
string callStacks = exceptionDetail.Substring(stackLocation + 16).Replace(" [", " \n[");
//
stackTrace = string.Format("{0}\n{1}", stackTrace, callStacks.Trim());
}
}
}
}
} catch {
}
if (string.IsNullOrEmpty (reason)) {
reason = message;
}
}
if (string.IsNullOrEmpty (name)) {
if (string.IsNullOrEmpty (type)) {
type = string.Format ("Unity{0}", logLevel.ToString ());
}
} else {
type = name;
}
_reportException (uncaught, type, reason, stackTrace);
}
private static bool _uncaughtAutoReportOnce = false;
#endregion
}
\ No newline at end of file
fileFormatVersion: 2
guid: be621fe31508b4f2ab134ee879ec97b4
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
// ----------------------------------------
//
// BuglyCallbackDelegate.cs
//
// Author:
// Yeelik, <bugly@tencent.com>
//
// Copyright (c) 2015 Bugly, Tencent. All rights reserved.
//
// ----------------------------------------
//
using UnityEngine;
using System.Collections;
public abstract class BuglyCallback
{
// The delegate of callback handler which Call the Application.RegisterLogCallback(Application.LogCallback)
/// <summary>
/// Raises the application log callback handler event.
/// </summary>
/// <param name="condition">Condition.</param>
/// <param name="stackTrace">Stack trace.</param>
/// <param name="type">Type.</param>
public abstract void OnApplicationLogCallbackHandler (string condition, string stackTrace, LogType type);
}
fileFormatVersion: 2
guid: 78e76f643d1884dcab602d5fe79b08e1
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
// ----------------------------------------
//
// BuglyInit.cs
//
// Author:
// Yeelik, <bugly@tencent.com>
//
// Copyright (c) 2015 Bugly, Tencent. All rights reserved.
//
// ----------------------------------------
//
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class BuglyInit : MonoBehaviour
{
/// <summary>
/// Your Bugly App ID. Every app has a special identifier that allows Bugly to associate error monitoring data with your app.
/// Your App ID can be found on the "Setting" page of the app you are trying to monitor.
/// </summary>
/// <example>A real App ID looks like this: 90000xxxx</example>
private const string BuglyAppID = "YOUR APP ID GOES HERE";
void Awake ()
{
// Enable the debug log print
BuglyAgent.ConfigDebugMode (false);
// Config default channel, version, user
BuglyAgent.ConfigDefault (null, null, null, 0);
// Config auto report log level, default is LogSeverity.LogError, so the LogError, LogException log will auto report
BuglyAgent.ConfigAutoReportLogLevel (LogSeverity.LogError);
// Config auto quit the application make sure only the first one c# exception log will be report, please don't set TRUE if you do not known what are you doing.
BuglyAgent.ConfigAutoQuitApplication (false);
// If you need register Application.RegisterLogCallback(LogCallback), you can replace it with this method to make sure your function is ok.
BuglyAgent.RegisterLogCallback (null);
// Init the bugly sdk and enable the c# exception handler.
BuglyAgent.InitWithAppId (BuglyAppID);
// TODO Required. If you do not need call 'InitWithAppId(string)' to initialize the sdk(may be you has initialized the sdk it associated Android or iOS project),
// please call this method to enable c# exception handler only.
BuglyAgent.EnableExceptionHandler ();
// TODO NOT Required. If you need to report extra data with exception, you can set the extra handler
BuglyAgent.SetLogCallbackExtrasHandler (MyLogCallbackExtrasHandler);
Destroy (this);
}
// Extra data handler to packet data and report them with exception.
// Please do not do hard work in this handler
static Dictionary<string, string> MyLogCallbackExtrasHandler ()
{
// TODO Test log, please do not copy it
BuglyAgent.PrintLog (LogSeverity.Log, "extra handler");
// TODO Sample code, please do not copy it
Dictionary<string, string> extras = new Dictionary<string, string> ();
extras.Add ("ScreenSolution", string.Format ("{0}x{1}", Screen.width, Screen.height));
extras.Add ("deviceModel", SystemInfo.deviceModel);
extras.Add ("deviceName", SystemInfo.deviceName);
extras.Add ("deviceType", SystemInfo.deviceType.ToString ());
extras.Add ("deviceUId", SystemInfo.deviceUniqueIdentifier);
extras.Add ("gDId", string.Format ("{0}", SystemInfo.graphicsDeviceID));
extras.Add ("gDName", SystemInfo.graphicsDeviceName);
extras.Add ("gDVdr", SystemInfo.graphicsDeviceVendor);
extras.Add ("gDVer", SystemInfo.graphicsDeviceVersion);
extras.Add ("gDVdrID", string.Format ("{0}", SystemInfo.graphicsDeviceVendorID));
extras.Add ("graphicsMemorySize", string.Format ("{0}", SystemInfo.graphicsMemorySize));
extras.Add ("systemMemorySize", string.Format ("{0}", SystemInfo.systemMemorySize));
extras.Add ("UnityVersion", Application.unityVersion);
BuglyAgent.PrintLog (LogSeverity.LogInfo, "Package extra data");
return extras;
}
}
fileFormatVersion: 2
guid: a717f6955eddf4463ad541714a1b5483
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
......@@ -34,6 +34,11 @@ public class InitProject : MonoBehaviour
#if !UNITY_EDITOR && UNITY_IOS
// 初始化友盟sdk
InitUMSDK(Constant.Instance.UM_ID);
#endif
#if UNITY_IPHONE || UNITY_IOS
//BuglyAgent.InitWithAppId ("Your App ID");
#elif UNITY_ANDROID
BuglyAgent.InitWithAppId("43671e8612");
#endif
}
......
......@@ -33,6 +33,7 @@ public class Splash : MonoBehaviour
{
EventUtils.OnEvent("app_start");
getPermission();
BuglyAgent.EnableExceptionHandler();
}
/// <summary>
......
......@@ -5,14 +5,14 @@
"depth": 0,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
"url": "https://packages.unity.cn"
},
"com.unity.ext.nunit": {
"version": "1.0.6",
"depth": 1,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
"url": "https://packages.unity.cn"
},
"com.unity.ide.rider": {
"version": "1.1.4",
......@@ -21,14 +21,14 @@
"dependencies": {
"com.unity.test-framework": "1.1.1"
},
"url": "https://packages.unity.com"
"url": "https://packages.unity.cn"
},
"com.unity.ide.vscode": {
"version": "1.2.3",
"depth": 0,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
"url": "https://packages.unity.cn"
},
"com.unity.test-framework": {
"version": "1.1.20",
......@@ -39,7 +39,7 @@
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
},
"url": "https://packages.unity.com"
"url": "https://packages.unity.cn"
},
"com.unity.textmeshpro": {
"version": "2.1.1",
......@@ -48,14 +48,14 @@
"dependencies": {
"com.unity.ugui": "1.0.0"
},
"url": "https://packages.unity.com"
"url": "https://packages.unity.cn"
},
"com.unity.timeline": {
"version": "1.2.17",
"depth": 0,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
"url": "https://packages.unity.cn"
},
"com.unity.ugui": {
"version": "1.0.0",
......
......@@ -17,7 +17,7 @@ MonoBehaviour:
m_Registries:
- m_Id: main
m_Name:
m_Url: https://packages.unity.com
m_Url: https://packages.unity.cn
m_Scopes: []
m_IsDefault: 1
m_UserSelectedRegistryName:
......
......@@ -41,6 +41,12 @@ PlayerSettings:
height: 1
m_SplashScreenLogos: []
m_VirtualRealitySplashScreen: {fileID: 0}
m_ShowUnitySplashAds: 0
m_AdsAndroidGameId:
m_AdsIosGameId:
m_ShowSplashAdsSlogan: 0
m_SloganImage: {fileID: 0}
m_SloganHeight: 150
m_HolographicTrackingLossScreen: {fileID: 0}
defaultScreenWidth: 1024
defaultScreenHeight: 768
......@@ -118,13 +124,14 @@ PlayerSettings:
vulkanNumSwapchainBuffers: 3
vulkanEnableSetSRGBWrite: 0
vulkanEnableLateAcquireNextImage: 0
useSecurityBuild: 0
m_SupportedAspectRatios:
4:3: 1
5:4: 1
16:10: 1
16:9: 1
Others: 1
bundleVersion: 1.0.4
bundleVersion: 1.0.5
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0
......@@ -172,7 +179,7 @@ PlayerSettings:
iPhone: com.ym.simulator
buildNumber:
iPhone: 4
AndroidBundleVersionCode: 1
AndroidBundleVersionCode: 6
AndroidMinSdkVersion: 21
AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1
......@@ -465,6 +472,7 @@ PlayerSettings:
m_Kind: 4
m_SubKind: App Store
m_BuildTargetBatching: []
m_BuildTargetEncrypting: []
m_BuildTargetGraphicsJobs:
- m_BuildTarget: MacStandaloneSupport
m_GraphicsJobs: 0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment