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.
326 lines
9.3 KiB
C++
326 lines
9.3 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>
|
|
|
|
// 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.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");
|
|
}
|
|
|
|
// 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: %n", (int)type);
|
|
}
|
|
pAsset->mAssetDir = mpProject->GetAssetDirectory();
|
|
|
|
// 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()
|
|
{
|
|
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();
|
|
|
|
// 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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::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;
|
|
}
|
|
|
|
delete iter->second;
|
|
mAssets.erase(iter);
|
|
}
|
|
|
|
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();
|
|
|
|
default: return nullptr;
|
|
}
|
|
}
|
|
|
|
void ContentManager::FreeAssets()
|
|
{
|
|
for (auto iter = mAssets.begin(); iter != mAssets.end(); iter++)
|
|
{
|
|
delete iter->second;
|
|
}
|
|
|
|
mAssets.clear();
|
|
}
|
|
}} |