From 1adb58742a8e1395ef9223890d7c0888c44dea18 Mon Sep 17 00:00:00 2001 From: Anthony Rabine Date: Wed, 31 May 2023 08:47:31 +0200 Subject: [PATCH] First working version of the software player --- .gitignore | 2 + README.md | 13 +- {images => art}/ost-1/audio_amplifier.png | Bin {images => art}/ost-1/audio_board.webp | Bin {images => art}/ost-1/complete.png | Bin {images => art}/ost-1/langan-nano.jpg | Bin {images => art}/ost-1/speaker_4ohms_3w.png | Bin {images => art}/story_editor_preview.png | Bin art/story_player.png | Bin 0 -> 56376 bytes software/chip32/chip32_vm.c | 2 +- software/chip32/chip32_vm.h | 8 +- story-player/CMakeLists.txt | 14 +- story-player/CMakeLists.txt.user | 434 --------------------- story-player/gui_file_dialog.h | 83 +++- story-player/main.c | 259 +++++++++++- 15 files changed, 350 insertions(+), 465 deletions(-) rename {images => art}/ost-1/audio_amplifier.png (100%) rename {images => art}/ost-1/audio_board.webp (100%) rename {images => art}/ost-1/complete.png (100%) rename {images => art}/ost-1/langan-nano.jpg (100%) rename {images => art}/ost-1/speaker_4ohms_3w.png (100%) rename {images => art}/story_editor_preview.png (100%) create mode 100644 art/story_player.png delete mode 100644 story-player/CMakeLists.txt.user diff --git a/.gitignore b/.gitignore index abe5c0a..f05c3c0 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ build-story-editor-Desktop_Qt_6_5_1_GCC_64bit-Release/ build-story-player-Desktop_Qt_6_5_1_GCC_64bit-Debug/ build-story-player-Desktop_Qt_GCC_64bit-Debug/ + +*.user diff --git a/README.md b/README.md index 89f143e..39cd0c5 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Current status: We propose a basic editor tool to create your own stories. The generated story script runs on our micro virtual machine and allow generate complex stories. -![editor](images/story_editor_preview.png) +![editor](art/story_editor_preview.png) Work in progress: - Project management @@ -57,6 +57,17 @@ Planned nodes: - Loop - Conditional +# Story Player + +The Story plater is a purely software implementation of a simple story player. It is provided as an example and a test device for the micro virtual machine. It is very portable and should run on a large number of platforms. + +![editor](art/story_player.png) + +Technolologies used: +- C language +- Raylib for graphics and sounds +- CMake build system + # License MIT License diff --git a/images/ost-1/audio_amplifier.png b/art/ost-1/audio_amplifier.png similarity index 100% rename from images/ost-1/audio_amplifier.png rename to art/ost-1/audio_amplifier.png diff --git a/images/ost-1/audio_board.webp b/art/ost-1/audio_board.webp similarity index 100% rename from images/ost-1/audio_board.webp rename to art/ost-1/audio_board.webp diff --git a/images/ost-1/complete.png b/art/ost-1/complete.png similarity index 100% rename from images/ost-1/complete.png rename to art/ost-1/complete.png diff --git a/images/ost-1/langan-nano.jpg b/art/ost-1/langan-nano.jpg similarity index 100% rename from images/ost-1/langan-nano.jpg rename to art/ost-1/langan-nano.jpg diff --git a/images/ost-1/speaker_4ohms_3w.png b/art/ost-1/speaker_4ohms_3w.png similarity index 100% rename from images/ost-1/speaker_4ohms_3w.png rename to art/ost-1/speaker_4ohms_3w.png diff --git a/images/story_editor_preview.png b/art/story_editor_preview.png similarity index 100% rename from images/story_editor_preview.png rename to art/story_editor_preview.png diff --git a/art/story_player.png b/art/story_player.png new file mode 100644 index 0000000000000000000000000000000000000000..32268105fadf52a73ebde093a9b2cca06324c899 GIT binary patch literal 56376 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 literal 0 HcmV?d00001 diff --git a/software/chip32/chip32_vm.c b/software/chip32/chip32_vm.c index 4d08d68..525bb3f 100644 --- a/software/chip32/chip32_vm.c +++ b/software/chip32/chip32_vm.c @@ -146,7 +146,7 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx) if (ctx->syscall != NULL) { - if (ctx->syscall(code) != 0) + if (ctx->syscall(ctx, code) != 0) { result = VM_WAIT_EVENT; } diff --git a/software/chip32/chip32_vm.h b/software/chip32/chip32_vm.h index 54e6abc..ebd93eb 100644 --- a/software/chip32/chip32_vm.h +++ b/software/chip32/chip32_vm.h @@ -185,13 +185,15 @@ typedef struct } virtual_mem_t; -typedef uint8_t (*syscall_t)(uint8_t); +typedef struct chip32_ctx_t chip32_ctx_t; + +typedef uint8_t (*syscall_t)(chip32_ctx_t *, uint8_t); #define SYSCALL_RET_OK 0 ///< Default state, continue execution immediately #define SYSCALL_RET_WAIT_EV 1 ///< Sets the VM in wait for event state -typedef struct +struct chip32_ctx_t { virtual_mem_t rom; virtual_mem_t ram; @@ -202,7 +204,7 @@ typedef struct uint32_t registers[REGISTER_COUNT]; syscall_t syscall; -} chip32_ctx_t; +}; // ======================================================================================= // VM RUN diff --git a/story-player/CMakeLists.txt b/story-player/CMakeLists.txt index 6f6b44c..7e24b86 100644 --- a/story-player/CMakeLists.txt +++ b/story-player/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.11) # FetchContent is available in 3.11+ -project(raylib_template) +project(story-player) # Generate compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -25,7 +25,17 @@ endif() # Our Project -add_executable(${PROJECT_NAME} main.c raygui.h) +add_executable(${PROJECT_NAME} + main.c + raygui.h + ../software/chip32/chip32_assembler.cpp + ../software/chip32/chip32_vm.c + ../software/chip32/chip32_assembler.h + ../software/chip32/chip32_vm.h +) +include_directories(../software/chip32) +include_directories(../software/library) + #set(raylib_VERBOSE 1) target_link_libraries(${PROJECT_NAME} raylib) diff --git a/story-player/CMakeLists.txt.user b/story-player/CMakeLists.txt.user deleted file mode 100644 index 5ea892c..0000000 --- a/story-player/CMakeLists.txt.user +++ /dev/null @@ -1,434 +0,0 @@ - - - - - - EnvironmentId - {75eff57d-71d0-4697-b143-c9d65b3913b6} - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - true - false - true - - Cpp - - CppGlobal - - - - QmlJS - - QmlJSGlobal - - - 2 - UTF-8 - false - 4 - false - 80 - true - true - 1 - false - true - false - 0 - true - true - 0 - 8 - true - false - 1 - true - true - true - *.md, *.MD, Makefile - false - true - true - - - - ProjectExplorer.Project.PluginSettings - - - true - false - true - true - true - true - - - 0 - true - - true - true - Builtin.DefaultTidyAndClazy - 6 - - - - true - - - - - ProjectExplorer.Project.Target.0 - - Desktop - Desktop Qt 6.5.1 GCC 64bit - Desktop Qt 6.5.1 GCC 64bit - qt.qt6.651.gcc_64_kit - 0 - 0 - 0 - - Debug - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=Debug --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - 0 - /mnt/data/git/open-story-teller/build-story-player-Desktop_Qt_6_5_1_GCC_64bit-Debug - - - - - all - - false - - true - Compilation - CMakeProjectManager.MakeStep - - 1 - Compiler - Compiler - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - Compilation - CMakeProjectManager.MakeStep - - 1 - Nettoyer - Nettoyer - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Debug - CMakeProjectManager.CMakeBuildConfiguration - - - Release - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=Release --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - /mnt/data/git/open-story-teller/build-story-player-Desktop_Qt_6_5_1_GCC_64bit-Release - - - - - all - - false - - true - CMakeProjectManager.MakeStep - - 1 - Compiler - Compiler - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - CMakeProjectManager.MakeStep - - 1 - Nettoyer - Nettoyer - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Release - CMakeProjectManager.CMakeBuildConfiguration - - - RelWithDebInfo - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - /mnt/data/git/open-story-teller/build-story-player-Desktop_Qt_6_5_1_GCC_64bit-RelWithDebInfo - - - - - all - - false - - true - CMakeProjectManager.MakeStep - - 1 - Compiler - Compiler - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - CMakeProjectManager.MakeStep - - 1 - Nettoyer - Nettoyer - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Release with Debug Information - CMakeProjectManager.CMakeBuildConfiguration - - - RelWithDebInfo - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - 0 - /mnt/data/git/open-story-teller/build-story-player-Desktop_Qt_6_5_1_GCC_64bit-Profile - - - - - all - - false - - true - CMakeProjectManager.MakeStep - - 1 - Compiler - Compiler - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - CMakeProjectManager.MakeStep - - 1 - Nettoyer - Nettoyer - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Profile - CMakeProjectManager.CMakeBuildConfiguration - - - MinSizeRel - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=MinSizeRel --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - /mnt/data/git/open-story-teller/build-story-player-Desktop_Qt_6_5_1_GCC_64bit-MinSizeRel - - - - - all - - false - - true - CMakeProjectManager.MakeStep - - 1 - Compiler - Compiler - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - CMakeProjectManager.MakeStep - - 1 - Nettoyer - Nettoyer - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Minimum Size Release - CMakeProjectManager.CMakeBuildConfiguration - - 5 - - - 0 - Déploiement - Déploiement - ProjectExplorer.BuildSteps.Deploy - - 1 - - false - ProjectExplorer.DefaultDeployConfiguration - - 1 - - true - true - true - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - - - 2 - - raylib_template - CMakeProjectManager.CMakeRunConfiguration.raylib_template - raylib_template - false - true - true - false - true - /mnt/data/git/open-story-teller/story-player - /mnt/data/git/open-story-teller/build-story-player-Desktop_Qt_6_5_1_GCC_64bit-Debug - - 1 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.FileVersion - 22 - - - Version - 22 - - diff --git a/story-player/gui_file_dialog.h b/story-player/gui_file_dialog.h index e593d8a..a6043ec 100644 --- a/story-player/gui_file_dialog.h +++ b/story-player/gui_file_dialog.h @@ -270,7 +270,7 @@ void GuiFileDialog(GuiFileDialogState *state) state->windowActive = !GuiWindowBox(state->windowBounds, "#198# Select File Dialog"); // Draw previous directory button + logic - if (GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 48, state->windowBounds.y + 24 + 12, 40, 24 }, "< ..")) + if (GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 48, state->windowBounds.y + 24 + 12, 40, 24 }, "#117#")) { // Move dir path one level up strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText)); @@ -345,7 +345,7 @@ void GuiFileDialog(GuiFileDialogState *state) // Draw bottom controls //-------------------------------------------------------------------------------------- GuiLabel((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 68, 60, 24 }, "File name:"); - if (GuiTextBox((Rectangle){ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 68, state->windowBounds.width - 184, 24 }, state->fileNameText, 128, state->fileNameEditMode)) + if (GuiTextBox((Rectangle){ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 68, state->windowBounds.width - 80, 24 }, state->fileNameText, 128, state->fileNameEditMode)) { if (*state->fileNameText) { @@ -372,10 +372,7 @@ void GuiFileDialog(GuiFileDialogState *state) state->fileNameEditMode = !state->fileNameEditMode; } - GuiLabel((Rectangle){ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 24 - 12, 68, 24 }, "File filter:"); - GuiComboBox((Rectangle){ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 24 - 12, state->windowBounds.width - 184, 24 }, "All files", &state->fileTypeActive); - - state->SelectFilePressed = GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 96 - 8, state->windowBounds.y + state->windowBounds.height - 68, 96, 24 }, "Select"); + state->SelectFilePressed = GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 96 *2 - 8*2, state->windowBounds.y + state->windowBounds.height - 24 - 12, 96, 24 }, "Select"); if (GuiButton((Rectangle){ state->windowBounds.x + state->windowBounds.width - 96 - 8, state->windowBounds.y + state->windowBounds.height - 24 - 12, 96, 24 }, "Cancel")) state->windowActive = false; //-------------------------------------------------------------------------------------- @@ -418,12 +415,84 @@ static inline int FileCompare(const char *d1, const char *d2, const char *dir) return strcmp(d1, d2); } + +#include +#include +#include +#include + +int is_regular_file(const char *path) +{ + struct stat path_stat; + stat(path, &path_stat); + return S_ISREG(path_stat.st_mode); +} + +#define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath +#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) + + +// Scan all files and directories in a base path +// WARNING: files.paths[] must be previously allocated and +// contain enough space to store all required paths +static void MyScanDirectoryFiles(const char *basePath, FilePathList *files, const char *filter) +{ + static char path[MAX_FILEPATH_LENGTH] = { 0 }; + memset(path, 0, MAX_FILEPATH_LENGTH); + + struct dirent *dp = NULL; + DIR *dir = opendir(basePath); + + if (dir != NULL) + { + while ((dp = readdir(dir)) != NULL) + { + if ((strcmp(dp->d_name, ".") != 0) && + (strcmp(dp->d_name, "..") != 0) && (dp->d_name[0] != '.')) + { + sprintf(path, "%s/%s", basePath, dp->d_name); + + if ((filter != NULL) && (is_regular_file(path))) + { + if (IsFileExtension(path, filter)) + { + strcpy(files->paths[files->count], path); + files->count++; + } + } + else + { + strcpy(files->paths[files->count], path); + files->count++; + } + } + } + + closedir(dir); + } +} + + +FilePathList MyLoadDirectoryFilesEx(const char *basePath, const char *filter) +{ + FilePathList files = { 0 }; + + files.capacity = MAX_FILEPATH_CAPACITY; + files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *)); + for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + + // WARNING: basePath is always prepended to scanned paths + MyScanDirectoryFiles(basePath, &files, filter); + + return files; +} + // Read files in new path static void ReloadDirectoryFiles(GuiFileDialogState *state) { UnloadDirectoryFiles(state->dirFiles); - state->dirFiles = LoadDirectoryFilesEx(state->dirPathText, (state->filterExt[0] == '\0')? NULL : state->filterExt, false); + state->dirFiles = MyLoadDirectoryFilesEx(state->dirPathText, (state->filterExt[0] == '\0')? NULL : state->filterExt); state->itemFocused = 0; // Reset dirFilesIcon memory diff --git a/story-player/main.c b/story-player/main.c index 920b876..6da16d3 100644 --- a/story-player/main.c +++ b/story-player/main.c @@ -24,11 +24,177 @@ #define GUI_FILE_DIALOG_IMPLEMENTATION #include "gui_file_dialog.h" +#include "chip32_vm.h" +#include + +int set_filename_from_memory(chip32_ctx_t *ctx, uint32_t addr, char *filename_mem) +{ + int valid = 0; + + // Test if address is valid + + bool isRam = addr & 0x80000000; + addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing + if (isRam) { + strcpy(&filename_mem[0], (const char *)&ctx->ram.mem[addr]); + } else { + strcpy(&filename_mem[0], (const char *)&ctx->rom.mem[addr]); + } + + return valid; +} + + +chip32_result_t vm_load_script(chip32_ctx_t *ctx, const char *filename) +{ + chip32_result_t run_result = VM_FINISHED; + FILE *fp = fopen(filename, "rb"); + if (fp != NULL) + { + fseek(fp, 0L, SEEK_END); + long int sz = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + if (sz <= ctx->rom.size) + { + fread(ctx->rom.mem, sz, 1, fp); + run_result = VM_OK; + chip32_initialize(ctx); + } + fclose(fp); + } + return run_result; +} + +#define MAX_PATH 260 + +void get_home_path(char *homedir) +{ +#ifdef _WIN32 + snprintf(homedir, MAX_PATH, "%s%s", getenv("HOMEDRIVE"), getenv("HOMEPATH")); +#else + snprintf(homedir, MAX_PATH, "%s", getenv("HOME")); +#endif +} + + +void get_parent_dir(const char *path, char *parent) +{ + int parentLen; + char* last = strrchr(path, '/'); + + if (last != NULL) { + + parentLen = strlen(path) - strlen(last + 1); + strncpy(parent, path, parentLen); + } +} + + +static Music gMusic; +static char root_dir[260]; +static bool gMusicLoaded = false; + +static Texture texture = { 0 }; + +#define EV_BUTTON_OK 0x01 +#define EV_BUTTON_LEFT 0x02 +#define EV_BUTTON_RIGHT 0x04 + +uint8_t story_player_syscall(chip32_ctx_t *ctx, uint8_t code) +{ + uint8_t retCode = SYSCALL_RET_OK; + + static char image_path[260]; + static char sound_path[260]; + + if (code == 1) // // Execute media + { + printf("SYSCALL 1\n"); + fflush(stdout); +// UnloadTexture(*tex); +// *tex = + + if (ctx->registers[R0] != 0) + { + // sound file name address is in R1 + char image[100]; + set_filename_from_memory(ctx, ctx->registers[R0], image); + + strcpy(image_path, root_dir); + strcat(image_path, "images/"); + strcat(image_path, image); + + texture = LoadTexture(image_path); + } + else + { + UnloadTexture(texture); + } + + + if (ctx->registers[R1] != 0) + { + // sound file name address is in R1 + char sound[100]; + set_filename_from_memory(ctx, ctx->registers[R1], sound); + + strcpy(sound_path, root_dir); + strcat(sound_path, "sounds/"); + strcat(sound_path, sound); + + gMusic = LoadMusicStream(sound_path); + gMusic.looping = false; + gMusicLoaded = true; + + if (IsMusicReady(gMusic)) + { + PlayMusicStream(gMusic); + } + } + retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause + } + else if (code == 2) // Wait Event + { + printf("SYSCALL 2\n"); + fflush(stdout); + retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause + } + return retCode; +} + + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ int main() { + // VM Stuff + //--------------------------------------------------------------------------------------- + uint8_t rom_data[16*1024]; + uint8_t ram_data[16*1024]; + chip32_ctx_t chip32_ctx; + + chip32_ctx.stack_size = 512; + + chip32_ctx.rom.mem = rom_data; + chip32_ctx.rom.addr = 0; + chip32_ctx.rom.size = sizeof(rom_data); + + chip32_ctx.ram.mem = ram_data; + chip32_ctx.ram.addr = sizeof(rom_data); + chip32_ctx.ram.size = sizeof(ram_data); + + chip32_ctx.syscall = story_player_syscall; + + chip32_result_t run_result = VM_FINISHED; + + // Directories + //--------------------------------------------------------------------------------------- + char homedir[MAX_PATH]; + + get_home_path(homedir); + // Initialization //--------------------------------------------------------------------------------------- int screenWidth = 800; @@ -41,7 +207,6 @@ int main() GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0x133D42ff); GuiSetStyle(DEFAULT, TEXT_SIZE, 14); - GuiSetIconScale(3); GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x6DBFB2ff); GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0xffffffff); @@ -53,16 +218,18 @@ int main() GuiSetFont(fontTtf); - // Custom file dialog GuiFileDialogState fileDialogState = InitGuiFileDialog(GetWorkingDirectory()); - Texture2D logoTexture = LoadTexture("logo-color2.png"); // Texture loading + strcpy(fileDialogState.filterExt, ".c32"); + strcpy(fileDialogState.dirPathText, homedir); + Texture2D logoTexture = LoadTexture("logo-color2.png"); bool exitWindow = false; char fileNameToLoad[512] = { 0 }; - Texture texture = { 0 }; + InitAudioDevice(); + UnloadMusicStream(gMusic); SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -76,16 +243,35 @@ int main() if (fileDialogState.SelectFilePressed) { - // Load image file (if supported extension) if (IsFileExtension(fileDialogState.fileNameText, ".c32")) { strcpy(fileNameToLoad, TextFormat("%s/%s", fileDialogState.dirPathText, fileDialogState.fileNameText)); - UnloadTexture(texture); - texture = LoadTexture(fileNameToLoad); + run_result = vm_load_script(&chip32_ctx, fileNameToLoad); + get_parent_dir(fileNameToLoad, root_dir); + printf("Root directory: %s\n", root_dir); } fileDialogState.SelectFilePressed = false; } + + // VM next instruction + if (run_result == VM_OK) + { + run_result = chip32_step(&chip32_ctx); + } + + if (gMusicLoaded) + { + UpdateMusicStream(gMusic); + if (!IsMusicStreamPlaying(gMusic)) + { + StopMusicStream(gMusic); + UnloadMusicStream(gMusic); + gMusicLoaded = false; + run_result = VM_OK; // continue VM execution + } + } + //---------------------------------------------------------------------------------- // Draw @@ -94,39 +280,76 @@ int main() ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); + DrawTextureEx(logoTexture, (Vector2){ (float)10, (float)10 }, 0, 0.15, WHITE); - DrawTexture(texture, GetScreenWidth()/2 - texture.width/2, GetScreenHeight()/2 - texture.height/2 - 5, WHITE); -// DrawRectangleLines(GetScreenWidth()/2 - texture.width/2, GetScreenHeight()/2 - texture.height/2 - 5, texture.width, texture.height, BLACK); + // Image de l'histoire + DrawRectangle(220, 25, 320, 240, WHITE); + DrawTexture(texture, 220, 25, WHITE); -// DrawText(fileNameToLoad, 208, GetScreenHeight() - 20, 10, GRAY); + GuiSetIconScale(3); - // raygui: controls drawing - //---------------------------------------------------------------------------------- - if (fileDialogState.windowActive) GuiLock(); + if (GuiButton((Rectangle){ 20, 140, 60, 60 }, "#05#")) + { + fileDialogState.windowActive = true; + } - if (GuiButton((Rectangle){ 20, 140, 60, 60 }, "#05#")) fileDialogState.windowActive = true; - - // Pause ICON_PLAYER_PAUSE + // ICON_PLAYER_PAUSE if (GuiButton((Rectangle){ 20 + 65, 140, 60, 60 }, "#132#")) { } - // House ICON_HOUSE + // ICON_HOUSE if (GuiButton((Rectangle){ 20 + 2*65, 140, 60, 60 }, "#185#")) { } + // ICON_ARROW_LEFT + if (GuiButton((Rectangle){ 20, 205, 60, 60 }, "#114#")) + { + if (run_result == VM_WAIT_EVENT) + { + chip32_ctx.registers[R0] = EV_BUTTON_LEFT; + run_result = VM_OK; + } + } + + // ICON_OK_TICK + if (GuiButton((Rectangle){ 20 + 65, 205, 60, 60 }, "#112#")) + { + if (run_result == VM_WAIT_EVENT) + { + chip32_ctx.registers[R0] = EV_BUTTON_OK; + run_result = VM_OK; + } + } + + // ICON_ARROW_RIGHT + if (GuiButton((Rectangle){ 20 + 2*65, 205, 60, 60 }, "#115#")) + { + if (run_result == VM_WAIT_EVENT) + { + chip32_ctx.registers[R0] = EV_BUTTON_RIGHT; + run_result = VM_OK; + } + } + + // raygui: controls drawing + //---------------------------------------------------------------------------------- + + if (fileDialogState.windowActive) GuiLock(); GuiUnlock(); // GUI: Dialog Window //-------------------------------------------------------------------------------- + GuiSetIconScale(1); GuiFileDialog(&fileDialogState); //-------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------- EndDrawing(); @@ -135,8 +358,10 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- + UnloadTexture(logoTexture); // Unload texture UnloadTexture(texture); // Unload texture + CloseAudioDevice(); CloseWindow(); // Close window and OpenGL context //--------------------------------------------------------------------------------------