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 Alignment adjustment function. 19*/ 20function AdjustAlignment(val, alignment) 21{ 22 return ((val + (alignment - 1)) & ~(alignment - 1)); 23}; 24 25/*===========================================================================*/ 26/*! 27@brief Counts the number of consecutive 0 bits starting from the left of the bit sequence. 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 Determines whether a value is an integer power of 2. 52*/ 53function IsPwr2(val) 54{ 55 return (0 == (val & (val - 1))); 56}; 57 58/*---------------------------------------------------------------------------*/ 59/*! 60@brief Calculates the base 2 logarithm of an integer and returns the result as an integer. 61*/ 62function ILog2(val) 63{ 64 return 31 - CntLz(val); 65}; 66 67/*---------------------------------------------------------------------------*/ 68/*! 69@brief Exception definition. 70*/ 71function ResultInvalidArgument() 72{ 73}; 74 75/*===========================================================================*/ 76/*! 77@brief Entry map table. 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 Allocation table. 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 File system management information. 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 Hierarchical duplex file. 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 // Determine the size of the management region. 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 bits to bytes. 305 sizeLevel[1] = Math.floor((sizeLevel[1] + 7) / 8); 306 // Align the size to the L1 block boundary. 307 sizeLevel[1] = AdjustAlignment(sizeLevel[1], inputParam.sizeBlockLevel[0]); 308 // Bits. 309 sizeLevel[0] = Math.floor(((sizeLevel[1] + inputParam.sizeBlockLevel[0] - 1) / inputParam.sizeBlockLevel[0])); 310 // Convert bits to bytes. 311 sizeLevel[0] = Math.floor((sizeLevel[0] + 7) / 8); 312 // Align the size to the byte boundary of 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 and L1 are aligned to at least 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 // The actual data is aligned to the L2 block size. 332 if (level == MAX_LEVEL - 1) 333 { 334 offset = AdjustAlignment(offset, inputParam.sizeBlockLevel[1]); 335 } 336 337 levelInfo[level].offset = offset; 338 levelInfo[level].size = sizeLevel[level]; 339 levelInfo[level].orderBlock = orderBlock[level]; 340 offset += sizeLevel[level] * 2; 341 } 342 343 // Determine the size of the actual data. 344 outResult.sizeBody = offset; 345}; 346 347/*===========================================================================*/ 348/*! 349@brief Hierarchical integrity verification file. 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 // Determine the size of the management region. 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 the size of the master hash. 451 outResult.sizeMasterHash = sizeLevel[0]; 452 453 // Construct the level information. 454 var levelInfo = new Array(MAX_LEVEL); 455 var level; 456 457 offset = 0; 458 for (level = 1; level < MAX_LEVEL; level++) 459 { 460 levelInfo[level - 1] = new HierarchicalIntegrityVerificationFile.LevelInfomation(); 461 462 if (sizeLevel[level] >= (4 << orderBlock[level])) 463 { 464 offset = AdjustAlignment(offset, 1 << orderBlock[level]); 465 } 466 else 467 { 468 offset = AdjustAlignment(offset, 8/*sizeof(s64)*/); 469 } 470 471 levelInfo[level - 1].offset = offset; 472 levelInfo[level - 1].size = sizeLevel[level]; 473 levelInfo[level - 1].orderBlock = orderBlock[level]; 474 offset += sizeLevel[level]; 475 } 476 477 // Determine the size of the layer hash. 478 outResult.sizeLayeredHash = levelInfo[MAX_LEVEL - 3].offset 479 + levelInfo[MAX_LEVEL - 3].size 480 - levelInfo[0].offset; 481 482 // Determine the size of the actual data. 483 outResult.sizeBody = levelInfo[MAX_LEVEL - 2].size; 484 485 // Determine the total size. 486 outResult.sizeTotal = levelInfo[MAX_LEVEL - 2].offset 487 + levelInfo[MAX_LEVEL - 2].size 488 - levelInfo[0].offset; 489}; 490 491/*===========================================================================*/ 492/*! 493@brief Duplicated + integrity verification file. 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 // Calculate the size of the file for verifying completeness of the hierarchy. 534 var resultIntegrity = new HierarchicalIntegrityVerificationFile.QuerySizeResult(); 535 536 HierarchicalIntegrityVerificationFile.QuerySize( 537 resultIntegrity, 538 inputParamIntegrity, 539 sizeOriginalData 540 ); 541 542 var sizeDuplicateOriginal = 0; 543 544 if (!isDuplicateOnlyHash) 545 { 546 sizeDuplicateOriginal = AdjustAlignment(resultIntegrity.sizeTotal, inputParamDuplex.sizeBlockLevel[1]); 547 } 548 else 549 { 550 sizeDuplicateOriginal = AdjustAlignment(resultIntegrity.sizeLayeredHash, inputParamDuplex.sizeBlockLevel[1]); 551 } 552 553 // Calculate the size of the file for hierarchy duplexing. 554 var resultDuplex = new HierarchicalDuplexFile.QuerySizeResult(); 555 556 HierarchicalDuplexFile.QuerySize( 557 resultDuplex, 558 inputParamDuplex, 559 sizeDuplicateOriginal 560 ); 561 562 // Calculate the size of the file for verification of duplexing and completeness. 563 var offsetOriginalData; 564 if (!isDuplicateOnlyHash) 565 { 566 offsetOriginalData = 0; 567 outResult.sizeTotalBody = resultDuplex.sizeBody; 568 } 569 else 570 { 571 // Place the actual data after the duplicated signature data. 572 offsetOriginalData = AdjustAlignment(resultDuplex.sizeBody, inputParamIntegrity.sizeBlockLevel[3]); 573 outResult.sizeTotalBody = offsetOriginalData + sizeOriginalData; 574 } 575 576 // Metadata 577 var offset = DuplicatedIntegrityFile.META_INFO_SIZE; 578 579 // Management region for the integrity verification layer. 580 offset = AdjustAlignment(offset, 4); 581 offset += resultIntegrity.sizeControlArea; 582 583 // Region for managing the duplexing 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 // Final size of the management region. 592 outResult.sizeControlArea = offset; 593}; 594 595/*===========================================================================*/ 596/*! 597@brief Duplicated + integrity verification filter file. 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 // The filter file does not use the option region. 626 inputParamDuplex.sizeOptionalInfo = 0; 627 628 // Get the size of the management region for duplicated files with integrity verification. 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 // Duplicated management region A. 641 offset = AdjustAlignment(offset, 8/*sizeof(s64)*/); 642 offset += resultDupInt.sizeControlArea; 643 644 // Duplicated management region B 645 offset = AdjustAlignment(offset, 8/*sizeof(s64)*/); 646 offset += resultDupInt.sizeControlArea; 647 648 var maxBlockSize = ((inputParamDuplex.sizeBlockLevel[0] - 1) | 649 (inputParamDuplex.sizeBlockLevel[1] - 1) | 650 (inputParamIntegrity.sizeBlockLevel[0] - 1) | 651 (inputParamIntegrity.sizeBlockLevel[1] - 1) | 652 (inputParamIntegrity.sizeBlockLevel[2] - 1) | 653 (inputParamIntegrity.sizeBlockLevel[3] - 1)) + 1; 654 655 // Insert padding and place the data portion after the management region for duplicated files by using integrity verification. 656 // 657 offset = AdjustAlignment(offset, maxBlockSize); 658 offset += resultDupInt.sizeTotalBody;; 659 660 // Return the final size. 661 outResult.sizeTotalBody = offset; 662}; 663 664/*===========================================================================*/ 665/*! 666@brief The archive class for transparently handling an archive with a file ID as a path using a string path. 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 // When there are extremely few entries, return a fixed size for the number of buckets. 747 return 3; 748 } 749 if (countEntries <= 19) 750 { 751 // If there are fewer than 20 entries, an odd number is returned. 752 return countEntries | 1; 753 } 754 755 // If there are more than 20 entries, redistribute entries in buckets based on dividing the total number of candidates by prime numbers up to 17 and using the resulting modulus. 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 Save data archive class. 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: Consider standardizing to WStringOnBit64PathStorageArchive.QueryOptimalBucketCount. 874 if (countEntries <= 3) 875 { 876 // When there are extremely few entries, return a fixed size for the number of buckets. 877 return 3; 878 } 879 if (countEntries <= 19) 880 { 881 // If there are fewer than 20 entries, an odd number is returned 882 return countEntries | 1; 883 } 884 885 // If there are more than 20 entries, redistribute entries in buckets based on dividing the total number of candidates by prime numbers up to 17 and using the resulting modulus. 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/* 923var sizeDirectoryEntry = SaveDataArchive.QueryDirectoryEntryStorageSize(countDirectoryEntry); 924var blockCountDirectoryEntry = Math.floor((sizeDirectoryEntry + sizeBlock - 1) / sizeBlock); 925var sizeFileEntry = SaveDataArchive.QueryFileEntryStorageSize(countFileEntry); 926var blockCountFileEntry = Math.floor((sizeFileEntry + sizeBlock - 1) / sizeBlock); 927return 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 Duplicated + integrity verification save data archive class. 982@ref DuplicatedIntegritySaveDataArchive. 983*/ 984function DuplicatedIntegritySaveDataArchive() 985{ 986}; 987 988/*---------------------------------------------------------------------------*/ 989 990DuplicatedIntegritySaveDataArchive.DUPLICATE_FULL = 0x0001; //!< Full duplexing. 991DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META = 0x0002; //!< Half duplexing. 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 management region for duplicated files with integrity verification. 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 for the 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 for the replicated placement. 1043 var offsetControlAreaA = DuplicatedIntegritySaveDataArchive.ATOMIC_HEADER_SIZE; 1044 var sizeControlAreaAB = resultDupInt.sizeControlArea; 1045 1046 var offset = offsetControlAreaA; 1047 offset += sizeControlAreaAB; 1048 offset = AdjustAlignment(offset, 8/*sizeof(s64)*/); 1049 offset += sizeControlAreaAB; 1050 1051 // Place the data portion after the management region for the duplexing file that has verification features. 1052 // Insert padding to match the maximum block size. 1053 offset = AdjustAlignment(offset, sizeBlock); 1054 offset += resultDupInt.sizeTotalBody; 1055 1056 // Return total data size. 1057 return offset; 1058}; 1059 1060/*---------------------------------------------------------------------------*/ 1061 1062DuplicatedIntegritySaveDataArchive.QuerySizeAsMetaDuplication = function( 1063 inputParamDuplex, 1064 inputParamIntegrity, 1065 sizeArchiveBlock, 1066 countDirectoryEntry, 1067 countFileEntry, 1068 countDirectoryEntryBucket, 1069 countFileEntryBucket, 1070 countDataBlock 1071 ) 1072{ 1073 // Request the size of the actual data. 1074 var sizeData = SaveDataArchive.QueryMinDataSize( 1075 countDirectoryEntry, 1076 countFileEntry, 1077 countDirectoryEntryBucket, 1078 countFileEntryBucket, 1079 sizeArchiveBlock, 1080 countDataBlock 1081 ); 1082 1083 // Request the size of the metadata. 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 management region for the duplexing file, by using the verification feature for the metadata. 1094 var resultDupInt1 = new DuplicatedIntegrityFile.QuerySizeResult(); 1095 DuplicatedIntegrityFile.QuerySize( 1096 resultDupInt1, 1097 inputParamDuplex, 1098 inputParamIntegrity, 1099 sizeMeta, 1100 false 1101 ); 1102 1103 // Get the size of the management region for the duplexing file, by using the verification feature for the actual data region. 1104 // Duplex only the signature in the actual data region. 1105 var resultDupInt2 = new DuplicatedIntegrityFile.QuerySizeResult(); 1106 // Copy the completeness verification parameter. (Additional information size is fixed to 0.) 1107 var inputParamIntegrity2 = new HierarchicalIntegrityVerificationFile.InputParam(); 1108 inputParamIntegrity2 = inputParamIntegrity; 1109 inputParamIntegrity2.sizeOptionalInfo = 0; 1110 1111 DuplicatedIntegrityFile.QuerySize( 1112 resultDupInt2, 1113 inputParamDuplex, 1114 inputParamIntegrity2, 1115 sizeData, 1116 true 1117 ); 1118 1119 // Calculate the maximum value for the 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 for the replicated placement. 1128 var offsetDuplicatedIntegrityControlArea1 = DuplicatedIntegritySaveDataArchive.ATOMIC_HEADER_SIZE; 1129 var offsetDuplicatedIntegrityControlArea2 = 1130 AdjustAlignment(resultDupInt1.sizeControlArea, 8/*sizeof(s64)*/); 1131 var sizeControlAreaAB = 1132 offsetDuplicatedIntegrityControlArea2 + 1133 AdjustAlignment(resultDupInt2.sizeControlArea, 8/*sizeof(s64)*/); 1134 1135 var offset = offsetDuplicatedIntegrityControlArea1; 1136 offset += sizeControlAreaAB; 1137 offset += sizeControlAreaAB; 1138 1139 // Place the metadata region after the management region (actual data) for the duplexing file that has verification features. 1140 // Insert padding to match the maximum block size. 1141 offset = AdjustAlignment(offset, sizeBlock); 1142 offset += resultDupInt1.sizeTotalBody; 1143 1144 // Place the actual data region after the metadata region. 1145 offset = AdjustAlignment(offset, sizeBlock); 1146 offset += resultDupInt2.sizeTotalBody; 1147 1148 // Return 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/*! 1204@brief Extended save data filter archive class. 1205@ref ExtSaveDataFilterArchive. 1206*/ 1207function ExtSaveDataFilterArchive() 1208{ 1209}; 1210 1211/*---------------------------------------------------------------------------*/ 1212 1213ExtSaveDataFilterArchive.QueryFilteredFileSizeInternal = function( 1214 sizeOriginalData, 1215 isMetaFile 1216 ) 1217{ 1218 // Duplexing parameter. 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 // Request the size of the filter file for duplexing and completeness verification. 1234 var resultDupIntFilter = new DuplicatedIntegrityFilterFile.QuerySizeResult(); 1235 1236 DuplicatedIntegrityFilterFile.QuerySize( 1237 resultDupIntFilter, 1238 inputParamDuplex, 1239 inputParamIntegrity, 1240 sizeOriginalData, 1241 !isMetaFile // Duplicate all of the metafiles only 1242 ); 1243 1244 return resultDupIntFilter.sizeTotalBody; 1245}; 1246 1247/*---------------------------------------------------------------------------*/ 1248 1249ExtSaveDataFilterArchive.QueryFilteredMetaFileSize = function( 1250 sizeMetaData 1251 ) 1252{ 1253 return ExtSaveDataFilterArchive.QueryFilteredFileSizeInternal( 1254 sizeMetaData, 1255 true 1256 ); 1257}; 1258 1259/*---------------------------------------------------------------------------*/ 1260 1261ExtSaveDataFilterArchive.QueryFilteredFileSize = function( 1262 sizeOriginalData 1263 ) 1264{ 1265 return ExtSaveDataFilterArchive.QueryFilteredFileSizeInternal( 1266 sizeOriginalData, 1267 false 1268 ); 1269}; 1270 1271/*===========================================================================*/ 1272/*! 1273@brief Extended save data storage archive class. 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 // Divide into two because ".", "..". 1292 return Math.floor(sizeArchiveBlock / sizeEntry) - 2; 1293} 1294 1295/*===========================================================================*/ 1296/*! 1297@brief Extended save data management class. 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; // Added directories for user and boss. 1335 countFileEntry += 1; // Add icon file TO DO:. Remove for shared and expanded save data? 1336 var sizeMetaData = ExtSaveDataManager.QueryMetaDataSize( 1337 sizeArchiveBlock, 1338 countDirectoryEntry, 1339 countFileEntry 1340 ); 1341 var resultDupIntFilter = new DuplicatedIntegrityFilterFile.QuerySizeResult(); 1342 var sizeFilteredMetaData = ExtSaveDataFilterArchive.QueryFilteredMetaFileSize(sizeMetaData); 1343 var filteredMetaDataBlocks = Math.floor((sizeFilteredMetaData + sizeArchiveBlock - 1) / sizeArchiveBlock); 1344 1345 var fileBlocks = 0; 1346 for (var i = 0; i < fileSizeArrayLength; i++) 1347 { 1348 var sizeFilteredFile = ExtSaveDataFilterArchive.QueryFilteredFileSize(fileSizeArray[i]); 1349 fileBlocks += Math.floor((sizeFilteredFile + sizeArchiveBlock - 1) / sizeArchiveBlock); 1350 } 1351 1352 // Number of files that can fit in a single directory. 1353 var countMaxEntryPerDirectory = ExtSaveDataStorageArchive.QueryMaxDirectoryEntryCount( 1354 sizeArchiveBlock, 1355 ExtSaveDataStorageArchive.ENTRY_SIZE 1356 ); 1357 1358 // Number of directories that are created when the maximum number of files (equal to countFileEntry) is created. 1359 var countMaxDirEntries = Math.floor((countFileEntry + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory); 1360 if (countMaxDirEntries == 0) 1361 { 1362 countMaxDirEntries = 1; 1363 } 1364 // Number of blocks required by root. 1365 var rootEntryBlocks = Math.floor((countMaxDirEntries + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory); 1366 1367 // Number of directories that store files, including metafiles and icon files. 1368 var countDirEntries = Math.floor(((fileSizeArrayLength + 2) + countMaxEntryPerDirectory - 1) / countMaxEntryPerDirectory); 1369 1370 var iconBlocks = 0; 1371 { 1372 var sizeFilterdIcon = ExtSaveDataFilterArchive.QueryFilteredFileSize(sizeIcon); 1373 iconBlocks = Math.floor((sizeFilterdIcon + sizeArchiveBlock - 1) / sizeArchiveBlock); 1374 } 1375 1376 return (filteredMetaDataBlocks 1377 + fileBlocks 1378 + countDirEntries 1379 + rootEntryBlocks 1380 + iconBlocks) * sizeArchiveBlock; 1381}; 1382 1383/*===========================================================================*/ 1384function ClearDebugPrint() 1385{ 1386 if (!document.FsDebug) return; 1387 1388 var out = document.FsDebug.output; 1389 if (!out) return; 1390 out.value = ""; 1391}; 1392 1393/*---------------------------------------------------------------------------*/ 1394 1395function DebugPrint(str) 1396{ 1397 if (!document.FsDebug) return; 1398 1399 var out = document.FsDebug.output; 1400 if (!out) return; 1401 out.value += str + "\n"; 1402}; 1403 1404/*===========================================================================*/ 1405 1406CalcFsSpaceResult = function() 1407{ 1408 this.sizeTotal = 0; 1409 this.countDataBlock = 0; 1410}; 1411 1412/*---------------------------------------------------------------------------*/ 1413 1414function calcFsSpaceInternal( 1415 outResult, 1416 countDirectoryEntry, 1417 countFileEntry, 1418 sizeArchiveBlock, 1419 sizeCapacity, 1420 option 1421 ) 1422{ 1423 // Number of directory or file buckets. 1424 var countDirectoryEntryBucket = DuplicatedIntegritySaveDataArchive 1425 .QueryOptimalBucketCount(countDirectoryEntry); 1426 var countFileEntryBucket = DuplicatedIntegritySaveDataArchive 1427 .QueryOptimalBucketCount(countFileEntry); 1428 1429 // Duplexing parameter. 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 // Change according to the duplexing type and block size. 1439 switch (sizeArchiveBlock) 1440 { 1441 case 512: 1442 if (option & DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META) 1443 { 1444 inputParamIntegrity.sizeBlockLevel[0] = 512; 1445 inputParamIntegrity.sizeBlockLevel[1] = 512; 1446 inputParamIntegrity.sizeBlockLevel[2] = 512 * 8; 1447 inputParamIntegrity.sizeBlockLevel[3] = 512; 1448 inputParamIntegrity.sizeOptionalInfo = 0; 1449 } 1450 else if (option & DuplicatedIntegritySaveDataArchive.DUPLICATE_FULL) 1451 { 1452 inputParamIntegrity.sizeBlockLevel[0] = 512; 1453 inputParamIntegrity.sizeBlockLevel[1] = 512; 1454 inputParamIntegrity.sizeBlockLevel[2] = 512 * 8; 1455 inputParamIntegrity.sizeBlockLevel[3] = 512 * 8; 1456 inputParamIntegrity.sizeOptionalInfo = 0; 1457 } 1458 else 1459 { 1460 throw new ResultInvalidArgument(); 1461 } 1462 break; 1463 case 4096: 1464 { 1465 inputParamIntegrity.sizeBlockLevel[0] = 512; 1466 inputParamIntegrity.sizeBlockLevel[1] = 512; 1467 inputParamIntegrity.sizeBlockLevel[2] = 512 * 8; 1468 inputParamIntegrity.sizeBlockLevel[3] = 512 * 8; 1469 inputParamIntegrity.sizeOptionalInfo = 0; 1470 } 1471 break; 1472 default: 1473 throw new ResultInvalidArgument(); 1474 break; 1475 } 1476 1477 // Total size. 1478 var sizeTotal = 0; 1479 1480 // Adjust the total number of blocks to match the capacity. 1481 var countDataBlockMin = 0; 1482 var countDataBlockMax = Math.floor(sizeCapacity / sizeArchiveBlock); 1483 var retryCount = 32; 1484 1485 while (1 <= (countDataBlockMax - countDataBlockMin) && (0 < retryCount--)) 1486 { 1487 var countDataBlock = Math.floor((countDataBlockMin + countDataBlockMax) / 2); 1488 1489 sizeTotal = DuplicatedIntegritySaveDataArchive.QuerySize( 1490 inputParamDuplex, 1491 inputParamIntegrity, 1492 sizeArchiveBlock, 1493 countDirectoryEntry, 1494 countFileEntry, 1495 countDirectoryEntryBucket, 1496 countFileEntryBucket, 1497 countDataBlock, 1498 option 1499 ); 1500 1501 DebugPrint("sizeCapacity = " + sizeCapacity 1502 + ", sizeTotal = " + sizeTotal 1503 + ", countDataBlockMax = " + countDataBlockMax 1504 + ", countDataBlockMin = " + countDataBlockMin); 1505 1506 if (sizeTotal > sizeCapacity) 1507 { 1508 countDataBlockMax = countDataBlock; 1509 } 1510 else 1511 { 1512 countDataBlockMin = countDataBlock; 1513 } 1514 1515 // Determine whether to leave partway. (No changes occur even if you continue.) 1516 if ((countDataBlockMax - countDataBlockMin) <= 1 1517 && (countDataBlock == countDataBlockMin)) 1518 { 1519 break; 1520 } 1521 } 1522 1523 // Calculate the 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 1550var capacityUnit; 1551var type2BlockSize; 1552 1553function calcFsSpace() 1554{ 1555 // Clear debug output. 1556 ClearDebugPrint(); 1557 1558 if (!document.FsSpace) return; 1559 1560 var FsSpace = document.FsSpace; 1561 1562 if (FsSpace.fsblocksize[0].checked) 1563 { 1564 if (FsSpace.capacitytype[2].checked) 1565 { 1566 FsSpace.capacitytype[0].checked = true; 1567 } 1568 1569 FsSpace.capacitytype[0].disabled = false; 1570 FsSpace.capacitytype[0].readonly = true; 1571 1572 FsSpace.capacitytype[1].disabled = false; 1573 FsSpace.capacitytype[1].readonly = true; 1574 1575 FsSpace.capacitytype[2].disabled = true; 1576 FsSpace.capacitytype[2].readonly = false; 1577 } 1578 else 1579 { 1580 FsSpace.capacitytype[2].checked = true; 1581 1582 FsSpace.capacitytype[0].disabled = true; 1583 FsSpace.capacitytype[0].readonly = false; 1584 1585 FsSpace.capacitytype[1].disabled = true; 1586 FsSpace.capacitytype[1].readonly = false; 1587 1588 FsSpace.capacitytype[2].disabled = false; 1589 FsSpace.capacitytype[2].readonly = true; 1590 } 1591 1592 if (FsSpace.capacitytype[2].checked) 1593 { 1594 FsSpace.capacitytypeuser.disabled = false; 1595 FsSpace.capacitytypeuser.readonly = true; 1596 } 1597 else 1598 { 1599 FsSpace.capacitytypeuser.readonly = false; 1600 FsSpace.capacitytypeuser.disabled = true; 1601 } 1602 1603 // Full duplex or half duplex. 1604 var option = 0; 1605 if (FsSpace.fstype[0].checked) 1606 { 1607 option |= DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META; 1608 } 1609 else if (FsSpace.fstype[1].checked) 1610 { 1611 option |= DuplicatedIntegritySaveDataArchive.DUPLICATE_FULL; 1612 } 1613 1614 // Block size. 1615 var sizeArchiveBlock = 512; 1616 if (FsSpace.fsblocksize[1].checked) 1617 { 1618 sizeArchiveBlock = type2BlockSize; 1619 } 1620 1621 // Maximum number of directories or files. 1622 var countDirectoryEntry = parseInt(FsSpace.CountDirectoryEntry.value); 1623 var countFileEntry = parseInt(FsSpace.CountFileEntry.value); 1624 1625 // Entry correction. 1626 if (isNaN(countDirectoryEntry) || countDirectoryEntry < 0) 1627 { 1628 countDirectoryEntry = 0; 1629 } 1630 if (isNaN(countFileEntry) || countFileEntry < 1) 1631 { 1632 countFileEntry = 1; 1633 } 1634 1635 // Writes and returns the correction result. 1636 FsSpace.CountDirectoryEntry.value = countDirectoryEntry; 1637 FsSpace.CountFileEntry.value = countFileEntry; 1638 1639 // Total capacity. 1640 var sizeCapacity = parseInt(FsSpace.capacitytypeuser.value) * capacityUnit; 1641 1642 // Entry correction. 1643 if (isNaN(sizeCapacity) || sizeCapacity < 1 * capacityUnit) 1644 { 1645 sizeCapacity = 1 * capacityUnit; 1646 } else if (sizeCapacity > 2047 * capacityUnit && capacityUnit == 1024 * 1024) 1647 { 1648 // Upper-limit correction is performed only during card2. 1649 sizeCapacity = 2047 * capacityUnit; 1650 } 1651 1652 // Write and return the correction result. 1653 FsSpace.capacitytypeuser.value = Math.floor(sizeCapacity / capacityUnit); 1654 1655 if (FsSpace.capacitytype[0].checked) 1656 { 1657 sizeCapacity = 120 * 1024; 1658 } 1659 else if (FsSpace.capacitytype[1].checked) 1660 { 1661 sizeCapacity = 504 * 1024; 1662 } 1663 1664 // Calculate the size of the save data based on the obtained parameters. 1665 var result = new CalcFsSpaceResult(); 1666 1667 calcFsSpaceInternal( 1668 result, 1669 countDirectoryEntry, 1670 countFileEntry, 1671 sizeArchiveBlock, 1672 sizeCapacity, 1673 option 1674 ) 1675 1676 var sizeTotal = result.sizeTotal; 1677 var countDataBlock = result.countDataBlock; 1678 1679 FsSpace.SaveDataBlockSize.value = sizeArchiveBlock; 1680 FsSpace.SaveDataCapacities.value = countDataBlock * sizeArchiveBlock; 1681 FsSpace.SaveDataBlocks.value = countDataBlock; 1682 FsSpace.SaveDataCapacitiesKilloByte.value = Math.floor(countDataBlock * sizeArchiveBlock / 1024); 1683}; 1684 1685/*---------------------------------------------------------------------------*/ 1686 1687function calcFsSpaceExtMeta() 1688{ 1689 // Clear debug output. 1690 ClearDebugPrint(); 1691 1692 if (!document.FsSpaceExtEntry) return; 1693 1694 var FsSpace = document.FsSpaceExtEntry; 1695 1696 // Full duplex or half duplex. 1697 var option = DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META; 1698 1699 // Block size. 1700 var sizeArchiveBlock = 4096; 1701 1702 // Icon size. 1703 var sizeIcon = parseInt(FsSpace.IconSize.value); 1704 1705 // Entry correction. 1706 if (isNaN(sizeIcon) || sizeIcon <= 0) 1707 { 1708 sizeIcon = 1; 1709 } 1710 1711 // Write and return the correction result. 1712 FsSpace.IconSize.value = sizeIcon; 1713 1714 // Maximum number of directories or files. 1715 var countDirectoryEntry = parseInt(FsSpace.CountDirectoryEntry.value); 1716 var countFileEntry = parseInt(FsSpace.CountFileEntry.value); 1717 1718 // Entry correction. 1719 if (isNaN(countDirectoryEntry) || countDirectoryEntry < 0) 1720 { 1721 countDirectoryEntry = 0; 1722 } 1723 if (isNaN(countFileEntry) || countFileEntry < 1) 1724 { 1725 countFileEntry = 1; 1726 } 1727 1728 // Write and return the correction result. 1729 FsSpace.CountDirectoryEntry.value = countDirectoryEntry; 1730 FsSpace.CountFileEntry.value = countFileEntry; 1731 1732 // Empty the file list. 1733 var arrayFileSize = []; 1734 1735 // Calculate the size of the save data based on the obtained parameters. 1736 var sizeTotal = ExtSaveDataManager.QueryTotalQuotaSize( 1737 sizeArchiveBlock, 1738 countDirectoryEntry, 1739 countFileEntry, 1740 sizeIcon, 1741 arrayFileSize, 1742 0); 1743 var countDataBlock = Math.floor((sizeTotal + sizeArchiveBlock - 1) / sizeArchiveBlock); 1744 1745 FsSpace.ExtSaveDataBlocks.value = countDataBlock; 1746 FsSpace.ExtSaveDataCapacities.value = sizeTotal; 1747 FsSpace.ExtSaveDataCapacitiesKilloByte.value = Math.floor(sizeTotal / 1024); 1748}; 1749 1750/*---------------------------------------------------------------------------*/ 1751 1752function calcFsSpaceExtFile() 1753{ 1754 // Clear debug output. 1755 ClearDebugPrint(); 1756 1757 if (!document.FsSpaceExtFile) return; 1758 1759 var FsSpace = document.FsSpaceExtFile; 1760 1761 // Full duplex or half duplex. 1762 var option = DuplicatedIntegritySaveDataArchive.DUPLICATE_ONLY_META; 1763 1764 // Block size. 1765 var sizeArchiveBlock = 4096; 1766 1767 // Total capacity. 1768 var sizeCapacity = parseInt(FsSpace.capacitytypeuser.value) * 1024; 1769 1770 // Entry correction. 1771 if (isNaN(sizeCapacity) || sizeCapacity < 0) 1772 { 1773 sizeCapacity = 0; 1774 } 1775 1776 // Write and return the correction result. 1777 FsSpace.capacitytypeuser.value = Math.floor(sizeCapacity / 1024); 1778 1779 // Calculate the size of the save data based on the obtained parameters. 1780 var sizeTotal = ExtSaveDataFilterArchive.QueryFilteredFileSize(sizeCapacity); 1781 var countDataBlock = Math.floor((sizeTotal + sizeArchiveBlock - 1) / sizeArchiveBlock); 1782 1783 // Every 124 + 126 * n, a directory is created, so that the maximum number that can be consumed is displayed. 1784 countDataBlock += 1; 1785 sizeTotal = countDataBlock * sizeArchiveBlock; 1786 1787 FsSpace.ExtSaveDataBlocks.value = countDataBlock; 1788 FsSpace.ExtSaveDataCapacities.value = sizeTotal; 1789 FsSpace.ExtSaveDataCapacitiesKilloByte.value = Math.floor(sizeTotal / 1024); 1790}; 1791