Use open/read/write/close for binary saves instead of iostreams

This commit is contained in:
Ilari Liusvaara 2014-04-10 20:51:43 +03:00
parent 0e0989b568
commit 4c952beb3b
7 changed files with 197 additions and 65 deletions

View file

@ -36,7 +36,7 @@ struct moviefile_branch_extractor_binary : public moviefile::branch_extractor
std::set<std::string> enumerate();
void read(const std::string& name, controller_frame_vector& v);
private:
std::ifstream s;
int s;
};
#endif

View file

@ -33,7 +33,7 @@ struct moviefile
uint64_t rerecords;
private:
void load(zip::reader& r);
void binary_io(std::istream& s);
void binary_io(int s);
};
/**
* Extract branches.
@ -274,8 +274,8 @@ struct moviefile
private:
moviefile(const moviefile&);
moviefile& operator=(const moviefile&);
void binary_io(std::ostream& stream, rrdata_set& rrd) 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(int stream, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error);
void binary_io(int stream, struct core_type& romtype) throw(std::bad_alloc, std::runtime_error);
void save(zip::writer& w, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error);
void load(zip::reader& r, core_type& romtype) throw(std::bad_alloc, std::runtime_error);
};

View file

@ -26,7 +26,7 @@ public:
*
* Parameter s: The stream to output to.
*/
output(std::ostream& s);
output(int s);
/**
* Output a byte to stream (1 byte).
*
@ -130,8 +130,9 @@ public:
*/
std::string get();
private:
std::ostream& strm;
std::ostringstream buf;
inline void write(const char* buf, size_t size);
int strm;
std::vector<char> buf;
};
/**
@ -159,7 +160,7 @@ public:
/**
* Create a new top-level input stream, reading from specified stream.
*/
input(std::istream& s);
input(int s);
/**
* Create a new input substream, under specified top-level stream and with specified length.
*
@ -251,7 +252,7 @@ private:
bool read(char* buf, size_t size, bool allow_none = false);
void flush();
input* parent;
std::istream& strm;
int strm;
uint64_t left;
};

View file

@ -10,8 +10,18 @@
#include "library/serialization.hpp"
#include "library/binarystream.hpp"
#include "interface/romtype.hpp"
#include <cstring>
#include <cerrno>
#include <fcntl.h>
#include <unistd.h>
#if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
//FUCK YOU. SERIOUSLY.
#define EXTRA_OPENFLAGS O_BINARY
#else
#define EXTRA_OPENFLAGS 0
#endif
void moviefile::brief_info::binary_io(std::istream& _stream)
void moviefile::brief_info::binary_io(int _stream)
{
binarystream::input in(_stream);
sysregion = in.string();
@ -50,7 +60,7 @@ void moviefile::brief_info::binary_io(std::istream& _stream)
}, binarystream::null_default);
}
void moviefile::binary_io(std::ostream& _stream, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error)
void moviefile::binary_io(int _stream, rrdata_set& rrd) throw(std::bad_alloc, std::runtime_error)
{
binarystream::output out(_stream);
out.string(gametype->get_name());
@ -184,7 +194,7 @@ void moviefile::binary_io(std::ostream& _stream, rrdata_set& rrd) throw(std::bad
}
}
void moviefile::binary_io(std::istream& _stream, core_type& romtype) throw(std::bad_alloc, std::runtime_error)
void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc, std::runtime_error)
{
binarystream::input in(_stream);
std::string tmp = in.string();
@ -289,9 +299,11 @@ void moviefile::binary_io(std::istream& _stream, core_type& romtype) throw(std::
moviefile_branch_extractor_binary::moviefile_branch_extractor_binary(const std::string& filename)
{
s.open(filename);
if(!s)
(stringfmt() << "Can't open file '" << filename << "' for reading").throwex();
s = open(filename.c_str(), O_RDONLY | EXTRA_OPENFLAGS);
if(s < 0) {
int err = errno;
(stringfmt() << "Can't open file '" << filename << "' for reading: " << strerror(err)).throwex();
}
}
moviefile_branch_extractor_binary::~moviefile_branch_extractor_binary()
@ -302,9 +314,10 @@ std::set<std::string> moviefile_branch_extractor_binary::enumerate()
{
std::set<std::string> r;
std::string name;
s.seekg(5);
if(!s)
(stringfmt() << "Can't read the file").throwex();
if(lseek(s, 5, SEEK_SET) < 0) {
int err = errno;
(stringfmt() << "Can't read the file: " << strerror(err)).throwex();
}
binarystream::input b(s);
//Skip the headers.
b.string();
@ -329,9 +342,10 @@ std::set<std::string> moviefile_branch_extractor_binary::enumerate()
void moviefile_branch_extractor_binary::read(const std::string& name, controller_frame_vector& v)
{
std::string mname;
s.seekg(5);
if(!s)
(stringfmt() << "Can't read the file").throwex();
if(lseek(s, 5, SEEK_SET) < 0) {
int err = errno;
(stringfmt() << "Can't read the file: " << strerror(err)).throwex();
}
binarystream::input b(s);
bool done = false;
//Skip the headers.

View file

@ -7,6 +7,10 @@
#include <unistd.h>
#if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
#include <windows.h>
//FUCK YOU. SERIOUSLY.
#define EXTRA_OPENFLAGS O_BINARY
#else
#define EXTRA_OPENFLAGS 0
#endif
namespace
@ -134,7 +138,7 @@ name_again:
strcpy(filename_buf + strlen(filename_buf), "-");
append_number(filename_buf + strlen(filename_buf), number++);
strcpy(filename_buf + strlen(filename_buf), ".lsmv");
fd = open(filename_buf, O_WRONLY | O_CREAT | O_EXCL, 0666);
fd = open(filename_buf, O_WRONLY | O_CREAT | O_EXCL | EXTRA_OPENFLAGS, 0666);
if(fd < 0 && errno == EEXIST) goto name_again;
if(fd < 0) return; //Can't open.
//Headers.

View file

@ -19,11 +19,53 @@
#include <boost/iostreams/device/back_inserter.hpp>
#if defined(_WIN32) || defined(_WIN64) || defined(TEST_WIN32_CODE)
#include <windows.h>
//FUCK YOU. SERIOUSLY.
#define EXTRA_OPENFLAGS O_BINARY
#else
#define EXTRA_OPENFLAGS 0
#endif
//Damn Windows.
#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif
namespace
{
std::map<std::string, moviefile*> memory_saves;
bool check_binary_magic(int s)
{
char buf[6] = {0};
int x = 0;
while(x < 5) {
int r = read(s, buf + x, 5 - x);
if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
continue;
if(r <= 0) //0 => EOF, break on that too.
return false;
x += r;
}
return !strcmp(buf, "lsmv\x1A");
}
void write_whole(int s, const char* buf, size_t size)
{
size_t w = 0;
while(w < size) {
int maxw = 32767;
if((size_t)maxw > (size - w))
maxw = size - w;
int r = write(s, buf + w, maxw);
if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
continue;
if(r < 0) {
int err = errno;
(stringfmt() << strerror(err)).throwex();
}
w += r;
}
}
}
moviefile::brief_info::brief_info(const std::string& filename)
@ -46,15 +88,17 @@ moviefile::brief_info::brief_info(const std::string& filename)
return;
}
{
std::istream& s = zip::openrel(filename, "");
char buf[6] = {0};
s.read(buf, 5);
if(!strcmp(buf, "lsmv\x1A")) {
binary_io(s);
delete &s;
int s = open(filename.c_str(), O_RDONLY | EXTRA_OPENFLAGS);
if(s < 0) {
int err = errno;
(stringfmt() << "Can't read file '" << filename << "': " << strerror(err)).throwex();
}
if(check_binary_magic(s)) {
try { binary_io(s); } catch(...) { close(s); throw; }
close(s);
return;
}
delete &s;
close(s);
}
zip::reader r(filename);
load(r);
@ -125,15 +169,17 @@ moviefile::moviefile(const std::string& movie, core_type& romtype) throw(std::ba
is_savestate = false;
lazy_project_create = false;
{
std::istream& s = zip::openrel(movie, "");
char buf[6] = {0};
s.read(buf, 5);
if(!strcmp(buf, "lsmv\x1A")) {
binary_io(s, romtype);
delete &s;
int s = open(movie.c_str(), O_RDONLY | EXTRA_OPENFLAGS);
if(s < 0) {
int err = errno;
(stringfmt() << "Can't read file '" << movie << "': " << strerror(err)).throwex();
}
if(check_binary_magic(s)) {
try { binary_io(s, romtype); } catch(...) { close(s); throw; }
close(s);
return;
}
delete &s;
close(s);
}
zip::reader r(movie);
load(r, romtype);
@ -164,17 +210,23 @@ void moviefile::save(const std::string& movie, unsigned compression, bool binary
}
if(binary) {
std::string tmp = movie + ".tmp";
std::ofstream strm(tmp.c_str(), std::ios_base::binary);
if(!strm)
throw std::runtime_error("Can't open output file");
char buf[5] = {'l', 's', 'm', 'v', 0x1A};
strm.write(buf, 5);
if(!strm)
throw std::runtime_error("Failed to write to output file");
binary_io(strm, rrd);
if(!strm)
throw std::runtime_error("Failed to write to output file");
strm.close();
int strm = open(tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC | EXTRA_OPENFLAGS, 0644);
if(strm < 0) {
int err = errno;
(stringfmt() << "Failed to open '" << tmp << "': " << strerror(err)).throwex();
}
try {
char buf[5] = {'l', 's', 'm', 'v', 0x1A};
write_whole(strm, buf, 5);
binary_io(strm, rrd);
} catch(std::exception& e) {
close(strm);
(stringfmt() << "Failed to write '" << tmp << "': " << e.what()).throwex();
}
if(close(strm) < 0) {
int err = errno;
(stringfmt() << "Failed to write '" << tmp << "': " << strerror(err)).throwex();
}
std::string backup = movie + ".backup";
zip::rename_overwrite(movie.c_str(), backup.c_str());
if(zip::rename_overwrite(tmp.c_str(), movie.c_str()) < 0)

View file

@ -1,28 +1,77 @@
#include "binarystream.hpp"
#include "serialization.hpp"
#include "minmax.hpp"
#include "string.hpp"
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>
#include <unistd.h>
//Damn Windows.
#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif
namespace
{
void write_whole(int s, const char* buf, size_t size)
{
size_t w = 0;
while(w < size) {
int maxw = 32767;
if((size_t)maxw > (size - w))
maxw = size - w;
int r = write(s, buf + w, maxw);
if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
continue;
if(r < 0) {
int err = errno;
(stringfmt() << strerror(err)).throwex();
}
w += r;
}
}
size_t whole_read(int s, char* buf, size_t size)
{
size_t r = 0;
while(r < size) {
int maxr = 32767;
if((size_t)maxr > (size - r))
maxr = size - r;
int x = read(s, buf + r, maxr);
if(x < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
continue;
if(x < 0) {
int err = errno;
(stringfmt() << strerror(err)).throwex();
}
if(x == 0)
break; //EOF.
r += x;
}
return r;
}
}
namespace binarystream
{
const uint32_t TAG_ = 0xaddb2d86;
output::output()
: strm(buf)
: strm(-1)
{
}
output::output(std::ostream& s)
output::output(int s)
: strm(s)
{
}
void output::byte(uint8_t byte)
{
strm.write(reinterpret_cast<char*>(&byte), 1);
write(reinterpret_cast<char*>(&byte), 1);
}
void output::number(uint64_t number)
@ -34,7 +83,7 @@ void output::number(uint64_t number)
data[len++] = (cont ? 0x80 : 0x00) | (number & 0x7F);
number >>= 7;
} while(number);
strm.write(data, len);
write(data, len);
}
size_t output::numberbytes(uint64_t number)
@ -57,28 +106,30 @@ void output::number32(uint32_t number)
{
char data[4];
serialization::u32b(data, number);
strm.write(data, 4);
write(data, 4);
}
void output::string(const std::string& string)
{
number(string.length());
std::copy(string.begin(), string.end(), std::ostream_iterator<char>(strm));
std::vector<char> tmp(string.begin(), string.end());
write(&tmp[0], tmp.size());
}
void output::string_implicit(const std::string& string)
{
std::copy(string.begin(), string.end(), std::ostream_iterator<char>(strm));
std::vector<char> tmp(string.begin(), string.end());
write(&tmp[0], tmp.size());
}
void output::blob_implicit(const std::vector<char>& blob)
{
strm.write(&blob[0], blob.size());
write(&blob[0], blob.size());
}
void output::raw(const void* buf, size_t bufsize)
{
strm.write(reinterpret_cast<const char*>(buf), bufsize);
write(reinterpret_cast<const char*>(buf), bufsize);
}
void output::write_extension_tag(uint32_t tag, uint64_t size)
@ -92,12 +143,12 @@ void output::extension(uint32_t tag, std::function<void(output&)> fn, bool even_
{
output tmp;
fn(tmp);
std::string str = tmp.get();
if(!even_empty && !str.length())
if(!even_empty && !tmp.buf.size())
return;
number32(TAG_);
number32(tag);
string(str);
number(tmp.buf.size());
blob_implicit(tmp.buf);
}
void output::extension(uint32_t tag, std::function<void(output&)> fn, bool even_empty,
@ -111,12 +162,22 @@ void output::extension(uint32_t tag, std::function<void(output&)> fn, bool even_
fn(*this);
}
void output::write(const char* ibuf, size_t size)
{
if(strm >= 0)
write_whole(strm, ibuf, size);
else {
size_t o = buf.size();
buf.resize(o + size);
memcpy(&buf[o], ibuf, size);
}
}
std::string output::get()
{
if(&strm != &buf)
if(strm >= 0)
throw std::logic_error("Get can only be used without explicit sink");
return buf.str();
return std::string(buf.begin(), buf.end());
}
uint8_t input::byte()
@ -175,7 +236,7 @@ void input::blob_implicit(std::vector<char>& blob)
read(&blob[0], left);
}
input::input(std::istream& s)
input::input(int s)
: parent(NULL), strm(s), left(0)
{
}
@ -240,9 +301,9 @@ bool input::read(char* buf, size_t size, bool allow_none)
parent->read(buf, size, false);
left -= size;
} else {
strm.read(buf, size);
if(!strm) {
if(!strm.gcount() && allow_none)
size_t r = whole_read(strm, buf, size);
if(r < size) {
if(!r && allow_none)
return false;
throw std::runtime_error("Unexpected EOF");
}