#include #include #include "debug.h" #include "ost_hal.h" #include "filesystem.h" #include "mini_qoi.h" #include "serializers.h" #include "sdcard.h" #ifdef OST_USE_FF_LIBRARY #include "ff.h" #include "diskio.h" typedef FIL file_t; #else // Use standard library typedef FILE *file_t; typedef int FRESULT; #define F_OK #endif // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. #define SD_FAT_TYPE 2 static FATFS fs; static FIL File[2]; /* File object */ static DIR Dir; /* Directory object */ static FILINFO Finfo; static SD_CardInfo cardinfo; file_t file_open(const char *filename) { #ifdef OST_USE_FF_LIBRARY file_t fil; FRESULT fr = f_open(&fil, filename, FA_READ); if (fr != FR_OK) { debug_printf("ERROR: f_open %d\n\r", (int)fr); } return fil; #else return fopen(filename, "r"); #endif } static FRESULT scan_files( char *path /* Pointer to the working buffer with start path */ ) { DIR dirs; FRESULT fr; fr = f_opendir(&dirs, path); if (fr == FR_OK) { while (((fr = f_readdir(&dirs, &Finfo)) == FR_OK) && Finfo.fname[0]) { if (Finfo.fattrib & AM_DIR) { // i = strlen(path); debug_printf("/%s\r\n", Finfo.fname); if (fr != FR_OK) break; } else { debug_printf("%s\r\n", Finfo.fname); } } } else { debug_printf("[OST] Cannot open directory\r\n"); } return fr; } void filesystem_mount() { uint32_t retries = 3; FRESULT fr; do { fr = f_mount(&fs, "", 1); // 0: mount successful ; 1: mount failed ost_system_delay_ms(10); } while (--retries && fr); if (fr) { debug_printf("[OST] No Card Found! Err=%d\r\n", (int)fr); } debug_printf("[OST] SD Card File System = %d\r\n", fs.fs_type); // FS_EXFAT = 4 // debug_printf("[OST] Starting with CPU=%d\r\n", (int)SystemCoreClock); // Get SD Card info if (sdcard_get_card_info(&cardinfo) == SD_RESPONSE_NO_ERROR) { debug_printf("[OST] Sectors: %d, sector size: %d\r\n", cardinfo.CardCapacity, cardinfo.CardBlockSize); } else { debug_printf("[OST] Cannot read SDCard info\r\n"); } scan_files(""); scan_files("/ba869e4b-03d6-4249-9202-85b4cec767a7/assets"); } uint32_t filesystem_get_capacity() { return cardinfo.CardCapacity; } /* // Loop in all directories void disk_start() { // default is current directory const char* dpath = "."; if (argc) dpath = args; DIR dir; if ( FR_OK != f_opendir(&dir, dpath) ) { printf("cannot access '%s': No such file or directory\r\n", dpath); return; } FILINFO fno; while( (f_readdir(&dir, &fno) == FR_OK) && (fno.fname[0] != 0) ) { if ( fno.fname[0] != '.' ) // ignore . and .. entry { if ( fno.fattrib & AM_DIR ) { // directory printf("/%s\r\n", fno.fname); }else { printf("%-40s", fno.fname); if (fno.fsize < 1024) { printf("%lu B\r\n", fno.fsize); }else { printf("%lu KB\r\n", fno.fsize / 1024); } } } } f_closedir(&dir); } */ // =========================================================================================================== // INDEX FILE MANAGEMENT // =========================================================================================================== static file_t IndexFile; static uint8_t IndexBuf[260]; static uint8_t ImageBuf[100]; static uint8_t SoundBuf[100]; static const char *IndexFileName = "index.ost"; #define TLV_ARRAY_TYPE 0xAB #define TLV_OBJECT_TYPE 0xE7 #define TLV_STRING_TYPE 0x3D #define TL_SIZE 3 bool filesystem_read_index_file(ost_context_t *ctx) { FILINFO fno; FRESULT fr = f_stat(IndexFileName, &fno); ctx->number_of_stories = 0; ctx->current_story = 0; if (fr == FR_OK) { ctx->index_file_size = fno.fsize; UINT br; bool valid_file = false; // Minimum size of TLV is type + length, so check if there is potential data if (ctx->index_file_size > TL_SIZE) { FRESULT fr = f_open(&IndexFile, IndexFileName, FA_READ); if (fr == FR_OK) { fr = f_read(&IndexFile, &IndexBuf[0], TL_SIZE, &br); if ((fr == FR_OK) && (br == TL_SIZE) && (IndexBuf[0] = TLV_ARRAY_TYPE)) { // nomber of stories ctx->number_of_stories = leu16_get(&IndexBuf[1]); debug_printf("SUCCESS: found %d stories\r\n", ctx->number_of_stories); } } } else { debug_printf("ERROR: index.ost not found\r\n"); } } } static uint32_t tlv_get_string(char *destination) { uint32_t size = 0; UINT br; FRESULT fr = f_read(&IndexFile, &IndexBuf[0], TL_SIZE, &br); if ((fr == FR_OK) && (br == TL_SIZE) && (IndexBuf[0] = TLV_STRING_TYPE)) { size = leu16_get(&IndexBuf[1]); // read string, directly fill destination f_read(&IndexFile, destination, size, &br); if ((fr == FR_OK) && (br == size)) { // good, end propertly with zero destination[size] = 0; } else { size = 0; } } return size; } void filesystem_get_story_title(ost_context_t *ctx) { UINT br; ctx->current_story++; if (ctx->current_story >= ctx->number_of_stories) { // Rewind to the begining of the index file data (first array element) f_lseek(&IndexFile, TL_SIZE); } FRESULT fr = f_read(&IndexFile, &IndexBuf[0], 3, &br); if ((fr == FR_OK) && (br == 3) && (IndexBuf[0] = TLV_OBJECT_TYPE)) { tlv_get_string(ctx->uuid); tlv_get_string(ImageBuf); tlv_get_string(SoundBuf); ctx->image = ImageBuf; ctx->sound = SoundBuf; } } // =========================================================================================================== // IMAGE DECODER // =========================================================================================================== static mqoi_dec_t dec; static mqoi_desc_t desc; static uint8_t bmpImage[256]; static color_t line[320]; void filesystem_display_image(const char *filename) { file_t fil; unsigned int br; fil = file_open(filename); uint32_t imgW, imgH; mqoi_desc_init(&desc); // 1. Read header f_read(&fil, &desc.magic[0], sizeof(mqoi_desc_t) - 1, &br); uint8_t errn = mqoi_desc_verify(&desc, &imgW, &imgH); if (errn) { debug_printf("Invalid image, code %d\n", errn); return; } debug_printf("Image dimensions: %d, %d\n", imgW, imgH); uint32_t start, end, pxCount = 0; volatile mqoi_rgba_t *px; mqoi_dec_init(&dec, imgW * imgH); // Serial.println("starting decode..."); int index = 256; // force refill first time int x = 0; int y = 0; while (!mqoi_dec_done(&dec)) { if (index >= sizeof(bmpImage)) { // refill buffer f_read(&fil, bmpImage, sizeof(bmpImage), &br); index = 0; } mqoi_dec_push(&dec, bmpImage[index++]); while ((px = mqoi_dec_pop(&dec)) != NULL) { pxCount++; line[x].r = px->r; line[x].g = px->g; line[x].b = px->b; x++; if (x >= 320) { ost_display_draw_h_line_rgb888(y, line); x = 0; y++; } } } f_close(&fil); } #define FS_MAX_SIZE_READ 256 void filesystem_load_rom(uint8_t *mem, const char *filename) { file_t fil; FILINFO fno; unsigned int br; FRESULT fr = f_stat(filename, &fno); if (fr == FR_OK) { int total_size = fno.fsize; fil = file_open(filename); int copied_size = 0; do { int read_size = total_size > FS_MAX_SIZE_READ ? FS_MAX_SIZE_READ : total_size; f_read(&fil, &mem[copied_size], read_size, &br); if (br == read_size) { total_size -= read_size; copied_size += read_size; } else { debug_printf("Read file error\n"); total_size = 0; // force exit return; } } while (total_size > 0); f_close(&fil); } }