WbfsBlob.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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 const u64 WII_SECTOR_SIZE = 0x8000;
  20. static const u64 WII_SECTOR_COUNT = 143432 * 2;
  21. static const 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()
  41. {
  42. }
  43. std::unique_ptr<BlobReader> WbfsFileReader::CopyReader() const
  44. {
  45. auto retval =
  46. std::unique_ptr<WbfsFileReader>(new WbfsFileReader(m_files[0].file.Duplicate("rb")));
  47. for (size_t ix = 1; ix < m_files.size(); ix++)
  48. retval->AddFileToList(m_files[ix].file.Duplicate("rb"));
  49. return retval;
  50. }
  51. u64 WbfsFileReader::GetDataSize() const
  52. {
  53. return WII_SECTOR_COUNT * WII_SECTOR_SIZE;
  54. }
  55. void WbfsFileReader::OpenAdditionalFiles(const std::string& path)
  56. {
  57. if (path.length() < 4)
  58. return;
  59. ASSERT(!m_files.empty()); // The code below gives .wbf0 for index 0, but it should be .wbfs
  60. while (true)
  61. {
  62. // Replace last character with index (e.g. wbfs = wbf1)
  63. if (m_files.size() >= 10)
  64. return;
  65. std::string current_path = path;
  66. current_path.back() = static_cast<char>('0' + m_files.size());
  67. if (!AddFileToList(File::IOFile(current_path, "rb")))
  68. return;
  69. }
  70. }
  71. bool WbfsFileReader::AddFileToList(File::IOFile file)
  72. {
  73. if (!file.IsOpen())
  74. return false;
  75. const u64 file_size = file.GetSize();
  76. m_files.emplace_back(std::move(file), m_size, file_size);
  77. m_size += file_size;
  78. return true;
  79. }
  80. bool WbfsFileReader::ReadHeader()
  81. {
  82. // Read hd size info
  83. m_files[0].file.Seek(0, File::SeekOrigin::Begin);
  84. m_files[0].file.ReadBytes(&m_header, sizeof(WbfsHeader));
  85. if (m_header.magic != WBFS_MAGIC)
  86. return false;
  87. m_header.hd_sector_count = Common::swap32(m_header.hd_sector_count);
  88. m_hd_sector_size = 1ull << m_header.hd_sector_shift;
  89. if (m_size != (m_header.hd_sector_count * m_hd_sector_size))
  90. return false;
  91. // Read wbfs cluster info
  92. m_wbfs_sector_size = 1ull << m_header.wbfs_sector_shift;
  93. m_wbfs_sector_count = m_size / m_wbfs_sector_size;
  94. if (m_wbfs_sector_size < WII_SECTOR_SIZE)
  95. return false;
  96. m_blocks_per_disc =
  97. (WII_SECTOR_COUNT * WII_SECTOR_SIZE + m_wbfs_sector_size - 1) / m_wbfs_sector_size;
  98. m_disc_info_size =
  99. Common::AlignUp(WII_DISC_HEADER_SIZE + m_blocks_per_disc * sizeof(u16), m_hd_sector_size);
  100. return m_header.disc_table[0] != 0;
  101. }
  102. bool WbfsFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
  103. {
  104. if (offset + nbytes > GetDataSize())
  105. return false;
  106. while (nbytes)
  107. {
  108. u64 read_size;
  109. File::IOFile& data_file = SeekToCluster(offset, &read_size);
  110. if (read_size == 0)
  111. return false;
  112. read_size = std::min(read_size, nbytes);
  113. if (!data_file.ReadBytes(out_ptr, read_size))
  114. {
  115. data_file.ClearError();
  116. return false;
  117. }
  118. out_ptr += read_size;
  119. nbytes -= read_size;
  120. offset += read_size;
  121. }
  122. return true;
  123. }
  124. File::IOFile& WbfsFileReader::SeekToCluster(u64 offset, u64* available)
  125. {
  126. u64 base_cluster = (offset >> m_header.wbfs_sector_shift);
  127. if (base_cluster < m_blocks_per_disc)
  128. {
  129. u64 cluster_address = m_wbfs_sector_size * m_wlba_table[base_cluster];
  130. u64 cluster_offset = offset & (m_wbfs_sector_size - 1);
  131. u64 final_address = cluster_address + cluster_offset;
  132. for (FileEntry& file_entry : m_files)
  133. {
  134. if (final_address < (file_entry.base_address + file_entry.size))
  135. {
  136. file_entry.file.Seek(final_address - file_entry.base_address, File::SeekOrigin::Begin);
  137. if (available)
  138. {
  139. u64 till_end_of_file = file_entry.size - (final_address - file_entry.base_address);
  140. u64 till_end_of_sector = m_wbfs_sector_size - cluster_offset;
  141. *available = std::min(till_end_of_file, till_end_of_sector);
  142. }
  143. return file_entry.file;
  144. }
  145. }
  146. }
  147. ERROR_LOG_FMT(DISCIO, "Read beyond end of disc");
  148. if (available)
  149. *available = 0;
  150. m_files[0].file.Seek(0, File::SeekOrigin::Begin);
  151. return m_files[0].file;
  152. }
  153. std::unique_ptr<WbfsFileReader> WbfsFileReader::Create(File::IOFile file, const std::string& path)
  154. {
  155. auto reader = std::unique_ptr<WbfsFileReader>(new WbfsFileReader(std::move(file), path));
  156. if (!reader->IsGood())
  157. reader.reset();
  158. return reader;
  159. }
  160. } // namespace DiscIO