You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lunarium_OLD/src/run_modes/editor/contents/content_manager.cpp

379 lines
11 KiB
C++

/******************************************************************************
* 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 <core/common_defs.h>
#include "editor_asset.h"
#include "../project.h"
#include <editor/editor.h>
#include <fstream>
#include <utils/logger.h>
// Asset types
#include "tile_set.h"
#include "world.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.json";
// If this is a new project being generated just create a base contents file
if (!std::filesystem::exists(mContentFile))
{
return Save();
}
std::ifstream ifs = std::ifstream(mContentFile.string().c_str());
if (!ifs.is_open())
{
return OpRes::Fail("Could not open contents file: %s", mContentFile.string().c_str());
}
nlohmann::json j;
ifs >> j;
ifs.close();
auto p = j["Project"];
if (p.is_null())
{
return OpRes::Fail("content_meta.json missing Project element");
}
if (p["NextID"].is_null())
{
return OpRes::Fail("content_meta.xml missing NextID node");
}
mNextID = p["NextID"].get<u64>();
auto c = p["Contents"];
if (c.is_null())
{
return OpRes::OK();
// Not actually an error if a project does not have any content yet
//return OpRes::Fail("content_meta.xml missing Contents node");
}
for (auto it = c.begin(); it != c.end(); ++it)
{
auto& asset = (*it);
if (!IsValidAsset(asset))
{
return OpRes::Fail("Invalid asset node in content_meta.json");
}
// Check if asset is trashed
if (asset["Trashed"].get<bool>())
{
// Skip trashed assets
continue;
}
// Load Type
AssetType type = (AssetType)asset["Type"].get<int>();
EditorAsset* pAsset = CreateAsset(type);
if (!pAsset)
{
return OpRes::Fail("Could not create Editor Asset. Unknown type: %d", (int)type);
}
pAsset->mAssetDir = mpProject->GetAssetDirectory();
// If we got to this point the asset must not be trashed
pAsset->mIsTrashed = false;
// Load ID
pAsset->mID = asset["ID"].get<u64>();
// Load Location
pAsset->mLocation = std::filesystem::path(asset["Location"].get<std::string>());
// Load type specific data
if (Failed(pAsset->LoadFromJSON(asset).LogIfFailed(Editor::LogCat).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
auto iter = mAssets.find(pAsset->mID);
if (iter != mAssets.end())
{
return OpRes::Fail("Asset ID collision, First asset: ID: %llu, File: %s, Second asset: ID: %llu, File: %s",
iter->second->GetID(), iter->second->GetFileLocation().filename().string().c_str(),
pAsset->GetID(), pAsset->GetFileLocation().filename().string().c_str());
}
mAssets[pAsset->mID] = pAsset;
}
return OpRes::OK();
}
OpRes ContentManager::Save()
{
Logger::Info(Editor::LogCat, "Updating content_meta.json...");
if (!mpProject)
{
return OpRes::Fail("ConentManager::Save failed: no project set");
}
nlohmann::ordered_json oj;
auto& p = oj["Project"];
// Save header info
p["Name"] = mpProject->GetName();
p["NextID"] = mNextID;
// Save all assets
auto& c = p["Contents"];
for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++)
{
EditorAsset* pAsset = iter->second;
nlohmann::json asset;
asset["Type"] = (i32)pAsset->GetType();
asset["ID"] = pAsset->GetID();
asset["Trashed"] = pAsset->GetIsTrashed();
// TODO: This needs to be a relative path! (Relative to the project root)
asset["Location"] = pAsset->GetFileLocation().string().c_str();
if (Failed(pAsset->SaveToJSON(asset).LogIfFailed(Editor::LogCat).LogIfFailed(Editor::LogCat)))
{
return OpRes::Fail("Could not save asset meta data for file: %s", pAsset->GetFileLocation().string().c_str());
}
//c.push_back(asset);
c.emplace_back(asset);
}
std::ofstream ofs = std::ofstream(mContentFile.string().c_str(), std::ios_base::trunc);
if (!ofs.is_open())
{
return OpRes::Fail("ContentManager could not save file: %s - could not open file", mContentFile.string().c_str());
}
ofs << std::setw(4) << oj;
ofs.close();
return OpRes::OK();
}
void ContentManager::Unload()
{
mpProject = nullptr;
FreeAssets();
mContentFile = "";
mNextID = 0;
}
void ContentManager::GetAllAssetIDs(std::vector<uint64_t>& container) const
{
container.clear();
for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++)
{
container.push_back(iter->first);
}
}
void ContentManager::GetAllAssetsByType(std::vector<EditorAsset*>& 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);
}
}
}
void ContentManager::GetAllAssetsInDirectory(std::vector<EditorAsset*>& container, std::filesystem::path dir)
{
container.clear();
for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++)
{
auto temp = iter->second->GetFileLocation().remove_filename().parent_path();
if (temp == dir)
{
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;
Save().LogIfFailed(Editor::LogCat, "Asset was created");
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::exists(to_location))
{
std::filesystem::remove(to_location);
}
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 = mpProject->MakeRelativeToAssets(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;
}
// Move the asset to a trash directory to prevent data loss
iter->second->mIsTrashed = true;
MoveAsset(iter->second, mpProject->GetTrashDirectory());
delete iter->second;
mAssets.erase(iter);
Save().LogIfFailed(Editor::LogCat, "Asset was removed");
}
void ContentManager::MoveAsset(EditorAsset* asset, std::filesystem::path to)
{
std::filesystem::path from = mpProject->GetAssetDirectory() / asset->GetFileLocation();
// Not all assets will have files associated with them
if (std::filesystem::exists(from))
{
std::filesystem::rename(from, to);
}
std::filesystem::path r = mpProject->MakeRelativeToAssets(to);
asset->mLocation = r;
Save().LogIfFailed(Editor::LogCat, "Asset was moved");
}
bool ContentManager::IsValidAsset(nlohmann::json& node)
{
if (node.is_null()) { return false; }
if (node["ID"].is_null()) { return false; }
if (!node["ID"].is_number_unsigned()) { return false; }
if (node["Type"].is_null()) { return false; }
if (!node["Type"].is_number()) { return false; }
if (node["Location"].is_null()) { return false; }
return true;
}
EditorAsset* ContentManager::CreateAsset(AssetType type)
{
switch (type)
{
case AssetType::EATYPE_TILE_SET: return new TileSet();
case AssetType::EATYPE_WORLD: return new editor::World();
default: return nullptr;
}
}
void ContentManager::FreeAssets()
{
for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++)
{
delete iter->second;
}
mAssets.clear();
}
}}