mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-23 06:44:38 +01:00
* Fix #24904: gracefully handle incomplete CSS files Some CSS files are truncated, possibly due to users overwriting RCT2 assets with parts of RCT1. This causes incorrect access to freed memory and potential double frees. This change verifies CSS data has expected samples and if not, informs user about incorrect assets. The samples that exist are still loaded and available for use. There was no backtrace report generated for this on Windows. * Skip reading of unused variables
This commit is contained in:
committed by
GitHub
parent
0fee079931
commit
c54466ec28
@@ -3841,3 +3841,4 @@ STR_6799 :Mini map
|
||||
STR_7000 :or
|
||||
STR_7001 :Ride name
|
||||
STR_7002 :{STRINGID} {STRINGID}
|
||||
STR_7003 :Audio file ‘{STRING}’ is truncated. Expected sample {INT32}, but only {INT32} are available. Consider reinstalling RCT2.
|
||||
|
||||
@@ -78,22 +78,28 @@ namespace OpenRCT2::Audio
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLAudioSource> source;
|
||||
try
|
||||
{
|
||||
auto source = CreateAudioSource(rw, index);
|
||||
|
||||
// Stream will already be in memory, so convert to target format
|
||||
auto& targetFormat = _audioMixer->GetFormat();
|
||||
source = source->ToMemory(targetFormat);
|
||||
|
||||
return AddSource(std::move(source));
|
||||
source = CreateAudioSource(rw, index);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
SDL_RWclose(rw);
|
||||
LOG_VERBOSE("Unable to create audio source: %s", e.what());
|
||||
}
|
||||
|
||||
SDL_RWclose(rw);
|
||||
|
||||
if (source == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Stream will already be in memory, so convert to target format
|
||||
auto& targetFormat = _audioMixer->GetFormat();
|
||||
source = source->ToMemory(targetFormat);
|
||||
|
||||
return AddSource(std::move(source));
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) override
|
||||
|
||||
@@ -116,46 +116,45 @@ std::unique_ptr<SDLAudioSource> OpenRCT2::Audio::CreateAudioSource(SDL_RWops* rw
|
||||
std::unique_ptr<SDLAudioSource> OpenRCT2::Audio::CreateAudioSource(SDL_RWops* rw, uint32_t cssIndex)
|
||||
{
|
||||
auto numSounds = SDL_ReadLE32(rw);
|
||||
if (cssIndex < numSounds)
|
||||
if (cssIndex >= numSounds)
|
||||
{
|
||||
SDL_RWseek(rw, cssIndex * 4, RW_SEEK_CUR);
|
||||
|
||||
auto pcmOffset = SDL_ReadLE32(rw);
|
||||
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
|
||||
|
||||
auto pcmLength = SDL_ReadLE32(rw);
|
||||
|
||||
AudioFormat format;
|
||||
[[maybe_unused]] auto encoding = SDL_ReadLE16(rw);
|
||||
format.channels = SDL_ReadLE16(rw);
|
||||
format.freq = SDL_ReadLE32(rw);
|
||||
[[maybe_unused]] auto byterate = SDL_ReadLE32(rw);
|
||||
[[maybe_unused]] auto blockalign = SDL_ReadLE16(rw);
|
||||
[[maybe_unused]] auto bitspersample = SDL_ReadLE16(rw);
|
||||
switch (bitspersample)
|
||||
{
|
||||
case 8:
|
||||
format.format = AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
format.format = AUDIO_S16LSB;
|
||||
break;
|
||||
default:
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("Unsupported bits per sample");
|
||||
}
|
||||
[[maybe_unused]] auto extrasize = SDL_ReadLE16(rw);
|
||||
|
||||
std::vector<uint8_t> pcmData;
|
||||
pcmData.resize(pcmLength);
|
||||
SDL_RWread(rw, pcmData.data(), pcmLength, 1);
|
||||
|
||||
SDL_RWclose(rw);
|
||||
return CreateMemoryAudioSource(format, format, std::move(pcmData));
|
||||
// Not enough sounds, caller is responsible for freeing rw
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
|
||||
SDL_RWseek(rw, cssIndex * 4, RW_SEEK_CUR);
|
||||
|
||||
auto pcmOffset = SDL_ReadLE32(rw);
|
||||
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
|
||||
|
||||
auto pcmLength = SDL_ReadLE32(rw);
|
||||
|
||||
AudioFormat format;
|
||||
// encoding, 16 bits
|
||||
rw->seek(rw, 2, RW_SEEK_CUR);
|
||||
format.channels = SDL_ReadLE16(rw);
|
||||
format.freq = SDL_ReadLE32(rw);
|
||||
// byterate, 32 bits
|
||||
// blockalign, 16 bits
|
||||
rw->seek(rw, 6, RW_SEEK_CUR);
|
||||
auto bitspersample = SDL_ReadLE16(rw);
|
||||
switch (bitspersample)
|
||||
{
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("CSS does not contain required entry");
|
||||
case 8:
|
||||
format.format = AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
format.format = AUDIO_S16LSB;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unsupported bits per sample");
|
||||
}
|
||||
// extrasize, 16 bits
|
||||
rw->seek(rw, 2, RW_SEEK_CUR);
|
||||
|
||||
std::vector<uint8_t> pcmData;
|
||||
pcmData.resize(pcmLength);
|
||||
SDL_RWread(rw, pcmData.data(), pcmLength, 1);
|
||||
|
||||
return CreateMemoryAudioSource(format, format, std::move(pcmData));
|
||||
}
|
||||
|
||||
@@ -1754,6 +1754,9 @@ enum : StringId
|
||||
STR_GAMEPAD_DEADZONE_TOOLTIP_FORMAT = 6790,
|
||||
STR_GAMEPAD_SENSITIVITY_TOOLTIP_FORMAT = 6791,
|
||||
|
||||
// Window: Error
|
||||
STR_AUDIO_FILE_TRUNCATED = 7003,
|
||||
|
||||
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
|
||||
/* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings
|
||||
};
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
#include "../core/File.h"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../core/Path.hpp"
|
||||
#include "../localisation/Formatting.h"
|
||||
#include "../localisation/StringIds.h"
|
||||
#include "../ui/UiContext.h"
|
||||
#include "Object.h"
|
||||
|
||||
using namespace OpenRCT2;
|
||||
@@ -160,6 +163,17 @@ IAudioSource* AudioSampleTable::LoadSample(uint32_t index) const
|
||||
auto& audioContext = GetContext()->GetAudioContext();
|
||||
if (entry.PathIndex)
|
||||
{
|
||||
auto originalPosition = stream->GetPosition();
|
||||
auto numSounds = stream->ReadValue<uint32_t>();
|
||||
stream->SetPosition(originalPosition);
|
||||
|
||||
if (*entry.PathIndex >= numSounds)
|
||||
{
|
||||
auto& ui = GetContext()->GetUiContext();
|
||||
ui.ShowMessageBox(FormatStringID(
|
||||
STR_AUDIO_FILE_TRUNCATED, entry.Asset->GetPath().c_str(), *entry.PathIndex, numSounds));
|
||||
}
|
||||
|
||||
result = audioContext.CreateStreamFromCSS(std::move(stream), *entry.PathIndex);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -375,6 +375,26 @@ std::unique_ptr<IStream> ObjectAsset::GetStream() const
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string& ObjectAsset::GetZipPath() const
|
||||
{
|
||||
return _zipPath;
|
||||
}
|
||||
|
||||
const std::string& ObjectAsset::GetPath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
size_t ObjectAsset::GetHash() const
|
||||
{
|
||||
// Combine hashes of zipPath and path
|
||||
std::hash<std::string> hasher;
|
||||
auto h1 = hasher(_zipPath);
|
||||
auto h2 = hasher(_path);
|
||||
// Combine the hashes based on example from https://en.cppreference.com/w/cpp/utility/hash.html
|
||||
return h1 ^ (h2 << 1);
|
||||
}
|
||||
|
||||
u8string VersionString(const ObjectVersion& version)
|
||||
{
|
||||
return std::to_string(std::get<0>(version)) + "." + std::to_string(std::get<1>(version)) + "."
|
||||
|
||||
Reference in New Issue
Block a user