1 /**
2  * @file lv_fs_win32.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../../lvgl.h"
10 #if LV_USE_FS_WIN32 != '\0'
11 
12 #include <windows.h>
13 #include <stdio.h>
14 
15 /*********************
16  *      DEFINES
17  *********************/
18 #define MAX_PATH_LEN 256
19 
20 /**********************
21  *      TYPEDEFS
22  **********************/
23 typedef struct {
24     HANDLE dir_p;
25     char next_fn[MAX_PATH_LEN];
26     lv_fs_res_t next_error;
27 } dir_handle_t;
28 
29 /**********************
30  *  STATIC PROTOTYPES
31  **********************/
32 
33 static bool is_dots_name(const char * name);
34 static lv_fs_res_t fs_error_from_win32(DWORD error);
35 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
36 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
37 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
38 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
39 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
40 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
41 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
42 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
43 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
44 
45 /**********************
46  *  STATIC VARIABLES
47  **********************/
48 
49 /**********************
50  *      MACROS
51  **********************/
52 
53 /**********************
54  *   GLOBAL FUNCTIONS
55  **********************/
56 
57 /**
58  * Register a driver for the File system interface
59  */
lv_fs_win32_init(void)60 void lv_fs_win32_init(void)
61 {
62     /*---------------------------------------------------
63      * Register the file system interface in LVGL
64      *--------------------------------------------------*/
65 
66     /*Add a simple driver to open images*/
67     static lv_fs_drv_t fs_drv; /*A driver descriptor*/
68     lv_fs_drv_init(&fs_drv);
69 
70     /*Set up fields...*/
71     fs_drv.letter = LV_FS_WIN32_LETTER;
72     fs_drv.cache_size = LV_FS_WIN32_CACHE_SIZE;
73 
74     fs_drv.open_cb = fs_open;
75     fs_drv.close_cb = fs_close;
76     fs_drv.read_cb = fs_read;
77     fs_drv.write_cb = fs_write;
78     fs_drv.seek_cb = fs_seek;
79     fs_drv.tell_cb = fs_tell;
80 
81     fs_drv.dir_close_cb = fs_dir_close;
82     fs_drv.dir_open_cb = fs_dir_open;
83     fs_drv.dir_read_cb = fs_dir_read;
84 
85     lv_fs_drv_register(&fs_drv);
86 }
87 
88 /**********************
89  *   STATIC FUNCTIONS
90  **********************/
91 
92 /**
93  * Check the dots name
94  * @param name file or dir name
95  * @return true if the name is dots name
96  */
is_dots_name(const char * name)97 static bool is_dots_name(const char * name)
98 {
99     return name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2]));
100 }
101 
102 /**
103  * Convert Win32 error code to error from lv_fs_res_t enum
104  * @param error Win32 error code
105  * @return LV_FS_RES_OK: no error, the file is read
106  *         any error from lv_fs_res_t enum
107  */
fs_error_from_win32(DWORD error)108 static lv_fs_res_t fs_error_from_win32(DWORD error)
109 {
110     lv_fs_res_t res;
111 
112     switch(error) {
113         case ERROR_SUCCESS:
114             res = LV_FS_RES_OK;
115             break;
116         case ERROR_BAD_UNIT:
117         case ERROR_NOT_READY:
118         case ERROR_CRC:
119         case ERROR_SEEK:
120         case ERROR_NOT_DOS_DISK:
121         case ERROR_WRITE_FAULT:
122         case ERROR_READ_FAULT:
123         case ERROR_GEN_FAILURE:
124         case ERROR_WRONG_DISK:
125             res = LV_FS_RES_HW_ERR;
126             break;
127         case ERROR_INVALID_HANDLE:
128         case ERROR_INVALID_TARGET_HANDLE:
129             res = LV_FS_RES_FS_ERR;
130             break;
131         case ERROR_FILE_NOT_FOUND:
132         case ERROR_PATH_NOT_FOUND:
133         case ERROR_INVALID_DRIVE:
134         case ERROR_NO_MORE_FILES:
135         case ERROR_SECTOR_NOT_FOUND:
136         case ERROR_BAD_NETPATH:
137         case ERROR_BAD_NET_NAME:
138         case ERROR_BAD_PATHNAME:
139         case ERROR_FILENAME_EXCED_RANGE:
140             res = LV_FS_RES_NOT_EX;
141             break;
142         case ERROR_DISK_FULL:
143             res = LV_FS_RES_FULL;
144             break;
145         case ERROR_SHARING_VIOLATION:
146         case ERROR_LOCK_VIOLATION:
147         case ERROR_DRIVE_LOCKED:
148             res = LV_FS_RES_LOCKED;
149             break;
150         case ERROR_ACCESS_DENIED:
151         case ERROR_CURRENT_DIRECTORY:
152         case ERROR_WRITE_PROTECT:
153         case ERROR_NETWORK_ACCESS_DENIED:
154         case ERROR_CANNOT_MAKE:
155         case ERROR_FAIL_I24:
156         case ERROR_SEEK_ON_DEVICE:
157         case ERROR_NOT_LOCKED:
158         case ERROR_LOCK_FAILED:
159             res = LV_FS_RES_DENIED;
160             break;
161         case ERROR_BUSY:
162             res = LV_FS_RES_BUSY;
163             break;
164         case ERROR_TIMEOUT:
165             res = LV_FS_RES_TOUT;
166             break;
167         case ERROR_NOT_SAME_DEVICE:
168         case ERROR_DIRECT_ACCESS_HANDLE:
169             res = LV_FS_RES_NOT_IMP;
170             break;
171         case ERROR_TOO_MANY_OPEN_FILES:
172         case ERROR_ARENA_TRASHED:
173         case ERROR_NOT_ENOUGH_MEMORY:
174         case ERROR_INVALID_BLOCK:
175         case ERROR_OUT_OF_PAPER:
176         case ERROR_SHARING_BUFFER_EXCEEDED:
177         case ERROR_NOT_ENOUGH_QUOTA:
178             res = LV_FS_RES_OUT_OF_MEM;
179             break;
180         case ERROR_INVALID_FUNCTION:
181         case ERROR_INVALID_ACCESS:
182         case ERROR_INVALID_DATA:
183         case ERROR_BAD_COMMAND:
184         case ERROR_BAD_LENGTH:
185         case ERROR_INVALID_PARAMETER:
186         case ERROR_NEGATIVE_SEEK:
187             res = LV_FS_RES_INV_PARAM;
188             break;
189         default:
190             res = LV_FS_RES_UNKNOWN;
191             break;
192     }
193 
194     return res;
195 }
196 
197 /**
198  * Open a file
199  * @param drv pointer to a driver where this function belongs
200  * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
201  * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
202  * @return pointer to FIL struct or NULL in case of fail
203  */
fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)204 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
205 {
206     LV_UNUSED(drv);
207 
208     DWORD desired_access = 0;
209 
210     if(mode & LV_FS_MODE_RD) {
211         desired_access |= GENERIC_READ;
212     }
213 
214     if(mode & LV_FS_MODE_WR) {
215         desired_access |= GENERIC_WRITE;
216     }
217 
218     /*Make the path relative to the current directory (the projects root folder)*/
219 
220     char buf[MAX_PATH];
221     lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s", path);
222 
223     return (void *)CreateFileA(
224                buf,
225                desired_access,
226                FILE_SHARE_READ,
227                NULL,
228                OPEN_EXISTING,
229                FILE_ATTRIBUTE_NORMAL,
230                NULL);
231 }
232 
233 /**
234  * Close an opened file
235  * @param drv pointer to a driver where this function belongs
236  * @param file_p pointer to a FILE variable. (opened with fs_open)
237  * @return LV_FS_RES_OK: no error, the file is read
238  *         any error from lv_fs_res_t enum
239  */
fs_close(lv_fs_drv_t * drv,void * file_p)240 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
241 {
242     LV_UNUSED(drv);
243     return CloseHandle((HANDLE)file_p)
244            ? LV_FS_RES_OK
245            : fs_error_from_win32(GetLastError());
246 }
247 
248 /**
249  * Read data from an opened file
250  * @param drv pointer to a driver where this function belongs
251  * @param file_p pointer to a FILE variable.
252  * @param buf pointer to a memory block where to store the read data
253  * @param btr number of Bytes To Read
254  * @param br the real number of read bytes (Byte Read)
255  * @return LV_FS_RES_OK: no error, the file is read
256  *         any error from lv_fs_res_t enum
257  */
fs_read(lv_fs_drv_t * drv,void * file_p,void * buf,uint32_t btr,uint32_t * br)258 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
259 {
260     LV_UNUSED(drv);
261     return ReadFile((HANDLE)file_p, buf, btr, (LPDWORD)br, NULL)
262            ? LV_FS_RES_OK
263            : fs_error_from_win32(GetLastError());
264 }
265 
266 /**
267  * Write into a file
268  * @param drv pointer to a driver where this function belongs
269  * @param file_p pointer to a FILE variable
270  * @param buf pointer to a buffer with the bytes to write
271  * @param btw Bytes To Write
272  * @param bw the number of real written bytes (Bytes Written). NULL if unused.
273  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
274  */
fs_write(lv_fs_drv_t * drv,void * file_p,const void * buf,uint32_t btw,uint32_t * bw)275 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
276 {
277     LV_UNUSED(drv);
278     return WriteFile((HANDLE)file_p, buf, btw, (LPDWORD)bw, NULL)
279            ? LV_FS_RES_OK
280            : fs_error_from_win32(GetLastError());
281 }
282 
283 /**
284  * Set the read write pointer. Also expand the file size if necessary.
285  * @param drv pointer to a driver where this function belongs
286  * @param file_p pointer to a FILE variable. (opened with fs_open )
287  * @param pos the new position of read write pointer
288  * @return LV_FS_RES_OK: no error, the file is read
289  *         any error from lv_fs_res_t enum
290  */
fs_seek(lv_fs_drv_t * drv,void * file_p,uint32_t pos,lv_fs_whence_t whence)291 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
292 {
293     LV_UNUSED(drv);
294 
295     DWORD move_method = (DWORD) -1;
296     if(whence == LV_FS_SEEK_SET) {
297         move_method = FILE_BEGIN;
298     }
299     else if(whence == LV_FS_SEEK_CUR) {
300         move_method = FILE_CURRENT;
301     }
302     else if(whence == LV_FS_SEEK_END) {
303         move_method = FILE_END;
304     }
305 
306     LARGE_INTEGER distance_to_move;
307     distance_to_move.QuadPart = pos;
308     return SetFilePointerEx((HANDLE)file_p, distance_to_move, NULL, move_method)
309            ? LV_FS_RES_OK
310            : fs_error_from_win32(GetLastError());
311 }
312 
313 /**
314  * Give the position of the read write pointer
315  * @param drv pointer to a driver where this function belongs
316  * @param file_p pointer to a FILE variable.
317  * @param pos_p pointer to to store the result
318  * @return LV_FS_RES_OK: no error, the file is read
319  *         any error from lv_fs_res_t enum
320  */
fs_tell(lv_fs_drv_t * drv,void * file_p,uint32_t * pos_p)321 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
322 {
323     LV_UNUSED(drv);
324 
325     if(!pos_p) {
326         return LV_FS_RES_INV_PARAM;
327     }
328 
329     LARGE_INTEGER file_pointer;
330     file_pointer.QuadPart = 0;
331 
332     LARGE_INTEGER distance_to_move;
333     distance_to_move.QuadPart = 0;
334     if(SetFilePointerEx(
335            (HANDLE)file_p,
336            distance_to_move,
337            &file_pointer,
338            FILE_CURRENT)) {
339         if(file_pointer.QuadPart > LONG_MAX) {
340             return LV_FS_RES_INV_PARAM;
341         }
342         else {
343             *pos_p = file_pointer.LowPart;
344             return LV_FS_RES_OK;
345         }
346     }
347     else {
348         return fs_error_from_win32(GetLastError());
349     }
350 }
351 
352 /**
353  * Initialize a 'DIR' or 'HANDLE' variable for directory reading
354  * @param drv pointer to a driver where this function belongs
355  * @param path path to a directory
356  * @return pointer to an initialized 'DIR' or 'HANDLE' variable
357  */
fs_dir_open(lv_fs_drv_t * drv,const char * path)358 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
359 {
360     LV_UNUSED(drv);
361     dir_handle_t * handle = (dir_handle_t *)lv_mem_alloc(sizeof(dir_handle_t));
362     handle->dir_p = INVALID_HANDLE_VALUE;
363     handle->next_error = LV_FS_RES_OK;
364     WIN32_FIND_DATAA fdata;
365 
366     /*Make the path relative to the current directory (the projects root folder)*/
367     char buf[MAX_PATH_LEN];
368 #ifdef LV_FS_WIN32_PATH
369     lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s\\*", path);
370 #else
371     lv_snprintf(buf, sizeof(buf), "%s\\*", path);
372 #endif
373 
374     strcpy(handle->next_fn, "");
375     handle->dir_p = FindFirstFileA(buf, &fdata);
376     do {
377         if(is_dots_name(fdata.cFileName)) {
378             continue;
379         }
380         else {
381             if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
382                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
383             }
384             else {
385                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
386             }
387             break;
388         }
389     } while(FindNextFileA(handle->dir_p, &fdata));
390 
391     if(handle->dir_p == INVALID_HANDLE_VALUE) {
392         lv_mem_free(handle);
393         handle->next_error = fs_error_from_win32(GetLastError());
394         return INVALID_HANDLE_VALUE;
395     }
396     else {
397         handle->next_error = LV_FS_RES_OK;
398         return handle;
399     }
400 }
401 
402 /**
403  * Read the next filename from a directory.
404  * The name of the directories will begin with '/'
405  * @param drv pointer to a driver where this function belongs
406  * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
407  * @param fn pointer to a buffer to store the filename
408  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
409  */
fs_dir_read(lv_fs_drv_t * drv,void * dir_p,char * fn)410 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn)
411 {
412     LV_UNUSED(drv);
413     dir_handle_t * handle = (dir_handle_t *)dir_p;
414     strcpy(fn, handle->next_fn);
415     lv_fs_res_t current_error = handle->next_error;
416     strcpy(handle->next_fn, "");
417 
418     WIN32_FIND_DATAA fdata;
419 
420     while(FindNextFileA(handle->dir_p, &fdata)) {
421         if(is_dots_name(fdata.cFileName)) {
422             continue;
423         }
424         else {
425             if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
426                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
427             }
428             else {
429                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
430             }
431             break;
432         }
433     }
434 
435     if(handle->next_fn[0] == '\0') {
436         handle->next_error = fs_error_from_win32(GetLastError());
437     }
438 
439     return current_error;
440 }
441 
442 /**
443  * Close the directory reading
444  * @param drv pointer to a driver where this function belongs
445  * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
446  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
447  */
fs_dir_close(lv_fs_drv_t * drv,void * dir_p)448 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
449 {
450     LV_UNUSED(drv);
451     dir_handle_t * handle = (dir_handle_t *)dir_p;
452     lv_fs_res_t res = FindClose(handle->dir_p)
453                       ? LV_FS_RES_OK
454                       : fs_error_from_win32(GetLastError());
455     lv_mem_free(handle);
456     return res;
457 }
458 
459 #else /*LV_USE_FS_WIN32 == 0*/
460 
461 #if defined(LV_FS_WIN32_LETTER) && LV_FS_WIN32_LETTER != '\0'
462     #warning "LV_USE_FS_WIN32 is not enabled but LV_FS_WIN32_LETTER is set"
463 #endif
464 
465 #endif
466