Megafile.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // The Command & Conquer Map Editor and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // The Command & Conquer Map Editor and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. using System;
  15. using System.Collections;
  16. using System.Collections.Generic;
  17. using System.IO;
  18. using System.IO.MemoryMappedFiles;
  19. using System.Runtime.InteropServices;
  20. namespace MobiusEditor.Utility
  21. {
  22. [StructLayout(LayoutKind.Sequential, Pack = 2)]
  23. struct SubFileData
  24. {
  25. public ushort Flags;
  26. public uint CRCValue;
  27. public int SubfileIndex;
  28. public uint SubfileSize;
  29. public uint SubfileImageDataOffset;
  30. public ushort SubfileNameIndex;
  31. public static readonly uint Size = (uint)Marshal.SizeOf(typeof(SubFileData));
  32. }
  33. public class Megafile : IEnumerable<string>, IEnumerable, IDisposable
  34. {
  35. private readonly MemoryMappedFile megafileMap;
  36. private readonly string[] stringTable;
  37. private readonly Dictionary<string, SubFileData> fileTable = new Dictionary<string, SubFileData>();
  38. public Megafile(string megafilePath)
  39. {
  40. megafileMap = MemoryMappedFile.CreateFromFile(
  41. new FileStream(megafilePath, FileMode.Open, FileAccess.Read, FileShare.Read) , null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, false
  42. );
  43. var numFiles = 0U;
  44. var numStrings = 0U;
  45. var stringTableSize = 0U;
  46. var fileTableSize = 0U;
  47. var readOffset = 0U;
  48. using (var magicNumberReader = new BinaryReader(megafileMap.CreateViewStream(readOffset, 4, MemoryMappedFileAccess.Read)))
  49. {
  50. var magicNumber = magicNumberReader.ReadUInt32();
  51. if ((magicNumber == 0xFFFFFFFF) || (magicNumber == 0x8FFFFFFF))
  52. {
  53. // Skip header size and version
  54. readOffset += 8;
  55. }
  56. }
  57. readOffset += 4U;
  58. using (var headerReader = new BinaryReader(megafileMap.CreateViewStream(readOffset, 12, MemoryMappedFileAccess.Read)))
  59. {
  60. numFiles = headerReader.ReadUInt32();
  61. numStrings = headerReader.ReadUInt32();
  62. stringTableSize = headerReader.ReadUInt32();
  63. fileTableSize = numFiles * SubFileData.Size;
  64. }
  65. readOffset += 12U;
  66. using (var stringReader = new BinaryReader(megafileMap.CreateViewStream(readOffset, stringTableSize, MemoryMappedFileAccess.Read)))
  67. {
  68. stringTable = new string[numStrings];
  69. for (var i = 0U; i < numStrings; ++i)
  70. {
  71. var stringSize = stringReader.ReadUInt16();
  72. stringTable[i] = new string(stringReader.ReadChars(stringSize));
  73. }
  74. }
  75. readOffset += stringTableSize;
  76. using (var subFileAccessor = megafileMap.CreateViewAccessor(readOffset, fileTableSize, MemoryMappedFileAccess.Read))
  77. {
  78. for (var i = 0U; i < numFiles; ++i)
  79. {
  80. subFileAccessor.Read(i * SubFileData.Size, out SubFileData subFile);
  81. var fullName = stringTable[subFile.SubfileNameIndex];
  82. fileTable[fullName] = subFile;
  83. }
  84. }
  85. }
  86. public Stream Open(string path)
  87. {
  88. if (!fileTable.TryGetValue(path, out SubFileData subFile))
  89. {
  90. return null;
  91. }
  92. return megafileMap.CreateViewStream(subFile.SubfileImageDataOffset, subFile.SubfileSize, MemoryMappedFileAccess.Read);
  93. }
  94. public IEnumerator<string> GetEnumerator()
  95. {
  96. foreach (var file in stringTable)
  97. {
  98. yield return file;
  99. }
  100. }
  101. IEnumerator IEnumerable.GetEnumerator()
  102. {
  103. return GetEnumerator();
  104. }
  105. #region IDisposable Support
  106. private bool disposedValue = false;
  107. protected virtual void Dispose(bool disposing)
  108. {
  109. if (!disposedValue)
  110. {
  111. if (disposing)
  112. {
  113. megafileMap.Dispose();
  114. }
  115. disposedValue = true;
  116. }
  117. }
  118. public void Dispose()
  119. {
  120. Dispose(true);
  121. }
  122. #endregion
  123. }
  124. }