diff --git a/include/europa/io/PakProgressReportSink.hpp b/include/europa/io/PakProgressReportSink.hpp new file mode 100644 index 0000000..4ed3237 --- /dev/null +++ b/include/europa/io/PakProgressReportSink.hpp @@ -0,0 +1,51 @@ +// +// EuropaTools +// +// (C) 2021-2022 modeco80 +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef EUROPA_IO_PAKPROGRESSREPORTSINK_H +#define EUROPA_IO_PAKPROGRESSREPORTSINK_H + +#include + +namespace europa::io { + + /** + * Interface for the writer to output detailed progress information. + */ + struct PakProgressReportSink { + + struct PakEvent { + enum class Type { + FillInHeader, ///< Filling in header + WritingHeader, ///< Writing header + + WritingToc ///< Writing archive TOC + }; + + Type type; + }; + + struct FileEvent { + enum class Type { + FileBeginWrite, ///< File has began write to package + FileEndWrite, ///< File writing finished + }; + + Type type; + std::string filename; + }; + + virtual ~PakProgressReportSink() = default; + + virtual void OnEvent(const PakEvent& event) = 0; + virtual void OnEvent(const FileEvent& event) = 0; + }; + + +} // namespace europa::io + +#endif // EUROPA_IO_PAKPROGRESSREPORTSINK_H diff --git a/include/europa/io/PakWriter.hpp b/include/europa/io/PakWriter.hpp index 7d91502..4723d9c 100644 --- a/include/europa/io/PakWriter.hpp +++ b/include/europa/io/PakWriter.hpp @@ -10,6 +10,7 @@ #define EUROPA_IO_PAKWRITER_H #include +#include #include #include #include @@ -30,7 +31,7 @@ namespace europa::io { /** * Write the resulting archive to the given output stream. */ - void Write(std::ostream& os); + void Write(std::ostream& os, PakProgressReportSink& sink); private: structs::PakHeader pakHeader {}; diff --git a/src/libeuropa/io/PakWriter.cpp b/src/libeuropa/io/PakWriter.cpp index d15a396..0288b97 100644 --- a/src/libeuropa/io/PakWriter.cpp +++ b/src/libeuropa/io/PakWriter.cpp @@ -65,7 +65,7 @@ namespace europa::io { // - Composable operations (WriteTOC, WriteFile, WriteHeader) // - Add IProgressReportSink reporting - void PakWriter::Write(std::ostream& os) { + void PakWriter::Write(std::ostream& os, PakProgressReportSink& sink) { // This essentially converts our map we use for faster insertion // into a flat array we can sort easily. @@ -93,15 +93,32 @@ namespace europa::io { for(auto& [filename, file] : sortedFiles) { //std::cout << "PakWriteFile \"" << filename << "\"\n Size " << file.GetTOCEntry().size << "\n"; + + sink.OnEvent({ + PakProgressReportSink::FileEvent::Type::FileBeginWrite, + filename + }); + file.GetTOCEntry().offset = os.tellp(); os.write(reinterpret_cast(file.GetData().data()), file.GetData().size()); // Flush on file writing os.flush(); + + + sink.OnEvent({ + PakProgressReportSink::FileEvent::Type::FileEndWrite, + filename + }); } pakHeader.tocOffset = os.tellp(); + + sink.OnEvent({ + PakProgressReportSink::PakEvent::Type::WritingToc + }); + // Write the TOC for(auto& [filename, file] : sortedFiles) { file.FillTOCEntry(); @@ -115,10 +132,21 @@ namespace europa::io { impl::WriteStreamType(os, file.GetTOCEntry()); } + + sink.OnEvent({ + PakProgressReportSink::PakEvent::Type::FillInHeader + }); + + // Fill out the rest of the header. pakHeader.fileCount = archiveFiles.size(); pakHeader.tocSize = static_cast(os.tellp()) - (pakHeader.tocOffset - 1); + + sink.OnEvent({ + PakProgressReportSink::PakEvent::Type::WritingHeader + }); + // As the last step, write it. os.seekp(0, std::ostream::beg); impl::WriteStreamType(os, pakHeader); diff --git a/src/tools/eupak/Utils.cpp b/src/tools/eupak/Utils.cpp index dff66e3..a7e7ef7 100644 --- a/src/tools/eupak/Utils.cpp +++ b/src/tools/eupak/Utils.cpp @@ -34,7 +34,7 @@ namespace eupak { div *= unit; - exp++; // TODO: break if too big + exp++; } } diff --git a/src/tools/eupak/tasks/CreateTask.cpp b/src/tools/eupak/tasks/CreateTask.cpp index a9c842e..3952701 100644 --- a/src/tools/eupak/tasks/CreateTask.cpp +++ b/src/tools/eupak/tasks/CreateTask.cpp @@ -16,6 +16,65 @@ namespace eupak::tasks { + struct CreateArchiveReportSink : public europa::io::PakProgressReportSink { + + CreateArchiveReportSink(int fileCount = 0) + : europa::io::PakProgressReportSink() { + indicators::show_console_cursor(false); + progress.set_option(indicators::option::MaxProgress { fileCount }); + } + + ~CreateArchiveReportSink() { + indicators::show_console_cursor(true); + } + + void OnEvent(const PakEvent& event) override { + using enum PakEvent::Type; + switch(event.type) { + case WritingHeader: + progress.set_option(indicators::option::PostfixText {"Writing header"}); + progress.print_progress(); + break; + + case FillInHeader: + progress.set_option(indicators::option::PostfixText {"Filling in header"}); + progress.print_progress(); + break; + + case WritingToc: + progress.set_option(indicators::option::PostfixText {"Writing TOC"}); + progress.print_progress(); + break; + } + } + + void OnEvent(const FileEvent& event) override { + using enum FileEvent::Type; + switch(event.type) { + case FileBeginWrite: + progress.set_option(indicators::option::PostfixText {"Writing " + event.filename}); + progress.print_progress(); + break; + + case FileEndWrite: + progress.set_option(indicators::option::PostfixText {"Written " + event.filename}); + progress.tick(); + break; + } + } + + private: + indicators::ProgressBar progress { + indicators::option::BarWidth { 50 }, + indicators::option::ForegroundColor { indicators::Color::yellow }, + indicators::option::ShowPercentage { true }, + indicators::option::ShowElapsedTime { true }, + indicators::option::ShowRemainingTime { true }, + + indicators::option::PrefixText { "Writing archive " } + }; + }; + int CreateTask::Run(Arguments&& args) { europa::io::PakWriter writer; @@ -41,7 +100,7 @@ namespace eupak::tasks { indicators::option::ShowElapsedTime { true }, indicators::option::ShowRemainingTime { true }, - indicators::option::PrefixText { "Creating archive " } + indicators::option::PrefixText { "Adding files to archive " } }; indicators::show_console_cursor(false); @@ -91,6 +150,8 @@ namespace eupak::tasks { currFile++; } + indicators::show_console_cursor(true); + std::ofstream ofs(args.outputFile.string(), std::ofstream::binary); if(!ofs) { @@ -98,8 +159,10 @@ namespace eupak::tasks { return 1; } - writer.Write(ofs); - indicators::show_console_cursor(true); + + CreateArchiveReportSink sink(fileCount); + + writer.Write(ofs, sink); return 0; }