From 1ac35c2773e706720b8ac57aa6dd7c3e51fe1879 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Sun, 3 Nov 2013 16:13:04 +0200 Subject: [PATCH] Zip: Allow output to arbitrary ostream --- include/core/moviefile.hpp | 8 +++++-- include/library/zip.hpp | 7 ++++-- src/core/moviefile.cpp | 12 +++++++++- src/library/zip.cpp | 49 ++++++++++++++++++++++++-------------- 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/include/core/moviefile.hpp b/include/core/moviefile.hpp index bfdd4637..3b40f64f 100644 --- a/include/core/moviefile.hpp +++ b/include/core/moviefile.hpp @@ -8,7 +8,7 @@ #include "core/controllerframe.hpp" #include "core/rom.hpp" #include "core/subtitles.hpp" - +#include "library/zip.hpp" /** * This structure gives parsed representationg of movie file, as result of decoding or for encoding. @@ -61,7 +61,10 @@ struct moviefile */ void save(const std::string& filename, unsigned compression, bool binary) throw(std::bad_alloc, std::runtime_error); - +/** + * Reads this movie structure and saves it to stream (uncompressed ZIP). + */ + void save(std::ostream& outstream) throw(std::bad_alloc, std::runtime_error); /** * Force loading as corrupt. */ @@ -213,6 +216,7 @@ struct moviefile private: void binary_io(std::ostream& stream) throw(std::bad_alloc, std::runtime_error); void binary_io(std::istream& stream, struct core_type& romtype) throw(std::bad_alloc, std::runtime_error); + void save(zip_writer& w) throw(std::bad_alloc, std::runtime_error); }; #endif diff --git a/include/library/zip.hpp b/include/library/zip.hpp index 35f9fc74..94e1028d 100644 --- a/include/library/zip.hpp +++ b/include/library/zip.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include /** @@ -297,12 +298,13 @@ public: * Creates new empty ZIP archive. The members will be compressed according to specified compression. * * parameter zipfile: The zipfile to create. + * parameter stream: The stream to write the ZIP to. * parameter _compression: Compression. 0 is uncompressed, 1-9 are deflate compression levels. * throws std::bad_alloc: Not enough memory. * throws std::runtime_error: Can't open archive or invalid argument. */ zip_writer(const std::string& zipfile, unsigned _compression) throw(std::bad_alloc, std::runtime_error); - + zip_writer(std::ostream& stream, unsigned _compression) throw(std::bad_alloc, std::runtime_error); /** * Destroys ZIP writer, aborting the transaction (unless commit() has been called). */ @@ -347,7 +349,8 @@ private: zip_writer(zip_writer&); zip_writer& operator=(zip_writer&); - std::ofstream zipstream; + std::ostream* zipstream; + bool system_stream; std::string temp_path; std::string zipfile_path; std::string open_file; diff --git a/src/core/moviefile.cpp b/src/core/moviefile.cpp index 5b2a3862..66051723 100644 --- a/src/core/moviefile.cpp +++ b/src/core/moviefile.cpp @@ -703,6 +703,17 @@ void moviefile::save(const std::string& movie, unsigned compression, bool binary return; } zip_writer w(movie, compression); + save(w); +} + +void moviefile::save(std::ostream& stream) throw(std::bad_alloc, std::runtime_error) +{ + zip_writer w(stream, 0); + save(w); +} + +void moviefile::save(zip_writer& w) throw(std::bad_alloc, std::runtime_error) +{ write_linefile(w, "gametype", gametype->get_name()); write_settings(w, settings, gametype->get_type().get_settings(), [](zip_writer& w, const std::string& name, const std::string& value) -> void { @@ -754,7 +765,6 @@ void moviefile::save(const std::string& movie, unsigned compression, bool binary write_raw_file(w, "initram." + i.first, i.second); write_authors_file(w, authors); write_input(w, input); - w.commit(); } diff --git a/src/library/zip.cpp b/src/library/zip.cpp index 462ea28c..c040f085 100644 --- a/src/library/zip.cpp +++ b/src/library/zip.cpp @@ -414,16 +414,27 @@ zip_writer::zip_writer(const std::string& zipfile, unsigned _compression) throw( compression = _compression; zipfile_path = zipfile; temp_path = zipfile + ".tmp"; - zipstream.open(temp_path.c_str(), std::ios::binary); - if(!zipstream) + zipstream = new std::ofstream(temp_path.c_str(), std::ios::binary); + if(!*zipstream) throw std::runtime_error("Can't open zipfile '" + temp_path + "' for writing"); committed = false; + system_stream = true; +} + +zip_writer::zip_writer(std::ostream& stream, unsigned _compression) throw(std::bad_alloc, std::runtime_error) +{ + compression = _compression; + zipstream = &stream; + committed = false; + system_stream = false; } zip_writer::~zip_writer() throw() { - if(!committed) + if(!committed && system_stream) remove(temp_path.c_str()); + if(system_stream) + delete zipstream; } void zip_writer::commit() throw(std::bad_alloc, std::logic_error, std::runtime_error) @@ -434,7 +445,7 @@ void zip_writer::commit() throw(std::bad_alloc, std::logic_error, std::runtime_e throw std::logic_error("Can't commit with file open"); std::vector directory_entry; uint32_t cdirsize = 0; - uint32_t cdiroff = zipstream.tellp(); + uint32_t cdiroff = zipstream->tellp(); if(cdiroff == (uint32_t)-1) throw std::runtime_error("Can't read current ZIP stream position"); for(auto i : files) { @@ -458,8 +469,8 @@ void zip_writer::commit() throw(std::bad_alloc, std::logic_error, std::runtime_e write32(&directory_entry[38], 0); write32(&directory_entry[42], i.second.offset); memcpy(&directory_entry[46], i.first.c_str(), i.first.length()); - zipstream.write(reinterpret_cast(&directory_entry[0]), directory_entry.size()); - if(!zipstream) + zipstream->write(reinterpret_cast(&directory_entry[0]), directory_entry.size()); + if(!*zipstream) throw std::runtime_error("Failed to write central directory entry to output file"); } directory_entry.resize(22); @@ -471,14 +482,16 @@ void zip_writer::commit() throw(std::bad_alloc, std::logic_error, std::runtime_e write32(&directory_entry[12], cdirsize); write32(&directory_entry[16], cdiroff); write16(&directory_entry[20], 0); - zipstream.write(reinterpret_cast(&directory_entry[0]), directory_entry.size()); - if(!zipstream) + zipstream->write(reinterpret_cast(&directory_entry[0]), directory_entry.size()); + if(!*zipstream) throw std::runtime_error("Failed to write central directory end marker to output file"); - zipstream.close(); - std::string backup = zipfile_path + ".backup"; - rename_file_overwrite(zipfile_path.c_str(), backup.c_str()); - if(rename_file_overwrite(temp_path.c_str(), zipfile_path.c_str()) < 0) - throw std::runtime_error("Can't rename '" + temp_path + "' -> '" + zipfile_path + "'"); + if(system_stream) { + dynamic_cast(zipstream)->close(); + std::string backup = zipfile_path + ".backup"; + rename_file_overwrite(zipfile_path.c_str(), backup.c_str()); + if(rename_file_overwrite(temp_path.c_str(), zipfile_path.c_str()) < 0) + throw std::runtime_error("Can't rename '" + temp_path + "' -> '" + zipfile_path + "'"); + } committed = true; } @@ -514,7 +527,7 @@ void zip_writer::close_file() throw(std::bad_alloc, std::logic_error, std::runti crc32 = f.crc32(); delete s; - base_offset = zipstream.tellp(); + base_offset = zipstream->tellp(); if(base_offset == (uint32_t)-1) throw std::runtime_error("Can't read current ZIP stream position"); unsigned char header[30]; @@ -529,10 +542,10 @@ void zip_writer::close_file() throw(std::bad_alloc, std::logic_error, std::runti write32(header + 18, cs); write32(header + 22, ucs); write16(header + 26, open_file.length()); - zipstream.write(reinterpret_cast(header), 30); - zipstream.write(open_file.c_str(), open_file.length()); - zipstream.write(¤t_compressed_file[0], current_compressed_file.size()); - if(!zipstream) + zipstream->write(reinterpret_cast(header), 30); + zipstream->write(open_file.c_str(), open_file.length()); + zipstream->write(¤t_compressed_file[0], current_compressed_file.size()); + if(!*zipstream) throw std::runtime_error("Can't write member to ZIP file"); current_compressed_file.resize(0); zip_file_info info;