tas_playback.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. #include <furi.h>
  2. #include <cli/cli.h>
  3. #include <cli/cli_vcp.h>
  4. #include <dialogs/dialogs.h>
  5. #include <gui/gui.h>
  6. #include <gui/elements.h>
  7. #include <storage/storage.h>
  8. #include <furi_hal_resources.h>
  9. #include <furi_hal_light.h>
  10. #include <furi_hal_console.h>
  11. #include <math.h>
  12. /* generated by fbt from .png files in images folder */
  13. #include <tas_playback_icons.h>
  14. #include "WString.h"
  15. typedef enum {
  16. O_READ = FSAM_READ,
  17. O_WRITE = FSAM_WRITE,
  18. O_CREAT = ((uint16_t)FSOM_OPEN_ALWAYS << 8),
  19. O_APPEND = ((uint16_t)FSOM_OPEN_APPEND << 8),
  20. O_EXCL = ((uint16_t)FSOM_CREATE_NEW << 8),
  21. O_TRUNC = ((uint16_t)FSOM_CREATE_ALWAYS << 8),
  22. } FileOpenMode;
  23. #define SD_CARD_ERROR_ACMD41 0x16
  24. class SdFs {
  25. public:
  26. bool begin(int) {
  27. m_storage = (Storage*)furi_record_open(RECORD_STORAGE);
  28. return m_storage != nullptr;
  29. }
  30. bool mkdir(const char* path) {
  31. return storage_simply_mkdir(m_storage, path);
  32. }
  33. ~SdFs() {
  34. if(m_storage) furi_record_close(RECORD_STORAGE);
  35. }
  36. uint8_t sdErrorCode() const {
  37. return 0;
  38. }
  39. uint8_t sdErrorData() const {
  40. return 0;
  41. }
  42. private:
  43. Storage* m_storage = nullptr;
  44. friend class SdFile;
  45. };
  46. template <class T>
  47. static inline void printSdErrorSymbol(T*, uint8_t) {
  48. }
  49. class SdFile {
  50. public:
  51. ~SdFile() {
  52. if(m_file) storage_file_free(m_file);
  53. }
  54. void close() {
  55. if(m_file) storage_file_close(m_file);
  56. }
  57. bool open(SdFs* from, const char* path, uint16_t mode) {
  58. m_fs = from;
  59. initFile();
  60. m_path = path;
  61. return storage_file_open(
  62. m_file, path, FS_AccessMode((uint16_t)mode & 0xFF), FS_OpenMode((uint16_t)mode >> 8));
  63. }
  64. bool open(SdFs* from, const char* path, FileOpenMode mode) {
  65. return open(from, path, (uint16_t)mode);
  66. }
  67. void getName(char* dest, uint16_t size) {
  68. m_path.toCharArray(dest, size);
  69. }
  70. unsigned read(void* buff, int bytes_to_read) {
  71. return storage_file_read(m_file, buff, bytes_to_read);
  72. }
  73. unsigned write(const void* buff, int bytes_to_read) {
  74. return storage_file_write(m_file, buff, bytes_to_read);
  75. }
  76. bool available() {
  77. return !storage_file_eof(m_file);
  78. }
  79. uint64_t fileSize() {
  80. return storage_file_size(m_file);
  81. }
  82. uint64_t curPosition() {
  83. return storage_file_tell(m_file);
  84. }
  85. bool seekSet(uint32_t pos) {
  86. return storage_file_seek(m_file, pos, true);
  87. }
  88. bool isOpen() {
  89. return m_file && storage_file_is_open(m_file);
  90. }
  91. bool openNext(SdFile* from) {
  92. char name[64];
  93. bool ret = storage_dir_read(from->m_file, NULL, name, sizeof(name));
  94. if(!ret) return false;
  95. String path = from->m_path;
  96. path += "/";
  97. path += name;
  98. return open(from->m_fs, path.c_str(), O_READ);
  99. }
  100. bool rewind() {
  101. return false;
  102. // return storage_dir_rewind(m_file);
  103. }
  104. private:
  105. void initFile() {
  106. close();
  107. if(!m_file) m_file = storage_file_alloc(m_fs->m_storage);
  108. }
  109. File* m_file = nullptr;
  110. SdFs* m_fs = nullptr;
  111. String m_path;
  112. };
  113. #define F(x) x
  114. #define noInterrupts() uint32_t intLevel = ulPortRaiseBASEPRI()
  115. #define interrupts() vPortSetBASEPRI(intLevel)
  116. #define F_CPU /*SystemCoreClock*/ 64000000
  117. typedef enum { HEX = 16 } BaseType;
  118. class SerialObj {
  119. public:
  120. template <class... T>
  121. void print(const T&... v) {
  122. String out(v...);
  123. furi_hal_console_tx((uint8_t*)out.c_str(), out.length());
  124. }
  125. void println() {
  126. furi_hal_console_tx((uint8_t*)"\r\n", 2);
  127. }
  128. template <class... T>
  129. void println(const T&... v) {
  130. String out(v...);
  131. furi_hal_console_tx((uint8_t*)out.c_str(), out.length());
  132. println();
  133. }
  134. template <class... T>
  135. void write(const T&... v) {
  136. print(v...);
  137. }
  138. int read() {
  139. return -1;
  140. /*
  141. uint8_t buf;
  142. if (!cli_read_timeout(m_cli, &buf, 1, 0))
  143. return -1;
  144. return buf;*/
  145. }
  146. bool begin() {
  147. /*
  148. m_cli = (Cli*)furi_record_open(RECORD_CLI);
  149. // if (m_cli)
  150. // cli_session_open(m_cli, &cli_vcp);
  151. */
  152. return (bool)*this;
  153. }
  154. ~SerialObj() {
  155. if(m_cli) furi_record_close(RECORD_CLI);
  156. }
  157. explicit operator bool() {
  158. return true; // m_cli && cli_is_connected(m_cli);
  159. }
  160. void flush() {
  161. }
  162. private:
  163. Cli* m_cli = nullptr;
  164. };
  165. #define SERIAL_BAUD_RATE
  166. #define BUILTIN_SDCARD 0
  167. #define NO_CONSOLE_GPIO
  168. #define ARM_DWT_CYCCNT DWT->CYCCNT
  169. SerialObj Serial;
  170. typedef void (*ArduinoISR)();
  171. void gpioCallback(void* ctx) {
  172. ArduinoISR cb = (ArduinoISR)ctx;
  173. cb();
  174. }
  175. #define LOW false
  176. #define HIGH true
  177. typedef enum {
  178. INPUT = GpioModeInput,
  179. OUTPUT = GpioModeOutputOpenDrain,
  180. INPUT_PULLUP = (uint16_t)GpioModeInput | ((uint16_t)GpioPullUp << 8),
  181. INPUT_PULLDOWN = (uint16_t)GpioModeInput | ((uint16_t)GpioPullDown << 8),
  182. FALLING = (uint16_t)GpioModeInterruptFall | ((uint16_t)GpioPullUp << 8),
  183. } PinMode;
  184. void pinMode(const GpioPin* pin, PinMode mode) {
  185. furi_hal_gpio_init(
  186. pin, GpioMode((uint16_t)mode & 0xFF), GpioPull((uint16_t)mode >> 8), GpioSpeedVeryHigh);
  187. }
  188. void attachInterrupt(const GpioPin* pin, ArduinoISR cb, PinMode mode) {
  189. pinMode(pin, mode);
  190. furi_hal_gpio_remove_int_callback(pin);
  191. furi_hal_gpio_add_int_callback(pin, gpioCallback, (void*)cb);
  192. }
  193. static inline void digitalWrite(const GpioPin* pin, bool state) {
  194. furi_hal_gpio_write(pin, state);
  195. }
  196. static inline bool digitalRead(const GpioPin* pin) {
  197. return furi_hal_gpio_read(pin);
  198. }
  199. #define N64_PIN &gpio_ext_pc0
  200. #define N64_PIN_PREFIX PC0
  201. #define CONCAT1(a, b) a##b
  202. #define CONCAT(a, b) CONCAT1(a, b)
  203. #define N64_PORT CONCAT(N64_PIN_PREFIX, _GPIO_Port)
  204. #define N64_Pin CONCAT(N64_PIN_PREFIX, _Pin)
  205. #define N64_IRQ EXTI0_IRQn
  206. #define N64_PIN_LINE LL_SYSCFG_EXTI_LINE0
  207. #define DISABLE_N64_INTERRUPT() LL_EXTI_DisableFallingTrig_0_31(N64_PIN_LINE)
  208. #define ENABLE_N64_INTERRUPT() LL_EXTI_EnableFallingTrig_0_31(N64_PIN_LINE)
  209. #define N64_HIGH LL_GPIO_SetPinMode((N64_PIN)->port, (N64_PIN)->pin, LL_GPIO_MODE_INPUT)
  210. #define N64_LOW LL_GPIO_SetPinMode((N64_PIN)->port, (N64_PIN)->pin, LL_GPIO_MODE_OUTPUT)
  211. #define N64_QUERY (N64_PORT->IDR & N64_Pin)
  212. #define LED_HIGH (void)0; //furi_hal_light_set(LightGreen, 0xFF)
  213. #define LED_LOW (void)0; //furi_hal_light_set(LightGreen, 0x00)
  214. template <class T1, class T2>
  215. auto min(T1 x, T2 y) {
  216. return x < y ? x : y;
  217. }
  218. //#define USE_COMPARE_ABSOLUTE
  219. FuriMessageQueue* msg_queue;
  220. Gui* gui = nullptr;
  221. ViewPort* view_port = nullptr;
  222. typedef struct {
  223. enum {
  224. Input,
  225. ISRLog,
  226. } msg_type;
  227. union {
  228. InputEvent input;
  229. };
  230. } QueueMessage;
  231. void isrLogCb() {
  232. QueueMessage msg = {QueueMessage::ISRLog, {}};
  233. furi_message_queue_put(msg_queue, &msg, 0);
  234. }
  235. #define ISR_LOG_CB isrLogCb
  236. unsigned char frameDat[4] = {0};
  237. void frameCb(const unsigned char* dat, size_t count) {
  238. memcpy(frameDat, dat, count);
  239. }
  240. #define FRAME_CB frameCb
  241. #include "teensy/teensy.ino"
  242. static void input_callback(InputEvent* input_event, void* ctx) {
  243. (void)ctx;
  244. QueueMessage msg = {QueueMessage::Input, {.input = *input_event}};
  245. furi_message_queue_put(msg_queue, &msg, 0);
  246. }
  247. static void render_callback(Canvas* canvas, void* ctx) {
  248. (void)ctx;
  249. elements_text_box(canvas, 0, 0, 128, 10, AlignCenter, AlignTop, filename.c_str(), true);
  250. #define INVERT "\e!"
  251. static char inputStr[128] = "";
  252. #define GET_BIT(byte, bit) (frameDat[byte] & (1 << bit))
  253. #define INV_IF(x) ((x) ? INVERT : "")
  254. #define FMT_BTN(byte, bit) INV_IF(GET_BIT(byte, bit)), INV_IF(GET_BIT(byte, bit))
  255. #define PLUS_MIN_ZERO(x) (((x) == 0) ? '0' : (((x) > 127) ? '-' : '+'))
  256. #define UN_SIGN(x) (((x) <= 127) ? x : (-((int)(x)-256)))
  257. #define FMT_JOY(byte) \
  258. INV_IF(frameDat[byte] != 0), PLUS_MIN_ZERO(frameDat[byte]), UN_SIGN(frameDat[byte]), \
  259. INV_IF(frameDat[byte] != 0)
  260. if(console == N64)
  261. snprintf(
  262. inputStr,
  263. sizeof(inputStr),
  264. "\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)",
  265. FMT_JOY(2),
  266. FMT_JOY(3),
  267. FMT_BTN(0, 1),
  268. FMT_BTN(0, 0),
  269. FMT_BTN(0, 3),
  270. FMT_BTN(0, 2),
  271. FMT_BTN(0, 7),
  272. FMT_BTN(0, 6),
  273. FMT_BTN(0, 5),
  274. FMT_BTN(0, 4),
  275. FMT_BTN(1, 5),
  276. FMT_BTN(1, 4),
  277. FMT_BTN(1, 1),
  278. FMT_BTN(1, 0),
  279. FMT_BTN(1, 3),
  280. FMT_BTN(1, 2));
  281. elements_text_box(canvas, 0, 15, 128, 32, AlignCenter, AlignTop, inputStr, false);
  282. int denom = numFrames ? numFrames : 1;
  283. float progress = (float)curFrame / denom;
  284. char progStr[32];
  285. snprintf(
  286. progStr, sizeof(progStr), "%07lu/%07lu:%03i%%", curFrame, numFrames, (int)(progress * 100));
  287. uint8_t progress_y = 41;
  288. uint8_t progress_h = 12;
  289. uint8_t progress_length = roundf(progress * (126));
  290. canvas_set_color(canvas, ColorWhite);
  291. canvas_draw_box(canvas, 1, progress_y + 1, 126, progress_h - 2);
  292. canvas_set_color(canvas, ColorBlack);
  293. canvas_draw_rframe(canvas, 0, progress_y, 128, progress_h, 3);
  294. canvas_draw_box(canvas, 1, progress_y + 1, progress_length, progress_h - 2);
  295. canvas_set_color(canvas, ColorXOR);
  296. canvas_set_font(canvas, FontKeyboard);
  297. canvas_draw_str_aligned(canvas, 64, progress_y + 2, AlignCenter, AlignTop, progStr);
  298. canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
  299. canvas_set_font(canvas, FontSecondary);
  300. elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
  301. }
  302. extern "C" int32_t tas_playback_app(const char* p) {
  303. FURI_LOG_I("TAS", "Hello world");
  304. FURI_LOG_I("TAS", "I'm tas_playback!");
  305. uint32_t oldPrio = NVIC_GetPriority(N64_IRQ);
  306. NVIC_SetPriority(N64_IRQ, 0);
  307. LL_GPIO_SetPinOutputType((N64_PIN)->port, (N64_PIN)->pin, LL_GPIO_OUTPUT_PUSHPULL);
  308. setup();
  309. FURI_LOG_I("TAS", "SETUP DONE");
  310. msg_queue = furi_message_queue_alloc(8, sizeof(QueueMessage));
  311. gui = (Gui*)furi_record_open(RECORD_GUI);
  312. view_port = view_port_alloc();
  313. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  314. view_port_draw_callback_set(view_port, render_callback, NULL);
  315. view_port_input_callback_set(view_port, input_callback, NULL);
  316. FuriString* file_path = furi_string_alloc();
  317. FURI_LOG_I("TAS", "STARTING LOOP");
  318. do {
  319. if(p && strlen(p)) {
  320. FURI_LOG_I("TAS", "GOT ARG");
  321. furi_string_set_str(file_path, (const char*)p);
  322. } else {
  323. FURI_LOG_I("TAS", "NO ARG");
  324. Storage* storage = (Storage*)furi_record_open(RECORD_STORAGE);
  325. storage_common_migrate(storage, EXT_PATH("tas"), STORAGE_APP_DATA_PATH_PREFIX);
  326. furi_record_close(RECORD_STORAGE);
  327. FURI_LOG_I("TAS", "MIGRATED");
  328. furi_string_set_str(file_path, STORAGE_APP_DATA_PATH_PREFIX);
  329. DialogsFileBrowserOptions browser_options;
  330. dialog_file_browser_set_basic_options(&browser_options, "m64", &I_tas_playback);
  331. browser_options.hide_ext = false;
  332. browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
  333. FURI_LOG_I("TAS", "SETUP OPTIONS");
  334. DialogsApp* dialogs = (DialogsApp*)furi_record_open(RECORD_DIALOGS);
  335. bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
  336. furi_record_close(RECORD_DIALOGS);
  337. if(!res) {
  338. FURI_LOG_E("TAS", "No file selected");
  339. break;
  340. }
  341. }
  342. if(!openTAS(furi_string_get_cstr(file_path))) continue;
  343. FURI_LOG_E("TAS", "Got file");
  344. QueueMessage msg;
  345. while(furi_message_queue_get(msg_queue, &msg, FuriWaitForever) == FuriStatusOk) {
  346. if(msg.msg_type == QueueMessage::Input) {
  347. if(msg.input.type == InputTypeLong) {
  348. if(msg.input.key == InputKeyBack) break;
  349. } else if(msg.input.type == InputTypeShort) {
  350. if(msg.input.key == InputKeyUp) {
  351. } else if(msg.input.key == InputKeyDown) {
  352. }
  353. }
  354. }
  355. view_port_update(view_port);
  356. loop();
  357. }
  358. view_port_update(view_port);
  359. if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg
  360. } while(1);
  361. furi_string_free(file_path);
  362. gui_remove_view_port(gui, view_port);
  363. furi_record_close(RECORD_GUI);
  364. view_port_free(view_port);
  365. furi_message_queue_free(msg_queue);
  366. furi_hal_gpio_init(N64_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  367. furi_hal_gpio_remove_int_callback(N64_PIN);
  368. NVIC_SetPriority(N64_IRQ, oldPrio);
  369. return 0;
  370. }