Factorised more code in LabraryManager and StoryProject, used widely

This commit is contained in:
Anthony Rabine 2024-01-16 13:18:59 +01:00
parent 4bcb8e4490
commit 6a610200d0
12 changed files with 135 additions and 187 deletions

View file

@ -1,9 +1,11 @@
#include "library_manager.h"
#include "tlv.h"
#include <filesystem>
#include <regex>
#include "json.hpp"
#include "story_project.h"
#include "uuid.h"
LibraryManager::LibraryManager() {}
@ -13,14 +15,6 @@ void LibraryManager::Initialize(const std::string &library_path)
Scan();
}
bool IsUUIDV4(const std::string& input) {
// Le motif regex pour un UUID V4
std::regex uuidRegex("^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$",std::regex_constants::icase);
// Vérifier si la chaîne correspond au motif UUID V4
return std::regex_match(input, uuidRegex);
}
void LibraryManager::Scan()
{
std::filesystem::path directoryPath(m_library_path);
@ -33,7 +27,7 @@ void LibraryManager::Scan()
{
// Si c'est un sous-répertoire, récursivement scanner le contenu
std::string uuid = entry.path().filename();
if (IsUUIDV4(uuid))
if (UUID::IsValid(uuid))
{
std::cout << "Found story directory" << std::endl;
// Look for a story.json file in this directory
@ -49,6 +43,7 @@ void LibraryManager::Scan()
if (proj->ParseStoryInformation(j))
{
// Valid project file, add it to the list
proj->SetPaths(uuid, m_library_path);
m_projectsList.push_back(proj);
}
}
@ -63,6 +58,32 @@ void LibraryManager::Scan()
}
}
std::shared_ptr<StoryProject> LibraryManager::NewProject()
{
auto story = std::make_shared<StoryProject>();
std::string uuid = UUID().String();
story->New(uuid, m_library_path);
story->SetDisplayFormat(320, 240);
story->SetImageFormat(StoryProject::IMG_FORMAT_QOIF);
story->SetSoundFormat(StoryProject::SND_FORMAT_WAV);
story->SetName("New project");
return story;
}
std::shared_ptr<StoryProject> LibraryManager::GetStory(const std::string &uuid)
{
std::shared_ptr<StoryProject> current;
for (const auto &s : m_projectsList)
{
if (s->GetUuid() == uuid)
{
current = s;
}
}
return current;
}
void LibraryManager::Save()
{
auto p = std::filesystem::path(m_library_path) / "index.ost";

View file

@ -22,6 +22,11 @@ public:
void Save();
void Scan();
std::shared_ptr<StoryProject> NewProject();
std::shared_ptr<StoryProject> GetStory(const std::string &uuid);
private:
std::string m_library_path;
std::vector<std::shared_ptr<StoryProject>> m_projectsList;

View file

@ -2,7 +2,6 @@
#include <fstream>
#include <iostream>
#include <queue>
#include <filesystem>
#include "json.hpp"
@ -16,34 +15,27 @@ StoryProject::~StoryProject()
{
}
void StoryProject::New(const std::string &uuid, const std::string &file_path)
void StoryProject::SetPaths(const std::string &uuid, const std::string &library_path)
{
m_uuid = uuid;
Initialize(file_path);
m_project_file_path = std::filesystem::path(library_path) / uuid / std::filesystem::path("project.json");
m_working_dir = m_project_file_path.parent_path().generic_string();
m_assetsPath = m_working_dir / std::filesystem::path("assets");
std::cout << "Working dir is: " << m_working_dir << std::endl;
}
void StoryProject::SaveStory(const std::vector<uint8_t> &m_program)
void StoryProject::New(const std::string &uuid, const std::string &library_path)
{
std::ofstream o(m_working_dir / "story.c32", std::ios::out | std::ios::binary);
o.write(reinterpret_cast<const char*>(m_program.data()), m_program.size());
o.close();
}
void StoryProject::Initialize(const std::string &file_path)
{
m_story_file_path = file_path;
std::filesystem::path p(file_path);
m_working_dir= p.parent_path().generic_string();
SetPaths(uuid, library_path);
// First try to create the working directory
if (!std::filesystem::is_directory(m_working_dir))
{
std::filesystem::create_directories(m_working_dir);
}
m_assetsPath = std::filesystem::path(m_working_dir) / "assets";
std::filesystem::create_directories(m_assetsPath);
@ -51,6 +43,13 @@ void StoryProject::Initialize(const std::string &file_path)
}
void StoryProject::SaveBinary(const std::vector<uint8_t> &m_program)
{
std::ofstream o(m_working_dir / "story.c32", std::ios::out | std::ios::binary);
o.write(reinterpret_cast<const char*>(m_program.data()), m_program.size());
o.close();
}
bool StoryProject::ParseStoryInformation(nlohmann::json &j)
{
bool success = false;
@ -71,22 +70,12 @@ bool StoryProject::ParseStoryInformation(nlohmann::json &j)
}
bool StoryProject::Load(const std::string &file_path, nlohmann::json &model, ResourceManager &manager)
bool StoryProject::Load(nlohmann::json &model, ResourceManager &manager)
{
std::ifstream f(file_path);
bool success = false;
std::filesystem::path p(file_path);
m_working_dir= p.parent_path().generic_string();
std::cout << "Working dir is: " << m_working_dir << std::endl;
try {
std::ifstream f(m_project_file_path);
nlohmann::json j = nlohmann::json::parse(f);
// m_nodes.clear();
manager.Clear();
if (ParseStoryInformation(j))
@ -109,7 +98,7 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model, Res
if (j.contains("nodegraph"))
{
model = j["nodegraph"];
success = true;
m_initialized = true;
}
}
}
@ -180,7 +169,7 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model, Res
std::cout << e.what() << std::endl;
}
return success;
return m_initialized;
}
void StoryProject::Save(const nlohmann::json &model, ResourceManager &manager)
@ -206,7 +195,7 @@ void StoryProject::Save(const nlohmann::json &model, ResourceManager &manager)
j["nodegraph"] = model;
std::ofstream o(m_story_file_path);
std::ofstream o(m_project_file_path);
o << std::setw(4) << j << std::endl;
}
/*
@ -248,7 +237,7 @@ void StoryProject::Clear()
{
m_uuid = "";
m_working_dir = "";
m_story_file_path = "";
m_project_file_path = "";
m_initialized = false;
}
@ -339,7 +328,7 @@ void StoryProject::SetDisplayFormat(int w, int h)
std::string StoryProject::GetProjectFilePath() const
{
return m_story_file_path;
return m_project_file_path;
}
std::string StoryProject::GetWorkingDir() const

View file

@ -59,8 +59,11 @@ public:
StoryNode *m_tree;
*/
bool Load(const std::string &file_path, nlohmann::json &model, ResourceManager &manager);
void New(const std::string &uuid, const std::string &library_path);
bool Load(nlohmann::json &model, ResourceManager &manager);
void Save(const nlohmann::json &model, ResourceManager &manager);
void SaveBinary(const std::vector<uint8_t> &m_program);
void SetPaths(const std::string &uuid, const std::string &library_path);
void CreateTree();
void Clear();
@ -95,14 +98,13 @@ public:
std::string GetTitleSound() const { return m_titleSound; }
// Initialize with an existing project
void Initialize(const std::string &file_path);
const bool IsInitialized() const { return m_initialized; }
void New(const std::string &uuid, const std::string &file_path);
static void EraseString(std::string &theString, const std::string &toErase);
static std::string ToUpper(const std::string &input);
void SaveStory(const std::vector<uint8_t> &m_program);
bool ParseStoryInformation(nlohmann::json &j);
private:
@ -119,7 +121,7 @@ private:
bool m_initialized{false};
std::filesystem::path m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped
std::string m_story_file_path; /// JSON project file
std::filesystem::path m_project_file_path; /// JSON project file
int m_display_w{320};
int m_display_h{240};

View file

@ -3,6 +3,7 @@
#include <string>
#include <random>
#include <regex>
// Encaasulate the genaeration of a Version 4 UUID object
// A Version 4 UUID is a universally unique identifier that is generated using random numbers.
@ -46,6 +47,13 @@ public:
return uuid;
}
static bool IsValid(const std::string& input) {
// Le motif regex pour un UUID V4
std::regex uuidRegex("^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$",std::regex_constants::icase);
// Vérifier si la chaîne correspond au motif UUID V4
return std::regex_match(input, uuidRegex);
}
unsigned char _data[16] = {0};
};

View file

@ -14,7 +14,7 @@ class IStoryManager
public:
virtual ~IStoryManager() {}
virtual void OpenProject(const std::string &filename) = 0;
virtual void OpenProject(const std::string &uuid) = 0;
virtual void Log(const std::string &txt, bool critical = false) = 0;
virtual void PlaySoundFile(const std::string &fileName) = 0;
virtual std::string BuildFullAssetsPath(const std::string &fileName) const = 0;

View file

@ -2,6 +2,8 @@
#include "gui.h"
#include "ImGuiFileDialog.h"
#include <filesystem>
#include "IconsMaterialDesignIcons.h"
LibraryWindow::LibraryWindow(IStoryManager &project, LibraryManager &library)
: WindowBase("Library Manager")
@ -40,7 +42,7 @@ void LibraryWindow::Draw()
WindowBase::BeginDraw();
ImGui::SetWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver);
if (ImGui::Button("Select directory"))
if (ImGui::Button( ICON_MDI_FOLDER " Select directory"))
{
ImGuiFileDialog::Instance()->OpenDialog("ChooseLibraryDirDialog", "Choose a library directory", nullptr, ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
}
@ -96,8 +98,7 @@ void LibraryWindow::Draw()
if (ImGui::SmallButton("Load"))
{
auto filename = std::filesystem::path(m_libraryManager.LibraryPath()) / p->GetUuid() / std::filesystem::path("project.json");
m_storyManager.OpenProject(filename);
m_storyManager.OpenProject(p->GetUuid());
}
ImGui::SameLine();

View file

@ -1,14 +1,6 @@
#include "main_window.h"
#include <iostream> // std::cerr
#include <exception> // std::set_terminate
#include <cstdlib> // std::abort
#include <csignal>
#include <iostream>
// Main code
int main(int, char**)
{

View file

@ -2,7 +2,7 @@
#include <filesystem>
#include <SDL.h>
#include "platform_folders.h"
#include "uuid.h"
#include "media_converter.h"
#ifdef USE_WINDOWS_OS
@ -15,7 +15,6 @@
#pragma comment(lib, "ws2_32.lib")
#endif
#include "IconsMaterialDesignIcons.h"
#include "ImGuiFileDialog.h"
MainWindow::MainWindow()
@ -40,8 +39,6 @@ MainWindow::MainWindow()
Callback<uint8_t(chip32_ctx_t *, uint8_t)>::func = std::bind(&MainWindow::Syscall, this, std::placeholders::_1, std::placeholders::_2);
m_chip32_ctx.syscall = static_cast<syscall_t>(Callback<uint8_t(chip32_ctx_t *, uint8_t)>::callback);
m_story.Clear();
CloseProject();
}
@ -187,7 +184,7 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
if (m_chip32_ctx.registers[R0] != 0)
{
// image file name address is in R0
std::string imageFile = m_story.BuildFullAssetsPath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
std::string imageFile = m_story->BuildFullAssetsPath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
Log("Image: " + imageFile);
m_emulatorWindow.SetImage(imageFile);
}
@ -199,7 +196,7 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
if (m_chip32_ctx.registers[R1] != 0)
{
// sound file name address is in R1
std::string soundFile = m_story.BuildFullAssetsPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
std::string soundFile = m_story->BuildFullAssetsPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
Log(", Sound: " + soundFile);
m_player.Play(soundFile);
}
@ -267,11 +264,6 @@ void MainWindow::DrawMainMenuBar()
showNewProject = true;
}
/*
if (ImGui::MenuItem("Open project"))
{
showOpenProject = true;
}
if (ImGui::BeginMenu("Open Recent"))
{
for (auto &e : m_recentProjects)
@ -289,7 +281,7 @@ void MainWindow::DrawMainMenuBar()
}
*/
bool init = m_story.IsInitialized(); // local copy because CloseProject() changes the status between BeginDisabled/EndDisabled
bool init = m_story ? true : false; // local copy because CloseProject() changes the status between BeginDisabled/EndDisabled
if (!init)
ImGui::BeginDisabled();
@ -334,9 +326,13 @@ void MainWindow::DrawMainMenuBar()
if (showParameters)
{
if (m_story.IsInitialized())
if (m_story)
{
ImGui::OpenPopup("ProjectPropertiesPopup");
// Init some variables
std::size_t length = m_story->GetName().copy(m_project_name, sizeof(m_project_name));
m_project_name[length] = '\0';
}
}
@ -345,12 +341,6 @@ void MainWindow::DrawMainMenuBar()
ImGuiFileDialog::Instance()->OpenDialog("ChooseDirDialog", "Choose a parent directory for your project", nullptr, ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
}
if (showOpenProject)
{
std::string home = pf::getUserHome() + "/";
ImGuiFileDialog::Instance()->OpenDialog("OpenProjectDlgKey", "Choose File", ".json", home, 1, nullptr, ImGuiFileDialogFlags_Modal);
}
// Always center this window when appearing
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
//ImVec2 parent_pos = ImGui::GetWindowPos();
@ -426,27 +416,6 @@ bool MainWindow::ShowQuitConfirm()
}
void MainWindow::OpenProjectDialog()
{
ImGui::SetNextWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver);
if (ImGuiFileDialog::Instance()->Display("OpenProjectDlgKey"))
{
// ImGui::SetWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver);
// action if OK
if (ImGuiFileDialog::Instance()->IsOk())
{
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
OpenProject(filePathName);
}
// close
ImGuiFileDialog::Instance()->Close();
}
}
void MainWindow::NewProjectPopup()
{
// Always center this window when appearing
@ -463,19 +432,13 @@ void MainWindow::NewProjectPopup()
if (!std::filesystem::is_directory(projdir))
{
m_story = m_libraryManager.NewProject();
std::string uuid = UUID().String();
auto p = std::filesystem::path(projdir) / uuid / std::filesystem::path("project.json");
m_story.Initialize(p.generic_string());
m_story.SetDisplayFormat(320, 240);
m_story.SetImageFormat(StoryProject::IMG_FORMAT_QOIF);
m_story.SetSoundFormat(StoryProject::SND_FORMAT_WAV);
m_story.SetName("New project");
m_story.SetUuid(uuid);
SaveProject();
OpenProject(p.generic_string());
if (m_story)
{
SaveProject();
OpenProject(m_story->GetUuid());
}
}
}
@ -486,45 +449,15 @@ void MainWindow::NewProjectPopup()
void MainWindow::ProjectPropertiesPopup()
{
static std::string projdir;
// Always center this window when appearing
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("ProjectPropertiesPopup", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("New project parameters (directory must be empty)");
ImGui::Separator();
ImGui::Text("Directory: "); ImGui::SameLine();
static char project_dir[256] = "";
ImGui::InputTextWithHint("##project_path", "Project path", project_dir, IM_ARRAYSIZE(project_dir));
ImGui::SameLine();
if (ImGui::Button( ICON_MDI_FOLDER " ..."))
{
ImGuiFileDialog::Instance()->OpenDialog("ChooseDirDialog", "Choose File", nullptr, ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
}
// display
if (ImGuiFileDialog::Instance()->Display("ChooseDirDialog"))
{
// action if OK
if (ImGuiFileDialog::Instance()->IsOk())
{
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
projdir = ImGuiFileDialog::Instance()->GetCurrentPath();
}
// close
ImGuiFileDialog::Instance()->Close();
}
ImGui::Text("Project name: "); ImGui::SameLine();
static char project_name[256] = "";
ImGui::InputTextWithHint("##project_name", "Project name", project_name, IM_ARRAYSIZE(project_name));
ImGui::InputTextWithHint("##project_name", "Project name", m_project_name, IM_ARRAYSIZE(m_project_name));
ImGui::Text("Size of display screen: ");
ImGui::SameLine();
@ -630,33 +563,23 @@ void MainWindow::ProjectPropertiesPopup()
if (ImGui::Button("OK", ImVec2(120, 0)))
{
bool valid{true};
if (!std::filesystem::is_directory(projdir))
if (display_item_current_idx == 0)
{
valid = false;
m_story->SetDisplayFormat(320, 240);
}
else
{
m_story->SetDisplayFormat(640, 480);
}
if (valid)
{
m_story->SetImageFormat(GetImageFormat(image_item_current_idx));
m_story->SetSoundFormat(GetSoundFormat(sound_item_current_idx));
m_story->SetName(m_project_name);
if (display_item_current_idx == 0)
{
m_story.SetDisplayFormat(320, 240);
}
else
{
m_story.SetDisplayFormat(640, 480);
}
SaveProject();
m_story.SetImageFormat(GetImageFormat(image_item_current_idx));
m_story.SetSoundFormat(GetSoundFormat(sound_item_current_idx));
m_story.SetName(project_name);
ImGui::CloseCurrentPopup();
SaveProject();
ImGui::CloseCurrentPopup();
}
}
ImGui::SetItemDefaultFocus();
ImGui::SameLine();
@ -666,29 +589,31 @@ void MainWindow::ProjectPropertiesPopup()
}
ImGui::EndPopup();
}
else
{
projdir = "";
}
}
void MainWindow::SaveProject()
{
nlohmann::json model;
m_nodeEditorWindow.Save(model);
m_story.Save(model, m_resources);
m_story->Save(model, m_resources);
}
void MainWindow::OpenProject(const std::string &filename)
void MainWindow::OpenProject(const std::string &uuid)
{
CloseProject();
nlohmann::json model;
if (m_story.Load(filename, model, m_resources))
m_story = m_libraryManager.GetStory(uuid);
if (!m_story)
{
Log("Cannot find story: " + uuid);
}
else if (m_story->Load(model, m_resources))
{
Log("Open project success");
m_nodeEditorWindow.Load(model);
auto proj = m_story.GetProjectFilePath();
auto proj = m_story->GetProjectFilePath();
// Add to recent if not exists
if (std::find(m_recentProjects.begin(), m_recentProjects.end(), proj) == m_recentProjects.end())
{
@ -719,14 +644,23 @@ void MainWindow::OpenProject(const std::string &filename)
void MainWindow::RefreshProjectInformation()
{
std::string fullText = "Story Editor " + LibraryManager::GetVersion() + " - " + m_story.GetProjectFilePath();
std::string fullText = "Story Editor " + LibraryManager::GetVersion();
if (m_story)
{
fullText += " - " + m_story->GetProjectFilePath();
}
m_gui.SetWindowTitle(fullText);
}
void MainWindow::CloseProject()
{
m_story.Clear();
if (m_story)
{
m_story->Clear();
}
m_resources.Clear();
m_nodeEditorWindow.Clear();
@ -782,7 +716,6 @@ void MainWindow::Loop()
}
NewProjectPopup();
OpenProjectDialog();
ProjectPropertiesPopup();
if (aboutToClose)
@ -816,7 +749,7 @@ void MainWindow::PlaySoundFile(const std::string &fileName)
std::string MainWindow::BuildFullAssetsPath(const std::string &fileName) const
{
return m_story.BuildFullAssetsPath(fileName);
return m_story->BuildFullAssetsPath(fileName);
}
std::pair<FilterIterator, FilterIterator> MainWindow::Images()
@ -915,7 +848,7 @@ void MainWindow::GenerateBinary()
// FIXME
// m_ramView->SetMemory(m_ram_data, sizeof(m_ram_data));
// m_romView->SetMemory(m_rom_data, m_program.size());
m_story.SaveStory(m_program);
m_story->SaveBinary(m_program);
chip32_initialize(&m_chip32_ctx);
m_dbg.run_result = VM_READY;
UpdateVmView();
@ -976,8 +909,8 @@ void MainWindow::ConvertResources()
auto [b, e] = m_resources.Items();
for (auto it = b; it != e; ++it)
{
std::string inputfile = m_story.BuildFullAssetsPath((*it)->file.c_str());
std::string outputfile = std::filesystem::path(m_story.AssetsPath() / StoryProject::RemoveFileExtension((*it)->file)).string();
std::string inputfile = m_story->BuildFullAssetsPath((*it)->file.c_str());
std::string outputfile = std::filesystem::path(m_story->AssetsPath() / StoryProject::RemoveFileExtension((*it)->file)).string();
int retCode = 0;
if ((*it)->format == "PNG")

View file

@ -93,7 +93,7 @@ public:
private:
enum VmEventType { EvNoEvent, EvStep, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished};
StoryProject m_story;
std::shared_ptr<StoryProject> m_story;
// VM
uint8_t m_rom_data[16*1024];
@ -119,6 +119,8 @@ private:
ConsoleWindow m_consoleWindow;
CodeEditor m_editorWindow;
char m_project_name[256] = "";
ResourcesWindow m_resourcesWindow;
NodeEditorWindow m_nodeEditorWindow;
@ -138,7 +140,7 @@ private:
// From IStoryManager (proxy to StoryProject class)
virtual void OpenProject(const std::string &filename) override;
virtual void OpenProject(const std::string &uuid) override;
virtual void Log(const std::string &txt, bool critical = false) override;
virtual void PlaySoundFile(const std::string &fileName) override;;
virtual std::string BuildFullAssetsPath(const std::string &fileName) const override;
@ -170,7 +172,6 @@ private:
void NewProjectPopup();
void SaveProject();
void CloseProject();
void OpenProjectDialog();
void DrawStatusBar();
bool CompileToAssembler();

View file

@ -1,6 +1,5 @@
#include "resources_window.h"
#include "imgui.h"
#include <random>
#include <filesystem>
#include <memory>
#include "resource.h"

View file

@ -1,8 +1,5 @@
#pragma once
#include <vector>
#include <map>
#include <mutex>
#include "i_story_manager.h"
#include "window_base.h"