| // Copyright 2020 Project U-Ray Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <vector> |
| #include <iostream> |
| #include <map> |
| #include <string> |
| #include <fstream> |
| #include <stdexcept> |
| #include <iterator> |
| #include <stdarg.h> |
| #include <unordered_set> |
| #include <unordered_map> |
| #include <set> |
| #include <sstream> |
| #include <algorithm> |
| #include <filesystem> |
| namespace fs = std::filesystem; |
| |
| struct FeatureData { |
| std::unordered_set<int> always_set_with_feature; |
| std::unordered_set<int> never_set_with_feature; |
| std::unordered_set<int> always_set_without_feature; |
| |
| std::vector<std::pair<int, bool>> featbits; |
| std::set<std::string> deps; |
| int count; |
| }; |
| |
| struct TileTypeData { |
| std::unordered_map<std::string, std::unordered_set<std::string>> inst_features; |
| std::unordered_map<std::string, std::unordered_set<int>> inst_bits; |
| std::unordered_set<int> settable_bits; |
| std::set<std::string> extant_features; |
| std::unordered_map<std::string, FeatureData> features; |
| }; |
| |
| std::map<std::string, TileTypeData> tiletypes; |
| std::set<std::string> include_tts; |
| |
| std::pair<std::string, std::string> split_tilename(const std::string &name) { |
| size_t idx = name.find(':'); |
| return std::make_pair(name.substr(0, idx), name.substr(idx+1)); |
| } |
| |
| void parse_bits(const std::string &prefix, std::istream &in) { |
| std::string line; |
| std::unordered_set<int> *bits = nullptr; |
| std::unordered_set<int> *settable_bits = nullptr; |
| bool skip = false; |
| |
| while (std::getline(in, line)) { |
| if (line.empty()) |
| continue; |
| std::istringstream iss(line); |
| if (line.front() == '.') { |
| std::string t, tn; |
| iss >> t >> tn; |
| auto spn = split_tilename(tn); |
| if (!include_tts.empty() && !include_tts.count(spn.second)) { |
| skip = true; |
| continue; |
| } else { |
| skip = false; |
| } |
| settable_bits = &(tiletypes[spn.second].settable_bits); |
| bits = &(tiletypes[spn.second].inst_bits[prefix + spn.first]); |
| } else { |
| int bit = -1; |
| iss >> bit; |
| if (!skip && bit != -1) { |
| bits->insert(bit); |
| settable_bits->insert(bit); |
| } |
| } |
| } |
| } |
| |
| void parse_features(const std::string &prefix, std::istream &in) { |
| std::string line; |
| std::unordered_set<std::string> *feats = nullptr; |
| std::set<std::string> *ext_feats = nullptr; |
| bool skip = false; |
| |
| while (std::getline(in, line)) { |
| if (line.empty()) |
| continue; |
| std::istringstream iss(line); |
| if (line.front() == '.') { |
| std::string t, tn; |
| iss >> t >> tn; |
| auto spn = split_tilename(tn); |
| if (!include_tts.empty() && !include_tts.count(spn.second)) { |
| skip = true; |
| continue; |
| } else { |
| skip = false; |
| } |
| ext_feats = &(tiletypes[spn.second].extant_features); |
| feats = &(tiletypes[spn.second].inst_features[prefix + spn.first]); |
| } else { |
| std::string feat; |
| iss >> feat; |
| if (!feat.empty() && !skip) { |
| feats->insert(feat); |
| ext_feats->insert(feat); |
| } |
| } |
| } |
| } |
| |
| template <typename Tc, typename Tv, typename Tf> void set_erase_if(Tc &target, std::vector<Tv> &temp, Tf pred) { |
| temp.clear(); |
| for (auto &entry : target) |
| if (pred(entry)) |
| temp.push_back(entry); |
| for (auto &toerase : temp) |
| target.erase(toerase); |
| } |
| |
| void find_feature_deps(TileTypeData &tt) { |
| for (auto &f : tt.extant_features) { |
| auto &fd = tt.features[f]; |
| fd.deps = tt.extant_features; |
| fd.deps.erase(f); |
| std::vector<std::string> temp; |
| for (auto &inst : tt.inst_features) { |
| if (!inst.second.count(f)) |
| continue; |
| set_erase_if(fd.deps, temp, [&](const std::string &ef){ return !inst.second.count(ef); }); |
| } |
| } |
| } |
| |
| void process_feature(TileTypeData &tt, const std::string &feature) { |
| |
| FeatureData &fd = tt.features[feature]; |
| |
| fd.always_set_with_feature = tt.settable_bits; |
| //fd.never_set_with_feature = tt.settable_bits; |
| fd.always_set_without_feature = tt.settable_bits; |
| |
| std::vector<int> temp; |
| fd.count = 0; |
| bool always_have_feature = true; |
| for (auto &inst : tt.inst_bits) { |
| if (!tt.inst_features.count(inst.first)) |
| continue; |
| auto &ib = inst.second; |
| bool has_feature = tt.inst_features.at(inst.first).count(feature); |
| if (!has_feature) |
| always_have_feature = false; |
| |
| if (has_feature) |
| ++fd.count; |
| |
| if (has_feature) |
| set_erase_if(fd.always_set_with_feature, temp, [&](int bit){ return !ib.count(bit); }); |
| //if (has_feature) |
| // set_erase_if(fd.never_set_with_feature, temp, [&](int bit){ return ib.count(bit); }); |
| if (!has_feature) |
| set_erase_if(fd.always_set_without_feature, temp, [&](int bit){ return !ib.count(bit); }); |
| } |
| for (int as : fd.always_set_with_feature) |
| if (always_have_feature || !fd.always_set_without_feature.count(as)) |
| if (std::all_of(fd.deps.begin(), fd.deps.end(), [&](const std::string &dep) { |
| auto &dd = tt.features[dep]; |
| return std::find(dd.featbits.begin(), dd.featbits.end(), std::make_pair(as, false)) == dd.featbits.end(); |
| })) |
| fd.featbits.emplace_back(as, false); |
| /*for (int nv : fd.never_set_with_feature) |
| if (fd.always_set_without_feature.count(nv)) |
| fd.featbits.emplace_back(nv, true);*/ |
| // FIXME: inverted feature bits? |
| std::sort(fd.featbits.begin(), fd.featbits.end()); |
| } |
| |
| int main(int argc, char *argv[]) { |
| if (argc < 3) { |
| std::cerr << "usage: correlate specfolder tiledata" << std::endl; |
| return 2; |
| } |
| |
| if (argc > 3) { |
| for (int i = 3; i < argc; i++) |
| include_tts.insert(argv[i]); |
| } |
| |
| for (const auto &entry : fs::directory_iterator(argv[1])) { |
| auto p = entry.path(); |
| if (p.extension() != ".features") |
| continue; |
| std::ifstream tilebits(p.parent_path().string() + "/" + p.stem().string() + ".tbits"); |
| if (!tilebits) { |
| std::cerr << "Failed to open " << (p.parent_path().string() + "/" + p.stem().string() + ".tbits") << std::endl; |
| return 1; |
| } |
| parse_bits(p.stem().string(), tilebits); |
| std::ifstream features(p.string()); |
| if (!features) { |
| std::cerr << "Failed to open " << p.string() << std::endl; |
| return 1; |
| } |
| parse_features(p.stem().string(), features); |
| } |
| |
| |
| |
| for (auto &tiletype : tiletypes) { |
| auto &t = tiletype.second; |
| std::ofstream td(std::string(argv[2]) + "/" + tiletype.first + ".bits"); |
| find_feature_deps(tiletype.second); |
| std::vector<std::string> ord_feats(t.extant_features.begin(), |
| t.extant_features.end()); |
| std::stable_sort(ord_feats.begin(), ord_feats.end(), [&](const std::string &a, const std::string &b) { |
| return t.features[a].deps.size() < t.features[b].deps.size(); |
| }); |
| |
| for (auto &feat : ord_feats) { |
| std::cerr << "Processing " << tiletype.first << "." << feat << std::endl; |
| FeatureData fd; |
| process_feature(t, feat); |
| } |
| |
| for (auto &feat : t.extant_features) { |
| auto &fd = t.features[feat]; |
| if (fd.count < 2) |
| continue; |
| td << feat; |
| for (auto &fb : fd.featbits) |
| td << " " << (fb.second ? "!" : "") << fb.first; |
| td << " # count: " << fd.count; |
| if (fd.deps.size() > 0) { |
| td << ", deps: "; |
| for (auto &d : fd.deps) |
| td << " " << d; |
| } |
| td << std::endl; |
| } |
| } |
| |
| return 0; |
| } |