2019-04-22 23:52:21 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2019-10-14 03:02:39 +02:00
|
|
|
|
using System.IO;
|
2019-04-22 23:52:21 +02:00
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
2019-07-16 23:35:21 +02:00
|
|
|
|
using Toolbox.Library.IO;
|
|
|
|
|
using Toolbox.Library;
|
2019-04-22 23:52:21 +02:00
|
|
|
|
using System.Windows.Forms;
|
2019-10-14 03:02:39 +02:00
|
|
|
|
using Toolbox.Library.Forms;
|
|
|
|
|
using System.Xml;
|
|
|
|
|
using System.Xml.Serialization;
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
|
|
|
|
namespace FirstPlugin
|
|
|
|
|
{
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public class XLINK : IEditor<TextEditor>, IFileFormat
|
2019-04-22 23:52:21 +02:00
|
|
|
|
{
|
2019-05-08 22:45:27 +02:00
|
|
|
|
public FileType FileType { get; set; } = FileType.Effect;
|
|
|
|
|
|
2019-04-22 23:52:21 +02:00
|
|
|
|
public bool CanSave { get; set; }
|
|
|
|
|
public string[] Description { get; set; } = new string[] { "Effect Link" };
|
|
|
|
|
public string[] Extension { get; set; } = new string[] { "*.bslnk", "*.belnk" };
|
|
|
|
|
public string FileName { get; set; }
|
|
|
|
|
public string FilePath { get; set; }
|
|
|
|
|
public IFileInfo IFileInfo { get; set; }
|
|
|
|
|
|
|
|
|
|
public bool Identify(System.IO.Stream stream)
|
|
|
|
|
{
|
2019-07-16 23:35:21 +02:00
|
|
|
|
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
|
2019-04-22 23:52:21 +02:00
|
|
|
|
{
|
|
|
|
|
return reader.CheckSignature(4, "XLNK");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public TextEditor OpenForm()
|
|
|
|
|
{
|
|
|
|
|
return new TextEditor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void FillEditor(UserControl control)
|
|
|
|
|
{
|
|
|
|
|
((TextEditor)control).FillEditor(ToText());
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-22 23:52:21 +02:00
|
|
|
|
public Type[] Types
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
List<Type> types = new List<Type>();
|
|
|
|
|
return types.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public string ToText()
|
|
|
|
|
{
|
|
|
|
|
XmlWriterSettings settings = new XmlWriterSettings
|
|
|
|
|
{
|
|
|
|
|
Encoding = Encoding.UTF8,
|
|
|
|
|
Indent = true,
|
|
|
|
|
IndentChars = " ",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
|
|
|
|
|
ns.Add("", "");
|
|
|
|
|
|
|
|
|
|
XmlDocument doc = new XmlDocument();
|
|
|
|
|
XmlDeclaration xmldecl = doc.CreateXmlDeclaration("1.0", null, null);
|
|
|
|
|
xmldecl.Encoding = "UTF-8";
|
|
|
|
|
xmldecl.Standalone = "yes";
|
|
|
|
|
|
|
|
|
|
var stringWriter = new StringWriter();
|
|
|
|
|
XmlSerializer serializer = new XmlSerializer(typeof(Header));
|
|
|
|
|
XmlWriter output = XmlWriter.Create(stringWriter, settings);
|
|
|
|
|
serializer.Serialize(output, header, ns);
|
|
|
|
|
return stringWriter.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Header header;
|
|
|
|
|
|
2019-04-22 23:52:21 +02:00
|
|
|
|
public void Load(System.IO.Stream stream)
|
|
|
|
|
{
|
|
|
|
|
CanSave = false;
|
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
header = new Header();
|
|
|
|
|
header.Read(new FileReader(stream));
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
|
|
|
|
var userData = new TreeNode("User Data");
|
2019-10-14 03:02:39 +02:00
|
|
|
|
// Nodes.Add(userData);
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
|
|
|
|
var hashes = new TreeNode("Hashes");
|
|
|
|
|
userData.Nodes.Add(hashes);
|
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
foreach (var hash in header.UserDataTable.CRC32Hashes)
|
2019-04-22 23:52:21 +02:00
|
|
|
|
hashes.Nodes.Add(new TreeNode(hash.ToString("x")));
|
|
|
|
|
|
|
|
|
|
var paramDefines = new TreeNode("Param Defines");
|
2019-10-14 03:02:39 +02:00
|
|
|
|
// Nodes.Add(paramDefines);
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
foreach (var param in header.ParamDefineTable.UserParams)
|
2019-04-22 23:52:21 +02:00
|
|
|
|
paramDefines.Nodes.Add(param.Name);
|
2019-10-14 03:02:39 +02:00
|
|
|
|
foreach (var param in header.ParamDefineTable.TriggerParams)
|
2019-04-22 23:52:21 +02:00
|
|
|
|
paramDefines.Nodes.Add(param.Name);
|
2019-10-14 03:02:39 +02:00
|
|
|
|
foreach (var param in header.ParamDefineTable.UserParams)
|
2019-04-22 23:52:21 +02:00
|
|
|
|
paramDefines.Nodes.Add(param.Name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Unload()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
2019-08-06 23:35:18 +02:00
|
|
|
|
|
|
|
|
|
public void Save(System.IO.Stream stream)
|
2019-04-22 23:52:21 +02:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Documentation from https://github.com/Kinnay/Nintendo-File-Formats/wiki/XLINK-File-Format#header
|
|
|
|
|
|
|
|
|
|
public class Header
|
|
|
|
|
{
|
|
|
|
|
public ushort ByteOrderMark;
|
|
|
|
|
|
|
|
|
|
public UserDataTable UserDataTable;
|
|
|
|
|
public ParamDefineTable ParamDefineTable;
|
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public List<ResourceAssetParamTable> ResourceAssetParamTables = new List<ResourceAssetParamTable>();
|
|
|
|
|
public List<TriggerOverwriteParamTable> TriggerOverwriteParamTables = new List<TriggerOverwriteParamTable>();
|
|
|
|
|
|
|
|
|
|
public List<LocalNameProperty> LocalNameProperties = new List<LocalNameProperty>();
|
|
|
|
|
public List<LocalNameProperty> LocalNameEnumProperties = new List<LocalNameProperty>();
|
|
|
|
|
|
|
|
|
|
internal uint nameTablePos;
|
|
|
|
|
|
2019-04-22 23:52:21 +02:00
|
|
|
|
public void Read(FileReader reader)
|
|
|
|
|
{
|
|
|
|
|
reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian;
|
|
|
|
|
reader.ReadSignature(4, "XLNK");
|
|
|
|
|
uint FileSize = reader.ReadUInt32();
|
|
|
|
|
uint Version = reader.ReadUInt32();
|
|
|
|
|
uint numResParam = reader.ReadUInt32();
|
|
|
|
|
uint numResAssetParam = reader.ReadUInt32();
|
|
|
|
|
uint numResTriggerOverwriteParam = reader.ReadUInt32();
|
|
|
|
|
uint triggerOverwriteParamTablePos = reader.ReadUInt32();
|
|
|
|
|
uint localPropertyNameRefTablePos = reader.ReadUInt32();
|
|
|
|
|
uint numLocalPropertyNameRefTable = reader.ReadUInt32();
|
|
|
|
|
uint numLocalPropertyEnumNameRefTable = reader.ReadUInt32();
|
|
|
|
|
uint numDirectValueTable = reader.ReadUInt32();
|
|
|
|
|
uint numRandomTable = reader.ReadUInt32();
|
|
|
|
|
uint numCurveTable = reader.ReadUInt32();
|
|
|
|
|
uint numCurvePointTable = reader.ReadUInt32();
|
|
|
|
|
uint exRegionPos = reader.ReadUInt32();
|
|
|
|
|
uint numUser = reader.ReadUInt32();
|
|
|
|
|
uint conditionTablePos = reader.ReadUInt32();
|
2019-10-14 03:02:39 +02:00
|
|
|
|
nameTablePos = reader.ReadUInt32();
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
|
|
|
|
UserDataTable = new UserDataTable();
|
|
|
|
|
UserDataTable.Read(reader, (int)numUser);
|
|
|
|
|
|
|
|
|
|
ParamDefineTable = new ParamDefineTable();
|
2019-10-14 03:02:39 +02:00
|
|
|
|
ParamDefineTable.Read(reader, this);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < numResAssetParam; i++)
|
|
|
|
|
{
|
|
|
|
|
var resAssetsParam = new ResourceAssetParamTable();
|
|
|
|
|
resAssetsParam.Read(reader);
|
|
|
|
|
ResourceAssetParamTables.Add(resAssetsParam);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reader.SeekBegin(triggerOverwriteParamTablePos);
|
|
|
|
|
for (int i = 0; i < numResTriggerOverwriteParam; i++)
|
|
|
|
|
{
|
|
|
|
|
var triggerOverwriteParamTbl = new TriggerOverwriteParamTable();
|
|
|
|
|
triggerOverwriteParamTbl.Read(reader);
|
|
|
|
|
TriggerOverwriteParamTables.Add(triggerOverwriteParamTbl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reader.SeekBegin(localPropertyNameRefTablePos);
|
|
|
|
|
for (int i = 0; i < numLocalPropertyNameRefTable; i++)
|
|
|
|
|
{
|
|
|
|
|
var localNameProp = new LocalNameProperty();
|
|
|
|
|
localNameProp.Read(reader, nameTablePos);
|
|
|
|
|
LocalNameProperties.Add(localNameProp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < numLocalPropertyEnumNameRefTable; i++)
|
|
|
|
|
{
|
|
|
|
|
var localNameProp = new LocalNameProperty();
|
|
|
|
|
localNameProp.Read(reader, nameTablePos);
|
|
|
|
|
LocalNameEnumProperties.Add(localNameProp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class LocalNameProperty
|
|
|
|
|
{
|
|
|
|
|
[XmlAttribute]
|
|
|
|
|
public string Name;
|
|
|
|
|
|
|
|
|
|
public void Read(FileReader reader, uint nameTableOffset)
|
|
|
|
|
{
|
|
|
|
|
uint offset = reader.ReadUInt32();
|
|
|
|
|
long pos = reader.Position;
|
|
|
|
|
|
|
|
|
|
reader.SeekBegin(offset + nameTableOffset);
|
|
|
|
|
Name = reader.ReadZeroTerminatedString(Encoding.GetEncoding(932));
|
|
|
|
|
|
|
|
|
|
reader.SeekBegin(pos);
|
2019-04-22 23:52:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class UserDataTable
|
|
|
|
|
{
|
|
|
|
|
public uint[] CRC32Hashes;
|
|
|
|
|
public uint[] ExRegionOffsets;
|
|
|
|
|
|
|
|
|
|
public void Read(FileReader reader, int EntryCount)
|
|
|
|
|
{
|
|
|
|
|
CRC32Hashes = reader.ReadUInt32s(EntryCount);
|
|
|
|
|
ExRegionOffsets = reader.ReadUInt32s(EntryCount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ParamDefineTable
|
|
|
|
|
{
|
|
|
|
|
public List<ParamDefineEntry> UserParams = new List<ParamDefineEntry>();
|
|
|
|
|
public List<ParamDefineEntry> AssetParams = new List<ParamDefineEntry>();
|
|
|
|
|
public List<ParamDefineEntry> TriggerParams = new List<ParamDefineEntry>();
|
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public void Read(FileReader reader, Header header)
|
2019-04-22 23:52:21 +02:00
|
|
|
|
{
|
|
|
|
|
uint SectionSize = reader.ReadUInt32();
|
|
|
|
|
uint numUserParams = reader.ReadUInt32();
|
|
|
|
|
uint numAssetParams = reader.ReadUInt32();
|
|
|
|
|
uint unknown = reader.ReadUInt32();
|
|
|
|
|
uint numTriggerParams = reader.ReadUInt32();
|
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
uint nameTblPos = (uint)reader.Position + ((numUserParams + numAssetParams + numTriggerParams) * 12);
|
|
|
|
|
|
2019-04-22 23:52:21 +02:00
|
|
|
|
for (int i = 0; i < numUserParams; i++)
|
|
|
|
|
{
|
|
|
|
|
var entry = new ParamDefineEntry();
|
2019-10-14 03:02:39 +02:00
|
|
|
|
entry.Read(reader, nameTblPos);
|
2019-04-22 23:52:21 +02:00
|
|
|
|
UserParams.Add(entry);
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < numAssetParams; i++)
|
|
|
|
|
{
|
|
|
|
|
var entry = new ParamDefineEntry();
|
2019-10-14 03:02:39 +02:00
|
|
|
|
entry.Read(reader, nameTblPos);
|
2019-04-22 23:52:21 +02:00
|
|
|
|
AssetParams.Add(entry);
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < numTriggerParams; i++)
|
|
|
|
|
{
|
|
|
|
|
var entry = new ParamDefineEntry();
|
2019-10-14 03:02:39 +02:00
|
|
|
|
entry.Read(reader, nameTblPos);
|
2019-04-22 23:52:21 +02:00
|
|
|
|
TriggerParams.Add(entry);
|
|
|
|
|
}
|
2019-10-14 03:02:39 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ParamDefineEntry
|
|
|
|
|
{
|
|
|
|
|
[XmlAttribute]
|
|
|
|
|
public string Name { get; set; }
|
|
|
|
|
|
|
|
|
|
[XmlAttribute]
|
|
|
|
|
public uint Type { get; set; }
|
|
|
|
|
|
|
|
|
|
[XmlElement("int", typeof(int))]
|
|
|
|
|
[XmlElement("str", typeof(string))]
|
|
|
|
|
[XmlElement("uint", typeof(uint))]
|
|
|
|
|
[XmlElement("float", typeof(float))]
|
|
|
|
|
public object DefaultValue { get; set; }
|
|
|
|
|
|
|
|
|
|
public void Read(FileReader reader, uint nameTblPos)
|
|
|
|
|
{
|
|
|
|
|
long pos = reader.Position;
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
uint NamePos = reader.ReadUInt32(); //Offset from string table
|
|
|
|
|
Type = reader.ReadUInt32();
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
Console.WriteLine("Type " + Type);
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
if (Type == 0)
|
|
|
|
|
{
|
|
|
|
|
uint defaultPos = reader.ReadUInt32();
|
|
|
|
|
reader.SeekBegin(nameTblPos + defaultPos);
|
|
|
|
|
DefaultValue = reader.ReadZeroTerminatedString();
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
Console.WriteLine("defaultPos " + defaultPos);
|
|
|
|
|
}
|
|
|
|
|
else if (Type == 1)
|
|
|
|
|
DefaultValue = reader.ReadSingle();
|
|
|
|
|
else
|
|
|
|
|
DefaultValue = reader.ReadInt32();
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
reader.SeekBegin(nameTblPos + NamePos);
|
|
|
|
|
Name = reader.ReadZeroTerminatedString();
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Name " + Name);
|
|
|
|
|
|
|
|
|
|
reader.SeekBegin(pos + 12);
|
2019-04-22 23:52:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public class ResourceAssetParamTable
|
2019-04-22 23:52:21 +02:00
|
|
|
|
{
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public ulong Mask;
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public uint FirstReference;
|
|
|
|
|
public uint SecondReference;
|
|
|
|
|
public uint ThirdReference;
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
|
|
|
|
public void Read(FileReader reader)
|
|
|
|
|
{
|
2019-10-14 03:02:39 +02:00
|
|
|
|
Mask = reader.ReadUInt64();
|
|
|
|
|
if ((Mask & 1) != 0)
|
|
|
|
|
FirstReference = reader.ReadUInt32();
|
|
|
|
|
if ((Mask & 2) != 0)
|
|
|
|
|
SecondReference = reader.ReadUInt32();
|
|
|
|
|
if ((Mask & 4) != 0)
|
|
|
|
|
ThirdReference = reader.ReadUInt32();
|
2019-04-22 23:52:21 +02:00
|
|
|
|
}
|
2019-10-14 03:02:39 +02:00
|
|
|
|
}
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public class TriggerOverwriteParamTable
|
|
|
|
|
{
|
|
|
|
|
public uint Mask;
|
2019-04-22 23:52:21 +02:00
|
|
|
|
|
2019-10-14 03:02:39 +02:00
|
|
|
|
public uint FirstReference;
|
|
|
|
|
public uint SecondReference;
|
|
|
|
|
public uint ThirdReference;
|
|
|
|
|
|
|
|
|
|
public void Read(FileReader reader)
|
|
|
|
|
{
|
|
|
|
|
Mask = reader.ReadUInt32();
|
|
|
|
|
if ((Mask & 1) != 0)
|
|
|
|
|
FirstReference = reader.ReadUInt32();
|
|
|
|
|
if ((Mask & 2) != 0)
|
|
|
|
|
SecondReference = reader.ReadUInt32();
|
|
|
|
|
if ((Mask & 4) != 0)
|
|
|
|
|
ThirdReference = reader.ReadUInt32();
|
2019-04-22 23:52:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|