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); }
public static void parseTMD(ROMInfo info, byte[] tmd) { WrappedInputStream s = new WrappedInputStream(new System.IO.MemoryStream(tmd)) { Position = 0x184 }; byte[] iosVersion = s.read(8); info.addInfo("IOS version", iosVersion, true); byte[] titleID = s.read(4); //The one and only documentation says this is 8 bytes, but that doesn't seem to work as there's 4 weird bytes at the front //This might be the type actually (game = 0x10000, channel = 0x10001, etc) info.addInfo("Title ID unknown", titleID, true); string productCode = s.read(4, Encoding.ASCII); info.addInfo("Product code", productCode); char gameType = productCode[0]; info.addInfo("Type", gameType, NintendoCommon.DISC_TYPES); string shortTitle = productCode.Substring(1, 2); info.addInfo("Short title", shortTitle); char country = productCode[3]; info.addInfo("Country", country, NintendoCommon.COUNTRIES); int titleFlags = s.readIntBE(); info.addInfo("Title flags", titleFlags, true); info.addInfo("Is official", (titleFlags & 1) == 1); //Hmmmmmm string maker = s.read(2, Encoding.ASCII); //Documentation calls this "Group ID" for some reason info.addInfo("Publisher", maker, NintendoCommon.LICENSEE_CODES); byte[] unused = s.read(2); info.addInfo("Unused", unused, true); short regionCode = s.readShortBE(); info.addInfo("Region", regionCode, NintendoCommon.REGIONS); byte[] ratings = s.read(16); NintendoCommon.parseRatings(info, ratings, false); byte[] reserved = s.read(12); info.addInfo("TMD reserved", reserved, true); byte[] ipcMask = s.read(12); //The heck is that info.addInfo("IPC mask", ipcMask, true); byte[] reserved2 = s.read(18); info.addInfo("TMD reserved 2", reserved2, true); byte[] accessRights = s.read(4); info.addInfo("Access rights", accessRights, true); //How do I interpret these? short version = s.readShortBE(); info.addInfo("Version", version); }
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)); }
public override void addROMInfo(ROMInfo info, ROMFile file) { WrappedInputStream s = file.stream; Gamecube.parseGamecubeHeader(info, s); s.Position = 0x40000; int totalPartitions = 0; bool containsUpdate = false; int partitionIndex = 0; for (int partitionGroup = 0; partitionGroup < 4; ++partitionGroup) { int partitions = s.readIntBE(); totalPartitions += partitions; int partitionInfoOffset = s.readIntBE() << 2; for (int i = 0; i < partitions; ++i) { long pos = s.Position; try { s.Position = partitionInfoOffset + (i * 8) + 4; byte[] partitionType = s.read(4); string type; if (partitionType[0] == 0) { if (partitionType[3] == 0) { type = "Game data"; } else if (partitionType[3] == 1) { containsUpdate = true; type = "Update"; } else if (partitionType[3] == 2) { type = "Channel"; } else { type = String.Format("Unknown ({0})", partitionType[3]); } } else { type = Encoding.ASCII.GetString(partitionType); } info.addInfo(String.Format("Partition {0} type", partitionIndex), type, true); //TODO: Read TMD from partition } finally { s.Position = pos; } ++partitionIndex; } } info.addInfo("Number of partitions", totalPartitions); info.addInfo("Contains update partition", containsUpdate); s.Position = 0x4e000; int region = s.readIntBE(); info.addInfo("Region", region, NintendoCommon.REGIONS); byte[] unused = s.read(12); info.addInfo("Unused region data", unused, true); byte[] ratings = s.read(16); NintendoCommon.parseRatings(info, ratings, false); }