diff --git a/firmware/library/resource.h b/firmware/library/resource.h index f4ff26a..bd57114 100644 --- a/firmware/library/resource.h +++ b/firmware/library/resource.h @@ -15,7 +15,7 @@ struct Resource std::string type; ~Resource() { - std::cout << "Res deleted" << std::endl; + // std::cout << "Res deleted" << std::endl; } }; diff --git a/story-editor/src/emulator_window.cpp b/story-editor/src/emulator_window.cpp index 7696000..88e8096 100644 --- a/story-editor/src/emulator_window.cpp +++ b/story-editor/src/emulator_window.cpp @@ -75,16 +75,28 @@ void EmulatorWindow::Draw() ImGui::SeparatorText("Script control and debug"); - if (ImGui::Button("Build")) + if (ImGui::Button("Generate pack")) { - m_story.Build(); + m_story.Build(false); + } + if (ImGui::Button("Build script")) + { + m_story.Build(true); } ImGui::SameLine(); - if (ImGui::Button("Build & Play")) + if (ImGui::Button("Play")) { + m_story.Log("Play story"); m_story.Play(); } + ImGui::SameLine(); + if (ImGui::Button("Stop")) + { + m_story.Log("Stop story"); + m_story.Stop(); + } + ImGui::SameLine(); WindowBase::EndDraw(); diff --git a/story-editor/src/i_story_manager.h b/story-editor/src/i_story_manager.h index 16d7adc..8a596b5 100644 --- a/story-editor/src/i_story_manager.h +++ b/story-editor/src/i_story_manager.h @@ -45,11 +45,12 @@ public: virtual void DeleteResource(FilterIterator &it) = 0; // Node interaction - virtual void Build() = 0; + virtual void Build(bool compileonly) = 0; virtual std::list> GetNodeConnections(const std::string &nodeId) = 0; virtual std::string GetNodeEntryLabel(const std::string &nodeId) = 0; virtual void Play() = 0; virtual void Ok() = 0; + virtual void Stop() = 0; virtual void Pause() = 0; virtual void Next() = 0; virtual void Previous() = 0; diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index 69a39a6..fa70339 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -66,16 +66,13 @@ std::string MainWindow::GetFileNameFromMemory(uint32_t addr) 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 - } + if ((m_dbg.run_result == VM_READY) || (m_dbg.run_result == VM_FINISHED)) + { + m_dbg.free_run = true; + m_dbg.run_result = VM_OK; // actually starts the execution } + } void MainWindow::Ok() @@ -83,6 +80,11 @@ void MainWindow::Ok() m_eventQueue.push({VmEventType::EvOkButton}); } +void MainWindow::Stop() +{ + m_eventQueue.push({VmEventType::EvOkButton}); +} + void MainWindow::Pause() { @@ -150,6 +152,10 @@ void MainWindow::ProcessStory() m_chip32_ctx.registers[R0] = 0x08; m_dbg.run_result = VM_OK; } + else if (event.type == VmEventType::EvStop) + { + m_dbg.run_result = VM_FINISHED; + } } } @@ -704,6 +710,7 @@ void MainWindow::Loop() while (!done) { + auto time = SDL_GetTicks(); bool aboutToClose = m_gui.PollEvent(); m_gui.StartFrame(); @@ -746,6 +753,12 @@ void MainWindow::Loop() m_gui.EndFrame(); + + // Rendering and event handling + if ((SDL_GetTicks() - time) < 10) { + SDL_Delay(10); + } + } @@ -799,18 +812,24 @@ void MainWindow::DeleteResource(FilterIterator &it) return m_resources.Delete(it); } -void MainWindow::Build() +void MainWindow::Build(bool compileonly) { // 1. First compile nodes to assembly - CompileToAssembler(); + if (CompileToAssembler()) + { - // 2. Compile the assembly to machine binary - GenerateBinary(); + // 2. Compile the assembly to machine binary + GenerateBinary(); - // 3. Convert all media to desired type format - ConvertResources(); + if (!compileonly) + { + // 3. Convert all media to desired type format + ConvertResources(); + } + } } + std::string MainWindow::GetNodeEntryLabel(const std::string &nodeId) { return m_nodeEditorWindow.GetNodeEntryLabel(nodeId); @@ -827,9 +846,10 @@ bool MainWindow::CompileToAssembler() // FIXME // 2. Generate the assembly code from the model - m_currentCode = m_nodeEditorWindow.Build(); + bool ret = m_nodeEditorWindow.Build(m_currentCode); // Add global functions + if (ret) { std::string buffer; @@ -839,11 +859,11 @@ bool MainWindow::CompileToAssembler() f.seekg(0); f.read(buffer.data(), buffer.size()); m_currentCode += buffer; + + m_editorWindow.SetScript(m_currentCode); } - - m_editorWindow.SetScript(m_currentCode); - - return true; + + return ret; } void MainWindow::GenerateBinary() @@ -932,14 +952,19 @@ void MainWindow::ConvertResources() int retCode = 0; if ((*it)->format == "PNG") { - outputfile += ".qoi"; // FIXME: prendre la congif en cours désirée + outputfile += ".qoi"; // FIXME: prendre la config 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 + outputfile += ".wav"; // FIXME: prendre la config en cours désirée retCode = MediaConverter::Mp3ToWav(inputfile, outputfile); } + else if ((*it)->format == "OGG") + { + outputfile += ".wav"; // FIXME: prendre la config en cours désirée + retCode = MediaConverter::OggToWav(inputfile, outputfile); + } else { Log("Skipped: " + inputfile + ", unknown format" + outputfile, true); diff --git a/story-editor/src/main_window.h b/story-editor/src/main_window.h index 2efe504..1dfccd7 100644 --- a/story-editor/src/main_window.h +++ b/story-editor/src/main_window.h @@ -73,7 +73,7 @@ public: void Loop(); private: - enum VmEventType { EvNoEvent, EvStep, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished}; + enum VmEventType { EvNoEvent, EvStep, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished, EvStop}; std::shared_ptr m_story; @@ -134,11 +134,12 @@ private: virtual void ClearResources() override; virtual std::pair Resources() override; virtual void DeleteResource(FilterIterator &it) override; - virtual void Build() override; + virtual void Build(bool compileonly) override; virtual std::list> GetNodeConnections(const std::string &nodeId) override; virtual std::string GetNodeEntryLabel(const std::string &nodeId) override; virtual void Play() override; virtual void Ok() override; + virtual void Stop() override; virtual void Pause() override; virtual void Next() override; virtual void Previous() override; diff --git a/story-editor/src/media_converter.cpp b/story-editor/src/media_converter.cpp index 20b23cd..c38de3d 100644 --- a/story-editor/src/media_converter.cpp +++ b/story-editor/src/media_converter.cpp @@ -3,6 +3,10 @@ #include #include #include +#include + +#define STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.c" #define STB_IMAGE_IMPLEMENTATION @@ -158,6 +162,86 @@ int MediaConverter::Mp3ToWav(const std::string &inputFileName, const std::string return cSuccess; } + +// Function to read the entire contents of a file into memory +static unsigned char* read_entire_file(const char* filename, int* length) { + FILE* f = fopen(filename, "rb"); + if (!f) return NULL; + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char* buffer = (unsigned char*)malloc(size); + if (!buffer) { + fclose(f); + return NULL; + } + fread(buffer, 1, size, f); + fclose(f); + if (length) *length = (int)size; + return buffer; +} + +int MediaConverter::OggToWav(const std::string &inputFileName, const std::string &outputFileName) +{ + + int ogg_length; + unsigned char* ogg_data = read_entire_file(inputFileName.c_str(), &ogg_length); + if (!ogg_data) { + std::cout << "Failed to read input file " << inputFileName << std::endl; + return 1; + } + + int channels, sample_rate; + short* samples = NULL; + // (const uint8 *mem, int len, int *channels, int *sample_rate, short **output + int data_len = stb_vorbis_decode_memory(ogg_data, ogg_length, &channels, &sample_rate, &samples); + if (!samples) { + std::cout <<"Failed to decode OGG file " << inputFileName << std::endl; + free(ogg_data); + return 1; + } + + // Write WAV file header + FILE* wav_file = fopen(outputFileName.c_str(), "wb"); + if (!wav_file) { + std::cout << "Failed to create output file " << outputFileName << std::endl; + free(ogg_data); + free(samples); + return 1; + } + fwrite("RIFF", 1, 4, wav_file); + int total_size = 36 + channels * (sample_rate * sizeof(short)); + fwrite(&total_size, 4, 1, wav_file); + fwrite("WAVEfmt ", 1, 8, wav_file); + int format_size = 16; + fwrite(&format_size, 4, 1, wav_file); + short format_type = 1; // PCM + fwrite(&format_type, 2, 1, wav_file); + fwrite(&channels, 2, 1, wav_file); + fwrite(&sample_rate, 4, 1, wav_file); + int byte_rate = sample_rate * channels * sizeof(short); + fwrite(&byte_rate, 4, 1, wav_file); + short block_align = channels * sizeof(short); + fwrite(&block_align, 2, 1, wav_file); + short bits_per_sample = 16; + fwrite(&bits_per_sample, 2, 1, wav_file); + fwrite("data", 1, 4, wav_file); + // int data_size = channels * (sample_rate * sizeof(short)); + + int data_size = data_len * channels * 2; // *2 to make in bytes + fwrite(&data_size, 4, 1, wav_file); + + // Write sample data + fwrite(samples, sizeof(short), data_len, wav_file); + + fclose(wav_file); + + free(ogg_data); + free(samples); + + return cSuccess; +} + /* OGG TO WAV diff --git a/story-editor/src/media_converter.h b/story-editor/src/media_converter.h index fd972a1..2cbd1b8 100644 --- a/story-editor/src/media_converter.h +++ b/story-editor/src/media_converter.h @@ -22,6 +22,7 @@ public: MediaConverter(); static int ImageToQoi(const std::string &inputFileName, const std::string &outputFileName); static int Mp3ToWav(const std::string &inputFileName, const std::string &outputFileName); + static int OggToWav(const std::string &inputFileName, const std::string &outputFileName); private: static short *Mp3Read(const std::string &path, MediaInfo &desc); static int WavWrite(const std::string &path, short *sample_data, MediaInfo &desc); diff --git a/story-editor/src/node_editor/node_editor_window.cpp b/story-editor/src/node_editor/node_editor_window.cpp index 381b465..091fccf 100644 --- a/story-editor/src/node_editor/node_editor_window.cpp +++ b/story-editor/src/node_editor/node_editor_window.cpp @@ -256,6 +256,7 @@ std::string NodeEditorWindow::FindFirstNode() const for (const auto & n : m_nodes) { bool foundConnection = false; + for (const auto& l : m_links) { if (l->model->inNodeId == n->GetId()) @@ -275,7 +276,7 @@ std::string NodeEditorWindow::FindFirstNode() const return id; } -std::string NodeEditorWindow::Build() +bool NodeEditorWindow::Build(std::string &codeStr) { std::stringstream code; ed::SetCurrentEditor(m_context); @@ -285,6 +286,12 @@ std::string NodeEditorWindow::Build() std::string firstNode = FindFirstNode(); + if (firstNode == "") + { + m_story.Log("First node not found, there must be only one node with a free input.", true); + return false; + } + code << "\tjump " << GetNodeEntryLabel(firstNode) << "\r\n"; // First generate all constants @@ -299,7 +306,8 @@ std::string NodeEditorWindow::Build() } ed::SetCurrentEditor(nullptr); - return code.str(); + codeStr = code.str(); + return true; } std::list> NodeEditorWindow::GetNodeConnections(const std::string &nodeId) @@ -429,6 +437,18 @@ void NodeEditorWindow::Draw() // Handle deletion action if (ed::BeginDelete()) { + ed::NodeId nodeId = 0; + while (ed::QueryDeletedNode(&nodeId)) + { + if (ed::AcceptDeletedItem()) + { + auto id = std::find_if(m_nodes.begin(), m_nodes.end(), [nodeId](std::shared_ptr node) { return node->GetInternalId() == nodeId.Get(); }); + if (id != m_nodes.end()) + m_nodes.erase(id); + } + } + + // There may be many links marked for deletion, let's loop over them. ed::LinkId deletedLinkId; while (ed::QueryDeletedLink(&deletedLinkId)) diff --git a/story-editor/src/node_editor/node_editor_window.h b/story-editor/src/node_editor/node_editor_window.h index e225524..daa021b 100644 --- a/story-editor/src/node_editor/node_editor_window.h +++ b/story-editor/src/node_editor/node_editor_window.h @@ -54,7 +54,7 @@ public: void Clear(); void Load(const nlohmann::json &model); void Save(nlohmann::json &model); - std::string Build(); + bool Build(std::string &codeStr); std::list > GetNodeConnections(const std::string &nodeId); std::string GetNodeEntryLabel(const std::string &nodeId);