mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
Intermediatre commit: separation between logic and UI
This commit is contained in:
parent
d2fac4d79e
commit
8a2f70ea01
30 changed files with 1669 additions and 624 deletions
16
core/story-manager/interfaces/i_audio_event.h
Normal file
16
core/story-manager/interfaces/i_audio_event.h
Normal 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
|
||||
|
|
@ -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() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
973
story-editor/src/app/app_controller.cpp
Normal file
973
story-editor/src/app/app_controller.cpp
Normal 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();
|
||||
}
|
||||
149
story-editor/src/app/app_controller.h
Normal file
149
story-editor/src/app/app_controller.h
Normal 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
|
||||
51
story-editor/src/app/debug_context.h
Normal file
51
story-editor/src/app/debug_context.h
Normal 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
31
story-editor/src/app/logger.h
Normal file
31
story-editor/src/app/logger.h
Normal 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;
|
||||
};
|
||||
|
||||
56
story-editor/src/app/status_bar.h
Normal file
56
story-editor/src/app/status_bar.h
Normal 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
|
||||
101
story-editor/src/app/tool_bar.h
Normal file
101
story-editor/src/app/tool_bar.h
Normal 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
|
||||
45
story-editor/src/events/all_events.h
Normal file
45
story-editor/src/events/all_events.h
Normal 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
|
||||
22
story-editor/src/events/event.h
Normal file
22
story-editor/src/events/event.h
Normal 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
|
||||
32
story-editor/src/events/event_bus.cpp
Normal file
32
story-editor/src/events/event_bus.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
53
story-editor/src/events/event_bus.h
Normal file
53
story-editor/src/events/event_bus.h
Normal 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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,27 +578,11 @@ 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)
|
||||
{
|
||||
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
|
||||
|
|
@ -862,7 +595,7 @@ void MainWindow::OpenProject(const std::string &uuid)
|
|||
}
|
||||
|
||||
// Save recent projects on disk
|
||||
SaveParams();
|
||||
m_appController.SaveParams();
|
||||
}
|
||||
|
||||
m_nodeEditorWindow.Enable();
|
||||
|
|
@ -874,11 +607,6 @@ void MainWindow::OpenProject(const std::string &uuid)
|
|||
m_PropertiesWindow.Enable();
|
||||
m_variablesWindow.Enable();
|
||||
m_cpuWindow.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Open project error");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
Loading…
Reference in a new issue