[librpbase] RomFields: Added a new field type RFT_DIMENSIONS.

This field type holds up to 3 dimensions: width, height, depth.
If a dimension is 0, it's assumed to not exist.

[libromdata] Use RFT_DIMENSIONS for e.g. image sizes.

Updated all UI frontends to support RFT_DIMENSIONS.

This fixes issue #143: New RFT_DIMENSIONS type for e.g. width/height/depth
This commit is contained in:
David Korth 2018-06-03 17:29:25 -04:00
parent c89f3c1d5b
commit 5d42d17248
12 changed files with 281 additions and 80 deletions

View File

@ -1147,6 +1147,38 @@ rom_data_view_init_age_ratings(G_GNUC_UNUSED RomDataView *page, const RomFields:
return widget;
}
/**
* Initialize a Dimensions field.
* @param page RomDataView object.
* @param field RomFields::Field
* @return Display widget, or nullptr on error.
*/
static GtkWidget*
rom_data_view_init_dimensions(G_GNUC_UNUSED RomDataView *page, const RomFields::Field *field)
{
// Dimensions.
GtkWidget *widget = gtk_label_new(nullptr);
gtk_label_set_use_underline(GTK_LABEL(widget), false);
gtk_label_set_selectable(GTK_LABEL(widget), TRUE);
gtk_label_set_justify(GTK_LABEL(widget), GTK_JUSTIFY_LEFT);
gtk_widget_show(widget);
GTK_WIDGET_HALIGN_LEFT(widget);
// TODO: 'x' or '×'? Using 'x' for now.
const int *const dimensions = field->data.dimensions;
ostringstream oss;
oss << dimensions[0];
if (dimensions[1] > 0) {
oss << 'x' << dimensions[1];
if (dimensions[2] > 0) {
oss << 'x' << dimensions[2];
}
}
gtk_label_set_text(GTK_LABEL(widget), oss.str().c_str());
return widget;
}
static void
rom_data_view_update_display(RomDataView *page)
{
@ -1292,31 +1324,29 @@ rom_data_view_update_display(RomDataView *page)
case RomFields::RFT_INVALID:
// No data here.
break;
default:
// Unsupported right now.
assert(!"Unsupported RomFields::RomFieldsType.");
break;
case RomFields::RFT_STRING:
widget = rom_data_view_init_string(page, field);
break;
case RomFields::RFT_BITFIELD:
widget = rom_data_view_init_bitfield(page, field);
break;
case RomFields::RFT_LISTDATA:
separate_rows = !!(field->desc.list_data.flags & RomFields::RFT_LISTDATA_SEPARATE_ROW);
widget = rom_data_view_init_listdata(page, field);
break;
case RomFields::RFT_DATETIME:
widget = rom_data_view_init_datetime(page, field);
break;
case RomFields::RFT_AGE_RATINGS:
widget = rom_data_view_init_age_ratings(page, field);
break;
default:
// Unsupported right now.
assert(!"Unsupported RomFields::RomFieldsType.");
case RomFields::RFT_DIMENSIONS:
widget = rom_data_view_init_dimensions(page, field);
break;
}

View File

@ -141,6 +141,13 @@ class RomDataViewPrivate
*/
void initAgeRatings(QLabel *lblDesc, const RomFields::Field *field);
/**
* Initialize a Dimensions field.
* @param lblDesc Description label.
* @param field RomFields::Field
*/
void initDimensions(QLabel *lblDesc, const RomFields::Field *field);
/**
* Initialize the display widgets.
* If the widgets already exist, they will
@ -722,6 +729,39 @@ void RomDataViewPrivate::initAgeRatings(QLabel *lblDesc, const RomFields::Field
tabs[field->tabIdx].formLayout->addRow(lblDesc, lblAgeRatings);
}
/**
* Initialize a Dimensions field.
* @param lblDesc Description label.
* @param field RomFields::Field
*/
void RomDataViewPrivate::initDimensions(QLabel *lblDesc, const RomFields::Field *field)
{
// Dimensions.
Q_Q(RomDataView);
QLabel *lblDimensions = new QLabel(q);
lblDimensions->setAlignment(Qt::AlignLeft | Qt::AlignTop);
lblDimensions->setTextFormat(Qt::PlainText);
lblDimensions->setTextInteractionFlags(
Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
lblDimensions->setFocusPolicy(Qt::StrongFocus);
// TODO: 'x' or '×'? Using 'x' for now.
const int *const dimensions = field->data.dimensions;
QString str = QString::number(dimensions[0]);
if (dimensions[1] > 0) {
str.reserve(str.size() * 3);
str += QChar(L'x');
str += QString::number(dimensions[1]);
if (dimensions[2] > 0) {
str += QChar(L'x');
str += QString::number(dimensions[2]);
}
}
lblDimensions->setText(str);
tabs[field->tabIdx].formLayout->addRow(lblDesc, lblDimensions);
}
/**
* Initialize the display widgets.
* If the widgets already exist, they will
@ -840,32 +880,30 @@ void RomDataViewPrivate::initDisplayWidgets(void)
// No data here.
delete lblDesc;
break;
case RomFields::RFT_STRING:
initString(lblDesc, field);
break;
case RomFields::RFT_BITFIELD:
initBitfield(lblDesc, field);
break;
case RomFields::RFT_LISTDATA:
initListData(lblDesc, field);
break;
case RomFields::RFT_DATETIME:
initDateTime(lblDesc, field);
break;
case RomFields::RFT_AGE_RATINGS:
initAgeRatings(lblDesc, field);
break;
default:
// Unsupported right now.
assert(!"Unsupported RomFields::RomFieldsType.");
delete lblDesc;
break;
case RomFields::RFT_STRING:
initString(lblDesc, field);
break;
case RomFields::RFT_BITFIELD:
initBitfield(lblDesc, field);
break;
case RomFields::RFT_LISTDATA:
initListData(lblDesc, field);
break;
case RomFields::RFT_DATETIME:
initDateTime(lblDesc, field);
break;
case RomFields::RFT_AGE_RATINGS:
initAgeRatings(lblDesc, field);
break;
case RomFields::RFT_DIMENSIONS:
initDimensions(lblDesc, field);
break;
}
}

View File

@ -630,8 +630,8 @@ int NintendoBadge::loadFieldData(void)
// Mega badge size.
if (d->megaBadge) {
d->fields->addField_string(C_("NintendoBadge", "Mega Badge Size"),
rp_sprintf("%ux%u", prbs->mb_width, prbs->mb_height));
d->fields->addField_dimensions(C_("NintendoBadge", "Mega Badge Size"),
prbs->mb_width, prbs->mb_height);
}
// Title ID.

View File

@ -1076,13 +1076,9 @@ int DirectDrawSurface::loadFieldData(void)
d->fields->reserve(12); // Maximum of 12 fields.
// Texture size.
if (ddsHeader->dwFlags & DDSD_DEPTH) {
d->fields->addField_string(C_("DirectDrawSurface", "Texture Size"),
rp_sprintf("%ux%ux%u", ddsHeader->dwWidth, ddsHeader->dwHeight, ddsHeader->dwDepth));
} else {
d->fields->addField_string(C_("DirectDrawSurface", "Texture Size"),
rp_sprintf("%ux%u", ddsHeader->dwWidth, ddsHeader->dwHeight));
}
d->fields->addField_dimensions(C_("DirectDrawSurface", "Texture Size"),
ddsHeader->dwWidth, ddsHeader->dwHeight,
(ddsHeader->dwFlags & DDSD_DEPTH) ? ddsHeader->dwDepth : 0);
// Pitch (uncompressed)
// Linear size (compressed)

View File

@ -791,18 +791,9 @@ int KhronosKTX::loadFieldData(void)
d->fields->reserve(10); // Maximum of 10 fields.
// Texture size.
if (ktxHeader->pixelDepth > 0) {
d->fields->addField_string(C_("KhronosKTX", "Texture Size"),
rp_sprintf("%ux%ux%u", ktxHeader->pixelWidth,
ktxHeader->pixelHeight,
ktxHeader->pixelDepth));
} else if (ktxHeader->pixelHeight > 0) {
d->fields->addField_string(C_("KhronosKTX", "Texture Size"),
rp_sprintf("%ux%u", ktxHeader->pixelWidth,
ktxHeader->pixelHeight));
} else {
d->fields->addField_string_numeric(C_("KhronosKTX", "Texture Size"), ktxHeader->pixelWidth);
}
d->fields->addField_dimensions(C_("KhronosKTX", "Texture Size"),
ktxHeader->pixelWidth, ktxHeader->pixelHeight,
ktxHeader->pixelDepth);
// Endianness.
// TODO: Save big vs. little in the constructor instead of just "needs byteswapping"?

View File

@ -892,8 +892,8 @@ int SegaPVR::loadFieldData(void)
d->fields->reserve(4); // Maximum of 4 fields.
// Texture size.
d->fields->addField_string(C_("SegaPVR", "Texture Size"),
rp_sprintf("%ux%u", pvrHeader->width, pvrHeader->height));
d->fields->addField_dimensions(C_("SegaPVR", "Texture Size"),
pvrHeader->width, pvrHeader->height);
// Pixel format.
static const char *const pxfmt_tbl[SegaPVRPrivate::PVR_TYPE_MAX][8] = {

View File

@ -793,21 +793,14 @@ int ValveVTF::loadFieldData(void)
// Texture size.
// 7.2+ supports 3D textures.
int depth = 0;
if ((vtfHeader->version[0] > 7 || (vtfHeader->version[0] == 7 && vtfHeader->version[1] >= 2)) &&
vtfHeader->depth > 1)
{
d->fields->addField_string(C_("ValveVTF", "Texture Size"),
rp_sprintf("%ux%ux%u", vtfHeader->width,
vtfHeader->height,
vtfHeader->depth));
} else if (vtfHeader->height > 0) {
// TODO: >0 or >1?
d->fields->addField_string(C_("ValveVTF", "Texture Size"),
rp_sprintf("%ux%u", vtfHeader->width,
vtfHeader->height));
} else {
d->fields->addField_string_numeric(C_("ValveVTF", "Texture Size"), vtfHeader->width);
depth = vtfHeader->depth;
}
d->fields->addField_dimensions(C_("ValveVTF", "Texture Size"),
vtfHeader->width, vtfHeader->height, depth);
// Flags.
// TODO: Show "deprecated" flags for older versions.
@ -943,15 +936,9 @@ int ValveVTF::loadFieldData(void)
d->fields->addField_string(C_("ValveVTF", "Low-Res Image Format"),
dpgettext_expr(RP_I18N_DOMAIN, "ValveVTF|ImageFormat", img_format));
// Low-res image size.
if (vtfHeader->lowResImageHeight > 0) {
// TODO: >0 or >1?
d->fields->addField_string(C_("ValveVTF", "Low-Res Size"),
rp_sprintf("%ux%u", vtfHeader->lowResImageWidth,
vtfHeader->lowResImageHeight));
} else {
d->fields->addField_string_numeric(C_("ValveVTF", "Low-Res Size"),
vtfHeader->lowResImageWidth);
}
d->fields->addField_dimensions(C_("ValveVTF", "Low-Res Size"),
vtfHeader->lowResImageWidth,
vtfHeader->lowResImageHeight);
} else {
d->fields->addField_string(C_("ValveVTF", "Low-Res Image Format"),
rp_sprintf(C_("ValveVTF", "Unknown (%d)"), vtfHeader->highResImageFormat));

View File

@ -426,14 +426,8 @@ int ValveVTF3::loadFieldData(void)
// Texture size.
// TODO: 3D textures?
if (vtf3Header->height > 0) {
// TODO: >0 or >1?
d->fields->addField_string(C_("ValveVTF3", "Texture Size"),
rp_sprintf("%ux%u", vtf3Header->width,
vtf3Header->height));
} else {
d->fields->addField_string_numeric(C_("ValveVTF3", "Texture Size"), vtf3Header->width);
}
d->fields->addField_dimensions(C_("ValveVTF3", "Texture Size"),
vtf3Header->width, vtf3Header->height);
// Image format.
d->fields->addField_string(C_("ValveVTF3", "Image Format"),

View File

@ -155,6 +155,7 @@ void RomFieldsPrivate::delete_data(void)
switch (field.type) {
case RomFields::RFT_INVALID:
case RomFields::RFT_DATETIME:
case RomFields::RFT_DIMENSIONS:
// No data here.
break;
@ -275,6 +276,7 @@ void RomFields::detach(void)
field_new.desc.flags = 0;
field_new.data.generic = 0;
break;
case RFT_STRING:
field_new.desc.flags = field_old.desc.flags;
if (field_old.data.str) {
@ -319,6 +321,10 @@ void RomFields::detach(void)
case RFT_AGE_RATINGS:
field_new.data.age_ratings = field_old.data.age_ratings;
break;
case RFT_DIMENSIONS:
memcpy(field_new.data.dimensions, field_old.data.dimensions, sizeof(field_old.data.dimensions));
break;
default:
// ERROR!
assert(!"Unsupported RomFields::RomFieldsType.");
@ -831,6 +837,9 @@ int RomFields::addFields_romFields(const RomFields *other, int tabOffset)
? new age_ratings_t(*src->data.age_ratings)
: nullptr);
break;
case RFT_DIMENSIONS:
memcpy(field.data.dimensions, src->data.dimensions, sizeof(src->data.dimensions));
break;
default:
assert(!"Unsupported RomFields::RomFieldsType.");
@ -1167,4 +1176,34 @@ int RomFields::addField_ageRatings(const char *name, const age_ratings_t &age_ra
return idx;
}
/**
* Add image dimensions.
* @param name Field name.
* @param dimX X dimension.
* @param dimY Y dimension.
* @param dimZ Z dimension.
* @return Field index, or -1 on error.
*/
int RomFields::addField_dimensions(const char *name, int dimX, int dimY, int dimZ)
{
assert(name != nullptr);
if (!name)
return -1;
// RFT_DIMENSIONS
RP_D(RomFields);
int idx = static_cast<int>(d->fields.size());
d->fields.resize(idx+1);
Field &field = d->fields.at(idx);
field.name = name;
field.type = RFT_DIMENSIONS;
field.data.dimensions[0] = dimX;
field.data.dimensions[1] = dimY;
field.data.dimensions[2] = dimZ;
field.tabIdx = d->tabIdx;
field.isValid = true;
return idx;
}
}

View File

@ -44,6 +44,7 @@ class RomFields
RFT_LISTDATA, // ListData.
RFT_DATETIME, // Date/Time.
RFT_AGE_RATINGS, // Age ratings.
RFT_DIMENSIONS, // Image dimensions.
};
// String format flags. (RFT_STRING)
@ -195,6 +196,11 @@ class RomFields
// RFT_AGE_RATINGS
// See AgeRatingsCountry for field indexes.
const age_ratings_t *age_ratings;
// RFT_DIMENSIONS
// Up to three image dimensions.
// If a dimension is not present, set to 0.
int dimensions[3];
} data;
};
@ -478,6 +484,16 @@ class RomFields
* @return Field index, or -1 on error.
*/
int addField_ageRatings(const char *name, const age_ratings_t &age_ratings);
/**
* Add image dimensions.
* @param name Field name.
* @param dimX X dimension.
* @param dimY Y dimension.
* @param dimZ Z dimension.
* @return Field index, or -1 on error.
*/
int addField_dimensions(const char *name, int dimX, int dimY = 0, int dimZ = 0);
};
}

View File

@ -371,6 +371,30 @@ public:
}
};
class DimensionsField {
size_t width;
const RomFields::Field *romField;
public:
DimensionsField(size_t width, const RomFields::Field *romField) :width(width), romField(romField) {}
friend ostream& operator<<(ostream& os, const DimensionsField& field) {
auto romField = field.romField;
os << ColonPad(field.width, romField->name.c_str());
StreamStateSaver state(os);
// Convert the dimensions field to a string.
const int *const dimensions = romField->data.dimensions;
os << dimensions[0];
if (dimensions[1] > 0) {
os << 'x' << dimensions[1];
if (dimensions[2] > 0) {
os << 'x' << dimensions[2];
}
}
return os;
}
};
class FieldsOutput {
const RomFields& fields;
public:
@ -419,6 +443,10 @@ public:
os << AgeRatingsField(maxWidth, romField);
break;
}
case RomFields::RFT_DIMENSIONS: {
os << DimensionsField(maxWidth, romField);
break;
}
default: {
assert(!"Unknown RomFieldType");
os << ColonPad(maxWidth, romField->name.c_str()) << "NYI";
@ -635,6 +663,22 @@ public:
break;
}
case RomFields::RFT_DIMENSIONS: {
os << "{\"type\":\"DIMENSIONS\",\"desc\":{\"name\":" << JSONString(romField->name.c_str())
<< "},\"data\":";
const int *const dimensions = romField->data.dimensions;
os << "[\"w\":" << dimensions[0];
if (dimensions[1] > 0) {
os << ",\"h\":" << dimensions[1];
if (dimensions[2] > 0) {
os << ",\"d\":" << dimensions[2];
}
}
os << "]}";
break;
}
default: {
assert(!"Unknown RomFieldType");
os << "{\"type\":\"NYI\",\"desc\":{\"name\":" << JSONString(romField->name.c_str()) << "}}";

View File

@ -299,6 +299,21 @@ class RP_ShellPropSheetExt_Private
const POINT &pt_start, int idx, const SIZE &size,
const RomFields::Field *field);
/**
* Initialize a Dimensions field.
* This function internally calls initString().
* @param hDlg [in] Parent dialog window. (for dialog unit mapping)
* @param hWndTab [in] Tab window. (for the actual control)
* @param pt_start [in] Starting position, in pixels.
* @param idx [in] Field index.
* @param size [in] Width and height for a single line label.
* @param field [in] RomFields::Field
* @return Field height, in pixels.
*/
int initDimensions(HWND hDlg, HWND hWndTab,
const POINT &pt_start, int idx, const SIZE &size,
const RomFields::Field *field);
/**
* Initialize the bold font.
* @param hFont Base font.
@ -1436,6 +1451,45 @@ int RP_ShellPropSheetExt_Private::initAgeRatings(HWND hDlg, HWND hWndTab,
return initString(hDlg, hWndTab, pt_start, idx, size, field, U82W_s(str));
}
/**
* Initialize a Dimensions field.
* This function internally calls initString().
* @param hDlg [in] Parent dialog window. (for dialog unit mapping)
* @param hWndTab [in] Tab window. (for the actual control)
* @param pt_start [in] Starting position, in pixels.
* @param idx [in] Field index.
* @param size [in] Width and height for a single line label.
* @param field [in] RomFields::Field
* @return Field height, in pixels.
*/
int RP_ShellPropSheetExt_Private::initDimensions(HWND hDlg, HWND hWndTab,
const POINT &pt_start, int idx, const SIZE &size,
const RomFields::Field *field)
{
assert(hDlg != nullptr);
assert(hWndTab != nullptr);
assert(field != nullptr);
assert(field->type == RomFields::RFT_DIMENSIONS);
if (!hDlg || !hWndTab || !field)
return 0;
if (field->type != RomFields::RFT_DIMENSIONS)
return 0;
// TODO: 'x' or '×'? Using 'x' for now.
const int *const dimensions = field->data.dimensions;
wostringstream woss;
woss << dimensions[0];
if (dimensions[1] > 0) {
woss << L'x' << dimensions[1];
if (dimensions[2] > 0) {
woss << L'x' << dimensions[2];
}
}
// Initialize the string field.
return initString(hDlg, hWndTab, pt_start, idx, size, field, woss.str().c_str());
}
/**
* Initialize the bold font.
* @param hFont Base font.
@ -1849,6 +1903,18 @@ void RP_ShellPropSheetExt_Private::initDialog(HWND hDlg)
break;
}
case RomFields::RFT_DIMENSIONS: {
// Dimensions field.
SIZE size = {dlg_value_width, field_cy};
field_cy = initDimensions(hDlg, tab.hDlg, pt_start, idx, size, field);
if (field_cy == 0) {
// initDimensions() failed.
// Remove the description label.
DestroyWindow(hStatic);
}
break;
}
default:
// Unsupported data type.
assert(!"Unsupported RomFields::RomFieldsType.");