DatIO.cpp 15 KB

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