diff --git a/src/network/NetworkConnection.h b/src/network/NetworkConnection.h index 0c49b1c0bb..f5e9487449 100644 --- a/src/network/NetworkConnection.h +++ b/src/network/NetworkConnection.h @@ -28,17 +28,19 @@ #include "TcpSocket.h" class NetworkPlayer; +struct ObjectRepositoryItem; class NetworkConnection final { public: - ITcpSocket * Socket = nullptr; - NetworkPacket InboundPacket; - NETWORK_AUTH AuthStatus = NETWORK_AUTH_NONE; - NetworkPlayer * Player = nullptr; - uint32 PingTime = 0; - NetworkKey Key; - std::vector Challenge; + ITcpSocket * Socket = nullptr; + NetworkPacket InboundPacket; + NETWORK_AUTH AuthStatus = NETWORK_AUTH_NONE; + NetworkPlayer * Player = nullptr; + uint32 PingTime = 0; + NetworkKey Key; + std::vector Challenge; + std::vector RequestedObjects; NetworkConnection(); ~NetworkConnection(); diff --git a/src/network/NetworkPacket.cpp b/src/network/NetworkPacket.cpp index f1eaeffa9e..21afdf1993 100644 --- a/src/network/NetworkPacket.cpp +++ b/src/network/NetworkPacket.cpp @@ -68,6 +68,7 @@ bool NetworkPacket::CommandRequiresAuth() case NETWORK_COMMAND_AUTH: case NETWORK_COMMAND_TOKEN: case NETWORK_COMMAND_GAMEINFO: + case NETWORK_COMMAND_OBJECTS: return false; default: return true; diff --git a/src/network/NetworkTypes.h b/src/network/NetworkTypes.h index 0c933fba2b..2dc75b13d6 100644 --- a/src/network/NetworkTypes.h +++ b/src/network/NetworkTypes.h @@ -56,6 +56,7 @@ enum NETWORK_COMMAND NETWORK_COMMAND_GROUPLIST, NETWORK_COMMAND_EVENT, NETWORK_COMMAND_TOKEN, + NETWORK_COMMAND_OBJECTS, NETWORK_COMMAND_MAX, NETWORK_COMMAND_INVALID = -1 }; diff --git a/src/network/network.cpp b/src/network/network.cpp index 7cad35da23..b9e16cf975 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -50,6 +50,8 @@ int _pickup_peep_old_x = SPRITE_LOCATION_NULL; #include "../core/Path.hpp" #include "../core/String.hpp" #include "../core/Util.hpp" +#include "../object/ObjectRepository.h" +#include "../rct2/S6Exporter.h" extern "C" { #include "../config.h" @@ -116,6 +118,7 @@ Network::Network() client_command_handlers[NETWORK_COMMAND_EVENT] = &Network::Client_Handle_EVENT; client_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Client_Handle_GAMEINFO; client_command_handlers[NETWORK_COMMAND_TOKEN] = &Network::Client_Handle_TOKEN; + client_command_handlers[NETWORK_COMMAND_OBJECTS] = &Network::Client_Handle_OBJECTS; server_command_handlers.resize(NETWORK_COMMAND_MAX, 0); server_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Server_Handle_AUTH; server_command_handlers[NETWORK_COMMAND_CHAT] = &Network::Server_Handle_CHAT; @@ -123,6 +126,7 @@ Network::Network() server_command_handlers[NETWORK_COMMAND_PING] = &Network::Server_Handle_PING; server_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Server_Handle_GAMEINFO; server_command_handlers[NETWORK_COMMAND_TOKEN] = &Network::Server_Handle_TOKEN; + server_command_handlers[NETWORK_COMMAND_OBJECTS] = &Network::Server_Handle_OBJECTS; OpenSSL_add_all_algorithms(); } @@ -429,7 +433,7 @@ void Network::UpdateClient() } break; } - case NETWORK_STATUS_CONNECTED: + case SOCKET_STATUS_CONNECTED: { status = NETWORK_STATUS_CONNECTED; server_connection.ResetLastPacketTime(); @@ -858,6 +862,19 @@ void Network::Client_Send_AUTH(const char* name, const char* password, const cha server_connection.QueuePacket(std::move(packet)); } +void Network::Client_Send_OBJECTS(const std::vector &objects) +{ + log_verbose("client requests %u objects", uint32(objects.size())); + std::unique_ptr packet(NetworkPacket::Allocate()); + *packet << (uint32)NETWORK_COMMAND_OBJECTS << (uint32)objects.size(); + for (uint32 i = 0; i < objects.size(); i++) + { + log_verbose("client requests object %s", objects[i].c_str()); + packet->Write((const uint8 *)objects[i].c_str(), 8); + } + server_connection.QueuePacket(std::move(packet)); +} + void Network::Server_Send_TOKEN(NetworkConnection& connection) { std::unique_ptr packet(NetworkPacket::Allocate()); @@ -866,6 +883,20 @@ void Network::Server_Send_TOKEN(NetworkConnection& connection) connection.QueuePacket(std::move(packet)); } +void Network::Server_Send_OBJECTS(NetworkConnection& connection, const std::vector &objects) const +{ + log_verbose("Server sends objects list with %u items", objects.size()); + std::unique_ptr packet(NetworkPacket::Allocate()); + *packet << (uint32)NETWORK_COMMAND_OBJECTS << (uint32)objects.size(); + for (size_t i = 0; i < objects.size(); i++) + { + log_verbose("Object %.8s (checksum %x)", objects[i]->ObjectEntry.name, objects[i]->ObjectEntry.checksum); + packet->Write((const uint8 *)objects[i]->ObjectEntry.name, 8); + *packet << objects[i]->ObjectEntry.checksum << objects[i]->ObjectEntry.flags; + } + connection.QueuePacket(std::move(packet)); +} + void Network::Server_Send_AUTH(NetworkConnection& connection) { uint8 new_playerid = 0; @@ -886,56 +917,22 @@ void Network::Server_Send_AUTH(NetworkConnection& connection) void Network::Server_Send_MAP(NetworkConnection* connection) { - bool RLEState = gUseRLE; - gUseRLE = false; FILE* temp = tmpfile(); if (!temp) { log_warning("Failed to create temporary file to save map."); return; } SDL_RWops* rw = SDL_RWFromFP(temp, SDL_TRUE); - scenario_save_network(rw); - gUseRLE = RLEState; - int size = (int)SDL_RWtell(rw); - std::vector buffer(size); - SDL_RWseek(rw, 0, RW_SEEK_SET); - if (SDL_RWread(rw, &buffer[0], size, 1) == 0) { - log_warning("Failed to read temporary map file into memory."); - SDL_RWclose(rw); + size_t out_size; + unsigned char *header; + header = save_for_network(rw, out_size, connection->RequestedObjects); + SDL_RWclose(rw); + if (header == nullptr) { + connection->SetLastDisconnectReason(STR_MULTIPLAYER_CONNECTION_CLOSED); + connection->Socket->Disconnect(); return; } size_t chunksize = 65000; - size_t out_size = size; - unsigned char *compressed = util_zlib_deflate(&buffer[0], size, &out_size); - unsigned char *header; - if (compressed != NULL) - { - header = (unsigned char *)_strdup("open2_sv6_zlib"); - size_t header_len = strlen((char *)header) + 1; // account for null terminator - header = (unsigned char *)realloc(header, header_len + out_size); - if (header == nullptr) { - log_error("Failed to allocate %u bytes.", header_len + out_size); - connection->SetLastDisconnectReason(STR_MULTIPLAYER_CONNECTION_CLOSED); - connection->Socket->Disconnect(); - free(compressed); - return; - } - memcpy(&header[header_len], compressed, out_size); - out_size += header_len; - free(compressed); - log_verbose("Sending map of size %u bytes, compressed to %u bytes", size, out_size); - } else { - log_warning("Failed to compress the data, falling back to non-compressed sv6."); - header = (unsigned char *)malloc(size); - if (header == nullptr) { - log_error("Failed to allocate %u bytes.", size); - connection->SetLastDisconnectReason(STR_MULTIPLAYER_CONNECTION_CLOSED); - connection->Socket->Disconnect(); - return; - } - out_size = size; - memcpy(header, &buffer[0], size); - } for (size_t i = 0; i < out_size; i += chunksize) { size_t datasize = Math::Min(chunksize, out_size - i); std::unique_ptr packet(NetworkPacket::Allocate()); @@ -948,7 +945,48 @@ void Network::Server_Send_MAP(NetworkConnection* connection) } } free(header); - SDL_RWclose(rw); +} + +unsigned char * Network::save_for_network(SDL_RWops *rw_buffer, size_t &out_size, const std::vector &objects) const +{ + unsigned char * header = nullptr; + out_size = 0; + bool RLEState = gUseRLE; + gUseRLE = false; + scenario_save_network(rw_buffer, objects); + gUseRLE = RLEState; + int size = (int)SDL_RWtell(rw_buffer); + std::vector buffer(size); + SDL_RWseek(rw_buffer, 0, RW_SEEK_SET); + if (SDL_RWread(rw_buffer, &buffer[0], size, 1) == 0) { + log_warning("Failed to read temporary map file into memory."); + return nullptr; + } + unsigned char *compressed = util_zlib_deflate(&buffer[0], size, &out_size); + if (compressed != NULL) + { + header = (unsigned char *)_strdup("open2_sv6_zlib"); + size_t header_len = strlen((char *)header) + 1; // account for null terminator + header = (unsigned char *)realloc(header, header_len + out_size); + if (header == nullptr) { + log_error("Failed to allocate %u bytes.", header_len + out_size); + } else { + memcpy(&header[header_len], compressed, out_size); + out_size += header_len; + log_verbose("Sending map of size %u bytes, compressed to %u bytes", size, out_size); + } + free(compressed); + } else { + log_warning("Failed to compress the data, falling back to non-compressed sv6."); + header = (unsigned char *)malloc(size); + if (header == nullptr) { + log_error("Failed to allocate %u bytes.", size); + } else { + out_size = size; + memcpy(header, &buffer[0], size); + } + } + return header; } void Network::Client_Send_CHAT(const char* text) @@ -1418,10 +1456,8 @@ void Network::Server_Client_Joined(const char* name, const std::string &keyhash, const char * player_name = (const char *) player->name.c_str(); format_string(text, 256, STR_MULTIPLAYER_PLAYER_HAS_JOINED_THE_GAME, &player_name); chat_history_add(text); - Server_Send_MAP(&connection); - gNetwork.Server_Send_EVENT_PLAYER_JOINED(player_name); - Server_Send_GROUPLIST(connection); - Server_Send_PLAYERLIST(); + std::vector objects = scenario_get_packable_objects(); + Server_Send_OBJECTS(connection, objects); } } @@ -1435,6 +1471,62 @@ void Network::Server_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& Server_Send_TOKEN(connection); } +void Network::Client_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet) +{ + IObjectRepository * repo = GetObjectRepository(); + uint32 size; + packet >> size; + log_verbose("client received object list, it has %u entries", size); + std::vector requested_objects; + for (uint32 i = 0; i < size; i++) + { + const char * name = (const char *)packet.Read(8); + // Required, as packet has no null terminators. + std::string s(name, name + 8); + uint32 checksum, flags; + packet >> checksum >> flags; + const ObjectRepositoryItem * ori = repo->FindObject(s.c_str()); + // This could potentially request the object if checksums don't match, but since client + // won't replace its version with server-provided one, we don't do that. + if (ori == nullptr) { + log_verbose("Requesting object %s with checksum %x from server", + s.c_str(), checksum); + requested_objects.push_back(s); + } else if (ori->ObjectEntry.checksum != checksum || ori->ObjectEntry.flags != flags) { + log_warning("Object %s has different checksum/flags (%x/%x) than server (%x/%x).", + s.c_str(), ori->ObjectEntry.checksum, ori->ObjectEntry.flags, checksum, flags); + } + } + Client_Send_OBJECTS(requested_objects); +} + +void Network::Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet) +{ + uint32 size; + packet >> size; + log_verbose("Client requested %u objects", size); + IObjectRepository * repo = GetObjectRepository(); + for (uint32 i = 0; i < size; i++) + { + const char * name = (const char *)packet.Read(8); + // This is required, as packet does not have null terminator + std::string s(name, name + 8); + log_verbose("Client requested object %s", s.c_str()); + const ObjectRepositoryItem * item = repo->FindObject(s.c_str()); + if (item == nullptr) { + log_warning("Client tried getting non-existent object %s from us.", s.c_str()); + } else { + connection.RequestedObjects.push_back(item); + } + } + + const char * player_name = (const char *) connection.Player->name.c_str(); + Server_Send_MAP(&connection); + gNetwork.Server_Send_EVENT_PLAYER_JOINED(player_name); + Server_Send_GROUPLIST(connection); + Server_Send_PLAYERLIST(); +} + void Network::Server_Handle_AUTH(NetworkConnection& connection, NetworkPacket& packet) { if (connection.AuthStatus != NETWORK_AUTH_OK) { diff --git a/src/network/network.h b/src/network/network.h index 2ef7ee687a..7d1b62f0ca 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -55,7 +55,7 @@ extern "C" { // This define specifies which version of network stream current build uses. // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "15" +#define NETWORK_STREAM_VERSION "16" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION #ifdef __cplusplus @@ -83,6 +83,8 @@ enum { NETWORK_TICK_FLAG_CHECKSUMS = 1 << 0, }; +struct ObjectRepositoryItem; + class Network { public: @@ -142,6 +144,8 @@ public: void Server_Send_EVENT_PLAYER_JOINED(const char *playerName); void Server_Send_EVENT_PLAYER_DISCONNECTED(const char *playerName, const char *reason); void Client_Send_GAMEINFO(); + void Client_Send_OBJECTS(const std::vector &objects); + void Server_Send_OBJECTS(NetworkConnection& connection, const std::vector &objects) const; std::vector> player_list; std::vector> group_list; @@ -236,6 +240,10 @@ private: void Client_Handle_EVENT(NetworkConnection& connection, NetworkPacket& packet); void Client_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet); void Server_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet); + void Client_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet); + void Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet); + + unsigned char * save_for_network(SDL_RWops *buffer, size_t &out_size, const std::vector &objects) const; }; namespace Convert diff --git a/src/rct2/S6Exporter.cpp b/src/rct2/S6Exporter.cpp index 5a572a1314..e6bcd7f81a 100644 --- a/src/rct2/S6Exporter.cpp +++ b/src/rct2/S6Exporter.cpp @@ -17,6 +17,8 @@ #include "../core/Exception.hpp" #include "../core/IStream.hpp" #include "../core/String.hpp" +#include "../object/ObjectRepository.h" +#include "../object/Object.h" #include "S6Exporter.h" extern "C" @@ -46,7 +48,6 @@ extern "C" S6Exporter::S6Exporter() { - ExportObjects = false; RemoveTracklessRides = false; memset(&_s6, 0, sizeof(_s6)); } @@ -90,7 +91,7 @@ void S6Exporter::SaveScenario(SDL_RWops *rw) void S6Exporter::Save(SDL_RWops * rw, bool isScenario) { _s6.header.type = isScenario ? S6_TYPE_SCENARIO : S6_TYPE_SAVEDGAME; - _s6.header.num_packed_objects = ExportObjects ? scenario_get_num_packed_objects_to_write() : 0; + _s6.header.num_packed_objects = uint16(ExportObjectsList.size()); _s6.header.version = S6_RCT2_VERSION; _s6.header.magic_number = S6_MAGIC_NUMBER; @@ -121,10 +122,11 @@ void S6Exporter::Save(SDL_RWops * rw, bool isScenario) SDL_RWwrite(rw, buffer, encodedLength, 1); } + log_warning("exporting %u objects", _s6.header.num_packed_objects); // 2: Write packed objects if (_s6.header.num_packed_objects > 0) { - if (!scenario_write_packed_objects(rw)) + if (!scenario_write_packed_objects(rw, ExportObjectsList)) { free(buffer); throw Exception("Unable to pack objects."); @@ -330,7 +332,7 @@ void S6Exporter::Export() _s6.current_expenditure = gCurrentExpenditure; _s6.current_profit = gCurrentProfit; _s6.weekly_profit_average_dividend = gWeeklyProfitAverageDividend; - _s6.weekly_profit_average_divisor = gWeeklyProfitAverageDivisor; + _s6.weekly_profit_average_divisor = gWeeklyProfitAverageDivisor; // pad_0135833A memcpy(_s6.weekly_profit_history, gWeeklyProfitHistory, sizeof(_s6.weekly_profit_history)); @@ -451,6 +453,112 @@ uint32 S6Exporter::GetLoanHash(money32 initialCash, money32 bankLoan, uint32 max return value; } + +// Save game state without modifying any of the state for multiplayer +int scenario_save_network(SDL_RWops * rw, const std::vector &objects) +{ + viewport_set_saved_view(); + + bool result = false; + auto s6exporter = new S6Exporter(); + try + { + s6exporter->ExportObjectsList = objects; + s6exporter->Export(); + s6exporter->SaveGame(rw); + result = true; + } + catch (Exception) + { + } + delete s6exporter; + + if (!result) + { + return 0; + } + + // Write other data not in normal save files + SDL_RWwrite(rw, gSpriteSpatialIndex, 0x10001 * sizeof(uint16), 1); + SDL_WriteLE32(rw, gGamePaused); + SDL_WriteLE32(rw, _guestGenerationProbability); + SDL_WriteLE32(rw, _suggestedGuestMaximum); + SDL_WriteU8(rw, gCheatsSandboxMode); + SDL_WriteU8(rw, gCheatsDisableClearanceChecks); + SDL_WriteU8(rw, gCheatsDisableSupportLimits); + SDL_WriteU8(rw, gCheatsDisableTrainLengthLimit); + SDL_WriteU8(rw, gCheatsEnableChainLiftOnAllTrack); + SDL_WriteU8(rw, gCheatsShowAllOperatingModes); + SDL_WriteU8(rw, gCheatsShowVehiclesFromOtherTrackTypes); + SDL_WriteU8(rw, gCheatsFastLiftHill); + SDL_WriteU8(rw, gCheatsDisableBrakesFailure); + SDL_WriteU8(rw, gCheatsDisableAllBreakdowns); + SDL_WriteU8(rw, gCheatsUnlockAllPrices); + SDL_WriteU8(rw, gCheatsBuildInPauseMode); + SDL_WriteU8(rw, gCheatsIgnoreRideIntensity); + SDL_WriteU8(rw, gCheatsDisableVandalism); + SDL_WriteU8(rw, gCheatsDisableLittering); + SDL_WriteU8(rw, gCheatsNeverendingMarketing); + SDL_WriteU8(rw, gCheatsFreezeClimate); + SDL_WriteU8(rw, gCheatsDisablePlantAging); + SDL_WriteU8(rw, gCheatsAllowArbitraryRideTypeChanges); + + gfx_invalidate_screen(); + return 1; +} + +static bool object_is_custom(const ObjectRepositoryItem * object) +{ + Guard::ArgumentNotNull(object); + + // Validate the object is not one from base game or expansion pack + return (object->LoadedObject != nullptr && + object->LoadedObject->GetLegacyData() != nullptr + && !(object->ObjectEntry.flags & 0xF0)); +} + +int scenario_write_packed_objects(SDL_RWops* rw, std::vector &objects) +{ + log_verbose("exporting packed objects"); + for (const auto &object : objects) + { + Guard::ArgumentNotNull(object); + log_verbose("exporting object %.8s", object->ObjectEntry.name); + if (object_is_custom(object)) + { + if (!object_saved_packed(rw, &object->ObjectEntry)) + { + return 0; + } + } + else + { + log_warning("Refusing to pack vanilla/expansion object \"%s\"", object->ObjectEntry.name); + } + } + return 1; +} + +/** + * + * rct2: 0x006AA244 + */ +std::vector scenario_get_packable_objects() +{ + std::vector objects; + IObjectRepository * repo = GetObjectRepository(); + for (size_t i = 0; i < repo->GetNumObjects(); i++) + { + const ObjectRepositoryItem *item = &repo->GetObjects()[i]; + // Validate the object is not one from base game or expansion pack + if (object_is_custom(item)) + { + objects.push_back(item); + } + } + return objects; +} + extern "C" { enum { @@ -489,7 +597,9 @@ extern "C" auto s6exporter = new S6Exporter(); try { - s6exporter->ExportObjects = (flags & S6_SAVE_FLAG_EXPORT); + if (flags & S6_SAVE_FLAG_EXPORT) { + s6exporter->ExportObjectsList = scenario_get_packable_objects(); + } s6exporter->RemoveTracklessRides = true; s6exporter->Export(); if (flags & S6_SAVE_FLAG_SCENARIO) @@ -515,57 +625,4 @@ extern "C" } return result; } - - // Save game state without modifying any of the state for multiplayer - int scenario_save_network(SDL_RWops * rw) - { - viewport_set_saved_view(); - - bool result = false; - auto s6exporter = new S6Exporter(); - try - { - s6exporter->ExportObjects = true; - s6exporter->Export(); - s6exporter->SaveGame(rw); - result = true; - } - catch (Exception) - { - } - delete s6exporter; - - if (!result) - { - return 0; - } - - // Write other data not in normal save files - SDL_RWwrite(rw, gSpriteSpatialIndex, 0x10001 * sizeof(uint16), 1); - SDL_WriteLE32(rw, gGamePaused); - SDL_WriteLE32(rw, _guestGenerationProbability); - SDL_WriteLE32(rw, _suggestedGuestMaximum); - SDL_WriteU8(rw, gCheatsSandboxMode); - SDL_WriteU8(rw, gCheatsDisableClearanceChecks); - SDL_WriteU8(rw, gCheatsDisableSupportLimits); - SDL_WriteU8(rw, gCheatsDisableTrainLengthLimit); - SDL_WriteU8(rw, gCheatsEnableChainLiftOnAllTrack); - SDL_WriteU8(rw, gCheatsShowAllOperatingModes); - SDL_WriteU8(rw, gCheatsShowVehiclesFromOtherTrackTypes); - SDL_WriteU8(rw, gCheatsFastLiftHill); - SDL_WriteU8(rw, gCheatsDisableBrakesFailure); - SDL_WriteU8(rw, gCheatsDisableAllBreakdowns); - SDL_WriteU8(rw, gCheatsUnlockAllPrices); - SDL_WriteU8(rw, gCheatsBuildInPauseMode); - SDL_WriteU8(rw, gCheatsIgnoreRideIntensity); - SDL_WriteU8(rw, gCheatsDisableVandalism); - SDL_WriteU8(rw, gCheatsDisableLittering); - SDL_WriteU8(rw, gCheatsNeverendingMarketing); - SDL_WriteU8(rw, gCheatsFreezeClimate); - SDL_WriteU8(rw, gCheatsDisablePlantAging); - SDL_WriteU8(rw, gCheatsAllowArbitraryRideTypeChanges); - - gfx_invalidate_screen(); - return 1; - } } diff --git a/src/rct2/S6Exporter.h b/src/rct2/S6Exporter.h index 9cba306ddf..96cba63b6e 100644 --- a/src/rct2/S6Exporter.h +++ b/src/rct2/S6Exporter.h @@ -16,6 +16,9 @@ #pragma once +#include +#include + #include "../common.h" extern "C" @@ -24,14 +27,20 @@ extern "C" #include "../object_list.h" } +struct ObjectRepositoryItem; + +int scenario_save_network(SDL_RWops* rw, const std::vector &objects); +int scenario_write_packed_objects(SDL_RWops* rw, std::vector &objects); +std::vector scenario_get_packable_objects(); + /** * Class to export RollerCoaster Tycoon 2 scenarios (*.SC6) and saved games (*.SV6). */ class S6Exporter final { public: - bool ExportObjects; bool RemoveTracklessRides; + std::vector ExportObjectsList; S6Exporter(); diff --git a/src/scenario.c b/src/scenario.c index c5689d500b..5bd2f30e26 100644 --- a/src/scenario.c +++ b/src/scenario.c @@ -685,41 +685,6 @@ int scenario_prepare_for_save() return 1; } -/** - * - * rct2: 0x006AA244 - */ -int scenario_get_num_packed_objects_to_write() -{ - int count = 0; - for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) { - const rct_object_entry *entry = get_loaded_object_entry(i); - void *entryData = get_loaded_object_chunk(i); - if (entryData != (void*)-1 && !(entry->flags & 0xF0)) { - count++; - } - } - return count; -} - -/** - * - * rct2: 0x006AA26E - */ -int scenario_write_packed_objects(SDL_RWops* rw) -{ - for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) { - const rct_object_entry *entry = get_loaded_object_entry(i); - void *entryData = get_loaded_object_chunk(i); - if (entryData != (void*)-1 && !(entry->flags & 0xF0)) { - if (!object_saved_packed(rw, entry)) { - return 0; - } - } - } - return 1; -} - /** * * rct2: 0x006AA039 diff --git a/src/scenario.h b/src/scenario.h index 15f486c31b..5b4531ebc4 100644 --- a/src/scenario.h +++ b/src/scenario.h @@ -416,9 +416,6 @@ unsigned int scenario_rand(); unsigned int scenario_rand_max(unsigned int max); int scenario_prepare_for_save(); int scenario_save(SDL_RWops* rw, int flags); -int scenario_save_network(SDL_RWops* rw); -int scenario_get_num_packed_objects_to_write(); -int scenario_write_packed_objects(SDL_RWops* rw); void scenario_remove_trackless_rides(rct_s6_data *s6); void scenario_fix_ghosts(rct_s6_data *s6); void scenario_set_filename(const char *value);