CArchiveLoader.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. * CArchiveLoader.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "CArchiveLoader.h"
  12. #include "VCMIDirs.h"
  13. #include "CFileInputStream.h"
  14. #include "CCompressedStream.h"
  15. #include "CBinaryReader.h"
  16. VCMI_LIB_NAMESPACE_BEGIN
  17. ArchiveEntry::ArchiveEntry()
  18. : offset(0), fullSize(0), compressedSize(0)
  19. {
  20. }
  21. CArchiveLoader::CArchiveLoader(std::string _mountPoint, bfs::path _archive, bool extractArchives) :
  22. archive(std::move(_archive)),
  23. mountPoint(std::move(_mountPoint)),
  24. extractArchives(extractArchives)
  25. {
  26. // Open archive file(.snd, .vid, .lod)
  27. CFileInputStream fileStream(archive);
  28. // Fake .lod file with no data has to be silently ignored.
  29. if(fileStream.getSize() < 10)
  30. return;
  31. // Retrieve file extension of archive in uppercase
  32. const std::string ext = boost::to_upper_copy(archive.extension().string());
  33. // Init the specific lod container format
  34. if(ext == ".LOD" || ext == ".PAC")
  35. initLODArchive(mountPoint, fileStream);
  36. else if(ext == ".VID")
  37. initVIDArchive(mountPoint, fileStream);
  38. else if(ext == ".SND")
  39. initSNDArchive(mountPoint, fileStream);
  40. else
  41. throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive.string());
  42. logGlobal->trace("%sArchive \"%s\" loaded (%d files found).", ext, archive.filename(), entries.size());
  43. }
  44. void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStream & fileStream)
  45. {
  46. // Read count of total files
  47. CBinaryReader reader(&fileStream);
  48. fileStream.seek(8);
  49. ui32 totalFiles = reader.readUInt32();
  50. // Get all entries from file
  51. fileStream.seek(0x5c);
  52. // Insert entries to list
  53. for(ui32 i = 0; i < totalFiles; ++i)
  54. {
  55. char filename[16];
  56. reader.read(reinterpret_cast<ui8*>(filename), 16);
  57. // Create archive entry
  58. ArchiveEntry entry;
  59. entry.name = filename;
  60. entry.offset = reader.readUInt32();
  61. entry.fullSize = reader.readUInt32();
  62. fileStream.skip(4); // unused, unknown
  63. entry.compressedSize = reader.readUInt32();
  64. // Add lod entry to local entries map
  65. entries[ResourceID(mountPoint + entry.name)] = entry;
  66. if(extractArchives)
  67. {
  68. si64 currentPosition = fileStream.tell(); // save filestream position
  69. std::string fName = filename;
  70. boost::to_upper(fName);
  71. if(fName.find(".PCX") != std::string::npos)
  72. extractToFolder("IMAGES", mountPoint, entry);
  73. else if ((fName.find(".DEF") != std::string::npos ) || (fName.find(".MSK") != std::string::npos) || (fName.find(".FNT") != std::string::npos) || (fName.find(".PAL") != std::string::npos))
  74. extractToFolder("SPRITES", mountPoint, entry);
  75. else if ((fName.find(".h3c") != std::string::npos))
  76. extractToFolder("SPRITES", mountPoint, entry);
  77. else
  78. extractToFolder("MISC", mountPoint, entry);
  79. fileStream.seek(currentPosition); // restore filestream position
  80. }
  81. }
  82. }
  83. void CArchiveLoader::initVIDArchive(const std::string &mountPoint, CFileInputStream & fileStream)
  84. {
  85. // Read count of total files
  86. CBinaryReader reader(&fileStream);
  87. fileStream.seek(0);
  88. ui32 totalFiles = reader.readUInt32();
  89. std::set<int> offsets;
  90. // Insert entries to list
  91. for(ui32 i = 0; i < totalFiles; ++i)
  92. {
  93. char filename[40];
  94. reader.read(reinterpret_cast<ui8*>(filename), 40);
  95. ArchiveEntry entry;
  96. entry.name = filename;
  97. entry.offset = reader.readInt32();
  98. entry.compressedSize = 0;
  99. offsets.insert(entry.offset);
  100. entries[ResourceID(mountPoint + entry.name)] = entry;
  101. }
  102. offsets.insert((int)fileStream.getSize());
  103. // now when we know position of all files their sizes can be set correctly
  104. for (auto & entry : entries)
  105. {
  106. auto it = offsets.find(entry.second.offset);
  107. it++;
  108. entry.second.fullSize = *it - entry.second.offset;
  109. if(extractArchives)
  110. extractToFolder("VIDEO", fileStream, entry.second);
  111. }
  112. }
  113. void CArchiveLoader::initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream)
  114. {
  115. // Read count of total files
  116. CBinaryReader reader(&fileStream);
  117. fileStream.seek(0);
  118. ui32 totalFiles = reader.readUInt32();
  119. // Insert entries to list
  120. for(ui32 i = 0; i < totalFiles; ++i)
  121. {
  122. char filename[40];
  123. reader.read(reinterpret_cast<ui8*>(filename), 40);
  124. //for some reason entries in snd have format NAME\0WAVRUBBISH....
  125. //we need to replace first \0 with dot and take the 3 chars with extension (and drop the rest)
  126. ArchiveEntry entry;
  127. entry.name = filename; // till 1st \0
  128. entry.name += '.';
  129. entry.name += std::string(filename + entry.name.size(), 3);
  130. entry.offset = reader.readInt32();
  131. entry.fullSize = reader.readInt32();
  132. entry.compressedSize = 0;
  133. entries[ResourceID(mountPoint + entry.name)] = entry;
  134. if(extractArchives)
  135. extractToFolder("SOUND", fileStream, entry);
  136. }
  137. }
  138. std::unique_ptr<CInputStream> CArchiveLoader::load(const ResourceID & resourceName) const
  139. {
  140. assert(existsResource(resourceName));
  141. const ArchiveEntry & entry = entries.at(resourceName);
  142. if (entry.compressedSize != 0) //compressed data
  143. {
  144. auto fileStream = make_unique<CFileInputStream>(archive, entry.offset, entry.compressedSize);
  145. return make_unique<CCompressedStream>(std::move(fileStream), false, entry.fullSize);
  146. }
  147. else
  148. {
  149. return make_unique<CFileInputStream>(archive, entry.offset, entry.fullSize);
  150. }
  151. }
  152. bool CArchiveLoader::existsResource(const ResourceID & resourceName) const
  153. {
  154. return entries.count(resourceName) != 0;
  155. }
  156. std::string CArchiveLoader::getMountPoint() const
  157. {
  158. return mountPoint;
  159. }
  160. std::unordered_set<ResourceID> CArchiveLoader::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
  161. {
  162. std::unordered_set<ResourceID> foundID;
  163. for (auto & file : entries)
  164. {
  165. if (filter(file.first))
  166. foundID.insert(file.first);
  167. }
  168. return foundID;
  169. }
  170. void CArchiveLoader::extractToFolder(const std::string & outputSubFolder, CFileInputStream & fileStream, ArchiveEntry entry)
  171. {
  172. si64 currentPosition = fileStream.tell(); // save filestream position
  173. std::vector<ui8> data(entry.fullSize);
  174. fileStream.seek(entry.offset);
  175. fileStream.read(data.data(), entry.fullSize);
  176. bfs::path extractedFilePath = createExtractedFilePath(outputSubFolder, entry.name);
  177. // writeToOutputFile
  178. std::ofstream out(extractedFilePath.string(), std::ofstream::binary);
  179. out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
  180. out.write((char*)&data[0], entry.fullSize);
  181. fileStream.seek(currentPosition); // restore filestream position
  182. }
  183. void CArchiveLoader::extractToFolder(const std::string & outputSubFolder, const std::string & mountPoint, ArchiveEntry entry)
  184. {
  185. std::unique_ptr<CInputStream> inputStream = load(ResourceID(mountPoint + entry.name));
  186. std::vector<ui8> data(entry.fullSize);
  187. inputStream->read(data.data(), entry.fullSize);
  188. bfs::path extractedFilePath = createExtractedFilePath(outputSubFolder, entry.name);
  189. // writeToOutputFile
  190. std::ofstream out(extractedFilePath.string(), std::ofstream::binary);
  191. out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
  192. out.write((char*)&data[0], entry.fullSize);
  193. }
  194. bfs::path createExtractedFilePath(const std::string & outputSubFolder, const std::string & entryName)
  195. {
  196. bfs::path extractionFolderPath = VCMIDirs::get().userCachePath() / "extracted" / outputSubFolder;
  197. bfs::path extractedFilePath = extractionFolderPath / entryName;
  198. bfs::create_directories(extractionFolderPath);
  199. return extractedFilePath;
  200. }
  201. VCMI_LIB_NAMESPACE_END