Clone of Bael'Zharon's Respite @ https://github.com/boardwalk/bzr

DatFile.cpp 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * Bael'Zharon's Respite
  3. * Copyright (C) 2014 Daniel Skorupski
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include "DatFile.h"
  19. #include <algorithm>
  20. static const uint32_t kHeaderMagicNumber = 0x5442; // 'BT\0\0'
  21. static const int kMaxEntries = 61;
  22. PACK(struct DiskFileInfo
  23. {
  24. uint32_t magicNumber;
  25. uint32_t blockSize;
  26. uint32_t fileSize;
  27. uint32_t dataSet_lm;
  28. uint32_t dataSubset_lm;
  29. uint32_t firstFree;
  30. uint32_t finalFree;
  31. uint32_t freeBlockCount;
  32. uint32_t rootPosition;
  33. uint32_t youngLRU_lm;
  34. uint32_t oldLRU_lm;
  35. uint8_t useLRU_fm;
  36. uint8_t pad1;
  37. uint8_t pad2;
  38. uint8_t pad3;
  39. uint32_t masterMapId;
  40. uint32_t englishPackVersion;
  41. uint32_t gamePackVersion;
  42. });
  43. PACK(struct DiskHeaderBlock
  44. {
  45. uint8_t acVersionStr[256];
  46. uint8_t acTransactionRecord[64];
  47. DiskFileInfo fileInfo;
  48. });
  49. PACK(struct BTEntry
  50. {
  51. uint32_t flags; // comp, resv, ver?
  52. uint32_t id;
  53. uint32_t position;
  54. uint32_t size;
  55. uint32_t timestamp;
  56. uint32_t version;
  57. });
  58. PACK(struct BTNode
  59. {
  60. uint32_t nextNode[kMaxEntries + 1];
  61. uint32_t numEntries;
  62. BTEntry entries[kMaxEntries];
  63. });
  64. DatFile::DatFile(const string& path) :
  65. fs_(path.c_str(), ios_base::in|ios_base::binary)
  66. {
  67. DiskHeaderBlock headerBlock;
  68. fs_.read(reinterpret_cast<char*>(&headerBlock), sizeof(headerBlock));
  69. if(!fs_.good())
  70. {
  71. throw runtime_error("Could not read header block");
  72. }
  73. if(headerBlock.fileInfo.magicNumber != kHeaderMagicNumber)
  74. {
  75. throw runtime_error("Header block has bad magic number");
  76. }
  77. blockSize_ = headerBlock.fileInfo.blockSize - sizeof(uint32_t); // exclude next block position
  78. rootPosition_ = headerBlock.fileInfo.rootPosition;
  79. }
  80. vector<uint8_t> DatFile::read(uint32_t id) const
  81. {
  82. uint32_t position = rootPosition_;
  83. for(;;)
  84. {
  85. vector<uint8_t> nodeData = readBlocks(position, sizeof(BTNode));
  86. BTNode* node = reinterpret_cast<BTNode*>(nodeData.data());
  87. if(node->numEntries > kMaxEntries)
  88. {
  89. throw runtime_error("Node has bad entry count");
  90. }
  91. uint32_t i = 0;
  92. for(; i < node->numEntries; i++)
  93. {
  94. if(id <= node->entries[i].id)
  95. {
  96. break;
  97. }
  98. }
  99. if(i < node->numEntries && id == node->entries[i].id)
  100. {
  101. return readBlocks(node->entries[i].position, node->entries[i].size);
  102. }
  103. if(node->nextNode[0] == 0)
  104. {
  105. return vector<uint8_t>();
  106. }
  107. position = node->nextNode[i];
  108. }
  109. }
  110. vector<uint32_t> DatFile::list() const
  111. {
  112. vector<uint32_t> result;
  113. listDir(rootPosition_, result);
  114. return result;
  115. }
  116. vector<uint8_t> DatFile::readBlocks(uint32_t position, size_t size) const
  117. {
  118. vector<uint8_t> result(size);
  119. size_t offset = 0;
  120. while(offset < size)
  121. {
  122. if(position == 0)
  123. {
  124. throw runtime_error("Not enough blocks for resource");
  125. }
  126. size_t readSize = min<size_t>(size - offset, blockSize_);
  127. fs_.seekg(position);
  128. fs_.read(reinterpret_cast<char*>(&position), sizeof(position));
  129. fs_.read(reinterpret_cast<char*>(result.data() + offset), readSize);
  130. if(!fs_.good())
  131. {
  132. throw runtime_error("Failed to read block");
  133. }
  134. offset += readSize;
  135. }
  136. return result;
  137. }
  138. void DatFile::listDir(uint32_t position, vector<uint32_t>& result) const
  139. {
  140. vector<uint8_t> nodeData = readBlocks(position, sizeof(BTNode));
  141. BTNode* node = reinterpret_cast<BTNode*>(nodeData.data());
  142. if(node->numEntries > kMaxEntries)
  143. {
  144. throw runtime_error("Node has bad entry count");
  145. }
  146. for(uint32_t i = 0; i < node->numEntries; i++)
  147. {
  148. if(node->nextNode[0] != 0)
  149. {
  150. listDir(node->nextNode[i], result);
  151. }
  152. result.push_back(node->entries[i].id);
  153. }
  154. if(node->nextNode[0] != 0)
  155. {
  156. listDir(node->nextNode[node->numEntries], result);
  157. }
  158. }