Exemplo n.º 1
0
        private void ImportLocalDirectory(List <LocalDirectoryContentInfo> localDirectoryContentInfos, DirectoryInfo directoryInfo, int parentDirectoryCluster)
        {
            int newDirectoryClusterIndex = AddNextFreeClusterToChain();

            _clusterInfos[newDirectoryClusterIndex] = new ClusterInfo()
            {
                FileOffset = -1,
                DataBuffer = new byte[Parameters.BytesPerCluster]
            };

            FatAddDirectoryEntry(localDirectoryContentInfos,
                                 parentDirectoryCluster,
                                 newDirectoryClusterIndex,
                                 directoryInfo.Parent.FullName,
                                 directoryInfo.Name,
                                 FAT16Helper.GetShortFileName(directoryInfo.Name),
                                 FAT16Helper.DirectoryIdentifier,
                                 directoryInfo.LastWriteTime,
                                 0);

            FatAddDirectoryEntry(localDirectoryContentInfos, newDirectoryClusterIndex, newDirectoryClusterIndex, directoryInfo.FullName, ".", ".", FAT16Helper.DirectoryIdentifier, directoryInfo.LastWriteTime, 0);
            FatAddDirectoryEntry(localDirectoryContentInfos, newDirectoryClusterIndex, parentDirectoryCluster, directoryInfo.Parent.FullName, "..", "..", FAT16Helper.DirectoryIdentifier, directoryInfo.LastWriteTime, 0);

            ImportLocalDiskContent(localDirectoryContentInfos, directoryInfo.FullName, newDirectoryClusterIndex);
        }
Exemplo n.º 2
0
        private void ImportLocalFile(List <LocalDirectoryContentInfo> localDirectoryContentInfos, FileInfo fileInfo, int directoryClusterIndex)
        {
            long fileOffset = 0;
            int  fileentryStartClusterIndex = 0;
            int? nextFileClusterIndex       = null;

            while (fileOffset < fileInfo.Length)
            {
                try
                {
                    nextFileClusterIndex = AddNextFreeClusterToChain(nextFileClusterIndex);

                    if (fileentryStartClusterIndex == _rootDirectoryClusterIndex)
                    {
                        fileentryStartClusterIndex = nextFileClusterIndex.Value;
                    }

                    _clusterInfos[nextFileClusterIndex.Value] = new ClusterInfo()
                    {
                        FileOffset = fileOffset
                    };

                    fileOffset += Parameters.BytesPerCluster;
                }

                catch (IndexOutOfRangeException outOfRangeEx)
                {
                    int localDirectorySizeMiB = (int)Directory.GetFiles(Parameters.LocalDirectoryPath, "*", SearchOption.AllDirectories).Sum(file => (new FileInfo(file).Length)) / FAT16Helper.BytesPerMiB;
                    _logger.LogException(outOfRangeEx, $"Local directory size is {localDirectorySizeMiB} MiB, which is too large for the given virtual disk size ({Parameters.DiskTotalBytes / FAT16Helper.BytesPerMiB} MiB)");
                    throw;
                }
            }

            // handle duplicate short filenames
            string TOSFileName = FAT16Helper.GetShortFileName(fileInfo.Name);
            int    duplicateId = 1;

            while (localDirectoryContentInfos.Where(ldi => ldi.TOSFileName.Equals(TOSFileName, StringComparison.InvariantCultureIgnoreCase) &&
                                                    ldi.DirectoryCluster == directoryClusterIndex).Any())
            {
                int numberStringLength = duplicateId.ToString().Length + 1; // +1 for ~
                int replaceIndex       = TOSFileName.LastIndexOf('.') != -1 ? TOSFileName.LastIndexOf('.') : TOSFileName.Length;
                replaceIndex -= numberStringLength;
                TOSFileName   = TOSFileName.Remove(replaceIndex, numberStringLength).Insert(replaceIndex, $"~{duplicateId}");
                duplicateId++;
            }

            FatAddDirectoryEntry(localDirectoryContentInfos, directoryClusterIndex, fileentryStartClusterIndex,
                                 fileInfo.DirectoryName, fileInfo.Name, TOSFileName, 0x00, fileInfo.LastWriteTime, fileInfo.Length);
        }
Exemplo n.º 3
0
        public Disk(DiskParameters diskParams, ILogger logger)
        {
            _logger    = logger;
            Parameters = diskParams;
            _rootDirectoryClusterIndex = 0;

            try
            {
                InitDiskContentVariables();

                int maxRootDirectoryEntries = ((diskParams.RootDirectorySectors * diskParams.BytesPerSector) / 32) - 2; // Each entry is 32 bytes, 2 entries reserved for . and ..
                FAT16Helper.ValidateLocalDirectory(diskParams.LocalDirectoryPath, diskParams.DiskTotalBytes, maxRootDirectoryEntries, diskParams.SectorsPerCluster, diskParams.TOS);
            }

            catch (Exception ex)
            {
                _logger.LogException(ex, ex.Message);
                throw;
            }

            ImportLocalDiskContent(_localDirectoryContentInfos, Parameters.LocalDirectoryPath, _rootDirectoryClusterIndex);
        }
Exemplo n.º 4
0
        private bool FatAddDirectoryEntry(List <LocalDirectoryContentInfo> localDirectoryContentInfos,
                                          int directoryClusterIndex, int entryStartClusterIndex,
                                          string directoryPath, string fileName, string TOSFileName,
                                          byte attributeFlags, DateTime lastWriteDateTime, long fileSize)
        {
            byte[] directoryBuffer;
            int    entryIndex = 0;

            int maxEntryIndex = directoryClusterIndex == _rootDirectoryClusterIndex ? _rootDirectoryBuffer.Length : Parameters.BytesPerCluster;

            if (directoryClusterIndex == _rootDirectoryClusterIndex)
            {
                directoryBuffer = _rootDirectoryBuffer;
            }
            else
            {
                directoryBuffer = _clusterInfos[directoryClusterIndex].DataBuffer;
            }

            // Check whether there is any space left in the cluster
            do
            {
                // No space left
                if (entryIndex >= maxEntryIndex)
                {
                    int nextDirectoryClusterIndex = FatGetClusterValue(directoryClusterIndex);

                    // This is the final cluster, allocate new cluster
                    if (FAT16Helper.IsEndOfFile(nextDirectoryClusterIndex))
                    {
                        try
                        {
                            int newDirectoryCluster = AddNextFreeClusterToChain(directoryClusterIndex);

                            _clusterInfos[newDirectoryCluster] = new ClusterInfo()
                            {
                                FileOffset = -1,
                                DataBuffer = new byte[Parameters.BytesPerCluster]
                            };

                            _clusterInfos[newDirectoryCluster].LocalDirectoryContent = _clusterInfos[directoryClusterIndex].LocalDirectoryContent;

                            entryIndex = 0;
                        }

                        catch (IndexOutOfRangeException outOfRangeEx)
                        {
                            int localDirectorySizeMiB = (int)Directory.GetFiles(Parameters.LocalDirectoryPath, "*", SearchOption.AllDirectories).Sum(file => (new FileInfo(file).Length)) / FAT16Helper.BytesPerMiB;
                            _logger.LogException(outOfRangeEx, $"Local directory size is {localDirectorySizeMiB} MiB, which is too large for the given virtual disk size ({Parameters.DiskTotalBytes / FAT16Helper.BytesPerMiB} MiB)");
                            throw;
                        }
                    }

                    else
                    {
                        directoryClusterIndex = nextDirectoryClusterIndex;
                    }

                    directoryBuffer = _clusterInfos[directoryClusterIndex].DataBuffer;
                    entryIndex      = 0;
                }

                // Find next unused entry in directory
                while (entryIndex < maxEntryIndex && directoryBuffer[entryIndex] != 0)
                {
                    entryIndex += 32;
                }

                if (entryIndex >= maxEntryIndex)
                {
                    if (directoryClusterIndex == _rootDirectoryClusterIndex)
                    {
                        Exception outofIndexesException = new Exception($"Exceeded available directory entries in {directoryPath}. There may be too many files in directory (max {(maxEntryIndex / 32) - 2} items).");
                        _logger.LogException(outofIndexesException, outofIndexesException.Message);
                        throw outofIndexesException;
                    }
                }
            } while (entryIndex >= maxEntryIndex);

            // Remember which local content matches this entry.

            if (TOSFileName[0] != '.')
            {
                LocalDirectoryContentInfo newLocalDirectoryContentInfo = new LocalDirectoryContentInfo()
                {
                    ParentDirectory  = _clusterInfos[directoryClusterIndex]?.LocalDirectoryContent,
                    LocalFileName    = fileName,
                    TOSFileName      = TOSFileName,
                    EntryIndex       = entryIndex,
                    DirectoryCluster = directoryClusterIndex,
                    StartCluster     = entryStartClusterIndex
                };

                localDirectoryContentInfos.Add(newLocalDirectoryContentInfo);

                // Cluster index 0 indicates empty file, so no data clusters to update
                if (entryStartClusterIndex != 0)
                {
                    _clusterInfos[entryStartClusterIndex].LocalDirectoryContent = newLocalDirectoryContentInfo;

                    int clusterValue = FatGetClusterValue(entryStartClusterIndex);

                    while (!FAT16Helper.IsEndOfFile(clusterValue))
                    {
                        _clusterInfos[clusterValue].LocalDirectoryContent = newLocalDirectoryContentInfo;
                        clusterValue = FatGetClusterValue(clusterValue);
                    }
                }
            }

            // File name.
            int fileNameIndex;

            for (fileNameIndex = 0; fileNameIndex < (8 + 3); fileNameIndex++)
            {
                directoryBuffer[entryIndex + fileNameIndex] = 0x20;
            }

            string[] nameAndExtender;
            byte[]   asciiName;
            byte[]   asciiExtender;

            if (TOSFileName == "." || TOSFileName == "..")
            {
                asciiName     = ASCIIEncoding.ASCII.GetBytes(TOSFileName);
                asciiExtender = null;
            }
            else
            {
                nameAndExtender = TOSFileName.Split('.');
                asciiName       = ASCIIEncoding.ASCII.GetBytes(nameAndExtender[0]);
                asciiExtender   = nameAndExtender.Length == 2 ? ASCIIEncoding.ASCII.GetBytes(nameAndExtender[1]) : null;
            }

            for (fileNameIndex = 0; fileNameIndex < asciiName.Length; fileNameIndex++)
            {
                directoryBuffer[entryIndex + fileNameIndex] = asciiName[fileNameIndex];
            }

            if (asciiExtender != null)
            {
                for (fileNameIndex = 0; fileNameIndex < asciiExtender.Length; fileNameIndex++)
                {
                    directoryBuffer[entryIndex + 8 + fileNameIndex] = asciiExtender[fileNameIndex];
                }
            }

            // File attribute flags.

            directoryBuffer[entryIndex + 11] = attributeFlags;

            // File write time and date (little endian).

            UInt16 fatFileWriteTime = 0;
            UInt16 fatFileWriteDate = 0;

            int TwoSeconds     = lastWriteDateTime.Second / 2;
            int Minutes        = lastWriteDateTime.Minute;
            int Hours          = lastWriteDateTime.Hour;
            int DayOfMonth     = lastWriteDateTime.Day;
            int Month          = lastWriteDateTime.Month;
            int YearsSince1980 = lastWriteDateTime.Year - 1980;

            fatFileWriteTime |= (UInt16)TwoSeconds;
            fatFileWriteTime |= (UInt16)(Minutes << 5);
            fatFileWriteTime |= (UInt16)(Hours << 11);

            fatFileWriteDate |= (UInt16)DayOfMonth;
            fatFileWriteDate |= (UInt16)(Month << 5);
            fatFileWriteDate |= (UInt16)(YearsSince1980 << 9);

            directoryBuffer[entryIndex + 22] = (byte)(fatFileWriteTime & 0xff);
            directoryBuffer[entryIndex + 23] = (byte)((fatFileWriteTime >> 8) & 0xff);
            directoryBuffer[entryIndex + 24] = (byte)(fatFileWriteDate & 0xff);
            directoryBuffer[entryIndex + 25] = (byte)((fatFileWriteDate >> 8) & 0xff);

            // Cluster (little endian).

            directoryBuffer[entryIndex + 26] = (byte)(entryStartClusterIndex & 0xff);
            directoryBuffer[entryIndex + 27] = (byte)((entryStartClusterIndex >> 8) & 0xff);

            // File size (little endian).

            directoryBuffer[entryIndex + 28] = (byte)(fileSize & 0xff);
            directoryBuffer[entryIndex + 29] = (byte)((fileSize >> 8) & 0xff);
            directoryBuffer[entryIndex + 30] = (byte)((fileSize >> 16) & 0xff);
            directoryBuffer[entryIndex + 31] = (byte)((fileSize >> 24) & 0xff);

            return(true);
        }
Exemplo n.º 5
0
        private void UpdateLocalDirectoryOrFile(List <LocalDirectoryContentInfo> localDirectoryContentInfos, byte[] directoryData,
                                                int directoryClusterIndex, int directoryEntryIndex, int entryStartClusterIndex, string newContentName)
        {
            string newContentPath = null;

            if (directoryClusterIndex != _rootDirectoryClusterIndex)
            {
                string newPathDirectory = _clusterInfos[directoryClusterIndex].LocalDirectoryContent.LocalPath;
                newContentPath = Path.Combine(newPathDirectory, newContentName);
            }

            else
            {
                newContentPath = newContentName;
            }

            try
            {
                if (directoryData[directoryEntryIndex + 11] == FAT16Helper.DirectoryIdentifier)
                {
                    // Is it a directory with a valid start cluster?
                    if (entryStartClusterIndex != 0)
                    {
                        _logger.Log("Creating local directory \"" + newContentPath + "\"", Constants.LoggingLevel.Info);

                        var CreatedLocalDirectory = Directory.CreateDirectory(GetAbsolutePath(newContentPath));

                        var newLocalDirectoryContent = new LocalDirectoryContentInfo
                        {
                            ParentDirectory  = _clusterInfos[directoryClusterIndex]?.LocalDirectoryContent,
                            LocalFileName    = newContentName,
                            TOSFileName      = newContentName,
                            EntryIndex       = directoryEntryIndex,
                            DirectoryCluster = directoryClusterIndex,
                            StartCluster     = entryStartClusterIndex,
                            FinalCluster     = entryStartClusterIndex,
                            WriteInProgress  = false
                        };

                        localDirectoryContentInfos.Add(newLocalDirectoryContent);

                        _clusterInfos[entryStartClusterIndex].LocalDirectoryContent = newLocalDirectoryContent;
                        _clusterInfos[entryStartClusterIndex].FileOffset            = FAT16Helper.DirectoryFileOffset;
                    }
                }

                // it's a file
                else
                {
                    _logger.Log($"Checking if local file {newContentPath} should be updated", Constants.LoggingLevel.Debug);

                    int fileSize = directoryData[directoryEntryIndex + 28] | (directoryData[directoryEntryIndex + 29] << 8) | (directoryData[directoryEntryIndex + 30] << 16) | (directoryData[directoryEntryIndex + 31] << 24);

                    _logger.Log("File size to write:" + fileSize, Constants.LoggingLevel.All);

                    int fileClusterIndex = entryStartClusterIndex;

                    _logger.Log("Finding existing content info for local file \"" + newContentPath + "\".", Constants.LoggingLevel.All);

                    var localDirectoryContent = localDirectoryContentInfos.Where(ldci => ldci.LocalPath == newContentPath).SingleOrDefault();

                    if (localDirectoryContent == null)
                    {
                        _logger.Log($"Creating local file: {newContentPath}", Constants.LoggingLevel.Info);
                        File.Create(GetAbsolutePath(newContentPath)).Dispose();

                        var newLocalDirectoryContent = new LocalDirectoryContentInfo
                        {
                            ParentDirectory  = _clusterInfos[directoryClusterIndex]?.LocalDirectoryContent,
                            LocalFileName    = newContentName,
                            TOSFileName      = newContentName,
                            EntryIndex       = directoryEntryIndex,
                            DirectoryCluster = directoryClusterIndex,
                            StartCluster     = entryStartClusterIndex,
                            FinalCluster     = -1
                        };

                        localDirectoryContentInfos.Add(newLocalDirectoryContent);

                        localDirectoryContent = newLocalDirectoryContent;
                    }

                    localDirectoryContent.StartCluster = entryStartClusterIndex;
                    _logger.Log("File start cluster: " + localDirectoryContent.StartCluster + " (" + FatGetClusterValue(localDirectoryContent.StartCluster) + ")", Constants.LoggingLevel.All);

                    // Entry cluster will be assigned if this is a non-empty file
                    if (entryStartClusterIndex != 0)
                    {
                        localDirectoryContent.WriteInProgress = true;
                        _logger.Log("Content marked for write: " + localDirectoryContent.WriteInProgress, Constants.LoggingLevel.All);

                        int remainingBytes = fileSize;

                        // Check if the file has been completely written.
                        // Number of bytes in the cluster chain must be checked in case this file is being written
                        // in multiple passes due to lack of available RAM on the Atari
                        while (!FAT16Helper.IsEndOfClusterChain(fileClusterIndex))
                        {
                            // Keep final cluster updated in case this file is being written in multiple passes
                            localDirectoryContent.FinalCluster = fileClusterIndex;
                            remainingBytes  -= Parameters.BytesPerCluster;
                            fileClusterIndex = FatGetClusterValue(fileClusterIndex);
                        }

                        bool allBytesAvailable = remainingBytes <= 0 && FAT16Helper.IsEndOfFile(fileClusterIndex);

                        _logger.Log("All bytes available?: " + allBytesAvailable, Constants.LoggingLevel.All);

                        // Final FAT cluster value in chain matches a fully written file
                        if (allBytesAvailable)
                        {
                            localDirectoryContent.WriteInProgress = false;

                            try
                            {
                                _logger.Log("Finding existing content info for local file \"" + newContentPath + "\".", Constants.LoggingLevel.All);

                                localDirectoryContent = localDirectoryContentInfos.Where(ldci => ldci.LocalPath == newContentPath).Single();

                                localDirectoryContent.StartCluster = entryStartClusterIndex;


                                _logger.Log("Writing to local file \"" + newContentPath + "\".", Constants.LoggingLevel.Info);

                                using (BinaryWriter FileBinaryWriter = new BinaryWriter(File.OpenWrite(GetAbsolutePath(newContentPath))))
                                {
                                    fileClusterIndex = entryStartClusterIndex;
                                    remainingBytes   = fileSize;
                                    int fileOffset = 0;

                                    while (!FAT16Helper.IsEndOfFile(fileClusterIndex))
                                    {
                                        _clusterInfos[fileClusterIndex].LocalDirectoryContent = localDirectoryContent;
                                        _clusterInfos[fileClusterIndex].FileOffset            = fileOffset;

                                        FileBinaryWriter.Write(_clusterInfos[fileClusterIndex].DataBuffer, 0, Math.Min(_clusterInfos[fileClusterIndex].DataBuffer.Length, remainingBytes));

                                        remainingBytes -= _clusterInfos[fileClusterIndex].DataBuffer.Length;
                                        fileOffset     += _clusterInfos[fileClusterIndex].DataBuffer.Length;

                                        // Buffer has been written to disk; free up RAM
                                        _clusterInfos[fileClusterIndex].DataBuffer = null;

                                        localDirectoryContent.FinalCluster = fileClusterIndex;
                                        fileClusterIndex = FatGetClusterValue(fileClusterIndex);
                                    }

                                    _logger.Log("Bytes remaining after write:" + remainingBytes, Constants.LoggingLevel.All);
                                }
                            }

                            catch (Exception ex)
                            {
                                _logger.LogException(ex);
                            }
                        }
                    }
                }
            }

            catch (Exception ex)
            {
                _logger.LogException(ex);
            }
        }
Exemplo n.º 6
0
        private void SyncDirectoryClusterToLocalDisk(List <LocalDirectoryContentInfo> localDirectoryContentInfos, int clusterIndex)
        {
            byte[] directoryData;

            directoryData = GetDirectoryClusterData(clusterIndex);

            // Only check for changes if this cluster contains directory entry information
            if (clusterIndex == _rootDirectoryClusterIndex || _clusterInfos[clusterIndex].FileOffset == -1)
            {
                bool continueCheckingEntries = true;

                do
                {
                    _logger.Log($"Updating directory cluster {clusterIndex}", Constants.LoggingLevel.All);
                    int directoryEntryIndex = 0;

                    while (directoryEntryIndex < directoryData.Length && directoryData[directoryEntryIndex] != 0)
                    {
                        // The entry is not "." or "..".
                        if (directoryData[directoryEntryIndex] != 0x2e)
                        {
                            string fileName      = ASCIIEncoding.ASCII.GetString(directoryData, directoryEntryIndex, 8).Trim();
                            string fileExtension = ASCIIEncoding.ASCII.GetString(directoryData, directoryEntryIndex + 8, 3).Trim();

                            if (fileExtension != "")
                            {
                                fileName += "." + fileExtension;
                            }

                            int entryStartClusterIndex = directoryData[directoryEntryIndex + 26] | (directoryData[directoryEntryIndex + 27] << 8);

                            // Find the matching local content and check what happened to it.
                            var localContent = FindLocalDirectoryContentInfo(localDirectoryContentInfos, clusterIndex, directoryEntryIndex, entryStartClusterIndex);

                            if (localContent != null)
                            {
                                if (localContent.TOSFileName != fileName)
                                {
                                    if (directoryData[directoryEntryIndex] == FAT16Helper.DeletedEntryIdentifier)
                                    {
                                        DeleteLocalDirectoryOrFile(localDirectoryContentInfos, directoryData, directoryEntryIndex, localContent);
                                    }

                                    else
                                    {
                                        RenameLocalDirectoryOrFile(directoryData, directoryEntryIndex, localContent, fileName);
                                    }
                                }
                            }

                            // Entry is new
                            else if (directoryData[directoryEntryIndex] != FAT16Helper.DeletedEntryIdentifier)
                            {
                                UpdateLocalDirectoryOrFile(localDirectoryContentInfos, directoryData, clusterIndex, directoryEntryIndex, entryStartClusterIndex, fileName);
                            }
                        }

                        directoryEntryIndex += 32;
                    }

                    if (directoryEntryIndex < directoryData.Length || FAT16Helper.IsEndOfClusterChain(FatGetClusterValue(clusterIndex)))
                    {
                        continueCheckingEntries = false;
                    }

                    else
                    {
                        var nextClusterIndex = FatGetClusterValue(clusterIndex);
                        _logger.Log($"Directory cluster {clusterIndex} continues in cluster {nextClusterIndex}", Constants.LoggingLevel.All);

                        // If the next directory cluster is new, ensure it has a LocalDirectoryContentInfo assigned
                        if (_clusterInfos[nextClusterIndex].LocalDirectoryContent == null)
                        {
                            _clusterInfos[nextClusterIndex].LocalDirectoryContent = _clusterInfos[clusterIndex].LocalDirectoryContent;
                            _clusterInfos[nextClusterIndex].FileOffset            = FAT16Helper.DirectoryFileOffset;
                            _logger.Log($"Directory cluster {clusterIndex} extended to cluster {nextClusterIndex}", Constants.LoggingLevel.All);
                        }

                        clusterIndex = nextClusterIndex;

                        directoryData = GetDirectoryClusterData(clusterIndex);
                    }
                } while (continueCheckingEntries);
            }
        }
Exemplo n.º 7
0
        public void ValidDiskSizes(TOSVersion tosVersion, int expectedMaxDiskSizeBytes)
        {
            var maxDiskSizeBytes = FAT16Helper.MaxDiskSizeBytes(tosVersion, _sectorsPerCluster);

            Assert.AreEqual(expectedMaxDiskSizeBytes, maxDiskSizeBytes);
        }
Exemplo n.º 8
0
        public void CreateShortFileNameFromInvalidFileName(string invalidFileName, string expectedShortFileName)
        {
            var shortFileName = FAT16Helper.GetShortFileName(invalidFileName);

            Assert.AreEqual(expectedShortFileName, shortFileName);
        }
Exemplo n.º 9
0
        public void CreateShortFileNameFromLongFileName(string longFileName, string expectedShortFileName)
        {
            var shortFileName = FAT16Helper.GetShortFileName(longFileName);

            Assert.AreEqual(expectedShortFileName, shortFileName);
        }