mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-16 20:17:30 +08:00
Fixed YED/XML conversion
This commit is contained in:
parent
5d3849b682
commit
97c24710e4
@ -3203,9 +3203,7 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
foreach (RpfEntry entry in file.AllEntries)
|
foreach (RpfEntry entry in file.AllEntries)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
//try
|
||||||
try
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
var n = entry.NameLower;
|
var n = entry.NameLower;
|
||||||
if (!(n.EndsWith(".pso") ||
|
if (!(n.EndsWith(".pso") ||
|
||||||
@ -3273,13 +3271,11 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
//catch (Exception ex)
|
||||||
catch (Exception ex)
|
//{
|
||||||
{
|
// UpdateStatus("Error! " + ex.ToString());
|
||||||
UpdateStatus("Error! " + ex.ToString());
|
// exceptions.Add(ex);
|
||||||
exceptions.Add(ex);
|
//}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3381,9 +3377,7 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
foreach (RpfEntry entry in file.AllEntries)
|
foreach (RpfEntry entry in file.AllEntries)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
//try
|
||||||
try
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
var rfe = entry as RpfFileEntry;
|
var rfe = entry as RpfFileEntry;
|
||||||
if (rfe == null) continue;
|
if (rfe == null) continue;
|
||||||
@ -3398,13 +3392,11 @@ namespace CodeWalker.GameFiles
|
|||||||
//PsoTypes.EnsurePsoTypes(cut.Pso);
|
//PsoTypes.EnsurePsoTypes(cut.Pso);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
//catch (Exception ex)
|
||||||
catch (Exception ex)
|
//{
|
||||||
{
|
// UpdateStatus("Error! " + ex.ToString());
|
||||||
UpdateStatus("Error! " + ex.ToString());
|
// exceptions.Add(ex);
|
||||||
exceptions.Add(ex);
|
//}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3422,9 +3414,7 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
foreach (RpfEntry entry in file.AllEntries)
|
foreach (RpfEntry entry in file.AllEntries)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
//try
|
||||||
try
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
var rfe = entry as RpfFileEntry;
|
var rfe = entry as RpfFileEntry;
|
||||||
if (rfe == null) continue;
|
if (rfe == null) continue;
|
||||||
@ -3438,13 +3428,11 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
//catch (Exception ex)
|
||||||
catch (Exception ex)
|
//{
|
||||||
{
|
// UpdateStatus("Error! " + ex.ToString());
|
||||||
UpdateStatus("Error! " + ex.ToString());
|
// exceptions.Add(ex);
|
||||||
exceptions.Add(ex);
|
//}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3453,16 +3441,14 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
public void TestYeds()
|
public void TestYeds()
|
||||||
{
|
{
|
||||||
|
bool xmltest = true;
|
||||||
var exceptions = new List<Exception>();
|
var exceptions = new List<Exception>();
|
||||||
|
|
||||||
foreach (RpfFile file in AllRpfs)
|
foreach (RpfFile file in AllRpfs)
|
||||||
{
|
{
|
||||||
foreach (RpfEntry entry in file.AllEntries)
|
foreach (RpfEntry entry in file.AllEntries)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
//try
|
||||||
try
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
var rfe = entry as RpfFileEntry;
|
var rfe = entry as RpfFileEntry;
|
||||||
if (rfe == null) continue;
|
if (rfe == null) continue;
|
||||||
@ -3474,24 +3460,25 @@ namespace CodeWalker.GameFiles
|
|||||||
YedFile yed = new YedFile(rfe);
|
YedFile yed = new YedFile(rfe);
|
||||||
RpfMan.LoadFile(yed, rfe);
|
RpfMan.LoadFile(yed, rfe);
|
||||||
|
|
||||||
var xml = YedXml.GetXml(yed);
|
if (xmltest)
|
||||||
var yed2 = XmlYed.GetYed(xml);
|
{
|
||||||
var data2 = yed2.Save();
|
var xml = YedXml.GetXml(yed);
|
||||||
var yed3 = new YedFile();
|
var yed2 = XmlYed.GetYed(xml);
|
||||||
RpfFile.LoadResourceFile(yed3, data2, 25);//full roundtrip
|
var data2 = yed2.Save();
|
||||||
var xml2 = YedXml.GetXml(yed3);
|
var yed3 = new YedFile();
|
||||||
if (xml != xml2)
|
RpfFile.LoadResourceFile(yed3, data2, 25);//full roundtrip
|
||||||
{ }
|
var xml2 = YedXml.GetXml(yed3);
|
||||||
|
if (xml != xml2)
|
||||||
|
{ }//no hitting
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
//catch (Exception ex)
|
||||||
catch (Exception ex)
|
//{
|
||||||
{
|
// UpdateStatus("Error! " + ex.ToString());
|
||||||
UpdateStatus("Error! " + ex.ToString());
|
// exceptions.Add(ex);
|
||||||
exceptions.Add(ex);
|
//}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4370,9 +4357,7 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
foreach (RpfEntry entry in file.AllEntries)
|
foreach (RpfEntry entry in file.AllEntries)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
//try
|
||||||
try
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
var rfe = entry as RpfFileEntry;
|
var rfe = entry as RpfFileEntry;
|
||||||
if (rfe == null) continue;
|
if (rfe == null) continue;
|
||||||
@ -4397,13 +4382,11 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
//catch (Exception ex)
|
||||||
catch (Exception ex)
|
//{
|
||||||
{
|
// UpdateStatus("Error! " + ex.ToString());
|
||||||
UpdateStatus("Error! " + ex.ToString());
|
// exceptions.Add(ex);
|
||||||
exceptions.Add(ex);
|
//}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4419,9 +4402,7 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
foreach (RpfEntry entry in file.AllEntries)
|
foreach (RpfEntry entry in file.AllEntries)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
//try
|
||||||
try
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
var rfe = entry as RpfFileEntry;
|
var rfe = entry as RpfFileEntry;
|
||||||
if (rfe == null) continue;
|
if (rfe == null) continue;
|
||||||
@ -4444,13 +4425,11 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
//catch (Exception ex)
|
||||||
catch (Exception ex)
|
//{
|
||||||
{
|
// UpdateStatus("Error! " + ex.ToString());
|
||||||
UpdateStatus("Error! " + ex.ToString());
|
// exceptions.Add(ex);
|
||||||
exceptions.Add(ex);
|
//}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,11 +38,8 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
[TC(typeof(EXP))] public class ExpressionDictionary : ResourceFileBase
|
[TC(typeof(EXP))] public class ExpressionDictionary : ResourceFileBase
|
||||||
{
|
{
|
||||||
// pgDictionaryBase
|
// pgDictionary<crExpressions> : pgDictionaryBase
|
||||||
// pgDictionary<crExpressions>
|
|
||||||
public override long BlockLength => 0x40;
|
public override long BlockLength => 0x40;
|
||||||
|
|
||||||
// structure data
|
|
||||||
public uint Unknown_10h { get; set; } = 0;
|
public uint Unknown_10h { get; set; } = 0;
|
||||||
public uint Unknown_14h { get; set; } = 0;
|
public uint Unknown_14h { get; set; } = 0;
|
||||||
public uint Unknown_18h { get; set; } = 1;
|
public uint Unknown_18h { get; set; } = 1;
|
||||||
@ -50,40 +47,32 @@ namespace CodeWalker.GameFiles
|
|||||||
public ResourceSimpleList64_s<MetaHash> ExpressionNameHashes { get; set; }
|
public ResourceSimpleList64_s<MetaHash> ExpressionNameHashes { get; set; }
|
||||||
public ResourcePointerList64<Expression> Expressions { get; set; }
|
public ResourcePointerList64<Expression> Expressions { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public Dictionary<MetaHash, Expression> ExprMap { get; set; }
|
public Dictionary<MetaHash, Expression> ExprMap { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||||
{
|
{
|
||||||
base.Read(reader, parameters);
|
base.Read(reader, parameters);
|
||||||
|
Unknown_10h = reader.ReadUInt32();
|
||||||
// read structure data
|
Unknown_14h = reader.ReadUInt32();
|
||||||
this.Unknown_10h = reader.ReadUInt32();
|
Unknown_18h = reader.ReadUInt32();
|
||||||
this.Unknown_14h = reader.ReadUInt32();
|
Unknown_1Ch = reader.ReadUInt32();
|
||||||
this.Unknown_18h = reader.ReadUInt32();
|
ExpressionNameHashes = reader.ReadBlock<ResourceSimpleList64_s<MetaHash>>();
|
||||||
this.Unknown_1Ch = reader.ReadUInt32();
|
Expressions = reader.ReadBlock<ResourcePointerList64<Expression>>();
|
||||||
this.ExpressionNameHashes = reader.ReadBlock<ResourceSimpleList64_s<MetaHash>>();
|
|
||||||
this.Expressions = reader.ReadBlock<ResourcePointerList64<Expression>>();
|
|
||||||
|
|
||||||
BuildMap();
|
BuildMap();
|
||||||
|
|
||||||
}
|
}
|
||||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||||
{
|
{
|
||||||
base.Write(writer, parameters);
|
base.Write(writer, parameters);
|
||||||
|
writer.Write(Unknown_10h);
|
||||||
// write structure data
|
writer.Write(Unknown_14h);
|
||||||
writer.Write(this.Unknown_10h);
|
writer.Write(Unknown_18h);
|
||||||
writer.Write(this.Unknown_14h);
|
writer.Write(Unknown_1Ch);
|
||||||
writer.Write(this.Unknown_18h);
|
writer.WriteBlock(ExpressionNameHashes);
|
||||||
writer.Write(this.Unknown_1Ch);
|
writer.WriteBlock(Expressions);
|
||||||
writer.WriteBlock(this.ExpressionNameHashes);
|
|
||||||
writer.WriteBlock(this.Expressions);
|
|
||||||
}
|
}
|
||||||
public void WriteXml(StringBuilder sb, int indent)
|
public void WriteXml(StringBuilder sb, int indent)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (Expressions?.data_items != null)
|
if (Expressions?.data_items != null)
|
||||||
{
|
{
|
||||||
foreach (var e in Expressions.data_items)
|
foreach (var e in Expressions.data_items)
|
||||||
@ -93,7 +82,6 @@ namespace CodeWalker.GameFiles
|
|||||||
YedXml.CloseTag(sb, indent, "Item");
|
YedXml.CloseTag(sb, indent, "Item");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public void ReadXml(XmlNode node)
|
public void ReadXml(XmlNode node)
|
||||||
{
|
{
|
||||||
@ -108,6 +96,12 @@ namespace CodeWalker.GameFiles
|
|||||||
var e = new Expression();
|
var e = new Expression();
|
||||||
e.ReadXml(inode);
|
e.ReadXml(inode);
|
||||||
expressions.Add(e);
|
expressions.Add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
//expressions in the file should be sorted by hash
|
||||||
|
expressions.Sort((a, b) => a.NameHash.Hash.CompareTo(b.NameHash.Hash));
|
||||||
|
foreach (var e in expressions)
|
||||||
|
{
|
||||||
expressionhashes.Add(e.NameHash);
|
expressionhashes.Add(e.NameHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,11 +176,8 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
[TC(typeof(EXP))] public class Expression : ResourceSystemBlock
|
[TC(typeof(EXP))] public class Expression : ResourceSystemBlock
|
||||||
{
|
{
|
||||||
// pgBase
|
// crExpressions : pgBase
|
||||||
// crExpressions
|
|
||||||
public override long BlockLength => 0x90;
|
public override long BlockLength => 0x90;
|
||||||
|
|
||||||
// structure data
|
|
||||||
public uint VFT { get; set; }
|
public uint VFT { get; set; }
|
||||||
public uint Unknown_4h { get; set; } = 1;
|
public uint Unknown_4h { get; set; } = 1;
|
||||||
public uint Unknown_8h { get; set; } // 0x00000000
|
public uint Unknown_8h { get; set; } // 0x00000000
|
||||||
@ -206,13 +197,12 @@ namespace CodeWalker.GameFiles
|
|||||||
public uint Unknown_70h { get; set; } = 1;
|
public uint Unknown_70h { get; set; } = 1;
|
||||||
public uint Signature { get; set; }
|
public uint Signature { get; set; }
|
||||||
public uint MaxStreamSize { get; set; } // max length of any item in Streams
|
public uint MaxStreamSize { get; set; } // max length of any item in Streams
|
||||||
public uint Unknown_7Ch { get; set; } // 3 or 2
|
public uint Unknown_7Ch { get; set; } // 3 or 2 - type or flags?
|
||||||
public uint Unknown_80h { get; set; } // 0x00000000
|
public uint Unknown_80h { get; set; } // 0x00000000
|
||||||
public uint Unknown_84h { get; set; } // 0x00000000
|
public uint Unknown_84h { get; set; } // 0x00000000
|
||||||
public uint Unknown_88h { get; set; } // 0x00000000
|
public uint Unknown_88h { get; set; } // 0x00000000
|
||||||
public uint Unknown_8Ch { get; set; } // 0x00000000
|
public uint Unknown_8Ch { get; set; } // 0x00000000
|
||||||
|
|
||||||
// reference data
|
|
||||||
public string_r Name { get; set; }
|
public string_r Name { get; set; }
|
||||||
public MetaHash NameHash { get; set; }
|
public MetaHash NameHash { get; set; }
|
||||||
|
|
||||||
@ -221,36 +211,32 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||||
{
|
{
|
||||||
// read structure data
|
VFT = reader.ReadUInt32();
|
||||||
this.VFT = reader.ReadUInt32();
|
Unknown_4h = reader.ReadUInt32();
|
||||||
this.Unknown_4h = reader.ReadUInt32();
|
Unknown_8h = reader.ReadUInt32();
|
||||||
this.Unknown_8h = reader.ReadUInt32();
|
Unknown_Ch = reader.ReadUInt32();
|
||||||
this.Unknown_Ch = reader.ReadUInt32();
|
Unknown_10h = reader.ReadUInt32();
|
||||||
this.Unknown_10h = reader.ReadUInt32();
|
Unknown_14h = reader.ReadUInt32();
|
||||||
this.Unknown_14h = reader.ReadUInt32();
|
Unknown_18h = reader.ReadUInt32();
|
||||||
this.Unknown_18h = reader.ReadUInt32();
|
Unknown_1Ch = reader.ReadUInt32();
|
||||||
this.Unknown_1Ch = reader.ReadUInt32();
|
Streams = reader.ReadBlock<ResourcePointerList64<ExpressionStream>>();
|
||||||
this.Streams = reader.ReadBlock<ResourcePointerList64<ExpressionStream>>();
|
Tracks = reader.ReadBlock<ResourceSimpleList64_s<ExpressionTrack>>();
|
||||||
this.Tracks = reader.ReadBlock<ResourceSimpleList64_s<ExpressionTrack>>();
|
Springs = reader.ReadBlock<ResourceSimpleList64<ExpressionSpringDescriptionBlock>>();
|
||||||
this.Springs = reader.ReadBlock<ResourceSimpleList64<ExpressionSpringDescriptionBlock>>();
|
Variables = reader.ReadBlock<ResourceSimpleList64_s<MetaHash>>();
|
||||||
this.Variables = reader.ReadBlock<ResourceSimpleList64_s<MetaHash>>();
|
NamePointer = reader.ReadUInt64();
|
||||||
this.NamePointer = reader.ReadUInt64();
|
NameLength = reader.ReadUInt16();
|
||||||
this.NameLength = reader.ReadUInt16();
|
NameCapacity = reader.ReadUInt16();
|
||||||
this.NameCapacity = reader.ReadUInt16();
|
Unknown_6Ch = reader.ReadUInt32();
|
||||||
this.Unknown_6Ch = reader.ReadUInt32();
|
Unknown_70h = reader.ReadUInt32();
|
||||||
this.Unknown_70h = reader.ReadUInt32();
|
Signature = reader.ReadUInt32();
|
||||||
this.Signature = reader.ReadUInt32();
|
MaxStreamSize = reader.ReadUInt32();
|
||||||
this.MaxStreamSize = reader.ReadUInt32();
|
Unknown_7Ch = reader.ReadUInt32();
|
||||||
this.Unknown_7Ch = reader.ReadUInt32();
|
Unknown_80h = reader.ReadUInt32();
|
||||||
this.Unknown_80h = reader.ReadUInt32();
|
Unknown_84h = reader.ReadUInt32();
|
||||||
this.Unknown_84h = reader.ReadUInt32();
|
Unknown_88h = reader.ReadUInt32();
|
||||||
this.Unknown_88h = reader.ReadUInt32();
|
Unknown_8Ch = reader.ReadUInt32();
|
||||||
this.Unknown_8Ch = reader.ReadUInt32();
|
|
||||||
|
|
||||||
// read reference data
|
Name = reader.ReadBlockAt<string_r>(NamePointer);
|
||||||
this.Name = reader.ReadBlockAt<string_r>(
|
|
||||||
this.NamePointer // offset
|
|
||||||
);
|
|
||||||
|
|
||||||
JenkIndex.Ensure(GetShortName());
|
JenkIndex.Ensure(GetShortName());
|
||||||
|
|
||||||
@ -261,120 +247,46 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
#region testing
|
#region testing
|
||||||
|
|
||||||
//if (Streams?.data_items != null)
|
|
||||||
//{ }
|
|
||||||
|
|
||||||
////if ((this.JiggleData?.data_items?.Length ?? 0) > 0)
|
|
||||||
//{
|
|
||||||
// var cnt1 = JiggleData?.data_items?.Length ?? 0;
|
|
||||||
// var cnt2 = 0;
|
|
||||||
// foreach (var stream in Streams.data_items)
|
|
||||||
// {
|
|
||||||
// foreach (var node in stream.Items)
|
|
||||||
// {
|
|
||||||
// if (node is ExpressionNodeJiggle jnode)
|
|
||||||
// {
|
|
||||||
// var trackrot = BoneTracks.data_items[jnode.BoneTrackRot];
|
|
||||||
// var trackpos = BoneTracks.data_items[jnode.BoneTrackPos];
|
|
||||||
// var jd1 = jnode.JiggleData;
|
|
||||||
// var jd2 = JiggleData.data_items[cnt2].JiggleData;
|
|
||||||
// if (!jd1.Compare(jd2))
|
|
||||||
// { }//no hit
|
|
||||||
// if (jd2.BoneTag != trackrot.BoneTag)
|
|
||||||
// { }//no hit
|
|
||||||
// cnt2++;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (cnt1 != cnt2)
|
|
||||||
// { }//no hit
|
|
||||||
//}
|
|
||||||
|
|
||||||
//long tlen = 0;
|
//long tlen = 0;
|
||||||
//if (Streams?.data_items != null) foreach (var item in Streams.data_items) tlen = Math.Max(tlen, item.BlockLength);
|
//if (Streams?.data_items != null) foreach (var item in Streams.data_items) tlen = Math.Max(tlen, item.BlockLength);
|
||||||
//if (MaxStreamSize != tlen)
|
//if (MaxStreamSize != tlen)
|
||||||
//{ }//no hit
|
//{ }//no hit
|
||||||
|
|
||||||
//if (Unknown_4h != 1)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_8h != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_Ch != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_10h != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_14h != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_18h != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_1Ch != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (NameLength != (Name?.Value?.Length ?? 0))
|
|
||||||
//{ }//no hit
|
|
||||||
//if (NameCapacity != (NameLength + 1))
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_6Ch != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_70h != 1)
|
|
||||||
//{ }//no hit
|
|
||||||
//switch (Unknown_74h)
|
|
||||||
//{
|
|
||||||
// default:
|
|
||||||
// break;
|
|
||||||
//}
|
|
||||||
//switch (Unknown_7Ch)
|
|
||||||
//{
|
|
||||||
// case 3:
|
|
||||||
// case 2:
|
|
||||||
// break;
|
|
||||||
// default:
|
|
||||||
// break;//no hit
|
|
||||||
//}
|
|
||||||
//if (Unknown_80h != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_84h != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_88h != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
//if (Unknown_8Ch != 0)
|
|
||||||
//{ }//no hit
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||||
{
|
{
|
||||||
// update structure data
|
NamePointer = (ulong)(Name != null ? Name.FilePosition : 0);
|
||||||
this.NamePointer = (ulong)(this.Name != null ? this.Name.FilePosition : 0);
|
|
||||||
|
|
||||||
// write structure data
|
writer.Write(VFT);
|
||||||
writer.Write(this.VFT);
|
writer.Write(Unknown_4h);
|
||||||
writer.Write(this.Unknown_4h);
|
writer.Write(Unknown_8h);
|
||||||
writer.Write(this.Unknown_8h);
|
writer.Write(Unknown_Ch);
|
||||||
writer.Write(this.Unknown_Ch);
|
writer.Write(Unknown_10h);
|
||||||
writer.Write(this.Unknown_10h);
|
writer.Write(Unknown_14h);
|
||||||
writer.Write(this.Unknown_14h);
|
writer.Write(Unknown_18h);
|
||||||
writer.Write(this.Unknown_18h);
|
writer.Write(Unknown_1Ch);
|
||||||
writer.Write(this.Unknown_1Ch);
|
writer.WriteBlock(Streams);
|
||||||
writer.WriteBlock(this.Streams);
|
writer.WriteBlock(Tracks);
|
||||||
writer.WriteBlock(this.Tracks);
|
writer.WriteBlock(Springs);
|
||||||
writer.WriteBlock(this.Springs);
|
writer.WriteBlock(Variables);
|
||||||
writer.WriteBlock(this.Variables);
|
writer.Write(NamePointer);
|
||||||
writer.Write(this.NamePointer);
|
writer.Write(NameLength);
|
||||||
writer.Write(this.NameLength);
|
writer.Write(NameCapacity);
|
||||||
writer.Write(this.NameCapacity);
|
writer.Write(Unknown_6Ch);
|
||||||
writer.Write(this.Unknown_6Ch);
|
writer.Write(Unknown_70h);
|
||||||
writer.Write(this.Unknown_70h);
|
writer.Write(Signature);
|
||||||
writer.Write(this.Signature);
|
writer.Write(MaxStreamSize);
|
||||||
writer.Write(this.MaxStreamSize);
|
writer.Write(Unknown_7Ch);
|
||||||
writer.Write(this.Unknown_7Ch);
|
writer.Write(Unknown_80h);
|
||||||
writer.Write(this.Unknown_80h);
|
writer.Write(Unknown_84h);
|
||||||
writer.Write(this.Unknown_84h);
|
writer.Write(Unknown_88h);
|
||||||
writer.Write(this.Unknown_88h);
|
writer.Write(Unknown_8Ch);
|
||||||
writer.Write(this.Unknown_8Ch);
|
|
||||||
}
|
}
|
||||||
public void WriteXml(StringBuilder sb, int indent)
|
public void WriteXml(StringBuilder sb, int indent)
|
||||||
{
|
{
|
||||||
YedXml.StringTag(sb, indent, "Name", Name?.Value ?? "");
|
YedXml.StringTag(sb, indent, "Name", Name?.Value ?? "");
|
||||||
YedXml.ValueTag(sb, indent, "Signature", Signature.ToString()); // TODO: calculate signature
|
YedXml.ValueTag(sb, indent, "Signature", Signature.ToString()); // TODO: calculate signature?
|
||||||
YedXml.ValueTag(sb, indent, "Unk7C", Unknown_7Ch.ToString());
|
YedXml.ValueTag(sb, indent, "Unk7C", Unknown_7Ch.ToString());
|
||||||
|
|
||||||
if ((Tracks?.data_items?.Length ?? 0) > 0)
|
if ((Tracks?.data_items?.Length ?? 0) > 0)
|
||||||
@ -408,6 +320,7 @@ namespace CodeWalker.GameFiles
|
|||||||
BuildSpringsList();
|
BuildSpringsList();
|
||||||
UpdateVariables();
|
UpdateVariables();
|
||||||
UpdateStreamBuffers();
|
UpdateStreamBuffers();
|
||||||
|
UpdateJumpInstructions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IResourceBlock[] GetReferences()
|
public override IResourceBlock[] GetReferences()
|
||||||
@ -449,7 +362,6 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BuildSpringsList()
|
public void BuildSpringsList()
|
||||||
{
|
{
|
||||||
var springs = new List<ExpressionSpringDescriptionBlock>();
|
var springs = new List<ExpressionSpringDescriptionBlock>();
|
||||||
@ -471,7 +383,6 @@ namespace CodeWalker.GameFiles
|
|||||||
Springs = new ResourceSimpleList64<ExpressionSpringDescriptionBlock>();
|
Springs = new ResourceSimpleList64<ExpressionSpringDescriptionBlock>();
|
||||||
Springs.data_items = springs.ToArray();
|
Springs.data_items = springs.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateVariables()
|
public void UpdateVariables()
|
||||||
{
|
{
|
||||||
var dict = new Dictionary<MetaHash, uint>();
|
var dict = new Dictionary<MetaHash, uint>();
|
||||||
@ -513,7 +424,6 @@ namespace CodeWalker.GameFiles
|
|||||||
Variables.data_items = list.ToArray();
|
Variables.data_items = list.ToArray();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateStreamBuffers()
|
public void UpdateStreamBuffers()
|
||||||
{
|
{
|
||||||
MaxStreamSize = 0;
|
MaxStreamSize = 0;
|
||||||
@ -526,6 +436,26 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void UpdateJumpInstructions()
|
||||||
|
{
|
||||||
|
if (Streams?.data_items != null)
|
||||||
|
{
|
||||||
|
foreach (var stream in Streams.data_items)
|
||||||
|
{
|
||||||
|
if (stream?.Instructions == null) continue;
|
||||||
|
foreach (var node in stream.Instructions)
|
||||||
|
{
|
||||||
|
if (node is ExpressionInstrJump jump)
|
||||||
|
{
|
||||||
|
//indexes and offsets need to be updated already - done by UpdateMaxStreamSize()
|
||||||
|
var target = stream.Instructions[jump.Index + jump.Data3Offset];
|
||||||
|
jump.Data1Offset = (uint)(target.Offset1 - jump.Offset1);
|
||||||
|
jump.Data2Offset = (uint)(target.Offset2 - jump.Offset2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public string GetShortName()
|
public string GetShortName()
|
||||||
@ -544,17 +474,12 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
[TC(typeof(EXP))] public class ExpressionStream : ResourceSystemBlock, IMetaXmlItem
|
[TC(typeof(EXP))] public class ExpressionStream : ResourceSystemBlock, IMetaXmlItem
|
||||||
{
|
{
|
||||||
public override long BlockLength
|
public override long BlockLength => 16 + Data1.Length + Data2.Length + Data3.Length;
|
||||||
{
|
public MetaHash NameHash { get; set; }
|
||||||
get { return 16 + Data1.Length + Data2.Length + Data3.Length; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// structure data
|
|
||||||
public MetaHash NameHash { get; set; }//presumably name hash
|
|
||||||
public uint Data1Length { get; set; }
|
public uint Data1Length { get; set; }
|
||||||
public uint Data2Length { get; set; }
|
public uint Data2Length { get; set; }
|
||||||
public ushort Data3Length { get; set; }
|
public ushort Data3Length { get; set; }
|
||||||
public ushort Unk0E { get; set; }//what is this? possibly max hierarchy depth
|
public ushort Depth { get; set; }//or stack size?
|
||||||
public byte[] Data1 { get; set; }
|
public byte[] Data1 { get; set; }
|
||||||
public byte[] Data2 { get; set; }
|
public byte[] Data2 { get; set; }
|
||||||
public byte[] Data3 { get; set; }
|
public byte[] Data3 { get; set; }
|
||||||
@ -566,37 +491,32 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||||
{
|
{
|
||||||
// read structure data
|
NameHash = reader.ReadUInt32();
|
||||||
this.NameHash = reader.ReadUInt32();
|
Data1Length = reader.ReadUInt32();
|
||||||
this.Data1Length = reader.ReadUInt32();
|
Data2Length = reader.ReadUInt32();
|
||||||
this.Data2Length = reader.ReadUInt32();
|
Data3Length = reader.ReadUInt16();
|
||||||
this.Data3Length = reader.ReadUInt16();
|
Depth = reader.ReadUInt16();
|
||||||
this.Unk0E = reader.ReadUInt16();
|
Data1 = reader.ReadBytes((int)Data1Length);
|
||||||
this.Data1 = reader.ReadBytes((int)Data1Length);
|
Data2 = reader.ReadBytes((int)Data2Length);
|
||||||
this.Data2 = reader.ReadBytes((int)Data2Length);
|
Data3 = reader.ReadBytes((int)Data3Length);
|
||||||
this.Data3 = reader.ReadBytes((int)Data3Length);
|
|
||||||
|
|
||||||
|
|
||||||
ReadInstructions();
|
ReadInstructions();
|
||||||
}
|
}
|
||||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||||
{
|
{
|
||||||
//WriteItems();//should already be done by Expression.UpdateStreamBuffers
|
//WriteInstructions();//should already be done by Expression.UpdateStreamBuffers
|
||||||
|
writer.Write(NameHash);
|
||||||
// write structure data
|
writer.Write(Data1Length);
|
||||||
writer.Write(this.NameHash);
|
writer.Write(Data2Length);
|
||||||
writer.Write(this.Data1Length);
|
writer.Write(Data3Length);
|
||||||
writer.Write(this.Data2Length);
|
writer.Write(Depth);
|
||||||
writer.Write(this.Data3Length);
|
writer.Write(Data1);
|
||||||
writer.Write(this.Unk0E);
|
writer.Write(Data2);
|
||||||
writer.Write(this.Data1);
|
writer.Write(Data3);
|
||||||
writer.Write(this.Data2);
|
|
||||||
writer.Write(this.Data3);
|
|
||||||
}
|
}
|
||||||
public void WriteXml(StringBuilder sb, int indent)
|
public void WriteXml(StringBuilder sb, int indent)
|
||||||
{
|
{
|
||||||
YedXml.StringTag(sb, indent, "Name", YedXml.HashString(NameHash));
|
YedXml.StringTag(sb, indent, "Name", YedXml.HashString(NameHash));
|
||||||
YedXml.ValueTag(sb, indent, "Unk0E", Unk0E.ToString());
|
YedXml.ValueTag(sb, indent, "Depth", Depth.ToString());
|
||||||
|
|
||||||
YedXml.OpenTag(sb, indent, "Instructions");
|
YedXml.OpenTag(sb, indent, "Instructions");
|
||||||
var cind = indent + 1;
|
var cind = indent + 1;
|
||||||
@ -621,7 +541,7 @@ namespace CodeWalker.GameFiles
|
|||||||
public void ReadXml(XmlNode node)
|
public void ReadXml(XmlNode node)
|
||||||
{
|
{
|
||||||
NameHash = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Name"));
|
NameHash = XmlMeta.GetHash(Xml.GetChildInnerText(node, "Name"));
|
||||||
Unk0E = (ushort)Xml.GetChildUIntAttribute(node, "Unk0E", "value");
|
Depth = (ushort)Xml.GetChildUIntAttribute(node, "Depth", "value");
|
||||||
|
|
||||||
var items = new List<ExpressionInstrBase>();
|
var items = new List<ExpressionInstrBase>();
|
||||||
var instnode = node.SelectSingleNode("Instructions");
|
var instnode = node.SelectSingleNode("Instructions");
|
||||||
@ -639,6 +559,8 @@ namespace CodeWalker.GameFiles
|
|||||||
item.ReadXml(inode);
|
item.ReadXml(inode);
|
||||||
items.Add(item);
|
items.Add(item);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -647,81 +569,37 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
public void ReadInstructions()
|
public void ReadInstructions()
|
||||||
{
|
{
|
||||||
|
var insts = new ExpressionInstrBase[Data3.Length];
|
||||||
var s1 = new MemoryStream(Data1);
|
var s1 = new MemoryStream(Data1);
|
||||||
var s2 = new MemoryStream(Data2);
|
var s2 = new MemoryStream(Data2);
|
||||||
var s3 = new MemoryStream(Data3);
|
|
||||||
var r1 = new DataReader(s1);
|
var r1 = new DataReader(s1);
|
||||||
var r2 = new DataReader(s2);
|
var r2 = new DataReader(s2);
|
||||||
var r3 = new DataReader(s3);
|
|
||||||
|
|
||||||
var unresolvedJumps = new Dictionary<(long, long, long), List<ExpressionInstrJump>>();
|
//dexy: removed unresolvedjumps stuff from here as it should all resolve to jump.Data3Offset+1
|
||||||
var instructions = new List<ExpressionInstrBase>();
|
|
||||||
while (s3.Position < s3.Length)
|
for (int i = 0; i < insts.Length; i++)
|
||||||
{
|
{
|
||||||
var loc = (s1.Position, s2.Position, s3.Position);
|
var type = (ExpressionInstrType)Data3[i];
|
||||||
|
if (type == ExpressionInstrType.End)
|
||||||
var type = (ExpressionInstrType)r3.ReadByte();
|
|
||||||
if (type == ExpressionInstrType.None)
|
|
||||||
{
|
{
|
||||||
if (s3.Position != s3.Length)
|
if (i != insts.Length - 1)
|
||||||
{ }//no hit
|
{ }//no hit
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
var instr = CreateInstruction(type);
|
var instr = CreateInstruction(type);
|
||||||
instr.Type = type;
|
instr.Type = type;
|
||||||
|
instr.Index = i;
|
||||||
|
instr.Offset1 = (int)r1.Position;
|
||||||
|
instr.Offset2 = (int)r2.Position;
|
||||||
instr.Read(r1, r2);
|
instr.Read(r1, r2);
|
||||||
var instrIndex = (uint)instructions.Count;
|
insts[i] = instr;
|
||||||
instructions.Add(instr);
|
|
||||||
|
|
||||||
// resolve jumps by converting data byte offsets to instruction indices offsets
|
|
||||||
// this is to make it easier to edit the XML
|
|
||||||
if (unresolvedJumps.TryGetValue(loc, out var jumps))
|
|
||||||
{
|
|
||||||
foreach (var jump in jumps)
|
|
||||||
{
|
|
||||||
// relative offset between jump and target instructions
|
|
||||||
jump.InstructionOffset = instrIndex - jump.InstructionOffset;
|
|
||||||
}
|
|
||||||
unresolvedJumps.Remove(loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instr is ExpressionInstrJump j)
|
|
||||||
{
|
|
||||||
j.InstructionOffset = instrIndex;
|
|
||||||
var targetLoc = (s1.Position + j.Data1Offset, s2.Position + j.Data2Offset, s3.Position + j.Data3Offset);
|
|
||||||
if (!unresolvedJumps.TryGetValue(targetLoc, out jumps))
|
|
||||||
{
|
|
||||||
jumps = new List<ExpressionInstrJump>();
|
|
||||||
unresolvedJumps[targetLoc] = jumps;
|
|
||||||
}
|
|
||||||
jumps.Add(j);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// special case to resolve jumps to the end of the instructions list since we don't store the finish marker instruction
|
|
||||||
var endLoc = (s1.Position, s2.Position, s3.Position - 1);
|
|
||||||
if (unresolvedJumps.TryGetValue(endLoc, out var endJumps))
|
|
||||||
{
|
|
||||||
foreach (var jump in endJumps)
|
|
||||||
{
|
|
||||||
jump.InstructionOffset = (uint)instructions.Count - jump.InstructionOffset;
|
|
||||||
}
|
|
||||||
unresolvedJumps.Remove(endLoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ((r1.Length - r1.Position) != 0)
|
if ((r1.Length - r1.Position) != 0)
|
||||||
{ }//no hit
|
{ }//no hit
|
||||||
if ((r2.Length - r2.Position) != 0)
|
if ((r2.Length - r2.Position) != 0)
|
||||||
{ }//no hit
|
{ }//no hit
|
||||||
if ((r3.Length - r3.Position) != 0)
|
|
||||||
{ }//no hit
|
|
||||||
|
|
||||||
// there should be no unresolved jumps at this point
|
Instructions = insts;
|
||||||
if (unresolvedJumps.Count != 0)
|
|
||||||
{ }//no hit
|
|
||||||
|
|
||||||
Instructions = instructions.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteInstructions()
|
public void WriteInstructions()
|
||||||
@ -735,10 +613,12 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
foreach (var instr in Instructions)
|
foreach (var instr in Instructions)
|
||||||
{
|
{
|
||||||
|
instr.Offset1 = (int)w1.Position;
|
||||||
|
instr.Offset2 = (int)w2.Position;
|
||||||
|
instr.Index = (int)w3.Position;
|
||||||
w3.Write((byte)instr.Type);
|
w3.Write((byte)instr.Type);
|
||||||
instr.Write(w1, w2);
|
instr.Write(w1, w2);
|
||||||
}
|
}
|
||||||
w3.Write((byte)0);
|
|
||||||
|
|
||||||
Data1 = s1.ToArray();
|
Data1 = s1.ToArray();
|
||||||
Data2 = s2.ToArray();
|
Data2 = s2.ToArray();
|
||||||
@ -753,6 +633,7 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
case ExpressionInstrType.End:
|
||||||
case ExpressionInstrType.Pop:
|
case ExpressionInstrType.Pop:
|
||||||
case ExpressionInstrType.Dup:
|
case ExpressionInstrType.Dup:
|
||||||
case ExpressionInstrType.Push0:
|
case ExpressionInstrType.Push0:
|
||||||
@ -838,7 +719,7 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
public enum ExpressionInstrType : byte
|
public enum ExpressionInstrType : byte
|
||||||
{
|
{
|
||||||
None = 0,
|
End = 0,
|
||||||
Pop = 0x01,
|
Pop = 0x01,
|
||||||
Dup = 0x02,
|
Dup = 0x02,
|
||||||
Push0 = 0x03,
|
Push0 = 0x03,
|
||||||
@ -903,6 +784,9 @@ namespace CodeWalker.GameFiles
|
|||||||
[TC(typeof(EXP))] public abstract class ExpressionInstrBase
|
[TC(typeof(EXP))] public abstract class ExpressionInstrBase
|
||||||
{
|
{
|
||||||
public ExpressionInstrType Type { get; set; }
|
public ExpressionInstrType Type { get; set; }
|
||||||
|
public int Offset1 { get; set; }
|
||||||
|
public int Offset2 { get; set; }
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
public virtual void Read(DataReader r1, DataReader r2)
|
public virtual void Read(DataReader r1, DataReader r2)
|
||||||
{ }
|
{ }
|
||||||
@ -922,12 +806,22 @@ namespace CodeWalker.GameFiles
|
|||||||
{ }
|
{ }
|
||||||
[TC(typeof(EXP))] public class ExpressionInstrBlend : ExpressionInstrBase
|
[TC(typeof(EXP))] public class ExpressionInstrBlend : ExpressionInstrBase
|
||||||
{
|
{
|
||||||
|
public struct SourceInfo
|
||||||
|
{
|
||||||
|
public ushort TrackIndex;
|
||||||
|
public ushort ComponentOffset;
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{TrackIndex} : {ComponentOffset}";
|
||||||
|
}
|
||||||
|
}
|
||||||
[TC(typeof(EXP))] public class SourceComponent : IMetaXmlItem
|
[TC(typeof(EXP))] public class SourceComponent : IMetaXmlItem
|
||||||
{
|
{
|
||||||
public float[] Weights { get; set; }
|
public float[] Weights { get; set; }
|
||||||
public float[] Offsets { get; set; }
|
public float[] Offsets { get; set; }
|
||||||
public float[] Thresholds { get; set; }
|
public float[] Thresholds { get; set; }
|
||||||
|
|
||||||
|
public SourceComponent() { }
|
||||||
public SourceComponent(uint numSourceWeights)
|
public SourceComponent(uint numSourceWeights)
|
||||||
{
|
{
|
||||||
Weights = new float[numSourceWeights];
|
Weights = new float[numSourceWeights];
|
||||||
@ -941,25 +835,59 @@ namespace CodeWalker.GameFiles
|
|||||||
YedXml.WriteRawArray(sb, Offsets, indent, "Offsets", "", FloatUtil.ToString, 32);
|
YedXml.WriteRawArray(sb, Offsets, indent, "Offsets", "", FloatUtil.ToString, 32);
|
||||||
YedXml.WriteRawArray(sb, Thresholds, indent, "Thresholds", "", FloatUtil.ToString, 32);
|
YedXml.WriteRawArray(sb, Thresholds, indent, "Thresholds", "", FloatUtil.ToString, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadXml(XmlNode node)
|
public void ReadXml(XmlNode node)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (node == null) return;
|
||||||
|
Weights = Xml.GetChildRawFloatArray(node, "Weights");
|
||||||
|
Offsets = Xml.GetChildRawFloatArray(node, "Offsets");
|
||||||
|
Thresholds = Xml.GetChildRawFloatArray(node, "Thresholds");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Source : IMetaXmlItem
|
[TC(typeof(EXP))] public class Source : IMetaXmlItem
|
||||||
{
|
{
|
||||||
public ushort TrackIndex { get; set; }
|
public SourceInfo Info { get; set; }
|
||||||
public ushort ComponentOffset { get; set; }
|
|
||||||
public SourceComponent X { get; set; }
|
public SourceComponent X { get; set; }
|
||||||
public SourceComponent Y { get; set; }
|
public SourceComponent Y { get; set; }
|
||||||
public SourceComponent Z { get; set; }
|
public SourceComponent Z { get; set; }
|
||||||
|
|
||||||
|
public Source()
|
||||||
|
{ }
|
||||||
|
public Source(SourceInfo info, uint numSourceWeights, int index, Vector4[] values)
|
||||||
|
{
|
||||||
|
Info = info;
|
||||||
|
X = new SourceComponent(numSourceWeights);
|
||||||
|
Y = new SourceComponent(numSourceWeights);
|
||||||
|
Z = new SourceComponent(numSourceWeights);
|
||||||
|
|
||||||
|
var j = index / 4;
|
||||||
|
var k = index % 4;
|
||||||
|
var v = j * (6 + 9 * (int)(numSourceWeights - 1));
|
||||||
|
X.Weights[0] = values[v + 0][k];
|
||||||
|
Y.Weights[0] = values[v + 1][k];
|
||||||
|
Z.Weights[0] = values[v + 2][k];
|
||||||
|
X.Offsets[0] = values[v + 3][k];
|
||||||
|
Y.Offsets[0] = values[v + 4][k];
|
||||||
|
Z.Offsets[0] = values[v + 5][k];
|
||||||
|
for (int n = 1; n < numSourceWeights; n++)
|
||||||
|
{
|
||||||
|
var m = n - 1;
|
||||||
|
var b = v + 6 + (9 * m);
|
||||||
|
X.Thresholds[m] = values[b + 0][k];
|
||||||
|
Y.Thresholds[m] = values[b + 1][k];
|
||||||
|
Z.Thresholds[m] = values[b + 2][k];
|
||||||
|
X.Weights[n] = values[b + 3][k];
|
||||||
|
Y.Weights[n] = values[b + 4][k];
|
||||||
|
Z.Weights[n] = values[b + 5][k];
|
||||||
|
X.Offsets[n] = values[b + 6][k];
|
||||||
|
Y.Offsets[n] = values[b + 7][k];
|
||||||
|
Z.Offsets[n] = values[b + 8][k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void WriteXml(StringBuilder sb, int indent)
|
public void WriteXml(StringBuilder sb, int indent)
|
||||||
{
|
{
|
||||||
YedXml.ValueTag(sb, indent, "TrackIndex", TrackIndex.ToString());
|
YedXml.ValueTag(sb, indent, "TrackIndex", Info.TrackIndex.ToString());
|
||||||
YedXml.ValueTag(sb, indent, "ComponentIndex", (ComponentOffset / 4).ToString());
|
YedXml.ValueTag(sb, indent, "ComponentIndex", (Info.ComponentOffset / 4).ToString());
|
||||||
YedXml.OpenTag(sb, indent, "X");
|
YedXml.OpenTag(sb, indent, "X");
|
||||||
X.WriteXml(sb, indent + 1);
|
X.WriteXml(sb, indent + 1);
|
||||||
YedXml.CloseTag(sb, indent, "X");
|
YedXml.CloseTag(sb, indent, "X");
|
||||||
@ -970,25 +898,74 @@ namespace CodeWalker.GameFiles
|
|||||||
Z.WriteXml(sb, indent + 1);
|
Z.WriteXml(sb, indent + 1);
|
||||||
YedXml.CloseTag(sb, indent, "Z");
|
YedXml.CloseTag(sb, indent, "Z");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadXml(XmlNode node)
|
public void ReadXml(XmlNode node)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var info = new SourceInfo();
|
||||||
|
info.TrackIndex = (ushort)Xml.GetChildUIntAttribute(node, "TrackIndex", "value");
|
||||||
|
info.ComponentOffset = (ushort)(Xml.GetChildUIntAttribute(node, "ComponentIndex", "value") * 4);
|
||||||
|
Info = info;
|
||||||
|
X = new SourceComponent();
|
||||||
|
X.ReadXml(node.SelectSingleNode("X"));
|
||||||
|
Y = new SourceComponent();
|
||||||
|
Y.ReadXml(node.SelectSingleNode("Y"));
|
||||||
|
Z = new SourceComponent();
|
||||||
|
Z.ReadXml(node.SelectSingleNode("Z"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateValues(uint numSourceWeights, int index, Vector4[] values)
|
||||||
|
{
|
||||||
|
if (X == null) return;
|
||||||
|
if (Y == null) return;
|
||||||
|
if (Z == null) return;
|
||||||
|
if (X.Weights?.Length < numSourceWeights) return;
|
||||||
|
if (Y.Weights?.Length < numSourceWeights) return;
|
||||||
|
if (Z.Weights?.Length < numSourceWeights) return;
|
||||||
|
if (X.Offsets?.Length < numSourceWeights) return;
|
||||||
|
if (Y.Offsets?.Length < numSourceWeights) return;
|
||||||
|
if (Z.Offsets?.Length < numSourceWeights) return;
|
||||||
|
if (X.Thresholds?.Length < (numSourceWeights - 1)) return;
|
||||||
|
if (Y.Thresholds?.Length < (numSourceWeights - 1)) return;
|
||||||
|
if (Z.Thresholds?.Length < (numSourceWeights - 1)) return;
|
||||||
|
var j = index / 4;
|
||||||
|
var k = index % 4;
|
||||||
|
var v = j * (6 + 9 * (int)(numSourceWeights - 1));
|
||||||
|
values[v + 0][k] = X.Weights[0];
|
||||||
|
values[v + 1][k] = Y.Weights[0];
|
||||||
|
values[v + 2][k] = Z.Weights[0];
|
||||||
|
values[v + 3][k] = X.Offsets[0];
|
||||||
|
values[v + 4][k] = Y.Offsets[0];
|
||||||
|
values[v + 5][k] = Z.Offsets[0];
|
||||||
|
for (int n = 1; n < numSourceWeights; n++)
|
||||||
|
{
|
||||||
|
var m = n - 1;
|
||||||
|
var b = v + 6 + (9 * m);
|
||||||
|
values[b + 0][k] = X.Thresholds[m];
|
||||||
|
values[b + 1][k] = Y.Thresholds[m];
|
||||||
|
values[b + 2][k] = Z.Thresholds[m];
|
||||||
|
values[b + 3][k] = X.Weights[n];
|
||||||
|
values[b + 4][k] = Y.Weights[n];
|
||||||
|
values[b + 5][k] = Z.Weights[n];
|
||||||
|
values[b + 6][k] = X.Offsets[n];
|
||||||
|
values[b + 7][k] = Y.Offsets[n];
|
||||||
|
values[b + 8][k] = Z.Offsets[n];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return "TODO"; // TODO
|
return $"TrackIndex {Info.TrackIndex}, ComponentIndex {Info.ComponentOffset / 4} (offset {Info.ComponentOffset})";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint ByteLength { get; set; } //updated automatically
|
public uint ByteLength { get; set; } //updated automatically
|
||||||
public uint SourceCount { get; set; } //updated automatically
|
public uint SourceCount { get; set; } //updated automatically //0-84+, multiple of 4
|
||||||
public uint NumSourceWeights { get; set; }
|
public uint NumSourceWeights { get; set; }//1-4
|
||||||
public uint Unk1 { get; set; } // 0x00000000
|
public uint Unk1 { get; set; } // 0x00000000
|
||||||
public Source[] Sources { get; set; }
|
public SourceInfo[] SourceInfos { get; set; }
|
||||||
public Vector4[] Values { get; set; }
|
public Vector4[] Values { get; set; }
|
||||||
|
|
||||||
|
public uint RequiredValueCount => (SourceCount / 4) * (6 + ((NumSourceWeights - 1) * 9));
|
||||||
|
|
||||||
public override void Read(DataReader r1, DataReader r2)
|
public override void Read(DataReader r1, DataReader r2)
|
||||||
{
|
{
|
||||||
ByteLength = r1.ReadUInt32();
|
ByteLength = r1.ReadUInt32();
|
||||||
@ -996,106 +973,23 @@ namespace CodeWalker.GameFiles
|
|||||||
NumSourceWeights = r1.ReadUInt32();
|
NumSourceWeights = r1.ReadUInt32();
|
||||||
Unk1 = r1.ReadUInt32();
|
Unk1 = r1.ReadUInt32();
|
||||||
|
|
||||||
Sources = new Source[SourceCount];
|
SourceInfos = new SourceInfo[SourceCount];
|
||||||
for (int i = 0; i < SourceCount; i++)
|
for (int i = 0; i < SourceCount; i++)
|
||||||
{
|
{
|
||||||
var s = new Source();
|
var s = new SourceInfo();
|
||||||
s.TrackIndex = r1.ReadUInt16();
|
s.TrackIndex = r1.ReadUInt16();
|
||||||
s.ComponentOffset = r1.ReadUInt16();
|
s.ComponentOffset = r1.ReadUInt16();
|
||||||
s.X = new SourceComponent(NumSourceWeights);
|
SourceInfos[i] = s;
|
||||||
s.Y = new SourceComponent(NumSourceWeights);
|
|
||||||
s.Z = new SourceComponent(NumSourceWeights);
|
|
||||||
Sources[i] = s;
|
|
||||||
}
|
}
|
||||||
|
Values = new Vector4[RequiredValueCount];
|
||||||
// TODO: rename
|
for (int i = 0; i < Values.Length; i++)
|
||||||
var numIters = SourceCount / 4;
|
|
||||||
var numSubpasses = NumSourceWeights - 1;
|
|
||||||
var numValues = (numIters * 6) + (numIters * numSubpasses * 9);
|
|
||||||
Values = new Vector4[numValues];
|
|
||||||
for (int i = 0; i < numValues; i++)
|
|
||||||
{
|
{
|
||||||
Values[i] = r1.ReadVector4();
|
Values[i] = r1.ReadVector4();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
switch (SourceCount)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
case 4:
|
|
||||||
case 8:
|
|
||||||
case 12:
|
|
||||||
case 20:
|
|
||||||
case 24:
|
|
||||||
case 48:
|
|
||||||
case 52:
|
|
||||||
case 76:
|
|
||||||
case 32:
|
|
||||||
case 16:
|
|
||||||
case 40:
|
|
||||||
case 44:
|
|
||||||
case 60:
|
|
||||||
case 72:
|
|
||||||
case 56:
|
|
||||||
case 28:
|
|
||||||
case 68:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break; //no hit
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (NumSourceWeights)
|
|
||||||
{
|
|
||||||
case 1: break;
|
|
||||||
case 2: break;
|
|
||||||
case 3: break;
|
|
||||||
case 4: break;
|
|
||||||
default:
|
|
||||||
break; //no hit
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Unk1 != 0)
|
|
||||||
{
|
|
||||||
} //no hit
|
|
||||||
|
|
||||||
|
|
||||||
// organize data into more human-readable layout
|
|
||||||
// the file layout is optimized for vectorized operations
|
|
||||||
for (int i = 0; i < (Sources.Length / 4); i++)
|
|
||||||
{
|
|
||||||
var it = i * 4;
|
|
||||||
int v = i * 6 + (i * (int)(NumSourceWeights - 1) * 9);
|
|
||||||
for (int k = 0; k < 4; k++)
|
|
||||||
{
|
|
||||||
Sources[it + k].X.Weights[0] = Values[v + 0][k];
|
|
||||||
Sources[it + k].Y.Weights[0] = Values[v + 1][k];
|
|
||||||
Sources[it + k].Z.Weights[0] = Values[v + 2][k];
|
|
||||||
Sources[it + k].X.Offsets[0] = Values[v + 3][k];
|
|
||||||
Sources[it + k].Y.Offsets[0] = Values[v + 4][k];
|
|
||||||
Sources[it + k].Z.Offsets[0] = Values[v + 5][k];
|
|
||||||
|
|
||||||
for (int n = 1; n < NumSourceWeights; n++)
|
|
||||||
{
|
|
||||||
int b = v + 6 + (n - 1) * 9;
|
|
||||||
Sources[it + k].X.Thresholds[n - 1] = Values[b + 0][k];
|
|
||||||
Sources[it + k].Y.Thresholds[n - 1] = Values[b + 1][k];
|
|
||||||
Sources[it + k].Z.Thresholds[n - 1] = Values[b + 2][k];
|
|
||||||
Sources[it + k].X.Weights[n] = Values[b + 3][k];
|
|
||||||
Sources[it + k].Y.Weights[n] = Values[b + 4][k];
|
|
||||||
Sources[it + k].Z.Weights[n] = Values[b + 5][k];
|
|
||||||
Sources[it + k].X.Offsets[n] = Values[b + 6][k];
|
|
||||||
Sources[it + k].Y.Offsets[n] = Values[b + 7][k];
|
|
||||||
Sources[it + k].Z.Offsets[n] = Values[b + 8][k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(DataWriter w1, DataWriter w2)
|
public override void Write(DataWriter w1, DataWriter w2)
|
||||||
{
|
{
|
||||||
// TODO
|
SourceCount = (uint)(SourceInfos?.Length ?? 0);
|
||||||
SourceCount = (uint)(Sources?.Length ?? 0);
|
|
||||||
NumSourceWeights = Math.Max(NumSourceWeights, 1);
|
NumSourceWeights = Math.Max(NumSourceWeights, 1);
|
||||||
var valcnt = (NumSourceWeights - 1) * 9 + 6;
|
var valcnt = (NumSourceWeights - 1) * 9 + 6;
|
||||||
var hlen = SourceCount * 4 + 16;
|
var hlen = SourceCount * 4 + 16;
|
||||||
@ -1107,32 +1001,43 @@ namespace CodeWalker.GameFiles
|
|||||||
w1.Write(NumSourceWeights);
|
w1.Write(NumSourceWeights);
|
||||||
w1.Write(Unk1);
|
w1.Write(Unk1);
|
||||||
|
|
||||||
// TODO
|
|
||||||
for (int i = 0; i < SourceCount; i++)
|
for (int i = 0; i < SourceCount; i++)
|
||||||
{
|
{
|
||||||
// Sources[i].Write(w1);
|
var si = SourceInfos[i];
|
||||||
|
w1.Write(si.TrackIndex);
|
||||||
|
w1.Write(si.ComponentOffset);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Values.Length; i++)
|
||||||
|
{
|
||||||
|
w1.Write(Values[i]);
|
||||||
}
|
}
|
||||||
// TODO
|
|
||||||
// for (int n = 0; n < valcnt; n++)
|
|
||||||
// {
|
|
||||||
// for (int i = 0; i < ItemCount; i++)
|
|
||||||
// {
|
|
||||||
// var vals = Items[i].Values;
|
|
||||||
// w1.Write((n < (vals?.Length ?? 0)) ? vals[n] : 0.0f);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WriteXml(StringBuilder sb, int indent)
|
public override void WriteXml(StringBuilder sb, int indent)
|
||||||
{
|
{
|
||||||
YedXml.ValueTag(sb, indent, "NumSourceWeights", NumSourceWeights.ToString());
|
// organize data into more human-readable layout
|
||||||
YedXml.WriteItemArray(sb, Sources, indent, "Sources");
|
// the file layout is optimized for vectorized operations
|
||||||
}
|
var sources = new Source[SourceCount];
|
||||||
|
for (int i = 0; i < SourceCount; i++)
|
||||||
|
{
|
||||||
|
sources[i] = new Source(SourceInfos[i], NumSourceWeights, i, Values);
|
||||||
|
}
|
||||||
|
|
||||||
|
YedXml.ValueTag(sb, indent, "NumSourceWeights", NumSourceWeights.ToString());
|
||||||
|
YedXml.WriteItemArray(sb, sources, indent, "Sources");
|
||||||
|
}
|
||||||
public override void ReadXml(XmlNode node)
|
public override void ReadXml(XmlNode node)
|
||||||
{
|
{
|
||||||
NumSourceWeights = Xml.GetChildUIntAttribute(node, "NumSourceWeights");
|
NumSourceWeights = Math.Max(Xml.GetChildUIntAttribute(node, "NumSourceWeights"), 1);
|
||||||
Sources = XmlMeta.ReadItemArray<Source>(node, "Sources");
|
var sources = XmlMeta.ReadItemArray<Source>(node, "Sources");
|
||||||
|
SourceCount = (uint)(sources?.Length ?? 0);
|
||||||
|
SourceInfos = new SourceInfo[SourceCount];
|
||||||
|
Values = new Vector4[RequiredValueCount];
|
||||||
|
for (int i = 0; i < SourceCount; i++)
|
||||||
|
{
|
||||||
|
var s = sources[i];
|
||||||
|
SourceInfos[i] = s.Info;
|
||||||
|
s.UpdateValues(NumSourceWeights, i, Values);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
@ -1140,7 +1045,6 @@ namespace CodeWalker.GameFiles
|
|||||||
return base.ToString() + " - " + SourceCount + ", " + NumSourceWeights;
|
return base.ToString() + " - " + SourceCount + ", " + NumSourceWeights;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class ExpressionInstrBone : ExpressionInstrBase
|
[TC(typeof(EXP))] public class ExpressionInstrBone : ExpressionInstrBase
|
||||||
{
|
{
|
||||||
public ushort TrackIndex { get; set; } //index of the BoneTag in the Expression.BoneTracks array
|
public ushort TrackIndex { get; set; } //index of the BoneTag in the Expression.BoneTracks array
|
||||||
@ -1179,7 +1083,7 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
public override void ReadXml(XmlNode node)
|
public override void ReadXml(XmlNode node)
|
||||||
{
|
{
|
||||||
TrackIndex = (ushort)Xml.GetChildUIntAttribute(node, "BoneTrack", "value");
|
TrackIndex = (ushort)Xml.GetChildUIntAttribute(node, "TrackIndex", "value");
|
||||||
BoneId = (ushort)Xml.GetChildUIntAttribute(node, "BoneId", "value");
|
BoneId = (ushort)Xml.GetChildUIntAttribute(node, "BoneId", "value");
|
||||||
Track = (byte)Xml.GetChildUIntAttribute(node, "Track", "value");
|
Track = (byte)Xml.GetChildUIntAttribute(node, "Track", "value");
|
||||||
Format = (byte)Xml.GetChildUIntAttribute(node, "Format", "value");
|
Format = (byte)Xml.GetChildUIntAttribute(node, "Format", "value");
|
||||||
@ -1223,12 +1127,9 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
[TC(typeof(EXP))] public class ExpressionInstrJump : ExpressionInstrBase
|
[TC(typeof(EXP))] public class ExpressionInstrJump : ExpressionInstrBase
|
||||||
{
|
{
|
||||||
// note: unsigned so can only jump forwards
|
public uint Data1Offset { get; set; } // note: unsigned so can only jump forwards
|
||||||
public uint Data1Offset { get; set; }
|
|
||||||
public uint Data2Offset { get; set; }
|
public uint Data2Offset { get; set; }
|
||||||
public uint Data3Offset { get; set; }
|
public uint Data3Offset { get; set; } //instruction offset
|
||||||
|
|
||||||
public uint InstructionOffset { get; set; }
|
|
||||||
|
|
||||||
public override void Read(DataReader r1, DataReader r2)
|
public override void Read(DataReader r1, DataReader r2)
|
||||||
{
|
{
|
||||||
@ -1244,18 +1145,11 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
public override void WriteXml(StringBuilder sb, int indent)
|
public override void WriteXml(StringBuilder sb, int indent)
|
||||||
{
|
{
|
||||||
YedXml.ValueTag(sb, indent, "InstructionOffset", InstructionOffset.ToString());
|
YedXml.ValueTag(sb, indent, "InstructionOffset", Data3Offset.ToString());
|
||||||
// YedXml.ValueTag(sb, indent, "Data1Offset", Data1Offset.ToString());
|
|
||||||
// YedXml.ValueTag(sb, indent, "Data2Offset", Data2Offset.ToString());
|
|
||||||
// YedXml.ValueTag(sb, indent, "Data3Offset", Data3Offset.ToString());
|
|
||||||
}
|
}
|
||||||
public override void ReadXml(XmlNode node)
|
public override void ReadXml(XmlNode node)
|
||||||
{
|
{
|
||||||
// TODO: calculate data offsets
|
Data3Offset = Xml.GetChildUIntAttribute(node, "InstructionOffset");
|
||||||
InstructionOffset = Xml.GetChildUIntAttribute(node, "InstructionOffset");
|
|
||||||
// Data1Offset = Xml.GetChildUIntAttribute(node, "Data1Offset");
|
|
||||||
// Data2Offset = Xml.GetChildUIntAttribute(node, "Data2Offset");
|
|
||||||
// Data3Offset = Xml.GetChildUIntAttribute(node, "Data3Offset");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
@ -343,11 +343,11 @@ namespace CodeWalker.GameFiles
|
|||||||
//BaseSize = 0x2000 << BaseShift (max BaseShift = 0xF)
|
//BaseSize = 0x2000 << BaseShift (max BaseShift = 0xF)
|
||||||
//then allocate page counts for the page sizes:
|
//then allocate page counts for the page sizes:
|
||||||
//allows for 5 page sizes, each double the size of the previous, with max counts 0x7F, 0x3F, 0xF, 3, 1
|
//allows for 5 page sizes, each double the size of the previous, with max counts 0x7F, 0x3F, 0xF, 3, 1
|
||||||
//also allows for 4 tail pages, each half the size of the previous, only one page of each size
|
//also allows for 4 tail pages, each half the size of the previous, only one page of each size [TODO?]
|
||||||
|
|
||||||
var sys = (basePosition == 0x50000000);
|
var sys = (basePosition == 0x50000000);
|
||||||
var maxPageSizeMult = 16L;//the biggest page is 16x the base page size.
|
var maxPageSizeMult = 16L;//the biggest page is 16x the base page size.
|
||||||
var maxPageSize = (0x2000 << 0xF) * maxPageSizeMult; //this is the size of the biggest possible page [256MB!]
|
var maxPageSize = (0x2000 << 0xF) * maxPageSizeMult; //this is the size of the biggest possible page [4GB!]
|
||||||
var maxBlockSize = 0L;
|
var maxBlockSize = 0L;
|
||||||
var minBlockSize = (blocks.Count == 0) ? 0 : maxPageSize;
|
var minBlockSize = (blocks.Count == 0) ? 0 : maxPageSize;
|
||||||
foreach (var block in blocks)
|
foreach (var block in blocks)
|
||||||
@ -389,7 +389,6 @@ namespace CodeWalker.GameFiles
|
|||||||
pageSizes[i] = null;
|
pageSizes[i] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var testOk = true;
|
|
||||||
var largestPageSizeI = 0;
|
var largestPageSizeI = 0;
|
||||||
var largestPageSize = baseSize;
|
var largestPageSize = baseSize;
|
||||||
while (largestPageSize < maxBlockSize)
|
while (largestPageSize < maxBlockSize)
|
||||||
@ -457,6 +456,7 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testOk = true;
|
||||||
var totalPageCount = 0u;
|
var totalPageCount = 0u;
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user