this repo has no description
1// MIT License
2
3// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
4
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23#include "studio.h"
24#include "fs.h"
25#include "net.h"
26#include "ext/json.h"
27
28#if defined(BAREMETALPI) || defined(_3DS)
29 #ifdef EN_DEBUG
30 #define dbg(...) printf(__VA_ARGS__)
31 #else
32 #define dbg(...)
33 #endif
34#endif
35
36#if defined(BAREMETALPI)
37#include "../../circle-stdlib/libs/circle/addon/fatfs/ff.h"
38#else
39#include <dirent.h>
40#include <sys/stat.h>
41#include <sys/types.h>
42#endif
43
44#if defined(__TIC_WINDOWS__)
45#include <direct.h>
46#include <windows.h>
47#else
48#include <unistd.h>
49#endif
50
51#if defined(__EMSCRIPTEN__)
52#include <emscripten.h>
53#endif
54
55#if defined(__TIC_WINDOWS__)
56#define SLASH_SYMBOL ('\\')
57#else
58#define SLASH_SYMBOL ('/')
59#endif
60
61#if defined (__TIC_MACOSX__)
62#include <mach-o/dyld.h>
63#endif
64
65static const char* PublicDir = TIC_HOST;
66
67struct tic_fs
68{
69 char dir[TICNAME_MAX];
70 char work[TICNAME_MAX];
71 tic_net* net;
72};
73
74#if defined(__EMSCRIPTEN__)
75void syncfs()
76{
77 EM_ASM({Module.syncFSRequests++;});
78}
79#endif
80
81const char* tic_fs_pathroot(tic_fs* fs, const char* name)
82{
83 static char path[TICNAME_MAX];
84
85 snprintf(path, sizeof path, "%s%s", fs->dir, name);
86
87#if defined(__TIC_WINDOWS__)
88 char* ptr = path;
89 while (*ptr)
90 {
91 if (*ptr == '/') *ptr = SLASH_SYMBOL;
92 ptr++;
93 }
94#endif
95
96 return path;
97}
98
99const char* tic_fs_path(tic_fs* fs, const char* name)
100{
101 static char path[TICNAME_MAX+1];
102
103 if(*name == '/')
104 strncpy(path, name + 1, sizeof path);
105 else if(strlen(fs->work))
106 snprintf(path, sizeof path, "%s/%s", fs->work, name);
107 else
108 strncpy(path, name, sizeof path);
109
110 return tic_fs_pathroot(fs, path);
111}
112
113static bool isRoot(tic_fs* fs)
114{
115 return fs->work[0] == '\0';
116}
117
118bool tic_fs_isroot(tic_fs* fs)
119{
120 return isRoot(fs);
121}
122
123static bool isPublicRoot(tic_fs* fs)
124{
125 return strcmp(fs->work, PublicDir) == 0;
126}
127
128
129static bool isPublic(tic_fs* fs)
130{
131 return memcmp(fs->work, PublicDir, STRLEN(PublicDir)) == 0;
132}
133
134bool tic_fs_ispubdir(tic_fs* fs)
135{
136 return isPublic(fs);
137}
138
139#if defined(__TIC_WINDOWS__)
140
141typedef wchar_t FsString;
142
143#define __S(x) L ## x
144#define _S(x) __S(x)
145
146static const FsString* utf8ToString(const char* str)
147{
148 FsString* wstr = malloc(TICNAME_MAX * sizeof(FsString));
149
150 MultiByteToWideChar(CP_UTF8, 0, str, TICNAME_MAX, wstr, TICNAME_MAX);
151
152 return wstr;
153}
154
155static const char* stringToUtf8(const FsString* wstr)
156{
157 char* str = malloc(TICNAME_MAX * sizeof(char));
158
159 WideCharToMultiByte(CP_UTF8, 0, wstr, TICNAME_MAX, str, TICNAME_MAX, 0, 0);
160
161 return str;
162}
163
164#if defined(__TIC_WIN7__)
165
166time_t FileTimeToTimeT(FILETIME* ft) {
167 ULARGE_INTEGER ull;
168 ull.LowPart = ft->dwLowDateTime;
169 ull.HighPart = ft->dwHighDateTime;
170 return (time_t)((ull.QuadPart / 10000000ULL) - 11644473600ULL);
171}
172
173// There is a bug in the toolchain when compiling with v141_xp
174// This shim is a workaround for that, using GetFileAttributesEx
175// see https://stackoverflow.com/questions/32452777/visual-c-2015-express-stat-not-working-on-windows-xp
176static int _wstat_win32_shim(const wchar_t* path, struct _stat* buffer)
177{
178 WIN32_FILE_ATTRIBUTE_DATA fileInfo;
179 if (!GetFileAttributesExW(path, GetFileExInfoStandard, &fileInfo))
180 {
181 return -1;
182 }
183
184 memset(buffer, 0, sizeof(struct _stat));
185
186 buffer->st_mode = _S_IREAD;
187 if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
188 {
189 buffer->st_mode |= _S_IWRITE;
190 }
191 if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
192 {
193 buffer->st_mode |= _S_IFDIR;
194 }
195 else
196 {
197 buffer->st_mode |= _S_IFREG;
198 }
199
200 // Set the file size
201 buffer->st_size = ((_off_t)fileInfo.nFileSizeHigh << 32) | fileInfo.nFileSizeLow;
202
203 // Set the file times
204 buffer->st_mtime = FileTimeToTimeT(&fileInfo.ftLastWriteTime);
205 buffer->st_atime = FileTimeToTimeT(&fileInfo.ftLastAccessTime);
206 buffer->st_ctime = FileTimeToTimeT(&fileInfo.ftCreationTime);
207 return 0;
208}
209
210#endif
211
212#define freeString(S) free((void*)S)
213
214
215#define TIC_DIR _WDIR
216#define tic_dirent _wdirent
217#define tic_stat_struct _stat
218
219#define tic_opendir _wopendir
220#define tic_readdir _wreaddir
221#define tic_closedir _wclosedir
222#define tic_rmdir _wrmdir
223
224// use the shim (see above) if we're targeting Windows XP
225#if defined(__TIC_WIN7__)
226 #define tic_stat _wstat_win32_shim
227#else
228 #define tic_stat _wstat
229#endif
230#define tic_remove _wremove
231#define tic_fopen _wfopen
232#define tic_mkdir(name) _wmkdir(name)
233#define tic_strncpy wcsncpy
234#define tic_strncat wcsncat
235#define tic_strlen wcslen
236
237#else
238
239typedef char FsString;
240
241#define _S(x) (x)
242
243#define utf8ToString(S) (S)
244#define stringToUtf8(S) (S)
245
246#define freeString(S)
247
248#define TIC_DIR DIR
249#define tic_dirent dirent
250#define tic_stat_struct stat
251
252#define tic_opendir opendir
253#define tic_readdir readdir
254#define tic_closedir closedir
255#define tic_rmdir rmdir
256#define tic_stat stat
257#define tic_remove remove
258#define tic_fopen fopen
259#define tic_mkdir(name) mkdir(name, 0777)
260#define tic_strncpy strncpy
261#define tic_strncat strncat
262#define tic_strlen strlen
263
264#endif
265
266typedef struct
267{
268 fs_list_callback item;
269 fs_done_callback done;
270 void* data;
271} NetDirData;
272
273static void onDirResponse(const net_get_data* netData)
274{
275 NetDirData* netDirData = (NetDirData*)netData->calldata;
276
277 if(netData->type == net_get_done)
278 {
279 if(json_parse((char*)netData->done.data, netData->done.size))
280 {
281 typedef char string[TICNAME_MAX];
282
283 string name, hash, filename;
284
285 s32 folders = json_array("folders", 0);
286 for(s32 i = 0, size = json_array_size(folders); i < size; i++)
287 {
288 if(json_string("name", json_array_item(folders, i), name, sizeof name))
289 netDirData->item(name, NULL, NULL, 0, netDirData->data, true);
290 }
291
292 s32 files = json_array("files", 0);
293 for(s32 i = 0, size = json_array_size(files); i < size; i++)
294 {
295 s32 item = json_array_item(files, i);
296
297 if(json_string("name", item, name, sizeof name)
298 && json_string("hash", item, hash, sizeof hash)
299 && json_string("filename", item, filename, sizeof filename))
300 netDirData->item(filename, name, hash, json_int("id", item), netDirData->data, false);
301 }
302 }
303 }
304
305 switch (netData->type)
306 {
307 case net_get_done:
308 case net_get_error:
309 netDirData->done(netDirData->data);
310 free(netDirData);
311 break;
312 default: break;
313 }
314}
315
316void fs_enum(const char* path, fs_list_callback callback, void* data)
317{
318#if defined(BAREMETALPI)
319 dbg("fs_enum %s", path);
320
321 if (path && *path) {
322 // ok
323 }
324 else
325 {
326 return;
327 }
328
329 static char path2[TICNAME_MAX];
330 strncpy(path2, path, sizeof path2);
331
332 if (path2[strlen(path2) - 1] == '/') // one character
333 path2[strlen(path2) - 1] = 0;
334
335 dbg("fs_enum Real %s", path2);
336
337
338 DIR Directory;
339 FILINFO FileInfo;
340 FRESULT Result = f_findfirst (&Directory, &FileInfo, path2, "*");
341 dbg("enumFilesRes %d", Result);
342
343 for (unsigned i = 0; Result == FR_OK && FileInfo.fname[0]; i++)
344 {
345 if (!(FileInfo.fattrib & (AM_HID | AM_SYS)))
346 {
347 bool result = callback(FileInfo.fname, NULL, NULL, 0, data, FileInfo.fattrib & AM_DIR);
348
349 if (!result)
350 {
351 break;
352 }
353 }
354
355 Result = f_findnext (&Directory, &FileInfo);
356 }
357
358#else
359 TIC_DIR *dir = NULL;
360 struct tic_dirent* ent = NULL;
361
362 const FsString* pathString = utf8ToString(path);
363
364 if ((dir = tic_opendir(pathString)) != NULL)
365 {
366 FsString fullPath[TICNAME_MAX] = {0};
367 struct tic_stat_struct s;
368
369 while ((ent = tic_readdir(dir)) != NULL)
370 {
371 if(*ent->d_name != _S('.'))
372 {
373 size_t pathLen = tic_strlen(pathString);
374 size_t nameLen = tic_strlen(ent->d_name);
375
376 if (pathLen + nameLen < COUNT_OF(fullPath)) {
377 tic_strncpy(fullPath, pathString, COUNT_OF(fullPath));
378 tic_strncat(fullPath, ent->d_name, COUNT_OF(fullPath) - pathLen - 1);
379 }
380
381 if(tic_stat(fullPath, &s) == 0)
382 {
383 const char* name = stringToUtf8(ent->d_name);
384 bool result = callback(name, NULL, NULL, 0, data, S_ISDIR(s.st_mode));
385 freeString(name);
386
387 if(!result) break;
388 }
389 }
390 }
391
392 tic_closedir(dir);
393 }
394
395 freeString(pathString);
396#endif
397}
398
399void tic_fs_enum(tic_fs* fs, fs_list_callback onItem, fs_done_callback onDone, void* data)
400{
401#ifndef BAREMETALPI
402 if (isRoot(fs) && !onItem(PublicDir, NULL, NULL, 0, data, true))
403 {
404 onDone(data);
405 return;
406 }
407
408#if defined(BUILD_EDITORS)
409 if(isPublic(fs))
410 {
411 char request[TICNAME_MAX];
412 snprintf(request, sizeof request, "/json?fn=dir&path=%s", fs->work + sizeof(TIC_HOST));
413
414 NetDirData netDirData = { onItem, onDone, data };
415 tic_net_get(fs->net, request, onDirResponse, MOVE(netDirData));
416
417 return;
418 }
419#endif
420
421#endif
422
423 const char* path = tic_fs_path(fs, "");
424
425 fs_enum(path, onItem, data);
426
427 onDone(data);
428}
429
430bool tic_fs_deldir(tic_fs* fs, const char* name)
431{
432#if defined(BAREMETALPI)
433 const char* path = tic_fs_path(fs, name);
434 const FsString* pathString = utf8ToString(path);
435 bool result = (f_unlink(pathString) != FR_OK);
436 freeString(pathString);
437
438 return result;
439#else
440#if defined(__TIC_WINDOWS__)
441 const char* path = tic_fs_path(fs, name);
442
443 const FsString* pathString = utf8ToString(path);
444 bool result = tic_rmdir(pathString);
445 freeString(pathString);
446
447#else
448 bool result = rmdir(tic_fs_path(fs, name));
449#endif
450
451#if defined(__EMSCRIPTEN__)
452 syncfs();
453#endif
454
455 return result;
456#endif
457}
458
459bool tic_fs_delfile(tic_fs* fs, const char* name)
460{
461 const char* path = tic_fs_path(fs, name);
462
463 const FsString* pathString = utf8ToString(path);
464#if defined(BAREMETALPI)
465 bool result = (f_unlink(pathString) != FR_OK);
466#else
467 bool result = tic_remove(pathString);
468#endif
469 freeString(pathString);
470
471#if defined(__EMSCRIPTEN__)
472 syncfs();
473#endif
474
475 return result;
476}
477
478void tic_fs_homedir(tic_fs* fs)
479{
480 memset(fs->work, 0, sizeof fs->work);
481}
482
483void tic_fs_dirback(tic_fs* fs)
484{
485 if(isPublicRoot(fs))
486 {
487 tic_fs_homedir(fs);
488 return;
489 }
490
491 char* start = fs->work;
492 char* ptr = start + strlen(fs->work);
493
494 while (ptr > start && *ptr != '/') ptr--;
495 while (*ptr) *ptr++ = '\0';
496}
497
498void tic_fs_dir(tic_fs* fs, char* dir)
499{
500 snprintf(dir, TICNAME_MAX, "%s", fs->work);
501}
502
503void tic_fs_changedir(tic_fs* fs, const char* dir)
504{
505 char temp[TICNAME_MAX+1];
506
507 if (strlen(fs->work) > 0) {
508 snprintf(temp, TICNAME_MAX+1, "%s/%s", fs->work, dir);
509 } else {
510 snprintf(temp, TICNAME_MAX, "%s", dir);
511 }
512
513 strncpy(fs->work, temp, TICNAME_MAX - 1);
514
515#if defined(__TIC_WINDOWS__)
516 for(char *ptr = fs->work, *end = ptr + strlen(ptr); ptr < end; ptr++)
517 if(*ptr == SLASH_SYMBOL)
518 *ptr = '/';
519#endif
520}
521
522typedef struct
523{
524 char* name;
525 bool found;
526 fs_isdir_callback done;
527 void* data;
528
529} EnumPublicDirsData;
530
531static bool onEnumPublicDirs(const char* name, const char* title, const char* hash, s32 id, void* data, bool dir)
532{
533 EnumPublicDirsData* enumPublicDirsData = (EnumPublicDirsData*)data;
534
535 if(strcmp(name, enumPublicDirsData->name) == 0)
536 {
537 enumPublicDirsData->found = true;
538 return false;
539 }
540
541 return true;
542}
543
544static void onEnumPublicDirsDone(void* data)
545{
546 EnumPublicDirsData* enumPublicDirsData = data;
547 enumPublicDirsData->done(enumPublicDirsData->found, enumPublicDirsData->data);
548 free(enumPublicDirsData->name);
549 free(enumPublicDirsData);
550}
551
552bool fs_isdir(const char* path)
553{
554#if defined(BAREMETALPI)
555 FILINFO s;
556 FRESULT res = f_stat(path, &s);
557 if (res != FR_OK) return false;
558 return s.fattrib & AM_DIR;
559#else
560 struct tic_stat_struct s;
561 const FsString* pathString = utf8ToString(path);
562 bool isdir = tic_stat(pathString, &s) == 0 && S_ISDIR(s.st_mode);
563 freeString(pathString);
564 return isdir;
565#endif
566}
567
568bool tic_fs_isdir(tic_fs* fs, const char* name)
569{
570 if (*name == '.') return false;
571 if (isRoot(fs) && strcmp(name, PublicDir) == 0) return true;
572
573#if defined(BAREMETALPI)
574 dbg("fsIsDirSync %s\n", name);
575 FILINFO s;
576 const char* path = tic_fs_path(fs, name);
577 FRESULT res = f_stat(path, &s);
578 if (res != FR_OK) return false;
579
580 return s.fattrib & AM_DIR;
581#else
582 return fs_isdir(tic_fs_path(fs, name));
583#endif
584}
585
586void tic_fs_isdir_async(tic_fs* fs, const char* name, fs_isdir_callback callback, void* data)
587{
588 if(isPublic(fs))
589 {
590 EnumPublicDirsData enumPublicDirsData = { strdup(name), false, callback, data};
591 tic_fs_enum(fs, onEnumPublicDirs, onEnumPublicDirsDone, MOVE(enumPublicDirsData));
592 return;
593 }
594
595 callback(tic_fs_isdir(fs, name), data);
596}
597
598bool fs_write(const char* name, const void* buffer, s32 size)
599{
600#if defined(BAREMETALPI)
601 dbg("fs_write %s\n", name);
602 FIL File;
603 FRESULT res = f_open (&File, name, FA_WRITE | FA_CREATE_ALWAYS);
604 if (res != FR_OK)
605 {
606 return false;
607 }
608
609 u32 written=0;
610
611 res = f_write(&File, buffer, size, &written);
612
613 f_close(&File);
614 if (res != FR_OK)
615 {
616 return false;
617 }
618 if(written!=size)
619 {
620 dbg("Write size diff %d %d", size, written);
621 return false;
622 }
623 return true;
624#else
625 const FsString* pathString = utf8ToString(name);
626 FILE* file = tic_fopen(pathString, _S("wb"));
627 freeString(pathString);
628
629 if(file)
630 {
631 fwrite(buffer, 1, size, file);
632 fclose(file);
633
634#if defined(__EMSCRIPTEN__)
635 syncfs();
636#endif
637
638 return true;
639 }
640
641 return false;
642#endif
643}
644
645void* fs_read(const char* path, s32* size)
646{
647#if defined(BAREMETALPI)
648 dbg("fs_read %s\n", path);
649 FILINFO fi;
650 FRESULT res = f_stat(path, &fi);
651 if(res!=FR_OK) return NULL;
652 FIL file;
653 res = f_open (&file, path, FA_READ | FA_OPEN_EXISTING);
654 if(res!=FR_OK) return NULL;
655 *size = fi.fsize; // size is in output!
656 void* buffer = malloc(*size);
657 UINT read = 0;
658 res = f_read(&file, buffer, fi.fsize, &read);
659
660 f_close(&file);
661 if(read!=(*size)) return NULL;
662
663 return buffer;
664
665#else
666 const FsString* pathString = utf8ToString(path);
667 FILE* file = tic_fopen(pathString, _S("rb"));
668 freeString(pathString);
669
670 void* buffer = NULL;
671
672 if(file)
673 {
674
675 fseek(file, 0, SEEK_END);
676 *size = ftell(file);
677 fseek(file, 0, SEEK_SET);
678
679 if((buffer = malloc(*size)) && fread(buffer, *size, 1, file)) {}
680
681 fclose(file);
682 }
683
684 return buffer;
685
686#endif
687}
688
689bool fs_exists(const char* name)
690{
691#if defined(BAREMETALPI)
692 dbg("fs_exists %s\n", name);
693 FILINFO s;
694
695 FRESULT res = f_stat(name, &s);
696 return res == FR_OK;
697#else
698 struct tic_stat_struct s;
699
700 const FsString* pathString = utf8ToString(name);
701 bool ret = tic_stat(pathString, &s) == 0;
702 freeString(pathString);
703
704 return ret;
705#endif
706}
707
708bool tic_fs_exists(tic_fs* fs, const char* name)
709{
710 return fs_exists(tic_fs_path(fs, name));
711}
712
713u64 fs_date(const char* path)
714{
715#if defined(BAREMETALPI)
716 dbg("fs_date %s\n", path);
717 // TODO BAREMETALPI
718 return 0;
719#else
720 struct tic_stat_struct s;
721
722 const FsString* pathString = utf8ToString(path);
723 s32 ret = tic_stat(pathString, &s);
724 freeString(pathString);
725
726 if(ret == 0 && S_ISREG(s.st_mode))
727 {
728 return s.st_mtime;
729 }
730
731 return 0;
732#endif
733}
734
735bool tic_fs_save(tic_fs* fs, const char* name, const void* data, s32 size, bool overwrite)
736{
737 if(!overwrite)
738 {
739 if(tic_fs_exists(fs, name))
740 return false;
741 }
742
743 return fs_write(tic_fs_path(fs, name), data, size);
744}
745
746bool tic_fs_saveroot(tic_fs* fs, const char* name, const void* data, s32 size, bool overwrite)
747{
748 const char* path = tic_fs_pathroot(fs, name);
749
750 if(!overwrite)
751 {
752 if(fs_exists(path))
753 return false;
754 }
755
756 return fs_write(path, data, size);
757}
758
759typedef struct
760{
761 tic_fs* fs;
762 fs_load_callback done;
763 void* data;
764 char* cachePath;
765} LoadFileByHashData;
766
767static void fileByHashLoaded(const net_get_data* netData)
768{
769 LoadFileByHashData* loadFileByHashData = netData->calldata;
770
771 if (netData->type == net_get_done)
772 {
773 tic_fs_saveroot(loadFileByHashData->fs, loadFileByHashData->cachePath, netData->done.data, netData->done.size, false);
774 loadFileByHashData->done(netData->done.data, netData->done.size, loadFileByHashData->data);
775 }
776
777 switch (netData->type)
778 {
779 case net_get_done:
780 case net_get_error:
781
782 free(loadFileByHashData->cachePath);
783 free(loadFileByHashData);
784 break;
785 default: break;
786 }
787}
788
789void tic_fs_hashload(tic_fs* fs, const char* name, const char* hash, fs_load_callback callback, void* data)
790{
791#if defined(BAREMETALPI)
792 // TODO BAREMETALPI
793 return;
794#else
795
796 char cachePath[TICNAME_MAX];
797 snprintf(cachePath, sizeof cachePath, TIC_CACHE "%s.tic", hash);
798
799 {
800 s32 size = 0;
801 void* buffer = tic_fs_loadroot(fs, cachePath, &size);
802 if (buffer)
803 {
804 callback(buffer, size, data);
805 free(buffer);
806 return;
807 }
808 }
809
810#if defined(BUILD_EDITORS)
811 char path[TICNAME_MAX];
812 snprintf(path, sizeof path, "/cart/%s/%s", hash, name);
813
814 LoadFileByHashData loadFileByHashData = { fs, callback, data, strdup(cachePath) };
815 tic_net_get(fs->net, path, fileByHashLoaded, MOVE(loadFileByHashData));
816#endif
817
818#endif
819}
820
821void* tic_fs_load(tic_fs* fs, const char* name, s32* size)
822{
823#if defined(BAREMETALPI)
824 dbg("tic_fs_load x %s\n", name);
825 dbg("fs.dir %s\n", fs->dir);
826 dbg("fs.work %s\n", fs->work);
827
828 if(isPublic(fs))
829 {
830 dbg("Public ??\n");
831 return NULL;
832 }
833 else
834 {
835 dbg("non public \n");
836 const char* fp = tic_fs_path(fs, name);
837 dbg("loading: %s\n", fp);
838
839
840 FILINFO fi;
841 FRESULT res = f_stat(fp, &fi);
842 dbg("fstat done %d \n", res);
843
844 if(res!=FR_OK)
845 {
846 dbg("NO F_STAT %d\n", res);
847 return NULL;
848 }
849 FIL file;
850 res = f_open (&file, fp, FA_READ | FA_OPEN_EXISTING);
851 if(res!=FR_OK)
852 {
853 dbg("NO F_OPEN %d\n", res);
854 return NULL;
855 }
856 dbg("BUFFERING %d\n", res);
857
858 void* buffer = malloc(fi.fsize);
859 dbg("BUFFERED %d\n", fi.fsize);
860
861 UINT read = 0;
862 res = f_read(&file, buffer, fi.fsize, &read);
863 dbg("F_READ %d %ld\n", res, read);
864
865 f_close(&file);
866 if(read!=fi.fsize)
867 {
868 dbg("NO F_READ %d \n", res);
869 return NULL;
870 }
871 dbg("RETURNING!!\n");
872 *size = fi.fsize;
873 return buffer;
874 }
875 return NULL;
876#else
877
878 const FsString* pathString = utf8ToString(tic_fs_path(fs, name));
879 FILE* file = tic_fopen(pathString, _S("rb"));
880 freeString(pathString);
881
882 void* ptr = NULL;
883
884 if(file)
885 {
886 fseek(file, 0, SEEK_END);
887 *size = ftell(file);
888 fseek(file, 0, SEEK_SET);
889
890 u8* buffer = malloc(*size);
891
892 if(buffer && fread(buffer, *size, 1, file)) ptr = buffer;
893
894 fclose(file);
895 }
896
897 return ptr;
898
899#endif
900}
901
902void* tic_fs_loadroot(tic_fs* fs, const char* name, s32* size)
903{
904 return fs_read(tic_fs_pathroot(fs, name), size);
905}
906
907bool tic_fs_makedir(tic_fs* fs, const char* name)
908{
909#if defined(BAREMETALPI)
910 // TODO BAREMETALPI
911 dbg("makeDir %s\n", name);
912
913 const FsString* pathString = tic_fs_path(fs, name);
914 char* path = strdup(pathString);
915 if (path && *path) { // make sure result has at least
916 if (path[strlen(path) - 1] == '/') // one character
917 path[strlen(path) - 1] = 0;
918 }
919
920 FRESULT res = f_mkdir(path);
921 if(res != FR_OK)
922 {
923 dbg("Could not mkdir %s\n", name);
924 }
925 free(path);
926 return (res != FR_OK);
927#else
928
929 const FsString* pathString = utf8ToString(tic_fs_path(fs, name));
930 int result = tic_mkdir(pathString);
931 freeString(pathString);
932
933#if defined(__EMSCRIPTEN__)
934 syncfs();
935#endif
936 return result;
937#endif
938}
939
940void tic_fs_openfolder(tic_fs* fs)
941{
942 const char* path = tic_fs_path(fs, "");
943
944 if(isPublic(fs))
945 path = fs->dir;
946
947 tic_sys_open_path(path);
948}
949
950#if defined(__TIC_WINDOWS__)
951#define SEP "\\"
952#else
953#define SEP "/"
954#endif
955
956tic_fs* tic_fs_create(const char* path, tic_net* net)
957{
958 tic_fs* fs = (tic_fs*)calloc(1, sizeof(tic_fs));
959
960 strncpy(fs->dir, path, TICNAME_MAX);
961
962 if(path[strlen(path) - 1] != SEP[0])
963 strcat(fs->dir, SEP);
964
965 fs->net = net;
966
967 return fs;
968}
969
970const char* fs_apppath()
971{
972 static char apppath[TICNAME_MAX];
973
974#if defined(__TIC_WINDOWS__)
975 {
976 wchar_t wideAppPath[TICNAME_MAX];
977 GetModuleFileNameW(NULL, wideAppPath, sizeof wideAppPath);
978 WideCharToMultiByte(CP_UTF8, 0, wideAppPath, COUNT_OF(wideAppPath), apppath, COUNT_OF(apppath), 0, 0);
979 }
980#elif defined(__TIC_LINUX__)
981 s32 size = readlink("/proc/self/exe", apppath, sizeof apppath);
982 apppath[size] = '\0';
983#elif defined(__TIC_MACOSX__)
984 u32 size = sizeof apppath;
985 _NSGetExecutablePath(apppath, &size);
986#endif
987
988 return apppath;
989}
990
991const char* fs_appfolder()
992{
993 static char appfolder[TICNAME_MAX];
994 strcpy(appfolder, fs_apppath());
995
996 char* pos = strrchr(appfolder, SLASH_SYMBOL);
997
998 if(pos && pos[1])
999 pos[1] = '\0';
1000
1001 return appfolder;
1002}