From fa633aa6906f3b130b691568bcd20b2b76bb1cbb Mon Sep 17 00:00:00 2001 From: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> Date: Mon, 12 Jun 2023 18:30:23 +0200 Subject: [PATCH] streams: Teach AutoFile how to XOR --- src/streams.cpp | 32 +++++++++++++++++++++--- src/streams.h | 5 ++-- src/test/streams_tests.cpp | 50 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/streams.cpp b/src/streams.cpp index 16a8e517228..6921dad6773 100644 --- a/src/streams.cpp +++ b/src/streams.cpp @@ -5,10 +5,20 @@ #include #include +#include + std::size_t AutoFile::detail_fread(Span dst) { if (!m_file) throw std::ios_base::failure("AutoFile::read: file handle is nullptr"); - return std::fread(dst.data(), 1, dst.size(), m_file); + if (m_xor.empty()) { + return std::fread(dst.data(), 1, dst.size(), m_file); + } else { + const auto init_pos{std::ftell(m_file)}; + if (init_pos < 0) throw std::ios_base::failure("AutoFile::read: ftell failed"); + std::size_t ret{std::fread(dst.data(), 1, dst.size(), m_file)}; + util::Xor(dst.subspan(0, ret), m_xor, init_pos); + return ret; + } } void AutoFile::read(Span dst) @@ -34,7 +44,23 @@ void AutoFile::ignore(size_t nSize) void AutoFile::write(Span src) { if (!m_file) throw std::ios_base::failure("AutoFile::write: file handle is nullptr"); - if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) { - throw std::ios_base::failure("AutoFile::write: write failed"); + if (m_xor.empty()) { + if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) { + throw std::ios_base::failure("AutoFile::write: write failed"); + } + } else { + auto current_pos{std::ftell(m_file)}; + if (current_pos < 0) throw std::ios_base::failure("AutoFile::write: ftell failed"); + std::array buf; + while (src.size() > 0) { + auto buf_now{Span{buf}.first(std::min(src.size(), buf.size()))}; + std::copy(src.begin(), src.begin() + buf_now.size(), buf_now.begin()); + util::Xor(buf_now, m_xor, current_pos); + if (std::fwrite(buf_now.data(), 1, buf_now.size(), m_file) != buf_now.size()) { + throw std::ios_base::failure{"XorFile::write: failed"}; + } + src = src.subspan(buf_now.size()); + current_pos += buf_now.size(); + } } } diff --git a/src/streams.h b/src/streams.h index 27875775a7f..5ff952be76d 100644 --- a/src/streams.h +++ b/src/streams.h @@ -482,9 +482,10 @@ class AutoFile { protected: std::FILE* m_file; + const std::vector m_xor; public: - explicit AutoFile(std::FILE* file) : m_file{file} {} + explicit AutoFile(std::FILE* file, std::vector data_xor={}) : m_file{file}, m_xor{std::move(data_xor)} {} ~AutoFile() { fclose(); } @@ -553,7 +554,7 @@ private: const int nVersion; public: - CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn) : AutoFile{filenew}, nType(nTypeIn), nVersion(nVersionIn) {} + explicit CAutoFile(std::FILE* file, int type, int version, std::vector data_xor = {}) : AutoFile{file, std::move(data_xor)}, nType{type}, nVersion{version} {} int GetType() const { return nType; } int GetVersion() const { return nVersion; } diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 55e4f200b1c..52321758245 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -13,6 +14,55 @@ using namespace std::string_literals; BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(xor_file) +{ + fs::path xor_path{m_args.GetDataDirBase() / "test_xor.bin"}; + auto raw_file{[&](const auto& mode) { return fsbridge::fopen(xor_path, mode); }}; + const std::vector test1{1, 2, 3}; + const std::vector test2{4, 5}; + const std::vector xor_pat{std::byte{0xff}, std::byte{0x00}}; + { + // Check errors for missing file + AutoFile xor_file{raw_file("rb"), xor_pat}; + BOOST_CHECK_EXCEPTION(xor_file << std::byte{}, std::ios_base::failure, HasReason{"AutoFile::write: file handle is nullpt"}); + BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: file handle is nullpt"}); + BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: file handle is nullpt"}); + } + { + AutoFile xor_file{raw_file("wbx"), xor_pat}; + xor_file << test1 << test2; + } + { + // Read raw from disk + AutoFile non_xor_file{raw_file("rb")}; + std::vector raw(7); + non_xor_file >> Span{raw}; + BOOST_CHECK_EQUAL(HexStr(raw), "fc01fd03fd04fa"); + // Check that no padding exists + BOOST_CHECK_EXCEPTION(non_xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: end of file"}); + } + { + AutoFile xor_file{raw_file("rb"), xor_pat}; + std::vector read1, read2; + xor_file >> read1 >> read2; + BOOST_CHECK_EQUAL(HexStr(read1), HexStr(test1)); + BOOST_CHECK_EQUAL(HexStr(read2), HexStr(test2)); + // Check that eof was reached + BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: end of file"}); + } + { + AutoFile xor_file{raw_file("rb"), xor_pat}; + std::vector read2; + // Check that ignore works + xor_file.ignore(4); + xor_file >> read2; + BOOST_CHECK_EQUAL(HexStr(read2), HexStr(test2)); + // Check that ignore and read fail now + BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: end of file"}); + BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: end of file"}); + } +} + BOOST_AUTO_TEST_CASE(streams_vector_writer) { unsigned char a(1);