Example #1
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);
        }
Example #2
0
        private void RenameLocalDirectoryOrFile(byte[] directoryData, int directoryEntryIndex, LocalDirectoryContentInfo directoryContentInfo, string newContentName)
        {
            string oldContentPath = directoryContentInfo.LocalPath;

            directoryContentInfo.LocalFileName = newContentName;
            directoryContentInfo.TOSFileName   = newContentName;

            if (directoryData[directoryEntryIndex + 11] == FAT16Helper.DirectoryIdentifier)
            {
                _logger.Log($"Renaming local directory \"{oldContentPath}\" to \"{directoryContentInfo.LocalPath}\"", Constants.LoggingLevel.Info);
                Directory.Move(GetAbsolutePath(oldContentPath), GetAbsolutePath(directoryContentInfo.LocalPath));
            }

            // It's a file
            else
            {
                _logger.Log($"Renaming local file \"{oldContentPath}\" to \"{directoryContentInfo.LocalPath}\"", Constants.LoggingLevel.Info);
                File.Move(GetAbsolutePath(oldContentPath), GetAbsolutePath(directoryContentInfo.LocalPath));
            }
        }
Example #3
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);
            }
        }
Example #4
0
        private void DeleteLocalDirectoryOrFile(List <LocalDirectoryContentInfo> localDirectoryContentInfos, byte[] directoryData, int directoryEntryIndex, LocalDirectoryContentInfo directoryContentInfo)
        {
            if (directoryData[directoryEntryIndex + 11] == FAT16Helper.DirectoryIdentifier)
            {
                _logger.Log($"Deleting local directory \"{ directoryContentInfo.LocalPath}\"", Constants.LoggingLevel.Info);

                Directory.Delete(GetAbsolutePath(directoryContentInfo.LocalPath), true);
            }

            // It's a file
            else
            {
                _logger.Log($"Deleting local file \"{directoryContentInfo.LocalPath}\"", Constants.LoggingLevel.Info);

                File.Delete(GetAbsolutePath(directoryContentInfo.LocalPath));
            }

            var clusterIndexesToDelete = _clusterInfos
                                         .Select((ci, index) => new { ci, index })
                                         .Where(_ => _.ci?.LocalDirectoryContent == directoryContentInfo)
                                         .Select(_ => _.index);

            foreach (int clusterIndex in clusterIndexesToDelete)
            {
                _logger.Log($"Removing local data for cluster {clusterIndex}", Constants.LoggingLevel.All);
                _clusterInfos[clusterIndex] = null;
            }

            localDirectoryContentInfos.Remove(directoryContentInfo);
        }