/* Starshatter: The Open Source Project Copyright (c) 2021-2022, Starshatter: The Open Source Project Contributors Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors Copyright (c) 1997-2006, Destroyer Studios LLC. */ #include "DataSource.h" #include #include #include #include #include "Archive.h" #include "List.h" #include "Text.h" #include "Utils.h" int DataSource::s_next_id {0}; DataSource::DataSource() : m_id {s_next_id++} { } DataSource::~DataSource() { } int DataSource::Id() const { return m_id; } ArchiveDataSource::ArchiveDataSource(const char* name) : ArchiveDataSource(new DataArchive(name)) { } ArchiveDataSource::ArchiveDataSource(DataArchive* archive) : DataSource(), m_archive {archive} { } ArchiveDataSource::~ArchiveDataSource() { delete m_archive; } bool ArchiveDataSource::Find(const Text& prefix, const char* name) const { const int index = m_archive->FindEntry(prefix.concat(name)); return index > -1; } int ArchiveDataSource::ListFiles(const Text& prefix, Text filter, List& items, bool recurse) const { (void) recurse; // Lookup in DataArchives was always recursive so far filter = filter.replace("*", ""); // Wildcards worked only on boundaries const int first = prefix.length(); const int count = m_archive->NumFiles(); for (int i = 0; i < count; ++i) { const auto* entry = m_archive->GetFile(i); Text name = entry->name; name.setSensitive(false); if (name.contains(prefix) && name.contains(filter)) { const auto without_prefix = name.substring(first, name.length()); if (!items.contains(&without_prefix)) items.append(new Text(without_prefix)); } } return items.size(); } int ArchiveDataSource::Load(const Text& prefix, const char* name, std::uint8_t*& buf, bool null_terminate) const { const int index = m_archive->FindEntry(prefix.concat(name)); if (index > -1) return m_archive->ExpandEntry(index, buf, null_terminate); return 0; // -1 would be preferable, but 0 is from legacy } FileSystemDataSource::FileSystemDataSource(const char* path) : DataSource(), m_path {path} { } FileSystemDataSource::~FileSystemDataSource() { } bool FileSystemDataSource::Find(const Text& prefix, const char* name) const { std::filesystem::path full_path {m_path}; full_path.append(prefix.data()); full_path.append(name); return std::filesystem::is_regular_file(full_path); } int FileSystemDataSource::ListFiles(const Text& prefix, Text filter, List& items, bool recurse) const { std::filesystem::path full_path {m_path}; full_path.append(prefix.data()); filter = filter.replace("*", ""); const auto first = full_path.string().length(); const auto check = [&](const std::filesystem::directory_entry& entry){ const auto filename = entry.path().filename().string(); // more of a reason to switch to string soon const auto index = filename.find(filter.data()); if (index != decltype(filename)::npos) { Text path = entry.path().string().c_str(); items.append(new Text(path.substring(first, path.length()))); } }; try { if (recurse) { for (const auto& entry : std::filesystem::recursive_directory_iterator(full_path)) check(entry); } else { for (const auto& entry : std::filesystem::directory_iterator(full_path)) check(entry); } } catch (const std::filesystem::filesystem_error& err) { Print("FS::ListFiles: %s\n", err.what()); } return items.size(); } int FileSystemDataSource::Load(const Text& prefix, const char* name, std::uint8_t*& buf, bool null_terminate) const { std::filesystem::path full_path {m_path}; full_path.append(prefix.data()); full_path.append(name); auto f = std::fopen(full_path.string().c_str(), "rb"); if (!f) return 0; // Again, -1 would be better to differentiate an error from an empty file std::fseek(f, 0, SEEK_END); int length = std::ftell(f); std::fseek(f, 0, SEEK_SET); if (null_terminate) { buf = new std::uint8_t[length + 1]; if (buf) buf[length] = 0; } else { buf = new std::uint8_t[length]; } int bytes = 0; if (buf) bytes = std::fread(buf, length, 1, f); std::fclose(f); return bytes; }