/****************************************************************************** * File - content_manager.cpp * Author - Joey Pollack * Date - 2022/02/22 (y/m/d) * Mod Date - 2022/02/22 (y/m/d) * Description - Keeps track of all resource files in the project. * Reads/Writes meta-data to the contents_meta.xml file. * Also manages the physical location of each asset file. ******************************************************************************/ #include "content_manager.h" #include #include "../project.h" #include #include "editor_asset.h" // Asset types #include "tile_set.h" namespace lunarium { namespace editor { ContentManager* ContentManager::mpInstance = nullptr; ContentManager::ContentManager() : mpProject(nullptr), mNextID(0) { } ContentManager& ContentManager::GetInstance() { if (!mpInstance) { mpInstance = new ContentManager(); } return *mpInstance; } void ContentManager::FreeInstance() { if (mpInstance) { delete mpInstance; mpInstance = nullptr; } } OpRes ContentManager::Load(Project* project) { Unload(); mpProject = project; std::filesystem::path root = mpProject->GetRootDirectory(); mContentFile = root / "contents/content_meta.xml"; // If this is a new project being generated just create a base contents file if (!std::filesystem::exists(mContentFile)) { // pugi::xml_node proj_node = doc.append_child("Project"); // proj_node.append_attribute("Name").set_value(mpProject->GetName().c_str()); // mNextID = 0; // proj_node.append_child("NextID").append_attribute("ID").set_value(mNextID); // // pugi::xml_node proj_node = doc.append_child("Contents"); // proj_node.append_child("Contents"); // if (!doc.save_file(mContentFile.string().c_str())) // { // return OpRes::Fail("ContentManager could not save file: %s", mContentFile.string().c_str()); // } return Save(); } // Load the file pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(mContentFile.string().c_str()); if (result.status != pugi::xml_parse_status::status_ok) { return OpRes::Fail("Could not open contents file: %s, %s", mContentFile.string().c_str(), result.description()); } pugi::xml_node proj_node = doc.child("Project"); if (!proj_node) { return OpRes::Fail("content_meta.xml missing Project root node"); } // Get ID pugi::xml_node ID = proj_node.child("NextID"); if (!ID) { return OpRes::Fail("content_meta.xml missing NextID node"); } mNextID = ID.attribute("ID").as_ullong(); pugi::xml_node contents = proj_node.child("Contents"); if (!contents) { return OpRes::Fail("content_meta.xml missing Contents node"); } // Iterate through content for (pugi::xml_node asset = contents.child("Asset"); asset; asset = asset.next_sibling("Asset")) { if (!IsValidAsset(asset)) { return OpRes::Fail("Invalid asset node in content_meta.xml"); } // Load Type AssetType type = (AssetType)asset.attribute("Type").as_int(); EditorAsset* pAsset = CreateAsset(type); pAsset->mAssetDir = mpProject->GetAssetDirectory(); if (!pAsset) { return OpRes::Fail("Could not create Editor Asset. Unknown type: %n", (int)type); } // Load ID pAsset->mID = (uint64_t)asset.attribute("ID").as_ullong(); // Load Location pAsset->mLocation = std::filesystem::path(asset.attribute("Location").as_string()); // Load type specific data if (Failed(pAsset->LoadFromXML(asset).LogIfFailed(Editor::LogCat))) { return OpRes::Fail("Could not load asset type specific data for asset with ID: %llu, File: %s", pAsset->GetID(), pAsset->GetFileLocation().filename().string().c_str()); } // Store asset if (mAssets.find(pAsset->mID) != mAssets.end()) { return OpRes::Fail("Asset ID collision, ID: %llu, File: %s", pAsset->GetID(), pAsset->GetFileLocation().filename().string().c_str()); } mAssets[pAsset->mID] = pAsset; } return OpRes::OK(); } OpRes ContentManager::Save() { if (!mpProject) { return OpRes::Fail("ConentManager::Save failed: no project set"); } // Save header info pugi::xml_document doc; pugi::xml_node proj_node = doc.append_child("Project"); proj_node.append_attribute("Name").set_value(mpProject->GetName().c_str()); proj_node.append_child("NextID").append_attribute("ID").set_value(mNextID); // pugi::xml_node proj_node = doc.append_child("Contents"); // Save all assets pugi::xml_node contents = proj_node.append_child("Contents"); for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++) { EditorAsset* pAsset = iter->second; pugi::xml_node asset = contents.append_child("Asset"); asset.append_attribute("Type").set_value((int)pAsset->GetType()); asset.append_attribute("ID").set_value(pAsset->GetID()); asset.append_attribute("Location").set_value(pAsset->GetFileLocation().string().c_str()); if (Failed(pAsset->SaveToXML(asset).LogIfFailed(Editor::LogCat))) { return OpRes::Fail("Could not save asset meta data for file: %s", pAsset->GetFileLocation().string().c_str()); } } if (!doc.save_file(mContentFile.string().c_str())) { return OpRes::Fail("ContentManager could not save file: %s", mContentFile.string().c_str()); } return OpRes::OK(); } void ContentManager::Unload() { mpProject = nullptr; FreeAssets(); mContentFile = ""; mNextID = 0; } void ContentManager::GetAllAssetIDs(std::vector& container) const { container.clear(); for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++) { container.push_back(iter->first); } } void ContentManager::GetAllAssetsByType(std::vector& container, AssetType type) const { container.clear(); for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++) { if (iter->second->GetType() == type) { container.push_back(iter->second); } } } EditorAsset* ContentManager::GetAsset(uint64_t id) { auto iter = mAssets.find(id); if (iter == mAssets.end()) { return nullptr; } return iter->second; } OpRes ContentManager::AddGeneratedAsset(EditorAsset* asset, uint64_t& id) { if (mAssets.find(mNextID) != mAssets.end()) { return OpRes::Fail("Could not import asset, ID collision. ID: %llu", mNextID); } asset->mID = mNextID; mNextID++; mAssets[asset->mID] = asset; id = asset->mID; return OpRes::OK(); } OpRes ContentManager::ImportFile(std::filesystem::path file, std::filesystem::path to_location, AssetType type, uint64_t& id) { if (mAssets.find(mNextID) != mAssets.end()) { return OpRes::Fail("Could not import asset, ID collision. ID: %llu", mNextID); } if (!std::filesystem::copy_file(file, to_location)) { return OpRes::Fail("Could not copy asset file from: %s to: %s", file.string().c_str(), to_location.string().c_str()); } EditorAsset* pAsset = CreateAsset(type); pAsset->mAssetDir = mpProject->GetAssetDirectory(); pAsset->mID = mNextID; mNextID++; pAsset->mLocation = to_location; if (Failed(pAsset->LoadRawFile().LogIfFailed(Editor::LogCat))) { delete pAsset; return OpRes::Fail("Could not load asset data from raw file: %s", file.string().c_str()); } mAssets[pAsset->mID] = pAsset; id = pAsset->mID; return OpRes::OK(); } void ContentManager::RemoveAsset(uint64_t asset_id) { auto iter = mAssets.find(asset_id); if (iter == mAssets.end()) { return; } delete iter->second; mAssets.erase(iter); } bool ContentManager::IsValidAsset(pugi::xml_node& node) { if (!node.attribute("ID")) { return false; } if (!node.attribute("Type")) { return false; } if (!node.attribute("Location")) { return false; } return true; } EditorAsset* ContentManager::CreateAsset(AssetType type) { switch (type) { case AssetType::EATYPE_TILE_SET: return new TileSet(); default: return nullptr; } } void ContentManager::FreeAssets() { for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++) { delete iter->second; } mAssets.clear(); } }}