Added support for U.C.S.D. Pascal filesystem, closes #31

This commit is contained in:
2016-07-31 20:56:53 +01:00
parent 8de5e47b3b
commit d4fa3b3e1b
13 changed files with 499 additions and 262 deletions

View File

@@ -1,3 +1,15 @@
2016-07-31 Natalia Portillo <claunia@claunia.com>
* Dir.cs:
* File.cs:
* Info.cs:
* Super.cs:
* Consts.cs:
* Structs.cs:
* UCSDPascal.cs:
* DiscImageChef.Filesystems.csproj: Added support for U.C.S.D.
Pascal filesystem, closes #31
2016-07-31 Natalia Portillo <claunia@claunia.com> 2016-07-31 Natalia Portillo <claunia@claunia.com>
* Super.cs: Reports correct filesystem version on Statfs(); * Super.cs: Reports correct filesystem version on Statfs();

View File

@@ -71,6 +71,13 @@
<Compile Include="LisaFS\File.cs" /> <Compile Include="LisaFS\File.cs" />
<Compile Include="LisaFS\Extent.cs" /> <Compile Include="LisaFS\Extent.cs" />
<Compile Include="LisaFS\Encoding.cs" /> <Compile Include="LisaFS\Encoding.cs" />
<Compile Include="UCSDPascal\UCSDPascal.cs" />
<Compile Include="UCSDPascal\Consts.cs" />
<Compile Include="UCSDPascal\File.cs" />
<Compile Include="UCSDPascal\Info.cs" />
<Compile Include="UCSDPascal\Structs.cs" />
<Compile Include="UCSDPascal\Super.cs" />
<Compile Include="UCSDPascal\Dir.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>
@@ -105,6 +112,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="LisaFS\" /> <Folder Include="LisaFS\" />
<Folder Include="UCSDPascal\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="..\LICENSE.LGPL"> <EmbeddedResource Include="..\LICENSE.LGPL">

View File

@@ -5,11 +5,11 @@
// Filename : Consts.cs // Filename : Consts.cs
// Author(s) : Natalia Portillo <claunia@claunia.com> // Author(s) : Natalia Portillo <claunia@claunia.com>
// //
// Component : Component // Component : U.C.S.D. Pascal filesystem plugin.
// //
// --[ Description ] ---------------------------------------------------------- // --[ Description ] ----------------------------------------------------------
// //
// Description // U.C.S.D. Pascal filesystem constants.
// //
// --[ License ] -------------------------------------------------------------- // --[ License ] --------------------------------------------------------------
// //
@@ -29,13 +29,32 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo // Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System;
namespace DiscImageChef.Filesystems.UCSDPascal namespace DiscImageChef.Filesystems.UCSDPascal
{ {
public class Consts // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure
public partial class PascalPlugin : Filesystem
{ {
public Consts() enum PascalFileKind : short
{ {
/// <summary>Disk volume entry</summary>
Volume = 0,
/// <summary>File containing bad blocks</summary>
Bad,
/// <summary>Code file, machine executable</summary>
Code,
/// <summary>Text file, human readable</summary>
Text,
/// <summary>Information file for debugger</summary>
Info,
/// <summary>Data file</summary>
Data,
/// <summary>Graphics vectors</summary>
Graf,
/// <summary>Graphics screen image</summary>
Foto,
/// <summary>Security, not used</summary>
Secure
} }
} }
} }

View File

@@ -5,11 +5,11 @@
// Filename : Dir.cs // Filename : Dir.cs
// Author(s) : Natalia Portillo <claunia@claunia.com> // Author(s) : Natalia Portillo <claunia@claunia.com>
// //
// Component : Component // Component : U.C.S.D. Pascal filesystem plugin.
// //
// --[ Description ] ---------------------------------------------------------- // --[ Description ] ----------------------------------------------------------
// //
// Description // Methods to handle show the U.C.S.D. Pascal catalog as a directory.
// //
// --[ License ] -------------------------------------------------------------- // --[ License ] --------------------------------------------------------------
// //
@@ -29,13 +29,35 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo // Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
using System.Collections.Generic;
namespace DiscImageChef.Filesystems.UCSDPascal namespace DiscImageChef.Filesystems.UCSDPascal
{ {
public class Dir // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure
public partial class PascalPlugin : Filesystem
{ {
public Dir() public override Errno ReadDir(string path, ref List<string> contents)
{ {
if(!mounted)
return Errno.AccessDenied;
if(!string.IsNullOrEmpty(path) && string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0)
return Errno.NotSupported;
contents = new List<string>();
foreach(PascalFileEntry ent in fileEntries)
contents.Add(StringHandlers.PascalToString(ent.filename));
if(debug)
{
contents.Add("$");
contents.Add("$Boot");
}
contents.Sort();
return Errno.NoError;
} }
} }
} }

View File

@@ -5,11 +5,11 @@
// Filename : File.cs // Filename : File.cs
// Author(s) : Natalia Portillo <claunia@claunia.com> // Author(s) : Natalia Portillo <claunia@claunia.com>
// //
// Component : Component // Component : U.C.S.D. Pascal filesystem plugin.
// //
// --[ Description ] ---------------------------------------------------------- // --[ Description ] ----------------------------------------------------------
// //
// Description // Methods to handle files.
// //
// --[ License ] -------------------------------------------------------------- // --[ License ] --------------------------------------------------------------
// //
@@ -29,13 +29,142 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo // Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
namespace DiscImageChef.Filesystems.UCSDPascal namespace DiscImageChef.Filesystems.UCSDPascal
{ {
public class File // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure
public partial class PascalPlugin : Filesystem
{ {
public File() public override Errno MapBlock(string path, long fileBlock, ref long deviceBlock)
{ {
if(!mounted)
return Errno.AccessDenied;
return Errno.NotImplemented;
}
public override Errno GetAttributes(string path, ref FileAttributes attributes)
{
if(!mounted)
return Errno.AccessDenied;
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
PascalFileEntry entry;
Errno error = GetFileEntry(path, out entry);
if(error == Errno.NoError)
{
attributes = new FileAttributes();
attributes = FileAttributes.File;
}
return error;
}
public override Errno Read(string path, long offset, long size, ref byte[] buf)
{
if(!mounted)
return Errno.AccessDenied;
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
byte[] file;
Errno error;
if(debug &&
(string.Compare(path, "$", StringComparison.InvariantCulture) == 0
|| string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0))
{
if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0)
file = catalogBlocks;
else
file = bootBlocks;
}
else
{
PascalFileEntry entry;
error = GetFileEntry(path, out entry);
if(error != Errno.NoError)
return error;
byte[] tmp = device.ReadSectors((ulong)entry.firstBlock, (uint)(entry.lastBlock - entry.firstBlock + 1));
file = new byte[(entry.lastBlock - entry.firstBlock) * device.GetSectorSize() + entry.lastBytes];
Array.Copy(tmp, 0, file, 0, file.Length);
}
if(offset >= file.Length || size == 0 || offset + size == file.Length)
{
buf = new byte[0];
return Errno.NoError;
}
if(offset + size > file.Length)
size = file.Length - offset;
buf = new byte[size];
Array.Copy(file, offset, buf, 0, size);
return Errno.NoError;
}
public override Errno Stat(string path, ref FileEntryInfo stat)
{
if(!mounted)
return Errno.AccessDenied;
if(!mounted)
return Errno.AccessDenied;
string[] pathElements = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if(pathElements.Length != 1)
return Errno.NotSupported;
PascalFileEntry entry;
Errno error = GetFileEntry(path, out entry);
if(error != Errno.NoError)
return error;
stat = new FileEntryInfo();
stat.Attributes = new FileAttributes();
stat.Attributes = FileAttributes.File;
stat.Blocks = entry.lastBlock - entry.firstBlock + 1;
stat.BlockSize = device.GetSectorSize();
stat.DeviceNo = 0;
stat.GID = 0;
stat.Inode = 0;
stat.LastWriteTimeUtc = DateHandlers.UCSDPascalToDateTime(entry.mtime);
stat.Length = (entry.lastBlock - entry.firstBlock) * device.GetSectorSize() + entry.lastBytes;
stat.Links = 1;
stat.Mode = 0x124;
stat.UID = 0;
return Errno.NoError;
}
Errno GetFileEntry(string path, out PascalFileEntry entry)
{
entry = new PascalFileEntry();
foreach(PascalFileEntry ent in fileEntries)
{
if(string.Compare(path, StringHandlers.PascalToString(ent.filename), StringComparison.InvariantCultureIgnoreCase) == 0)
{
entry = ent;
return Errno.NoError;
}
}
return Errno.NoSuchFile;
} }
} }
} }

View File

@@ -5,11 +5,11 @@
// Filename : Info.cs // Filename : Info.cs
// Author(s) : Natalia Portillo <claunia@claunia.com> // Author(s) : Natalia Portillo <claunia@claunia.com>
// //
// Component : Component // Component : U.C.S.D. Pascal filesystem plugin.
// //
// --[ Description ] ---------------------------------------------------------- // --[ Description ] ----------------------------------------------------------
// //
// Description // Identifies the U.C.S.D. Pascal filesystem and shows information.
// //
// --[ License ] -------------------------------------------------------------- // --[ License ] --------------------------------------------------------------
// //
@@ -29,13 +29,133 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo // Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
using System.Text;
namespace DiscImageChef.Filesystems.UCSDPascal namespace DiscImageChef.Filesystems.UCSDPascal
{ {
public class Info // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure
public partial class PascalPlugin : Filesystem
{ {
public Info() public override bool Identify(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd)
{ {
if(imagePlugin.GetSectors() < 3)
return false;
// Blocks 0 and 1 are boot code
byte[] volBlock = imagePlugin.ReadSector(2 + partitionStart);
PascalVolumeEntry volEntry = new PascalVolumeEntry();
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
volEntry.firstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00);
volEntry.lastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02);
volEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04);
volEntry.volumeName = new byte[8];
Array.Copy(volBlock, 0x06, volEntry.volumeName, 0, 8);
volEntry.blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E);
volEntry.files = BigEndianBitConverter.ToInt16(volBlock, 0x10);
volEntry.dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12);
volEntry.lastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14);
volEntry.tail = BigEndianBitConverter.ToInt32(volBlock, 0x16);
// First block is always 0 (even is it's sector 2)
if(volEntry.firstBlock != 0)
return false;
// Last volume record block must be after first block, and before end of device
if(volEntry.lastBlock <= volEntry.firstBlock || (ulong)volEntry.lastBlock > imagePlugin.GetSectors() - 2)
return false;
// Volume record entry type must be volume or secure
if(volEntry.entryType != PascalFileKind.Volume && volEntry.entryType != PascalFileKind.Secure)
return false;
// Volume name is max 7 characters
if(volEntry.volumeName[0] > 7)
return false;
// Volume blocks is equal to volume sectors
if(volEntry.blocks < 0 || (ulong)volEntry.blocks != imagePlugin.GetSectors())
return false;
// There can be not less than zero files
if(volEntry.files < 0)
return false;
return true;
}
public override void GetInformation(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd, out string information)
{
StringBuilder sbInformation = new StringBuilder();
information = "";
if(imagePlugin.GetSectors() < 3)
return;
// Blocks 0 and 1 are boot code
byte[] volBlock = imagePlugin.ReadSector(2 + partitionStart);
PascalVolumeEntry volEntry = new PascalVolumeEntry();
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
volEntry.firstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00);
volEntry.lastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02);
volEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04);
volEntry.volumeName = new byte[8];
Array.Copy(volBlock, 0x06, volEntry.volumeName, 0, 8);
volEntry.blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E);
volEntry.files = BigEndianBitConverter.ToInt16(volBlock, 0x10);
volEntry.dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12);
volEntry.lastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14);
volEntry.tail = BigEndianBitConverter.ToInt32(volBlock, 0x16);
// First block is always 0 (even is it's sector 2)
if(volEntry.firstBlock != 0)
return;
// Last volume record block must be after first block, and before end of device
if(volEntry.lastBlock <= volEntry.firstBlock || (ulong)volEntry.lastBlock > imagePlugin.GetSectors() - 2)
return;
// Volume record entry type must be volume or secure
if(volEntry.entryType != PascalFileKind.Volume && volEntry.entryType != PascalFileKind.Secure)
return;
// Volume name is max 7 characters
if(volEntry.volumeName[0] > 7)
return;
// Volume blocks is equal to volume sectors
if(volEntry.blocks < 0 || (ulong)volEntry.blocks != imagePlugin.GetSectors())
return;
// There can be not less than zero files
if(volEntry.files < 0)
return;
sbInformation.AppendFormat("Volume record spans from block {0} to block {1}", volEntry.firstBlock, volEntry.lastBlock).AppendLine();
sbInformation.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(volEntry.volumeName)).AppendLine();
sbInformation.AppendFormat("Volume has {0} blocks", volEntry.blocks).AppendLine();
sbInformation.AppendFormat("Volume has {0} files", volEntry.files).AppendLine();
sbInformation.AppendFormat("Volume last booted at {0}", DateHandlers.UCSDPascalToDateTime(volEntry.lastBoot)).AppendLine();
information = sbInformation.ToString();
xmlFSType = new Schemas.FileSystemType();
xmlFSType.Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(imagePlugin.ReadSectors(partitionStart, 2));
xmlFSType.Clusters = volEntry.blocks;
xmlFSType.ClusterSize = (int)imagePlugin.GetSectorSize();
xmlFSType.Files = volEntry.files;
xmlFSType.FilesSpecified = true;
xmlFSType.Type = "UCSD Pascal";
xmlFSType.VolumeName = StringHandlers.PascalToString(volEntry.volumeName);
return;
} }
} }
} }

View File

@@ -5,11 +5,11 @@
// Filename : Structs.cs // Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com> // Author(s) : Natalia Portillo <claunia@claunia.com>
// //
// Component : Component // Component : U.C.S.D. Pascal filesystem plugin.
// //
// --[ Description ] ---------------------------------------------------------- // --[ Description ] ----------------------------------------------------------
// //
// Description // U.C.S.D. Pascal filesystem structures.
// //
// --[ License ] -------------------------------------------------------------- // --[ License ] --------------------------------------------------------------
// //
@@ -29,13 +29,48 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo // Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System;
namespace DiscImageChef.Filesystems.UCSDPascal namespace DiscImageChef.Filesystems.UCSDPascal
{ {
public class Structs // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure
public partial class PascalPlugin : Filesystem
{ {
public Structs() struct PascalVolumeEntry
{ {
/// <summary>0x00, first block of volume entry</summary>
public short firstBlock;
/// <summary>0x02, last block of volume entry</summary>
public short lastBlock;
/// <summary>0x04, entry type</summary>
public PascalFileKind entryType;
/// <summary>0x06, volume name</summary>
public byte[] volumeName;
/// <summary>0x0E, block in volume</summary>
public short blocks;
/// <summary>0x10, files in volume</summary>
public short files;
/// <summary>0x12, dummy</summary>
public short dummy;
/// <summary>0x14, last booted</summary>
public short lastBoot;
/// <summary>0x16, tail to make record same size as <see cref="PascalFileEntry"/></summary>
public int tail;
}
struct PascalFileEntry
{
/// <summary>0x00, first block of file</summary>
public short firstBlock;
/// <summary>0x02, last block of file</summary>
public short lastBlock;
/// <summary>0x04, entry type</summary>
public PascalFileKind entryType;
/// <summary>0x06, file name</summary>
public byte[] filename;
/// <summary>0x16, bytes used in last block</summary>
public short lastBytes;
/// <summary>0x18, modification time</summary>
public short mtime;
} }
} }
} }

View File

@@ -5,11 +5,11 @@
// Filename : Super.cs // Filename : Super.cs
// Author(s) : Natalia Portillo <claunia@claunia.com> // Author(s) : Natalia Portillo <claunia@claunia.com>
// //
// Component : Component // Component : U.C.S.D. Pascal filesystem plugin.
// //
// --[ Description ] ---------------------------------------------------------- // --[ Description ] ----------------------------------------------------------
// //
// Description // Handles mounting and umounting the U.C.S.D. Pascal filesystem.
// //
// --[ License ] -------------------------------------------------------------- // --[ License ] --------------------------------------------------------------
// //
@@ -29,13 +29,108 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Copyright © 2011-2016 Natalia Portillo // Copyright © 2011-2016 Natalia Portillo
// ****************************************************************************/ // ****************************************************************************/
using System; using System;
using System.Collections.Generic;
namespace DiscImageChef.Filesystems.UCSDPascal namespace DiscImageChef.Filesystems.UCSDPascal
{ {
public class Super // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure
public partial class PascalPlugin : Filesystem
{ {
public Super() public override Errno Mount()
{ {
return Mount(false);
}
public override Errno Mount(bool debug)
{
this.debug = debug;
if(device.GetSectors() < 3)
return Errno.InvalidArgument;
// Blocks 0 and 1 are boot code
catalogBlocks = device.ReadSector(2);
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
mountedVolEntry.firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x00);
mountedVolEntry.lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, 0x02);
mountedVolEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, 0x04);
mountedVolEntry.volumeName = new byte[8];
Array.Copy(catalogBlocks, 0x06, mountedVolEntry.volumeName, 0, 8);
mountedVolEntry.blocks = BigEndianBitConverter.ToInt16(catalogBlocks, 0x0E);
mountedVolEntry.files = BigEndianBitConverter.ToInt16(catalogBlocks, 0x10);
mountedVolEntry.dummy = BigEndianBitConverter.ToInt16(catalogBlocks, 0x12);
mountedVolEntry.lastBoot = BigEndianBitConverter.ToInt16(catalogBlocks, 0x14);
mountedVolEntry.tail = BigEndianBitConverter.ToInt32(catalogBlocks, 0x16);
if(mountedVolEntry.firstBlock != 0 || mountedVolEntry.lastBlock <= mountedVolEntry.firstBlock ||
(ulong)mountedVolEntry.lastBlock > device.GetSectors() - 2 ||
(mountedVolEntry.entryType != PascalFileKind.Volume && mountedVolEntry.entryType != PascalFileKind.Secure) ||
mountedVolEntry.volumeName[0] > 7 || mountedVolEntry.blocks < 0 || (ulong)mountedVolEntry.blocks != device.GetSectors() ||
mountedVolEntry.files < 0)
return Errno.InvalidArgument;
catalogBlocks = device.ReadSectors(2, (uint)(mountedVolEntry.lastBlock - mountedVolEntry.firstBlock - 2));
int offset = 26;
fileEntries = new List<PascalFileEntry>();
while(offset + 26 < catalogBlocks.Length)
{
PascalFileEntry entry = new PascalFileEntry();
entry.filename = new byte[16];
entry.firstBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x00);
entry.lastBlock = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x02);
entry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x04);
Array.Copy(catalogBlocks, offset + 0x06, entry.filename, 0, 16);
entry.lastBytes = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x16);
entry.mtime = BigEndianBitConverter.ToInt16(catalogBlocks, offset + 0x18);
if(entry.filename[0] <= 15 && entry.filename[0] > 0)
fileEntries.Add(entry);
offset += 26;
}
bootBlocks = device.ReadSectors(0, 2);
xmlFSType = new Schemas.FileSystemType();
xmlFSType.Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlocks);
xmlFSType.Clusters = mountedVolEntry.blocks;
xmlFSType.ClusterSize = (int)device.GetSectorSize();
xmlFSType.Files = mountedVolEntry.files;
xmlFSType.FilesSpecified = true;
xmlFSType.Type = "UCSD Pascal";
xmlFSType.VolumeName = StringHandlers.PascalToString(mountedVolEntry.volumeName);
mounted = true;
return Errno.NoError;
}
public override Errno Unmount()
{
mounted = false;
fileEntries = null;
return Errno.NoError;
}
public override Errno StatFs(ref FileSystemInfo stat)
{
stat = new FileSystemInfo();
stat.Blocks = mountedVolEntry.blocks;
stat.FilenameLength = 16;
stat.Files = (ulong)mountedVolEntry.files;
stat.FreeBlocks = 0;
stat.PluginId = PluginUUID;
stat.Type = "UCSD Pascal";
stat.FreeBlocks = mountedVolEntry.blocks - (mountedVolEntry.lastBlock - mountedVolEntry.firstBlock + 1);
foreach(PascalFileEntry entry in fileEntries)
stat.FreeBlocks -= (entry.lastBlock - entry.firstBlock + 1);
return Errno.NotImplemented;
} }
} }
} }

View File

@@ -9,7 +9,7 @@
// //
// --[ Description ] ---------------------------------------------------------- // --[ Description ] ----------------------------------------------------------
// //
// Identifies the U.C.S.D. Pascal filesystem and shows information. // Constructors and common variables for the U.C.S.D. Pascal filesystem plugin.
// //
// --[ License ] -------------------------------------------------------------- // --[ License ] --------------------------------------------------------------
// //
@@ -32,266 +32,48 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using DiscImageChef.ImagePlugins; using DiscImageChef.ImagePlugins;
namespace DiscImageChef.Filesystems namespace DiscImageChef.Filesystems.UCSDPascal
{ {
// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure
public class PascalPlugin : Filesystem public partial class PascalPlugin : Filesystem
{ {
bool mounted;
bool debug;
readonly ImagePlugin device;
PascalVolumeEntry mountedVolEntry;
List<PascalFileEntry> fileEntries;
byte[] bootBlocks;
byte[] catalogBlocks;
public PascalPlugin() public PascalPlugin()
{ {
Name = "U.C.S.D. Pascal filesystem"; Name = "U.C.S.D. Pascal filesystem";
PluginUUID = new Guid("B0AC2CB5-72AA-473A-9200-270B5A2C2D53"); PluginUUID = new Guid("B0AC2CB5-72AA-473A-9200-270B5A2C2D53");
} }
public PascalPlugin(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd) public PascalPlugin(ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd)
{ {
device = imagePlugin;
Name = "U.C.S.D. Pascal filesystem"; Name = "U.C.S.D. Pascal filesystem";
PluginUUID = new Guid("B0AC2CB5-72AA-473A-9200-270B5A2C2D53"); PluginUUID = new Guid("B0AC2CB5-72AA-473A-9200-270B5A2C2D53");
} }
public override bool Identify(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd)
{
if(imagePlugin.GetSectors() < 3)
return false;
// Blocks 0 and 1 are boot code
byte[] volBlock = imagePlugin.ReadSector(2 + partitionStart);
PascalVolumeEntry volEntry = new PascalVolumeEntry();
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
volEntry.firstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00);
volEntry.lastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02);
volEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04);
volEntry.volumeName = new byte[8];
Array.Copy(volBlock, 0x06, volEntry.volumeName, 0, 8);
volEntry.blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E);
volEntry.files = BigEndianBitConverter.ToInt16(volBlock, 0x10);
volEntry.dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12);
volEntry.lastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14);
volEntry.tail = BigEndianBitConverter.ToInt32(volBlock, 0x16);
// First block is always 0 (even is it's sector 2)
if(volEntry.firstBlock != 0)
return false;
// Last volume record block must be after first block, and before end of device
if(volEntry.lastBlock <= volEntry.firstBlock || (ulong)volEntry.lastBlock > imagePlugin.GetSectors() - 2)
return false;
// Volume record entry type must be volume or secure
if(volEntry.entryType != PascalFileKind.Volume && volEntry.entryType != PascalFileKind.Secure)
return false;
// Volume name is max 7 characters
if(volEntry.volumeName[0] > 7)
return false;
// Volume blocks is equal to volume sectors
if(volEntry.blocks < 0 || (ulong)volEntry.blocks != imagePlugin.GetSectors())
return false;
// There can be not less than zero files
if(volEntry.files < 0)
return false;
return true;
}
public override void GetInformation(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd, out string information)
{
StringBuilder sbInformation = new StringBuilder();
information = "";
if(imagePlugin.GetSectors() < 3)
return;
// Blocks 0 and 1 are boot code
byte[] volBlock = imagePlugin.ReadSector(2 + partitionStart);
PascalVolumeEntry volEntry = new PascalVolumeEntry();
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
volEntry.firstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00);
volEntry.lastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02);
volEntry.entryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04);
volEntry.volumeName = new byte[8];
Array.Copy(volBlock, 0x06, volEntry.volumeName, 0, 8);
volEntry.blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E);
volEntry.files = BigEndianBitConverter.ToInt16(volBlock, 0x10);
volEntry.dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12);
volEntry.lastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14);
volEntry.tail = BigEndianBitConverter.ToInt32(volBlock, 0x16);
// First block is always 0 (even is it's sector 2)
if(volEntry.firstBlock != 0)
return;
// Last volume record block must be after first block, and before end of device
if(volEntry.lastBlock <= volEntry.firstBlock || (ulong)volEntry.lastBlock > imagePlugin.GetSectors() - 2)
return;
// Volume record entry type must be volume or secure
if(volEntry.entryType != PascalFileKind.Volume && volEntry.entryType != PascalFileKind.Secure)
return;
// Volume name is max 7 characters
if(volEntry.volumeName[0] > 7)
return;
// Volume blocks is equal to volume sectors
if(volEntry.blocks < 0 || (ulong)volEntry.blocks != imagePlugin.GetSectors())
return;
// There can be not less than zero files
if(volEntry.files < 0)
return;
sbInformation.AppendFormat("Volume record spans from block {0} to block {1}", volEntry.firstBlock, volEntry.lastBlock).AppendLine();
sbInformation.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(volEntry.volumeName)).AppendLine();
sbInformation.AppendFormat("Volume has {0} blocks", volEntry.blocks).AppendLine();
sbInformation.AppendFormat("Volume has {0} files", volEntry.files).AppendLine();
sbInformation.AppendFormat("Volume last booted at {0}", DateHandlers.UCSDPascalToDateTime(volEntry.lastBoot)).AppendLine();
information = sbInformation.ToString();
byte[] bootBlocks = imagePlugin.ReadSectors(partitionStart, 2);
xmlFSType = new Schemas.FileSystemType();
xmlFSType.Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlocks);
xmlFSType.Clusters = volEntry.blocks;
xmlFSType.ClusterSize = (int)imagePlugin.GetSectorSize();
xmlFSType.Files = volEntry.files;
xmlFSType.FilesSpecified = true;
xmlFSType.Type = "UCSD Pascal";
xmlFSType.VolumeName = StringHandlers.PascalToString(volEntry.volumeName);
return;
}
enum PascalFileKind : short
{
/// <summary>Disk volume entry</summary>
Volume = 0,
/// <summary>File containing bad blocks</summary>
Bad,
/// <summary>Code file, machine executable</summary>
Code,
/// <summary>Text file, human readable</summary>
Text,
/// <summary>Information file for debugger</summary>
Info,
/// <summary>Data file</summary>
Data,
/// <summary>Graphics vectors</summary>
Graf,
/// <summary>Graphics screen image</summary>
Foto,
/// <summary>Security, not used</summary>
Secure
}
struct PascalVolumeEntry
{
/// <summary>0x00, first block of volume entry</summary>
public short firstBlock;
/// <summary>0x02, last block of volume entry</summary>
public short lastBlock;
/// <summary>0x04, entry type</summary>
public PascalFileKind entryType;
/// <summary>0x06, volume name</summary>
public byte[] volumeName;
/// <summary>0x0E, block in volume</summary>
public short blocks;
/// <summary>0x10, files in volume</summary>
public short files;
/// <summary>0x12, dummy</summary>
public short dummy;
/// <summary>0x14, last booted</summary>
public short lastBoot;
/// <summary>0x16, tail to make record same size as <see cref="PascalFileEntry"/></summary>
public int tail;
}
struct PascalFileEntry
{
/// <summary>0x00, first block of file</summary>
public short firstBlock;
/// <summary>0x02, last block of file</summary>
public short lastBlock;
/// <summary>0x04, entry type</summary>
public PascalFileKind entryType;
/// <summary>0x06, file name</summary>
public byte[] filename;
/// <summary>0x16, bytes used in last block</summary>
public short lastBytes;
/// <summary>0x18, modification time</summary>
public short mtime;
}
public override Errno Mount()
{
return Errno.NotImplemented;
}
public override Errno Mount(bool debug)
{
return Errno.NotImplemented;
}
public override Errno Unmount()
{
return Errno.NotImplemented;
}
public override Errno MapBlock(string path, long fileBlock, ref long deviceBlock)
{
return Errno.NotImplemented;
}
public override Errno GetAttributes(string path, ref FileAttributes attributes)
{
return Errno.NotImplemented;
}
public override Errno ListXAttr(string path, ref List<string> xattrs) public override Errno ListXAttr(string path, ref List<string> xattrs)
{ {
return Errno.NotImplemented; return Errno.NotSupported;
} }
public override Errno GetXattr(string path, string xattr, ref byte[] buf) public override Errno GetXattr(string path, string xattr, ref byte[] buf)
{ {
return Errno.NotImplemented; return Errno.NotSupported;
}
public override Errno Read(string path, long offset, long size, ref byte[] buf)
{
return Errno.NotImplemented;
}
public override Errno ReadDir(string path, ref List<string> contents)
{
return Errno.NotImplemented;
}
public override Errno StatFs(ref FileSystemInfo stat)
{
return Errno.NotImplemented;
}
public override Errno Stat(string path, ref FileEntryInfo stat)
{
return Errno.NotImplemented;
} }
public override Errno ReadLink(string path, ref string dest) public override Errno ReadLink(string path, ref string dest)
{ {
return Errno.NotImplemented; return Errno.NotSupported;
} }
} }
} }

View File

@@ -1,3 +1,8 @@
2016-07-31 Natalia Portillo <claunia@claunia.com>
* DateHandlers.cs: Added support for U.C.S.D. Pascal
filesystem, closes #31
2016-07-29 Natalia Portillo <claunia@claunia.com> 2016-07-29 Natalia Portillo <claunia@claunia.com>
* DiscImageChef.Helpers.csproj: Bump to version 3.1.0. * DiscImageChef.Helpers.csproj: Bump to version 3.1.0.

View File

@@ -140,6 +140,16 @@ namespace DiscImageChef
temp = temp.AddMinutes(minutes); temp = temp.AddMinutes(minutes);
return temp.AddMilliseconds(ticks * 20); return temp.AddMilliseconds(ticks * 20);
} }
public static DateTime UCSDPascalToDateTime(short dateRecord)
{
int year = ((dateRecord & 0xFE00) >> 9) + 1900;
int day = (dateRecord & 0x01F0) >> 4;
int month = (dateRecord & 0x000F);
DicConsole.DebugWriteLine("UCSDPascalToDateTime handler", "dateRecord = 0x{0:X4}, year = {1}, month = {2}, day = {3}", dateRecord, year, month, day);
return new DateTime(year, month, day);
}
} }
} }

View File

@@ -61,6 +61,7 @@ Supported partitioning schemes
Supported file systems for read-only operations Supported file systems for read-only operations
=============================================== ===============================================
* Apple Lisa file system * Apple Lisa file system
* U.C.S.D Pascal file system
Supported file systems for identification and information only Supported file systems for identification and information only
============================================================== ==============================================================

1
TODO
View File

@@ -21,7 +21,6 @@ Filesystem plugins:
--- Add support for SFS filesystem --- Add support for SFS filesystem
--- Add support for PFS3 filesystem --- Add support for PFS3 filesystem
--- Add support for Apple DOS filesystems --- Add support for Apple DOS filesystems
--- Add support for UCSD/Pascal filesystem
--- Add support for AMSDOS filesystem --- Add support for AMSDOS filesystem
--- Add support for CP/M filesystem --- Add support for CP/M filesystem
--- Add support for CBM filesystem --- Add support for CBM filesystem