Exemplo n.º 1
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;
        }
Exemplo n.º 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;
        }
Exemplo n.º 3
0
        public static string ReadToNulltermAndDecode( System.IO.Stream s )
        {
            StringBuilder sb = new StringBuilder();
            byte[] buffer = new byte[2];

            int b = s.ReadByte();
            while ( b != 0 && b != -1 ) {
                if ( ( b >= 0 && b <= 0x80 ) || ( b >= 0xA0 && b <= 0xDF ) ) {
                    // is a single byte
                        buffer[0] = (byte)b;
                        sb.Append( Util.ShiftJISEncoding.GetString( buffer, 0, 1 ) );
                } else {
                    if ( b != 0xFF ) {
                        // is two bytes
                        buffer[0] = (byte)b;
                        buffer[1] = (byte)s.ReadByte();
                        sb.Append( Util.ShiftJISEncoding.GetString( buffer ) );
                    } else {
                        // is a FFCC command
                        b = s.ReadByte();
                        switch ( b ) {
                            case 0xA0: // newline
                                sb.AppendLine();
                                break;
                            case 0xA1: // end of textbox
                                sb.AppendLine();
                                sb.AppendLine();
                                break;
                            case 0xA2:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xA3: // wait before printing more
                                sb.Append( "<Wait: " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xA4:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xA5:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xA6:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xA7: // multiple choice
                                sb.Append( "<Choice: " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xA8:
                                sb.Append( "<Player Name: " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xA9:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xAA:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt48().ToString( "X12" ) + ">" );
                                break;
                            case 0xAB:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xAC:
                                sb.Append( "<Color: Black>" );
                                break;
                            case 0xAD:
                                sb.Append( "<Color: Blue>" );
                                break;
                            case 0xAE:
                                sb.Append( "<Color: Red>" );
                                break;
                            case 0xAF:
                                sb.Append( "<Color: Pink>" );
                                break;
                            case 0xB0:
                                sb.Append( "<Color: Green>" );
                                break;
                            case 0xB1:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" ); // color?
                                break;
                            case 0xB2:
                                sb.Append( "<Color: Yellow>" );
                                break;
                            case 0xB3:
                                sb.Append( "<Color: Default>" );
                                break;
                            case 0xB4:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xB5:
                                sb.Append( "<Color: Orange>" );
                                break;
                            case 0xB6:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" ); // color?
                                break;
                            case 0xBA:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xBD:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xC2:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt64().ToString( "X16" ) + ">" );
                                break;
                            case 0xC4:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xC5:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xC6:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xC7:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xC8:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xC9:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xCA:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xCB:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xCC:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xCD:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt48().ToString( "X12" ) + ">" );
                                break;
                            case 0xCE:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xCF:
                                sb.Append( "<Hometown>" );
                                break;
                            case 0xD0: // print numeric variable
                                sb.Append( "<Numeric Variable " + s.ReadUInt24().ToString( "X6" ) + ">" );
                                break;
                            case 0xD1:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt64().ToString( "X16" ) + ">" );
                                break;
                            case 0xD2:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xD3:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xD4:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xD5:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xD6:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt24().ToString( "X6" ) + ">" );
                                break;
                            case 0xD7:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xD8:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xD9:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xDA:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt40().ToString( "X10" ) + ">" );
                                break;
                            case 0xDB:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt32().ToString( "X8" ) + ">" );
                                break;
                            case 0xDC:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt40().ToString( "X10" ) + ">" );
                                break;
                            case 0xDD:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt48().ToString( "X12" ) + ">" );
                                break;
                            case 0xDE:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt56().ToString( "X14" ) + ">" );
                                break;
                            case 0xDF:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt64().ToString( "X16" ) + ">" );
                                break;
                            case 0xE0:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt56().ToString( "X14" ) + ">" );
                                break;
                            case 0xE1:
                                sb.Append( "<" + b.ToString( "X2" ) + ": " + s.ReadUInt16().ToString( "X4" ) + ">" );
                                break;
                            case 0xE2: // start of a singular/plural either/or checking a variable; singular follows
                                sb.Append( "<If Variable " + s.ReadUInt16().ToString( "X4" ) + " == 1 Then: " );
                                break;
                            case 0xE3: // start of a singular/plural either/or checking the single/multiplayer status; singular follows
                                sb.Append( "<If Player Count == 1 Then: " );
                                break;
                            case 0xE4: // start of a male/female either/or checking the current player character; male follows
                                sb.Append( "<If Player is Male Then: " );
                                break;
                            case 0xE5: // start of a male/female either/or checking a different player character; male follows
                                sb.Append( "<If Character is Male Then: " );
                                break;
                            case 0xE6: // middle of an either/or; else case follows
                                sb.Append( " / Else: " );
                                break;
                            case 0xE8:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xE9:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xEB:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xED:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xEE:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xEF:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xF0:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xF2:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xF3:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xF4:
                                sb.Append( "<" + b.ToString( "X2" ) + ">" );
                                break;
                            case 0xE7: // end of an either/or
                                sb.Append( ">" );
                                break;
                            default:
                                string unknownCommandString = "Unknown FFCC text command 0x" + b.ToString( "X2" );
                                sb.Append( "<" + unknownCommandString + ">" );
                                break;
                        }
                    }
                }
                b = s.ReadByte();
            }
            return sb.ToString();
        }