GCVolume: supports reading all opening.bnr information

DQT2: Game properties dialog contains info tab giving information about the selected iso.
This commit is contained in:
Rukai
2016-02-29 19:52:15 +11:00
parent afa202738e
commit b5104a79f1
29 changed files with 693 additions and 202 deletions

View File

@ -19,7 +19,8 @@ namespace DiscIO
class IVolume
{
public:
// Increment CACHE_REVISION if the enums below are modified (ISOFile.cpp & GameFile.cpp)
// Increment CACHE_REVISION if the enums below are modified (ISOFile.cpp &
// GameFile.cpp)
enum EPlatform
{
GAMECUBE_DISC = 0,
@ -85,12 +86,26 @@ public:
virtual std::string GetMakerID() const = 0;
virtual u16 GetRevision() const = 0;
virtual std::string GetInternalName() const = 0;
virtual std::map<ELanguage, std::string> GetNames(bool prefer_long) const = 0;
virtual std::map<ELanguage, std::string> GetShortNames() const
{
return std::map<ELanguage, std::string>();
}
virtual std::map<ELanguage, std::string> GetLongNames() const
{
return std::map<ELanguage, std::string>();
}
virtual std::map<ELanguage, std::string> GetShortMakers() const
{
return std::map<ELanguage, std::string>();
}
virtual std::map<ELanguage, std::string> GetLongMakers() const
{
return std::map<ELanguage, std::string>();
}
virtual std::map<ELanguage, std::string> GetDescriptions() const
{
return std::map<ELanguage, std::string>();
}
virtual std::string GetCompany() const { return std::string(); }
virtual std::vector<u32> GetBanner(int* width, int* height) const = 0;
virtual u64 GetFSTSize() const = 0;
virtual std::string GetApploaderDate() const = 0;
@ -116,7 +131,7 @@ protected:
// strnlen to trim NULLs
std::string string(data, strnlen(data, sizeof(data)));
// There don't seem to be any GC discs with the country set to Taiwan...
// There doesn't seem to be any GC discs with the country set to Taiwan...
// But maybe they would use Shift_JIS if they existed? Not sure
bool use_shift_jis = (COUNTRY_JAPAN == GetCountry() || COUNTRY_TAIWAN == GetCountry());

View File

@ -63,10 +63,14 @@ bool CVolumeDirectory::Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt
if (!decrypt && (_Offset + _Length >= 0x400) && m_is_wii)
{
// Fully supporting this would require re-encrypting every file that's read.
// Only supporting the areas that IOS allows software to read could be more feasible.
// Currently, only the header (up to 0x400) is supported, though we're cheating a bit
// with it by reading the header inside the current partition instead. Supporting the
// header is enough for booting games, but not for running things like the Disc Channel.
// Only supporting the areas that IOS allows software to read could be more
// feasible.
// Currently, only the header (up to 0x400) is supported, though we're
// cheating a bit
// with it by reading the header inside the current partition instead.
// Supporting the
// header is enough for booting games, but not for running things like the
// Disc Channel.
return false;
}
@ -183,7 +187,7 @@ std::string CVolumeDirectory::GetInternalName() const
return "";
}
std::map<IVolume::ELanguage, std::string> CVolumeDirectory::GetNames(bool prefer_long) const
std::map<IVolume::ELanguage, std::string> CVolumeDirectory::GetLongNames() const
{
std::string name = GetInternalName();
if (name.empty())

View File

@ -19,7 +19,8 @@ struct FSTEntry;
}
//
// --- this volume type is used for reading files directly from the hard drive ---
// --- this volume type is used for reading files directly from the hard drive
// ---
//
namespace DiscIO
@ -43,7 +44,7 @@ public:
u16 GetRevision() const override { return 0; }
std::string GetInternalName() const override;
std::map<IVolume::ELanguage, std::string> GetNames(bool prefer_long) const override;
std::map<IVolume::ELanguage, std::string> GetLongNames() const override;
std::vector<u32> GetBanner(int* width, int* height) const override;
void SetName(const std::string&);

View File

@ -100,48 +100,46 @@ std::string CVolumeGC::GetInternalName() const
char name[0x60];
if (m_pReader != nullptr && Read(0x20, 0x60, (u8*)name))
return DecodeString(name);
else
return "";
return "";
}
std::map<IVolume::ELanguage, std::string> CVolumeGC::GetNames(bool prefer_long) const
std::map<IVolume::ELanguage, std::string> CVolumeGC::GetShortNames() const
{
return ReadMultiLanguageStrings(false, prefer_long);
LoadBannerFile();
return m_short_names;
}
std::map<IVolume::ELanguage, std::string> CVolumeGC::GetLongNames() const
{
LoadBannerFile();
return m_long_names;
}
std::map<IVolume::ELanguage, std::string> CVolumeGC::GetShortMakers() const
{
LoadBannerFile();
return m_short_makers;
}
std::map<IVolume::ELanguage, std::string> CVolumeGC::GetLongMakers() const
{
LoadBannerFile();
return m_long_makers;
}
std::map<IVolume::ELanguage, std::string> CVolumeGC::GetDescriptions() const
{
return ReadMultiLanguageStrings(true);
}
std::string CVolumeGC::GetCompany() const
{
if (!LoadBannerFile())
return "";
std::string company = DecodeString(m_banner_file.comment[0].longMaker);
if (company.empty())
company = DecodeString(m_banner_file.comment[0].shortMaker);
return company;
LoadBannerFile();
return m_descriptions;
}
std::vector<u32> CVolumeGC::GetBanner(int* width, int* height) const
{
if (!LoadBannerFile())
{
*width = 0;
*height = 0;
return std::vector<u32>();
}
std::vector<u32> image_buffer(GC_BANNER_WIDTH * GC_BANNER_HEIGHT);
ColorUtil::decode5A3image(image_buffer.data(), m_banner_file.image, GC_BANNER_WIDTH,
GC_BANNER_HEIGHT);
*width = GC_BANNER_WIDTH;
*height = GC_BANNER_HEIGHT;
return image_buffer;
LoadBannerFile();
*width = m_image_width;
*height = m_image_height;
return m_image_buffer;
}
u64 CVolumeGC::GetFSTSize() const
@ -201,102 +199,93 @@ IVolume::EPlatform CVolumeGC::GetVolumeType() const
return GAMECUBE_DISC;
}
// Returns true if the loaded banner file is valid,
// regardless of whether it was loaded by the current call
bool CVolumeGC::LoadBannerFile() const
void CVolumeGC::LoadBannerFile() const
{
// The methods ReadMultiLanguageStrings, GetCompany and GetBanner
// need to access the opening.bnr file. These methods are
// usually called one after another. The file is cached in
// RAM to avoid reading it from the disc several times, but
// if none of these methods are called, the file is never loaded.
// If opening.bnr has been loaded already, return immediately
if (m_banner_file_type != BANNER_NOT_LOADED)
return m_banner_file_type != BANNER_INVALID;
if (m_banner_loaded)
return;
GCBanner banner_file;
std::unique_ptr<IFileSystem> file_system(CreateFileSystem(this));
size_t file_size = (size_t)file_system->GetFileSize("opening.bnr");
if (file_size == BNR1_SIZE || file_size == BNR2_SIZE)
{
file_system->ReadFile("opening.bnr", reinterpret_cast<u8*>(&m_banner_file), file_size);
if (file_size == BNR1_SIZE && m_banner_file.id == 0x31524e42) // "BNR1"
{
m_banner_file_type = BANNER_BNR1;
}
else if (file_size == BNR2_SIZE && m_banner_file.id == 0x32524e42) // "BNR2"
{
m_banner_file_type = BANNER_BNR2;
}
else
{
m_banner_file_type = BANNER_INVALID;
WARN_LOG(DISCIO, "Invalid opening.bnr. Type: %0x Size: %0zx", m_banner_file.id, file_size);
}
constexpr int BNR1_MAGIC = 0x31524e42;
constexpr int BNR2_MAGIC = 0x32524e42;
if (file_size != BNR1_SIZE && file_size != BNR2_SIZE)
{
WARN_LOG(DISCIO, "Invalid opening.bnr. Size: %0zx", file_size);
return;
}
file_system->ReadFile("opening.bnr", reinterpret_cast<u8*>(&banner_file), file_size);
bool is_bnr1;
if (banner_file.id == BNR1_MAGIC && file_size == BNR1_SIZE)
{
is_bnr1 = true;
}
else if (banner_file.id == BNR2_MAGIC && file_size == BNR2_SIZE)
{
is_bnr1 = false;
}
else
{
m_banner_file_type = BANNER_INVALID;
WARN_LOG(DISCIO, "Invalid opening.bnr. Size: %0zx", file_size);
WARN_LOG(DISCIO, "Invalid opening.bnr. Type: %0x Size: %0zx", banner_file.id, file_size);
return;
}
return m_banner_file_type != BANNER_INVALID;
ExtractBannerInformation(banner_file, is_bnr1);
m_banner_loaded = true;
}
std::map<IVolume::ELanguage, std::string>
CVolumeGC::ReadMultiLanguageStrings(bool description, bool prefer_long) const
void CVolumeGC::ExtractBannerInformation(const GCBanner& banner_file, bool is_bnr1) const
{
std::map<ELanguage, std::string> strings;
if (!LoadBannerFile())
return strings;
u32 number_of_languages = 0;
ELanguage start_language = LANGUAGE_UNKNOWN;
bool is_japanese = GetCountry() == ECountry::COUNTRY_JAPAN;
switch (m_banner_file_type)
if (is_bnr1) // NTSC
{
case BANNER_BNR1: // NTSC
number_of_languages = 1;
start_language = is_japanese ? ELanguage::LANGUAGE_JAPANESE : ELanguage::LANGUAGE_ENGLISH;
break;
case BANNER_BNR2: // PAL
}
else // PAL
{
number_of_languages = 6;
start_language = ELanguage::LANGUAGE_ENGLISH;
break;
// Shouldn't happen
case BANNER_INVALID:
case BANNER_NOT_LOADED:
break;
}
m_image_width = GC_BANNER_WIDTH;
m_image_height = GC_BANNER_HEIGHT;
m_image_buffer = std::vector<u32>(m_image_width * m_image_height);
ColorUtil::decode5A3image(m_image_buffer.data(), banner_file.image, m_image_width,
m_image_height);
for (u32 i = 0; i < number_of_languages; ++i)
{
const GCBannerComment& comment = m_banner_file.comment[i];
std::string string;
const GCBannerInformation& info = banner_file.information[i];
ELanguage language = static_cast<ELanguage>(start_language + i);
if (description)
{
string = DecodeString(comment.comment);
}
else // Title
{
if (prefer_long)
string = DecodeString(comment.longTitle);
std::string description = DecodeString(info.description);
if (!description.empty())
m_descriptions[language] = description;
if (string.empty())
string = DecodeString(comment.shortTitle);
}
std::string short_name = DecodeString(info.short_name);
if (!short_name.empty())
m_short_names[language] = short_name;
if (!string.empty())
strings[(ELanguage)(start_language + i)] = string;
std::string long_name = DecodeString(info.long_name);
if (!long_name.empty())
m_long_names[language] = long_name;
std::string short_maker = DecodeString(info.short_maker);
if (!short_maker.empty())
m_short_makers[language] = short_maker;
std::string long_maker = DecodeString(info.long_maker);
if (!long_maker.empty())
m_long_makers[language] = long_maker;
}
return strings;
}
} // namespace

View File

@ -27,9 +27,11 @@ public:
std::string GetMakerID() const override;
u16 GetRevision() const override;
std::string GetInternalName() const override;
std::map<ELanguage, std::string> GetNames(bool prefer_long) const override;
std::map<ELanguage, std::string> GetShortNames() const override;
std::map<ELanguage, std::string> GetLongNames() const override;
std::map<ELanguage, std::string> GetShortMakers() const override;
std::map<ELanguage, std::string> GetLongMakers() const override;
std::map<ELanguage, std::string> GetDescriptions() const override;
std::string GetCompany() const override;
std::vector<u32> GetBanner(int* width, int* height) const override;
u64 GetFSTSize() const override;
std::string GetApploaderDate() const override;
@ -42,21 +44,18 @@ public:
u64 GetRawSize() const override;
private:
bool LoadBannerFile() const;
std::map<ELanguage, std::string> ReadMultiLanguageStrings(bool description,
bool prefer_long = true) const;
static const int GC_BANNER_WIDTH = 96;
static const int GC_BANNER_HEIGHT = 32;
// Banner Comment
struct GCBannerComment
struct GCBannerInformation
{
char shortTitle[32]; // Short game title shown in IPL menu
char shortMaker[32]; // Short developer, publisher names shown in IPL menu
char longTitle[64]; // Long game title shown in IPL game start screen
char longMaker[64]; // Long developer, publisher names shown in IPL game start screen
char comment[128]; // Game description shown in IPL game start screen in two lines.
char short_name[32]; // Short game title shown in IPL menu
char short_maker[32]; // Short developer, publisher names shown in IPL menu
char long_name[64]; // Long game title shown in IPL game start screen
char long_maker[64]; // Long developer, publisher names shown in IPL game
// start screen
char description[128]; // Game description shown in IPL game start screen in
// two lines.
};
struct GCBanner
@ -64,22 +63,27 @@ private:
u32 id; // "BNR1" for NTSC, "BNR2" for PAL
u32 padding[7];
u16 image[GC_BANNER_WIDTH * GC_BANNER_HEIGHT]; // RGB5A3 96x32 image
GCBannerComment comment[6]; // Comments in six languages (only one for BNR1 type)
GCBannerInformation information[6]; // information comes in six languages
// (only one for BNR1 type)
};
static const size_t BNR1_SIZE = sizeof(GCBanner) - sizeof(GCBannerComment) * 5;
void LoadBannerFile() const;
void ExtractBannerInformation(const GCBanner& banner_file, bool is_bnr1) const;
static const size_t BNR1_SIZE = sizeof(GCBanner) - sizeof(GCBannerInformation) * 5;
static const size_t BNR2_SIZE = sizeof(GCBanner);
enum BannerFileType
{
BANNER_NOT_LOADED,
BANNER_INVALID,
BANNER_BNR1,
BANNER_BNR2
};
mutable std::map<ELanguage, std::string> m_short_names;
mutable BannerFileType m_banner_file_type = BANNER_NOT_LOADED;
mutable GCBanner m_banner_file;
mutable std::map<ELanguage, std::string> m_long_names;
mutable std::map<ELanguage, std::string> m_short_makers;
mutable std::map<ELanguage, std::string> m_long_makers;
mutable std::map<ELanguage, std::string> m_descriptions;
mutable bool m_banner_loaded = false;
mutable std::vector<u32> m_image_buffer;
mutable int m_image_height = 0;
mutable int m_image_width = 0;
std::unique_ptr<IBlobReader> m_pReader;
};

View File

@ -89,7 +89,8 @@ std::string CVolumeWAD::GetUniqueID() const
std::string CVolumeWAD::GetMakerID() const
{
char temp[2] = {1};
// Some weird channels use 0x0000 in place of the MakerID, so we need a check there
// Some weird channels use 0x0000 in place of the MakerID, so we need a check
// there
if (!Read(0x198 + m_tmd_offset, 2, (u8*)temp) || temp[0] == 0 || temp[1] == 0)
return "00";
@ -119,7 +120,7 @@ IVolume::EPlatform CVolumeWAD::GetVolumeType() const
return WII_WAD;
}
std::map<IVolume::ELanguage, std::string> CVolumeWAD::GetNames(bool prefer_long) const
std::map<IVolume::ELanguage, std::string> CVolumeWAD::GetLongNames() const
{
std::vector<u8> name_data(NAMES_TOTAL_BYTES);
if (!Read(m_opening_bnr_offset + 0x9C, NAMES_TOTAL_BYTES, name_data.data()))

View File

@ -14,7 +14,8 @@
#include "DiscIO/Volume.h"
// --- this volume type is used for Wad files ---
// Some of this code might look redundant with the CNANDContentLoader class, however,
// Some of this code might look redundant with the CNANDContentLoader class,
// however,
// We do not do any decryption here, we do raw read, so things are -Faster-
namespace DiscIO
@ -30,7 +31,7 @@ public:
std::string GetMakerID() const override;
u16 GetRevision() const override;
std::string GetInternalName() const override { return ""; }
std::map<IVolume::ELanguage, std::string> GetNames(bool prefer_long) const override;
std::map<IVolume::ELanguage, std::string> GetLongNames() const override;
std::vector<u32> GetBanner(int* width, int* height) const override;
u64 GetFSTSize() const override { return 0; }
std::string GetApploaderDate() const override { return ""; }

View File

@ -238,7 +238,7 @@ std::string CVolumeWiiCrypted::GetInternalName() const
return "";
}
std::map<IVolume::ELanguage, std::string> CVolumeWiiCrypted::GetNames(bool prefer_long) const
std::map<IVolume::ELanguage, std::string> CVolumeWiiCrypted::GetLongNames() const
{
std::unique_ptr<IFileSystem> file_system(CreateFileSystem(this));
std::vector<u8> opening_bnr(NAMES_TOTAL_BYTES);

View File

@ -31,7 +31,7 @@ public:
std::string GetMakerID() const override;
u16 GetRevision() const override;
std::string GetInternalName() const override;
std::map<IVolume::ELanguage, std::string> GetNames(bool prefer_long) const override;
std::map<IVolume::ELanguage, std::string> GetLongNames() const override;
std::vector<u32> GetBanner(int* width, int* height) const override;
u64 GetFSTSize() const override;
std::string GetApploaderDate() const override;