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 0000000..3226810 Binary files /dev/null and b/art/story_player.png differ 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 //--------------------------------------------------------------------------------------