sql_importer_file.cpp (4677B)
1 #include "scraps/format/rags_sql/sql_importer_private.hpp" // IWYU pragma: associated 2 3 #include "scraps/format/archive.hpp" 4 #include "scraps/format/rags_sql/test_helper/sql_helper.hpp" 5 6 #include <libshit/doctest.hpp> 7 #include <libshit/except.hpp> 8 #include <libshit/low_io.hpp> 9 #include <libshit/memory_utils.hpp> 10 #include <libshit/utils.hpp> 11 #include <libshit/nonowning_string.hpp> 12 13 #include <capnp/list.h> 14 15 #include <sstream> 16 #include <string> 17 18 // IWYU pragma: no_forward_declare capnp::List 19 20 namespace Scraps::Format::RagsSql 21 { 22 TEST_SUITE_BEGIN("Scraps::Format::RagsSql"); 23 24 template <typename T, typename U, typename V> 25 static void ImportFileCommon(Importer& imp, T init, U append, V fini) 26 { 27 auto [file_orphan, groups] = imp.ImportGroups("MediaGroups"); 28 imp.game.AdoptFileGroups(Libshit::Move(file_orphan)); 29 30 Importer::NameSet id_set; 31 auto file = imp.game.InitFiles(SCRAPS_CAPNP_LIST_LEN(imp.file_ids.size())); 32 imp.sql.Query("SELECT [Name], [GroupName], [Data] FROM [Media];", 3); 33 for (std::uint32_t i = 0, n = imp.file_ids.size(); i < n; ++i) 34 { 35 imp.sql.AssertHasRow(); 36 auto name = Importer::GetUniqueName(id_set, imp.sql.GetWString()); 37 file[i].SetId(imp.file_ids.at(name)); 38 file[i].SetName(imp.sp.InternCopy(name)); 39 file[i].SetGroupId( 40 Importer::FindId(groups, imp.sql.GetWString(), "file group", "")); 41 42 auto data_info = imp.sql.GetCellInfo(); 43 if (data_info.status != Sql::DbStatus::OK) 44 LIBSHIT_THROW(SqlError, "Failed to get file data", 45 "Status", Sql::ToString(data_info.status)); 46 if (data_info.len != Sql::CellInfo::STREAM) 47 LIBSHIT_THROW(SqlError, "Media is not blob", "Length", data_info.len); 48 49 init(file[i], name); 50 51 char buf[4096]; 52 while (auto got = imp.sql.GetChunk(buf, sizeof(buf))) 53 append(Libshit::StringView{buf, got}); 54 fini(file[i]); 55 } 56 imp.sql.AssertNoRow(); 57 } 58 59 void Importer::ImportFile(ArchiveWriter& aw) 60 { 61 std::uint64_t len; 62 ImportFileCommon( 63 *this, [&](Proto::File::Builder file, const auto& name) 64 { 65 file.InitData(); 66 file.GetData().SetOffset(aw.Tell()); 67 len = 0; 68 }, [&](auto chunk) { aw.Append(chunk); len += chunk.size(); }, 69 [&](auto media) { media.GetData().SetSize(len); }); 70 } 71 72 TEST_CASE("ImportFile to archive") 73 { 74 TestHelper::TestSqls([](Sql& sql, Memory::DB& db) 75 { 76 using C = Memory::Cell; 77 std::string big(4096*4 + 123, 'a'); 78 db.tables["Media"] = { 79 { 80 { "Name", Memory::Type::NVARCHAR, 250 }, 81 { "Data", Memory::Type::IMAGE }, 82 { "GroupName", Memory::Type::NVARCHAR, 255 }, 83 }, { 84 { C::Ntext("foo.jpg"), C::Image("jpeg data..."), C::Ntext("") }, 85 { C::Ntext(u8"bar 猫.mp4"), C::Image(big), C::Ntext("gggrp") }, 86 } 87 }; 88 db.tables["MediaGroups"] = { 89 { 90 { "Name", Memory::Type::NVARCHAR, 255 }, 91 { "Parent", Memory::Type::NVARCHAR, 255 }, 92 }, { 93 { C::Ntext("gggrp"), C::Ntext("") }, 94 } 95 }; 96 97 Importer imp{sql}; 98 imp.GenIds(imp.file_ids, "Media", "Name"); // 2-3 99 auto sstream_moved = Libshit::MakeUnique<std::stringstream>(); 100 auto ss = sstream_moved.get(); 101 ArchiveWriter aw{Libshit::Move(sstream_moved)}; 102 imp.ImportFile(aw); // group: 4 103 104 std::string expected_content(3*8, '\0'); 105 expected_content += "jpeg data..."; 106 expected_content += big; 107 CHECK(ss->str() == expected_content); 108 109 auto file = imp.game.GetFiles(); 110 REQUIRE(file.size() == 2); 111 112 CHECK(file[0].GetId() == 2); 113 CHECK(imp.sp.Get(file[0].GetName()) == "foo.jpg"); 114 CHECK(file[0].GetGroupId() == 0); 115 REQUIRE(file[0].IsData()); 116 CHECK(file[0].GetData().GetOffset() == 3*8); 117 CHECK(file[0].GetData().GetSize() == 12); 118 119 CHECK(file[1].GetId() == 3); 120 CHECK(imp.sp.Get(file[1].GetName()) == u8"bar 猫.mp4"); 121 CHECK(file[1].GetGroupId() == 4); 122 REQUIRE(file[1].IsData()); 123 CHECK(file[1].GetData().GetOffset() == 3*8 + 12); 124 CHECK(file[1].GetData().GetSize() == 4096*4 + 123); 125 }); 126 } 127 128 void Importer::ImportFile(Libshit::StringView dirname) 129 { 130 Libshit::LowIo io; 131 ImportFileCommon( 132 *this, [&](Proto::File::Builder file, auto&& name) 133 { 134 auto [new_name, io2] = UniqueName( 135 dirname, NormalizeFilename(Libshit::Move(name))); 136 io = Libshit::Move(io2); 137 file.SetFileName(sp.InternString(Libshit::Move(new_name))); 138 }, [&](auto chunk) { io.Write(chunk.data(), chunk.size()); }, 139 [](auto) {}); 140 } 141 142 TEST_SUITE_END(); 143 }