1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include "RCT12.h"
11
12 #include "../core/String.hpp"
13 #include "../localisation/Formatting.h"
14 #include "../localisation/Localisation.h"
15 #include "../ride/Track.h"
16 #include "../scenario/Scenario.h"
17 #include "../world/Banner.h"
18 #include "../world/Footpath.h"
19 #include "../world/LargeScenery.h"
20 #include "../world/SmallScenery.h"
21 #include "../world/Surface.h"
22 #include "../world/TileElement.h"
23 #include "../world/Wall.h"
24
25 using namespace OpenRCT2;
26
GetType() const27 uint8_t RCT12TileElementBase::GetType() const
28 {
29 return this->type & TILE_ELEMENT_TYPE_MASK;
30 }
31
GetDirection() const32 uint8_t RCT12TileElementBase::GetDirection() const
33 {
34 return this->type & TILE_ELEMENT_DIRECTION_MASK;
35 }
36
GetOccupiedQuadrants() const37 uint8_t RCT12TileElementBase::GetOccupiedQuadrants() const
38 {
39 return flags & TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK;
40 }
41
SetOccupiedQuadrants(uint8_t quadrants)42 void RCT12TileElementBase::SetOccupiedQuadrants(uint8_t quadrants)
43 {
44 flags &= ~TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK;
45 flags |= (quadrants & TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK);
46 }
47
IsLastForTile() const48 bool RCT12TileElementBase::IsLastForTile() const
49 {
50 return (this->flags & RCT12_TILE_ELEMENT_FLAG_LAST_TILE) != 0;
51 }
52
IsGhost() const53 bool RCT12TileElementBase::IsGhost() const
54 {
55 return (this->flags & RCT12_TILE_ELEMENT_FLAG_GHOST) != 0;
56 }
57
SetLastForTile(bool on)58 void RCT12TileElementBase::SetLastForTile(bool on)
59 {
60 if (on)
61 flags |= RCT12_TILE_ELEMENT_FLAG_LAST_TILE;
62 else
63 flags &= ~RCT12_TILE_ELEMENT_FLAG_LAST_TILE;
64 }
65
SetGhost(bool isGhost)66 void RCT12TileElementBase::SetGhost(bool isGhost)
67 {
68 if (isGhost)
69 {
70 this->flags |= RCT12_TILE_ELEMENT_FLAG_GHOST;
71 }
72 else
73 {
74 this->flags &= ~RCT12_TILE_ELEMENT_FLAG_GHOST;
75 }
76 }
77
GetSlope() const78 uint8_t RCT12SurfaceElement::GetSlope() const
79 {
80 return (slope & TILE_ELEMENT_SURFACE_SLOPE_MASK);
81 }
82
GetSurfaceStyle() const83 uint32_t RCT12SurfaceElement::GetSurfaceStyle() const
84 {
85 uint32_t retVal = (terrain >> 5) & 7;
86 retVal |= (type & RCT12_SURFACE_ELEMENT_TYPE_SURFACE_MASK) << 3;
87 return retVal;
88 }
89
GetEdgeStyle() const90 uint32_t RCT12SurfaceElement::GetEdgeStyle() const
91 {
92 uint32_t terrain_edge = (slope >> 5) & 7;
93 if (type & 128)
94 terrain_edge |= (1 << 3);
95 return terrain_edge;
96 }
97
GetGrassLength() const98 uint8_t RCT12SurfaceElement::GetGrassLength() const
99 {
100 return grass_length;
101 }
102
GetOwnership() const103 uint8_t RCT12SurfaceElement::GetOwnership() const
104 {
105 return (ownership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK);
106 }
107
GetWaterHeight() const108 uint32_t RCT12SurfaceElement::GetWaterHeight() const
109 {
110 return (terrain & RCT12_TILE_ELEMENT_SURFACE_WATER_HEIGHT_MASK) * 16;
111 }
112
GetParkFences() const113 uint8_t RCT12SurfaceElement::GetParkFences() const
114 {
115 return (ownership & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK);
116 }
117
HasTrackThatNeedsWater() const118 bool RCT12SurfaceElement::HasTrackThatNeedsWater() const
119 {
120 return (type & SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER) != 0;
121 }
122
GetEntryIndex() const123 uint8_t RCT12PathElement::GetEntryIndex() const
124 {
125 return (entryIndex & RCT12_FOOTPATH_PROPERTIES_TYPE_MASK) >> 4;
126 }
127
GetQueueBannerDirection() const128 uint8_t RCT12PathElement::GetQueueBannerDirection() const
129 {
130 return ((type & FOOTPATH_ELEMENT_TYPE_DIRECTION_MASK) >> 6);
131 }
132
IsSloped() const133 bool RCT12PathElement::IsSloped() const
134 {
135 return (entryIndex & RCT12_FOOTPATH_PROPERTIES_FLAG_IS_SLOPED) != 0;
136 }
137
GetSlopeDirection() const138 uint8_t RCT12PathElement::GetSlopeDirection() const
139 {
140 return entryIndex & RCT12_FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK;
141 }
142
GetRideIndex() const143 uint8_t RCT12PathElement::GetRideIndex() const
144 {
145 return rideIndex;
146 }
147
GetStationIndex() const148 uint8_t RCT12PathElement::GetStationIndex() const
149 {
150 return (additions & RCT12_FOOTPATH_PROPERTIES_ADDITIONS_STATION_INDEX_MASK) >> 4;
151 }
152
IsWide() const153 bool RCT12PathElement::IsWide() const
154 {
155 return (type & FOOTPATH_ELEMENT_TYPE_FLAG_IS_WIDE) != 0;
156 }
157
IsQueue() const158 bool RCT12PathElement::IsQueue() const
159 {
160 return (type & FOOTPATH_ELEMENT_TYPE_FLAG_IS_QUEUE) != 0;
161 }
162
HasQueueBanner() const163 bool RCT12PathElement::HasQueueBanner() const
164 {
165 return (entryIndex & RCT12_FOOTPATH_PROPERTIES_FLAG_HAS_QUEUE_BANNER) != 0;
166 }
GetEdges() const167 uint8_t RCT12PathElement::GetEdges() const
168 {
169 return edges & FOOTPATH_PROPERTIES_EDGES_EDGES_MASK;
170 }
171
GetCorners() const172 uint8_t RCT12PathElement::GetCorners() const
173 {
174 return edges >> 4;
175 }
176
GetAddition() const177 uint8_t RCT12PathElement::GetAddition() const
178 {
179 return additions & RCT12_FOOTPATH_PROPERTIES_ADDITIONS_TYPE_MASK;
180 }
181
AdditionIsGhost() const182 bool RCT12PathElement::AdditionIsGhost() const
183 {
184 return (additions & RCT12_FOOTPATH_PROPERTIES_ADDITIONS_FLAG_GHOST) != 0;
185 }
186
GetAdditionStatus() const187 uint8_t RCT12PathElement::GetAdditionStatus() const
188 {
189 return additionStatus;
190 }
191
GetRCT1PathType() const192 uint8_t RCT12PathElement::GetRCT1PathType() const
193 {
194 uint8_t pathColour = type & 3;
195 uint8_t pathType2 = (entryIndex & RCT12_FOOTPATH_PROPERTIES_TYPE_MASK) >> 2;
196
197 pathType2 = pathType2 | pathColour;
198 return pathType2;
199 }
200
GetRCT1SupportType() const201 uint8_t RCT12PathElement::GetRCT1SupportType() const
202 {
203 return (flags & 0b01100000) >> 5;
204 }
205
GetTrackType() const206 uint8_t RCT12TrackElement::GetTrackType() const
207 {
208 return trackType;
209 }
210
GetSequenceIndex() const211 uint8_t RCT12TrackElement::GetSequenceIndex() const
212 {
213 return sequence & RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK;
214 }
215
GetRideIndex() const216 uint8_t RCT12TrackElement::GetRideIndex() const
217 {
218 return rideIndex;
219 }
220
GetColourScheme() const221 uint8_t RCT12TrackElement::GetColourScheme() const
222 {
223 return colour & 0x3;
224 }
225
GetStationIndex() const226 uint8_t RCT12TrackElement::GetStationIndex() const
227 {
228 if (track_type_is_station(trackType) || trackType == TrackElemType::TowerBase)
229 {
230 return (sequence & RCT12_TRACK_ELEMENT_SEQUENCE_STATION_INDEX_MASK) >> 4;
231 }
232 return 0;
233 }
234
HasChain() const235 bool RCT12TrackElement::HasChain() const
236 {
237 return type & RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT;
238 }
239
HasCableLift() const240 bool RCT12TrackElement::HasCableLift() const
241 {
242 return colour & RCT12_TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT;
243 }
244
IsInverted() const245 bool RCT12TrackElement::IsInverted() const
246 {
247 return colour & RCT12_TRACK_ELEMENT_COLOUR_FLAG_INVERTED;
248 }
249
GetBrakeBoosterSpeed() const250 uint8_t RCT12TrackElement::GetBrakeBoosterSpeed() const
251 {
252 if (TrackTypeHasSpeedSetting(GetTrackType()))
253 {
254 return (sequence >> 4) << 1;
255 }
256 return 0;
257 }
258
HasGreenLight() const259 bool RCT12TrackElement::HasGreenLight() const
260 {
261 if (track_type_is_station(trackType))
262 {
263 return (sequence & MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT) != 0;
264 }
265 return false;
266 }
267
GetSeatRotation() const268 uint8_t RCT12TrackElement::GetSeatRotation() const
269 {
270 return colour >> 4;
271 }
272
GetMazeEntry() const273 uint16_t RCT12TrackElement::GetMazeEntry() const
274 {
275 return mazeEntry;
276 }
277
GetPhotoTimeout() const278 uint8_t RCT12TrackElement::GetPhotoTimeout() const
279 {
280 if (GetTrackType() == TrackElemType::OnRidePhoto)
281 {
282 return sequence >> 4;
283 }
284 return 0;
285 }
286
GetDoorAState() const287 uint8_t RCT12TrackElement::GetDoorAState() const
288 {
289 return (colour & RCT12_TRACK_ELEMENT_DOOR_A_MASK) >> 2;
290 }
291
GetDoorBState() const292 uint8_t RCT12TrackElement::GetDoorBState() const
293 {
294 return (colour & RCT12_TRACK_ELEMENT_DOOR_B_MASK) >> 5;
295 }
296
SetDoorAState(uint8_t newState)297 void RCT12TrackElement::SetDoorAState(uint8_t newState)
298 {
299 colour &= ~RCT12_TRACK_ELEMENT_DOOR_B_MASK;
300 colour |= ((newState << 2) & RCT12_TRACK_ELEMENT_DOOR_B_MASK);
301 }
302
SetDoorBState(uint8_t newState)303 void RCT12TrackElement::SetDoorBState(uint8_t newState)
304 {
305 colour &= ~RCT12_TRACK_ELEMENT_DOOR_B_MASK;
306 colour |= ((newState << 5) & RCT12_TRACK_ELEMENT_DOOR_B_MASK);
307 }
308
IsIndestructible() const309 bool RCT12TrackElement::IsIndestructible() const
310 {
311 return (flags & RCT12_TILE_ELEMENT_FLAG_INDESTRUCTIBLE_TRACK_PIECE) != 0;
312 }
313
SetIsIndestructible(bool isIndestructible)314 void RCT12TrackElement::SetIsIndestructible(bool isIndestructible)
315 {
316 if (isIndestructible)
317 {
318 flags |= RCT12_TILE_ELEMENT_FLAG_INDESTRUCTIBLE_TRACK_PIECE;
319 }
320 else
321 {
322 flags &= ~RCT12_TILE_ELEMENT_FLAG_INDESTRUCTIBLE_TRACK_PIECE;
323 }
324 }
325
GetEntryIndex() const326 uint8_t RCT12SmallSceneryElement::GetEntryIndex() const
327 {
328 return this->entryIndex;
329 }
330
GetAge() const331 uint8_t RCT12SmallSceneryElement::GetAge() const
332 {
333 return this->age;
334 }
335
GetSceneryQuadrant() const336 uint8_t RCT12SmallSceneryElement::GetSceneryQuadrant() const
337 {
338 return (this->type & TILE_ELEMENT_QUADRANT_MASK) >> 6;
339 }
340
GetPrimaryColour() const341 colour_t RCT12SmallSceneryElement::GetPrimaryColour() const
342 {
343 return colour_1 & TILE_ELEMENT_COLOUR_MASK;
344 }
345
GetSecondaryColour() const346 colour_t RCT12SmallSceneryElement::GetSecondaryColour() const
347 {
348 return colour_2 & TILE_ELEMENT_COLOUR_MASK;
349 }
350
NeedsSupports() const351 bool RCT12SmallSceneryElement::NeedsSupports() const
352 {
353 return colour_1 & MAP_ELEM_SMALL_SCENERY_COLOUR_FLAG_NEEDS_SUPPORTS;
354 }
355
GetEntryIndex() const356 uint32_t RCT12LargeSceneryElement::GetEntryIndex() const
357 {
358 return entryIndex & RCT12_TILE_ELEMENT_LARGE_TYPE_MASK;
359 }
360
GetSequenceIndex() const361 uint16_t RCT12LargeSceneryElement::GetSequenceIndex() const
362 {
363 return (entryIndex >> 10);
364 }
GetPrimaryColour() const365 colour_t RCT12LargeSceneryElement::GetPrimaryColour() const
366 {
367 return colour[0] & TILE_ELEMENT_COLOUR_MASK;
368 }
369
GetSecondaryColour() const370 colour_t RCT12LargeSceneryElement::GetSecondaryColour() const
371 {
372 return colour[1] & TILE_ELEMENT_COLOUR_MASK;
373 }
374
GetBannerIndex() const375 uint8_t RCT12LargeSceneryElement::GetBannerIndex() const
376 {
377 return (type & 0xC0) | (((colour[0]) & ~TILE_ELEMENT_COLOUR_MASK) >> 2) | (((colour[1]) & ~TILE_ELEMENT_COLOUR_MASK) >> 5);
378 }
379
GetEntryIndex() const380 uint8_t RCT12WallElement::GetEntryIndex() const
381 {
382 return entryIndex;
383 }
384
GetSlope() const385 uint8_t RCT12WallElement::GetSlope() const
386 {
387 return (type & TILE_ELEMENT_QUADRANT_MASK) >> 6;
388 }
389
GetPrimaryColour() const390 colour_t RCT12WallElement::GetPrimaryColour() const
391 {
392 return colour_1 & TILE_ELEMENT_COLOUR_MASK;
393 }
394
GetSecondaryColour() const395 colour_t RCT12WallElement::GetSecondaryColour() const
396 {
397 uint8_t secondaryColour = (colour_1 & ~TILE_ELEMENT_COLOUR_MASK) >> 5;
398 secondaryColour |= (flags & 0x60) >> 2;
399 return secondaryColour;
400 }
401
GetTertiaryColour() const402 colour_t RCT12WallElement::GetTertiaryColour() const
403 {
404 return colour_3 & TILE_ELEMENT_COLOUR_MASK;
405 }
406
GetAnimationFrame() const407 uint8_t RCT12WallElement::GetAnimationFrame() const
408 {
409 return (animation >> 3) & 0xF;
410 }
411
GetBannerIndex() const412 uint8_t RCT12WallElement::GetBannerIndex() const
413 {
414 return banner_index;
415 }
416
IsAcrossTrack() const417 bool RCT12WallElement::IsAcrossTrack() const
418 {
419 return (animation & WALL_ANIMATION_FLAG_ACROSS_TRACK) != 0;
420 }
421
AnimationIsBackwards() const422 bool RCT12WallElement::AnimationIsBackwards() const
423 {
424 return (animation & WALL_ANIMATION_FLAG_DIRECTION_BACKWARD) != 0;
425 }
426
GetRCT1WallType(int32_t edge) const427 int32_t RCT12WallElement::GetRCT1WallType(int32_t edge) const
428 {
429 uint8_t var_05 = colour_3;
430 uint16_t var_06 = colour_1 | (animation << 8);
431
432 int32_t typeA = (var_05 >> (edge * 2)) & 3;
433 int32_t typeB = (var_06 >> (edge * 4)) & 0x0F;
434
435 if (typeB != 0x0F)
436 {
437 return typeA | (typeB << 2);
438 }
439
440 return -1;
441 }
442
GetRCT1WallColour() const443 colour_t RCT12WallElement::GetRCT1WallColour() const
444 {
445 return ((type & 0xC0) >> 3) | ((entryIndex & 0xE0) >> 5);
446 }
447
GetRCT1Slope() const448 uint8_t RCT12WallElement::GetRCT1Slope() const
449 {
450 return entryIndex & 0b00011111;
451 }
452
GetEntranceType() const453 uint8_t RCT12EntranceElement::GetEntranceType() const
454 {
455 return entranceType;
456 }
457
GetRideIndex() const458 uint8_t RCT12EntranceElement::GetRideIndex() const
459 {
460 return rideIndex;
461 }
462
GetStationIndex() const463 uint8_t RCT12EntranceElement::GetStationIndex() const
464 {
465 return (index & RCT12_TRACK_ELEMENT_SEQUENCE_STATION_INDEX_MASK) >> 4;
466 }
467
GetSequenceIndex() const468 uint8_t RCT12EntranceElement::GetSequenceIndex() const
469 {
470 return index & 0xF;
471 }
472
GetPathType() const473 uint8_t RCT12EntranceElement::GetPathType() const
474 {
475 return pathType;
476 }
477
GetIndex() const478 uint8_t RCT12BannerElement::GetIndex() const
479 {
480 return index;
481 }
482
GetPosition() const483 uint8_t RCT12BannerElement::GetPosition() const
484 {
485 return position;
486 }
487
GetAllowedEdges() const488 uint8_t RCT12BannerElement::GetAllowedEdges() const
489 {
490 return AllowedEdges & 0b00001111;
491 }
492
is_user_string_id(rct_string_id stringId)493 bool is_user_string_id(rct_string_id stringId)
494 {
495 return stringId >= 0x8000 && stringId < 0x9000;
496 }
497
GetBannerIndex()498 uint8_t RCT12TileElement::GetBannerIndex()
499 {
500 switch (GetType())
501 {
502 case TILE_ELEMENT_TYPE_LARGE_SCENERY:
503 {
504 auto* sceneryEntry = get_large_scenery_entry(AsLargeScenery()->GetEntryIndex());
505 if (sceneryEntry->scrolling_mode == SCROLLING_MODE_NONE)
506 return RCT12_BANNER_INDEX_NULL;
507
508 return AsLargeScenery()->GetBannerIndex();
509 }
510 case TILE_ELEMENT_TYPE_WALL:
511 {
512 auto* wallEntry = get_wall_entry(AsWall()->GetEntryIndex());
513 if (wallEntry == nullptr || wallEntry->scrolling_mode == SCROLLING_MODE_NONE)
514 return RCT12_BANNER_INDEX_NULL;
515
516 return AsWall()->GetBannerIndex();
517 }
518 case TILE_ELEMENT_TYPE_BANNER:
519 return AsBanner()->GetIndex();
520 default:
521 return RCT12_BANNER_INDEX_NULL;
522 }
523 }
524
SetDirection(uint8_t direction)525 void RCT12TileElementBase::SetDirection(uint8_t direction)
526 {
527 this->type &= ~TILE_ELEMENT_DIRECTION_MASK;
528 this->type |= (direction & TILE_ELEMENT_DIRECTION_MASK);
529 }
530
ClearAs(uint8_t newType)531 void RCT12TileElement::ClearAs(uint8_t newType)
532 {
533 type = newType;
534 flags = 0;
535 base_height = MINIMUM_LAND_HEIGHT;
536 clearance_height = MINIMUM_LAND_HEIGHT;
537 std::fill_n(pad_04, sizeof(pad_04), 0x00);
538 }
539
SetEntryIndex(uint32_t newIndex)540 void RCT12LargeSceneryElement::SetEntryIndex(uint32_t newIndex)
541 {
542 entryIndex &= ~RCT12_TILE_ELEMENT_LARGE_TYPE_MASK;
543 entryIndex |= (newIndex & RCT12_TILE_ELEMENT_LARGE_TYPE_MASK);
544 }
545
SetSequenceIndex(uint16_t sequence)546 void RCT12LargeSceneryElement::SetSequenceIndex(uint16_t sequence)
547 {
548 entryIndex &= RCT12_TILE_ELEMENT_LARGE_TYPE_MASK;
549 entryIndex |= (sequence << 10);
550 }
551
SetPrimaryColour(colour_t newColour)552 void RCT12LargeSceneryElement::SetPrimaryColour(colour_t newColour)
553 {
554 assert(newColour <= 31);
555 colour[0] &= ~TILE_ELEMENT_COLOUR_MASK;
556 colour[0] |= newColour;
557 }
558
SetSecondaryColour(colour_t newColour)559 void RCT12LargeSceneryElement::SetSecondaryColour(colour_t newColour)
560 {
561 assert(newColour <= 31);
562 colour[1] &= ~TILE_ELEMENT_COLOUR_MASK;
563 colour[1] |= newColour;
564 }
565
SetBannerIndex(uint8_t newIndex)566 void RCT12LargeSceneryElement::SetBannerIndex(uint8_t newIndex)
567 {
568 type |= newIndex & 0xC0;
569 colour[0] |= (newIndex & 0x38) << 2;
570 colour[1] |= (newIndex & 7) << 5;
571 }
572
SetSurfaceStyle(uint32_t newStyle)573 void RCT12SurfaceElement::SetSurfaceStyle(uint32_t newStyle)
574 {
575 // Bits 3, 4 for terrain are stored in element.type bit 0, 1
576 type &= ~RCT12_SURFACE_ELEMENT_TYPE_SURFACE_MASK;
577 type |= (newStyle >> 3) & RCT12_SURFACE_ELEMENT_TYPE_SURFACE_MASK;
578
579 // Bits 0, 1, 2 for terrain are stored in element.terrain bit 5, 6, 7
580 terrain &= ~0xE0;
581 terrain |= (newStyle & 7) << 5;
582 }
583
SetEdgeStyle(uint32_t newStyle)584 void RCT12SurfaceElement::SetEdgeStyle(uint32_t newStyle)
585 {
586 // Bit 3 for terrain is stored in element.type bit 7
587 if (newStyle & 8)
588 type |= 128;
589 else
590 type &= ~128;
591
592 // Bits 0, 1, 2 for terrain are stored in element.slope bit 5, 6, 7
593 slope &= ~RCT12_TILE_ELEMENT_SURFACE_EDGE_STYLE_MASK;
594 slope |= (newStyle & 7) << 5;
595 }
596
SetWaterHeight(uint32_t newWaterHeight)597 void RCT12SurfaceElement::SetWaterHeight(uint32_t newWaterHeight)
598 {
599 newWaterHeight >>= 4;
600 newWaterHeight &= 0x1F;
601 terrain &= ~RCT12_TILE_ELEMENT_SURFACE_WATER_HEIGHT_MASK;
602 terrain |= newWaterHeight;
603 }
604
SetGrassLength(uint8_t newLength)605 void RCT12SurfaceElement::SetGrassLength(uint8_t newLength)
606 {
607 grass_length = newLength;
608 }
609
SetOwnership(uint8_t newOwnership)610 void RCT12SurfaceElement::SetOwnership(uint8_t newOwnership)
611 {
612 ownership &= ~TILE_ELEMENT_SURFACE_OWNERSHIP_MASK;
613 ownership |= (newOwnership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK);
614 }
615
SetParkFences(uint8_t newParkFences)616 void RCT12SurfaceElement::SetParkFences(uint8_t newParkFences)
617 {
618 ownership &= ~TILE_ELEMENT_SURFACE_PARK_FENCE_MASK;
619 ownership |= (newParkFences & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK);
620 }
621
SetSlope(uint8_t newSlope)622 void RCT12SurfaceElement::SetSlope(uint8_t newSlope)
623 {
624 slope &= ~TILE_ELEMENT_SURFACE_SLOPE_MASK;
625 slope |= (newSlope & TILE_ELEMENT_SURFACE_SLOPE_MASK);
626 }
627
SetHasTrackThatNeedsWater(bool on)628 void RCT12SurfaceElement::SetHasTrackThatNeedsWater(bool on)
629 {
630 type &= ~SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER;
631 if (on)
632 type |= SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER;
633 }
634
SetPathEntryIndex(uint8_t newEntryIndex)635 void RCT12PathElement::SetPathEntryIndex(uint8_t newEntryIndex)
636 {
637 entryIndex &= ~RCT12_FOOTPATH_PROPERTIES_TYPE_MASK;
638 entryIndex |= (newEntryIndex << 4);
639 }
640
SetQueueBannerDirection(uint8_t direction)641 void RCT12PathElement::SetQueueBannerDirection(uint8_t direction)
642 {
643 type &= ~FOOTPATH_ELEMENT_TYPE_DIRECTION_MASK;
644 type |= (direction << 6);
645 }
646
SetRideIndex(uint8_t newRideIndex)647 void RCT12PathElement::SetRideIndex(uint8_t newRideIndex)
648 {
649 rideIndex = newRideIndex;
650 }
651
SetAdditionStatus(uint8_t newStatus)652 void RCT12PathElement::SetAdditionStatus(uint8_t newStatus)
653 {
654 additionStatus = newStatus;
655 }
656
SetEdges(uint8_t newEdges)657 void RCT12PathElement::SetEdges(uint8_t newEdges)
658 {
659 edges &= ~FOOTPATH_PROPERTIES_EDGES_EDGES_MASK;
660 edges |= (newEdges & FOOTPATH_PROPERTIES_EDGES_EDGES_MASK);
661 }
662
SetCorners(uint8_t newCorners)663 void RCT12PathElement::SetCorners(uint8_t newCorners)
664 {
665 edges &= ~FOOTPATH_PROPERTIES_EDGES_CORNERS_MASK;
666 edges |= (newCorners << 4);
667 }
668
SetSloped(bool isSloped)669 void RCT12PathElement::SetSloped(bool isSloped)
670 {
671 entryIndex &= ~RCT12_FOOTPATH_PROPERTIES_FLAG_IS_SLOPED;
672 if (isSloped)
673 entryIndex |= RCT12_FOOTPATH_PROPERTIES_FLAG_IS_SLOPED;
674 }
675
SetSlopeDirection(uint8_t newSlope)676 void RCT12PathElement::SetSlopeDirection(uint8_t newSlope)
677 {
678 entryIndex &= ~RCT12_FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK;
679 entryIndex |= newSlope & RCT12_FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK;
680 }
681
SetStationIndex(uint8_t newStationIndex)682 void RCT12PathElement::SetStationIndex(uint8_t newStationIndex)
683 {
684 additions &= ~RCT12_FOOTPATH_PROPERTIES_ADDITIONS_STATION_INDEX_MASK;
685 additions |= ((newStationIndex << 4) & RCT12_FOOTPATH_PROPERTIES_ADDITIONS_STATION_INDEX_MASK);
686 }
687
SetWide(bool isWide)688 void RCT12PathElement::SetWide(bool isWide)
689 {
690 type &= ~FOOTPATH_ELEMENT_TYPE_FLAG_IS_WIDE;
691 if (isWide)
692 type |= FOOTPATH_ELEMENT_TYPE_FLAG_IS_WIDE;
693 }
694
SetIsQueue(bool isQueue)695 void RCT12PathElement::SetIsQueue(bool isQueue)
696 {
697 type &= ~FOOTPATH_ELEMENT_TYPE_FLAG_IS_QUEUE;
698 if (isQueue)
699 type |= FOOTPATH_ELEMENT_TYPE_FLAG_IS_QUEUE;
700 }
701
SetHasQueueBanner(bool hasQueueBanner)702 void RCT12PathElement::SetHasQueueBanner(bool hasQueueBanner)
703 {
704 entryIndex &= ~RCT12_FOOTPATH_PROPERTIES_FLAG_HAS_QUEUE_BANNER;
705 if (hasQueueBanner)
706 entryIndex |= RCT12_FOOTPATH_PROPERTIES_FLAG_HAS_QUEUE_BANNER;
707 }
708
SetAddition(uint8_t newAddition)709 void RCT12PathElement::SetAddition(uint8_t newAddition)
710 {
711 additions &= ~RCT12_FOOTPATH_PROPERTIES_ADDITIONS_TYPE_MASK;
712 additions |= newAddition;
713 }
714
SetAdditionIsGhost(bool isGhost)715 void RCT12PathElement::SetAdditionIsGhost(bool isGhost)
716 {
717 additions &= ~RCT12_FOOTPATH_PROPERTIES_ADDITIONS_FLAG_GHOST;
718 if (isGhost)
719 additions |= RCT12_FOOTPATH_PROPERTIES_ADDITIONS_FLAG_GHOST;
720 }
721
IsBroken() const722 bool RCT12PathElement::IsBroken() const
723 {
724 return (flags & RCT12_TILE_ELEMENT_FLAG_BROKEN) != 0;
725 }
726
SetIsBroken(bool isBroken)727 void RCT12PathElement::SetIsBroken(bool isBroken)
728 {
729 if (isBroken)
730 {
731 flags |= RCT12_TILE_ELEMENT_FLAG_BROKEN;
732 }
733 else
734 {
735 flags &= ~RCT12_TILE_ELEMENT_FLAG_BROKEN;
736 }
737 }
738
IsBlockedByVehicle() const739 bool RCT12PathElement::IsBlockedByVehicle() const
740 {
741 return (flags & RCT12_TILE_ELEMENT_FLAG_BLOCKED_BY_VEHICLE) != 0;
742 }
743
SetIsBlockedByVehicle(bool isBlocked)744 void RCT12PathElement::SetIsBlockedByVehicle(bool isBlocked)
745 {
746 if (isBlocked)
747 {
748 flags |= RCT12_TILE_ELEMENT_FLAG_BLOCKED_BY_VEHICLE;
749 }
750 else
751 {
752 flags &= ~RCT12_TILE_ELEMENT_FLAG_BLOCKED_BY_VEHICLE;
753 }
754 }
755
SetTrackType(uint8_t newType)756 void RCT12TrackElement::SetTrackType(uint8_t newType)
757 {
758 trackType = newType;
759 }
760
SetSequenceIndex(uint8_t newSequenceIndex)761 void RCT12TrackElement::SetSequenceIndex(uint8_t newSequenceIndex)
762 {
763 sequence &= ~RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK;
764 sequence |= (newSequenceIndex & RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK);
765 }
766
SetStationIndex(uint8_t newStationIndex)767 void RCT12TrackElement::SetStationIndex(uint8_t newStationIndex)
768 {
769 if (track_type_is_station(trackType) || trackType == TrackElemType::TowerBase)
770 {
771 sequence &= ~RCT12_TRACK_ELEMENT_SEQUENCE_STATION_INDEX_MASK;
772 sequence |= (newStationIndex << 4);
773 }
774 }
775
SetRideIndex(uint8_t newRideIndex)776 void RCT12TrackElement::SetRideIndex(uint8_t newRideIndex)
777 {
778 rideIndex = newRideIndex;
779 }
780
SetColourScheme(uint8_t newColourScheme)781 void RCT12TrackElement::SetColourScheme(uint8_t newColourScheme)
782 {
783 colour &= ~0x3;
784 colour |= (newColourScheme & 0x3);
785 }
786
SetHasCableLift(bool on)787 void RCT12TrackElement::SetHasCableLift(bool on)
788 {
789 colour &= ~RCT12_TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT;
790 if (on)
791 colour |= RCT12_TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT;
792 }
793
SetInverted(bool inverted)794 void RCT12TrackElement::SetInverted(bool inverted)
795 {
796 if (inverted)
797 {
798 colour |= RCT12_TRACK_ELEMENT_COLOUR_FLAG_INVERTED;
799 }
800 else
801 {
802 colour &= ~RCT12_TRACK_ELEMENT_COLOUR_FLAG_INVERTED;
803 }
804 }
805
SetBrakeBoosterSpeed(uint8_t speed)806 void RCT12TrackElement::SetBrakeBoosterSpeed(uint8_t speed)
807 {
808 if (TrackTypeHasSpeedSetting(GetTrackType()))
809 {
810 sequence &= ~0b11110000;
811 sequence |= ((speed >> 1) << 4);
812 }
813 }
814
BlockBrakeClosed() const815 bool RCT12TrackElement::BlockBrakeClosed() const
816 {
817 return (flags & RCT12_TILE_ELEMENT_FLAG_BLOCK_BRAKE_CLOSED) != 0;
818 }
819
SetBlockBrakeClosed(bool isClosed)820 void RCT12TrackElement::SetBlockBrakeClosed(bool isClosed)
821 {
822 if (isClosed)
823 {
824 flags |= RCT12_TILE_ELEMENT_FLAG_BLOCK_BRAKE_CLOSED;
825 }
826 else
827 {
828 flags &= ~RCT12_TILE_ELEMENT_FLAG_BLOCK_BRAKE_CLOSED;
829 }
830 }
831
SetHasGreenLight(uint8_t greenLight)832 void RCT12TrackElement::SetHasGreenLight(uint8_t greenLight)
833 {
834 if (track_type_is_station(trackType))
835 {
836 sequence &= ~MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT;
837 if (greenLight)
838 {
839 sequence |= MAP_ELEM_TRACK_SEQUENCE_GREEN_LIGHT;
840 }
841 }
842 }
843
SetHasChain(bool on)844 void RCT12TrackElement::SetHasChain(bool on)
845 {
846 if (on)
847 {
848 type |= RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT;
849 }
850 else
851 {
852 type &= ~RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT;
853 }
854 }
855
SetSeatRotation(uint8_t newSeatRotation)856 void RCT12TrackElement::SetSeatRotation(uint8_t newSeatRotation)
857 {
858 colour &= 0x0F;
859 colour |= (newSeatRotation << 4);
860 }
861
SetMazeEntry(uint16_t newMazeEntry)862 void RCT12TrackElement::SetMazeEntry(uint16_t newMazeEntry)
863 {
864 mazeEntry = newMazeEntry;
865 }
866
SetPhotoTimeout(uint8_t value)867 void RCT12TrackElement::SetPhotoTimeout(uint8_t value)
868 {
869 if (GetTrackType() == TrackElemType::OnRidePhoto)
870 {
871 sequence &= RCT12_TRACK_ELEMENT_SEQUENCE_SEQUENCE_MASK;
872 sequence |= (value << 4);
873 }
874 }
875
SetEntryIndex(uint8_t newIndex)876 void RCT12SmallSceneryElement::SetEntryIndex(uint8_t newIndex)
877 {
878 this->entryIndex = newIndex;
879 }
880
SetAge(uint8_t newAge)881 void RCT12SmallSceneryElement::SetAge(uint8_t newAge)
882 {
883 this->age = newAge;
884 }
885
SetPrimaryColour(colour_t colour)886 void RCT12SmallSceneryElement::SetPrimaryColour(colour_t colour)
887 {
888 assert(colour <= 31);
889 colour_1 &= ~TILE_ELEMENT_COLOUR_MASK;
890 colour_1 |= colour;
891 }
892
SetSecondaryColour(colour_t colour)893 void RCT12SmallSceneryElement::SetSecondaryColour(colour_t colour)
894 {
895 assert(colour <= 31);
896 colour_2 &= ~TILE_ELEMENT_COLOUR_MASK;
897 colour_2 |= colour;
898 }
899
SetSceneryQuadrant(uint8_t newQuadrant)900 void RCT12SmallSceneryElement::SetSceneryQuadrant(uint8_t newQuadrant)
901 {
902 type &= ~TILE_ELEMENT_QUADRANT_MASK;
903 type |= (newQuadrant << 6);
904 }
905
SetNeedsSupports()906 void RCT12SmallSceneryElement::SetNeedsSupports()
907 {
908 colour_1 |= MAP_ELEM_SMALL_SCENERY_COLOUR_FLAG_NEEDS_SUPPORTS;
909 }
910
SetEntryIndex(uint8_t newIndex)911 void RCT12WallElement::SetEntryIndex(uint8_t newIndex)
912 {
913 entryIndex = newIndex;
914 }
915
SetBannerIndex(uint8_t newIndex)916 void RCT12WallElement::SetBannerIndex(uint8_t newIndex)
917 {
918 banner_index = newIndex;
919 }
920
SetAcrossTrack(bool acrossTrack)921 void RCT12WallElement::SetAcrossTrack(bool acrossTrack)
922 {
923 animation &= ~WALL_ANIMATION_FLAG_ACROSS_TRACK;
924 if (acrossTrack)
925 animation |= WALL_ANIMATION_FLAG_ACROSS_TRACK;
926 }
927
SetAnimationIsBackwards(bool isBackwards)928 void RCT12WallElement::SetAnimationIsBackwards(bool isBackwards)
929 {
930 animation &= ~WALL_ANIMATION_FLAG_DIRECTION_BACKWARD;
931 if (isBackwards)
932 animation |= WALL_ANIMATION_FLAG_DIRECTION_BACKWARD;
933 }
SetSlope(uint8_t newSlope)934 void RCT12WallElement::SetSlope(uint8_t newSlope)
935 {
936 type &= ~TILE_ELEMENT_QUADRANT_MASK;
937 type |= (newSlope << 6);
938 }
939
SetPrimaryColour(colour_t newColour)940 void RCT12WallElement::SetPrimaryColour(colour_t newColour)
941 {
942 assert(newColour <= 31);
943 colour_1 &= ~TILE_ELEMENT_COLOUR_MASK;
944 colour_1 |= newColour;
945 }
946
SetSecondaryColour(colour_t newColour)947 void RCT12WallElement::SetSecondaryColour(colour_t newColour)
948 {
949 colour_1 &= TILE_ELEMENT_COLOUR_MASK;
950 colour_1 |= (newColour & 0x7) << 5;
951 flags &= ~0x60;
952 flags |= (newColour & 0x18) << 2;
953 }
954
SetTertiaryColour(colour_t newColour)955 void RCT12WallElement::SetTertiaryColour(colour_t newColour)
956 {
957 assert(newColour <= 31);
958 colour_3 &= ~TILE_ELEMENT_COLOUR_MASK;
959 colour_3 |= newColour;
960 }
961
SetAnimationFrame(uint8_t frameNum)962 void RCT12WallElement::SetAnimationFrame(uint8_t frameNum)
963 {
964 animation &= WALL_ANIMATION_FLAG_ALL_FLAGS;
965 animation |= (frameNum & 0xF) << 3;
966 }
967
SetEntranceType(uint8_t newType)968 void RCT12EntranceElement::SetEntranceType(uint8_t newType)
969 {
970 entranceType = newType;
971 }
972
SetRideIndex(uint8_t newRideIndex)973 void RCT12EntranceElement::SetRideIndex(uint8_t newRideIndex)
974 {
975 rideIndex = newRideIndex;
976 }
977
SetPathType(uint8_t newPathType)978 void RCT12EntranceElement::SetPathType(uint8_t newPathType)
979 {
980 pathType = newPathType;
981 }
982
SetSequenceIndex(uint8_t newSequenceIndex)983 void RCT12EntranceElement::SetSequenceIndex(uint8_t newSequenceIndex)
984 {
985 index &= ~0xF;
986 index |= (newSequenceIndex & 0xF);
987 }
988
SetStationIndex(uint8_t stationIndex)989 void RCT12EntranceElement::SetStationIndex(uint8_t stationIndex)
990 {
991 index &= ~RCT12_TRACK_ELEMENT_SEQUENCE_STATION_INDEX_MASK;
992 index |= (stationIndex << 4);
993 }
994
SetIndex(uint8_t newIndex)995 void RCT12BannerElement::SetIndex(uint8_t newIndex)
996 {
997 index = newIndex;
998 }
999
SetPosition(uint8_t newPosition)1000 void RCT12BannerElement::SetPosition(uint8_t newPosition)
1001 {
1002 position = newPosition;
1003 }
1004
SetAllowedEdges(uint8_t newEdges)1005 void RCT12BannerElement::SetAllowedEdges(uint8_t newEdges)
1006 {
1007 AllowedEdges &= ~0b00001111;
1008 AllowedEdges |= (newEdges & 0b00001111);
1009 }
1010
IsInventedEndMarker() const1011 bool RCT12ResearchItem::IsInventedEndMarker() const
1012 {
1013 return rawValue == RCT12_RESEARCHED_ITEMS_SEPARATOR;
1014 }
1015
IsUninventedEndMarker() const1016 bool RCT12ResearchItem::IsUninventedEndMarker() const
1017 {
1018 return rawValue == RCT12_RESEARCHED_ITEMS_END;
1019 }
1020
IsRandomEndMarker() const1021 bool RCT12ResearchItem::IsRandomEndMarker() const
1022 {
1023 return rawValue == RCT12_RESEARCHED_ITEMS_END_2;
1024 }
1025
RCTEntryIndexToOpenRCT2EntryIndex(const RCT12ObjectEntryIndex index)1026 ObjectEntryIndex RCTEntryIndexToOpenRCT2EntryIndex(const RCT12ObjectEntryIndex index)
1027 {
1028 if (index == RCT12_OBJECT_ENTRY_INDEX_NULL)
1029 return OBJECT_ENTRY_INDEX_NULL;
1030
1031 return index;
1032 }
1033
OpenRCT2EntryIndexToRCTEntryIndex(const ObjectEntryIndex index)1034 RCT12ObjectEntryIndex OpenRCT2EntryIndexToRCTEntryIndex(const ObjectEntryIndex index)
1035 {
1036 if (index == OBJECT_ENTRY_INDEX_NULL)
1037 return RCT12_OBJECT_ENTRY_INDEX_NULL;
1038
1039 return index;
1040 }
1041
RCT12RideIdToOpenRCT2RideId(const RCT12RideId rideId)1042 ride_id_t RCT12RideIdToOpenRCT2RideId(const RCT12RideId rideId)
1043 {
1044 if (rideId == RCT12_RIDE_ID_NULL)
1045 return RIDE_ID_NULL;
1046
1047 return static_cast<ride_id_t>(rideId);
1048 }
1049
OpenRCT2RideIdToRCT12RideId(const ride_id_t rideId)1050 RCT12RideId OpenRCT2RideIdToRCT12RideId(const ride_id_t rideId)
1051 {
1052 if (rideId == RIDE_ID_NULL)
1053 return RCT12_RIDE_ID_NULL;
1054
1055 return static_cast<RCT12RideId>(rideId);
1056 }
1057
RCT12IsFormatChar(codepoint_t c)1058 static bool RCT12IsFormatChar(codepoint_t c)
1059 {
1060 if (c >= RCT2_STRING_FORMAT_ARG_START && c <= RCT2_STRING_FORMAT_ARG_END)
1061 {
1062 return true;
1063 }
1064 if (c >= RCT2_STRING_FORMAT_COLOUR_START && c <= RCT2_STRING_FORMAT_COLOUR_END)
1065 {
1066 return true;
1067 }
1068 return false;
1069 }
1070
RCT12IsFormatChar(char c)1071 static bool RCT12IsFormatChar(char c)
1072 {
1073 return RCT12IsFormatChar(static_cast<codepoint_t>(c));
1074 }
1075
IsLikelyUTF8(std::string_view s)1076 bool IsLikelyUTF8(std::string_view s)
1077 {
1078 // RCT2 uses CP-1252 so some characters may be >= 128. However we don't expect any
1079 // characters that are reserved for formatting strings, so if those are found, assume
1080 // that the string is UTF-8.
1081 for (auto c : s)
1082 {
1083 if (RCT12IsFormatChar(c))
1084 {
1085 return true;
1086 }
1087 }
1088 return false;
1089 }
1090
RCT12RemoveFormattingUTF8(std::string_view s)1091 std::string RCT12RemoveFormattingUTF8(std::string_view s)
1092 {
1093 std::string result;
1094 result.reserve(s.size() * 2);
1095
1096 CodepointView codepoints(s);
1097 for (auto codepoint : codepoints)
1098 {
1099 if (!RCT12IsFormatChar(codepoint))
1100 {
1101 String::AppendCodepoint(result, codepoint);
1102 }
1103 }
1104
1105 result.shrink_to_fit();
1106 return result;
1107 }
1108
1109 namespace RCT12FormatCode
1110 {
1111 constexpr codepoint_t Newline = 5;
1112 constexpr codepoint_t NewlineSmall = 6;
1113 constexpr codepoint_t ColourBlack = 142;
1114 constexpr codepoint_t ColourGrey = 143;
1115 constexpr codepoint_t ColourWhite = 144;
1116 constexpr codepoint_t ColourRed = 145;
1117 constexpr codepoint_t ColourGreen = 146;
1118 constexpr codepoint_t ColourYellow = 147;
1119 constexpr codepoint_t ColourTopaz = 148;
1120 constexpr codepoint_t ColourCeladon = 149;
1121 constexpr codepoint_t ColourBabyBlue = 150;
1122 constexpr codepoint_t ColourPaleLavender = 151;
1123 constexpr codepoint_t ColourPaleGold = 152;
1124 constexpr codepoint_t ColourLightPink = 153;
1125 constexpr codepoint_t ColourPearlAqua = 154;
1126 constexpr codepoint_t ColourPaleSilver = 155;
1127 } // namespace RCT12FormatCode
1128
GetFormatTokenFromRCT12Code(codepoint_t codepoint)1129 static FormatToken GetFormatTokenFromRCT12Code(codepoint_t codepoint)
1130 {
1131 switch (codepoint)
1132 {
1133 case RCT12FormatCode::Newline:
1134 return FormatToken::Newline;
1135 case RCT12FormatCode::NewlineSmall:
1136 return FormatToken::NewlineSmall;
1137 case RCT12FormatCode::ColourBlack:
1138 return FormatToken::ColourBlack;
1139 case RCT12FormatCode::ColourGrey:
1140 return FormatToken::ColourGrey;
1141 case RCT12FormatCode::ColourWhite:
1142 return FormatToken::ColourWhite;
1143 case RCT12FormatCode::ColourRed:
1144 return FormatToken::ColourRed;
1145 case RCT12FormatCode::ColourGreen:
1146 return FormatToken::ColourGreen;
1147 case RCT12FormatCode::ColourYellow:
1148 return FormatToken::ColourYellow;
1149 case RCT12FormatCode::ColourTopaz:
1150 return FormatToken::ColourTopaz;
1151 case RCT12FormatCode::ColourCeladon:
1152 return FormatToken::ColourCeladon;
1153 case RCT12FormatCode::ColourBabyBlue:
1154 return FormatToken::ColourBabyBlue;
1155 case RCT12FormatCode::ColourPaleLavender:
1156 return FormatToken::ColourPaleLavender;
1157 case RCT12FormatCode::ColourPaleGold:
1158 return FormatToken::ColourPaleGold;
1159 case RCT12FormatCode::ColourLightPink:
1160 return FormatToken::ColourLightPink;
1161 case RCT12FormatCode::ColourPearlAqua:
1162 return FormatToken::ColourPearlAqua;
1163 case RCT12FormatCode::ColourPaleSilver:
1164 return FormatToken::ColourPaleSilver;
1165 default:
1166 return FormatToken::Unknown;
1167 }
1168 }
1169
GetRCT12CodeFromFormatToken(FormatToken token)1170 static codepoint_t GetRCT12CodeFromFormatToken(FormatToken token)
1171 {
1172 switch (token)
1173 {
1174 case FormatToken::Newline:
1175 return RCT12FormatCode::Newline;
1176 case FormatToken::NewlineSmall:
1177 return RCT12FormatCode::NewlineSmall;
1178 case FormatToken::ColourBlack:
1179 return RCT12FormatCode::ColourBlack;
1180 case FormatToken::ColourGrey:
1181 return RCT12FormatCode::ColourGrey;
1182 case FormatToken::ColourWhite:
1183 return RCT12FormatCode::ColourWhite;
1184 case FormatToken::ColourRed:
1185 return RCT12FormatCode::ColourRed;
1186 case FormatToken::ColourGreen:
1187 return RCT12FormatCode::ColourGreen;
1188 case FormatToken::ColourYellow:
1189 return RCT12FormatCode::ColourYellow;
1190 case FormatToken::ColourTopaz:
1191 return RCT12FormatCode::ColourTopaz;
1192 case FormatToken::ColourCeladon:
1193 return RCT12FormatCode::ColourCeladon;
1194 case FormatToken::ColourBabyBlue:
1195 return RCT12FormatCode::ColourBabyBlue;
1196 case FormatToken::ColourPaleLavender:
1197 return RCT12FormatCode::ColourPaleLavender;
1198 case FormatToken::ColourPaleGold:
1199 return RCT12FormatCode::ColourPaleGold;
1200 case FormatToken::ColourLightPink:
1201 return RCT12FormatCode::ColourLightPink;
1202 case FormatToken::ColourPearlAqua:
1203 return RCT12FormatCode::ColourPearlAqua;
1204 case FormatToken::ColourPaleSilver:
1205 return RCT12FormatCode::ColourPaleSilver;
1206 default:
1207 return 0;
1208 }
1209 }
1210
ConvertFormattedStringToOpenRCT2(std::string_view buffer)1211 std::string ConvertFormattedStringToOpenRCT2(std::string_view buffer)
1212 {
1213 auto nullTerminator = buffer.find('\0');
1214 if (nullTerminator != std::string::npos)
1215 {
1216 buffer = buffer.substr(0, nullTerminator);
1217 }
1218 auto asUtf8 = rct2_to_utf8(buffer, RCT2LanguageId::EnglishUK);
1219
1220 std::string result;
1221 CodepointView codepoints(asUtf8);
1222 for (auto codepoint : codepoints)
1223 {
1224 auto token = GetFormatTokenFromRCT12Code(codepoint);
1225 if (token != FormatToken::Unknown)
1226 {
1227 result += GetFormatTokenStringWithBraces(token);
1228 }
1229 else
1230 {
1231 String::AppendCodepoint(result, codepoint);
1232 }
1233 }
1234 return result;
1235 }
1236
ConvertFormattedStringToRCT2(std::string_view buffer,size_t maxLength)1237 std::string ConvertFormattedStringToRCT2(std::string_view buffer, size_t maxLength)
1238 {
1239 std::string result;
1240 FmtString fmt(buffer);
1241 for (const auto& token : fmt)
1242 {
1243 if (token.IsLiteral())
1244 {
1245 result += token.text;
1246 }
1247 else
1248 {
1249 auto codepoint = GetRCT12CodeFromFormatToken(token.kind);
1250 if (codepoint == 0)
1251 {
1252 result += token.text;
1253 }
1254 else
1255 {
1256 String::AppendCodepoint(result, codepoint);
1257 }
1258 }
1259 }
1260 return GetTruncatedRCT2String(result, maxLength);
1261 }
1262
GetTruncatedRCT2String(std::string_view src,size_t maxLength)1263 std::string GetTruncatedRCT2String(std::string_view src, size_t maxLength)
1264 {
1265 auto rct2encoded = utf8_to_rct2(src);
1266 if (rct2encoded.size() > maxLength - 1)
1267 {
1268 log_warning(
1269 "The user string '%s' is too long for the S6 file format and has therefore been truncated.",
1270 std::string(src).c_str());
1271
1272 rct2encoded.resize(maxLength - 1);
1273 for (size_t i = 0; i < rct2encoded.size(); i++)
1274 {
1275 if (rct2encoded[i] == static_cast<char>(static_cast<uint8_t>(0xFF)))
1276 {
1277 if (i > maxLength - 4)
1278 {
1279 // This codepoint was truncated, remove codepoint altogether
1280 rct2encoded.resize(i);
1281 break;
1282 }
1283
1284 // Skip the next two bytes which represent the unicode character
1285 i += 2;
1286 }
1287 }
1288 }
1289 return rct2encoded;
1290 }
1291
RCT12FlatTrackTypeToOpenRCT2(RCT12TrackType origTrackType)1292 track_type_t RCT12FlatTrackTypeToOpenRCT2(RCT12TrackType origTrackType)
1293 {
1294 switch (origTrackType)
1295 {
1296 case TrackElemType::FlatTrack1x4A_Alias:
1297 return TrackElemType::FlatTrack1x4A;
1298 case TrackElemType::FlatTrack2x2_Alias:
1299 return TrackElemType::FlatTrack2x2;
1300 case TrackElemType::FlatTrack4x4_Alias:
1301 return TrackElemType::FlatTrack4x4;
1302 case TrackElemType::FlatTrack2x4_Alias:
1303 return TrackElemType::FlatTrack2x4;
1304 case TrackElemType::FlatTrack1x5_Alias:
1305 return TrackElemType::FlatTrack1x5;
1306 case TrackElemType::FlatTrack1x1A_Alias:
1307 return TrackElemType::FlatTrack1x1A;
1308 case TrackElemType::FlatTrack1x4B_Alias:
1309 return TrackElemType::FlatTrack1x4B;
1310 case TrackElemType::FlatTrack1x1B_Alias:
1311 return TrackElemType::FlatTrack1x1B;
1312 case TrackElemType::FlatTrack1x4C_Alias:
1313 return TrackElemType::FlatTrack1x4C;
1314 case TrackElemType::FlatTrack3x3_Alias:
1315 return TrackElemType::FlatTrack3x3;
1316 }
1317
1318 return origTrackType;
1319 }
1320
OpenRCT2FlatTrackTypeToRCT12(track_type_t origTrackType)1321 RCT12TrackType OpenRCT2FlatTrackTypeToRCT12(track_type_t origTrackType)
1322 {
1323 switch (origTrackType)
1324 {
1325 case TrackElemType::FlatTrack1x4A:
1326 return TrackElemType::FlatTrack1x4A_Alias;
1327 case TrackElemType::FlatTrack2x2:
1328 return TrackElemType::FlatTrack2x2_Alias;
1329 case TrackElemType::FlatTrack4x4:
1330 return TrackElemType::FlatTrack4x4_Alias;
1331 case TrackElemType::FlatTrack2x4:
1332 return TrackElemType::FlatTrack2x4_Alias;
1333 case TrackElemType::FlatTrack1x5:
1334 return TrackElemType::FlatTrack1x5_Alias;
1335 case TrackElemType::FlatTrack1x1A:
1336 return TrackElemType::FlatTrack1x1A_Alias;
1337 case TrackElemType::FlatTrack1x4B:
1338 return TrackElemType::FlatTrack1x4B_Alias;
1339 case TrackElemType::FlatTrack1x1B:
1340 return TrackElemType::FlatTrack1x1B_Alias;
1341 case TrackElemType::FlatTrack1x4C:
1342 return TrackElemType::FlatTrack1x4C_Alias;
1343 case TrackElemType::FlatTrack3x3:
1344 return TrackElemType::FlatTrack3x3_Alias;
1345 }
1346
1347 return origTrackType;
1348 }
1349
1350 static constexpr std::string_view _stationStyles[] = {
1351 "rct2.station.plain", "rct2.station.wooden", "rct2.station.canvas_tent", "rct2.station.castle_grey",
1352 "rct2.station.castle_brown", "rct2.station.jungle", "rct2.station.log", "rct2.station.classical",
1353 "rct2.station.abstract", "rct2.station.snow", "rct2.station.pagoda", "rct2.station.space",
1354 "openrct2.station.noentrance",
1355 };
1356
1357 static constexpr std::string_view _musicStyles[] = {
1358 "rct2.music.dodgems",
1359 "rct2.music.fairground",
1360 "rct2.music.roman",
1361 "rct2.music.oriental",
1362 "rct2.music.martian",
1363 "rct2.music.jungle",
1364 "rct2.music.egyptian",
1365 "rct2.music.toyland",
1366 "", // CIRCUS
1367 "rct2.music.space",
1368 "rct2.music.horror",
1369 "rct2.music.techno",
1370 "rct2.music.gentle",
1371 "rct2.music.summer",
1372 "rct2.music.water",
1373 "rct2.music.wildwest",
1374 "rct2.music.jurassic",
1375 "rct2.music.rock1",
1376 "rct2.music.ragtime",
1377 "rct2.music.fantasy",
1378 "rct2.music.rock2",
1379 "rct2.music.ice",
1380 "rct2.music.snow",
1381 "rct2.music.custom1",
1382 "rct2.music.custom2",
1383 "rct2.music.medieval",
1384 "rct2.music.urban",
1385 "rct2.music.organ",
1386 "rct2.music.mechanical",
1387 "rct2.music.modern",
1388 "rct2.music.pirate",
1389 "rct2.music.rock3",
1390 "rct2.music.candy",
1391 };
1392
GetStationIdentifierFromStyle(uint8_t style)1393 std::string_view GetStationIdentifierFromStyle(uint8_t style)
1394 {
1395 if (style < std::size(_stationStyles))
1396 {
1397 return _stationStyles[style];
1398 }
1399 return {};
1400 }
1401
GetStyleFromMusicIdentifier(std::string_view identifier)1402 std::optional<uint8_t> GetStyleFromMusicIdentifier(std::string_view identifier)
1403 {
1404 auto it = std::find(std::begin(_musicStyles), std::end(_musicStyles), identifier);
1405 if (it != std::end(_musicStyles))
1406 {
1407 return std::distance(std::begin(_musicStyles), it);
1408 }
1409 return std::nullopt;
1410 }
1411
RCT12CompletedCompanyValueToOpenRCT2(money32 origValue)1412 money64 RCT12CompletedCompanyValueToOpenRCT2(money32 origValue)
1413 {
1414 if (origValue == RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE)
1415 return COMPANY_VALUE_ON_FAILED_OBJECTIVE;
1416
1417 return ToMoney64(origValue);
1418 }
1419
OpenRCT2CompletedCompanyValueToRCT12(money64 origValue)1420 money32 OpenRCT2CompletedCompanyValueToRCT12(money64 origValue)
1421 {
1422 if (origValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE)
1423 return RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE;
1424
1425 return ToMoney32(origValue);
1426 }
1427