#include "bf_dev_env.h" #include typedef struct BFDevEnv { View* view; DevEnvOkCallback callback; void* context; BFApp* appDev; } BFDevEnv; typedef struct { uint32_t row; uint32_t col; } BFDevEnvModel; typedef struct { int up; int down; int left; int right; } bMapping; #ifdef FW_ORIGIN_Official #define FONT_NAME FontSecondary #else #define FONT_NAME FontBatteryPercent #endif static bool bf_dev_process_up(BFDevEnv* devEnv); static bool bf_dev_process_down(BFDevEnv* devEnv); static bool bf_dev_process_left(BFDevEnv* devEnv); static bool bf_dev_process_right(BFDevEnv* devEnv); static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event); BFApp* appDev; FuriThread* workerThread; char bfChars[9] = {'<', '>', '[', ']', '+', '-', '.', ',', 0x00}; int selectedButton = 0; int saveNotifyCountdown = 0; int execCountdown = 0; char dspLine0[25] = {0x00}; char dspLine1[25] = {0x00}; char dspLine2[25] = {0x00}; static bMapping buttonMappings[12] = { {8, 8, 7, 1}, //0 {8, 8, 0, 2}, //1 {9, 9, 1, 3}, //2 {9, 9, 2, 4}, //3 {10, 10, 3, 5}, //4 {10, 10, 4, 6}, //5 {11, 11, 5, 7}, //6 {11, 11, 6, 0}, //7 {0, 0, 11, 9}, //8 {3, 3, 8, 10}, //9 {5, 5, 9, 11}, //10 {6, 6, 10, 8} //11 }; #define BT_X 14 #define BT_Y 14 static void bf_dev_draw_button(Canvas* canvas, int x, int y, bool selected, const char* lbl) { UNUSED(lbl); if(selected) { canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3); canvas_invert_color(canvas); canvas_set_font(canvas, FONT_NAME); canvas_draw_str_aligned( canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl); canvas_invert_color(canvas); } else { canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3); canvas_invert_color(canvas); canvas_draw_rbox(canvas, x + 2, y - 1, BT_X - 2, BT_Y - 1, 3); canvas_invert_color(canvas); canvas_draw_rframe(canvas, x, y, BT_X, BT_Y, 3); canvas_set_font(canvas, FONT_NAME); canvas_draw_str_aligned( canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl); } } void bf_save_changes() { //remove old file Storage* storage = furi_record_open(RECORD_STORAGE); storage_simply_remove(storage, furi_string_get_cstr(appDev->BF_file_path)); //save new file Stream* stream = buffered_file_stream_alloc(storage); buffered_file_stream_open( stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS); stream_write(stream, (const uint8_t*)appDev->dataBuffer, appDev->dataSize); buffered_file_stream_close(stream); } static void bf_dev_draw_callback(Canvas* canvas, void* _model) { UNUSED(_model); if(saveNotifyCountdown > 0) { canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "SAVED"); saveNotifyCountdown--; return; } bf_dev_draw_button(canvas, 1, 36, (selectedButton == 0), "+"); //T 0 bf_dev_draw_button(canvas, 17, 36, (selectedButton == 1), "-"); //T 1 bf_dev_draw_button(canvas, 33, 36, (selectedButton == 2), "<"); //T 2 bf_dev_draw_button(canvas, 49, 36, (selectedButton == 3), ">"); //T 3 bf_dev_draw_button(canvas, 65, 36, (selectedButton == 4), "["); //B 0 bf_dev_draw_button(canvas, 81, 36, (selectedButton == 5), "]"); //B 1 bf_dev_draw_button(canvas, 97, 36, (selectedButton == 6), "."); //B 2 bf_dev_draw_button(canvas, 113, 36, (selectedButton == 7), ","); //B 3 //backspace, input, run, save canvas_draw_icon( canvas, 1, 52, (selectedButton == 8) ? &I_KeyBackspaceSelected_24x11 : &I_KeyBackspace_24x11); canvas_draw_icon( canvas, 45, 52, (selectedButton == 9) ? &I_KeyInputSelected_30x11 : &I_KeyInput_30x11); canvas_draw_icon( canvas, 77, 52, (selectedButton == 10) ? &I_KeyRunSelected_24x11 : &I_KeyRun_24x11); canvas_draw_icon( canvas, 103, 52, (selectedButton == 11) ? &I_KeySaveSelected_24x11 : &I_KeySave_24x11); if(saveNotifyCountdown > 0) { canvas_draw_icon(canvas, 98, 54, &I_ButtonRightSmall_3x5); saveNotifyCountdown--; } //textbox //grossly overcomplicated. not fixing it. canvas_draw_rframe(canvas, 1, 1, 126, 33, 2); canvas_set_font(canvas, FONT_NAME); int dbOffset = 0; if(appDev->dataSize > 72) { dbOffset = (appDev->dataSize - 72); } memset(dspLine0, 0x00, 25); memset(dspLine1, 0x00, 25); memset(dspLine2, 0x00, 25); int tpM = 0; int tp0 = 0; int tp1 = 0; int tp2 = 0; for(int p = dbOffset; p < appDev->dataSize; p++) { if(tpM < 24 * 1) { dspLine0[tp0] = appDev->dataBuffer[p]; tp0++; } else if(tpM < 24 * 2) { dspLine1[tp1] = appDev->dataBuffer[p]; tp1++; } else if(tpM < 24 * 3) { dspLine2[tp2] = appDev->dataBuffer[p]; tp2++; } tpM++; } canvas_draw_str_aligned(canvas, 3, 8, AlignLeft, AlignCenter, dspLine0); canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignCenter, dspLine1); canvas_draw_str_aligned(canvas, 3, 26, AlignLeft, AlignCenter, dspLine2); } static bool bf_dev_input_callback(InputEvent* event, void* context) { furi_assert(context); BFDevEnv* devEnv = context; bool consumed = false; if(event->type == InputTypeShort) { if(event->key == InputKeyRight) { consumed = bf_dev_process_right(devEnv); } else if(event->key == InputKeyLeft) { consumed = bf_dev_process_left(devEnv); } else if(event->key == InputKeyUp) { consumed = bf_dev_process_up(devEnv); } else if(event->key == InputKeyDown) { consumed = bf_dev_process_down(devEnv); } } else if(event->key == InputKeyOk) { consumed = bf_dev_process_ok(devEnv, event); } return consumed; } static bool bf_dev_process_up(BFDevEnv* devEnv) { UNUSED(devEnv); selectedButton = buttonMappings[selectedButton].up; return true; } static bool bf_dev_process_down(BFDevEnv* devEnv) { UNUSED(devEnv); selectedButton = buttonMappings[selectedButton].down; return true; } static bool bf_dev_process_left(BFDevEnv* devEnv) { UNUSED(devEnv); selectedButton = buttonMappings[selectedButton].left; return true; } static bool bf_dev_process_right(BFDevEnv* devEnv) { UNUSED(devEnv); selectedButton = buttonMappings[selectedButton].right; return true; } static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event) { UNUSED(devEnv); UNUSED(event); if(event->type != InputTypePress) { return false; } switch(selectedButton) { case 0: { if(appDev->dataSize < BF_INST_BUFFER_SIZE) { appDev->dataBuffer[appDev->dataSize] = '+'; appDev->dataSize++; } break; } case 1: { if(appDev->dataSize < BF_INST_BUFFER_SIZE) { appDev->dataBuffer[appDev->dataSize] = '-'; appDev->dataSize++; } break; } case 2: { if(appDev->dataSize < BF_INST_BUFFER_SIZE) { appDev->dataBuffer[appDev->dataSize] = '<'; appDev->dataSize++; } break; } case 3: { if(appDev->dataSize < BF_INST_BUFFER_SIZE) { appDev->dataBuffer[appDev->dataSize] = '>'; appDev->dataSize++; } break; } case 4: { if(appDev->dataSize < BF_INST_BUFFER_SIZE) { appDev->dataBuffer[appDev->dataSize] = '['; appDev->dataSize++; } break; } case 5: { if(appDev->dataSize < BF_INST_BUFFER_SIZE) { appDev->dataBuffer[appDev->dataSize] = ']'; appDev->dataSize++; } break; } case 6: { if(appDev->dataSize < BF_INST_BUFFER_SIZE) { appDev->dataBuffer[appDev->dataSize] = '.'; appDev->dataSize++; } break; } case 7: { if(appDev->dataSize < BF_INST_BUFFER_SIZE) { appDev->dataBuffer[appDev->dataSize] = ','; appDev->dataSize++; } break; } case 8: { if(appDev->dataSize > 0) { appDev->dataSize--; appDev->dataBuffer[appDev->dataSize] = (uint32_t)0x00; } break; } case 9: { scene_manager_next_scene(appDev->scene_manager, brainfuckSceneSetInput); break; } case 10: { if(getStatus() != 0) { killThread(); furi_thread_join(workerThread); } bf_save_changes(); initWorker(appDev); text_box_set_focus(appDev->text_box, TextBoxFocusEnd); text_box_set_text(appDev->text_box, workerGetOutput()); workerThread = furi_thread_alloc_ex("Worker", 2048, (void*)beginWorker, NULL); furi_thread_start(workerThread); scene_manager_next_scene(appDev->scene_manager, brainfuckSceneExecEnv); break; } case 11: { bf_save_changes(); saveNotifyCountdown = 3; break; } } bool consumed = false; return consumed; } static void bf_dev_enter_callback(void* context) { furi_assert(context); BFDevEnv* devEnv = context; with_view_model( devEnv->view, BFDevEnvModel * model, { model->col = 0; model->row = 0; }, true); appDev = devEnv->appDev; selectedButton = 0; //exit the running thread if required if(getStatus() != 0) { killThread(); furi_thread_join(workerThread); } //clear the bf instruction buffer memset(appDev->dataBuffer, 0x00, BF_INST_BUFFER_SIZE * sizeof(char)); //open the file Storage* storage = furi_record_open(RECORD_STORAGE); Stream* stream = buffered_file_stream_alloc(storage); buffered_file_stream_open( stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_READ, FSOM_OPEN_EXISTING); //read into the buffer appDev->dataSize = stream_size(stream); if(appDev->dataSize > 2000) { return; //BF file is too large } stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize); buffered_file_stream_close(stream); //replaces any invalid characters with an underscore. strips out newlines, comments, etc for(int i = 0; i < appDev->dataSize; i++) { if(!strchr(bfChars, appDev->dataBuffer[i])) { appDev->dataBuffer[i] = '_'; } } //find the end of the file to begin editing int tptr = 0; while(appDev->dataBuffer[tptr] != 0x00) { tptr++; } appDev->dataSize = tptr; } BFDevEnv* bf_dev_env_alloc(BFApp* appDev) { BFDevEnv* devEnv = malloc(sizeof(BFDevEnv)); devEnv->view = view_alloc(); devEnv->appDev = appDev; view_allocate_model(devEnv->view, ViewModelTypeLocking, sizeof(BFDevEnvModel)); with_view_model( devEnv->view, BFDevEnvModel * model, { model->col = 0; model->row = 0; }, true); view_set_context(devEnv->view, devEnv); view_set_draw_callback(devEnv->view, bf_dev_draw_callback); view_set_input_callback(devEnv->view, bf_dev_input_callback); view_set_enter_callback(devEnv->view, bf_dev_enter_callback); return devEnv; } void bf_dev_env_free(BFDevEnv* devEnv) { if(getStatus() != 0) { killThread(); furi_thread_join(workerThread); } furi_assert(devEnv); view_free(devEnv->view); free(devEnv); } View* bf_dev_env_get_view(BFDevEnv* devEnv) { furi_assert(devEnv); return devEnv->view; }