Пример #1
0
        static bool PatchFile(string filename, string newFilename)
        {
            Console.WriteLine("Reading and copying " + filename + "...");
            using (var nds = new System.IO.FileStream(newFilename, System.IO.FileMode.Create)) {
                using (var ndsSrc = new System.IO.FileStream(filename, System.IO.FileMode.Open)) {
                    Util.CopyStream(ndsSrc, nds, (int)ndsSrc.Length);
                    ndsSrc.Close();
                }

                // http://dsibrew.org/wiki/DSi_Cartridge_Header

                // arm
                Console.WriteLine("Patching ARM Executables...");
                nds.Position = 0x20;
                uint arm9offset = nds.ReadUInt32();
                uint arm9entry  = nds.ReadUInt32();
                uint arm9load   = nds.ReadUInt32();
                uint arm9size   = nds.ReadUInt32();
                uint arm7offset = nds.ReadUInt32();
                uint arm7entry  = nds.ReadUInt32();
                uint arm7load   = nds.ReadUInt32();
                uint arm7size   = nds.ReadUInt32();

                bool modArm9 = PatchArm9(nds, arm9offset, arm9size);
                bool modArm7 = PatchArm7(nds, arm7offset, arm7size);

                // overlays
                Console.WriteLine("Patching Overlays...");
                nds.Position = 0x50;
                uint arm9overlayoff = nds.ReadUInt32();
                uint arm9overlaylen = nds.ReadUInt32();
                uint arm7overlayoff = nds.ReadUInt32();
                uint arm7overlaylen = nds.ReadUInt32();

                bool modOvl9 = PatchOverlay(nds, arm9overlayoff, arm9overlaylen);
                bool modOvl7 = PatchOverlay(nds, arm7overlayoff, arm7overlaylen);

                nds.Close();

                return(modArm9 || modArm7 || modOvl9 || modOvl7);
            }
        }
Пример #2
0
        static bool PatchOverlay(System.IO.FileStream nds, uint pos, uint len)
        {
            // http://sourceforge.net/p/devkitpro/ndstool/ci/master/tree/source/ndsextract.cpp
            // http://sourceforge.net/p/devkitpro/ndstool/ci/master/tree/source/overlay.h
            // header compression info from http://gbatemp.net/threads/recompressing-an-overlay-file.329576/

            nds.Position = 0x048;
            uint fatOffset = nds.ReadUInt32();

            bool modified = false;

            for (uint i = 0; i < len; i += 0x20)
            {
                nds.Position = pos + i;
                uint id                = nds.ReadUInt32();
                uint ramAddr           = nds.ReadUInt32();
                uint ramSize           = nds.ReadUInt32();
                uint bssSize           = nds.ReadUInt32();
                uint sinitInit         = nds.ReadUInt32();
                uint sinitInitEnd      = nds.ReadUInt32();
                uint fileId            = nds.ReadUInt32();
                uint compressedSize    = nds.ReadUInt24();
                byte compressedBitmask = (byte)nds.ReadByte();

                nds.Position = fatOffset + 8 * id;
                uint overlayPositionStart = nds.ReadUInt32();
                uint overlayPositionEnd   = nds.ReadUInt32();
                uint overlaySize          = overlayPositionEnd - overlayPositionStart;

                if (overlaySize == 0)
                {
                    continue;
                }

                nds.Position = overlayPositionStart;
                byte[] data = new byte[overlaySize];
                nds.Read(data, 0, (int)overlaySize);

                blz    blz = new blz();
                byte[] decData;

                bool compressed = (compressedBitmask & 0x01) == 0x01;
                if (compressed)
                {
                    try {
                        decData = blz.BLZ_Decode(data);
                    } catch (blzDecodingException) {
                        Console.WriteLine("WARNING: Decompression of Overlay " + (i / 0x20) + " failed!");
                        decData    = data;
                        compressed = false;
                    }
                }
                else
                {
                    decData = data;
                }

#if DEBUG
                System.IO.File.WriteAllBytes("overlay" + (i / 0x20) + "-dec.bin", decData);
#endif

                if (ReplaceInData(decData))
                {
                    modified = true;
                    int newOverlaySize;
                    int diff;

                    // if something was replaced, put it back into the ROM
                    if (compressed)
                    {
                        Console.WriteLine("Replacing and recompressing overlay " + id + "...");

                        uint newCompressedSize = 0;
                        data = blz.BLZ_Encode(decData, 0);
                        newCompressedSize = (uint)data.Length;

                        newOverlaySize = data.Length;
                        diff           = (int)overlaySize - newOverlaySize;

                        if (diff < 0)
                        {
                            Console.WriteLine("Removing known debug strings and recompressing overlay " + id + "...");
                            RemoveDebugStrings(decData);
                            data = blz.BLZ_Encode(decData, 0, supressWarnings: true);
                            newCompressedSize = (uint)data.Length;

                            newOverlaySize = data.Length;
                            diff           = (int)overlaySize - newOverlaySize;
                            if (diff < 0)
                            {
                                Console.WriteLine("WARNING: Recompressed overlay is " + -diff + " bytes bigger than original!");
                                Console.WriteLine("         Patched game may be corrupted!");
                            }
                        }

                        // replace compressed size, if it was used before
                        if (compressedSize == overlaySize)
                        {
                            byte[] newCompressedSizeBytes = BitConverter.GetBytes(newCompressedSize);
                            nds.Position = pos + i + 0x1C;
                            nds.Write(newCompressedSizeBytes, 0, 3);
                        }
                    }
                    else
                    {
                        Console.WriteLine("Replacing overlay " + id + "...");

                        data = decData;
                    }

                    newOverlaySize = data.Length;
                    diff           = (int)overlaySize - newOverlaySize;

                    nds.Position = overlayPositionStart;
                    nds.Write(data, 0, data.Length);

                    overlayPositionEnd = (uint)nds.Position;

                    // padding
                    for (int j = 0; j < diff; ++j)
                    {
                        nds.WriteByte(0xFF);
                    }

                    // new file end offset
                    byte[] newPosEndData = BitConverter.GetBytes(overlayPositionEnd);
                    nds.Position = fatOffset + 8 * id + 4;
                    nds.Write(newPosEndData, 0, 4);
                }
            }

            return(modified);
        }
Пример #3
0
        private bool Load(System.IO.FileStream stream)
        {
            CreationTime        = stream.ReadUInt64();
            FileHashArrayOffset = stream.ReadUInt32();

            // This should always be 0x20, so we can use it to detect endianness
            if (FileHashArrayOffset <= 0xFFFF)
            {
                Endian = EndianUtils.Endianness.LittleEndian;
            }
            else
            {
                Endian              = EndianUtils.Endianness.BigEndian;
                CreationTime        = CreationTime.SwapEndian();
                FileHashArrayOffset = FileHashArrayOffset.SwapEndian();
            }

            // We adjust the offsets to be from file start rather than from position, makes them easier to seek to.
            FileHashArrayOffset    += (uint)stream.Position - 4;
            FileHashArraySize       = stream.ReadUInt32().FromEndian(Endian);
            VirtualHashArrayOffset  = (uint)stream.Position;
            VirtualHashArrayOffset += stream.ReadUInt32().FromEndian(Endian);
            VirtualHashArraySize    = stream.ReadUInt32().FromEndian(Endian);
            FileArrayOffset         = (uint)stream.Position;
            FileArrayOffset        += stream.ReadUInt32().FromEndian(Endian);
            FileArraySize           = stream.ReadUInt32().FromEndian(Endian);
            VirtualPackArrayOffset  = (uint)stream.Position;
            VirtualPackArrayOffset += stream.ReadUInt32().FromEndian(Endian);
            VirtualPackArraySize    = stream.ReadUInt32().FromEndian(Endian);

            // Hashes
            stream.Position = FileHashArrayOffset;
            FileHashArray   = new List <TOFHDBhash>((int)FileHashArraySize);
            for (UInt32 i = 0; i < FileHashArraySize; i++)
            {
                FileHashArray.Add(new TOFHDBhash()
                {
                    Key   = stream.ReadUInt32().FromEndian(Endian),
                    Value = stream.ReadUInt32().FromEndian(Endian),
                });
            }

            // Files
            stream.Position = FileArrayOffset;
            FileArray       = new List <TOFHDBfile>((int)FileArraySize);
            for (UInt32 i = 0; i < FileArraySize; i++)
            {
                FileArray.Add(new TOFHDBfile()
                {
                    Filesize       = stream.ReadUInt64().FromEndian(Endian),
                    CompressedSize = stream.ReadUInt64().FromEndian(Endian),
                    Offset         = stream.ReadUInt64().FromEndian(Endian),
                    Hash           = stream.ReadUInt32().FromEndian(Endian),
                    Extension      = stream.ReadAscii(0x0A).TrimNull(),
                    Unknown        = stream.ReadUInt16().FromEndian(Endian),
                });
            }

            return(true);
        }
Пример #4
0
        static bool PatchArm9(System.IO.FileStream nds, uint pos, uint len)
        {
            nds.Position = pos;
            byte[] data = new byte[len];
            nds.Read(data, 0, (int)len);

            // decompress size info: http://www.crackerscrap.com/docs/dsromstructure.html
            // TODO: Is there a better way to figure out if an ARM9 is compressed?

            nds.Position = nds.Position - 8;
            uint compressedSize           = nds.ReadUInt24();
            byte headerLength             = (byte)nds.ReadByte();
            uint additionalCompressedSize = nds.ReadUInt32();
            uint decompressedSize         = additionalCompressedSize + len;

            bool compressed = false;

            byte[] decData = data;

#if DEBUG
            Console.WriteLine("ARM9 old dec size: 0x" + decompressedSize.ToString("X6"));
            Console.WriteLine("ARM9 old cmp size: 0x" + compressedSize.ToString("X6"));
            Console.WriteLine("ARM9 old filesize: 0x" + len.ToString("X6"));
            Console.WriteLine("ARM9 old diff:     0x" + additionalCompressedSize.ToString("X6"));

            System.IO.File.WriteAllBytes("arm9-raw.bin", data);
#endif

            blz blz = new blz();
            // if one of these isn't true then it can't be blz-compressed so don't even try
            bool headerLengthValid   = (headerLength >= 8 && headerLength <= 11);
            bool compressedSizeValid = (data.Length >= compressedSize + 0x4000 && data.Length <= compressedSize + 0x400B);
            if (headerLengthValid && compressedSizeValid)
            {
                try {
                    blz.arm9 = 1;
                    byte[] maybeDecData = blz.BLZ_Decode(data);

                    if (maybeDecData.Length == decompressedSize)
                    {
                        compressed = true;
                        decData    = maybeDecData;
#if DEBUG
                        System.IO.File.WriteAllBytes("arm9-dec.bin", decData);
#endif
                    }
                } catch (blzDecodingException) {
                    compressed = false;
                }
            }

            byte[] decDataUnmodified = (byte[])decData.Clone();
            if (ReplaceInData(decData, 0x00, true))
            {
                if (compressed)
                {
                    Console.WriteLine("Replacing and recompressing ARM9...");
                    data = blz.BLZ_Encode(decData, 0);

                    uint newCompressedSize = (uint)data.Length;
                    if (newCompressedSize > len)
                    {
                        // new ARM is actually bigger, redo without the additional nullterm replacement
                        decData = decDataUnmodified;
                        ReplaceInData(decData, 0x00, false);
                        data = blz.BLZ_Encode(decData, 0, supressWarnings: true);
                        newCompressedSize = (uint)data.Length;

                        int arm9diff = (int)len - (int)newCompressedSize;
                        if (arm9diff < 0)
                        {
                            // still too big, remove debug strings
                            if (!RemoveStringsInKnownGames(GetGamecode(nds), decData))
                            {
                                RemoveDebugStrings(decData);
                            }
#if DEBUG
                            System.IO.File.WriteAllBytes("arm9-dec-without-debug.bin", decData);
#endif
                            data = blz.BLZ_Encode(decData, 0, supressWarnings: true);
                            newCompressedSize = (uint)data.Length;

                            arm9diff = (int)len - (int)newCompressedSize;
                            if (arm9diff < 0)
                            {
                                Console.WriteLine("WARNING: Recompressed ARM9 is " + -arm9diff + " bytes bigger than original!");
                                Console.WriteLine("         Patched game may be corrupted!");
#if DEBUG
                                System.IO.File.WriteAllBytes("arm9-too-big-recomp.bin", data);
#endif
                            }
                        }
                    }

                    if (newCompressedSize != len)
                    {
                        // new ARM is (still) different, attempt to find the metadata in the ARM9 secure area and replace that
                        bool foundSize = false;
                        for (int i = 0; i < 0x4000; i += 4)
                        {
                            uint maybeSize = BitConverter.ToUInt32(data, i);
                            if (maybeSize == len + 0x02000000u || maybeSize == len + 0x02004000u)
                            {
                                foundSize = true;

                                byte[] newCmpSizeBytes;
                                if (maybeSize == len + 0x02004000u)
                                {
                                    newCmpSizeBytes = BitConverter.GetBytes(newCompressedSize + 0x02004000u);
                                }
                                else
                                {
                                    newCmpSizeBytes = BitConverter.GetBytes(newCompressedSize + 0x02000000u);
                                }

                                data[i + 0] = newCmpSizeBytes[0];
                                data[i + 1] = newCmpSizeBytes[1];
                                data[i + 2] = newCmpSizeBytes[2];
                                data[i + 3] = newCmpSizeBytes[3];
                                break;
                            }
                        }
                        if (!foundSize)
                        {
                            Console.WriteLine("WARNING: Recompressed ARM9 is different size, and size could not be found in secure area!");
                            Console.WriteLine("         Patched game will probably not boot!");
                        }
                    }
#if DEBUG
                    uint newDecompressedSize         = (uint)decData.Length;
                    uint newAdditionalCompressedSize = newDecompressedSize - newCompressedSize;
                    Console.WriteLine("ARM9 new dec size: 0x" + newDecompressedSize.ToString("X6"));
                    Console.WriteLine("ARM9 new cmp size: 0x" + newCompressedSize.ToString("X6"));
                    Console.WriteLine("ARM9 new diff:     0x" + newAdditionalCompressedSize.ToString("X6"));
#endif
                }
                else
                {
                    Console.WriteLine("Replacing ARM9...");
                    data = decData;
                }
#if DEBUG
                System.IO.File.WriteAllBytes("arm9-new.bin", data);
#endif

                nds.Position = pos;
                nds.Write(data, 0, data.Length);

                int newSize = data.Length;
                int diff    = (int)len - newSize;

                // copy back footer
                if (diff > 0)
                {
                    List <byte> footer = new List <byte>();
                    nds.Position = pos + len;
                    if (nds.PeekUInt32() == 0xDEC00621)
                    {
                        for (int j = 0; j < 12; ++j)
                        {
                            footer.Add((byte)nds.ReadByte());
                        }

                        nds.Position = pos + newSize;
                        nds.Write(footer.ToArray(), 0, footer.Count);
                    }

                    // padding
                    for (int j = 0; j < diff; ++j)
                    {
                        nds.WriteByte(0xFF);
                    }
                }

                // write new size
                byte[] newSizeBytes = BitConverter.GetBytes(newSize);
                nds.Position = 0x2C;
                nds.Write(newSizeBytes, 0, 4);

                // recalculate checksums
                nds.Position = pos;
                ushort secureChecksum = new Crc16().ComputeChecksum(nds, 0x4000, 0xFFFF);
                nds.Position = 0x6C;
                nds.Write(BitConverter.GetBytes(secureChecksum), 0, 2);

                nds.Position = 0;
                ushort headerChecksum = new Crc16().ComputeChecksum(nds, 0x15E, 0xFFFF);
                nds.Write(BitConverter.GetBytes(headerChecksum), 0, 2);

                return(true);
            }

            return(false);
        }
Пример #5
0
        static bool PatchFile( string filename, string newFilename )
        {
            Console.WriteLine( "Reading and copying " + filename + "..." );
            using ( var nds = new System.IO.FileStream( newFilename, System.IO.FileMode.Create ) ) {
                using ( var ndsSrc = new System.IO.FileStream( filename, System.IO.FileMode.Open ) ) {
                    Util.CopyStream( ndsSrc, nds, (int)ndsSrc.Length );
                    ndsSrc.Close();
                }

                // http://dsibrew.org/wiki/DSi_Cartridge_Header

                // arm
                Console.WriteLine( "Patching ARM Executables..." );
                nds.Position = 0x20;
                uint arm9offset = nds.ReadUInt32();
                uint arm9entry = nds.ReadUInt32();
                uint arm9load = nds.ReadUInt32();
                uint arm9size = nds.ReadUInt32();
                uint arm7offset = nds.ReadUInt32();
                uint arm7entry = nds.ReadUInt32();
                uint arm7load = nds.ReadUInt32();
                uint arm7size = nds.ReadUInt32();

                bool modArm9 = PatchArm9( nds, arm9offset, arm9size );
                bool modArm7 = PatchArm7( nds, arm7offset, arm7size );

                // overlays
                Console.WriteLine( "Patching Overlays..." );
                nds.Position = 0x50;
                uint arm9overlayoff = nds.ReadUInt32();
                uint arm9overlaylen = nds.ReadUInt32();
                uint arm7overlayoff = nds.ReadUInt32();
                uint arm7overlaylen = nds.ReadUInt32();

                bool modOvl9 = PatchOverlay( nds, arm9overlayoff, arm9overlaylen );
                bool modOvl7 = PatchOverlay( nds, arm7overlayoff, arm7overlaylen );

                nds.Close();

                return modArm9 || modArm7 || modOvl9 || modOvl7;
            }
        }