1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     lyt_Arc.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 15741 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/lyt/lyt_Arc.h>
19 #include <ctype.h>          // for tolower
20 
21 namespace nw
22 {
23 namespace lyt
24 {
25 
26 typedef struct FSTEntry FSTEntry;
27 
28 struct FSTEntry
29 {
30     unsigned int    isDirAndStringOff;    // the first byte is for isDir
31                                           // the next 3bytes: name offset
32     unsigned int    parentOrPosition;     // parent entry (dir entry)
33                                           // position (file entry)
34     unsigned int    nextEntryOrLength;    // next entry (dir entry)
35                                           // length (file entry)
36 };
37 
38 #define entryIsDir(fstStart, i)     \
39     ( ( ( fstStart[i].isDirAndStringOff & 0xff000000 ) == 0 )? false:true )
40 #define stringOff(fstStart, i)      \
41         ( fstStart[i].isDirAndStringOff & 0x00ffffff )
42 #define parentDir(fstStart, i)       \
43         ( fstStart[i].parentOrPosition )
44 #define nextDir(fstStart, i)        \
45         ( fstStart[i].nextEntryOrLength )
46 #define filePosition(fstStart, i)       \
47         ( fstStart[i].parentOrPosition )
48 #define fileLength(fstStart, i)         \
49         ( fstStart[i].nextEntryOrLength )
50 
51 namespace
52 {
53 
54 inline
55 wchar_t*
GetStringPtr(wchar_t * str,size_t offset)56 GetStringPtr(
57     wchar_t*    str,
58     size_t      offset
59 )
60 {
61     return
62         reinterpret_cast<wchar_t*>(
63             reinterpret_cast<u8*>(str) + offset
64         );
65 }
66 
67 inline
68 const wchar_t*
GetStringPtr(const wchar_t * str,size_t offset)69 GetStringPtr(
70     const wchar_t*  str,
71     size_t          offset
72 )
73 {
74     return
75         reinterpret_cast<const wchar_t*>(
76             reinterpret_cast<const u8*>(str) + offset
77         );
78 }
79 
80 #if defined(_MSC_VER) && _MSC_VER >= 1500
81     #pragma warning(push)
82     #pragma warning(disable: 4996)
83 #endif
84 
85 inline
86 size_t
WcsToMbs(char * mbstr,const wchar_t * wcstr,size_t count)87 WcsToMbs(
88    char*            mbstr,
89    const wchar_t*   wcstr,
90    size_t           count
91 )
92 {
93     return std::wcstombs(mbstr, wcstr, count);
94 }
95 
96 #if defined(_MSC_VER) && _MSC_VER >= 1500
97     #pragma warning(pop)
98 #endif
99 
100 }   // namespace
101 
ARCInitHandle(void * arcStart,ARCHandle * handle)102 bool ARCInitHandle(void* arcStart, ARCHandle* handle)
103 {
104     FSTEntry*           FSTEntries;
105     ARCHeader*          arcHeader;
106 
107     arcHeader = (ARCHeader*)arcStart;
108 
109     NN_ASSERTMSG(arcHeader->signature == DARCH_SIGNATURE,       "ARCInitHandle: bad archive format");
110     NN_ASSERTMSG(arcHeader->byteOrder == DARCH_BYTE_ORDER_MARK, "ARCInitHandle: bad archive format");
111     NN_ASSERTMSG(arcHeader->version   == DARCH_VERSION,         "ARCInitHandle: bad archive format");
112 
113     handle->archiveStartAddr = arcStart;
114     handle->FSTStart = FSTEntries = reinterpret_cast<FSTEntry*>((u32)arcStart + arcHeader->fstStart);
115     handle->fileStart = (void*)((u32)arcStart + arcHeader->fileStart);
116 
117     NN_ASSERTMSG(FSTEntries != NULL, "ARCInitHandle: bad archive format");
118 
119     handle->entryNum = nextDir(FSTEntries, 0);
120     handle->FSTStringStart = reinterpret_cast<wchar_t*>(&(FSTEntries[handle->entryNum]));
121     handle->FSTLength = (u32)arcHeader->fstSize;
122     handle->currDir = 0;
123 
124     return true;
125 }
126 
ARCOpen(ARCHandle * handle,const wchar_t * fileName,ARCFileInfo * af)127 bool ARCOpen(ARCHandle* handle, const wchar_t* fileName, ARCFileInfo* af)
128 {
129     s32         entry;
130     FSTEntry*   FSTEntries;
131 
132     NN_ASSERTMSG( handle, "ARCOpen(): NULL pointer is specified to ARCHandle structure" );
133     NN_ASSERTMSG( fileName, "ARCOpen(): NULL pointer is specified to fileName" );
134     NN_ASSERTMSG( af, "ARCOpen(): NULL pointer is specified to ARCFileInfo structure" );
135 
136     FSTEntries = (FSTEntry*)handle->FSTStart;
137 
138     entry = ARCConvertPathToEntrynum(handle, fileName);
139 
140 #if defined(NW_DEBUG)
141     if (0 > entry)
142     {
143         const size_t BufMax = 127;
144         wchar_t     currentDir[BufMax + 1];
145         char chBuf[BufMax + 1];
146         WcsToMbs(chBuf, fileName, BufMax);
147         chBuf[BufMax] = '\0';
148         NN_LOG("Warning: ARCOpen(): file '%s' was not found", chBuf);
149         ARCGetCurrentDir(handle, currentDir, BufMax + 1);
150         WcsToMbs(chBuf, currentDir, BufMax);
151         chBuf[BufMax] = '\0';
152         NN_LOG(" under %s in the archive.\n", chBuf);
153         NN_ASSERT(false);
154     }
155 #endif
156 
157     NN_ASSERTMSG( !entryIsDir(FSTEntries, entry), "ARCOpen(): %s is a directory", fileName );
158 
159     if ( (entry < 0) || entryIsDir(FSTEntries, entry) )
160     {
161         return false;
162     }
163 
164     af->handle      = handle;
165     af->startOffset = filePosition(FSTEntries, entry);
166     af->length      = fileLength(FSTEntries, entry);
167 
168     return true;
169 }
170 
ARCFastOpen(ARCHandle * handle,s32 entrynum,ARCFileInfo * af)171 bool ARCFastOpen(ARCHandle* handle, s32 entrynum, ARCFileInfo* af)
172 {
173     FSTEntry*           FSTEntries;
174 
175     NN_ASSERTMSG(handle, "ARCFastOpen(): null pointer is specified to ARCHandle address  ");
176     NN_ASSERTMSG(af,
177               "ARCFastOpen(): null pointer is specified to ARCFileInfo address  ");
178     NN_ASSERTMSG((0 <= entrynum) && (entrynum < static_cast<s32>(handle->entryNum)),
179                "ARCFastOpen(): specified entry number '%d' is out of range  ",
180                entrynum);
181 
182     FSTEntries = (FSTEntry*)handle->FSTStart;
183 
184     NN_ASSERTMSG(!entryIsDir(FSTEntries, entrynum),
185                "ARCFastOpen(): entry number '%d' is assigned to a directory  ",
186                entrynum);
187 
188     if ( (entrynum < 0) || (entrynum >= static_cast<s32>(handle->entryNum)) ||
189          entryIsDir(FSTEntries, entrynum) )
190     {
191         return false;
192     }
193 
194     af->handle      = handle;
195     af->startOffset = filePosition(FSTEntries, entrynum);
196     af->length      = fileLength(FSTEntries, entrynum);
197 
198     return true;
199 }
200 
201  /*---------------------------------------------------------------------------*
202     Description:        compare two strings up to the first string hits '/' or
203                         '\0'
204 
205     Arguments:          path     path name
206                         string   directory or file name
207 
208     Returns:            true if same, false if not
209  *---------------------------------------------------------------------------*/
isSame(const wchar_t * path,const wchar_t * string)210 static bool isSame(const wchar_t* path, const wchar_t* string)
211 {
212     while(*string != '\0')
213     {
214         // compare in case-insensitive
215         if (tolower(*path++) != tolower(*string++))
216         {
217             return false;
218         }
219     }
220 
221     if ( (*path == '/') || (*path == '\0') )
222     {
223         return true;
224     }
225 
226     return false;
227 }
228 
229 
ARCConvertPathToEntrynum(ARCHandle * handle,const wchar_t * pathPtr)230 s32 ARCConvertPathToEntrynum(ARCHandle* handle, const wchar_t* pathPtr)
231 {
232     const wchar_t* ptr;
233     wchar_t*     stringPtr;
234     bool         isDir;
235     s32          length; // must be signed
236     u32          dirLookAt;
237     u32          i;
238     const wchar_t*  origPathPtr = pathPtr;
239     FSTEntry*    FSTEntries;
240 
241 
242     NN_ASSERTMSG(handle, "ARCConvertPathToEntrynum(): null pointer is specified to ARCHandle structure");
243     NN_ASSERTMSG(pathPtr, "ARCConvertPathToEntrynum(): null pointer is specified to file name");
244 
245 
246     dirLookAt = handle->currDir;
247     FSTEntries = (FSTEntry*)handle->FSTStart;
248 
249     while (1)
250     {
251 
252         // check /, ./, ../
253         if (*pathPtr == '\0')
254         {
255             return (s32)dirLookAt;
256         } else if (*pathPtr == '/')
257         {
258             dirLookAt = 0;
259             pathPtr++;
260             continue;
261         }
262         else if (*pathPtr == '.')
263         {
264             if (*(pathPtr + 1) == '.')
265             {
266                 if (*(pathPtr + 2) == '/')
267                 {
268                     dirLookAt = parentDir(FSTEntries, dirLookAt);
269                     pathPtr += 3;
270                     continue;
271                 }
272                 else if (*(pathPtr + 2) == '\0')
273                 {
274                     return (s32)parentDir(FSTEntries, dirLookAt);
275                 }
276             }
277             else if (*(pathPtr + 1) == '/')
278             {
279                 pathPtr += 2;
280                 continue;
281             }
282             else if (*(pathPtr + 1) == '\0')
283             {
284                 return (s32)dirLookAt;
285             }
286         }
287 
288         // directory or file? Can be checked by the endmark
289         for(ptr = pathPtr; (*ptr != '\0') && (*ptr != '/'); ptr++)
290             ;
291 
292         // it's true that one ends with '/' should be a directory name,
293         // but one ends with '\0' is not always a file name.
294         isDir = (*ptr == '\0')? false : true;
295         length = (s32)(ptr - pathPtr);
296 
297         ptr = pathPtr;
298 
299         for(i = dirLookAt + 1; i < nextDir(FSTEntries, dirLookAt);
300             i = entryIsDir(FSTEntries, i)? nextDir(FSTEntries, i): (i+1) )
301         {
302 dot:
303             // isDir == false doesn't mean it's a file.
304             // so it's legal to compare it to a directory
305             if ( ( entryIsDir(FSTEntries, i) == false ) &&
306                  (isDir == true) )
307             {
308                 continue;
309             }
310 
311             stringPtr = GetStringPtr(handle->FSTStringStart, stringOff(FSTEntries, i));
312 
313             // check "."
314             // support archive files created by "darch[D] -c ."
315             if (*stringPtr == '.' && *(stringPtr + 1) == '\0') {
316                 i++;
317                 goto dot;
318             }
319 
320             if ( isSame(ptr, stringPtr) == true )
321             {
322                 goto next_hier;
323             }
324 
325         } // for()
326 
327         return -1;
328 
329       next_hier:
330         if (! isDir)
331         {
332             return (s32)i;
333         }
334 
335         dirLookAt = i;
336         pathPtr += length + 1;
337 
338     } // while (1)
339 
340     // NOT REACHED
341 }
342 
ARCEntrynumIsDir(const ARCHandle * handle,s32 entrynum)343 bool  ARCEntrynumIsDir( const ARCHandle * handle, s32 entrynum )
344 {
345     FSTEntry*           FSTEntries;
346 
347     NN_ASSERTMSG(handle, "ARCEntrynumIsDir(): null pointer is specified to ARCHandle structure");
348     NN_ASSERTMSG((entrynum >= 0) , "ARCEntrynumIsDir(): no file/directory is specified to entrynum");
349     FSTEntries = (FSTEntry*)handle->FSTStart;
350 
351     return entryIsDir( FSTEntries, entrynum );
352 }
353 
354 /*---------------------------------------------------------------------------*
355   Name:         myStrncpy
356 
357   Description:  copy a string. Difference from standard strncpy is:
358                     1. do not pad dest with null characters
359                     2. do not terminate dest with a null
360                     3. return the number of chars copied
361 
362   Arguments:    dest        destination
363                 src         source
364                 maxlen      maximum length of copy
365 
366   Returns:      Num of chars copied
367  *---------------------------------------------------------------------------*/
myStrncpy(wchar_t * dest,wchar_t * src,u32 maxlen)368 static u32 myStrncpy(wchar_t* dest, wchar_t* src, u32 maxlen)
369 {
370     u32         i = maxlen;
371 
372     while( (i > 0) && (*src != 0) )
373     {
374         *dest++ = *src++;
375         i--;
376     }
377 
378     return (maxlen - i);
379 }
380 
381 /*---------------------------------------------------------------------------*
382   Name:         entryToPath
383 
384   Description:  Convert from fst entry to path. The result is not null
385                 terminated.
386 
387   Arguments:    entry       FST entry
388                 path        pointer to store the result
389                 maxlen      size of the buffer start from path
390 
391   Returns:      Num of chars copied
392  *---------------------------------------------------------------------------*/
entryToPath(ARCHandle * handle,u32 entry,wchar_t * path,u32 maxlen)393 static u32 entryToPath(ARCHandle* handle, u32 entry, wchar_t* path, u32 maxlen)
394 {
395     wchar_t*    name;
396     u32         loc;
397     FSTEntry*    FSTEntries;
398 
399     FSTEntries = (FSTEntry*)handle->FSTStart;
400 
401     if (entry == 0)
402     {
403         return 0;
404     }
405 
406     name = GetStringPtr(handle->FSTStringStart, stringOff(FSTEntries, entry));
407 
408     loc = entryToPath(handle, parentDir(FSTEntries, entry), path, maxlen);
409 
410     if (loc == maxlen)
411     {
412         return loc;
413     }
414 
415     *(path + loc++) = '/';
416 
417     loc += myStrncpy(path + loc, name, maxlen - loc);
418 
419     return loc;
420 }
421 
422 
423 /*---------------------------------------------------------------------------*
424   Name:         ARCConvertEntrynumToPath
425 
426   Description:  Convert from fst entry to path.
427 
428   Arguments:    entrynum    FST entry
429                 path        pointer to store the result
430                 maxlen      size of the buffer start from path
431 
432   Returns:      true if all the path fits in maxlen. false if not (in this
433                 case, it's truncated to fit maxlen).
434  *---------------------------------------------------------------------------*/
ARCConvertEntrynumToPath(ARCHandle * handle,s32 entrynum,wchar_t * path,u32 maxlen)435 static bool ARCConvertEntrynumToPath(ARCHandle* handle, s32 entrynum, wchar_t* path, u32 maxlen)
436 {
437     u32         loc;
438     FSTEntry*    FSTEntries;
439 
440 
441     FSTEntries = (FSTEntry*)handle->FSTStart;
442     NN_ASSERTMSG((0 <= entrynum) && (entrynum < static_cast<s32>(handle->entryNum)),
443                "ARCConvertEntrynumToPath: specified entrynum(%d) is out of range  ",
444                 entrynum );
445     NN_ASSERTMSG(1 < maxlen, "ARCConvertEntrynumToPath: maxlen should be more than 1 (%d is specified)",
446                maxlen );
447     // currently only directories can be converted to path
448     // finding the directory where a file is located is not so easy
449     // while finding the parent directory of a directory is a piece of cake.
450     NN_ASSERTMSG(entryIsDir(FSTEntries, entrynum),
451               "ARCConvertEntrynumToPath: cannot convert an entry num for a file to path  ");
452 
453     loc = entryToPath(handle, (u32)entrynum, path, maxlen);
454 
455     if (loc == maxlen)
456     {
457         // Overwrite the last char with NULL
458         path[maxlen - 1] = '\0';
459         return false;
460     }
461 
462     // For directories, put '/' at the last
463     if (entryIsDir(FSTEntries, entrynum))
464     {
465         if (loc == maxlen - 1)
466         {
467             // There's no room to put the last '/', so just put '\0' and return false
468             path[loc] = '\0';
469             return false;
470         }
471 
472         path[loc++] = '/';
473     }
474 
475     path[loc] = '\0';
476     return true;
477 }
478 
479 /*---------------------------------------------------------------------------*
480   Name:         ARCGetCurrentDir
481 
482   Description:  Get current directory
483 
484   Arguments:    path        pointer to store the result
485                 maxlen      size of the buffer start from path
486 
487   Returns:      true if all the path fits in maxlen. false if not (in this
488                 case, it's truncated to fit maxlen).
489  *---------------------------------------------------------------------------*/
ARCGetCurrentDir(ARCHandle * handle,wchar_t * path,u32 maxlen)490 bool ARCGetCurrentDir(ARCHandle* handle, wchar_t* path, u32 maxlen)
491 {
492     NN_ASSERTMSG( 1 < maxlen, "ARCGetCurrentDir: maxlen should be more than 1 (%d is specified)",
493                 maxlen );
494 
495     return ARCConvertEntrynumToPath(handle, (s32)handle->currDir, path, maxlen);
496 }
497 
ARCGetStartAddrInMem(ARCFileInfo * af)498 void* ARCGetStartAddrInMem(ARCFileInfo* af)
499 {
500     ARCHandle* handle;
501 
502     handle = af->handle;
503 
504     NN_ASSERTMSG(handle, "ARCGetFileAddr(): af->handle is null pointer. Maybe it's not initialized properly");
505     NN_ASSERTMSG(af, "ARCGetFileAddr(): null pointer is specified to ARCFileInfo structure");
506 
507     return (void*)( (u32)handle->archiveStartAddr + af->startOffset );
508 }
509 
ARCGetStartOffset(ARCFileInfo * af)510 u32 ARCGetStartOffset(ARCFileInfo* af)
511 {
512     return af->startOffset;
513 }
514 
ARCGetLength(ARCFileInfo * af)515 u32 ARCGetLength(ARCFileInfo* af)
516 {
517     return af->length;
518 }
519 
520 
ARCClose(ARCFileInfo * af)521 bool ARCClose(ARCFileInfo* af)
522 {
523     NN_UNUSED_VAR(af);
524 
525     return true;
526 }
527 
ARCChangeDir(ARCHandle * handle,const wchar_t * dirName)528 bool ARCChangeDir(ARCHandle* handle, const wchar_t* dirName)
529 {
530     s32         entry;
531     FSTEntry*   FSTEntries;
532 
533     NN_ASSERTMSG(handle, "ARCChangeDir(): null pointer is specified to ARCHandle");
534     NN_ASSERTMSG(dirName, "ARCChangeDir(): null pointer is specified to dirname");
535 
536     entry = ARCConvertPathToEntrynum(handle, dirName);
537     FSTEntries = (FSTEntry*)handle->FSTStart;
538 
539 #if defined(NW_DEBUG)
540     if (0 > entry)
541     {
542         const size_t BufMax = 127;
543         wchar_t currentDir[BufMax + 1];
544         char chBuf[BufMax + 1];
545         WcsToMbs(chBuf, dirName, BufMax);
546         chBuf[BufMax] = '\0';
547         NN_LOG("ARCOpendir(): directory '%s' is not found", chBuf);
548         ARCGetCurrentDir(handle, currentDir, BufMax + 1);
549         WcsToMbs(chBuf, currentDir, BufMax);
550         chBuf[BufMax] = '\0';
551         NN_LOG(" under %s  ", chBuf);
552         NN_ASSERT(false);
553     }
554 #endif
555     NN_ASSERTMSG(entryIsDir(FSTEntries, entry), "ARCChangeDir(): %s is not a directory", dirName );
556 
557     if ( (entry < 0) || (entryIsDir(FSTEntries, entry) == false) )
558     {
559         return false;
560     }
561 
562     handle->currDir = (u32)entry;
563 
564     return true;
565 }
566 
567 
ARCOpenDir(ARCHandle * handle,const wchar_t * dirName,ARCDir * dir)568 bool ARCOpenDir(ARCHandle* handle, const wchar_t* dirName, ARCDir* dir)
569 {
570     s32         entry;
571     FSTEntry*   FSTEntries;
572 
573     NN_ASSERTMSG(handle, "ARCOpenDir(): null pointer is specified to ARCHandle");
574     NN_ASSERTMSG(dirName, "ARCOpenDir(): null pointer is specified to ARCDir");
575 
576     entry = ARCConvertPathToEntrynum(handle, dirName);
577     FSTEntries = (FSTEntry*)handle->FSTStart;
578 
579 #if defined(NW_DEBUG)
580     if (entry < 0)
581     {
582         const size_t BufMax = 127;
583         wchar_t currentDir[BufMax + 1];
584         char chBuf[BufMax + 1];
585         WcsToMbs(chBuf, dirName, BufMax);
586         chBuf[BufMax] = '\0';
587         NN_LOG("ARCOpenDir(): directory '%s' is not found", chBuf);
588         ARCGetCurrentDir(handle, currentDir, BufMax + 1);
589         WcsToMbs(chBuf, currentDir, BufMax);
590         chBuf[BufMax] = '\0';
591         NN_LOG(" under %s  ", chBuf);
592         NN_ASSERT(false);
593     }
594 #endif
595 
596     NN_ASSERTMSG( entryIsDir(FSTEntries, entry), "ARCOpenDir(): %s is a regular file", dirName );
597 
598     if ( (entry < 0) || (entryIsDir(FSTEntries, entry) == false) )
599         return false;
600 
601     dir->handle = handle;
602     dir->entryNum = (u32)entry;
603     dir->location = (u32)entry + 1;
604     dir->next = nextDir(FSTEntries, entry);
605 
606     return true;
607 }
608 
ARCReadDir(ARCDir * dir,ARCDirEntry * dirent)609 bool ARCReadDir(ARCDir* dir, ARCDirEntry* dirent)
610 {
611     u32         loc;
612     FSTEntry*   FSTEntries;
613     ARCHandle*  handle;
614 
615     handle = dir->handle;
616 
617     NN_ASSERTMSG(handle, "ARCReadDir: dir->handle is null pointer. Maybe it's not initialized properly");
618 
619     FSTEntries = (FSTEntry*)handle->FSTStart;
620 
621     // Check the next location. loc == dir->next means it reached the
622     // end of the directory. Other check is for illegal setting by ARCSeekDir.
623     loc = dir->location;
624 retry:
625     if ( (loc <= dir->entryNum) || (dir->next <= loc) )
626         return false;
627 
628     dirent->handle = handle;
629     dirent->entryNum = loc;
630     dirent->isDir = entryIsDir(FSTEntries, loc);
631     dirent->name = GetStringPtr(handle->FSTStringStart, stringOff(FSTEntries, loc));
632 
633     // check "."
634     // support archive files created by "darch[D] -c ."
635     if (dirent->name[0] == '.' && dirent->name[1] == '\0') {
636         loc++;
637         goto retry;
638     }
639 
640     dir->location = entryIsDir(FSTEntries, loc)? nextDir(FSTEntries, loc) : (loc+1);
641 
642     return true;
643 }
644 
ARCCloseDir(ARCDir * dir)645 bool ARCCloseDir(ARCDir* dir)
646 {
647     NN_UNUSED_VAR(dir);
648 
649     return true;
650 }
651 
652 } // namespace lyt
653 } // namespace nw
654