Parse(bool isAnime) // = true) { if (_text == null) { return(false); } XmlDocument doc = new XmlDocument(); // doc is the new xml document. doc.LoadXml(_text); // load the file. string rootTagStr, rootNSStr; if (isAnime) { // this is .ssax rootTagStr = "SpriteStudioMotion"; rootNSStr = "http://www.webtech.co.jp/SpriteStudio/XML/Motion"; } else { // this is .sssx rootTagStr = "SpriteStudioScene"; rootNSStr = "http://www.webtech.co.jp/SpriteStudio/XML/Scene"; } // check the root tag name validity // root is ?xml, so skip to the next XmlNode rootNode = doc.FirstChild; rootNode = rootNode.NextSibling; if (rootNode.Name != rootTagStr) { Debug.LogError("Invalid root tag name: " + rootNode.Name); return(false); } // check the root tag namespace validity XmlAttributeCollection rootNodeAttrs = rootNode.Attributes; XmlNode nodeXMLNS = rootNodeAttrs["xmlns"]; if (nodeXMLNS.Value != rootNSStr) { Debug.LogError("\"xmlns\" doesn't match: " + rootNode.Name); return(false); } // check file format version validity XmlNode versionNode = rootNodeAttrs["version"]; int readVersion = SsVersion.ToInt(versionNode.Value); if (readVersion < RequiredVersion) { Debug.LogError("Version under " + SsVersion.ToString(RequiredVersion) + " is not supported -> " + SsVersion.ToString(readVersion)); return(false); } if (readVersion > CurrentVersion) { Debug.LogError("This version " + SsVersion.ToString(readVersion) + " is not supported yet. supports up to " + SsVersion.ToString(CurrentVersion)); return(false); } #if false // made sure the rootNode has Header, ImageList, Parts XmlNodeList children = rootNode.ChildNodes; foreach (XmlNode n in children) { Debug.Log(n.Name); } #endif // Create an XmlNamespaceManager to resolve namespaces. NameTable nt = new NameTable(); _nsMgr = new XmlNamespaceManager(nt); _nsMgr.AddNamespace("cur", nodeXMLNS.Value); // read header XmlNode headerNode = _SelectSingleNode(rootNode, "cur:Header"); if (headerNode == null) { Debug.LogError("Header node is not found"); return(false); } XmlNode endFrameNode = _SelectSingleNode(headerNode, "cur:EndFrame"); if (endFrameNode == null) { Debug.LogError("EndFrame node is not found"); return(false); } _anmRes.EndFrame = _ToInt(endFrameNode.InnerText); #if NEED_EDIT_MODE _editMode = _ToInt(_SelectSingleNode(headerNode, "cur:EditMode").InnerText); #endif _anmRes.FPS = _ToInt(_SelectSingleNode(headerNode, "cur:BaseTickTime").InnerText); // read option settings XmlNode optionNode = _SelectSingleNode(headerNode, "cur:OptionState"); if (optionNode != null) { XmlNode n = _SelectSingleNode(optionNode, "cur:hvFlipForImageOnly"); if (n == null) { _anmRes.hvFlipForImageOnly = true; } else { _anmRes.hvFlipForImageOnly = _ToBool(n.InnerText); } } // create image manager singleton //var imgMgr = SsImageManager.Singleton; // enumerate image paths // it is possible to be nothing in .ssax XmlNodeList imageNodeList = _SelectNodes(rootNode, "./cur:ImageList/cur:Image"); if (imageNodeList == null) { Debug.LogError("ImageList node is not found"); return(false); } if (imageNodeList.Count <= 0) { Debug.LogError("ImageList has no contents"); return(false); } _anmRes.ImageList = new SsImageFile[imageNodeList.Count]; //Debug.Log("imageNodeList.Count: " + imageNodeList.Count); foreach (XmlNode e in imageNodeList) { string idStr = e.Attributes["Id"].Value; int index = _ToInt(idStr) - 1; // because it starts from 1. string path = e.Attributes["Path"].Value; // remove the useless string "./cur:" on head //Debug.Log("Id=" + idStr + " Path=" + path); if (path.StartsWith(@".\")) { path = path.Remove(0, 2); } path = path.Replace(@"\", "/"); // for Mac //path = _relPath + "/" + path; // doesn't care about ../ string baseFullPath = Path.GetFullPath("./"); path = Path.GetFullPath(Path.Combine(_relPath, path)); // combine path considering ../ path = path.Substring(baseFullPath.Length); path = path.Replace(@"\", "/"); // just in case //Debug.Log("Id=" + index + " Path=" + path); // get image file info which contains a Texture Asset. SsImageFile imgFile = GetImage(path); if (imgFile == null) { return(false); } XmlNode widthNode = e.Attributes["Width"]; if (widthNode != null) { imgFile.width = _ToInt(widthNode.Value); if (!_IsPowerOfTwo(imgFile.width)) { Debug.LogWarning("Image width is not power of 2, it will be scaled."); } } else { imgFile.width = imgFile.texture.width; } XmlNode heightNode = e.Attributes["Height"]; if (heightNode != null) { imgFile.height = _ToInt(heightNode.Value); if (!_IsPowerOfTwo(imgFile.height)) { Debug.LogWarning("Image height is not power of 2, it will be scaled."); } } else { imgFile.height = imgFile.texture.height; } // For now, bpp is not used anywhere. XmlNode bppNode = e.Attributes["Bpp"]; if (bppNode != null) { imgFile.bpp = _ToInt(bppNode.Value); if (imgFile.bpp <= 8) { Debug.LogWarning(path + " seems index color image, so it has to be converted to direct color or compressed type"); } } else { switch (imgFile.texture.format) { default: imgFile.bpp = 0; // Zero means something compressed type break; case TextureFormat.ARGB4444: case TextureFormat.RGB565: imgFile.bpp = 16; break; case TextureFormat.RGB24: imgFile.bpp = 24; break; case TextureFormat.ARGB32: case TextureFormat.RGBA32: imgFile.bpp = 32; break; } } // register image info into the list _anmRes.ImageList[index] = imgFile; } // enumerate parts XmlNodeList partList = _SelectNodes(rootNode, "./cur:Parts/cur:Part"); if (partList == null) { Debug.LogError("Parts node is not found"); return(false); } //Debug.Log("Parts Num: " + partList.Count); if (partList.Count <= 0) { Debug.LogError("No existence of Parts"); return(false); } _anmRes.PartList = new SsPartRes[partList.Count]; foreach (XmlNode part in partList) { // create a part var partBase = new SsPartRes(); partBase.AnimeRes = _anmRes; XmlAttribute rootAttr = part.Attributes["Root"]; partBase.InheritState.Initialize(4, _anmRes.hvFlipForImageOnly); string partName = _GetNodeValue(part, "./cur:Name"); partBase.Name = System.String.Copy(partName); if (rootAttr != null) { // this is root parts partBase.Type = SsPartType.Root; partBase.MyId = 0; partBase.ParentId = -1; } else { // general parts partBase.Type = (SsPartType)_ToInt(_GetNodeValue(part, "./cur:Type")); partBase.PicArea.Top = _ToInt(_GetNodeValue(part, "./cur:PictArea/cur:Top")); partBase.PicArea.Left = _ToInt(_GetNodeValue(part, "./cur:PictArea/cur:Left")); partBase.PicArea.Bottom = _ToInt(_GetNodeValue(part, "./cur:PictArea/cur:Bottom")); partBase.PicArea.Right = _ToInt(_GetNodeValue(part, "./cur:PictArea/cur:Right")); partBase.OriginX = _ToInt(_GetNodeValue(part, "./cur:OriginX")); partBase.OriginY = _ToInt(_GetNodeValue(part, "./cur:OriginY")); partBase.MyId = 1 + _ToInt(_GetNodeValue(part, "./cur:ID")); partBase.ParentId = 1 + _ToInt(_GetNodeValue(part, "./cur:ParentID")); partBase.SrcObjId = _ToInt(_GetNodeValue(part, "./cur:PicID")); partBase.AlphaBlendType = (SsAlphaBlendOperation)(1 + _ToInt(_GetNodeValue(part, "./cur:TransBlendType"))); partBase.InheritState.Type = (SsInheritanceType)_ToInt(_GetNodeValue(part, "./cur:InheritType")); if (partBase.SrcObjId >= _anmRes.ImageList.Length) { /* * // supply lack of image * int count = 1 + partBase.SrcObjId - _anmRes.ImageList.Length; * _anmRes.ImageList. * for (int i = 0; i < count; ++i) * { * _anmRes.ImageList[partBase.SrcObjId + i] = SsImageFile.invalidInstance; * } */ // clamp id Debug.LogWarning("Picture ID is out of image list. Part: " + partName + " PicId: " + partBase.SrcObjId); partBase.SrcObjId = 0; } partBase.imageFile = _anmRes.ImageList[partBase.SrcObjId]; if (partBase.imageFile != SsImageFile.invalidInstance) { // precalc UV coordinates partBase.CalcUVs(0, 0, 0, 0); // add basic material with basic shader AddMaterials(partBase, SsColorBlendOperation.Non); } if (partBase.InheritState.Type == SsInheritanceType.Parent) { // copy parent's value and rate statically. dynamic reference is certain implement but it costs much more. for (int i = 0; i < (int)SsKeyAttr.Num; ++i) { SsKeyAttr attr = (SsKeyAttr)i; var param = _anmRes.PartList[partBase.ParentId].InheritParam(attr); partBase.InheritState.Values[i] = param; //Debug.Log(partBase.Name +" inherits parent's attr: " + attr + " use: " + param.Use + " rate:" + param.Rate); } } } // make original 4 vertices will be not modified. it consists of OriginX/Y and PicArea.WH. partBase.OrgVertices = partBase.GetVertices(partBase.PicArea.WH()); #if false // parent part SsPartRes parentPart = null; if (partBase.ParentId >= 0) { parentPart = _anmRes.PartList[partBase.ParentId]; } #endif if (!isAnime) { // scene only has this element partBase.SrcObjType = (SsSourceObjectType)_ToInt(_GetNodeValue(part, "./cur:ObjectType")); } // parse attributes XmlNodeList attrList = _SelectNodes(part, "./cur:Attributes/cur:Attribute"); string attrName; foreach (XmlNode attrNode in attrList) { // recognize the tag attrName = attrNode.Attributes["Tag"].Value; if (attrName.Length != 4) { Debug.LogWarning("invalid attribute tag!!: " + attrName); continue; } SsKeyAttrDesc attrDesc = SsKeyAttrDescManager.Get(attrName); if (attrDesc == null) { Debug.LogWarning("Unknown or Unsupported attribute: " + attrName); continue; } switch (attrDesc.Attr) { case SsKeyAttr.PartsPal: if (attrNode.HasChildNodes) { Debug.LogWarning("Unsupported attribute: " + attrName); } continue; } // set inheritance parameter to part instance. if (partBase.Type != SsPartType.Root) { if (partBase.InheritState.Type == SsInheritanceType.Self) { // has its own value. var InheritParam = new SsInheritanceParam(); XmlNode InheritNode = attrNode.Attributes.GetNamedItem("Inherit"); if (InheritNode != null) { // mix my value and parent's value, but actually user 100% parent's... InheritParam.Use = true; InheritParam.Rate = (100 * _ToInt(InheritNode.Value)) / SSIO_SUCCEED_DENOMINATOR; } else { // absolutely not refer the parent's value. InheritParam.Use = false; InheritParam.Rate = 0; } // apply to part partBase.InheritState.Values[(int)attrDesc.Attr] = InheritParam; //Debug.LogError(partBase.Name +" has own value! attr:" + attrDesc.Attr + " use:" + InheritParam.Use); } } // enumerate keys XmlNodeList keyList = _SelectNodes(attrNode, "./cur:Key"); if (keyList == null || keyList.Count < 1) { //Debug.Log(attrName + " has no keys"); } else { foreach (XmlNode key in keyList) { //dynamic keyBase; cannot use dynamic at this time SsKeyFrameInterface keyBase; bool bNeedCurve = true; switch (attrDesc.ValueType) { case SsKeyValueType.Data: { // if value type is Data, string format of node "Value" depends on attribute type. string strValue = _GetNodeValue(key, "./cur:Value"); switch (attrDesc.CastType) { default: case SsKeyCastType.Bool: // not supported currently case SsKeyCastType.Other: // not supported currently case SsKeyCastType.Int: case SsKeyCastType.Hex: // these types are used as integer. var intKey = new SsIntKeyFrame(); // to make intellisence working, really?? if (attrDesc.CastType == SsKeyCastType.Hex) { intKey.Value = _HexToInt(strValue); } else { intKey.Value = _ToInt(strValue); } switch (attrDesc.Attr) { case SsKeyAttr.PosX: case SsKeyAttr.PosY: // apply scale factor intKey.Value = (int)((float)intKey.Value * _anmRes.ScaleFactor); break; #if _FORCE_BOUND_PART_TO_MOST_TOP // force bound part to most top to draw surely if wanted case SsKeyAttr.Prio: if (partBase.Type == SsPartType.Bound) { intKey.Value = short.MaxValue; // int.MaxValue is failed to unbox } break; #endif } #if _DEBUG Debug.LogWarning("Is it OK? -> " + strValue + " = " + intKey.Value); #endif keyBase = intKey; break; case SsKeyCastType.Float: case SsKeyCastType.Degree: var floatKey = new SsFloatKeyFrame(); // to make intellisence working, really?? #if _FORCE_BOUND_PART_TO_BE_TRANSPARENT // force bound part to transparent to draw if wanted if (attrDesc.Attr == SsKeyAttr.Trans && partBase.Type == SsPartType.Bound) { floatKey.Value = 0.5f; } else #endif floatKey.Value = (float)_ToDouble(strValue); // unnecessary to convert to radian. because Unity requires degree unit. //if (attrDesc.CastType == SsKeyCastType.Degree) //floatKey.Value = (float)_DegToRad( _ToDouble(strValue) ); keyBase = floatKey; break; } } break; case SsKeyValueType.Param: { var boolKey = new SsBoolKeyFrame(); keyBase = boolKey; boolKey.Value = _ToUInt(_GetNodeValue(key, "./cur:Value")) != 0 ? true : false; bNeedCurve = false; } break; case SsKeyValueType.Palette: { var paletteKey = new SsPaletteKeyFrame(); keyBase = paletteKey; var v = new SsPaletteKeyValue(); v.Use = _ToUInt(_GetNodeValue(key, "./cur:Use")) != 0 ? true : false; v.Page = _ToInt(_GetNodeValue(key, "./cur:Page")); v.Block = (byte)_ToInt(_GetNodeValue(key, "./cur:Block")); paletteKey.Value = v; } break; case SsKeyValueType.Color: { var colorBlendKey = new SsColorBlendKeyFrame(); keyBase = colorBlendKey; var v = new SsColorBlendKeyValue(4); v.Target = (SsColorBlendTarget)_ToUInt(_GetNodeValue(key, "./cur:Type")); v.Operation = (SsColorBlendOperation)(1 + _ToUInt(_GetNodeValue(key, "./cur:Blend"))); AddMaterials(partBase, v.Operation); switch (v.Target) { case SsColorBlendTarget.None: // no color, but do care about interpolation to/from 4 vertex colors for (int i = 0; i < 4; ++i) { v.Colors[i].R = v.Colors[i].G = v.Colors[i].B = 255; // 0 alpha value means no effect. v.Colors[i].A = 0; } break; case SsColorBlendTarget.Overall: // one color, but do care about interpolation to/from 4 vertex colors _GetColorRef(v.Colors[0], key, "./cur:Value"); for (int i = 1; i < 4; ++i) { v.Colors[i] = v.Colors[0]; } break; case SsColorBlendTarget.Vertex: // 4 vertex colors. the order is clockwise. _GetColorRef(v.Colors[0], key, "./cur:TopLeft"); _GetColorRef(v.Colors[1], key, "./cur:TopRight"); _GetColorRef(v.Colors[2], key, "./cur:BottomRight"); _GetColorRef(v.Colors[3], key, "./cur:BottomLeft"); break; } colorBlendKey.Value = v; } break; case SsKeyValueType.Vertex: { var vertexKey = new SsVertexKeyFrame(); keyBase = vertexKey; var v = new SsVertexKeyValue(4); _GetPoint(v.Vertices[0], key, "./cur:TopLeft"); _GetPoint(v.Vertices[1], key, "./cur:TopRight"); if (readVersion <= (uint)SsAnimeFormatVersion.V320) { // for obsolete version _GetPoint(v.Vertices[2], key, "./cur:BottomLeft"); _GetPoint(v.Vertices[3], key, "./cur:BottomRight"); } else { _GetPoint(v.Vertices[3], key, "./cur:BottomLeft"); _GetPoint(v.Vertices[2], key, "./cur:BottomRight"); } // apply scale factor for (int i = 0; i < v.Vertices.Length; ++i) { v.Vertices[i].Scale(_anmRes.ScaleFactor); } vertexKey.Value = v; } break; case SsKeyValueType.User: { var userKey = new SsUserDataKeyFrame(); keyBase = userKey; var v = new SsUserDataKeyValue(); XmlNode numberNode = _SelectSingleNode(key, "./cur:Number"); if (numberNode != null) { v.IsNum = true; v.Num = _ToInt(numberNode.InnerText); } XmlNode rectNode = _SelectSingleNode(key, "./cur:Rect"); if (rectNode != null) { v.IsRect = true; v.Rect = new SsRect(); _GetRect(v.Rect, rectNode); } XmlNode pointNode = _SelectSingleNode(key, "./cur:Point"); if (pointNode != null) { v.IsPoint = true; v.Point = new SsPoint(); _GetPoint(v.Point, pointNode); } XmlNode stringNode = _SelectSingleNode(key, "./cur:String"); if (stringNode != null) { v.IsString = true; v.String = System.String.Copy(stringNode.InnerText); } // writeback userKey.Value = v; bNeedCurve = false; } // case KEYTYPE_USERDATA break; case SsKeyValueType.Sound: { var soundKey = new SsSoundKeyFrame(); keyBase = soundKey; var v = new SsSoundKeyValue(); v.Flags = (SsSoundKeyFlags)_ToUInt(_SelectSingleNode(key, "./cur:Use").InnerText); v.NoteOn = _ToByte(_SelectSingleNode(key, "./cur:Note").InnerText); v.SoundId = _ToByte(_SelectSingleNode(key, "./cur:ID").InnerText); v.LoopNum = _ToByte(_SelectSingleNode(key, "./cur:Loop").InnerText); v.Volume = _ToByte(_SelectSingleNode(key, "./cur:Volume").InnerText); v.UserData = _ToUInt(_SelectSingleNode(key, "./cur:Data").InnerText); // writeback soundKey.Value = v; bNeedCurve = false; } break; default: Debug.LogError("Fatal error!! not implemented cast type: " + _ToInt(attrNode.Value)); continue; } // switch //keyBase = InitKeyFrameData(); keyBase.ValueType = attrDesc.ValueType; keyBase.Time = _ToInt(key.Attributes["Time"].Value); if (bNeedCurve) { // make values to interpolate var v = new SsCurveParams(); XmlNode curveTypeNode = key.Attributes["CurveType"]; if (curveTypeNode == null) { v.Type = SsInterpolationType.None; } else { v.Type = (SsInterpolationType)_ToUInt(curveTypeNode.Value); if (v.Type == SsInterpolationType.Hermite || v.Type == SsInterpolationType.Bezier) { // must exist if (key.Attributes["CurveStartT"] == null) { Debug.LogError("CurveStartT param not found!!"); return(false); } v.StartT = _ToFloat(key.Attributes["CurveStartT"].Value); v.StartV = _ToFloat(key.Attributes["CurveStartV"].Value); v.EndT = _ToFloat(key.Attributes["CurveEndT"].Value); v.EndV = _ToFloat(key.Attributes["CurveEndV"].Value); switch (attrDesc.Attr) { case SsKeyAttr.PosX: case SsKeyAttr.PosY: case SsKeyAttr.Vertex: // apply scale factor v.StartV *= _anmRes.ScaleFactor; v.EndV *= _anmRes.ScaleFactor; break; case SsKeyAttr.Angle: if (_database.AngleCurveParamAsRadian) { // degree to radian v.StartV = (float)(v.StartV * 180 / System.Math.PI); v.EndV = (float)(v.EndV * 180 / System.Math.PI); } break; } } } keyBase.Curve = v; } #if false//_DEBUG // dump debug info SsDebugLog.Print("Attr: " + attrDesc.Attr + " " + keyBase.ToString()); #endif // add keyFrame info to part instance. partBase.AddKeyFrame(attrDesc.Attr, keyBase); } } } #if _DEBUG // dump debug info SsDebugLog.Print(partBase.ToString()); #endif if (_precalcAttrValues) { // precalculate attribute values each frame partBase.CreateAttrValues(_anmRes.EndFrame + 1); } // add to list // if the container is Dictionary, allow non-sequential ID order of the stored parts, but it occurs to search by ID. _anmRes.PartList[partBase.MyId] = partBase; //_anmRes.PartList.Add(partBase); } // Debug.Log("_anmRes.PartList.Count: " + _anmRes.PartList.Count); #if false // precalculate values to inherit parent's one. if (PrecalcAttrValues) { //foreach (var e in _anmRes.PartList.Values) // for Dictionary version foreach (var e in _anmRes.PartList) { if (!e.IsRoot && e.HasParent) { e.CalcInheritedValues(_anmRes.PartList[e.ParentId]); } } } #endif return(true); }