Intermediatre commit: separation between logic and UI
Some checks failed
Build-StoryEditor / build_linux (push) Has been cancelled
Build-StoryEditor / build_win32 (push) Has been cancelled
Deploy-Documentation / deploy (push) Has been cancelled

This commit is contained in:
anthony@rabine.fr 2025-08-07 22:51:23 +02:00
parent d2fac4d79e
commit 8a2f70ea01
30 changed files with 1669 additions and 624 deletions

View file

@ -0,0 +1,16 @@
#ifndef I_AUDIO_EVENT_H
#define I_AUDIO_EVENT_H
// Interface pour gérer les événements audio, comme la fin d'une piste.
// Ceci permet de découpler le lecteur audio de l'entité qui doit réagir à ces événements (par exemple, la VM).
class IAudioEvent
{
public:
virtual ~IAudioEvent() = default;
// Appelé lorsque la lecture d'une piste audio est terminée.
virtual void EndOfAudio() = 0;
};
#endif // I_AUDIO_EVENT_H

View file

@ -2,10 +2,16 @@
#include <string>
class ILogSubject {
public:
virtual void LogEvent(const std::string &txt, bool critical = false) = 0;
};
class ILogger
{
public:
virtual void Log(const std::string &txt, bool critical = false) = 0;
virtual void RegisterSubject(std::shared_ptr<ILogSubject> subject) = 0;
virtual ~ILogger() {}
};

View file

@ -42,14 +42,6 @@ public:
// Modules
virtual void OpenModule(const std::string &uuid) = 0;
// Resources management
virtual std::pair<FilterIterator, FilterIterator> Images() = 0;
virtual std::pair<FilterIterator, FilterIterator> Sounds() = 0;
virtual std::pair<FilterIterator, FilterIterator> Resources() = 0;
virtual void AddResource(std::shared_ptr<Resource> res) = 0;
virtual void ClearResources() = 0;
virtual void DeleteResource(FilterIterator &it) = 0;
// Node interaction
virtual void BuildNodes(bool compileonly) = 0;
virtual void BuildCode(bool compileonly) = 0;

View file

@ -9,21 +9,13 @@
#include <queue>
#include <thread>
#include "thread_safe_queue.h"
#include "i_audio_event.h"
struct AudioCommand {
std::string order;
std::string filename;
};
class IAudioEvent
{
public:
virtual ~IAudioEvent() {}
virtual void EndOfAudio() = 0;
};
class AudioPlayer
{

View file

@ -120,6 +120,8 @@ set(SRCS
src/main.cpp
src/main_window.cpp
src/app/app_controller.cpp
src/windows/window_base.cpp
src/windows/console_window.cpp
src/windows/library_window.cpp
@ -167,7 +169,6 @@ set(SRCS
../shared/resource_manager.cpp
../shared/library_manager.cpp
../shared/downloader.cpp
../shared/story_db.cpp
../shared/miniz.c
../shared/zip.cpp
../shared/platform_folders.cpp
@ -187,10 +188,9 @@ set(SRCS
../core/story-manager/src/nodes/print_node.cpp
../core/story-manager/src/nodes/syscall_node.cpp
../core/story-manager/src/nodes/connection.cpp
../core/story-manager/lib/sys_lib.cpp
../core/story-manager/lib/resource.cpp
../core/story-manager/src/story_db.cpp
../core/story-manager/src/sys_lib.cpp
../core/story-manager/src/resource.cpp
../core/chip32/chip32_assembler.cpp
../core/chip32/chip32_vm.c
@ -267,6 +267,8 @@ target_include_directories(${STORY_EDITOR_PROJECT} PUBLIC
src/windows
src/docks
src/dialogs
src/app
src/events
../firmware/library
../core/chip32
@ -274,7 +276,6 @@ target_include_directories(${STORY_EDITOR_PROJECT} PUBLIC
../core/story-manager/src
../core/story-manager/src/nodes
../core/story-manager/src/compiler
../core/story-manager/lib
../core/story-manager/interfaces
)

View file

@ -1,6 +1,6 @@
[Window][WindowOverViewport_11111111]
Pos=60,26
Size=1220,694
Size=1220,962
Collapsed=0
[Window][Debug##Default]
@ -9,32 +9,32 @@ Size=400,400
Collapsed=0
[Window][Library Manager]
Pos=568,26
Size=712,288
Pos=878,26
Size=402,403
Collapsed=0
DockId=0x00000003,0
[Window][Console]
Pos=60,467
Pos=60,735
Size=610,253
Collapsed=0
DockId=0x00000004,0
[Window][Emulator]
Pos=568,26
Size=712,288
Pos=878,26
Size=402,403
Collapsed=0
DockId=0x00000003,5
[Window][Code viewer]
Pos=568,26
Size=712,288
Pos=878,26
Size=402,403
Collapsed=0
DockId=0x00000003,4
[Window][Resources]
Pos=568,26
Size=712,288
Pos=878,26
Size=402,403
Collapsed=0
DockId=0x00000003,1
@ -50,36 +50,36 @@ Size=150,42
Collapsed=0
[Window][Variables]
Pos=672,467
Pos=672,735
Size=608,253
Collapsed=0
DockId=0x00000005,0
[Window][CPU]
Pos=568,26
Size=712,288
Pos=878,26
Size=402,403
Collapsed=0
DockId=0x00000003,2
[Window][RAM view]
Pos=568,26
Size=712,288
Pos=878,26
Size=402,403
Collapsed=0
DockId=0x00000003,3
[Window][Properties]
Pos=568,316
Size=712,149
Pos=878,431
Size=402,302
Collapsed=0
DockId=0x00000006,0
[Window][ToolBar]
Pos=0,26
Size=60,694
Size=60,962
Collapsed=0
[Window][QuitConfirm]
Pos=508,312
Pos=508,446
Size=264,96
Collapsed=0
@ -90,13 +90,13 @@ Collapsed=0
[Window][Module editor]
Pos=60,26
Size=506,439
Size=816,707
Collapsed=0
DockId=0x00000001,1
[Window][Story editor]
Pos=60,26
Size=506,439
Size=816,707
Collapsed=0
DockId=0x00000001,0
@ -133,12 +133,12 @@ Column 1 Width=104
Column 2 Width=120
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,962 Split=Y
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,439 Split=X
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=506,694 CentralNode=1 Selected=0x93ADCAAB
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=712,694 Split=Y Selected=0x52EB28B5
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,369 Selected=0x63869CAF
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,191 Selected=0x8C72BEA8
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=816,694 CentralNode=1 Selected=0x93ADCAAB
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=402,694 Split=Y Selected=0x52EB28B5
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,250 Selected=0x4B07C626
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,187 Selected=0x8C72BEA8
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,253 Split=X Selected=0xEA83D666
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=610,192 Selected=0xEA83D666
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=608,192 Selected=0x6DE9B20C

View file

@ -0,0 +1,973 @@
// app_controller.cpp
#include "app_controller.h"
// Inclure les entêtes des dépendances de AppController nécessaires pour l'implémentation
#include <filesystem>
#include <fstream>
#include <chrono> // Pour std::this_thread::sleep_for
#include <thread> // Pour std::this_thread
#include <algorithm> // Pour std::find
#include "platform_folders.h" // Pour pf::getConfigHome()
#include "uuid.h" // Pour la génération d'UUID
#include "sys_lib.h" // Pour SysLib::ReadFile, GetFileExtension, GetFileName
#include "pack_archive.h" // Pour PackArchive
#include "json.hpp"
#include "variable.h" // Pour Variable
#include "all_events.h"
// Définitions des registres et événements CHIP-32 si non déjà dans chip32_vm.h
// Assurez-vous que ces définitions sont accessibles.
#ifndef R0
#define R0 0
#endif
#ifndef R1
#define R1 1
#endif
#ifndef R2
#define R2 2
#endif
#ifndef R3
#define R3 3
#endif
#ifndef R4
#define R4 4
#endif
#ifndef PC
#define PC 7 // Exemple de registre Program Counter
#endif
#ifndef EV_MASK_OK_BUTTON
#define EV_MASK_OK_BUTTON 1
#endif
#ifndef EV_MASK_HOME_BUTTON
#define EV_MASK_HOME_BUTTON 2
#endif
#ifndef EV_MASK_PREVIOUS_BUTTON
#define EV_MASK_PREVIOUS_BUTTON 4
#endif
#ifndef EV_MASK_NEXT_BUTTON
#define EV_MASK_NEXT_BUTTON 8
#endif
#ifndef EV_MASK_END_OF_AUDIO
#define EV_MASK_END_OF_AUDIO 16
#endif
// Définitions pour les codes de retour de Syscall
#ifndef SYSCALL_RET_OK
#define SYSCALL_RET_OK 0
#endif
#ifndef SYSCALL_RET_WAIT_EV
#define SYSCALL_RET_WAIT_EV 1 // Exemple: VM doit attendre un événement
#endif
// Implémentation du callback pour le syscall de la VM
// C'est un peu délicat avec une fonction membre.
// Une solution courante est d'utiliser un 'trampoline' ou std::function.
// Pour ce projet, nous allons faire une adaptation simple en passant 'this'
// via un mécanisme global ou un wrapper. L'approche originale de MainWindow
// utilisait une classe Callback statique, que nous allons adapter ici.
// Classe trampoline pour les syscalls de la VM
// Ceci est une version simplifiée. Dans un système plus robuste, vous pourriez
// vouloir un registre de callbacks ou une approche plus souple.
class SyscallTrampoline
{
public:
static AppController* s_instance; // Pointer vers l'instance de AppController
static uint8_t Callback(chip32_ctx_t *ctx, uint8_t code)
{
if (s_instance)
{
return s_instance->Syscall(ctx, code);
}
return SYSCALL_RET_OK; // Ou gérer l'erreur
}
};
AppController* SyscallTrampoline::s_instance = nullptr;
AppController::AppController(ILogger& logger, EventBus& eventBus)
: m_logger(logger)
, m_eventBus(eventBus) // m_eventBus pour les événements
, m_resources(logger) // m_resources a besoin d'un IStoryManager/IResourceSource
, m_nodesFactory(logger) // m_nodesFactory a besoin d'un IStoryManager/IResourceSource
, m_libraryManager(logger, m_nodesFactory) // m_libraryManager a besoin d'un IStoryManager/INodeFactory
, m_player(*this) // m_player a besoin d'un IAudioEvent
, m_webServer(m_libraryManager)
{
// VM Initialize - Déplacé du constructeur de MainWindow
m_chip32_ctx.stack_size = 512;
m_chip32_ctx.rom.mem = m_rom_data;
m_chip32_ctx.rom.addr = 0;
m_chip32_ctx.rom.size = sizeof(m_rom_data);
m_chip32_ctx.ram.mem = m_ram_data;
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
m_chip32_ctx.ram.size = sizeof(m_ram_data);
// Initialise le trampoline de syscall avec cette instance
// SyscallTrampoline::s_instance = this;
// m_chip32_ctx.syscall = SyscallTrampoline::Callback;
Callback<uint8_t(chip32_ctx_t *, uint8_t)>::func = std::bind(&AppController::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);
// Assurez-vous que ces fonctions existent ou sont implémentées ailleurs
// CloseProject() et CloseModule() étaient dans MainWindow
// Si elles gèrent des états VM/projet, elles doivent être ici.
CloseProject();
CloseModule();
}
AppController::~AppController()
{
SaveParams(); // Sauvegarde les paramètres à la destruction
}
bool AppController::Initialize()
{
LoadParams();
m_nodesFactory.ScanModules();
m_player.Initialize(); // Initialise l'audio
return true; // Supposons que l'initialisation de AppController est toujours un succès simple
}
// --- Implémentations de IStoryManager ---
void AppController::OpenProject(const std::string &uuid)
{
CloseProject();
m_story = m_libraryManager.GetStory(uuid);
// DEBUG CODE !!!!!!!!!!!!! Permet si décommenter de forcer l'import, permet de tester plus facilement l'algo en ouvrant le projet
// PackArchive arch(*this);
// std::string basePath = m_libraryManager.LibraryPath() + "/" + uuid;
// arch.ConvertJsonStudioToOst(basePath, uuid, m_libraryManager.LibraryPath());
if (!m_story)
{
Log("Cannot find story: " + uuid);
}
else if (m_story->Load(m_resources, m_nodesFactory))
{
Log("Open project success");
m_eventBus.Emit(std::make_shared<OpenProjectEvent>(uuid));
}
}
void AppController::Log(const std::string &txt, bool critical)
{
m_logger.Log(txt, critical);
}
void AppController::PlaySoundFile(const std::string &fileName)
{
m_player.Play(fileName);
}
std::string AppController::BuildFullAssetsPath(const std::string_view fileName) const
{
if (m_story)
{
return m_story->BuildFullAssetsPath(fileName);
}
return std::string();
}
void AppController::OpenFunction(const std::string &uuid, const std::string &name)
{
m_eventBus.Emit(std::make_shared<OpenFunctionEvent>(uuid, name));
}
void AppController::NewStory()
{
CloseProject();
m_story = m_libraryManager.NewProject();
if (m_story)
{
SaveStory(); // Sauvegarde le nouveau projet
m_libraryManager.Scan(); // Ajoute le nouveau projet à la bibliothèque
OpenStory(m_story->GetUuid()); // Ouvre le nouveau projet
}
}
void AppController::OpenStory(const std::string &uuid_or_path)
{
CloseProject();
// Tente d'abord d'ouvrir par UUID via LibraryManager
m_story = m_libraryManager.GetStory(uuid_or_path);
// Si non trouvé par UUID, essaie d'ouvrir par chemin direct (si uuid_or_path est un chemin)
if (!m_story && std::filesystem::exists(uuid_or_path)) {
// Crée un StoryProject à partir d'un chemin existant si nécessaire
// Cette logique est plus complexe et dépend de comment votre StoryProject est construit
// For now, assume GetStory can handle paths if it's not a UUID
// Or you might need a dedicated method like m_libraryManager.GetStoryByPath()
// For simplicity, if it's a path, try to find its UUID or create temp StoryProject
// This part needs careful design based on your LibraryManager/StoryProject logic.
// For this example, we assume GetStory() attempts to resolve paths if it's not a UUID.
m_logger.Log("Attempting to open story by path (not UUID): " + uuid_or_path);
// A more robust solution would involve LibraryManager::GetStory(path) or similar.
// For now, if GetStory(uuid) failed and it's a path, we might assume it's a direct project path.
// This is a simplification and might need adjustment based on your actual library logic.
// If your StoryProject can be loaded directly from a path:
// m_story = std::make_shared<StoryProject>(uuid_or_path); // Example, constructor might need more args
// m_story->Load(m_resources, m_nodesFactory);
// Or, more likely, LibraryManager needs to handle this.
}
if (!m_story)
{
m_logger.Log("Cannot find story: " + uuid_or_path, true);
}
else if (m_story->Load(m_resources, m_nodesFactory))
{
m_logger.Log("Open project success: " + m_story->GetProjectFilePath());
// Add to recent if not exists
const auto& proj_path = m_story->GetProjectFilePath();
AddRecentProject(proj_path);
// Notify GUI (or specific windows) to enable/load project data
// These calls would be replaced by events or direct calls to GUI objects in MainWindow
// For now, we'll keep the conceptual calls that AppController would trigger:
// m_nodeEditorWindow.Load(m_story); // This logic belongs to GUI, AppController notifies
// m_nodeEditorWindow.Enable(); // This logic belongs to GUI
// etc.
UpdateVmView(); // To refresh debugger/CPU views
}
else
{
m_logger.Log("Open project error for: " + uuid_or_path, true);
}
}
void AppController::SaveStory(const std::string &path)
{
if (m_story)
{
m_story->Save(m_resources);
m_logger.Log("Project saved: " + m_story->GetProjectFilePath());
} else {
m_logger.Log("No project open to save.", true);
}
}
void AppController::ExportStory(const std::string &filename)
{
// Logique d'exportation de l'histoire
// Cette partie est manquante dans le snippet original, mais devrait être ici.
m_logger.Log("Export story to: " + filename);
}
void AppController::BuildNodes(IStoryProject::Type type)
{
if (type == IStoryProject::Type::PROJECT_TYPE_STORY && m_story) {
if (m_story->GenerateScript(m_currentCode))
{
// La GUI (DebuggerWindow) doit être notifiée de cette mise à jour.
// Au lieu de appeler m_debuggerWindow.SetScript(m_currentCode); directement,
// AppController pourrait émettre un événement ou un callback.
// Pour l'instant, on suppose une notification ou que la GUI tire les données.
m_logger.Log("Nodes script generated for story.");
Build(true); // Compile seulement par défaut
}
} else if (type == IStoryProject::Type::PROJECT_TYPE_MODULE && m_module) {
// Logique de génération de script pour le module
// Similaire à BuildNodes pour le projet principal.
m_logger.Log("Nodes script generated for module.");
}
}
void AppController::Build(bool compileonly)
{
m_dbg.run_result = VM_FINISHED;
m_dbg.free_run = false;
m_dbg.m_breakpoints.clear(); // Clear breakpoints on new build
if (!m_story) {
m_logger.Log("No story loaded to build.", true);
return;
}
if (!compileonly)
{
// Convert all media to desired type format
auto options = m_story->GetOptions();
m_resources.ConvertResources(m_story->AssetsPath(), "", options.image_format, options.sound_format);
}
Chip32::Assembler::Error err;
// La GUI (DebuggerWindow) doit être notifiée pour effacer les erreurs.
// m_debuggerWindow.ClearErrors();
if (m_story->GenerateBinary(m_currentCode, err))
{
m_result.Print(); // Imprime le résultat de l'assemblage (Debug uniquement)
if (m_story->CopyProgramTo(m_rom_data, sizeof (m_rom_data)))
{
m_story->SaveBinary();
chip32_initialize(&m_chip32_ctx);
m_dbg.run_result = VM_READY;
UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM
m_logger.Log("Build successful. VM ready.");
}
else
{
m_logger.Log("Program too big. Expand ROM memory.", true);
}
}
else
{
m_logger.Log(err.ToString(), true);
// La GUI (DebuggerWindow) doit être notifiée pour ajouter l'erreur.
// m_debuggerWindow.AddError(err.line, err.message);
}
}
void AppController::BuildCode(std::shared_ptr<StoryProject> story, bool compileonly, bool force)
{
// Note: Dans le code original, BuildCode lisait m_externalSourceFileName.
// Il faut s'assurer que m_currentCode est bien défini avant d'appeler Build.
if (story) {
m_currentCode = SysLib::ReadFile(story->GetProjectFilePath()); // Simplifié pour l'exemple
// La GUI (DebuggerWindow) doit être notifiée pour SetScript.
// m_debuggerWindow.SetScript(m_currentCode);
Build(compileonly);
} else {
m_logger.Log("No story provided for BuildCode.", true);
}
}
void AppController::SetExternalSourceFile(const std::string &filename)
{
m_externalSourceFileName = filename;
m_logger.Log("External source file set to: " + filename);
}
void AppController::LoadBinaryStory(const std::string &filename)
{
FILE *fp = fopen(filename.c_str(), "rb");
if (fp != NULL)
{
fseek(fp, 0L, SEEK_END);
long int sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (sz <= m_chip32_ctx.rom.size)
{
size_t sizeRead = fread(m_chip32_ctx.rom.mem, 1, sz, fp); // Corrected fread args
if (sizeRead == (size_t)sz) // Cast sz to size_t for comparison
{
m_dbg.run_result = VM_READY;
chip32_initialize(&m_chip32_ctx);
m_logger.Log("Loaded binary file: " + filename);
UpdateVmView();
}
else
{
m_logger.Log("Failed to load binary file completely. Read " + std::to_string(sizeRead) + " of " + std::to_string(sz) + " bytes.", true);
}
} else {
m_logger.Log("Binary file is too large for ROM: " + std::to_string(sz) + " bytes, max " + std::to_string(m_chip32_ctx.rom.size) + " bytes.", true);
}
fclose(fp);
} else {
m_logger.Log("Failed to open binary file: " + filename, true);
}
}
void AppController::ToggleBreakpoint(int line)
{
if (m_dbg.m_breakpoints.contains(line))
{
m_dbg.m_breakpoints.erase(line);
m_logger.Log("Removed breakpoint at line: " + std::to_string(line + 1));
}
else
{
m_dbg.m_breakpoints.insert(line);
m_logger.Log("Set breakpoint at line: " + std::to_string(line + 1));
}
// Notify debugger window to update breakpoint display
}
uint32_t AppController::GetRegister(int reg)
{
uint32_t regVal = 0;
if (reg >= 0 && reg < REGISTER_COUNT) // Assurez-vous que REGISTER_COUNT est défini
{
regVal = m_chip32_ctx.registers[reg];
}
return regVal;
}
void AppController::ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation)
{
if (m_story)
{
m_story->ScanVariable(operation);
}
}
void AppController::AddVariable()
{
if (m_story)
{
m_story->AddVariable();
m_logger.Log("Variable added to project: " + m_story->GetProjectFilePath());
} else {
m_logger.Log("No project open to add a variable.", true);
}
}
void AppController::DeleteVariable(int i)
{
if (m_story)
{
m_story->DeleteVariable(i);
m_logger.Log("Variable deleted from project: " + m_story->GetProjectFilePath());
} else {
m_logger.Log("No project open to delete a variable.", true);
}
}
void AppController::Play()
{
if ((m_dbg.run_result == VM_READY) || (m_dbg.run_result == VM_FINISHED))
{
m_dbg.free_run = true;
m_dbg.run_result = VM_OK; // actually starts the execution
m_logger.Log("VM: Play initiated.");
}
}
void AppController::Step()
{
m_eventQueue.push({VmEventType::EvStep});
m_logger.Log("VM: Step event queued.");
}
void AppController::Run()
{
m_eventQueue.push({VmEventType::EvRun});
m_logger.Log("VM: Run event queued.");
}
void AppController::Ok()
{
m_eventQueue.push({VmEventType::EvOkButton});
m_logger.Log("VM: OK button event queued.");
}
void AppController::Stop()
{
m_dbg.run_result = VM_FINISHED;
m_dbg.free_run = false;
m_logger.Log("VM: Stopped.");
}
void AppController::Pause()
{
// Logic for pausing VM (if VM supports pausing execution mid-instruction)
// For now, if free_run is false and VM_OK, it effectively waits for event
m_dbg.free_run = false;
m_logger.Log("VM: Pause requested.");
}
void AppController::Home()
{
m_eventQueue.push({VmEventType::EvHomeButton});
m_logger.Log("VM: Home button event queued.");
}
void AppController::Next()
{
m_logger.Log("VM: Next button event queued.");
m_eventQueue.push({VmEventType::EvNextButton});
}
void AppController::Previous()
{
m_logger.Log("VM: Previous button event queued.");
m_eventQueue.push({VmEventType::EvPreviousButton});
}
std::string AppController::VmState() const
{
std::string state = "Unknown";
switch (m_dbg.run_result)
{
case VM_READY:
state = "VM Ready";
break;
case VM_FINISHED:
state = "VM Finished";
break;
case VM_SKIPED:
state = "VM Skipped"; // Typo in original, corrected to Skipped
break;
case VM_WAIT_EVENT:
state = "VM Wait Event";
break;
case VM_OK:
state = "VM Ok";
break;
default:
state = "VM Error";
break;
}
return state;
}
// --- Implémentations de IAudioEvent ---
void AppController::EndOfAudio()
{
m_logger.Log("End of audio track. Queuing event for VM.");
m_eventQueue.push({VmEventType::EvAudioFinished});
}
// --- Fonctions internes de AppController (logique métier) ---
void AppController::SaveParams()
{
nlohmann::json j;
j["recents"] = m_recentProjects;
j["library_path"] = m_libraryManager.LibraryPath();
j["store_url"] = m_libraryManager.GetStoreUrl();
std::string loc = pf::getConfigHome() + "/ost_settings.json";
std::ofstream o(loc);
o << std::setw(4) << j << std::endl;
m_logger.Log("Saved settings to: " + loc);
}
void AppController::LoadParams()
{
try {
std::filesystem::path dlDir = std::filesystem::path(pf::getConfigHome()) / "ost_modules";
std::filesystem::create_directories(dlDir);
m_nodesFactory.SetModulesRootDirectory(dlDir.string());
std::string loc = pf::getConfigHome() + "/ost_settings.json";
std::ifstream i(loc);
nlohmann::json j;
i >> j;
if (j.contains("recents")) {
for (auto& element : j["recents"]) {
std::string path_str = element.get<std::string>();
if (std::filesystem::exists(path_str))
{
m_recentProjects.push_back(path_str);
}
}
}
if (j.contains("library_path")) {
std::string lib_path = j["library_path"].get<std::string>();
if (std::filesystem::exists(lib_path))
{
m_libraryManager.Initialize(lib_path);
}
}
if (j.contains("store_url")) {
m_libraryManager.SetStoreUrl(j.value("store_url", "https://gist.githubusercontent.com/DantSu/3aea4c1fe15070bcf394a40b89aec33e/raw/stories.json"));
} else {
m_logger.Log("No 'store_url' found in settings, using default.", false);
}
}
catch(const std::exception &e)
{
m_logger.Log("Error loading settings: " + std::string(e.what()), true);
}
}
std::string AppController::GetStringFromMemory(uint32_t addr)
{
// Buffer local pour la chaîne
// Assurez-vous qu'il est assez grand pour gérer les chaînes de votre VM
// et qu'il est terminé par null
char strBuf[256]; // Augmenté la taille pour plus de sécurité
// Le bit le plus significatif indique si c'est de la RAM (0x80000000) ou ROM
bool isRam = (addr & 0x80000000) != 0;
addr &= 0xFFFF; // Masque pour obtenir l'adresse 16 bits
// Vérification de l'adresse pour éviter les dépassements de buffer
if (isRam) {
if (addr < m_chip32_ctx.ram.size) {
strncpy(strBuf, (const char *)&m_chip32_ctx.ram.mem[addr], sizeof(strBuf) - 1);
strBuf[sizeof(strBuf) - 1] = '\0'; // S'assurer que c'est null-terminated
} else {
m_logger.Log("GetStringFromMemory: Invalid RAM address: 0x" + std::to_string(addr), true);
return "";
}
} else {
if (addr < m_chip32_ctx.rom.size) {
strncpy(strBuf, (const char *)&m_chip32_ctx.rom.mem[addr], sizeof(strBuf) - 1);
strBuf[sizeof(strBuf) - 1] = '\0'; // S'assurer que c'est null-terminated
} else {
m_logger.Log("GetStringFromMemory: Invalid ROM address: 0x" + std::to_string(addr), true);
return "";
}
}
return strBuf;
}
void AppController::ProcessStory()
{
if (m_dbg.run_result == VM_FINISHED || m_dbg.run_result > VM_OK)
return;
VmEvent event;
if (m_eventQueue.try_pop(event))
{
if (event.type == VmEventType::EvStep)
{
StepInstruction();
m_dbg.run_result = VM_OK; // Correction : ceci écrase le code de retour, potentiellement indésirable
}
else if (event.type == VmEventType::EvRun)
{
m_dbg.free_run = true;
m_dbg.run_result = VM_OK;
}
else if (event.type == VmEventType::EvStop)
{
m_dbg.run_result = VM_FINISHED;
}
// Events managed only if the code is in wait event state
if (m_dbg.run_result == VM_WAIT_EVENT)
{
if (event.type == VmEventType::EvOkButton)
{
if (m_dbg.IsValidEvent(EV_MASK_OK_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_OK_BUTTON;
m_dbg.run_result = VM_OK;
}
}
else if (event.type == VmEventType::EvPreviousButton)
{
if (m_dbg.IsValidEvent(EV_MASK_PREVIOUS_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_PREVIOUS_BUTTON;
m_dbg.run_result = VM_OK;
}
}
else if (event.type == VmEventType::EvNextButton)
{
if (m_dbg.IsValidEvent(EV_MASK_NEXT_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_NEXT_BUTTON;
m_dbg.run_result = VM_OK;
}
}
else if (event.type == VmEventType::EvAudioFinished)
{
if (m_dbg.IsValidEvent(EV_MASK_END_OF_AUDIO))
{
m_chip32_ctx.registers[R0] = EV_MASK_END_OF_AUDIO;
m_dbg.run_result = VM_OK;
}
}
else if (event.type == VmEventType::EvHomeButton)
{
if (m_dbg.IsValidEvent(EV_MASK_HOME_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_HOME_BUTTON;
m_dbg.run_result = VM_OK;
}
}
}
}
if (m_dbg.run_result == VM_OK)
{
if (m_dbg.m_breakpoints.contains(m_dbg.line))
{
m_logger.Log("Breakpoint hit on line: " + std::to_string(m_dbg.line + 1));
m_dbg.run_result = VM_WAIT_EVENT;
m_dbg.free_run = false;
}
if (m_dbg.free_run)
{
StepInstruction();
}
}
if (m_dbg.run_result == VM_FINISHED)
{
m_dbg.free_run = false;
}
else if (m_dbg.run_result > VM_OK)
{
std::string error = "VM Error: ";
switch (m_dbg.run_result)
{
case VM_ERR_STACK_OVERFLOW: error += "Stack overflow"; break;
case VM_ERR_STACK_UNDERFLOW: error += "Stack underflow"; break;
case VM_ERR_INVALID_ADDRESS: error += "Invalid address"; break;
case VM_ERR_UNSUPPORTED_OPCODE: error += "Unsupported opcode"; break;
case VM_ERR_UNKNOWN_OPCODE: error += "Unknown opcode"; break;
case VM_ERR_UNHANDLED_INTERRUPT: error += "Unhandled interrupt"; break;
case VM_ERR_INVALID_REGISTER: error += "Invalid register"; break;
default: error += "Unknown error"; break;
}
error += " (line: " + std::to_string(m_dbg.line) + ")";
m_logger.Log(error, true);
}
// In this case, we wait for single step debugger
if ((m_dbg.run_result == VM_OK) && !m_dbg.free_run)
{
m_dbg.run_result = VM_WAIT_EVENT;
}
}
void AppController::StepInstruction()
{
m_dbg.run_result = chip32_step(&m_chip32_ctx);
UpdateVmView();
}
uint8_t AppController::Syscall(chip32_ctx_t *ctx, uint8_t code)
{
uint8_t retCode = SYSCALL_RET_OK;
m_logger.Log("SYSCALL: " + std::to_string(code));
// Media
if (code == 1) // Execute media
{
// R0: image file name address, R1: sound file name address
if (ctx->registers[R0] != 0)
{
std::string imageFile = m_story->BuildFullAssetsPath(GetStringFromMemory(ctx->registers[R0]));
m_logger.Log("Image: " + imageFile);
// Ici, vous notifieriez la fenêtre de l'émulateur
// m_emulatorWindow.SetImage(imageFile); // Ceci est une dépendance GUI
}
else
{
// m_emulatorWindow.ClearImage(); // Dépendance GUI
}
if (ctx->registers[R1] != 0)
{
std::string soundFile = m_story->BuildFullAssetsPath(GetStringFromMemory(ctx->registers[R1]));
m_logger.Log("Sound: " + soundFile);
m_player.Play(soundFile);
}
retCode = SYSCALL_RET_OK;
}
else if (code == 2) // Wait for event
{
m_eventQueue.clear();
m_dbg.event_mask = ctx->registers[R0];
// optional timeout is located in R1
retCode = SYSCALL_RET_WAIT_EV;
}
else if (code == 3)
{
// FIXME: Unknown syscall 3, log it for debugging
m_logger.Log("Syscall 3 called (FIXME)", false);
}
else if (code == 4) // Printf (printf-like behavior)
{
std::string text = GetStringFromMemory(ctx->registers[R0]);
int arg_count = ctx->registers[R1];
char working_buf[200] = {0};
// Simplified printf logic for logging
switch(arg_count){
case 0: m_logger.Log(text); break;
case 1: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]); m_logger.Log(working_buf); break;
case 2: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3]); m_logger.Log(working_buf); break;
case 3: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]); m_logger.Log(working_buf); break;
default: m_logger.Log("Printf with unsupported arg_count: " + std::to_string(arg_count) + " text: " + text, true); break;
}
}
else if (code == 5) // WAIT (sleep)
{
// Sleep is in milliseconds, R0 contains the duration
std::this_thread::sleep_for(std::chrono::milliseconds(ctx->registers[R0]));
m_logger.Log("VM slept for " + std::to_string(ctx->registers[R0]) + " ms.");
}
else
{
m_logger.Log("Unknown syscall code: " + std::to_string(code), true);
}
return retCode;
}
void AppController::UpdateVmView()
{
// C'est une fonction de notification pour la GUI.
// AppController ne devrait pas directement manipuler les vues GUI.
// Au lieu de cela, il émettrait un signal ou appellerait un observer.
uint32_t pcVal = m_chip32_ctx.registers[PC];
if (m_story && m_story->GetAssemblyLine(pcVal, m_dbg.line))
{
m_logger.Log("Executing line: " + std::to_string(m_dbg.line + 1));
// m_debuggerWindow.HighlightLine(m_dbg.line); // Dépendance GUI
}
else
{
m_logger.Log("Reached end or instruction not found (line: " + std::to_string(m_dbg.line) + ")", false);
}
// m_cpuWindow.updateRegistersView(m_chip32_ctx); // Dépendance GUI
// m_memoryEditor.DrawWindow("RAM view", m_chip32_ctx.ram.mem, m_chip32_ctx.ram.size); // Dépendance GUI
}
void AppController::AddRecentProject(const std::string& projectPath)
{
// Remove if already exists to move to front
m_recentProjects.erase(
std::remove(m_recentProjects.begin(), m_recentProjects.end(), projectPath),
m_recentProjects.end()
);
// Add to front
m_recentProjects.insert(m_recentProjects.begin(), projectPath);
// Limit to 10 recent projects
if (m_recentProjects.size() > 10) {
m_recentProjects.resize(10);
}
// Save recent projects on disk
SaveParams();
}
void AppController::CloseProject()
{
if (m_story)
{
m_story->Clear(); // Clear story resources/data
m_story.reset(); // Release shared_ptr
}
// Clear VM state
m_dbg.run_result = VM_FINISHED;
m_dbg.free_run = false;
m_dbg.m_breakpoints.clear();
chip32_initialize(&m_chip32_ctx); // Reset VM context
m_resources.Clear(); // Clear loaded resources
m_eventQueue.clear(); // Clear any pending VM events
// Notify GUI (or specific windows) to clear/disable project-related data
// These would be events or direct calls from AppController to GUI objects
// m_nodeEditorWindow.Clear();
// m_emulatorWindow.ClearImage();
// m_consoleWindow.ClearLog();
// m_debuggerWindow.ClearErrors();
// m_debuggerWindow.SetScript("");
// m_nodeEditorWindow.Disable();
// m_emulatorWindow.Disable();
// m_debuggerWindow.Disable();
// m_resourcesWindow.Disable();
// m_PropertiesWindow.Disable();
// m_variablesWindow.Disable();
// m_cpuWindow.Disable();
m_logger.Log("Project closed.");
}
void AppController::NewModule()
{
m_module = m_nodesFactory.NewModule();
if (m_module) {
// Notify GUI (e.g., m_moduleEditorWindow.Load(m_module);)
// m_moduleEditorWindow.Enable();
m_logger.Log("New module created.");
}
}
void AppController::SaveModule()
{
m_nodesFactory.SaveAllModules(m_resources);
m_logger.Log("Modules saved.");
}
void AppController::OpenModule(const std::string &uuid)
{
m_module = m_nodesFactory.GetModule(uuid);
if (!m_module)
{
m_logger.Log("Cannot find module: " + uuid, true);
}
else if (m_module->Load(m_resources, m_nodesFactory))
{
m_logger.Log("Open module success: " + uuid);
// Notify GUI (e.g., m_moduleEditorWindow.Load(m_module);)
// m_moduleEditorWindow.Enable();
}
else
{
m_logger.Log("Open module error: " + uuid, true);
}
}
void AppController::CloseModule()
{
if (m_module) {
m_module->Clear();
m_module.reset();
}
// Notify GUI (e.g., m_moduleEditorWindow.Clear(); m_moduleEditorWindow.Disable();)
m_logger.Log("Module closed.");
}
void AppController::ImportProject(const std::string &filePathName, int format)
{
(void) format; // Not used in the original snippet but kept for signature.
PackArchive archive(*this, m_nodesFactory); // PackArchive constructor might need an ILogger, adjust if needed
auto ext = SysLib::GetFileExtension(filePathName);
auto filename = SysLib::GetFileName(filePathName);
if ((ext == "pk") || (filename == "ni"))
{
archive.ImportCommercialFormat(filePathName, m_libraryManager.LibraryPath(), m_libraryManager.CommercialDbView());
m_logger.Log("Imported commercial format: " + filePathName);
}
else if ((ext == "json") || (ext == "zip"))
{
archive.ImportStudioFormat(filePathName, m_libraryManager.LibraryPath());
m_logger.Log("Imported studio format: " + filePathName);
}
else
{
m_logger.Log("Unknown file format for import: " + filePathName, true);
}
}
std::pair<FilterIterator, FilterIterator> AppController::Images()
{
return m_resources.Images();
}
std::pair<FilterIterator, FilterIterator> AppController::Sounds()
{
return m_resources.Sounds();
}

View file

@ -0,0 +1,149 @@
// app_controller.h
#ifndef APP_CONTROLLER_H
#define APP_CONTROLLER_H
#include <string>
#include <vector>
#include <memory> // Pour std::shared_ptr
#include <functional> // Pour std::function
#include <set> // Pour std::set
// Inclure les entêtes des dépendances de AppController
#include "story_project.h" // Pour StoryProject
#include "chip32_assembler.h" // Pour Chip32::Assembler
#include "chip32_vm.h" // Pour chip32_ctx_t, Chip32::Result, chip32_result_t
#include "i_story_manager.h" // Interface implémentée par AppController
#include "i_audio_event.h" // Interface implémentée par AppController
#include "i_logger.h" // Interface implémentée par AppController (pour les logs métier)
#include "thread_safe_queue.h" // Pour ThreadSafeQueue
#include "audio_player.h" // Pour AudioPlayer
#include "library_manager.h" // Pour LibraryManager
#include "nodes_factory.h" // Pour NodesFactory
#include "resource_manager.h" // Pour ResourceManager (si elle est utilisée directement par AppController)
#include "web_server.h" // Pour WebServer
#include "story_machine.h" // Peut-être renommé en VmEvent ou Processus de VM
#include "variable.h" // Pour Variable (si géré par AppController)
#include "debug_context.h" // Pour DebugContext
#include "event_bus.h"
// Forward declaration pour éviter les dépendances circulaires si le logger est une GUI
class ILogger; // Peut être implémenté par la console_window par exemple
class AppController : public IStoryManager, public IAudioEvent
{
public:
enum VmEventType { EvNoEvent, EvStep, EvRun, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished, EvStop, EvHomeButton};
struct VmEvent
{
VmEventType type;
};
// Le constructeur prend une référence vers une implémentation de ILogger
// pour que la logique métier puisse logger sans dépendre de la GUI.
AppController(ILogger& logger, EventBus& eventBus);
~AppController();
// Initialisation de l'AppController
bool Initialize();
void OpenProject(const std::string &uuid);
void ImportProject(const std::string &fileName, int format);
void Log(const std::string &txt, bool critical = false) override;
void PlaySoundFile(const std::string &fileName);
std::string BuildFullAssetsPath(const std::string_view fileName) const;
void OpenFunction(const std::string &uuid, const std::string &name);
void NewStory();
void CloseProject();
void NewModule();
void SaveModule();
void CloseModule();
void OpenModule(const std::string &uuid);
void OpenStory(const std::string &path = "");
void SaveStory(const std::string &path = "");
void ExportStory(const std::string &filename);
std::shared_ptr<StoryProject> GetCurrentStory() const { return m_story; }
std::shared_ptr<StoryProject> GetModuleStory() const { return m_module; }
void BuildNodes(IStoryProject::Type type);
void Build(bool compileonly);
void BuildCode(std::shared_ptr<StoryProject> story, bool compileonly, bool force = false);
// --- Fonctions de IStoryManager ---
virtual void SetExternalSourceFile(const std::string &filename) override;
virtual void LoadBinaryStory(const std::string &filename) override;
virtual void ToggleBreakpoint(int line) override;
virtual uint32_t GetRegister(int reg) override;
virtual void ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation) override;
virtual void AddVariable() override;
virtual void DeleteVariable(int i) override;
virtual void Play() override;
virtual void Step() override;
virtual void Run() override;
virtual void Ok() override;
virtual void Stop() override;
virtual void Pause() override;
virtual void Home() override;
virtual void Next() override;
virtual void Previous() override;
virtual std::string VmState() const override;
// --- Fonctions de IAudioEvent ---
virtual void EndOfAudio() override;
// --- Fonctions internes de AppController (logique métier) ---
void SaveParams();
void LoadParams();
// Méthodes pour interagir avec la VM et le débogueur
chip32_ctx_t* GetChip32Context() { return &m_chip32_ctx; }
DebugContext* GetDebugContext() { return &m_dbg; }
std::string GetStringFromMemory(uint32_t addr);
void ProcessStory();
void StepInstruction();
// Getters pour les managers gérés par AppController
ResourceManager& GetResourceManager() { return m_resources; }
LibraryManager& GetLibraryManager() { return m_libraryManager; }
NodesFactory& GetNodesFactory() { return m_nodesFactory; }
AudioPlayer& GetAudioPlayer() { return m_player; }
WebServer& GetWebServer() { return m_webServer; }
const std::vector<std::string>& GetRecentProjects() const { return m_recentProjects; }
void AddRecentProject(const std::string& projectPath);
// Fonction pour les syscalls (appel système) de la VM
uint8_t Syscall(chip32_ctx_t *ctx, uint8_t code);
// Méthode pour mettre à jour l'état de la vue VM (peut être un événement notifié à la GUI)
void UpdateVmView();
virtual std::pair<FilterIterator, FilterIterator> Images() override;
virtual std::pair<FilterIterator, FilterIterator> Sounds() override;
private:
ILogger& m_logger; // Référence au logger pour les messages métier
EventBus& m_eventBus; // Bus d'événements pour la communication entre composants
std::shared_ptr<StoryProject> m_story;
std::shared_ptr<StoryProject> m_module;
uint8_t m_rom_data[16*1024];
uint8_t m_ram_data[16*1024];
chip32_ctx_t m_chip32_ctx;
Chip32::Result m_result;
DebugContext m_dbg; // Contexte de débogage
std::string m_currentCode;
std::string m_externalSourceFileName;
std::vector<std::string> m_recentProjects;
NodesFactory m_nodesFactory;
ResourceManager m_resources; // Gère les ressources (images, sons)
LibraryManager m_libraryManager; // Gère les bibliothèques
AudioPlayer m_player; // Gère la lecture audio
ThreadSafeQueue<VmEvent> m_eventQueue; // File d'événements de la VM
WebServer m_webServer; // Serveur web intégré
// Fonctions privées utilitaires pour la logique métier
void SetupVM(int start_line, uint32_t entry_point);
};
#endif // APP_CONTROLLER_H

View file

@ -0,0 +1,51 @@
#pragma once
#include <set>
#include <stdint.h>
#include "chip32_assembler.h"
#include "chip32_vm.h"
struct DebugContext
{
uint32_t event_mask{0};
bool wait_event{0};
bool free_run{false};
uint32_t line{0};
chip32_result_t run_result{VM_FINISHED};
std::set<int> m_breakpoints;
void Stop() {
run_result = VM_FINISHED;
}
bool IsValidEvent(uint32_t event) {
return (event_mask & event) != 0;
}
static void DumpCodeAssembler(Chip32::Assembler & assembler) {
for (std::vector<Chip32::Instr>::const_iterator iter = assembler.Begin();
iter != assembler.End(); ++iter)
{
if (iter->isRomCode() || iter->isRomData)
{
std::cout << "-------------------" << std::endl;
std::cout << "Instr: " << iter->mnemonic.c_str() << std::endl;
std::cout << "Addr: " << std::hex << iter->addr << std::endl;
std::cout << "Line: " << iter->line << std::endl;
std::cout << "\t- Opcode: " << std::hex << iter->code.opcode
<< ", opcode args: " << iter->code.bytes << std::endl;
int i = 1;
for (auto arg : iter->compiledArgs)
{
std::cout << "\t- Arg " << i << " : " << std::hex << arg << std::endl;
i++;
}
}
}
}
};

View file

@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#include "i_logger.h"
class Logger : public ILogger
{
public:
void Log(const std::string& message, bool critical = false) override
{
for (const auto& subject : m_subjects)
{
if (subject)
{
subject->LogEvent(message, critical);
}
}
}
void RegisterSubject(std::shared_ptr<ILogSubject> subject) override
{
m_subjects.push_back(subject);
}
private:
// List of log subjects
std::vector<std::shared_ptr<ILogSubject>> m_subjects;
};

View file

@ -0,0 +1,56 @@
// status_bar.h
#ifndef STATUS_BAR_H
#define STATUS_BAR_H
#include "imgui.h" // Nécessaire pour ImGui::Begin, ImGui::Text, etc.
#include <string> // Pour std::string
#include "app_controller.h" // Supposons que AppController expose les infos nécessaires
class StatusBar
{
public:
// Le constructeur prend une référence au contrôleur d'application.
// Cela permet à la barre de statut d'accéder aux informations qu'elle doit afficher.
StatusBar(AppController& appController)
: m_appController(appController) {}
// Dessine la barre de statut.
// Doit être appelée à chaque frame dans la boucle de rendu ImGui.
void Draw()
{
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + viewport->Size.y - ImGui::GetFrameHeight()), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, ImGui::GetFrameHeight()), ImGuiCond_Always);
// Pas de barre de titre, pas de redimensionnement, pas de mouvement, etc.
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.f, 4.f)); // Petit padding
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); // Pas d'arrondi
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); // Pas de bordure
if (ImGui::Begin("StatusBar", nullptr, windowFlags))
{
// Affiche les FPS
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
// Affiche l'état de la VM (supposons que AppController a une méthode pour cela)
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
ImGui::Text("VM State: %s", m_appController.VmState().c_str());
// Vous pouvez ajouter d'autres informations ici, par exemple :
// - Nom du projet actuel
// - État du serveur web
// - Messages de log courts (dernier message critique, etc.)
}
ImGui::End();
ImGui::PopStyleVar(3); // Pop les 3 styles poussés
}
private:
AppController& m_appController; // Référence au contrôleur d'application
};
#endif // STATUS_BAR_H

View file

@ -0,0 +1,101 @@
// toolbar.h
#ifndef TOOL_BAR_H
#define TOOL_BAR_H
#include "imgui.h" // Nécessaire pour ImGui::Begin, ImGui::Button, etc.
#include "app_controller.h" // Supposons que AppController gère les actions
#include "IconsMaterialDesignIcons.h" // Pour les icônes (si vous utilisez le même jeu d'icônes)
class ToolBar
{
public:
// Le constructeur prend une référence au contrôleur d'application.
// Cela permet à la barre d'outils de déclencher des actions métier.
ToolBar(AppController& appController)
: m_appController(appController) {}
// Dessine la barre d'outils.
// 'topPadding' est utile si vous avez une barre de menu horizontale au-dessus.
void Draw(float topPadding)
{
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + topPadding), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(ImGui::GetFrameHeightWithSpacing() * 1.5f, viewport->Size.y - topPadding - ImGui::GetFrameHeight()), ImGuiCond_Always);
// Pas de barre de titre, pas de redimensionnement, pas de mouvement, etc.
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(5.f, 5.f)); // Petit padding
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); // Pas d'arrondi
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); // Pas de bordure
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, 10.f)); // Espacement vertical entre les boutons
if (ImGui::Begin("ToolBar", nullptr, windowFlags))
{
float buttonSize = ImGui::GetContentRegionAvail().x; // Remplir la largeur disponible
// Boutons d'action : Ouvrir, Sauvegarder, Compiler, etc.
// Utilisez des icônes pour une meilleure UX.
// Assurez-vous que les icônes sont chargées dans la police ImGui.
ImGui::PushID("ToolBarButtons"); // Empêche les ID de collision si plusieurs boutons ont le même texte
// Bouton Nouveau
if (ImGui::Button(ICON_MDI_FILE_OUTLINE " New", ImVec2(buttonSize, buttonSize))) {
m_appController.NewStory(); // Supposons que AppController ait une méthode NewStory()
}
ImGui::Tooltip("New Story");
// Bouton Ouvrir
if (ImGui::Button(ICON_MDI_FOLDER_OPEN_OUTLINE " Open", ImVec2(buttonSize, buttonSize))) {
// Ici, vous pourriez déclencher une boîte de dialogue d'ouverture de fichier via AppController
// Ou, si la boîte de dialogue est une fenêtre ImGui séparée, MainWindow la gèrerait.
// Pour simplifier, nous supposons AppController gère le dialogue ou déclenche un événement.
m_appController.OpenStory();
}
ImGui::Tooltip("Open Story");
// Bouton Sauvegarder
if (ImGui::Button(ICON_MDI_CONTENT_SAVE_OUTLINE " Save", ImVec2(buttonSize, buttonSize))) {
m_appController.SaveStory();
}
ImGui::Tooltip("Save Story");
ImGui::Separator(); // Séparateur visuel
// Bouton Compiler
if (ImGui::Button(ICON_MDI_CODE_BRACES " Compile", ImVec2(buttonSize, buttonSize))) {
m_appController.Build(true); // Appelle la méthode de compilation dans AppController
}
ImGui::Tooltip("Compile Code");
// Bouton Exécuter/Build
if (ImGui::Button(ICON_MDI_PLAY_OUTLINE " Run", ImVec2(buttonSize, buttonSize))) {
m_appController.Build(false); // Appelle la méthode de build et exécution
}
ImGui::Tooltip("Build and Run");
// Bouton Stop VM
if (ImGui::Button(ICON_MDI_STOP " Stop", ImVec2(buttonSize, buttonSize))) {
m_appController.Stop(); // Arrête la VM
}
ImGui::Tooltip("Stop VM");
// Bouton Pas à pas (Debugger)
if (ImGui::Button(ICON_MDI_STEP_FORWARD " Step", ImVec2(buttonSize, buttonSize))) {
m_appController.Step(); // Exécute une instruction
}
ImGui::Tooltip("Step VM Instruction");
ImGui::PopID(); // Pop l'ID pushé
}
ImGui::End();
ImGui::PopStyleVar(4); // Pop les 4 styles poussés
}
private:
AppController& m_appController; // Référence au contrôleur d'application
};
#endif // TOOL_BAR_H

View file

@ -0,0 +1,45 @@
// events/vm_state_event.h
#ifndef ALL_EVENTS_H
#define ALL_EVENTS_H
#include "event.h"
#include <string>
#include <set>
#include <chip32_vm.h> // Pour chip32_ctx_t
struct VmStateEvent : public Event
{
chip32_ctx_t vmContext;
int currentLine;
chip32_result_t vmResult;
std::set<int> breakpoints;
// Ajoutez d'autres données nécessaires, ex: std::vector<std::shared_ptr<Variable>> variables;
// Constructeur pour faciliter la création de l'événement
VmStateEvent(const chip32_ctx_t& ctx, int line, chip32_result_t result, const std::set<int>& bps)
: vmContext(ctx), currentLine(line), vmResult(result), breakpoints(bps) {}
};
class OpenProjectEvent : public Event
{
public:
OpenProjectEvent(const std::string &uuid) : m_uuid(uuid) {}
const std::string& GetUuid() const { return m_uuid; }
private:
std::string m_uuid; // UUID du projet à ouvrir
};
class OpenFunctionEvent : public Event
{
public:
OpenFunctionEvent(const std::string &uuid, const std::string &name)
: m_uuid(uuid), m_name(name) {}
const std::string& GetUuid() const { return m_uuid; }
const std::string& GetName() const { return m_name; }
private:
std::string m_uuid; // UUID du projet ou module
std::string m_name; // Nom de la fonction à ouvrir
};
#endif // ALL_EVENTS_H

View file

@ -0,0 +1,22 @@
// events/event.h
#ifndef EVENT_H
#define EVENT_H
#include <typeindex> // Pour std::type_index
// Chaque événement aura un TypeId unique.
// Ceci permet de stocker et de récupérer des pointeurs vers des handlers spécifiques.
struct Event
{
virtual ~Event() = default;
};
// Fonction utilitaire pour obtenir le TypeId unique d'une classe d'événement.
// Utilise std::type_index pour l'identification au runtime.
template <typename T>
std::type_index GetEventTypeId()
{
return std::type_index(typeid(T));
}
#endif // EVENT_H

View file

@ -0,0 +1,32 @@
// event_bus.cpp
#include "event_bus.h"
#include <iostream> // Pour des logs simples de débogage si nécessaire
void EventBus::Emit(std::shared_ptr<Event> event)
{
if (!event) {
// Gérer le cas où l'événement est nul
std::cerr << "EventBus: Attempted to emit a null event." << std::endl;
return;
}
std::type_index eventTypeId = GetEventTypeId<Event>(); // Default type for base Event
// Tente de trouver le type exact de l'événement pour la distribution
// C'est un peu tricky car dynamic_cast ne peut pas être utilisé avec typeid(Event).
// On doit utiliser typeid(*event) pour obtenir le type réel de l'objet pointé.
eventTypeId = std::type_index(typeid(*event));
auto it = m_subscribers.find(eventTypeId);
if (it != m_subscribers.end())
{
// Parcourt tous les abonnés pour ce type d'événement
for (const auto& callback : it->second)
{
callback(*event); // Appelle la callback en passant l'événement de base
}
} else {
// Optionnel: loguer si aucun abonné pour ce type d'événement
// std::cout << "EventBus: No subscribers for event type: " << eventTypeId.name() << std::endl;
}
}

View file

@ -0,0 +1,53 @@
// event_bus.h
#ifndef EVENT_BUS_H
#define EVENT_BUS_H
#include "events/event.h" // Inclure la classe de base Event
#include <functional> // Pour std::function
#include <map> // Pour std::map (TypeId -> vector of handlers)
#include <vector> // Pour std::vector (list of handlers)
#include <memory> // Pour std::shared_ptr si on veut gérer la durée de vie des événements
// Définition du type de handler générique
// Un handler est une fonction qui prend une référence constante à un Event.
using EventCallback = std::function<void(const Event&)>;
class EventBus
{
public:
EventBus() = default;
~EventBus() = default;
// Supprime la copie pour éviter des comportements inattendus avec les callbacks
EventBus(const EventBus&) = delete;
EventBus& operator=(const EventBus&) = delete;
// S'abonne à un type d'événement spécifique.
// 'TEvent' est le type d'événement à écouter (ex: VmStateChangedEvent).
// 'callback' est la fonction à appeler lorsque cet événement est émis.
template <typename TEvent>
void Subscribe(std::function<void(const TEvent&)> callback)
{
// On stocke une lambda qui convertit l'Event de base en TEvent
// pour que la callback spécifique à TEvent puisse être appelée.
std::type_index typeId = GetEventTypeId<TEvent>();
m_subscribers[typeId].push_back([callback](const Event& event) {
// S'assurer que le cast est sûr
if (const TEvent* specificEvent = dynamic_cast<const TEvent*>(&event)) {
callback(*specificEvent);
}
});
}
// Émet un événement.
// L'EventBus prend la possession de l'événement (via std::shared_ptr).
// Ceci est crucial car l'événement doit exister pendant que les callbacks sont appelées.
void Emit(std::shared_ptr<Event> event);
private:
// Mappe les TypeId des événements à une liste de callbacks.
std::map<std::type_index, std::vector<EventCallback>> m_subscribers;
};
#endif // EVENT_BUS_H

View file

@ -1,14 +1,23 @@
#include "main_window.h"
#include "app_controller.h"
#include "event_bus.h"
#include "logger.h"
// Main code
int main(int, char**)
{
Logger logger;
EventBus eventBus;
AppController appController(logger, eventBus);
MainWindow w(logger, eventBus, appController);
MainWindow w;
if (w.Initialize())
{
w.Loop();
appController.ProcessStory();
}
return 0;

View file

@ -22,34 +22,24 @@
#include "ImGuiFileDialog.h"
#include "imgui_memory_editor.h"
MainWindow::MainWindow()
: m_resources(*this)
, m_nodesFactory(*this)
, m_libraryManager(*this, m_nodesFactory)
, m_emulatorWindow(*this)
, m_debuggerWindow(*this)
, m_cpuWindow(*this)
, m_resourcesWindow(*this)
, m_nodeEditorWindow(*this, m_nodesFactory)
, m_moduleEditorWindow(*this, m_nodesFactory, IStoryProject::Type::PROJECT_TYPE_MODULE)
, m_libraryWindow(*this, m_libraryManager, m_nodesFactory)
, m_variablesWindow(*this)
, m_player(*this)
, m_webServer(m_libraryManager)
#include "app_controller.h"
#include "all_events.h"
MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController)
: m_logger(logger)
, m_eventBus(eventBus)
, m_appController(appController)
, m_emulatorWindow(appController)
, m_debuggerWindow(appController)
, m_cpuWindow(appController)
, m_resourcesWindow(appController.GetResourceManager())
, m_nodeEditorWindow(appController)
, m_moduleEditorWindow(appController)
, m_libraryWindow(appController)
, m_variablesWindow(appController)
{
// VM Initialize
m_chip32_ctx.stack_size = 512;
m_chip32_ctx.rom.mem = m_rom_data;
m_chip32_ctx.rom.addr = 0;
m_chip32_ctx.rom.size = sizeof(m_rom_data);
m_chip32_ctx.ram.mem = m_ram_data;
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
m_chip32_ctx.ram.size = sizeof(m_ram_data);
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);
logger.RegisterSubject(shared_from_this());
CloseProject();
CloseModule();
@ -59,11 +49,19 @@ MainWindow::MainWindow()
// define style for all files
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile, "", ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ICON_MDI_FILE);
m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) {
OpenProject(event.GetUuid());
});
m_eventBus.Subscribe<OpenFunctionEvent>([this](const OpenFunctionEvent &event) {
OpenFunction(event.GetUuid(), event.GetName());
});
}
MainWindow::~MainWindow()
{
SaveParams();
m_appController.SaveParams();
}
@ -81,240 +79,16 @@ std::string MainWindow::GetStringFromMemory(uint32_t addr)
}
void MainWindow::Play()
{
if ((m_dbg.run_result == VM_READY) || (m_dbg.run_result == VM_FINISHED))
{
m_dbg.free_run = true;
m_dbg.run_result = VM_OK; // actually starts the execution
}
}
void MainWindow::Step()
{
m_eventQueue.push({VmEventType::EvStep});
}
void MainWindow::Run()
{
m_eventQueue.push({VmEventType::EvRun});
}
void MainWindow::Ok()
{
m_eventQueue.push({VmEventType::EvOkButton});
}
void MainWindow::Stop()
{
m_dbg.run_result = VM_FINISHED; // better than sending an event: avoid infinite loops in assembly
}
void MainWindow::Pause()
{
}
void MainWindow::Home()
{
m_eventQueue.push({VmEventType::EvHomeButton});
}
void MainWindow::Next()
{
Log("Next button");
m_eventQueue.push({VmEventType::EvNextButton});
}
void MainWindow::Previous()
{
Log("Previous button");
m_eventQueue.push({VmEventType::EvPreviousButton});
}
std::string MainWindow::VmState() const
{
std::string state = "Unknown";
switch (m_dbg.run_result)
{
case VM_READY:
state = "VM Ready";
break;
case VM_FINISHED:
state = "VM Finished";
break;
case VM_SKIPED:
state = "VM Skiped";
break;
case VM_WAIT_EVENT:
state = "VM Wait Event";
break;
case VM_OK:
state = "VM Ok";
break;
default:
state = "VM Error";
break;
}
return state;
}
void MainWindow::EndOfAudio()
{
Log("End of audio track");
m_eventQueue.push({VmEventType::EvAudioFinished});
}
void MainWindow::StepInstruction()
{
m_dbg.run_result = chip32_step(&m_chip32_ctx);
UpdateVmView();
}
void MainWindow::ProcessStory()
{
if (m_dbg.run_result == VM_FINISHED)
return;
// Error states
if (m_dbg.run_result > VM_OK)
return;
// Check events
VmEvent event;
if (m_eventQueue.try_pop(event))
{
if (event.type == VmEventType::EvStep)
{
StepInstruction();
m_dbg.run_result = VM_OK; // FIXME: bizarre d'écraser le code de retour...
}
else if (event.type == VmEventType::EvRun)
{
m_dbg.free_run = true;
m_dbg.run_result = VM_OK;
}
else if (event.type == VmEventType::EvStop)
{
m_dbg.run_result = VM_FINISHED;
}
// Events managed only if the code is in wait event state
if (m_dbg.run_result == VM_WAIT_EVENT)
{
if (event.type == VmEventType::EvOkButton)
{
if (m_dbg.IsValidEvent(EV_MASK_OK_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_OK_BUTTON;
m_dbg.run_result = VM_OK;
}
}
else if (event.type == VmEventType::EvPreviousButton)
{
if (m_dbg.IsValidEvent(EV_MASK_PREVIOUS_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_PREVIOUS_BUTTON;
m_dbg.run_result = VM_OK;
}
}
else if (event.type == VmEventType::EvNextButton)
{
if (m_dbg.IsValidEvent(EV_MASK_NEXT_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_NEXT_BUTTON;
m_dbg.run_result = VM_OK;
}
}
else if (event.type == VmEventType::EvAudioFinished)
{
if (m_dbg.IsValidEvent(EV_MASK_END_OF_AUDIO))
{
m_chip32_ctx.registers[R0] = EV_MASK_END_OF_AUDIO;
m_dbg.run_result = VM_OK;
}
}
else if (event.type == VmEventType::EvHomeButton)
{
if (m_dbg.IsValidEvent(EV_MASK_HOME_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_HOME_BUTTON;
m_dbg.run_result = VM_OK;
}
}
}
}
if (m_dbg.run_result == VM_OK)
{
if (m_dbg.m_breakpoints.contains(m_dbg.line))
{
// Log("Breakpoint on line: " + std::to_string(m_dbg.line + 1));
m_dbg.run_result = VM_WAIT_EVENT;
m_dbg.free_run = false;
}
if (m_dbg.free_run)
{
StepInstruction();
}
}
if (m_dbg.run_result == VM_FINISHED)
{
m_dbg.free_run = false;
}
else if (m_dbg.run_result > VM_OK)
{
std::string error = "VM Error: ";
switch (m_dbg.run_result)
{
case VM_ERR_STACK_OVERFLOW:
error += "Stack overflow";
break;
case VM_ERR_STACK_UNDERFLOW:
error += "Stack underflow";
break;
case VM_ERR_INVALID_ADDRESS:
error += "Invalid address";
break;
case VM_ERR_UNSUPPORTED_OPCODE:
error += "Invalid address";
break;
case VM_ERR_UNKNOWN_OPCODE:
error += "Unknown opcode";
break;
case VM_ERR_UNHANDLED_INTERRUPT:
error += "Unhandled interrupt";
break;
case VM_ERR_INVALID_REGISTER:
error += "Invalid register";
break;
default:
error += "Unknown error";
break;
}
error += " (line: " + std::to_string(m_dbg.line) + ")";
Log(error, true);
}
// In this case, we wait for single step debugger
if ((m_dbg.run_result == VM_OK) && !m_dbg.free_run)
{
m_dbg.run_result = VM_WAIT_EVENT;
}
}
uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
{
uint8_t retCode = SYSCALL_RET_OK;
Log("SYSCALL: " + std::to_string(code));
m_logger.Log("SYSCALL: " + std::to_string(code));
// Media
if (code == 1) // Execute media
@ -323,7 +97,7 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
{
// image file name address is in R0
std::string imageFile = m_story->BuildFullAssetsPath(GetStringFromMemory(m_chip32_ctx.registers[R0]));
Log("Image: " + imageFile);
m_logger.Log("Image: " + imageFile);
m_emulatorWindow.SetImage(imageFile);
}
else
@ -335,7 +109,7 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
{
// sound file name address is in R1
std::string soundFile = m_story->BuildFullAssetsPath(GetStringFromMemory(m_chip32_ctx.registers[R1]));
Log("Sound: " + soundFile);
m_logger.Log("Sound: " + soundFile);
m_player.Play(soundFile);
}
retCode = SYSCALL_RET_OK; // We continue execution, script must wait for event if necessary (end of audio)
@ -379,19 +153,19 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
switch(arg_count){
case 0:
Log(text);
m_logger.Log(text);
break;
case 1:
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]);
Log(working_buf);
m_logger.Log(working_buf);
break;
case 2:
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3]);
Log(working_buf);
m_logger.Log(working_buf);
break;
case 3:
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]);
Log(working_buf);
m_logger.Log(working_buf);
break;
default:
break;
@ -408,31 +182,6 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
return retCode;
}
void MainWindow::DrawStatusBar()
{
float statusWindowHeight = ImGui::GetFrameHeight() * 1.4f;
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + viewport->Size.y - statusWindowHeight));
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, statusWindowHeight));
ImGui::SetNextWindowViewport(viewport->ID);
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking;
ImGui::Begin("StatusBar", nullptr, windowFlags);
if (true)
{
float dy = ImGui::GetFontSize() * 0.15f;
ImGui::SameLine(ImGui::GetIO().DisplaySize.x - 14.f * ImGui::GetFontSize());
ImGui::SameLine();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - dy);
ImGui::Text("FPS: %.1f", 1000.0f / ImGui::GetIO().Framerate);
}
ImGui::End();
}
float MainWindow::DrawMainMenuBar()
{
bool showParameters = false;
@ -829,56 +578,35 @@ void MainWindow::SaveProject()
{
nlohmann::json model;
m_story->Save(m_resources);
Log("Project saved");
m_logger.Log("Project saved");
}
void MainWindow::OpenProject(const std::string &uuid)
{
CloseProject();
m_story = m_libraryManager.GetStory(uuid);
// DEBUG CODE !!!!!!!!!!!!! Permet si décommenter de forcer l'import, permet de tester plus facilement l'algo en ouvrant le projet
// PackArchive arch(*this);
// std::string basePath = m_libraryManager.LibraryPath() + "/" + uuid;
// arch.ConvertJsonStudioToOst(basePath, uuid, m_libraryManager.LibraryPath());
if (!m_story)
m_nodeEditorWindow.Load(m_story);
auto proj = m_story->GetProjectFilePath();
// Add to recent if not exists
if (std::find(m_recentProjects.begin(), m_recentProjects.end(), proj) == m_recentProjects.end())
{
Log("Cannot find story: " + uuid);
}
else if (m_story->Load(m_resources, m_nodesFactory))
{
Log("Open project success");
m_nodeEditorWindow.Load(m_story);
auto proj = m_story->GetProjectFilePath();
// Add to recent if not exists
if (std::find(m_recentProjects.begin(), m_recentProjects.end(), proj) == m_recentProjects.end())
{
m_recentProjects.push_back(proj);
// Limit to 10 recent projects
if (m_recentProjects.size() > 10) {
m_recentProjects.pop_back();
}
// Save recent projects on disk
SaveParams();
m_recentProjects.push_back(proj);
// Limit to 10 recent projects
if (m_recentProjects.size() > 10) {
m_recentProjects.pop_back();
}
m_nodeEditorWindow.Enable();
// Save recent projects on disk
m_appController.SaveParams();
}
m_emulatorWindow.Enable();
m_consoleWindow.Enable();
m_debuggerWindow.Enable();
m_resourcesWindow.Enable();
m_PropertiesWindow.Enable();
m_variablesWindow.Enable();
m_cpuWindow.Enable();
}
else
{
Log("Open project error");
}
m_nodeEditorWindow.Enable();
m_emulatorWindow.Enable();
m_consoleWindow.Enable();
m_debuggerWindow.Enable();
m_resourcesWindow.Enable();
m_PropertiesWindow.Enable();
m_variablesWindow.Enable();
m_cpuWindow.Enable();
RefreshProjectInformation();
}
@ -895,7 +623,7 @@ void MainWindow::NewModule()
void MainWindow::SaveModule()
{
m_nodesFactory.SaveAllModules(m_resources);;
Log("Modules saved");
m_logger.Log("Modules saved");
}
void MainWindow::OpenModule(const std::string &uuid)
@ -903,17 +631,17 @@ void MainWindow::OpenModule(const std::string &uuid)
m_module = m_nodesFactory.GetModule(uuid);
if (!m_module)
{
Log("Cannot find module: " + uuid);
m_logger.Log("Cannot find module: " + uuid);
}
else if (m_module->Load(m_resources, m_nodesFactory))
{
Log("Open module success");
m_logger.Log("Open module success");
m_moduleEditorWindow.Load(m_module);
m_moduleEditorWindow.Enable();
}
else
{
Log("Open module error");
m_logger.Log("Open module error");
}
}
@ -975,7 +703,7 @@ void MainWindow::ImportProject(const std::string &filePathName, int format)
}
else
{
Log("Unknown file format: " + filePathName);
m_logger.Log("Unknown file format: " + filePathName);
}
}
@ -1051,8 +779,6 @@ void MainWindow::Loop()
ImGui::DockSpaceOverViewport(0, vp);
float height = DrawMainMenuBar();
// DrawStatusBar();
ProcessStory();
// ------------ Draw all windows
@ -1111,56 +837,24 @@ void MainWindow::Loop()
}
void MainWindow::Log(const std::string &txt, bool critical)
void MainWindow::LogEvent(const std::string &txt, bool critical)
{
m_consoleWindow.AddLog(txt, critical ? 1 : 0);
}
void MainWindow::PlaySoundFile(const std::string &fileName)
{
Log("Play sound file: " + fileName);
m_player.Play(fileName);
}
std::string MainWindow::BuildFullAssetsPath(const std::string_view fileName) const
{
return m_story->BuildFullAssetsPath(fileName);
}
std::pair<FilterIterator, FilterIterator> MainWindow::Images()
{
return m_resources.Images();
}
std::pair<FilterIterator, FilterIterator> MainWindow::Sounds()
{
return m_resources.Sounds();
}
void MainWindow::OpenFunction(const std::string &uuid, const std::string &name)
{
m_nodeEditorWindow.OpenFunction(uuid, name);
}
void MainWindow::AddResource(std::shared_ptr<Resource> res)
{
m_resources.Add(res);
}
void MainWindow::ClearResources()
{
m_resources.Clear();
}
std::pair<FilterIterator, FilterIterator> MainWindow::Resources()
{
return m_resources.Items();
}
void MainWindow::DeleteResource(FilterIterator &it)
{
return m_resources.Delete(it);
}
void MainWindow::LoadBinaryStory(const std::string &filename)
{
@ -1178,11 +872,11 @@ void MainWindow::LoadBinaryStory(const std::string &filename)
{
m_dbg.run_result = VM_READY;
chip32_initialize(&m_chip32_ctx);
Log("Loaded binary file: " + filename);
m_logger.Log("Loaded binary file: " + filename);
}
else
{
Log("Failed to load binary file", true);
m_logger.Log("Failed to load binary file", true);
}
}
@ -1190,47 +884,7 @@ void MainWindow::LoadBinaryStory(const std::string &filename)
}
}
void MainWindow::ToggleBreakpoint(int line)
{
if (m_dbg.m_breakpoints.contains(line))
{
m_dbg.m_breakpoints.erase(line);
}
else
{
m_dbg.m_breakpoints.insert(line);
}
}
uint32_t MainWindow::GetRegister(int reg)
{
uint32_t regVal = 0;
if (reg >= 0 && reg < REGISTER_COUNT)
{
regVal = m_chip32_ctx.registers[reg];
}
return regVal;
}
void MainWindow::ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation)
{
if (m_story)
{
m_story->ScanVariable(operation);
}
}
void MainWindow::AddVariable()
{
m_story->AddVariable();
}
void MainWindow::DeleteVariable(int i)
{
m_story->DeleteVariable(i);
}
void MainWindow::BuildNodes(bool compileonly)
{
@ -1271,12 +925,12 @@ void MainWindow::Build(bool compileonly)
}
else
{
Log("Program too big. Expand ROM memory.");
m_logger.Log("Program too big. Expand ROM memory.");
}
}
else
{
Log(err.ToString(), true);
m_logger.Log(err.ToString(), true);
m_debuggerWindow.AddError(err.line, err.message); // show also the error in the code editor
}
}
@ -1311,29 +965,13 @@ void MainWindow::UpdateVmView()
else
{
// Not found
Log("Reached end or instruction not found line: " + std::to_string(m_dbg.line));
m_logger.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::SaveParams()
{
nlohmann::json j;
nlohmann::json recents(m_recentProjects);
j["recents"] = recents;
j["library_path"] = m_libraryManager.LibraryPath();
j["store_url"] = m_libraryManager.GetStoreUrl();
std::string loc = pf::getConfigHome() + "/ost_settings.json";
std::ofstream o(loc);
o << std::setw(4) << j << std::endl;
Log("Saved settings to: " + loc);
}
void MainWindow::LoadParams()
{
try {
@ -1371,6 +1009,6 @@ void MainWindow::LoadParams()
}
catch(std::exception &e)
{
Log(e.what(), true);
m_logger.Log(e.what(), true);
}
}

View file

@ -3,82 +3,31 @@
#include <functional>
#include <memory>
#include <string>
#include "gui.h"
#include "console_window.h"
#include "debugger_window.h"
#include "emulator_dock.h"
#include "resources_window.h"
#include "node_editor_window.h"
#include "properties_window.h"
#include "variables_window.h"
#include "chip32_assembler.h"
#include "chip32_vm.h"
#include "story_project.h"
#include "i_story_manager.h"
#include "thread_safe_queue.h"
#include "audio_player.h"
#include "library_manager.h"
#include "library_window.h"
#include "cpu_window.h"
#include "story_machine.h"
#include "web_server.h"
#include "nodes_factory.h"
// Dialogs
#include "about_dialog.h"
struct DebugContext
{
uint32_t event_mask{0};
bool wait_event{0};
bool free_run{false};
uint32_t line{0};
chip32_result_t run_result{VM_FINISHED};
std::set<int> m_breakpoints;
void Stop() {
run_result = VM_FINISHED;
}
bool IsValidEvent(uint32_t event) {
return (event_mask & event) != 0;
}
static void DumpCodeAssembler(Chip32::Assembler & assembler) {
for (std::vector<Chip32::Instr>::const_iterator iter = assembler.Begin();
iter != assembler.End(); ++iter)
{
if (iter->isRomCode() || iter->isRomData)
{
std::cout << "-------------------" << std::endl;
std::cout << "Instr: " << iter->mnemonic.c_str() << std::endl;
std::cout << "Addr: " << std::hex << iter->addr << std::endl;
std::cout << "Line: " << iter->line << std::endl;
std::cout << "\t- Opcode: " << std::hex << iter->code.opcode
<< ", opcode args: " << iter->code.bytes << std::endl;
int i = 1;
for (auto arg : iter->compiledArgs)
{
std::cout << "\t- Arg " << i << " : " << std::hex << arg << std::endl;
i++;
}
}
}
}
};
#include "event_bus.h"
#include "app_controller.h"
#include "i_logger.h"
class MainWindow : public IStoryManager, public IAudioEvent, public ILogger
class MainWindow : public std::enable_shared_from_this<MainWindow>, public ILogSubject
{
public:
MainWindow();
MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController);
~MainWindow();
bool Initialize();
@ -87,27 +36,15 @@ public:
private:
enum VmEventType { EvNoEvent, EvStep, EvRun, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished, EvStop, EvHomeButton};
ILogger& m_logger;
EventBus& m_eventBus;
AppController &m_appController; // Controller for application logic
std::shared_ptr<StoryProject> m_story; // Current story
std::shared_ptr<StoryProject> m_module; // Current module
// VM
uint8_t m_rom_data[16*1024];
uint8_t m_ram_data[16*1024];
chip32_ctx_t m_chip32_ctx;
Chip32::Result m_result;
DebugContext m_dbg;
std::string m_currentCode;
std::string m_externalSourceFileName; // path of an external script to be used as compilation input
std::shared_ptr<StoryProject> m_module; // Current module
std::vector<std::string> m_recentProjects;
NodesFactory m_nodesFactory;
ResourceManager m_resources;
LibraryManager m_libraryManager;
Gui m_gui;
EmulatorDock m_emulatorWindow;
ConsoleWindow m_consoleWindow;
@ -117,33 +54,17 @@ private:
char m_project_name[256] = "";
ResourcesWindow m_resourcesWindow;
NodeEditorWindow m_nodeEditorWindow;
NodeEditorWindow m_moduleEditorWindow;
PropertiesWindow m_PropertiesWindow;
LibraryWindow m_libraryWindow;
VariablesWindow m_variablesWindow;
AudioPlayer m_player;
struct VmEvent
{
VmEventType type;
};
ThreadSafeQueue<VmEvent> m_eventQueue;
WebServer m_webServer;
// Dialogs
AboutDialog m_aboutDialog;
// From IStoryManager (proxy to StoryProject class)
virtual void OpenProject(const std::string &uuid) override;
void OpenProject(const std::string &uuid);
void SaveProject();
void CloseProject();
@ -152,59 +73,15 @@ private:
void SaveModule();
void CloseModule();
// From IStoryManager (proxy to StoryProject class)
virtual void ImportProject(const std::string &filePathName, int format);
virtual void PlaySoundFile(const std::string &fileName) override;;
virtual std::string BuildFullAssetsPath(const std::string_view fileName) const override;
virtual std::pair<FilterIterator, FilterIterator> Images() override;
virtual std::pair<FilterIterator, FilterIterator> Sounds() override;
virtual void OpenFunction(const std::string &uuid, const std::string &name) override;
// From ILogSubject
virtual void LogEvent(const std::string &txt, bool critical) override;
virtual void AddResource(std::shared_ptr<Resource> res) override;
virtual void ClearResources() override;
virtual std::pair<FilterIterator, FilterIterator> Resources() override;
virtual void DeleteResource(FilterIterator &it) override;
virtual void BuildNodes(bool compileonly) override;
virtual void BuildCode(bool compileonly) override;
virtual void SetExternalSourceFile(const std::string &filename) override;
virtual void LoadBinaryStory(const std::string &filename) override;
virtual void ToggleBreakpoint(int line) override;
virtual uint32_t GetRegister(int reg) override;
// Variable
virtual void ScanVariable(const std::function<void(std::shared_ptr<Variable> element)>& operation) override;
virtual void AddVariable() override;
virtual void DeleteVariable(int i);
virtual void Play() override;
virtual void Step() override;
virtual void Run() override;
virtual void Ok() override;
virtual void Stop() override;
virtual void Pause() override;
virtual void Home() override;
virtual void Next() override;
virtual void Previous() override;
virtual std::string VmState() const override;
// From ILogger
virtual void Log(const std::string &txt, bool critical = false) override;
// From IAudioEvent
virtual void EndOfAudio() override;
void SaveParams();
void LoadParams();
float DrawMainMenuBar();
bool ShowQuitConfirm();
void DrawToolBar(float topPadding);
void DrawStatusBar();
void UpdateVmView();
uint8_t Syscall(chip32_ctx_t *ctx, uint8_t code);
std::string GetStringFromMemory(uint32_t addr);

View file

@ -7,9 +7,9 @@
//static thread_pool pool;
ResourcesWindow::ResourcesWindow(IStoryManager &project)
ResourcesWindow::ResourcesWindow(ResourceManager &resources)
: WindowBase("Resources")
, m_story(project)
, m_resources(resources)
{
}
@ -29,7 +29,7 @@ void ResourcesWindow::ChooseFile()
m_showImportDialog = false;
// open Dialog Simple
IGFD::FileDialogConfig config;
config.path = m_story.BuildFullAssetsPath("");
config.path = m_resources.BuildFullAssetsPath("");
config.countSelectionMax = 1;
config.sidePaneWidth = 350.0f;
config.flags = ImGuiFileDialogFlags_Modal;
@ -49,7 +49,7 @@ void ResourcesWindow::ChooseFile()
std::filesystem::path p(filePathName);
std::filesystem::path p2 = m_story.BuildFullAssetsPath( p.filename().generic_string());
std::filesystem::path p2 = m_resources.BuildFullAssetsPath( p.filename().generic_string());
bool allowCopy = true;
// On ne copie pas le fichier sur lui-même
@ -72,7 +72,7 @@ void ResourcesWindow::ChooseFile()
res->format = ext;
res->type = m_soundFile ? "sound" : "image";
res->file = p.filename().generic_string();
m_story.AddResource(res);
m_resources.Add(res);
}
// close
@ -120,7 +120,7 @@ void ResourcesWindow::Draw()
ImGui::TableHeadersRow();
auto [b, e] = m_story.Resources();
auto [b, e] = m_resources.Items();
int id = 1000;
for (auto it = b; it != e; ++it)
@ -168,7 +168,7 @@ void ResourcesWindow::Draw()
ImGui::TableNextColumn();
if (ImGui::SmallButton("Delete"))
{
m_story.DeleteResource(it);
m_resources.DeleteResource(it);
quitLoop = true;
}
ImGui::PopID();

View file

@ -2,16 +2,17 @@
#include "i_story_manager.h"
#include "window_base.h"
#include "resource_manager.h"
class ResourcesWindow : public WindowBase
{
public:
ResourcesWindow(IStoryManager &project);
ResourcesWindow(ResourceManager &resources);
~ResourcesWindow();
virtual void Draw() override;
private:
IStoryManager &m_story;
ResourceManager &m_resources;
bool m_showImportDialog{false};
bool m_soundFile{false};