better event, better audio, add QOI to emulator, emulator is executed

This commit is contained in:
Anthony Rabine 2023-12-23 14:33:05 +01:00
parent a730ea4e0b
commit 41b52e3532
30 changed files with 690 additions and 216 deletions

View file

@ -130,6 +130,7 @@ typedef enum
typedef enum typedef enum
{ {
VM_READY, // VM Ready to be started
VM_FINISHED, // execution completed (i.e. got halt instruction) VM_FINISHED, // execution completed (i.e. got halt instruction)
VM_SKIPED, // skipped instruction VM_SKIPED, // skipped instruction
VM_WAIT_EVENT, // execution paused since we hit the maximum instructions VM_WAIT_EVENT, // execution paused since we hit the maximum instructions

View file

@ -13,10 +13,14 @@ the simple_mixing example for how best to do this.
#define MINIAUDIO_IMPLEMENTATION #define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h" #include "miniaudio.h"
#include <functional>
#include <stdio.h> #include <stdio.h>
#include "audio_player.h"
ma_event g_stopEvent; /* <-- Signaled by the audio thread, waited on by the main thread. */ static ThreadSafeQueue<AudioCommand> g_audioQueue;
static ma_decoder decoder;
static ma_device device;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
{ {
@ -28,20 +32,23 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
ma_uint64 framesRead; ma_uint64 framesRead;
ma_result result = ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount, &framesRead); ma_result result = ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount, &framesRead);
if (framesRead < frameCount) {
// Reached the end. if (result == MA_AT_END) {
ma_event_signal(&g_stopEvent); g_audioQueue.push({"end", ""});
} }
// if (framesRead < frameCount) {
// // Reached the end.
// ma_event_signal(&g_stopEvent);
// }
(void)pInput; (void)pInput;
} }
int miniaudio_play(const char* filename)
static int miniaudio_play(const char* filename)
{ {
ma_result result; ma_result result;
ma_decoder decoder;
ma_device_config deviceConfig; ma_device_config deviceConfig;
ma_device device;
result = ma_decoder_init_file(filename, NULL, &decoder); result = ma_decoder_init_file(filename, NULL, &decoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
@ -62,8 +69,6 @@ int miniaudio_play(const char* filename)
return -3; return -3;
} }
ma_event_init(&g_stopEvent);
if (ma_device_start(&device) != MA_SUCCESS) { if (ma_device_start(&device) != MA_SUCCESS) {
printf("Failed to start playback device.\n"); printf("Failed to start playback device.\n");
ma_device_uninit(&device); ma_device_uninit(&device);
@ -71,14 +76,67 @@ int miniaudio_play(const char* filename)
return -4; return -4;
} }
printf("Wait untile end...\n");
// getchar();
ma_event_wait(&g_stopEvent);
printf("End!\n");
ma_device_uninit(&device);
ma_decoder_uninit(&decoder);
return 0; return 0;
} }
AudioPlayer::AudioPlayer(IAudioEvent &event)
: m_event(event)
{
m_audioThread = std::thread( std::bind(&AudioPlayer::AudioThread, this) );
}
void AudioPlayer::Play(const std::string &filename)
{
g_audioQueue.push({"play", filename});
}
AudioPlayer::~AudioPlayer()
{
// Quit audio thread
g_audioQueue.push({"quit", ""});
if (m_audioThread.joinable())
{
m_audioThread.join();
}
}
void AudioPlayer::CloseAudio()
{
ma_device_uninit(&device);
ma_decoder_uninit(&decoder);
}
#define AUDIO_STATE_WAIT_PLAY 1
#define AUDIO_STATE_WAIT_END 2
void AudioPlayer::AudioThread()
{
int state = AUDIO_STATE_WAIT_PLAY;
for (;;)
{
auto cmd = g_audioQueue.front();
g_audioQueue.pop();
if (cmd.order == "quit") {
return;
}
else if (cmd.order == "play")
{
if (state == AUDIO_STATE_WAIT_PLAY)
{
state = AUDIO_STATE_WAIT_END;
miniaudio_play(cmd.filename.c_str());
}
}
else if (cmd.order == "end")
{
if (state == AUDIO_STATE_WAIT_END)
{
state = AUDIO_STATE_WAIT_PLAY;
CloseAudio();
m_event.EndOfAudio();
}
}
}
}

View file

@ -0,0 +1,47 @@
#ifndef AUDIO_PLAYER_H
#define AUDIO_PLAYER_H
#include "miniaudio.h"
#include <string>
#include <thread>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <queue>
#include "thread_safe_queue.h"
struct AudioCommand {
std::string order;
std::string filename;
};
class IAudioEvent
{
public:
virtual ~IAudioEvent() {}
virtual void EndOfAudio() = 0;
};
class AudioPlayer
{
public:
AudioPlayer(IAudioEvent &event);
~AudioPlayer();
void Play(const std::string &filename);
private:
IAudioEvent &m_event;
std::thread m_audioThread;
void AudioThread();
void CloseAudio();
};
#endif // AUDIO_PLAYER_H

View file

@ -4,21 +4,16 @@
#include <iostream> #include <iostream>
#include <queue> #include <queue>
#include <filesystem> #include <filesystem>
#include "json.hpp" #include "json.hpp"
StoryProject::StoryProject() StoryProject::StoryProject()
{ {
m_audioThread = std::thread( std::bind(&StoryProject::AudioThread, this) );
} }
StoryProject::~StoryProject() StoryProject::~StoryProject()
{ {
// Quit audio thread
m_audioQueue.push({"quit", ""});
if (m_audioThread.joinable())
{
m_audioThread.join();
}
} }
@ -100,7 +95,7 @@ void StoryProject::SaveStory(const std::vector<uint8_t> &m_program)
void StoryProject::Initialize(const std::string &file_path) void StoryProject::Initialize(const std::string &file_path)
{ {
m_project_file_path = file_path; m_story_file_path = file_path;
std::filesystem::path p(file_path); std::filesystem::path p(file_path);
m_working_dir= p.parent_path().generic_string(); m_working_dir= p.parent_path().generic_string();
@ -243,7 +238,7 @@ void StoryProject::Save(const nlohmann::json &model, ResourceManager &manager)
{ {
nlohmann::json resourcesData; nlohmann::json resourcesData;
auto [b, e] = manager.filter(""); auto [b, e] = manager.Items();
for (auto it = b; it != e; ++it) for (auto it = b; it != e; ++it)
{ {
nlohmann::json obj = {{"type", (*it)->type}, nlohmann::json obj = {{"type", (*it)->type},
@ -258,7 +253,7 @@ void StoryProject::Save(const nlohmann::json &model, ResourceManager &manager)
j["nodegraph"] = model; j["nodegraph"] = model;
std::ofstream o(m_project_file_path); std::ofstream o(m_story_file_path);
o << std::setw(4) << j << std::endl; o << std::setw(4) << j << std::endl;
} }
@ -364,32 +359,6 @@ std::string StoryProject::GetFileExtension(const std::string &fileName)
return ""; return "";
} }
extern "C" int miniaudio_play(const char* filename);
void StoryProject::AudioThread()
{
for (;;)
{
auto cmd = m_audioQueue.front();
if (cmd.order == "play") {
miniaudio_play(cmd.filename.c_str());
// QMetaObject::invokeMethod(this, "sigAudioStopped", Qt::QueuedConnection);
m_audioQueue.pop();
} else {
return;
}
}
}
void StoryProject::PlaySoundFile(const std::string &fileName)
{
m_audioQueue.push({"play", fileName});
}
void StoryProject::SetImageFormat(ImageFormat format) void StoryProject::SetImageFormat(ImageFormat format)
{ {
m_imageFormat = format; m_imageFormat = format;
@ -408,7 +377,7 @@ void StoryProject::SetDisplayFormat(int w, int h)
std::string StoryProject::GetProjectFilePath() const std::string StoryProject::GetProjectFilePath() const
{ {
return m_project_file_path; return m_story_file_path;
} }
std::string StoryProject::GetWorkingDir() const std::string StoryProject::GetWorkingDir() const

View file

@ -5,47 +5,13 @@
#include <string> #include <string>
#include <filesystem> #include <filesystem>
#include "json.hpp" #include "json.hpp"
#include <condition_variable>
#include <queue>
#include <memory> #include <memory>
#include <random> #include <random>
#include <thread>
#include <mutex>
#include "json.hpp" #include "json.hpp"
#include "resource_manager.h" #include "resource_manager.h"
#include "audio_player.h"
template <typename T>
class ThreadSafeQueue {
std::mutex mutex;
std::condition_variable cond_var;
std::queue<T> queue;
public:
void push(T&& item) {
{
std::lock_guard lock(mutex);
queue.push(item);
}
cond_var.notify_one();
}
T& front() {
std::unique_lock lock(mutex);
cond_var.wait(lock, [&]{ return !queue.empty(); });
return queue.front();
}
void pop() {
std::lock_guard lock(mutex);
queue.pop();
}
};
struct AudioCommand {
std::string order;
std::string filename;
};
@ -141,9 +107,8 @@ public:
static void EraseString(std::string &theString, const std::string &toErase); static void EraseString(std::string &theString, const std::string &toErase);
static std::string ToUpper(const std::string &input); static std::string ToUpper(const std::string &input);
void SaveStory(const std::vector<uint8_t> &m_program); void SaveStory(const std::vector<uint8_t> &m_program);
void PlaySoundFile(const std::string &fileName);
private: private:
// Project properties and location // Project properties and location
std::string m_name; /// human readable name std::string m_name; /// human readable name
@ -156,17 +121,13 @@ private:
std::string m_titleSound; std::string m_titleSound;
std::filesystem::path m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped std::filesystem::path m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped
std::string m_project_file_path; /// JSON project file std::string m_story_file_path; /// JSON project file
int m_display_w{320}; int m_display_w{320};
int m_display_h{240}; int m_display_h{240};
std::thread m_audioThread;
ThreadSafeQueue<AudioCommand> m_audioQueue;
ImageFormat m_imageFormat{IMG_FORMAT_BMP_4BITS}; ImageFormat m_imageFormat{IMG_FORMAT_BMP_4BITS};
SoundFormat m_soundFormat{SND_FORMAT_WAV}; SoundFormat m_soundFormat{SND_FORMAT_WAV};
void AudioThread();
}; };
#endif // STORY_PROJECT_H #endif // STORY_PROJECT_H

View file

@ -0,0 +1,52 @@
#ifndef THREAD_SAFE_QUEUE_H
#define THREAD_SAFE_QUEUE_H
#include <thread>
#include <condition_variable>
#include <mutex>
#include <queue>
template <typename T>
class ThreadSafeQueue {
std::mutex mutex;
std::condition_variable cond_var;
std::queue<T> queue;
public:
void push(T&& item) {
{
std::lock_guard lock(mutex);
queue.push(item);
}
cond_var.notify_one();
}
T& front() {
std::unique_lock lock(mutex);
cond_var.wait(lock, [&]{ return !queue.empty(); });
return queue.front();
}
void pop() {
std::lock_guard lock(mutex);
queue.pop();
}
bool try_pop(T& popped_item) {
std::unique_lock lock(mutex);
if (queue.empty()) {
return false; // La file d'attente est vide
}
popped_item = std::move(queue.front());
queue.pop();
return true;
}
};
#endif // THREAD_SAFE_QUEUE_H

View file

@ -131,10 +131,10 @@ set(SRCS
src/connection.cpp src/connection.cpp
src/connection.h src/connection.h
src/uuid.h src/media_converter.cpp
src/resource_manager.h src/media_converter.h
src/i_story_project.h
src/resource.h src/i_story_manager.h
libs/ImGuiColorTextEdit/TextEditor.cpp libs/ImGuiColorTextEdit/TextEditor.cpp
libs/ImGuiColorTextEdit/TextEditor.h libs/ImGuiColorTextEdit/TextEditor.h
@ -155,13 +155,15 @@ set(SRCS
../software/chip32/chip32_assembler.cpp ../software/chip32/chip32_assembler.cpp
../software/chip32/chip32_vm.c ../software/chip32/chip32_vm.c
../software/library/miniaudio.c ../software/library/audio_player.cpp
../software/library/audio_player.h
../software/library/miniaudio.h ../software/library/miniaudio.h
../software/library/uuid.h
src/story_project.cpp ../software/library/resource.h
src/story_project.h ../software/library/resource_manager.h
src/media_converter.cpp ../software/library/story_project.cpp
src/media_converter.h ../software/library/story_project.h
../software/library/thread_safe_queue.h
) )
if(WIN32) if(WIN32)

View file

@ -26,7 +26,8 @@ bool equals(InputIt1 first1, InputIt1 last1,
TextEditor::TextEditor() TextEditor::TextEditor()
: mLineSpacing(1.0f) : mLineSpacing(1.0f)
, mUndoIndex(0) , mUndoIndex(0)
, mExternalUndoBuffer(nullptr)
, mTabSize(4) , mTabSize(4)
, mOverwrite(false) , mOverwrite(false)
, mReadOnly(false) , mReadOnly(false)
@ -41,15 +42,10 @@ TextEditor::TextEditor()
, mColorRangeMin(0) , mColorRangeMin(0)
, mColorRangeMax(0) , mColorRangeMax(0)
, mSelectionMode(SelectionMode::Normal) , mSelectionMode(SelectionMode::Normal)
, mCheckComments(true) , mShowShortTabGlyphs(false)
, mLastClick(-1.0f) , mCheckComments(true)
, mHandleKeyboardInputs(true) , mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
, mHandleMouseInputs(true) , mLastClick(-1.0f)
, mIgnoreImGuiChild(false)
, mShowWhitespaces(true)
, mShowShortTabGlyphs(false)
, mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
, mExternalUndoBuffer(nullptr)
{ {
SetPalette(GetDarkPalette()); SetPalette(GetDarkPalette());
SetLanguageDefinition(LanguageDefinition::HLSL()); SetLanguageDefinition(LanguageDefinition::HLSL());
@ -981,6 +977,15 @@ void TextEditor::Render()
} }
} }
// Draw execution marker
if (mExecutionMarker == (lineNo + 1))
{
auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y);
drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Selection]);
}
// Draw line number (right aligned) // Draw line number (right aligned)
snprintf(buf, 16, "%d ", lineNo + 1); snprintf(buf, 16, "%d ", lineNo + 1);

View file

@ -247,6 +247,10 @@ public:
void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; }
void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; } void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; }
void SetExecutionMarker(int line) {
mExecutionMarker = line;
}
void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false);
void SetText(const std::string& aText); void SetText(const std::string& aText);
std::string GetText() const; std::string GetText() const;
@ -386,10 +390,10 @@ private:
bool mCursorPositionChanged; bool mCursorPositionChanged;
int mColorRangeMin, mColorRangeMax; int mColorRangeMin, mColorRangeMax;
SelectionMode mSelectionMode; SelectionMode mSelectionMode;
bool mHandleKeyboardInputs; bool mHandleKeyboardInputs{true};
bool mHandleMouseInputs; bool mHandleMouseInputs{true};
bool mIgnoreImGuiChild; bool mIgnoreImGuiChild{false};
bool mShowWhitespaces; bool mShowWhitespaces{true};
bool mShowShortTabGlyphs; bool mShowShortTabGlyphs;
Palette mPaletteBase; Palette mPaletteBase;
@ -404,6 +408,6 @@ private:
Coordinates mInteractiveStart, mInteractiveEnd; Coordinates mInteractiveStart, mInteractiveEnd;
std::string mLineBuffer; std::string mLineBuffer;
uint64_t mStartTime; uint64_t mStartTime;
float mLastClick; float mLastClick;
int mExecutionMarker{-1};
}; };

View file

@ -5,8 +5,8 @@
unsigned long BaseNode::s_nextId = 1; unsigned long BaseNode::s_nextId = 1;
BaseNode::BaseNode(const std::string &title, IStoryProject &proj) BaseNode::BaseNode(const std::string &title, IStoryManager &proj)
: m_project(proj) : m_story(proj)
{ {
// m_id = UUID().String(); // m_id = UUID().String();

View file

@ -7,7 +7,7 @@
#include <string> #include <string>
#include "json.hpp" #include "json.hpp"
#include "i_story_project.h" #include "i_story_manager.h"
#include <imgui_node_editor.h> #include <imgui_node_editor.h>
namespace ed = ax::NodeEditor; namespace ed = ax::NodeEditor;
@ -101,7 +101,7 @@ public:
float y; float y;
}; };
BaseNode(const std::string &title, IStoryProject &proj); BaseNode(const std::string &title, IStoryManager &proj);
virtual void Draw() = 0; virtual void Draw() = 0;
@ -223,7 +223,7 @@ public:
void DeleteOutput(); void DeleteOutput();
private: private:
IStoryProject &m_project; IStoryManager &m_story;
std::unique_ptr<Node> m_node; std::unique_ptr<Node> m_node;

View file

@ -18,8 +18,8 @@ void CodeEditor::Initialize()
// "breakpoint" markers // "breakpoint" markers
m_breakPoints.insert(42); // m_breakPoints.insert(42);
mEditor.SetBreakpoints(m_breakPoints); // mEditor.SetBreakpoints(m_breakPoints);
} }

View file

@ -15,6 +15,11 @@ public:
void SetScript(const std::string &txt); void SetScript(const std::string &txt);
void ClearErrors(); void ClearErrors();
void AddError(int line, const std::string &text); void AddError(int line, const std::string &text);
void HighlightLine(int line)
{
mEditor.SetExecutionMarker(line);
}
private: private:
TextEditor mEditor; TextEditor mEditor;
TextEditor::Breakpoints m_breakPoints; TextEditor::Breakpoints m_breakPoints;

View file

@ -2,9 +2,9 @@
#include "gui.h" #include "gui.h"
#include "IconsMaterialDesignIcons.h" #include "IconsMaterialDesignIcons.h"
EmulatorWindow::EmulatorWindow(IStoryProject &proj) EmulatorWindow::EmulatorWindow(IStoryManager &proj)
: WindowBase("Emulator") : WindowBase("Emulator")
, m_project(proj) , m_story(proj)
{ {
} }
@ -31,8 +31,17 @@ void EmulatorWindow::Draw()
// ImGui::Image((void*)(intptr_t)my_image_texture, ImVec2(313, 367)); // ImGui::Image((void*)(intptr_t)my_image_texture, ImVec2(313, 367));
// float sz = ImGui::GetTextLineHeight(); // float sz = ImGui::GetTextLineHeight();
ImVec2 p = ImGui::GetCursorScreenPos(); ImVec2 p = ImGui::GetCursorScreenPos();
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 320, p.y + 240), ImGui::GetColorU32(ImVec4(1.0, 1.0, 1.0, 1.0)));
if (m_image.Valid())
{
ImGui::Image(m_image.texture, ImVec2(320, 240));
}
else
{
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 320, p.y + 240), ImGui::GetColorU32(ImVec4(1.0, 1.0, 1.0, 1.0)));
}
ImGui::SetCursorScreenPos(ImVec2(p.x, p.y + 244)); ImGui::SetCursorScreenPos(ImVec2(p.x, p.y + 244));
@ -42,13 +51,25 @@ void EmulatorWindow::Draw()
ImGui::PushFont(ImGui::GetFont()); ImGui::PushFont(ImGui::GetFont());
ImGui::Button(ICON_MDI_PLAY_CIRCLE_OUTLINE, ImVec2(50, 50)); if (ImGui::Button(ICON_MDI_PLAY_CIRCLE_OUTLINE, ImVec2(50, 50)))
{
m_story.Play();
}
ImGui::SameLine(); ImGui::SameLine();
ImGui::Button(ICON_MDI_STOP_CIRCLE_OUTLINE, ImVec2(50, 50)); if (ImGui::Button(ICON_MDI_STOP_CIRCLE_OUTLINE, ImVec2(50, 50)))
{
m_story.Pause();
}
ImGui::SameLine(); ImGui::SameLine();
ImGui::Button(ICON_MDI_ARROW_LEFT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50)); if (ImGui::Button(ICON_MDI_ARROW_LEFT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50)))
{
m_story.Previous();
}
ImGui::SameLine(); ImGui::SameLine();
ImGui::Button(ICON_MDI_ARROW_RIGHT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50)); if (ImGui::Button(ICON_MDI_ARROW_RIGHT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50)))
{
m_story.Next();
}
ImGui::GetFont()->Scale = old_size; ImGui::GetFont()->Scale = old_size;
ImGui::PopFont(); ImGui::PopFont();
@ -57,9 +78,20 @@ void EmulatorWindow::Draw()
if (ImGui::Button("Build")) if (ImGui::Button("Build"))
{ {
m_project.Build(); m_story.Build();
} }
ImGui::SameLine(); ImGui::SameLine();
WindowBase::EndDraw(); WindowBase::EndDraw();
} }
void EmulatorWindow::ClearImage()
{
m_image.Clear();
}
void EmulatorWindow::SetImage(const std::string &image)
{
m_image.name = image;
m_image.Load(m_story.BuildFullAssetsPath(image));
}

View file

@ -1,17 +1,22 @@
#pragma once #pragma once
#include "window_base.h" #include "window_base.h"
#include "i_story_project.h" #include "i_story_manager.h"
#include "gui.h"
class EmulatorWindow : public WindowBase class EmulatorWindow : public WindowBase
{ {
public: public:
EmulatorWindow(IStoryProject &proj); EmulatorWindow(IStoryManager &proj);
void Initialize(); void Initialize();
virtual void Draw() override; virtual void Draw() override;
void ClearImage();
void SetImage(const std::string &image);
private: private:
IStoryProject &m_project; IStoryManager &m_story;
Gui::Image m_image;
}; };

View file

@ -22,6 +22,7 @@ your use of the corresponding standard functions.
#include "IconsMaterialDesignIcons.h" #include "IconsMaterialDesignIcons.h"
#include "IconsFontAwesome5_c.h" #include "IconsFontAwesome5_c.h"
#include "qoi.h"
static void glfw_error_callback(int error, const char* description) static void glfw_error_callback(int error, const char* description)
{ {
@ -34,24 +35,59 @@ static SDL_Renderer* renderer{nullptr};
#include "stb_image.h" #include "stb_image.h"
static std::string GetFileExtension(const std::string &fileName)
{
if(fileName.find_last_of(".") != std::string::npos)
return fileName.substr(fileName.find_last_of(".")+1);
return "";
}
// Simple helper function to load an image into a OpenGL texture with common settings // Simple helper function to load an image into a OpenGL texture with common settings
bool LoadTextureFromFile(const char* filename, Gui::Image &img) bool LoadTextureFromFile(const char* filename, Gui::Image &img)
{ {
int channels;
unsigned char* data = stbi_load(filename, &img.w, &img.h, &channels, 0);
if (data == nullptr) {
fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason()); std::string ext = GetFileExtension(filename);
return false;
SDL_Surface* surface = nullptr;
if (ext == "qoi")
{
qoi_desc desc;
void *pixels = qoi_read(filename, &desc, 0);
unsigned int channels = desc.channels;
img.w = desc.width;
img.h = desc.height;
surface = SDL_CreateRGBSurfaceFrom((void*)pixels, img.w, img.h, channels * 8, channels * img.w,
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
if (pixels != NULL)
{
free(pixels);
}
} }
else
{
unsigned char* data = nullptr;
int channels;
data = stbi_load(filename, &img.w, &img.h, &channels, 0);
// SDL3 if (data == nullptr) {
// SDL_Surface* surface = SDL_CreateSurfaceFrom((void*)data, img.w, img.h, 4 * img.w, SDL_PIXELFORMAT_RGBA8888); fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason());
return false;
}
// SDL2
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*)data, img.w, img.h, channels * 8, channels * img.w, // SDL3
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); // SDL_Surface* surface = SDL_CreateSurfaceFrom((void*)data, img.w, img.h, 4 * img.w, SDL_PIXELFORMAT_RGBA8888);
// SDL2
surface = SDL_CreateRGBSurfaceFrom((void*)data, img.w, img.h, channels * 8, channels * img.w,
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
stbi_image_free(data);
}
if (surface == nullptr) { if (surface == nullptr) {
@ -67,7 +103,7 @@ bool LoadTextureFromFile(const char* filename, Gui::Image &img)
// SDL_DestroySurface(surface); // SDL3 // SDL_DestroySurface(surface); // SDL3
SDL_FreeSurface(surface); // SDL2 SDL_FreeSurface(surface); // SDL2
stbi_image_free(data);
return true; return true;
} }

View file

@ -1,5 +1,5 @@
#ifndef I_STORY_PROJECT_H #ifndef I_STORY_MANAGER_H
#define I_STORY_PROJECT_H #define I_STORY_MANAGER_H
#include <string> #include <string>
@ -10,10 +10,10 @@
#include "resource.h" #include "resource.h"
#include "connection.h" #include "connection.h"
class IStoryProject class IStoryManager
{ {
public: public:
virtual ~IStoryProject() {} virtual ~IStoryManager() {}
virtual void Log(const std::string &txt, bool critical = false) = 0; virtual void Log(const std::string &txt, bool critical = false) = 0;
virtual void PlaySoundFile(const std::string &fileName) = 0; virtual void PlaySoundFile(const std::string &fileName) = 0;
@ -31,7 +31,12 @@ public:
virtual void Build() = 0; virtual void Build() = 0;
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(unsigned long nodeId) = 0; virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(unsigned long nodeId) = 0;
virtual std::string GetNodeEntryLabel(unsigned long nodeId) = 0; virtual std::string GetNodeEntryLabel(unsigned long nodeId) = 0;
virtual void Play() = 0;
virtual void Pause() = 0;
virtual void Next() = 0;
virtual void Previous() = 0;
}; };
#endif // I_STORY_PROJECT_H #endif // I_STORY_MANAGER_H

View file

@ -4,6 +4,7 @@
#include "platform_folders.h" #include "platform_folders.h"
#include "uuid.h" #include "uuid.h"
#include "media_converter.h"
#ifdef USE_WINDOWS_OS #ifdef USE_WINDOWS_OS
#include <winsock2.h> #include <winsock2.h>
@ -22,9 +23,24 @@ MainWindow::MainWindow()
: m_emulatorWindow(*this) : m_emulatorWindow(*this)
, m_resourcesWindow(*this) , m_resourcesWindow(*this)
, m_nodeEditorWindow(*this) , m_nodeEditorWindow(*this)
, m_player(*this)
{ {
m_project.Clear();
// VM Initialize
m_chip32_ctx.stack_size = 512;
m_chip32_ctx.rom.mem = m_rom_data;
m_chip32_ctx.rom.addr = 0;
m_chip32_ctx.rom.size = sizeof(m_rom_data);
m_chip32_ctx.ram.mem = m_ram_data;
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
m_chip32_ctx.ram.size = sizeof(m_ram_data);
Callback<uint8_t(chip32_ctx_t *, uint8_t)>::func = std::bind(&MainWindow::Syscall, this, std::placeholders::_1, std::placeholders::_2);
m_chip32_ctx.syscall = static_cast<syscall_t>(Callback<uint8_t(chip32_ctx_t *, uint8_t)>::callback);
m_story.Clear();
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@ -32,6 +48,194 @@ MainWindow::~MainWindow()
} }
std::string MainWindow::GetFileNameFromMemory(uint32_t addr)
{
char strBuf[100];
bool isRam = addr & 0x80000000;
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing
if (isRam) {
strcpy(&strBuf[0], (const char *)&m_chip32_ctx.ram.mem[addr]);
} else {
strcpy(&strBuf[0], (const char *)&m_chip32_ctx.rom.mem[addr]);
}
return strBuf;
}
/*
void MainWindow::EventFinished(uint32_t replyEvent)
{
if (m_dbg.run_result == VM_WAIT_EVENT)
{
// Result event is in R0
m_chip32_ctx.registers[R0] = replyEvent;
m_dbg.run_result = VM_OK;
if (m_dbg.free_run)
{
m_runTimer->start(100);
}
else
{
stepInstruction();
}
}
}*/
void MainWindow::Play()
{
if (m_dbg.run_result == VM_FINISHED)
{
Build();
if (m_dbg.run_result == VM_READY)
{
m_dbg.free_run = true;
m_dbg.run_result = VM_OK; // actually starts the execution
}
}
}
void MainWindow::Pause()
{
}
void MainWindow::Next()
{
}
void MainWindow::Previous()
{
}
void MainWindow::EndOfAudio()
{
Log("End of audio track");
m_eventQueue.push({VmEventType::EvAudioFinished});
}
void MainWindow::StepInstruction()
{
m_dbg.run_result = chip32_step(&m_chip32_ctx);
UpdateVmView();
}
void MainWindow::ProcessStory()
{
if (m_dbg.run_result == VM_FINISHED)
return;
if (m_dbg.run_result == VM_READY)
return;
// 1. First, check events
if (m_dbg.run_result == VM_WAIT_EVENT)
{
VmEvent event;
if (m_eventQueue.try_pop(event))
{
if (event.type == VmEventType::EvStep)
{
m_dbg.run_result = VM_OK;
}
else if (event.type == VmEventType::EvOkButton)
{
m_chip32_ctx.registers[R0] = 0x01;
m_dbg.run_result = VM_OK;
}
else if (event.type == VmEventType::EvLeftButton)
{
m_chip32_ctx.registers[R0] = 0x02;
m_dbg.run_result = VM_OK;
}
else if (event.type == VmEventType::EvRightButton)
{
m_chip32_ctx.registers[R0] = 0x04;
m_dbg.run_result = VM_OK;
}
else if (event.type == VmEventType::EvAudioFinished)
{
m_chip32_ctx.registers[R0] = 0x08;
m_dbg.run_result = VM_OK;
}
}
}
if (m_dbg.run_result == VM_OK)
{
if (m_dbg.m_breakpoints.contains(m_dbg.line + 1))
{
Log("Breakpoint on line: " + std::to_string(m_dbg.line + 1));
m_dbg.free_run = false;
return;
}
StepInstruction();
}
if (m_dbg.run_result == VM_FINISHED)
{
m_dbg.free_run = false;
}
// In this case, we wait for single step debugger
if ((m_dbg.run_result == VM_OK) && !m_dbg.free_run)
{
m_dbg.run_result = VM_WAIT_EVENT;
}
}
uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
{
uint8_t retCode = SYSCALL_RET_OK;
Log("SYSCALL: " + std::to_string(code));
// Media
if (code == 1) // Execute media
{
if (m_chip32_ctx.registers[R0] != 0)
{
// image file name address is in R0
std::string imageFile = m_story.BuildFullAssetsPath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
Log("Image: " + imageFile);
m_emulatorWindow.SetImage(imageFile);
}
else
{
m_emulatorWindow.ClearImage();
}
if (m_chip32_ctx.registers[R1] != 0)
{
// sound file name address is in R1
std::string soundFile = m_story.BuildFullAssetsPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
Log(", Sound: " + soundFile);
m_player.Play(soundFile);
}
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
}
// WAIT EVENT bits:
// 0: block
// 1: OK button
// 2: home button
// 3: pause button
// 4: rotary left
// 5: rotary right
else if (code == 2) // Wait for event
{
// Event mask is located in R0
// optional timeout is located in R1
// if timeout is set to zero, wait for infinite and beyond
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
}
return retCode;
}
void MainWindow::DrawStatusBar() void MainWindow::DrawStatusBar()
{ {
float statusWindowHeight = ImGui::GetFrameHeight() * 1.4f; float statusWindowHeight = ImGui::GetFrameHeight() * 1.4f;
@ -221,11 +425,11 @@ void MainWindow::OpenProjectDialog()
std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
m_project.Initialize(filePathName); m_story.Initialize(filePathName);
nlohmann::json model; nlohmann::json model;
if (m_project.Load(filePathName, model, m_resources)) if (m_story.Load(filePathName, model, m_resources))
{ {
Log("Open project success"); Log("Open project success");
m_nodeEditorWindow.Load(model); m_nodeEditorWindow.Load(model);
@ -247,7 +451,7 @@ void MainWindow::OpenProjectDialog()
void MainWindow::EnableProject() void MainWindow::EnableProject()
{ {
auto proj = m_project.GetProjectFilePath(); auto proj = m_story.GetProjectFilePath();
// Add to recent if not exists // Add to recent if not exists
if (std::find(m_recentProjects.begin(), m_recentProjects.end(), proj) != m_recentProjects.end()) if (std::find(m_recentProjects.begin(), m_recentProjects.end(), proj) != m_recentProjects.end())
{ {
@ -426,21 +630,21 @@ void MainWindow::NewProjectPopup()
if (valid) if (valid)
{ {
m_project.Initialize(std::filesystem::path(projdir) / "project.json"); m_story.Initialize(std::filesystem::path(projdir) / "project.json");
if (display_item_current_idx == 0) if (display_item_current_idx == 0)
{ {
m_project.SetDisplayFormat(320, 240); m_story.SetDisplayFormat(320, 240);
} }
else else
{ {
m_project.SetDisplayFormat(640, 480); m_story.SetDisplayFormat(640, 480);
} }
m_project.SetImageFormat(GetImageFormat(image_item_current_idx)); m_story.SetImageFormat(GetImageFormat(image_item_current_idx));
m_project.SetSoundFormat(GetSoundFormat(sound_item_current_idx)); m_story.SetSoundFormat(GetSoundFormat(sound_item_current_idx));
m_project.SetName(project_name); m_story.SetName(project_name);
m_project.SetUuid(UUID().String()); m_story.SetUuid(UUID().String());
SaveProject(); SaveProject();
EnableProject(); EnableProject();
@ -466,12 +670,12 @@ void MainWindow::SaveProject()
{ {
nlohmann::json model; nlohmann::json model;
m_nodeEditorWindow.Save(model); m_nodeEditorWindow.Save(model);
m_project.Save(model, m_resources); m_story.Save(model, m_resources);
} }
void MainWindow::CloseProject() void MainWindow::CloseProject()
{ {
m_project.Clear(); m_story.Clear();
m_nodeEditorWindow.Clear(); m_nodeEditorWindow.Clear();
// m_model.Clear(); // m_model.Clear();
@ -505,6 +709,10 @@ void MainWindow::Loop()
DrawMainMenuBar(); DrawMainMenuBar();
// DrawStatusBar(); // DrawStatusBar();
ProcessStory();
// ------------ Draw all windows // ------------ Draw all windows
m_consoleWindow.Draw(); m_consoleWindow.Draw();
m_emulatorWindow.Draw(); m_emulatorWindow.Draw();
@ -543,12 +751,12 @@ void MainWindow::Log(const std::string &txt, bool critical)
void MainWindow::PlaySoundFile(const std::string &fileName) void MainWindow::PlaySoundFile(const std::string &fileName)
{ {
Log("Play sound file: " + fileName); Log("Play sound file: " + fileName);
m_project.PlaySoundFile(fileName); m_player.Play(fileName);
} }
std::string MainWindow::BuildFullAssetsPath(const std::string &fileName) const std::string MainWindow::BuildFullAssetsPath(const std::string &fileName) const
{ {
return m_project.BuildFullAssetsPath(fileName); return m_story.BuildFullAssetsPath(fileName);
} }
std::pair<FilterIterator, FilterIterator> MainWindow::Images() std::pair<FilterIterator, FilterIterator> MainWindow::Images()
@ -647,9 +855,9 @@ void MainWindow::GenerateBinary()
// FIXME // FIXME
// m_ramView->SetMemory(m_ram_data, sizeof(m_ram_data)); // m_ramView->SetMemory(m_ram_data, sizeof(m_ram_data));
// m_romView->SetMemory(m_rom_data, m_program.size()); // m_romView->SetMemory(m_rom_data, m_program.size());
m_project.SaveStory(m_program); m_story.SaveStory(m_program);
chip32_initialize(&m_chip32_ctx); chip32_initialize(&m_chip32_ctx);
m_dbg.run_result = VM_OK; m_dbg.run_result = VM_READY;
UpdateVmView(); UpdateVmView();
// DebugContext::DumpCodeAssembler(m_assembler); // DebugContext::DumpCodeAssembler(m_assembler);
} }
@ -672,14 +880,70 @@ void MainWindow::UpdateVmView()
{ {
// FIXME // FIXME
// m_vmDock->updateRegistersView(m_chip32_ctx); // m_vmDock->updateRegistersView(m_chip32_ctx);
// highlightNextLine();
// Highlight next line in the test editor
uint32_t pcVal = m_chip32_ctx.registers[PC];
// On recherche quelle est la ligne qui possède une instruction à cette adresse
std::vector<Chip32::Instr>::const_iterator ptr = m_assembler.Begin();
for (; ptr != m_assembler.End(); ++ptr)
{
if ((ptr->addr == pcVal) && ptr->isRomCode())
{
break;
}
}
if (ptr != m_assembler.End())
{
m_dbg.line = (ptr->line - 1);
m_editor.HighlightLine(m_dbg.line);
}
else
{
// Not found
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::ConvertResources() void MainWindow::ConvertResources()
{ {
auto [b, e] = m_resources.Items();
for (auto it = b; it != e; ++it)
{
std::string inputfile = m_story.BuildFullAssetsPath((*it)->file.c_str());
std::string outputfile = std::filesystem::path(m_story.AssetsPath() / StoryProject::RemoveFileExtension((*it)->file)).string();
int retCode = 0;
if ((*it)->format == "PNG")
{
outputfile += ".qoi"; // FIXME: prendre la congif en cours désirée
retCode = MediaConverter::ImageToQoi(inputfile, outputfile);
}
else if ((*it)->format == "MP3")
{
outputfile += ".wav"; // FIXME: prendre la congif en cours désirée
retCode = MediaConverter::Mp3ToWav(inputfile, outputfile);
}
else
{
Log("Skipped: " + inputfile + ", unknown format" + outputfile, true);
}
if (retCode < 0)
{
Log("Failed to convert media file " + inputfile + ", error code: " + std::to_string(retCode) + " to: " + outputfile, true);
}
else if (retCode == 0)
{
Log("Convertered file: " + inputfile);
}
}
} }
void MainWindow::SaveParams() void MainWindow::SaveParams()

View file

@ -1,6 +1,9 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <functional>
#include "gui.h" #include "gui.h"
#include "console_window.h" #include "console_window.h"
#include "code_editor.h" #include "code_editor.h"
@ -13,7 +16,8 @@
#include "chip32_assembler.h" #include "chip32_assembler.h"
#include "chip32_vm.h" #include "chip32_vm.h"
#include "story_project.h" #include "story_project.h"
#include "i_story_project.h" #include "i_story_manager.h"
#include "thread_safe_queue.h"
struct DebugContext struct DebugContext
{ {
@ -25,6 +29,10 @@ struct DebugContext
std::set<int> m_breakpoints; std::set<int> m_breakpoints;
void Stop() {
run_result = VM_FINISHED;
}
static void DumpCodeAssembler(Chip32::Assembler & assembler) { static void DumpCodeAssembler(Chip32::Assembler & assembler) {
for (std::vector<Chip32::Instr>::const_iterator iter = assembler.Begin(); for (std::vector<Chip32::Instr>::const_iterator iter = assembler.Begin();
@ -51,7 +59,7 @@ struct DebugContext
} }
}; };
#include <functional>
template <typename T> template <typename T>
struct Callback; struct Callback;
@ -70,7 +78,7 @@ std::function<Ret(Params...)> Callback<Ret(Params...)>::func;
class MainWindow : public IStoryProject class MainWindow : public IStoryManager, public IAudioEvent
{ {
public: public:
MainWindow(); MainWindow();
@ -80,8 +88,9 @@ public:
void Loop(); void Loop();
private: private:
enum VmEventType { EvNoEvent, EvStep, EvOkButton, EvLeftButton, EvRightButton, EvAudioFinished};
StoryProject m_project; StoryProject m_story;
// VM // VM
uint8_t m_rom_data[16*1024]; uint8_t m_rom_data[16*1024];
@ -95,6 +104,7 @@ private:
DebugContext m_dbg; DebugContext m_dbg;
std::string m_currentCode; std::string m_currentCode;
std::vector<std::string> m_recentProjects; std::vector<std::string> m_recentProjects;
ResourceManager m_resources; ResourceManager m_resources;
@ -110,8 +120,17 @@ private:
PropertiesWindow m_PropertiesWindow; PropertiesWindow m_PropertiesWindow;
AudioPlayer m_player;
// From IStoryProject (proxy to StoryProject class) struct VmEvent
{
VmEventType type;
};
ThreadSafeQueue<VmEvent> m_eventQueue;
// From IStoryManager (proxy to StoryProject class)
virtual void Log(const std::string &txt, bool critical = false) override; virtual void Log(const std::string &txt, bool critical = false) override;
virtual void PlaySoundFile(const std::string &fileName) override;; virtual void PlaySoundFile(const std::string &fileName) override;;
virtual std::string BuildFullAssetsPath(const std::string &fileName) const override; virtual std::string BuildFullAssetsPath(const std::string &fileName) const override;
@ -125,6 +144,13 @@ private:
virtual void Build() override; virtual void Build() override;
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(unsigned long nodeId) override; virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(unsigned long nodeId) override;
virtual std::string GetNodeEntryLabel(unsigned long nodeId) override; virtual std::string GetNodeEntryLabel(unsigned long nodeId) override;
virtual void Play() override;
virtual void Pause() override;
virtual void Next() override;
virtual void Previous() override;
// From IAudioEvent
virtual void EndOfAudio() override;
void SaveParams(); void SaveParams();
void LoadParams(); void LoadParams();
@ -143,6 +169,10 @@ private:
void ConvertResources(); void ConvertResources();
void GenerateBinary(); void GenerateBinary();
void UpdateVmView(); void UpdateVmView();
uint8_t Syscall(chip32_ctx_t *ctx, uint8_t code);
std::string GetFileNameFromMemory(uint32_t addr);
void ProcessStory();
void StepInstruction();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View file

@ -4,9 +4,9 @@ namespace ed = ax::NodeEditor;
#include "IconsMaterialDesignIcons.h" #include "IconsMaterialDesignIcons.h"
#include "story_project.h" #include "story_project.h"
MediaNode::MediaNode(const std::string &title, IStoryProject &proj) MediaNode::MediaNode(const std::string &title, IStoryManager &proj)
: BaseNode(title, proj) : BaseNode(title, proj)
, m_project(proj) , m_story(proj)
{ {
Gui::LoadRawImage("fairy.png", m_image); Gui::LoadRawImage("fairy.png", m_image);
@ -87,8 +87,6 @@ void MediaNode::Draw()
DrawPins(); DrawPins();
BaseNode::FrameEnd(); BaseNode::FrameEnd();
} }
@ -144,7 +142,7 @@ void MediaNode::DrawProperties()
if (ImGui::Button(m_buttonUniqueName.c_str())) if (ImGui::Button(m_buttonUniqueName.c_str()))
{ {
m_project.PlaySoundFile(m_soundPath); m_story.PlaySoundFile(m_soundPath);
} }
ImGui::SameLine(); ImGui::SameLine();
@ -163,7 +161,7 @@ void MediaNode::DrawProperties()
if (ImGui::BeginPopup("popup_button")) { if (ImGui::BeginPopup("popup_button")) {
ImGui::SeparatorText(isImage ? "Images" : "Sounds"); ImGui::SeparatorText(isImage ? "Images" : "Sounds");
auto [filtreDebut, filtreFin] = isImage ? m_project.Images() : m_project.Sounds(); auto [filtreDebut, filtreFin] = isImage ? m_story.Images() : m_story.Sounds();
int n = 0; int n = 0;
for (auto it = filtreDebut; it != filtreFin; ++it, n++) for (auto it = filtreDebut; it != filtreFin; ++it, n++)
{ {
@ -188,13 +186,13 @@ void MediaNode::DrawProperties()
void MediaNode::SetImage(const std::string &f) void MediaNode::SetImage(const std::string &f)
{ {
m_image.name = f; m_image.name = f;
m_image.Load(m_project.BuildFullAssetsPath(f)); m_image.Load(m_story.BuildFullAssetsPath(f));
} }
void MediaNode::SetSound(const std::string &f) void MediaNode::SetSound(const std::string &f)
{ {
m_soundName = f; m_soundName = f;
m_soundPath = m_project.BuildFullAssetsPath(m_soundName); m_soundPath = m_story.BuildFullAssetsPath(m_soundName);
} }
@ -238,14 +236,14 @@ std::string MediaNode::GenerateConstants()
<< " DC32, " << " DC32, "
<< nb_out_conns << ", "; << nb_out_conns << ", ";
std::list<std::shared_ptr<Connection>> conns = m_project.GetNodeConnections(GetId()); std::list<std::shared_ptr<Connection>> conns = m_story.GetNodeConnections(GetId());
int i = 0; int i = 0;
for (auto & c : conns) for (auto & c : conns)
{ {
std::stringstream ssChoice; std::stringstream ssChoice;
// On va chercher le label d'entrée du noeud connecté à l'autre bout // On va chercher le label d'entrée du noeud connecté à l'autre bout
ss << m_project.GetNodeEntryLabel(c->inNodeId); ss << m_story.GetNodeEntryLabel(c->inNodeId);
if (i < (nb_out_conns - 1)) if (i < (nb_out_conns - 1))
{ {
ss << ", "; ss << ", ";
@ -311,7 +309,7 @@ std::string MediaNode::Build()
} }
else if (nb_out_conns == 1) // it is a transition node else if (nb_out_conns == 1) // it is a transition node
{ {
std::list<std::shared_ptr<Connection>> conns = m_project.GetNodeConnections(GetId()); std::list<std::shared_ptr<Connection>> conns = m_story.GetNodeConnections(GetId());
for (auto c : conns) for (auto c : conns)
@ -320,7 +318,7 @@ std::string MediaNode::Build()
{ {
// On place dans R0 le prochain noeud à exécuter en cas de OK // On place dans R0 le prochain noeud à exécuter en cas de OK
ss << "lcons r0, " ss << "lcons r0, "
<< m_project.GetNodeEntryLabel(c->inNodeId) << "\n" << m_story.GetNodeEntryLabel(c->inNodeId) << "\n"
<< "ret\n"; << "ret\n";
} }
} }

View file

@ -6,7 +6,7 @@
#include <set> #include <set>
#include "base_node.h" #include "base_node.h"
#include "i_story_project.h" #include "i_story_manager.h"
#include "gui.h" #include "gui.h"
#include <imgui_node_editor.h> #include <imgui_node_editor.h>
@ -14,7 +14,7 @@
class MediaNode : public BaseNode class MediaNode : public BaseNode
{ {
public: public:
MediaNode(const std::string &title, IStoryProject &proj); MediaNode(const std::string &title, IStoryManager &proj);
void Draw() override; void Draw() override;
@ -25,7 +25,7 @@ public:
virtual std::string GetEntryLabel() override; virtual std::string GetEntryLabel() override;
virtual std::string GenerateConstants() override; virtual std::string GenerateConstants() override;
private: private:
IStoryProject &m_project; IStoryManager &m_story;
Gui::Image m_image; Gui::Image m_image;
std::string m_soundName; std::string m_soundName;
std::string m_soundPath; std::string m_soundPath;

View file

@ -10,9 +10,9 @@
#include "gui.h" #include "gui.h"
NodeEditorWindow::NodeEditorWindow(IStoryProject &proj) NodeEditorWindow::NodeEditorWindow(IStoryManager &proj)
: WindowBase("Node editor") : WindowBase("Node editor")
, m_project(proj) , m_story(proj)
{ {
registerNode<MediaNode>("media-node"); registerNode<MediaNode>("media-node");
@ -47,7 +47,7 @@ void NodeEditorWindow::LoadNode(const nlohmann::json &nodeJson)
nlohmann::json internalDataJson = nodeJson["internal-data"]; nlohmann::json internalDataJson = nodeJson["internal-data"];
std::string type = nodeJson["type"].get<std::string>(); std::string type = nodeJson["type"].get<std::string>();
auto n = createNode(type, "", m_project); auto n = createNode(type, "", m_story);
if (n) if (n)
{ {
n->SetType(type); // FIXME: set type in createNode factory? n->SetType(type); // FIXME: set type in createNode factory?
@ -225,7 +225,7 @@ uint32_t NodeEditorWindow::FindFirstNode() const
if (!foundConnection) if (!foundConnection)
{ {
id = n->GetId(); id = n->GetId();
m_project.Log("First node is: " + std::to_string(id)); m_story.Log("First node is: " + std::to_string(id));
break; break;
} }
} }

View file

@ -9,7 +9,7 @@
#include <imgui_node_editor.h> #include <imgui_node_editor.h>
#include "base_node.h" #include "base_node.h"
#include "window_base.h" #include "window_base.h"
#include "i_story_project.h" #include "i_story_manager.h"
#include "story_project.h" #include "story_project.h"
#include "json.hpp" #include "json.hpp"
@ -48,7 +48,7 @@ public:
std::shared_ptr<Connection> model; std::shared_ptr<Connection> model;
}; };
NodeEditorWindow(IStoryProject &proj); NodeEditorWindow(IStoryManager &proj);
~NodeEditorWindow(); ~NodeEditorWindow();
virtual void Draw() override; virtual void Draw() override;
@ -63,7 +63,7 @@ public:
std::shared_ptr<BaseNode> GetSelectedNode(); std::shared_ptr<BaseNode> GetSelectedNode();
private: private:
IStoryProject &m_project; IStoryManager &m_story;
ed::EditorContext* m_context = nullptr; ed::EditorContext* m_context = nullptr;
@ -90,12 +90,12 @@ private:
template<class NodeType> template<class NodeType>
struct Factory { struct Factory {
static std::shared_ptr<BaseNode> create_func(const std::string &title, IStoryProject &proj) { static std::shared_ptr<BaseNode> create_func(const std::string &title, IStoryManager &proj) {
return std::make_shared<NodeType>(title, proj); return std::make_shared<NodeType>(title, proj);
} }
}; };
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &title, IStoryProject &proj); typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &title, IStoryManager &proj);
typedef std::map<std::string, GenericCreator> Registry; typedef std::map<std::string, GenericCreator> Registry;
Registry m_registry; Registry m_registry;
@ -104,7 +104,7 @@ private:
m_registry.insert(typename Registry::value_type(key, Factory<Derived>::create_func)); m_registry.insert(typename Registry::value_type(key, Factory<Derived>::create_func));
} }
std::shared_ptr<BaseNode> createNode(const std::string& key, const std::string &title, IStoryProject &proj) { std::shared_ptr<BaseNode> createNode(const std::string& key, const std::string &title, IStoryManager &proj) {
typename Registry::const_iterator i = m_registry.find(key); typename Registry::const_iterator i = m_registry.find(key);
if (i == m_registry.end()) { if (i == m_registry.end()) {
throw std::invalid_argument(std::string(__PRETTY_FUNCTION__) + throw std::invalid_argument(std::string(__PRETTY_FUNCTION__) +

View file

@ -8,9 +8,9 @@
//static thread_pool pool; //static thread_pool pool;
ResourcesWindow::ResourcesWindow(IStoryProject &project) ResourcesWindow::ResourcesWindow(IStoryManager &project)
: WindowBase("Resources") : WindowBase("Resources")
, m_project(project) , m_story(project)
{ {
} }
@ -44,7 +44,7 @@ void ResourcesWindow::ChooseFile()
std::filesystem::path p(filePathName); std::filesystem::path p(filePathName);
std::filesystem::path p2 = m_project.BuildFullAssetsPath( p.filename()); std::filesystem::path p2 = m_story.BuildFullAssetsPath( p.filename());
std::filesystem::copy(p, p2, std::filesystem::copy_options::overwrite_existing); std::filesystem::copy(p, p2, std::filesystem::copy_options::overwrite_existing);
auto res = std::make_shared<Resource>(); auto res = std::make_shared<Resource>();
@ -56,7 +56,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_project.AddResource(res); m_story.AddResource(res);
} }
// close // close
@ -104,7 +104,7 @@ void ResourcesWindow::Draw()
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
auto [b, e] = m_project.Resources(); auto [b, e] = m_story.Resources();
int id = 1000; int id = 1000;
for (auto it = b; it != e; ++it) for (auto it = b; it != e; ++it)
@ -152,7 +152,7 @@ void ResourcesWindow::Draw()
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::SmallButton("Delete")) if (ImGui::SmallButton("Delete"))
{ {
m_project.DeleteResource(it); m_story.DeleteResource(it);
quitLoop = true; quitLoop = true;
} }
ImGui::PopID(); ImGui::PopID();

View file

@ -3,18 +3,18 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include "i_story_project.h" #include "i_story_manager.h"
#include "window_base.h" #include "window_base.h"
class ResourcesWindow : public WindowBase class ResourcesWindow : public WindowBase
{ {
public: public:
ResourcesWindow(IStoryProject &project); ResourcesWindow(IStoryManager &project);
~ResourcesWindow(); ~ResourcesWindow();
virtual void Draw() override; virtual void Draw() override;
private: private:
IStoryProject &m_project; IStoryManager &m_story;
bool m_showImportDialog{false}; bool m_showImportDialog{false};
bool m_soundFile{false}; bool m_soundFile{false};