1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2006, 2007 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27 
28 #include "modules/module_grp.h"
29 
30 #include <boost/algorithm/string.hpp>
31 #include <functional>
32 #include <iostream>
33 #include <string>
34 #include <vector>
35 
36 #include "effects/effect.h"
37 #include "effects/effect_factory.h"
38 #include "libreallive/bytecode.h"
39 #include "libreallive/expression.h"
40 #include "libreallive/gameexe.h"
41 #include "long_operations/wait_long_operation.h"
42 #include "long_operations/zoom_long_operation.h"
43 #include "machine/general_operations.h"
44 #include "machine/rlmachine.h"
45 #include "machine/rloperation.h"
46 #include "machine/rloperation/argc_t.h"
47 #include "machine/rloperation/complex_t.h"
48 #include "machine/rloperation/default_value.h"
49 #include "machine/rloperation/rect_t.h"
50 #include "machine/rloperation/rgb_colour_t.h"
51 #include "machine/rloperation/special_t.h"
52 #include "systems/base/colour.h"
53 #include "systems/base/graphics_stack_frame.h"
54 #include "systems/base/graphics_system.h"
55 #include "systems/base/surface.h"
56 #include "systems/base/system.h"
57 #include "systems/base/text_system.h"
58 #include "utilities/graphics.h"
59 
60 using std::get;
61 
62 namespace fs = boost::filesystem;
63 
64 namespace {
65 
66 // Constant names used in the graphics stack.
67 const std::string GRP_ALLOC = "allocDC";
68 const std::string GRP_WIPE = "wipe";
69 const std::string GRP_COPY = "copy";
70 const std::string GRP_DISPLAY = "display";
71 const std::string GRP_LOAD = "grpLoad";
72 const std::string GRP_OPEN = "grpOpen";
73 const std::string GRP_OPENBG = "grpOpenBg";
74 
blitDC1toDC0(RLMachine & machine)75 void blitDC1toDC0(RLMachine& machine) {
76   GraphicsSystem& graphics = machine.system().graphics();
77 
78   std::shared_ptr<Surface> src = graphics.GetDC(1);
79   std::shared_ptr<Surface> dst = graphics.GetDC(0);
80 
81   // Blit DC1 onto DC0, with full opacity, and end the operation
82   src->BlitToSurface(*dst, src->GetRect(), dst->GetRect(), 255);
83 
84   // Mark that the background should be DC0 instead of the Haikei.
85   graphics.set_graphics_background(BACKGROUND_DC0);
86 
87   // Promote the objects if we're in normal mode. If we're restoring the
88   // graphics stack, we already have our layers promoted.
89   if (!machine.replaying_graphics_stack())
90     graphics.ClearAndPromoteObjects();
91 }
92 
93 // Performs half the grunt work of a recOpen command; Copies DC0 to DC1, loads
94 // a graphics file, and then composites that file to DC1.
95 //
96 // Kanon uses the recOpen('?', ...) form for rendering Last Regrets. This isn't
97 // documented in the rldev manual, and we must check for that case.
loadImageToDC1(RLMachine & machine,std::string name,const Rect & srcRect,const Point & dest,int opacity,bool useAlpha)98 void loadImageToDC1(RLMachine& machine,
99                     std::string name,
100                     const Rect& srcRect,
101                     const Point& dest,
102                     int opacity,
103                     bool useAlpha) {
104   GraphicsSystem& graphics = machine.system().graphics();
105 
106   if (name != "?") {
107     if (name == "???")
108       name = graphics.default_grp_name();
109 
110     std::shared_ptr<Surface> dc0 = graphics.GetDC(0);
111     std::shared_ptr<Surface> dc1 = graphics.GetDC(1);
112 
113     // Inclusive ranges are a monstrosity to computer people
114     Size size = srcRect.size() + Size(1, 1);
115 
116     dc0->BlitToSurface(*dc1, dc0->GetRect(), dc0->GetRect(), 255);
117 
118     // Load the section of the image file on top of dc1
119     std::shared_ptr<const Surface> surface(
120         graphics.GetSurfaceNamedAndMarkViewed(machine, name));
121     surface->BlitToSurface(*graphics.GetDC(1),
122                            Rect(srcRect.origin(), size),
123                            Rect(dest, size),
124                            opacity,
125                            useAlpha);
126   }
127 }
128 
loadDCToDC1(RLMachine & machine,int srcDc,const Rect & srcRect,const Point & dest,int opacity)129 void loadDCToDC1(RLMachine& machine,
130                  int srcDc,
131                  const Rect& srcRect,
132                  const Point& dest,
133                  int opacity) {
134   GraphicsSystem& graphics = machine.system().graphics();
135   std::shared_ptr<Surface> dc1 = graphics.GetDC(1);
136   std::shared_ptr<Surface> src = graphics.GetDC(srcDc);
137 
138   // Inclusive ranges are a monstrosity to computer people
139   Size size = srcRect.size() + Size(1, 1);
140 
141   src->BlitToSurface(
142       *dc1, Rect(srcRect.origin(), size), Rect(dest, size), opacity, false);
143 }
144 
performEffect(RLMachine & machine,const std::shared_ptr<Surface> & src,const std::shared_ptr<Surface> & dst,int selnum)145 void performEffect(RLMachine& machine,
146                    const std::shared_ptr<Surface>& src,
147                    const std::shared_ptr<Surface>& dst,
148                    int selnum) {
149   if (!machine.replaying_graphics_stack()) {
150     LongOperation* lop = EffectFactory::BuildFromSEL(machine, src, dst, selnum);
151     machine.PushLongOperation(lop);
152   }
153 }
154 
performEffect(RLMachine & machine,const std::shared_ptr<Surface> & src,const std::shared_ptr<Surface> & dst,int time,int style,int direction,int interpolation,int xsize,int ysize,int a,int b,int c)155 void performEffect(RLMachine& machine,
156                    const std::shared_ptr<Surface>& src,
157                    const std::shared_ptr<Surface>& dst,
158                    int time,
159                    int style,
160                    int direction,
161                    int interpolation,
162                    int xsize,
163                    int ysize,
164                    int a,
165                    int b,
166                    int c) {
167   if (!machine.replaying_graphics_stack()) {
168     LongOperation* lop = EffectFactory::Build(machine,
169                                               src,
170                                               dst,
171                                               time,
172                                               style,
173                                               direction,
174                                               interpolation,
175                                               xsize,
176                                               ysize,
177                                               a,
178                                               b,
179                                               c);
180     machine.PushLongOperation(lop);
181   }
182 }
183 
184 // We don't hide text windows when replaying the stack because hiding the
185 // window won't be undone like it normally is!
performHideAllTextWindows(RLMachine & machine)186 void performHideAllTextWindows(RLMachine& machine) {
187   if (!machine.replaying_graphics_stack()) {
188     machine.system().text().HideAllTextWindows();
189   }
190 }
191 
192 // Common code to all the openBg commands.
OpenBgPrelude(RLMachine & machine,const std::string & filename)193 void OpenBgPrelude(RLMachine& machine, const std::string& filename) {
194   if (!boost::starts_with(filename, "?")) {
195     GraphicsSystem& graphics = machine.system().graphics();
196     graphics.set_default_grp_name(filename);
197 
198     // Only clear the stack when we are the command setting the background.
199     graphics.ClearStack();
200   }
201 }
202 
203 // Implements op<1:Grp:00015, 0>, fun allocDC('DC', 'width', 'height').
204 //
205 // Allocates a blank width * height bitmap in dc. Any DC apart from DC 0 may be
206 // allocated thus, although DC 1 is never given a size smaller than the screen
207 // resolution. Any previous contents of dc are erased.
208 struct allocDC
209     : public RLOpcode<IntConstant_T, IntConstant_T, IntConstant_T> {
operator ()__anon19ca9ffc0111::allocDC210   void operator()(RLMachine& machine, int dc, int width, int height) {
211     machine.system().graphics().AllocateDC(dc, Size(width, height));
212   }
213 };
214 
215 // Implements op<1:Grp:00031, 0>, fun wipe('DC', 'r', 'g', 'b')
216 //
217 // Fills dc with the colour indicated by the given RGB triplet.
218 struct wipe : public RLOpcode<IntConstant_T,
219                              IntConstant_T,
220                              IntConstant_T,
221                              IntConstant_T> {
operator ()__anon19ca9ffc0111::wipe222   void operator()(RLMachine& machine, int dc, int r, int g, int b) {
223     machine.system().graphics().GetDC(dc)->Fill(RGBAColour(r, g, b));
224   }
225 };
226 
227 struct shake : public RLOpcode<IntConstant_T> {
operator ()__anon19ca9ffc0111::shake228   void operator()(RLMachine& machine, int spec) {
229     machine.system().graphics().QueueShakeSpec(spec);
230 
231     WaitLongOperation* wait_op = new WaitLongOperation(machine);
232     wait_op->BreakOnEvent(std::bind(StopShaking, std::ref(machine)));
233     machine.PushLongOperation(wait_op);
234   }
235 
StopShaking__anon19ca9ffc0111::shake236   static bool StopShaking(RLMachine& machine) {
237     return machine.system().graphics().IsShaking() == false;
238   }
239 };
240 
241 // -----------------------------------------------------------------------
242 // {grp,rec}Load Commands
243 // -----------------------------------------------------------------------
244 
245 // Implements the {grp,rec}(Mask)?Load family of functions.
246 //
247 // Loads filename into dc; note that filename may not be '???'.
248 //
249 // Since this function deals with the entire screen (and therefore doesn't need
250 // to worry about the difference between grp/rec coordinate space), we write
251 // one function for both versions.
252 struct load_1
253     : public RLOpcode<StrConstant_T, IntConstant_T, DefaultIntValue_T<255>> {
254   bool use_alpha_;
load_1__anon19ca9ffc0111::load_1255   explicit load_1(bool in) : use_alpha_(in) {}
256 
operator ()__anon19ca9ffc0111::load_1257   void operator()(RLMachine& machine, string filename, int dc, int opacity) {
258     GraphicsSystem& graphics = machine.system().graphics();
259 
260     std::shared_ptr<const Surface> surface(
261         graphics.GetSurfaceNamedAndMarkViewed(machine, filename));
262 
263     if (dc != 0 && dc != 1) {
264       graphics.AllocateDC(dc, surface->GetSize());
265     }
266 
267     surface->BlitToSurface(*graphics.GetDC(dc),
268                            surface->GetRect(),
269                            surface->GetRect(),
270                            opacity,
271                            use_alpha_);
272   }
273 };
274 
275 // Implements op<1:Grp:00050, 3>, fun grpLoad(strC 'filename', 'DC',
276 // 'x1', 'y1', 'x2', 'y2', 'dx', 'dy', 'alpha').
277 //
278 // Loads filename into dc; note that filename may not be '???'. Using this
279 // form, the given area of the bitmap is loaded at the given location.
280 template <typename SPACE>
281 struct load_3 : public RLOpcode<StrConstant_T,
282                                IntConstant_T,
283                                Rect_T<SPACE>,
284                                Point_T,
285                                DefaultIntValue_T<255>> {
286   bool use_alpha_;
load_3__anon19ca9ffc0111::load_3287   explicit load_3(bool in) : use_alpha_(in) {}
288 
operator ()__anon19ca9ffc0111::load_3289   void operator()(RLMachine& machine,
290                   string filename,
291                   int dc,
292                   Rect srcRect,
293                   Point dest,
294                   int opacity) {
295     GraphicsSystem& graphics = machine.system().graphics();
296     std::shared_ptr<const Surface> surface(
297         graphics.GetSurfaceNamedAndMarkViewed(machine, filename));
298 
299     Rect destRect = Rect(dest, srcRect.size());
300 
301     if (dc != 0 && dc != 1) {
302       graphics.SetMinimumSizeForDC(dc, surface->GetSize());
303     }
304 
305     surface->BlitToSurface(
306         *graphics.GetDC(dc), srcRect, destRect, opacity, use_alpha_);
307   }
308 };
309 
310 // -----------------------------------------------------------------------
311 // {grp,rec}Display
312 // -----------------------------------------------------------------------
313 
314 struct display_1
315     : public RLOpcode<IntConstant_T, IntConstant_T, IntConstant_T> {
operator ()__anon19ca9ffc0111::display_1316   void operator()(RLMachine& machine, int dc, int effectNum, int opacity) {
317     Rect src;
318     Point dest;
319     GetSELPointAndRect(machine, effectNum, src, dest);
320 
321     GraphicsSystem& graphics = machine.system().graphics();
322 
323     std::shared_ptr<Surface> before = graphics.RenderToSurface();
324 
325     loadDCToDC1(machine, dc, src, dest, opacity);
326     blitDC1toDC0(machine);
327 
328     std::shared_ptr<Surface> after = graphics.RenderToSurface();
329     performEffect(machine, after, before, effectNum);
330   }
331 };
332 
333 struct display_0 : public RLOpcode<IntConstant_T, IntConstant_T> {
334   display_1 delegate_;
335 
operator ()__anon19ca9ffc0111::display_0336   void operator()(RLMachine& machine, int dc, int effectNum) {
337     std::vector<int> selEffect = GetSELEffect(machine, effectNum);
338     delegate_(machine, dc, effectNum, selEffect.at(14));
339   }
340 };
341 
342 template <typename SPACE>
343 struct display_3 : public RLOpcode<IntConstant_T,
344                                   IntConstant_T,
345                                   Rect_T<SPACE>,
346                                   Point_T,
347                                   IntConstant_T> {
operator ()__anon19ca9ffc0111::display_3348   void operator()(RLMachine& machine,
349                   int dc,
350                   int effectNum,
351                   Rect srcRect,
352                   Point dest,
353                   int opacity) {
354     GraphicsSystem& graphics = machine.system().graphics();
355 
356     std::shared_ptr<Surface> before = graphics.RenderToSurface();
357 
358     loadDCToDC1(machine, dc, srcRect, dest, opacity);
359     blitDC1toDC0(machine);
360 
361     std::shared_ptr<Surface> after = graphics.RenderToSurface();
362     performEffect(machine, after, before, effectNum);
363   }
364 };
365 
366 template <typename SPACE>
367 struct display_2
368     : public RLOpcode<IntConstant_T, IntConstant_T, Rect_T<SPACE>, Point_T> {
operator ()__anon19ca9ffc0111::display_2369   void operator()(RLMachine& machine,
370                   int dc,
371                   int effectNum,
372                   Rect src_rect,
373                   Point dest) {
374     int opacity = GetSELEffect(machine, effectNum).at(14);
375     display_3<SPACE>()(machine, dc, effectNum, src_rect, dest, opacity);
376   }
377 };
378 
379 template <typename SPACE>
380 struct display_4 : public RLOpcode<IntConstant_T,
381                                   Rect_T<SPACE>,
382                                   Point_T,
383                                   IntConstant_T,
384                                   IntConstant_T,
385                                   IntConstant_T,
386                                   IntConstant_T,
387                                   IntConstant_T,
388                                   IntConstant_T,
389                                   IntConstant_T,
390                                   IntConstant_T,
391                                   IntConstant_T,
392                                   IntConstant_T> {
operator ()__anon19ca9ffc0111::display_4393   void operator()(RLMachine& machine,
394                   int dc,
395                   Rect srcRect,
396                   Point dest,
397                   int time,
398                   int style,
399                   int direction,
400                   int interpolation,
401                   int xsize,
402                   int ysize,
403                   int a,
404                   int b,
405                   int opacity,
406                   int c) {
407     GraphicsSystem& graphics = machine.system().graphics();
408 
409     std::shared_ptr<Surface> before = graphics.RenderToSurface();
410 
411     loadDCToDC1(machine, dc, srcRect, dest, opacity);
412     blitDC1toDC0(machine);
413 
414     std::shared_ptr<Surface> after = graphics.RenderToSurface();
415     performEffect(machine,
416                   after,
417                   before,
418                   time,
419                   style,
420                   direction,
421                   interpolation,
422                   xsize,
423                   ysize,
424                   a,
425                   b,
426                   c);
427   }
428 };
429 
430 // -----------------------------------------------------------------------
431 // {grp,rec}Open
432 // -----------------------------------------------------------------------
433 
434 // Implements op<1:Grp:00076, 1>, fun grpOpen(strC 'filename', '#SEL',
435 // 'opacity').
436 //
437 // Load and display a bitmap. |filename| is loaded into DC1 with opacity
438 // |opacity|, and then is passed off to whatever transition effect, which will
439 // perform some intermediary steps and then render DC1 to DC0.
440 //
441 // TODO(erg): factor out the common code between grpOpens!
442 struct open_1
443     : public RLOpcode<StrConstant_T, IntConstant_T, IntConstant_T> {
444   bool use_alpha_;
open_1__anon19ca9ffc0111::open_1445   explicit open_1(bool in) : use_alpha_(in) {}
446 
operator ()__anon19ca9ffc0111::open_1447   void operator()(RLMachine& machine,
448                   string filename,
449                   int effectNum,
450                   int opacity) {
451     Rect src;
452     Point dest;
453     GetSELPointAndRect(machine, effectNum, src, dest);
454 
455     GraphicsSystem& graphics = machine.system().graphics();
456     std::shared_ptr<Surface> before = graphics.RenderToSurface();
457 
458     loadImageToDC1(machine, filename, src, dest, opacity, use_alpha_);
459     blitDC1toDC0(machine);
460 
461     std::shared_ptr<Surface> after = graphics.RenderToSurface();
462     performEffect(machine, after, before, effectNum);
463     performHideAllTextWindows(machine);
464   }
465 };
466 
467 // Implements op<1:Grp:00076, 0>, fun grpOpen(strC 'filename', '\#SEL').
468 //
469 // Load and display a bitmap. |filename| is loaded into DC1, and then is passed
470 // off to whatever transition effect, which will perform some intermediary
471 // steps and then render DC1 to DC0.
472 struct open_0 : public RLOpcode<StrConstant_T, IntConstant_T> {
473   open_1 delegate_;
open_0__anon19ca9ffc0111::open_0474   explicit open_0(bool in) : delegate_(in) {}
475 
operator ()__anon19ca9ffc0111::open_0476   void operator()(RLMachine& machine, string filename, int effectNum) {
477     std::vector<int> selEffect = GetSELEffect(machine, effectNum);
478     delegate_(machine, filename, effectNum, selEffect[14]);
479   }
480 };
481 
482 template <typename SPACE>
483 struct open_3 : public RLOpcode<StrConstant_T,
484                                IntConstant_T,
485                                Rect_T<SPACE>,
486                                Point_T,
487                                IntConstant_T> {
488   bool use_alpha_;
open_3__anon19ca9ffc0111::open_3489   explicit open_3(bool in) : use_alpha_(in) {}
490 
operator ()__anon19ca9ffc0111::open_3491   void operator()(RLMachine& machine,
492                   string filename,
493                   int effectNum,
494                   Rect srcRect,
495                   Point dest,
496                   int opacity) {
497     GraphicsSystem& graphics = machine.system().graphics();
498 
499     std::shared_ptr<Surface> before = graphics.RenderToSurface();
500 
501     // Kanon uses the recOpen('?', ...) form for rendering Last Regrets. This
502     // isn't documented in the rldev manual.
503     loadImageToDC1(machine, filename, srcRect, dest, opacity, use_alpha_);
504     blitDC1toDC0(machine);
505 
506     std::shared_ptr<Surface> after = graphics.RenderToSurface();
507     performEffect(machine, after, before, effectNum);
508     performHideAllTextWindows(machine);
509   }
510 };
511 
512 // Implements op<1:Grp:00076, 1>, fun grpOpen(strC 'filename', '\#SEL',
513 // 'opacity').
514 //
515 // Load and display a bitmap. |filename| is loaded into DC1 with opacity
516 // |opacity|, and then is passed off to whatever transition effect, which will
517 // perform some intermediary steps and then render DC1 to DC0.
518 template <typename SPACE>
519 struct open_2
520     : public RLOpcode<StrConstant_T, IntConstant_T, Rect_T<SPACE>, Point_T> {
521   open_3<SPACE> delegate_;
open_2__anon19ca9ffc0111::open_2522   explicit open_2(bool in) : delegate_(in) {}
523 
operator ()__anon19ca9ffc0111::open_2524   void operator()(RLMachine& machine,
525                   string filename,
526                   int effectNum,
527                   Rect src,
528                   Point dest) {
529     int opacity = GetSELEffect(machine, effectNum).at(14);
530     delegate_(machine, filename, effectNum, src, dest, opacity);
531   }
532 };
533 
534 template <typename SPACE>
535 struct open_4 : public RLOpcode<StrConstant_T,
536                                     Rect_T<SPACE>,
537                                     Point_T,
538                                     IntConstant_T,
539                                     IntConstant_T,
540                                     IntConstant_T,
541                                     IntConstant_T,
542                                     IntConstant_T,
543                                     IntConstant_T,
544                                     IntConstant_T,
545                                     IntConstant_T,
546                                     IntConstant_T,
547                                     IntConstant_T> {
548   bool use_alpha_;
open_4__anon19ca9ffc0111::open_4549   explicit open_4(bool in) : use_alpha_(in) {}
550 
operator ()__anon19ca9ffc0111::open_4551   void operator()(RLMachine& machine,
552                   string fileName,
553                   Rect srcRect,
554                   Point dest,
555                   int time,
556                   int style,
557                   int direction,
558                   int interpolation,
559                   int xsize,
560                   int ysize,
561                   int a,
562                   int b,
563                   int opacity,
564                   int c) {
565     GraphicsSystem& graphics = machine.system().graphics();
566 
567     std::shared_ptr<Surface> before = graphics.RenderToSurface();
568 
569     // Kanon uses the recOpen('?', ...) form for rendering Last Regrets. This
570     // isn't documented in the rldev manual.
571     loadImageToDC1(machine, fileName, srcRect, dest, opacity, use_alpha_);
572     blitDC1toDC0(machine);
573 
574     std::shared_ptr<Surface> after = graphics.RenderToSurface();
575     performEffect(machine,
576                   after,
577                   before,
578                   time,
579                   style,
580                   direction,
581                   interpolation,
582                   xsize,
583                   ysize,
584                   a,
585                   b,
586                   c);
587     performHideAllTextWindows(machine);
588   }
589 };
590 
591 struct openBg_1
592     : public RLOpcode<StrConstant_T, IntConstant_T, IntConstant_T> {
operator ()__anon19ca9ffc0111::openBg_1593   void operator()(RLMachine& machine,
594                   string fileName,
595                   int effectNum,
596                   int opacity) {
597     GraphicsSystem& graphics = machine.system().graphics();
598     Rect srcRect;
599     Point destPoint;
600     GetSELPointAndRect(machine, effectNum, srcRect, destPoint);
601 
602     OpenBgPrelude(machine, fileName);
603 
604     std::shared_ptr<Surface> before = graphics.RenderToSurface();
605 
606     loadImageToDC1(machine, fileName, srcRect, destPoint, opacity, false);
607     blitDC1toDC0(machine);
608 
609     std::shared_ptr<Surface> after = graphics.RenderToSurface();
610     performEffect(machine, after, before, effectNum);
611     performHideAllTextWindows(machine);
612   }
613 };
614 
615 struct openBg_0 : public RLOpcode<StrConstant_T, IntConstant_T> {
616   openBg_1 delegate_;
617 
operator ()__anon19ca9ffc0111::openBg_0618   void operator()(RLMachine& machine, string filename, int effectNum) {
619     std::vector<int> selEffect = GetSELEffect(machine, effectNum);
620     delegate_(machine, filename, effectNum, selEffect[14]);
621   }
622 };
623 
624 template <typename SPACE>
625 struct openBg_3 : public RLOpcode<StrConstant_T,
626                                  IntConstant_T,
627                                  Rect_T<SPACE>,
628                                  Point_T,
629                                  IntConstant_T> {
630   bool use_alpha_;
openBg_3__anon19ca9ffc0111::openBg_3631   explicit openBg_3(bool in) : use_alpha_(in) {}
632 
operator ()__anon19ca9ffc0111::openBg_3633   void operator()(RLMachine& machine,
634                   string fileName,
635                   int effectNum,
636                   Rect srcRect,
637                   Point destPt,
638                   int opacity) {
639     GraphicsSystem& graphics = machine.system().graphics();
640     OpenBgPrelude(machine, fileName);
641 
642     // Set the long operation for the correct transition long operation
643     std::shared_ptr<Surface> before = graphics.RenderToSurface();
644 
645     loadImageToDC1(machine, fileName, srcRect, destPt, opacity, use_alpha_);
646     blitDC1toDC0(machine);
647 
648     std::shared_ptr<Surface> after = graphics.RenderToSurface();
649     performEffect(machine, after, before, effectNum);
650     performHideAllTextWindows(machine);
651   }
652 };
653 
654 template <typename SPACE>
655 struct openBg_2
656     : public RLOpcode<StrConstant_T, IntConstant_T, Rect_T<SPACE>, Point_T> {
657   openBg_3<SPACE> delegate_;
openBg_2__anon19ca9ffc0111::openBg_2658   explicit openBg_2(bool in) : delegate_(in) {}
659 
operator ()__anon19ca9ffc0111::openBg_2660   void operator()(RLMachine& machine,
661                   string fileName,
662                   int effectNum,
663                   Rect srcRect,
664                   Point destPt) {
665     std::vector<int> selEffect = GetSELEffect(machine, effectNum);
666     delegate_(machine, fileName, effectNum, srcRect, destPt, selEffect[14]);
667   }
668 };
669 
670 template <typename SPACE>
671 struct openBg_4 : public RLOpcode<StrConstant_T,
672                                       Rect_T<SPACE>,
673                                       Point_T,
674                                       IntConstant_T,
675                                       IntConstant_T,
676                                       IntConstant_T,
677                                       IntConstant_T,
678                                       IntConstant_T,
679                                       IntConstant_T,
680                                       IntConstant_T,
681                                       IntConstant_T,
682                                       IntConstant_T,
683                                       IntConstant_T> {
684   bool use_alpha_;
openBg_4__anon19ca9ffc0111::openBg_4685   explicit openBg_4(bool in) : use_alpha_(in) {}
686 
operator ()__anon19ca9ffc0111::openBg_4687   void operator()(RLMachine& machine,
688                   string fileName,
689                   Rect srcRect,
690                   Point destPt,
691                   int time,
692                   int style,
693                   int direction,
694                   int interpolation,
695                   int xsize,
696                   int ysize,
697                   int a,
698                   int b,
699                   int opacity,
700                   int c) {
701     GraphicsSystem& graphics = machine.system().graphics();
702     OpenBgPrelude(machine, fileName);
703 
704     // Set the long operation for the correct transition long operation
705     std::shared_ptr<Surface> before = graphics.RenderToSurface();
706 
707     loadImageToDC1(machine, fileName, srcRect, destPt, opacity, use_alpha_);
708     blitDC1toDC0(machine);
709 
710     // Render the screen to a temporary
711     std::shared_ptr<Surface> after = graphics.RenderToSurface();
712     performEffect(machine,
713                   after,
714                   before,
715                   time,
716                   style,
717                   direction,
718                   interpolation,
719                   xsize,
720                   ysize,
721                   a,
722                   b,
723                   c);
724     performHideAllTextWindows(machine);
725   }
726 };
727 
728 // -----------------------------------------------------------------------
729 // {grp,rec}Copy
730 // -----------------------------------------------------------------------
731 template <typename SPACE>
732 struct copy_3 : public RLOpcode<Rect_T<SPACE>,
733                                IntConstant_T,
734                                Point_T,
735                                IntConstant_T,
736                                DefaultIntValue_T<255>> {
737   bool use_alpha_;
copy_3__anon19ca9ffc0111::copy_3738   explicit copy_3(bool in) : use_alpha_(in) {}
739 
operator ()__anon19ca9ffc0111::copy_3740   void operator()(RLMachine& machine,
741                   Rect srcRect,
742                   int src,
743                   Point destPoint,
744                   int dst,
745                   int opacity) {
746     // Copying to self is a noop
747     if (src == dst)
748       return;
749 
750     GraphicsSystem& graphics = machine.system().graphics();
751 
752     std::shared_ptr<Surface> sourceSurface = graphics.GetDC(src);
753 
754     if (dst != 0 && dst != 1) {
755       graphics.SetMinimumSizeForDC(dst, srcRect.size());
756     }
757 
758     sourceSurface->BlitToSurface(*graphics.GetDC(dst),
759                                  srcRect,
760                                  Rect(destPoint, srcRect.size()),
761                                  opacity,
762                                  use_alpha_);
763   }
764 };
765 
766 struct copy_1
767     : public RLOpcode<IntConstant_T, IntConstant_T, DefaultIntValue_T<255>> {
768   bool use_alpha_;
copy_1__anon19ca9ffc0111::copy_1769   explicit copy_1(bool in) : use_alpha_(in) {}
770 
operator ()__anon19ca9ffc0111::copy_1771   void operator()(RLMachine& machine, int src, int dst, int opacity) {
772     // Copying to self is a noop
773     if (src == dst)
774       return;
775 
776     GraphicsSystem& graphics = machine.system().graphics();
777 
778     std::shared_ptr<Surface> sourceSurface = graphics.GetDC(src);
779 
780     if (dst != 0 && dst != 1) {
781       graphics.SetMinimumSizeForDC(dst, sourceSurface->GetSize());
782     }
783 
784     sourceSurface->BlitToSurface(*graphics.GetDC(dst),
785                                  sourceSurface->GetRect(),
786                                  sourceSurface->GetRect(),
787                                  opacity,
788                                  use_alpha_);
789   }
790 };
791 
792 // -----------------------------------------------------------------------
793 // {grp,rec}Fill
794 // -----------------------------------------------------------------------
795 
796 struct fill_0 : public RLOpcode<IntConstant_T, RGBColour_T> {
operator ()__anon19ca9ffc0111::fill_0797   void operator()(RLMachine& machine, int dc, RGBAColour colour) {
798     // Justification: Maiden Halo uses fill(x, 0, 0, 0) as a synanom for clear
799     // and since it uses haikei, the DC0 needs to be transparent.
800     if (colour.r() == 0 && colour.g() == 0 && colour.b() == 0)
801       colour.set_alpha(0);
802 
803     machine.system().graphics().GetDC(dc)->Fill(colour);
804   }
805 };
806 
807 struct fill_1 : public RLOpcode<IntConstant_T, RGBMaybeAColour_T> {
operator ()__anon19ca9ffc0111::fill_1808   void operator()(RLMachine& machine, int dc, RGBAColour colour) {
809     machine.system().graphics().GetDC(dc)->Fill(colour);
810   }
811 };
812 
813 template <typename SPACE>
814 struct fill_3
815     : public RLOpcode<Rect_T<SPACE>, IntConstant_T, RGBMaybeAColour_T> {
operator ()__anon19ca9ffc0111::fill_3816   void operator()(RLMachine& machine,
817                   Rect destRect,
818                   int dc,
819                   RGBAColour colour) {
820     machine.system().graphics().GetDC(dc)->Fill(colour, destRect);
821   }
822 };
823 
824 struct invert_1 : public RLOpcode<IntConstant_T> {
operator ()__anon19ca9ffc0111::invert_1825   void operator()(RLMachine& machine, int dc) {
826     std::shared_ptr<Surface> surface = machine.system().graphics().GetDC(dc);
827     surface->Invert(surface->GetRect());
828   }
829 };
830 
831 template <typename SPACE>
832 struct invert_3 : public RLOpcode<Rect_T<SPACE>, IntConstant_T> {
operator ()__anon19ca9ffc0111::invert_3833   void operator()(RLMachine& machine, Rect rect, int dc) {
834     machine.system().graphics().GetDC(dc)->Invert(rect);
835   }
836 };
837 
838 struct mono_1 : public RLOpcode<IntConstant_T> {
operator ()__anon19ca9ffc0111::mono_1839   void operator()(RLMachine& machine, int dc) {
840     std::shared_ptr<Surface> surface = machine.system().graphics().GetDC(dc);
841     surface->Mono(surface->GetRect());
842   }
843 };
844 
845 template <typename SPACE>
846 struct mono_3 : public RLOpcode<Rect_T<SPACE>, IntConstant_T> {
operator ()__anon19ca9ffc0111::mono_3847   void operator()(RLMachine& machine, Rect rect, int dc) {
848     machine.system().graphics().GetDC(dc)->Mono(rect);
849   }
850 };
851 
852 struct colour_1 : public RLOpcode<IntConstant_T, RGBColour_T> {
operator ()__anon19ca9ffc0111::colour_1853   void operator()(RLMachine& machine, int dc, RGBAColour colour) {
854     std::shared_ptr<Surface> surface = machine.system().graphics().GetDC(dc);
855     surface->ApplyColour(colour.rgb(), surface->GetRect());
856   }
857 };
858 
859 template <typename SPACE>
860 struct colour_2
861     : public RLOpcode<Rect_T<SPACE>, IntConstant_T, RGBColour_T> {
operator ()__anon19ca9ffc0111::colour_2862   void operator()(RLMachine& machine, Rect rect, int dc, RGBAColour colour) {
863     std::shared_ptr<Surface> surface = machine.system().graphics().GetDC(dc);
864     surface->ApplyColour(colour.rgb(), rect);
865   }
866 };
867 
868 struct light_1 : public RLOpcode<IntConstant_T, IntConstant_T> {
operator ()__anon19ca9ffc0111::light_1869   void operator()(RLMachine& machine, int dc, int level) {
870     std::shared_ptr<Surface> surface = machine.system().graphics().GetDC(dc);
871     surface->ApplyColour(RGBColour(level, level, level), surface->GetRect());
872   }
873 };
874 
875 template <typename SPACE>
876 struct light_2
877     : public RLOpcode<Rect_T<SPACE>, IntConstant_T, IntConstant_T> {
operator ()__anon19ca9ffc0111::light_2878   void operator()(RLMachine& machine, Rect rect, int dc, int level) {
879     std::shared_ptr<Surface> surface = machine.system().graphics().GetDC(dc);
880     surface->ApplyColour(RGBColour(level, level, level), rect);
881   }
882 };
883 
884 // -----------------------------------------------------------------------
885 // {grp,rec}Fade
886 // -----------------------------------------------------------------------
887 
888 template <typename SPACE>
889 struct fade_7
890     : public RLOpcode<Rect_T<SPACE>, RGBColour_T, DefaultIntValue_T<0>> {
operator ()__anon19ca9ffc0111::fade_7891   void operator()(RLMachine& machine, Rect rect, RGBAColour colour, int time) {
892     GraphicsSystem& graphics = machine.system().graphics();
893     std::shared_ptr<Surface> before = graphics.RenderToSurface();
894     graphics.GetDC(0)->Fill(colour, rect);
895     std::shared_ptr<Surface> after = graphics.RenderToSurface();
896 
897     if (time > 0) {
898       performEffect(machine, after, before, time, 0, 0, 0, 0, 0, 0, 0, 0);
899     }
900   }
901 };
902 
903 template <typename SPACE>
904 struct fade_5
905     : public RLOpcode<Rect_T<SPACE>, IntConstant_T, DefaultIntValue_T<0>> {
906   fade_7<SPACE> delegate_;
907 
operator ()__anon19ca9ffc0111::fade_5908   void operator()(RLMachine& machine, Rect rect, int colour_num, int time) {
909     Gameexe& gexe = machine.system().gameexe();
910     const std::vector<int>& rgb = gexe("COLOR_TABLE", colour_num).ToIntVector();
911     delegate_(machine, rect, RGBAColour(rgb), time);
912   }
913 };
914 
915 struct fade_3 : public RLOpcode<RGBColour_T, DefaultIntValue_T<0>> {
916   fade_7<rect_impl::REC> delegate_;
917 
operator ()__anon19ca9ffc0111::fade_3918   void operator()(RLMachine& machine, RGBAColour colour, int time) {
919     Size screen_size = machine.system().graphics().screen_size();
920     delegate_(machine, Rect(0, 0, screen_size), colour, time);
921   }
922 };
923 
924 struct fade_1 : public RLOpcode<IntConstant_T, DefaultIntValue_T<0>> {
925   fade_7<rect_impl::REC> delegate_;
926 
operator ()__anon19ca9ffc0111::fade_1927   void operator()(RLMachine& machine, int colour_num, int time) {
928     Size screen_size = machine.system().graphics().screen_size();
929     Gameexe& gexe = machine.system().gameexe();
930     const std::vector<int>& rgb = gexe("COLOR_TABLE", colour_num).ToIntVector();
931     delegate_(machine, Rect(0, 0, screen_size), RGBAColour(rgb), time);
932   }
933 };
934 
935 // -----------------------------------------------------------------------
936 // {grp,rec}StretchBlit
937 // -----------------------------------------------------------------------
938 template <typename SPACE>
939 struct stretchBlit_1 : public RLOpcode<Rect_T<SPACE>,
940                                       IntConstant_T,
941                                       Rect_T<SPACE>,
942                                       IntConstant_T,
943                                       DefaultIntValue_T<255>> {
944   bool use_alpha_;
stretchBlit_1__anon19ca9ffc0111::stretchBlit_1945   explicit stretchBlit_1(bool in) : use_alpha_(in) {}
946 
operator ()__anon19ca9ffc0111::stretchBlit_1947   void operator()(RLMachine& machine,
948                   Rect src_rect,
949                   int src,
950                   Rect dst_rect,
951                   int dst,
952                   int opacity) {
953     // Copying to self is a noop
954     if (src == dst)
955       return;
956 
957     GraphicsSystem& graphics = machine.system().graphics();
958     std::shared_ptr<Surface> sourceSurface = graphics.GetDC(src);
959 
960     if (dst != 0 && dst != 1) {
961       graphics.SetMinimumSizeForDC(dst, sourceSurface->GetSize());
962     }
963 
964     sourceSurface->BlitToSurface(
965         *graphics.GetDC(dst), src_rect, dst_rect, opacity, use_alpha_);
966   }
967 };
968 
969 template <typename SPACE>
970 struct zoom : public RLOpcode<Rect_T<SPACE>,
971                              Rect_T<SPACE>,
972                              IntConstant_T,
973                              Rect_T<SPACE>,
974                              IntConstant_T> {
operator ()__anon19ca9ffc0111::zoom975   void operator()(RLMachine& machine,
976                   Rect frect,
977                   Rect trect,
978                   int srcDC,
979                   Rect drect,
980                   int time) {
981     GraphicsSystem& gs = machine.system().graphics();
982     gs.set_graphics_background(BACKGROUND_DC0);
983 
984     LongOperation* zoomOp = new ZoomLongOperation(
985         machine, gs.GetDC(0), gs.GetDC(srcDC), frect, trect, drect, time);
986     BlitAfterEffectFinishes* blitOp = new BlitAfterEffectFinishes(
987         zoomOp, gs.GetDC(srcDC), gs.GetDC(0), trect, drect);
988     machine.PushLongOperation(blitOp);
989   }
990 };
991 
992 // -----------------------------------------------------------------------
993 // {grp,rec}multi
994 // -----------------------------------------------------------------------
995 
996 // Defines the fairly complex parameter definition for the list of functions to
997 // call in a {grp,rec}Multi command.
998 typedef Argc_T<Special_T<
999     DefaultSpecialMapper,
1000     // 0:copy(strC 'filename')
1001     StrConstant_T,
1002     // 1:copy(strC 'filename', 'effect')
1003     Complex_T<StrConstant_T, IntConstant_T>,
1004     // 2:copy(strC 'filename', 'effect', 'alpha')
1005     Complex_T<StrConstant_T, IntConstant_T, IntConstant_T>,
1006     // 3:area(strC 'filename', 'x1', 'y1', 'x2', 'y2', 'dx', 'dy')
1007     Complex_T<StrConstant_T,
1008               IntConstant_T,
1009               IntConstant_T,
1010               IntConstant_T,
1011               IntConstant_T,
1012               IntConstant_T,
1013               IntConstant_T>,
1014     // 4:area(strC 'filename', 'x1', 'y1', 'x2', 'y2', 'dx', 'dy', 'alpha')
1015     Complex_T<StrConstant_T,
1016               IntConstant_T,
1017               IntConstant_T,
1018               IntConstant_T,
1019               IntConstant_T,
1020               IntConstant_T,
1021               IntConstant_T,
1022               IntConstant_T>>> MultiCommand;
1023 
1024 // -----------------------------------------------------------------------
1025 
1026 // Defines the weird multi commands. I will be the first to admit that the
1027 // following is fairly difficult to read; it comes from the quagmire of
1028 // composing Special_T and ComplexX_T templates.
1029 //
1030 // In the end, this operation struct simply Dispatches the Special/Complex
1031 // commands to functions and other operation structs that are clearer in
1032 // purpose.
1033 
1034 // All work is applied to DC 1.
1035 const int MULTI_TARGET_DC = 1;
1036 
1037 template <typename SPACE>
1038 struct multi_command {
1039   void handleMultiCommands(RLMachine& machine,
1040                            const MultiCommand::type& commands);
1041 };
1042 
1043 template <typename SPACE>
handleMultiCommands(RLMachine & machine,const MultiCommand::type & commands)1044 void multi_command<SPACE>::handleMultiCommands(
1045     RLMachine& machine,
1046     const MultiCommand::type& commands) {
1047   for (MultiCommand::type::const_iterator it = commands.begin();
1048        it != commands.end();
1049        it++) {
1050     switch (it->type) {
1051       case 0:
1052         // 0:copy(strC 'filename')
1053         if (it->first != "")
1054           load_1(true)(machine, it->first, MULTI_TARGET_DC, 255);
1055         break;
1056       case 1: {
1057         // 1:copy(strC 'filename', 'effect')
1058         if (get<0>(it->second) != "") {
1059           Rect src;
1060           Point dest;
1061           GetSELPointAndRect(machine, get<1>(it->second), src, dest);
1062 
1063           load_3<SPACE>(true)(
1064               machine, get<0>(it->second), MULTI_TARGET_DC, src, dest, 255);
1065         }
1066         break;
1067       }
1068       case 2: {
1069         // 2:copy(strC 'filename', 'effect', 'alpha')
1070         if (get<0>(it->third) != "") {
1071           Rect src;
1072           Point dest;
1073           GetSELPointAndRect(machine, get<1>(it->third), src, dest);
1074 
1075           load_3<SPACE>(true)(machine,
1076                               get<0>(it->third),
1077                               MULTI_TARGET_DC,
1078                               src,
1079                               dest,
1080                               get<2>(it->third));
1081         }
1082         break;
1083       }
1084       case 3: {
1085         // 3:area(strC 'filename', 'x1', 'y1', 'x2', 'y2', 'dx', 'dy')
1086         if (get<0>(it->fourth) != "") {
1087           load_3<SPACE>(true)(machine,
1088                               get<0>(it->fourth),
1089                               MULTI_TARGET_DC,
1090                               SPACE::makeRect(get<1>(it->fourth),
1091                                               get<2>(it->fourth),
1092                                               get<3>(it->fourth),
1093                                               get<4>(it->fourth)),
1094                               Point(get<5>(it->fourth), get<6>(it->fourth)),
1095                               255);
1096         }
1097         break;
1098       }
1099       case 4: {
1100         // 4:area(strC 'filename', 'x1', 'y1', 'x2', 'y2', 'dx', 'dy', 'alpha')
1101         if (get<0>(it->fifth) != "") {
1102           load_3<SPACE>(true)(machine,
1103                               get<0>(it->fifth),
1104                               MULTI_TARGET_DC,
1105                               SPACE::makeRect(get<1>(it->fifth),
1106                                               get<2>(it->fifth),
1107                                               get<3>(it->fifth),
1108                                               get<4>(it->fifth)),
1109                               Point(get<5>(it->fifth), get<6>(it->fifth)),
1110                               get<7>(it->fifth));
1111         }
1112         break;
1113       }
1114     }
1115   }
1116 }
1117 
1118 // fun grpMulti <1:Grp:00075, 4> (<strC 'filename', <'effect', MultiCommand)
1119 template <typename SPACE>
1120 struct multi_str_1 : public RLOpcode<StrConstant_T,
1121                                     IntConstant_T,
1122                                     IntConstant_T,
1123                                     MultiCommand>,
1124                      public multi_command<SPACE> {
operator ()__anon19ca9ffc0111::multi_str_11125   void operator()(RLMachine& machine,
1126                   string filename,
1127                   int effect,
1128                   int alpha,
1129                   MultiCommand::type commands) {
1130     load_1(false)(machine, filename, MULTI_TARGET_DC, 255);
1131     multi_command<SPACE>::handleMultiCommands(machine, commands);
1132     display_0()(machine, MULTI_TARGET_DC, effect);
1133   }
1134 };
1135 
1136 template <typename SPACE>
1137 struct multi_str_0
1138     : public RLOpcode<StrConstant_T, IntConstant_T, MultiCommand> {
1139   multi_str_1<SPACE> delegate_;
1140 
operator ()__anon19ca9ffc0111::multi_str_01141   void operator()(RLMachine& machine,
1142                   string filename,
1143                   int effect,
1144                   MultiCommand::type commands) {
1145     delegate_(machine, filename, effect, 255, commands);
1146   }
1147 };
1148 
1149 template <typename SPACE>
1150 struct multi_dc_1 : public RLOpcode<IntConstant_T,
1151                                    IntConstant_T,
1152                                    IntConstant_T,
1153                                    MultiCommand>,
1154                     public multi_command<SPACE> {
operator ()__anon19ca9ffc0111::multi_dc_11155   void operator()(RLMachine& machine,
1156                   int dc,
1157                   int effect,
1158                   int alpha,
1159                   MultiCommand::type commands) {
1160     copy_1(false)(machine, dc, MULTI_TARGET_DC, 255);
1161     multi_command<SPACE>::handleMultiCommands(machine, commands);
1162     display_0()(machine, MULTI_TARGET_DC, effect);
1163   }
1164 };
1165 
1166 template <typename SPACE>
1167 struct multi_dc_0
1168     : public RLOpcode<IntConstant_T, IntConstant_T, MultiCommand> {
1169   multi_dc_1<SPACE> delegate_;
1170 
operator ()__anon19ca9ffc0111::multi_dc_01171   void operator()(RLMachine& machine,
1172                   int dc,
1173                   int effect,
1174                   MultiCommand::type commands) {
1175     delegate_(machine, dc, effect, 255, commands);
1176   }
1177 };
1178 
1179 // Special case adapter to record every graphics command onto the "graphics
1180 // stack"
1181 class GrpStackAdapter : public RLOp_SpecialCase {
1182  public:
GrpStackAdapter(RLOperation * in)1183   explicit GrpStackAdapter(RLOperation* in) : operation(in) {}
1184 
operator ()(RLMachine & machine,const libreallive::CommandElement & ff)1185   void operator()(RLMachine& machine, const libreallive::CommandElement& ff) {
1186     operation->DispatchFunction(machine, ff);
1187 
1188     // Record this command's reallive bytecode form onto the graphics stack.
1189     machine.system().graphics().AddGraphicsStackCommand(
1190         ff.GetSerializedCommand(machine));
1191   }
1192 
1193  private:
1194   std::unique_ptr<RLOperation> operation;
1195 };
1196 
1197 }  // namespace
1198 
GraphicsStackMappingFun(RLOperation * op)1199 RLOperation* GraphicsStackMappingFun(RLOperation* op) {
1200   return new GrpStackAdapter(op);
1201 }
1202 
GrpModule()1203 GrpModule::GrpModule() : MappedRLModule(GraphicsStackMappingFun, "Grp", 1, 33) {
1204   using rect_impl::GRP;
1205   using rect_impl::REC;
1206 
1207   AddOpcode(15, 0, "allocDC", new allocDC);
1208   AddOpcode(16, 0, "FreeDC", CallFunction(&GraphicsSystem::FreeDC));
1209 
1210   AddUnsupportedOpcode(20, 0, "grpLoadMask");
1211   // AddOpcode(30, 0, new grpTextout);
1212 
1213   AddOpcode(31, 0, "wipe", new wipe);
1214   AddOpcode(32, 0, "shake", new shake);
1215 
1216   AddOpcode(50, 0, "grpLoad", new load_1(false));
1217   AddOpcode(50, 1, "grpLoad", new load_1(false));
1218   AddOpcode(50, 2, "grpLoad", new load_3<GRP>(false));
1219   AddOpcode(50, 3, "grpLoad", new load_3<GRP>(false));
1220   AddOpcode(51, 0, "grpMaskLoad", new load_1(true));
1221   AddOpcode(51, 1, "grpMaskLoad", new load_1(true));
1222   AddOpcode(51, 2, "grpMaskLoad", new load_3<GRP>(true));
1223   AddOpcode(51, 3, "grpMaskLoad", new load_3<GRP>(true));
1224 
1225   // These are grpBuffer, which is very similar to grpLoad and Haeleth
1226   // doesn't know how they differ. For now, we just assume they're
1227   // equivalent.
1228   AddOpcode(70, 0, "grpBuffer", new load_1(false));
1229   AddOpcode(70, 1, "grpBuffer", new load_1(false));
1230   AddOpcode(70, 2, "grpBuffer", new load_3<GRP>(false));
1231   AddOpcode(70, 3, "grpBuffer", new load_3<GRP>(false));
1232   AddOpcode(71, 0, "grpMaskBuffer", new load_1(true));
1233   AddOpcode(71, 1, "grpMaskBuffer", new load_1(true));
1234   AddOpcode(71, 2, "grpMaskBuffer", new load_3<GRP>(true));
1235   AddOpcode(71, 3, "grpMaskBuffer", new load_3<GRP>(true));
1236 
1237   AddOpcode(72, 0, "grpDisplay", new display_0);
1238   AddOpcode(72, 1, "grpDisplay", new display_1);
1239   AddOpcode(72, 2, "grpDisplay", new display_2<GRP>());
1240   AddOpcode(72, 3, "grpDisplay", new display_3<GRP>());
1241   AddOpcode(72, 4, "grpDisplay", new display_4<GRP>());
1242 
1243   AddOpcode(73, 0, "grpOpenBg", new openBg_0);
1244   AddOpcode(73, 1, "grpOpenBg", new openBg_1);
1245   AddOpcode(73, 2, "grpOpenBg", new openBg_2<GRP>(false));
1246   AddOpcode(73, 3, "grpOpenBg", new openBg_3<GRP>(false));
1247   AddOpcode(73, 4, "grpOpenBg", new openBg_4<GRP>(false));
1248 
1249   AddOpcode(74, 0, "grpMaskOpen", new open_0(true));
1250   AddOpcode(74, 1, "grpMaskOpen", new open_1(true));
1251   AddOpcode(74, 2, "grpMaskOpen", new open_2<GRP>(true));
1252   AddOpcode(74, 3, "grpMaskOpen", new open_3<GRP>(true));
1253   AddOpcode(74, 4, "grpMaskOpen", new open_4<GRP>(true));
1254 
1255   AddOpcode(75, 0, "grpMulti", new multi_str_0<GRP>());
1256   AddOpcode(75, 1, "grpMulti", new multi_str_1<GRP>());
1257   AddUnsupportedOpcode(75, 2, "grpMulti");
1258   AddUnsupportedOpcode(75, 3, "grpMulti");
1259   AddUnsupportedOpcode(75, 4, "grpMulti");
1260 
1261   AddOpcode(76, 0, "grpOpen", new open_0(false));
1262   AddOpcode(76, 1, "grpOpen", new open_1(false));
1263   AddOpcode(76, 2, "grpOpen", new open_2<GRP>(false));
1264   AddOpcode(76, 3, "grpOpen", new open_3<GRP>(false));
1265   AddOpcode(76, 4, "grpOpen", new open_4<GRP>(false));
1266 
1267   AddOpcode(77, 0, "grpMulti", new multi_dc_0<GRP>());
1268   AddOpcode(77, 1, "grpMulti", new multi_dc_1<GRP>());
1269   AddUnsupportedOpcode(77, 2, "grpMulti");
1270   AddUnsupportedOpcode(77, 3, "grpMulti");
1271   AddUnsupportedOpcode(77, 4, "grpMulti");
1272 
1273   AddOpcode(100, 0, "grpCopy", new copy_1(false));
1274   AddOpcode(100, 1, "grpCopy", new copy_1(false));
1275   AddOpcode(100, 2, "grpCopy", new copy_3<GRP>(false));
1276   AddOpcode(100, 3, "grpCopy", new copy_3<GRP>(false));
1277   AddOpcode(101, 0, "grpMaskCopy", new copy_1(true));
1278   AddOpcode(101, 1, "grpMaskCopy", new copy_1(true));
1279   AddOpcode(101, 2, "grpMaskCopy", new copy_3<GRP>(true));
1280   AddOpcode(101, 3, "grpMaskCopy", new copy_3<GRP>(true));
1281 
1282   AddUnsupportedOpcode(120, 5, "grpCopyWithMask");
1283   AddUnsupportedOpcode(140, 5, "grpCopyInvMask");
1284 
1285   AddOpcode(201, 0, "grpFill", new fill_0);
1286   AddOpcode(201, 1, "grpFill", new fill_1);
1287   AddOpcode(201, 2, "grpFill", new fill_3<GRP>());
1288   AddOpcode(201, 3, "grpFill", new fill_3<GRP>());
1289 
1290   AddOpcode(300, 0, "grpInvert", new invert_1);
1291   AddUnsupportedOpcode(300, 1, "grpInvert");
1292   AddOpcode(300, 2, "grpInvert", new invert_3<GRP>());
1293   AddUnsupportedOpcode(300, 3, "grpInvert");
1294 
1295   AddOpcode(301, 0, "grpMono", new mono_1);
1296   AddUnsupportedOpcode(301, 1, "grpMono");
1297   AddOpcode(301, 2, "grpMono", new mono_3<GRP>());
1298   AddUnsupportedOpcode(301, 3, "grpMono");
1299 
1300   AddOpcode(302, 0, "grpColour", new colour_1);
1301   AddOpcode(302, 1, "grpColour", new colour_2<GRP>());
1302 
1303   AddOpcode(303, 0, "grpLight", new light_1);
1304   AddOpcode(303, 1, "grpLight", new light_2<GRP>());
1305 
1306   AddUnsupportedOpcode(400, 0, "grpSwap");
1307   AddUnsupportedOpcode(400, 1, "grpSwap");
1308 
1309   AddOpcode(401, 0, "grpStretchBlt", new stretchBlit_1<GRP>(false));
1310   AddOpcode(401, 1, "grpStretchBlt", new stretchBlit_1<GRP>(false));
1311 
1312   AddOpcode(402, 0, "grpZoom", new zoom<GRP>());
1313 
1314   AddOpcode(403, 0, "grpFade", new fade_1);
1315   AddOpcode(403, 1, "grpFade", new fade_1);
1316   AddOpcode(403, 2, "grpFade", new fade_3);
1317   AddOpcode(403, 3, "grpFade", new fade_3);
1318   AddOpcode(403, 4, "grpFade", new fade_5<GRP>());
1319   AddOpcode(403, 5, "grpFade", new fade_5<GRP>());
1320   AddOpcode(403, 6, "grpFade", new fade_7<GRP>());
1321   AddOpcode(403, 7, "grpFade", new fade_7<GRP>());
1322 
1323   AddOpcode(409, 0, "grpMaskStretchBlt", new stretchBlit_1<GRP>(true));
1324   AddOpcode(409, 1, "grpMaskStretchBlt", new stretchBlit_1<GRP>(true));
1325 
1326   AddUnsupportedOpcode(601, 0, "grpMaskAdd");
1327   AddUnsupportedOpcode(601, 1, "grpMaskAdd");
1328   AddUnsupportedOpcode(601, 2, "grpMaskAdd");
1329   AddUnsupportedOpcode(601, 3, "grpMaskAdd");
1330 
1331   // -----------------------------------------------------------------------
1332 
1333   AddOpcode(1050, 0, "recLoad", new load_1(false));
1334   AddOpcode(1050, 1, "recLoad", new load_1(false));
1335   AddOpcode(1050, 2, "recLoad", new load_3<REC>(false));
1336   AddOpcode(1050, 3, "recLoad", new load_3<REC>(false));
1337 
1338   AddOpcode(1051, 0, "recMaskLoad", new load_1(true));
1339   AddOpcode(1051, 1, "recMaskLoad", new load_1(true));
1340   AddOpcode(1051, 2, "recMaskLoad", new load_3<REC>(true));
1341   AddOpcode(1051, 3, "recMaskLoad", new load_3<REC>(true));
1342 
1343   AddOpcode(1052, 0, "recDisplay", new display_0);
1344   AddOpcode(1052, 1, "recDisplay", new display_1);
1345   AddOpcode(1052, 2, "recDisplay", new display_2<REC>());
1346   AddOpcode(1052, 3, "recDisplay", new display_3<REC>());
1347   AddOpcode(1052, 4, "recDisplay", new display_4<REC>());
1348 
1349   AddOpcode(1053, 0, "recOpenBg", new openBg_0);
1350   AddOpcode(1053, 1, "recOpenBg", new openBg_1);
1351   AddOpcode(1053, 2, "recOpenBg", new openBg_2<REC>(false));
1352   AddOpcode(1053, 3, "recOpenBg", new openBg_3<REC>(false));
1353   AddOpcode(1053, 4, "recOpenBg", new openBg_4<REC>(false));
1354 
1355   AddOpcode(1054, 0, "recMaskOpen", new open_0(true));
1356   AddOpcode(1054, 1, "recMaskOpen", new open_1(true));
1357   AddOpcode(1054, 2, "recMaskOpen", new open_2<REC>(true));
1358   AddOpcode(1054, 3, "recMaskOpen", new open_3<REC>(true));
1359   AddOpcode(1054, 4, "recMaskOpen", new open_4<REC>(true));
1360 
1361   AddOpcode(1056, 0, "recOpen", new open_0(false));
1362   AddOpcode(1056, 1, "recOpen", new open_1(false));
1363   AddOpcode(1056, 2, "recOpen", new open_2<REC>(false));
1364   AddOpcode(1056, 3, "recOpen", new open_3<REC>(false));
1365   AddOpcode(1056, 4, "recOpen", new open_4<REC>(false));
1366 
1367   AddOpcode(1055, 0, "recMulti", new multi_str_0<REC>());
1368   AddOpcode(1055, 1, "recMulti", new multi_str_1<REC>());
1369   AddUnsupportedOpcode(1055, 2, "recMulti");
1370   AddUnsupportedOpcode(1055, 3, "recMulti");
1371   AddUnsupportedOpcode(1055, 4, "recMulti");
1372 
1373   AddOpcode(1057, 0, "recMulti", new multi_dc_0<REC>());
1374   AddOpcode(1057, 1, "recMulti", new multi_dc_1<REC>());
1375   AddUnsupportedOpcode(1057, 2, "recMulti");
1376   AddUnsupportedOpcode(1057, 3, "recMulti");
1377   AddUnsupportedOpcode(1057, 4, "recMulti");
1378 
1379   AddOpcode(1100, 0, "recCopy", new copy_1(false));
1380   AddOpcode(1100, 1, "recCopy", new copy_1(false));
1381   AddOpcode(1100, 2, "recCopy", new copy_3<REC>(false));
1382   AddOpcode(1100, 3, "recCopy", new copy_3<REC>(false));
1383   AddOpcode(1101, 0, "recMaskCopy", new copy_1(true));
1384   AddOpcode(1101, 1, "recMaskCopy", new copy_1(true));
1385   AddOpcode(1101, 2, "recMaskCopy", new copy_3<REC>(true));
1386   AddOpcode(1101, 3, "recMaskCopy", new copy_3<REC>(true));
1387 
1388   AddOpcode(1201, 0, "recFill", new fill_0);
1389   AddOpcode(1201, 1, "recFill", new fill_1);
1390   AddOpcode(1201, 2, "recFill", new fill_3<REC>());
1391   AddOpcode(1201, 3, "recFill", new fill_3<REC>());
1392 
1393   AddOpcode(1300, 0, "recInvert", new invert_1);
1394   AddUnsupportedOpcode(1300, 1, "recInvert");
1395   AddOpcode(1300, 2, "recInvert", new invert_3<REC>());
1396   AddUnsupportedOpcode(1300, 3, "recInvert");
1397 
1398   AddOpcode(1301, 0, "recMono", new mono_1);
1399   AddUnsupportedOpcode(1301, 1, "recMono");
1400   AddOpcode(1301, 2, "recMono", new mono_3<REC>());
1401   AddUnsupportedOpcode(1301, 3, "recMono");
1402 
1403   AddOpcode(1302, 0, "recColour", new colour_1);
1404   AddOpcode(1302, 1, "recColour", new colour_2<REC>());
1405 
1406   AddOpcode(1303, 0, "recLight", new light_1);
1407   AddOpcode(1303, 1, "recLight", new light_2<REC>());
1408 
1409   AddUnsupportedOpcode(1400, 0, "recSwap");
1410   AddUnsupportedOpcode(1400, 1, "recSwap");
1411 
1412   AddOpcode(1401, 0, "recStretchBlt", new stretchBlit_1<REC>(false));
1413   AddOpcode(1401, 1, "recStretchBlt", new stretchBlit_1<REC>(false));
1414 
1415   AddOpcode(1402, 0, "recZoom", new zoom<REC>());
1416 
1417   AddOpcode(1403, 0, "recFade", new fade_1);
1418   AddOpcode(1403, 1, "recFade", new fade_1);
1419   AddOpcode(1403, 2, "recFade", new fade_3);
1420   AddOpcode(1403, 3, "recFade", new fade_3);
1421   AddOpcode(1403, 4, "recFade", new fade_5<REC>());
1422   AddOpcode(1403, 5, "recFade", new fade_5<REC>());
1423   AddOpcode(1403, 6, "recFade", new fade_7<REC>());
1424   AddOpcode(1403, 7, "recFade", new fade_7<REC>());
1425 
1426   AddUnsupportedOpcode(1404, 0, "recFlash");
1427   AddUnsupportedOpcode(1404, 1, "recFlash");
1428   AddUnsupportedOpcode(1404, 2, "recFlash");
1429   AddUnsupportedOpcode(1404, 3, "recFlash");
1430 
1431   AddUnsupportedOpcode(1406, 0, "recPan");
1432   AddUnsupportedOpcode(1407, 0, "recShift");
1433   AddUnsupportedOpcode(1408, 0, "recSlide");
1434   AddOpcode(1409, 0, "recMaskStretchBlt", new stretchBlit_1<REC>(true));
1435   AddOpcode(1409, 1, "recMaskStretchBlt", new stretchBlit_1<REC>(true));
1436 }
1437 
1438 // @}
1439 
1440 // -----------------------------------------------------------------------
1441 
ReplayGraphicsStackCommand(RLMachine & machine,const std::deque<std::string> & stack)1442 void ReplayGraphicsStackCommand(RLMachine& machine,
1443                                 const std::deque<std::string>& stack) {
1444   try {
1445     for (auto const& command : stack) {
1446       if (command != "") {
1447         // Parse the string as a chunk of Reallive bytecode.
1448         libreallive::ConstructionData cdata(0, libreallive::pointer_t());
1449         libreallive::BytecodeElement* element =
1450             libreallive::BytecodeElement::Read(
1451                 command.c_str(), command.c_str() + command.size(), cdata);
1452         libreallive::CommandElement* command =
1453             dynamic_cast<libreallive::CommandElement*>(element);
1454         if (command) {
1455           machine.ExecuteCommand(*command);
1456         }
1457       }
1458     }
1459   }
1460   catch (std::exception& e) {
1461     std::cerr << "Error while replaying graphics stack: " << e.what()
1462               << std::endl;
1463     return;
1464   }
1465 }
1466 
1467 // -----------------------------------------------------------------------
1468 
ReplayDepricatedGraphicsStackVector(RLMachine & machine,const std::vector<GraphicsStackFrame> & gstack)1469 void ReplayDepricatedGraphicsStackVector(
1470     RLMachine& machine,
1471     const std::vector<GraphicsStackFrame>& gstack) {
1472   for (auto const& frame : gstack) {
1473     try {
1474       if (frame.name() == GRP_LOAD) {
1475         if (frame.hasTargetCoordinates()) {
1476           load_3<rect_impl::REC>(frame.mask())(machine,
1477                                                frame.filename(),
1478                                                frame.targetDC(),
1479                                                frame.sourceRect(),
1480                                                frame.targetPoint(),
1481                                                frame.opacity());
1482         } else {
1483           // Older versions of rlvm didn't record the mask bit, so make sure we
1484           // check for that since we don't want to break old save games.
1485           bool mask = (frame.hasMask() ? frame.mask() : true);
1486           load_1 loader(mask);
1487           loader(machine, frame.filename(), frame.targetDC(), frame.opacity());
1488         }
1489       } else if (frame.name() == GRP_OPEN) {
1490         // open is just a load + an animation.
1491         loadImageToDC1(machine,
1492                        frame.filename(),
1493                        frame.sourceRect(),
1494                        frame.targetPoint(),
1495                        frame.opacity(),
1496                        frame.mask());
1497         blitDC1toDC0(machine);
1498       } else if (frame.name() == GRP_COPY) {
1499         if (frame.hasSourceCoordinates()) {
1500           copy_3<rect_impl::REC>(frame.mask())(machine,
1501                                                frame.sourceRect(),
1502                                                frame.sourceDC(),
1503                                                frame.targetPoint(),
1504                                                frame.targetDC(),
1505                                                frame.opacity());
1506         } else {
1507           copy_1(frame.mask())(
1508               machine, frame.sourceDC(), frame.targetDC(), frame.opacity());
1509         }
1510       } else if (frame.name() == GRP_DISPLAY) {
1511         loadDCToDC1(machine,
1512                     frame.sourceDC(),
1513                     frame.sourceRect(),
1514                     frame.targetPoint(),
1515                     frame.opacity());
1516         blitDC1toDC0(machine);
1517       } else if (frame.name() == GRP_OPENBG) {
1518         loadImageToDC1(machine,
1519                        frame.filename(),
1520                        frame.sourceRect(),
1521                        frame.targetPoint(),
1522                        frame.opacity(),
1523                        false);
1524         blitDC1toDC0(machine);
1525       } else if (frame.name() == GRP_ALLOC) {
1526         Point target = frame.targetPoint();
1527         allocDC()(machine, frame.targetDC(), target.x(), target.y());
1528       } else if (frame.name() == GRP_WIPE) {
1529         wipe()(machine, frame.targetDC(), frame.r(), frame.g(), frame.b());
1530       }
1531     }
1532     catch (rlvm::Exception& e) {
1533       std::cerr << "WARNING: Error while thawing graphics stack: "
1534                 << e.what() << std::endl;
1535     }
1536   }
1537 }
1538