WbfsBlob.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright 2012 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "DiscIO/WbfsBlob.h"
  4. #include <algorithm>
  5. #include <cstdio>
  6. #include <cstring>
  7. #include <memory>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include "Common/Align.h"
  12. #include "Common/Assert.h"
  13. #include "Common/CommonTypes.h"
  14. #include "Common/IOFile.h"
  15. #include "Common/Logging/Log.h"
  16. #include "Common/Swap.h"
  17. namespace DiscIO
  18. {
  19. static constexpr u64 WII_SECTOR_SIZE = 0x8000;
  20. static constexpr u64 WII_SECTOR_COUNT = 143432 * 2;
  21. static constexpr u64 WII_DISC_HEADER_SIZE = 256;
  22. WbfsFileReader::WbfsFileReader(File::IOFile file, const std::string& path)
  23. : m_size(0), m_good(false)
  24. {
  25. if (!AddFileToList(std::move(file)))
  26. return;
  27. if (!path.empty())
  28. OpenAdditionalFiles(path);
  29. if (!ReadHeader())
  30. return;
  31. m_good = true;
  32. // Grab disc info (assume slot 0, checked in ReadHeader())
  33. m_wlba_table.resize(m_blocks_per_disc);
  34. m_files[0].file.Seek(m_hd_sector_size + WII_DISC_HEADER_SIZE /*+ i * m_disc_info_size*/,
  35. File::SeekOrigin::Begin);
  36. m_files[0].file.ReadBytes(m_wlba_table.data(), m_blocks_per_disc * sizeof(u16));
  37. for (size_t i = 0; i < m_blocks_per_disc; i++)
  38. m_wlba_table[i] = Common::swap16(m_wlba_table[i]);
  39. }
  40. WbfsFileReader::~WbfsFileReader() = default;
  41. std::unique_ptr<BlobReader> WbfsFileReader::CopyReader() const
  42. {
  43. auto retval =
  44. std::unique_ptr<WbfsFileReader>(new WbfsFileReader(m_files[0].file.Duplicate("rb")));
  45. for (size_t ix = 1; ix < m_files.size(); ix++)
  46. retval->AddFileToList(m_files[ix].file.Duplicate("rb"));
  47. return retval;
  48. }
  49. u64 WbfsFileReader::GetDataSize() const
  50. {
  51. return WII_SECTOR_COUNT * WII_SECTOR_SIZE;
  52. }
  53. void WbfsFileReader::OpenAdditionalFiles(const std::string& path)
  54. {
  55. if (path.length() < 4)
  56. return;
  57. ASSERT(!m_files.empty()); // The code below gives .wbf0 for index 0, but it should be .wbfs
  58. while (true)
  59. {
  60. // Replace last character with index (e.g. wbfs = wbf1)
  61. if (m_files.size() >= 10)
  62. return;
  63. std::string current_path = path;
  64. current_path.back() = static_cast<char>('0' + m_files.size());
  65. if (!AddFileToList(File::IOFile(current_path, "rb")))
  66. return;
  67. }
  68. }
  69. bool WbfsFileReader::AddFileToList(File::IOFile file)
  70. {
  71. if (!file.IsOpen())
  72. return false;
  73. const u64 file_size = file.GetSize();
  74. m_files.emplace_back(std::move(file), m_size, file_size);
  75. m_size += file_size;
  76. return true;
  77. }
  78. bool WbfsFileReader::ReadHeader()
  79. {
  80. // Read hd size info
  81. m_files[0].file.Seek(0, File::SeekOrigin::Begin);
  82. m_files[0].file.ReadBytes(&m_header, sizeof(WbfsHeader));
  83. if (m_header.magic != WBFS_MAGIC)
  84. return false;
  85. m_header.hd_sector_count = Common::swap32(m_header.hd_sector_count);
  86. m_hd_sector_size = 1ull << m_header.hd_sector_shift;
  87. if (m_size != (m_header.hd_sector_count * m_hd_sector_size))
  88. return false;
  89. // Read wbfs cluster info
  90. m_wbfs_sector_size = 1ull << m_header.wbfs_sector_shift;
  91. m_wbfs_sector_count = m_size / m_wbfs_sector_size;
  92. if (m_wbfs_sector_size < WII_SECTOR_SIZE)
  93. return false;
  94. m_blocks_per_disc =
  95. (WII_SECTOR_COUNT * WII_SECTOR_SIZE + m_wbfs_sector_size - 1) / m_wbfs_sector_size;
  96. m_disc_info_size =
  97. Common::AlignUp(WII_DISC_HEADER_SIZE + m_blocks_per_disc * sizeof(u16), m_hd_sector_size);
  98. return m_header.disc_table[0] != 0;
  99. }
  100. bool WbfsFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
  101. {
  102. if (offset + nbytes > GetDataSize())
  103. return false;
  104. while (nbytes)
  105. {
  106. u64 read_size;
  107. File::IOFile& data_file = SeekToCluster(offset, &read_size);
  108. if (read_size == 0)
  109. return false;
  110. read_size = std::min(read_size, nbytes);
  111. if (!data_file.ReadBytes(out_ptr, read_size))
  112. {
  113. data_file.ClearError();
  114. return false;
  115. }
  116. out_ptr += read_size;
  117. nbytes -= read_size;
  118. offset += read_size;
  119. }
  120. return true;
  121. }
  122. File::IOFile& WbfsFileReader::SeekToCluster(u64 offset, u64* available)
  123. {
  124. u64 base_cluster = (offset >> m_header.wbfs_sector_shift);
  125. if (base_cluster < m_blocks_per_disc)
  126. {
  127. u64 cluster_address = m_wbfs_sector_size * m_wlba_table[base_cluster];
  128. u64 cluster_offset = offset & (m_wbfs_sector_size - 1);
  129. u64 final_address = cluster_address + cluster_offset;
  130. for (FileEntry& file_entry : m_files)
  131. {
  132. if (final_address < (file_entry.base_address + file_entry.size))
  133. {
  134. file_entry.file.Seek(final_address - file_entry.base_address, File::SeekOrigin::Begin);
  135. if (available)
  136. {
  137. u64 till_end_of_file = file_entry.size - (final_address - file_entry.base_address);
  138. u64 till_end_of_sector = m_wbfs_sector_size - cluster_offset;
  139. *available = std::min(till_end_of_file, till_end_of_sector);
  140. }
  141. return file_entry.file;
  142. }
  143. }
  144. }
  145. ERROR_LOG_FMT(DISCIO, "Read beyond end of disc");
  146. if (available)
  147. *available = 0;
  148. m_files[0].file.Seek(0, File::SeekOrigin::Begin);
  149. return m_files[0].file;
  150. }
  151. std::unique_ptr<WbfsFileReader> WbfsFileReader::Create(File::IOFile file, const std::string& path)
  152. {
  153. auto reader = std::unique_ptr<WbfsFileReader>(new WbfsFileReader(std::move(file), path));
  154. if (!reader->IsGood())
  155. reader.reset();
  156. return reader;
  157. }
  158. } // namespace DiscIO