#include #include #include #include #include #include #include #include #include #include #include /* generated by fbt from .png files in images folder */ #include #include "WString.h" typedef enum { O_READ = FSAM_READ, O_WRITE = FSAM_WRITE, O_CREAT = ((uint16_t)FSOM_OPEN_ALWAYS << 8), O_APPEND = ((uint16_t)FSOM_OPEN_APPEND << 8), O_EXCL = ((uint16_t)FSOM_CREATE_NEW << 8), O_TRUNC = ((uint16_t)FSOM_CREATE_ALWAYS << 8), } FileOpenMode; #define SD_CARD_ERROR_ACMD41 0x16 class SdFs { public: bool begin(int) { m_storage = (Storage*)furi_record_open(RECORD_STORAGE); return m_storage != nullptr; } bool mkdir(const char* path) { return storage_simply_mkdir(m_storage, path); } ~SdFs() { if(m_storage) furi_record_close(RECORD_STORAGE); } uint8_t sdErrorCode() const { return 0; } uint8_t sdErrorData() const { return 0; } private: Storage* m_storage = nullptr; friend class SdFile; }; template static inline void printSdErrorSymbol(T*, uint8_t) { } class SdFile { public: ~SdFile() { if(m_file) storage_file_free(m_file); } void close() { if(m_file) storage_file_close(m_file); } bool open(SdFs* from, const char* path, uint16_t mode) { m_fs = from; initFile(); m_path = path; return storage_file_open( m_file, path, FS_AccessMode((uint16_t)mode & 0xFF), FS_OpenMode((uint16_t)mode >> 8)); } bool open(SdFs* from, const char* path, FileOpenMode mode) { return open(from, path, (uint16_t)mode); } void getName(char* dest, uint16_t size) { m_path.toCharArray(dest, size); } unsigned read(void* buff, int bytes_to_read) { return storage_file_read(m_file, buff, bytes_to_read); } unsigned write(const void* buff, int bytes_to_read) { return storage_file_write(m_file, buff, bytes_to_read); } bool available() { return !storage_file_eof(m_file); } uint64_t fileSize() { return storage_file_size(m_file); } uint64_t curPosition() { return storage_file_tell(m_file); } bool seekSet(uint32_t pos) { return storage_file_seek(m_file, pos, true); } bool isOpen() { return m_file && storage_file_is_open(m_file); } bool openNext(SdFile* from) { char name[64]; bool ret = storage_dir_read(from->m_file, NULL, name, sizeof(name)); if(!ret) return false; String path = from->m_path; path += "/"; path += name; return open(from->m_fs, path.c_str(), O_READ); } bool rewind() { return false; // return storage_dir_rewind(m_file); } private: void initFile() { close(); if(!m_file) m_file = storage_file_alloc(m_fs->m_storage); } File* m_file = nullptr; SdFs* m_fs = nullptr; String m_path; }; #define F(x) x #define noInterrupts() uint32_t intLevel = ulPortRaiseBASEPRI() #define interrupts() vPortSetBASEPRI(intLevel) #define F_CPU /*SystemCoreClock*/ 64000000 typedef enum { HEX = 16 } BaseType; class SerialObj { public: template void print(const T&... v) { String out(v...); furi_hal_console_tx((uint8_t*)out.c_str(), out.length()); } void println() { furi_hal_console_tx((uint8_t*)"\r\n", 2); } template void println(const T&... v) { String out(v...); furi_hal_console_tx((uint8_t*)out.c_str(), out.length()); println(); } template void write(const T&... v) { print(v...); } int read() { return -1; /* uint8_t buf; if (!cli_read_timeout(m_cli, &buf, 1, 0)) return -1; return buf;*/ } bool begin() { /* m_cli = (Cli*)furi_record_open(RECORD_CLI); // if (m_cli) // cli_session_open(m_cli, &cli_vcp); */ return (bool)*this; } ~SerialObj() { if(m_cli) furi_record_close(RECORD_CLI); } explicit operator bool() { return true; // m_cli && cli_is_connected(m_cli); } void flush() { } private: Cli* m_cli = nullptr; }; #define SERIAL_BAUD_RATE #define BUILTIN_SDCARD 0 #define NO_CONSOLE_GPIO #define ARM_DWT_CYCCNT DWT->CYCCNT SerialObj Serial; typedef void (*ArduinoISR)(); void gpioCallback(void* ctx) { ArduinoISR cb = (ArduinoISR)ctx; cb(); } #define LOW false #define HIGH true typedef enum { INPUT = GpioModeInput, OUTPUT = GpioModeOutputOpenDrain, INPUT_PULLUP = (uint16_t)GpioModeInput | ((uint16_t)GpioPullUp << 8), INPUT_PULLDOWN = (uint16_t)GpioModeInput | ((uint16_t)GpioPullDown << 8), FALLING = (uint16_t)GpioModeInterruptFall | ((uint16_t)GpioPullUp << 8), } PinMode; void pinMode(const GpioPin* pin, PinMode mode) { furi_hal_gpio_init( pin, GpioMode((uint16_t)mode & 0xFF), GpioPull((uint16_t)mode >> 8), GpioSpeedVeryHigh); } void attachInterrupt(const GpioPin* pin, ArduinoISR cb, PinMode mode) { pinMode(pin, mode); furi_hal_gpio_remove_int_callback(pin); furi_hal_gpio_add_int_callback(pin, gpioCallback, (void*)cb); } static inline void digitalWrite(const GpioPin* pin, bool state) { furi_hal_gpio_write(pin, state); } static inline bool digitalRead(const GpioPin* pin) { return furi_hal_gpio_read(pin); } #define N64_PIN &gpio_ext_pc0 #define N64_PIN_PREFIX PC0 #define CONCAT1(a, b) a##b #define CONCAT(a, b) CONCAT1(a, b) #define N64_PORT CONCAT(N64_PIN_PREFIX, _GPIO_Port) #define N64_Pin CONCAT(N64_PIN_PREFIX, _Pin) #define N64_IRQ EXTI0_IRQn #define N64_PIN_LINE LL_SYSCFG_EXTI_LINE0 #define DISABLE_N64_INTERRUPT() LL_EXTI_DisableFallingTrig_0_31(N64_PIN_LINE) #define ENABLE_N64_INTERRUPT() LL_EXTI_EnableFallingTrig_0_31(N64_PIN_LINE) #define N64_HIGH LL_GPIO_SetPinMode((N64_PIN)->port, (N64_PIN)->pin, LL_GPIO_MODE_INPUT) #define N64_LOW LL_GPIO_SetPinMode((N64_PIN)->port, (N64_PIN)->pin, LL_GPIO_MODE_OUTPUT) #define N64_QUERY (N64_PORT->IDR & N64_Pin) #define LED_HIGH (void)0; //furi_hal_light_set(LightGreen, 0xFF) #define LED_LOW (void)0; //furi_hal_light_set(LightGreen, 0x00) template auto min(T1 x, T2 y) { return x < y ? x : y; } //#define USE_COMPARE_ABSOLUTE FuriMessageQueue* msg_queue; Gui* gui = nullptr; ViewPort* view_port = nullptr; typedef struct { enum { Input, ISRLog, } msg_type; union { InputEvent input; }; } QueueMessage; void isrLogCb() { QueueMessage msg = {QueueMessage::ISRLog, {}}; furi_message_queue_put(msg_queue, &msg, 0); } #define ISR_LOG_CB isrLogCb unsigned char frameDat[4] = {0}; void frameCb(const unsigned char* dat, size_t count) { memcpy(frameDat, dat, count); } #define FRAME_CB frameCb #include "teensy/teensy.ino" static void input_callback(InputEvent* input_event, void* ctx) { (void)ctx; QueueMessage msg = {QueueMessage::Input, {.input = *input_event}}; furi_message_queue_put(msg_queue, &msg, 0); } static void render_callback(Canvas* canvas, void* ctx) { (void)ctx; elements_text_box(canvas, 0, 0, 128, 10, AlignCenter, AlignTop, filename.c_str(), true); #define INVERT "\e!" static char inputStr[128] = ""; #define GET_BIT(byte, bit) (frameDat[byte] & (1 << bit)) #define INV_IF(x) ((x) ? INVERT : "") #define FMT_BTN(byte, bit) INV_IF(GET_BIT(byte, bit)), INV_IF(GET_BIT(byte, bit)) #define PLUS_MIN_ZERO(x) (((x) == 0) ? '0' : (((x) > 127) ? '-' : '+')) #define UN_SIGN(x) (((x) <= 127) ? x : (-((int)(x)-256))) #define FMT_JOY(byte) \ INV_IF(frameDat[byte] != 0), PLUS_MIN_ZERO(frameDat[byte]), UN_SIGN(frameDat[byte]), \ INV_IF(frameDat[byte] != 0) if(console == N64) snprintf( inputStr, sizeof(inputStr), "\e*\e!X\e!:%s%c%02x%s \e!Y\e!:%s%c%02x%s %s<%s%s>%s%s^%s%sv%s\n%sA%s%sB%s%sZ%s%sS%s%sL%s%sR%s C(%s<%s%s>%s%s^%s%sv%s)", FMT_JOY(2), FMT_JOY(3), FMT_BTN(0, 1), FMT_BTN(0, 0), FMT_BTN(0, 3), FMT_BTN(0, 2), FMT_BTN(0, 7), FMT_BTN(0, 6), FMT_BTN(0, 5), FMT_BTN(0, 4), FMT_BTN(1, 5), FMT_BTN(1, 4), FMT_BTN(1, 1), FMT_BTN(1, 0), FMT_BTN(1, 3), FMT_BTN(1, 2)); elements_text_box(canvas, 0, 15, 128, 32, AlignCenter, AlignTop, inputStr, false); int denom = numFrames ? numFrames : 1; float progress = (float)curFrame / denom; char progStr[32]; snprintf( progStr, sizeof(progStr), "%07lu/%07lu:%03i%%", curFrame, numFrames, (int)(progress * 100)); uint8_t progress_y = 41; uint8_t progress_h = 12; uint8_t progress_length = roundf(progress * (126)); canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 1, progress_y + 1, 126, progress_h - 2); canvas_set_color(canvas, ColorBlack); canvas_draw_rframe(canvas, 0, progress_y, 128, progress_h, 3); canvas_draw_box(canvas, 1, progress_y + 1, progress_length, progress_h - 2); canvas_set_color(canvas, ColorXOR); canvas_set_font(canvas, FontKeyboard); canvas_draw_str_aligned(canvas, 64, progress_y + 2, AlignCenter, AlignTop, progStr); canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } extern "C" int32_t tas_playback_app(const char* p) { FURI_LOG_I("TAS", "Hello world"); FURI_LOG_I("TAS", "I'm tas_playback!"); uint32_t oldPrio = NVIC_GetPriority(N64_IRQ); NVIC_SetPriority(N64_IRQ, 0); LL_GPIO_SetPinOutputType((N64_PIN)->port, (N64_PIN)->pin, LL_GPIO_OUTPUT_PUSHPULL); setup(); FURI_LOG_I("TAS", "SETUP DONE"); msg_queue = furi_message_queue_alloc(8, sizeof(QueueMessage)); gui = (Gui*)furi_record_open(RECORD_GUI); view_port = view_port_alloc(); gui_add_view_port(gui, view_port, GuiLayerFullscreen); view_port_draw_callback_set(view_port, render_callback, NULL); view_port_input_callback_set(view_port, input_callback, NULL); FuriString* file_path = furi_string_alloc(); FURI_LOG_I("TAS", "STARTING LOOP"); do { if(p && strlen(p)) { FURI_LOG_I("TAS", "GOT ARG"); furi_string_set_str(file_path, (const char*)p); } else { FURI_LOG_I("TAS", "NO ARG"); Storage* storage = (Storage*)furi_record_open(RECORD_STORAGE); storage_common_migrate(storage, EXT_PATH("tas"), STORAGE_APP_DATA_PATH_PREFIX); furi_record_close(RECORD_STORAGE); FURI_LOG_I("TAS", "MIGRATED"); furi_string_set_str(file_path, STORAGE_APP_DATA_PATH_PREFIX); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, "m64", &I_tas_playback); browser_options.hide_ext = false; browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; FURI_LOG_I("TAS", "SETUP OPTIONS"); DialogsApp* dialogs = (DialogsApp*)furi_record_open(RECORD_DIALOGS); bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); furi_record_close(RECORD_DIALOGS); if(!res) { FURI_LOG_E("TAS", "No file selected"); break; } } if(!openTAS(furi_string_get_cstr(file_path))) continue; FURI_LOG_E("TAS", "Got file"); QueueMessage msg; while(furi_message_queue_get(msg_queue, &msg, FuriWaitForever) == FuriStatusOk) { if(msg.msg_type == QueueMessage::Input) { if(msg.input.type == InputTypeLong) { if(msg.input.key == InputKeyBack) break; } else if(msg.input.type == InputTypeShort) { if(msg.input.key == InputKeyUp) { } else if(msg.input.key == InputKeyDown) { } } } view_port_update(view_port); loop(); } view_port_update(view_port); if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg } while(1); furi_string_free(file_path); gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); view_port_free(view_port); furi_message_queue_free(msg_queue); furi_hal_gpio_init(N64_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_remove_int_callback(N64_PIN); NVIC_SetPriority(N64_IRQ, oldPrio); return 0; }