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