mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
Scanner: pick an Xbox release by the regions its executable names
Redump gives one serial to every release of an Xbox or Xbox 360 title, and to the extras discs that came with them, so a serial lookup found the first record - "Halo Movies (Japan)" for a North American Halo. formats/xdvdfs now reads the regions a title is for: the game region of an XBE's certificate, and the region of an XEX's security info, each only where the executable is long enough to hold it. The scanner reads the region field of the records a serial matches when it has regions to go by, and takes the first record whose regions overlap them; when none does, the first record stands in, as before. The duplicate DB_EXTRACT_* definitions in database_info.c go: the header carries them, with DB_EXTRACT_REGION added.
This commit is contained in:
1 parent
b93738dd4a
commit
1acc68146f
9 files changed
+265
-76
No files matched your search
+10
-14
@@ -219,20 +219,6 @@ char *bin_to_hex_alloc(const uint8_t *data, size_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Field selection flags for database_cursor_iterate_filtered.
|
||||
* 0 = extract all fields (backward compatible). */
|
||||
#define DB_EXTRACT_NAME (1 << 0)
|
||||
#define DB_EXTRACT_CRC (1 << 1)
|
||||
#define DB_EXTRACT_SERIAL (1 << 2)
|
||||
#define DB_EXTRACT_SIZE (1 << 3)
|
||||
#define DB_EXTRACT_MD5 (1 << 4)
|
||||
#define DB_EXTRACT_SHA1 (1 << 5)
|
||||
#define DB_EXTRACT_ROM_NAME (1 << 6)
|
||||
|
||||
/* Combination used by the scanner */
|
||||
#define DB_EXTRACT_SCAN_FIELDS \
|
||||
(DB_EXTRACT_NAME | DB_EXTRACT_CRC | DB_EXTRACT_SERIAL | DB_EXTRACT_SIZE)
|
||||
|
||||
static int database_cursor_iterate(libretrodb_cursor_t *cur,
|
||||
database_info_t *db_info)
|
||||
{
|
||||
@@ -683,6 +669,16 @@ static int database_info_fill_from_dom(struct rmsgpack_dom_value *item,
|
||||
db_info->serial = strdup(vs);
|
||||
}
|
||||
}
|
||||
else if ( (fields & DB_EXTRACT_REGION)
|
||||
&& memcmp(str, "region", 6) == 0)
|
||||
{
|
||||
if (val->type == RDT_STRING || val->type == RDT_BINARY)
|
||||
{
|
||||
const char *vs = val->val.string.buff;
|
||||
if (vs && *vs)
|
||||
db_info->region = strdup(vs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
|
||||
@@ -147,6 +147,7 @@ typedef struct
|
||||
#define DB_EXTRACT_MD5 (1 << 4)
|
||||
#define DB_EXTRACT_SHA1 (1 << 5)
|
||||
#define DB_EXTRACT_ROM_NAME (1 << 6)
|
||||
#define DB_EXTRACT_REGION (1 << 7)
|
||||
|
||||
/* Preset used by the ROM scanner */
|
||||
#define DB_EXTRACT_SCAN_FIELDS \
|
||||
|
||||
@@ -34,7 +34,9 @@
|
||||
*
|
||||
* A title is read from the head of an XBE (little endian), the
|
||||
* optional headers of an XEX or the header of an STFS package (big
|
||||
* endian), each only as far as the title needs. */
|
||||
* endian), each only as far as the title needs: the ID, the place in
|
||||
* a set and the regions - an XBE's certificate holds them, an XEX's
|
||||
* execution info and security info. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -57,6 +59,8 @@
|
||||
#define XDVDFS_XEX_INFO_KEY 0x00040006u
|
||||
#define XDVDFS_XEX_INFO 24
|
||||
#define XDVDFS_STFS_FIELDS 0x360 /* title, platform, type, disc, discs */
|
||||
#define XDVDFS_XBE_REGION 0xA0 /* in the certificate */
|
||||
#define XDVDFS_XEX_REGION 0x178 /* in the security info */
|
||||
|
||||
/* Where whole disc images keep the game partition, after a trimmed
|
||||
* XISO's start: XGD1, XGD3, XGD2, and two seen in development images */
|
||||
@@ -78,6 +82,8 @@ static const uint32_t xdvdfs_parts[] = {
|
||||
#define XDVDFS_TITLE_XEX_TABLE 4
|
||||
#define XDVDFS_TITLE_XEX_INFO 5
|
||||
#define XDVDFS_TITLE_STFS 6
|
||||
#define XDVDFS_TITLE_XBE_REGION 7
|
||||
#define XDVDFS_TITLE_XEX_REGION 8
|
||||
|
||||
struct xdvdfs
|
||||
{
|
||||
@@ -86,6 +92,7 @@ struct xdvdfs
|
||||
uint64_t dir; /* the directory searched, in the image */
|
||||
uint64_t root;
|
||||
xdvdfs_extent_t file; /* what the lookup found, or the executable */
|
||||
xdvdfs_title_t title; /* the title read so far */
|
||||
const char *path; /* the lookup's next component */
|
||||
size_t got; /* octets of the range in hand */
|
||||
size_t cap; /* octets of it asked for */
|
||||
@@ -97,6 +104,7 @@ struct xdvdfs
|
||||
uint32_t xex_head; /* the XEX's header size */
|
||||
uint32_t xex_count; /* its optional headers */
|
||||
uint32_t xex_index; /* the next of them to look at */
|
||||
uint32_t xex_sec; /* its security info */
|
||||
int op;
|
||||
int stage; /* the title read's, or the partition probed */
|
||||
uint8_t buf[XDVDFS_BUF]; /* an entry, or a run of an executable */
|
||||
@@ -129,6 +137,8 @@ xdvdfs_t *xdvdfs_new(uint64_t image_size)
|
||||
x->xex_head = 0;
|
||||
x->xex_count = 0;
|
||||
x->xex_index = 0;
|
||||
x->xex_sec = 0;
|
||||
memset(&x->title, 0, sizeof(x->title));
|
||||
x->disc = false;
|
||||
x->xbe = false;
|
||||
x->op = XDVDFS_OP_OPEN;
|
||||
@@ -450,6 +460,7 @@ static int xdvdfs_title_run(xdvdfs_t *x, xdvdfs_request_t *req,
|
||||
else if (!memcmp(x->buf, "XEX2", 4) || !memcmp(x->buf, "XEX1", 4))
|
||||
{
|
||||
x->xex_head = xdvdfs_be32(x->buf + 8);
|
||||
x->xex_sec = xdvdfs_be32(x->buf + 16);
|
||||
x->xex_count = xdvdfs_be32(x->buf + 20);
|
||||
x->xex_index = 0;
|
||||
if ( x->xex_head < XDVDFS_EXE_HEAD
|
||||
@@ -473,17 +484,33 @@ static int xdvdfs_title_run(xdvdfs_t *x, xdvdfs_request_t *req,
|
||||
i = xdvdfs_le32(x->buf + 0x14); /* certificate */
|
||||
if (i < v || i - v > x->file.size || x->file.size - (i - v) < 12)
|
||||
return XDVDFS_ERROR_DATA;
|
||||
x->exe_at = i - v + 8;
|
||||
x->exe_at = i - v;
|
||||
x->stage = XDVDFS_TITLE_XBE_ID;
|
||||
/* fall through */
|
||||
case XDVDFS_TITLE_XBE_ID:
|
||||
if (x->got < 4)
|
||||
return xdvdfs_exe_ask(x, req, x->exe_at, 4);
|
||||
x->got = 0;
|
||||
title->id = xdvdfs_le32(x->buf);
|
||||
title->platform = XDVDFS_XBOX;
|
||||
title->disc = 0;
|
||||
title->discs = 0;
|
||||
return xdvdfs_exe_ask(x, req, x->exe_at + 8, 4);
|
||||
x->got = 0;
|
||||
x->title.id = xdvdfs_le32(x->buf);
|
||||
x->title.platform = XDVDFS_XBOX;
|
||||
x->title.disc = 0;
|
||||
x->title.discs = 0;
|
||||
x->title.regions = 0;
|
||||
x->stage = XDVDFS_TITLE_XBE_REGION;
|
||||
/* fall through */
|
||||
case XDVDFS_TITLE_XBE_REGION:
|
||||
/* a certificate too short to hold one says no region */
|
||||
if (x->file.size - x->exe_at >= XDVDFS_XBE_REGION + 4)
|
||||
{
|
||||
if (x->got < 4)
|
||||
return xdvdfs_exe_ask(x, req,
|
||||
x->exe_at + XDVDFS_XBE_REGION, 4);
|
||||
x->got = 0;
|
||||
x->title.regions = (uint8_t)(x->buf[0]
|
||||
& (XDVDFS_REGION_NA | XDVDFS_REGION_JAPAN
|
||||
| XDVDFS_REGION_REST));
|
||||
}
|
||||
*title = x->title;
|
||||
return XDVDFS_OK;
|
||||
case XDVDFS_TITLE_XEX_TABLE:
|
||||
/* the optional headers, as many at a time as the buffer
|
||||
@@ -514,20 +541,42 @@ static int xdvdfs_title_run(xdvdfs_t *x, xdvdfs_request_t *req,
|
||||
case XDVDFS_TITLE_XEX_INFO:
|
||||
if (x->got < XDVDFS_XEX_INFO)
|
||||
return xdvdfs_exe_ask(x, req, x->exe_at, XDVDFS_XEX_INFO);
|
||||
x->got = 0;
|
||||
title->id = xdvdfs_be32(x->buf + 12);
|
||||
title->platform = XDVDFS_XBOX360;
|
||||
title->disc = x->buf[18];
|
||||
title->discs = x->buf[19];
|
||||
x->got = 0;
|
||||
x->title.id = xdvdfs_be32(x->buf + 12);
|
||||
x->title.platform = XDVDFS_XBOX360;
|
||||
x->title.disc = x->buf[18];
|
||||
x->title.discs = x->buf[19];
|
||||
x->title.regions = 0;
|
||||
x->stage = XDVDFS_TITLE_XEX_REGION;
|
||||
/* fall through */
|
||||
case XDVDFS_TITLE_XEX_REGION:
|
||||
/* security info the header does not hold says no region */
|
||||
if ( x->xex_sec <= x->xex_head
|
||||
&& x->xex_head - x->xex_sec >= XDVDFS_XEX_REGION + 4)
|
||||
{
|
||||
if (x->got < 4)
|
||||
return xdvdfs_exe_ask(x, req,
|
||||
x->xex_sec + XDVDFS_XEX_REGION, 4);
|
||||
x->got = 0;
|
||||
/* NTSC-U, NTSC-J and PAL, a byte each, low to high */
|
||||
v = xdvdfs_be32(x->buf);
|
||||
x->title.regions = (uint8_t)(
|
||||
((v & 0x000000FFu) ? XDVDFS_REGION_NA : 0)
|
||||
| ((v & 0x0000FF00u) ? XDVDFS_REGION_JAPAN : 0)
|
||||
| ((v & 0x00FF0000u) ? XDVDFS_REGION_REST : 0));
|
||||
}
|
||||
*title = x->title;
|
||||
return XDVDFS_OK;
|
||||
case XDVDFS_TITLE_STFS:
|
||||
if (x->got < 8)
|
||||
return xdvdfs_exe_ask(x, req, XDVDFS_STFS_FIELDS, 8);
|
||||
x->got = 0;
|
||||
title->id = xdvdfs_be32(x->buf);
|
||||
title->platform = XDVDFS_XBOX360;
|
||||
title->disc = x->buf[6];
|
||||
title->discs = x->buf[7];
|
||||
x->got = 0;
|
||||
x->title.id = xdvdfs_be32(x->buf);
|
||||
x->title.platform = XDVDFS_XBOX360;
|
||||
x->title.disc = x->buf[6];
|
||||
x->title.discs = x->buf[7];
|
||||
x->title.regions = 0;
|
||||
*title = x->title;
|
||||
return XDVDFS_OK;
|
||||
default:
|
||||
return XDVDFS_ERROR_STATE;
|
||||
|
||||
@@ -64,6 +64,11 @@ RETRO_BEGIN_DECLS
|
||||
#define XDVDFS_XBOX 1
|
||||
#define XDVDFS_XBOX360 2
|
||||
|
||||
/* the regions a title is for */
|
||||
#define XDVDFS_REGION_NA 1 /* North America */
|
||||
#define XDVDFS_REGION_JAPAN 2 /* Japan and Asia */
|
||||
#define XDVDFS_REGION_REST 4 /* the rest of the world */
|
||||
|
||||
typedef struct xdvdfs xdvdfs_t;
|
||||
|
||||
/**
|
||||
@@ -92,6 +97,7 @@ typedef struct xdvdfs_title
|
||||
uint8_t platform; /* XDVDFS_XBOX or XDVDFS_XBOX360 */
|
||||
uint8_t disc; /* of the set, from 1; 0 when not said */
|
||||
uint8_t discs; /* in the set; 0 when not said */
|
||||
uint8_t regions; /* XDVDFS_REGION_*; 0 when not said */
|
||||
} xdvdfs_title_t;
|
||||
|
||||
/* @image_size: the image's size, so a partition past its end is not
|
||||
|
||||
@@ -144,27 +144,39 @@ static void entry(image_t *im, uint64_t dir, uint32_t off, uint16_t l,
|
||||
}
|
||||
|
||||
/* An XEX at @at: @n optional headers, the execution info's last,
|
||||
* with @title, disc @disc of @discs */
|
||||
static void put_xex(image_t *im, uint64_t at, uint32_t n, uint32_t title,
|
||||
uint8_t disc, uint8_t discs)
|
||||
* with @title, disc @disc of @discs, then the security info with the
|
||||
* region word @region, or none when @region is 0 */
|
||||
static void put_xex_region(image_t *im, uint64_t at, uint32_t n,
|
||||
uint32_t title, uint8_t disc, uint8_t discs, uint32_t region)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t head = 24 + n * 8 + 32;
|
||||
uint32_t info = 24 + n * 8;
|
||||
uint32_t sec = info + 24;
|
||||
uint32_t head = region ? sec + 0x178 + 4 : sec + 8;
|
||||
uint8_t d[2];
|
||||
put(im, at, "XEX2", 4);
|
||||
put_be32(im, at + 8, head);
|
||||
put_be32(im, at + 16, sec);
|
||||
put_be32(im, at + 20, n);
|
||||
if (region)
|
||||
put_be32(im, at + sec + 0x178, region);
|
||||
for (i = 0; i + 1 < n; i++)
|
||||
{
|
||||
put_be32(im, at + 24 + i * 8, 0x00010000u + i);
|
||||
put_be32(im, at + 24 + i * 8 + 4, 0x11111111u);
|
||||
}
|
||||
put_be32(im, at + 24 + i * 8, 0x00040006u);
|
||||
put_be32(im, at + 24 + i * 8 + 4, head - 24);
|
||||
put_be32(im, at + head - 24 + 12, title);
|
||||
put_be32(im, at + 24 + i * 8 + 4, info);
|
||||
put_be32(im, at + info + 12, title);
|
||||
d[0] = disc;
|
||||
d[1] = discs;
|
||||
put(im, at + head - 24 + 18, d, 2);
|
||||
put(im, at + info + 18, d, 2);
|
||||
}
|
||||
|
||||
static void put_xex(image_t *im, uint64_t at, uint32_t n, uint32_t title,
|
||||
uint8_t disc, uint8_t discs)
|
||||
{
|
||||
put_xex_region(im, at, n, title, disc, discs, 0);
|
||||
}
|
||||
|
||||
/* The disc: a game partition at @base holding
|
||||
@@ -197,13 +209,14 @@ static void make_disc_for(image_t *im, uint64_t base, uint64_t size,
|
||||
|
||||
if (x360)
|
||||
{
|
||||
put_xex(im, xbe, 3, title, 1, 2);
|
||||
put_xex_region(im, xbe, 3, title, 1, 2, 0x00FF00FFu);
|
||||
return;
|
||||
}
|
||||
put(im, xbe, "XBEH", 4);
|
||||
put32(im, xbe + 0x104, 0x10000);
|
||||
put32(im, xbe + 0x118, 0x10000 + 0x178);
|
||||
put32(im, xbe + 0x178 + 8, title);
|
||||
put32(im, xbe + 0x178 + 0xA0, 0x80000001u);
|
||||
}
|
||||
|
||||
static void make_disc(image_t *im, uint64_t base, uint64_t size,
|
||||
@@ -281,7 +294,13 @@ static void disc_lane(uint64_t base, uint64_t size, const char *what)
|
||||
* default.xbe found at the root entry, then the XBE's head, its
|
||||
* base and certificate addresses, the ID */
|
||||
sprintf(name, "%s: title reads only what it needs", what);
|
||||
check(pass || im.asked == 25 + 25 + 25 + 24 + 24 + 4, name);
|
||||
check(pass || im.asked == 25 + 25 + 25 + 24 + 24 + 4 + 4, name);
|
||||
{
|
||||
xdvdfs_title_t t;
|
||||
sprintf(name, "%s: regions of default.xbe", what);
|
||||
check( title_of(&im, x, NULL, &t) == XDVDFS_OK
|
||||
&& t.regions == XDVDFS_REGION_NA, name);
|
||||
}
|
||||
|
||||
sprintf(name, "%s: nested lookup", what);
|
||||
check( lookup(&im, x, "\\MEDIA/Intro.WMV", &ext) == XDVDFS_OK
|
||||
@@ -322,10 +341,12 @@ static void x360_lanes(void)
|
||||
im.asked = 0;
|
||||
check( title_of(&im, x, NULL, &t) == XDVDFS_OK
|
||||
&& t.id == 0x41560817u && t.platform == XDVDFS_XBOX360
|
||||
&& t.disc == 1 && t.discs == 2, "360 XGD2: title of default.xex");
|
||||
&& t.disc == 1 && t.discs == 2
|
||||
&& t.regions == (XDVDFS_REGION_NA | XDVDFS_REGION_REST),
|
||||
"360 XGD2: title of default.xex");
|
||||
/* default.xex at the root entry, the XEX's head, its three
|
||||
* optional headers, the execution info */
|
||||
check(pass || im.asked == 25 + 24 + 3 * 8 + 24,
|
||||
check(pass || im.asked == 25 + 24 + 3 * 8 + 24 + 4,
|
||||
"360 XGD2: title reads only what it needs");
|
||||
xdvdfs_free(x);
|
||||
image_free(&im);
|
||||
@@ -340,8 +361,8 @@ static void x360_lanes(void)
|
||||
ext.size = 0x10000;
|
||||
im.asked = 0;
|
||||
check( title_of(&im, x, &ext, &t) == XDVDFS_OK
|
||||
&& t.id == 0x4D5307E6u && t.platform == XDVDFS_XBOX360,
|
||||
"XEX on its own, many optional headers");
|
||||
&& t.id == 0x4D5307E6u && t.platform == XDVDFS_XBOX360
|
||||
&& !t.regions, "XEX on its own, many optional headers");
|
||||
check(im.asked == 24 + 40 * 8 + 24, "XEX on its own: headers once each");
|
||||
xdvdfs_free(x);
|
||||
image_free(&im);
|
||||
@@ -377,6 +398,19 @@ static void x360_lanes(void)
|
||||
xdvdfs_free(x);
|
||||
image_free(&im);
|
||||
|
||||
/* a certificate too short to hold the regions says none, and the
|
||||
* read stays inside the XBE */
|
||||
make_disc(&im, 0, 0x100000, 0x4D530004u);
|
||||
entry(&im, 33 * SECTOR, 0, 7, 13, 50, 0x178 + 0x40, 0x20, "default.xbe");
|
||||
open_image(&im, &x);
|
||||
im.asked = 0;
|
||||
check( title_of(&im, x, NULL, &t) == XDVDFS_OK
|
||||
&& t.id == 0x4D530004u && !t.regions
|
||||
&& im.asked == 25 + 25 + 25 + 24 + 24 + 4,
|
||||
"short certificate: no regions");
|
||||
xdvdfs_free(x);
|
||||
image_free(&im);
|
||||
|
||||
/* an empty root: one sector of padding */
|
||||
make_disc(&im, 0, 0x100000, 1);
|
||||
{
|
||||
|
||||
@@ -1269,8 +1269,10 @@ typedef struct
|
||||
|
||||
/* One database holding @n records; a NULL serial or rom_name, or
|
||||
* unsized, leaves the key out. */
|
||||
static int write_db_set(const char *path, const db_rec_t *recs,
|
||||
unsigned n)
|
||||
/* As write_db_set(), each record @i given the region @regions[i] when
|
||||
* that is not NULL */
|
||||
static int write_db_set_regions(const char *path, const db_rec_t *recs,
|
||||
const char *const *regions, unsigned n)
|
||||
{
|
||||
buf_t body, meta;
|
||||
FILE *f;
|
||||
@@ -1285,8 +1287,14 @@ static int write_db_set(const char *path, const db_rec_t *recs,
|
||||
for (r = 0; r < n; r++)
|
||||
{
|
||||
bfixmap(&body, 2 + (recs[r].serial ? 1 : 0)
|
||||
+ (recs[r].rom_name ? 1 : 0) + (recs[r].unsized ? 0 : 1));
|
||||
+ (recs[r].rom_name ? 1 : 0) + (recs[r].unsized ? 0 : 1)
|
||||
+ (regions && regions[r] ? 1 : 0));
|
||||
bfixstr(&body, "name"); bfixstr(&body, recs[r].name);
|
||||
if (regions && regions[r])
|
||||
{
|
||||
bfixstr(&body, "region");
|
||||
bfixstr(&body, regions[r]);
|
||||
}
|
||||
if (recs[r].rom_name)
|
||||
{
|
||||
bfixstr(&body, "rom_name");
|
||||
@@ -1334,6 +1342,12 @@ static int write_db_set(const char *path, const db_rec_t *recs,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_db_set(const char *path, const db_rec_t *recs,
|
||||
unsigned n)
|
||||
{
|
||||
return write_db_set_regions(path, recs, NULL, n);
|
||||
}
|
||||
|
||||
/* One database holding a single named record. */
|
||||
static int write_db(const char *path, const char *game, uint32_t crc,
|
||||
uint32_t size)
|
||||
@@ -2348,8 +2362,10 @@ static void xbox_xex(uint8_t *x, uint32_t title, uint8_t disc,
|
||||
uint8_t discs)
|
||||
{
|
||||
memcpy(x, "XEX2", 4);
|
||||
xbox_put_be32(x + 8, 0x60);
|
||||
xbox_put_be32(x + 8, 0x200);
|
||||
xbox_put_be32(x + 16, 0x60);
|
||||
xbox_put_be32(x + 20, 1);
|
||||
xbox_put_be32(x + 0x60 + 0x178, 0x000000FFu);
|
||||
xbox_put_be32(x + 24, 0x00040006u);
|
||||
xbox_put_be32(x + 28, 0x48);
|
||||
xbox_put_be32(x + 0x48 + 12, title);
|
||||
@@ -2398,6 +2414,7 @@ static int write_xbox_disc(const char *path, uint32_t base, uint32_t size,
|
||||
xbox_put32(x + 0x104, 0x10000);
|
||||
xbox_put32(x + 0x118, 0x10000 + 0x178);
|
||||
xbox_put32(x + 0x178 + 8, title);
|
||||
xbox_put32(x + 0x178 + 0xA0, 1);
|
||||
}
|
||||
|
||||
if (!(f = fopen(path, "wb")))
|
||||
@@ -2433,10 +2450,15 @@ static void xbox_lane(const char *root, const char *pl_dir,
|
||||
{
|
||||
/* sizes no trimmed image comes near, as Redump's do */
|
||||
static const db_rec_t xbox_recs[] = {
|
||||
/* an extra of another region's release, ahead of the game */
|
||||
{ "Halo Extras", "MS-004", 0x3B0E0004u, 0xF0000000u },
|
||||
{ "Halo The Game", "MS-004", 0x3B0E0001u, 0xF0000000u },
|
||||
{ "Trim The Game", "MS-012", 0x3B0E0002u, 0xF0000000u },
|
||||
{ "Whole The Game", "EA-013", 0x3B0E0003u, 0xF0000000u }
|
||||
};
|
||||
static const char *const xbox_regions[] = {
|
||||
"Japan", "USA, Europe", NULL, NULL
|
||||
};
|
||||
char *xbox_db = (char*)malloc(512);
|
||||
char *xbox_in = (char*)malloc(512);
|
||||
char *p = (char*)malloc(1024);
|
||||
@@ -2454,7 +2476,8 @@ static void xbox_lane(const char *root, const char *pl_dir,
|
||||
path_mkdir(xbox_in);
|
||||
|
||||
sprintf(p, "%s/Test Xbox.rdb", xbox_db);
|
||||
if (!write_db_set(p, xbox_recs, ARRAY_SIZE(xbox_recs)))
|
||||
if (!write_db_set_regions(p, xbox_recs, xbox_regions,
|
||||
ARRAY_SIZE(xbox_recs)))
|
||||
{ check(0, "fixture", "could not write database"); goto end; }
|
||||
|
||||
sprintf(p, "%s/60_halo.xiso", xbox_in);
|
||||
@@ -2484,6 +2507,8 @@ static void xbox_lane(const char *root, const char *pl_dir,
|
||||
check( file_contains(p, "60_halo.xiso")
|
||||
&& file_contains(p, "Halo The Game"),
|
||||
"XISO matches by its XBE title", "60_halo.xiso");
|
||||
check(!file_contains(p, "Halo Extras"),
|
||||
"the XBE's regions pick the release", "not the extra");
|
||||
check( file_contains(p, "61_trim.iso")
|
||||
&& file_contains(p, "Trim The Game"),
|
||||
"trimmed .iso not ruled out by size", "61_trim.iso");
|
||||
@@ -2576,11 +2601,16 @@ static void x360_lane(const char *root, const char *pl_dir,
|
||||
{
|
||||
static const db_rec_t x360_recs[] = {
|
||||
{ "Set The Game (Disc 1)", "FO-2046-0", 0x3B360001u, 0xF0000000u },
|
||||
{ "Set The Game (Disc 2) (Japan)", "FO-2046-1", 0x3B360007u,
|
||||
0xF0000000u },
|
||||
{ "Set The Game (Disc 2)", "FO-2046-1", 0x3B360002u, 0xF0000000u },
|
||||
{ "Exe The Game", "AV-2071", 0x3B360003u, 0xF0000000u },
|
||||
{ "Arcade The Game", "XA-2209", 0x3B360004u, 0xF0000000u },
|
||||
{ "Zar The Game", "SE-2142", 0x3B360006u, 0xF0000000u }
|
||||
};
|
||||
static const char *const x360_regions[] = {
|
||||
NULL, "Japan", "USA", NULL, NULL, NULL
|
||||
};
|
||||
char *x_db = (char*)malloc(512);
|
||||
char *x_in = (char*)malloc(512);
|
||||
char *p = (char*)malloc(1024);
|
||||
@@ -2599,7 +2629,8 @@ static void x360_lane(const char *root, const char *pl_dir,
|
||||
path_mkdir(x_in);
|
||||
|
||||
sprintf(p, "%s/Test Xbox 360.rdb", x_db);
|
||||
if (!write_db_set(p, x360_recs, ARRAY_SIZE(x360_recs)))
|
||||
if (!write_db_set_regions(p, x360_recs, x360_regions,
|
||||
ARRAY_SIZE(x360_recs)))
|
||||
{ check(0, "fixture", "could not write database"); goto end; }
|
||||
sprintf(p, "%s/Test Atari.rdb", x_db);
|
||||
if (!write_db(p, "Atari The Game", 0x3B360005u, 0x1000))
|
||||
@@ -2644,6 +2675,8 @@ static void x360_lane(const char *root, const char *pl_dir,
|
||||
&& file_contains(p, "Set The Game (Disc 2)")
|
||||
&& !file_contains(p, "Set The Game (Disc 1)"),
|
||||
"360 disc matches its place in the set", "70_disc2.iso");
|
||||
check(!file_contains(p, "Set The Game (Disc 2) (Japan)"),
|
||||
"the XEX's regions pick the release", "not the Japanese one");
|
||||
check( file_contains(p, "71_game.xex")
|
||||
&& file_contains(p, "Exe The Game"),
|
||||
"XEX matches by its title", "71_game.xex");
|
||||
|
||||
+74
-7
@@ -24,6 +24,7 @@
|
||||
#include <memory/mem_stats.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <file/file_path.h>
|
||||
#include <formats/xdvdfs.h>
|
||||
#include <formats/logiqx_dat.h>
|
||||
#include <formats/rm3u.h>
|
||||
#include <formats/rm3u_stream.h>
|
||||
@@ -346,6 +347,9 @@ typedef struct database_state_handle
|
||||
uint32_t crc_acc;
|
||||
uint8_t crc_dest; /* enum db_crc_dest */
|
||||
uint8_t crc_fail; /* enum db_crc_fail */
|
||||
/* The XDVDFS_REGION_* an Xbox title's executable says it is for,
|
||||
* or 0: picks among the releases its serial is shared by. */
|
||||
uint8_t regions;
|
||||
bool crc_done;
|
||||
/* The index build in flight, run by task_database_index_step()
|
||||
* until index_ready, then taken by the lookup that began it. */
|
||||
@@ -2126,6 +2130,7 @@ static int task_database_iterate_playlist(
|
||||
database_state_handle_t *db_state,
|
||||
database_info_handle_t *db, const char *name)
|
||||
{
|
||||
db_state->regions = 0;
|
||||
switch (extension_to_file_type(path_get_extension(name)))
|
||||
{
|
||||
case FILE_TYPE_COMPRESSED:
|
||||
@@ -2186,17 +2191,17 @@ static int task_database_iterate_playlist(
|
||||
* so other discs read nothing more for it. Its size says
|
||||
* nothing: an XISO is trimmed. */
|
||||
if ( !intfstream_file_get_serial(name, 0, INT64_MAX, db_state->serial, sizeof(db_state->serial),&db_state->size)
|
||||
&& task_database_xbox_get_serial(name, true, db_state->serial, sizeof(db_state->serial),&db_state->size))
|
||||
&& task_database_xbox_get_serial(name, true, db_state->serial, sizeof(db_state->serial),&db_state->size,&db_state->regions))
|
||||
db->type = DATABASE_TYPE_SERIAL_LOOKUP;
|
||||
break;
|
||||
case FILE_TYPE_XISO:
|
||||
db_state->serial[0] = '\0';
|
||||
task_database_xbox_get_serial(name, true, db_state->serial, sizeof(db_state->serial),&db_state->size);
|
||||
task_database_xbox_get_serial(name, true, db_state->serial, sizeof(db_state->serial),&db_state->size,&db_state->regions);
|
||||
db->type = DATABASE_TYPE_SERIAL_LOOKUP;
|
||||
break;
|
||||
case FILE_TYPE_ZAR:
|
||||
db_state->serial[0] = '\0';
|
||||
task_database_zar_get_serial(name, db_state->serial, sizeof(db_state->serial),&db_state->size);
|
||||
task_database_zar_get_serial(name, db_state->serial, sizeof(db_state->serial),&db_state->size,&db_state->regions);
|
||||
db->type = DATABASE_TYPE_SERIAL_LOOKUP;
|
||||
break;
|
||||
/* An Xbox 360 executable or package; anything else by these names
|
||||
@@ -2204,7 +2209,7 @@ static int task_database_iterate_playlist(
|
||||
case FILE_TYPE_XEX:
|
||||
case FILE_TYPE_XCP:
|
||||
db_state->serial[0] = '\0';
|
||||
if (task_database_xbox_get_serial(name, false, db_state->serial, sizeof(db_state->serial),&db_state->size))
|
||||
if (task_database_xbox_get_serial(name, false, db_state->serial, sizeof(db_state->serial),&db_state->size,&db_state->regions))
|
||||
{
|
||||
db->type = DATABASE_TYPE_SERIAL_LOOKUP;
|
||||
break;
|
||||
@@ -2215,7 +2220,7 @@ static int task_database_iterate_playlist(
|
||||
TASK_DATABASE_CRC_FILE, DB_CRC_DEST_FILE, DB_CRC_FAIL_ERROR);
|
||||
case FILE_TYPE_CHD:
|
||||
db_state->serial[0] = '\0';
|
||||
if (task_database_chd_get_serial(name, db_state->serial, sizeof(db_state->serial),&db_state->size))
|
||||
if (task_database_chd_get_serial(name, db_state->serial, sizeof(db_state->serial),&db_state->size,&db_state->regions))
|
||||
db->type = DATABASE_TYPE_SERIAL_LOOKUP;
|
||||
else
|
||||
{
|
||||
@@ -2366,8 +2371,10 @@ static int database_info_list_iterate_new(
|
||||
database_info_list_free(db_state->info);
|
||||
free(db_state->info);
|
||||
}
|
||||
/* the records' regions only for a title that says its own */
|
||||
db_state->info = database_info_list_new_filtered(
|
||||
new_database, query, DB_EXTRACT_SCAN_FIELDS);
|
||||
new_database, query, DB_EXTRACT_SCAN_FIELDS
|
||||
| (db_state->regions ? DB_EXTRACT_REGION : 0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3573,6 +3580,44 @@ static bool task_database_check_serial_and_crc(
|
||||
string_starts_with(db_name, "Sega - Dreamcast"));
|
||||
}
|
||||
|
||||
/* The XDVDFS_REGION_* a Redump region list ("USA, Europe") names:
|
||||
* the Xbox's three, North America, Japan with the rest of Asia, and
|
||||
* the rest of the world */
|
||||
static uint8_t task_database_xbox_regions(const char *list)
|
||||
{
|
||||
static const char *const na[] = { "USA", "Canada", "Brazil" };
|
||||
static const char *const japan[] = {
|
||||
"Japan", "Asia", "Korea", "Taiwan", "China", "Hong Kong" };
|
||||
uint8_t regions = 0;
|
||||
|
||||
while (list && *list)
|
||||
{
|
||||
const char *end;
|
||||
size_t len, i;
|
||||
uint8_t r = XDVDFS_REGION_REST;
|
||||
|
||||
while (*list == ' ' || *list == ',')
|
||||
list++;
|
||||
for (end = list; *end && *end != ','; end++) { }
|
||||
len = (size_t)(end - list);
|
||||
while (len && list[len - 1] == ' ')
|
||||
len--;
|
||||
if (!len)
|
||||
break;
|
||||
if (len == 5 && !strncmp(list, "World", 5))
|
||||
r = XDVDFS_REGION_NA | XDVDFS_REGION_JAPAN | XDVDFS_REGION_REST;
|
||||
for (i = 0; i < ARRAY_SIZE(na); i++)
|
||||
if (strlen(na[i]) == len && !strncmp(list, na[i], len))
|
||||
r = XDVDFS_REGION_NA;
|
||||
for (i = 0; i < ARRAY_SIZE(japan); i++)
|
||||
if (strlen(japan[i]) == len && !strncmp(list, japan[i], len))
|
||||
r = XDVDFS_REGION_JAPAN;
|
||||
regions |= r;
|
||||
list = end;
|
||||
}
|
||||
return regions;
|
||||
}
|
||||
|
||||
static int task_database_iterate_serial_lookup(
|
||||
manual_scan_handle_t *_db,
|
||||
database_state_handle_t *db_state,
|
||||
@@ -3710,7 +3755,8 @@ static int task_database_iterate_serial_lookup(
|
||||
if (db_state->serial_index[db_state->list_index])
|
||||
hits = database_info_list_new_serial(
|
||||
db_state->serial_index[db_state->list_index], rdb,
|
||||
db_state->serial, DB_EXTRACT_SCAN_FIELDS);
|
||||
db_state->serial, DB_EXTRACT_SCAN_FIELDS
|
||||
| (db_state->regions ? DB_EXTRACT_REGION : 0));
|
||||
}
|
||||
|
||||
if (hits)
|
||||
@@ -3785,6 +3831,27 @@ serial_query_done:
|
||||
return database_info_list_iterate_next(db_state);
|
||||
}
|
||||
|
||||
/* An Xbox serial is shared by a title's releases, and by the
|
||||
* extras that came with them: the regions its executable says it is
|
||||
* for pick the first that fits, and when none does the first of them
|
||||
* stands in, as the loop below finds it. */
|
||||
if (db_state->info && db_state->regions && !db_state->entry_index)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < db_state->info->count; i++)
|
||||
{
|
||||
const database_info_t *rec = &db_state->info->list[i];
|
||||
if ( rec->serial
|
||||
&& string_is_equal(db_state->serial, rec->serial)
|
||||
&& (task_database_xbox_regions(rec->region)
|
||||
& db_state->regions))
|
||||
{
|
||||
db_state->entry_index = i;
|
||||
return database_info_list_iterate_found_match(_db,
|
||||
db_state, db, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (db_state->info)
|
||||
{
|
||||
/* A 3DS product code is shared by a title's revisions and
|
||||
|
||||
+14
-13
@@ -2215,10 +2215,10 @@ bool is_chd_file_cdi(const char *path)
|
||||
}
|
||||
|
||||
static size_t detect_xbox_game(intfstream_t *fd, uint64_t pos, bool disc,
|
||||
char *s, size_t len);
|
||||
char *s, size_t len, uint8_t *regions);
|
||||
|
||||
int task_database_chd_get_serial(const char *name, char *serial,
|
||||
size_t len, uint64_t *filesize)
|
||||
size_t len, uint64_t *filesize, uint8_t *regions)
|
||||
{
|
||||
int result;
|
||||
/* CD-i discs store data in AUDIO-labeled tracks, so we must
|
||||
@@ -2238,7 +2238,8 @@ int task_database_chd_get_serial(const char *name, char *serial,
|
||||
/* An Xbox DVD is looked for only once nothing else has been found,
|
||||
* so other discs read nothing more for it */
|
||||
if (!result)
|
||||
result = detect_xbox_game(fd, ~(uint64_t)0, true, serial, len) != 0;
|
||||
result = detect_xbox_game(fd, ~(uint64_t)0, true, serial, len,
|
||||
regions) != 0;
|
||||
intfstream_close(fd);
|
||||
free(fd);
|
||||
return result;
|
||||
@@ -2375,10 +2376,10 @@ static size_t xbox_title_serial(const xdvdfs_title_t *t, char *s,
|
||||
}
|
||||
|
||||
/* An Xbox or Xbox 360 serial from the title of the executable @fd
|
||||
* holds, or with @disc of the one the disc image boots. @pos is where
|
||||
* the stream stands. */
|
||||
* holds, or with @disc of the one the disc image boots, and the
|
||||
* regions it is for. @pos is where the stream stands. */
|
||||
static size_t detect_xbox_game(intfstream_t *fd, uint64_t pos, bool disc,
|
||||
char *s, size_t len)
|
||||
char *s, size_t len, uint8_t *regions)
|
||||
{
|
||||
task_database_src_t src;
|
||||
xdvdfs_request_t req;
|
||||
@@ -2405,15 +2406,15 @@ static size_t detect_xbox_game(intfstream_t *fd, uint64_t pos, bool disc,
|
||||
while ((err = xdvdfs_title_step(x, &req, &title)) == XDVDFS_PENDING)
|
||||
xdvdfs_feed(x, task_database_pull(&src, req.offset, req.dst,
|
||||
req.length));
|
||||
if (err == XDVDFS_OK)
|
||||
_len = xbox_title_serial(&title, s, len);
|
||||
if (err == XDVDFS_OK && (_len = xbox_title_serial(&title, s, len)))
|
||||
*regions = title.regions;
|
||||
}
|
||||
xdvdfs_free(x);
|
||||
return _len;
|
||||
}
|
||||
|
||||
int task_database_xbox_get_serial(const char *name, bool disc, char *s,
|
||||
size_t len, uint64_t *filesize)
|
||||
size_t len, uint64_t *filesize, uint8_t *regions)
|
||||
{
|
||||
size_t _len;
|
||||
/* The descriptor path: the few ranges the probe needs land where
|
||||
@@ -2425,7 +2426,7 @@ int task_database_xbox_get_serial(const char *name, bool disc, char *s,
|
||||
if (!fd)
|
||||
return 0;
|
||||
*filesize = (uint64_t)intfstream_get_size(fd);
|
||||
_len = detect_xbox_game(fd, 0, disc, s, len);
|
||||
_len = detect_xbox_game(fd, 0, disc, s, len, regions);
|
||||
intfstream_close(fd);
|
||||
free(fd);
|
||||
return _len != 0;
|
||||
@@ -2455,7 +2456,7 @@ static size_t task_database_zar_pull(task_database_zar_src_t *zs,
|
||||
}
|
||||
|
||||
int task_database_zar_get_serial(const char *name, char *s, size_t len,
|
||||
uint64_t *filesize)
|
||||
uint64_t *filesize, uint8_t *regions)
|
||||
{
|
||||
task_database_zar_src_t zs;
|
||||
zar_request_t req;
|
||||
@@ -2501,8 +2502,8 @@ int task_database_zar_get_serial(const char *name, char *s, size_t len,
|
||||
while ((err = xdvdfs_title_step(x, &xreq, &title)) == XDVDFS_PENDING)
|
||||
xdvdfs_feed(x, task_database_zar_pull(&zs, xreq.offset, xreq.dst,
|
||||
xreq.length));
|
||||
if (err == XDVDFS_OK)
|
||||
_len = xbox_title_serial(&title, s, len);
|
||||
if (err == XDVDFS_OK && (_len = xbox_title_serial(&title, s, len)))
|
||||
*regions = title.regions;
|
||||
|
||||
end:
|
||||
xdvdfs_free(x);
|
||||
|
||||
@@ -73,15 +73,17 @@ int task_database_3ds_get_serial(const char *name, char *s, size_t len,
|
||||
int task_database_ciso_get_serial(const char *name, char *s, size_t len,
|
||||
uint64_t *filesize);
|
||||
/* An Xbox or Xbox 360 serial for @name: with @disc that of the disc
|
||||
* image's game, otherwise that of the executable or package it is */
|
||||
* image's game, otherwise that of the executable or package it is.
|
||||
* @regions gets the XDVDFS_REGION_* the title says it is for, or 0. */
|
||||
int task_database_xbox_get_serial(const char *name, bool disc, char *s,
|
||||
size_t len, uint64_t *filesize);
|
||||
/* The Xbox 360 serial of the game a ZArchive holds */
|
||||
size_t len, uint64_t *filesize, uint8_t *regions);
|
||||
/* The Xbox 360 serial of the game a ZArchive holds, @regions as above */
|
||||
int task_database_zar_get_serial(const char *name, char *s, size_t len,
|
||||
uint64_t *filesize);
|
||||
uint64_t *filesize, uint8_t *regions);
|
||||
bool is_chd_file_cdi(const char *path);
|
||||
/* @regions as for task_database_xbox_get_serial(), for an Xbox disc */
|
||||
int task_database_chd_get_serial(const char *name, char *serial, size_t len,
|
||||
uint64_t *filesize);
|
||||
uint64_t *filesize, uint8_t *regions);
|
||||
|
||||
enum task_database_crc_kind
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user