Esempio n. 1
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            if ("dol".Equals(file.extension))
            {
                //TODO Parse what little info there is out of dol (could use this for boot .dol and Wii homebrew .dol too I guess)
                info.addInfo("Platform", "GameCube");
                return;
            }

            WrappedInputStream s = file.stream;

            byte[] magic = s.read(4);
            if (isTGCMagic(magic))
            {
                s.Position = 8;
                int tgcHeaderSize = s.readIntBE();
                info.addInfo("TGC header size", tgcHeaderSize, ROMInfo.FormatMode.SIZE);

                //What the f**k? What does Dolphin know that YAGD doesn't and didn't decide to tell the rest of the class?
                s.Position = 0x24;
                int realOffset = s.readIntBE();
                s.Position = 0x34;
                int virtualOffset = s.readIntBE();
                int fileOffset    = realOffset - virtualOffset;
                parseGamecubeDisc(info, s, tgcHeaderSize, fileOffset, true);
            }
            else
            {
                parseGamecubeDisc(info, s, 0, 0, false);
            }
        }
Esempio n. 2
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. 3
0
        public static void readRootFST(FilesystemDirectory fs, WrappedInputStream s, int fstOffset, int fstSize, int virtualOffset)
        {
            //TODO: Check against FST max size in header, and also Wii RAM limit (since this is also used on Wii I think, otherwise I'd say GameCube RAM limit) (88MB for Wii, 24MB system / 43MB total for GameCube) as it would be impossible for real hardware to read a bigger filesystem
            //TODO: Wii shifts offset by one
            long origPos = s.Position;
            Dictionary <int, FilesystemDirectory> parentDirectories = new Dictionary <int, FilesystemDirectory> {
                { 0, fs }
            };

            try {
                s.Position = fstOffset + 8;
                //1 byte flag at fstOffset + 0: Filesystem root _must_ be a directory
                //Name offset is irrelevant since the root doesn't really have a name (I think it's just set to 0 anyway)
                //Parent index would also be irrelevant because it doesn't have a parent by definition
                //TODO: Throw error or something if not a directory
                //TODO: Throw error if number of entries * 12 > fstSize
                int numberOfEntries = s.readIntBE();
                int fntOffset       = fstOffset + (numberOfEntries * 12);
                int fntSize         = fstSize - ((numberOfEntries) * 12);
                s.Position = fntOffset;
                byte[] fnt = s.read(fntSize);
                s.Position = fstOffset + 12;

                //Due to weirdness, we need to figure out which directories these files go in afterwards. It's really weird. I just hope it makes enough sense for whatever purpose you're reading this code for.
                Dictionary <int, FilesystemFile> filesToAdd           = new Dictionary <int, FilesystemFile>();
                Dictionary <int, int>            directoryNextIndexes = new Dictionary <int, int> {
                    { 0, numberOfEntries }
                };

                for (int i = 0; i < numberOfEntries - 1; ++i)
                {
                    byte[] entry = s.read(12);
                    readFileEntry(entry, fnt, virtualOffset, parentDirectories, i + 1, filesToAdd, directoryNextIndexes);
                }

                //Now that we have the directory structure, add the files to them
                //This sucks, by the way
                //Like it's not that my code sucks (although it probably kinda does), it's like... I hate the GameCube filesystem
                foreach (var fileToAdd in filesToAdd)
                {
                    int            fileIndex = fileToAdd.Key;
                    FilesystemFile file      = fileToAdd.Value;

                    for (int potentialParentIndex = fileIndex - 1; potentialParentIndex >= 0; potentialParentIndex--)
                    {
                        if (directoryNextIndexes.ContainsKey(potentialParentIndex))
                        {
                            if (directoryNextIndexes[potentialParentIndex] > fileIndex)
                            {
                                var parentDir = parentDirectories[potentialParentIndex];
                                parentDir.addChild(file);
                                break;
                            }
                        }
                    }
                }
            } finally {
                s.Position = origPos;
            }
        }
Esempio n. 4
0
        public static void parseCART(ROMInfo info, WrappedInputStream s)
        {
            int cartTypeNumber = s.readIntBE();

            if (CART_TYPES.TryGetValue(cartTypeNumber, out CartType cartType))
            {
                info.addInfo("Type", cartType.name);
                info.addInfo("ROM size", cartType.size, ROMInfo.FormatMode.SIZE);
                if (cartType.platform == CartPlatform.Atari5200)
                {
                    info.addInfo("Platform", "Atari 5200");
                }
                else if (cartType.platform == CartPlatform.XEGS)
                {
                    info.addInfo("Platform", "XEGS");
                }
                else if (cartType.platform == CartPlatform.Atari800)
                {
                    info.addInfo("Platform", "Atari 800");
                }
                else
                {
                    info.addInfo("Platform", "Atari 400/800/XL/XE");
                }
            }
            else
            {
                info.addInfo("Type", String.Format("Unknown ({0})", cartTypeNumber));
                info.addInfo("Platform", "Atari 8-bit");
            }
            int checksum = s.readIntBE();             //TODO: How is this calculated?

            info.addInfo("Checksum", checksum, ROMInfo.FormatMode.HEX);
            int unused = s.readIntBE();

            info.addInfo("Unused", unused, true);
        }
Esempio n. 5
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            info.addInfo("Platform", name);

            WrappedInputStream s = file.stream;

            int headerSize = s.readIntBE();

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

            byte[] wadType = s.read(4);
            info.addInfo("WAD type", wadType, true);             //Can be either Is, ib or Bk with by two nulls, but what does it mean?
            info.addInfo("WAD type ASCII", Encoding.ASCII.GetString(wadType), true);

            int certChainSize = s.readIntBE();

            info.addInfo("Certificate chain size", certChainSize, true);

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

            int ticketSize = s.readIntBE();

            info.addInfo("Ticket size", ticketSize, ROMInfo.FormatMode.SIZE, true);

            int tmdSize = s.readIntBE();

            info.addInfo("TMD size", tmdSize, ROMInfo.FormatMode.SIZE, true);

            int dataSize = s.readIntBE();

            info.addInfo("Data size", dataSize, ROMInfo.FormatMode.SIZE, true);

            int footerSize = s.readIntBE();

            info.addInfo("Footer size", footerSize, ROMInfo.FormatMode.SIZE, true);

            s.Position = 0x40;             //All blocks are stored in the order of their sizes in the header, and aligned to 0x40 bytes, we've just read the header of course

            //TODO: Read certificate chain and ticket
            s.Seek(roundUpToMultiple(certChainSize, 0x40), System.IO.SeekOrigin.Current);
            s.Seek(roundUpToMultiple(ticketSize, 0x40), System.IO.SeekOrigin.Current);

            byte[] tmd = s.read(roundUpToMultiple(tmdSize, 0x40));
            parseTMD(info, tmd);
        }
Esempio n. 6
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            info.addInfo("Platform", name);
            WrappedInputStream s = file.stream;

            int    headerVersion  = s.read();
            string atari7800Magic = s.read(16, Encoding.ASCII);             //Should be "ATARI7800" null padded

            if (!"ATARI7800\0\0\0\0\0\0\0".Equals(atari7800Magic))
            {
                info.addInfo("Detected format", "Non-headered");
                return;
            }
            info.addInfo("Detected format", "Headered");
            info.addInfo("Magic", atari7800Magic, true);
            info.addInfo("Header version", headerVersion);


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

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

            int romSize = s.readIntBE();             //Excluding this header

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

            int specialCartType = s.read();

            info.addInfo("Special cart type data", specialCartType, true);

            int cartType = s.read();

            info.addInfo("Cart type data", cartType, true);

            bool pokey = (cartType & 1) > 0;

            info.addInfo("Contains POKEY chip?", pokey);
            bool supercartBankSwitched = (cartType & 2) > 0;

            info.addInfo("Supercart bank switched?", supercartBankSwitched);
            bool supercartRAMat4000 = (cartType & 4) > 0;

            info.addInfo("Supercart RAM at 0x4000?", supercartRAMat4000);
            bool romAt4000 = (cartType & 8) > 0;

            info.addInfo("ROM at 0x4000?", romAt4000);
            bool bank6At4000 = (cartType & 16) > 0;

            info.addInfo("Bank 6 at 0x4000?", bank6At4000);

            int controller1Type = s.read();

            info.addInfo("Controller 1 type", controller1Type, CONTROLLER_TYPES);
            int controller2Type = s.read();

            info.addInfo("Controller 2 type", controller2Type, CONTROLLER_TYPES);

            int tvType = s.read();

            info.addInfo("TV type", tvType, TV_TYPES);
            int saveType = s.read();

            info.addInfo("Save type", saveType, SAVE_TYPES);
            byte[] reserved = s.read(4);
            info.addInfo("Reserved", reserved, true);
            bool expansionModuleRequired = (s.read() & 1) > 0;

            info.addInfo("Expansion module required?", expansionModuleRequired);

            //Then there's a 28 byte string which literally says "ACTUAL CART DATA STARTS HERE" in ASCII, and indeed
            //the actual cart data starts there
        }
Esempio n. 7
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. 8
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. 9
0
        public static void parseN64ROM(WrappedInputStream s, ROMInfo info)
        {
            int clockRate = s.readIntBE();             //0 = default, apparently the low nibble isn't read

            info.addInfo("Clock rate", clockRate, ROMInfo.FormatMode.HEX, true);
            int programCounter = s.readIntBE();             //This technically is the entry point but the CIC chip might alter that

            info.addInfo("Entry point", programCounter, ROMInfo.FormatMode.HEX, true);
            int release = s.readIntBE();

            info.addInfo("Release address", release, ROMInfo.FormatMode.HEX, true);             //What the f**k does that even mean
            uint crc1 = (uint)s.readIntBE();
            uint crc2 = (uint)s.readIntBE();

            info.addInfo("Checksum", crc1, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Checksum 2", crc2, ROMInfo.FormatMode.HEX, true);
            byte[] unknown = s.read(8);             //Should be 0 filled, console probably doesn't read it though
            info.addInfo("Unknown", unknown, true);

            //The N64 does use Shift-JIS for its internal names, and if anyone says it is
            //ASCII I will smack them on the head with a copy of Densha de Go 64
            string name = s.read(20, MainProgram.shiftJIS).TrimEnd('\0');

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

            byte[] unknown2 = s.read(4);
            info.addInfo("Unknown 2", unknown2, true);
            byte[] unknown3 = s.read(3);
            info.addInfo("Unknown 3", unknown3, true);

            //A lot of N64 documentation seems to think the media type (or in the case of
            //n64dev, the manufacturer which is not what these bytes are for) is 4 bytes, but it's
            //just one byte, these are just there, all I know is that Custom Robo's fan
            //translation patch changes this and messes up parsing the header if I think that
            //the media type is 4 bytes
            string gameCode = s.read(4, Encoding.ASCII);             //Just alphanumeric but ASCII will do

            info.addInfo("Product code", gameCode);
            char mediaType = gameCode[0];

            info.addInfo("Type", mediaType, N64_MEDIA_TYPES);
            string shortTitle = gameCode.Substring(1, 2);

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

            info.addInfo("Country", country, NintendoCommon.COUNTRIES);
            int version = s.read();

            info.addInfo("Version", version);

            int[] bootCode         = new int[1008];
            uint  bootCodeChecksum = 0;

            for (var i = 0; i < 1008; ++i)
            {
                bootCode[i]      = s.readIntBE();
                bootCodeChecksum = (uint)(bootCodeChecksum + bootCode[i]) & 0xffffffff;
            }
            //Not sure why I insisted on making bootCode a separate array, but I'm sure I'll remember the reason later... until then, there's no point in adding an info item which just shows up as System.Int32[]
            //info.addInfo("Boot code", bootCode, true);

            switch (bootCodeChecksum)
            {
            case CIC_LYLAT:
            case CIC_6101:                     //Star Fox 64 (USA), or at least rev A; for some reason Lylat Wars ends up with a different boot code checksum even though it'd be the same chip except PAL
                info.addInfo("CIC chip", "6101/7102 (Star Fox 64)");
                break;

            case CIC_6102:
                info.addInfo("CIC chip", "6102/7101 (standard, Super Mario 64 etc)");
                break;

            case CIC_6103:
                info.addInfo("CIC chip", "6103/7103 (Banjo-Kazooie, Paper Mario etc)");
                break;

            case CIC_6105:
                info.addInfo("CIC chip", "6105/7105 (Ocarina of Time etc)");
                break;

            case CIC_6106:
                info.addInfo("CIC chip", "6106/7106 (F-Zero X, Yoshi's Story etc)");
                break;

            case CIC_64DD:
                info.addInfo("CIC chip", "5137 (64DD cartridge conversion)");
                break;

            default:
                info.addInfo("CIC chip", String.Format("Unknown {0:X}", bootCodeChecksum));
                break;

                //Others:
                //64DD modem: D1055850 (IIRC, this doesn't actually have a CIC chip at all)
                //2C21F6CA in most Aleck64 games hacked to run on retail N64 carts via Everdrive, although Tower & Shaft uses 1950CEA5 and Star Soldier Vanishing Earth Arcade uses AC11F6CA
                //Vivid Dolls ripped from the MAME romset without further modifications: F80BF620
            }

            Tuple <uint, uint> calculatedChecksum = calcChecksum(s, bootCodeChecksum);

            info.addInfo("Calculated checksum", calculatedChecksum.Item1, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Calculated checksum 2", calculatedChecksum.Item2, ROMInfo.FormatMode.HEX, true);
            info.addInfo("Checksum valid?", calculatedChecksum.Item1 == crc1);
            info.addInfo("Checksum 2 valid?", calculatedChecksum.Item2 == crc2);

            //Might be a way to detect save type (probably not)
        }
Esempio n. 10
0
        public static Tuple <uint, uint> calcChecksum(WrappedInputStream s, uint cicType)
        {
            //What the f**k
            uint seed;

            switch (cicType)
            {
            case CIC_6106:
                seed = 0x1fea617a;
                break;

            case CIC_6105:
                seed = 0xdf26f436;
                break;

            case CIC_6103:
                seed = 0xa3886759;
                break;

            default:
                seed = 0xf8ca4ddc;
                break;
            }

            uint[] t = new uint[6];
            t[0] = t[1] = t[2] = t[3] = t[4] = t[5] = seed;

            s.Position = 0x1000;
            //What the f*****g dicks
            //Why isn't this even documented properly anywhere other than "use this tool" well how about I don't wanna use that tool
            uint d, r;

            for (int i = 0x1000; i < 0x101000; i += 4)
            {
                d = (uint)s.readIntBE();
                if ((t[5] + d) < t[5])
                {
                    t[3]++;
                }
                t[5] += d;
                t[2] ^= d;
                r     = rol(d, (int)(d & 0x1f));
                t[4] += r;
                if (t[1] > d)
                {
                    t[1] ^= r;
                }
                else
                {
                    t[1] ^= t[5] ^ d;
                }

                if (cicType == CIC_6105)
                {
                    //What the arse titty balls
                    long pos = s.Position;
                    s.Position = 0x750 + (i & 0xff);
                    t[0]      += (uint)s.readIntBE() ^ d;
                    s.Position = pos;
                }
                else
                {
                    t[0] += t[4] ^ d;
                }
            }

            //What the absolute f**k I'm telling my therapist about this shit
            if (cicType == CIC_6103)
            {
                return(new Tuple <uint, uint>((t[5] ^ t[3]) + t[2], (t[4] ^ t[1]) + t[0]));
            }
            else if (cicType == CIC_6106)
            {
                return(new Tuple <uint, uint>((t[5] * t[3]) + t[2], (t[4] * t[1]) + t[0]));
            }
            else
            {
                return(new Tuple <uint, uint>(t[5] ^ t[3] ^ t[2], t[4] ^ t[1] ^ t[0]));
            }
        }
Esempio n. 11
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);
        }
Esempio n. 12
0
        static void parseGamecubeDisc(ROMInfo info, WrappedInputStream s, int startOffset, int fileOffset, bool isEmbedded)
        {
            s.Position = startOffset;
            parseGamecubeHeader(info, s);

            s.Position = 0x400 + startOffset;

            int debugMonitorOffset = s.readIntBE();

            info.addInfo("Debug monitor offset", debugMonitorOffset - startOffset, ROMInfo.FormatMode.HEX, true);

            int debugMonitorLoadAddress = s.readIntBE();

            info.addInfo("Debug monitor load address", debugMonitorLoadAddress, ROMInfo.FormatMode.HEX, true);

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

            int bootDOLOffset, fstOffset, fstSize, fstMaxSize;

            if (!isEmbedded)
            {
                bootDOLOffset = s.readIntBE();
                fstOffset     = s.readIntBE();
                fstSize       = s.readIntBE();
                fstMaxSize    = s.readIntBE();
            }
            else
            {
                long pos = s.Position;
                try {
                    s.Position    = 16;
                    fstOffset     = s.readIntBE();
                    fstSize       = s.readIntBE();
                    fstMaxSize    = s.readIntBE();
                    bootDOLOffset = s.readIntBE();
                } finally {
                    s.Position = pos;
                }
            }
            info.addInfo("Boot DOL offset", bootDOLOffset, ROMInfo.FormatMode.HEX, true);
            info.addInfo("FST offset", fstOffset, ROMInfo.FormatMode.HEX, true);
            info.addInfo("FST size", fstSize, ROMInfo.FormatMode.SIZE, true);
            info.addInfo("FST maximum size", fstMaxSize, ROMInfo.FormatMode.SIZE, true);


            int userPosition = s.readIntBE();

            info.addInfo("User position", userPosition, ROMInfo.FormatMode.HEX, true);

            int userSize = s.readIntBE();

            info.addInfo("User size", userSize, ROMInfo.FormatMode.SIZE, true);

            s.Position = 0x458 + startOffset;
            int region = s.readIntBE();

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

            s.Position = 0x2440 + startOffset;
            string apploaderDate = s.read(16, Encoding.ASCII).Trim('\0');

            if (DateTime.TryParseExact(apploaderDate, "yyyy/MM/dd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime date))
            {
                info.addInfo("Date", date);
                info.addInfo("Year", date.Year);
                info.addInfo("Month", DateTimeFormatInfo.CurrentInfo.GetMonthName(date.Month));
                info.addInfo("Day", date.Day);
            }
            else
            {
                info.addInfo("Date", apploaderDate);
            }

            if (fstOffset > 0 && fstSize > 12 && fstSize < (128 * 1024 * 1024))
            {
                var fs = readGamecubeFS(s, fstOffset, fstSize, fileOffset);
                if (fs.contains("opening.bnr"))
                {
                    var banner = (FilesystemFile)fs.getChild("opening.bnr");
                    s.Position = banner.offset;
                    byte[] bannerData = s.read((int)banner.size);
                    parseBanner(info, bannerData, region);
                }
                info.addFilesystem(fs);
            }
        }
Esempio n. 13
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            WrappedInputStream s = file.stream;

            Gamecube.parseGamecubeHeader(info, s);

            s.Position = 0x40000;

            int  totalPartitions = 0;
            bool containsUpdate  = false;
            int  partitionIndex  = 0;

            for (int partitionGroup = 0; partitionGroup < 4; ++partitionGroup)
            {
                int partitions = s.readIntBE();
                totalPartitions += partitions;

                int partitionInfoOffset = s.readIntBE() << 2;
                for (int i = 0; i < partitions; ++i)
                {
                    long pos = s.Position;
                    try {
                        s.Position = partitionInfoOffset + (i * 8) + 4;

                        byte[] partitionType = s.read(4);
                        string type;
                        if (partitionType[0] == 0)
                        {
                            if (partitionType[3] == 0)
                            {
                                type = "Game data";
                            }
                            else if (partitionType[3] == 1)
                            {
                                containsUpdate = true;
                                type           = "Update";
                            }
                            else if (partitionType[3] == 2)
                            {
                                type = "Channel";
                            }
                            else
                            {
                                type = String.Format("Unknown ({0})", partitionType[3]);
                            }
                        }
                        else
                        {
                            type = Encoding.ASCII.GetString(partitionType);
                        }
                        info.addInfo(String.Format("Partition {0} type", partitionIndex), type, true);
                        //TODO: Read TMD from partition
                    } finally {
                        s.Position = pos;
                    }
                    ++partitionIndex;
                }
            }
            info.addInfo("Number of partitions", totalPartitions);
            info.addInfo("Contains update partition", containsUpdate);

            s.Position = 0x4e000;
            int region = s.readIntBE();

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

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

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