static void Main(string[] args) { Console.Write("PAPA_Tool\nProgrammed by GovanifY for ChrisX930\n\n1) Extract 2) Create\n"); string choice = Console.ReadLine(); if (choice == "1") { Console.WriteLine("\n\nPlease enter the name of the file to extract: "); string arg = Console.ReadLine(); if (File.Exists(arg)) { BinaryStream input = new BinaryStream(File.Open(arg, FileMode.Open)); UInt32 magic = input.ReadUInt32(); if (magic != 0x41504150) { Console.WriteLine("INCORRECT MAGIC!\nExiting..."); return; } input.ReadUInt32();//Padding UInt32 Headeroffset = input.ReadUInt32(); UInt32 Headersize = input.ReadUInt32(); UInt32 count = input.ReadUInt32(); count -= 1; string dirname = "@" + arg + "/"; #region Dir creation try { Directory.CreateDirectory(dirname); } catch (IOException e) { Console.Write("Failed creating directory: {0}", e.Message); } #endregion for (int i = 0; i < count; i++) { UInt32 FileOffset = input.ReadUInt32(); long tmp = input.Tell(); UInt32 NextFileOffset = input.ReadUInt32(); UInt32 FileSize = NextFileOffset; FileSize -= FileOffset + 5*4; input.Seek(FileOffset, SeekOrigin.Begin); Console.WriteLine("Extracting...: {0}", "@" + arg + "/" + i + ".bin"); UInt32 completesize = input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32();//Constants: 3 then 14 then 18 UInt32 secondsize = input.ReadUInt32(); byte[] PAPAtmp = input.ReadBytes((int)FileSize); var PAPAfs = new FileStream("@" + arg + "/" + i + ".bin", FileMode.Create, FileAccess.ReadWrite, FileShare.None); PAPAfs.Write(PAPAtmp, 0, PAPAtmp.Length); input.Seek(tmp, SeekOrigin.Begin); } } else { Console.WriteLine("Cannot open file!"); } } else { if (choice == "2") { long disposer; Console.WriteLine("\n\nPlease enter the name of the file to create: "); string arg = Console.ReadLine(); BinaryWriter output = new BinaryWriter(File.Open(Path.GetFileNameWithoutExtension(arg) + "Modded" + Path.GetExtension(arg), FileMode.Create)); string dirname = "@" + arg + "/"; output.Write((UInt32)0x41504150);//Magic! output.Write((UInt32)0);//Padding output.Write((UInt32)0xC); output.Write((UInt32)0xADC);//Header offset and size, but always constants! string[] files = Directory.GetFiles(dirname);//The files are sorted numerically by default, all hail to .NET o/(actually not) NumericComparer ns = new NumericComparer(); Array.Sort(files, ns); output.Write((UInt32)files.Length + 1);//Number of files for (int i = 0; i < files.Length; i++ ) { output.Write((UInt32)0);//FileOffset //Garbages since we're going to mod this later } disposer = output.BaseStream.Position; int y = 0; foreach (string name in files) { byte[] file = File.ReadAllBytes(name); output.Write((UInt32)file.Length + 5 * 4);//Complete Size output.Write((UInt32)0x3);//Constants output.Write((UInt32)0x14);//Constants output.Write((UInt32)0x18);//Constants output.Write((UInt32)file.Length + 4);//Second Size output.Write(file); //Then write file datas here long tmp = output.BaseStream.Position; output.Seek(20 + y * 4, SeekOrigin.Begin); output.Write((UInt32)disposer); output.Seek((int)tmp, SeekOrigin.Begin); disposer = output.BaseStream.Position; y++; } } else { Console.WriteLine("Please enter a correct option!"); } } }
static void Main(string[] args) { Console.Write("XBB_Tool\nProgrammed by GovanifY for ChrisX930\n\n1) Extract 2) Create\n"); string choice = Console.ReadLine(); if (choice == "1") { Console.WriteLine("\n\nPlease enter the name of the file to extract: "); string arg = Console.ReadLine(); if (File.Exists(arg)) { BinaryStream input = new BinaryStream(File.Open(arg, FileMode.Open)); UInt32 magic = input.ReadUInt32(); if (magic != 0x01424258) { Console.WriteLine("INCORRECT MAGIC!\nExiting..."); return; } UInt32 count = input.ReadUInt32(); input.Seek(0x20, SeekOrigin.Begin);//Padding...? string dirname = "@" + arg + "/"; #region Dir creation try { Directory.CreateDirectory(dirname); } catch (IOException e) { Console.Write("Failed creating directory: {0}", e.Message); } #endregion for (int i = 0; i < count; i++) { UInt32 offset = input.ReadUInt32(); UInt32 size = input.ReadUInt32(); UInt32 nameoffset = input.ReadUInt32(); UInt32 ID = input.ReadUInt32(); long tmp = input.Tell(); input.Seek(nameoffset, SeekOrigin.Begin); byte[] namet = new byte[0]; //Reads name until 0 while(true) { byte test = input.ReadByte(); if (test == 0){goto next;} byte[] tmpnamet = new byte[namet.Length + 1]; namet.CopyTo(tmpnamet, 1); tmpnamet[0] = test; namet = tmpnamet; } next: Array.Reverse(namet, 0, namet.Length); string name = dirname + Encoding.ASCII.GetString(namet); Console.WriteLine("Extracting...: {0}", name); input.Seek(offset, SeekOrigin.Begin); byte[] PAPAtmp = input.ReadBytes((int)size); var PAPAfs = new FileStream(name, FileMode.Create, FileAccess.ReadWrite, FileShare.None); PAPAfs.Write(PAPAtmp, 0, PAPAtmp.Length); input.Seek(tmp, SeekOrigin.Begin); } } else { Console.WriteLine("Cannot open file!"); } } else { if (choice == "2") { Console.WriteLine("\n\nPlease enter the name of the file to create: "); string arg = Console.ReadLine(); BinaryWriter output = new BinaryWriter(File.Open(Path.GetFileNameWithoutExtension(arg) + "Modded" + Path.GetExtension(arg), FileMode.Create)); string dirname = "@" + arg + "/"; UInt32 IDCustom = 0x90000000; output.Write((uint)0x01424258); string[] files = Directory.GetFiles(dirname); output.Write((uint)files.Length); output.Write((uint)0);//Padding output.Write((uint)0);//Padding output.Write((uint)0);//Padding output.Write((uint)0);//Padding output.Write((uint)0);//Padding output.Write((uint)0);//Padding BinaryStream input = new BinaryStream(File.Open(arg, FileMode.Open)); UInt32 magic = input.ReadUInt32(); if (magic != 0x01424258) { Console.WriteLine("INCORRECT MAGIC!\nExiting..."); return; } UInt32 count = input.ReadUInt32(); input.Seek(0x20, SeekOrigin.Begin);//Padding...? int disposername = (files.Length * 4 * 4) + 0x20 + 8 + (files.Length * 4 * 2); int disposer2LBA = (files.Length * 4 * 4) + 0x20 + 8; /*long tmp = output.BaseStream.Position; output.Seek(disposername - 8, SeekOrigin.Begin); output.Write((UInt32)0);//Garbage, dunno what those 2 bytes are output.Write((UInt32)0); output.Seek((int)tmp, SeekOrigin.Begin);*/ int disposer = (files.Length * 4 * 4) + 0x20 + 8 + 2 + (files.Length * 4 * 2); foreach (string name in files) { disposer += Path.GetFileName(name).Length + 1; } foreach (string name in files) { UInt32 ID; UInt32 unk1; UInt32 unk2; try { input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); ID = input.ReadUInt32(); long tmp = input.Tell(); input.Seek(disposer2LBA, SeekOrigin.Begin); unk1 = input.ReadUInt32(); unk2 = input.ReadUInt32(); input.Seek(tmp, SeekOrigin.Begin); } catch { unk1 = 0; unk2 = 0; ID = IDCustom;//custom ID } byte[] file = File.ReadAllBytes(name); output.Write((UInt32)disposer); output.Write((UInt32)file.Length);//Size output.Write((UInt32)disposername); output.Write((UInt32)ID); Console.WriteLine("Adding: {0}, using ID {1}",name, ID); IDCustom++; long tmp2 = output.BaseStream.Position; output.Seek(disposer2LBA, SeekOrigin.Begin); output.Write(unk1); output.Write(unk2);//Unknowns to figure out!!! disposer2LBA += 8; output.Seek(disposername, SeekOrigin.Begin); disposername += Path.GetFileName(name).Length + 1; byte[] tmp3 = System.Text.Encoding.ASCII.GetBytes(Path.GetFileName(name)); byte[] tmp4 = new byte[] { 0x00 }; output.Write(tmp3); output.Write(tmp4); output.Seek(disposer, SeekOrigin.Begin); output.Write(file); disposer += file.Length; output.Seek((int)tmp2, SeekOrigin.Begin); } } else { Console.WriteLine("Please enter a correct option!"); } } }
private static void KH2PatchExtractor(Stream patch, string outputname) { try { Directory.CreateDirectory(Path.GetDirectoryName("output/")); } catch { } //Creating folder using (var br = new BinaryStream(patch, Encoding.ASCII, leaveOpen: true)) { using (TextWriter op = new StreamWriter("output/log.log")) { uint tmp = br.ReadUInt32(); if (tmp != 1345472587u && tmp != 1362249803u) { br.Close(); br.Close(); throw new InvalidDataException("Invalid KH2Patch file!"); } uint oaAuther = br.ReadUInt32(), obFileCount = br.ReadUInt32(), num = br.ReadUInt32(); string patchname = ""; patchname = Path.GetFileName(patchname); try { string author = br.ReadCString(); op.WriteLine(author); op.WriteLine(num); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Loading patch {0} version {1} by {2}", patchname, num, author); Console.ResetColor(); br.Seek(oaAuther, SeekOrigin.Begin); uint os1 = br.ReadUInt32(), os2 = br.ReadUInt32(), os3 = br.ReadUInt32(); br.Seek(oaAuther + os1, SeekOrigin.Begin); num = br.ReadUInt32(); if (num > 0) { br.Seek(num * 4, SeekOrigin.Current); //Console.WriteLine("Changelog:"); Console.ForegroundColor = ConsoleColor.Green; while (num > 0) { --num; op.WriteLine(br.ReadCString()); //Console.WriteLine(" * {0}", br.ReadCString()); } op.WriteLine(""); } br.Seek(oaAuther + os2, SeekOrigin.Begin); num = br.ReadUInt32(); if (num > 0) { br.Seek(num * 4, SeekOrigin.Current); Console.ResetColor(); //Console.WriteLine("Credits:"); Console.ForegroundColor = ConsoleColor.Green; while (num > 0) { --num; op.WriteLine(br.ReadCString()); //Console.WriteLine(" * {0}", br.ReadCString()); } op.WriteLine(""); Console.ResetColor(); } br.Seek(oaAuther + os3, SeekOrigin.Begin); author = br.ReadCString(); /*author = author.Replace("\r\n", string.Empty); author = author.Replace("\n", string.Empty);//Shitty but I know someone who made mods for adding more than one line...*/ if (author.Length != 0) { // Console.WriteLine("Other information:\r\n"); Console.ForegroundColor = ConsoleColor.Green; op.WriteLine(author); //Console.WriteLine("{0}", author); } op.WriteLine(""); Console.ResetColor(); } catch (Exception e) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error reading kh2patch header: {0}: {1}\r\nAttempting to continue files...", e.GetType(), e.Message); Console.ResetColor(); } Console.WriteLine(""); br.Seek(obFileCount, SeekOrigin.Begin); num = br.ReadUInt32(); while (num > 0) { --num; uint Hash = br.ReadUInt32(); oaAuther = br.ReadUInt32(); uint CompressedSize = br.ReadUInt32(); uint UncompressedSize = br.ReadUInt32(); uint Parent = br.ReadUInt32(); uint Relink = br.ReadUInt32(); bool Compressed = br.ReadUInt32() != 0; bool IsNew = br.ReadUInt32() == 1; //Custom string fname3 = ""; string fname2; if (Relink == 0) { if (CompressedSize != 0) { var KH2PFileStream = new Substream(patch, oaAuther, CompressedSize); if (HashList.HashList.pairs.TryGetValue(Hash, out fname2)) { Console.Write("Extracting {0}...", fname2); } else { fname2 = String.Format("@noname/{0:X8}.bin", Hash); ; Console.Write("Extracting {0}...", fname2); } long brpos = br.Tell(); KH2PATCHInternal(KH2PFileStream, fname2, Compressed, UncompressedSize); br.ChangePosition((int)brpos); //Changing the original position of the BinaryReader for what's next } else { throw new InvalidDataException("File length is 0, but not relinking."); } op.WriteLine(fname2); op.WriteLine(fname3); string Compressed2 = ""; if (Compressed) { Compressed2 = "y"; } else { Compressed2 = "n"; } op.WriteLine(Compressed2); string Parent2 = ""; if (Parent == 0) { } if (Parent == 1) { Parent2 = "OVL"; } if (Parent == 2) { Parent2 = "ISO"; } op.WriteLine(Parent2); string IsNew2 = ""; if (IsNew) { IsNew2 = "y"; } else { IsNew2 = "n"; } op.WriteLine(IsNew2); } else { if (!HashList.HashList.pairs.TryGetValue(Hash, out fname2)) { fname2 = String.Format("@noname/{0:X8}.bin", Hash); } if (!HashList.HashList.pairs.TryGetValue(Relink, out fname3)) { fname3 = String.Format("@noname/{0:X8}.bin", Relink); } Console.WriteLine("File {1} relinked to {0}, no need to extract", fname3, fname2); op.WriteLine(fname2); op.WriteLine(fname3); string Parent2 = ""; if (Parent == 0) { } if (Parent == 1) { Parent2 = "OVL"; } if (Parent == 2) { Parent2 = "ISO"; } op.WriteLine(Parent2); string IsNew2 = ""; if (IsNew) { IsNew2 = "y"; } else { IsNew2 = "n"; } op.WriteLine(IsNew2); } br.Seek(60, SeekOrigin.Current); } op.WriteLine(""); using (TextWriter bat = new StreamWriter("output/output.bat")) { bat.WriteLine("@echo off"); bat.WriteLine("KH2FM_Toolkit.exe -patchmaker -batch -uselog log.log -output \"{0}\"", outputname); } File.Copy(System.Reflection.Assembly.GetEntryAssembly().Location, "output/KH2FM_Toolkit.exe"); } } //End of br }
private static void CreateFireEmblemArchive(string outdir, string newname) { Console.WriteLine("Creating archive {0}", Path.GetFileName(newname)); FileStream newfilestream = File.Create(newname); //Let's get the number of files string[] files = Directory.GetFiles(outdir); uint FileCount = (uint)files.Length; Console.WriteLine("{0} files detected!", FileCount); var ShiftJIS = Encoding.GetEncoding(932); BinaryStream newFile = new BinaryStream(newfilestream); MemoryStream infos = new MemoryStream(); BinaryWriter FileInfos = new BinaryWriter(infos); Console.WriteLine("Creating dummy header..."); newFile.Write(0);//Dummy; file size //MetaOffset 0x4 newFile.Write(0);//dummy should be MetaOffset newFile.Write(FileCount); newFile.Write(FileCount + 3); byte nil = 0; for (int i = 0; i < 0x70; i++) { newFile.Write(nil); } int z = 0; foreach (string fileName in files) { Console.WriteLine("Adding file {0}...", Path.GetFileName(fileName)); byte[] filetoadd = File.ReadAllBytes(fileName); uint fileoff = (uint)newFile.Tell(); newFile.Write(filetoadd); while ((int)newFile.Tell() % 128 != 0) { newFile.Write(nil); } FileInfos.Write(0);//Name position FileInfos.Write(z);//FileIndex FileInfos.Write(filetoadd.Length);//Length of the file FileInfos.Write(fileoff - 0x80);//Data Offset - 0x80 z++; } long countinfo = newFile.Tell(); newFile.Write(files.Length);//Count is written there afaik long infopointer = newFile.Tell(); Console.WriteLine("Adding dummy FileInfos..."); infos.Seek(0, SeekOrigin.Begin); var infopos = newFile.Tell(); newFile.Write(infos.ToArray()); Console.WriteLine("Rewriting header..."); long metapos = newFile.Tell(); newFile.Seek(4, SeekOrigin.Begin); newFile.Write((uint)metapos - 0x20); newFile.Seek(metapos, SeekOrigin.Begin); Console.WriteLine("Adding FileInfos pointer..."); for (int i = 0; i < files.Length; i++) { newFile.Write((uint)((infopointer + i * 16) - 0x20)); } Console.WriteLine("Adding Advanced pointers..."); newFile.Write((uint)0x60); newFile.Write(0); newFile.Write((uint)(countinfo - 0x20)); newFile.Write((uint)5); newFile.Write((uint)(countinfo + 4 - 0x20)); newFile.Write((uint)0xB); for(int i = 0; i < files.Length; i++) { newFile.Write((uint)((countinfo + 4) + i * 16 ) - 0x20); //Second pointer is a bit more complicated if (i == 0) { newFile.Write((uint)0x10); } else { if (i == 1) { newFile.Write((uint)0x1C); } else { newFile.Write((uint)(0x1C + (10 * (i - 1))));//Currently this pointer is unknown, so we assume blindly that a basic pattern is correct } } } //This need to be reversed! //0, 5, 0B, 10, 1C, 26, 30, 3A, 44, 4E //+5, +6, +4, +12, +10, +10, +10, +10, +10 Console.WriteLine("Adding Filenames..."); var datcount = new byte[] { 0x44, 0x61, 0x74, 0x61, 0x00, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x49, 0x6E, 0x66, 0x6F, 0x00 }; newFile.Write(datcount); int y = 0; foreach (string fileName in files) { FileInfos.Seek(y * 16, SeekOrigin.Begin); long namepos = newFile.Tell(); FileInfos.Write((uint)namepos - 0x20); newFile.Write(ShiftJIS.GetBytes(Path.GetFileName(fileName))); newFile.Write(nil); y++; } Console.WriteLine("Rewriting FileInfos..."); newFile.Seek(infopos, SeekOrigin.Begin); infos.Seek(0, SeekOrigin.Begin); newFile.Write(infos.ToArray()); Console.WriteLine("Finishing the job..."); newFile.Seek(0, SeekOrigin.Begin); UInt32 newlength = (UInt32)newFile.BaseStream.Length; newFile.Write(newlength); Console.WriteLine( "Done!"); newFile.Close(); }
private void AddPatch(Stream ms, string patchname = "") { using (var br = new BinaryStream(ms, Encoding.ASCII, leaveOpen: true)) { if (br.ReadUInt32() != 0x5032484b) { br.Seek(0, SeekOrigin.Begin); if (br.ReadUInt32() != 0x5132484b) { br.Seek(0, SeekOrigin.Begin); if (br.ReadUInt32() != 0x4632484b) { br.Close(); ms.Close(); throw new InvalidDataException("Invalid KH2Patch file!"); } else { fast_patch=true; Console.WriteLine("Fast patch and dev flags detected! You might get some issues with those!"); } } } patchms.Add(ms); uint oaAuther = br.ReadUInt32(), obFileCount = br.ReadUInt32(), num = br.ReadUInt32(); patchname = Path.GetFileName(patchname); try { string author = br.ReadCString(); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Loading patch {0} version {1} by {2}", patchname, num, author); Console.ResetColor(); br.Seek(oaAuther, SeekOrigin.Begin); uint os1 = br.ReadUInt32(), os2 = br.ReadUInt32(), os3 = br.ReadUInt32(); br.Seek(oaAuther + os1, SeekOrigin.Begin); num = br.ReadUInt32(); if (num > 0) { br.Seek(num*4, SeekOrigin.Current); Console.WriteLine("Changelog:"); Console.ForegroundColor = ConsoleColor.Green; while (num > 0) { --num; Console.WriteLine(" * {0}", br.ReadCString()); } } br.Seek(oaAuther + os2, SeekOrigin.Begin); num = br.ReadUInt32(); if (num > 0) { br.Seek(num*4, SeekOrigin.Current); Console.ResetColor(); Console.WriteLine("Credits:"); Console.ForegroundColor = ConsoleColor.Green; while (num > 0) { --num; Console.WriteLine(" * {0}", br.ReadCString()); } Console.ResetColor(); } br.Seek(oaAuther + os3, SeekOrigin.Begin); author = br.ReadCString(); if (author.Length != 0) { Console.WriteLine("Other information:\r\n"); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("{0}", author); } Console.ResetColor(); } catch (Exception e) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error reading kh2patch header: {0}: {1}\r\nAttempting to continue files...", e.GetType(), e.Message); Console.ResetColor(); } Console.WriteLine(""); br.Seek(obFileCount, SeekOrigin.Begin); num = br.ReadUInt32(); while (num > 0) { --num; var nPatch = new Patch(); nPatch.Hash = br.ReadUInt32(); oaAuther = br.ReadUInt32(); nPatch.CompressedSize = br.ReadUInt32(); nPatch.UncompressedSize = br.ReadUInt32(); nPatch.Parent = br.ReadUInt32(); nPatch.Relink = br.ReadUInt32(); nPatch.Compressed = br.ReadUInt32() != 0; nPatch.IsNew = br.ReadUInt32() == 1; //Custom if (!nPatch.IsRelink) { if (nPatch.CompressedSize != 0) { nPatch.Stream = new Substream(ms, oaAuther, nPatch.CompressedSize); } else { throw new InvalidDataException("File length is 0, but not relinking."); } } // Use the last file patch if (patches.ContainsKey(nPatch.Hash)) { Console.ForegroundColor = ConsoleColor.Red; #if DEBUG Console.WriteLine("The file {0} has been included multiple times. Using the one from {1}.", HashList.HashList.NameFromHash(nPatch.Hash), patchname); #endif patches[nPatch.Hash].Dispose(); patches.Remove(nPatch.Hash); Console.ResetColor(); } patches.Add(nPatch.Hash, nPatch); //Global checks if (!KH2Changed && nPatch.IsInKH2 || nPatch.IsInKH2Sub) { KH2Changed = true; } else if (!OVLChanged && nPatch.IsInOVL) { OVLChanged = true; } else if (!ISOChanged && nPatch.IsinISO) { ISOChanged = true; } if (nPatch.IsNew) { AddToNewFiles(nPatch); } br.Seek(60, SeekOrigin.Current); } } }