Compare commits

...

2 commits

Author SHA1 Message Date
anthony@rabine.fr
6ec1f39db7 Remove story in library + save/load variables
Some checks are pending
Build-StoryEditor / build_linux (push) Waiting to run
Build-StoryEditor / build_win32 (push) Waiting to run
Deploy-Documentation / deploy (push) Waiting to run
2025-04-25 23:40:21 +02:00
anthony@rabine.fr
47552d4719 Multiple runtime fixes, first build+execution with graphical nodes 2025-04-24 11:39:56 +02:00
12 changed files with 264 additions and 52 deletions

View file

@ -208,6 +208,24 @@ public:
return result;
}
static std::string ValueTypeToString(ValueType type) {
switch (type) {
case ValueType::INTEGER: return "Integer";
case ValueType::FLOAT: return "Float";
case ValueType::BOOL: return "Bool";
case ValueType::STRING: return "String";
default: return "Unknown";
}
}
static ValueType StringToValueType(const std::string& type) {
if (type == "Integer") return ValueType::INTEGER;
if (type == "Float") return ValueType::FLOAT;
if (type == "Bool") return ValueType::BOOL;
if (type == "String") return ValueType::STRING;
throw std::runtime_error("[variable.h] StringToValueType(): Invalid value type string");
}
private:
std::string m_variableName; // nom humain
ValueType m_valueType;

View file

@ -118,12 +118,18 @@ private:
auto* varNode = inputNode->GetAs<VariableNode>();
if (varNode) {
auto var = varNode->GetVariable();
// Generate code to load the variable value
// FIXME: hardcoded 4 bytes, replace by actual real variable size
m_assembly << " load r" << reg << ", $" << var->GetLabel() << ", 4" << "; Load variable " << var->GetVariableName() << "\n";
m_assembly << " push r" << reg << "\n";
// Assuming we have a function to load the variable value
// m_assembly << " load r0, " << varNode->GetVariableName() << "\n";
if (var)
{
// Generate code to load the variable value
// FIXME: hardcoded 4 bytes, replace by actual real variable size
m_assembly << " load r" << reg << ", $" << var->GetLabel() << ", 4" << "; Load variable " << var->GetVariableName() << "\n";
m_assembly << " push r" << reg << "\n";
}
else
{
throw std::runtime_error("ERROR! Variable not set in node: " + inputNode->node->GetId());
}
}
reg++;
}

View file

@ -10,6 +10,7 @@ PrintNode::PrintNode(const std::string &type)
// Create empty variable in memory
auto v = std::make_shared<Variable>(m_label);
v->SetTextValue("");
v->SetConstant(true);
m_label = v->GetLabel();
m_variables[m_label] = v;
}

View file

@ -19,7 +19,7 @@ StoryProject::StoryProject(ILogger &log)
: m_log(log)
{
// registerNode<MediaNode>("media-node");
registerNode<FunctionNode>("operator-node");
registerNode<OperatorNode>("operator-node");
registerNode<FunctionNode>("function-node");
registerNode<VariableNode>("variable-node");
registerNode<PrintNode>("print-node");
@ -384,6 +384,7 @@ bool StoryProject::UseResource(const std::string &label)
bool StoryProject::GenerateScript(std::string &codeStr)
{
bool retCode = true;
std::stringstream code;
// Empty resources usage
@ -403,43 +404,40 @@ bool StoryProject::GenerateScript(std::string &codeStr)
// Create generator
AssemblyGeneratorChip32 generator(context);
generator.Reset();
// Generate header comments
generator.GenerateHeader();
// Generate data section
generator.StartSection(AssemblyGenerator::Section::DATA);
for (const auto & p : m_pages)
try
{
p->BuildNodesVariables(generator);
generator.Reset();
// Generate header comments
generator.GenerateHeader();
// Generate text section
generator.StartSection(AssemblyGenerator::Section::TEXT);
for (const auto & p : m_pages)
{
p->BuildNodes(generator);
}
// Generate data section
generator.StartSection(AssemblyGenerator::Section::DATA);
for (const auto & p : m_pages)
{
p->BuildNodesVariables(generator);
}
generator.GenerateGlobalVariables(m_variables);
generator.GenerateExit();
}
generator.GenerateGlobalVariables(m_variables);
// Generate text section
generator.StartSection(AssemblyGenerator::Section::TEXT);
for (const auto & p : m_pages)
catch (const std::exception &e)
{
p->BuildNodes(generator);
m_log.Log(e.what(), true);
retCode = false;
}
generator.GenerateExit();
codeStr = generator.GetAssembly();
// Add our utility functions
// std::string buffer;
// std::ifstream f("scripts/media.chip32");
// f.seekg(0, std::ios::end);
// buffer.resize(f.tellg());
// f.seekg(0);
// f.read(buffer.data(), buffer.size());
// codeStr += buffer;
return true;
return retCode;
}
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)
@ -503,6 +501,40 @@ bool StoryProject::Load(ResourceManager &manager)
ModelFromJson(j);
m_initialized = true;
}
if (j.contains("variables"))
{
nlohmann::json variablesData = j["variables"];
for (const auto &obj : variablesData)
{
auto v = std::make_shared<Variable>(obj["label"].get<std::string>());
v->SetUuid(obj["uuid"].get<std::string>());
v->SetValueType(Variable::StringToValueType(obj["type"].get<std::string>()));
v->SetScalePower(obj["scale"].get<int>());
v->SetConstant(obj["constant"].get<bool>());
v->SetVariableName(obj["name"].get<std::string>());
if (v->IsFloat())
{
v->SetFloatValue(std::stof(obj["value"].get<std::string>()));
}
else if (v->IsInteger())
{
v->SetIntegerValue(std::stoi(obj["value"].get<std::string>()));
}
else if (v->IsString())
{
v->SetTextValue(obj["value"].get<std::string>());
}
else if (v->IsBool())
{
v->SetBoolValue(obj["value"].get<std::string>() == "true");
}
m_variables.push_back(v);
}
}
}
}
}
@ -544,6 +576,40 @@ void StoryProject::Save(ResourceManager &manager)
ModelToJson(model);
j["pages"] = model;
nlohmann::json variablesData;
for (const auto &v : m_variables)
{
std::string value;
if (v->IsFloat())
{
value = std::to_string(v->GetFloatValue());
}
else if (v->IsInteger())
{
value = std::to_string(v->GetIntegerValue());
}
else if (v->IsString())
{
value = v->GetStringValue();
}
else if (v->IsBool())
{
value = v->GetBoolValue() ? "true" : "false";
}
nlohmann::json obj = {{"name", v->GetVariableName()},
{"label", v->GetLabel()},
{"uuid", v->GetUuid()},
{"value", value},
{"scale", v->GetScalePower()},
{"constant", v->IsConstant()},
{"type", Variable::ValueTypeToString(v->GetValueType())}};
variablesData.push_back(obj);
}
j["variables"] = variablesData;
std::ofstream o(m_project_file_path);
o << std::setw(4) << j << std::endl;
}
@ -555,6 +621,7 @@ void StoryProject::Clear()
m_working_dir = "";
m_project_file_path = "";
m_initialized = false;
m_variables.clear();
}

View file

@ -87,7 +87,7 @@ uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t code)
if (m_chip32_ctx.registers[R0] != 0)
{
// image file name address is in R0
// QString imageFile = m_model.BuildFullImagePath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
// QString imageFile = m_model.BuildFullImagePath(GetStringFromMemory(m_chip32_ctx.registers[R0]));
// m_ostHmiDock->SetImage(imageFile);
get_file_from_memory(ImageFile, m_chip32_ctx.registers[R0]);
fs_task_image_start(ImageFile);
@ -96,7 +96,7 @@ uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t code)
if (m_chip32_ctx.registers[R1] != 0)
{
// sound file name address is in R1
// QString soundFile = m_model.BuildFullSoundPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
// QString soundFile = m_model.BuildFullSoundPath(GetStringFromMemory(m_chip32_ctx.registers[R1]));
// qDebug() << ", Sound: " << soundFile;
// m_model.PlaySoundFile(soundFile);
get_file_from_memory(SoundFile, m_chip32_ctx.registers[R1]);

View file

@ -104,6 +104,21 @@ std::shared_ptr<StoryProject> LibraryManager::GetStory(const std::string &uuid)
return current;
}
void LibraryManager::RemoveStory(const std::string &uuid)
{
for (const auto &s : m_projectsList)
{
if (s->GetUuid() == uuid)
{
// Just rename the project file, keep the whole directory
auto oldname = s->GetProjectFilePath();
std::filesystem::rename(oldname, oldname + "__removed");
break;
}
}
Scan();
}
std::string LibraryManager::IndexFileName() const
{
auto p = std::filesystem::path(m_library_path) / "index.ost";

View file

@ -45,6 +45,7 @@ public:
std::string GetStoreUrl() const { return m_storeUrl; }
void AddStory(IStoryDb::Info &info, int origin);
void RemoveStory(const std::string &uuid);
void ParseCommunityStore(const std::string &jsonFileName);
void ParseCommercialStore(const std::string &jsonFileName);

View file

@ -116,6 +116,30 @@ void BaseNodeWidget::FrameStart()
}
}
m_firstFrame = false;
// Title
const char * text = m_base->GetTypeName().c_str();
// Obtenir la position courante du curseur
ImVec2 pos = ImGui::GetCursorScreenPos();
// Définir les dimensions du texte
ImVec2 text_size = ImGui::CalcTextSize(text);
// Ajouter un padding autour du texte
float padding = 5.0f;
ImVec2 rect_min = ImVec2(pos.x - padding, pos.y - padding);
ImVec2 rect_max = ImVec2(pos.x + text_size.x + padding, pos.y + text_size.y + padding);
// Définir la couleur du rectangle (bleu avec transparence)
ImU32 bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 1.0f));
// Dessiner le rectangle de fond
ImGui::GetWindowDrawList()->AddRectFilled(rect_min, rect_max, bg_color);
// Afficher le texte
ImGui::TextUnformatted(text);
}
void BaseNodeWidget::FrameEnd()

View file

@ -481,6 +481,8 @@ void LibraryWindow::Draw()
);
}
std::string rmUuid;
if (ImGui::BeginTable("library_table", 3, tableFlags))
{
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
@ -539,11 +541,16 @@ void LibraryWindow::Draw()
ImGui::SameLine();
if (ImGui::SmallButton("Remove"))
{
rmUuid = p->GetUuid();
}
ImGui::PopID();
}
ImGui::EndTable();
if (!rmUuid.empty())
{
m_libraryManager.RemoveStory(rmUuid);
}
}
ImGui::EndTabItem();
}

View file

@ -64,7 +64,7 @@ MainWindow::~MainWindow()
}
std::string MainWindow::GetFileNameFromMemory(uint32_t addr)
std::string MainWindow::GetStringFromMemory(uint32_t addr)
{
char strBuf[100];
bool isRam = addr & 0x80000000;
@ -77,6 +77,7 @@ std::string MainWindow::GetFileNameFromMemory(uint32_t addr)
return strBuf;
}
void MainWindow::Play()
{
@ -268,7 +269,36 @@ void MainWindow::ProcessStory()
}
else if (m_dbg.run_result > VM_OK)
{
Log("VM critical error", true);
std::string error = "VM Error: ";
switch (m_dbg.run_result)
{
case VM_ERR_STACK_OVERFLOW:
error += "Stack overflow";
break;
case VM_ERR_STACK_UNDERFLOW:
error += "Stack underflow";
break;
case VM_ERR_INVALID_ADDRESS:
error += "Invalid address";
break;
case VM_ERR_UNSUPPORTED_OPCODE:
error += "Invalid address";
break;
case VM_ERR_UNKNOWN_OPCODE:
error += "Unknown opcode";
break;
case VM_ERR_UNHANDLED_INTERRUPT:
error += "Unhandled interrupt";
break;
case VM_ERR_INVALID_REGISTER:
error += "Invalid register";
break;
default:
error += "Unknown error";
break;
}
error += " (line: " + std::to_string(m_dbg.line) + ")";
Log(error, true);
}
// In this case, we wait for single step debugger
@ -289,7 +319,7 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
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]));
std::string imageFile = m_story->BuildFullAssetsPath(GetStringFromMemory(m_chip32_ctx.registers[R0]));
Log("Image: " + imageFile);
m_emulatorWindow.SetImage(imageFile);
}
@ -301,7 +331,7 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
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]));
std::string soundFile = m_story->BuildFullAssetsPath(GetStringFromMemory(m_chip32_ctx.registers[R1]));
Log("Sound: " + soundFile);
m_player.Play(soundFile);
}
@ -325,7 +355,52 @@ uint8_t MainWindow::Syscall(chip32_ctx_t *ctx, uint8_t code)
// if timeout is set to zero, wait for infinite and beyond
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
}
else if (code == 3)
{
// FIXME
}
else // Printf
if (code == 4)
{
// In R0: string with escaped characters
// R1: Number of arguments
// R2, R3 ... arguments
// Integers: stored in registers by values
// Strings: first character address in register
std::string text = GetStringFromMemory(ctx->registers[R0]);
int arg_count = ctx->registers[R1];
char working_buf[200] = {0};
switch(arg_count){
case 0:
Log(text);
break;
case 1:
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]);
Log(working_buf);
break;
case 2:
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3]);
Log(working_buf);
break;
case 3:
snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]);
Log(working_buf);
break;
default:
break;
}
}
// WAIT (sleep)
else if (code == 5)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ctx->registers[R0]));
}
return retCode;
}
@ -849,11 +924,11 @@ void MainWindow::RefreshProjectInformation()
void MainWindow::CloseProject()
{
// if (m_story)
// {
// m_story->Clear();
// m_story.reset();
// }
if (m_story)
{
m_story->Clear();
m_story.reset();
}
m_resources.Clear();

View file

@ -188,7 +188,7 @@ private:
void UpdateVmView();
uint8_t Syscall(chip32_ctx_t *ctx, uint8_t code);
std::string GetFileNameFromMemory(uint32_t addr);
std::string GetStringFromMemory(uint32_t addr);
void ProcessStory();
void StepInstruction();
void RefreshProjectInformation();

View file

@ -15,8 +15,6 @@ VariablesWindow::VariablesWindow(IStoryManager &proj)
void VariablesWindow::Initialize()
{
}