diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 8d99a054a8..5f221925e4 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -19,10 +19,23 @@ 791166FB1D7486EF005912EA /* NetworkServerAdvertiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 791166F91D7486EF005912EA /* NetworkServerAdvertiser.cpp */; }; 85060FD31D8C17CC00DFA2B3 /* track_data_old.c in Sources */ = {isa = PBXBuildFile; fileRef = 8594C05F1D885CF600235E93 /* track_data_old.c */; }; 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 */; }; 85B468FC1D96822F000F1DB5 /* paint_helpers.c in Sources */ = {isa = PBXBuildFile; fileRef = 85B468FB1D96822F000F1DB5 /* paint_helpers.c */; }; 85B468FD1D96822F000F1DB5 /* paint_helpers.c in Sources */ = {isa = PBXBuildFile; fileRef = 85B468FB1D96822F000F1DB5 /* paint_helpers.c */; }; - 85B5C0B01D81D912001B99A8 /* intercept_2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 85B5C0AF1D81D912001B99A8 /* intercept_2.cpp */; }; + C606CCBE1DB4054000FE4015 /* compat.c in Sources */ = {isa = PBXBuildFile; fileRef = C606CCAB1DB4054000FE4015 /* compat.c */; }; + C606CCBF1DB4054000FE4015 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = C606CCAC1DB4054000FE4015 /* data.c */; }; + C606CCC01DB4054000FE4015 /* FunctionCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCAE1DB4054000FE4015 /* FunctionCall.cpp */; }; + C606CCC11DB4054000FE4015 /* generate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCB01DB4054000FE4015 /* generate.cpp */; }; + C606CCC41DB4054000FE4015 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCB41DB4054000FE4015 /* main.cpp */; }; + C606CCC51DB4054000FE4015 /* PaintIntercept.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCB51DB4054000FE4015 /* PaintIntercept.cpp */; }; + C606CCC61DB4054000FE4015 /* Printer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCB61DB4054000FE4015 /* Printer.cpp */; }; + C606CCC71DB4054000FE4015 /* String.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCB81DB4054000FE4015 /* String.cpp */; }; + C606CCC81DB4054000FE4015 /* TestTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCBA1DB4054000FE4015 /* TestTrack.cpp */; }; + C606CCC91DB4054000FE4015 /* Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCBC1DB4054000FE4015 /* Utils.cpp */; }; + C606CCCE1DB427A000FE4015 /* GeneralSupportHeightCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCCA1DB427A000FE4015 /* GeneralSupportHeightCall.cpp */; }; + C606CCCF1DB427A000FE4015 /* SegmentSupportHeightCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCCC1DB427A000FE4015 /* SegmentSupportHeightCall.cpp */; }; + C606CCD21DB4D7C800FE4015 /* SideTunnelCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCD01DB4D7C800FE4015 /* SideTunnelCall.cpp */; }; + C606CCD51DB4DD6C00FE4015 /* VerticalTunnelCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CCD31DB4DD6C00FE4015 /* VerticalTunnelCall.cpp */; }; + C606CD261DB5120C00FE4015 /* TestPaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C606CD231DB5120900FE4015 /* TestPaint.cpp */; }; C612A8991D64825300B634CA /* vehicle_data.c in Sources */ = {isa = PBXBuildFile; fileRef = C612A8971D64825300B634CA /* vehicle_data.c */; }; C61FB7241CF86356004CE991 /* NetworkUser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C61FB7221CF86356004CE991 /* NetworkUser.cpp */; }; C64FDA641D6D9A2100F259B9 /* air_powered_vertical_coaster.c in Sources */ = {isa = PBXBuildFile; fileRef = C686F8BB1CDBC3B7009F9BFC /* air_powered_vertical_coaster.c */; }; @@ -106,9 +119,6 @@ C64FDAC01D6D9E3B00F259B9 /* track_data.c in Sources */ = {isa = PBXBuildFile; fileRef = D442717B1CC81B3200D84D28 /* track_data.c */; }; C64FDAC21D6DA0B800F259B9 /* diagnostic.c in Sources */ = {isa = PBXBuildFile; fileRef = D44270FE1CC81B3200D84D28 /* diagnostic.c */; }; C64FDAC31D6DA41000F259B9 /* track_paint.c in Sources */ = {isa = PBXBuildFile; fileRef = D442717D1CC81B3200D84D28 /* track_paint.c */; }; - C64FDAC51D6DA55E00F259B9 /* compat.c in Sources */ = {isa = PBXBuildFile; fileRef = C64FDAC41D6DA55E00F259B9 /* compat.c */; }; - C64FDAC81D6DA72400F259B9 /* intercept.c in Sources */ = {isa = PBXBuildFile; fileRef = C64FDAC71D6DA72400F259B9 /* intercept.c */; }; - C64FDACA1D6DA92D00F259B9 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = C64FDAC91D6DA92D00F259B9 /* data.c */; }; C64FDACB1D6DBCC700F259B9 /* hook.c in Sources */ = {isa = PBXBuildFile; fileRef = D44271121CC81B3200D84D28 /* hook.c */; }; C650B2191CCABBDD00B4D91C /* S4Importer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C650B2151CCABBDD00B4D91C /* S4Importer.cpp */; }; C650B21A1CCABBDD00B4D91C /* tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C650B2171CCABBDD00B4D91C /* tables.cpp */; }; @@ -392,7 +402,6 @@ D44272A51CC81B3200D84D28 /* particle.c in Sources */ = {isa = PBXBuildFile; fileRef = D44271EF1CC81B3200D84D28 /* particle.c */; }; D44272A61CC81B3200D84D28 /* scenery.c in Sources */ = {isa = PBXBuildFile; fileRef = D44271F01CC81B3200D84D28 /* scenery.c */; }; D44272A71CC81B3200D84D28 /* sprite.c in Sources */ = {isa = PBXBuildFile; fileRef = D44271F21CC81B3200D84D28 /* sprite.c */; }; - D452919B1DAA204200C11788 /* generate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D45291991DAA202C00C11788 /* generate.cpp */; }; D45A38BC1CF3006400659A24 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B31CF3006400659A24 /* libcrypto.dylib */; }; D45A38BE1CF3006400659A24 /* libjansson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B51CF3006400659A24 /* libjansson.dylib */; }; D45A38C01CF3006400659A24 /* libSDL2_ttf.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D45A38B71CF3006400659A24 /* libSDL2_ttf.dylib */; }; @@ -494,19 +503,39 @@ 791166F91D7486EF005912EA /* NetworkServerAdvertiser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkServerAdvertiser.cpp; sourceTree = ""; }; 791166FA1D7486EF005912EA /* NetworkServerAdvertiser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkServerAdvertiser.h; sourceTree = ""; }; 8594C05F1D885CF600235E93 /* track_data_old.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = track_data_old.c; sourceTree = ""; }; - 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 = ""; }; 85B468FB1D96822F000F1DB5 /* paint_helpers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = paint_helpers.c; sourceTree = ""; }; - 85B5C0AF1D81D912001B99A8 /* intercept_2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = intercept_2.cpp; sourceTree = ""; }; + C606CCAB1DB4054000FE4015 /* compat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compat.c; sourceTree = ""; }; + C606CCAC1DB4054000FE4015 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = ""; }; + C606CCAD1DB4054000FE4015 /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = ""; }; + C606CCAE1DB4054000FE4015 /* FunctionCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FunctionCall.cpp; sourceTree = ""; }; + C606CCAF1DB4054000FE4015 /* FunctionCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FunctionCall.hpp; sourceTree = ""; }; + C606CCB01DB4054000FE4015 /* generate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = generate.cpp; sourceTree = ""; }; + C606CCB41DB4054000FE4015 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; + C606CCB51DB4054000FE4015 /* PaintIntercept.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PaintIntercept.cpp; sourceTree = ""; }; + C606CCB61DB4054000FE4015 /* Printer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Printer.cpp; sourceTree = ""; }; + C606CCB71DB4054000FE4015 /* Printer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Printer.hpp; sourceTree = ""; }; + C606CCB81DB4054000FE4015 /* String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = String.cpp; sourceTree = ""; }; + C606CCB91DB4054000FE4015 /* String.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = String.hpp; sourceTree = ""; }; + C606CCBA1DB4054000FE4015 /* TestTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestTrack.cpp; sourceTree = ""; }; + C606CCBB1DB4054000FE4015 /* TestTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TestTrack.hpp; sourceTree = ""; }; + C606CCBC1DB4054000FE4015 /* Utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Utils.cpp; sourceTree = ""; }; + C606CCBD1DB4054000FE4015 /* Utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Utils.hpp; sourceTree = ""; }; + C606CCCA1DB427A000FE4015 /* GeneralSupportHeightCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeneralSupportHeightCall.cpp; sourceTree = ""; }; + C606CCCB1DB427A000FE4015 /* GeneralSupportHeightCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GeneralSupportHeightCall.hpp; sourceTree = ""; }; + C606CCCC1DB427A000FE4015 /* SegmentSupportHeightCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentSupportHeightCall.cpp; sourceTree = ""; }; + C606CCCD1DB427A000FE4015 /* SegmentSupportHeightCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SegmentSupportHeightCall.hpp; sourceTree = ""; }; + C606CCD01DB4D7C800FE4015 /* SideTunnelCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SideTunnelCall.cpp; sourceTree = ""; }; + C606CCD11DB4D7C800FE4015 /* SideTunnelCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SideTunnelCall.hpp; sourceTree = ""; }; + C606CCD31DB4DD6C00FE4015 /* VerticalTunnelCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VerticalTunnelCall.cpp; sourceTree = ""; }; + C606CCD41DB4DD6C00FE4015 /* VerticalTunnelCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VerticalTunnelCall.hpp; sourceTree = ""; }; + C606CCD61DB4E1CD00FE4015 /* PaintIntercept.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PaintIntercept.hpp; sourceTree = ""; }; + C606CD231DB5120900FE4015 /* TestPaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestPaint.cpp; sourceTree = ""; }; + C606CD241DB5120900FE4015 /* TestPaint.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TestPaint.hpp; 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; }; C61FB7231CF86356004CE991 /* NetworkUser.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = NetworkUser.h; sourceTree = ""; usesTabs = 0; }; C64FDA5D1D6D99F400F259B9 /* PaintTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PaintTest; sourceTree = BUILT_PRODUCTS_DIR; }; - C64FDAC41D6DA55E00F259B9 /* compat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compat.c; sourceTree = ""; }; - C64FDAC71D6DA72400F259B9 /* intercept.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intercept.c; sourceTree = ""; }; - C64FDAC91D6DA92D00F259B9 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = ""; }; C650B2151CCABBDD00B4D91C /* S4Importer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = S4Importer.cpp; sourceTree = ""; usesTabs = 0; }; C650B2161CCABBDD00B4D91C /* S4Importer.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = S4Importer.h; sourceTree = ""; usesTabs = 0; }; C650B2171CCABBDD00B4D91C /* tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tables.cpp; sourceTree = ""; usesTabs = 0; }; @@ -908,7 +937,6 @@ D44271F21CC81B3200D84D28 /* sprite.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sprite.c; sourceTree = ""; }; D44271F31CC81B3200D84D28 /* sprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sprite.h; sourceTree = ""; }; D44271F41CC81B3200D84D28 /* water.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = water.h; sourceTree = ""; }; - D45291991DAA202C00C11788 /* generate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = generate.cpp; sourceTree = ""; }; D45A38B31CF3006400659A24 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libcrypto.dylib; sourceTree = ""; }; D45A38B41CF3006400659A24 /* libfreetype.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libfreetype.dylib; sourceTree = ""; }; D45A38B51CF3006400659A24 /* libjansson.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libjansson.dylib; sourceTree = ""; }; @@ -1167,14 +1195,33 @@ C64FDA5E1D6D99F400F259B9 /* Paint */ = { isa = PBXGroup; children = ( - C64FDAC41D6DA55E00F259B9 /* compat.c */, - C64FDAC91D6DA92D00F259B9 /* data.c */, - 85AFA2141D7DDFA100221B42 /* data.h */, - D45291991DAA202C00C11788 /* generate.cpp */, - 85B5C0AF1D81D912001B99A8 /* intercept_2.cpp */, - C64FDAC71D6DA72400F259B9 /* intercept.c */, - 85AFA2101D7DB83E00221B42 /* intercept.h */, - 85AFA20F1D7DB83E00221B42 /* main.cpp */, + C606CCAB1DB4054000FE4015 /* compat.c */, + C606CCAC1DB4054000FE4015 /* data.c */, + C606CCAD1DB4054000FE4015 /* data.h */, + C606CCAE1DB4054000FE4015 /* FunctionCall.cpp */, + C606CCAF1DB4054000FE4015 /* FunctionCall.hpp */, + C606CCCA1DB427A000FE4015 /* GeneralSupportHeightCall.cpp */, + C606CCCB1DB427A000FE4015 /* GeneralSupportHeightCall.hpp */, + C606CCB01DB4054000FE4015 /* generate.cpp */, + C606CCB41DB4054000FE4015 /* main.cpp */, + C606CCB51DB4054000FE4015 /* PaintIntercept.cpp */, + C606CCD61DB4E1CD00FE4015 /* PaintIntercept.hpp */, + C606CCB61DB4054000FE4015 /* Printer.cpp */, + C606CCB71DB4054000FE4015 /* Printer.hpp */, + C606CCCC1DB427A000FE4015 /* SegmentSupportHeightCall.cpp */, + C606CCCD1DB427A000FE4015 /* SegmentSupportHeightCall.hpp */, + C606CCD01DB4D7C800FE4015 /* SideTunnelCall.cpp */, + C606CCD11DB4D7C800FE4015 /* SideTunnelCall.hpp */, + C606CCB81DB4054000FE4015 /* String.cpp */, + C606CCB91DB4054000FE4015 /* String.hpp */, + C606CD231DB5120900FE4015 /* TestPaint.cpp */, + C606CD241DB5120900FE4015 /* TestPaint.hpp */, + C606CCBA1DB4054000FE4015 /* TestTrack.cpp */, + C606CCBB1DB4054000FE4015 /* TestTrack.hpp */, + C606CCBC1DB4054000FE4015 /* Utils.cpp */, + C606CCBD1DB4054000FE4015 /* Utils.hpp */, + C606CCD31DB4DD6C00FE4015 /* VerticalTunnelCall.cpp */, + C606CCD41DB4DD6C00FE4015 /* VerticalTunnelCall.hpp */, ); name = Paint; path = test/testpaint; @@ -2360,12 +2407,13 @@ C64FDAC01D6D9E3B00F259B9 /* track_data.c in Sources */, C64FDABF1D6D9CEA00F259B9 /* map_element.c in Sources */, C64FDABE1D6D9CD900F259B9 /* ride_data.c in Sources */, + C606CCD21DB4D7C800FE4015 /* SideTunnelCall.cpp in Sources */, C64FDABC1D6D9C8800F259B9 /* addresses.c in Sources */, C64FDA641D6D9A2100F259B9 /* air_powered_vertical_coaster.c in Sources */, + C606CCD51DB4DD6C00FE4015 /* VerticalTunnelCall.cpp in Sources */, C64FDA651D6D9A2100F259B9 /* bobsleigh_coaster.c in Sources */, C64FDA661D6D9A2100F259B9 /* compact_inverted_coaster.c in Sources */, C64FDA671D6D9A2100F259B9 /* corkscrew_roller_coaster.c in Sources */, - 85AFA2111D7DB83E00221B42 /* main.cpp in Sources */, C64FDA681D6D9A2100F259B9 /* flying_roller_coaster.c in Sources */, C64FDA691D6D9A2100F259B9 /* giga_coaster.c in Sources */, C64FDA6A1D6D9A2100F259B9 /* heartline_twister_coaster.c in Sources */, @@ -2373,27 +2421,29 @@ C64FDA6C1D6D9A2100F259B9 /* inverted_impulse_coaster.c in Sources */, C64FDA6D1D6D9A2100F259B9 /* inverted_roller_coaster.c in Sources */, C64FDA6E1D6D9A2100F259B9 /* junior_roller_coaster.c in Sources */, + C606CCCF1DB427A000FE4015 /* SegmentSupportHeightCall.cpp in Sources */, C64FDA6F1D6D9A2100F259B9 /* lay_down_roller_coaster.c in Sources */, C64FDA701D6D9A2100F259B9 /* lim_launched_roller_coaster.c in Sources */, C64FDA711D6D9A2100F259B9 /* looping_roller_coaster.c in Sources */, - C64FDAC81D6DA72400F259B9 /* intercept.c in Sources */, C64FDA721D6D9A2100F259B9 /* mine_ride.c in Sources */, C64FDA731D6D9A2100F259B9 /* mine_train_coaster.c in Sources */, C64FDA741D6D9A2100F259B9 /* mini_roller_coaster.c in Sources */, C64FDA751D6D9A2100F259B9 /* mini_suspended_coaster.c in Sources */, C64FDA761D6D9A2100F259B9 /* multi_dimension_roller_coaster.c in Sources */, C64FDA771D6D9A2100F259B9 /* reverse_freefall_coaster.c in Sources */, + C606CCBE1DB4054000FE4015 /* compat.c in Sources */, C64FDA781D6D9A2100F259B9 /* reverser_roller_coaster.c in Sources */, C64FDA791D6D9A2100F259B9 /* side_friction_roller_coaster.c in Sources */, - C64FDACA1D6DA92D00F259B9 /* data.c in Sources */, C64FDA7A1D6D9A2100F259B9 /* spiral_roller_coaster.c in Sources */, C64FDA7B1D6D9A2100F259B9 /* stand_up_roller_coaster.c in Sources */, + C606CCC61DB4054000FE4015 /* Printer.cpp in Sources */, C64FDA7C1D6D9A2100F259B9 /* steeplechase.c in Sources */, C64FDA7D1D6D9A2100F259B9 /* suspended_swinging_coaster.c in Sources */, C64FDA7E1D6D9A2100F259B9 /* twister_roller_coaster.c in Sources */, C64FDA7F1D6D9A2100F259B9 /* vertical_drop_roller_coaster.c in Sources */, C64FDA801D6D9A2100F259B9 /* virginia_reel.c in Sources */, C64FDA811D6D9A2100F259B9 /* wild_mouse.c in Sources */, + C606CCC41DB4054000FE4015 /* main.cpp in Sources */, C64FDA821D6D9A2100F259B9 /* wooden_roller_coaster.c in Sources */, C64FDA831D6D9A2100F259B9 /* wooden_wild_mouse.c in Sources */, C64FDA841D6D9A2100F259B9 /* car_ride.c in Sources */, @@ -2402,26 +2452,29 @@ 85060FD31D8C17CC00DFA2B3 /* track_data_old.c in Sources */, C64FDA871D6D9A2100F259B9 /* dodgems.c in Sources */, C64FDA881D6D9A2100F259B9 /* ferris_wheel.c in Sources */, - D452919B1DAA204200C11788 /* generate.cpp in Sources */, C64FDA891D6D9A2100F259B9 /* flying_saucers.c in Sources */, + C606CCC11DB4054000FE4015 /* generate.cpp in Sources */, C64FDA8A1D6D9A2100F259B9 /* ghost_train.c in Sources */, + C606CCBF1DB4054000FE4015 /* data.c in Sources */, C64FDA8B1D6D9A2100F259B9 /* haunted_house.c in Sources */, C64FDA8C1D6D9A2100F259B9 /* maze.c in Sources */, C64FDA8D1D6D9A2100F259B9 /* merry_go_round.c in Sources */, C64FDA8E1D6D9A2100F259B9 /* mini_golf.c in Sources */, + C606CCC81DB4054000FE4015 /* TestTrack.cpp in Sources */, C64FDA8F1D6D9A2100F259B9 /* mini_helicopters.c in Sources */, + C606CCC51DB4054000FE4015 /* PaintIntercept.cpp in Sources */, C64FDA901D6D9A2100F259B9 /* monorail_cycles.c in Sources */, - 85B5C0B01D81D912001B99A8 /* intercept_2.cpp in Sources */, + C606CCCE1DB427A000FE4015 /* GeneralSupportHeightCall.cpp in Sources */, C64FDA911D6D9A2100F259B9 /* observation_tower.c in Sources */, C64FDA921D6D9A2100F259B9 /* space_rings.c in Sources */, C64FDA931D6D9A2100F259B9 /* spiral_slide.c in Sources */, C64FDA941D6D9A2100F259B9 /* facility.c in Sources */, - C64FDAC51D6DA55E00F259B9 /* compat.c in Sources */, C64FDA951D6D9A2100F259B9 /* misc.c in Sources */, C64FDA961D6D9A2100F259B9 /* shop.c in Sources */, C64FDA971D6D9A2100F259B9 /* 3d_cinema.c in Sources */, C64FDA981D6D9A2100F259B9 /* enterprise.c in Sources */, C64FDA991D6D9A2100F259B9 /* go_karts.c in Sources */, + C606CCC01DB4054000FE4015 /* FunctionCall.cpp in Sources */, C64FDA9A1D6D9A2100F259B9 /* launched_freefall.c in Sources */, C64FDA9B1D6D9A2100F259B9 /* magic_carpet.c in Sources */, C64FDA9C1D6D9A2100F259B9 /* motion_simulator.c in Sources */, @@ -2430,7 +2483,9 @@ C64FDA9F1D6D9A2100F259B9 /* swinging_inverter_ship.c in Sources */, C64FDAA01D6D9A2100F259B9 /* top_spin.c in Sources */, C64FDAA11D6D9A2100F259B9 /* twist.c in Sources */, + C606CD261DB5120C00FE4015 /* TestPaint.cpp in Sources */, C64FDAA21D6D9A2100F259B9 /* chairlift.c in Sources */, + C606CCC71DB4054000FE4015 /* String.cpp in Sources */, C64FDAA31D6D9A2100F259B9 /* lift.c in Sources */, C64FDAA41D6D9A2100F259B9 /* miniature_railway.c in Sources */, C64FDAA51D6D9A2100F259B9 /* monorail.c in Sources */, @@ -2440,6 +2495,7 @@ C64FDAA81D6D9A2100F259B9 /* dingy_slide.c in Sources */, C64FDAA91D6D9A2100F259B9 /* log_flume.c in Sources */, C64FDAAA1D6D9A2100F259B9 /* river_rafts.c in Sources */, + C606CCC91DB4054000FE4015 /* Utils.cpp in Sources */, C64FDAAB1D6D9A2100F259B9 /* river_rapids.c in Sources */, C64FDAAC1D6D9A2100F259B9 /* splash_boats.c in Sources */, C64FDAAD1D6D9A2100F259B9 /* submarine_ride.c in Sources */, diff --git a/scripts/linux/build.sh b/scripts/linux/build.sh index 8b9a629648..c07b28687e 100755 --- a/scripts/linux/build.sh +++ b/scripts/linux/build.sh @@ -69,6 +69,13 @@ pushd build chmod g+s $(pwd) # CMAKE and MAKE opts from environment docker run -v $PARENT:/work/openrct2 -w /work/openrct2/build -i -t openrct2/openrct2:64bit-only bash -c "cmake ../ $OPENRCT2_CMAKE_OPTS && make $OPENRCT_MAKE_OPTS" + elif [[ $TARGET == "linux" ]] + then + cmake $OPENRCT2_CMAKE_OPTS .. -DCMAKE_BUILD_TYPE=debug + # NOT the same variable as above + # this target also includes building & running of testpaint + make $OPENRCT2_MAKE_OPTS testpaint + ./testpaint --quiet || if [[ $? -eq 1 ]] ; then echo Allowing failed tests to pass ; else false; fi else cmake $OPENRCT2_CMAKE_OPTS .. # NOT the same variable as above diff --git a/src/paint/map_element/map_element.c b/src/paint/map_element/map_element.c index ec1cfd940b..d6dccbfec6 100644 --- a/src/paint/map_element/map_element.c +++ b/src/paint/map_element/map_element.c @@ -38,9 +38,9 @@ uint16 gUnk141E9DC; rct_xy16 gPaintMapPosition; bool gDidPassSurface; rct_map_element * gSurfaceElement; -tunnel_entry gLeftTunnels[65]; +tunnel_entry gLeftTunnels[TUNNEL_MAX_COUNT]; uint8 gLeftTunnelCount; -tunnel_entry gRightTunnels[65]; +tunnel_entry gRightTunnels[TUNNEL_MAX_COUNT]; uint8 gRightTunnelCount; uint8 gVerticalTunnelHeight; #endif diff --git a/src/paint/map_element/map_element.h b/src/paint/map_element/map_element.h index d1065c32ad..e2e7580ec8 100644 --- a/src/paint/map_element/map_element.h +++ b/src/paint/map_element/map_element.h @@ -80,15 +80,17 @@ enum G141E9DB_FLAG_2 = 2, }; +#define TUNNEL_MAX_COUNT 65 + #ifdef NO_RCT2 extern uint8 g141E9DB; extern uint16 gUnk141E9DC; extern rct_xy16 gPaintMapPosition; extern bool gDidPassSurface; extern rct_map_element * gSurfaceElement; -extern tunnel_entry gLeftTunnels[65]; +extern tunnel_entry gLeftTunnels[TUNNEL_MAX_COUNT]; extern uint8 gLeftTunnelCount; -extern tunnel_entry gRightTunnels[65]; +extern tunnel_entry gRightTunnels[TUNNEL_MAX_COUNT]; extern uint8 gRightTunnelCount; extern uint8 gVerticalTunnelHeight; #else diff --git a/src/paint/map_element/surface.c b/src/paint/map_element/surface.c index 4db53e69b9..0d5ab35926 100644 --- a/src/paint/map_element/surface.c +++ b/src/paint/map_element/surface.c @@ -634,7 +634,7 @@ static void viewport_surface_draw_land_side_bottom(enum edge edge, uint8 height, // Normal walls while (curHeight > tunnelArray[0].height) { // TODO: Should probably be done by just keeping track of the current index - memmove(&tunnelArray[0], &tunnelArray[1], sizeof(tunnel_entry) * 64); + memmove(&tunnelArray[0], &tunnelArray[1], sizeof(tunnel_entry) * (TUNNEL_MAX_COUNT - 1)); } if (curHeight != tunnelArray[0].height) { @@ -682,7 +682,7 @@ static void viewport_surface_draw_land_side_bottom(enum edge edge, uint8 height, curHeight += stru_97B570[tunnelType][0]; // TODO: Should probably be done by just keeping track of the current index - memmove(&tunnelArray[0], &tunnelArray[1], sizeof(tunnel_entry) * 64); + memmove(&tunnelArray[0], &tunnelArray[1], sizeof(tunnel_entry) * (TUNNEL_MAX_COUNT - 1)); } } @@ -906,7 +906,7 @@ static void viewport_surface_draw_water_side_bottom(enum edge edge, uint8 height // Normal walls while (curHeight > tunnelArray[0].height) { // TODO: Should probably be done by just keeping track of the current index - memmove(&tunnelArray[0], &tunnelArray[1], sizeof(tunnel_entry) * 64); + memmove(&tunnelArray[0], &tunnelArray[1], sizeof(tunnel_entry) * (TUNNEL_MAX_COUNT - 1)); } sub_98196C(base_image_id, offset.x, offset.y, bounds.x, bounds.y, 15, curHeight * 16, rotation); @@ -952,7 +952,7 @@ static void viewport_surface_draw_water_side_bottom(enum edge edge, uint8 height curHeight += stru_97B570[tunnelType][0]; // TODO: Should probably be done by just keeping track of the current index - memmove(&tunnelArray[0], &tunnelArray[1], sizeof(tunnel_entry) * 64); + memmove(&tunnelArray[0], &tunnelArray[1], sizeof(tunnel_entry) * (TUNNEL_MAX_COUNT - 1)); } } @@ -1316,18 +1316,18 @@ void surface_paint(uint8 direction, uint16 height, rct_map_element * mapElement) log_verbose("eax: %d", eax); } - tunnel_entry backupLeftTunnels[65]; - tunnel_entry backupRightTunnels[65]; + tunnel_entry backupLeftTunnels[TUNNEL_MAX_COUNT]; + tunnel_entry backupRightTunnels[TUNNEL_MAX_COUNT]; #ifdef __MINGW32__ // The other code crashes mingw 4.8.2, as available on Travis - for (int i = 0; i < 65; i++) { + for (int i = 0; i < TUNNEL_MAX_COUNT; i++) { backupLeftTunnels[i] = gLeftTunnels[i]; backupRightTunnels[i] = gRightTunnels[i]; } #else - memcpy(backupLeftTunnels, gLeftTunnels, sizeof(tunnel_entry) * 65); - memcpy(backupRightTunnels, gRightTunnels, sizeof(tunnel_entry) * 65); + memcpy(backupLeftTunnels, gLeftTunnels, sizeof(tunnel_entry) * TUNNEL_MAX_COUNT); + memcpy(backupRightTunnels, gRightTunnels, sizeof(tunnel_entry) * TUNNEL_MAX_COUNT); #endif viewport_surface_draw_land_side_top(EDGE_TOPLEFT, height / 16, eax / 32, tileDescriptors[0], tileDescriptors[3]); @@ -1338,13 +1338,13 @@ void surface_paint(uint8 direction, uint16 height, rct_map_element * mapElement) #ifdef __MINGW32__ // The other code crashes mingw 4.8.2, as available on Travis - for (int i = 0; i < 65; i++) { + for (int i = 0; i < TUNNEL_MAX_COUNT; i++) { gLeftTunnels[i] = backupLeftTunnels[i]; gRightTunnels[i] = backupRightTunnels[i]; } #else - memcpy(gLeftTunnels, backupLeftTunnels, sizeof(tunnel_entry) * 65); - memcpy(gRightTunnels, backupRightTunnels, sizeof(tunnel_entry) * 65); + memcpy(gLeftTunnels, backupLeftTunnels, sizeof(tunnel_entry) * TUNNEL_MAX_COUNT); + memcpy(gRightTunnels, backupRightTunnels, sizeof(tunnel_entry) * TUNNEL_MAX_COUNT); #endif } diff --git a/src/paint/supports.c b/src/paint/supports.c index 263020d17f..320f3fbbcb 100644 --- a/src/paint/supports.c +++ b/src/paint/supports.c @@ -687,7 +687,7 @@ bool wooden_b_supports_paint_setup(int supportType, int special, int height, uin * @param imageColourFlags (ebp) * rct2: 0x00663105 */ -bool metal_a_supports_paint_setup(int supportType, int segment, int special, int height, uint32 imageColourFlags) +bool metal_a_supports_paint_setup(uint8 supportType, uint8 segment, int special, int height, uint32 imageColourFlags) { if (gCurrentViewportFlags & VIEWPORT_FLAG_INVISIBLE_SUPPORTS) { return false; @@ -876,7 +876,7 @@ bool metal_a_supports_paint_setup(int supportType, int segment, int special, int * * @return (Carry Flag) */ -bool metal_b_supports_paint_setup(int supportType, uint8 segment, int special, int height, uint32 imageColourFlags) +bool metal_b_supports_paint_setup(uint8 supportType, uint8 segment, int special, int height, uint32 imageColourFlags) { #ifndef NO_RCT2 if (gUseOriginalRidePaint) { diff --git a/src/paint/supports.h b/src/paint/supports.h index 06e4feb187..5c0985cf87 100644 --- a/src/paint/supports.h +++ b/src/paint/supports.h @@ -28,8 +28,8 @@ extern paint_struct * gWoodenSupportsPrependTo; bool wooden_a_supports_paint_setup(int supportType, int special, int height, uint32 imageColourFlags, bool* underground); bool wooden_b_supports_paint_setup(int supportType, int special, int height, uint32 imageColourFlags, bool* underground); -bool metal_a_supports_paint_setup(int supportType, int segment, int special, int height, uint32 imageColourFlags); -bool metal_b_supports_paint_setup(int supportType, uint8 segment, int special, int height, uint32 imageColourFlags); +bool metal_a_supports_paint_setup(uint8 supportType, uint8 segment, int special, int height, uint32 imageColourFlags); +bool metal_b_supports_paint_setup(uint8 supportType, uint8 segment, int special, int height, uint32 imageColourFlags); bool path_a_supports_paint_setup(int supportType, int special, int height, uint32 imageColourFlags, rct_footpath_entry * pathEntry, bool * underground); bool path_b_supports_paint_setup(int supportType, int special, int height, uint32 imageColourFlags, rct_footpath_entry * pathEntry); diff --git a/test/testpaint/FunctionCall.cpp b/test/testpaint/FunctionCall.cpp new file mode 100644 index 0000000000..b120f5b2f7 --- /dev/null +++ b/test/testpaint/FunctionCall.cpp @@ -0,0 +1,140 @@ +#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 "FunctionCall.hpp" + +enum SpriteGroup { + SPRITEGROUP_NONE, + + SPRITEGROUP_FENCE_METAL_A, // 14568 + SPRITEGROUP_FENCE_METAL_B, // 14990 + SPRITEGROUP_FENCE_SPIRAL_SLIDE, // 20564 + SPRITEGROUP_FLOOR_CORK, // 22134 + SPRITEGROUP_FENCE_ROPE, // 22138 +}; + +static void canonicalizeFunctionCall(function_call *call); +static SpriteGroup getSpriteGroup(uint16 spriteIndex); + +bool FunctionCall::AssertsEquals(std::vector expected, std::vector actual) { + if (expected.size() != actual.size()) { + return false; + } + + for (size_t i = 0; i < expected.size(); i++) { + function_call expectedCall = expected[i]; + function_call actualCall = actual[i]; + + if (!AssertsEquals(expectedCall, actualCall)) { + return false; + } + } + + return true; +} + +bool FunctionCall::AssertsEquals(function_call expected, function_call actual) { + canonicalizeFunctionCall(&actual); + canonicalizeFunctionCall(&expected); + + if (expected.function != actual.function) { + return false; + } + + uint8 function = expected.function; + + if (function == SUPPORTS_WOOD_A || function == SUPPORTS_WOOD_B) { + if (expected.supports.type != actual.supports.type) return false; + if (expected.supports.special != actual.supports.special) return false; + if (expected.supports.height != actual.supports.height) return false; + if (expected.supports.colour_flags != actual.supports.colour_flags) return false; + + return true; + } + + if (function == SUPPORTS_METAL_A || function == SUPPORTS_METAL_B) { + if (expected.supports.type != actual.supports.type) return false; + if (expected.supports.segment != actual.supports.segment) return false; + if (expected.supports.special != actual.supports.special) return false; + if (expected.supports.height != actual.supports.height) return false; + if (expected.supports.colour_flags != actual.supports.colour_flags) return false; + + return true; + } + + if (function == SET_SEGMENT_HEIGHT) { + return true; + } + + if (expected.paint.image_id != actual.paint.image_id) { + SpriteGroup expectedSpriteGroup = getSpriteGroup(expected.paint.image_id & 0x7FFFF); + SpriteGroup actualSpriteGroup = getSpriteGroup(actual.paint.image_id & 0x7FFFF); + + if (expectedSpriteGroup != actualSpriteGroup) return false; + + if (expectedSpriteGroup == SPRITEGROUP_NONE) return false; + + return true; + } + + if (expected.paint.offset.x != actual.paint.offset.x) return false; + if (expected.paint.offset.y != actual.paint.offset.y) return false; + if (expected.paint.bound_box_length.x != actual.paint.bound_box_length.x) return false; + if (expected.paint.bound_box_length.y != actual.paint.bound_box_length.y) return false; + if (expected.paint.bound_box_length.z != actual.paint.bound_box_length.z) return false; + if (function != PAINT_98196C) { + if (expected.paint.bound_box_offset.x != actual.paint.bound_box_offset.x) return false; + if (expected.paint.bound_box_offset.y != actual.paint.bound_box_offset.y) return false; + if (expected.paint.bound_box_offset.z != actual.paint.bound_box_offset.z) return false; + } + if (expected.paint.z_offset != actual.paint.z_offset) return false; + if (expected.paint.rotation != actual.paint.rotation) return false; + + return true; +} + +static void canonicalizeFunctionCall(function_call *call) { + if (call->function != PAINT_98197C) return; + if (call->paint.offset.x != call->paint.bound_box_offset.x) return; + if (call->paint.offset.y != call->paint.bound_box_offset.y) return; + if (call->paint.z_offset != call->paint.bound_box_offset.z) return; + + call->function = PAINT_98196C; +} + +static SpriteGroup getSpriteGroup(uint16 spriteIndex) { + if (spriteIndex >= 14568 && spriteIndex <= 14571) { + return SPRITEGROUP_FENCE_METAL_A; + } + + if (spriteIndex >= 14990 && spriteIndex <= 14993) { + return SPRITEGROUP_FENCE_METAL_B; + } + + if (spriteIndex >= 20564 && spriteIndex <= 20567) { + return SPRITEGROUP_FENCE_SPIRAL_SLIDE; + } + + if (spriteIndex >= 22134 && spriteIndex <= 22137) { + return SPRITEGROUP_FLOOR_CORK; + } + + if (spriteIndex >= 22138 && spriteIndex <= 22141) { + return SPRITEGROUP_FENCE_ROPE; + } + + return SPRITEGROUP_NONE; +} diff --git a/test/testpaint/FunctionCall.hpp b/test/testpaint/FunctionCall.hpp new file mode 100644 index 0000000000..10c1c4d18c --- /dev/null +++ b/test/testpaint/FunctionCall.hpp @@ -0,0 +1,63 @@ +#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 + +#pragma once + +#include "../../src/common.h" +#include "TestPaint.hpp" + +enum +{ + PAINT_98196C, + PAINT_98197C, + PAINT_98198C, + PAINT_98199C, + + SUPPORTS_METAL_A, + SUPPORTS_METAL_B, + SUPPORTS_WOOD_A, + SUPPORTS_WOOD_B, + + SET_SEGMENT_HEIGHT, +}; + +typedef struct +{ + uint8 function; + struct paint + { + uint32 image_id; + rct_xy16 offset; + rct_xyz16 bound_box_length; + sint16 z_offset; + rct_xyz16 bound_box_offset; + uint32 rotation; + } paint; + struct supports + { + int type; + uint8 segment; + int special; + int height; + uint32 colour_flags; + } supports; +} function_call; + +class FunctionCall { +public: + static bool AssertsEquals(function_call expected, function_call actual); + static bool AssertsEquals(std::vector expected, std::vector actual); +}; diff --git a/test/testpaint/GeneralSupportHeightCall.cpp b/test/testpaint/GeneralSupportHeightCall.cpp new file mode 100644 index 0000000000..87a75ff973 --- /dev/null +++ b/test/testpaint/GeneralSupportHeightCall.cpp @@ -0,0 +1,79 @@ +#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 + +#include "GeneralSupportHeightCall.hpp" + +bool GeneralSupportHeightCall::CallsMatch(SupportCall tileSupportCalls[4]) { + SupportCall baseCall = tileSupportCalls[0]; + for (int i = 1; i < 4; i++) { + if (tileSupportCalls[i] != baseCall) return false; + } + + return true; +} + +bool GeneralSupportHeightCall::FindMostCommonSupportCall(SupportCall calls[4], SupportCall *out) { + std::map map; + + for (int i = 0; i < 4; ++i) { + if (map.count(calls[i]) == 0) { + map[calls[i]] = 1; + } else { + map[calls[i]] += 1; + } + } + + if (map.size() == 1) { + (*out) = calls[0]; + return true; + } + + if (map.size() == 2) { + for (auto &&item : map) { + if (item.second == 3) { + (*out) = item.first; + return true; + } + } + + return false; + } + + if (map.size() == 3) { + for (auto &&item : map) { + if (item.second == 2) { + (*out) = item.first; + return true; + } + } + + return false; + } + + return false; +} + +bool GeneralSupportHeightCall::AssertEquals(const SupportCall *lhs, const SupportCall *rhs) { + if (lhs == nullptr && rhs == nullptr) return true; + if (lhs == nullptr || rhs == nullptr) return false; + + if (lhs->height != rhs->height) return false; + if (lhs->slope != rhs->slope) return false; + + return true; +} diff --git a/test/testpaint/GeneralSupportHeightCall.hpp b/test/testpaint/GeneralSupportHeightCall.hpp new file mode 100644 index 0000000000..919c43529e --- /dev/null +++ b/test/testpaint/GeneralSupportHeightCall.hpp @@ -0,0 +1,51 @@ +#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 + +#pragma once + +#include "../../src/common.h" + +struct SupportCall { + sint32 height; + sint16 slope; + + friend bool operator==(const SupportCall& lhs, const SupportCall& rhs) { + if (lhs.height != rhs.height) return false; + if (lhs.slope != rhs.slope) return false; + + return true; + } + + bool operator!=(const SupportCall &other) const { + return !(*this == other); + } + + bool operator<(const SupportCall &other) const { + if (height != other.height) { + return height < other.height; + } + + return slope < other.slope; + } +}; + +namespace GeneralSupportHeightCall { + bool CallsMatch(SupportCall tileSupportCalls[4]); + + bool FindMostCommonSupportCall(SupportCall calls[4], SupportCall *out); + + bool AssertEquals(const SupportCall *lhs, const SupportCall *rhs); +}; diff --git a/test/testpaint/PaintIntercept.cpp b/test/testpaint/PaintIntercept.cpp new file mode 100644 index 0000000000..10d3c03280 --- /dev/null +++ b/test/testpaint/PaintIntercept.cpp @@ -0,0 +1,341 @@ +#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 "PaintIntercept.hpp" + +extern "C" { +#include "../../src/common.h" +#include "../../src/hook.h" +#include "../../src/interface/viewport.h" +#include "../../src/paint/supports.h" +} + +static bool _woodenSupports = false; +static uint8 _callCount = 0; +static function_call _calls[256] = {0}; + +namespace PaintIntercept { + static uint32 InterceptWoodenASupports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp); + static uint32 InterceptWoodenBSupports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp); + static uint32 InterceptMetalASupports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp); + static uint32 InterceptMetalBSupports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp); + static uint32 InterceptPaint6C(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp); + static uint32 InterceptPaint7C(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp); + static uint32 InterceptPaint8C(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp); + static uint32 InterceptPaint9C(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp); + static uint32 InterceptPaintFull(uint8 function, uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp); + bool PaintMetalSupports(uint8 function, int supportType, uint8 segment, int special, int height, uint32 imageColourFlags); + bool PaintWoodenSupports(uint8 function, int supportType, int special, int height, uint32 imageColourFlags, bool *underground); + static void CheckSegmentSupportHeight(); + + void InitHooks() { + int supportsRegisterArgs[] = {EAX, EBX, EDX, EDI, EBP, END}; + addhook(0x006629BC, (int) InterceptWoodenASupports, 0, supportsRegisterArgs, 0, EAX); + addhook(0x00662D5C, (int) InterceptWoodenBSupports, 0, supportsRegisterArgs, 0, EAX); + + addhook(0x00663105, (int) InterceptMetalASupports, 0, supportsRegisterArgs, 0, EAX); + addhook(0x00663584, (int) InterceptMetalBSupports, 0, supportsRegisterArgs, 0, EAX); + + int paintRegisterArgs[] = {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}; + addhook(0x006861AC, (int) InterceptPaint6C, 0, paintRegisterArgs, 0, EBP); + addhook(0x00686337, (int) InterceptPaint6C, 0, paintRegisterArgs, 0, EBP); + addhook(0x006864D0, (int) InterceptPaint6C, 0, paintRegisterArgs, 0, EBP); + addhook(0x0068666B, (int) InterceptPaint6C, 0, paintRegisterArgs, 0, EBP); + + addhook(0x00686806, (int) InterceptPaint7C, 0, paintRegisterArgs, 0, EBP); + addhook(0x006869B2, (int) InterceptPaint7C, 0, paintRegisterArgs, 0, EBP); + addhook(0x00686B6F, (int) InterceptPaint7C, 0, paintRegisterArgs, 0, EBP); + addhook(0x00686D31, (int) InterceptPaint7C, 0, paintRegisterArgs, 0, EBP); + + addhook(0x00686EF0, (int) InterceptPaint8C, 0, paintRegisterArgs, 0, EBP); + addhook(0x00687056, (int) InterceptPaint8C, 0, paintRegisterArgs, 0, EBP); + addhook(0x006871C8, (int) InterceptPaint8C, 0, paintRegisterArgs, 0, EBP); + addhook(0x0068733C, (int) InterceptPaint8C, 0, paintRegisterArgs, 0, EBP); + + addhook(0x006874B0, (int) InterceptPaint9C, 0, paintRegisterArgs, 0, EBP); + addhook(0x00687618, (int) InterceptPaint9C, 0, paintRegisterArgs, 0, EBP); + addhook(0x0068778C, (int) InterceptPaint9C, 0, paintRegisterArgs, 0, EBP); + addhook(0x00687902, (int) InterceptPaint9C, 0, paintRegisterArgs, 0, EBP); + } + + bool PaintWoodenSupports(uint8 function, int supportType, int special, int height, uint32 imageColourFlags, bool *underground) { + function_call call = {0}; + call.function = function; + call.supports.type = supportType; + call.supports.special = special; + call.supports.height = height; + call.supports.colour_flags = imageColourFlags; + + _calls[_callCount] = call; + _callCount++; + + return _woodenSupports; + } + + bool PaintMetalSupports(uint8 function, int supportType, uint8 segment, int special, int height, uint32 imageColourFlags) { + CheckSegmentSupportHeight(); + + function_call call = {0}; + call.function = function; + call.supports.type = supportType; + call.supports.segment = segment; + call.supports.special = special; + call.supports.height = height; + call.supports.colour_flags = imageColourFlags; + + _calls[_callCount] = call; + _callCount++; + + return false; + } + + static paint_struct *Paint6C( + uint32 imageID, + sint8 xOffset, sint8 yOffset, + sint16 boundBoxLengthX, sint16 boundBoxLengthY, sint8 boundBoxLengthZ, + sint16 zOffset, + uint32 rotation + ) { + function_call call = {0}; + call.function = PAINT_98196C; + call.paint.image_id = imageID; + call.paint.offset = {xOffset, yOffset}; + call.paint.bound_box_length = {boundBoxLengthX, boundBoxLengthY, boundBoxLengthZ}; + call.paint.z_offset = zOffset; + call.paint.rotation = rotation; + + _calls[_callCount] = call; + _callCount++; + + return nullptr; + } + + static paint_struct *PaintFull( + uint8 function, + uint32 imageID, + sint8 xOffset, sint8 yOffset, + sint16 boundBoxLengthX, sint16 boundBoxLengthY, sint8 boundBoxLengthZ, + sint16 zOffset, + sint16 boundBoxOffsetX, sint16 boundBoxOffsetY, sint16 boundBoxOffsetZ, + uint32 rotation + ) { + function_call call = {0}; + call.function = function; + call.paint.image_id = imageID; + call.paint.offset = {xOffset, yOffset}; + call.paint.bound_box_length = {boundBoxLengthX, boundBoxLengthY, boundBoxLengthZ}; + call.paint.bound_box_offset = {boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ}; + call.paint.z_offset = zOffset; + call.paint.rotation = rotation; + + _calls[_callCount] = call; + _callCount++; + + return nullptr; + } + + void ClearCalls() { + _callCount = 0; + memset(_calls, 0, sizeof(_calls)); + } + + int GetCalls(function_call *buffer) { + memcpy(buffer, _calls, _callCount * sizeof(function_call)); + return _callCount; + } + + void SetSimulateWoodenSupports(bool enabled) { + _woodenSupports = enabled; + } + + static uint32 InterceptMetalASupports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp) { + bool output = PaintMetalSupports(SUPPORTS_METAL_A, edi, ebx, (sint16) (eax & 0xFFFF), (edx & 0xFFFF), ebp); + + return output ? 1 : 0; + } + + static uint32 InterceptMetalBSupports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp) { + bool output = PaintMetalSupports(SUPPORTS_METAL_B, edi, ebx, (sint16) (eax & 0xFFFF), (edx & 0xFFFF), ebp); + + return output ? 1 : 0; + } + + static void CheckSegmentSupportHeight() { + bool hasChanged = false; + for (int i = 0; i < 9; i++) { + if (gSupportSegments[i].height != 0) hasChanged = true; + if (gSupportSegments[i].slope != 0xFF) hasChanged = true; + } + + if (!hasChanged) { + return; + } + + function_call call = {0}; + call.function = SET_SEGMENT_HEIGHT; + + _calls[_callCount] = call; + _callCount++; + } + + static uint32 InterceptWoodenASupports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp) { + bool output = PaintWoodenSupports(SUPPORTS_WOOD_A, edi, eax & 0xFFFF, edx & 0xFFFF, ebp, nullptr); + + return output ? 1 : 0; + } + + static uint32 InterceptWoodenBSupports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp) { + bool output = PaintWoodenSupports(SUPPORTS_WOOD_B, edi, eax & 0xFFFF, edx & 0xFFFF, ebp, nullptr); + + return output ? 1 : 0; + } + + static uint32 InterceptPaint6C(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp) { + if ((ebp & 0x03) != get_current_rotation()) { + // Log error + log_error("Ebp is different from current rotation"); + } + + return (uintptr_t) Paint6C( + ebx, + (sint8) (eax & 0xFF), (sint8) (ecx & 0xFF), + (sint16) (edi & 0xFFFF), (sint16) (esi & 0xFFFF), (sint8) ((eax >> 8) & 0xFF), + edx & 0xFFFF, + ebp & 0x03 + ); + } + + static uint32 InterceptPaint7C(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp) { + return InterceptPaintFull(PAINT_98197C, eax, ebx, ecx, edx, esi, edi, ebp); + } + + static uint32 InterceptPaint8C(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp) { + return InterceptPaintFull(PAINT_98198C, eax, ebx, ecx, edx, esi, edi, ebp); + } + + static uint32 InterceptPaint9C(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp) { + return InterceptPaintFull(PAINT_98199C, eax, ebx, ecx, edx, esi, edi, ebp); + } + + static uint32 InterceptPaintFull(uint8 function, uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp) { + if ((ebp & 0x03) != get_current_rotation()) { + // Log error + log_error("Ebp is different from current rotation"); + } + + rct_xyz16 boundOffset = { + RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_X, sint16), + RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Y, sint16), + RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Z, sint16) + }; + + return (uintptr_t) PaintFull( + function, + ebx, + (sint8) (eax & 0xFF), (sint8) (ecx & 0xFF), + (sint16) (edi & 0xFFFF), (sint16) (esi & 0xFFFF), (sint8) ((eax >> 8) & 0xFF), + edx & 0xFFFF, + boundOffset.x, boundOffset.y, boundOffset.z, + ebp & 0x03 + ); + } +}; + + +extern "C" { +bool wooden_a_supports_paint_setup(int supportType, int special, int height, uint32 imageColourFlags, bool *underground) { + return PaintIntercept::PaintWoodenSupports(SUPPORTS_WOOD_A, supportType, special, height, imageColourFlags, underground); +} + +bool wooden_b_supports_paint_setup(int supportType, int special, int height, uint32 imageColourFlags, bool *underground) { + return PaintIntercept::PaintWoodenSupports(SUPPORTS_WOOD_B, supportType, special, height, imageColourFlags, underground); +} + +bool metal_a_supports_paint_setup(uint8 supportType, uint8 segment, int special, int height, uint32 imageColourFlags) { + return PaintIntercept::PaintMetalSupports(SUPPORTS_METAL_A, supportType, segment, special, height, imageColourFlags); +} + +bool metal_b_supports_paint_setup(uint8 supportType, uint8 segment, int special, int height, uint32 imageColourFlags) { + return PaintIntercept::PaintMetalSupports(SUPPORTS_METAL_B, supportType, segment, special, height, imageColourFlags); +} + +paint_struct *sub_98196C(uint32 image_id, sint8 x_offset, sint8 y_offset, sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, sint16 z_offset, uint32 rotation) { + return PaintIntercept::Paint6C(image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset, rotation); +} + +paint_struct *sub_98197C( + uint32 image_id, + sint8 x_offset, sint8 y_offset, + sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, + sint16 z_offset, + sint16 bound_box_offset_x, sint16 bound_box_offset_y, sint16 bound_box_offset_z, + uint32 rotation +) { + return PaintIntercept::PaintFull( + PAINT_98197C, + image_id, + x_offset, y_offset, + bound_box_length_x, bound_box_length_y, bound_box_length_z, + z_offset, + bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, + rotation + ); +} + +paint_struct *sub_98198C( + uint32 image_id, + sint8 x_offset, sint8 y_offset, + sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, + sint16 z_offset, + sint16 bound_box_offset_x, sint16 bound_box_offset_y, sint16 bound_box_offset_z, + uint32 rotation +) { + return PaintIntercept::PaintFull( + PAINT_98198C, + image_id, + x_offset, y_offset, + bound_box_length_x, bound_box_length_y, bound_box_length_z, + z_offset, + bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, + rotation + ); +} + +paint_struct *sub_98199C( + uint32 image_id, + sint8 x_offset, sint8 y_offset, + sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, + sint16 z_offset, + sint16 bound_box_offset_x, sint16 bound_box_offset_y, sint16 bound_box_offset_z, + uint32 rotation +) { + return PaintIntercept::PaintFull( + PAINT_98199C, + image_id, + x_offset, y_offset, + bound_box_length_x, bound_box_length_y, bound_box_length_z, + z_offset, + bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, + rotation + ); +} + +bool paint_attach_to_previous_ps(uint32 image_id, uint16 x, uint16 y) { + return false; +} + +} diff --git a/test/testpaint/PaintIntercept.hpp b/test/testpaint/PaintIntercept.hpp new file mode 100644 index 0000000000..32ee986583 --- /dev/null +++ b/test/testpaint/PaintIntercept.hpp @@ -0,0 +1,29 @@ +#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 + +#pragma once + +#include "../../src/common.h" +#include "FunctionCall.hpp" + +namespace PaintIntercept { + void InitHooks(); + + void ClearCalls(); + int GetCalls(function_call *buffer); + + void SetSimulateWoodenSupports(bool enabled); +}; diff --git a/test/testpaint/Printer.cpp b/test/testpaint/Printer.cpp new file mode 100644 index 0000000000..31d5d75e09 --- /dev/null +++ b/test/testpaint/Printer.cpp @@ -0,0 +1,253 @@ +#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 "Printer.hpp" +#include "String.hpp" +#include "../../src/core/Util.hpp" + +namespace Printer { + + static const char *functionNames[] = { + "sub_98196C", + "sub_98197C", + "sub_98198C", + "sub_98199C", + "metal_a_supports_paint_setup", + "metal_b_supports_paint_setup", + "wooden_a_supports_paint_setup", + "wooden_b_supports_paint_setup", + "paint_util_set_segment_support_height", + }; + + static std::string GetImageIdString(uint32 imageId); + + static std::string GetOffsetExpressionString(int offset); + + static std::string PrintSegmentSupportHeightCall(SegmentSupportCall call); + + static std::string PrintSideTunnelEdge(TunnelCall edge); + + std::string PrintFunctionCalls(std::vector calls, uint16 baseHeight) { + std::string out; + + for (auto &&call : calls) { + out += PrintFunctionCall(call, baseHeight).c_str(); + out += "\n"; + } + + return out; + } + + std::string PrintFunctionCall(function_call call, uint16 baseHeight) { + std::string imageId = GetImageIdString(call.supports.colour_flags); + assert(call.function < Util::CountOf(functionNames)); + const char *functionName = functionNames[call.function]; + + switch (call.function) { + case SUPPORTS_WOOD_A: + case SUPPORTS_WOOD_B: + return String::Format( + "%s(%d, %d, %s, %s)", functionName, call.supports.type, call.supports.special, + PrintHeightOffset(call.supports.height, baseHeight).c_str(), imageId.c_str() + ); + + case SUPPORTS_METAL_A: + case SUPPORTS_METAL_B: + return String::Format( + "%s(%d, %d, %d, %s, %s)", functionName, call.supports.type, call.supports.segment, call.supports.special, + PrintHeightOffset(call.supports.height, baseHeight).c_str(), imageId.c_str() + ); + + case SET_SEGMENT_HEIGHT: + return "paint_util_set_segment_support_height"; + } + + std::string s = String::Format("%s(", functionName); + + imageId = GetImageIdString(call.paint.image_id); + s += String::Format("%s, ", imageId.c_str()); + s += String::Format("%d, %d, ", call.paint.offset.x, call.paint.offset.y); + s += String::Format( + "%d, %d, %d, ", + call.paint.bound_box_length.x, call.paint.bound_box_length.y, call.paint.bound_box_length.z + ); + s += String::Format("%s, ", PrintHeightOffset(call.paint.z_offset, baseHeight).c_str()); + + if (call.function != PAINT_98196C) { + s += String::Format( + "%d, %d, %s, ", + call.paint.bound_box_offset.x, call.paint.bound_box_offset.y, + PrintHeightOffset(call.paint.bound_box_offset.z, baseHeight).c_str() + ); + } + + s += String::Format("%d)", call.paint.rotation); + + + if (call.function != PAINT_98196C) { + s += String::Format( + " = { %d, %d, %s }, { %d, %d, %s }, { %d, %d, %d }", + call.paint.offset.x, call.paint.offset.y, PrintHeightOffset(call.paint.z_offset, baseHeight).c_str(), + call.paint.bound_box_offset.x, call.paint.bound_box_offset.y, + PrintHeightOffset(call.paint.bound_box_offset.z, baseHeight).c_str(), + call.paint.bound_box_length.x, call.paint.bound_box_length.y, call.paint.bound_box_length.z); + } + + return s; + } + + std::string PrintSegmentSupportHeightCalls(std::vector calls) { + std::string out = ""; + + for (auto &&call : calls) { + out += PrintSegmentSupportHeightCall(call); + } + + return out; + } + + static std::string PrintSegmentSupportHeightCall(SegmentSupportCall call) { + std::string out = ""; + + int segmentsPrinted = 0; + for (int i = 0; i < 9; i++) { + if (call.segments & segment_offsets[i]) { + if (segmentsPrinted > 0) { + out += " | "; + } + out += String::Format("SEGMENT_%02X", 0xB4 + 4 * i); + segmentsPrinted++; + } + } + + if (call.height == 0xFFFF) { + out += ", 0xFFFF"; + } else { + out += String::Format(", %d", call.height); + } + + out += String::Format(", 0x%02X\n", call.slope); + + return out; + } + + std::string PrintSideTunnelCalls(TunnelCall tunnelCalls[4][4]) { + std::string s; + + for (int direction = 0; direction < 4; ++direction) { + s += " + "; + } + s += "\n"; + + for (int direction = 0; direction < 4; ++direction) { + std::string leftEdge = PrintSideTunnelEdge(tunnelCalls[direction][2]); + std::string rightEdge = PrintSideTunnelEdge(tunnelCalls[direction][3]); + s += String::Format(" %s %s ", leftEdge.c_str(), rightEdge.c_str()); + } + s += "\n"; + + for (int direction = 0; direction < 4; ++direction) { + s += " + + "; + } + s += "\n"; + + for (int direction = 0; direction < 4; ++direction) { + std::string leftEdge = PrintSideTunnelEdge(tunnelCalls[direction][0]); + std::string rightEdge = PrintSideTunnelEdge(tunnelCalls[direction][1]); + s += String::Format(" %s %s ", leftEdge.c_str(), rightEdge.c_str()); + } + s += "\n"; + + for (int direction = 0; direction < 4; ++direction) { + s += " + "; + } + s += "\n"; + + for (int direction = 0; direction < 4; ++direction) { + s += String::Format(" direction %d ", direction); + } + s += "\n"; + + return s; + } + + static std::string PrintSideTunnelEdge(TunnelCall edge) { + std::string s; + + switch (edge.call) { + case TUNNELCALL_SKIPPED: + s = " "; + break; + + case TUNNELCALL_NONE: + s = " - "; + break; + + case TUNNELCALL_CALL: + std::string offset; + + if (edge.offset <= 0) { + offset = String::Format("%d", edge.offset); + } else { + offset = String::Format("+%d", edge.offset); + } + s = String::Format("%3s/%X ", offset.c_str(), edge.type); + break; + } + + return s; + } + + static std::string GetImageIdString(uint32 imageId) + { + std::string result; + + uint32 image = imageId & 0x7FFFF; + uint32 palette = imageId & ~0x7FFFF; + + std::string paletteName; + if (palette == TestPaint::DEFAULT_SCHEME_TRACK) paletteName = "SCHEME_TRACK"; + else if (palette == TestPaint::DEFAULT_SCHEME_SUPPORTS) paletteName = "SCHEME_SUPPORTS"; + else if (palette == TestPaint::DEFAULT_SCHEME_MISC) paletteName = "SCHEME_MISC"; + else if (palette == TestPaint::DEFAULT_SCHEME_3) paletteName = "SCHEME_3"; + else { + paletteName = String::Format("0x%08X", palette); + } + + if (image == 0) { + result = paletteName; + } else if (image & 0x70000) { + result = String::Format("%s | vehicle.base_image_id + %d", paletteName.c_str(), image & ~0x70000); + } else { + result = String::Format("%s | %d", paletteName.c_str(), image); + } + + return result; + } + + std::string PrintHeightOffset(uint16 height, uint16 baseHeight) { + int offset = height - baseHeight; + + return String::Format("height%s", GetOffsetExpressionString(offset).c_str()); + } + + static std::string GetOffsetExpressionString(int offset) + { + if (offset < 0) return std::string(" - ") + std::to_string(-offset); + if (offset > 0) return std::string(" + ") + std::to_string(offset); + return std::string(); + } +}; diff --git a/test/testpaint/Printer.hpp b/test/testpaint/Printer.hpp new file mode 100644 index 0000000000..f387f6adfa --- /dev/null +++ b/test/testpaint/Printer.hpp @@ -0,0 +1,35 @@ +#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 + +#pragma once + +#include +#include + +#include "FunctionCall.hpp" +#include "SideTunnelCall.hpp" +#include "SegmentSupportHeightCall.hpp" + +namespace Printer { + std::string PrintFunctionCall(function_call call, uint16 baseHeight); + std::string PrintFunctionCalls(std::vector calls, uint16 baseHeight); + + std::string PrintSegmentSupportHeightCalls(std::vector calls); + + std::string PrintSideTunnelCalls(TunnelCall tunnelCalls[4][4]); + + std::string PrintHeightOffset(uint16 height, uint16 baseHeight); +} diff --git a/test/testpaint/SegmentSupportHeightCall.cpp b/test/testpaint/SegmentSupportHeightCall.cpp new file mode 100644 index 0000000000..082df5665c --- /dev/null +++ b/test/testpaint/SegmentSupportHeightCall.cpp @@ -0,0 +1,152 @@ +#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 +#include + +#include "SegmentSupportHeightCall.hpp" + +extern "C" { +#include "../../src/paint/map_element/map_element.h" +} + +static 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; +} + +std::vector SegmentSupportHeightCall::getSegmentCalls(support_height *supports, 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 = { 0 }; + + 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; +} + +bool SegmentSupportHeightCall::CallsMatch(std::vector tileSegmentSupportCalls[4]) +{ + std::vector baseCallList = tileSegmentSupportCalls[0]; + for (int i = 1; i < 4; i++) { + if (!CallsEqual(baseCallList, tileSegmentSupportCalls[i])) { + return false; + } + } + + return true; +} + +bool SegmentSupportHeightCall::CallsEqual(std::vector lhs, std::vector rhs) +{ + if (lhs.size() != rhs.size()) return false; + for (size_t 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; +} + +bool SegmentSupportHeightCall::FindMostCommonSupportCall(std::vector calls[4], std::vector *out) { + std::map, int> map; + + for (int i = 0; i < 4; ++i) { + map[calls[i]] += 1; + } + + if (map.size() == 1) { + (*out) = calls[0]; + return true; + } + + if (map.size() == 2) { + for (auto &&item : map) { + if (item.second == 3) { + (*out) = item.first; + return true; + } + } + + return false; + } + + if (map.size() == 3) { + for (auto &&item : map) { + if (item.second == 2) { + (*out) = item.first; + return true; + } + } + + return false; + } + + return false; +} diff --git a/test/testpaint/SegmentSupportHeightCall.hpp b/test/testpaint/SegmentSupportHeightCall.hpp new file mode 100644 index 0000000000..6b3c9728c2 --- /dev/null +++ b/test/testpaint/SegmentSupportHeightCall.hpp @@ -0,0 +1,52 @@ +#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 + +#pragma once + +#include + +#include "../../src/common.h" + +extern "C" { + #include "../../src/paint/paint.h" +} + +struct SegmentSupportCall +{ + uint16 segments; + sint32 height; + sint16 slope; + + bool operator<(const SegmentSupportCall &other) const { + if (height != other.height) { + return height < other.height; + } + + if (segments != other.segments) { + return segments < other.segments; + } + + return slope < other.slope; + } +}; + +class SegmentSupportHeightCall { +public: + static std::vector getSegmentCalls(support_height supports[9], uint8 rotation); + static bool CallsMatch(std::vector tileSegmentSupportCalls[4]); + static bool CallsEqual(std::vector lhs, std::vector rhs); + static bool FindMostCommonSupportCall(std::vector calls[4], std::vector * out); +}; diff --git a/test/testpaint/SideTunnelCall.cpp b/test/testpaint/SideTunnelCall.cpp new file mode 100644 index 0000000000..cca3c35a44 --- /dev/null +++ b/test/testpaint/SideTunnelCall.cpp @@ -0,0 +1,106 @@ +#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 "SideTunnelCall.hpp" + +sint16 SideTunnelCall::GetTunnelOffset(uint32 baseHeight, tunnel_entry calls[3]) { + for (sint16 offset = -56; offset <= 56; offset += 8) { + if (calls[0].height != (baseHeight - 8 + offset) / 16) continue; + if (calls[1].height != (baseHeight + 0 + offset) / 16) continue; + if (calls[2].height != (baseHeight + 8 + offset) / 16) continue; + + return offset; + } + + log_error("Unknown tunnel height. (%d, %d, %d)", calls[0].height, calls[1].height, calls[2].height); + return 0; +} + + +TunnelCall SideTunnelCall::ExtractTunnelCalls(tunnel_entry *calls, uint8 count, uint16 baseHeight, bool *error) { + TunnelCall tunnelCall = {0}; + + if (count == 0) { + tunnelCall.call = TUNNELCALL_NONE; + return tunnelCall; + } + + if (count == 3) { + tunnelCall.call = TUNNELCALL_CALL; + tunnelCall.offset = GetTunnelOffset(baseHeight, calls); + tunnelCall.type = calls[0].type; + return tunnelCall; + } + + *error = true; + return tunnelCall; +} + + +bool SideTunnelCall::TunnelCallsLineUp(TunnelCall tunnelCalls[4][4]) +{ + for (int side = 0; side < 4; ++side) { + TunnelCall * referenceCall = nullptr; + for (int direction = 0; direction < 4; ++direction) { + if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED) { + continue; + } + + if (referenceCall == nullptr) { + referenceCall = &tunnelCalls[direction][side]; + continue; + } + + if (referenceCall->call != tunnelCalls[direction][side].call) return false; + + if (referenceCall->call == TUNNELCALL_CALL) { + if (referenceCall->type != tunnelCalls[direction][side].type) return false; + if (referenceCall->offset != tunnelCalls[direction][side].offset) return false; + } + } + } + + return true; +} + +void SideTunnelCall::GetTunnelCallReferencePattern(TunnelCall tunnelCalls[4][4], TunnelCall (*out)[4]) +{ + for (int side = 0; side < 4; ++side) { + for (int direction = 0; direction < 4; ++direction) { + if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED) { + continue; + } + + (*out)[side].call = tunnelCalls[direction][side].call; + (*out)[side].type = tunnelCalls[direction][side].type; + (*out)[side].offset = tunnelCalls[direction][side].offset; + } + } +} + +bool SideTunnelCall::TunnelPatternsMatch(TunnelCall expected[4], TunnelCall actual[4]) +{ + for (int side = 0; side < 4; side++) { + if (expected[side].call != actual[side].call) return false; + + if (expected[side].call == TUNNELCALL_CALL) { + if (expected[side].type != actual[side].type) return false; + if (expected[side].offset != actual[side].offset) return false; + } + } + + return true; +} diff --git a/test/testpaint/SideTunnelCall.hpp b/test/testpaint/SideTunnelCall.hpp new file mode 100644 index 0000000000..0c585516e6 --- /dev/null +++ b/test/testpaint/SideTunnelCall.hpp @@ -0,0 +1,44 @@ +#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 + +#pragma once + +#include "../../src/common.h" + +extern "C" { +#include "../../src/paint/map_element/map_element.h" +} + +enum { + TUNNELCALL_SKIPPED, + TUNNELCALL_NONE, + TUNNELCALL_CALL, +}; + +struct TunnelCall { + uint8 call; + sint16 offset; + uint8 type; +}; + +namespace SideTunnelCall { + sint16 GetTunnelOffset(uint32 baseHeight, tunnel_entry calls[3]); + TunnelCall ExtractTunnelCalls(tunnel_entry * list, uint8 count, uint16 baseHeight, bool * error); + + bool TunnelPatternsMatch(TunnelCall expected[4], TunnelCall actual[4]); + void GetTunnelCallReferencePattern(TunnelCall tunnelCalls[4][4], TunnelCall (*out)[4]); + bool TunnelCallsLineUp(TunnelCall tunnelCalls[4][4]); +}; diff --git a/test/testpaint/String.cpp b/test/testpaint/String.cpp new file mode 100644 index 0000000000..6260e2870d --- /dev/null +++ b/test/testpaint/String.cpp @@ -0,0 +1,34 @@ +#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 + +#include "String.hpp" + +namespace String { + std::string Format(const char * format, ...) + { + va_list args; + char buffer[512]; + + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + + return std::string(buffer); + } +}; + diff --git a/test/testpaint/String.hpp b/test/testpaint/String.hpp new file mode 100644 index 0000000000..67c232530a --- /dev/null +++ b/test/testpaint/String.hpp @@ -0,0 +1,23 @@ +#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 + +#pragma once + +#include + +namespace String { + std::string Format(const char *format, ...); +} diff --git a/test/testpaint/TestPaint.cpp b/test/testpaint/TestPaint.cpp new file mode 100644 index 0000000000..a5a74ce3b0 --- /dev/null +++ b/test/testpaint/TestPaint.cpp @@ -0,0 +1,151 @@ +#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 +#include + +#include "TestPaint.hpp" +#include "GeneralSupportHeightCall.hpp" +#include "Printer.hpp" +#include "SegmentSupportHeightCall.hpp" +#include "Utils.hpp" + +extern "C" { + #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 TestPaint +{ + void ResetEnvironment() { + gPaintInteractionType = VIEWPORT_INTERACTION_ITEM_RIDE; + gTrackColours[SCHEME_TRACK] = DEFAULT_SCHEME_TRACK; + gTrackColours[SCHEME_SUPPORTS] = DEFAULT_SCHEME_SUPPORTS; + gTrackColours[SCHEME_MISC] = DEFAULT_SCHEME_MISC; + gTrackColours[SCHEME_3] = DEFAULT_SCHEME_3; + + rct_drawpixelinfo dpi = { 0 }; + dpi.zoom_level = 1; + unk_140E9A8 = &dpi; + + rct_ride ride = {0}; + ride.entrance_style = RIDE_ENTRANCE_STYLE_PLAIN; + + rct_ride_entry rideEntry = {0}; + rct_ride_entry_vehicle vehicleEntry { 0 }; + vehicleEntry.base_image_id = 0x70000; + rideEntry.vehicles[0] = vehicleEntry; + + gRideList[0] = ride; + gRideEntries[0] = &rideEntry; + + g141E9DB = G141E9DB_FLAG_1 | G141E9DB_FLAG_2; + } + + void ResetTunnels() { + gLeftTunnelCount = 0; + gRightTunnelCount = 0; + + for (int i = 0; i < TUNNEL_MAX_COUNT; i++) { + gLeftTunnels[i].height = 0; + gLeftTunnels[i].type = 0; + gRightTunnels[i].height = 0; + gRightTunnels[i].type = 0; + } + + gLeftTunnels[0].height = 0xFF; + gLeftTunnels[0].type = 0xFF; + gRightTunnels[0].height = 0xFF; + gRightTunnels[0].type = 0xFF; + } + + void ResetSupportHeights() { + for (int s = 0; s < 9; ++s) + { + gSupportSegments[s].height = 0; + gSupportSegments[s].slope = 0xFF; + } + + gSupport.height = 0; + gSupport.slope = 0xFF; + } + + struct IgnoredEntry + { + uint8 Direction; + uint8 TrackSequence; + }; + + static bool _ignoredAll; + static std::vector _ignoredEntries; + + static void testClearIgnore() + { + _ignoredAll = false; + _ignoredEntries.clear(); + } + + static void testIgnore(uint8 direction, uint8 trackSequence) + { + _ignoredEntries.push_back({ direction, trackSequence }); + } + + static void testIgnoreAll() + { + _ignoredAll = true; + } + + static bool testIsIgnored(uint8 direction, uint8 trackSequence) + { + if (_ignoredAll) return true; + for (const IgnoredEntry &entry : _ignoredEntries) + { + if (entry.Direction == direction && + entry.TrackSequence == trackSequence) + { + return true; + } + } + return false; + } +} + +extern "C" +{ + void testpaint_clear_ignore() + { + TestPaint::testClearIgnore(); + } + + void testpaint_ignore(uint8 direction, uint8 trackSequence) + { + TestPaint::testIgnore(direction, trackSequence); + } + + void testpaint_ignore_all() + { + TestPaint::testIgnoreAll(); + } + + bool testpaint_is_ignored(uint8 direction, uint8 trackSequence) + { + return TestPaint::testIsIgnored(direction, trackSequence); + } + +} diff --git a/test/testpaint/TestPaint.hpp b/test/testpaint/TestPaint.hpp new file mode 100644 index 0000000000..f5e4f3de89 --- /dev/null +++ b/test/testpaint/TestPaint.hpp @@ -0,0 +1,59 @@ +#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 + +#pragma once + +#include + +#include "../../src/common.h" + +extern "C" +{ + #include "../../src/interface/colour.h" + #include "../../src/paint/paint.h" + #include "../../src/paint/map_element/map_element.h" +} + +#define gRideEntries RCT2_ADDRESS(RCT2_ADDRESS_RIDE_ENTRIES, rct_ride_entry*) +#define gCurrentRotation RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8) + +enum { + TEST_SUCCESS, + TEST_FAILED, + TEST_SKIPPED, +}; + +namespace TestPaint +{ + static const uint32 DEFAULT_SCHEME_TRACK = COLOUR_GREY << 19 | COLOUR_WHITE << 24 | 0xA0000000; + static const uint32 DEFAULT_SCHEME_SUPPORTS = COLOUR_LIGHT_BLUE << 19 | COLOUR_ICY_BLUE << 24 | 0xA0000000; + static const uint32 DEFAULT_SCHEME_MISC = COLOUR_DARK_PURPLE << 19 | COLOUR_LIGHT_PURPLE << 24 | 0xA0000000; + static const uint32 DEFAULT_SCHEME_3 = COLOUR_BRIGHT_PURPLE << 19 | COLOUR_DARK_BLUE << 24 | 0xA0000000; + + void ResetEnvironment(); + void ResetTunnels(); + void ResetSupportHeights(); +} + +enum Verbosity { + QUIET, + NORMAL, +}; + +extern "C" +{ +int generatePaintCode(uint8 rideType); +} diff --git a/test/testpaint/TestTrack.cpp b/test/testpaint/TestTrack.cpp new file mode 100644 index 0000000000..c067779e43 --- /dev/null +++ b/test/testpaint/TestTrack.cpp @@ -0,0 +1,751 @@ +#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 +#include + +#include "TestPaint.hpp" +#include "FunctionCall.hpp" +#include "GeneralSupportHeightCall.hpp" +#include "PaintIntercept.hpp" +#include "Printer.hpp" +#include "SegmentSupportHeightCall.hpp" +#include "SideTunnelCall.hpp" +#include "String.hpp" +#include "TestTrack.hpp" +#include "Utils.hpp" +#include "VerticalTunnelCall.hpp" + +extern "C" { +#include "../../src/ride/ride.h" +#include "../../src/ride/track.h" +#include "../../src/ride/track_data.h" +} + +interface ITestTrackFilter { +public: + virtual ~ITestTrackFilter() {} + + virtual bool AppliesTo(uint8 rideType, uint8 trackType) abstract; + + virtual int Variations(uint8 rideType, uint8 trackType) abstract; + + virtual std::string VariantName(uint8 rideType, uint8 trackType, int variant) abstract; + + virtual void ApplyTo(uint8 rideType, uint8 trackType, int variant, + rct_map_element *mapElement, rct_map_element *surfaceElement, + rct_ride *ride, rct_ride_entry *rideEntry + ) abstract; +}; + +class CableLiftFilter : public ITestTrackFilter { +public: + bool AppliesTo(uint8 rideType, uint8 trackType) { + return rideType == RIDE_TYPE_GIGA_COASTER; + } + + int Variations(uint8 rideType, uint8 trackType) { + return 2; + } + + std::string VariantName(uint8 rideType, uint8 trackType, int variant) { + return String::Format("cableLift:%d", variant); + } + + virtual void ApplyTo(uint8 rideType, uint8 trackType, int variant, + rct_map_element *mapElement, rct_map_element *surfaceElement, + rct_ride *ride, rct_ride_entry *rideEntry + ) { + if (variant == 0) { + mapElement->properties.track.colour &= ~TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + } else { + mapElement->properties.track.colour |= TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; + } + } +}; + +class ChainLiftFilter : public ITestTrackFilter { +public: + bool AppliesTo(uint8 rideType, uint8 trackType) { + return !ride_type_has_flag(rideType, RIDE_TYPE_FLAG_FLAT_RIDE); + } + + int Variations(uint8 rideType, uint8 trackType) { + return 2; + } + + std::string VariantName(uint8 rideType, uint8 trackType, int variant) { + return String::Format("chainLift:%d", variant); + } + + virtual void ApplyTo(uint8 rideType, uint8 trackType, int variant, + rct_map_element *mapElement, rct_map_element *surfaceElement, + rct_ride *ride, rct_ride_entry *rideEntry + ) { + if (variant == 0) { + mapElement->type &= ~TRACK_ELEMENT_FLAG_CHAIN_LIFT; + } else { + mapElement->type |= TRACK_ELEMENT_FLAG_CHAIN_LIFT; + } + } +}; + +class InvertedFilter : public ITestTrackFilter { +public: + bool AppliesTo(uint8 rideType, uint8 trackType) { + if (rideType == RIDE_TYPE_MULTI_DIMENSION_ROLLER_COASTER || + rideType == RIDE_TYPE_FLYING_ROLLER_COASTER || + rideType == RIDE_TYPE_LAY_DOWN_ROLLER_COASTER) { + return true; + } + + return false; + } + + int Variations(uint8 rideType, uint8 trackType) { + return 2; + } + + std::string VariantName(uint8 rideType, uint8 trackType, int variant) { + return String::Format("inverted:%d", variant); + } + + virtual void ApplyTo(uint8 rideType, uint8 trackType, int variant, + rct_map_element *mapElement, rct_map_element *surfaceElement, + rct_ride *ride, rct_ride_entry *rideEntry + ) { + if (variant == 0) { + mapElement->properties.track.colour &= ~TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + } else { + mapElement->properties.track.colour |= TRACK_ELEMENT_COLOUR_FLAG_INVERTED; + } + } +}; + +class EntranceStyleFilter : public ITestTrackFilter { +public: + bool AppliesTo(uint8 rideType, uint8 trackType) { + if (trackType == TRACK_ELEM_BEGIN_STATION || + trackType == TRACK_ELEM_MIDDLE_STATION || + trackType == TRACK_ELEM_END_STATION) { + return true; + } + + return false; + } + + int Variations(uint8 rideType, uint8 trackType) { + return RIDE_ENTRANCE_STYLE_COUNT - 1; + } + + std::string VariantName(uint8 rideType, uint8 trackType, int variant) { + return String::Format("entranceStyle:%d", variant); + } + + virtual void ApplyTo(uint8 rideType, uint8 trackType, int variant, + rct_map_element *mapElement, rct_map_element *surfaceElement, + rct_ride *ride, rct_ride_entry *rideEntry + ) { + ride->entrance_style = variant; + } +}; + + + +static void CallOriginal( + uint8 rideType, + uint8 trackType, + uint8 direction, + uint8 trackSequence, + uint16 height, + rct_map_element *mapElement +) { + uint32 *trackDirectionList = (uint32 *) RideTypeTrackPaintFunctionsOld[rideType][trackType]; + const uint8 rideIndex = 0; + + // 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 + ); +} + +static void CallNew( + uint8 rideType, + uint8 trackType, + uint8 direction, + uint8 trackSequence, + uint16 height, + rct_map_element *mapElement +) { + TRACK_PAINT_FUNCTION_GETTER newPaintFunctionGetter = RideTypeTrackPaintFunctions[rideType]; + TRACK_PAINT_FUNCTION newPaintFunction = newPaintFunctionGetter(trackType, direction); + + newPaintFunction(0, trackSequence, direction, height, mapElement); +} + +typedef uint8 (*TestFunction)(uint8, uint8, uint8, std::string *); + +static uint8 TestTrackElementPaintCalls(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error); + +static uint8 TestTrackElementSegmentSupportHeight(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error); + +static uint8 TestTrackElementGeneralSupportHeight(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error); + +static uint8 TestTrackElementSideTunnels(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error); + +static uint8 TestTrackElementVerticalTunnels(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error); + +uint8 TestTrack::TestPaintTrackElement(uint8 rideType, uint8 trackType, std::string *out) { + if (!Utils::rideSupportsTrackType(rideType, trackType)) { + return TEST_FAILED; + } + + if (rideType == RIDE_TYPE_CHAIRLIFT) { + if (trackType == TRACK_ELEM_BEGIN_STATION || trackType == TRACK_ELEM_MIDDLE_STATION || + trackType == TRACK_ELEM_END_STATION) { + // These rides check neighbouring tiles for tracks + return TEST_SKIPPED; + } + } + + int sequenceCount = Utils::getTrackSequenceCount(rideType, trackType); + std::string error = String::Format("rct2: 0x%08X\n", RideTypeTrackPaintFunctionsOld[rideType][trackType]); + + uint8 retVal = TEST_SUCCESS; + + static TestFunction functions[] = { + TestTrackElementPaintCalls, + TestTrackElementSegmentSupportHeight, + TestTrackElementGeneralSupportHeight, + TestTrackElementSideTunnels, + TestTrackElementVerticalTunnels, + }; + + for (int trackSequence = 0; trackSequence < sequenceCount; trackSequence++) { + for (auto &&function : functions) { + retVal = function(rideType, trackType, trackSequence, &error); + + if (retVal != TEST_SUCCESS) { + *out += error + "\n"; + return retVal; + } + } + } + + return retVal; +} + +static uint8 TestTrackElementPaintCalls(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error) { + uint8 rideIndex = 0; + uint16 height = 3 * 16; + + rct_map_element mapElement = {0}; + mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; + mapElement.properties.track.type = trackType; + mapElement.base_height = height / 16; + g_currently_drawn_item = &mapElement; + + rct_map_element surfaceElement = {0}; + surfaceElement.type = MAP_ELEMENT_TYPE_SURFACE; + surfaceElement.base_height = 2; + gSurfaceElement = &surfaceElement; + gDidPassSurface = true; + + TestPaint::ResetEnvironment(); + TestPaint::ResetTunnels(); + + function_call callBuffer[256] = {0}; + int callCount = 0; + + // TODO: test supports + // TODO: test flat rides + // TODO: test mazes + // TODO: test underground (Wooden RC) + // TODO: test station fences + + std::vector filters; + filters.push_back(new CableLiftFilter()); + filters.push_back(new ChainLiftFilter()); + filters.push_back(new InvertedFilter()); + filters.push_back(new EntranceStyleFilter()); + + std::vector activeFilters; + + for (auto &&filter : filters) { + if (filter->AppliesTo(rideType, trackType)) { + activeFilters.push_back(filter); + } + } + + // Add an element so there's always something to add to + std::vector filler; + filler.push_back(0); + + std::vector> argumentPermutations; + argumentPermutations.push_back(filler); + for (size_t filterIndex = 0; filterIndex < activeFilters.size(); ++filterIndex) { + ITestTrackFilter *filter = activeFilters[filterIndex]; + uint8 variantCount = filter->Variations(rideType, trackType); + + std::vector> newArgumentPermutations; + for (int variant = 0; variant < variantCount; variant++) { + for (auto &&oldPermutation : argumentPermutations) { + std::vector permutation; + permutation.insert(permutation.begin(), oldPermutation.begin(), oldPermutation.end()); + permutation.push_back(variant); + newArgumentPermutations.push_back(permutation); + } + } + + argumentPermutations.clear(); + argumentPermutations.insert(argumentPermutations.begin(), newArgumentPermutations.begin(), + newArgumentPermutations.end()); + } + + for (auto &&arguments : argumentPermutations) { + std::string baseCaseName = "["; + + for (size_t filterIndex = 0; filterIndex < activeFilters.size(); ++filterIndex) { + uint8 &variant = arguments[1 + filterIndex]; + baseCaseName += activeFilters[filterIndex]->VariantName(rideType, trackType, variant); + baseCaseName += " "; + + activeFilters[filterIndex]->ApplyTo(rideType, trackType, variant, &mapElement, &surfaceElement, &(gRideList[0]), gRideEntries[0]); + } + + + for (int currentRotation = 0; currentRotation < 4; currentRotation++) { + gCurrentRotation = currentRotation; + for (int direction = 0; direction < 4; direction++) { + RCT2_GLOBAL(0x009DE56A, sint16) = 64; // x + RCT2_GLOBAL(0x009DE56E, sint16) = 64; // y + + std::string caseName = String::Format( + "%srotation:%d direction:%d trackSequence:%d]", + baseCaseName.c_str(), currentRotation, direction, trackSequence + ); + + PaintIntercept::ClearCalls(); + TestPaint::ResetSupportHeights(); + + CallOriginal(rideType, trackType, direction, trackSequence, height, &mapElement); + + callCount = PaintIntercept::GetCalls(callBuffer); + std::vector oldCalls; + oldCalls.insert(oldCalls.begin(), callBuffer, callBuffer + callCount); + + PaintIntercept::ClearCalls(); + testpaint_clear_ignore(); + TestPaint::ResetSupportHeights(); + + CallNew(rideType, trackType, direction, trackSequence, height, &mapElement); + + if (testpaint_is_ignored(direction, trackSequence)) { + *error += String::Format("[ IGNORED ] %s\n", caseName.c_str()); + continue; + } + + callCount = PaintIntercept::GetCalls(callBuffer); + std::vector newCalls; + newCalls.insert(newCalls.begin(), callBuffer, callBuffer + callCount); + + bool sucess = true; + if (oldCalls.size() != newCalls.size()) { + *error += String::Format( + "Call counts don't match (was %d, expected %d). %s\n", + newCalls.size(), oldCalls.size(), caseName.c_str() + ); + sucess = false; + } else if (!FunctionCall::AssertsEquals(oldCalls, newCalls)) { + *error += String::Format("Calls don't match. %s\n", caseName.c_str()); + sucess = false; + } + + if (!sucess) { + *error += " Expected:\n"; + *error += Printer::PrintFunctionCalls(oldCalls, height); + *error += " Actual:\n"; + *error += Printer::PrintFunctionCalls(newCalls, height); + + return TEST_FAILED; + } + } + } + } + + return TEST_SUCCESS; +} + +static uint8 TestTrackElementSegmentSupportHeight(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error) { + uint8 rideIndex = 0; + uint16 height = 3 * 16; + + rct_map_element mapElement = {0}; + mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; + mapElement.properties.track.type = trackType; + mapElement.base_height = height / 16; + g_currently_drawn_item = &mapElement; + + rct_map_element surfaceElement = {0}; + surfaceElement.type = MAP_ELEMENT_TYPE_SURFACE; + surfaceElement.base_height = 2; + gSurfaceElement = &surfaceElement; + gDidPassSurface = true; + + TestPaint::ResetEnvironment(); + TestPaint::ResetTunnels(); + + // TODO: Test Chainlift + // TODO: Test Maze + // TODO: Allow skip + + std::string state = String::Format("[trackSequence:%d chainLift:%d]", trackSequence, 0); + + std::vector tileSegmentSupportCalls[4]; + + for (int direction = 0; direction < 4; direction++) { + TestPaint::ResetSupportHeights(); + + CallOriginal(rideType, trackType, direction, trackSequence, height, &mapElement); + + tileSegmentSupportCalls[direction] = SegmentSupportHeightCall::getSegmentCalls(gSupportSegments, direction); + } + + std::vector referenceCalls = tileSegmentSupportCalls[0]; + + if (!SegmentSupportHeightCall::CallsMatch(tileSegmentSupportCalls)) { + bool success = SegmentSupportHeightCall::FindMostCommonSupportCall(tileSegmentSupportCalls, &referenceCalls); + if (!success) { + *error += String::Format("Original segment calls didn't match. %s\n", state.c_str()); + for (int direction = 0; direction < 4; direction++) { + *error += String::Format("# %d\n", direction); + *error += Printer::PrintSegmentSupportHeightCalls(tileSegmentSupportCalls[direction]); + } + return TEST_FAILED; + } + } + + for (int direction = 0; direction < 4; direction++) { + TestPaint::ResetSupportHeights(); + + testpaint_clear_ignore(); + CallNew(rideType, trackType, direction, trackSequence, height, &mapElement); + if (testpaint_is_ignored(direction, trackSequence)) { + continue; + } + + std::vector newCalls = SegmentSupportHeightCall::getSegmentCalls(gSupportSegments, + direction); + if (!SegmentSupportHeightCall::CallsEqual(referenceCalls, newCalls)) { + *error += String::Format( + "Segment support heights didn't match. [direction:%d] %s\n", + direction, state.c_str() + ); + *error += " Expected:\n"; + *error += Printer::PrintSegmentSupportHeightCalls(referenceCalls); + *error += " Actual:\n"; + *error += Printer::PrintSegmentSupportHeightCalls(newCalls); + + return TEST_FAILED; + } + } + + return TEST_SUCCESS; +} + +static uint8 TestTrackElementGeneralSupportHeight(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error) { + uint8 rideIndex = 0; + uint16 height = 3 * 16; + + rct_map_element mapElement = {0}; + mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; + mapElement.properties.track.type = trackType; + mapElement.base_height = height / 16; + g_currently_drawn_item = &mapElement; + + rct_map_element surfaceElement = {0}; + surfaceElement.type = MAP_ELEMENT_TYPE_SURFACE; + surfaceElement.base_height = 2; + gSurfaceElement = &surfaceElement; + gDidPassSurface = true; + + TestPaint::ResetEnvironment(); + TestPaint::ResetTunnels(); + + // TODO: Test Chainlift + // TODO: Test Maze + // TODO: Allow skip + + std::string state = String::Format("[trackSequence:%d chainLift:%d]", trackSequence, 0); + + SupportCall tileGeneralSupportCalls[4]; + for (int direction = 0; direction < 4; direction++) { + TestPaint::ResetSupportHeights(); + + CallOriginal(rideType, trackType, direction, trackSequence, height, &mapElement); + + tileGeneralSupportCalls[direction].height = -1; + tileGeneralSupportCalls[direction].slope = -1; + if (gSupport.height != 0) { + tileGeneralSupportCalls[direction].height = gSupport.height; + } + if (gSupport.slope != 0xFF) { + tileGeneralSupportCalls[direction].slope = gSupport.slope; + } + } + + SupportCall referenceCall = tileGeneralSupportCalls[0]; + if (!GeneralSupportHeightCall::CallsMatch(tileGeneralSupportCalls)) { + bool success = GeneralSupportHeightCall::FindMostCommonSupportCall(tileGeneralSupportCalls, &referenceCall); + if (!success) { + *error += String::Format("Original support calls didn't match. %s\n", state.c_str()); + for (int i = 0; i < 4; ++i) { + *error += String::Format("[%d, 0x%02X] ", tileGeneralSupportCalls[i].height, tileGeneralSupportCalls[i].slope); + } + *error += "\n"; + return TEST_FAILED; + } + } + + for (int direction = 0; direction < 4; direction++) { + TestPaint::ResetSupportHeights(); + + testpaint_clear_ignore(); + CallNew(rideType, trackType, direction, trackSequence, height, &mapElement); + if (testpaint_is_ignored(direction, trackSequence)) { + continue; + } + + + if (referenceCall.height != -1) { + if (gSupport.height != referenceCall.height) { + *error += String::Format( + "General support heights didn't match. (expected height + %d, actual: height + %d) [direction:%d] %s\n", + referenceCall.height - height, + gSupport.height - height, + direction, + state.c_str() + ); + return TEST_FAILED; + } + } + if (referenceCall.slope != -1) { + if (gSupport.slope != referenceCall.slope) { + *error += String::Format( + "General support slopes didn't match. (expected 0x%02X, actual: 0x%02X) [direction:%d] %s\n", + referenceCall.slope, + gSupport.slope, + direction, + state.c_str() + ); + return TEST_FAILED; + } + } + } + + return TEST_SUCCESS; +} + +static uint8 TestTrackElementSideTunnels(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error) { + uint8 rideIndex = 0; + uint16 height = 3 * 16; + + rct_map_element mapElement = {0}; + mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; + mapElement.properties.track.type = trackType; + mapElement.base_height = height / 16; + g_currently_drawn_item = &mapElement; + + rct_map_element surfaceElement = {0}; + surfaceElement.type = MAP_ELEMENT_TYPE_SURFACE; + surfaceElement.base_height = 2; + gSurfaceElement = &surfaceElement; + gDidPassSurface = true; + + TestPaint::ResetEnvironment(); + TestPaint::ResetTunnels(); + + TunnelCall tileTunnelCalls[4][4]; + + for (int direction = 0; direction < 4; direction++) { + TestPaint::ResetTunnels(); + + for (sint8 offset = -8; offset <= 8; offset += 8) { + CallOriginal(rideType, trackType, direction, trackSequence, height + offset, &mapElement); + } + + uint8 rightIndex = (4 - direction) % 4; + uint8 leftIndex = (rightIndex + 1) % 4; + + for (int i = 0; i < 4; ++i) { + tileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED; + } + + bool err = false; + tileTunnelCalls[direction][rightIndex] = SideTunnelCall::ExtractTunnelCalls(gRightTunnels, gRightTunnelCount, height, + &err); + + tileTunnelCalls[direction][leftIndex] = SideTunnelCall::ExtractTunnelCalls(gLeftTunnels, gLeftTunnelCount, height, + &err); + + if (err) { + *error += "Multiple tunnels on one side aren't supported.\n"; + return TEST_FAILED; + } + } + + TunnelCall newTileTunnelCalls[4][4]; + for (int direction = 0; direction < 4; direction++) { + TestPaint::ResetTunnels(); + + testpaint_clear_ignore(); + + for (sint8 offset = -8; offset <= 8; offset += 8) { + // TODO: move tunnel pushing to interface so we don't have to check the output 3 times + CallNew(rideType, trackType, direction, trackSequence, height + offset, &mapElement); + } + + uint8 rightIndex = (4 - direction) % 4; + uint8 leftIndex = (rightIndex + 1) % 4; + + for (int i = 0; i < 4; ++i) { + newTileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED; + } + + bool err = false; + newTileTunnelCalls[direction][rightIndex] = SideTunnelCall::ExtractTunnelCalls(gRightTunnels, gRightTunnelCount, height, + &err); + + newTileTunnelCalls[direction][leftIndex] = SideTunnelCall::ExtractTunnelCalls(gLeftTunnels, gLeftTunnelCount, height, + &err); + + if (err) { + *error += "Multiple tunnels on one side aren't supported.\n"; + return TEST_FAILED; + } + } + + + if (!SideTunnelCall::TunnelCallsLineUp(tileTunnelCalls)) { + *error += String::Format( + "Original tunnel calls don\'t line up. Skipping tunnel validation [trackSequence:%d].\n", + trackSequence + ); + *error += Printer::PrintSideTunnelCalls(tileTunnelCalls); + + if (!SideTunnelCall::TunnelCallsLineUp(newTileTunnelCalls)) { + *error += String::Format("Decompiled tunnel calls don\'t line up. [trackSequence:%d].\n", trackSequence); + *error += Printer::PrintSideTunnelCalls(newTileTunnelCalls); + return TEST_FAILED; + } + + return TEST_SUCCESS; + } + + TunnelCall referencePattern[4]; + SideTunnelCall::GetTunnelCallReferencePattern(tileTunnelCalls, &referencePattern); + + TunnelCall actualPattern[4]; + SideTunnelCall::GetTunnelCallReferencePattern(newTileTunnelCalls, &actualPattern); + + if (!SideTunnelCall::TunnelPatternsMatch(referencePattern, actualPattern)) { + *error += String::Format("Tunnel calls don't match expected pattern. [trackSequence:%d]\n", trackSequence); + *error += " Expected:\n"; + *error += Printer::PrintSideTunnelCalls(tileTunnelCalls); + *error += " Actual:\n"; + *error += Printer::PrintSideTunnelCalls(newTileTunnelCalls); + return TEST_FAILED; + } + + return TEST_SUCCESS; +} + +static uint8 TestTrackElementVerticalTunnels(uint8 rideType, uint8 trackType, uint8 trackSequence, std::string *error) { + uint8 rideIndex = 0; + uint16 height = 3 * 16; + + rct_map_element mapElement = {0}; + mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; + mapElement.properties.track.type = trackType; + mapElement.base_height = height / 16; + g_currently_drawn_item = &mapElement; + + rct_map_element surfaceElement = {0}; + surfaceElement.type = MAP_ELEMENT_TYPE_SURFACE; + surfaceElement.base_height = 2; + gSurfaceElement = &surfaceElement; + gDidPassSurface = true; + + TestPaint::ResetEnvironment(); + TestPaint::ResetTunnels(); + + uint8 verticalTunnelHeight[4]; + + for (int direction = 0; direction < 4; direction++) { + gVerticalTunnelHeight = 0; + CallOriginal(rideType, trackType, direction, trackSequence, height, &mapElement); + verticalTunnelHeight[direction] = gVerticalTunnelHeight; + } + + if (!VerticalTunnelCall::HeightIsConsistent(verticalTunnelHeight)) { + *error += String::Format( + "Original vertical tunnel height is inconsistent, skipping test. [trackSequence:%d]\n", + trackSequence + ); + return TEST_SUCCESS; + } + + uint8 referenceHeight = verticalTunnelHeight[0]; + + for (int direction = 0; direction < 4; direction++) { + gVerticalTunnelHeight = 0; + + testpaint_clear_ignore(); + CallOriginal(rideType, trackType, direction, trackSequence, height, &mapElement); + if (testpaint_is_ignored(direction, trackSequence)) { + continue; + } + + if (gVerticalTunnelHeight != referenceHeight) { + if (gVerticalTunnelHeight == 0) { + *error += String::Format( + "Expected no tunnel. Actual: %d [trackSequence:%d]\n", + gVerticalTunnelHeight, trackSequence + ); + return TEST_FAILED; + } + + *error += String::Format( + "Expected vertical tunnel height to be `%s`, was `%s`. [trackSequence:%d direction:%d]\n", + Printer::PrintHeightOffset((referenceHeight * 16), height).c_str(), + Printer::PrintHeightOffset((gVerticalTunnelHeight * 16), height).c_str(), + trackSequence, + direction + ); + + return TEST_FAILED; + } + } + + return TEST_SUCCESS; +} diff --git a/test/testpaint/TestTrack.hpp b/test/testpaint/TestTrack.hpp new file mode 100644 index 0000000000..811cb2cfe3 --- /dev/null +++ b/test/testpaint/TestTrack.hpp @@ -0,0 +1,26 @@ +#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 + +#pragma once + +#include + +#include "../../src/common.h" + +class TestTrack { +public: + static uint8 TestPaintTrackElement(uint8 rideType, uint8 trackType, std::string *out); +}; diff --git a/test/testpaint/Utils.cpp b/test/testpaint/Utils.cpp new file mode 100644 index 0000000000..344a408b3d --- /dev/null +++ b/test/testpaint/Utils.cpp @@ -0,0 +1,69 @@ +#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 "Utils.hpp" + +extern "C" { + #include "../../src/ride/ride.h" + #include "../../src/ride/track.h" + #include "../../src/ride/track_data.h" +} + +namespace Utils { + int getTrackSequenceCount(uint8 rideType, uint8 trackType) { + int sequenceCount = 0; + const rct_preview_track **trackBlocks; + + if (ride_type_has_flag(rideType, RIDE_TYPE_FLAG_FLAT_RIDE)) { + trackBlocks = FlatRideTrackBlocks; + } else { + trackBlocks = TrackBlocks; + } + + for (int i = 0; i < 256; i++) { + if (trackBlocks[trackType][i].index == 0xFF) { + break; + } + + sequenceCount++; + } + + return sequenceCount; + } + + bool rideSupportsTrackType(uint8 rideType, uint8 trackType) { + TRACK_PAINT_FUNCTION_GETTER newPaintGetter = RideTypeTrackPaintFunctions[rideType]; + + if (newPaintGetter == NULL) { + return false; + } + + if (newPaintGetter(trackType, 0) == NULL) { + return false; + } + + if (RideTypeTrackPaintFunctionsOld[rideType][trackType] == 0) { + return false; + } + + return true; + } + + bool rideIsImplemented(uint8 rideType) { + TRACK_PAINT_FUNCTION_GETTER newPaintGetter = RideTypeTrackPaintFunctions[rideType]; + return (newPaintGetter != 0); + } +} diff --git a/test/testpaint/Utils.hpp b/test/testpaint/Utils.hpp new file mode 100644 index 0000000000..9ced7e4a2c --- /dev/null +++ b/test/testpaint/Utils.hpp @@ -0,0 +1,25 @@ +#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 + +#pragma once + +#include "../../src/common.h" + +namespace Utils { + int getTrackSequenceCount(uint8 rideType, uint8 trackType); + bool rideSupportsTrackType(uint8 rideType, uint8 trackType); + bool rideIsImplemented(uint8 rideType); +} diff --git a/test/testpaint/VerticalTunnelCall.cpp b/test/testpaint/VerticalTunnelCall.cpp new file mode 100644 index 0000000000..e989febe4b --- /dev/null +++ b/test/testpaint/VerticalTunnelCall.cpp @@ -0,0 +1,25 @@ +#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 "VerticalTunnelCall.hpp" + +bool VerticalTunnelCall::HeightIsConsistent(uint8 heights[4]) { + for (int i = 1; i < 4; ++i) { + if (heights[i] != heights[0]) return false; + } + + return true; +} diff --git a/test/testpaint/VerticalTunnelCall.hpp b/test/testpaint/VerticalTunnelCall.hpp new file mode 100644 index 0000000000..b72c4cd41d --- /dev/null +++ b/test/testpaint/VerticalTunnelCall.hpp @@ -0,0 +1,23 @@ +#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 + +#pragma once + +#include "../../src/common.h" + +namespace VerticalTunnelCall { + bool HeightIsConsistent(uint8 heights[4]); +}; diff --git a/test/testpaint/generate.cpp b/test/testpaint/generate.cpp index 417774d751..e3be70c09d 100644 --- a/test/testpaint/generate.cpp +++ b/test/testpaint/generate.cpp @@ -18,7 +18,12 @@ #include #include -#include "intercept.h" +#include "FunctionCall.hpp" +#include "PaintIntercept.hpp" +#include "SegmentSupportHeightCall.hpp" +#include "SideTunnelCall.hpp" +#include "String.hpp" +#include "Utils.hpp" extern "C" { @@ -172,7 +177,7 @@ private: void GenerateTrackFunctionBody(int tabs, int trackType) { - int numSequences = getTrackSequenceCount(_rideType, trackType); + int numSequences = Utils::getTrackSequenceCount(_rideType, trackType); if (numSequences > 1) { WriteLine(tabs, "switch (trackSequence) {"); @@ -397,9 +402,9 @@ private: bool blockSegmentsBeforeSupports = false; std::vector calls[4], chainLiftCalls[4], cableLiftCalls[4]; - Intercept2::TunnelCall tileTunnelCalls[4][4]; + TunnelCall tileTunnelCalls[4][4]; sint16 verticalTunnelHeights[4]; - std::vector segmentSupportCalls[4]; + std::vector segmentSupportCalls[4]; support_height generalSupports[4] = { 0 }; for (int direction = 0; direction < 4; direction++) { rct_map_element mapElement = { 0 }; @@ -417,9 +422,9 @@ private: RCT2_GLOBAL(0x009DE56E, sint16) = 64; function_call callBuffer[256] = { 0 }; - intercept_clear_calls(); + PaintIntercept::ClearCalls(); CallOriginal(trackType, direction, trackSequence, height, &mapElement); - int numCalls = intercept_get_calls(callBuffer); + int numCalls = PaintIntercept::GetCalls(callBuffer); calls[direction].insert(calls[direction].begin(), callBuffer, callBuffer + numCalls); for (auto &&call : calls[direction]) { @@ -429,7 +434,7 @@ private: } } - segmentSupportCalls[direction] = Intercept2::getSegmentCalls(gSupportSegments, direction); + segmentSupportCalls[direction] = SegmentSupportHeightCall::getSegmentCalls(gSupportSegments, direction); generalSupports[direction] = gSupport; if (gSupport.slope != 0xFF && gSupport.height != 0) { @@ -438,9 +443,9 @@ private: // Get chain lift calls mapElement.type |= 0x80; - intercept_clear_calls(); + PaintIntercept::ClearCalls(); CallOriginal(trackType, direction, trackSequence, height, &mapElement); - numCalls = intercept_get_calls(callBuffer); + numCalls = PaintIntercept::GetCalls(callBuffer); chainLiftCalls[direction].insert(chainLiftCalls[direction].begin(), callBuffer, callBuffer + numCalls); // Get cable lift calls (giga coaster only) @@ -448,9 +453,9 @@ private: { mapElement.type = 0; mapElement.properties.track.colour |= TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; - intercept_clear_calls(); + PaintIntercept::ClearCalls(); CallOriginal(trackType, direction, trackSequence, height, &mapElement); - numCalls = intercept_get_calls(callBuffer); + numCalls = PaintIntercept::GetCalls(callBuffer); cableLiftCalls[direction].insert(cableLiftCalls[direction].begin(), callBuffer, callBuffer + numCalls); } @@ -461,9 +466,9 @@ private: RCT2_GLOBAL(0x009DE56E, sint16) = 64; mapElement.type = 0; mapElement.properties.track.colour &= ~TRACK_ELEMENT_COLOUR_FLAG_CABLE_LIFT; - intercept_clear_calls(); + PaintIntercept::ClearCalls(); CallOriginal(trackType, direction, trackSequence, height, &mapElement); - numCalls = intercept_get_calls(callBuffer); + numCalls = PaintIntercept::GetCalls(callBuffer); std::vector checkCalls = std::vector(callBuffer, callBuffer + numCalls); if (!CompareFunctionCalls(checkCalls, calls[direction])) { @@ -630,17 +635,17 @@ private: { const char * funcName = GetFunctionCallName(call.function); std::string imageId = GetImageIdString(call.paint.image_id); - std::string s = StringFormat("%s_rotated(direction, %s, ", funcName, imageId.c_str()); + std::string s = String::Format("%s_rotated(direction, %s, ", funcName, imageId.c_str()); s += FormatXYSwap(call.paint.offset.x, call.paint.offset.y, direction); s += ", "; s += FormatXYSwap(call.paint.bound_box_length.x, call.paint.bound_box_length.y, direction); - s += StringFormat(", %d, height%s", call.paint.bound_box_length.z, GetOffsetExpressionString(call.paint.z_offset - height).c_str()); + s += String::Format(", %d, height%s", call.paint.bound_box_length.z, GetOffsetExpressionString(call.paint.z_offset - height).c_str()); if (call.function != PAINT_98196C) { s += ", "; s += FormatXYSwap(call.paint.bound_box_offset.x, call.paint.bound_box_offset.y, direction); - s += StringFormat(", height%s", GetOffsetExpressionString(call.paint.bound_box_offset.z - height).c_str()); + s += String::Format(", height%s", GetOffsetExpressionString(call.paint.bound_box_offset.z - height).c_str()); } s += ");"; @@ -651,11 +656,11 @@ private: { if (direction & 1) { - return StringFormat("%d, %d", y, x); + return String::Format("%d, %d", y, x); } else { - return StringFormat("%d, %d", x, y); + return String::Format("%d, %d", x, y); } } @@ -711,7 +716,7 @@ private: bool CompareFunctionCall(const function_call a, const function_call &b) { - return assertFunctionCallEquals(a, b); + return FunctionCall::AssertsEquals(a, b); } const char * GetFunctionCallName(int function) @@ -734,11 +739,11 @@ private: int trackSequence, int height, rct_map_element * mapElement, - Intercept2::TunnelCall tileTunnelCalls[4][4], + TunnelCall tileTunnelCalls[4][4], sint16 verticalTunnelHeights[4]) { - gLeftTunnelCount = 0; - gRightTunnelCount = 0; + TestPaint::ResetTunnels(); + for (int offset = -8; offset <= 8; offset += 8) { CallOriginal(trackType, direction, trackSequence, height + offset, mapElement); @@ -748,13 +753,13 @@ private: uint8 leftIndex = (rightIndex + 1) % 4; for (int i = 0; i < 4; ++i) { - tileTunnelCalls[direction][i].call = Intercept2::TUNNELCALL_SKIPPED; + tileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED; } if (gRightTunnelCount == 0) { - tileTunnelCalls[direction][rightIndex].call = Intercept2::TUNNELCALL_NONE; + tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_NONE; } else if (gRightTunnelCount == 3) { - tileTunnelCalls[direction][rightIndex].call = Intercept2::TUNNELCALL_CALL; - tileTunnelCalls[direction][rightIndex].offset = Intercept2::getTunnelOffset(height, gRightTunnels); + tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_CALL; + tileTunnelCalls[direction][rightIndex].offset = SideTunnelCall::GetTunnelOffset(height, gRightTunnels); tileTunnelCalls[direction][rightIndex].type = gRightTunnels[0].type; } else { printf("Multiple tunnels on one side aren't supported.\n"); @@ -762,10 +767,10 @@ private: } if (gLeftTunnelCount == 0) { - tileTunnelCalls[direction][leftIndex].call = Intercept2::TUNNELCALL_NONE; + tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_NONE; } else if (gLeftTunnelCount == 3) { - tileTunnelCalls[direction][leftIndex].call = Intercept2::TUNNELCALL_CALL; - tileTunnelCalls[direction][leftIndex].offset = Intercept2::getTunnelOffset(height, gLeftTunnels); + tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_CALL; + tileTunnelCalls[direction][leftIndex].offset = SideTunnelCall::GetTunnelOffset(height, gLeftTunnels); tileTunnelCalls[direction][leftIndex].type = gLeftTunnels[0].type; } else { printf("Multiple tunnels on one side aren't supported.\n"); @@ -785,7 +790,7 @@ private: return true; } - void GenerateTunnelCall(int tabs, Intercept2::TunnelCall tileTunnelCalls[4][4], sint16 verticalTunnelHeights[4]) + void GenerateTunnelCall(int tabs, TunnelCall tileTunnelCalls[4][4], sint16 verticalTunnelHeights[4]) { constexpr uint8 TunnelLeft = 0; constexpr uint8 TunnelRight = 1; @@ -806,7 +811,7 @@ private: for (int side = 0; side < 4; side++) { auto tunnel = tileTunnelCalls[direction][side]; - if (tunnel.call == Intercept2::TUNNELCALL_CALL) + if (tunnel.call == TUNNELCALL_CALL) { tunnelOffset[direction] = tunnel.offset; tunnelType[direction] = tunnel.type; @@ -854,7 +859,7 @@ private: WriteLine(tabs, "case %d:", i); for (int side = 0; side < 4; side++) { - if (tileTunnelCalls[i][side].call == Intercept2::TUNNELCALL_CALL) + if (tileTunnelCalls[i][side].call == TUNNELCALL_CALL) { GenerateTunnelCall(tabs + 1, tileTunnelCalls[i][side].offset, tileTunnelCalls[i][side].type, dsToWay[i][side]); } @@ -892,7 +897,7 @@ private: WriteLine(tabs, "paint_util_push_tunnel_rotated(direction, height%s, TUNNEL_%d);", GetOffsetExpressionString(offset).c_str(), type); } - void GenerateSegmentSupportCall(int tabs, std::vector segmentSupportCalls[4]) + void GenerateSegmentSupportCall(int tabs, std::vector segmentSupportCalls[4]) { for (size_t i = 0; i < segmentSupportCalls[0].size(); i++) { @@ -912,12 +917,12 @@ private: if (ssh.height == 0xFFFF) { szCall += "0xFFFF"; - szCall += StringFormat(", 0);", ssh.slope); + szCall += String::Format(", 0);", ssh.slope); } else { szCall += std::to_string(ssh.height); - szCall += StringFormat(", 0x%02X);", ssh.slope); + szCall += String::Format(", 0x%02X);", ssh.slope); } WriteLine(tabs, szCall); } @@ -948,20 +953,20 @@ private: uint32 palette = imageId & ~0x7FFFF; std::string paletteName; - if (palette == Intercept2::DEFAULT_SCHEME_TRACK) paletteName = "gTrackColours[SCHEME_TRACK]"; - else if (palette == Intercept2::DEFAULT_SCHEME_SUPPORTS) paletteName = "gTrackColours[SCHEME_SUPPORTS]"; - else if (palette == Intercept2::DEFAULT_SCHEME_MISC) paletteName = "gTrackColours[SCHEME_MISC]"; - else if (palette == Intercept2::DEFAULT_SCHEME_3) paletteName = "gTrackColours[SCHEME_3]"; + if (palette == TestPaint::DEFAULT_SCHEME_TRACK) paletteName = "gTrackColours[SCHEME_TRACK]"; + else if (palette == TestPaint::DEFAULT_SCHEME_SUPPORTS) paletteName = "gTrackColours[SCHEME_SUPPORTS]"; + else if (palette == TestPaint::DEFAULT_SCHEME_MISC) paletteName = "gTrackColours[SCHEME_MISC]"; + else if (palette == TestPaint::DEFAULT_SCHEME_3) paletteName = "gTrackColours[SCHEME_3]"; else { - paletteName = StringFormat("0x%08X", palette); + paletteName = String::Format("0x%08X", palette); } if (image == 0) { result = paletteName; } else if (image & 0x70000) { - result = StringFormat("%s | vehicle.base_image_id + %d", paletteName.c_str(), image & ~0x70000); + result = String::Format("%s | vehicle.base_image_id + %d", paletteName.c_str(), image & ~0x70000); } else { - result = StringFormat("%s | %d", paletteName.c_str(), image); + result = String::Format("%s | %d", paletteName.c_str(), image); } return result; } @@ -982,7 +987,7 @@ private: if (segmentsPrinted > 0) { s += " | "; } - s += StringFormat("SEGMENT_%02X", 0xB4 + 4 * i); + s += String::Format("SEGMENT_%02X", 0xB4 + 4 * i); segmentsPrinted++; } } @@ -1004,35 +1009,8 @@ private: void CallOriginal(int trackType, int direction, int trackSequence, int height, rct_map_element *mapElement) { - gPaintInteractionType = VIEWPORT_INTERACTION_ITEM_RIDE; - gTrackColours[SCHEME_TRACK] = Intercept2::DEFAULT_SCHEME_TRACK; - gTrackColours[SCHEME_SUPPORTS] = Intercept2::DEFAULT_SCHEME_SUPPORTS; - gTrackColours[SCHEME_MISC] = Intercept2::DEFAULT_SCHEME_MISC; - gTrackColours[SCHEME_3] = Intercept2::DEFAULT_SCHEME_3; - - rct_drawpixelinfo dpi = { 0 }; - dpi.zoom_level = 1; - unk_140E9A8 = &dpi; - - rct_ride ride = {0}; - - rct_ride_entry rideEntry = {0}; - rct_ride_entry_vehicle vehicleEntry { 0 }; - vehicleEntry.base_image_id = 0x70000; - rideEntry.vehicles[0] = vehicleEntry; - - gRideList[0] = ride; - gRideEntries[0] = &rideEntry; - - for (int s = 0; s < 9; ++s) - { - gSupportSegments[s].height = 0; - gSupportSegments[s].slope = 0xFF; - } - - gSupport.height = 0; - gSupport.slope = 0xFF; - g141E9DB = G141E9DB_FLAG_1 | G141E9DB_FLAG_2; + TestPaint::ResetEnvironment(); + TestPaint::ResetSupportHeights(); uint32 *trackDirectionList = (uint32 *)RideTypeTrackPaintFunctionsOld[_rideType][trackType]; // Have to call from this point as it pushes esi and expects callee to pop it @@ -1120,18 +1098,6 @@ private: } fprintf(_file, "%s\n", s.c_str()); } - - static std::string StringFormat(const char * format, ...) - { - va_list args; - char buffer[512]; - - va_start(args, format); - vsnprintf(buffer, sizeof(buffer), format, args); - va_end(args); - - return std::string(buffer); - } }; extern "C" diff --git a/test/testpaint/intercept.c b/test/testpaint/intercept.c deleted file mode 100644 index 12e546b2eb..0000000000 --- a/test/testpaint/intercept.c +++ /dev/null @@ -1,820 +0,0 @@ -#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 "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" - -static const uint32 DEFAULT_SCHEME_TRACK = COLOUR_GREY << 19 | COLOUR_WHITE << 24 | 0xA0000000; -static const uint32 DEFAULT_SCHEME_SUPPORTS = COLOUR_LIGHT_BLUE << 19 | COLOUR_ICY_BLUE << 24 | 0xA0000000; -static const uint32 DEFAULT_SCHEME_MISC = COLOUR_DARK_PURPLE << 19 | COLOUR_LIGHT_PURPLE << 24 | 0xA0000000; -static const uint32 DEFAULT_SCHEME_3 = COLOUR_BRIGHT_PURPLE << 19 | COLOUR_DARK_BLUE << 24 | 0xA0000000; - -#define BLANK_SUPPORT {.height = 0, .slope = 0xFF} -static const support_height DefaultSegmentHeight[9] = { - BLANK_SUPPORT, BLANK_SUPPORT, BLANK_SUPPORT, - BLANK_SUPPORT, BLANK_SUPPORT, BLANK_SUPPORT, - BLANK_SUPPORT, BLANK_SUPPORT, BLANK_SUPPORT -}; - -extern const utf8string RideNames[91]; -extern const utf8string TrackNames[256]; -extern const utf8string FlatTrackNames[256]; - -static bool _woodenSupports; -static uint8 callCount; -static function_call calls[256]; - -void intercept_clear_calls() -{ - callCount = 0; - memset(calls, 0, sizeof(calls)); -} - -int intercept_get_calls(function_call * buffer) -{ - memcpy(buffer, calls, 256); - return callCount; -} - -bool paint_attach_to_previous_ps(uint32 image_id, uint16 x, uint16 y) { - return false; -} - -paint_struct *sub_98196C( - uint32 image_id, - sint8 x_offset, sint8 y_offset, - sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, - sint16 z_offset, - uint32 rotation -) { - function_call call = { - .function = PAINT_98196C, - .paint = { - .image_id = image_id, - .offset = {x_offset, y_offset}, - .bound_box_length = {bound_box_length_x, bound_box_length_y, bound_box_length_z}, - .z_offset = z_offset, - .rotation = rotation - }, - }; - - calls[callCount] = call; - callCount++; - - return NULL; -} - -paint_struct *sub_98197C( - uint32 image_id, - sint8 x_offset, sint8 y_offset, - sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, - sint16 z_offset, - sint16 bound_box_offset_x, sint16 bound_box_offset_y, sint16 bound_box_offset_z, - uint32 rotation -) { - function_call call = { - .function = PAINT_98197C, - .paint = { - .image_id = image_id, - .offset = {x_offset, y_offset}, - .bound_box_length = {bound_box_length_x, bound_box_length_y, bound_box_length_z}, - .bound_box_offset = {bound_box_offset_x, bound_box_offset_y, bound_box_offset_z}, - .z_offset = z_offset, - .rotation = rotation, - }, - }; - - calls[callCount] = call; - callCount++; - - return NULL; -} - -paint_struct *sub_98198C( - uint32 image_id, - sint8 x_offset, sint8 y_offset, - sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, - sint16 z_offset, - sint16 bound_box_offset_x, sint16 bound_box_offset_y, sint16 bound_box_offset_z, - uint32 rotation -) { - function_call call = { - .function = PAINT_98198C, - .paint = { - .image_id = image_id, - .offset = {x_offset, y_offset}, - .bound_box_length = {bound_box_length_x, bound_box_length_y, bound_box_length_z}, - .bound_box_offset = {bound_box_offset_x, bound_box_offset_y, bound_box_offset_z}, - .z_offset = z_offset, - .rotation = rotation, - }, - }; - - calls[callCount] = call; - callCount++; - - return NULL; -} - -paint_struct *sub_98199C( - uint32 image_id, - sint8 x_offset, sint8 y_offset, - sint16 bound_box_length_x, sint16 bound_box_length_y, sint8 bound_box_length_z, - sint16 z_offset, - sint16 bound_box_offset_x, sint16 bound_box_offset_y, sint16 bound_box_offset_z, - uint32 rotation -) { - function_call call = { - .function = PAINT_98199C, - .paint = { - .image_id = image_id, - .offset = {x_offset, y_offset}, - .bound_box_length = {bound_box_length_x, bound_box_length_y, bound_box_length_z}, - .bound_box_offset = {bound_box_offset_x, bound_box_offset_y, bound_box_offset_z}, - .z_offset = z_offset, - .rotation = rotation, - }, - }; - - calls[callCount] = call; - callCount++; - - return NULL; -} - -bool wooden_a_supports_paint_setup(int supportType, int special, int height, uint32 imageColourFlags, bool *underground) { - - function_call call = { - .function = SUPPORTS_WOOD_A, - .supports = { - .type = supportType, - .special = special, - .height = height, - .colour_flags = imageColourFlags, - } - }; - - calls[callCount] = call; - callCount++; - return _woodenSupports; -} - -bool wooden_b_supports_paint_setup(int supportType, int special, int height, uint32 imageColourFlags, bool *underground) { - - function_call call = { - .function = SUPPORTS_WOOD_B, - .supports = { - .type = supportType, - .special = special, - .height = height, - .colour_flags = imageColourFlags, - } - }; - - calls[callCount] = call; - callCount++; - return _woodenSupports; -} - -static void check_support_height() -{ - // First get last known support height state - if (memcmp(gSupportSegments, &DefaultSegmentHeight, sizeof(support_height) * 9) == 0) { - // Nothing changed - return; - } - - function_call call = { - .function = SET_SEGMENT_HEIGHT - }; - - calls[callCount] = call; - callCount++; -} - -bool metal_a_supports_paint_setup(int supportType, int segment, int special, int height, uint32 imageColourFlags) { - - check_support_height(); - - function_call call = { - .function = SUPPORTS_METAL_A, - .supports = { - .type = supportType, - .segment = segment, - .special = special, - .height = height, - .colour_flags = imageColourFlags, - } - }; - - calls[callCount] = call; - callCount++; - return false; -} - -bool metal_b_supports_paint_setup(int supportType, uint8 segment, int special, int height, uint32 imageColourFlags) { - - check_support_height(); - - function_call call = { - .function = SUPPORTS_METAL_B, - .supports = { - .type = supportType, - .segment = segment, - .special = special, - .height = height, - .colour_flags = imageColourFlags, - } - }; - - calls[callCount] = call; - callCount++; - return false; -} - -enum { - SPRITEGROUP_NONE, - - SPRITEGROUP_FENCE_METAL_A, // 14568 - SPRITEGROUP_FENCE_METAL_B, // 14990 - SPRITEGROUP_FENCE_SPIRAL_SLIDE, // 20564 - SPRITEGROUP_FLOOR_CORK, // 22134 - SPRITEGROUP_FENCE_ROPE, // 22138 - -}; - -static int getSpriteGroup(uint16 spriteIndex) { - if (spriteIndex >= 14568 && spriteIndex <= 14571) { - return SPRITEGROUP_FENCE_METAL_A; - } - - if (spriteIndex >= 14990 && spriteIndex <= 14993) { - return SPRITEGROUP_FENCE_METAL_B; - } - - if (spriteIndex >= 20564 && spriteIndex <= 20567) { - return SPRITEGROUP_FENCE_SPIRAL_SLIDE; - } - - if (spriteIndex >= 22134 && spriteIndex <= 22137) { - return SPRITEGROUP_FLOOR_CORK; - } - - if (spriteIndex >= 22138 && spriteIndex <= 22141) { - return SPRITEGROUP_FENCE_ROPE; - } - - return SPRITEGROUP_NONE; -} - -static void canonicalizeFunctionCall(function_call *call) { - if (call->function != PAINT_98197C) return; - if (call->paint.offset.x != call->paint.bound_box_offset.x) return; - if (call->paint.offset.y != call->paint.bound_box_offset.y) return; - if (call->paint.z_offset != call->paint.bound_box_offset.z) return; - - call->function = PAINT_98196C; -} - -bool assertFunctionCallEquals(function_call expected, function_call actual) { - canonicalizeFunctionCall(&actual); - canonicalizeFunctionCall(&expected); - - if (expected.function != actual.function) { - return false; - } - - uint8 function = expected.function; - - if (function == SUPPORTS_WOOD_A || function == SUPPORTS_WOOD_B) { - if (expected.supports.type != actual.supports.type) return false; - if (expected.supports.special != actual.supports.special) return false; - if (expected.supports.height != actual.supports.height) return false; - if (expected.supports.colour_flags != actual.supports.colour_flags) return false; - - return true; - } - - if (function == SUPPORTS_METAL_A || function == SUPPORTS_METAL_B) { - if (expected.supports.type != actual.supports.type) return false; - if (expected.supports.segment != actual.supports.segment) return false; - if (expected.supports.special != actual.supports.special) return false; - if (expected.supports.height != actual.supports.height) return false; - if (expected.supports.colour_flags != actual.supports.colour_flags) return false; - - return true; - } - - if (function == SET_SEGMENT_HEIGHT) { - return true; - } - - if (expected.paint.image_id != actual.paint.image_id) { - int expectedSpriteGroup = getSpriteGroup(expected.paint.image_id & 0x7FFFF); - int actualSpriteGroup = getSpriteGroup(actual.paint.image_id & 0x7FFFF); - - if (expectedSpriteGroup != actualSpriteGroup) return false; - - if (expectedSpriteGroup == SPRITEGROUP_NONE) return false; - - return true; - } - - if (expected.paint.offset.x != actual.paint.offset.x) return false; - if (expected.paint.offset.y != actual.paint.offset.y) return false; - if (expected.paint.bound_box_length.x != actual.paint.bound_box_length.x) return false; - if (expected.paint.bound_box_length.y != actual.paint.bound_box_length.y) return false; - if (expected.paint.bound_box_length.z != actual.paint.bound_box_length.z) return false; - if (function != PAINT_98196C) { - if (expected.paint.bound_box_offset.x != actual.paint.bound_box_offset.x) return false; - if (expected.paint.bound_box_offset.y != actual.paint.bound_box_offset.y) return false; - if (expected.paint.bound_box_offset.z != actual.paint.bound_box_offset.z) return false; - } - if (expected.paint.z_offset != actual.paint.z_offset) return false; - if (expected.paint.rotation != actual.paint.rotation) return false; - - return true; -} - -static bool assertFunctionCallArrayEquals(function_call expected[], uint8 expectedCount, function_call actual[], uint8 actualCount) { - if (expectedCount != actualCount) { - return false; - } - - for (int i = 0; i < expectedCount; i++) { - function_call expectedCall = expected[i]; - function_call actualCall = actual[i]; - - if (!assertFunctionCallEquals(expectedCall, actualCall)) { - return false; - } - } - - return true; -} - -static void printImageId(uint32 input, utf8string out, size_t len) { - uint32 image = input & 0x7FFFF; - uint32 palette = input & ~0x7FFFF; - - utf8string paletteName; - if (palette == DEFAULT_SCHEME_TRACK)paletteName = "SCHEME_TRACK"; - else if (palette == DEFAULT_SCHEME_SUPPORTS)paletteName = "SCHEME_SUPPORTS"; - else if (palette == DEFAULT_SCHEME_MISC)paletteName = "SCHEME_MISC"; - else if (palette == DEFAULT_SCHEME_3)paletteName = "SCHEME_3"; - else { - paletteName = malloc(16); - snprintf(paletteName, 16, "0x%08X", palette); - } - - if (image == 0) { - snprintf(out, len, "%s", paletteName); - } else if (image & 0x70000) { - snprintf(out, len, "%s | vehicle.base_image_id + %d", paletteName, image & ~0x70000); - } else { - snprintf(out, len, "%s | %d", paletteName, image); - } -} - -static void printFunctionCall(utf8string out, size_t len, function_call call) { - utf8string imageId = malloc(64); - printImageId(call.supports.colour_flags, imageId, 64); - switch (call.function) { - case SUPPORTS_WOOD_A: - snprintf(out, len, "wooden_a_supports_paint_setup(%d, %d, %d, %s)", call.supports.type, call.supports.special, call.supports.height, imageId); - return; - case SUPPORTS_WOOD_B: - snprintf(out, len, "wooden_b_supports_paint_setup(%d, %d, %d, %s)", call.supports.type, call.supports.special, call.supports.height, imageId); - return; - - case SUPPORTS_METAL_A: - snprintf(out, len, "metal_a_supports_paint_setup(%d, %d, %d, %d, %s)", call.supports.type, call.supports.segment, call.supports.special, call.supports.height, imageId); - return; - case SUPPORTS_METAL_B: - snprintf(out, len, "metal_b_supports_paint_setup(%d, %d, %d, %d, %s)", call.supports.type, call.supports.segment, call.supports.special, call.supports.height, imageId); - return; - - case SET_SEGMENT_HEIGHT: - snprintf(out, len, "paint_util_set_segment_support_height"); - return; - } - - utf8string name = "_default"; - switch (call.function) { - case PAINT_98196C: - name = "sub_98196C"; - break; - case PAINT_98197C: - name = "sub_98197C"; - break; - case PAINT_98198C: - name = "sub_98198C"; - break; - case PAINT_98199C: - name = "sub_98199C"; - break; - } - - size_t slen; - - printImageId(call.paint.image_id, imageId, 64); - slen = snprintf( - out, - len, - "%s(%s, %d, %d, %d, %d, %d, %d, ", - name, - imageId, - call.paint.offset.x, call.paint.offset.y, - call.paint.bound_box_length.x, call.paint.bound_box_length.y, call.paint.bound_box_length.z, - call.paint.z_offset - ); - if (slen >= len) return; - - if (call.function != PAINT_98196C) { - if (slen < len) - slen += snprintf( - out + slen, - len - slen, - "%d, %d, %d, ", - call.paint.bound_box_offset.x, call.paint.bound_box_offset.y, call.paint.bound_box_offset.z - ); - } - - if (slen < len) - slen += snprintf(out + slen, len - slen, "%d)", call.paint.rotation); - - if (call.function != PAINT_98196C) { - if (slen < len) - snprintf(out + slen, len - slen, " = { %d, %d, %d }, { %d, %d, %d }, { %d, %d, %d }", - call.paint.offset.x, call.paint.offset.y, call.paint.z_offset - 48, - call.paint.bound_box_offset.x, call.paint.bound_box_offset.y, call.paint.bound_box_offset.z - 48, - call.paint.bound_box_length.x, call.paint.bound_box_length.y, call.paint.bound_box_length.z); - } -} - -static void printFunctionCallArray(utf8string out, size_t len, function_call calls[], uint8 count) { - for (int i = 0; i < count; i++) { - utf8string callOut = malloc(1024); - printFunctionCall(callOut, 1024, calls[i]); - size_t slen = strlen(out); - if (slen < len) - snprintf(out + slen, len - slen, "%s\n", callOut); - } -} - -int getTrackSequenceCount(uint8 rideType, uint8 trackType) { - int sequenceCount = 0; - const rct_preview_track **trackBlocks; - - if (ride_type_has_flag(rideType, RIDE_TYPE_FLAG_FLAT_RIDE)) { - trackBlocks = FlatRideTrackBlocks; - } else { - trackBlocks = TrackBlocks; - } - - for (int i = 0; i < 256; i++) { - if (trackBlocks[trackType][i].index == 0xFF) { - break; - } - - sequenceCount++; - } - - return sequenceCount; -} - -bool rideSupportsTrackType(int rideType, int trackType) -{ - TRACK_PAINT_FUNCTION_GETTER newPaintGetter = RideTypeTrackPaintFunctions[rideType]; - - if (newPaintGetter == NULL) { - return false; - } - - if (newPaintGetter(trackType, 0) == NULL) { - return false; - } - - if (RideTypeTrackPaintFunctionsOld[rideType][trackType] == 0) { - return false; - } - - return true; -} - - -extern bool testSupportSegments(uint8 rideType, uint8 trackType); -extern bool testTunnels(uint8 rideType, uint8 trackType); -extern bool testVerticalTunnels(uint8 rideType, uint8 trackType); - -static bool testTrackElement(uint8 rideType, uint8 trackType, utf8string error, size_t len) { - if (rideType == RIDE_TYPE_CHAIRLIFT) { - if (trackType == TRACK_ELEM_BEGIN_STATION || trackType == TRACK_ELEM_MIDDLE_STATION || trackType == TRACK_ELEM_END_STATION) { - // These rides chechk neighbouring tiles for tracks - snprintf(error, len, "Skipped"); - return false; - } - } - uint8 rideIndex = 0; - rct_map_element mapElement = { 0 }; - mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; - 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; - gTrackColours[SCHEME_TRACK] = DEFAULT_SCHEME_TRACK; - gTrackColours[SCHEME_SUPPORTS] = DEFAULT_SCHEME_SUPPORTS; - gTrackColours[SCHEME_MISC] = DEFAULT_SCHEME_MISC; - gTrackColours[SCHEME_3] = DEFAULT_SCHEME_3; - - rct_drawpixelinfo dpi = {.zoom_level = 1}; - unk_140E9A8 = &dpi; - - rct_ride ride = { 0 }; - ride.entrance_style = RIDE_ENTRANCE_STYLE_CANVAS_TENT; - - 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; - - snprintf(error, len, "rct2: 0x%08X\n", RideTypeTrackPaintFunctionsOld[rideType][trackType]); - - TRACK_PAINT_FUNCTION_GETTER newPaintGetter = RideTypeTrackPaintFunctions[rideType]; - int sequenceCount = getTrackSequenceCount(rideType, trackType); - - for (int supports = 0; supports < 2; supports++) { - if (supports == 0) { - _woodenSupports = false; - } else { - _woodenSupports = true; - } - for (int inverted = 0; inverted < 2; inverted++) { - if (inverted == 0) { - mapElement.properties.track.colour &= ~TRACK_ELEMENT_COLOUR_FLAG_INVERTED; - } else { - mapElement.properties.track.colour |= TRACK_ELEMENT_COLOUR_FLAG_INVERTED; - } - for (int chainLift = 0; chainLift < 2; chainLift++) { - if (chainLift == 0) { - mapElement.type &= ~0x80; - } else { - mapElement.type |= 0x80; - } - for (int currentRotation = 0; currentRotation < 4; currentRotation++) { - gCurrentRotation = currentRotation; - for (int direction = 0; direction < 4; direction++) { - TRACK_PAINT_FUNCTION newPaintFunction = newPaintGetter(trackType, direction); - for (int trackSequence = 0; trackSequence < sequenceCount; trackSequence++) { - RCT2_GLOBAL(0x009DE56A, sint16) = 64; // x - RCT2_GLOBAL(0x009DE56E, sint16) = 64; // y - gDidPassSurface = true; // Above surface - gSurfaceElement = &surfaceElement; - g141E9DB = G141E9DB_FLAG_1 | G141E9DB_FLAG_2; - - callCount = 0; - memset(&calls, 0, sizeof(calls)); - - memcpy(gSupportSegments, DefaultSegmentHeight, sizeof(support_height) * 9); - - uint32 *trackDirectionList = (uint32 *)RideTypeTrackPaintFunctionsOld[rideType][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 - ); - // segment heights - // tunnels - - uint8 oldCallCount = callCount; - function_call oldCalls[256]; - memcpy(&oldCalls, &calls, sizeof(calls)); - - callCount = 0; - - testpaint_clear_ignore(); - memcpy(gSupportSegments, DefaultSegmentHeight, sizeof(support_height) * 9); - newPaintFunction(rideIndex, trackSequence, direction, height, &mapElement); - if (testpaint_is_ignored(direction, trackSequence)) { - snprintf(error, len, "[ IGNORED ] [direction:%d trackSequence:%d chainLift:%d inverted:%d]\n", - direction, trackSequence, chainLift, inverted); - continue; - } - - uint8 newCallCount = callCount; - function_call newCalls[256]; - memcpy(&newCalls, &calls, sizeof(calls)); - - if (!assertFunctionCallArrayEquals(oldCalls, oldCallCount, newCalls, newCallCount)) { - utf8string diff = malloc(2048); - - snprintf(diff, 2048, "<<< EXPECTED\n"); - printFunctionCallArray(diff, 2048, oldCalls, oldCallCount); - - size_t slen = strlen(diff); - if (slen < 2048) - snprintf(diff + slen, 2048 - slen, "====\n"); - printFunctionCallArray(diff, 2048, newCalls, newCallCount); - - slen = strlen(diff); - if (slen < 2048) - snprintf(diff + slen, 2048 - slen, ">>> ACTUAL\n"); - - if (oldCallCount != newCallCount) { - slen = strlen(error); - if (slen < len) - snprintf(error + slen, len - slen, "Call counts don't match (was %d, expected %d) [direction:%d trackSequence:%d chainLift:%d inverted:%d]", - newCallCount, oldCallCount, direction, trackSequence, chainLift, inverted); - } else { - slen = strlen(error); - if (slen < len) - snprintf(error + slen, len - slen, "Calls don't match [direction:%d trackSequence:%d chainLift:%d inverted:%d]", - direction, trackSequence, chainLift, inverted); - } - - slen = strlen(error); - if (slen < len) - snprintf(error + slen, len - slen, "\n%s", diff); - - free(diff); - - return false; - } - - } - } - } - } - } - } - - bool segmentSuccess = testSupportSegments(rideType, trackType); - if (!segmentSuccess) { - return false; - } - - bool tunnelSuccess = testTunnels(rideType, trackType); - if (!tunnelSuccess) { - return false; - } - - bool verticalTunnelSuccess = testVerticalTunnels(rideType, trackType); - if (!verticalTunnelSuccess) { - return false; - } - return true; -} - -bool rideIsImplemented(int rideType) { - TRACK_PAINT_FUNCTION_GETTER newPaintGetter = RideTypeTrackPaintFunctions[rideType]; - return (newPaintGetter != 0); -} - -bool testTrackPainting(int rideType, int trackType) { - TRACK_PAINT_FUNCTION_GETTER newPaintGetter = RideTypeTrackPaintFunctions[rideType]; - if (newPaintGetter == NULL) { - return false; - } - - if (newPaintGetter(trackType, 0) == NULL) { - return false; - } - - utf8string error = malloc(2048); - bool success = testTrackElement(rideType, trackType, error, 2048); - if (!success) { - printf("%s\n", error); - } - free(error); - - return success; -} - -static int intercept_draw_6c(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp) { - registers regs = {.eax =eax, .ebx = ebx, .ecx = ecx, .edx = edx, .esi = esi, .edi = edi, .ebp = ebp}; - if ((ebp & 0x03) != get_current_rotation()) { - // Log error - log_error("Ebp is different from current rotation"); - } - - return (int) sub_98196C(ebx, regs.al, regs.cl, regs.di, regs.si, regs.ah, regs.dx, regs.ebp & 0x03); -} - -static int intercept_draw_7c(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp) { - registers regs = {.eax =eax, .ebx = ebx, .ecx = ecx, .edx = edx, .esi = esi, .edi = edi, .ebp = ebp}; - if ((ebp & 0x03) != get_current_rotation()) { - // Log error - log_error("Ebp is different from current rotation"); - } - - rct_xyz16 boundOffset = { - RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_X, sint16), - RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Y, sint16), - RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Z, sint16) - }; - - return (int) sub_98197C(ebx, regs.al, regs.cl, regs.di, regs.si, regs.ah, regs.dx, boundOffset.x, boundOffset.y, boundOffset.z, regs.ebp & 0x03); -} - -static int intercept_draw_9c(uint32 eax, uint32 ebx, uint32 ecx, uint32 edx, uint32 esi, uint32 edi, uint32 ebp) { - registers regs = {.eax =eax, .ebx = ebx, .ecx = ecx, .edx = edx, .esi = esi, .edi = edi, .ebp = ebp}; - if ((ebp & 0x03) != get_current_rotation()) { - // Log error - log_error("Ebp is different from current rotation"); - } - - rct_xyz16 boundOffset = { - RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_X, sint16), - RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Y, sint16), - RCT2_GLOBAL(RCT2_ADDRESS_PAINT_BOUNDBOX_OFFSET_Z, sint16) - }; - - return (int) sub_98199C(ebx, regs.al, regs.cl, regs.di, regs.si, regs.ah, regs.dx, boundOffset.x, boundOffset.y, boundOffset.z, regs.ebp & 0x03); -} - -static uint32 intercept_wooden_a_supports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp) { - registers regs = {.eax =eax, .ebx = ebx, .edx = edx, .edi = edi, .ebp = ebp}; - bool output = wooden_a_supports_paint_setup(regs.edi, (sint16) regs.ax, regs.dx, (uint32) regs.ebp, NULL); - - return output ? 1 : 0; -} - -static uint32 intercept_wooden_b_supports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp) { - registers regs = {.eax =eax, .ebx = ebx, .edx = edx, .edi = edi, .ebp = ebp}; - bool output = wooden_b_supports_paint_setup(regs.edi, (sint16) regs.ax, regs.dx, (uint32) regs.ebp, NULL); - - return output ? 1 : 0; -} - -static uint32 intercept_metal_a_supports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp) { - registers regs = {.eax =eax, .ebx = ebx, .edx = edx, .edi = edi, .ebp = ebp}; - metal_a_supports_paint_setup(regs.edi, regs.ebx, (sint16) regs.ax, regs.dx, (uint32) regs.ebp); - - return 0; -} - -static uint32 intercept_metal_b_supports(uint32 eax, uint32 ebx, uint32 edx, uint32 edi, uint32 ebp) { - registers regs = {.eax =eax, .ebx = ebx, .edx = edx, .edi = edi, .ebp = ebp}; - metal_b_supports_paint_setup(regs.edi, regs.ebx, (sint16) regs.ax, regs.dx, (uint32) regs.ebp); - - return 0; -} - - -void initHooks() { - addhook(0x00686806, (int) intercept_draw_7c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x006869B2, (int) intercept_draw_7c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x00686B6F, (int) intercept_draw_7c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x00686D31, (int) intercept_draw_7c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - - addhook(0x006861AC, (int) intercept_draw_6c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x00686337, (int) intercept_draw_6c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x006864D0, (int) intercept_draw_6c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x0068666B, (int) intercept_draw_6c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - - addhook(0x006874B0, (int) intercept_draw_9c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x00687618, (int) intercept_draw_9c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x0068778C, (int) intercept_draw_9c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - addhook(0x00687902, (int) intercept_draw_9c, 0, (int[]) {EAX, EBX, ECX, EDX, ESI, EDI, EBP, END}, 0, EBP); - - addhook(0x006629BC, (int) intercept_wooden_a_supports, 0, (int[]) {EAX, EBX, EDX, EDI, EBP, END}, 0, EAX); - addhook(0x00662D5C, (int) intercept_wooden_b_supports, 0, (int[]) {EAX, EBX, EDX, EDI, EBP, END}, 0, EAX); - - addhook(0x00663105, (int) intercept_metal_a_supports, 0, (int[]) {EAX, EBX, EDX, EDI, EBP, END}, 0, EAX); - addhook(0x00663584, (int) intercept_metal_b_supports, 0, (int[]) {EAX, EBX, EDX, EDI, EBP, END}, 0, EAX); -} diff --git a/test/testpaint/intercept.h b/test/testpaint/intercept.h deleted file mode 100644 index 7194042178..0000000000 --- a/test/testpaint/intercept.h +++ /dev/null @@ -1,137 +0,0 @@ -#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 - -#ifndef _TEST_PAINT_INTERCEPT_H_ -#define _TEST_PAINT_INTERCEPT_H_ - -#include "../../src/common.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - #include "../../src/interface/colour.h" - #include "../../src/paint/paint.h" - #include "../../src/paint/map_element/map_element.h" -#ifdef __cplusplus -} -#endif - -#define gRideEntries RCT2_ADDRESS(RCT2_ADDRESS_RIDE_ENTRIES, rct_ride_entry*) -#define gCurrentRotation RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8) - -enum -{ - PAINT_98196C, - PAINT_98197C, - PAINT_98198C, - PAINT_98199C, - - SUPPORTS_METAL_A, - SUPPORTS_METAL_B, - SUPPORTS_WOOD_A, - SUPPORTS_WOOD_B, - - SET_SEGMENT_HEIGHT, -}; - -typedef struct -{ - uint8 function; - struct paint - { - uint32 image_id; - rct_xy16 offset; - rct_xyz16 bound_box_length; - sint16 z_offset; - rct_xyz16 bound_box_offset; - uint32 rotation; - } paint; - struct supports - { - int type; - uint8 segment; - int special; - int height; - uint32 colour_flags; - } supports; -} function_call; - -#ifdef __cplusplus -extern "C" -{ -#endif - 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); - bool testSupportSegments(uint8 rideType, uint8 trackType); - bool testTunnels(uint8 rideType, uint8 trackType); - bool testVerticalTunnels(uint8 rideType, uint8 trackType); - void intercept_clear_calls(); - int intercept_get_calls(function_call * buffer); - bool assertFunctionCallEquals(function_call expected, function_call actual); - - int generatePaintCode(uint8 rideType); -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus - -#include - -namespace Intercept2 -{ - static const uint32 DEFAULT_SCHEME_TRACK = COLOUR_GREY << 19 | COLOUR_WHITE << 24 | 0xA0000000; - static const uint32 DEFAULT_SCHEME_SUPPORTS = COLOUR_LIGHT_BLUE << 19 | COLOUR_ICY_BLUE << 24 | 0xA0000000; - static const uint32 DEFAULT_SCHEME_MISC = COLOUR_DARK_PURPLE << 19 | COLOUR_LIGHT_PURPLE << 24 | 0xA0000000; - static const uint32 DEFAULT_SCHEME_3 = COLOUR_BRIGHT_PURPLE << 19 | COLOUR_DARK_BLUE << 24 | 0xA0000000; - - struct SegmentSupportCall - { - uint16 segments; - sint32 height; - sint16 slope; - }; - - struct SupportCall - { - sint32 height; - sint16 slope; - }; - - enum { - TUNNELCALL_SKIPPED, - TUNNELCALL_NONE, - TUNNELCALL_CALL, - }; - - struct TunnelCall { - uint8 call; - sint16 offset; - uint8 type; - }; - - sint16 getTunnelOffset(uint32 baseHeight, tunnel_entry calls[3]); - std::vector getSegmentCalls(support_height supports[9], uint8 rotation); -} - -#endif - -#endif // #endif _TEST_PAINT_INTERCEPT_H_ diff --git a/test/testpaint/intercept_2.cpp b/test/testpaint/intercept_2.cpp deleted file mode 100644 index 3d087ff458..0000000000 --- a/test/testpaint/intercept_2.cpp +++ /dev/null @@ -1,872 +0,0 @@ -#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 -#include - -#include "intercept.h" - -extern "C" { - #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 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; - } - - 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 = { 0 }; - - 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 (size_t 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 bool supportCallsMatch(SupportCall tileSupportCalls[4]) - { - SupportCall baseCall = tileSupportCalls[0]; - for (int i = 1; i < 4; i++) { - if (tileSupportCalls[i].height != baseCall.height) return false; - if (tileSupportCalls[i].slope != baseCall.slope) return false; - } - - return true; - } - - static void printSegmentSupports(utf8string out, size_t len, 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) { - size_t slen = strlen(out); - if (slen < len) - snprintf(out + slen, len - slen, " | "); - } - size_t slen = strlen(out); - if (slen < len) - snprintf(out + slen, slen - len, "SEGMENT_%02X", 0xB4 + 4 * i); - segmentsPrinted++; - } - } - - if (call.height == 0xFFFF) { - size_t slen = strlen(out); - if (slen < len) - snprintf(out + slen, len - slen, ", 0xFFFF"); - } else { - size_t slen = strlen(out); - if (slen < len) - snprintf(out + slen, len - slen, ", %d", call.height); - } - - size_t slen = strlen(out); - if (slen < len) - snprintf(out + slen, len - slen, ", 0x%02X\n", call.slope); - } - } - - static bool tunnelCallsLineUp(TunnelCall tunnelCalls[4][4]) - { - for (int side = 0; side < 4; ++side) { - TunnelCall * referenceCall = nullptr; - for (int direction = 0; direction < 4; ++direction) { - if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED) { - continue; - } - - if (referenceCall == nullptr) { - referenceCall = &tunnelCalls[direction][side]; - continue; - } - - if (referenceCall->call != tunnelCalls[direction][side].call) return false; - - if (referenceCall->call == TUNNELCALL_CALL) { - if (referenceCall->type != tunnelCalls[direction][side].type) return false; - if (referenceCall->offset != tunnelCalls[direction][side].offset) return false; - } - } - } - - return true; - } - - static void getTunnelCallReferencePattern(TunnelCall tunnelCalls[4][4], TunnelCall (*out)[4]) - { - for (int side = 0; side < 4; ++side) { - for (int direction = 0; direction < 4; ++direction) { - if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED) { - continue; - } - - (*out)[side].call = tunnelCalls[direction][side].call; - (*out)[side].type = tunnelCalls[direction][side].type; - (*out)[side].offset = tunnelCalls[direction][side].offset; - } - } - } - - static utf8string getTunnelEdgeString(TunnelCall edge) - { - utf8string out = new utf8[32]; - - switch (edge.call) { - case TUNNELCALL_SKIPPED: - snprintf(out, 32, "%s", " "); - break; - - case TUNNELCALL_NONE: - snprintf(out, 32, "%s", " - "); - break; - - case TUNNELCALL_CALL: - if (edge.offset == 0) { - snprintf(out, 32, " 0/%X ", edge.type); - } else { - utf8string offset = new utf8[16]; - if (edge.offset < 0) { - snprintf(offset, 16, "%d", edge.offset); - } else { - snprintf(offset, 16, "+%d", edge.offset); - } - - snprintf(out, 32, "%3s/%X ", offset, edge.type); - - delete[] offset; - } - break; - } - - return out; - } - - static void printTunnelCalls(TunnelCall tunnelCalls[4][4]) - { - for (int direction = 0; direction < 4; ++direction) { - printf(" + "); - } - printf("\n"); - - for (int direction = 0; direction < 4; ++direction) { - utf8string tlEdge = getTunnelEdgeString(tunnelCalls[direction][2]); - utf8string trEdge = getTunnelEdgeString(tunnelCalls[direction][3]); - printf(" %s %s ", tlEdge, trEdge); - delete [] tlEdge; - delete [] trEdge; - } - printf("\n"); - - for (int direction = 0; direction < 4; ++direction) { - printf(" + + "); - } - printf("\n"); - - for (int direction = 0; direction < 4; ++direction) { - utf8string brEdge = getTunnelEdgeString(tunnelCalls[direction][0]); - utf8string blEdge = getTunnelEdgeString(tunnelCalls[direction][1]); - printf(" %s %s ", blEdge, brEdge); - delete [] blEdge; - delete [] brEdge; - } - printf("\n"); - - for (int direction = 0; direction < 4; ++direction) { - printf(" + "); - } - printf("\n"); - - for (int direction = 0; direction < 4; ++direction) { - printf(" direction %d ", direction); - } - printf("\n"); - } - - static bool testSupportSegments(uint8 rideType, uint8 trackType) - { - uint8 rideIndex = 0; - rct_map_element mapElement = {0}; - mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; - mapElement.properties.track.type = trackType; - mapElement.base_height = 3; - - g_currently_drawn_item = &mapElement; - - rct_map_element surfaceElement = {0}; - surfaceElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; - surfaceElement.type = MAP_ELEMENT_TYPE_SURFACE; - surfaceElement.base_height = 2; - - gPaintInteractionType = VIEWPORT_INTERACTION_ITEM_RIDE; - gTrackColours[SCHEME_TRACK] = DEFAULT_SCHEME_TRACK; - gTrackColours[SCHEME_SUPPORTS] = DEFAULT_SCHEME_SUPPORTS; - gTrackColours[SCHEME_MISC] = DEFAULT_SCHEME_MISC; - gTrackColours[SCHEME_3] = DEFAULT_SCHEME_3; - - rct_drawpixelinfo dpi = { 0 }; - dpi.zoom_level = 1; - unk_140E9A8 = &dpi; - - rct_ride ride = {0}; - - rct_ride_entry rideEntry = {0}; - rct_ride_entry_vehicle vehicleEntry { 0 }; - 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 chainLift = 0; chainLift < 2; chainLift++) { - if (chainLift == 0) { - mapElement.type &= ~0x80; - } else { - mapElement.type |= 0x80; - } - - for (int trackSequence = 0; trackSequence < sequenceCount; trackSequence++) { - std::vector tileSegmentSupportCalls[4]; - SupportCall tileGeneralSupportCalls[4]; - - for (int direction = 0; direction < 4; direction++) { - for (int s = 0; s < 9; ++s) { - gSupportSegments[s].height = 0; - gSupportSegments[s].slope = 0xFF; - } - - gSupport.height = 0; - gSupport.slope = 0xFF; - g141E9DB = G141E9DB_FLAG_1 | G141E9DB_FLAG_2; - - uint32 *trackDirectionList = (uint32 *)RideTypeTrackPaintFunctionsOld[rideType][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); - - tileGeneralSupportCalls[direction].height = -1; - tileGeneralSupportCalls[direction].slope = -1; - if (gSupport.height != 0) { - tileGeneralSupportCalls[direction].height = gSupport.height; - } - if (gSupport.slope != 0xFF) { - tileGeneralSupportCalls[direction].height = gSupport.height; - } - } - - 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 chainLift:%d]\n", trackSequence, chainLift); - continue; - } - - for (int direction = 0; direction < 4; direction++) { - for (int s = 0; s < 9; ++s) { - gSupportSegments[s].height = 0; - gSupportSegments[s].slope = 0xFF; - } - - testpaint_clear_ignore(); - TRACK_PAINT_FUNCTION newPaintFunction = newPaintGetter(trackType, direction); - newPaintFunction(rideIndex, trackSequence, direction, height, &mapElement); - if (testpaint_is_ignored(direction, trackSequence)) { - continue; - } - - std::vector newCalls = getSegmentCalls(gSupportSegments, direction); - - if (!SegmentCallEquals(tileSegmentSupportCalls[0], newCalls)) { - // TODO put this into *error - utf8string diff = new utf8[2048]; - snprintf(diff, 2048, "<<< EXPECTED\n"); - printSegmentSupports(diff, 2048, tileSegmentSupportCalls[0]); - - size_t slen = strlen(diff); - if (slen < 2048) - snprintf(diff + slen, 2048 - slen, "====\n"); - printSegmentSupports(diff, 2048, newCalls); - - slen = strlen(diff); - if (slen < 2048) - snprintf(diff + strlen(diff), 2048 - slen, ">>> ACTUAL\n"); - - printf("Segment support heights didn't match. [direction:%d trackSequence:%d chainLift:%d]\n", direction, - trackSequence, chainLift); - printf("%s", diff); - delete[] diff; - return false; - } - } - - if (!supportCallsMatch(tileGeneralSupportCalls)) { - // TODO: if 3 directions do share the output, use that. - printf("Original support calls didn't match. [trackSequence:%d chainLift:%d]\n", trackSequence, chainLift); - continue; - } - - SupportCall referenceGeneralSupportCall = tileGeneralSupportCalls[0]; - - - for (int direction = 0; direction < 4; direction++) { - gSupport.height = 0; - gSupport.slope = 0xFF; - - testpaint_clear_ignore(); - TRACK_PAINT_FUNCTION newPaintFunction = newPaintGetter(trackType, direction); - newPaintFunction(rideIndex, trackSequence, direction, height, &mapElement); - if (testpaint_is_ignored(direction, trackSequence)) { - continue; - } - - if (referenceGeneralSupportCall.height != -1) { - if (gSupport.height != referenceGeneralSupportCall.height) { - printf("General support heights didn't match. (expected height + %d, actual: height + %d) [direction:%d trackSequence:%d chainLift:%d]\n", - referenceGeneralSupportCall.height - height, - gSupport.height - height, - direction, - trackSequence, - chainLift); - return false; - } - } - if (referenceGeneralSupportCall.slope != -1) { - if (gSupport.slope != referenceGeneralSupportCall.slope) { - printf("General support slopes didn't match. [direction:%d trackSequence:%d chainLift:%d]\n", direction, - trackSequence, chainLift); - return false; - } - } - } - - } - } - return true; - } - - static bool tunnelPatternsMatch(TunnelCall expected[4], TunnelCall actual[4]) - { - for (int side = 0; side < 4; side++) { - if (expected[side].call != actual[side].call) return false; - - if (expected[side].call == TUNNELCALL_CALL) { - if (expected[side].type != actual[side].type) return false; - if (expected[side].offset != actual[side].offset) return false; - } - } - - return true; - } - - sint16 getTunnelOffset(uint32 baseHeight, tunnel_entry calls[3]) - { - for (sint16 offset = -56; offset <= 56; offset += 8) { - if (calls[0].height != (baseHeight - 8 + offset) / 16) continue; - if (calls[1].height != (baseHeight + 0 + offset) / 16) continue; - if (calls[2].height != (baseHeight + 8 + offset) / 16) continue; - - return offset; - } - - log_error("Unknown tunnel height. (%d, %d, %d)", calls[0].height, calls[1].height, calls[2].height); - return 0; - } - - static bool testTunnels(uint8 rideType, uint8 trackType) - { - uint8 rideIndex = 0; - rct_map_element mapElement = {0}; - mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; - mapElement.properties.track.type = trackType; - mapElement.base_height = 3; - - g_currently_drawn_item = &mapElement; - - gPaintInteractionType = VIEWPORT_INTERACTION_ITEM_RIDE; - gTrackColours[SCHEME_TRACK] = DEFAULT_SCHEME_TRACK; - gTrackColours[SCHEME_SUPPORTS] = DEFAULT_SCHEME_SUPPORTS; - gTrackColours[SCHEME_MISC] = DEFAULT_SCHEME_MISC; - gTrackColours[SCHEME_3] = DEFAULT_SCHEME_3; - - rct_drawpixelinfo dpi { 0 }; - dpi.zoom_level = 1; - unk_140E9A8 = &dpi; - - rct_ride ride = {0}; - - rct_ride_entry rideEntry = {0}; - rct_ride_entry_vehicle vehicleEntry { 0 }; - 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++) { - TunnelCall tileTunnelCalls[4][4]; - - for (int direction = 0; direction < 4; direction++) { - gLeftTunnelCount = 0; - gRightTunnelCount = 0; - - uint32 *trackDirectionList = (uint32 *)RideTypeTrackPaintFunctionsOld[rideType][trackType]; - - for (int offset = -8; offset <= 8; offset += 8) { - // 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 + offset, - (int) &mapElement, - rideIndex * sizeof(rct_ride), - trackSequence - ); - } - - uint8 rightIndex = (4 - direction) % 4; - uint8 leftIndex = (rightIndex + 1) % 4; - - for (int i = 0; i < 4; ++i) { - tileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED; - } - if (gRightTunnelCount == 0) { - tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_NONE; - } else if (gRightTunnelCount == 3) { - tileTunnelCalls[direction][rightIndex].call = TUNNELCALL_CALL; - tileTunnelCalls[direction][rightIndex].offset = getTunnelOffset(height, gRightTunnels); - tileTunnelCalls[direction][rightIndex].type = gRightTunnels[0].type; - } else { - printf("Multiple tunnels on one side aren't supported.\n"); - return false; - } - - if (gLeftTunnelCount == 0) { - tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_NONE; - } else if (gLeftTunnelCount == 3) { - tileTunnelCalls[direction][leftIndex].call = TUNNELCALL_CALL; - tileTunnelCalls[direction][leftIndex].offset = getTunnelOffset(height, gLeftTunnels); - tileTunnelCalls[direction][leftIndex].type = gLeftTunnels[0].type; - } else { - printf("Multiple tunnels on one side aren't supported.\n"); - return false; - } - } - - TunnelCall newTileTunnelCalls[4][4]; - for (int direction = 0; direction < 4; direction++) { - gLeftTunnelCount = 0; - gRightTunnelCount = 0; - - testpaint_clear_ignore(); - TRACK_PAINT_FUNCTION newPaintFunction = newPaintGetter(trackType, direction); - - for (int offset = -8; offset <= 8; offset += 8) { - // TODO: move tunnel pushing to interface so we don't have to check the output 3 times - newPaintFunction(rideIndex, trackSequence, direction, height + offset, &mapElement); - } - - uint8 rightIndex = (4 - direction) % 4; - uint8 leftIndex = (rightIndex + 1) % 4; - - for (int i = 0; i < 4; ++i) { - newTileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED; - } - - if (gRightTunnelCount == 0) { - newTileTunnelCalls[direction][rightIndex].call = TUNNELCALL_NONE; - } else if (gRightTunnelCount == 3) { - newTileTunnelCalls[direction][rightIndex].call = TUNNELCALL_CALL; - newTileTunnelCalls[direction][rightIndex].offset = getTunnelOffset(height, gRightTunnels); - newTileTunnelCalls[direction][rightIndex].type = gRightTunnels[0].type; - } else { - printf("Multiple tunnels on one side aren't supported.\n"); - return false; - } - - if (gLeftTunnelCount == 0) { - newTileTunnelCalls[direction][leftIndex].call = TUNNELCALL_NONE; - } else if (gLeftTunnelCount == 3) { - newTileTunnelCalls[direction][leftIndex].call = TUNNELCALL_CALL; - newTileTunnelCalls[direction][leftIndex].offset = getTunnelOffset(height, gLeftTunnels); - newTileTunnelCalls[direction][leftIndex].type = gLeftTunnels[0].type; - } else { - printf("Multiple tunnels on one side aren't supported.\n"); - return false; - } - } - - - if (!tunnelCallsLineUp(tileTunnelCalls)) { - printf("Original tunnel calls don\'t line up. Skipping tunnel validation [trackSequence:%d].\n", - trackSequence); - printTunnelCalls(tileTunnelCalls); - - if (!tunnelCallsLineUp(newTileTunnelCalls)) { - printf("Decompiled tunnel calls don\'t line up. [trackSequence:%d].\n", trackSequence); - printTunnelCalls(newTileTunnelCalls); - return false; - } - continue; - } - - TunnelCall referencePattern[4] = { 0 }; - getTunnelCallReferencePattern(tileTunnelCalls, &referencePattern); - - TunnelCall actualPattern[4] = { 0 }; - getTunnelCallReferencePattern(newTileTunnelCalls, &actualPattern); - - if (!tunnelPatternsMatch(referencePattern, actualPattern)) { - printf("Tunnel calls don't match expected pattern. [trackSequence:%d]\n", trackSequence); - printf("expected:\n"); - printTunnelCalls(tileTunnelCalls); - printf("actual:\n"); - printTunnelCalls(newTileTunnelCalls); - return false; - } - - } - return true; - } - - static bool verticalTunnelHeightIsConsistent(uint8 heights[4]) - { - for (int i = 1; i < 4; ++i) { - if (heights[i] != heights[0]) return false; - } - - return true; - } - - static void printRelativeHeight(utf8string out, size_t len, sint16 height) - { - if (height == 0) { - snprintf(out, len, "height"); - return; - } - - if (height > 0) { - snprintf(out, len, "height + %d", height); - return; - } - - if (height < 0) { - snprintf(out, len, "height - %d", int(abs(height))); - return; - } - } - - static bool testVerticalTunnels(uint8 rideType, uint8 trackType) - { - uint8 rideIndex = 0; - rct_map_element mapElement = {0}; - mapElement.flags |= MAP_ELEMENT_FLAG_LAST_TILE; - mapElement.properties.track.type = trackType; - mapElement.base_height = 3; - - g_currently_drawn_item = &mapElement; - - gPaintInteractionType = VIEWPORT_INTERACTION_ITEM_RIDE; - gTrackColours[SCHEME_TRACK] = DEFAULT_SCHEME_TRACK; - gTrackColours[SCHEME_SUPPORTS] = DEFAULT_SCHEME_SUPPORTS; - gTrackColours[SCHEME_MISC] = DEFAULT_SCHEME_MISC; - gTrackColours[SCHEME_3] = DEFAULT_SCHEME_3; - - rct_drawpixelinfo dpi = { 0 }; - dpi.zoom_level = 1; - unk_140E9A8 = &dpi; - - rct_ride ride = {0}; - - rct_ride_entry rideEntry = {0}; - rct_ride_entry_vehicle vehicleEntry = { 0 }; - 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++) { - uint8 verticalTunnelHeight[4]; - - for (int direction = 0; direction < 4; direction++) { - gVerticalTunnelHeight = 0; - - uint32 *trackDirectionList = (uint32 *)RideTypeTrackPaintFunctionsOld[rideType][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 - ); - - verticalTunnelHeight[direction] = gVerticalTunnelHeight; - } - - if (!verticalTunnelHeightIsConsistent(verticalTunnelHeight)) { - printf( - "Original vertical tunnel height is inconsistent, skipping test. [trackSequence:%d]\n", - trackSequence - ); - continue; - } - - uint8 referenceHeight = verticalTunnelHeight[0]; - for (int direction = 0; direction < 4; direction++) { - gVerticalTunnelHeight = 0; - - testpaint_clear_ignore(); - TRACK_PAINT_FUNCTION newPaintFunction = newPaintGetter(trackType, direction); - newPaintFunction(rideIndex, trackSequence, direction, height, &mapElement); - if (testpaint_is_ignored(direction, trackSequence)) { - continue; - } - - if (gVerticalTunnelHeight != referenceHeight) { - if (referenceHeight == 0) { - printf( - "Expected no vertical tunnel. [trackSequence:%d direction:%d]\n", - trackSequence, - direction - ); - - return false; - } - - utf8string strExpectedTunnelHeight = new utf8[16]; - utf8string strActualTunnelHeight = new utf8[16]; - printRelativeHeight(strExpectedTunnelHeight, 16, (referenceHeight * 16) - 48); - printRelativeHeight(strActualTunnelHeight, 16, (gVerticalTunnelHeight * 16) - 48); - - printf( - "Expected vertical tunnel height to be `%s`, was `%s`. [trackSequence:%d direction:%d]\n", - strExpectedTunnelHeight, - strActualTunnelHeight, - trackSequence, - direction - ); - - delete []strExpectedTunnelHeight; - delete []strActualTunnelHeight; - - return false; - } - } - } - - return true; - } - - struct IgnoredEntry - { - uint8 Direction; - uint8 TrackSequence; - }; - - static bool _ignoredAll; - static std::vector _ignoredEntries; - - static void testClearIgnore() - { - _ignoredAll = false; - _ignoredEntries.clear(); - } - - static void testIgnore(uint8 direction, uint8 trackSequence) - { - _ignoredEntries.push_back({ direction, trackSequence }); - } - - static void testIgnoreAll() - { - _ignoredAll = true; - } - - static bool testIsIgnored(uint8 direction, uint8 trackSequence) - { - if (_ignoredAll) return true; - for (const IgnoredEntry &entry : _ignoredEntries) - { - if (entry.Direction == direction && - entry.TrackSequence == trackSequence) - { - return true; - } - } - return false; - } -} - -extern "C" -{ - bool testSupportSegments(uint8 rideType, uint8 trackType) - { - return Intercept2::testSupportSegments(rideType, trackType); - } - - bool testTunnels(uint8 rideType, uint8 trackType) - { - return Intercept2::testTunnels(rideType, trackType); - } - - bool testVerticalTunnels(uint8 rideType, uint8 trackType) - { - return Intercept2::testVerticalTunnels(rideType, trackType); - } - - void testpaint_clear_ignore() - { - Intercept2::testClearIgnore(); - } - - void testpaint_ignore(uint8 direction, uint8 trackSequence) - { - Intercept2::testIgnore(direction, trackSequence); - } - - void testpaint_ignore_all() - { - Intercept2::testIgnoreAll(); - } - - bool testpaint_is_ignored(uint8 direction, uint8 trackSequence) - { - return Intercept2::testIsIgnored(direction, trackSequence); - } -} diff --git a/test/testpaint/main.cpp b/test/testpaint/main.cpp index 6f23dbb827..d99a3e73bd 100644 --- a/test/testpaint/main.cpp +++ b/test/testpaint/main.cpp @@ -23,7 +23,9 @@ #include #endif // defined(__unix__) -#include "intercept.h" +#include "PaintIntercept.hpp" +#include "TestTrack.hpp" +#include "Utils.hpp" extern "C" { #include "data.h" @@ -42,10 +44,12 @@ typedef struct { enum CLIColour { DEFAULT, RED, + YELLOW, GREEN, }; bool gTestColor = true; +Verbosity _verbosity = NORMAL; static bool CStringEquals(const char *lhs, const char *rhs) { if (lhs == NULL) return rhs == NULL; @@ -96,6 +100,8 @@ static const char* GetAnsiColorCode(CLIColour color) { switch (color) { case RED: return "1"; case GREEN: return "2"; + case YELLOW: + return "3"; default: return NULL; }; } @@ -114,16 +120,16 @@ static WORD GetWindowsConsoleAttribute(CLIColour color, WORD defaultAttr) switch (color) { case RED: return FOREGROUND_RED; case GREEN: return FOREGROUND_GREEN; + case YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; default: return defaultAttr; }; } #endif -static void ColouredPrintF(CLIColour colour, const char* fmt, ...) +static void Write_VA(Verbosity verbosity, CLIColour colour, const char *fmt, va_list args) { - va_list args; - va_start(args, fmt); + if (_verbosity < verbosity) return; COLOUR_METHOD colourMethod = GetColourMethod(); @@ -142,6 +148,37 @@ static void ColouredPrintF(CLIColour colour, const char* fmt, ...) SetConsoleTextAttribute(hStdOut, defaultAttr); #endif } +} + +static void Write(Verbosity verbosity, CLIColour colour, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + Write_VA(verbosity, colour, fmt, args); + va_end(args); +} + +static void Write(Verbosity verbosity, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Write_VA(verbosity, DEFAULT, fmt, args); + va_end(args); +} + +static void Write(CLIColour colour, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Write_VA(NORMAL, colour, fmt, args); + va_end(args); +} + +static void Write(const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + Write_VA(NORMAL, DEFAULT, fmt, args); va_end(args); } @@ -340,7 +377,7 @@ static void PrintRideTypes() { for (uint8 rideType = 0; rideType < 91; rideType++) { CLIColour colour = CLIColour::DEFAULT; - bool implemented = rideIsImplemented(rideType); + bool implemented = Utils::rideIsImplemented(rideType); const char * rideName = RideNames[rideType]; const char * status = ""; if (implemented) { @@ -348,11 +385,48 @@ static void PrintRideTypes() colour = CLIColour::GREEN; } - ColouredPrintF(colour, "%2d: %-30s%s\n", rideType, rideName, status); + Write(colour, "%2d: %-30s%s\n", rideType, rideName, status); } } +#include "GeneralSupportHeightCall.hpp" + +static void TestGeneralSupportHeightCall() { + SupportCall callA = {16, 0x20}; + SupportCall callB = {32, 0x20}; + SupportCall callC = {48, 0x20}; + SupportCall callD = {48, 0x1F}; + + SupportCall out = {0,0}; + bool success; + + SupportCall groupA[4] = {callA, callA, callA, callA}; + success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupA, &out); + assert(success); + assert(out == callA); + + SupportCall groupB[4] = {callB, callA, callA, callA}; + success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupB, &out); + assert(success); + assert(out == callA); + + SupportCall groupC[4] = {callB, callA, callB, callA}; + success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupC, &out); + assert(!success); + + SupportCall groupD[4] = {callB, callC, callB, callA}; + success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupD, &out); + assert(success); + assert(out == callB); + + SupportCall groupE[4] = {callD, callC, callB, callA}; + success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupE, &out); + assert(!success); +} + int main(int argc, char *argv[]) { + TestGeneralSupportHeightCall(); + std::vector testCases; bool generate = false; @@ -362,6 +436,9 @@ int main(int argc, char *argv[]) { if (strcmp(arg, "--gtest_color=no") == 0) { gTestColor = false; } + else if (strcmp(arg, "--quiet") == 0) { + _verbosity = Verbosity::QUIET; + } else if (strcmp(arg, "--ride-type") == 0) { if (i + 1 < argc) { i++; @@ -383,7 +460,7 @@ int main(int argc, char *argv[]) { } openrct2_setup_rct2_segment(); - initHooks(); + PaintIntercept::InitHooks(); return generatePaintCode(specificRideType); } @@ -393,7 +470,7 @@ int main(int argc, char *argv[]) { continue; } - if (!rideIsImplemented(rideType)) { + if (!Utils::rideIsImplemented(rideType)) { continue; } @@ -404,7 +481,7 @@ int main(int argc, char *argv[]) { testCase.trackTypes.push_back(RideConstructionDefaultTrackType[rideType]); } else { for (int trackType = 0; trackType < 256; trackType++) { - if (rideSupportsTrackType(rideType, trackType)) { + if (Utils::rideSupportsTrackType(rideType, trackType)) { testCase.trackTypes.push_back(trackType); } } @@ -419,20 +496,20 @@ int main(int argc, char *argv[]) { testCount += tc.trackTypes.size(); } - ColouredPrintF(CLIColour::GREEN, "[==========] "); - printf("Running %d tests from %d test cases.\n", testCount, testCaseCount); + Write(CLIColour::GREEN, "[==========] "); + Write("Running %d tests from %d test cases.\n", testCount, testCaseCount); - ColouredPrintF(CLIColour::GREEN, "[----------] "); - printf("Global test environment set-up.\n"); + Write(CLIColour::GREEN, "[----------] "); + Write("Global test environment set-up.\n"); openrct2_setup_rct2_segment(); - initHooks(); + PaintIntercept::InitHooks(); int successCount = 0; std::vector failures; for (auto &&tc : testCases) { const utf8string rideTypeName = RideNames[tc.rideType]; - ColouredPrintF(CLIColour::GREEN, "[----------] "); - printf("%d tests from %s\n", (int)tc.trackTypes.size(), rideTypeName); + Write(CLIColour::GREEN, "[----------] "); + Write("%d tests from %s\n", (int)tc.trackTypes.size(), rideTypeName); for (auto &&trackType : tc.trackTypes) { utf8string trackTypeName; @@ -442,50 +519,65 @@ int main(int argc, char *argv[]) { trackTypeName = TrackNames[trackType]; } - ColouredPrintF(CLIColour::GREEN, "[ RUN ] "); - printf("%s.%s\n", rideTypeName, trackTypeName); - bool success = testTrackPainting(tc.rideType, trackType); - if (!success) { - utf8string testCaseName = new utf8[64]; - snprintf(testCaseName, 64, "%s.%s", rideTypeName, trackTypeName); + Write(CLIColour::GREEN, "[ RUN ] "); + Write("%s.%s\n", rideTypeName, trackTypeName); - ColouredPrintF(CLIColour::RED, "[ FAILED ] "); - printf("%s (0 ms)\n", testCaseName); - failures.push_back(testCaseName); - } else { - ColouredPrintF(CLIColour::GREEN, "[ OK ] "); - printf("%s.%s (0 ms)\n", rideTypeName, trackTypeName); - successCount++; + std::string out; + int retVal = TestTrack::TestPaintTrackElement(tc.rideType, trackType, &out); + Write("%s", out.c_str()); + switch (retVal) { + case TEST_SUCCESS: + Write(CLIColour::GREEN, "[ OK ] "); + Write("%s.%s (0 ms)\n", rideTypeName, trackTypeName); + successCount++; + break; + + case TEST_SKIPPED: + Write("Skipped\n"); + // Outputting this as OK because CLion only allows FAILED or OK + Write(CLIColour::YELLOW, "[ OK ] "); + Write("%s.%s (0 ms)\n", rideTypeName, trackTypeName); + successCount++; + break; + + case TEST_FAILED: + utf8string testCaseName = new utf8[64]; + snprintf(testCaseName, 64, "%s.%s", rideTypeName, trackTypeName); + + Write(CLIColour::RED, "[ FAILED ] "); + Write("%s (0 ms)\n", testCaseName); + failures.push_back(testCaseName); + break; } } - ColouredPrintF(CLIColour::GREEN, "[----------] "); - printf("%d tests from %s (0 ms total)\n", (int)tc.trackTypes.size(), rideTypeName); + Write(CLIColour::GREEN, "[----------] "); + Write("%d tests from %s (0 ms total)\n", (int)tc.trackTypes.size(), rideTypeName); } - printf("\n"); + Write("\n"); - ColouredPrintF(CLIColour::GREEN, "[----------] "); - printf("Global test environment tear-down\n"); + Write(CLIColour::GREEN, "[----------] "); + Write("Global test environment tear-down\n"); - ColouredPrintF(CLIColour::GREEN, "[==========] "); - printf("%d tests from %d test cases ran. (0 ms total).\n", testCount, testCaseCount); + Write(CLIColour::GREEN, "[==========] "); + Write("%d tests from %d test cases ran. (0 ms total).\n", testCount, testCaseCount); - ColouredPrintF(CLIColour::GREEN, "[ PASSED ] "); - printf("%d tests.\n", successCount); + Write(Verbosity::QUIET, CLIColour::GREEN, "[ PASSED ] "); + Write(Verbosity::QUIET, "%d tests.\n", successCount); if (failures.size() > 0) { - ColouredPrintF(CLIColour::RED, "[ FAILED ] "); - printf("%d tests, listed below:\n", (int)failures.size()); + Write(Verbosity::QUIET, CLIColour::RED, "[ FAILED ] "); + Write(Verbosity::QUIET, "%d tests, listed below:\n", (int)failures.size()); for (auto &&failure : failures) { - ColouredPrintF(CLIColour::RED, "[ FAILED ] "); - printf("%s\n", failure); + Write(Verbosity::QUIET, CLIColour::RED, "[ FAILED ] "); + Write(Verbosity::QUIET, "%s\n", failure); delete [] failure; } - printf("\n"); + Write(Verbosity::QUIET, "\n"); - printf("%d FAILED TESTS\n", (int)failures.size()); + Write(Verbosity::QUIET, "%d FAILED TESTS\n", (int)failures.size()); return 1; } diff --git a/test/testpaint/testpaint.vcxproj b/test/testpaint/testpaint.vcxproj index 76e617c82a..09b27dffd4 100644 --- a/test/testpaint/testpaint.vcxproj +++ b/test/testpaint/testpaint.vcxproj @@ -95,10 +95,19 @@ + + - - + + + + + + + + +