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    // Determine size of control area
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    // Convert bit -> byte
305    sizeLevel[1] = Math.floor((sizeLevel[1] + 7) / 8);
306    // Align size to border of L1 block
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 size to border of DPFS_BITMAP_ALIGN bytes
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    // Align Master and L1 at least to DPFS_BITMAP_ALIGN.
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        // Align actual data to 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    // Determine size of the actual data
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    // Determine size of control area
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    // Determine master hash size
451    outResult.sizeMasterHash = sizeLevel[0];
452
453    // Construct 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    // Determine size of layer hash
478    outResult.sizeLayeredHash = levelInfo[MAX_LEVEL - 3].offset
479                              + levelInfo[MAX_LEVEL - 3].size
480                              - levelInfo[0].offset;
481
482    // Determine size of the actual data
483    outResult.sizeBody = levelInfo[MAX_LEVEL - 2].size;
484
485    // Determine total size
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    // Calculates 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    // Calculates 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    // Calculates the size of 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 actual data behind the duplexed 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    // Control area for integrity verification layer
580    offset = AdjustAlignment(offset, 4);
581    offset += resultIntegrity.sizeControlArea;
582
583    // Control area for duplex layer
584    offset = AdjustAlignment(offset, 4);
585    offset += resultDuplex.sizeControlArea;
586
587    // Master signature
588    offset = AdjustAlignment(offset, 4);
589    offset += resultIntegrity.sizeMasterHash;
590
591    // Size of final control area
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    // Option area is not used in the filter file.
626    inputParamDuplex.sizeOptionalInfo = 0;
627
628    // Get the size of the control area for the duplex file that has 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    // Duplexed control area A
641    offset = AdjustAlignment(offset, 8/*sizeof(s64)*/);
642    offset += resultDupInt.sizeControlArea;
643
644    // Duplexed control area 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 inserting padding, place the data section after the control area for the duplex file that has verification feature.
656    //
657    offset = AdjustAlignment(offset, maxBlockSize);
658    offset += resultDupInt.sizeTotalBody;;
659
660    // Returns 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 extremely small, return a fixed size for the bucket number.
747        return 3;
748    }
749    if (countEntries <= 19)
750    {
751        // When the number of entries is less than 20, return an odd number
752        return countEntries | 1;
753    }
754
755    // When the number of entries exceeds 20, consider how the bucket is distributed, and then prune with small value prime numbers.
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: We will examine to standardize WStringOnBit64PathStorageArchive.QueryOptimalBucketCount.
874    if (countEntries <= 3)
875    {
876        // When the number of entries is extremely small, return a fixed size for the bucket number.
877        return 3;
878    }
879    if (countEntries <= 19)
880    {
881        // When the number of entries is less than 20, return an odd number
882        return countEntries | 1;
883    }
884
885    // When the number of entries exceeds 20, consider how the bucket is distributed, and then prune with small value prime numbers.
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 control area for the duplex file that has 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 the maximum value of block size
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    // Calculate the size while rebuilding the arrangement.
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    // Position the data portion after the control area for the duplexed file that has verification feature.
1052    // Insert padding to match the maximum block size.
1053    offset = AdjustAlignment(offset, sizeBlock);
1054    offset += resultDupInt.sizeTotalBody;
1055
1056    // Return the 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    // Find the actual data size.
1074    var sizeData = SaveDataArchive.QueryMinDataSize(
1075                                       countDirectoryEntry,
1076                                       countFileEntry,
1077                                       countDirectoryEntryBucket,
1078                                       countFileEntryBucket,
1079                                       sizeArchiveBlock,
1080                                       countDataBlock
1081                                   );
1082
1083    // Find 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 control area for the duplex file that has metadata verification feature.
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 control area for the duplex file that has verification feature for the actual data area.
1104    // Duplicate only the signature data of the actual data area.
1105    var resultDupInt2 = new DuplicatedIntegrityFile.QuerySizeResult();
1106    // Duplicate the integrity verification parameters (additional information size is fixed at 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 the maximum value of block size
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    // Calculate the size while rebuilding the arrangement.
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    // Position the metadata area after the control area (the actual data) for the duplexed file that has verification feature.
1140    // Insert padding to match the maximum block size.
1141    offset = AdjustAlignment(offset, sizeBlock);
1142    offset += resultDupInt1.sizeTotalBody;
1143
1144    // Position the actual data area after the metadata area.
1145    offset = AdjustAlignment(offset, sizeBlock);
1146    offset += resultDupInt2.sizeTotalBody;
1147
1148    // Return the 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 parameters
1219    var inputParamDuplex = new HierarchicalDuplexFile.InputParam();
1220
1221    inputParamDuplex.sizeBlockLevel[0] = 128;
1222    inputParamDuplex.sizeBlockLevel[1] = 4096;
1223
1224    // Integrity verification parameters
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    // Find the size of duplex + integrity verification filter file.
1234    var resultDupIntFilter = new DuplicatedIntegrityFilterFile.QuerySizeResult();
1235
1236    DuplicatedIntegrityFilterFile.QuerySize(
1237                                      resultDupIntFilter,
1238                                      inputParamDuplex,
1239                                      inputParamIntegrity,
1240                                      sizeOriginalData,
1241                                      !isMetaFile // Full duplex only for metafile
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    // Exclude two parts for "." and ".."
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; //  Add entries for "user" and "boss" directories
1335    countFileEntry += 1;      //  Add entry for icon file  TODO: Remove entries for 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 per directory
1353    var countMaxEntryPerDirectory = ExtSaveDataStorageArchive.QueryMaxDirectoryEntryCount(
1354                                                                  sizeArchiveBlock,
1355                                                                  ExtSaveDataStorageArchive.ENTRY_SIZE
1356                                                              );
1357
1358    //  Number of directories to create when the max number of files was created (the number of countFileEntry)
1359    var countMaxDirEntries = Math.floor((countFileEntry + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory);
1360    if (countMaxDirEntries == 0)
1361    {
1362        countMaxDirEntries = 1;
1363    }
1364    //  Number of blocks needed for roots
1365    var rootEntryBlocks = Math.floor((countMaxDirEntries + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory);
1366
1367    // Number of directories that store files that contain 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 directories/file buckets
1424    var countDirectoryEntryBucket = DuplicatedIntegritySaveDataArchive
1425                                        .QueryOptimalBucketCount(countDirectoryEntry);
1426    var countFileEntryBucket = DuplicatedIntegritySaveDataArchive
1427                                        .QueryOptimalBucketCount(countFileEntry);
1428
1429    // Duplex parameters
1430    var inputParamDuplex = new HierarchicalDuplexFile.InputParam();
1431
1432    inputParamDuplex.sizeBlockLevel[0] = 128;
1433    inputParamDuplex.sizeBlockLevel[1] = 512 * 8;
1434
1435    // Integrity verification parameters
1436    var inputParamIntegrity = new HierarchicalIntegrityVerificationFile.InputParam();
1437
1438    // Modify based on 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        // Detect exit while in progress (no changes if continued beyond this point)
1516        if ((countDataBlockMax - countDataBlockMin) <= 1
1517         && (countDataBlock == countDataBlockMin))
1518        {
1519            break;
1520        }
1521    }
1522
1523    // Calculated number of blocks
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
1550function calcFsSpace()
1551{
1552    // Clear debug output
1553    ClearDebugPrint();
1554
1555    if (!document.FsSpace) return;
1556
1557    var FsSpace = document.FsSpace;
1558
1559    if (FsSpace.fsblocksize[0].checked)
1560    {
1561        if (FsSpace.capacitytype[2].checked)
1562        {
1563            FsSpace.capacitytype[0].checked = true;
1564        }
1565
1566        FsSpace.capacitytype[0].disabled = false;
1567        FsSpace.capacitytype[0].readonly = true;
1568
1569        FsSpace.capacitytype[1].disabled = false;
1570        FsSpace.capacitytype[1].readonly = true;
1571
1572        FsSpace.capacitytype[2].disabled = true;
1573        FsSpace.capacitytype[2].readonly = false;
1574    }
1575    else
1576    {
1577        FsSpace.capacitytype[2].checked = true;
1578
1579        FsSpace.capacitytype[0].disabled = true;
1580        FsSpace.capacitytype[0].readonly = false;
1581
1582        FsSpace.capacitytype[1].disabled = true;
1583        FsSpace.capacitytype[1].readonly = false;
1584
1585        FsSpace.capacitytype[2].disabled = false;
1586        FsSpace.capacitytype[2].readonly = true;
1587    }
1588
1589    if (FsSpace.capacitytype[2].checked)
1590    {
1591        FsSpace.capacitytypeuser.disabled = false;
1592        FsSpace.capacitytypeuser.readonly = true;
1593    }
1594    else
1595    {
1596        FsSpace.capacitytypeuser.readonly = false;
1597        FsSpace.capacitytypeuser.disabled = true;
1598    }
1599
1600    // Full duplex or half duplex
1601    var option = 0;
1602    if (FsSpace.fstype[0].checked)
1603    {
1604        option |= DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META;
1605    }
1606    else if (FsSpace.fstype[1].checked)
1607    {
1608        option |= DuplicatedIntegritySaveDataArchive.DUPLICATE_FULL;
1609    }
1610
1611    // Block size
1612    var sizeArchiveBlock = 512;
1613    if (FsSpace.fsblocksize[1].checked)
1614    {
1615        sizeArchiveBlock = 4096;
1616    }
1617
1618    // Maximum number of directories/files
1619    var countDirectoryEntry = parseInt(FsSpace.CountDirectoryEntry.value);
1620    var countFileEntry = parseInt(FsSpace.CountFileEntry.value);
1621
1622    // Correct input value
1623    if (isNaN(countDirectoryEntry) || countDirectoryEntry < 0)
1624    {
1625        countDirectoryEntry = 0;
1626    }
1627    if (isNaN(countFileEntry) || countFileEntry < 1)
1628    {
1629        countFileEntry = 1;
1630    }
1631
1632    // Rewrite correction result.
1633    FsSpace.CountDirectoryEntry.value = countDirectoryEntry;
1634    FsSpace.CountFileEntry.value = countFileEntry;
1635
1636    // Total capacity
1637    var sizeCapacity = parseInt(FsSpace.capacitytypeuser.value) * 1024;
1638
1639    // Correct input value
1640    if (isNaN(sizeCapacity) || sizeCapacity < 0)
1641    {
1642        sizeCapacity = 0;
1643    }
1644
1645    // Rewrite correction result.
1646    FsSpace.capacitytypeuser.value = Math.floor(sizeCapacity / 1024);
1647
1648    if (FsSpace.capacitytype[0].checked)
1649    {
1650        sizeCapacity = 120 * 1024;
1651    }
1652    else if (FsSpace.capacitytype[1].checked)
1653    {
1654        sizeCapacity = 504 * 1024;
1655    }
1656
1657    // Calculate the save data size based on the obtained parameters.
1658    var result = new CalcFsSpaceResult();
1659
1660    calcFsSpaceInternal(
1661         result,
1662         countDirectoryEntry,
1663         countFileEntry,
1664         sizeArchiveBlock,
1665         sizeCapacity,
1666         option
1667     )
1668
1669    var sizeTotal = result.sizeTotal;
1670    var countDataBlock = result.countDataBlock;
1671
1672    FsSpace.SaveDataBlockSize.value = sizeArchiveBlock;
1673    FsSpace.SaveDataCapacities.value = countDataBlock * sizeArchiveBlock;
1674    FsSpace.SaveDataBlocks.value = countDataBlock;
1675    FsSpace.SaveDataCapacitiesKilloByte.value = Math.floor(countDataBlock * sizeArchiveBlock / 1024);
1676};
1677
1678/*---------------------------------------------------------------------------*/
1679
1680function calcFsSpaceExtMeta()
1681{
1682    // Clear debug output
1683    ClearDebugPrint();
1684
1685    if (!document.FsSpaceExtEntry) return;
1686
1687    var FsSpace = document.FsSpaceExtEntry;
1688
1689    // Full duplex or half duplex
1690    var option = DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META;
1691
1692    // Block size
1693    var sizeArchiveBlock = 4096;
1694
1695    // Icon size
1696    var sizeIcon = parseInt(FsSpace.IconSize.value);
1697
1698    // Correct input value
1699    if (isNaN(sizeIcon) || sizeIcon <= 0)
1700    {
1701        sizeIcon = 1;
1702    }
1703
1704    // Rewrite correction result.
1705    FsSpace.IconSize.value = sizeIcon;
1706
1707    // Maximum number of directories/files
1708    var countDirectoryEntry = parseInt(FsSpace.CountDirectoryEntry.value);
1709    var countFileEntry = parseInt(FsSpace.CountFileEntry.value);
1710
1711    // Correct input value
1712    if (isNaN(countDirectoryEntry) || countDirectoryEntry < 0)
1713    {
1714        countDirectoryEntry = 0;
1715    }
1716    if (isNaN(countFileEntry) || countFileEntry < 1)
1717    {
1718        countFileEntry = 1;
1719    }
1720
1721    // Rewrite correction result.
1722    FsSpace.CountDirectoryEntry.value = countDirectoryEntry;
1723    FsSpace.CountFileEntry.value = countFileEntry;
1724
1725    // Empty the file list
1726    var arrayFileSize = [];
1727
1728    // Calculate the save data size based on the obtained parameters.
1729    var sizeTotal = ExtSaveDataManager.QueryTotalQuotaSize(
1730                                           sizeArchiveBlock,
1731                                           countDirectoryEntry,
1732                                           countFileEntry,
1733                                           sizeIcon,
1734                                           arrayFileSize,
1735                                           0);
1736    var countDataBlock = Math.floor((sizeTotal + sizeArchiveBlock - 1) / sizeArchiveBlock);
1737
1738    FsSpace.ExtSaveDataBlocks.value = countDataBlock;
1739    FsSpace.ExtSaveDataCapacities.value = sizeTotal;
1740    FsSpace.ExtSaveDataCapacitiesKilloByte.value = Math.floor(sizeTotal / 1024);
1741};
1742
1743/*---------------------------------------------------------------------------*/
1744
1745function calcFsSpaceExtFile()
1746{
1747    // Clear debug output
1748    ClearDebugPrint();
1749
1750    if (!document.FsSpaceExtFile) return;
1751
1752    var FsSpace = document.FsSpaceExtFile;
1753
1754    // Full duplex or half duplex
1755    var option = DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META;
1756
1757    // Block size
1758    var sizeArchiveBlock = 4096;
1759
1760    // Total capacity
1761    var sizeCapacity = parseInt(FsSpace.capacitytypeuser.value) * 1024;
1762
1763    // Correct input value
1764    if (isNaN(sizeCapacity) || sizeCapacity < 0)
1765    {
1766        sizeCapacity = 0;
1767    }
1768
1769    // Rewrite correction result.
1770    FsSpace.capacitytypeuser.value = Math.floor(sizeCapacity / 1024);
1771
1772    // Calculate the save data size based on the obtained parameters.
1773    var sizeTotal = ExtSaveDataFilterArchive.QueryFilteredFileSize(sizeCapacity);
1774    var countDataBlock = Math.floor((sizeTotal + sizeArchiveBlock - 1) / sizeArchiveBlock);
1775
1776    // One directory is created for each 124 + 126 * n. Accordingly, display the maximum value that could be used.
1777    countDataBlock += 1;
1778    sizeTotal = countDataBlock * sizeArchiveBlock;
1779
1780    FsSpace.ExtSaveDataBlocks.value = countDataBlock;
1781    FsSpace.ExtSaveDataCapacities.value = sizeTotal;
1782    FsSpace.ExtSaveDataCapacitiesKilloByte.value = Math.floor(sizeTotal / 1024);
1783};
1784