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>
|
#include <string>
|
||||||
|
|
||||||
|
class ILogSubject {
|
||||||
|
public:
|
||||||
|
virtual void LogEvent(const std::string &txt, bool critical = false) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class ILogger
|
class ILogger
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void Log(const std::string &txt, bool critical = false) = 0;
|
virtual void Log(const std::string &txt, bool critical = false) = 0;
|
||||||
|
virtual void RegisterSubject(std::shared_ptr<ILogSubject> subject) = 0;
|
||||||
virtual ~ILogger() {}
|
virtual ~ILogger() {}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -42,14 +42,6 @@ public:
|
||||||
// Modules
|
// Modules
|
||||||
virtual void OpenModule(const std::string &uuid) = 0;
|
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
|
// Node interaction
|
||||||
virtual void BuildNodes(bool compileonly) = 0;
|
virtual void BuildNodes(bool compileonly) = 0;
|
||||||
virtual void BuildCode(bool compileonly) = 0;
|
virtual void BuildCode(bool compileonly) = 0;
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,13 @@
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "thread_safe_queue.h"
|
#include "thread_safe_queue.h"
|
||||||
|
#include "i_audio_event.h"
|
||||||
|
|
||||||
struct AudioCommand {
|
struct AudioCommand {
|
||||||
std::string order;
|
std::string order;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IAudioEvent
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~IAudioEvent() {}
|
|
||||||
|
|
||||||
virtual void EndOfAudio() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class AudioPlayer
|
class AudioPlayer
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,8 @@ set(SRCS
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/main_window.cpp
|
src/main_window.cpp
|
||||||
|
|
||||||
|
src/app/app_controller.cpp
|
||||||
|
|
||||||
src/windows/window_base.cpp
|
src/windows/window_base.cpp
|
||||||
src/windows/console_window.cpp
|
src/windows/console_window.cpp
|
||||||
src/windows/library_window.cpp
|
src/windows/library_window.cpp
|
||||||
|
|
@ -167,7 +169,6 @@ set(SRCS
|
||||||
../shared/resource_manager.cpp
|
../shared/resource_manager.cpp
|
||||||
../shared/library_manager.cpp
|
../shared/library_manager.cpp
|
||||||
../shared/downloader.cpp
|
../shared/downloader.cpp
|
||||||
../shared/story_db.cpp
|
|
||||||
../shared/miniz.c
|
../shared/miniz.c
|
||||||
../shared/zip.cpp
|
../shared/zip.cpp
|
||||||
../shared/platform_folders.cpp
|
../shared/platform_folders.cpp
|
||||||
|
|
@ -187,10 +188,9 @@ set(SRCS
|
||||||
../core/story-manager/src/nodes/print_node.cpp
|
../core/story-manager/src/nodes/print_node.cpp
|
||||||
../core/story-manager/src/nodes/syscall_node.cpp
|
../core/story-manager/src/nodes/syscall_node.cpp
|
||||||
../core/story-manager/src/nodes/connection.cpp
|
../core/story-manager/src/nodes/connection.cpp
|
||||||
|
../core/story-manager/src/story_db.cpp
|
||||||
../core/story-manager/lib/sys_lib.cpp
|
../core/story-manager/src/sys_lib.cpp
|
||||||
../core/story-manager/lib/resource.cpp
|
../core/story-manager/src/resource.cpp
|
||||||
|
|
||||||
|
|
||||||
../core/chip32/chip32_assembler.cpp
|
../core/chip32/chip32_assembler.cpp
|
||||||
../core/chip32/chip32_vm.c
|
../core/chip32/chip32_vm.c
|
||||||
|
|
@ -267,6 +267,8 @@ target_include_directories(${STORY_EDITOR_PROJECT} PUBLIC
|
||||||
src/windows
|
src/windows
|
||||||
src/docks
|
src/docks
|
||||||
src/dialogs
|
src/dialogs
|
||||||
|
src/app
|
||||||
|
src/events
|
||||||
|
|
||||||
../firmware/library
|
../firmware/library
|
||||||
../core/chip32
|
../core/chip32
|
||||||
|
|
@ -274,7 +276,6 @@ target_include_directories(${STORY_EDITOR_PROJECT} PUBLIC
|
||||||
../core/story-manager/src
|
../core/story-manager/src
|
||||||
../core/story-manager/src/nodes
|
../core/story-manager/src/nodes
|
||||||
../core/story-manager/src/compiler
|
../core/story-manager/src/compiler
|
||||||
../core/story-manager/lib
|
|
||||||
../core/story-manager/interfaces
|
../core/story-manager/interfaces
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[Window][WindowOverViewport_11111111]
|
[Window][WindowOverViewport_11111111]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=1220,694
|
Size=1220,962
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Debug##Default]
|
[Window][Debug##Default]
|
||||||
|
|
@ -9,32 +9,32 @@ Size=400,400
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Library Manager]
|
[Window][Library Manager]
|
||||||
Pos=568,26
|
Pos=878,26
|
||||||
Size=712,288
|
Size=402,403
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,0
|
DockId=0x00000003,0
|
||||||
|
|
||||||
[Window][Console]
|
[Window][Console]
|
||||||
Pos=60,467
|
Pos=60,735
|
||||||
Size=610,253
|
Size=610,253
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,0
|
DockId=0x00000004,0
|
||||||
|
|
||||||
[Window][Emulator]
|
[Window][Emulator]
|
||||||
Pos=568,26
|
Pos=878,26
|
||||||
Size=712,288
|
Size=402,403
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,5
|
DockId=0x00000003,5
|
||||||
|
|
||||||
[Window][Code viewer]
|
[Window][Code viewer]
|
||||||
Pos=568,26
|
Pos=878,26
|
||||||
Size=712,288
|
Size=402,403
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,4
|
DockId=0x00000003,4
|
||||||
|
|
||||||
[Window][Resources]
|
[Window][Resources]
|
||||||
Pos=568,26
|
Pos=878,26
|
||||||
Size=712,288
|
Size=402,403
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,1
|
DockId=0x00000003,1
|
||||||
|
|
||||||
|
|
@ -50,36 +50,36 @@ Size=150,42
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Variables]
|
[Window][Variables]
|
||||||
Pos=672,467
|
Pos=672,735
|
||||||
Size=608,253
|
Size=608,253
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
[Window][CPU]
|
[Window][CPU]
|
||||||
Pos=568,26
|
Pos=878,26
|
||||||
Size=712,288
|
Size=402,403
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,2
|
DockId=0x00000003,2
|
||||||
|
|
||||||
[Window][RAM view]
|
[Window][RAM view]
|
||||||
Pos=568,26
|
Pos=878,26
|
||||||
Size=712,288
|
Size=402,403
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,3
|
DockId=0x00000003,3
|
||||||
|
|
||||||
[Window][Properties]
|
[Window][Properties]
|
||||||
Pos=568,316
|
Pos=878,431
|
||||||
Size=712,149
|
Size=402,302
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000006,0
|
DockId=0x00000006,0
|
||||||
|
|
||||||
[Window][ToolBar]
|
[Window][ToolBar]
|
||||||
Pos=0,26
|
Pos=0,26
|
||||||
Size=60,694
|
Size=60,962
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][QuitConfirm]
|
[Window][QuitConfirm]
|
||||||
Pos=508,312
|
Pos=508,446
|
||||||
Size=264,96
|
Size=264,96
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
|
|
@ -90,13 +90,13 @@ Collapsed=0
|
||||||
|
|
||||||
[Window][Module editor]
|
[Window][Module editor]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=506,439
|
Size=816,707
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,1
|
DockId=0x00000001,1
|
||||||
|
|
||||||
[Window][Story editor]
|
[Window][Story editor]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=506,439
|
Size=816,707
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000001,0
|
||||||
|
|
||||||
|
|
@ -133,12 +133,12 @@ Column 1 Width=104
|
||||||
Column 2 Width=120
|
Column 2 Width=120
|
||||||
|
|
||||||
[Docking][Data]
|
[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=0x00000007 Parent=0x08BD597D SizeRef=1220,439 Split=X
|
||||||
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=506,694 CentralNode=1 Selected=0x93ADCAAB
|
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=816,694 CentralNode=1 Selected=0x93ADCAAB
|
||||||
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=712,694 Split=Y Selected=0x52EB28B5
|
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=402,694 Split=Y Selected=0x52EB28B5
|
||||||
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,369 Selected=0x63869CAF
|
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,250 Selected=0x4B07C626
|
||||||
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,191 Selected=0x8C72BEA8
|
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,187 Selected=0x8C72BEA8
|
||||||
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,253 Split=X Selected=0xEA83D666
|
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,253 Split=X Selected=0xEA83D666
|
||||||
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=610,192 Selected=0xEA83D666
|
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=610,192 Selected=0xEA83D666
|
||||||
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=608,192 Selected=0x6DE9B20C
|
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 "main_window.h"
|
||||||
|
#include "app_controller.h"
|
||||||
|
#include "event_bus.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
// Main code
|
// Main code
|
||||||
int main(int, char**)
|
int main(int, char**)
|
||||||
{
|
{
|
||||||
|
Logger logger;
|
||||||
|
EventBus eventBus;
|
||||||
|
|
||||||
|
AppController appController(logger, eventBus);
|
||||||
|
|
||||||
|
MainWindow w(logger, eventBus, appController);
|
||||||
|
|
||||||
MainWindow w;
|
|
||||||
if (w.Initialize())
|
if (w.Initialize())
|
||||||
{
|
{
|
||||||
w.Loop();
|
w.Loop();
|
||||||
|
appController.ProcessStory();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -22,34 +22,24 @@
|
||||||
#include "ImGuiFileDialog.h"
|
#include "ImGuiFileDialog.h"
|
||||||
#include "imgui_memory_editor.h"
|
#include "imgui_memory_editor.h"
|
||||||
|
|
||||||
MainWindow::MainWindow()
|
#include "app_controller.h"
|
||||||
: m_resources(*this)
|
#include "all_events.h"
|
||||||
, m_nodesFactory(*this)
|
|
||||||
, m_libraryManager(*this, m_nodesFactory)
|
MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController)
|
||||||
, m_emulatorWindow(*this)
|
: m_logger(logger)
|
||||||
, m_debuggerWindow(*this)
|
, m_eventBus(eventBus)
|
||||||
, m_cpuWindow(*this)
|
, m_appController(appController)
|
||||||
, m_resourcesWindow(*this)
|
, m_emulatorWindow(appController)
|
||||||
, m_nodeEditorWindow(*this, m_nodesFactory)
|
, m_debuggerWindow(appController)
|
||||||
, m_moduleEditorWindow(*this, m_nodesFactory, IStoryProject::Type::PROJECT_TYPE_MODULE)
|
, m_cpuWindow(appController)
|
||||||
, m_libraryWindow(*this, m_libraryManager, m_nodesFactory)
|
, m_resourcesWindow(appController.GetResourceManager())
|
||||||
, m_variablesWindow(*this)
|
, m_nodeEditorWindow(appController)
|
||||||
, m_player(*this)
|
, m_moduleEditorWindow(appController)
|
||||||
, m_webServer(m_libraryManager)
|
, m_libraryWindow(appController)
|
||||||
|
, m_variablesWindow(appController)
|
||||||
{
|
{
|
||||||
// VM Initialize
|
|
||||||
m_chip32_ctx.stack_size = 512;
|
|
||||||
|
|
||||||
m_chip32_ctx.rom.mem = m_rom_data;
|
logger.RegisterSubject(shared_from_this());
|
||||||
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);
|
|
||||||
|
|
||||||
CloseProject();
|
CloseProject();
|
||||||
CloseModule();
|
CloseModule();
|
||||||
|
|
@ -59,11 +49,19 @@ MainWindow::MainWindow()
|
||||||
// define style for all files
|
// define style for all files
|
||||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile, "", ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ICON_MDI_FILE);
|
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()
|
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()
|
void MainWindow::StepInstruction()
|
||||||
{
|
{
|
||||||
m_dbg.run_result = chip32_step(&m_chip32_ctx);
|
m_dbg.run_result = chip32_step(&m_chip32_ctx);
|
||||||
UpdateVmView();
|
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 MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
|
||||||
{
|
{
|
||||||
uint8_t retCode = SYSCALL_RET_OK;
|
uint8_t retCode = SYSCALL_RET_OK;
|
||||||
Log("SYSCALL: " + std::to_string(code));
|
m_logger.Log("SYSCALL: " + std::to_string(code));
|
||||||
|
|
||||||
// Media
|
// Media
|
||||||
if (code == 1) // Execute 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
|
// image file name address is in R0
|
||||||
std::string imageFile = m_story->BuildFullAssetsPath(GetStringFromMemory(m_chip32_ctx.registers[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);
|
m_emulatorWindow.SetImage(imageFile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -335,7 +109,7 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
|
||||||
{
|
{
|
||||||
// sound file name address is in R1
|
// sound file name address is in R1
|
||||||
std::string soundFile = m_story->BuildFullAssetsPath(GetStringFromMemory(m_chip32_ctx.registers[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);
|
m_player.Play(soundFile);
|
||||||
}
|
}
|
||||||
retCode = SYSCALL_RET_OK; // We continue execution, script must wait for event if necessary (end of audio)
|
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){
|
switch(arg_count){
|
||||||
case 0:
|
case 0:
|
||||||
Log(text);
|
m_logger.Log(text);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]);
|
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]);
|
||||||
Log(working_buf);
|
m_logger.Log(working_buf);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3]);
|
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3]);
|
||||||
Log(working_buf);
|
m_logger.Log(working_buf);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -408,31 +182,6 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
|
||||||
return retCode;
|
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()
|
float MainWindow::DrawMainMenuBar()
|
||||||
{
|
{
|
||||||
bool showParameters = false;
|
bool showParameters = false;
|
||||||
|
|
@ -829,27 +578,11 @@ void MainWindow::SaveProject()
|
||||||
{
|
{
|
||||||
nlohmann::json model;
|
nlohmann::json model;
|
||||||
m_story->Save(m_resources);
|
m_story->Save(m_resources);
|
||||||
Log("Project saved");
|
m_logger.Log("Project saved");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OpenProject(const std::string &uuid)
|
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);
|
m_nodeEditorWindow.Load(m_story);
|
||||||
auto proj = m_story->GetProjectFilePath();
|
auto proj = m_story->GetProjectFilePath();
|
||||||
// Add to recent if not exists
|
// Add to recent if not exists
|
||||||
|
|
@ -862,7 +595,7 @@ void MainWindow::OpenProject(const std::string &uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save recent projects on disk
|
// Save recent projects on disk
|
||||||
SaveParams();
|
m_appController.SaveParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_nodeEditorWindow.Enable();
|
m_nodeEditorWindow.Enable();
|
||||||
|
|
@ -874,11 +607,6 @@ void MainWindow::OpenProject(const std::string &uuid)
|
||||||
m_PropertiesWindow.Enable();
|
m_PropertiesWindow.Enable();
|
||||||
m_variablesWindow.Enable();
|
m_variablesWindow.Enable();
|
||||||
m_cpuWindow.Enable();
|
m_cpuWindow.Enable();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log("Open project error");
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshProjectInformation();
|
RefreshProjectInformation();
|
||||||
}
|
}
|
||||||
|
|
@ -895,7 +623,7 @@ void MainWindow::NewModule()
|
||||||
void MainWindow::SaveModule()
|
void MainWindow::SaveModule()
|
||||||
{
|
{
|
||||||
m_nodesFactory.SaveAllModules(m_resources);;
|
m_nodesFactory.SaveAllModules(m_resources);;
|
||||||
Log("Modules saved");
|
m_logger.Log("Modules saved");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OpenModule(const std::string &uuid)
|
void MainWindow::OpenModule(const std::string &uuid)
|
||||||
|
|
@ -903,17 +631,17 @@ void MainWindow::OpenModule(const std::string &uuid)
|
||||||
m_module = m_nodesFactory.GetModule(uuid);
|
m_module = m_nodesFactory.GetModule(uuid);
|
||||||
if (!m_module)
|
if (!m_module)
|
||||||
{
|
{
|
||||||
Log("Cannot find module: " + uuid);
|
m_logger.Log("Cannot find module: " + uuid);
|
||||||
}
|
}
|
||||||
else if (m_module->Load(m_resources, m_nodesFactory))
|
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.Load(m_module);
|
||||||
m_moduleEditorWindow.Enable();
|
m_moduleEditorWindow.Enable();
|
||||||
}
|
}
|
||||||
else
|
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
|
else
|
||||||
{
|
{
|
||||||
Log("Unknown file format: " + filePathName);
|
m_logger.Log("Unknown file format: " + filePathName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1051,8 +779,6 @@ void MainWindow::Loop()
|
||||||
ImGui::DockSpaceOverViewport(0, vp);
|
ImGui::DockSpaceOverViewport(0, vp);
|
||||||
float height = DrawMainMenuBar();
|
float height = DrawMainMenuBar();
|
||||||
|
|
||||||
// DrawStatusBar();
|
|
||||||
|
|
||||||
ProcessStory();
|
ProcessStory();
|
||||||
|
|
||||||
// ------------ Draw all windows
|
// ------------ 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);
|
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
|
std::string MainWindow::BuildFullAssetsPath(const std::string_view fileName) const
|
||||||
{
|
{
|
||||||
return m_story->BuildFullAssetsPath(fileName);
|
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)
|
void MainWindow::OpenFunction(const std::string &uuid, const std::string &name)
|
||||||
{
|
{
|
||||||
m_nodeEditorWindow.OpenFunction(uuid, 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)
|
void MainWindow::LoadBinaryStory(const std::string &filename)
|
||||||
{
|
{
|
||||||
|
|
@ -1178,11 +872,11 @@ void MainWindow::LoadBinaryStory(const std::string &filename)
|
||||||
{
|
{
|
||||||
m_dbg.run_result = VM_READY;
|
m_dbg.run_result = VM_READY;
|
||||||
chip32_initialize(&m_chip32_ctx);
|
chip32_initialize(&m_chip32_ctx);
|
||||||
Log("Loaded binary file: " + filename);
|
m_logger.Log("Loaded binary file: " + filename);
|
||||||
}
|
}
|
||||||
else
|
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)
|
void MainWindow::BuildNodes(bool compileonly)
|
||||||
{
|
{
|
||||||
|
|
@ -1271,12 +925,12 @@ void MainWindow::Build(bool compileonly)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log("Program too big. Expand ROM memory.");
|
m_logger.Log("Program too big. Expand ROM memory.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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
|
m_debuggerWindow.AddError(err.line, err.message); // show also the error in the code editor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1311,29 +965,13 @@ void MainWindow::UpdateVmView()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Not found
|
// 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
|
// Refresh RAM content
|
||||||
// m_ramView->SetMemory(m_ram_data, m_chip32_ctx.ram.size);
|
// 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()
|
void MainWindow::LoadParams()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
@ -1371,6 +1009,6 @@ void MainWindow::LoadParams()
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
Log(e.what(), true);
|
m_logger.Log(e.what(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,82 +3,31 @@
|
||||||
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include "console_window.h"
|
#include "console_window.h"
|
||||||
#include "debugger_window.h"
|
#include "debugger_window.h"
|
||||||
|
|
||||||
#include "emulator_dock.h"
|
#include "emulator_dock.h"
|
||||||
#include "resources_window.h"
|
#include "resources_window.h"
|
||||||
#include "node_editor_window.h"
|
#include "node_editor_window.h"
|
||||||
#include "properties_window.h"
|
#include "properties_window.h"
|
||||||
#include "variables_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 "library_window.h"
|
||||||
#include "cpu_window.h"
|
#include "cpu_window.h"
|
||||||
#include "story_machine.h"
|
|
||||||
#include "web_server.h"
|
|
||||||
#include "nodes_factory.h"
|
|
||||||
|
|
||||||
// Dialogs
|
// Dialogs
|
||||||
#include "about_dialog.h"
|
#include "about_dialog.h"
|
||||||
|
|
||||||
struct DebugContext
|
#include "event_bus.h"
|
||||||
{
|
#include "app_controller.h"
|
||||||
uint32_t event_mask{0};
|
#include "i_logger.h"
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class MainWindow : public IStoryManager, public IAudioEvent, public ILogger
|
class MainWindow : public std::enable_shared_from_this<MainWindow>, public ILogSubject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MainWindow();
|
MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
@ -87,27 +36,15 @@ public:
|
||||||
private:
|
private:
|
||||||
enum VmEventType { EvNoEvent, EvStep, EvRun, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished, EvStop, EvHomeButton};
|
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_story; // Current story
|
||||||
std::shared_ptr<StoryProject> m_module; // Current module
|
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;
|
std::vector<std::string> m_recentProjects;
|
||||||
|
|
||||||
NodesFactory m_nodesFactory;
|
|
||||||
|
|
||||||
ResourceManager m_resources;
|
|
||||||
|
|
||||||
LibraryManager m_libraryManager;
|
|
||||||
|
|
||||||
Gui m_gui;
|
Gui m_gui;
|
||||||
EmulatorDock m_emulatorWindow;
|
EmulatorDock m_emulatorWindow;
|
||||||
ConsoleWindow m_consoleWindow;
|
ConsoleWindow m_consoleWindow;
|
||||||
|
|
@ -117,33 +54,17 @@ private:
|
||||||
char m_project_name[256] = "";
|
char m_project_name[256] = "";
|
||||||
|
|
||||||
ResourcesWindow m_resourcesWindow;
|
ResourcesWindow m_resourcesWindow;
|
||||||
|
|
||||||
NodeEditorWindow m_nodeEditorWindow;
|
NodeEditorWindow m_nodeEditorWindow;
|
||||||
|
|
||||||
NodeEditorWindow m_moduleEditorWindow;
|
NodeEditorWindow m_moduleEditorWindow;
|
||||||
|
|
||||||
PropertiesWindow m_PropertiesWindow;
|
PropertiesWindow m_PropertiesWindow;
|
||||||
|
|
||||||
LibraryWindow m_libraryWindow;
|
LibraryWindow m_libraryWindow;
|
||||||
|
|
||||||
VariablesWindow m_variablesWindow;
|
VariablesWindow m_variablesWindow;
|
||||||
|
|
||||||
AudioPlayer m_player;
|
|
||||||
|
|
||||||
struct VmEvent
|
|
||||||
{
|
|
||||||
VmEventType type;
|
|
||||||
};
|
|
||||||
|
|
||||||
ThreadSafeQueue<VmEvent> m_eventQueue;
|
|
||||||
|
|
||||||
WebServer m_webServer;
|
|
||||||
|
|
||||||
// Dialogs
|
// Dialogs
|
||||||
AboutDialog m_aboutDialog;
|
AboutDialog m_aboutDialog;
|
||||||
|
|
||||||
// From IStoryManager (proxy to StoryProject class)
|
// From IStoryManager (proxy to StoryProject class)
|
||||||
virtual void OpenProject(const std::string &uuid) override;
|
void OpenProject(const std::string &uuid);
|
||||||
void SaveProject();
|
void SaveProject();
|
||||||
void CloseProject();
|
void CloseProject();
|
||||||
|
|
||||||
|
|
@ -152,59 +73,15 @@ private:
|
||||||
void SaveModule();
|
void SaveModule();
|
||||||
void CloseModule();
|
void CloseModule();
|
||||||
|
|
||||||
// From IStoryManager (proxy to StoryProject class)
|
// From ILogSubject
|
||||||
virtual void ImportProject(const std::string &filePathName, int format);
|
virtual void LogEvent(const std::string &txt, bool critical) override;
|
||||||
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;
|
|
||||||
|
|
||||||
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();
|
void LoadParams();
|
||||||
|
|
||||||
float DrawMainMenuBar();
|
float DrawMainMenuBar();
|
||||||
bool ShowQuitConfirm();
|
bool ShowQuitConfirm();
|
||||||
void DrawToolBar(float topPadding);
|
void DrawToolBar(float topPadding);
|
||||||
|
|
||||||
void DrawStatusBar();
|
|
||||||
|
|
||||||
void UpdateVmView();
|
void UpdateVmView();
|
||||||
uint8_t Syscall(chip32_ctx_t *ctx, uint8_t code);
|
uint8_t Syscall(chip32_ctx_t *ctx, uint8_t code);
|
||||||
std::string GetStringFromMemory(uint32_t addr);
|
std::string GetStringFromMemory(uint32_t addr);
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
//static thread_pool pool;
|
//static thread_pool pool;
|
||||||
|
|
||||||
ResourcesWindow::ResourcesWindow(IStoryManager &project)
|
ResourcesWindow::ResourcesWindow(ResourceManager &resources)
|
||||||
: WindowBase("Resources")
|
: WindowBase("Resources")
|
||||||
, m_story(project)
|
, m_resources(resources)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ void ResourcesWindow::ChooseFile()
|
||||||
m_showImportDialog = false;
|
m_showImportDialog = false;
|
||||||
// open Dialog Simple
|
// open Dialog Simple
|
||||||
IGFD::FileDialogConfig config;
|
IGFD::FileDialogConfig config;
|
||||||
config.path = m_story.BuildFullAssetsPath("");
|
config.path = m_resources.BuildFullAssetsPath("");
|
||||||
config.countSelectionMax = 1;
|
config.countSelectionMax = 1;
|
||||||
config.sidePaneWidth = 350.0f;
|
config.sidePaneWidth = 350.0f;
|
||||||
config.flags = ImGuiFileDialogFlags_Modal;
|
config.flags = ImGuiFileDialogFlags_Modal;
|
||||||
|
|
@ -49,7 +49,7 @@ void ResourcesWindow::ChooseFile()
|
||||||
|
|
||||||
|
|
||||||
std::filesystem::path p(filePathName);
|
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;
|
bool allowCopy = true;
|
||||||
// On ne copie pas le fichier sur lui-même
|
// On ne copie pas le fichier sur lui-même
|
||||||
|
|
@ -72,7 +72,7 @@ void ResourcesWindow::ChooseFile()
|
||||||
res->format = ext;
|
res->format = ext;
|
||||||
res->type = m_soundFile ? "sound" : "image";
|
res->type = m_soundFile ? "sound" : "image";
|
||||||
res->file = p.filename().generic_string();
|
res->file = p.filename().generic_string();
|
||||||
m_story.AddResource(res);
|
m_resources.Add(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// close
|
// close
|
||||||
|
|
@ -120,7 +120,7 @@ void ResourcesWindow::Draw()
|
||||||
|
|
||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
auto [b, e] = m_story.Resources();
|
auto [b, e] = m_resources.Items();
|
||||||
|
|
||||||
int id = 1000;
|
int id = 1000;
|
||||||
for (auto it = b; it != e; ++it)
|
for (auto it = b; it != e; ++it)
|
||||||
|
|
@ -168,7 +168,7 @@ void ResourcesWindow::Draw()
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (ImGui::SmallButton("Delete"))
|
if (ImGui::SmallButton("Delete"))
|
||||||
{
|
{
|
||||||
m_story.DeleteResource(it);
|
m_resources.DeleteResource(it);
|
||||||
quitLoop = true;
|
quitLoop = true;
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,17 @@
|
||||||
|
|
||||||
#include "i_story_manager.h"
|
#include "i_story_manager.h"
|
||||||
#include "window_base.h"
|
#include "window_base.h"
|
||||||
|
#include "resource_manager.h"
|
||||||
|
|
||||||
class ResourcesWindow : public WindowBase
|
class ResourcesWindow : public WindowBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ResourcesWindow(IStoryManager &project);
|
ResourcesWindow(ResourceManager &resources);
|
||||||
~ResourcesWindow();
|
~ResourcesWindow();
|
||||||
virtual void Draw() override;
|
virtual void Draw() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IStoryManager &m_story;
|
ResourceManager &m_resources;
|
||||||
|
|
||||||
bool m_showImportDialog{false};
|
bool m_showImportDialog{false};
|
||||||
bool m_soundFile{false};
|
bool m_soundFile{false};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue