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