Support latest BEA, multiple UVs for bfres, bounding box/shadow fixes, and more

This commit is contained in:
KillzXGaming 2024-10-17 20:16:32 -04:00
parent 44a88b4191
commit bdec926c2d
16 changed files with 1634 additions and 65 deletions

View file

@ -179,6 +179,12 @@ namespace FirstPlugin
asset.FileName = node.FileName;
asset.FileData = node.CompressedData;
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.FileDictionary.Add(asset.FileName);
}
@ -204,6 +210,9 @@ namespace FirstPlugin
public class FileEntry : ArchiveFileInfo
{
BEA ArchiveFile;
public FileEntry(BEA bea)
{
ArchiveFile = bea;
@ -233,6 +242,11 @@ namespace FirstPlugin
public ushort unk1;
public ushort unk2;
public bool IsCompressed;
public ulong FileID1;
public ulong FileID2;
public uint FileHash;
public string FileType = "";
public uint Unknown3;
public override byte[] FileData
{
@ -248,6 +262,8 @@ namespace FirstPlugin
unk1 = 2;
unk2 = 12;
FileData = data;
FileID1 = (ulong)new Random().Next(0, int.MaxValue);
FileID2 = (ulong)new Random().Next(0, int.MaxValue);
}
public override Dictionary<string, string> ExtensionImageKeyLookup
@ -309,6 +325,12 @@ namespace FirstPlugin
fileEntry.unk2 = asset.unk2;
fileEntry.IsCompressed = asset.IsCompressed;
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;
}
}

View 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);
}
}
}

View file

@ -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);
}
}
}
}

View 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;
}
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}
}
}

View 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);
}
}

View file

@ -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();
}
}
}

View file

@ -773,7 +773,7 @@ namespace Bfres.Structs
if (AttributeMatcher.ContainsKey(obj.ObjectName))
shape.vertexAttributes = csvsettings.CreateNewAttributes(AttributeMatcher[obj.ObjectName]);
else
shape.vertexAttributes = csvsettings.CreateNewAttributes();
shape.vertexAttributes = csvsettings.CreateNewAttributes(GetMaterial(shape.MaterialIndex));
shape.BoneIndex = 0;
shape.Text = obj.ObjectName;
@ -1304,12 +1304,6 @@ namespace Bfres.Structs
shape.VertexBufferIndex = shapes.Count;
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;
if (obj.MaterialIndex + MatStartIndex < materials.Count && obj.MaterialIndex > 0)
@ -1317,6 +1311,11 @@ namespace Bfres.Structs
else
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.CreateBoneList(obj, this, ForceSkinInfluence, ForceSkinInfluenceMax);
shape.CreateIndexList(obj, this, ForceSkinInfluence, ForceSkinInfluenceMax);

View file

@ -1400,16 +1400,17 @@ namespace Bfres.Structs
max = CalculateBBMax(vertices);
}
//Get the largest values in local space to create the largest bounding box
foreach (var bounding in aabb)
var c = (min + max) / 2.0f;
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);
min.Y = Math.Min(min.Y, bounding.Min.Y);
min.Z = Math.Min(min.Z, bounding.Min.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);
}
Radius = sphereRadius,
Center = new Vector3(c.X, c.Y, c.Z),
Extend = new Vector3(e.X, e.Y, e.Z),
};
}
else
{
@ -1756,6 +1757,22 @@ namespace Bfres.Structs
vert.Format = att.Format;
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")
{
VertexBufferHelperAttrib vert = new VertexBufferHelperAttrib();
@ -1780,6 +1797,31 @@ namespace Bfres.Structs
vert.Format = att.Format;
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
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>> boneInd = new List<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)
{
@ -2059,6 +2104,9 @@ namespace Bfres.Structs
colors.Clear();
weights.Clear();
boneInd.Clear();
colors1.Clear();
colors2.Clear();
colors3.Clear();
// Create arrays to be able to fit the needed skin count
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));
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));
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));
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));
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
float[] weightsA = new float[TargetVertexSkinCount];

View file

@ -263,6 +263,9 @@ namespace FirstPlugin
Syroot.Maths.Vector4F[] vec4uv1 = 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[] 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[] vec4b0 = 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[] 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
Syroot.Maths.Vector4F[] vec4Positions1 = 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");
if (att.Name == "_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")
vec4t0 = AttributeData(att, helper, "_t0");
if (att.Name == "_b0")
@ -300,10 +312,10 @@ namespace FirstPlugin
vec4w0 = AttributeData(att, helper, "_w0");
if (att.Name == "_i0")
vec4i0 = AttributeData(att, helper, "_i0");
if (att.Name == "_w1")
vec4w1 = AttributeData(att, helper, "_w1");
if (att.Name == "_i1")
vec4i1 = AttributeData(att, helper, "_i1");
if (att.Name == "_g3d_02_u0_u1")
vec4uv01 = AttributeData(att, helper, "_g3d_02_u0_u1");
if (att.Name == "_g3d_02_u2_u3")
vec4uv23 = AttributeData(att, helper, "_g3d_02_u2_u3");
if (att.Name == "_p1")
vec4Positions1 = AttributeData(att, helper, "_p1");
@ -330,6 +342,17 @@ namespace FirstPlugin
if (vec4uv2.Length > 0)
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 (fshp.VertexSkinCount > 0)
@ -374,12 +397,19 @@ namespace FirstPlugin
if (fshp.VertexSkinCount > 7)
v.boneIds.Add((int)vec4i1[i].W);
}
if (vec4t0.Length > 0)
v.tan = new Vector4(vec4t0[i].X, vec4t0[i].Y, vec4t0[i].Z, vec4t0[i].W);
if (vec4b0.Length > 0)
v.bitan = new Vector4(vec4b0[i].X, vec4b0[i].Y, vec4b0[i].Z, vec4b0[i].W);
if (vec4c0.Length > 0)
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)
{
@ -778,7 +808,6 @@ namespace FirstPlugin
m.HasNormalMap = true;
texture.Type = MatTexture.TextureType.Normal;
}
else if (texture.SamplerName == "_e0")
{
m.HasEmissionMap = true;
@ -796,7 +825,7 @@ namespace FirstPlugin
}
else if (texture.SamplerName == "_gn0") //Damage
{
}
// EOW Samplers
else if (useSampler == "_albedo0")
@ -1063,7 +1092,6 @@ namespace FirstPlugin
m.HasEmissionMap = true;
texture.Type = MatTexture.TextureType.Emission;
}
else if (texture.SamplerName == "_s0" || useSampler == "_s0")
{
m.HasSpecularMap = true;
@ -1084,14 +1112,11 @@ namespace FirstPlugin
m.HasLightMap = true;
texture.Type = MatTexture.TextureType.Light;
}
else if (texture.SamplerName == "bake0")
{
m.HasShadowMap = true;
texture.Type = MatTexture.TextureType.Shadow;
}
// EOW Frag Samplers
} // EOW Frag Samplers
else if (useSampler == "Albedo0")
{

View file

@ -50,10 +50,6 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Toolbox\Lib\BcresLibrary.dll</HintPath>
</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">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Toolbox\Lib\BfshaLibrary.dll</HintPath>
@ -229,6 +225,13 @@
<Compile Include="FileFormats\AAMP\AAMP.cs" />
<Compile Include="FileFormats\Archives\APAK.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\DARC.cs" />
<Compile Include="FileFormats\Archives\DAT_Bayonetta.cs" />

View file

@ -105,6 +105,7 @@
this.stCheckBox1 = new Toolbox.Library.Forms.STCheckBox();
this.chkMapOriginalMaterials = new Toolbox.Library.Forms.STCheckBox();
this.ogSkinCountChkBox = new Toolbox.Library.Forms.STCheckBox();
this.combineUVs = new Toolbox.Library.Forms.STCheckBox();
this.contentContainer.SuspendLayout();
this.panel1.SuspendLayout();
this.panel2.SuspendLayout();
@ -460,6 +461,7 @@
//
// panel8
//
this.panel8.Controls.Add(this.combineUVs);
this.panel8.Controls.Add(this.stLabel4);
this.panel8.Controls.Add(this.lodCountUD);
this.panel8.Controls.Add(this.chkCreateDummyLODs);
@ -603,7 +605,6 @@
this.chkBoxRotNegative90Y.Text = "Rotate -90 degrees";
this.chkBoxRotNegative90Y.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.chkBoxRotNegative90Y.UseVisualStyleBackColor = true;
this.chkBoxRotNegative90Y.CheckedChanged += new System.EventHandler(this.chkBoxSettings_CheckedChanged);
//
// textBoxMaterialPath
//
@ -836,7 +837,7 @@
this.tabPageAdvanced.Location = new System.Drawing.Point(4, 25);
this.tabPageAdvanced.Name = "tabPageAdvanced";
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.Text = "Advanced Settings";
//
@ -853,7 +854,7 @@
this.stPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.stPanel1.Location = new System.Drawing.Point(3, 3);
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;
//
// tabPage1
@ -870,7 +871,7 @@
this.tabPage1.Location = new System.Drawing.Point(4, 25);
this.tabPage1.Name = "tabPage1";
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.Text = "Inject Mode";
this.tabPage1.UseVisualStyleBackColor = true;
@ -981,6 +982,16 @@
this.ogSkinCountChkBox.Text = "Keep Original Skin Count (can help crashes)";
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
//
this.ClientSize = new System.Drawing.Size(547, 412);
@ -1097,5 +1108,6 @@
private Toolbox.Library.Forms.STCheckBox chkCreateDummyLODs;
private Toolbox.Library.Forms.STLabel stLabel4;
private Toolbox.Library.Forms.NumericUpDownUint lodCountUD;
private Toolbox.Library.Forms.STCheckBox combineUVs;
}
}

View file

@ -7,6 +7,8 @@ using Toolbox.Library.Forms;
using Toolbox.Library.Rendering;
using Bfres.Structs;
using System.Linq;
using Syroot.NintenTools.NSW.Bfres;
using static OpenTK.Graphics.OpenGL.GL;
namespace FirstPlugin
{
@ -65,6 +67,8 @@ namespace FirstPlugin
public bool ResetUVParams;
public bool ResetColorParams;
public bool CombineUVs => combineUVs.Checked;
public bool LimitSkinCount => ogSkinCountChkBox.Checked;
public bool MapOriginalMaterials
{
@ -181,13 +185,18 @@ namespace FirstPlugin
Attributes[i].Format = (AttribFormat)comboBoxFormatWeights.SelectedItem;
if (Attributes[i].Name == "_i0")
Attributes[i].Format = (AttribFormat)comboBoxFormatIndices.SelectedItem;
if (CombineUVs && Attributes[i].Name == "_u0")
Attributes[i].Format = AttribFormat.Format_16_16_16_16_Single;
}
}
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>();
@ -230,20 +239,35 @@ namespace FirstPlugin
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
att.Name = "_u0";
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);
}
if (EnableUV1 && EnableUV0)
if (EnableUV1 && EnableUV0 && !attribute.ContainsKey("_g3d_02_u0_u1"))
{
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
att.Name = "_u1";
att.Format = (AttribFormat)comboBoxFormatUvs.SelectedItem;
attribute.Add(att.Name, att);
}
if (EnableUV2 && EnableUV0)
{
FSHP.VertexAttribute att = new FSHP.VertexAttribute();
att.Name = "_u2";
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);
}
if (EnableTangents)
@ -275,6 +299,25 @@ namespace FirstPlugin
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)
{
//Use single buffer
@ -492,12 +535,10 @@ namespace FirstPlugin
int assimpIndex = assimpMeshListView.SelectedIndices[0];
objectNameTB.BackColor = System.Drawing.Color.DarkRed;
foreach (ListViewItem item in originalMeshListView.Items)
{
if (objectNameTB.Text == item.Text)
objectNameTB.BackColor = System.Drawing.Color.Green;
}
if (objectNameTB.Text == originalMeshListView.Items[assimpIndex].Text)
objectNameTB.BackColor = System.Drawing.Color.Green;
else
objectNameTB.BackColor = System.Drawing.Color.DarkRed;
NewMeshlist[assimpIndex].ObjectName = objectNameTB.Text;
}

View file

@ -15,7 +15,7 @@ using Toolbox.Library.IO;
namespace Toolbox.Library
{
public class DAE
public class DAE
{
public class ExportSettings
{
@ -55,11 +55,12 @@ namespace Toolbox.Library
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) {
Export(FileName, settings, model.Objects.ToList(), model.Materials.ToList(), Textures, skeleton, NodeArray);
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);
}
public static void Export(string FileName, ExportSettings settings,
public static void Export(string FileName, ExportSettings settings,
List<STGenericObject> Meshes, List<STGenericMaterial> Materials,
List<STGenericTexture> Textures, STSkeleton skeleton = null, List<int> NodeArray = null)
{
@ -102,7 +103,8 @@ namespace Toolbox.Library
if (!textureNames.Contains(Textures[i].Text))
textureNames.Add(Textures[i].Text);
if (settings.ExportTextures) {
if (settings.ExportTextures)
{
progressBar.Task = $"Exporting Texture {Textures[i].Text}";
progressBar.Value = ((i * 100) / Textures.Count);
@ -137,7 +139,8 @@ namespace Toolbox.Library
GC.Collect();
}
}
catch (Exception ex) {
catch (Exception ex)
{
failedTextureExport.Add(Textures[i].Text);
}
}
@ -189,7 +192,7 @@ namespace Toolbox.Library
texMap.WrapModeT = SamplerWrapMode.WRAP;
else if (tex.WrapModeT == STTextureWrapMode.Mirror)
texMap.WrapModeT = SamplerWrapMode.MIRROR;
else if(tex.WrapModeT == STTextureWrapMode.Clamp)
else if (tex.WrapModeT == STTextureWrapMode.Clamp)
texMap.WrapModeT = SamplerWrapMode.CLAMP;
@ -210,7 +213,8 @@ namespace Toolbox.Library
else
writer.WriteLibraryImages();
if (skeleton != null) {
if (skeleton != null)
{
//Search for bones with rigging first
List<string> riggedBones = new List<string>();
if (settings.OnlyExportRiggedBones)
@ -223,7 +227,8 @@ namespace Toolbox.Library
for (int j = 0; j < vertex.boneIds.Count; j++)
{
int id = -1;
if (NodeArray != null && NodeArray.Count > vertex.boneIds[j]) {
if (NodeArray != null && NodeArray.Count > vertex.boneIds[j])
{
id = NodeArray[vertex.boneIds[j]];
}
else
@ -288,7 +293,7 @@ namespace Toolbox.Library
if (mesh.MaterialIndex != -1 && Materials.Count > mesh.MaterialIndex)
{
writer.CurrentMaterial = Materials[mesh.MaterialIndex].Text;
Console.WriteLine($"MaterialIndex {mesh.MaterialIndex } {Materials[mesh.MaterialIndex].Text}");
Console.WriteLine($"MaterialIndex {mesh.MaterialIndex} {Materials[mesh.MaterialIndex].Text}");
}
@ -312,18 +317,21 @@ namespace Toolbox.Library
transform = diffuse.Transform;
var vertexA = mesh.vertices[faces[v]];
var vertexB = mesh.vertices[faces[v+1]];
var vertexC = mesh.vertices[faces[v+2]];
var vertexB = mesh.vertices[faces[v + 1]];
var vertexC = mesh.vertices[faces[v + 2]];
if (!transformedVertices.Contains(vertexA)) {
if (!transformedVertices.Contains(vertexA))
{
vertexA.uv0 = (vertexA.uv0 * transform.Scale) + transform.Translate;
transformedVertices.Add(vertexA);
}
if (!transformedVertices.Contains(vertexB)) {
if (!transformedVertices.Contains(vertexB))
{
vertexB.uv0 = (vertexB.uv0 * transform.Scale) + transform.Translate;
transformedVertices.Add(vertexB);
}
if (!transformedVertices.Contains(vertexC)) {
if (!transformedVertices.Contains(vertexC))
{
vertexC.uv0 = (vertexC.uv0 * transform.Scale) + transform.Translate;
transformedVertices.Add(vertexC);
}
@ -340,15 +348,20 @@ namespace Toolbox.Library
List<float> UV3 = new List<float>();
List<float> Color = 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<float[]> BoneWeights = new List<float[]>();
bool HasNormals = false;
bool HasColors = false;
bool HasColors2 = false;
bool HasColors3 = false;
bool HasColors4 = false;
bool HasUV0 = false;
bool HasUV1 = false;
bool HasUV2 = false;
bool HasUV3 = false;
bool HasBoneIds = false;
foreach (var vertex in mesh.vertices)
@ -356,9 +369,13 @@ namespace Toolbox.Library
if (vertex.nrm != Vector3.Zero) HasNormals = true;
if (vertex.col != Vector4.One && settings.UseVertexColors) HasColors = 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.uv1 != Vector2.Zero) HasUV1 = true;
if (vertex.uv2 != Vector2.Zero) HasUV2 = true;
if (vertex.uv3 != Vector2.Zero) HasUV3 = true;
if (vertex.boneIds.Count > 0) HasBoneIds = true;
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);
UV1.Add(vertex.uv1.X); UV1.Add(1 - vertex.uv1.Y);
UV2.Add(vertex.uv2.X); UV2.Add(1 - vertex.uv2.Y);
UV3.Add(vertex.uv3.X); UV3.Add(1 - vertex.uv3.Y);
}
else
{
UV0.Add(vertex.uv0.X); UV0.Add(vertex.uv0.Y);
UV1.Add(vertex.uv1.X); UV1.Add(vertex.uv1.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 });
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<float> bWeights = new List<float>();
@ -446,7 +467,7 @@ namespace Toolbox.Library
foreach (var group in mesh.PolygonGroups)
{
TriangleList triangleList = new TriangleList();
triangleLists.Add(triangleList);
STGenericMaterial material = new STGenericMaterial();
@ -484,9 +505,12 @@ namespace Toolbox.Library
if (HasColors)
writer.WriteGeometrySource(mesh.Text, SemanticType.COLOR, Color.ToArray(), triangleLists.ToArray(), 0);
if (HasColors2)
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)
writer.WriteGeometrySource(mesh.Text, SemanticType.TEXCOORD, UV0.ToArray(), triangleLists.ToArray(), 0);
@ -497,6 +521,9 @@ namespace Toolbox.Library
if (HasUV2)
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)
writer.AttachGeometryController(BoneIndices, BoneWeights);
@ -508,7 +535,7 @@ namespace Toolbox.Library
progressBar?.Close();
if (!settings.SuppressConfirmDialog)
System.Windows.Forms.MessageBox.Show($"Exported {FileName} successfully!");
System.Windows.Forms.MessageBox.Show($"Exported {FileName} Successfully!");
}
@ -530,7 +557,7 @@ namespace Toolbox.Library
COLLADA collada = COLLADA.Load(FileName);
//Check axis up
if (collada.asset != null)
{
@ -593,7 +620,7 @@ namespace Toolbox.Library
private void LoadNodes(library_nodes nodes)
{
}
private void LoadMaterials(library_materials materials)

View file

@ -24,6 +24,8 @@ namespace Toolbox.Library.Rendering
public Vector3 nrm = new Vector3(0);
public Vector4 col = 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 uv1 = new Vector2(0);