5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
27 #if defined(SIMU_USE_SDCARD) // rest of file is excluded otherwise
29 #if defined(_MSC_VER) || !defined (__GNUC__)
35 // NOTE: the #include order is important here, sensitive on different platoforms.
45 #include <sys/utime.h>
46 #define mkdir(s, f) _mkdir(s)
56 #if !defined(_MSC_VER)
61 std::string simuSdDirectory
; // path to the root of the SD card image
62 std::string simuSettingsDirectory
; // path to the root of the models and settings (only for the radios that use SD for model storage)
64 bool isPathDelimiter(char delimiter
)
66 return delimiter
== '/';
69 std::string
removeTrailingPathDelimiter(const std::string
& path
)
71 std::string result
= path
;
72 while (!result
.empty() && isPathDelimiter(result
.back())) {
78 std::string
fixPathDelimiters(const char * path
)
80 // replace all '\' characters with '/'
81 std::string
result(path
);
82 std::replace(result
.begin(), result
.end(), '\\', '/');
83 // TRACE_SIMPGMSPACE("fixPathDelimiters(): %s -> %s", path, result.c_str());
87 void simuFatfsSetPaths(const char * sdPath
, const char * settingsPath
)
90 simuSdDirectory
= removeTrailingPathDelimiter(fixPathDelimiters(sdPath
));
94 f_getcwd(buff
, sizeof(buff
)-1);
95 simuSdDirectory
= removeTrailingPathDelimiter(fixPathDelimiters(buff
));
98 simuSettingsDirectory
= removeTrailingPathDelimiter(fixPathDelimiters(settingsPath
));
100 TRACE_SIMPGMSPACE("simuFatfsSetPaths(): simuSdDirectory: \"%s\"", simuSdDirectory
.c_str());
101 TRACE_SIMPGMSPACE("simuFatfsSetPaths(): simuSettingsDirectory: \"%s\"", simuSettingsDirectory
.c_str());
104 bool startsWith(const std::string
& str
, const std::string
& prefix
)
106 return str
.length() >= prefix
.length() &&
107 str
.compare(0, prefix
.length(), prefix
) == 0;
110 bool endsWith(const std::string
& str
, const std::string
& suffix
)
112 return str
.length() >= suffix
.length() &&
113 str
.compare(str
.length() - suffix
.length(), suffix
.length(), suffix
) == 0;
116 bool redirectToSettingsDirectory(const std::string
& path
)
119 Decide if we use special simuSettingsDirectory path or normal path
121 We use special path for:
122 * radio settings and models list in /RADIO directory
123 * model (*.bin) files in /MODELS directory
125 if (!simuSettingsDirectory
.empty()) {
126 #if defined(COLORLCD)
127 if (path
== RADIO_MODELSLIST_PATH
|| path
== RADIO_SETTINGS_PATH
) {
131 if (startsWith(path
, "/MODELS") && endsWith(path
, MODELS_EXT
)) {
138 std::string
convertToSimuPath(const char * path
)
141 if (isPathDelimiter(path
[0])) {
142 if (redirectToSettingsDirectory(path
)) {
143 // TRACE("REDIRECT ********************************************");
144 result
= simuSettingsDirectory
+ std::string(path
);
147 result
= simuSdDirectory
+ std::string(path
);
151 result
= std::string(path
);
153 TRACE_SIMPGMSPACE("convertToSimuPath(): %s -> %s", path
, result
.c_str());
157 std::string
convertFromSimuPath(const char * path
)
160 if (startsWith(path
, simuSdDirectory
)) {
161 result
= std::string(path
).substr(simuSdDirectory
.length(), std::string::npos
);
162 if (result
.empty()) {
167 result
= std::string(path
);
168 if (!result
.empty() && !isPathDelimiter(result
[0])) {
169 result
= "/" + result
;
172 TRACE_SIMPGMSPACE("convertFromSimuPath(): %s -> %s", path
, result
.c_str());
176 typedef std::map
<std::string
, std::string
> filemap_t
;
180 void splitPath(const std::string
& path
, std::string
& dir
, std::string
& name
)
183 char drive
[_MAX_DRIVE
];
184 char directory
[_MAX_DIR
];
185 char fname
[_MAX_FNAME
];
187 _splitpath(path
.c_str(), drive
, directory
, fname
, ext
);
188 name
= std::string(fname
) + std::string(ext
);
189 dir
= std::string(drive
) + std::string(directory
);
191 char * buff
= new char[path
.length()+1];
192 strcpy(buff
, path
.c_str());
193 name
= simu::basename(buff
);
194 strcpy(buff
, path
.c_str());
195 dir
= simu::dirname(buff
);
202 bool isFile(const std::string
& fullName
, unsigned int d_type
)
204 #if defined(WIN32) || defined(__APPLE__) || defined(__FreeBSD__)
205 #define REGULAR_FILE DT_REG
206 #define SYMBOLIC_LINK DT_LNK
208 #define REGULAR_FILE simu::DT_REG
209 #define SYMBOLIC_LINK simu::DT_LNK
212 if (d_type
== REGULAR_FILE
) return true;
213 if (d_type
== SYMBOLIC_LINK
) {
215 if (stat(fullName
.c_str(), &tmp
) == 0) {
216 // TRACE_SIMPGMSPACE("\tsymlik: %s is %s", fullName.c_str(), (tmp.st_mode & S_IFREG) ? "file" : "other");
217 if (tmp
.st_mode
& S_IFREG
) return true;
224 std::vector
<std::string
> listDirectoryFiles(const std::string
& dirName
)
226 std::vector
<std::string
> result
;
229 std::string searchName
= dirName
+ "*";
230 // TRACE_SIMPGMSPACE("\tsearching for: %s", fileName.c_str());
232 HANDLE hFind
= FindFirstFile(searchName
.c_str(), &ffd
);
233 if (INVALID_HANDLE_VALUE
!= hFind
) {
235 if (!(ffd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)) {
236 std::string fullName
= dirName
+ std::string(ffd
.cFileName
);
237 // TRACE_SIMPGMSPACE("listDirectoryFiles(): %s", fullName.c_str());
238 result
.push_back(fullName
);
241 while (FindNextFile(hFind
, &ffd
) != 0);
244 simu::DIR * dir
= simu::opendir(dirName
.c_str());
246 struct simu::dirent
* res
;
247 while ((res
= simu::readdir(dir
)) != 0) {
248 std::string fullName
= dirName
+ "/" + std::string(res
->d_name
);
249 if (isFile(fullName
, res
->d_type
)) {
250 // TRACE_SIMPGMSPACE("listDirectoryFiles(): %s", fullName.c_str());
251 result
.push_back(fullName
);
260 std::string
findTrueFileName(const std::string
& path
)
262 TRACE_SIMPGMSPACE("findTrueFileName(%s)", path
.c_str());
264 filemap_t::iterator i
= fileMap
.find(path
);
265 if (i
!= fileMap
.end()) {
267 TRACE_SIMPGMSPACE("\tfound in map: %s", result
.c_str());
271 //find file and add to map
273 std::string fileName
;
274 splitPath(path
, dirName
, fileName
);
275 std::vector
<std::string
> files
= listDirectoryFiles(dirName
);
276 for(unsigned int i
=0; i
<files
.size(); ++i
) {
277 if (!strcasecmp(files
[i
].c_str(), path
.c_str())) {
278 TRACE_SIMPGMSPACE("\tfound: %s", files
[i
].c_str());
279 fileMap
.insert(filemap_t::value_type(path
, files
[i
]));
284 TRACE_SIMPGMSPACE("\tnot found");
285 return std::string(path
);
288 FRESULT
f_stat (const TCHAR
* name
, FILINFO
*fno
)
290 std::string path
= convertToSimuPath(name
);
291 std::string realPath
= findTrueFileName(path
);
293 if (stat(realPath
.c_str(), &tmp
)) {
294 TRACE_SIMPGMSPACE("f_stat(%s) = error %d (%s)", path
.c_str(), errno
, strerror(errno
));
295 return FR_INVALID_NAME
;
298 TRACE_SIMPGMSPACE("f_stat(%s) = OK", path
.c_str());
300 fno
->fattrib
= (tmp
.st_mode
& S_IFDIR
) ? AM_DIR
: 0;
301 // convert to FatFs fdate/ftime
302 struct tm
*ltime
= localtime(&tmp
.st_mtime
);
303 fno
->fdate
= ((ltime
->tm_year
- 80) << 9) | ((ltime
->tm_mon
+ 1) << 5) | ltime
->tm_mday
;
304 fno
->ftime
= (ltime
->tm_hour
<< 11) | (ltime
->tm_min
<< 5) | (ltime
->tm_sec
/ 2);
305 fno
->fsize
= (DWORD
)tmp
.st_size
;
311 FRESULT
f_mount (FATFS
* ,const TCHAR
*, BYTE opt
)
316 FRESULT
f_open (FIL
* fil
, const TCHAR
*name
, BYTE flag
)
318 std::string path
= convertToSimuPath(name
);
319 std::string realPath
= findTrueFileName(path
);
321 if (!(flag
& FA_WRITE
)) {
323 if (stat(realPath
.c_str(), &tmp
)) {
324 TRACE_SIMPGMSPACE("f_open(%s) = INVALID_NAME (FIL %p)", path
.c_str(), fil
);
325 return FR_INVALID_NAME
;
327 fil
->obj
.objsize
= tmp
.st_size
;
330 fil
->obj
.fs
= (FATFS
*)fopen(realPath
.c_str(), (flag
& FA_WRITE
) ? ((flag
& FA_CREATE_ALWAYS
) ? "wb+" : "ab+") : "rb+");
333 TRACE_SIMPGMSPACE("f_open(%s, %x) = %p (FIL %p)", path
.c_str(), flag
, fil
->obj
.fs
, fil
);
336 TRACE_SIMPGMSPACE("f_open(%s) = error %d (%s) (FIL %p)", path
.c_str(), errno
, strerror(errno
), fil
);
337 return FR_INVALID_NAME
;
340 FRESULT
f_read (FIL
* fil
, void* data
, UINT size
, UINT
* read
)
342 if (fil
&& fil
->obj
.fs
) {
343 *read
= fread(data
, 1, size
, (FILE*)fil
->obj
.fs
);
345 // TRACE_SIMPGMSPACE("fread(%p) %u, %u", fil->obj.fs, size, *read);
350 FRESULT
f_write (FIL
* fil
, const void* data
, UINT size
, UINT
* written
)
352 if (fil
&& fil
->obj
.fs
) {
353 *written
= fwrite(data
, 1, size
, (FILE*)fil
->obj
.fs
);
355 // TRACE_SIMPGMSPACE("fwrite(%p) %u, %u", fil->obj.fs, size, *written);
360 TCHAR
* f_gets (TCHAR
* buff
, int len
, FIL
* fil
)
362 if (fil
&& fil
->obj
.fs
) {
363 buff
= fgets(buff
, len
, (FILE*)fil
->obj
.fs
);
364 if (buff
!= nullptr) {
367 // TRACE_SIMPGMSPACE("fgets(%p) %u, %s", fil->obj.fs, len, buff);
372 FRESULT
f_lseek (FIL
* fil
, DWORD offset
)
374 if (fil
&& fil
->obj
.fs
) {
375 fseek((FILE*)fil
->obj
.fs
, offset
, SEEK_SET
);
381 UINT
f_size(FIL
* fil
)
383 if (fil
&& fil
->obj
.fs
) {
384 long curr
= ftell((FILE*)fil
->obj
.fs
);
385 fseek((FILE*)fil
->obj
.fs
, 0, SEEK_END
);
386 long size
= ftell((FILE*)fil
->obj
.fs
);
387 fseek((FILE*)fil
->obj
.fs
, curr
, SEEK_SET
);
388 TRACE_SIMPGMSPACE("f_size(%p) %u", fil
->obj
.fs
, size
);
394 FRESULT
f_close (FIL
* fil
)
396 TRACE_SIMPGMSPACE("f_close(%p) (FIL:%p)", fil
->obj
.fs
, fil
);
398 fclose((FILE*)fil
->obj
.fs
);
399 fil
->obj
.fs
= nullptr;
404 FRESULT
f_chdir (const TCHAR
*name
)
406 std::string path
= convertToSimuPath(name
);
407 if (chdir(path
.c_str())) {
408 TRACE_SIMPGMSPACE("f_chdir(%s) = error %d (%s)", path
.c_str(), errno
, strerror(errno
));
411 TRACE_SIMPGMSPACE("f_chdir(%s)", path
.c_str());
415 FRESULT
f_opendir (DIR * rep
, const TCHAR
* name
)
417 std::string path
= convertToSimuPath(name
);
418 rep
->obj
.fs
= (FATFS
*)simu::opendir(path
.c_str());
420 TRACE_SIMPGMSPACE("f_opendir(%s) = OK", path
.c_str());
423 TRACE_SIMPGMSPACE("f_opendir(%s) = error %d (%s)", path
.c_str(), errno
, strerror(errno
));
427 FRESULT
f_closedir (DIR * rep
)
429 TRACE_SIMPGMSPACE("f_closedir(%p)", rep
);
431 simu::closedir((simu::DIR *)rep
->obj
.fs
);
436 FRESULT
f_readdir (DIR * rep
, FILINFO
* fil
)
439 if (!rep
->obj
.fs
) return FR_NO_FILE
;
441 ent
= simu::readdir((simu::DIR *)rep
->obj
.fs
);
442 if (!ent
) return FR_NO_FILE
;
443 if (strcmp(ent
->d_name
, ".") && strcmp(ent
->d_name
, "..") ) break;
446 #if defined(WIN32) || !defined(__GNUC__) || defined(__APPLE__) || defined(__FreeBSD__)
447 fil
->fattrib
= (ent
->d_type
== DT_DIR
? AM_DIR
: 0);
449 if (ent
->d_type
== simu::DT_UNKNOWN
|| ent
->d_type
== simu::DT_LNK
) {
452 if (stat(ent
->d_name
, &buf
) == 0) {
453 fil
->fattrib
= (S_ISDIR(buf
.st_mode
) ? AM_DIR
: 0);
457 fil
->fattrib
= (ent
->d_type
== simu::DT_DIR
? AM_DIR
: 0);
461 memset(fil
->fname
, 0, _MAX_LFN
);
462 strcpy(fil
->fname
, ent
->d_name
);
463 // TRACE_SIMPGMSPACE("f_readdir(): %s", fil->fname);
467 FRESULT
f_mkfs (const TCHAR
* path
, BYTE opt
, DWORD au
, void* work
, UINT len
)
469 TRACE_SIMPGMSPACE("Format SD...");
473 FRESULT
f_mkdir (const TCHAR
* name
)
475 std::string path
= convertToSimuPath(name
);
476 #if defined(WIN32) && defined(__GNUC__)
477 if (mkdir(path
.c_str())) {
479 if (mkdir(path
.c_str(), 0777)) {
481 TRACE_SIMPGMSPACE("mkdir(%s) = error %d (%s)", path
.c_str(), errno
, strerror(errno
));
482 return FR_INVALID_NAME
;
485 TRACE_SIMPGMSPACE("mkdir(%s) = OK", path
.c_str());
491 FRESULT
f_unlink (const TCHAR
* name
)
493 std::string path
= convertToSimuPath(name
);
494 if (unlink(path
.c_str())) {
495 TRACE_SIMPGMSPACE("f_unlink(%s) = error %d (%s)", path
.c_str(), errno
, strerror(errno
));
496 return FR_INVALID_NAME
;
499 TRACE_SIMPGMSPACE("f_unlink(%s) = OK", path
.c_str());
504 FRESULT
f_rename(const TCHAR
*oldname
, const TCHAR
*newname
)
506 std::string old
= convertToSimuPath(oldname
);
507 std::string path
= convertToSimuPath(newname
);
509 if (rename(old
.c_str(), path
.c_str()) < 0) {
510 TRACE_SIMPGMSPACE("f_rename(%s, %s) = error %d (%s)", old
.c_str(), path
.c_str(), errno
, strerror(errno
));
511 return FR_INVALID_NAME
;
513 TRACE_SIMPGMSPACE("f_rename(%s, %s) = OK", old
.c_str(), path
.c_str());
517 FRESULT
f_utime(const TCHAR
* path
, const FILINFO
* fno
)
520 return FR_INVALID_PARAMETER
;
522 std::string simpath
= convertToSimuPath(path
);
523 std::string realPath
= findTrueFileName(simpath
);
524 struct utimbuf newTimes
;
527 // convert from FatFs fdate/ftime
528 ltime
.tm_year
= ((fno
->fdate
>> 9) & 0x7F) + 80;
529 ltime
.tm_mon
= ((fno
->fdate
>> 5) & 0xF) - 1;
530 ltime
.tm_mday
= (fno
->fdate
& 0x1F);
531 ltime
.tm_hour
= ((fno
->ftime
>> 11) & 0x1F);
532 ltime
.tm_min
= ((fno
->ftime
>> 5) & 0x3F);
533 ltime
.tm_sec
= (fno
->ftime
& 0x1F) * 2;
534 ltime
.tm_isdst
= -1; // force mktime() to check dst
536 newTimes
.modtime
= mktime(<ime
);
537 newTimes
.actime
= newTimes
.modtime
;
539 if (utime(realPath
.c_str(), &newTimes
)) {
540 TRACE_SIMPGMSPACE("f_utime(%s) = error %d (%s)", simpath
.c_str(), errno
, strerror(errno
));
544 TRACE_SIMPGMSPACE("f_utime(%s) set mtime = %s", simpath
.c_str(), ctime(&newTimes
.modtime
));
549 int f_putc (TCHAR c
, FIL
* fil
)
551 if (fil
&& fil
->obj
.fs
) fwrite(&c
, 1, 1, (FILE*)fil
->obj
.fs
);
555 int f_puts (const TCHAR
* str
, FIL
* fil
)
558 for (n
= 0; *str
; str
++, n
++) {
559 if (f_putc(*str
, fil
) == EOF
) return EOF
;
564 int f_printf (FIL
*fil
, const TCHAR
* format
, ...)
567 va_start(arglist
, format
);
568 if (fil
&& fil
->obj
.fs
) vfprintf((FILE*)fil
->obj
.fs
, format
, arglist
);
573 FRESULT
f_getcwd (TCHAR
*path
, UINT sz_path
)
576 if (!getcwd(cwd
, 1024)) {
577 TRACE_SIMPGMSPACE("f_getcwd() = getcwd() error %d (%s)", errno
, strerror(errno
));
582 std::string result
= convertFromSimuPath(fixPathDelimiters(cwd
).c_str());
583 if (result
.length() > sz_path
) {
584 //TRACE_SIMPGMSPACE("f_getcwd(): buffer too short");
585 return FR_NOT_ENOUGH_CORE
;
588 strcpy(path
, result
.c_str());
589 TRACE_SIMPGMSPACE("f_getcwd() = %s", path
);
593 FRESULT
f_getfree (const TCHAR
* path
, DWORD
* nclst
, FATFS
** fatfs
)
595 // just fake that we always have some clusters free
600 #if defined(PCBSKY9X)
601 int32_t Card_state
= SD_ST_MOUNTED
;
602 uint32_t Card_CSD
[4]; // TODO elsewhere
605 #endif // #if defined(SIMU_USE_SDCARD)