usbloadergx/source/banner/OpeningBNR.cpp

509 lines
13 KiB
C++

/*
Copyright (c) 2012 - Dimok
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#if __GNUC__ > 8
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
#endif
#include <malloc.h>
#include <stdio.h>
#include "Channels/channels.h"
#include "GameCube/GCGames.h"
#include "libs/libwbfs/gcdisc.h"
#include "FileOperations/fileops.h"
#include "language/gettext.h"
#include "usbloader/disc.h"
#include "usbloader/wbfs.h"
#include "usbloader/wdvd.h"
#include "usbloader/wbfs/wbfs_rw.h"
#include "utils/sjis.h"
#include "utils/uncompress.h"
#include "themes/CTheme.h"
#include "settings/GameTitles.h"
#include "wstring.hpp"
#include "OpeningBNR.hpp"
BNRInstance *BNRInstance::instance = NULL;
OpeningBNR::OpeningBNR()
: imetHdr(0), filesize(0)
{
memset(gameID, 0, sizeof(gameID));
}
OpeningBNR::~OpeningBNR()
{
if (imetHdr)
free(imetHdr);
}
bool OpeningBNR::LoadCachedBNR(const char *id)
{
char path[255];
snprintf(path, sizeof(path), "%s%.6s.bnr", Settings.BNRCachePath, id);
if ((filesize = FileSize(path)) == 0)
{
snprintf(path, sizeof(path), "%s%.3s.bnr", Settings.BNRCachePath, id);
if ((filesize = FileSize(path)) == 0)
return false;
}
FILE *f = fopen(path, "rb");
if (!f)
return false;
imetHdr = (IMETHeader *)malloc(filesize);
if (!imetHdr)
{
fclose(f);
return false;
}
fread(imetHdr, 1, filesize, f);
fclose(f);
if (imetHdr->fcc != 'IMET')
{
//! check if it's a channel .app file
IMETHeader *channelImet = (IMETHeader *)(((u8 *)imetHdr) + 0x40);
if (channelImet->fcc != 'IMET')
{
free(imetHdr);
imetHdr = NULL;
return false;
}
//! just move it 0x40 bytes back, the rest 0x40 bytes will just be unused
//! as it's a temporary file usually it's not worth to reallocate
filesize -= 0x40;
memcpy(imetHdr, channelImet, filesize);
}
return true;
}
void OpeningBNR::WriteCachedBNR(const char *id, const u8 *buffer, u32 size)
{
char path[255];
snprintf(path, sizeof(path), "%s%.6s.bnr", Settings.BNRCachePath, id);
CreateSubfolder(Settings.BNRCachePath);
FILE *f = fopen(path, "wb");
if (!f)
return;
fwrite(buffer, 1, size, f);
fclose(f);
}
bool OpeningBNR::Load(const discHdr *header)
{
if (!header)
return false;
if (memcmp(gameID, header->id, 6) == 0)
return true;
memcpy(gameID, header->id, 6);
if (imetHdr)
free(imetHdr);
imetHdr = NULL;
switch (header->type)
{
case TYPE_GAME_WII_IMG:
case TYPE_GAME_WII_DISC:
return LoadWiiBanner(header);
case TYPE_GAME_NANDCHAN:
case TYPE_GAME_EMUNANDCHAN:
return LoadChannelBanner(header);
case TYPE_GAME_GC_IMG:
case TYPE_GAME_GC_DISC:
case TYPE_GAME_GC_EXTRACTED:
if (!Settings.CacheBNRFiles)
return false;
return LoadCachedBNR((char *)header->id);
default:
break;
}
return false;
}
bool OpeningBNR::LoadWiiBanner(const discHdr *header)
{
if (!header || ((header->type != TYPE_GAME_WII_IMG) && (header->type != TYPE_GAME_WII_DISC)))
return false;
if (Settings.CacheBNRFiles && LoadCachedBNR((const char *)header->id))
return true;
if (header->type == TYPE_GAME_WII_DISC)
{
wiidisc_t *wdisc = wd_open_disc(__ReadDVD, 0);
if (!wdisc)
return false;
imetHdr = (IMETHeader *)wd_extract_file(wdisc, ALL_PARTITIONS, (char *)"opening.bnr");
filesize = wdisc->extracted_size;
wd_close_disc(wdisc);
}
else
{
wbfs_disc_t *disc = WBFS_OpenDisc((u8 *)gameID);
if (!disc)
return false;
wiidisc_t *wdisc = wd_open_disc((s32(*)(void *, u32, u32, void *))wbfs_disc_read, disc);
if (!wdisc)
{
WBFS_CloseDisc(disc);
return false;
}
imetHdr = (IMETHeader *)wd_extract_file(wdisc, ALL_PARTITIONS, (char *)"opening.bnr");
filesize = wdisc->extracted_size;
wd_close_disc(wdisc);
WBFS_CloseDisc(disc);
}
if (!imetHdr)
return false;
if (imetHdr->fcc != 'IMET')
{
free(imetHdr);
imetHdr = NULL;
return false;
}
if (Settings.CacheBNRFiles)
WriteCachedBNR((const char *)header->id, (u8 *)imetHdr, filesize);
return true;
}
bool OpeningBNR::LoadChannelBanner(const discHdr *header)
{
if (!header || (header->tid == 0) || ((header->type != TYPE_GAME_NANDCHAN) && (header->type != TYPE_GAME_EMUNANDCHAN)))
return false;
if (Settings.CacheBNRFiles && LoadCachedBNR((char *)header->id))
return true;
const u64 &tid = header->tid;
const char *pathPrefix = (header->type == TYPE_GAME_EMUNANDCHAN) ? Settings.NandEmuChanPath : "";
imetHdr = (IMETHeader *)Channels::GetOpeningBnr(tid, &filesize, pathPrefix);
if (!imetHdr)
return false;
if (imetHdr->fcc != 'IMET')
{
free(imetHdr);
imetHdr = NULL;
return false;
}
if (Settings.CacheBNRFiles)
WriteCachedBNR((char *)header->id, (u8 *)imetHdr, filesize);
return true;
}
const u16 *OpeningBNR::GetIMETTitle(int lang)
{
if (!imetHdr || lang < 0 || lang >= 10)
return NULL;
if (imetHdr->fcc != 'IMET')
return NULL;
if (imetHdr->names[lang][0] == 0)
lang = CONF_LANG_ENGLISH;
return imetHdr->names[lang]; // possible unaligned pointer value
}
static s32 GC_Disc_Read(void *fp, u32 offset, u32 count, void *iobuf)
{
if (fp)
{
fseek((FILE *)fp, offset, SEEK_SET);
return fread(iobuf, 1, count, (FILE *)fp);
}
return __ReadDVDPlain(iobuf, count, offset);
}
u8 *OpeningBNR::LoadGCBNR(const discHdr *header, u32 *len)
{
if (!header || ((header->type != TYPE_GAME_GC_IMG) && (header->type != TYPE_GAME_GC_DISC) && (header->type != TYPE_GAME_GC_EXTRACTED)))
return NULL;
const char *path = GCGames::Instance()->GetPath((char *)header->id);
if (!path)
return NULL;
FILE *file = NULL;
GC_OpeningBnr *openingBnr = NULL;
// read from file
if ((header->type == TYPE_GAME_GC_IMG) || (header->type == TYPE_GAME_GC_DISC))
{
//! open iso file if it's iso
if (header->type == TYPE_GAME_GC_IMG)
{
file = fopen(path, "rb");
if (!file)
return NULL;
}
gcdisc_t *disc = gc_open_disc(GC_Disc_Read, file);
if (!disc)
{
fclose(file);
return NULL;
}
if (!strcmp(Settings.db_language, "JA"))
{
bool loaded = gc_extract_file(disc, "openingJA.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "opening.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "openingUS.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "openingEU.bnr");
}
else if (!strcmp(Settings.db_language, "EN"))
{
bool loaded = gc_extract_file(disc, "openingUS.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "opening.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "openingEU.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "openingJA.bnr");
}
else
{
bool loaded = gc_extract_file(disc, "openingEU.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "opening.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "openingUS.bnr");
if (!loaded)
loaded = gc_extract_file(disc, "openingJA.bnr");
}
openingBnr = (GC_OpeningBnr *)disc->extracted_buffer;
if (len)
*len = disc->extracted_size;
gc_close_disc(disc);
}
else if (header->type == TYPE_GAME_GC_EXTRACTED)
{
std::string gamePath(path);
gamePath += "root/";
//! open default file first
file = fopen((gamePath + "opening.bnr").c_str(), "rb");
// if not found try the region specific ones
if (!strcmp(Settings.db_language, "JA"))
{
if (!file)
file = fopen((gamePath + "openingJA.bnr").c_str(), "rb");
if (!file)
file = fopen((gamePath + "openingUS.bnr").c_str(), "rb");
if (!file)
file = fopen((gamePath + "openingEU.bnr").c_str(), "rb");
}
else if (!strcmp(Settings.db_language, "EN"))
{
if (!file)
file = fopen((gamePath + "openingUS.bnr").c_str(), "rb");
if (!file)
file = fopen((gamePath + "openingEU.bnr").c_str(), "rb");
if (!file)
file = fopen((gamePath + "openingJA.bnr").c_str(), "rb");
}
else
{
if (!file)
file = fopen((gamePath + "openingEU.bnr").c_str(), "rb");
if (!file)
file = fopen((gamePath + "openingUS.bnr").c_str(), "rb");
if (!file)
file = fopen((gamePath + "openingJA.bnr").c_str(), "rb");
}
// file not found
if (!file)
return NULL;
fseek(file, 0, SEEK_END);
int size = ftell(file);
rewind(file);
openingBnr = (GC_OpeningBnr *)malloc(size);
if (openingBnr)
{
if (len)
*len = size;
fread(openingBnr, 1, size, file);
}
}
if (file)
fclose(file);
if (!openingBnr)
return NULL;
// check magic of the opening bnr
if (openingBnr->magic != 'BNR1' && openingBnr->magic != 'BNR2')
{
free(openingBnr);
return NULL;
}
return (u8 *)openingBnr;
}
CustomBanner *OpeningBNR::CreateGCBanner(const discHdr *header)
{
int language = 0;
u32 openingBnrSize;
wString developer;
GC_OpeningBnr *openingBnr = (GC_OpeningBnr *)LoadGCBNR(header, &openingBnrSize);
CustomBanner *banner = new CustomBanner;
banner->LoadBanner(Resources::GetFile("custom_banner.bnr"), Resources::GetFileSize("custom_banner.bnr"));
banner->SetBannerPngImage("bg.tpl", Resources::GetFile("gc_banner_bg.png"), Resources::GetFileSize("gc_banner_bg.png"));
banner->SetBannerText("T_PF", tr("GameCube"));
if (openingBnr)
{
banner->SetBannerTexture("HBPic.tpl", openingBnr->tpl_data, 96, 32, GX_TF_RGB5A3);
// European opening bnr file
if (openingBnr->magic == 'BNR2')
{
if (!strcmp(Settings.db_language, "DE"))
language = 1;
else if (!strcmp(Settings.db_language, "FR"))
language = 2;
else if (!strcmp(Settings.db_language, "ES"))
language = 3;
else if (!strcmp(Settings.db_language, "IT"))
language = 4;
else if (!strcmp(Settings.db_language, "NL"))
language = 5;
if ((0x1820 + sizeof(openingBnr->description[0]) * language) > openingBnrSize)
{
language = 0;
}
}
// sets the developer
developer.resize(strlen((char *)openingBnr->description[language].developer));
for (u32 i = 0; i < developer.size(); i++)
developer[i] = *(openingBnr->description[language].developer + i);
banner->SetBannerText("T_Coded_by", tr("Developer:"));
banner->SetBannerText("T_coder", developer.toUTF8().c_str());
// sets the description and converts encodings (Japan and Taiwan)
if (header->id[3] == 'J' || header->id[3] == 'W')
{
std::string description((char *)openingBnr->description[language].long_description);
banner->SetBannerText("T_short_descript", sj2utf8(description).c_str());
}
else
{
wString description;
description.resize(strlen((char *)openingBnr->description[language].long_description));
for (u32 i = 0; i < description.size(); i++)
description[i] = *(openingBnr->description[language].long_description + i);
banner->SetBannerText("T_short_descript", description.toUTF8().c_str());
}
// free buffer
free(openingBnr);
}
else
{
banner->SetBannerPngImage("HBPic.tpl", Resources::GetFile("gc_icon_bg.png"), Resources::GetFileSize("gc_icon_bg.png"));
banner->SetBannerText("T_Coded_by", tr("Developer:"));
banner->SetBannerText("T_coder", tr("Unknown"));
banner->SetBannerText("T_short_descript", " ");
}
if (strcmp(Settings.db_language, "KO") == 0)
{
banner->SetBannerText("T_Coded_by", developer.toUTF8().c_str());
banner->SetBannerPaneVisible("T_coder", false);
}
else
banner->SetBannerText("T_name", GameTitles.GetTitle(header));
banner->SetBannerPaneVisible("Line1", false);
banner->SetBannerPaneVisible("Line2", false);
banner->SetBannerPaneVisible("T_Released", false);
banner->SetBannerPaneVisible("T_release_date", false);
banner->SetBannerPaneVisible("T_versiontext", false);
banner->SetBannerPaneVisible("T_version", false);
banner->SetBannerTextureScale(Settings.GCBannerScale);
return banner;
}
CustomBanner *OpeningBNR::CreateGCIcon(const discHdr *header)
{
GC_OpeningBnr *openingBnr = (GC_OpeningBnr *)LoadGCBNR(header);
CustomBanner *newBanner = new CustomBanner;
newBanner->LoadIcon(Resources::GetFile("custom_banner.bnr"), Resources::GetFileSize("custom_banner.bnr"));
if (openingBnr)
newBanner->SetIconTexture("Iconpng.tpl", openingBnr->tpl_data, 96, 32, GX_TF_RGB5A3);
else
newBanner->SetIconPngImage("Iconpng.tpl", Resources::GetFile("gc_icon_bg.png"), Resources::GetFileSize("gc_icon_bg.png"));
newBanner->SetIconPngImage("HBLogo.tpl", Resources::GetFile("gc_icon_bg.png"), Resources::GetFileSize("gc_icon_bg.png"));
// free buffer
if (openingBnr)
free(openingBnr);
return newBanner;
}