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 "ScenarioSources.h"
11 
12 #include "../core/Guard.hpp"
13 #include "../core/String.hpp"
14 #include "Scenario.h"
15 #include "ScenarioRepository.h"
16 
17 #include <iterator>
18 
19 namespace ScenarioSources
20 {
21     struct ScenarioAlias
22     {
23         const utf8* Original;
24         const utf8* Alternative;
25     };
26 
27     struct ScenarioTitleDescriptor
28     {
29         const uint8_t Id;
30         const utf8* Title;
31         const uint8_t Category;
32     };
33 
34 #pragma region Scenario Data
35 
36     // clang-format off
37     static constexpr const ScenarioAlias ScenarioAliases[] =
38     {
39         // UK - US differences:
40         { "Katie's Dreamland",                      "Katie's World"                             },
41         { "Pokey Park",                             "Dinky Park"                                },
42         { "White Water Park",                       "Aqua Park"                                 },
43         { "Mystic Mountain",                        "Mothball Mountain"                         },
44         { "Paradise Pier",                          "Big Pier"                                  },
45         { "Paradise Pier 2",                        "Big Pier 2"                                },
46         { "Haunted Harbour",                        "Haunted Harbor"                            },
47         { "Mythological - Cradle of Civilisation",  "Mythological - Cradle of Civilization"     },
48 
49         // RCT1 pack by RCTScenarioLover has a mistake:
50         { "Geoffrey Gardens",                       "Geoffery Gardens"                          },
51         // RCT1 pack by Crappage uses park name to identify Utopia Park, instead of scenario list name:
52         { "Utopia Park",                            "Utopia"                                    },
53 
54         // CD Projekt Polish Edition
55         { "Alpine Adventures",                          "Górska przygoda"                           },
56         { "Amity Airfield",                             "Lotnisko Pogięcie"                         },
57         { "Botany Breakers",                            "Wyspa Odludna"                             },
58         { "Bumbly Bazaar",                              "Gwarne targowisko"                         },
59         { "Crazy Castle",                               "Zwariowany zamek"                          },
60         { "Dusty Greens",                               "Zapiaszczone Dołki"                        },
61         { "Electric Fields",                            "Pola elektryczne"                          },
62         { "Extreme Heights",                            "Ekstremalne wysokości"                     },
63         { "Factory Capers",                             "300% normy"                                },
64         { "Fungus Woods",                               "Grzybowe Lasy"                             },
65         { "Ghost Town",                                 "Miasto-widmo"                              },
66         { "Gravity Gardens",                            "Ogrody Grawitacji"                         },
67         { "Infernal Views",                             "Piekielny widok"                           },
68         { "Lucky Lake",                                 "Jezioro Szczęściarzy"                      },
69         { "Rainbow Summit",                             "Tęczowy Szczyt"                            },
70 
71         { "Six Flags Belgium",                          "\"Sześć Flag\" - Belgia"                   },
72         { "Six Flags Holland",                          "\"Sześć Flag\" - Holandia"                 },
73         { "Six Flags Magic Mountain",                   "Czarodziejska góra \"Sześciu Flag\""       },
74         { "Six Flags Great Adventure",                  "\"Sześć Flag\" - wielka przygoda"          },
75         { "Six Flags over Texas",                       "\"Sześć Flag nad Teksasem\""             },
76         { "Build your own Six Flags Belgium",           "Wybuduj własny park \"Sześć Flag\" - Belgia" },
77         { "Build your own Six Flags Holland",           "Wybuduj własny park \"Sześć Flag\" - Holandia" },
78         { "Build your own Six Flags Magic Mountain",    "Wybuduj własny park czarodziejska góra \"Sześciu Flag\"" },
79         { "Build your own Six Flags Great Adventure",   "Wybuduj własny park \"Sześć Flag\" - wielka przygoda" },
80         { "Build your own Six Flags over Texas",        "Wybuduj własny park \"Sześć Flag nad Teksasem\"" },
81         { "Build your own Six Flags Park",              "Wybuduj własny park \"Sześć Flag\""        },
82 
83         { "Africa - African Diamond Mine",              "Afryka - kopalnia diamentów"               },
84         { "Africa - Oasis",                             "Afryka - oaza"                             },
85         { "Africa - Victoria Falls",                    "Afryka - Wodospad Wiktorii"                },
86         { "Antarctic - Ecological Salvage",             "Antarktyka - utylizacja"                   },
87         { "Asia - Great Wall of China Tourism Enhancement", "Azja - rozwój turystyki wokół Wielkiego Muru Chińskiego" },
88         { "Asia - Japanese Coastal Reclaim",            "Azja - odzysk japońskiego wybrzeża"        },
89         { "Asia - Maharaja Palace",                     "Azja - pałac maharadży"                    },
90         { "Australasia - Ayers Rock",                   "Australazja - Ayers Rock"                  },
91         { "Australasia - Fun at the beach",             "Australazja - zabawa na plaży"             },
92         { "Europe - European Cultural Festival",        "Europa - Europejski Festiwal Kulturalny"   },
93         { "Europe - Renovation",                        "Europa - odbudowa"                         },
94         { "N. America - Extreme Hawaiian Island",       "Ameryka Płn. - ekstremalne Hawaje"         },
95         { "North America - Grand Canyon",               "Ameryka Płn. - Wielki Kanion"              },
96         { "North America - Rollercoaster Heaven",       "Ameryka Płn. - raj kolejek górskich"       },
97         { "South America - Inca Lost City",             "Ameryka Płd. - zaginione miasto Inków"     },
98         { "South America - Rain Forest Plateau",        "Ameryka Płd. - tropikalny płaskowyż"       },
99         { "South America - Rio Carnival",               "Ameryka Płd. - karnawał w Rio"             },
100 
101         { "Dark Age - Castle",                          "Mroczne Wieki - zamek"                     },
102         { "Dark Age - Robin Hood",                      "Mroczne Wieki - Robin Hood"                },
103         { "Future - First Encounters",                  "Przyszłość - pierwsze spotkania"           },
104         { "Future - Future World",                      "Przyszłość - świat przyszłości"            },
105         { "Mythological - Animatronic Film Set",        "Mitologia - mechaniczny plan filmowy"      },
106         { "Mythological - Cradle of Civilisation",      "Mitologia - kołyska cywilizacji"           },
107         { "Prehistoric - After the Asteroid",           "Prehistoria - po asteroidzie"              },
108         { "Prehistoric - Jurassic Safari",              "Prehistoria - jurajskie safari"            },
109         { "Prehistoric - Stone Age",                    "Prehistoria - epoka kamienna"              },
110         { "Roaring Twenties - Prison Island",           "Ryczące Dwudziestki - wyspa więzienna"     },
111         { "Roaring Twenties - Schneider Cup",           "Ryczące Dwudziestki - puchar Schneidera"   },
112         { "Roaring Twenties - Skyscrapers",             "Ryczące Dwudziestki - drapacze chmur"      },
113         { "Rock 'n' Roll - Flower Power",               "Rock 'n' Roll - dzieci kwiaty"             },
114         // Skipping Rock 'n' Roll - Rock 'n' Roll as that name matches exactly.
115 
116     };
117 
118     // RCT
119     static constexpr const ScenarioTitleDescriptor ScenarioTitlesRCT1[] =
120     {
121         { SC_FOREST_FRONTIERS,          "Forest Frontiers",     SCENARIO_CATEGORY_BEGINNER      },
122         { SC_DYNAMITE_DUNES,            "Dynamite Dunes",       SCENARIO_CATEGORY_BEGINNER      },
123         { SC_LEAFY_LAKE,                "Leafy Lake",           SCENARIO_CATEGORY_BEGINNER      },
124         { SC_DIAMOND_HEIGHTS,           "Diamond Heights",      SCENARIO_CATEGORY_BEGINNER      },
125         { SC_EVERGREEN_GARDENS,         "Evergreen Gardens",    SCENARIO_CATEGORY_BEGINNER      },
126         { SC_BUMBLY_BEACH,              "Bumbly Beach",         SCENARIO_CATEGORY_BEGINNER      },
127         { SC_TRINITY_ISLANDS,           "Trinity Islands",      SCENARIO_CATEGORY_CHALLENGING   },
128         { SC_KATIES_DREAMLAND,          "Katie's Dreamland",    SCENARIO_CATEGORY_CHALLENGING   },
129         { SC_POKEY_PARK,                "Pokey Park",           SCENARIO_CATEGORY_CHALLENGING   },
130         { SC_WHITE_WATER_PARK,          "White Water Park",     SCENARIO_CATEGORY_CHALLENGING   },
131         { SC_MILLENNIUM_MINES,          "Millennium Mines",     SCENARIO_CATEGORY_CHALLENGING   },
132         { SC_KARTS_COASTERS,            "Karts & Coasters",     SCENARIO_CATEGORY_CHALLENGING   },
133         { SC_MELS_WORLD,                "Mel's World",          SCENARIO_CATEGORY_CHALLENGING   },
134         { SC_MYSTIC_MOUNTAIN,           "Mystic Mountain",      SCENARIO_CATEGORY_CHALLENGING   },
135         { SC_PACIFIC_PYRAMIDS,          "Pacific Pyramids",     SCENARIO_CATEGORY_CHALLENGING   },
136         { SC_CRUMBLY_WOODS,             "Crumbly Woods",        SCENARIO_CATEGORY_CHALLENGING   },
137         { SC_PARADISE_PIER,             "Paradise Pier",        SCENARIO_CATEGORY_CHALLENGING   },
138         { SC_LIGHTNING_PEAKS,           "Lightning Peaks",      SCENARIO_CATEGORY_EXPERT        },
139         { SC_IVORY_TOWERS,              "Ivory Towers",         SCENARIO_CATEGORY_EXPERT        },
140         { SC_RAINBOW_VALLEY,            "Rainbow Valley",       SCENARIO_CATEGORY_EXPERT        },
141         { SC_THUNDER_ROCK,              "Thunder Rock",         SCENARIO_CATEGORY_EXPERT        },
142         { SC_MEGA_PARK,                 "Mega Park",            SCENARIO_CATEGORY_OTHER         },
143     };
144 
145     // RCT: Added Attractions
146     static constexpr const ScenarioTitleDescriptor ScenarioTitlesRCT1AA[] =
147     {
148         { SC_WHISPERING_CLIFFS,         "Whispering Cliffs",    SCENARIO_CATEGORY_BEGINNER      },
149         { SC_THREE_MONKEYS_PARK,        "Three Monkeys Park",   SCENARIO_CATEGORY_BEGINNER      },
150         { SC_CANARY_MINES,              "Canary Mines",         SCENARIO_CATEGORY_BEGINNER      },
151         { SC_BARONY_BRIDGE,             "Barony Bridge",        SCENARIO_CATEGORY_BEGINNER      },
152         { SC_FUNTOPIA,                  "Funtopia",             SCENARIO_CATEGORY_BEGINNER      },
153         { SC_HAUNTED_HARBOUR,           "Haunted Harbour",      SCENARIO_CATEGORY_BEGINNER      },
154         { SC_FUN_FORTRESS,              "Fun Fortress",         SCENARIO_CATEGORY_BEGINNER      },
155         { SC_FUTURE_WORLD,              "Future World",         SCENARIO_CATEGORY_BEGINNER      },
156         { SC_GENTLE_GLEN,               "Gentle Glen",          SCENARIO_CATEGORY_BEGINNER      },
157         { SC_JOLLY_JUNGLE,              "Jolly Jungle",         SCENARIO_CATEGORY_CHALLENGING   },
158         { SC_HYDRO_HILLS,               "Hydro Hills",          SCENARIO_CATEGORY_CHALLENGING   },
159         { SC_SPRIGHTLY_PARK,            "Sprightly Park",       SCENARIO_CATEGORY_CHALLENGING   },
160         { SC_MAGIC_QUARTERS,            "Magic Quarters",       SCENARIO_CATEGORY_CHALLENGING   },
161         { SC_FRUIT_FARM,                "Fruit Farm",           SCENARIO_CATEGORY_CHALLENGING   },
162         { SC_BUTTERFLY_DAM,             "Butterfly Dam",        SCENARIO_CATEGORY_CHALLENGING   },
163         { SC_COASTER_CANYON,            "Coaster Canyon",       SCENARIO_CATEGORY_CHALLENGING   },
164         { SC_THUNDERSTORM_PARK,         "Thunderstorm Park",    SCENARIO_CATEGORY_CHALLENGING   },
165         { SC_HARMONIC_HILLS,            "Harmonic Hills",       SCENARIO_CATEGORY_CHALLENGING   },
166         { SC_ROMAN_VILLAGE,             "Roman Village",        SCENARIO_CATEGORY_CHALLENGING   },
167         { SC_SWAMP_COVE,                "Swamp Cove",           SCENARIO_CATEGORY_CHALLENGING   },
168         { SC_ADRENALINE_HEIGHTS,        "Adrenaline Heights",   SCENARIO_CATEGORY_CHALLENGING   },
169         { SC_UTOPIA_PARK,               "Utopia Park",          SCENARIO_CATEGORY_CHALLENGING   },
170         { SC_ROTTING_HEIGHTS,           "Rotting Heights",      SCENARIO_CATEGORY_EXPERT        },
171         { SC_FIASCO_FOREST,             "Fiasco Forest",        SCENARIO_CATEGORY_EXPERT        },
172         { SC_PICKLE_PARK,               "Pickle Park",          SCENARIO_CATEGORY_EXPERT        },
173         { SC_GIGGLE_DOWNS,              "Giggle Downs",         SCENARIO_CATEGORY_EXPERT        },
174         { SC_MINERAL_PARK,              "Mineral Park",         SCENARIO_CATEGORY_EXPERT        },
175         { SC_COASTER_CRAZY,             "Coaster Crazy",        SCENARIO_CATEGORY_EXPERT        },
176         { SC_URBAN_PARK,                "Urban Park",           SCENARIO_CATEGORY_EXPERT        },
177         { SC_GEOFFREY_GARDENS,          "Geoffrey Gardens",     SCENARIO_CATEGORY_EXPERT        },
178     };
179 
180     // RCT: Loopy Landscapes
181     static constexpr const ScenarioTitleDescriptor ScenarioTitlesRCT1LL[] =
182     {
183         {   SC_ICEBERG_ISLANDS,         "Iceberg Islands",      SCENARIO_CATEGORY_BEGINNER      },
184         {   SC_VOLCANIA,                "Volcania",             SCENARIO_CATEGORY_BEGINNER      },
185         {   SC_ARID_HEIGHTS,            "Arid Heights",         SCENARIO_CATEGORY_BEGINNER      },
186         {   SC_RAZOR_ROCKS,             "Razor Rocks",          SCENARIO_CATEGORY_BEGINNER      },
187         {   SC_CRATER_LAKE,             "Crater Lake",          SCENARIO_CATEGORY_BEGINNER      },
188         {   SC_VERTIGO_VIEWS,           "Vertigo Views",        SCENARIO_CATEGORY_BEGINNER      },
189         {   SC_PARADISE_PIER_2,         "Paradise Pier 2",      SCENARIO_CATEGORY_CHALLENGING   },
190         {   SC_DRAGONS_COVE,            "Dragon's Cove",        SCENARIO_CATEGORY_CHALLENGING   },
191         {   SC_GOOD_KNIGHT_PARK,        "Good Knight Park",     SCENARIO_CATEGORY_CHALLENGING   },
192         {   SC_WACKY_WARREN,            "Wacky Warren",         SCENARIO_CATEGORY_CHALLENGING   },
193         {   SC_GRAND_GLACIER,           "Grand Glacier",        SCENARIO_CATEGORY_CHALLENGING   },
194         {   SC_CRAZY_CRATERS,           "Crazy Craters",        SCENARIO_CATEGORY_CHALLENGING   },
195         {   SC_DUSTY_DESERT,            "Dusty Desert",         SCENARIO_CATEGORY_CHALLENGING   },
196         {   SC_WOODWORM_PARK,           "Woodworm Park",        SCENARIO_CATEGORY_CHALLENGING   },
197         {   SC_ICARUS_PARK,             "Icarus Park",          SCENARIO_CATEGORY_CHALLENGING   },
198         {   SC_SUNNY_SWAMPS,            "Sunny Swamps",         SCENARIO_CATEGORY_CHALLENGING   },
199         {   SC_FRIGHTMARE_HILLS,        "Frightmare Hills",     SCENARIO_CATEGORY_CHALLENGING   },
200         {   SC_THUNDER_ROCKS,           "Thunder Rocks",        SCENARIO_CATEGORY_CHALLENGING   },
201         {   SC_OCTAGON_PARK,            "Octagon Park",         SCENARIO_CATEGORY_CHALLENGING   },
202         {   SC_PLEASURE_ISLAND,         "Pleasure Island",      SCENARIO_CATEGORY_CHALLENGING   },
203         {   SC_ICICLE_WORLDS,           "Icicle Worlds",        SCENARIO_CATEGORY_CHALLENGING   },
204         {   SC_SOUTHERN_SANDS,          "Southern Sands",       SCENARIO_CATEGORY_CHALLENGING   },
205         {   SC_TINY_TOWERS,             "Tiny Towers",          SCENARIO_CATEGORY_CHALLENGING   },
206         {   SC_NEVERMORE_PARK,          "Nevermore Park",       SCENARIO_CATEGORY_CHALLENGING   },
207         {   SC_PACIFICA,                "Pacifica",             SCENARIO_CATEGORY_CHALLENGING   },
208         {   SC_URBAN_JUNGLE,            "Urban Jungle",         SCENARIO_CATEGORY_EXPERT        },
209         {   SC_TERROR_TOWN,             "Terror Town",          SCENARIO_CATEGORY_EXPERT        },
210         {   SC_MEGAWORLD_PARK,          "Megaworld Park",       SCENARIO_CATEGORY_EXPERT        },
211         {   SC_VENUS_PONDS,             "Venus Ponds",          SCENARIO_CATEGORY_EXPERT        },
212         {   SC_MICRO_PARK,              "Micro Park",           SCENARIO_CATEGORY_EXPERT        },
213     };
214 
215     // RCT2
216     static constexpr const ScenarioTitleDescriptor ScenarioTitlesRCT2[] =
217     {
218         { SC_UNIDENTIFIED,              "Crazy Castle",         SCENARIO_CATEGORY_BEGINNER      },
219         { SC_UNIDENTIFIED,              "Electric Fields",      SCENARIO_CATEGORY_BEGINNER      },
220         { SC_UNIDENTIFIED,              "Factory Capers",       SCENARIO_CATEGORY_BEGINNER      },
221         { SC_UNIDENTIFIED,              "Amity Airfield",       SCENARIO_CATEGORY_CHALLENGING   },
222         { SC_UNIDENTIFIED,              "Botany Breakers",      SCENARIO_CATEGORY_CHALLENGING   },
223         { SC_UNIDENTIFIED,              "Bumbly Bazaar",        SCENARIO_CATEGORY_CHALLENGING   },
224         { SC_UNIDENTIFIED,              "Dusty Greens",         SCENARIO_CATEGORY_CHALLENGING   },
225         { SC_UNIDENTIFIED,              "Fungus Woods",         SCENARIO_CATEGORY_CHALLENGING   },
226         { SC_UNIDENTIFIED,              "Gravity Gardens",      SCENARIO_CATEGORY_CHALLENGING   },
227         { SC_UNIDENTIFIED,              "Infernal Views",       SCENARIO_CATEGORY_CHALLENGING   },
228         { SC_UNIDENTIFIED,              "Alpine Adventures",    SCENARIO_CATEGORY_EXPERT        },
229         { SC_UNIDENTIFIED,              "Extreme Heights",      SCENARIO_CATEGORY_EXPERT        },
230         { SC_UNIDENTIFIED,              "Ghost Town",           SCENARIO_CATEGORY_EXPERT        },
231         { SC_UNIDENTIFIED,              "Lucky Lake",           SCENARIO_CATEGORY_EXPERT        },
232         { SC_UNIDENTIFIED,              "Rainbow Summit",       SCENARIO_CATEGORY_EXPERT        },
233     };
234 
235     // RCT2: Wacky Worlds
236     static constexpr const ScenarioTitleDescriptor ScenarioTitlesRCT2WW[] =
237     {
238         { SC_UNIDENTIFIED,              "Africa - Victoria Falls",                          SCENARIO_CATEGORY_BEGINNER      },
239         { SC_UNIDENTIFIED,              "Asia - Great Wall of China Tourism Enhancement",   SCENARIO_CATEGORY_BEGINNER      },
240         { SC_UNIDENTIFIED,              "North America - Grand Canyon",                     SCENARIO_CATEGORY_BEGINNER      },
241         { SC_UNIDENTIFIED,              "South America - Rio Carnival",                     SCENARIO_CATEGORY_BEGINNER      },
242         { SC_UNIDENTIFIED,              "Africa - African Diamond Mine",                    SCENARIO_CATEGORY_CHALLENGING   },
243         { SC_UNIDENTIFIED,              "Asia - Maharaja Palace",                           SCENARIO_CATEGORY_CHALLENGING   },
244         { SC_UNIDENTIFIED,              "Australasia - Ayers Rock",                         SCENARIO_CATEGORY_CHALLENGING   },
245         { SC_UNIDENTIFIED,              "Europe - European Cultural Festival",              SCENARIO_CATEGORY_CHALLENGING   },
246         { SC_UNIDENTIFIED,              "North America - Rollercoaster Heaven",             SCENARIO_CATEGORY_CHALLENGING   },
247         { SC_UNIDENTIFIED,              "South America - Inca Lost City",                   SCENARIO_CATEGORY_CHALLENGING   },
248         { SC_UNIDENTIFIED,              "Africa - Oasis",                                   SCENARIO_CATEGORY_EXPERT        },
249         { SC_UNIDENTIFIED,              "Antarctic - Ecological Salvage",                   SCENARIO_CATEGORY_EXPERT        },
250         { SC_UNIDENTIFIED,              "Asia - Japanese Coastal Reclaim",                  SCENARIO_CATEGORY_EXPERT        },
251         { SC_UNIDENTIFIED,              "Australasia - Fun at the Beach",                   SCENARIO_CATEGORY_EXPERT        },
252         { SC_UNIDENTIFIED,              "Europe - Renovation",                              SCENARIO_CATEGORY_EXPERT        },
253         { SC_UNIDENTIFIED,              "N. America - Extreme Hawaiian Island",             SCENARIO_CATEGORY_EXPERT        },
254         { SC_UNIDENTIFIED,              "South America - Rain Forest Plateau",              SCENARIO_CATEGORY_EXPERT        },
255     };
256 
257     // RCT2: Time Twister
258     static constexpr const ScenarioTitleDescriptor ScenarioTitlesRCT2TT[] =
259     {
260         { SC_UNIDENTIFIED,              "Dark Age - Robin Hood",                            SCENARIO_CATEGORY_BEGINNER      },
261         { SC_UNIDENTIFIED,              "Prehistoric - After the Asteroid",                 SCENARIO_CATEGORY_BEGINNER      },
262         { SC_UNIDENTIFIED,              "Roaring Twenties - Prison Island",                 SCENARIO_CATEGORY_BEGINNER      },
263         { SC_UNIDENTIFIED,              "Rock 'n' Roll - Flower Power",                     SCENARIO_CATEGORY_BEGINNER      },
264         { SC_UNIDENTIFIED,              "Dark Age - Castle",                                SCENARIO_CATEGORY_CHALLENGING   },
265         { SC_UNIDENTIFIED,              "Future - First Encounters",                        SCENARIO_CATEGORY_CHALLENGING   },
266         { SC_UNIDENTIFIED,              "Mythological - Animatronic Film Set",              SCENARIO_CATEGORY_CHALLENGING   },
267         { SC_UNIDENTIFIED,              "Prehistoric - Jurassic Safari",                    SCENARIO_CATEGORY_CHALLENGING   },
268         { SC_UNIDENTIFIED,              "Roaring Twenties - Schneider Cup",                 SCENARIO_CATEGORY_CHALLENGING   },
269         { SC_UNIDENTIFIED,              "Future - Future World",                            SCENARIO_CATEGORY_EXPERT        },
270         { SC_UNIDENTIFIED,              "Mythological - Cradle of Civilisation",            SCENARIO_CATEGORY_EXPERT        },
271         { SC_UNIDENTIFIED,              "Prehistoric - Stone Age",                          SCENARIO_CATEGORY_EXPERT        },
272         { SC_UNIDENTIFIED,              "Roaring Twenties - Skyscrapers",                   SCENARIO_CATEGORY_EXPERT        },
273         { SC_UNIDENTIFIED,              "Rock 'n' Roll - Rock 'n' Roll",                    SCENARIO_CATEGORY_EXPERT        },
274     };
275 
276     // Real parks
277     static constexpr const ScenarioTitleDescriptor ScenarioTitlesRealParks[] =
278     {
279         { SC_ALTON_TOWERS,              "Alton Towers",                                     SCENARIO_CATEGORY_REAL  },
280         { SC_HEIDE_PARK,                "Heide-Park",                                       SCENARIO_CATEGORY_REAL  },
281         { SC_BLACKPOOL_PLEASURE_BEACH,  "Blackpool Pleasure Beach",                         SCENARIO_CATEGORY_REAL  },
282         { SC_UNIDENTIFIED,              "Six Flags Belgium",                                SCENARIO_CATEGORY_REAL  },
283         { SC_UNIDENTIFIED,              "Six Flags Great Adventure",                        SCENARIO_CATEGORY_REAL  },
284         { SC_UNIDENTIFIED,              "Six Flags Holland",                                SCENARIO_CATEGORY_REAL  },
285         { SC_UNIDENTIFIED,              "Six Flags Magic Mountain",                         SCENARIO_CATEGORY_REAL  },
286         { SC_UNIDENTIFIED,              "Six Flags over Texas",                             SCENARIO_CATEGORY_REAL  },
287     };
288 
289     // Other parks
290     static constexpr const ScenarioTitleDescriptor ScenarioTitlesOtherParks[] =
291     {
292         { SC_FORT_ANACHRONISM,                          "Fort Anachronism",                                 SCENARIO_CATEGORY_DLC            },
293         { SC_PCPLAYER,                                  "PC Player",                                        SCENARIO_CATEGORY_DLC            },
294         { SC_PCGW,                                      "PC Gaming World",                                  SCENARIO_CATEGORY_DLC            },
295         { SC_GAMEPLAY,                                  "gameplay",                                         SCENARIO_CATEGORY_DLC            },
296         { SC_UNIDENTIFIED,                              "Panda World",                                      SCENARIO_CATEGORY_DLC            },
297         { SC_UNIDENTIFIED,                              "Competition Land 1",                               SCENARIO_CATEGORY_DLC            },
298         { SC_UNIDENTIFIED,                              "Competition Land 2",                               SCENARIO_CATEGORY_DLC            },
299         { SC_STEEL_TWISTER_COMPETITION,                 "Steel Twister Roller Coaster Competition",         SCENARIO_CATEGORY_DLC            },
300         { SC_GO_KARTS_COMPETITION,                      "Go Karts Competition",                             SCENARIO_CATEGORY_DLC            },
301         { SC_STEEL_CORKSCREW_COMPETITION,               "Steel Corkscrew Roller Coaster Competition",       SCENARIO_CATEGORY_DLC            },
302         { SC_MINE_TRAIN_COMPETITION,                    "Mine Train Roller Coaster Competition",            SCENARIO_CATEGORY_DLC            },
303         { SC_STEEL_ROLLER_COASTER_COMPETITION,          "Steel Roller Coaster Competition",                 SCENARIO_CATEGORY_DLC            },
304         { SC_BOBSLED_COMPETITION,                       "Bobsled Roller Coaster Competition",               SCENARIO_CATEGORY_DLC            },
305         { SC_INVERTED_ROLLER_COASTER_COMPETITION,       "Inverted Roller Coaster Competition",              SCENARIO_CATEGORY_DLC            },
306         { SC_SUSPENDED_ROLLER_COASTER_COMPETITION,      "Suspended Roller Coaster Competition",             SCENARIO_CATEGORY_DLC            },
307         { SC_STAND_UP_STEEL_ROLLER_COASTER_COMPETITION, "Stand-Up Steel Roller Coaster Competition",        SCENARIO_CATEGORY_DLC            },
308         { SC_WOODEN_ROLLER_COASTER_COMPETITION,         "Wooden Roller Coaster Competition",                SCENARIO_CATEGORY_DLC            },
309         { SC_STEEL_MINI_ROLLER_COASTER_COMPETITION,     "Steel Mini Roller Coaster Competition",            SCENARIO_CATEGORY_DLC            },
310         { SC_UNIDENTIFIED,                              "Build your own Six Flags Belgium",                 SCENARIO_CATEGORY_BUILD_YOUR_OWN },
311         { SC_UNIDENTIFIED,                              "Build your own Six Flags Great Adventure",         SCENARIO_CATEGORY_BUILD_YOUR_OWN },
312         { SC_UNIDENTIFIED,                              "Build your own Six Flags Holland",                 SCENARIO_CATEGORY_BUILD_YOUR_OWN },
313         { SC_UNIDENTIFIED,                              "Build your own Six Flags Magic Mountain",          SCENARIO_CATEGORY_BUILD_YOUR_OWN },
314         { SC_UNIDENTIFIED,                              "Build your own Six Flags Park",                    SCENARIO_CATEGORY_BUILD_YOUR_OWN },
315         { SC_UNIDENTIFIED,                              "Build your own Six Flags over Texas",              SCENARIO_CATEGORY_BUILD_YOUR_OWN },
316     };
317 
318     #define DEFINE_SCENARIO_TITLE_DESC_GROUP(x) { std::size(x), x }
319     const struct {
320         size_t count;
321         const ScenarioTitleDescriptor * const titles;
322     } ScenarioTitlesBySource[] = {
323         DEFINE_SCENARIO_TITLE_DESC_GROUP(ScenarioTitlesRCT1),
324         DEFINE_SCENARIO_TITLE_DESC_GROUP(ScenarioTitlesRCT1AA),
325         DEFINE_SCENARIO_TITLE_DESC_GROUP(ScenarioTitlesRCT1LL),
326         DEFINE_SCENARIO_TITLE_DESC_GROUP(ScenarioTitlesRCT2),
327         DEFINE_SCENARIO_TITLE_DESC_GROUP(ScenarioTitlesRCT2WW),
328         DEFINE_SCENARIO_TITLE_DESC_GROUP(ScenarioTitlesRCT2TT),
329         DEFINE_SCENARIO_TITLE_DESC_GROUP(ScenarioTitlesRealParks),
330         DEFINE_SCENARIO_TITLE_DESC_GROUP(ScenarioTitlesOtherParks),
331     };
332     // clang-format on
333 
334 #pragma endregion
335 
TryGetByName(const utf8 * name,source_desc * outDesc)336     bool TryGetByName(const utf8* name, source_desc* outDesc)
337     {
338         Guard::ArgumentNotNull(outDesc, GUARD_LINE);
339 
340         int32_t currentIndex = 0;
341         for (size_t i = 0; i < std::size(ScenarioTitlesBySource); i++)
342         {
343             for (size_t j = 0; j < ScenarioTitlesBySource[i].count; j++)
344             {
345                 const ScenarioTitleDescriptor* desc = &ScenarioTitlesBySource[i].titles[j];
346                 if (String::Equals(name, desc->Title, true))
347                 {
348                     outDesc->title = desc->Title;
349                     outDesc->id = desc->Id;
350                     outDesc->source = static_cast<uint8_t>(i);
351                     outDesc->index = currentIndex;
352                     outDesc->category = desc->Category;
353                     return true;
354                 }
355                 currentIndex++;
356             }
357         }
358 
359         outDesc->title = nullptr;
360         outDesc->id = SC_UNIDENTIFIED;
361         outDesc->source = static_cast<uint8_t>(ScenarioSource::Other);
362         outDesc->index = -1;
363         outDesc->category = SCENARIO_CATEGORY_OTHER;
364         return false;
365     }
366 
TryGetById(uint8_t id,source_desc * outDesc)367     bool TryGetById(uint8_t id, source_desc* outDesc)
368     {
369         Guard::ArgumentNotNull(outDesc, GUARD_LINE);
370 
371         int32_t currentIndex = 0;
372         for (size_t i = 0; i < std::size(ScenarioTitlesBySource); i++)
373         {
374             for (size_t j = 0; j < ScenarioTitlesBySource[i].count; j++)
375             {
376                 const ScenarioTitleDescriptor* desc = &ScenarioTitlesBySource[i].titles[j];
377                 if (id == desc->Id)
378                 {
379                     outDesc->title = desc->Title;
380                     outDesc->id = desc->Id;
381                     outDesc->source = static_cast<uint8_t>(i);
382                     outDesc->index = currentIndex;
383                     outDesc->category = desc->Category;
384                     return true;
385                 }
386                 currentIndex++;
387             }
388         }
389 
390         outDesc->title = "";
391         outDesc->id = SC_UNIDENTIFIED;
392         outDesc->source = static_cast<uint8_t>(ScenarioSource::Other);
393         outDesc->index = -1;
394         outDesc->category = SCENARIO_CATEGORY_OTHER;
395         return false;
396     }
397 
NormaliseName(utf8 * buffer,size_t bufferSize,const utf8 * name)398     void NormaliseName(utf8* buffer, size_t bufferSize, const utf8* name)
399     {
400         size_t nameLength = String::LengthOf(name);
401 
402         // Strip "RCT(1|2)?" prefix off scenario names.
403         if (nameLength >= 3 && (name[0] == 'R' && name[1] == 'C' && name[2] == 'T'))
404         {
405             if (nameLength >= 4 && (name[3] == '1' || name[3] == '2'))
406             {
407                 log_verbose("Stripping RCT/1/2 from name: %s", name);
408                 String::Set(buffer, bufferSize, name + 4);
409             }
410             else
411             {
412                 String::Set(buffer, bufferSize, name + 3);
413             }
414         }
415 
416         // Trim (for the sake of the above and WW / TT scenarios
417         String::TrimStart(buffer, bufferSize, name);
418 
419         // American scenario titles should be converted to British name
420         // Don't worry, names will be translated using language packs later
421         for (const ScenarioAlias& alias : ScenarioAliases)
422         {
423             if (String::Equals(alias.Alternative, name))
424             {
425                 log_verbose("Found alias: %s; will treat as: %s", name, alias.Original);
426                 String::Set(buffer, bufferSize, alias.Original);
427             }
428         }
429     }
430 } // namespace ScenarioSources
431