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/*!
18    @brief アラインメント調整用関数
19*/
20function AdjustAlignment(val, alignment)
21{
22    return ((val + (alignment - 1)) & ~(alignment - 1));
23};
24
25/*===========================================================================*/
26/*!
27    @brief ビット列の左側からの連続する 0 のビットの数を数えます。
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/*!
51    @brief 値が 2 の整数乗かどうかを判定します。
52*/
53function IsPwr2(val)
54{
55    return (0 == (val & (val - 1)));
56};
57
58/*---------------------------------------------------------------------------*/
59/*!
60    @brief 整数の 2 を底とする対数を計算し、結果を整数で返します。
61*/
62function ILog2(val)
63{
64    return 31 - CntLz(val);
65};
66
67/*---------------------------------------------------------------------------*/
68/*!
69    @brief 例外定義
70*/
71function ResultInvalidArgument()
72{
73};
74
75/*===========================================================================*/
76/*!
77    @brief エントリマップテーブル
78    @ref EntryMapTable
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/*!
157    @brief アロケーションテーブル
158    @ref AllocationTableTemplate
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/*!
181    @brief ファイルシステム管理情報
182    @ref HierarchicalDuplexFile
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/*!
221    @brief 階層化二重化ファイル
222    @ref HierarchicalDuplexFile
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    // 管理領域サイズが確定
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    // bit単位のデータサイズ
303    sizeLevel[1] = Math.floor(((sizeLevel[2] + inputParam.sizeBlockLevel[1] - 1) / inputParam.sizeBlockLevel[1]));
304    // bit -> byteに変換します
305    sizeLevel[1] = Math.floor((sizeLevel[1] + 7) / 8);
306    // サイズをL1ブロック境界に整合します
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    // サイズを DPFS_BITMAP_ALIGN バイト境界に整合します
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, L1 は少なくとも 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        // 実データはL2ブロックサイズに整合しておきます。
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    // 実データのサイズが確定
344    outResult.sizeBody = offset;
345};
346
347/*===========================================================================*/
348/*!
349    @brief 階層化完全性検証ファイル
350    @ref HierarchicalDuplexFile
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    // 管理領域サイズが確定
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    // マスターハッシュサイズが確定
451    outResult.sizeMasterHash = sizeLevel[0];
452
453    // レベル情報を構築
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    // レイヤーハッシュサイズが確定
478    outResult.sizeLayeredHash = levelInfo[MAX_LEVEL - 3].offset
479                              + levelInfo[MAX_LEVEL - 3].size
480                              - levelInfo[0].offset;
481
482    // 実データのサイズが確定
483    outResult.sizeBody = levelInfo[MAX_LEVEL - 2].size;
484
485    // トータルサイズが確定
486    outResult.sizeTotal = levelInfo[MAX_LEVEL - 2].offset
487                        + levelInfo[MAX_LEVEL - 2].size
488                        - levelInfo[0].offset;
489};
490
491/*===========================================================================*/
492/*!
493    @brief 二重化+完全性検証ファイル
494    @ref DuplicatedIntegrityFile
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    // 階層化完全性検証ファイルのサイズを計算します。
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    // 階層化二重化ファイルのサイズを計算します。
554    var resultDuplex = new HierarchicalDuplexFile.QuerySizeResult();
555
556    HierarchicalDuplexFile.QuerySize(
557                               resultDuplex,
558                               inputParamDuplex,
559                               sizeDuplicateOriginal
560                           );
561
562    // 二重化+完全性検証ファイルのサイズを計算します。
563    var offsetOriginalData;
564    if (!isDuplicateOnlyHash)
565    {
566        offsetOriginalData = 0;
567        outResult.sizeTotalBody = resultDuplex.sizeBody;
568    }
569    else
570    {
571        // 二重化した署名データの後ろに実データを配置
572        offsetOriginalData = AdjustAlignment(resultDuplex.sizeBody, inputParamIntegrity.sizeBlockLevel[3]);
573        outResult.sizeTotalBody = offsetOriginalData + sizeOriginalData;
574    }
575
576    // メタ情報
577    var offset = DuplicatedIntegrityFile.META_INFO_SIZE;
578
579    // 完全性検証レイヤー管理領域
580    offset = AdjustAlignment(offset, 4);
581    offset += resultIntegrity.sizeControlArea;
582
583    // 二重化レイヤー管理領域
584    offset = AdjustAlignment(offset, 4);
585    offset += resultDuplex.sizeControlArea;
586
587    // マスター署名
588    offset = AdjustAlignment(offset, 4);
589    offset += resultIntegrity.sizeMasterHash;
590
591    // 最終的な管理領域のサイズ
592    outResult.sizeControlArea = offset;
593};
594
595/*===========================================================================*/
596/*!
597    @brief 二重化+完全性検証フィルタファイル
598    @ref DuplicatedIntegrityFile
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    // フィルタファイルではオプション領域は使用しません。
626    inputParamDuplex.sizeOptionalInfo = 0;
627
628    // 検証機能つき二重化ファイル管理領域のサイズを取得します。
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    // 二重化された管理領域A
641    offset = AdjustAlignment(offset, 8/*sizeof(s64)*/);
642    offset += resultDupInt.sizeControlArea;
643
644    // 二重化された管理領域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    // パディングを挿入しつつ、検証機能つき二重化ファイルの管理領域の後に
656    // データ部を配置します。
657    offset = AdjustAlignment(offset, maxBlockSize);
658    offset += resultDupInt.sizeTotalBody;;
659
660    // 最終的なサイズを返します。
661    outResult.sizeTotalBody = offset;
662};
663
664/*===========================================================================*/
665/*!
666    @brief ファイル ID をパスとするアーカイブを文字列パスを用いて透過的に扱うためのアーカイブクラス
667    @ref WStringOnBit64PathStorageArchive
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        // エントリー数が極めて少ない場合、バケット数は固定サイズを返します。
747        return 3;
748    }
749    if (countEntries <= 19)
750    {
751        // エントリー数が 20 個未満の場合、奇数にして返します。
752        return countEntries | 1;
753    }
754
755    // エントリー数が20個を超える場合、バケットの分散具合を考慮し、
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/*!
804    @brief セーブデータアーカイブクラス
805    @ref SaveDataArchiveTemplate
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: WStringOnBit64PathStorageArchive.QueryOptimalBucketCount に統一することを検討
874    if (countEntries <= 3)
875    {
876        // エントリー数が極めて少ない場合、バケット数は固定サイズを返します。
877        return 3;
878    }
879    if (countEntries <= 19)
880    {
881        // エントリー数が 20 個未満の場合、奇数にして返します
882        return countEntries | 1;
883    }
884
885    // エントリー数が20個を超える場合、バケットの分散具合を考慮し、
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/*!
981    @brief 二重化+完全性検証セーブデータアーカイブクラス
982    @ref DuplicatedIntegritySaveDataArchive
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    // 検証機能つき二重化ファイル管理領域のサイズを取得します。
1025    var resultDupInt = new DuplicatedIntegrityFile.QuerySizeResult();
1026    DuplicatedIntegrityFile.QuerySize(
1027                                resultDupInt,
1028                                inputParamDuplex,
1029                                inputParamIntegrity,
1030                                sizeArchive,
1031                                false
1032                            );
1033
1034    // ブロックサイズの最大値を計算
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    // 配置状態を再現しつつサイズを計算します。
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    // 検証機能つき二重化ファイルの管理領域の後にデータ部分を配置します。
1052    // 最大のブロックサイズに合わせるためパディングを挿入します。
1053    offset = AdjustAlignment(offset, sizeBlock);
1054    offset += resultDupInt.sizeTotalBody;
1055
1056    // 総データサイズを返します。
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    // 実データのサイズを求めます。
1074    var sizeData = SaveDataArchive.QueryMinDataSize(
1075                                       countDirectoryEntry,
1076                                       countFileEntry,
1077                                       countDirectoryEntryBucket,
1078                                       countFileEntryBucket,
1079                                       sizeArchiveBlock,
1080                                       countDataBlock
1081                                   );
1082
1083    // メタデータのサイズを求めます。
1084    var sizeMeta = SaveDataArchive.QueryMetaSize(
1085                                       countDirectoryEntry,
1086                                       countFileEntry,
1087                                       countDirectoryEntryBucket,
1088                                       countFileEntryBucket,
1089                                       sizeArchiveBlock,
1090                                       Math.floor(sizeData / sizeArchiveBlock)
1091                                   );
1092
1093    // メタデータに対する検証機能つき二重化ファイル管理領域のサイズを取得します。
1094    var resultDupInt1 = new DuplicatedIntegrityFile.QuerySizeResult();
1095    DuplicatedIntegrityFile.QuerySize(
1096                                resultDupInt1,
1097                                inputParamDuplex,
1098                                inputParamIntegrity,
1099                                sizeMeta,
1100                                false
1101                            );
1102
1103    // 実データ領域に対する検証機能つき二重化ファイル管理領域のサイズを取得します。
1104    // 実データ領域の署名データのみ二重化します。
1105    var resultDupInt2 = new DuplicatedIntegrityFile.QuerySizeResult();
1106    // 完全性検証パラメータを複製(付加情報サイズを 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    // ブロックサイズの最大値を計算
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    // 配置状態を再現しつつサイズを計算します。
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    // 検証機能つき二重化ファイルの管理領域(実データ)の後にメタデータ領域を配置します。
1140    // 最大のブロックサイズに合わせるためパディングを挿入します。
1141    offset = AdjustAlignment(offset, sizeBlock);
1142    offset += resultDupInt1.sizeTotalBody;
1143
1144    // メタデータ領域の後に実データ領域を配置します。
1145    offset = AdjustAlignment(offset, sizeBlock);
1146    offset += resultDupInt2.sizeTotalBody;
1147
1148    // 総データサイズを返します。
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/*!
1204    @brief 拡張セーブデータフィルタアーカイブクラス
1205    @ref ExtSaveDataFilterArchive
1206*/
1207function ExtSaveDataFilterArchive()
1208{
1209};
1210
1211/*---------------------------------------------------------------------------*/
1212
1213ExtSaveDataFilterArchive.QueryFilteredFileSizeInternal = function(
1214                             sizeOriginalData,
1215                             isMetaFile
1216                         )
1217{
1218    // 二重化パラメータ
1219    var inputParamDuplex = new HierarchicalDuplexFile.InputParam();
1220
1221    inputParamDuplex.sizeBlockLevel[0] = 128;
1222    inputParamDuplex.sizeBlockLevel[1] = 4096;
1223
1224    // 完全性検証パラメータ
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    // 二重化+完全性検証フィルタファイルのサイズを求めます。
1234    var resultDupIntFilter = new DuplicatedIntegrityFilterFile.QuerySizeResult();
1235
1236    DuplicatedIntegrityFilterFile.QuerySize(
1237                                      resultDupIntFilter,
1238                                      inputParamDuplex,
1239                                      inputParamIntegrity,
1240                                      sizeOriginalData,
1241                                      !isMetaFile // メタファイルのみ全二重化
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/*!
1273    @brief 拡張セーブデータストレージアーカイブクラス
1274    @ref ExtSaveDataStorageArchive
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    // "."、".."のために 2 つ分除きます。
1292    return Math.floor(sizeArchiveBlock / sizeEntry) - 2;
1293}
1294
1295/*===========================================================================*/
1296/*!
1297    @brief 拡張セーブデータ管理クラス
1298    @ref ExtSaveDataManager
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; //  user, boss ディレクトリの分を追加
1335    countFileEntry += 1;      //  icon ファイルの分を追加 TODO: 共有拡張セーブデータの場合ははずす?
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    //  ディレクトリひとつ当たりに入るファイル数
1353    var countMaxEntryPerDirectory = ExtSaveDataStorageArchive.QueryMaxDirectoryEntryCount(
1354                                                                  sizeArchiveBlock,
1355                                                                  ExtSaveDataStorageArchive.ENTRY_SIZE
1356                                                              );
1357
1358    //  ファイル数を最大まで(countFileEntry個)作成したときに作られるディレクトリの数
1359    var countMaxDirEntries = Math.floor((countFileEntry + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory);
1360    if (countMaxDirEntries == 0)
1361    {
1362        countMaxDirEntries = 1;
1363    }
1364    //  ルートが必要とするブロック数
1365    var rootEntryBlocks = Math.floor((countMaxDirEntries + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory);
1366
1367    // メタファイルやアイコンファイルを含むファイルが格納されるディレクトリの数
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    // ディレクトリ/ファイルバケット数
1424    var countDirectoryEntryBucket = DuplicatedIntegritySaveDataArchive
1425                                        .QueryOptimalBucketCount(countDirectoryEntry);
1426    var countFileEntryBucket = DuplicatedIntegritySaveDataArchive
1427                                        .QueryOptimalBucketCount(countFileEntry);
1428
1429    // 二重化パラメータ
1430    var inputParamDuplex = new HierarchicalDuplexFile.InputParam();
1431
1432    inputParamDuplex.sizeBlockLevel[0] = 128;
1433    inputParamDuplex.sizeBlockLevel[1] = 512 * 8;
1434
1435    // 完全性検証パラメータ
1436    var inputParamIntegrity = new HierarchicalIntegrityVerificationFile.InputParam();
1437
1438    // 二重化タイプとブロックサイズに応じて変更
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    // トータルサイズ
1478    var sizeTotal = 0;
1479
1480    // キャパシティに合うように総ブロック数を調整します。
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        // 途中抜け判定(これ以上継続しても変化なし)
1516        if ((countDataBlockMax - countDataBlockMin) <= 1
1517         && (countDataBlock == countDataBlockMin))
1518        {
1519            break;
1520        }
1521    }
1522
1523    // 計算されたブロック数
1524    outResult.countDataBlock = countDataBlockMin;
1525
1526    // 最終的なサイズを計算します。
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    // デバッグ出力クリア
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    // 全二重 or 半二重
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    // ブロックサイズ
1612    var sizeArchiveBlock = 512;
1613    if (FsSpace.fsblocksize[1].checked)
1614    {
1615        sizeArchiveBlock = 4096;
1616    }
1617
1618    // 最大ディレクトリ/ファイル数
1619    var countDirectoryEntry = parseInt(FsSpace.CountDirectoryEntry.value);
1620    var countFileEntry = parseInt(FsSpace.CountFileEntry.value);
1621
1622    // 入力値の補正
1623    if (isNaN(countDirectoryEntry) || countDirectoryEntry < 0)
1624    {
1625        countDirectoryEntry = 0;
1626    }
1627    if (isNaN(countFileEntry) || countFileEntry < 1)
1628    {
1629        countFileEntry = 1;
1630    }
1631
1632    // 補正結果を書き戻す
1633    FsSpace.CountDirectoryEntry.value = countDirectoryEntry;
1634    FsSpace.CountFileEntry.value = countFileEntry;
1635
1636    // 全体容量
1637    var sizeCapacity = parseInt(FsSpace.capacitytypeuser.value) * 1024;
1638
1639    // 入力値の補正
1640    if (isNaN(sizeCapacity) || sizeCapacity < 0)
1641    {
1642        sizeCapacity = 0;
1643    }
1644
1645    // 補正結果を書き戻す
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    // 得られたパラメータを元にセーブデータサイズを計算します。
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    // デバッグ出力クリア
1683    ClearDebugPrint();
1684
1685    if (!document.FsSpaceExtEntry) return;
1686
1687    var FsSpace = document.FsSpaceExtEntry;
1688
1689    // 全二重 or 半二重
1690    var option = DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META;
1691
1692    // ブロックサイズ
1693    var sizeArchiveBlock = 4096;
1694
1695    // アイコンサイズ
1696    var sizeIcon = parseInt(FsSpace.IconSize.value);
1697
1698    // 入力値の補正
1699    if (isNaN(sizeIcon) || sizeIcon <= 0)
1700    {
1701        sizeIcon = 1;
1702    }
1703
1704    // 補正結果を書き戻す
1705    FsSpace.IconSize.value = sizeIcon;
1706
1707    // 最大ディレクトリ/ファイル数
1708    var countDirectoryEntry = parseInt(FsSpace.CountDirectoryEntry.value);
1709    var countFileEntry = parseInt(FsSpace.CountFileEntry.value);
1710
1711    // 入力値の補正
1712    if (isNaN(countDirectoryEntry) || countDirectoryEntry < 0)
1713    {
1714        countDirectoryEntry = 0;
1715    }
1716    if (isNaN(countFileEntry) || countFileEntry < 1)
1717    {
1718        countFileEntry = 1;
1719    }
1720
1721    // 補正結果を書き戻す
1722    FsSpace.CountDirectoryEntry.value = countDirectoryEntry;
1723    FsSpace.CountFileEntry.value = countFileEntry;
1724
1725    // ファイル一覧は空とします
1726    var arrayFileSize = [];
1727
1728    // 得られたパラメータを元にセーブデータサイズを計算します。
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    // デバッグ出力クリア
1748    ClearDebugPrint();
1749
1750    if (!document.FsSpaceExtFile) return;
1751
1752    var FsSpace = document.FsSpaceExtFile;
1753
1754    // 全二重 or 半二重
1755    var option = DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META;
1756
1757    // ブロックサイズ
1758    var sizeArchiveBlock = 4096;
1759
1760    // 全体容量
1761    var sizeCapacity = parseInt(FsSpace.capacitytypeuser.value) * 1024;
1762
1763    // 入力値の補正
1764    if (isNaN(sizeCapacity) || sizeCapacity < 0)
1765    {
1766        sizeCapacity = 0;
1767    }
1768
1769    // 補正結果を書き戻す
1770    FsSpace.capacitytypeuser.value = Math.floor(sizeCapacity / 1024);
1771
1772    // 得られたパラメータを元にセーブデータサイズを計算します。
1773    var sizeTotal = ExtSaveDataFilterArchive.QueryFilteredFileSize(sizeCapacity);
1774    var countDataBlock = Math.floor((sizeTotal + sizeArchiveBlock - 1) / sizeArchiveBlock);
1775
1776    // 124 + 126 * n個のたびにディレクトリが1つ作られるので、消費される可能性のある最大値を表示しておく
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