Esempio n. 1
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            info.addInfo("Platform", name);
            WrappedInputStream s = file.stream;

            s.Seek(-544, System.IO.SeekOrigin.End);             //Yeah, this one's a bit weird

            string title = s.read(20, MainProgram.shiftJIS).TrimEnd(' ');

            info.addInfo("Internal name", title);
            byte[] reserved = s.read(5);
            info.addInfo("Reserved", reserved, true);
            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);
            //I don't know what to do about the game type, since it's all V so far
            info.addInfo("Type", productCode[0], true);
            string shortTitle = productCode.Substring(1, 2);

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

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

            info.addInfo("Version", version);
        }
Esempio n. 2
0
        public static WrappedInputStream decodeSMD(WrappedInputStream s)
        {
            s.Seek(512, SeekOrigin.Current);
            //Should only need this much to read the header. If I was actually converting
            //the ROM I'd need to use the SMD header to know how many blocks there are
            //and read multiple blocks and whatnot
            byte[] block = s.read(16384);

            byte[] buf = new byte[16386];
            //Yes, the below starting points are correct. It makes no goddamned
            //sense but if I use even = 0 and odd = 1 as the starting points
            //it goes off by one so I don't know anymore
            int buf_even = 1;
            int buf_odd  = 2;

            int midpoint = 8192;

            for (int i = 0; i < block.Length; ++i)
            {
                if (i <= midpoint)
                {
                    buf[buf_even] = block[i];
                    buf_even     += 2;
                }
                else
                {
                    buf[buf_odd] = block[i];
                    buf_odd     += 2;
                }
            }

            byte[] buf2 = new byte[buf_odd];
            Array.Copy(buf, buf2, buf_odd);
            return(new WrappedInputStream(new MemoryStream(buf2)));
        }
Esempio n. 3
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. 4
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. 5
0
        public static void parseSufamiTurboHeader(ROMInfo info, WrappedInputStream s)
        {
            //Well this is a lot more straightforward and relaxing
            //Some Sufami Turbo games have an ordinary SNES header, but most of them don't

            s.Position = 0;

            string magic = s.read(14, Encoding.ASCII);

            info.addInfo("Magic", magic, true);        //Should be "BANDAI SFC-ADX"

            s.Seek(2, SeekOrigin.Current);             //Skip zero-filled padding
            string title = s.read(14, MainProgram.shiftJIS).TrimEnd(' ');

            info.addInfo("Internal name", title);
            s.Seek(2, SeekOrigin.Current);
            byte[] entryPoint = s.read(4);
            info.addInfo("Entry point", entryPoint, ROMInfo.FormatMode.HEX, true);
            s.Position = 0x30;             //Skip over all these vectors whatevs
            byte[] gameID = s.read(3);
            info.addInfo("Game ID", gameID);
            int seriesIndex = s.read();

            info.addInfo("Index within series", seriesIndex);
            int romSpeed = s.read();

            info.addInfo("ROM speed", romSpeed == 1 ? "Fast (3.58MHz)" : "Slow (2.68MHz)");
            int features = s.read();

            info.addInfo("Features", features);
            int romSize = s.read() * 128 * 1024;

            info.addInfo("ROM size", romSize, ROMInfo.FormatMode.SIZE);
            int saveSize = s.read() * 2 * 1024;

            info.addInfo("Save size", saveSize, ROMInfo.FormatMode.SIZE);
        }
Esempio n. 6
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. 7
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. 8
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. 9
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. 10
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"
        }