[libromdata] GameBoyAdvance: Added external title screen images using my new RPDB subdomain.

Example URL: https://rpdb.gerbilsoft.com/gba/title/E/ASOE78.png

I spent a good while taking screenshots of the vast majority of retail
GBA games. There's probably a few missing, especially GBA Video titles.

Next is DMG, which will be a bit more difficult:
- Game IDs weren't present in the header prior to CGB, and early CGB
  titles don't have them. We'll need to use the title.
- Some games have the same title in multiple regions. We'll need to
  use the region byte for this, though it only distinguishes between
  JP and non-JP.
- SGB-enhanced games that don't also support CGB will have the SGB
  border. Should we also take SGB screenshots of CGB+SGB games, and
  non-SGB screenshots of SGB games?

[rp-download] Added support for the gba/ cache key.
This commit is contained in:
David Korth 2020-03-23 17:53:42 -04:00
parent 390e8f14c9
commit 346dad0faa
7 changed files with 238 additions and 2 deletions

View File

@ -20,6 +20,8 @@ The configuration file is located at:
* https://art.gametdb.com/ - Box, cover, and media scans for Nintendo
GameCube, Wii, Wii U, DS, and 3DS games.
* https://amiibo.life/ - Images of Nintendo amiibo products.
* https://rpdb.gerbilsoft.com/ - Title screen images of Nintendo
Game Boy Advance games.
## Security features

View File

@ -22,6 +22,7 @@ using std::vector;
namespace LibRomData {
ROMDATA_IMPL(GameBoyAdvance)
ROMDATA_IMPL_IMG(GameBoyAdvance)
class GameBoyAdvancePrivate : public RomDataPrivate
{
@ -245,6 +246,68 @@ const char *const *GameBoyAdvance::supportedMimeTypes_static(void)
return mimeTypes;
}
/**
* Get a bitfield of image types this class can retrieve.
* @return Bitfield of supported image types. (ImageTypesBF)
*/
uint32_t GameBoyAdvance::supportedImageTypes_static(void)
{
return IMGBF_EXT_TITLE_SCREEN;
}
/**
* Get a list of all available image sizes for the specified image type.
* @param imageType Image type.
* @return Vector of available image sizes, or empty vector if no images are available.
*/
vector<RomData::ImageSizeDef> GameBoyAdvance::supportedImageSizes_static(ImageType imageType)
{
ASSERT_supportedImageSizes(imageType);
switch (imageType) {
case IMG_EXT_TITLE_SCREEN: {
static const ImageSizeDef sz_EXT_TITLE_SCREEN[] = {
{nullptr, 240, 160, 0},
};
return vector<ImageSizeDef>(sz_EXT_TITLE_SCREEN,
sz_EXT_TITLE_SCREEN + ARRAY_SIZE(sz_EXT_TITLE_SCREEN));
}
default:
break;
}
// Unsupported image type.
return vector<ImageSizeDef>();
}
/**
* Get image processing flags.
*
* These specify post-processing operations for images,
* e.g. applying transparency masks.
*
* @param imageType Image type.
* @return Bitfield of ImageProcessingBF operations to perform.
*/
uint32_t GameBoyAdvance::imgpf(ImageType imageType) const
{
ASSERT_imgpf(imageType);
uint32_t ret = 0;
switch (imageType) {
case IMG_EXT_TITLE_SCREEN:
// Use nearest-neighbor scaling when resizing.
ret = IMGPF_RESCALE_NEAREST;
break;
default:
// GameTDB's Nintendo DS cover scans have alpha transparency.
// Hence, no image processing is required.
break;
}
return ret;
}
/**
* Load field data.
* Called by RomData::fields() if the field data hasn't been loaded yet.
@ -361,4 +424,98 @@ int GameBoyAdvance::loadFieldData(void)
return static_cast<int>(d->fields->count());
}
/**
* Get a list of URLs for an external image type.
*
* A thumbnail size may be requested from the shell.
* If the subclass supports multiple sizes, it should
* try to get the size that most closely matches the
* requested size.
*
* @param imageType [in] Image type.
* @param pExtURLs [out] Output vector.
* @param size [in,opt] Requested image size. This may be a requested
* thumbnail size in pixels, or an ImageSizeType
* enum value.
* @return 0 on success; negative POSIX error code on error.
*/
int GameBoyAdvance::extURLs(ImageType imageType, vector<ExtURL> *pExtURLs, int size) const
{
ASSERT_extURLs(imageType, pExtURLs);
pExtURLs->clear();
// Check for GBA ROMs that don't have external images.
RP_D(const GameBoyAdvance);
if (!d->isValid || d->romType < 0) {
// ROM image isn't valid.
return -EIO;
} else if (!memcmp(d->romHeader.id4, "AGBJ", 4) ||
!memcmp(d->romHeader.id4, " ", 4))
{
// This is a prototype.
// No external images are available.
// TODO: CRC32-based images?
return -ENOENT;
} else if (d->romType == GameBoyAdvancePrivate::ROM_NDS_EXP) {
// This is a Nintendo DS expansion cartridge.
// No external images are available.
return -ENOENT;
}
// NOTE: We only have one size for GBA right now.
RP_UNUSED(size);
vector<ImageSizeDef> sizeDefs = supportedImageSizes(imageType);
assert(sizeDefs.size() == 1);
if (sizeDefs.empty()) {
// No image sizes.
return -ENOENT;
}
// NOTE: RPDB's title screen database only has one size.
// There's no need to check image sizes, but we need to
// get the image size for the extURLs struct.
// Determine the image type name.
const char *imageTypeName;
const char *ext;
switch (imageType) {
case IMG_EXT_TITLE_SCREEN:
imageTypeName = "title";
ext = ".png";
break;
default:
// Unsupported image type.
return -ENOENT;
}
// Game ID. (RPDB uses ID6 for Game Boy Advance.)
// The ID6 cannot have non-printable characters.
char id6[7];
for (int i = ARRAY_SIZE(d->romHeader.id6)-1; i >= 0; i--) {
if (!ISPRINT(d->romHeader.id6[i])) {
// Non-printable character found.
return -ENOENT;
}
id6[i] = d->romHeader.id6[i];
}
// NULL-terminated ID6 is needed for the
// RPDB URL functions.
id6[6] = 0;
// Region code is id6[3].
const char region_code[2] = {id6[3], '\0'};
// Add the URLs.
pExtURLs->resize(1);
auto extURL_iter = pExtURLs->begin();
extURL_iter->url = d->getURL_RPDB("gba", imageTypeName, region_code, id6, ext);
extURL_iter->cache_key = d->getCacheKey_RPDB("gba", imageTypeName, region_code, id6, ext);
extURL_iter->width = sizeDefs[0].width;
extURL_iter->height = sizeDefs[0].height;
extURL_iter->high_res = (sizeDefs[0].index >= 2);
// All URLs added.
return 0;
}
}

View File

@ -2,7 +2,7 @@
* ROM Properties Page shell extension. (libromdata) *
* GameBoyAdvance.hpp: Nintendo Game Boy Advance ROM reader. *
* *
* Copyright (c) 2016-2018 by David Korth. *
* Copyright (c) 2016-2020 by David Korth. *
* SPDX-License-Identifier: GPL-2.0-or-later *
***************************************************************************/
@ -14,6 +14,9 @@
namespace LibRomData {
ROMDATA_DECL_BEGIN(GameBoyAdvance)
ROMDATA_DECL_IMGSUPPORT()
ROMDATA_DECL_IMGPF()
ROMDATA_DECL_IMGEXT()
ROMDATA_DECL_END()
}

View File

@ -246,7 +246,7 @@ const RomDataFactoryPrivate::RomDataFns RomDataFactoryPrivate::romDataFns_magic[
// Handhelds
GetRomDataFns_addr(DMG, ATTR_HAS_METADATA, 0x104, 0xCEED6666),
GetRomDataFns_addr(GameBoyAdvance, ATTR_NONE, 0x04, 0x24FFAE51),
GetRomDataFns_addr(GameBoyAdvance, ATTR_HAS_THUMBNAIL, 0x04, 0x24FFAE51),
GetRomDataFns_addr(Lynx, ATTR_NONE, 0, 'LYNX'),
GetRomDataFns_addr(NGPC, ATTR_HAS_METADATA, 12, ' SNK'),
GetRomDataFns_addr(Nintendo3DSFirm, ATTR_NONE, 0, 'FIRM'),

View File

@ -104,6 +104,44 @@ string RomDataPrivate::getCacheKey_GameTDB(
system, type, region, gameID, ext);
}
/**
* Get the RPDB URL for a given game.
* @param system System name.
* @param type Image type.
* @param region Region name.
* @param gameID Game ID.
* @param ext File extension, e.g. ".png" or ".jpg".
* TODO: PAL multi-region selection?
* @return RPDB URL.
*/
string RomDataPrivate::getURL_RPDB(
const char *system, const char *type,
const char *region, const char *gameID,
const char *ext)
{
return rp_sprintf("https://rpdb.gerbilsoft.com/%s/%s/%s/%s%s",
system, type, region, gameID, ext);
}
/**
* Get the RPDB cache key for a given game.
* @param system System name.
* @param type Image type.
* @param region Region name.
* @param gameID Game ID.
* @param ext File extension, e.g. ".png" or ".jpg".
* TODO: PAL multi-region selection?
* @return RPDB cache key.
*/
string RomDataPrivate::getCacheKey_RPDB(
const char *system, const char *type,
const char *region, const char *gameID,
const char *ext)
{
return rp_sprintf("%s/%s/%s/%s%s",
system, type, region, gameID, ext);
}
/**
* Select the best size for an image.
* @param sizeDefs Image size definitions.

View File

@ -98,6 +98,36 @@ class RomDataPrivate
const char *region, const char *gameID,
const char *ext);
/**
* Get the RPDB URL for a given game.
* @param system System name.
* @param type Image type.
* @param region Region name.
* @param gameID Game ID.
* @param ext File extension, e.g. ".png" or ".jpg".
* TODO: PAL multi-region selection?
* @return RPDB URL.
*/
static std::string getURL_RPDB(
const char *system, const char *type,
const char *region, const char *gameID,
const char *ext);
/**
* Get the RPDB cache key for a given game.
* @param system System name.
* @param type Image type.
* @param region Region name.
* @param gameID Game ID.
* @param ext File extension, e.g. ".png" or ".jpg".
* TODO: PAL multi-region selection?
* @return RPDB cache key.
*/
static std::string getCacheKey_RPDB(
const char *system, const char *type,
const char *region, const char *gameID,
const char *ext);
/**
* Select the best size for an image.
* @param sizeDefs Image size definitions.

View File

@ -342,6 +342,7 @@ int RP_C_API _tmain(int argc, TCHAR *argv[])
SCMP_SYS(recvfrom), // getaddrinfo() (32-bit only?)
// cURL and OpenSSL
SCMP_SYS(bind), // getaddrinfo() [curl_thread_create_thunk(), curl-7.68.0]
#ifdef __SNR_getrandom
SCMP_SYS(getrandom),
#endif /* __SNR_getrandom */
@ -406,6 +407,7 @@ int RP_C_API _tmain(int argc, TCHAR *argv[])
// - 3ds: https://art.gametdb.com/3ds/[key]
// - ds: https://art.gametdb.com/3ds/[key]
// - amiibo: https://amiibo.life/[key]/image
// - gba: https://rpdb.gerbilsoft.com/gba/[key]
const TCHAR *slash_pos = _tcschr(cache_key, _T('/'));
if (slash_pos == nullptr || slash_pos == cache_key ||
slash_pos[1] == '\0')
@ -463,6 +465,10 @@ int RP_C_API _tmain(int argc, TCHAR *argv[])
_sntprintf(full_url, _countof(full_url),
_T("https://amiibo.life/nfc/%.*s/image"),
static_cast<int>(filename_len), slash_pos+1);
} else if (prefix_len == 3 && !_tcsncmp(cache_key, _T("gba"), 3)) {
// Game Boy Advance
_sntprintf(full_url, _countof(full_url),
_T("https://rpdb.gerbilsoft.com/%s"), cache_key);
} else {
// Prefix is not supported.
SHOW_ERROR(_T("Cache key '%s' has an unsupported prefix."), cache_key);