Use open/read/write/close for binary saves instead of iostreams
This commit is contained in:
parent
0e0989b568
commit
4c952beb3b
7 changed files with 197 additions and 65 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue