#include "spt.h" #include "m2_tokvec.h" // read_file #include #include #include namespace fmt { namespace { bool ends_with_ci(const std::string& s, const char* suf) { size_t n = std::strlen(suf); if (s.size() < n) return false; for (size_t i = 0; i < n; ++i) if (std::tolower((unsigned char)s[s.size() - n + i]) != std::tolower((unsigned char)suf[i])) return false; return true; } std::string token_ci(const std::string& bytes, const char* needle) { const size_t n = std::strlen(needle); if (n == 0 || bytes.size() < n) return ""; for (size_t i = 0; i + n <= bytes.size(); ++i) { bool match = true; for (size_t j = 0; j < n; ++j) { if (std::tolower((unsigned char)bytes[i + j]) != std::tolower((unsigned char)needle[j])) { match = false; break; } } if (!match) continue; size_t e = i + n; while (e < bytes.size()) { const unsigned char c = (unsigned char)bytes[e]; if (!(std::isalnum(c) || c == '_' || c == '-')) break; ++e; } return bytes.substr(i, e - i); } return ""; } std::string as_dds(std::string s) { if (s.empty()) return s; const size_t dot = s.find_last_of('.'); if (dot != std::string::npos) s.resize(dot); return s + ".dds"; } } // namespace bool sniff_spt(const std::string& b, SptInfo& out) { out = SptInfo{}; if (b.size() < 20) return false; std::memcpy(&out.header_dword, b.data(), 4); uint32_t maglen = 0; std::memcpy(&maglen, b.data() + 4, 4); if (maglen >= 4 && maglen <= 32 && 8 + maglen <= b.size()) { out.magic.assign(b.data() + 8, b.data() + 8 + maglen); // 去掉可能的尾随 NUL while (!out.magic.empty() && out.magic.back() == '\0') out.magic.pop_back(); } out.ok = out.magic.rfind("__IdvSpt", 0) == 0; // 扫描可打印 ASCII 段,收 .tga / .dds 结尾的 std::string cur; auto flush = [&] { if (cur.size() >= 5 && (ends_with_ci(cur, ".tga") || ends_with_ci(cur, ".dds"))) { // 只留文件名基础部分之后的路径(原样) out.texture_refs.push_back(cur); } cur.clear(); }; for (unsigned char c : b) { if (c >= 0x20 && c < 0x7F) { cur += (char)c; } else { flush(); } } flush(); // 这两个名字在真实 v2 .spt 中常紧跟二进制标记,不会由上面的 // "字符串必须正好以扩展名结尾" 规则捕获。 out.composite_texture = as_dds(token_ci(b, "CompositeMap")); out.self_shadow_texture = as_dds(token_ci(b, "CompositeShadowMap")); return out.ok; } bool sniff_spt_file(const std::string& path, SptInfo& out) { std::string b; if (!read_file(path, b)) return false; return sniff_spt(b, out); } } // namespace fmt