Support latest BEA, multiple UVs for bfres, bounding box/shadow fixes, and more
This commit is contained in:
parent
44a88b4191
commit
bdec926c2d
@ -179,6 +179,12 @@ namespace FirstPlugin
|
|||||||
asset.FileName = node.FileName;
|
asset.FileName = node.FileName;
|
||||||
asset.FileData = node.CompressedData;
|
asset.FileData = node.CompressedData;
|
||||||
asset.UncompressedSize = node.FileData.Length;
|
asset.UncompressedSize = node.FileData.Length;
|
||||||
|
asset.FileID1 = node.FileID1;
|
||||||
|
asset.FileID2 = node.FileID2;
|
||||||
|
asset.FileHash = node.FileHash;
|
||||||
|
asset.Unknown3 = node.Unknown3;
|
||||||
|
asset.FileType = node.FileType;
|
||||||
|
|
||||||
beaFile.FileList.Add(asset.FileName, asset);
|
beaFile.FileList.Add(asset.FileName, asset);
|
||||||
beaFile.FileDictionary.Add(asset.FileName);
|
beaFile.FileDictionary.Add(asset.FileName);
|
||||||
}
|
}
|
||||||
@ -204,6 +210,9 @@ namespace FirstPlugin
|
|||||||
public class FileEntry : ArchiveFileInfo
|
public class FileEntry : ArchiveFileInfo
|
||||||
{
|
{
|
||||||
BEA ArchiveFile;
|
BEA ArchiveFile;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public FileEntry(BEA bea)
|
public FileEntry(BEA bea)
|
||||||
{
|
{
|
||||||
ArchiveFile = bea;
|
ArchiveFile = bea;
|
||||||
@ -233,6 +242,11 @@ namespace FirstPlugin
|
|||||||
public ushort unk1;
|
public ushort unk1;
|
||||||
public ushort unk2;
|
public ushort unk2;
|
||||||
public bool IsCompressed;
|
public bool IsCompressed;
|
||||||
|
public ulong FileID1;
|
||||||
|
public ulong FileID2;
|
||||||
|
public uint FileHash;
|
||||||
|
public string FileType = "";
|
||||||
|
public uint Unknown3;
|
||||||
|
|
||||||
public override byte[] FileData
|
public override byte[] FileData
|
||||||
{
|
{
|
||||||
@ -248,6 +262,8 @@ namespace FirstPlugin
|
|||||||
unk1 = 2;
|
unk1 = 2;
|
||||||
unk2 = 12;
|
unk2 = 12;
|
||||||
FileData = data;
|
FileData = data;
|
||||||
|
FileID1 = (ulong)new Random().Next(0, int.MaxValue);
|
||||||
|
FileID2 = (ulong)new Random().Next(0, int.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Dictionary<string, string> ExtensionImageKeyLookup
|
public override Dictionary<string, string> ExtensionImageKeyLookup
|
||||||
@ -309,6 +325,12 @@ namespace FirstPlugin
|
|||||||
fileEntry.unk2 = asset.unk2;
|
fileEntry.unk2 = asset.unk2;
|
||||||
fileEntry.IsCompressed = asset.IsCompressed;
|
fileEntry.IsCompressed = asset.IsCompressed;
|
||||||
fileEntry.CompressedData = asset.FileData;
|
fileEntry.CompressedData = asset.FileData;
|
||||||
|
fileEntry.Unknown3 = asset.Unknown3;
|
||||||
|
fileEntry.FileID1 = asset.FileID1;
|
||||||
|
fileEntry.FileID2 = asset.FileID2;
|
||||||
|
fileEntry.FileHash = asset.FileHash;
|
||||||
|
fileEntry.FileType = asset.FileType;
|
||||||
|
|
||||||
return fileEntry;
|
return fileEntry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
96
File_Format_Library/FileFormats/Archives/BEA/ASST.cs
Normal file
96
File_Format_Library/FileFormats/Archives/BEA/ASST.cs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
using System;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BezelEngineArchive_Lib
|
||||||
|
{
|
||||||
|
public class ASST : IFileData //File asset
|
||||||
|
{
|
||||||
|
private const string _signature = "ASST";
|
||||||
|
|
||||||
|
public ushort unk = 2;
|
||||||
|
public ushort unk2 = 12;
|
||||||
|
public string FileName;
|
||||||
|
public long UncompressedSize;
|
||||||
|
public bool IsCompressed = true;
|
||||||
|
|
||||||
|
public ulong FileID1;
|
||||||
|
public ulong FileID2;
|
||||||
|
|
||||||
|
public uint FileHash;
|
||||||
|
public uint Unknown3;
|
||||||
|
|
||||||
|
public byte[] FileData;
|
||||||
|
|
||||||
|
public uint FileSize;
|
||||||
|
public long FileOffset;
|
||||||
|
|
||||||
|
public string FileType = "";
|
||||||
|
|
||||||
|
public BezelEngineArchive ParentArchive;
|
||||||
|
|
||||||
|
void IFileData.Load(FileLoader loader)
|
||||||
|
{
|
||||||
|
loader.CheckSignature(_signature);
|
||||||
|
loader.LoadBlockHeader();
|
||||||
|
unk = loader.ReadUInt16();
|
||||||
|
unk2 = loader.ReadUInt16();
|
||||||
|
FileSize = loader.ReadUInt32();
|
||||||
|
UncompressedSize = loader.ReadUInt32();
|
||||||
|
|
||||||
|
if (loader.Archive.VersionMajor2 >= 5)
|
||||||
|
{
|
||||||
|
FileType = Encoding.ASCII.GetString(loader.ReadBytes(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
Unknown3 = loader.ReadUInt32();
|
||||||
|
|
||||||
|
if (loader.Archive.VersionMajor2 >= 6)
|
||||||
|
{
|
||||||
|
FileID1 = loader.ReadUInt64();
|
||||||
|
FileID2 = loader.ReadUInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileOffset = loader.ReadInt64();
|
||||||
|
FileName = loader.LoadString();
|
||||||
|
|
||||||
|
using (loader.TemporarySeek(FileOffset, SeekOrigin.Begin)) {
|
||||||
|
FileData = loader.ReadBytes((int)FileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UncompressedSize == FileSize)
|
||||||
|
IsCompressed = false;
|
||||||
|
}
|
||||||
|
void IFileData.Save(FileSaver saver)
|
||||||
|
{
|
||||||
|
saver.WriteSignature(_signature);
|
||||||
|
saver.SaveBlockHeader();
|
||||||
|
saver.Write(unk);
|
||||||
|
saver.Write(unk2);
|
||||||
|
saver.Write((uint)FileData.Length);
|
||||||
|
saver.Write((uint)UncompressedSize);
|
||||||
|
|
||||||
|
if (saver.Archive.VersionMajor2 >= 5)
|
||||||
|
{
|
||||||
|
if (FileType.Length > 8)
|
||||||
|
throw new Exception($"Invalid file type length! Must be equal or less than 12 bytes! {FileType}!");
|
||||||
|
|
||||||
|
saver.Write(Encoding.ASCII.GetBytes(FileType));
|
||||||
|
saver.Write(new byte[8 - FileType.Length]);
|
||||||
|
}
|
||||||
|
|
||||||
|
saver.Write(Unknown3);
|
||||||
|
|
||||||
|
if (saver.Archive.VersionMajor2 >= 6)
|
||||||
|
{
|
||||||
|
saver.Write(FileID1);
|
||||||
|
saver.Write(FileID2);
|
||||||
|
}
|
||||||
|
|
||||||
|
saver.SaveBlock(FileData, (uint)saver.Archive.RawAlignment, () => saver.Write(FileData));
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "Asst File Name Offset");
|
||||||
|
saver.SaveString(FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,182 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BezelEngineArchive_Lib
|
||||||
|
{
|
||||||
|
public class BezelEngineArchive : IFileData
|
||||||
|
{
|
||||||
|
private const string _signature = "SCNE";
|
||||||
|
|
||||||
|
public BezelEngineArchive(Stream stream, bool leaveOpen = false)
|
||||||
|
{
|
||||||
|
using (FileLoader loader = new FileLoader(this, stream, leaveOpen))
|
||||||
|
{
|
||||||
|
loader.Execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BezelEngineArchive(string fileName)
|
||||||
|
{
|
||||||
|
using (FileLoader loader = new FileLoader(this, fileName))
|
||||||
|
{
|
||||||
|
loader.Execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ushort ByteOrder { get; private set; }
|
||||||
|
public uint VersionMajor { get; set; }
|
||||||
|
public uint VersionMajor2 { get; set; }
|
||||||
|
public uint VersionMinor { get; set; }
|
||||||
|
public uint VersionMinor2 { get; set; }
|
||||||
|
public uint Alignment { get; set; }
|
||||||
|
public uint TargetAddressSize { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string CompressionName { get; set; }
|
||||||
|
|
||||||
|
public ResDict FileDictionary { get; set; } //Todo, Need to setup ResDict to grab indexes quicker
|
||||||
|
public Dictionary<string, ASST> FileList { get; set; } //Use a dictionary for now so i can look up files quickly
|
||||||
|
|
||||||
|
/// <summary>s
|
||||||
|
/// Gets or sets the alignment to use for raw data blocks in the file.
|
||||||
|
/// </summary>
|
||||||
|
public int RawAlignment { get; set; }
|
||||||
|
|
||||||
|
public List<string> ReferenceList = new List<string>();
|
||||||
|
|
||||||
|
public void Save(Stream stream, bool leaveOpen = false)
|
||||||
|
{
|
||||||
|
using (FileSaver saver = new FileSaver(this, stream, leaveOpen))
|
||||||
|
{
|
||||||
|
saver.Execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(string FileName)
|
||||||
|
{
|
||||||
|
using (FileSaver saver = new FileSaver(this, FileName))
|
||||||
|
{
|
||||||
|
saver.Execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal uint SaveVersion()
|
||||||
|
{
|
||||||
|
return VersionMajor << 24 | VersionMajor2 << 16 | VersionMinor << 8 | VersionMinor2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetVersionInfo(uint Version)
|
||||||
|
{
|
||||||
|
VersionMajor = Version >> 24;
|
||||||
|
VersionMajor2 = Version >> 16 & 0xFF;
|
||||||
|
VersionMinor = Version >> 8 & 0xFF;
|
||||||
|
VersionMinor2 = Version & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Stream _stream;
|
||||||
|
|
||||||
|
void IFileData.Load(FileLoader loader)
|
||||||
|
{
|
||||||
|
_stream = loader.BaseStream;
|
||||||
|
|
||||||
|
loader.CheckSignature(_signature);
|
||||||
|
uint padding = loader.ReadUInt32();
|
||||||
|
uint Version = loader.ReadUInt32();
|
||||||
|
SetVersionInfo(Version);
|
||||||
|
ByteOrder = loader.ReadUInt16();
|
||||||
|
Alignment = (uint)loader.ReadByte();
|
||||||
|
TargetAddressSize = (uint)loader.ReadByte();
|
||||||
|
uint Padding = loader.ReadUInt32(); //Usually name offset for file with other switch formats
|
||||||
|
ushort Padding2 = loader.ReadUInt16();
|
||||||
|
ushort BlockOffset = loader.ReadUInt16(); //Goes to ASST section which seems to have block headers
|
||||||
|
uint RelocationTableOffset = loader.ReadUInt32();
|
||||||
|
uint DataOffset = loader.ReadUInt32(); //data or end of file offset
|
||||||
|
var FileCount = loader.ReadUInt32();
|
||||||
|
var RefCount = loader.ReadUInt32();
|
||||||
|
|
||||||
|
ulong FileInfoOffset = 0;
|
||||||
|
|
||||||
|
if (VersionMajor2 >= 5)
|
||||||
|
{
|
||||||
|
var AssetOffset = loader.ReadUInt64(); //asset offset
|
||||||
|
FileInfoOffset = loader.ReadUInt64();
|
||||||
|
FileDictionary = loader.LoadDict();
|
||||||
|
Name = loader.LoadString();
|
||||||
|
CompressionName = loader.LoadString();
|
||||||
|
ulong RefOffset = loader.ReadUInt64();
|
||||||
|
|
||||||
|
ReferenceList = loader.LoadCustom(() =>
|
||||||
|
{
|
||||||
|
List<string> list = new List<string>();
|
||||||
|
for (int i = 0; i < (int)RefCount; i++)
|
||||||
|
list.Add(loader.LoadString());
|
||||||
|
return list;
|
||||||
|
|
||||||
|
}, (long)RefOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FileInfoOffset = loader.ReadUInt64();
|
||||||
|
FileDictionary = loader.LoadDict();
|
||||||
|
ulong unk = loader.ReadUInt64();
|
||||||
|
Name = loader.LoadString();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileList = loader.LoadCustom(() =>
|
||||||
|
{
|
||||||
|
Dictionary<string, ASST> asstList = new Dictionary<string, ASST>();
|
||||||
|
for (int i = 0; i < (int)FileCount; i++)
|
||||||
|
{
|
||||||
|
var asset = loader.Load<ASST>();
|
||||||
|
asstList.Add(asset.FileName, asset);
|
||||||
|
}
|
||||||
|
return asstList;
|
||||||
|
}, (long)FileInfoOffset);
|
||||||
|
}
|
||||||
|
void IFileData.Save(FileSaver saver)
|
||||||
|
{
|
||||||
|
RawAlignment = (1 << (int)Alignment);
|
||||||
|
|
||||||
|
saver.WriteSignature(_signature);
|
||||||
|
saver.Write(0);
|
||||||
|
saver.Write(SaveVersion());
|
||||||
|
saver.Write(ByteOrder);
|
||||||
|
saver.Write((byte)Alignment);
|
||||||
|
saver.Write((byte)TargetAddressSize);
|
||||||
|
saver.Write(0);
|
||||||
|
saver.Write((ushort)0);
|
||||||
|
saver.SaveFirstBlock();
|
||||||
|
saver.SaveRelocationTablePointer();
|
||||||
|
saver.SaveFileSize();
|
||||||
|
saver.Write((uint)FileList.Count);
|
||||||
|
saver.Write(ReferenceList == null ? 0u : (uint)ReferenceList.Count);
|
||||||
|
|
||||||
|
if (VersionMajor2 >= 5)
|
||||||
|
{
|
||||||
|
saver.SaveAssetBlock();
|
||||||
|
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "Asst Offset Array");
|
||||||
|
saver.SaveFileAsstPointer();
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "DIC");
|
||||||
|
saver.SaveFileDictionaryPointer();
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "FileName");
|
||||||
|
saver.SaveString(Name);
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "CompressionName");
|
||||||
|
saver.SaveString(CompressionName);
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "Ref Offset");
|
||||||
|
saver.SaveAssetRefPointer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "Asst Offset Array");
|
||||||
|
saver.SaveFileAsstPointer();
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "DIC");
|
||||||
|
saver.SaveFileDictionaryPointer();
|
||||||
|
saver.Write(0L);
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "FileName");
|
||||||
|
saver.SaveString(Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
428
File_Format_Library/FileFormats/Archives/BEA/DIC/ResDict.cs
Normal file
428
File_Format_Library/FileFormats/Archives/BEA/DIC/ResDict.cs
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace BezelEngineArchive_Lib
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the non-generic base of a dictionary which can quickly look up <see cref="IResData"/> instances via
|
||||||
|
/// key or index.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Count = {Count}")]
|
||||||
|
[DebuggerTypeProxy(typeof(TypeProxy))]
|
||||||
|
public class ResDict : IEnumerable, IFileData
|
||||||
|
{
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private IList<Node> _nodes; // Includes root node.
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ResDict"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ResDict()
|
||||||
|
{
|
||||||
|
// Create root node.
|
||||||
|
_nodes = new List<Node> { new Node() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of instances stored.
|
||||||
|
/// </summary>
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get { return _nodes.Count - 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- OPERATORS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the given <paramref name="key"/> to insert in the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentException">Duplicated <paramref name="key"/> instances
|
||||||
|
/// already exists.</exception>
|
||||||
|
public void Add(string key)
|
||||||
|
{
|
||||||
|
if (!ContainsKey((key)))
|
||||||
|
_nodes.Add(new Node(key));
|
||||||
|
else
|
||||||
|
throw new Exception($"key {key} already exists in the dictionary!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the given <paramref name="key"/> from the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentException">Duplicated <paramref name="key"/> instances
|
||||||
|
/// already exists.</exception>
|
||||||
|
public void Remove(string key)
|
||||||
|
{
|
||||||
|
_nodes.Remove(_nodes.Where(n => n.Key == key).FirstOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the given <paramref name="key"/> is in the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if <paramref name="key"/> was found in the dictionary; otherwise <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public bool ContainsKey(string key)
|
||||||
|
{
|
||||||
|
return _nodes.Any(p => p.Key == key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the key given <paramref name="index"/> is within range of the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
public string GetKey(int index)
|
||||||
|
{
|
||||||
|
if (index < _nodes.Count || index > 0)
|
||||||
|
return _nodes[index + 1].Key;
|
||||||
|
else
|
||||||
|
throw new Exception($"Index {index} is out of range!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all elements from the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
// Create new collection with root node.
|
||||||
|
_nodes.Clear();
|
||||||
|
_nodes.Add(new Node());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void IFileData.Load(FileLoader loader)
|
||||||
|
{
|
||||||
|
// Read the header.
|
||||||
|
uint signature = loader.ReadUInt32(); //Always 0x00000000
|
||||||
|
int numNodes = loader.ReadInt32(); // Excludes root node.
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
// Read the nodes including the root node.
|
||||||
|
List<Node> nodes = new List<Node>();
|
||||||
|
for (; numNodes >= 0; numNodes--)
|
||||||
|
{
|
||||||
|
nodes.Add(ReadNode(loader));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
_nodes = nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IFileData.Save(FileSaver saver)
|
||||||
|
{
|
||||||
|
// Update the Patricia trie values in the nodes.
|
||||||
|
UpdateNodes();
|
||||||
|
|
||||||
|
// Write header.
|
||||||
|
saver.WriteSignature("_DIC");
|
||||||
|
saver.Write(Count);
|
||||||
|
|
||||||
|
// Write nodes.
|
||||||
|
int index = -1; // Start at -1 due to root node.
|
||||||
|
int curNode = 0;
|
||||||
|
foreach (Node node in _nodes)
|
||||||
|
{
|
||||||
|
saver.Write(node.Reference);
|
||||||
|
saver.Write(node.IdxLeft);
|
||||||
|
saver.Write(node.IdxRight);
|
||||||
|
|
||||||
|
if (curNode == 0)
|
||||||
|
{
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "DIC " + node.Key); // <------------ Entry Set
|
||||||
|
saver.SaveString("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saver.SaveRelocateEntryToSection(saver.Position, 1, 1, 0, 1, "DIC " + node.Key); // <------------ Entry Set
|
||||||
|
saver.SaveString(node.Key);
|
||||||
|
}
|
||||||
|
curNode++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
IEnumerator<string> GetEnumerator()
|
||||||
|
{
|
||||||
|
foreach (Node node in Nodes)
|
||||||
|
{
|
||||||
|
yield return node.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns only the publically visible nodes, excluding the root node.
|
||||||
|
/// </summary>
|
||||||
|
protected IEnumerable<Node> Nodes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
for (int i = 1; i < _nodes.Count; i++)
|
||||||
|
{
|
||||||
|
yield return _nodes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static string ToBinaryString(string text, Encoding encoding)
|
||||||
|
{
|
||||||
|
return string.Join("", encoding.GetBytes(text).Select(n => Convert.ToString(n, 2).PadLeft(8, '0')));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _bit(BigInteger n, int b)
|
||||||
|
{
|
||||||
|
BigInteger test = (n >> (int)(b & 0xffffffff)) & 1;
|
||||||
|
return (int)test;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int first_1bit(BigInteger n)
|
||||||
|
{
|
||||||
|
int bitlength1 = BitLength(n);
|
||||||
|
for (int i = 0; i < bitlength1; i++)
|
||||||
|
{
|
||||||
|
if (((n >> i) & 1) == 1)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Operation Failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bit_mismatch(BigInteger int1, BigInteger int2)
|
||||||
|
{
|
||||||
|
int bitlength1 = BitLength(int1);
|
||||||
|
int bitlength2 = BitLength(int2);
|
||||||
|
|
||||||
|
for (int i = 0; i < Math.Max(bitlength1, bitlength2); i++)
|
||||||
|
{
|
||||||
|
if (((int1 >> i) & 1) != ((int2 >> i) & 1))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int BitLength(BigInteger bits)
|
||||||
|
{
|
||||||
|
int bitLength = 0;
|
||||||
|
while (bits / 2 != 0)
|
||||||
|
{
|
||||||
|
bits /= 2;
|
||||||
|
bitLength++;
|
||||||
|
}
|
||||||
|
bitLength += 1;
|
||||||
|
return bitLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Tree
|
||||||
|
{
|
||||||
|
public Node root;
|
||||||
|
|
||||||
|
public Dictionary<BigInteger, Tuple<int, Node>> entries;
|
||||||
|
|
||||||
|
public Tree()
|
||||||
|
{
|
||||||
|
entries = new Dictionary<BigInteger, Tuple<int, Node>>();
|
||||||
|
|
||||||
|
root = new Node(0, -1, root);
|
||||||
|
root.Parent = root;
|
||||||
|
|
||||||
|
insertEntry(0, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetCompactBitIdx()
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertEntry(BigInteger data, Node node)
|
||||||
|
{
|
||||||
|
entries[data] = (Tuple.Create(entries.Count, node));
|
||||||
|
}
|
||||||
|
|
||||||
|
Node Search(BigInteger data, bool prev)
|
||||||
|
{
|
||||||
|
if (root.Child[0] == root)
|
||||||
|
return root;
|
||||||
|
|
||||||
|
Node node = root.Child[0];
|
||||||
|
Node prevNode = node;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
prevNode = node;
|
||||||
|
node = node.Child[_bit(data, node.bitInx)];
|
||||||
|
if (node.bitInx <= prevNode.bitInx)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (prev)
|
||||||
|
return prevNode;
|
||||||
|
else
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(string Name)
|
||||||
|
{
|
||||||
|
string bits = ToBinaryString(Name, Encoding.UTF8);
|
||||||
|
BigInteger data = bits.Aggregate(new BigInteger(), (b, c) => b * 2 + c - '0');
|
||||||
|
Node current = Search(data, true);
|
||||||
|
int bitIdx = bit_mismatch(current.Data, data);
|
||||||
|
|
||||||
|
while (bitIdx < current.Parent.bitInx)
|
||||||
|
current = current.Parent;
|
||||||
|
|
||||||
|
if (bitIdx < current.bitInx)
|
||||||
|
{
|
||||||
|
Node newNode = new Node(data, bitIdx, current.Parent);
|
||||||
|
newNode.Child[_bit(data, bitIdx) ^ 1] = current;
|
||||||
|
current.Parent.Child[_bit(data, current.Parent.bitInx)] = newNode;
|
||||||
|
current.Parent = newNode;
|
||||||
|
|
||||||
|
insertEntry(data, newNode);
|
||||||
|
}
|
||||||
|
else if (bitIdx > current.bitInx)
|
||||||
|
{
|
||||||
|
Node newNode = new Node(data, bitIdx, current);
|
||||||
|
if (_bit(current.Data, bitIdx) == (_bit(data, bitIdx) ^ 1))
|
||||||
|
newNode.Child[_bit(data, bitIdx) ^ 1] = current;
|
||||||
|
else
|
||||||
|
newNode.Child[_bit(data, bitIdx) ^ 1] = root;
|
||||||
|
|
||||||
|
|
||||||
|
current.Child[_bit(data, current.bitInx)] = newNode;
|
||||||
|
insertEntry(data, newNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
int NewBitIdx = first_1bit(data);
|
||||||
|
|
||||||
|
if (current.Child[_bit(data, bitIdx)] != root)
|
||||||
|
NewBitIdx = bit_mismatch(current.Child[_bit(data, bitIdx)].Data, data);
|
||||||
|
Node newNode = new Node(data, NewBitIdx, current);
|
||||||
|
|
||||||
|
newNode.Child[_bit(data, NewBitIdx) ^ 1] = current.Child[_bit(data, bitIdx)];
|
||||||
|
current.Child[_bit(data, bitIdx)] = newNode;
|
||||||
|
insertEntry(data, newNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void UpdateNodes()
|
||||||
|
{
|
||||||
|
Tree tree = new Tree();
|
||||||
|
|
||||||
|
// Create a new root node with empty key so the length can be retrieved throughout the process.
|
||||||
|
_nodes[0] = new Node() { Key = String.Empty, bitInx = -1, Parent = _nodes[0] };
|
||||||
|
|
||||||
|
// Update the data-referencing nodes.
|
||||||
|
for (ushort i = 1; i < _nodes.Count; i++)
|
||||||
|
tree.Insert(_nodes[i].Key);
|
||||||
|
|
||||||
|
int CurEntry = 0;
|
||||||
|
foreach (var entry in tree.entries.Values)
|
||||||
|
{
|
||||||
|
Node node = entry.Item2;
|
||||||
|
|
||||||
|
node.Reference = (uint)(node.GetCompactBitIdx() & 0xffffffff);
|
||||||
|
node.IdxLeft = (ushort)tree.entries[node.Child[0].Data].Item1;
|
||||||
|
node.IdxRight = (ushort)tree.entries[node.Child[1].Data].Item1;
|
||||||
|
node.Key = node.GetName();
|
||||||
|
_nodes[CurEntry] = node;
|
||||||
|
|
||||||
|
CurEntry++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the dummy empty key in the root again.
|
||||||
|
_nodes[0].Key = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node ReadNode(FileLoader loader)
|
||||||
|
{
|
||||||
|
return new Node()
|
||||||
|
{
|
||||||
|
Reference = loader.ReadUInt32(),
|
||||||
|
IdxLeft = loader.ReadUInt16(),
|
||||||
|
IdxRight = loader.ReadUInt16(),
|
||||||
|
Key = loader.LoadString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- CLASSES ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a node forming the Patricia trie of the dictionary.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay(nameof(Node) + " {" + nameof(Key) + "}")]
|
||||||
|
protected class Node
|
||||||
|
{
|
||||||
|
internal const int SizeInBytes = 16;
|
||||||
|
|
||||||
|
internal List<Node> Child = new List<Node>();
|
||||||
|
internal Node Parent;
|
||||||
|
internal int bitInx;
|
||||||
|
internal BigInteger Data;
|
||||||
|
internal uint Reference;
|
||||||
|
internal ushort IdxLeft;
|
||||||
|
internal ushort IdxRight;
|
||||||
|
internal string Key;
|
||||||
|
|
||||||
|
internal Node()
|
||||||
|
{
|
||||||
|
Child.Add(this);
|
||||||
|
Child.Add(this);
|
||||||
|
Reference = UInt32.MaxValue;
|
||||||
|
}
|
||||||
|
internal string GetName()
|
||||||
|
{
|
||||||
|
BigInteger data = BitLength(Data) + 7 / 8;
|
||||||
|
byte[] stringBytes = Data.ToByteArray();
|
||||||
|
Array.Reverse(stringBytes, 0, stringBytes.Length); //Convert to big endian
|
||||||
|
return Encoding.UTF8.GetString(stringBytes); //Decode byte[] to string
|
||||||
|
}
|
||||||
|
internal int GetCompactBitIdx()
|
||||||
|
{
|
||||||
|
int byteIndx = bitInx / 8;
|
||||||
|
return (byteIndx << 3) | bitInx - 8 * byteIndx;
|
||||||
|
}
|
||||||
|
internal Node(BigInteger data, int bitidx, Node parent) : this()
|
||||||
|
{
|
||||||
|
Data = data;
|
||||||
|
bitInx = bitidx;
|
||||||
|
Parent = parent;
|
||||||
|
}
|
||||||
|
internal Node(string key) : this()
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TypeProxy
|
||||||
|
{
|
||||||
|
private ResDict _dict;
|
||||||
|
|
||||||
|
internal TypeProxy(ResDict dict)
|
||||||
|
{
|
||||||
|
_dict = dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
|
||||||
|
namespace BezelEngineArchive_Lib
|
||||||
|
{
|
||||||
|
//Thanks to Syroot for the IO and methods
|
||||||
|
public class FileLoader : BinaryDataReader
|
||||||
|
{
|
||||||
|
private IDictionary<uint, IFileData> _dataMap;
|
||||||
|
|
||||||
|
public BezelEngineArchive Archive;
|
||||||
|
|
||||||
|
public FileLoader(BezelEngineArchive bea, Stream stream, bool leaveOpen = true)
|
||||||
|
: base(stream, Encoding.ASCII, leaveOpen)
|
||||||
|
{
|
||||||
|
ByteOrder = ByteOrder.LittleEndian;
|
||||||
|
Archive = bea;
|
||||||
|
_dataMap = new Dictionary<uint, IFileData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FileLoader(BezelEngineArchive bea, string fileName)
|
||||||
|
: this(bea, new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal T LoadCustom<T>(Func<T> callback, long? offset = null)
|
||||||
|
{
|
||||||
|
offset = offset ?? ReadOffset();
|
||||||
|
if (offset == 0) return default(T);
|
||||||
|
|
||||||
|
using (this.TemporarySeek(offset.Value, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
return callback.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string LoadString(Encoding encoding = null)
|
||||||
|
{
|
||||||
|
long offset = ReadInt64();
|
||||||
|
if (offset == 0) return null;
|
||||||
|
|
||||||
|
encoding = encoding ?? Encoding;
|
||||||
|
using (this.TemporarySeek(offset, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
ushort count = ReadUInt16();
|
||||||
|
return this.ReadString(count, encoding: encoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal void LoadBlockHeader()
|
||||||
|
{
|
||||||
|
uint offset = ReadUInt32();
|
||||||
|
ulong size = ReadUInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Execute()
|
||||||
|
{
|
||||||
|
Seek(0, SeekOrigin.Begin);
|
||||||
|
((IFileData)Archive).Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal long ReadOffset()
|
||||||
|
{
|
||||||
|
long offset = ReadInt64();
|
||||||
|
return offset == 0 ? 0 : offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal T Load<T>()
|
||||||
|
where T : IFileData, new()
|
||||||
|
{
|
||||||
|
long offset = ReadOffset();
|
||||||
|
if (offset == 0) return default(T);
|
||||||
|
|
||||||
|
// Seek to the instance data and load it.
|
||||||
|
using (this.TemporarySeek(offset, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
return ReadData<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IList<T> LoadList<T>(int count, long? offset = null)
|
||||||
|
where T : IFileData, new()
|
||||||
|
{
|
||||||
|
List<T> list = new List<T>(count);
|
||||||
|
offset = offset ?? ReadOffset();
|
||||||
|
if (offset == 0 || count == 0) return list;
|
||||||
|
|
||||||
|
// Seek to the list start and read it.
|
||||||
|
using (this.TemporarySeek(offset.Value, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
for (; count > 0; count--)
|
||||||
|
{
|
||||||
|
list.Add(ReadData<T>());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ResDict LoadDict()
|
||||||
|
{
|
||||||
|
long offset = ReadInt64();
|
||||||
|
if (offset == 0) return new ResDict();
|
||||||
|
|
||||||
|
using (this.TemporarySeek(offset, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
ResDict dict = new ResDict();
|
||||||
|
((IFileData)dict).Load(this);
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void CheckSignature(string validSignature)
|
||||||
|
{
|
||||||
|
// Read the actual signature and compare it.
|
||||||
|
string signature = this.ReadString(sizeof(uint), Encoding.ASCII);
|
||||||
|
if (signature != validSignature)
|
||||||
|
{
|
||||||
|
throw new Exception($"Invalid signature, expected '{validSignature}' but got '{signature}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private T ReadData<T>()
|
||||||
|
where T : IFileData, new()
|
||||||
|
{
|
||||||
|
uint offset = (uint)Position;
|
||||||
|
|
||||||
|
// Same data can be referenced multiple times. Load it in any case to move in the stream, needed for lists.
|
||||||
|
T instance = new T();
|
||||||
|
instance.Load(this);
|
||||||
|
|
||||||
|
// If possible, return an instance already representing the data.
|
||||||
|
if (_dataMap.TryGetValue(offset, out IFileData existingInstance))
|
||||||
|
{
|
||||||
|
return (T)existingInstance;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dataMap.Add(offset, instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,455 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
|
||||||
|
namespace BezelEngineArchive_Lib
|
||||||
|
{
|
||||||
|
public class FileSaver : BinaryDataWriter
|
||||||
|
{
|
||||||
|
private IDictionary<string, StringEntry> _savedStrings;
|
||||||
|
private List<long> _savedBlockPositions;
|
||||||
|
private IDictionary<object, BlockEntry> _savedBlocks;
|
||||||
|
private List<RelocationEntry> _savedSection1Entries;
|
||||||
|
private List<RelocationSection> _savedRelocatedSections;
|
||||||
|
private List<ItemEntry> _savedItems;
|
||||||
|
|
||||||
|
internal BezelEngineArchive Archive;
|
||||||
|
private long _ofsFileSize;
|
||||||
|
private long _ofsRelocationTable;
|
||||||
|
private long _ofsFirstBlock;
|
||||||
|
private long _ofsAsstArray;
|
||||||
|
private long _ofsAsstRefArray;
|
||||||
|
private long _ofsFileDictionary;
|
||||||
|
private long _ofsEndOfBlock;
|
||||||
|
private uint Section1Size;
|
||||||
|
private uint beaSize; //Excludes data blocks
|
||||||
|
private long _ofsAsstStart;
|
||||||
|
|
||||||
|
internal FileSaver(BezelEngineArchive bea, Stream stream, bool leaveOpen = true)
|
||||||
|
: base(stream, Encoding.ASCII, leaveOpen)
|
||||||
|
{
|
||||||
|
ByteOrder = ByteOrder.LittleEndian;
|
||||||
|
Archive = bea;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FileSaver(BezelEngineArchive bea, string fileName)
|
||||||
|
: this(bea, new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read), true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Execute()
|
||||||
|
{
|
||||||
|
_savedBlockPositions = new List<long>();
|
||||||
|
_savedBlocks = new Dictionary<object, BlockEntry>();
|
||||||
|
_savedSection1Entries = new List<RelocationEntry>();
|
||||||
|
_savedStrings = new Dictionary<string, StringEntry>();
|
||||||
|
_savedRelocatedSections = new List<RelocationSection>();
|
||||||
|
|
||||||
|
((IFileData)Archive).Save(this);
|
||||||
|
|
||||||
|
//Ref list for version 5 and up
|
||||||
|
long RefOffset = Position; //Offset to blocks
|
||||||
|
|
||||||
|
if (Archive.ReferenceList != null)
|
||||||
|
{
|
||||||
|
SaveRelocateEntryToSection(Position, (uint)Archive.ReferenceList.Count, 1, 0, 1, "Ref String List ");
|
||||||
|
foreach (var asstRef in Archive.ReferenceList)
|
||||||
|
SaveString(asstRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set enough space to add offsets later
|
||||||
|
long OffsetArrayASST = Position; //Offset to blocks
|
||||||
|
|
||||||
|
List<long> _ofsAsstOffsets = new List<long>();
|
||||||
|
foreach (ASST asst in Archive.FileList.Values)
|
||||||
|
{
|
||||||
|
SaveRelocateEntryToSection(Position, 1, 1, 0, 1, "Asst Offset ");
|
||||||
|
_ofsAsstOffsets.Add(Position);
|
||||||
|
Write(0L);
|
||||||
|
}
|
||||||
|
//Now padding. 40 per file
|
||||||
|
Seek(40 * Archive.FileList.Count, SeekOrigin.Current);
|
||||||
|
|
||||||
|
//Now save dictionary.
|
||||||
|
long DictionaryOffset = Position; //Offset to blocks
|
||||||
|
((IFileData)Archive.FileDictionary).Save(this);
|
||||||
|
|
||||||
|
long BlockOffset = Position; //Offset to blocks
|
||||||
|
for (int i = 0; i < Archive.FileList.Count; i++)
|
||||||
|
{
|
||||||
|
long AsstOffset = Position;
|
||||||
|
using (this.TemporarySeek(_ofsAsstOffsets[i], SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
Write(AsstOffset);
|
||||||
|
}
|
||||||
|
string FileName = Archive.FileDictionary.GetKey(i);
|
||||||
|
((IFileData)Archive.FileList[FileName]).Save(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteStringPool();
|
||||||
|
|
||||||
|
|
||||||
|
SetupRelocationTable();
|
||||||
|
WriteRelocationTable();
|
||||||
|
|
||||||
|
WriteBlocks();
|
||||||
|
|
||||||
|
for (int i = 0; i < _savedBlockPositions.Count; i++)
|
||||||
|
{
|
||||||
|
Position = _savedBlockPositions[i];
|
||||||
|
|
||||||
|
if (i == _savedBlockPositions.Count - 1)
|
||||||
|
{
|
||||||
|
Write(0);
|
||||||
|
Write(_ofsEndOfBlock - _savedBlockPositions[i]); //Size of string table to relocation table
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint blockSize = (uint)(_savedBlockPositions[i + 1] - _savedBlockPositions[i]);
|
||||||
|
WriteHeaderBlock(blockSize, blockSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Position = _ofsAsstArray;
|
||||||
|
Write((ulong)OffsetArrayASST);
|
||||||
|
|
||||||
|
if (_ofsAsstStart != 0)
|
||||||
|
{
|
||||||
|
Position = _ofsAsstStart;
|
||||||
|
Write((ulong)BlockOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Position = _ofsFirstBlock;
|
||||||
|
Write((ushort)BlockOffset);
|
||||||
|
|
||||||
|
Position = _ofsFileDictionary;
|
||||||
|
Write((ulong)DictionaryOffset);
|
||||||
|
|
||||||
|
if (_ofsAsstRefArray != 0)
|
||||||
|
{
|
||||||
|
Position = _ofsAsstRefArray;
|
||||||
|
Write((ulong)RefOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Position = _ofsFileSize;
|
||||||
|
Write(beaSize);
|
||||||
|
Flush();
|
||||||
|
}
|
||||||
|
internal void SaveFirstBlock()
|
||||||
|
{
|
||||||
|
_ofsFirstBlock = Position;
|
||||||
|
Write((ushort)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SaveAssetBlock()
|
||||||
|
{
|
||||||
|
_ofsAsstStart = Position;
|
||||||
|
Write((ulong)0);
|
||||||
|
}
|
||||||
|
internal void SaveFileAsstPointer()
|
||||||
|
{
|
||||||
|
_ofsAsstArray = Position;
|
||||||
|
Write(0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal void SaveAssetRefPointer()
|
||||||
|
{
|
||||||
|
_ofsAsstRefArray = Position;
|
||||||
|
Write(0L);
|
||||||
|
}
|
||||||
|
internal void SaveFileDictionaryPointer()
|
||||||
|
{
|
||||||
|
_ofsFileDictionary = Position;
|
||||||
|
Write(0L);
|
||||||
|
}
|
||||||
|
internal void SaveRelocationTablePointer()
|
||||||
|
{
|
||||||
|
_ofsRelocationTable = Position;
|
||||||
|
Write(0);
|
||||||
|
}
|
||||||
|
internal void SaveFileSize()
|
||||||
|
{
|
||||||
|
_ofsFileSize = Position;
|
||||||
|
Write(0);
|
||||||
|
}
|
||||||
|
internal void WriteSize(uint Offset, uint value)
|
||||||
|
{
|
||||||
|
using (this.TemporarySeek(Offset, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal uint SaveSizePtr()
|
||||||
|
{
|
||||||
|
Write(0);
|
||||||
|
return (uint)Position;
|
||||||
|
}
|
||||||
|
internal void SaveBlockHeader()
|
||||||
|
{
|
||||||
|
_savedBlockPositions.Add(Position);
|
||||||
|
Write(0);
|
||||||
|
Write(0L);
|
||||||
|
}
|
||||||
|
internal void SaveBlock(object data, uint alignment, Action callback)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
Write(0L);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_savedBlocks.TryGetValue(data, out BlockEntry entry))
|
||||||
|
{
|
||||||
|
entry.Offsets.Add((uint)Position);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_savedBlocks.Add(data, new BlockEntry((uint)Position, alignment, callback));
|
||||||
|
}
|
||||||
|
Write(UInt64.MaxValue);
|
||||||
|
}
|
||||||
|
internal void SaveString(string str, Encoding encoding = null)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
Write(0L);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_savedStrings.TryGetValue(str, out StringEntry entry))
|
||||||
|
{
|
||||||
|
entry.Offsets.Add((uint)Position);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_savedStrings.Add(str, new StringEntry((uint)Position, encoding));
|
||||||
|
}
|
||||||
|
Write(UInt64.MaxValue);
|
||||||
|
}
|
||||||
|
internal void WriteSignature(string value)
|
||||||
|
{
|
||||||
|
this.Write(Encoding.ASCII.GetBytes(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
//This only should use 1 section to relocate data
|
||||||
|
internal void SaveRelocateEntryToSection(long pos, uint OffsetCount, uint StructCount, uint PaddingCount, int SectionNumber, string Hint)
|
||||||
|
{
|
||||||
|
if (SectionNumber == 1)
|
||||||
|
_savedSection1Entries.Add(new RelocationEntry((uint)pos, OffsetCount, StructCount, PaddingCount, Hint));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void WriteHeaderBlock(uint Size, long Offset)
|
||||||
|
{
|
||||||
|
Write(Size);
|
||||||
|
Write(Offset);
|
||||||
|
}
|
||||||
|
private void SetupRelocationTable()
|
||||||
|
{
|
||||||
|
this.Align(Archive.RawAlignment);
|
||||||
|
RelocationSection FileMainSect;
|
||||||
|
|
||||||
|
long RelocationTableOffset = Position;
|
||||||
|
|
||||||
|
int EntryIndex = 0;
|
||||||
|
uint EntryPos = 0;
|
||||||
|
|
||||||
|
foreach (RelocationEntry entry in _savedSection1Entries)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Pos = " + entry.Position + " " + entry.StructCount + " " + entry.OffsetCount + " " + entry.PadingCount + " " + entry.Hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
_savedSection1Entries = _savedSection1Entries.OrderBy(o => o.Position).ToList();
|
||||||
|
FileMainSect = new RelocationSection(EntryPos, EntryIndex, Section1Size, _savedSection1Entries);
|
||||||
|
|
||||||
|
_savedRelocatedSections.Add(FileMainSect);
|
||||||
|
|
||||||
|
}
|
||||||
|
private void WriteRelocationTable()
|
||||||
|
{
|
||||||
|
uint relocationTableOffset = (uint)Position;
|
||||||
|
WriteSignature("_RLT");
|
||||||
|
_ofsEndOfBlock = (uint)Position;
|
||||||
|
Write(relocationTableOffset);
|
||||||
|
Write(_savedRelocatedSections.Count);
|
||||||
|
Write(0); //padding
|
||||||
|
|
||||||
|
foreach (RelocationSection section in _savedRelocatedSections)
|
||||||
|
{
|
||||||
|
Write(0L); //padding
|
||||||
|
Write(section.Position);
|
||||||
|
Write(section.Size);
|
||||||
|
Write(section.EntryIndex);
|
||||||
|
Write(section.Entries.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (RelocationSection section in _savedRelocatedSections)
|
||||||
|
{
|
||||||
|
foreach (RelocationEntry entry in section.Entries)
|
||||||
|
{
|
||||||
|
Write(entry.Position);
|
||||||
|
Write((ushort)entry.StructCount);
|
||||||
|
Write((byte)entry.OffsetCount);
|
||||||
|
Write((byte)entry.PadingCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beaSize = (uint)Position;
|
||||||
|
|
||||||
|
using (this.TemporarySeek(_ofsRelocationTable, SeekOrigin.Begin))
|
||||||
|
{
|
||||||
|
Write(relocationTableOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void WriteBlocks()
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<object, BlockEntry> entry in _savedBlocks)
|
||||||
|
{
|
||||||
|
// Align and satisfy offsets.
|
||||||
|
Console.WriteLine(entry.Value.Alignment);
|
||||||
|
Console.WriteLine(Position);
|
||||||
|
if (entry.Value.Alignment != 0) this.Align((int)entry.Value.Alignment);
|
||||||
|
Console.WriteLine(Position);
|
||||||
|
|
||||||
|
using (this.TemporarySeek())
|
||||||
|
{
|
||||||
|
SatisfyOffsets(entry.Value.Offsets, (uint)Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the data.
|
||||||
|
entry.Value.Callback.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void WriteStringPool()
|
||||||
|
{
|
||||||
|
WriteSignature("_STR");
|
||||||
|
SaveBlockHeader();
|
||||||
|
Write(_savedStrings.Count - 1);
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, StringEntry> entry in _savedStrings)
|
||||||
|
{
|
||||||
|
using (this.TemporarySeek())
|
||||||
|
{
|
||||||
|
SatisfyOffsets(entry.Value.Offsets, (uint)Position);
|
||||||
|
}
|
||||||
|
// Write the name.
|
||||||
|
Write(entry.Key, BinaryStringFormat.WordLengthPrefix, entry.Value.Encoding ?? Encoding);
|
||||||
|
Align(2);
|
||||||
|
}
|
||||||
|
Section1Size = (uint)Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SatisfyOffsets(IEnumerable<uint> offsets, uint target)
|
||||||
|
{
|
||||||
|
foreach (uint offset in offsets)
|
||||||
|
{
|
||||||
|
Position = offset;
|
||||||
|
Write((long)(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetItemEntry(object data, ItemEntryType type, out ItemEntry entry)
|
||||||
|
{
|
||||||
|
foreach (ItemEntry savedItem in _savedItems)
|
||||||
|
{
|
||||||
|
if (savedItem.Data.Equals(data) && savedItem.Type == type)
|
||||||
|
{
|
||||||
|
entry = savedItem;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StringEntry
|
||||||
|
{
|
||||||
|
internal List<uint> Offsets;
|
||||||
|
internal Encoding Encoding;
|
||||||
|
|
||||||
|
internal StringEntry(uint offset, Encoding encoding = null)
|
||||||
|
{
|
||||||
|
Offsets = new List<uint>(new uint[] { offset });
|
||||||
|
Encoding = encoding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private class BlockEntry
|
||||||
|
{
|
||||||
|
internal List<uint> Offsets;
|
||||||
|
internal uint Alignment;
|
||||||
|
internal Action Callback;
|
||||||
|
|
||||||
|
internal BlockEntry(uint offset, uint alignment, Action callback)
|
||||||
|
{
|
||||||
|
Offsets = new List<uint> { offset };
|
||||||
|
Alignment = alignment;
|
||||||
|
Callback = callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private class RelocationSection
|
||||||
|
{
|
||||||
|
internal List<RelocationEntry> Entries;
|
||||||
|
internal int EntryIndex;
|
||||||
|
internal uint Size;
|
||||||
|
internal uint Position;
|
||||||
|
|
||||||
|
internal RelocationSection(uint position, int entryIndex, uint size, List<RelocationEntry> entries)
|
||||||
|
{
|
||||||
|
Position = position;
|
||||||
|
EntryIndex = entryIndex;
|
||||||
|
Size = size;
|
||||||
|
Entries = entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private enum ItemEntryType
|
||||||
|
{
|
||||||
|
List, Dict, FileData, Custom
|
||||||
|
}
|
||||||
|
private class ItemEntry
|
||||||
|
{
|
||||||
|
internal object Data;
|
||||||
|
internal ItemEntryType Type;
|
||||||
|
internal List<uint> Offsets;
|
||||||
|
internal uint? Target;
|
||||||
|
internal Action Callback;
|
||||||
|
internal int Index;
|
||||||
|
|
||||||
|
internal ItemEntry(object data, ItemEntryType type, uint? offset = null, uint? target = null,
|
||||||
|
Action callback = null, int index = -1)
|
||||||
|
{
|
||||||
|
Data = data;
|
||||||
|
Type = type;
|
||||||
|
Offsets = new List<uint>();
|
||||||
|
if (offset.HasValue) // Might be null for enumerable entries to resolve references to them later.
|
||||||
|
{
|
||||||
|
Offsets.Add(offset.Value);
|
||||||
|
}
|
||||||
|
Callback = callback;
|
||||||
|
Target = target;
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private class RelocationEntry
|
||||||
|
{
|
||||||
|
internal uint Position;
|
||||||
|
internal uint PadingCount;
|
||||||
|
internal uint StructCount;
|
||||||
|
internal uint OffsetCount;
|
||||||
|
internal string Hint;
|
||||||
|
|
||||||
|
internal RelocationEntry(uint position, uint offsetCount, uint structCount, uint padingCount, string hint)
|
||||||
|
{
|
||||||
|
Position = position;
|
||||||
|
StructCount = structCount;
|
||||||
|
OffsetCount = offsetCount;
|
||||||
|
PadingCount = padingCount;
|
||||||
|
Hint = hint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
File_Format_Library/FileFormats/Archives/BEA/IO/IResData.cs
Normal file
22
File_Format_Library/FileFormats/Archives/BEA/IO/IResData.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
namespace BezelEngineArchive_Lib
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the common interface for <see cref="ResFile"/> data instances.
|
||||||
|
/// </summary>
|
||||||
|
public interface IFileData
|
||||||
|
{
|
||||||
|
// ---- METHODS ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads raw data from the <paramref name="loader"/> data stream into instances.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loader">The <see cref="ResFileLoader"/> to load data with.</param>
|
||||||
|
void Load(FileLoader loader);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves header data of the instance and queues referenced data in the given <paramref name="saver"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="saver">The <see cref="ResFileSaver"/> to save headers and queue data with.</param>
|
||||||
|
void Save(FileSaver saver);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BezelEngineArchive_Lib
|
||||||
|
{
|
||||||
|
public class SubStreamBea : Stream
|
||||||
|
{
|
||||||
|
Stream baseStream;
|
||||||
|
readonly long length;
|
||||||
|
readonly long baseOffset;
|
||||||
|
public SubStreamBea(Stream baseStream, long offset, long length)
|
||||||
|
{
|
||||||
|
if (baseStream == null) throw new ArgumentNullException("baseStream");
|
||||||
|
if (!baseStream.CanRead) throw new ArgumentException("baseStream.CanRead is false");
|
||||||
|
if (!baseStream.CanSeek) throw new ArgumentException("baseStream.CanSeek is false");
|
||||||
|
if (offset < 0) throw new ArgumentOutOfRangeException("offset");
|
||||||
|
if (offset + length > baseStream.Length) throw new ArgumentOutOfRangeException("length");
|
||||||
|
|
||||||
|
this.baseStream = baseStream;
|
||||||
|
this.length = length;
|
||||||
|
baseOffset = offset;
|
||||||
|
}
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
baseStream.Position = baseOffset + offset + Position;
|
||||||
|
int read = baseStream.Read(buffer, offset, (int)Math.Min(count, length - Position));
|
||||||
|
Position += read;
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
public override long Length => length;
|
||||||
|
public override bool CanRead => true;
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
public override bool CanSeek => true;
|
||||||
|
public override long Position { get; set; }
|
||||||
|
public override void Flush() => baseStream.Flush();
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
switch (origin)
|
||||||
|
{
|
||||||
|
case SeekOrigin.Begin: return Position = offset;
|
||||||
|
case SeekOrigin.Current: return Position += offset;
|
||||||
|
case SeekOrigin.End: return Position = length + offset;
|
||||||
|
}
|
||||||
|
throw new ArgumentException("origin is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -773,7 +773,7 @@ namespace Bfres.Structs
|
|||||||
if (AttributeMatcher.ContainsKey(obj.ObjectName))
|
if (AttributeMatcher.ContainsKey(obj.ObjectName))
|
||||||
shape.vertexAttributes = csvsettings.CreateNewAttributes(AttributeMatcher[obj.ObjectName]);
|
shape.vertexAttributes = csvsettings.CreateNewAttributes(AttributeMatcher[obj.ObjectName]);
|
||||||
else
|
else
|
||||||
shape.vertexAttributes = csvsettings.CreateNewAttributes();
|
shape.vertexAttributes = csvsettings.CreateNewAttributes(GetMaterial(shape.MaterialIndex));
|
||||||
|
|
||||||
shape.BoneIndex = 0;
|
shape.BoneIndex = 0;
|
||||||
shape.Text = obj.ObjectName;
|
shape.Text = obj.ObjectName;
|
||||||
@ -1304,12 +1304,6 @@ namespace Bfres.Structs
|
|||||||
|
|
||||||
shape.VertexBufferIndex = shapes.Count;
|
shape.VertexBufferIndex = shapes.Count;
|
||||||
shape.vertices = obj.vertices;
|
shape.vertices = obj.vertices;
|
||||||
|
|
||||||
if (AttributeMatcher.ContainsKey(obj.ObjectName))
|
|
||||||
shape.vertexAttributes = settings.CreateNewAttributes(AttributeMatcher[obj.ObjectName]);
|
|
||||||
else
|
|
||||||
shape.vertexAttributes = settings.CreateNewAttributes();
|
|
||||||
|
|
||||||
shape.BoneIndex = obj.BoneIndex;
|
shape.BoneIndex = obj.BoneIndex;
|
||||||
|
|
||||||
if (obj.MaterialIndex + MatStartIndex < materials.Count && obj.MaterialIndex > 0)
|
if (obj.MaterialIndex + MatStartIndex < materials.Count && obj.MaterialIndex > 0)
|
||||||
@ -1317,6 +1311,11 @@ namespace Bfres.Structs
|
|||||||
else
|
else
|
||||||
shape.MaterialIndex = 0;
|
shape.MaterialIndex = 0;
|
||||||
|
|
||||||
|
if (AttributeMatcher.ContainsKey(obj.ObjectName))
|
||||||
|
shape.vertexAttributes = settings.CreateNewAttributes(AttributeMatcher[obj.ObjectName]);
|
||||||
|
else
|
||||||
|
shape.vertexAttributes = settings.CreateNewAttributes(GetMaterial(shape.MaterialIndex));
|
||||||
|
|
||||||
shape.lodMeshes = obj.lodMeshes;
|
shape.lodMeshes = obj.lodMeshes;
|
||||||
shape.CreateBoneList(obj, this, ForceSkinInfluence, ForceSkinInfluenceMax);
|
shape.CreateBoneList(obj, this, ForceSkinInfluence, ForceSkinInfluenceMax);
|
||||||
shape.CreateIndexList(obj, this, ForceSkinInfluence, ForceSkinInfluenceMax);
|
shape.CreateIndexList(obj, this, ForceSkinInfluence, ForceSkinInfluenceMax);
|
||||||
|
@ -1400,16 +1400,17 @@ namespace Bfres.Structs
|
|||||||
max = CalculateBBMax(vertices);
|
max = CalculateBBMax(vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the largest values in local space to create the largest bounding box
|
var c = (min + max) / 2.0f;
|
||||||
foreach (var bounding in aabb)
|
var e = (max - min) / 2.0f;
|
||||||
|
|
||||||
|
float sphereRadius = (float)(c.Length + e.Length);
|
||||||
|
|
||||||
|
return new BoundingBox()
|
||||||
{
|
{
|
||||||
min.X = Math.Min(min.X, bounding.Min.X);
|
Radius = sphereRadius,
|
||||||
min.Y = Math.Min(min.Y, bounding.Min.Y);
|
Center = new Vector3(c.X, c.Y, c.Z),
|
||||||
min.Z = Math.Min(min.Z, bounding.Min.Z);
|
Extend = new Vector3(e.X, e.Y, e.Z),
|
||||||
max.X = Math.Max(max.X, bounding.Max.X);
|
};
|
||||||
max.Y = Math.Max(max.Y, bounding.Max.Y);
|
|
||||||
max.Z = Math.Max(max.Z, bounding.Max.Z);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1756,6 +1757,22 @@ namespace Bfres.Structs
|
|||||||
vert.Format = att.Format;
|
vert.Format = att.Format;
|
||||||
atrib.Add(vert);
|
atrib.Add(vert);
|
||||||
}
|
}
|
||||||
|
if (att.Name == "_g3d_02_u0_u1")
|
||||||
|
{
|
||||||
|
VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib();
|
||||||
|
vert.Name = att.Name;
|
||||||
|
vert.Data = uv0.ToArray();
|
||||||
|
vert.Format = att.Format;
|
||||||
|
atrib.Add(vert);
|
||||||
|
}
|
||||||
|
if (att.Name == "_g3d_02_u2_u3")
|
||||||
|
{
|
||||||
|
VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib();
|
||||||
|
vert.Name = att.Name;
|
||||||
|
vert.Data = uv2.ToArray();
|
||||||
|
vert.Format = att.Format;
|
||||||
|
atrib.Add(vert);
|
||||||
|
}
|
||||||
if (att.Name == "_b0")
|
if (att.Name == "_b0")
|
||||||
{
|
{
|
||||||
VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib();
|
VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib();
|
||||||
@ -1780,6 +1797,31 @@ namespace Bfres.Structs
|
|||||||
vert.Format = att.Format;
|
vert.Format = att.Format;
|
||||||
atrib.Add(vert);
|
atrib.Add(vert);
|
||||||
}
|
}
|
||||||
|
if (att.Name == "_c1")
|
||||||
|
{
|
||||||
|
VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib();
|
||||||
|
vert.Name = att.Name;
|
||||||
|
vert.Data = colors1.ToArray();
|
||||||
|
vert.Format = att.Format;
|
||||||
|
atrib.Add(vert);
|
||||||
|
}
|
||||||
|
if (att.Name == "_c2")
|
||||||
|
{
|
||||||
|
VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib();
|
||||||
|
vert.Name = att.Name;
|
||||||
|
vert.Data = colors2.ToArray();
|
||||||
|
vert.Format = att.Format;
|
||||||
|
atrib.Add(vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (att.Name == "_c3")
|
||||||
|
{
|
||||||
|
VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib();
|
||||||
|
vert.Name = att.Name;
|
||||||
|
vert.Data = colors3.ToArray();
|
||||||
|
vert.Format = att.Format;
|
||||||
|
atrib.Add(vert);
|
||||||
|
}
|
||||||
|
|
||||||
// Set _w and _i
|
// Set _w and _i
|
||||||
for (int i = 0; i < weights.Count; i++)
|
for (int i = 0; i < weights.Count; i++)
|
||||||
@ -1830,6 +1872,9 @@ namespace Bfres.Structs
|
|||||||
internal List<List<Syroot.Maths.Vector4F>> weights = new List<List<Syroot.Maths.Vector4F>>();
|
internal List<List<Syroot.Maths.Vector4F>> weights = new List<List<Syroot.Maths.Vector4F>>();
|
||||||
internal List<List<Syroot.Maths.Vector4F>> boneInd = new List<List<Syroot.Maths.Vector4F>>();
|
internal List<List<Syroot.Maths.Vector4F>> boneInd = new List<List<Syroot.Maths.Vector4F>>();
|
||||||
internal List<Syroot.Maths.Vector4F> colors = new List<Syroot.Maths.Vector4F>();
|
internal List<Syroot.Maths.Vector4F> colors = new List<Syroot.Maths.Vector4F>();
|
||||||
|
internal List<Syroot.Maths.Vector4F> colors1 = new List<Syroot.Maths.Vector4F>();
|
||||||
|
internal List<Syroot.Maths.Vector4F> colors2 = new List<Syroot.Maths.Vector4F>();
|
||||||
|
internal List<Syroot.Maths.Vector4F> colors3 = new List<Syroot.Maths.Vector4F>();
|
||||||
|
|
||||||
public string GetBoneNameFromIndex(FMDL mdl, int index)
|
public string GetBoneNameFromIndex(FMDL mdl, int index)
|
||||||
{
|
{
|
||||||
@ -2059,6 +2104,9 @@ namespace Bfres.Structs
|
|||||||
colors.Clear();
|
colors.Clear();
|
||||||
weights.Clear();
|
weights.Clear();
|
||||||
boneInd.Clear();
|
boneInd.Clear();
|
||||||
|
colors1.Clear();
|
||||||
|
colors2.Clear();
|
||||||
|
colors3.Clear();
|
||||||
|
|
||||||
// Create arrays to be able to fit the needed skin count
|
// Create arrays to be able to fit the needed skin count
|
||||||
int listCount = (int)Math.Ceiling(VertexSkinCount / 4.0);
|
int listCount = (int)Math.Ceiling(VertexSkinCount / 4.0);
|
||||||
@ -2086,12 +2134,15 @@ namespace Bfres.Structs
|
|||||||
|
|
||||||
verts.Add(new Syroot.Maths.Vector4F(vtx.pos.X, vtx.pos.Y, vtx.pos.Z, 1.0f));
|
verts.Add(new Syroot.Maths.Vector4F(vtx.pos.X, vtx.pos.Y, vtx.pos.Z, 1.0f));
|
||||||
norms.Add(new Syroot.Maths.Vector4F(vtx.nrm.X, vtx.nrm.Y, vtx.nrm.Z, 0));
|
norms.Add(new Syroot.Maths.Vector4F(vtx.nrm.X, vtx.nrm.Y, vtx.nrm.Z, 0));
|
||||||
uv0.Add(new Syroot.Maths.Vector4F(vtx.uv0.X, vtx.uv0.Y, 0, 0));
|
uv0.Add(new Syroot.Maths.Vector4F(vtx.uv0.X, vtx.uv0.Y, vtx.uv1.X, vtx.uv1.Y));
|
||||||
uv1.Add(new Syroot.Maths.Vector4F(vtx.uv1.X, vtx.uv1.Y, 0, 0));
|
uv1.Add(new Syroot.Maths.Vector4F(vtx.uv1.X, vtx.uv1.Y, 0, 0));
|
||||||
uv2.Add(new Syroot.Maths.Vector4F(vtx.uv2.X, vtx.uv2.Y, 0, 0));
|
uv2.Add(new Syroot.Maths.Vector4F(vtx.uv2.X, vtx.uv2.Y, vtx.uv3.X, vtx.uv3.Y));
|
||||||
tans.Add(new Syroot.Maths.Vector4F(vtx.tan.X, vtx.tan.Y, vtx.tan.Z, vtx.tan.W));
|
tans.Add(new Syroot.Maths.Vector4F(vtx.tan.X, vtx.tan.Y, vtx.tan.Z, vtx.tan.W));
|
||||||
bitans.Add(new Syroot.Maths.Vector4F(vtx.bitan.X, vtx.bitan.Y, vtx.bitan.Z, vtx.bitan.W));
|
bitans.Add(new Syroot.Maths.Vector4F(vtx.bitan.X, vtx.bitan.Y, vtx.bitan.Z, vtx.bitan.W));
|
||||||
colors.Add(new Syroot.Maths.Vector4F(vtx.col.X, vtx.col.Y, vtx.col.Z, vtx.col.W));
|
colors.Add(new Syroot.Maths.Vector4F(vtx.col.X, vtx.col.Y, vtx.col.Z, vtx.col.W));
|
||||||
|
colors1.Add(new Syroot.Maths.Vector4F(vtx.col2.X, vtx.col2.Y, vtx.col2.Z, vtx.col2.W));
|
||||||
|
colors2.Add(new Syroot.Maths.Vector4F(vtx.col3.X, vtx.col3.Y, vtx.col3.Z, vtx.col3.W));
|
||||||
|
colors3.Add(new Syroot.Maths.Vector4F(vtx.col4.X, vtx.col4.Y, vtx.col4.Z, vtx.col4.W));
|
||||||
|
|
||||||
// Init arrays based on skincount
|
// Init arrays based on skincount
|
||||||
float[] weightsA = new float[TargetVertexSkinCount];
|
float[] weightsA = new float[TargetVertexSkinCount];
|
||||||
|
@ -263,6 +263,9 @@ namespace FirstPlugin
|
|||||||
Syroot.Maths.Vector4F[] vec4uv1 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4uv1 = new Syroot.Maths.Vector4F[0];
|
||||||
Syroot.Maths.Vector4F[] vec4uv2 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4uv2 = new Syroot.Maths.Vector4F[0];
|
||||||
Syroot.Maths.Vector4F[] vec4c0 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4c0 = new Syroot.Maths.Vector4F[0];
|
||||||
|
Syroot.Maths.Vector4F[] vec4c1 = new Syroot.Maths.Vector4F[0];
|
||||||
|
Syroot.Maths.Vector4F[] vec4c2 = new Syroot.Maths.Vector4F[0];
|
||||||
|
Syroot.Maths.Vector4F[] vec4c3 = new Syroot.Maths.Vector4F[0];
|
||||||
Syroot.Maths.Vector4F[] vec4t0 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4t0 = new Syroot.Maths.Vector4F[0];
|
||||||
Syroot.Maths.Vector4F[] vec4b0 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4b0 = new Syroot.Maths.Vector4F[0];
|
||||||
Syroot.Maths.Vector4F[] vec4w0 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4w0 = new Syroot.Maths.Vector4F[0];
|
||||||
@ -270,6 +273,9 @@ namespace FirstPlugin
|
|||||||
Syroot.Maths.Vector4F[] vec4i0 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4i0 = new Syroot.Maths.Vector4F[0];
|
||||||
Syroot.Maths.Vector4F[] vec4i1 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4i1 = new Syroot.Maths.Vector4F[0];
|
||||||
|
|
||||||
|
Syroot.Maths.Vector4F[] vec4uv01 = new Syroot.Maths.Vector4F[0];
|
||||||
|
Syroot.Maths.Vector4F[] vec4uv23 = new Syroot.Maths.Vector4F[0];
|
||||||
|
|
||||||
//For shape morphing
|
//For shape morphing
|
||||||
Syroot.Maths.Vector4F[] vec4Positions1 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4Positions1 = new Syroot.Maths.Vector4F[0];
|
||||||
Syroot.Maths.Vector4F[] vec4Positions2 = new Syroot.Maths.Vector4F[0];
|
Syroot.Maths.Vector4F[] vec4Positions2 = new Syroot.Maths.Vector4F[0];
|
||||||
@ -292,6 +298,12 @@ namespace FirstPlugin
|
|||||||
vec4uv2 = AttributeData(att, helper, "_u2");
|
vec4uv2 = AttributeData(att, helper, "_u2");
|
||||||
if (att.Name == "_c0")
|
if (att.Name == "_c0")
|
||||||
vec4c0 = AttributeData(att, helper, "_c0");
|
vec4c0 = AttributeData(att, helper, "_c0");
|
||||||
|
if (att.Name == "_c1")
|
||||||
|
vec4c1 = AttributeData(att, helper, "_c1");
|
||||||
|
if (att.Name == "_c2")
|
||||||
|
vec4c2 = AttributeData(att, helper, "_c2");
|
||||||
|
if (att.Name == "_c3")
|
||||||
|
vec4c3 = AttributeData(att, helper, "_c3");
|
||||||
if (att.Name == "_t0")
|
if (att.Name == "_t0")
|
||||||
vec4t0 = AttributeData(att, helper, "_t0");
|
vec4t0 = AttributeData(att, helper, "_t0");
|
||||||
if (att.Name == "_b0")
|
if (att.Name == "_b0")
|
||||||
@ -300,10 +312,10 @@ namespace FirstPlugin
|
|||||||
vec4w0 = AttributeData(att, helper, "_w0");
|
vec4w0 = AttributeData(att, helper, "_w0");
|
||||||
if (att.Name == "_i0")
|
if (att.Name == "_i0")
|
||||||
vec4i0 = AttributeData(att, helper, "_i0");
|
vec4i0 = AttributeData(att, helper, "_i0");
|
||||||
if (att.Name == "_w1")
|
if (att.Name == "_g3d_02_u0_u1")
|
||||||
vec4w1 = AttributeData(att, helper, "_w1");
|
vec4uv01 = AttributeData(att, helper, "_g3d_02_u0_u1");
|
||||||
if (att.Name == "_i1")
|
if (att.Name == "_g3d_02_u2_u3")
|
||||||
vec4i1 = AttributeData(att, helper, "_i1");
|
vec4uv23 = AttributeData(att, helper, "_g3d_02_u2_u3");
|
||||||
|
|
||||||
if (att.Name == "_p1")
|
if (att.Name == "_p1")
|
||||||
vec4Positions1 = AttributeData(att, helper, "_p1");
|
vec4Positions1 = AttributeData(att, helper, "_p1");
|
||||||
@ -330,6 +342,17 @@ namespace FirstPlugin
|
|||||||
if (vec4uv2.Length > 0)
|
if (vec4uv2.Length > 0)
|
||||||
v.uv2 = new Vector2(vec4uv2[i].X, vec4uv2[i].Y);
|
v.uv2 = new Vector2(vec4uv2[i].X, vec4uv2[i].Y);
|
||||||
|
|
||||||
|
if (vec4uv01.Length > 0)
|
||||||
|
{
|
||||||
|
v.uv0 = new Vector2(vec4uv01[i].X, vec4uv01[i].Y);
|
||||||
|
v.uv1 = new Vector2(vec4uv01[i].Z, vec4uv01[i].W);
|
||||||
|
}
|
||||||
|
if (vec4uv23.Length > 0)
|
||||||
|
{
|
||||||
|
v.uv2 = new Vector2(vec4uv23[i].X, vec4uv23[i].Y);
|
||||||
|
v.uv3 = new Vector2(vec4uv23[i].Z, vec4uv23[i].W);
|
||||||
|
}
|
||||||
|
|
||||||
if (vec4w0.Length > 0)
|
if (vec4w0.Length > 0)
|
||||||
{
|
{
|
||||||
if (fshp.VertexSkinCount > 0)
|
if (fshp.VertexSkinCount > 0)
|
||||||
@ -374,12 +397,19 @@ namespace FirstPlugin
|
|||||||
if (fshp.VertexSkinCount > 7)
|
if (fshp.VertexSkinCount > 7)
|
||||||
v.boneIds.Add((int)vec4i1[i].W);
|
v.boneIds.Add((int)vec4i1[i].W);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vec4t0.Length > 0)
|
if (vec4t0.Length > 0)
|
||||||
v.tan = new Vector4(vec4t0[i].X, vec4t0[i].Y, vec4t0[i].Z, vec4t0[i].W);
|
v.tan = new Vector4(vec4t0[i].X, vec4t0[i].Y, vec4t0[i].Z, vec4t0[i].W);
|
||||||
if (vec4b0.Length > 0)
|
if (vec4b0.Length > 0)
|
||||||
v.bitan = new Vector4(vec4b0[i].X, vec4b0[i].Y, vec4b0[i].Z, vec4b0[i].W);
|
v.bitan = new Vector4(vec4b0[i].X, vec4b0[i].Y, vec4b0[i].Z, vec4b0[i].W);
|
||||||
if (vec4c0.Length > 0)
|
if (vec4c0.Length > 0)
|
||||||
v.col = new Vector4(vec4c0[i].X, vec4c0[i].Y, vec4c0[i].Z, vec4c0[i].W);
|
v.col = new Vector4(vec4c0[i].X, vec4c0[i].Y, vec4c0[i].Z, vec4c0[i].W);
|
||||||
|
if (vec4c1.Length > 0)
|
||||||
|
v.col2 = new Vector4(vec4c1[i].X, vec4c1[i].Y, vec4c1[i].Z, vec4c1[i].W);
|
||||||
|
if (vec4c2.Length > 0)
|
||||||
|
v.col3 = new Vector4(vec4c2[i].X, vec4c2[i].Y, vec4c2[i].Z, vec4c2[i].W);
|
||||||
|
if (vec4c3.Length > 0)
|
||||||
|
v.col4 = new Vector4(vec4c3[i].X, vec4c3[i].Y, vec4c3[i].Z, vec4c3[i].W);
|
||||||
|
|
||||||
if (fshp.VertexSkinCount == 1)
|
if (fshp.VertexSkinCount == 1)
|
||||||
{
|
{
|
||||||
@ -778,7 +808,6 @@ namespace FirstPlugin
|
|||||||
m.HasNormalMap = true;
|
m.HasNormalMap = true;
|
||||||
texture.Type = MatTexture.TextureType.Normal;
|
texture.Type = MatTexture.TextureType.Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (texture.SamplerName == "_e0")
|
else if (texture.SamplerName == "_e0")
|
||||||
{
|
{
|
||||||
m.HasEmissionMap = true;
|
m.HasEmissionMap = true;
|
||||||
@ -1063,7 +1092,6 @@ namespace FirstPlugin
|
|||||||
m.HasEmissionMap = true;
|
m.HasEmissionMap = true;
|
||||||
texture.Type = MatTexture.TextureType.Emission;
|
texture.Type = MatTexture.TextureType.Emission;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (texture.SamplerName == "_s0" || useSampler == "_s0")
|
else if (texture.SamplerName == "_s0" || useSampler == "_s0")
|
||||||
{
|
{
|
||||||
m.HasSpecularMap = true;
|
m.HasSpecularMap = true;
|
||||||
@ -1084,14 +1112,11 @@ namespace FirstPlugin
|
|||||||
m.HasLightMap = true;
|
m.HasLightMap = true;
|
||||||
texture.Type = MatTexture.TextureType.Light;
|
texture.Type = MatTexture.TextureType.Light;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (texture.SamplerName == "bake0")
|
else if (texture.SamplerName == "bake0")
|
||||||
{
|
{
|
||||||
m.HasShadowMap = true;
|
m.HasShadowMap = true;
|
||||||
texture.Type = MatTexture.TextureType.Shadow;
|
texture.Type = MatTexture.TextureType.Shadow;
|
||||||
}
|
} // EOW Frag Samplers
|
||||||
|
|
||||||
// EOW Frag Samplers
|
|
||||||
|
|
||||||
else if (useSampler == "Albedo0")
|
else if (useSampler == "Albedo0")
|
||||||
{
|
{
|
||||||
|
@ -50,10 +50,6 @@
|
|||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\Toolbox\Lib\BcresLibrary.dll</HintPath>
|
<HintPath>..\Toolbox\Lib\BcresLibrary.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="BezelEngineArchive_Lib">
|
|
||||||
<HintPath>..\Toolbox\Lib\BezelEngineArchive_Lib.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="BfshaLibrary, Version=1.2.3.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="BfshaLibrary, Version=1.2.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\Toolbox\Lib\BfshaLibrary.dll</HintPath>
|
<HintPath>..\Toolbox\Lib\BfshaLibrary.dll</HintPath>
|
||||||
@ -229,6 +225,13 @@
|
|||||||
<Compile Include="FileFormats\AAMP\AAMP.cs" />
|
<Compile Include="FileFormats\AAMP\AAMP.cs" />
|
||||||
<Compile Include="FileFormats\Archives\APAK.cs" />
|
<Compile Include="FileFormats\Archives\APAK.cs" />
|
||||||
<Compile Include="FileFormats\Archives\ARC.cs" />
|
<Compile Include="FileFormats\Archives\ARC.cs" />
|
||||||
|
<Compile Include="FileFormats\Archives\BEA\ASST.cs" />
|
||||||
|
<Compile Include="FileFormats\Archives\BEA\BevelEngineArchive.cs" />
|
||||||
|
<Compile Include="FileFormats\Archives\BEA\DIC\ResDict.cs" />
|
||||||
|
<Compile Include="FileFormats\Archives\BEA\IO\BinaryDataReader.cs" />
|
||||||
|
<Compile Include="FileFormats\Archives\BEA\IO\BinaryDataWriter.cs" />
|
||||||
|
<Compile Include="FileFormats\Archives\BEA\IO\IResData.cs" />
|
||||||
|
<Compile Include="FileFormats\Archives\BEA\IO\SubStreamBea.cs" />
|
||||||
<Compile Include="FileFormats\Archives\BNR.cs" />
|
<Compile Include="FileFormats\Archives\BNR.cs" />
|
||||||
<Compile Include="FileFormats\Archives\DARC.cs" />
|
<Compile Include="FileFormats\Archives\DARC.cs" />
|
||||||
<Compile Include="FileFormats\Archives\DAT_Bayonetta.cs" />
|
<Compile Include="FileFormats\Archives\DAT_Bayonetta.cs" />
|
||||||
|
@ -105,6 +105,7 @@
|
|||||||
this.stCheckBox1 = new Toolbox.Library.Forms.STCheckBox();
|
this.stCheckBox1 = new Toolbox.Library.Forms.STCheckBox();
|
||||||
this.chkMapOriginalMaterials = new Toolbox.Library.Forms.STCheckBox();
|
this.chkMapOriginalMaterials = new Toolbox.Library.Forms.STCheckBox();
|
||||||
this.ogSkinCountChkBox = new Toolbox.Library.Forms.STCheckBox();
|
this.ogSkinCountChkBox = new Toolbox.Library.Forms.STCheckBox();
|
||||||
|
this.combineUVs = new Toolbox.Library.Forms.STCheckBox();
|
||||||
this.contentContainer.SuspendLayout();
|
this.contentContainer.SuspendLayout();
|
||||||
this.panel1.SuspendLayout();
|
this.panel1.SuspendLayout();
|
||||||
this.panel2.SuspendLayout();
|
this.panel2.SuspendLayout();
|
||||||
@ -460,6 +461,7 @@
|
|||||||
//
|
//
|
||||||
// panel8
|
// panel8
|
||||||
//
|
//
|
||||||
|
this.panel8.Controls.Add(this.combineUVs);
|
||||||
this.panel8.Controls.Add(this.stLabel4);
|
this.panel8.Controls.Add(this.stLabel4);
|
||||||
this.panel8.Controls.Add(this.lodCountUD);
|
this.panel8.Controls.Add(this.lodCountUD);
|
||||||
this.panel8.Controls.Add(this.chkCreateDummyLODs);
|
this.panel8.Controls.Add(this.chkCreateDummyLODs);
|
||||||
@ -603,7 +605,6 @@
|
|||||||
this.chkBoxRotNegative90Y.Text = "Rotate -90 degrees";
|
this.chkBoxRotNegative90Y.Text = "Rotate -90 degrees";
|
||||||
this.chkBoxRotNegative90Y.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
this.chkBoxRotNegative90Y.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
this.chkBoxRotNegative90Y.UseVisualStyleBackColor = true;
|
this.chkBoxRotNegative90Y.UseVisualStyleBackColor = true;
|
||||||
this.chkBoxRotNegative90Y.CheckedChanged += new System.EventHandler(this.chkBoxSettings_CheckedChanged);
|
|
||||||
//
|
//
|
||||||
// textBoxMaterialPath
|
// textBoxMaterialPath
|
||||||
//
|
//
|
||||||
@ -836,7 +837,7 @@
|
|||||||
this.tabPageAdvanced.Location = new System.Drawing.Point(4, 25);
|
this.tabPageAdvanced.Location = new System.Drawing.Point(4, 25);
|
||||||
this.tabPageAdvanced.Name = "tabPageAdvanced";
|
this.tabPageAdvanced.Name = "tabPageAdvanced";
|
||||||
this.tabPageAdvanced.Padding = new System.Windows.Forms.Padding(3);
|
this.tabPageAdvanced.Padding = new System.Windows.Forms.Padding(3);
|
||||||
this.tabPageAdvanced.Size = new System.Drawing.Size(530, 347);
|
this.tabPageAdvanced.Size = new System.Drawing.Size(192, 71);
|
||||||
this.tabPageAdvanced.TabIndex = 0;
|
this.tabPageAdvanced.TabIndex = 0;
|
||||||
this.tabPageAdvanced.Text = "Advanced Settings";
|
this.tabPageAdvanced.Text = "Advanced Settings";
|
||||||
//
|
//
|
||||||
@ -853,7 +854,7 @@
|
|||||||
this.stPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.stPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.stPanel1.Location = new System.Drawing.Point(3, 3);
|
this.stPanel1.Location = new System.Drawing.Point(3, 3);
|
||||||
this.stPanel1.Name = "stPanel1";
|
this.stPanel1.Name = "stPanel1";
|
||||||
this.stPanel1.Size = new System.Drawing.Size(524, 341);
|
this.stPanel1.Size = new System.Drawing.Size(186, 65);
|
||||||
this.stPanel1.TabIndex = 17;
|
this.stPanel1.TabIndex = 17;
|
||||||
//
|
//
|
||||||
// tabPage1
|
// tabPage1
|
||||||
@ -870,7 +871,7 @@
|
|||||||
this.tabPage1.Location = new System.Drawing.Point(4, 25);
|
this.tabPage1.Location = new System.Drawing.Point(4, 25);
|
||||||
this.tabPage1.Name = "tabPage1";
|
this.tabPage1.Name = "tabPage1";
|
||||||
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
||||||
this.tabPage1.Size = new System.Drawing.Size(530, 347);
|
this.tabPage1.Size = new System.Drawing.Size(192, 71);
|
||||||
this.tabPage1.TabIndex = 2;
|
this.tabPage1.TabIndex = 2;
|
||||||
this.tabPage1.Text = "Inject Mode";
|
this.tabPage1.Text = "Inject Mode";
|
||||||
this.tabPage1.UseVisualStyleBackColor = true;
|
this.tabPage1.UseVisualStyleBackColor = true;
|
||||||
@ -981,6 +982,16 @@
|
|||||||
this.ogSkinCountChkBox.Text = "Keep Original Skin Count (can help crashes)";
|
this.ogSkinCountChkBox.Text = "Keep Original Skin Count (can help crashes)";
|
||||||
this.ogSkinCountChkBox.UseVisualStyleBackColor = true;
|
this.ogSkinCountChkBox.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
|
// combineUVs
|
||||||
|
//
|
||||||
|
this.combineUVs.AutoSize = true;
|
||||||
|
this.combineUVs.Location = new System.Drawing.Point(414, 88);
|
||||||
|
this.combineUVs.Name = "combineUVs";
|
||||||
|
this.combineUVs.Size = new System.Drawing.Size(90, 17);
|
||||||
|
this.combineUVs.TabIndex = 40;
|
||||||
|
this.combineUVs.Text = "Combine UVs";
|
||||||
|
this.combineUVs.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
// BfresModelImportSettings
|
// BfresModelImportSettings
|
||||||
//
|
//
|
||||||
this.ClientSize = new System.Drawing.Size(547, 412);
|
this.ClientSize = new System.Drawing.Size(547, 412);
|
||||||
@ -1097,5 +1108,6 @@
|
|||||||
private Toolbox.Library.Forms.STCheckBox chkCreateDummyLODs;
|
private Toolbox.Library.Forms.STCheckBox chkCreateDummyLODs;
|
||||||
private Toolbox.Library.Forms.STLabel stLabel4;
|
private Toolbox.Library.Forms.STLabel stLabel4;
|
||||||
private Toolbox.Library.Forms.NumericUpDownUint lodCountUD;
|
private Toolbox.Library.Forms.NumericUpDownUint lodCountUD;
|
||||||
|
private Toolbox.Library.Forms.STCheckBox combineUVs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ using Toolbox.Library.Forms;
|
|||||||
using Toolbox.Library.Rendering;
|
using Toolbox.Library.Rendering;
|
||||||
using Bfres.Structs;
|
using Bfres.Structs;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Syroot.NintenTools.NSW.Bfres;
|
||||||
|
using static OpenTK.Graphics.OpenGL.GL;
|
||||||
|
|
||||||
namespace FirstPlugin
|
namespace FirstPlugin
|
||||||
{
|
{
|
||||||
@ -65,6 +67,8 @@ namespace FirstPlugin
|
|||||||
public bool ResetUVParams;
|
public bool ResetUVParams;
|
||||||
public bool ResetColorParams;
|
public bool ResetColorParams;
|
||||||
|
|
||||||
|
public bool CombineUVs => combineUVs.Checked;
|
||||||
|
|
||||||
public bool LimitSkinCount => ogSkinCountChkBox.Checked;
|
public bool LimitSkinCount => ogSkinCountChkBox.Checked;
|
||||||
public bool MapOriginalMaterials
|
public bool MapOriginalMaterials
|
||||||
{
|
{
|
||||||
@ -181,13 +185,18 @@ namespace FirstPlugin
|
|||||||
Attributes[i].Format = (AttribFormat)comboBoxFormatWeights.SelectedItem;
|
Attributes[i].Format = (AttribFormat)comboBoxFormatWeights.SelectedItem;
|
||||||
if (Attributes[i].Name == "_i0")
|
if (Attributes[i].Name == "_i0")
|
||||||
Attributes[i].Format = (AttribFormat)comboBoxFormatIndices.SelectedItem;
|
Attributes[i].Format = (AttribFormat)comboBoxFormatIndices.SelectedItem;
|
||||||
|
|
||||||
|
if (CombineUVs && Attributes[i].Name == "_u0")
|
||||||
|
Attributes[i].Format = AttribFormat.Format_16_16_16_16_Single;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Attributes;
|
return Attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FSHP.VertexAttribute> CreateNewAttributes()
|
public List<FSHP.VertexAttribute> CreateNewAttributes(FMAT material = null)
|
||||||
{
|
{
|
||||||
Dictionary<string, FSHP.VertexAttribute> attribute = new Dictionary<string, FSHP.VertexAttribute>();
|
Dictionary<string, FSHP.VertexAttribute> attribute = new Dictionary<string, FSHP.VertexAttribute>();
|
||||||
|
|
||||||
@ -230,20 +239,35 @@ namespace FirstPlugin
|
|||||||
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
|
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
|
||||||
att.Name = "_u0";
|
att.Name = "_u0";
|
||||||
att.Format = (AttribFormat)comboBoxFormatUvs.SelectedItem;
|
att.Format = (AttribFormat)comboBoxFormatUvs.SelectedItem;
|
||||||
|
|
||||||
|
if (material.shaderassign.attributes.ContainsValue("_g3d_02_u0_u1"))
|
||||||
|
{
|
||||||
|
att.Format = AttribFormat.Format_16_16_16_16_Single;
|
||||||
|
att.Name = "_g3d_02_u0_u1";
|
||||||
|
}
|
||||||
|
|
||||||
attribute.Add(att.Name, att);
|
attribute.Add(att.Name, att);
|
||||||
}
|
}
|
||||||
if (EnableUV1 && EnableUV0)
|
if (EnableUV1 && EnableUV0 && !attribute.ContainsKey("_g3d_02_u0_u1"))
|
||||||
{
|
{
|
||||||
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
|
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
|
||||||
att.Name = "_u1";
|
att.Name = "_u1";
|
||||||
att.Format = (AttribFormat)comboBoxFormatUvs.SelectedItem;
|
att.Format = (AttribFormat)comboBoxFormatUvs.SelectedItem;
|
||||||
attribute.Add(att.Name, att);
|
attribute.Add(att.Name, att);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EnableUV2 && EnableUV0)
|
if (EnableUV2 && EnableUV0)
|
||||||
{
|
{
|
||||||
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
|
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
|
||||||
att.Name = "_u2";
|
att.Name = "_u2";
|
||||||
att.Format = (AttribFormat)comboBoxFormatUvs.SelectedItem;
|
att.Format = (AttribFormat)comboBoxFormatUvs.SelectedItem;
|
||||||
|
|
||||||
|
if (material.shaderassign.attributes.ContainsValue("_g3d_02_u2_u3"))
|
||||||
|
{
|
||||||
|
att.Format = AttribFormat.Format_16_16_16_16_Single;
|
||||||
|
att.Name = "_g3d_02_u2_u3";
|
||||||
|
}
|
||||||
|
|
||||||
attribute.Add(att.Name, att);
|
attribute.Add(att.Name, att);
|
||||||
}
|
}
|
||||||
if (EnableTangents)
|
if (EnableTangents)
|
||||||
@ -275,6 +299,25 @@ namespace FirstPlugin
|
|||||||
attribute.Add(att.Name, att);
|
attribute.Add(att.Name, att);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (material.shaderassign.attributes.ContainsValue("_c0") && !EnableVertexColors)
|
||||||
|
{
|
||||||
|
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
|
||||||
|
att.Name = "_c0";
|
||||||
|
att.Format = (AttribFormat)comboBoxFormatVertexColors.SelectedItem;
|
||||||
|
attribute.Add(att.Name, att);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < 6; i++)
|
||||||
|
{
|
||||||
|
if (material.shaderassign.attributes.ContainsValue($"_c{i}"))
|
||||||
|
{
|
||||||
|
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
|
||||||
|
att.Name = $"_c{i}";
|
||||||
|
att.Format = (AttribFormat)comboBoxFormatVertexColors.SelectedItem;
|
||||||
|
attribute.Add(att.Name, att);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch ((GamePreset)gamePresetCB.SelectedItem)
|
switch ((GamePreset)gamePresetCB.SelectedItem)
|
||||||
{
|
{
|
||||||
//Use single buffer
|
//Use single buffer
|
||||||
@ -492,12 +535,10 @@ namespace FirstPlugin
|
|||||||
|
|
||||||
int assimpIndex = assimpMeshListView.SelectedIndices[0];
|
int assimpIndex = assimpMeshListView.SelectedIndices[0];
|
||||||
|
|
||||||
objectNameTB.BackColor = System.Drawing.Color.DarkRed;
|
if (objectNameTB.Text == originalMeshListView.Items[assimpIndex].Text)
|
||||||
foreach (ListViewItem item in originalMeshListView.Items)
|
|
||||||
{
|
|
||||||
if (objectNameTB.Text == item.Text)
|
|
||||||
objectNameTB.BackColor = System.Drawing.Color.Green;
|
objectNameTB.BackColor = System.Drawing.Color.Green;
|
||||||
}
|
else
|
||||||
|
objectNameTB.BackColor = System.Drawing.Color.DarkRed;
|
||||||
|
|
||||||
NewMeshlist[assimpIndex].ObjectName = objectNameTB.Text;
|
NewMeshlist[assimpIndex].ObjectName = objectNameTB.Text;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,8 @@ namespace Toolbox.Library
|
|||||||
new List<STGenericMaterial>(), new List<STGenericTexture>());
|
new List<STGenericMaterial>(), new List<STGenericTexture>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Export(string FileName, ExportSettings settings, STGenericModel model, List<STGenericTexture> Textures, STSkeleton skeleton = null, List<int> NodeArray = null) {
|
public static void Export(string FileName, ExportSettings settings, STGenericModel model, List<STGenericTexture> Textures, STSkeleton skeleton = null, List<int> NodeArray = null)
|
||||||
|
{
|
||||||
Export(FileName, settings, model.Objects.ToList(), model.Materials.ToList(), Textures, skeleton, NodeArray);
|
Export(FileName, settings, model.Objects.ToList(), model.Materials.ToList(), Textures, skeleton, NodeArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +103,8 @@ namespace Toolbox.Library
|
|||||||
if (!textureNames.Contains(Textures[i].Text))
|
if (!textureNames.Contains(Textures[i].Text))
|
||||||
textureNames.Add(Textures[i].Text);
|
textureNames.Add(Textures[i].Text);
|
||||||
|
|
||||||
if (settings.ExportTextures) {
|
if (settings.ExportTextures)
|
||||||
|
{
|
||||||
|
|
||||||
progressBar.Task = $"Exporting Texture {Textures[i].Text}";
|
progressBar.Task = $"Exporting Texture {Textures[i].Text}";
|
||||||
progressBar.Value = ((i * 100) / Textures.Count);
|
progressBar.Value = ((i * 100) / Textures.Count);
|
||||||
@ -137,7 +139,8 @@ namespace Toolbox.Library
|
|||||||
GC.Collect();
|
GC.Collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex)
|
||||||
|
{
|
||||||
failedTextureExport.Add(Textures[i].Text);
|
failedTextureExport.Add(Textures[i].Text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,7 +213,8 @@ namespace Toolbox.Library
|
|||||||
else
|
else
|
||||||
writer.WriteLibraryImages();
|
writer.WriteLibraryImages();
|
||||||
|
|
||||||
if (skeleton != null) {
|
if (skeleton != null)
|
||||||
|
{
|
||||||
//Search for bones with rigging first
|
//Search for bones with rigging first
|
||||||
List<string> riggedBones = new List<string>();
|
List<string> riggedBones = new List<string>();
|
||||||
if (settings.OnlyExportRiggedBones)
|
if (settings.OnlyExportRiggedBones)
|
||||||
@ -223,7 +227,8 @@ namespace Toolbox.Library
|
|||||||
for (int j = 0; j < vertex.boneIds.Count; j++)
|
for (int j = 0; j < vertex.boneIds.Count; j++)
|
||||||
{
|
{
|
||||||
int id = -1;
|
int id = -1;
|
||||||
if (NodeArray != null && NodeArray.Count > vertex.boneIds[j]) {
|
if (NodeArray != null && NodeArray.Count > vertex.boneIds[j])
|
||||||
|
{
|
||||||
id = NodeArray[vertex.boneIds[j]];
|
id = NodeArray[vertex.boneIds[j]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -315,15 +320,18 @@ namespace Toolbox.Library
|
|||||||
var vertexB = mesh.vertices[faces[v + 1]];
|
var vertexB = mesh.vertices[faces[v + 1]];
|
||||||
var vertexC = mesh.vertices[faces[v + 2]];
|
var vertexC = mesh.vertices[faces[v + 2]];
|
||||||
|
|
||||||
if (!transformedVertices.Contains(vertexA)) {
|
if (!transformedVertices.Contains(vertexA))
|
||||||
|
{
|
||||||
vertexA.uv0 = (vertexA.uv0 * transform.Scale) + transform.Translate;
|
vertexA.uv0 = (vertexA.uv0 * transform.Scale) + transform.Translate;
|
||||||
transformedVertices.Add(vertexA);
|
transformedVertices.Add(vertexA);
|
||||||
}
|
}
|
||||||
if (!transformedVertices.Contains(vertexB)) {
|
if (!transformedVertices.Contains(vertexB))
|
||||||
|
{
|
||||||
vertexB.uv0 = (vertexB.uv0 * transform.Scale) + transform.Translate;
|
vertexB.uv0 = (vertexB.uv0 * transform.Scale) + transform.Translate;
|
||||||
transformedVertices.Add(vertexB);
|
transformedVertices.Add(vertexB);
|
||||||
}
|
}
|
||||||
if (!transformedVertices.Contains(vertexC)) {
|
if (!transformedVertices.Contains(vertexC))
|
||||||
|
{
|
||||||
vertexC.uv0 = (vertexC.uv0 * transform.Scale) + transform.Translate;
|
vertexC.uv0 = (vertexC.uv0 * transform.Scale) + transform.Translate;
|
||||||
transformedVertices.Add(vertexC);
|
transformedVertices.Add(vertexC);
|
||||||
}
|
}
|
||||||
@ -340,15 +348,20 @@ namespace Toolbox.Library
|
|||||||
List<float> UV3 = new List<float>();
|
List<float> UV3 = new List<float>();
|
||||||
List<float> Color = new List<float>();
|
List<float> Color = new List<float>();
|
||||||
List<float> Color2 = new List<float>();
|
List<float> Color2 = new List<float>();
|
||||||
|
List<float> Color3 = new List<float>();
|
||||||
|
List<float> Color4 = new List<float>();
|
||||||
List<int[]> BoneIndices = new List<int[]>();
|
List<int[]> BoneIndices = new List<int[]>();
|
||||||
List<float[]> BoneWeights = new List<float[]>();
|
List<float[]> BoneWeights = new List<float[]>();
|
||||||
|
|
||||||
bool HasNormals = false;
|
bool HasNormals = false;
|
||||||
bool HasColors = false;
|
bool HasColors = false;
|
||||||
bool HasColors2 = false;
|
bool HasColors2 = false;
|
||||||
|
bool HasColors3 = false;
|
||||||
|
bool HasColors4 = false;
|
||||||
bool HasUV0 = false;
|
bool HasUV0 = false;
|
||||||
bool HasUV1 = false;
|
bool HasUV1 = false;
|
||||||
bool HasUV2 = false;
|
bool HasUV2 = false;
|
||||||
|
bool HasUV3 = false;
|
||||||
bool HasBoneIds = false;
|
bool HasBoneIds = false;
|
||||||
|
|
||||||
foreach (var vertex in mesh.vertices)
|
foreach (var vertex in mesh.vertices)
|
||||||
@ -356,9 +369,13 @@ namespace Toolbox.Library
|
|||||||
if (vertex.nrm != Vector3.Zero) HasNormals = true;
|
if (vertex.nrm != Vector3.Zero) HasNormals = true;
|
||||||
if (vertex.col != Vector4.One && settings.UseVertexColors) HasColors = true;
|
if (vertex.col != Vector4.One && settings.UseVertexColors) HasColors = true;
|
||||||
if (vertex.col2 != Vector4.One && settings.UseVertexColors) HasColors2 = true;
|
if (vertex.col2 != Vector4.One && settings.UseVertexColors) HasColors2 = true;
|
||||||
|
if (vertex.col3 != Vector4.One && settings.UseVertexColors) HasColors3 = true;
|
||||||
|
if (vertex.col4 != Vector4.One && settings.UseVertexColors) HasColors4 = true;
|
||||||
|
|
||||||
if (vertex.uv0 != Vector2.Zero) HasUV0 = true;
|
if (vertex.uv0 != Vector2.Zero) HasUV0 = true;
|
||||||
if (vertex.uv1 != Vector2.Zero) HasUV1 = true;
|
if (vertex.uv1 != Vector2.Zero) HasUV1 = true;
|
||||||
if (vertex.uv2 != Vector2.Zero) HasUV2 = true;
|
if (vertex.uv2 != Vector2.Zero) HasUV2 = true;
|
||||||
|
if (vertex.uv3 != Vector2.Zero) HasUV3 = true;
|
||||||
if (vertex.boneIds.Count > 0) HasBoneIds = true;
|
if (vertex.boneIds.Count > 0) HasBoneIds = true;
|
||||||
|
|
||||||
Position.Add(vertex.pos.X); Position.Add(vertex.pos.Y); Position.Add(vertex.pos.Z);
|
Position.Add(vertex.pos.X); Position.Add(vertex.pos.Y); Position.Add(vertex.pos.Z);
|
||||||
@ -369,16 +386,20 @@ namespace Toolbox.Library
|
|||||||
UV0.Add(vertex.uv0.X); UV0.Add(1 - vertex.uv0.Y);
|
UV0.Add(vertex.uv0.X); UV0.Add(1 - vertex.uv0.Y);
|
||||||
UV1.Add(vertex.uv1.X); UV1.Add(1 - vertex.uv1.Y);
|
UV1.Add(vertex.uv1.X); UV1.Add(1 - vertex.uv1.Y);
|
||||||
UV2.Add(vertex.uv2.X); UV2.Add(1 - vertex.uv2.Y);
|
UV2.Add(vertex.uv2.X); UV2.Add(1 - vertex.uv2.Y);
|
||||||
|
UV3.Add(vertex.uv3.X); UV3.Add(1 - vertex.uv3.Y);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UV0.Add(vertex.uv0.X); UV0.Add(vertex.uv0.Y);
|
UV0.Add(vertex.uv0.X); UV0.Add(vertex.uv0.Y);
|
||||||
UV1.Add(vertex.uv1.X); UV1.Add(vertex.uv1.Y);
|
UV1.Add(vertex.uv1.X); UV1.Add(vertex.uv1.Y);
|
||||||
UV2.Add(vertex.uv2.X); UV2.Add(vertex.uv2.Y);
|
UV2.Add(vertex.uv2.X); UV2.Add(vertex.uv2.Y);
|
||||||
|
UV3.Add(vertex.uv3.X); UV3.Add(vertex.uv3.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color.AddRange(new float[] { vertex.col.X, vertex.col.Y, vertex.col.Z, vertex.col.W });
|
Color.AddRange(new float[] { vertex.col.X, vertex.col.Y, vertex.col.Z, vertex.col.W });
|
||||||
Color2.AddRange(new float[] { vertex.col2.X, vertex.col2.Y, vertex.col2.Z, vertex.col2.W });
|
Color2.AddRange(new float[] { vertex.col2.X, vertex.col2.Y, vertex.col2.Z, vertex.col2.W });
|
||||||
|
Color3.AddRange(new float[] { vertex.col3.X, vertex.col3.Y, vertex.col3.Z, vertex.col3.W });
|
||||||
|
Color4.AddRange(new float[] { vertex.col4.X, vertex.col4.Y, vertex.col4.Z, vertex.col4.W });
|
||||||
|
|
||||||
List<int> bIndices = new List<int>();
|
List<int> bIndices = new List<int>();
|
||||||
List<float> bWeights = new List<float>();
|
List<float> bWeights = new List<float>();
|
||||||
@ -484,9 +505,12 @@ namespace Toolbox.Library
|
|||||||
|
|
||||||
if (HasColors)
|
if (HasColors)
|
||||||
writer.WriteGeometrySource(mesh.Text, SemanticType.COLOR, Color.ToArray(), triangleLists.ToArray(), 0);
|
writer.WriteGeometrySource(mesh.Text, SemanticType.COLOR, Color.ToArray(), triangleLists.ToArray(), 0);
|
||||||
|
|
||||||
if (HasColors2)
|
if (HasColors2)
|
||||||
writer.WriteGeometrySource(mesh.Text, SemanticType.COLOR, Color2.ToArray(), triangleLists.ToArray(), 1);
|
writer.WriteGeometrySource(mesh.Text, SemanticType.COLOR, Color2.ToArray(), triangleLists.ToArray(), 1);
|
||||||
|
if (HasColors3)
|
||||||
|
writer.WriteGeometrySource(mesh.Text, SemanticType.COLOR, Color3.ToArray(), triangleLists.ToArray(), 2);
|
||||||
|
if (HasColors4)
|
||||||
|
writer.WriteGeometrySource(mesh.Text, SemanticType.COLOR, Color4.ToArray(), triangleLists.ToArray(), 3);
|
||||||
|
|
||||||
if (HasUV0)
|
if (HasUV0)
|
||||||
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV0.ToArray(), triangleLists.ToArray(), 0);
|
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV0.ToArray(), triangleLists.ToArray(), 0);
|
||||||
@ -497,6 +521,9 @@ namespace Toolbox.Library
|
|||||||
if (HasUV2)
|
if (HasUV2)
|
||||||
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV2.ToArray(), triangleLists.ToArray(), 2);
|
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV2.ToArray(), triangleLists.ToArray(), 2);
|
||||||
|
|
||||||
|
if (HasUV3)
|
||||||
|
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV3.ToArray(), triangleLists.ToArray(), 3);
|
||||||
|
|
||||||
if (HasBoneIds)
|
if (HasBoneIds)
|
||||||
writer.AttachGeometryController(BoneIndices, BoneWeights);
|
writer.AttachGeometryController(BoneIndices, BoneWeights);
|
||||||
|
|
||||||
@ -508,7 +535,7 @@ namespace Toolbox.Library
|
|||||||
progressBar?.Close();
|
progressBar?.Close();
|
||||||
|
|
||||||
if (!settings.SuppressConfirmDialog)
|
if (!settings.SuppressConfirmDialog)
|
||||||
System.Windows.Forms.MessageBox.Show($"Exported {FileName} successfully!");
|
System.Windows.Forms.MessageBox.Show($"Exported {FileName} Successfully!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ namespace Toolbox.Library.Rendering
|
|||||||
public Vector3 nrm = new Vector3(0);
|
public Vector3 nrm = new Vector3(0);
|
||||||
public Vector4 col = new Vector4(1);
|
public Vector4 col = new Vector4(1);
|
||||||
public Vector4 col2 = new Vector4(1);
|
public Vector4 col2 = new Vector4(1);
|
||||||
|
public Vector4 col3 = new Vector4(1);
|
||||||
|
public Vector4 col4 = new Vector4(1);
|
||||||
|
|
||||||
public Vector2 uv0 = new Vector2(0);
|
public Vector2 uv0 = new Vector2(0);
|
||||||
public Vector2 uv1 = new Vector2(0);
|
public Vector2 uv1 = new Vector2(0);
|
||||||
|
Loading…
Reference in New Issue
Block a user