Beispiel #1
0
        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);
                }
            }
        }
Beispiel #2
0
        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();
        }