Example #1
0
 public DiabloPack(DiabloDiskType type)
 {
     _diskType = type;
     _packName = null;
     _geometry = new DiskGeometry(type == DiabloDiskType.Diablo31 ? 203 : 406, 2, 12);
     _sectors  = new DiabloDiskSector[_geometry.Cylinders, _geometry.Tracks, _geometry.Sectors];
 }
Example #2
0
        public void Save(Stream imageStream, bool reverseByteOrder)
        {
            byte[] emptyHeader = new byte[4];        // 2 words
            byte[] emptyLabel  = new byte[16];       // 8 words
            byte[] emptyData   = new byte[512];      // 256 words

            for (int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++)
            {
                for (int track = 0; track < _geometry.Tracks; track++)
                {
                    for (int sector = 0; sector < _geometry.Sectors; sector++)
                    {
                        //
                        // Bitsavers images have an extra word in the header for some reason.
                        // We will follow this 'standard' when writing out.
                        // TODO: should support different formats ("correct" raw, Alto CopyDisk format, etc.)
                        //
                        byte[] dummy = new byte[2];
                        imageStream.Write(dummy, 0, 2);

                        DiabloDiskSector s = GetSector(cylinder, track, sector);

                        imageStream.Write(reverseByteOrder ? SwapBytes(s.Header) : s.Header, 0, s.Header.Length);
                        imageStream.Write(reverseByteOrder ? SwapBytes(s.Label) : s.Label, 0, s.Label.Length);
                        imageStream.Write(reverseByteOrder ? SwapBytes(s.Data) : s.Data, 0, s.Data.Length);
                    }
                }
            }
        }
Example #3
0
        public DiabloDiskSector GetSector(int cylinder, int track, int sector)
        {
            DiabloDiskSector s = _sectors[cylinder, track, sector];

            // For invalid / empty sectors, return an Empty sector.
            if (s == null)
            {
                s = DiabloDiskSector.Empty;
            }

            return(s);
        }
Example #4
0
        public void SetSector(int address, DiabloDiskSector newSector)
        {
            if (address < 0 || address >= MaxAddress)
            {
                throw new InvalidOperationException("Disk address is out of range.");
            }

            int sector   = address % _geometry.Sectors;
            int track    = (address / _geometry.Sectors) % _geometry.Tracks;
            int cylinder = (address / (_geometry.Sectors * _geometry.Tracks));

            SetSector(cylinder, track, sector, newSector);
        }
Example #5
0
        public void Load(Stream imageStream, string path, bool reverseByteOrder)
        {
            _packName = path;
            for (int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++)
            {
                for (int track = 0; track < _geometry.Tracks; track++)
                {
                    for (int sector = 0; sector < _geometry.Sectors; sector++)
                    {
                        byte[] header = new byte[4];        // 2 words
                        byte[] label  = new byte[16];       // 8 words
                        byte[] data   = new byte[512];      // 256 words

                        //
                        // Bitsavers images have an extra word in the header for some reason.
                        // ignore it.
                        // TODO: should support different formats ("correct" raw, Alto CopyDisk format, etc.)
                        //
                        imageStream.Seek(2, SeekOrigin.Current);

                        if (imageStream.Read(header, 0, header.Length) != header.Length)
                        {
                            throw new InvalidOperationException("Short read while reading sector header.");
                        }

                        if (imageStream.Read(label, 0, label.Length) != label.Length)
                        {
                            throw new InvalidOperationException("Short read while reading sector label.");
                        }

                        if (imageStream.Read(data, 0, data.Length) != data.Length)
                        {
                            throw new InvalidOperationException("Short read while reading sector data.");
                        }

                        _sectors[cylinder, track, sector] =
                            new DiabloDiskSector(
                                reverseByteOrder ? SwapBytes(header) : header,
                                reverseByteOrder ? SwapBytes(label) : label,
                                reverseByteOrder ? SwapBytes(data) : data);
                    }
                }
            }

            if (imageStream.Position != imageStream.Length)
            {
                throw new InvalidOperationException("Extra data at end of image file.");
            }
        }
Example #6
0
        private void CopyDiskWorkerThread()
        {
            // TODO: enforce state (i.e. reject out-of-order block types.)
            while (_running)
            {
                // Retrieve length of this block (in bytes):
                int length = Channel.ReadUShort() * 2;

                // Sanity check that length is a reasonable value.
                if (length > 2048)
                {
                    Channel.SendAbort("Block length is invalid.");
                    throw new InvalidOperationException(String.Format("Insane block length ({0})", length));
                }

                // Retrieve type
                CopyDiskBlock blockType = (CopyDiskBlock)Channel.ReadUShort();

                // Read rest of block starting at offset 4 (so deserialization works)
                byte[] data = new byte[length];
                Channel.Read(ref data, data.Length - 4, 4);

                Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Copydisk block type is {0}", blockType);

                switch (blockType)
                {
                case CopyDiskBlock.Version:
                {
                    VersionYesNoBlock vbIn = (VersionYesNoBlock)Serializer.Deserialize(data, typeof(VersionYesNoBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Copydisk client is version {0}, '{1}'", vbIn.Code, vbIn.Herald.ToString());

                    // Send the response:
                    VersionYesNoBlock vbOut = new VersionYesNoBlock(CopyDiskBlock.Version, vbIn.Code, "LCM+L IFS CopyDisk of 26-Jan-2016");
                    Channel.Send(Serializer.Serialize(vbOut));
                }
                break;

                case CopyDiskBlock.Login:
                {
                    LoginBlock login = (LoginBlock)Serializer.Deserialize(data, typeof(LoginBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Login is for user '{0}', password '{1}', connection '{2}', connection password '{3}'.",
                              login.UserName,
                              login.UserPassword,
                              login.ConnName,
                              login.ConnPassword);

                    _userToken = AuthenticateUser(login.UserName.ToString(), login.UserPassword.ToString());

                    if (_userToken != null)
                    {
                        //
                        // Send a "Yes" response back.
                        //
                        VersionYesNoBlock yes = new VersionYesNoBlock(CopyDiskBlock.Yes, 0, "Come on in, the water's fine.");
                        Channel.Send(Serializer.Serialize(yes));
                    }
                    else
                    {
                        //
                        // Send a "No" response back indicating the login failure.
                        //
                        VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.IllegalOrIncorrectPassword, "Invalid username or password.");
                        Channel.Send(Serializer.Serialize(no), true);
                    }
                }
                break;

                case CopyDiskBlock.SendDiskParamsR:
                {
                    SendDiskParamsBlock p = (SendDiskParamsBlock)Serializer.Deserialize(data, typeof(SendDiskParamsBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Requested unit for reading is '{0}'", p.UnitName);

                    //
                    // See if the pack image exists, return HereAreDiskParams if so, or No if not.
                    // If the image exists, save the path for future use.
                    //
                    // Some sanity (and security) checks:
                    // Name must be a filename only, no paths of any kind allowed.
                    // Oh, and the file must exist in the directory holding disk packs.
                    //
                    string diskPath = GetPathForDiskImage(p.UnitName.ToString());
                    if (!String.IsNullOrEmpty(Path.GetDirectoryName(p.UnitName.ToString())) ||
                        !File.Exists(diskPath))
                    {
                        // Invalid name, return No reponse.
                        VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Invalid unit name.");
                        Channel.Send(Serializer.Serialize(no));
                    }
                    else
                    {
                        //
                        // Attempt to open the image file and read it into memory.
                        //
                        try
                        {
                            using (FileStream packStream = new FileStream(diskPath, FileMode.Open, FileAccess.Read))
                            {
                                // TODO: determine pack type rather than assuming Diablo 31
                                _pack = new DiabloPack(DiabloDiskType.Diablo31);
                                _pack.Load(packStream, diskPath, true /* reverse byte order */);
                            }

                            // Send a "HereAreDiskParams" response indicating success.
                            //
                            HereAreDiskParamsBFSBlock diskParams = new HereAreDiskParamsBFSBlock(_pack.Geometry);
                            Channel.Send(Serializer.Serialize(diskParams));
                        }
                        catch
                        {
                            // If we fail for any reason, return a "No" response.
                            // TODO: can we be more helpful here?
                            VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Image could not be opened.");
                            Channel.Send(Serializer.Serialize(no));
                        }
                    }
                }
                break;

                case CopyDiskBlock.SendDiskParamsW:
                {
                    SendDiskParamsBlock p = (SendDiskParamsBlock)Serializer.Deserialize(data, typeof(SendDiskParamsBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Requested unit for writing is '{0}'", p.UnitName);

                    //
                    // Some sanity (and security) checks:
                    // Name must be a filename only, no paths of any kind allowed.
                    // Oh, and the file must not exist in the directory holding disk packs.
                    //
                    string diskPath = GetPathForDiskImage(p.UnitName.ToString());
                    if (!String.IsNullOrEmpty(Path.GetDirectoryName(p.UnitName.ToString())) ||
                        File.Exists(diskPath))
                    {
                        // Invalid name, return No reponse.
                        VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Invalid unit name or image already exists.");
                        Channel.Send(Serializer.Serialize(no));
                    }
                    else
                    {
                        //
                        // Create a new in-memory disk image.  We will write it out to disk when the transfer is completed.
                        //
                        // TODO: determine pack type based on disk params rather than assuming Diablo 31
                        _pack          = new DiabloPack(DiabloDiskType.Diablo31);
                        _pack.PackName = diskPath;


                        // Send a "HereAreDiskParams" response indicating success.
                        //
                        HereAreDiskParamsBFSBlock diskParams = new HereAreDiskParamsBFSBlock(_pack.Geometry);
                        Channel.Send(Serializer.Serialize(diskParams));
                    }
                }
                break;

                case CopyDiskBlock.HereAreDiskParams:
                {
                    HereAreDiskParamsBFSBlock diskParams = (HereAreDiskParamsBFSBlock)Serializer.Deserialize(data, typeof(HereAreDiskParamsBFSBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Disk params are: Type {0}, C/H/S {1}/{2}/{3}",
                              diskParams.DiskType,
                              diskParams.Cylinders,
                              diskParams.Heads,
                              diskParams.Sectors);
                }
                break;

                case CopyDiskBlock.RetrieveDisk:
                case CopyDiskBlock.StoreDisk:
                {
                    TransferParametersBlock transferParameters = (TransferParametersBlock)Serializer.Deserialize(data, typeof(TransferParametersBlock));

                    _startAddress = _pack.DiskAddressToVirtualAddress(transferParameters.StartAddress);
                    _endAddress   = _pack.DiskAddressToVirtualAddress(transferParameters.EndAddress);

                    // Validate that the user is allowed to store.
                    if (blockType == CopyDiskBlock.StoreDisk)
                    {
                        if (_userToken.Privileges != IFSPrivileges.ReadWrite)
                        {
                            VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitWriteProtected, "You do not have permission to store disk images.");
                            Channel.Send(Serializer.Serialize(no));
                            break;
                        }
                    }

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Transfer is from block {0} to block {1}", transferParameters.StartAddress, transferParameters.EndAddress);

                    // Validate start/end parameters
                    if (_endAddress <= _startAddress ||
                        _startAddress > _pack.MaxAddress ||
                        _endAddress > _pack.MaxAddress)
                    {
                        VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnknownCommand, "Transfer parameters are invalid.");
                        Channel.Send(Serializer.Serialize(no));
                    }
                    else
                    {
                        // We're OK.  Save the parameters and send a Yes response.
                        VersionYesNoBlock yes = new VersionYesNoBlock(CopyDiskBlock.Yes, 0, "You are cleared for launch.");
                        Channel.Send(Serializer.Serialize(yes));

                        //
                        // And send the requested range of pages if this is a Retrieve operation
                        // (otherwise wait for a HereIsDiskPage block from the client.)
                        //
                        if (blockType == CopyDiskBlock.RetrieveDisk)
                        {
                            for (int i = _startAddress; i < _endAddress + 1; i++)
                            {
                                DiabloDiskSector    sector = _pack.GetSector(i);
                                HereIsDiskPageBlock block  = new HereIsDiskPageBlock(sector.Header, sector.Label, sector.Data);
                                Channel.Send(Serializer.Serialize(block), false /* do not flush */);

                                if ((i % 100) == 0)
                                {
                                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Sent page {0}", i);
                                }
                            }

                            // Send "EndOfTransfer" block to finish the transfer.
                            EndOfTransferBlock endTransfer = new EndOfTransferBlock(0);
                            Channel.Send(Serializer.Serialize(endTransfer));

                            Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Send done.");
                        }
                        else
                        {
                            _currentAddress = _startAddress;
                        }
                    }
                }
                break;

                case CopyDiskBlock.HereIsDiskPage:
                {
                    if (_currentAddress > _endAddress)
                    {
                        Channel.SendAbort("Invalid address for page.");
                        _running = false;
                        break;
                    }

                    if ((_currentAddress % 100) == 0)
                    {
                        Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Received page {0}", _currentAddress);
                    }

                    if (data.Length < 512 + 16 + 4 + 4)
                    {
                        // Incomplete ("incorrigable") page, indicating an unreadable or empty sector, just copy
                        // the header/label data in and leave an empty data page.
                        HereIsDiskPageIncorrigableBlock diskPage = (HereIsDiskPageIncorrigableBlock)Serializer.Deserialize(data, typeof(HereIsDiskPageIncorrigableBlock));
                        DiabloDiskSector sector = new DiabloDiskSector(diskPage.Header, diskPage.Label, new byte[512]);
                        _pack.SetSector(_currentAddress, sector);

                        _currentAddress++;

                        Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Page is empty / incorrigable.");
                    }
                    else
                    {
                        HereIsDiskPageBlock diskPage = (HereIsDiskPageBlock)Serializer.Deserialize(data, typeof(HereIsDiskPageBlock));
                        DiabloDiskSector    sector   = new DiabloDiskSector(diskPage.Header, diskPage.Label, diskPage.Data);
                        _pack.SetSector(_currentAddress, sector);

                        _currentAddress++;
                    }
                }
                break;

                case CopyDiskBlock.EndOfTransfer:
                {
                    // No data in block.  If we aren't currently at the end of the transfer, the transfer has been aborted.
                    // Do nothing right now.
                    if (_currentAddress < _endAddress)
                    {
                        Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Transfer was aborted.");
                        _running = false;
                    }
                    else
                    {
                        try
                        {
                            // Commit disk image to disk.
                            using (FileStream packStream = new FileStream(_pack.PackName, FileMode.OpenOrCreate, FileAccess.Write))
                            {
                                Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Saving {0}...", _pack.PackName);
                                _pack.Save(packStream, true /* reverse byte order */);
                                Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Saved.");
                            }
                        }
                        catch (Exception e)
                        {
                            // Log error, reset state.
                            Log.Write(LogType.Error, LogComponent.CopyDisk, "Failed to save pack {0} - {1}", _pack.PackName, e.Message);
                        }
                    }
                }
                break;

                case CopyDiskBlock.SendErrors:
                {
                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Sending error summary...");
                    // No data in block.  Send list of errors we encountered.  (There should always be none since we're perfect and have no disk errors.)
                    HereAreErrorsBFSBlock errorBlock = new HereAreErrorsBFSBlock(0, 0);
                    Channel.Send(Serializer.Serialize(errorBlock));
                }
                break;

                default:
                    Log.Write(LogType.Warning, LogComponent.CopyDisk, "Unhandled CopyDisk block {0}", blockType);
                    break;
                }
            }

            if (OnExit != null)
            {
                OnExit(this);
            }
        }
Example #7
0
 public void SetSector(int cylinder, int track, int sector, DiabloDiskSector newSector)
 {
     _sectors[cylinder, track, sector] = newSector;
 }