Пример #1
0
        static void addCartHardwareInfo(ROMInfo info, CartAdditionalHardware hardware, bool isOriginalFromGBX)
        {
            string prefix = isOriginalFromGBX ? "Original cart header claims to have " : "Has ";             //I should word that better, but whatevs

            info.addInfo(prefix + "RAM", hardware.HasFlag(CartAdditionalHardware.RAM), isOriginalFromGBX);
            info.addInfo(prefix + "battery", hardware.HasFlag(CartAdditionalHardware.Battery), isOriginalFromGBX);
            info.addInfo(prefix + "RTC", hardware.HasFlag(CartAdditionalHardware.RTC), isOriginalFromGBX);
            info.addInfo(prefix + "rumble", hardware.HasFlag(CartAdditionalHardware.Rumble), isOriginalFromGBX);
            info.addInfo(prefix + "accelerometer", hardware.HasFlag(CartAdditionalHardware.Accelerometer), isOriginalFromGBX);
        }
Пример #2
0
 public CartInfo(string mapper, CartAdditionalHardware flags = CartAdditionalHardware.None)
 {
     this.mapper = mapper;
     this.flags  = flags;
 }
Пример #3
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);
            }
        }