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