public void WriteDecrypted(Stream stream) { stream.Position = 0; uint changeLen = 0, creditLen = 0; changeLen = Changelogs.Aggregate(changeLen, (current, b) => current + (4 + (uint) b.Length)); creditLen = Credits.Aggregate(creditLen, (current, b) => current + (4 + (uint) b.Length)); using (var bw = new BinaryStream(stream, leaveOpen: true)) { uint i; bw.Write(Signature); bw.Write((uint) (16 + _Author.Length)); bw.Write((uint) (16 + _Author.Length + 16 + changeLen + 4 + creditLen + _OtherInfo.Length)); bw.Write(Version); bw.Write(_Author); bw.Write((uint) 12); bw.Write(16 + changeLen); bw.Write(16 + changeLen + 4 + creditLen); bw.Write(i = (uint) Changelogs.Count); i *= 4; foreach (var b in Changelogs) { bw.Write(i); i += (uint) b.Length; } foreach (var b in Changelogs) { bw.Write(b); } bw.Write(i = (uint) Credits.Count); i *= 4; foreach (var b in Credits) { bw.Write(i); i += (uint) b.Length; } foreach (var b in Credits) { bw.Write(b); } bw.Write(_OtherInfo); bw.Write((uint) Files.Count); //Check total size to add long fileTotal = 0; try { fileTotal = Files.Where(file => file.Relink == 0) .Aggregate(fileTotal, (current, file) => checked(current + file.Data.Length)); } catch (OverflowException) { ISOTP.WriteError( "That's WAY too much file data... is there even that much in the gameo.O?\r\nTry to split up the patch..."); return; } Stream filedata = null; string filename = null; //Use a MemoryStream if we can, much cleaner\faster if (fileTotal <= int.MaxValue) { try { filedata = new MemoryStream((int) fileTotal); } catch (OutOfMemoryException) { filedata = null; ISOTP.WriteWarning("Failed to allocate enough memory, trying temporary file fallback..."); } } //If we can't use a MemStream (or that failed), try a FileStream as a temp file if (filedata == null) { filename = Path.GetTempFileName(); Console.WriteLine("Wow there's a lot of file data! Using a temporary file now!\r\nUsing {0}", filename); filedata = File.Open(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.None); } using (filedata) { i = (uint) (stream.Position + Files.Count*92); foreach (FileEntry file in Files) { bw.Write(file.Hash); if (file.Relink != 0) { bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write(file.ParentHash); bw.Write(file.Relink); bw.Write((uint) 0); } else { uint cSize; file.Data.Position = 0; if (file.IsCompressed) { try { var input = new byte[file.Data.Length]; file.Data.Read(input, 0, (int) file.Data.Length); Console.Write("Compressing {0}: ", file.name ?? file.Hash.ToString("X8")); byte[] output = KH1Compressor.compress(input); uint cSizeSectors = (uint) Math.Ceiling((double) output.Length/2048) - 1; if (output.LongLength > int.MaxValue) { throw new NotSupportedException( "Compressed data too big to store (Program limitation)"); } if (cSizeSectors > 0x2FFF) { throw new NotSupportedException( "Compressed data too big to store (IDX limitation)"); } if ((cSizeSectors & 0x1000u) == 0x1000u) { throw new NotSupportedException( "Compressed data size hit 0x1000 bit limitation (IDX limitation)"); } cSize = (uint) output.Length; filedata.Write(output, 0, output.Length); } catch (NotCompressableException e) { string es = "ERROR: Failed to compress file: " + e.Message; ISOTP.WriteWarning(es); Console.Write("Add it without compressing? [Y/n] "); if (Program.GetYesNoInput()) { file.IsCompressed = false; cSize = (uint) file.Data.Length; file.Data.Position = 0; //Ensure at beginning file.Data.CopyTo(filedata); } else { throw new NotCompressableException(es, e); } } } else { Console.WriteLine("Adding {0}", file.name ?? file.Hash.ToString("X8")); cSize = (uint) file.Data.Length; file.Data.Position = 0; //Ensure at beginning file.Data.CopyTo(filedata); } if (!file.IsCompressed && (((uint) Math.Ceiling((double) cSize/2048) - 1) & 0x1000u) == 0x1000u) { ISOTP.WriteWarning( "Data size hit 0x1000 bit limitation, but this file may be OK if it's streamed."); } bw.Write(i); i += cSize; bw.Write(cSize); bw.Write((uint) file.Data.Length); bw.Write(file.ParentHash); bw.Write((uint) 0); bw.Write((uint) (file.IsCompressed ? 1 : 0)); } bw.Write((uint) (file.IsNewFile ? 1 : 0)); //Custom //Padding bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); bw.Write((uint) 0); } filedata.Position = 0; //Ensure at beginning filedata.CopyTo(stream); } //If we used a temp file, delete it if (filename != null) { File.Delete(filename); } } }
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(); }