summaryrefslogtreecommitdiff
path: root/Source/Core/DiscIO/VolumeDisc.cpp
blob: 2ec71b9fe111274b9ad2a7269dba8b5720d8f617 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// Copyright 2020 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "DiscIO/VolumeDisc.h"

#include <memory>
#include <optional>
#include <string>

#include "Common/CommonTypes.h"
#include "Common/Crypto/SHA1.h"
#include "DiscIO/DiscUtils.h"
#include "DiscIO/Enums.h"
#include "DiscIO/Filesystem.h"
#include "DiscIO/VolumeGC.h"

namespace DiscIO
{
std::string VolumeDisc::GetGameID(const Partition& partition) const
{
  char id[6]{};

  if (GetVolumeType() == Platform::Triforce)
  {
    // Triforce games have their Game ID stored in the boot.id file
    const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();

    // Construct game ID from the BTID
    id[0] = 'G';

    memcpy(id + 1, boot_id->game_id.data() + 2, 2);

    switch (GetCountry())
    {
    default:
    case Country::Japan:
      id[3] = 'J';
      break;
    case Country::Taiwan:
      id[3] = 'W';
      break;
    case Country::USA:
      id[3] = 'E';
      break;
    case Country::Europe:
      id[3] = 'P';
      break;
    }

    const std::string maker_id{GetMakerID()};
    memcpy(id + 4, maker_id.c_str(), std::min<std::size_t>(maker_id.size(), 2));

    return FilterGameID(id);
  }

  if (!Read(0, sizeof(id), reinterpret_cast<u8*>(id), partition))
    return std::string();

  return FilterGameID(id);
}

Country VolumeDisc::GetCountry(const Partition& partition) const
{
  u8 country_byte = 0;

  if (GetVolumeType() == Platform::Triforce)
  {
    const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();

    switch (boot_id->region_flags)
    {
    default:
    case 0x02:  // JAPAN
      country_byte = 'J';
      break;
    case 0x08:  // ASIA
      country_byte = 'W';
      break;
    case 0x0E:  // USA
      country_byte = 'E';
      break;
    case 0x0C:  // EXPORT
      country_byte = 'P';
      break;
    }
  }
  else
  {
    // The 0 that we use as a default value is mapped to Country::Unknown and Region::Unknown
    country_byte = ReadSwapped<u8>(3, partition).value_or(0);
  }

  const Region region = GetRegion();
  const std::optional<u16> revision = GetRevision();

  if (CountryCodeToRegion(country_byte, GetVolumeType(), region, revision) != region)
    return TypicalCountryForRegion(region);

  return CountryCodeToCountry(country_byte, GetVolumeType(), region, revision);
}

Region VolumeDisc::RegionCodeToRegion(std::optional<u32> region_code) const
{
  if (!region_code)
    return Region::Unknown;

  const Region region = static_cast<Region>(*region_code);
  return region <= Region::NTSC_K ? region : Region::Unknown;
}

std::string VolumeDisc::GetMakerID(const Partition& partition) const
{
  char maker_id[2];

  // Triforce games have their maker stored in the boot.id file as an actual string
  if (GetVolumeType() == Platform::Triforce)
  {
    const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();

    // There only seem to be three different makers here,
    // so we can just check the first char to difference between them.
    //
    // NAMCO CORPORATION, SEGA CORPORATION and Hitmaker co,ltd.

    switch (boot_id->manufacturer[0])
    {
    case 'S':  // SEGA CORPORATION
    case 'H':  // Hitmaker co,ltd
      return "6E";
    case 'N':  // NAMCO CORPORATION
      return "82";
    default:
      break;
    }
    // Fall back to normal maker from header
  }

  if (!Read(0x4, sizeof(maker_id), reinterpret_cast<u8*>(&maker_id), partition))
    return std::string();

  return FilterGameID(maker_id);
}

std::optional<u16> VolumeDisc::GetRevision(const Partition& partition) const
{
  std::optional<u8> revision = ReadSwapped<u8>(7, partition);
  return revision ? *revision : std::optional<u16>();
}

std::string VolumeDisc::GetInternalName(const Partition& partition) const
{
  char name[0x60];

  // Triforce games have their Title stored in the boot.id file
  if (GetVolumeType() == Platform::Triforce)
  {
    const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();
    return DecodeString(boot_id->game_name);
  }

  if (!Read(0x20, sizeof(name), reinterpret_cast<u8*>(&name), partition))
    return std::string();

  return DecodeString(name);
}

std::string VolumeDisc::GetApploaderDate(const Partition& partition) const
{
  char date[16];

  if (!Read(0x2440, sizeof(date), reinterpret_cast<u8*>(&date), partition))
    return std::string();

  return DecodeString(date);
}

std::optional<u8> VolumeDisc::GetDiscNumber(const Partition& partition) const
{
  return ReadSwapped<u8>(6, partition);
}

bool VolumeDisc::IsNKit() const
{
  constexpr u32 NKIT_MAGIC = 0x4E4B4954;  // "NKIT"
  return ReadSwapped<u32>(0x200, PARTITION_NONE) == NKIT_MAGIC;
}

void VolumeDisc::AddGamePartitionToSyncHash(Common::SHA1::Context* context) const
{
  const Partition partition = GetGamePartition();

  // All headers at the beginning of the partition, plus the apploader
  ReadAndAddToSyncHash(context, 0, 0x2440 + GetApploaderSize(*this, partition).value_or(0),
                       partition);

  // Boot DOL (may be missing if this is a Datel disc)
  const std::optional<u64> dol_offset = GetBootDOLOffset(*this, partition);
  if (dol_offset)
  {
    ReadAndAddToSyncHash(context, *dol_offset,
                         GetBootDOLSize(*this, partition, *dol_offset).value_or(0), partition);
  }

  // File system
  const std::optional<u64> fst_offset = GetFSTOffset(*this, partition);
  if (fst_offset)
    ReadAndAddToSyncHash(context, *fst_offset, GetFSTSize(*this, partition).value_or(0), partition);

  // opening.bnr (name and banner)
  const FileSystem* file_system = GetFileSystem(partition);
  if (file_system)
  {
    std::unique_ptr<FileInfo> file_info = file_system->FindFileInfo("opening.bnr");
    if (file_info && !file_info->IsDirectory())
      ReadAndAddToSyncHash(context, file_info->GetOffset(), file_info->GetSize(), partition);
  }
}

}  // namespace DiscIO