[libromdata] Wim_xml: Add special handling for unstaged images.

Unstaged images contain sets of components instead of a fully-installed
Windows system. These aren't as common as staged images.

The list of editions appears to be in the display description, though
I don't have an unstaged image to verify this. If the display description
contains "EDITIONS:", and the regular edition field and languages are
both empty, then it will be assumed to be unstaged. The list of editions
will be split and one image will be shown for each edition, using the
image number and a letter.

Should fix #445: [libromdata - WIM] Unstaged Windows OS installation images not properly supported
Requested by @pivotman319-owo.

NOTE: Needs to be verified!
This commit is contained in:
David Korth 2025-04-17 20:03:13 -04:00
parent 34b1c4b05b
commit b4fe8c22e1
2 changed files with 65 additions and 5 deletions

View File

@ -17,6 +17,9 @@
* Handled similarly to WiiUPackage.
* Fixes #436: Add folder icon support for OG Xbox games
* Requested by @Masamune3210.
* Wim: Add special handling for unstaged images.
* Fixes #445: [libromdata - WIM] Unstaged Windows OS installation images not properly supported
* Requested by @pivotman319-owo.
* Bug fixes:
* Amiibo: Fix an error that can cause the wrong Character Variant to be

View File

@ -82,6 +82,7 @@ struct WimIndex {
//string flags; // not used right now
string dispname, dispdescription;
bool containswindowsimage = false;
bool is_unstaged = false; // unstaged images have sets of components
};
/**
@ -195,16 +196,20 @@ int WimPrivate::addFields_XML()
}
xml_node windowsinfo = currentimage.child("WINDOWS");
xml_node editionid;
xml_node language; // first language only
if (windowsinfo) {
currentindex.containswindowsimage = true;
currentindex.windowsinfo.arch = static_cast<WimWindowsArchitecture>(
windowsinfo.child("ARCH").text().as_int(0));
currentindex.windowsinfo.editionid = windowsinfo.child("EDITIONID").text().as_string(s_unknown);
editionid = windowsinfo.child("EDITIONID");
currentindex.windowsinfo.editionid = editionid.text().as_string(s_unknown);
xml_node languages = windowsinfo.child("LANGUAGES");
if (languages) {
// NOTE: Only retrieving the first language.
currentindex.windowsinfo.languages.language = languages.child("LANGUAGE").text().get();
language = languages.child("LANGUAGE");
currentindex.windowsinfo.languages.language = language.text().get();
}
if (currentindex.windowsinfo.languages.language.empty()) {
currentindex.windowsinfo.languages.language = s_unknown;
@ -228,9 +233,44 @@ int WimPrivate::addFields_XML()
currentindex.name = currentimage.child("NAME").text().as_string(s_none);
currentindex.description = currentimage.child("DESCRIPTION").text().as_string(s_none);
currentindex.dispname = currentimage.child("DISPLAYNAME").text().as_string(s_none);
currentindex.dispdescription = currentimage.child("DISPLAYDESCRIPTION").text().as_string(s_none);
xml_node dispdescription = currentimage.child("DISPLAYDESCRIPTION");
// Check for an unstaged image.
if (currentindex.containswindowsimage && editionid.empty() && language.empty()) {
// This may be an unstaged image.
// Check for "EDITIONS:" in the display description.
// TODO: Verify that this is the correct field.
const char *const s_dispdescription = dispdescription.text().get();
const char *p;
if (s_dispdescription[0] != '\0' && (p = strstr(s_dispdescription, "EDITIONS:")) != nullptr) {
// Found editions. Split it on commas and make each a separate image.
currentindex.windowsinfo.languages.language = "N/A"; // TODO
currentindex.dispdescription.assign(s_dispdescription, p - s_dispdescription);
// Remove trailing spaces, if any.
size_t cur_size = currentindex.dispdescription.size();
while (cur_size > 0 && currentindex.dispdescription[cur_size - 1] == ' ') {
cur_size--;
}
currentindex.dispdescription.resize(cur_size);
currentindex.is_unstaged = true;
char *const dupdesc = strdup(p + 9);
char *saveptr = nullptr;
for (const char *token = strtok_r(dupdesc, ",", &saveptr);
token != nullptr; token = strtok_r(nullptr, ",", &saveptr))
{
currentindex.windowsinfo.editionid = token;
images.push_back(currentindex);
}
free(dupdesc);
}
}
if (!currentindex.is_unstaged) {
// Not an unstaged image. Use the display description as-is.
currentindex.dispdescription = dispdescription.text().as_string(s_none);
images.push_back(std::move(currentindex));
}
images.push_back(std::move(currentindex));
currentimage = currentimage.next_sibling();
}
@ -239,11 +279,28 @@ int WimPrivate::addFields_XML()
// loop for the rows
unsigned int idx = 1;
char unstaged_idx = 'a';
for (const auto &image : images) {
vv_data->resize(vv_data->size()+1);
auto &data_row = vv_data->at(vv_data->size()-1);
data_row.reserve(10);
data_row.push_back(fmt::to_string(idx++));
if (likely(!image.is_unstaged)) {
// Staged images use the format "1", "2", "3", etc.
if (unstaged_idx != 'a') {
// Need to reset the unstaged index and increment the staged index.
idx++;
unstaged_idx = 'a';
}
data_row.push_back(fmt::to_string(idx++));
} else {
// Unstaged sub-images will use the format "1a", "1b", "1c", etc.
// TODO: What if there's more than 26 sub-images?
assert(unstaged_idx <= 'z');
data_row.push_back(fmt::format(FSTR("{:d}{:c}"), idx, unstaged_idx));
unstaged_idx++;
}
data_row.push_back(image.name);
data_row.push_back(image.description);
data_row.push_back(image.dispname);