1 /*****************************************************************************
2  * Copyright (c) 2014-2018 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "PaintIntercept.hpp"
11 
12 #include "FunctionCall.hpp"
13 #include "Hook.h"
14 #include "TestPaint.hpp"
15 
16 #include <cstring>
17 #include <openrct2/common.h>
18 #include <openrct2/interface/Viewport.h>
19 #include <openrct2/paint/Supports.h>
20 #include <openrct2/sprites.h>
21 
22 paint_session gPaintSession;
23 
24 static bool _woodenSupports = false;
25 static uint8_t _callCount = 0;
26 static function_call _calls[256] = {};
27 static paint_struct _paintStructs = {};
28 
29 namespace PaintIntercept
30 {
31     static uint8_t InterceptWoodenASupports(registers* regs);
32     static uint8_t InterceptWoodenBSupports(registers* regs);
33     static uint8_t InterceptMetalASupports(registers* regs);
34     static uint8_t InterceptMetalBSupports(registers* regs);
35     static uint8_t InterceptPaint6C(registers* regs);
36     static uint8_t InterceptPaint7C(registers* regs);
37     static uint8_t InterceptPaint8C(registers* regs);
38     static uint8_t InterceptPaint9C(registers* regs);
39     static uint8_t InterceptPaintFull(uint8_t function, registers* regs);
40 
41     bool PaintMetalSupports(
42         uint8_t function, int supportType, uint8_t segment, int special, int height, uint32_t imageColourFlags,
43         const support_height* supportSegments);
44     bool PaintWoodenSupports(
45         uint8_t function, int supportType, int special, int height, uint32_t imageColourFlags, bool* underground,
46         const paint_struct* prependTo);
47     static void CheckSegmentSupportHeight(const support_height* supportSegments);
48 
InitHooks()49     void InitHooks()
50     {
51         addhook(0x006629BC, InterceptWoodenASupports);
52         addhook(0x00662D5C, InterceptWoodenBSupports);
53 
54         addhook(0x00663105, InterceptMetalASupports);
55         addhook(0x00663584, InterceptMetalBSupports);
56 
57         addhook(0x006861AC, InterceptPaint6C);
58         addhook(0x00686337, InterceptPaint6C);
59         addhook(0x006864D0, InterceptPaint6C);
60         addhook(0x0068666B, InterceptPaint6C);
61 
62         addhook(0x00686806, InterceptPaint7C);
63         addhook(0x006869B2, InterceptPaint7C);
64         addhook(0x00686B6F, InterceptPaint7C);
65         addhook(0x00686D31, InterceptPaint7C);
66 
67         addhook(0x00686EF0, InterceptPaint8C);
68         addhook(0x00687056, InterceptPaint8C);
69         addhook(0x006871C8, InterceptPaint8C);
70         addhook(0x0068733C, InterceptPaint8C);
71 
72         addhook(0x006874B0, InterceptPaint9C);
73         addhook(0x00687618, InterceptPaint9C);
74         addhook(0x0068778C, InterceptPaint9C);
75         addhook(0x00687902, InterceptPaint9C);
76     }
77 
PaintWoodenSupports(uint8_t function,int supportType,int special,int height,uint32_t imageColourFlags,bool * underground,const paint_struct * prependTo)78     bool PaintWoodenSupports(
79         uint8_t function, int supportType, int special, int height, uint32_t imageColourFlags, bool* underground,
80         const paint_struct* prependTo)
81     {
82         function_call* call = &_calls[_callCount];
83         call->function = function;
84         call->supports.type = supportType;
85         call->supports.special = special;
86         call->supports.height = height;
87         call->supports.colour_flags = imageColourFlags;
88 
89         call->supports.prepend_to = SPR_NONE;
90         if (prependTo != nullptr)
91         {
92             for (int i = 0; i < _callCount; i++)
93             {
94                 if (&_calls[i].paint.output_struct == prependTo)
95                 {
96                     call->supports.prepend_to = _calls[i].paint.image_id;
97                     break;
98                 }
99             }
100         }
101 
102         _callCount++;
103 
104         return _woodenSupports;
105     }
106 
PaintMetalSupports(uint8_t function,int supportType,uint8_t segment,int special,int height,uint32_t imageColourFlags,const support_height * supportSegments)107     bool PaintMetalSupports(
108         uint8_t function, int supportType, uint8_t segment, int special, int height, uint32_t imageColourFlags,
109         const support_height* supportSegments)
110     {
111         CheckSegmentSupportHeight(supportSegments);
112 
113         function_call* call = &_calls[_callCount];
114         call->function = function;
115         call->supports.type = supportType;
116         call->supports.segment = segment;
117         call->supports.special = special;
118         call->supports.height = height;
119         call->supports.colour_flags = imageColourFlags;
120 
121         _callCount++;
122 
123         return false;
124     }
125 
Paint6C(uint32_t imageID,int8_t xOffset,int8_t yOffset,int16_t boundBoxLengthX,int16_t boundBoxLengthY,int8_t boundBoxLengthZ,int16_t zOffset,uint32_t rotation)126     static paint_struct* Paint6C(
127         uint32_t imageID, int8_t xOffset, int8_t yOffset, int16_t boundBoxLengthX, int16_t boundBoxLengthY,
128         int8_t boundBoxLengthZ, int16_t zOffset, uint32_t rotation)
129     {
130         function_call* call = &_calls[_callCount];
131         call->function = PAINT_98196C;
132         call->paint.image_id = imageID;
133         call->paint.offset = { xOffset, yOffset };
134         call->paint.bound_box_length = { boundBoxLengthX, boundBoxLengthY, boundBoxLengthZ };
135         call->paint.z_offset = zOffset;
136         call->paint.rotation = rotation;
137 
138         _callCount++;
139 
140         return &call->paint.output_struct;
141     }
142 
PaintFull(uint8_t function,uint32_t imageID,int8_t xOffset,int8_t yOffset,int16_t boundBoxLengthX,int16_t boundBoxLengthY,int8_t boundBoxLengthZ,int16_t zOffset,int16_t boundBoxOffsetX,int16_t boundBoxOffsetY,int16_t boundBoxOffsetZ,uint32_t rotation)143     static paint_struct* PaintFull(
144         uint8_t function, uint32_t imageID, int8_t xOffset, int8_t yOffset, int16_t boundBoxLengthX, int16_t boundBoxLengthY,
145         int8_t boundBoxLengthZ, int16_t zOffset, int16_t boundBoxOffsetX, int16_t boundBoxOffsetY, int16_t boundBoxOffsetZ,
146         uint32_t rotation)
147     {
148         function_call* call = &_calls[_callCount];
149         call->function = function;
150         call->paint.image_id = imageID;
151         call->paint.offset = { xOffset, yOffset };
152         call->paint.bound_box_length = { boundBoxLengthX, boundBoxLengthY, boundBoxLengthZ };
153         call->paint.bound_box_offset = { boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ };
154         call->paint.z_offset = zOffset;
155         call->paint.rotation = rotation;
156 
157         _callCount++;
158 
159         return &call->paint.output_struct;
160     }
161 
PaintFull(uint8_t function,uint32_t image_id,const CoordsXYZ & offset,const CoordsXYZ & boundBoxLength,const CoordsXYZ & boundBoxOffset,uint32_t rotation)162     static paint_struct* PaintFull(
163         uint8_t function, uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxLength,
164         const CoordsXYZ& boundBoxOffset, uint32_t rotation)
165     {
166         return PaintFull(
167             function, image_id, static_cast<int8_t>(offset.x), static_cast<int8_t>(offset.y),
168             static_cast<int16_t>(boundBoxLength.x), static_cast<int16_t>(boundBoxLength.y),
169             static_cast<int16_t>(boundBoxLength.z), static_cast<int16_t>(offset.z), static_cast<int16_t>(boundBoxOffset.x),
170             static_cast<int16_t>(boundBoxOffset.y), static_cast<int16_t>(boundBoxOffset.z), rotation);
171     }
172 
ClearCalls()173     void ClearCalls()
174     {
175         _callCount = 0;
176         memset(_calls, 0, sizeof(_calls));
177     }
178 
GetCalls(function_call * buffer)179     int GetCalls(function_call* buffer)
180     {
181         memcpy(buffer, _calls, _callCount * sizeof(function_call));
182         return _callCount;
183     }
184 
SetSimulateWoodenSupports(bool enabled)185     void SetSimulateWoodenSupports(bool enabled)
186     {
187         _woodenSupports = enabled;
188     }
189 
InterceptMetalASupports(registers * regs)190     static uint8_t InterceptMetalASupports(registers* regs)
191     {
192         bool output = PaintMetalSupports(
193             SUPPORTS_METAL_A, regs->edi, regs->ebx, (int16_t)regs->ax, regs->dx, regs->ebp, gSupportSegments);
194 
195         return output ? X86_FLAG_CARRY : 0;
196     }
197 
InterceptMetalBSupports(registers * regs)198     static uint8_t InterceptMetalBSupports(registers* regs)
199     {
200         bool output = PaintMetalSupports(
201             SUPPORTS_METAL_B, regs->edi, regs->ebx, (int16_t)regs->ax, regs->dx, regs->ebp, gSupportSegments);
202 
203         return output ? X86_FLAG_CARRY : 0;
204     }
205 
CheckSegmentSupportHeight(const support_height * supportSegments)206     static void CheckSegmentSupportHeight(const support_height* supportSegments)
207     {
208         bool hasChanged = false;
209         for (int i = 0; i < 9; i++)
210         {
211             if (supportSegments[i].height != 0)
212                 hasChanged = true;
213             if (supportSegments[i].slope != 0xFF)
214                 hasChanged = true;
215         }
216 
217         if (!hasChanged)
218         {
219             return;
220         }
221 
222         function_call call = {};
223         call.function = SET_SEGMENT_HEIGHT;
224 
225         _calls[_callCount] = call;
226         _callCount++;
227     }
228 
InterceptWoodenASupports(registers * regs)229     static uint8_t InterceptWoodenASupports(registers* regs)
230     {
231         bool cf = false;
232         regs->al = PaintWoodenSupports(
233             SUPPORTS_WOOD_A, regs->edi, regs->ax, regs->dx, regs->ebp, &cf, gWoodenSupportsPrependTo);
234 
235         if (cf)
236         {
237             return X86_FLAG_CARRY;
238         }
239 
240         return 0;
241     }
242 
InterceptWoodenBSupports(registers * regs)243     static uint8_t InterceptWoodenBSupports(registers* regs)
244     {
245         bool cf = false;
246         regs->al = PaintWoodenSupports(
247             SUPPORTS_WOOD_B, regs->edi, regs->ax, regs->dx, regs->ebp, &cf, gWoodenSupportsPrependTo);
248 
249         if (cf)
250         {
251             return X86_FLAG_CARRY;
252         }
253 
254         return 0;
255     }
256 
InterceptPaint6C(registers * regs)257     static uint8_t InterceptPaint6C(registers* regs)
258     {
259         if ((regs->ebp & 0x03) != RCT2_CurrentRotation)
260         {
261             // Log error
262             log_error("Ebp is different from current rotation");
263         }
264 
265         paint_struct* out = Paint6C(
266             regs->ebx, (int8_t)regs->al, (int8_t)regs->cl, (int16_t)regs->di, (int16_t)regs->si, (int8_t)regs->ah, regs->dx,
267             regs->ebp & 0x03);
268 
269         if (out == nullptr)
270         {
271             return X86_FLAG_CARRY;
272         }
273 
274         regs->ebp = (int)out;
275         regs->al = 1;
276         return 0;
277     }
278 
InterceptPaint7C(registers * regs)279     static uint8_t InterceptPaint7C(registers* regs)
280     {
281         return InterceptPaintFull(PAINT_98197C, regs);
282     }
283 
InterceptPaint8C(registers * regs)284     static uint8_t InterceptPaint8C(registers* regs)
285     {
286         return InterceptPaintFull(PAINT_98198C, regs);
287     }
288 
InterceptPaint9C(registers * regs)289     static uint8_t InterceptPaint9C(registers* regs)
290     {
291         return InterceptPaintFull(PAINT_98199C, regs);
292     }
293 
InterceptPaintFull(uint8_t function,registers * regs)294     static uint8_t InterceptPaintFull(uint8_t function, registers* regs)
295     {
296         if ((regs->ebp & 0x03) != RCT2_CurrentRotation)
297         {
298             // Log error
299             log_error("Ebp is different from current rotation");
300         }
301 
302         LocationXYZ16 boundOffset = { RCT2_PaintBoundBoxOffsetX, RCT2_PaintBoundBoxOffsetY, RCT2_PaintBoundBoxOffsetZ };
303 
304         paint_struct* out = PaintFull(
305             function, regs->ebx, (int8_t)regs->al, (int8_t)regs->cl, (int16_t)regs->di, (int16_t)regs->si, (int8_t)regs->ah,
306             regs->dx, boundOffset.x, boundOffset.y, boundOffset.z, regs->ebp & 0x03);
307 
308         if (out == nullptr)
309         {
310             return X86_FLAG_CARRY;
311         }
312 
313         regs->ebp = (int)out;
314         return 0;
315     }
316 }; // namespace PaintIntercept
317 
wooden_a_supports_paint_setup(paint_session * session,int supportType,int special,int height,uint32_t imageColourFlags,bool * underground)318 bool wooden_a_supports_paint_setup(
319     paint_session* session, int supportType, int special, int height, uint32_t imageColourFlags, bool* underground)
320 {
321     return PaintIntercept::PaintWoodenSupports(
322         SUPPORTS_WOOD_A, supportType, special, height, imageColourFlags, underground, gPaintSession.WoodenSupportsPrependTo);
323 }
324 
wooden_b_supports_paint_setup(paint_session * session,int supportType,int special,int height,uint32_t imageColourFlags,bool * underground)325 bool wooden_b_supports_paint_setup(
326     paint_session* session, int supportType, int special, int height, uint32_t imageColourFlags, bool* underground)
327 {
328     return PaintIntercept::PaintWoodenSupports(
329         SUPPORTS_WOOD_B, supportType, special, height, imageColourFlags, underground, gPaintSession.WoodenSupportsPrependTo);
330 }
331 
metal_a_supports_paint_setup(paint_session * session,uint8_t supportType,uint8_t segment,int special,int height,uint32_t imageColourFlags)332 bool metal_a_supports_paint_setup(
333     paint_session* session, uint8_t supportType, uint8_t segment, int special, int height, uint32_t imageColourFlags)
334 {
335     return PaintIntercept::PaintMetalSupports(
336         SUPPORTS_METAL_A, supportType, segment, special, height, imageColourFlags, gPaintSession.SupportSegments);
337 }
338 
metal_b_supports_paint_setup(paint_session * session,uint8_t supportType,uint8_t segment,int special,int height,uint32_t imageColourFlags)339 bool metal_b_supports_paint_setup(
340     paint_session* session, uint8_t supportType, uint8_t segment, int special, int height, uint32_t imageColourFlags)
341 {
342     return PaintIntercept::PaintMetalSupports(
343         SUPPORTS_METAL_B, supportType, segment, special, height, imageColourFlags, gPaintSession.SupportSegments);
344 }
345 
PaintAddImageAsParent(paint_session * session,uint32_t image_id,int8_t x_offset,int8_t y_offset,int16_t bound_box_length_x,int16_t bound_box_length_y,int8_t bound_box_length_z,int16_t z_offset)346 paint_struct* PaintAddImageAsParent(
347     paint_session* session, uint32_t image_id, int8_t x_offset, int8_t y_offset, int16_t bound_box_length_x,
348     int16_t bound_box_length_y, int8_t bound_box_length_z, int16_t z_offset)
349 {
350     return PaintIntercept::Paint6C(
351         image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
352         session->CurrentRotation);
353 }
354 
PaintAddImageAsParent(paint_session * session,uint32_t image_id,const CoordsXYZ & offset,const CoordsXYZ & boundBoxSize)355 paint_struct* PaintAddImageAsParent(
356     paint_session* session, uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxSize)
357 {
358     return PaintIntercept::Paint6C(
359         image_id, offset.x, offset.y, boundBoxSize.x, boundBoxSize.y, boundBoxSize.z, offset.z, session->CurrentRotation);
360 }
361 
PaintAddImageAsParent(paint_session * session,uint32_t image_id,int8_t x_offset,int8_t y_offset,int16_t bound_box_length_x,int16_t bound_box_length_y,int8_t bound_box_length_z,int16_t z_offset,int16_t bound_box_offset_x,int16_t bound_box_offset_y,int16_t bound_box_offset_z)362 paint_struct* PaintAddImageAsParent(
363     paint_session* session, uint32_t image_id, int8_t x_offset, int8_t y_offset, int16_t bound_box_length_x,
364     int16_t bound_box_length_y, int8_t bound_box_length_z, int16_t z_offset, int16_t bound_box_offset_x,
365     int16_t bound_box_offset_y, int16_t bound_box_offset_z)
366 {
367     return PaintIntercept::PaintFull(
368         PAINT_98197C, image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
369         bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, session->CurrentRotation);
370 }
371 
PaintAddImageAsParent(paint_session * session,uint32_t image_id,const CoordsXYZ & offset,const CoordsXYZ & boundBoxSize,const CoordsXYZ & boundBoxOffset)372 paint_struct* PaintAddImageAsParent(
373     paint_session* session, uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxSize,
374     const CoordsXYZ& boundBoxOffset)
375 {
376     return PaintAddImageAsParent(
377         session, image_id, offset.x, offset.y, boundBoxSize.x, boundBoxSize.y, boundBoxSize.z, offset.z, boundBoxOffset.x,
378         boundBoxOffset.y, boundBoxOffset.z);
379 }
380 
PaintAddImageAsChild(paint_session * session,uint32_t image_id,const CoordsXYZ & offset,const CoordsXYZ & boundBoxLength,const CoordsXYZ & boundBoxOffset)381 paint_struct* PaintAddImageAsChild(
382     paint_session* session, uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxLength,
383     const CoordsXYZ& boundBoxOffset)
384 {
385     return PaintIntercept::PaintFull(
386         PAINT_98198C_COORDS, image_id, offset, boundBoxLength, boundBoxOffset, session->CurrentRotation);
387 }
388 
PaintAddImageAsOrphan(paint_session * session,uint32_t image_id,int8_t x_offset,int8_t y_offset,int16_t bound_box_length_x,int16_t bound_box_length_y,int8_t bound_box_length_z,int16_t z_offset,int16_t bound_box_offset_x,int16_t bound_box_offset_y,int16_t bound_box_offset_z)389 paint_struct* PaintAddImageAsOrphan(
390     paint_session* session, uint32_t image_id, int8_t x_offset, int8_t y_offset, int16_t bound_box_length_x,
391     int16_t bound_box_length_y, int8_t bound_box_length_z, int16_t z_offset, int16_t bound_box_offset_x,
392     int16_t bound_box_offset_y, int16_t bound_box_offset_z)
393 {
394     return PaintIntercept::PaintFull(
395         PAINT_98198C, image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
396         bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, session->CurrentRotation);
397 }
398 
PaintAddImageAsChild(paint_session * session,uint32_t image_id,int8_t x_offset,int8_t y_offset,int16_t bound_box_length_x,int16_t bound_box_length_y,int8_t bound_box_length_z,int16_t z_offset,int16_t bound_box_offset_x,int16_t bound_box_offset_y,int16_t bound_box_offset_z)399 paint_struct* PaintAddImageAsChild(
400     paint_session* session, uint32_t image_id, int8_t x_offset, int8_t y_offset, int16_t bound_box_length_x,
401     int16_t bound_box_length_y, int8_t bound_box_length_z, int16_t z_offset, int16_t bound_box_offset_x,
402     int16_t bound_box_offset_y, int16_t bound_box_offset_z)
403 {
404     return PaintIntercept::PaintFull(
405         PAINT_98199C, image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
406         bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, session->CurrentRotation);
407 }
408 
PaintAttachToPreviousPS(paint_session * session,uint32_t image_id,int16_t x,int16_t y)409 bool PaintAttachToPreviousPS(paint_session* session, uint32_t image_id, int16_t x, int16_t y)
410 {
411     return false;
412 }
413