*: Implement detailed progress reporting when creating

Ditto!
This commit is contained in:
Lily Tsuru 2022-09-22 19:07:56 -05:00
parent e82d693dfc
commit cf9d84cb24
5 changed files with 149 additions and 6 deletions

View File

@ -0,0 +1,51 @@
//
// EuropaTools
//
// (C) 2021-2022 modeco80 <lily.modeco80@protonmail.ch>
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#ifndef EUROPA_IO_PAKPROGRESSREPORTSINK_H
#define EUROPA_IO_PAKPROGRESSREPORTSINK_H
#include <string>
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

View File

@ -10,6 +10,7 @@
#define EUROPA_IO_PAKWRITER_H
#include <europa/io/PakFile.hpp>
#include <europa/io/PakProgressReportSink.hpp>
#include <iosfwd>
#include <string>
#include <unordered_map>
@ -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 {};

View File

@ -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<const char*>(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<std::uint32_t>(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);

View File

@ -34,7 +34,7 @@ namespace eupak {
div *= unit;
exp++; // TODO: break if too big
exp++;
}
}

View File

@ -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;
}