1 /*---------------------------------------------------------------------------*
2 
3   Copyright (C) Nintendo.  All rights reserved.
4 
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law.  They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10 
11  *---------------------------------------------------------------------------*/
12 ////===========================================================================
13 ///  DEMO_Fs.c
14 ///
15 ///     This is file system code for the DEMO library.
16 ///
17 ////===========================================================================
18 
19 #include <cafe/demo.h>
20 #include <nn/save.h>
21 
22 static u32 _DEMOFSInitRefCount = 0;
23 static FSClient _DEMOFSClient;
24 
25 /*---------------------------------------------------------------------------*
26   Name:         stateChangeCallback
27 
28   Description:  Handler of state change notification.
29                 Never invoked in this demo.
30  *---------------------------------------------------------------------------*/
stateChangeCallback(FSClient * pClient,FSVolumeState state,void * pContext)31 static void stateChangeCallback( FSClient* pClient,
32                                  FSVolumeState state,
33                                  void* pContext)
34 {
35     // Report volume state and last error
36     FSError lastError = FSGetLastError(pClient);
37     OSReport("Volume state of client 0x%08x changed to %d\n", pClient, state);
38     OSReport("Last error: %d\n", lastError);
39 }
40 
41 // -------------------------------------------------------
42 
43 // "Safe" string concat function
44 
45 // Like strncat, but len = size of dst buffer
strlcat(char * dst,const char * src,size_t len)46 static inline char *strlcat(char *dst, const char *src, size_t len)
47 {
48     int n=strlen(dst);
49     if (n>len-1) n=len-1;
50     return strncat(dst, src, len-1-n);
51 }
52 
53 // -------------------------------------------------------
54 
55 // Startup the FS subsystem if it isn't already up and running.
56 // Note that demo libraries only initialize this once, but never release it.
_DemoInitFS(void)57 static s32 _DemoInitFS(void)
58 {
59     if (!_DEMOFSInitRefCount)
60     {
61         FSInit();
62         FSStatus result = FSAddClient(&_DEMOFSClient, FS_RET_ALL_ERROR);
63         if (result != FS_STATUS_OK)
64         {
65             DEMOPrintf("DEMOFS Error: FSAddClient() returned: %d!\n", result);
66             return DEMO_FS_RESULT_FATAL_ERROR;
67         }
68         FSStateChangeParams stateChangeParams = {
69             .userCallback = stateChangeCallback,
70             .userContext  = NULL,
71             .ioMsgQueue   = NULL
72         };
73         FSSetStateChangeNotification(&_DEMOFSClient, &stateChangeParams);
74 
75         // Do not call SAVEInit() every time.
76         // Application should call SAVEInit().
77 
78         // Do not call SAVEInitSaveDir() every time.
79         // Application should call SAVEInitSaveDir() if the save directory does not exist.
80     }
81     _DEMOFSInitRefCount++;
82 
83     return DEMO_FS_RESULT_OK;
84 }
85 
86 
DEMOFSOpenFile(const char * path,DEMOFSFileInfo * info)87 s32 DEMOFSOpenFile(const char* path, DEMOFSFileInfo* info)
88 {
89     return DEMOFSOpenFileMode(path, info, "r");
90 }
91 
92 
DEMOFSOpenFileMode(const char * path,DEMOFSFileInfo * info,const char * mode)93 s32 DEMOFSOpenFileMode(const char* path, DEMOFSFileInfo* info, const char* mode)
94 {
95     FSStatus result = FS_STATUS_OK;
96     char openPath[256];
97     FSCmdBlock* pCmd;
98 
99     if (path==NULL) {
100         DEMOPrintf("DEMOFS Error: NULL path!\n");
101         return DEMO_FS_RESULT_FATAL_ERROR;
102     }
103 
104     result = _DemoInitFS();
105     if (DEMO_FS_RESULT_OK != result)
106     {
107         DEMOPrintf("DEMOFS Error: _DemoInitFS failed!\n");
108         return DEMO_FS_RESULT_FATAL_ERROR;
109     }
110 
111     if (path[0]!='/') {
112         strncpy(openPath, "/vol/content/", 256);
113     } else {
114         openPath[0]=0;
115     }
116     strlcat(openPath, path, 256);
117 
118     pCmd = DEMOAlloc(sizeof(FSCmdBlock));
119     if( pCmd == NULL )
120     {
121         DEMOPrintf("DEMOFS Error: DEMOAlloc failed!\n");
122         return DEMO_FS_RESULT_FATAL_ERROR;
123     }
124     FSInitCmdBlock(pCmd);
125 
126     result = FSOpenFile(&_DEMOFSClient, pCmd, openPath, mode, info, FS_RET_ALL_ERROR);
127 
128     DEMOFree(pCmd);
129 
130     if (FS_STATUS_OK != result)
131     {
132         DEMOPrintf("DEMOFS Error: FSOpenFile(%s) returned %d!\n", path, result);
133         return DEMO_FS_RESULT_FATAL_ERROR;
134     }
135 
136     return DEMO_FS_RESULT_OK;
137 }
138 
139 
DEMOFSGetLength(const DEMOFSFileInfo * fileInfo,u32 * length)140 s32 DEMOFSGetLength( const DEMOFSFileInfo* fileInfo, u32* length )
141 {
142     u32 sLength;
143     FSStat stat;
144     FSStatus result;
145     FSCmdBlock* pCmd;
146 
147     pCmd = DEMOAlloc(sizeof(FSCmdBlock));
148     if( pCmd == NULL )
149     {
150         DEMOPrintf("DEMOFS Error: DEMOAlloc failed!\n");
151         return DEMO_FS_RESULT_FATAL_ERROR;
152     }
153     FSInitCmdBlock(pCmd);
154 
155     result = FSGetStatFile(&_DEMOFSClient, pCmd, *fileInfo, &stat, FS_RET_ALL_ERROR);
156 
157     DEMOFree(pCmd);
158 
159     if(result != FS_STATUS_OK)
160     {
161         DEMOPrintf("DEMOFS Error: FSStatFile returned %d\n", result);
162         return DEMO_FS_RESULT_FATAL_ERROR;
163     }
164 
165     sLength = stat.size;
166     *length = sLength;
167 
168     return DEMO_FS_RESULT_OK;
169 }
170 
DEMOFSRead(DEMOFSFileInfo * fileInfo,void * addr,s32 length,s32 offset)171 s32 DEMOFSRead( DEMOFSFileInfo* fileInfo, void* addr, s32 length, s32 offset )
172 {
173     FSStatus result;
174     FSCmdBlock* pCmd;
175 
176     pCmd = DEMOAlloc(sizeof(FSCmdBlock));
177     if( pCmd == NULL )
178     {
179         DEMOPrintf("DEMOFS Error: DEMOAlloc failed!\n");
180         return DEMO_FS_RESULT_FATAL_ERROR;
181     }
182     FSInitCmdBlock(pCmd);
183 
184     result = FSReadFileWithPos(&_DEMOFSClient, pCmd, addr, (u32)length, 1, (u32)offset, *fileInfo, 0, FS_RET_ALL_ERROR);
185 
186     DEMOFree(pCmd);
187 
188     if( 0 > result )
189     {
190         DEMOPrintf("DEMOFS Error: FSReadFile returned: %d\n", result);
191         return DEMO_FS_RESULT_FATAL_ERROR;
192     }
193     return DEMO_FS_RESULT_OK;
194 }
195 
DEMOFSWrite(DEMOFSFileInfo * fileInfo,const void * addr,s32 length)196 s32 DEMOFSWrite(DEMOFSFileInfo* fileInfo, const void* addr, s32 length)
197 {
198     FSStatus result;
199     FSCmdBlock* pCmd;
200 
201     pCmd = DEMOAlloc(sizeof(FSCmdBlock));
202     if( pCmd == NULL )
203     {
204         DEMOPrintf("DEMOFS Error: DEMOAlloc failed!\n");
205         return DEMO_FS_RESULT_FATAL_ERROR;
206     }
207     FSInitCmdBlock(pCmd);
208 
209     result = FSWriteFile(&_DEMOFSClient, pCmd, (void *)addr, 1, (u32)length, *fileInfo, 0, FS_RET_ALL_ERROR);
210 
211     DEMOFree(pCmd);
212 
213     if( 0 > result )
214     {
215         DEMOPrintf("DEMOFS Error: FSWriteFile returned: %d\n", result);
216         return DEMO_FS_RESULT_FATAL_ERROR;
217     }
218     else if (result != length)
219     {
220         DEMOPrintf("DEMOFS Error: FSWriteFile actual write count %d != requested write count %d.\n", result, length);
221         return DEMO_FS_RESULT_FATAL_ERROR;
222     }
223     return DEMO_FS_RESULT_OK;
224 }
225 
DEMOFSCloseFile(DEMOFSFileInfo * fileInfo)226 s32 DEMOFSCloseFile( DEMOFSFileInfo* fileInfo )
227 {
228     FSStatus result;
229     FSCmdBlock* pCmd;
230 
231     pCmd = DEMOAlloc(sizeof(FSCmdBlock));
232     if( pCmd == NULL )
233     {
234         DEMOPrintf("DEMOFS Error: DEMOAlloc failed!\n");
235         return DEMO_FS_RESULT_FATAL_ERROR;
236     }
237     FSInitCmdBlock(pCmd);
238 
239     result = FSCloseFile(&_DEMOFSClient, pCmd, *fileInfo, FS_RET_ALL_ERROR);
240 
241     DEMOFree(pCmd);
242 
243     if(FS_STATUS_OK != result )
244     {
245         DEMOPrintf("DEMOFS Error: FSCloseFile returned %d\n", result);
246         return DEMO_FS_RESULT_FATAL_ERROR;
247     }
248 
249     return DEMO_FS_RESULT_OK;
250 }
251 
252 
DEMOFSSimpleRead(const char * filename,u32 * len)253 void * DEMOFSSimpleRead(const char * filename, u32 * len)
254 {
255     DEMOFSFileInfo file;
256     u32 fileLen;
257     u32 fileAllocLen;
258     s32 fileRet;
259     void *fileBuf = NULL;
260 
261     fileRet = DEMOFSOpenFile(filename, &file);
262     DEMOAssert(fileRet == DEMO_FS_RESULT_OK && "couldn't open file");
263 
264     fileRet = DEMOFSGetLength(&file, &fileLen);
265     DEMOAssert(fileRet == DEMO_FS_RESULT_OK && "couldn't get file length");
266     fileAllocLen = DEMORoundUpForIO(fileLen);
267 
268     fileBuf = DEMOAllocEx(fileAllocLen, PPC_IO_BUFFER_ALIGN);
269     DEMOAssert(fileBuf && "couldn't alloc memory");
270     fileRet = DEMOFSRead(&file, fileBuf, (s32)fileAllocLen, 0);
271     DEMOAssert(fileRet == DEMO_FS_RESULT_OK && "couldn't read file");
272 
273     fileRet = DEMOFSCloseFile(&file);
274     if (fileRet != DEMO_FS_RESULT_OK)
275     {
276         DEMOAssert(!"couldn't close file");
277     }
278 
279     *len = fileLen;
280     return fileBuf;
281 }
282 
DEMOFSSimpleReadAlign(const char * filename,u32 * len,u32 alignSize)283 void * DEMOFSSimpleReadAlign(const char * filename, u32 * len, u32 alignSize)
284 {
285     DEMOFSFileInfo file;
286     u32 fileLen;
287     u32 fileAllocLen;
288     s32 fileRet;
289     void *fileBuf = NULL;
290 
291     fileRet = DEMOFSOpenFile(filename, &file);
292     DEMOAssert(fileRet == DEMO_FS_RESULT_OK && "couldn't open file");
293 
294     fileRet = DEMOFSGetLength(&file, &fileLen);
295     DEMOAssert(fileRet == DEMO_FS_RESULT_OK && "couldn't get file length");
296     fileAllocLen = DEMORoundUpForIO(fileLen);
297 
298     fileBuf = DEMOAllocEx(fileAllocLen, alignSize);
299     DEMOAssert(fileBuf && "couldn't alloc memory");
300     fileRet = DEMOFSRead(&file, fileBuf, (s32)fileAllocLen, 0);
301     DEMOAssert(fileRet == DEMO_FS_RESULT_OK && "couldn't read file");
302 
303     fileRet = DEMOFSCloseFile(&file);
304     if (fileRet != DEMO_FS_RESULT_OK)
305     {
306         DEMOAssert(!"couldn't close file");
307     }
308 
309     *len = fileLen;
310     return fileBuf;
311 }
312 
DEMOFSScanDir(const char * pSearchPath,const char * pPrefixPath,u32 * pFileCount,u32 MaxFiles,char ** ppFileNames)313 s32 DEMOFSScanDir(const char *pSearchPath, const char *pPrefixPath, u32 *pFileCount, u32 MaxFiles, char** ppFileNames )
314 {
315     FSStatus        result;
316     FSDirHandle    dh;
317     FSDirEntry     dirent;
318     u32             fileCount = 0;
319     u32             dirCount = 0;
320     char            dirPath[256] = {0};
321     FSCmdBlock*     pCmd;
322 
323 
324     if (pSearchPath==NULL) {
325         DEMOPrintf("DEMOFS Error: NULL path!\n");
326         return DEMO_FS_RESULT_FATAL_ERROR;
327     }
328 
329     result = _DemoInitFS();
330     if (DEMO_FS_RESULT_OK != result)
331     {
332         DEMOPrintf("DEMOFS Error: _DemoInitFS failed!\n");
333         return DEMO_FS_RESULT_FATAL_ERROR;
334     }
335 
336     // is pathname too long?
337     if(strlen(pSearchPath) >= sizeof(dirPath) - (pSearchPath[0]=='/' ? 0 : sizeof("/vol/content/")))
338     {
339         DEMOPrintf("DEMO Error: pathname is too long: %s!\n", pSearchPath);
340         return DEMO_FS_RESULT_FATAL_ERROR;
341     }
342 
343     if (pSearchPath[0]!='/') {
344         strncpy(dirPath, "/vol/content/", 256);
345     } else {
346         dirPath[0]=0;
347     }
348     strlcat(dirPath, pSearchPath, 256);
349 
350     pCmd = DEMOAlloc(sizeof(FSCmdBlock));
351     if( pCmd == NULL )
352     {
353         DEMOPrintf("DEMOFS Error: DEMOAlloc failed!\n");
354         return DEMO_FS_RESULT_FATAL_ERROR;
355     }
356     FSInitCmdBlock(pCmd);
357 
358     // open directory (include the volume)
359     result = FSOpenDir(&_DEMOFSClient, pCmd, dirPath, &dh, FS_RET_ALL_ERROR);
360     if(result != FS_STATUS_OK)
361     {
362         DEMOFree(pCmd);
363         DEMOPrintf("DEMO Error: FSOpenDir(%s) returned: %d!\n", dirPath, result);
364         return DEMO_FS_RESULT_NOT_FOUND;
365     }
366 
367     // how many files in directory?  What are their names?
368     // (may be able to get this from parent directory via the
369     //     dirent.stat.size, but that means we need to open the parent up)
370     while( FS_STATUS_END !=
371            (result = FSReadDir(&_DEMOFSClient, pCmd, dh, &dirent, FS_RET_ALL_ERROR)) )
372     {
373         if (FS_STATUS_OK != result)
374         {
375             DEMOFree(pCmd);
376             DEMOPrintf("DEMO Error: FSReadDir(%s) returned: %d!\n", dirPath, result);
377             return DEMO_FS_RESULT_FATAL_ERROR;
378         }
379 
380         if (dirent.stat.flag & FS_STAT_FLAG_IS_DIRECTORY)
381         {
382             dirCount++;
383         }
384         else /* this is file */
385         {
386             if(fileCount < MaxFiles)
387             {
388               // get file name (not including the volume, DemoSimpleRead puts it back on)
389                 const u32 MaxNameLen = 256;
390                 u32 nameLen = strlen(dirPath) + 1 + strlen(dirent.name);
391 
392                     // be safe, if file name too long, don't return it. (Assert)!
393                 if(nameLen < MaxNameLen)
394                 {
395 
396                     char *pBuff = DEMOAlloc(nameLen+1);   // allocate new space for this string
397                     DEMOAssert(pBuff && "couldn't alloc memory");
398                     strncpy(pBuff, pPrefixPath, nameLen+1);
399                     if(pBuff[0] && pBuff[strlen(pBuff)-1] != '/')
400                         strlcat(pBuff, "/", nameLen+1);
401                     strlcat(pBuff, dirent.name, nameLen+1);
402 
403                     ppFileNames[fileCount] = pBuff;
404                     fileCount++;
405                 } else {
406                     DEMOFree(pCmd);
407                     DEMOPrintf("DEMO Error: filename found in dir that is too long\n");
408                     return DEMO_FS_RESULT_FATAL_ERROR;
409                 }
410             }
411         }
412     }
413     *pFileCount = fileCount;
414 
415     // now, reached to the end of directory. close directory
416     result = FSCloseDir(&_DEMOFSClient, pCmd, dh, FS_RET_ALL_ERROR);
417 
418     DEMOFree(pCmd);
419 
420     if(result != FS_STATUS_OK)
421     {
422         DEMOPrintf("DEMO Error: Failed to close dir %s!\n", dirPath);
423         return DEMO_FS_RESULT_FATAL_ERROR;
424     }
425 
426     return DEMO_FS_RESULT_OK;
427 }
428 
DEMOFSSimpleScanDir(const char * pPath,u32 * pFileCount,u32 MaxFiles,char ** ppFileNames)429 s32 DEMOFSSimpleScanDir(const char *pPath, u32 *pFileCount, u32 MaxFiles, char** ppFileNames )
430 {
431     return DEMOFSScanDir(pPath, pPath, pFileCount, MaxFiles, ppFileNames);
432 }
433 
DEMOFSRename(const char * oldpath,const char * newpath)434 s32 DEMOFSRename(const char *oldpath, const char *newpath)
435 {
436     FSStatus result = FS_STATUS_OK;
437     char prefixedOldPath[256];
438     char prefixedNewPath[256];
439     FSCmdBlock* pCmd;
440 
441     if (oldpath==NULL || newpath==NULL) {
442         DEMOPrintf("DEMOFS Error: NULL path!\n");
443         return DEMO_FS_RESULT_FATAL_ERROR;
444     }
445 
446     result = _DemoInitFS();
447     if (DEMO_FS_RESULT_OK != result)
448     {
449         DEMOPrintf("DEMOFS Error: _DemoInitFS failed!\n");
450         return DEMO_FS_RESULT_FATAL_ERROR;
451     }
452 
453     if (oldpath[0]!='/') {
454         strncpy(prefixedOldPath, "/vol/content/", 256);
455     } else {
456         prefixedOldPath[0]=0;
457     }
458     strlcat(prefixedOldPath, oldpath, 256);
459 
460     if (newpath[0]!='/') {
461         strncpy(prefixedNewPath, "/vol/content/", 256);
462     } else {
463         prefixedNewPath[0]=0;
464     }
465     strlcat(prefixedNewPath, newpath, 256);
466 
467     pCmd = DEMOAlloc(sizeof(FSCmdBlock));
468     if( pCmd == NULL )
469     {
470         DEMOPrintf("DEMOFS Error: DEMOAlloc failed!\n");
471         return DEMO_FS_RESULT_FATAL_ERROR;
472     }
473     FSInitCmdBlock(pCmd);
474 
475     result = FSRename(&_DEMOFSClient, pCmd, prefixedOldPath, prefixedNewPath, FS_RET_ALL_ERROR);
476 
477     DEMOFree(pCmd);
478 
479     if (FS_STATUS_OK != result)
480     {
481         DEMOPrintf("DEMOFS Error: FSRename(%s,%s) returned %d!\n", oldpath, newpath, result);
482         return DEMO_FS_RESULT_FATAL_ERROR;
483     }
484 
485     return DEMO_FS_RESULT_OK;
486 }
487 
DEMOFSRemove(const char * path,BOOL ignoreError)488 s32 DEMOFSRemove(const char *path, BOOL ignoreError)
489 {
490     FSStatus result = FS_STATUS_OK;
491     char prefixedPath[256];
492     FSCmdBlock* pCmd;
493 
494     if (path==NULL) {
495         DEMOPrintf("DEMOFS Error: NULL path!\n");
496         return DEMO_FS_RESULT_FATAL_ERROR;
497     }
498 
499     result = _DemoInitFS();
500     if (DEMO_FS_RESULT_OK != result)
501     {
502         DEMOPrintf("DEMOFS Error: _DemoInitFS failed!\n");
503         return DEMO_FS_RESULT_FATAL_ERROR;
504     }
505 
506     if (path[0]!='/') {
507         strncpy(prefixedPath, "/vol/content/", 256);
508     } else {
509         prefixedPath[0]=0;
510     }
511     strlcat(prefixedPath, path, 256);
512 
513     pCmd = DEMOAlloc(sizeof(FSCmdBlock));
514     if( pCmd == NULL )
515     {
516         DEMOPrintf("DEMOFS Error: DEMOAlloc failed!\n");
517         return DEMO_FS_RESULT_FATAL_ERROR;
518     }
519     FSInitCmdBlock(pCmd);
520 
521     result = FSRemove(&_DEMOFSClient, pCmd, prefixedPath, FS_RET_ALL_ERROR);
522 
523     DEMOFree(pCmd);
524 
525     if (FS_STATUS_OK != result && !ignoreError)
526     {
527         DEMOPrintf("DEMOFS Error: FSRemove(%s) returned %d!\n", path, result);
528         return DEMO_FS_RESULT_FATAL_ERROR;
529     }
530 
531     return DEMO_FS_RESULT_OK;
532 }
533 
DEMOFSFileExists(const char * path)534 BOOL DEMOFSFileExists(const char *path)
535 {
536     DEMOFSFileInfo file;
537 
538     if (DEMOFSOpenFile(path, &file) != DEMO_FS_RESULT_OK)
539         return FALSE;
540 
541     s32 result = DEMOFSCloseFile(&file);
542     DEMOAssert(result == DEMO_FS_RESULT_OK && "couldn't close file!");
543 
544     return TRUE;
545 }
546 
547 // --------------------------------------------------------
548