1/*---------------------------------------------------------------------------*
2  Project:  Horizon
3  File:     savedata_calc.js
4
5  Copyright (C)2009 Nintendo Co., Ltd.  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  $Rev:$
14 *---------------------------------------------------------------------------*/
15
16/*===========================================================================*/
17/* Please see man pages for details
18
19*/
20function AdjustAlignment(val, alignment)
21{
22    return ((val + (alignment - 1)) & ~(alignment - 1));
23};
24
25/*===========================================================================*/
26/* Please see man pages for details
27
28*/
29function CntLz(val)
30{
31    var x = 0;
32
33    var n = 32;
34    var c = 16;
35    do
36    {
37        x = val >> c;
38        if (x != 0)
39        {
40            n -= c;
41            val = x;
42        }
43        c >>= 1;
44    } while(c != 0);
45
46    return (n - val);
47};
48
49/*---------------------------------------------------------------------------*/
50/* Please see man pages for details
51
52*/
53function IsPwr2(val)
54{
55    return (0 == (val & (val - 1)));
56};
57
58/*---------------------------------------------------------------------------*/
59/* Please see man pages for details
60
61*/
62function ILog2(val)
63{
64    return 31 - CntLz(val);
65};
66
67/*---------------------------------------------------------------------------*/
68/* Please see man pages for details
69
70*/
71function ResultInvalidArgument()
72{
73};
74
75/*===========================================================================*/
76/* Please see man pages for details
77
78
79*/
80function EntryMapTable()
81{
82};
83
84/*---------------------------------------------------------------------------*/
85
86EntryMapTable.STORAGE_SYSTEM_RESERVED = 1;
87
88EntryMapTable.DIRECTORY_SYSTEM_RESERVED = 1;
89EntryMapTable.FILE_SYSTEM_RESERVED = 0;
90
91EntryMapTable.INDEX_SIZE = 4; // sizeof(u32)
92EntryMapTable.STORAGE_INDEX_SIZE = 4; // sizeof(u32)
93
94EntryMapTable.DIRECTORY_NAME_SIZE = 16;
95EntryMapTable.FILE_NAME_SIZE = 16;
96
97EntryMapTable.DIRECTORY_INFO_SIZE = 4; // sizeof(bit8[4])
98
99EntryMapTable.FILE_SYSTEM_INFO_SIZE = 4 + // sizeof(u32)
100                                          8;  // sizeof(S64)
101EntryMapTable.FILE_OPTIONAL_INFO_SIZE = 4; // sizeof(bit8[4])
102EntryMapTable.FILE_INFO_SIZE = EntryMapTable.FILE_NAME_SIZE;
103
104EntryMapTable.DIRECTORY_KEY_SIZE = EntryMapTable.STORAGE_INDEX_SIZE +
105                                   EntryMapTable.DIRECTORY_NAME_SIZE;
106EntryMapTable.DIRECTORY_VALUE_SIZE = EntryMapTable.STORAGE_INDEX_SIZE +
107                                     EntryMapTable.STORAGE_INDEX_SIZE +
108                                     EntryMapTable.STORAGE_INDEX_SIZE +
109                                     EntryMapTable.DIRECTORY_INFO_SIZE;
110
111EntryMapTable.FILE_KEY_SIZE = EntryMapTable.STORAGE_INDEX_SIZE +
112                              EntryMapTable.DIRECTORY_NAME_SIZE;
113EntryMapTable.FILE_VALUE_SIZE = EntryMapTable.STORAGE_INDEX_SIZE +
114                                4 + // PADDING4
115                                EntryMapTable.FILE_INFO_SIZE;
116
117EntryMapTable.DIRECTORY_STORAGE_ELEMENT_SIZE = EntryMapTable.DIRECTORY_KEY_SIZE +
118                                               EntryMapTable.DIRECTORY_VALUE_SIZE +
119                                               EntryMapTable.INDEX_SIZE;
120
121EntryMapTable.FILE_STORAGE_ELEMENT_SIZE = EntryMapTable.FILE_KEY_SIZE +
122                                          EntryMapTable.FILE_VALUE_SIZE +
123                                          EntryMapTable.INDEX_SIZE;
124
125/*---------------------------------------------------------------------------*/
126
127EntryMapTable.QueryDirectoryEntryStorageSize = function(countDirectoryEntry)
128{
129    return ((countDirectoryEntry + EntryMapTable.DIRECTORY_SYSTEM_RESERVED)
130             + EntryMapTable.STORAGE_SYSTEM_RESERVED) * EntryMapTable.DIRECTORY_STORAGE_ELEMENT_SIZE;
131};
132
133/*---------------------------------------------------------------------------*/
134
135EntryMapTable.QueryFileEntryStorageSize = function(countFileEntry)
136{
137    return ((countFileEntry + EntryMapTable.FILE_SYSTEM_RESERVED)
138            + EntryMapTable.STORAGE_SYSTEM_RESERVED) * EntryMapTable.FILE_STORAGE_ELEMENT_SIZE;
139};
140
141/*---------------------------------------------------------------------------*/
142
143EntryMapTable.QueryDirectoryEntryBucketStorageSize = function(countDirectoryBucket)
144{
145    return countDirectoryBucket * EntryMapTable.INDEX_SIZE;
146};
147
148/*---------------------------------------------------------------------------*/
149
150EntryMapTable.QueryFileEntryBucketStorageSize = function(countFileBucket)
151{
152    return countFileBucket * EntryMapTable.INDEX_SIZE;
153};
154
155/*===========================================================================*/
156/* Please see man pages for details
157
158
159*/
160function AllocationTable()
161{
162};
163
164/*---------------------------------------------------------------------------*/
165
166AllocationTable.SECTOR_RESERVED_COUNT = 1;
167
168AllocationTable.TABLE_ELEMENT_SIZE = 4 // sizeof(u32)
169                                   + 4;// sizeof(u32)
170
171/*---------------------------------------------------------------------------*/
172
173AllocationTable.QuerySize = function(blockCount)
174{
175    return (blockCount + AllocationTable.SECTOR_RESERVED_COUNT)
176            * AllocationTable.TABLE_ELEMENT_SIZE;
177};
178
179/*===========================================================================*/
180/* Please see man pages for details
181
182
183*/
184function FileSystemControlArea()
185{
186};
187
188/*---------------------------------------------------------------------------*/
189
190FileSystemControlArea.STORAGE_INFO_SIZE = 8 // sizeof(s64)
191                                        + 4 // sizeof(u32)
192                                        + 4;// PADDING4
193
194FileSystemControlArea.ALLOCATION_INFO_SIZE = 4 // sizeof(u32)
195                                           + 4 // sizeof(size_t)
196                                           + 4 // sizeof(u32)
197                                           + 4;// PADDING4
198
199/*---------------------------------------------------------------------------*/
200
201FileSystemControlArea.QuerySize = function()
202{
203    var STORAGE_OR_ALLOCATION_INFO_SIZE = FileSystemControlArea.STORAGE_INFO_SIZE;
204    if (FileSystemControlArea.STORAGE_INFO_SIZE < FileSystemControlArea.ALLOCATION_INFO_SIZE)
205    {
206        STORAGE_OR_ALLOCATION_INFO_SIZE = FileSystemControlArea.ALLOCATION_INFO_SIZE;
207    }
208
209    return (4
210          + 4
211          + FileSystemControlArea.STORAGE_INFO_SIZE
212          + FileSystemControlArea.STORAGE_INFO_SIZE
213          + FileSystemControlArea.STORAGE_INFO_SIZE
214          + FileSystemControlArea.STORAGE_INFO_SIZE
215          + STORAGE_OR_ALLOCATION_INFO_SIZE
216          + STORAGE_OR_ALLOCATION_INFO_SIZE);
217};
218
219/*===========================================================================*/
220/* Please see man pages for details
221
222
223*/
224function HierarchicalDuplexFile()
225{
226};
227
228/*---------------------------------------------------------------------------*/
229
230HierarchicalDuplexFile.DPFS_BITMAP_ALIGN = 4;
231HierarchicalDuplexFile.MAX_LAYERS = 3; // Master, L1, BODY
232
233HierarchicalDuplexFile.LEVEL_INFO_SIZE = 8 // sizeof(Int64)
234                                       + 8 // sizeof(Int64)
235                                       + 4 // sizeof(s32)
236                                       + 4;// PADDING4
237
238HierarchicalDuplexFile.INFOMATION_SIZE = HierarchicalDuplexFile.LEVEL_INFO_SIZE
239                                       * HierarchicalDuplexFile.MAX_LAYERS;
240
241HierarchicalDuplexFile.META_INFO_SIZE = 4 // sizeof(u32)
242                                      + 4 // sizeof(u32)
243                                      + HierarchicalDuplexFile.INFOMATION_SIZE
244
245/*---------------------------------------------------------------------------*/
246
247HierarchicalDuplexFile.InputParam = function()
248{
249    this.sizeBlockLevel = new Array(2);
250};
251
252/*---------------------------------------------------------------------------*/
253
254HierarchicalDuplexFile.LevelInfomation = function()
255{
256    this.offset = 0;
257    this.size = 0;
258    this.orderBlock = 0;
259};
260
261/*---------------------------------------------------------------------------*/
262
263HierarchicalDuplexFile.QuerySizeResult = function()
264{
265    this.sizeControlArea = 0;
266    this.sizeBody = 0;
267};
268
269/*---------------------------------------------------------------------------*/
270
271HierarchicalDuplexFile.QuerySize = function(
272                                       outResult,
273                                       inputParam,
274                                       sizeData
275                                   )
276{
277    if (inputParam.sizeBlockLevel[0] <= 0 || !IsPwr2(inputParam.sizeBlockLevel[0])
278     || inputParam.sizeBlockLevel[1] <= 0 || !IsPwr2(inputParam.sizeBlockLevel[1]))
279    {
280        throw new ResultInvalidArgument();
281    }
282
283    if (0 != (sizeData % inputParam.sizeBlockLevel[1]))
284    {
285        throw new ResultInvalidArgument();
286    }
287
288    if (inputParam.sizeBlockLevel[0] < HierarchicalDuplexFile.DPFS_BITMAP_ALIGN
289     || inputParam.sizeBlockLevel[1] < HierarchicalDuplexFile.DPFS_BITMAP_ALIGN)
290    {
291        throw new ResultInvalidArgument();
292    }
293
294    // Management region size is determined
295    outResult.sizeControlArea = HierarchicalDuplexFile.META_INFO_SIZE;
296
297    var MAX_LEVEL = HierarchicalDuplexFile.MAX_LAYERS;
298
299    var sizeLevel = new Array(MAX_LEVEL);
300    // Body
301    sizeLevel[2] = sizeData;
302    // Data size in bits
303    sizeLevel[1] = Math.floor(((sizeLevel[2] + inputParam.sizeBlockLevel[1] - 1) / inputParam.sizeBlockLevel[1]));
304    // Converts bit -> byte
305    sizeLevel[1] = Math.floor((sizeLevel[1] + 7) / 8);
306    // Align the size with the L1 block boundary
307    sizeLevel[1] = AdjustAlignment(sizeLevel[1], inputParam.sizeBlockLevel[0]);
308    // bit
309    sizeLevel[0] = Math.floor(((sizeLevel[1] + inputParam.sizeBlockLevel[0] - 1) / inputParam.sizeBlockLevel[0]));
310    // bit -> byte
311    sizeLevel[0] = Math.floor((sizeLevel[0] + 7) / 8);
312    // Align the size with the  DPFS_BITMAP_ALIGN byte boundary
313    sizeLevel[0] = AdjustAlignment(sizeLevel[0], HierarchicalDuplexFile.DPFS_BITMAP_ALIGN);
314
315    var orderBlock = new Array(MAX_LEVEL);
316
317    orderBlock[0] = 0;
318    orderBlock[1] = ILog2(inputParam.sizeBlockLevel[0]);
319    orderBlock[2] = ILog2(inputParam.sizeBlockLevel[1]);
320
321    // Master and L1 is aligned to DPFS_BITMAP_ALIGN at least.
322    var offset = 0;
323
324    var levelInfo = new Array(MAX_LEVEL);
325    var level;
326
327    for (level = 0; level < MAX_LEVEL; level++)
328    {
329        levelInfo[level] = new HierarchicalDuplexFile.LevelInfomation();
330
331        // The actual data is aligned to the L2 block size.
332        if (level == MAX_LEVEL - 1)
333        {
334            offset = AdjustAlignment(offset, inputParam.sizeBlockLevel[1]);
335        }
336
337        levelInfo[level].offset = offset;
338        levelInfo[level].size = sizeLevel[level];
339        levelInfo[level].orderBlock = orderBlock[level];
340        offset += sizeLevel[level] * 2;
341    }
342
343    // The actual data size is determined
344    outResult.sizeBody = offset;
345};
346
347/*===========================================================================*/
348/* Please see man pages for details
349
350
351*/
352function HierarchicalIntegrityVerificationFile()
353{
354};
355
356/*---------------------------------------------------------------------------*/
357
358HierarchicalIntegrityVerificationFile.HASH_SIZE = Math.floor(256 / 8);
359HierarchicalIntegrityVerificationFile.MAX_LAYERS = 5; // Master, L1, L2, L3, BODY
360
361HierarchicalIntegrityVerificationFile.LEVEL_INFO_SIZE = 8 // sizeof(Int64)
362                                                      + 8 // sizeof(Int64)
363                                                      + 4 // sizeof(s32)
364                                                      + 4;// PADDING4
365HierarchicalIntegrityVerificationFile.INFOMATION_SIZE = 4 // sizeof(u32)
366                                                      + HierarchicalIntegrityVerificationFile.LEVEL_INFO_SIZE
367                                                        * (HierarchicalIntegrityVerificationFile.MAX_LAYERS - 1);
368HierarchicalIntegrityVerificationFile.META_INFO_SIZE = 4 // sizeof(u32)
369                                                     + 4 // sizeof(u32)
370                                                     + 4 // sizeof(u32)
371                                                     + HierarchicalIntegrityVerificationFile.INFOMATION_SIZE
372                                                     + 4 // sizeof(u32)
373                                                     + 4; // sizeof(u32)
374
375/*---------------------------------------------------------------------------*/
376
377HierarchicalIntegrityVerificationFile.InputParam = function()
378{
379    this.sizeOptionalInfo = 0;
380    this.sizeBlockLevel = new Array(HierarchicalIntegrityVerificationFile.MAX_LAYERS - 1);
381};
382
383/*---------------------------------------------------------------------------*/
384
385HierarchicalIntegrityVerificationFile.LevelInfomation = function()
386{
387    this.offset = 0;
388    this.size = 0;
389    this.orderBlock = 0;
390};
391
392/*---------------------------------------------------------------------------*/
393
394HierarchicalIntegrityVerificationFile.QuerySizeResult = function()
395{
396    this.sizeControlArea = 0;
397    this.sizeMasterHash = 0;
398    this.sizeLayerdHash = 0;
399    this.sizeBody = 0;
400    this.sizeTotal = 0;
401};
402
403/*---------------------------------------------------------------------------*/
404
405HierarchicalIntegrityVerificationFile.QuerySize = function(
406                                                      outResult,
407                                                      inputParam,
408                                                      sizeData
409                                                  )
410{
411    if (inputParam.sizeBlockLevel[0] <= 0 || !IsPwr2(inputParam.sizeBlockLevel[0])
412     || inputParam.sizeBlockLevel[1] <= 0 || !IsPwr2(inputParam.sizeBlockLevel[1])
413     || inputParam.sizeBlockLevel[2] <= 0 || !IsPwr2(inputParam.sizeBlockLevel[2])
414     || inputParam.sizeBlockLevel[3] <= 0 || !IsPwr2(inputParam.sizeBlockLevel[3]))
415    {
416        throw new ResultInvalidArgument();
417    }
418
419    var offset = 0;
420    offset += HierarchicalIntegrityVerificationFile.META_INFO_SIZE;
421    offset = AdjustAlignment(offset, 4);
422
423    offset += inputParam.sizeOptionalInfo;
424    // Management region size is determined
425    outResult.sizeControlArea = offset;
426
427    var MAX_LEVEL = HierarchicalIntegrityVerificationFile.MAX_LAYERS;
428    var HASH_SIZE = HierarchicalIntegrityVerificationFile.HASH_SIZE;
429
430    var sizeLevel = new Array(MAX_LEVEL);
431
432    // Body
433    sizeLevel[4] = sizeData;
434    // L3
435    sizeLevel[3] = Math.floor(((sizeLevel[4] + inputParam.sizeBlockLevel[3] - 1) / inputParam.sizeBlockLevel[3])) * HASH_SIZE;
436    // L2
437    sizeLevel[2] = Math.floor(((sizeLevel[3] + inputParam.sizeBlockLevel[2] - 1) / inputParam.sizeBlockLevel[2])) * HASH_SIZE;
438    // L1
439    sizeLevel[1] = Math.floor(((sizeLevel[2] + inputParam.sizeBlockLevel[1] - 1) / inputParam.sizeBlockLevel[1])) * HASH_SIZE;
440    // Master
441    sizeLevel[0] = Math.floor(((sizeLevel[1] + inputParam.sizeBlockLevel[0] - 1) / inputParam.sizeBlockLevel[0])) * HASH_SIZE;
442
443    var orderBlock = new Array(MAX_LEVEL);
444
445    orderBlock[1] = ILog2(inputParam.sizeBlockLevel[0]);
446    orderBlock[2] = ILog2(inputParam.sizeBlockLevel[1]);
447    orderBlock[3] = ILog2(inputParam.sizeBlockLevel[2]);
448    orderBlock[4] = ILog2(inputParam.sizeBlockLevel[3]);
449
450    // Master hash size is determined
451    outResult.sizeMasterHash = sizeLevel[0];
452
453    // Build the level information
454    var levelInfo = new Array(MAX_LEVEL);
455    var level;
456
457    offset = 0;
458    for (level = 1; level < MAX_LEVEL; level++)
459    {
460        levelInfo[level - 1] = new HierarchicalIntegrityVerificationFile.LevelInfomation();
461
462        if (sizeLevel[level] >= (4 << orderBlock[level]))
463        {
464            offset = AdjustAlignment(offset, 1 << orderBlock[level]);
465        }
466        else
467        {
468            offset = AdjustAlignment(offset, 8/*sizeof(s64)*/);
469        }
470
471        levelInfo[level - 1].offset = offset;
472        levelInfo[level - 1].size = sizeLevel[level];
473        levelInfo[level - 1].orderBlock = orderBlock[level];
474        offset += sizeLevel[level];
475    }
476
477    // Layer hash size is determined
478    outResult.sizeLayeredHash = levelInfo[MAX_LEVEL - 3].offset
479                              + levelInfo[MAX_LEVEL - 3].size
480                              - levelInfo[0].offset;
481
482    // The actual data size is determined
483    outResult.sizeBody = levelInfo[MAX_LEVEL - 2].size;
484
485    // Total size is determined
486    outResult.sizeTotal = levelInfo[MAX_LEVEL - 2].offset
487                        + levelInfo[MAX_LEVEL - 2].size
488                        - levelInfo[0].offset;
489};
490
491/*===========================================================================*/
492/* Please see man pages for details
493
494
495*/
496function DuplicatedIntegrityFile()
497{
498};
499
500/*---------------------------------------------------------------------------*/
501
502DuplicatedIntegrityFile.META_INFO_SIZE = 4 // sizeof(u32)
503                                       + 4 // sizeof(u32)
504                                       + 8 // sizeof(Int64)
505                                       + 8 // sizeof(Int64)
506                                       + 8 // sizeof(Int64)
507                                       + 8 // sizeof(Int64)
508                                       + 8 // sizeof(Int64)
509                                       + 8 // sizeof(Int64)
510                                       + 1 // sizeof(bool)
511                                       + 1 // sizeof(bool)
512                                       + 2 // PADDING2
513                                       + 8;// sizeof(Int64)
514
515/*---------------------------------------------------------------------------*/
516
517DuplicatedIntegrityFile.QuerySizeResult = function()
518{
519    this.sizeControlArea = 0;
520    this.sizeTotalBody = 0;
521};
522
523/*---------------------------------------------------------------------------*/
524
525DuplicatedIntegrityFile.QuerySize = function(
526                                        outResult,
527                                        inputParamDuplex,
528                                        inputParamIntegrity,
529                                        sizeOriginalData,
530                                        isDuplicateOnlyHash
531                                    )
532{
533    // Calculate the size of the hierarchical integrity verification file.
534    var resultIntegrity = new HierarchicalIntegrityVerificationFile.QuerySizeResult();
535
536    HierarchicalIntegrityVerificationFile.QuerySize(
537                                              resultIntegrity,
538                                              inputParamIntegrity,
539                                              sizeOriginalData
540                                          );
541
542    var sizeDuplicateOriginal = 0;
543
544    if (!isDuplicateOnlyHash)
545    {
546        sizeDuplicateOriginal = AdjustAlignment(resultIntegrity.sizeTotal, inputParamDuplex.sizeBlockLevel[1]);
547    }
548    else
549    {
550        sizeDuplicateOriginal = AdjustAlignment(resultIntegrity.sizeLayeredHash, inputParamDuplex.sizeBlockLevel[1]);
551    }
552
553    // Calculate the size of the hierarchical duplex file.
554    var resultDuplex = new HierarchicalDuplexFile.QuerySizeResult();
555
556    HierarchicalDuplexFile.QuerySize(
557                               resultDuplex,
558                               inputParamDuplex,
559                               sizeDuplicateOriginal
560                           );
561
562    // Calculate the size of the duplex + integrity verification file.
563    var offsetOriginalData;
564    if (!isDuplicateOnlyHash)
565    {
566        offsetOriginalData = 0;
567        outResult.sizeTotalBody = resultDuplex.sizeBody;
568    }
569    else
570    {
571        // Place the actual data after the duplicated signature data
572        offsetOriginalData = AdjustAlignment(resultDuplex.sizeBody, inputParamIntegrity.sizeBlockLevel[3]);
573        outResult.sizeTotalBody = offsetOriginalData + sizeOriginalData;
574    }
575
576    // Meta information
577    var offset = DuplicatedIntegrityFile.META_INFO_SIZE;
578
579    // Integrity verification layer management region
580    offset = AdjustAlignment(offset, 4);
581    offset += resultIntegrity.sizeControlArea;
582
583    // Duplex layer management region
584    offset = AdjustAlignment(offset, 4);
585    offset += resultDuplex.sizeControlArea;
586
587    // Master signature
588    offset = AdjustAlignment(offset, 4);
589    offset += resultIntegrity.sizeMasterHash;
590
591    // Final management region size
592    outResult.sizeControlArea = offset;
593};
594
595/*===========================================================================*/
596/* Please see man pages for details
597
598
599*/
600function DuplicatedIntegrityFilterFile()
601{
602};
603
604/*---------------------------------------------------------------------------*/
605
606DuplicatedIntegrityFilterFile.ATOMIC_HEADER_SIZE = 512;
607
608/*---------------------------------------------------------------------------*/
609
610DuplicatedIntegrityFilterFile.QuerySizeResult = function()
611{
612    this.sizeTotalBody = 0;
613};
614
615/*---------------------------------------------------------------------------*/
616
617DuplicatedIntegrityFilterFile.QuerySize = function(
618                                              outResult,
619                                              inputParamDuplex,
620                                              inputParamIntegrity,
621                                              sizeOriginalData,
622                                              isDuplicateOnlyHash
623                                          )
624{
625    // The filter file does not use the option region.
626    inputParamDuplex.sizeOptionalInfo = 0;
627
628    // Get the size of the duplex file management region with verification feature.
629    var resultDupInt = new DuplicatedIntegrityFile.QuerySizeResult();
630    DuplicatedIntegrityFile.QuerySize(
631                                resultDupInt,
632                                inputParamDuplex,
633                                inputParamIntegrity,
634                                sizeOriginalData,
635                                isDuplicateOnlyHash
636                            );
637
638    var offset = DuplicatedIntegrityFilterFile.ATOMIC_HEADER_SIZE;
639
640    // Duplex management region A
641    offset = AdjustAlignment(offset, 8/*sizeof(s64)*/);
642    offset += resultDupInt.sizeControlArea;
643
644    // Duplex management region B
645    offset = AdjustAlignment(offset, 8/*sizeof(s64)*/);
646    offset += resultDupInt.sizeControlArea;
647
648    var maxBlockSize = ((inputParamDuplex.sizeBlockLevel[0] - 1) |
649                        (inputParamDuplex.sizeBlockLevel[1] - 1) |
650                        (inputParamIntegrity.sizeBlockLevel[0] - 1) |
651                        (inputParamIntegrity.sizeBlockLevel[1] - 1) |
652                        (inputParamIntegrity.sizeBlockLevel[2] - 1) |
653                        (inputParamIntegrity.sizeBlockLevel[3] - 1)) + 1;
654
655    // While continuing to insert padding, place the data portion after the duplex file management region with the verification feature.
656    //
657    offset = AdjustAlignment(offset, maxBlockSize);
658    offset += resultDupInt.sizeTotalBody;;
659
660    // Return the final size.
661    outResult.sizeTotalBody = offset;
662};
663
664/*===========================================================================*/
665/* Please see man pages for details
666
667
668*/
669function WStringOnBit64PathStorageArchive()
670{
671};
672
673/*---------------------------------------------------------------------------*/
674
675WStringOnBit64PathStorageArchive.MAX_PATH_LENGTH = 256;
676
677WStringOnBit64PathStorageArchive.ARCHIVE_HEADER_SIZE = 4 // sizeof(u32)
678                                                     + 4 // sizeof(u32)
679                                                     + 8 // sizeof(s64)
680                                                     + 8 // sizeof(s64)
681                                                     + 4 // sizeof(u32)
682                                                     + 4 // PADDING4
683                                                     + 8
684                                                     + (
685                                                         4 // sizeof(bit32)
686                                                       + 8 // sizeof(FileId) -> sizeof(bit64)
687                                                       + WStringOnBit64PathStorageArchive.MAX_PATH_LENGTH
688                                                       ) // sizeof(transaction)
689                                                     ;
690
691/*---------------------------------------------------------------------------*/
692
693WStringOnBit64PathStorageArchive.QueryHeaderSize = function()
694{
695    return WStringOnBit64PathStorageArchive.ARCHIVE_HEADER_SIZE;
696};
697
698/*---------------------------------------------------------------------------*/
699
700WStringOnBit64PathStorageArchive.QueryFileSystemControlAreaSize = function()
701{
702    return FileSystemControlArea.QuerySize();
703};
704
705/*---------------------------------------------------------------------------*/
706
707WStringOnBit64PathStorageArchive.QueryDirectoryEntryStorageSize = function(countDirectoryEntry)
708{
709    return EntryMapTable.QueryDirectoryEntryStorageSize(countDirectoryEntry);
710};
711
712/*---------------------------------------------------------------------------*/
713
714WStringOnBit64PathStorageArchive.QueryDirectoryEntryBucketStorageSize = function(countDirectoryBucket)
715{
716    return EntryMapTable.QueryDirectoryEntryBucketStorageSize(countDirectoryBucket);
717};
718
719/*---------------------------------------------------------------------------*/
720
721WStringOnBit64PathStorageArchive.QueryFileEntryStorageSize = function(countFileEntry)
722{
723    return EntryMapTable.QueryFileEntryStorageSize(countFileEntry);
724};
725
726/*---------------------------------------------------------------------------*/
727
728WStringOnBit64PathStorageArchive.QueryFileEntryBucketStorageSize = function(countFileBucket)
729{
730    return EntryMapTable.QueryFileEntryBucketStorageSize(countFileBucket);
731};
732
733/*---------------------------------------------------------------------------*/
734
735WStringOnBit64PathStorageArchive.QueryAllocationTableStorageSize = function(countDataBlock)
736{
737    return AllocationTable.QuerySize(countDataBlock);
738};
739
740/*---------------------------------------------------------------------------*/
741
742WStringOnBit64PathStorageArchive.QueryOptimalBucketCount = function(countEntries)
743{
744    if (countEntries <= 3)
745    {
746        // When the number of entries is very small, the packet number returns a fixed size.
747        return 3;
748    }
749    if (countEntries <= 19)
750    {
751        // When the number of entries is less than 20, an odd number is returned.
752        return countEntries | 1;
753    }
754
755    // When the number of entries exceeds 20, the distribution state of the packet is considered, and a branch search is performed using a small prime number.
756    //
757    var i;
758    for (i = 0;i < 100;i++)
759    {
760        var candidate = (countEntries + i);
761        if (
762            (candidate % 2) != 0 &&
763            (candidate % 3) != 0 &&
764            (candidate % 5) != 0 &&
765            (candidate % 7) != 0 &&
766            (candidate % 11) != 0 &&
767            (candidate % 13) != 0 &&
768            (candidate % 17) != 0
769        )
770        {
771            return candidate;
772        }
773    }
774    return countEntries | 1;
775};
776
777/*---------------------------------------------------------------------------*/
778
779WStringOnBit64PathStorageArchive.QueryTotalSize = function(
780                                     countDirectoryEntry,
781                                     countDirectoryEntryBucket,
782                                     countFileEntry,
783                                     countFileEntryBucket,
784                                     sizeBlock
785                                 )
786{
787    var sizeDirectoryEntry = WStringOnBit64PathStorageArchive.QueryDirectoryEntryStorageSize(countDirectoryEntry);
788    var blockCountDirectoryEntry = Math.floor((sizeDirectoryEntry + sizeBlock - 1) / sizeBlock);
789    var sizeFileEntry = WStringOnBit64PathStorageArchive.QueryFileEntryStorageSize(countFileEntry);
790    var blockCountFileEntry = Math.floor((sizeFileEntry + sizeBlock - 1) / sizeBlock);
791    var sizeFixed =
792        WStringOnBit64PathStorageArchive.QueryHeaderSize() +
793        WStringOnBit64PathStorageArchive.QueryFileSystemControlAreaSize() +
794        WStringOnBit64PathStorageArchive.QueryDirectoryEntryBucketStorageSize(countDirectoryEntryBucket) +
795        WStringOnBit64PathStorageArchive.QueryFileEntryBucketStorageSize(countFileEntryBucket) +
796        WStringOnBit64PathStorageArchive.QueryAllocationTableStorageSize(blockCountDirectoryEntry + blockCountFileEntry)
797        ;
798    sizeFixed = AdjustAlignment(sizeFixed, sizeBlock);
799    return sizeFixed + sizeBlock * (blockCountDirectoryEntry + blockCountFileEntry);
800}
801
802/*===========================================================================*/
803/* Please see man pages for details
804
805
806*/
807function SaveDataArchive()
808{
809};
810
811/*---------------------------------------------------------------------------*/
812
813SaveDataArchive.ARCHIVE_HEADER_SIZE = 4 // sizeof(u32)
814                                    + 4 // sizeof(u32)
815                                    + 8 // sizeof(s64)
816                                    + 8 // sizeof(s64)
817                                    + 4 // sizeof(u32)
818                                    + 4;// PADDING4
819
820/*---------------------------------------------------------------------------*/
821
822SaveDataArchive.QueryHeaderSize = function()
823{
824    return SaveDataArchive.ARCHIVE_HEADER_SIZE;
825};
826
827/*---------------------------------------------------------------------------*/
828
829SaveDataArchive.QueryFileSystemControlAreaSize = function()
830{
831    return FileSystemControlArea.QuerySize();
832};
833
834/*---------------------------------------------------------------------------*/
835
836SaveDataArchive.QueryDirectoryEntryStorageSize = function(countDirectoryEntry)
837{
838    return EntryMapTable.QueryDirectoryEntryStorageSize(countDirectoryEntry);
839};
840
841/*---------------------------------------------------------------------------*/
842
843SaveDataArchive.QueryDirectoryEntryBucketStorageSize = function(countDirectoryBucket)
844{
845    return EntryMapTable.QueryDirectoryEntryBucketStorageSize(countDirectoryBucket);
846};
847
848/*---------------------------------------------------------------------------*/
849
850SaveDataArchive.QueryFileEntryStorageSize = function(countFileEntry)
851{
852    return EntryMapTable.QueryFileEntryStorageSize(countFileEntry);
853};
854
855/*---------------------------------------------------------------------------*/
856
857SaveDataArchive.QueryFileEntryBucketStorageSize = function(countFileBucket)
858{
859    return EntryMapTable.QueryFileEntryBucketStorageSize(countFileBucket);
860};
861
862/*---------------------------------------------------------------------------*/
863
864SaveDataArchive.QueryAllocationTableStorageSize = function(countDataBlock)
865{
866    return AllocationTable.QuerySize(countDataBlock);
867};
868
869/*---------------------------------------------------------------------------*/
870
871SaveDataArchive.QueryOptimalBucketCount = function(countEntries)
872{
873    // TODO: Considering standardizing to WStringOnBit64PathStorageArchive.QueryOptimalBucketCount
874    if (countEntries <= 3)
875    {
876        // When the number of entries is very small, the packet number returns a fixed size.
877        return 3;
878    }
879    if (countEntries <= 19)
880    {
881        // When the number of entries is less than 20, an odd number is returned
882        return countEntries | 1;
883    }
884
885    // When the number of entries exceeds 20, the distribution state of the packet is considered, and a branch search is performed using a small prime number.
886    //
887    var i;
888    for (i = 0; i < 100; i++)
889    {
890        var candidate = (countEntries + i);
891        if (
892            (candidate % 2) != 0 &&
893            (candidate % 3) != 0 &&
894            (candidate % 5) != 0 &&
895            (candidate % 7) != 0 &&
896            (candidate % 11) != 0 &&
897            (candidate % 13) != 0 &&
898            (candidate % 17) != 0
899        )
900        {
901            return candidate;
902        }
903    }
904    return countEntries | 1;
905};
906
907/*---------------------------------------------------------------------------*/
908
909SaveDataArchive.QueryMinDataSize = function(
910                                       countDirectoryEntry,
911                                       countFileEntry,
912                                       countDirectoryBucket,
913                                       countFileBucket,
914                                       sizeBlock,
915                                       countDataBlock
916                                   )
917{
918    if (countDirectoryBucket <= 0 || countFileBucket <= 0)
919    {
920        throw new ResultInvalidArgument();
921    }
922/*
923    var sizeDirectoryEntry = SaveDataArchive.QueryDirectoryEntryStorageSize(countDirectoryEntry);
924    var blockCountDirectoryEntry = Math.floor((sizeDirectoryEntry + sizeBlock - 1) / sizeBlock);
925    var sizeFileEntry = SaveDataArchive.QueryFileEntryStorageSize(countFileEntry);
926    var blockCountFileEntry = Math.floor((sizeFileEntry + sizeBlock - 1) / sizeBlock);
927    return sizeBlock * (blockCountDirectoryEntry + blockCountFileEntry + countDataBlock);
928*/
929    return sizeBlock * countDataBlock;
930};
931
932/*---------------------------------------------------------------------------*/
933
934SaveDataArchive.QueryMetaSize = function(
935                                    countDirectoryEntry,
936                                    countFileEntry,
937                                    countDirectoryBucket,
938                                    countFileBucket,
939                                    sizeBlock,
940                                    countDataBlock
941                                )
942{
943    var sizeFixed =
944        SaveDataArchive.QueryHeaderSize() +
945        SaveDataArchive.QueryFileSystemControlAreaSize() +
946        SaveDataArchive.QueryDirectoryEntryBucketStorageSize(countDirectoryBucket) +
947        SaveDataArchive.QueryFileEntryBucketStorageSize(countFileBucket) +
948        SaveDataArchive.QueryAllocationTableStorageSize(countDataBlock) +
949        SaveDataArchive.QueryDirectoryEntryStorageSize(countDirectoryEntry) +
950        SaveDataArchive.QueryFileEntryStorageSize(countFileEntry);
951    return AdjustAlignment(sizeFixed, sizeBlock);
952};
953
954/*---------------------------------------------------------------------------*/
955
956SaveDataArchive.QueryTotalSize = function(
957                                     countDirectoryEntry,
958                                     countFileEntry,
959                                     countDirectoryBucket,
960                                     countFileBucket,
961                                     sizeBlock,
962                                     countDataBlock
963                                 )
964{
965    var sizeDirectoryEntry = SaveDataArchive.QueryDirectoryEntryStorageSize(countDirectoryEntry);
966    var blockCountDirectoryEntry = Math.floor((sizeDirectoryEntry + sizeBlock - 1) / sizeBlock);
967    var sizeFileEntry = SaveDataArchive.QueryFileEntryStorageSize(countFileEntry);
968    var blockCountFileEntry = Math.floor((sizeFileEntry + sizeBlock - 1) / sizeBlock);
969    var sizeFixed =
970        SaveDataArchive.QueryHeaderSize() +
971        SaveDataArchive.QueryFileSystemControlAreaSize() +
972        SaveDataArchive.QueryDirectoryEntryBucketStorageSize(countDirectoryBucket) +
973        SaveDataArchive.QueryFileEntryBucketStorageSize(countFileBucket) +
974        SaveDataArchive.QueryAllocationTableStorageSize(blockCountDirectoryEntry + blockCountFileEntry + countDataBlock);
975    sizeFixed = AdjustAlignment(sizeFixed, sizeBlock);
976    return sizeFixed + sizeBlock * (blockCountDirectoryEntry + blockCountFileEntry + countDataBlock);
977};
978
979/*===========================================================================*/
980/* Please see man pages for details
981
982
983*/
984function DuplicatedIntegritySaveDataArchive()
985{
986};
987
988/*---------------------------------------------------------------------------*/
989
990DuplicatedIntegritySaveDataArchive.DUPLICATE_FULL      = 0x0001;    //
991DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META = 0x0002;    //
992
993DuplicatedIntegritySaveDataArchive.ATOMIC_HEADER_SIZE = 512;
994
995/*---------------------------------------------------------------------------*/
996
997DuplicatedIntegritySaveDataArchive.QueryOptimalBucketCount = function(countEntries)
998{
999    return SaveDataArchive.QueryOptimalBucketCount(countEntries);
1000};
1001
1002/*---------------------------------------------------------------------------*/
1003
1004DuplicatedIntegritySaveDataArchive.QuerySizeAsFullDuplication = function(
1005                                                                    inputParamDuplex,
1006                                                                    inputParamIntegrity,
1007                                                                    sizeArchiveBlock,
1008                                                                    countDirectoryEntry,
1009                                                                    countFileEntry,
1010                                                                    countDirectoryEntryBucket,
1011                                                                    countFileEntryBucket,
1012                                                                    countDataBlock
1013                                                                )
1014{
1015    var sizeArchive = SaveDataArchive.QueryTotalSize(
1016                                          countDirectoryEntry,
1017                                          countFileEntry,
1018                                          countDirectoryEntryBucket,
1019                                          countFileEntryBucket,
1020                                          sizeArchiveBlock,
1021                                          countDataBlock
1022                                      );
1023
1024    // Get the size of the duplex file management region with verification feature.
1025    var resultDupInt = new DuplicatedIntegrityFile.QuerySizeResult();
1026    DuplicatedIntegrityFile.QuerySize(
1027                                resultDupInt,
1028                                inputParamDuplex,
1029                                inputParamIntegrity,
1030                                sizeArchive,
1031                                false
1032                            );
1033
1034    // Calculate maximum block size value
1035    var sizeBlock =((inputParamDuplex.sizeBlockLevel[0] - 1)|
1036                    (inputParamDuplex.sizeBlockLevel[1] - 1)|
1037                    (inputParamIntegrity.sizeBlockLevel[0] - 1)|
1038                    (inputParamIntegrity.sizeBlockLevel[1] - 1)|
1039                    (inputParamIntegrity.sizeBlockLevel[2] - 1)|
1040                    (inputParamIntegrity.sizeBlockLevel[3] - 1)) + 1;
1041
1042    // While continuing to reproduce the placement status, calculate the size.
1043    var offsetControlAreaA = DuplicatedIntegritySaveDataArchive.ATOMIC_HEADER_SIZE;
1044    var sizeControlAreaAB = resultDupInt.sizeControlArea;
1045
1046    var offset = offsetControlAreaA;
1047    offset += sizeControlAreaAB;
1048    offset = AdjustAlignment(offset, 8/*sizeof(s64)*/);
1049    offset += sizeControlAreaAB;
1050
1051    // Place the data portion after the duplex file management region with verification feature.
1052    // Insert padding to match the maximum block size.
1053    offset = AdjustAlignment(offset, sizeBlock);
1054    offset += resultDupInt.sizeTotalBody;
1055
1056    // Return total data size.
1057    return offset;
1058};
1059
1060/*---------------------------------------------------------------------------*/
1061
1062DuplicatedIntegritySaveDataArchive.QuerySizeAsMetaDuplication = function(
1063                                                                    inputParamDuplex,
1064                                                                    inputParamIntegrity,
1065                                                                    sizeArchiveBlock,
1066                                                                    countDirectoryEntry,
1067                                                                    countFileEntry,
1068                                                                    countDirectoryEntryBucket,
1069                                                                    countFileEntryBucket,
1070                                                                    countDataBlock
1071                                                                )
1072{
1073    // Request the actual data size.
1074    var sizeData = SaveDataArchive.QueryMinDataSize(
1075                                       countDirectoryEntry,
1076                                       countFileEntry,
1077                                       countDirectoryEntryBucket,
1078                                       countFileEntryBucket,
1079                                       sizeArchiveBlock,
1080                                       countDataBlock
1081                                   );
1082
1083    // Request the metadata size.
1084    var sizeMeta = SaveDataArchive.QueryMetaSize(
1085                                       countDirectoryEntry,
1086                                       countFileEntry,
1087                                       countDirectoryEntryBucket,
1088                                       countFileEntryBucket,
1089                                       sizeArchiveBlock,
1090                                       Math.floor(sizeData / sizeArchiveBlock)
1091                                   );
1092
1093    // Get the size of the duplex file management region with verification feature for the metadata.
1094    var resultDupInt1 = new DuplicatedIntegrityFile.QuerySizeResult();
1095    DuplicatedIntegrityFile.QuerySize(
1096                                resultDupInt1,
1097                                inputParamDuplex,
1098                                inputParamIntegrity,
1099                                sizeMeta,
1100                                false
1101                            );
1102
1103    // Get the size of the duplex file management region with verification feature for the actual data region.
1104    // Duplicate only signature data of the actual data region.
1105    var resultDupInt2 = new DuplicatedIntegrityFile.QuerySizeResult();
1106    // Copy the integrity verification parameter (fix the additional information size to 0)
1107    var inputParamIntegrity2 = new HierarchicalIntegrityVerificationFile.InputParam();
1108    inputParamIntegrity2 = inputParamIntegrity;
1109    inputParamIntegrity2.sizeOptionalInfo = 0;
1110
1111    DuplicatedIntegrityFile.QuerySize(
1112                                resultDupInt2,
1113                                inputParamDuplex,
1114                                inputParamIntegrity2,
1115                                sizeData,
1116                                true
1117                            );
1118
1119    // Calculate maximum block size value
1120    var sizeBlock =((inputParamDuplex.sizeBlockLevel[0] - 1)|
1121                    (inputParamDuplex.sizeBlockLevel[1] - 1)|
1122                    (inputParamIntegrity.sizeBlockLevel[0] - 1)|
1123                    (inputParamIntegrity.sizeBlockLevel[1] - 1)|
1124                    (inputParamIntegrity.sizeBlockLevel[2] - 1)|
1125                    (inputParamIntegrity.sizeBlockLevel[3] - 1)) + 1;
1126
1127    // While continuing to reproduce the placement status, calculate the size.
1128    var offsetDuplicatedIntegrityControlArea1 = DuplicatedIntegritySaveDataArchive.ATOMIC_HEADER_SIZE;
1129    var offsetDuplicatedIntegrityControlArea2 =
1130        AdjustAlignment(resultDupInt1.sizeControlArea, 8/*sizeof(s64)*/);
1131    var sizeControlAreaAB =
1132        offsetDuplicatedIntegrityControlArea2 +
1133        AdjustAlignment(resultDupInt2.sizeControlArea, 8/*sizeof(s64)*/);
1134
1135    var offset = offsetDuplicatedIntegrityControlArea1;
1136    offset += sizeControlAreaAB;
1137    offset += sizeControlAreaAB;
1138
1139    // Place the data portion after the duplex file management region with the verification feature (actual data).
1140    // Insert padding to match the maximum block size.
1141    offset = AdjustAlignment(offset, sizeBlock);
1142    offset += resultDupInt1.sizeTotalBody;
1143
1144    // Place the actual data region after the metadata region.
1145    offset = AdjustAlignment(offset, sizeBlock);
1146    offset += resultDupInt2.sizeTotalBody;
1147
1148    // Return total data size.
1149    return offset;
1150};
1151
1152/*---------------------------------------------------------------------------*/
1153
1154DuplicatedIntegritySaveDataArchive.QuerySize = function(
1155                                                   inputParamDuplex,
1156                                                   inputParamIntegrity,
1157                                                   sizeArchiveBlock,
1158                                                   countDirectoryEntry,
1159                                                   countFileEntry,
1160                                                   countDirectoryEntryBucket,
1161                                                   countFileEntryBucket,
1162                                                   countDataBlock,
1163                                                   option
1164                                               )
1165{
1166    var sizeTotal = 0;
1167
1168    if (option & DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META)
1169    {
1170        sizeTotal = DuplicatedIntegritySaveDataArchive.QuerySizeAsMetaDuplication(
1171                        inputParamDuplex,
1172                        inputParamIntegrity,
1173                        sizeArchiveBlock,
1174                        countDirectoryEntry,
1175                        countFileEntry,
1176                        countDirectoryEntryBucket,
1177                        countFileEntryBucket,
1178                        countDataBlock
1179                    );
1180    }
1181    else if (option & DuplicatedIntegritySaveDataArchive.DUPLICATE_FULL)
1182    {
1183        sizeTotal = DuplicatedIntegritySaveDataArchive.QuerySizeAsFullDuplication(
1184                        inputParamDuplex,
1185                        inputParamIntegrity,
1186                        sizeArchiveBlock,
1187                        countDirectoryEntry,
1188                        countFileEntry,
1189                        countDirectoryEntryBucket,
1190                        countFileEntryBucket,
1191                        countDataBlock
1192                    );
1193    }
1194    else
1195    {
1196        throw new ResultInvalidArgument();
1197    }
1198
1199    return sizeTotal;
1200};
1201
1202/*===========================================================================*/
1203/* Please see man pages for details
1204
1205
1206*/
1207function ExtSaveDataFilterArchive()
1208{
1209};
1210
1211/*---------------------------------------------------------------------------*/
1212
1213ExtSaveDataFilterArchive.QueryFilteredFileSizeInternal = function(
1214                             sizeOriginalData,
1215                             isMetaFile
1216                         )
1217{
1218    // Duplex parameter
1219    var inputParamDuplex = new HierarchicalDuplexFile.InputParam();
1220
1221    inputParamDuplex.sizeBlockLevel[0] = 128;
1222    inputParamDuplex.sizeBlockLevel[1] = 4096;
1223
1224    // Integrity verification parameter
1225    var inputParamIntegrity = new HierarchicalIntegrityVerificationFile.InputParam();
1226
1227    inputParamIntegrity.sizeBlockLevel[0] = 512;
1228    inputParamIntegrity.sizeBlockLevel[1] = 512;
1229    inputParamIntegrity.sizeBlockLevel[2] = 4096;
1230    inputParamIntegrity.sizeBlockLevel[3] = 4096;
1231    inputParamIntegrity.sizeOptionalInfo = 0;
1232
1233    // Request the file size of the duplex + integrity verification file.
1234    var resultDupIntFilter = new DuplicatedIntegrityFilterFile.QuerySizeResult();
1235
1236    DuplicatedIntegrityFilterFile.QuerySize(
1237                                      resultDupIntFilter,
1238                                      inputParamDuplex,
1239                                      inputParamIntegrity,
1240                                      sizeOriginalData,
1241                                      !isMetaFile // Duplicate all of the metafiles only
1242                                  );
1243
1244    return resultDupIntFilter.sizeTotalBody;
1245};
1246
1247/*---------------------------------------------------------------------------*/
1248
1249ExtSaveDataFilterArchive.QueryFilteredMetaFileSize = function(
1250                             sizeMetaData
1251                         )
1252{
1253    return ExtSaveDataFilterArchive.QueryFilteredFileSizeInternal(
1254                                        sizeMetaData,
1255                                        true
1256                                    );
1257};
1258
1259/*---------------------------------------------------------------------------*/
1260
1261ExtSaveDataFilterArchive.QueryFilteredFileSize = function(
1262                             sizeOriginalData
1263                         )
1264{
1265    return ExtSaveDataFilterArchive.QueryFilteredFileSizeInternal(
1266                                        sizeOriginalData,
1267                                        false
1268                                    );
1269};
1270
1271/*===========================================================================*/
1272/* Please see man pages for details
1273
1274
1275*/
1276function ExtSaveDataStorageArchive()
1277{
1278};
1279
1280/*---------------------------------------------------------------------------*/
1281
1282ExtSaveDataStorageArchive.ENTRY_SIZE = 32;
1283
1284/*---------------------------------------------------------------------------*/
1285
1286ExtSaveDataStorageArchive.QueryMaxDirectoryEntryCount = function(
1287                              sizeArchiveBlock,
1288                              sizeEntry
1289                          )
1290{
1291    // Divide into two because ".", "..".
1292    return Math.floor(sizeArchiveBlock / sizeEntry) - 2;
1293}
1294
1295/*===========================================================================*/
1296/* Please see man pages for details
1297
1298
1299*/
1300function ExtSaveDataManager()
1301{
1302};
1303
1304/*---------------------------------------------------------------------------*/
1305
1306ExtSaveDataManager.QueryMetaDataSize = function(
1307                                            sizeArchiveBlock,
1308                                            countDirectoryEntry,
1309                                            countFileEntry
1310                                       )
1311{
1312    var countDirectoryBucket = WStringOnBit64PathStorageArchive.QueryOptimalBucketCount(countDirectoryEntry);
1313    var countFileBucket = WStringOnBit64PathStorageArchive.QueryOptimalBucketCount(countFileEntry);
1314    return WStringOnBit64PathStorageArchive.QueryTotalSize(
1315                                                countDirectoryEntry,
1316                                                countDirectoryBucket,
1317                                                countFileEntry,
1318                                                countFileBucket,
1319                                                sizeArchiveBlock
1320                                            );
1321};
1322
1323/*---------------------------------------------------------------------------*/
1324
1325ExtSaveDataManager.QueryTotalQuotaSize = function(
1326                                            sizeArchiveBlock,
1327                                            countDirectoryEntry,
1328                                            countFileEntry,
1329                                            sizeIcon,
1330                                            fileSizeArray,
1331                                            fileSizeArrayLength
1332                                         )
1333{
1334    countDirectoryEntry += 2; //  Added directories for user and boss
1335    countFileEntry += 1;      //  Add icon file portion TODO: Remove when there is shared expanded save data?
1336    var sizeMetaData = ExtSaveDataManager.QueryMetaDataSize(
1337                                              sizeArchiveBlock,
1338                                              countDirectoryEntry,
1339                                              countFileEntry
1340                                          );
1341    var resultDupIntFilter = new DuplicatedIntegrityFilterFile.QuerySizeResult();
1342    var sizeFilteredMetaData = ExtSaveDataFilterArchive.QueryFilteredMetaFileSize(sizeMetaData);
1343    var filteredMetaDataBlocks = Math.floor((sizeFilteredMetaData + sizeArchiveBlock - 1) / sizeArchiveBlock);
1344
1345    var fileBlocks = 0;
1346    for (var i = 0; i < fileSizeArrayLength; i++)
1347    {
1348        var sizeFilteredFile = ExtSaveDataFilterArchive.QueryFilteredFileSize(fileSizeArray[i]);
1349        fileBlocks += Math.floor((sizeFilteredFile + sizeArchiveBlock - 1) / sizeArchiveBlock);
1350    }
1351
1352    //  Number of files that can be in one directory
1353    var countMaxEntryPerDirectory = ExtSaveDataStorageArchive.QueryMaxDirectoryEntryCount(
1354                                                                  sizeArchiveBlock,
1355                                                                  ExtSaveDataStorageArchive.ENTRY_SIZE
1356                                                              );
1357
1358    //  Number of directories created when the maximum number of files (countFileEntry files) are created
1359    var countMaxDirEntries = Math.floor((countFileEntry + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory);
1360    if (countMaxDirEntries == 0)
1361    {
1362        countMaxDirEntries = 1;
1363    }
1364    //  Number of blocks required by root
1365    var rootEntryBlocks = Math.floor((countMaxDirEntries + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory);
1366
1367    // Number of directories that store files, including metafiles and icon files
1368    var countDirEntries = Math.floor(((fileSizeArrayLength + 2) + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory);
1369
1370    var iconBlocks = 0;
1371    {
1372        var sizeFilterdIcon = ExtSaveDataFilterArchive.QueryFilteredFileSize(sizeIcon);
1373        iconBlocks = Math.floor((sizeFilterdIcon + sizeArchiveBlock - 1) / sizeArchiveBlock);
1374    }
1375
1376    return (filteredMetaDataBlocks
1377          + fileBlocks
1378          + countDirEntries
1379          + rootEntryBlocks
1380          + iconBlocks) * sizeArchiveBlock;
1381};
1382
1383/*===========================================================================*/
1384function ClearDebugPrint()
1385{
1386    if (!document.FsDebug) return;
1387
1388    var out = document.FsDebug.output;
1389    if (!out) return;
1390    out.value = "";
1391};
1392
1393/*---------------------------------------------------------------------------*/
1394
1395function DebugPrint(str)
1396{
1397    if (!document.FsDebug) return;
1398
1399    var out = document.FsDebug.output;
1400    if (!out) return;
1401    out.value += str + "\n";
1402};
1403
1404/*===========================================================================*/
1405
1406CalcFsSpaceResult = function()
1407{
1408    this.sizeTotal = 0;
1409    this.countDataBlock = 0;
1410};
1411
1412/*---------------------------------------------------------------------------*/
1413
1414function calcFsSpaceInternal(
1415             outResult,
1416             countDirectoryEntry,
1417             countFileEntry,
1418             sizeArchiveBlock,
1419             sizeCapacity,
1420             option
1421         )
1422{
1423    // Number of directory/file packets
1424    var countDirectoryEntryBucket = DuplicatedIntegritySaveDataArchive
1425                                        .QueryOptimalBucketCount(countDirectoryEntry);
1426    var countFileEntryBucket = DuplicatedIntegritySaveDataArchive
1427                                        .QueryOptimalBucketCount(countFileEntry);
1428
1429    // Duplex parameter
1430    var inputParamDuplex = new HierarchicalDuplexFile.InputParam();
1431
1432    inputParamDuplex.sizeBlockLevel[0] = 128;
1433    inputParamDuplex.sizeBlockLevel[1] = 512 * 8;
1434
1435    // Integrity verification parameter
1436    var inputParamIntegrity = new HierarchicalIntegrityVerificationFile.InputParam();
1437
1438    // Change according to duplex type and block size
1439    switch (sizeArchiveBlock)
1440    {
1441    case 512:
1442        if (option & DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META)
1443        {
1444            inputParamIntegrity.sizeBlockLevel[0] = 512;
1445            inputParamIntegrity.sizeBlockLevel[1] = 512;
1446            inputParamIntegrity.sizeBlockLevel[2] = 512 * 8;
1447            inputParamIntegrity.sizeBlockLevel[3] = 512;
1448            inputParamIntegrity.sizeOptionalInfo = 0;
1449        }
1450        else if (option & DuplicatedIntegritySaveDataArchive.DUPLICATE_FULL)
1451        {
1452            inputParamIntegrity.sizeBlockLevel[0] = 512;
1453            inputParamIntegrity.sizeBlockLevel[1] = 512;
1454            inputParamIntegrity.sizeBlockLevel[2] = 512 * 8;
1455            inputParamIntegrity.sizeBlockLevel[3] = 512 * 8;
1456            inputParamIntegrity.sizeOptionalInfo = 0;
1457        }
1458        else
1459        {
1460            throw new ResultInvalidArgument();
1461        }
1462        break;
1463    case 4096:
1464        {
1465            inputParamIntegrity.sizeBlockLevel[0] = 512;
1466            inputParamIntegrity.sizeBlockLevel[1] = 512;
1467            inputParamIntegrity.sizeBlockLevel[2] = 512 * 8;
1468            inputParamIntegrity.sizeBlockLevel[3] = 512 * 8;
1469            inputParamIntegrity.sizeOptionalInfo = 0;
1470        }
1471        break;
1472    default:
1473        throw new ResultInvalidArgument();
1474        break;
1475    }
1476
1477    // Total size
1478    var sizeTotal = 0;
1479
1480    // Adjust the total number of blocks to match the capacity.
1481    var countDataBlockMin = 0;
1482    var countDataBlockMax = Math.floor(sizeCapacity / sizeArchiveBlock);
1483    var retryCount = 32;
1484
1485    while (1 <= (countDataBlockMax - countDataBlockMin) && (0 < retryCount--))
1486    {
1487        var countDataBlock = Math.floor((countDataBlockMin + countDataBlockMax) / 2);
1488
1489        sizeTotal = DuplicatedIntegritySaveDataArchive.QuerySize(
1490                                                           inputParamDuplex,
1491                                                           inputParamIntegrity,
1492                                                           sizeArchiveBlock,
1493                                                           countDirectoryEntry,
1494                                                           countFileEntry,
1495                                                           countDirectoryEntryBucket,
1496                                                           countFileEntryBucket,
1497                                                           countDataBlock,
1498                                                           option
1499                                                       );
1500
1501        DebugPrint("sizeCapacity = " + sizeCapacity
1502                 + ", sizeTotal = " + sizeTotal
1503                 + ", countDataBlockMax = " + countDataBlockMax
1504                 + ", countDataBlockMin = " + countDataBlockMin);
1505
1506        if (sizeTotal > sizeCapacity)
1507        {
1508            countDataBlockMax = countDataBlock;
1509        }
1510        else
1511        {
1512            countDataBlockMin = countDataBlock;
1513        }
1514
1515        // Determine whether to exit midway (no changes beyond this even if continued)
1516        if ((countDataBlockMax - countDataBlockMin) <= 1
1517         && (countDataBlock == countDataBlockMin))
1518        {
1519            break;
1520        }
1521    }
1522
1523    // Calculated block count
1524    outResult.countDataBlock = countDataBlockMin;
1525
1526    // Calculate the final size.
1527    sizeTotal = DuplicatedIntegritySaveDataArchive.QuerySize(
1528                                                       inputParamDuplex,
1529                                                       inputParamIntegrity,
1530                                                       sizeArchiveBlock,
1531                                                       countDirectoryEntry,
1532                                                       countFileEntry,
1533                                                       countDirectoryEntryBucket,
1534                                                       countFileEntryBucket,
1535                                                       countDataBlock,
1536                                                       option
1537                                                   );
1538    if (sizeTotal <= sizeCapacity)
1539    {
1540        outResult.sizeTotal = sizeTotal;
1541    }
1542    else
1543    {
1544        outResult.sizeTotal = sizeTotal;
1545    }
1546};
1547
1548/*---------------------------------------------------------------------------*/
1549
1550var capacityUnit;
1551var type2BlockSize;
1552
1553function calcFsSpace()
1554{
1555    // Clear debug output
1556    ClearDebugPrint();
1557
1558    if (!document.FsSpace) return;
1559
1560    var FsSpace = document.FsSpace;
1561
1562    if (FsSpace.fsblocksize[0].checked)
1563    {
1564        if (FsSpace.capacitytype[2].checked)
1565        {
1566            FsSpace.capacitytype[0].checked = true;
1567        }
1568
1569        FsSpace.capacitytype[0].disabled = false;
1570        FsSpace.capacitytype[0].readonly = true;
1571
1572        FsSpace.capacitytype[1].disabled = false;
1573        FsSpace.capacitytype[1].readonly = true;
1574
1575        FsSpace.capacitytype[2].disabled = true;
1576        FsSpace.capacitytype[2].readonly = false;
1577    }
1578    else
1579    {
1580        FsSpace.capacitytype[2].checked = true;
1581
1582        FsSpace.capacitytype[0].disabled = true;
1583        FsSpace.capacitytype[0].readonly = false;
1584
1585        FsSpace.capacitytype[1].disabled = true;
1586        FsSpace.capacitytype[1].readonly = false;
1587
1588        FsSpace.capacitytype[2].disabled = false;
1589        FsSpace.capacitytype[2].readonly = true;
1590    }
1591
1592    if (FsSpace.capacitytype[2].checked)
1593    {
1594        FsSpace.capacitytypeuser.disabled = false;
1595        FsSpace.capacitytypeuser.readonly = true;
1596    }
1597    else
1598    {
1599        FsSpace.capacitytypeuser.readonly = false;
1600        FsSpace.capacitytypeuser.disabled = true;
1601    }
1602
1603    // Full duplex or half duplex
1604    var option = 0;
1605    if (FsSpace.fstype[0].checked)
1606    {
1607        option |= DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META;
1608    }
1609    else if (FsSpace.fstype[1].checked)
1610    {
1611        option |= DuplicatedIntegritySaveDataArchive.DUPLICATE_FULL;
1612    }
1613
1614    // Block size
1615    var sizeArchiveBlock = 512;
1616    if (FsSpace.fsblocksize[1].checked)
1617    {
1618        sizeArchiveBlock = type2BlockSize;
1619    }
1620
1621    // Maximum number of directories/files
1622    var countDirectoryEntry = parseInt(FsSpace.CountDirectoryEntry.value);
1623    var countFileEntry = parseInt(FsSpace.CountFileEntry.value);
1624
1625    // Input value compensation
1626    if (isNaN(countDirectoryEntry) || countDirectoryEntry < 0)
1627    {
1628        countDirectoryEntry = 0;
1629    }
1630    if (isNaN(countFileEntry) || countFileEntry < 1)
1631    {
1632        countFileEntry = 1;
1633    }
1634
1635    // Rewrite compensated result
1636    FsSpace.CountDirectoryEntry.value = countDirectoryEntry;
1637    FsSpace.CountFileEntry.value = countFileEntry;
1638
1639    // Overall capacity
1640    var sizeCapacity = parseInt(FsSpace.capacitytypeuser.value) * capacityUnit;
1641
1642    // Input value compensation
1643    if (isNaN(sizeCapacity) || sizeCapacity < 1 * capacityUnit)
1644    {
1645        sizeCapacity = 1 * capacityUnit;
1646    } else if (sizeCapacity > 2047 * capacityUnit && capacityUnit == 1024 * 1024)
1647    {
1648        // Upper limit corrected only when card2.
1649        sizeCapacity = 2047 * capacityUnit;
1650    }
1651
1652    // Rewrite compensated result
1653    FsSpace.capacitytypeuser.value = Math.floor(sizeCapacity / capacityUnit);
1654
1655    if (FsSpace.capacitytype[0].checked)
1656    {
1657        sizeCapacity = 120 * 1024;
1658    }
1659    else if (FsSpace.capacitytype[1].checked)
1660    {
1661        sizeCapacity = 504 * 1024;
1662    }
1663
1664    // Calculate save data size based on obtained parameters.
1665    var result = new CalcFsSpaceResult();
1666
1667    calcFsSpaceInternal(
1668         result,
1669         countDirectoryEntry,
1670         countFileEntry,
1671         sizeArchiveBlock,
1672         sizeCapacity,
1673         option
1674     )
1675
1676    var sizeTotal = result.sizeTotal;
1677    var countDataBlock = result.countDataBlock;
1678
1679    FsSpace.SaveDataBlockSize.value = sizeArchiveBlock;
1680    FsSpace.SaveDataCapacities.value = countDataBlock * sizeArchiveBlock;
1681    FsSpace.SaveDataBlocks.value = countDataBlock;
1682    FsSpace.SaveDataCapacitiesKilloByte.value = Math.floor(countDataBlock * sizeArchiveBlock / 1024);
1683};
1684
1685/*---------------------------------------------------------------------------*/
1686
1687function calcFsSpaceExtMeta()
1688{
1689    // Clear debug output
1690    ClearDebugPrint();
1691
1692    if (!document.FsSpaceExtEntry) return;
1693
1694    var FsSpace = document.FsSpaceExtEntry;
1695
1696    // Full duplex or half duplex
1697    var option = DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META;
1698
1699    // Block size
1700    var sizeArchiveBlock = 4096;
1701
1702    // Icon size
1703    var sizeIcon = parseInt(FsSpace.IconSize.value);
1704
1705    // Input value compensation
1706    if (isNaN(sizeIcon) || sizeIcon <= 0)
1707    {
1708        sizeIcon = 1;
1709    }
1710
1711    // Rewrite compensated result
1712    FsSpace.IconSize.value = sizeIcon;
1713
1714    // Maximum number of directories/files
1715    var countDirectoryEntry = parseInt(FsSpace.CountDirectoryEntry.value);
1716    var countFileEntry = parseInt(FsSpace.CountFileEntry.value);
1717
1718    // Input value compensation
1719    if (isNaN(countDirectoryEntry) || countDirectoryEntry < 0)
1720    {
1721        countDirectoryEntry = 0;
1722    }
1723    if (isNaN(countFileEntry) || countFileEntry < 1)
1724    {
1725        countFileEntry = 1;
1726    }
1727
1728    // Rewrite compensated result
1729    FsSpace.CountDirectoryEntry.value = countDirectoryEntry;
1730    FsSpace.CountFileEntry.value = countFileEntry;
1731
1732    // Blank the file list
1733    var arrayFileSize = [];
1734
1735    // Calculate save data size based on obtained parameters.
1736    var sizeTotal = ExtSaveDataManager.QueryTotalQuotaSize(
1737                                           sizeArchiveBlock,
1738                                           countDirectoryEntry,
1739                                           countFileEntry,
1740                                           sizeIcon,
1741                                           arrayFileSize,
1742                                           0);
1743    var countDataBlock = Math.floor((sizeTotal + sizeArchiveBlock - 1) / sizeArchiveBlock);
1744
1745    FsSpace.ExtSaveDataBlocks.value = countDataBlock;
1746    FsSpace.ExtSaveDataCapacities.value = sizeTotal;
1747    FsSpace.ExtSaveDataCapacitiesKilloByte.value = Math.floor(sizeTotal / 1024);
1748};
1749
1750/*---------------------------------------------------------------------------*/
1751
1752function calcFsSpaceExtFile()
1753{
1754    // Clear debug output
1755    ClearDebugPrint();
1756
1757    if (!document.FsSpaceExtFile) return;
1758
1759    var FsSpace = document.FsSpaceExtFile;
1760
1761    // Full duplex or half duplex
1762    var option = DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META;
1763
1764    // Block size
1765    var sizeArchiveBlock = 4096;
1766
1767    // Overall capacity
1768    var sizeCapacity = parseInt(FsSpace.capacitytypeuser.value) * 1024;
1769
1770    // Input value compensation
1771    if (isNaN(sizeCapacity) || sizeCapacity < 0)
1772    {
1773        sizeCapacity = 0;
1774    }
1775
1776    // Rewrite compensated result
1777    FsSpace.capacitytypeuser.value = Math.floor(sizeCapacity / 1024);
1778
1779    // Calculate save data size based on obtained parameters.
1780    var sizeTotal = ExtSaveDataFilterArchive.QueryFilteredFileSize(sizeCapacity);
1781    var countDataBlock = Math.floor((sizeTotal + sizeArchiveBlock - 1) / sizeArchiveBlock);
1782
1783    // Each 124 + 126 * n times, a directory is created, so the maximum value that can be consumed is displayed
1784    countDataBlock += 1;
1785    sizeTotal = countDataBlock * sizeArchiveBlock;
1786
1787    FsSpace.ExtSaveDataBlocks.value = countDataBlock;
1788    FsSpace.ExtSaveDataCapacities.value = sizeTotal;
1789    FsSpace.ExtSaveDataCapacitiesKilloByte.value = Math.floor(sizeTotal / 1024);
1790};
1791