add logger, fix mem leak, pack copy to device finished.

This commit is contained in:
Anthony Rabine 2024-05-02 13:22:17 +02:00
parent 73aefa7100
commit 3490cf0050
17 changed files with 503 additions and 411 deletions

10
shared/i_logger.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
#include <string>
class ILogger
{
public:
virtual void Log(const std::string &txt, bool critical = false) = 0;
};

View file

@ -8,7 +8,19 @@
#include "uuid.h"
LibraryManager::LibraryManager() {}
LibraryManager::LibraryManager(ILogger &log)
: m_log(log)
{
}
LibraryManager::~LibraryManager()
{
if (m_copyWorker.joinable())
{
m_copyWorker.join();
}
}
void LibraryManager::Initialize(const std::string &library_path)
{
@ -43,7 +55,7 @@ void LibraryManager::Scan()
if (std::filesystem::exists(p))
{
// okay, open it
auto proj = std::make_shared<StoryProject>();
auto proj = std::make_shared<StoryProject>(m_log);
try {
std::ifstream f(p);
nlohmann::json j = nlohmann::json::parse(f);
@ -55,7 +67,7 @@ void LibraryManager::Scan()
m_projectsList.push_back(proj);
}
}
catch(std::exception &e)
catch(nlohmann::json::exception &e)
{
std::cout << e.what() << std::endl;
}
@ -68,7 +80,7 @@ void LibraryManager::Scan()
std::shared_ptr<StoryProject> LibraryManager::NewProject()
{
auto story = std::make_shared<StoryProject>();
auto story = std::make_shared<StoryProject>(m_log);
std::string uuid = Uuid().String();
story->New(uuid, m_library_path);
@ -92,10 +104,15 @@ std::shared_ptr<StoryProject> LibraryManager::GetStory(const std::string &uuid)
return current;
}
void LibraryManager::Save()
std::string LibraryManager::IndexFileName() const
{
auto p = std::filesystem::path(m_library_path) / "index.ost";
Tlv tlv(p.string());
return p.string();
}
void LibraryManager::Save()
{
Tlv tlv;
tlv.add_object(1);
tlv.add_string(GetVersion());
@ -115,6 +132,8 @@ void LibraryManager::Save()
}
}
tlv.Save(IndexFileName());
/*
@ -130,16 +149,37 @@ void LibraryManager::Save()
void LibraryManager::CopyToDevice(const std::string &outputDir)
{
std::thread myThread([&]() {
myThread.detach();
try
{
// Generate TLV file (index of all stories)
Save();
// Copy TLV to the directory root
std::filesystem::copy(IndexFileName(), outputDir, std::filesystem::copy_options::overwrite_existing);
std::cout << "Starting to copy elements" << std::endl;
for (auto p : *this)
if (m_copyWorker.joinable())
{
m_copyWorker.join();
}
});
m_copyWorker = std::thread([&, outputDir]() {
// myThread.detach();
std::cout << "Starting to copy elements" << std::endl;
for (auto p : *this)
{
if (p->IsSelected())
{
p->CopyToDevice(outputDir);
}
}
});
} catch (const std::filesystem::filesystem_error& e) {
std::cerr << "Filesystem error: " << e.what() << std::endl;
} catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
}
bool LibraryManager::IsInitialized() const

View file

@ -4,12 +4,15 @@
#include <string>
#include <vector>
#include <memory>
#include "story_project.h"
#include <thread>
#include "story_project.h"
#include "i_logger.h"
class LibraryManager
{
public:
LibraryManager();
LibraryManager(ILogger &log);
~LibraryManager();
void Initialize(const std::string &library_path);
bool IsInitialized() const;
@ -34,13 +37,17 @@ public:
std::shared_ptr<StoryProject> GetStory(const std::string &uuid);
std::string IndexFileName() const;
void SetStoreUrl(const std::string &store_url) { m_storeUrl = store_url; }
std::string GetStoreUrl() const { return m_storeUrl; }
private:
ILogger &m_log;
std::string m_library_path;
std::vector<std::shared_ptr<StoryProject>> m_projectsList;
std::string m_storeUrl;
std::thread m_copyWorker;
};
#endif // LIBRARYMANAGER_H

View file

@ -1,6 +1,10 @@
#include "resource_manager.h"
#include <algorithm>
#include <filesystem>
#include "resource_manager.h"
#include "media_converter.h"
#include "sys_lib.h"
struct Media {
std::string type;
std::string format;
@ -32,3 +36,59 @@ std::string ResourceManager::ExtentionInfo(std::string extension, int info_type)
}
}
void ResourceManager::ConvertResources(const std::filesystem::path &assetsPath, const std::string &destAssetsPath)
{
auto [b, e] = Items();
for (auto it = b; it != e; ++it)
{
std::filesystem::path inputfile = std::filesystem::path(assetsPath / (*it)->file);
std::filesystem::path outputfile = std::filesystem::path(assetsPath / SysLib::RemoveFileExtension((*it)->file));
int retCode = 0;
if (!std::filesystem::exists(outputfile))
{
if ((*it)->format == "PNG")
{
outputfile += ".qoi"; // FIXME: prendre la config en cours désirée
retCode = MediaConverter::ImageToQoi(inputfile.generic_string(), outputfile.generic_string());
}
else if ((*it)->format == "MP3")
{
outputfile += ".wav"; // FIXME: prendre la config en cours désirée
retCode = MediaConverter::Mp3ToWav(inputfile.generic_string(), outputfile.generic_string());
}
else if ((*it)->format == "OGG")
{
outputfile += ".wav"; // FIXME: prendre la config en cours désirée
retCode = MediaConverter::OggToWav(inputfile.generic_string(), outputfile.generic_string());
}
else
{
// Log("Skipped: " + inputfile + ", unknown format" + outputfile, true);
}
}
if (retCode < 0)
{
//m_log.Log("Failed to convert media file " + inputfile + ", error code: " + std::to_string(retCode) + " to: " + outputfile, true);
}
else if (retCode == 0)
{
// Log("Convertered file: " + inputfile);
// Conversion success, now copy to output directory if necessary (ie: to an external device)
if ((destAssetsPath != assetsPath.generic_string()) && (std::filesystem::exists(destAssetsPath)))
{
// destination filename
auto destFile = destAssetsPath / outputfile.filename();
if (!std::filesystem::exists(destFile))
{
std::filesystem::copy(outputfile, destAssetsPath, std::filesystem::copy_options::overwrite_existing);
}
}
}
}
}

View file

@ -9,6 +9,7 @@
#include <ranges>
#include "resource.h"
#include "i_logger.h"
class ResourceManager
{
@ -24,6 +25,8 @@ public:
static std::string ExtentionInfo(std::string extension, int info_type);
void ConvertResources(const std::filesystem::path &assetsPath, const std::string &destAssetsPath);
~ResourceManager() {
}

View file

@ -1,14 +1,17 @@
#include "story_project.h"
#include <fstream>
#include <iostream>
#include <filesystem>
#include <regex>
#include "story_project.h"
#include "json.hpp"
#include "media_node.h"
#include "sys_lib.h"
StoryProject::StoryProject()
StoryProject::StoryProject(ILogger &log)
: m_log(log)
{
registerNode<MediaNode>("media-node");
}
@ -34,6 +37,23 @@ void StoryProject::CopyToDevice(const std::string &outputDir)
Load(manager);
// Output dir is the root. Build an assets directory to the device location
std::filesystem::path destRootDir = std::filesystem::path(outputDir) / m_uuid;
std::filesystem::path destAssetsDir = destRootDir / "assets";
std::filesystem::create_directories(destAssetsDir);
// Generate and copy binary
std::string code;
GenerateScript(code);
Chip32::Assembler::Error err;
if (GenerateBinary(code, err))
{
std::filesystem::copy(BinaryFileName(), destRootDir, std::filesystem::copy_options::overwrite_existing);
// Convert resources (if necessary) and copy them to destination assets
manager.ConvertResources(AssetsPath(), destAssetsDir);
}
}
void StoryProject::New(const std::string &uuid, const std::string &library_path)
@ -51,10 +71,15 @@ void StoryProject::New(const std::string &uuid, const std::string &library_path)
m_initialized = true;
}
void StoryProject::SaveBinary(const std::vector<uint8_t> &m_program)
std::filesystem::path StoryProject::BinaryFileName() const
{
std::ofstream o(m_working_dir / "story.c32", std::ios::out | std::ios::binary);
return m_working_dir / "story.c32";
}
void StoryProject::SaveBinary()
{
std::ofstream o(BinaryFileName() , std::ios::out | std::ios::binary);
o.write(reinterpret_cast<const char*>(m_program.data()), m_program.size());
o.close();
}
@ -192,13 +217,47 @@ bool StoryProject::ModelFromJson(const nlohmann::json &model)
}
success = true;
}
catch(std::exception &e)
catch(nlohmann::json::exception &e)
{
std::cout << "(NodeEditorWindow::Load) " << e.what() << std::endl;
}
return success;
}
bool StoryProject::CopyProgramTo(uint8_t *memory, uint32_t size)
{
bool success = false;
// Update ROM memory
if (m_program.size() < size)
{
std::copy(m_program.begin(), m_program.end(), memory);
success = true;
}
return success;
}
bool StoryProject::GetAssemblyLine(uint32_t pointer_counter, uint32_t &line)
{
bool success = false;
// On recherche quelle est la ligne qui possède une instruction à cette adresse
std::vector<Chip32::Instr>::const_iterator ptr = m_assembler.Begin();
for (; ptr != m_assembler.End(); ++ptr)
{
if ((ptr->addr == pointer_counter) && ptr->isRomCode())
{
break;
}
}
if (ptr != m_assembler.End())
{
line = ptr->line;
success = true;
}
return success;
}
int StoryProject::OutputsCount(const std::string &nodeId)
{
int count = 0;
@ -258,7 +317,7 @@ std::string StoryProject::FindFirstNode() const
}
bool StoryProject::Build(std::string &codeStr)
bool StoryProject::GenerateScript(std::string &codeStr)
{
std::stringstream code;
std::stringstream chip32;
@ -267,7 +326,7 @@ bool StoryProject::Build(std::string &codeStr)
if (firstNode == "")
{
std::cout << "First node not found, there must be only one node with a free input." << std::endl;
m_log.Log("First node not found, there must be only one node with a free input.");
return false;
}
@ -285,9 +344,51 @@ bool StoryProject::Build(std::string &codeStr)
}
codeStr = code.str();
// Add our utility functions
std::string buffer;
std::ifstream f("scripts/media.asm");
f.seekg(0, std::ios::end);
buffer.resize(f.tellg());
f.seekg(0);
f.read(buffer.data(), buffer.size());
codeStr += buffer;
return true;
}
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)
{
Chip32::Result result;
bool success = false;
if (m_assembler.Parse(code) == true)
{
if (m_assembler.BuildBinary(m_program, result) == true)
{
result.Print();
m_log.Log("Binary successfully generated.");
SaveBinary();
success = true;
}
else
{
err = m_assembler.GetLastError();
}
}
else
{
err = m_assembler.GetLastError();
m_log.Log(err.ToString(), true);
}
return success;
}
bool StoryProject::Load(ResourceManager &manager)
{
try {
@ -320,69 +421,8 @@ bool StoryProject::Load(ResourceManager &manager)
}
}
}
/*
if (j.contains("nodes"))
{
for (auto& element : j["nodes"])
{
StoryNode n;
n.auto_jump = element["auto_jump"];
for (auto& jump : element["jumps"])
{
n.jumps.push_back(jump.get<int>());
}
n.id = element["id"];
n.image = element["image"];
n.sound = element["sound"];
m_nodes.push_back(n);
}
}
m_images.clear();
if (j.contains("images"))
{
for (auto& element : j["images"])
{
Resource r;
r.file = element["file"];
r.description = element["description"];
r.format = element["format"];
m_images.push_back(r);
}
}
m_sounds.clear();
if (j.contains("sounds"))
{
for (auto& element : j["sounds"])
{
Resource r;
r.file = element["file"];
r.description = element["description"];
r.format = element["format"];
m_sounds.push_back(r);
}
}
m_type = j["type"];
m_code = j["code"];
m_name = j["name"];
success = true;
*/
}
catch(std::exception &e)
catch(nlohmann::json::exception &e)
{
std::cout << e.what() << std::endl;
}
@ -463,74 +503,7 @@ void StoryProject::Clear()
m_links.clear();
}
void StoryProject::EraseString(std::string &theString, const std::string &toErase)
{
std::size_t found;
found = theString.find(toErase);
if (found != std::string::npos)
{
theString.erase(found, toErase.size());
}
}
std::string StoryProject::ToUpper(const std::string &input)
{
std::string str = input;
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
return str;
}
std::string StoryProject::GetFileName(const std::string &path)
{
auto found = path.find_last_of("/\\");
return path.substr(found+1);
}
void StoryProject::ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace)
{
std::size_t found;
do
{
found = theString.find(toFind);
if (found != std::string::npos)
{
theString.replace(found, 1, toReplace);
}
}
while (found != std::string::npos);
}
std::string StoryProject::RemoveFileExtension(const std::string &FileName)
{
std::string f = GetFileName(FileName);
std::string ext = GetFileExtension(f);
EraseString(f, "." + ext);
return f;
}
std::string StoryProject::FileToConstant(const std::string &FileName, const std::string &extension)
{
std::string f = RemoveFileExtension(FileName);
return "$" + f + " DC8 \"" + f + extension + "\", 8\r\n";
}
std::string StoryProject::Normalize(const std::string &input)
{
std::string valid_file = input;
std::replace(valid_file.begin(), valid_file.end(), '\\', '_');
std::replace(valid_file.begin(), valid_file.end(), '/', '_');
std::replace(valid_file.begin(), valid_file.end(), ':', '_');
std::replace(valid_file.begin(), valid_file.end(), '?', '_');
std::replace(valid_file.begin(), valid_file.end(), '\"', '_');
std::replace(valid_file.begin(), valid_file.end(), '<', '_');
std::replace(valid_file.begin(), valid_file.end(), '>', '_');
std::replace(valid_file.begin(), valid_file.end(), '|', '_');
std::replace(valid_file.begin(), valid_file.end(), ' ', '_');
return valid_file;
}
void StoryProject::SetTitleImage(const std::string &titleImage)
{
@ -542,12 +515,7 @@ void StoryProject::SetTitleSound(const std::string &titleSound)
m_titleSound = titleSound;
}
std::string StoryProject::GetFileExtension(const std::string &fileName)
{
if(fileName.find_last_of(".") != std::string::npos)
return fileName.substr(fileName.find_last_of(".")+1);
return "";
}
void StoryProject::SetImageFormat(ImageFormat format)
{
@ -580,5 +548,11 @@ std::string StoryProject::BuildFullAssetsPath(const std::string &fileName) const
return (AssetsPath() / fileName).generic_string();
}
std::string StoryProject::FileToConstant(const std::string &FileName, const std::string &extension)
{
std::string f = SysLib::RemoveFileExtension(FileName);
return "$" + f + " DC8 \"" + f + extension + "\", 8\r\n";
}

View file

@ -11,6 +11,7 @@
#include "connection.h"
#include "base_node.h"
#include "i_story_project.h"
#include "chip32_assembler.h"
// FIXME : Structure très Lunii style, à utiliser pour la conversion peut-être ...
struct StoryNode
@ -51,7 +52,7 @@ public:
enum ImageFormat { IMG_FORMAT_BMP_4BITS, IMG_FORMAT_QOIF, IMG_FORMAT_COUNT };
enum SoundFormat { SND_FORMAT_WAV, SND_FORMAT_QOAF, SND_FORMAT_COUNT };
StoryProject();
StoryProject(ILogger &log);
~StoryProject();
bool *Selected() {
@ -66,20 +67,26 @@ public:
StoryNode *m_tree;
*/
void New(const std::string &uuid, const std::string &library_path);
bool Build(std::string &codeStr);
std::filesystem::path BinaryFileName() const;
bool GenerateScript(std::string &codeStr);
bool GenerateBinary(const std::string &code, Chip32::Assembler::Error &err);
bool Load(ResourceManager &manager);
void Save(ResourceManager &manager);
void SaveBinary(const std::vector<uint8_t> &m_program);
void SaveBinary();
void SetPaths(const std::string &uuid, const std::string &library_path);
void CopyToDevice(const std::string &outputDir);
void ModelToJson(nlohmann::json &model);
bool ModelFromJson(const nlohmann::json &model);
bool CopyProgramTo(uint8_t *memory, uint32_t size);
// returns >= 0 on success
bool GetAssemblyLine(uint32_t pointer_counter, uint32_t &line);
void CreateTree();
void Clear();
std::pair<std::list<std::shared_ptr<BaseNode>>::iterator, std::list<std::shared_ptr<BaseNode>>::iterator> Nodes() {
return std::make_pair(m_nodes.begin(), m_nodes.end());
}
@ -102,18 +109,13 @@ public:
std::string GetName() const { return m_name; }
std::string GetUuid() const { return m_uuid; }
std::string GetDescription() const { return m_description; }
int GetVersion() const { return m_version; }
uint32_t GetVersion() const { return m_version; }
std::string BuildFullAssetsPath(const std::string &fileName) const;
std::filesystem::path AssetsPath() const { return m_assetsPath; }
static std::string GetFileExtension(const std::string &FileName);
static std::string GetFileName(const std::string &path);
static std::string RemoveFileExtension(const std::string &FileName);
static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace);
static std::string FileToConstant(const std::string &FileName, const std::string &extension);
static std::string Normalize(const std::string &input);
std::filesystem::path AssetsPath() const { return m_assetsPath; }
void SetTitleImage(const std::string &titleImage);
void SetTitleSound(const std::string &titleSound);
@ -124,9 +126,6 @@ public:
// Initialize with an existing project
const bool IsInitialized() const { return m_initialized; }
static void EraseString(std::string &theString, const std::string &toErase);
static std::string ToUpper(const std::string &input);
bool ParseStoryInformation(nlohmann::json &j);
// From IStoryProject
@ -140,17 +139,22 @@ public:
void DeleteLink(std::shared_ptr<Connection> c);
private:
ILogger &m_log;
// Project properties and location
std::string m_name; /// human readable name
std::string m_uuid;
std::string m_titleImage;
std::string m_titleSound;
std::string m_description;
int m_version;
uint32_t m_version;
bool m_selected{false};
std::filesystem::path m_assetsPath;
Chip32::Assembler m_assembler;
std::vector<uint8_t> m_program;
// Model in memory
std::list<std::shared_ptr<Connection>> m_links;
std::list<std::shared_ptr<BaseNode>> m_nodes;

99
shared/sys_lib.cpp Normal file
View file

@ -0,0 +1,99 @@
#include "sys_lib.h"
#include <algorithm>
#include <regex>
void SysLib::EraseString(std::string &theString, const std::string &toErase)
{
std::size_t found;
found = theString.find(toErase);
if (found != std::string::npos)
{
theString.erase(found, toErase.size());
}
}
std::string SysLib::ToUpper(const std::string &input)
{
std::string str = input;
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
return str;
}
std::string SysLib::GetFileExtension(const std::string &fileName)
{
auto idx = fileName.find_last_of(".");
if(idx != std::string::npos)
{
return fileName.substr(idx + 1);
}
return "";
}
std::string SysLib::GetFileName(const std::string &path)
{
if (path.size() > 0)
{
auto found = path.find_last_of("/\\");
if (found != std::string::npos)
{
return path.substr(found+1);
}
else
{
return "";
}
}
else
{
return "";
}
}
void SysLib::ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace)
{
std::size_t found;
do
{
found = theString.find(toFind);
if (found != std::string::npos)
{
theString.replace(found, 1, toReplace);
}
}
while (found != std::string::npos);
}
std::string SysLib::RemoveFileExtension(const std::string &filename)
{
// Trouver la dernière occurrence du point
std::size_t dotPos = filename.rfind('.');
if (dotPos == std::string::npos) {
// Pas d'extension trouvée, retourner le nom tel quel
return filename;
}
// Retourner la sous-chaîne avant le point
return filename.substr(0, dotPos);
}
std::string SysLib::Normalize(const std::string &input)
{
std::string valid_file = input;
std::replace(valid_file.begin(), valid_file.end(), '\\', '_');
std::replace(valid_file.begin(), valid_file.end(), '/', '_');
std::replace(valid_file.begin(), valid_file.end(), ':', '_');
std::replace(valid_file.begin(), valid_file.end(), '?', '_');
std::replace(valid_file.begin(), valid_file.end(), '\"', '_');
std::replace(valid_file.begin(), valid_file.end(), '<', '_');
std::replace(valid_file.begin(), valid_file.end(), '>', '_');
std::replace(valid_file.begin(), valid_file.end(), '|', '_');
std::replace(valid_file.begin(), valid_file.end(), ' ', '_');
return valid_file;
}

17
shared/sys_lib.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include <string>
class SysLib
{
public:
static std::string GetFileExtension(const std::string &FileName);
static std::string GetFileName(const std::string &path);
static std::string RemoveFileExtension(const std::string &FileName);
static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace);
static std::string Normalize(const std::string &input);
static void EraseString(std::string &theString, const std::string &toErase);
static std::string ToUpper(const std::string &input);
};

View file

@ -15,34 +15,41 @@
class Tlv
{
public:
explicit Tlv(const std::string &filename)
explicit Tlv()
{
m_file = std::ofstream(filename, std::ios::out | std::ios::binary);
}
~Tlv() {
m_file.close();
}
void Save(const std::string &filename)
{
auto f = std::ofstream(filename, std::ios::out | std::ios::binary);
f << m_mem;
std::flush(f);
f.close();
}
void add_array(uint16_t size)
{
m_file.write(reinterpret_cast<const char*>(&m_objectType), sizeof(m_objectType));
m_file.write(reinterpret_cast<const char*>(&size), sizeof(size));
m_mem.append(reinterpret_cast<const char*>(&m_arrayType), sizeof(m_arrayType));
m_mem.append(reinterpret_cast<const char*>(&size), sizeof(size));
}
void add_string(const char *s, uint16_t size)
{
m_file.write(reinterpret_cast<const char*>(&m_stringType), sizeof(m_stringType));
m_file.write(s, size);
m_mem.append(reinterpret_cast<const char*>(&m_stringType), sizeof(m_stringType));
m_mem.append(reinterpret_cast<const char*>(&size), sizeof(size));
m_mem.append(s, size);
}
void add_integer(uint32_t value)
{
static const uint16_t size = 4;
m_file.write(reinterpret_cast<const char*>(&m_integerType), sizeof(m_integerType));
m_file.write(reinterpret_cast<const char*>(&size), sizeof(size));
m_file.write(reinterpret_cast<const char*>(&value), sizeof(value));
m_mem.append(reinterpret_cast<const char*>(&m_integerType), sizeof(m_integerType));
m_mem.append(reinterpret_cast<const char*>(&size), sizeof(size));
m_mem.append(reinterpret_cast<const char*>(&value), size);
}
void add_string(const std::string &s)
@ -52,12 +59,12 @@ public:
void add_object(uint16_t entries)
{
m_file.write(reinterpret_cast<const char*>(&m_arrayType), sizeof(m_arrayType));
m_file.write(reinterpret_cast<const char*>(&entries), sizeof(entries));
m_mem.append(reinterpret_cast<const char*>(&m_objectType), sizeof(m_objectType));
m_mem.append(reinterpret_cast<const char*>(&entries), sizeof(entries));
}
private:
std::ofstream m_file;
std::string m_mem;
uint8_t m_arrayType = TLV_ARRAY_TYPE;
uint8_t m_objectType = TLV_OBJECT_TYPE;

View file

@ -239,6 +239,8 @@ set(SRCS
../shared/thread_safe_queue.h
../shared/library_manager.h
../shared/library_manager.cpp
../shared/sys_lib.cpp
../shared/sys_lib.h
)
if(WIN32)

View file

@ -13,8 +13,10 @@
#include "story_project.h"
#include "resource_manager.h"
#include "uuid.h"
#include "sys_lib.h"
PackArchive::PackArchive()
PackArchive::PackArchive(ILogger &log)
: m_log(log)
{
}
@ -34,50 +36,6 @@ std::vector<std::string> PackArchive::GetImages()
}
std::string PackArchive::GetFileName(const std::string &path)
{
auto found = path.find_last_of("/\\");
return path.substr(found+1);
}
std::string PackArchive::GetFileExtension(const std::string &FileName)
{
if(FileName.find_last_of(".") != std::string::npos)
return FileName.substr(FileName.find_last_of(".")+1);
return "";
}
void PackArchive::ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace)
{
std::size_t found;
do
{
found = theString.find(toFind);
if (found != std::string::npos)
{
theString.replace(found, 1, toReplace);
}
}
while (found != std::string::npos);
}
void PackArchive::EraseString(std::string &theString, const std::string &toErase)
{
std::size_t found;
found = theString.find(toErase);
if (found != std::string::npos)
{
theString.erase(found, toErase.size());
}
}
std::string PackArchive::ToUpper(const std::string &input)
{
std::string str = input;
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
return str;
}
void PackArchive::Unzip(const std::string &filePath, const std::string &parent_dest_dir)
{
// std::string fileName = GetFileName(filePath);
@ -189,7 +147,7 @@ std::vector<std::string> PackArchive::FilesToJson(const std::string &type, const
res_file[12] = '\0';
std::string res_file_string(res_file);
ReplaceCharacter(res_file_string, "\\", "/");
SysLib::ReplaceCharacter(res_file_string, "\\", "/");
resList.push_back(res_file_string);
@ -381,10 +339,10 @@ bool PackArchive::Load(const std::string &filePath)
mZip.Close();
mCurrentNodeId = 0;
std::string fileName = GetFileName(filePath);
std::string ext = GetFileExtension(fileName);
EraseString(fileName, "." + ext); // on retire l'extension du pack
mPackName = ToUpper(fileName);
std::string fileName = SysLib::GetFileName(filePath);
std::string ext = SysLib::GetFileExtension(fileName);
SysLib::EraseString(fileName, "." + ext); // on retire l'extension du pack
mPackName = SysLib::ToUpper(fileName);
std::cout << "Pack name: " << mPackName << std::endl;
@ -427,7 +385,7 @@ bool PackArchive::ImportStudioFormat(const std::string &fileName, const std::str
// STUDIO format
std::ifstream f(basePath + "/story.json");
nlohmann::json j = nlohmann::json::parse(f);
StoryProject proj;
StoryProject proj(m_log);
ResourceManager res;
if (j.contains("title"))
@ -517,7 +475,7 @@ bool PackArchive::ImportStudioFormat(const std::string &fileName, const std::str
}
catch(std::exception &e)
{
std::cout << e.what() << std::endl;
m_log.Log(std::string("Import failure: ") + e.what());
}
return false;
@ -527,7 +485,7 @@ std::string PackArchive::GetImage(const std::string &fileName)
{
//"C8B39950DE174EAA8E852A07FC468267/rf/000/05FB5530"
std::string imagePath = mPackName + "/rf/" + fileName;
ReplaceCharacter(imagePath, "\\", "/");
SysLib::ReplaceCharacter(imagePath, "\\", "/");
std::cout << "Loading " + imagePath << std::endl;
return OpenImage(imagePath);
@ -542,7 +500,7 @@ std::string PackArchive::CurrentSound()
{
//"C8B39950DE174EAA8E852A07FC468267/sf/000/05FB5530"
std::string soundPath = mPackName + "/sf/" + std::string(mCurrentNode.si_file);
ReplaceCharacter(soundPath, "\\", "/");
SysLib::ReplaceCharacter(soundPath, "\\", "/");
std::cout << "Loading " + soundPath << std::endl;

View file

@ -6,11 +6,12 @@
#include <vector>
#include "ni_parser.h"
#include "json.hpp"
#include "i_logger.h"
class PackArchive
{
public:
PackArchive();
PackArchive(ILogger &log);
bool Load(const std::string &filePath);
std::string OpenImage(const std::string &fileName);
@ -34,6 +35,7 @@ public:
std::string HexDump(const char *desc, const void *addr, int len);
private:
ILogger &m_log;
Zip mZip;
std::string mPackName;
uint32_t mCurrentNodeId = 0;
@ -47,11 +49,7 @@ private:
std::map<std::string, std::string> m_resources;
bool ParseNIFile(const std::string &root);
std::string GetFileName(const std::string &path);
std::string GetFileExtension(const std::string &FileName);
void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace);
void EraseString(std::string &theString, const std::string &toErase);
std::string ToUpper(const std::string &input);
void DecipherFileOnDisk(const std::string &fileName);
void DecipherFiles(const std::string &directory, const std::string &suffix);

View file

@ -1,11 +1,13 @@
#include <filesystem>
#include <functional>
#include "IconsMaterialDesignIcons.h"
#include "i_story_manager.h"
#include "base64.hpp"
#include "sys_lib.h"
#include "library_window.h"
#include "gui.h"
#include "ImGuiFileDialog.h"
#include <filesystem>
#include "IconsMaterialDesignIcons.h"
#include "i_story_manager.h"
#include <functional>
#include "base64.hpp"
typedef int (*xfer_callback_t)(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);
@ -237,19 +239,21 @@ void LibraryWindow::SharePointJsonDownloadedCallback(bool success, const std::st
std::ifstream f(filename);
nlohmann::json j = nlohmann::json::parse(f);
std::string archive = j["@content.downloadUrl"].get<std::string>();
std::string name = j["name"].get<std::string>();
m_downloadQueue.push({
"dl",
archive,
ToLocalStoreFile(StoryProject::Normalize(name)),
ToLocalStoreFile(SysLib::Normalize(name)),
std::bind(&LibraryWindow::StoryFileDownloadedCallback, this, std::placeholders::_1, std::placeholders::_2)
});
}
catch(nlohmann::json::exception &e)
{
std::cout << "Json parse error: " << e.what() << std::endl;
}
catch(std::exception &e)
{
std::cout << e.what() << std::endl;
@ -261,7 +265,7 @@ void LibraryWindow::StoryFileDownloadedCallback(bool success, const std::string
std::cout << "Finished to download: " << filename << std::endl;
std::string ext = StoryProject::GetFileExtension(filename);
std::string ext = SysLib::GetFileExtension(filename);
if (ext == "zip")
{
@ -354,7 +358,7 @@ inline void InfosPane(const char *vFilter, IGFDUserDatas vUserDatas, bool *vCant
std::string LibraryWindow::ToLocalStoreFile(const std::string &url)
{
auto filename = StoryProject::GetFileName(url);
auto filename = SysLib::GetFileName(url);
filename = m_libraryManager.LibraryPath() + "/store/" + filename;
std::cout << "Store file: " << filename << std::endl;
@ -576,7 +580,7 @@ void LibraryWindow::Draw()
m_downloadQueue.push({
"dl",
obj.download,
ToLocalStoreFile(StoryProject::Normalize(obj.title)),
ToLocalStoreFile(SysLib::Normalize(obj.title)),
std::bind(&LibraryWindow::StoryFileDownloadedCallback, this, std::placeholders::_1, std::placeholders::_2)
});
}
@ -647,9 +651,6 @@ void LibraryWindow::Draw()
if (std::filesystem::is_directory(outputDir))
{
// Generate TLV file (index of all stories)
m_libraryManager.Save();
// Copy all files to device
m_libraryManager.CopyToDevice(outputDir);
}

View file

@ -21,7 +21,8 @@
#include "ImGuiFileDialog.h"
MainWindow::MainWindow()
: m_emulatorWindow(*this)
: m_libraryManager(*this)
, m_emulatorWindow(*this)
, m_resourcesWindow(*this)
, m_nodeEditorWindow(*this)
, m_libraryWindow(*this, m_libraryManager)
@ -658,7 +659,7 @@ void MainWindow::OpenProject(const std::string &uuid)
void MainWindow::ImportProject(const std::string &fileName, int format)
{
PackArchive archive;
PackArchive archive(*this);
archive.ImportStudioFormat(fileName, m_libraryManager.LibraryPath());
@ -710,7 +711,7 @@ void MainWindow::Loop()
while (!done)
{
auto time = SDL_GetTicks();
Uint64 frameStart = SDL_GetTicks();
bool aboutToClose = m_gui.PollEvent();
m_gui.StartFrame();
@ -755,8 +756,10 @@ void MainWindow::Loop()
// Rendering and event handling
if ((SDL_GetTicks() - time) < 10) {
SDL_Delay(10);
Uint64 frameTime = SDL_GetTicks() - frameStart; // Temps écoulé pour la frame
if (frameTime < 16) { // Limite de 60 FPS
SDL_Delay(16 - frameTime); // Attendez pour compléter la frame
}
@ -819,19 +822,46 @@ std::shared_ptr<BaseNode> MainWindow::CreateNode(const std::string &type)
void MainWindow::Build(bool compileonly)
{
// 1. First compile nodes to assembly
if (CompileToAssembler())
if (m_story->GenerateScript(m_currentCode))
{
// 2. Compile the assembly to machine binary
GenerateBinary();
m_editorWindow.SetScript(m_currentCode);
m_dbg.run_result = VM_FINISHED;
m_dbg.free_run = false;
if (!compileonly)
{
// 3. Convert all media to desired type format
ConvertResources();
m_resources.ConvertResources(m_story->AssetsPath(), ""); // pas de répertoire de destination
}
Chip32::Assembler::Error err;
if (m_story->GenerateBinary(m_currentCode, err))
{
m_result.Print();
Log("Binary successfully generated.");
if (m_story->CopyProgramTo(m_rom_data, sizeof (m_rom_data)))
{
// m_ramView->SetMemory(m_ram_data, sizeof(m_ram_data));
// m_romView->SetMemory(m_rom_data, m_program.size());
m_story->SaveBinary();
chip32_initialize(&m_chip32_ctx);
m_dbg.run_result = VM_READY;
UpdateVmView();
}
else
{
Log("Program too big. Expand ROM memory.");
}
}
else
{
Log(err.ToString(), true);
m_editorWindow.AddError(err.line, err.message); // show also the error in the code editor
}
}
}
void MainWindow::DeleteNode(const std::string &id)
@ -849,94 +879,18 @@ std::list<std::shared_ptr<Connection>> MainWindow::GetNodeConnections(const std:
return m_story->GetNodeConnections(nodeId);
}
bool MainWindow::CompileToAssembler()
{
// 1. Check if the model can be compiled, check for errors and report
// FIXME
// 2. Generate the assembly code from the model
bool ret = m_story->Build(m_currentCode);
// Add global functions
if (ret)
{
std::string buffer;
std::ifstream f("scripts/media.asm");
f.seekg(0, std::ios::end);
buffer.resize(f.tellg());
f.seekg(0);
f.read(buffer.data(), buffer.size());
m_currentCode += buffer;
m_editorWindow.SetScript(m_currentCode);
}
return ret;
}
void MainWindow::GenerateBinary()
{
m_dbg.run_result = VM_FINISHED;
m_dbg.free_run = false;
if (m_assembler.Parse(m_currentCode) == true )
{
if (m_assembler.BuildBinary(m_program, m_result) == true)
{
m_result.Print();
Log("Binary successfully generated.");
// Update ROM memory
std::copy(m_program.begin(), m_program.end(), m_rom_data);
// FIXME
// m_ramView->SetMemory(m_ram_data, sizeof(m_ram_data));
// m_romView->SetMemory(m_rom_data, m_program.size());
m_story->SaveBinary(m_program);
chip32_initialize(&m_chip32_ctx);
m_dbg.run_result = VM_READY;
UpdateVmView();
// DebugContext::DumpCodeAssembler(m_assembler);
}
else
{
Chip32::Assembler::Error err = m_assembler.GetLastError();
Log(err.ToString(), true);
m_editorWindow.AddError(err.line, err.message); // show also the error in the code editor
}
}
else
{
Chip32::Assembler::Error err = m_assembler.GetLastError();
Log(err.ToString(), true);
m_editorWindow.AddError(err.line, err.message); // show also the error in the code editor
}
}
void MainWindow::UpdateVmView()
{
// FIXME
// m_vmDock->updateRegistersView(m_chip32_ctx);
// Highlight next line in the test editor
uint32_t pcVal = m_chip32_ctx.registers[PC];
// On recherche quelle est la ligne qui possède une instruction à cette adresse
std::vector<Chip32::Instr>::const_iterator ptr = m_assembler.Begin();
for (; ptr != m_assembler.End(); ++ptr)
uint32_t line = 1;
if (m_story->GetAssemblyLine(pcVal, line))
{
if ((ptr->addr == pcVal) && ptr->isRomCode())
{
break;
}
}
if (ptr != m_assembler.End())
{
m_dbg.line = (ptr->line - 1);
m_dbg.line = (line - 1);
m_editorWindow.HighlightLine(m_dbg.line);
}
else
@ -944,51 +898,10 @@ void MainWindow::UpdateVmView()
// Not found
Log("Reached end or instruction not found line: " + std::to_string(m_dbg.line));
}
// Refresh RAM content
// m_ramView->SetMemory(m_ram_data, m_chip32_ctx.ram.size);
}
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();
int retCode = 0;
if ((*it)->format == "PNG")
{
outputfile += ".qoi"; // FIXME: prendre la config en cours désirée
retCode = MediaConverter::ImageToQoi(inputfile, outputfile);
}
else if ((*it)->format == "MP3")
{
outputfile += ".wav"; // FIXME: prendre la config en cours désirée
retCode = MediaConverter::Mp3ToWav(inputfile, outputfile);
}
else if ((*it)->format == "OGG")
{
outputfile += ".wav"; // FIXME: prendre la config en cours désirée
retCode = MediaConverter::OggToWav(inputfile, outputfile);
}
else
{
Log("Skipped: " + inputfile + ", unknown format" + outputfile, true);
}
if (retCode < 0)
{
Log("Failed to convert media file " + inputfile + ", error code: " + std::to_string(retCode) + " to: " + outputfile, true);
}
else if (retCode == 0)
{
Log("Convertered file: " + inputfile);
}
}
}
void MainWindow::SaveParams()
{

View file

@ -63,7 +63,7 @@ struct DebugContext
};
class MainWindow : public IStoryManager, public IAudioEvent
class MainWindow : public IStoryManager, public IAudioEvent, public ILogger
{
public:
MainWindow();
@ -82,9 +82,7 @@ private:
uint8_t m_ram_data[16*1024];
chip32_ctx_t m_chip32_ctx;
// Assembleur & Debugger
std::vector<uint8_t> m_program;
Chip32::Assembler m_assembler;
Chip32::Result m_result;
DebugContext m_dbg;
std::string m_currentCode;
@ -124,7 +122,6 @@ private:
// From IStoryManager (proxy to StoryProject class)
virtual void OpenProject(const std::string &uuid) override;
virtual void ImportProject(const std::string &fileName, int format);
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;
virtual std::pair<FilterIterator, FilterIterator> Images() override;
@ -148,6 +145,10 @@ private:
virtual void Next() override;
virtual void Previous() override;
// From ILogger
virtual void Log(const std::string &txt, bool critical = false) override;
// From IAudioEvent
virtual void EndOfAudio() override;
@ -162,9 +163,6 @@ private:
void CloseProject();
void DrawStatusBar();
bool CompileToAssembler();
void ConvertResources();
void GenerateBinary();
void UpdateVmView();
uint8_t Syscall(chip32_ctx_t *ctx, uint8_t code);
std::string GetFileNameFromMemory(uint32_t addr);

View file

@ -1,7 +1,7 @@
#include "media_node.h"
#include "story_project.h"
#include "connection.h"
#include "sys_lib.h"
static std::string ChoiceLabel(const std::string &id)
{
@ -74,25 +74,26 @@ std::string MediaNode::Build(IStoryProject &story, int nb_out_conns)
<< " Type: "
<< (nb_out_conns == 0 ? "End" : nb_out_conns == 1 ? "Transition" : "Choice")
<< "\n";
std::string image = StoryProject::RemoveFileExtension(image);
std::string sound = StoryProject::RemoveFileExtension(sound);
std::string img = SysLib::RemoveFileExtension(image);
std::string snd = SysLib::RemoveFileExtension(sound);
// Le label de ce noeud est généré de la façon suivante :
// "media" + Node ID + id du noeud parent. Si pas de noeud parent, alors rien
ss << BaseNode::GetEntryLabel(GetId()) << ":\n";
if (image.size() > 0)
if (img.size() > 0)
{
ss << "lcons r0, $" << image << "\n";
ss << "lcons r0, $" << img << "\n";
}
else
{
ss << "lcons r0, 0\n";
}
if (sound.size() > 0)
if (snd.size() > 0)
{
ss << "lcons r1, $" << sound << "\n";
ss << "lcons r1, $" << snd << "\n";
}
else
{