From df5457b39854d6ede29f1ded50523ac1bd04a536 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Mon, 29 Jul 2024 18:20:16 -0400 Subject: [PATCH 01/21] Add files via upload --- OpenKh.Kh2/BaseTable.cs | 32 +++- OpenKh.Kh2/Battle/Atkp.cs | 46 ++++- OpenKh.Kh2/Battle/Limt.cs | 46 ++++- OpenKh.Kh2/Battle/Stop.cs | 26 +++ OpenKh.Kh2/Jigsaw.cs | 41 +++- OpenKh.Kh2/Libretto.cs | 145 ++++++++++++++ OpenKh.Kh2/Localset.cs | 16 ++ OpenKh.Kh2/Place.cs | 48 +++++ OpenKh.Kh2/Soundinfo.cs | 49 +++++ OpenKh.Kh2/SystemData/Memt.cs | 346 ++++++++++++++++++---------------- 10 files changed, 617 insertions(+), 178 deletions(-) create mode 100644 OpenKh.Kh2/Battle/Stop.cs create mode 100644 OpenKh.Kh2/Libretto.cs create mode 100644 OpenKh.Kh2/Localset.cs create mode 100644 OpenKh.Kh2/Place.cs create mode 100644 OpenKh.Kh2/Soundinfo.cs diff --git a/OpenKh.Kh2/BaseTable.cs b/OpenKh.Kh2/BaseTable.cs index 77d8db9d5..c0de1a628 100644 --- a/OpenKh.Kh2/BaseTable.cs +++ b/OpenKh.Kh2/BaseTable.cs @@ -31,7 +31,9 @@ public static void Write(Stream stream, int version, IEnumerable items) foreach (var item in itemList) BinaryMapping.WriteObject(stream, item); } - } + } + + public class BaseShortTable where T : class @@ -56,6 +58,34 @@ public static void Write(Stream stream, int id, IEnumerable items) Count = (short)itemList.Count, }); + foreach (var item in itemList) + BinaryMapping.WriteObject(stream, item); + } + } + + + //Tables that only have a Count of entries; like soundinfo, etc. + public class BaseTableCountOnly + where T : class + { + [Data] public int Count { get; set; } + + public static List Read(Stream stream) + { + var header = BinaryMapping.ReadObject>(stream); + return Enumerable.Range(0, header.Count) + .Select(_ => BinaryMapping.ReadObject(stream)) + .ToList(); + } + + public static void Write(Stream stream, IEnumerable items) + { + var itemList = items as IList ?? items.ToList(); + BinaryMapping.WriteObject(stream, new BaseTableCountOnly + { + Count = (short)itemList.Count, + }); + foreach (var item in itemList) BinaryMapping.WriteObject(stream, item); } diff --git a/OpenKh.Kh2/Battle/Atkp.cs b/OpenKh.Kh2/Battle/Atkp.cs index 8224c72fe..7f421d565 100644 --- a/OpenKh.Kh2/Battle/Atkp.cs +++ b/OpenKh.Kh2/Battle/Atkp.cs @@ -110,14 +110,42 @@ public enum AttackKind : byte 0x66, 0x5F, 0x6D, 0x6F, 0x76, 0x65, 0x00, 0x63, 0x72, 0x61, 0x73, 0x68, 0x00 }; - public static List Read(Stream stream) => BaseTable.Read(stream); - - public static void Write(Stream stream, IEnumerable items) - { - BaseTable.Write(stream, 6, items); - stream.Position = 130232; - stream.Write(endBytes,0,277); - } - + public static List Read(Stream stream) => BaseTable.Read(stream); + + //New ATKP: Extra byte addition is no longer hardcoded to add to a specific position. + //Rather, they're now added at the end. + //The bytes seem to not be important, the strings resemble ones found in AI. + //However, to prevent messing with the ATKP offset unless explicity adding attack hitboxes, they'll be appended to the end of the file. + public static void Write(Stream stream, IEnumerable items) + { + // Get the initial length of the stream + long initialLength = stream.Length; + + // Write the items to the stream + BaseTable.Write(stream, 6, items); + + // Check if the stream length has increased + if (stream.Length > initialLength) + { + + // Seek to the end of the stream + stream.Seek(0, SeekOrigin.End); + + // Append the bytes to the end of the stream + stream.Write(endBytes, 0, endBytes.Length); + } + if (stream.Length == initialLength) + { + // Seek to the end of the stream + stream.Seek(0, SeekOrigin.End); + } + //Currently if you're just editing hitboxes, nothing changes. No offset differences occur. + //If you add one hitbox, it overwrites endbytes until it reaches the end of the file, and starts appending new bytes AND endbytes after. + + else + { + // If no new data was written, do nothing + } + } } } diff --git a/OpenKh.Kh2/Battle/Limt.cs b/OpenKh.Kh2/Battle/Limt.cs index d7288161a..34f20c44e 100644 --- a/OpenKh.Kh2/Battle/Limt.cs +++ b/OpenKh.Kh2/Battle/Limt.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using Xe.BinaryMapper; @@ -5,18 +6,51 @@ namespace OpenKh.Kh2.Battle { public class Limt - { + { + + + [Flags] + public enum Characters : byte + { + None = 0, + Sora = 1, + Donald = 2, + Goofy = 3, + Mickey = 4, + Auron = 5, + Mulan = 6, + Aladdin = 7, + JackSparrow = 8, + Beast = 9, + JackSkellington = 10, + Simba = 11, + Tron = 12, + Riku = 13, + Roxas = 14, + Ping = 15, + Stitch = 200, + Genie = 201, + PeterPan = 202, + ChickenLittle = 204, + } + + + [Data] public byte Id { get; set; } - [Data] public byte Character { get; set; } - [Data] public byte Summon { get; set; } + [Data] public Characters Character { get; set; } + [Data] public Characters Summon { get; set; } [Data] public byte Group { get; set; } [Data(Count = 32)] public string FileName { get; set; } [Data] public uint SpawnId { get; set; } [Data] public ushort Command { get; set; } [Data] public ushort Limit { get; set; } - [Data] public byte World { get; set; } - [Data(Count = 19)] public byte[] Padding { get; set; } - + [Data] public ushort World { get; set; } + [Data(Count = 18)] public byte[] Padding { get; set; } + + public override string ToString() + { + return FileName; + } public static List Read(Stream stream) => BaseTable.Read(stream); public static void Write(Stream stream, IEnumerable items) => diff --git a/OpenKh.Kh2/Battle/Stop.cs b/OpenKh.Kh2/Battle/Stop.cs new file mode 100644 index 000000000..140c44743 --- /dev/null +++ b/OpenKh.Kh2/Battle/Stop.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.Battle +{ + public class Stop + { + [Flags] + public enum Flag : uint + { + Exist = 0x01, + DisableDamageReaction = 0x02, + Star = 0x04, + DisableDraw = 0x08, + } + [Data] public ushort Id { get; set; } + [Data] public Flag Flags { get; set; } + + public static List Read(Stream stream) => BaseTable.Read(stream); + + public static void Write(Stream stream, IEnumerable items) => + BaseTable.Write(stream, 2, items); + } +} diff --git a/OpenKh.Kh2/Jigsaw.cs b/OpenKh.Kh2/Jigsaw.cs index ffc68a57e..da382c244 100644 --- a/OpenKh.Kh2/Jigsaw.cs +++ b/OpenKh.Kh2/Jigsaw.cs @@ -1,15 +1,48 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using Xe.BinaryMapper; namespace OpenKh.Kh2 { public class Jigsaw - { - [Data] public byte Picture { get; set; } + { + public enum PictureName : byte + { + Awakening = 0, + Heart = 1, + Duality = 2, + Frontier = 3, + Daylight = 4, + Sunset = 5 + } + + public enum WorldList : byte + { + ZZ = 0, + EndofSea = 1, + TwilightTown = 2, + DestinyIsland = 3, + HollowBastion = 4, + BeastsCastle = 5, + OlympusColiseum = 6, + Agrabah = 7, + TheLandOfDragons = 8, + HundredAcreWood = 9, + PrideLand = 10, + Atlantica = 11, + DisneyCastle = 12, + TimelessRiver = 13, + HalloweenTown = 14, + WorldMap = 15, + PortRoyal = 16, + SpaceParanoids = 17, + TheWorldThatNeverWas = 18, + } + + [Data] public PictureName Picture { get; set; } [Data] public byte Part { get; set; } [Data] public ushort Text { get; set; } //z_un_002a2de8, binary addition 0x8000 - [Data] public byte World { get; set; } + [Data] public WorldList World { get; set; } [Data] public byte Room { get; set; } [Data] public byte JigsawIdWorld { get; set; } [Data] public byte Unk07 { get; set; } //has also something to do with pos and orientation diff --git a/OpenKh.Kh2/Libretto.cs b/OpenKh.Kh2/Libretto.cs new file mode 100644 index 000000000..cf1fb2a39 --- /dev/null +++ b/OpenKh.Kh2/Libretto.cs @@ -0,0 +1,145 @@ +using OpenKh.Common; +using System; +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; +namespace OpenKh.Kh2 +//Can properly read/write and update. Can insert new entries between. +{ + public class Libretto + { + [Data] public int MagicCode { get; set; } + [Data] public int Count { get; set; } + [Data] public List Definitions { get; set; } + [Data] public List> Contents { get; set; } + + public class TalkMessageDefinition + { + [Data] public ushort TalkMessageId { get; set; } + [Data] public ushort Unknown { get; set; } + [Data] public uint ContentPointer { get; set; } + } + + public class TalkMessageContent + { + [Data] public uint Unknown1 { get; set; } + [Data] public uint TextId { get; set; } + } + + public class TalkMessagePatch + { + [Data] public ushort TalkMessageId { get; set; } + [Data] public ushort Unknown { get; set; } + [Data] public List Contents { get; set; } + } + + public class ContentPatch + { + [Data] public uint Unknown1 { get; set; } + [Data] public uint TextId { get; set; } + } + + public static Libretto Read(Stream stream) + { + //Store initial position of stream. + var basePosition = stream.Position; + //Create new Libretto object, then read MagicCode & Count from stream. + var libretto = new Libretto + { + MagicCode = stream.ReadInt32(), + Count = stream.ReadInt32() + }; + + //Initialize definitions/contents list w/ capacity equal to count + libretto.Definitions = new List(libretto.Count); + libretto.Contents = new List>(libretto.Count); + + //Loop over number of definitions specified by count. + for (int i = 0; i < libretto.Count; i++) + { + //Read TalkMessageDefinition from the stream, add to Definitions list. + libretto.Definitions.Add(new TalkMessageDefinition + { + TalkMessageId = stream.ReadUInt16(), + Unknown = stream.ReadUInt16(), + ContentPointer = stream.ReadUInt32() + }); + } + + //Loop over each definition in the Definitions list. + foreach (var definition in libretto.Definitions) + { + //Set stream position to the Content Pointer for the current definition. + stream.Position = basePosition + definition.ContentPointer; + //Create a new list to hold our TalkMessageContent objects for the current definition. + var contents = new List(); + + //Read all TalkMessageContents for current definition until Terminating Condition is met. + //while (true) //CAn set to while (true)... + //var content = BinaryMapping.ReadObject(stream); + //while (content.Unknown1 != 0) + + //Literally had to do both a while loop and manually change the content check. + //Also changed the way content is read, from being read like this: + // var content = BinaryMapping.ReadObject(stream); + //And changed the condition. So, might be times where you need to read like this. + //Yea. issue is down to using the BinaryMapper for this. + while (true) + { + // Read a TalkMessageContent object manually from the stream + var content = new TalkMessageContent + { + Unknown1 = stream.ReadUInt32(), + TextId = stream.ReadUInt32() + }; + + // Check for the termination condition: Unknown1 == 0 and TextId == 0 + if (content.Unknown1 == 0 || content.Unknown1 == null) + { + break; + } + + // Add content to the contents list + contents.Add(content); + } + //ADd list of contents for the current definition to the Contents list. + libretto.Contents.Add(contents); + } + + return libretto; + } + + + public static void Write(Stream stream, Libretto libretto) + { + var basePosition = stream.Position; + + stream.Write(libretto.MagicCode); + stream.Write(libretto.Count); + + var offset = 8 + libretto.Definitions.Count * 8; //Set offset variable; start AFTER magiccode+count and update the offset later. Offset = 8 + # of Definitions*8. + + foreach (var definition in libretto.Definitions) + { + stream.Write(definition.TalkMessageId); //Write the TalkMessage for each Definition. + stream.Write(definition.Unknown); //Write the Unknown for each Definition. + stream.Write(offset); //Write the offset for each Definition. + offset += libretto.Contents[libretto.Definitions.IndexOf(definition)].Count * 8 + 4; //Update the Offset in each definition. + } + + stream.Position = basePosition + 8 + libretto.Definitions.Count * 8; + foreach (var contents in libretto.Contents) + { + foreach (var content in contents) + { + stream.Write(content.Unknown1); + stream.Write(content.TextId); + //if (content.Unknown1 == 0) + // break; + } + //Break this until we figure out why it isn't reading. + stream.Write(0); // Write the padding (0x00000000) + } + } + } +} diff --git a/OpenKh.Kh2/Localset.cs b/OpenKh.Kh2/Localset.cs new file mode 100644 index 000000000..01d707668 --- /dev/null +++ b/OpenKh.Kh2/Localset.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2 +{ + public class Localset + { + [Data] public ushort ProgramId { get; set; } + [Data] public ushort MapNumber { get; set; } + + public static List Read(Stream stream) => BaseTable.Read(stream); + public static void Write(Stream stream, IEnumerable entries) => + BaseTable.Write(stream, 1, entries); + } +} diff --git a/OpenKh.Kh2/Place.cs b/OpenKh.Kh2/Place.cs new file mode 100644 index 000000000..0be12ff9c --- /dev/null +++ b/OpenKh.Kh2/Place.cs @@ -0,0 +1,48 @@ +using OpenKh.Common; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2 +{ + public class Places + { + [Data] public ushort MessageId { get; set; } + [Data] public ushort Padding { get; set; } + + //Two bytes after MessageId don't seem to matter. + + public class PlacePatch + { + [Data] public int Index { get; set; } + [Data] public ushort MessageId { get; set; } + [Data] public ushort Padding { get; set; } + } + public static List Read(Stream stream) + { + long count = stream.Length / 4; // Each entry is 4 bytes + var placesList = new List((int)count); + + for (long i = 0; i < count; i++) + { + placesList.Add(new Places + { + MessageId = stream.ReadUInt16(), + Padding = stream.ReadUInt16() + }); + } + + return placesList; + } + + public static void Write(Stream stream, List placesList) + { + foreach (var place in placesList) + { + stream.Write(place.MessageId); + stream.Write(place.Padding); + } + } + } +} diff --git a/OpenKh.Kh2/Soundinfo.cs b/OpenKh.Kh2/Soundinfo.cs new file mode 100644 index 000000000..e2196f829 --- /dev/null +++ b/OpenKh.Kh2/Soundinfo.cs @@ -0,0 +1,49 @@ +using OpenKh.Common; +using System; +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; +namespace OpenKh.Kh2 +//Can properly read/write and update. Can insert new entries between. +{ + public class Soundinfo + { + //[Data] public int Count { get; set; } + + [Data] public short Reverb { get; set; } + [Data] public short Rate { get; set; } + [Data] public short EnvironmentWAV { get; set; } + [Data] public short EnvironmentSEB { get; set; } + [Data] public short EnvironmentNUMBER { get; set; } + [Data] public short EnvironmentSPOT { get; set; } + [Data] public short FootstepWAV { get; set; } + [Data] public short FootstepSORA{ get; set; } + [Data] public short FootstepDONALD { get; set; } + [Data] public short FootstepGOOFY { get; set; } + [Data] public short FootstepWORLDFRIEND { get; set; } + [Data] public short FootstepOTHER { get; set; } + + + public class SoundinfoPatch + { + [Data] public int Index { get; set; } + [Data] public short Reverb { get; set; } + [Data] public short Rate { get; set; } + [Data] public short EnvironmentWAV { get; set; } + [Data] public short EnvironmentSEB { get; set; } + [Data] public short EnvironmentNUMBER { get; set; } + [Data] public short EnvironmentSPOT { get; set; } + [Data] public short FootstepWAV { get; set; } + [Data] public short FootstepSORA { get; set; } + [Data] public short FootstepDONALD { get; set; } + [Data] public short FootstepGOOFY { get; set; } + [Data] public short FootstepWORLDFRIEND { get; set; } + [Data] public short FootstepOTHER { get; set; } + } + + public static List Read(Stream stream) => BaseTableCountOnly.Read(stream); + public static void Write(Stream stream, IEnumerable entries) => + BaseTableCountOnly.Write(stream, entries); + + } +} diff --git a/OpenKh.Kh2/SystemData/Memt.cs b/OpenKh.Kh2/SystemData/Memt.cs index 257a132ef..c71bbdfae 100644 --- a/OpenKh.Kh2/SystemData/Memt.cs +++ b/OpenKh.Kh2/SystemData/Memt.cs @@ -1,158 +1,188 @@ -using OpenKh.Common; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Xe.BinaryMapper; - -namespace OpenKh.Kh2.SystemData -{ - public enum MemberVanilla - { - Sora, - Donald, - Goofy, - WorldCharacter, - FormValor, - FormWisdom, - FormTrinity, - FormFinal, - Antiform, - Mickey, - SoraHighPoly, - FormValorHighPoly, - FormWisdomHighPoly, - FormTrinityHighPoly, - FormFinalHighPoly, - AntiformHighPoly, - } - - public enum MemberFinalMix - { - Sora, - Donald, - Goofy, - WorldCharacter, - FormValor, - FormWisdom, - FormLimit, - FormTrinity, - FormFinal, - Antiform, - Mickey, - SoraHighPoly, - FormValorHighPoly, - FormWisdomHighPoly, - FormLimitHighPoly, - FormTrinityHighPoly, - FormFinalHighPoly, - AntiformHighPoly, - } - - public class Memt - { - private const int Version = 5; - public const int MemberCountVanilla = 16; - public const int MemberCountFinalMix = 18; - - public interface IEntry - { - short CheckStoryFlag { get; set; } - short CheckStoryFlagNegation { get; set; } - short[] Members { get; set; } - short Unk06 { get; set; } - short Unk08 { get; set; } - short Unk0A { get; set; } - short Unk0C { get; set; } - short Unk0E { get; set; } - short WorldId { get; set; } - } - - public class EntryVanilla : IEntry - { - [Data] public short WorldId { get; set; } - [Data] public short CheckStoryFlag { get; set; } - [Data] public short CheckStoryFlagNegation { get; set; } - [Data] public short Unk06 { get; set; } - [Data] public short Unk08 { get; set; } - [Data] public short Unk0A { get; set; } - [Data] public short Unk0C { get; set; } - [Data] public short Unk0E { get; set; } - [Data(Count = MemberCountVanilla)] public short[] Members { get; set; } - } - - public class EntryFinalMix : IEntry - { - [Data] public short WorldId { get; set; } - [Data] public short CheckStoryFlag { get; set; } - [Data] public short CheckStoryFlagNegation { get; set; } - [Data] public short Unk06 { get; set; } - [Data] public short Unk08 { get; set; } - [Data] public short Unk0A { get; set; } - [Data] public short Unk0C { get; set; } - [Data] public short Unk0E { get; set; } - [Data(Count = MemberCountFinalMix)] public short[] Members { get; set; } - } - - public class MemberIndices - { - [Data] public byte Player { get; set; } - [Data] public byte Friend1 { get; set; } - [Data] public byte Friend2 { get; set; } - [Data] public byte FriendWorld { get; set; } - } - - public List Entries { get; } - public MemberIndices[] MemberIndexCollection { get; } - - public Memt() - { - Entries = new List().Cast().ToList(); - MemberIndexCollection = new MemberIndices[1] - { - new MemberIndices - { - Player = 0, - Friend1 = 1, - Friend2 = 2, - FriendWorld = 3 - } - }; - } - - internal Memt(Stream stream) - { - const int HeaderSize = 8; - const int StructLengthVanilla = 48; - const int MemberIndicesExpected = 7; - - var previousPosition = stream.Position; - var version = stream.ReadInt32(); - var count = stream.ReadInt32(); - stream.Position = previousPosition; - - if (stream.Length - previousPosition == HeaderSize + - count * StructLengthVanilla + MemberIndicesExpected * 4) - Entries = BaseTable.Read(stream).Cast().ToList(); - else - Entries = BaseTable.Read(stream).Cast().ToList(); - - MemberIndexCollection = Enumerable.Range(0, MemberIndicesExpected) - .Select(x => BinaryMapping.ReadObject(stream)) - .ToArray(); - } - - public static Memt Read(Stream stream) => new Memt(stream); - - public static void Write(Stream stream, Memt memt) - { - var firstElement = memt.Entries.FirstOrDefault(); - if (firstElement is EntryVanilla) - BaseTable.Write(stream, Version, memt.Entries.Cast()); - else if (firstElement is EntryFinalMix) - BaseTable.Write(stream, Version, memt.Entries.Cast()); - - foreach (var item in memt.MemberIndexCollection) - BinaryMapping.WriteObject(stream, item); - } - } -} +using OpenKh.Common; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.SystemData +{ + public enum MemberVanilla + { + Sora, + Donald, + Goofy, + WorldCharacter, + FormValor, + FormWisdom, + FormTrinity, + FormFinal, + Antiform, + Mickey, + SoraHighPoly, + FormValorHighPoly, + FormWisdomHighPoly, + FormTrinityHighPoly, + FormFinalHighPoly, + AntiformHighPoly, + } + + public enum MemberFinalMix + { + Sora, + Donald, + Goofy, + WorldCharacter, + FormValor, + FormWisdom, + FormLimit, + FormTrinity, + FormFinal, + Antiform, + Mickey, + SoraHighPoly, + FormValorHighPoly, + FormWisdomHighPoly, + FormLimitHighPoly, + FormTrinityHighPoly, + FormFinalHighPoly, + AntiformHighPoly, + } + + public class Memt + { + private const int Version = 5; + public const int MemberCountVanilla = 16; + public const int MemberCountFinalMix = 18; + + public interface IEntry + { + short CheckStoryFlag { get; set; } + short CheckStoryFlagNegation { get; set; } + short[] Members { get; set; } + short Unk06 { get; set; } + short Unk08 { get; set; } + short Unk0A { get; set; } + short Unk0C { get; set; } + short Unk0E { get; set; } + short WorldId { get; set; } + } + + public class EntryVanilla : IEntry + { + [Data] public short WorldId { get; set; } + [Data] public short CheckStoryFlag { get; set; } + [Data] public short CheckStoryFlagNegation { get; set; } + [Data] public short Unk06 { get; set; } + [Data] public short Unk08 { get; set; } + [Data] public short Unk0A { get; set; } + [Data] public short Unk0C { get; set; } + [Data] public short Unk0E { get; set; } + [Data(Count = MemberCountVanilla)] public short[] Members { get; set; } + } + + public class EntryFinalMix : IEntry + { + [Data] public short WorldId { get; set; } + [Data] public short CheckStoryFlag { get; set; } + [Data] public short CheckStoryFlagNegation { get; set; } + [Data] public short Unk06 { get; set; } + [Data] public short Unk08 { get; set; } + [Data] public short Unk0A { get; set; } + [Data] public short Unk0C { get; set; } + [Data] public short Unk0E { get; set; } + [Data(Count = MemberCountFinalMix)] public short[] Members { get; set; } + } + + public class MemberIndices + { + [Data] public byte Player { get; set; } + [Data] public byte Friend1 { get; set; } + [Data] public byte Friend2 { get; set; } + [Data] public byte FriendWorld { get; set; } + } + + public List Entries { get; } + public MemberIndices[] MemberIndexCollection { get; } + + public class MemtEntryPatch + { + public int Index { get; set; } + public short WorldId { get; set; } + public short CheckStoryFlag { get; set; } + public short CheckStoryFlagNegation { get; set; } + public short Unk06 { get; set; } + public short Unk08 { get; set; } + public short Unk0A { get; set; } + public short Unk0C { get; set; } + public short Unk0E { get; set; } + public List Members { get; set; } + } + + public class MemberIndicesPatch + { + public int Index { get; set; } + public byte Player { get; set; } + public byte Friend1 { get; set; } + public byte Friend2 { get; set; } + public byte FriendWorld { get; set; } + } + + public class MemtPatches + { + public List MemtEntries { get; set; } + public List MemberIndices { get; set; } + } + + public Memt() + { + Entries = new List(); + MemberIndexCollection = new MemberIndices[7]; + for (int i = 0; i < MemberIndexCollection.Length; i++) + { + MemberIndexCollection[i] = new MemberIndices + { + Player = 0, + Friend1 = 0, + Friend2 = 0, + FriendWorld = 0 + }; + } + } + + internal Memt(Stream stream) + { + const int HeaderSize = 8; + const int StructLengthVanilla = 48; + const int MemberIndicesExpected = 7; + + var previousPosition = stream.Position; + var version = stream.ReadInt32(); + var count = stream.ReadInt32(); + stream.Position = previousPosition; + + if (stream.Length - previousPosition == HeaderSize + + count * StructLengthVanilla + MemberIndicesExpected * 4) + Entries = BaseTable.Read(stream).Cast().ToList(); + else + Entries = BaseTable.Read(stream).Cast().ToList(); + + MemberIndexCollection = Enumerable.Range(0, MemberIndicesExpected) + .Select(x => BinaryMapping.ReadObject(stream)) + .ToArray(); + } + + public static Memt Read(Stream stream) => new Memt(stream); + + public static void Write(Stream stream, Memt memt) + { + var firstElement = memt.Entries.FirstOrDefault(); + if (firstElement is EntryVanilla) + BaseTable.Write(stream, Version, memt.Entries.Cast()); + else if (firstElement is EntryFinalMix) + BaseTable.Write(stream, Version, memt.Entries.Cast()); + + foreach (var item in memt.MemberIndexCollection) + BinaryMapping.WriteObject(stream, item); + } + } +} From bd4e9cc11ea9c8d25078644e85ad0437088d2cb0 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Mon, 29 Jul 2024 18:20:39 -0400 Subject: [PATCH 02/21] Add files via upload --- OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs | 73 ++++++++++++ OpenKh.Kh2/SystemData/Pref/Fmab.cs | 37 ++++++ OpenKh.Kh2/SystemData/Pref/Magi.cs | 45 ++++++++ OpenKh.Kh2/SystemData/Pref/Plyr.cs | 39 +++++++ OpenKh.Kh2/SystemData/Pref/Prty.cs | 83 ++++++++++++++ OpenKh.Kh2/SystemData/Pref/Sstm.cs | 118 ++++++++++++++++++++ 6 files changed, 395 insertions(+) create mode 100644 OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs create mode 100644 OpenKh.Kh2/SystemData/Pref/Fmab.cs create mode 100644 OpenKh.Kh2/SystemData/Pref/Magi.cs create mode 100644 OpenKh.Kh2/SystemData/Pref/Plyr.cs create mode 100644 OpenKh.Kh2/SystemData/Pref/Prty.cs create mode 100644 OpenKh.Kh2/SystemData/Pref/Sstm.cs diff --git a/OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs b/OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs new file mode 100644 index 000000000..859620a5c --- /dev/null +++ b/OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2 +{ + public class BaseTableOffsets + where T : class + { + [Data] public int Count { get; set; } + + public static List Read(Stream stream) + { + using (var reader = new BinaryReader(stream, System.Text.Encoding.Default, true)) + { + var header = new BaseTableOffsets + { + Count = reader.ReadInt32() + }; + + var offsets = new int[header.Count]; + for (int i = 0; i < header.Count; i++) + { + offsets[i] = reader.ReadInt32(); + } + + var items = new List(); + foreach (var offset in offsets) + { + stream.Position = offset; + items.Add(BinaryMapping.ReadObject(stream)); + } + return items; + } + } + + public static void Write(Stream stream, IEnumerable items) + { + using (var writer = new BinaryWriter(stream, System.Text.Encoding.Default, true)) + { + var itemList = items as IList ?? items.ToList(); + var offsets = new List(); + + var header = new BaseTableOffsets + { + Count = itemList.Count + }; + writer.Write(header.Count); + + var offsetPosition = stream.Position; + for (int i = 0; i < itemList.Count; i++) + { + writer.Write(0); // Placeholder for offset + } + + for (int i = 0; i < itemList.Count; i++) + { + offsets.Add((int)stream.Position); + BinaryMapping.WriteObject(stream, itemList[i]); + } + + var currentPosition = stream.Position; + stream.Position = offsetPosition; + foreach (var offset in offsets) + { + writer.Write(offset); + } + stream.Position = currentPosition; + } + } + } +} diff --git a/OpenKh.Kh2/SystemData/Pref/Fmab.cs b/OpenKh.Kh2/SystemData/Pref/Fmab.cs new file mode 100644 index 000000000..29c8eda26 --- /dev/null +++ b/OpenKh.Kh2/SystemData/Pref/Fmab.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.SystemData +{ + public class Fmab + { + //public int Index { get; set; } + [Data] public float HighJumpHeight { get; set; } + [Data] public float AirDodgeHeight { get; set; } + [Data] public float AirDodgeSpeed { get; set; } + [Data] public float AirSlideTime { get; set; } + [Data] public float AirSlideSpeed { get; set; } + [Data] public float AirSlideBrake { get; set; } + [Data] public float AirSlideStopBrake { get; set; } + [Data] public float AirSlideInvulnerableFrames { get; set; } + [Data] public float GlideSpeed { get; set; } + [Data] public float GlideFallRatio { get; set; } + [Data] public float GlideFallHeight { get; set; } + [Data] public float GlideFallMax { get; set; } + [Data] public float GlideAcceleration { get; set; } + [Data] public float GlideStartHeight { get; set; } + [Data] public float GlideEndHeight { get; set; } + [Data] public float GlideTurnSpeed { get; set; } + [Data] public float DodgeRollInvulnerableFrames { get; set; } + + public class FmabEntries + { + [Data] public Dictionary Entries { get; set; } + } + + public static List Read(Stream stream) => BaseTableOffsets.Read(stream); + + public static void Write(Stream stream, IEnumerable entries) => BaseTableOffsets.Write(stream, entries); + } +} diff --git a/OpenKh.Kh2/SystemData/Pref/Magi.cs b/OpenKh.Kh2/SystemData/Pref/Magi.cs new file mode 100644 index 000000000..7406150e7 --- /dev/null +++ b/OpenKh.Kh2/SystemData/Pref/Magi.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.SystemData +{ + public class Magi + { + //[Data] public uint Id { get; set; } + [Data] public float FireRadius { get; set; } + [Data] public float FireHeight { get; set; } + [Data] public float FireTime { get; set; } + [Data] public float BlizzardFadeTime { get; set; } + [Data] public float BlizzardTime { get; set; } + [Data] public float BlizzardSpeed { get; set; } + [Data] public float BlizzardRadius { get; set; } + [Data] public float BlizzardHitBack { get; set; } + [Data] public float ThunderNoTargetDistance { get; set; } + [Data] public float ThunderBorderHeight { get; set; } + [Data] public float ThunderCheckHeight { get; set; } + [Data] public float ThunderBurstRadius { get; set; } + [Data] public float ThunderHeight { get; set; } + [Data] public float ThunderRadius { get; set; } + [Data] public float ThunderAttackWait { get; set; } + [Data] public float ThunderTime { get; set; } + [Data] public float CureRange { get; set; } + [Data] public float MagnetMinYOffset { get; set; } + [Data] public float MagnetLargeTime { get; set; } + [Data] public float MagnetStayTime { get; set; } + [Data] public float MagnetSmallTime { get; set; } + [Data] public float MagnetRadius { get; set; } + [Data] public float ReflectRadius { get; set; } + [Data] public float ReflectLaserTime { get; set; } + [Data] public float ReflectFinishTime { get; set; } + [Data] public float ReflectLv1Radius { get; set; } + [Data] public float ReflectLv1Height { get; set; } + [Data] public int ReflectLv2Count { get; set; } + [Data] public float ReflectLv2Radius { get; set; } + [Data] public float ReflectLv2Height { get; set; } + [Data] public int ReflectLv3Count { get; set; } + [Data] public float ReflectLv3Radius { get; set; } + [Data] public float ReflectLv3Height { get; set; } + + } +} diff --git a/OpenKh.Kh2/SystemData/Pref/Plyr.cs b/OpenKh.Kh2/SystemData/Pref/Plyr.cs new file mode 100644 index 000000000..2fa563260 --- /dev/null +++ b/OpenKh.Kh2/SystemData/Pref/Plyr.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.SystemData +{ + public class Plyr + { + [Data] public float AttackYOffset { get; set; } + [Data] public float AttackRadius { get; set; } + [Data] public float AttackMinHeight { get; set; } + [Data] public float AttackMaxHeight { get; set; } + [Data] public float AttackVAngle { get; set; } + [Data] public float AttackVRange { get; set; } + [Data] public float AttackSRange { get; set; } + [Data] public float AttackHAngle { get; set; } + [Data] public float AttackUMinHeight { get; set; } + [Data] public float AttackUMaxHeight { get; set; } + [Data] public float AttackURange { get; set; } + [Data] public float AttackJFront { get; set; } + [Data] public float AttackAirMinHeight { get; set; } + [Data] public float AttackAirMaxHeight { get; set; } + [Data] public float AttackAirBigHeightOffset { get; set; } + [Data] public float AttackUV0 { get; set; } + [Data] public float AttackJV0 { get; set; } + [Data] public float AttackFirstV0 { get; set; } + [Data] public float AttackComboV0 { get; set; } + [Data] public float AttackFinishV0 { get; set; } + [Data] public float BlowRecoveryH { get; set; } + [Data] public float BlowRecoveryV { get; set; } + [Data] public float BlowRecoveryTime { get; set; } + [Data] public float AutoLockonRange { get; set; } + [Data] public float AutoLockonMinHeight { get; set; } + [Data] public float AutoLockonMaxHeight { get; set; } + [Data] public float AutoLockonTime { get; set; } + [Data] public float AutoLockonHeightAdjust { get; set; } + [Data] public float AutoLockonInnerAdjust { get; set; } + } +} diff --git a/OpenKh.Kh2/SystemData/Pref/Prty.cs b/OpenKh.Kh2/SystemData/Pref/Prty.cs new file mode 100644 index 000000000..477805dc1 --- /dev/null +++ b/OpenKh.Kh2/SystemData/Pref/Prty.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.SystemData +{ + public class Prty + { + //Structure: 0x4 bytes for Count, then for Count, offsets pointing to different characters in the file. + //0x00000000 seems to be some filler data? Unknown what purpose it serves. + //Offsets also repeat. This is because the index of an offset is used for the NeoMoveset of a character. + //Unsure why, but worlds with different costumes (NM, WI, TR, etc.) need to use different NeoMoveset values even if they read the exact same values. + //For Sora, NeoMoveset also is linked to the PTYA entry to use. + [Data] public float WalkSpeed { get; set; } + [Data] public float RunSpeed { get; set; } + [Data] public float JumpHeight { get; set; } + [Data] public float TurnSpeed { get; set; } + [Data] public float HangHeight { get; set; } + [Data] public float HangMargin { get; set; } + [Data] public float StunTime { get; set; } + [Data] public float MpChargeTime { get; set; } + [Data] public float UpDownSpeed { get; set; } + [Data] public float DashSpeed { get; set; } + [Data] public float Acceleration { get; set; } + [Data] public float Brake { get; set; } + [Data] public float Subjective { get; set; } + + public class PrtyEntries + { + [Data] public Dictionary Entries { get; set; } + } + public static readonly Dictionary CharacterMap = new Dictionary + { + {"Sora", 0}, + {"ValorForm", 1}, + {"WisdomForm", 2}, + {"MasterForm", 3}, + {"FinalForm", 4}, + {"AntiForm", 5}, + {"SoraLK", 6}, + {"SoraLM", 7}, + {"Donald", 8}, + {"DonaldLK", 9}, + {"DonaldLM", 10}, + {"Goofy", 11}, + {"GoofyLK", 12}, + {"Aladdin", 13}, + {"Auron", 14}, + {"Mulan", 15}, + {"Ping", 16}, + {"Tron", 17}, + {"Mickey", 18}, + {"Beast", 19}, + {"Jack", 20}, + {"Simba", 21}, + {"Sparrow", 22}, + {"Riku", 23}, + {"MagicCarpet", 24}, + {"LightCycle", 25}, + {"SoraDie", 26}, + {"Unknown1", 27}, + {"Unknown2", 28}, + {"GummiShip", 29}, + {"RedRocket", 30}, + {"Neverland", 31}, + {"Session", 32}, + {"LimitForm", 33} + }; + + //Read/Write doesn't currently work. + + public static (List Items, List Offsets) Read(Stream stream) + { + return BaseTableOffsetWithPaddings.ReadWithOffsets(stream); + } + + public static void Write(Stream stream, IEnumerable entries, List originalOffsets) + { + BaseTableOffsetWithPaddings.WriteWithOffsets(stream, entries, originalOffsets); + } + + } +} diff --git a/OpenKh.Kh2/SystemData/Pref/Sstm.cs b/OpenKh.Kh2/SystemData/Pref/Sstm.cs new file mode 100644 index 000000000..110041728 --- /dev/null +++ b/OpenKh.Kh2/SystemData/Pref/Sstm.cs @@ -0,0 +1,118 @@ +using System.Collections.Generic; +using System.IO; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.SystemData +{ + public class Sstm + { + //[Data] public uint Id { get; set; } + [Data] public float CeilingStop { get; set; } + [Data] public float CeilingDisableCommandTime { get; set; } + [Data] public float HangRangeH { get; set; } + [Data] public float HangRangeL { get; set; } + [Data] public float HangRangeXZ { get; set; } + [Data] public float FallMax { get; set; } + [Data] public float BlowBrakeXZ { get; set; } + [Data] public float BlowMinXZ { get; set; } + [Data] public float BlowBrakeUp { get; set; } + [Data] public float BlowUp { get; set; } + [Data] public float BlowSpeed { get; set; } + [Data] public float BlowToHitBack { get; set; } + [Data] public float HitBack { get; set; } + [Data] public float HitBackSmall { get; set; } + [Data] public float HitBackToJump { get; set; } + [Data] public float FlyBlowBrake { get; set; } + [Data] public float FlyBlowStop { get; set; } + [Data] public float FlyBlowUpAdjust { get; set; } + [Data] public float MagicJump { get; set; } + [Data] public float LockOnRange { get; set; } + [Data] public float LockOnReleaseRange { get; set; } + [Data] public float StunRecovery { get; set; } + [Data] public float StunRecoveryHp { get; set; } + [Data] public float StunRelax { get; set; } + [Data] public float DriveEnemy { get; set; } + [Data] public float ChangeTimeEnemy { get; set; } + [Data] public float DriveTime { get; set; } + [Data] public float DriveTimeRelax { get; set; } + [Data] public float ChangeTimeAddRate { get; set; } + [Data] public float ChangeTimeSubRate { get; set; } + [Data] public float MpDriveRate { get; set; } + [Data] public float MpToMpDrive { get; set; } + [Data] public float SummonTimeRelax { get; set; } + [Data] public float SummonPrayTime { get; set; } + [Data] public float SummonPrayTimeSkip { get; set; } + [Data] public int AntiFormDriveCount { get; set; } + [Data] public int AntiFormSubCount { get; set; } + [Data] public float AntiFormDamageRate { get; set; } + [Data] public float FinalFormRate { get; set; } + [Data] public float FinalFormMulRate { get; set; } + [Data] public float FinalFormMaxRate { get; set; } + [Data] public int FinalFormSubCount { get; set; } + [Data] public float AttackDistanceToSpeed { get; set; } + [Data] public float AlCarpetDashInner { get; set; } + [Data] public float AlCarpetDashDelay { get; set; } + [Data] public float AlCarpetDashAcceleration { get; set; } + [Data] public float AlCarpetDashBrake { get; set; } + [Data] public float LkDashDriftInner { get; set; } + [Data] public float LkDashDriftTime { get; set; } + [Data] public float LkDashAccelerationDrift { get; set; } + [Data] public float LkDashAccelerationStop { get; set; } + [Data] public float LkDashDriftSpeed { get; set; } + [Data] public float LkMagicJump { get; set; } + [Data] public float MickeyChargeWait { get; set; } + [Data] public float MickeyDownRate { get; set; } + [Data] public float MickeyMinRate { get; set; } + [Data] public float LmSwimSpeed { get; set; } + [Data] public float LmSwimControl { get; set; } + [Data] public float LmSwimAcceleration { get; set; } + [Data] public float LmDolphinAcceleration { get; set; } + [Data] public float LmDolphinSpeedMax { get; set; } + [Data] public float LmDolphinSpeedMin { get; set; } + [Data] public float LmDolphinSpeedMaxDistance { get; set; } + [Data] public float LmDolphinSpeedMinDistance { get; set; } + [Data] public float LmDolphinRotationMax { get; set; } + [Data] public float LmDolphinRotationDistance { get; set; } + [Data] public float LmDolphinRotationMaxDistance { get; set; } + [Data] public float LmDolphinDistanceToTime { get; set; } + [Data] public float LmDolphinTimeMax { get; set; } + [Data] public float LmDolphinTimeMin { get; set; } + [Data] public float LmDolphinNearSpeed { get; set; } + [Data] public int DriveBerserkAttack { get; set; } + [Data] public float MpHaste { get; set; } + [Data] public float MpHastera { get; set; } + [Data] public float MpHastega { get; set; } + [Data] public float DrawRange { get; set; } + [Data] public int ComboDamageUp { get; set; } + [Data] public int ReactionDamageUp { get; set; } + [Data] public float DamageDrive { get; set; } + [Data] public float DriveBoost { get; set; } + [Data] public float FormBoost { get; set; } + [Data] public float ExpChance { get; set; } + [Data] public int Defender { get; set; } + [Data] public int ElementUp { get; set; } + [Data] public float DamageAspir { get; set; } + [Data] public float HyperHeal { get; set; } + [Data] public float CombinationBoost { get; set; } + [Data] public float PrizeUp { get; set; } + [Data] public float LuckUp { get; set; } + [Data] public int ItemUp { get; set; } + [Data] public float AutoHeal { get; set; } + [Data] public float SummonBoost { get; set; } + [Data] public float DriveConvert { get; set; } + [Data] public float DefenseMaster { get; set; } + [Data] public int DefenseMasterRatio { get; set; } + + public class SstmPatch + { + public Dictionary Properties { get; set; } = new Dictionary(); + } + + //Read/Write doesn't currently work. + public static List Read(Stream stream) => BaseTableSstm.Read(stream); + + public static void Write(Stream stream, IEnumerable entries) => BaseTableSstm.Write(stream, entries); + + } + +} From d9f6023f4467ba01673e233ef0ae3204a9697580 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Mon, 29 Jul 2024 18:21:32 -0400 Subject: [PATCH 03/21] Add listpatch version of Mixdata files. --- OpenKh.Kh2/Mixdata/Cond_LP.cs | 87 ++++++++++++++++++++++++++ OpenKh.Kh2/Mixdata/Leve_LP.cs | 64 ++++++++++++++++++++ OpenKh.Kh2/Mixdata/Reci_LP.cs | 111 ++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 OpenKh.Kh2/Mixdata/Cond_LP.cs create mode 100644 OpenKh.Kh2/Mixdata/Leve_LP.cs create mode 100644 OpenKh.Kh2/Mixdata/Reci_LP.cs diff --git a/OpenKh.Kh2/Mixdata/Cond_LP.cs b/OpenKh.Kh2/Mixdata/Cond_LP.cs new file mode 100644 index 000000000..9fed483b9 --- /dev/null +++ b/OpenKh.Kh2/Mixdata/Cond_LP.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.Mixdata +{ + public class CondLP + //LP for Listpatch version of the file. Currently the BaseMixdata Read/Write seems to not work with reading/writing these files? Tests seemed to fail. + //LP versions of the files exist so as to not ruin the original, cleaner version of the code, however if the original version of the code is used for the Listpatch then current mods wouldn't break. + { + private const int MagicCode = 0x4F43494D; + + public enum RewardType + { + Item = 0, + ShopUpgrade = 1 + } + + public enum CollectionType + { + Stack = 0, + Unique = 1 + } + + [Data] public ushort TextId { get; set; } + [Data] public short Reward { get; set; } //either item from 03system or shop upgrades + [Data] public RewardType Type { get; set; } + [Data] public byte MaterialType { get; set; } + [Data] public byte MaterialRank { get; set; } + [Data] public CollectionType ItemCollect { get; set; } + [Data] public short Count { get; set; } + [Data] public short ShopUnlock { get; set; } + + public static List Read(Stream stream) + { + var condLPList = new List(); + using (var reader = new BinaryReader(stream, System.Text.Encoding.Default, true)) + { + int magicCode = reader.ReadInt32(); + int version = reader.ReadInt32(); + int count = reader.ReadInt32(); + reader.ReadInt32(); // Skip padding + + for (int i = 0; i < count; i++) + { + var condLP = new CondLP + { + TextId = reader.ReadUInt16(), + Reward = reader.ReadInt16(), + Type = (CondLP.RewardType)reader.ReadByte(), + MaterialType = reader.ReadByte(), + MaterialRank = reader.ReadByte(), + ItemCollect = (CondLP.CollectionType)reader.ReadByte(), + Count = reader.ReadInt16(), + ShopUnlock = reader.ReadInt16() + }; + condLPList.Add(condLP); + } + } + return condLPList; + } + public static void Write(Stream stream, List condLPList) + { + stream.Position = 0; + using (var writer = new BinaryWriter(stream, System.Text.Encoding.Default, true)) + { + writer.Write(MagicCode); + writer.Write(2); // Version number, hardcoded for example + writer.Write(condLPList.Count); + writer.Write(0); // Padding + + foreach (var condLP in condLPList) + { + writer.Write(condLP.TextId); + writer.Write(condLP.Reward); + writer.Write((byte)condLP.Type); + writer.Write(condLP.MaterialType); + writer.Write(condLP.MaterialRank); + writer.Write((byte)condLP.ItemCollect); + writer.Write(condLP.Count); + writer.Write(condLP.ShopUnlock); + } + } + } + } +} diff --git a/OpenKh.Kh2/Mixdata/Leve_LP.cs b/OpenKh.Kh2/Mixdata/Leve_LP.cs new file mode 100644 index 000000000..5ef327aa5 --- /dev/null +++ b/OpenKh.Kh2/Mixdata/Leve_LP.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.Mixdata +{ + public class LeveLP + { + private const int MagicCode = 0x564C494D; + + [Data] public ushort Title { get; set; } //Names from Sys.bar like "Amateur Moogle" + [Data] public ushort Stat { get; set; } //Another text ID. + [Data] public short Enable { get; set; } + [Data] public ushort Padding { get; set; } + [Data] public int Exp { get; set; } + + public static List Read(Stream stream) + { + var leveLPList = new List(); + using (var reader = new BinaryReader(stream, System.Text.Encoding.Default, true)) + { + int magicCode = reader.ReadInt32(); + int version = reader.ReadInt32(); + int count = reader.ReadInt32(); + reader.ReadInt32(); // Skip padding + + for (int i = 0; i < count; i++) + { + var leveLP = new LeveLP + { + Title = reader.ReadUInt16(), + Stat = reader.ReadUInt16(), + Enable = reader.ReadInt16(), + Padding = reader.ReadUInt16(), + Exp = reader.ReadInt32() + }; + leveLPList.Add(leveLP); + } + } + return leveLPList; + } + public static void Write(Stream stream, List leveLPList) + { + stream.Position = 0; + using (var writer = new BinaryWriter(stream, System.Text.Encoding.Default, true)) + { + writer.Write(MagicCode); + writer.Write(2); // Version number, hardcoded for example + writer.Write(leveLPList.Count); + writer.Write(0); // Padding + + foreach (var leveLP in leveLPList) + { + writer.Write(leveLP.Title); + writer.Write(leveLP.Stat); + writer.Write(leveLP.Enable); + writer.Write(leveLP.Padding); + writer.Write(leveLP.Exp); + } + } + } + } +} diff --git a/OpenKh.Kh2/Mixdata/Reci_LP.cs b/OpenKh.Kh2/Mixdata/Reci_LP.cs new file mode 100644 index 000000000..a712c51ee --- /dev/null +++ b/OpenKh.Kh2/Mixdata/Reci_LP.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xe.BinaryMapper; + +namespace OpenKh.Kh2.Mixdata +{ + public class ReciLP + { + public const int MagicCode = 0x4552494D; + + public enum UnlockType + { + Recipe = 0, + FreeDevelopment1 = 1, + FreeDevelopment2 = 2, + FreeDevelopment3 = 3, + } + + [Data] public ushort Id { get; set; } //03system -> item + [Data] public UnlockType Unlock { get; set; } + [Data] public byte Rank { get; set; } + [Data] public ushort Item { get; set; } + [Data] public ushort UpgradedItem { get; set; } + [Data] public ushort Ingredient1 { get; set; } + [Data] public ushort Ingredient1Amount { get; set; } + [Data] public ushort Ingredient2 { get; set; } + [Data] public ushort Ingredient2Amount { get; set; } + [Data] public ushort Ingredient3 { get; set; } + [Data] public ushort Ingredient3Amount { get; set; } + [Data] public ushort Ingredient4 { get; set; } + [Data] public ushort Ingredient4Amount { get; set; } + [Data] public ushort Ingredient5 { get; set; } + [Data] public ushort Ingredient5Amount { get; set; } + [Data] public ushort Ingredient6 { get; set; } + [Data] public ushort Ingredient6Amount { get; set; } + + public static List Read(Stream stream) + { + var recipes = new List(); + using (var reader = new BinaryReader(stream, System.Text.Encoding.Default, true)) + { + int magicCode = reader.ReadInt32(); + int version = reader.ReadInt32(); + int count = reader.ReadInt32(); + reader.ReadInt32(); // Skip padding + + for (int i = 0; i < count; i++) + { + var recipe = new ReciLP + { + Id = reader.ReadUInt16(), + Unlock = (UnlockType)reader.ReadByte(), + Rank = reader.ReadByte(), + Item = reader.ReadUInt16(), + UpgradedItem = reader.ReadUInt16(), + Ingredient1 = reader.ReadUInt16(), + Ingredient1Amount = reader.ReadUInt16(), + Ingredient2 = reader.ReadUInt16(), + Ingredient2Amount = reader.ReadUInt16(), + Ingredient3 = reader.ReadUInt16(), + Ingredient3Amount = reader.ReadUInt16(), + Ingredient4 = reader.ReadUInt16(), + Ingredient4Amount = reader.ReadUInt16(), + Ingredient5 = reader.ReadUInt16(), + Ingredient5Amount = reader.ReadUInt16(), + Ingredient6 = reader.ReadUInt16(), + Ingredient6Amount = reader.ReadUInt16() + }; + recipes.Add(recipe); + } + } + return recipes; + } + + public static void Write(Stream stream, List recipes) + { + stream.Position = 0; + using (var writer = new BinaryWriter(stream, System.Text.Encoding.Default, true)) + { + writer.Write(MagicCode); + writer.Write(2); // Version number, hardcoded for example + writer.Write(recipes.Count); + writer.Write(0); // Padding + + foreach (var recipe in recipes) + { + writer.Write(recipe.Id); + writer.Write((byte)recipe.Unlock); + writer.Write(recipe.Rank); + writer.Write(recipe.Item); + writer.Write(recipe.UpgradedItem); + writer.Write(recipe.Ingredient1); + writer.Write(recipe.Ingredient1Amount); + writer.Write(recipe.Ingredient2); + writer.Write(recipe.Ingredient2Amount); + writer.Write(recipe.Ingredient3); + writer.Write(recipe.Ingredient3Amount); + writer.Write(recipe.Ingredient4); + writer.Write(recipe.Ingredient4Amount); + writer.Write(recipe.Ingredient5); + writer.Write(recipe.Ingredient5Amount); + writer.Write(recipe.Ingredient6); + writer.Write(recipe.Ingredient6Amount); + } + } + } + + + } +} From d7c9761e0e3b89851646f329f9b9636e285e0e9f Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Mon, 29 Jul 2024 18:22:41 -0400 Subject: [PATCH 04/21] Add new listpatches, fix failure to open program with invalid mod.yml --- OpenKh.Patcher/Metadata.cs | 69 ++- OpenKh.Patcher/PatcherProcessor.cs | 718 +++++++++++++++++++++++++++-- 2 files changed, 751 insertions(+), 36 deletions(-) diff --git a/OpenKh.Patcher/Metadata.cs b/OpenKh.Patcher/Metadata.cs index c1da57177..450995150 100644 --- a/OpenKh.Patcher/Metadata.cs +++ b/OpenKh.Patcher/Metadata.cs @@ -1,6 +1,9 @@ using OpenKh.Kh2; +using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.Text.RegularExpressions; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -29,12 +32,72 @@ public class Dependency .Build(); private static readonly ISerializer serializer = new SerializerBuilder() + //.JsonCompatible() //Experimental. May allow mod.ymls/listpatches to be in json formatting, and mod manager will accept it? .IgnoreFields() .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); + .Build(); + + //Old Metadata Read. Two lines but isn't comprehensive enough. + // public static Metadata Read(Stream stream) => + // deserializer.Deserialize(new StreamReader(stream)); + + public static Metadata Read(Stream stream) + { + try + { + return deserializer.Deserialize(new StreamReader(stream)); //Simplifed way of doing this. Cuts out the "deserializer" redundancies and variable for StreamReader(stream)) + } + + //OLD TRY METHOD: Seems to use a lot of redundant code. + //try + //{ + // var deserializer = new DeserializerBuilder() + // .WithNamingConvention(CamelCaseNamingConvention.Instance) + // .Build(); + + // using (var reader = new StreamReader(stream)) + // { + // var metadata = deserializer.Deserialize(reader); + // return metadata; + // } + // } + + + //Use this to handle YML errors. Prevents crashing upon immediate startup. + catch (YamlDotNet.Core.YamlException ex) + { + // Handle YAML parsing errors + Debug.WriteLine($"Error deserializing YAML: {ex.Message}"); + + string originalTitle = string.Empty; + + // Extract title using regex + stream.Position = 0; // Reset stream position + using (var reader = new StreamReader(stream)) + { + string yamlContent = reader.ReadToEnd(); + var match = Regex.Match(yamlContent, @"(?<=title:).*"); + if (match.Success) + { + originalTitle = match.Value.Trim(); + } + } + + var metadata = new Metadata + { + Title = $"{originalTitle}* \nMOD YML ERROR DETECTED: CHECK FORMATTING" + }; - public static Metadata Read(Stream stream) => - deserializer.Deserialize(new StreamReader(stream)); + return metadata; // Return modified metadata indicating failure + } + catch (Exception ex) + { + // Handle other unexpected errors + Debug.WriteLine($"Unexpected error: {ex.Message}"); + throw; // Rethrow other exceptions for further investigation + } + } + public void Write(Stream stream) { using (var writer = new StreamWriter(stream)) diff --git a/OpenKh.Patcher/PatcherProcessor.cs b/OpenKh.Patcher/PatcherProcessor.cs index 913e278e2..2db71f6dc 100644 --- a/OpenKh.Patcher/PatcherProcessor.cs +++ b/OpenKh.Patcher/PatcherProcessor.cs @@ -11,6 +11,12 @@ using System.IO; using System.Linq; using YamlDotNet.Serialization; +using Xe.BinaryMapper; +using System.Collections; +using Antlr4.Runtime.Dfa; +using OpenKh.Kh2.Mixdata; +using System.Diagnostics; + namespace OpenKh.Patcher { @@ -81,11 +87,11 @@ public void Patch(string originalAssets, string outputDir, string modFilePath) public void Patch(string originalAssets, string outputDir, Metadata metadata, string modBasePath, int platform = 1, bool fastMode = false, IDictionary packageMap = null, string LaunchGame = null) { - + var context = new Context(metadata, originalAssets, modBasePath, outputDir); try { - + if (metadata.Assets == null) throw new Exception("No assets found."); if (metadata.Game != null && GamesList.Contains(metadata.Game.ToLower()) && metadata.Game.ToLower() != LaunchGame.ToLower()) @@ -116,8 +122,8 @@ public void Patch(string originalAssets, string outputDir, Metadata metadata, st _packageFile = assetFile.Package != null && !fastMode ? assetFile.Package : "bbs_first"; break; case "Recom": - if (assetFile!= null) - _packageFile = "Recom"; + if (assetFile != null) + _packageFile = "Recom"; break; default: _packageFile = assetFile.Package != null && !fastMode ? assetFile.Package : "kh2_first"; @@ -180,15 +186,19 @@ public void Patch(string originalAssets, string outputDir, Metadata metadata, st context.EnsureDirectoryExists(dstFile); + //Update: Prevent from copying a blank file should it not exist. try { - context.CopyOriginalFile(name, dstFile); + if (((assetFile.Type == "internal" || assetFile.Source[0].Type == "internal") && File.Exists(context.GetOriginalAssetPath(assetFile.Source[0].Name))) || assetFile.Type != "internal" && assetFile.Source[0].Type != "internal") + { + context.CopyOriginalFile(name, dstFile); - using var _stream = File.Open(dstFile, FileMode.OpenOrCreate, FileAccess.ReadWrite); - PatchFile(context, assetFile, _stream); + using var _stream = File.Open(dstFile, FileMode.OpenOrCreate, FileAccess.ReadWrite); + PatchFile(context, assetFile, _stream); - _stream.Close(); - _stream.Dispose(); + _stream.Close(); + _stream.Dispose(); + } } catch (IOException) { } @@ -243,6 +253,9 @@ private static void PatchFile(Context context, AssetFile assetFile, Stream strea case "listpatch": PatchList(context, assetFile.Source, stream); break; + case "synthpatch": + PatchSynth(context, assetFile.Source, stream); + break; default: Log.Warn($"Method '{assetFile.Method}' not recognized for '{assetFile.Name}'. Falling back to 'copy'"); CopyFile(context, assetFile, stream); @@ -254,17 +267,25 @@ private static void PatchFile(Context context, AssetFile assetFile, Stream strea + //New goal for this: Prevent it from stopping the mod application completely. + //Instead, just write an error to the Log with Log.Warn, AND ensure it copies nothing. + + //Below: Reverted CopyFile back to Vanilla. Seems to have caused some issues. private static void CopyFile(Context context, AssetFile assetFile, Stream stream) { if (assetFile.Source == null || assetFile.Source.Count == 0) throw new Exception($"File '{assetFile.Name}' does not contain any source"); - string srcFile; + string srcFile; if (assetFile.Source[0].Type == "internal") { srcFile = context.GetOriginalAssetPath(assetFile.Source[0].Name); } + if (assetFile.Source[0].Type == "external") + { + srcFile = context.GetDestinationPath(assetFile.Source[0].Name); + } else { srcFile = context.GetSourceModAssetPath(assetFile.Source[0].Name); @@ -275,6 +296,10 @@ private static void CopyFile(Context context, AssetFile assetFile, Stream stream srcStream.CopyTo(stream); } + + + + //Binarc Update: Specify by Name OR Index. Some files in BARS may have the same name but different indexes, and you want to patch a later index only. private static void PatchBinarc(Context context, AssetFile assetFile, Stream stream) { var binarc = Bar.IsValid(stream) ? Bar.Read(stream) : @@ -288,7 +313,20 @@ private static void PatchBinarc(Context context, AssetFile assetFile, Stream str if (!Enum.TryParse(file.Type, true, out var barEntryType)) throw new Exception($"BinArc type {file.Type} not recognized"); - var entry = binarc.FirstOrDefault(x => x.Name == file.Name && x.Type == barEntryType); + Bar.Entry entry = null; + + // Check if the name is specified + if (!string.IsNullOrEmpty(file.Name)) + { + entry = binarc.FirstOrDefault(x => x.Name == file.Name && x.Type == barEntryType); + } + // If name is not specified but index is + else if (file.Index >= 0 && file.Index < binarc.Count) + { + entry = binarc[file.Index]; + } + + // If entry is not found by name or index, create a new one if (entry == null) { entry = new Bar.Entry @@ -308,6 +346,7 @@ private static void PatchBinarc(Context context, AssetFile assetFile, Stream str entry.Stream?.Dispose(); } + private static Imgd CreateImageImd(Context context, AssetFile source) { var srcFile = context.GetSourceModAssetPath(source.Name); @@ -429,7 +468,7 @@ private static void PatchBdscript(Context context, AssetFile assetFile, Stream s if (!File.Exists(srcFile)) throw new FileNotFoundException($"The mod does not contain the file {scriptName}", srcFile); - + var programsInput = File.ReadAllText(context.GetSourceModAssetPath(scriptName)); var ascii = BdxAsciiModel.ParseText(programsInput); var decoder = new BdxEncoder( @@ -457,15 +496,34 @@ private static void PatchSpawnPoint(Context context, AssetFile assetFile, Stream if (!File.Exists(srcFile)) throw new FileNotFoundException($"The mod does not contain the file {assetFile.Source[0].Name}", srcFile); - var spawnPoint = Helpers.YamlDeserialize>(File.ReadAllText(srcFile)); + var spawnPoint = OpenKh.Common.Helpers.YamlDeserialize>(File.ReadAllText(srcFile)); Kh2.Ard.SpawnPoint.Write(stream.SetPosition(0), spawnPoint); } private static readonly Dictionary characterMap = new Dictionary(){ - { "Sora", 1 }, { "Donald", 2 }, { "Goofy", 3 }, { "Mickey", 4 }, { "Auron", 5 }, { "PingMulan",6 }, { "Aladdin", 7 }, { "Sparrow", 8 }, { "Beast", 9 }, { "Jack", 10 }, { "Simba", 11 }, { "Tron", 12 }, { "Riku", 13 }, { "Roxas", 14}, {"Ping", 15} + { "Sora", 1 }, { "Donald", 2 }, { "Goofy", 3 }, { "Mickey", 4 }, { "Auron", 5 }, { "PingMulan",6 }, { "Aladdin", 7 }, { "Sparrow", 8 }, { "Beast", 9 }, { "Jack", 10 }, { "Simba", 11 }, { "Tron", 12 }, { "Riku", 13 }, { "Roxas", 14}, {"Ping", 15} }; + private static readonly Dictionary weaponCharacterMap = new Dictionary(){ + { "Sora", 0 }, { "SoraNM", 1 }, { "Donald", 2 }, { "DonaldNM", 3 }, { "Goofy", 4 }, { "Goofy2",5 }, { "GoofyNM", 6 }, { "Aladdin", 7 }, { "Auron", 8 }, { "MulanPing", 9 }, { "Tron", 10 }, { "Mickey", 11 }, { "Beast", 12 }, { "Jack", 13}, {"Simba", 14}, { "Sparrow", 15 }, { "Riku", 16 }, { "SparrowHU", 17 }, { "SoraTR", 18}, {"SoraWI", 19}, { "DonaldTR", 20 }, { "DonaldWI", 21 }, { "GoofyTR", 22}, {"GoofyWI", 23} +}; + + + + private static readonly Dictionary worldIndexMapSB = new Dictionary() { + { 0, "ZZ" }, { 1, "End Of Sea" }, { 2, "TwilightTown" }, { 3, "DestinyIsland" }, { 4, "HollowBastion" }, { 5, "BeastsCastle" }, { 6, "OlympusColiseum" }, { 7, "Agrabah" }, { 8, "TheLandOfDragons" }, { 9, "100AcreWood" }, { 10, "PrideLand" }, { 11, "Atlantica" }, { 12, "DisneyCastle" }, { 13, "TimelessRiver" }, { 14, "HalloweenTown" }, { 15, "WorldMap" }, { 16, "PortRoyal" }, { 17, "SpaceParanoids" }, { 18, "TheWorldThatNeverWas" } + }; + + + private static readonly Dictionary worldIndexMap = new Dictionary(StringComparer.OrdinalIgnoreCase){ + { "worldzz", 0 }, { "endofsea", 1 }, { "twilighttown", 2 }, { "destinyisland", 3 }, { "hollowbastion", 4 }, { "beastscastle", 5 }, { "olympuscoliseum", 6 }, { "agrabah", 7 }, { "thelandofdragons", 8 }, { "100acrewood", 9 }, { "prideland", 10 }, { "atlantica", 11 }, { "disneycastle", 12 }, { "timelessriver", 13}, {"halloweentown", 14}, { "worldmap", 15 }, { "portroyal", 16 }, { "spaceparanoids", 17 }, { "theworldthatneverwas", 18 } + }; + + + + + private static readonly IDeserializer deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); private static void PatchList(Context context, List sources, Stream stream) @@ -500,6 +558,7 @@ private static void PatchList(Context context, List sources, Stream s Kh2.SystemData.Trsr.Write(stream.SetPosition(0), trsrList.Values); break; + //Updated. Now merges fields. Likely better ways of doing this that don't bloat the code. case "item": var itemList = Kh2.SystemData.Item.Read(stream); var moddedItem = deserializer.Deserialize(sourceText); @@ -536,6 +595,7 @@ private static void PatchList(Context context, List sources, Stream s itemList.Write(stream.SetPosition(0)); break; + case "fmlv": var formRaw = Kh2.Battle.Fmlv.Read(stream).ToList(); var formList = new Dictionary>(); @@ -615,11 +675,25 @@ private static void PatchList(Context context, List sources, Stream s case "atkp": var atkpList = Kh2.Battle.Atkp.Read(stream); var moddedAtkp = deserializer.Deserialize>(sourceText); + foreach (var attack in moddedAtkp) { - var oldAtkp = atkpList.First(x => x.Id == attack.Id && x.SubId == attack.SubId && x.Switch == attack.Switch); - atkpList[atkpList.IndexOf(oldAtkp)] = attack; + //Same general template used for cmd, enmp, and przt. + // Check if the attack exists in atkpList based on Id, SubId, and Switch + var existingAttack = atkpList.FirstOrDefault(x => x.Id == attack.Id && x.SubId == attack.SubId && x.Switch == attack.Switch); + + if (existingAttack != null) + { + // Update existing attack in atkpList + atkpList[atkpList.IndexOf(existingAttack)] = attack; + } + else + { + // Add the attack to atkpList if it doesn't exist + atkpList.Add(attack); + } } + Kh2.Battle.Atkp.Write(stream.SetPosition(0), atkpList); break; @@ -646,70 +720,648 @@ private static void PatchList(Context context, List sources, Stream s foreach (var plrp in moddedPlrp) { var oldPlrp = plrpList.First(x => x.Character == plrp.Character && x.Id == plrp.Id); - plrpList[plrpList.IndexOf(oldPlrp)] = plrp; + if (oldPlrp != null) + { + plrpList[plrpList.IndexOf(oldPlrp)] = plrp; + } + else + { + plrpList.Add(plrp); + } } Kh2.Battle.Plrp.Write(stream.SetPosition(0), plrpList); break; case "cmd": - var cmdList = Kh2.SystemData.Cmd.Read(stream); - var moddedCmd = deserializer.Deserialize>(sourceText); - foreach (var commands in moddedCmd) + var cmdList = Kh2.SystemData.Cmd.Read(stream); + var moddedCmd = deserializer.Deserialize>(sourceText); + + foreach (var commands in moddedCmd) { - var oldCommands = cmdList.First(x => x.Id == commands.Id && x.Id == commands.Id); - cmdList[cmdList.IndexOf(oldCommands)] = commands; + var existingCommand = cmdList.FirstOrDefault(x => x.Id == commands.Id); + + if (existingCommand != null) + { + cmdList[cmdList.IndexOf(existingCommand)] = commands; + } + else + { + cmdList.Add(commands); + } } + Kh2.SystemData.Cmd.Write(stream.SetPosition(0), cmdList); break; + case "localset": + var localList = Kh2.Localset.Read(stream); + var moddedLocal = deserializer.Deserialize>(sourceText); + + foreach (var set in moddedLocal) + { + var existingSet = localList.FirstOrDefault(x => x.ProgramId == set.ProgramId); + + if (existingSet != null) + { + localList[localList.IndexOf(existingSet)] = set; + } + else + { + localList.Add(set); + } + } + + Kh2.Localset.Write(stream.SetPosition(0), localList); + break; + + case "jigsaw": + var jigsawList = Kh2.Jigsaw.Read(stream); + var moddedJigsaw = deserializer.Deserialize>(sourceText); + + foreach (var piece in moddedJigsaw) + { + //Allow variations of capitalizations with World spellings; can't handle an extra space unfortunately, making it inconsistent with Arif. + //Can just use ID's for worlds, though. + if (worldIndexMap.TryGetValue(piece.World.ToString().Replace(" ", "").ToLower(), out var worldValue)) + { + piece.World = (Kh2.Jigsaw.WorldList)worldValue; + } + + var existingPiece = jigsawList.FirstOrDefault(x => x.Picture == piece.Picture && x.Part == piece.Part); //Identify a puzzle by its Picture+Part. + + if (existingPiece != null) + { + jigsawList[jigsawList.IndexOf(existingPiece)] = piece; + } + else + { + jigsawList.Add(piece); + } + } + + Kh2.Jigsaw.Write(stream.SetPosition(0), jigsawList); + break; + + + case "enmp": var enmpList = Kh2.Battle.Enmp.Read(stream); var moddedEnmp = deserializer.Deserialize>(sourceText); + foreach (var enmp in moddedEnmp) { - var oldEnmp = enmpList.First(x => x.Id == enmp.Id); - enmpList[enmpList.IndexOf(oldEnmp)] = enmp; + var existingEnmp = enmpList.FirstOrDefault(x => x.Id == enmp.Id); + + if (existingEnmp != null) + { + enmpList[enmpList.IndexOf(existingEnmp)] = enmp; + } + else + { + enmpList.Add(enmp); + } } + Kh2.Battle.Enmp.Write(stream.SetPosition(0), enmpList); break; + case "sklt": var skltList = Kh2.SystemData.Sklt.Read(stream); var moddedSklt = deserializer.Deserialize>(sourceText); + foreach (var sklt in moddedSklt) { - var oldSklt = skltList.First(x => x.CharacterId == sklt.CharacterId); - skltList[skltList.IndexOf(oldSklt)] = sklt; + var existingSklt = skltList.FirstOrDefault(x => x.CharacterId == sklt.CharacterId); + + if (existingSklt != null) + { + skltList[skltList.IndexOf(existingSklt)] = sklt; + } + else + { + skltList.Add(sklt); + } } + Kh2.SystemData.Sklt.Write(stream.SetPosition(0), skltList); break; - + case "przt": var prztList = Kh2.Battle.Przt.Read(stream); var moddedPrzt = deserializer.Deserialize>(sourceText); + foreach (var przt in moddedPrzt) { - var oldPrzt = prztList.First(x => x.Id == przt.Id); - prztList[prztList.IndexOf(oldPrzt)] = przt; + var existingPrzt = prztList.FirstOrDefault(x => x.Id == przt.Id); + + if (existingPrzt != null) + { + prztList[prztList.IndexOf(existingPrzt)] = przt; + } + else + { + prztList.Add(przt); + } } Kh2.Battle.Przt.Write(stream.SetPosition(0), prztList); break; case "magc": - var magcList = Kh2.Battle.Magc.Read(stream); - var moddedMagc = deserializer.Deserialize>(sourceText); + var magcList = Kh2.Battle.Magc.Read(stream); + var moddedMagc = deserializer.Deserialize>(sourceText); foreach (var magc in moddedMagc) { - var oldMagc = magcList.First(x => x.Id == magc.Id && x.Level == magc.Level); - magcList[magcList.IndexOf(oldMagc)] = magc; + var existingMagc = magcList.First(x => x.Id == magc.Id && x.Level == magc.Level); + if (existingMagc != null) + { + //existingMagc = MergeHelper.Merge(existingMagc, magc); + magcList[magcList.IndexOf(existingMagc)] = magc; + } + else + { + magcList.Add(magc); + } } Kh2.Battle.Magc.Write(stream.SetPosition(0), magcList); break; + case "btlv": + var btlvList = Kh2.Battle.Btlv.Read(stream); + var moddedBtlv = deserializer.Deserialize>(sourceText); + + foreach (var btlv in moddedBtlv) + { + var existingBtlv = btlvList.FirstOrDefault(x => x.Id == btlv.Id); + + if (existingBtlv != null) + { + btlvList[btlvList.IndexOf(existingBtlv)] = btlv; + } + else + { + btlvList.Add(btlv); + } + } + + Kh2.Battle.Btlv.Write(stream.SetPosition(0), btlvList); + break; + + case "vtbl": + var vtblList = Kh2.Battle.Vtbl.Read(stream); + var moddedVtbl = deserializer.Deserialize>(sourceText); + + foreach (var vtbl in moddedVtbl) + { + var existingVtbl = vtblList.FirstOrDefault(x => x.Id == vtbl.Id && x.CharacterId == vtbl.CharacterId); + //Search for CharacterID & "Action" ID. + if (existingVtbl != null) + { + vtblList[vtblList.IndexOf(existingVtbl)] = vtbl; + + } + else + { + vtblList.Add(vtbl); + } + } + + Kh2.Battle.Vtbl.Write(stream.SetPosition(0), vtblList); + break; + + //Add new limits. ID found -> Continue. + case "limt": + var limtList = Kh2.Battle.Limt.Read(stream); + var moddedLimt = deserializer.Deserialize>(sourceText); + + foreach (var limt in moddedLimt) + { + var existingLimt = limtList.FirstOrDefault(x => x.Id == limt.Id); + + if (existingLimt != null) + { + limtList[limtList.IndexOf(existingLimt)] = limt; + } + else + { + limtList.Add(limt); + } + } + Kh2.Battle.Limt.Write(stream.SetPosition(0), limtList); + break; + + //Listpatch for Arif. Takes a string as an input for the WorldIndex to use, and ints for the roomIndex to edit. + //Strings do not need to worry about capitalization, etc. so long as they have the same characters. + case "arif": + var originalData = Kh2.SystemData.Arif.Read(stream); + var patches = deserializer.Deserialize>>(sourceText); + + foreach (var worldPatch in patches) + { + if (!worldIndexMap.TryGetValue(worldPatch.Key.ToLower().Replace(" ", ""), out var worldIndex)) + { + Log.Warn($"Invalid world index: {worldPatch.Key}"); + } + + if (worldIndex >= 0 && worldIndex < originalData.Count) + { + var worldData = originalData[worldIndex]; + + foreach (var areaPatch in worldPatch.Value) + { + int areaIndex = areaPatch.Key; + var patch = areaPatch.Value; + + // Add new areas. + while (areaIndex >= worldData.Count) + { + worldData.Add(new Kh2.SystemData.Arif + { + Bgms = new Kh2.SystemData.BgmSet[8], + Reserved = new byte[11] + }); + + // Initialize the BgmSet elements within the Bgms array + for (int i = 0; i < 8; i++) + { + worldData[worldData.Count - 1].Bgms[i] = new Kh2.SystemData.BgmSet(); + } + } + // End of adding new areas. + + if (areaIndex >= 0 && areaIndex < worldData.Count) + { + var areaData = worldData[areaIndex]; + //Below: Compares each field to see if it's specified in the YML. + //If yes, update w/ YML value. + //If no, retain original value. + areaData.Flags = patch.Flags != 0 ? patch.Flags : areaData.Flags; + areaData.Reverb = patch.Reverb != 0 ? patch.Reverb : areaData.Reverb; + areaData.SoundEffectBank1 = patch.SoundEffectBank1 != 0 ? patch.SoundEffectBank1 : areaData.SoundEffectBank1; + areaData.SoundEffectBank2 = patch.SoundEffectBank2 != 0 ? patch.SoundEffectBank2 : areaData.SoundEffectBank2; + for (int i = 0; i < patch.Bgms.Length && i < areaData.Bgms.Length; i++) + { + areaData.Bgms[i].BgmField = patch.Bgms[i].BgmField != 0 ? (ushort)patch.Bgms[i].BgmField : areaData.Bgms[i].BgmField; + areaData.Bgms[i].BgmBattle = patch.Bgms[i].BgmBattle != 0 ? (ushort)patch.Bgms[i].BgmBattle : areaData.Bgms[i].BgmBattle; + } + areaData.Voice = patch.Voice != 0 ? patch.Voice : areaData.Voice; + areaData.NavigationMapItem = patch.NavigationMapItem != 0 ? patch.NavigationMapItem : areaData.NavigationMapItem; + areaData.Command = patch.Command != 0 ? patch.Command : areaData.Command; + areaData.Reserved = patch.Reserved != null ? patch.Reserved : areaData.Reserved; + } + } + } + } + + + Kh2.SystemData.Arif.Write(stream.SetPosition(0), originalData); + break; + + case "place": + var originalPlace = Kh2.Places.Read(stream); + var moddedPlace = deserializer.Deserialize>(sourceText); + + foreach (var place in moddedPlace) + { + if (place.Index >= 0 && place.Index < originalPlace.Count) + { + // Update existing entry + originalPlace[place.Index].MessageId = place.MessageId; + originalPlace[place.Index].Padding = place.Padding; + } + else if (place.Index == originalPlace.Count) + { + // Add new entry + originalPlace.Add(new Places { MessageId = place.MessageId, Padding = place.Padding }); + } + else + { + // Expand the list and add the new entry at the specified index + while (originalPlace.Count < place.Index) + { + originalPlace.Add(new Places { MessageId = 0, Padding = 0 }); + } + originalPlace.Add(new Places { MessageId = place.MessageId, Padding = place.Padding }); + } + } + + // Write the updated list back to the stream + Kh2.Places.Write(stream.SetPosition(0), originalPlace); + break; + + case "soundinfo": + var originalSoundInfo = Kh2.Soundinfo.Read(stream); + var moddedSoundInfo = deserializer.Deserialize>(sourceText); + + foreach (var info in moddedSoundInfo) + { + while (originalSoundInfo.Count <= info.Index) + { + originalSoundInfo.Add(new Soundinfo + { + Reverb = 0, + Rate = 0, + EnvironmentWAV = 0, + EnvironmentSEB = 0, + EnvironmentNUMBER = 0, + EnvironmentSPOT = 0, + FootstepWAV = 0, + FootstepSORA = 0, + FootstepDONALD = 0, + FootstepGOOFY = 0, + FootstepWORLDFRIEND = 0, + FootstepOTHER = 0 + }); + } + + originalSoundInfo[info.Index].Reverb = info.Reverb; + originalSoundInfo[info.Index].Rate = info.Rate; + originalSoundInfo[info.Index].EnvironmentWAV = info.EnvironmentWAV; + originalSoundInfo[info.Index].EnvironmentSEB = info.EnvironmentSEB; + originalSoundInfo[info.Index].EnvironmentNUMBER = info.EnvironmentNUMBER; + originalSoundInfo[info.Index].EnvironmentSPOT = info.EnvironmentSPOT; + originalSoundInfo[info.Index].FootstepWAV = info.FootstepWAV; + originalSoundInfo[info.Index].FootstepSORA = info.FootstepSORA; + originalSoundInfo[info.Index].FootstepDONALD = info.FootstepDONALD; + originalSoundInfo[info.Index].FootstepGOOFY = info.FootstepGOOFY; + originalSoundInfo[info.Index].FootstepWORLDFRIEND = info.FootstepWORLDFRIEND; + originalSoundInfo[info.Index].FootstepOTHER = info.FootstepOTHER; + } + + // Write the updated list back to the stream + Kh2.Soundinfo.Write(stream.SetPosition(0), originalSoundInfo); + break; + + + //Ok. Will need to figure this out. + //FIRST GOAL: Simpify as many redundancies as possible in the code; useless if/else statements, etc. + //More traditional version, without any weird string/conversion stuff. + case "libretto": + var originalLibretto = Kh2.Libretto.Read(stream); + + var patches2 = deserializer.Deserialize>(sourceText); + + foreach (var patch in patches2) + { + var definition = originalLibretto.Definitions.FirstOrDefault(def => def.TalkMessageId == patch.TalkMessageId); + + if (definition != null) + { + definition.Unknown = patch.Unknown; + + var contentList = new List(); + foreach (var contentPatch in patch.Contents) + { + contentList.Add(new Libretto.TalkMessageContent + { + Unknown1 = contentPatch.Unknown1, + TextId = contentPatch.TextId + }); + } + originalLibretto.Contents[originalLibretto.Definitions.IndexOf(definition)] = contentList; + } + else + { + var newDefinition = new Libretto.TalkMessageDefinition + { + TalkMessageId = patch.TalkMessageId, + Unknown = patch.Unknown, + ContentPointer = 0 // Will update this later after adding content entries + }; + + originalLibretto.Definitions.Add(newDefinition); + originalLibretto.Count++; + + var contentList = new List(); + foreach (var contentPatch in patch.Contents) + { + contentList.Add(new Libretto.TalkMessageContent + { + Unknown1 = contentPatch.Unknown1, + TextId = contentPatch.TextId + }); + } + originalLibretto.Contents.Add(contentList); + } + } + + // Write the modified data back to the stream + stream.Position = 0; + Kh2.Libretto.Write(stream, originalLibretto); + break; + + + case "memt": + var memt = Kh2.SystemData.Memt.Read(stream); + var memtEntries = memt.Entries.Cast().ToList(); + var memtPatches = deserializer.Deserialize(sourceText); + + if (memtPatches.MemtEntries != null) + { + foreach (var patch in memtPatches.MemtEntries) + { + if (patch.Index < 0) + throw new IndexOutOfRangeException($"Invalid index {patch.Index} for Memt."); + + if (patch.Index >= memtEntries.Count) + { + // Index is beyond current entries, append new entries up to the patch index + while (memtEntries.Count <= patch.Index) + { + memtEntries.Add(new Kh2.SystemData.Memt.EntryFinalMix()); + } + } + + var memtEntry = memtEntries[patch.Index]; + memtEntry.WorldId = patch.WorldId; + memtEntry.CheckStoryFlag = patch.CheckStoryFlag; + memtEntry.CheckStoryFlagNegation = patch.CheckStoryFlagNegation; + memtEntry.Unk06 = patch.Unk06; + memtEntry.Unk08 = patch.Unk08; + memtEntry.Unk0A = patch.Unk0A; + memtEntry.Unk0C = patch.Unk0C; + memtEntry.Unk0E = patch.Unk0E; + memtEntry.Members = patch.Members.ToArray(); + + memtEntries[patch.Index] = memtEntry; + } + + memt.Entries.Clear(); + memt.Entries.AddRange(memtEntries); + + stream.Position = 0; + Kh2.SystemData.Memt.Write(stream, memt); + } + + if (memtPatches.MemberIndices != null) + { + foreach (var patch in memtPatches.MemberIndices) + { + if (patch.Index < 0 || patch.Index >= memt.MemberIndexCollection.Length) + throw new IndexOutOfRangeException($"Invalid MemberIndices index {patch.Index}."); + + var memberIndices = memt.MemberIndexCollection[patch.Index]; + memberIndices.Player = patch.Player; + memberIndices.Friend1 = patch.Friend1; + memberIndices.Friend2 = patch.Friend2; + memberIndices.FriendWorld = patch.FriendWorld; + + memt.MemberIndexCollection[patch.Index] = memberIndices; + } + + stream.Position = 0; + Kh2.SystemData.Memt.Write(stream, memt); + } + break; + + //New: Pref listpatches. More rigid as they're mostly offset-based. Can be updated to eventually support addition though. + case "fmab": + var fmabList = Kh2.SystemData.Fmab.Read(stream); + + var moddedFmab = deserializer.Deserialize(sourceText); + + foreach (var patch in moddedFmab.Entries) + { + if (patch.Key >= 0 && patch.Key < fmabList.Count) + { + fmabList[patch.Key] = patch.Value; + } + } + + stream.SetLength(0); // Clear the stream before writing + Kh2.SystemData.Fmab.Write(stream, fmabList); + break; + + case "prty": + var moddedPrty = deserializer.Deserialize>(sourceText); + + var (prtyList, originalOffsets) = Kh2.SystemData.Prty.Read(stream); + + foreach (var patch in moddedPrty) + { + if (Kh2.SystemData.Prty.CharacterMap.TryGetValue(patch.Key, out var index)) + { + if (index >= 0 && index < prtyList.Count) + { + prtyList[index] = patch.Value; + } + } + } + + stream.SetLength(0); // Clear the stream before writing + Kh2.SystemData.Prty.Write(stream, prtyList, originalOffsets); + + break; + + + case "sstm": + var sstmList = Kh2.SystemData.Sstm.Read(stream); + var moddedSstm = deserializer.Deserialize>(sourceText); + + foreach (var sstm in moddedSstm) + { + var existingSstm = sstmList.FirstOrDefault(x => x.CeilingStop == sstm.CeilingStop); + + if (existingSstm != null) + { + //existingSstm = MergeHelper.Merge(existingSstm, sstm); + sstmList[sstmList.IndexOf(existingSstm)] = sstm; + } + } + Kh2.SystemData.Sstm.Write(stream.SetPosition(0), sstmList); + break; + + default: break; } } + } + private static void PatchSynth(Context context, List sources, Stream stream) + { + foreach (var source in sources) + { + string sourceText = File.ReadAllText(context.GetSourceModAssetPath(source.Name)); + switch (source.Type) + { + case "recipe": + var recipeList = Kh2.Mixdata.ReciLP.Read(stream); // Read existing Reci list + var moddedRecipes = deserializer.Deserialize>(sourceText); // Deserialize modded recipes + + foreach (var moddedRecipe in moddedRecipes) + { + var existingRecipe = recipeList.FirstOrDefault(x => x.Id == moddedRecipe.Id); + + if (existingRecipe != null) + { + // Update existing recipe in the list + recipeList[recipeList.IndexOf(existingRecipe)] = moddedRecipe; + + // Update other properties as needed + } + else + { + // Add new recipe to the list + recipeList.Add(moddedRecipe); + } + } + + // Write the updated recipe list back to the stream + Kh2.Mixdata.ReciLP.Write(stream, recipeList); // Pass IEnumerable + break; + + case "condition": + var conditionList = Kh2.Mixdata.CondLP.Read(stream); // Read existing Reci list + var moddedConditions = deserializer.Deserialize>(sourceText); // Deserialize modded recipes + + foreach (var moddedCondition in moddedConditions) + { + var existingCondition = conditionList.FirstOrDefault(x => x.TextId == moddedCondition.TextId); + + if (existingCondition != null) + { + // Update existing recipe in the list + conditionList[conditionList.IndexOf(existingCondition)] = moddedCondition; + + // Update other properties as needed + } + else + { + // Add new recipe to the list + conditionList.Add(moddedCondition); + } + } + + // Write the updated recipe list back to the stream + Kh2.Mixdata.CondLP.Write(stream, conditionList); // Pass IEnumerable + break; + + case "level": + var levelList = Kh2.Mixdata.LeveLP.Read(stream); // Read existing Reci list + var moddedLevels = deserializer.Deserialize>(sourceText); // Deserialize modded recipes + + foreach (var moddedLevel in moddedLevels) + { + var existingLevel = levelList.FirstOrDefault(x => x.Title == moddedLevel.Title); + + if (existingLevel != null) + { + // Update existing recipe in the list + levelList[levelList.IndexOf(existingLevel)] = moddedLevel; + // Update other properties as needed + } + else + { + // Add new recipe to the list + levelList.Add(moddedLevel); + } + } + + // Write the updated recipe list back to the stream + Kh2.Mixdata.LeveLP.Write(stream, levelList); // Pass IEnumerable + break; + } + } } } } From 03f7e2a61fdc78c0f7d83420f5ecb5f12e504b5b Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:43:19 -0400 Subject: [PATCH 05/21] BasePrefTable update --- OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs | 109 ++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs b/OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs index 859620a5c..43be455db 100644 --- a/OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs +++ b/OpenKh.Kh2/SystemData/Pref/BasePrefTable.cs @@ -5,6 +5,68 @@ namespace OpenKh.Kh2 { + public class BaseTableOffsetWithPaddings + where T : class + { + [Data] public int Count { get; set; } + + public static (List Items, List Offsets) ReadWithOffsets(Stream stream) + { + using (var reader = new BinaryReader(stream, System.Text.Encoding.Default, true)) + { + var header = new BaseTableOffsetWithPaddings + { + Count = reader.ReadInt32(), + }; + + var offsets = new List(); + for (int i = 0; i < header.Count; i++) + { + offsets.Add(reader.ReadInt32()); + } + + var items = new List(); + foreach (var offset in offsets.Distinct()) + { + stream.Position = offset; + items.Add(BinaryMapping.ReadObject(stream)); + } + + return (items, offsets); + } + } + + public static void WriteWithOffsets(Stream stream, IEnumerable items, List originalOffsets) + { + using (var writer = new BinaryWriter(stream, System.Text.Encoding.Default, true)) + { + var itemList = items as IList ?? items.ToList(); + var offsets = new List(); + + var header = new BaseTableOffsetWithPaddings + { + Count = originalOffsets.Count + }; + writer.Write(header.Count); + + foreach (var offset in originalOffsets) + { + writer.Write(offset); // Write original offsets + } + + var offsetMap = originalOffsets.Distinct().ToDictionary(offset => offset, offset => (int)stream.Position); + + foreach (var offset in offsetMap.Values) + { + stream.Position = offset; + BinaryMapping.WriteObject(stream, itemList[offsetMap.Keys.ToList().IndexOf(offset)]); + } + } + } + } + + + public class BaseTableOffsets where T : class { @@ -70,4 +132,51 @@ public static void Write(Stream stream, IEnumerable items) } } } + + public class BaseTableSstm + where T : class + { + [Data] public int Version { get; set; } + [Data] public int MagicCode { get; set; } + + public static List Read(Stream stream) + { + using (var reader = new BinaryReader(stream, System.Text.Encoding.Default, true)) + { + var header = new BaseTableSstm + { + Version = reader.ReadInt32(), + MagicCode = reader.ReadInt32() + }; + + var items = new List(); + for (int i = 0; i < 95; i++) // 95 float values + { + items.Add(BinaryMapping.ReadObject(stream)); + } + return items; + } + } + + public static void Write(Stream stream, IEnumerable items) + { + using (var writer = new BinaryWriter(stream, System.Text.Encoding.Default, true)) + { + var itemList = items as IList ?? items.ToList(); + + var header = new BaseTableSstm + { + Version = 6, + MagicCode = 0 + }; + writer.Write(header.Version); + writer.Write(header.MagicCode); + + foreach (var item in itemList) + { + BinaryMapping.WriteObject(stream, item); + } + } + } + } } From 418718be27e27e2f7389ccd777ea72c01cff1972 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:50:40 -0400 Subject: [PATCH 06/21] Remove incomplete listpatches (sstm & prty), unused dictionaries & redundant "using" --- OpenKh.Patcher/PatcherProcessor.cs | 63 +----------------------------- 1 file changed, 2 insertions(+), 61 deletions(-) diff --git a/OpenKh.Patcher/PatcherProcessor.cs b/OpenKh.Patcher/PatcherProcessor.cs index 2db71f6dc..cec826ccc 100644 --- a/OpenKh.Patcher/PatcherProcessor.cs +++ b/OpenKh.Patcher/PatcherProcessor.cs @@ -14,7 +14,6 @@ using Xe.BinaryMapper; using System.Collections; using Antlr4.Runtime.Dfa; -using OpenKh.Kh2.Mixdata; using System.Diagnostics; @@ -504,18 +503,7 @@ private static void PatchSpawnPoint(Context context, AssetFile assetFile, Stream private static readonly Dictionary characterMap = new Dictionary(){ { "Sora", 1 }, { "Donald", 2 }, { "Goofy", 3 }, { "Mickey", 4 }, { "Auron", 5 }, { "PingMulan",6 }, { "Aladdin", 7 }, { "Sparrow", 8 }, { "Beast", 9 }, { "Jack", 10 }, { "Simba", 11 }, { "Tron", 12 }, { "Riku", 13 }, { "Roxas", 14}, {"Ping", 15} }; - - private static readonly Dictionary weaponCharacterMap = new Dictionary(){ - { "Sora", 0 }, { "SoraNM", 1 }, { "Donald", 2 }, { "DonaldNM", 3 }, { "Goofy", 4 }, { "Goofy2",5 }, { "GoofyNM", 6 }, { "Aladdin", 7 }, { "Auron", 8 }, { "MulanPing", 9 }, { "Tron", 10 }, { "Mickey", 11 }, { "Beast", 12 }, { "Jack", 13}, {"Simba", 14}, { "Sparrow", 15 }, { "Riku", 16 }, { "SparrowHU", 17 }, { "SoraTR", 18}, {"SoraWI", 19}, { "DonaldTR", 20 }, { "DonaldWI", 21 }, { "GoofyTR", 22}, {"GoofyWI", 23} -}; - - - - private static readonly Dictionary worldIndexMapSB = new Dictionary() { - { 0, "ZZ" }, { 1, "End Of Sea" }, { 2, "TwilightTown" }, { 3, "DestinyIsland" }, { 4, "HollowBastion" }, { 5, "BeastsCastle" }, { 6, "OlympusColiseum" }, { 7, "Agrabah" }, { 8, "TheLandOfDragons" }, { 9, "100AcreWood" }, { 10, "PrideLand" }, { 11, "Atlantica" }, { 12, "DisneyCastle" }, { 13, "TimelessRiver" }, { 14, "HalloweenTown" }, { 15, "WorldMap" }, { 16, "PortRoyal" }, { 17, "SpaceParanoids" }, { 18, "TheWorldThatNeverWas" } - }; - - + private static readonly Dictionary worldIndexMap = new Dictionary(StringComparer.OrdinalIgnoreCase){ { "worldzz", 0 }, { "endofsea", 1 }, { "twilighttown", 2 }, { "destinyisland", 3 }, { "hollowbastion", 4 }, { "beastscastle", 5 }, { "olympuscoliseum", 6 }, { "agrabah", 7 }, { "thelandofdragons", 8 }, { "100acrewood", 9 }, { "prideland", 10 }, { "atlantica", 11 }, { "disneycastle", 12 }, { "timelessriver", 13}, {"halloweentown", 14}, { "worldmap", 15 }, { "portroyal", 16 }, { "spaceparanoids", 17 }, { "theworldthatneverwas", 18 } }; @@ -558,7 +546,6 @@ private static void PatchList(Context context, List sources, Stream s Kh2.SystemData.Trsr.Write(stream.SetPosition(0), trsrList.Values); break; - //Updated. Now merges fields. Likely better ways of doing this that don't bloat the code. case "item": var itemList = Kh2.SystemData.Item.Read(stream); var moddedItem = deserializer.Deserialize(sourceText); @@ -1044,7 +1031,6 @@ private static void PatchList(Context context, List sources, Stream s } } - // Write the updated list back to the stream Kh2.Places.Write(stream.SetPosition(0), originalPlace); break; @@ -1091,10 +1077,6 @@ private static void PatchList(Context context, List sources, Stream s Kh2.Soundinfo.Write(stream.SetPosition(0), originalSoundInfo); break; - - //Ok. Will need to figure this out. - //FIRST GOAL: Simpify as many redundancies as possible in the code; useless if/else statements, etc. - //More traditional version, without any weird string/conversion stuff. case "libretto": var originalLibretto = Kh2.Libretto.Read(stream); @@ -1144,7 +1126,6 @@ private static void PatchList(Context context, List sources, Stream s } } - // Write the modified data back to the stream stream.Position = 0; Kh2.Libretto.Write(stream, originalLibretto); break; @@ -1227,50 +1208,10 @@ private static void PatchList(Context context, List sources, Stream s } } - stream.SetLength(0); // Clear the stream before writing + stream.SetLength(0); Kh2.SystemData.Fmab.Write(stream, fmabList); break; - case "prty": - var moddedPrty = deserializer.Deserialize>(sourceText); - - var (prtyList, originalOffsets) = Kh2.SystemData.Prty.Read(stream); - - foreach (var patch in moddedPrty) - { - if (Kh2.SystemData.Prty.CharacterMap.TryGetValue(patch.Key, out var index)) - { - if (index >= 0 && index < prtyList.Count) - { - prtyList[index] = patch.Value; - } - } - } - - stream.SetLength(0); // Clear the stream before writing - Kh2.SystemData.Prty.Write(stream, prtyList, originalOffsets); - - break; - - - case "sstm": - var sstmList = Kh2.SystemData.Sstm.Read(stream); - var moddedSstm = deserializer.Deserialize>(sourceText); - - foreach (var sstm in moddedSstm) - { - var existingSstm = sstmList.FirstOrDefault(x => x.CeilingStop == sstm.CeilingStop); - - if (existingSstm != null) - { - //existingSstm = MergeHelper.Merge(existingSstm, sstm); - sstmList[sstmList.IndexOf(existingSstm)] = sstm; - } - } - Kh2.SystemData.Sstm.Write(stream.SetPosition(0), sstmList); - break; - - default: break; } From 644c181c756d2fe2a32eb6ab76024d4ad37c4b39 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 19:32:54 -0400 Subject: [PATCH 07/21] Add files via upload --- docs/tool/GUI.ModsManager/creatingMods.md | 454 +++++++++++++--------- 1 file changed, 265 insertions(+), 189 deletions(-) diff --git a/docs/tool/GUI.ModsManager/creatingMods.md b/docs/tool/GUI.ModsManager/creatingMods.md index cfa1e093f..ce3500dc8 100644 --- a/docs/tool/GUI.ModsManager/creatingMods.md +++ b/docs/tool/GUI.ModsManager/creatingMods.md @@ -1,114 +1,31 @@ # [OpenKh Tool Documentation](../index.md) - KH2 Mods Manager -This document will focus on teaching you how to create mods using the OpenKH Mod Manager. - -# [Table of Contents]() -* [Creating a Mod for Use With OpenKH](#creating-a-mod-for-use-with-openkh) -* [Asset Types](#asset-types) -* [Asset Methods](#asset-methods) - * [copy](#copy-any-game---performs-a-direct-copy-to-overwrite-a-file-works-on-any-file-type) - * [binarc (KH2)](#binarc-kh2---specifies-a-modification-to-a-subfile-within-a-binarc-using-one-of-the-available-methods-see-binarc-methods-for-details-on-implementing-a-specific-method) - * [copy](#copy-kh2---performs-a-copy-on-a-supfile-within-a-bar-must-be-one-of-the-following-types) - * [imgd](#imgd-kh2---replaces-a-single-imgd-found-within-a-binarc) - * [imgz / fac](#imgz--fac-kh2---replaces-multiple-imgds-found-within-a-binarc) - * [kh2msg](#kh2msg-kh2---replaces-text-found-within-a-kh2-messages-file-uses-a-yaml-file-as-an-source) - * [areadatascript](#areadatascript-kh2---modifies-a-series-programs-found-within-a-kh2-spawnscript-subfile-located-within-ard-files-using-the-text-format-created-by-openkhcommandspawnscript-you-can-only-provide-a-subset-of-the-programs-found-within-the-spawnscript-the-others-will-be-taken-from-the-original-file) - * [areadataspawn](#areadataspawn-kh2---modifies-a-kh2-spawnpoint-subfile-located-within-ard-files-using-an-yaml-file-created-using-openkhcommandspawnscript) - * [listpatch](#listpatch-kh2---can-modify-the-following-different-types-of-list-binaries-found-within-kh2) - * [trsr](#trsr-source-example) - * [cmd](#cmd-source-example) - * [item](#item-source-example) - * [sklt](#sklt-source-example) - * [enmp](#enmp-source-example) - * [fmlv](#fmlv-source-example) - * [lvup](#lvup-source-example) - * [bons](#bons-source-example) - * [atkp](#atkp-source-example) - * [przt](#przt-source-example) - * [magc](#magc-source-example) - * [objentry](#objentry-source-example) - * [Example of a Fully Complete `mod.yml` File](#an-example-of-a-fully-complete-modyml-can-be-seen-below-and-the-full-source-of-the-mod-can-be-seen-here) -* [Generating a Simple `mod.yml` for New Mod Authors](#generating-a-simple-modyml-for-new-mod-authors) -* [Publishing a Mod on GitHub](#publishing-a-mod-on-github) - -## Creating a Mod for Use With OpenKH +This document will focus on teaching you how to create mods using the OpenKH Mods Manager + +## Creating a mod A well produced mod should contain the following -* `mod.yml` - The format of which is explained below -* `icon.png` - A 128x128 image which will be seen in the mods list -* `preview.png` - A image of maximum size 512 px wide by 288 px tall, which will be shown in the mod description on the right -* Other files - whatever files are needed for the mod, as defined by the `mod.yml` +* mod.yml - The format of which is explained below +* icon.png - A 128x128 image which will be seen in the mods list +* preview.png - A image of maximum size 512 px wide by 288 px tall, which will be shown in the mod description on the right +* Other files - whatever files are needed for the mod, as defined by the mod.yml -The mod.yml file is a YAML format specification for your mod. It will contain the following fields: +The mod.yml file is a YAML format specification for your mod. It will contain the following fields * `title` - The title of your mod, as displayed in the mods manager * `description` - A description of what your mod does -* `originalAuthor` - The name of the original author who created this mod. If you just ported this mod to the modsmanager for someone else, include the original author's name. If you do not, this is considered plagiarism, and people tend to not like that. +* `originalAuthor` - The name of the original author who created this mod. If you just ported this mod to the modsmanager for someone else, include the original authors name. * `logo` - The path to the icon.png -* `assets` - A list of assets that will be modified when the mod runs. - * See [`asset types`](#asset-types), for details on creating an asset. Some asset types will work on any game, while others are game specific. - -While you are developing a mod you can create a folder inside the `mods` directory of the mod manager release. I.e.: - -`openkh/mods//` - -## Asset Types - -The truth of the matter is there are seemingly innumerable "asset types" in these games. Each game has several that are exclusive to itself, with little to none shared between any two or more. Some (but not all!) examples of the types of assets you may work with as a mod author are as follows: - -* Models - * .mdls - * .mdlx - * .cvbl - * .wpn - * .pmo - * .mdl -* Binary archives - * .bar - * .arc - * .ard - * .bin -* Sound archives - * .wd - * .seb - * .scd - * .snd -* Texture formats - * .img - * .imd / .imgd - * .imz / .imgz - * .dds / .png (PC only) - * .tm2 -* Movesets & animation - * .mset - * .anb - * .arc -* Menu & UI layouts - * .seqd - * .lad - * .l2d - * .2ld - * .2dd -* Maps & scripting - * .map - * .bar - * .arc - * .ard -* Many, many, many more... - -However, there is a point to knowing the many asset types the games have on offer. With enough know-how, custom events can be made, called, and integrated into their respective games. Not all of the formats above are from any one single game either; some are shared, some are specific to only one game. Some formats are not even the same between two games, despite having the same file extension. ~~I'm looking at you, `.arc`, `.ard`, and `.pmo`.~~ - -There are many tools at your disposal to edit a vast array of these formats, thanks to the large contributions over the years to the OpenKH project. We're far from finished yet, though, and would love more help looking into formats and making even more tools! - -Additionally, the type of format you are working with will work better with certain [asset methods](#asset-methods) as shown below. Many of the games' files are just archive formats with many other files inside. For example, KH2's `title.2ld` UI layout file for the title screen is an archive that contains a `.imz`, which is another archive with multiple images inside. It also contains `layout data` (primarily called `sequence data` in KH2) in the form of `.seqd` files, which help manage the intro logos and title screen assets contained within the aforementioned `.imz` image archive. - -In that example, if you were to simply replace one image of the title screen, you would only need to look as far as one of the several images within, which would best utilize the [`imgz`](#imgz--fac-kh2---replaces-multiple-imgds-found-within-a-binarc) asset method shown below. - - -# Asset Methods - -## `copy` (any game) - Performs a direct copy to overwrite a file. Works on any file type. +* `assets` - A list of assets that will be modified when the mod runs. See `asset types`, for details on creating an asset. Some asset types will work on any game, while others are game specific. + +While you are developing a mod you can create a folder inside the "mods" directory of the mods manager release, IE + +`/mods//` + +## Asset Methods + +* `copy` (any game) - Performs a direct copy to overwrite a file. Works on any file type. Asset Example: @@ -119,7 +36,7 @@ Asset Example: - name: files/modified_msn.bar ``` -## `binarc` (KH2) - Specifies a modification to a subfile within a binarc, using one of the available methods. See `binarc methods` for details on implementing a specific method. +* `binarc` (KH2) - Specifies a modification to a subfile within a binarc, using one of the available methods. See `binarc methods` for details on implementing a specific method. Asset Example @@ -134,9 +51,9 @@ Asset Example type: AreaDataSpawn ``` -### Binarc Methods +## Binarc Methods -## `copy` (KH2) - Performs a copy on a supfile within a Bar. Must be one of the [following](https://github.com/Xeeynamo/OpenKh/blob/master/OpenKh.Tools.BarEditor/Helpers.cs#L14) types +* `copy` (KH2) - Performs a copy on a supfile within a Bar. Must be one of the [following](https://github.com/Xeeynamo/OpenKh/blob/master/OpenKh.Tools.BarEditor/Helpers.cs#L14) types Asset Example @@ -151,7 +68,7 @@ Asset Example type: Bdx ``` -## `imgd` (KH2) - Replaces a single imgd found within a binarc +* `imgd` (KH2) - Replaces a single imgd found within a binarc Asset Example @@ -170,7 +87,7 @@ Asset Example highdef: title/title1_hd.png ``` -## `imgz` // `fac` (KH2) - Replaces multiple imgd's found within a binarc. +* // `imgz` // `fac` (KH2) - Replaces multiple imgd's found within a binarc. Asset Example @@ -196,7 +113,7 @@ Asset Example index: 1 ``` -## `kh2msg` (KH2) - Replaces text found within a kh2 messages file. Uses a yaml file as an source. +* `kh2msg` (KH2) - Replaces text found within a kh2 messages file. Uses a yaml file as an source. Asset Example @@ -224,7 +141,7 @@ Yaml Source Example jp: OPENKHすばらしい! ``` -## `areadatascript` (KH2) - Modifies a series programs found within a KH2 Spawnscript subfile (located within ard files), using the text format created by OpenKh.Command.SpawnScript. You can only provide a subset of the programs found within the spawnscript, the others will be taken from the original file. +* `areadatascript` (KH2) - Modifies a series programs found within a KH2 Spawnscript subfile (located within ard files), using the text format created by OpenKh.Command.SpawnScript. You can only provide a subset of the programs found within the spawnscript, the others will be taken from the original file. Asset Example ``` @@ -251,7 +168,7 @@ AreaSettings 0 -1 SetPartyMenu 0 ``` -## `areadataspawn` (KH2) - Modifies a KH2 Spawnpoint subfile (located within ard files), using an yaml file created using OpenKh.Command.SpawnScript. +* `areadataspawn` (KH2) - Modifies a KH2 Spawnpoint subfile (located within ard files), using an yaml file created using OpenKh.Command.SpawnScript. Asset Example @@ -266,19 +183,32 @@ Asset Example type: AreaDataSpawn ``` -## `listpatch` (KH2) - Can modify the following different types of list binaries found within KH2: +* `listpatch` (KH2) - Can modify the following different types of list binaries found within KH2. +* Fields marked with 'merge' will allow other mods to edit the differing fields of the same item, and merge the change together. +* Example: Mod A changes the Kingdom Key to be 6 strength, leaving the ability field blank. Mod B changes the ability of the Kingdom Key to be Combo Master, leaving the strength field blank. +* When building with both mods, the changes will be merged to have a Kingdom Key with 6 strength and Combo Master. + * `trsr` - * `cmd` - * `item` + * `cmd` (merge) + * `item` (merge) * `sklt` + * 'arif' + * 'memt' * `enmp` - * `fmlv` - * `lvup` + * `fmlv` (merge) + * `lvup` (merge) * `bons` - * `atkp` - * `przt` + * `atkp` (merge) + * `przt` (merge) * `magc` + * 'limt' * `objentry` + * 'libretto' + * 'localset' + * 'soundinfo' + * 'place' + * 'jigsaw' + Asset Example ``` @@ -293,12 +223,12 @@ Asset Example type: fmlv ``` -### `trsr` Source Example +`trsr` Source Example ``` 2: ItemId: 347 ``` -### `cmd` Source Example +`cmd` Source Example ``` - Id: 1 Execute: 3 @@ -325,7 +255,7 @@ Asset Example Group: 2 Reserve: 0 ``` -### `item` Source Example +`item` Source Example ``` Stats: - Ability: 412 @@ -359,14 +289,74 @@ Items: Icon1: 9 Icon2: 0 ``` -### `sklt` Source Example +`sklt` Source Example ``` - CharacterId: 1 Bone1: 178 Bone2: 86 ``` - -### `enmp` Source Example +`arif` Source Example +``` +End of Sea: #End of Sea. Names are taken from worlds.md + 2: + Flags: IsKnownArea, IndoorArea, Monochrome #Other acceptable flags are NoShadow and HasGlow. + Reverb: 10 + SoundEffectBank1: 20 + SoundEffectBank2: 30 + Bgms: + - BgmField: 2000 + BgmBattle: 2000 + - BgmField: 600 + BgmBattle: 600 + - BgmField: 1200 + BgmBattle: 1200 + - BgmField: 1200 + BgmBattle: 1200 + - BgmField: 1000 + BgmBattle: 1000 + - BgmField: 1000 + BgmBattle: 1000 + - BgmField: 1500 + BgmBattle: 1500 + - BgmField: 1500 + BgmBattle: 1500 + Voice: 40 + NavigationMapItem: 50 + Command: 60 + Reserved: [] +``` +`memt` Source Example +``` +MemtEntries: + - Index: 0 #Specify a memt index to edit. Use a new index to create a new MemtEntry. + WorldId: 2 + CheckStoryFlag: 3 + CheckStoryFlagNegation: 4 + Unk06: 5 + Unk08: 6 + Unk0A: 7 + Unk0C: 8 + Unk0E: 9 + Members: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27] + - Index: 37 + WorldId: 2 + CheckStoryFlag: 3 + CheckStoryFlagNegation: 4 + Unk06: 5 + Unk08: 6 + Unk0A: 7 + Unk0C: 8 + Unk0E: 9 + Members: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27] + +MemberIndices: + - Index: 0 + Player: 15 + Friend1: 20 + Friend2: 32 + FriendWorld: 42 +``` +`enmp` Source Example ``` - Id: 0 Level: 1 @@ -392,7 +382,7 @@ Items: BonusLevel: 1 ``` -### `fmlv` Source Example +`fmlv` Source Example ``` Final: - Ability: 578 @@ -402,7 +392,7 @@ Final: GrowthAbilityLevel: 1 ``` -### `lvup` Source Example +`lvup` Source Example ``` Sora: 2: @@ -419,7 +409,7 @@ Sora: SwordAbility: 577 ``` -### `bons` Source Example +`bons` Source Example ``` 2: Sora: @@ -437,7 +427,7 @@ Sora: Unknown0c: 0 ``` -### `atkp` Source Example +`atkp` Source Example ``` - Id: 0 #Hitbox 0 SubId: 3 @@ -474,7 +464,7 @@ Sora: HpDrain: 15 ``` -### `przt` Source Example +`przt` Source Example ``` - Id: 1 SmallHpOrbs: 0 @@ -494,7 +484,7 @@ Sora: Item3Percentage: 0 ``` -### `magc` Source Example +`magc` Source Example ``` - Id: 0 Level: 3 @@ -512,8 +502,31 @@ Sora: VoiceFinisher: 11 VoiceSelf: -1 ``` - -### `objentry` Source Example +`limt` Source Example +``` +- Id: 1 + Character: Sora + Summon: None + Group: 0 + FileName: TESTLIMIT + SpawnId: 0 + Command: 100 + Limit: 0 + World: 00 + Padding: [] +- Id: 30 + Character: Donald + Summon: Goofy + Group: 0 + FileName: TESTLIMIT + SpawnId: 0 + Command: 100 + Limit: 0 + World: 00 + Padding: [] +``` + +`objentry` Source Example ``` 4: ObjectId: 4 @@ -539,7 +552,118 @@ Sora: SpawnObject4: 0 ``` -### An example of a fully complete mod.yml can be seen below, and the full source of the mod can be seen [here](https://github.com/OpenKH/mod-template) +`libretto` Source Example +``` +- TalkMessageId: 752 #Id to update. + Unknown: 3 #Unknown to update. + Contents: #Contents to update. Will insert additional Contents as necessary. When no additional are detected, terminates w/ 0. + - Unknown1: 0x00010001 + TextId: 0x3DEB + - Unknown1: 0x00010001 + TextId: 0x183C +``` +`localset` Source Example +``` +- ProgramId: 999 + MapNumber: 25 +``` +`soundinfo` Source Example +``` +- Index: 0 #Specify an index to modify; otherwise if the index doesn't exist it will be created. + Reverb: -1 + Rate: 1 + EnvironmentWAV: 99 + EnvironmentSEB: 99 + EnvironmentNUMBER: 99 + FootstepWAV: 99 + FootstepSORA: 99 + FootstepDONALD: 99 + FootstepGOOFY: 99 + FootstepWORLDFRIEND: 99 + FootstepOTHER: 99 +``` +`place` Source Example +``` +- Index: 0 #Index should match the ID of the room in the world; i.e, Index 0 = al00 if you were modifying Agrabah. + MessageId: 1234 + Padding: 0 +``` +`jigsaw` Source Example +``` +- Picture: 2 + Part: 4 + Text: 1500 + World: 2 + Room: 1 + JigsawIdWorld: 99 + Unk07: 0 + Unk08: 0 +``` + +* `synthpatch` (KH2) - Modifies Mixdata.bar, a file used for various properties related to synthesis in KH2. + + * `recipe` + * `level` + * `condition` + + +Asset Example + +``` +- name: menu/us/mixdata.bar + method: binarc + source: + - name: reci + method: synthpatch + type: Synthesis + source: + - name: ReciList.yml + type: recipe +``` + +`recipe` Source Example +``` +- Id: 1 + Unlock: 0 + Rank: 5 + Item: 100 + UpgradedItem: 101 + Ingredient1: 200 + Ingredient1Amount: 1 + Ingredient2: 201 + Ingredient2Amount: 2 + Ingredient3: 202 + Ingredient3Amount: 3 + Ingredient4: 203 + Ingredient4Amount: 4 + Ingredient5: 204 + Ingredient5Amount: 5 + Ingredient6: 205 + Ingredient6Amount: 6 +``` + +`level` Source Example +``` +- Title: 48338 #TextID to use for Moogle Level "Title", pulls from Sys.Bar. + Stat: 48740 + Enable: -1 + Padding: 0 + Exp: 0 +``` + +`condition` Source Example +``` +- TextId: 151 + RewardId: 0 + Type: 5 + MaterialType: 100 + MaterialRank: 101 + ItemCollect: 200 + Count: 1 + ShopUnlock: 201 +``` + +An example of a fully complete mod.yml can be seen below, and the full source of the mod can be seen [here](https://github.com/OpenKH/mod-template) ``` title: OpenKH mod template @@ -594,61 +718,13 @@ assets: language: it ``` -## Generating a Simple `mod.yml` for New Mod Authors - -This method is recommended exclusively for mod authors, as the YAML Generator built into the Mod Manager is considered fully functional, yet lacks all of the aforementioned [asset methods](#asset-methods), with the exception of a couple. Those being: -* [`copy`](#copy-any-game---performs-a-direct-copy-to-overwrite-a-file-works-on-any-file-type) (game agnostic) -* [`binarc`](#binarc-kh2---specifies-a-modification-to-a-subfile-within-a-binarc-using-one-of-the-available-methods-see-binarc-methods-for-details-on-implementing-a-specific-method) (KH2 only so far) - -To start, here are the steps: - -1. Create an isolated folder where you will be working on your mod. Ideally, to make it easy for testing in-game, this would be in `openkh/mods/Your Name/Your Mod Name`. -2. To keep things simple, recreate the game's folder structure. I.e., if you were to edit KH2's title screen, Sora's HUD sprites, and replacing the font in KH2 with something goofy like Comic Sans, you would have a file tree that looks like this: -``` -├───menu -│ └───us -│ └───title.2ld -│ -└───remastered - └───msg - │ └───us - │ └───fontimage.bar - └───obj - └───P_EX100.a.us - └─── ~8.dds -``` - -3. Open the `Creator` menu on the top of Mod Manager, and select `YamlGenerator`. -4. Make the path to your `mod.yml` where your root mod folder is (i.e., where the example file structure would be located at). -5. Click `Generate or update mod.yml`. -6. Make the `GameDataPath` the same folder as where your new `mod.yml` has been generated. -7. Click `Begin`. -8. Click `Search` in the upper right. -9. Select all your files (**not** `mod.yml`). -10. Click `Copy each`. -11. Make sure the `[ ]Exists` box is checked. If it's not, you did something wrong. Retrace your steps. -12. Click `Proceed` at the bottom. -13. Answer yes, you would like to commit the change to the `mod.yml` file. -14. Close the creator and edit your `mod.yml` file with a text editor to change the following fields as necessary: - * `title` - * `originalAuthor` - * `description` -15. Save your changes and upload the entirety of this folder, including the `mod.yml` file to a new GitHub repository. -16. (Optional) Archive everything into a `.zip` archive and upload to Nexusmods under the appropriate game section. -17. -18. Profit! - -## Publishing a Mod on GitHub - -Mods should be published to a public GitHUb repository, so that users can install the mod just by providing the repository name. - -It is recommended to apply the following tags to the repository, in order to make it easily found by searching GitHub for mods manager mods: - -* `openkh-mods` - -* `` - * `kh1` - * `kh2` - * `bbs` - * `ddd` - * `recom` \ No newline at end of file +## Publishing a mod + +Mods should be published to a public github repository, so that users an install the mod just by providing the repository name. + + +It is recommended to apply the following tags to the repository, in order to make it easily found by searching GitHub for mods manager mods. + +`openkh-mods` + +`` (ie `kh2` or `bbs`) From 5e868185002f950a30a9281b2ee089de614f0bed Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 19:35:00 -0400 Subject: [PATCH 08/21] Fix w/ btlv & vtbl examples --- docs/tool/GUI.ModsManager/creatingMods.md | 61 +++++++++++++++++++---- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/docs/tool/GUI.ModsManager/creatingMods.md b/docs/tool/GUI.ModsManager/creatingMods.md index ce3500dc8..9a50a999c 100644 --- a/docs/tool/GUI.ModsManager/creatingMods.md +++ b/docs/tool/GUI.ModsManager/creatingMods.md @@ -184,24 +184,23 @@ Asset Example ``` * `listpatch` (KH2) - Can modify the following different types of list binaries found within KH2. -* Fields marked with 'merge' will allow other mods to edit the differing fields of the same item, and merge the change together. -* Example: Mod A changes the Kingdom Key to be 6 strength, leaving the ability field blank. Mod B changes the ability of the Kingdom Key to be Combo Master, leaving the strength field blank. -* When building with both mods, the changes will be merged to have a Kingdom Key with 6 strength and Combo Master. * `trsr` - * `cmd` (merge) - * `item` (merge) + * `cmd` + * `item` * `sklt` * 'arif' * 'memt' * `enmp` - * `fmlv` (merge) - * `lvup` (merge) + * `fmlv` + * `lvup` * `bons` - * `atkp` (merge) - * `przt` (merge) + * `atkp` + * `przt` * `magc` * 'limt' + * 'vtbl' + * 'btlv' * `objentry` * 'libretto' * 'localset' @@ -526,6 +525,50 @@ Sora: Padding: [] ``` +`vtbl` Source Example +``` +- Id: 26 + CharacterId: 1 + Priority: 01 + Voices: + - VsbIndex: 5 + Weight: 100 + - VsbIndex: -1 + Weight: 0 + - VsbIndex: -1 + Weight: 0 + - VsbIndex: -1 + Weight: 0 + - VsbIndex: 6 + Weight: 5 + Reserved: 0 +``` + +`btlv` Source Example +``` +- Id: 0 + ProgressFlag: 0x1099 + WorldZZ: 1 + WorldOfDarkness: 1 + TwilightTown: 1 + DestinyIslands: 1 + HollowBastion: 1 + BeastCastle: 1 + OlympusColiseum: 1 + Agrabah: 1 + LandOfDragons: 1 + HundredAcreWoods: 1 + PrideLands: 1 + Atlantica: 1 + DisneyCastle: 1 + TimelessRiver: 1 + HalloweenTown: 1 + PortRoyal: 1 + SpaceParanoids: 1 + TheWorldThatNeverWas: 1 + Padding: [] +``` + `objentry` Source Example ``` 4: From 058e338c1b9d2f360e0906eec07f8c740553e713 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 19:46:15 -0400 Subject: [PATCH 09/21] Add tests --- OpenKh.Tests/Patcher/PatcherTests.cs | 1241 +++++++++++++++++++++++++- 1 file changed, 1217 insertions(+), 24 deletions(-) diff --git a/OpenKh.Tests/Patcher/PatcherTests.cs b/OpenKh.Tests/Patcher/PatcherTests.cs index 1cd014fae..c4562925b 100644 --- a/OpenKh.Tests/Patcher/PatcherTests.cs +++ b/OpenKh.Tests/Patcher/PatcherTests.cs @@ -1,3 +1,4 @@ +using Antlr4.Runtime; using OpenKh.Command.Bdxio.Utils; using OpenKh.Common; using OpenKh.Imaging; @@ -954,8 +955,326 @@ public void ListPatchSkltTest() Assert.Equal(178, skltStream[0].Bone1); Assert.Equal(86, skltStream[0].Bone2); }); + } + + [Fact] //Fixed, needed to initialize the BGMSet & Reserved bytes. + public void ListPatchArifTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bin", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "arif", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "ArifList.yml", + Type = "arif" + } + } + } + } + } + } + }; + + using (var stream = File.Create(Path.Combine(AssetsInputDir, "03system.bin"))) + { + var arifEntry = new Kh2.SystemData.Arif + { + Flags = Kh2.SystemData.Arif.ArifFlags.IsKnownArea, + Reverb = 0, + SoundEffectBank1 = 13, + SoundEffectBank2 = 0, + Bgms = Enumerable.Range(0, 8).Select(_ => new Kh2.SystemData.BgmSet { BgmField = 0, BgmBattle = 0 }).ToArray(), + Voice = 0, + NavigationMapItem = 0, + Command = 0, + Reserved = new byte[11] + }; + + using (var arifStream = new MemoryStream()) + { + Kh2.SystemData.Arif.Write(arifStream, new List> { new List { arifEntry } }); + Bar.Write(stream, new Bar + { + new Bar.Entry() + { + Name = "arif", + Type = Bar.EntryType.List, + Stream = arifStream + } + }); + } + } + + File.Create(Path.Combine(ModInputDir, "ArifList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("EndOfSea:"); + writer.WriteLine(" 1:"); + writer.WriteLine(" SoundEffectBank1: 13"); + writer.WriteLine(" Voice: 0"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "03system.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bin")).Using(stream => + { + var binarc = Bar.Read(stream); + var arifList = Kh2.SystemData.Arif.Read(binarc[0].Stream); + var arifEntry = arifList[0][0]; + Assert.Equal(13, arifEntry.SoundEffectBank1); + Assert.Equal(0, arifEntry.Voice); + }); + } + + [Fact] + public void ListPatchMemtTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "memt", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "MemtList.yml", + Type = "memt" + } + } + } + } + } + } + }; + + // Create the 03system.bar file with initial data + File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + { + var memtEntries = new List() + { + new Kh2.SystemData.Memt.EntryFinalMix + { + WorldId = 1, + CheckStoryFlag = 1, + CheckStoryFlagNegation = 0, + Unk06 = 0, + Unk08 = 0, + Unk0A = 0, + Unk0C = 0, + Unk0E = 0, + Members = new short[18] + } + }; + + var memberIndices = new Kh2.SystemData.Memt.MemberIndices[7]; // Ensure there are 7 MemberIndices + for (int i = 0; i < memberIndices.Length; i++) + { + memberIndices[i] = new Kh2.SystemData.Memt.MemberIndices + { + Player = 0, + Friend1 = 0, + Friend2 = 0, + FriendWorld = 0 + }; + } + + using var memtStream = new MemoryStream(); + var memt = new Kh2.SystemData.Memt(); + memt.Entries.AddRange(memtEntries.Cast()); + + Kh2.SystemData.Memt.Write(memtStream, memt); + memtStream.Seek(0, SeekOrigin.Begin); + + Bar.Write(stream, new Bar() + { + new Bar.Entry() + { + Name = "memt", + Type = Bar.EntryType.List, + Stream = memtStream + } + }); + }); + + // Create the MemtList.yml patch file + File.Create(Path.Combine(ModInputDir, "MemtList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("MemtEntries:"); + writer.WriteLine(" - Index: 0"); + writer.WriteLine(" WorldId: 2"); + writer.WriteLine(" CheckStoryFlag: 3"); + writer.WriteLine(" CheckStoryFlagNegation: 4"); + writer.WriteLine(" Unk06: 5"); + writer.WriteLine(" Unk08: 6"); + writer.WriteLine(" Unk0A: 7"); + writer.WriteLine(" Unk0C: 8"); + writer.WriteLine(" Unk0E: 9"); + writer.WriteLine(" Members: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]"); + writer.WriteLine("MemberIndices:"); + writer.WriteLine(" - Index: 0"); + writer.WriteLine(" Player: 0"); + writer.WriteLine(" Friend1: 0"); + writer.WriteLine(" Friend2: 0"); + writer.WriteLine(" FriendWorld: 0"); + writer.Flush(); + }); + + // Apply the patch + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + // Verify the patched data + AssertFileExists(ModOutputDir, "03system.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var memtStream = binarc[0].Stream; + memtStream.Seek(0, SeekOrigin.Begin); + + var memt = Kh2.SystemData.Memt.Read(memtStream); + + var memtEntry = memt.Entries.Cast().First(); + Assert.Equal((short)2, memtEntry.WorldId); + Assert.Equal((short)3, memtEntry.CheckStoryFlag); + Assert.Equal((short)4, memtEntry.CheckStoryFlagNegation); + Assert.Equal((short)5, memtEntry.Unk06); + Assert.Equal((short)6, memtEntry.Unk08); + Assert.Equal((short)7, memtEntry.Unk0A); + Assert.Equal((short)8, memtEntry.Unk0C); + Assert.Equal((short)9, memtEntry.Unk0E); + Assert.Equal(new short[] { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }, memtEntry.Members); + + // Check MemberIndices deserialization + var memberIndices = memt.MemberIndexCollection.First(); + Assert.Equal((byte)0, memberIndices.Player); + Assert.Equal((byte)0, memberIndices.Friend1); + Assert.Equal((byte)0, memberIndices.Friend2); + Assert.Equal((byte)0, memberIndices.FriendWorld); + }); } + + + [Fact] + public void ListPatchFmabTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "pref", + Method = "binarc", + Type = "Binary", + Source = new List() + { + new AssetFile() + { + Name = "fmab", + Method = "listpatch", + Type = "list", + Source = new List() + { + new AssetFile() + { + Name = "FmabList.yml", + Type = "fmab" + } + } + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + { + var FmabEntry = new List() + { + new Kh2.SystemData.Fmab + { + HighJumpHeight = 178, + AirDodgeHeight = 86 + } + }; + + using var fmabStream = new MemoryStream(); + Kh2.SystemData.Fmab.Write(fmabStream, FmabEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "fmab", + Type = Bar.EntryType.List, + Stream = fmabStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "FmabList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- HighJumpHeight: 178"); + writer.WriteLine(" AirDodgeHeight: 86"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "03system.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var fmabStream = Kh2.SystemData.Fmab.Read(binarc[0].Stream); + Assert.Equal(178, fmabStream[0].HighJumpHeight); + Assert.Equal(86, fmabStream[0].AirDodgeHeight); + }); + } + [Fact] public void ListPatchFmlvTest() { @@ -1379,6 +1698,7 @@ public void ListPatchLvupTest() } + [Fact] void ListPatchAtkpTest() { var patcher = new PatcherProcessor(); @@ -1901,10 +2221,127 @@ public void ListPatchMagcTest() Assert.Equal(3, magc[0].Level); }); - } - + } + + [Fact] + public void ListPatchLimtTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "Limt", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "LimtList.yml", + Type = "limt" + } + } + } + } + } + } + }; + + // Create the initial 00battle.bar file with Limt entry + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var limtEntry = new List() + { + new Kh2.Battle.Limt + { + Id = 0, + Character = Kh2.Battle.Limt.Characters.Auron, + Summon = Kh2.Battle.Limt.Characters.Sora, + Group = 0, + FileName = "auron.bar", + SpawnId = 0, + Command = 82, + Limit = 204, + World = 0, + Padding = new byte[18] + } + }; + + using var limtStream = new MemoryStream(); + Kh2.Battle.Limt.Write(limtStream, limtEntry); + limtStream.Position = 0; // Ensure stream position is reset before writing to Bar + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "Limt", + Type = Bar.EntryType.List, + Stream = limtStream + } + }); + }); + + // Create the LimtList.yml file using the serializer + File.Create(Path.Combine(ModInputDir, "LimtList.yml")).Using(stream => + { + using var writer = new StreamWriter(stream); + var moddedLimt = new List + { + new Kh2.Battle.Limt + { + Id = 0, + Character = Kh2.Battle.Limt.Characters.Auron, + Summon = Kh2.Battle.Limt.Characters.Sora, + Group = 0, + FileName = "auron.bar", + SpawnId = 0, + Command = 82, + Limit = 204, + World = 0, + Padding = new byte[18] + } + }; + writer.Write(serializer.Serialize(moddedLimt)); + writer.Flush(); + }); + + // Apply the patch + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + // Verify the output file exists + AssertFileExists(ModOutputDir, "00battle.bar"); + + // Read and validate the output file + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var limt = Kh2.Battle.Limt.Read(binarc[0].Stream); + + Assert.Equal(0, limt[0].Id); + Assert.Equal(Kh2.Battle.Limt.Characters.Auron, limt[0].Character); + Assert.Equal(Kh2.Battle.Limt.Characters.Sora, limt[0].Summon); + Assert.Equal(0, limt[0].Group); + Assert.Equal("auron.bar", limt[0].FileName.Trim()); + Assert.Equal(0u, limt[0].SpawnId); + Assert.Equal(82, limt[0].Command); + Assert.Equal(204, limt[0].Limit); + Assert.Equal(0, limt[0].World); + Assert.Equal(new byte[18], limt[0].Padding); + }); + } + + [Fact] - public void ListPatchPrztTest() + public void ListPatchBtlvTest() { var patcher = new PatcherProcessor(); var serializer = new Serializer(); @@ -1920,15 +2357,15 @@ public void ListPatchPrztTest() { new AssetFile() { - Name = "przt", + Name = "btlv", Method = "listpatch", Type = "List", Source = new List() { new AssetFile() { - Name = "PrztList.yml", - Type = "przt" + Name = "BtlvList.yml", + Type = "btlv" } } } @@ -1939,41 +2376,44 @@ public void ListPatchPrztTest() File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => { - var prztEntry = new List() + var btlvEntry = new List() { - new Kh2.Battle.Przt + new Kh2.Battle.Btlv { - Id = 1, - SmallHpOrbs = 0, - BigHpOrbs = 1 + Id = 0, + ProgressFlag = 3, + WorldZZ = 1, + Padding = new byte[5], } }; - using var prztStream = new MemoryStream(); - Kh2.Battle.Przt.Write(prztStream, prztEntry); + using var btlvStream = new MemoryStream(); + Kh2.Battle.Btlv.Write(btlvStream, btlvEntry); Bar.Write(stream, new Bar() { new Bar.Entry() { - Name = "przt", + Name = "btlv", Type = Bar.EntryType.List, - Stream = prztStream + Stream = btlvStream } }); }); - File.Create(Path.Combine(ModInputDir, "PrztList.yml")).Using(stream => + File.Create(Path.Combine(ModInputDir, "BtlvList.yml")).Using(stream => { var writer = new StreamWriter(stream); var serializer = new Serializer(); - var moddedPrzt = new List{ - new Kh2.Battle.Przt + var moddedBtlv = new List{ + new Kh2.Battle.Btlv { - Id = 1, - SmallHpOrbs = 0, - BigHpOrbs = 1 + Id = 0, + ProgressFlag = 3, + WorldZZ = 1, + Padding = new byte[5], + } }; - writer.Write(serializer.Serialize(moddedPrzt)); + writer.Write(serializer.Serialize(moddedBtlv)); writer.Flush(); }); @@ -1984,11 +2424,764 @@ public void ListPatchPrztTest() File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => { var binarc = Bar.Read(stream); - var przt = Kh2.Battle.Przt.Read(binarc[0].Stream); + var btlv = Kh2.Battle.Btlv.Read(binarc[0].Stream); - Assert.Equal(1, przt[0].BigHpOrbs); + Assert.Equal(3, btlv[0].ProgressFlag); }); + } + + [Fact] + public void ListPatchVtblTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "vtbl", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "VtblList.yml", + Type = "vtbl" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var vtblEntry = new List() + { + new Kh2.Battle.Vtbl + { + Id = 0, + CharacterId = 1, + Priority = 1, + Reserved = 0, + Voices = new List + { + new Kh2.Battle.Vtbl.Voice { VsbIndex = 0, Weight = 0 } + } + } + }; + + using var vtblStream = new MemoryStream(); + Kh2.Battle.Vtbl.Write(vtblStream, vtblEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "vtbl", + Type = Bar.EntryType.List, + Stream = vtblStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "VtblList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + var moddedVtbl = new List{ + new Kh2.Battle.Vtbl + { + Id = 0, + CharacterId = 1, + Priority = 1, + Reserved = 0, + Voices = new List + { + new Kh2.Battle.Vtbl.Voice { VsbIndex = 0, Weight = 0 } + } + } + }; + writer.Write(serializer.Serialize(moddedVtbl)); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00battle.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var vtbl = Kh2.Battle.Vtbl.Read(binarc[0].Stream); + + Assert.Equal(1, vtbl[0].Priority); + }); } + + + + [Fact] //Libretto test. + public void ListPatchLibrettoTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "libretto-ca.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "ca", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "LibrettoList.yml", + Type = "libretto" + } + } + } + } + } + } + }; + + using (var stream = File.Create(Path.Combine(AssetsInputDir, "libretto-ca.bar"))) + { + var librettoEntry = new Kh2.Libretto + { + MagicCode = 0x03, // "LIBR" in ASCII + Count = 1, + Definitions = new List + { + new Kh2.Libretto.TalkMessageDefinition + { + TalkMessageId = 1, + Unknown = 0, + ContentPointer = 8 + 8 // MagicCode (4 bytes) + Count (4 bytes) + Definitions (8 bytes each) + } + }, + Contents = new List> + { + new List + { + new Kh2.Libretto.TalkMessageContent { Unknown1 = 0x02000200, TextId = 2500 } + } + } + }; + + using (var librettoStream = new MemoryStream()) + { + Kh2.Libretto.Write(librettoStream, librettoEntry); + Bar.Write(stream, new Bar + { + new Bar.Entry() + { + Name = "libretto", + Type = Bar.EntryType.List, + Stream = librettoStream + } + }); + } + } + + File.Create(Path.Combine(ModInputDir, "LibrettoList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- TalkMessageId: 1"); + writer.WriteLine(" Unknown: 0"); + writer.WriteLine(" Contents:"); + writer.WriteLine(" - Unknown1: 0x02000200"); + writer.WriteLine(" TextId: 2500"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "libretto-ca.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "libretto-ca.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var librettoList = Kh2.Libretto.Read(binarc[0].Stream); + var librettoEntry = librettoList.Contents[0][0]; + Assert.Equal(0x02000200u, librettoEntry.Unknown1); + Assert.Equal(2500u, librettoEntry.TextId); + }); + } + + [Fact] + public void ListPatchLocalsetTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "07localset.bin", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "loca", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "LocalsetList.yml", + Type = "localset" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "07localset.bin")).Using(stream => + { + var localEntry = new List() + { + new Kh2.Localset + { + ProgramId = 1300, + MapNumber = 6, + } + }; + + using var localStream = new MemoryStream(); + Kh2.Localset.Write(localStream, localEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "loca", + Type = Bar.EntryType.List, + Stream = localStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "LocalsetList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- ProgramId: 1300"); + writer.WriteLine(" MapNumber: 6"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "07localset.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "07localset.bin")).Using(stream => + { + var binarc = Bar.Read(stream); + var localStream = Kh2.Localset.Read(binarc[0].Stream); + Assert.Equal(1300u, localStream[0].ProgramId); + Assert.Equal(6u, localStream[0].MapNumber); + }); + } + + [Fact] + public void ListPatchJigsawTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "15jigsaw.bin", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "JigsawList.yml", + Type = "jigsaw", + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "15jigsaw.bin")).Using(stream => + { + var jigsawEntry = new List() + { + new Kh2.Jigsaw + { + Picture = Kh2.Jigsaw.PictureName.Duality, + Part = 2, + Text = 1500 + } + }; + Kh2.Jigsaw.Write(stream, jigsawEntry); + }); + + File.Create(Path.Combine(ModInputDir, "JigsawList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Picture: Duality"); + writer.WriteLine(" Part: 2"); + writer.WriteLine(" Text: 1500"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "15jigsaw.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "15jigsaw.bin")).Using(stream => + { + var jigsawStream = Kh2.Jigsaw.Read(stream); + Assert.Equal(2, jigsawStream[0].Part); + }); + + } + + [Fact] + public void ListPatchPlacesTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "place.bin", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "PlaceList.yml", + Type = "place", + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "place.bin")).Using(stream => + { + var placeEntry = new List() + { + new Kh2.Places + { + MessageId = 100, + Padding = 0, + } + }; + Kh2.Places.Write(stream, placeEntry); + }); + + File.Create(Path.Combine(ModInputDir, "PlaceList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- MessageId: 100"); + writer.WriteLine(" Padding: 0"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "place.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "place.bin")).Using(stream => + { + var placesStream = Kh2.Places.Read(stream); + Assert.Equal(100, placesStream[0].MessageId); + }); + + } + + [Fact] + public void ListPatchSoundInfoTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "12soundinfo.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "zz", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "SoundInfoList.yml", + Type = "soundinfo" + } + } + } + } + } + } + }; + File.Create(Path.Combine(AssetsInputDir, "12soundinfo.bar")).Using(stream => + { + var soundinfoEntry = new List() + { + new Kh2.Soundinfo + { + Reverb = -1, + Rate = 1, + } + }; + Kh2.Soundinfo.Write(stream, soundinfoEntry); + }); + + File.Create(Path.Combine(ModInputDir, "SoundInfoList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Reverb: -1"); + writer.WriteLine(" Rate: 1"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "12soundinfo.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "12soundinfo.bar")).Using(stream => + { + var soundinfoStream = Kh2.Soundinfo.Read(stream); + Assert.Equal(1, soundinfoStream[0].Rate); + }); + + } + + [Fact] + public void ListPatchMixdataReciTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "mixdata.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "reci", + Method = "synthpatch", + Type = "Synthesis", + Source = new List() + { + new AssetFile() + { + Name = "ReciList.yml", + Type = "recipe" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "mixdata.bar")).Using(stream => + { + var recipeEntry = new List() + { + new Kh2.Mixdata.ReciLP + { + Id = 1, + Unlock = 0, + Rank = 0, + } + }; + using var recipeStream = new MemoryStream(); + Kh2.Mixdata.ReciLP.Write(recipeStream, recipeEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "reci", + Type = Bar.EntryType.List, + Stream = recipeStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "ReciList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Id: 1"); + writer.WriteLine(" Rank: 0"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "mixdata.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "mixdata.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var recipeStream = Kh2.Mixdata.ReciLP.Read(binarc[0].Stream); + Assert.Equal(1, recipeStream[0].Id); + Assert.Equal(0, recipeStream[0].Rank); + }); + + } + + [Fact] + public void ListPatchMixdataLeveLPTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "mixdata.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "leve", + Method = "synthpatch", + Type = "Synthesis", + Source = new List() + { + new AssetFile() + { + Name = "LeveList.yml", + Type = "level" + } + } + } + } + } + } + }; + + // Create the initial mixdata.bar file with LeveLP entry + File.Create(Path.Combine(AssetsInputDir, "mixdata.bar")).Using(stream => + { + var leveEntry = new List() + { + new Kh2.Mixdata.LeveLP + { + Title = 1, + Stat = 2, + Enable = 1, + Padding = 0, + Exp = 100 + } + }; + + using var leveStream = new MemoryStream(); + Kh2.Mixdata.LeveLP.Write(leveStream, leveEntry); + leveStream.Position = 0; // Ensure stream position is reset before writing to Bar + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "leve", + Type = Bar.EntryType.List, + Stream = leveStream + } + }); + }); + + // Create the LeveList.yml file using the serializer + File.Create(Path.Combine(ModInputDir, "LeveList.yml")).Using(stream => + { + using var writer = new StreamWriter(stream); + var moddedLeve = new List + { + new Kh2.Mixdata.LeveLP + { + Title = 1, + Stat = 2, + Enable = 1, + Padding = 0, + Exp = 100 + } + }; + writer.Write(serializer.Serialize(moddedLeve)); + writer.Flush(); + }); + + // Apply the patch + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + // Verify the output file exists + AssertFileExists(ModOutputDir, "mixdata.bar"); + + // Read and validate the output file + File.OpenRead(Path.Combine(ModOutputDir, "mixdata.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var leveStream = Kh2.Mixdata.LeveLP.Read(binarc[0].Stream); + + Assert.Equal(1, leveStream[0].Title); + Assert.Equal(2, leveStream[0].Stat); + Assert.Equal(1, leveStream[0].Enable); + Assert.Equal(0, leveStream[0].Padding); + Assert.Equal(100, leveStream[0].Exp); + }); + } + + [Fact] + public void ListPatchMixdataCondLPTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "mixdata.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "cond", + Method = "synthpatch", + Type = "Synthesis", + Source = new List() + { + new AssetFile() + { + Name = "CondList.yml", + Type = "condition" + } + } + } + } + } + } + }; + + // Create the initial mixdata.bar file with CondLP entry + File.Create(Path.Combine(AssetsInputDir, "mixdata.bar")).Using(stream => + { + var condEntry = new List() + { + new Kh2.Mixdata.CondLP + { + TextId = 1, + Reward = 100, + Type = Kh2.Mixdata.CondLP.RewardType.Item, + MaterialType = 0, + MaterialRank = 1, + ItemCollect = Kh2.Mixdata.CondLP.CollectionType.Stack, + Count = 10, + ShopUnlock = 5 + } + }; + + using var condStream = new MemoryStream(); + Kh2.Mixdata.CondLP.Write(condStream, condEntry); + condStream.Position = 0; // Ensure stream position is reset before writing to Bar + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "cond", + Type = Bar.EntryType.List, + Stream = condStream + } + }); + }); + + // Create the CondList.yml file using the serializer + File.Create(Path.Combine(ModInputDir, "CondList.yml")).Using(stream => + { + using var writer = new StreamWriter(stream); + var moddedCond = new List + { + new Kh2.Mixdata.CondLP + { + TextId = 1, + Reward = 100, + Type = Kh2.Mixdata.CondLP.RewardType.Item, + MaterialType = 0, + MaterialRank = 1, + ItemCollect = Kh2.Mixdata.CondLP.CollectionType.Stack, + Count = 10, + ShopUnlock = 5 + } + }; + writer.Write(serializer.Serialize(moddedCond)); + writer.Flush(); + }); + + // Apply the patch + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + // Verify the output file exists + AssertFileExists(ModOutputDir, "mixdata.bar"); + + // Read and validate the output file + File.OpenRead(Path.Combine(ModOutputDir, "mixdata.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var condStream = Kh2.Mixdata.CondLP.Read(binarc[0].Stream); + + Assert.Equal(1, condStream[0].TextId); + Assert.Equal(100, condStream[0].Reward); + Assert.Equal(Kh2.Mixdata.CondLP.RewardType.Item, condStream[0].Type); + Assert.Equal(0, condStream[0].MaterialType); + Assert.Equal(1, condStream[0].MaterialRank); + Assert.Equal(Kh2.Mixdata.CondLP.CollectionType.Stack, condStream[0].ItemCollect); + Assert.Equal(10, condStream[0].Count); + Assert.Equal(5, condStream[0].ShopUnlock); + }); + } + + + [Fact] public void ProcessMultipleTest() From e82c7290e0246ca3ec857aa5d2488085e7f8e0ff Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:37:07 -0400 Subject: [PATCH 10/21] Remove unused "external" method --- OpenKh.Patcher/PatcherProcessor.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/OpenKh.Patcher/PatcherProcessor.cs b/OpenKh.Patcher/PatcherProcessor.cs index cec826ccc..f0c027e2e 100644 --- a/OpenKh.Patcher/PatcherProcessor.cs +++ b/OpenKh.Patcher/PatcherProcessor.cs @@ -266,10 +266,6 @@ private static void PatchFile(Context context, AssetFile assetFile, Stream strea - //New goal for this: Prevent it from stopping the mod application completely. - //Instead, just write an error to the Log with Log.Warn, AND ensure it copies nothing. - - //Below: Reverted CopyFile back to Vanilla. Seems to have caused some issues. private static void CopyFile(Context context, AssetFile assetFile, Stream stream) { if (assetFile.Source == null || assetFile.Source.Count == 0) @@ -281,10 +277,6 @@ private static void CopyFile(Context context, AssetFile assetFile, Stream stream { srcFile = context.GetOriginalAssetPath(assetFile.Source[0].Name); } - if (assetFile.Source[0].Type == "external") - { - srcFile = context.GetDestinationPath(assetFile.Source[0].Name); - } else { srcFile = context.GetSourceModAssetPath(assetFile.Source[0].Name); From e4a0c549a63ca2551a4296ce2c8aaeaad6627519 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:44:53 -0400 Subject: [PATCH 11/21] Restore przt test --- OpenKh.Tests/Patcher/PatcherTests.cs | 5532 +++++++++++++------------- 1 file changed, 2810 insertions(+), 2722 deletions(-) diff --git a/OpenKh.Tests/Patcher/PatcherTests.cs b/OpenKh.Tests/Patcher/PatcherTests.cs index c4562925b..bddcdc017 100644 --- a/OpenKh.Tests/Patcher/PatcherTests.cs +++ b/OpenKh.Tests/Patcher/PatcherTests.cs @@ -1,2226 +1,2314 @@ using Antlr4.Runtime; -using OpenKh.Command.Bdxio.Utils; -using OpenKh.Common; -using OpenKh.Imaging; -using OpenKh.Kh2; -using OpenKh.Kh2.Messages; -using OpenKh.Patcher; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Xunit; -using Xunit.Sdk; -using YamlDotNet.Serialization; - -namespace OpenKh.Tests.Patcher -{ - public class PatcherTests : IDisposable - { - private const string AssetsInputDir = "original_input"; - private const string ModInputDir = "mod_input"; - private const string ModOutputDir = "mod_output"; - - public PatcherTests() - { - Dispose(); - Directory.CreateDirectory(AssetsInputDir); - Directory.CreateDirectory(ModInputDir); - Directory.CreateDirectory(ModOutputDir); - } - - public void Dispose() - { - if (Directory.Exists(AssetsInputDir)) - Directory.Delete(AssetsInputDir, true); - if (Directory.Exists(ModInputDir)) - Directory.Delete(ModInputDir, true); - if (Directory.Exists(ModOutputDir)) - Directory.Delete(ModOutputDir, true); - } - - [Fact] - public void Kh2CopyBinariesTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bin", - Method = "copy", - Source = new List - { - new AssetFile - { - Name = "somedir/somefile.bin" - } - } - } - } - }; - - CreateFile(ModInputDir, patch.Assets[0].Name).Dispose(); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void Kh2CreateBinArcIfSourceDoesntExistsTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bar", - Method = "binarc", - Source = new List - { - new AssetFile - { - Name = "abcd", - Type = "list", - Method = "copy", - Source = new List - { - new AssetFile - { - Name = "somedir/somefile/abcd.bin" - } - } - } - } - } - } - }; - - CreateFile(ModInputDir, "somedir/somefile/abcd.bin").Using(x => - { - x.WriteByte(0); - x.WriteByte(1); - x.WriteByte(2); - x.WriteByte(3); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertBarFile("abcd", entry => - { - Assert.Equal(Bar.EntryType.List, entry.Type); - Assert.Equal(4, entry.Stream.Length); - Assert.Equal(0, entry.Stream.ReadByte()); - Assert.Equal(1, entry.Stream.ReadByte()); - Assert.Equal(2, entry.Stream.ReadByte()); - Assert.Equal(3, entry.Stream.ReadByte()); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void Kh2MergeWithOriginalBinArcTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bar", - Method = "binarc", - Source = new List - { - new AssetFile - { - Name = "abcd", - Type = "list", - Method = "copy", - Source = new List - { - new AssetFile - { - Name = "somedir/somefile/abcd.bin" - } - } - } - } - } - } - }; - - CreateFile(ModInputDir, "somedir/somefile/abcd.bin").Using(x => - { - x.WriteByte(0); - x.WriteByte(1); - x.WriteByte(2); - x.WriteByte(3); - }); - - CreateFile(AssetsInputDir, "somedir/somefile.bar").Using(x => - { - Bar.Write(x, new Bar - { - new Bar.Entry - { - Name = "nice", - Type = Bar.EntryType.Model, - Stream = new MemoryStream(new byte[] { 4, 5, 6, 7 }) - } - }); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertBarFile("abcd", entry => - { - Assert.Equal(Bar.EntryType.List, entry.Type); - Assert.Equal(4, entry.Stream.Length); - Assert.Equal(0, entry.Stream.ReadByte()); - Assert.Equal(1, entry.Stream.ReadByte()); - Assert.Equal(2, entry.Stream.ReadByte()); - Assert.Equal(3, entry.Stream.ReadByte()); - }, ModOutputDir, patch.Assets[0].Name); - AssertBarFile("nice", entry => - { - Assert.Equal(Bar.EntryType.Model, entry.Type); - Assert.Equal(4, entry.Stream.Length); - Assert.Equal(4, entry.Stream.ReadByte()); - Assert.Equal(5, entry.Stream.ReadByte()); - Assert.Equal(6, entry.Stream.ReadByte()); - Assert.Equal(7, entry.Stream.ReadByte()); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void Kh2CreateImgdTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bar", - Method = "binarc", - Source = new List - { - new AssetFile - { - Name = "test", - Method = "imgd", - Type = "imgd", - Source = new List - { - new AssetFile - { - Name = "sample.png", - IsSwizzled = false - } - } - } - } - } - } - }; - - File.Copy("Imaging/res/png/32.png", Path.Combine(ModInputDir, "sample.png")); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertBarFile("test", entry => - { - Assert.True(Imgd.IsValid(entry.Stream)); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void Kh2MergeImzTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "out.imz", - Method = "imgz", - Source = new List - { - new AssetFile - { - Name = "test.imd", - Index = 1, - } - } - } - } - }; - - var tmpImd = Imgd.Create(new System.Drawing.Size(16, 16), PixelFormat.Indexed4, new byte[16 * 16 / 2], new byte[4], false); - var patchImd = Imgd.Create(new System.Drawing.Size(32, 16), PixelFormat.Indexed4, new byte[32 * 16 / 2], new byte[4], false); - CreateFile(AssetsInputDir, "out.imz").Using(x => - { - Imgz.Write(x, new Imgd[] - { - tmpImd, - tmpImd, - tmpImd, - }); - }); - CreateFile(ModInputDir, "test.imd").Using(patchImd.Write); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "out.imz"); - File.OpenRead(Path.Combine(ModOutputDir, "out.imz")).Using(x => - { - var images = Imgz.Read(x).ToList(); - Assert.Equal(3, images.Count); - Assert.Equal(16, images[0].Size.Width); - Assert.Equal(32, images[1].Size.Width); - Assert.Equal(16, images[2].Size.Width); - }); - } - - [Fact] - public void Kh2MergeImzInsideBarTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "out.bar", - Method = "binarc", - Source = new List - { - new AssetFile - { - Name = "test", - Type = "imgz", - Method = "imgz", - Source = new List - { - new AssetFile - { - Name = "test.imd", - Index = 1 - } - }, - } - } - } - } - }; - - var tmpImd = Imgd.Create(new System.Drawing.Size(16, 16), PixelFormat.Indexed4, new byte[16 * 16 / 2], new byte[4], false); - var patchImd = Imgd.Create(new System.Drawing.Size(32, 16), PixelFormat.Indexed4, new byte[32 * 16 / 2], new byte[4], false); - CreateFile(AssetsInputDir, "out.bar").Using(x => - { - using var memoryStream = new MemoryStream(); - Imgz.Write(memoryStream, new Imgd[] - { - tmpImd, - tmpImd, - tmpImd, - }); - - Bar.Write(x, new Bar - { - new Bar.Entry - { - Name = "test", - Type = Bar.EntryType.Imgz, - Stream = memoryStream - } - }); - }); - CreateFile(ModInputDir, "test.imd").Using(patchImd.Write); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "out.bar"); - AssertBarFile("test", x => - { - var images = Imgz.Read(x.Stream).ToList(); - Assert.Equal(3, images.Count); - Assert.Equal(16, images[0].Size.Width); - Assert.Equal(32, images[1].Size.Width); - Assert.Equal(16, images[2].Size.Width); - }, ModOutputDir, "out.bar"); - } - - [Fact] - public void MergeKh2MsgTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "msg/us/sys.msg", - Method = "kh2msg", - Source = new List - { - new AssetFile - { - Name = "sys.yml", - Language = "en", - } - } - }, - new AssetFile - { - Name = "msg/it/sys.msg", - Method = "kh2msg", - Source = new List - { - new AssetFile - { - Name = "sys.yml", - Language = "it", - } - } - }, - new AssetFile - { - Name = "msg/jp/sys.msg", - Method = "kh2msg", - Source = new List - { - new AssetFile - { - Name = "sys.yml", - Language = "jp", - } - } - } - } - }; - - Directory.CreateDirectory(Path.Combine(AssetsInputDir, "msg/us/")); - File.Create(Path.Combine(AssetsInputDir, "msg/us/sys.msg")).Using(stream => - { - Msg.Write(stream, new List - { - new Msg.Entry - { - Data = new byte[] { 1, 2, 3, 0 }, - Id = 123 - } - }); - }); - File.Create(Path.Combine(ModInputDir, "sys.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- id: 456"); - writer.WriteLine(" en: English"); - writer.WriteLine(" it: Italiano"); - writer.WriteLine(" jp: テスト"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "msg/jp/sys.msg"); - File.OpenRead(Path.Combine(ModOutputDir, "msg/jp/sys.msg")).Using(stream => - { - var msg = Msg.Read(stream); - Assert.Single(msg); - Assert.Equal(456, msg[0].Id); - Assert.Equal("テスト", Encoders.JapaneseSystem.Decode(msg[0].Data).First().Text); - }); - - AssertFileExists(ModOutputDir, "msg/us/sys.msg"); - File.OpenRead(Path.Combine(ModOutputDir, "msg/us/sys.msg")).Using(stream => - { - var msg = Msg.Read(stream); - Assert.Equal(2, msg.Count); - Assert.Equal(123, msg[0].Id); - Assert.Equal(456, msg[1].Id); - Assert.Equal("English", Encoders.InternationalSystem.Decode(msg[1].Data).First().Text); - }); - - AssertFileExists(ModOutputDir, "msg/it/sys.msg"); - File.OpenRead(Path.Combine(ModOutputDir, "msg/it/sys.msg")).Using(stream => - { - var msg = Msg.Read(stream); - Assert.Single(msg); - Assert.Equal(456, msg[0].Id); - Assert.Equal("Italiano", Encoders.InternationalSystem.Decode(msg[0].Data).First().Text); - }); - } - - [Fact] - public void MergeKh2AreaDataScriptTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "map.script", - Method = "areadatascript", - Source = new List - { - new AssetFile - { - Name = "map.txt", - } - } - }, - } - }; - - File.Create(Path.Combine(AssetsInputDir, "map.script")).Using(stream => - { - var compiledProgram = Kh2.Ard.AreaDataScript.Compile("Program 1\nSpawn \"1111\""); - Kh2.Ard.AreaDataScript.Write(stream, compiledProgram); - }); - File.Create(Path.Combine(ModInputDir, "map.txt")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("Program 2"); - writer.WriteLine("Spawn \"2222\""); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "map.script"); - File.OpenRead(Path.Combine(ModOutputDir, "map.script")).Using(stream => - { - var scripts = Kh2.Ard.AreaDataScript.Read(stream); - var decompiled = Kh2.Ard.AreaDataScript.Decompile(scripts); - decompiled.Contains("Program 1"); - decompiled.Contains("Spawn \"1111\""); - decompiled.Contains("Program 2"); - decompiled.Contains("Spawn \"2222\""); - }); - } - - [Fact] - public void PatchKh2BdscriptTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "aaa", - Method = "bdscript", - Source = new List - { - new AssetFile - { - Name = "test.bdscript", - } - } - }, - } - }; - File.Create(Path.Combine(ModInputDir, "test.bdscript")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("---"); - writer.WriteLine("WorkSize: 64"); - writer.WriteLine("StackSize: 64"); - writer.WriteLine("TempSize: 64"); - writer.WriteLine("Triggers:"); - writer.WriteLine("- Key: 0"); - writer.WriteLine(" Addr: TR0"); - writer.WriteLine("Name: aaa"); - writer.WriteLine("---"); - writer.WriteLine(" section .text"); - writer.WriteLine("TR0:"); - writer.WriteLine(" ret"); - writer.WriteLine("DUMMY:"); - writer.WriteLine(" ret"); - writer.Flush(); - }); - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "aaa"); - - var bdxStream = new MemoryStream(File.ReadAllBytes(Path.Combine(ModOutputDir, "aaa"))); - var decoder = new BdxDecoder(bdxStream); - var script = BdxDecoder.TextFormatter.Format(decoder); - - var lines = script.Split("\r\n"); - - Assert.Equal("WorkSize: 64", lines[1]); - Assert.Equal("Name: aaa", lines[7]); - - } - - [Fact] - public void PatchKh2SpawnPointTest() - { - - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "map.script", - Method = "areadataspawn", - Source = new List - { - new AssetFile - { - Name = "map.yaml", - } - } - }, - } - }; - - File.Create(Path.Combine(AssetsInputDir, "map.script")).Using(stream => - { - var spawnPoint = new List(); - - Kh2.Ard.SpawnPoint.Write(stream, spawnPoint); - }); - File.Create(Path.Combine(ModInputDir, "map.yaml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Type: 2"); - writer.WriteLine(" Flag: 1"); - writer.Flush(); - }); - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "map.script"); - var content = File.ReadAllText(Path.Combine(ModOutputDir, "map.script")); - var scripts = new Deserializer().Deserialize> (content); - - Assert.Equal(2, scripts[0].Type); - Assert.Equal(1, scripts[0].Flag); - - } - - - public void ListPatchTrsrTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "trsr", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "TrsrList.yml", - Type = "trsr" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var trsrEntry = new List() - { - new Kh2.SystemData.Trsr - { - Id = 1, - ItemId = 10 - } - }; - using var trsrStream = new MemoryStream(); - Kh2.SystemData.Trsr.Write(trsrStream, trsrEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "trsr", - Type = Bar.EntryType.List, - Stream = trsrStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "TrsrList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("1:"); - writer.WriteLine(" ItemId: 200"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var trsrStream = Kh2.SystemData.Trsr.Read(binarc[0].Stream); - Assert.Equal(200, trsrStream[0].ItemId); - }); - - } - - [Fact] - public void ListPatchCmdTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "cmd", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "CmdList.yml", - Type = "cmd" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var cmdEntry = new List() - { - new Kh2.SystemData.Cmd - { - Id = 1, - Execute = 3, - Argument = 3, - SubMenu = 1, - } - }; - using var cmdStream = new MemoryStream(); - Kh2.SystemData.Cmd.Write(cmdStream, cmdEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "cmd", - Type = Bar.EntryType.List, - Stream = cmdStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "CmdList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Id: 1"); - writer.WriteLine(" Execute: 3"); - writer.WriteLine(" Argument: 3"); - writer.WriteLine(" SubMenu: 1"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var cmdStream = Kh2.SystemData.Cmd.Read(binarc[0].Stream); - Assert.Equal(1, cmdStream[0].Id); - Assert.Equal(3, cmdStream[0].Execute); - Assert.Equal(3, cmdStream[0].Argument); - Assert.Equal(1, cmdStream[0].SubMenu); - }); - - } - - [Fact] - public void ListPatchItemTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "item", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "ItemList.yml", - Type = "item" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var itemEntry = new List() - { - new Kh2.SystemData.Item - { - Items = new List() - { - new Kh2.SystemData.Item.Entry() - { - Id = 1, - ShopBuy = 10 - } - }, - Stats = new List() - { - new Kh2.SystemData.Item.Stat() - { - Id = 10, - Ability = 15 - } - } - - } - }; - using var itemStream = new MemoryStream(); - itemEntry[0].Write(itemStream); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "item", - Type = Bar.EntryType.List, - Stream = itemStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "ItemList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("Items:"); - writer.WriteLine("- Id: 1"); - writer.WriteLine(" ShopBuy: 200"); - writer.WriteLine("Stats:"); - writer.WriteLine("- Id: 10"); - writer.WriteLine(" Ability: 150"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var itemStream = Kh2.SystemData.Item.Read(binarc[0].Stream); - Assert.Equal(200, itemStream.Items[0].ShopBuy); - Assert.Equal(150, itemStream.Stats[0].Ability); - }); - - } - - [Fact] - public void ListPatchSkltTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "sklt", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "SkltList.yml", - Type = "sklt" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var skltEntry = new List() - { - new Kh2.SystemData.Sklt - { - CharacterId = 1, - Bone1 = 178, - Bone2 = 86 - } - }; - - using var skltStream = new MemoryStream(); - Kh2.SystemData.Sklt.Write(skltStream, skltEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "sklt", - Type = Bar.EntryType.List, - Stream = skltStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "SkltList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- CharacterId: 1"); - writer.WriteLine(" Bone1: 178"); - writer.WriteLine(" Bone2: 86"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var skltStream = Kh2.SystemData.Sklt.Read(binarc[0].Stream); - Assert.Equal(1U, skltStream[0].CharacterId); - Assert.Equal(178, skltStream[0].Bone1); - Assert.Equal(86, skltStream[0].Bone2); - }); +using OpenKh.Command.Bdxio.Utils; +using OpenKh.Common; +using OpenKh.Imaging; +using OpenKh.Kh2; +using OpenKh.Kh2.Messages; +using OpenKh.Patcher; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Xunit; +using Xunit.Sdk; +using YamlDotNet.Serialization; + +namespace OpenKh.Tests.Patcher +{ + public class PatcherTests : IDisposable + { + private const string AssetsInputDir = "original_input"; + private const string ModInputDir = "mod_input"; + private const string ModOutputDir = "mod_output"; + + public PatcherTests() + { + Dispose(); + Directory.CreateDirectory(AssetsInputDir); + Directory.CreateDirectory(ModInputDir); + Directory.CreateDirectory(ModOutputDir); + } + + public void Dispose() + { + if (Directory.Exists(AssetsInputDir)) + Directory.Delete(AssetsInputDir, true); + if (Directory.Exists(ModInputDir)) + Directory.Delete(ModInputDir, true); + if (Directory.Exists(ModOutputDir)) + Directory.Delete(ModOutputDir, true); + } + + [Fact] + public void Kh2CopyBinariesTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "somedir/somefile.bin", + Method = "copy", + Source = new List + { + new AssetFile + { + Name = "somedir/somefile.bin" + } + } + } + } + }; + + CreateFile(ModInputDir, patch.Assets[0].Name).Dispose(); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, patch.Assets[0].Name); + } + + [Fact] + public void Kh2CreateBinArcIfSourceDoesntExistsTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "somedir/somefile.bar", + Method = "binarc", + Source = new List + { + new AssetFile + { + Name = "abcd", + Type = "list", + Method = "copy", + Source = new List + { + new AssetFile + { + Name = "somedir/somefile/abcd.bin" + } + } + } + } + } + } + }; + + CreateFile(ModInputDir, "somedir/somefile/abcd.bin").Using(x => + { + x.WriteByte(0); + x.WriteByte(1); + x.WriteByte(2); + x.WriteByte(3); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, patch.Assets[0].Name); + AssertBarFile("abcd", entry => + { + Assert.Equal(Bar.EntryType.List, entry.Type); + Assert.Equal(4, entry.Stream.Length); + Assert.Equal(0, entry.Stream.ReadByte()); + Assert.Equal(1, entry.Stream.ReadByte()); + Assert.Equal(2, entry.Stream.ReadByte()); + Assert.Equal(3, entry.Stream.ReadByte()); + }, ModOutputDir, patch.Assets[0].Name); + } + + [Fact] + public void Kh2MergeWithOriginalBinArcTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "somedir/somefile.bar", + Method = "binarc", + Source = new List + { + new AssetFile + { + Name = "abcd", + Type = "list", + Method = "copy", + Source = new List + { + new AssetFile + { + Name = "somedir/somefile/abcd.bin" + } + } + } + } + } + } + }; + + CreateFile(ModInputDir, "somedir/somefile/abcd.bin").Using(x => + { + x.WriteByte(0); + x.WriteByte(1); + x.WriteByte(2); + x.WriteByte(3); + }); + + CreateFile(AssetsInputDir, "somedir/somefile.bar").Using(x => + { + Bar.Write(x, new Bar + { + new Bar.Entry + { + Name = "nice", + Type = Bar.EntryType.Model, + Stream = new MemoryStream(new byte[] { 4, 5, 6, 7 }) + } + }); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, patch.Assets[0].Name); + AssertBarFile("abcd", entry => + { + Assert.Equal(Bar.EntryType.List, entry.Type); + Assert.Equal(4, entry.Stream.Length); + Assert.Equal(0, entry.Stream.ReadByte()); + Assert.Equal(1, entry.Stream.ReadByte()); + Assert.Equal(2, entry.Stream.ReadByte()); + Assert.Equal(3, entry.Stream.ReadByte()); + }, ModOutputDir, patch.Assets[0].Name); + AssertBarFile("nice", entry => + { + Assert.Equal(Bar.EntryType.Model, entry.Type); + Assert.Equal(4, entry.Stream.Length); + Assert.Equal(4, entry.Stream.ReadByte()); + Assert.Equal(5, entry.Stream.ReadByte()); + Assert.Equal(6, entry.Stream.ReadByte()); + Assert.Equal(7, entry.Stream.ReadByte()); + }, ModOutputDir, patch.Assets[0].Name); + } + + [Fact] + public void Kh2CreateImgdTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "somedir/somefile.bar", + Method = "binarc", + Source = new List + { + new AssetFile + { + Name = "test", + Method = "imgd", + Type = "imgd", + Source = new List + { + new AssetFile + { + Name = "sample.png", + IsSwizzled = false + } + } + } + } + } + } + }; + + File.Copy("Imaging/res/png/32.png", Path.Combine(ModInputDir, "sample.png")); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, patch.Assets[0].Name); + AssertBarFile("test", entry => + { + Assert.True(Imgd.IsValid(entry.Stream)); + }, ModOutputDir, patch.Assets[0].Name); + } + + [Fact] + public void Kh2MergeImzTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "out.imz", + Method = "imgz", + Source = new List + { + new AssetFile + { + Name = "test.imd", + Index = 1, + } + } + } + } + }; + + var tmpImd = Imgd.Create(new System.Drawing.Size(16, 16), PixelFormat.Indexed4, new byte[16 * 16 / 2], new byte[4], false); + var patchImd = Imgd.Create(new System.Drawing.Size(32, 16), PixelFormat.Indexed4, new byte[32 * 16 / 2], new byte[4], false); + CreateFile(AssetsInputDir, "out.imz").Using(x => + { + Imgz.Write(x, new Imgd[] + { + tmpImd, + tmpImd, + tmpImd, + }); + }); + CreateFile(ModInputDir, "test.imd").Using(patchImd.Write); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "out.imz"); + File.OpenRead(Path.Combine(ModOutputDir, "out.imz")).Using(x => + { + var images = Imgz.Read(x).ToList(); + Assert.Equal(3, images.Count); + Assert.Equal(16, images[0].Size.Width); + Assert.Equal(32, images[1].Size.Width); + Assert.Equal(16, images[2].Size.Width); + }); + } + + [Fact] + public void Kh2MergeImzInsideBarTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "out.bar", + Method = "binarc", + Source = new List + { + new AssetFile + { + Name = "test", + Type = "imgz", + Method = "imgz", + Source = new List + { + new AssetFile + { + Name = "test.imd", + Index = 1 + } + }, + } + } + } + } + }; + + var tmpImd = Imgd.Create(new System.Drawing.Size(16, 16), PixelFormat.Indexed4, new byte[16 * 16 / 2], new byte[4], false); + var patchImd = Imgd.Create(new System.Drawing.Size(32, 16), PixelFormat.Indexed4, new byte[32 * 16 / 2], new byte[4], false); + CreateFile(AssetsInputDir, "out.bar").Using(x => + { + using var memoryStream = new MemoryStream(); + Imgz.Write(memoryStream, new Imgd[] + { + tmpImd, + tmpImd, + tmpImd, + }); + + Bar.Write(x, new Bar + { + new Bar.Entry + { + Name = "test", + Type = Bar.EntryType.Imgz, + Stream = memoryStream + } + }); + }); + CreateFile(ModInputDir, "test.imd").Using(patchImd.Write); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "out.bar"); + AssertBarFile("test", x => + { + var images = Imgz.Read(x.Stream).ToList(); + Assert.Equal(3, images.Count); + Assert.Equal(16, images[0].Size.Width); + Assert.Equal(32, images[1].Size.Width); + Assert.Equal(16, images[2].Size.Width); + }, ModOutputDir, "out.bar"); + } + + [Fact] + public void MergeKh2MsgTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "msg/us/sys.msg", + Method = "kh2msg", + Source = new List + { + new AssetFile + { + Name = "sys.yml", + Language = "en", + } + } + }, + new AssetFile + { + Name = "msg/it/sys.msg", + Method = "kh2msg", + Source = new List + { + new AssetFile + { + Name = "sys.yml", + Language = "it", + } + } + }, + new AssetFile + { + Name = "msg/jp/sys.msg", + Method = "kh2msg", + Source = new List + { + new AssetFile + { + Name = "sys.yml", + Language = "jp", + } + } + } + } + }; + + Directory.CreateDirectory(Path.Combine(AssetsInputDir, "msg/us/")); + File.Create(Path.Combine(AssetsInputDir, "msg/us/sys.msg")).Using(stream => + { + Msg.Write(stream, new List + { + new Msg.Entry + { + Data = new byte[] { 1, 2, 3, 0 }, + Id = 123 + } + }); + }); + File.Create(Path.Combine(ModInputDir, "sys.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- id: 456"); + writer.WriteLine(" en: English"); + writer.WriteLine(" it: Italiano"); + writer.WriteLine(" jp: テスト"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "msg/jp/sys.msg"); + File.OpenRead(Path.Combine(ModOutputDir, "msg/jp/sys.msg")).Using(stream => + { + var msg = Msg.Read(stream); + Assert.Single(msg); + Assert.Equal(456, msg[0].Id); + Assert.Equal("テスト", Encoders.JapaneseSystem.Decode(msg[0].Data).First().Text); + }); + + AssertFileExists(ModOutputDir, "msg/us/sys.msg"); + File.OpenRead(Path.Combine(ModOutputDir, "msg/us/sys.msg")).Using(stream => + { + var msg = Msg.Read(stream); + Assert.Equal(2, msg.Count); + Assert.Equal(123, msg[0].Id); + Assert.Equal(456, msg[1].Id); + Assert.Equal("English", Encoders.InternationalSystem.Decode(msg[1].Data).First().Text); + }); + + AssertFileExists(ModOutputDir, "msg/it/sys.msg"); + File.OpenRead(Path.Combine(ModOutputDir, "msg/it/sys.msg")).Using(stream => + { + var msg = Msg.Read(stream); + Assert.Single(msg); + Assert.Equal(456, msg[0].Id); + Assert.Equal("Italiano", Encoders.InternationalSystem.Decode(msg[0].Data).First().Text); + }); + } + + [Fact] + public void MergeKh2AreaDataScriptTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "map.script", + Method = "areadatascript", + Source = new List + { + new AssetFile + { + Name = "map.txt", + } + } + }, + } + }; + + File.Create(Path.Combine(AssetsInputDir, "map.script")).Using(stream => + { + var compiledProgram = Kh2.Ard.AreaDataScript.Compile("Program 1\nSpawn \"1111\""); + Kh2.Ard.AreaDataScript.Write(stream, compiledProgram); + }); + File.Create(Path.Combine(ModInputDir, "map.txt")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("Program 2"); + writer.WriteLine("Spawn \"2222\""); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "map.script"); + File.OpenRead(Path.Combine(ModOutputDir, "map.script")).Using(stream => + { + var scripts = Kh2.Ard.AreaDataScript.Read(stream); + var decompiled = Kh2.Ard.AreaDataScript.Decompile(scripts); + decompiled.Contains("Program 1"); + decompiled.Contains("Spawn \"1111\""); + decompiled.Contains("Program 2"); + decompiled.Contains("Spawn \"2222\""); + }); + } + + [Fact] + public void PatchKh2BdscriptTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "aaa", + Method = "bdscript", + Source = new List + { + new AssetFile + { + Name = "test.bdscript", + } + } + }, + } + }; + File.Create(Path.Combine(ModInputDir, "test.bdscript")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("---"); + writer.WriteLine("WorkSize: 64"); + writer.WriteLine("StackSize: 64"); + writer.WriteLine("TempSize: 64"); + writer.WriteLine("Triggers:"); + writer.WriteLine("- Key: 0"); + writer.WriteLine(" Addr: TR0"); + writer.WriteLine("Name: aaa"); + writer.WriteLine("---"); + writer.WriteLine(" section .text"); + writer.WriteLine("TR0:"); + writer.WriteLine(" ret"); + writer.WriteLine("DUMMY:"); + writer.WriteLine(" ret"); + writer.Flush(); + }); + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "aaa"); + + var bdxStream = new MemoryStream(File.ReadAllBytes(Path.Combine(ModOutputDir, "aaa"))); + var decoder = new BdxDecoder(bdxStream); + var script = BdxDecoder.TextFormatter.Format(decoder); + + var lines = script.Split("\r\n"); + + Assert.Equal("WorkSize: 64", lines[1]); + Assert.Equal("Name: aaa", lines[7]); + + } + + [Fact] + public void PatchKh2SpawnPointTest() + { + + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "map.script", + Method = "areadataspawn", + Source = new List + { + new AssetFile + { + Name = "map.yaml", + } + } + }, + } + }; + + File.Create(Path.Combine(AssetsInputDir, "map.script")).Using(stream => + { + var spawnPoint = new List(); + + Kh2.Ard.SpawnPoint.Write(stream, spawnPoint); + }); + File.Create(Path.Combine(ModInputDir, "map.yaml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Type: 2"); + writer.WriteLine(" Flag: 1"); + writer.Flush(); + }); + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "map.script"); + var content = File.ReadAllText(Path.Combine(ModOutputDir, "map.script")); + var scripts = new Deserializer().Deserialize> (content); + + Assert.Equal(2, scripts[0].Type); + Assert.Equal(1, scripts[0].Flag); + + } + + + public void ListPatchTrsrTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "trsr", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "TrsrList.yml", + Type = "trsr" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + { + var trsrEntry = new List() + { + new Kh2.SystemData.Trsr + { + Id = 1, + ItemId = 10 + } + }; + using var trsrStream = new MemoryStream(); + Kh2.SystemData.Trsr.Write(trsrStream, trsrEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "trsr", + Type = Bar.EntryType.List, + Stream = trsrStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "TrsrList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("1:"); + writer.WriteLine(" ItemId: 200"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "03system.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var trsrStream = Kh2.SystemData.Trsr.Read(binarc[0].Stream); + Assert.Equal(200, trsrStream[0].ItemId); + }); + + } + + [Fact] + public void ListPatchCmdTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "cmd", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "CmdList.yml", + Type = "cmd" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + { + var cmdEntry = new List() + { + new Kh2.SystemData.Cmd + { + Id = 1, + Execute = 3, + Argument = 3, + SubMenu = 1, + } + }; + using var cmdStream = new MemoryStream(); + Kh2.SystemData.Cmd.Write(cmdStream, cmdEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "cmd", + Type = Bar.EntryType.List, + Stream = cmdStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "CmdList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Id: 1"); + writer.WriteLine(" Execute: 3"); + writer.WriteLine(" Argument: 3"); + writer.WriteLine(" SubMenu: 1"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "03system.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var cmdStream = Kh2.SystemData.Cmd.Read(binarc[0].Stream); + Assert.Equal(1, cmdStream[0].Id); + Assert.Equal(3, cmdStream[0].Execute); + Assert.Equal(3, cmdStream[0].Argument); + Assert.Equal(1, cmdStream[0].SubMenu); + }); + + } + + [Fact] + public void ListPatchItemTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "item", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "ItemList.yml", + Type = "item" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + { + var itemEntry = new List() + { + new Kh2.SystemData.Item + { + Items = new List() + { + new Kh2.SystemData.Item.Entry() + { + Id = 1, + ShopBuy = 10 + } + }, + Stats = new List() + { + new Kh2.SystemData.Item.Stat() + { + Id = 10, + Ability = 15 + } + } + + } + }; + using var itemStream = new MemoryStream(); + itemEntry[0].Write(itemStream); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "item", + Type = Bar.EntryType.List, + Stream = itemStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "ItemList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("Items:"); + writer.WriteLine("- Id: 1"); + writer.WriteLine(" ShopBuy: 200"); + writer.WriteLine("Stats:"); + writer.WriteLine("- Id: 10"); + writer.WriteLine(" Ability: 150"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "03system.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var itemStream = Kh2.SystemData.Item.Read(binarc[0].Stream); + Assert.Equal(200, itemStream.Items[0].ShopBuy); + Assert.Equal(150, itemStream.Stats[0].Ability); + }); + + } + + [Fact] + public void ListPatchSkltTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "sklt", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "SkltList.yml", + Type = "sklt" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + { + var skltEntry = new List() + { + new Kh2.SystemData.Sklt + { + CharacterId = 1, + Bone1 = 178, + Bone2 = 86 + } + }; + + using var skltStream = new MemoryStream(); + Kh2.SystemData.Sklt.Write(skltStream, skltEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "sklt", + Type = Bar.EntryType.List, + Stream = skltStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "SkltList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- CharacterId: 1"); + writer.WriteLine(" Bone1: 178"); + writer.WriteLine(" Bone2: 86"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "03system.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var skltStream = Kh2.SystemData.Sklt.Read(binarc[0].Stream); + Assert.Equal(1U, skltStream[0].CharacterId); + Assert.Equal(178, skltStream[0].Bone1); + Assert.Equal(86, skltStream[0].Bone2); + }); + } + + [Fact] //Fixed, needed to initialize the BGMSet & Reserved bytes. + public void ListPatchArifTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bin", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "arif", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "ArifList.yml", + Type = "arif" + } + } + } + } + } + } + }; + + using (var stream = File.Create(Path.Combine(AssetsInputDir, "03system.bin"))) + { + var arifEntry = new Kh2.SystemData.Arif + { + Flags = Kh2.SystemData.Arif.ArifFlags.IsKnownArea, + Reverb = 0, + SoundEffectBank1 = 13, + SoundEffectBank2 = 0, + Bgms = Enumerable.Range(0, 8).Select(_ => new Kh2.SystemData.BgmSet { BgmField = 0, BgmBattle = 0 }).ToArray(), + Voice = 0, + NavigationMapItem = 0, + Command = 0, + Reserved = new byte[11] + }; + + using (var arifStream = new MemoryStream()) + { + Kh2.SystemData.Arif.Write(arifStream, new List> { new List { arifEntry } }); + Bar.Write(stream, new Bar + { + new Bar.Entry() + { + Name = "arif", + Type = Bar.EntryType.List, + Stream = arifStream + } + }); + } + } + + File.Create(Path.Combine(ModInputDir, "ArifList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("EndOfSea:"); + writer.WriteLine(" 1:"); + writer.WriteLine(" SoundEffectBank1: 13"); + writer.WriteLine(" Voice: 0"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "03system.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bin")).Using(stream => + { + var binarc = Bar.Read(stream); + var arifList = Kh2.SystemData.Arif.Read(binarc[0].Stream); + var arifEntry = arifList[0][0]; + Assert.Equal(13, arifEntry.SoundEffectBank1); + Assert.Equal(0, arifEntry.Voice); + }); + } + + [Fact] + public void ListPatchMemtTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "memt", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "MemtList.yml", + Type = "memt" + } + } + } + } + } + } + }; + + // Create the 03system.bar file with initial data + File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + { + var memtEntries = new List() + { + new Kh2.SystemData.Memt.EntryFinalMix + { + WorldId = 1, + CheckStoryFlag = 1, + CheckStoryFlagNegation = 0, + Unk06 = 0, + Unk08 = 0, + Unk0A = 0, + Unk0C = 0, + Unk0E = 0, + Members = new short[18] + } + }; + + var memberIndices = new Kh2.SystemData.Memt.MemberIndices[7]; // Ensure there are 7 MemberIndices + for (int i = 0; i < memberIndices.Length; i++) + { + memberIndices[i] = new Kh2.SystemData.Memt.MemberIndices + { + Player = 0, + Friend1 = 0, + Friend2 = 0, + FriendWorld = 0 + }; + } + + using var memtStream = new MemoryStream(); + var memt = new Kh2.SystemData.Memt(); + memt.Entries.AddRange(memtEntries.Cast()); + + Kh2.SystemData.Memt.Write(memtStream, memt); + memtStream.Seek(0, SeekOrigin.Begin); + + Bar.Write(stream, new Bar() + { + new Bar.Entry() + { + Name = "memt", + Type = Bar.EntryType.List, + Stream = memtStream + } + }); + }); + + // Create the MemtList.yml patch file + File.Create(Path.Combine(ModInputDir, "MemtList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("MemtEntries:"); + writer.WriteLine(" - Index: 0"); + writer.WriteLine(" WorldId: 2"); + writer.WriteLine(" CheckStoryFlag: 3"); + writer.WriteLine(" CheckStoryFlagNegation: 4"); + writer.WriteLine(" Unk06: 5"); + writer.WriteLine(" Unk08: 6"); + writer.WriteLine(" Unk0A: 7"); + writer.WriteLine(" Unk0C: 8"); + writer.WriteLine(" Unk0E: 9"); + writer.WriteLine(" Members: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]"); + writer.WriteLine("MemberIndices:"); + writer.WriteLine(" - Index: 0"); + writer.WriteLine(" Player: 0"); + writer.WriteLine(" Friend1: 0"); + writer.WriteLine(" Friend2: 0"); + writer.WriteLine(" FriendWorld: 0"); + writer.Flush(); + }); + + // Apply the patch + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + // Verify the patched data + AssertFileExists(ModOutputDir, "03system.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var memtStream = binarc[0].Stream; + memtStream.Seek(0, SeekOrigin.Begin); + + var memt = Kh2.SystemData.Memt.Read(memtStream); + + var memtEntry = memt.Entries.Cast().First(); + Assert.Equal((short)2, memtEntry.WorldId); + Assert.Equal((short)3, memtEntry.CheckStoryFlag); + Assert.Equal((short)4, memtEntry.CheckStoryFlagNegation); + Assert.Equal((short)5, memtEntry.Unk06); + Assert.Equal((short)6, memtEntry.Unk08); + Assert.Equal((short)7, memtEntry.Unk0A); + Assert.Equal((short)8, memtEntry.Unk0C); + Assert.Equal((short)9, memtEntry.Unk0E); + Assert.Equal(new short[] { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }, memtEntry.Members); + + // Check MemberIndices deserialization + var memberIndices = memt.MemberIndexCollection.First(); + Assert.Equal((byte)0, memberIndices.Player); + Assert.Equal((byte)0, memberIndices.Friend1); + Assert.Equal((byte)0, memberIndices.Friend2); + Assert.Equal((byte)0, memberIndices.FriendWorld); + }); + } + + + + [Fact] + public void ListPatchFmabTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "03system.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "pref", + Method = "binarc", + Type = "Binary", + Source = new List() + { + new AssetFile() + { + Name = "fmab", + Method = "listpatch", + Type = "list", + Source = new List() + { + new AssetFile() + { + Name = "FmabList.yml", + Type = "fmab" + } + } + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + { + var FmabEntry = new List() + { + new Kh2.SystemData.Fmab + { + HighJumpHeight = 178, + AirDodgeHeight = 86 + } + }; + + using var fmabStream = new MemoryStream(); + Kh2.SystemData.Fmab.Write(fmabStream, FmabEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "fmab", + Type = Bar.EntryType.List, + Stream = fmabStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "FmabList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- HighJumpHeight: 178"); + writer.WriteLine(" AirDodgeHeight: 86"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "03system.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var fmabStream = Kh2.SystemData.Fmab.Read(binarc[0].Stream); + Assert.Equal(178, fmabStream[0].HighJumpHeight); + Assert.Equal(86, fmabStream[0].AirDodgeHeight); + }); + } + + [Fact] + public void ListPatchFmlvTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "fmlv", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "FmlvList.yml", + Type = "fmlv" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var fmlvEntry = new List() + { + new Kh2.Battle.Fmlv + { + FormId = 1, + FormLevel = 1, + Exp = 100, + Ability = 200 + }, + new Kh2.Battle.Fmlv + { + FormId = 1, + FormLevel = 2, + Exp = 100, + Ability = 200 + }, + new Kh2.Battle.Fmlv + { + FormId = 2, + FormLevel = 1, + Exp = 100, + Ability = 200 + }, + }; + + using var fmlvStream = new MemoryStream(); + Kh2.Battle.Fmlv.Write(fmlvStream, fmlvEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "fmlv", + Type = Bar.EntryType.List, + Stream = fmlvStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "FmlvList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + var serializer = new Serializer(); + serializer.Serialize(writer, new Dictionary + { + ["Valor"] = new[] + { + new FmlvDTO + { + FormLevel = 1, + Experience = 5, + Ability = 127 + } + } + }); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00battle.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var fmlv = Kh2.Battle.Fmlv.Read(binarc[0].Stream); + + Assert.Equal(3, fmlv.Count); + Assert.Equal(1, fmlv[0].FormId); + Assert.Equal(1, fmlv[0].FormLevel); + Assert.Equal(5, fmlv[0].Exp); + Assert.Equal(127, fmlv[0].Ability); + + Assert.Equal(1, fmlv[1].FormId); + Assert.Equal(2, fmlv[1].FormLevel); + + Assert.Equal(2, fmlv[2].FormId); + Assert.Equal(1, fmlv[2].FormLevel); + }); + } + + [Fact] + public void ListPatchBonsTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "bons", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "BonsList.yml", + Type = "bons" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var bonsEntry = new List() + { + new Kh2.Battle.Bons + { + CharacterId = 1, + RewardId = 15, + BonusItem1 = 10 + }, + new Kh2.Battle.Bons + { + CharacterId = 2, + RewardId = 15, + BonusItem1 = 5 + } + }; + using var bonsStream = new MemoryStream(); + Kh2.Battle.Bons.Write(bonsStream, bonsEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "bons", + Type = Bar.EntryType.List, + Stream = bonsStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "BonsList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("15:"); + writer.WriteLine(" Sora:"); + writer.WriteLine(" BonusItem1: 200"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00battle.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var bonsStream = Kh2.Battle.Bons.Read(binarc[0].Stream); + Assert.Equal(200, bonsStream[0].BonusItem1); + Assert.Equal(5, bonsStream[1].BonusItem1); + }); + + } + + [Fact] + public void ListPatchLvupTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bin", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "lvup", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "LvupList.yml", + Type = "lvup" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bin")).Using(stream => + { + var lvupEntry = new Kh2.Battle.Lvup + { + Count = 13, + Unknown08 = new byte[0x38], + Characters = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + }, + new Kh2.Battle.Lvup.PlayableCharacter() + { + NumLevels = 1, + Levels = new List() + { + new Kh2.Battle.Lvup.PlayableCharacter.Level() + { + Exp = 50 + } + } + } + + } + }; + using var lvupStream = new MemoryStream(); + lvupEntry.Write(lvupStream); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "lvup", + Type = Bar.EntryType.List, + Stream = lvupStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "LvupList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("Sora:"); + writer.WriteLine(" 1:"); + writer.WriteLine(" Exp: 500"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00battle.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bin")).Using(stream => + { + var binarc = Bar.Read(stream); + var lvupStream = Kh2.Battle.Lvup.Read(binarc[0].Stream); + Assert.Equal(500, lvupStream.Characters[0].Levels[0].Exp); + }); + + } + + [Fact] + void ListPatchAtkpTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "atkp", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "AtkpList.yml", + Type = "atkp" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var atkpEntry = new List() + { + new Kh2.Battle.Atkp + { + Id = 0, + SubId = 3, + Type = Kh2.Battle.Atkp.AttackType.PierceArmor, + CriticalAdjust = 0, + Power = 25, + Team = 0, + Element = 0, + EnemyReaction = 0, + EffectOnHit = 2, + KnockbackStrength1 = 32767, + KnockbackStrength2 = 0, + Unknown = 0, + Flags = Kh2.Battle.Atkp.AttackFlags.BGHit, + RefactSelf = Kh2.Battle.Atkp.Refact.Reflect, + RefactOther = Kh2.Battle.Atkp.Refact.Reflect, + ReflectedMotion = 0, + ReflectHitBack = 0, + ReflectAction = 0, + ReflectHitSound = 0, + ReflectRC = 0, + ReflectRange = 0, + ReflectAngle = 0, + DamageEffect = 0, + Switch = 1, + Interval = 1, + FloorCheck = 1, + DriveDrain = 1, + RevengeDamage = 1, + AttackTrReaction = Kh2.Battle.Atkp.TrReaction.Charge, + ComboGroup = 1, + RandomEffect = 1, + Kind = Kh2.Battle.Atkp.AttackKind.ComboFinisher, + HpDrain = 15 + } + }; + + using var atkpStream = new MemoryStream(); + Kh2.Battle.Atkp.Write(atkpStream, atkpEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "atkp", + Type = Bar.EntryType.List, + Stream = atkpStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "AtkpList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Id: 0"); + writer.WriteLine(" SubId: 3"); + writer.WriteLine(" Type: PierceArmor"); + writer.WriteLine(" CriticalAdjust: 0"); + writer.WriteLine(" Power: 25"); + writer.WriteLine(" Team: 0"); + writer.WriteLine(" Element: 0"); + writer.WriteLine(" EnemyReaction: 0"); + writer.WriteLine(" EffectOnHit: 2"); + writer.WriteLine(" KnockbackStrength1: 32767"); + writer.WriteLine(" KnockbackStrength2: 0"); + writer.WriteLine(" Unknown: 0"); + writer.WriteLine(" Flags: BGHit"); + writer.WriteLine(" RefactSelf: 0"); + writer.WriteLine(" RefactOther: 0"); + writer.WriteLine(" ReflectedMotion: 0"); + writer.WriteLine(" ReflectHitBack: 0"); + writer.WriteLine(" ReflectAction: 0"); + writer.WriteLine(" ReflectHitSound: 0"); + writer.WriteLine(" ReflectRC: 0"); + writer.WriteLine(" ReflectRange: 0"); + writer.WriteLine(" ReflectAngle: 0"); + writer.WriteLine(" DamageEffect: 0"); + writer.WriteLine(" Switch: 1"); + writer.WriteLine(" Interval: 1"); + writer.WriteLine(" FloorCheck: 1"); + writer.WriteLine(" DriveDrain: 1"); + writer.WriteLine(" RevengeDamage: 1"); + writer.WriteLine(" AttackTrReaction: 1"); + writer.WriteLine(" ComboGroup: 1"); + writer.WriteLine(" RandomEffect: 1"); + writer.WriteLine(" Kind: ComboFinisher"); + writer.WriteLine(" HpDrain: 15"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00battle.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var atkpStream = Kh2.Battle.Atkp.Read(binarc[0].Stream); + Assert.Equal(0, atkpStream[0].Id); + Assert.Equal(3, atkpStream[0].SubId); + Assert.Equal(Kh2.Battle.Atkp.AttackType.PierceArmor, atkpStream[0].Type); + Assert.Equal(0, atkpStream[0].CriticalAdjust); + Assert.Equal(25, atkpStream[0].Power); + Assert.Equal(0, atkpStream[0].Team); + Assert.Equal(0, atkpStream[0].Element); + Assert.Equal(0, atkpStream[0].EnemyReaction); + Assert.Equal(2, atkpStream[0].EffectOnHit); + Assert.Equal(32767, atkpStream[0].KnockbackStrength1); + Assert.Equal(0, atkpStream[0].KnockbackStrength2); + Assert.Equal(0000, atkpStream[0].Unknown); + Assert.Equal(Kh2.Battle.Atkp.AttackFlags.BGHit, atkpStream[0].Flags); + Assert.Equal(Kh2.Battle.Atkp.Refact.Reflect, atkpStream[0].RefactSelf); + Assert.Equal(Kh2.Battle.Atkp.Refact.Reflect, atkpStream[0].RefactOther); + Assert.Equal(0, atkpStream[0].ReflectedMotion); + Assert.Equal(0, atkpStream[0].ReflectHitBack); + Assert.Equal(0, atkpStream[0].ReflectAction); + Assert.Equal(0, atkpStream[0].ReflectHitSound); + Assert.Equal(0, atkpStream[0].ReflectRC); + Assert.Equal(0, atkpStream[0].ReflectRange); + Assert.Equal(0, atkpStream[0].ReflectAngle); + Assert.Equal(0, atkpStream[0].DamageEffect); + Assert.Equal(1, atkpStream[0].Switch); + Assert.Equal(1, atkpStream[0].Interval); + Assert.Equal(1, atkpStream[0].FloorCheck); + Assert.Equal(1, atkpStream[0].DriveDrain); + Assert.Equal(1, atkpStream[0].RevengeDamage); + Assert.Equal(Kh2.Battle.Atkp.TrReaction.Charge, atkpStream[0].AttackTrReaction); + Assert.Equal(1, atkpStream[0].ComboGroup); + Assert.Equal(1, atkpStream[0].RandomEffect); + Assert.Equal(Kh2.Battle.Atkp.AttackKind.ComboFinisher, atkpStream[0].Kind); + Assert.Equal(15, atkpStream[0].HpDrain); + }); + } + + [Fact] + public void ListPatchPrztTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "przt", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "PrztList.yml", + Type = "przt" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var prztEntry = new List() + { + new Kh2.Battle.Przt + { + Id = 1, + SmallHpOrbs = 0, + BigHpOrbs = 1 + } + }; + + using var prztStream = new MemoryStream(); + Kh2.Battle.Przt.Write(prztStream, prztEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "przt", + Type = Bar.EntryType.List, + Stream = prztStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "PrztList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + var serializer = new Serializer(); + var moddedPrzt = new List{ + new Kh2.Battle.Przt + { + Id = 1, + SmallHpOrbs = 0, + BigHpOrbs = 1 + } + }; + writer.Write(serializer.Serialize(moddedPrzt)); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00battle.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var przt = Kh2.Battle.Przt.Read(binarc[0].Stream); + + Assert.Equal(1, przt[0].BigHpOrbs); + }); + } + + + [Fact] + public void ListPatchObjEntryTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00objentry.bin", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "ObjList.yml", + Type = "objentry", + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00objentry.bin")).Using(stream => + { + var objEntry = new List() + { + new Kh2.Objentry + { + ObjectId = 1, + ModelName = "M_EX060", + AnimationName = "M_EX060.mset" + } + }; + Kh2.Objentry.Write(stream, objEntry); + }); + + File.Create(Path.Combine(ModInputDir, "ObjList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("1:"); + writer.WriteLine(" ObjectId: 1"); + writer.WriteLine(" ModelName: M_EX100"); + writer.WriteLine(" AnimationName: M_EX100.mset"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00objentry.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "00objentry.bin")).Using(stream => + { + var objStream = Kh2.Objentry.Read(stream); + Assert.Equal("M_EX100", objStream[0].ModelName); + Assert.Equal("M_EX100.mset", objStream[0].AnimationName); + }); + + } + + [Fact] + public void ListPatchPlrpTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "plrp", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "PlrpList.yml", + Type = "plrp" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var plrpEntry = new List() + { + new Kh2.Battle.Plrp + { + Id = 7, + Character = 1, + Ap = 2, + Items = new List(32), + Padding = new byte[52] + } + }; + + using var plrpStream = new MemoryStream(); + Kh2.Battle.Plrp.Write(plrpStream, plrpEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "plrp", + Type = Bar.EntryType.List, + Stream = plrpStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "PlrpList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + var serializer = new Serializer(); + var moddedPlrp = new List{ + new Kh2.Battle.Plrp + { + Id = 7, + Character = 1, + Ap = 200, + Items = new List(32), + Padding = new byte[52] + } + }; + writer.Write(serializer.Serialize(moddedPlrp)); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00battle.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var plrp = Kh2.Battle.Plrp.Read(binarc[0].Stream); + + Assert.Equal(200, plrp[0].Ap); + }); } - [Fact] //Fixed, needed to initialize the BGMSet & Reserved bytes. - public void ListPatchArifTest() + [Fact] + public void ListPatchEnmpTest() { var patcher = new PatcherProcessor(); var serializer = new Serializer(); var patch = new Metadata() { Assets = new List() - { - new AssetFile() - { - Name = "03system.bin", - Method = "binarc", - Source = new List() { new AssetFile() { - Name = "arif", - Method = "listpatch", - Type = "List", + Name = "00battle.bar", + Method = "binarc", Source = new List() { new AssetFile() { - Name = "ArifList.yml", - Type = "arif" + Name = "enmp", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "EnmpList.yml", + Type = "enmp" + } + } } } } } - } - } }; - using (var stream = File.Create(Path.Combine(AssetsInputDir, "03system.bin"))) + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => { - var arifEntry = new Kh2.SystemData.Arif + var enmpEntry = new List() { - Flags = Kh2.SystemData.Arif.ArifFlags.IsKnownArea, - Reverb = 0, - SoundEffectBank1 = 13, - SoundEffectBank2 = 0, - Bgms = Enumerable.Range(0, 8).Select(_ => new Kh2.SystemData.BgmSet { BgmField = 0, BgmBattle = 0 }).ToArray(), - Voice = 0, - NavigationMapItem = 0, - Command = 0, - Reserved = new byte[11] + new Kh2.Battle.Enmp + { + Id = 7, + Level = 1, + Health = new short[32], + MaxDamage = 1, + MinDamage = 1, + PhysicalWeakness = 1, + FireWeakness = 1, + IceWeakness = 1, + ThunderWeakness = 1, + DarkWeakness = 1, + LightWeakness = 1, + GeneralWeakness = 1, + Experience = 1, + Prize = 1, + BonusLevel = 1 + } }; - using (var arifStream = new MemoryStream()) - { - Kh2.SystemData.Arif.Write(arifStream, new List> { new List { arifEntry } }); - Bar.Write(stream, new Bar - { - new Bar.Entry() - { - Name = "arif", - Type = Bar.EntryType.List, - Stream = arifStream - } + using var enmpStream = new MemoryStream(); + Kh2.Battle.Enmp.Write(enmpStream, enmpEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "enmp", + Type = Bar.EntryType.List, + Stream = enmpStream + } + }); }); - } - } - File.Create(Path.Combine(ModInputDir, "ArifList.yml")).Using(stream => + File.Create(Path.Combine(ModInputDir, "EnmpList.yml")).Using(stream => { var writer = new StreamWriter(stream); - writer.WriteLine("EndOfSea:"); - writer.WriteLine(" 1:"); - writer.WriteLine(" SoundEffectBank1: 13"); - writer.WriteLine(" Voice: 0"); + var serializer = new Serializer(); + var moddedEnmp = new List{ + new Kh2.Battle.Enmp + { + Id = 7, + Level = 1, + Health = new short[32], + MaxDamage = 1, + MinDamage = 1, + PhysicalWeakness = 1, + FireWeakness = 1, + IceWeakness = 1, + ThunderWeakness = 1, + DarkWeakness = 1, + LightWeakness = 1, + GeneralWeakness = 1, + Experience = 1, + Prize = 1, + BonusLevel = 1 + } + }; + writer.Write(serializer.Serialize(moddedEnmp)); writer.Flush(); }); patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - AssertFileExists(ModOutputDir, "03system.bin"); + AssertFileExists(ModOutputDir, "00battle.bar"); - File.OpenRead(Path.Combine(ModOutputDir, "03system.bin")).Using(stream => + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => { var binarc = Bar.Read(stream); - var arifList = Kh2.SystemData.Arif.Read(binarc[0].Stream); - var arifEntry = arifList[0][0]; - Assert.Equal(13, arifEntry.SoundEffectBank1); - Assert.Equal(0, arifEntry.Voice); + var enmp = Kh2.Battle.Enmp.Read(binarc[0].Stream); + + Assert.Equal(1, enmp[0].Level); }); } - + [Fact] - public void ListPatchMemtTest() + public void ListPatchMagcTest() { var patcher = new PatcherProcessor(); var serializer = new Serializer(); var patch = new Metadata() { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "memt", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "MemtList.yml", - Type = "memt" - } - } - } - } - } - } + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "magc", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "MagcList.yml", + Type = "magc" + } + } + } + } + } + } }; - // Create the 03system.bar file with initial data - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => { - var memtEntries = new List() - { - new Kh2.SystemData.Memt.EntryFinalMix - { - WorldId = 1, - CheckStoryFlag = 1, - CheckStoryFlagNegation = 0, - Unk06 = 0, - Unk08 = 0, - Unk0A = 0, - Unk0C = 0, - Unk0E = 0, - Members = new short[18] - } - }; - - var memberIndices = new Kh2.SystemData.Memt.MemberIndices[7]; // Ensure there are 7 MemberIndices - for (int i = 0; i < memberIndices.Length; i++) + var magcEntry = new List() { - memberIndices[i] = new Kh2.SystemData.Memt.MemberIndices + new Kh2.Battle.Magc { - Player = 0, - Friend1 = 0, - Friend2 = 0, - FriendWorld = 0 - }; - } - - using var memtStream = new MemoryStream(); - var memt = new Kh2.SystemData.Memt(); - memt.Entries.AddRange(memtEntries.Cast()); - - Kh2.SystemData.Memt.Write(memtStream, memt); - memtStream.Seek(0, SeekOrigin.Begin); + Id = 7, + Level = 3, + World = 1, + FileName = "magic/FIRE_7.mag" + } + }; - Bar.Write(stream, new Bar() - { - new Bar.Entry() - { - Name = "memt", - Type = Bar.EntryType.List, - Stream = memtStream - } - }); + using var magcStream = new MemoryStream(); + Kh2.Battle.Magc.Write(magcStream, magcEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "magc", + Type = Bar.EntryType.List, + Stream = magcStream + } + }); }); - // Create the MemtList.yml patch file - File.Create(Path.Combine(ModInputDir, "MemtList.yml")).Using(stream => + File.Create(Path.Combine(ModInputDir, "MagcList.yml")).Using(stream => { var writer = new StreamWriter(stream); - writer.WriteLine("MemtEntries:"); - writer.WriteLine(" - Index: 0"); - writer.WriteLine(" WorldId: 2"); - writer.WriteLine(" CheckStoryFlag: 3"); - writer.WriteLine(" CheckStoryFlagNegation: 4"); - writer.WriteLine(" Unk06: 5"); - writer.WriteLine(" Unk08: 6"); - writer.WriteLine(" Unk0A: 7"); - writer.WriteLine(" Unk0C: 8"); - writer.WriteLine(" Unk0E: 9"); - writer.WriteLine(" Members: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]"); - writer.WriteLine("MemberIndices:"); - writer.WriteLine(" - Index: 0"); - writer.WriteLine(" Player: 0"); - writer.WriteLine(" Friend1: 0"); - writer.WriteLine(" Friend2: 0"); - writer.WriteLine(" FriendWorld: 0"); + var serializer = new Serializer(); + var moddedMagc = new List{ + new Kh2.Battle.Magc + { + Id = 7, + Level = 3, + World = 1, + FileName = "magic/FIRE_7.mag" + } + }; + writer.Write(serializer.Serialize(moddedMagc)); writer.Flush(); }); - // Apply the patch patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - // Verify the patched data - AssertFileExists(ModOutputDir, "03system.bar"); + AssertFileExists(ModOutputDir, "00battle.bar"); - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => { var binarc = Bar.Read(stream); - var memtStream = binarc[0].Stream; - memtStream.Seek(0, SeekOrigin.Begin); - - var memt = Kh2.SystemData.Memt.Read(memtStream); - - var memtEntry = memt.Entries.Cast().First(); - Assert.Equal((short)2, memtEntry.WorldId); - Assert.Equal((short)3, memtEntry.CheckStoryFlag); - Assert.Equal((short)4, memtEntry.CheckStoryFlagNegation); - Assert.Equal((short)5, memtEntry.Unk06); - Assert.Equal((short)6, memtEntry.Unk08); - Assert.Equal((short)7, memtEntry.Unk0A); - Assert.Equal((short)8, memtEntry.Unk0C); - Assert.Equal((short)9, memtEntry.Unk0E); - Assert.Equal(new short[] { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }, memtEntry.Members); + var magc = Kh2.Battle.Magc.Read(binarc[0].Stream); - // Check MemberIndices deserialization - var memberIndices = memt.MemberIndexCollection.First(); - Assert.Equal((byte)0, memberIndices.Player); - Assert.Equal((byte)0, memberIndices.Friend1); - Assert.Equal((byte)0, memberIndices.Friend2); - Assert.Equal((byte)0, memberIndices.FriendWorld); + Assert.Equal(3, magc[0].Level); }); - } - - - - [Fact] - public void ListPatchFmabTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "pref", - Method = "binarc", - Type = "Binary", - Source = new List() - { - new AssetFile() - { - Name = "fmab", - Method = "listpatch", - Type = "list", - Source = new List() - { - new AssetFile() - { - Name = "FmabList.yml", - Type = "fmab" - } - } - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var FmabEntry = new List() - { - new Kh2.SystemData.Fmab - { - HighJumpHeight = 178, - AirDodgeHeight = 86 - } - }; - - using var fmabStream = new MemoryStream(); - Kh2.SystemData.Fmab.Write(fmabStream, FmabEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "fmab", - Type = Bar.EntryType.List, - Stream = fmabStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "FmabList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- HighJumpHeight: 178"); - writer.WriteLine(" AirDodgeHeight: 86"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var fmabStream = Kh2.SystemData.Fmab.Read(binarc[0].Stream); - Assert.Equal(178, fmabStream[0].HighJumpHeight); - Assert.Equal(86, fmabStream[0].AirDodgeHeight); - }); - } - - [Fact] - public void ListPatchFmlvTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "fmlv", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "FmlvList.yml", - Type = "fmlv" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var fmlvEntry = new List() - { - new Kh2.Battle.Fmlv - { - FormId = 1, - FormLevel = 1, - Exp = 100, - Ability = 200 - }, - new Kh2.Battle.Fmlv - { - FormId = 1, - FormLevel = 2, - Exp = 100, - Ability = 200 - }, - new Kh2.Battle.Fmlv - { - FormId = 2, - FormLevel = 1, - Exp = 100, - Ability = 200 - }, - }; - - using var fmlvStream = new MemoryStream(); - Kh2.Battle.Fmlv.Write(fmlvStream, fmlvEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "fmlv", - Type = Bar.EntryType.List, - Stream = fmlvStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "FmlvList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - serializer.Serialize(writer, new Dictionary - { - ["Valor"] = new[] - { - new FmlvDTO - { - FormLevel = 1, - Experience = 5, - Ability = 127 - } - } - }); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var fmlv = Kh2.Battle.Fmlv.Read(binarc[0].Stream); - - Assert.Equal(3, fmlv.Count); - Assert.Equal(1, fmlv[0].FormId); - Assert.Equal(1, fmlv[0].FormLevel); - Assert.Equal(5, fmlv[0].Exp); - Assert.Equal(127, fmlv[0].Ability); - - Assert.Equal(1, fmlv[1].FormId); - Assert.Equal(2, fmlv[1].FormLevel); - - Assert.Equal(2, fmlv[2].FormId); - Assert.Equal(1, fmlv[2].FormLevel); - }); - } - - [Fact] - public void ListPatchBonsTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "bons", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "BonsList.yml", - Type = "bons" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var bonsEntry = new List() - { - new Kh2.Battle.Bons - { - CharacterId = 1, - RewardId = 15, - BonusItem1 = 10 - }, - new Kh2.Battle.Bons - { - CharacterId = 2, - RewardId = 15, - BonusItem1 = 5 - } - }; - using var bonsStream = new MemoryStream(); - Kh2.Battle.Bons.Write(bonsStream, bonsEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "bons", - Type = Bar.EntryType.List, - Stream = bonsStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "BonsList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("15:"); - writer.WriteLine(" Sora:"); - writer.WriteLine(" BonusItem1: 200"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var bonsStream = Kh2.Battle.Bons.Read(binarc[0].Stream); - Assert.Equal(200, bonsStream[0].BonusItem1); - Assert.Equal(5, bonsStream[1].BonusItem1); - }); - - } - - [Fact] - public void ListPatchLvupTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bin", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "lvup", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "LvupList.yml", - Type = "lvup" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bin")).Using(stream => - { - var lvupEntry = new Kh2.Battle.Lvup - { - Count = 13, - Unknown08 = new byte[0x38], - Characters = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - } - - } - }; - using var lvupStream = new MemoryStream(); - lvupEntry.Write(lvupStream); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "lvup", - Type = Bar.EntryType.List, - Stream = lvupStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "LvupList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("Sora:"); - writer.WriteLine(" 1:"); - writer.WriteLine(" Exp: 500"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bin"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bin")).Using(stream => - { - var binarc = Bar.Read(stream); - var lvupStream = Kh2.Battle.Lvup.Read(binarc[0].Stream); - Assert.Equal(500, lvupStream.Characters[0].Levels[0].Exp); - }); - - } - - [Fact] - void ListPatchAtkpTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "atkp", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "AtkpList.yml", - Type = "atkp" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var atkpEntry = new List() - { - new Kh2.Battle.Atkp - { - Id = 0, - SubId = 3, - Type = Kh2.Battle.Atkp.AttackType.PierceArmor, - CriticalAdjust = 0, - Power = 25, - Team = 0, - Element = 0, - EnemyReaction = 0, - EffectOnHit = 2, - KnockbackStrength1 = 32767, - KnockbackStrength2 = 0, - Unknown = 0, - Flags = Kh2.Battle.Atkp.AttackFlags.BGHit, - RefactSelf = Kh2.Battle.Atkp.Refact.Reflect, - RefactOther = Kh2.Battle.Atkp.Refact.Reflect, - ReflectedMotion = 0, - ReflectHitBack = 0, - ReflectAction = 0, - ReflectHitSound = 0, - ReflectRC = 0, - ReflectRange = 0, - ReflectAngle = 0, - DamageEffect = 0, - Switch = 1, - Interval = 1, - FloorCheck = 1, - DriveDrain = 1, - RevengeDamage = 1, - AttackTrReaction = Kh2.Battle.Atkp.TrReaction.Charge, - ComboGroup = 1, - RandomEffect = 1, - Kind = Kh2.Battle.Atkp.AttackKind.ComboFinisher, - HpDrain = 15 - } - }; - - using var atkpStream = new MemoryStream(); - Kh2.Battle.Atkp.Write(atkpStream, atkpEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "atkp", - Type = Bar.EntryType.List, - Stream = atkpStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "AtkpList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Id: 0"); - writer.WriteLine(" SubId: 3"); - writer.WriteLine(" Type: PierceArmor"); - writer.WriteLine(" CriticalAdjust: 0"); - writer.WriteLine(" Power: 25"); - writer.WriteLine(" Team: 0"); - writer.WriteLine(" Element: 0"); - writer.WriteLine(" EnemyReaction: 0"); - writer.WriteLine(" EffectOnHit: 2"); - writer.WriteLine(" KnockbackStrength1: 32767"); - writer.WriteLine(" KnockbackStrength2: 0"); - writer.WriteLine(" Unknown: 0"); - writer.WriteLine(" Flags: BGHit"); - writer.WriteLine(" RefactSelf: 0"); - writer.WriteLine(" RefactOther: 0"); - writer.WriteLine(" ReflectedMotion: 0"); - writer.WriteLine(" ReflectHitBack: 0"); - writer.WriteLine(" ReflectAction: 0"); - writer.WriteLine(" ReflectHitSound: 0"); - writer.WriteLine(" ReflectRC: 0"); - writer.WriteLine(" ReflectRange: 0"); - writer.WriteLine(" ReflectAngle: 0"); - writer.WriteLine(" DamageEffect: 0"); - writer.WriteLine(" Switch: 1"); - writer.WriteLine(" Interval: 1"); - writer.WriteLine(" FloorCheck: 1"); - writer.WriteLine(" DriveDrain: 1"); - writer.WriteLine(" RevengeDamage: 1"); - writer.WriteLine(" AttackTrReaction: 1"); - writer.WriteLine(" ComboGroup: 1"); - writer.WriteLine(" RandomEffect: 1"); - writer.WriteLine(" Kind: ComboFinisher"); - writer.WriteLine(" HpDrain: 15"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var atkpStream = Kh2.Battle.Atkp.Read(binarc[0].Stream); - Assert.Equal(0, atkpStream[0].Id); - Assert.Equal(3, atkpStream[0].SubId); - Assert.Equal(Kh2.Battle.Atkp.AttackType.PierceArmor, atkpStream[0].Type); - Assert.Equal(0, atkpStream[0].CriticalAdjust); - Assert.Equal(25, atkpStream[0].Power); - Assert.Equal(0, atkpStream[0].Team); - Assert.Equal(0, atkpStream[0].Element); - Assert.Equal(0, atkpStream[0].EnemyReaction); - Assert.Equal(2, atkpStream[0].EffectOnHit); - Assert.Equal(32767, atkpStream[0].KnockbackStrength1); - Assert.Equal(0, atkpStream[0].KnockbackStrength2); - Assert.Equal(0000, atkpStream[0].Unknown); - Assert.Equal(Kh2.Battle.Atkp.AttackFlags.BGHit, atkpStream[0].Flags); - Assert.Equal(Kh2.Battle.Atkp.Refact.Reflect, atkpStream[0].RefactSelf); - Assert.Equal(Kh2.Battle.Atkp.Refact.Reflect, atkpStream[0].RefactOther); - Assert.Equal(0, atkpStream[0].ReflectedMotion); - Assert.Equal(0, atkpStream[0].ReflectHitBack); - Assert.Equal(0, atkpStream[0].ReflectAction); - Assert.Equal(0, atkpStream[0].ReflectHitSound); - Assert.Equal(0, atkpStream[0].ReflectRC); - Assert.Equal(0, atkpStream[0].ReflectRange); - Assert.Equal(0, atkpStream[0].ReflectAngle); - Assert.Equal(0, atkpStream[0].DamageEffect); - Assert.Equal(1, atkpStream[0].Switch); - Assert.Equal(1, atkpStream[0].Interval); - Assert.Equal(1, atkpStream[0].FloorCheck); - Assert.Equal(1, atkpStream[0].DriveDrain); - Assert.Equal(1, atkpStream[0].RevengeDamage); - Assert.Equal(Kh2.Battle.Atkp.TrReaction.Charge, atkpStream[0].AttackTrReaction); - Assert.Equal(1, atkpStream[0].ComboGroup); - Assert.Equal(1, atkpStream[0].RandomEffect); - Assert.Equal(Kh2.Battle.Atkp.AttackKind.ComboFinisher, atkpStream[0].Kind); - Assert.Equal(15, atkpStream[0].HpDrain); - }); - } - - [Fact] - public void ListPatchObjEntryTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00objentry.bin", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "ObjList.yml", - Type = "objentry", - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00objentry.bin")).Using(stream => - { - var objEntry = new List() - { - new Kh2.Objentry - { - ObjectId = 1, - ModelName = "M_EX060", - AnimationName = "M_EX060.mset" - } - }; - Kh2.Objentry.Write(stream, objEntry); - }); - - File.Create(Path.Combine(ModInputDir, "ObjList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("1:"); - writer.WriteLine(" ObjectId: 1"); - writer.WriteLine(" ModelName: M_EX100"); - writer.WriteLine(" AnimationName: M_EX100.mset"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00objentry.bin"); - - File.OpenRead(Path.Combine(ModOutputDir, "00objentry.bin")).Using(stream => - { - var objStream = Kh2.Objentry.Read(stream); - Assert.Equal("M_EX100", objStream[0].ModelName); - Assert.Equal("M_EX100.mset", objStream[0].AnimationName); - }); - - } - - [Fact] - public void ListPatchPlrpTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "plrp", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "PlrpList.yml", - Type = "plrp" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var plrpEntry = new List() - { - new Kh2.Battle.Plrp - { - Id = 7, - Character = 1, - Ap = 2, - Items = new List(32), - Padding = new byte[52] - } - }; - - using var plrpStream = new MemoryStream(); - Kh2.Battle.Plrp.Write(plrpStream, plrpEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "plrp", - Type = Bar.EntryType.List, - Stream = plrpStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "PlrpList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - var moddedPlrp = new List{ - new Kh2.Battle.Plrp - { - Id = 7, - Character = 1, - Ap = 200, - Items = new List(32), - Padding = new byte[52] - } - }; - writer.Write(serializer.Serialize(moddedPlrp)); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var plrp = Kh2.Battle.Plrp.Read(binarc[0].Stream); - - Assert.Equal(200, plrp[0].Ap); - }); - } - - [Fact] - public void ListPatchEnmpTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "enmp", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "EnmpList.yml", - Type = "enmp" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var enmpEntry = new List() - { - new Kh2.Battle.Enmp - { - Id = 7, - Level = 1, - Health = new short[32], - MaxDamage = 1, - MinDamage = 1, - PhysicalWeakness = 1, - FireWeakness = 1, - IceWeakness = 1, - ThunderWeakness = 1, - DarkWeakness = 1, - LightWeakness = 1, - GeneralWeakness = 1, - Experience = 1, - Prize = 1, - BonusLevel = 1 - } - }; - - using var enmpStream = new MemoryStream(); - Kh2.Battle.Enmp.Write(enmpStream, enmpEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "enmp", - Type = Bar.EntryType.List, - Stream = enmpStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "EnmpList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - var moddedEnmp = new List{ - new Kh2.Battle.Enmp - { - Id = 7, - Level = 1, - Health = new short[32], - MaxDamage = 1, - MinDamage = 1, - PhysicalWeakness = 1, - FireWeakness = 1, - IceWeakness = 1, - ThunderWeakness = 1, - DarkWeakness = 1, - LightWeakness = 1, - GeneralWeakness = 1, - Experience = 1, - Prize = 1, - BonusLevel = 1 - } - }; - writer.Write(serializer.Serialize(moddedEnmp)); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var enmp = Kh2.Battle.Enmp.Read(binarc[0].Stream); - - Assert.Equal(1, enmp[0].Level); - }); - } - - [Fact] - public void ListPatchMagcTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "magc", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "MagcList.yml", - Type = "magc" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var magcEntry = new List() - { - new Kh2.Battle.Magc - { - Id = 7, - Level = 3, - World = 1, - FileName = "magic/FIRE_7.mag" - } - }; - - using var magcStream = new MemoryStream(); - Kh2.Battle.Magc.Write(magcStream, magcEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "magc", - Type = Bar.EntryType.List, - Stream = magcStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "MagcList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - var moddedMagc = new List{ - new Kh2.Battle.Magc - { - Id = 7, - Level = 3, - World = 1, - FileName = "magic/FIRE_7.mag" - } - }; - writer.Write(serializer.Serialize(moddedMagc)); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var magc = Kh2.Battle.Magc.Read(binarc[0].Stream); - - Assert.Equal(3, magc[0].Level); - }); } [Fact] @@ -2340,174 +2428,174 @@ public void ListPatchLimtTest() } - [Fact] - public void ListPatchBtlvTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "btlv", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "BtlvList.yml", - Type = "btlv" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var btlvEntry = new List() - { - new Kh2.Battle.Btlv - { - Id = 0, - ProgressFlag = 3, - WorldZZ = 1, - Padding = new byte[5], - } - }; - - using var btlvStream = new MemoryStream(); - Kh2.Battle.Btlv.Write(btlvStream, btlvEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "btlv", - Type = Bar.EntryType.List, - Stream = btlvStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "BtlvList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - var moddedBtlv = new List{ - new Kh2.Battle.Btlv - { - Id = 0, - ProgressFlag = 3, - WorldZZ = 1, - Padding = new byte[5], - - } - }; - writer.Write(serializer.Serialize(moddedBtlv)); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var btlv = Kh2.Battle.Btlv.Read(binarc[0].Stream); - - Assert.Equal(3, btlv[0].ProgressFlag); - }); + [Fact] + public void ListPatchBtlvTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "btlv", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "BtlvList.yml", + Type = "btlv" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var btlvEntry = new List() + { + new Kh2.Battle.Btlv + { + Id = 0, + ProgressFlag = 3, + WorldZZ = 1, + Padding = new byte[5], + } + }; + + using var btlvStream = new MemoryStream(); + Kh2.Battle.Btlv.Write(btlvStream, btlvEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "btlv", + Type = Bar.EntryType.List, + Stream = btlvStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "BtlvList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + var serializer = new Serializer(); + var moddedBtlv = new List{ + new Kh2.Battle.Btlv + { + Id = 0, + ProgressFlag = 3, + WorldZZ = 1, + Padding = new byte[5], + + } + }; + writer.Write(serializer.Serialize(moddedBtlv)); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "00battle.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var btlv = Kh2.Battle.Btlv.Read(binarc[0].Stream); + + Assert.Equal(3, btlv[0].ProgressFlag); + }); } - [Fact] + [Fact] public void ListPatchVtblTest() { var patcher = new PatcherProcessor(); var serializer = new Serializer(); var patch = new Metadata() { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "vtbl", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "VtblList.yml", - Type = "vtbl" - } - } - } - } - } + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "vtbl", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "VtblList.yml", + Type = "vtbl" + } + } + } + } + } } }; File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => { - var vtblEntry = new List() - { - new Kh2.Battle.Vtbl - { - Id = 0, - CharacterId = 1, - Priority = 1, - Reserved = 0, - Voices = new List - { - new Kh2.Battle.Vtbl.Voice { VsbIndex = 0, Weight = 0 } - } - } + var vtblEntry = new List() + { + new Kh2.Battle.Vtbl + { + Id = 0, + CharacterId = 1, + Priority = 1, + Reserved = 0, + Voices = new List + { + new Kh2.Battle.Vtbl.Voice { VsbIndex = 0, Weight = 0 } + } + } }; using var vtblStream = new MemoryStream(); Kh2.Battle.Vtbl.Write(vtblStream, vtblEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "vtbl", - Type = Bar.EntryType.List, - Stream = vtblStream - } + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "vtbl", + Type = Bar.EntryType.List, + Stream = vtblStream + } }); }); File.Create(Path.Combine(ModInputDir, "VtblList.yml")).Using(stream => { var writer = new StreamWriter(stream); - var moddedVtbl = new List{ - new Kh2.Battle.Vtbl - { - Id = 0, - CharacterId = 1, - Priority = 1, - Reserved = 0, - Voices = new List - { - new Kh2.Battle.Vtbl.Voice { VsbIndex = 0, Weight = 0 } - } - } + var moddedVtbl = new List{ + new Kh2.Battle.Vtbl + { + Id = 0, + CharacterId = 1, + Priority = 1, + Reserved = 0, + Voices = new List + { + new Kh2.Battle.Vtbl.Voice { VsbIndex = 0, Weight = 0 } + } + } }; writer.Write(serializer.Serialize(moddedVtbl)); writer.Flush(); @@ -2524,7 +2612,7 @@ public void ListPatchVtblTest() Assert.Equal(1, vtbl[0].Priority); }); - } + } @@ -2626,349 +2714,349 @@ public void ListPatchLibrettoTest() }); } - [Fact] - public void ListPatchLocalsetTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "07localset.bin", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "loca", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "LocalsetList.yml", - Type = "localset" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "07localset.bin")).Using(stream => - { - var localEntry = new List() - { - new Kh2.Localset - { - ProgramId = 1300, - MapNumber = 6, - } - }; - - using var localStream = new MemoryStream(); - Kh2.Localset.Write(localStream, localEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "loca", - Type = Bar.EntryType.List, - Stream = localStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "LocalsetList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- ProgramId: 1300"); - writer.WriteLine(" MapNumber: 6"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "07localset.bin"); - - File.OpenRead(Path.Combine(ModOutputDir, "07localset.bin")).Using(stream => - { - var binarc = Bar.Read(stream); - var localStream = Kh2.Localset.Read(binarc[0].Stream); - Assert.Equal(1300u, localStream[0].ProgramId); - Assert.Equal(6u, localStream[0].MapNumber); - }); + [Fact] + public void ListPatchLocalsetTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "07localset.bin", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "loca", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "LocalsetList.yml", + Type = "localset" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "07localset.bin")).Using(stream => + { + var localEntry = new List() + { + new Kh2.Localset + { + ProgramId = 1300, + MapNumber = 6, + } + }; + + using var localStream = new MemoryStream(); + Kh2.Localset.Write(localStream, localEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "loca", + Type = Bar.EntryType.List, + Stream = localStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "LocalsetList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- ProgramId: 1300"); + writer.WriteLine(" MapNumber: 6"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "07localset.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "07localset.bin")).Using(stream => + { + var binarc = Bar.Read(stream); + var localStream = Kh2.Localset.Read(binarc[0].Stream); + Assert.Equal(1300u, localStream[0].ProgramId); + Assert.Equal(6u, localStream[0].MapNumber); + }); } - [Fact] - public void ListPatchJigsawTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "15jigsaw.bin", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "JigsawList.yml", - Type = "jigsaw", - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "15jigsaw.bin")).Using(stream => - { - var jigsawEntry = new List() - { - new Kh2.Jigsaw - { - Picture = Kh2.Jigsaw.PictureName.Duality, - Part = 2, - Text = 1500 - } - }; - Kh2.Jigsaw.Write(stream, jigsawEntry); - }); - - File.Create(Path.Combine(ModInputDir, "JigsawList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Picture: Duality"); - writer.WriteLine(" Part: 2"); - writer.WriteLine(" Text: 1500"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "15jigsaw.bin"); - - File.OpenRead(Path.Combine(ModOutputDir, "15jigsaw.bin")).Using(stream => - { - var jigsawStream = Kh2.Jigsaw.Read(stream); - Assert.Equal(2, jigsawStream[0].Part); - }); - + [Fact] + public void ListPatchJigsawTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "15jigsaw.bin", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "JigsawList.yml", + Type = "jigsaw", + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "15jigsaw.bin")).Using(stream => + { + var jigsawEntry = new List() + { + new Kh2.Jigsaw + { + Picture = Kh2.Jigsaw.PictureName.Duality, + Part = 2, + Text = 1500 + } + }; + Kh2.Jigsaw.Write(stream, jigsawEntry); + }); + + File.Create(Path.Combine(ModInputDir, "JigsawList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Picture: Duality"); + writer.WriteLine(" Part: 2"); + writer.WriteLine(" Text: 1500"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "15jigsaw.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "15jigsaw.bin")).Using(stream => + { + var jigsawStream = Kh2.Jigsaw.Read(stream); + Assert.Equal(2, jigsawStream[0].Part); + }); + } - [Fact] - public void ListPatchPlacesTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "place.bin", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "PlaceList.yml", - Type = "place", - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "place.bin")).Using(stream => - { - var placeEntry = new List() - { - new Kh2.Places - { - MessageId = 100, - Padding = 0, - } - }; - Kh2.Places.Write(stream, placeEntry); - }); - - File.Create(Path.Combine(ModInputDir, "PlaceList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- MessageId: 100"); - writer.WriteLine(" Padding: 0"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "place.bin"); - - File.OpenRead(Path.Combine(ModOutputDir, "place.bin")).Using(stream => - { - var placesStream = Kh2.Places.Read(stream); - Assert.Equal(100, placesStream[0].MessageId); - }); - + [Fact] + public void ListPatchPlacesTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "place.bin", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "PlaceList.yml", + Type = "place", + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "place.bin")).Using(stream => + { + var placeEntry = new List() + { + new Kh2.Places + { + MessageId = 100, + Padding = 0, + } + }; + Kh2.Places.Write(stream, placeEntry); + }); + + File.Create(Path.Combine(ModInputDir, "PlaceList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- MessageId: 100"); + writer.WriteLine(" Padding: 0"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "place.bin"); + + File.OpenRead(Path.Combine(ModOutputDir, "place.bin")).Using(stream => + { + var placesStream = Kh2.Places.Read(stream); + Assert.Equal(100, placesStream[0].MessageId); + }); + } - [Fact] - public void ListPatchSoundInfoTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "12soundinfo.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "zz", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "SoundInfoList.yml", - Type = "soundinfo" - } - } - } - } - } - } - }; - File.Create(Path.Combine(AssetsInputDir, "12soundinfo.bar")).Using(stream => - { - var soundinfoEntry = new List() - { - new Kh2.Soundinfo - { - Reverb = -1, - Rate = 1, - } - }; - Kh2.Soundinfo.Write(stream, soundinfoEntry); - }); - - File.Create(Path.Combine(ModInputDir, "SoundInfoList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Reverb: -1"); - writer.WriteLine(" Rate: 1"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "12soundinfo.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "12soundinfo.bar")).Using(stream => - { - var soundinfoStream = Kh2.Soundinfo.Read(stream); - Assert.Equal(1, soundinfoStream[0].Rate); - }); - + [Fact] + public void ListPatchSoundInfoTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "12soundinfo.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "zz", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "SoundInfoList.yml", + Type = "soundinfo" + } + } + } + } + } + } + }; + File.Create(Path.Combine(AssetsInputDir, "12soundinfo.bar")).Using(stream => + { + var soundinfoEntry = new List() + { + new Kh2.Soundinfo + { + Reverb = -1, + Rate = 1, + } + }; + Kh2.Soundinfo.Write(stream, soundinfoEntry); + }); + + File.Create(Path.Combine(ModInputDir, "SoundInfoList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Reverb: -1"); + writer.WriteLine(" Rate: 1"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "12soundinfo.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "12soundinfo.bar")).Using(stream => + { + var soundinfoStream = Kh2.Soundinfo.Read(stream); + Assert.Equal(1, soundinfoStream[0].Rate); + }); + } [Fact] - public void ListPatchMixdataReciTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); + public void ListPatchMixdataReciTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); var patch = new Metadata() { - Assets = new List() - { - new AssetFile() - { - Name = "mixdata.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "reci", - Method = "synthpatch", - Type = "Synthesis", - Source = new List() - { - new AssetFile() - { - Name = "ReciList.yml", - Type = "recipe" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "mixdata.bar")).Using(stream => - { - var recipeEntry = new List() - { - new Kh2.Mixdata.ReciLP - { - Id = 1, - Unlock = 0, - Rank = 0, - } - }; - using var recipeStream = new MemoryStream(); - Kh2.Mixdata.ReciLP.Write(recipeStream, recipeEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "reci", - Type = Bar.EntryType.List, - Stream = recipeStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "ReciList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Id: 1"); - writer.WriteLine(" Rank: 0"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "mixdata.bar"); - + Assets = new List() + { + new AssetFile() + { + Name = "mixdata.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "reci", + Method = "synthpatch", + Type = "Synthesis", + Source = new List() + { + new AssetFile() + { + Name = "ReciList.yml", + Type = "recipe" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "mixdata.bar")).Using(stream => + { + var recipeEntry = new List() + { + new Kh2.Mixdata.ReciLP + { + Id = 1, + Unlock = 0, + Rank = 0, + } + }; + using var recipeStream = new MemoryStream(); + Kh2.Mixdata.ReciLP.Write(recipeStream, recipeEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "reci", + Type = Bar.EntryType.List, + Stream = recipeStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "ReciList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + writer.WriteLine("- Id: 1"); + writer.WriteLine(" Rank: 0"); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, "mixdata.bar"); + File.OpenRead(Path.Combine(ModOutputDir, "mixdata.bar")).Using(stream => { var binarc = Bar.Read(stream); var recipeStream = Kh2.Mixdata.ReciLP.Read(binarc[0].Stream); Assert.Equal(1, recipeStream[0].Id); Assert.Equal(0, recipeStream[0].Rank); - }); - - } - + }); + + } + [Fact] public void ListPatchMixdataLeveLPTest() { @@ -3069,7 +3157,7 @@ public void ListPatchMixdataLeveLPTest() Assert.Equal(100, leveStream[0].Exp); }); } - + [Fact] public void ListPatchMixdataCondLPTest() { @@ -3179,95 +3267,95 @@ public void ListPatchMixdataCondLPTest() Assert.Equal(5, condStream[0].ShopUnlock); }); } - - - - - [Fact] - public void ProcessMultipleTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bar", - Method = "binarc", - Multi = new List - { - new Multi { Name = "somedir/another.bar" } - }, - Source = new List - { - new AssetFile - { - Name = "test", - Method = "imgd", - Type = "imgd", - Source = new List - { - new AssetFile - { - Name = "sample.png", - IsSwizzled = false - } - } - } - } - } - } - }; - - File.Copy("Imaging/res/png/32.png", Path.Combine(ModInputDir, "sample.png")); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertBarFile("test", entry => - { - Assert.True(Imgd.IsValid(entry.Stream)); - }, ModOutputDir, patch.Assets[0].Name); - - AssertFileExists(ModOutputDir, patch.Assets[0].Multi[0].Name); - AssertBarFile("test", entry => - { - Assert.True(Imgd.IsValid(entry.Stream)); - }, ModOutputDir, patch.Assets[0].Multi[0].Name); - } - - - private static void AssertFileExists(params string[] paths) - { - var filePath = Path.Join(paths); - if (File.Exists(filePath) == false) - Assert.Fail($"File not found '{filePath}'"); - } - - private static void AssertBarFile(string name, Action assertion, params string[] paths) - { - var filePath = Path.Join(paths); - var entries = File.OpenRead(filePath).Using(x => - { - if (!Bar.IsValid(x)) - Assert.Fail($"Not a valid BinArc"); - return Bar.Read(x); - }); - - var entry = entries.SingleOrDefault(x => x.Name == name); - if (entry == null) - throw new XunitException($"Entry '{name}' not found"); - - assertion(entry); - } - - private static Stream CreateFile(params string[] paths) - { - var filePath = Path.Join(paths); - var dirPath = Path.GetDirectoryName(filePath); - Directory.CreateDirectory(dirPath); - return File.Create(filePath); - } - } -} + + + + + [Fact] + public void ProcessMultipleTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List + { + new AssetFile + { + Name = "somedir/somefile.bar", + Method = "binarc", + Multi = new List + { + new Multi { Name = "somedir/another.bar" } + }, + Source = new List + { + new AssetFile + { + Name = "test", + Method = "imgd", + Type = "imgd", + Source = new List + { + new AssetFile + { + Name = "sample.png", + IsSwizzled = false + } + } + } + } + } + } + }; + + File.Copy("Imaging/res/png/32.png", Path.Combine(ModInputDir, "sample.png")); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, patch.Assets[0].Name); + AssertBarFile("test", entry => + { + Assert.True(Imgd.IsValid(entry.Stream)); + }, ModOutputDir, patch.Assets[0].Name); + + AssertFileExists(ModOutputDir, patch.Assets[0].Multi[0].Name); + AssertBarFile("test", entry => + { + Assert.True(Imgd.IsValid(entry.Stream)); + }, ModOutputDir, patch.Assets[0].Multi[0].Name); + } + + + private static void AssertFileExists(params string[] paths) + { + var filePath = Path.Join(paths); + if (File.Exists(filePath) == false) + Assert.Fail($"File not found '{filePath}'"); + } + + private static void AssertBarFile(string name, Action assertion, params string[] paths) + { + var filePath = Path.Join(paths); + var entries = File.OpenRead(filePath).Using(x => + { + if (!Bar.IsValid(x)) + Assert.Fail($"Not a valid BinArc"); + return Bar.Read(x); + }); + + var entry = entries.SingleOrDefault(x => x.Name == name); + if (entry == null) + throw new XunitException($"Entry '{name}' not found"); + + assertion(entry); + } + + private static Stream CreateFile(params string[] paths) + { + var filePath = Path.Join(paths); + var dirPath = Path.GetDirectoryName(filePath); + Directory.CreateDirectory(dirPath); + return File.Create(filePath); + } + } +} From bb3e1a01f585537619991f824376d385d9e86563 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:46:23 -0400 Subject: [PATCH 12/21] Removed duplicate reci comments --- OpenKh.Patcher/PatcherProcessor.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/OpenKh.Patcher/PatcherProcessor.cs b/OpenKh.Patcher/PatcherProcessor.cs index f0c027e2e..86c98cb2f 100644 --- a/OpenKh.Patcher/PatcherProcessor.cs +++ b/OpenKh.Patcher/PatcherProcessor.cs @@ -1243,8 +1243,8 @@ private static void PatchSynth(Context context, List sources, Stream break; case "condition": - var conditionList = Kh2.Mixdata.CondLP.Read(stream); // Read existing Reci list - var moddedConditions = deserializer.Deserialize>(sourceText); // Deserialize modded recipes + var conditionList = Kh2.Mixdata.CondLP.Read(stream); + var moddedConditions = deserializer.Deserialize>(sourceText); foreach (var moddedCondition in moddedConditions) { @@ -1252,25 +1252,21 @@ private static void PatchSynth(Context context, List sources, Stream if (existingCondition != null) { - // Update existing recipe in the list conditionList[conditionList.IndexOf(existingCondition)] = moddedCondition; - // Update other properties as needed } else { - // Add new recipe to the list conditionList.Add(moddedCondition); } } - // Write the updated recipe list back to the stream Kh2.Mixdata.CondLP.Write(stream, conditionList); // Pass IEnumerable break; case "level": - var levelList = Kh2.Mixdata.LeveLP.Read(stream); // Read existing Reci list - var moddedLevels = deserializer.Deserialize>(sourceText); // Deserialize modded recipes + var levelList = Kh2.Mixdata.LeveLP.Read(stream); + var moddedLevels = deserializer.Deserialize>(sourceText); foreach (var moddedLevel in moddedLevels) { @@ -1278,20 +1274,15 @@ private static void PatchSynth(Context context, List sources, Stream if (existingLevel != null) { - // Update existing recipe in the list levelList[levelList.IndexOf(existingLevel)] = moddedLevel; - - // Update other properties as needed } else { - // Add new recipe to the list levelList.Add(moddedLevel); } } - // Write the updated recipe list back to the stream - Kh2.Mixdata.LeveLP.Write(stream, levelList); // Pass IEnumerable + Kh2.Mixdata.LeveLP.Write(stream, levelList); break; } } From 5c116ad53198330992e98eddfdc72407aa000001 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:46:54 -0400 Subject: [PATCH 13/21] Remove redundant lines --- OpenKh.Patcher/Metadata.cs | 196 +++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 108 deletions(-) diff --git a/OpenKh.Patcher/Metadata.cs b/OpenKh.Patcher/Metadata.cs index 450995150..c6bb4be9d 100644 --- a/OpenKh.Patcher/Metadata.cs +++ b/OpenKh.Patcher/Metadata.cs @@ -1,45 +1,41 @@ -using OpenKh.Kh2; +using OpenKh.Kh2; using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; -using System.IO; +using System.IO; using System.Text.RegularExpressions; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace OpenKh.Patcher -{ - public class Metadata - { - public class Dependency - { - public string Name { get; set; } - } - - public string Title { get; set; } - public string OriginalAuthor { get; set; } - public string Description { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Game { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public int Specifications { get; set; } - public List Dependencies { get; set; } - public List Assets { get; set; } - - private static readonly IDeserializer deserializer = - new DeserializerBuilder() - .IgnoreFields() - .IgnoreUnmatchedProperties() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); - private static readonly ISerializer serializer = - new SerializerBuilder() - //.JsonCompatible() //Experimental. May allow mod.ymls/listpatches to be in json formatting, and mod manager will accept it? - .IgnoreFields() - .WithNamingConvention(CamelCaseNamingConvention.Instance) +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace OpenKh.Patcher +{ + public class Metadata + { + public class Dependency + { + public string Name { get; set; } + } + + public string Title { get; set; } + public string OriginalAuthor { get; set; } + public string Description { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Game { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public int Specifications { get; set; } + public List Dependencies { get; set; } + public List Assets { get; set; } + + private static readonly IDeserializer deserializer = + new DeserializerBuilder() + .IgnoreFields() + .IgnoreUnmatchedProperties() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build(); + private static readonly ISerializer serializer = + new SerializerBuilder() + .IgnoreFields() + .WithNamingConvention(CamelCaseNamingConvention.Instance) .Build(); - //Old Metadata Read. Two lines but isn't comprehensive enough. - // public static Metadata Read(Stream stream) => - // deserializer.Deserialize(new StreamReader(stream)); public static Metadata Read(Stream stream) { @@ -48,54 +44,38 @@ public static Metadata Read(Stream stream) return deserializer.Deserialize(new StreamReader(stream)); //Simplifed way of doing this. Cuts out the "deserializer" redundancies and variable for StreamReader(stream)) } - //OLD TRY METHOD: Seems to use a lot of redundant code. - //try - //{ - // var deserializer = new DeserializerBuilder() - // .WithNamingConvention(CamelCaseNamingConvention.Instance) - // .Build(); - - // using (var reader = new StreamReader(stream)) - // { - // var metadata = deserializer.Deserialize(reader); - // return metadata; - // } - // } - + catch (YamlDotNet.Core.YamlException ex) + { + // Handle YAML parsing errors + Debug.WriteLine($"Error deserializing YAML: {ex.Message}"); - //Use this to handle YML errors. Prevents crashing upon immediate startup. - catch (YamlDotNet.Core.YamlException ex) - { - // Handle YAML parsing errors - Debug.WriteLine($"Error deserializing YAML: {ex.Message}"); - string originalTitle = string.Empty; // Extract title using regex - stream.Position = 0; // Reset stream position - using (var reader = new StreamReader(stream)) - { - string yamlContent = reader.ReadToEnd(); - var match = Regex.Match(yamlContent, @"(?<=title:).*"); - if (match.Success) - { - originalTitle = match.Value.Trim(); - } - } - - var metadata = new Metadata - { - Title = $"{originalTitle}* \nMOD YML ERROR DETECTED: CHECK FORMATTING" - }; - - return metadata; // Return modified metadata indicating failure - } - catch (Exception ex) - { - // Handle other unexpected errors - Debug.WriteLine($"Unexpected error: {ex.Message}"); - throw; // Rethrow other exceptions for further investigation - } + stream.Position = 0; // Reset stream position + using (var reader = new StreamReader(stream)) + { + string yamlContent = reader.ReadToEnd(); + var match = Regex.Match(yamlContent, @"(?<=title:).*"); + if (match.Success) + { + originalTitle = match.Value.Trim(); + } + } + + var metadata = new Metadata + { + Title = $"{originalTitle}* \nMOD YML ERROR DETECTED: CHECK FORMATTING" + }; + + return metadata; // Return modified metadata indicating failure + } + catch (Exception ex) + { + // Handle other unexpected errors + Debug.WriteLine($"Unexpected error: {ex.Message}"); + throw; // Rethrow other exceptions for further investigation + } } public void Write(Stream stream) @@ -104,30 +84,30 @@ public void Write(Stream stream) { serializer.Serialize(writer, this); } - } - public override string ToString() => - serializer.Serialize(this); - } - - public class AssetFile - { - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Name { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Method { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Platform { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Package { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public List Multi { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public List Source { get; set; } - - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public bool Required { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Type { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public Bar.MotionsetType MotionsetType { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Language { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public bool IsSwizzled { get; set; } - [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public int Index { get; set; } - } - - public class Multi - { - public string Name { get; set; } - } -} + } + public override string ToString() => + serializer.Serialize(this); + } + + public class AssetFile + { + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Name { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Method { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Platform { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Package { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public List Multi { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public List Source { get; set; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public bool Required { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Type { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public Bar.MotionsetType MotionsetType { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public string Language { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public bool IsSwizzled { get; set; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public int Index { get; set; } + } + + public class Multi + { + public string Name { get; set; } + } +} From bfb85b092582911b4682d3cd0cdc00cab05152e5 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:49:45 -0400 Subject: [PATCH 14/21] Fix merge conflict w/ Bbsarc --- OpenKh.Patcher/PatcherProcessor.cs | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/OpenKh.Patcher/PatcherProcessor.cs b/OpenKh.Patcher/PatcherProcessor.cs index 86c98cb2f..b130e28a1 100644 --- a/OpenKh.Patcher/PatcherProcessor.cs +++ b/OpenKh.Patcher/PatcherProcessor.cs @@ -15,6 +15,7 @@ using System.Collections; using Antlr4.Runtime.Dfa; using System.Diagnostics; +using OpenKh.Bbs; namespace OpenKh.Patcher @@ -226,6 +227,9 @@ private static void PatchFile(Context context, AssetFile assetFile, Stream strea case "binarc": PatchBinarc(context, assetFile, stream); break; + case "bbsarc": + PatchBBSArc(context, assetFile, stream); + break; case "imd": case "imgd": CreateImageImd(context, assetFile.Source[0]).Write(stream); @@ -337,6 +341,38 @@ private static void PatchBinarc(Context context, AssetFile assetFile, Stream str entry.Stream?.Dispose(); } + private static void PatchBBSArc(Context context, AssetFile assetFile, Stream stream) + { + var entryList = Arc.IsValid(stream) ? Arc.Read(stream).ToList() : new List(); + foreach (var file in assetFile.Source) + { + var entry = entryList.FirstOrDefault(e => e.Name == file.Name); + + if (entry == null) + { + entry = new Arc.Entry() + { + Name = file.Name + }; + entryList.Add(entry); + } + else if (entry.IsLink) + { + throw new Exception("Cannot patch an arc link!"); + } + + MemoryStream data = new MemoryStream(); + if (entry.Data != null) + { + data.Write(entry.Data); + data.SetPosition(0); + } + PatchFile(context, file, data); + entry.Data = data.ToArray(); + } + + OpenKh.Bbs.Arc.Write(entryList, stream.SetPosition(0)); + } private static Imgd CreateImageImd(Context context, AssetFile source) { From 5d4d2891af85c55f96e8fffb0a21801858359f09 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:51:15 -0400 Subject: [PATCH 15/21] Fix conflict w/ BBS Tests --- OpenKh.Tests/Patcher/PatcherTests.cs | 173 +++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/OpenKh.Tests/Patcher/PatcherTests.cs b/OpenKh.Tests/Patcher/PatcherTests.cs index bddcdc017..fc3ed08ec 100644 --- a/OpenKh.Tests/Patcher/PatcherTests.cs +++ b/OpenKh.Tests/Patcher/PatcherTests.cs @@ -3269,7 +3269,163 @@ public void ListPatchMixdataCondLPTest() } + [Fact] + public void BbsArcCreateArcTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List { + new AssetFile + { + Name = "somedir/somearc.arc", + Method = "bbsarc", + Source = new List { + new AssetFile + { + Name = "newfile", + Method = "copy", + Source = new List { + new AssetFile + { + Name = "somedir/somearc/newfile.bin" + } + } + } + } + } + } + }; + + CreateFile(ModInputDir, "somedir/somearc/newfile.bin").Using(x => + { + x.Write(new byte[] { 4, 5, 6, 7 }); + }); + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, patch.Assets[0].Name); + AssertArcFile("newfile", entry => + { + Assert.Equal(4, entry.Data.Length); + Assert.Equal(new byte[] { 4, 5, 6, 7 }, entry.Data); + }, ModOutputDir, patch.Assets[0].Name); + } + + [Fact] + public void BbsArcAddToArcTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List { + new AssetFile + { + Name = "somedir/somearc.arc", + Method = "bbsarc", + Source = new List { + new AssetFile + { + Name = "newfile", + Method = "copy", + Source = new List { + new AssetFile + { + Name = "somedir/somearc/newfile.bin" + } + } + } + } + } + } + }; + + CreateFile(AssetsInputDir, "somedir/somearc.arc").Using(x => + { + Arc.Write(new List + { + new Arc.Entry + { + Name = "abcd", + Data = new byte[] {0, 1, 2, 3 } + } + }, x); + }); + + CreateFile(ModInputDir, "somedir/somearc/newfile.bin").Using(x => + { + x.Write(new byte[] { 4, 5, 6, 7 }); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, patch.Assets[0].Name); + AssertArcFile("abcd", entry => + { + Assert.Equal(4, entry.Data.Length); + Assert.Equal(new byte[] { 0, 1, 2, 3 }, entry.Data); + }, ModOutputDir, patch.Assets[0].Name); + AssertArcFile("newfile", entry => + { + Assert.Equal(4, entry.Data.Length); + Assert.Equal(new byte[] { 4, 5, 6, 7 }, entry.Data); + }, ModOutputDir, patch.Assets[0].Name); + } + + [Fact] + public void BbsArcReplaceInArcTest() + { + var patcher = new PatcherProcessor(); + var patch = new Metadata + { + Assets = new List { + new AssetFile + { + Name = "somedir/somearc.arc", + Method = "bbsarc", + Source = new List { + new AssetFile + { + Name = "abcd", + Method = "copy", + Source = new List { + new AssetFile + { + Name = "somedir/somearc/abcd.bin" + } + } + } + } + } + } + }; + + CreateFile(AssetsInputDir, "somedir/somearc.arc").Using(x => + { + Arc.Write(new List + { + new Arc.Entry + { + Name = "abcd", + Data = new byte[] {0, 1, 2, 3} + } + }, x); + }); + + CreateFile(ModInputDir, "somedir/somearc/abcd.bin").Using(x => + { + x.Write(new byte[] { 4, 5, 6, 7 }); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); + + AssertFileExists(ModOutputDir, patch.Assets[0].Name); + AssertArcFile("abcd", entry => + { + Assert.Equal(4, entry.Data.Length); + Assert.Equal(new byte[] { 4, 5, 6, 7 }, entry.Data); + }, ModOutputDir, patch.Assets[0].Name); + } [Fact] public void ProcessMultipleTest() @@ -3350,6 +3506,23 @@ private static void AssertBarFile(string name, Action assertion, para assertion(entry); } + private static void AssertArcFile(string name, Action assertion, params string[] paths) + { + var filePath = Path.Join(paths); + var entries = File.OpenRead(filePath).Using(x => + { + if (!Arc.IsValid(x)) + Assert.Fail($"Not a valid Arc"); + return Arc.Read(x); + }); + + var entry = entries.SingleOrDefault(x => x.Name == name); + if (entry == null) + throw new XunitException($"Arc Entry '{name}' not found"); + + assertion(entry); + } + private static Stream CreateFile(params string[] paths) { var filePath = Path.Join(paths); From 02d4289d406511c8a903f440337e92c3ef6cdf75 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:56:27 -0400 Subject: [PATCH 16/21] Fix old version of creatingMods --- docs/tool/GUI.ModsManager/creatingMods.md | 235 ++++++++++++++++++---- 1 file changed, 195 insertions(+), 40 deletions(-) diff --git a/docs/tool/GUI.ModsManager/creatingMods.md b/docs/tool/GUI.ModsManager/creatingMods.md index 9a50a999c..1261f95a5 100644 --- a/docs/tool/GUI.ModsManager/creatingMods.md +++ b/docs/tool/GUI.ModsManager/creatingMods.md @@ -1,31 +1,125 @@ # [OpenKh Tool Documentation](../index.md) - KH2 Mods Manager -This document will focus on teaching you how to create mods using the OpenKH Mods Manager - -## Creating a mod +This document will focus on teaching you how to create mods using the OpenKH Mod Manager. + +# [Table of Contents]() +* [Creating a Mod for Use With OpenKH](#creating-a-mod-for-use-with-openkh) +* [Asset Types](#asset-types) +* [Asset Methods](#asset-methods) + * [copy](#copy-any-game---performs-a-direct-copy-to-overwrite-a-file-works-on-any-file-type) + * [binarc (KH2)](#binarc-kh2---specifies-a-modification-to-a-subfile-within-a-binarc-using-one-of-the-available-methods-see-binarc-methods-for-details-on-implementing-a-specific-method) + * [copy](#copy-kh2---performs-a-copy-on-a-supfile-within-a-bar-must-be-one-of-the-following-types) + * [imgd](#imgd-kh2---replaces-a-single-imgd-found-within-a-binarc) + * [imgz / fac](#imgz--fac-kh2---replaces-multiple-imgds-found-within-a-binarc) + * [kh2msg](#kh2msg-kh2---replaces-text-found-within-a-kh2-messages-file-uses-a-yaml-file-as-an-source) + * [areadatascript](#areadatascript-kh2---modifies-a-series-programs-found-within-a-kh2-spawnscript-subfile-located-within-ard-files-using-the-text-format-created-by-openkhcommandspawnscript-you-can-only-provide-a-subset-of-the-programs-found-within-the-spawnscript-the-others-will-be-taken-from-the-original-file) + * [areadataspawn](#areadataspawn-kh2---modifies-a-kh2-spawnpoint-subfile-located-within-ard-files-using-an-yaml-file-created-using-openkhcommandspawnscript) + * [listpatch](#listpatch-kh2---can-modify-the-following-different-types-of-list-binaries-found-within-kh2) + * [trsr](#trsr-source-example) + * [cmd](#cmd-source-example) + * [item](#item-source-example) + * [sklt](#sklt-source-example) + * [arif](#arif-source-example) + * [memt](#memt-source-example) + * [enmp](#enmp-source-example) + * [fmlv](#fmlv-source-example) + * [lvup](#lvup-source-example) + * [bons](#bons-source-example) + * [atkp](#atkp-source-example) + * [przt](#przt-source-example) + * [magc](#magc-source-example) + * [limt](#limt-source-example) + * [vtbl](#vtbl-source-example) + * [btlv](#btlv-source-example) + * [objentry](#objentry-source-example) + * [libretto](#libretto-source-example) + * [localset](#localset-source-example) + * [soundinfo](#soundinfo-source-example) + * [place](#place-source-example) + * [jigsaw](#jigsaw-source-example) + * [bbsarc](#bbsarc-bbs) + * [Example of a Fully Complete `mod.yml` File](#an-example-of-a-fully-complete-modyml-can-be-seen-below-and-the-full-source-of-the-mod-can-be-seen-here) +* [Generating a Simple `mod.yml` for New Mod Authors](#generating-a-simple-modyml-for-new-mod-authors) +* [Publishing a Mod on GitHub](#publishing-a-mod-on-github) + +## Creating a Mod for Use With OpenKH A well produced mod should contain the following -* mod.yml - The format of which is explained below -* icon.png - A 128x128 image which will be seen in the mods list -* preview.png - A image of maximum size 512 px wide by 288 px tall, which will be shown in the mod description on the right -* Other files - whatever files are needed for the mod, as defined by the mod.yml +* `mod.yml` - The format of which is explained below +* `icon.png` - A 128x128 image which will be seen in the mods list +* `preview.png` - A image of maximum size 512 px wide by 288 px tall, which will be shown in the mod description on the right +* Other files - whatever files are needed for the mod, as defined by the `mod.yml` -The mod.yml file is a YAML format specification for your mod. It will contain the following fields +The mod.yml file is a YAML format specification for your mod. It will contain the following fields: * `title` - The title of your mod, as displayed in the mods manager * `description` - A description of what your mod does -* `originalAuthor` - The name of the original author who created this mod. If you just ported this mod to the modsmanager for someone else, include the original authors name. +* `originalAuthor` - The name of the original author who created this mod. If you just ported this mod to the modsmanager for someone else, include the original author's name. If you do not, this is considered plagiarism, and people tend to not like that. * `logo` - The path to the icon.png -* `assets` - A list of assets that will be modified when the mod runs. See `asset types`, for details on creating an asset. Some asset types will work on any game, while others are game specific. - -While you are developing a mod you can create a folder inside the "mods" directory of the mods manager release, IE - -`/mods//` - -## Asset Methods - -* `copy` (any game) - Performs a direct copy to overwrite a file. Works on any file type. +* `assets` - A list of assets that will be modified when the mod runs. + * See [`asset types`](#asset-types), for details on creating an asset. Some asset types will work on any game, while others are game specific. + +While you are developing a mod you can create a folder inside the `mods` directory of the mod manager release. I.e.: + +`openkh/mods//` + +## Asset Types + +The truth of the matter is there are seemingly innumerable "asset types" in these games. Each game has several that are exclusive to itself, with little to none shared between any two or more. Some (but not all!) examples of the types of assets you may work with as a mod author are as follows: + +* Models + * .mdls + * .mdlx + * .cvbl + * .wpn + * .pmo + * .mdl +* Binary archives + * .bar + * .arc + * .ard + * .bin +* Sound archives + * .wd + * .seb + * .scd + * .snd +* Texture formats + * .img + * .imd / .imgd + * .imz / .imgz + * .dds / .png (PC only) + * .tm2 +* Movesets & animation + * .mset + * .anb + * .arc +* Menu & UI layouts + * .seqd + * .lad + * .l2d + * .2ld + * .2dd +* Maps & scripting + * .map + * .bar + * .arc + * .ard +* Many, many, many more... + +However, there is a point to knowing the many asset types the games have on offer. With enough know-how, custom events can be made, called, and integrated into their respective games. Not all of the formats above are from any one single game either; some are shared, some are specific to only one game. Some formats are not even the same between two games, despite having the same file extension. ~~I'm looking at you, `.arc`, `.ard`, and `.pmo`.~~ + +There are many tools at your disposal to edit a vast array of these formats, thanks to the large contributions over the years to the OpenKH project. We're far from finished yet, though, and would love more help looking into formats and making even more tools! + +Additionally, the type of format you are working with will work better with certain [asset methods](#asset-methods) as shown below. Many of the games' files are just archive formats with many other files inside. For example, KH2's `title.2ld` UI layout file for the title screen is an archive that contains a `.imz`, which is another archive with multiple images inside. It also contains `layout data` (primarily called `sequence data` in KH2) in the form of `.seqd` files, which help manage the intro logos and title screen assets contained within the aforementioned `.imz` image archive. + +In that example, if you were to simply replace one image of the title screen, you would only need to look as far as one of the several images within, which would best utilize the [`imgz`](#imgz--fac-kh2---replaces-multiple-imgds-found-within-a-binarc) asset method shown below. + + +# Asset Methods + +## `copy` (any game) - Performs a direct copy to overwrite a file. Works on any file type. Asset Example: @@ -36,7 +130,7 @@ Asset Example: - name: files/modified_msn.bar ``` -* `binarc` (KH2) - Specifies a modification to a subfile within a binarc, using one of the available methods. See `binarc methods` for details on implementing a specific method. +## `binarc` (KH2) - Specifies a modification to a subfile within a binarc, using one of the available methods. See `binarc methods` for details on implementing a specific method. Asset Example @@ -51,9 +145,9 @@ Asset Example type: AreaDataSpawn ``` -## Binarc Methods +### Binarc Methods -* `copy` (KH2) - Performs a copy on a supfile within a Bar. Must be one of the [following](https://github.com/Xeeynamo/OpenKh/blob/master/OpenKh.Tools.BarEditor/Helpers.cs#L14) types +## `copy` (KH2) - Performs a copy on a supfile within a Bar. Must be one of the [following](https://github.com/Xeeynamo/OpenKh/blob/master/OpenKh.Tools.BarEditor/Helpers.cs#L14) types Asset Example @@ -68,7 +162,7 @@ Asset Example type: Bdx ``` -* `imgd` (KH2) - Replaces a single imgd found within a binarc +## `imgd` (KH2) - Replaces a single imgd found within a binarc Asset Example @@ -87,7 +181,7 @@ Asset Example highdef: title/title1_hd.png ``` -* // `imgz` // `fac` (KH2) - Replaces multiple imgd's found within a binarc. +## `imgz` // `fac` (KH2) - Replaces multiple imgd's found within a binarc. Asset Example @@ -113,7 +207,7 @@ Asset Example index: 1 ``` -* `kh2msg` (KH2) - Replaces text found within a kh2 messages file. Uses a yaml file as an source. +## `kh2msg` (KH2) - Replaces text found within a kh2 messages file. Uses a yaml file as an source. Asset Example @@ -141,7 +235,7 @@ Yaml Source Example jp: OPENKHすばらしい! ``` -* `areadatascript` (KH2) - Modifies a series programs found within a KH2 Spawnscript subfile (located within ard files), using the text format created by OpenKh.Command.SpawnScript. You can only provide a subset of the programs found within the spawnscript, the others will be taken from the original file. +## `areadatascript` (KH2) - Modifies a series programs found within a KH2 Spawnscript subfile (located within ard files), using the text format created by OpenKh.Command.SpawnScript. You can only provide a subset of the programs found within the spawnscript, the others will be taken from the original file. Asset Example ``` @@ -168,7 +262,7 @@ AreaSettings 0 -1 SetPartyMenu 0 ``` -* `areadataspawn` (KH2) - Modifies a KH2 Spawnpoint subfile (located within ard files), using an yaml file created using OpenKh.Command.SpawnScript. +## `areadataspawn` (KH2) - Modifies a KH2 Spawnpoint subfile (located within ard files), using an yaml file created using OpenKh.Command.SpawnScript. Asset Example @@ -183,8 +277,7 @@ Asset Example type: AreaDataSpawn ``` -* `listpatch` (KH2) - Can modify the following different types of list binaries found within KH2. - +## `listpatch` (KH2) - Can modify the following different types of list binaries found within KH2: * `trsr` * `cmd` * `item` @@ -207,7 +300,6 @@ Asset Example * 'soundinfo' * 'place' * 'jigsaw' - Asset Example ``` @@ -706,7 +798,22 @@ Asset Example ShopUnlock: 201 ``` -An example of a fully complete mod.yml can be seen below, and the full source of the mod can be seen [here](https://github.com/OpenKH/mod-template) +### `bbsarc` (BBS) +Allows you to add/patch files inside a bbs `.arc` container without having to `copy` the entire arc file into your mod. You can use any method to patch those files, although at time of writing the only one that works for BBS files (other than `bbsarc`) is `copy`. + +Asset example: + +``` +- name: arc/map/SW10.arc + method: bbsarc + source: + - name: sw_10.pvd + method: copy + source: + - name: sw_10.pvd +``` + +### An example of a fully complete mod.yml can be seen below, and the full source of the mod can be seen [here](https://github.com/OpenKH/mod-template) ``` title: OpenKH mod template @@ -761,13 +868,61 @@ assets: language: it ``` -## Publishing a mod - -Mods should be published to a public github repository, so that users an install the mod just by providing the repository name. - - -It is recommended to apply the following tags to the repository, in order to make it easily found by searching GitHub for mods manager mods. - -`openkh-mods` - -`` (ie `kh2` or `bbs`) +## Generating a Simple `mod.yml` for New Mod Authors + +This method is recommended exclusively for mod authors, as the YAML Generator built into the Mod Manager is considered fully functional, yet lacks all of the aforementioned [asset methods](#asset-methods), with the exception of a couple. Those being: +* [`copy`](#copy-any-game---performs-a-direct-copy-to-overwrite-a-file-works-on-any-file-type) (game agnostic) +* [`binarc`](#binarc-kh2---specifies-a-modification-to-a-subfile-within-a-binarc-using-one-of-the-available-methods-see-binarc-methods-for-details-on-implementing-a-specific-method) (KH2 only so far) + +To start, here are the steps: + +1. Create an isolated folder where you will be working on your mod. Ideally, to make it easy for testing in-game, this would be in `openkh/mods/Your Name/Your Mod Name`. +2. To keep things simple, recreate the game's folder structure. I.e., if you were to edit KH2's title screen, Sora's HUD sprites, and replacing the font in KH2 with something goofy like Comic Sans, you would have a file tree that looks like this: +``` +├───menu +│ └───us +│ └───title.2ld +│ +└───remastered + └───msg + │ └───us + │ └───fontimage.bar + └───obj + └───P_EX100.a.us + └─── ~8.dds +``` + +3. Open the `Creator` menu on the top of Mod Manager, and select `YamlGenerator`. +4. Make the path to your `mod.yml` where your root mod folder is (i.e., where the example file structure would be located at). +5. Click `Generate or update mod.yml`. +6. Make the `GameDataPath` the same folder as where your new `mod.yml` has been generated. +7. Click `Begin`. +8. Click `Search` in the upper right. +9. Select all your files (**not** `mod.yml`). +10. Click `Copy each`. +11. Make sure the `[ ]Exists` box is checked. If it's not, you did something wrong. Retrace your steps. +12. Click `Proceed` at the bottom. +13. Answer yes, you would like to commit the change to the `mod.yml` file. +14. Close the creator and edit your `mod.yml` file with a text editor to change the following fields as necessary: + * `title` + * `originalAuthor` + * `description` +15. Save your changes and upload the entirety of this folder, including the `mod.yml` file to a new GitHub repository. +16. (Optional) Archive everything into a `.zip` archive and upload to Nexusmods under the appropriate game section. +17. +18. Profit! + +## Publishing a Mod on GitHub + +Mods should be published to a public GitHUb repository, so that users can install the mod just by providing the repository name. + +It is recommended to apply the following tags to the repository, in order to make it easily found by searching GitHub for mods manager mods: + +* `openkh-mods` + +* `` + * `kh1` + * `kh2` + * `bbs` + * `ddd` + * `recom` From 78791e1b4fa8d24223ab4b1cbac7fd11e63a20cc Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 21:05:51 -0400 Subject: [PATCH 17/21] fix ==== --- docs/tool/GUI.ModsManager/creatingMods.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/tool/GUI.ModsManager/creatingMods.md b/docs/tool/GUI.ModsManager/creatingMods.md index 2eb5b3bee..1261f95a5 100644 --- a/docs/tool/GUI.ModsManager/creatingMods.md +++ b/docs/tool/GUI.ModsManager/creatingMods.md @@ -798,7 +798,6 @@ Asset Example ShopUnlock: 201 ``` -======= ### `bbsarc` (BBS) Allows you to add/patch files inside a bbs `.arc` container without having to `copy` the entire arc file into your mod. You can use any method to patch those files, although at time of writing the only one that works for BBS files (other than `bbsarc`) is `copy`. From 825c74c60ffe3743eee65e3be24beeb3207ede1e Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 21:06:28 -0400 Subject: [PATCH 18/21] Fix merge conflict --- OpenKh.Tests/Patcher/PatcherTests.cs | 2257 -------------------------- 1 file changed, 2257 deletions(-) diff --git a/OpenKh.Tests/Patcher/PatcherTests.cs b/OpenKh.Tests/Patcher/PatcherTests.cs index 800ae7da9..ffc7ff4fa 100644 --- a/OpenKh.Tests/Patcher/PatcherTests.cs +++ b/OpenKh.Tests/Patcher/PatcherTests.cs @@ -3483,2263 +3483,6 @@ public void ProcessMultipleTest() } - private static void AssertFileExists(params string[] paths) - { - var filePath = Path.Join(paths); - if (File.Exists(filePath) == false) - Assert.Fail($"File not found '{filePath}'"); - } - - private static void AssertBarFile(string name, Action assertion, params string[] paths) - { - var filePath = Path.Join(paths); - var entries = File.OpenRead(filePath).Using(x => - { - if (!Bar.IsValid(x)) - Assert.Fail($"Not a valid BinArc"); - return Bar.Read(x); - }); - - var entry = entries.SingleOrDefault(x => x.Name == name); - if (entry == null) - throw new XunitException($"Entry '{name}' not found"); - - assertion(entry); - } - - private static void AssertArcFile(string name, Action assertion, params string[] paths) - { - var filePath = Path.Join(paths); - var entries = File.OpenRead(filePath).Using(x => - { - if (!Arc.IsValid(x)) - Assert.Fail($"Not a valid Arc"); - return Arc.Read(x); - }); - - var entry = entries.SingleOrDefault(x => x.Name == name); - if (entry == null) - throw new XunitException($"Arc Entry '{name}' not found"); - - assertion(entry); - } - - private static Stream CreateFile(params string[] paths) - { - var filePath = Path.Join(paths); - var dirPath = Path.GetDirectoryName(filePath); - Directory.CreateDirectory(dirPath); - return File.Create(filePath); - } - } -} -======= -using OpenKh.Bbs; -using OpenKh.Command.Bdxio.Utils; -using OpenKh.Common; -using OpenKh.Imaging; -using OpenKh.Kh2; -using OpenKh.Kh2.Messages; -using OpenKh.Patcher; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Xunit; -using Xunit.Sdk; -using YamlDotNet.Serialization; - -namespace OpenKh.Tests.Patcher -{ - public class PatcherTests : IDisposable - { - private const string AssetsInputDir = "original_input"; - private const string ModInputDir = "mod_input"; - private const string ModOutputDir = "mod_output"; - - public PatcherTests() - { - Dispose(); - Directory.CreateDirectory(AssetsInputDir); - Directory.CreateDirectory(ModInputDir); - Directory.CreateDirectory(ModOutputDir); - } - - public void Dispose() - { - if (Directory.Exists(AssetsInputDir)) - Directory.Delete(AssetsInputDir, true); - if (Directory.Exists(ModInputDir)) - Directory.Delete(ModInputDir, true); - if (Directory.Exists(ModOutputDir)) - Directory.Delete(ModOutputDir, true); - } - - [Fact] - public void Kh2CopyBinariesTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bin", - Method = "copy", - Source = new List - { - new AssetFile - { - Name = "somedir/somefile.bin" - } - } - } - } - }; - - CreateFile(ModInputDir, patch.Assets[0].Name).Dispose(); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void Kh2CreateBinArcIfSourceDoesntExistsTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bar", - Method = "binarc", - Source = new List - { - new AssetFile - { - Name = "abcd", - Type = "list", - Method = "copy", - Source = new List - { - new AssetFile - { - Name = "somedir/somefile/abcd.bin" - } - } - } - } - } - } - }; - - CreateFile(ModInputDir, "somedir/somefile/abcd.bin").Using(x => - { - x.WriteByte(0); - x.WriteByte(1); - x.WriteByte(2); - x.WriteByte(3); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertBarFile("abcd", entry => - { - Assert.Equal(Bar.EntryType.List, entry.Type); - Assert.Equal(4, entry.Stream.Length); - Assert.Equal(0, entry.Stream.ReadByte()); - Assert.Equal(1, entry.Stream.ReadByte()); - Assert.Equal(2, entry.Stream.ReadByte()); - Assert.Equal(3, entry.Stream.ReadByte()); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void Kh2MergeWithOriginalBinArcTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bar", - Method = "binarc", - Source = new List - { - new AssetFile - { - Name = "abcd", - Type = "list", - Method = "copy", - Source = new List - { - new AssetFile - { - Name = "somedir/somefile/abcd.bin" - } - } - } - } - } - } - }; - - CreateFile(ModInputDir, "somedir/somefile/abcd.bin").Using(x => - { - x.WriteByte(0); - x.WriteByte(1); - x.WriteByte(2); - x.WriteByte(3); - }); - - CreateFile(AssetsInputDir, "somedir/somefile.bar").Using(x => - { - Bar.Write(x, new Bar - { - new Bar.Entry - { - Name = "nice", - Type = Bar.EntryType.Model, - Stream = new MemoryStream(new byte[] { 4, 5, 6, 7 }) - } - }); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertBarFile("abcd", entry => - { - Assert.Equal(Bar.EntryType.List, entry.Type); - Assert.Equal(4, entry.Stream.Length); - Assert.Equal(0, entry.Stream.ReadByte()); - Assert.Equal(1, entry.Stream.ReadByte()); - Assert.Equal(2, entry.Stream.ReadByte()); - Assert.Equal(3, entry.Stream.ReadByte()); - }, ModOutputDir, patch.Assets[0].Name); - AssertBarFile("nice", entry => - { - Assert.Equal(Bar.EntryType.Model, entry.Type); - Assert.Equal(4, entry.Stream.Length); - Assert.Equal(4, entry.Stream.ReadByte()); - Assert.Equal(5, entry.Stream.ReadByte()); - Assert.Equal(6, entry.Stream.ReadByte()); - Assert.Equal(7, entry.Stream.ReadByte()); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void Kh2CreateImgdTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bar", - Method = "binarc", - Source = new List - { - new AssetFile - { - Name = "test", - Method = "imgd", - Type = "imgd", - Source = new List - { - new AssetFile - { - Name = "sample.png", - IsSwizzled = false - } - } - } - } - } - } - }; - - File.Copy("Imaging/res/png/32.png", Path.Combine(ModInputDir, "sample.png")); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertBarFile("test", entry => - { - Assert.True(Imgd.IsValid(entry.Stream)); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void Kh2MergeImzTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "out.imz", - Method = "imgz", - Source = new List - { - new AssetFile - { - Name = "test.imd", - Index = 1, - } - } - } - } - }; - - var tmpImd = Imgd.Create(new System.Drawing.Size(16, 16), PixelFormat.Indexed4, new byte[16 * 16 / 2], new byte[4], false); - var patchImd = Imgd.Create(new System.Drawing.Size(32, 16), PixelFormat.Indexed4, new byte[32 * 16 / 2], new byte[4], false); - CreateFile(AssetsInputDir, "out.imz").Using(x => - { - Imgz.Write(x, new Imgd[] - { - tmpImd, - tmpImd, - tmpImd, - }); - }); - CreateFile(ModInputDir, "test.imd").Using(patchImd.Write); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "out.imz"); - File.OpenRead(Path.Combine(ModOutputDir, "out.imz")).Using(x => - { - var images = Imgz.Read(x).ToList(); - Assert.Equal(3, images.Count); - Assert.Equal(16, images[0].Size.Width); - Assert.Equal(32, images[1].Size.Width); - Assert.Equal(16, images[2].Size.Width); - }); - } - - [Fact] - public void Kh2MergeImzInsideBarTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "out.bar", - Method = "binarc", - Source = new List - { - new AssetFile - { - Name = "test", - Type = "imgz", - Method = "imgz", - Source = new List - { - new AssetFile - { - Name = "test.imd", - Index = 1 - } - }, - } - } - } - } - }; - - var tmpImd = Imgd.Create(new System.Drawing.Size(16, 16), PixelFormat.Indexed4, new byte[16 * 16 / 2], new byte[4], false); - var patchImd = Imgd.Create(new System.Drawing.Size(32, 16), PixelFormat.Indexed4, new byte[32 * 16 / 2], new byte[4], false); - CreateFile(AssetsInputDir, "out.bar").Using(x => - { - using var memoryStream = new MemoryStream(); - Imgz.Write(memoryStream, new Imgd[] - { - tmpImd, - tmpImd, - tmpImd, - }); - - Bar.Write(x, new Bar - { - new Bar.Entry - { - Name = "test", - Type = Bar.EntryType.Imgz, - Stream = memoryStream - } - }); - }); - CreateFile(ModInputDir, "test.imd").Using(patchImd.Write); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "out.bar"); - AssertBarFile("test", x => - { - var images = Imgz.Read(x.Stream).ToList(); - Assert.Equal(3, images.Count); - Assert.Equal(16, images[0].Size.Width); - Assert.Equal(32, images[1].Size.Width); - Assert.Equal(16, images[2].Size.Width); - }, ModOutputDir, "out.bar"); - } - - [Fact] - public void MergeKh2MsgTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "msg/us/sys.msg", - Method = "kh2msg", - Source = new List - { - new AssetFile - { - Name = "sys.yml", - Language = "en", - } - } - }, - new AssetFile - { - Name = "msg/it/sys.msg", - Method = "kh2msg", - Source = new List - { - new AssetFile - { - Name = "sys.yml", - Language = "it", - } - } - }, - new AssetFile - { - Name = "msg/jp/sys.msg", - Method = "kh2msg", - Source = new List - { - new AssetFile - { - Name = "sys.yml", - Language = "jp", - } - } - } - } - }; - - Directory.CreateDirectory(Path.Combine(AssetsInputDir, "msg/us/")); - File.Create(Path.Combine(AssetsInputDir, "msg/us/sys.msg")).Using(stream => - { - Msg.Write(stream, new List - { - new Msg.Entry - { - Data = new byte[] { 1, 2, 3, 0 }, - Id = 123 - } - }); - }); - File.Create(Path.Combine(ModInputDir, "sys.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- id: 456"); - writer.WriteLine(" en: English"); - writer.WriteLine(" it: Italiano"); - writer.WriteLine(" jp: テスト"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "msg/jp/sys.msg"); - File.OpenRead(Path.Combine(ModOutputDir, "msg/jp/sys.msg")).Using(stream => - { - var msg = Msg.Read(stream); - Assert.Single(msg); - Assert.Equal(456, msg[0].Id); - Assert.Equal("テスト", Encoders.JapaneseSystem.Decode(msg[0].Data).First().Text); - }); - - AssertFileExists(ModOutputDir, "msg/us/sys.msg"); - File.OpenRead(Path.Combine(ModOutputDir, "msg/us/sys.msg")).Using(stream => - { - var msg = Msg.Read(stream); - Assert.Equal(2, msg.Count); - Assert.Equal(123, msg[0].Id); - Assert.Equal(456, msg[1].Id); - Assert.Equal("English", Encoders.InternationalSystem.Decode(msg[1].Data).First().Text); - }); - - AssertFileExists(ModOutputDir, "msg/it/sys.msg"); - File.OpenRead(Path.Combine(ModOutputDir, "msg/it/sys.msg")).Using(stream => - { - var msg = Msg.Read(stream); - Assert.Single(msg); - Assert.Equal(456, msg[0].Id); - Assert.Equal("Italiano", Encoders.InternationalSystem.Decode(msg[0].Data).First().Text); - }); - } - - [Fact] - public void MergeKh2AreaDataScriptTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "map.script", - Method = "areadatascript", - Source = new List - { - new AssetFile - { - Name = "map.txt", - } - } - }, - } - }; - - File.Create(Path.Combine(AssetsInputDir, "map.script")).Using(stream => - { - var compiledProgram = Kh2.Ard.AreaDataScript.Compile("Program 1\nSpawn \"1111\""); - Kh2.Ard.AreaDataScript.Write(stream, compiledProgram); - }); - File.Create(Path.Combine(ModInputDir, "map.txt")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("Program 2"); - writer.WriteLine("Spawn \"2222\""); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "map.script"); - File.OpenRead(Path.Combine(ModOutputDir, "map.script")).Using(stream => - { - var scripts = Kh2.Ard.AreaDataScript.Read(stream); - var decompiled = Kh2.Ard.AreaDataScript.Decompile(scripts); - decompiled.Contains("Program 1"); - decompiled.Contains("Spawn \"1111\""); - decompiled.Contains("Program 2"); - decompiled.Contains("Spawn \"2222\""); - }); - } - - [Fact] - public void PatchKh2BdscriptTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "aaa", - Method = "bdscript", - Source = new List - { - new AssetFile - { - Name = "test.bdscript", - } - } - }, - } - }; - File.Create(Path.Combine(ModInputDir, "test.bdscript")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("---"); - writer.WriteLine("WorkSize: 64"); - writer.WriteLine("StackSize: 64"); - writer.WriteLine("TempSize: 64"); - writer.WriteLine("Triggers:"); - writer.WriteLine("- Key: 0"); - writer.WriteLine(" Addr: TR0"); - writer.WriteLine("Name: aaa"); - writer.WriteLine("---"); - writer.WriteLine(" section .text"); - writer.WriteLine("TR0:"); - writer.WriteLine(" ret"); - writer.WriteLine("DUMMY:"); - writer.WriteLine(" ret"); - writer.Flush(); - }); - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "aaa"); - - var bdxStream = new MemoryStream(File.ReadAllBytes(Path.Combine(ModOutputDir, "aaa"))); - var decoder = new BdxDecoder(bdxStream); - var script = BdxDecoder.TextFormatter.Format(decoder); - - var lines = script.Split("\r\n"); - - Assert.Equal("WorkSize: 64", lines[1]); - Assert.Equal("Name: aaa", lines[7]); - - } - - [Fact] - public void PatchKh2SpawnPointTest() - { - - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "map.script", - Method = "areadataspawn", - Source = new List - { - new AssetFile - { - Name = "map.yaml", - } - } - }, - } - }; - - File.Create(Path.Combine(AssetsInputDir, "map.script")).Using(stream => - { - var spawnPoint = new List(); - - Kh2.Ard.SpawnPoint.Write(stream, spawnPoint); - }); - File.Create(Path.Combine(ModInputDir, "map.yaml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Type: 2"); - writer.WriteLine(" Flag: 1"); - writer.Flush(); - }); - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "map.script"); - var content = File.ReadAllText(Path.Combine(ModOutputDir, "map.script")); - var scripts = new Deserializer().Deserialize> (content); - - Assert.Equal(2, scripts[0].Type); - Assert.Equal(1, scripts[0].Flag); - - } - - - public void ListPatchTrsrTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "trsr", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "TrsrList.yml", - Type = "trsr" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var trsrEntry = new List() - { - new Kh2.SystemData.Trsr - { - Id = 1, - ItemId = 10 - } - }; - using var trsrStream = new MemoryStream(); - Kh2.SystemData.Trsr.Write(trsrStream, trsrEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "trsr", - Type = Bar.EntryType.List, - Stream = trsrStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "TrsrList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("1:"); - writer.WriteLine(" ItemId: 200"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var trsrStream = Kh2.SystemData.Trsr.Read(binarc[0].Stream); - Assert.Equal(200, trsrStream[0].ItemId); - }); - - } - - [Fact] - public void ListPatchCmdTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "cmd", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "CmdList.yml", - Type = "cmd" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var cmdEntry = new List() - { - new Kh2.SystemData.Cmd - { - Id = 1, - Execute = 3, - Argument = 3, - SubMenu = 1, - } - }; - using var cmdStream = new MemoryStream(); - Kh2.SystemData.Cmd.Write(cmdStream, cmdEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "cmd", - Type = Bar.EntryType.List, - Stream = cmdStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "CmdList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Id: 1"); - writer.WriteLine(" Execute: 3"); - writer.WriteLine(" Argument: 3"); - writer.WriteLine(" SubMenu: 1"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var cmdStream = Kh2.SystemData.Cmd.Read(binarc[0].Stream); - Assert.Equal(1, cmdStream[0].Id); - Assert.Equal(3, cmdStream[0].Execute); - Assert.Equal(3, cmdStream[0].Argument); - Assert.Equal(1, cmdStream[0].SubMenu); - }); - - } - - [Fact] - public void ListPatchItemTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "item", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "ItemList.yml", - Type = "item" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var itemEntry = new List() - { - new Kh2.SystemData.Item - { - Items = new List() - { - new Kh2.SystemData.Item.Entry() - { - Id = 1, - ShopBuy = 10 - } - }, - Stats = new List() - { - new Kh2.SystemData.Item.Stat() - { - Id = 10, - Ability = 15 - } - } - - } - }; - using var itemStream = new MemoryStream(); - itemEntry[0].Write(itemStream); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "item", - Type = Bar.EntryType.List, - Stream = itemStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "ItemList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("Items:"); - writer.WriteLine("- Id: 1"); - writer.WriteLine(" ShopBuy: 200"); - writer.WriteLine("Stats:"); - writer.WriteLine("- Id: 10"); - writer.WriteLine(" Ability: 150"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var itemStream = Kh2.SystemData.Item.Read(binarc[0].Stream); - Assert.Equal(200, itemStream.Items[0].ShopBuy); - Assert.Equal(150, itemStream.Stats[0].Ability); - }); - - } - - [Fact] - public void ListPatchSkltTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "03system.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "sklt", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "SkltList.yml", - Type = "sklt" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "03system.bar")).Using(stream => - { - var skltEntry = new List() - { - new Kh2.SystemData.Sklt - { - CharacterId = 1, - Bone1 = 178, - Bone2 = 86 - } - }; - - using var skltStream = new MemoryStream(); - Kh2.SystemData.Sklt.Write(skltStream, skltEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "sklt", - Type = Bar.EntryType.List, - Stream = skltStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "SkltList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- CharacterId: 1"); - writer.WriteLine(" Bone1: 178"); - writer.WriteLine(" Bone2: 86"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "03system.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "03system.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var skltStream = Kh2.SystemData.Sklt.Read(binarc[0].Stream); - Assert.Equal(1U, skltStream[0].CharacterId); - Assert.Equal(178, skltStream[0].Bone1); - Assert.Equal(86, skltStream[0].Bone2); - }); - } - - [Fact] - public void ListPatchFmlvTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "fmlv", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "FmlvList.yml", - Type = "fmlv" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var fmlvEntry = new List() - { - new Kh2.Battle.Fmlv - { - FormId = 1, - FormLevel = 1, - Exp = 100, - Ability = 200 - }, - new Kh2.Battle.Fmlv - { - FormId = 1, - FormLevel = 2, - Exp = 100, - Ability = 200 - }, - new Kh2.Battle.Fmlv - { - FormId = 2, - FormLevel = 1, - Exp = 100, - Ability = 200 - }, - }; - - using var fmlvStream = new MemoryStream(); - Kh2.Battle.Fmlv.Write(fmlvStream, fmlvEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "fmlv", - Type = Bar.EntryType.List, - Stream = fmlvStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "FmlvList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - serializer.Serialize(writer, new Dictionary - { - ["Valor"] = new[] - { - new FmlvDTO - { - FormLevel = 1, - Experience = 5, - Ability = 127 - } - } - }); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var fmlv = Kh2.Battle.Fmlv.Read(binarc[0].Stream); - - Assert.Equal(3, fmlv.Count); - Assert.Equal(1, fmlv[0].FormId); - Assert.Equal(1, fmlv[0].FormLevel); - Assert.Equal(5, fmlv[0].Exp); - Assert.Equal(127, fmlv[0].Ability); - - Assert.Equal(1, fmlv[1].FormId); - Assert.Equal(2, fmlv[1].FormLevel); - - Assert.Equal(2, fmlv[2].FormId); - Assert.Equal(1, fmlv[2].FormLevel); - }); - } - - [Fact] - public void ListPatchBonsTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "bons", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "BonsList.yml", - Type = "bons" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var bonsEntry = new List() - { - new Kh2.Battle.Bons - { - CharacterId = 1, - RewardId = 15, - BonusItem1 = 10 - }, - new Kh2.Battle.Bons - { - CharacterId = 2, - RewardId = 15, - BonusItem1 = 5 - } - }; - using var bonsStream = new MemoryStream(); - Kh2.Battle.Bons.Write(bonsStream, bonsEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "bons", - Type = Bar.EntryType.List, - Stream = bonsStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "BonsList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("15:"); - writer.WriteLine(" Sora:"); - writer.WriteLine(" BonusItem1: 200"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var bonsStream = Kh2.Battle.Bons.Read(binarc[0].Stream); - Assert.Equal(200, bonsStream[0].BonusItem1); - Assert.Equal(5, bonsStream[1].BonusItem1); - }); - - } - - [Fact] - public void ListPatchLvupTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bin", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "lvup", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "LvupList.yml", - Type = "lvup" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bin")).Using(stream => - { - var lvupEntry = new Kh2.Battle.Lvup - { - Count = 13, - Unknown08 = new byte[0x38], - Characters = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - }, - new Kh2.Battle.Lvup.PlayableCharacter() - { - NumLevels = 1, - Levels = new List() - { - new Kh2.Battle.Lvup.PlayableCharacter.Level() - { - Exp = 50 - } - } - } - - } - }; - using var lvupStream = new MemoryStream(); - lvupEntry.Write(lvupStream); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "lvup", - Type = Bar.EntryType.List, - Stream = lvupStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "LvupList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("Sora:"); - writer.WriteLine(" 1:"); - writer.WriteLine(" Exp: 500"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bin"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bin")).Using(stream => - { - var binarc = Bar.Read(stream); - var lvupStream = Kh2.Battle.Lvup.Read(binarc[0].Stream); - Assert.Equal(500, lvupStream.Characters[0].Levels[0].Exp); - }); - - } - - void ListPatchAtkpTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "atkp", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "AtkpList.yml", - Type = "atkp" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var atkpEntry = new List() - { - new Kh2.Battle.Atkp - { - Id = 0, - SubId = 3, - Type = Kh2.Battle.Atkp.AttackType.PierceArmor, - CriticalAdjust = 0, - Power = 25, - Team = 0, - Element = 0, - EnemyReaction = 0, - EffectOnHit = 2, - KnockbackStrength1 = 32767, - KnockbackStrength2 = 0, - Unknown = 0, - Flags = Kh2.Battle.Atkp.AttackFlags.BGHit, - RefactSelf = Kh2.Battle.Atkp.Refact.Reflect, - RefactOther = Kh2.Battle.Atkp.Refact.Reflect, - ReflectedMotion = 0, - ReflectHitBack = 0, - ReflectAction = 0, - ReflectHitSound = 0, - ReflectRC = 0, - ReflectRange = 0, - ReflectAngle = 0, - DamageEffect = 0, - Switch = 1, - Interval = 1, - FloorCheck = 1, - DriveDrain = 1, - RevengeDamage = 1, - AttackTrReaction = Kh2.Battle.Atkp.TrReaction.Charge, - ComboGroup = 1, - RandomEffect = 1, - Kind = Kh2.Battle.Atkp.AttackKind.ComboFinisher, - HpDrain = 15 - } - }; - - using var atkpStream = new MemoryStream(); - Kh2.Battle.Atkp.Write(atkpStream, atkpEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "atkp", - Type = Bar.EntryType.List, - Stream = atkpStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "AtkpList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("- Id: 0"); - writer.WriteLine(" SubId: 3"); - writer.WriteLine(" Type: PierceArmor"); - writer.WriteLine(" CriticalAdjust: 0"); - writer.WriteLine(" Power: 25"); - writer.WriteLine(" Team: 0"); - writer.WriteLine(" Element: 0"); - writer.WriteLine(" EnemyReaction: 0"); - writer.WriteLine(" EffectOnHit: 2"); - writer.WriteLine(" KnockbackStrength1: 32767"); - writer.WriteLine(" KnockbackStrength2: 0"); - writer.WriteLine(" Unknown: 0"); - writer.WriteLine(" Flags: BGHit"); - writer.WriteLine(" RefactSelf: 0"); - writer.WriteLine(" RefactOther: 0"); - writer.WriteLine(" ReflectedMotion: 0"); - writer.WriteLine(" ReflectHitBack: 0"); - writer.WriteLine(" ReflectAction: 0"); - writer.WriteLine(" ReflectHitSound: 0"); - writer.WriteLine(" ReflectRC: 0"); - writer.WriteLine(" ReflectRange: 0"); - writer.WriteLine(" ReflectAngle: 0"); - writer.WriteLine(" DamageEffect: 0"); - writer.WriteLine(" Switch: 1"); - writer.WriteLine(" Interval: 1"); - writer.WriteLine(" FloorCheck: 1"); - writer.WriteLine(" DriveDrain: 1"); - writer.WriteLine(" RevengeDamage: 1"); - writer.WriteLine(" AttackTrReaction: 1"); - writer.WriteLine(" ComboGroup: 1"); - writer.WriteLine(" RandomEffect: 1"); - writer.WriteLine(" Kind: ComboFinisher"); - writer.WriteLine(" HpDrain: 15"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var atkpStream = Kh2.Battle.Atkp.Read(binarc[0].Stream); - Assert.Equal(0, atkpStream[0].Id); - Assert.Equal(3, atkpStream[0].SubId); - Assert.Equal(Kh2.Battle.Atkp.AttackType.PierceArmor, atkpStream[0].Type); - Assert.Equal(0, atkpStream[0].CriticalAdjust); - Assert.Equal(25, atkpStream[0].Power); - Assert.Equal(0, atkpStream[0].Team); - Assert.Equal(0, atkpStream[0].Element); - Assert.Equal(0, atkpStream[0].EnemyReaction); - Assert.Equal(2, atkpStream[0].EffectOnHit); - Assert.Equal(32767, atkpStream[0].KnockbackStrength1); - Assert.Equal(0, atkpStream[0].KnockbackStrength2); - Assert.Equal(0000, atkpStream[0].Unknown); - Assert.Equal(Kh2.Battle.Atkp.AttackFlags.BGHit, atkpStream[0].Flags); - Assert.Equal(Kh2.Battle.Atkp.Refact.Reflect, atkpStream[0].RefactSelf); - Assert.Equal(Kh2.Battle.Atkp.Refact.Reflect, atkpStream[0].RefactOther); - Assert.Equal(0, atkpStream[0].ReflectedMotion); - Assert.Equal(0, atkpStream[0].ReflectHitBack); - Assert.Equal(0, atkpStream[0].ReflectAction); - Assert.Equal(0, atkpStream[0].ReflectHitSound); - Assert.Equal(0, atkpStream[0].ReflectRC); - Assert.Equal(0, atkpStream[0].ReflectRange); - Assert.Equal(0, atkpStream[0].ReflectAngle); - Assert.Equal(0, atkpStream[0].DamageEffect); - Assert.Equal(1, atkpStream[0].Switch); - Assert.Equal(1, atkpStream[0].Interval); - Assert.Equal(1, atkpStream[0].FloorCheck); - Assert.Equal(1, atkpStream[0].DriveDrain); - Assert.Equal(1, atkpStream[0].RevengeDamage); - Assert.Equal(Kh2.Battle.Atkp.TrReaction.Charge, atkpStream[0].AttackTrReaction); - Assert.Equal(1, atkpStream[0].ComboGroup); - Assert.Equal(1, atkpStream[0].RandomEffect); - Assert.Equal(Kh2.Battle.Atkp.AttackKind.ComboFinisher, atkpStream[0].Kind); - Assert.Equal(15, atkpStream[0].HpDrain); - }); - } - - [Fact] - public void ListPatchObjEntryTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00objentry.bin", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "ObjList.yml", - Type = "objentry", - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00objentry.bin")).Using(stream => - { - var objEntry = new List() - { - new Kh2.Objentry - { - ObjectId = 1, - ModelName = "M_EX060", - AnimationName = "M_EX060.mset" - } - }; - Kh2.Objentry.Write(stream, objEntry); - }); - - File.Create(Path.Combine(ModInputDir, "ObjList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - writer.WriteLine("1:"); - writer.WriteLine(" ObjectId: 1"); - writer.WriteLine(" ModelName: M_EX100"); - writer.WriteLine(" AnimationName: M_EX100.mset"); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00objentry.bin"); - - File.OpenRead(Path.Combine(ModOutputDir, "00objentry.bin")).Using(stream => - { - var objStream = Kh2.Objentry.Read(stream); - Assert.Equal("M_EX100", objStream[0].ModelName); - Assert.Equal("M_EX100.mset", objStream[0].AnimationName); - }); - - } - - [Fact] - public void ListPatchPlrpTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "plrp", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "PlrpList.yml", - Type = "plrp" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var plrpEntry = new List() - { - new Kh2.Battle.Plrp - { - Id = 7, - Character = 1, - Ap = 2, - Items = new List(32), - Padding = new byte[52] - } - }; - - using var plrpStream = new MemoryStream(); - Kh2.Battle.Plrp.Write(plrpStream, plrpEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "plrp", - Type = Bar.EntryType.List, - Stream = plrpStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "PlrpList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - var moddedPlrp = new List{ - new Kh2.Battle.Plrp - { - Id = 7, - Character = 1, - Ap = 200, - Items = new List(32), - Padding = new byte[52] - } - }; - writer.Write(serializer.Serialize(moddedPlrp)); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var plrp = Kh2.Battle.Plrp.Read(binarc[0].Stream); - - Assert.Equal(200, plrp[0].Ap); - }); - } - - [Fact] - public void ListPatchEnmpTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "enmp", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "EnmpList.yml", - Type = "enmp" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var enmpEntry = new List() - { - new Kh2.Battle.Enmp - { - Id = 7, - Level = 1, - Health = new short[32], - MaxDamage = 1, - MinDamage = 1, - PhysicalWeakness = 1, - FireWeakness = 1, - IceWeakness = 1, - ThunderWeakness = 1, - DarkWeakness = 1, - LightWeakness = 1, - GeneralWeakness = 1, - Experience = 1, - Prize = 1, - BonusLevel = 1 - } - }; - - using var enmpStream = new MemoryStream(); - Kh2.Battle.Enmp.Write(enmpStream, enmpEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "enmp", - Type = Bar.EntryType.List, - Stream = enmpStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "EnmpList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - var moddedEnmp = new List{ - new Kh2.Battle.Enmp - { - Id = 7, - Level = 1, - Health = new short[32], - MaxDamage = 1, - MinDamage = 1, - PhysicalWeakness = 1, - FireWeakness = 1, - IceWeakness = 1, - ThunderWeakness = 1, - DarkWeakness = 1, - LightWeakness = 1, - GeneralWeakness = 1, - Experience = 1, - Prize = 1, - BonusLevel = 1 - } - }; - writer.Write(serializer.Serialize(moddedEnmp)); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var enmp = Kh2.Battle.Enmp.Read(binarc[0].Stream); - - Assert.Equal(1, enmp[0].Level); - }); - } - - [Fact] - public void ListPatchMagcTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "magc", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "MagcList.yml", - Type = "magc" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var magcEntry = new List() - { - new Kh2.Battle.Magc - { - Id = 7, - Level = 3, - World = 1, - FileName = "magic/FIRE_7.mag" - } - }; - - using var magcStream = new MemoryStream(); - Kh2.Battle.Magc.Write(magcStream, magcEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "magc", - Type = Bar.EntryType.List, - Stream = magcStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "MagcList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - var moddedMagc = new List{ - new Kh2.Battle.Magc - { - Id = 7, - Level = 3, - World = 1, - FileName = "magic/FIRE_7.mag" - } - }; - writer.Write(serializer.Serialize(moddedMagc)); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var magc = Kh2.Battle.Magc.Read(binarc[0].Stream); - - Assert.Equal(3, magc[0].Level); - }); - } - - [Fact] - public void ListPatchPrztTest() - { - var patcher = new PatcherProcessor(); - var serializer = new Serializer(); - var patch = new Metadata() - { - Assets = new List() - { - new AssetFile() - { - Name = "00battle.bar", - Method = "binarc", - Source = new List() - { - new AssetFile() - { - Name = "przt", - Method = "listpatch", - Type = "List", - Source = new List() - { - new AssetFile() - { - Name = "PrztList.yml", - Type = "przt" - } - } - } - } - } - } - }; - - File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => - { - var prztEntry = new List() - { - new Kh2.Battle.Przt - { - Id = 1, - SmallHpOrbs = 0, - BigHpOrbs = 1 - } - }; - - using var prztStream = new MemoryStream(); - Kh2.Battle.Przt.Write(prztStream, prztEntry); - Bar.Write(stream, new Bar() { - new Bar.Entry() - { - Name = "przt", - Type = Bar.EntryType.List, - Stream = prztStream - } - }); - }); - - File.Create(Path.Combine(ModInputDir, "PrztList.yml")).Using(stream => - { - var writer = new StreamWriter(stream); - var serializer = new Serializer(); - var moddedPrzt = new List{ - new Kh2.Battle.Przt - { - Id = 1, - SmallHpOrbs = 0, - BigHpOrbs = 1 - } - }; - writer.Write(serializer.Serialize(moddedPrzt)); - writer.Flush(); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, "00battle.bar"); - - File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => - { - var binarc = Bar.Read(stream); - var przt = Kh2.Battle.Przt.Read(binarc[0].Stream); - - Assert.Equal(1, przt[0].BigHpOrbs); - }); - } - - [Fact] - public void BbsArcCreateArcTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List { - new AssetFile - { - Name = "somedir/somearc.arc", - Method = "bbsarc", - Source = new List { - new AssetFile - { - Name = "newfile", - Method = "copy", - Source = new List { - new AssetFile - { - Name = "somedir/somearc/newfile.bin" - } - } - } - } - } - } - }; - - CreateFile(ModInputDir, "somedir/somearc/newfile.bin").Using(x => - { - x.Write(new byte[] { 4, 5, 6, 7 }); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertArcFile("newfile", entry => - { - Assert.Equal(4, entry.Data.Length); - Assert.Equal(new byte[] { 4, 5, 6, 7 }, entry.Data); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void BbsArcAddToArcTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List { - new AssetFile - { - Name = "somedir/somearc.arc", - Method = "bbsarc", - Source = new List { - new AssetFile - { - Name = "newfile", - Method = "copy", - Source = new List { - new AssetFile - { - Name = "somedir/somearc/newfile.bin" - } - } - } - } - } - } - }; - - CreateFile(AssetsInputDir, "somedir/somearc.arc").Using(x => - { - Arc.Write(new List - { - new Arc.Entry - { - Name = "abcd", - Data = new byte[] {0, 1, 2, 3 } - } - }, x); - }); - - CreateFile(ModInputDir, "somedir/somearc/newfile.bin").Using(x => - { - x.Write(new byte[] { 4, 5, 6, 7 }); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertArcFile("abcd", entry => - { - Assert.Equal(4, entry.Data.Length); - Assert.Equal(new byte[] { 0, 1, 2, 3 }, entry.Data); - }, ModOutputDir, patch.Assets[0].Name); - AssertArcFile("newfile", entry => - { - Assert.Equal(4, entry.Data.Length); - Assert.Equal(new byte[] { 4, 5, 6, 7 }, entry.Data); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void BbsArcReplaceInArcTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List { - new AssetFile - { - Name = "somedir/somearc.arc", - Method = "bbsarc", - Source = new List { - new AssetFile - { - Name = "abcd", - Method = "copy", - Source = new List { - new AssetFile - { - Name = "somedir/somearc/abcd.bin" - } - } - } - } - } - } - }; - - CreateFile(AssetsInputDir, "somedir/somearc.arc").Using(x => - { - Arc.Write(new List - { - new Arc.Entry - { - Name = "abcd", - Data = new byte[] {0, 1, 2, 3} - } - }, x); - }); - - CreateFile(ModInputDir, "somedir/somearc/abcd.bin").Using(x => - { - x.Write(new byte[] { 4, 5, 6, 7 }); - }); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertArcFile("abcd", entry => - { - Assert.Equal(4, entry.Data.Length); - Assert.Equal(new byte[] { 4, 5, 6, 7 }, entry.Data); - }, ModOutputDir, patch.Assets[0].Name); - } - - [Fact] - public void ProcessMultipleTest() - { - var patcher = new PatcherProcessor(); - var patch = new Metadata - { - Assets = new List - { - new AssetFile - { - Name = "somedir/somefile.bar", - Method = "binarc", - Multi = new List - { - new Multi { Name = "somedir/another.bar" } - }, - Source = new List - { - new AssetFile - { - Name = "test", - Method = "imgd", - Type = "imgd", - Source = new List - { - new AssetFile - { - Name = "sample.png", - IsSwizzled = false - } - } - } - } - } - } - }; - - File.Copy("Imaging/res/png/32.png", Path.Combine(ModInputDir, "sample.png")); - - patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir); - - AssertFileExists(ModOutputDir, patch.Assets[0].Name); - AssertBarFile("test", entry => - { - Assert.True(Imgd.IsValid(entry.Stream)); - }, ModOutputDir, patch.Assets[0].Name); - - AssertFileExists(ModOutputDir, patch.Assets[0].Multi[0].Name); - AssertBarFile("test", entry => - { - Assert.True(Imgd.IsValid(entry.Stream)); - }, ModOutputDir, patch.Assets[0].Multi[0].Name); - } - - private static void AssertFileExists(params string[] paths) { var filePath = Path.Join(paths); From 5b06606f0e964805086f691294a7da56c52787a4 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 21:06:39 -0400 Subject: [PATCH 19/21] Update PatcherTests.cs --- OpenKh.Tests/Patcher/PatcherTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKh.Tests/Patcher/PatcherTests.cs b/OpenKh.Tests/Patcher/PatcherTests.cs index ffc7ff4fa..904a368bd 100644 --- a/OpenKh.Tests/Patcher/PatcherTests.cs +++ b/OpenKh.Tests/Patcher/PatcherTests.cs @@ -1,5 +1,5 @@ using Antlr4.Runtime; -using OpenKh.Bbs +using OpenKh.Bbs; using OpenKh.Command.Bdxio.Utils; using OpenKh.Common; using OpenKh.Imaging; From 2eb6c8e055014c6eaf1a40b927624312b14491c9 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 21:18:00 -0400 Subject: [PATCH 20/21] Update Arif.cs --- OpenKh.Kh2/SystemData/Arif.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/OpenKh.Kh2/SystemData/Arif.cs b/OpenKh.Kh2/SystemData/Arif.cs index 29e52264f..eb2561a87 100644 --- a/OpenKh.Kh2/SystemData/Arif.cs +++ b/OpenKh.Kh2/SystemData/Arif.cs @@ -1,4 +1,5 @@ using OpenKh.Common; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -17,7 +18,17 @@ public class BgmSet /// public class Arif { - [Data] public uint Flags { get; set; } + [Flags] + public enum ArifFlags: uint + { + IsKnownArea = 0x01, + IndoorArea = 0x02, + Monochrome = 0x04, + NoShadow = 0x08, + HasGlow = 0x10 + } + + [Data] public ArifFlags Flags { get; set; } //Originally a uint. Flags are now defined here. YMLs can use either flag names or values [Data] public int Reverb { get; set; } [Data] public int SoundEffectBank1 { get; set; } [Data] public int SoundEffectBank2 { get; set; } From 01a18f7239c8efd321fc1ed3c3f36c654716d283 Mon Sep 17 00:00:00 2001 From: Zurphing <72351816+Zurphing@users.noreply.github.com> Date: Wed, 31 Jul 2024 21:26:05 -0400 Subject: [PATCH 21/21] Update Kh2Field.cs to accomodate Arif --- OpenKh.Game/Field/Kh2Field.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenKh.Game/Field/Kh2Field.cs b/OpenKh.Game/Field/Kh2Field.cs index dcb10b250..601214eb6 100644 --- a/OpenKh.Game/Field/Kh2Field.cs +++ b/OpenKh.Game/Field/Kh2Field.cs @@ -133,11 +133,11 @@ public void LoadArea(int world, int area) if (area >= 0 && area < worldInfos.Count) { var areaInfo = worldInfos[area]; - var isKnown = (areaInfo.Flags & 1) != 0; - var isInDoor = (areaInfo.Flags & 2) != 0; - var isMonochrome = (areaInfo.Flags & 4) != 0; - var hasNoShadow = (areaInfo.Flags & 8) != 0; - var hasGlow = (areaInfo.Flags & 16) != 0; + var isKnown = areaInfo.Flags.HasFlag(Kh2.SystemData.Arif.ArifFlags.IsKnownArea); + var isInDoor = areaInfo.Flags.HasFlag(Kh2.SystemData.Arif.ArifFlags.IndoorArea); + var isMonochrome = areaInfo.Flags.HasFlag(Kh2.SystemData.Arif.ArifFlags.Monochrome); + var hasNoShadow = areaInfo.Flags.HasFlag(Kh2.SystemData.Arif.ArifFlags.NoShadow); + var hasGlow = areaInfo.Flags.HasFlag(Kh2.SystemData.Arif.ArifFlags.HasGlow); _targetCamera.Type = isInDoor ? 1 : 0; }