#pragma once #include "ccl/tools/JSON.h" #include "xtr/doc/FileVersions.hpp" #include "CArchiveAdapter.hpp" using JSON = nlohmann::ordered_json; inline void to_json(JSON& object, const CString str) { object = xtr::mfc::ToSTL(str); } inline void from_json(const JSON& object, CString& str) { const auto result = object.get(); str = xtr::mfc::ToMFC(result); } namespace xtr::io { //! MFC serializable abstract base template class MFCSerializable { inline static const std::string jsonDocumentName = "document.json"; public: void Serialize(CArchive& ar) { CArchiveAdapter adapter{ ar }; ASSERT(!ar.IsStoring()); if (!LegacyLoad(adapter)) { AfxThrowArchiveException(CArchiveException::genericException); } } bool SaveJSON(const std::u8string& path) { auto json = ExtractData(); json["version"] = static_cast(Version::Latest()); json["versionInfo"] = Version::LastestInfo(); return WriteToFile(json.dump(4), path); } bool LoadJSON(const std::u8string& path) { const auto data = ReadFromFile(path); if (!data.has_value()) { return false; } const Version versionID = static_cast(data->at("version").get()); return Version::Latest() >= versionID && LoadData(data.value()); } private: virtual JSON ExtractData() const = 0; virtual bool LoadData(const JSON& object) = 0; virtual bool LegacyLoad(CArchiveAdapter& ar) = 0; bool WriteToFile(std::string_view data, const std::u8string& path) { try { std::ofstream outputFile(std::filesystem::path(path), std::ios::binary); Poco::Zip::Compress archive(outputFile, true); Poco::MemoryInputStream inputStream(data.data(), size(data)); archive.addFile(inputStream, Poco::DateTime(), jsonDocumentName); archive.close(); outputFile.close(); } catch (const Poco::Exception& /*error*/) { return false; } return true; } std::optional ReadFromFile(const std::u8string& path) { namespace fs = std::filesystem; auto tmpDirectory = fs::temp_directory_path() / "exteor" / fs::path(path).filename().replace_extension(""); std::ifstream inputFile(fs::path(path), std::ios::binary); if (!inputFile) { return std::nullopt; } try { const auto u8folder = tmpDirectory.u8string(); const auto outputFolder = Poco::Path(std::string{ begin(u8folder), end(u8folder) }); Poco::Zip::Decompress(inputFile, outputFolder).decompressAllFiles(); inputFile.close(); } catch (const Poco::Exception& /*error*/) { return std::nullopt; } std::ifstream jsonFile(tmpDirectory / jsonDocumentName); if (!jsonFile) { fs::remove_all(tmpDirectory); return std::nullopt; } else { auto jsonData = JSON::parse(jsonFile); jsonFile.close(); fs::remove_all(tmpDirectory); return jsonData; } } }; } // namespace xtr::io