VolumeDirectory.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. // Copyright 2008 Dolphin Emulator Project
  2. // Licensed under GPLv2+
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <cstddef>
  6. #include <cstring>
  7. #include <map>
  8. #include <memory>
  9. #include <string>
  10. #include <vector>
  11. #include "Common/CommonPaths.h"
  12. #include "Common/CommonTypes.h"
  13. #include "Common/FileUtil.h"
  14. #include "Common/MathUtil.h"
  15. #include "DiscIO/FileBlob.h"
  16. #include "DiscIO/FileMonitor.h"
  17. #include "DiscIO/Volume.h"
  18. #include "DiscIO/VolumeDirectory.h"
  19. namespace DiscIO
  20. {
  21. CVolumeDirectory::CVolumeDirectory(const std::string& _rDirectory, bool _bIsWii,
  22. const std::string& _rApploader, const std::string& _rDOL)
  23. : m_totalNameSize(0)
  24. , m_dataStartAddress(-1)
  25. , m_diskHeader(DISKHEADERINFO_ADDRESS)
  26. , m_diskHeaderInfo(new SDiskHeaderInfo())
  27. , m_fst_address(0)
  28. , m_dol_address(0)
  29. {
  30. m_rootDirectory = ExtractDirectoryName(_rDirectory);
  31. // create the default disk header
  32. SetUniqueID("AGBJ01");
  33. SetName("Default name");
  34. if (_bIsWii)
  35. {
  36. SetDiskTypeWii();
  37. }
  38. else
  39. {
  40. SetDiskTypeGC();
  41. }
  42. // Don't load the dol if we've no apploader...
  43. if (SetApploader(_rApploader))
  44. SetDOL(_rDOL);
  45. BuildFST();
  46. }
  47. CVolumeDirectory::~CVolumeDirectory()
  48. {
  49. }
  50. bool CVolumeDirectory::IsValidDirectory(const std::string& _rDirectory)
  51. {
  52. std::string directoryName = ExtractDirectoryName(_rDirectory);
  53. return File::IsDirectory(directoryName);
  54. }
  55. bool CVolumeDirectory::Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const
  56. {
  57. if (!decrypt && (_Offset + _Length >= 0x400) && m_is_wii)
  58. {
  59. // Fully supporting this would require re-encrypting every file that's read.
  60. // Only supporting the areas that IOS allows software to read could be more feasible.
  61. // Currently, only the header (up to 0x400) is supported, though we're cheating a bit
  62. // with it by reading the header inside the current partition instead. Supporting the
  63. // header is enough for booting games, but not for running things like the Disc Channel.
  64. return false;
  65. }
  66. if (decrypt && !m_is_wii)
  67. PanicAlertT("Tried to decrypt data from a non-Wii volume");
  68. // header
  69. if (_Offset < DISKHEADERINFO_ADDRESS)
  70. {
  71. WriteToBuffer(DISKHEADER_ADDRESS, DISKHEADERINFO_ADDRESS, m_diskHeader.data(), _Offset, _Length, _pBuffer);
  72. }
  73. // header info
  74. if (_Offset >= DISKHEADERINFO_ADDRESS && _Offset < APPLOADER_ADDRESS)
  75. {
  76. WriteToBuffer(DISKHEADERINFO_ADDRESS, sizeof(m_diskHeaderInfo), (u8*)m_diskHeaderInfo.get(), _Offset, _Length, _pBuffer);
  77. }
  78. // apploader
  79. if (_Offset >= APPLOADER_ADDRESS && _Offset < APPLOADER_ADDRESS + m_apploader.size())
  80. {
  81. WriteToBuffer(APPLOADER_ADDRESS, m_apploader.size(), m_apploader.data(), _Offset, _Length, _pBuffer);
  82. }
  83. // dol
  84. if (_Offset >= m_dol_address && _Offset < m_dol_address + m_DOL.size())
  85. {
  86. WriteToBuffer(m_dol_address, m_DOL.size(), m_DOL.data(), _Offset, _Length, _pBuffer);
  87. }
  88. // fst
  89. if (_Offset >= m_fst_address && _Offset < m_dataStartAddress)
  90. {
  91. WriteToBuffer(m_fst_address, m_FSTData.size(), m_FSTData.data(), _Offset, _Length, _pBuffer);
  92. }
  93. if (m_virtualDisk.empty())
  94. return true;
  95. // Determine which file the offset refers to
  96. std::map<u64, std::string>::const_iterator fileIter = m_virtualDisk.lower_bound(_Offset);
  97. if (fileIter->first > _Offset && fileIter != m_virtualDisk.begin())
  98. --fileIter;
  99. // zero fill to start of file data
  100. PadToAddress(fileIter->first, _Offset, _Length, _pBuffer);
  101. while (fileIter != m_virtualDisk.end() && _Length > 0)
  102. {
  103. _dbg_assert_(DVDINTERFACE, fileIter->first <= _Offset);
  104. u64 fileOffset = _Offset - fileIter->first;
  105. const std::string fileName = fileIter->second;
  106. std::unique_ptr<PlainFileReader> reader(PlainFileReader::Create(fileName));
  107. if (reader == nullptr)
  108. return false;
  109. u64 fileSize = reader->GetDataSize();
  110. FileMon::CheckFile(fileName, fileSize);
  111. if (fileOffset < fileSize)
  112. {
  113. u64 fileBytes = fileSize - fileOffset;
  114. if (_Length < fileBytes)
  115. fileBytes = _Length;
  116. if (!reader->Read(fileOffset, fileBytes, _pBuffer))
  117. return false;
  118. _Length -= fileBytes;
  119. _pBuffer += fileBytes;
  120. _Offset += fileBytes;
  121. }
  122. ++fileIter;
  123. if (fileIter != m_virtualDisk.end())
  124. {
  125. _dbg_assert_(DVDINTERFACE, fileIter->first >= _Offset);
  126. PadToAddress(fileIter->first, _Offset, _Length, _pBuffer);
  127. }
  128. }
  129. return true;
  130. }
  131. std::string CVolumeDirectory::GetUniqueID() const
  132. {
  133. static const size_t ID_LENGTH = 6;
  134. return std::string(m_diskHeader.begin(), m_diskHeader.begin() + ID_LENGTH);
  135. }
  136. void CVolumeDirectory::SetUniqueID(const std::string& id)
  137. {
  138. size_t length = id.length();
  139. if (length > 6)
  140. length = 6;
  141. memcpy(m_diskHeader.data(), id.c_str(), length);
  142. }
  143. IVolume::ECountry CVolumeDirectory::GetCountry() const
  144. {
  145. u8 country_code = m_diskHeader[3];
  146. return CountrySwitch(country_code);
  147. }
  148. std::string CVolumeDirectory::GetMakerID() const
  149. {
  150. return "VOID";
  151. }
  152. std::string CVolumeDirectory::GetInternalName() const
  153. {
  154. char name[0x60];
  155. if (Read(0x20, 0x60, (u8*)name, false))
  156. return DecodeString(name);
  157. else
  158. return "";
  159. }
  160. std::map<IVolume::ELanguage, std::string> CVolumeDirectory::GetNames(bool prefer_long) const
  161. {
  162. std::map<IVolume::ELanguage, std::string> names;
  163. std::string name = GetInternalName();
  164. if (!name.empty())
  165. names[IVolume::ELanguage::LANGUAGE_UNKNOWN] = name;
  166. return names;
  167. }
  168. void CVolumeDirectory::SetName(const std::string& name)
  169. {
  170. size_t length = name.length();
  171. if (length > MAX_NAME_LENGTH)
  172. length = MAX_NAME_LENGTH;
  173. memcpy(&m_diskHeader[0x20], name.c_str(), length);
  174. m_diskHeader[length + 0x20] = 0;
  175. }
  176. u64 CVolumeDirectory::GetFSTSize() const
  177. {
  178. return 0;
  179. }
  180. std::string CVolumeDirectory::GetApploaderDate() const
  181. {
  182. return "VOID";
  183. }
  184. IVolume::EPlatform CVolumeDirectory::GetVolumeType() const
  185. {
  186. return m_is_wii ? WII_DISC : GAMECUBE_DISC;
  187. }
  188. u64 CVolumeDirectory::GetSize() const
  189. {
  190. return 0;
  191. }
  192. u64 CVolumeDirectory::GetRawSize() const
  193. {
  194. return GetSize();
  195. }
  196. std::string CVolumeDirectory::ExtractDirectoryName(const std::string& _rDirectory)
  197. {
  198. std::string directoryName = _rDirectory;
  199. size_t lastSep = directoryName.find_last_of(DIR_SEP_CHR);
  200. if (lastSep != directoryName.size() - 1)
  201. {
  202. // TODO: This assumes that file names will always have a dot in them
  203. // and directory names never will; both assumptions are often
  204. // right but in general wrong.
  205. size_t extensionStart = directoryName.find_last_of('.');
  206. if (extensionStart != std::string::npos && extensionStart > lastSep)
  207. {
  208. directoryName.resize(lastSep);
  209. }
  210. }
  211. else
  212. {
  213. directoryName.resize(lastSep);
  214. }
  215. return directoryName;
  216. }
  217. void CVolumeDirectory::SetDiskTypeWii()
  218. {
  219. m_diskHeader[0x18] = 0x5d;
  220. m_diskHeader[0x19] = 0x1c;
  221. m_diskHeader[0x1a] = 0x9e;
  222. m_diskHeader[0x1b] = 0xa3;
  223. memset(&m_diskHeader[0x1c], 0, 4);
  224. m_is_wii = true;
  225. m_addressShift = 2;
  226. }
  227. void CVolumeDirectory::SetDiskTypeGC()
  228. {
  229. memset(&m_diskHeader[0x18], 0, 4);
  230. m_diskHeader[0x1c] = 0xc2;
  231. m_diskHeader[0x1d] = 0x33;
  232. m_diskHeader[0x1e] = 0x9f;
  233. m_diskHeader[0x1f] = 0x3d;
  234. m_is_wii = false;
  235. m_addressShift = 0;
  236. }
  237. bool CVolumeDirectory::SetApploader(const std::string& _rApploader)
  238. {
  239. if (!_rApploader.empty())
  240. {
  241. std::string data;
  242. if (!File::ReadFileToString(_rApploader, data))
  243. {
  244. PanicAlertT("Apploader unable to load from file");
  245. return false;
  246. }
  247. size_t apploaderSize = 0x20 + Common::swap32(*(u32*)&data.data()[0x14]) + Common::swap32(*(u32*)&data.data()[0x18]);
  248. if (apploaderSize != data.size())
  249. {
  250. PanicAlertT("Apploader is the wrong size...is it really an apploader?");
  251. return false;
  252. }
  253. m_apploader.resize(apploaderSize);
  254. std::copy(data.begin(), data.end(), m_apploader.begin());
  255. // 32byte aligned (plus 0x20 padding)
  256. m_dol_address = ROUND_UP(APPLOADER_ADDRESS + m_apploader.size() + 0x20, 0x20ull);
  257. return true;
  258. }
  259. else
  260. {
  261. m_apploader.resize(0x20);
  262. // Make sure BS2 HLE doesn't try to run the apploader
  263. *(u32*)&m_apploader[0x10] = (u32)-1;
  264. return false;
  265. }
  266. }
  267. void CVolumeDirectory::SetDOL(const std::string& rDOL)
  268. {
  269. if (!rDOL.empty())
  270. {
  271. std::string data;
  272. File::ReadFileToString(rDOL, data);
  273. m_DOL.resize(data.size());
  274. std::copy(data.begin(), data.end(), m_DOL.begin());
  275. Write32((u32)(m_dol_address >> m_addressShift), 0x0420, &m_diskHeader);
  276. // 32byte aligned (plus 0x20 padding)
  277. m_fst_address = ROUND_UP(m_dol_address + m_DOL.size() + 0x20, 0x20ull);
  278. }
  279. }
  280. void CVolumeDirectory::BuildFST()
  281. {
  282. m_FSTData.clear();
  283. File::FSTEntry rootEntry;
  284. // read data from physical disk to rootEntry
  285. u64 totalEntries = AddDirectoryEntries(m_rootDirectory, rootEntry) + 1;
  286. m_fstNameOffset = totalEntries * ENTRY_SIZE; // offset in FST nameTable
  287. m_FSTData.resize(m_fstNameOffset + m_totalNameSize);
  288. // if FST hasn't been assigned (ie no apploader/dol setup), set to default
  289. if (m_fst_address == 0)
  290. m_fst_address = APPLOADER_ADDRESS + 0x2000;
  291. // 4 byte aligned start of data on disk
  292. m_dataStartAddress = ROUND_UP(m_fst_address + m_FSTData.size(), 0x8000ull);
  293. u64 curDataAddress = m_dataStartAddress;
  294. u32 fstOffset = 0; // Offset within FST data
  295. u32 nameOffset = 0; // Offset within name table
  296. u32 rootOffset = 0; // Offset of root of FST
  297. // write root entry
  298. WriteEntryData(fstOffset, DIRECTORY_ENTRY, 0, 0, totalEntries);
  299. for (auto& entry : rootEntry.children)
  300. {
  301. WriteEntry(entry, fstOffset, nameOffset, curDataAddress, rootOffset);
  302. }
  303. // overflow check
  304. _dbg_assert_(DVDINTERFACE, nameOffset == m_totalNameSize);
  305. // write FST size and location
  306. Write32((u32)(m_fst_address >> m_addressShift), 0x0424, &m_diskHeader);
  307. Write32((u32)(m_FSTData.size() >> m_addressShift), 0x0428, &m_diskHeader);
  308. Write32((u32)(m_FSTData.size() >> m_addressShift), 0x042c, &m_diskHeader);
  309. }
  310. void CVolumeDirectory::WriteToBuffer(u64 _SrcStartAddress, u64 _SrcLength, const u8* _Src,
  311. u64& _Address, u64& _Length, u8*& _pBuffer) const
  312. {
  313. if (_Length == 0)
  314. return;
  315. _dbg_assert_(DVDINTERFACE, _Address >= _SrcStartAddress);
  316. u64 srcOffset = _Address - _SrcStartAddress;
  317. if (srcOffset < _SrcLength)
  318. {
  319. u64 srcBytes = _SrcLength - srcOffset;
  320. if (_Length < srcBytes)
  321. srcBytes = _Length;
  322. memcpy(_pBuffer, _Src + srcOffset, (size_t)srcBytes);
  323. _Length -= srcBytes;
  324. _pBuffer += srcBytes;
  325. _Address += srcBytes;
  326. }
  327. }
  328. void CVolumeDirectory::PadToAddress(u64 _StartAddress, u64& _Address, u64& _Length, u8*& _pBuffer) const
  329. {
  330. if (_StartAddress <= _Address)
  331. return;
  332. u64 padBytes = _StartAddress - _Address;
  333. if (padBytes > _Length)
  334. padBytes = _Length;
  335. if (_Length > 0)
  336. {
  337. memset(_pBuffer, 0, (size_t)padBytes);
  338. _Length -= padBytes;
  339. _pBuffer += padBytes;
  340. _Address += padBytes;
  341. }
  342. }
  343. void CVolumeDirectory::Write32(u32 data, u32 offset, std::vector<u8>* const buffer)
  344. {
  345. (*buffer)[offset++] = (data >> 24);
  346. (*buffer)[offset++] = (data >> 16) & 0xff;
  347. (*buffer)[offset++] = (data >> 8) & 0xff;
  348. (*buffer)[offset] = (data) & 0xff;
  349. }
  350. void CVolumeDirectory::WriteEntryData(u32& entryOffset, u8 type, u32 nameOffset, u64 dataOffset, u64 length)
  351. {
  352. m_FSTData[entryOffset++] = type;
  353. m_FSTData[entryOffset++] = (nameOffset >> 16) & 0xff;
  354. m_FSTData[entryOffset++] = (nameOffset >> 8) & 0xff;
  355. m_FSTData[entryOffset++] = (nameOffset) & 0xff;
  356. Write32((u32)(dataOffset >> m_addressShift), entryOffset, &m_FSTData);
  357. entryOffset += 4;
  358. Write32((u32)length, entryOffset, &m_FSTData);
  359. entryOffset += 4;
  360. }
  361. void CVolumeDirectory::WriteEntryName(u32& nameOffset, const std::string& name)
  362. {
  363. strncpy((char*)&m_FSTData[nameOffset + m_fstNameOffset], name.c_str(), name.length() + 1);
  364. nameOffset += (u32)(name.length() + 1);
  365. }
  366. void CVolumeDirectory::WriteEntry(const File::FSTEntry& entry, u32& fstOffset, u32& nameOffset, u64& dataOffset, u32 parentEntryNum)
  367. {
  368. if (entry.isDirectory)
  369. {
  370. u32 myOffset = fstOffset;
  371. u32 myEntryNum = myOffset / ENTRY_SIZE;
  372. WriteEntryData(fstOffset, DIRECTORY_ENTRY, nameOffset, parentEntryNum, myEntryNum + entry.size + 1);
  373. WriteEntryName(nameOffset, entry.virtualName);
  374. for (const auto& child : entry.children)
  375. {
  376. WriteEntry(child, fstOffset, nameOffset, dataOffset, myEntryNum);
  377. }
  378. }
  379. else
  380. {
  381. // put entry in FST
  382. WriteEntryData(fstOffset, FILE_ENTRY, nameOffset, dataOffset, entry.size);
  383. WriteEntryName(nameOffset, entry.virtualName);
  384. // write entry to virtual disk
  385. _dbg_assert_(DVDINTERFACE, m_virtualDisk.find(dataOffset) == m_virtualDisk.end());
  386. m_virtualDisk.insert(make_pair(dataOffset, entry.physicalName));
  387. // 4 byte aligned
  388. dataOffset = ROUND_UP(dataOffset + entry.size, 0x8000ull);
  389. }
  390. }
  391. static u32 ComputeNameSize(const File::FSTEntry& parentEntry)
  392. {
  393. u32 nameSize = 0;
  394. const std::vector<File::FSTEntry>& children = parentEntry.children;
  395. for (auto it = children.cbegin(); it != children.cend(); ++it)
  396. {
  397. const File::FSTEntry& entry = *it;
  398. if (entry.isDirectory)
  399. {
  400. nameSize += ComputeNameSize(entry);
  401. }
  402. nameSize += (u32)entry.virtualName.length() + 1;
  403. }
  404. return nameSize;
  405. }
  406. u64 CVolumeDirectory::AddDirectoryEntries(const std::string& _Directory, File::FSTEntry& parentEntry)
  407. {
  408. parentEntry = File::ScanDirectoryTree(_Directory, true);
  409. m_totalNameSize += ComputeNameSize(parentEntry);
  410. return parentEntry.size;
  411. }
  412. } // namespace