diff --git a/DiscImageChef.Filesystems/Opera/Consts.cs b/DiscImageChef.Filesystems/Opera/Consts.cs index 4ee4cc7b0..3bd1af546 100644 --- a/DiscImageChef.Filesystems/Opera/Consts.cs +++ b/DiscImageChef.Filesystems/Opera/Consts.cs @@ -1,3 +1,5 @@ +using DiscImageChef.Helpers; + namespace DiscImageChef.Filesystems { public partial class OperaFS @@ -18,6 +20,7 @@ namespace DiscImageChef.Filesystems /// Catapult /// const uint TYPE_ZAP = 0x2A7A6170; + static readonly int DirectoryEntrySize = Marshal.SizeOf(); enum FileFlags : uint { diff --git a/DiscImageChef.Filesystems/Opera/Dir.cs b/DiscImageChef.Filesystems/Opera/Dir.cs index 9882a25fd..cef48e5e6 100644 --- a/DiscImageChef.Filesystems/Opera/Dir.cs +++ b/DiscImageChef.Filesystems/Opera/Dir.cs @@ -1,11 +1,57 @@ using System; using System.Collections.Generic; using DiscImageChef.CommonTypes.Structs; +using DiscImageChef.Helpers; namespace DiscImageChef.Filesystems { public partial class OperaFS { public Errno ReadDir(string path, out List contents) => throw new NotImplementedException(); + + Dictionary DecodeDirectory(int firstBlock) + { + Dictionary entries = + new Dictionary(); + + int nextBlock = firstBlock; + + do + { + byte[] data = + image.ReadSectors((ulong)(nextBlock * volumeBlockSizeRatio), volumeBlockSizeRatio); + DirectoryHeader header = Marshal.ByteArrayToStructureBigEndian(data); + nextBlock = header.next_block; + + int off = (int)header.first_used; + + DirectoryEntry entry = new DirectoryEntry(); + + while(off + DirectoryEntrySize < data.Length) + { + entry = Marshal.ByteArrayToStructureBigEndian(data, off, DirectoryEntrySize); + string name = StringHandlers.CToString(entry.name, Encoding); + + DirectoryEntryWithPointers entryWithPointers = + new DirectoryEntryWithPointers {entry = entry, pointers = new uint[entry.last_copy + 1]}; + + for(int i = 0; i <= entry.last_copy; i++) + entryWithPointers.pointers[i] = + BigEndianBitConverter.ToUInt32(data, off + DirectoryEntrySize + i * 4); + + entries.Add(name, entryWithPointers); + + if((entry.flags & (uint)FileFlags.LastEntry) != 0 || + (entry.flags & (uint)FileFlags.LastEntryInBlock) != 0) break; + + off += (int)(DirectoryEntrySize + (entry.last_copy + 1) * 4); + } + + if((entry.flags & (uint)FileFlags.LastEntry) != 0) break; + } + while(nextBlock != -1); + + return entries; + } } } \ No newline at end of file diff --git a/DiscImageChef.Filesystems/Opera/Opera.cs b/DiscImageChef.Filesystems/Opera/Opera.cs index 4147e5c77..79ff37f15 100644 --- a/DiscImageChef.Filesystems/Opera/Opera.cs +++ b/DiscImageChef.Filesystems/Opera/Opera.cs @@ -41,6 +41,12 @@ namespace DiscImageChef.Filesystems { public partial class OperaFS : IReadOnlyFilesystem { + IMediaImage image; + bool mounted; + Dictionary rootDirectoryCache; + FileSystemInfo statfs; + uint volumeBlockSizeRatio; + public FileSystemType XmlFsType { get; private set; } public Encoding Encoding { get; private set; } public string Name => "Opera Filesystem Plugin"; diff --git a/DiscImageChef.Filesystems/Opera/Super.cs b/DiscImageChef.Filesystems/Opera/Super.cs index 52f797d04..330b696d3 100644 --- a/DiscImageChef.Filesystems/Opera/Super.cs +++ b/DiscImageChef.Filesystems/Opera/Super.cs @@ -4,17 +4,74 @@ using System.Text; using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes.Interfaces; using DiscImageChef.CommonTypes.Structs; +using DiscImageChef.Helpers; +using Schemas; namespace DiscImageChef.Filesystems { public partial class OperaFS { public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) => + Dictionary options, string @namespace) + { + // TODO: Find correct default encoding + Encoding = Encoding.ASCII; + + byte[] sbSector = imagePlugin.ReadSector(0 + partition.Start); + + SuperBlock sb = Marshal.ByteArrayToStructureBigEndian(sbSector); + + if(sb.record_type != 1 || sb.record_version != 1) return Errno.InvalidArgument; + if(Encoding.ASCII.GetString(sb.sync_bytes) != SYNC) return Errno.InvalidArgument; + + if(imagePlugin.Info.SectorSize == 2336 || imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448) volumeBlockSizeRatio = sb.block_size / 2048; + else volumeBlockSizeRatio = sb.block_size / imagePlugin.Info.SectorSize; + + XmlFsType = new FileSystemType + { + Type = "Opera", + VolumeName = StringHandlers.CToString(sb.volume_label, Encoding), + ClusterSize = sb.block_size, + Clusters = sb.block_count, + Bootable = true, + VolumeSerial = $"{sb.volume_id:X8}" + }; + + statfs = new FileSystemInfo + { + Blocks = sb.block_count, + FilenameLength = MAX_NAME, + FreeBlocks = 0, + Id = new FileSystemId {IsInt = true, Serial32 = sb.volume_id}, + PluginId = Id, + Type = "Opera" + }; + + image = imagePlugin; + int firstRootBlock = BigEndianBitConverter.ToInt32(sbSector, Marshal.SizeOf()); + rootDirectoryCache = DecodeDirectory(firstRootBlock); + mounted = true; + throw new NotImplementedException(); + } - public Errno Unmount() => throw new NotImplementedException(); + public Errno Unmount() + { + if(!mounted) return Errno.AccessDenied; - public Errno StatFs(out FileSystemInfo stat) => throw new NotImplementedException(); + mounted = false; + + return Errno.NoError; + } + + public Errno StatFs(out FileSystemInfo stat) + { + stat = null; + if(!mounted) return Errno.AccessDenied; + + stat = statfs.ShallowCopy(); + return Errno.NoError; + } } } \ No newline at end of file