u32 GetDOLSize(u64 _DOLOffset) { u32 offset = 0, size = 0, max = 0; // Iterate through the 7 code segments for (u8 i = 0; i < 7; i++) { ReadFromVolume(_DOLOffset + 0x00 + i * 4, 4, offset); ReadFromVolume(_DOLOffset + 0x90 + i * 4, 4, size); if (offset + size > max) max = offset + size; } // Iterate through the 11 data segments for (u8 i = 0; i < 11; i++) { ReadFromVolume(_DOLOffset + 0x1c + i * 4, 4, offset); ReadFromVolume(_DOLOffset + 0xac + i * 4, 4, size); if (offset + size > max) max = offset + size; } return max; }
// Operations dealing with encrypted space are done here - the volume is swapped to allow this bool ParsePartitionData(SPartition& _rPartition) { bool ParsedOK = true; // Switch out the main volume temporarily IVolume *OldVolume = m_Disc; // Ready some stuff m_Disc = CreateVolumeFromFilename(m_Filename, _rPartition.GroupNumber, _rPartition.Number); IFileSystem *FileSystem = CreateFileSystem(m_Disc); if (!FileSystem) { ERROR_LOG(DISCIO, "Failed to create filesystem for group %d partition %u", _rPartition.GroupNumber, _rPartition.Number); ParsedOK = false; } else { std::vector<const SFileInfo *> Files; size_t numFiles = FileSystem->GetFileList(Files); // Mark things as used which are not in the filesystem // Header, Header Information, Apploader ReadFromVolume(0x2440 + 0x14, 4, _rPartition.Header.ApploaderSize); ReadFromVolume(0x2440 + 0x18, 4, _rPartition.Header.ApploaderTrailerSize); MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset , 0 , 0x2440 + _rPartition.Header.ApploaderSize + _rPartition.Header.ApploaderTrailerSize); // DOL ReadFromVolume(0x420, 4, _rPartition.Header.DOLOffset); _rPartition.Header.DOLSize = GetDOLSize(_rPartition.Header.DOLOffset); MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset , _rPartition.Header.DOLOffset , _rPartition.Header.DOLSize); // FST ReadFromVolume(0x424, 4, _rPartition.Header.FSTOffset); ReadFromVolume(0x428, 4, _rPartition.Header.FSTSize); MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset , _rPartition.Header.FSTOffset , _rPartition.Header.FSTSize); // Go through the filesystem and mark entries as used for (size_t currentFile = 0; currentFile < numFiles; currentFile++) { DEBUG_LOG(DISCIO, "%s", currentFile ? (*Files.at(currentFile)).m_FullPath : "/"); // Just 1byte for directory? - it will end up reserving a cluster this way if ((*Files.at(currentFile)).m_NameOffset & 0x1000000) MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset , (*Files.at(currentFile)).m_Offset, 1); else MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset , (*Files.at(currentFile)).m_Offset, (*Files.at(currentFile)).m_FileSize); } } delete FileSystem; // Swap back delete m_Disc; m_Disc = OldVolume; return ParsedOK; }
bool ParseDisc() { // Mark the header as used - it's mostly 0s anyways MarkAsUsed(0, 0x50000); for (int x = 0; x < 4; x++) { if (!ReadFromVolume(0x40000 + (x * 8) + 0, PartitionGroup[x].numPartitions, false) || !ReadFromVolume(0x40000 + (x * 8) + 4, PartitionGroup[x].PartitionsOffset, false)) return false; // Read all partitions for (u32 i = 0; i < PartitionGroup[x].numPartitions; i++) { SPartition Partition; Partition.GroupNumber = x; Partition.Number = i; if (!ReadFromVolume(PartitionGroup[x].PartitionsOffset + (i * 8) + 0, Partition.Offset, false) || !ReadFromVolume(PartitionGroup[x].PartitionsOffset + (i * 8) + 4, Partition.Type, false) || !ReadFromVolume(Partition.Offset + 0x2a4, Partition.Header.TMDSize, false) || !ReadFromVolume(Partition.Offset + 0x2a8, Partition.Header.TMDOffset, false) || !ReadFromVolume(Partition.Offset + 0x2ac, Partition.Header.CertChainSize, false) || !ReadFromVolume(Partition.Offset + 0x2b0, Partition.Header.CertChainOffset, false) || !ReadFromVolume(Partition.Offset + 0x2b4, Partition.Header.H3Offset, false) || !ReadFromVolume(Partition.Offset + 0x2b8, Partition.Header.DataOffset, false) || !ReadFromVolume(Partition.Offset + 0x2bc, Partition.Header.DataSize, false)) return false; PartitionGroup[x].PartitionsVec.push_back(Partition); } for (auto& rPartition : PartitionGroup[x].PartitionsVec) { const SPartitionHeader& rHeader = rPartition.Header; MarkAsUsed(rPartition.Offset, 0x2c0); MarkAsUsed(rPartition.Offset + rHeader.TMDOffset, rHeader.TMDSize); MarkAsUsed(rPartition.Offset + rHeader.CertChainOffset, rHeader.CertChainSize); MarkAsUsed(rPartition.Offset + rHeader.H3Offset, 0x18000); // This would mark the whole (encrypted) data area // we need to parse FST and other crap to find what's free within it! // MarkAsUsed(rPartition.Offset + rHeader.DataOffset, rHeader.DataSize); // Parse Data! This is where the big gain is if (!ParsePartitionData(rPartition)) return false; } } return true; }
// Operations dealing with encrypted space are done here - the volume is swapped to allow this bool ParsePartitionData(SPartition& partition) { bool parsed_ok = true; // Switch out the main volume temporarily std::unique_ptr<IVolume> old_volume; s_disc.swap(old_volume); // Ready some stuff s_disc = CreateVolumeFromFilename(m_Filename, partition.GroupNumber, partition.Number); if (s_disc == nullptr) { ERROR_LOG(DISCIO, "Failed to create volume from file %s", m_Filename.c_str()); s_disc.swap(old_volume); return false; } std::unique_ptr<IFileSystem> filesystem(CreateFileSystem(s_disc.get())); if (!filesystem) { ERROR_LOG(DISCIO, "Failed to create filesystem for group %d partition %u", partition.GroupNumber, partition.Number); parsed_ok = false; } else { // Mark things as used which are not in the filesystem // Header, Header Information, Apploader parsed_ok = parsed_ok && ReadFromVolume(0x2440 + 0x14, partition.Header.ApploaderSize, true); parsed_ok = parsed_ok && ReadFromVolume(0x2440 + 0x18, partition.Header.ApploaderTrailerSize, true); MarkAsUsedE(partition.Offset + partition.Header.DataOffset, 0, 0x2440 + partition.Header.ApploaderSize + partition.Header.ApploaderTrailerSize); // DOL partition.Header.DOLOffset = filesystem->GetBootDOLOffset(); partition.Header.DOLSize = filesystem->GetBootDOLSize(partition.Header.DOLOffset); parsed_ok = parsed_ok && partition.Header.DOLOffset && partition.Header.DOLSize; MarkAsUsedE(partition.Offset + partition.Header.DataOffset, partition.Header.DOLOffset, partition.Header.DOLSize); // FST parsed_ok = parsed_ok && ReadFromVolume(0x424, partition.Header.FSTOffset, true); parsed_ok = parsed_ok && ReadFromVolume(0x428, partition.Header.FSTSize, true); MarkAsUsedE(partition.Offset + partition.Header.DataOffset, partition.Header.FSTOffset, partition.Header.FSTSize); // Go through the filesystem and mark entries as used for (SFileInfo file : filesystem->GetFileList()) { DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str()); if ((file.m_NameOffset & 0x1000000) == 0) MarkAsUsedE(partition.Offset + partition.Header.DataOffset, file.m_Offset, file.m_FileSize); } } // Swap back s_disc.swap(old_volume); return parsed_ok; }
// Operations dealing with encrypted space are done here - the volume is swapped to allow this bool ParsePartitionData(SPartition& _rPartition) { bool ParsedOK = true; // Switch out the main volume temporarily IVolume *OldVolume = m_Disc; // Ready some stuff m_Disc = CreateVolumeFromFilename(m_Filename, _rPartition.GroupNumber, _rPartition.Number); if (m_Disc == nullptr) { ERROR_LOG(DISCIO, "Failed to create volume from file %s", m_Filename.c_str()); m_Disc = OldVolume; return false; } std::unique_ptr<IFileSystem> filesystem(CreateFileSystem(m_Disc)); if (!filesystem) { ERROR_LOG(DISCIO, "Failed to create filesystem for group %d partition %u", _rPartition.GroupNumber, _rPartition.Number); ParsedOK = false; } else { // Mark things as used which are not in the filesystem // Header, Header Information, Apploader ReadFromVolume(0x2440 + 0x14, 4, _rPartition.Header.ApploaderSize, true); ReadFromVolume(0x2440 + 0x18, 4, _rPartition.Header.ApploaderTrailerSize, true); MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset , 0 , 0x2440 + _rPartition.Header.ApploaderSize + _rPartition.Header.ApploaderTrailerSize); // DOL ReadFromVolume(0x420, 4, _rPartition.Header.DOLOffset, true); _rPartition.Header.DOLSize = GetDOLSize(_rPartition.Header.DOLOffset); MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset , _rPartition.Header.DOLOffset , _rPartition.Header.DOLSize); // FST ReadFromVolume(0x424, 4, _rPartition.Header.FSTOffset, true); ReadFromVolume(0x428, 4, _rPartition.Header.FSTSize, true); MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset , _rPartition.Header.FSTOffset , _rPartition.Header.FSTSize); // Go through the filesystem and mark entries as used for (SFileInfo file : filesystem->GetFileList()) { DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str()); // Just 1byte for directory? - it will end up reserving a cluster this way if (file.m_NameOffset & 0x1000000) MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset, file.m_Offset, 1); else MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset, file.m_Offset, file.m_FileSize); } } // Swap back delete m_Disc; m_Disc = OldVolume; return ParsedOK; }