mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
better event, better audio, add QOI to emulator, emulator is executed
This commit is contained in:
parent
a730ea4e0b
commit
41b52e3532
30 changed files with 690 additions and 216 deletions
|
|
@ -130,6 +130,7 @@ typedef enum
|
|||
|
||||
typedef enum
|
||||
{
|
||||
VM_READY, // VM Ready to be started
|
||||
VM_FINISHED, // execution completed (i.e. got halt instruction)
|
||||
VM_SKIPED, // skipped instruction
|
||||
VM_WAIT_EVENT, // execution paused since we hit the maximum instructions
|
||||
|
|
|
|||
|
|
@ -13,10 +13,14 @@ the simple_mixing example for how best to do this.
|
|||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
|
||||
#include <functional>
|
||||
#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)
|
||||
{
|
||||
|
|
@ -28,20 +32,23 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
|
|||
ma_uint64 framesRead;
|
||||
ma_result result = ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount, &framesRead);
|
||||
|
||||
if (framesRead < frameCount) {
|
||||
// Reached the end.
|
||||
ma_event_signal(&g_stopEvent);
|
||||
|
||||
if (result == MA_AT_END) {
|
||||
g_audioQueue.push({"end", ""});
|
||||
}
|
||||
// if (framesRead < frameCount) {
|
||||
// // Reached the end.
|
||||
// ma_event_signal(&g_stopEvent);
|
||||
// }
|
||||
|
||||
(void)pInput;
|
||||
}
|
||||
|
||||
int miniaudio_play(const char* filename)
|
||||
|
||||
static int miniaudio_play(const char* filename)
|
||||
{
|
||||
ma_result result;
|
||||
ma_decoder decoder;
|
||||
ma_device_config deviceConfig;
|
||||
ma_device device;
|
||||
|
||||
result = ma_decoder_init_file(filename, NULL, &decoder);
|
||||
if (result != MA_SUCCESS) {
|
||||
|
|
@ -62,8 +69,6 @@ int miniaudio_play(const char* filename)
|
|||
return -3;
|
||||
}
|
||||
|
||||
ma_event_init(&g_stopEvent);
|
||||
|
||||
if (ma_device_start(&device) != MA_SUCCESS) {
|
||||
printf("Failed to start playback device.\n");
|
||||
ma_device_uninit(&device);
|
||||
|
|
@ -71,14 +76,67 @@ int miniaudio_play(const char* filename)
|
|||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
software/library/audio_player.h
Normal file
47
software/library/audio_player.h
Normal 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
|
||||
|
|
@ -4,21 +4,16 @@
|
|||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <filesystem>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
StoryProject::StoryProject()
|
||||
{
|
||||
m_audioThread = std::thread( std::bind(&StoryProject::AudioThread, this) );
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_project_file_path = file_path;
|
||||
m_story_file_path = file_path;
|
||||
std::filesystem::path p(file_path);
|
||||
m_working_dir= p.parent_path().generic_string();
|
||||
|
||||
|
|
@ -243,7 +238,7 @@ void StoryProject::Save(const nlohmann::json &model, ResourceManager &manager)
|
|||
{
|
||||
nlohmann::json resourcesData;
|
||||
|
||||
auto [b, e] = manager.filter("");
|
||||
auto [b, e] = manager.Items();
|
||||
for (auto it = b; it != e; ++it)
|
||||
{
|
||||
nlohmann::json obj = {{"type", (*it)->type},
|
||||
|
|
@ -258,7 +253,7 @@ void StoryProject::Save(const nlohmann::json &model, ResourceManager &manager)
|
|||
|
||||
j["nodegraph"] = model;
|
||||
|
||||
std::ofstream o(m_project_file_path);
|
||||
std::ofstream o(m_story_file_path);
|
||||
o << std::setw(4) << j << std::endl;
|
||||
}
|
||||
|
||||
|
|
@ -364,32 +359,6 @@ std::string StoryProject::GetFileExtension(const std::string &fileName)
|
|||
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)
|
||||
{
|
||||
m_imageFormat = format;
|
||||
|
|
@ -408,7 +377,7 @@ void StoryProject::SetDisplayFormat(int w, int h)
|
|||
|
||||
std::string StoryProject::GetProjectFilePath() const
|
||||
{
|
||||
return m_project_file_path;
|
||||
return m_story_file_path;
|
||||
}
|
||||
|
||||
std::string StoryProject::GetWorkingDir() const
|
||||
|
|
@ -5,47 +5,13 @@
|
|||
#include <string>
|
||||
#include <filesystem>
|
||||
#include "json.hpp"
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "json.hpp"
|
||||
#include "resource_manager.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;
|
||||
};
|
||||
#include "audio_player.h"
|
||||
|
||||
|
||||
|
||||
|
|
@ -141,9 +107,8 @@ public:
|
|||
static void EraseString(std::string &theString, const std::string &toErase);
|
||||
static std::string ToUpper(const std::string &input);
|
||||
|
||||
|
||||
void SaveStory(const std::vector<uint8_t> &m_program);
|
||||
void PlaySoundFile(const std::string &fileName);
|
||||
|
||||
private:
|
||||
// Project properties and location
|
||||
std::string m_name; /// human readable name
|
||||
|
|
@ -156,17 +121,13 @@ private:
|
|||
std::string m_titleSound;
|
||||
|
||||
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_h{240};
|
||||
|
||||
std::thread m_audioThread;
|
||||
ThreadSafeQueue<AudioCommand> m_audioQueue;
|
||||
|
||||
ImageFormat m_imageFormat{IMG_FORMAT_BMP_4BITS};
|
||||
SoundFormat m_soundFormat{SND_FORMAT_WAV};
|
||||
void AudioThread();
|
||||
};
|
||||
|
||||
#endif // STORY_PROJECT_H
|
||||
52
software/library/thread_safe_queue.h
Normal file
52
software/library/thread_safe_queue.h
Normal 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
|
||||
|
|
@ -131,10 +131,10 @@ set(SRCS
|
|||
src/connection.cpp
|
||||
src/connection.h
|
||||
|
||||
src/uuid.h
|
||||
src/resource_manager.h
|
||||
src/i_story_project.h
|
||||
src/resource.h
|
||||
src/media_converter.cpp
|
||||
src/media_converter.h
|
||||
|
||||
src/i_story_manager.h
|
||||
|
||||
libs/ImGuiColorTextEdit/TextEditor.cpp
|
||||
libs/ImGuiColorTextEdit/TextEditor.h
|
||||
|
|
@ -155,13 +155,15 @@ set(SRCS
|
|||
|
||||
../software/chip32/chip32_assembler.cpp
|
||||
../software/chip32/chip32_vm.c
|
||||
../software/library/miniaudio.c
|
||||
../software/library/audio_player.cpp
|
||||
../software/library/audio_player.h
|
||||
../software/library/miniaudio.h
|
||||
|
||||
src/story_project.cpp
|
||||
src/story_project.h
|
||||
src/media_converter.cpp
|
||||
src/media_converter.h
|
||||
../software/library/uuid.h
|
||||
../software/library/resource.h
|
||||
../software/library/resource_manager.h
|
||||
../software/library/story_project.cpp
|
||||
../software/library/story_project.h
|
||||
../software/library/thread_safe_queue.h
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ bool equals(InputIt1 first1, InputIt1 last1,
|
|||
TextEditor::TextEditor()
|
||||
: mLineSpacing(1.0f)
|
||||
, mUndoIndex(0)
|
||||
, mExternalUndoBuffer(nullptr)
|
||||
, mTabSize(4)
|
||||
, mOverwrite(false)
|
||||
, mReadOnly(false)
|
||||
|
|
@ -41,15 +42,10 @@ TextEditor::TextEditor()
|
|||
, mColorRangeMin(0)
|
||||
, mColorRangeMax(0)
|
||||
, mSelectionMode(SelectionMode::Normal)
|
||||
, mCheckComments(true)
|
||||
, mLastClick(-1.0f)
|
||||
, mHandleKeyboardInputs(true)
|
||||
, mHandleMouseInputs(true)
|
||||
, mIgnoreImGuiChild(false)
|
||||
, mShowWhitespaces(true)
|
||||
, mShowShortTabGlyphs(false)
|
||||
, mCheckComments(true)
|
||||
, mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
|
||||
, mExternalUndoBuffer(nullptr)
|
||||
, mLastClick(-1.0f)
|
||||
{
|
||||
SetPalette(GetDarkPalette());
|
||||
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)
|
||||
snprintf(buf, 16, "%d ", lineNo + 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -247,6 +247,10 @@ public:
|
|||
void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = 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 SetText(const std::string& aText);
|
||||
std::string GetText() const;
|
||||
|
|
@ -386,10 +390,10 @@ private:
|
|||
bool mCursorPositionChanged;
|
||||
int mColorRangeMin, mColorRangeMax;
|
||||
SelectionMode mSelectionMode;
|
||||
bool mHandleKeyboardInputs;
|
||||
bool mHandleMouseInputs;
|
||||
bool mIgnoreImGuiChild;
|
||||
bool mShowWhitespaces;
|
||||
bool mHandleKeyboardInputs{true};
|
||||
bool mHandleMouseInputs{true};
|
||||
bool mIgnoreImGuiChild{false};
|
||||
bool mShowWhitespaces{true};
|
||||
bool mShowShortTabGlyphs;
|
||||
|
||||
Palette mPaletteBase;
|
||||
|
|
@ -404,6 +408,6 @@ private:
|
|||
Coordinates mInteractiveStart, mInteractiveEnd;
|
||||
std::string mLineBuffer;
|
||||
uint64_t mStartTime;
|
||||
|
||||
float mLastClick;
|
||||
int mExecutionMarker{-1};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
unsigned long BaseNode::s_nextId = 1;
|
||||
|
||||
BaseNode::BaseNode(const std::string &title, IStoryProject &proj)
|
||||
: m_project(proj)
|
||||
BaseNode::BaseNode(const std::string &title, IStoryManager &proj)
|
||||
: m_story(proj)
|
||||
{
|
||||
// m_id = UUID().String();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "json.hpp"
|
||||
#include "i_story_project.h"
|
||||
#include "i_story_manager.h"
|
||||
|
||||
#include <imgui_node_editor.h>
|
||||
namespace ed = ax::NodeEditor;
|
||||
|
|
@ -101,7 +101,7 @@ public:
|
|||
float y;
|
||||
};
|
||||
|
||||
BaseNode(const std::string &title, IStoryProject &proj);
|
||||
BaseNode(const std::string &title, IStoryManager &proj);
|
||||
|
||||
|
||||
virtual void Draw() = 0;
|
||||
|
|
@ -223,7 +223,7 @@ public:
|
|||
void DeleteOutput();
|
||||
|
||||
private:
|
||||
IStoryProject &m_project;
|
||||
IStoryManager &m_story;
|
||||
|
||||
std::unique_ptr<Node> m_node;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ void CodeEditor::Initialize()
|
|||
|
||||
|
||||
// "breakpoint" markers
|
||||
m_breakPoints.insert(42);
|
||||
mEditor.SetBreakpoints(m_breakPoints);
|
||||
// m_breakPoints.insert(42);
|
||||
// mEditor.SetBreakpoints(m_breakPoints);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ public:
|
|||
void SetScript(const std::string &txt);
|
||||
void ClearErrors();
|
||||
void AddError(int line, const std::string &text);
|
||||
|
||||
void HighlightLine(int line)
|
||||
{
|
||||
mEditor.SetExecutionMarker(line);
|
||||
}
|
||||
private:
|
||||
TextEditor mEditor;
|
||||
TextEditor::Breakpoints m_breakPoints;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
#include "gui.h"
|
||||
#include "IconsMaterialDesignIcons.h"
|
||||
|
||||
EmulatorWindow::EmulatorWindow(IStoryProject &proj)
|
||||
EmulatorWindow::EmulatorWindow(IStoryManager &proj)
|
||||
: 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));
|
||||
|
||||
// float sz = ImGui::GetTextLineHeight();
|
||||
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
|
||||
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));
|
||||
|
||||
|
|
@ -42,13 +51,25 @@ void EmulatorWindow::Draw()
|
|||
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::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::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::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::PopFont();
|
||||
|
|
@ -57,9 +78,20 @@ void EmulatorWindow::Draw()
|
|||
|
||||
if (ImGui::Button("Build"))
|
||||
{
|
||||
m_project.Build();
|
||||
m_story.Build();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "window_base.h"
|
||||
#include "i_story_project.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "gui.h"
|
||||
|
||||
class EmulatorWindow : public WindowBase
|
||||
{
|
||||
public:
|
||||
EmulatorWindow(IStoryProject &proj);
|
||||
EmulatorWindow(IStoryManager &proj);
|
||||
|
||||
void Initialize();
|
||||
virtual void Draw() override;
|
||||
|
||||
void ClearImage();
|
||||
void SetImage(const std::string &image);
|
||||
|
||||
private:
|
||||
IStoryProject &m_project;
|
||||
IStoryManager &m_story;
|
||||
Gui::Image m_image;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ your use of the corresponding standard functions.
|
|||
|
||||
#include "IconsMaterialDesignIcons.h"
|
||||
#include "IconsFontAwesome5_c.h"
|
||||
#include "qoi.h"
|
||||
|
||||
static void glfw_error_callback(int error, const char* description)
|
||||
{
|
||||
|
|
@ -34,24 +35,59 @@ static SDL_Renderer* renderer{nullptr};
|
|||
|
||||
#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
|
||||
bool LoadTextureFromFile(const char* filename, Gui::Image &img)
|
||||
{
|
||||
|
||||
|
||||
std::string ext = GetFileExtension(filename);
|
||||
|
||||
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;
|
||||
unsigned char* data = stbi_load(filename, &img.w, &img.h, &channels, 0);
|
||||
data = stbi_load(filename, &img.w, &img.h, &channels, 0);
|
||||
|
||||
if (data == nullptr) {
|
||||
fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// SDL3
|
||||
// SDL_Surface* surface = SDL_CreateSurfaceFrom((void*)data, img.w, img.h, 4 * img.w, SDL_PIXELFORMAT_RGBA8888);
|
||||
// SDL_Surface* surface = SDL_CreateSurfaceFrom((void*)data, img.w, img.h, 4 * img.w, SDL_PIXELFORMAT_RGBA8888);
|
||||
|
||||
// SDL2
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*)data, img.w, img.h, channels * 8, channels * img.w,
|
||||
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) {
|
||||
|
|
@ -67,7 +103,7 @@ bool LoadTextureFromFile(const char* filename, Gui::Image &img)
|
|||
|
||||
// SDL_DestroySurface(surface); // SDL3
|
||||
SDL_FreeSurface(surface); // SDL2
|
||||
stbi_image_free(data);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef I_STORY_PROJECT_H
|
||||
#define I_STORY_PROJECT_H
|
||||
#ifndef I_STORY_MANAGER_H
|
||||
#define I_STORY_MANAGER_H
|
||||
|
||||
|
||||
#include <string>
|
||||
|
|
@ -10,10 +10,10 @@
|
|||
#include "resource.h"
|
||||
#include "connection.h"
|
||||
|
||||
class IStoryProject
|
||||
class IStoryManager
|
||||
{
|
||||
public:
|
||||
virtual ~IStoryProject() {}
|
||||
virtual ~IStoryManager() {}
|
||||
|
||||
virtual void Log(const std::string &txt, bool critical = false) = 0;
|
||||
virtual void PlaySoundFile(const std::string &fileName) = 0;
|
||||
|
|
@ -31,7 +31,12 @@ public:
|
|||
virtual void Build() = 0;
|
||||
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(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
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "platform_folders.h"
|
||||
#include "uuid.h"
|
||||
#include "media_converter.h"
|
||||
|
||||
#ifdef USE_WINDOWS_OS
|
||||
#include <winsock2.h>
|
||||
|
|
@ -22,9 +23,24 @@ MainWindow::MainWindow()
|
|||
: m_emulatorWindow(*this)
|
||||
, m_resourcesWindow(*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()
|
||||
|
|
@ -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()
|
||||
{
|
||||
float statusWindowHeight = ImGui::GetFrameHeight() * 1.4f;
|
||||
|
|
@ -221,11 +425,11 @@ void MainWindow::OpenProjectDialog()
|
|||
std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
|
||||
|
||||
|
||||
m_project.Initialize(filePathName);
|
||||
m_story.Initialize(filePathName);
|
||||
|
||||
nlohmann::json model;
|
||||
|
||||
if (m_project.Load(filePathName, model, m_resources))
|
||||
if (m_story.Load(filePathName, model, m_resources))
|
||||
{
|
||||
Log("Open project success");
|
||||
m_nodeEditorWindow.Load(model);
|
||||
|
|
@ -247,7 +451,7 @@ void MainWindow::OpenProjectDialog()
|
|||
|
||||
void MainWindow::EnableProject()
|
||||
{
|
||||
auto proj = m_project.GetProjectFilePath();
|
||||
auto proj = m_story.GetProjectFilePath();
|
||||
// Add to recent if not exists
|
||||
if (std::find(m_recentProjects.begin(), m_recentProjects.end(), proj) != m_recentProjects.end())
|
||||
{
|
||||
|
|
@ -426,21 +630,21 @@ void MainWindow::NewProjectPopup()
|
|||
|
||||
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)
|
||||
{
|
||||
m_project.SetDisplayFormat(320, 240);
|
||||
m_story.SetDisplayFormat(320, 240);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_project.SetDisplayFormat(640, 480);
|
||||
m_story.SetDisplayFormat(640, 480);
|
||||
}
|
||||
|
||||
m_project.SetImageFormat(GetImageFormat(image_item_current_idx));
|
||||
m_project.SetSoundFormat(GetSoundFormat(sound_item_current_idx));
|
||||
m_project.SetName(project_name);
|
||||
m_project.SetUuid(UUID().String());
|
||||
m_story.SetImageFormat(GetImageFormat(image_item_current_idx));
|
||||
m_story.SetSoundFormat(GetSoundFormat(sound_item_current_idx));
|
||||
m_story.SetName(project_name);
|
||||
m_story.SetUuid(UUID().String());
|
||||
|
||||
SaveProject();
|
||||
EnableProject();
|
||||
|
|
@ -466,12 +670,12 @@ void MainWindow::SaveProject()
|
|||
{
|
||||
nlohmann::json model;
|
||||
m_nodeEditorWindow.Save(model);
|
||||
m_project.Save(model, m_resources);
|
||||
m_story.Save(model, m_resources);
|
||||
}
|
||||
|
||||
void MainWindow::CloseProject()
|
||||
{
|
||||
m_project.Clear();
|
||||
m_story.Clear();
|
||||
m_nodeEditorWindow.Clear();
|
||||
|
||||
// m_model.Clear();
|
||||
|
|
@ -505,6 +709,10 @@ void MainWindow::Loop()
|
|||
DrawMainMenuBar();
|
||||
// DrawStatusBar();
|
||||
|
||||
|
||||
ProcessStory();
|
||||
|
||||
|
||||
// ------------ Draw all windows
|
||||
m_consoleWindow.Draw();
|
||||
m_emulatorWindow.Draw();
|
||||
|
|
@ -543,12 +751,12 @@ void MainWindow::Log(const std::string &txt, bool critical)
|
|||
void MainWindow::PlaySoundFile(const std::string &fileName)
|
||||
{
|
||||
Log("Play sound file: " + fileName);
|
||||
m_project.PlaySoundFile(fileName);
|
||||
m_player.Play(fileName);
|
||||
}
|
||||
|
||||
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()
|
||||
|
|
@ -647,9 +855,9 @@ void MainWindow::GenerateBinary()
|
|||
// FIXME
|
||||
// m_ramView->SetMemory(m_ram_data, sizeof(m_ram_data));
|
||||
// m_romView->SetMemory(m_rom_data, m_program.size());
|
||||
m_project.SaveStory(m_program);
|
||||
m_story.SaveStory(m_program);
|
||||
chip32_initialize(&m_chip32_ctx);
|
||||
m_dbg.run_result = VM_OK;
|
||||
m_dbg.run_result = VM_READY;
|
||||
UpdateVmView();
|
||||
// DebugContext::DumpCodeAssembler(m_assembler);
|
||||
}
|
||||
|
|
@ -672,14 +880,70 @@ void MainWindow::UpdateVmView()
|
|||
{
|
||||
// FIXME
|
||||
// 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
|
||||
// m_ramView->SetMemory(m_ram_data, m_chip32_ctx.ram.size);
|
||||
}
|
||||
|
||||
void MainWindow::ConvertResources()
|
||||
{
|
||||
auto [b, e] = m_resources.Items();
|
||||
for (auto it = b; it != e; ++it)
|
||||
{
|
||||
std::string inputfile = m_story.BuildFullAssetsPath((*it)->file.c_str());
|
||||
std::string outputfile = std::filesystem::path(m_story.AssetsPath() / StoryProject::RemoveFileExtension((*it)->file)).string();
|
||||
|
||||
int retCode = 0;
|
||||
if ((*it)->format == "PNG")
|
||||
{
|
||||
outputfile += ".qoi"; // FIXME: prendre la 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()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "gui.h"
|
||||
#include "console_window.h"
|
||||
#include "code_editor.h"
|
||||
|
|
@ -13,7 +16,8 @@
|
|||
#include "chip32_assembler.h"
|
||||
#include "chip32_vm.h"
|
||||
#include "story_project.h"
|
||||
#include "i_story_project.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "thread_safe_queue.h"
|
||||
|
||||
struct DebugContext
|
||||
{
|
||||
|
|
@ -25,6 +29,10 @@ struct DebugContext
|
|||
|
||||
std::set<int> m_breakpoints;
|
||||
|
||||
void Stop() {
|
||||
run_result = VM_FINISHED;
|
||||
}
|
||||
|
||||
static void DumpCodeAssembler(Chip32::Assembler & assembler) {
|
||||
|
||||
for (std::vector<Chip32::Instr>::const_iterator iter = assembler.Begin();
|
||||
|
|
@ -51,7 +59,7 @@ struct DebugContext
|
|||
}
|
||||
};
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
template <typename T>
|
||||
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:
|
||||
MainWindow();
|
||||
|
|
@ -80,8 +88,9 @@ public:
|
|||
void Loop();
|
||||
|
||||
private:
|
||||
enum VmEventType { EvNoEvent, EvStep, EvOkButton, EvLeftButton, EvRightButton, EvAudioFinished};
|
||||
|
||||
StoryProject m_project;
|
||||
StoryProject m_story;
|
||||
|
||||
// VM
|
||||
uint8_t m_rom_data[16*1024];
|
||||
|
|
@ -95,6 +104,7 @@ private:
|
|||
DebugContext m_dbg;
|
||||
std::string m_currentCode;
|
||||
|
||||
|
||||
std::vector<std::string> m_recentProjects;
|
||||
|
||||
ResourceManager m_resources;
|
||||
|
|
@ -110,8 +120,17 @@ private:
|
|||
|
||||
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 PlaySoundFile(const std::string &fileName) override;;
|
||||
virtual std::string BuildFullAssetsPath(const std::string &fileName) const override;
|
||||
|
|
@ -125,6 +144,13 @@ private:
|
|||
virtual void Build() override;
|
||||
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(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 LoadParams();
|
||||
|
|
@ -143,6 +169,10 @@ private:
|
|||
void ConvertResources();
|
||||
void GenerateBinary();
|
||||
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
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ namespace ed = ax::NodeEditor;
|
|||
#include "IconsMaterialDesignIcons.h"
|
||||
#include "story_project.h"
|
||||
|
||||
MediaNode::MediaNode(const std::string &title, IStoryProject &proj)
|
||||
MediaNode::MediaNode(const std::string &title, IStoryManager &proj)
|
||||
: BaseNode(title, proj)
|
||||
, m_project(proj)
|
||||
, m_story(proj)
|
||||
{
|
||||
Gui::LoadRawImage("fairy.png", m_image);
|
||||
|
||||
|
|
@ -87,8 +87,6 @@ void MediaNode::Draw()
|
|||
|
||||
DrawPins();
|
||||
|
||||
|
||||
|
||||
BaseNode::FrameEnd();
|
||||
|
||||
}
|
||||
|
|
@ -144,7 +142,7 @@ void MediaNode::DrawProperties()
|
|||
|
||||
if (ImGui::Button(m_buttonUniqueName.c_str()))
|
||||
{
|
||||
m_project.PlaySoundFile(m_soundPath);
|
||||
m_story.PlaySoundFile(m_soundPath);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
|
@ -163,7 +161,7 @@ void MediaNode::DrawProperties()
|
|||
if (ImGui::BeginPopup("popup_button")) {
|
||||
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;
|
||||
for (auto it = filtreDebut; it != filtreFin; ++it, n++)
|
||||
{
|
||||
|
|
@ -188,13 +186,13 @@ void MediaNode::DrawProperties()
|
|||
void MediaNode::SetImage(const std::string &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)
|
||||
{
|
||||
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, "
|
||||
<< 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;
|
||||
for (auto & c : conns)
|
||||
{
|
||||
std::stringstream ssChoice;
|
||||
|
||||
// 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))
|
||||
{
|
||||
ss << ", ";
|
||||
|
|
@ -311,7 +309,7 @@ std::string MediaNode::Build()
|
|||
}
|
||||
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)
|
||||
|
|
@ -320,7 +318,7 @@ std::string MediaNode::Build()
|
|||
{
|
||||
// On place dans R0 le prochain noeud à exécuter en cas de OK
|
||||
ss << "lcons r0, "
|
||||
<< m_project.GetNodeEntryLabel(c->inNodeId) << "\n"
|
||||
<< m_story.GetNodeEntryLabel(c->inNodeId) << "\n"
|
||||
<< "ret\n";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#include <set>
|
||||
|
||||
#include "base_node.h"
|
||||
#include "i_story_project.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "gui.h"
|
||||
#include <imgui_node_editor.h>
|
||||
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
class MediaNode : public BaseNode
|
||||
{
|
||||
public:
|
||||
MediaNode(const std::string &title, IStoryProject &proj);
|
||||
MediaNode(const std::string &title, IStoryManager &proj);
|
||||
|
||||
void Draw() override;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ public:
|
|||
virtual std::string GetEntryLabel() override;
|
||||
virtual std::string GenerateConstants() override;
|
||||
private:
|
||||
IStoryProject &m_project;
|
||||
IStoryManager &m_story;
|
||||
Gui::Image m_image;
|
||||
std::string m_soundName;
|
||||
std::string m_soundPath;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
#include "gui.h"
|
||||
|
||||
|
||||
NodeEditorWindow::NodeEditorWindow(IStoryProject &proj)
|
||||
NodeEditorWindow::NodeEditorWindow(IStoryManager &proj)
|
||||
: WindowBase("Node editor")
|
||||
, m_project(proj)
|
||||
, m_story(proj)
|
||||
{
|
||||
|
||||
registerNode<MediaNode>("media-node");
|
||||
|
|
@ -47,7 +47,7 @@ void NodeEditorWindow::LoadNode(const nlohmann::json &nodeJson)
|
|||
nlohmann::json internalDataJson = nodeJson["internal-data"];
|
||||
std::string type = nodeJson["type"].get<std::string>();
|
||||
|
||||
auto n = createNode(type, "", m_project);
|
||||
auto n = createNode(type, "", m_story);
|
||||
if (n)
|
||||
{
|
||||
n->SetType(type); // FIXME: set type in createNode factory?
|
||||
|
|
@ -225,7 +225,7 @@ uint32_t NodeEditorWindow::FindFirstNode() const
|
|||
if (!foundConnection)
|
||||
{
|
||||
id = n->GetId();
|
||||
m_project.Log("First node is: " + std::to_string(id));
|
||||
m_story.Log("First node is: " + std::to_string(id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include <imgui_node_editor.h>
|
||||
#include "base_node.h"
|
||||
#include "window_base.h"
|
||||
#include "i_story_project.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "story_project.h"
|
||||
#include "json.hpp"
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ public:
|
|||
std::shared_ptr<Connection> model;
|
||||
};
|
||||
|
||||
NodeEditorWindow(IStoryProject &proj);
|
||||
NodeEditorWindow(IStoryManager &proj);
|
||||
~NodeEditorWindow();
|
||||
virtual void Draw() override;
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ public:
|
|||
std::shared_ptr<BaseNode> GetSelectedNode();
|
||||
|
||||
private:
|
||||
IStoryProject &m_project;
|
||||
IStoryManager &m_story;
|
||||
|
||||
ed::EditorContext* m_context = nullptr;
|
||||
|
||||
|
|
@ -90,12 +90,12 @@ private:
|
|||
|
||||
template<class NodeType>
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
Registry m_registry;
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ private:
|
|||
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);
|
||||
if (i == m_registry.end()) {
|
||||
throw std::invalid_argument(std::string(__PRETTY_FUNCTION__) +
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
//static thread_pool pool;
|
||||
|
||||
ResourcesWindow::ResourcesWindow(IStoryProject &project)
|
||||
ResourcesWindow::ResourcesWindow(IStoryManager &project)
|
||||
: WindowBase("Resources")
|
||||
, m_project(project)
|
||||
, m_story(project)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ void ResourcesWindow::ChooseFile()
|
|||
|
||||
|
||||
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);
|
||||
|
||||
auto res = std::make_shared<Resource>();
|
||||
|
|
@ -56,7 +56,7 @@ void ResourcesWindow::ChooseFile()
|
|||
res->format = ext;
|
||||
res->type = m_soundFile ? "sound" : "image";
|
||||
res->file = p.filename().generic_string();
|
||||
m_project.AddResource(res);
|
||||
m_story.AddResource(res);
|
||||
}
|
||||
|
||||
// close
|
||||
|
|
@ -104,7 +104,7 @@ void ResourcesWindow::Draw()
|
|||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto [b, e] = m_project.Resources();
|
||||
auto [b, e] = m_story.Resources();
|
||||
|
||||
int id = 1000;
|
||||
for (auto it = b; it != e; ++it)
|
||||
|
|
@ -152,7 +152,7 @@ void ResourcesWindow::Draw()
|
|||
ImGui::TableNextColumn();
|
||||
if (ImGui::SmallButton("Delete"))
|
||||
{
|
||||
m_project.DeleteResource(it);
|
||||
m_story.DeleteResource(it);
|
||||
quitLoop = true;
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include "i_story_project.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "window_base.h"
|
||||
|
||||
class ResourcesWindow : public WindowBase
|
||||
{
|
||||
public:
|
||||
ResourcesWindow(IStoryProject &project);
|
||||
ResourcesWindow(IStoryManager &project);
|
||||
~ResourcesWindow();
|
||||
virtual void Draw() override;
|
||||
|
||||
private:
|
||||
IStoryProject &m_project;
|
||||
IStoryManager &m_story;
|
||||
|
||||
bool m_showImportDialog{false};
|
||||
bool m_soundFile{false};
|
||||
|
|
|
|||
Loading…
Reference in a new issue