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