Esempio n. 1
0
        public static void parseCCS64Cart(ROMInfo info, WrappedInputStream s)
        {
            s.Position = 0x10;
            int headerLength = s.readIntBE();

            info.addInfo("Header size", headerLength);

            short version = s.readShortBE();

            info.addInfo("Version", version);             //Is this actually the kind of version I'm thinking of, or is it more like the header version?

            short cartType = s.readShortBE();

            info.addInfo("Type", cartType, CARTRIDGE_TYPES);
            info.addInfo("Platform", cartType == 15 ? "Commodore 64GS" : "Commodore 64");

            int exromLineStatus = s.read();

            info.addInfo("EXROM line status", exromLineStatus == 0 ? "Active" : "Inactive");

            int gameLineStatus = s.read();

            info.addInfo("Game line status", gameLineStatus == 0 ? "Active" : "Inactive");

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

            string name = s.read(32, Encoding.ASCII).TrimEnd('\0');

            info.addInfo("Internal name", name);
            //TODO Read CHIP packets (not entirely trivial as there can be more than one)
        }
Esempio n. 2
0
        public static int calcChecksum(WrappedInputStream s)
        {
            long pos = s.Position;
            long len = s.Length;

            try {
                s.Position = 0x200;
                int checksum = 0;
                while (s.Position < len)
                {
                    checksum = (checksum + s.readShortBE()) & 0xffff;
                }
                return(checksum);
            } finally {
                s.Position = pos;
            }
        }
Esempio n. 3
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. 4
0
        public static void parseMegadriveROM(ROMInfo info, WrappedInputStream s, bool isCD = false)
        {
            s.Position = 0x100;

            //While we're here, it should be noted that some allegedly official licensed and final don't have correct header information
            //of any kind, and as such you will see plain garbage all across here. Even the console name, which results in them not being
            //able to boot on real Model 2 and Model 3 hardware. Point at them and laugh.
            //These games are: Zany Golf (rev 0) and Budokan: The Martial Spirit, among others which aren't actually licensed.
            //Anyway, I just thought that was interesting enough to comment on. I found it fascinating. Am I just kinda weird? Oh well.

            string consoleName = s.read(16, Encoding.ASCII).TrimEnd('\0', ' ');

            info.addInfo("Console name", consoleName);
            info.addInfo("Platform", getPlatformFromConsoleName(consoleName.TrimStart(' '), isCD));

            string copyright = s.read(16, Encoding.ASCII).TrimEnd('\0', ' ');

            info.addInfo("Copyright", copyright);
            var matches = copyrightRegex.Match(copyright);

            if (matches.Success)
            {
                string maker = matches.Groups[1].Value?.Trim().TrimEnd(',');
                maker = Regex.Replace(maker, "^T-0", "T-");
                maker = Regex.Replace(maker, "^T(?!-)", "T-");

                info.addInfo("Publisher", maker, SegaCommon.LICENSEES);
                info.addInfo("Year", matches.Groups[2].Value);
                if (MONTH_ABBREVIATIONS.TryGetValue(matches.Groups[3].Value?.ToUpper(), out int month))
                {
                    info.addInfo("Month", System.Globalization.DateTimeFormatInfo.CurrentInfo.GetMonthName(month));
                }
                else
                {
                    info.addInfo("Month", String.Format("Unknown ({0})", matches.Groups[3].Value));
                }
            }

            string domesticName = s.read(48, MainProgram.shiftJIS).TrimEnd('\0', ' ');

            info.addInfo("Internal name", domesticName);
            string overseasName = s.read(48, MainProgram.shiftJIS).TrimEnd('\0', ' ');

            info.addInfo("Overseas name", overseasName);
            string productType = s.read(2, Encoding.ASCII);

            info.addInfo("Type", productType, PRODUCT_TYPES);

            s.read();             //Space for padding
            string serialNumber = s.read(8, Encoding.ASCII).TrimEnd('\0', ' ');

            info.addInfo("Product code", serialNumber);
            s.read();             //- for padding
            string version = s.read(2, Encoding.ASCII);

            info.addInfo("Version", version);

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

            info.addInfo("Checksum", checksum, ROMInfo.FormatMode.HEX, true);
            if (!isCD)
            {
                int calculatedChecksum = calcChecksum(s);
                info.addInfo("Calculated checksum", calculatedChecksum, ROMInfo.FormatMode.HEX, true);
                info.addInfo("Checksum valid?", checksum == calculatedChecksum);
            }

            char[] ioSupportList = s.read(16, Encoding.ASCII).ToCharArray().Where((c) => c != ' ' && c != '\0').ToArray();
            info.addInfo("Compatible peripherals", ioSupportList, IO_SUPPORT);

            int romStart = s.readIntBE();

            info.addInfo("ROM start", romStart, ROMInfo.FormatMode.HEX, true);
            int romEnd = s.readIntBE();

            info.addInfo("ROM end", romEnd, ROMInfo.FormatMode.HEX, true);
            info.addInfo("ROM size", romEnd - romStart, ROMInfo.FormatMode.SIZE);
            int ramStart = s.readIntBE();

            info.addInfo("RAM start", ramStart, ROMInfo.FormatMode.HEX, true);
            int ramEnd = s.readIntBE();

            info.addInfo("RAM end", ramEnd, ROMInfo.FormatMode.HEX, true);
            info.addInfo("RAM size", ramEnd - ramStart, ROMInfo.FormatMode.SIZE);
            byte[] backupRamID = s.read(4);
            info.addInfo("Backup RAM ID", backupRamID);
            int backupRamStart = s.readIntBE();

            info.addInfo("Backup RAM start", backupRamStart, ROMInfo.FormatMode.HEX, true);
            int backupRamEnd = s.readIntBE();

            info.addInfo("Backup RAM end", backupRamEnd, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Save size", backupRamEnd - backupRamStart, ROMInfo.FormatMode.SIZE);

            string modemData = s.read(12, Encoding.ASCII).TrimEnd(' ');

            info.addInfo("Modem data", modemData);
            //If entirely spaces, modem not supported
            //Should be an ASCII string in the format MO<company><modem no#>.<version>, padding with spaces as needed
            //Not really seen often, only seen so far in:
            //Advanced Daisenryaku: Deutsch Dengeki Sakusen Mar 7 1991 prototype: "MOSEGA05,010"
            //Sorcer Kingdom Nov 8 1991 prototype: "MO(C)T-25"
            //Ma Jiang Qing Ren: Ji Ma Jiang Zhi (bootleg): Quite literally "MOxxxxyy.z"
            //Game Toshokan, MegaMind: "MOSEGA03.000"

            string memo = s.read(40, Encoding.ASCII).TrimEnd('\0', ' ');

            info.addInfo("Memo", memo);
            char[] countries = s.read(3, Encoding.ASCII).ToCharArray().Where((c) => c != ' ' && c != '\0').ToArray();
            info.addInfo("Country", countries, COUNTRY);
        }
Esempio n. 5
0
        public override string name => "APF-MP1000";         //Or is it "APF Microcomputer System"? I don't f****n know

        public static void parseAPFCart(ROMInfo info, WrappedInputStream s)
        {
            //First byte should always be 0xbb, and I should check that, but ehhhhh
            s.Position = 1;
            ushort menuString = (ushort)(((ushort)s.readShortBE()) - 0x8000);
            int    numChoices = s.read();

            info.addInfo("Number of menu choices", numChoices);

            //Next byte must be 0, not very interesting I know
            s.Position = menuString;

            //Basically this just writes a giant string, but the screen is 32 chars wide (in text mode, which the game menus are; 16 lines long if you care) so it wraps there
            List <byte> bytes = new List <byte>();

            while (true)
            {
                int b = s.read();
                if (b == -1 || b == 255)
                {
                    //255 indicates end of string, but end of file would imply end of string too (or would that just crash or do bad things on a real cart that didn't terminate its string? Oh well)
                    break;
                }

                if (b < 0x80)
                {
                    bytes.Add((byte)b);
                }
                else if (b > 0xc0)
                {
                    //Heckin control characters
                    //0x80 to 0xbf are ignored (and 0xc0 would write 0 characters)
                    byte fillByte;
                    if (b >= 0xe0)
                    {
                        fillByte = 0x20;
                    }
                    else
                    {
                        int nextByte = s.read();
                        if (b == -1)
                        {
                            break;
                        }

                        fillByte = (byte)nextByte;
                        if (fillByte > 0x7f)
                        {
                            //These are actually coloured drawing shapes, but we don't really have those in 2018, so let's just pretend they're spaces
                            //Note that 0x60 will appear as a light green square and not `
                            fillByte = 0x20;
                        }
                    }

                    int num = b & 0x1f;

                    for (int i = 0; i < num; ++i)
                    {
                        bytes.Add(fillByte);
                    }
                }
            }

            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < bytes.Count; i += 32)
            {
                var line = bytes.GetRange(i, 32).ToArray();
                sb.AppendLine(Encoding.ASCII.GetString(line));                 //Well it's not quite ASCII but still... okay yeah I should fix that
            }
            //Actual character set:
            //0x00 - 0x3f: Light green text inside dark green squares
            //@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] (up) (left) (space) ! " # $ % & * ( ) <diamond> + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
            //0x40 - 0x7f: That but dark green text inside light green squares (hence 0x60, being a space, appears as a light green square)
            //0x80 - 0xff (for fill bytes): Drawing characters (coloured 4x4 grid on black background):
            //	High nibble: 8 = green, 9 = yellow, a = blue, b = red, c = white, d = cyan, e = magenta, f = orange
            //	Low nibble: 0 = not filled in (black square), 1 = lower right corner, 2 = lower left, 3 = lower, 4 = upper right, 5 = right, 6 = upper right and lower left, 7 = all except upper left, 8 = upper left, 9 = upper left and lower right, a = left, b = all except upper right, c = upper, d = all except lower left, e = all except lower right, f = filled in completely
            //
            //It's just lucky this means the dark-on-light alphabet and the light-on-dark numbers match up with ASCII, and the cartridges tend to do that
            //TODO Do that thing (can't be bothered implementing a character map thing right now)

            info.addInfo("Menu text", sb.ToString());
            //Hmmmmm might be cool to parse this to get the main names of the games inside the cartridge (if any), and the menu options? But that requires making some assumptions on how developers like to format their games, which is only _sort of_ consistent with the official carts
        }
Esempio n. 6
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            WrappedInputStream f = file.stream;

            f.Seek(-4, SeekOrigin.End);
            string magic = f.read(4, Encoding.ASCII);
            bool   isGBX = false;

            if ("GBX!".Equals(magic))
            {
                //See also: http://hhug.me/gbx/1.0
                isGBX = true;
                f.Seek(-16, SeekOrigin.End);
                int footerSize   = f.readIntBE();
                int majorVersion = f.readIntBE();
                int minorVersion = f.readIntBE();
                info.addInfo("GBX footer size", footerSize, ROMInfo.FormatMode.SIZE);
                info.addInfo("GBX version", String.Format("{0}.{1}", majorVersion, minorVersion));

                if (majorVersion == 1)
                {
                    f.Seek(-footerSize, SeekOrigin.End);
                    string mapperID = f.read(4, Encoding.ASCII).TrimEnd('\0');
                    CartAdditionalHardware hardware = CartAdditionalHardware.None;
                    if (f.read() == 1)
                    {
                        hardware |= CartAdditionalHardware.Battery;
                    }
                    if (f.read() == 1)
                    {
                        hardware |= CartAdditionalHardware.Rumble;
                    }
                    if (f.read() == 1)
                    {
                        hardware |= CartAdditionalHardware.RTC;
                    }
                    info.addInfo("Mapper", mapperID, GBX_MAPPERS);
                    addCartHardwareInfo(info, hardware, false);

                    int unused = f.read();
                    info.addInfo("GBX unused", unused, true);

                    int gbxRomSize = f.readIntBE();
                    int gbxRamSize = f.readIntBE();
                    info.addInfo("ROM size", gbxRomSize, ROMInfo.FormatMode.SIZE);
                    info.addInfo("Save size", gbxRamSize, ROMInfo.FormatMode.SIZE);

                    byte[] gbxFlags = f.read(32);
                    info.addInfo("GBX flags", gbxFlags, true);
                }
            }

            f.Position = 0x100;

            byte[] startVector = f.read(4);
            info.addInfo("Entry point", startVector, true);
            byte[] nintendoLogo = f.read(48);
            info.addInfo("Nintendo logo", nintendoLogo, true);
            info.addInfo("Nintendo logo valid?", isNintendoLogoEqual(nintendoLogo));

            bool isCGB;

            //Hoo boy this is gonna be tricky hold my... I don't have a beer right now
            byte[] title = f.read(16);
            //This gets tricky because only early games use the full 16 characters and then
            //at some point the last byte became the CGB flag, and then afterwards 4 characters
            //became the product code leaving only 11 characters for the title and there's not
            //really a 100% accurate heuristic to detect if the game uses 11 or 15 characters
            //Most emulators and whatnot use 11 if the game uses a new licensee code and 15
            //otherwise, but stuff like Pokemon Yellow and Gameboy Camera disprove that theory
            int titleLength = 16;

            //At least we can reliably detect if the game uses a CGB flag or not because the only
            //two valid values aren't valid inside titles
            if (CGB_FLAGS.ContainsKey(title[15]))
            {
                isCGB       = title[15] != 0;
                titleLength = 15;
                info.addInfo("Is colour", title[15], CGB_FLAGS);
                info.addInfo("Platform", title[15] == 0xc0 ? "Game Boy Color" : "Game Boy");
                //Here's the tricky part... well, we know that any game old enough to not
                //have a CGB flag isn't going to have a product code either, because those are new
                //and also I looked at every single commercially released GB/GBC ROM I have to figure out
                //what works and what doesn't
                //We also know that any game that uses the _old_ licensee code _isn't_ going to have a
                //product code, but a game that uses the new licensee code might have a product code and
                //also might not as previously mentioned
                //We can also see that any game that is exclusive to the Game Boy Color will have a
                //product code - but not necessarily a game that is merely GBC enhanced but GB compatible
                //With that in mind... for now, I'll only use 11 characters + product code if I know for sure it has one

                //The other way would be to check if there are extra characters beyond the first null character, because
                //you're not allowed to have a null character in the middle of a title so if I see characters after that, then
                //it's the manufacturer code (which is always in the same place)
                //So if the null character appears inside the 11 bytes, then it definitely ends the string, and then we can
                //just check to see if there's a manufacturer code afterwards
                int lastNullCharIndex = Array.IndexOf(title, 0);
                if (title[15] == 0xc0 || ((lastNullCharIndex != -1 && lastNullCharIndex <= 11) && title[14] != 0))
                {
                    titleLength = 11;
                    string productCode = Encoding.ASCII.GetString(title, 11, 4);
                    info.addInfo("Product code", productCode);
                    //No documentation I've found at all knows what the product type means! It looks like it works the same way
                    //as GBA, right down to V being the product type for rumble-enabled games and Kirby's Tilt and Tumble
                    //using K. How about that?
                    //Anyway yeah it all works out except for homebrews that stuff up my heuristic and don't really have
                    //product codes, and Robopon games which have a H for game type? That probably means something involving their infrared stuff
                    char gameType = productCode[0];
                    info.addInfo("Type", gameType, GBA.GBA_GAME_TYPES);
                    string shortTitle = productCode.Substring(1, 2);
                    info.addInfo("Short title", shortTitle);
                    char country = productCode[3];
                    info.addInfo("Country", country, NintendoCommon.COUNTRIES);
                }
            }
            else
            {
                info.addInfo("Platform", "Game Boy");
                isCGB = false;
                info.addInfo("Is colour", false);
            }

            //Now we can add what's left of the title
            info.addInfo("Internal name", Encoding.ASCII.GetString(title, 0, titleLength).TrimEnd('\0', ' '));


            string licenseeCode = f.read(2, Encoding.ASCII);
            bool   isSGB        = f.read() == 3;

            info.addInfo("Super Game Boy Enhanced?", isSGB);

            int cartType = f.read();
            int romSize  = f.read();
            int ramSize  = f.read();

            addCartTypeInfo(info, cartType, isGBX);
            if (isGBX)
            {
                info.addInfo("Original ROM size", romSize, ROM_SIZES, ROMInfo.FormatMode.SIZE, true);
                info.addInfo("Original save size", ramSize, RAM_SIZES, ROMInfo.FormatMode.SIZE, true);
            }
            else
            {
                info.addInfo("ROM size", romSize, ROM_SIZES, ROMInfo.FormatMode.SIZE);
                info.addInfo("Save size", ramSize, RAM_SIZES, ROMInfo.FormatMode.SIZE);
            }

            int destinationCode = f.read();

            info.addInfo("Destination code", destinationCode);             //Don't want to call this "Region", it's soorrrta what it is but only sorta. 0 is Japan and anything else is non-Japan basically

            int oldLicensee = f.read();

            if (oldLicensee == 0x33)
            {
                info.addInfo("Publisher", licenseeCode, NintendoCommon.LICENSEE_CODES);
                info.addInfo("Uses new licensee", true);
            }
            else
            {
                info.addInfo("Publisher", String.Format("{0:X2}", oldLicensee), NintendoCommon.LICENSEE_CODES);
                info.addInfo("Uses new licensee", false);
            }
            int version = f.read();

            info.addInfo("Version", version);
            int checksum = f.read();

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

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

            short globalChecksum = f.readShortBE();

            info.addInfo("Global checksum", globalChecksum, ROMInfo.FormatMode.HEX, true);
            short calculatedGlobalChecksum = calcGlobalChecksum(f);

            info.addInfo("Calculated global checksum", calculatedGlobalChecksum, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Global checksum valid?", globalChecksum == calculatedGlobalChecksum);

            if (!isCGB)
            {
                //Game Boy Color automatically colorizes some old Game Boy games via hashing the title and other data, albeit it only does this if licensee code = 01
                //If I had a way for the user to point to the location of the GBC BIOS, I could do more here, but until then nah
                f.Position = 0x134;
                byte[] hashData = f.read(15);
                byte   hash     = (byte)hashData.Aggregate(0, (x, y) => (x + y) & 0xff);
                info.addInfo("GBC palette hash", hash, ROMInfo.FormatMode.HEX);
                info.addInfo("GBC palette disambiguation value", title[3], ROMInfo.FormatMode.HEX);
            }
        }
Esempio n. 7
0
        public static void parseTMD(ROMInfo info, byte[] tmd)
        {
            WrappedInputStream s = new WrappedInputStream(new System.IO.MemoryStream(tmd))
            {
                Position = 0x184
            };

            byte[] iosVersion = s.read(8);
            info.addInfo("IOS version", iosVersion, true);

            byte[] titleID = s.read(4);
            //The one and only documentation says this is 8 bytes, but that doesn't seem to work as there's 4 weird bytes at the front
            //This might be the type actually (game = 0x10000, channel = 0x10001, etc)
            info.addInfo("Title ID unknown", titleID, true);

            string productCode = s.read(4, Encoding.ASCII);

            info.addInfo("Product code", productCode);
            char gameType = productCode[0];

            info.addInfo("Type", gameType, NintendoCommon.DISC_TYPES);
            string shortTitle = productCode.Substring(1, 2);

            info.addInfo("Short title", shortTitle);
            char country = productCode[3];

            info.addInfo("Country", country, NintendoCommon.COUNTRIES);

            int titleFlags = s.readIntBE();

            info.addInfo("Title flags", titleFlags, true);
            info.addInfo("Is official", (titleFlags & 1) == 1);   //Hmmmmmm

            string maker = s.read(2, Encoding.ASCII);             //Documentation calls this "Group ID" for some reason

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

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

            short regionCode = s.readShortBE();

            info.addInfo("Region", regionCode, NintendoCommon.REGIONS);

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

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

            byte[] ipcMask = s.read(12);             //The heck is that
            info.addInfo("IPC mask", ipcMask, true);

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

            byte[] accessRights = s.read(4);
            info.addInfo("Access rights", accessRights, true);             //How do I interpret these?

            short version = s.readShortBE();

            info.addInfo("Version", version);
        }