#include "pack_mount.h" #include #include namespace fs = std::filesystem; namespace mtpack { std::string PackMount::norm(std::string s) { std::replace(s.begin(), s.end(), '\\', '/'); // strip a leading "x:/" drive if (s.size() > 2 && s[1] == ':') { s = s.substr(2); } while (!s.empty() && s.front() == '/') { s.erase(s.begin()); } std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); // fold "//" std::string o; o.reserve(s.size()); for (char c : s) { if (c == '/' && !o.empty() && o.back() == '/') { continue; } o.push_back(c); } return o; } bool PackMount::mount(const std::string &epk_path, std::string *err) { auto pk = std::make_shared(); if (!pk->open(epk_path, err)) { return false; } const size_t pi = m_packs.size(); m_packs.push_back(pk); for (const std::string &name : pk->names()) { std::string n = norm(name); m_by_norm[n] = {pi, name}; // later pack wins auto p = n.find("ymir work/"); if (p != std::string::npos) { m_by_ymir[n.substr(p + 10)] = {pi, name}; } } return true; } int PackMount::scan_dir(const std::string &dir, const std::string &patch_prefix) { std::error_code ec; if (!fs::is_directory(dir, ec)) { return 0; } std::vector base, patch; for (const auto &e : fs::directory_iterator(dir, ec)) { if (!e.is_regular_file()) { continue; } const auto &p = e.path(); if (p.extension() != ".epk") { continue; } if (!patch_prefix.empty() && p.filename().string().rfind(patch_prefix, 0) == 0) { patch.push_back(p.string()); } else { base.push_back(p.string()); } } std::sort(base.begin(), base.end()); std::sort(patch.begin(), patch.end()); int n = 0; for (const auto &v : {&base, &patch}) { for (const std::string &f : *v) { if (mount(f)) { ++n; } } } return n; } bool PackMount::has(const std::string &vp) const { std::string n = norm(vp); if (m_by_norm.count(n)) { return true; } auto p = n.find("ymir work/"); std::string suf = (p != std::string::npos) ? n.substr(p + 10) : n; return m_by_ymir.count(suf) != 0; } bool PackMount::read(const std::string &vp, std::vector &out, std::string *err) const { std::string n = norm(vp); const Ref *r = nullptr; if (auto it = m_by_norm.find(n); it != m_by_norm.end()) { r = &it->second; } else { auto p = n.find("ymir work/"); std::string suf = (p != std::string::npos) ? n.substr(p + 10) : n; if (auto it2 = m_by_ymir.find(suf); it2 != m_by_ymir.end()) { r = &it2->second; } } if (!r) { if (err) *err = "not mounted: " + vp; return false; } return m_packs[r->pack]->read(r->name, out, err); } } // namespace mtpack