ANTFS_Directory.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. /*
  2. This software is subject to the license described in the License.txt file
  3. included with this software distribution. You may not use this file except
  4. in compliance with this license.
  5. Copyright (c) Dynastream Innovations Inc. 2016
  6. All rights reserved.
  7. */
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Text;
  11. using System.Runtime.InteropServices;
  12. using System.Linq;
  13. using System.ComponentModel;
  14. #pragma warning disable 1591
  15. namespace ANT_Managed_Library.ANTFS
  16. {
  17. /// <summary>
  18. /// ANT-FS Directory
  19. /// Contains information about the files available on a remote device
  20. /// </summary>
  21. public class ANTFS_Directory
  22. {
  23. #region Variables
  24. /// <summary>
  25. /// Directory Header
  26. /// </summary>
  27. public Header dirHeader;
  28. /// <summary>
  29. /// Number of entries contained in directory
  30. /// </summary>
  31. public uint dirSize;
  32. /// <summary>
  33. /// Indexed entries in the directory
  34. /// </summary>
  35. public Dictionary<ushort, Entry> dirFiles; // TODO: This should probably not be public, as we might decide later on to change the implementation
  36. #endregion
  37. #region Constants
  38. public static readonly byte DirectoryVersion= 1;
  39. public static readonly byte EntryLength = 16;
  40. public static readonly byte FitDataType = 0x80;
  41. /// <summary>
  42. /// Format of time used in directory
  43. /// </summary>
  44. public enum TimeFormat : byte
  45. {
  46. [Description("Device will use the time as described in the Date field; if the correct time is not known, it will use system time")]
  47. Auto = 0,
  48. [Description("Device will only use system time (seconds since power up)")]
  49. System = 1,
  50. [Description("Device will only use the Date parameter as counter")]
  51. Date = 2
  52. }
  53. /// <summary>
  54. /// Bit mapped flags of file permissions
  55. /// </summary>
  56. [Flags]
  57. public enum GeneralFlags : byte
  58. {
  59. [Description("File can be downloaded")]
  60. Read = 0x80,
  61. [Description("File can be uploaded")]
  62. Write = 0x40,
  63. [Description("File can be erased")]
  64. Erase = 0x20,
  65. [Description("File has been previously downloaded")]
  66. Archive = 0x10,
  67. [Description("Can append data to file")]
  68. Append = 0x08,
  69. [Description("File is encrypted")]
  70. Crypto = 0x04
  71. }
  72. [Flags]
  73. public enum FitSpecificFlags : byte
  74. {
  75. [Description("File is selected for download")]
  76. Selected = 0x01
  77. }
  78. #endregion
  79. #region Data Structures
  80. /// <summary>
  81. /// Directory header structure
  82. /// </summary>
  83. [StructLayout(LayoutKind.Sequential, Pack=1, Size=16)]
  84. public struct Header
  85. {
  86. /// <summary>
  87. /// The version of the Directory File Structure. The most significant 4 bits
  88. /// indicate major revision while the least significant 4 bits indicate a minor
  89. /// revision
  90. /// </summary>
  91. public byte Version;
  92. /// <summary>
  93. /// The length of each structure, in bytes
  94. /// </summary>
  95. public byte ElementLength;
  96. /// <summary>
  97. /// Defines how the system will keep track of Date/Time stamps
  98. /// </summary>
  99. public byte TimeFormat;
  100. /// <summary>
  101. /// Reserved bytes
  102. /// </summary>
  103. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public byte[] Reserved;
  104. /// <summary>
  105. /// Number of seconds elapsed since the system was powered up
  106. /// </summary>
  107. public uint SystemTime;
  108. /// <summary>
  109. /// The number of seconds elapsed since 00:00 in the morning of December 31, 1989.
  110. /// The value zero (0) specifies an unknown date.
  111. /// Values of less than 0x0FFFFFFF will be interpreted as being system time or
  112. /// some other custom time format (e.g. counter incremented every time directory
  113. /// entries are modified)
  114. /// </summary>
  115. public uint Date;
  116. }
  117. /// <summary>
  118. /// Directory file entry structure
  119. /// </summary>
  120. [StructLayout(LayoutKind.Sequential, Pack=1, Size=16)]
  121. public struct Entry
  122. {
  123. /// <summary>
  124. /// Data file index
  125. /// </summary>
  126. public ushort FileIndex;
  127. /// <summary>
  128. /// Data type of the file, which informs how to interpret the file
  129. /// </summary>
  130. public byte FileDataType;
  131. /// <summary>
  132. /// Part of the identifier field, used to uniquely identify a file.
  133. /// In .FIT, used to identify a sub type of the .FIT file type.
  134. /// </summary>
  135. public byte FileSubType;
  136. /// <summary>
  137. /// Part of the identifier field, used to uniquely identify a file.
  138. /// In .FIT, used to identify a particular instance of a file sub type.
  139. /// </summary>
  140. public ushort FileNumber;
  141. /// <summary>
  142. /// File data type specific bit mapped flags
  143. /// </summary>
  144. public byte SpecificFlags;
  145. /// <summary>
  146. /// Bit mapped flags of file permissions
  147. /// </summary>
  148. public byte GeneralFlags;
  149. /// <summary>
  150. /// Size of file in bytes
  151. /// </summary>
  152. public uint FileSize;
  153. /// <summary>
  154. /// The number of seconds elapsed since 00:00 in the morning of December 31, 1989.
  155. /// The value zero (0) specifies an unknown date.
  156. /// Values of less than 0x0FFFFFFF will be interpreted as being system time or
  157. /// some other custom time format.
  158. /// </summary>
  159. public uint TimeStamp;
  160. }
  161. #endregion
  162. #region DLLImports
  163. [DllImport(ANT_Common.ANT_UNMANAGED_WRAPPER, CallingConvention=CallingConvention.Cdecl)]
  164. private static extern uint ANTFSDirectory_GetNumberOfFileEntries(
  165. [MarshalAs(UnmanagedType.LPArray)] byte[] pvDirectory,
  166. uint ulDirectoryFileLength);
  167. [DllImport(ANT_Common.ANT_UNMANAGED_WRAPPER, CallingConvention=CallingConvention.Cdecl)]
  168. private static extern int ANTFSDirectory_LookupFileEntry(
  169. [MarshalAs(UnmanagedType.LPArray)] byte[] pvDirectory,
  170. uint ulDirectoryFileLength,
  171. uint ulFileEntry,
  172. ref Entry pstDirStruct);
  173. [DllImport(ANT_Common.ANT_UNMANAGED_WRAPPER, CallingConvention=CallingConvention.Cdecl)]
  174. private static extern int ANTFSDirectory_GetNewFileList(
  175. [MarshalAs(UnmanagedType.LPArray)] byte[] pvDirectory,
  176. uint ulDirectoryFileLength,
  177. [MarshalAs(UnmanagedType.LPArray)] ushort[] pusFileIndexList,
  178. ref ushort pusListLength);
  179. [DllImport(ANT_Common.ANT_UNMANAGED_WRAPPER, CallingConvention=CallingConvention.Cdecl)]
  180. private static extern int ANTFSDirectory_LookupFileIndex(
  181. [MarshalAs(UnmanagedType.LPArray)] byte[] pvDirectory,
  182. uint ulDirectoryFileLength,
  183. ushort usFileIndex,
  184. ref Entry pstDirStruct);
  185. #endregion
  186. #region Constructor
  187. /// <summary>
  188. /// Creates an empty ANT-FS Directory structure
  189. /// </summary>
  190. public ANTFS_Directory()
  191. {
  192. // Initialize header
  193. dirHeader.Version = DirectoryVersion;
  194. dirHeader.ElementLength = EntryLength;
  195. dirHeader.TimeFormat = (byte) TimeFormat.Auto;
  196. dirHeader.Reserved = new byte[] {0, 0, 0, 0, 0};
  197. dirHeader.SystemTime = 0;
  198. dirHeader.Date = 0;
  199. // Empty directory: initial size is 0
  200. dirSize = 0;
  201. dirFiles = new Dictionary<ushort,Entry>();
  202. }
  203. /// <summary>
  204. /// Creates an ANTFS_Directory object from downlaoded data
  205. /// </summary>
  206. /// <param name="DirectoryFile">Directory file received on a download</param>
  207. public ANTFS_Directory(byte[] DirectoryFile)
  208. {
  209. // Check endianness
  210. if (!BitConverter.IsLittleEndian)
  211. {
  212. Array.Reverse(DirectoryFile);
  213. }
  214. // Parse header
  215. this.dirHeader = GetHeader(DirectoryFile);
  216. // Get number of file entries
  217. this.dirSize = GetNumberOfFileEntries(DirectoryFile);
  218. // Populate list of files
  219. this.dirFiles = new Dictionary<ushort,Entry>();
  220. if (this.dirSize > 0)
  221. {
  222. for (uint i = 0; i < this.dirSize; i++)
  223. {
  224. Entry? newEntry = LookupFileEntry(DirectoryFile, i);
  225. if (newEntry != null)
  226. {
  227. dirFiles.Add(newEntry.Value.FileIndex, newEntry.Value);
  228. }
  229. }
  230. }
  231. }
  232. #endregion
  233. #region Interface for ANT-FS library functions (unmanaged)
  234. // These functions are static, and operate on raw directory data
  235. /// <summary>
  236. /// Obtains the number of file entries contained in the directory
  237. /// </summary>
  238. /// <param name="pvDirectory">Directory file</param>
  239. /// <returns>Number of file entries contained in directory</returns>
  240. public static uint GetNumberOfFileEntries(byte[] pvDirectory)
  241. {
  242. return ANTFSDirectory_GetNumberOfFileEntries(pvDirectory, (uint) pvDirectory.Length);
  243. }
  244. /// <summary>
  245. /// Decodes the directory and gets a list of files that need to be downloaded
  246. /// </summary>
  247. /// <param name="pvDirectory">Directory file</param>
  248. /// <returns>Array containing the file indexes that need to be downloaded.
  249. /// Returns an empty array if there are no new files.</returns>
  250. public static ushort[] GetNewFileList(byte[] pvDirectory)
  251. {
  252. ushort usListLength = UInt16.MaxValue;
  253. ushort[] pusFileList = null;
  254. // Set array to null to retrieve the size first
  255. if (ANTFSDirectory_GetNewFileList(pvDirectory, (uint)pvDirectory.Length, pusFileList, ref usListLength) == 0)
  256. return new ushort[0];
  257. // Allocate array of correct size, and request list
  258. pusFileList = new ushort[usListLength];
  259. if (usListLength > 0)
  260. {
  261. if (ANTFSDirectory_GetNewFileList(pvDirectory, (uint)pvDirectory.Length, pusFileList, ref usListLength) == 0)
  262. return new ushort[0];
  263. }
  264. return pusFileList;
  265. }
  266. /// <summary>
  267. /// Looks up the requested directory entry
  268. /// </summary>
  269. /// <param name="pvDirectory">Directory file</param>
  270. /// <param name="ulFileEntry">Zero-based entry number of the requested file (based on the order in which files are written in directory)</param>
  271. /// <returns>Requested directory entry, or null if entry is not valid</returns>
  272. public static Entry? LookupFileEntry(byte[] pvDirectory, uint ulFileEntry)
  273. {
  274. Entry myEntry = new Entry();
  275. if (ANTFSDirectory_LookupFileEntry(pvDirectory, (uint)pvDirectory.Length, ulFileEntry, ref myEntry) == 0)
  276. return null;
  277. return myEntry;
  278. }
  279. /// <summary>
  280. /// Looks up the requested directory entry
  281. /// </summary>
  282. /// <param name="pvDirectory">Directory file</param>
  283. /// <param name="usFileIndex">Index of file to be looked up</param>
  284. /// <returns>Requested directory entry, or null if entry is not valid</returns>
  285. public static Entry? LookupFileIndex(byte[] pvDirectory, ushort usFileIndex)
  286. {
  287. Entry myEntry = new Entry();
  288. if (ANTFSDirectory_LookupFileIndex(pvDirectory, (uint)pvDirectory.Length, usFileIndex, ref myEntry) == 0)
  289. return null;
  290. return myEntry;
  291. }
  292. #endregion
  293. #region Managed Directory Functions
  294. /// <summary>
  295. /// Retrieves the directory file header
  296. /// </summary>
  297. /// <param name="pvDirectory">Directory file</param>
  298. /// <returns>Directory header structure. An exception is thrown if the file is too small to contain a header</returns>
  299. public Header GetHeader(byte[] pvDirectory)
  300. {
  301. // Make sure data is at least the size of the header
  302. if (pvDirectory.Length < Marshal.SizeOf(typeof(Header)))
  303. throw new ANT_Exception("Error: Invalid directory file");
  304. // Fill header struct
  305. GCHandle pinnedDirectory = GCHandle.Alloc(pvDirectory, GCHandleType.Pinned); // Pin directory while we parse header
  306. Header myHeader = (Header)Marshal.PtrToStructure(pinnedDirectory.AddrOfPinnedObject(), typeof(Header)); // Marshal byte array to C# structure
  307. pinnedDirectory.Free(); // Unpin directory
  308. return myHeader;
  309. }
  310. /// <summary>
  311. /// Print directory
  312. /// </summary>
  313. /// <returns>Formatted string with decoded directory</returns>
  314. public override string ToString()
  315. {
  316. string strPrint = "";
  317. // Header Info
  318. strPrint += "ANT-FS Directory Version: " + GetVersion() + Environment.NewLine;
  319. if (dirSize > 0)
  320. {
  321. strPrint += "Index".PadRight(7);
  322. strPrint += "Data Type".PadRight(11);
  323. strPrint += "Identifier".PadRight(12);
  324. strPrint += "Flags".PadRight(14);
  325. strPrint += "File Size".PadRight(13);
  326. strPrint += "Timestamp";
  327. strPrint += Environment.NewLine;
  328. }
  329. // Files
  330. var sortedDir = (from entry in dirFiles orderby entry.Key ascending select entry); // Sort by index
  331. foreach(KeyValuePair<ushort, Entry> kvp in sortedDir)
  332. {
  333. strPrint += kvp.Key.ToString().PadRight(7);
  334. strPrint += kvp.Value.FileDataType.ToString().PadRight(11);
  335. if (kvp.Value.FileDataType == 128) // .FIT file
  336. {
  337. strPrint += (kvp.Value.FileSubType + "-" + kvp.Value.FileNumber).PadRight(12);
  338. }
  339. else
  340. {
  341. strPrint += (((uint)kvp.Value.FileSubType << 16) + (uint)kvp.Value.FileNumber).ToString().PadRight(12);
  342. }
  343. strPrint += ParseFlags(kvp.Value.GeneralFlags).PadRight(14);
  344. strPrint += kvp.Value.FileSize.ToString().PadRight(13);
  345. uint ulDate = kvp.Value.TimeStamp;
  346. if (ulDate == 0)
  347. {
  348. strPrint += "Unknown";
  349. }
  350. else if (ulDate < 0x0FFFFFFF)
  351. {
  352. strPrint += ulDate; // System Time
  353. }
  354. else
  355. {
  356. DateTime dateBase = new DateTime(1989, 12, 31, 0, 0, 0); // December 31, 1989, 00:00 hrs
  357. dateBase = dateBase.AddSeconds(ulDate);
  358. strPrint += dateBase.ToString();
  359. }
  360. strPrint += Environment.NewLine;
  361. }
  362. strPrint += " " + dirSize + " File(s)" + Environment.NewLine;
  363. return strPrint;
  364. }
  365. /// <summary>
  366. /// Obtains version of the directory
  367. /// </summary>
  368. /// <returns>Formatted string with ANT-FS directory version</returns>
  369. public string GetVersion()
  370. {
  371. byte ucMajorRev = (byte)((dirHeader.Version & (byte)0xF0) >> 4);
  372. byte ucMinorRev = (byte)(dirHeader.Version & (byte)0x0F);
  373. return (ucMajorRev + "." + ucMinorRev);
  374. }
  375. public void Clear()
  376. {
  377. dirFiles.Clear();
  378. dirSize = 0;
  379. }
  380. public void AddEntry(Entry newEntry)
  381. {
  382. if (newEntry.FileIndex == 0)
  383. throw new ArgumentException("File index 0 is reserved for the directory");
  384. if (dirFiles.ContainsKey(newEntry.FileIndex))
  385. throw new ArgumentException("Specified file index is already in use");
  386. dirFiles.Add(newEntry.FileIndex, newEntry);
  387. dirSize++;
  388. }
  389. public void AddOrReplaceEntry(Entry newEntry)
  390. {
  391. if (newEntry.FileIndex == 0)
  392. throw new ArgumentException("File index 0 is reserved for the directory");
  393. if (!dirFiles.ContainsKey(newEntry.FileIndex))
  394. {
  395. dirFiles.Add(newEntry.FileIndex, newEntry);
  396. dirSize++;
  397. }
  398. else
  399. {
  400. dirFiles[newEntry.FileIndex] = newEntry;
  401. }
  402. }
  403. public void DeleteEntry(ushort fileIndex)
  404. {
  405. if (fileIndex == 0)
  406. throw new UnauthorizedAccessException("Directory cannot be erased");
  407. if (!dirFiles.ContainsKey(fileIndex))
  408. throw new ArgumentException("Invalid Index: File does not exist");
  409. Entry tempEntry = dirFiles[fileIndex];
  410. if ((tempEntry.GeneralFlags & (byte)GeneralFlags.Erase) == 0)
  411. throw new UnauthorizedAccessException("Not enough permissions to erase the file");
  412. dirFiles.Remove(fileIndex);
  413. dirSize--;
  414. }
  415. public void ForceDeleteEntry(ushort fileIndex)
  416. {
  417. if (fileIndex == 0)
  418. throw new UnauthorizedAccessException("Directory cannot be erased");
  419. if (!dirFiles.ContainsKey(fileIndex))
  420. throw new ArgumentException("Invalid Index: File does not exist");
  421. dirFiles.Remove(fileIndex);
  422. dirSize--;
  423. }
  424. public Entry GetEntry(ushort fileIndex)
  425. {
  426. if (fileIndex == 0)
  427. throw new ArgumentException("There is no entry associated with the directory");
  428. if (!dirFiles.ContainsKey(fileIndex))
  429. throw new ArgumentException("Invalid Index: File does not exist");
  430. return dirFiles[fileIndex];
  431. }
  432. public List<ushort> GetAllIndexes()
  433. {
  434. return dirFiles.Keys.ToList();
  435. }
  436. public List<ushort> GetIndexes(Func<Entry, bool> filter) // TODO: or array? or something else?
  437. {
  438. return dirFiles.Where(kvp => filter(kvp.Value)).Select(kvp => kvp.Key).ToList();
  439. }
  440. public byte[] ToByteArray()
  441. {
  442. // Size of the entire directory structure:
  443. int offset = 0;
  444. uint dirLength = EntryLength * (dirSize + 1); // Entries + header
  445. byte[] rawDir = new byte[dirLength];
  446. // First convert the header
  447. IntPtr elementPtr = Marshal.AllocHGlobal(EntryLength);
  448. Marshal.StructureToPtr(dirHeader, elementPtr, true);
  449. Marshal.Copy(elementPtr, rawDir, offset, EntryLength);
  450. Marshal.FreeHGlobal(elementPtr);
  451. // Now walk through the entries
  452. foreach (Entry fileEntry in dirFiles.Values)
  453. {
  454. offset += EntryLength;
  455. elementPtr = Marshal.AllocHGlobal(EntryLength);
  456. Marshal.StructureToPtr(fileEntry, elementPtr, true);
  457. Marshal.Copy(elementPtr, rawDir, offset, EntryLength);
  458. Marshal.FreeHGlobal(elementPtr);
  459. }
  460. return rawDir;
  461. }
  462. public uint GetFileSize(ushort fileIndex)
  463. {
  464. if(fileIndex == 0)
  465. return EntryLength * (dirSize + 1);
  466. if (dirFiles.ContainsKey(fileIndex))
  467. {
  468. return dirFiles[fileIndex].FileSize;
  469. }
  470. else
  471. throw new ArgumentException("Invalid Index: File does not exist");
  472. }
  473. // Convenience methods for checking permissions
  474. public static bool IsFileReadable(Entry entry)
  475. {
  476. return (entry.GeneralFlags & (byte)GeneralFlags.Read) != 0;
  477. }
  478. public bool IsFileReadable(ushort fileIndex)
  479. {
  480. if (fileIndex == 0)
  481. return true;
  482. if (!dirFiles.ContainsKey(fileIndex))
  483. throw new ArgumentException("Invalid Index: File does not exist");
  484. return IsFileReadable(dirFiles[fileIndex]);
  485. }
  486. public static bool IsFileWriteable(Entry entry)
  487. {
  488. return (entry.GeneralFlags & (byte)ANTFS_Directory.GeneralFlags.Write) != 0;
  489. }
  490. public bool IsFileWriteable(ushort fileIndex)
  491. {
  492. if (fileIndex == 0)
  493. return false;
  494. if (!dirFiles.ContainsKey(fileIndex))
  495. throw new ArgumentException("Invalid Index: File does not exist");
  496. return IsFileWriteable(dirFiles[fileIndex]);
  497. }
  498. public static bool IsFileEraseable(Entry entry)
  499. {
  500. return (entry.GeneralFlags & (byte)GeneralFlags.Erase) != 0;
  501. }
  502. public bool IsFileEraseable(ushort fileIndex)
  503. {
  504. if (fileIndex == 0)
  505. return false;
  506. if (!dirFiles.ContainsKey(fileIndex))
  507. throw new ArgumentException("Invalid Index: File does not exist");
  508. return IsFileEraseable(dirFiles[fileIndex]);
  509. }
  510. public static bool IsFileArchived(Entry entry)
  511. {
  512. return (entry.GeneralFlags & (byte)GeneralFlags.Archive) != 0;
  513. }
  514. public bool IsFileArchived(ushort fileIndex)
  515. {
  516. if (fileIndex == 0)
  517. return false;
  518. if (!dirFiles.ContainsKey(fileIndex))
  519. throw new ArgumentException("Invalid Index: File does not exist");
  520. return IsFileArchived(dirFiles[fileIndex]);
  521. }
  522. public static bool IsFileEncrypted(Entry entry)
  523. {
  524. return (entry.GeneralFlags & (byte)GeneralFlags.Crypto) != 0;
  525. }
  526. public bool IsFileEncrypted(ushort fileIndex)
  527. {
  528. if (fileIndex == 0)
  529. return false;
  530. if (!dirFiles.ContainsKey(fileIndex))
  531. throw new ArgumentException("Invalid Index: File does not exist");
  532. return IsFileEncrypted(dirFiles[fileIndex]);
  533. }
  534. public static bool IsFileSelected(Entry entry)
  535. {
  536. return (entry.SpecificFlags & (byte)FitSpecificFlags.Selected) != 0;
  537. }
  538. public bool IsFileSelected(ushort fileIndex)
  539. {
  540. if (fileIndex == 0)
  541. return false;
  542. if (!dirFiles.ContainsKey(fileIndex))
  543. throw new ArgumentException("Invalid Index: File does not exist");
  544. return IsFileSelected(dirFiles[fileIndex]);
  545. }
  546. /// <summary>
  547. /// Parses general flags into a string
  548. /// </summary>
  549. /// <param name="ucFlags">Flag byte</param>
  550. /// <returns>Formatted string with decoded flags</returns>
  551. public static string ParseFlags(byte ucFlags)
  552. {
  553. bool bFirst = true;
  554. string strFlags = "";
  555. if ((ucFlags & (byte)GeneralFlags.Read) != 0)
  556. {
  557. if (!bFirst)
  558. strFlags += "|";
  559. else
  560. bFirst = false;
  561. strFlags += "Re";
  562. }
  563. if ((ucFlags & (byte)GeneralFlags.Write) != 0)
  564. {
  565. if (!bFirst)
  566. strFlags += "|";
  567. else
  568. bFirst = false;
  569. strFlags += "Wr";
  570. }
  571. if ((ucFlags & (byte)GeneralFlags.Erase) != 0)
  572. {
  573. if (!bFirst)
  574. strFlags += "|";
  575. else
  576. bFirst = false;
  577. strFlags += "Er";
  578. }
  579. if ((ucFlags & (byte)GeneralFlags.Archive) != 0)
  580. {
  581. if (!bFirst)
  582. strFlags += "|";
  583. else
  584. bFirst = false;
  585. strFlags += "Ar";
  586. }
  587. if ((ucFlags & (byte)GeneralFlags.Append) != 0)
  588. {
  589. if (!bFirst)
  590. strFlags += "|";
  591. else
  592. bFirst = false;
  593. strFlags += "Ap";
  594. }
  595. if ((ucFlags & (byte)GeneralFlags.Crypto) != 0)
  596. {
  597. if (!bFirst)
  598. strFlags += "|";
  599. else
  600. bFirst = false;
  601. strFlags += "Cr";
  602. }
  603. return strFlags;
  604. }
  605. #endregion
  606. }
  607. }