static List <PanePatch> DiffPanes(BflytFile _or, BflytFile _ed, string filename) { List <PanePatch> curFile = new List <PanePatch>(); foreach (var orpane_ in _or.EnumeratePanes().Where(x => x is INamedPane)) { var edpane = _ed[((INamedPane)orpane_).PaneName]; if (edpane == null) { throw new Exception($"{filename} is missing {((INamedPane)orpane_).PaneName}"); } if (orpane_.name != edpane.name) { throw new Exception($"{filename} : {((INamedPane)orpane_).PaneName} Two panes with the same name are of a different type"); } if (IgnorePaneList.Contains(orpane_.name)) { continue; } var edPan = (Pan1Pane)edpane; var orPan = (Pan1Pane)orpane_; PanePatch curPatch = new PanePatch() { PaneName = edPan.PaneName }; curPatch.UsdPatches = MakeUsdPatch(edPan.UserData, orPan.UserData); if (edPan.data.SequenceEqual(orPan.data)) { if (curPatch.UsdPatches != null) { curFile.Add(curPatch); } continue; } if (edPan.Position != orPan.Position) { curPatch.Position = edPan.Position; } if (edPan.Rotation != orPan.Rotation) { curPatch.Rotation = edPan.Rotation; } if (edPan.Scale != orPan.Scale) { curPatch.Scale = edPan.Scale; } if (edPan.Size != orPan.Size) { curPatch.Size = edPan.Size; } if (edPan.Visible != orPan.Visible) { curPatch.Visible = edPan.Visible; } if (edPan.originX != orPan.originX) { curPatch.OriginX = (byte)edPan.originX; } if (edPan.originY != orPan.originY) { curPatch.OriginY = (byte)edPan.originY; } if (edPan.ParentOriginX != orPan.ParentOriginX) { curPatch.ParentOriginX = (byte)edPan.ParentOriginX; } if (edPan.ParentOriginY != orPan.ParentOriginY) { curPatch.ParentOriginY = (byte)edPan.ParentOriginY; } if (edPan is Pic1Pane && orPan is Pic1Pane) { var edPic = (Pic1Pane)edPan; var orPic = (Pic1Pane)orPan; if (edPic.ColorTopLeft != orPic.ColorTopLeft) { curPatch.PaneSpecific0 = edPic.ColorTopLeft.AsHexLEString(); } if (edPic.ColorTopRight != orPic.ColorTopRight) { curPatch.PaneSpecific1 = edPic.ColorTopRight.AsHexLEString(); } if (edPic.ColorBottomLeft != orPic.ColorBottomLeft) { curPatch.PaneSpecific2 = edPic.ColorBottomLeft.AsHexLEString(); } if (edPic.ColorBottomRight != orPic.ColorBottomRight) { curPatch.PaneSpecific3 = edPic.ColorBottomRight.AsHexLEString(); } } if (edPan is Txt1Pane && orPan is Txt1Pane) { var edTxt = (Txt1Pane)edPan; var orTxt = (Txt1Pane)orPan; if (edTxt.FontTopColor != orTxt.FontTopColor) { curPatch.PaneSpecific0 = edTxt.FontTopColor.AsHexLEString(); } if (edTxt.ShadowTopColor != orTxt.ShadowTopColor) { curPatch.PaneSpecific1 = edTxt.ShadowTopColor.AsHexLEString(); } if (edTxt.FontBottomColor != orTxt.FontBottomColor) { curPatch.PaneSpecific2 = edTxt.FontBottomColor.AsHexLEString(); } if (edTxt.ShadowBottomColor != orTxt.ShadowBottomColor) { curPatch.PaneSpecific3 = edTxt.ShadowBottomColor.AsHexLEString(); } } curFile.Add(curPatch); } return(curFile); }
public static (LayoutPatch, string) Diff(SarcData original, SarcData edited) { List <LayoutFilePatch> Patches = new List <LayoutFilePatch>(); if (!ScrambledEquals <string>(original.Files.Keys, edited.Files.Keys)) { throw new Exception("The provided archives don't have the same files"); } bool hasAtLeastAnExtraGroup = false; //Used to detect if animations are properly implemented foreach (var f in original.Files.Keys.Where(x => x.EndsWith(".bflyt"))) { if (original.Files[f].SequenceEqual(edited.Files[f])) { continue; } BflytFile _or = new BflytFile(original.Files[f]); BflytFile _ed = new BflytFile(edited.Files[f]); List <PanePatch> curFile = new List <PanePatch>(); foreach (var orpane_ in _or.EnumeratePanes().Where(x => x is INamedPane)) { var edpane = _ed[((INamedPane)orpane_).PaneName]; if (edpane == null) { throw new Exception($"{f} is missing {((INamedPane)orpane_).PaneName}"); } if (orpane_.name != edpane.name) { throw new Exception($"{f} : {((INamedPane)orpane_).PaneName} Two panes with the same name are of a different type"); } if (IgnorePaneList.Contains(orpane_.name)) { continue; } var edPan = (Pan1Pane)edpane; var orPan = (Pan1Pane)orpane_; PanePatch curPatch = new PanePatch() { PaneName = edPan.PaneName }; curPatch.UsdPatches = MakeUsdPatch(edPan.UserData, orPan.UserData); if (edPan.data.SequenceEqual(orPan.data)) { if (curPatch.UsdPatches != null) { curFile.Add(curPatch); } continue; } if (!VecEqual(edPan.Position, orPan.Position)) { curPatch.Position = ToNullVec(edPan.Position); } if (!VecEqual(edPan.Rotation, orPan.Rotation)) { curPatch.Rotation = ToNullVec(edPan.Rotation); } if (!VecEqual(edPan.Scale, orPan.Scale)) { curPatch.Scale = ToNullVec(edPan.Scale); } if (!VecEqual(edPan.Size, orPan.Size)) { curPatch.Size = ToNullVec(edPan.Size); } if (edPan.Visible != orPan.Visible) { curPatch.Visible = edPan.Visible; } if (edPan.originX != orPan.originX) { curPatch.OriginX = (byte)edPan.originX; } if (edPan.originY != orPan.originY) { curPatch.OriginY = (byte)edPan.originY; } if (edPan.ParentOriginX != orPan.ParentOriginX) { curPatch.ParentOriginX = (byte)edPan.ParentOriginX; } if (edPan.ParentOriginY != orPan.ParentOriginY) { curPatch.ParentOriginY = (byte)edPan.ParentOriginY; } if (edPan is Pic1Pane && orPan is Pic1Pane) { var edPic = (Pic1Pane)edPan; var orPic = (Pic1Pane)orPan; if (edPic.ColorTopLeft != orPic.ColorTopLeft) { curPatch.PaneSpecific0 = edPic.ColorTopLeft.AsHexLEString(); } if (edPic.ColorTopRight != orPic.ColorTopRight) { curPatch.PaneSpecific1 = edPic.ColorTopRight.AsHexLEString(); } if (edPic.ColorBottomLeft != orPic.ColorBottomLeft) { curPatch.PaneSpecific2 = edPic.ColorBottomLeft.AsHexLEString(); } if (edPic.ColorBottomRight != orPic.ColorBottomRight) { curPatch.PaneSpecific3 = edPic.ColorBottomRight.AsHexLEString(); } } if (edPan is Txt1Pane && orPan is Txt1Pane) { var edTxt = (Txt1Pane)edPan; var orTxt = (Txt1Pane)orPan; if (edTxt.FontTopColor != orTxt.FontTopColor) { curPatch.PaneSpecific0 = edTxt.FontTopColor.AsHexLEString(); } if (edTxt.ShadowTopColor != orTxt.ShadowTopColor) { curPatch.PaneSpecific1 = edTxt.ShadowTopColor.AsHexLEString(); } if (edTxt.FontBottomColor != orTxt.FontBottomColor) { curPatch.PaneSpecific2 = edTxt.FontBottomColor.AsHexLEString(); } if (edTxt.ShadowBottomColor != orTxt.ShadowBottomColor) { curPatch.PaneSpecific3 = edTxt.ShadowBottomColor.AsHexLEString(); } } curFile.Add(curPatch); } List <ExtraGroup> extraGroups = new List <ExtraGroup>(); string[] ogPanes = _or.EnumeratePanes(_or.RootGroup).Select(x => ((Grp1Pane)x).GroupName).ToArray(); var edPanes = _ed.EnumeratePanes(_ed.RootGroup).Cast <Grp1Pane>(); foreach (var p in edPanes) { if (ogPanes.Contains(p.GroupName)) { continue; } extraGroups.Add(new ExtraGroup() { GroupName = p.GroupName, Panes = p.Panes.ToArray() }); hasAtLeastAnExtraGroup = true; } if (extraGroups.Count == 0) { extraGroups = null; } List <MaterialPatch> materials = new List <MaterialPatch>(); if (_ed.GetMat != null && _or.GetMat != null) { var edMat = _ed.GetMat; foreach (var orM in _or.GetMat.Materials) { var edM = edMat.Materials.Where(x => x.Name == orM.Name).FirstOrDefault(); if (edM == null) { continue; } if (edM.ForegroundColor == orM.ForegroundColor && edM.BackgroundColor == orM.BackgroundColor) { continue; } MaterialPatch m = new MaterialPatch() { MaterialName = orM.Name }; if (edM.ForegroundColor != orM.ForegroundColor) { m.ForegroundColor = edM.ForegroundColor.AsHexLEString(); } if (edM.BackgroundColor != orM.BackgroundColor) { m.BackgroundColor = edM.BackgroundColor.AsHexLEString(); } materials.Add(m); } } if (materials.Count == 0) { materials = null; } if (curFile.Count > 0 || extraGroups?.Count > 0 || materials?.Count > 0) { Patches.Add(new LayoutFilePatch() { FileName = f, Patches = curFile.ToArray(), Materials = materials?.ToArray(), AddGroups = extraGroups?.ToArray() }); } } if (Patches.Count == 0) //animation edits depend on bflyt changes so this is relevant { throw new Exception("Couldn't find any difference"); } string Message = null; List <AnimFilePatch> AnimPatches = new List <AnimFilePatch>(); foreach (var f in original.Files.Keys.Where(x => x.EndsWith(".bflan"))) { if (original.Files[f].SequenceEqual(edited.Files[f])) { continue; } BflanFile anim = new BflanFile(edited.Files[f]); AnimPatches.Add(new AnimFilePatch() { FileName = f, AnimJson = BflanSerializer.ToJson(anim) }); } if (AnimPatches.Count == 0) { AnimPatches = null; } else if (!hasAtLeastAnExtraGroup) { Message = "This theme uses custom animations but doesn't have custom group in the layouts, this means that the nxtheme will work on the firmware it has been developed on but it may break on older or newer ones. It's *highly recommended* to create custom groups to handle animations"; } var targetPatch = SzsPatcher.DetectSarc(original, DefaultTemplates.templates); return(new LayoutPatch() { PatchName = "diffPatch" + (targetPatch == null ? "" : " for " + targetPatch.TemplateName), TargetName = targetPatch?.szsName, AuthorName = "autoDiff", Files = Patches.ToArray(), Anims = AnimPatches?.ToArray(), ID = $"Generated_{Guid.NewGuid()}" }, Message); }