CPackage.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. #include "CPackage.h"
  2. #include "CAssetLoadException.h"
  3. #include "CPackageLoadException.h"
  4. #include <utility>
  5. #include <sstream>
  6. using namespace WallpaperEngine::Assets;
  7. class CPackageEntry
  8. {
  9. public:
  10. CPackageEntry (std::string filename, uint32_t offset, uint32_t length) :
  11. filename (std::move(filename)),
  12. offset (offset),
  13. length (length) { }
  14. std::string filename;
  15. uint32_t offset;
  16. uint32_t length;
  17. };
  18. CPackage::CPackage (const std::string& path) :
  19. m_path (path),
  20. m_contents ()
  21. {
  22. this->init ();
  23. }
  24. CPackage::~CPackage()
  25. {
  26. }
  27. const void* CPackage::readFile (std::string filename, uint32_t* length) const
  28. {
  29. auto it = this->m_contents.find (filename);
  30. if (it == this->m_contents.end ())
  31. throw CAssetLoadException(filename, "Cannot find the file in the package");
  32. // set file length if required
  33. if (length != nullptr)
  34. *length = (*it).second.length;
  35. return (*it).second.address;
  36. }
  37. void CPackage::init ()
  38. {
  39. FILE* fp = fopen (this->m_path.c_str (), "rb+");
  40. if (fp == nullptr)
  41. throw CPackageLoadException (this->m_path, std::to_string (errno));
  42. // first validate header
  43. this->validateHeader (fp);
  44. // header is okay, load everything into memory
  45. this->loadFiles (fp);
  46. fclose (fp);
  47. }
  48. char* CPackage::readSizedString (FILE* fp)
  49. {
  50. unsigned int length = 0;
  51. if (fread (&length, sizeof (unsigned int), 1, fp) != 1)
  52. throw std::runtime_error ("Cannot read enough bytes from file");
  53. // account for 0 termination of the string
  54. length ++;
  55. char* pointer = new char [length];
  56. memset (pointer, 0, length);
  57. // read only the string bytes so the last one in the memory is 0
  58. length --;
  59. // read data from file
  60. if (fread (pointer, sizeof (char), length, fp) != length)
  61. throw std::runtime_error ("Cannot read package version from file");
  62. return pointer;
  63. }
  64. uint32_t CPackage::readInteger (FILE* fp)
  65. {
  66. uint32_t output;
  67. if (fread (&output, sizeof (uint32_t), 1, fp) != 1)
  68. throw std::runtime_error ("Cannot read integer value from file");
  69. return output;
  70. }
  71. void CPackage::validateHeader (FILE* fp)
  72. {
  73. char* pointer = this->readSizedString (fp);
  74. // finally validate the header version
  75. if (strcmp ("PKGV0001", pointer) != 0 &&
  76. strcmp ("PKGV0002", pointer) != 0 &&
  77. strcmp ("PKGV0003", pointer) != 0 &&
  78. strcmp ("PKGV0004", pointer) != 0 &&
  79. strcmp ("PKGV0005", pointer) != 0 &&
  80. strcmp ("PKGV0006", pointer) != 0 &&
  81. strcmp ("PKGV0007", pointer) != 0 &&
  82. strcmp ("PKGV0008", pointer) != 0 &&
  83. strcmp ("PKGV0009", pointer) != 0 &&
  84. strcmp ("PKGV0010", pointer) != 0 &&
  85. strcmp ("PKGV0012", pointer) != 0 &&
  86. strcmp ("PKGV0013", pointer) != 0 &&
  87. strcmp ("PKGV0014", pointer) != 0 &&
  88. strcmp ("PKGV0015", pointer) != 0 &&
  89. strcmp ("PKGV0016", pointer) != 0 &&
  90. strcmp ("PKGV0017", pointer) != 0 &&
  91. strcmp ("PKGV0018", pointer) != 0)
  92. {
  93. std::stringstream msg;
  94. msg << "Unsupported package version: " << pointer;
  95. delete[] pointer;
  96. throw std::runtime_error (msg.str ());
  97. }
  98. // free memory
  99. delete[] pointer;
  100. }
  101. void CPackage::loadFiles (FILE* fp)
  102. {
  103. uint32_t count = this->readInteger (fp);
  104. std::vector<CPackageEntry> list;
  105. for (uint32_t index = 0; index < count; index ++)
  106. {
  107. // first read the filename
  108. char* filename = this->readSizedString (fp);
  109. uint32_t offset = this->readInteger (fp);
  110. uint32_t length = this->readInteger (fp);
  111. // add the file to the list
  112. list.emplace_back(filename, offset, length);
  113. // only free filename, the file's contents are stored in a map for a later use
  114. delete[] filename;
  115. }
  116. // get current baseOffset, this is where the files start
  117. long baseOffset = ftell (fp);
  118. // read file contents now
  119. auto cur = list.begin ();
  120. auto end = list.end ();
  121. for (; cur != end; cur ++)
  122. {
  123. long offset = (*cur).offset + baseOffset;
  124. // with all the data we can jump to the offset and read the content
  125. if (fseek (fp, offset, SEEK_SET) != 0)
  126. throw std::runtime_error ("Cannot find file in package");
  127. // allocate memory for the file's contents and read it from the file
  128. char* fileContents = new char [(*cur).length];
  129. if (fread (fileContents, (*cur).length, 1, fp) != 1)
  130. {
  131. delete[] fileContents;
  132. throw std::runtime_error ("Cannot read file contents from package");
  133. }
  134. // add the file to the map
  135. this->m_contents.insert (std::make_pair <std::string, CFileEntry> (
  136. std::move((*cur).filename),
  137. CFileEntry (fileContents, (*cur).length))
  138. );
  139. }
  140. }