public static async Task <LoadSegBlock> Parse(byte[] blockBytes)
        {
            var blockStream = new MemoryStream(blockBytes);

            var identifier = await blockStream.ReadAsciiString(); // Identifier 32 bit word : 'LSEG'

            if (!identifier.Equals(BlockIdentifiers.LoadSegBlock))
            {
                throw new IOException("Invalid load seg block identifier");
            }

            var size = await blockStream.ReadUInt32();            // Size of the structure for checksums

            var checksum = await blockStream.ReadInt32();         // Checksum of the structure

            var hostId = await blockStream.ReadUInt32();          // SCSI Target ID of host

            var nextLoadSegBlock = await blockStream.ReadInt32(); // block number of the next LoadSegBlock, -1 for last

            var calculatedChecksum = await BlockHelper.CalculateChecksum(blockBytes, 8);

            if (checksum != calculatedChecksum)
            {
                throw new IOException("Invalid load seg block checksum");
            }

            var length = (size - 5) * 4;
            var data   = new byte[length];

            Array.Copy(blockBytes, 5 * 4, data, 0, length);

            return(new LoadSegBlock
            {
                BlockBytes = blockBytes,
                Checksum = checksum,
                HostId = hostId,
                NextLoadSegBlock = nextLoadSegBlock,
                Data = data
            });
        }
        public static async Task <IEnumerable <FileSystemHeaderBlock> > Read(
            RigidDiskBlock rigidDiskBlock, Stream stream)
        {
            if (rigidDiskBlock.FileSysHdrList == BlockIdentifiers.EndOfBlock)
            {
                return(Enumerable.Empty <FileSystemHeaderBlock>());
            }

            var fileSystemHeaderBlocks = new List <FileSystemHeaderBlock>();

            var fileSysHdrList = rigidDiskBlock.FileSysHdrList;

            do
            {
                // calculate file system header block offset
                var fileSystemHeaderBlockOffset = rigidDiskBlock.BlockSize * fileSysHdrList;

                // seek partition block offset
                stream.Seek(fileSystemHeaderBlockOffset, SeekOrigin.Begin);

                // read block
                var block = await BlockHelper.ReadBlock(stream);

                // parse file system header block
                var fileSystemHeaderBlock = await Parse(block);

                fileSystemHeaderBlocks.Add(fileSystemHeaderBlock);

                // get next partition list block and increase partition number
                fileSysHdrList = fileSystemHeaderBlock.NextFileSysHeaderBlock;
            } while (fileSysHdrList > 0 && fileSysHdrList != BlockIdentifiers.EndOfBlock);

            foreach (var fileSystemHeaderBlock in fileSystemHeaderBlocks)
            {
                fileSystemHeaderBlock.LoadSegBlocks =
                    await LoadSegBlockReader.Read(rigidDiskBlock, fileSystemHeaderBlock, stream);
            }

            return(fileSystemHeaderBlocks);
        }
Example #3
0
        public static async Task <RigidDiskBlock> Read(Stream stream)
        {
            var            rdbIndex         = 0;
            var            blockSize        = 512;
            var            rdbLocationLimit = 16;
            RigidDiskBlock rigidDiskBlock;

            // read rigid disk block from one of the first 15 blocks
            do
            {
                // calculate block offset
                var blockOffset = blockSize * rdbIndex;

                // seek block offset
                stream.Seek(blockOffset, SeekOrigin.Begin);

                // read block
                var block = await BlockHelper.ReadBlock(stream);

                // read rigid disk block
                rigidDiskBlock = await Parse(block);

                rdbIndex++;
            } while (rdbIndex < rdbLocationLimit && rigidDiskBlock == null);

            // fail, if rigid disk block is null
            if (rigidDiskBlock == null)
            {
                return(null);
            }

            rigidDiskBlock.PartitionBlocks = await PartitionBlockReader.Read(rigidDiskBlock, stream);

            rigidDiskBlock.BadBlocks = await BadBlockReader.Read(rigidDiskBlock, stream);

            return(rigidDiskBlock);
        }
        public static async Task <BadBlock> Parse(byte[] blockBytes)
        {
            var blockStream = new MemoryStream(blockBytes);

            var identifier = await blockStream.ReadAsciiString(); // Identifier 32 bit word : 'BADB'

            if (!identifier.Equals(BlockIdentifiers.BadBlock))
            {
                throw new IOException("Invalid bad block identifier");
            }

            await blockStream.ReadUInt32();                    // Size of the structure for checksums

            var checksum = await blockStream.ReadInt32();      // Checksum of the structure

            var hostId = await blockStream.ReadUInt32();       // SCSI Target ID of host, not really used

            var nextBadBlock = await blockStream.ReadUInt32(); // next BadBlock block

            var calculatedChecksum = await BlockHelper.CalculateChecksum(blockBytes, 8);

            if (checksum != calculatedChecksum)
            {
                throw new IOException("Invalid bad block checksum");
            }

            var data = await blockStream.ReadBytes(((blockBytes.Length / 4) - 6) / 2);

            return(new BadBlock
            {
                BlockBytes = blockBytes,
                Checksum = checksum,
                HostId = hostId,
                NextBadBlock = nextBadBlock,
                Data = data
            });
        }
        public static async Task <byte[]> BuildBlock(PartitionBlock partitionBlock)
        {
            var blockStream =
                new MemoryStream(partitionBlock.BlockBytes == null || partitionBlock.BlockBytes.Length == 0
                    ? new byte[BlockSize.PartitionBlock * 4]
                    : partitionBlock.BlockBytes);

            await blockStream.WriteAsciiString(BlockIdentifiers.PartitionBlock);

            await blockStream.WriteLittleEndianUInt32(BlockSize.PartitionBlock); // size

            // skip checksum, calculated when block is built
            blockStream.Seek(4, SeekOrigin.Current);

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .HostId); // SCSI Target ID of host, not really used

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .NextPartitionBlock);  // Block number of the next PartitionBlock

            await blockStream.WriteLittleEndianUInt32(partitionBlock.Flags); // Part Flags (NOMOUNT and BOOTABLE)

            // skip reserved
            blockStream.Seek(4 * 2, SeekOrigin.Current);

            await blockStream.WriteLittleEndianUInt32(partitionBlock.DevFlags); // Preferred flags for OpenDevice

            var driveName = partitionBlock.DriveName.Length > 31
                ? partitionBlock.DriveName.Substring(0, 31)
                : partitionBlock.DriveName;

            await blockStream.WriteBytes(new[] { Convert.ToByte(driveName.Length) });

            await blockStream.WriteString(driveName, 31);

            // skip reserved
            blockStream.Seek(4 * 15, SeekOrigin.Current);

            await blockStream.WriteLittleEndianUInt32(partitionBlock.SizeOfVector); // Size of Environment vector

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .SizeBlock);              // Size of the blocks in 32 bit words, usually 128

            await blockStream.WriteLittleEndianUInt32(partitionBlock.SecOrg);   // Not used; must be 0

            await blockStream.WriteLittleEndianUInt32(partitionBlock.Surfaces); // Number of heads (surfaces)

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .Sectors); // Disk sectors per block, used with SizeBlock, usually 1

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .BlocksPerTrack); // Blocks per track. drive specific

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .Reserved); // DOS reserved blocks at start of partition.

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .PreAlloc);                 // DOS reserved blocks at end of partition

            await blockStream.WriteLittleEndianUInt32(partitionBlock.Interleave); // Not used, usually 0

            await blockStream.WriteLittleEndianUInt32(partitionBlock.LowCyl);     // First cylinder of the partition

            await blockStream.WriteLittleEndianUInt32(partitionBlock.HighCyl);    // Last cylinder of the partition

            await blockStream.WriteLittleEndianUInt32(partitionBlock.NumBuffer);  // Initial # DOS of buffers.

            await blockStream.WriteLittleEndianUInt32(partitionBlock.BufMemType); // Type of mem to allocate for buffers

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .MaxTransfer);                // Max number of bytes to transfer at a time

            await blockStream.WriteLittleEndianUInt32(partitionBlock.Mask);         // Address Mask to block out certain memory

            await blockStream.WriteLittleEndianUInt32(partitionBlock.BootPriority); // Boot priority for autoboot

            await blockStream.WriteBytes(partitionBlock.DosType);                   // # Dostype of the file system

            await blockStream.WriteLittleEndianUInt32(partitionBlock.Baud);         // Baud rate for serial handler

            await blockStream.WriteLittleEndianUInt32(partitionBlock.Control);      // Control word for handler/filesystem

            await blockStream.WriteLittleEndianUInt32(partitionBlock
                                                      .BootBlocks); // Number of blocks containing boot code

            // skip reserved
            blockStream.Seek(4 * 12, SeekOrigin.Current);

            // calculate and update checksum
            var blockBytes = blockStream.ToArray();

            partitionBlock.Checksum = await BlockHelper.UpdateChecksum(blockBytes, 8);

            partitionBlock.BlockBytes = blockBytes;

            return(blockBytes);
        }
        public static async Task <FileSystemHeaderBlock> Parse(byte[] blockBytes)
        {
            var blockStream = new MemoryStream(blockBytes);

            var identifier = await blockStream.ReadAsciiString(); // Identifier 32 bit word : 'RDSK'

            if (!identifier.Equals(BlockIdentifiers.FileSystemHeaderBlock))
            {
                throw new IOException("Invalid file system header block identifier");
            }

            await blockStream.ReadUInt32();                              // Size of the structure for checksums

            var checksum = await blockStream.ReadInt32();                // Checksum of the structure

            var hostId = await blockStream.ReadUInt32();                 // SCSI Target ID of host, not really used

            var nextFileSysHeaderBlock = await blockStream.ReadUInt32(); // Block number of the next FileSysHeaderBlock

            var flags = await blockStream.ReadUInt32();                  // Flags

            // read reserved, unused word
            for (var i = 0; i < 2; i++)
            {
                await blockStream.ReadBytes(4);
            }

            var dosType =
                await blockStream
                .ReadBytes(4);                            // # Dostype of the file system, file system description: match this with partition environment's DE_DOSTYPE entry

            var version = await blockStream.ReadUInt32(); // filesystem version 0x0027001b == 39.27

            var patchFlags = await blockStream.ReadUInt32();

            var type = await blockStream.ReadUInt32();

            var task = await blockStream.ReadUInt32();

            var fileSysLock = await blockStream.ReadUInt32();

            var handler = await blockStream.ReadUInt32();

            var stackSize = await blockStream.ReadUInt32();

            var priority = await blockStream.ReadInt32();

            var startup = await blockStream.ReadInt32();

            var segListBlocks = await blockStream.ReadInt32(); // first of linked list of LoadSegBlocks

            var globalVec = await blockStream.ReadInt32();

            blockStream.Seek(172, SeekOrigin.Begin);
            var fileSystemName = await blockStream.ReadNullTerminatedString();

            var calculatedChecksum = await BlockHelper.CalculateChecksum(blockBytes, 8);

            if (checksum != calculatedChecksum)
            {
                throw new IOException("Invalid file system header block checksum");
            }

            return(new FileSystemHeaderBlock
            {
                BlockBytes = blockBytes,
                Checksum = checksum,
                HostId = hostId,
                NextFileSysHeaderBlock = nextFileSysHeaderBlock,
                Flags = flags,
                DosType = dosType,
                Version = version,
                PatchFlags = patchFlags,
                Type = type,
                Task = task,
                Lock = fileSysLock,
                Handler = handler,
                StackSize = stackSize,
                Priority = priority,
                Startup = startup,
                SegListBlocks = segListBlocks,
                GlobalVec = globalVec,
                FileSystemName = fileSystemName
            });
        }
Example #7
0
        public static async Task <byte[]> BuildBlock(RigidDiskBlock rigidDiskBlock)
        {
            var blockStream =
                new MemoryStream(rigidDiskBlock.BlockBytes == null || rigidDiskBlock.BlockBytes.Length == 0
                    ? new byte[BlockSize.RigidDiskBlock * 4]
                    : rigidDiskBlock.BlockBytes);

            await blockStream.WriteAsciiString(BlockIdentifiers.RigidDiskBlock);

            await blockStream.WriteLittleEndianUInt32(BlockSize.RigidDiskBlock); // size

            // skip checksum, calculated when block is built
            blockStream.Seek(4, SeekOrigin.Current);

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.HostId);         // SCSI Target ID of host, not really used

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.BlockSize);      // Size of disk blocks

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Flags);          // RDB Flags

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.BadBlockList);   // Bad block list

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.PartitionList);  // Partition list

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.FileSysHdrList); // File system header list

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.DriveInitCode);  // Drive specific init code

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.BootBlockList);  // Amiga OS 4 Boot Blocks

            // read reserved, unused word, need to be set to $ffffffff
            var reservedBytes = new byte[] { 255, 255, 255, 255 };

            for (var i = 0; i < 5; i++)
            {
                await blockStream.WriteBytes(reservedBytes);
            }

            // physical drive characteristics
            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Cylinders);   // Number of the cylinders of the drive

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Sectors);     // Number of sectors of the drive

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Heads);       // Number of heads of the drive

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Interleave);  // Interleave

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.ParkingZone); // Head parking cylinder

            // read reserved, unused word, need to be set to $ffffffff
            reservedBytes = new byte[4];
            for (var i = 0; i < 3; i++)
            {
                await blockStream.WriteBytes(reservedBytes);
            }

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock
                                                      .WritePreComp); // Starting cylinder of write pre-compensation

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock
                                                      .ReducedWrite);           // Starting cylinder of reduced write current

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.StepRate); // Step rate of the drive

            // read reserved, unused word, need to be set to $ffffffff
            for (var i = 0; i < 5; i++)
            {
                await blockStream.WriteBytes(reservedBytes);
            }

            // logical drive characteristics
            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock
                                                      .RdbBlockLo); // low block of range reserved for hardblocks

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock
                                                      .RdbBlockHi); // high block of range for these hardblocks

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock
                                                      .LoCylinder); // low cylinder of partitionable disk area

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock
                                                      .HiCylinder); // high cylinder of partitionable data area

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock
                                                      .CylBlocks);                     // number of blocks available per cylinder

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.AutoParkSeconds); // zero for no auto park

            await blockStream.WriteLittleEndianUInt32(rigidDiskBlock
                                                      .HighRsdkBlock); // highest block used by RDSK (not including replacement bad blocks)

            await blockStream.WriteBytes(reservedBytes);               // read reserved, unused word

            // drive identification
            await blockStream.WriteString(rigidDiskBlock.DiskVendor, 8, 32);

            await blockStream.WriteString(rigidDiskBlock.DiskProduct, 16, 32);

            await blockStream.WriteString(rigidDiskBlock.DiskRevision, 4, 32);

            // write controller vendor
            if (!string.IsNullOrWhiteSpace(rigidDiskBlock.ControllerVendor))
            {
                await blockStream.WriteString(rigidDiskBlock.ControllerVendor, 8, 32);
            }
            else
            {
                await blockStream.WriteBytes(new byte[8]);
            }

            // write controller product
            if (!string.IsNullOrWhiteSpace(rigidDiskBlock.ControllerProduct))
            {
                await blockStream.WriteString(rigidDiskBlock.ControllerProduct, 16, 32);
            }
            else
            {
                await blockStream.WriteBytes(new byte[16]);
            }

            // write controller revision
            if (!string.IsNullOrWhiteSpace(rigidDiskBlock.ControllerRevision))
            {
                await blockStream.WriteString(rigidDiskBlock.ControllerRevision, 4, 32);
            }
            else
            {
                await blockStream.WriteBytes(new byte[4]);
            }

            await blockStream.WriteBytes(reservedBytes); // read reserved, unused word

            // calculate and update checksum
            var blockBytes = blockStream.ToArray();

            rigidDiskBlock.Checksum = await BlockHelper.UpdateChecksum(blockBytes, 8);

            rigidDiskBlock.BlockBytes = blockBytes;

            return(blockBytes);
        }
Example #8
0
        public static async Task WriteBlock(RigidDiskBlock rigidDiskBlock, Stream stream)
        {
            // update block pointers to maintain rigid disk block structure
            BlockHelper.UpdateBlockPointers(rigidDiskBlock);

            // seek rigid disk block offset
            stream.Seek(rigidDiskBlock.RdbBlockLo * 512, SeekOrigin.Begin);

            var rigidDiskBlockBytes = await BuildBlock(rigidDiskBlock);

            await stream.WriteBytes(rigidDiskBlockBytes);

            if (rigidDiskBlock.PartitionList != BlockIdentifiers.EndOfBlock)
            {
                // seek partition block index offset
                stream.Seek(rigidDiskBlock.PartitionList * 512, SeekOrigin.Begin);

                foreach (var partitionBlock in rigidDiskBlock.PartitionBlocks)
                {
                    var partitionBlockBytes = await PartitionBlockWriter.BuildBlock(partitionBlock);

                    await stream.WriteBytes(partitionBlockBytes);

                    if (partitionBlock.NextPartitionBlock == BlockIdentifiers.EndOfBlock)
                    {
                        break;
                    }

                    // seek next partition block index offset
                    stream.Seek(partitionBlock.NextPartitionBlock * 512, SeekOrigin.Begin);
                }
            }

            if (rigidDiskBlock.FileSysHdrList != BlockIdentifiers.EndOfBlock)
            {
                // seek file system header block index offset
                stream.Seek(rigidDiskBlock.FileSysHdrList * 512, SeekOrigin.Begin);

                foreach (var fileSystemHeaderBlock in rigidDiskBlock.FileSystemHeaderBlocks)
                {
                    var fileSystemHeaderBytes = await FileSystemHeaderBlockWriter.BuildBlock(fileSystemHeaderBlock);

                    await stream.WriteBytes(fileSystemHeaderBytes);

                    // seek load seg block index offset
                    stream.Seek(fileSystemHeaderBlock.SegListBlocks * 512, SeekOrigin.Begin);

                    foreach (var loadSegBlock in fileSystemHeaderBlock.LoadSegBlocks)
                    {
                        var loadSegBlockBytes = await LoadSegBlockWriter.BuildBlock(loadSegBlock);

                        await stream.WriteBytes(loadSegBlockBytes);

                        if (loadSegBlock.NextLoadSegBlock == -1)
                        {
                            break;
                        }

                        // seek next load seg block index offset
                        stream.Seek(loadSegBlock.NextLoadSegBlock * 512, SeekOrigin.Begin);
                    }

                    if (fileSystemHeaderBlock.NextFileSysHeaderBlock == BlockIdentifiers.EndOfBlock)
                    {
                        break;
                    }

                    // seek next file system header block index offset
                    stream.Seek(fileSystemHeaderBlock.NextFileSysHeaderBlock * 512, SeekOrigin.Begin);
                }
            }

            if (rigidDiskBlock.BadBlockList != BlockIdentifiers.EndOfBlock)
            {
                // seek bad block index offset
                stream.Seek(rigidDiskBlock.BadBlockList * 512, SeekOrigin.Begin);

                foreach (var badBlock in rigidDiskBlock.BadBlocks)
                {
                    var badBlockBytes = await BadBlockWriter.BuildBlock(badBlock);

                    await stream.WriteBytes(badBlockBytes);

                    if (badBlock.NextBadBlock == BlockIdentifiers.EndOfBlock)
                    {
                        break;
                    }

                    // seek next bad block index offset
                    stream.Seek(badBlock.NextBadBlock * 512, SeekOrigin.Begin);
                }
            }
        }
Example #9
0
        public static async Task <RigidDiskBlock> Parse(byte[] blockBytes)
        {
            var blockStream = new MemoryStream(blockBytes);

            var magic = await blockStream.ReadAsciiString(); // Identifier 32 bit word : 'RDSK'

            if (!magic.Equals(BlockIdentifiers.RigidDiskBlock))
            {
                return(null);
            }

            await blockStream.ReadUInt32();                      // Size of the structure for checksums

            var checksum = await blockStream.ReadInt32();        // Checksum of the structure

            var hostId = await blockStream.ReadUInt32();         // SCSI Target ID of host, not really used

            var blockSize = await blockStream.ReadUInt32();      // Size of disk blocks

            var flags = await blockStream.ReadUInt32();          // RDB Flags

            var badBlockList = await blockStream.ReadUInt32();   // Bad block list

            var partitionList = await blockStream.ReadUInt32();  // Partition list

            var fileSysHdrList = await blockStream.ReadUInt32(); // File system header list

            var driveInitCode = await blockStream.ReadUInt32();  // Drive specific init code

            var bootBlockList = await blockStream.ReadUInt32();  // Amiga OS 4 Boot Blocks

            // skip reserved
            blockStream.Seek(4 * 5, SeekOrigin.Current);

            // physical drive characteristics
            var cylinders = await blockStream.ReadUInt32();   // Number of the cylinders of the drive

            var sectors = await blockStream.ReadUInt32();     // Number of sectors of the drive

            var heads = await blockStream.ReadUInt32();       // Number of heads of the drive

            var interleave = await blockStream.ReadUInt32();  // Interleave

            var parkingZone = await blockStream.ReadUInt32(); // Head parking cylinder

            // skip reserved
            blockStream.Seek(4 * 3, SeekOrigin.Current);

            var writePreComp = await blockStream.ReadUInt32(); // Starting cylinder of write pre-compensation

            var reducedWrite = await blockStream.ReadUInt32(); // Starting cylinder of reduced write current

            var stepRate = await blockStream.ReadUInt32();     // Step rate of the drive

            // skip reserved
            blockStream.Seek(4 * 5, SeekOrigin.Current);

            // logical drive characteristics
            var rdbBlockLo = await blockStream.ReadUInt32();      // low block of range reserved for hardblocks

            var rdbBlockHi = await blockStream.ReadUInt32();      // high block of range for these hardblocks

            var loCylinder = await blockStream.ReadUInt32();      // low cylinder of partitionable disk area

            var hiCylinder = await blockStream.ReadUInt32();      // high cylinder of partitionable data area

            var cylBlocks = await blockStream.ReadUInt32();       // number of blocks available per cylinder

            var autoParkSeconds = await blockStream.ReadUInt32(); // zero for no auto park

            var highRsdkBlock =
                await blockStream.ReadUInt32(); // highest block used by RDSK (not including replacement bad blocks)

            // skip reserved
            blockStream.Seek(4, SeekOrigin.Current);

            // drive identification
            var diskVendor         = (await blockStream.ReadBytes(8)).ReadNullTerminatedString().Trim();
            var diskProduct        = (await blockStream.ReadBytes(16)).ReadNullTerminatedString().Trim();
            var diskRevision       = (await blockStream.ReadBytes(4)).ReadNullTerminatedString().Trim();
            var controllerVendor   = (await blockStream.ReadBytes(8)).ReadNullTerminatedString().Trim();
            var controllerProduct  = (await blockStream.ReadBytes(16)).ReadNullTerminatedString().Trim();
            var controllerRevision = (await blockStream.ReadBytes(4)).ReadNullTerminatedString().Trim();

            // skip reserved
            blockStream.Seek(4, SeekOrigin.Current);

            // calculate size of disk in bytes
            var diskSize = (long)cylinders * heads * sectors * blockSize;

            var calculatedChecksum = await BlockHelper.CalculateChecksum(blockBytes, 8);

            if (checksum != calculatedChecksum)
            {
                throw new Exception("Invalid rigid disk block checksum");
            }

            return(new RigidDiskBlock
            {
                BlockBytes = blockBytes,
                Checksum = checksum,
                HostId = hostId,
                BlockSize = blockSize,
                Flags = flags,
                BadBlockList = badBlockList,
                PartitionList = partitionList,
                FileSysHdrList = fileSysHdrList,
                DriveInitCode = driveInitCode,
                BootBlockList = bootBlockList,
                Cylinders = cylinders,
                Sectors = sectors,
                Heads = heads,
                Interleave = interleave,
                ParkingZone = parkingZone,
                WritePreComp = writePreComp,
                ReducedWrite = reducedWrite,
                StepRate = stepRate,
                RdbBlockLo = rdbBlockLo,
                RdbBlockHi = rdbBlockHi,
                LoCylinder = loCylinder,
                HiCylinder = hiCylinder,
                CylBlocks = cylBlocks,
                AutoParkSeconds = autoParkSeconds,
                HighRsdkBlock = highRsdkBlock,
                DiskVendor = diskVendor,
                DiskProduct = diskProduct,
                DiskRevision = diskRevision,
                ControllerVendor = controllerVendor,
                ControllerProduct = controllerProduct,
                ControllerRevision = controllerRevision,
                DiskSize = diskSize
            });
        }
        public static async Task <PartitionBlock> Parse(RigidDiskBlock rigidDiskBlock, byte[] blockBytes)
        {
            var blockStream = new MemoryStream(blockBytes);

            var magic = await blockStream.ReadAsciiString(); // Identifier 32 bit word : 'PART'

            if (!magic.Equals(BlockIdentifiers.PartitionBlock))
            {
                return(null);
            }

            await blockStream.ReadUInt32();                          // Size of the structure for checksums

            var checksum = await blockStream.ReadInt32();            // Checksum of the structure

            var hostId = await blockStream.ReadUInt32();             // SCSI Target ID of host, not really used

            var nextPartitionBlock = await blockStream.ReadUInt32(); // Block number of the next PartitionBlock

            var flags = await blockStream.ReadUInt32();              // Part Flags (NOMOUNT and BOOTABLE)

            // skip reserved
            blockStream.Seek(4 * 2, SeekOrigin.Current);

            var devFlags = await blockStream.ReadUInt32(); // Preferred flags for OpenDevice

            var driveNameLength =
                (await blockStream.ReadBytes(1)).FirstOrDefault();         //  Preferred DOS device name: BSTR form
            var driveName = await blockStream.ReadString(driveNameLength); // # Preferred DOS device name: BSTR form

            if (driveNameLength < 31)
            {
                await blockStream.ReadBytes(31 - driveNameLength);
            }

            // skip reserved
            blockStream.Seek(4 * 15, SeekOrigin.Current);

            var sizeOfVector = await blockStream.ReadUInt32();   // Size of Environment vector

            var sizeBlock = await blockStream.ReadUInt32();      // Size of the blocks in 32 bit words, usually 128

            var secOrg = await blockStream.ReadUInt32();         // Not used; must be 0

            var surfaces = await blockStream.ReadUInt32();       // Number of heads (surfaces)

            var sectors = await blockStream.ReadUInt32();        // Disk sectors per block, used with SizeBlock, usually 1

            var blocksPerTrack = await blockStream.ReadUInt32(); // Blocks per track. drive specific

            var reserved = await blockStream.ReadUInt32();       // DOS reserved blocks at start of partition.

            var preAlloc = await blockStream.ReadUInt32();       // DOS reserved blocks at end of partition

            var interleave = await blockStream.ReadUInt32();     // Not used, usually 0

            var lowCyl = await blockStream.ReadUInt32();         // First cylinder of the partition

            var highCyl = await blockStream.ReadUInt32();        // Last cylinder of the partition

            var numBuffer = await blockStream.ReadUInt32();      // Initial # DOS of buffers.

            var bufMemType = await blockStream.ReadUInt32();     // Type of mem to allocate for buffers

            var maxTransfer = await blockStream.ReadUInt32();    // Max number of bytes to transfer at a time

            var mask = await blockStream.ReadUInt32();           // Address Mask to block out certain memory

            var bootPriority = await blockStream.ReadUInt32();   // Boot priority for autoboot

            var dosType = await blockStream.ReadBytes(4);        // # Dostype of the file system

            var baud = await blockStream.ReadUInt32();           // Baud rate for serial handler

            var control = await blockStream.ReadUInt32();        // Control word for handler/filesystem

            var bootBlocks = await blockStream.ReadUInt32();     // Number of blocks containing boot code

            // skip reserved
            blockStream.Seek(4 * 12, SeekOrigin.Current);

            // calculate size of partition in bytes
            var partitionSize = (long)(highCyl - lowCyl + 1) * surfaces * blocksPerTrack * rigidDiskBlock.BlockSize;

            var calculatedChecksum = await BlockHelper.CalculateChecksum(blockBytes, 8);

            if (checksum != calculatedChecksum)
            {
                throw new Exception("Invalid partition block checksum");
            }

            var fileSystemBlockSize = sizeBlock * 4;

            return(new PartitionBlock
            {
                BlockBytes = blockBytes,
                Checksum = checksum,
                HostId = hostId,
                NextPartitionBlock = nextPartitionBlock,
                Flags = flags,
                DevFlags = devFlags,
                DriveName = driveName,
                SizeOfVector = sizeOfVector,
                SizeBlock = sizeBlock,
                SecOrg = secOrg,
                Surfaces = surfaces,
                Sectors = sectors,
                BlocksPerTrack = blocksPerTrack,
                Reserved = reserved,
                PreAlloc = preAlloc,
                Interleave = interleave,
                LowCyl = lowCyl,
                HighCyl = highCyl,
                NumBuffer = numBuffer,
                BufMemType = bufMemType,
                MaxTransfer = maxTransfer,
                Mask = mask,
                BootPriority = bootPriority,
                DosType = dosType,
                Baud = baud,
                Control = control,
                BootBlocks = bootBlocks,
                PartitionSize = partitionSize,
                FileSystemBlockSize = fileSystemBlockSize,
            });
        }
        public static async Task <byte[]> BuildBlock(FileSystemHeaderBlock fileSystemHeaderBlock)
        {
            var blockStream =
                new MemoryStream(
                    fileSystemHeaderBlock.BlockBytes == null || fileSystemHeaderBlock.BlockBytes.Length == 0
                        ? new byte[BlockSize.FileSystemHeaderBlock * 4]
                        : fileSystemHeaderBlock.BlockBytes);

            await blockStream.WriteAsciiString(BlockIdentifiers.FileSystemHeaderBlock);

            await blockStream.WriteLittleEndianUInt32(BlockSize.FileSystemHeaderBlock); // size

            // skip checksum, calculated when block is built
            blockStream.Seek(4, SeekOrigin.Current);

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock
                                                      .HostId); // SCSI Target ID of host, not really used

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock
                                                      .NextFileSysHeaderBlock);     // Block number of the next FileSysHeaderBlock

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock.Flags); // Flags

            // skip reserved
            blockStream.Seek(4 * 2, SeekOrigin.Current);

            await blockStream
            .WriteBytes(fileSystemHeaderBlock
                        .DosType); // # Dostype of the file system, file system description: match this with partition environment's DE_DOSTYPE entry

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock
                                                      .Version); // filesystem version 0x0027001b == 39.27

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock.PatchFlags);

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock.Type);

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock.Task);

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock.Lock);

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock.Handler);

            await blockStream.WriteLittleEndianUInt32(fileSystemHeaderBlock.StackSize);

            await blockStream.WriteLittleEndianInt32(fileSystemHeaderBlock.Priority);

            await blockStream.WriteLittleEndianInt32(fileSystemHeaderBlock.Startup);

            await blockStream.WriteLittleEndianInt32(fileSystemHeaderBlock
                                                     .SegListBlocks); // first of linked list of LoadSegBlocks

            await blockStream.WriteLittleEndianInt32(fileSystemHeaderBlock.GlobalVec);

            // skip reserved
            blockStream.Seek((23 + 21) * 4, SeekOrigin.Current);

            // calculate and update checksum
            var blockBytes = blockStream.ToArray();

            fileSystemHeaderBlock.Checksum = await BlockHelper.UpdateChecksum(blockBytes, 8);

            fileSystemHeaderBlock.BlockBytes = blockBytes;

            return(blockBytes);
        }