Zip: Allow output to arbitrary ostream

This commit is contained in:
Ilari Liusvaara 2013-11-03 16:13:04 +02:00
parent 3ab4db9aeb
commit 1ac35c2773
4 changed files with 53 additions and 23 deletions

View file

@ -8,7 +8,7 @@
#include "core/controllerframe.hpp" #include "core/controllerframe.hpp"
#include "core/rom.hpp" #include "core/rom.hpp"
#include "core/subtitles.hpp" #include "core/subtitles.hpp"
#include "library/zip.hpp"
/** /**
* This structure gives parsed representationg of movie file, as result of decoding or for encoding. * 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, void save(const std::string& filename, unsigned compression, bool binary) throw(std::bad_alloc,
std::runtime_error); 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. * Force loading as corrupt.
*/ */
@ -213,6 +216,7 @@ struct moviefile
private: private:
void binary_io(std::ostream& stream) throw(std::bad_alloc, std::runtime_error); 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 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 #endif

View file

@ -7,6 +7,7 @@
#include <string> #include <string>
#include <map> #include <map>
#include <fstream> #include <fstream>
#include <sstream>
#include <zlib.h> #include <zlib.h>
/** /**
@ -297,12 +298,13 @@ public:
* Creates new empty ZIP archive. The members will be compressed according to specified compression. * Creates new empty ZIP archive. The members will be compressed according to specified compression.
* *
* parameter zipfile: The zipfile to create. * 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. * parameter _compression: Compression. 0 is uncompressed, 1-9 are deflate compression levels.
* throws std::bad_alloc: Not enough memory. * throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Can't open archive or invalid argument. * 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(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). * Destroys ZIP writer, aborting the transaction (unless commit() has been called).
*/ */
@ -347,7 +349,8 @@ private:
zip_writer(zip_writer&); zip_writer(zip_writer&);
zip_writer& operator=(zip_writer&); zip_writer& operator=(zip_writer&);
std::ofstream zipstream; std::ostream* zipstream;
bool system_stream;
std::string temp_path; std::string temp_path;
std::string zipfile_path; std::string zipfile_path;
std::string open_file; std::string open_file;

View file

@ -703,6 +703,17 @@ void moviefile::save(const std::string& movie, unsigned compression, bool binary
return; return;
} }
zip_writer w(movie, compression); 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_linefile(w, "gametype", gametype->get_name());
write_settings<zip_writer>(w, settings, gametype->get_type().get_settings(), [](zip_writer& w, write_settings<zip_writer>(w, settings, gametype->get_type().get_settings(), [](zip_writer& w,
const std::string& name, const std::string& value) -> void { 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_raw_file(w, "initram." + i.first, i.second);
write_authors_file(w, authors); write_authors_file(w, authors);
write_input(w, input); write_input(w, input);
w.commit(); w.commit();
} }

View file

@ -414,16 +414,27 @@ zip_writer::zip_writer(const std::string& zipfile, unsigned _compression) throw(
compression = _compression; compression = _compression;
zipfile_path = zipfile; zipfile_path = zipfile;
temp_path = zipfile + ".tmp"; temp_path = zipfile + ".tmp";
zipstream.open(temp_path.c_str(), std::ios::binary); zipstream = new std::ofstream(temp_path.c_str(), std::ios::binary);
if(!zipstream) if(!*zipstream)
throw std::runtime_error("Can't open zipfile '" + temp_path + "' for writing"); throw std::runtime_error("Can't open zipfile '" + temp_path + "' for writing");
committed = false; 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() zip_writer::~zip_writer() throw()
{ {
if(!committed) if(!committed && system_stream)
remove(temp_path.c_str()); remove(temp_path.c_str());
if(system_stream)
delete zipstream;
} }
void zip_writer::commit() throw(std::bad_alloc, std::logic_error, std::runtime_error) 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"); throw std::logic_error("Can't commit with file open");
std::vector<unsigned char> directory_entry; std::vector<unsigned char> directory_entry;
uint32_t cdirsize = 0; uint32_t cdirsize = 0;
uint32_t cdiroff = zipstream.tellp(); uint32_t cdiroff = zipstream->tellp();
if(cdiroff == (uint32_t)-1) if(cdiroff == (uint32_t)-1)
throw std::runtime_error("Can't read current ZIP stream position"); throw std::runtime_error("Can't read current ZIP stream position");
for(auto i : files) { 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[38], 0);
write32(&directory_entry[42], i.second.offset); write32(&directory_entry[42], i.second.offset);
memcpy(&directory_entry[46], i.first.c_str(), i.first.length()); memcpy(&directory_entry[46], i.first.c_str(), i.first.length());
zipstream.write(reinterpret_cast<char*>(&directory_entry[0]), directory_entry.size()); zipstream->write(reinterpret_cast<char*>(&directory_entry[0]), directory_entry.size());
if(!zipstream) if(!*zipstream)
throw std::runtime_error("Failed to write central directory entry to output file"); throw std::runtime_error("Failed to write central directory entry to output file");
} }
directory_entry.resize(22); 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[12], cdirsize);
write32(&directory_entry[16], cdiroff); write32(&directory_entry[16], cdiroff);
write16(&directory_entry[20], 0); write16(&directory_entry[20], 0);
zipstream.write(reinterpret_cast<char*>(&directory_entry[0]), directory_entry.size()); zipstream->write(reinterpret_cast<char*>(&directory_entry[0]), directory_entry.size());
if(!zipstream) if(!*zipstream)
throw std::runtime_error("Failed to write central directory end marker to output file"); throw std::runtime_error("Failed to write central directory end marker to output file");
zipstream.close(); if(system_stream) {
std::string backup = zipfile_path + ".backup"; dynamic_cast<std::ofstream*>(zipstream)->close();
rename_file_overwrite(zipfile_path.c_str(), backup.c_str()); std::string backup = zipfile_path + ".backup";
if(rename_file_overwrite(temp_path.c_str(), zipfile_path.c_str()) < 0) rename_file_overwrite(zipfile_path.c_str(), backup.c_str());
throw std::runtime_error("Can't rename '" + temp_path + "' -> '" + zipfile_path + "'"); 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; committed = true;
} }
@ -514,7 +527,7 @@ void zip_writer::close_file() throw(std::bad_alloc, std::logic_error, std::runti
crc32 = f.crc32(); crc32 = f.crc32();
delete s; delete s;
base_offset = zipstream.tellp(); base_offset = zipstream->tellp();
if(base_offset == (uint32_t)-1) if(base_offset == (uint32_t)-1)
throw std::runtime_error("Can't read current ZIP stream position"); throw std::runtime_error("Can't read current ZIP stream position");
unsigned char header[30]; 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 + 18, cs);
write32(header + 22, ucs); write32(header + 22, ucs);
write16(header + 26, open_file.length()); write16(header + 26, open_file.length());
zipstream.write(reinterpret_cast<char*>(header), 30); zipstream->write(reinterpret_cast<char*>(header), 30);
zipstream.write(open_file.c_str(), open_file.length()); zipstream->write(open_file.c_str(), open_file.length());
zipstream.write(&current_compressed_file[0], current_compressed_file.size()); zipstream->write(&current_compressed_file[0], current_compressed_file.size());
if(!zipstream) if(!*zipstream)
throw std::runtime_error("Can't write member to ZIP file"); throw std::runtime_error("Can't write member to ZIP file");
current_compressed_file.resize(0); current_compressed_file.resize(0);
zip_file_info info; zip_file_info info;