├── res ├── rungd.bin ├── dcload-ip.bin ├── ebdragon.tex ├── dark-wall-128.png ├── dark-wall-256.png ├── dark-wall-512.png ├── dcload-serial.bin ├── lite-wall-128.png ├── lite-wall-256.png ├── lite-wall-512.png ├── wallpaper-128.png ├── boot_loader_devkit.bios ├── boot_loader_retail.bios ├── boot_loader_devkit_32mb.bios ├── boot_loader_retail_32mb.bios ├── boot_loader_devkit_nogdrom.bios ├── boot_loader_retail_nogdrom.bios ├── boot_loader_devkit_nogdrom_32mb.bios ├── boot_loader_retail_nogdrom_32mb.bios └── ebdragon.fnt ├── .gitignore ├── src ├── input.h ├── log.h ├── menu.h ├── input.c ├── disc.h ├── fatfs │ ├── integer.h │ ├── diskio.h │ ├── fatfs.h │ ├── option │ │ ├── syscall.c │ │ └── ccsbcs.c │ ├── dc_bdev.c │ ├── ffconf.h │ ├── ff.h │ └── dc.c ├── log.c ├── utility.h ├── bmfont.h ├── main.c ├── drawing.h ├── bmfont.c ├── disc.c ├── drawing.c ├── utility.c └── menu.c ├── Makefile.cfg ├── README.md └── Makefile /res/rungd.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/rungd.bin -------------------------------------------------------------------------------- /res/dcload-ip.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/dcload-ip.bin -------------------------------------------------------------------------------- /res/ebdragon.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/ebdragon.tex -------------------------------------------------------------------------------- /res/dark-wall-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/dark-wall-128.png -------------------------------------------------------------------------------- /res/dark-wall-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/dark-wall-256.png -------------------------------------------------------------------------------- /res/dark-wall-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/dark-wall-512.png -------------------------------------------------------------------------------- /res/dcload-serial.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/dcload-serial.bin -------------------------------------------------------------------------------- /res/lite-wall-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/lite-wall-128.png -------------------------------------------------------------------------------- /res/lite-wall-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/lite-wall-256.png -------------------------------------------------------------------------------- /res/lite-wall-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/lite-wall-512.png -------------------------------------------------------------------------------- /res/wallpaper-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/wallpaper-128.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | dreamdash.elf 3 | kos/ 4 | kos-ports/ 5 | release/ 6 | romdisk.img 7 | romdisk/ 8 | -------------------------------------------------------------------------------- /res/boot_loader_devkit.bios: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/boot_loader_devkit.bios -------------------------------------------------------------------------------- /res/boot_loader_retail.bios: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/boot_loader_retail.bios -------------------------------------------------------------------------------- /res/boot_loader_devkit_32mb.bios: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/boot_loader_devkit_32mb.bios -------------------------------------------------------------------------------- /res/boot_loader_retail_32mb.bios: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/boot_loader_retail_32mb.bios -------------------------------------------------------------------------------- /res/boot_loader_devkit_nogdrom.bios: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/boot_loader_devkit_nogdrom.bios -------------------------------------------------------------------------------- /res/boot_loader_retail_nogdrom.bios: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/boot_loader_retail_nogdrom.bios -------------------------------------------------------------------------------- /res/boot_loader_devkit_nogdrom_32mb.bios: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/boot_loader_devkit_nogdrom_32mb.bios -------------------------------------------------------------------------------- /res/boot_loader_retail_nogdrom_32mb.bios: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcagn/DreamDash/HEAD/res/boot_loader_retail_nogdrom_32mb.bios -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | #ifndef LOADER_INPUT_H 2 | #define LOADER_INPUT_H 3 | 4 | #define INPUT_QUIT (1U<<(20)) 5 | 6 | uint32_t get_input(); 7 | 8 | #endif //LOADER_INPUT_H 9 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | #ifndef DREAMDASH_LOG_H 2 | #define DREAMDASH_LOG_H 3 | 4 | #include 5 | 6 | void dash_log(int level, const char *fmt, ...); 7 | 8 | #endif //DREAMDASH_LOG_H 9 | -------------------------------------------------------------------------------- /src/menu.h: -------------------------------------------------------------------------------- 1 | #ifndef LOADER_MENU_H 2 | #define LOADER_MENU_H 3 | 4 | enum Menu { 5 | MENU_MAIN, 6 | MENU_RETRODREAM, 7 | MENU_DREAMSHELL, 8 | MENU_FILER, 9 | MENU_DCLOAD_SERIAL, 10 | MENU_DCLOAD_IP, 11 | #ifdef DISC_SUPPORT 12 | MENU_DISC, 13 | #endif 14 | MENU_LOGS 15 | }; 16 | 17 | void menu_run(); 18 | 19 | #endif //LOADER_MENU_H 20 | -------------------------------------------------------------------------------- /Makefile.cfg: -------------------------------------------------------------------------------- 1 | # Project name 2 | TARGET = dreamdash 3 | 4 | # Project version 5 | VERSION = 0.81 6 | 7 | # Autoboot applications if found 8 | AUTOBOOT = 0 9 | 10 | # Wallpaper dark or lite 11 | WALLPAPER_SHADE = lite 12 | 13 | # Wallpaper resolution: 512, 256, 128 14 | WALLPAPER_RES = 256 15 | 16 | # Include support for launching discs 17 | DISC_SUPPORT = 0 18 | 19 | # Choose a FAT filesystem library: 20 | # - `kosfat` for libkosfat (built into KOS) 21 | # - `fatfs` for FatFS (addon library) 22 | FAT_LIBRARY = kosfat 23 | -------------------------------------------------------------------------------- /src/input.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static uint32_t last_buttons = 0; 4 | 5 | uint32_t get_input() { 6 | 7 | maple_device_t *cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); 8 | if (cont == NULL) { 9 | return 0; 10 | } 11 | 12 | cont_state_t *state = (cont_state_t *) maple_dev_status(cont); 13 | if (state == NULL) { 14 | return 0; 15 | } 16 | 17 | if (last_buttons != state->buttons) { 18 | last_buttons = state->buttons; 19 | return state->buttons; 20 | } 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/disc.h: -------------------------------------------------------------------------------- 1 | #ifndef DREAMDASH_DISC_H 2 | #define DREAMDASH_DISC_H 3 | 4 | typedef struct ip_meta { 5 | char hardware_ID[16]; 6 | char maker_ID[16]; 7 | char ks[5]; 8 | char disk_type[6]; 9 | char disk_num[5]; 10 | char country_codes[8]; 11 | char ctrl[4]; 12 | char dev[1]; 13 | char VGA[1]; 14 | char WinCE[1]; 15 | char unk[1]; 16 | char product_ID[10]; 17 | char product_version[6]; 18 | char release_date[8]; 19 | char unk2[8]; 20 | char boot_file[16]; 21 | char software_maker_info[16]; 22 | char title[128]; 23 | } ip_meta_t; 24 | 25 | extern ip_meta_t *ip_info; 26 | 27 | int disc_init(void); 28 | void disc_shutdown(void); 29 | void disc_launch(void); 30 | 31 | #endif //DREAMDASH_DISC_H 32 | -------------------------------------------------------------------------------- /src/fatfs/integer.h: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------*/ 2 | /* Integer type definitions for FatFs module */ 3 | /*-------------------------------------------*/ 4 | 5 | #ifndef _FF_INTEGER 6 | #define _FF_INTEGER 7 | 8 | #ifdef _WIN32 /* Development platform */ 9 | 10 | #include 11 | #include 12 | 13 | #else /* Embedded platform */ 14 | 15 | /* This type MUST be 8-bit */ 16 | typedef unsigned char BYTE; 17 | 18 | /* These types MUST be 16-bit */ 19 | typedef short SHORT; 20 | typedef unsigned short WORD; 21 | typedef unsigned short WCHAR; 22 | 23 | /* These types MUST be 16-bit or 32-bit */ 24 | typedef int INT; 25 | typedef unsigned int UINT; 26 | 27 | /* These types MUST be 32-bit */ 28 | typedef long LONG; 29 | typedef unsigned long DWORD; 30 | 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "drawing.h" 8 | #include "utility.h" 9 | #include "utlist.h" 10 | 11 | List logList = {NULL, 0, "LOGS"}; 12 | 13 | void dash_log(int level, const char *fmt, ...) { 14 | 15 | ListItem *item; 16 | va_list args; 17 | 18 | item = (ListItem *) malloc(sizeof *item); 19 | memset(item, 0, sizeof(ListItem)); 20 | 21 | switch (level) { 22 | case DBG_DEAD: 23 | case DBG_CRITICAL: 24 | case DBG_ERROR: 25 | item->type = TYPE_DIR; 26 | break; 27 | case DBG_WARNING: 28 | item->type = TYPE_BIN; 29 | break; 30 | 31 | default: 32 | item->type = TYPE_FILE; 33 | break; 34 | } 35 | 36 | va_start(args, fmt); 37 | vsnprintf(item->name, MAX_PATH, fmt, args); 38 | va_end(args); 39 | 40 | DL_APPEND(logList.head, item); 41 | logList.size++; 42 | 43 | // debug to screen too 44 | draw_printf(level, item->name); 45 | // debug to console too 46 | dbglog(level, item->name); 47 | } 48 | -------------------------------------------------------------------------------- /src/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef LOADER_UTILITY_H 2 | #define LOADER_UTILITY_H 3 | 4 | #define MAX_PATH 512 5 | 6 | enum FileType { 7 | TYPE_DIR, 8 | TYPE_FILE, 9 | TYPE_BIN 10 | }; 11 | 12 | typedef struct ListItem { 13 | struct ListItem *next, *prev; 14 | char name[MAX_PATH]; 15 | char path[MAX_PATH]; 16 | int type; 17 | } ListItem; 18 | 19 | typedef struct List { 20 | ListItem *head; 21 | int size; 22 | char path[MAX_PATH]; 23 | } List; 24 | 25 | int list_cmp(ListItem *a, ListItem *b); 26 | 27 | void get_dir(List *list, const char *path); 28 | 29 | void free_dir(List *list); 30 | 31 | ListItem *get_item(List *list, int index); 32 | 33 | int file_exists(const char *file); 34 | 35 | int dir_exists(const char *dir); 36 | 37 | void try_boot(); 38 | 39 | char *read_file(const char *file, int *size); 40 | 41 | void *decompress_file_aligned(const char *file, int alignment, int output_size); 42 | 43 | void *decompress_file(const char *file, int output_size); 44 | 45 | void exec(const char *path); 46 | 47 | void launch_retrodream(); 48 | 49 | void launch_dreamshell(); 50 | 51 | void launch_dcload_serial(); 52 | 53 | void launch_dcload_ip(); 54 | 55 | void trim(char *str); 56 | 57 | #endif //LOADER_UTILITY_H 58 | -------------------------------------------------------------------------------- /src/bmfont.h: -------------------------------------------------------------------------------- 1 | #ifndef BMFONTLIB_BMFONT_H 2 | #define BMFONTLIB_BMFONT_H 3 | 4 | #define BMF_MAX_PATH 512 5 | #define BMF_MAX_CHAR 128 6 | 7 | typedef struct bmf_font_info_s { 8 | char face[BMF_MAX_PATH]; 9 | int size; 10 | int bold; 11 | int italic; 12 | char charset[BMF_MAX_PATH]; 13 | int unicode; 14 | int stretchH; 15 | int smooth; 16 | int aa; 17 | int padding[4]; 18 | int spacing[2]; 19 | int outline; 20 | } BMFontInfo; 21 | 22 | typedef struct bmf_font_common_s { 23 | int lineHeight; 24 | int base; 25 | int scaleW; 26 | int scaleH; 27 | int pages; 28 | int packed; 29 | int alphaChnl; 30 | int redChnl; 31 | int greenChnl; 32 | int blueChnl; 33 | } BMFontCommon; 34 | 35 | typedef struct bmf_font_page_s { 36 | int id; 37 | char file[BMF_MAX_PATH]; 38 | 39 | } BMFontPage; 40 | 41 | typedef struct bmf_font_char_s { 42 | int id; 43 | int x; 44 | int y; 45 | int width; 46 | int height; 47 | int xoffset; 48 | int yoffset; 49 | int xadvance; 50 | int page; 51 | int chnl; 52 | } BMFontChar; 53 | 54 | typedef struct bmf_font_kerning_s { 55 | int first; 56 | int second; 57 | int amount; 58 | } BMFontKerning; 59 | 60 | typedef struct bmf_font_s { 61 | BMFontInfo info; 62 | BMFontCommon common; 63 | BMFontPage page; 64 | int charsCount; 65 | BMFontChar chars[BMF_MAX_CHAR]; 66 | //BMFontKerning kernings[4096]; 67 | } BMFont; 68 | 69 | int bmf_parse(const char *fntPath, BMFont *bmFont); 70 | 71 | #endif //BMFONTLIB_BMFONT_H 72 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "disc.h" 4 | #include "drawing.h" 5 | #include "input.h" 6 | #include "log.h" 7 | #include "menu.h" 8 | #include "utility.h" 9 | 10 | #include 11 | #include 12 | 13 | #ifdef FAT_LIBRARY_FATFS 14 | #include "fatfs/fatfs.h" 15 | #endif 16 | 17 | #ifdef FAT_LIBRARY_KOSFAT 18 | #include 19 | 20 | static kos_blockdev_t sd_dev; 21 | static kos_blockdev_t ide_dev; 22 | 23 | void sdcard_init(void) { 24 | uint8_t partition_type; 25 | 26 | if (sd_init()) 27 | return; 28 | 29 | if (sd_blockdev_for_partition(0, &sd_dev, &partition_type)) 30 | return; 31 | 32 | if (fs_fat_mount("/sd", &sd_dev, FS_FAT_MOUNT_READWRITE)) 33 | return; 34 | 35 | printf("mounted sd card at /sd\n"); 36 | } 37 | 38 | void ide_init(void) { 39 | uint8_t partition_type; 40 | 41 | if (g1_ata_init()) 42 | return; 43 | 44 | if (g1_ata_blockdev_for_partition(0, 1, &ide_dev, &partition_type)) 45 | return; 46 | 47 | if (fs_fat_mount("/ide", &ide_dev, FS_FAT_MOUNT_READWRITE)) 48 | return; 49 | 50 | printf("mounted ide partition at /ide\n"); 51 | } 52 | #endif 53 | 54 | int main(int argc, char **argv) { 55 | dash_log(DBG_INFO, "%s", kos_get_banner()); 56 | 57 | uint32_t keys = get_input(); 58 | if (keys & CONT_A && keys & CONT_B) { 59 | launch_dcload_serial(); 60 | } else if (keys & CONT_X && keys & CONT_Y) { 61 | launch_dcload_ip(); 62 | } 63 | 64 | #ifdef FAT_LIBRARY_KOSFAT 65 | fs_fat_init(); 66 | ide_init(); 67 | sdcard_init(); 68 | #endif 69 | 70 | #ifdef FAT_LIBRARY_FATFS 71 | fs_fat_mount_ide(); 72 | fs_fat_mount_sd(); 73 | #endif 74 | 75 | #ifdef DISC_SUPPORT 76 | disc_init(); 77 | #endif 78 | 79 | draw_init(); 80 | back_init(); 81 | 82 | #ifdef AUTOBOOT 83 | if (keys & CONT_START) { 84 | menu_run(); 85 | } else { 86 | try_boot(); 87 | menu_run(); 88 | } 89 | #else 90 | menu_run(); 91 | #endif 92 | 93 | draw_exit(); 94 | 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /src/drawing.h: -------------------------------------------------------------------------------- 1 | #ifndef LOADER_DRAWING_H 2 | #define LOADER_DRAWING_H 3 | 4 | #include 5 | 6 | #define DRAW_FONT_WIDTH 12.0f 7 | #define DRAW_FONT_HEIGHT 24.0f 8 | #define DRAW_FONT_LINE_SPACING 4.0f 9 | 10 | #define DRAW_PACK_COLOR(a, r, g, b) ( \ 11 | a << 24 | \ 12 | r << 16 | \ 13 | g << 8 | \ 14 | b << 0 ) 15 | 16 | typedef struct color_t { 17 | uint8_t r; 18 | uint8_t g; 19 | uint8_t b; 20 | uint8_t a; 21 | } Color; 22 | 23 | typedef struct rect_t { 24 | float left; 25 | float top; 26 | float width; 27 | float height; 28 | } Rect; 29 | 30 | typedef struct vec2_t { 31 | float x; 32 | float y; 33 | } Vec2; 34 | 35 | #define COL_WHITE (Color) {255, 255, 255, 255} 36 | 37 | #define COL_BLUE (Color) {54, 70, 93, 255} 38 | #define COL_BLUE_LIGHT (Color) {178, 226, 249, 255} 39 | #define COL_BLUE_TRANS (Color) {54, 70, 93, 128} 40 | 41 | #define COL_RED (Color) {255, 81, 72, 255} 42 | #define COL_RED_TRANS (Color) {255, 81, 72, 64} 43 | 44 | #define COL_YELLOW (Color) {240, 226, 107, 255} 45 | 46 | #define COL_TRUE_BLUE (Color) {0, 0, 255, 255} 47 | #define COL_TRUE_BLUE_TRANS1 (Color) {0, 0, 255, 96} 48 | #define COL_TRUE_BLUE_TRANS2 (Color) {0, 0, 255, 128} 49 | 50 | #define COL_GREEN (Color) {0, 255, 0, 255} 51 | #define COL_GREEN_TRANS (Color) {0, 255, 0, 64} 52 | 53 | #define COL_BLACK_TRANS1 (Color) {0, 0, 0, 128} 54 | #define COL_BLACK_TRANS2 (Color) {0, 0, 0, 96} 55 | 56 | void back_init(); 57 | 58 | void draw_back(); 59 | 60 | void draw_init(); 61 | 62 | void draw_exit(); 63 | 64 | void draw_start(); 65 | 66 | void draw_end(); 67 | 68 | void draw_string(float x, float y, float z, Color color, char *str); 69 | 70 | void draw_box(float x, float y, float w, float h, float z, Color color); 71 | 72 | void draw_box_outline(float x, float y, float w, float h, float z, 73 | Color color, Color outline_color, float outline_size); 74 | 75 | int draw_printf(int level, const char *fmt, ...); 76 | 77 | Vec2 draw_get_screen_size(); 78 | 79 | #endif //LOADER_DRAWING_H 80 | -------------------------------------------------------------------------------- /src/fatfs/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2014 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | #define _USE_WRITE 1 /* 1: Enable disk_write function */ 13 | #define _USE_IOCTL 1 /* 1: Enable disk_ioctl fucntion */ 14 | 15 | #include "integer.h" 16 | 17 | 18 | /* Status of Disk Functions */ 19 | typedef BYTE DSTATUS; 20 | 21 | /* Results of Disk Functions */ 22 | typedef enum { 23 | RES_OK = 0, /* 0: Successful */ 24 | RES_ERROR, /* 1: R/W Error */ 25 | RES_WRPRT, /* 2: Write Protected */ 26 | RES_NOTRDY, /* 3: Not Ready */ 27 | RES_PARERR /* 4: Invalid Parameter */ 28 | } DRESULT; 29 | 30 | 31 | /*---------------------------------------*/ 32 | /* Prototypes for disk control functions */ 33 | 34 | 35 | DSTATUS disk_initialize (BYTE pdrv); 36 | DSTATUS disk_status (BYTE pdrv); 37 | DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); 38 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); 39 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 40 | 41 | 42 | /* Disk Status Bits (DSTATUS) */ 43 | 44 | #define STA_NOINIT 0x01 /* Drive not initialized */ 45 | #define STA_NODISK 0x02 /* No medium in the drive */ 46 | #define STA_PROTECT 0x04 /* Write protected */ 47 | 48 | 49 | /* Command code for disk_ioctrl fucntion */ 50 | 51 | /* Generic command (Used by FatFs) */ 52 | #define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ 53 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ 54 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ 55 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ 56 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ 57 | 58 | /* Generic command (Not used by FatFs) */ 59 | #define CTRL_POWER 5 /* Get/Set power status */ 60 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 61 | #define CTRL_EJECT 7 /* Eject media */ 62 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 63 | 64 | /* MMC/SDC specific ioctl command */ 65 | #define MMC_GET_TYPE 10 /* Get card type */ 66 | #define MMC_GET_CSD 11 /* Get CSD */ 67 | #define MMC_GET_CID 12 /* Get CID */ 68 | #define MMC_GET_OCR 13 /* Get OCR */ 69 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 70 | 71 | /* ATA/CF specific ioctl command */ 72 | #define ATA_GET_REV 20 /* Get F/W revision */ 73 | #define ATA_GET_MODEL 21 /* Get model name */ 74 | #define ATA_GET_SN 22 /* Get serial number */ 75 | 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DreamDash BIOS 2 | DreamDash is a replacement BIOS for Dreamcast power users and developers. It requires KallistiOS to build. 3 | 4 | ![dreamdash-preview](https://github.com/user-attachments/assets/b0aa299f-2c0e-4fa7-9a9c-868ccfb49de6) 5 | 6 | ## Functionality 7 | - Launch binaries directly from SD card or IDE drive 8 | - dcload-ip and dcload-serial binaries embedded directly into ROM 9 | - Menu entries appear for DreamShell or RetroDream if found on SD or IDE device 10 | - Configurable to auto-boot into DreamShell or RetroDream if found on on SD or IDE device 11 | - Launch games or applications from GD-ROM drive 12 | - Choose between light or dark wallpaper theme 13 | - Choose between retail or devkit style intro 14 | - Support for Dreamcast consoles with 32MB RAM 15 | - Build as a BIOS, a standalone binary utility, or a CD image 16 | 17 | ## Issues/Limitations 18 | - BIOS image will not work if stripped binary size is larger than roughly 500KB 19 | - Build your KallistiOS, kos-ports, and DreamDash with `-Os` and `-flto=auto` to help stay below this limit 20 | - Always have a backup BIOS image on your console for recovery if flashing to console 21 | - Certain older homebrew is not compatible with consoles using a custom BIOS without patching 22 | - This applies equally for launching from CD, SD, or IDE 23 | - Phantasy Star Online Ver 2's anti-cheat tamper protection causes bugs when launched via this BIOS 24 | 25 | ## How to Use 26 | - [Set up KallistiOS on your computer](https://dreamcast.wiki/Getting_Started_with_Dreamcast_development). 27 | - Compile KOS and the `zlib`/`libpng` kos-ports with `-Os` and `-flto=auto` in your `KOS_CFLAGS` to keep code size small. 28 | - KOS master with commit ID `9c5ee7e` is known to compile with this code. 29 | - Open a terminal and source your KOS environment, clone this repo, and change into this repo's directory. 30 | - Run `make` to build `dreamdash.elf`. Run `kos-strip dreamdash.elf` and check the filesize. If larger than 500KB, it may not work and can produce a non-working BIOS image, so check your `KOS_CFLAGS` and try again. 31 | - Run `make all` to build everything. Check the `release` directory for generated files: 32 | - `dreamdash.bin`: Plain raw binary 33 | - `1ST_READ.BIN`: Scrambled binary for booting from CD-R 34 | - `dreamdash.cdi`: DiscJuggler CDI image for burning to CD-R (requires `mkdcdisc` installed to `$PATH` to generate CDI) 35 | - `dreamdash.bios`: BIOS image with standard bootup intro. There are several variants: 36 | - BIOS files with `devkit` use the devkit bootup intro 37 | - BIOS files with `nogdrom` are used on consoles with no GD-ROM drive installed (**currently untested!**) 38 | - BIOS files with `32mb` are used on consoles with 32MB RAM modification 39 | - Use DreamShell's BIOS Flasher application to write the `.bios` file to a Dreamcast's writeable BIOS flashROM. 40 | 41 | ## Discussion 42 | A chat room is available on the [dreamcast.wiki Discord server](https://discord.gg/Bs6Fe4stzE). Join us! 43 | 44 | ## Acknowledgements 45 | - **KallistiOS** and **DreamShell** - kernel and drivers for the underlying operating system 46 | - **Cpajuste** - Dreamboot, from which this project was originally forked 47 | - **Troy D. Hanson & Arthur O'Dwyer** - ut* C structures libraries 48 | -------------------------------------------------------------------------------- /src/bmfont.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "bmfont.h" 6 | 7 | int bmf_parse(const char *fntPath, BMFont *bmFont) { 8 | 9 | FILE *fd = fopen(fntPath, "r"); 10 | if (fd == NULL) { 11 | return -1; 12 | } 13 | 14 | char *lineBuf = (char *) malloc(BMF_MAX_PATH); 15 | 16 | /// parse info 17 | if (fgets(lineBuf, BMF_MAX_PATH, fd) == NULL) { 18 | fclose(fd); 19 | free(lineBuf); 20 | return -1; 21 | } 22 | // TODO: fix empty "charset" 23 | if (sscanf(lineBuf, 24 | "info face=\"%[^\"]\" size=%d bold=%d italic=%d charset=\"%[^\"]\" unicode=%d stretchH=%d smooth=%d aa=%d padding=%d,%d,%d,%d spacing=%d,%d outline=%d\n", 25 | (char *) &bmFont->info.face, &bmFont->info.size, &bmFont->info.bold, &bmFont->info.italic, (char *) &bmFont->info.charset, 26 | &bmFont->info.unicode, &bmFont->info.stretchH, &bmFont->info.smooth, &bmFont->info.aa, 27 | &bmFont->info.padding[0], &bmFont->info.padding[1], &bmFont->info.padding[2], &bmFont->info.padding[3], 28 | &bmFont->info.spacing[0], &bmFont->info.spacing[1], &bmFont->info.outline) != 16) { 29 | fclose(fd); 30 | free(lineBuf); 31 | printf("sscanf failed on info\n"); 32 | return -1; 33 | } 34 | 35 | /// parse common 36 | if (fgets(lineBuf, BMF_MAX_PATH, fd) == NULL) { 37 | fclose(fd); 38 | free(lineBuf); 39 | return -1; 40 | } 41 | if (sscanf(lineBuf, 42 | "common lineHeight=%d base=%d scaleW=%d scaleH=%d pages=%d packed=%d alphaChnl=%d redChnl=%d greenChnl=%d blueChnl=%d\n", 43 | &bmFont->common.lineHeight, &bmFont->common.base, 44 | &bmFont->common.scaleW, &bmFont->common.scaleH, 45 | &bmFont->common.pages, &bmFont->common.packed, 46 | &bmFont->common.alphaChnl, 47 | &bmFont->common.redChnl, &bmFont->common.greenChnl, &bmFont->common.blueChnl) != 10) { 48 | fclose(fd); 49 | free(lineBuf); 50 | printf("sscanf failed on common\n"); 51 | return -1; 52 | } 53 | 54 | /// parse page 55 | if (fgets(lineBuf, BMF_MAX_PATH, fd) == NULL) { 56 | fclose(fd); 57 | free(lineBuf); 58 | return -1; 59 | } 60 | if (sscanf(lineBuf, "page id=%d file=\"%[^\"]\"\n", &bmFont->page.id, (char *) &bmFont->page.file) != 2) { 61 | fclose(fd); 62 | free(lineBuf); 63 | printf("sscanf failed on page\n"); 64 | return -1; 65 | } 66 | 67 | /// parse chars count 68 | if (fgets(lineBuf, BMF_MAX_PATH, fd) == NULL) { 69 | fclose(fd); 70 | free(lineBuf); 71 | return -1; 72 | } 73 | if (sscanf(lineBuf, "chars count=%d\n", &bmFont->charsCount) != 1) { 74 | fclose(fd); 75 | free(lineBuf); 76 | printf("sscanf failed on chars count\n"); 77 | return -1; 78 | } 79 | 80 | /// parse chars 81 | char str_id[3]; 82 | int id; 83 | 84 | if (bmFont->charsCount > BMF_MAX_CHAR) { 85 | bmFont->charsCount = BMF_MAX_CHAR; 86 | } 87 | 88 | for (int i = 0; i < bmFont->charsCount; i++) { 89 | if (fgets(lineBuf, BMF_MAX_PATH, fd) == NULL) { 90 | fclose(fd); 91 | free(lineBuf); 92 | return -1; 93 | } 94 | // extract for id 95 | char *pos = strchr(lineBuf, '=') + 1; 96 | if (pos == NULL) { 97 | continue; 98 | } 99 | 100 | memset(str_id, 0, 3); 101 | strncpy(str_id, pos, 3); 102 | id = atoi(str_id); 103 | if (id >= BMF_MAX_CHAR) { 104 | continue; 105 | } 106 | 107 | if (sscanf(lineBuf, 108 | "char id=%d x=%d y=%d width=%d height=%d xoffset=%d yoffset=%d xadvance=%d page=%d chnl=%d\n", 109 | &bmFont->chars[id].id, &bmFont->chars[id].x, &bmFont->chars[id].y, 110 | &bmFont->chars[id].width, &bmFont->chars[id].height, 111 | &bmFont->chars[id].xoffset, &bmFont->chars[id].yoffset, &bmFont->chars[id].xadvance, 112 | &bmFont->chars[id].page, &bmFont->chars[id].chnl) != 10) { 113 | fclose(fd); 114 | free(lineBuf); 115 | printf("sscanf failed on char[%i]\n", i); 116 | return -1; 117 | } 118 | } 119 | 120 | fclose(fd); 121 | free(lineBuf); 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /src/fatfs/fatfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FatFs for the Sega Dreamcast 3 | * 4 | * This file is part of the FatFs module, a generic FAT filesystem 5 | * module for small embedded systems. This version has been ported and 6 | * optimized specifically for the Sega Dreamcast platform. 7 | * 8 | * Copyright (c) 2007-2025 Ruslan Rostovtsev 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a 11 | * copy of this software and associated documentation files (the "Software"), 12 | * to deal in the Software without restriction, including without limitation 13 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | * and/or sell copies of the Software, and to permit persons to whom the 15 | * Software is furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included 18 | * in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | /** \file fatfs.h 30 | \brief FatFs for the Sega Dreamcast. 31 | \author Ruslan Rostovtsev 32 | */ 33 | #ifndef _FATFS_H 34 | #define _FATFS_H 35 | 36 | #include 37 | 38 | /** 39 | * \enum fatfs_ioctl_t 40 | * \brief FAT filesystem IOCTL commands. 41 | */ 42 | typedef enum fatfs_ioctl { 43 | 44 | FATFS_IOCTL_CTRL_SYNC = 0, /**< Flush disk cache (for write functions). */ 45 | FATFS_IOCTL_GET_SECTOR_COUNT, /**< Get media size (for f_mkfs()), 4-byte unsigned. */ 46 | FATFS_IOCTL_GET_SECTOR_SIZE, /**< Get sector size (for multiple sector size (_MAX_SS >= 1024)), 2-byte unsigned. */ 47 | FATFS_IOCTL_GET_BLOCK_SIZE, /**< Get erase block size (for f_mkfs()), 2-byte unsigned. */ 48 | FATFS_IOCTL_CTRL_ERASE_SECTOR, /**< Force erase a block of sectors (for _USE_ERASE). */ 49 | FATFS_IOCTL_GET_BOOT_SECTOR_DATA, /**< Get first sector data, ffconf.h _MAX_SS bytes. */ 50 | FATFS_IOCTL_GET_FD_LBA, /**< Get file LBA, 4-byte unsigned. */ 51 | FATFS_IOCTL_GET_FD_LINK_MAP /**< Get file clusters link map, 128+ bytes. */ 52 | 53 | } fatfs_ioctl_t; 54 | 55 | /** 56 | * \brief Initialize the FAT filesystem. 57 | * 58 | * \return 0 on success, or a negative value if an error occurred. 59 | */ 60 | int fs_fat_init(void); 61 | 62 | /** 63 | * \brief Shutdown the FAT filesystem. 64 | * 65 | * \return 0 on success, or a negative value if an error occurred. 66 | */ 67 | int fs_fat_shutdown(void); 68 | 69 | /** 70 | * \brief Mount the FAT filesystem on the specified partition. 71 | * 72 | * \param mp Mount point path. 73 | * \param dev_pio Pointer to the block device for PIO. 74 | * \param dev_dma Pointer to the block device for DMA. 75 | * \param partition Partition number (reset to 0 for start block). 76 | * \return 0 on success, or a negative value if an error occurred. 77 | */ 78 | int fs_fat_mount(const char *mp, kos_blockdev_t *dev_pio, 79 | kos_blockdev_t *dev_dma, int partition); 80 | 81 | /** 82 | * \brief Unmount the FAT filesystem. 83 | * 84 | * \param mp Mount point path. 85 | * \return 0 on success, or a negative value if an error occurred. 86 | */ 87 | int fs_fat_unmount(const char *mp); 88 | 89 | /** 90 | * \brief Check if a mount point is using a FAT filesystem. 91 | * 92 | * \param mp Mount point path. 93 | * \return 0 if not FAT, 1 if FAT. 94 | */ 95 | int fs_fat_is_mounted(const char *mp); 96 | 97 | /** 98 | * \brief Initialize the FAT and SD card, then mount all partitions on it. 99 | * This function will try to detect and mount both SCIF and SCI interfaces 100 | * if they are available. 101 | * 102 | * \return 0 on success, or a negative value if an error occurred. 103 | */ 104 | int fs_fat_mount_sd(void); 105 | 106 | /** 107 | * \brief Unmount all SD card partitions and free resources. 108 | */ 109 | void fs_fat_unmount_sd(void); 110 | 111 | /** 112 | * \brief Initialize the FAT and IDE (G1-ATA), then mount all partitions on it. 113 | * 114 | * \return 0 on success, or a negative value if an error occurred. 115 | */ 116 | int fs_fat_mount_ide(void); 117 | 118 | /** 119 | * \brief Unmount all IDE partitions and free resources. 120 | */ 121 | void fs_fat_unmount_ide(void); 122 | 123 | #endif /* _FATFS_H */ 124 | -------------------------------------------------------------------------------- /src/disc.c: -------------------------------------------------------------------------------- 1 | /* 2 | disc.c 3 | Copyright (C)2024 darcagn 4 | 5 | Derived from DreamShell GDPlay module 6 | Copyright (C)2014 megavolt85 7 | Copyright (C)2024 SWAT 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "disc.h" 15 | #include "log.h" 16 | #include "utility.h" 17 | 18 | #define RUNGZ_FILE "/rd/rungd.bin.gz" 19 | #define RUNGZ_SIZE 65280 20 | 21 | ip_meta_t *ip_info; 22 | static int cmd_response; 23 | static int status; 24 | static int disc_type; 25 | 26 | kthread_t *check_gdrom_thd; 27 | int kill_gdrom_thd = 0; 28 | 29 | static void set_info() { 30 | int lba = 45150; 31 | char pbuff[2048]; 32 | 33 | cdrom_reinit(); 34 | cdrom_get_status(&status, &disc_type); 35 | 36 | if(disc_type == CD_CDROM_XA) { 37 | CDROM_TOC toc; 38 | 39 | if(cdrom_read_toc(&toc, 0) != ERR_OK) { 40 | printf("Error reading disc TOC!\n"); 41 | return; 42 | } 43 | 44 | if(!(lba = cdrom_locate_data_track(&toc))) { 45 | printf("Error locating data track on disc!\n"); 46 | return; 47 | } 48 | } 49 | 50 | cmd_response = cdrom_read_sectors(pbuff, lba, 1); 51 | 52 | if(cmd_response != ERR_OK) { 53 | if(cmd_response == (ERR_DISC_CHG || ERR_NO_DISC)) { 54 | return; 55 | } else { 56 | printf("Error %d reading disc at LBA %d\n", cmd_response, lba); 57 | return; 58 | } 59 | } 60 | 61 | ip_info = (ip_meta_t *) pbuff; 62 | 63 | if(strncmp(ip_info->hardware_ID, "SEGA", 4)) { 64 | printf("No valid initial program (IP.BIN) found.\n"); 65 | return; 66 | } 67 | 68 | printf("\nDisc header info:\n" 69 | "\tHardware ID:\t%.*s\n" 70 | "\tMaker ID:\t%.*s\n" 71 | "\tHeader CRC:\t%.*s\n" 72 | "\tDisc Number:\t%c of %c\n" 73 | "\tRegion(s):\t%.*s\n" 74 | "\tControl:\t%.*s\n" 75 | "\tDevices:\t%.*s\n" 76 | "\tVGA support:\t%s\n" 77 | "\tWindows CE:\t%s\n" 78 | "\tProduct ID:\t%.*s\n" 79 | "\tVersion:\t%.*s\n" 80 | "\tDate:\t\t%c%c%c%c-%c%c-%c%c\n" 81 | "\tBoot file:\t%.*s\n" 82 | "\tDeveloper:\t%.*s\n" 83 | "\tTitle:\t\t%.*s\n", 84 | 16, ip_info->hardware_ID, 85 | 16, ip_info->maker_ID, 86 | 5, ip_info->ks, 87 | ip_info->disk_num[0], ip_info->disk_num[2], 88 | 3, ip_info->country_codes, 89 | 4, ip_info->ctrl, 90 | 1, ip_info->dev, 91 | ip_info->VGA[0] == '1' ? "Yes" : "No", 92 | ip_info->WinCE[0] == '1' ? "Yes" : "No", 93 | 10, ip_info->product_ID, 94 | 6, ip_info->product_version, 95 | ip_info->release_date[0], ip_info->release_date[1], ip_info->release_date[2], ip_info->release_date[3], 96 | ip_info->release_date[4], ip_info->release_date[5], ip_info->release_date[6], ip_info->release_date[7], 97 | 16, ip_info->boot_file, 98 | 16, ip_info->software_maker_info, 99 | 128, ip_info->title); 100 | fflush(stdout); 101 | } 102 | 103 | static void *check_gdrom() { 104 | cdrom_init(); 105 | 106 | while(!kill_gdrom_thd) { 107 | if(cdrom_get_status(&status, &disc_type) == ERR_OK) { 108 | switch(status) { 109 | case CD_STATUS_OPEN: 110 | case CD_STATUS_NO_DISC: 111 | if(ip_info) { 112 | printf("\nPlease insert disc and close drive lid...\n"); 113 | ip_info = NULL; 114 | } 115 | break; 116 | default: 117 | switch(disc_type) { 118 | case CD_CDROM_XA: 119 | case CD_GDROM: 120 | if(!ip_info) 121 | set_info(); 122 | break; 123 | } 124 | } 125 | } 126 | thd_pass(); 127 | } 128 | 129 | return NULL; 130 | } 131 | 132 | void disc_launch(void) { 133 | printf("Shutting down KOS and lauching disc... have fun!\n\n"); 134 | 135 | /* Open syscalls patch */ 136 | gzFile rungz = gzopen(RUNGZ_FILE, "rb"); 137 | if(!rungz) { 138 | dash_log(DBG_ERROR, "Error opening %s!", RUNGZ_FILE); 139 | return; 140 | } 141 | 142 | /* Decompress patched syscalls into place */ 143 | if(gzread(rungz, (void *)0x8C000100, RUNGZ_SIZE) != RUNGZ_SIZE) { 144 | dash_log(DBG_ERROR, "Error decompressing %s!", RUNGZ_FILE); 145 | return; 146 | } 147 | 148 | /* Clean up */ 149 | gzclose(rungz); 150 | disc_shutdown(); 151 | fflush(stdout); 152 | 153 | /* Disable and invalidate the cache */ 154 | *(volatile unsigned long *)0xFF00001C = 0x0808; 155 | 156 | /* Bye bye! */ 157 | ((void (*)(volatile unsigned short))0x8C000120)(0xFFF); 158 | __builtin_unreachable(); 159 | } 160 | 161 | void disc_shutdown(void) { 162 | kill_gdrom_thd = 1; 163 | thd_join(check_gdrom_thd, NULL); 164 | } 165 | 166 | int disc_init(void) { 167 | // TODO: Check if GD-ROM drive is available is available 168 | // If not, dash_log(DBG_INFO, "No GD-ROM drive found."); return -1; 169 | 170 | if(!file_exists("/rd/rungd.bin.gz")) { 171 | dash_log(DBG_ERROR, "Error accessing rungd.bin.gz!"); 172 | } 173 | 174 | check_gdrom_thd = thd_create(1, check_gdrom, NULL); 175 | 176 | dash_log(DBG_INFO, "GD-ROM initialized."); 177 | 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /src/fatfs/option/syscall.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Sample code of OS dependent controls for FatFs */ 3 | /* (C)ChaN, 2014 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include "../ff.h" 8 | 9 | 10 | #if _FS_REENTRANT 11 | /*------------------------------------------------------------------------*/ 12 | /* Create a Synchronization Object */ 13 | /*------------------------------------------------------------------------*/ 14 | /* This function is called in f_mount() function to create a new 15 | / synchronization object, such as semaphore and mutex. When a 0 is returned, 16 | / the f_mount() function fails with FR_INT_ERR. 17 | */ 18 | 19 | int ff_cre_syncobj ( /* !=0:Function succeeded, ==0:Could not create due to any error */ 20 | BYTE vol, /* Corresponding logical drive being processed */ 21 | _SYNC_t *sobj /* Pointer to return the created sync object */ 22 | ) 23 | { 24 | int ret; 25 | 26 | 27 | *sobj = CreateMutex(NULL, FALSE, NULL); /* Win32 */ 28 | ret = (int)(*sobj != INVALID_HANDLE_VALUE); 29 | 30 | // *sobj = SyncObjects[vol]; /* uITRON (give a static created sync object) */ 31 | // ret = 1; /* The initial value of the semaphore must be 1. */ 32 | 33 | // *sobj = OSMutexCreate(0, &err); /* uC/OS-II */ 34 | // ret = (int)(err == OS_NO_ERR); 35 | 36 | // *sobj = xSemaphoreCreateMutex(); /* FreeRTOS */ 37 | // ret = (int)(*sobj != NULL); 38 | 39 | return ret; 40 | } 41 | 42 | 43 | 44 | /*------------------------------------------------------------------------*/ 45 | /* Delete a Synchronization Object */ 46 | /*------------------------------------------------------------------------*/ 47 | /* This function is called in f_mount() function to delete a synchronization 48 | / object that created with ff_cre_syncobj function. When a 0 is returned, 49 | / the f_mount() function fails with FR_INT_ERR. 50 | */ 51 | 52 | int ff_del_syncobj ( /* !=0:Function succeeded, ==0:Could not delete due to any error */ 53 | _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ 54 | ) 55 | { 56 | int ret; 57 | 58 | 59 | ret = CloseHandle(sobj); /* Win32 */ 60 | 61 | // ret = 1; /* uITRON (nothing to do) */ 62 | 63 | // OSMutexDel(sobj, OS_DEL_ALWAYS, &err); /* uC/OS-II */ 64 | // ret = (int)(err == OS_NO_ERR); 65 | 66 | // vSemaphoreDelete(sobj); /* FreeRTOS */ 67 | // ret = 1; 68 | 69 | return ret; 70 | } 71 | 72 | 73 | 74 | /*------------------------------------------------------------------------*/ 75 | /* Request Grant to Access the Volume */ 76 | /*------------------------------------------------------------------------*/ 77 | /* This function is called on entering file functions to lock the volume. 78 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 79 | */ 80 | 81 | int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ 82 | _SYNC_t sobj /* Sync object to wait */ 83 | ) 84 | { 85 | int ret; 86 | 87 | ret = (int)(WaitForSingleObject(sobj, _FS_TIMEOUT) == WAIT_OBJECT_0); /* Win32 */ 88 | 89 | // ret = (int)(wai_sem(sobj) == E_OK); /* uITRON */ 90 | 91 | // OSMutexPend(sobj, _FS_TIMEOUT, &err)); /* uC/OS-II */ 92 | // ret = (int)(err == OS_NO_ERR); 93 | 94 | // ret = (int)(xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE); /* FreeRTOS */ 95 | 96 | return ret; 97 | } 98 | 99 | 100 | 101 | /*------------------------------------------------------------------------*/ 102 | /* Release Grant to Access the Volume */ 103 | /*------------------------------------------------------------------------*/ 104 | /* This function is called on leaving file functions to unlock the volume. 105 | */ 106 | 107 | void ff_rel_grant ( 108 | _SYNC_t sobj /* Sync object to be signaled */ 109 | ) 110 | { 111 | ReleaseMutex(sobj); /* Win32 */ 112 | 113 | // sig_sem(sobj); /* uITRON */ 114 | 115 | // OSMutexPost(sobj); /* uC/OS-II */ 116 | 117 | // xSemaphoreGive(sobj); /* FreeRTOS */ 118 | } 119 | 120 | #endif 121 | 122 | 123 | 124 | 125 | #if _USE_LFN == 3 /* LFN with a working buffer on the heap */ 126 | /*------------------------------------------------------------------------*/ 127 | /* Allocate a memory block */ 128 | /*------------------------------------------------------------------------*/ 129 | /* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE. 130 | */ 131 | 132 | void* ff_memalloc ( /* Returns pointer to the allocated memory block */ 133 | UINT msize /* Number of bytes to allocate */ 134 | ) 135 | { 136 | return malloc(msize); /* Allocate a new memory block with POSIX API */ 137 | } 138 | 139 | 140 | /*------------------------------------------------------------------------*/ 141 | /* Free a memory block */ 142 | /*------------------------------------------------------------------------*/ 143 | 144 | void ff_memfree ( 145 | void* mblock /* Pointer to the memory block to free */ 146 | ) 147 | { 148 | free(mblock); /* Discard the memory block with POSIX API */ 149 | } 150 | 151 | #endif 152 | -------------------------------------------------------------------------------- /src/drawing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "bmfont.h" 7 | #include "drawing.h" 8 | #include "log.h" 9 | 10 | static BMFont bmf_font; 11 | static pvr_ptr_t bmf_tex = NULL; 12 | 13 | pvr_init_params_t params = { 14 | {PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_32, PVR_BINSIZE_0, PVR_BINSIZE_0}, 15 | 512 * 1024 16 | }; 17 | 18 | typedef struct { 19 | char id[4]; 20 | short width; 21 | short height; 22 | int type; 23 | int size; 24 | } tex_header_t; 25 | 26 | pvr_ptr_t back_tex; 27 | 28 | void back_init(void) { 29 | back_tex = pvr_mem_malloc(WALLPAPER_RES * WALLPAPER_RES * 2); 30 | png_to_texture("/rd/"WALLPAPER_FILE, back_tex, PNG_NO_ALPHA); 31 | } 32 | 33 | void draw_back(void) { 34 | pvr_poly_cxt_t cxt; 35 | pvr_poly_hdr_t hdr; 36 | pvr_vertex_t vert; 37 | 38 | pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_RGB565, WALLPAPER_RES, WALLPAPER_RES, back_tex, PVR_FILTER_BILINEAR); 39 | pvr_poly_compile(&hdr, &cxt); 40 | pvr_prim(&hdr, sizeof(hdr)); 41 | 42 | vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); 43 | vert.oargb = 0; 44 | vert.flags = PVR_CMD_VERTEX; 45 | 46 | vert.x = 1; 47 | vert.y = 1; 48 | vert.z = 1; 49 | vert.u = 0.0; 50 | vert.v = 0.0; 51 | pvr_prim(&vert, sizeof(vert)); 52 | 53 | vert.x = 640; 54 | vert.y = 1; 55 | vert.z = 1; 56 | vert.u = 1.0; 57 | vert.v = 0.0; 58 | pvr_prim(&vert, sizeof(vert)); 59 | 60 | vert.x = 1; 61 | vert.y = 480; 62 | vert.z = 1; 63 | vert.u = 0.0; 64 | vert.v = 1.0; 65 | pvr_prim(&vert, sizeof(vert)); 66 | 67 | vert.x = 640; 68 | vert.y = 480; 69 | vert.z = 1; 70 | vert.u = 1.0; 71 | vert.v = 1.0; 72 | vert.flags = PVR_CMD_VERTEX_EOL; 73 | pvr_prim(&vert, sizeof(vert)); 74 | } 75 | 76 | static void draw_init_font() { 77 | 78 | FILE *fp; 79 | tex_header_t hdr; 80 | 81 | // parse BMFont font information 82 | if (bmf_parse("/rd/ebdragon.fnt", &bmf_font) != 0) { 83 | return; 84 | } 85 | 86 | // load "texconv" texture 87 | fp = fopen("/rd/ebdragon.tex", "r"); 88 | if (fp == NULL) { 89 | return; 90 | } 91 | // read "texconv" texture header 92 | fread(&hdr, sizeof(hdr), 1, fp); 93 | // allocate pvr mem 94 | bmf_tex = pvr_mem_malloc(hdr.size); 95 | // read "texconv" texture to pvr mem 96 | fread(bmf_tex, hdr.size, 1, fp); 97 | 98 | // all done 99 | fclose(fp); 100 | } 101 | 102 | static void draw_char(float x1, float y1, float z1, Color color, BMFontChar *c) { 103 | 104 | pvr_vertex_t vert; 105 | 106 | vert.flags = PVR_CMD_VERTEX; 107 | vert.x = x1 + (float) c->xoffset; 108 | vert.y = y1 + (float) c->height + (float) c->yoffset; 109 | vert.z = z1; 110 | vert.u = (float) c->x / (float) bmf_font.common.scaleW; 111 | vert.v = (float) (c->y + c->height) / (float) bmf_font.common.scaleH; 112 | vert.argb = DRAW_PACK_COLOR(color.a, color.r, color.g, color.b); 113 | vert.oargb = 0; 114 | pvr_prim(&vert, sizeof(vert)); 115 | 116 | vert.x = x1 + (float) c->xoffset; 117 | vert.y = y1 + (float) c->yoffset; 118 | vert.u = (float) c->x / (float) bmf_font.common.scaleW; 119 | vert.v = (float) c->y / (float) bmf_font.common.scaleH; 120 | pvr_prim(&vert, sizeof(vert)); 121 | 122 | vert.x = x1 + (float) (c->width + c->xoffset); 123 | vert.y = y1 + (float) (c->height + c->yoffset); 124 | vert.u = (float) (c->x + c->width) / (float) bmf_font.common.scaleW; 125 | vert.v = (float) (c->y + c->height) / (float) bmf_font.common.scaleH; 126 | pvr_prim(&vert, sizeof(vert)); 127 | 128 | vert.flags = PVR_CMD_VERTEX_EOL; 129 | vert.x = x1 + (float) (c->width + c->xoffset); 130 | vert.y = y1 + (float) c->yoffset; 131 | vert.u = (float) (c->x + c->width) / (float) bmf_font.common.scaleW; 132 | vert.v = (float) c->y / (float) bmf_font.common.scaleH; 133 | pvr_prim(&vert, sizeof(vert)); 134 | } 135 | 136 | /* draw len chars at string */ 137 | void draw_string(float x, float y, float z, Color color, char *str) { 138 | 139 | int i, len; 140 | pvr_poly_cxt_t cxt; 141 | pvr_poly_hdr_t poly; 142 | 143 | pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, PVR_TXRFMT_ARGB1555 | PVR_TXRFMT_VQ_ENABLE, 144 | 256, 256, bmf_tex, PVR_FILTER_NONE); 145 | pvr_poly_compile(&poly, &cxt); 146 | pvr_prim(&poly, sizeof(poly)); 147 | 148 | len = strlen(str); 149 | for (i = 0; i < len; i++) { 150 | char c = str[i]; 151 | if (!(c > 31 && c < 127)) { 152 | continue; 153 | } 154 | BMFontChar bmfChar = bmf_font.chars[(int) c]; 155 | draw_char(x, y, z, color, &bmfChar); 156 | x += (float) (bmfChar.xadvance + bmfChar.xoffset); 157 | } 158 | } 159 | 160 | /* draw a box (used by cursor and border, etc) (at 1.0f z coord) */ 161 | void draw_box(float x, float y, float w, float h, float z, Color color) { 162 | pvr_poly_cxt_t cxt; 163 | pvr_poly_hdr_t poly; 164 | pvr_vertex_t vert; 165 | 166 | pvr_poly_cxt_col(&cxt, PVR_LIST_TR_POLY); 167 | pvr_poly_compile(&poly, &cxt); 168 | pvr_prim(&poly, sizeof(poly)); 169 | 170 | vert.flags = PVR_CMD_VERTEX; 171 | vert.x = x; 172 | vert.y = y + h; 173 | vert.z = z; 174 | vert.u = vert.v = 0.0f; 175 | vert.argb = DRAW_PACK_COLOR(color.a, color.r, color.g, color.b); 176 | vert.oargb = 0; 177 | pvr_prim(&vert, sizeof(vert)); 178 | 179 | vert.y -= h; 180 | pvr_prim(&vert, sizeof(vert)); 181 | 182 | vert.y += h; 183 | vert.x += w; 184 | pvr_prim(&vert, sizeof(vert)); 185 | 186 | vert.flags = PVR_CMD_VERTEX_EOL; 187 | vert.y -= h; 188 | pvr_prim(&vert, sizeof(vert)); 189 | } 190 | 191 | void draw_box_outline(float x, float y, float w, float h, float z, Color color, 192 | Color outline_color, float outline_size) { 193 | 194 | draw_box(x - outline_size, y - outline_size, w + (outline_size * 2), h + (outline_size * 2), z - 1, outline_color); 195 | draw_box(x, y, w, h, z, color); 196 | } 197 | 198 | void draw_init() { 199 | pvr_init(¶ms); 200 | draw_init_font(); 201 | } 202 | 203 | void draw_exit() { 204 | if (bmf_tex != NULL) { 205 | pvr_mem_free(bmf_tex); 206 | } 207 | } 208 | 209 | void draw_start() { 210 | pvr_wait_ready(); 211 | pvr_scene_begin(); 212 | pvr_list_begin(PVR_LIST_TR_POLY); 213 | } 214 | 215 | void draw_end() { 216 | pvr_list_finish(); 217 | pvr_scene_finish(); 218 | } 219 | 220 | Vec2 draw_get_screen_size() { 221 | return (Vec2) {640, 480}; 222 | } 223 | 224 | int draw_printf(int level, const char *fmt, ...) { 225 | 226 | if (bmf_tex == NULL) { 227 | return 0; 228 | } 229 | 230 | char buff[512]; 231 | va_list args; 232 | Color color = COL_WHITE; 233 | Vec2 screenSize = draw_get_screen_size(); 234 | 235 | memset(buff, 0, 512); 236 | va_start(args, fmt); 237 | int ret = vsnprintf(buff, 512, fmt, args); 238 | va_end(args); 239 | 240 | switch (level) { 241 | case DBG_DEAD: 242 | case DBG_ERROR: 243 | case DBG_CRITICAL: 244 | color = COL_RED; 245 | break; 246 | case DBG_WARNING: 247 | color = COL_YELLOW; 248 | default: 249 | break; 250 | } 251 | 252 | draw_start(); 253 | draw_string(16, screenSize.y - DRAW_FONT_HEIGHT - 16, 200, color, buff); 254 | draw_end(); 255 | 256 | return ret; 257 | } 258 | -------------------------------------------------------------------------------- /src/utility.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "drawing.h" 7 | #include "utility.h" 8 | #include "utlist.h" 9 | 10 | KOS_INIT_FLAGS(INIT_IRQ | INIT_THD_PREEMPT | INIT_FS_ALL | \ 11 | INIT_LIBRARY | INIT_CDROM | INIT_CONTROLLER | INIT_VMU); 12 | 13 | void dash_log(int level, const char *fmt, ...); 14 | 15 | #define dbglog(lv, fmt, ...) dash_log(lv, fmt, ##__VA_ARGS__) 16 | 17 | int list_cmp(ListItem *a, ListItem *b) { 18 | 19 | if (a->type == TYPE_DIR && b->type != TYPE_DIR) { 20 | return -1; 21 | } else if (a->type != TYPE_DIR && b->type == TYPE_DIR) { 22 | return 1; 23 | } 24 | 25 | return strcasecmp(a->name, b->name); 26 | } 27 | 28 | void free_dir(List *list) { 29 | 30 | ListItem *elt, *tmp; 31 | DL_FOREACH_SAFE(list->head, elt, tmp) { 32 | DL_DELETE(list->head, elt); 33 | free(elt); 34 | } 35 | } 36 | 37 | ListItem *get_item(List *list, int index) { 38 | 39 | ListItem *file = list->head; 40 | if (index == 0) { 41 | return file; 42 | } 43 | 44 | for (int i = 1; i < list->size; i++) { 45 | file = (ListItem *) file->next; 46 | if (index == i) { 47 | return file; 48 | } 49 | } 50 | 51 | return NULL; 52 | } 53 | 54 | void try_boot() { 55 | 56 | // first check for boot config 57 | if (file_exists("/sd/boot.cfg")) { 58 | char *path = read_file("/sd/boot.cfg", NULL); 59 | if (path != NULL) { 60 | trim(path); 61 | if (file_exists(path)) { 62 | exec(path); 63 | } 64 | } 65 | } else if (file_exists("/ide/boot.cfg")) { 66 | char *path = read_file("/ide/boot.cfg", NULL); 67 | if (path != NULL) { 68 | trim(path); 69 | if (file_exists(path)) { 70 | exec(path); 71 | } 72 | } 73 | } 74 | 75 | // then retrodream.bin 76 | launch_retrodream(); 77 | 78 | // finally check for DS_CORE.BIN 79 | launch_dreamshell(); 80 | } 81 | 82 | void trim(char *str) { 83 | 84 | char *pos = NULL; 85 | 86 | while ((pos = strrchr(str, '\n')) != NULL) { 87 | *pos = '\0'; 88 | } 89 | 90 | size_t len = strlen(str) - 1; 91 | for (int i = len; i; i--) { 92 | if (str[i] > ' ') { 93 | break; 94 | } 95 | str[i] = '\0'; 96 | } 97 | } 98 | 99 | void get_dir(List *list, const char *path) { 100 | 101 | dirent_t *ent; 102 | file_t fd; 103 | ListItem *entry; 104 | 105 | memset(list, 0, sizeof(List)); 106 | strncpy(list->path, path, MAX_PATH - 1); 107 | 108 | if ((fd = fs_open(path, O_RDONLY | O_DIR)) != FILEHND_INVALID) { 109 | while ((ent = fs_readdir(fd)) != NULL) { 110 | 111 | // skip "." 112 | if (ent->name[0] == '.') { 113 | continue; 114 | } 115 | 116 | if (strncmp(ent->name, "dev", 3) == 0 || strncmp(ent->name, "pty", 3) == 0 117 | || strncmp(ent->name, "ram", 3) == 0 || strncmp(ent->name, "pc", 2) == 0 118 | || strncmp(ent->name, "cd", 2) == 0) { 119 | continue; 120 | } 121 | 122 | entry = (ListItem *) malloc(sizeof(ListItem)); 123 | memset(entry, 0, sizeof(ListItem)); 124 | 125 | strncpy(entry->name, ent->name, MAX_PATH - 1); 126 | if (list->path[strlen(list->path) - 1] != '/') { 127 | snprintf(entry->path, MAX_PATH - 1, "%s/%s", list->path, ent->name); 128 | } else { 129 | snprintf(entry->path, MAX_PATH - 1, "%s%s", list->path, ent->name); 130 | } 131 | 132 | entry->type = ent->attr == O_DIR ? TYPE_DIR : TYPE_FILE; 133 | if (entry->type == TYPE_FILE) { 134 | if (strstr(entry->name, ".bin") != NULL || strstr(entry->name, ".BIN") != NULL || 135 | strstr(entry->name, ".elf") != NULL || strstr(entry->name, ".ELF") != NULL) { 136 | entry->type = TYPE_BIN; 137 | } 138 | } 139 | 140 | DL_APPEND(list->head, entry); 141 | list->size++; 142 | } 143 | 144 | DL_SORT(list->head, list_cmp); 145 | fs_close(fd); 146 | } 147 | } 148 | 149 | int file_exists(const char *fn) { 150 | file_t f; 151 | 152 | f = fs_open(fn, O_RDONLY); 153 | 154 | if (f == FILEHND_INVALID) { 155 | return 0; 156 | } 157 | 158 | fs_close(f); 159 | return 1; 160 | } 161 | 162 | int dir_exists(const char *dir) { 163 | file_t f; 164 | 165 | f = fs_open(dir, O_DIR | O_RDONLY); 166 | 167 | if (f == FILEHND_INVALID) { 168 | return 0; 169 | } 170 | 171 | fs_close(f); 172 | return 1; 173 | } 174 | 175 | char *read_file(const char *file, int *size) { 176 | 177 | file_t fd; 178 | ssize_t fsize; 179 | char *buffer = NULL; 180 | 181 | fd = fs_open(file, O_RDONLY); 182 | if (fd == FILEHND_INVALID) { 183 | printf("read_file: can't open %s\n", file); 184 | if (size != NULL) { 185 | *size = 0; 186 | } 187 | return NULL; 188 | } 189 | 190 | fsize = fs_total(fd); 191 | buffer = (char *) malloc(fsize); 192 | memset(buffer, 0, fsize); 193 | 194 | if (fs_read(fd, buffer, fsize) != fsize) { 195 | fs_close(fd); 196 | free(buffer); 197 | printf("read_file: can't read %s\n", file); 198 | if (size != NULL) { 199 | *size = 0; 200 | } 201 | return NULL; 202 | } 203 | 204 | fs_close(fd); 205 | 206 | if (size != NULL) { 207 | *size = fsize; 208 | } 209 | 210 | return buffer; 211 | } 212 | 213 | void *decompress_file(const char *file, int output_size) { 214 | gzFile gzfile = gzopen(file, "rb"); 215 | 216 | if(!gzfile) { 217 | dash_log(DBG_ERROR, "Error opening %s!", file); 218 | return NULL; 219 | } 220 | 221 | void *buffer = malloc(output_size); 222 | if(!buffer) { 223 | dash_log(DBG_ERROR, "Error in memalign!"); 224 | return NULL; 225 | } 226 | 227 | if(gzread(gzfile, buffer, output_size) != output_size) { 228 | dash_log(DBG_ERROR, "Error decompressing %s!", file); 229 | return NULL; 230 | } 231 | 232 | gzclose(gzfile); 233 | 234 | return buffer; 235 | } 236 | 237 | void exec(const char *path) { 238 | 239 | draw_printf(DBG_INFO, "LOADING: %s\n", path); 240 | 241 | int size = 0; 242 | char *bin = read_file(path, &size); 243 | if (bin == NULL || size < 1) { 244 | dash_log(DBG_ERROR, "EXEC: COULD NOT READ %s\n", path); 245 | return; 246 | } 247 | 248 | arch_exec(bin, size); 249 | } 250 | 251 | void exec_gz(const char *path, size_t size) { 252 | draw_printf(DBG_INFO, "LOADING: %s\n", path); 253 | 254 | char *bin = decompress_file(path, size); 255 | if (bin == NULL || size < 1) { 256 | dash_log(DBG_ERROR, "EXEC: COULD NOT READ %s\n", path); 257 | return; 258 | } 259 | 260 | arch_exec(bin, size); 261 | } 262 | 263 | void launch_retrodream(void) { 264 | if (file_exists("/sd/RD/retrodream.bin")) { 265 | exec("/sd/RD/retrodream.bin"); 266 | } else if (file_exists("/ide/RD/retrodream.bin")) { 267 | exec("/ide/RD/retrodream.bin"); 268 | } 269 | } 270 | 271 | void launch_dreamshell(void) { 272 | if (file_exists("/sd/DS/DS_CORE.BIN")) { 273 | exec("/sd/DS/DS_CORE.BIN"); 274 | } else if (file_exists("/ide/DS/DS_CORE.BIN")) { 275 | exec("/ide/DS/DS_CORE.BIN"); 276 | } 277 | } 278 | 279 | void launch_dcload_serial(void) { 280 | exec_gz("/rd/dcload-serial.bin.gz", 15776); 281 | } 282 | 283 | void launch_dcload_ip(void) { 284 | exec_gz("/rd/dcload-ip.bin.gz", 23736); 285 | } 286 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## NOTE: You shouldn't have to edit anything in this file. Check out Makefile.cfg for user config ## 3 | #################################################################################################### 4 | 5 | ###### Configuration ############################################################################### 6 | 7 | config_file=Makefile.cfg 8 | ifneq ("$(wildcard $(config_file))","") 9 | include $(config_file) 10 | else 11 | ifneq ("$(wildcard Makefile.default.cfg)","") 12 | include Makefile.default.cfg 13 | else 14 | $(error No configuration file found at $(config_file) or Makefile.default.cfg) 15 | endif 16 | endif 17 | 18 | ###### Pre-Checks ################################################################################## 19 | 20 | HAVE_MKDCDISC := $(shell command -v mkdcdisc 2> /dev/null) 21 | 22 | ###### Objects ##################################################################################### 23 | 24 | OBJS = src/main.o src/menu.o src/log.o src/utility.o src/bmfont.o src/drawing.o src/input.o 25 | 26 | ifneq ($(DISC_SUPPORT),0) 27 | OBJS += src/disc.o 28 | endif 29 | 30 | ifeq ("$(FAT_LIBRARY)","fatfs") 31 | OBJS += src/fatfs/dc.o src/fatfs/dc_bdev.o src/fatfs/ff.o \ 32 | src/fatfs/option/ccsbcs.o src/fatfs/option/syscall.o 33 | endif 34 | 35 | ###### Libraries ################################################################################### 36 | 37 | LIBS = -lpng -lz -lm 38 | 39 | ifeq ("$(FAT_LIBRARY)","kosfat") 40 | LIBS += -lkosfat 41 | endif 42 | 43 | #ifeq ("$(FAT_LIBRARY)","fatfs") 44 | # LIBS += -lfatfs 45 | #endif 46 | 47 | ###### Resources ################################################################################### 48 | 49 | RELEASE_DIR = release 50 | RESOURCE_DIR = res 51 | KOS_ROMDISK_DIR = romdisk 52 | 53 | WALLPAPER_FILE = $(WALLPAPER_SHADE)-wall-$(WALLPAPER_RES).png 54 | ROMDISK_FILES = ebdragon.fnt ebdragon.tex $(WALLPAPER_FILE) 55 | GZ_ROMDISK_FILES = dcload-ip.bin dcload-serial.bin 56 | 57 | ifneq ($(DISC_SUPPORT),0) 58 | GZ_ROMDISK_FILES += rungd.bin 59 | endif 60 | 61 | ###### Flags ####################################################################################### 62 | 63 | KOS_CFLAGS += -DWALLPAPER_FILE="$(WALLPAPER_FILE)" -DWALLPAPER_RES=$(WALLPAPER_RES) 64 | KOS_CFLAGS += -DDASH_VERSION="$(VERSION)" 65 | 66 | ifneq ($(AUTOBOOT),0) 67 | KOS_CFLAGS += -DAUTOBOOT 68 | endif 69 | 70 | ifneq ($(DISC_SUPPORT),0) 71 | KOS_CFLAGS += -DDISC_SUPPORT 72 | endif 73 | 74 | ifeq ("$(FAT_LIBRARY)","kosfat") 75 | KOS_CFLAGS += -DFAT_LIBRARY_KOSFAT 76 | endif 77 | 78 | ifeq ("$(FAT_LIBRARY)","fatfs") 79 | KOS_CFLAGS += -DFAT_LIBRARY_FATFS 80 | endif 81 | 82 | ###### Functions ################################################################################### 83 | 84 | define check_size 85 | @size=$$(wc -c < $(1)); \ 86 | if [ $$size -gt $(2) ]; then \ 87 | echo "Error: $(1) ($$size bytes) exceeds $(3) limit ($(2) bytes)!"; \ 88 | echo " In order to build a BIOS image, DreamDash must fit within that size."; \ 89 | echo " Tips: - Adjust settings in Makefile.cfg to reduce code and resources."; \ 90 | echo " - Make sure your KallistiOS and all used kos-ports are built using the "; \ 91 | echo " -Os and -flto=auto flags in your $KOS_CFLAGS build flags."; \ 92 | echo " - Use GCC 13.2.0 (KOS stable compiler profile) as it generates smaller code."; \ 93 | exit 1; \ 94 | fi 95 | endef 96 | 97 | ###### Rules ####################################################################################### 98 | 99 | default: rm-elf $(TARGET).elf 100 | 101 | include $(KOS_BASE)/Makefile.rules 102 | 103 | $(ROMDISK_FILES): 104 | @mkdir -p $(KOS_ROMDISK_DIR) 105 | cp $(RESOURCE_DIR)/$@ $(KOS_ROMDISK_DIR)/$@ 106 | 107 | $(GZ_ROMDISK_FILES): 108 | @mkdir -p $(KOS_ROMDISK_DIR) 109 | gzip -c $(RESOURCE_DIR)/$@ > $(KOS_ROMDISK_DIR)/$@.gz 110 | 111 | $(TARGET).elf: $(ROMDISK_FILES) $(GZ_ROMDISK_FILES) $(OBJS) romdisk.o 112 | kos-cc -o $(TARGET).elf $(OBJS) romdisk.o $(LIBS) 113 | 114 | all: $(TARGET).elf $(TARGET).bin 1ST_READ.BIN $(TARGET).cdi bios-all 115 | 116 | release: all rm-elf 117 | 118 | clean: rm-elf 119 | -rm -f $(OBJS) 120 | -rm -rf $(RELEASE_DIR) 121 | -rm -rf $(KOS_ROMDISK_DIR) 122 | 123 | rm-elf: 124 | -rm -f $(TARGET).elf romdisk.* 125 | 126 | run: $(TARGET).elf 127 | $(KOS_LOADER) $(TARGET).elf 128 | 129 | release-dir: 130 | mkdir -p $(RELEASE_DIR) 131 | 132 | $(TARGET).bin: release-dir $(TARGET).elf 133 | $(KOS_OBJCOPY) -R .stack -O binary $(TARGET).elf release/$(TARGET).bin 134 | 135 | 1ST_READ.BIN: release-dir $(TARGET).bin 136 | $(KOS_BASE)/utils/scramble/scramble release/$(TARGET).bin release/1ST_READ.BIN 137 | 138 | $(TARGET).cdi: release-dir $(TARGET).elf 139 | ifneq ($(HAVE_MKDCDISC),) 140 | mkdcdisc --author $(TARGET) -e $(TARGET).elf --no-mr -n $(TARGET)-$(VERSION) -r 20250125 -o release/$(TARGET).cdi 141 | else 142 | $(info mkdcdisc utility not found in PATH. Skipping CDI generation.) 143 | endif 144 | 145 | bios: $(TARGET).bios 146 | $(TARGET).bios: release-dir $(TARGET).bin 147 | $(call check_size,release/$(TARGET).bin,517120,505KB) 148 | cp -f res/boot_loader_retail.bios release/$(TARGET).bios 149 | dd if=release/$(TARGET).bin of=release/$(TARGET).bios bs=1024 seek=64 conv=notrunc 150 | 151 | bios-nogdrom: $(TARGET)-nogdrom.bios 152 | $(TARGET)-nogdrom.bios: release-dir $(TARGET).bin 153 | $(call check_size,release/$(TARGET).bin,517120,505KB) 154 | cp -f res/boot_loader_retail_nogdrom.bios release/$(TARGET)-nogdrom.bios 155 | dd if=release/$(TARGET).bin of=release/$(TARGET)-nogdrom.bios bs=1024 seek=64 conv=notrunc 156 | 157 | bios-devkit: $(TARGET)-devkit.bios 158 | $(TARGET)-devkit.bios: release-dir $(TARGET).bin 159 | $(call check_size,release/$(TARGET).bin,517120,505KB) 160 | cp -f res/boot_loader_devkit.bios release/$(TARGET)-devkit.bios 161 | dd if=release/$(TARGET).bin of=release/$(TARGET)-devkit.bios bs=1024 seek=64 conv=notrunc 162 | 163 | bios-devkit-nogdrom: $(TARGET)-devkit-nogdrom.bios 164 | $(TARGET)-devkit-nogdrom.bios: release-dir $(TARGET).bin 165 | $(call check_size,release/$(TARGET).bin,517120,505KB) 166 | cp -f res/boot_loader_devkit_nogdrom.bios release/$(TARGET)-devkit-nogdrom.bios 167 | dd if=release/$(TARGET).bin of=release/$(TARGET)-devkit-nogdrom.bios bs=1024 seek=64 conv=notrunc 168 | 169 | bios-32mb: $(TARGET)-32mb.bios 170 | $(TARGET)-32mb.bios: release-dir $(TARGET).bin 171 | $(call check_size,release/$(TARGET).bin,517120,505KB) 172 | cp -f res/boot_loader_retail_32mb.bios release/$(TARGET)-32mb.bios 173 | dd if=release/$(TARGET).bin of=release/$(TARGET)-32mb.bios bs=1024 seek=64 conv=notrunc 174 | 175 | bios-nogdrom-32mb: $(TARGET)-nogdrom-32mb.bios 176 | $(TARGET)-nogdrom-32mb.bios: release-dir $(TARGET).bin 177 | $(call check_size,release/$(TARGET).bin,517120,505KB) 178 | cp -f res/boot_loader_retail_nogdrom_32mb.bios release/$(TARGET)-nogdrom-32mb.bios 179 | dd if=release/$(TARGET).bin of=release/$(TARGET)-nogdrom-32mb.bios bs=1024 seek=64 conv=notrunc 180 | 181 | bios-devkit-32mb: $(TARGET)-devkit-32mb.bios 182 | $(TARGET)-devkit-32mb.bios: release-dir $(TARGET).bin 183 | $(call check_size,release/$(TARGET).bin,517120,505KB) 184 | cp -f res/boot_loader_devkit_32mb.bios release/$(TARGET)-devkit-32mb.bios 185 | dd if=release/$(TARGET).bin of=release/$(TARGET)-devkit-32mb.bios bs=1024 seek=64 conv=notrunc 186 | 187 | bios-devkit-nogdrom-32mb: $(TARGET)-devkit-nogdrom-32mb.bios 188 | $(TARGET)-devkit-nogdrom-32mb.bios: release-dir $(TARGET).bin 189 | $(call check_size,release/$(TARGET).bin,517120,505KB) 190 | cp -f res/boot_loader_devkit_nogdrom_32mb.bios release/$(TARGET)-devkit-nogdrom-32mb.bios 191 | dd if=release/$(TARGET).bin of=release/$(TARGET)-devkit-nogdrom-32mb.bios bs=1024 seek=64 conv=notrunc 192 | 193 | bios-all: bios bios-nogdrom bios-devkit bios-devkit-nogdrom bios-32mb bios-nogdrom-32mb bios-devkit-32mb bios-devkit-nogdrom-32mb 194 | 195 | -------------------------------------------------------------------------------- /src/menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "disc.h" 7 | #include "drawing.h" 8 | #include "input.h" 9 | #include "log.h" 10 | #include "menu.h" 11 | #include "utility.h" 12 | #include "utlist.h" 13 | 14 | static int menu_id = MENU_MAIN; 15 | static int line_height = 0; 16 | static int line_max = 0; 17 | static int list_index = 0; 18 | static int highlight_index = 0; 19 | static Rect menuRect; 20 | static Rect pathRect; 21 | static Rect filerRect; 22 | static List menuList = {NULL, 0, "BOOT MENU"}; 23 | static List fileList; 24 | extern List logList; 25 | 26 | static void menu_main_add_item(const char *name, int menu_id) { 27 | ListItem *item = (ListItem *) malloc(sizeof *item); 28 | memset(item, 0, sizeof(ListItem)); 29 | item->type = menu_id; 30 | strcpy(item->name, name); 31 | DL_APPEND(menuList.head, item); 32 | menuList.size++; 33 | } 34 | 35 | static void menu_init() { 36 | 37 | Vec2 screenSize = draw_get_screen_size(); 38 | 39 | menuRect = (Rect) {32, 32, screenSize.x - 64, screenSize.y - 64}; 40 | 41 | pathRect = (Rect) {menuRect.left + 8, menuRect.top + 8, 42 | menuRect.width - 16, DRAW_FONT_HEIGHT + 16}; 43 | 44 | filerRect = (Rect) {menuRect.left + 8, pathRect.top + pathRect.height + 10, 45 | menuRect.width - 16, menuRect.height - pathRect.height - 26}; 46 | 47 | line_height = DRAW_FONT_HEIGHT + DRAW_FONT_LINE_SPACING; 48 | line_max = (int) filerRect.height / line_height; 49 | if (line_max * line_height < (int) filerRect.height) { 50 | line_height = (int) filerRect.height / line_max; 51 | } 52 | 53 | // build main menu 54 | if (file_exists("/sd/RD/retrodream.bin") || file_exists("/ide/RD/retrodream.bin")) { 55 | menu_main_add_item("RetroDream", MENU_RETRODREAM); 56 | } 57 | if (file_exists("/sd/DS/DS_CORE.BIN") || file_exists("/ide/DS/DS_CORE.BIN")) { 58 | menu_main_add_item("DreamShell", MENU_DREAMSHELL); 59 | } 60 | menu_main_add_item("File Browser", MENU_FILER); 61 | menu_main_add_item("dcload-ip", MENU_DCLOAD_IP); 62 | menu_main_add_item("dcload-serial", MENU_DCLOAD_SERIAL); 63 | #ifdef DISC_SUPPORT 64 | menu_main_add_item("Play Disc", MENU_DISC); 65 | #endif 66 | menu_main_add_item("View Logs", MENU_LOGS); 67 | } 68 | 69 | static void menu_get_dir(char *path) { 70 | 71 | list_index = 0; 72 | highlight_index = 0; 73 | 74 | free_dir(&fileList); 75 | get_dir(&fileList, path); 76 | } 77 | 78 | static List *menu_get_list() { 79 | 80 | List *list = &menuList; 81 | if (menu_id == MENU_FILER) { 82 | list = &fileList; 83 | } else if (menu_id == MENU_LOGS) { 84 | list = &logList; 85 | } 86 | return list; 87 | } 88 | 89 | static void menu_draw_main() { 90 | 91 | List *list = &menuList; 92 | 93 | Rect mainRect = (Rect) {menuRect.left + 30, menuRect.top + 30, 94 | menuRect.width - 380, (DRAW_FONT_HEIGHT + DRAW_FONT_LINE_SPACING + 2) * (float) list->size}; 95 | 96 | draw_box_outline(mainRect.left, mainRect.top, mainRect.width, mainRect.height, 97 | 100, COL_BLACK_TRANS1, COL_BLACK_TRANS2, 4); 98 | 99 | for (int i = 0; i < (unsigned int) line_max; i++) { 100 | 101 | if (list_index + i < list->size) { 102 | 103 | if (i == highlight_index) { 104 | draw_box_outline(mainRect.left, mainRect.top + ((float) (i * line_height)), 105 | mainRect.width, (float) line_height, 106 | 102, COL_TRUE_BLUE, COL_WHITE, 2); 107 | } 108 | 109 | ListItem *item = get_item(list, list_index + i); 110 | if (item != NULL) { 111 | draw_string(mainRect.left + 4, 112 | mainRect.top + (DRAW_FONT_LINE_SPACING / 2) + ((float) (i * line_height)) + 2, 113 | 103, COL_WHITE, item->name); 114 | } 115 | } 116 | } 117 | 118 | // version 119 | draw_string(16, draw_get_screen_size().y - DRAW_FONT_HEIGHT - 16, 120 | 103, COL_TRUE_BLUE, "DreamDash BIOS v"DASH_VERSION); 121 | } 122 | 123 | static void menu_draw() { 124 | 125 | if (menu_id == MENU_MAIN) { 126 | menu_draw_main(); 127 | return; 128 | } 129 | 130 | List *list = menu_get_list(); 131 | 132 | draw_box_outline(pathRect.left, pathRect.top, pathRect.width, pathRect.height, 133 | 100, COL_BLACK_TRANS1, COL_BLACK_TRANS2, 4); 134 | draw_string(pathRect.left + 4, pathRect.top + (DRAW_FONT_LINE_SPACING / 2) + 6, 103, COL_TRUE_BLUE, list->path); 135 | 136 | draw_box_outline(filerRect.left, filerRect.top, filerRect.width, filerRect.height, 137 | 100, COL_BLACK_TRANS1, COL_BLACK_TRANS2, 4); 138 | 139 | for (int i = 0; i < (unsigned int) line_max; i++) { 140 | 141 | if (list_index + i < list->size) { 142 | 143 | if (menu_id == MENU_FILER && i == highlight_index) { 144 | draw_box_outline(filerRect.left, filerRect.top + ((float) (i * line_height)), 145 | filerRect.width, (float) line_height, 146 | 102, COL_TRUE_BLUE, COL_WHITE, 2); 147 | } 148 | 149 | ListItem *item = get_item(list, list_index + i); 150 | if (item != NULL) { 151 | Color color = COL_WHITE; 152 | if ((menu_id == MENU_FILER && item->type == TYPE_DIR) 153 | || (menu_id == MENU_LOGS && item->type == TYPE_BIN)) { 154 | color = COL_YELLOW; 155 | } else if (menu_id == MENU_LOGS && item->type == TYPE_DIR) { 156 | color = COL_RED; 157 | } 158 | draw_string(filerRect.left + 4, 159 | filerRect.top + (DRAW_FONT_LINE_SPACING / 2) + ((float) (i * line_height)), 160 | 103, color, item->name); 161 | } 162 | } 163 | } 164 | } 165 | 166 | static int menu_input() { 167 | 168 | uint32_t input = get_input(); 169 | List *list = menu_get_list(); 170 | 171 | if (input & INPUT_QUIT) { 172 | return INPUT_QUIT; 173 | } 174 | 175 | if (input & CONT_DPAD_UP) { 176 | if (highlight_index <= line_max / 2 && list_index > 0) { 177 | list_index--; 178 | } else { 179 | highlight_index--; 180 | if (highlight_index < 0) { 181 | highlight_index = line_max / 2; 182 | if (highlight_index >= list->size) { 183 | highlight_index = list->size - 1; 184 | list_index = 0; 185 | } else { 186 | list_index = (list->size - 1) - highlight_index; 187 | } 188 | } 189 | } 190 | } else if (input & CONT_DPAD_DOWN) { 191 | if (highlight_index >= line_max / 2) { 192 | list_index++; 193 | if (list_index + highlight_index >= list->size) { 194 | list_index = 0; 195 | highlight_index = 0; 196 | } 197 | } else { 198 | highlight_index++; 199 | if (highlight_index >= list->size) { 200 | highlight_index = 0; 201 | } 202 | } 203 | } 204 | 205 | if (menu_id == MENU_MAIN) { 206 | if (input & CONT_A) { 207 | ListItem *item = get_item(list, list_index + highlight_index); 208 | if (item != NULL) { 209 | if (item->type == MENU_FILER) { 210 | menu_id = MENU_FILER; 211 | menu_get_dir("/"); 212 | } else if (item->type == MENU_LOGS) { 213 | list_index = 0; 214 | highlight_index = 0; 215 | menu_id = MENU_LOGS; 216 | #ifdef DISC_SUPPORT 217 | } else if (item->type == MENU_DISC) { 218 | if (ip_info != NULL) { 219 | disc_launch(); 220 | } 221 | #endif 222 | } else if (item->type == MENU_RETRODREAM) { 223 | launch_retrodream(); 224 | } else if (item->type == MENU_DREAMSHELL) { 225 | launch_dreamshell(); 226 | } else if (item->type == MENU_DCLOAD_IP) { 227 | launch_dcload_ip(); 228 | } else if (item->type == MENU_DCLOAD_SERIAL) { 229 | launch_dcload_serial(); 230 | } 231 | } 232 | } 233 | } else if (menu_id == MENU_LOGS) { 234 | if (input & CONT_B) { 235 | list_index = 0; 236 | highlight_index = 0; 237 | menu_id = MENU_MAIN; 238 | } 239 | } else if (menu_id == MENU_FILER) { 240 | if (input & CONT_A) { 241 | ListItem *file = get_item(list, list_index + highlight_index); 242 | if (file != NULL) { 243 | if (file->type == TYPE_DIR) { 244 | menu_get_dir(file->path); 245 | } else if (file->type == TYPE_BIN) { 246 | exec(file->path); 247 | } 248 | } 249 | } else if (input & CONT_B) { 250 | if (strlen(list->path) > 1) { 251 | char *pos = strrchr(list->path, '/'); 252 | if (pos != NULL) { 253 | if (strlen(pos) == 1) { 254 | menu_get_dir("/"); 255 | } else { 256 | char prev[MAX_PATH]; 257 | memset(prev, 0, MAX_PATH); 258 | strncpy(prev, list->path, strlen(list->path) - strlen(pos) + 1); 259 | menu_get_dir(prev); 260 | } 261 | } 262 | } else { 263 | list_index = 0; 264 | highlight_index = 0; 265 | menu_id = MENU_MAIN; 266 | } 267 | } 268 | } 269 | 270 | return 0; 271 | } 272 | 273 | void menu_run() { 274 | 275 | uint32_t input = 0; 276 | 277 | menu_init(); 278 | 279 | while (1) { 280 | 281 | input = menu_input(); 282 | if (input & INPUT_QUIT) { 283 | break; 284 | } 285 | 286 | draw_start(); 287 | 288 | menu_draw(); 289 | 290 | draw_back(); 291 | 292 | draw_end(); 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /res/ebdragon.fnt: -------------------------------------------------------------------------------- 1 | info face="Eight Bit Dragon" size=24 bold=0 italic=0 charset="unicode" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 2 | common lineHeight=24 base=22 scaleW=256 scaleH=256 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0 3 | page id=0 file="ebdragon_0.png" 4 | chars count=101 5 | char id=32 x=8 y=23 width=3 height=1 xoffset=-1 yoffset=23 xadvance=7 page=0 chnl=15 6 | char id=33 x=89 y=69 width=5 height=22 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=15 7 | char id=34 x=225 y=87 width=13 height=10 xoffset=0 yoffset=0 xadvance=15 page=0 chnl=15 8 | char id=35 x=26 y=0 width=17 height=22 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 9 | char id=36 x=160 y=0 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 10 | char id=37 x=175 y=0 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 11 | char id=38 x=190 y=0 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 12 | char id=39 x=245 y=87 width=5 height=10 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=15 13 | char id=40 x=26 y=69 width=10 height=22 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=15 14 | char id=41 x=37 y=69 width=10 height=22 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=15 15 | char id=42 x=197 y=89 width=12 height=11 xoffset=0 yoffset=0 xadvance=14 page=0 chnl=15 16 | char id=43 x=182 y=89 width=14 height=12 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 17 | char id=44 x=239 y=87 width=5 height=10 xoffset=0 yoffset=14 xadvance=7 page=0 chnl=15 18 | char id=45 x=44 y=110 width=14 height=2 xoffset=0 yoffset=10 xadvance=16 page=0 chnl=15 19 | char id=46 x=38 y=110 width=5 height=5 xoffset=0 yoffset=17 xadvance=7 page=0 chnl=15 20 | char id=47 x=15 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 21 | char id=48 x=30 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 22 | char id=49 x=45 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 23 | char id=50 x=60 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 24 | char id=51 x=75 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 25 | char id=52 x=90 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 26 | char id=53 x=105 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 27 | char id=54 x=120 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 28 | char id=55 x=135 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 29 | char id=56 x=90 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 30 | char id=57 x=165 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 31 | char id=58 x=150 y=89 width=5 height=17 xoffset=0 yoffset=5 xadvance=7 page=0 chnl=15 32 | char id=59 x=197 y=69 width=5 height=19 xoffset=0 yoffset=5 xadvance=7 page=0 chnl=15 33 | char id=60 x=169 y=89 width=12 height=16 xoffset=0 yoffset=3 xadvance=14 page=0 chnl=15 34 | char id=61 x=0 y=112 width=14 height=7 xoffset=0 yoffset=8 xadvance=16 page=0 chnl=15 35 | char id=62 x=156 y=89 width=12 height=16 xoffset=0 yoffset=3 xadvance=14 page=0 chnl=15 36 | char id=63 x=225 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 37 | char id=64 x=44 y=0 width=17 height=22 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 38 | char id=65 x=240 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 39 | char id=66 x=0 y=48 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 40 | char id=67 x=15 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 41 | char id=68 x=30 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 42 | char id=69 x=112 y=0 width=15 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 43 | char id=70 x=96 y=0 width=15 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 44 | char id=71 x=45 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 45 | char id=72 x=60 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 46 | char id=73 x=95 y=69 width=5 height=22 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=15 47 | char id=74 x=75 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 48 | char id=75 x=195 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 49 | char id=76 x=144 y=0 width=15 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 50 | char id=77 x=62 y=0 width=17 height=22 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 51 | char id=78 x=135 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 52 | char id=79 x=150 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 53 | char id=80 x=105 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 54 | char id=81 x=80 y=0 width=15 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 55 | char id=82 x=235 y=0 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 56 | char id=83 x=210 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 57 | char id=84 x=195 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 58 | char id=85 x=180 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 59 | char id=86 x=128 y=0 width=15 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 60 | char id=87 x=8 y=0 width=17 height=22 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15 61 | char id=88 x=150 y=23 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 62 | char id=89 x=205 y=0 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 63 | char id=90 x=0 y=25 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 64 | char id=91 x=67 y=69 width=7 height=22 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 65 | char id=92 x=220 y=0 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 66 | char id=93 x=58 y=69 width=8 height=22 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 67 | char id=94 x=210 y=87 width=14 height=10 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 68 | char id=95 x=74 y=110 width=14 height=2 xoffset=0 yoffset=20 xadvance=16 page=0 chnl=15 69 | char id=96 x=30 y=110 width=7 height=5 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 70 | char id=97 x=30 y=92 width=14 height=17 xoffset=0 yoffset=5 xadvance=17 page=0 chnl=15 71 | char id=98 x=120 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 72 | char id=99 x=60 y=92 width=14 height=17 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 73 | char id=100 x=165 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=17 page=0 chnl=15 74 | char id=101 x=90 y=92 width=14 height=17 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 75 | char id=102 x=152 y=69 width=14 height=19 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 76 | char id=103 x=167 y=69 width=14 height=19 xoffset=0 yoffset=5 xadvance=17 page=0 chnl=15 77 | char id=104 x=180 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 78 | char id=105 x=101 y=69 width=5 height=22 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=15 79 | char id=106 x=0 y=0 width=7 height=24 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 80 | char id=107 x=210 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 81 | char id=108 x=83 y=69 width=5 height=22 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=15 82 | char id=109 x=221 y=69 width=17 height=17 xoffset=0 yoffset=5 xadvance=19 page=0 chnl=15 83 | char id=110 x=105 y=92 width=14 height=17 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 84 | char id=111 x=120 y=89 width=14 height=17 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 85 | char id=112 x=122 y=69 width=14 height=19 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 86 | char id=113 x=137 y=69 width=14 height=19 xoffset=0 yoffset=5 xadvance=17 page=0 chnl=15 87 | char id=114 x=0 y=94 width=14 height=17 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 88 | char id=115 x=15 y=92 width=14 height=17 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 89 | char id=116 x=107 y=69 width=14 height=19 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 90 | char id=117 x=45 y=92 width=14 height=17 xoffset=0 yoffset=5 xadvance=17 page=0 chnl=15 91 | char id=118 x=239 y=69 width=15 height=17 xoffset=0 yoffset=5 xadvance=17 page=0 chnl=15 92 | char id=119 x=203 y=69 width=17 height=17 xoffset=0 yoffset=5 xadvance=19 page=0 chnl=15 93 | char id=120 x=135 y=89 width=14 height=17 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 94 | char id=121 x=182 y=69 width=14 height=19 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 95 | char id=122 x=75 y=92 width=14 height=17 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 96 | char id=123 x=15 y=69 width=10 height=22 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=15 97 | char id=124 x=250 y=0 width=5 height=22 xoffset=0 yoffset=0 xadvance=7 page=0 chnl=15 98 | char id=125 x=48 y=69 width=9 height=22 xoffset=0 yoffset=0 xadvance=12 page=0 chnl=15 99 | char id=126 x=15 y=110 width=14 height=7 xoffset=0 yoffset=8 xadvance=16 page=0 chnl=15 100 | char id=160 x=251 y=87 width=3 height=1 xoffset=-1 yoffset=23 xadvance=7 page=0 chnl=15 101 | char id=163 x=240 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 102 | char id=165 x=0 y=71 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 103 | char id=173 x=59 y=110 width=14 height=2 xoffset=0 yoffset=10 xadvance=16 page=0 chnl=15 104 | char id=180 x=75 y=69 width=7 height=22 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15 105 | char id=247 x=225 y=46 width=14 height=22 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15 106 | -------------------------------------------------------------------------------- /src/fatfs/dc_bdev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FatFs for the Sega Dreamcast 3 | * 4 | * This file is part of the FatFs module, a generic FAT filesystem 5 | * module for small embedded systems. This version has been ported and 6 | * optimized specifically for the Sega Dreamcast platform. 7 | * 8 | * Copyright (c) 2007-2025 Ruslan Rostovtsev 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a 11 | * copy of this software and associated documentation files (the "Software"), 12 | * to deal in the Software without restriction, including without limitation 13 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | * and/or sell copies of the Software, and to permit persons to whom the 15 | * Software is furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included 18 | * in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #include "fatfs.h" 43 | #include "../log.h" 44 | 45 | #define MAX_PARTITIONS 4 46 | 47 | static kos_blockdev_t *sd_dev = NULL; 48 | static kos_blockdev_t *g1_dev = NULL; 49 | static kos_blockdev_t *g1_dev_dma = NULL; 50 | 51 | static int is_fat_partition(uint8_t partition_type) { 52 | switch(partition_type) { 53 | case 0x04: /* 32MB */ 54 | case 0x06: /* Over 32 to 2GB */ 55 | return 16; 56 | case 0x0B: 57 | case 0x0C: 58 | return 32; 59 | default: 60 | return 0; 61 | } 62 | } 63 | 64 | static int check_partition(uint8_t *buf, int partition) { 65 | int pval; 66 | 67 | if (buf[0x01FE] != 0x55 || buf[0x1FF] != 0xAA) { 68 | // dash_log(DBG_DEBUG, "Device doesn't appear to have a MBR\n"); 69 | return -1; 70 | } 71 | 72 | pval = 16 * partition + 0x01BE; 73 | 74 | if (buf[pval + 4] == 0) { 75 | // dash_log(DBG_DEBUG, "Partition empty: 0x%02x\n", buf[pval + 4]); 76 | return -1; 77 | } 78 | 79 | return 0; 80 | } 81 | 82 | static bool mount_sd_card(uint8_t *mbr_buf) { 83 | uint8_t partition_type; 84 | int part = 0, fat_part = 0; 85 | char path[8]; 86 | kos_blockdev_t *dev; 87 | const char *prefix = "/sd"; 88 | 89 | if (sd_dev == NULL) { 90 | sd_dev = malloc(sizeof(kos_blockdev_t) * MAX_PARTITIONS); 91 | 92 | if (sd_dev == NULL) { 93 | dash_log(DBG_ERROR, "FATFS: Can't allocate memory for SD card partitions\n"); 94 | return false; 95 | } 96 | 97 | memset(sd_dev, 0, sizeof(kos_blockdev_t) * MAX_PARTITIONS); 98 | } 99 | 100 | for (part = 0; part < MAX_PARTITIONS; part++) { 101 | dev = &sd_dev[part]; 102 | 103 | if (check_partition(mbr_buf, part)) { 104 | continue; 105 | } 106 | if (sd_blockdev_for_partition(part, dev, &partition_type)) { 107 | continue; 108 | } 109 | 110 | if (part == 0) { 111 | strcpy(path, prefix); 112 | } 113 | else { 114 | sprintf(path, "%s%d", prefix, part); 115 | } 116 | 117 | /* Check to see if the MBR says that we have a FAT partition. */ 118 | fat_part = is_fat_partition(partition_type); 119 | 120 | if (fat_part) { 121 | 122 | dash_log(DBG_INFO, "FATFS: Detected FAT%d on partition %d\n", fat_part, part); 123 | 124 | if (fs_fat_init()) { 125 | dash_log(DBG_INFO, "FATFS: Could not initialize fatfs!\n"); 126 | dev->shutdown(dev); 127 | } 128 | else { 129 | /* Need full disk block device for FAT */ 130 | dev->shutdown(dev); 131 | 132 | if (sd_blockdev_for_device(dev)) { 133 | continue; 134 | } 135 | 136 | dash_log(DBG_INFO, "FATFS: Mounting filesystem to %s...\n", path); 137 | 138 | if (fs_fat_mount(path, dev, NULL, part)) { 139 | dash_log(DBG_INFO, "FATFS: Could not mount device as fatfs.\n"); 140 | dev->shutdown(dev); 141 | } 142 | else { 143 | return true; 144 | } 145 | } 146 | } 147 | else { 148 | dash_log(DBG_INFO, "FATFS: Unknown filesystem: 0x%02x\n", partition_type); 149 | dev->shutdown(dev); 150 | } 151 | } 152 | 153 | return false; 154 | } 155 | 156 | int fs_fat_mount_sd() { 157 | uint8_t mbr_buffer[512]; 158 | sd_init_params_t params; 159 | 160 | dash_log(DBG_INFO, "FATFS: Checking for SD cards...\n"); 161 | 162 | /* Try SCIF interface first */ 163 | params.interface = SD_IF_SCIF; 164 | #ifdef FATFS_SD_CHECK_CRC 165 | params.check_crc = true; 166 | #else 167 | params.check_crc = false; 168 | #endif 169 | 170 | memset(mbr_buffer, 0, sizeof(mbr_buffer)); 171 | 172 | if (sd_init_ex(¶ms) == 0) { 173 | dash_log(DBG_INFO, "FATFS: SD card found on SCIF-SPI: %"PRIu32" MB\n", 174 | (uint32)(sd_get_size() / 1024 / 1024)); 175 | 176 | if (sd_read_blocks(0, 1, mbr_buffer) == 0) { 177 | if (mount_sd_card(mbr_buffer)) { 178 | return 0; 179 | } 180 | } 181 | else { 182 | dash_log(DBG_ERROR, "FATFS: Can't read MBR from SCIF-SPI SD card\n"); 183 | } 184 | } 185 | 186 | /* Get back dbglog if they are used */ 187 | scif_init(); 188 | 189 | /* If no card found on SCIF, try SCI interface */ 190 | params.interface = SD_IF_SCI; 191 | 192 | if (sd_init_ex(¶ms) == 0) { 193 | dash_log(DBG_INFO, "FATFS: SD card found on SCI-SPI: %"PRIu32" MB\n", 194 | (uint32)(sd_get_size() / 1024 / 1024)); 195 | 196 | if (sd_read_blocks(0, 1, mbr_buffer) == 0) { 197 | if (mount_sd_card(mbr_buffer)) { 198 | return 0; 199 | } 200 | } 201 | else { 202 | dash_log(DBG_ERROR, "FATFS: Can't read MBR from SCI-SPI SD card\n"); 203 | } 204 | } 205 | /* No cards found on any interface */ 206 | return -1; 207 | } 208 | 209 | int fs_fat_mount_ide() { 210 | 211 | uint8_t partition_type; 212 | int part = 0, fat_part = 0; 213 | char path[8]; 214 | uint8_t buf[512]; 215 | kos_blockdev_t *dev; 216 | kos_blockdev_t *dev_dma; 217 | 218 | dash_log(DBG_INFO, "FATFS: Checking for G1 ATA devices...\n"); 219 | 220 | if (g1_ata_init()) { 221 | return -1; 222 | } 223 | 224 | /* Read the MBR from the disk */ 225 | if (g1_ata_lba_mode()) { 226 | if (g1_ata_read_lba(0, 1, (uint16_t *)buf) < 0) { 227 | dash_log(DBG_ERROR, "FATFS: Can't read MBR from IDE by LBA\n"); 228 | return -1; 229 | } 230 | } 231 | else { 232 | if (g1_ata_read_chs(0, 0, 1, 1, (uint16_t *)buf) < 0) { 233 | dash_log(DBG_ERROR, "FATFS: Can't read MBR from IDE by CHS\n"); 234 | return -1; 235 | } 236 | } 237 | 238 | if (g1_dev == NULL) { 239 | g1_dev = malloc(sizeof(kos_blockdev_t) * MAX_PARTITIONS); 240 | g1_dev_dma = malloc(sizeof(kos_blockdev_t) * MAX_PARTITIONS); 241 | } 242 | if (!g1_dev || !g1_dev_dma) { 243 | dash_log(DBG_ERROR, "FATFS: Can't allocate memory for IDE partitions\n"); 244 | return -1; 245 | } 246 | 247 | memset(&g1_dev[0], 0, sizeof(kos_blockdev_t) * MAX_PARTITIONS); 248 | memset(&g1_dev_dma[0], 0, sizeof(kos_blockdev_t) * MAX_PARTITIONS); 249 | 250 | for (part = 0; part < MAX_PARTITIONS; part++) { 251 | 252 | dev = &g1_dev[part]; 253 | dev_dma = &g1_dev_dma[part]; 254 | 255 | if (check_partition(buf, part)) { 256 | continue; 257 | } 258 | if (g1_ata_blockdev_for_partition(part, 0, dev, &partition_type)) { 259 | continue; 260 | } 261 | 262 | if (!part) { 263 | strcpy(path, "/ide"); 264 | path[4] = '\0'; 265 | } 266 | else { 267 | sprintf(path, "/ide%d", part); 268 | path[strlen(path)] = '\0'; 269 | } 270 | 271 | /* Check to see if the MBR says that we have a FAT partition. */ 272 | fat_part = is_fat_partition(partition_type); 273 | 274 | if (fat_part) { 275 | 276 | dash_log(DBG_INFO, "FATFS: Detected FAT%d on partition %d\n", fat_part, part); 277 | 278 | if (fs_fat_init()) { 279 | dash_log(DBG_INFO, "FATFS: Could not initialize fs_fat!\n"); 280 | dev->shutdown(dev); 281 | } 282 | else { 283 | /* Need full disk block device for FAT */ 284 | dev->shutdown(dev); 285 | 286 | if (g1_ata_blockdev_for_device(0, dev)) { 287 | continue; 288 | } 289 | 290 | if (g1_ata_blockdev_for_device(1, dev_dma)) { 291 | dev_dma = NULL; 292 | } 293 | 294 | dash_log(DBG_INFO, "FATFS: Mounting filesystem to %s...\n", path); 295 | 296 | if (fs_fat_mount(path, dev, dev_dma, part)) { 297 | dash_log(DBG_INFO, "FATFS: Could not mount device as fatfs.\n"); 298 | dev->shutdown(dev); 299 | if (dev_dma) { 300 | dev_dma->shutdown(dev_dma); 301 | } 302 | } 303 | } 304 | } 305 | else { 306 | dash_log(DBG_INFO, "FATFS: Unknown filesystem: 0x%02x\n", partition_type); 307 | dev->shutdown(dev); 308 | } 309 | } 310 | return 0; 311 | } 312 | 313 | /* Unmount and cleanup SD devices */ 314 | void fs_fat_unmount_sd(void) { 315 | if (sd_dev != NULL) { 316 | for (int i = 0; i < MAX_PARTITIONS; i++) { 317 | if (sd_dev[i].dev_data != NULL) { 318 | char path[16]; 319 | if (i == 0) { 320 | strcpy(path, "/sd"); 321 | } 322 | else { 323 | sprintf(path, "/sd%d", i); 324 | } 325 | fs_fat_unmount(path); 326 | sd_dev[i].shutdown(&sd_dev[i]); 327 | } 328 | } 329 | free(sd_dev); 330 | sd_dev = NULL; 331 | } 332 | } 333 | 334 | /* Unmount and cleanup IDE devices */ 335 | void fs_fat_unmount_ide(void) { 336 | if (g1_dev != NULL) { 337 | for (int i = 0; i < MAX_PARTITIONS; i++) { 338 | if (g1_dev[i].dev_data != NULL) { 339 | char path[16]; 340 | if (i == 0) { 341 | strcpy(path, "/ide"); 342 | } 343 | else { 344 | sprintf(path, "/ide%d", i); 345 | } 346 | fs_fat_unmount(path); 347 | g1_dev[i].shutdown(&g1_dev[i]); 348 | } 349 | } 350 | free(g1_dev); 351 | g1_dev = NULL; 352 | } 353 | 354 | if (g1_dev_dma != NULL) { 355 | for (int i = 0; i < MAX_PARTITIONS; i++) { 356 | if (g1_dev_dma[i].dev_data != NULL) { 357 | g1_dev_dma[i].shutdown(&g1_dev_dma[i]); 358 | } 359 | } 360 | free(g1_dev_dma); 361 | g1_dev_dma = NULL; 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/fatfs/ffconf.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / FatFs - FAT file system module configuration file R0.11a (C)ChaN, 2015 3 | /---------------------------------------------------------------------------*/ 4 | 5 | #define _FFCONF 64180 /* Revision ID */ 6 | 7 | /*---------------------------------------------------------------------------/ 8 | / Function Configurations 9 | /---------------------------------------------------------------------------*/ 10 | 11 | #define _FS_READONLY 0 12 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 13 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 14 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 15 | / and optional writing functions as well. */ 16 | 17 | 18 | #define _FS_MINIMIZE 0 19 | /* This option defines minimization level to remove some basic API functions. 20 | / 21 | / 0: All basic functions are enabled. 22 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(), 23 | / f_truncate() and f_rename() function are removed. 24 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 25 | / 3: f_lseek() function is removed in addition to 2. */ 26 | 27 | 28 | #define _USE_STRFUNC 0 29 | /* This option switches string functions, f_gets(), f_putc(), f_puts() and 30 | / f_printf(). 31 | / 32 | / 0: Disable string functions. 33 | / 1: Enable without LF-CRLF conversion. 34 | / 2: Enable with LF-CRLF conversion. */ 35 | 36 | 37 | #define _USE_FIND 0 38 | /* This option switches filtered directory read feature and related functions, 39 | / f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */ 40 | 41 | 42 | #define _USE_MKFS 1 43 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 44 | 45 | 46 | #define _USE_FASTSEEK 1 47 | /* This option switches fast seek feature. (0:Disable or 1:Enable) */ 48 | 49 | 50 | #define _USE_LABEL 1 51 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 52 | / (0:Disable or 1:Enable) */ 53 | 54 | 55 | #define _USE_FORWARD 0 56 | /* This option switches f_forward() function. (0:Disable or 1:Enable) 57 | / To enable it, also _FS_TINY need to be set to 1. */ 58 | 59 | 60 | /*---------------------------------------------------------------------------/ 61 | / Locale and Namespace Configurations 62 | /---------------------------------------------------------------------------*/ 63 | 64 | #define _CODE_PAGE 866 65 | /* This option specifies the OEM code page to be used on the target system. 66 | / Incorrect setting of the code page can cause a file open failure. 67 | / 68 | / 1 - ASCII (No extended character. Non-LFN cfg. only) 69 | / 437 - U.S. 70 | / 720 - Arabic 71 | / 737 - Greek 72 | / 771 - KBL 73 | / 775 - Baltic 74 | / 850 - Latin 1 75 | / 852 - Latin 2 76 | / 855 - Cyrillic 77 | / 857 - Turkish 78 | / 860 - Portuguese 79 | / 861 - Icelandic 80 | / 862 - Hebrew 81 | / 863 - Canadian French 82 | / 864 - Arabic 83 | / 865 - Nordic 84 | / 866 - Russian 85 | / 869 - Greek 2 86 | / 932 - Japanese (DBCS) 87 | / 936 - Simplified Chinese (DBCS) 88 | / 949 - Korean (DBCS) 89 | / 950 - Traditional Chinese (DBCS) 90 | */ 91 | 92 | 93 | #define _USE_LFN 2 94 | #define _MAX_LFN 255 95 | /* The _USE_LFN option switches the LFN feature. 96 | / 97 | / 0: Disable LFN feature. _MAX_LFN has no effect. 98 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 99 | / 2: Enable LFN with dynamic working buffer on the STACK. 100 | / 3: Enable LFN with dynamic working buffer on the HEAP. 101 | / 102 | / When enable the LFN feature, Unicode handling functions (option/unicode.c) must 103 | / be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. 104 | / When use stack for the working buffer, take care on stack overflow. When use heap 105 | / memory for the working buffer, memory management functions, ff_memalloc() and 106 | / ff_memfree(), must be added to the project. */ 107 | 108 | 109 | #define _LFN_UNICODE 0 110 | /* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode) 111 | / To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE 112 | / to 1. This option also affects behavior of string I/O functions. */ 113 | 114 | 115 | #define _STRF_ENCODE 3 116 | /* When _LFN_UNICODE is 1, this option selects the character encoding on the file to 117 | / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). 118 | / 119 | / 0: ANSI/OEM 120 | / 1: UTF-16LE 121 | / 2: UTF-16BE 122 | / 3: UTF-8 123 | / 124 | / When _LFN_UNICODE is 0, this option has no effect. */ 125 | 126 | 127 | #define _FS_RPATH 2 128 | /* This option configures relative path feature. 129 | / 130 | / 0: Disable relative path feature and remove related functions. 131 | / 1: Enable relative path feature. f_chdir() and f_chdrive() are available. 132 | / 2: f_getcwd() function is available in addition to 1. 133 | / 134 | / Note that directory items read via f_readdir() are affected by this option. */ 135 | 136 | 137 | /*---------------------------------------------------------------------------/ 138 | / Drive/Volume Configurations 139 | /---------------------------------------------------------------------------*/ 140 | 141 | #define _VOLUMES 8 142 | /* Number of volumes (logical drives) to be used. */ 143 | 144 | 145 | #define _STR_VOLUME_ID 0 146 | #define _VOLUME_STRS "SD","SD1","SD2","SD3","IDE","IDE1","IDE2","IDE3" 147 | /* _STR_VOLUME_ID option switches string volume ID feature. 148 | / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive 149 | / number in the path name. _VOLUME_STRS defines the drive ID strings for each 150 | / logical drives. Number of items must be equal to _VOLUMES. Valid characters for 151 | / the drive ID strings are: A-Z and 0-9. */ 152 | 153 | 154 | #define _MULTI_PARTITION 1 155 | /* This option switches multi-partition feature. By default (0), each logical drive 156 | / number is bound to the same physical drive number and only an FAT volume found on 157 | / the physical drive will be mounted. When multi-partition feature is enabled (1), 158 | / each logical drive number is bound to arbitrary physical drive and partition 159 | / listed in the VolToPart[]. Also f_fdisk() funciton will be available. */ 160 | 161 | 162 | #define _MIN_SS 512 163 | #define _MAX_SS 512 164 | /* These options configure the range of sector size to be supported. (512, 1024, 165 | / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and 166 | / harddisk. But a larger value may be required for on-board flash memory and some 167 | / type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured 168 | / to variable sector size and GET_SECTOR_SIZE command must be implemented to the 169 | / disk_ioctl() function. */ 170 | 171 | 172 | #define _USE_TRIM 0 173 | /* This option switches ATA-TRIM feature. (0:Disable or 1:Enable) 174 | / To enable Trim feature, also CTRL_TRIM command should be implemented to the 175 | / disk_ioctl() function. */ 176 | 177 | 178 | #define _FS_NOFSINFO 0 179 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 180 | / option, and f_getfree() function at first time after volume mount will force 181 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 182 | / 183 | / bit0=0: Use free cluster count in the FSINFO if available. 184 | / bit0=1: Do not trust free cluster count in the FSINFO. 185 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 186 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 187 | */ 188 | 189 | 190 | 191 | /*---------------------------------------------------------------------------/ 192 | / System Configurations 193 | /---------------------------------------------------------------------------*/ 194 | 195 | #define _FS_TINY 0 196 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 197 | / At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS 198 | / bytes. Instead of private sector buffer eliminated from the file object, 199 | / common sector buffer in the file system object (FATFS) is used for the file 200 | / data transfer. */ 201 | 202 | 203 | #define _FS_NORTC 0 204 | #define _NORTC_MON 1 205 | #define _NORTC_MDAY 1 206 | #define _NORTC_YEAR 2015 207 | /* The _FS_NORTC option switches timestamp feature. If the system does not have 208 | / an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable 209 | / the timestamp feature. All objects modified by FatFs will have a fixed timestamp 210 | / defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR. 211 | / When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need 212 | / to be added to the project to read current time form RTC. _NORTC_MON, 213 | / _NORTC_MDAY and _NORTC_YEAR have no effect. 214 | / These options have no effect at read-only configuration (_FS_READONLY == 1). */ 215 | 216 | 217 | #define _FS_LOCK 0 218 | /* The _FS_LOCK option switches file lock feature to control duplicated file open 219 | / and illegal operation to open objects. This option must be 0 when _FS_READONLY 220 | / is 1. 221 | / 222 | / 0: Disable file lock feature. To avoid volume corruption, application program 223 | / should avoid illegal open, remove and rename to the open objects. 224 | / >0: Enable file lock feature. The value defines how many files/sub-directories 225 | / can be opened simultaneously under file lock control. Note that the file 226 | / lock feature is independent of re-entrancy. */ 227 | 228 | 229 | #define _FS_REENTRANT 0 230 | #define _FS_TIMEOUT 1000 231 | #define _SYNC_t void * 232 | /* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs 233 | / module itself. Note that regardless of this option, file access to different 234 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 235 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 236 | / to the same volume is under control of this feature. 237 | / 238 | / 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. 239 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 240 | / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() 241 | / function, must be added to the project. Samples are available in 242 | / option/syscall.c. 243 | / 244 | / The _FS_TIMEOUT defines timeout period in unit of time tick. 245 | / The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, 246 | / SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be 247 | / included somewhere in the scope of ff.c. */ 248 | 249 | 250 | #define _WORD_ACCESS 0 251 | /* The _WORD_ACCESS option is an only platform dependent option. It defines 252 | / which access method is used to the word data on the FAT volume. 253 | / 254 | / 0: Byte-by-byte access. Always compatible with all platforms. 255 | / 1: Word access. Do not choose this unless under both the following conditions. 256 | / 257 | / * Address misaligned memory access is always allowed to ALL instructions. 258 | / * Byte order on the memory is little-endian. 259 | / 260 | / If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size. 261 | / Following table shows allowable settings of some type of processors. 262 | / 263 | / ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2 264 | / Cortex-M3 0 *3 Z80 0/1 V850ES 0/1 265 | / Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1 266 | / AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1 267 | / AVR32 0 *1 RL78 0 *2 R32C 0 *2 268 | / PIC18 0/1 SH-2 0 *1 M16C 0/1 269 | / PIC24 0 *2 H8S 0 *1 MSP430 0 *2 270 | / PIC32 0 *1 H8/300H 0 *1 8051 0/1 271 | / 272 | / *1:Big-endian. 273 | / *2:Unaligned memory access is not supported. 274 | / *3:Some compilers generate LDM/STM for mem_cpy function. 275 | */ 276 | 277 | -------------------------------------------------------------------------------- /src/fatfs/ff.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / FatFs - FAT file system module include R0.11a (C)ChaN, 2015 3 | /----------------------------------------------------------------------------/ 4 | / FatFs module is a free software that opened under license policy of 5 | / following conditions. 6 | / 7 | / Copyright (C) 2015, ChaN, all right reserved. 8 | / Copyright (C) 2025 Ruslan Rostovtsev 9 | / 10 | / 1. Redistributions of source code must retain the above copyright notice, 11 | / this condition and the following disclaimer. 12 | / 13 | / This software is provided by the copyright holder and contributors "AS IS" 14 | / and any warranties related to this software are DISCLAIMED. 15 | / The copyright owner or contributors be NOT LIABLE for any damages caused 16 | / by use of this software. 17 | /---------------------------------------------------------------------------*/ 18 | 19 | 20 | #ifndef _FATFS 21 | #define _FATFS 64180 /* Revision ID */ 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #include "integer.h" /* Basic integer types */ 28 | #include "ffconf.h" /* FatFs configuration options */ 29 | #if _FATFS != _FFCONF 30 | #error Wrong configuration file (ffconf.h). 31 | #endif 32 | 33 | 34 | 35 | /* Definitions of volume management */ 36 | 37 | #if _MULTI_PARTITION /* Multiple partition configuration */ 38 | typedef struct { 39 | BYTE pd; /* Physical drive number */ 40 | BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ 41 | } PARTITION; 42 | extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ 43 | #define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */ 44 | #define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */ 45 | 46 | #else /* Single partition configuration */ 47 | #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ 48 | #define LD2PT(vol) 0 /* Find first valid partition or in SFD */ 49 | 50 | #endif 51 | 52 | 53 | 54 | /* Type of path name strings on FatFs API */ 55 | 56 | #if _LFN_UNICODE /* Unicode string */ 57 | #if !_USE_LFN 58 | #error _LFN_UNICODE must be 0 at non-LFN cfg. 59 | #endif 60 | #ifndef _INC_TCHAR 61 | typedef WCHAR TCHAR; 62 | #define _T(x) L ## x 63 | #define _TEXT(x) L ## x 64 | #endif 65 | 66 | #else /* ANSI/OEM string */ 67 | #ifndef _INC_TCHAR 68 | typedef char TCHAR; 69 | #define _T(x) x 70 | #define _TEXT(x) x 71 | #endif 72 | 73 | #endif 74 | 75 | 76 | 77 | /* File system object structure (FATFS) */ 78 | 79 | typedef struct { 80 | BYTE fs_type; /* FAT sub-type (0:Not mounted) */ 81 | BYTE drv; /* Physical drive number */ 82 | BYTE csize; /* Sectors per cluster (1,2,4...128) */ 83 | BYTE n_fats; /* Number of FAT copies (1 or 2) */ 84 | BYTE wflag; /* win[] flag (b0:dirty) */ 85 | BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ 86 | WORD id; /* File system mount ID */ 87 | WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ 88 | #if _MAX_SS != _MIN_SS 89 | WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */ 90 | #endif 91 | #if _FS_REENTRANT 92 | _SYNC_t sobj; /* Identifier of sync object */ 93 | #endif 94 | #if !_FS_READONLY 95 | DWORD last_clust; /* Last allocated cluster */ 96 | DWORD free_clust; /* Number of free clusters */ 97 | #endif 98 | #if _FS_RPATH 99 | DWORD cdir; /* Current directory start cluster (0:root) */ 100 | #endif 101 | DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */ 102 | DWORD fsize; /* Sectors per FAT */ 103 | DWORD volbase; /* Volume start sector */ 104 | DWORD fatbase; /* FAT start sector */ 105 | DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */ 106 | DWORD database; /* Data start sector */ 107 | DWORD winsect; /* Current sector appearing in the win[] */ 108 | BYTE win[_MAX_SS] __attribute__((aligned(32))); /* Disk access window for Directory, FAT (and file data at tiny cfg) */ 109 | } FATFS; 110 | 111 | 112 | 113 | /* File object structure (FIL) */ 114 | 115 | typedef struct { 116 | FATFS* fs; /* Pointer to the related file system object (**do not change order**) */ 117 | WORD id; /* Owner file system mount ID (**do not change order**) */ 118 | BYTE flag; /* Status flags */ 119 | BYTE err; /* Abort flag (error code) */ 120 | DWORD fptr; /* File read/write pointer (Zeroed on file open) */ 121 | DWORD fsize; /* File size */ 122 | DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */ 123 | DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */ 124 | DWORD dsect; /* Sector number appearing in buf[] (0:invalid) */ 125 | #if !_FS_READONLY 126 | DWORD dir_sect; /* Sector number containing the directory entry */ 127 | BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ 128 | #endif 129 | #if _USE_FASTSEEK 130 | DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */ 131 | #endif 132 | #if _FS_LOCK 133 | UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ 134 | #endif 135 | #if !_FS_TINY 136 | BYTE buf[_MAX_SS] __attribute__((aligned(32))); /* File private data read/write window */ 137 | #endif 138 | } FIL; 139 | 140 | 141 | 142 | /* Directory object structure (DIR) */ 143 | 144 | typedef struct { 145 | FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */ 146 | WORD id; /* Owner file system mount ID (**do not change order**) */ 147 | WORD index; /* Current read/write index number */ 148 | DWORD sclust; /* Table start cluster (0:Root dir) */ 149 | DWORD clust; /* Current cluster */ 150 | DWORD sect; /* Current sector */ 151 | BYTE* dir; /* Pointer to the current SFN entry in the win[] */ 152 | BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ 153 | #if _FS_LOCK 154 | UINT lockid; /* File lock ID (index of file semaphore table Files[]) */ 155 | #endif 156 | #if _USE_LFN 157 | WCHAR* lfn; /* Pointer to the LFN working buffer */ 158 | WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ 159 | #endif 160 | #if _USE_FIND 161 | const TCHAR* pat; /* Pointer to the name matching pattern */ 162 | #endif 163 | } DIR; 164 | 165 | 166 | 167 | /* File information structure (FILINFO) */ 168 | 169 | typedef struct { 170 | DWORD fsize; /* File size */ 171 | WORD fdate; /* Last modified date */ 172 | WORD ftime; /* Last modified time */ 173 | BYTE fattrib; /* Attribute */ 174 | TCHAR fname[13]; /* Short file name (8.3 format) */ 175 | #if _USE_LFN 176 | TCHAR* lfname; /* Pointer to the LFN buffer */ 177 | UINT lfsize; /* Size of LFN buffer in TCHAR */ 178 | #endif 179 | } FILINFO; 180 | 181 | 182 | 183 | /* File function return code (FRESULT) */ 184 | 185 | typedef enum { 186 | FR_OK = 0, /* (0) Succeeded */ 187 | FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ 188 | FR_INT_ERR, /* (2) Assertion failed */ 189 | FR_NOT_READY, /* (3) The physical drive cannot work */ 190 | FR_NO_FILE, /* (4) Could not find the file */ 191 | FR_NO_PATH, /* (5) Could not find the path */ 192 | FR_INVALID_NAME, /* (6) The path name format is invalid */ 193 | FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ 194 | FR_EXIST, /* (8) Access denied due to prohibited access */ 195 | FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ 196 | FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ 197 | FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ 198 | FR_NOT_ENABLED, /* (12) The volume has no work area */ 199 | FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ 200 | FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */ 201 | FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ 202 | FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ 203 | FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ 204 | FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */ 205 | FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ 206 | } FRESULT; 207 | 208 | 209 | 210 | /*--------------------------------------------------------------*/ 211 | /* FatFs module application interface */ 212 | 213 | FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ 214 | FRESULT f_close (FIL* fp); /* Close an open file object */ 215 | FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */ 216 | FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */ 217 | FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ 218 | FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */ 219 | FRESULT f_truncate (FIL* fp); /* Truncate file */ 220 | FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */ 221 | FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ 222 | FRESULT f_closedir (DIR* dp); /* Close an open directory */ 223 | FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ 224 | FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ 225 | FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ 226 | FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ 227 | FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ 228 | FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ 229 | FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ 230 | FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */ 231 | FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change times-tamp of the file/dir */ 232 | FRESULT f_chdir (const TCHAR* path); /* Change current directory */ 233 | FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ 234 | FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ 235 | FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ 236 | FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ 237 | FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ 238 | FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ 239 | FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */ 240 | FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */ 241 | int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ 242 | int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ 243 | int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ 244 | TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ 245 | 246 | #define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize)) 247 | #define f_error(fp) ((fp)->err) 248 | #define f_tell(fp) ((fp)->fptr) 249 | #define f_size(fp) ((fp)->fsize) 250 | #define f_rewind(fp) f_lseek((fp), 0) 251 | #define f_rewinddir(dp) f_readdir((dp), 0) 252 | 253 | #ifndef EOF 254 | #define EOF (-1) 255 | #endif 256 | 257 | 258 | 259 | 260 | /*--------------------------------------------------------------*/ 261 | /* Additional user defined functions */ 262 | 263 | /* RTC function */ 264 | #if !_FS_READONLY && !_FS_NORTC 265 | DWORD get_fattime (void); 266 | #endif 267 | 268 | /* Unicode support functions */ 269 | #if _USE_LFN /* Unicode - OEM code conversion */ 270 | WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ 271 | WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ 272 | #if _USE_LFN == 3 /* Memory functions */ 273 | void* ff_memalloc (UINT msize); /* Allocate memory block */ 274 | void ff_memfree (void* mblock); /* Free memory block */ 275 | #endif 276 | #endif 277 | 278 | /* Sync functions */ 279 | #if _FS_REENTRANT 280 | int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */ 281 | int ff_req_grant (_SYNC_t sobj); /* Lock sync object */ 282 | void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */ 283 | int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */ 284 | #endif 285 | 286 | 287 | 288 | 289 | /*--------------------------------------------------------------*/ 290 | /* Flags and offset address */ 291 | 292 | 293 | /* File access control and file status flags (FIL.flag) */ 294 | 295 | #define FA_READ 0x01 296 | #define FA_OPEN_EXISTING 0x00 297 | 298 | #if !_FS_READONLY 299 | #define FA_WRITE 0x02 300 | #define FA_CREATE_NEW 0x04 301 | #define FA_CREATE_ALWAYS 0x08 302 | #define FA_OPEN_ALWAYS 0x10 303 | #define FA__WRITTEN 0x20 304 | #define FA__DIRTY 0x40 305 | #endif 306 | 307 | 308 | /* FAT sub type (FATFS.fs_type) */ 309 | 310 | #define FS_FAT12 1 311 | #define FS_FAT16 2 312 | #define FS_FAT32 3 313 | 314 | 315 | /* File attribute bits for directory entry */ 316 | 317 | #define AM_RDO 0x01 /* Read only */ 318 | #define AM_HID 0x02 /* Hidden */ 319 | #define AM_SYS 0x04 /* System */ 320 | #define AM_VOL 0x08 /* Volume label */ 321 | #define AM_LFN 0x0F /* LFN entry */ 322 | #define AM_DIR 0x10 /* Directory */ 323 | #define AM_ARC 0x20 /* Archive */ 324 | #define AM_MASK 0x3F /* Mask of defined bits */ 325 | 326 | 327 | /* Fast seek feature */ 328 | #define CREATE_LINKMAP 0xFFFFFFFF 329 | 330 | 331 | 332 | /*--------------------------------*/ 333 | /* Multi-byte word access macros */ 334 | 335 | #if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ 336 | #define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) 337 | #define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) 338 | #define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) 339 | #define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) 340 | #else /* Use byte-by-byte access to the FAT structure */ 341 | #define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) 342 | #define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr)) 343 | #define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8) 344 | #define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24) 345 | #endif 346 | 347 | #ifdef __cplusplus 348 | } 349 | #endif 350 | 351 | #endif /* _FATFS */ 352 | -------------------------------------------------------------------------------- /src/fatfs/option/ccsbcs.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Unicode - Local code bidirectional converter (C)ChaN, 2015 */ 3 | /* (SBCS code pages) */ 4 | /*------------------------------------------------------------------------*/ 5 | /* 437 U.S. 6 | / 720 Arabic 7 | / 737 Greek 8 | / 771 KBL 9 | / 775 Baltic 10 | / 850 Latin 1 11 | / 852 Latin 2 12 | / 855 Cyrillic 13 | / 857 Turkish 14 | / 860 Portuguese 15 | / 861 Icelandic 16 | / 862 Hebrew 17 | / 863 Canadian French 18 | / 864 Arabic 19 | / 865 Nordic 20 | / 866 Russian 21 | / 869 Greek 2 22 | */ 23 | 24 | #include "../ff.h" 25 | 26 | 27 | #if _CODE_PAGE == 437 28 | #define _TBLDEF 1 29 | static 30 | const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ 31 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 32 | 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 33 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 34 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 35 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 36 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 37 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 38 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 39 | }; 40 | 41 | #elif _CODE_PAGE == 720 42 | #define _TBLDEF 1 43 | static 44 | const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */ 45 | 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, 46 | 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, 47 | 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, 48 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 49 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 50 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 51 | 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 52 | 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 53 | }; 54 | 55 | #elif _CODE_PAGE == 737 56 | #define _TBLDEF 1 57 | static 58 | const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */ 59 | 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 60 | 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 61 | 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 62 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 63 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 64 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 65 | 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, 66 | 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 67 | }; 68 | 69 | #elif _CODE_PAGE == 771 70 | #define _TBLDEF 1 71 | static 72 | const WCHAR Tbl[] = { /* CP771(0x80-0xFF) to Unicode conversion table */ 73 | 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 74 | 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 75 | 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 76 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 77 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 78 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D, 79 | 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 80 | 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0 81 | }; 82 | 83 | #elif _CODE_PAGE == 775 84 | #define _TBLDEF 1 85 | static 86 | const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */ 87 | 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, 88 | 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, 89 | 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, 90 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, 91 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, 92 | 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 93 | 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, 94 | 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 95 | }; 96 | 97 | #elif _CODE_PAGE == 850 98 | #define _TBLDEF 1 99 | static 100 | const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */ 101 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 102 | 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, 103 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 104 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, 105 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 106 | 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 107 | 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, 108 | 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 109 | }; 110 | 111 | #elif _CODE_PAGE == 852 112 | #define _TBLDEF 1 113 | static 114 | const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */ 115 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, 116 | 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, 117 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, 118 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, 119 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 120 | 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, 121 | 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, 122 | 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 123 | }; 124 | 125 | #elif _CODE_PAGE == 855 126 | #define _TBLDEF 1 127 | static 128 | const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */ 129 | 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, 130 | 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, 131 | 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, 132 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, 133 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 134 | 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, 135 | 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, 136 | 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 137 | }; 138 | 139 | #elif _CODE_PAGE == 857 140 | #define _TBLDEF 1 141 | static 142 | const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */ 143 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, 144 | 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, 145 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 146 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, 147 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, 148 | 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, 149 | 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, 150 | 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 151 | }; 152 | 153 | #elif _CODE_PAGE == 860 154 | #define _TBLDEF 1 155 | static 156 | const WCHAR Tbl[] = { /* CP860(0x80-0xFF) to Unicode conversion table */ 157 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2, 158 | 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3, 159 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 160 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 161 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 162 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 163 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 164 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 165 | }; 166 | 167 | #elif _CODE_PAGE == 861 168 | #define _TBLDEF 1 169 | static 170 | const WCHAR Tbl[] = { /* CP861(0x80-0xFF) to Unicode conversion table */ 171 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5, 172 | 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, 173 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 174 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 175 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 176 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 177 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 178 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 179 | }; 180 | 181 | #elif _CODE_PAGE == 862 182 | #define _TBLDEF 1 183 | static 184 | const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */ 185 | 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 186 | 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, 187 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 188 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 189 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 190 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 191 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 192 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 193 | }; 194 | 195 | #elif _CODE_PAGE == 863 196 | #define _TBLDEF 1 197 | static 198 | const WCHAR Tbl[] = { /* CP863(0x80-0xFF) to Unicode conversion table */ 199 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0, 200 | 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192, 201 | 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB, 202 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 203 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 204 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 205 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219, 206 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 207 | }; 208 | 209 | #elif _CODE_PAGE == 864 210 | #define _TBLDEF 1 211 | static 212 | const WCHAR Tbl[] = { /* CP864(0x80-0xFF) to Unicode conversion table */ 213 | 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518, 214 | 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000, 215 | 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5, 216 | 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F, 217 | 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, 218 | 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9, 219 | 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1, 220 | 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000 221 | }; 222 | 223 | #elif _CODE_PAGE == 865 224 | #define _TBLDEF 1 225 | static 226 | const WCHAR Tbl[] = { /* CP865(0x80-0xFF) to Unicode conversion table */ 227 | 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, 228 | 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, 229 | 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4, 230 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, 231 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 232 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 233 | 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 234 | 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 235 | }; 236 | 237 | #elif _CODE_PAGE == 866 238 | #define _TBLDEF 1 239 | static 240 | const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */ 241 | 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 242 | 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 243 | 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 244 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 245 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 246 | 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 247 | 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 248 | 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 249 | }; 250 | 251 | #elif _CODE_PAGE == 869 252 | #define _TBLDEF 1 253 | static 254 | const WCHAR Tbl[] = { /* CP869(0x80-0xFF) to Unicode conversion table */ 255 | 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, 256 | 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF, 257 | 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB, 258 | 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510, 259 | 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3, 260 | 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580, 261 | 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384, 262 | 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0 263 | }; 264 | 265 | #endif 266 | 267 | 268 | #if !_TBLDEF || !_USE_LFN 269 | #error This file is not needed at current configuration. Remove from the project. 270 | #endif 271 | 272 | 273 | 274 | 275 | WCHAR ff_convert ( /* Converted character, Returns zero on error */ 276 | WCHAR chr, /* Character code to be converted */ 277 | UINT dir /* 0: Unicode to OEM code, 1: OEM code to Unicode */ 278 | ) 279 | { 280 | WCHAR c; 281 | 282 | 283 | if (chr < 0x80) { /* ASCII */ 284 | c = chr; 285 | 286 | } else { 287 | if (dir) { /* OEM code to Unicode */ 288 | c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80]; 289 | 290 | } else { /* Unicode to OEM code */ 291 | for (c = 0; c < 0x80; c++) { 292 | if (chr == Tbl[c]) break; 293 | } 294 | c = (c + 0x80) & 0xFF; 295 | } 296 | } 297 | 298 | return c; 299 | } 300 | 301 | 302 | 303 | 304 | WCHAR ff_wtoupper ( /* Returns upper converted character */ 305 | WCHAR chr /* Unicode character to be upper converted */ 306 | ) 307 | { 308 | static const WCHAR lower[] = { /* Lower case characters to be converted */ 309 | /* Latin Supplement */ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, 310 | /* Latin Extended-A */ 0x101,0x103,0x105,0x107,0x109,0x10B,0x10D,0x10F,0x111,0x113,0x115,0x117,0x119,0x11B,0x11D,0x11F,0x121,0x123,0x125,0x127,0x129,0x12B,0x12D,0x12F,0x131,0x133,0x135,0x137,0x13A,0x13C,0x13E,0x140,0x142,0x144,0x146,0x148,0x14B,0x14D,0x14F,0x151,0x153,0x155,0x157,0x159,0x15B,0x15D,0x15F,0x161,0x163,0x165,0x167,0x169,0x16B,0x16D,0x16F,0x171,0x173,0x175,0x177,0x17A,0x17C,0x17E, 311 | /* Latin Extended-B */ 0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217, 312 | /* Greek, Coptic */ 0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF,0x3C0,0x3C1,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB, 313 | /* Cyrillic */ 0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F,0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9, 314 | /* Armenian */ 0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586, 315 | /* Latin Extended Additional */ 0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9, 316 | /* Number forms */ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F, 317 | /* Full-width */ 0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A 318 | }; 319 | static const WCHAR upper[] = { /* Upper case characters correspond to lower[] */ 320 | 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178, 321 | 0x100,0x102,0x104,0x106,0x108,0x10A,0x10C,0x10E,0x110,0x112,0x114,0x116,0x118,0x11A,0x11C,0x11E,0x120,0x122,0x124,0x126,0x128,0x12A,0x12C,0x12E,0x130,0x132,0x134,0x136,0x139,0x13B,0x13D,0x13F,0x141,0x143,0x145,0x147,0x14A,0x14C,0x14E,0x150,0x152,0x154,0x156,0x158,0x15A,0x15C,0x15E,0x160,0x162,0x164,0x166,0x168,0x16A,0x16C,0x16E,0x170,0x172,0x174,0x176,0x179,0x17B,0x17D, 322 | 0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216, 323 | 0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F,0x3A0,0x3A1,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA, 324 | 0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8, 325 | 0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556, 326 | 0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8, 327 | 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F, 328 | 0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A 329 | }; 330 | UINT i, n, hi, li; 331 | 332 | 333 | if (chr < 0x80) { /* ASCII characters (acceleration) */ 334 | if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20; 335 | 336 | } else { /* Non ASCII characters (table search) */ 337 | n = 12; li = 0; hi = sizeof lower / sizeof lower[0]; 338 | do { 339 | i = li + (hi - li) / 2; 340 | if (chr == lower[i]) break; 341 | if (chr > lower[i]) li = i; else hi = i; 342 | } while (--n); 343 | if (n) chr = upper[i]; 344 | } 345 | 346 | return chr; 347 | } 348 | 349 | -------------------------------------------------------------------------------- /src/fatfs/dc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FatFs for the Sega Dreamcast 3 | * 4 | * This file is part of the FatFs module, a generic FAT filesystem 5 | * module for small embedded systems. This version has been ported and 6 | * optimized specifically for the Sega Dreamcast platform. 7 | * 8 | * Copyright (c) 2007-2025 Ruslan Rostovtsev 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a 11 | * copy of this software and associated documentation files (the "Software"), 12 | * to deal in the Software without restriction, including without limitation 13 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | * and/or sell copies of the Software, and to permit persons to whom the 15 | * Software is furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included 18 | * in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "diskio.h" 44 | #include "fatfs.h" 45 | #include "ff.h" 46 | #include "integer.h" 47 | #include "../log.h" 48 | 49 | #define MAX_FAT_MOUNTS _VOLUMES 50 | #define MAX_FAT_FILES 16 51 | #define FATFS_LINK_TBL_SIZE 32 52 | 53 | typedef uint32_t ptr_t; 54 | 55 | typedef struct fatfs_mnt { 56 | 57 | FATFS *fs; 58 | vfs_handler_t *vfsh; 59 | kos_blockdev_t *dev; 60 | kos_blockdev_t *dev_dma; 61 | 62 | DSTATUS dev_stat; 63 | BYTE dev_id; 64 | 65 | TCHAR dev_path[16]; 66 | 67 | #ifdef FATFS_USE_DMA_BUF 68 | uint8_t *dmabuf; 69 | #endif 70 | 71 | } fatfs_mnt_t; 72 | 73 | typedef struct fatfs { 74 | 75 | FIL fil __attribute__((aligned(32))); 76 | DIR dir; 77 | int type; 78 | int used; 79 | int mode; 80 | 81 | DWORD lktbl[FATFS_LINK_TBL_SIZE]; 82 | dirent_t dent; 83 | 84 | fatfs_mnt_t *mnt; 85 | 86 | } fatfs_t; 87 | 88 | static mutex_t fat_mutex = MUTEX_INITIALIZER; 89 | #define FAT_LOCK() mutex_lock(&fat_mutex); 90 | #define FAT_LOCK_SCOPED() mutex_lock_scoped(&fat_mutex); 91 | #define FAT_UNLOCK() mutex_unlock(&fat_mutex); 92 | 93 | static int initted = 0; 94 | static fatfs_t fh[MAX_FAT_FILES] __attribute__((aligned(32))); 95 | static fatfs_mnt_t fat_mnt[MAX_FAT_MOUNTS] __attribute__((aligned(32))); 96 | 97 | #if _MULTI_PARTITION /* Volume - Partition resolution table */ 98 | 99 | /* Physical drive number; Partition: 0:Auto detect, 1-4:Forced partition) */ 100 | 101 | PARTITION VolToPart[16] = { 102 | {0, 0}, {0, 0}, {0, 0}, 103 | {0, 0}, {0, 0}, {0, 0}, 104 | {0, 0}, {0, 0}, {0, 0}, 105 | {0, 0}, {0, 0}, {0, 0}, 106 | {0, 0}, {0, 0}, {0, 0}, 107 | {0, 0} 108 | }; 109 | 110 | #endif 111 | 112 | 113 | #ifdef FATFS_DEBUG 114 | 115 | # define DBG(x) dbglog x 116 | 117 | static void put_rc(FRESULT rc, const char *func) { 118 | const char *p; 119 | static const char str[] = 120 | "OK\0" "DISK_ERR\0" "INT_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0" 121 | "INVALID_NAME\0" "DENIED\0" "EXIST\0" "INVALID_OBJECT\0" "WRITE_PROTECTED\0" 122 | "INVALID_DRIVE\0" "NOT_ENABLED\0" "NO_FILE_SYSTEM\0" "MKFS_ABORTED\0" "TIMEOUT\0" 123 | "LOCKED\0" "NOT_ENOUGH_CORE\0" "TOO_MANY_OPEN_FILES\0"; 124 | 125 | FRESULT i; 126 | 127 | for (p = str, i = 0; i != rc && *p; ++i) { 128 | while(*p++); 129 | } 130 | DBG((DBG_DEBUG, "FATFS: %s: %u FR_%s\n", func, (UINT)rc, p)); 131 | } 132 | 133 | #else 134 | # define DBG(x) 135 | # define put_rc(r, f) 136 | #endif 137 | 138 | static void fatfs_set_errno(FRESULT rc) { 139 | switch (rc) { 140 | case FR_OK: /* (0) Succeeded */ 141 | errno = 0; 142 | break; 143 | case FR_DISK_ERR: /* (1) A hard error occurred in the low level disk I/O layer */ 144 | //errno = EIO; already set in driver 145 | break; 146 | case FR_INT_ERR: /* (2) Assertion failed */ 147 | errno = EFAULT; 148 | break; 149 | case FR_NOT_READY: /* (3) The physical drive cannot work */ 150 | errno = ENODEV; 151 | break; 152 | case FR_NO_FILE: /* (4) Could not find the file */ 153 | case FR_NO_PATH: /* (5) Could not find the path */ 154 | errno = ENOENT; 155 | break; 156 | case FR_INVALID_NAME: /* (6) The path name format is invalid */ 157 | errno = EINVAL; 158 | break; 159 | case FR_DENIED: /* (7) Access denied due to prohibited access or directory full */ 160 | errno = ENOSPC; 161 | break; 162 | case FR_EXIST: /* (8) Access denied due to prohibited access */ 163 | errno = EACCES; 164 | break; 165 | case FR_INVALID_OBJECT: /* (9) The file/directory object is invalid */ 166 | errno = EBADF; 167 | break; 168 | case FR_WRITE_PROTECTED: /* (10) The physical drive is write protected */ 169 | errno = EROFS; 170 | break; 171 | case FR_INVALID_DRIVE: /* (11) The logical drive number is invalid */ 172 | errno = ENXIO; 173 | break; 174 | case FR_NOT_ENABLED: /* (12) The volume has no work area */ 175 | errno = EIDRM; 176 | break; 177 | case FR_NO_FILESYSTEM: /* (13) There is no valid FAT volume */ 178 | errno = EIO; 179 | break; 180 | case FR_MKFS_ABORTED: /* (14) The f_mkfs() aborted due to any parameter error */ 181 | errno = EINVAL; 182 | break; 183 | case FR_TIMEOUT: /* (15) Could not get a grant to access the volume within defined period */ 184 | errno = ETIME; 185 | break; 186 | case FR_LOCKED: /* (16) The operation is rejected according to the file sharing policy */ 187 | errno = EAGAIN; 188 | break; 189 | case FR_NOT_ENOUGH_CORE: /* (17) LFN working buffer could not be allocated */ 190 | errno = ENOMEM; 191 | break; 192 | case FR_TOO_MANY_OPEN_FILES: /* (18) Number of open files > _FS_SHARE */ 193 | errno = EMFILE; 194 | break; 195 | case FR_INVALID_PARAMETER: /* (19) Given parameter is invalid */ 196 | errno = EINVAL; 197 | break; 198 | default: 199 | errno = 0; 200 | break; 201 | } 202 | } 203 | 204 | static FRESULT fat_create_linkmap(fatfs_t *sf) { 205 | FRESULT rc; 206 | 207 | if (sf->fil.cltbl != NULL) { 208 | return FR_OK; 209 | } 210 | 211 | memset(&sf->lktbl, 0, FATFS_LINK_TBL_SIZE * sizeof(DWORD)); 212 | sf->fil.cltbl = sf->lktbl; /* Enable fast seek feature */ 213 | sf->lktbl[0] = FATFS_LINK_TBL_SIZE; /* Set table size to the first item */ 214 | 215 | /* Create CLMT */ 216 | rc = f_lseek(&sf->fil, CREATE_LINKMAP); 217 | 218 | if (rc == FR_NOT_ENOUGH_CORE) { 219 | 220 | DBG((DBG_DEBUG, "FATFS: Creating linkmap %d < %ld, retry...", 221 | FATFS_LINK_TBL_SIZE, sf->lktbl[0])); 222 | 223 | size_t lms = sf->fil.cltbl[0]; 224 | sf->fil.cltbl = (DWORD *) calloc(lms, sizeof(DWORD)); 225 | 226 | if (sf->fil.cltbl != NULL) { 227 | sf->fil.cltbl[0] = lms; 228 | rc = f_lseek(&sf->fil, CREATE_LINKMAP); 229 | 230 | if (rc != FR_OK) { 231 | free(sf->fil.cltbl); 232 | } 233 | } 234 | } 235 | 236 | if (rc != FR_OK) { 237 | sf->fil.cltbl = NULL; 238 | DBG((DBG_ERROR, "FATFS: Create linkmap %ld error: %d", sf->lktbl[0], rc)); 239 | } 240 | else { 241 | DBG((DBG_DEBUG, "FATFS: Created linkmap %ld dwords\n", sf->lktbl[0])); 242 | } 243 | return rc; 244 | } 245 | 246 | 247 | #define FAT_GET_HND(hnd, rv) \ 248 | file_t fd = ((file_t)hnd) - 1; \ 249 | fatfs_t *sf = NULL; \ 250 | FAT_LOCK_SCOPED(); \ 251 | if (fd > -1 && fd < MAX_FAT_FILES) { \ 252 | sf = &fh[fd]; \ 253 | } else { \ 254 | errno = ENFILE; \ 255 | return rv; \ 256 | } 257 | 258 | 259 | static void *fat_open(vfs_handler_t *vfs, const char *fn, int flags) { 260 | file_t fd; 261 | fatfs_t *sf; 262 | fatfs_mnt_t *mnt; 263 | FRESULT rc; 264 | int fat_flags = 0, mode = (flags & O_MODE_MASK); 265 | 266 | FAT_LOCK_SCOPED(); 267 | mnt = (fatfs_mnt_t *)vfs->privdata; 268 | 269 | if (mnt == NULL) { 270 | dash_log(DBG_ERROR, "FATFS: Error, not mounted.\n"); 271 | errno = ENOMEM; 272 | return NULL; 273 | } 274 | 275 | for (fd = 0; fd < MAX_FAT_FILES; ++fd) { 276 | if (fh[fd].used == 0) { 277 | sf = &fh[fd]; 278 | break; 279 | } 280 | } 281 | 282 | if (fd >= MAX_FAT_FILES) { 283 | errno = ENFILE; 284 | dash_log(DBG_ERROR, "FATFS: The maximum number of opened files exceeded.\n"); 285 | return NULL; 286 | } 287 | 288 | memset(sf, 0, sizeof(fatfs_t)); 289 | rc = f_chdrive(mnt->dev_path); 290 | 291 | if (rc != FR_OK) { 292 | dash_log(DBG_ERROR, "FATFS: Error change drive to - %s\n", mnt->dev_path); 293 | put_rc(rc, __func__); 294 | fatfs_set_errno(rc); 295 | return NULL; 296 | } 297 | 298 | sf->mode = flags; 299 | sf->mnt = mnt; 300 | 301 | /* Directory */ 302 | if (flags & O_DIR) { 303 | 304 | DBG((DBG_DEBUG, "FATFS: Opening directory - %s%s\n", mnt->dev_path, fn)); 305 | rc = f_opendir(&sf->dir, (const TCHAR*)(fn == NULL ? "/" : fn)); 306 | 307 | if (rc != FR_OK) { 308 | DBG((DBG_ERROR, "FATFS: Can't open directory - %s%s\n", mnt->dev_path, fn)); 309 | put_rc(rc, __func__); 310 | fatfs_set_errno(rc); 311 | return NULL; 312 | } 313 | 314 | sf->used = 1; 315 | sf->type = STAT_TYPE_DIR; 316 | 317 | return (void *)(fd + 1); 318 | } 319 | 320 | /* File */ 321 | switch (mode) { 322 | case O_RDONLY: 323 | fat_flags = (FA_OPEN_EXISTING | FA_READ); 324 | break; 325 | case O_WRONLY: 326 | fat_flags = FA_WRITE | (flags & O_TRUNC ? FA_CREATE_ALWAYS : FA_CREATE_NEW); 327 | break; 328 | case O_RDWR: 329 | fat_flags = (FA_WRITE | FA_READ) | (flags & O_TRUNC ? FA_CREATE_ALWAYS : FA_CREATE_NEW); 330 | break; 331 | default: 332 | DBG((DBG_ERROR, "FATFS: Uknown flags\n")); 333 | errno = EINVAL; 334 | return NULL; 335 | } 336 | 337 | DBG((DBG_DEBUG, "FATFS: Opening file - %s%s 0x%02x\n", mnt->dev_path, fn, (uint8)(fat_flags & 0xff))); 338 | 339 | sf->type = STAT_TYPE_FILE; 340 | rc = f_open(&sf->fil, (const TCHAR*)(fn == NULL ? "/" : fn), fat_flags); 341 | 342 | if (rc != FR_OK) { 343 | DBG((DBG_ERROR, "FATFS: Can't open file - %s%s\n", mnt->dev_path, fn)); 344 | put_rc(rc, __func__); 345 | fatfs_set_errno(rc); 346 | return NULL; 347 | } 348 | 349 | if (fat_flags & FA_WRITE) { 350 | f_sync(&sf->fil); 351 | } 352 | 353 | if ((flags & O_APPEND) && sf->fil.fsize > 0) { 354 | DBG((DBG_ERROR, "FATFS: Append file...\n")); 355 | f_lseek(&sf->fil, sf->fil.fsize - 1); 356 | } 357 | 358 | sf->used = 1; 359 | return (void *)(fd + 1); 360 | } 361 | 362 | static int fat_close(void *hnd) { 363 | FAT_GET_HND(hnd, -1); 364 | sf->used = 0; 365 | FRESULT rc = FR_OK; 366 | 367 | DBG((DBG_ERROR, "FATFS: Closing file - %d\n", fd)); 368 | 369 | switch (sf->type) { 370 | case STAT_TYPE_FILE: 371 | if (sf->fil.cltbl != (DWORD*)&sf->lktbl && sf->fil.cltbl != NULL) { 372 | DBG((DBG_ERROR, "FATFS: Freeing linktable\n")); 373 | free(sf->fil.cltbl); 374 | } 375 | rc = f_close(&sf->fil); 376 | break; 377 | case STAT_TYPE_DIR: 378 | rc = f_closedir(&sf->dir); 379 | break; 380 | default: 381 | return -1; 382 | } 383 | 384 | if (rc != FR_OK) { 385 | DBG((DBG_ERROR, "FATFS: Closing error\n")); 386 | put_rc(rc, __func__); 387 | fatfs_set_errno(rc); 388 | return -1; 389 | } 390 | 391 | return 0; 392 | } 393 | 394 | static ssize_t fat_read(void *hnd, void *buffer, size_t size) { 395 | 396 | UINT rs = 0; 397 | FRESULT rc; 398 | 399 | FAT_GET_HND(hnd, -1); 400 | 401 | if (sf->fil.cltbl == NULL && 402 | (sf->mode & O_MODE_MASK) == O_RDONLY && 403 | f_size(&sf->fil) > (DWORD)(sf->mnt->fs->csize * (1 << sf->mnt->dev->l_block_size))) 404 | { 405 | /* Using fast seek feature for files larger than the cluster size */ 406 | rc = fat_create_linkmap(sf); 407 | } 408 | 409 | /* We can use first fs_read just for preparing fast seek feature */ 410 | if(size == 0) { 411 | return 0; 412 | } 413 | 414 | rc = f_read(&sf->fil, buffer, (UINT) size, &rs); 415 | 416 | if (rc != FR_OK) { 417 | put_rc(rc, __func__); 418 | fatfs_set_errno(rc); 419 | return -1; 420 | } 421 | 422 | // DBG((DBG_DEBUG, "FATFS: Read %d %d\n", size, rs)); 423 | return (ssize_t) rs; 424 | } 425 | 426 | static ssize_t fat_write(void *hnd, const void *buffer, size_t cnt) { 427 | UINT bw = 0; 428 | FRESULT rc; 429 | FAT_GET_HND(hnd, -1); 430 | 431 | rc = f_write(&sf->fil, buffer, (UINT) cnt, &bw); 432 | 433 | if (rc != FR_OK) { 434 | put_rc(rc, __func__); 435 | fatfs_set_errno(rc); 436 | return -1; 437 | } 438 | 439 | // DBG((DBG_DEBUG, "FATFS: Write %d %d\n", cnt, bw)); 440 | // f_sync(&sf->fil); 441 | return (ssize_t)bw; 442 | } 443 | 444 | static off_t fat_tell(void * hnd) { 445 | FAT_GET_HND(hnd, -1); 446 | return (off_t)f_tell(&sf->fil); 447 | } 448 | 449 | static off_t fat_seek(void *hnd, off_t offset, int whence) { 450 | FRESULT rc; 451 | DWORD off; 452 | FAT_GET_HND(hnd, -1); 453 | 454 | switch (whence) { 455 | case SEEK_SET: 456 | off = (DWORD) offset; 457 | break; 458 | case SEEK_CUR: 459 | off = (DWORD) (sf->fil.fptr + offset); 460 | break; 461 | case SEEK_END: 462 | off = (DWORD) (sf->fil.fsize + offset); 463 | break; 464 | default: 465 | errno = EINVAL; 466 | return -1; 467 | } 468 | 469 | // DBG((DBG_DEBUG, "FATFS: Seeking: whence=%d req=%ld res=%ld\n", whence, offset, off)); 470 | 471 | rc = f_lseek(&sf->fil, off); 472 | 473 | if (rc != FR_OK) { 474 | put_rc(rc, __func__); 475 | fatfs_set_errno(rc); 476 | return -1; 477 | } 478 | return (off_t) sf->fil.fptr; 479 | } 480 | 481 | static size_t fat_total(void *hnd) { 482 | FAT_GET_HND(hnd, -1); 483 | return (size_t)f_size(&sf->fil); 484 | } 485 | 486 | static dirent_t *fat_readdir(void *hnd) { 487 | FILINFO inf; 488 | FRESULT rc; 489 | FAT_GET_HND(hnd, NULL); 490 | 491 | memset(&sf->dent, 0, sizeof(dirent_t)); 492 | 493 | #if _USE_LFN 494 | inf.lfname = sf->dent.name; 495 | inf.lfsize = NAME_MAX; 496 | #endif 497 | 498 | rc = f_readdir(&sf->dir, &inf); 499 | 500 | if (rc != FR_OK) { 501 | DBG((DBG_ERROR, "FATFS: Error reading directory entry\n")); 502 | put_rc(rc, __func__); 503 | fatfs_set_errno(rc); 504 | return NULL; 505 | } 506 | 507 | if (inf.fname[0] == 0) { 508 | return NULL; 509 | } 510 | 511 | if (!*inf.lfname) { 512 | strncpy(sf->dent.name, inf.fname, 12); 513 | } 514 | 515 | // TODO: date and time parsing 516 | sf->dent.time = (time_t) inf.ftime; 517 | 518 | if (inf.fattrib & AM_DIR) { 519 | sf->dent.attr = O_DIR; 520 | sf->dent.size = -1; 521 | } 522 | else { 523 | sf->dent.attr = 0; 524 | sf->dent.size = inf.fsize; 525 | } 526 | 527 | return &sf->dent; 528 | } 529 | 530 | static int fat_rewinddir(void *hnd) { 531 | FRESULT rc; 532 | FAT_GET_HND(hnd, -1); 533 | 534 | rc = f_rewinddir(&sf->dir); 535 | 536 | if (rc != FR_OK) { 537 | DBG((DBG_ERROR, "FATFS: Error rewind directory\n")); 538 | put_rc(rc, __func__); 539 | fatfs_set_errno(rc); 540 | return -1; 541 | } 542 | 543 | return 0; 544 | } 545 | 546 | /* !=0: Sector number, 0: Failed - invalid cluster# */ 547 | DWORD clust2sect(FATFS *fs, DWORD clst); 548 | 549 | static int fat_ioctl(void *hnd, int cmd, va_list ap) { 550 | DRESULT rc = RES_OK; 551 | FAT_GET_HND(hnd, -1); 552 | void *data = va_arg(ap, void *); 553 | 554 | switch (cmd) { 555 | case FATFS_IOCTL_GET_BOOT_SECTOR_DATA: 556 | rc = disk_read(sf->fil.fs->drv, (BYTE *)data, 0, 1); 557 | break; 558 | case FATFS_IOCTL_GET_FD_LBA: 559 | { 560 | DWORD lba = clust2sect(sf->fil.fs, sf->fil.sclust); 561 | 562 | if (lba > 0) { 563 | *(uint32_t *)data = lba; 564 | rc = RES_OK; 565 | } 566 | else { 567 | rc = RES_ERROR; 568 | } 569 | 570 | break; 571 | } 572 | case FATFS_IOCTL_GET_FD_LINK_MAP: 573 | { 574 | if (fat_create_linkmap(sf) == FR_OK) { 575 | memcpy(data, sf->fil.cltbl, sf->fil.cltbl[0] * sizeof(DWORD)); 576 | } 577 | else { 578 | memset(data, 0, sizeof(DWORD)); 579 | } 580 | break; 581 | } 582 | default: 583 | rc = disk_ioctl(sf->fil.fs->drv, (BYTE)cmd, data); 584 | break; 585 | } 586 | 587 | return rc == RES_OK ? 0 : -1; 588 | } 589 | 590 | 591 | #define FAT_GET_MNT() \ 592 | FRESULT rc = FR_OK; \ 593 | fatfs_mnt_t *mnt; \ 594 | FAT_LOCK_SCOPED(); \ 595 | mnt = (fatfs_mnt_t*)vfs->privdata; \ 596 | if (mnt == NULL) \ 597 | goto error; \ 598 | if (f_chdrive(mnt->dev_path) != FR_OK) \ 599 | goto error 600 | 601 | 602 | static int fat_rename(struct vfs_handler *vfs, const char *fn1, const char *fn2) { 603 | FAT_GET_MNT(); 604 | 605 | if ((rc = f_rename((const TCHAR*)fn1, (const TCHAR*)fn2)) != FR_OK) { 606 | goto error; 607 | } 608 | 609 | return 0; 610 | 611 | error: 612 | fatfs_set_errno(rc); 613 | put_rc(rc, __func__); 614 | return -1; 615 | } 616 | 617 | static int fat_unlink(struct vfs_handler *vfs, const char *fn) { 618 | FAT_GET_MNT(); 619 | 620 | if ((rc = f_unlink((const TCHAR *)fn)) != FR_OK) { 621 | goto error; 622 | } 623 | 624 | return 0; 625 | 626 | error: 627 | fatfs_set_errno(rc); 628 | put_rc(rc, __func__); 629 | return -1; 630 | } 631 | 632 | static void *fat_mmap(void * hnd) { 633 | uint8_t *data = NULL; 634 | int size = 0; 635 | int cnt = 0; 636 | 637 | size = fat_total(hnd); 638 | DBG((DBG_DEBUG, "FATFS: Mmap %d\n", size)); 639 | 640 | if (size) { 641 | 642 | data = (uint8_t *) memalign(32, size); 643 | cnt = fat_read(hnd, data, size); 644 | 645 | if (cnt != size) { 646 | free(data); 647 | return NULL; 648 | } 649 | 650 | return (void*) data; 651 | } 652 | 653 | return NULL; 654 | } 655 | 656 | static int fat_complete(void *hnd, ssize_t *rv) { 657 | FRESULT rc; 658 | (void)rv; 659 | 660 | FAT_GET_HND(hnd, -1); 661 | 662 | DBG((DBG_DEBUG, "FATFS: fs_complete\n")); 663 | 664 | if ((rc = f_sync(&sf->fil)) != FR_OK) { 665 | goto error; 666 | } 667 | 668 | return 0; 669 | 670 | error: 671 | fatfs_set_errno(rc); 672 | put_rc(rc, __func__); 673 | return -1; 674 | } 675 | 676 | static int fat_mkdir(struct vfs_handler *vfs, const char *fn) { 677 | FAT_GET_MNT(); 678 | 679 | if ((rc = f_mkdir((const TCHAR *)fn)) != FR_OK) { 680 | goto error; 681 | } 682 | 683 | return 0; 684 | 685 | error: 686 | fatfs_set_errno(rc); 687 | put_rc(rc, __func__); 688 | return -1; 689 | } 690 | 691 | static int fat_rmdir(struct vfs_handler *vfs, const char *fn) { 692 | FAT_GET_MNT(); 693 | 694 | if ((rc = f_unlink((const TCHAR *)fn)) != FR_OK) { 695 | goto error; 696 | } 697 | 698 | return 0; 699 | 700 | error: 701 | fatfs_set_errno(rc); 702 | put_rc(rc, __func__); 703 | return -1; 704 | } 705 | 706 | static int fat_fcntl(void *hnd, int cmd, va_list ap) { 707 | int rv = -1; 708 | (void)ap; 709 | 710 | FAT_GET_HND(hnd, -1); 711 | 712 | switch (cmd) { 713 | case F_GETFL: 714 | rv = sf->mode; 715 | break; 716 | 717 | case F_SETFL: 718 | case F_GETFD: 719 | case F_SETFD: 720 | rv = 0; 721 | break; 722 | default: 723 | errno = EINVAL; 724 | } 725 | 726 | return rv; 727 | } 728 | 729 | static int fat_stat(struct vfs_handler *vfs, const char *path, struct stat *st, int flag) { 730 | FILINFO inf; 731 | FAT_GET_MNT(); 732 | size_t len = strlen(path); 733 | (void)flag; 734 | 735 | memset(st, 0, sizeof(struct stat)); 736 | st->st_dev = (dev_t)((ptr_t)vfs); 737 | st->st_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH; 738 | st->st_nlink = 1; 739 | 740 | /* Root directory */ 741 | if (len == 0 || (len == 1 && *path == '/')) { 742 | st->st_mode |= S_IFDIR; 743 | st->st_size = -1; 744 | return 0; 745 | } 746 | 747 | if ((rc = f_stat((const TCHAR*)path, &inf)) != FR_OK) { 748 | goto error; 749 | } 750 | 751 | // FIXME 752 | st->st_atime = inf.fdate + inf.ftime; 753 | st->st_mtime = inf.fdate + inf.ftime; 754 | st->st_ctime = inf.fdate + inf.ftime; 755 | 756 | if (inf.fattrib & AM_DIR) { 757 | st->st_mode |= S_IFDIR; 758 | st->st_size = -1; 759 | } 760 | else { 761 | st->st_mode |= S_IFREG; 762 | st->st_size = inf.fsize; 763 | st->st_blksize = 1 << mnt->dev->l_block_size; 764 | st->st_blocks = inf.fsize >> mnt->dev->l_block_size; 765 | 766 | if (inf.fsize & (st->st_blksize - 1)) { 767 | ++st->st_blocks; 768 | } 769 | } 770 | return 0; 771 | 772 | error: 773 | fatfs_set_errno(rc); 774 | put_rc(rc, __func__); 775 | return -1; 776 | } 777 | 778 | 779 | static int fat_fstat(void *hnd, struct stat *st) { 780 | FAT_GET_HND(hnd, -1); 781 | memset(st, 0, sizeof(struct stat)); 782 | 783 | st->st_nlink = 1; 784 | st->st_blksize = 1 << sf->mnt->dev->l_block_size; 785 | st->st_dev = (dev_t)((ptr_t)sf->mnt->dev); 786 | st->st_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH; 787 | 788 | if (sf->type == STAT_TYPE_DIR) { 789 | st->st_mode |= S_IFDIR; 790 | st->st_size = -1; 791 | } 792 | else { 793 | st->st_mode |= S_IFREG; 794 | st->st_size = sf->fil.fsize; 795 | st->st_blocks = sf->fil.fsize >> sf->mnt->dev->l_block_size; 796 | 797 | if (sf->fil.fsize & (st->st_blksize - 1)) { 798 | ++st->st_blocks; 799 | } 800 | } 801 | return 0; 802 | } 803 | 804 | #define FAT_GET_MOUNT() \ 805 | fatfs_mnt_t *mnt = NULL; \ 806 | if (pdrv < MAX_FAT_MOUNTS && fat_mnt[pdrv].dev != NULL) { \ 807 | mnt = &fat_mnt[pdrv]; \ 808 | } \ 809 | else { \ 810 | DBG((DBG_ERROR, "FATFS: %s[%d] pdrv error\n", __func__, pdrv)); \ 811 | return STA_NOINIT; \ 812 | } 813 | 814 | DSTATUS disk_initialize ( 815 | BYTE pdrv /* Physical drive nmuber (0..) */ 816 | ) { 817 | FAT_GET_MOUNT(); 818 | 819 | if (mnt->dev->init(mnt->dev) < 0) { 820 | mnt->dev_stat |= STA_NOINIT; 821 | } 822 | else { 823 | mnt->dev_stat &= ~STA_NOINIT; 824 | } 825 | 826 | if (mnt->dev_dma) { 827 | if (mnt->dev_dma->init(mnt->dev_dma) < 0) { 828 | mnt->dev_stat |= STA_NOINIT; 829 | } 830 | } 831 | 832 | DBG((DBG_DEBUG, "FATFS: %s[%d] 0x%02x\n", __func__, pdrv, mnt->dev_stat)); 833 | return mnt->dev_stat; 834 | } 835 | 836 | 837 | /*-----------------------------------------------------------------------*/ 838 | /* Get Disk Status */ 839 | /*-----------------------------------------------------------------------*/ 840 | 841 | DSTATUS disk_status ( 842 | BYTE pdrv /* Physical drive nmuber (0..) */ 843 | ) { 844 | FAT_GET_MOUNT(); 845 | // DBG((DBG_DEBUG, "FATFS: %s[%d] 0x%02x\n", __func__, pdrv, mnt->dev_stat)); 846 | return mnt->dev_stat; 847 | } 848 | 849 | 850 | /*-----------------------------------------------------------------------*/ 851 | /* Read Sector(s) */ 852 | /*-----------------------------------------------------------------------*/ 853 | 854 | DRESULT disk_read ( 855 | BYTE pdrv, /* Physical drive nmuber (0..) */ 856 | BYTE *buff, /* Data buffer to store read data */ 857 | DWORD sector, /* Sector address (LBA) */ 858 | UINT count /* Number of sectors to read */ 859 | ) { 860 | FAT_GET_MOUNT(); 861 | uint8_t *dest = buff; 862 | kos_blockdev_t *dev = mnt->dev; 863 | int rv; 864 | 865 | if (count > 1 && mnt->dev_dma) { 866 | if (((uintptr_t)buff & 31) == 0) { 867 | dev = mnt->dev_dma; 868 | } 869 | #ifdef FATFS_USE_DMA_BUF 870 | else if (count <= mnt->fs->csize) { 871 | dest = mnt->dmabuf; 872 | dev = mnt->dev_dma; 873 | } 874 | #endif 875 | } 876 | 877 | DBG((DBG_DEBUG, "FATFS: %s[%d] %s %ld %d 0x%08lx 0x%08lx\n", 878 | __func__, pdrv, (dev == mnt->dev_dma ? "dma" : "pio"), 879 | sector, (int)count, (uintptr_t)buff, (uintptr_t)dest)); 880 | 881 | rv = dev->read_blocks(dev, sector, count, dest); 882 | 883 | #ifdef FATFS_USE_DMA_BUF 884 | if (dest != buff) { 885 | memcpy(buff, dest, count << dev->l_block_size); 886 | } 887 | #endif 888 | 889 | if (rv < 0) { 890 | DBG((DBG_ERROR, "FATFS: %s[%d] %s error: %d\n", 891 | __func__, pdrv, (dev == mnt->dev_dma ? "dma" : "pio"), errno)); 892 | return (errno == EOVERFLOW ? RES_PARERR : RES_ERROR); 893 | } 894 | return RES_OK; 895 | } 896 | 897 | 898 | /*-----------------------------------------------------------------------*/ 899 | /* Write Sector(s) */ 900 | /*-----------------------------------------------------------------------*/ 901 | 902 | #if _USE_WRITE 903 | DRESULT disk_write ( 904 | BYTE pdrv, /* Physical drive nmuber (0..) */ 905 | const BYTE *buff, /* Data to be written */ 906 | DWORD sector, /* Sector address (LBA) */ 907 | UINT count /* Number of sectors to write */ 908 | ) { 909 | FAT_GET_MOUNT(); 910 | uint8_t *src = (uint8_t *)buff; 911 | kos_blockdev_t *dev = mnt->dev; 912 | int rv; 913 | 914 | if (count > 1 && mnt->dev_dma) { 915 | if (((uintptr_t)buff & 31) == 0) { 916 | dev = mnt->dev_dma; 917 | } 918 | #ifdef FATFS_USE_DMA_BUF 919 | else if (count <= mnt->fs->csize) { 920 | src = mnt->dmabuf; 921 | dev = mnt->dev_dma; 922 | memcpy(src, buff, count << dev->l_block_size); 923 | } 924 | #endif 925 | } 926 | 927 | DBG((DBG_DEBUG, "FATFS: %s[%d] %s %ld %d 0x%08lx 0x%08lx\n", 928 | __func__, pdrv, (dev == mnt->dev_dma ? "dma" : "pio"), 929 | sector, (int)count, (uintptr_t)buff, (uintptr_t)src)); 930 | 931 | rv = dev->write_blocks(dev, sector, count, src); 932 | 933 | if (rv < 0) { 934 | DBG((DBG_ERROR, "FATFS: %s[%d] %s error: %d\n", 935 | __func__, pdrv, 936 | (dev == mnt->dev_dma ? "dma" : "pio"), 937 | errno)); 938 | return errno == EOVERFLOW ? RES_PARERR : RES_ERROR; 939 | } 940 | return RES_OK; 941 | } 942 | #endif 943 | 944 | 945 | /*-----------------------------------------------------------------------*/ 946 | /* Miscellaneous Functions */ 947 | /*-----------------------------------------------------------------------*/ 948 | 949 | #if _USE_IOCTL 950 | DRESULT disk_ioctl ( 951 | BYTE pdrv, /* Physical drive nmuber (0..) */ 952 | BYTE cmd, /* Control code */ 953 | void *buff /* Buffer to send/receive control data */ 954 | ) { 955 | FAT_GET_MOUNT(); 956 | 957 | switch (cmd) { 958 | case CTRL_SYNC: 959 | mnt->dev->flush(mnt->dev); 960 | DBG((DBG_DEBUG, "FATFS: %s[%d] Sync\n", __func__, pdrv)); 961 | return RES_OK; 962 | case GET_SECTOR_COUNT: 963 | *(ulong*)buff = mnt->dev->count_blocks(mnt->dev); 964 | DBG((DBG_DEBUG, "FATFS: %s[%d] Sector count: %d\n", __func__, pdrv, *(ushort*)buff)); 965 | return RES_OK; 966 | case GET_SECTOR_SIZE: 967 | *(ushort*)buff = (1 << mnt->dev->l_block_size); 968 | DBG((DBG_DEBUG, "FATFS: %s[%d] Sector size: %d\n", __func__, pdrv, *(ushort*)buff)); 969 | return RES_OK; 970 | case GET_BLOCK_SIZE: 971 | *(ushort*)buff = (1 << mnt->dev->l_block_size); 972 | DBG((DBG_DEBUG, "FATFS: %s[%d] Block size: %d\n", __func__, pdrv, *(ushort*)buff)); 973 | return RES_OK; 974 | case CTRL_TRIM: 975 | DBG((DBG_DEBUG, "FATFS: %s[%d] Trim sector\n", __func__, pdrv)); 976 | return RES_OK; 977 | default: 978 | DBG((DBG_ERROR, "FATFS: %s[%d] Unknown control code: %d\n", __func__, pdrv, cmd)); 979 | return RES_PARERR; 980 | } 981 | } 982 | #endif 983 | 984 | DWORD get_fattime() { 985 | struct tm *time; 986 | time_t unix_time; 987 | DWORD tmr = 0; 988 | 989 | unix_time = rtc_unix_secs(); 990 | time = gmtime(&unix_time); 991 | 992 | if (time != NULL) { 993 | tmr = (((DWORD)(time->tm_year - 80)) << 25) /* tm_year is years since 1900; FAT starts from 1980 */ 994 | | ((DWORD)(time->tm_mon + 1) << 21) /* tm_mon ranges from 0 to 11; add 1 for FAT */ 995 | | ((DWORD)(time->tm_mday) << 16) /* tm_mday ranges from 1 to 31 */ 996 | | ((DWORD)(time->tm_hour) << 11) /* tm_hour ranges from 0 to 23 */ 997 | | ((DWORD)(time->tm_min) << 5) /* tm_min ranges from 0 to 59 */ 998 | | ((DWORD)(time->tm_sec / 2)); /* tm_sec ranges from 0 to 59; FAT stores seconds in 2-second steps */ 999 | } 1000 | 1001 | return tmr; 1002 | } 1003 | 1004 | /* This is a template that will be used for each mount */ 1005 | static vfs_handler_t vh = { 1006 | /* Name Handler */ 1007 | { 1008 | { 0 }, /* name */ 1009 | 0, /* in-kernel */ 1010 | 0x00010000, /* Version 1.0 */ 1011 | NMMGR_FLAGS_NEEDSFREE, /* We malloc each VFS struct */ 1012 | NMMGR_TYPE_VFS, /* VFS handler */ 1013 | NMMGR_LIST_INIT /* list */ 1014 | }, 1015 | 0, NULL, /* no cacheing, privdata */ 1016 | fat_open, /* open */ 1017 | fat_close, /* close */ 1018 | fat_read, /* read */ 1019 | fat_write, /* write */ 1020 | fat_seek, /* seek */ 1021 | fat_tell, /* tell */ 1022 | fat_total, /* total */ 1023 | fat_readdir, /* readdir */ 1024 | fat_ioctl, /* ioctl */ 1025 | fat_rename, /* rename */ 1026 | fat_unlink, /* unlink */ 1027 | fat_mmap, /* mmap */ 1028 | fat_complete, /* complete */ 1029 | fat_stat, /* stat */ 1030 | fat_mkdir, /* mkdir */ 1031 | fat_rmdir, /* rmdir */ 1032 | fat_fcntl, /* fcntl */ 1033 | NULL, /* poll */ 1034 | NULL, /* link */ 1035 | NULL, /* symlink */ 1036 | NULL, /* seek64 */ 1037 | NULL, /* tell64 */ 1038 | NULL, /* total64 */ 1039 | NULL, /* readlink */ 1040 | fat_rewinddir, /* rewinddir */ 1041 | fat_fstat /* fstat */ 1042 | }; 1043 | 1044 | static void fs_fat_free(fatfs_mnt_t *mnt) { 1045 | if (mnt == NULL) { 1046 | return; 1047 | } 1048 | if (mnt->vfsh) { 1049 | free(mnt->vfsh); 1050 | } 1051 | if (mnt->fs) { 1052 | free(mnt->fs); 1053 | } 1054 | if (mnt->dev) { 1055 | mnt->dev->shutdown(mnt->dev); 1056 | } 1057 | if (mnt->dev_dma) { 1058 | mnt->dev_dma->shutdown(mnt->dev_dma); 1059 | } 1060 | #ifdef FATFS_USE_DMA_BUF 1061 | if (mnt->dmabuf) { 1062 | free(mnt->dmabuf); 1063 | } 1064 | #endif 1065 | memset(mnt, 0, sizeof(fatfs_mnt_t)); 1066 | } 1067 | 1068 | int fs_fat_mount(const char *mp, kos_blockdev_t *dev_pio, kos_blockdev_t *dev_dma, int partition) { 1069 | 1070 | fatfs_mnt_t *mnt = NULL; 1071 | FRESULT rc; 1072 | int i; 1073 | 1074 | if (!initted) { 1075 | return -1; 1076 | } 1077 | 1078 | FAT_LOCK_SCOPED(); 1079 | 1080 | for (i = 0; i < MAX_FAT_MOUNTS; ++i) { 1081 | if (fat_mnt[i].dev == NULL) { 1082 | mnt = &fat_mnt[i]; 1083 | memset(mnt, 0, sizeof(fatfs_mnt_t)); 1084 | mnt->dev_id = i; 1085 | DBG((DBG_DEBUG, "FATFS: Mounting device %d to %s\n", mnt->dev_id, mp)); 1086 | break; 1087 | } 1088 | } 1089 | 1090 | if (mnt == NULL) { 1091 | dash_log(DBG_ERROR, "FATFS: The maximum number of mounts exceeded.\n"); 1092 | goto error; 1093 | } 1094 | 1095 | if (dev_pio->init(dev_pio) < 0) { 1096 | dash_log(DBG_ERROR, "FATFS: Can't initialize block device for PIO: %d\n", errno); 1097 | goto error; 1098 | } 1099 | 1100 | mnt->dev = dev_pio; 1101 | mnt->dev_dma = dev_dma; 1102 | 1103 | if (dev_dma && dev_dma->init(dev_dma) < 0) { 1104 | dash_log(DBG_ERROR, "FATFS: Can't initialize block device for DMA: %d\n", errno); 1105 | mnt->dev_dma = NULL; 1106 | } 1107 | 1108 | VolToPart[mnt->dev_id].pd = mnt->dev_id; 1109 | VolToPart[mnt->dev_id].pt = partition + 1; 1110 | 1111 | /* Create a VFS structure */ 1112 | if (!(mnt->vfsh = (vfs_handler_t *)malloc(sizeof(vfs_handler_t)))) { 1113 | dash_log(DBG_ERROR, "FATFS: Out of memory for creating vfs handler\n"); 1114 | goto error; 1115 | } 1116 | 1117 | memcpy(mnt->vfsh, &vh, sizeof(vfs_handler_t)); 1118 | strcpy(mnt->vfsh->nmmgr.pathname, mp); 1119 | mnt->vfsh->privdata = mnt; 1120 | 1121 | /* Create a FATFS structure */ 1122 | if (!(mnt->fs = (FATFS *)malloc(sizeof(FATFS)))) { 1123 | dash_log(DBG_ERROR, "FATFS: Out of memory for creating FATFS native mount structure\n"); 1124 | goto error; 1125 | } 1126 | 1127 | snprintf((TCHAR *)mnt->dev_path, sizeof(mnt->dev_path), "%d:", mnt->dev_id); 1128 | rc = f_mount(mnt->fs, mnt->dev_path, 1); 1129 | 1130 | if (rc != FR_OK) { 1131 | fatfs_set_errno(rc); 1132 | dash_log(DBG_ERROR, "FATFS: Error %d in mounting a logical drive %d\n", errno, mnt->dev_id); 1133 | #ifdef FATFS_DEBUG 1134 | put_rc(rc, __func__); 1135 | #endif 1136 | goto error; 1137 | } 1138 | 1139 | uint32_t sect_size = (1 << mnt->dev->l_block_size); 1140 | 1141 | #ifdef FATFS_USE_DMA_BUF 1142 | if (mnt->dev_dma) { 1143 | DBG((DBG_DEBUG, "FATFS: Allocating %d bytes for DMA buffer\n", mnt->fs->csize * sect_size)); 1144 | if (!(mnt->dmabuf = (uint8_t *)memalign(32, mnt->fs->csize * sect_size))) { 1145 | dash_log(DBG_ERROR, "FATFS: Out of memory for DMA buffer\n"); 1146 | } 1147 | else { 1148 | DBG((DBG_DEBUG, "FATFS: Allocated %d bytes for DMA buffer at %p\n", 1149 | mnt->fs->csize * sect_size, mnt->dmabuf)); 1150 | } 1151 | } 1152 | #endif 1153 | 1154 | FATFS *fs; 1155 | DWORD fre_clust; 1156 | uint64_t fre_sect, tot_sect; 1157 | rc = f_getfree(mnt->dev_path, &fre_clust, &fs); 1158 | 1159 | /* Get total sectors and free sectors */ 1160 | tot_sect = mnt->dev->count_blocks(mnt->dev); 1161 | fre_sect = fre_clust * fs->csize; 1162 | 1163 | if (rc == FR_OK) { 1164 | dash_log(DBG_DEBUG, "FATFS: %lu MB total, %lu MB free.\n", 1165 | (uint32_t)((tot_sect * sect_size) / 1024 / 1024), 1166 | (uint32_t)((fre_sect * sect_size) / 1024 / 1024)); 1167 | } 1168 | 1169 | DBG((DBG_DEBUG, "FATFS: FAT start sector: %ld\n", mnt->fs->fatbase)); 1170 | DBG((DBG_DEBUG, "FATFS: Data start sector: %ld\n", mnt->fs->database)); 1171 | DBG((DBG_DEBUG, "FATFS: Root directory start sector: %ld\n", mnt->fs->dirbase * mnt->fs->csize)); 1172 | 1173 | /* Register with the VFS */ 1174 | if (nmmgr_handler_add(&mnt->vfsh->nmmgr)) { 1175 | dash_log(DBG_ERROR, "FATFS: Couldn't add vfs to nmmgr\n"); 1176 | goto error; 1177 | } 1178 | 1179 | return 0; 1180 | 1181 | error: 1182 | fs_fat_free(mnt); 1183 | return -1; 1184 | } 1185 | 1186 | 1187 | int fs_fat_unmount(const char *mp) { 1188 | fatfs_mnt_t *mnt; 1189 | int found = 0, rv = 0, i; 1190 | 1191 | FAT_LOCK_SCOPED(); 1192 | 1193 | for (i = 0; i < MAX_FAT_MOUNTS; i++) { 1194 | if (fat_mnt[i].vfsh != NULL && !strcmp(mp, fat_mnt[i].vfsh->nmmgr.pathname)) { 1195 | mnt = &fat_mnt[i]; 1196 | found = 1; 1197 | break; 1198 | } 1199 | } 1200 | 1201 | if (found) { 1202 | nmmgr_handler_remove(&mnt->vfsh->nmmgr); 1203 | fs_fat_free(mnt); 1204 | } 1205 | else { 1206 | errno = ENOENT; 1207 | rv = -1; 1208 | } 1209 | return rv; 1210 | } 1211 | 1212 | 1213 | int fs_fat_is_mounted(const char *mp) { 1214 | int i, found = 0; 1215 | 1216 | FAT_LOCK_SCOPED(); 1217 | 1218 | for (i = 0; i < MAX_FAT_MOUNTS; ++i) { 1219 | if (fat_mnt[i].vfsh != NULL && !strcmp(mp, fat_mnt[i].vfsh->nmmgr.pathname)) { 1220 | found = i + 1; 1221 | break; 1222 | } 1223 | } 1224 | 1225 | return found; 1226 | } 1227 | 1228 | 1229 | int fs_fat_init(void) { 1230 | if (initted) { 1231 | return 0; 1232 | } 1233 | /* Reset mounts */ 1234 | memset(fat_mnt, 0, sizeof(fat_mnt)); 1235 | 1236 | /* Reset fd's */ 1237 | memset(fh, 0, sizeof(fh)); 1238 | 1239 | initted = 1; 1240 | return 0; 1241 | } 1242 | 1243 | int fs_fat_shutdown(void) { 1244 | int i; 1245 | fatfs_mnt_t *mnt; 1246 | 1247 | if (!initted) { 1248 | return 0; 1249 | } 1250 | 1251 | /* Clean up SD and IDE resources */ 1252 | fs_fat_unmount_sd(); 1253 | fs_fat_unmount_ide(); 1254 | 1255 | for (i = 0; i < MAX_FAT_MOUNTS; ++i) { 1256 | 1257 | if (fat_mnt[i].dev != NULL) { 1258 | 1259 | mnt = &fat_mnt[i]; 1260 | nmmgr_handler_remove(&mnt->vfsh->nmmgr); 1261 | fs_fat_free(mnt); 1262 | } 1263 | } 1264 | initted = 0; 1265 | return 0; 1266 | } 1267 | --------------------------------------------------------------------------------