Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified BrawlboxHelper/BrawlboxHelper.dll
Binary file not shown.
Binary file modified BrawlboxHelper/Syroot.NintenTools.NSW.Bfres.dll
Binary file not shown.
179 changes: 134 additions & 45 deletions File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FSKA.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,94 +164,183 @@ public void UpdateEditor() {
public override string ExportFilter => FileFilters.GetFilter(typeof(FSKA),null, true);
public override string ReplaceFilter => FileFilters.GetFilter(typeof(FSKA));

public override void Export(string FileName)
public override void Export(string FilePath)
{
string ext = Utils.GetExtension(FileName);
Export(FilePath, null);
}

public void Export(string FilePath, STSkeleton backupSkeleton)
{
Console.WriteLine($"Anim Export: {FilePath}");
string sourcePath = "";
if (SkeletalAnimU != null)
sourcePath = SkeletalAnimU.Path;
else if (SkeletalAnim != null)
sourcePath = SkeletalAnim.Path;

string ext = Utils.GetExtension(FilePath);
if (ext == ".bfska")
{
if (GetResFileU() != null)
{
SkeletalAnimU.Export(FileName, GetResFileU());
SkeletalAnimU.Export(FilePath, GetResFileU());
}
else
{
SkeletalAnim.Export(FileName, GetResFile());
SkeletalAnim.Export(FilePath, GetResFile());
}
}
else if (ext == ".json")
{
if (SkeletalAnimU != null)
System.IO.File.WriteAllText(FileName, Newtonsoft.Json.JsonConvert.SerializeObject(SkeletalAnimU,
System.IO.File.WriteAllText(FilePath, Newtonsoft.Json.JsonConvert.SerializeObject(SkeletalAnimU,
Newtonsoft.Json.Formatting.Indented));
else
System.IO.File.WriteAllText(FileName, Newtonsoft.Json.JsonConvert.SerializeObject(SkeletalAnim,
System.IO.File.WriteAllText(FilePath, Newtonsoft.Json.JsonConvert.SerializeObject(SkeletalAnim,
Newtonsoft.Json.Formatting.Indented));
}
else if (ext == ".chr0")
{
if (SkeletalAnimU != null)
BrawlboxHelper.FSKAConverter.Fska2Chr0(BfresPlatformConverter.FSKAConvertWiiUToSwitch(SkeletalAnimU), FilePath);
else
BrawlboxHelper.FSKAConverter.Fska2Chr0(SkeletalAnim, FilePath);
}
else
{
// The export functions of the following formats require a skeleton.
// Try to find best matching skeleton in the visible viewport skeletons,
// or the animation's nodegroup, if it's in the nodetree (when this function
// is called by right click->Export)
STSkeleton skeleton = GetActiveSkeleton();
if (skeleton == null)
throw new Exception("No skeleton found to assign! Make sure a model is open in the viewport.");

if (ext == ".chr0")
skeleton = backupSkeleton; // When this func is called from Tools->Batch Export Animations, backupSkeleton is the one from the .sbfres.
if (skeleton == null)
{
if (SkeletalAnimU != null)
BrawlboxHelper.FSKAConverter.Fska2Chr0(BfresPlatformConverter.FSKAConvertWiiUToSwitch(SkeletalAnimU), FileName);
else
BrawlboxHelper.FSKAConverter.Fska2Chr0(SkeletalAnim, FileName);
throw new Exception($"{Text} No skeleton found.\n Load a skeleton in the viewport with a skeleton matching the animations.");
}
else if (ext == ".smd")
SMD.Save(this, skeleton, FileName);
var missingBones = new HashSet<string>();
foreach (KeyNode boneAnim in Bones)
{
STBone bone = skeleton.GetBone(boneAnim.Text);
if (bone == null)
{
missingBones.Add(boneAnim.Text);
}
}

if (ext == ".smd")
SMD.Save(this, skeleton, FilePath);
else if (ext == ".anim")
ANIM.CreateANIM(FileName, this, skeleton);
ANIM.CreateANIM(FilePath, this, skeleton);
else if (ext == ".seanim")
SEANIM.SaveAnimation(FileName, this, skeleton);
{
SEANIM.SaveAnimation(FilePath, this, skeleton);
}

if (missingBones.Count > 0)
throw new Exception($"{Text}: Discarded animation of {missingBones.Count} bones:\n {string.Join("\n ", missingBones)}\n Load a skeleton in the viewport with a skeleton matching the animations.");
}
}

private STSkeleton GetActiveSkeleton()
{
STSkeleton perfectSkeleton = GetPerfectMatchSkeleton_InViewport();
if (perfectSkeleton != null)
return perfectSkeleton;

perfectSkeleton = GetPerfectMatchSkeleton_InOwnNodeGroup();
if (perfectSkeleton != null)
return perfectSkeleton;

//If those didn't return, resort to the first model of the nodegroup,
//even though it's an imperfect match.
if (Parent == null)
return null;

//Check parent renderer and find skeleton
var render = ((BFRES)Parent.Parent.Parent).BFRESRender;
if (render != null)
if (render != null && render.models.Count == 1)
return render.models[0].Skeleton;

// If even that didn't work, resort to just WHATEVER THE HECK is loaded in the viewport.
var skeletons = GetViewportSkeletons();
if (skeletons.Count > 0)
{
//Return individual skeleton for single model files
if (render.models.Count == 1)
return render.models[0].Skeleton;
return skeletons[0];
}

//Search multiple FMDL to find matching bones
foreach (var model in render.models)
{
//Check if all the bones in the animation are present in the skeleton
bool areAllBonesPresent = model.Skeleton.bones.Count > 0;
foreach (var bone in Bones)
{
var animBone = model.Skeleton.GetBone(bone.Text);
return null;
}

if (animBone == null)
areAllBonesPresent = false;
}
if (areAllBonesPresent)
return model.Skeleton;
}
public List<STSkeleton> GetViewportSkeletons()
{
var viewport = LibraryGUI.GetActiveViewport();
if (viewport == null)
return null;

//If not all bones were present but models are present, use first model
if (render.models.Count > 0)
return render.models[0].Skeleton;
}
var containers = viewport.GetActiveContainers();
if (containers == null || containers.Count == 0)
return null;

//Search by viewport active model in the event the animation is externally loaded
var viewport = LibraryGUI.GetActiveViewport();
if (viewport != null)
var skeletons = new List<STSkeleton>();

foreach (var container in containers)
{
foreach (var drawable in viewport.scene.objects)
foreach (var drawable in container.Drawables)
{
if (drawable is STSkeleton)
return ((STSkeleton)drawable);
skeletons.Add((STSkeleton)drawable);
}
}

return skeletons;
}

public STSkeleton GetPerfectMatchSkeleton_InViewport()
{
//Try to find a skeleton matching this animation out of visible viewport skeletons.
var skeletons = GetViewportSkeletons();
return GetPerfectMatchSkeleton(skeletons);
}

public STSkeleton GetPerfectMatchSkeleton_InOwnNodeGroup()
{
if (Parent == null)
return null;

//Check parent renderer and find skeleton
var render = ((BFRES)Parent.Parent.Parent).BFRESRender;
if (render == null)
return null;

var skeletons = (from model in render.models select (STSkeleton)model.Skeleton).ToList();

STSkeleton bestMatchSkeleton = GetPerfectMatchSkeleton(skeletons);
if (bestMatchSkeleton != null)
return bestMatchSkeleton;

return null;
}

public STSkeleton GetPerfectMatchSkeleton(List<STSkeleton> skeletons)
{
//Search multiple FMDL to find matching bones
foreach (var skeleton in skeletons)
{
//Check if all the bones in the animation are present in the skeleton
bool areAllBonesPresent = skeleton.bones.Count > 0;
foreach (var bone in Bones)
{
var animBone = skeleton.GetBone(bone.Text);

if (animBone == null)
{
areAllBonesPresent = false;
break;
}
}
if (areAllBonesPresent)
return skeleton;
}

return null;
Expand Down
5 changes: 3 additions & 2 deletions File_Format_Library/NodeWrappers/FileFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public class FileFilters
public static string BONE = GetFilter(".bfbon");
public static string FMAT = GetFilter(".bfmat");

public static string FSKA_EXPORT = GetFilter(".bfska", ".anim", ".seanim", ".smd", ".chr0");
public static string FSKA_REPLACE = GetFilter(".bfska",".anim", ".seanim", ".smd", ".chr0");
public static string FSKA_EXPORT = GetFilter(".seanim", ".smd", ".anim", ".bfska", ".chr0", ".json");
public static string FSKA_REPLACE = GetFilter(".seanim", ".smd", ".anim", ".bfska", ".chr0");

public static string FMAA = GetFilter(".bfmaa",".yaml", ".gif");

Expand Down Expand Up @@ -142,6 +142,7 @@ public static Dictionary<string, string> GetDescription(string[] extensions)
case ".yaml": filters.Add(ext, "Yet Another Markup Language"); break;
case ".gif": filters.Add(ext, "Graphics Interchange Format"); break;
case ".cmdl": filters.Add(ext, "CTR Model"); break;
case ".json": filters.Add(ext, "JavaScript Object Notation"); break;
default:
filters.Add(ext, ""); break;
}
Expand Down
15 changes: 14 additions & 1 deletion Switch_Toolbox_Library/Forms/Custom/Treeview/TreeViewCustom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,22 @@ public virtual void OnExpand() { }
public TreeNodeCustom()
{
}
public TreeNodeCustom RootNode
{
get
{
TreeNodeCustom currentNode = this;
while (currentNode.Parent != null)
{
currentNode = currentNode.Parent as TreeNodeCustom;
}

return currentNode;
}
}
}

public class TreeNodeFile : TreeNodeCustom
public class TreeNodeFile : TreeNodeCustom
{
public bool CanDelete
{
Expand Down
23 changes: 19 additions & 4 deletions Switch_Toolbox_Library/Generics/STGenericWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Windows.Forms;
using Toolbox.Library;
using Toolbox.Library.Forms;
using System.IO;

namespace Toolbox.Library.NodeWrappers
{
Expand Down Expand Up @@ -146,21 +147,35 @@ protected void ExportAllAction(object sender, EventArgs e)
FolderSelectDialog sfd = new FolderSelectDialog();
if (sfd.ShowDialog() == DialogResult.OK)
{
string folderPath = sfd.SelectedPath;

BatchFormatExport form = new BatchFormatExport(Formats);
if (form.ShowDialog() == DialogResult.OK)
{
string folderPath = $"{sfd.SelectedPath}\\{RootNode.Text}";
Directory.CreateDirectory(folderPath);

string extension = form.GetSelectedExtension();
extension.Replace(" ", string.Empty);

var failedExports = new List<string>();

foreach (TreeNode node in Nodes)
{
if (node is STGenericWrapper)
try
{
if (node is STGenericWrapper)
{
((STGenericWrapper)node).Export($"{folderPath}\\{node.Text}{extension}");
}
} catch (Exception ex)
{
((STGenericWrapper)node).Export($"{folderPath}\\{node.Text}{extension}");
failedExports.Add(ex.Message);
}

}

if (failedExports.Count > 0)
STErrorDialog.Show("Files exported with warnings.", "Switch Toolbox", string.Join("\n", failedExports));

}
}
}
Expand Down
14 changes: 13 additions & 1 deletion Toolbox/MainForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading