DatIO.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. #include "BinaryData.h"
  2. #include "DatFile.h"
  3. #include "EasyLogging++/easylogging++.h"
  4. #include <DatOperationResult.h>
  5. #include <algorithm>
  6. #include <locale>
  7. #include <DatSubsystems/DatIO.h>
  8. #include <zconf.h>
  9. #ifdef WIN32
  10. #define fseek _fseeki64
  11. #define ftell _ftelli64
  12. #endif
  13. unsigned int RSHash(const std::string& str) {
  14. unsigned int b = 378551;
  15. unsigned int a = 63689;
  16. unsigned int hash = 0;
  17. for (char i : str) {
  18. hash = hash * a + i;
  19. a = a * b;
  20. }
  21. return hash;
  22. }
  23. extern "C++"
  24. {
  25. namespace LOTRO_DAT {
  26. /*!
  27. * \author Gi1dor
  28. * \date 30.06.2018
  29. * Конструктор объекта модуля. Вызывается только из класса DatFile. Создаёт указатель но объект DatFile, но
  30. * не инициализирует модуль. Инизиализация происходит в функции Init
  31. * \param[in] datFile Указатель на объект управляющего класса DatFile
  32. */
  33. DatIO::DatIO(DatFile *datFile) : dat(datFile), file_handler_(nullptr), filename_("none"), actual_dat_size_(0) {
  34. }
  35. DatIO::~DatIO() {
  36. DeInit();
  37. }
  38. //------------------------------------------------//
  39. // INIT SECTION
  40. //------------------------------------------------//
  41. /*!
  42. * \author Gi1dor
  43. * \date 30.06.2018
  44. * Инициализация модуля. Открывает файл на чтение и блокирует его для доступа из других программ.
  45. * Читает заголовочную информацию, необходимую для инициализации остальных модулей. Не должна вызываться где-либо,
  46. * кроме как из управляющего объекта DatFile.
  47. * \param[in] filename имя файла для открытия (включая путь)
  48. * \return Возвращает result = ERROR в случае, если не удалось открыть файл или файл некорректный
  49. */
  50. DatOperationResult<> DatIO::Init(const std::string &filename) {
  51. LOG(INFO) << "Initializing IO: " << filename;
  52. filename_ = filename;
  53. auto result = OpenDatFile();
  54. if (result.result == ERROR) {
  55. LOG(ERROR) << "Error in OpenDatFile: " << result.msg;
  56. DeInit();
  57. return result;
  58. }
  59. result = ReadSuperBlock();
  60. if (result.result == ERROR) {
  61. LOG(ERROR) << "Error in ReadSuperBlock: " << result.msg;
  62. DeInit();
  63. return result;
  64. }
  65. LOG(INFO) << "Successfull initializing IO: " << filename;
  66. return DatOperationResult<>(SUCCESS, "DatIO initialized successfully");
  67. }
  68. /*!
  69. * \author Gi1dor
  70. * \date 30.06.2018
  71. * Открытие файла, имя которого записано в переменной file_handler_
  72. * \return Возвращает result = ERROR в случае, если не удалось открыть файл
  73. */
  74. DatOperationResult<> DatIO::OpenDatFile() {
  75. LOG(DEBUG) << "DatIO: Started opening DatFile";
  76. file_handler_ = fopen(filename_.c_str(), "r+b");
  77. if (file_handler_ == nullptr) {
  78. LOG(ERROR) << "DatIO: Unable to open file " << filename_ << ". Presumably - no file found...";
  79. return DatOperationResult<>(ERROR, std::string("Unable to locate and open file " + filename_));
  80. }
  81. fseek(file_handler_, 0, SEEK_END);
  82. actual_dat_size_ = ftell(file_handler_);
  83. fseek(file_handler_, 0, SEEK_SET);
  84. LOG(INFO) << "DatIO: file opened";
  85. return DatOperationResult<>(SUCCESS, std::string("Successfully opened file " + filename_));
  86. }
  87. /*!
  88. * \author Gi1dor
  89. * \date 30.06.2018
  90. * Инициализация основных переменных файла, располагающихся в начале.
  91. *
  92. * Известные переменные (каждая по 4 байта):
  93. *
  94. * Офсет - описание переменной
  95. * =========
  96. * 0x100 - константа, одинаковая для всех .dat файлов
  97. * 0x140 - ещё одна константа, одинаковая для всех .dat файлов
  98. * 0x14C - Первая переменная версии .dat файла
  99. * 0x148 - размер .dat файла. Указывает на начало свободного места. Необязательно должен являться физическим концом
  100. * 0x150 - Вторая переменная версии dat файла
  101. * 0x154 - Положение журнала фрагментации (который представляет собой список свободных блоков в dat-файле, куда можно писать новые данные
  102. * 0x158 - Позиция окончания журнала фрагментации (?)
  103. * 0x15C - Кол-во записей в журнале фрагментации (?)
  104. * 0x160 - Офсет корневой папки
  105. * 0x19C - Свободное место в dat файле (?)
  106. *
  107. * \return Возвращает result = ERROR в случае, если константы не совпадают с требуемыми значениями
  108. */
  109. DatOperationResult<> DatIO::ReadSuperBlock() {
  110. LOG(INFO) << "DatIO: Started reading superblock";
  111. BinaryData data(1024);
  112. ReadData(data, 1024);
  113. constant1 = data.ToNumber<4>(0x100);
  114. constant2 = data.ToNumber<4>(0x140);
  115. version1 = data.ToNumber<4>(0x14C);
  116. file_size = data.ToNumber<4>(0x148);
  117. version2 = data.ToNumber<4>(0x150);
  118. fragmentation_journal_offset = data.ToNumber<4>(0x154);
  119. fragmentation_journal_end = data.ToNumber<4>(0x158);
  120. fragmentation_journal_size = data.ToNumber<4>(0x15C);
  121. root_directory_offset = data.ToNumber<4>(0x160);
  122. free_dat_size = data.ToNumber<4>(0x19C);
  123. if (constant1 != 0x4C5000)
  124. return DatOperationResult<>(ERROR, "Variable at position 0x100 is not equal to .dat file constant!");
  125. if (constant2 != 0x5442)
  126. return DatOperationResult<>(ERROR, "Variable at position 0x140 is not equal to .dat file constant!");
  127. LOG(INFO) << "DatIO: Superblock read successfully";
  128. return DatOperationResult<>(SUCCESS, "Superblock read successfully.");
  129. }
  130. //------------------------------------------------//
  131. // PUBLIC READ/WRITE SECTION
  132. //------------------------------------------------//
  133. /*!
  134. * \author Gi1dor
  135. * \date 30.06.2018
  136. * Чтение бинарных данных из файла. Считает size байт dat файла, начиная с положения offset, и запишет их в
  137. * объект data, причём положение первого байта считанных данных будет равно значению data_offset
  138. *
  139. * \param[in, out] data Объект класса BinaryData, в который будут считаны данные
  140. * \param[in] size Количество байт, которые будут считаны
  141. * \param[in] offset Положение начала данных в dat файле, откуда считать
  142. * \param[in] data_offset Положение записи. Считанные данные будут записаны с отступом data_offset в data
  143. *
  144. * \return Возвращает result = ERROR в случае, если считать не удалось (неверные входные данные)
  145. */
  146. DatOperationResult<>
  147. DatIO::ReadData(BinaryData &data, long long size, long long offset, long long data_offset) const {
  148. if (file_handler_ == nullptr)
  149. return DatOperationResult<>(ERROR, "IOREADDATA: file handler is null pointer on reading data.");
  150. if (data_offset + size > data.size())
  151. return DatOperationResult<>(ERROR, "IOREADDATA: Parameters: offset, size are out of range.");
  152. if (offset + size > actual_dat_size_)
  153. return DatOperationResult<>(ERROR, "IOREADDATA: Reading more than DatFile size elapsed.");
  154. if (offset != ftell(file_handler_))
  155. fseek(file_handler_, offset, SEEK_SET);
  156. fread(data.data() + data_offset, unsigned(size), 1, file_handler_);
  157. return DatOperationResult<>(SUCCESS, "Read data successful.");
  158. }
  159. /*!
  160. * \author Gi1dor
  161. * \date 10.07.2018
  162. *
  163. * Проверка буфера перед записью новых size_to_write байт в конец файла. Если буфер мал - то дозаписывается
  164. * новый чистый блок размера MAX_EOF_BUFFER (константное поле класса DatIO);
  165. *
  166. * \param[in] size_to_write Размер данных в байтах, которые будут сейчас записаны в конец файла
  167. *
  168. */
  169. void DatIO::UpdateBufferIfNeeded(long long size_to_write) {
  170. BinaryData empty_buffer(std::max(MAX_EOF_BUFFER, unsigned(size_to_write)));
  171. fseek(file_handler_, 0, SEEK_END);
  172. fwrite(empty_buffer.data(), empty_buffer.size(), 1, file_handler_);
  173. actual_dat_size_ += empty_buffer.size();
  174. }
  175. /*!
  176. * \author Gi1dor
  177. * \date 30.06.2018
  178. * Запись бинарных данных в dat файл. Запишет size байт данных из data, начиная с позиции data_offset,
  179. * в позицию offset .dat файла.
  180. *
  181. * \param[in] data Объект класса BinaryData, откуда будут взяты данные для записи
  182. * \param[in] size Количество байт для записи
  183. * \param[in] offset Положение начала данных в dat файле, куда записывать
  184. * \param[in] data_offset Положение начала данных в data, от которого будет взято size байт и записано в dat файл
  185. *
  186. * \return Возвращает result = ERROR в случае, если записать не удалось (неверные входные данные)
  187. */
  188. DatOperationResult<>
  189. DatIO::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset) {
  190. if (file_handler_ == nullptr)
  191. return DatOperationResult<>(ERROR, "IOWRITEDATA: file handler is null pointer on writing data.");
  192. if (data_offset + size > data.size())
  193. return DatOperationResult<>(ERROR, "IOWRITEDATA: writing more than BinaryData size.");
  194. if (offset + size > actual_dat_size_)
  195. UpdateBufferIfNeeded(offset + size - actual_dat_size_);
  196. if (offset != ftell(file_handler_))
  197. fseek(file_handler_, offset, SEEK_SET);
  198. fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
  199. return DatOperationResult<>(SUCCESS, "Data writing successful.");
  200. }
  201. //------------------------------------------------//
  202. // DEINIT SECTION
  203. //------------------------------------------------//
  204. /*!
  205. * \author Gi1dor
  206. * \date 30.06.2018
  207. * Деинициализация модуля. Не должна вызываться где-либо, кроме как из управляющего объекта DatFile.
  208. */
  209. DatOperationResult<> DatIO::DeInit() {
  210. if (!dat->Initialized()) {
  211. ClearData();
  212. return DatOperationResult<>(SUCCESS);
  213. }
  214. if (file_handler_ != nullptr)
  215. fclose(file_handler_);
  216. truncate64(filename_.c_str(), actual_dat_size_);
  217. filename_ = "none";
  218. file_handler_ = nullptr;
  219. constant1 = 0;
  220. constant2 = 0;
  221. file_size = 0;
  222. version1 = 0;
  223. version2 = 0;
  224. fragmentation_journal_size = 0;
  225. fragmentation_journal_end = 0;
  226. root_directory_offset = 0;
  227. fragmentation_journal_offset = 0;
  228. return DatOperationResult<>(SUCCESS, "File deinitialisation successfull");
  229. }
  230. DatOperationResult<> DatIO::ModifyFragmentationJournal() {
  231. if (fragmentation_journal_size == 0)
  232. return DatOperationResult<>(SUCCESS,
  233. "DatIO: Fragmentation journal is empty. Nothing to do.");
  234. LOG(DEBUG) << "Modifying fragmentation journal";
  235. BinaryData data(4);
  236. ReadData(data, 4, fragmentation_journal_offset + 8 * fragmentation_journal_size);
  237. LOG(INFO) << "FREE_SIZE BLOCK = " << data.ToNumber<4>(0);
  238. long long free_size = data.ToNumber<4>(0);
  239. long long free_offset = file_size;
  240. BinaryData nulldata = BinaryData(unsigned(free_size));
  241. WriteData(nulldata, nulldata.size(), file_size);
  242. file_size += nulldata.size();
  243. WriteData(BinaryData::FromNumber<4>(free_size), 4,
  244. fragmentation_journal_offset + 8 * fragmentation_journal_size);
  245. WriteData(BinaryData::FromNumber<4>(free_offset), 4,
  246. fragmentation_journal_offset + 8 * fragmentation_journal_size + 4);
  247. //nulldata = BinaryData(8);
  248. //WriteData(nulldata, nulldata.size(), fragmentation_journal_offset + 16);
  249. LOG(DEBUG) << "Finished modifying fragmentation journal";
  250. return DatOperationResult<>(SUCCESS, "Fragmentation journal patched successfully!");
  251. }
  252. DatOperationResult<> DatIO::UpdateHeader() {
  253. LOG(DEBUG) << "Updating header";
  254. WriteData(BinaryData::FromNumber<4>(constant1), 4, 0x100);
  255. WriteData(BinaryData::FromNumber<4>(constant2), 4, 0x140);
  256. //WriteData(BinaryData::FromNumber<4>( 0 ), 4, 0x144);
  257. WriteData(BinaryData::FromNumber<4>(file_size), 4, 0x148);
  258. WriteData(BinaryData::FromNumber<4>(version1), 4, 0x14C);
  259. WriteData(BinaryData::FromNumber<4>(version2), 4, 0x150);
  260. WriteData(BinaryData::FromNumber<4>(fragmentation_journal_offset), 4, 0x154);
  261. WriteData(BinaryData::FromNumber<4>(fragmentation_journal_end), 4, 0x158);
  262. WriteData(BinaryData::FromNumber<4>(fragmentation_journal_size), 4, 0x15C);
  263. WriteData(BinaryData::FromNumber<4>(root_directory_offset), 4, 0x160);
  264. WriteData(BinaryData::FromNumber<4>(free_dat_size), 4, 0x19C);
  265. LOG(DEBUG) << "Finished updating header";
  266. return DatOperationResult<>(SUCCESS, "File header patched successfully");;
  267. }
  268. /*!
  269. * \author Gi1dor
  270. * \date 30.06.2018
  271. * Возвращает размер dat файла в байтах (может отличаться от значения внутренней переменной file_size dat файла
  272. */
  273. DatOperationResult<long long> DatIO::GetActualDatSize() {
  274. return DatOperationResult<long long>(actual_dat_size_, SUCCESS);
  275. }
  276. /*!
  277. * \author Gi1dor
  278. * \date 30.06.2018
  279. * Возвращает имя dat файла, которое было передано при инициализации
  280. */
  281. DatOperationResult<std::string> DatIO::GetFilename() {
  282. if (filename_ == "none")
  283. return DatOperationResult<std::string>("", ERROR, "DATIOGETFILENAME: not initialised");
  284. return DatOperationResult<std::string>(filename_, SUCCESS);
  285. }
  286. /*!
  287. * \author Gi1dor
  288. * \date 30.06.2018
  289. * Выводит информацию о состоянии модуля в файл
  290. * \param[in] file указатель на объект файла, в конец которого будет напечатана информация
  291. * \warning Файл file должен быть открыт для записи. После завершения работы функции файл остаётся открытым
  292. */
  293. void DatIO::PrintInformaion(FILE *file) {
  294. if (!file)
  295. return;
  296. fprintf(file, "========== IO info ==========\n");
  297. fprintf(file, "Filename = %s\n", filename_.c_str());
  298. fprintf(file, "File header data:\n");
  299. fprintf(file, "constant1 = %lld\n", constant1);
  300. fprintf(file, "constant2 = %lld\n", constant2);
  301. fprintf(file, "file_size = %lld\n", file_size);
  302. fprintf(file, "version1 = %lld\n", version1);
  303. fprintf(file, "version2 = %lld\n", version2);
  304. fprintf(file, "fragment_journal_offset = %lld\n", fragmentation_journal_offset);
  305. fprintf(file, "fragment_journal_end = %lld\n", fragmentation_journal_end);
  306. fprintf(file, "fragment_journal_size = %lld\n", fragmentation_journal_size);
  307. fprintf(file, "root_dir_offset = %lld\n", root_directory_offset);
  308. fprintf(file, "free_dat_size = %lld\n", free_dat_size);
  309. fprintf(file, "DatLibrary: EOF buffer size constant = %d\n", MAX_EOF_BUFFER);
  310. }
  311. unsigned int DatIO::GetHeaderHash() {
  312. std::string values =
  313. std::to_string(constant1) +
  314. std::to_string(constant2) +
  315. std::to_string(file_size) +
  316. std::to_string(version1) +
  317. std::to_string(version2) +
  318. std::to_string(fragmentation_journal_offset) +
  319. std::to_string(fragmentation_journal_end) +
  320. std::to_string(fragmentation_journal_size) +
  321. std::to_string(root_directory_offset) +
  322. std::to_string(free_dat_size);
  323. return RSHash(values);
  324. }
  325. void DatIO::ClearData() {
  326. constant1 = -1;
  327. constant2 = -1;
  328. file_size = -1;
  329. version1 = -1;
  330. version2 = -1;
  331. fragmentation_journal_size = -1;
  332. fragmentation_journal_end = -1;
  333. root_directory_offset = -1;
  334. fragmentation_journal_offset = -1;
  335. free_dat_size = -1;
  336. actual_dat_size_ = -1;
  337. }
  338. }
  339. }