#include "amuse/Common.hpp" #include "logvisor/logvisor.hpp" using namespace std::literals; namespace amuse { static logvisor::Module Log("amuse"); bool Copy(const SystemChar* from, const SystemChar* to) { #if _WIN32 return CopyFileW(from, to, FALSE) != 0; #else FILE* fi = fopen(from, "rb"); if (!fi) return false; FILE* fo = fopen(to, "wb"); if (!fo) { fclose(fi); return false; } std::unique_ptr buf(new uint8_t[65536]); size_t readSz = 0; while ((readSz = fread(buf.get(), 1, 65536, fi))) fwrite(buf.get(), 1, readSz, fo); fclose(fi); fclose(fo); struct stat theStat; if (::stat(from, &theStat)) return true; #if __APPLE__ struct timespec times[] = { theStat.st_atimespec, theStat.st_mtimespec }; #else struct timespec times[] = { theStat.st_atim, theStat.st_mtim }; #endif utimensat(AT_FDCWD, to, times, 0); return true; #endif } #define DEFINE_ID_TYPE(type, typeName) \ thread_local NameDB* type::CurNameDB = nullptr; \ template<> template<> \ void type##DNA::Enumerate(athena::io::IStreamReader& reader) \ { \ id = reader.readUint16Little(); \ } \ template<> template<> \ void type##DNA::Enumerate(athena::io::IStreamWriter& writer) \ { \ writer.writeUint16Little(id); \ } \ template<> template<> \ void type##DNA::Enumerate(size_t& sz) \ { \ sz += 2; \ } \ template<> template<> \ void type##DNA::Enumerate(athena::io::YAMLDocReader& reader) \ { \ _read(reader); \ } \ template<> template<> \ void type##DNA::Enumerate(athena::io::YAMLDocWriter& writer) \ { \ _write(writer); \ } \ template<> template<> \ void type##DNA::Enumerate(athena::io::IStreamReader& reader) \ { \ id = reader.readUint16Big(); \ } \ template<> template<> \ void type##DNA::Enumerate(athena::io::IStreamWriter& writer) \ { \ writer.writeUint16Big(id); \ } \ template<> template<> \ void type##DNA::Enumerate(size_t& sz) \ { \ sz += 2; \ } \ template<> template<> \ void type##DNA::Enumerate(athena::io::YAMLDocReader& reader) \ { \ _read(reader); \ } \ template<> template<> \ void type##DNA::Enumerate(athena::io::YAMLDocWriter& writer) \ { \ _write(writer); \ } \ template \ void type##DNA::_read(athena::io::YAMLDocReader& r) \ { \ std::string name = r.readString(nullptr); \ if (!type::CurNameDB) \ Log.report(logvisor::Fatal, "Unable to resolve " typeName " name %s, no database present", name.c_str()); \ if (name.empty()) \ { \ id.id = 0xffff; \ return; \ } \ id = type::CurNameDB->resolveIdFromName(name); \ } \ template \ void type##DNA::_write(athena::io::YAMLDocWriter& w) \ { \ if (!type::CurNameDB) \ Log.report(logvisor::Fatal, "Unable to resolve " typeName " ID %d, no database present", id.id); \ if (id.id == 0xffff) \ return; \ std::string_view name = type::CurNameDB->resolveNameFromId(id); \ if (!name.empty()) \ w.writeString(nullptr, name); \ } \ template \ const char* type##DNA::DNAType() \ { \ return "amuse::" #type "DNA"; \ } \ template struct type##DNA; \ template struct type##DNA; DEFINE_ID_TYPE(ObjectId, "object") DEFINE_ID_TYPE(SoundMacroId, "SoundMacro") DEFINE_ID_TYPE(SampleId, "sample") DEFINE_ID_TYPE(TableId, "table") DEFINE_ID_TYPE(KeymapId, "keymap") DEFINE_ID_TYPE(LayersId, "layers") DEFINE_ID_TYPE(SongId, "song") DEFINE_ID_TYPE(SFXId, "sfx") DEFINE_ID_TYPE(GroupId, "group") template<> template<> void PageObjectIdDNA::Enumerate(athena::io::IStreamReader& reader) { id = reader.readUint16Little(); } template<> template<> void PageObjectIdDNA::Enumerate(athena::io::IStreamWriter& writer) { writer.writeUint16Little(id); } template<> template<> void PageObjectIdDNA::Enumerate(size_t& sz) { sz += 2; } template<> template<> void PageObjectIdDNA::Enumerate(athena::io::YAMLDocReader& reader) { _read(reader); } template<> template<> void PageObjectIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) { _write(writer); } template<> template<> void PageObjectIdDNA::Enumerate(athena::io::IStreamReader& reader) { id = reader.readUint16Big(); } template<> template<> void PageObjectIdDNA::Enumerate(athena::io::IStreamWriter& writer) { writer.writeUint16Big(id); } template<> template<> void PageObjectIdDNA::Enumerate(size_t& sz) { sz += 2; } template<> template<> void PageObjectIdDNA::Enumerate(athena::io::YAMLDocReader& reader) { _read(reader); } template<> template<> void PageObjectIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) { _write(writer); } template void PageObjectIdDNA::_read(athena::io::YAMLDocReader& r) { std::string name = r.readString(nullptr); if (!KeymapId::CurNameDB || !LayersId::CurNameDB) Log.report(logvisor::Fatal, "Unable to resolve keymap or layers name %s, no database present", name.c_str()); if (name.empty()) { id.id = 0xffff; return; } auto search = KeymapId::CurNameDB->m_stringToId.find(name); if (search == KeymapId::CurNameDB->m_stringToId.cend()) { search = LayersId::CurNameDB->m_stringToId.find(name); if (search == LayersId::CurNameDB->m_stringToId.cend()) { search = SoundMacroId::CurNameDB->m_stringToId.find(name); if (search == SoundMacroId::CurNameDB->m_stringToId.cend()) { Log.report(logvisor::Error, "Unable to resolve name %s", name.c_str()); id.id = 0xffff; return; } } } id = search->second; } template void PageObjectIdDNA::_write(athena::io::YAMLDocWriter& w) { if (!KeymapId::CurNameDB || !LayersId::CurNameDB) Log.report(logvisor::Fatal, "Unable to resolve keymap or layers ID %d, no database present", id.id); if (id.id == 0xffff) return; if (id.id & 0x8000) { std::string_view name = LayersId::CurNameDB->resolveNameFromId(id); if (!name.empty()) w.writeString(nullptr, name); } else if (id.id & 0x4000) { std::string_view name = KeymapId::CurNameDB->resolveNameFromId(id); if (!name.empty()) w.writeString(nullptr, name); } else { std::string_view name = SoundMacroId::CurNameDB->resolveNameFromId(id); if (!name.empty()) w.writeString(nullptr, name); } } template const char* PageObjectIdDNA::DNAType() { return "amuse::PageObjectIdDNA"; } template struct PageObjectIdDNA; template struct PageObjectIdDNA; template<> template<> void SoundMacroStepDNA::Enumerate(athena::io::IStreamReader& reader) { step = reader.readUint16Little(); } template<> template<> void SoundMacroStepDNA::Enumerate(athena::io::IStreamWriter& writer) { writer.writeUint16Little(step); } template<> template<> void SoundMacroStepDNA::Enumerate(size_t& sz) { sz += 2; } template<> template<> void SoundMacroStepDNA::Enumerate(athena::io::YAMLDocReader& reader) { step = reader.readUint16(nullptr); } template<> template<> void SoundMacroStepDNA::Enumerate(athena::io::YAMLDocWriter& writer) { writer.writeUint16(nullptr, step); } template<> template<> void SoundMacroStepDNA::Enumerate(athena::io::IStreamReader& reader) { step = reader.readUint16Big(); } template<> template<> void SoundMacroStepDNA::Enumerate(athena::io::IStreamWriter& writer) { writer.writeUint16Big(step); } template<> template<> void SoundMacroStepDNA::Enumerate(size_t& sz) { sz += 2; } template<> template<> void SoundMacroStepDNA::Enumerate(athena::io::YAMLDocReader& reader) { step = reader.readUint16(nullptr); } template<> template<> void SoundMacroStepDNA::Enumerate(athena::io::YAMLDocWriter& writer) { writer.writeUint16(nullptr, step); } template const char* SoundMacroStepDNA::DNAType() { return "amuse::SoundMacroStepDNA"; } template struct SoundMacroStepDNA; template struct SoundMacroStepDNA; ObjectId NameDB::generateId(Type tp) const { uint16_t maxMatch = 0; if (tp == Type::Layer) maxMatch = 0x8000; else if (tp == Type::Keymap) maxMatch = 0x4000; for (const auto& p : m_idToString) if (p.first >= maxMatch) maxMatch = p.first + 1; return maxMatch; } std::string NameDB::generateName(ObjectId id, Type tp) { char name[32]; switch (tp) { case Type::SoundMacro: snprintf(name, 32, "macro%04X", id.id); break; case Type::Table: snprintf(name, 32, "table%04X", id.id); break; case Type::Keymap: snprintf(name, 32, "keymap%04X", id.id); break; case Type::Layer: snprintf(name, 32, "layers%04X", id.id); break; case Type::Song: snprintf(name, 32, "song%04X", id.id); break; case Type::SFX: snprintf(name, 32, "sfx%04X", id.id); break; case Type::Group: snprintf(name, 32, "group%04X", id.id); break; case Type::Sample: snprintf(name, 32, "sample%04X", id.id); break; default: snprintf(name, 32, "obj%04X", id.id); break; } return name; } std::string NameDB::generateDefaultName(Type tp) const { return generateName(generateId(tp), tp); } std::string_view NameDB::registerPair(std::string_view str, ObjectId id) { m_stringToId[std::string(str)] = id; return m_idToString.insert(std::make_pair(id, str)).first->second; } std::string_view NameDB::resolveNameFromId(ObjectId id) const { auto search = m_idToString.find(id); if (search == m_idToString.cend()) { Log.report(logvisor::Error, "Unable to resolve ID 0x%04X", id.id); return ""sv; } return search->second; } ObjectId NameDB::resolveIdFromName(std::string_view str) const { auto search = m_stringToId.find(std::string(str)); if (search == m_stringToId.cend()) { Log.report(logvisor::Error, "Unable to resolve name %s", str.data()); return {}; } return search->second; } void NameDB::remove(ObjectId id) { auto search = m_idToString.find(id); if (search == m_idToString.cend()) return; auto search2 = m_stringToId.find(search->second); if (search2 == m_stringToId.cend()) return; m_idToString.erase(search); m_stringToId.erase(search2); } void NameDB::rename(ObjectId id, std::string_view str) { auto search = m_idToString.find(id); if (search == m_idToString.cend()) return; if (search->second == str) return; auto search2 = m_stringToId.find(search->second); if (search2 == m_stringToId.cend()) return; #if __APPLE__ std::swap(m_stringToId[std::string(str)], search2->second); m_stringToId.erase(search2); #else auto nh = m_stringToId.extract(search2); nh.key() = str; m_stringToId.insert(std::move(nh)); #endif search->second = str; } template<> void LittleUInt24::Enumerate(athena::io::IStreamReader& reader) { union { atUint32 val; char bytes[4]; } data = {}; reader.readBytesToBuf(data.bytes, 3); val = SLittle(data.val); } template<> void LittleUInt24::Enumerate(athena::io::IStreamWriter& writer) { union { atUint32 val; char bytes[4]; } data; data.val = SLittle(val); writer.writeBytes(data.bytes, 3); } template<> void LittleUInt24::Enumerate(size_t& sz) { sz += 3; } template<> void LittleUInt24::Enumerate(athena::io::YAMLDocReader& reader) { val = reader.readUint32(nullptr); } template<> void LittleUInt24::Enumerate(athena::io::YAMLDocWriter& writer) { writer.writeUint32(nullptr, val); } const char* LittleUInt24::DNAType() { return "amuse::LittleUInt24"; } }