2017-09-21 18:33:05 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace CodeWalker.GameFiles
|
|
|
|
|
{
|
|
|
|
|
[TypeConverter(typeof(ExpandableObjectConverter))]
|
2018-03-08 09:15:28 +08:00
|
|
|
|
public class YtypFile : GameFile, PackedFile
|
2017-09-21 18:33:05 +08:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Meta Meta { get; set; }
|
|
|
|
|
public PsoFile Pso { get; set; }
|
|
|
|
|
public RbfFile Rbf { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public uint NameHash { get; set; }
|
|
|
|
|
public string[] Strings { get; set; }
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
public CMapTypes _CMapTypes;
|
|
|
|
|
public CMapTypes CMapTypes { get { return _CMapTypes; } set { _CMapTypes = value; } }
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
public Archetype[] AllArchetypes { get; set; }
|
|
|
|
|
|
|
|
|
|
public MetaWrapper[] Extensions { get; set; }
|
|
|
|
|
|
2017-09-28 00:24:21 +08:00
|
|
|
|
public CCompositeEntityType[] CompositeEntityTypes { get; set; }
|
|
|
|
|
|
|
|
|
|
|
2018-03-03 21:03:08 +08:00
|
|
|
|
//fields used by the editor:
|
|
|
|
|
public bool HasChanged { get; set; } = false;
|
|
|
|
|
public List<string> SaveWarnings = null;
|
|
|
|
|
|
2017-09-28 00:24:21 +08:00
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
2018-03-08 09:15:28 +08:00
|
|
|
|
public YtypFile() : base(null, GameFileType.Ytyp)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
public YtypFile(RpfFileEntry entry) : base(entry, GameFileType.Ytyp)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
2018-03-03 21:03:08 +08:00
|
|
|
|
return (RpfFileEntry != null) ? RpfFileEntry.Name : string.Empty;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
public byte[] Save()
|
|
|
|
|
{
|
|
|
|
|
MetaBuilder mb = new MetaBuilder();
|
|
|
|
|
|
|
|
|
|
var mdb = mb.EnsureBlock(MetaName.CMapTypes);
|
|
|
|
|
|
|
|
|
|
CMapTypes mapTypes = _CMapTypes;
|
|
|
|
|
|
|
|
|
|
if (Extensions == null || Extensions.Length <= 0)
|
|
|
|
|
mapTypes.extensions = new Array_StructurePointer();
|
|
|
|
|
else
|
|
|
|
|
mapTypes.extensions = mb.AddWrapperArrayPtr(Extensions);
|
|
|
|
|
|
|
|
|
|
if ((AllArchetypes != null) && (AllArchetypes.Length > 0))
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < AllArchetypes.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var arch = AllArchetypes[i]; //save the extensions first..
|
|
|
|
|
if (arch._BaseArchetypeDef.extensions.Count1 > 0)
|
|
|
|
|
{
|
|
|
|
|
arch._BaseArchetypeDef.extensions = mb.AddWrapperArrayPtr(arch.Extensions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MetaPOINTER[] ptrs = new MetaPOINTER[AllArchetypes.Length];
|
|
|
|
|
for (int i = 0; i < AllArchetypes.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var arch = AllArchetypes[i];
|
|
|
|
|
switch (arch)
|
|
|
|
|
{
|
|
|
|
|
case TimeArchetype t:
|
|
|
|
|
ptrs[i] = mb.AddItemPtr(MetaName.CTimeArchetypeDef, t._TimeArchetypeDef);
|
|
|
|
|
break;
|
|
|
|
|
case MloArchetype m:
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
m._MloArchetypeDef._MloArchetypeDef.entities = mb.AddWrapperArrayPtr(m.entities);
|
|
|
|
|
m._MloArchetypeDef._MloArchetypeDef.rooms = mb.AddWrapperArray(m.rooms);
|
|
|
|
|
m._MloArchetypeDef._MloArchetypeDef.portals = mb.AddWrapperArray(m.portals);
|
|
|
|
|
m._MloArchetypeDef._MloArchetypeDef.entitySets = mb.AddWrapperArray(m.entitySets);
|
2018-12-20 10:38:39 +08:00
|
|
|
|
m._MloArchetypeDef._MloArchetypeDef.timeCycleModifiers = mb.AddItemArrayPtr(MetaName.CMloTimeCycleModifier, m.timeCycleModifiers);
|
2018-12-03 16:54:04 +08:00
|
|
|
|
}
|
|
|
|
|
catch/* (Exception e)*/
|
|
|
|
|
{
|
|
|
|
|
//todo: log save error.
|
|
|
|
|
}
|
|
|
|
|
ptrs[i] = mb.AddItemPtr(MetaName.CMloArchetypeDef, m._MloArchetypeDef);
|
|
|
|
|
break;
|
|
|
|
|
case Archetype a:
|
|
|
|
|
ptrs[i] = mb.AddItemPtr(MetaName.CBaseArchetypeDef, a._BaseArchetypeDef);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mapTypes.archetypes = mb.AddPointerArray(ptrs);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mapTypes.archetypes = new Array_StructurePointer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CompositeEntityTypes != null && CompositeEntityTypes.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
var cptrs = new MetaPOINTER[CompositeEntityTypes.Length];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < cptrs.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var cet = CompositeEntityTypes[i];
|
|
|
|
|
cptrs[i] = mb.AddItemPtr(MetaName.CCompositeEntityType, cet);
|
|
|
|
|
}
|
|
|
|
|
mapTypes.compositeEntityTypes = mb.AddItemArrayPtr(MetaName.CCompositeEntityType, cptrs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mapTypes.name = NameHash;
|
|
|
|
|
mapTypes.dependencies = new Array_uint(); // is this never used? possibly a todo?
|
|
|
|
|
|
|
|
|
|
mb.AddStructureInfo(MetaName.CMapTypes);
|
|
|
|
|
|
|
|
|
|
if ((AllArchetypes != null && AllArchetypes.Length > 0))
|
|
|
|
|
{
|
2018-12-20 10:38:39 +08:00
|
|
|
|
mb.AddStructureInfo(MetaName.CBaseArchetypeDef);
|
2019-01-13 16:51:49 +08:00
|
|
|
|
mb.AddEnumInfo(MetaName.rage__fwArchetypeDef__eAssetType); // ASSET_TYPE_
|
2018-12-03 16:54:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-20 10:38:39 +08:00
|
|
|
|
if ((AllArchetypes != null) && (AllArchetypes.Any(x => x is MloArchetype)))
|
|
|
|
|
{
|
|
|
|
|
mb.AddStructureInfo(MetaName.CMloArchetypeDef);
|
|
|
|
|
mb.AddStructureInfo(MetaName.CMloRoomDef);
|
|
|
|
|
mb.AddStructureInfo(MetaName.CMloPortalDef);
|
|
|
|
|
mb.AddStructureInfo(MetaName.CMloEntitySet);
|
|
|
|
|
mb.AddStructureInfo(MetaName.CMloTimeCycleModifier);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
if ((AllArchetypes != null) && (AllArchetypes.Any(x => x is MloArchetype m && m.entities.Length > 0)))
|
|
|
|
|
{
|
|
|
|
|
mb.AddStructureInfo(MetaName.CEntityDef);
|
2019-01-13 16:51:49 +08:00
|
|
|
|
mb.AddEnumInfo(MetaName.rage__eLodType); //LODTYPES_
|
|
|
|
|
mb.AddEnumInfo(MetaName.rage__ePriorityLevel); //PRI_
|
2018-12-03 16:54:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-20 10:38:39 +08:00
|
|
|
|
if ((AllArchetypes != null) && (AllArchetypes.Any(x => x is TimeArchetype)))
|
|
|
|
|
{
|
|
|
|
|
mb.AddStructureInfo(MetaName.CTimeArchetypeDef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CompositeEntityTypes?.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
mb.AddStructureInfo(MetaName.CCompositeEntityType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
mb.AddItem(MetaName.CMapTypes, mapTypes);
|
|
|
|
|
|
|
|
|
|
Meta meta = mb.GetMeta();
|
|
|
|
|
byte[] data = ResourceBuilder.Build(meta, 2);
|
|
|
|
|
|
|
|
|
|
HasChanged = false;
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
2018-03-08 09:15:28 +08:00
|
|
|
|
|
|
|
|
|
public void Load(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
//direct load from a raw, compressed ytyp file (openIV-compatible format)
|
|
|
|
|
|
|
|
|
|
RpfFile.LoadResourceFile(this, data, 2);
|
|
|
|
|
|
|
|
|
|
Loaded = true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public void Load(byte[] data, RpfFileEntry entry)
|
|
|
|
|
{
|
2018-03-03 21:03:08 +08:00
|
|
|
|
Name = entry.Name;
|
|
|
|
|
RpfFileEntry = entry;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
|
|
|
|
if (resentry == null)
|
|
|
|
|
{
|
|
|
|
|
MemoryStream ms = new MemoryStream(data);
|
|
|
|
|
if (RbfFile.IsRBF(ms))
|
|
|
|
|
{
|
|
|
|
|
Rbf = new RbfFile();
|
|
|
|
|
Rbf.Load(ms);
|
|
|
|
|
}
|
|
|
|
|
else if (PsoFile.IsPSO(ms))
|
|
|
|
|
{
|
|
|
|
|
Pso = new PsoFile();
|
|
|
|
|
Pso.Load(ms);
|
|
|
|
|
//PsoTypes.EnsurePsoTypes(Pso);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
|
|
|
|
|
|
|
|
|
Meta = rd.ReadBlock<Meta>();
|
|
|
|
|
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
_CMapTypes = MetaTypes.GetTypedData<CMapTypes>(Meta, MetaName.CMapTypes);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<Archetype> allarchs = new List<Archetype>();
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
var ptrs = MetaTypes.GetPointerArray(Meta, _CMapTypes.archetypes);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
if (ptrs != null)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < ptrs.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var ptr = ptrs[i];
|
2017-09-28 12:28:09 +08:00
|
|
|
|
var offset = ptr.Offset;
|
|
|
|
|
var block = Meta.GetBlock(ptr.BlockID);
|
|
|
|
|
if (block == null)
|
2017-09-21 18:33:05 +08:00
|
|
|
|
{ continue; }
|
|
|
|
|
if ((offset < 0) || (block.Data == null) || (offset >= block.Data.Length))
|
|
|
|
|
{ continue; }
|
|
|
|
|
|
2017-09-28 12:28:09 +08:00
|
|
|
|
Archetype a = null;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
switch (block.StructureNameHash)
|
|
|
|
|
{
|
|
|
|
|
case MetaName.CBaseArchetypeDef:
|
|
|
|
|
var basearch = PsoTypes.ConvertDataRaw<CBaseArchetypeDef>(block.Data, offset);
|
2017-09-28 12:28:09 +08:00
|
|
|
|
a = new Archetype();
|
|
|
|
|
a.Init(this, ref basearch);
|
|
|
|
|
a.Extensions = MetaTypes.GetExtensions(Meta, basearch.extensions);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
break;
|
|
|
|
|
case MetaName.CTimeArchetypeDef:
|
|
|
|
|
var timearch = PsoTypes.ConvertDataRaw<CTimeArchetypeDef>(block.Data, offset);
|
2017-09-28 12:28:09 +08:00
|
|
|
|
var ta = new TimeArchetype();
|
|
|
|
|
ta.Init(this, ref timearch);
|
|
|
|
|
ta.Extensions = MetaTypes.GetExtensions(Meta, timearch._BaseArchetypeDef.extensions);
|
|
|
|
|
a = ta;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
break;
|
|
|
|
|
case MetaName.CMloArchetypeDef:
|
|
|
|
|
var mloarch = PsoTypes.ConvertDataRaw<CMloArchetypeDef>(block.Data, offset);
|
2017-09-28 12:28:09 +08:00
|
|
|
|
var ma = new MloArchetype();
|
|
|
|
|
ma.Init(this, ref mloarch);
|
|
|
|
|
ma.Extensions = MetaTypes.GetExtensions(Meta, mloarch._BaseArchetypeDef.extensions);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
2018-01-02 12:37:09 +08:00
|
|
|
|
ma.LoadChildren(Meta);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
2017-09-28 12:28:09 +08:00
|
|
|
|
a = ma;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-28 12:28:09 +08:00
|
|
|
|
if (a != null)
|
|
|
|
|
{
|
|
|
|
|
allarchs.Add(a);
|
|
|
|
|
}
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
AllArchetypes = allarchs.ToArray();
|
|
|
|
|
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
Extensions = MetaTypes.GetExtensions(Meta, _CMapTypes.extensions);
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
if (Extensions != null)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
2017-09-28 00:24:21 +08:00
|
|
|
|
//AudioEmitters = MetaTypes.GetTypedDataArray<CExtensionDefAudioEmitter>(Meta, MetaName.CExtensionDefAudioEmitter);
|
|
|
|
|
//if (AudioEmitters != null)
|
|
|
|
|
//{ }
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
//CEntityDefs = MetaTypes.GetTypedDataArray<CEntityDef>(Meta, MetaName.CEntityDef);
|
|
|
|
|
|
2017-09-28 00:24:21 +08:00
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
CompositeEntityTypes = MetaTypes.ConvertDataArray<CCompositeEntityType>(Meta, MetaName.CCompositeEntityType, _CMapTypes.compositeEntityTypes);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
if (CompositeEntityTypes != null)
|
|
|
|
|
{ }
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
NameHash = _CMapTypes.name;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
if (NameHash == 0)
|
|
|
|
|
{
|
|
|
|
|
int ind = entry.NameLower.LastIndexOf('.');
|
|
|
|
|
if (ind > 0)
|
|
|
|
|
{
|
|
|
|
|
NameHash = JenkHash.GenHash(entry.NameLower.Substring(0, ind));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NameHash = JenkHash.GenHash(entry.NameLower);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Strings = MetaTypes.GetStrings(Meta);
|
|
|
|
|
if (Strings != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (string str in Strings)
|
|
|
|
|
{
|
|
|
|
|
JenkIndex.Ensure(str); //just shove them in there
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-09-28 00:24:21 +08:00
|
|
|
|
//foreach (var block in Meta.DataBlocks)
|
|
|
|
|
//{
|
|
|
|
|
// switch(block.StructureNameHash)
|
|
|
|
|
// {
|
|
|
|
|
// case MetaName.CMapTypes:
|
|
|
|
|
// case MetaName.CTimeArchetypeDef:
|
|
|
|
|
// case MetaName.CBaseArchetypeDef:
|
|
|
|
|
// case MetaName.CMloArchetypeDef:
|
|
|
|
|
// case MetaName.CMloTimeCycleModifier:
|
|
|
|
|
// case MetaName.CMloRoomDef:
|
|
|
|
|
// case MetaName.CMloPortalDef:
|
|
|
|
|
// case MetaName.CMloEntitySet:
|
|
|
|
|
// case MetaName.CEntityDef:
|
|
|
|
|
// case MetaName.CExtensionDefParticleEffect:
|
|
|
|
|
// case MetaName.CExtensionDefAudioCollisionSettings:
|
|
|
|
|
// case MetaName.CExtensionDefSpawnPoint:
|
|
|
|
|
// case MetaName.CExtensionDefSpawnPointOverride:
|
|
|
|
|
// case MetaName.CExtensionDefExplosionEffect:
|
|
|
|
|
// case MetaName.CExtensionDefAudioEmitter:
|
|
|
|
|
// case MetaName.CExtensionDefLadder:
|
|
|
|
|
// case MetaName.CExtensionDefBuoyancy:
|
|
|
|
|
// case MetaName.CExtensionDefExpression:
|
|
|
|
|
// case MetaName.CExtensionDefLightShaft:
|
|
|
|
|
// case MetaName.CExtensionDefLightEffect:
|
|
|
|
|
// case MetaName.CExtensionDefDoor:
|
|
|
|
|
// case MetaName.CExtensionDefWindDisturbance:
|
|
|
|
|
// case MetaName.CExtensionDefProcObject:
|
|
|
|
|
// case MetaName.CLightAttrDef:
|
|
|
|
|
// case MetaName.STRING:
|
|
|
|
|
// case MetaName.POINTER:
|
|
|
|
|
// case MetaName.UINT:
|
|
|
|
|
// case MetaName.VECTOR4:
|
|
|
|
|
// break;
|
|
|
|
|
// default:
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//MetaTypes.ParseMetaData(Meta);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-27 04:16:54 +08:00
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
public void AddArchetype(Archetype archetype)
|
|
|
|
|
{
|
|
|
|
|
if (AllArchetypes == null)
|
|
|
|
|
AllArchetypes = new Archetype[0];
|
2018-07-27 04:16:54 +08:00
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
List<Archetype> archetypes = AllArchetypes.ToList();
|
|
|
|
|
archetype.Ytyp = this;
|
|
|
|
|
archetypes.Add(archetype);
|
|
|
|
|
AllArchetypes = archetypes.ToArray();
|
2018-07-27 04:16:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
public Archetype AddArchetype()
|
2018-07-27 04:16:54 +08:00
|
|
|
|
{
|
2018-12-03 16:54:04 +08:00
|
|
|
|
var a = new Archetype();
|
2019-01-13 16:51:49 +08:00
|
|
|
|
a._BaseArchetypeDef.assetType = rage__fwArchetypeDef__eAssetType.ASSET_TYPE_DRAWABLE;
|
2018-12-03 16:54:04 +08:00
|
|
|
|
a._BaseArchetypeDef.lodDist = 60;
|
|
|
|
|
a._BaseArchetypeDef.hdTextureDist = 15;
|
|
|
|
|
a.Ytyp = this;
|
|
|
|
|
AddArchetype(a);
|
|
|
|
|
return a;
|
2018-07-27 04:16:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-03 16:54:04 +08:00
|
|
|
|
public bool RemoveArchetype(Archetype archetype)
|
2018-07-27 04:16:54 +08:00
|
|
|
|
{
|
2018-12-03 16:54:04 +08:00
|
|
|
|
List<Archetype> archetypes = AllArchetypes.ToList();
|
|
|
|
|
if (archetypes.Remove(archetype))
|
2018-07-27 04:16:54 +08:00
|
|
|
|
{
|
2018-12-03 16:54:04 +08:00
|
|
|
|
AllArchetypes = archetypes.ToArray();
|
|
|
|
|
return true;
|
2018-07-27 04:16:54 +08:00
|
|
|
|
}
|
2018-12-03 16:54:04 +08:00
|
|
|
|
return false;
|
2018-07-27 04:16:54 +08:00
|
|
|
|
}
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|