DiscScrubber.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. // Copyright 2009 Dolphin Emulator Project
  2. // Licensed under GPLv2+
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <cinttypes>
  6. #include <cstddef>
  7. #include <cstdio>
  8. #include <memory>
  9. #include <string>
  10. #include <vector>
  11. #include "Common/CommonTypes.h"
  12. #include "Common/FileUtil.h"
  13. #include "DiscIO/DiscScrubber.h"
  14. #include "DiscIO/Filesystem.h"
  15. #include "DiscIO/Volume.h"
  16. #include "DiscIO/VolumeCreator.h"
  17. namespace DiscIO
  18. {
  19. namespace DiscScrubber
  20. {
  21. #define CLUSTER_SIZE 0x8000
  22. static u8* m_FreeTable = nullptr;
  23. static u64 m_FileSize;
  24. static u64 m_BlockCount;
  25. static u32 m_BlockSize;
  26. static int m_BlocksPerCluster;
  27. static bool m_isScrubbing = false;
  28. static std::string m_Filename;
  29. static IVolume* m_Disc = nullptr;
  30. struct SPartitionHeader
  31. {
  32. u8* Ticket[0x2a4];
  33. u32 TMDSize;
  34. u64 TMDOffset;
  35. u32 CertChainSize;
  36. u64 CertChainOffset;
  37. // H3Size is always 0x18000
  38. u64 H3Offset;
  39. u64 DataOffset;
  40. u64 DataSize;
  41. // TMD would be here
  42. u64 DOLOffset;
  43. u64 DOLSize;
  44. u64 FSTOffset;
  45. u64 FSTSize;
  46. u32 ApploaderSize;
  47. u32 ApploaderTrailerSize;
  48. };
  49. struct SPartition
  50. {
  51. u32 GroupNumber;
  52. u32 Number;
  53. u64 Offset;
  54. u32 Type;
  55. SPartitionHeader Header;
  56. };
  57. struct SPartitionGroup
  58. {
  59. u32 numPartitions;
  60. u64 PartitionsOffset;
  61. std::vector<SPartition> PartitionsVec;
  62. };
  63. static SPartitionGroup PartitionGroup[4];
  64. void MarkAsUsed(u64 _Offset, u64 _Size);
  65. void MarkAsUsedE(u64 _PartitionDataOffset, u64 _Offset, u64 _Size);
  66. void ReadFromVolume(u64 _Offset, u64 _Length, u32& _Buffer, bool _Decrypt);
  67. void ReadFromVolume(u64 _Offset, u64 _Length, u64& _Buffer, bool _Decrypt);
  68. bool ParseDisc();
  69. bool ParsePartitionData(SPartition& _rPartition);
  70. u32 GetDOLSize(u64 _DOLOffset);
  71. bool SetupScrub(const std::string& filename, int block_size)
  72. {
  73. bool success = true;
  74. m_Filename = filename;
  75. m_BlockSize = block_size;
  76. if (CLUSTER_SIZE % m_BlockSize != 0)
  77. {
  78. ERROR_LOG(DISCIO, "Block size %i is not a factor of 0x8000, scrubbing not possible", m_BlockSize);
  79. return false;
  80. }
  81. m_BlocksPerCluster = CLUSTER_SIZE / m_BlockSize;
  82. m_Disc = CreateVolumeFromFilename(filename);
  83. if (!m_Disc)
  84. return false;
  85. m_FileSize = m_Disc->GetSize();
  86. u32 numClusters = (u32)(m_FileSize / CLUSTER_SIZE);
  87. // Warn if not DVD5 or DVD9 size
  88. if (numClusters != 0x23048 && numClusters != 0x46090)
  89. WARN_LOG(DISCIO, "%s is not a standard sized Wii disc! (%x blocks)", filename.c_str(), numClusters);
  90. // Table of free blocks
  91. m_FreeTable = new u8[numClusters];
  92. std::fill(m_FreeTable, m_FreeTable + numClusters, 1);
  93. // Fill out table of free blocks
  94. success = ParseDisc();
  95. // Done with it; need it closed for the next part
  96. delete m_Disc;
  97. m_Disc = nullptr;
  98. m_BlockCount = 0;
  99. // Let's not touch the file if we've failed up to here :p
  100. if (!success)
  101. Cleanup();
  102. m_isScrubbing = success;
  103. return success;
  104. }
  105. size_t GetNextBlock(File::IOFile& in, u8* buffer)
  106. {
  107. u64 CurrentOffset = m_BlockCount * m_BlockSize;
  108. u64 i = CurrentOffset / CLUSTER_SIZE;
  109. size_t ReadBytes = 0;
  110. if (m_isScrubbing && m_FreeTable[i])
  111. {
  112. DEBUG_LOG(DISCIO, "Freeing 0x%016" PRIx64, CurrentOffset);
  113. std::fill(buffer, buffer + m_BlockSize, 0xFF);
  114. in.Seek(m_BlockSize, SEEK_CUR);
  115. ReadBytes = m_BlockSize;
  116. }
  117. else
  118. {
  119. DEBUG_LOG(DISCIO, "Used 0x%016" PRIx64, CurrentOffset);
  120. in.ReadArray(buffer, m_BlockSize, &ReadBytes);
  121. }
  122. m_BlockCount++;
  123. return ReadBytes;
  124. }
  125. void Cleanup()
  126. {
  127. if (m_FreeTable) delete[] m_FreeTable;
  128. m_FreeTable = nullptr;
  129. m_FileSize = 0;
  130. m_BlockCount = 0;
  131. m_BlockSize = 0;
  132. m_BlocksPerCluster = 0;
  133. m_isScrubbing = false;
  134. }
  135. void MarkAsUsed(u64 _Offset, u64 _Size)
  136. {
  137. u64 CurrentOffset = _Offset;
  138. u64 EndOffset = CurrentOffset + _Size;
  139. DEBUG_LOG(DISCIO, "Marking 0x%016" PRIx64 " - 0x%016" PRIx64 " as used", _Offset, EndOffset);
  140. while ((CurrentOffset < EndOffset) && (CurrentOffset < m_FileSize))
  141. {
  142. m_FreeTable[CurrentOffset / CLUSTER_SIZE] = 0;
  143. CurrentOffset += CLUSTER_SIZE;
  144. }
  145. }
  146. // Compensate for 0x400(SHA-1) per 0x8000(cluster)
  147. void MarkAsUsedE(u64 _PartitionDataOffset, u64 _Offset, u64 _Size)
  148. {
  149. u64 Offset;
  150. u64 Size;
  151. Offset = _Offset / 0x7c00;
  152. Offset = Offset * CLUSTER_SIZE;
  153. Offset += _PartitionDataOffset;
  154. Size = _Size / 0x7c00;
  155. Size = (Size + 1) * CLUSTER_SIZE;
  156. // Add on the offset in the first block for the case where data straddles blocks
  157. Size += _Offset % 0x7c00;
  158. MarkAsUsed(Offset, Size);
  159. }
  160. // Helper functions for reading the BE volume
  161. void ReadFromVolume(u64 _Offset, u64 _Length, u32& _Buffer, bool _Decrypt)
  162. {
  163. m_Disc->Read(_Offset, _Length, (u8*)&_Buffer, _Decrypt);
  164. _Buffer = Common::swap32(_Buffer);
  165. }
  166. void ReadFromVolume(u64 _Offset, u64 _Length, u64& _Buffer, bool _Decrypt)
  167. {
  168. m_Disc->Read(_Offset, _Length, (u8*)&_Buffer, _Decrypt);
  169. _Buffer = Common::swap32((u32)_Buffer);
  170. _Buffer <<= 2;
  171. }
  172. bool ParseDisc()
  173. {
  174. // Mark the header as used - it's mostly 0s anyways
  175. MarkAsUsed(0, 0x50000);
  176. for (int x = 0; x < 4; x++)
  177. {
  178. ReadFromVolume(0x40000 + (x * 8) + 0, 4, PartitionGroup[x].numPartitions, false);
  179. ReadFromVolume(0x40000 + (x * 8) + 4, 4, PartitionGroup[x].PartitionsOffset, false);
  180. // Read all partitions
  181. for (u32 i = 0; i < PartitionGroup[x].numPartitions; i++)
  182. {
  183. SPartition Partition;
  184. Partition.GroupNumber = x;
  185. Partition.Number = i;
  186. ReadFromVolume(PartitionGroup[x].PartitionsOffset + (i * 8) + 0, 4, Partition.Offset, false);
  187. ReadFromVolume(PartitionGroup[x].PartitionsOffset + (i * 8) + 4, 4, Partition.Type, false);
  188. ReadFromVolume(Partition.Offset + 0x2a4, 4, Partition.Header.TMDSize, false);
  189. ReadFromVolume(Partition.Offset + 0x2a8, 4, Partition.Header.TMDOffset, false);
  190. ReadFromVolume(Partition.Offset + 0x2ac, 4, Partition.Header.CertChainSize, false);
  191. ReadFromVolume(Partition.Offset + 0x2b0, 4, Partition.Header.CertChainOffset, false);
  192. ReadFromVolume(Partition.Offset + 0x2b4, 4, Partition.Header.H3Offset, false);
  193. ReadFromVolume(Partition.Offset + 0x2b8, 4, Partition.Header.DataOffset, false);
  194. ReadFromVolume(Partition.Offset + 0x2bc, 4, Partition.Header.DataSize, false);
  195. PartitionGroup[x].PartitionsVec.push_back(Partition);
  196. }
  197. for (auto& rPartition : PartitionGroup[x].PartitionsVec)
  198. {
  199. const SPartitionHeader& rHeader = rPartition.Header;
  200. MarkAsUsed(rPartition.Offset, 0x2c0);
  201. MarkAsUsed(rPartition.Offset + rHeader.TMDOffset, rHeader.TMDSize);
  202. MarkAsUsed(rPartition.Offset + rHeader.CertChainOffset, rHeader.CertChainSize);
  203. MarkAsUsed(rPartition.Offset + rHeader.H3Offset, 0x18000);
  204. // This would mark the whole (encrypted) data area
  205. // we need to parse FST and other crap to find what's free within it!
  206. //MarkAsUsed(rPartition.Offset + rHeader.DataOffset, rHeader.DataSize);
  207. // Parse Data! This is where the big gain is
  208. if (!ParsePartitionData(rPartition))
  209. return false;
  210. }
  211. }
  212. return true;
  213. }
  214. // Operations dealing with encrypted space are done here - the volume is swapped to allow this
  215. bool ParsePartitionData(SPartition& _rPartition)
  216. {
  217. bool ParsedOK = true;
  218. // Switch out the main volume temporarily
  219. IVolume *OldVolume = m_Disc;
  220. // Ready some stuff
  221. m_Disc = CreateVolumeFromFilename(m_Filename, _rPartition.GroupNumber, _rPartition.Number);
  222. if (m_Disc == nullptr)
  223. {
  224. ERROR_LOG(DISCIO, "Failed to create volume from file %s", m_Filename.c_str());
  225. m_Disc = OldVolume;
  226. return false;
  227. }
  228. std::unique_ptr<IFileSystem> filesystem(CreateFileSystem(m_Disc));
  229. if (!filesystem)
  230. {
  231. ERROR_LOG(DISCIO, "Failed to create filesystem for group %d partition %u", _rPartition.GroupNumber, _rPartition.Number);
  232. ParsedOK = false;
  233. }
  234. else
  235. {
  236. // Mark things as used which are not in the filesystem
  237. // Header, Header Information, Apploader
  238. ReadFromVolume(0x2440 + 0x14, 4, _rPartition.Header.ApploaderSize, true);
  239. ReadFromVolume(0x2440 + 0x18, 4, _rPartition.Header.ApploaderTrailerSize, true);
  240. MarkAsUsedE(_rPartition.Offset
  241. + _rPartition.Header.DataOffset
  242. , 0
  243. , 0x2440
  244. + _rPartition.Header.ApploaderSize
  245. + _rPartition.Header.ApploaderTrailerSize);
  246. // DOL
  247. ReadFromVolume(0x420, 4, _rPartition.Header.DOLOffset, true);
  248. _rPartition.Header.DOLSize = GetDOLSize(_rPartition.Header.DOLOffset);
  249. MarkAsUsedE(_rPartition.Offset
  250. + _rPartition.Header.DataOffset
  251. , _rPartition.Header.DOLOffset
  252. , _rPartition.Header.DOLSize);
  253. // FST
  254. ReadFromVolume(0x424, 4, _rPartition.Header.FSTOffset, true);
  255. ReadFromVolume(0x428, 4, _rPartition.Header.FSTSize, true);
  256. MarkAsUsedE(_rPartition.Offset
  257. + _rPartition.Header.DataOffset
  258. , _rPartition.Header.FSTOffset
  259. , _rPartition.Header.FSTSize);
  260. // Go through the filesystem and mark entries as used
  261. for (SFileInfo file : filesystem->GetFileList())
  262. {
  263. DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str());
  264. // Just 1byte for directory? - it will end up reserving a cluster this way
  265. if (file.m_NameOffset & 0x1000000)
  266. MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset, file.m_Offset, 1);
  267. else
  268. MarkAsUsedE(_rPartition.Offset + _rPartition.Header.DataOffset, file.m_Offset, file.m_FileSize);
  269. }
  270. }
  271. // Swap back
  272. delete m_Disc;
  273. m_Disc = OldVolume;
  274. return ParsedOK;
  275. }
  276. u32 GetDOLSize(u64 _DOLOffset)
  277. {
  278. u32 offset = 0, size = 0, max = 0;
  279. // Iterate through the 7 code segments
  280. for (u8 i = 0; i < 7; i++)
  281. {
  282. ReadFromVolume(_DOLOffset + 0x00 + i * 4, 4, offset, true);
  283. ReadFromVolume(_DOLOffset + 0x90 + i * 4, 4, size, true);
  284. if (offset + size > max)
  285. max = offset + size;
  286. }
  287. // Iterate through the 11 data segments
  288. for (u8 i = 0; i < 11; i++)
  289. {
  290. ReadFromVolume(_DOLOffset + 0x1c + i * 4, 4, offset, true);
  291. ReadFromVolume(_DOLOffset + 0xac + i * 4, 4, size, true);
  292. if (offset + size > max)
  293. max = offset + size;
  294. }
  295. return max;
  296. }
  297. } // namespace DiscScrubber
  298. } // namespace DiscIO