2018-03-03 21:03:08 +08:00
|
|
|
|
using CodeWalker.GameFiles;
|
|
|
|
|
using SharpDX;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
|
|
|
|
|
namespace CodeWalker.Project.Panels
|
|
|
|
|
{
|
|
|
|
|
public partial class EditYmapCarGenPanel : ProjectPanel
|
|
|
|
|
{
|
2018-03-03 21:09:31 +08:00
|
|
|
|
public ProjectForm ProjectForm;
|
2018-03-03 21:03:08 +08:00
|
|
|
|
public YmapCarGen CurrentCarGen { get; set; }
|
|
|
|
|
|
|
|
|
|
private bool populatingui = false;
|
|
|
|
|
|
2018-03-03 21:09:31 +08:00
|
|
|
|
public EditYmapCarGenPanel(ProjectForm owner)
|
2018-03-03 21:03:08 +08:00
|
|
|
|
{
|
|
|
|
|
ProjectForm = owner;
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetCarGen(YmapCarGen cargen)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen = cargen;
|
|
|
|
|
Tag = cargen;
|
|
|
|
|
LoadCarGen();
|
|
|
|
|
UpdateFormTitle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateFormTitle()
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.carModel == 0)
|
|
|
|
|
{
|
|
|
|
|
Text = "Car Generator";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Text = CurrentCarGen?.NameString() ?? "Car Generator";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void LoadCarGen()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (CurrentCarGen == null)
|
|
|
|
|
{
|
|
|
|
|
//CarGenPanel.Enabled = false;
|
|
|
|
|
CarAddToProjectButton.Enabled = false;
|
|
|
|
|
CarDeleteButton.Enabled = false;
|
|
|
|
|
CarModelTextBox.Text = string.Empty;
|
|
|
|
|
CarModelHashLabel.Text = "Hash: 0";
|
|
|
|
|
CarPopGroupTextBox.Text = string.Empty;
|
|
|
|
|
CarPopGroupHashLabel.Text = "Hash: 0";
|
|
|
|
|
CarFlagsTextBox.Text = string.Empty;
|
|
|
|
|
CarPositionTextBox.Text = string.Empty;
|
|
|
|
|
CarOrientXTextBox.Text = string.Empty;
|
|
|
|
|
CarOrientYTextBox.Text = string.Empty;
|
|
|
|
|
CarPerpendicularLengthTextBox.Text = string.Empty;
|
|
|
|
|
CarBodyColorRemap1TextBox.Text = string.Empty;
|
|
|
|
|
CarBodyColorRemap2TextBox.Text = string.Empty;
|
|
|
|
|
CarBodyColorRemap3TextBox.Text = string.Empty;
|
|
|
|
|
CarBodyColorRemap4TextBox.Text = string.Empty;
|
|
|
|
|
CarLiveryTextBox.Text = string.Empty;
|
|
|
|
|
foreach (int i in CarFlagsCheckedListBox.CheckedIndices)
|
|
|
|
|
{
|
|
|
|
|
CarFlagsCheckedListBox.SetItemCheckState(i, CheckState.Unchecked);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
populatingui = true;
|
|
|
|
|
var c = CurrentCarGen.CCarGen;
|
|
|
|
|
//CarGenPanel.Enabled = true;
|
|
|
|
|
CarAddToProjectButton.Enabled = !ProjectForm.YmapExistsInProject(CurrentCarGen.Ymap);
|
|
|
|
|
CarDeleteButton.Enabled = !CarAddToProjectButton.Enabled;
|
|
|
|
|
CarModelTextBox.Text = c.carModel.ToString();
|
|
|
|
|
CarModelHashLabel.Text = "Hash: " + c.carModel.Hash.ToString();
|
|
|
|
|
CarPopGroupTextBox.Text = c.popGroup.ToString();
|
|
|
|
|
CarPopGroupHashLabel.Text = "Hash: " + c.popGroup.Hash.ToString();
|
|
|
|
|
CarFlagsTextBox.Text = c.flags.ToString();
|
|
|
|
|
CarPositionTextBox.Text = FloatUtil.GetVector3String(c.position);
|
|
|
|
|
CarOrientXTextBox.Text = FloatUtil.ToString(c.orientX);
|
|
|
|
|
CarOrientYTextBox.Text = FloatUtil.ToString(c.orientY);
|
|
|
|
|
CarPerpendicularLengthTextBox.Text = FloatUtil.ToString(c.perpendicularLength);
|
|
|
|
|
CarBodyColorRemap1TextBox.Text = c.bodyColorRemap1.ToString();
|
|
|
|
|
CarBodyColorRemap2TextBox.Text = c.bodyColorRemap2.ToString();
|
|
|
|
|
CarBodyColorRemap3TextBox.Text = c.bodyColorRemap3.ToString();
|
|
|
|
|
CarBodyColorRemap4TextBox.Text = c.bodyColorRemap4.ToString();
|
|
|
|
|
CarLiveryTextBox.Text = c.livery.ToString();
|
|
|
|
|
for (int i = 0; i < CarFlagsCheckedListBox.Items.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var cv = ((c.flags & (1u << i)) > 0);
|
|
|
|
|
CarFlagsCheckedListBox.SetItemCheckState(i, cv ? CheckState.Checked : CheckState.Unchecked);
|
|
|
|
|
}
|
|
|
|
|
populatingui = false;
|
|
|
|
|
|
|
|
|
|
if (ProjectForm.WorldForm != null)
|
|
|
|
|
{
|
|
|
|
|
ProjectForm.WorldForm.SelectCarGen(CurrentCarGen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////struct CCarGen:
|
|
|
|
|
//Vector3 position { get; set; } //16 16: Float_XYZ: 0: position
|
|
|
|
|
//float orientX { get; set; } //32 32: Float: 0: orientX=735213009
|
|
|
|
|
//float orientY { get; set; } //36 36: Float: 0: orientY=979440342
|
|
|
|
|
//float perpendicularLength { get; set; } //40 40: Float: 0: perpendicularLength=124715667
|
|
|
|
|
//MetaHash carModel { get; set; } //44 44: Hash: 0: carModel
|
|
|
|
|
//uint flags { get; set; } //48 48: UnsignedInt: 0: flags
|
|
|
|
|
//int bodyColorRemap1 { get; set; } //52 52: SignedInt: 0: bodyColorRemap1=1429703670
|
|
|
|
|
//int bodyColorRemap2 { get; set; } //56 56: SignedInt: 0: bodyColorRemap2=1254848286
|
|
|
|
|
//int bodyColorRemap3 { get; set; } //60 60: SignedInt: 0: bodyColorRemap3=1880965569
|
|
|
|
|
//int bodyColorRemap4 { get; set; } //64 64: SignedInt: 0: bodyColorRemap4=1719152247
|
|
|
|
|
//MetaHash popGroup { get; set; } //68 68: Hash: 0: popGroup=911358791
|
|
|
|
|
//sbyte livery { get; set; } //72 72: SignedByte: 0: livery
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarModelTextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
uint hash = 0;
|
|
|
|
|
string name = CarModelTextBox.Text;
|
|
|
|
|
if (!uint.TryParse(name, out hash))//don't re-hash hashes
|
|
|
|
|
{
|
|
|
|
|
hash = JenkHash.GenHash(name);
|
|
|
|
|
JenkIndex.Ensure(name);
|
|
|
|
|
}
|
|
|
|
|
CarModelHashLabel.Text = "Hash: " + hash.ToString();
|
|
|
|
|
|
|
|
|
|
//var model = GameFileCache.GetCarInfo(hash); //todo: something like this for car info?
|
|
|
|
|
//if (model == null)
|
|
|
|
|
//{
|
|
|
|
|
// CarModelHashLabel.Text += " (not found)";
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
if (CurrentCarGen != null)
|
|
|
|
|
{
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
var modelhash = new MetaHash(hash);
|
|
|
|
|
if (CurrentCarGen._CCarGen.carModel != modelhash)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.carModel = modelhash;
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProjectForm.ProjectExplorer?.UpdateCarGenTreeNode(CurrentCarGen);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarFlagsTextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
uint flags = 0;
|
|
|
|
|
uint.TryParse(CarFlagsTextBox.Text, out flags);
|
|
|
|
|
populatingui = true;
|
|
|
|
|
for (int i = 0; i < CarFlagsCheckedListBox.Items.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var c = ((flags & (1u << i)) > 0);
|
|
|
|
|
CarFlagsCheckedListBox.SetItemCheckState(i, c ? CheckState.Checked : CheckState.Unchecked);
|
|
|
|
|
}
|
|
|
|
|
populatingui = false;
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.flags != flags)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.flags = flags;
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarFlagsCheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
uint flags = 0;
|
|
|
|
|
for (int i = 0; i < CarFlagsCheckedListBox.Items.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (e.Index == i)
|
|
|
|
|
{
|
|
|
|
|
if (e.NewValue == CheckState.Checked)
|
|
|
|
|
{
|
|
|
|
|
flags += (uint)(1 << i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (CarFlagsCheckedListBox.GetItemChecked(i))
|
|
|
|
|
{
|
|
|
|
|
flags += (uint)(1 << i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
populatingui = true;
|
|
|
|
|
CarFlagsTextBox.Text = flags.ToString();
|
|
|
|
|
populatingui = false;
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.flags != flags)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.flags = flags;
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarPositionTextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
Vector3 v = FloatUtil.ParseVector3String(CarPositionTextBox.Text);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen.Position != v)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen.SetPosition(v);
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
ProjectForm.WorldForm?.SetWidgetPosition(v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProjectForm.ProjectExplorer?.UpdateCarGenTreeNode(CurrentCarGen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarOrientXTextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
float ox = FloatUtil.Parse(CarOrientXTextBox.Text);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.orientX != ox)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.orientX = ox;
|
|
|
|
|
CurrentCarGen.CalcOrientation();
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
ProjectForm.WorldForm?.SetWidgetRotation(CurrentCarGen.Orientation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarOrientYTextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
float oy = FloatUtil.Parse(CarOrientYTextBox.Text);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.orientY != oy)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.orientY = oy;
|
|
|
|
|
CurrentCarGen.CalcOrientation();
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
ProjectForm.WorldForm?.SetWidgetRotation(CurrentCarGen.Orientation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarPerpendicularLengthTextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
float len = FloatUtil.Parse(CarPerpendicularLengthTextBox.Text);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.perpendicularLength != len)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen.SetLength(len);
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
ProjectForm.WorldForm?.SetWidgetScale(new Vector3(len));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarBodyColorRemap1TextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
int cr = 0;
|
|
|
|
|
int.TryParse(CarBodyColorRemap1TextBox.Text, out cr);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.bodyColorRemap1 != cr)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.bodyColorRemap1 = cr;
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarBodyColorRemap2TextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
int cr = 0;
|
|
|
|
|
int.TryParse(CarBodyColorRemap2TextBox.Text, out cr);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.bodyColorRemap2 != cr)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.bodyColorRemap2 = cr;
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarBodyColorRemap3TextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
int cr = 0;
|
|
|
|
|
int.TryParse(CarBodyColorRemap3TextBox.Text, out cr);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.bodyColorRemap3 != cr)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.bodyColorRemap3 = cr;
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarBodyColorRemap4TextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
int cr = 0;
|
|
|
|
|
int.TryParse(CarBodyColorRemap4TextBox.Text, out cr);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.bodyColorRemap4 != cr)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.bodyColorRemap4 = cr;
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarLiveryTextBox_TextChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (populatingui) return;
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
sbyte cr = 0;
|
|
|
|
|
sbyte.TryParse(CarLiveryTextBox.Text, out cr);
|
|
|
|
|
lock (ProjectForm.ProjectSyncRoot)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen._CCarGen.livery != cr)
|
|
|
|
|
{
|
|
|
|
|
CurrentCarGen._CCarGen.livery = cr;
|
|
|
|
|
ProjectForm.SetYmapHasChanged(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ProjectForm.ProjectExplorer?.UpdateCarGenTreeNode(CurrentCarGen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarGoToButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (CurrentCarGen == null) return;
|
|
|
|
|
ProjectForm.WorldForm?.GoToPosition(CurrentCarGen.Position);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarAddToProjectButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
ProjectForm.SetProjectItem(CurrentCarGen);
|
|
|
|
|
ProjectForm.AddCarGenToProject();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CarDeleteButton_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
ProjectForm.SetProjectItem(CurrentCarGen);
|
|
|
|
|
ProjectForm.DeleteCarGen();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|