Esempio n. 1
0
        public static void parseT64(ROMInfo info, WrappedInputStream s)
        {
            info.addInfo("Platform", "Commodore 64");
            s.Position = 32;

            short version = s.readShortLE();

            info.addInfo("Version", version);

            short dirEntries = s.readShortLE();

            info.addInfo("Number of files", dirEntries);

            short usedEntries = s.readShortLE();

            info.addInfo("Number of used entries", usedEntries);

            short reserved = s.readShortLE();

            info.addInfo("Reserved", reserved, true);

            string name = s.read(24, Encoding.ASCII).TrimEnd(' ');

            info.addInfo("Internal name", name);
        }
Esempio n. 2
0
        public static Dictionary <string, object> convertParamSFO(WrappedInputStream s)
        {
            var d = new Dictionary <string, object>();

            s.Position = 0x08;
            int keyTableStart   = s.readIntLE();
            int dataTableStart  = s.readIntLE();
            int numberOfEntries = s.readIntLE();

            for (int i = 0; i < numberOfEntries; ++i)
            {
                short keyRelativeOffset = s.readShortLE();
                int   keyOffset         = keyTableStart + keyRelativeOffset;

                short dataFormat      = s.readShortLE();
                int   dataUsedLength  = s.readIntLE();
                int   dataTotalLength = s.readIntLE();

                int dataRelativeOffset = s.readIntLE();
                int dataOffset         = dataTableStart + dataRelativeOffset;

                long originalPos = s.Position;

                s.Position = keyOffset;
                string key = s.readNullTerminatedString(Encoding.UTF8);

                s.Position = dataOffset;
                object value = null;
                switch (dataFormat)
                {
                case 0x0004:                         //utf8 special mode (not null terminated)
                    value = s.read(dataUsedLength, Encoding.UTF8);
                    break;

                case 0x0204:                         //utf8 (null terminated)
                    value = s.readNullTerminatedString(Encoding.UTF8, dataUsedLength);
                    break;

                case 0x0404:                         //int32
                    value = s.readIntLE();
                    break;

                default:
                    value = String.Format("Unknown format!!! 0x{0:X2}", dataFormat);
                    break;
                }
                if (!d.ContainsKey(key))
                {
                    d.Add(key, value);
                }
                //I guess I should report if there's a duplicate key but that shouldn't happen

                s.Position = originalPos;
            }

            return(d);
        }
Esempio n. 3
0
        public static void parseSDSCHeader(ROMInfo info, WrappedInputStream s)
        {
            int majorVersion = s.read();

            info.addInfo("Major version", decodeBCD(majorVersion));
            int minorVersion = s.read();

            info.addInfo("Minor version", decodeBCD(minorVersion));

            int day = decodeBCD(s.read());

            info.addInfo("Day", day);
            int month = decodeBCD(s.read());

            info.addInfo("Month", (month != 0 && month < 13) ? System.Globalization.DateTimeFormatInfo.CurrentInfo.GetMonthName(month) : String.Format("Unknown ({0})", month));
            int year = decodeBCD(s.read(2));

            info.addInfo("Year", year);

            try {
                info.addInfo("Date", new DateTime(year, month, day));
            } catch (ArgumentOutOfRangeException) {
                //Ignore
            }

            ushort authorNameOffset  = (ushort)s.readShortLE();
            ushort nameOffset        = (ushort)s.readShortLE();
            ushort descriptionOffset = (ushort)s.readShortLE();

            info.addInfo("Author offset", authorNameOffset, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Name offset", nameOffset, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Description offset", descriptionOffset, ROMInfo.FormatMode.HEX, true);

            if (authorNameOffset != 0xfff && authorNameOffset != 0)
            {
                s.Position = authorNameOffset;
                info.addInfo("Author", readNullTerminatedString(s));
            }
            if (nameOffset != 0xffff)
            {
                s.Position = nameOffset;
                info.addInfo("Internal name", readNullTerminatedString(s));
            }
            if (descriptionOffset != 0xffff)
            {
                s.Position = descriptionOffset;
                info.addInfo("Description", readNullTerminatedString(s));
            }
        }
Esempio n. 4
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            WrappedInputStream s = file.stream;

            string copyrightInfo = s.read(28, Encoding.ASCII);

            info.addInfo("Copyright", copyrightInfo, true);
            //For first party games this should say that, and for third party games it should say " LICENSED BY SNK CORPORATION"
            info.addInfo("First party", "COPYRIGHT BY SNK CORPORATION".Equals(copyrightInfo));

            byte[] entryPoint = s.read(4);
            info.addInfo("Entry point", entryPoint, ROMInfo.FormatMode.HEX, true);

            short gameNumber = s.readShortLE();

            info.addInfo("Product code", gameNumber.ToString("X2"));

            int version = s.read();

            info.addInfo("Version", version);

            bool isColor = s.read() == 0x10;

            info.addInfo("Is colour", isColor);
            info.addInfo("Platform", isColor ? "Neo Geo Pocket Color" : "Neo Geo Pocket");


            string internalName = s.read(12, Encoding.ASCII);

            info.addInfo("Internal name", internalName);

            byte[] reserved = s.read(16);
            info.addInfo("Reserved", reserved, true);             //Should be 0 filled
        }
Esempio n. 5
0
        public static void readWonderswanROM(ROMInfo info, ROMFile file)
        {
            WrappedInputStream s = file.stream;

            s.Seek(-10, System.IO.SeekOrigin.End);

            int publisher = s.read();

            info.addInfo("Publisher", publisher, PUBLISHERS);

            int  deviceFlag = s.read();
            bool isColor    = deviceFlag == 1;

            info.addInfo("Is colour", isColor);
            info.addInfo("Device type", deviceFlag, true); //This might have more to it, but probably not

            int cartID = s.read();                         //Last 2 digits of SKU

            info.addInfo("Product code", cartID);

            int version = s.read();

            info.addInfo("Version", version);

            int romSize = s.read();

            info.addInfo("ROM size", romSize, ROM_SIZES, ROMInfo.FormatMode.SIZE);

            int ramSize = s.read();

            info.addInfo("Save size", ramSize, RAM_SIZES, ROMInfo.FormatMode.SIZE);
            info.addInfo("Save type", ramSize >= 10 ? "EEPROM" : ramSize == 0 ? "None" : "SRAM");

            int flags = s.read();

            info.addInfo("Flags", flags, true);             //Maybe there are more flags than these three, probably not
            info.addInfo("ROM speed", (flags & 4) > 0 ? "3 speed" : "1 speed");
            info.addInfo("Bus width (bits)", (flags & 2) > 0 ? 16 : 8);
            info.addInfo("Screen orientation", (flags & 1) == 1 ? "Vertical" : "Horizontal");

            bool hasRTC = s.read() == 1;

            info.addInfo("Has RTC", hasRTC);

            ushort checksum = (ushort)s.readShortLE();

            info.addInfo("Checksum", checksum, ROMInfo.FormatMode.HEX, true);
            int calculatedChecksum = calcChecksum(s);

            info.addInfo("Calculated checksum", calculatedChecksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Checksum valid?", checksum == calculatedChecksum);
        }
Esempio n. 6
0
        public static void parseSegaHeader(ROMInfo info, WrappedInputStream s, long offset, bool isGameGear)
        {
            s.Position = offset;

            byte[] reserved = s.read(2);
            info.addInfo("Reserved", reserved, true);

            ushort checksum = (ushort)s.readShortLE();

            info.addInfo("Checksum", checksum, ROMInfo.FormatMode.HEX, true);

            byte[] productCodeHi         = s.read(2);
            int    productCodeAndVersion = s.read();
            int    productCodeLo         = (productCodeAndVersion & 0xf0) >> 4;
            int    version = productCodeAndVersion & 0x0f;

            string productCode = String.Format("{0}{1:0000}", productCodeLo == 0 ? "" : productCodeLo.ToString(), decodeBCD(productCodeHi));

            info.addInfo("Product code", productCode);
            if (isGameGear)
            {
                if (productCode.Length >= 5)
                {
                    string makerCode = "T-" + productCode.Substring(0, productCode.Length - 3);
                    info.addInfo("Publisher", makerCode, SegaCommon.LICENSEES);
                }
                else
                {
                    info.addInfo("Publisher", "Sega");
                }
            }
            info.addInfo("Version", version);

            int regionCodeAndROMSize = s.read();
            int regionCode           = (regionCodeAndROMSize & 0xf0) >> 4;
            int romSize = regionCodeAndROMSize & 0x0f;

            info.addInfo("Region", regionCode, REGIONS);
            info.addInfo("ROM size", romSize, ROM_SIZES, ROMInfo.FormatMode.SIZE);

            ushort calculatedChecksum = calculateChecksum(s, romSize);

            info.addInfo("Calculated checksum", calculatedChecksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Checksum valid?", checksum == calculatedChecksum);
        }
Esempio n. 7
0
        public static void readNitroSubFNT(FilesystemDirectory fs, WrappedInputStream s, uint fatOffset, uint fatSize, uint fntOffset, uint fntSize, uint subTableOffset, ushort firstID)
        {
            long origPos = s.Position;

            try {
                ushort currentFileID = firstID;
                s.Position = fntOffset + subTableOffset;
                while (true)
                {
                    int length = s.read();
                    if (length == -1 || length == 0 || length == 0x80)
                    {
                        break;
                    }

                    if (length < 0x80)
                    {
                        ushort fileID = currentFileID;
                        currentFileID++;
                        string name          = s.read(length, Encoding.ASCII);
                        var    offsetAndSize = getFatInfo(s, fatOffset, fileID);
                        fs.addChild(name, offsetAndSize.Item1, offsetAndSize.Item2);
                    }
                    else
                    {
                        length -= 0x80;

                        string name  = s.read(length, Encoding.ASCII);
                        ushort dirID = (ushort)((ushort)s.readShortLE() & 0x0fff);
                        FilesystemDirectory folder = new FilesystemDirectory()
                        {
                            name = name,
                        };
                        readNitroMainFNT(folder, s, fatOffset, fatSize, fntOffset, fntSize, dirID * (uint)8);
                        fs.addChild(folder);
                    }
                }
            } finally {
                s.Position = origPos;
            }
        }
Esempio n. 8
0
        public static void parse3DSX(ROMInfo info, ROMFile file)
        {
            WrappedInputStream s = file.stream;

            s.Position = 4;
            short headerSize        = s.readShortLE();
            bool  hasExtendedHeader = headerSize > 32;

            info.addInfo("Header size", headerSize, ROMInfo.FormatMode.SIZE);

            //meh..... don't really care about the rest of the 3dsx header, it's basically just a boneless .elf
            bool lookForSMDHFile = true;

            if (hasExtendedHeader)
            {
                s.Position = 32;
                uint smdhOffset = (uint)s.readIntLE();
                uint smdhSize   = (uint)s.readIntLE();
                info.addInfo("SMDH offset", smdhOffset, ROMInfo.FormatMode.HEX);
                info.addInfo("SMDH size", smdhSize, ROMInfo.FormatMode.SIZE);

                if (smdhSize > 0)
                {
                    lookForSMDHFile = false;
                    parseSMDH(info, s, null, smdhOffset);
                }
            }

            if (lookForSMDHFile)
            {
                string smdhName = Path.ChangeExtension(file.name, "smdh");
                if (file.hasSiblingFile(smdhName))
                {
                    var smdh = file.getSiblingFile(smdhName);
                    parseSMDH(info, smdh, null);
                }
            }
        }
Esempio n. 9
0
        public static void readNitroMainFNT(FilesystemDirectory fs, WrappedInputStream s, uint fatOffset, uint fatSize, uint fntOffset, uint filenameTableSize, uint tableOffset)
        {
            //tableOffset being the offset _into_ the FNT, not the offset _of_ the FNT (which is fntOffset). That's confusing. I'll figure out how to be less confusing later

            long origPos = s.Position;

            try {
                s.Position = fntOffset + tableOffset;
                uint   subTableOffset = (uint)s.readIntLE();
                ushort firstID        = (ushort)s.readShortLE();
                if (firstID > 0xefff)
                {
                    //Shouldn't go higher than that, apparently
                    return;
                }
                //TODO: For tableOffset = 0 (root), read number of directories (short LE here):
                //Validate that <= 4096 directories
                //Validate that number of directories * 8 < fntSize

                readNitroSubFNT(fs, s, fatOffset, fatSize, fntOffset, filenameTableSize, subTableOffset, firstID);
            } finally {
                s.Position = origPos;
            }
        }
Esempio n. 10
0
        public static void parseUninterleavedData(ROMInfo info, byte[] data)
        {
            var s = new WrappedInputStream(new MemoryStream(data));

            byte[] fixed1 = s.read(3);             //00 30 01, supposedly the 01 means don't calculate global checksum (okay I won't)
            info.addInfo("Fixed value 1", fixed1, true);

            int primaryType      = s.read();
            int cardTypeUpperBit = primaryType & 1; //Rest of primaryType is unknown

            byte[] fixed2 = s.read(2);              //00 01
            info.addInfo("Fixed value 2", fixed2, true);

            short stripSize = s.readShortBE();             //FIXME Not sure if this is BE or LE because it seems wrong either way... but BE is closest to not being wrong (LE results in everything being 4KB which is definitely not the case... or is it? No, that can't be right. I'm confused...)

            info.addInfo("Strip size", stripSize, ROMInfo.FormatMode.SIZE);

            byte[] fixed3 = s.read(4);             //00 00 10 12
            info.addInfo("Fixed value 3", fixed3, true);

            short regionAndType     = s.readShortLE();
            int   cardTypeLowerBits = (regionAndType & 0b0000_0000_1111_0000) >> 4;
            int   cardType          = (cardTypeUpperBit << 4) | cardTypeLowerBits;

            info.addInfo("Type", cardType, CARD_TYPES);
            int region = (regionAndType & 0b0000_1111_0000_0000) >> 8;

            info.addInfo("Region", region, REGIONS);

            int stripType = s.read();
            //2 = short and 1 = long, but we might have already gotten that from the .raw block header. Or have we?

            int fixed4 = s.read();             //0

            byte[] unknown = s.read(2);
            int    fixed5  = s.read();         //0x10

            info.addInfo("Fixed value 4", fixed4, true);
            info.addInfo("Unknown 4", unknown, true);
            info.addInfo("Fixed value 5", fixed5, true);

            //"Data Checksum [13h-14h] is the complement (NOT) of the sum of all halfwords in all Data Fragments, however, it's all done in reversed byte order: checksum is calculated with halfwords that are read in MSB,LSB order, and the resulting checksum is stored in MSB,LSB order in the Header Fragment."
            //Needless to say I will not bother
            short dataChecksum = s.readShortLE();

            info.addInfo("Data checksum", dataChecksum, ROMInfo.FormatMode.HEX, true);

            byte[] fixed6 = s.read(5);             //19 00 00 00 08
            info.addInfo("Fixed value 6", fixed6, true);

            string copyright = s.read(8, Encoding.ASCII);             //Should be NINTENDO

            info.addInfo("Copyright", copyright, true);

            byte[] fixed7 = s.read(4);             //00 22 00 09
            info.addInfo("Fixed value 7", fixed7, true);

            int sizeInfo = s.readIntLE();
            //Bit 0 is unknown, but it seems to be only set for Pokemon TCG cards. Not all of them though, so there's no workarounds for them being "funny"
            int stripNumber     = (sizeInfo & 0b0000_0000_0000_0000_0000_0000_0001_1110) >> 1;
            int numberOfStrips  = (sizeInfo & 0b0000_0000_0000_0000_0000_0001_1110_0000) >> 5;            //FIXME This ain't right sometimes... what's going on? Anything that's a normal e-Reader card is fine, Pokemon TCG cards are often not. I blame Wizards of the Coast for everything, like sometimes you'll have stripNumber > numberOfStrips, or stripNumber == 0, or numberOfStrips == 0, and that's obviously wrong
            int sizeOfAllStrips = (sizeInfo & 0b0000_0000_1111_1111_1111_1110_0000_0000) >> 9;            //This ain't right either...

            info.addInfo("Strip number", stripNumber);
            info.addInfo("Number of strips", numberOfStrips);
            info.addInfo("Size of all strips", sizeOfAllStrips, ROMInfo.FormatMode.SIZE);

            int flags = s.readIntLE();

            info.addInfo("Permission to save", (flags & 1) > 0 ? "Prompt for save" : "Start immediately");
            bool hasSubTitle = (flags & 2) == 0;

            info.addInfo("Has sub-title", hasSubTitle);
            bool isNES = (flags & 4) > 0;

            int headerChecksum = s.read();             //regionAndType, unknown, sizeInfo, flags xored together
            int globalChecksum = s.read();             //"Global Checksum [2Fh] is the complement (NOT) of the sum of the first 2Fh bytes in the Data Header plus the sum of all Data Fragment checksums; the Data Fragment checksums are all 30h bytes in a fragment XORed with each other." what

            info.addInfo("Checksum", headerChecksum, ROMInfo.FormatMode.HEX, true);
            int calculatedHeaderChecksum = xorBytes(shortToBytesLE(regionAndType)) ^ xorBytes(unknown) ^ xorBytes(intToBytesLE(sizeInfo)) ^ xorBytes(intToBytesLE(flags));

            info.addInfo("Calculated checksum", calculatedHeaderChecksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Checksum valid?", headerChecksum == calculatedHeaderChecksum);
            info.addInfo("Global checksum", globalChecksum, ROMInfo.FormatMode.HEX, true);

            bool isPokemon = cardType >= 2 && cardType <= 5;             //It warms my heart immensely to write this line
            //Well, what it really means is "has stats"

            int mainTitleLength = isPokemon ? 17 : 33;

            Encoding encoding = region == 1 ? Encoding.ASCII : MainProgram.shiftJIS;

            //FIXME This is broken for "Construction" things and "P-Letter" things, do they not have a title?
            byte[] mainTitle = s.read(mainTitleLength);
            mainTitle = mainTitle.TakeWhile(b => b != 0).ToArray();
            //This seems to be blank for most of those applications with "Permission to save" == "Start immediately", but could also just be things being broken
            info.addInfo("Internal name", encoding.GetString(mainTitle));

            if (hasSubTitle)
            {
                int subTitleLength = isPokemon ? 21 : 33;

                for (int i = 0; i < numberOfStrips; ++i)
                {
                    string prefix        = "Sub-title " + (i + 1);
                    byte[] subTitleBytes = s.read(subTitleLength);

                    string subTitle;
                    if (isPokemon)
                    {
                        int stats = (subTitleBytes[2] << 16) | (subTitleBytes[1] << 8) | subTitleBytes[0];

                        int hp = (stats & 0xf) * 10;
                        info.addInfo(prefix + " Pokemon HP", hp);

                        int id3 = (stats & 0b0000_0000_0000_0000_0111_0000) >> 4;
                        int id2 = (stats & 0b0000_0000_0011_1111_1000_0000) >> 7;
                        int id1 = (stats & 0b0000_0111_1100_0000_0000_0000) >> 14;

                        info.addInfo(prefix + " ID", String.Format("{0}-{1:D2}-{2}", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[id1], id2 + 1, "ABCDEFG#"[id3]));

                        subTitle = encoding.GetString(subTitleBytes.Skip(3).TakeWhile(b => b != 0).ToArray());
                    }
                    else
                    {
                        subTitle = encoding.GetString(subTitleBytes.TakeWhile(b => b != 0).ToArray());
                    }
                    info.addInfo(prefix, subTitle);
                }
            }


            if (stripNumber == 1)
            {
                short vpkSize = s.readShortLE();
                info.addInfo("VPK size", vpkSize, ROMInfo.FormatMode.SIZE);

                byte[] check = s.read(4);
                if (check[0] == 0 && check[1] == 0 && check[2] == 0 && check[3] == 0)
                {
                    //GBA type cards have a 4 byte value here for some reason
                    info.addInfo("Application type", "GBA");
                }
                else
                {
                    info.addInfo("Application type", isNES ? "NES" : "Z80");
                    s.Seek(-4, SeekOrigin.Current);
                }
            }
            else
            {
                info.addInfo("Application type", isNES ? "NES" : "GBA/Z80");
            }

            //The rest of this is VPK compressed data (should be vpkSize)
        }
Esempio n. 11
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            if ("plg".Equals(file.extension))
            {
                addSupercardDS2PluginInfo(info, file);
                return;
            }

            WrappedInputStream s = file.stream;

            s.Position = 0xc0;
            bool passMe = isPassMeEntryPoint(s.read(8));

            s.Position = 0;
            info.addInfo("PassMe", passMe);

            string title = s.read(12, Encoding.ASCII).TrimEnd('\0');

            string gameCode   = s.read(4, Encoding.ASCII);
            char   gameType   = gameCode[0];
            string shortTitle = gameCode.Substring(1, 2);
            char   country    = gameCode[3];

            string makerCode = s.read(2, Encoding.ASCII);

            if (!passMe)
            {
                info.addInfo("Internal name", title);
                info.addInfo("Product code", gameCode);
                info.addInfo("Type", gameType, GAME_TYPES);
                info.addInfo("Short title", shortTitle);
                info.addInfo("Country", country, NintendoCommon.COUNTRIES);
                info.addInfo("Publisher", makerCode, NintendoCommon.LICENSEE_CODES);
            }

            int unitCode = s.read();

            info.addInfo("Device type", unitCode, UNIT_CODES);
            info.addInfo("Platform", unitCode == 3 ? "DSi" : "DS");
            int encryption_seed = s.read();             //From 0 to 7, usually 0

            info.addInfo("Encryption seed", encryption_seed, true);
            long romSize = (128 * 1024) << s.read();

            info.addInfo("ROM size", romSize, ROMInfo.FormatMode.SIZE);

            //Should be 0 filled (Pokemon Black 2 doesn't 0 fill it, so maybe it doesn't have to be)
            byte[] reserved = s.read(7);
            info.addInfo("Reserved", reserved, true);
            //Should be 0 normally, but used somehow on DSi
            int reserved2 = s.read();

            info.addInfo("Reserved 2", reserved2, true);

            int regionCode = s.read();

            if (unitCode >= 2)
            {
                //DSi has its own region locking system, and will happily play iQue games etc
                info.addInfo("DS region", regionCode, REGIONS);
            }
            else
            {
                info.addInfo("Region", regionCode, REGIONS);
            }
            int version = s.read();

            info.addInfo("Version", version);
            int autostart = s.read();             //Bit 2 skips health and safety screen when autostarting the game

            info.addInfo("Autostart param", autostart, ROMInfo.FormatMode.HEX, true);

            int arm9Offset = s.readIntLE();

            info.addInfo("ARM9 offset", arm9Offset, ROMInfo.FormatMode.HEX, true);
            int arm9Entry = s.readIntLE();

            info.addInfo("ARM9 entry point", arm9Entry, ROMInfo.FormatMode.HEX, true);
            int arm9RAMAddress = s.readIntLE();

            info.addInfo("ARM9 RAM address", arm9RAMAddress, ROMInfo.FormatMode.HEX, true);
            int arm9Size = s.readIntLE();

            info.addInfo("ARM9 size", arm9Size, ROMInfo.FormatMode.SIZE, true);

            int arm7Offset = s.readIntLE();

            info.addInfo("ARM7 offset", arm7Offset, ROMInfo.FormatMode.HEX, true);
            int arm7Entry = s.readIntLE();

            info.addInfo("ARM7 entry point", arm7Entry, ROMInfo.FormatMode.HEX, true);
            int arm7RAMAddress = s.readIntLE();

            info.addInfo("ARM7 RAM address", arm7RAMAddress, ROMInfo.FormatMode.HEX, true);
            int arm7Size = s.readIntLE();

            info.addInfo("ARM7 size", arm7Size, ROMInfo.FormatMode.SIZE, true);

            int filenameTableOffset = s.readIntLE();

            info.addInfo("Filename table offset", filenameTableOffset, ROMInfo.FormatMode.HEX, true);
            int filenameTableSize = s.readIntLE();

            info.addInfo("Filename table size", filenameTableSize, ROMInfo.FormatMode.SIZE, true);
            int fatOffset = s.readIntLE();

            info.addInfo("File allocation table offset", fatOffset, ROMInfo.FormatMode.HEX, true);
            int fatSize = s.readIntLE();

            info.addInfo("File allocation table size", fatSize, ROMInfo.FormatMode.SIZE, true);
            int fileARM9OverlayOffset = s.readIntLE();

            info.addInfo("File ARM9 overlay offset", fileARM9OverlayOffset, ROMInfo.FormatMode.HEX, true);
            int fileARM9OverlaySize = s.readIntLE();

            info.addInfo("File ARM9 overlay size", fileARM9OverlaySize, ROMInfo.FormatMode.SIZE, true);
            int fileARM7OverlayOffset = s.readIntLE();

            info.addInfo("File ARM7 overlay offset", fileARM7OverlayOffset, ROMInfo.FormatMode.HEX, true);
            int fileARM7OverlaySize = s.readIntLE();

            info.addInfo("File ARM7 overlay size", fileARM7OverlaySize, ROMInfo.FormatMode.SIZE, true);

            byte[] normalCommandSetting = s.read(4);           //For port 0x40001A4 (ROMCTRL), usually 0x00586000
            info.addInfo("Normal command setting", normalCommandSetting, true);
            byte[] key1CommandSetting = s.read(4);             //For port 0x40001A4 (ROMCTRL), usually 0x001808f8
            info.addInfo("KEY1 command cetting", key1CommandSetting, true);

            int bannerOffset = s.readIntLE();

            info.addInfo("Banner offset", bannerOffset, ROMInfo.FormatMode.HEX, true);

            short secureAreaChecksum = s.readShortLE();

            info.addInfo("Secure area checksum", secureAreaChecksum, ROMInfo.FormatMode.HEX, true);
            short calculatedSecureAreaChecksum = calcCRC16(s, 0x4000, 0x7fff);

            info.addInfo("Calculated secure area checksum", calculatedSecureAreaChecksum, ROMInfo.FormatMode.HEX, true);
            //Do not be alarmed if this is not valid, since most DS dumps erase the secure area
            info.addInfo("Secure area checksum valid?", secureAreaChecksum == calculatedSecureAreaChecksum);

            short secureAreaDelay = s.readShortLE();             //131kHz units, 0x051e = 10ms, 0x0d7e = 26ms

            info.addInfo("Secure area delay (ms)", secureAreaDelay / 131, true);

            int arm9AutoLoadRAMAddress = s.readIntLE();

            info.addInfo("ARM9 auto load RAM address", arm9AutoLoadRAMAddress, ROMInfo.FormatMode.HEX, true);
            int arm7AutoLoadRAMAddress = s.readIntLE();

            info.addInfo("ARM7 auto load RAM address", arm7AutoLoadRAMAddress, ROMInfo.FormatMode.HEX, true);

            byte[] secureAreaDisable = s.read(8);             //Usually 0 filled
            info.addInfo("Secure area disable", secureAreaDisable, true);

            int usedROMSize   = s.readIntLE();           //Excludes DSi area, so we add the info item later to determine the meaning
            int romHeaderSize = s.readIntLE();

            info.addInfo("Header size", romHeaderSize, ROMInfo.FormatMode.SIZE);

            byte[] reserved3 = s.read(0x38);             //0 filled except on DSi which uses first 12 bytes for some purpose
            info.addInfo("Reserved 3", reserved3, true);
            byte[] nintendoLogo = s.read(0x9c);          //Same as on GBA (I think?)
            info.addInfo("Nintendo logo", nintendoLogo, true);

            short nintendoLogoChecksum = s.readShortLE();             //CRC16 of nintendoLogo, should be 0xcf56?

            info.addInfo("Nintendo logo checksum", nintendoLogoChecksum, ROMInfo.FormatMode.HEX, true);
            short calculatedNintendoLogoChecksum = CRC16.crc16(nintendoLogo);

            info.addInfo("Calculated Nintendo logo checksum", calculatedNintendoLogoChecksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Nintendo logo checksum valid?", nintendoLogoChecksum == calculatedNintendoLogoChecksum);

            short headerChecksum = s.readShortLE();             //CRC16 of header up until here (first 0x15d bytes)

            info.addInfo("Header checksum", headerChecksum, ROMInfo.FormatMode.HEX, true);
            short calculatedHeaderChecksum = calcCRC16(s, 0, 0x15d);

            info.addInfo("Calculated header checksum", calculatedHeaderChecksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Header checksum valid?", headerChecksum == calculatedHeaderChecksum);

            int debugROMOffset = s.readIntLE();

            info.addInfo("Debug ROM offset", debugROMOffset, ROMInfo.FormatMode.HEX, true);
            int debugSize = s.readIntLE();

            info.addInfo("Debug ROM size", debugSize, ROMInfo.FormatMode.SIZE, true);
            int debugRAMAddress = s.readIntLE();

            info.addInfo("Debug RAM address", debugRAMAddress, ROMInfo.FormatMode.HEX, true);

            //Both zero filled, who cares
            byte[] reserved4 = s.read(4);
            info.addInfo("Reserved 4", reserved4, true);
            byte[] reserved5 = s.read(0x90);
            info.addInfo("Reserved 5", reserved5, true);

            if (unitCode >= 2)
            {
                info.addInfo("Used ROM size excluding DSi area", usedROMSize, ROMInfo.FormatMode.SIZE);
                parseDSiHeader(info, s);
            }
            else
            {
                info.addInfo("Used ROM size", usedROMSize, ROMInfo.FormatMode.SIZE);
            }

            info.addInfo("Homebrew?", arm9Offset < 0x4000);
            if (arm9Offset >= 0x4000 && arm9Offset < 0x8000)
            {
                info.addInfo("Contains secure area", true);
                s.Position = 0x4000;
                //Secure area, please leave all your electronic devices at the front counter before entering
                byte[] secureAreaID = s.read(8);
                info.addInfo("Multiboot", secureAreaID.All(x => x == 0));
                info.addInfo("Decrypted", isDecryptedSecureAreaID(secureAreaID));
            }
            else
            {
                info.addInfo("Contains secure area", false);
            }
            s.Position = 0x1000;
            //See also: https://twitter.com/Myriachan/status/964580936561000448
            //This should be true, but it's often false, especially for No-Intro verified dumps
            info.addInfo("Contains Blowfish encryption tables", s.read(8).Any(x => x != 0));

            bool containsWifi = false;

            if (fatOffset > 0 && fatSize > 0 && filenameTableOffset > 0 && filenameTableSize > 0)
            {
                //TODO: Negative fatOffset/fatSize/etc values might mean it is actually there, just really big (and unsigned). But we should check for absurdly big values that are invalid before doing that
                var fs = readNitroFS(s, (uint)fatOffset, (uint)fatSize, (uint)filenameTableOffset, (uint)filenameTableSize);
                info.addFilesystem(fs);
                if (fs.contains("dwc"))
                {
                    var dwc = (FilesystemDirectory)fs.getChild("dwc");
                    containsWifi = dwc.contains("utility.bin");
                }
            }
            info.addInfo("Contains WFC setup", containsWifi);
            parseBanner(info, s, bannerOffset);
        }
Esempio n. 12
0
        public static void parseSNESHeader(WrappedInputStream s, ROMInfo info, long offset)
        {
            s.Position = offset;

            //Finally now I can get on with the fun stuff
            //A lot of this was written at like 4am so ignore the random yelling, or at least that's what I think the reason for some of these old comments is

            //It's not ASCII
            string name = s.read(21, MainProgram.shiftJIS).TrimEnd('\0', ' ');

            info.addInfo("Internal name", name);

            int layout = s.read();

            info.addInfo("Mapper", layout, ROM_LAYOUTS);

            int type = s.read();

            info.addInfo("ROM type", type, ROM_TYPES);

            int romSize = s.read();

            info.addInfo("ROM size", romSize, ROM_RAM_SIZES, ROMInfo.FormatMode.SIZE);

            int ramSize = s.read();

            info.addInfo("Save size", ramSize, ROM_RAM_SIZES, ROMInfo.FormatMode.SIZE);

            int countryCode = s.read();

            int  licenseeCode       = s.read();
            bool usesExtendedHeader = false;

            if (licenseeCode == 0x33)
            {
                //WHY"D YOU HAVE TO GO AND MAKE EVERYTHING SO COMPLICATED
                usesExtendedHeader = true;
            }
            else
            {
                info.addInfo("Publisher", licenseeCode.ToString("X2"), NintendoCommon.LICENSEE_CODES);
            }
            info.addInfo("Uses extended header", usesExtendedHeader);

            int version = s.read();

            info.addInfo("Version", version);

            ushort inverseChecksum = (ushort)s.readShortLE();

            info.addInfo("Inverse checksum", inverseChecksum, ROMInfo.FormatMode.HEX, true);
            ushort checksum = (ushort)s.readShortLE();

            info.addInfo("Checksum", checksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Checksums add up?", checksum + inverseChecksum == 0xffff);
            int calculatedChecksum = calcChecksum(s);

            info.addInfo("Calculated checksum", calculatedChecksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Checksum valid?", checksum == calculatedChecksum);

            if (usesExtendedHeader)
            {
                //Heck you
                s.Position = offset - 0x10;

                string makerCode = s.read(2, Encoding.ASCII);
                info.addInfo("Publisher", makerCode, NintendoCommon.LICENSEE_CODES);

                string productCode = s.read(4, Encoding.ASCII);
                info.addInfo("Product code", productCode);

                if (isProductCodeValid(productCode))
                {
                    info.addInfo("Type", productCode[0], GAME_TYPES);
                    info.addInfo("Short title", productCode.Substring(1, 2));
                    info.addInfo("Country", productCode[3], NintendoCommon.COUNTRIES);
                    info.addInfo("Country 2", countryCode, COUNTRIES);                     //TODO Word this better
                }
                else
                {
                    if (productCode[2] == ' ' && productCode[3] == ' ')
                    {
                        info.addInfo("Short title", productCode.TrimEnd(' '));
                    }
                    info.addInfo("Country", countryCode, COUNTRIES);
                }

                byte[] reserved = s.read(6);
                info.addInfo("Reserved", reserved, true);

                int expansionFlashSize = s.read();
                if (expansionFlashSize > 0)
                {
                    expansionFlashSize = (1 << s.read()) * 1024;
                }
                info.addInfo("Expansion Flash size", expansionFlashSize, ROMInfo.FormatMode.SIZE);
                int expansionRAMSize = s.read();
                if (expansionRAMSize > 0)
                {
                    expansionRAMSize = (1 << s.read()) * 1024;
                }
                info.addInfo("Expansion RAM size", expansionRAMSize, ROMInfo.FormatMode.SIZE);

                int specialVersion = s.read();
                info.addInfo("Special version", specialVersion);
            }
            else
            {
                info.addInfo("Country", countryCode, COUNTRIES);
            }

            s.Position = offset - 1;
            int chipSubtype = s.read();

            info.addInfo("ROM sub-type", chipSubtype);
        }
Esempio n. 13
0
        public static void parseSMDH(ROMInfo info, WrappedInputStream s, string prefix, long offset = 0)
        {
            s.Position = offset + 4;
            short version = s.readShortLE();

            info.addInfo("SMDH version", version);

            s.Position = offset + 8;

            for (int i = 0; i < 16; ++i)
            {
                byte[] shortNameBytes = s.read(0x80);
                if (shortNameBytes.All(b => b == 0))
                {
                    s.Seek(0x180, SeekOrigin.Current);
                    continue;
                }

                string shortName     = Encoding.Unicode.GetString(shortNameBytes).TrimEnd('\0').Replace("\n", Environment.NewLine);
                string longName      = s.read(0x100, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                string publisherName = s.read(0x80, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);

                string key = combinePrefix(prefix, titleLanguages[i], true);
                info.addInfo(key + " short name", shortName);
                info.addInfo(key + " long name", longName);
                info.addInfo(key + " publisher name", publisherName);
            }

            byte[] ratings = s.read(16);
            NintendoCommon.parseRatings(info, ratings, true);

            int region = s.readIntLE();

            if (region == 0x7fffffff)
            {
                info.addInfo(combinePrefix(prefix, "Region"), "Region free");
            }
            else
            {
                info.addInfo(combinePrefix(prefix, "Region"), Enum.ToObject(typeof(RegionFlags), region).ToString());
            }

            //Stuff used in online connectivity
            byte[] matchMakerID    = s.read(4);
            byte[] matchMakerBitID = s.read(8);
            info.addInfo(combinePrefix(prefix, "Match maker ID"), matchMakerID, true);
            info.addInfo(combinePrefix(prefix, "Match maker BIT ID"), matchMakerBitID, true);

            int flags = s.readIntLE();

            info.addInfo(combinePrefix(prefix, "Visible on Home Menu"), (flags & 1) > 0);
            info.addInfo(combinePrefix(prefix, "Auto-boot"), (flags & 2) > 0);
            info.addInfo(combinePrefix(prefix, "Uses 3D"), (flags & 4) > 0);             //For parental controls use, doesn't actually stop an application using 3D
            info.addInfo(combinePrefix(prefix, "Requires EULA"), (flags & 8) > 0);
            info.addInfo(combinePrefix(prefix, "Autosave on exit"), (flags & 16) > 0);
            info.addInfo(combinePrefix(prefix, "Uses extended banner"), (flags & 32) > 0);
            info.addInfo(combinePrefix(prefix, "Region rating required"), (flags & 64) > 0);            //weh
            info.addInfo(combinePrefix(prefix, "Warn about save data"), (flags & 128) > 0);             //Just changes the warning when closing an application to "Do you want to close blah (Unsaved data will be lost.)"
            info.addInfo(combinePrefix(prefix, "Record application usage"), (flags & 256) > 0);         //This is unset on developer/customer service tools to stop them showing up in the activity log, apparently
            info.addInfo(combinePrefix(prefix, "Disable SD card save backup"), (flags & 1024) > 0);
            info.addInfo(combinePrefix(prefix, "New 3DS exclusive"), (flags & 4096) > 0);

            int eulaMajorVersion = s.read();
            int eulaMinorVersion = s.read();

            info.addInfo("EULA version", eulaMajorVersion + "." + eulaMinorVersion);

            s.Position = offset + 0x2034;
            byte[] cecID = s.read(4);
            info.addInfo("CEC (StreetPass) ID", cecID);

            s.Position = offset + 0x2040;
            byte[] iconData      = s.read(0x480);
            byte[] largeIconData = s.read(0x1200);
            info.addInfo("Small icon", decodeIcon(iconData, 24, 24));
            info.addInfo("Icon", decodeIcon(largeIconData, 48, 48));
        }
Esempio n. 14
0
        public static void parseNCCH(ROMInfo info, WrappedInputStream s, string prefix, long offset = 0)
        {
            //"NCCD" magic at 0x100
            s.Position = offset + 0x104;
            uint contentSize = (uint)s.readIntLE() * MEDIA_UNIT;

            info.addInfo(combinePrefix(prefix, "Content size"), contentSize, ROMInfo.FormatMode.SIZE);

            byte[] partitionID = s.read(8);
            info.addInfo(combinePrefix(prefix, "Partition ID"), partitionID);

            string makerCode = s.read(2, Encoding.ASCII);

            info.addInfo(combinePrefix(prefix, "Publisher"), makerCode, NintendoCommon.LICENSEE_CODES);

            short version = s.readShortLE();

            info.addInfo(combinePrefix(prefix, "NCCH version", true), version);             //Seemingly not quite the same as card version... always 2?

            //Some stuff about a hash

            s.Position = offset + 0x118;
            byte[] programID = s.read(8);
            info.addInfo(combinePrefix(prefix, "Program ID"), programID);

            //Some stuff about a reserved and a logo SHA-256
            s.Position = offset + 0x150;
            string productCode = s.read(16, Encoding.ASCII).TrimEnd('\0');             //Not just ABBC anymore! It's now CTR-P-ABBC... albeit that's 10 chars?

            info.addInfo(combinePrefix(prefix, "Product code"), productCode);
            if (productCode.Length == 10 && !productCode.Equals("CTR-P-CTAP"))
            {
                info.addInfo(combinePrefix(prefix, "Category"), productCode[4], CATEGORIES);
                info.addInfo(combinePrefix(prefix, "Type"), productCode[6], GAME_TYPES);
                info.addInfo(combinePrefix(prefix, "Short title"), productCode.Substring(7, 2));
                info.addInfo(combinePrefix(prefix, "Country"), productCode[9], NintendoCommon.COUNTRIES);
            }
            s.Position = offset + 0x180;
            int extendedHeaderSize = s.readIntLE();             //NOT in media units!

            info.addInfo(combinePrefix(prefix, "Extended header size"), extendedHeaderSize, ROMInfo.FormatMode.SIZE);

            //Something about a reserved and an extended header SHA-256

            s.Position = offset + 0x188;
            byte[] flags = s.read(8);

            bool isData             = (flags[5] & 1) > 0;
            bool isExecutable       = (flags[5] & 2) > 0;
            bool isCXI              = !(isData & !isExecutable);
            bool isSystemUpdate     = (flags[5] & 4) > 0;
            bool isElectronicManual = (flags[5] & 8) > 0;
            bool isTrial            = (flags[5] & 16) > 0;  //This isn't set on trials...
            bool isZeroKeyEncrypted = (flags[7] & 1) > 0;
            bool isDecrypted        = (flags[7] & 4) > 0;

            info.addInfo(combinePrefix(prefix, "Is CXI"), isCXI);
            info.addInfo(combinePrefix(prefix, "Is data"), isData);
            info.addInfo(combinePrefix(prefix, "Is executable"), isExecutable);
            info.addInfo(combinePrefix(prefix, "Is system update"), isSystemUpdate);
            info.addInfo(combinePrefix(prefix, "Is electronic manual"), isElectronicManual);             //TODO This just goes to show we should make some of this stuff extra if it's not the main thing ("Electronic manual is electronic manual" = true)
            info.addInfo(combinePrefix(prefix, "Is trial"), isTrial);
            info.addInfo(combinePrefix(prefix, "Is encrypted with 0 key"), isZeroKeyEncrypted);
            info.addInfo(combinePrefix(prefix, "Is decrypted"), isDecrypted);

            long plainRegionOffset = (uint)s.readIntLE() * MEDIA_UNIT + offset;
            long plainRegionSize   = (uint)s.readIntLE() * MEDIA_UNIT;
            long logoRegionOffset  = (uint)s.readIntLE() * MEDIA_UNIT + offset;
            long logoRegionSize    = (uint)s.readIntLE() * MEDIA_UNIT;
            long exeFSOffset       = (uint)s.readIntLE() * MEDIA_UNIT + offset;
            long exeFSSize         = (uint)s.readIntLE() * MEDIA_UNIT;

            s.Seek(8, SeekOrigin.Current);             //Skip over ExeFS hash region size and reserved
            long romFSOffset = (uint)s.readIntLE() * MEDIA_UNIT + offset;
            long romFSSize   = (uint)s.readIntLE() * MEDIA_UNIT;

            FilesystemDirectory partition = new FilesystemDirectory()
            {
                name = prefix ?? "Main partition"
            };

            info.addFilesystem(partition);

            if (plainRegionSize > 0)
            {
                info.addInfo(combinePrefix(prefix, "Plain region offset"), plainRegionOffset, ROMInfo.FormatMode.HEX);
                info.addInfo(combinePrefix(prefix, "Plain region size"), plainRegionSize, ROMInfo.FormatMode.SIZE);

                parsePlainRegion(info, s, prefix, plainRegionOffset);
            }

            if (exeFSSize > 0)
            {
                info.addInfo(combinePrefix(prefix, "ExeFS offset", true), exeFSOffset, ROMInfo.FormatMode.HEX);
                info.addInfo(combinePrefix(prefix, "ExeFS size", true), exeFSSize, ROMInfo.FormatMode.SIZE);

                if (isDecrypted)
                {
                    //If the ROM is encrypted, it'll be all garbled, so there's not much we can do there...
                    parseExeFS(info, s, partition, exeFSOffset);
                }
            }
            if (romFSSize > 0)
            {
                info.addInfo(combinePrefix(prefix, "RomFS offset", true), romFSOffset, ROMInfo.FormatMode.HEX);
                info.addInfo(combinePrefix(prefix, "RomFS size", true), romFSSize, ROMInfo.FormatMode.SIZE);

                if (isDecrypted)
                {
                    parseRomFS(info, s, partition, romFSOffset);
                }
            }

            var icon = getIconFile(partition);

            if (icon != null)
            {
                parseSMDH(info, s, prefix, icon.offset);
            }

            if (isCXI & isDecrypted)
            {
                s.Position = offset + 0x200;
                string appTitle = s.read(8, Encoding.ASCII).TrimEnd('\0');
                info.addInfo(combinePrefix(prefix, "Internal name"), appTitle);

                s.Position = offset + 0x20d;
                int extendedFlags = s.read();
                info.addInfo(combinePrefix(prefix, "Compressed ExeFS code"), (extendedFlags & 1) > 0, true);
                info.addInfo(combinePrefix(prefix, "SD application", true), (extendedFlags & 2) > 0, true);

                short remasterVersion = s.readShortLE();
                info.addInfo(combinePrefix(prefix, "Remaster version"), remasterVersion, true);

                s.Position = offset + 0x450;
                //This makes no sense - it should be at offset + 0x240 according to documentation, but it isn't
                var dependencyList = new List <string>();
                for (int i = 0; i < 48; ++i)
                {
                    string dependency = s.read(8, Encoding.ASCII).TrimEnd('\0');
                    if (dependency.Length == 0)
                    {
                        break;
                    }
                    dependencyList.Add(dependency);
                }
                info.addInfo("Dependencies", String.Join(", ", dependencyList), true);

                s.Position = offset + 0x3c0;
                //TODO: Add readLongLE and readLongBE to WrappedInputStream
                //This wouldn't work if you used this on a big endian C# environment I would think
                ulong saveDataSize = BitConverter.ToUInt64(s.read(8), 0);
                info.addInfo("Save size", saveDataSize, ROMInfo.FormatMode.SIZE);

                //TODO: Access control info stuff https://www.3dbrew.org/wiki/NCCH/Extended_Header
            }
        }
Esempio n. 15
0
        int scoreHeader(WrappedInputStream s, long offset)
        {
            //Well, this is a fun one. It's adapted from MAME devices/bus/snes/snes_slot.cpp (snes_validate_infoblock(), to be precise), which doesn't have much license
            //information, except for this:
            // license:BSD-3-Clause
            // copyright-holders:Fabio Priuli,Cowering
            //So that's why I put that BSD header up there, and I hope that satisfies everything license wise, and that no lawyers will hunt me down and kill me
            //Anyway, there's basically no other way to do this. It seems to just be what every single SNES emulator does
            //I don't even know what the heck is going on down here

            if (s.Length < offset)
            {
                //Well I added this check here at least, because if the ROM isn't even that big then
                //there's a good chance that offset isn't valid
                return(0);
            }

            int score = 0;

            s.Position = offset + 0x3c;
            ushort resetVector = (ushort)s.readShortLE();

            s.Position = offset + 0x1c;
            ushort inverseChecksum = (ushort)s.readShortLE();
            ushort checksum        = (ushort)s.readShortLE();

            long resetOpcodeOffset = ((uint)(offset & -0x7fff)) | (ushort)(resetVector & 0x7ffff);

            s.Position = resetOpcodeOffset;
            int resetOpcode = s.read();

            s.Position = offset + 0x15;
            int mapper = s.read() & ~0x10;

            if (resetVector < 0x8000)
            {
                return(0);
            }

            if ((new int[] { 0x78, 0x18, 0x38, 0x9c, 0x4c, 0x5c }.Contains(resetOpcode)))
            {
                score += 8;
            }

            if (new int[] { 0xc2, 0xe2, 0xad, 0xae, 0xac, 0xaf, 0xa9, 0xa2, 0xa0, 0x20, 0x22 }.Contains(resetOpcode))
            {
                score += 4;
            }

            if (new int[] { 0x40, 0x60, 0x6b, 0xcd, 0xec, 0xcc }.Contains(resetOpcode))
            {
                score -= 4;
            }

            if (new int[] { 0x00, 0x02, 0xdb, 0x42, 0xff }.Contains(resetOpcode))
            {
                score -= 8;
            }

            //Okay now that we aren't talking opcodes I can at least make sense of this part
            //Here we check that the checksum and inverse checksum add up, because they're meant to do that (although I guess some unlicensed carts might not)
            //Anyway, if you actually wanted to make sense of this function, you'd just look at MAME anyway instead of my amateur explanations
            if ((checksum + inverseChecksum) == 0xffff && (checksum != 0) && (inverseChecksum != 0))
            {
                score += 4;
            }

            if (offset == 0x7fc0 && (mapper == 0x20 || mapper == 0x22))
            {
                score += 2;
            }
            if (offset == 0xffc0 && mapper == 0x21)
            {
                score += 2;
            }
            if (offset == 0x40ffc0 && mapper == 0x25)
            {
                score += 2;
            }

            s.Position = offset + 0x16;
            if (s.read() < 8)
            {
                //Check if ROM type is a normal value
                score++;
            }
            if (s.read() < 16)
            {
                //Check if ROM size is a normal value (what even is normal anyway the SNES is weird)
                score++;
            }
            if (s.read() < 8)
            {
                //Check if SRAM size is a normal value
                score++;
            }
            if (s.read() < 14)
            {
                //Check if region is normal
                score++;
            }
            if (s.read() == 0x33)
            {
                //Check if extended header is used, because it so commonly is
                score += 2;
            }

            return(score < 0 ? 0 : score);
        }
Esempio n. 16
0
        public static void parseBSHeader(WrappedInputStream s, ROMInfo info, long offset)
        {
            s.Position = offset;

            string name = s.read(16, MainProgram.shiftJIS).TrimEnd('\0', ' ');

            info.addInfo("Internal name", name);

            byte[] blockAllocation = s.read(4);
            info.addInfo("Block allocation flags", blockAllocation, true);             //TODO (at the moment this confuzzles me)

            ushort limitedStarts = (ushort)s.readShortLE();

            if ((limitedStarts & 0x8000) > 0)
            {
                info.addInfo("Boots left", "Unlimited");
            }
            else
            {
                info.addInfo("Boots left", limitedStarts, SATELLAVIEW_BOOTS_LEFT);
            }

            byte[] date  = s.read(2);
            int    month = (date[0] & 0b11110000) >> 4;
            int    day   = (date[1] & 0b11111000) >> 3;

            info.addInfo("Month", (month != 0 && month < 13) ? System.Globalization.DateTimeFormatInfo.CurrentInfo.GetMonthName(month) : String.Format("Unknown ({0})", month));
            info.addInfo("Day", day);

            int romType = s.read();

            info.addInfo("Mapper", romType, ROM_LAYOUTS);

            int flags = s.read();

            info.addInfo("SoundLink enabled", (flags & 16) == 0);
            int executionArea = (flags & 96) >> 5;

            info.addInfo("Execution area", executionArea);
            info.addInfo("Skip intro", (flags & 128) > 0);

            int fakeLicensee = s.read();
            //Always 0x33

            int version = s.read();

            info.addInfo("Version", version);
            //superfamicom.org says: Version Number is an extension. Actual format is 1 + ord(val($ffdb))/10. (what the heckie)

            short checksum        = s.readShortLE();
            short inverseChecksum = s.readShortLE();

            info.addInfo("Checksum", checksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Inverse checksum", inverseChecksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Checksums add up?", checksum + inverseChecksum == 0xffff);
            //TODO calculate checksum

            byte[] unknown = s.read(4);
            info.addInfo("Unknown", unknown, true);

            s.Position = offset - 16;
            string licensee = s.read(2, Encoding.ASCII);

            info.addInfo("Publisher", licensee, NintendoCommon.LICENSEE_CODES);

            //Is there a product code in here? Who knows
            info.addInfo("Unknown 2", s.read(8), true);
        }
Esempio n. 17
0
        public static void parseBanner(ROMInfo info, WrappedInputStream s, long bannerOffset)
        {
            s.Position = bannerOffset;
            short bannerVersion = s.readShortLE();

            info.addInfo("Banner version", bannerVersion, BANNER_VERSIONS);
            if (BANNER_VERSIONS.ContainsKey(bannerVersion))
            {
                short bannerChecksum  = s.readShortLE();                //CRC16 of 0x20 to 0x83f (icons and titles)
                short bannerChecksum2 = s.readShortLE();                //CRC16 of 0x20 to 0x93f (icons and titles including Chinese)
                short bannerChecksum3 = s.readShortLE();                //CRC16 of 0x20 to 0xa3f (icons and titles including Chinese + Korea)
                short bannerChecksum4 = s.readShortLE();                //CRC16 of 0x1240 to 0x23bf (i.e. DSi animated icon)

                info.addInfo("Banner checksum", bannerChecksum, ROMInfo.FormatMode.HEX, true);
                short calculatedBannerChecksum = calcCRC16(s, bannerOffset + 0x20, bannerOffset + 0x83f);
                info.addInfo("Calculated banner checksum", calculatedBannerChecksum, ROMInfo.FormatMode.HEX, true);
                info.addInfo("Banner checksum valid?", bannerChecksum == calculatedBannerChecksum);

                if (bannerVersion >= 2)
                {
                    info.addInfo("Banner checksum 2", bannerChecksum2, ROMInfo.FormatMode.HEX, true);
                    short calculatedBannerChecksum2 = calcCRC16(s, bannerOffset + 0x20, bannerOffset + 0x93f);
                    info.addInfo("Calculated banner checksum 2", calculatedBannerChecksum2, ROMInfo.FormatMode.HEX, true);
                    info.addInfo("Banner checksum 2 valid?", bannerChecksum2 == calculatedBannerChecksum2);
                }

                if (bannerVersion >= 3)
                {
                    info.addInfo("Banner checksum 3", bannerChecksum3, ROMInfo.FormatMode.HEX, true);
                    short calculatedBannerChecksum3 = calcCRC16(s, bannerOffset + 0x20, bannerOffset + 0xa3f);
                    info.addInfo("Calculated banner checksum 3", calculatedBannerChecksum3, ROMInfo.FormatMode.HEX, true);
                    info.addInfo("Banner checksum 3 valid?", bannerChecksum3 == calculatedBannerChecksum3);
                }

                if (bannerVersion >= 0x103)
                {
                    info.addInfo("Banner checksum 4", bannerChecksum4, ROMInfo.FormatMode.HEX, true);
                    short calculatedBannerChecksum4 = calcCRC16(s, bannerOffset + 0x1240, bannerOffset + 0x23bf);
                    info.addInfo("Calculated banner checksum 4", calculatedBannerChecksum4, ROMInfo.FormatMode.HEX, true);
                    info.addInfo("Banner checksum 4 valid?", bannerChecksum4 == calculatedBannerChecksum4);
                }

                byte[] bannerReserved = s.read(0x16);                 //Should be zero filled
                info.addInfo("Banner reserved", bannerReserved, true);

                byte[] iconBitmap  = s.read(0x200);
                byte[] iconPalette = s.read(0x20);
                info.addInfo("Icon", decodeDSIcon(iconBitmap, iconPalette));

                string japaneseTitle = s.read(256, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                info.addInfo("Japanese title", japaneseTitle);
                string englishTitle = s.read(256, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                info.addInfo("English title", englishTitle);
                string frenchTitle = s.read(256, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                info.addInfo("French title", frenchTitle);
                string germanTitle = s.read(256, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                info.addInfo("German title", germanTitle);
                string italianTitle = s.read(256, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                info.addInfo("Italian title", italianTitle);
                string spanishTitle = s.read(256, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                info.addInfo("Spanish title", spanishTitle);
                if (bannerVersion >= 2)
                {
                    string chineseTitle = s.read(256, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                    info.addInfo("Chinese title", chineseTitle);
                }
                if (bannerVersion >= 3)
                {
                    string koreanTitle = s.read(256, Encoding.Unicode).TrimEnd('\0').Replace("\n", Environment.NewLine);
                    info.addInfo("Korean title", koreanTitle);
                }

                //Should be zero filled, but it's not
                byte[] titleReserved = s.read(0x800);

                if (bannerVersion >= 0x103)
                {
                    //Same format as DS icon, but animated
                    IList <byte[]> dsiIconBitmaps = new List <byte[]>();
                    for (int i = 0; i < 8; ++i)
                    {
                        dsiIconBitmaps.Add(s.read(0x200));
                    }
                    IList <byte[]> dsiIconPalettes = new List <byte[]>();
                    for (int i = 0; i < 8; ++i)
                    {
                        dsiIconPalettes.Add(s.read(0x20));
                    }

                    for (int i = 0; i < 64; ++i)
                    {
                        ushort sequence = (ushort)s.readShortLE();
                        if (sequence == 0)
                        {
                            break;
                        }

                        bool flipVertically   = (sequence & 0x8000) > 0;
                        bool flipHorizontally = (sequence & 0x4000) > 0;

                        int paletteIndex = (sequence & 0x3800) >> 11;
                        int bitmapIndex  = (sequence & 0x700) >> 8;

                        Bitmap frame = decodeDSIcon(dsiIconBitmaps[bitmapIndex], dsiIconPalettes[paletteIndex]);
                        if (flipHorizontally)
                        {
                            frame.RotateFlip(RotateFlipType.RotateNoneFlipX);
                        }
                        if (flipVertically)
                        {
                            frame.RotateFlip(RotateFlipType.RotateNoneFlipY);
                        }
                        info.addInfo("DSi icon frame " + (i + 1), frame, true);

                        //Duration: sequence & 0xff (in 60Hz units, so I guess how many frames this lasts at 60fps?) but we have no method of storing that at the moment
                    }
                }
            }
        }
Esempio n. 18
0
        public static void parseXBE(ROMInfo info, WrappedInputStream s)
        {
            string magic = s.read(4, Encoding.ASCII);

            if (!"XBEH".Equals(magic))
            {
                info.addInfo("Detected format", "Unknown");
                return;
            }

            info.addInfo("Detected format", "XBE");

            byte[] signature = s.read(256);
            info.addInfo("Signed", signature.Any(b => b != 0));

            s.Position = 0x104;
            int baseAddress = s.readIntLE();

            info.addInfo("Base address", baseAddress, ROMInfo.FormatMode.HEX, true);

            int headerSize      = s.readIntLE();
            int imageSize       = s.readIntLE();
            int imageHeaderSize = s.readIntLE();

            info.addInfo("Header size", headerSize, true);
            info.addInfo("Image size", imageSize, true);
            info.addInfo("Image header size", imageHeaderSize, true);

            DateTime xbeDate = convertWindowsDate(s.readIntLE());

            //Presumably, this is the same format as the timestamp in Windows PE executables
            info.addInfo("XBE date", xbeDate);
            info.addInfo("XBE year", xbeDate.Year);
            info.addInfo("XBE month", System.Globalization.DateTimeFormatInfo.CurrentInfo.GetMonthName(xbeDate.Month));
            info.addInfo("XBE day", xbeDate.Day);

            int certificateOffset = s.readIntLE() - baseAddress;

            info.addInfo("Certificate offset", certificateOffset, ROMInfo.FormatMode.HEX, true);

            int sectionCount   = s.readIntLE();
            int sectionsOffset = s.readIntLE() - baseAddress;

            info.addInfo("Number of sections", sectionCount, true);
            info.addInfo("Address of sections", sectionsOffset, ROMInfo.FormatMode.HEX, true);

            int initFlags = s.readIntLE();

            info.addInfo("Initialization flags", initFlags, true);

            int  entryPoint      = s.readIntLE();
            uint debugEntryPoint = (uint)(entryPoint ^ 0x94859d4b) - (uint)baseAddress;

            if (debugEntryPoint <= s.Length)
            {
                info.addInfo("Entry point", debugEntryPoint, ROMInfo.FormatMode.HEX, true);
                info.addInfo("Is debug", true);
            }
            else
            {
                //I'm gonna be real, all I have on me is homebrew and prototypes, so this could be all completely wrong
                uint retailEntryPoint = (uint)(entryPoint ^ 0xa8fc57ab) - (uint)baseAddress;
                info.addInfo("Entry point", retailEntryPoint, ROMInfo.FormatMode.HEX, true);
                info.addInfo("Is debug", false);
            }

            if (certificateOffset > 0 && certificateOffset < s.Length)
            {
                s.Position = certificateOffset;
                int certificateSize = s.readIntLE();
                info.addInfo("Certificate size", certificateSize, true);

                DateTime certDate = convertWindowsDate(s.readIntLE());
                info.addInfo("Date", certDate);
                info.addInfo("Year", certDate.Year);
                info.addInfo("Month", System.Globalization.DateTimeFormatInfo.CurrentInfo.GetMonthName(certDate.Month));
                info.addInfo("Day", certDate.Day);
                //Not sure what the difference is between this and the XBE date, but sometimes it's different, most of the time not
                //Anyway, when it is different it's always later, so let's go with this as the definitive date

                //Supposedly, this stuff is printed onto retail discs
                short titleID = s.readShortLE();
                info.addInfo("Title ID", titleID);                                                      //Could be a product code? I don't know, to be honest
                string maker = new string(s.read(2, Encoding.ASCII).ToCharArray().Reverse().ToArray()); //It's... backwards. Don't ask me why. Endian weirdness most likely
                info.addInfo("Publisher", maker, MicrosoftCommon.LICENSEE_CODES);

                string name = s.read(80, Encoding.Unicode);
                info.addInfo("Internal name", name);

                byte[] altTitleIDS = s.read(64);
                info.addInfo("Alt IDs", altTitleIDS, true);                 //Usually blank

                int allowedMedia = s.readIntLE();
                info.addInfo("Allowed on hard disk", (allowedMedia & 1) > 0, true);
                info.addInfo("Allowed on DVD X2", (allowedMedia & 2) > 0, true);
                info.addInfo("Allowed on DVD CD", (allowedMedia & 4) > 0, true);
                info.addInfo("Allowed on CD", (allowedMedia & 8) > 0, true);
                info.addInfo("Allowed on DVD", (allowedMedia & 16) > 0, true);
                info.addInfo("Allowed on DVD DL", (allowedMedia & 32) > 0, true);
                info.addInfo("Allowed on DVD-RW", (allowedMedia & 64) > 0, true);
                info.addInfo("Allowed on DVD-RW DL", (allowedMedia & 128) > 0, true);
                info.addInfo("Allowed on dongle", (allowedMedia & 256) > 0, true);
                info.addInfo("Allowed on media board", (allowedMedia & 512) > 0, true);

                int region = s.readIntLE();
                //TODO Make this look much nicer
                info.addInfo("Region", Enum.ToObject(typeof(XboxRegions), region).ToString());

                byte[] ratings = s.read(4);
                info.addInfo("Ratings", ratings, true);
                //This is where it'd be useful if I could dump a physical disk..
                //Metal Arms prototype has 30-00-00-00 here
                //farbrausch (demo by Limp Ninja) has 40-00-00-00 here
                //The rest I have are just prototypes with either 00-00-00-00 or FF-FF-FF-FF

                int discNumber = s.readIntLE();
                info.addInfo("Disc number", discNumber);

                int version = s.readIntLE();
                info.addInfo("Version", version);
            }
        }
Esempio n. 19
0
        public override void addROMInfo(ROMInfo info, ROMFile file, WrappedInputStream stream)
        {
            //Note! This is track 2! Right now this fuckiness just lets you choose whatever track, or the first data track, but like... the thing is on track 2, okay? God damn I need to rewrite this whole entire damn thing

            info.addInfo("Platform", "PC-FX");             //TODO: Can we detect PC-FXGA, which isn't forwards compatible? It doesn't seem to be different in any obvious way so far

            //Don't like this hack, it looks like really I should be taking into account this INDEX 01 00:03:00 business in the cue sheet, but I don't
            //What the cool kids (i.e. Mednafen) seem to do, who probably know what they are doing, is to look through every single track and then look through every single sector looking for the thing
            //So I guess I'll have to do that, except how this works is that it's done on each individual track, so yeah
            //The thing

            long position = 0;

            while (true)
            {
                stream.Seek(position, System.IO.SeekOrigin.Begin);
                var magic = stream.read(16, Encoding.ASCII);
                if (magic.Length < 16)
                {
                    return;
                }
                if ("PC-FX:Hu_CD-ROM".Equals(magic.Substring(0, 15)))
                {
                    break;
                }
                position += 2048;
                if (position > stream.Length)
                {
                    return;
                }
            }

            var bootCode = stream.read(0x7f0);

            info.addInfo("Boot code", bootCode, true);

            var title = stream.read(32, MainProgram.shiftJIS).TrimEnd('\0');

            info.addInfo("Internal name", title);

            //Skip over sect_off, sect_count, prog_off, prog_point (all uint32) because I dunno what those do, and I'm not sure if I need to
            stream.Seek(4 + 4 + 4 + 4, System.IO.SeekOrigin.Current);

            var makerID = stream.read(4);

            info.addInfo("Maker ID", makerID);
            info.addInfo("Maker ID as ASCII", Encoding.ASCII.GetString(makerID).TrimEnd('\0'));             //Usually ASCII but I've also seen 05-00-00-00 which is not. Anyway, these are kinda interesting, and I should build a datamabase of them

            var makerName = stream.read(60, MainProgram.shiftJIS).TrimEnd('\0');

            info.addInfo("Publisher", makerName);

            var volumeNumber = stream.readIntLE();             //Disc number? Is 1 on official titles

            info.addInfo("Volume number", volumeNumber);

            var version = stream.readShortLE();

            info.addInfo("Version", version);             //Seems to be 256, except for Battle Heat where it's 257. Could be 0x0101 = v1.01? That's like... BCD I guess

            var country = stream.readShortLE();

            info.addInfo("Country", country);             //pcfx-cdlink defaults to 1. Anyway, since it was only released in Japan, official games only have the 1 country of... 1, which I guess means Japan

            var date = stream.read(8, Encoding.ASCII).TrimEnd('\0');

            info.addInfo("Date", date);             //TODO decode; YYYYMMDD format (D is single digit in a homebrew test program, so I guess I could either not care entirely, or just get the year and month if length != 8 after strip)

            //Then there's 0x380 bytes of "pad" and 0x400 bytes of "udata"
        }