1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-29 01:35:06 +01:00

Merge pull request #4620 from marijnvdwerf/clean/testpaint-1

Start cleanup of testpaint
This commit is contained in:
Ted John
2016-10-21 08:50:07 +01:00
committed by GitHub
35 changed files with 2841 additions and 2007 deletions

View File

@@ -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 = "<group>"; };
791166FA1D7486EF005912EA /* NetworkServerAdvertiser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkServerAdvertiser.h; sourceTree = "<group>"; };
8594C05F1D885CF600235E93 /* track_data_old.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = track_data_old.c; sourceTree = "<group>"; };
85AFA20F1D7DB83E00221B42 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
85AFA2101D7DB83E00221B42 /* intercept.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = intercept.h; sourceTree = "<group>"; };
85AFA2141D7DDFA100221B42 /* data.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = "<group>"; };
85B468FB1D96822F000F1DB5 /* paint_helpers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = paint_helpers.c; sourceTree = "<group>"; };
85B5C0AF1D81D912001B99A8 /* intercept_2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = intercept_2.cpp; sourceTree = "<group>"; };
C606CCAB1DB4054000FE4015 /* compat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compat.c; sourceTree = "<group>"; };
C606CCAC1DB4054000FE4015 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = "<group>"; };
C606CCAD1DB4054000FE4015 /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = "<group>"; };
C606CCAE1DB4054000FE4015 /* FunctionCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FunctionCall.cpp; sourceTree = "<group>"; };
C606CCAF1DB4054000FE4015 /* FunctionCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FunctionCall.hpp; sourceTree = "<group>"; };
C606CCB01DB4054000FE4015 /* generate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = generate.cpp; sourceTree = "<group>"; };
C606CCB41DB4054000FE4015 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
C606CCB51DB4054000FE4015 /* PaintIntercept.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PaintIntercept.cpp; sourceTree = "<group>"; };
C606CCB61DB4054000FE4015 /* Printer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Printer.cpp; sourceTree = "<group>"; };
C606CCB71DB4054000FE4015 /* Printer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Printer.hpp; sourceTree = "<group>"; };
C606CCB81DB4054000FE4015 /* String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = String.cpp; sourceTree = "<group>"; };
C606CCB91DB4054000FE4015 /* String.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = String.hpp; sourceTree = "<group>"; };
C606CCBA1DB4054000FE4015 /* TestTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestTrack.cpp; sourceTree = "<group>"; };
C606CCBB1DB4054000FE4015 /* TestTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TestTrack.hpp; sourceTree = "<group>"; };
C606CCBC1DB4054000FE4015 /* Utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Utils.cpp; sourceTree = "<group>"; };
C606CCBD1DB4054000FE4015 /* Utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Utils.hpp; sourceTree = "<group>"; };
C606CCCA1DB427A000FE4015 /* GeneralSupportHeightCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeneralSupportHeightCall.cpp; sourceTree = "<group>"; };
C606CCCB1DB427A000FE4015 /* GeneralSupportHeightCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GeneralSupportHeightCall.hpp; sourceTree = "<group>"; };
C606CCCC1DB427A000FE4015 /* SegmentSupportHeightCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentSupportHeightCall.cpp; sourceTree = "<group>"; };
C606CCCD1DB427A000FE4015 /* SegmentSupportHeightCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SegmentSupportHeightCall.hpp; sourceTree = "<group>"; };
C606CCD01DB4D7C800FE4015 /* SideTunnelCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SideTunnelCall.cpp; sourceTree = "<group>"; };
C606CCD11DB4D7C800FE4015 /* SideTunnelCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SideTunnelCall.hpp; sourceTree = "<group>"; };
C606CCD31DB4DD6C00FE4015 /* VerticalTunnelCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VerticalTunnelCall.cpp; sourceTree = "<group>"; };
C606CCD41DB4DD6C00FE4015 /* VerticalTunnelCall.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VerticalTunnelCall.hpp; sourceTree = "<group>"; };
C606CCD61DB4E1CD00FE4015 /* PaintIntercept.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PaintIntercept.hpp; sourceTree = "<group>"; };
C606CD231DB5120900FE4015 /* TestPaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestPaint.cpp; sourceTree = "<group>"; };
C606CD241DB5120900FE4015 /* TestPaint.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TestPaint.hpp; sourceTree = "<group>"; };
C612A8971D64825300B634CA /* vehicle_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vehicle_data.c; sourceTree = "<group>"; };
C612A8981D64825300B634CA /* vehicle_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vehicle_data.h; sourceTree = "<group>"; };
C61FB7221CF86356004CE991 /* NetworkUser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkUser.cpp; sourceTree = "<group>"; usesTabs = 0; };
C61FB7231CF86356004CE991 /* NetworkUser.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = NetworkUser.h; sourceTree = "<group>"; 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 = "<group>"; };
C64FDAC71D6DA72400F259B9 /* intercept.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intercept.c; sourceTree = "<group>"; };
C64FDAC91D6DA92D00F259B9 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = "<group>"; };
C650B2151CCABBDD00B4D91C /* S4Importer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = S4Importer.cpp; sourceTree = "<group>"; usesTabs = 0; };
C650B2161CCABBDD00B4D91C /* S4Importer.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = S4Importer.h; sourceTree = "<group>"; usesTabs = 0; };
C650B2171CCABBDD00B4D91C /* tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tables.cpp; sourceTree = "<group>"; usesTabs = 0; };
@@ -908,7 +937,6 @@
D44271F21CC81B3200D84D28 /* sprite.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sprite.c; sourceTree = "<group>"; };
D44271F31CC81B3200D84D28 /* sprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sprite.h; sourceTree = "<group>"; };
D44271F41CC81B3200D84D28 /* water.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = water.h; sourceTree = "<group>"; };
D45291991DAA202C00C11788 /* generate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = generate.cpp; sourceTree = "<group>"; };
D45A38B31CF3006400659A24 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libcrypto.dylib; sourceTree = "<group>"; };
D45A38B41CF3006400659A24 /* libfreetype.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libfreetype.dylib; sourceTree = "<group>"; };
D45A38B51CF3006400659A24 /* libjansson.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libjansson.dylib; sourceTree = "<group>"; };
@@ -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 */,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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<function_call> expected, std::vector<function_call> 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;
}

View File

@@ -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<function_call> expected, std::vector<function_call> actual);
};

View File

@@ -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 <map>
#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<SupportCall, int> 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;
}

View File

@@ -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);
};

View File

@@ -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;
}
}

View File

@@ -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);
};

253
test/testpaint/Printer.cpp Normal file
View File

@@ -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<function_call> 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<SegmentSupportCall> 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();
}
};

View File

@@ -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 <string>
#include <vector>
#include "FunctionCall.hpp"
#include "SideTunnelCall.hpp"
#include "SegmentSupportHeightCall.hpp"
namespace Printer {
std::string PrintFunctionCall(function_call call, uint16 baseHeight);
std::string PrintFunctionCalls(std::vector <function_call> calls, uint16 baseHeight);
std::string PrintSegmentSupportHeightCalls(std::vector<SegmentSupportCall> calls);
std::string PrintSideTunnelCalls(TunnelCall tunnelCalls[4][4]);
std::string PrintHeightOffset(uint16 height, uint16 baseHeight);
}

View File

@@ -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 <map>
#include <algorithm>
#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<SegmentSupportCall> 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<SegmentSupportCall> 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<SegmentSupportCall> tileSegmentSupportCalls[4])
{
std::vector<SegmentSupportCall> baseCallList = tileSegmentSupportCalls[0];
for (int i = 1; i < 4; i++) {
if (!CallsEqual(baseCallList, tileSegmentSupportCalls[i])) {
return false;
}
}
return true;
}
bool SegmentSupportHeightCall::CallsEqual(std::vector<SegmentSupportCall> lhs, std::vector<SegmentSupportCall> 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<SegmentSupportCall> calls[4], std::vector<SegmentSupportCall> *out) {
std::map<std::vector<SegmentSupportCall>, 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;
}

View File

@@ -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 <vector>
#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<SegmentSupportCall> getSegmentCalls(support_height supports[9], uint8 rotation);
static bool CallsMatch(std::vector<SegmentSupportCall> tileSegmentSupportCalls[4]);
static bool CallsEqual(std::vector<SegmentSupportCall> lhs, std::vector<SegmentSupportCall> rhs);
static bool FindMostCommonSupportCall(std::vector<SegmentSupportCall> calls[4], std::vector<SegmentSupportCall> * out);
};

View File

@@ -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;
}

View File

@@ -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]);
};

34
test/testpaint/String.cpp Normal file
View File

@@ -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 <cstdarg>
#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);
}
};

23
test/testpaint/String.hpp Normal file
View File

@@ -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 <string>
namespace String {
std::string Format(const char *format, ...);
}

View File

@@ -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 <vector>
#include <algorithm>
#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<IgnoredEntry> _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);
}
}

View File

@@ -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 <vector>
#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);
}

View File

@@ -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 <string>
#include <vector>
#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<ITestTrackFilter *> filters;
filters.push_back(new CableLiftFilter());
filters.push_back(new ChainLiftFilter());
filters.push_back(new InvertedFilter());
filters.push_back(new EntranceStyleFilter());
std::vector<ITestTrackFilter *> 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<uint8> filler;
filler.push_back(0);
std::vector<std::vector<uint8>> 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<std::vector<uint8>> newArgumentPermutations;
for (int variant = 0; variant < variantCount; variant++) {
for (auto &&oldPermutation : argumentPermutations) {
std::vector<uint8> 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<function_call> 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<function_call> 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<SegmentSupportCall> 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<SegmentSupportCall> 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<SegmentSupportCall> 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;
}

View File

@@ -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 <string>
#include "../../src/common.h"
class TestTrack {
public:
static uint8 TestPaintTrackElement(uint8 rideType, uint8 trackType, std::string *out);
};

69
test/testpaint/Utils.cpp Normal file
View File

@@ -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);
}
}

25
test/testpaint/Utils.hpp Normal file
View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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]);
};

View File

@@ -18,7 +18,12 @@
#include <string>
#include <vector>
#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<function_call> calls[4], chainLiftCalls[4], cableLiftCalls[4];
Intercept2::TunnelCall tileTunnelCalls[4][4];
TunnelCall tileTunnelCalls[4][4];
sint16 verticalTunnelHeights[4];
std::vector<Intercept2::SegmentSupportCall> segmentSupportCalls[4];
std::vector<SegmentSupportCall> 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<function_call> checkCalls = std::vector<function_call>(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<Intercept2::SegmentSupportCall> segmentSupportCalls[4])
void GenerateSegmentSupportCall(int tabs, std::vector<SegmentSupportCall> 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"

View File

@@ -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);
}

View File

@@ -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 <vector>
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<SegmentSupportCall> getSegmentCalls(support_height supports[9], uint8 rotation);
}
#endif
#endif // #endif _TEST_PAINT_INTERCEPT_H_

View File

@@ -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 <vector>
#include <algorithm>
#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<SegmentSupportCall> 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<SegmentSupportCall> 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<SegmentSupportCall> lhs, std::vector<SegmentSupportCall> 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<SegmentSupportCall> tileSegmentSupportCalls[4])
{
std::vector<SegmentSupportCall> 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<SegmentSupportCall> 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<SegmentSupportCall> 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<SegmentSupportCall> 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<IgnoredEntry> _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);
}
}

View File

@@ -23,7 +23,9 @@
#include <sys/mman.h>
#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<TestCase> 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<utf8string> 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;
}

View File

@@ -95,10 +95,19 @@
<ClCompile Include="..\..\src\paint\paint_helpers.c" />
<ClCompile Include="compat.c" />
<ClCompile Include="data.c" />
<ClCompile Include="FunctionCall.cpp" />
<ClCompile Include="GeneralSupportHeightCall.cpp" />
<ClCompile Include="generate.cpp" />
<ClCompile Include="intercept.c" />
<ClCompile Include="intercept_2.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="PaintIntercept.cpp" />
<ClCompile Include="Printer.cpp" />
<ClCompile Include="SegmentSupportHeightCall.cpp" />
<ClCompile Include="SideTunnelCall.cpp" />
<ClCompile Include="String.cpp" />
<ClCompile Include="TestPaint.cpp" />
<ClCompile Include="TestTrack.cpp" />
<ClCompile Include="Utils.cpp" />
<ClCompile Include="VerticalTunnelCall.cpp" />
<ClCompile Include="..\..\src\addresses.c" />
<ClCompile Include="..\..\src\diagnostic.c" />
<ClCompile Include="..\..\src\hook.c" />