123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- #include "BinaryData.h"
- #include "DatFile.h"
- #include "EasyLogging++/easylogging++.h"
- #include <DatOperationResult.h>
- #include <algorithm>
- #include <locale>
- #include <DatSubsystems/DatIO.h>
- #include <zconf.h>
- #ifdef WIN32
- #define fseek _fseeki64
- #define ftell _ftelli64
- #endif
- unsigned int RSHash(const std::string& str) {
- unsigned int b = 378551;
- unsigned int a = 63689;
- unsigned int hash = 0;
- for (char i : str) {
- hash = hash * a + i;
- a = a * b;
- }
- return hash;
- }
- extern "C++"
- {
- namespace LOTRO_DAT {
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Конструктор объекта модуля. Вызывается только из класса DatFile. Создаёт указатель но объект DatFile, но
- * не инициализирует модуль. Инизиализация происходит в функции Init
- * \param[in] datFile Указатель на объект управляющего класса DatFile
- */
- DatIO::DatIO(DatFile *datFile) : dat(datFile), file_handler_(nullptr), filename_("none"), actual_dat_size_(0) {
- }
- DatIO::~DatIO() {
- DeInit();
- }
- //------------------------------------------------//
- // INIT SECTION
- //------------------------------------------------//
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Инициализация модуля. Открывает файл на чтение и блокирует его для доступа из других программ.
- * Читает заголовочную информацию, необходимую для инициализации остальных модулей. Не должна вызываться где-либо,
- * кроме как из управляющего объекта DatFile.
- * \param[in] filename имя файла для открытия (включая путь)
- * \return Возвращает result = ERROR в случае, если не удалось открыть файл или файл некорректный
- */
- DatOperationResult<> DatIO::Init(const std::string &filename) {
- LOG(INFO) << "Initializing IO: " << filename;
- filename_ = filename;
- auto result = OpenDatFile();
- if (result.result == ERROR) {
- LOG(ERROR) << "Error in OpenDatFile: " << result.msg;
- DeInit();
- return result;
- }
- result = ReadSuperBlock();
- if (result.result == ERROR) {
- LOG(ERROR) << "Error in ReadSuperBlock: " << result.msg;
- DeInit();
- return result;
- }
- LOG(INFO) << "Successfull initializing IO: " << filename;
- return DatOperationResult<>(SUCCESS, "DatIO initialized successfully");
- }
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Открытие файла, имя которого записано в переменной file_handler_
- * \return Возвращает result = ERROR в случае, если не удалось открыть файл
- */
- DatOperationResult<> DatIO::OpenDatFile() {
- LOG(DEBUG) << "DatIO: Started opening DatFile";
- file_handler_ = fopen(filename_.c_str(), "r+b");
- if (file_handler_ == nullptr) {
- LOG(ERROR) << "DatIO: Unable to open file " << filename_ << ". Presumably - no file found...";
- return DatOperationResult<>(ERROR, std::string("Unable to locate and open file " + filename_));
- }
- fseek(file_handler_, 0, SEEK_END);
- actual_dat_size_ = ftell(file_handler_);
- fseek(file_handler_, 0, SEEK_SET);
- LOG(INFO) << "DatIO: file opened";
- return DatOperationResult<>(SUCCESS, std::string("Successfully opened file " + filename_));
- }
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Инициализация основных переменных файла, располагающихся в начале.
- *
- * Известные переменные (каждая по 4 байта):
- *
- * Офсет - описание переменной
- * =========
- * 0x100 - константа, одинаковая для всех .dat файлов
- * 0x140 - ещё одна константа, одинаковая для всех .dat файлов
- * 0x14C - Первая переменная версии .dat файла
- * 0x148 - размер .dat файла. Указывает на начало свободного места. Необязательно должен являться физическим концом
- * 0x150 - Вторая переменная версии dat файла
- * 0x154 - Положение журнала фрагментации (который представляет собой список свободных блоков в dat-файле, куда можно писать новые данные
- * 0x158 - Позиция окончания журнала фрагментации (?)
- * 0x15C - Кол-во записей в журнале фрагментации (?)
- * 0x160 - Офсет корневой папки
- * 0x19C - Свободное место в dat файле (?)
- *
- * \return Возвращает result = ERROR в случае, если константы не совпадают с требуемыми значениями
- */
- DatOperationResult<> DatIO::ReadSuperBlock() {
- LOG(INFO) << "DatIO: Started reading superblock";
- BinaryData data(1024);
- ReadData(data, 1024);
- constant1 = data.ToNumber<4>(0x100);
- constant2 = data.ToNumber<4>(0x140);
- version1 = data.ToNumber<4>(0x14C);
- file_size = data.ToNumber<4>(0x148);
- version2 = data.ToNumber<4>(0x150);
- fragmentation_journal_offset = data.ToNumber<4>(0x154);
- fragmentation_journal_end = data.ToNumber<4>(0x158);
- fragmentation_journal_size = data.ToNumber<4>(0x15C);
- root_directory_offset = data.ToNumber<4>(0x160);
- free_dat_size = data.ToNumber<4>(0x19C);
- if (constant1 != 0x4C5000)
- return DatOperationResult<>(ERROR, "Variable at position 0x100 is not equal to .dat file constant!");
- if (constant2 != 0x5442)
- return DatOperationResult<>(ERROR, "Variable at position 0x140 is not equal to .dat file constant!");
- LOG(INFO) << "DatIO: Superblock read successfully";
- return DatOperationResult<>(SUCCESS, "Superblock read successfully.");
- }
- //------------------------------------------------//
- // PUBLIC READ/WRITE SECTION
- //------------------------------------------------//
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Чтение бинарных данных из файла. Считает size байт dat файла, начиная с положения offset, и запишет их в
- * объект data, причём положение первого байта считанных данных будет равно значению data_offset
- *
- * \param[in, out] data Объект класса BinaryData, в который будут считаны данные
- * \param[in] size Количество байт, которые будут считаны
- * \param[in] offset Положение начала данных в dat файле, откуда считать
- * \param[in] data_offset Положение записи. Считанные данные будут записаны с отступом data_offset в data
- *
- * \return Возвращает result = ERROR в случае, если считать не удалось (неверные входные данные)
- */
- DatOperationResult<>
- DatIO::ReadData(BinaryData &data, long long size, long long offset, long long data_offset) const {
- if (file_handler_ == nullptr)
- return DatOperationResult<>(ERROR, "IOREADDATA: file handler is null pointer on reading data.");
- if (data_offset + size > data.size())
- return DatOperationResult<>(ERROR, "IOREADDATA: Parameters: offset, size are out of range.");
- if (offset + size > actual_dat_size_)
- return DatOperationResult<>(ERROR, "IOREADDATA: Reading more than DatFile size elapsed.");
- if (offset != ftell(file_handler_))
- fseek(file_handler_, offset, SEEK_SET);
- fread(data.data() + data_offset, unsigned(size), 1, file_handler_);
- return DatOperationResult<>(SUCCESS, "Read data successful.");
- }
- /*!
- * \author Gi1dor
- * \date 10.07.2018
- *
- * Проверка буфера перед записью новых size_to_write байт в конец файла. Если буфер мал - то дозаписывается
- * новый чистый блок размера MAX_EOF_BUFFER (константное поле класса DatIO);
- *
- * \param[in] size_to_write Размер данных в байтах, которые будут сейчас записаны в конец файла
- *
- */
- void DatIO::UpdateBufferIfNeeded(long long size_to_write) {
- BinaryData empty_buffer(std::max(MAX_EOF_BUFFER, unsigned(size_to_write)));
- fseek(file_handler_, 0, SEEK_END);
- fwrite(empty_buffer.data(), empty_buffer.size(), 1, file_handler_);
- actual_dat_size_ += empty_buffer.size();
- }
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Запись бинарных данных в dat файл. Запишет size байт данных из data, начиная с позиции data_offset,
- * в позицию offset .dat файла.
- *
- * \param[in] data Объект класса BinaryData, откуда будут взяты данные для записи
- * \param[in] size Количество байт для записи
- * \param[in] offset Положение начала данных в dat файле, куда записывать
- * \param[in] data_offset Положение начала данных в data, от которого будет взято size байт и записано в dat файл
- *
- * \return Возвращает result = ERROR в случае, если записать не удалось (неверные входные данные)
- */
- DatOperationResult<>
- DatIO::WriteData(const BinaryData &data, long long size, long long offset, long long data_offset) {
- if (file_handler_ == nullptr)
- return DatOperationResult<>(ERROR, "IOWRITEDATA: file handler is null pointer on writing data.");
- if (data_offset + size > data.size())
- return DatOperationResult<>(ERROR, "IOWRITEDATA: writing more than BinaryData size.");
- if (offset + size > actual_dat_size_)
- UpdateBufferIfNeeded(offset + size - actual_dat_size_);
- if (offset != ftell(file_handler_))
- fseek(file_handler_, offset, SEEK_SET);
- fwrite(data.data() + data_offset, unsigned(size), 1, file_handler_);
- return DatOperationResult<>(SUCCESS, "Data writing successful.");
- }
- //------------------------------------------------//
- // DEINIT SECTION
- //------------------------------------------------//
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Деинициализация модуля. Не должна вызываться где-либо, кроме как из управляющего объекта DatFile.
- */
- DatOperationResult<> DatIO::DeInit() {
- if (!dat->Initialized()) {
- ClearData();
- return DatOperationResult<>(SUCCESS);
- }
- if (file_handler_ != nullptr)
- fclose(file_handler_);
- truncate64(filename_.c_str(), actual_dat_size_);
- filename_ = "none";
- file_handler_ = nullptr;
- constant1 = 0;
- constant2 = 0;
- file_size = 0;
- version1 = 0;
- version2 = 0;
- fragmentation_journal_size = 0;
- fragmentation_journal_end = 0;
- root_directory_offset = 0;
- fragmentation_journal_offset = 0;
- return DatOperationResult<>(SUCCESS, "File deinitialisation successfull");
- }
- DatOperationResult<> DatIO::ModifyFragmentationJournal() {
- if (fragmentation_journal_size == 0)
- return DatOperationResult<>(SUCCESS,
- "DatIO: Fragmentation journal is empty. Nothing to do.");
- LOG(DEBUG) << "Modifying fragmentation journal";
- BinaryData data(4);
- ReadData(data, 4, fragmentation_journal_offset + 8 * fragmentation_journal_size);
- LOG(INFO) << "FREE_SIZE BLOCK = " << data.ToNumber<4>(0);
- long long free_size = data.ToNumber<4>(0);
- long long free_offset = file_size;
- BinaryData nulldata = BinaryData(unsigned(free_size));
- WriteData(nulldata, nulldata.size(), file_size);
- file_size += nulldata.size();
- WriteData(BinaryData::FromNumber<4>(free_size), 4,
- fragmentation_journal_offset + 8 * fragmentation_journal_size);
- WriteData(BinaryData::FromNumber<4>(free_offset), 4,
- fragmentation_journal_offset + 8 * fragmentation_journal_size + 4);
- //nulldata = BinaryData(8);
- //WriteData(nulldata, nulldata.size(), fragmentation_journal_offset + 16);
- LOG(DEBUG) << "Finished modifying fragmentation journal";
- return DatOperationResult<>(SUCCESS, "Fragmentation journal patched successfully!");
- }
- DatOperationResult<> DatIO::UpdateHeader() {
- LOG(DEBUG) << "Updating header";
- WriteData(BinaryData::FromNumber<4>(constant1), 4, 0x100);
- WriteData(BinaryData::FromNumber<4>(constant2), 4, 0x140);
- //WriteData(BinaryData::FromNumber<4>( 0 ), 4, 0x144);
- WriteData(BinaryData::FromNumber<4>(file_size), 4, 0x148);
- WriteData(BinaryData::FromNumber<4>(version1), 4, 0x14C);
- WriteData(BinaryData::FromNumber<4>(version2), 4, 0x150);
- WriteData(BinaryData::FromNumber<4>(fragmentation_journal_offset), 4, 0x154);
- WriteData(BinaryData::FromNumber<4>(fragmentation_journal_end), 4, 0x158);
- WriteData(BinaryData::FromNumber<4>(fragmentation_journal_size), 4, 0x15C);
- WriteData(BinaryData::FromNumber<4>(root_directory_offset), 4, 0x160);
- WriteData(BinaryData::FromNumber<4>(free_dat_size), 4, 0x19C);
- LOG(DEBUG) << "Finished updating header";
- return DatOperationResult<>(SUCCESS, "File header patched successfully");;
- }
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Возвращает размер dat файла в байтах (может отличаться от значения внутренней переменной file_size dat файла
- */
- DatOperationResult<long long> DatIO::GetActualDatSize() {
- return DatOperationResult<long long>(actual_dat_size_, SUCCESS);
- }
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Возвращает имя dat файла, которое было передано при инициализации
- */
- DatOperationResult<std::string> DatIO::GetFilename() {
- if (filename_ == "none")
- return DatOperationResult<std::string>("", ERROR, "DATIOGETFILENAME: not initialised");
- return DatOperationResult<std::string>(filename_, SUCCESS);
- }
- /*!
- * \author Gi1dor
- * \date 30.06.2018
- * Выводит информацию о состоянии модуля в файл
- * \param[in] file указатель на объект файла, в конец которого будет напечатана информация
- * \warning Файл file должен быть открыт для записи. После завершения работы функции файл остаётся открытым
- */
- void DatIO::PrintInformaion(FILE *file) {
- if (!file)
- return;
- fprintf(file, "========== IO info ==========\n");
- fprintf(file, "Filename = %s\n", filename_.c_str());
- fprintf(file, "File header data:\n");
- fprintf(file, "constant1 = %lld\n", constant1);
- fprintf(file, "constant2 = %lld\n", constant2);
- fprintf(file, "file_size = %lld\n", file_size);
- fprintf(file, "version1 = %lld\n", version1);
- fprintf(file, "version2 = %lld\n", version2);
- fprintf(file, "fragment_journal_offset = %lld\n", fragmentation_journal_offset);
- fprintf(file, "fragment_journal_end = %lld\n", fragmentation_journal_end);
- fprintf(file, "fragment_journal_size = %lld\n", fragmentation_journal_size);
- fprintf(file, "root_dir_offset = %lld\n", root_directory_offset);
- fprintf(file, "free_dat_size = %lld\n", free_dat_size);
- fprintf(file, "DatLibrary: EOF buffer size constant = %d\n", MAX_EOF_BUFFER);
- }
- unsigned int DatIO::getHeaderHash() {
- std::string values =
- std::to_string(constant1) +
- std::to_string(constant2) +
- std::to_string(file_size) +
- std::to_string(version1) +
- std::to_string(version2) +
- std::to_string(fragmentation_journal_offset) +
- std::to_string(fragmentation_journal_end) +
- std::to_string(fragmentation_journal_size) +
- std::to_string(root_directory_offset) +
- std::to_string(free_dat_size);
- return RSHash(values);
- }
- void DatIO::ClearData() {
- constant1 = -1;
- constant2 = -1;
- file_size = -1;
- version1 = -1;
- version2 = -1;
- fragmentation_journal_size = -1;
- fragmentation_journal_end = -1;
- root_directory_offset = -1;
- fragmentation_journal_offset = -1;
- free_dat_size = -1;
- actual_dat_size_ = -1;
- }
- }
- }
|