VolumeGC.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright 2008 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "DiscIO/VolumeGC.h"
  4. #include <cstddef>
  5. #include <map>
  6. #include <memory>
  7. #include <optional>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include "Common/Assert.h"
  12. #include "Common/ColorUtil.h"
  13. #include "Common/CommonTypes.h"
  14. #include "Common/Crypto/SHA1.h"
  15. #include "Common/Logging/Log.h"
  16. #include "Common/MsgHandler.h"
  17. #include "Common/StringUtil.h"
  18. #include "DiscIO/Blob.h"
  19. #include "DiscIO/DiscExtractor.h"
  20. #include "DiscIO/DiscUtils.h"
  21. #include "DiscIO/Enums.h"
  22. #include "DiscIO/FileSystemGCWii.h"
  23. #include "DiscIO/Filesystem.h"
  24. #include "DiscIO/Volume.h"
  25. namespace DiscIO
  26. {
  27. VolumeGC::VolumeGC(std::unique_ptr<BlobReader> reader)
  28. : m_reader(std::move(reader)), m_is_triforce(false)
  29. {
  30. ASSERT(m_reader);
  31. m_file_system = [this]() -> std::unique_ptr<FileSystem> {
  32. auto file_system = std::make_unique<FileSystemGCWii>(this, PARTITION_NONE);
  33. return file_system->IsValid() ? std::move(file_system) : nullptr;
  34. };
  35. m_converted_banner = [this] { return LoadBannerFile(); };
  36. constexpr u32 BTID_MAGIC = 0x44495442;
  37. auto tmp_fs = GetFileSystem(PARTITION_NONE);
  38. if (tmp_fs)
  39. {
  40. std::unique_ptr<FileInfo> file_info = tmp_fs->FindFileInfo("boot.id");
  41. if (!file_info)
  42. return;
  43. BootID triforce_header;
  44. const u64 file_size = ReadFile(*this, PARTITION_NONE, file_info.get(),
  45. reinterpret_cast<u8*>(&triforce_header), sizeof(BootID));
  46. if (file_size >= 4 && triforce_header.magic == BTID_MAGIC)
  47. {
  48. m_is_triforce = true;
  49. m_triforce_id = triforce_header.id;
  50. }
  51. }
  52. }
  53. VolumeGC::~VolumeGC() = default;
  54. bool VolumeGC::Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const
  55. {
  56. if (partition != PARTITION_NONE)
  57. return false;
  58. return m_reader->Read(offset, length, buffer);
  59. }
  60. const FileSystem* VolumeGC::GetFileSystem(const Partition& partition) const
  61. {
  62. return m_file_system->get();
  63. }
  64. std::string VolumeGC::GetGameTDBID(const Partition& partition) const
  65. {
  66. // Datel discs for the GameCube can have one of two different game IDs:
  67. //
  68. // 1: GNHE5d. (Yes, with a lowercase d.) This game ID is used not only for
  69. // all kinds of Datel discs, but also for the licensed release NHL Hitz 2002.
  70. //
  71. // 2: DTLX01. This game ID is used for a few late Datel releases. Both Action Replay
  72. // and FreeLoader are known to have been released under this game ID.
  73. //
  74. // Since no game ID used for Datel discs uniquely represents one product,
  75. // never use the game ID of a Datel disc for looking up the title or cover art.
  76. if (IsDatelDisc())
  77. return "";
  78. // Normal case. Just return the usual game ID.
  79. return GetGameID(partition);
  80. }
  81. std::string VolumeGC::GetTriforceID() const
  82. {
  83. if (m_is_triforce)
  84. return (std::string(m_triforce_id.data(), m_triforce_id.size()));
  85. else
  86. return "";
  87. }
  88. Region VolumeGC::GetRegion() const
  89. {
  90. return RegionCodeToRegion(m_reader->ReadSwapped<u32>(0x458));
  91. }
  92. std::map<Language, std::string> VolumeGC::GetShortNames() const
  93. {
  94. return m_converted_banner->short_names;
  95. }
  96. std::map<Language, std::string> VolumeGC::GetLongNames() const
  97. {
  98. return m_converted_banner->long_names;
  99. }
  100. std::map<Language, std::string> VolumeGC::GetShortMakers() const
  101. {
  102. return m_converted_banner->short_makers;
  103. }
  104. std::map<Language, std::string> VolumeGC::GetLongMakers() const
  105. {
  106. return m_converted_banner->long_makers;
  107. }
  108. std::map<Language, std::string> VolumeGC::GetDescriptions() const
  109. {
  110. return m_converted_banner->descriptions;
  111. }
  112. std::vector<u32> VolumeGC::GetBanner(u32* width, u32* height) const
  113. {
  114. *width = m_converted_banner->image_width;
  115. *height = m_converted_banner->image_height;
  116. return m_converted_banner->image_buffer;
  117. }
  118. BlobType VolumeGC::GetBlobType() const
  119. {
  120. return m_reader->GetBlobType();
  121. }
  122. u64 VolumeGC::GetDataSize() const
  123. {
  124. return m_reader->GetDataSize();
  125. }
  126. DataSizeType VolumeGC::GetDataSizeType() const
  127. {
  128. return m_reader->GetDataSizeType();
  129. }
  130. u64 VolumeGC::GetRawSize() const
  131. {
  132. return m_reader->GetRawSize();
  133. }
  134. const BlobReader& VolumeGC::GetBlobReader() const
  135. {
  136. return *m_reader;
  137. }
  138. Platform VolumeGC::GetVolumeType() const
  139. {
  140. if (m_is_triforce)
  141. return Platform::Triforce;
  142. else
  143. return Platform::GameCubeDisc;
  144. }
  145. bool VolumeGC::IsDatelDisc() const
  146. {
  147. return GetGameID() == "DTLX01" || !GetBootDOLOffset(*this, PARTITION_NONE).has_value();
  148. }
  149. std::array<u8, 20> VolumeGC::GetSyncHash() const
  150. {
  151. auto context = Common::SHA1::CreateContext();
  152. AddGamePartitionToSyncHash(context.get());
  153. return context->Finish();
  154. }
  155. VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const
  156. {
  157. GCBanner banner_file;
  158. const u64 file_size = ReadFile(*this, PARTITION_NONE, "opening.bnr",
  159. reinterpret_cast<u8*>(&banner_file), sizeof(GCBanner));
  160. if (file_size < 4)
  161. {
  162. WARN_LOG_FMT(DISCIO, "Could not read opening.bnr.");
  163. return {}; // Return early so that we don't access the uninitialized banner_file.id
  164. }
  165. constexpr u32 BNR1_MAGIC = 0x31524e42;
  166. constexpr u32 BNR2_MAGIC = 0x32524e42;
  167. bool is_bnr1;
  168. if (banner_file.id == BNR1_MAGIC && file_size == BNR1_SIZE)
  169. {
  170. is_bnr1 = true;
  171. }
  172. else if (banner_file.id == BNR2_MAGIC && file_size == BNR2_SIZE)
  173. {
  174. is_bnr1 = false;
  175. }
  176. else
  177. {
  178. WARN_LOG_FMT(DISCIO, "Invalid opening.bnr. Type: {:#0x} Size: {:#0x}", banner_file.id,
  179. file_size);
  180. return {};
  181. }
  182. return ExtractBannerInformation(banner_file, is_bnr1);
  183. }
  184. VolumeGC::ConvertedGCBanner VolumeGC::ExtractBannerInformation(const GCBanner& banner_file,
  185. bool is_bnr1) const
  186. {
  187. ConvertedGCBanner banner;
  188. u32 number_of_languages = 0;
  189. Language start_language = Language::Unknown;
  190. if (is_bnr1) // NTSC
  191. {
  192. number_of_languages = 1;
  193. start_language = GetRegion() == Region::NTSC_J ? Language::Japanese : Language::English;
  194. }
  195. else // PAL
  196. {
  197. number_of_languages = 6;
  198. start_language = Language::English;
  199. }
  200. banner.image_width = GC_BANNER_WIDTH;
  201. banner.image_height = GC_BANNER_HEIGHT;
  202. banner.image_buffer = std::vector<u32>(GC_BANNER_WIDTH * GC_BANNER_HEIGHT);
  203. Common::Decode5A3Image(banner.image_buffer.data(), banner_file.image, GC_BANNER_WIDTH,
  204. GC_BANNER_HEIGHT);
  205. for (u32 i = 0; i < number_of_languages; ++i)
  206. {
  207. const GCBannerInformation& info = banner_file.information[i];
  208. Language language = static_cast<Language>(static_cast<int>(start_language) + i);
  209. std::string description = DecodeString(info.description);
  210. if (!description.empty())
  211. banner.descriptions.emplace(language, description);
  212. std::string short_name = DecodeString(info.short_name);
  213. if (!short_name.empty())
  214. banner.short_names.emplace(language, short_name);
  215. std::string long_name = DecodeString(info.long_name);
  216. if (!long_name.empty())
  217. banner.long_names.emplace(language, long_name);
  218. std::string short_maker = DecodeString(info.short_maker);
  219. if (!short_maker.empty())
  220. banner.short_makers.emplace(language, short_maker);
  221. std::string long_maker = DecodeString(info.long_maker);
  222. if (!long_maker.empty())
  223. banner.long_makers.emplace(language, long_maker);
  224. }
  225. return banner;
  226. }
  227. VolumeGC::ConvertedGCBanner::ConvertedGCBanner() = default;
  228. VolumeGC::ConvertedGCBanner::~ConvertedGCBanner() = default;
  229. } // namespace DiscIO