Esempio n. 1
0
        public static void parseDSiHeader(ROMInfo info, WrappedInputStream s)
        {
            s.Position = 0x1b0;
            int regionFlags = s.readIntLE();

            //There's only 6 bits used, everything else is reserved. What a good use of 5 bytes!
            if (regionFlags == -1)
            {
                //Hmm... Pokemon gen 5 games (I sure do talk about them a lot, huh? Well, they're weird. And they're good games) use 0xffffffef / -17 here, actually; explain that one nerd (bit 27 I guess? But then what the heck)
                info.addInfo("Region", "Region free");
            }
            else
            {
                info.addInfo("Region", Enum.ToObject(typeof(DSiRegionFlags), regionFlags).ToString());
            }

            s.Position = 0x210;
            int usedROMSize = s.readIntLE();

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

            info.addInfo("DSi reserved", s.read(4), true);
            info.addInfo("DSi reserved 2", s.read(4), true);
            info.addInfo("DSi reserved 3", s.read(4), true);

            int modcryptOffset = s.readIntLE();

            info.addInfo("Modcrypt area 1 offset", modcryptOffset, ROMInfo.FormatMode.HEX, true);
            int modcryptSize = s.readIntLE();

            info.addInfo("Modcrypt area 1 size", modcryptSize, ROMInfo.FormatMode.SIZE, true);
            int modcryptOffset2 = s.readIntLE();

            info.addInfo("Modcrypt area 2 offset", modcryptOffset2, ROMInfo.FormatMode.HEX, true);
            int modcryptSize2 = s.readIntLE();

            info.addInfo("Modcrypt area 2 size", modcryptSize2, ROMInfo.FormatMode.SIZE, true);

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

            info.addInfo("Game code backwards", emagCode, true);
            int dsiType = s.read();

            info.addInfo("Filetype", dsiType, DSI_TYPES);
            byte[] titleIDReserved = s.read(3);             //Usually 00 03 00 for some reason
            info.addInfo("DSi title ID reserved", titleIDReserved, true);

            int publicSaveSize = s.readIntLE();

            info.addInfo("DSiWare public.sav filesize", publicSaveSize, ROMInfo.FormatMode.SIZE);
            int privateSaveSize = s.readIntLE();

            info.addInfo("DSiWare private.sav filesize", publicSaveSize, ROMInfo.FormatMode.SIZE);

            info.addInfo("DSi reserved 4", s.read(176), true);

            byte[] ratings = s.read(16);
            NintendoCommon.parseRatings(info, ratings, true);
        }
Esempio n. 2
0
        public static Tuple <uint, uint> getFatInfo(WrappedInputStream s, uint fatOffset, ushort fileID)
        {
            long origPos = s.Position;

            try {
                s.Position = fatOffset + (fileID * 8);

                uint startOffset = (uint)s.readIntLE();
                uint endOffset   = (uint)s.readIntLE();
                uint size        = endOffset - startOffset;
                return(new Tuple <uint, uint>(startOffset, size));
            } finally {
                s.Position = origPos;
            }
        }
Esempio n. 3
0
        public static Dictionary <string, object> convertParamSFO(WrappedInputStream s)
        {
            var d = new Dictionary <string, object>();

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

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

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

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

                long originalPos = s.Position;

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

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

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

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

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

                s.Position = originalPos;
            }

            return(d);
        }
Esempio n. 4
0
        public static void parseExeFS(ROMInfo info, WrappedInputStream s, FilesystemDirectory partition, long offset = 0)
        {
            FilesystemDirectory exefs = new FilesystemDirectory()
            {
                name = "ExeFS"
            };

            s.Position = offset;
            for (int i = 0; i < 10; ++i)
            {
                string filename   = s.read(8, Encoding.ASCII).TrimEnd('\0');
                long   fileOffset = (uint)s.readIntLE() + offset + 0x200;               //Add ExeFS header as well
                long   fileSize   = (uint)s.readIntLE();

                if (fileSize > 0)
                {
                    exefs.addChild(filename, fileOffset, fileSize);
                }
            }
            partition.addChild(exefs);
        }
Esempio n. 5
0
        public static void parse3DSX(ROMInfo info, ROMFile file)
        {
            WrappedInputStream s = file.stream;

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

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

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

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

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

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

            long origPos = s.Position;

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

                readNitroSubFNT(fs, s, fatOffset, fatSize, fntOffset, filenameTableSize, subTableOffset, firstID);
            } finally {
                s.Position = origPos;
            }
        }
Esempio n. 7
0
        public static void parseXBE(ROMInfo info, WrappedInputStream s)
        {
            string magic = s.read(4, Encoding.ASCII);

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

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

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

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

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

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

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

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

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

            int certificateOffset = s.readIntLE() - baseAddress;

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

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

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

            int initFlags = s.readIntLE();

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

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

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

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

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

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

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

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

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

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

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

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

                int version = s.readIntLE();
                info.addInfo("Version", version);
            }
        }
Esempio n. 8
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. 9
0
        public override void addROMInfo(ROMInfo info, ROMFile file)
        {
            if ("plg".Equals(file.extension))
            {
                addSupercardDS2PluginInfo(info, file);
                return;
            }

            WrappedInputStream s = file.stream;

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

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

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

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

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

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

            int unitCode = s.read();

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

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

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

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

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

            int regionCode = s.read();

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

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

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

            int arm9Offset = s.readIntLE();

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

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

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

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

            int arm7Offset = s.readIntLE();

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

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

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

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

            int filenameTableOffset = s.readIntLE();

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

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

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

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

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

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

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

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

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

            int bannerOffset = s.readIntLE();

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

            short secureAreaChecksum = s.readShortLE();

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

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

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

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

            int arm9AutoLoadRAMAddress = s.readIntLE();

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

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

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

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

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

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

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

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

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

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

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

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

            int debugROMOffset = s.readIntLE();

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

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

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

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

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

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

            bool containsWifi = false;

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

            s.Position = offset;
            string magic = s.read(4, Encoding.ASCII);

            if (!magic.Equals("IVFC"))
            {
                return;
            }

            s.Position = offset + 8;
            int masterHashSize = s.readIntLE();

            s.Position = offset + 0x4c;
            int level3BlockSize     = s.readIntLE();
            int level3HashBlockSize = 1 << level3BlockSize;
            int level3Offset        = roundUpToMultiple(0x60 + masterHashSize, level3HashBlockSize);

            s.Position = offset + level3Offset + 4;
            //Header size should be 0x28...
            int directoryHashTableOffset = s.readIntLE();
            int directoryHashTableLength = s.readIntLE();
            int directoryMetadataOffset  = s.readIntLE();
            int directoryMetadataLength  = s.readIntLE();

            int fileHashTableOffset = s.readIntLE();
            int fileHashTableLength = s.readIntLE();
            int fileMetadataOffset  = s.readIntLE();
            int fileMetadataLength  = s.readIntLE();

            int fileDataOffset = s.readIntLE();

            long baseOffset = offset + level3Offset;

            s.Position = baseOffset + directoryMetadataOffset;
            byte[] rootDirectory = s.read(directoryMetadataLength);
            iterateRomFSEntry(s, romfs, rootDirectory, baseOffset + directoryMetadataOffset, baseOffset + fileMetadataOffset, baseOffset + fileDataOffset);

            partition.addChild(romfs);
        }
Esempio n. 12
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. 13
0
        public static void parsePBP(ROMInfo info, WrappedInputStream s)
        {
            byte[] magic = s.read(4);
            info.addInfo("Magic", magic, true);             //Should be "\0PBP", or maybe "PBP\0" because endians confuse me
            if (isELFMagic(magic))
            {
                info.addInfo("Detected format", "ELF");
                //There will not be anything to see here
                return;
            }
            else if (!isPBPMagic(magic))
            {
                info.addInfo("Detected format", "Unknown");
                return;
            }
            info.addInfo("Detected format", "PBP");


            byte[] unknown = s.read(4);             //This is speculated to be some kind of version number but I dunno
            info.addInfo("Unknown", unknown, true);

            //The files embedded here are supposedly always in this order, so you get the size by getting the difference between that file's offset and the next one (or the end of the file if it's the last one)

            int paramOffset = s.readIntLE();             //Apparently should always be 0x28

            info.addInfo("PARAM.SFO offset", paramOffset, ROMInfo.FormatMode.HEX, true);

            int icon0Offset = s.readIntLE();

            info.addInfo("ICON0.PNG offset", icon0Offset, ROMInfo.FormatMode.HEX, true);

            int icon1Offset = s.readIntLE();

            info.addInfo("ICON1.PNG offset", icon1Offset, ROMInfo.FormatMode.HEX, true);

            int pic0Offset = s.readIntLE();

            info.addInfo("PIC0.PNG offset", pic0Offset, ROMInfo.FormatMode.HEX, true);

            int pic1Offset = s.readIntLE();

            info.addInfo("PIC1.PNG offset", pic1Offset, ROMInfo.FormatMode.HEX, true);

            int sndOffset = s.readIntLE();

            info.addInfo("SND0.AT3 offset", sndOffset, ROMInfo.FormatMode.HEX, true);

            int dataPSPOffset = s.readIntLE();

            info.addInfo("DATA.PSP offset", dataPSPOffset, ROMInfo.FormatMode.HEX, true);

            int dataPSAROffset = s.readIntLE();

            info.addInfo("DATA.PSAR offset", dataPSAROffset, ROMInfo.FormatMode.HEX, true);

            if (paramOffset > 0x24)
            {
                int paramSize = icon0Offset - paramOffset;
                if (paramSize > 0)
                {
                    s.Position = paramOffset;
                    byte[] param = s.read(paramSize);

                    using (WrappedInputStream mem = new WrappedInputStream(new MemoryStream(param))) {
                        parseParamSFO(info, mem);
                    }
                }
            }

            if (icon0Offset > paramOffset)
            {
                int icon0Size = icon1Offset - icon0Offset;
                if (icon0Size > 0)
                {
                    s.Position = icon0Offset;
                    byte[] icon0 = s.read(icon0Size);

                    using (MemoryStream mem = new MemoryStream(icon0)) {
                        info.addInfo("Icon", System.Drawing.Image.FromStream(mem));
                    }
                }
            }

            if (icon1Offset > icon0Offset)
            {
                int icon1Size = pic0Offset - icon1Offset;
                if (icon1Size > 0)
                {
                    s.Position = icon1Offset;
                    byte[] icon0 = s.read(icon1Size);
                    //TODO: Detect if PSMF which is some kind of animated icon (I think the magic number is either PSMF00 or PAMF00 at the beginning but not sure)

                    using (MemoryStream mem = new MemoryStream(icon0)) {
                        info.addInfo("Icon 2", System.Drawing.Image.FromStream(mem));
                    }
                }
            }

            if (pic0Offset > icon1Offset)
            {
                int pic0Size = pic1Offset - pic0Offset;
                if (pic0Size > 0)
                {
                    s.Position = pic0Offset;
                    byte[] pic0 = s.read(pic0Size);

                    using (MemoryStream mem = new MemoryStream(pic0)) {
                        //Added as extra info because these can be up to 480x272, and that would be kind of shit to display in a table
                        info.addInfo("Information image", System.Drawing.Image.FromStream(mem), true);
                    }
                }
            }

            if (pic1Offset > pic0Offset)
            {
                int pic1Size = sndOffset - pic1Offset;
                if (pic1Size > 0)
                {
                    s.Position = pic1Offset;
                    byte[] pic1 = s.read(pic1Size);

                    using (MemoryStream mem = new MemoryStream(pic1)) {
                        info.addInfo("Background image", System.Drawing.Image.FromStream(mem), true);
                    }
                }
            }

            //TODO Get the sound... maybe the individual file info can show a "Sounds" button which plays the thing
        }
Esempio n. 14
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"
        }