// // Copyright (c) 2008-2011, Kenneth Bell // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Globalization; using System.Text; using DiscUtils.CoreCompat; using DiscUtils.Streams; namespace DiscUtils.Iso9660Ps1 { /// /// Represents a directory that will be built into the ISO image. /// public sealed class BuildDirectoryInfo : BuildDirectoryMember { internal static readonly Comparer PathTableSortComparison = new PathTableComparison(); private readonly Dictionary _members; private readonly BuildDirectoryInfo _parent; private List _sortedMembers; private int _sectorSize; internal BuildDirectoryInfo(string name, BuildDirectoryInfo parent, int sectorSize) : base(name, MakeShortDirName(name, parent)) { _parent = parent == null ? this : parent; HierarchyDepth = parent == null ? 0 : parent.HierarchyDepth + 1; _members = new Dictionary(); _sectorSize = sectorSize; } internal int HierarchyDepth { get; } /// /// The parent directory, or null if none. /// public override BuildDirectoryInfo Parent { get { return _parent; } } /// /// Gets the specified child directory or file. /// /// The name of the file or directory to get. /// The member found (or null). /// true if the specified member was found. internal bool TryGetMember(string name, out BuildDirectoryMember member) { return _members.TryGetValue(name, out member); } internal void Add(BuildDirectoryMember member) { _members.Add(member.Name, member); _sortedMembers = null; } internal override long GetDataSize(Encoding enc) { List sorted = GetSortedMembers(); long total = 34 * 2; // Two pseudo entries (self & parent) foreach (BuildDirectoryMember m in sorted) { uint recordSize = m.GetDirectoryRecordSize(enc); // If this record would span a sector boundary, then the current sector is // zero-padded, and the record goes at the start of the next sector. if (total % _sectorSize + recordSize > _sectorSize) { long padLength = _sectorSize - total % _sectorSize; total += padLength; } total += recordSize; } return MathUtilities.RoundUp(total, _sectorSize); } internal uint GetPathTableEntrySize(Encoding enc) { int nameBytes = enc.GetByteCount(PickName(null, enc)); return (uint)(8 + nameBytes + ((nameBytes & 0x1) == 1 ? 1 : 0)); } internal int Write(byte[] buffer, int offset, Dictionary locationTable, Encoding enc) { int pos = 0; List sorted = GetSortedMembers(); // Two pseudo entries, effectively '.' and '..' pos += WriteMember(this, "\0", Encoding.ASCII, buffer, offset + pos, locationTable, enc, _sectorSize); pos += WriteMember(_parent, "\x01", Encoding.ASCII, buffer, offset + pos, locationTable, enc, _sectorSize); foreach (BuildDirectoryMember m in sorted) { uint recordSize = m.GetDirectoryRecordSize(enc); if (pos % _sectorSize + recordSize > _sectorSize) { int padLength = _sectorSize - pos % _sectorSize; Array.Clear(buffer, offset + pos, padLength); pos += padLength; } pos += WriteMember(m, null, enc, buffer, offset + pos, locationTable, enc, _sectorSize); } // Ensure final padding data is zero'd int finalPadLength = MathUtilities.RoundUp(pos, _sectorSize) - pos; Array.Clear(buffer, offset + pos, finalPadLength); return pos + finalPadLength; } private static int WriteMember(BuildDirectoryMember m, string nameOverride, Encoding nameEnc, byte[] buffer, int offset, Dictionary locationTable, Encoding dataEnc, int sectorSize) { DirectoryRecord dr = new DirectoryRecord(); dr.FileIdentifier = m.PickName(nameOverride, nameEnc); dr.LocationOfExtent = locationTable[m]; dr.DataLength = (uint)m.GetDataSize(dataEnc); dr.RecordingDateAndTime = m.CreationTime; dr.Flags = m is BuildDirectoryInfo ? FileFlags.Directory : FileFlags.None; return dr.WriteTo(buffer, offset, nameEnc); } private static string MakeShortDirName(string longName, BuildDirectoryInfo dir) { if (IsoUtilities.IsValidDirectoryName(longName)) { return longName; } char[] shortNameChars = longName.ToUpper(CultureInfo.InvariantCulture).ToCharArray(); for (int i = 0; i < shortNameChars.Length; ++i) { if (!IsoUtilities.IsValidDChar(shortNameChars[i]) && shortNameChars[i] != '.' && shortNameChars[i] != ';') { shortNameChars[i] = '_'; } } return new string(shortNameChars); } private List GetSortedMembers() { if (_sortedMembers == null) { List sorted = new List(_members.Values); sorted.Sort(SortedComparison); _sortedMembers = sorted; } return _sortedMembers; } private class PathTableComparison : Comparer { public override int Compare(BuildDirectoryInfo x, BuildDirectoryInfo y) { if (x.HierarchyDepth != y.HierarchyDepth) { return x.HierarchyDepth - y.HierarchyDepth; } if (x.Parent != y.Parent) { return Compare(x.Parent, y.Parent); } return CompareNames(x.Name, y.Name, ' '); } private static int CompareNames(string x, string y, char padChar) { int max = Math.Max(x.Length, y.Length); for (int i = 0; i < max; ++i) { char xChar = i < x.Length ? x[i] : padChar; char yChar = i < y.Length ? y[i] : padChar; if (xChar != yChar) { return xChar - yChar; } } return 0; } } } }