1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-23 14:54:30 +01:00
Files
OpenRCT2/src/openrct2/object/AudioSampleTable.cpp
Michał Janiszewski c54466ec28 Fix #24904: gracefully handle incomplete CSS files (#24923)
* 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
2025-08-09 21:56:12 +00:00

197 lines
5.6 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2025 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "AudioSampleTable.h"
#include "../Context.h"
#include "../PlatformEnvironment.h"
#include "../audio/AudioContext.h"
#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;
using namespace OpenRCT2::Audio;
std::vector<AudioSampleTable::Entry>& AudioSampleTable::GetEntries()
{
return _entries;
}
void AudioSampleTable::ReadFromJson(IReadObjectContext* context, const json_t& root)
{
json_t jSamples = root["samples"];
if (jSamples.is_array())
{
for (auto& jSample : jSamples)
{
SourceInfo sourceInfo;
int32_t modifier{};
if (jSample.is_string())
{
sourceInfo = ParseSource(jSample.get<std::string>());
}
else if (jSample.is_object())
{
auto& jSource = jSample.at("source");
if (jSource.is_string())
{
sourceInfo = ParseSource(jSource.get<std::string>());
if (jSample.contains("modifier"))
{
auto& jModifier = jSample.at("modifier");
if (jModifier.is_number())
{
modifier = jModifier.get<int32_t>();
}
}
}
}
auto asset = context->GetAsset(sourceInfo.Path);
if (!sourceInfo.SourceRange)
{
auto& entry = _entries.emplace_back();
entry.Asset = asset;
entry.Modifier = modifier;
}
else
{
Range<int32_t> r(1, 5);
for (auto index : *sourceInfo.SourceRange)
{
auto& entry = _entries.emplace_back();
entry.Asset = asset;
entry.PathIndex = index;
entry.Modifier = modifier;
}
}
}
}
}
void AudioSampleTable::LoadFrom(const AudioSampleTable& table, size_t sourceStartIndex, size_t length)
{
// Ensure we stay in bounds of source table
if (sourceStartIndex >= table._entries.size())
return;
length = std::min(length, table._entries.size() - sourceStartIndex);
// Asset packs may allocate more images for an object that original, or original object may
// not allocate any images at all.
if (_entries.size() < length)
{
_entries.resize(length);
}
for (size_t i = 0; i < length; i++)
{
const auto& sourceEntry = table._entries[sourceStartIndex + i];
if (sourceEntry.Asset)
{
auto stream = sourceEntry.Asset->GetStream();
if (stream != nullptr)
{
auto& entry = _entries[i];
entry.Asset = sourceEntry.Asset;
entry.PathIndex = sourceEntry.PathIndex;
entry.Modifier = sourceEntry.Modifier;
}
}
}
}
void AudioSampleTable::Load()
{
for (size_t i = 0; i < _entries.size(); i++)
{
auto& entry = _entries[i];
if (entry.Source == nullptr)
{
entry.Source = LoadSample(static_cast<uint32_t>(i));
}
}
}
void AudioSampleTable::Unload()
{
for (auto& entry : _entries)
{
if (entry.Source != nullptr)
{
entry.Source->Release();
entry.Source = nullptr;
}
}
}
size_t AudioSampleTable::GetCount() const
{
return _entries.size();
}
IAudioSource* AudioSampleTable::GetSample(uint32_t index) const
{
if (index < _entries.size())
{
return _entries[index].Source;
}
return nullptr;
}
IAudioSource* AudioSampleTable::LoadSample(uint32_t index) const
{
IAudioSource* result{};
if (index < _entries.size())
{
auto& entry = _entries[index];
if (entry.Asset)
{
auto stream = entry.Asset->GetStream();
if (stream != nullptr)
{
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
{
result = audioContext.CreateStreamFromWAV(std::move(stream));
}
}
}
}
return result;
}
int32_t AudioSampleTable::GetSampleModifier(uint32_t index) const
{
if (index < _entries.size())
{
return _entries[index].Modifier;
}
return 0;
}