Fix parsing large MVC files causing lockup/crash in GUI (fixes # 829).

Still TODO is deal with large files other than MVC, and also ZIP files.
At least the logic for analyzing size is now in one method (OSystem::openROM).
This commit is contained in:
Stephen Anthony 2022-04-16 19:25:50 -02:30
parent df4900828c
commit fed3fa2241
7 changed files with 70 additions and 50 deletions

View File

@ -351,8 +351,8 @@ size_t FilesystemNode::read(ByteBuffer& buffer, size_t size) const
if (sizeRead == 0)
throw runtime_error("Zero-byte file");
else if (size != 0)
sizeRead = size;
else if (size > 0) // If a requested size to read is provided, honour it
sizeRead = std::min(sizeRead, size);
buffer = make_unique<uInt8[]>(sizeRead);
in.read(reinterpret_cast<char*>(buffer.get()), sizeRead);

View File

@ -317,7 +317,7 @@ string hash(const ByteBuffer& buffer, size_t length)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string hash(const uInt8* buffer, size_t length)
{
constexpr char hex[] = "0123456789abcdef";
static constexpr char hex[] = "0123456789abcdef";
MD5_CTX context{};
uInt8 md5[16] = {0};
const uInt32 len32 = static_cast<uInt32>(length); // Always use 32-bit for now
@ -336,22 +336,4 @@ string hash(const uInt8* buffer, size_t length)
return result;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string hash(const FilesystemNode& node)
{
ByteBuffer image;
size_t size = 0;
try
{
size = node.read(image);
}
catch(...)
{
return EmptyString;
}
const string& md5 = hash(image, size);
return md5;
}
} // Namespace MD5

View File

@ -35,6 +35,7 @@ namespace MD5 {
@param buffer The message to compute the digest of
@param length The length of the message
@return The message-digest
*/
string hash(const ByteBuffer& buffer, size_t length);
@ -43,19 +44,11 @@ namespace MD5 {
Dito.
@param buffer The message to compute the digest of
@return The message - digest
*/
string hash(const string& buffer);
/**
Get the MD5 Message-Digest of the file contained in 'node'.
The digest consists of 32 hexadecimal digits.
@param node The file node to compute the digest of
@return The message-digest
*/
string hash(const FilesystemNode& node);
} // Namespace MD5
#endif

View File

@ -753,6 +753,34 @@ ByteBuffer OSystem::openROM(const FilesystemNode& rom, string& md5, size_t& size
// but also adds a properties entry if the one for the ROM doesn't
// contain a valid name
ByteBuffer image = openROM(rom, size);
if(image)
{
// If we get to this point, we know we have a valid file to open
// Now we make sure that the file has a valid properties entry
// To save time, only generate an MD5 if we really need one
if(md5 == "")
md5 = MD5::hash(image, size);
// Make sure to load a per-ROM properties entry, if one exists
myPropSet->loadPerROM(rom, md5);
}
return image;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string OSystem::getROMMD5(const FilesystemNode& rom) const
{
size_t size = 0;
const ByteBuffer image = openROM(rom, size);
return image ? MD5::hash(image, size) : EmptyString;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ByteBuffer OSystem::openROM(const FilesystemNode& rom, size_t& size) const
{
// First check if this is a 'streaming' ROM (one where we only read
// a portion of the file)
const size_t sizeToRead = CartDetector::isProbablyMVC(rom);
@ -762,17 +790,15 @@ ByteBuffer OSystem::openROM(const FilesystemNode& rom, string& md5, size_t& size
// if it's not a ZIP file (that size should be higher; still TBD)
ByteBuffer image;
try
{
if((size = rom.read(image, sizeToRead)) == 0)
return nullptr;
// If we get to this point, we know we have a valid file to open
// Now we make sure that the file has a valid properties entry
// To save time, only generate an MD5 if we really need one
if(md5 == "")
md5 = MD5::hash(image, size);
// Make sure to load a per-ROM properties entry, if one exists
myPropSet->loadPerROM(rom, md5);
}
catch(const runtime_error& e)
{
cerr << "ERROR: Couldn't open ROM (" << e.what() << ")";
}
return image;
}

View File

@ -338,6 +338,15 @@ class OSystem
*/
ByteBuffer openROM(const FilesystemNode& rom, string& md5, size_t& size);
/**
Open the given ROM and return the MD5sum of the data.
@param rom The file node of the ROM to open (contains path)
@return MD5 of the ROM image (if valid), otherwise EmptyString
*/
string getROMMD5(const FilesystemNode& rom) const;
/**
Creates a new game console from the specified romfile, and correctly
initializes the system state to start emulation of the Console.
@ -596,7 +605,6 @@ class OSystem
static bool ourOverrideBaseDirWithApp;
private:
/**
This method should be called to initiate the process of loading settings
from the config file. It takes care of loading settings, applying
@ -609,13 +617,24 @@ class OSystem
*/
void createSound();
/**
Open the given ROM and return an array containing its contents.
This method takes care of using only a valid size for the
@param romfile The file node of the ROM to open (contains path)
@param size The amount of data read into the image array
@return Unique pointer to the array, otherwise nullptr
*/
ByteBuffer openROM(const FilesystemNode& romfile, size_t& size) const;
/**
Creates an actual Console object based on the given info.
@param romfile The file node of the ROM to use (contains path)
@param md5 The MD5sum of the ROM
@return The actual Console object, otherwise nullptr.
@return The actual Console object, otherwise nullptr
*/
unique_ptr<Console> openConsole(const FilesystemNode& romfile, string& md5);

View File

@ -395,7 +395,7 @@ const string& LauncherDialog::selectedRomMD5()
// Lookup MD5, and if not present, cache it
const auto iter = myMD5List.find(currentNode().getPath());
if(iter == myMD5List.end())
myMD5List[currentNode().getPath()] = MD5::hash(currentNode());
myMD5List[currentNode().getPath()] = instance().getROMMD5(currentNode());
return myMD5List[currentNode().getPath()];
}

View File

@ -139,7 +139,7 @@ void RomAuditDialog::auditRoms()
// Calculate the MD5 so we can get the rest of the info
// from the PropertiesSet (stella.pro)
const string& md5 = MD5::hash(files[idx]);
const string& md5 = instance().getROMMD5(files[idx]);
if(instance().propSet().getMD5(md5, props))
{
const string& name = props.get(PropType::Cart_Name);