diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index f0c3d7dd73..10a75cc3f8 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 791166FB1D7486EF005912EA /* NetworkServerAdvertiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 791166F91D7486EF005912EA /* NetworkServerAdvertiser.cpp */; }; 8594C0601D885CF600235E93 /* track_data_old.c in Sources */ = {isa = PBXBuildFile; fileRef = 8594C05F1D885CF600235E93 /* track_data_old.c */; }; 85AFA2111D7DB83E00221B42 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 85AFA20F1D7DB83E00221B42 /* main.cpp */; }; + 85B5C0B01D81D912001B99A8 /* intercept_2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 85B5C0AF1D81D912001B99A8 /* intercept_2.cpp */; }; C612A8991D64825300B634CA /* vehicle_data.c in Sources */ = {isa = PBXBuildFile; fileRef = C612A8971D64825300B634CA /* vehicle_data.c */; }; C61FB7201CF6180C004CE991 /* libssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38BA1CF3006400659A24 /* libssl.dylib */; }; C61FB7211CF618BA004CE991 /* libssl.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38BA1CF3006400659A24 /* libssl.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -495,6 +496,7 @@ 85AFA20F1D7DB83E00221B42 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 85AFA2101D7DB83E00221B42 /* intercept.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = intercept.h; sourceTree = ""; }; 85AFA2141D7DDFA100221B42 /* data.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = ""; }; + 85B5C0AF1D81D912001B99A8 /* intercept_2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = intercept_2.cpp; sourceTree = ""; }; C612A8971D64825300B634CA /* vehicle_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vehicle_data.c; sourceTree = ""; }; C612A8981D64825300B634CA /* vehicle_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vehicle_data.h; sourceTree = ""; }; C61FB7221CF86356004CE991 /* NetworkUser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkUser.cpp; sourceTree = ""; usesTabs = 0; }; @@ -1165,6 +1167,7 @@ C64FDAC41D6DA55E00F259B9 /* compat.c */, C64FDAC91D6DA92D00F259B9 /* data.c */, 85AFA2141D7DDFA100221B42 /* data.h */, + 85B5C0AF1D81D912001B99A8 /* intercept_2.cpp */, C64FDAC71D6DA72400F259B9 /* intercept.c */, 85AFA2101D7DB83E00221B42 /* intercept.h */, 85AFA20F1D7DB83E00221B42 /* main.cpp */, @@ -2417,6 +2420,7 @@ C64FDA8E1D6D9A2100F259B9 /* mini_golf.c in Sources */, C64FDA8F1D6D9A2100F259B9 /* mini_helicopters.c in Sources */, C64FDA901D6D9A2100F259B9 /* monorail_cycles.c in Sources */, + 85B5C0B01D81D912001B99A8 /* intercept_2.cpp in Sources */, C64FDA911D6D9A2100F259B9 /* observation_tower.c in Sources */, C64FDA921D6D9A2100F259B9 /* space_rings.c in Sources */, C64FDA931D6D9A2100F259B9 /* spiral_slide.c in Sources */, diff --git a/test/testpaint/intercept.c b/test/testpaint/intercept.c index 4cf6a158fa..863b0d4a12 100644 --- a/test/testpaint/intercept.c +++ b/test/testpaint/intercept.c @@ -21,8 +21,6 @@ #include "../../src/interface/viewport.h" #include "../../src/hook.h" -#define gRideEntries RCT2_ADDRESS(RCT2_ADDRESS_RIDE_ENTRIES, rct_ride_entry*) -#define gCurrentRotation RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8) static const uint32 PALETTE_98 = COLOUR_GREY << 19 | COLOUR_WHITE << 24 | 0xA0000000; static const uint32 PALETTE_9C = COLOUR_LIGHT_BLUE << 19 | COLOUR_ICY_BLUE << 24 | 0xA0000000; @@ -446,7 +444,7 @@ static void printFunctionCallArray(utf8string *out, function_call calls[], uint8 } } -static int getTrackSequenceCount(uint8 rideType, uint8 trackType) { +int getTrackSequenceCount(uint8 rideType, uint8 trackType) { int sequenceCount = 0; const rct_preview_track **trackBlocks; @@ -479,6 +477,9 @@ bool rideSupportsTrackType(int rideType, int trackType) { return supportsTrackType; } + +extern bool testSupportSegments(uint8 rideType, uint8 trackType); + static bool testTrackElement(uint8 rideType, uint8 trackType, utf8string *error) { if (rideType == RIDE_TYPE_CHAIRLIFT) { if (trackType == TRACK_ELEM_BEGIN_STATION || trackType == TRACK_ELEM_MIDDLE_STATION || trackType == TRACK_ELEM_END_STATION) { @@ -590,6 +591,11 @@ static bool testTrackElement(uint8 rideType, uint8 trackType, utf8string *error) } } + bool segmentSuccess = testSupportSegments(rideType, trackType); + if (!segmentSuccess) { + return false; + } + return true; } diff --git a/test/testpaint/intercept.h b/test/testpaint/intercept.h index 7adb7d3d42..7971e733c2 100644 --- a/test/testpaint/intercept.h +++ b/test/testpaint/intercept.h @@ -19,8 +19,12 @@ #include "../../src/common.h" +#define gRideEntries RCT2_ADDRESS(RCT2_ADDRESS_RIDE_ENTRIES, rct_ride_entry*) +#define gCurrentRotation RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8) + bool testRide(int rideType); void initHooks(); +int getTrackSequenceCount(uint8 rideType, uint8 trackType); bool rideIsImplemented(int rideType); bool rideSupportsTrackType(int rideType, int trackType); bool testTrackPainting(int rideType, int trackType); diff --git a/test/testpaint/intercept_2.cpp b/test/testpaint/intercept_2.cpp new file mode 100644 index 0000000000..959386069a --- /dev/null +++ b/test/testpaint/intercept_2.cpp @@ -0,0 +1,271 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include + +extern "C" { + #include "intercept.h" + #include "../../src/paint/paint.h" + #include "../../src/paint/supports.h" + #include "../../src/ride/track_data.h" + #include "../../src/interface/viewport.h" + #include "../../src/hook.h" +} + +namespace Intercept2 +{ + + static const uint32 PALETTE_98 = COLOUR_GREY << 19 | COLOUR_WHITE << 24 | 0xA0000000; + static const uint32 PALETTE_9C = COLOUR_LIGHT_BLUE << 19 | COLOUR_ICY_BLUE << 24 | 0xA0000000; + static const uint32 PALETTE_A0 = COLOUR_DARK_PURPLE << 19 | COLOUR_LIGHT_PURPLE << 24 | 0xA0000000; + static const uint32 PALETTE_A4 = COLOUR_BRIGHT_PURPLE << 19 | COLOUR_DARK_BLUE << 24 | 0xA0000000; + + struct SegmentSupportCall + { + uint16 segments; + sint32 height; + sint16 slope; + }; + + bool SortSegmentSupportCalls(SegmentSupportCall lhs, SegmentSupportCall rhs) + { + if (lhs.height != rhs.height) { + return lhs.height < rhs.height; + } + + if (lhs.slope != rhs.slope) { + return lhs.slope < rhs.slope; + } + + return lhs.segments < rhs.segments; + } + + static std::vector getSegmentCalls(support_height supports[9], uint8 rotation) + { + uint16 positionsRemaining = SEGMENTS_ALL; + + for (int i = 0; i < 9; i++) { + if (supports[i].height == 0 && supports[i].slope == 0xFF) { + positionsRemaining &= ~segment_offsets[i]; + } + } + + std::vector calls; + + while (positionsRemaining != 0) { + SegmentSupportCall call = {0}; + call.height = -1; + call.slope = -1; + + support_height referenceSupport; + + for (int i = 0; i < 9; i++) { + if (positionsRemaining & segment_offsets[i]) { + referenceSupport = supports[i]; + if (supports[i].height != 0) { + call.height = supports[i].height; + } + if (supports[i].slope != 0xFF) { + call.slope = supports[i].slope; + } + break; + } + } + + uint16 positionsMatched = 0; + for (int i = 0; i < 9; i++) { + if (supports[i].height == referenceSupport.height && supports[i].slope == referenceSupport.slope) { + positionsMatched |= segment_offsets[i]; + } + } + positionsRemaining &= ~positionsMatched; + + call.segments = paint_util_rotate_segments(positionsMatched, (4 - rotation) % 4); + + calls.push_back(call); + } + + if (calls.size() > 1) { + std::sort(calls.begin(), calls.end(), SortSegmentSupportCalls); + } + + return calls; + } + + static bool SegmentCallEquals(std::vector lhs, std::vector rhs) + { + if (lhs.size() != rhs.size()) return false; + for (int i = 0; i < lhs.size(); ++i) { + if (lhs[i].segments != rhs[i].segments) + return false; + if (lhs[i].height != rhs[i].height) + return false; + if (lhs[i].slope != rhs[i].slope) + return false; + } + + return true; + } + + static bool segmentCallsMatch(std::vector tileSegmentSupportCalls[4]) + { + std::vector baseCallList = tileSegmentSupportCalls[0]; + for (int i = 1; i < 4; i++) { + if (!SegmentCallEquals(baseCallList, tileSegmentSupportCalls[i])) { + return false; + } + } + + return true; + } + + static void printSegmentSupports(utf8string * out, std::vector segmentCalls) + { + for (auto &&call : segmentCalls) { + int segmentsPrinted = 0; + for (int i = 0; i < 9; i++) { + if (call.segments & segment_offsets[i]) { + if (segmentsPrinted > 0) { + sprintf(*out + strlen(*out), " | "); + } + sprintf(*out + strlen(*out), "SEGMENT_%02X", 0xB4 + 4 * i); + segmentsPrinted++; + } + } + + if (call.height == 0xFFFF) { + sprintf(*out + strlen(*out), ", 0xFFFF"); + } else { + sprintf(*out + strlen(*out), ", %d", call.height); + } + + sprintf(*out + strlen(*out), ", 0x%02X\n", call.slope); + } + } + + static bool testSupportSegments(uint8 rideType, uint8 trackType) + { + uint8 rideIndex = 0; + rct_map_element mapElement = {0}; + mapElement.properties.track.type = trackType; + mapElement.base_height = 3; + + g_currently_drawn_item = &mapElement; + + rct_map_element surfaceElement = {0}; + surfaceElement.type = MAP_ELEMENT_TYPE_SURFACE; + surfaceElement.base_height = 2; + + gPaintInteractionType = VIEWPORT_INTERACTION_ITEM_RIDE; + RCT2_GLOBAL(0x00F44198, uint32) = PALETTE_98; + RCT2_GLOBAL(0x00F441A0, uint32) = PALETTE_A0; + RCT2_GLOBAL(0x00F441A4, uint32) = PALETTE_A4; + RCT2_GLOBAL(0x00F4419C, uint32) = PALETTE_9C; + + rct_drawpixelinfo dpi = {.zoom_level = 1}; + unk_140E9A8 = &dpi; + + rct_vehicle vehicle = {0}; + rct_ride ride = {0}; + + rct_ride_entry rideEntry = {0}; + rct_ride_entry_vehicle vehicleEntry = {.base_image_id = 0x70000}; + rideEntry.vehicles[0] = vehicleEntry; + + + gRideList[0] = ride; + gRideEntries[0] = &rideEntry; + + int height = 48; + + TRACK_PAINT_FUNCTION_GETTER newPaintGetter = RideTypeTrackPaintFunctions[rideType]; + int sequenceCount = getTrackSequenceCount(rideType, trackType); + + + for (int trackSequence = 0; trackSequence < sequenceCount; trackSequence++) { + std::vector tileSegmentSupportCalls[4]; + + for (int direction = 0; direction < 4; direction++) { + for (int s = 0; s < 9; ++s) { + gSupportSegments[s].height = 0; + gSupportSegments[s].slope = 0xFF; + } + + TRACK_PAINT_FUNCTION ** trackTypeList = (TRACK_PAINT_FUNCTION **) RideTypeTrackPaintFunctionsOld[rideType]; + uint32 * trackDirectionList = (uint32 *) trackTypeList[trackType]; + + // Have to call from this point as it pushes esi and expects callee to pop it + RCT2_CALLPROC_X( + 0x006C4934, + rideType, + (int) trackDirectionList, + direction, + height, + (int) &mapElement, + rideIndex * sizeof(rct_ride), + trackSequence + ); + + tileSegmentSupportCalls[direction] = getSegmentCalls(gSupportSegments, direction); + } + + if (!segmentCallsMatch(tileSegmentSupportCalls)) { + // TODO: if 3 directions do share the same mask, use that call list as a reference. + printf("Original segment calls didn't match. [trackSequence:%d]\n", trackSequence); + continue; + } + + for (int direction = 0; direction < 4; direction++) { + for (int s = 0; s < 9; ++s) { + gSupportSegments[s].height = 0; + gSupportSegments[s].slope = 0xFF; + } + + TRACK_PAINT_FUNCTION newPaintFunction = newPaintGetter(trackType, direction); + newPaintFunction(rideIndex, trackSequence, direction, height, &mapElement); + + std::vector newCalls = getSegmentCalls(gSupportSegments, direction); + + if (!SegmentCallEquals(tileSegmentSupportCalls[0], newCalls)) { + // TODO put this into *error + utf8string diff = new utf8[2048]; + sprintf(diff, "<<< EXPECTED\n"); + printSegmentSupports(&diff, tileSegmentSupportCalls[0]); + sprintf(diff + strlen(diff), "====\n"); + printSegmentSupports(&diff, newCalls); + sprintf(diff + strlen(diff), ">>> ACTUAL\n"); + + printf("Segment support heights didn't match. [direction:%d trackSequence:%d]\n", direction, + trackSequence); + printf("%s", diff); + delete[] diff; + return false; + } + } + } + + return true; + } +} + + +extern "C" +{ + bool testSupportSegments(uint8 rideType, uint8 trackType) + { + return Intercept2::testSupportSegments(rideType, trackType); + } +} diff --git a/test/testpaint/testpaint.vcxproj b/test/testpaint/testpaint.vcxproj index 3061cb2e8c..5610db872c 100644 --- a/test/testpaint/testpaint.vcxproj +++ b/test/testpaint/testpaint.vcxproj @@ -84,6 +84,7 @@ +