CArchiveLoader.cpp 7.9 KB

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