Browse Source

LotroDat-6: Implemented Callback functions feature, improved DatStatus, added callbacks to examples

Ivan Arkhipov 5 years ago
parent
commit
174342f3c5

+ 48 - 7
include/DatSubsystems/DatStatus.h

@@ -33,8 +33,49 @@ namespace LOTRO_DAT {
             E_FREE
             E_FREE
         };
         };
 
 
-        using Callback = std::function<void(DAT_STATUS, double, unsigned long long, unsigned long long, std::string)>; 
-        // Callback arguments: status, percentage, finished_items, total_items, debug message 
+        struct ProgressInfo {
+            DAT_STATUS status;
+            double percentage;
+            unsigned long long finished_parts;
+            unsigned long long total_parts;
+        };
+
+        using Callback = std::function<void(ProgressInfo)>; 
+        
+        struct CallbackStorage{
+        public:
+            CallbackStorage(Callback function_handle) {
+                func_ = function_handle;
+                handler_ = free_handler_++;
+            }
+
+            CallbackStorage(unsigned long long handler) {
+                func_ = nullptr;
+                handler_ = handler;
+            }
+
+            unsigned long long GetHandler() const {
+                return handler_;
+            }
+
+            bool operator<(const CallbackStorage& other) const {
+                return handler_ < other.handler_;
+            }
+
+            bool operator==(const CallbackStorage& other) const {
+                return handler_ == other.handler_;
+            }
+            
+            template<class ... Types>
+            void operator()(Types ... args) const {
+                func_(args...);
+            }
+
+        private:
+            Callback func_;
+            unsigned long long handler_;
+            static unsigned long long free_handler_;
+        };
         
         
         DatStatus() = delete;
         DatStatus() = delete;
 
 
@@ -68,14 +109,14 @@ namespace LOTRO_DAT {
 
 
         bool CheckIfNotPatched();
         bool CheckIfNotPatched();
         
         
-        void AddStatusChangedCallbackFunction(Callback* func);
-
-        void RemoveStatusChangedCallbackFunction(Callback* func);
+        unsigned long long AddStatusChangedCallbackFunction(Callback func);
 
 
-        bool IsFunctionRegisteredAsCallback(Callback* func);
+        void RemoveStatusChangedCallbackFunction(unsigned long long handler);
 
 
         void EraseAllCallbackFunctions();
         void EraseAllCallbackFunctions();
 
 
+        void InvokeCallbackFunctions();
+
     private:
     private:
         DatFile *dat = nullptr;
         DatFile *dat = nullptr;
         DAT_STATUS status_ = DAT_STATUS::E_FREE;
         DAT_STATUS status_ = DAT_STATUS::E_FREE;
@@ -84,7 +125,7 @@ namespace LOTRO_DAT {
         double percentage_ = 0;
         double percentage_ = 0;
         std::string debug_message_ = "";
         std::string debug_message_ = "";
 
 
-        std::set<Callback*> callback_functions_;
+        std::set<CallbackStorage> callback_functions_;
     };
     };
 }
 }
 
 

+ 3 - 4
src/DatFile.cpp

@@ -29,21 +29,20 @@ namespace LOTRO_DAT {
         el::Loggers::addFlag(el::LoggingFlag::LogDetailedCrashReason);
         el::Loggers::addFlag(el::LoggingFlag::LogDetailedCrashReason);
         el::Loggers::addFlag(el::LoggingFlag::ImmediateFlush);
         el::Loggers::addFlag(el::LoggingFlag::ImmediateFlush);
         el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);
         el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);
-        el::Loggers::addFlag(el::LoggingFlag::LogDetailedCrashReason);
         
         
         defaultConf.setToDefault();
         defaultConf.setToDefault();
         defaultConf.setGlobally(el::ConfigurationType::Format, "%datetime %level : %msg (function: %func)");
         defaultConf.setGlobally(el::ConfigurationType::Format, "%datetime %level : %msg (function: %func)");
 
 
-
         defaultConf.setGlobally(el::ConfigurationType::ToFile, "true");
         defaultConf.setGlobally(el::ConfigurationType::ToFile, "true");
         defaultConf.setGlobally(el::ConfigurationType::Filename, "dat_library.log");
         defaultConf.setGlobally(el::ConfigurationType::Filename, "dat_library.log");
         defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput, "false");
         defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput, "false");
-        defaultConf.setGlobally(el::ConfigurationType::PerformanceTracking, "true");
-        defaultConf.setGlobally(el::ConfigurationType::MaxLogFileSize, "‭20971520‬"); // 20MB
+        defaultConf.setGlobally(el::ConfigurationType::MaxLogFileSize, "15728640"); // 15MB
 
 
         #ifndef NDEBUG
         #ifndef NDEBUG
         defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "true");
         defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "true");
         defaultConf.set(el::Level::Debug, el::ConfigurationType::Filename, "dat_library_debug.log");
         defaultConf.set(el::Level::Debug, el::ConfigurationType::Filename, "dat_library_debug.log");
+        #elif NDEBUG
+        defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "false");
         #endif
         #endif
         
         
         el::Loggers::reconfigureAllLoggers(defaultConf);
         el::Loggers::reconfigureAllLoggers(defaultConf);

+ 3 - 3
src/DatSubsystems/DatBackupManager.cpp

@@ -23,7 +23,7 @@ namespace LOTRO_DAT {
 
 
     bool DatBackupManager::CheckIfBackupAvailable(const std::string &backup_datname) {
     bool DatBackupManager::CheckIfBackupAvailable(const std::string &backup_datname) {
         DatFile file;
         DatFile file;
-        bool result = file.Initialise(backup_datname, 0).result == SUCCESS && file.Initialized();
+        bool result = (file.Initialise(backup_datname, 0).result == SUCCESS && file.Initialized());
         file.Deinitialize();
         file.Deinitialize();
         return result;
         return result;
     }
     }
@@ -126,11 +126,11 @@ namespace LOTRO_DAT {
         long long newfile_size = 0;
         long long newfile_size = 0;
         unsigned elapsed_size = unsigned(source.GetIO().GetActualDatSize().value - newfile_size);
         unsigned elapsed_size = unsigned(source.GetIO().GetActualDatSize().value - newfile_size);
         
         
-        dat->GetStatusModule().SetTotalParts(parts_count);
+        dat->GetStatusModule().SetTotalParts(source.GetIO().GetActualDatSize().value);
 
 
         BinaryData data(COPY_BLOCK_SIZE);
         BinaryData data(COPY_BLOCK_SIZE);
         for (unsigned i = 0; i < parts_count; i++) {
         for (unsigned i = 0; i < parts_count; i++) {
-            dat->GetStatusModule().SetFinishedParts(i);
+            dat->GetStatusModule().SetFinishedParts(i * COPY_BLOCK_SIZE);
 
 
             auto operation = source.GetIO().ReadData(data, std::min(COPY_BLOCK_SIZE, elapsed_size), newfile_size);
             auto operation = source.GetIO().ReadData(data, std::min(COPY_BLOCK_SIZE, elapsed_size), newfile_size);
             if (operation.result == ERROR)
             if (operation.result == ERROR)

+ 9 - 0
src/DatSubsystems/DatLocaleManager.cpp

@@ -461,6 +461,10 @@ namespace LOTRO_DAT {
             return DatOperationResult<>(SUCCESS);
             return DatOperationResult<>(SUCCESS);
         }
         }
 
 
+        dat->GetStatusModule().SetStatus(DatStatus::E_COMMITING);
+        dat->GetStatusModule().SetTotalParts(orig_dict_.size() + patch_dict_.size());
+        dat->GetStatusModule().SetFinishedParts(0);
+
         BinaryData binary_data = BinaryData(4 + 4 + 4 + 4 + 14 + 15 + 4
         BinaryData binary_data = BinaryData(4 + 4 + 4 + 4 + 14 + 15 + 4
                                             + 4 + (32 + 4) * orig_dict_.size()
                                             + 4 + (32 + 4) * orig_dict_.size()
                                             + 4 + (32 + 4) * patch_dict_.size()
                                             + 4 + (32 + 4) * patch_dict_.size()
@@ -478,11 +482,14 @@ namespace LOTRO_DAT {
         binary_data.Append(BinaryData::FromNumber<4>(orig_dict_.size()), current_size);
         binary_data.Append(BinaryData::FromNumber<4>(orig_dict_.size()), current_size);
         current_size += 4;
         current_size += 4;
 
 
+        unsigned long long processed_files = 0;
+
         for (const auto &file : orig_dict_) {
         for (const auto &file : orig_dict_) {
             binary_data.Append(file.second.MakeHeaderData(), current_size);
             binary_data.Append(file.second.MakeHeaderData(), current_size);
             current_size += 32;
             current_size += 32;
             binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
             binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
             current_size += 4;
             current_size += 4;
+            dat->GetStatusModule().SetFinishedParts(++processed_files);
         }
         }
 
 
         binary_data.Append(BinaryData::FromNumber<4>(patch_dict_.size()), current_size);
         binary_data.Append(BinaryData::FromNumber<4>(patch_dict_.size()), current_size);
@@ -493,6 +500,7 @@ namespace LOTRO_DAT {
             current_size += 32;
             current_size += 32;
             binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
             binary_data.Append(BinaryData::FromNumber<4>(file.second.category), current_size);
             current_size += 4;
             current_size += 4;
+            dat->GetStatusModule().SetFinishedParts(++processed_files);
         }
         }
 
 
         binary_data.Append(BinaryData::FromNumber<4>(inactive_categories.size()), current_size);
         binary_data.Append(BinaryData::FromNumber<4>(inactive_categories.size()), current_size);
@@ -556,6 +564,7 @@ namespace LOTRO_DAT {
             }
             }
         }
         }
 
 
+        dat->GetStatusModule().SetDefaultStatus();
         LOG(INFO) << "Locales committed successfully";
         LOG(INFO) << "Locales committed successfully";
         return DatOperationResult<>(SUCCESS);
         return DatOperationResult<>(SUCCESS);
     }
     }

+ 24 - 15
src/DatSubsystems/DatStatus.cpp

@@ -4,24 +4,28 @@
 #include <math.h>
 #include <math.h>
 
 
 namespace LOTRO_DAT {
 namespace LOTRO_DAT {
-    bool equalOfftoTwoDecimalPoints(double x, double y) {
-        return int(std::floor((x * 100.0) + .5) / 100.0) == int(std::floor((y * 100.0) + .5) / 100.0);
+    unsigned long long DatStatus::CallbackStorage::free_handler_ = 0;
+
+    bool equalOfftoOneDecimalPoint(double x, double y) {
+        return int(x * 10.0) == int(y * 10.0);
     }
     }
 
 
     DatStatus::DatStatus(DatFile *datFilePtr) : dat(datFilePtr) {
     DatStatus::DatStatus(DatFile *datFilePtr) : dat(datFilePtr) {
     }
     }
 
 
     void DatStatus::SetStatus(DatStatus::DAT_STATUS status) {
     void DatStatus::SetStatus(DatStatus::DAT_STATUS status) {
+        bool need_to_invoke_progress_callback_functions = (status_ != status);
         status_ = status;
         status_ = status;
+        if (need_to_invoke_progress_callback_functions) {
+            InvokeCallbackFunctions();
+        }
     }
     }
         
         
     void DatStatus::SetFinishedParts(unsigned long long finished_parts) {
     void DatStatus::SetFinishedParts(unsigned long long finished_parts) {
         finished_parts_ = finished_parts;
         finished_parts_ = finished_parts;
-        if (!equalOfftoTwoDecimalPoints(GetPercentage(), percentage_)) {
+        if (!equalOfftoOneDecimalPoint(GetPercentage(), percentage_)) {
             percentage_ = GetPercentage();
             percentage_ = GetPercentage();
-            for (Callback* func : callback_functions_) {
-                (*func)(GetStatus(), GetPercentage(), GetFinishedParts(), GetTotalParts(), GetDebugMessage()); // evaluating callback functions, transfering all needed information.
-            }
+            InvokeCallbackFunctions();
         }
         }
     }
     }
 
 
@@ -38,7 +42,7 @@ namespace LOTRO_DAT {
     }
     }
 
 
     double DatStatus::GetPercentage() {
     double DatStatus::GetPercentage() {
-        return total_parts_ != 0 ? double(finished_parts_) / double(total_parts_) : 0;
+        return total_parts_ != 0 ? double(finished_parts_) * 100.0 / double(total_parts_) : 0;
     }
     }
 
 
     unsigned long long DatStatus::GetFinishedParts() {
     unsigned long long DatStatus::GetFinishedParts() {
@@ -58,25 +62,30 @@ namespace LOTRO_DAT {
         total_parts_ = 0;
         total_parts_ = 0;
         finished_parts_ = 0;
         finished_parts_ = 0;
         status_ = E_FREE;
         status_ = E_FREE;
+        InvokeCallbackFunctions();
     }
     }
 
 
     bool DatStatus::CheckIfNotPatched() {
     bool DatStatus::CheckIfNotPatched() {
         return dat->GetLocaleManager().patch_dict_.empty();
         return dat->GetLocaleManager().patch_dict_.empty();
     }
     }
 
 
-    void DatStatus::AddStatusChangedCallbackFunction(Callback* func) {
-        callback_functions_.insert(func);
-    }
-
-    void DatStatus::RemoveStatusChangedCallbackFunction(Callback* func) {
-        callback_functions_.erase(func);
+    unsigned long long DatStatus::AddStatusChangedCallbackFunction(Callback func) {
+        CallbackStorage storage(func);
+        callback_functions_.insert(storage);
+        return storage.GetHandler();
     }
     }
 
 
-    bool DatStatus::IsFunctionRegisteredAsCallback(Callback* func) {
-        return callback_functions_.count(func);
+    void DatStatus::RemoveStatusChangedCallbackFunction(unsigned long long handler) {
+        callback_functions_.erase(CallbackStorage(handler));
     }
     }
 
 
     void DatStatus::EraseAllCallbackFunctions() {
     void DatStatus::EraseAllCallbackFunctions() {
         callback_functions_.clear();
         callback_functions_.clear();
     }
     }
+
+    void DatStatus::InvokeCallbackFunctions() {
+        for (const auto& func : callback_functions_) {
+            func(ProgressInfo({GetStatus(), GetPercentage(), GetFinishedParts(), GetTotalParts()})); // evaluating callback functions, transfering all needed information.
+        }
+    }
 } // namespace LOTRO_DAT
 } // namespace LOTRO_DAT

+ 82 - 18
src/Examples/extractor_example.cpp

@@ -5,7 +5,7 @@
 #include <ctime>
 #include <ctime>
 #include <algorithm>
 #include <algorithm>
 
 
-#ifdef WIN32
+#ifdef _WIN32
 #include <direct.h>
 #include <direct.h>
 #define mkdir(dir, mode) _mkdir(dir)
 #define mkdir(dir, mode) _mkdir(dir)
 #endif
 #endif
@@ -31,7 +31,56 @@ bool exportTexturesToDb = false;
 bool exportUnknownToDb = false;
 bool exportUnknownToDb = false;
 // There is no need to change anything else
 // There is no need to change anything else
 
 
+void DatStatusChangedHandler(DatStatus::ProgressInfo info) {
+    if (info.status == DatStatus::DAT_STATUS::E_FREE) {
+        std::cout << "DatStatus: operation finished" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_BACKUP_CREATING) {
+        std::cout << "DatStatus: creating backup " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_BACKUP_REMOVING) {
+        std::cout << "DatStatus: removing backup " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_BACKUP_RESTORING) {
+        std::cout << "DatStatus: restoring backup " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_COMMITING) {
+        std::cout << "DatStatus: applying locales " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_EXTRACTING) {
+        std::cout << "DatStatus: extracting data " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_GATHERING_INFO) {
+        std::cout << "DatStatus: gathering info " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_INITIALISING) {
+        std::cout << "DatStatus: initialising " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_PATCHING) {
+        std::cout << "DatStatus: applying patch " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+}
+
 int main() {
 int main() {
+    std::cout.precision(1);
+    std::cout << fixed;
     std::cout << "Gi1dor's LotRO .dat extractor ver. " << LOTRO_DAT_VERSION << std::endl;
     std::cout << "Gi1dor's LotRO .dat extractor ver. " << LOTRO_DAT_VERSION << std::endl;
 
 
     std::cout << "Hello! I'm a basic shell version of .dat file extractor. I can open .dat file directly, "
     std::cout << "Hello! I'm a basic shell version of .dat file extractor. I can open .dat file directly, "
@@ -42,13 +91,13 @@ int main() {
     if (!in.fail()) {
     if (!in.fail()) {
         std::string filename;
         std::string filename;
         getline(in, filename);
         getline(in, filename);
-
         std::cout << "Using .dat file from dat_file_path.txt...\n";
         std::cout << "Using .dat file from dat_file_path.txt...\n";
         std::cout << "Opening file " << filename << std::endl;
         std::cout << "Opening file " << filename << std::endl;
 
 
         auto operation = file.Initialise(filename, 0);
         auto operation = file.Initialise(filename, 0);
-        if (operation.result == false)
+        if (operation.result == false) {
             std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\n";
             std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\n";
+        }
     }
     }
 
 
     while (!file.Initialized()) {
     while (!file.Initialized()) {
@@ -60,12 +109,14 @@ int main() {
         std::cout << "Opening file " << filename << std::endl;
         std::cout << "Opening file " << filename << std::endl;
 
 
         auto operation = file.Initialise(filename, 0);
         auto operation = file.Initialise(filename, 0);
-        if (operation.result == false)
+        if (operation.result == false) {
             std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\nTo try again enter path to .dat file\n";
             std::cout << "Dat initialisation failed. Error message: " << operation.msg << "\nTo try again enter path to .dat file\n";
+        }
     }
     }
 
 
     std::cout << "Great! File initialised successfully!\n";
     std::cout << "Great! File initialised successfully!\n";
-    std::cout << "Gathering file information...";
+    file.GetStatusModule().AddStatusChangedCallbackFunction(&DatStatusChangedHandler);
+    std::cout << "Gathering file information...\n";
     file.GatherInformation("dat_info.log");
     file.GatherInformation("dat_info.log");
 
 
     int cmd = 0;
     int cmd = 0;
@@ -111,65 +162,78 @@ int main() {
 
 
             if (exportImagesToDb) {
             if (exportImagesToDb) {
                 output_db.InitDatabase(output_dir + std::string("Images.db"));
                 output_db.InitDatabase(output_dir + std::string("Images.db"));
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(JPG, &output_db).value << " .jpg files to Images.db" << std::endl << std::flush;
+                int extracted_jpg_files_num = file.GetExporter().ExtractAllFilesByType(JPG, &output_db).value;
+                std::cout << "Extracted " << extracted_jpg_files_num << " .jpg files to Images.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
                 output_db.CloseDatabase();
             }
             }
 
 
             if (exportSoundsToDb) {
             if (exportSoundsToDb) {
                 output_db.InitDatabase(output_dir + std::string("Sounds.db"));
                 output_db.InitDatabase(output_dir + std::string("Sounds.db"));
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(WAV, &output_db).value << " .wav files to Sounds.db" << std::endl << std::flush;
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(OGG, &output_db).value << " .ogg files to Sounds.db" << std::endl << std::flush;
+                int extracted_wav_files_num = file.GetExporter().ExtractAllFilesByType(WAV, &output_db).value;
+                int extracted_ogg_files_num = file.GetExporter().ExtractAllFilesByType(OGG, &output_db).value;
+                std::cout << "Extracted " << extracted_wav_files_num << " .wav files to Sounds.db" << std::endl << std::flush;
+                std::cout << "Extracted " << extracted_ogg_files_num << " .ogg files to Sounds.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
                 output_db.CloseDatabase();
             }
             }
 
 
             if (exportTextsToDb) {
             if (exportTextsToDb) {
                 output_db.InitDatabase(output_dir + std::string("Texts.db"));
                 output_db.InitDatabase(output_dir + std::string("Texts.db"));
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(TEXT, &output_db).value << " text files to Texts.db" << std::endl << std::flush;
+                int extracted_text_files_num = file.GetExporter().ExtractAllFilesByType(TEXT, &output_db).value;
+                std::cout << "Extracted " << extracted_text_files_num << " text files to Texts.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
                 output_db.CloseDatabase();
             }
             }
 
 
             if (exportFontsToDb) {
             if (exportFontsToDb) {
                 output_db.InitDatabase(output_dir + std::string("Fonts.db"));
                 output_db.InitDatabase(output_dir + std::string("Fonts.db"));
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(FONT, &output_db).value << " font files to Fonts.db" << std::endl << std::flush;
+                int extracted_font_files_num = file.GetExporter().ExtractAllFilesByType(FONT, &output_db).value;
+                std::cout << "Extracted " << extracted_font_files_num << " font files to Fonts.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
                 output_db.CloseDatabase();
             }
             }
 
 
             if (exportTexturesToDb) {
             if (exportTexturesToDb) {
                 output_db.InitDatabase(output_dir + std::string("Textures.db"));
                 output_db.InitDatabase(output_dir + std::string("Textures.db"));
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(DDS, &output_db).value << " .dds files to Textures.db" << std::endl << std::flush;
+                int extracted_dds_files_num = file.GetExporter().ExtractAllFilesByType(DDS, &output_db).value;
+                std::cout << "Extracted " << extracted_dds_files_num << " .dds files to Textures.db" << std::endl << std::flush;
                 output_db.CloseDatabase();
                 output_db.CloseDatabase();
             }
             }
 
 
             if (exportImagesToFiles) {
             if (exportImagesToFiles) {
                 mkdir((output_dir + "jpg").c_str(), 744);
                 mkdir((output_dir + "jpg").c_str(), 744);
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(JPG, output_dir + "jpg\\").value << " .jpg files to directory" << std::endl << std::flush;
+                int extracted_jpg_files_num = file.GetExporter().ExtractAllFilesByType(JPG, output_dir + "jpg\\").value;
+                std::cout << "Extracted " << extracted_jpg_files_num << " .jpg files to directory" << std::endl << std::flush;
             }
             }
 
 
             if (exportTexturesToFiles) {
             if (exportTexturesToFiles) {
                 mkdir((output_dir + "dds").c_str(), 744);
                 mkdir((output_dir + "dds").c_str(), 744);
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(DDS, output_dir + "dds\\").value << " .dds files to directory" << std::endl << std::flush;
+                int extracted_dds_files_num = file.GetExporter().ExtractAllFilesByType(DDS, output_dir + "dds\\").value;
+                std::cout << "Extracted " << extracted_dds_files_num << " .dds files to directory" << std::endl << std::flush;
             }
             }
 
 
             if (exportSoundsToFiles) {
             if (exportSoundsToFiles) {
                 mkdir((output_dir + "wav").c_str(), 744);
                 mkdir((output_dir + "wav").c_str(), 744);
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(WAV, output_dir + "wav\\").value << " .wav files to directory" << std::endl << std::flush;
+                int extracted_wav_files_num = file.GetExporter().ExtractAllFilesByType(WAV, output_dir + "wav\\").value;
+                std::cout << "Extracted " << extracted_wav_files_num << " .wav files to directory" << std::endl << std::flush;
                 mkdir((output_dir + "ogg").c_str(), 744);
                 mkdir((output_dir + "ogg").c_str(), 744);
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(OGG, output_dir + "ogg\\").value << " .ogg files to directory" << std::endl << std::flush;
+                int extracted_ogg_files_num = file.GetExporter().ExtractAllFilesByType(OGG, output_dir + "ogg\\").value;
+                std::cout << "Extracted " << extracted_ogg_files_num << " .ogg files to directory" << std::endl << std::flush;
             }
             }
 
 
             if (exportFontsToFiles) {
             if (exportFontsToFiles) {
                 mkdir((output_dir + "fonts").c_str(), 744);
                 mkdir((output_dir + "fonts").c_str(), 744);
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(FONT, output_dir + "fonts\\").value << " font files to directory" << std::endl << std::flush;
+                int extracted_font_files_num = file.GetExporter().ExtractAllFilesByType(FONT, output_dir + "fonts\\").value;
+                std::cout << "Extracted " << extracted_font_files_num << " font files to directory" << std::endl << std::flush;
             }
             }
 
 
             if (exportUnknownToFiles) {
             if (exportUnknownToFiles) {
                 mkdir((output_dir + "unknown").c_str(), 744);
                 mkdir((output_dir + "unknown").c_str(), 744);
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(UNKNOWN, output_dir + "unknown\\").value << " unknown files to directory" << std::endl << std::flush;
+                int extracted_unknown_files_num = file.GetExporter().ExtractAllFilesByType(UNKNOWN, output_dir + "unknown\\").value;
+                std::cout << "Extracted " << extracted_unknown_files_num << " unknown files to directory" << std::endl << std::flush;
             }
             }
 
 
             if (exportTextsToFiles) {
             if (exportTextsToFiles) {
                 mkdir((output_dir + "texts").c_str(), 744);
                 mkdir((output_dir + "texts").c_str(), 744);
-                std::cout << "Extracted " << file.GetExporter().ExtractAllFilesByType(TEXT, output_dir + "texts\\").value << " text files to directory" << std::endl << std::flush;
+                int extracted_text_files_num = file.GetExporter().ExtractAllFilesByType(TEXT, output_dir + "texts\\").value;
+                std::cout << "Extracted " << extracted_text_files_num << " text files to directory" << std::endl << std::flush;
             }
             }
 
 
             fprintf(stdout, "Spent %f seconds on running unpacker! Thank you for your patience!\n",
             fprintf(stdout, "Spent %f seconds on running unpacker! Thank you for your patience!\n",

+ 54 - 15
src/Examples/patcher_example.cpp

@@ -14,7 +14,56 @@
 using namespace LOTRO_DAT;
 using namespace LOTRO_DAT;
 using namespace std;
 using namespace std;
 
 
+void DatStatusChangedHandler(DatStatus::ProgressInfo info) {
+    if (info.status == DatStatus::DAT_STATUS::E_FREE) {
+        std::cout << "DatStatus: operation finished" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_BACKUP_CREATING) {
+        std::cout << "DatStatus: creating backup " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_BACKUP_REMOVING) {
+        std::cout << "DatStatus: removing backup " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_BACKUP_RESTORING) {
+        std::cout << "DatStatus: restoring backup " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_COMMITING) {
+        std::cout << "DatStatus: applying locales " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_EXTRACTING) {
+        std::cout << "DatStatus: extracting data " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_GATHERING_INFO) {
+        std::cout << "DatStatus: gathering info " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_INITIALISING) {
+        std::cout << "DatStatus: initialising " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+    if (info.status == DatStatus::DAT_STATUS::E_PATCHING) {
+        std::cout << "DatStatus: applying patch " << info.percentage << "% " 
+        << "(" << info.finished_parts << "/" << info.total_parts << ")" << std::endl;
+        return;
+    }
+}
+
 int main() {
 int main() {
+    std::cout.precision(1);
+    std::cout << fixed;
     std::cout << "Gi1dor's LotRO .dat patcher ver. " << LOTRO_DAT_VERSION << std::endl;
     std::cout << "Gi1dor's LotRO .dat patcher ver. " << LOTRO_DAT_VERSION << std::endl;
     freopen("patcher_errors.log", "w", stderr);
     freopen("patcher_errors.log", "w", stderr);
 
 
@@ -54,6 +103,7 @@ int main() {
     }
     }
 
 
     std::cout << "Great! File initialised successfully!\n";
     std::cout << "Great! File initialised successfully!\n";
+    file.GetStatusModule().AddStatusChangedCallbackFunction(&DatStatusChangedHandler);
 
 
     while (true) {
     while (true) {
         std::cout << "Please, choose, what should I do. I can patch datfile from database to .dat file (enter 1), "
         std::cout << "Please, choose, what should I do. I can patch datfile from database to .dat file (enter 1), "
@@ -105,21 +155,10 @@ int main() {
                 std::cout << "There are " << db.CountRows() << " files in database." << std::endl;
                 std::cout << "There are " << db.CountRows() << " files in database." << std::endl;
 
 
                 std::cout << "Successfully opened database! Beginning patching...\n";
                 std::cout << "Successfully opened database! Beginning patching...\n";
+                
                 const clock_t begin_time = clock();
                 const clock_t begin_time = clock();
-                size_t all = db.CountRows();
-                size_t now = 0;
-
-                SubfileData subfile = db.GetNextFile();
-                while (!subfile.Empty()) {
-                    if (file.GetPatcher().PatchFile(subfile).result == ERROR) {
-                        fprintf(stderr, "Error! Caught exception while patching file! Passing it\n");
-                    }
-
-                    subfile = db.GetNextFile();
-                    ++now;
-                    if (now * 100 / all > (now - 1) * 100 / all)
-                        std::cout << now * 100 / all << "%\n";
-                }
+
+                file.GetPatcher().PatchAllDatabase(&db);
 
 
                 db.CloseDatabase();
                 db.CloseDatabase();
 
 
@@ -180,7 +219,7 @@ int main() {
         }
         }
 
 
         if (cmd == 10) {
         if (cmd == 10) {
-            std::cout << "Backup file " << (file.GetBackupManager().CheckIfBackupAvailable("clie_local_En.backup") ? "exists!" : "doesn't exist.") << std::endl;
+            std::cout << "Backup file " << (file.GetBackupManager().CheckIfBackupAvailable("cli_local_En.backup") ? "exists!" : "doesn't exist.") << std::endl;
         }
         }
     }
     }
     //system("pause");
     //system("pause");