using System; using System.IO; using System.Text; using System.Globalization; using FileSystemIDandChk; // This is coded following ECMA-119. // TODO: Differentiate ISO Level 1, 2, 3 and ISO 9660:1999 // TODO: Apple extensiones, requires XA or advance RR interpretation. namespace FileSystemIDandChk.Plugins { class ISO9660Plugin : Plugin { private static bool alreadyLaunched; public ISO9660Plugin(PluginBase Core) { base.Name = "ISO9660 Filesystem"; base.PluginUUID = new Guid("d812f4d3-c357-400d-90fd-3b22ef786aa8"); alreadyLaunched = false; } private struct DecodedVolumeDescriptor { public string SystemIdentifier; public string VolumeIdentifier; public string VolumeSetIdentifier; public string PublisherIdentifier; public string DataPreparerIdentifier; public string ApplicationIdentifier; public DateTime CreationTime; public bool HasModificationTime; public DateTime ModificationTime; public bool HasExpirationTime; public DateTime ExpirationTime; public bool HasEffectiveTime; public DateTime EffectiveTime; } public override bool Identify(FileStream fileStream, long offset) { if(alreadyLaunched) return false; else alreadyLaunched = true; byte VDType; // ISO9660 Primary Volume Descriptor starts at 32768, so that's minimal size. if (fileStream.Length < 32768) return false; // Seek to Volume Descriptor fileStream.Seek(32768, SeekOrigin.Begin); VDType = (byte)fileStream.ReadByte(); byte[] VDMagic = new byte[5]; // Wrong, VDs can be any order! if (VDType == 255) // Supposedly we are in the PVD. return false; if (fileStream.Read(VDMagic, 0, 5) != 5) return false; // Something bad happened if (Encoding.ASCII.GetString(VDMagic) != "CD001") // Recognized, it is an ISO9660, now check for rest of data. return false; return true; } public override void GetInformation (FileStream fileStream, long offset, out string information) { information = ""; StringBuilder ISOMetadata = new StringBuilder(); bool Joliet = false; bool Bootable = false; bool RockRidge = false; byte VDType; // Volume Descriptor Type, should be 1 or 2. byte[] VDMagic = new byte[5]; // Volume Descriptor magic "CD001" byte[] VDSysId = new byte[32]; // System Identifier byte[] VDVolId = new byte[32]; // Volume Identifier byte[] VDVolSetId = new byte[128]; // Volume Set Identifier byte[] VDPubId = new byte[128]; // Publisher Identifier byte[] VDDataPrepId = new byte[128]; // Data Preparer Identifier byte[] VDAppId = new byte[128]; // Application Identifier byte[] VCTime = new byte[17]; // Volume Creation Date and Time byte[] VMTime = new byte[17]; // Volume Modification Date and Time byte[] VXTime = new byte[17]; // Volume Expiration Date and Time byte[] VETime = new byte[17]; // Volume Effective Date and Time byte[] JolietMagic = new byte[3]; byte[] JolietSysId = new byte[32]; // System Identifier byte[] JolietVolId = new byte[32]; // Volume Identifier byte[] JolietVolSetId = new byte[128]; // Volume Set Identifier byte[] JolietPubId = new byte[128]; // Publisher Identifier byte[] JolietDataPrepId = new byte[128]; // Data Preparer Identifier byte[] JolietAppId = new byte[128]; // Application Identifier byte[] JolietCTime = new byte[17]; // Volume Creation Date and Time byte[] JolietMTime = new byte[17]; // Volume Modification Date and Time byte[] JolietXTime = new byte[17]; // Volume Expiration Date and Time byte[] JolietETime = new byte[17]; // Volume Effective Date and Time byte[] BootSysId = new byte[32]; string BootSpec = ""; byte[] VDPathTableStart = new byte[4]; byte[] RootDirectoryLocation = new byte[4]; fileStream.Seek(0, SeekOrigin.Begin); // ISO9660 Primary Volume Descriptor starts at 32768, so that's minimal size. if (fileStream.Length < 32768) return; int counter = 0; while (true) { // Seek to Volume Descriptor fileStream.Seek(32768+(2048*counter), SeekOrigin.Begin); VDType = (byte)fileStream.ReadByte(); if (VDType == 255) // Supposedly we are in the PVD. { if (counter == 0) return; break; } if (fileStream.Read(VDMagic, 0, 5) != 5) { if (counter == 0) return; // Something bad happened break; } if (Encoding.ASCII.GetString(VDMagic) != "CD001") // Recognized, it is an ISO9660, now check for rest of data. { if (counter == 0) return; break; } switch(VDType) { case 0: // TODO { Bootable = true; BootSpec = "Unknown"; // Seek to boot system identifier fileStream.Seek(32775 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(BootSysId, 0, 32) != 32) break; // Something bad happened if (Encoding.ASCII.GetString(BootSysId).Substring(0, 23) == "EL TORITO SPECIFICATION") BootSpec = "El Torito"; break; } case 1: { // Seek to first identifiers fileStream.Seek(32776 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(VDSysId, 0, 32) != 32) break; // Something bad happened if (fileStream.Read(VDVolId, 0, 32) != 32) break; // Something bad happened // Get path table start fileStream.Seek(32908 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(VDPathTableStart, 0, 4) != 4) break; // Something bad happened // Seek to next identifiers fileStream.Seek(32958 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(VDVolSetId, 0, 128) != 128) break; // Something bad happened if (fileStream.Read(VDPubId, 0, 128) != 128) break; // Something bad happened if (fileStream.Read(VDDataPrepId, 0, 128) != 128) break; // Something bad happened if (fileStream.Read(VDAppId, 0, 128) != 128) break; // Something bad happened // Seek to dates fileStream.Seek(33581 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(VCTime, 0, 17) != 17) break; // Something bad happened if (fileStream.Read(VMTime, 0, 17) != 17) break; // Something bad happened if (fileStream.Read(VXTime, 0, 17) != 17) break; // Something bad happened if (fileStream.Read(VETime, 0, 17) != 17) break; // Something bad happened break; } case 2: { // Check if this is Joliet fileStream.Seek(32856 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(JolietMagic, 0, 3) != 3) { break; // Something bad happened } if (JolietMagic[0] == '%' && JolietMagic[1] == '/') { if (JolietMagic[2] == '@' || JolietMagic[2] == 'C' || JolietMagic[2] == 'E') { Joliet = true; } else { break; } } else break; // Seek to first identifiers fileStream.Seek(32776 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(JolietSysId, 0, 32) != 32) break; // Something bad happened if (fileStream.Read(JolietVolId, 0, 32) != 32) break; // Something bad happened // Seek to next identifiers fileStream.Seek(32958 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(JolietVolSetId, 0, 128) != 128) break; // Something bad happened if (fileStream.Read(JolietPubId, 0, 128) != 128) break; // Something bad happened if (fileStream.Read(JolietDataPrepId, 0, 128) != 128) break; // Something bad happened if (fileStream.Read(JolietAppId, 0, 128) != 128) break; // Something bad happened // Seek to dates fileStream.Seek(33581 + (2048 * counter), SeekOrigin.Begin); if (fileStream.Read(JolietCTime, 0, 17) != 17) break; // Something bad happened if (fileStream.Read(JolietMTime, 0, 17) != 17) break; // Something bad happened if (fileStream.Read(JolietXTime, 0, 17) != 17) break; // Something bad happened if (fileStream.Read(JolietETime, 0, 17) != 17) break; // Something bad happened break; } } counter++; } DecodedVolumeDescriptor decodedVD = new DecodedVolumeDescriptor(); DecodedVolumeDescriptor decodedJolietVD = new DecodedVolumeDescriptor(); decodedVD = DecodeVolumeDescriptor(VDSysId, VDVolId, VDVolSetId, VDPubId, VDDataPrepId, VDAppId, VCTime, VMTime, VXTime, VETime); if(Joliet) decodedJolietVD = DecodeJolietDescriptor(JolietSysId, JolietVolId, JolietVolSetId, JolietPubId, JolietDataPrepId, JolietAppId, JolietCTime, JolietMTime, JolietXTime, JolietETime); int i = BitConverter.ToInt32(VDPathTableStart, 0); fileStream.Seek((i * 2048)+2, SeekOrigin.Begin); // Seek to first path table location field // Check for Rock Ridge if (fileStream.Read(RootDirectoryLocation, 0, 4) == 4) { fileStream.Seek((BitConverter.ToInt32(RootDirectoryLocation,0) * 2048)+34, SeekOrigin.Begin); // Seek to root directory, first entry, system use field byte[] SUSPMagic = new byte[2]; byte[] RRMagic = new byte[2]; fileStream.Read(SUSPMagic, 0, 2); if (Encoding.ASCII.GetString(SUSPMagic) == "SP") { fileStream.Seek(5, SeekOrigin.Current); // Seek for rock ridge magic fileStream.Read(RRMagic, 0, 2); if (Encoding.ASCII.GetString(RRMagic) == "RR") { RockRidge = true; } } } #region SEGA IP.BIN Read and decoding bool SegaCD = false; bool Saturn = false; bool Dreamcast = false; StringBuilder IPBinInformation = new StringBuilder(); byte[] SegaHardwareID = new byte[16]; fileStream.Seek(0, SeekOrigin.Begin); // Seek to start (again) fileStream.Read(SegaHardwareID, 0, 16); switch (Encoding.ASCII.GetString(SegaHardwareID)) { case "SEGADISCSYSTEM ": case "SEGADATADISC ": case "SEGAOS ": { SegaCD = true; // Ok, this contains SegaCD IP.BIN IPBinInformation.AppendLine("--------------------------------"); IPBinInformation.AppendLine("SEGA IP.BIN INFORMATION:"); IPBinInformation.AppendLine("--------------------------------"); // Definitions following byte[] volume_name = new byte[11]; // Varies byte[] spare_space1 = new byte[1]; // 0x00 byte[] volume_version = new byte[2]; // Volume version in BCD. <100 = Prerelease. byte[] volume_type = new byte[2]; // Bit 0 = 1 => CD-ROM. Rest should be 0. byte[] system_name = new byte[11]; // Unknown, varies! byte[] spare_space2 = new byte[1]; // 0x00 byte[] system_version = new byte[2]; // Should be 1 byte[] spare_space3 = new byte[2]; // 0x0000 byte[] ip_address = new byte[4]; // Initial program address byte[] ip_loadsize = new byte[4]; // Load size of initial program byte[] ip_entry_address = new byte[4]; // Initial program entry address byte[] ip_work_ram_size = new byte[4]; // Initial program work RAM size in bytes byte[] sp_address = new byte[4]; // System program address byte[] sp_loadsize = new byte[4]; // Load size of system program byte[] sp_entry_address = new byte[4]; // System program entry address byte[] sp_work_ram_size = new byte[4]; // System program work RAM size in bytes byte[] release_date = new byte[8]; // MMDDYYYY byte[] unknown1 = new byte[7]; // Seems to be all 0x20s byte[] spare_space4 = new byte[1]; // 0x00 ? byte[] system_reserved = new byte[160]; // System Reserved Area byte[] hardware_id = new byte[16]; // Hardware ID byte[] copyright = new byte[3]; // "(C)" -- Can be the developer code directly!, if that is the code release date will be displaced byte[] developer_code = new byte[5]; // "SEGA" or "T-xx" byte[] unknown2 = new byte[1]; // Seems to be part of developer code, need to get a SEGA disc to check byte[] release_date2 = new byte[8]; // Another release date, this with month in letters? byte[] domestic_title = new byte[48]; // Domestic version of the game title byte[] overseas_title = new byte[48]; // Overseas version of the game title byte[] application_type = new byte[2]; // Application type byte[] space_space5 = new byte[1]; // 0x20 byte[] product_code = new byte[13]; // Official product code byte[] peripherals = new byte[16]; // Supported peripherals, see above byte[] spare_space6 = new byte[16]; // 0x20 byte[] spare_space7 = new byte[64]; // Inside here should be modem information, but I need to get a modem-enabled game byte[] region_codes = new byte[16]; // Region codes, space-filled //Reading all data fileStream.Read(volume_name, 0, 11); // Varies fileStream.Read(spare_space1, 0, 1); // 0x00 fileStream.Read(volume_version, 0, 2); // Volume version in BCD. <100 = Prerelease. fileStream.Read(volume_type, 0, 2); // Bit 0 = 1 => CD-ROM. Rest should be 0. fileStream.Read(system_name, 0, 11); // Unknown, varies! fileStream.Read(spare_space2, 0, 1); // 0x00 fileStream.Read(system_version, 0, 2); // Should be 1 fileStream.Read(spare_space3, 0, 2); // 0x0000 fileStream.Read(ip_address, 0, 4); // Initial program address fileStream.Read(ip_loadsize, 0, 4); // Load size of initial program fileStream.Read(ip_entry_address, 0, 4); // Initial program entry address fileStream.Read(ip_work_ram_size, 0, 4); // Initial program work RAM size in bytes fileStream.Read(sp_address, 0, 4); // System program address fileStream.Read(sp_loadsize, 0, 4); // Load size of system program fileStream.Read(sp_entry_address, 0, 4); // System program entry address fileStream.Read(sp_work_ram_size, 0, 4); // System program work RAM size in bytes fileStream.Read(release_date, 0, 8); // MMDDYYYY fileStream.Read(unknown1, 0, 7); // Seems to be all 0x20s fileStream.Read(spare_space4, 0, 1); // 0x00 ? fileStream.Read(system_reserved, 0, 160); // System Reserved Area fileStream.Read(hardware_id, 0, 16); // Hardware ID fileStream.Read(copyright, 0, 3); // "(C)" -- Can be the developer code directly!, if that is the code release date will be displaced if (Encoding.ASCII.GetString(copyright) != "(C)") fileStream.Seek(-3, SeekOrigin.Current); fileStream.Read(developer_code, 0, 5); // "SEGA" or "T-xx" if (Encoding.ASCII.GetString(copyright) != "(C)") fileStream.Seek(1, SeekOrigin.Current); fileStream.Read(release_date2, 0, 8); // Another release date, this with month in letters? if (Encoding.ASCII.GetString(copyright) != "(C)") fileStream.Seek(2, SeekOrigin.Current); fileStream.Read(domestic_title, 0, 48); // Domestic version of the game title fileStream.Read(overseas_title, 0, 48); // Overseas version of the game title fileStream.Read(application_type, 0, 2); // Application type fileStream.Read(space_space5, 0, 1); // 0x20 fileStream.Read(product_code, 0, 13); // Official product code fileStream.Read(peripherals, 0, 16); // Supported peripherals, see above fileStream.Read(spare_space6, 0, 16); // 0x20 fileStream.Read(spare_space7, 0, 64); // Inside here should be modem information, but I need to get a modem-enabled game fileStream.Read(region_codes, 0, 16); // Region codes, space-filled // Decoding all data DateTime ipbindate = new DateTime(); CultureInfo provider = CultureInfo.InvariantCulture; ipbindate = DateTime.ParseExact(Encoding.ASCII.GetString(release_date), "MMddyyyy", provider); switch (Encoding.ASCII.GetString(application_type)) { case "GM": IPBinInformation.AppendLine("Disc is a game."); break; case "AI": IPBinInformation.AppendLine("Disc is an application."); break; default: IPBinInformation.AppendLine("Disc is from unknown type."); break; } IPBinInformation.AppendFormat("Volume name: {0}", Encoding.ASCII.GetString(volume_name)).AppendLine(); //IPBinInformation.AppendFormat("Volume version: {0}", Encoding.ASCII.GetString(volume_version)).AppendLine(); //IPBinInformation.AppendFormat("{0}", Encoding.ASCII.GetString(volume_type)).AppendLine(); IPBinInformation.AppendFormat("System name: {0}", Encoding.ASCII.GetString(system_name)).AppendLine(); //IPBinInformation.AppendFormat("System version: {0}", Encoding.ASCII.GetString(system_version)).AppendLine(); IPBinInformation.AppendFormat("Initial program address: 0x{0}", BitConverter.ToInt32(ip_address, 0).ToString("X")).AppendLine(); IPBinInformation.AppendFormat("Initial program load size: {0} bytes", BitConverter.ToInt32(ip_loadsize, 0)).AppendLine(); IPBinInformation.AppendFormat("Initial program entry address: 0x{0}", BitConverter.ToInt32(ip_entry_address, 0).ToString("X")).AppendLine(); IPBinInformation.AppendFormat("Initial program work RAM: {0} bytes", BitConverter.ToInt32(ip_work_ram_size, 0)).AppendLine(); IPBinInformation.AppendFormat("System program address: 0x{0}", BitConverter.ToInt32(sp_address, 0).ToString("X")).AppendLine(); IPBinInformation.AppendFormat("System program load size: {0} bytes", BitConverter.ToInt32(sp_loadsize, 0)).AppendLine(); IPBinInformation.AppendFormat("System program entry address: 0x{0}", BitConverter.ToInt32(sp_entry_address, 0).ToString("X")).AppendLine(); IPBinInformation.AppendFormat("System program work RAM: {0} bytes", BitConverter.ToInt32(sp_work_ram_size, 0)).AppendLine(); IPBinInformation.AppendFormat("Release date: {0}", ipbindate.ToString()).AppendLine(); IPBinInformation.AppendFormat("Release date (other format): {0}", Encoding.ASCII.GetString(release_date2)).AppendLine(); IPBinInformation.AppendFormat("Hardware ID: {0}", Encoding.ASCII.GetString(hardware_id)).AppendLine(); IPBinInformation.AppendFormat("Developer code: {0}", Encoding.ASCII.GetString(developer_code)).AppendLine(); IPBinInformation.AppendFormat("Domestic title: {0}", Encoding.ASCII.GetString(domestic_title)).AppendLine(); IPBinInformation.AppendFormat("Overseas title: {0}", Encoding.ASCII.GetString(overseas_title)).AppendLine(); IPBinInformation.AppendFormat("Product code: {0}", Encoding.ASCII.GetString(product_code)).AppendLine(); IPBinInformation.AppendFormat("Peripherals:").AppendLine(); foreach(byte peripheral in peripherals) { switch((char)peripheral) { case 'A': IPBinInformation.AppendLine("Game supports analog controller."); break; case 'B': IPBinInformation.AppendLine("Game supports trackball."); break; case 'G': IPBinInformation.AppendLine("Game supports light gun."); break; case 'J': IPBinInformation.AppendLine("Game supports JoyPad."); break; case 'K': IPBinInformation.AppendLine("Game supports keyboard."); break; case 'M': IPBinInformation.AppendLine("Game supports mouse."); break; case 'O': IPBinInformation.AppendLine("Game supports Master System's JoyPad."); break; case 'P': IPBinInformation.AppendLine("Game supports printer interface."); break; case 'R': IPBinInformation.AppendLine("Game supports serial (RS-232C) interface."); break; case 'T': IPBinInformation.AppendLine("Game supports tablet interface."); break; case 'V': IPBinInformation.AppendLine("Game supports paddle controller."); break; case ' ': break; default: IPBinInformation.AppendFormat("Game supports unknown peripheral {0}.", peripheral.ToString()).AppendLine(); break; } } IPBinInformation.AppendLine("Regions supported:"); foreach (byte region in region_codes) { switch ((char)region) { case 'J': IPBinInformation.AppendLine("Japanese NTSC."); break; case 'U': IPBinInformation.AppendLine("USA NTSC."); break; case 'E': IPBinInformation.AppendLine("Europe PAL."); break; case ' ': break; default: IPBinInformation.AppendFormat("Game supports unknown region {0}.", region.ToString()).AppendLine(); break; } } break; } case "SEGA SEGASATURN ": { Saturn = true; IPBinInformation.AppendLine("--------------------------------"); IPBinInformation.AppendLine("SEGA IP.BIN INFORMATION:"); IPBinInformation.AppendLine("--------------------------------"); // Definitions following byte[] maker_id = new byte[16]; // "SEGA ENTERPRISES" byte[] product_no = new byte[10]; // Product number byte[] product_version = new byte[6]; // Product version byte[] release_date = new byte[8]; // YYYYMMDD byte[] saturn_media = new byte[3]; // "CD-" byte[] disc_no = new byte[1]; // Disc number byte[] disc_no_separator = new byte[1]; // '/' byte[] disc_total_nos = new byte[1]; // Total number of discs byte[] spare_space1 = new byte[2]; // " " byte[] region_codes = new byte[10]; // Region codes, space-filled byte[] spare_space2 = new byte[6]; // " " byte[] peripherals = new byte[16]; // Supported peripherals, see above byte[] product_name = new byte[112]; // Game name, space-filled // Reading all data fileStream.Read(maker_id, 0, 16); // "SEGA ENTERPRISES" fileStream.Read(product_no, 0, 10); // Product number fileStream.Read(product_version, 0, 6); // Product version fileStream.Read(release_date, 0, 8); // YYYYMMDD fileStream.Read(saturn_media, 0, 3); // "CD-" fileStream.Read(disc_no, 0, 1); // Disc number fileStream.Read(disc_no_separator, 0, 1); // '/' fileStream.Read(disc_total_nos, 0, 1); // Total number of discs fileStream.Read(spare_space1, 0, 2); // " " fileStream.Read(region_codes, 0, 10); // Region codes, space-filled fileStream.Read(spare_space2, 0, 6); // " " fileStream.Read(peripherals, 0, 16); // Supported peripherals, see above fileStream.Read(product_name, 0, 112); // Game name, space-filled // Decoding all data DateTime ipbindate = new DateTime(); CultureInfo provider = CultureInfo.InvariantCulture; ipbindate = DateTime.ParseExact(Encoding.ASCII.GetString(release_date), "yyyyMMdd", provider); IPBinInformation.AppendFormat("Product name: {0}", Encoding.ASCII.GetString(product_name)).AppendLine(); IPBinInformation.AppendFormat("Product number: {0}", Encoding.ASCII.GetString(product_no)).AppendLine(); IPBinInformation.AppendFormat("Product version: {0}", Encoding.ASCII.GetString(product_version)).AppendLine(); IPBinInformation.AppendFormat("Release date: {0}", ipbindate.ToString()).AppendLine(); IPBinInformation.AppendFormat("Disc number {0} of {1}", Encoding.ASCII.GetString(disc_no), Encoding.ASCII.GetString(disc_total_nos)).AppendLine(); IPBinInformation.AppendFormat("Peripherals:").AppendLine(); foreach (byte peripheral in peripherals) { switch ((char)peripheral) { case 'A': IPBinInformation.AppendLine("Game supports analog controller."); break; case 'J': IPBinInformation.AppendLine("Game supports JoyPad."); break; case 'K': IPBinInformation.AppendLine("Game supports keyboard."); break; case 'M': IPBinInformation.AppendLine("Game supports mouse."); break; case 'S': IPBinInformation.AppendLine("Game supports analog steering controller."); break; case 'T': IPBinInformation.AppendLine("Game supports multitap."); break; case ' ': break; default: IPBinInformation.AppendFormat("Game supports unknown peripheral {0}.", peripheral.ToString()).AppendLine(); break; } } IPBinInformation.AppendLine("Regions supported:"); foreach (byte region in region_codes) { switch ((char)region) { case 'J': IPBinInformation.AppendLine("Japanese NTSC."); break; case 'U': IPBinInformation.AppendLine("North America NTSC."); break; case 'E': IPBinInformation.AppendLine("Europe PAL."); break; case 'T': IPBinInformation.AppendLine("Asia NTSC."); break; case ' ': break; default: IPBinInformation.AppendFormat("Game supports unknown region {0}.", region.ToString()).AppendLine(); break; } } break; } case "SEGA SEGAKATANA ": { Dreamcast = true; IPBinInformation.AppendLine("--------------------------------"); IPBinInformation.AppendLine("SEGA IP.BIN INFORMATION:"); IPBinInformation.AppendLine("--------------------------------"); // Declarations following byte[] maker_id = new byte[16]; // "SEGA ENTERPRISES" byte[] dreamcast_crc = new byte[4]; // CRC of product_no and product_version byte[] spare_space1 = new byte[1]; // " " byte[] dreamcast_media = new byte[6]; // "GD-ROM" byte[] disc_no = new byte[1]; // Disc number byte[] disc_no_separator = new byte[1]; // '/' byte[] disc_total_nos = new byte[1]; // Total number of discs byte[] spare_space2 = new byte[2]; // " " byte[] region_codes = new byte[8]; // Region codes, space-filled byte[] peripherals = new byte[4]; // Supported peripherals, bitwise byte[] product_no = new byte[10]; // Product number byte[] product_version = new byte[6]; // Product version byte[] release_date = new byte[8]; // YYYYMMDD byte[] spare_space3 = new byte[8]; // " " byte[] boot_filename = new byte[12]; // Usually "1ST_READ.BIN" or "0WINCE.BIN " byte[] producer = new byte[16]; // Game producer, space-filled byte[] product_name = new byte[128]; // Game name, space-filled // Reading all data fileStream.Read(maker_id, 0, 16); // "SEGA ENTERPRISES" fileStream.Read(dreamcast_crc, 0, 4); // CRC of product_no and product_version fileStream.Read(spare_space1, 0, 1); // " " fileStream.Read(dreamcast_media, 0, 6); // "GD-ROM" fileStream.Read(disc_no, 0, 1); // Disc number fileStream.Read(disc_no_separator, 0, 1); // '/' fileStream.Read(disc_total_nos, 0, 1); // Total number of discs fileStream.Read(spare_space2, 0, 2); // " " fileStream.Read(region_codes, 0, 8); // Region codes, space-filled fileStream.Read(peripherals, 0, 4); // Supported peripherals, bitwise fileStream.Read(product_no, 0, 10); // Product number fileStream.Read(product_version, 0, 6); // Product version fileStream.Read(release_date, 0, 8); // YYYYMMDD fileStream.Read(spare_space3, 0, 8); // " " fileStream.Read(boot_filename, 0, 12); // Usually "1ST_READ.BIN" or "0WINCE.BIN " fileStream.Read(producer, 0, 16); // Game producer, space-filled fileStream.Read(product_name, 0, 128); // Game name, space-filled // Decoding all data DateTime ipbindate = new DateTime(); CultureInfo provider = CultureInfo.InvariantCulture; ipbindate = DateTime.ParseExact(Encoding.ASCII.GetString(release_date), "yyyyMMdd", provider); IPBinInformation.AppendFormat("Product name: {0}", Encoding.ASCII.GetString(product_name)).AppendLine(); IPBinInformation.AppendFormat("Product version: {0}", Encoding.ASCII.GetString(product_version)).AppendLine(); IPBinInformation.AppendFormat("Producer: {0}", Encoding.ASCII.GetString(producer)).AppendLine(); IPBinInformation.AppendFormat("Disc media: {0}", Encoding.ASCII.GetString(dreamcast_media)).AppendLine(); IPBinInformation.AppendFormat("Disc number {0} of {1}", Encoding.ASCII.GetString(disc_no), Encoding.ASCII.GetString(disc_total_nos)).AppendLine(); IPBinInformation.AppendFormat("Release date: {0}", ipbindate.ToString()).AppendLine(); switch (Encoding.ASCII.GetString(boot_filename)) { case "1ST_READ.BIN": IPBinInformation.AppendLine("Disc boots natively."); break; case "0WINCE.BIN ": IPBinInformation.AppendLine("Disc boots using Windows CE."); break; default: IPBinInformation.AppendFormat("Disc boots using unknown loader: {0}.", Encoding.ASCII.GetString(boot_filename)).AppendLine(); break; } IPBinInformation.AppendLine("Regions supported:"); foreach (byte region in region_codes) { switch ((char)region) { case 'J': IPBinInformation.AppendLine("Japanese NTSC."); break; case 'U': IPBinInformation.AppendLine("North America NTSC."); break; case 'E': IPBinInformation.AppendLine("Europe PAL."); break; case ' ': break; default: IPBinInformation.AppendFormat("Game supports unknown region {0}.", region.ToString()).AppendLine(); break; } } int iPeripherals = BitConverter.ToInt32(peripherals, 0); if((iPeripherals & 0x00000010) == 0x00000010) IPBinInformation.AppendLine("Game uses Windows CE."); IPBinInformation.AppendFormat("Peripherals:").AppendLine(); if ((iPeripherals & 0x00000100) == 0x00000100) IPBinInformation.AppendLine("Game supports the VGA Box."); if ((iPeripherals & 0x00001000) == 0x00001000) IPBinInformation.AppendLine("Game supports other expansion."); if ((iPeripherals & 0x00002000) == 0x00002000) IPBinInformation.AppendLine("Game supports Puru Puru pack."); if ((iPeripherals & 0x00004000) == 0x00004000) IPBinInformation.AppendLine("Game supports Mike Device."); if ((iPeripherals & 0x00008000) == 0x00008000) IPBinInformation.AppendLine("Game supports Memory Card."); if ((iPeripherals & 0x00010000) == 0x00010000) IPBinInformation.AppendLine("Game requires A + B + Start buttons and D-Pad."); if ((iPeripherals & 0x00020000) == 0x00020000) IPBinInformation.AppendLine("Game requires C button."); if ((iPeripherals & 0x00040000) == 0x00040000) IPBinInformation.AppendLine("Game requires D button."); if ((iPeripherals & 0x00080000) == 0x00080000) IPBinInformation.AppendLine("Game requires X button."); if ((iPeripherals & 0x00100000) == 0x00100000) IPBinInformation.AppendLine("Game requires Y button."); if ((iPeripherals & 0x00200000) == 0x00200000) IPBinInformation.AppendLine("Game requires Z button."); if ((iPeripherals & 0x00400000) == 0x00400000) IPBinInformation.AppendLine("Game requires expanded direction buttons."); if ((iPeripherals & 0x00800000) == 0x00800000) IPBinInformation.AppendLine("Game requires analog R trigger."); if ((iPeripherals & 0x01000000) == 0x01000000) IPBinInformation.AppendLine("Game requires analog L trigger."); if ((iPeripherals & 0x02000000) == 0x02000000) IPBinInformation.AppendLine("Game requires analog horizontal controller."); if ((iPeripherals & 0x04000000) == 0x04000000) IPBinInformation.AppendLine("Game requires analog vertical controller."); if ((iPeripherals & 0x08000000) == 0x08000000) IPBinInformation.AppendLine("Game requires expanded analog horizontal controller."); if ((iPeripherals & 0x10000000) == 0x10000000) IPBinInformation.AppendLine("Game requires expanded analog vertical controller."); if ((iPeripherals & 0x20000000) == 0x20000000) IPBinInformation.AppendLine("Game supports Gun."); if ((iPeripherals & 0x40000000) == 0x40000000) IPBinInformation.AppendLine("Game supports Keyboard."); if ((iPeripherals & 0x80000000) == 0x80000000) IPBinInformation.AppendLine("Game supports Mouse."); break; } } #endregion ISOMetadata.AppendFormat("ISO9660 file system").AppendLine(); if(Joliet) ISOMetadata.AppendFormat("Joliet extensions present.").AppendLine(); if (RockRidge) ISOMetadata.AppendFormat("Rock Ridge Interchange Protocol present.").AppendLine(); if (Bootable) ISOMetadata.AppendFormat("Disc bootable following {0} specifications.", BootSpec).AppendLine(); if (SegaCD) { ISOMetadata.AppendLine("This is a SegaCD / MegaCD disc."); ISOMetadata.AppendLine(IPBinInformation.ToString()); } if (Saturn) { ISOMetadata.AppendLine("This is a Sega Saturn disc."); ISOMetadata.AppendLine(IPBinInformation.ToString()); } if (Dreamcast) { ISOMetadata.AppendLine("This is a Sega Dreamcast disc."); ISOMetadata.AppendLine(IPBinInformation.ToString()); } ISOMetadata.AppendLine("--------------------------------"); ISOMetadata.AppendLine("VOLUME DESCRIPTOR INFORMATION:"); ISOMetadata.AppendLine("--------------------------------"); ISOMetadata.AppendFormat("System identifier: {0}", decodedVD.SystemIdentifier).AppendLine(); ISOMetadata.AppendFormat("Volume identifier: {0}", decodedVD.VolumeIdentifier).AppendLine(); ISOMetadata.AppendFormat("Volume set identifier: {0}", decodedVD.VolumeSetIdentifier).AppendLine(); ISOMetadata.AppendFormat("Publisher identifier: {0}", decodedVD.PublisherIdentifier).AppendLine(); ISOMetadata.AppendFormat("Data preparer identifier: {0}", decodedVD.DataPreparerIdentifier).AppendLine(); ISOMetadata.AppendFormat("Application identifier: {0}", decodedVD.ApplicationIdentifier).AppendLine(); ISOMetadata.AppendFormat("Volume creation date: {0}", decodedVD.CreationTime.ToString()).AppendLine(); if (decodedVD.HasModificationTime) ISOMetadata.AppendFormat("Volume modification date: {0}", decodedVD.ModificationTime.ToString()).AppendLine(); else ISOMetadata.AppendFormat("Volume has not been modified.").AppendLine(); if (decodedVD.HasExpirationTime) ISOMetadata.AppendFormat("Volume expiration date: {0}", decodedVD.ExpirationTime.ToString()).AppendLine(); else ISOMetadata.AppendFormat("Volume does not expire.").AppendLine(); if (decodedVD.HasEffectiveTime) ISOMetadata.AppendFormat("Volume effective date: {0}", decodedVD.EffectiveTime.ToString()).AppendLine(); else ISOMetadata.AppendFormat("Volume has always been effective.").AppendLine(); if(Joliet) { ISOMetadata.AppendLine("---------------------------------------"); ISOMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:"); ISOMetadata.AppendLine("---------------------------------------"); ISOMetadata.AppendFormat("System identifier: {0}", decodedJolietVD.SystemIdentifier).AppendLine(); ISOMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVD.VolumeIdentifier).AppendLine(); ISOMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVD.VolumeSetIdentifier).AppendLine(); ISOMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVD.PublisherIdentifier).AppendLine(); ISOMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVD.DataPreparerIdentifier).AppendLine(); ISOMetadata.AppendFormat("Application identifier: {0}", decodedJolietVD.ApplicationIdentifier).AppendLine(); ISOMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVD.CreationTime.ToString()).AppendLine(); if (decodedJolietVD.HasModificationTime) ISOMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVD.ModificationTime.ToString()).AppendLine(); else ISOMetadata.AppendFormat("Volume has not been modified.").AppendLine(); if (decodedJolietVD.HasExpirationTime) ISOMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVD.ExpirationTime.ToString()).AppendLine(); else ISOMetadata.AppendFormat("Volume does not expire.").AppendLine(); if (decodedJolietVD.HasEffectiveTime) ISOMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVD.EffectiveTime.ToString()).AppendLine(); else ISOMetadata.AppendFormat("Volume has always been effective.").AppendLine(); } information = ISOMetadata.ToString(); } private DecodedVolumeDescriptor DecodeJolietDescriptor(byte[] VDSysId, byte[] VDVolId, byte[] VDVolSetId, byte[] VDPubId, byte[] VDDataPrepId, byte[] VDAppId, byte[] VCTime, byte[] VMTime, byte[] VXTime, byte[] VETime) { DecodedVolumeDescriptor decodedVD = new DecodedVolumeDescriptor(); decodedVD.SystemIdentifier = Encoding.BigEndianUnicode.GetString(VDSysId); decodedVD.VolumeIdentifier = Encoding.BigEndianUnicode.GetString(VDVolId); decodedVD.VolumeSetIdentifier = Encoding.BigEndianUnicode.GetString(VDVolSetId); decodedVD.PublisherIdentifier = Encoding.BigEndianUnicode.GetString(VDPubId); decodedVD.DataPreparerIdentifier = Encoding.BigEndianUnicode.GetString(VDDataPrepId); decodedVD.ApplicationIdentifier = Encoding.BigEndianUnicode.GetString(VDAppId); if (VCTime[0] == '0' || VCTime[0] == 0x00) decodedVD.CreationTime = DateTime.MinValue; else decodedVD.CreationTime = DateHandlers.ISO9660ToDateTime(VCTime); if (VMTime[0] == '0' || VMTime[0] == 0x00) { decodedVD.HasModificationTime = false; } else { decodedVD.HasModificationTime = true; decodedVD.ModificationTime = DateHandlers.ISO9660ToDateTime(VMTime); } if (VXTime[0] == '0' || VXTime[0] == 0x00) { decodedVD.HasExpirationTime = false; } else { decodedVD.HasExpirationTime = true; decodedVD.ExpirationTime = DateHandlers.ISO9660ToDateTime(VXTime); } if (VETime[0] == '0' || VETime[0] == 0x00) { decodedVD.HasEffectiveTime = false; } else { decodedVD.HasEffectiveTime = true; decodedVD.EffectiveTime = DateHandlers.ISO9660ToDateTime(VETime); } return decodedVD; } private DecodedVolumeDescriptor DecodeVolumeDescriptor(byte[] VDSysId, byte[] VDVolId, byte[] VDVolSetId, byte[] VDPubId, byte[] VDDataPrepId, byte[] VDAppId, byte[] VCTime, byte[] VMTime, byte[] VXTime, byte[] VETime) { DecodedVolumeDescriptor decodedVD = new DecodedVolumeDescriptor(); decodedVD.SystemIdentifier = Encoding.ASCII.GetString(VDSysId); decodedVD.VolumeIdentifier = Encoding.ASCII.GetString(VDVolId); decodedVD.VolumeSetIdentifier = Encoding.ASCII.GetString(VDVolSetId); decodedVD.PublisherIdentifier = Encoding.ASCII.GetString(VDPubId); decodedVD.DataPreparerIdentifier = Encoding.ASCII.GetString(VDDataPrepId); decodedVD.ApplicationIdentifier = Encoding.ASCII.GetString(VDAppId); if (VCTime[0] == '0' || VCTime[0] == 0x00) decodedVD.CreationTime = DateTime.MinValue; else decodedVD.CreationTime = DateHandlers.ISO9660ToDateTime(VCTime); if (VMTime[0] == '0' || VMTime[0] == 0x00) { decodedVD.HasModificationTime = false; } else { decodedVD.HasModificationTime = true; decodedVD.ModificationTime = DateHandlers.ISO9660ToDateTime(VMTime); } if (VXTime[0] == '0' || VXTime[0] == 0x00) { decodedVD.HasExpirationTime = false; } else { decodedVD.HasExpirationTime = true; decodedVD.ExpirationTime = DateHandlers.ISO9660ToDateTime(VXTime); } if (VETime[0] == '0' || VETime[0] == 0x00) { decodedVD.HasEffectiveTime = false; } else { decodedVD.HasEffectiveTime = true; decodedVD.EffectiveTime = DateHandlers.ISO9660ToDateTime(VETime); } return decodedVD; } } }