1 /*---------------------------------------------------------------------------*
2
3 Copyright 2010-2012 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
534 // --------------------------------------------------------
535