1 /*****************************************************************************
2 * Copyright (c) 2014-2018 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 "Data.h"
11 #include "FunctionCall.hpp"
12 #include "PaintIntercept.hpp"
13 #include "SegmentSupportHeightCall.hpp"
14 #include "SideTunnelCall.hpp"
15 #include "String.hpp"
16 #include "Utils.hpp"
17
18 #include <algorithm>
19 #include <cstdarg>
20 #include <cstring>
21 #include <openrct2/interface/Viewport.h>
22 #include <openrct2/rct2/RCT2.h>
23 #include <openrct2/ride/Ride.h>
24 #include <openrct2/ride/RideData.h>
25 #include <openrct2/ride/Track.h>
26 #include <openrct2/ride/TrackData.h>
27 #include <string>
28 #include <vector>
29
30 class PaintCodeGenerator
31 {
32 public:
Generate(uint8_t rideType)33 int Generate(uint8_t rideType)
34 {
35 auto filename = "paint_" + std::to_string(rideType) + ".c";
36 FILE* file = fopen(filename.c_str(), "w");
37 if (file == nullptr)
38 {
39 fprintf(stderr, "Unable to save to ./%s\n", filename.c_str());
40 return 1;
41 }
42
43 _file = file;
44 _rideType = rideType;
45 _rideName = std::string(RideCodeNames[rideType]);
46 Generate();
47
48 fclose(file);
49 return 0;
50 }
51
52 private:
53 std::string _rideName;
54 uint8_t _rideType;
55 FILE* _file;
56
57 bool _conditionalSupports;
58 bool _invertedTrack;
59
Generate()60 void Generate()
61 {
62 GenerateCopyrightHeader();
63 GenerateIncludes();
64 GenerateTrackFunctions();
65 GenerateMainFunction();
66 }
67
GenerateCopyrightHeader()68 void GenerateCopyrightHeader()
69 {
70 const char* copyrights[] = {
71 "/*****************************************************************************",
72 " * Copyright (c) 2014-2018 OpenRCT2 developers",
73 " *",
74 " * For a complete list of all authors, please refer to contributors.md",
75 " * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2",
76 " *",
77 " * OpenRCT2 is licensed under the GNU General Public License version 3.",
78 " *****************************************************************************/",
79 };
80
81 for (const auto copyright : copyrights)
82 {
83 WriteLine(0, copyright);
84 }
85 WriteLine();
86 }
87
GenerateIncludes()88 void GenerateIncludes()
89 {
90 const char* includes[] = {
91 "../../drawing/Drawing.h",
92 "../../paint/supports.h",
93 "../../interface/Viewport.h",
94 "../../paint/tile_element/tile_element.h",
95 "../../paint/paint.h",
96 "../../sprites.h",
97 "../../world/Map.h",
98 "../../world/Sprite.h",
99 "../ride_data.h",
100 "../TrackData.h",
101 "../track_paint.h",
102 };
103 for (auto include : includes)
104 {
105 WriteLine(0, "#include \"%s\"", include);
106 }
107 WriteLine();
108 }
109
GenerateTrackFunctions()110 void GenerateTrackFunctions()
111 {
112 for (int trackType = 0; trackType < 256; trackType++)
113 {
114 if (IsTrackTypeSupported(trackType))
115 {
116 GenerateTrackFunction(trackType);
117 WriteLine();
118 }
119
120 if (trackType == TrackElemType::EndStation)
121 {
122 const uint32_t* paintFunctionList = RideTypeTrackPaintFunctionsOld[_rideType];
123 WriteLine(
124 0, "/** rct2: 0x%08X, 0x%08X, 0x%08X */", paintFunctionList[TrackElemType::EndStation],
125 paintFunctionList[TrackElemType::BeginStation], paintFunctionList[TrackElemType::MiddleStation]);
126 WriteLine(
127 0,
128 "static void " + _rideName
129 + "_track_station(uint8_t rideIndex, uint8_t trackSequence, uint8_t direction, int height, "
130 "TileElement * tileElement)");
131 WriteLine(0, "{");
132 WriteLine(0, "}");
133 WriteLine();
134 }
135 }
136 }
137
GenerateTrackFunction(int trackType)138 void GenerateTrackFunction(int trackType)
139 {
140 WriteLine(0, "/** rct2: 0x%08X */", RideTypeTrackPaintFunctionsOld[_rideType][trackType]);
141 WriteLine(
142 0,
143 "static void " + GetTrackFunctionName(trackType)
144 + "(uint8_t rideIndex, uint8_t trackSequence, uint8_t direction, int height, TileElement * tileElement)");
145 WriteLine(0, "{");
146 if (!GenerateMirrorCall(1, trackType))
147 {
148 if (_rideType == RIDE_TYPE_MULTI_DIMENSION_ROLLER_COASTER || _rideType == RIDE_TYPE_FLYING_ROLLER_COASTER
149 || _rideType == RIDE_TYPE_LAY_DOWN_ROLLER_COASTER)
150 {
151 WriteLine(1, "if (!tileElement->AsTrack()->IsInverted()) {");
152 _invertedTrack = false;
153 GenerateTrackFunctionBody(2, trackType);
154 WriteLine(1, "} else {");
155 _invertedTrack = true;
156 GenerateTrackFunctionBody(2, trackType);
157 WriteLine(1, "}");
158 }
159 else
160 {
161 _invertedTrack = false;
162 GenerateTrackFunctionBody(1, trackType);
163 }
164 }
165 WriteLine(0, "}");
166 }
167
GenerateTrackFunctionBody(int tabs,int trackType)168 void GenerateTrackFunctionBody(int tabs, int trackType)
169 {
170 int numSequences = Utils::getTrackSequenceCount(_rideType, trackType);
171 if (numSequences > 1)
172 {
173 WriteLine(tabs, "switch (trackSequence) {");
174 for (int trackSequence = 0; trackSequence < numSequences; trackSequence++)
175 {
176 WriteLine(tabs, "case %d:", trackSequence);
177 GenerateTrackSequence(tabs + 1, trackType, trackSequence);
178 WriteLine(tabs + 1, "break;");
179 }
180 WriteLine(tabs, "}");
181 }
182 else
183 {
184 GenerateTrackSequence(tabs, trackType, 0);
185 }
186 }
187
GenerateMirrorCall(int tabs,int trackType)188 bool GenerateMirrorCall(int tabs, int trackType)
189 {
190 uint8_t mirrorTable[][3] = {
191 { 0, TrackElemType::Down25, TrackElemType::Up25 },
192 { 0, TrackElemType::Down60, TrackElemType::Up60 },
193 { 0, TrackElemType::FlatToDown25, TrackElemType::Up25ToFlat },
194 { 0, TrackElemType::Down25ToDown60, TrackElemType::Up60ToUp25 },
195 { 0, TrackElemType::Down60ToDown25, TrackElemType::Up25ToUp60 },
196 { 0, TrackElemType::Down25ToFlat, TrackElemType::FlatToUp25 },
197 { 0, TrackElemType::LeftBankToDown25, TrackElemType::Up25ToRightBank },
198 { 0, TrackElemType::RightBankToDown25, TrackElemType::Up25ToLeftBank },
199 { 0, TrackElemType::Down25ToLeftBank, TrackElemType::RightBankToUp25 },
200 { 0, TrackElemType::Down25ToRightBank, TrackElemType::LeftBankToUp25 },
201 { 0, TrackElemType::RightBank, TrackElemType::LeftBank },
202 { 0, TrackElemType::Down60ToFlat, TrackElemType::FlatToUp60 },
203 { 0, TrackElemType::FlatToDown60, TrackElemType::Up60ToFlat },
204 { 0, TrackElemType::Down25Covered, TrackElemType::Up25Covered },
205 { 0, TrackElemType::Down60Covered, TrackElemType::Up60Covered },
206 { 0, TrackElemType::FlatToDown25Covered, TrackElemType::Up25ToFlatCovered },
207 { 0, TrackElemType::Down25ToDown60Covered, TrackElemType::Up60ToUp25Covered },
208 { 0, TrackElemType::Down60ToDown25Covered, TrackElemType::Up25ToUp60Covered },
209 { 0, TrackElemType::Down25ToFlatCovered, TrackElemType::FlatToUp25Covered },
210 { 0, TrackElemType::Down25LeftBanked, TrackElemType::Up25RightBanked },
211 { 0, TrackElemType::Down25RightBanked, TrackElemType::Up25LeftBanked },
212 { 0, TrackElemType::Down90, TrackElemType::Up90 },
213 { 0, TrackElemType::Down90ToDown60, TrackElemType::Up60ToUp90 },
214 // { 0, TrackElemType::Down60ToDown90, TrackElemType::Up90ToUp60 },
215 { 0, TrackElemType::RightBankedDown25ToDown25, TrackElemType::Up25ToLeftBankedUp25 },
216 { 0, TrackElemType::LeftBankedDown25ToDown25, TrackElemType::Up25ToRightBankedUp25 },
217 { 0, TrackElemType::Down25ToRightBankedDown25, TrackElemType::LeftBankedUp25ToUp25 },
218 { 0, TrackElemType::Down25ToLeftBankedDown25, TrackElemType::RightBankedUp25ToUp25 },
219 { 0, TrackElemType::RightBankedDown25ToRightBankedFlat, TrackElemType::LeftBankedFlatToLeftBankedUp25 },
220 { 0, TrackElemType::LeftBankedDown25ToLeftBankedFlat, TrackElemType::RightBankedFlatToRightBankedUp25 },
221 { 0, TrackElemType::RightBankedFlatToRightBankedDown25, TrackElemType::LeftBankedUp25ToLeftBankedFlat },
222 { 0, TrackElemType::LeftBankedFlatToLeftBankedDown25, TrackElemType::RightBankedUp25ToRightBankedFlat },
223 { 0, TrackElemType::RightBankedDown25ToFlat, TrackElemType::FlatToLeftBankedUp25 },
224 { 0, TrackElemType::LeftBankedDown25ToFlat, TrackElemType::FlatToRightBankedUp25 },
225 { 0, TrackElemType::FlatToRightBankedDown25, TrackElemType::LeftBankedUp25ToFlat },
226 { 0, TrackElemType::FlatToLeftBankedDown25, TrackElemType::RightBankedUp25ToFlat },
227
228 { 1, TrackElemType::RightQuarterTurn5Tiles, TrackElemType::LeftQuarterTurn5Tiles },
229 { 1, TrackElemType::BankedRightQuarterTurn5Tiles, TrackElemType::BankedLeftQuarterTurn5Tiles },
230 { 1, TrackElemType::RightQuarterTurn5TilesDown25, TrackElemType::LeftQuarterTurn5TilesUp25 },
231 { 1, TrackElemType::RightQuarterTurn5TilesCovered, TrackElemType::LeftQuarterTurn5TilesCovered },
232 { 1, TrackElemType::RightBankedQuarterTurn5TileDown25, TrackElemType::LeftBankedQuarterTurn5TileUp25 },
233 { 2, TrackElemType::LeftQuarterTurn5TilesDown25, TrackElemType::RightQuarterTurn5TilesUp25 },
234 { 2, TrackElemType::LeftBankedQuarterTurn5TileDown25, TrackElemType::RightBankedQuarterTurn5TileUp25 },
235
236 { 3, TrackElemType::RightQuarterTurn3Tiles, TrackElemType::LeftQuarterTurn3Tiles },
237 { 3, TrackElemType::RightBankedQuarterTurn3Tiles, TrackElemType::LeftBankedQuarterTurn3Tiles },
238 { 3, TrackElemType::RightQuarterTurn3TilesDown25, TrackElemType::LeftQuarterTurn3TilesUp25 },
239 { 3, TrackElemType::RightQuarterTurn3TilesCovered, TrackElemType::LeftQuarterTurn3TilesCovered },
240 { 3, TrackElemType::RightBankedQuarterTurn3TileDown25, TrackElemType::LeftBankedQuarterTurn3TileUp25 },
241 { 4, TrackElemType::LeftQuarterTurn3TilesDown25, TrackElemType::RightQuarterTurn3TilesUp25 },
242 { 4, TrackElemType::LeftBankedQuarterTurn3TileDown25, TrackElemType::RightBankedQuarterTurn3TileUp25 },
243
244 { 5, TrackElemType::RightQuarterTurn1Tile, TrackElemType::LeftQuarterTurn1Tile },
245 { 5, TrackElemType::RightQuarterTurn1TileDown60, TrackElemType::LeftQuarterTurn1TileUp60 },
246 { 5, TrackElemType::RightQuarterTurn1TileDown90, TrackElemType::LeftQuarterTurn1TileUp90 },
247 { 6, TrackElemType::LeftQuarterTurn1TileDown60, TrackElemType::RightQuarterTurn1TileUp60 },
248 { 6, TrackElemType::LeftQuarterTurn1TileDown90, TrackElemType::RightQuarterTurn1TileUp90 },
249
250 { 7, TrackElemType::RightEighthToOrthogonal, TrackElemType::LeftEighthToDiag },
251 { 7, TrackElemType::RightEighthBankToOrthogonal, TrackElemType::LeftEighthBankToDiag },
252 { 8, TrackElemType::LeftEighthToOrthogonal, TrackElemType::RightEighthToDiag },
253 { 8, TrackElemType::LeftEighthBankToOrthogonal, TrackElemType::RightEighthBankToDiag },
254
255 { 9, TrackElemType::RightHalfBankedHelixDownSmall, TrackElemType::LeftHalfBankedHelixUpSmall },
256 { 10, TrackElemType::LeftHalfBankedHelixDownSmall, TrackElemType::RightHalfBankedHelixUpSmall },
257 { 11, TrackElemType::RightHalfBankedHelixDownLarge, TrackElemType::LeftHalfBankedHelixUpLarge },
258 { 12, TrackElemType::LeftHalfBankedHelixDownLarge, TrackElemType::RightHalfBankedHelixUpLarge },
259
260 { 13, TrackElemType::Down60ToFlatLongBase, TrackElemType::FlatToUp60LongBase },
261 { 13, TrackElemType::FlatToDown60LongBase, TrackElemType::Up60ToFlatLongBase },
262
263 { 14, TrackElemType::RightCorkscrewDown, TrackElemType::LeftCorkscrewUp },
264 { 15, TrackElemType::LeftCorkscrewDown, TrackElemType::RightCorkscrewUp },
265
266 { 16, TrackElemType::HalfLoopDown, TrackElemType::HalfLoopUp },
267
268 { 17, TrackElemType::InvertedFlatToDown90QuarterLoop, TrackElemType::Up90ToInvertedFlatQuarterLoop },
269
270 { 18, TrackElemType::LeftBarrelRollDownToUp, TrackElemType::LeftBarrelRollUpToDown },
271 { 18, TrackElemType::RightBarrelRollDownToUp, TrackElemType::RightBarrelRollUpToDown },
272
273 { 19, TrackElemType::LeftLargeHalfLoopDown, TrackElemType::LeftLargeHalfLoopUp },
274 { 19, TrackElemType::RightLargeHalfLoopDown, TrackElemType::RightLargeHalfLoopUp },
275 };
276
277 for (size_t i = 0; i < (sizeof(mirrorTable) / sizeof(mirrorTable[0])); i++)
278 {
279 if (mirrorTable[i][1] == trackType)
280 {
281 std::string destFuncName = GetTrackFunctionName(mirrorTable[i][2]);
282 switch (mirrorTable[i][0])
283 {
284 case 0:
285 WriteLine(
286 tabs, "%s(rideIndex, trackSequence, (direction + 2) & 3, height, tileElement);",
287 destFuncName.c_str());
288 break;
289 case 1:
290 WriteLine(tabs, "trackSequence = mapLeftQuarterTurn5TilesToRightQuarterTurn5Tiles[trackSequence];");
291 WriteLine(
292 tabs, "%s(rideIndex, trackSequence, (direction - 1) & 3, height, tileElement);",
293 destFuncName.c_str());
294 break;
295 case 2:
296 WriteLine(tabs, "trackSequence = mapLeftQuarterTurn5TilesToRightQuarterTurn5Tiles[trackSequence];");
297 WriteLine(
298 tabs, "%s(rideIndex, trackSequence, (direction + 1) & 3, height, tileElement);",
299 destFuncName.c_str());
300 break;
301 case 3:
302 WriteLine(tabs, "trackSequence = mapLeftQuarterTurn3TilesToRightQuarterTurn3Tiles[trackSequence];");
303 WriteLine(
304 tabs, "%s(rideIndex, trackSequence, (direction - 1) & 3, height, tileElement);",
305 destFuncName.c_str());
306 break;
307 case 4:
308 WriteLine(tabs, "trackSequence = mapLeftQuarterTurn3TilesToRightQuarterTurn3Tiles[trackSequence];");
309 WriteLine(
310 tabs, "%s(rideIndex, trackSequence, (direction + 1) & 3, height, tileElement);",
311 destFuncName.c_str());
312 break;
313 case 5:
314 WriteLine(
315 tabs, "%s(rideIndex, trackSequence, (direction - 1) & 3, height, tileElement);",
316 destFuncName.c_str());
317 break;
318 case 6:
319 WriteLine(
320 tabs, "%s(rideIndex, trackSequence, (direction + 1) & 3, height, tileElement);",
321 destFuncName.c_str());
322 break;
323 case 7:
324 WriteLine(tabs, "trackSequence = mapLeftEighthTurnToOrthogonal[trackSequence];");
325 WriteLine(
326 tabs, "%s(rideIndex, trackSequence, (direction + 3) & 3, height, tileElement);",
327 destFuncName.c_str());
328 break;
329 case 8:
330 WriteLine(tabs, "trackSequence = mapLeftEighthTurnToOrthogonal[trackSequence];");
331 WriteLine(
332 tabs, "%s(rideIndex, trackSequence, (direction + 2) & 3, height, tileElement);",
333 destFuncName.c_str());
334 break;
335 case 9:
336 WriteLine(tabs, "if (trackSequence >= 4) {");
337 WriteLine(tabs + 1, "trackSequence -= 4;");
338 WriteLine(tabs + 1, "direction = (direction + 1) & 3;");
339 WriteLine(tabs, "}");
340 WriteLine(tabs, "trackSequence = mapLeftQuarterTurn3TilesToRightQuarterTurn3Tiles[trackSequence];");
341 WriteLine(
342 tabs, "%s(rideIndex, trackSequence, (direction - 1) & 3, height, tileElement);",
343 destFuncName.c_str());
344 break;
345 case 10:
346 WriteLine(tabs, "if (trackSequence >= 4) {");
347 WriteLine(tabs + 1, "trackSequence -= 4;");
348 WriteLine(tabs + 1, "direction = (direction - 1) & 3;");
349 WriteLine(tabs, "}");
350 WriteLine(tabs, "trackSequence = mapLeftQuarterTurn3TilesToRightQuarterTurn3Tiles[trackSequence];");
351 WriteLine(
352 tabs, "%s(rideIndex, trackSequence, (direction + 1) & 3, height, tileElement);",
353 destFuncName.c_str());
354 break;
355 case 11:
356 WriteLine(tabs, "if (trackSequence >= 7) {");
357 WriteLine(tabs + 1, "trackSequence -= 7;");
358 WriteLine(tabs + 1, "direction = (direction + 1) & 3;");
359 WriteLine(tabs, "}");
360 WriteLine(tabs, "trackSequence = mapLeftQuarterTurn5TilesToRightQuarterTurn5Tiles[trackSequence];");
361 WriteLine(
362 tabs, "%s(rideIndex, trackSequence, (direction - 1) & 3, height, tileElement);",
363 destFuncName.c_str());
364 break;
365 case 12:
366 WriteLine(tabs, "if (trackSequence >= 7) {");
367 WriteLine(tabs + 1, "trackSequence -= 7;");
368 WriteLine(tabs + 1, "direction = (direction - 1) & 3;");
369 WriteLine(tabs, "}");
370 WriteLine(tabs, "trackSequence = mapLeftQuarterTurn5TilesToRightQuarterTurn5Tiles[trackSequence];");
371 WriteLine(
372 tabs, "%s(rideIndex, trackSequence, (direction + 1) & 3, height, tileElement);",
373 destFuncName.c_str());
374 break;
375 case 13:
376 WriteLine(
377 tabs, "%s(rideIndex, 3 - trackSequence, (direction + 2) & 3, height, tileElement);",
378 destFuncName.c_str());
379 break;
380 case 14:
381 WriteLine(
382 tabs, "%s(rideIndex, 2 - trackSequence, (direction - 1) & 3, height, tileElement);",
383 destFuncName.c_str());
384 break;
385 case 15:
386 WriteLine(
387 tabs, "%s(rideIndex, 2 - trackSequence, (direction + 1) & 3, height, tileElement);",
388 destFuncName.c_str());
389 break;
390 case 16:
391 WriteLine(
392 tabs, "%s(rideIndex, 3 - trackSequence, direction, height, tileElement);", destFuncName.c_str());
393 break;
394 case 17:
395 WriteLine(
396 tabs, "%s(rideIndex, 2 - trackSequence, direction, height, tileElement);", destFuncName.c_str());
397 break;
398 case 18:
399 WriteLine(
400 tabs, "%s(rideIndex, 2 - trackSequence, (direction + 2) & 3, height, tileElement);",
401 destFuncName.c_str());
402 break;
403 case 19:
404 WriteLine(
405 tabs, "%s(rideIndex, 6 - trackSequence, direction, height, tileElement);", destFuncName.c_str());
406 break;
407 }
408 return true;
409 }
410 }
411 return false;
412 }
413
ExtractMetalSupportCalls(std::vector<function_call> calls[4],std::vector<function_call> output[4])414 void ExtractMetalSupportCalls(std::vector<function_call> calls[4], std::vector<function_call> output[4])
415 {
416 for (int direction = 0; direction < 4; direction++)
417 {
418 auto cutPoint = std::find_if(calls[direction].begin(), calls[direction].end(), [](function_call call) {
419 return (call.function == SUPPORTS_METAL_A || call.function == SUPPORTS_METAL_B);
420 });
421 output[direction].insert(output[direction].begin(), cutPoint, calls[direction].end());
422 calls[direction].erase(cutPoint, calls[direction].end());
423 }
424 }
425
GenerateTrackSequence(int tabs,int trackType,int trackSequence)426 void GenerateTrackSequence(int tabs, int trackType, int trackSequence)
427 {
428 int height = 48;
429 _conditionalSupports = false;
430 bool blockSegmentsBeforeSupports = false;
431
432 std::vector<function_call> calls[4], chainLiftCalls[4], cableLiftCalls[4];
433 TunnelCall tileTunnelCalls[4][4];
434 int16_t verticalTunnelHeights[4];
435 std::vector<SegmentSupportCall> segmentSupportCalls[4];
436 support_height generalSupports[4] = {};
437 for (int direction = 0; direction < 4; direction++)
438 {
439 TileElement tileElement = {};
440 tileElement.SetType(TILE_ELEMENT_TYPE_TRACK);
441 tileElement.SetLastForTile(true);
442 tileElement.AsTrack()->SetTrackType(trackType);
443 tileElement.base_height = 3;
444 if (_invertedTrack)
445 {
446 tileElement.AsTrack()->SetInverted(true);
447 }
448 g_currently_drawn_item = &tileElement;
449
450 // Set position
451 RCT2_GLOBAL(0x009DE56A, int16_t) = 64;
452 RCT2_GLOBAL(0x009DE56E, int16_t) = 64;
453
454 function_call callBuffer[256] = {};
455 PaintIntercept::ClearCalls();
456 CallOriginal(trackType, direction, trackSequence, height, &tileElement);
457 int numCalls = PaintIntercept::GetCalls(callBuffer);
458 calls[direction].insert(calls[direction].begin(), callBuffer, callBuffer + numCalls);
459
460 for (auto&& call : calls[direction])
461 {
462 if (call.function == SET_SEGMENT_HEIGHT)
463 {
464 blockSegmentsBeforeSupports = true;
465 break;
466 }
467 }
468
469 segmentSupportCalls[direction] = SegmentSupportHeightCall::getSegmentCalls(gSupportSegments, direction);
470 generalSupports[direction] = gSupport;
471 if (gSupport.slope != 0xFF && gSupport.height != 0)
472 {
473 generalSupports[direction].height -= height;
474 }
475
476 // Get chain lift calls
477 tileElement.AsTrack()->SetHasChain(true);
478 PaintIntercept::ClearCalls();
479 CallOriginal(trackType, direction, trackSequence, height, &tileElement);
480 numCalls = PaintIntercept::GetCalls(callBuffer);
481 chainLiftCalls[direction].insert(chainLiftCalls[direction].begin(), callBuffer, callBuffer + numCalls);
482
483 // Get cable lift calls (giga coaster only)
484 if (_rideType == RIDE_TYPE_GIGA_COASTER)
485 {
486 tileElement.type = 0;
487 tileElement.AsTrack()->SetHasCableLift(true);
488 PaintIntercept::ClearCalls();
489 CallOriginal(trackType, direction, trackSequence, height, &tileElement);
490 numCalls = PaintIntercept::GetCalls(callBuffer);
491 cableLiftCalls[direction].insert(cableLiftCalls[direction].begin(), callBuffer, callBuffer + numCalls);
492 }
493
494 // Check a different position for direction 0 to see if supports are different
495 if (direction == 0)
496 {
497 RCT2_GLOBAL(0x009DE56A, int16_t) = 64 + 32;
498 RCT2_GLOBAL(0x009DE56E, int16_t) = 64;
499 tileElement.type = 0;
500 tileElement.AsTrack()->SetHasCableLift(false);
501 PaintIntercept::ClearCalls();
502 CallOriginal(trackType, direction, trackSequence, height, &tileElement);
503 numCalls = PaintIntercept::GetCalls(callBuffer);
504 std::vector<function_call> checkCalls = std::vector<function_call>(callBuffer, callBuffer + numCalls);
505 if (!CompareFunctionCalls(checkCalls, calls[direction]))
506 {
507 _conditionalSupports = true;
508 }
509 }
510
511 GetTunnelCalls(trackType, direction, trackSequence, height, &tileElement, tileTunnelCalls, verticalTunnelHeights);
512 }
513
514 std::vector<function_call> supportCalls[4], chainLiftSupportCalls[4], cableLiftSupportCalls[4];
515 if (blockSegmentsBeforeSupports)
516 {
517 ExtractMetalSupportCalls(calls, supportCalls);
518 ExtractMetalSupportCalls(cableLiftCalls, cableLiftSupportCalls);
519 ExtractMetalSupportCalls(chainLiftCalls, chainLiftSupportCalls);
520 }
521
522 if (_rideType == RIDE_TYPE_GIGA_COASTER && !CompareFunctionCalls(calls, cableLiftCalls))
523 {
524 WriteLine(tabs, "if (tileElement->AsTrack()->HasCableLift()) {");
525 GenerateCalls(tabs + 1, cableLiftCalls, height);
526
527 if (!CompareFunctionCalls(calls, chainLiftCalls))
528 {
529 WriteLine(tabs, "} else if (tileElement->AsTrack()->HasChain()) {");
530 GenerateCalls(tabs + 1, chainLiftCalls, height);
531 }
532
533 WriteLine(tabs, "} else {");
534 GenerateCalls(tabs + 1, calls, height);
535 WriteLine(tabs, "}");
536 }
537 else if (!CompareFunctionCalls(calls, chainLiftCalls))
538 {
539 WriteLine(tabs, "if (tileElement->AsTrack()->HasChain()) {");
540 GenerateCalls(tabs + 1, chainLiftCalls, height);
541 WriteLine(tabs, "} else {");
542 GenerateCalls(tabs + 1, calls, height);
543 WriteLine(tabs, "}");
544 }
545 else
546 {
547 GenerateCalls(tabs, calls, height);
548 }
549
550 if (blockSegmentsBeforeSupports)
551 {
552 if (_rideType == RIDE_TYPE_GIGA_COASTER && !CompareFunctionCalls(supportCalls, cableLiftSupportCalls))
553 {
554 printf("Error: Supports differ for cable lift.\n");
555 }
556 else if (!CompareFunctionCalls(supportCalls, chainLiftSupportCalls))
557 {
558 printf("Error: Supports differ for chain lift\n");
559 }
560 WriteLine();
561 GenerateSegmentSupportCall(tabs, segmentSupportCalls);
562
563 bool conditionalSupports = _conditionalSupports;
564 _conditionalSupports = false;
565 if (conditionalSupports)
566 {
567 WriteLine(tabs, "if (track_paint_util_should_paint_supports(gPaintMapPosition)) {");
568 tabs++;
569 }
570 GenerateCalls(tabs, supportCalls, height);
571 if (conditionalSupports)
572 {
573 tabs--;
574 WriteLine(tabs, "}");
575 }
576 WriteLine();
577 }
578
579 GenerateTunnelCall(tabs, tileTunnelCalls, verticalTunnelHeights);
580 if (!blockSegmentsBeforeSupports)
581 {
582 GenerateSegmentSupportCall(tabs, segmentSupportCalls);
583 }
584 GenerateGeneralSupportCall(tabs, generalSupports);
585 }
586
GenerateCalls(int tabs,std::vector<function_call> calls[4],int height)587 void GenerateCalls(int tabs, std::vector<function_call> calls[4], int height)
588 {
589 std::vector<function_call> commonCalls = TrimCommonCallsEnd(calls);
590
591 int totalCalls = 0;
592 for (int direction = 0; direction < 4; direction++)
593 {
594 totalCalls += calls[direction].size();
595 }
596 if (totalCalls != 0)
597 {
598 WriteLine(tabs, "switch (direction) {");
599 for (int direction = 0; direction < 4; direction++)
600 {
601 if (calls[direction].empty())
602 continue;
603
604 WriteLine(tabs, "case %d:", direction);
605 for (int d2 = direction + 1; d2 < 4; d2++)
606 {
607 if (CompareFunctionCalls(calls[direction], calls[d2]))
608 {
609 // Clear identical other direction calls and add case for it
610 calls[d2].clear();
611 WriteLine(tabs, "case %d:", d2);
612 }
613 }
614
615 for (auto call : calls[direction])
616 {
617 GenerateCalls(tabs + 1, call, height, direction);
618 }
619 WriteLine(tabs + 1, "break;");
620 }
621 WriteLine(tabs, "}");
622 }
623
624 for (auto call : commonCalls)
625 {
626 GenerateCalls(tabs, call, height, 0);
627 }
628 }
629
GenerateCalls(int tabs,const function_call & call,int height,int direction)630 void GenerateCalls(int tabs, const function_call& call, int height, int direction)
631 {
632 switch (call.function)
633 {
634 case PAINT_98196C:
635 case PAINT_98197C:
636 case PAINT_98198C:
637 case PAINT_98199C:
638 GeneratePaintCall(tabs, call, height, direction);
639 break;
640 case SUPPORTS_METAL_A:
641 case SUPPORTS_METAL_B:
642 {
643 int callTabs = tabs;
644 if (_conditionalSupports)
645 {
646 WriteLine(tabs, "if (track_paint_util_should_paint_supports(gPaintMapPosition)) {");
647 callTabs++;
648 }
649
650 WriteLine(
651 callTabs, "%s(%d, %d, %d, height%s, %s);", GetFunctionCallName(call.function), call.supports.type,
652 call.supports.segment, call.supports.special,
653 GetOffsetExpressionString(call.supports.height - height).c_str(),
654 GetImageIdString(call.supports.colour_flags).c_str());
655
656 if (_conditionalSupports)
657 {
658 WriteLine(tabs, "}");
659 }
660 break;
661 }
662 case SUPPORTS_WOOD_A:
663 case SUPPORTS_WOOD_B:
664 WriteLine(
665 tabs, "%s(%d, %d, height%s, %s, NULL);", GetFunctionCallName(call.function), call.supports.type,
666 call.supports.special, GetOffsetExpressionString(call.supports.height - height).c_str(),
667 GetImageIdString(call.supports.colour_flags).c_str());
668 break;
669 }
670 }
671
GeneratePaintCall(int tabs,const function_call & call,int height,int direction)672 void GeneratePaintCall(int tabs, const function_call& call, int height, int direction)
673 {
674 const char* funcName = GetFunctionCallName(call.function);
675 std::string imageId = GetImageIdString(call.paint.image_id);
676 std::string s = String::Format("%s_rotated(direction, %s, ", funcName, imageId.c_str());
677 s += FormatXYSwap(call.paint.offset.x, call.paint.offset.y, direction);
678 s += ", ";
679 s += FormatXYSwap(call.paint.bound_box_length.x, call.paint.bound_box_length.y, direction);
680 s += String::Format(
681 ", %d, height%s", call.paint.bound_box_length.z, GetOffsetExpressionString(call.paint.z_offset - height).c_str());
682
683 if (call.function != PAINT_98196C)
684 {
685 s += ", ";
686 s += FormatXYSwap(call.paint.bound_box_offset.x, call.paint.bound_box_offset.y, direction);
687 s += String::Format(", height%s", GetOffsetExpressionString(call.paint.bound_box_offset.z - height).c_str());
688 }
689
690 s += ");";
691 WriteLine(tabs, s);
692 }
693
FormatXYSwap(int16_t x,int16_t y,int direction)694 std::string FormatXYSwap(int16_t x, int16_t y, int direction)
695 {
696 if (direction & 1)
697 {
698 return String::Format("%d, %d", y, x);
699 }
700 else
701 {
702 return String::Format("%d, %d", x, y);
703 }
704 }
705
TrimCommonCallsEnd(std::vector<function_call> calls[4])706 std::vector<function_call> TrimCommonCallsEnd(std::vector<function_call> calls[4])
707 {
708 std::vector<function_call> commonCalls;
709
710 while (calls[0].size() != 0)
711 {
712 function_call lastCall = calls[0].back();
713 for (int i = 0; i < 4; i++)
714 {
715 if (calls[i].empty() || !CompareFunctionCall(calls[i].back(), lastCall))
716 {
717 goto finished;
718 }
719 }
720 for (int i = 0; i < 4; i++)
721 {
722 calls[i].pop_back();
723 }
724 commonCalls.push_back(lastCall);
725 }
726
727 finished:
728 return commonCalls;
729 }
730
CompareFunctionCalls(const std::vector<function_call> a[4],const std::vector<function_call> b[4])731 bool CompareFunctionCalls(const std::vector<function_call> a[4], const std::vector<function_call> b[4])
732 {
733 for (size_t i = 0; i < 4; i++)
734 {
735 if (!CompareFunctionCalls(a[i], b[i]))
736 {
737 return false;
738 }
739 }
740 return true;
741 }
742
CompareFunctionCalls(const std::vector<function_call> & a,const std::vector<function_call> & b)743 bool CompareFunctionCalls(const std::vector<function_call>& a, const std::vector<function_call>& b)
744 {
745 if (a.size() != b.size())
746 return false;
747 for (size_t i = 0; i < a.size(); i++)
748 {
749 if (!CompareFunctionCall(a[i], b[i]))
750 {
751 return false;
752 }
753 }
754 return true;
755 }
756
CompareFunctionCall(const function_call a,const function_call & b)757 bool CompareFunctionCall(const function_call a, const function_call& b)
758 {
759 return FunctionCall::AssertsEquals(a, b);
760 }
761
GetFunctionCallName(int function)762 const char* GetFunctionCallName(int function)
763 {
764 const char* functionNames[] = {
765 "sub_98196C",
766 "sub_98197C",
767 "sub_98198C",
768 "sub_98199C",
769 "metal_a_supports_paint_setup",
770 "metal_b_supports_paint_setup",
771 "wooden_a_supports_paint_setup",
772 "wooden_b_supports_paint_setup",
773 };
774 return functionNames[function];
775 }
776
GetTunnelCalls(int trackType,int direction,int trackSequence,int height,TileElement * tileElement,TunnelCall tileTunnelCalls[4][4],int16_t verticalTunnelHeights[4])777 bool GetTunnelCalls(
778 int trackType, int direction, int trackSequence, int height, TileElement* tileElement, TunnelCall tileTunnelCalls[4][4],
779 int16_t verticalTunnelHeights[4])
780 {
781 TestPaint::ResetTunnels();
782
783 for (int offset = -8; offset <= 8; offset += 8)
784 {
785 CallOriginal(trackType, direction, trackSequence, height + offset, tileElement);
786 }
787
788 uint8_t rightIndex = (4 - direction) % 4;
789 uint8_t leftIndex = (rightIndex + 1) % 4;
790
791 for (int i = 0; i < 4; ++i)
792 {
793 tileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED;
794 }
795 if (gRightTunnelCount == 0)
796 {
797 tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_NONE;
798 }
799 else if (gRightTunnelCount == 3)
800 {
801 tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_CALL;
802 tileTunnelCalls[direction][rightIndex].offset = SideTunnelCall::GetTunnelOffset(height, gRightTunnels);
803 tileTunnelCalls[direction][rightIndex].type = gRightTunnels[0].type;
804 }
805 else
806 {
807 printf("Multiple tunnels on one side aren't supported.\n");
808 return false;
809 }
810
811 if (gLeftTunnelCount == 0)
812 {
813 tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_NONE;
814 }
815 else if (gLeftTunnelCount == 3)
816 {
817 tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_CALL;
818 tileTunnelCalls[direction][leftIndex].offset = SideTunnelCall::GetTunnelOffset(height, gLeftTunnels);
819 tileTunnelCalls[direction][leftIndex].type = gLeftTunnels[0].type;
820 }
821 else
822 {
823 printf("Multiple tunnels on one side aren't supported.\n");
824 return false;
825 }
826
827 // Vertical tunnel
828 gVerticalTunnelHeight = 0;
829 CallOriginal(trackType, direction, trackSequence, height, tileElement);
830
831 int verticalTunnelHeight = gVerticalTunnelHeight;
832 if (verticalTunnelHeight != 0)
833 {
834 verticalTunnelHeight = (verticalTunnelHeight * 16) - height;
835 }
836 verticalTunnelHeights[direction] = verticalTunnelHeight;
837 return true;
838 }
839
GenerateTunnelCall(int tabs,TunnelCall tileTunnelCalls[4][4],int16_t verticalTunnelHeights[4])840 void GenerateTunnelCall(int tabs, TunnelCall tileTunnelCalls[4][4], int16_t verticalTunnelHeights[4])
841 {
842 constexpr uint8_t TunnelLeft = 0;
843 constexpr uint8_t TunnelRight = 1;
844 constexpr uint8_t TunnelNA = 255;
845 static const uint8_t dsToWay[4][4] = {
846 { TunnelRight, TunnelLeft, TunnelNA, TunnelNA },
847 { TunnelLeft, TunnelNA, TunnelNA, TunnelRight },
848 { TunnelNA, TunnelNA, TunnelRight, TunnelLeft },
849 { TunnelNA, TunnelRight, TunnelLeft, TunnelNA },
850 };
851
852 int16_t tunnelOffset[4] = { 0 };
853 uint8_t tunnelType[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
854 for (int direction = 0; direction < 4; direction++)
855 {
856 for (int side = 0; side < 4; side++)
857 {
858 auto tunnel = tileTunnelCalls[direction][side];
859 if (tunnel.call == TUNNELCALL_CALL)
860 {
861 tunnelOffset[direction] = tunnel.offset;
862 tunnelType[direction] = tunnel.type;
863 break;
864 }
865 }
866 }
867
868 if (AllMatch(tunnelOffset, 4) && AllMatch(tunnelType, 4))
869 {
870 if (tunnelType[0] != 0xFF)
871 {
872 GenerateTunnelCall(tabs, tunnelOffset[0], tunnelType[0]);
873 }
874 }
875 else if (
876 tunnelOffset[0] == tunnelOffset[3] && tunnelType[0] == tunnelType[3] && tunnelOffset[1] == tunnelOffset[2]
877 && tunnelType[1] == tunnelType[2] && tunnelType[0] != 0xFF)
878 {
879 if (tunnelType[0] != 0xFF)
880 {
881 WriteLine(tabs, "if (direction == 0 || direction == 3) {");
882 GenerateTunnelCall(tabs + 1, tunnelOffset[0], tunnelType[0]);
883 if (tunnelType[1] != 0xFF)
884 {
885 WriteLine(tabs, "} else {");
886 GenerateTunnelCall(tabs + 1, tunnelOffset[1], tunnelType[1]);
887 }
888 WriteLine(tabs, "}");
889 }
890 else
891 {
892 WriteLine(tabs, "if (direction == 1 || direction == 2) {");
893 GenerateTunnelCall(tabs + 1, tunnelOffset[1], tunnelType[1]);
894 WriteLine(tabs, "}");
895 }
896 }
897 else
898 {
899 WriteLine(tabs, "switch (direction) {");
900 for (int i = 0; i < 4; i++)
901 {
902 if (tunnelType[i] != 0xFF)
903 {
904 WriteLine(tabs, "case %d:", i);
905 for (int side = 0; side < 4; side++)
906 {
907 if (tileTunnelCalls[i][side].call == TUNNELCALL_CALL)
908 {
909 GenerateTunnelCall(
910 tabs + 1, tileTunnelCalls[i][side].offset, tileTunnelCalls[i][side].type, dsToWay[i][side]);
911 }
912 }
913 WriteLine(tabs + 1, "break;");
914 }
915 }
916 WriteLine(tabs, "}");
917 }
918
919 if (AllMatch(verticalTunnelHeights, 4))
920 {
921 int tunnelHeight = verticalTunnelHeights[0];
922 if (tunnelHeight != 0)
923 {
924 WriteLine(
925 tabs, "paint_util_set_vertical_tunnel(session, height%s);",
926 GetOffsetExpressionString(tunnelHeight).c_str());
927 }
928 }
929 }
930
GenerateTunnelCall(int tabs,int offset,int type,int way)931 void GenerateTunnelCall(int tabs, int offset, int type, int way)
932 {
933 switch (way)
934 {
935 case 0:
936 WriteLine(
937 tabs, "paint_util_push_tunnel_left(session, height%s, TUNNEL_%d);",
938 GetOffsetExpressionString(offset).c_str(), type);
939 break;
940 case 1:
941 WriteLine(
942 tabs, "paint_util_push_tunnel_right(session, height%s, TUNNEL_%d);",
943 GetOffsetExpressionString(offset).c_str(), type);
944 break;
945 }
946 }
947
GenerateTunnelCall(int tabs,int offset,int type)948 void GenerateTunnelCall(int tabs, int offset, int type)
949 {
950 WriteLine(
951 tabs, "paint_util_push_tunnel_rotated(session, direction, height%s, TUNNEL_%d);",
952 GetOffsetExpressionString(offset).c_str(), type);
953 }
954
GenerateSegmentSupportCall(int tabs,std::vector<SegmentSupportCall> segmentSupportCalls[4])955 void GenerateSegmentSupportCall(int tabs, std::vector<SegmentSupportCall> segmentSupportCalls[4])
956 {
957 for (size_t i = 0; i < segmentSupportCalls[0].size(); i++)
958 {
959 auto ssh = segmentSupportCalls[0][i];
960 std::string szCall = "paint_util_set_segment_support_height(session, ";
961 if (ssh.segments == SEGMENTS_ALL)
962 {
963 szCall += "SEGMENTS_ALL";
964 }
965 else
966 {
967 szCall += "paint_util_rotate_segments(";
968 szCall += GetORedSegments(ssh.segments);
969 szCall += ", direction)";
970 }
971 szCall += ", ";
972 if (ssh.height == 0xFFFF)
973 {
974 szCall += "0xFFFF";
975 szCall += String::Format(", 0);", ssh.slope);
976 }
977 else
978 {
979 szCall += std::to_string(ssh.height);
980 szCall += String::Format(", 0x%02X);", ssh.slope);
981 }
982 WriteLine(tabs, szCall);
983 }
984 }
985
GenerateGeneralSupportCall(int tabs,support_height generalSupports[4])986 void GenerateGeneralSupportCall(int tabs, support_height generalSupports[4])
987 {
988 if (generalSupports[0].height == 0 && generalSupports[0].slope == 0xFF)
989 {
990 return;
991 }
992
993 WriteLine(
994 tabs, "paint_util_set_general_support_height(session, height%s, 0x%02X);",
995 GetOffsetExpressionString((int16_t)generalSupports[0].height).c_str(), generalSupports[0].slope);
996 if (!AllMatch(generalSupports, 4))
997 {
998 // WriteLine(tabs, "#error Unsupported: different directional general supports");
999 }
1000 }
1001
GetImageIdString(uint32_t imageId)1002 std::string GetImageIdString(uint32_t imageId)
1003 {
1004 std::string result;
1005
1006 uint32_t image = imageId & 0x7FFFF;
1007 uint32_t palette = imageId & ~0x7FFFF;
1008
1009 std::string paletteName;
1010 if (palette == TestPaint::DEFAULT_SCHEME_TRACK)
1011 paletteName = "gTrackColours[SCHEME_TRACK]";
1012 else if (palette == TestPaint::DEFAULT_SCHEME_SUPPORTS)
1013 paletteName = "gTrackColours[SCHEME_SUPPORTS]";
1014 else if (palette == TestPaint::DEFAULT_SCHEME_MISC)
1015 paletteName = "gTrackColours[SCHEME_MISC]";
1016 else if (palette == TestPaint::DEFAULT_SCHEME_3)
1017 paletteName = "gTrackColours[SCHEME_3]";
1018 else
1019 {
1020 paletteName = String::Format("0x%08X", palette);
1021 }
1022
1023 if (image == 0)
1024 {
1025 result = paletteName;
1026 }
1027 else if (image & 0x70000)
1028 {
1029 result = String::Format("%s | vehicle.base_image_id + %d", paletteName.c_str(), image & ~0x70000);
1030 }
1031 else
1032 {
1033 result = String::Format("%s | %d", paletteName.c_str(), image);
1034 }
1035 return result;
1036 }
1037
GetOffsetExpressionString(int offset)1038 std::string GetOffsetExpressionString(int offset)
1039 {
1040 if (offset < 0)
1041 return std::string(" - ") + std::to_string(-offset);
1042 if (offset > 0)
1043 return std::string(" + ") + std::to_string(offset);
1044 return std::string();
1045 }
1046
GetORedSegments(int segments)1047 std::string GetORedSegments(int segments)
1048 {
1049 std::string s;
1050 int segmentsPrinted = 0;
1051 for (int i = 0; i < 9; i++)
1052 {
1053 if (segments & segment_offsets[i])
1054 {
1055 if (segmentsPrinted > 0)
1056 {
1057 s += " | ";
1058 }
1059 s += String::Format("SEGMENT_%02X", 0xB4 + 4 * i);
1060 segmentsPrinted++;
1061 }
1062 }
1063 return s;
1064 }
1065
AllMatch(T * arr,size_t count)1066 template<typename T> bool AllMatch(T* arr, size_t count)
1067 {
1068 for (size_t i = 1; i < count; i++)
1069 {
1070 if (memcmp((const void*)&arr[i], (const void*)&arr[0], sizeof(T)) != 0)
1071 {
1072 return false;
1073 }
1074 }
1075 return true;
1076 }
1077
CallOriginal(int trackType,int direction,int trackSequence,int height,TileElement * tileElement)1078 void CallOriginal(int trackType, int direction, int trackSequence, int height, TileElement* tileElement)
1079 {
1080 TestPaint::ResetEnvironment();
1081 TestPaint::ResetSupportHeights();
1082
1083 uint32_t* trackDirectionList = (uint32_t*)RideTypeTrackPaintFunctionsOld[_rideType][trackType];
1084 // Have to call from this point as it pushes esi and expects callee to pop it
1085 RCT2_CALLPROC_X(
1086 0x006C4934, _rideType, (int)trackDirectionList, direction, height, (int)tileElement, 0 * sizeof(Ride),
1087 trackSequence);
1088 }
1089
GenerateMainFunction()1090 void GenerateMainFunction()
1091 {
1092 WriteLine(0, "TRACK_PAINT_FUNCTION get_track_paint_function_" + _rideName + "(int trackType, int direction)");
1093 WriteLine(0, "{");
1094 WriteLine(1, "switch (trackType) {");
1095 for (int trackType = 0; trackType < 256; trackType++)
1096 {
1097 if (trackType == TrackElemType::EndStation)
1098 {
1099 WriteLine(1, "case " + std::string(TrackElemNames[TrackElemType::EndStation]) + ":");
1100 WriteLine(1, "case " + std::string(TrackElemNames[TrackElemType::BeginStation]) + ":");
1101 WriteLine(1, "case " + std::string(TrackElemNames[TrackElemType::MiddleStation]) + ":");
1102 WriteLine(2, "return %s_track_station;", _rideName.c_str());
1103 continue;
1104 }
1105
1106 if (IsTrackTypeSupported(trackType))
1107 {
1108 WriteLine(1, "case " + std::string(TrackElemNames[trackType]) + ":");
1109 WriteLine(2, "return %s;", GetTrackFunctionName(trackType).c_str());
1110 }
1111 }
1112 WriteLine(1, "}");
1113 WriteLine(1, "return nullptr;");
1114 WriteLine(0, "}");
1115 }
1116
GetTrackFunctionName(int trackType)1117 std::string GetTrackFunctionName(int trackType)
1118 {
1119 std::string trackName = TrackCodeNames[trackType];
1120 return _rideName + "_track_" + trackName;
1121 }
1122
IsTrackTypeSupported(int trackType)1123 bool IsTrackTypeSupported(int trackType)
1124 {
1125 if (trackType == TrackElemType::BeginStation || trackType == TrackElemType::MiddleStation
1126 || trackType == TrackElemType::EndStation)
1127 {
1128 return false;
1129 }
1130 if (RideTypeTrackPaintFunctionsOld[_rideType][trackType] != 0)
1131 {
1132 return true;
1133 }
1134 return false;
1135 }
1136
WriteLine()1137 void WriteLine()
1138 {
1139 WriteLine(0, "");
1140 }
1141
WriteLine(int tabs,const char * format,...)1142 void WriteLine(int tabs, const char* format, ...)
1143 {
1144 va_list args;
1145 char buffer[512];
1146
1147 va_start(args, format);
1148 vsnprintf(buffer, sizeof(buffer), format, args);
1149 va_end(args);
1150
1151 WriteLine(tabs, std::string(buffer));
1152 }
1153
WriteLine(int tabs,std::string s)1154 void WriteLine(int tabs, std::string s)
1155 {
1156 for (int i = 0; i < tabs; i++)
1157 {
1158 fprintf(_file, "\t");
1159 }
1160 fprintf(_file, "%s\n", s.c_str());
1161 }
1162 };
1163
generatePaintCode(uint8_t rideType)1164 int generatePaintCode(uint8_t rideType)
1165 {
1166 if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
1167 {
1168 fprintf(stderr, "Flat rides not supported.\n");
1169 }
1170
1171 auto pcg = PaintCodeGenerator();
1172 return pcg.Generate(rideType);
1173 }
1174