示例#1
0
        private void CategoryTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentEmitter?.AudioEmitter == null)
            {
                return;
            }

            uint   hash = 0;
            string name = CategoryTextBox.Text;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            //HashLabel.Text = "Hash: " + hash.ToString();

            if (CurrentEmitter.AudioEmitter.Category != hash)
            {
                CurrentEmitter.AudioEmitter.Category = hash;

                ProjectItemChanged();
            }
        }
        private void Unk14TextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentRoom == null)
            {
                return;
            }

            uint   hash = 0;
            string name = Unk14TextBox.Text;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            //NameHashLabel.Text = "Hash: " + hash.ToString();

            if (CurrentRoom.Unk14 != hash)
            {
                CurrentRoom.Unk14 = hash;

                ProjectItemChanged();
            }
        }
示例#3
0
        private void Hash1TextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentZone?.AudioZone == null)
            {
                return;
            }

            var  hashstr = Hash1TextBox.Text;
            uint hash    = 0;

            if (!uint.TryParse(hashstr, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(hashstr);
                JenkIndex.Ensure(hashstr);
            }

            if (CurrentZone.AudioZone.UnkHash1 != hash)
            {
                CurrentZone.AudioZone.UnkHash1 = hash;

                ProjectItemChanged();
            }
        }
示例#4
0
        private void NameTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentZone?.AudioZone == null)
            {
                return;
            }

            uint   hash = 0;
            string name = NameTextBox.Text;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            //NameHashLabel.Text = "Hash: " + hash.ToString();

            if (CurrentZone.AudioZone.NameHash != hash)
            {
                CurrentZone.AudioZone.Name     = NameTextBox.Text;
                CurrentZone.AudioZone.NameHash = hash;

                ProjectItemChanged();
                UpdateFormTitle();
            }
        }
示例#5
0
        private void HashesTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentZone?.AudioZone == null)
            {
                return;
            }

            var hashstrs = HashesTextBox.Text.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

            if (hashstrs?.Length > 0)
            {
                var hashlist = new List <MetaHash>();
                foreach (var hashstr in hashstrs)
                {
                    uint hash = 0;
                    if (!uint.TryParse(hashstr, out hash))//don't re-hash hashes
                    {
                        hash = JenkHash.GenHash(hashstr);
                        JenkIndex.Ensure(hashstr);
                    }
                    hashlist.Add(hash);
                }

                CurrentZone.AudioZone.Rules      = hashlist.ToArray();
                CurrentZone.AudioZone.RulesCount = (byte)hashlist.Count;

                ProjectItemChanged();
            }
        }
示例#6
0
        private void YmapNameTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            uint   hash = 0;
            string name = YmapNameTextBox.Text;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            YmapNameHashLabel.Text = "Hash: " + hash.ToString();

            if (Ymap != null)
            {
                lock (ProjectForm.ProjectSyncRoot)
                {
                    Ymap.SetName(name);
                    if (!File.Exists(Ymap.FilePath))
                    {
                        Ymap.FilePath = name;
                    }
                    SetYmapHasChanged(true);
                    UpdateFormTitle();
                }
            }
        }
示例#7
0
文件: YtdForm.cs 项目: q4a/CodeWalker
        private Texture OpenDDSFile()
        {
            if (OpenDDSFileDialog.ShowDialog() != DialogResult.OK)
            {
                return(null);
            }

            var fn = OpenDDSFileDialog.FileName;

            if (!File.Exists(fn))
            {
                return(null);                  //couldn't find file?
            }
            try
            {
                var dds = File.ReadAllBytes(fn);
                var tex = DDSIO.GetTexture(dds);
                tex.Name     = Path.GetFileNameWithoutExtension(fn);
                tex.NameHash = JenkHash.GenHash(tex.Name?.ToLowerInvariant());
                JenkIndex.Ensure(tex.Name?.ToLowerInvariant());
                return(tex);
            }
            catch
            {
                MessageBox.Show("Unable to load " + fn + ".\nAre you sure it's a valid .dds file?");
            }

            return(null);
        }
示例#8
0
        private void YmapNameTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            uint   hash = 0;
            string name = YmapNameTextBox.Text;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            YmapNameHashLabel.Text = "Hash: " + hash.ToString();

            if (Ymap != null)
            {
                lock (ProjectForm.ProjectSyncRoot)
                {
                    string ymname = name + ".ymap";
                    if (Ymap.Name != ymname)
                    {
                        Ymap.Name           = ymname;
                        Ymap._CMapData.name = new MetaHash(hash);
                        SetYmapHasChanged(true);
                        UpdateFormTitle();
                    }
                }
            }
        }
示例#9
0
        public void Init(XmlNode node)
        {
            Dict = new Dictionary <string, TimecycleModValue>();

            name      = Xml.GetStringAttribute(node, "name");
            numMods   = Xml.GetIntAttribute(node, "numMods");
            userFlags = Xml.GetIntAttribute(node, "userFlags");

            string namel = name.ToLowerInvariant();

            JenkIndex.Ensure(namel);
            nameHash = JenkHash.GenHash(namel);

            List <TimecycleModValue> vals = new List <TimecycleModValue>();

            foreach (XmlNode valnode in node.ChildNodes)
            {
                if (!(valnode is XmlElement))
                {
                    continue;
                }

                TimecycleModValue val = new TimecycleModValue();
                val.Init(valnode);

                vals.Add(val);
                Dict[val.name] = val;
            }
            Values = vals.ToArray();
        }
示例#10
0
        public void LoadRel(RelFile rel)
        {
            fileName = rel?.Name;
            if (string.IsNullOrEmpty(fileName))
            {
                fileName = rel?.FileEntry?.Name;
            }

            UpdateFormTitle();

            RelPropertyGrid.SelectedObject = rel;


            StringBuilder sb = new StringBuilder();

            if (rel != null)
            {
                if (rel.NameTable != null)
                {
                    sb.AppendLine("NameTable - " + rel.NameTable.Length.ToString() + " entries");
                    foreach (var name in rel.NameTable)
                    {
                        sb.AppendLine(name);
                    }
                    sb.AppendLine();
                }
                if (rel.IndexStrings != null)
                {
                    sb.AppendLine("IndexStrings - " + rel.IndexStrings.Length.ToString() + " entries");
                    foreach (var rstr in rel.IndexStrings)
                    {
                        sb.AppendLine(rstr.Name);
                    }
                    sb.AppendLine();
                }
                if (rel.IndexHashes != null)
                {
                    sb.AppendLine("IndexHashes - " + rel.IndexHashes.Length.ToString() + " entries");
                    foreach (var rhash in rel.IndexHashes)
                    {
                        uint h    = rhash.Name;
                        var  jstr = JenkIndex.TryGetString(h);
                        if (!string.IsNullOrEmpty(jstr))
                        {
                            sb.AppendLine(jstr);
                        }
                        else
                        {
                            sb.AppendLine("0x" + h.ToString("X").PadLeft(8, '0'));
                        }
                    }
                    sb.AppendLine();
                }
            }
            MainTextBox.Text = sb.ToString();
        }
示例#11
0
        private void EntityArchetypeTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentEntity == null)
            {
                return;
            }
            uint   hash = 0;
            string name = EntityArchetypeTextBox.Text;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            EntityArchetypeHashLabel.Text = "Hash: " + hash.ToString();

            var arch = ProjectForm.GameFileCache.GetArchetype(hash);

            if (arch == null)
            {
                EntityArchetypeHashLabel.Text += " (not found)";
            }

            if (CurrentEntity != null)
            {
                lock (ProjectForm.ProjectSyncRoot)
                {
                    CurrentEntity._CEntityDef.archetypeName = new MetaHash(hash);

                    if (CurrentMCEntity != null)
                    {
                        CurrentMCEntity._Data.archetypeName = new MetaHash(hash);
                    }

                    if (CurrentEntity.Archetype != arch)
                    {
                        CurrentEntity.SetArchetype(arch);

                        if (CurrentEntity.IsMlo)
                        {
                            CurrentEntity.MloInstance.InitYmapEntityArchetypes(ProjectForm.GameFileCache);
                        }

                        ProjectItemChanged();
                    }
                }
            }

            ProjectForm.ProjectExplorer?.UpdateEntityTreeNode(CurrentEntity);
        }
示例#12
0
        // from CodeWalker
        private MetaHash GetMetaHash2(string name)
        {
            uint hash = 0;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }

            return(hash);
        }
示例#13
0
        public YmapFile AddYmapFile(string filename)
        {
            YmapFile ymap = new YmapFile();

            ymap.RpfFileEntry      = new RpfResourceFileEntry();
            ymap.RpfFileEntry.Name = new FileInfo(filename).Name;
            ymap.FilePath          = GetFullFilePath(filename);
            ymap.Name = ymap.RpfFileEntry.Name;
            JenkIndex.Ensure(ymap.Name);
            JenkIndex.Ensure(Path.GetFileNameWithoutExtension(ymap.Name));
            JenkIndex.Ensure(filename);
            if (!AddYmapFile(ymap))
            {
                return(null);
            }
            return(ymap);
        }
示例#14
0
        public YtypFile AddYtypFile(string filename)
        {
            YtypFile ytyp = new YtypFile();

            ytyp.RpfFileEntry      = new RpfResourceFileEntry();
            ytyp.RpfFileEntry.Name = Path.GetFileName(filename);
            ytyp.FilePath          = GetFullFilePath(filename);
            ytyp.Name = ytyp.RpfFileEntry.Name;
            JenkIndex.Ensure(ytyp.Name);
            JenkIndex.Ensure(Path.GetFileNameWithoutExtension(ytyp.Name));
            JenkIndex.Ensure(filename);
            if (!AddYtypFile(ytyp))
            {
                return(null);
            }
            return(ytyp);
        }
示例#15
0
        private void ExtParamsTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentEmitter?.AudioEmitter == null)
            {
                return;
            }

            var paramstrs = ExtParamsTextBox.Text.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

            if (paramstrs?.Length > 0)
            {
                var paramlist = new List <Dat151AmbientRule.ExtParam>();
                foreach (var paramstr in paramstrs)
                {
                    var paramvals = paramstr.Split(',');
                    if (paramvals?.Length == 3)
                    {
                        var  param   = new Dat151AmbientRule.ExtParam();
                        var  hashstr = paramvals[0].Trim();
                        var  valstr  = paramvals[1].Trim();
                        var  flgstr  = paramvals[2].Trim();
                        uint hash    = 0;
                        if (!uint.TryParse(hashstr, out hash))//don't re-hash hashes
                        {
                            hash = JenkHash.GenHash(hashstr);
                            JenkIndex.Ensure(hashstr);
                        }
                        uint flags = 0;
                        uint.TryParse(flgstr, out flags);
                        param.Hash  = hash;
                        param.Value = FloatUtil.Parse(valstr);
                        param.Flags = flags;
                        paramlist.Add(param);
                    }
                }

                CurrentEmitter.AudioEmitter.ExtParams      = paramlist.ToArray();
                CurrentEmitter.AudioEmitter.ExtParamsCount = (ushort)paramlist.Count;

                ProjectItemChanged();
            }
        }
示例#16
0
        private void CarPopGroupTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentCarGen == null)
            {
                return;
            }
            uint   hash = 0;
            string name = CarPopGroupTextBox.Text;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            CarPopGroupHashLabel.Text = "Hash: " + hash.ToString();

            //var grp = GameFileCache.GetCarPopGroup(hash); //todo: something like this for popgroup info?
            //if (grp == null)
            //{
            //    CarPopGroupHashLabel.Text += " (not found)";
            //}

            if (CurrentCarGen != null)
            {
                lock (ProjectForm.ProjectSyncRoot)
                {
                    var pghash = new MetaHash(hash);
                    if (CurrentCarGen._CCarGen.popGroup != pghash)
                    {
                        CurrentCarGen._CCarGen.popGroup = pghash;
                        ProjectForm.SetYmapHasChanged(true);
                    }
                }
            }

            ProjectForm.ProjectExplorer?.UpdateCarGenTreeNode(CurrentCarGen);
        }
示例#17
0
        private void YmapParentTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            uint   hash = 0;
            string name = YmapParentTextBox.Text;

            if (!uint.TryParse(name, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(name);
                JenkIndex.Ensure(name);
            }
            YmapParentHashLabel.Text = "Hash: " + hash.ToString();

            if (hash != 0)
            {
                var entry = ProjectForm.FindParentYmapEntry(hash);
                if (entry == null)
                {
                    YmapParentHashLabel.Text += " (not found!)";
                }
            }

            if (Ymap != null)
            {
                lock (ProjectForm.ProjectSyncRoot)
                {
                    if (Ymap._CMapData.parent.Hash != hash)
                    {
                        Ymap._CMapData.parent = new MetaHash(hash);
                        SetYmapHasChanged(true);

                        //TODO: confirm entity parent linkage?
                    }
                }
            }
        }
示例#18
0
        private void SaveStringsButton_Click(object sender, EventArgs e)
        {
            if (SaveFileDialog.ShowDialog(this) != DialogResult.OK)
            {
                return;
            }

            string file = SaveFileDialog.FileName;

            try
            {
                string[] lines = JenkIndex.GetAllStrings();

                File.WriteAllLines(file, lines);

                MessageBox.Show(lines.Length.ToString() + " strings exported successfully.");
            }
            catch
            {
                MessageBox.Show("Error saving strings file.");
            }
        }
        private void Timecycle2TextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentRoom == null)
            {
                return;
            }

            var hash = JenkHash.GenHash(Timecycle2TextBox.Text);

            lock (ProjectForm.ProjectSyncRoot)
            {
                if (CurrentRoom._Data.secondaryTimecycleName != hash)
                {
                    CurrentRoom._Data.secondaryTimecycleName = hash;
                    ProjectForm.SetYtypHasChanged(true);
                    JenkIndex.Ensure(Timecycle2TextBox.Text);
                }
            }
        }
        private void EntitySetNameTextBox_TextChanged(object sender, EventArgs e)
        {
            if (populatingui)
            {
                return;
            }
            if (CurrentEntitySet == null)
            {
                return;
            }

            var str = EntitySetNameTextBox.Text;

            if (CurrentEntitySet.Name != str)
            {
                uint h = 0;
                if (uint.TryParse(str, out h))
                {
                    CurrentEntitySet._Data.name = h;
                }
                else
                {
                    JenkIndex.Ensure(str);
                    CurrentEntitySet._Data.name = JenkHash.GenHash(str);
                }

                TreeNode tn = ProjectForm.ProjectExplorer?.FindMloEntitySetTreeNode(CurrentEntitySet);
                if (tn != null)
                {
                    tn.Text = CurrentEntitySet.Name;
                }

                UpdateFormTitle();
                ProjectForm.SetYtypHasChanged(true);
            }
        }
示例#21
0
        private TreeNode AddFileNode(RpfFile file, TreeNode n)
        {
            var      nodes = (n == null) ? MainTreeView.Nodes : n.Nodes;
            TreeNode node  = nodes.Add(file.Path);

            node.Tag = file;

            foreach (RpfEntry entry in file.AllEntries)
            {
                if (entry is RpfFileEntry)
                {
                    bool show = !entry.NameLower.EndsWith(".rpf"); //rpf entries get their own root node..
                    if (show)
                    {
                        //string text = entry.Path.Substring(file.Path.Length + 1); //includes \ on the end
                        //TreeNode cnode = node.Nodes.Add(text);
                        //cnode.Tag = entry;
                        TreeNode cnode = AddEntryNode(entry, node);
                    }
                }
            }


            //make sure it's all in jenkindex...
            JenkIndex.Ensure(file.Name);
            foreach (RpfEntry entry in file.AllEntries)
            {
                if (string.IsNullOrEmpty(entry.Name))
                {
                    continue;
                }
                JenkIndex.Ensure(entry.Name);
                JenkIndex.Ensure(entry.NameLower);
                int ind = entry.Name.LastIndexOf('.');
                if (ind > 0)
                {
                    JenkIndex.Ensure(entry.Name.Substring(0, ind));
                    JenkIndex.Ensure(entry.NameLower.Substring(0, ind));
                }
            }

            return(node);



            //TreeNode lastNode = null;
            //string subPathAgg;
            //subPathAgg = string.Empty;
            //foreach (string subPath in file.Path.Split('\\'))
            //{
            //    subPathAgg += subPath + '\\';
            //    TreeNode[] nodes = MainTreeView.Nodes.Find(subPathAgg, true);
            //    if (nodes.Length == 0)
            //    {
            //        if (lastNode == null)
            //        {
            //            lastNode = MainTreeView.Nodes.Add(subPathAgg, subPath);
            //        }
            //        else
            //        {
            //            lastNode = lastNode.Nodes.Add(subPathAgg, subPath);
            //        }
            //    }
            //    else
            //    {
            //        lastNode = nodes[0];
            //    }
            //}
            //lastNode.Tag = file;
        }
示例#22
0
        private bool SaveToRPF(string txt)
        {
            if (!(exploreForm?.EditMode ?? false))
            {
                return(false);
            }
            if (rpfFileEntry?.Parent == null)
            {
                return(false);
            }

            byte[] data = null;

            if (fileType == TextFileType.Text)
            {
                data = Encoding.UTF8.GetBytes(txt);
            }
            else if (fileType == TextFileType.GXT2)
            {
                var gxt = Gxt2File.FromText(txt);
                data = gxt.Save();
            }
            else if (fileType == TextFileType.Nametable)
            {
                if (!txt.EndsWith("\n"))
                {
                    txt = txt + "\n";
                }
                txt = txt.Replace("\r", "");
                var lines = txt.Split('\n');
                foreach (var line in lines)
                {
                    var str = line.Trim();
                    if (string.IsNullOrEmpty(str))
                    {
                        continue;
                    }
                    var strl = str.ToLowerInvariant();
                    JenkIndex.Ensure(strl);
                }
                data = Encoding.UTF8.GetBytes(txt.Replace('\n', '\0'));
            }

            if (data == null)
            {
                MessageBox.Show("Unspecified error - data was null!", "Cannot save file");
                return(false);
            }

            if (!rpfFileEntry.Path.ToLowerInvariant().StartsWith("mods"))
            {
                if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes)
                {
                    return(false);//that was a close one
                }
            }

            try
            {
                if (!(exploreForm?.EnsureRpfValidEncryption(rpfFileEntry.File) ?? false))
                {
                    return(false);
                }

                var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data);
                if (newentry != rpfFileEntry)
                {
                }
                rpfFileEntry = newentry;

                exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer...

                modified = false;

                StatusLabel.Text = fileType.ToString() + " file saved successfully at " + DateTime.Now.ToString();

                return(true); //victory!
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error saving file to RPF! The RPF archive may be corrupted...\r\n" + ex.ToString(), "Really Bad Error");
            }

            return(false);
        }
示例#23
0
        public byte[] BuildYcd()
        {
            YcdFile ycd = new YcdFile();

            ycd.ClipDictionary = new ClipDictionary();


            var clipmap = new List <ClipMapEntry>();
            var animmap = new List <AnimationMapEntry>();

            foreach (var onim in OnimFiles)
            {
                var anim = new Animation();
                anim.Hash               = JenkHash.GenHash(onim.Name.ToLowerInvariant());
                anim.Frames             = (ushort)onim.Frames;
                anim.SequenceFrameLimit = (ushort)onim.SequenceFrameLimit;
                anim.Duration           = onim.Duration;

                JenkIndex.Ensure(onim.Name.ToLowerInvariant());//just to make it nicer to debug really

                bool isUV          = false;
                bool hasRootMotion = false;
                var  boneIds       = new List <AnimationBoneId>();
                var  seqs          = new List <Sequence>();
                var  aseqs         = new List <List <AnimSequence> >();
                foreach (var oseq in onim.SequenceList)
                {
                    var boneid = new AnimationBoneId();
                    boneid.BoneId = (ushort)oseq.BoneID; //TODO: bone ID mapping
                    boneid.Unk0   = 0;                   //what to use here?
                    switch (oseq.Track)
                    {
                    case "BonePosition": boneid.Track = 0;
                        break;

                    case "BoneRotation": boneid.Track = 1;
                        break;

                    case "ModelPosition": boneid.Track = 5; hasRootMotion = true;
                        break;

                    case "ModelRotation": boneid.Track = 6; hasRootMotion = true;
                        break;

                    case "UV0": boneid.Track = 17; isUV = true;
                        break;

                    case "UV1": boneid.Track = 18; isUV = true;
                        break;

                    case "LightColor": boneid.Track = 0;    //what should this be?
                        break;

                    case "LightRange": boneid.Track = 0;    //what should this be?
                        break;

                    case "LightIntensity1": boneid.Track = 0;    //what should this be?
                        break;

                    case "LightIntensity2": boneid.Track = 0;    //what should this be?
                        break;

                    case "LightDirection": boneid.Track = 0;    //what should this be?
                        break;

                    case "Type21": boneid.Track = 0;    //what should this be?
                        break;

                    case "CameraPosition": boneid.Track = 7;
                        break;

                    case "CameraRotation": boneid.Track = 8;
                        break;

                    case "CameraFOV": boneid.Track = 0;    //what should this be?
                        break;

                    case "CameraDof": boneid.Track = 0;    //what should this be?
                        break;

                    case "CameraMatrixRotateFactor": boneid.Track = 0;    //what should this be?
                        break;

                    case "CameraControl": boneid.Track = 0;    //what should this be?
                        break;

                    case "ActionFlags":    //not sure what this is for? just ignore it for now
                        continue;

                    default:
                        break;
                    }
                    boneIds.Add(boneid);

                    for (int i = 0; i < oseq.FramesData.Count; i++)
                    {
                        var framesData = oseq.FramesData[i];
                        if (i > 0)
                        {
                        }
                        Sequence            seq      = null;
                        List <AnimSequence> aseqlist = null;
                        while (i >= seqs.Count)
                        {
                            seq = new Sequence();
                            seqs.Add(seq);
                            aseqlist = new List <AnimSequence>();
                            aseqs.Add(aseqlist);
                        }
                        seq      = seqs[i];
                        aseqlist = aseqs[i];


                        var chanlist = new List <AnimChannel>();
                        if (framesData.IsStatic)
                        {
                            var vals = (framesData.Channels.Count > 0) ? framesData.Channels[0].Values : null;
                            if (vals != null)
                            {
                                if (vals.Length == 1)
                                {
                                    var acsf = new AnimChannelStaticFloat();
                                    acsf.Value = vals[0];
                                    chanlist.Add(acsf);
                                }
                                else if (vals.Length == 3)
                                {
                                    var acsv = new AnimChannelStaticVector3();
                                    acsv.Value = new Vector3(vals[0], vals[1], vals[2]);
                                    chanlist.Add(acsv);
                                }
                                else if (vals.Length == 4)
                                {
                                    var acsq = new AnimChannelStaticQuaternion();
                                    acsq.Value = new Quaternion(vals[0], vals[1], vals[2], vals[3]);
                                    chanlist.Add(acsq);
                                }
                                else
                                {
                                }
                            }
                            else
                            {
                            }
                        }
                        else
                        {
                            int chanCount = framesData.Channels.Count;

                            for (int c = 0; c < chanCount; c++)
                            {
                                var ochan = framesData.Channels[c];
                                var vals  = ochan.Values;
                                if (vals.Length == 1)//static channel...
                                {
                                    var acsf = new AnimChannelStaticFloat();
                                    acsf.Value = vals[0];
                                    chanlist.Add(acsf);
                                }
                                else //if (vals.Length == onim.Frames)
                                {
                                    float minval   = float.MaxValue;
                                    float maxval   = float.MinValue;
                                    float lastval  = 0;
                                    float mindelta = float.MaxValue;
                                    foreach (var val in vals)
                                    {
                                        minval = Math.Min(minval, val);
                                        maxval = Math.Max(maxval, val);
                                        if (val != lastval)
                                        {
                                            float adelta = Math.Abs(val - lastval);
                                            mindelta = Math.Min(mindelta, adelta);
                                        }
                                        lastval = val;
                                    }
                                    if (mindelta == float.MaxValue)
                                    {
                                        mindelta = 0;
                                    }
                                    float range    = maxval - minval;
                                    float minquant = range / 1048576.0f;
                                    float quantum  = Math.Max(mindelta, minquant);

                                    var acqf = new AnimChannelQuantizeFloat();
                                    acqf.Values  = vals;
                                    acqf.Offset  = minval;
                                    acqf.Quantum = quantum;
                                    chanlist.Add(acqf);
                                }
                            }

                            if (chanCount == 4)
                            {
                                //assume it's a quaternion... add the extra quaternion channel
                                var acq1 = new AnimChannelCachedQuaternion(AnimChannelType.CachedQuaternion2);
                                acq1.QuatIndex = 3;//what else should it be?
                                chanlist.Add(acq1);
                            }
                        }

                        if (chanlist.Count == 4)
                        {
                        }  //shouldn't happen

                        AnimSequence aseq = new AnimSequence();
                        aseq.Channels = chanlist.ToArray();

                        aseqlist.Add(aseq);
                    }
                }

                int remframes = anim.Frames;
                for (int i = 0; i < seqs.Count; i++)
                {
                    var seq      = seqs[i];
                    var aseqlist = aseqs[i];

                    seq.Unknown_00h = 0;//what to set this???
                    seq.NumFrames   = (ushort)Math.Max(Math.Min(anim.SequenceFrameLimit, remframes), 0);
                    seq.Sequences   = aseqlist.ToArray();

                    seq.AssociateSequenceChannels();

                    remframes -= anim.SequenceFrameLimit;
                }


                anim.BoneIds            = new ResourceSimpleList64_s <AnimationBoneId>();
                anim.BoneIds.data_items = boneIds.ToArray();

                anim.Sequences            = new ResourcePointerList64 <Sequence>();
                anim.Sequences.data_items = seqs.ToArray();

                anim.Unknown_10h = hasRootMotion ? (byte)16 : (byte)0;
                anim.Unknown_1Ch = 0; //???

                anim.AssignSequenceBoneIds();



                var cliphash = anim.Hash;
                if (isUV)
                {
                    var name  = onim.Name.ToLowerInvariant();
                    var uvind = name.IndexOf("_uv_");
                    if (uvind < 0)
                    {
                    }
                    var modelname = name.Substring(0, uvind);
                    var geoindstr = name.Substring(uvind + 4);
                    var geoind    = 0u;
                    uint.TryParse(geoindstr, out geoind);
                    cliphash = JenkHash.GenHash(modelname) + geoind + 1;
                }
                else
                {
                }


                var clip = new ClipAnimation();
                clip.Animation   = anim;
                clip.StartTime   = 0.0f;
                clip.EndTime     = anim.Duration;
                clip.Rate        = 1.0f;
                clip.Name        = "pack:/" + onim.Name + ".clip"; //pack:/name.clip
                clip.Unknown_30h = 0;                              //what's this then?
                clip.Properties  = new ClipPropertyMap();
                clip.Properties.CreatePropertyMap(null);           //TODO?
                clip.Tags = new ClipTagList();                     //TODO?

                var cme = new ClipMapEntry();
                cme.Clip = clip;
                cme.Hash = cliphash;
                clipmap.Add(cme);

                var ame = new AnimationMapEntry();
                ame.Hash      = anim.Hash;//is this right? what else to use?
                ame.Animation = anim;
                animmap.Add(ame);
            }


            ycd.ClipDictionary.CreateClipsMap(clipmap.ToArray());
            ycd.ClipDictionary.CreateAnimationsMap(animmap.ToArray());

            ycd.ClipDictionary.BuildMaps();
            ycd.ClipDictionary.UpdateUsageCounts();
            ycd.InitDictionaries();

            byte[] data = ycd.Save();
            return(data);
        }
示例#24
0
        private void Search()
        {
            SearchResultsGrid.SelectedObject = null;

            if (CurrentFile?.RelDatasSorted == null)
            {
                return;
            }


            bool textsearch = SearchTextRadio.Checked;
            var  text       = SearchTextBox.Text;
            var  textl      = text.ToLowerInvariant();

            uint hash  = 0;
            uint hashl = 0;

            if (!uint.TryParse(text, out hash))//don't re-hash hashes
            {
                hash = JenkHash.GenHash(text);
                JenkIndex.Ensure(text);
                hashl = JenkHash.GenHash(textl);
                JenkIndex.Ensure(textl);
            }
            else
            {
                hashl = hash;
            }


            var results = new List <RelData>();

            foreach (var rd in CurrentFile.RelDatasSorted)
            {
                if (textsearch)
                {
                    if (((rd.Name?.ToLowerInvariant().Contains(textl)) ?? false) || (rd.NameHash == hash) || (rd.NameHash == hashl) ||
                        (rd.NameHash.ToString().ToLowerInvariant().Contains(textl)))
                    {
                        results.Add(rd);
                    }
                }
                else
                {
                    if ((rd.NameHash == hash) || (rd.NameHash == hashl))
                    {
                        SearchResultsGrid.SelectedObject = rd;
                        return;
                    }
                }
            }

            if (textsearch && (results.Count > 0))
            {
                SearchResultsGrid.SelectedObject = results.ToArray();
            }
            else
            {
                SearchResultsGrid.SelectedObject = null;
            }
        }
示例#25
0
        private string GetRelDataTitleString(RelData item)
        {
            if (item == null)
            {
                return("");
            }
            var h   = item.NameHash;
            var str = JenkIndex.TryGetString(h);

            if (string.IsNullOrEmpty(str))
            {
                str = GlobalText.TryGetString(h);                           //is this necessary?
            }
            if (string.IsNullOrEmpty(str))
            {
                MetaNames.TryGetString(h, out str);
            }
            if (string.IsNullOrEmpty(str))
            {
                str = h.Hex;
            }
            var typeid = item.TypeID.ToString();
            var rel    = item.Rel;

            if (rel != null)
            {
                switch (rel.RelType)
                {
                case RelDatFileType.Dat54DataEntries:
                    typeid = ((Dat54SoundType)item.TypeID).ToString();
                    break;

                case RelDatFileType.Dat149:
                case RelDatFileType.Dat150:
                case RelDatFileType.Dat151:
                    typeid = ((Dat151RelType)item.TypeID).ToString();
                    break;

                case RelDatFileType.Dat4:
                    if (rel.IsAudioConfig)
                    {
                        typeid = ((Dat4ConfigType)item.TypeID).ToString();
                    }
                    else
                    {
                        typeid = ((Dat4SpeechType)item.TypeID).ToString();
                    }
                    break;

                case RelDatFileType.Dat10ModularSynth:
                    typeid = ((Dat10RelType)item.TypeID).ToString();
                    break;

                case RelDatFileType.Dat15DynamicMixer:
                    typeid = ((Dat15RelType)item.TypeID).ToString();
                    break;

                case RelDatFileType.Dat16Curves:
                    typeid = ((Dat16RelType)item.TypeID).ToString();
                    break;

                case RelDatFileType.Dat22Categories:
                    typeid = ((Dat22RelType)item.TypeID).ToString();
                    break;

                default:
                    break;
                }
            }
            return(str + " : " + typeid);
        }
示例#26
0
        public void UpdateYmapUI()
        {
            if (Ymap == null)
            {
                YmapNameTextBox.Text                 = "<No ymap selected>";
                YmapNameHashLabel.Text               = "Hash: 0";
                YmapParentTextBox.Text               = string.Empty;
                YmapParentHashLabel.Text             = "Hash: 0";
                YmapFlagsTextBox.Text                = string.Empty;
                YmapContentFlagsTextBox.Text         = string.Empty;
                YmapCFlagsHDCheckBox.Checked         = false;
                YmapCFlagsLODCheckBox.Checked        = false;
                YmapCFlagsSLOD2CheckBox.Checked      = false;
                YmapCFlagsInteriorCheckBox.Checked   = false;
                YmapCFlagsSLODCheckBox.Checked       = false;
                YmapCFlagsOcclusionCheckBox.Checked  = false;
                YmapCFlagsPhysicsCheckBox.Checked    = false;
                YmapCFlagsLODLightsCheckBox.Checked  = false;
                YmapCFlagsDistLightsCheckBox.Checked = false;
                YmapCFlagsCriticalCheckBox.Checked   = false;
                YmapCFlagsGrassCheckBox.Checked      = false;
                YmapFlagsScriptedCheckBox.Checked    = false;
                YmapFlagsLODCheckBox.Checked         = false;
                YmapPhysicsDictionariesTextBox.Text  = string.Empty;
                YmapEntitiesExtentsMinTextBox.Text   = string.Empty;
                YmapEntitiesExtentsMaxTextBox.Text   = string.Empty;
                YmapStreamingExtentsMinTextBox.Text  = string.Empty;
                YmapStreamingExtentsMaxTextBox.Text  = string.Empty;
                YmapFileLocationTextBox.Text         = string.Empty;
                YmapProjectPathTextBox.Text          = string.Empty;
            }
            else
            {
                populatingui = true;
                var md = Ymap.CMapData;
                if (md.name.Hash == 0)
                {
                    string name = Path.GetFileNameWithoutExtension(Ymap.Name);
                    JenkIndex.Ensure(name);
                    md.name = new MetaHash(JenkHash.GenHash(name));
                }

                var project = ProjectForm?.CurrentProjectFile;

                YmapNameTextBox.Text                = md.name.ToString();
                YmapNameHashLabel.Text              = "Hash: " + md.name.Hash.ToString();
                YmapParentTextBox.Text              = md.parent.ToString();
                YmapParentHashLabel.Text            = "Hash: " + md.parent.Hash.ToString();
                YmapEntitiesExtentsMinTextBox.Text  = FloatUtil.GetVector3String(md.entitiesExtentsMin);
                YmapEntitiesExtentsMaxTextBox.Text  = FloatUtil.GetVector3String(md.entitiesExtentsMax);
                YmapStreamingExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMin);
                YmapStreamingExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMax);
                YmapFileLocationTextBox.Text        = Ymap.RpfFileEntry?.Path ?? Ymap.FilePath;
                YmapProjectPathTextBox.Text         = (project != null) ? project.GetRelativePath(Ymap.FilePath) : Ymap.FilePath;

                UpdateYmapFlagsUI(true, true);

                UpdateYmapPhysicsDictionariesUI();

                populatingui = false;

                ////struct CMapData:
                //MetaHash name { get; set; } //8   8: Hash: 0: name
                //MetaHash parent { get; set; } //12   12: Hash: 0: parent
                //uint flags { get; set; } //16   16: UnsignedInt: 0: flags
                //uint contentFlags { get; set; } //20   20: UnsignedInt: 0: contentFlags//1785155637
                //Vector3 streamingExtentsMin { get; set; } //32   32: Float_XYZ: 0: streamingExtentsMin//3710026271
                //Vector3 streamingExtentsMax { get; set; } //48   48: Float_XYZ: 0: streamingExtentsMax//2720965429
                //Vector3 entitiesExtentsMin { get; set; } //64   64: Float_XYZ: 0: entitiesExtentsMin//477478129
                //Vector3 entitiesExtentsMax { get; set; } //80   80: Float_XYZ: 0: entitiesExtentsMax//1829192759
                //Array_StructurePointer entities { get; set; } //96   96: Array: 0: entities  {0: StructurePointer: 0: 256}
                //Array_Structure containerLods { get; set; } //112   112: Array: 0: containerLods//2935983381  {0: Structure: 372253349: 256}
                //Array_Structure boxOccluders { get; set; } //128   128: Array: 0: boxOccluders//3983590932  {0: Structure: SectionUNKNOWN7: 256}
                //Array_Structure occludeModels { get; set; } //144   144: Array: 0: occludeModels//2132383965  {0: Structure: SectionUNKNOWN5: 256}
                //Array_uint physicsDictionaries { get; set; } //160   160: Array: 0: physicsDictionaries//949589348  {0: Hash: 0: 256}
                //rage__fwInstancedMapData instancedData { get; set; } //176   176: Structure: rage__fwInstancedMapData: instancedData//2569067561
                //Array_Structure timeCycleModifiers { get; set; } //224   224: Array: 0: timeCycleModifiers  {0: Structure: CTimeCycleModifier: 256}
                //Array_Structure carGenerators { get; set; } //240   240: Array: 0: carGenerators//3254823756  {0: Structure: CCarGen: 256}
                //CLODLight LODLightsSOA { get; set; } //256   256: Structure: CLODLight: LODLightsSOA//1774371066
                //CDistantLODLight DistantLODLightsSOA { get; set; } //392   392: Structure: CDistantLODLight: DistantLODLightsSOA//2954466641
                //CBlockDesc block { get; set; } //440   440: Structure: CBlockDesc//3072355914: block
            }
        }
示例#27
0
        public void LoadRel(RelFile rel)
        {
            fileName = rel?.Name;
            if (string.IsNullOrEmpty(fileName))
            {
                fileName = rel?.RpfFileEntry?.Name;
            }

            UpdateFormTitle();

            RelPropertyGrid.SelectedObject = rel;

            CurrentFile = rel;

            rpfFileEntry = rel?.RpfFileEntry;

            Xml = RelXml.GetXml(rel);

            metaFormat = MetaFormat.AudioRel;

            StringBuilder sb = new StringBuilder();

            if (rel != null)
            {
                if (rel.NameTable != null)
                {
                    sb.AppendLine("NameTable - " + rel.NameTable.Length.ToString() + " entries");
                    foreach (var name in rel.NameTable)
                    {
                        sb.AppendLine(name);
                    }
                    sb.AppendLine();
                }
                if (rel.IndexStrings != null)
                {
                    sb.AppendLine("IndexStrings - " + rel.IndexStrings.Length.ToString() + " entries");
                    foreach (var rstr in rel.IndexStrings)
                    {
                        sb.AppendLine(rstr.Name);
                    }
                    sb.AppendLine();
                }
                if (rel.IndexHashes != null)
                {
                    sb.AppendLine("IndexHashes - " + rel.IndexHashes.Length.ToString() + " entries");
                    foreach (var rhash in rel.IndexHashes)
                    {
                        uint h    = rhash.Name;
                        var  jstr = JenkIndex.TryGetString(h);
                        if (!string.IsNullOrEmpty(jstr))
                        {
                            sb.AppendLine(jstr);
                        }
                        else
                        {
                            sb.AppendLine("0x" + h.ToString("X").PadLeft(8, '0'));
                        }
                    }
                    sb.AppendLine();
                }
            }
            MainTextBox.Text = sb.ToString();


            SynthsComboBox.Items.Clear();
            SynthTextBox.Language = Language.Custom;
            SynthTextBox.Text     = "";
            SynthTextBox.ClearUndo();
            if (rel.RelType == RelDatFileType.Dat10ModularSynth)
            {
                foreach (var relData in rel.RelDatasSorted)
                {
                    if ((Dat10RelType)relData.TypeID == Dat10RelType.Synth)
                    {
                        SynthsComboBox.Items.Add(relData);
                    }
                }
            }
            else
            {
                MainTabControl.TabPages.Remove(SynthsTabPage);
            }
        }
示例#28
0
        private void FindHash()
        {
            uint   hash    = 0;
            string hashtxt = HashTextBox.Text;

            MatchTextBox.Text = "";
            if (HexRadioButton.Checked)
            {
                try
                {
                    hash = Convert.ToUInt32(hashtxt, 16);
                }
                catch
                {
                    StatusLabel.Text = "Invalid hex value!";
                    return;
                }
            }
            else if (UnsignedRadioButton.Checked)
            {
                try
                {
                    hash = uint.Parse(hashtxt);
                }
                catch
                {
                    StatusLabel.Text = "Invalid unsigned int value!";
                    return;
                }
            }
            else if (SignedRadioButton.Checked)
            {
                try
                {
                    hash = (uint)int.Parse(hashtxt);
                }
                catch
                {
                    StatusLabel.Text = "Invalid signed int value!";
                    return;
                }
            }
            StatusLabel.Text = Convert.ToString(hash, 16).ToUpper().PadLeft(8, '0');


            var  str    = JenkIndex.TryGetString(hash);
            var  txt    = GlobalText.TryGetString(hash);
            var  sta    = StatsNames.TryGetString(hash);
            var  ext    = TryGetExtraString(hash);
            bool hasstr = !string.IsNullOrEmpty(str);
            bool hastxt = !string.IsNullOrEmpty(txt);
            bool hasext = !string.IsNullOrEmpty(ext);
            bool hassta = !string.IsNullOrEmpty(sta);

            if (hasstr && hastxt)
            {
                MatchTextBox.Text = string.Format("JenkIndex match:\r\n{0}\r\nGlobalText match:\r\n{1}", str, txt);
            }
            else if (hasstr)
            {
                MatchTextBox.Text = str;
            }
            else if (hastxt)
            {
                MatchTextBox.Text = "GlobalText match:\r\n" + txt;
            }
            else if (hasext)
            {
                MatchTextBox.Text = "Extra strings match:\r\n" + ext;
            }
            else if (hassta)
            {
                MatchTextBox.Text = "Stats match:\r\n" + sta;
            }
            else
            {
                MatchTextBox.Text = "[No match found]";
            }
        }
示例#29
0
        public void LoadProjectTree(ProjectFile projectFile)
        {
            ProjectTreeView.Nodes.Clear();

            CurrentProjectFile = projectFile;
            if (CurrentProjectFile == null)
            {
                return;
            }

            var pcstr = CurrentProjectFile.HasChanged ? "*" : "";

            var projnode = ProjectTreeView.Nodes.Add(pcstr + CurrentProjectFile.Name);

            projnode.Tag = CurrentProjectFile;


            if (CurrentProjectFile.YmapFiles.Count > 0)
            {
                var ymapsnode = projnode.Nodes.Add("Ymap Files");
                ymapsnode.Name = "Ymap";

                foreach (var ymapfile in CurrentProjectFile.YmapFiles)
                {
                    var    ycstr = ymapfile.HasChanged ? "*" : "";
                    string name  = ymapfile.Name;
                    if (ymapfile.RpfFileEntry != null)
                    {
                        name = ymapfile.RpfFileEntry.Name;
                    }
                    var ymapnode = ymapsnode.Nodes.Add(ycstr + name);
                    ymapnode.Tag = ymapfile;

                    LoadYmapTreeNodes(ymapfile, ymapnode);

                    JenkIndex.Ensure(name);
                    JenkIndex.Ensure(Path.GetFileNameWithoutExtension(name));
                }
                ymapsnode.Expand();
            }

            if (CurrentProjectFile.YtypFiles.Count > 0)
            {
                var ytypsnode = projnode.Nodes.Add("Ytyp Files");
                ytypsnode.Name = "Ytyp";

                foreach (var ytypfile in CurrentProjectFile.YtypFiles)
                {
                    var    ycstr = ytypfile.HasChanged ? "*" : "";
                    string name  = ytypfile.Name;
                    if (ytypfile.RpfFileEntry != null)
                    {
                        name = ytypfile.RpfFileEntry.Name;
                    }
                    var ytypnode = ytypsnode.Nodes.Add(ycstr + name);
                    ytypnode.Tag = ytypfile;

                    LoadYtypTreeNodes(ytypfile, ytypnode);

                    JenkIndex.Ensure(name);
                    JenkIndex.Ensure(Path.GetFileNameWithoutExtension(name));
                }
                ytypsnode.Expand();
            }

            if (CurrentProjectFile.YndFiles.Count > 0)
            {
                var yndsnode = projnode.Nodes.Add("Ynd Files");
                yndsnode.Name = "Ynd";

                foreach (var yndfile in CurrentProjectFile.YndFiles)
                {
                    var    ycstr = yndfile.HasChanged ? "*" : "";
                    string name  = yndfile.Name;
                    if (yndfile.RpfFileEntry != null)
                    {
                        name = yndfile.RpfFileEntry.Name;
                    }
                    var yndnode = yndsnode.Nodes.Add(ycstr + name);
                    yndnode.Tag = yndfile;

                    LoadYndTreeNodes(yndfile, yndnode);
                }
                yndsnode.Expand();
            }

            if (CurrentProjectFile.YnvFiles.Count > 0)
            {
                var ynvsnode = projnode.Nodes.Add("Ynv Files");
                ynvsnode.Name = "Ynv";

                foreach (var ynvfile in CurrentProjectFile.YnvFiles)
                {
                    var    ycstr = ynvfile.HasChanged ? "*" : "";
                    string name  = ynvfile.Name;
                    if (ynvfile.RpfFileEntry != null)
                    {
                        name = ynvfile.RpfFileEntry.Name;
                    }
                    var ynvnode = ynvsnode.Nodes.Add(ycstr + name);
                    ynvnode.Tag = ynvfile;

                    LoadYnvTreeNodes(ynvfile, ynvnode);
                }
                ynvsnode.Expand();
            }

            if (CurrentProjectFile.TrainsFiles.Count > 0)
            {
                var trainsnode = projnode.Nodes.Add("Trains Files");
                trainsnode.Name = "Trains";

                foreach (var trainfile in CurrentProjectFile.TrainsFiles)
                {
                    var    tcstr = trainfile.HasChanged ? "*" : "";
                    string name  = trainfile.Name;
                    if (trainfile.RpfFileEntry != null)
                    {
                        name = trainfile.RpfFileEntry.Name;
                    }
                    var trainnode = trainsnode.Nodes.Add(tcstr + name);
                    trainnode.Tag = trainfile;

                    LoadTrainTrackTreeNodes(trainfile, trainnode);
                }
                trainsnode.Expand();
            }

            if (CurrentProjectFile.ScenarioFiles.Count > 0)
            {
                var scenariosnode = projnode.Nodes.Add("Scenario Files");
                scenariosnode.Name = "Scenarios";

                foreach (var scenariofile in CurrentProjectFile.ScenarioFiles)
                {
                    var    scstr = scenariofile.HasChanged ? "*" : "";
                    string name  = scenariofile.Name;
                    if (scenariofile.RpfFileEntry != null)
                    {
                        name = scenariofile.RpfFileEntry.Name;
                    }
                    var scenarionode = scenariosnode.Nodes.Add(scstr + name);
                    scenarionode.Tag = scenariofile;

                    LoadScenarioTreeNodes(scenariofile, scenarionode);
                }
                scenariosnode.Expand();
            }

            projnode.Expand();
        }