You've already forked OpenRCT2-Unity
mirror of
https://github.com/izzy2lost/OpenRCT2-Unity.git
synced 2026-03-10 12:38:22 -07:00
Merge pull request #13738 from IntelOrca/ride-music-objects-3
Read and use the JSON music objects for ride music implementation. Supports both vanilla and custom music objects. More music objects can be added via object manager if you compile-in the hidden tabs. Refactors a lot of the ride music logic into a new source file: RideAudio.
This commit is contained in:
@@ -57,6 +57,8 @@
|
||||
4C8BB68125533D65005C8830 /* StringBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8BB67D25533D64005C8830 /* StringBuilder.cpp */; };
|
||||
4C8BB68225533D65005C8830 /* StringReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8BB67E25533D64005C8830 /* StringReader.cpp */; };
|
||||
4C8BB68525533DB9005C8830 /* ZoomLevel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C8BB68425533DB9005C8830 /* ZoomLevel.cpp */; };
|
||||
4C91FD5F25AE476700CA5DA4 /* MusicObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C91FD5D25AE476700CA5DA4 /* MusicObject.cpp */; };
|
||||
4C91FD6225AE483700CA5DA4 /* RideAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C91FD6025AE483600CA5DA4 /* RideAudio.cpp */; };
|
||||
4C93F1AD1F8CD9F000A9330D /* Input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1AC1F8CD9F000A9330D /* Input.cpp */; };
|
||||
4C93F1AF1F8CD9F600A9330D /* KeyboardShortcut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1AE1F8CD9F600A9330D /* KeyboardShortcut.cpp */; };
|
||||
4CA39E512513F8A00094066B /* RTL.ICU.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CA39E4E2513F8A00094066B /* RTL.ICU.cpp */; };
|
||||
@@ -458,7 +460,6 @@
|
||||
C688786F20289A6F0084B384 /* VehicleData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7B54052005735F00A52E21 /* VehicleData.cpp */; };
|
||||
C688787020289A6F0084B384 /* VehiclePaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7B54072005736700A52E21 /* VehiclePaint.cpp */; };
|
||||
C688787120289A780084B384 /* Ride.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C6A66BF1FF9322A00694CB6 /* Ride.cpp */; };
|
||||
C688787220289A780084B384 /* MusicList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F73E320F2011589F00C4D975 /* MusicList.cpp */; };
|
||||
C688787320289A780084B384 /* RideRatings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F73E320B2011589E00C4D975 /* RideRatings.cpp */; };
|
||||
C688787420289A780084B384 /* TrackDesignSave.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F73E320E2011589F00C4D975 /* TrackDesignSave.cpp */; };
|
||||
C688787520289A780084B384 /* RideData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C7B541420060D8E00A52E21 /* RideData.cpp */; };
|
||||
@@ -1033,6 +1034,10 @@
|
||||
4C8BB68325533DB9005C8830 /* ZoomLevel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZoomLevel.h; sourceTree = "<group>"; };
|
||||
4C8BB68425533DB9005C8830 /* ZoomLevel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZoomLevel.cpp; sourceTree = "<group>"; };
|
||||
4C9196ED204FF3E000869A24 /* Location.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Location.hpp; sourceTree = "<group>"; };
|
||||
4C91FD5D25AE476700CA5DA4 /* MusicObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MusicObject.cpp; sourceTree = "<group>"; };
|
||||
4C91FD5E25AE476700CA5DA4 /* MusicObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MusicObject.h; sourceTree = "<group>"; };
|
||||
4C91FD6025AE483600CA5DA4 /* RideAudio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RideAudio.cpp; sourceTree = "<group>"; };
|
||||
4C91FD6125AE483600CA5DA4 /* RideAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RideAudio.h; sourceTree = "<group>"; };
|
||||
4C93F1181F8B744400A9330D /* AirPoweredVerticalCoaster.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AirPoweredVerticalCoaster.cpp; sourceTree = "<group>"; };
|
||||
4C93F1191F8B744400A9330D /* BobsleighCoaster.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BobsleighCoaster.cpp; sourceTree = "<group>"; };
|
||||
4C93F11A1F8B744400A9330D /* BolligerMabillardTrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BolligerMabillardTrack.h; sourceTree = "<group>"; };
|
||||
@@ -1913,9 +1918,7 @@
|
||||
F70839911FFC0AFF002DCEFA /* Scenario.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scenario.cpp; sourceTree = "<group>"; };
|
||||
F73E320B2011589E00C4D975 /* RideRatings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RideRatings.cpp; sourceTree = "<group>"; };
|
||||
F73E320C2011589F00C4D975 /* RideRatings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RideRatings.h; sourceTree = "<group>"; };
|
||||
F73E320D2011589F00C4D975 /* MusicList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MusicList.h; sourceTree = "<group>"; };
|
||||
F73E320E2011589F00C4D975 /* TrackDesignSave.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackDesignSave.cpp; sourceTree = "<group>"; };
|
||||
F73E320F2011589F00C4D975 /* MusicList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MusicList.cpp; sourceTree = "<group>"; };
|
||||
F74789541EEDEA0D009E50E7 /* Input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Input.h; sourceTree = "<group>"; };
|
||||
F76C809A1EC4D9FA00FA49E2 /* libopenrct2.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libopenrct2.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F76C83571EC4E7CC00FA49E2 /* Audio.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Audio.cpp; sourceTree = "<group>"; };
|
||||
@@ -3384,6 +3387,8 @@
|
||||
F76C841B1EC4E7CC00FA49E2 /* ImageTable.h */,
|
||||
F76C841C1EC4E7CC00FA49E2 /* LargeSceneryObject.cpp */,
|
||||
F76C841D1EC4E7CC00FA49E2 /* LargeSceneryObject.h */,
|
||||
4C91FD5D25AE476700CA5DA4 /* MusicObject.cpp */,
|
||||
4C91FD5E25AE476700CA5DA4 /* MusicObject.h */,
|
||||
F76C841E1EC4E7CC00FA49E2 /* Object.cpp */,
|
||||
F76C841F1EC4E7CC00FA49E2 /* Object.h */,
|
||||
F76C84201EC4E7CC00FA49E2 /* ObjectFactory.cpp */,
|
||||
@@ -3559,10 +3564,10 @@
|
||||
F76C84EA1EC4E7CD00FA49E2 /* water */,
|
||||
4C6AC2101F9E1CB3004324AA /* CableLift.cpp */,
|
||||
4C6AC2111F9E1CB3004324AA /* CableLift.h */,
|
||||
F73E320F2011589F00C4D975 /* MusicList.cpp */,
|
||||
F73E320D2011589F00C4D975 /* MusicList.h */,
|
||||
4C6A66BF1FF9322A00694CB6 /* Ride.cpp */,
|
||||
4C6A66C01FF9322A00694CB6 /* Ride.h */,
|
||||
4C91FD6025AE483600CA5DA4 /* RideAudio.cpp */,
|
||||
4C91FD6125AE483600CA5DA4 /* RideAudio.h */,
|
||||
4C7B541420060D8E00A52E21 /* RideData.cpp */,
|
||||
4C7B541520060D8E00A52E21 /* RideData.h */,
|
||||
F73E320B2011589E00C4D975 /* RideRatings.cpp */,
|
||||
@@ -4508,6 +4513,7 @@
|
||||
930EEA6A24FC00950070314E /* ScenarioSelect.cpp in Sources */,
|
||||
C654DF3C1F69C0430040F43D /* TrackDesignManage.cpp in Sources */,
|
||||
C64645001F3FA4120026AC2D /* ViewClipping.cpp in Sources */,
|
||||
4C91FD6225AE483700CA5DA4 /* RideAudio.cpp in Sources */,
|
||||
C68878C020289B710084B384 /* ApplyPaletteShader.cpp in Sources */,
|
||||
C666EE791F37ACB10061AA04 /* ServerStart.cpp in Sources */,
|
||||
C61ADB231FBBCB8B0024F2EF /* GameBottomToolbar.cpp in Sources */,
|
||||
@@ -4587,6 +4593,7 @@
|
||||
C666EE771F37ACB10061AA04 /* SavePrompt.cpp in Sources */,
|
||||
C654DF391F69C0430040F43D /* TitleCommandEditor.cpp in Sources */,
|
||||
C61FB2731FA3E25D0095FB9D /* TextInput.cpp in Sources */,
|
||||
4C91FD5F25AE476700CA5DA4 /* MusicObject.cpp in Sources */,
|
||||
C62D83871FCC7D1A008C04F1 /* EditorObjectSelection.cpp in Sources */,
|
||||
4C3B4236205914F7000C5BB7 /* InGameConsole.cpp in Sources */,
|
||||
C68878C820289B710084B384 /* SwapFramebuffer.cpp in Sources */,
|
||||
@@ -4936,7 +4943,6 @@
|
||||
936F412A24CE030F00E07BCF /* NetworkClient.cpp in Sources */,
|
||||
F76C86C51EC4E88400FA49E2 /* S6Importer.cpp in Sources */,
|
||||
C688790A20289B9B0084B384 /* WoodenRollerCoaster.cpp in Sources */,
|
||||
C688787220289A780084B384 /* MusicList.cpp in Sources */,
|
||||
93F76F0220BFF77B00D4512C /* Paint.Surface.cpp in Sources */,
|
||||
66A10ECF257F1DF800DD651A /* ClimateSetAction.cpp in Sources */,
|
||||
93DFD02F24521BA0001FCBAF /* FileWatcher.cpp in Sources */,
|
||||
|
||||
@@ -70,6 +70,11 @@ namespace OpenRCT2::Audio
|
||||
return AudioSource::CreateStreamFromWAV(path);
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) override
|
||||
{
|
||||
return AudioSource::CreateStreamFromWAV(std::move(stream));
|
||||
}
|
||||
|
||||
void StartTitleMusic() override
|
||||
{
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace OpenRCT2::Audio
|
||||
IAudioSource* CreateMemoryFromWAV(const std::string& path, const AudioFormat* targetFormat = nullptr);
|
||||
IAudioSource* CreateStreamFromWAV(const std::string& path);
|
||||
IAudioSource* CreateStreamFromWAV(SDL_RWops* rw);
|
||||
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream);
|
||||
} // namespace AudioSource
|
||||
|
||||
namespace AudioChannel
|
||||
|
||||
@@ -202,4 +202,34 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
IAudioSource* AudioSource::CreateStreamFromWAV(std::unique_ptr<IStream> stream)
|
||||
{
|
||||
auto rw = new SDL_RWops();
|
||||
*rw = {};
|
||||
rw->type = SDL_RWOPS_UNKNOWN;
|
||||
rw->hidden.unknown.data1 = stream.release();
|
||||
rw->seek = [](SDL_RWops* ctx, Sint64 offset, int whence) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
ptr->Seek(offset, whence);
|
||||
return static_cast<Sint64>(ptr->GetPosition());
|
||||
};
|
||||
rw->read = [](SDL_RWops* ctx, void* buf, size_t size, size_t maxnum) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
return static_cast<size_t>(ptr->TryRead(buf, size * maxnum) / size);
|
||||
};
|
||||
rw->size = [](SDL_RWops* ctx) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
return static_cast<Sint64>(ptr->GetLength());
|
||||
};
|
||||
rw->close = [](SDL_RWops* ctx) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
delete ptr;
|
||||
ctx->hidden.unknown.data1 = nullptr;
|
||||
delete ctx;
|
||||
return 0;
|
||||
};
|
||||
return CreateStreamFromWAV(rw);
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Audio
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <openrct2/localisation/LocalisationService.h>
|
||||
#include <openrct2/network/network.h>
|
||||
#include <openrct2/platform/Platform2.h>
|
||||
#include <openrct2/ride/RideAudio.h>
|
||||
#include <openrct2/scenario/Scenario.h>
|
||||
#include <openrct2/sprites.h>
|
||||
#include <openrct2/title/TitleScreen.h>
|
||||
@@ -1374,7 +1375,7 @@ static void window_options_audio_mouseup(rct_window* w, rct_widgetindex widgetIn
|
||||
gConfigSound.ride_music_enabled = !gConfigSound.ride_music_enabled;
|
||||
if (!gConfigSound.ride_music_enabled)
|
||||
{
|
||||
OpenRCT2::Audio::StopRideMusic();
|
||||
OpenRCT2::RideAudio::StopAllChannels();
|
||||
}
|
||||
config_save_default();
|
||||
w->Invalidate();
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <openrct2/localisation/LocalisationService.h>
|
||||
#include <openrct2/localisation/StringIds.h>
|
||||
#include <openrct2/network/network.h>
|
||||
#include <openrct2/object/MusicObject.h>
|
||||
#include <openrct2/object/ObjectManager.h>
|
||||
#include <openrct2/object/ObjectRepository.h>
|
||||
#include <openrct2/object/StationObject.h>
|
||||
@@ -52,6 +53,8 @@
|
||||
#include <openrct2/sprites.h>
|
||||
#include <openrct2/windows/Intent.h>
|
||||
#include <openrct2/world/Park.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
static constexpr const rct_string_id WINDOW_TITLE = STR_RIDE_WINDOW_TITLE;
|
||||
@@ -4938,15 +4941,20 @@ static void window_ride_colour_scrollpaint(rct_window* w, rct_drawpixelinfo* dpi
|
||||
#pragma region Music
|
||||
|
||||
static constexpr const uint8_t MusicStyleOrder[] = {
|
||||
MUSIC_STYLE_GENTLE, MUSIC_STYLE_SUMMER, MUSIC_STYLE_WATER, MUSIC_STYLE_RAGTIME, MUSIC_STYLE_TECHNO,
|
||||
MUSIC_STYLE_MECHANICAL, MUSIC_STYLE_MODERN, MUSIC_STYLE_WILD_WEST, MUSIC_STYLE_PIRATES, MUSIC_STYLE_ROCK,
|
||||
MUSIC_STYLE_ROCK_STYLE_2, MUSIC_STYLE_ROCK_STYLE_3, MUSIC_STYLE_FANTASY, MUSIC_STYLE_HORROR, MUSIC_STYLE_TOYLAND,
|
||||
MUSIC_STYLE_CANDY_STYLE, MUSIC_STYLE_ROMAN_FANFARE, MUSIC_STYLE_ORIENTAL, MUSIC_STYLE_MARTIAN, MUSIC_STYLE_SPACE,
|
||||
MUSIC_STYLE_JUNGLE_DRUMS, MUSIC_STYLE_JURASSIC, MUSIC_STYLE_EGYPTIAN, MUSIC_STYLE_DODGEMS_BEAT, MUSIC_STYLE_SNOW,
|
||||
MUSIC_STYLE_ICE, MUSIC_STYLE_MEDIEVAL, MUSIC_STYLE_URBAN, MUSIC_STYLE_ORGAN
|
||||
MUSIC_STYLE_GENTLE, MUSIC_STYLE_SUMMER, MUSIC_STYLE_WATER,
|
||||
MUSIC_STYLE_RAGTIME, MUSIC_STYLE_TECHNO, MUSIC_STYLE_MECHANICAL,
|
||||
MUSIC_STYLE_MODERN, MUSIC_STYLE_WILD_WEST, MUSIC_STYLE_PIRATES,
|
||||
MUSIC_STYLE_ROCK, MUSIC_STYLE_ROCK_STYLE_2, MUSIC_STYLE_ROCK_STYLE_3,
|
||||
MUSIC_STYLE_FANTASY, MUSIC_STYLE_HORROR, MUSIC_STYLE_TOYLAND,
|
||||
MUSIC_STYLE_CANDY_STYLE, MUSIC_STYLE_ROMAN_FANFARE, MUSIC_STYLE_ORIENTAL,
|
||||
MUSIC_STYLE_MARTIAN, MUSIC_STYLE_SPACE, MUSIC_STYLE_JUNGLE_DRUMS,
|
||||
MUSIC_STYLE_JURASSIC, MUSIC_STYLE_EGYPTIAN, MUSIC_STYLE_DODGEMS_BEAT,
|
||||
MUSIC_STYLE_SNOW, MUSIC_STYLE_ICE, MUSIC_STYLE_MEDIEVAL,
|
||||
MUSIC_STYLE_URBAN, MUSIC_STYLE_ORGAN, MUSIC_STYLE_CUSTOM_MUSIC_1,
|
||||
MUSIC_STYLE_CUSTOM_MUSIC_2
|
||||
};
|
||||
|
||||
static uint8_t window_ride_current_music_style_order[42];
|
||||
static std::vector<ObjectEntryIndex> window_ride_current_music_style_order;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -5001,6 +5009,25 @@ static void window_ride_music_resize(rct_window* w)
|
||||
window_set_resize(w, 316, 81, 316, 81);
|
||||
}
|
||||
|
||||
static std::optional<size_t> GetMusicStyleOrder(ObjectEntryIndex musicObjectIndex)
|
||||
{
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, musicObjectIndex));
|
||||
|
||||
// Get the index in the order list
|
||||
auto originalStyleId = musicObj->GetOriginalStyleId();
|
||||
if (originalStyleId)
|
||||
{
|
||||
auto it = std::find(std::begin(MusicStyleOrder), std::end(MusicStyleOrder), *originalStyleId);
|
||||
if (it != std::end(MusicStyleOrder))
|
||||
{
|
||||
return std::distance(std::begin(MusicStyleOrder), it);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006B1EFC
|
||||
@@ -5015,37 +5042,67 @@ static void window_ride_music_mousedown(rct_window* w, rct_widgetindex widgetInd
|
||||
if (ride == nullptr)
|
||||
return;
|
||||
|
||||
int32_t numItems = 0;
|
||||
if (ride->type == RIDE_TYPE_MERRY_GO_ROUND)
|
||||
// Construct list of available music
|
||||
auto& musicOrder = window_ride_current_music_style_order;
|
||||
musicOrder.clear();
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
for (ObjectEntryIndex i = 0; i < MAX_MUSIC_OBJECTS; i++)
|
||||
{
|
||||
window_ride_current_music_style_order[numItems++] = MUSIC_STYLE_FAIRGROUND_ORGAN;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t n = 0; n < std::size(MusicStyleOrder); n++)
|
||||
window_ride_current_music_style_order[numItems++] = MusicStyleOrder[n];
|
||||
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, i));
|
||||
if (musicObj != nullptr)
|
||||
{
|
||||
// Hide custom music if the WAV file does not exist
|
||||
auto originalStyleId = musicObj->GetOriginalStyleId();
|
||||
if (originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_1 || originalStyleId == MUSIC_STYLE_CUSTOM_MUSIC_2)
|
||||
{
|
||||
auto numTracks = musicObj->GetTrackCount();
|
||||
if (numTracks != 0)
|
||||
{
|
||||
auto track0 = musicObj->GetTrack(0);
|
||||
if (!track0->Asset.IsAvailable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (OpenRCT2::Audio::gRideMusicInfoList[36].length != 0)
|
||||
window_ride_current_music_style_order[numItems++] = MUSIC_STYLE_CUSTOM_MUSIC_1;
|
||||
if (OpenRCT2::Audio::gRideMusicInfoList[37].length != 0)
|
||||
window_ride_current_music_style_order[numItems++] = MUSIC_STYLE_CUSTOM_MUSIC_2;
|
||||
if (musicObj->SupportsRideType(ride->type))
|
||||
{
|
||||
musicOrder.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i = 0; i < numItems; i++)
|
||||
// Sort available music by the original RCT2 list order
|
||||
std::stable_sort(musicOrder.begin(), musicOrder.end(), [](const ObjectEntryIndex& a, const ObjectEntryIndex& b) {
|
||||
auto orderA = GetMusicStyleOrder(a);
|
||||
auto orderB = GetMusicStyleOrder(b);
|
||||
return orderA < orderB;
|
||||
});
|
||||
|
||||
// Setup dropdown list
|
||||
auto numItems = musicOrder.size();
|
||||
for (size_t i = 0; i < numItems; i++)
|
||||
{
|
||||
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, musicOrder[i]));
|
||||
gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL;
|
||||
gDropdownItemsArgs[i] = MusicStyleNames[window_ride_current_music_style_order[i]];
|
||||
gDropdownItemsArgs[i] = musicObj->NameStringId;
|
||||
}
|
||||
|
||||
WindowDropdownShowTextCustomWidth(
|
||||
{ w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
|
||||
w->colours[1], 0, Dropdown::Flag::StayOpen, numItems, widget->right - dropdownWidget->left);
|
||||
|
||||
for (auto i = 0; i < numItems; i++)
|
||||
// Set currently checked item
|
||||
for (size_t i = 0; i < numItems; i++)
|
||||
{
|
||||
if (window_ride_current_music_style_order[i] == ride->music)
|
||||
if (musicOrder[i] == ride->music)
|
||||
{
|
||||
Dropdown::SetChecked(i, true);
|
||||
Dropdown::SetChecked(static_cast<int32_t>(i), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5056,13 +5113,12 @@ static void window_ride_music_mousedown(rct_window* w, rct_widgetindex widgetInd
|
||||
*/
|
||||
static void window_ride_music_dropdown(rct_window* w, rct_widgetindex widgetIndex, int32_t dropdownIndex)
|
||||
{
|
||||
uint8_t musicStyle;
|
||||
|
||||
if (widgetIndex != WIDX_MUSIC_DROPDOWN || dropdownIndex == -1)
|
||||
return;
|
||||
|
||||
musicStyle = window_ride_current_music_style_order[dropdownIndex];
|
||||
set_operating_setting(w->number, RideSetSetting::MusicType, musicStyle);
|
||||
if (widgetIndex == WIDX_MUSIC_DROPDOWN && dropdownIndex >= 0
|
||||
&& static_cast<size_t>(dropdownIndex) < window_ride_current_music_style_order.size())
|
||||
{
|
||||
auto musicStyle = window_ride_current_music_style_order[dropdownIndex];
|
||||
set_operating_setting(w->number, RideSetSetting::MusicType, musicStyle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5099,7 +5155,14 @@ static void window_ride_music_invalidate(rct_window* w)
|
||||
ride->FormatNameTo(ft);
|
||||
|
||||
// Set selected music
|
||||
window_ride_music_widgets[WIDX_MUSIC].text = MusicStyleNames[ride->music];
|
||||
rct_string_id musicName = STR_NONE;
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto musicObj = static_cast<MusicObject*>(objManager.GetLoadedObject(ObjectType::Music, ride->music));
|
||||
if (musicObj != nullptr)
|
||||
{
|
||||
musicName = musicObj->NameStringId;
|
||||
}
|
||||
window_ride_music_widgets[WIDX_MUSIC].text = musicName;
|
||||
|
||||
// Set music activated
|
||||
auto isMusicActivated = (ride->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) != 0;
|
||||
|
||||
@@ -175,6 +175,7 @@ namespace OpenRCT2
|
||||
gfx_object_check_all_images_freed();
|
||||
gfx_unload_g2();
|
||||
gfx_unload_g1();
|
||||
Audio::Close();
|
||||
config_release();
|
||||
|
||||
Instance = nullptr;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "RideSetSettingAction.h"
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../object/ObjectManager.h"
|
||||
#include "../ride/Ride.h"
|
||||
#include "../ride/RideData.h"
|
||||
|
||||
@@ -100,12 +102,16 @@ GameActions::Result::Ptr RideSetSettingAction::Query() const
|
||||
case RideSetSetting::Music:
|
||||
break;
|
||||
case RideSetSetting::MusicType:
|
||||
if (_value >= MUSIC_STYLE_COUNT)
|
||||
{
|
||||
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
|
||||
auto musicObj = objManager.GetLoadedObject(ObjectType::Music, _value);
|
||||
if (musicObj == nullptr)
|
||||
{
|
||||
log_warning("Invalid music style: %u", _value);
|
||||
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_CHANGE_OPERATING_MODE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RideSetSetting::LiftHillSpeed:
|
||||
if (!ride_is_valid_lift_hill_speed(ride))
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "../localisation/StringIds.h"
|
||||
#include "../peep/Peep.h"
|
||||
#include "../ride/Ride.h"
|
||||
#include "../ride/RideAudio.h"
|
||||
#include "../ui/UiContext.h"
|
||||
#include "../util/Util.h"
|
||||
#include "AudioContext.h"
|
||||
@@ -48,9 +49,6 @@ namespace OpenRCT2::Audio
|
||||
void* gTitleMusicChannel = nullptr;
|
||||
void* gWeatherSoundChannel = nullptr;
|
||||
|
||||
RideMusic gRideMusicList[MaxRideMusic];
|
||||
RideMusicParams gRideMusicParamsList[MaxRideMusic];
|
||||
RideMusicParams* gRideMusicParamsListEnd;
|
||||
VehicleSound gVehicleSoundList[MaxVehicleSounds];
|
||||
|
||||
// clang-format off
|
||||
@@ -290,26 +288,11 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
}
|
||||
|
||||
void StopRideMusic()
|
||||
{
|
||||
for (auto& rideMusic : gRideMusicList)
|
||||
{
|
||||
if (rideMusic.ride_id != RIDE_ID_NULL)
|
||||
{
|
||||
rideMusic.ride_id = RIDE_ID_NULL;
|
||||
if (rideMusic.sound_channel != nullptr)
|
||||
{
|
||||
Mixer_Stop_Channel(rideMusic.sound_channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StopAll()
|
||||
{
|
||||
StopTitleMusic();
|
||||
StopVehicleSounds();
|
||||
StopRideMusic();
|
||||
RideAudio::StopAllChannels();
|
||||
peep_stop_crowd_noise();
|
||||
StopWeatherSound();
|
||||
}
|
||||
@@ -355,33 +338,7 @@ namespace OpenRCT2::Audio
|
||||
|
||||
void InitRideSoundsAndInfo()
|
||||
{
|
||||
int32_t deviceNum = 0;
|
||||
InitRideSounds(deviceNum);
|
||||
|
||||
for (auto& rideMusicInfo : gRideMusicInfoList)
|
||||
{
|
||||
const utf8* path = context_get_path_legacy(rideMusicInfo.path_id);
|
||||
if (File::Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_OPEN);
|
||||
uint32_t head = fs.ReadValue<uint32_t>();
|
||||
if (head == 0x78787878)
|
||||
{
|
||||
rideMusicInfo.length = 0;
|
||||
}
|
||||
// The length used to be hardcoded, but we stopped doing that to allow replacement.
|
||||
if (rideMusicInfo.length == 0)
|
||||
{
|
||||
rideMusicInfo.length = fs.GetLength();
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
InitRideSounds(0);
|
||||
}
|
||||
|
||||
void InitRideSounds(int32_t device)
|
||||
@@ -394,17 +351,13 @@ namespace OpenRCT2::Audio
|
||||
|
||||
_currentAudioDevice = device;
|
||||
config_save_default();
|
||||
for (auto& rideMusic : gRideMusicList)
|
||||
{
|
||||
rideMusic.ride_id = RIDE_ID_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
peep_stop_crowd_noise();
|
||||
StopTitleMusic();
|
||||
StopRideMusic();
|
||||
RideAudio::StopAllChannels();
|
||||
StopWeatherSound();
|
||||
_currentAudioDevice = -1;
|
||||
}
|
||||
@@ -429,7 +382,7 @@ namespace OpenRCT2::Audio
|
||||
{
|
||||
gGameSoundsOff = true;
|
||||
StopVehicleSounds();
|
||||
StopRideMusic();
|
||||
RideAudio::StopAllChannels();
|
||||
peep_stop_crowd_noise();
|
||||
StopWeatherSound();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/IStream.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -34,6 +35,7 @@ namespace OpenRCT2::Audio
|
||||
virtual void SetOutputDevice(const std::string& deviceName) abstract;
|
||||
|
||||
virtual IAudioSource* CreateStreamFromWAV(const std::string& path) abstract;
|
||||
virtual IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) abstract;
|
||||
|
||||
virtual void StartTitleMusic() abstract;
|
||||
|
||||
|
||||
@@ -129,6 +129,28 @@ void Mixer_Channel_SetGroup(void* channel, MixerGroup group)
|
||||
static_cast<IAudioChannel*>(channel)->SetGroup(group);
|
||||
}
|
||||
|
||||
template<typename T> static void* PlayMusic(T&& src, int32_t loop)
|
||||
{
|
||||
auto* mixer = GetMixer();
|
||||
if (mixer == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto audioContext = GetContext()->GetAudioContext();
|
||||
auto stream = audioContext->CreateStreamFromWAV(std::forward<T&&>(src));
|
||||
if (stream == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto* channel = mixer->Play(stream, loop, false, true);
|
||||
if (channel == nullptr)
|
||||
{
|
||||
delete stream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
channel->SetGroup(MixerGroup::RideMusic);
|
||||
return channel;
|
||||
}
|
||||
|
||||
void* Mixer_Play_Music(int32_t pathId, int32_t loop, int32_t streaming)
|
||||
{
|
||||
IAudioChannel* channel = nullptr;
|
||||
@@ -166,6 +188,16 @@ void* Mixer_Play_Music(int32_t pathId, int32_t loop, int32_t streaming)
|
||||
return channel;
|
||||
}
|
||||
|
||||
void* Mixer_Play_Music(const char* path, int32_t loop)
|
||||
{
|
||||
return PlayMusic(path, loop);
|
||||
}
|
||||
|
||||
void* Mixer_Play_Music(std::unique_ptr<IStream> stream, int32_t loop)
|
||||
{
|
||||
return PlayMusic(std::move(stream), loop);
|
||||
}
|
||||
|
||||
void Mixer_SetVolume(float volume)
|
||||
{
|
||||
GetMixer()->SetVolume(volume);
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../core/IStream.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define MIXER_VOLUME_MAX 128
|
||||
#define MIXER_LOOP_NONE 0
|
||||
@@ -69,6 +72,8 @@ uint64_t Mixer_Channel_GetOffset(void* channel);
|
||||
int32_t Mixer_Channel_SetOffset(void* channel, uint64_t offset);
|
||||
void Mixer_Channel_SetGroup(void* channel, OpenRCT2::Audio::MixerGroup group);
|
||||
void* Mixer_Play_Music(int32_t pathId, int32_t loop, int32_t streaming);
|
||||
void* Mixer_Play_Music(const char* path, int32_t loop);
|
||||
void* Mixer_Play_Music(std::unique_ptr<OpenRCT2::IStream> stream, int32_t loop);
|
||||
void Mixer_SetVolume(float volume);
|
||||
|
||||
int32_t DStoMixerVolume(int32_t volume);
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace OpenRCT2::Audio
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream>) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void StartTitleMusic() override
|
||||
{
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
#include "../common.h"
|
||||
#include "../ride/RideTypes.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct CoordsXYZ;
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
constexpr size_t MaxDeviceNameSize = 256;
|
||||
constexpr size_t MaxRideMusic = 32;
|
||||
constexpr size_t MaxVehicleSounds = 14;
|
||||
constexpr size_t MaxDefaultMusic = 46;
|
||||
constexpr uint16_t SoundIdNull = 0xFFFF;
|
||||
@@ -26,33 +27,6 @@ namespace OpenRCT2::Audio
|
||||
|
||||
enum class SoundId : uint8_t;
|
||||
|
||||
struct RideMusic
|
||||
{
|
||||
ride_id_t ride_id;
|
||||
uint8_t tune_id;
|
||||
int16_t volume;
|
||||
int16_t pan;
|
||||
uint16_t frequency;
|
||||
void* sound_channel;
|
||||
};
|
||||
|
||||
struct RideMusicInfo
|
||||
{
|
||||
uint8_t path_id;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
struct RideMusicParams
|
||||
{
|
||||
ride_id_t ride_id;
|
||||
uint8_t tune_id;
|
||||
int32_t offset;
|
||||
int16_t volume;
|
||||
int16_t pan;
|
||||
uint16_t frequency;
|
||||
};
|
||||
|
||||
struct Sound
|
||||
{
|
||||
SoundId Id;
|
||||
@@ -157,11 +131,6 @@ namespace OpenRCT2::Audio
|
||||
extern void* gTitleMusicChannel;
|
||||
extern void* gWeatherSoundChannel;
|
||||
|
||||
extern RideMusic gRideMusicList[MaxRideMusic];
|
||||
extern RideMusicInfo gRideMusicInfoList[MaxDefaultMusic];
|
||||
extern RideMusicParams gRideMusicParamsList[MaxRideMusic];
|
||||
extern RideMusicParams* gRideMusicParamsListEnd;
|
||||
|
||||
extern VehicleSound gVehicleSoundList[MaxVehicleSounds];
|
||||
|
||||
/**
|
||||
@@ -247,12 +216,6 @@ namespace OpenRCT2::Audio
|
||||
*/
|
||||
void StopWeatherSound();
|
||||
|
||||
/**
|
||||
* Stops ride music from playing.
|
||||
* rct2: 0x006BCA9F
|
||||
*/
|
||||
void StopRideMusic();
|
||||
|
||||
/**
|
||||
* Stops the title music from playing.
|
||||
* rct2: 0x006BD0BD
|
||||
|
||||
@@ -51,6 +51,16 @@ namespace OpenRCT2
|
||||
{
|
||||
}
|
||||
|
||||
MemoryStream::MemoryStream(std::vector<uint8_t>&& v)
|
||||
{
|
||||
_access = MEMORY_ACCESS::OWNER;
|
||||
_dataCapacity = v.size();
|
||||
_dataSize = v.size();
|
||||
_data = Memory::Allocate<void>(v.size());
|
||||
_position = _data;
|
||||
std::memcpy(_data, v.data(), v.size());
|
||||
}
|
||||
|
||||
MemoryStream::MemoryStream(MemoryStream&& mv) noexcept
|
||||
: _access(mv._access)
|
||||
, _dataCapacity(mv._dataCapacity)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "IStream.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
@@ -42,6 +43,7 @@ namespace OpenRCT2
|
||||
explicit MemoryStream(size_t capacity);
|
||||
MemoryStream(void* data, size_t dataSize, uint8_t access = MEMORY_ACCESS::READ);
|
||||
MemoryStream(const void* data, size_t dataSize);
|
||||
MemoryStream(std::vector<uint8_t>&& v);
|
||||
virtual ~MemoryStream();
|
||||
|
||||
MemoryStream& operator=(MemoryStream&& mv) noexcept;
|
||||
|
||||
@@ -30,12 +30,45 @@ namespace Path
|
||||
return safe_strcat_path(buffer, src, bufferSize);
|
||||
}
|
||||
|
||||
std::string Combine(const std::string& a, const std::string& b)
|
||||
static constexpr bool IsPathSeparator(char c)
|
||||
{
|
||||
utf8 buffer[MAX_PATH];
|
||||
String::Set(buffer, sizeof(buffer), a.c_str());
|
||||
Path::Append(buffer, sizeof(buffer), b.c_str());
|
||||
return std::string(buffer);
|
||||
#ifdef _WIN32
|
||||
if (c == '\\')
|
||||
return true;
|
||||
#endif
|
||||
return c == '/';
|
||||
}
|
||||
|
||||
std::string Combine(std::string_view a, std::string_view b)
|
||||
{
|
||||
if (a.empty())
|
||||
return std::string(b);
|
||||
if (b.empty())
|
||||
return std::string(a);
|
||||
auto aEnd = a.back();
|
||||
auto bBegin = b.front();
|
||||
if (IsPathSeparator(aEnd))
|
||||
{
|
||||
if (IsPathSeparator(bBegin))
|
||||
{
|
||||
return std::string(a) + std::string(b.substr(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::string(a) + std::string(b);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsPathSeparator(bBegin))
|
||||
{
|
||||
return std::string(a) + std::string(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::string(a) + PATH_SEPARATOR + std::string(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetDirectory(const std::string& path)
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
namespace Path
|
||||
{
|
||||
utf8* Append(utf8* buffer, size_t bufferSize, const utf8* src);
|
||||
std::string Combine(const std::string& a, const std::string& b);
|
||||
std::string Combine(std::string_view a, std::string_view b);
|
||||
|
||||
template<typename... Args> static std::string Combine(const std::string& a, const std::string& b, Args... args)
|
||||
template<typename... Args> static std::string Combine(std::string_view a, std::string_view b, Args... args)
|
||||
{
|
||||
return Combine(a, Combine(b, args...));
|
||||
}
|
||||
|
||||
@@ -7,12 +7,65 @@
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "Zip.h"
|
||||
|
||||
#include "IStream.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#ifndef __ANDROID__
|
||||
# include "Zip.h"
|
||||
|
||||
# include "IStream.hpp"
|
||||
|
||||
# include <zip.h>
|
||||
#endif
|
||||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
static std::string NormalisePath(std::string_view path)
|
||||
{
|
||||
std::string result;
|
||||
if (!path.empty())
|
||||
{
|
||||
result.reserve(path.size());
|
||||
for (auto ch : path)
|
||||
{
|
||||
if (ch == '\\')
|
||||
{
|
||||
result += '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalises both the given path and the stored paths and finds the first match.
|
||||
*/
|
||||
std::optional<size_t> IZipArchive::GetIndexFromPath(std::string_view path) const
|
||||
{
|
||||
auto normalisedPath = NormalisePath(path);
|
||||
if (!normalisedPath.empty())
|
||||
{
|
||||
auto numFiles = GetNumFiles();
|
||||
for (size_t i = 0; i < numFiles; i++)
|
||||
{
|
||||
auto normalisedZipPath = NormalisePath(GetFileName(i));
|
||||
if (normalisedZipPath == normalisedPath)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool IZipArchive::Exists(std::string_view path) const
|
||||
{
|
||||
return GetIndexFromPath(path).has_value();
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
||||
class ZipArchive final : public IZipArchive
|
||||
{
|
||||
@@ -78,25 +131,37 @@ public:
|
||||
{
|
||||
std::vector<uint8_t> result;
|
||||
auto index = GetIndexFromPath(path);
|
||||
auto dataSize = GetFileSize(index);
|
||||
if (dataSize > 0 && dataSize < SIZE_MAX)
|
||||
if (index)
|
||||
{
|
||||
auto zipFile = zip_fopen_index(_zip, index, 0);
|
||||
if (zipFile != nullptr)
|
||||
auto dataSize = GetFileSize(*index);
|
||||
if (dataSize > 0 && dataSize < SIZE_MAX)
|
||||
{
|
||||
result.resize(static_cast<size_t>(dataSize));
|
||||
uint64_t readBytes = zip_fread(zipFile, result.data(), dataSize);
|
||||
if (readBytes != dataSize)
|
||||
auto zipFile = zip_fopen_index(_zip, *index, 0);
|
||||
if (zipFile != nullptr)
|
||||
{
|
||||
result.clear();
|
||||
result.shrink_to_fit();
|
||||
result.resize(static_cast<size_t>(dataSize));
|
||||
uint64_t readBytes = zip_fread(zipFile, result.data(), dataSize);
|
||||
if (readBytes != dataSize)
|
||||
{
|
||||
result = {};
|
||||
}
|
||||
zip_fclose(zipFile);
|
||||
}
|
||||
zip_fclose(zipFile);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStream> GetFileStream(std::string_view path) const override
|
||||
{
|
||||
auto index = GetIndexFromPath(path);
|
||||
if (index)
|
||||
{
|
||||
return std::make_unique<ZipItemStream>(_zip, *index);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void SetFileData(std::string_view path, std::vector<uint8_t>&& data) override
|
||||
{
|
||||
// Push buffer to an internal list as libzip requires access to it until the zip
|
||||
@@ -106,66 +171,203 @@ public:
|
||||
|
||||
auto source = zip_source_buffer(_zip, writeBuffer.data(), writeBuffer.size(), 0);
|
||||
auto index = GetIndexFromPath(path);
|
||||
if (index == -1)
|
||||
if (index)
|
||||
{
|
||||
zip_add(_zip, path.data(), source);
|
||||
zip_replace(_zip, *index, source);
|
||||
}
|
||||
else
|
||||
{
|
||||
zip_replace(_zip, index, source);
|
||||
zip_add(_zip, path.data(), source);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteFile(std::string_view path) override
|
||||
{
|
||||
auto index = GetIndexFromPath(path);
|
||||
zip_delete(_zip, index);
|
||||
if (index)
|
||||
{
|
||||
zip_delete(_zip, *index);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("File does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
void RenameFile(std::string_view path, std::string_view newPath) override
|
||||
{
|
||||
auto index = GetIndexFromPath(path);
|
||||
zip_file_rename(_zip, index, newPath.data(), ZIP_FL_ENC_GUESS);
|
||||
if (index)
|
||||
{
|
||||
zip_file_rename(_zip, *index, newPath.data(), ZIP_FL_ENC_GUESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("File does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Normalises both the given path and the stored paths and finds the first match.
|
||||
*/
|
||||
zip_int64_t GetIndexFromPath(std::string_view path) const
|
||||
class ZipItemStream final : public IStream
|
||||
{
|
||||
auto normalisedPath = NormalisePath(path);
|
||||
if (!normalisedPath.empty())
|
||||
{
|
||||
auto numFiles = zip_get_num_entries(_zip, 0);
|
||||
for (zip_int64_t i = 0; i < numFiles; i++)
|
||||
{
|
||||
auto normalisedZipPath = NormalisePath(zip_get_name(_zip, i, ZIP_FL_ENC_GUESS));
|
||||
if (normalisedZipPath == normalisedPath)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
private:
|
||||
zip* _zip;
|
||||
zip_int64_t _index;
|
||||
zip_file_t* _zipFile{};
|
||||
zip_uint64_t _len{};
|
||||
zip_uint64_t _pos{};
|
||||
|
||||
static std::string NormalisePath(std::string_view path)
|
||||
{
|
||||
std::string result(path);
|
||||
if (!path.empty())
|
||||
public:
|
||||
ZipItemStream(zip* zip, zip_int64_t index)
|
||||
: _zip(zip)
|
||||
, _index(index)
|
||||
{
|
||||
// Convert back slashes to forward slashes
|
||||
for (auto ch = result.data(); *ch != '\0'; ch++)
|
||||
}
|
||||
|
||||
~ZipItemStream() override
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool CanRead() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanWrite() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t GetLength() const override
|
||||
{
|
||||
return _len;
|
||||
}
|
||||
|
||||
uint64_t GetPosition() const override
|
||||
{
|
||||
return _pos;
|
||||
}
|
||||
|
||||
void SetPosition(uint64_t position) override
|
||||
{
|
||||
if (position > _pos)
|
||||
{
|
||||
if (*ch == '\\')
|
||||
{
|
||||
*ch = '/';
|
||||
}
|
||||
// Read to seek forwards
|
||||
Skip(position - _pos);
|
||||
}
|
||||
else if (position < _pos)
|
||||
{
|
||||
// Can not seek backwards, start from the beginning
|
||||
Reset();
|
||||
Skip(position);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Seek(int64_t offset, int32_t origin) override
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
case STREAM_SEEK_BEGIN:
|
||||
SetPosition(offset);
|
||||
break;
|
||||
case STREAM_SEEK_CURRENT:
|
||||
SetPosition(_pos + offset);
|
||||
break;
|
||||
case STREAM_SEEK_END:
|
||||
SetPosition(_len - offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Read(void* buffer, uint64_t length) override
|
||||
{
|
||||
size_t readBytes = TryRead(buffer, length);
|
||||
if (readBytes != length)
|
||||
{
|
||||
throw IOException("Attempted to read past end of file.");
|
||||
}
|
||||
}
|
||||
|
||||
void Write(const void* buffer, uint64_t length) override
|
||||
{
|
||||
throw IOException("Stream is read-only.");
|
||||
}
|
||||
|
||||
uint64_t TryRead(void* buffer, uint64_t length) override
|
||||
{
|
||||
if (_zipFile == nullptr && !Reset())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto readBytes = zip_fread(_zipFile, buffer, length);
|
||||
if (readBytes < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pos += readBytes;
|
||||
return static_cast<uint64_t>(readBytes);
|
||||
}
|
||||
}
|
||||
|
||||
const void* GetData() const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void Close()
|
||||
{
|
||||
if (_zipFile != nullptr)
|
||||
{
|
||||
zip_fclose(_zipFile);
|
||||
_zipFile = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Reset()
|
||||
{
|
||||
Close();
|
||||
|
||||
_pos = 0;
|
||||
_len = 0;
|
||||
_zipFile = zip_fopen_index(_zip, _index, 0);
|
||||
if (_zipFile == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
zip_stat_t zipFileStat{};
|
||||
if (zip_stat_index(_zip, _index, 0, &zipFileStat) != ZIP_ER_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_len = zipFileStat.size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Skip(zip_int64_t len)
|
||||
{
|
||||
// zip_fseek can not be used on compressed data, so skip bytes by
|
||||
// reading into a temporary buffer
|
||||
char buffer[2048]{};
|
||||
while (len > 0)
|
||||
{
|
||||
auto readLen = std::min<zip_int64_t>(len, sizeof(buffer));
|
||||
auto read = zip_fread(_zipFile, buffer, readLen);
|
||||
if (read <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
_pos += read;
|
||||
len -= read;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
namespace Zip
|
||||
|
||||
@@ -11,10 +11,17 @@
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
struct IStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a zip file.
|
||||
*/
|
||||
@@ -28,6 +35,7 @@ struct IZipArchive
|
||||
virtual std::string GetFileName(size_t index) const abstract;
|
||||
virtual uint64_t GetFileSize(size_t index) const abstract;
|
||||
virtual std::vector<uint8_t> GetFileData(std::string_view path) const abstract;
|
||||
virtual std::unique_ptr<OpenRCT2::IStream> GetFileStream(std::string_view path) const abstract;
|
||||
|
||||
/**
|
||||
* Creates or overwrites a file within the zip archive to the given data buffer.
|
||||
@@ -38,6 +46,9 @@ struct IZipArchive
|
||||
|
||||
virtual void DeleteFile(std::string_view path) abstract;
|
||||
virtual void RenameFile(std::string_view path, std::string_view newPath) abstract;
|
||||
|
||||
std::optional<size_t> GetIndexFromPath(std::string_view path) const;
|
||||
bool Exists(std::string_view path) const;
|
||||
};
|
||||
|
||||
enum class ZIP_ACCESS
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user