mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Added support for U.C.S.D. Pascal filesystem, closes #31
This commit is contained in:
@@ -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>
|
||||
|
||||
* Super.cs: Reports correct filesystem version on Statfs();
|
||||
|
||||
@@ -71,6 +71,13 @@
|
||||
<Compile Include="LisaFS\File.cs" />
|
||||
<Compile Include="LisaFS\Extent.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>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
@@ -105,6 +112,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="LisaFS\" />
|
||||
<Folder Include="UCSDPascal\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\LICENSE.LGPL">
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
// Filename : Consts.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Component
|
||||
// Component : U.C.S.D. Pascal filesystem plugin.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Description
|
||||
// U.C.S.D. Pascal filesystem constants.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
@@ -29,13 +29,32 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2016 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
using System;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
// Filename : Dir.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Component
|
||||
// Component : U.C.S.D. Pascal filesystem plugin.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Description
|
||||
// Methods to handle show the U.C.S.D. Pascal catalog as a directory.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
@@ -29,13 +29,35 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2016 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
// Filename : File.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Component
|
||||
// Component : U.C.S.D. Pascal filesystem plugin.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Description
|
||||
// Methods to handle files.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
@@ -29,13 +29,142 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2016 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
// Filename : Info.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Component
|
||||
// Component : U.C.S.D. Pascal filesystem plugin.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Description
|
||||
// Identifies the U.C.S.D. Pascal filesystem and shows information.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
@@ -29,13 +29,133 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2016 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
// Filename : Structs.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Component
|
||||
// Component : U.C.S.D. Pascal filesystem plugin.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Description
|
||||
// U.C.S.D. Pascal filesystem structures.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
@@ -29,13 +29,48 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2016 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
using System;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
// Filename : Super.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Component
|
||||
// Component : U.C.S.D. Pascal filesystem plugin.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Description
|
||||
// Handles mounting and umounting the U.C.S.D. Pascal filesystem.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
@@ -29,13 +29,108 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2016 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// --[ 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 ] --------------------------------------------------------------
|
||||
//
|
||||
@@ -32,266 +32,48 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using DiscImageChef.ImagePlugins;
|
||||
|
||||
namespace DiscImageChef.Filesystems
|
||||
namespace DiscImageChef.Filesystems.UCSDPascal
|
||||
{
|
||||
// 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()
|
||||
{
|
||||
Name = "U.C.S.D. Pascal filesystem";
|
||||
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";
|
||||
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)
|
||||
{
|
||||
return Errno.NotImplemented;
|
||||
return Errno.NotSupported;
|
||||
}
|
||||
|
||||
public override Errno GetXattr(string path, string xattr, ref byte[] buf)
|
||||
{
|
||||
return Errno.NotImplemented;
|
||||
}
|
||||
|
||||
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;
|
||||
return Errno.NotSupported;
|
||||
}
|
||||
|
||||
public override Errno ReadLink(string path, ref string dest)
|
||||
{
|
||||
return Errno.NotImplemented;
|
||||
return Errno.NotSupported;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
* DiscImageChef.Helpers.csproj: Bump to version 3.1.0.
|
||||
|
||||
@@ -140,6 +140,16 @@ namespace DiscImageChef
|
||||
temp = temp.AddMinutes(minutes);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ Supported partitioning schemes
|
||||
Supported file systems for read-only operations
|
||||
===============================================
|
||||
* Apple Lisa file system
|
||||
* U.C.S.D Pascal file system
|
||||
|
||||
Supported file systems for identification and information only
|
||||
==============================================================
|
||||
|
||||
1
TODO
1
TODO
@@ -21,7 +21,6 @@ Filesystem plugins:
|
||||
--- Add support for SFS filesystem
|
||||
--- Add support for PFS3 filesystem
|
||||
--- Add support for Apple DOS filesystems
|
||||
--- Add support for UCSD/Pascal filesystem
|
||||
--- Add support for AMSDOS filesystem
|
||||
--- Add support for CP/M filesystem
|
||||
--- Add support for CBM filesystem
|
||||
|
||||
Reference in New Issue
Block a user