commit beec65407a64973d74fcfed1e3ac0da89e5fd566 Author: Natalia Portillo Date: Thu Mar 3 18:34:33 2011 +0000 First commit to SVN git-svn-id: svn://claunia.com/FileSystemIDandChk@1 17725271-3d32-4980-a8cb-9ff532f270ba diff --git a/FileSystemIDandChk.sln b/FileSystemIDandChk.sln new file mode 100644 index 000000000..905c56a05 --- /dev/null +++ b/FileSystemIDandChk.sln @@ -0,0 +1,24 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSystemIDandChk", "FileSystemIDandChk\FileSystemIDandChk.csproj", "{7A4B05BE-73C9-4F34-87FE-E80CCF1F732D}" +EndProject +Project("{9344bdbb-3e7f-41fc-a0dd-8665d75ee146}") = "Packages", "Packages.mdproj", "{8996EF59-09B9-4920-A3DE-2F8EA2EBBCFF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7A4B05BE-73C9-4F34-87FE-E80CCF1F732D}.Debug|x86.ActiveCfg = Debug|x86 + {7A4B05BE-73C9-4F34-87FE-E80CCF1F732D}.Debug|x86.Build.0 = Debug|x86 + {7A4B05BE-73C9-4F34-87FE-E80CCF1F732D}.Release|x86.ActiveCfg = Release|x86 + {7A4B05BE-73C9-4F34-87FE-E80CCF1F732D}.Release|x86.Build.0 = Release|x86 + {8996EF59-09B9-4920-A3DE-2F8EA2EBBCFF}.Debug|x86.ActiveCfg = Debug|Any CPU + {8996EF59-09B9-4920-A3DE-2F8EA2EBBCFF}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = FileSystemIDandChk\FileSystemIDandChk.csproj + EndGlobalSection +EndGlobal diff --git a/FileSystemIDandChk/AssemblyInfo.cs b/FileSystemIDandChk/AssemblyInfo.cs new file mode 100644 index 000000000..6aa320b4e --- /dev/null +++ b/FileSystemIDandChk/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("FileSystemIDandChk")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/FileSystemIDandChk/CToString.cs b/FileSystemIDandChk/CToString.cs new file mode 100644 index 000000000..34039c1ac --- /dev/null +++ b/FileSystemIDandChk/CToString.cs @@ -0,0 +1,24 @@ +using System; +using System.Text; + +namespace FileSystemIDandChk +{ + public static class StringHandlers + { + public static string CToString (byte[] CString) + { + StringBuilder sb = new StringBuilder(); + + for(int i = 0; i + + + Debug + x86 + 9.0.21022 + 2.0 + {7A4B05BE-73C9-4F34-87FE-E80CCF1F732D} + Exe + FileSystemIDandChk + FileSystemIDandChk + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + x86 + true + + + none + false + bin\Release + prompt + 4 + x86 + true + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FileSystemIDandChk/Main.cs b/FileSystemIDandChk/Main.cs new file mode 100644 index 000000000..cf4310a41 --- /dev/null +++ b/FileSystemIDandChk/Main.cs @@ -0,0 +1,230 @@ +using System; +using System.IO; +using System.Collections.Generic; +using FileSystemIDandChk.Plugins; +using FileSystemIDandChk.PartPlugins; + +namespace FileSystemIDandChk +{ + class MainClass + { + static PluginBase plugins; + public static bool chkPartitions; + public static bool chkFilesystems; + public static bool isDebug; + + public static void Main (string[] args) + { + plugins = new PluginBase(); + + chkPartitions = true; + chkFilesystems = true; + isDebug = false; + + Console.WriteLine ("Filesystem Identificator and Checker"); + Console.WriteLine ("Copyright (C) Natalia Portillo, All Rights Reserved"); + + if(args.Length==0) + { + Usage(); + } + else if(args.Length==1) + { + plugins.RegisterAllPlugins(); + + if(args[0]=="--formats") + { + Console.WriteLine("Supported filesystems:"); + foreach(KeyValuePair kvp in plugins.PluginsList) + Console.WriteLine(kvp.Value.Name); + Console.WriteLine(); + Console.WriteLine("Supported partitions:"); + foreach(KeyValuePair kvp in plugins.PartPluginsList) + Console.WriteLine(kvp.Value.Name); + } + else + Runner(args[0]); + } + else + { + for(int i = 0; i id_plugins; + Plugin _plugin; + string information; + bool checkraw = false; + + try + { + stream = File.OpenRead(filename); + + if(chkPartitions) + { + List partitions = new List(); + string partition_scheme = ""; + + foreach (PartPlugin _partplugin in plugins.PartPluginsList.Values) + { + List _partitions; + + if (_partplugin.GetInformation(stream, out _partitions)) + { + partition_scheme=_partplugin.Name; + partitions = _partitions; + break; + } + } + + if(partition_scheme=="") + { + if(!chkFilesystems) + { + Console.WriteLine("No partitions founds, not searching for filesystems"); + return; + } + else + checkraw = true; + } + else + { + Console.WriteLine("Partition scheme identified as {0}", partition_scheme); + Console.WriteLine("{0} partitions found.", partitions.Count); + + for(int i = 0; i< partitions.Count; i++) + { + Console.WriteLine(); + Console.WriteLine("Partition {0}:", partitions[i].PartitionSequence); + Console.WriteLine("Partition name: {0}", partitions[i].PartitionName); + Console.WriteLine("Partition type: {0}", partitions[i].PartitionType); + Console.WriteLine("Partition start: {0}", partitions[i].PartitionStart); + Console.WriteLine("Partition length: {0}", partitions[i].PartitionLength); + Console.WriteLine("Partition description:"); + Console.WriteLine(partitions[i].PartitionDescription); + + if(chkFilesystems) + { + Console.WriteLine("Identifying filesystem on partition"); + + Identify(stream, out id_plugins, partitions[i].PartitionStart); + if(id_plugins.Count==0) + Console.WriteLine("Filesystem not identified"); + else if(id_plugins.Count>1) + { + Console.WriteLine(String.Format("Identified by {0} plugins", id_plugins.Count)); + + foreach(string plugin_name in id_plugins) + { + if(plugins.PluginsList.TryGetValue(plugin_name, out _plugin)) + { + Console.WriteLine(String.Format("As identified by {0}.", _plugin.Name)); + _plugin.GetInformation(stream, partitions[i].PartitionStart, out information); + Console.Write(information); + } + } + } + else + { + plugins.PluginsList.TryGetValue(id_plugins[0], out _plugin); + Console.WriteLine(String.Format("Identified by {0}.", _plugin.Name)); + _plugin.GetInformation(stream, partitions[i].PartitionStart, out information); + Console.Write(information); + } + } + } + } + } + + if(checkraw) + { + Identify(stream, out id_plugins, 0); + if(id_plugins.Count==0) + Console.WriteLine("Filesystem not identified"); + else if(id_plugins.Count>1) + { + Console.WriteLine(String.Format("Identified by {0} plugins", id_plugins.Count)); + + foreach(string plugin_name in id_plugins) + { + if(plugins.PluginsList.TryGetValue(plugin_name, out _plugin)) + { + Console.WriteLine(String.Format("As identified by {0}.", _plugin.Name)); + _plugin.GetInformation(stream, 0, out information); + Console.Write(information); + } + } + } + else + { + plugins.PluginsList.TryGetValue(id_plugins[0], out _plugin); + Console.WriteLine(String.Format("Identified by {0}.", _plugin.Name)); + _plugin.GetInformation(stream, 0, out information); + Console.Write(information); + } + } + } + catch(Exception ex) + { + Console.WriteLine(String.Format("Error reading file: {0}", ex.Message)); + if(isDebug) + Console.WriteLine(ex.StackTrace); + } + finally + { + stream.Close(); + } + } + + private static void Identify (FileStream stream, out List id_plugins, long offset) + { + id_plugins = new List(); + + foreach (Plugin _plugin in plugins.PluginsList.Values) + { + if (_plugin.Identify(stream, offset)) + id_plugins.Add(_plugin.Name.ToLower()); + } + } + + private static void Usage () + { + Console.WriteLine("Usage: filesystemidandchk [options] file"); + Console.WriteLine(); + Console.WriteLine(" --formats List all suported partition and filesystems"); + Console.WriteLine(" --debug Show debug information"); + Console.WriteLine(" --partitions Check only for partitions"); + Console.WriteLine(" --filesystems Check only for filesystems"); + Console.WriteLine(" --all Check for partitions and filesystems (default)"); + Console.WriteLine(); + } + } +} + diff --git a/FileSystemIDandChk/PartPlugins/AppleMap.cs b/FileSystemIDandChk/PartPlugins/AppleMap.cs new file mode 100644 index 000000000..d0672e7fd --- /dev/null +++ b/FileSystemIDandChk/PartPlugins/AppleMap.cs @@ -0,0 +1,202 @@ +using System; +using System.IO; +using System.Text; +using System.Collections.Generic; +using FileSystemIDandChk; + +namespace FileSystemIDandChk.PartPlugins +{ + class AppleMap : PartPlugin + { + public AppleMap (PluginBase Core) + { + base.Name = "Apple Partition Map"; + base.PluginUUID = new Guid("ffe4b4e9-82ed-4761-af49-8bade4081d10"); + } + + public override bool GetInformation (FileStream stream, out List partitions) + { + byte[] sixteen_bits = new byte[2]; + byte[] thirtytwo_bits = new byte[4]; + byte[] sixteen_bytes = new byte[16]; + byte[] thirtytwo_bytes = new byte[32]; + + ulong apm_entries; + + partitions = new List(); + + AppleMapBootEntry APMB = new AppleMapBootEntry(); + AppleMapPartitionEntry APMEntry = new AppleMapPartitionEntry(); + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(sixteen_bits, 0, 2); + sixteen_bits = Swapping.SwapTwoBytes(sixteen_bits); + APMB.signature = BitConverter.ToUInt16(sixteen_bits, 0); + + if(APMB.signature != 0x4552) + return false; // Not an Apple Partition Map + + stream.Read(sixteen_bits, 0, 2); + sixteen_bits = Swapping.SwapTwoBytes(sixteen_bits); + APMB.sector_size = BitConverter.ToUInt16(sixteen_bits, 0); + + stream.Seek(APMB.sector_size, SeekOrigin.Begin); // Seek to first entry + stream.Read(sixteen_bits, 0, 2); + sixteen_bits = Swapping.SwapTwoBytes(sixteen_bits); + APMEntry.signature = BitConverter.ToUInt16(sixteen_bits, 0); + if(APMEntry.signature != 0x504D && APMEntry.signature != 0x5453) // It should have partition entry signature + return false; + + stream.Seek(2, SeekOrigin.Current); // Skip reserved1 + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.entries = BitConverter.ToUInt32(thirtytwo_bits, 0); + if(APMEntry.entries <= 1) // It should have more than one entry + return false; + + stream.Seek(4, SeekOrigin.Current); // Skip start, we don't need it + stream.Seek(4, SeekOrigin.Current); // Skip sectors, we don't need it + stream.Seek(32, SeekOrigin.Current); // Skip name, we don't ned it + + stream.Read(thirtytwo_bytes, 0, 32); + APMEntry.type = StringHandlers.CToString(thirtytwo_bytes); + if(APMEntry.type != "Apple_partition_map") // APM self-describes, if not, this is incorrect + return false; + + apm_entries = APMEntry.entries; + + for(ulong i = 2; i <= apm_entries; i++) // For each partition + { + APMEntry = new AppleMapPartitionEntry(); + + stream.Seek((long)(APMB.sector_size*i), SeekOrigin.Begin); // Seek to partition descriptor + + stream.Read(sixteen_bits, 0, 2); + sixteen_bits = Swapping.SwapTwoBytes(sixteen_bits); + APMEntry.signature = BitConverter.ToUInt16(sixteen_bits, 0); + if(APMEntry.signature == 0x504D || APMEntry.signature == 0x5453) // It should have partition entry signature + { + Partition _partition = new Partition(); + StringBuilder sb = new StringBuilder(); + + stream.Seek(2, SeekOrigin.Current); // Skip reserved1 + stream.Seek(4, SeekOrigin.Current); // Skip entries + + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.start = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.sectors = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(thirtytwo_bytes, 0, 32); + APMEntry.name = StringHandlers.CToString(thirtytwo_bytes); + stream.Read(thirtytwo_bytes, 0, 32); + APMEntry.type = StringHandlers.CToString(thirtytwo_bytes); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.first_data_block = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.data_sectors = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.status = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.first_boot_block = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.boot_size = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.load_address = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Seek(4, SeekOrigin.Current); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.entry_point = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Seek(4, SeekOrigin.Current); + stream.Read(thirtytwo_bits, 0, 4); + thirtytwo_bits = Swapping.SwapFourBytes(thirtytwo_bits); + APMEntry.checksum = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(sixteen_bytes, 0, 16); + APMEntry.processor = StringHandlers.CToString(sixteen_bytes); + + _partition.PartitionSequence = i; + _partition.PartitionType = APMEntry.type; + _partition.PartitionName = APMEntry.name; + _partition.PartitionStart = APMEntry.start * APMB.sector_size; + _partition.PartitionLength = APMEntry.sectors * APMB.sector_size; + + sb.AppendLine("Partition flags:"); + if((APMEntry.status & 0x01) == 0x01) + sb.AppendLine("Partition is valid."); + if((APMEntry.status & 0x02) == 0x02) + sb.AppendLine("Partition entry is not available."); + if((APMEntry.status & 0x04) == 0x04) + sb.AppendLine("Partition is mounted."); + if((APMEntry.status & 0x08) == 0x08) + sb.AppendLine("Partition is bootable."); + if((APMEntry.status & 0x10) == 0x10) + sb.AppendLine("Partition is readable."); + if((APMEntry.status & 0x20) == 0x20) + sb.AppendLine("Partition is writable."); + if((APMEntry.status & 0x40) == 0x40) + sb.AppendLine("Partition's boot code is position independent."); + + if((APMEntry.status & 0x08) == 0x08) + { + sb.AppendFormat("First boot sector: {0}", APMEntry.first_boot_block).AppendLine(); + sb.AppendFormat("Boot is {0} bytes.", APMEntry.boot_size).AppendLine(); + sb.AppendFormat("Boot load address: 0x{0:X8}", APMEntry.load_address).AppendLine(); + sb.AppendFormat("Boot entry point: 0x{0:X8}", APMEntry.entry_point).AppendLine(); + sb.AppendFormat("Boot code checksum: 0x{0:X8}", APMEntry.checksum).AppendLine(); + sb.AppendFormat("Processor: {0}", APMEntry.processor).AppendLine(); + } + + _partition.PartitionDescription = sb.ToString(); + + if((APMEntry.status & 0x01) == 0x01) + partitions.Add(_partition); + } + } + + return true; + } + + public struct AppleMapBootEntry + { + public UInt16 signature; // Signature ("ER") + public UInt16 sector_size; // Byter per sector + public UInt32 sectors; // Sectors of the disk + public UInt16 reserved1; // Reserved + public UInt16 reserved2; // Reserved + public UInt32 reserved3; // Reserved + public UInt16 driver_entries; // Number of entries of the driver descriptor + public UInt32 first_driver_blk; // First sector of the driver + public UInt16 driver_size; // Size in 512bytes sectors of the driver + public UInt16 operating_system; // Operating system (MacOS = 1) + } + + public struct AppleMapPartitionEntry + { + public UInt16 signature; // Signature ("PM" or "TS") + public UInt16 reserved1; // Reserved + public UInt32 entries; // Number of entries on the partition map, each one sector + public UInt32 start; // First sector of the partition + public UInt32 sectors; // Number of sectos of the partition + public string name; // Partition name, 32 bytes, null-padded + public string type; // Partition type. 32 bytes, null-padded + public UInt32 first_data_block; // First sector of the data area + public UInt32 data_sectors; // Number of sectors of the data area + public UInt32 status; // Partition status + public UInt32 first_boot_block; // First sector of the boot code + public UInt32 boot_size; // Size in bytes of the boot code + public UInt32 load_address; // Load address of the boot code + public UInt32 reserved2; // Reserved + public UInt32 entry_point; // Entry point of the boot code + public UInt32 reserved3; // Reserved + public UInt32 checksum; // Boot code checksum + public string processor; // Processor type, 16 bytes, null-padded + } + } +} \ No newline at end of file diff --git a/FileSystemIDandChk/PartPlugins/PartPlugin.cs b/FileSystemIDandChk/PartPlugins/PartPlugin.cs new file mode 100644 index 000000000..ae2411cc7 --- /dev/null +++ b/FileSystemIDandChk/PartPlugins/PartPlugin.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; +using System.Collections.Generic; + +namespace FileSystemIDandChk.PartPlugins +{ + public abstract class PartPlugin + { + public string Name; + public Guid PluginUUID; + + protected PartPlugin () + { + } + + public abstract bool GetInformation(FileStream stream, out List partitions); + } + + public struct Partition + { + public ulong PartitionSequence; // Partition number, 0-started + public string PartitionType; // Partition type + public string PartitionName; // Partition name (if the scheme supports it) + public long PartitionStart; // Start of the partition, in bytes + public long PartitionLength; // Length in bytes of the partition + public string PartitionDescription; // Information that does not find space in this struct + } +} \ No newline at end of file diff --git a/FileSystemIDandChk/Plugins.cs b/FileSystemIDandChk/Plugins.cs new file mode 100644 index 000000000..f9c76a87e --- /dev/null +++ b/FileSystemIDandChk/Plugins.cs @@ -0,0 +1,64 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using FileSystemIDandChk.Plugins; +using FileSystemIDandChk.PartPlugins; + +namespace FileSystemIDandChk +{ + public class PluginBase + { + public Dictionary PluginsList; + public Dictionary PartPluginsList; + + public PluginBase () + { + this.PluginsList = new Dictionary(); + this.PartPluginsList = new Dictionary(); + } + + public void RegisterAllPlugins() + { + Assembly assembly = Assembly.GetExecutingAssembly(); + + foreach (Type type in assembly.GetTypes()) + { + try + { + if (type.IsSubclassOf(typeof(Plugin))) + { + Plugin plugin = (Plugin)type.GetConstructor(new Type[] { typeof(PluginBase) }).Invoke(new object[] { this }); + this.RegisterPlugin(plugin); + } + else if (type.IsSubclassOf(typeof(PartPlugin))) + { + PartPlugin partplugin = (PartPlugin)type.GetConstructor(new Type[] { typeof(PluginBase) }).Invoke(new object[] { this }); + this.RegisterPartPlugin(partplugin); + } + + } + catch (Exception exception) + { + Console.WriteLine(exception.ToString()); + } + } + } + + private void RegisterPlugin(Plugin plugin) + { + if (!this.PluginsList.ContainsKey(plugin.Name.ToLower())) + { + this.PluginsList.Add(plugin.Name.ToLower(), plugin); + } + } + + private void RegisterPartPlugin(PartPlugin partplugin) + { + if (!this.PartPluginsList.ContainsKey(partplugin.Name.ToLower())) + { + this.PartPluginsList.Add(partplugin.Name.ToLower(), partplugin); + } + } + } +} + diff --git a/FileSystemIDandChk/Plugins/AppleHFS.cs b/FileSystemIDandChk/Plugins/AppleHFS.cs new file mode 100644 index 000000000..17d3ea8a4 --- /dev/null +++ b/FileSystemIDandChk/Plugins/AppleHFS.cs @@ -0,0 +1,401 @@ +using System; +using System.IO; +using System.Text; +using FileSystemIDandChk; + +// Information from Inside Macintosh + +namespace FileSystemIDandChk.Plugins +{ + class AppleHFS : Plugin + { + private DateTime HFSDateDelta = new DateTime(1904, 01, 01, 00, 00, 00); + + public AppleHFS(PluginBase Core) + { + base.Name = "Apple Hierarchical File System"; + base.PluginUUID = new Guid("8f4ee9d5-6820-4d7a-ae6b-3a4a49e7a88f"); + } + + public override bool Identify(FileStream stream, long offset) + { + byte[] signature = new byte[2]; + ushort drSigWord; + + stream.Seek(0x400 + offset, SeekOrigin.Begin); + + stream.Read(signature, 0, 2); + signature = Swapping.SwapTwoBytes(signature); + + drSigWord = BitConverter.ToUInt16(signature, 0); + + if(drSigWord == 0x4244) + return true; + else + return false; + } + + public override void GetInformation (FileStream stream, long offset, out string information) + { + information = ""; + + StringBuilder sb = new StringBuilder(); + + HFS_MasterDirectoryBlock MDB = new HFS_MasterDirectoryBlock(); + HFS_BootBlock BB = new HFS_BootBlock(); + + byte[] sixteen_bit = new byte[2]; + byte[] thirtytwo_bit = new byte[4]; + byte[] fifthteen_bytes = new byte[15]; + + stream.Seek(0x400 + offset, SeekOrigin.Begin); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drSigWord = BitConverter.ToUInt16(sixteen_bit, 0); + if(MDB.drSigWord != 0x4244) + return; + + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drCrDate = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drLsMod = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drAtrb = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drNmFls = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drVBMSt = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drAllocPtr = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drNmAlBlks = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drAlBlkSiz = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drClpSiz = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drAlBlSt = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drNxtCNID = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drFreeBks = BitConverter.ToUInt16(sixteen_bit, 0); + MDB.drVN = new byte[28]; + stream.Read(MDB.drVN, 0, 28); + + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drVolBkUp = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drVSeqNum = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drWrCnt = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drXTClpSiz = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drCTClpSiz = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drNmRtDirs = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFilCnt = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drDirCnt = BitConverter.ToUInt32(thirtytwo_bit, 0); + + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFndrInfo0 = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFndrInfo1 = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFndrInfo2 = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFndrInfo3 = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFndrInfo4 = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFndrInfo5 = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFndrInfo6 = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drFndrInfo7 = BitConverter.ToUInt32(thirtytwo_bit, 0); + + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drEmbedSigWord = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.xdrStABNt = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.xdrNumABlks = BitConverter.ToUInt16(sixteen_bit, 0); + + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drXTFlSize = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Seek(12, SeekOrigin.Current); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drCTFlSize = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Seek(12, SeekOrigin.Current); + + stream.Seek(0 + offset, SeekOrigin.Begin); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + BB.signature = BitConverter.ToUInt16(sixteen_bit, 0); + + if(BB.signature == 0x4C4B) + { + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + BB.branch = BitConverter.ToUInt32(thirtytwo_bit, 0); + BB.boot_flags = (byte)stream.ReadByte(); + BB.boot_version = (byte)stream.ReadByte(); + + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + BB.sec_sv_pages = BitConverter.ToInt16(sixteen_bit, 0); + + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.system_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.finder_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.debug_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.disasm_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.stupscr_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.bootup_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.clipbrd_name = Encoding.ASCII.GetString(fifthteen_bytes); + + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + BB.max_files = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + BB.queue_size = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + BB.heap_128k = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + BB.heap_256k = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + BB.heap_512k = BitConverter.ToUInt32(thirtytwo_bit, 0); + } + else + BB.signature = 0x0000; + + sb.AppendLine("Apple Hierarchical File System"); + sb.AppendLine(); + sb.AppendLine("Master Directory Block:"); + sb.AppendFormat("Creation date: {0}", HFSDateDelta.AddTicks((long)(MDB.drCrDate*10000000))).AppendLine(); + sb.AppendFormat("Last modification date: {0}", HFSDateDelta.AddTicks((long)(MDB.drLsMod*10000000))).AppendLine(); + sb.AppendFormat("Last backup date: {0}", HFSDateDelta.AddTicks((long)(MDB.drVolBkUp*10000000))).AppendLine(); + sb.AppendFormat("Backup sequence number: {0}", MDB.drVSeqNum).AppendLine(); + + if((MDB.drAtrb & 0x80) == 0x80) + sb.AppendLine("Volume is locked by hardware."); + if((MDB.drAtrb & 0x100) == 0x100) + sb.AppendLine("Volume was unmonted."); + else + sb.AppendLine("Volume is mounted."); + if((MDB.drAtrb & 0x200) == 0x200) + sb.AppendLine("Volume has spared bad blocks."); + if((MDB.drAtrb & 0x400) == 0x400) + sb.AppendLine("Volume does not need cache."); + if((MDB.drAtrb & 0x800) == 0x800) + sb.AppendLine("Boot volume is inconsistent."); + if((MDB.drAtrb & 0x1000) == 0x1000) + sb.AppendLine("There are reused CNIDs."); + if((MDB.drAtrb & 0x2000) == 0x2000) + sb.AppendLine("Volume is journaled."); + if((MDB.drAtrb & 0x4000) == 0x4000) + sb.AppendLine("Volume is seriously inconsistent."); + if((MDB.drAtrb & 0x8000) == 0x8000) + sb.AppendLine("Volume is locked by software."); + + sb.AppendFormat("{0} files on root directory", MDB.drNmFls).AppendLine(); + sb.AppendFormat("{0} directories on root directory", MDB.drNmRtDirs).AppendLine(); + sb.AppendFormat("{0} files on volume", MDB.drFilCnt).AppendLine(); + sb.AppendFormat("{0} directories on volume", MDB.drDirCnt).AppendLine(); + sb.AppendFormat("Volume write count: {0}", MDB.drWrCnt).AppendLine(); + + sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", MDB.drVBMSt).AppendLine(); + sb.AppendFormat("Next allocation block: {0}.", MDB.drAllocPtr).AppendLine(); + sb.AppendFormat("{0} volume allocation blocks.", MDB.drNmAlBlks).AppendLine(); + sb.AppendFormat("{0} bytes per allocation block.", MDB.drAlBlkSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a file.", MDB.drClpSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", MDB.drXTClpSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", MDB.drCTClpSiz).AppendLine(); + sb.AppendFormat("Sector of first allocation block: {0}", MDB.drAlBlSt).AppendLine(); + sb.AppendFormat("Next unused CNID: {0}", MDB.drNxtCNID).AppendLine(); + sb.AppendFormat("{0} unused allocation blocks.", MDB.drFreeBks).AppendLine(); + + sb.AppendFormat("{0} bytes in the Extents B-Tree", MDB.drXTFlSize).AppendLine(); + sb.AppendFormat("{0} bytes in the Catalog B-Tree", MDB.drCTFlSize).AppendLine(); + + StringBuilder volumeName = new StringBuilder(); + for(int i = 1; i < 28; i++) + { + if(MDB.drVN[i] == 0) + break; + + volumeName.Append(Encoding.ASCII.GetString(MDB.drVN, i, 1)); + } + sb.AppendFormat("Volume name: {0}", volumeName.ToString()).AppendLine(); + + sb.AppendLine("Finder info:"); + sb.AppendFormat("CNID of bootable system's directory: {0}", MDB.drFndrInfo0).AppendLine(); + sb.AppendFormat("CNID of first-run application's directory: {0}", MDB.drFndrInfo1).AppendLine(); + sb.AppendFormat("CNID of previously opened directory: {0}", MDB.drFndrInfo2).AppendLine(); + sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", MDB.drFndrInfo3).AppendLine(); + sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", MDB.drFndrInfo5).AppendLine(); + sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", MDB.drFndrInfo6, MDB.drFndrInfo7).AppendLine(); + + if(MDB.drEmbedSigWord == 0x482B) + { + sb.AppendLine("Volume wraps a HFS+ volume."); + sb.AppendFormat("Starting block of the HFS+ volume: {0}", MDB.xdrStABNt).AppendLine(); + sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", MDB.xdrNumABlks).AppendLine(); + } + + if(BB.signature == 0x4C4B) + { + sb.AppendLine("Volume is bootable."); + sb.AppendLine(); + sb.AppendLine("Boot Block:"); + if((BB.boot_flags & 0x40) == 0x40) + sb.AppendLine("Boot block should be executed."); + if((BB.boot_flags & 0x80) == 0x80) + { + sb.AppendLine("Boot block is in new unknown format."); + } + else + { + if(BB.sec_sv_pages > 0) + sb.AppendLine("Allocate secondary sound buffer at boot."); + else if(BB.sec_sv_pages < 0) + sb.AppendLine("Allocate secondary sound and video buffers at boot."); + + sb.AppendFormat("System filename: {0}", BB.system_name).AppendLine(); + sb.AppendFormat("Finder filename: {0}", BB.finder_name).AppendLine(); + sb.AppendFormat("Debugger filename: {0}", BB.debug_name).AppendLine(); + sb.AppendFormat("Disassembler filename: {0}", BB.disasm_name).AppendLine(); + sb.AppendFormat("Startup screen filename: {0}", BB.stupscr_name).AppendLine(); + sb.AppendFormat("First program to execute at boot: {0}", BB.bootup_name).AppendLine(); + sb.AppendFormat("Clipboard filename: {0}", BB.clipbrd_name).AppendLine(); + sb.AppendFormat("Maximum opened files: {0}", BB.max_files*4).AppendLine(); + sb.AppendFormat("Event queue size: {0}", BB.queue_size).AppendLine(); + sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", BB.heap_128k).AppendLine(); + sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", BB.heap_256k).AppendLine(); + sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", BB.heap_512k).AppendLine(); + } + } + else + sb.AppendLine("Volume is not bootable."); + + information = sb.ToString(); + + return; + } + + private struct HFS_MasterDirectoryBlock // Should be offset 0x0400 bytes in volume + { + public UInt16 drSigWord; // Signature, 0x4244 + public UInt32 drCrDate; // Volume creation date + public UInt32 drLsMod; // Volume last modification date + public UInt16 drAtrb; // Volume attributes + public UInt16 drNmFls; // Files in root directory + public UInt16 drVBMSt; // Start 512-byte sector of volume bitmap + public UInt16 drAllocPtr; // Allocation block to begin next allocation + public UInt16 drNmAlBlks; // Allocation blocks + public UInt32 drAlBlkSiz; // Bytes per allocation block + public UInt32 drClpSiz; // Bytes to allocate when extending a file + public UInt16 drAlBlSt; // Start 512-byte sector of first allocation block + public UInt32 drNxtCNID; // CNID for next file + public UInt16 drFreeBks; // Free allocation blocks + public byte[] drVN; // Volume name (28 bytes) + public UInt32 drVolBkUp; // Volume last backup time + public UInt16 drVSeqNum; // Volume backup sequence number + public UInt32 drWrCnt; // Filesystem write count + public UInt32 drXTClpSiz; // Bytes to allocate when extending the extents B-Tree + public UInt32 drCTClpSiz; // Bytes to allocate when extending the catalog B-Tree + public UInt16 drNmRtDirs; // Number of directories in root directory + public UInt32 drFilCnt; // Number of files in the volume + public UInt32 drDirCnt; // Number of directories in the volume + public UInt32 drFndrInfo0; // finderInfo[0], CNID for bootable system's directory + public UInt32 drFndrInfo1; // finderInfo[1], CNID of the directory containing the boot application + public UInt32 drFndrInfo2; // finderInfo[2], CNID of the directory that should be opened on boot + public UInt32 drFndrInfo3; // finderInfo[3], CNID for Mac OS 8 or 9 directory + public UInt32 drFndrInfo4; // finderInfo[4], Reserved + public UInt32 drFndrInfo5; // finderInfo[5], CNID for Mac OS X directory + public UInt32 drFndrInfo6; // finderInfo[6], first part of Mac OS X volume ID + public UInt32 drFndrInfo7; // finderInfo[7], second part of Mac OS X volume ID + public UInt16 drEmbedSigWord; // Embedded volume signature, "H+" if HFS+ is embedded ignore following two fields if not + public UInt16 xdrStABNt; // Starting block number of embedded HFS+ volume + public UInt16 xdrNumABlks; // Allocation blocks used by embedded volume + public UInt32 drXTFlSize; // Bytes in the extents B-Tree + // 3 HFS extents following, 32 bits each + public UInt32 drCTFlSize; // Bytes in the catalog B-Tree + // 3 HFS extents following, 32 bits each + } + + private struct HFS_BootBlock // Should be offset 0x0000 bytes in volume + { + public UInt16 signature; // Signature, 0x4C4B if bootable + public UInt32 branch; // Branch + public byte boot_flags; // Boot block flags + public byte boot_version; // Boot block version + public Int16 sec_sv_pages; // Allocate secondary buffers + public string system_name; // System file name (10 bytes) + public string finder_name; // Finder file name (10 bytes) + public string debug_name; // Debugger file name (10 bytes) + public string disasm_name; // Disassembler file name (10 bytes) + public string stupscr_name; // Startup screen file name (10 bytes) + public string bootup_name; // First program to execute on boot (10 bytes) + public string clipbrd_name; // Clipboard file name (10 bytes) + public UInt16 max_files; // 1/4 of maximum opened at a time files + public UInt16 queue_size; // Event queue size + public UInt32 heap_128k; // Heap size on a Mac with 128KiB of RAM + public UInt32 heap_256k; // Heap size on a Mac with 256KiB of RAM + public UInt32 heap_512k; // Heap size on a Mac with 512KiB of RAM or more + } // Follows boot code + } +} + diff --git a/FileSystemIDandChk/Plugins/AppleMFS.cs b/FileSystemIDandChk/Plugins/AppleMFS.cs new file mode 100644 index 000000000..b733825ac --- /dev/null +++ b/FileSystemIDandChk/Plugins/AppleMFS.cs @@ -0,0 +1,259 @@ +using System; +using System.IO; +using System.Text; +using FileSystemIDandChk; + +// Information from Inside Macintosh + +namespace FileSystemIDandChk.Plugins +{ + class AppleMFS : Plugin + { + private DateTime MFSDateDelta = new DateTime(1904, 01, 01, 00, 00, 00); + + public AppleMFS(PluginBase Core) + { + base.Name = "Apple Macintosh File System"; + base.PluginUUID = new Guid("67591456-90fa-49bd-ac89-14ef750b8af3"); + } + + public override bool Identify(FileStream stream, long offset) + { + byte[] signature = new byte[2]; + ushort drSigWord; + + stream.Seek(0x400 + offset, SeekOrigin.Begin); + + stream.Read(signature, 0, 2); + signature = Swapping.SwapTwoBytes(signature); + + drSigWord = BitConverter.ToUInt16(signature, 0); + + if(drSigWord == 0xD2D7) + return true; + else + return false; + } + + public override void GetInformation (FileStream stream, long offset, out string information) + { + information = ""; + + StringBuilder sb = new StringBuilder(); + + MFS_MasterDirectoryBlock MDB = new MFS_MasterDirectoryBlock(); + MFS_BootBlock BB = new MFS_BootBlock(); + + byte[] sixteen_bit = new byte[2]; + byte[] thirtytwo_bit = new byte[4]; + byte[] fifthteen_bytes = new byte[15]; + byte[] variable_size; + + stream.Seek(0x400 + offset, SeekOrigin.Begin); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drSigWord = BitConverter.ToUInt16(sixteen_bit, 0); + if(MDB.drSigWord != 0xD2D7) + return; + + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drCrDate = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drLsBkUp = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drAtrb = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drNmFls = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drDirSt = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drBlLen = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drNmAlBlks = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drAlBlkSiz = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drClpSiz = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drAlBlSt = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + MDB.drNxtFNum = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + MDB.drFreeBks = BitConverter.ToUInt16(sixteen_bit, 0); + MDB.drVNSiz = (byte)stream.ReadByte(); + variable_size = new byte[MDB.drVNSiz]; + stream.Read(variable_size, 0, MDB.drVNSiz); + MDB.drVN = Encoding.ASCII.GetString(variable_size); + + stream.Seek(0 + offset, SeekOrigin.Begin); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + BB.signature = BitConverter.ToUInt16(sixteen_bit, 0); + + if(BB.signature == 0x4C4B) + { + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + BB.branch = BitConverter.ToUInt32(thirtytwo_bit, 0); + BB.boot_flags = (byte)stream.ReadByte(); + BB.boot_version = (byte)stream.ReadByte(); + + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + BB.sec_sv_pages = BitConverter.ToInt16(sixteen_bit, 0); + + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.system_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.finder_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.debug_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.disasm_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.stupscr_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.bootup_name = Encoding.ASCII.GetString(fifthteen_bytes); + stream.Seek(1, SeekOrigin.Current); + stream.Read(fifthteen_bytes, 0, 15); + BB.clipbrd_name = Encoding.ASCII.GetString(fifthteen_bytes); + + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + BB.max_files = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(sixteen_bit, 0, 2); + sixteen_bit = Swapping.SwapTwoBytes(sixteen_bit); + BB.queue_size = BitConverter.ToUInt16(sixteen_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + BB.heap_128k = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + BB.heap_256k = BitConverter.ToUInt32(thirtytwo_bit, 0); + stream.Read(thirtytwo_bit, 0, 4); + thirtytwo_bit = Swapping.SwapFourBytes(thirtytwo_bit); + BB.heap_512k = BitConverter.ToUInt32(thirtytwo_bit, 0); + } + else + BB.signature = 0x0000; + + sb.AppendLine("Apple Macintosh File System"); + sb.AppendLine(); + sb.AppendLine("Master Directory Block:"); + sb.AppendFormat("Creation date: {0}", MFSDateDelta.AddTicks((long)(MDB.drCrDate*10000000))).AppendLine(); + sb.AppendFormat("Last backup date: {0}", MFSDateDelta.AddTicks((long)(MDB.drLsBkUp*10000000))).AppendLine(); + if((MDB.drAtrb & 0x80) == 0x80) + sb.AppendLine("Volume is locked by hardware."); + if((MDB.drAtrb & 0x8000) == 0x8000) + sb.AppendLine("Volume is locked by software."); + sb.AppendFormat("{0} files on volume", MDB.drNmFls).AppendLine(); + sb.AppendFormat("First directory block: {0}", MDB.drDirSt).AppendLine(); + sb.AppendFormat("{0} blocks in directory.", MDB.drBlLen).AppendLine(); + sb.AppendFormat("{0} volume allocation blocks.", MDB.drNmAlBlks).AppendLine(); + sb.AppendFormat("Size of allocation blocks: {0}", MDB.drAlBlkSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate.", MDB.drClpSiz).AppendLine(); + sb.AppendFormat("{0} first allocation block.", MDB.drAlBlSt).AppendLine(); + sb.AppendFormat("Next unused file number: {0}", MDB.drNxtFNum).AppendLine(); + sb.AppendFormat("{0} unused allocation blocks.", MDB.drFreeBks).AppendLine(); + sb.AppendFormat("Volume name: {0}", MDB.drVN).AppendLine(); + + if(BB.signature == 0x4C4B) + { + sb.AppendLine("Volume is bootable."); + sb.AppendLine(); + sb.AppendLine("Boot Block:"); + if((BB.boot_flags & 0x40) == 0x40) + sb.AppendLine("Boot block should be executed."); + if((BB.boot_flags & 0x80) == 0x80) + { + sb.AppendLine("Boot block is in new unknown format."); + } + else + { + if(BB.sec_sv_pages > 0) + sb.AppendLine("Allocate secondary sound buffer at boot."); + else if(BB.sec_sv_pages < 0) + sb.AppendLine("Allocate secondary sound and video buffers at boot."); + + sb.AppendFormat("System filename: {0}", BB.system_name).AppendLine(); + sb.AppendFormat("Finder filename: {0}", BB.finder_name).AppendLine(); + sb.AppendFormat("Debugger filename: {0}", BB.debug_name).AppendLine(); + sb.AppendFormat("Disassembler filename: {0}", BB.disasm_name).AppendLine(); + sb.AppendFormat("Startup screen filename: {0}", BB.stupscr_name).AppendLine(); + sb.AppendFormat("First program to execute at boot: {0}", BB.bootup_name).AppendLine(); + sb.AppendFormat("Clipboard filename: {0}", BB.clipbrd_name).AppendLine(); + sb.AppendFormat("Maximum opened files: {0}", BB.max_files*4).AppendLine(); + sb.AppendFormat("Event queue size: {0}", BB.queue_size).AppendLine(); + sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", BB.heap_128k).AppendLine(); + sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", BB.heap_256k).AppendLine(); + sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", BB.heap_512k).AppendLine(); + } + } + else + sb.AppendLine("Volume is not bootable."); + + information = sb.ToString(); + + return; + } + + private struct MFS_MasterDirectoryBlock // Should be offset 0x0400 bytes in volume + { + public UInt16 drSigWord; // Signature, 0xD2D7 + public UInt32 drCrDate; // Volume creation date + public UInt32 drLsBkUp; // Volume last backup date + public UInt16 drAtrb; // Volume attributes + public UInt16 drNmFls; // Volume number of files + public UInt16 drDirSt; // First directory block + public UInt16 drBlLen; // Length of directory in blocks + public UInt16 drNmAlBlks; // Volume allocation blocks + public UInt32 drAlBlkSiz; // Size of allocation blocks + public UInt32 drClpSiz; // Number of bytes to allocate + public UInt16 drAlBlSt; // First allocation block in block map + public UInt32 drNxtFNum; // Next unused file number + public UInt16 drFreeBks; // Number of unused allocation blocks + public byte drVNSiz; // Length of volume name + public string drVN; // Characters of volume name + } + + private struct MFS_BootBlock // Should be offset 0x0000 bytes in volume + { + public UInt16 signature; // Signature, 0x4C4B if bootable + public UInt32 branch; // Branch + public byte boot_flags; // Boot block flags + public byte boot_version; // Boot block version + public short sec_sv_pages; // Allocate secondary buffers + public string system_name; // System file name (10 bytes) + public string finder_name; // Finder file name (10 bytes) + public string debug_name; // Debugger file name (10 bytes) + public string disasm_name; // Disassembler file name (10 bytes) + public string stupscr_name; // Startup screen file name (10 bytes) + public string bootup_name; // First program to execute on boot (10 bytes) + public string clipbrd_name; // Clipboard file name (10 bytes) + public UInt16 max_files; // 1/4 of maximum opened at a time files + public UInt16 queue_size; // Event queue size + public UInt32 heap_128k; // Heap size on a Mac with 128KiB of RAM + public UInt32 heap_256k; // Heap size on a Mac with 256KiB of RAM + public UInt32 heap_512k; // Heap size on a Mac with 512KiB of RAM or more + } // Follows boot code + } +} + diff --git a/FileSystemIDandChk/Plugins/FAT.cs b/FileSystemIDandChk/Plugins/FAT.cs new file mode 100644 index 000000000..22534446e --- /dev/null +++ b/FileSystemIDandChk/Plugins/FAT.cs @@ -0,0 +1,263 @@ +using System; +using System.IO; +using System.Text; +using FileSystemIDandChk; + +// Information from Inside Macintosh + +namespace FileSystemIDandChk.Plugins +{ + class FAT : Plugin + { + public FAT(PluginBase Core) + { + base.Name = "Microsoft File Allocation Table"; + base.PluginUUID = new Guid("67591456-90fa-49bd-ac89-14ef750b8af3"); + } + + public override bool Identify(FileStream stream, long offset) + { + byte media_descriptor; // Not present on DOS <= 3, present on TOS but != of first FAT entry + byte[] fat32_signature = new byte[8]; // "FAT32 " + byte[] first_fat_entry_b = new byte[4]; // No matter FAT size we read 2 bytes for checking + ulong first_fat_entry; + + stream.Seek(0x15 + offset, SeekOrigin.Begin); // Media Descriptor if present is in 0x15 + media_descriptor = (byte)stream.ReadByte(); + stream.Seek(0x52 + offset, SeekOrigin.Begin); // FAT32 signature, if present, is in 0x52 + stream.Read(fat32_signature, 0, 8); + stream.Seek(0x200 + offset, SeekOrigin.Begin); // First FAT entry is always at 0x200 in pre-FAT32 + stream.Read(first_fat_entry_b, 0, 4); + + first_fat_entry = BitConverter.ToUInt32(first_fat_entry_b, 0); // Easier to manage + + // Let's start the fun + if(Encoding.ASCII.GetString(fat32_signature) == "FAT32 ") + return true; // Seems easy, check reading + + if((first_fat_entry & 0xFFFFFFF0) == 0xFFFFFFF0) // Seems to be FAT16 + { + if((first_fat_entry & 0xFF) == media_descriptor) + return true; // It MUST be FAT16, or... maybe not :S + } + else if((first_fat_entry & 0x00FFFFF0) == 0x00FFFFF0) + { + //if((first_fat_entry & 0xFF) == media_descriptor) // Pre DOS<4 does not implement this, TOS does and is != + return true; // It MUST be FAT12, or... maybe not :S + } + + return false; + } + + public override void GetInformation (FileStream stream, long offset, out string information) + { + information = ""; + + StringBuilder sb = new StringBuilder(); + + byte media_descriptor; // Not present on DOS <= 3, present on TOS but != of first FAT entry + byte[] fat32_signature = new byte[8]; // "FAT32 " + byte[] first_fat_entry_b = new byte[4]; // No matter FAT size we read 2 bytes for checking + ulong first_fat_entry; + + stream.Seek(0x15 + offset, SeekOrigin.Begin); // Media Descriptor if present is in 0x15 + media_descriptor =(byte) stream.ReadByte(); + stream.Seek(0x52 + offset, SeekOrigin.Begin); // FAT32 signature, if present, is in 0x52 + stream.Read(fat32_signature, 0, 8); + stream.Seek(0x200 + offset, SeekOrigin.Begin); // First FAT entry is always at 0x200 in pre-FAT32 + stream.Read(first_fat_entry_b, 0, 4); + + first_fat_entry = BitConverter.ToUInt32(first_fat_entry_b, 0); // Easier to manage + + // Let's start the fun + if(Encoding.ASCII.GetString(fat32_signature) == "FAT32 ") + sb.AppendLine("Microsoft FAT32"); // Seems easy, check reading + else if((first_fat_entry & 0xFFFFFFF0) == 0xFFFFFFF0) // Seems to be FAT16 + { + if((first_fat_entry & 0xFF) == media_descriptor) + sb.AppendLine("Microsoft FAT16"); // It MUST be FAT16, or... maybe not :S + } + else if((first_fat_entry & 0x00FFFFF0) == 0x00FFFFF0) + { + //if((first_fat_entry & 0xFF) == media_descriptor) // Pre DOS<4 does not implement this, TOS does and is != + sb.AppendLine("Microsoft FAT12"); // It MUST be FAT12, or... maybe not :S + } + else + return; + + BIOSParameterBlock BPB = new BIOSParameterBlock(); + ExtendedParameterBlock EPB = new ExtendedParameterBlock(); + FAT32ParameterBlock FAT32PB = new FAT32ParameterBlock(); + + byte[] eight_bytes = new byte[8]; + byte[] eleven_bytes = new byte[11]; + byte[] sixteen_bits = new byte[2]; + byte[] thirtytwo_bits = new byte[4]; + + stream.Seek(3 + offset, SeekOrigin.Begin); + stream.Read(eight_bytes, 0, 8); + BPB.OEMName = Encoding.ASCII.GetString(eight_bytes); + stream.Read(sixteen_bits, 0, 2); + BPB.bps = BitConverter.ToUInt16(sixteen_bits, 0); + BPB.spc = (byte)stream.ReadByte(); + stream.Read(sixteen_bits, 0, 2); + BPB.rsectors = BitConverter.ToUInt16(sixteen_bits, 0); + BPB.fats_no = (byte)stream.ReadByte(); + stream.Read(sixteen_bits, 0, 2); + BPB.root_ent = BitConverter.ToUInt16(sixteen_bits, 0); + stream.Read(sixteen_bits, 0, 2); + BPB.sectors = BitConverter.ToUInt16(sixteen_bits, 0); + BPB.media = (byte)stream.ReadByte(); + stream.Read(sixteen_bits, 0, 2); + BPB.spfat = BitConverter.ToUInt16(sixteen_bits, 0); + stream.Read(sixteen_bits, 0, 2); + BPB.sptrk = BitConverter.ToUInt16(sixteen_bits, 0); + stream.Read(sixteen_bits, 0, 2); + BPB.heads = BitConverter.ToUInt16(sixteen_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + BPB.hsectors = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + BPB.big_sectors = BitConverter.ToUInt32(thirtytwo_bits, 0); + + if(Encoding.ASCII.GetString(fat32_signature) == "FAT32 ") + { + stream.Read(thirtytwo_bits, 0, 4); + FAT32PB.spfat = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(sixteen_bits, 0, 2); + FAT32PB.fat_flags = BitConverter.ToUInt16(sixteen_bits, 0); + stream.Read(sixteen_bits, 0, 2); + FAT32PB.version = BitConverter.ToUInt16(sixteen_bits, 0); + stream.Read(thirtytwo_bits, 0, 4); + FAT32PB.root_cluster = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(sixteen_bits, 0, 2); + FAT32PB.fsinfo_sector = BitConverter.ToUInt16(sixteen_bits, 0); + stream.Read(sixteen_bits, 0, 2); + FAT32PB.backup_sector = BitConverter.ToUInt16(sixteen_bits, 0); + FAT32PB.drive_no = (byte)stream.ReadByte(); + FAT32PB.nt_flags = (byte)stream.ReadByte(); + FAT32PB.signature = (byte)stream.ReadByte(); + stream.Read(thirtytwo_bits, 0, 4); + FAT32PB.serial_no = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(eleven_bytes, 0, 11); + FAT32PB.volume_label = Encoding.ASCII.GetString(eleven_bytes); + stream.Read(eight_bytes, 0, 8); + FAT32PB.fs_type = Encoding.ASCII.GetString(eight_bytes); + } + else + { + EPB.drive_no = (byte)stream.ReadByte(); + EPB.nt_flags = (byte)stream.ReadByte(); + EPB.signature = (byte)stream.ReadByte(); + stream.Read(thirtytwo_bits, 0, 4); + EPB.serial_no = BitConverter.ToUInt32(thirtytwo_bits, 0); + stream.Read(eleven_bytes, 0, 11); + EPB.volume_label = Encoding.ASCII.GetString(eleven_bytes); + stream.Read(eight_bytes, 0, 8); + EPB.fs_type = Encoding.ASCII.GetString(eight_bytes); + } + + sb.AppendFormat("OEM Name: {0}", BPB.OEMName).AppendLine(); + sb.AppendFormat("{0} bytes per sector.", BPB.bps).AppendLine(); + sb.AppendFormat("{0} sectors per cluster.", BPB.spc).AppendLine(); + sb.AppendFormat("{0} sectors reserved between BPB and FAT.", BPB.rsectors).AppendLine(); + sb.AppendFormat("{0} FATs.", BPB.fats_no).AppendLine(); + sb.AppendFormat("{0} entires on root directory.", BPB.root_ent).AppendLine(); + if(BPB.sectors==0) + sb.AppendFormat("{0} sectors on volume.", BPB.big_sectors).AppendLine(); + else + sb.AppendFormat("{0} sectors on volume.", BPB.sectors).AppendLine(); + if((BPB.media & 0xF0) == 0xF0) + sb.AppendFormat("Media format: 0x{0:X2}", BPB.media).AppendLine(); + if(Encoding.ASCII.GetString(fat32_signature) == "FAT32 ") + sb.AppendFormat("{0} sectors per FAT.", FAT32PB.spfat).AppendLine(); + else + sb.AppendFormat("{0} sectors per FAT.", BPB.spfat).AppendLine(); + sb.AppendFormat("{0} sectors per track.", BPB.sptrk).AppendLine(); + sb.AppendFormat("{0} heads.", BPB.heads).AppendLine(); + sb.AppendFormat("{0} hidden sectors before BPB.", BPB.hsectors).AppendLine(); + + if(Encoding.ASCII.GetString(fat32_signature) == "FAT32 ") + { + sb.AppendFormat("Cluster of root directory: {0}", FAT32PB.root_cluster).AppendLine(); + sb.AppendFormat("Sector of FSINFO structure: {0}", FAT32PB.fsinfo_sector).AppendLine(); + sb.AppendFormat("Sector of backup FAT32 parameter block: {0}", FAT32PB.backup_sector).AppendLine(); + sb.AppendFormat("Drive number: 0x{0:X2}", FAT32PB.drive_no).AppendLine(); + sb.AppendFormat("Volume Serial Number: 0x{0:X8}", FAT32PB.serial_no).AppendLine(); + if((FAT32PB.nt_flags & 0x01) == 0x01) + { + sb.AppendLine("Volume should be checked on next mount."); + if((EPB.nt_flags & 0x02) == 0x02) + sb.AppendLine("Disk surface should be checked also."); + } + + sb.AppendFormat("Volume label: {0}", EPB.volume_label).AppendLine(); + sb.AppendFormat("Filesystem type: {0}", EPB.fs_type).AppendLine(); + } + else if(EPB.signature == 0x28 || EPB.signature == 0x29) + { + sb.AppendFormat("Drive number: 0x{0:X2}", EPB.drive_no).AppendLine(); + sb.AppendFormat("Volume Serial Number: 0x{0:X8}", EPB.serial_no).AppendLine(); + if(EPB.signature==0x29) + { + if((EPB.nt_flags & 0x01) == 0x01) + { + sb.AppendLine("Volume should be checked on next mount."); + if((EPB.nt_flags & 0x02) == 0x02) + sb.AppendLine("Disk surface should be checked also."); + } + + sb.AppendFormat("Volume label: {0}", EPB.volume_label).AppendLine(); + sb.AppendFormat("Filesystem type: {0}", EPB.fs_type).AppendLine(); + } + } + + information = sb.ToString(); + } + + public struct BIOSParameterBlock + { + public string OEMName; // OEM Name, 8 bytes, space-padded + public UInt16 bps; // Bytes per sector + public byte spc; // Sectors per cluster + public UInt16 rsectors; // Reserved sectors between BPB and FAT + public byte fats_no; // Number of FATs + public UInt16 root_ent; // Number of entries on root directory + public UInt16 sectors; // Sectors in volume + public byte media; // Media descriptor + public UInt16 spfat; // Sectors per FAT + public UInt16 sptrk; // Sectors per track + public UInt16 heads; // Heads + public UInt32 hsectors; // Hidden sectors before BPB + public UInt32 big_sectors; // Sectors in volume if > 65535 + } + + public struct ExtendedParameterBlock + { + public byte drive_no; // Drive number + public byte nt_flags; // Volume flags if NT (must be 0x29 signature) + public byte signature; // EPB signature, 0x28 or 0x29 + public UInt32 serial_no; // Volume serial number + /* Present only if signature == 0x29 */ + public string volume_label; // Volume label, 11 bytes, space-padded + public string fs_type; // Filesystem type, 8 bytes, space-padded + } + + public struct FAT32ParameterBlock + { + public UInt32 spfat; // Sectors per FAT + public UInt16 fat_flags; // FAT flags + public UInt16 version; // FAT32 version + public UInt32 root_cluster; // Cluster of root directory + public UInt16 fsinfo_sector; // Sector of FSINFO structure + public UInt16 backup_sector; // Secfor of FAT32PB bacup + byte[] reserved; // 12 reserved bytes + public byte drive_no; // Drive number + public byte nt_flags; // Volume flags + public byte signature; // FAT32PB signature, should be 0x29 + public UInt32 serial_no; // Volume serial number + public string volume_label; // Volume label, 11 bytes, space-padded + public string fs_type; // Filesystem type, 8 bytes, space-padded, must be "FAT32 " + } + } +} + diff --git a/FileSystemIDandChk/Plugins/ISO9660.cs b/FileSystemIDandChk/Plugins/ISO9660.cs new file mode 100644 index 000000000..937d1b6f6 --- /dev/null +++ b/FileSystemIDandChk/Plugins/ISO9660.cs @@ -0,0 +1,927 @@ +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 + { + public ISO9660Plugin(PluginBase Core) + { + base.Name = "ISO9660 Filesystem"; + base.PluginUUID = new Guid("d812f4d3-c357-400d-90fd-3b22ef786aa8"); + } + + 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) + { + 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 + offset, SeekOrigin.Begin); + + VDType = (byte)fileStream.ReadByte(); + byte[] VDMagic = new byte[5]; + + 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 + offset, 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) + offset, 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) + offset, 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) + offset, 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) + offset, SeekOrigin.Begin); + + if (fileStream.Read(VDPathTableStart, 0, 4) != 4) + break; // Something bad happened + + // Seek to next identifiers + fileStream.Seek(32958 + (2048 * counter) + offset, 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) + offset, 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) + offset, 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) + offset, 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) + offset, 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) + offset, 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(); + if(!Joliet) + decodedVD = DecodeVolumeDescriptor(VDSysId, VDVolId, VDVolSetId, VDPubId, VDDataPrepId, VDAppId, VCTime, VMTime, VXTime, VETime); + else + decodedVD = DecodeJolietDescriptor(JolietSysId, JolietVolId, JolietVolSetId, JolietPubId, JolietDataPrepId, JolietAppId, JolietCTime, JolietMTime, JolietXTime, JolietETime); + + int i = BitConverter.ToInt32(VDPathTableStart, 0); + + fileStream.Seek((i * 2048)+2 + offset, 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 + offset, 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 + offset, 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(); + + 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 = DecodeVDDateTime(VCTime); + + if (VMTime[0] == '0' || VMTime[0] == 0x00) + { + decodedVD.HasModificationTime = false; + } + else + { + decodedVD.HasModificationTime = true; + decodedVD.ModificationTime = DecodeVDDateTime(VMTime); + } + + if (VXTime[0] == '0' || VXTime[0] == 0x00) + { + decodedVD.HasExpirationTime = false; + } + else + { + decodedVD.HasExpirationTime = true; + decodedVD.ExpirationTime = DecodeVDDateTime(VXTime); + } + + if (VETime[0] == '0' || VETime[0] == 0x00) + { + decodedVD.HasEffectiveTime = false; + } + else + { + decodedVD.HasEffectiveTime = true; + decodedVD.EffectiveTime = DecodeVDDateTime(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 = DecodeVDDateTime(VCTime); + + if (VMTime[0] == '0' || VMTime[0] == 0x00) + { + decodedVD.HasModificationTime = false; + } + else + { + decodedVD.HasModificationTime = true; + decodedVD.ModificationTime = DecodeVDDateTime(VMTime); + } + + if (VXTime[0] == '0' || VXTime[0] == 0x00) + { + decodedVD.HasExpirationTime = false; + } + else + { + decodedVD.HasExpirationTime = true; + decodedVD.ExpirationTime = DecodeVDDateTime(VXTime); + } + + if (VETime[0] == '0' || VETime[0] == 0x00) + { + decodedVD.HasEffectiveTime = false; + } + else + { + decodedVD.HasEffectiveTime = true; + decodedVD.EffectiveTime = DecodeVDDateTime(VETime); + } + + return decodedVD; + } + + private DateTime DecodeVDDateTime(byte[] VDDateTime) + { + int year, month, day, hour, minute, second, hundredths; + byte[] twocharvalue = new byte[2]; + byte[] fourcharvalue = new byte[4]; + + fourcharvalue[0] = VDDateTime[0]; + fourcharvalue[1] = VDDateTime[1]; + fourcharvalue[2] = VDDateTime[2]; + fourcharvalue[3] = VDDateTime[3]; + year = Convert.ToInt32(Encoding.ASCII.GetString(fourcharvalue)); + + twocharvalue[0] = VDDateTime[4]; + twocharvalue[1] = VDDateTime[5]; + month = Convert.ToInt32(Encoding.ASCII.GetString(twocharvalue)); + + twocharvalue[0] = VDDateTime[6]; + twocharvalue[1] = VDDateTime[7]; + day = Convert.ToInt32(Encoding.ASCII.GetString(twocharvalue)); + + twocharvalue[0] = VDDateTime[8]; + twocharvalue[1] = VDDateTime[9]; + hour = Convert.ToInt32(Encoding.ASCII.GetString(twocharvalue)); + + twocharvalue[0] = VDDateTime[10]; + twocharvalue[1] = VDDateTime[11]; + minute = Convert.ToInt32(Encoding.ASCII.GetString(twocharvalue)); + + twocharvalue[0] = VDDateTime[12]; + twocharvalue[1] = VDDateTime[13]; + second = Convert.ToInt32(Encoding.ASCII.GetString(twocharvalue)); + + twocharvalue[0] = VDDateTime[14]; + twocharvalue[1] = VDDateTime[15]; + hundredths = Convert.ToInt32(Encoding.ASCII.GetString(twocharvalue)); + + DateTime decodedDT = new DateTime(year, month, day, hour, minute, second, hundredths * 10, DateTimeKind.Unspecified); + + return decodedDT; + } + } +} diff --git a/FileSystemIDandChk/Plugins/Opera.cs b/FileSystemIDandChk/Plugins/Opera.cs new file mode 100644 index 000000000..604d84dd6 --- /dev/null +++ b/FileSystemIDandChk/Plugins/Opera.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Text; +using FileSystemIDandChk; + +// Information from Inside Macintosh + +namespace FileSystemIDandChk.Plugins +{ + class OperaFS : Plugin + { + public OperaFS(PluginBase Core) + { + base.Name = "Opera Filesystem Plugin"; + base.PluginUUID = new Guid("0ec84ec7-eae6-4196-83fe-943b3fe46dbd"); + } + + public override bool Identify(FileStream fileStream, long offset) + { + fileStream.Seek(0 + offset, SeekOrigin.Begin); + + byte record_type; + byte[] sync_bytes = new byte[5]; + byte record_version; + + record_type = (byte)fileStream.ReadByte(); + fileStream.Read(sync_bytes, 0, 5); + record_version = (byte)fileStream.ReadByte(); + + if (record_type != 1 || record_version != 1) + return false; + if(Encoding.ASCII.GetString(sync_bytes) != "ZZZZZ") + return false; + + return true; + } + + public override void GetInformation (FileStream fileStream, long offset, out string information) + { + information = ""; + StringBuilder SuperBlockMetadata = new StringBuilder(); + + fileStream.Seek(0 + offset, SeekOrigin.Begin); + + byte[] record_type = new byte[1]; + byte[] sync_bytes = new byte[5]; + byte[] record_version = new byte[1]; + byte[] volume_flags = new byte[1]; + byte[] volume_comment = new byte[32]; + byte[] volume_label = new byte[32]; + byte[] volume_id = new byte[4]; + byte[] block_size = new byte[4]; + byte[] block_count = new byte[4]; + byte[] root_dirid = new byte[4]; + byte[] rootdir_blocks = new byte[4]; + byte[] rootdir_bsize = new byte[4]; + byte[] last_root_copy = new byte[4]; + + fileStream.Read(record_type, 0, 1); + fileStream.Read(sync_bytes, 0, 5); + fileStream.Read(record_version, 0, 1); + fileStream.Read(volume_flags, 0, 1); + fileStream.Read(volume_comment, 0, 32); + fileStream.Read(volume_label, 0, 32); + fileStream.Read(volume_id, 0, 4); + fileStream.Read(block_size, 0, 4); + fileStream.Read(block_count, 0, 4); + fileStream.Read(root_dirid, 0, 4); + fileStream.Read(rootdir_blocks, 0, 4); + fileStream.Read(rootdir_bsize, 0, 4); + fileStream.Read(last_root_copy, 0, 4); + + if (record_type[0] != 1 || record_version[0] != 1) + return; + if(Encoding.ASCII.GetString(sync_bytes) != "ZZZZZ") + return; + + // Swapping data (C# is LE, Opera is BE) + volume_id = Swapping.SwapFourBytes(volume_id); + block_size = Swapping.SwapFourBytes(block_size); + block_count = Swapping.SwapFourBytes(block_count); + root_dirid = Swapping.SwapFourBytes(root_dirid); + rootdir_blocks = Swapping.SwapFourBytes(rootdir_blocks); + rootdir_bsize = Swapping.SwapFourBytes(rootdir_bsize); + last_root_copy = Swapping.SwapFourBytes(last_root_copy); + + int vid = BitConverter.ToInt32(volume_id, 0); + int rdid = BitConverter.ToInt32(root_dirid, 0); + + StringBuilder VolumeComment = new StringBuilder(); + + for (int i = 0; i < volume_comment.Length; i++) + { + if (volume_comment[i] != 0x00) + VolumeComment.Append((char)volume_comment[i]); + else + break; + } + if (VolumeComment.Length == 0) + VolumeComment.Append("Not set."); + + StringBuilder VolumeLabel = new StringBuilder(); + + for (int i = 0; i < volume_label.Length; i++) + { + if (volume_label[i] != 0x00) + VolumeLabel.Append((char)volume_label[i]); + else + break; + } + if (VolumeLabel.Length == 0) + VolumeLabel.Append("Not set."); + + int bs = BitConverter.ToInt32(block_size, 0); + int vblocks = BitConverter.ToInt32(block_count, 0); + int rbs = BitConverter.ToInt32(rootdir_bsize, 0); + int rblocks = BitConverter.ToInt32(rootdir_blocks, 0); + + SuperBlockMetadata.AppendFormat("Opera filesystem disc.").AppendLine(); + SuperBlockMetadata.AppendFormat("Volume label: {0}", VolumeLabel.ToString()).AppendLine(); + SuperBlockMetadata.AppendFormat("Volume comment: {0}", VolumeComment.ToString()).AppendLine(); + SuperBlockMetadata.AppendFormat("Volume identifier: 0x{0}", vid.ToString("X")).AppendLine(); + SuperBlockMetadata.AppendFormat("Block size: {0} bytes", bs).AppendLine(); + SuperBlockMetadata.AppendFormat("Volume size: {0} blocks, {1} bytes", vblocks, bs*vblocks).AppendLine(); + SuperBlockMetadata.AppendFormat("Root directory identifier: 0x{0}", rdid.ToString("X")).AppendLine(); + SuperBlockMetadata.AppendFormat("Root directory block size: {0} bytes", rbs).AppendLine(); + SuperBlockMetadata.AppendFormat("Root directory size: {0} blocks, {1} bytes", rblocks, rbs*rblocks).AppendLine(); + SuperBlockMetadata.AppendFormat("Last root directory copy: {0}", BitConverter.ToInt32(last_root_copy, 0)).AppendLine(); + + information = SuperBlockMetadata.ToString(); + } + } +} + diff --git a/FileSystemIDandChk/Plugins/PCEngine.cs b/FileSystemIDandChk/Plugins/PCEngine.cs new file mode 100644 index 000000000..eea17d365 --- /dev/null +++ b/FileSystemIDandChk/Plugins/PCEngine.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Text; +using FileSystemIDandChk; + +// Information from Inside Macintosh + +namespace FileSystemIDandChk.Plugins +{ + class PCEnginePlugin : Plugin + { + public PCEnginePlugin(PluginBase Core) + { + base.Name = "PC Engine CD Plugin"; + base.PluginUUID = new Guid("e5ee6d7c-90fa-49bd-ac89-14ef750b8af3"); + } + + public override bool Identify(FileStream stream, long offset) + { + byte[] system_descriptor = new byte[23]; + + stream.Seek(2080 + offset, SeekOrigin.Begin); + + stream.Read(system_descriptor, 0, 23); + + if(Encoding.ASCII.GetString(system_descriptor) == "PC Engine CD-ROM SYSTEM") + return true; + else + return false; + } + + public override void GetInformation (FileStream stream, long offset, out string information) + { + information = ""; + } + } +} + diff --git a/FileSystemIDandChk/Plugins/Plugin.cs b/FileSystemIDandChk/Plugins/Plugin.cs new file mode 100644 index 000000000..adb9136ea --- /dev/null +++ b/FileSystemIDandChk/Plugins/Plugin.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace FileSystemIDandChk.Plugins +{ + public abstract class Plugin + { + public string Name; + public Guid PluginUUID; + + protected Plugin() + { + } + + public abstract bool Identify(FileStream stream, long offset); + public abstract void GetInformation(FileStream stream, long offset, out string information); + } +} + diff --git a/FileSystemIDandChk/Swapping.cs b/FileSystemIDandChk/Swapping.cs new file mode 100644 index 000000000..0e9015350 --- /dev/null +++ b/FileSystemIDandChk/Swapping.cs @@ -0,0 +1,63 @@ +using System; + +namespace FileSystemIDandChk +{ + static class Swapping + { + public static byte[] SwapTenBytes(byte[] source) + { + byte[] destination = new byte[8]; + + destination[0] = source[9]; + destination[1] = source[8]; + destination[2] = source[7]; + destination[3] = source[6]; + destination[4] = source[5]; + destination[5] = source[4]; + destination[6] = source[3]; + destination[7] = source[2]; + destination[8] = source[1]; + destination[9] = source[0]; + + return destination; + } + + public static byte[] SwapEightBytes(byte[] source) + { + byte[] destination = new byte[8]; + + destination[0] = source[7]; + destination[1] = source[6]; + destination[2] = source[5]; + destination[3] = source[4]; + destination[4] = source[3]; + destination[5] = source[2]; + destination[6] = source[1]; + destination[7] = source[0]; + + return destination; + } + + public static byte[] SwapFourBytes(byte[] source) + { + byte[] destination = new byte[4]; + + destination[0] = source[3]; + destination[1] = source[2]; + destination[2] = source[1]; + destination[3] = source[0]; + + return destination; + } + + public static byte[] SwapTwoBytes(byte[] source) + { + byte[] destination = new byte[2]; + + destination[0] = source[1]; + destination[1] = source[0]; + + return destination; + } + } +} diff --git a/Packages.mdproj b/Packages.mdproj new file mode 100644 index 000000000..9ea6e64c3 --- /dev/null +++ b/Packages.mdproj @@ -0,0 +1 @@ + PackagingProject 9.0.21022 2.0 {8996EF59-09B9-4920-A3DE-2F8EA2EBBCFF} \ No newline at end of file