예제 #1
0
 public FatStream(FatDirectoryEntry entry)
 {
     DirectoryEntry = entry ?? throw new ArgumentNullException(nameof(entry));
     FS             = entry.GetFileSystem();
     FatTable       = entry.GetFatTable();
     Size           = entry.Size;
     if (FatTable == null)
     {
         throw new Exception("The fat chain returned for the directory entry was null.");
     }
 }
예제 #2
0
        public FatDirectoryEntry(FatFileSystem fileSystem, FatDirectoryEntry parent, string fullPath, long size, string name, uint firstCluster)
            : base(fileSystem, parent, fullPath, name, size, DirectoryEntryType.Directory)
        {
            if (firstCluster < fileSystem.RootCluster)
            {
                throw new ArgumentOutOfRangeException(nameof(firstCluster));
            }

            FirstClusterNum       = firstCluster;
            EntryHeaderDataOffset = 0;
        }
예제 #3
0
        public FatDirectoryEntry FindVolumeId()
        {
            if (!IsRootDirectory())
            {
                throw new Exception("VolumeId can be found only in Root Directory");
            }

            var data   = GetDirectoryEntryData();
            var parent = this;

            FatDirectoryEntry result = null;

            for (var i = 0; i < data.Length; i += 32)
            {
                var attrib = data[i + 11];

                if (attrib != FatDirectoryEntryAttributes.VolumeID)
                {
                    continue;
                }

                // The Label in FAT could be only a shortName (limited to 11 characters) so it is more easy
                var name = Encoding.ASCII.GetString(data, i, 11);
                name = name.TrimEnd();

                var fullPath = Path.Combine(FullPath, name);
                // Probably can be OK to hardcode 0 here
                var size = BitConverter.ToUInt32(data, i + 28);
                //var firstCluster = (uint)(data.ToUInt16(i + 20) << 16 | data.ToUInt16(i + 26));
                var firstCluster = parent.FirstClusterNum;

                result = new FatDirectoryEntry(((FatFileSystem)FileSystem), parent, fullPath, name, size, firstCluster, (uint)i, DirectoryEntryType.File);
                break;
            }

            if (result == null)
            {
                Debugger.Log(0, "FileSystem", $"VolumeID not found, returning null");
            }
            return(result);
        }
예제 #4
0
        /// <summary>
        /// Retrieves a <see cref="List{T}"/> of <see cref="FatDirectoryEntry"/> objects that represent the Directory Entries inside this Directory
        /// </summary>
        /// <returns>Returns a <see cref="List{T}"/> of the Directory Entries inside this Directory</returns>
        public List <FatDirectoryEntry> ReadDirectoryContents(bool returnShortFilenames = false)
        {
            var data   = GetDirectoryEntryData();
            var result = new List <FatDirectoryEntry>();
            FatDirectoryEntry parent = this;

            //TODO: Change longName to StringBuilder
            var longName = string.Empty;
            var name     = string.Empty;

            for (var i = 0; i < data.Length; i += 32)
            {
                var attrib = data[i + 11];
                var status = data[i];

                if (attrib == FatDirectoryEntryAttributes.LongName)
                {
                    var type = data[i + 12];

                    if (returnShortFilenames)
                    {
                        continue;
                    }

                    if (status == FatDirectoryEntryAttributes.UnusedOrDeletedEntry)
                    {
                        Debugger.Log(0, "FileSystem", $"<DELETED> : Attrib = {attrib}, Status = {status}");
                        continue;
                    }

                    if (type == 0)
                    {
                        if ((status & 0x40) > 0)
                        {
                            longName = string.Empty;
                        }

                        // TODO: Check LDIR_Ord for ordering and throw exception if entries are found out of order.
                        // Also save buffer and only copy name if a end Ord marker is found.
                        var longPart = Encoding.Unicode.GetString(data, i + 1, 10);

                        // We have to check the length because 0xFFFF is a valid Unicode codepoint. So we only want to stop if the 0xFFFF is AFTER a 0x0000. We can determin
                        // this by also looking at the length. Since we short circuit the or, the length is rarely evaluated.
                        if (BitConverter.ToUInt16(data, i + 14) != 0xFFFF || longPart.Length == 5)
                        {
                            longPart += Encoding.Unicode.GetString(data, i + 14, 12);
                            if (BitConverter.ToUInt16(data, i + 28) != 0xFFFF || longPart.Length == 11)
                            {
                                longPart += Encoding.Unicode.GetString(data, i + 28, 4);
                            }
                        }

                        longName = longPart + longName;
                        longPart = null;
                        //TODO: LDIR_Chksum
                    }
                }
                else
                {
                    name = longName;

                    if (status == 0x00)
                    {
                        Debugger.Log(0, "FileSystem", $"<EOF> : Attrib = {attrib}, Status = {status}");
                        break;
                    }

                    switch (status)
                    {
                    case 0x05: break;                                                // Japanese characters - We dont handle these

                    case 0x2E: continue;                                             // Dot entry

                    case FatDirectoryEntryAttributes.UnusedOrDeletedEntry: continue; // Empty slot, skip it

                    default:
                        var test = attrib & (FatDirectoryEntryAttributes.Directory | FatDirectoryEntryAttributes.VolumeID);

                        if (status >= 0x20)
                        {
                            if (longName.Length > 0)
                            {
                                // Leading and trailing spaces are to be ignored according to spec.
                                // Many programs (including Windows) pad trailing spaces although it is not required for long names.
                                // As per spec, ignore trailing periods
                                name = longName.Trim(new[] { '\0', '\uffff' }).Trim();

                                // If there are trailing periods
                                var nameIndex = name.Length - 1;
                                if (name[nameIndex] == '.')
                                {
                                    // Search backwards till we find the first non-period character
                                    for (; nameIndex > 0; nameIndex--)
                                    {
                                        if (name[nameIndex] != '.')
                                        {
                                            break;
                                        }
                                    }

                                    // Substring to remove the periods
                                    name = name.Substring(0, nameIndex + 1);
                                }
                                longName = string.Empty;
                            }
                            else if (test == 0)
                            {
                                var entry = Encoding.ASCII.GetString(data, i, 11);
                                name = entry.Substring(0, 8).TrimEnd();
                                var ext = entry.Substring(8, 3).TrimEnd();
                                if (ext.Length > 0)
                                {
                                    name = name + "." + ext;
                                }
                            }
                            else
                            {
                                name = Encoding.ASCII.GetString(data, i, 11).TrimEnd();
                            }
                        }

                        var firstCluster = (uint)(BitConverter.ToUInt16(data, i + 20) << 16 | BitConverter.ToUInt16(data, i + 26));
                        if (test == 0)
                        {
                            var size = BitConverter.ToUInt32(data, i + 28);
                            if (size == 0 && name.Length == 0)
                            {
                                continue;
                            }

                            var fullPath = Path.Combine(FullPath, name);
                            var entry    = new FatDirectoryEntry(((FatFileSystem)FileSystem), parent, fullPath, name, size, firstCluster, (uint)i, DirectoryEntryType.File);
                            result.Add(entry);
                        }
                        else if (test == FatDirectoryEntryAttributes.Directory)
                        {
                            var fullPath = Path.Combine(FullPath, name);
                            var size     = BitConverter.ToUInt32(data, (int)i + 28);
                            var entry    = new FatDirectoryEntry(((FatFileSystem)FileSystem), parent, fullPath, name, size, firstCluster, (uint)i, DirectoryEntryType.Directory);
                            result.Add(entry);
                        }
                        else if (test == FatDirectoryEntryAttributes.VolumeID)
                        {
                            Debugger.Log(0, "FileSystem", $"<VOLUME ID> : Attrib = {attrib}, Status = {status}");
                        }
                        else
                        {
                            Debugger.Log(0, "FileSystem", $"<INVALID ENTRY> : Attrib = {attrib}, Status = {status}");
                        }
                        break;
                    }
                }
            }
            return(result);
        }
예제 #5
0
        public FatDirectoryEntry AddDirectoryEntry(string name, DirectoryEntryType entryType)
        {
            if (entryType == DirectoryEntryType.Directory || entryType == DirectoryEntryType.File)
            {
                var    shortName = name;
                uint[] directoryEntriesToAllocate = null;

                var x1 = entryType == DirectoryEntryType.File;
                var x2 = name.Contains(".");
                var x3 = x2 ? name.Substring(0, name.LastIndexOf('.')).Contains(".") : false;
                var x4 = x2 ? name.Substring(0, name.IndexOf('.')).Length > 8 : false;
                var x5 = x2 ? name.Substring(name.IndexOf('.') + 1).Length > 3 : false;
                var x6 = entryType == DirectoryEntryType.Directory;
                var x7 = name.Length > 11;

                var x8 = x3 || (x4 || x5);
                var x9 = x2 && x8;

                var x10 = (x1 && x9) || (x6 && x7);

                if (x10)
                {
                    var longName = name;

                    var lastPeriodPosition = name.LastIndexOf('.');

                    var ext = string.Empty;

                    // Only take the name until the first dot
                    if (lastPeriodPosition + 1 > 0 && lastPeriodPosition + 1 < name.Length)
                    {
                        ext = shortName.Substring(lastPeriodPosition + 1);
                    }

                    // Remove all whitespaces and dots (except final)
                    for (var i = shortName.Length - 1; i > 0; i--)
                    {
                        var c = shortName[i];
                        if (char.IsWhiteSpace(c) || (c == '.' && i != lastPeriodPosition))
                        {
                            shortName.Remove(i, 1);
                        }
                    }

                    var invalidShortNameChars = new[] { '"', '*', '+', ',', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '|' };

                    // Remove all invalid characters
                    foreach (var invalidChar in invalidShortNameChars)
                    {
                        shortName.Replace(invalidChar, '_');
                    }

                    var n = 1;
                    var directoryEntries = ReadDirectoryContents(true);
                    var shortFilenames   = new string[directoryEntries.Count];
                    for (var i = 0; i < directoryEntries.Count; i++)
                    {
                        shortFilenames[i] = directoryEntries[i].Name;
                    }

                    var nameTry = string.Empty;
                    var test    = false;
                    do
                    {
                        nameTry = (shortName.Substring(0, 7 - n.ToString().Length) + "~" + n).ToUpperInvariant();
                        if (!string.IsNullOrEmpty(ext))
                        {
                            nameTry += '.' + ext.ToUpperInvariant();
                        }
                        n++;
                        test = false;
                        foreach (var name2 in shortFilenames)
                        {
                            if (name2 == nameTry)
                            {
                                test = true;
                                break;
                            }
                        }
                    }
                    //while (Array.IndexOf((Array)xShortFilenames, xNameTry) != -1); //TODO: use the generic version of IndexOf, just remove the cast to Array
                    while (test);

                    shortName = nameTry;
                    var checksum        = CalculateChecksum(GetShortName(shortName));
                    var numEntries      = (int)Math.Ceiling(longName.Length / 13d);
                    var longNameWithPad = new char[numEntries * 13];
                    longNameWithPad[longNameWithPad.Length - 1] = (char)0xFFFF;
                    Array.Copy(longName.ToCharArray(), longNameWithPad, longName.Length);

                    directoryEntriesToAllocate = GetNextUnallocatedDirectoryEntries(numEntries + 1);

                    for (var i = numEntries - 1; i >= 0; i--)
                    {
                        var entry = directoryEntriesToAllocate[numEntries - i - 1];

                        SetLongFilenameEntryMetadataValue(entry, FatDirectoryEntryMetadata.LongFilenameEntryMetadata.SequenceNumberAndAllocationStatus, (i + 1) | (i == numEntries - 1 ? (1 << 6) : 0));
                        SetLongFilenameEntryMetadataValue(entry, FatDirectoryEntryMetadata.LongFilenameEntryMetadata.Attributes, FatDirectoryEntryAttributes.LongName);
                        SetLongFilenameEntryMetadataValue(entry, FatDirectoryEntryMetadata.LongFilenameEntryMetadata.Checksum, checksum);

                        var a1 = new string(longNameWithPad, i * 13, 5);
                        var a2 = new string(longNameWithPad, i * 13 + 5, 6);
                        var a3 = new string(longNameWithPad, i * 13 + 11, 2);

                        SetLongFilenameEntryMetadataValue(entry, FatDirectoryEntryMetadata.LongFilenameEntryMetadata.LongName1, a1);
                        SetLongFilenameEntryMetadataValue(entry, FatDirectoryEntryMetadata.LongFilenameEntryMetadata.LongName2, a2);
                        SetLongFilenameEntryMetadataValue(entry, FatDirectoryEntryMetadata.LongFilenameEntryMetadata.LongName3, a3);
                    }
                }

                var fullPath              = Path.Combine(FullPath, name);
                var firstCluster          = ((FatFileSystem)FileSystem).GetFat(0).GetNextUnallocatedFatEntry();
                var entryHeaderDataOffset = directoryEntriesToAllocate == null?GetNextUnallocatedDirectoryEntry() : directoryEntriesToAllocate[directoryEntriesToAllocate.Length - 1];

                var newEntry = new FatDirectoryEntry((FatFileSystem)FileSystem, this, fullPath, name, 0, firstCluster, entryHeaderDataOffset, entryType);

                newEntry.AllocateDirectoryEntry(shortName);

                return(newEntry);
            }

            throw new ArgumentOutOfRangeException(nameof(entryType), "Unknown directory entry type.");
        }