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) 2009 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 #include "modules/module_bgr.h"
28 
29 #include <boost/algorithm/string.hpp>
30 #include <boost/filesystem.hpp>
31 #include <iostream>
32 #include <string>
33 
34 #include "effects/effect.h"
35 #include "effects/effect_factory.h"
36 #include "machine/general_operations.h"
37 #include "machine/rlmachine.h"
38 #include "machine/rloperation.h"
39 #include "machine/rloperation/argc_t.h"
40 #include "machine/rloperation/complex_t.h"
41 #include "machine/rloperation/special_t.h"
42 #include "modules/module_grp.h"
43 #include "systems/base/colour.h"
44 #include "systems/base/graphics_system.h"
45 #include "systems/base/hik_renderer.h"
46 #include "systems/base/hik_script.h"
47 #include "systems/base/surface.h"
48 #include "systems/base/system.h"
49 #include "utilities/graphics.h"
50 
51 namespace fs = boost::filesystem;
52 using boost::iends_with;
53 
54 // Working theory of how this module works: The haikei module is one backing
55 // surface and (optionally) a HIK script. Games like AIR and the Maiden Halo
56 // demo use just the surface with a combination of bgrMulti and
57 // bgrLoadHaikei. OTOH, ALMA and planetarian use HIK scripts and the whole
58 // point of HIK scripts is to manipulate the backing surface on a timer that's
59 // divorced from the main interpreter loop.
60 
61 namespace {
62 
63 struct bgrLoadHaikei_blank : public RLOpcode<IntConstant_T> {
operator ()__anon0d5ae00e0111::bgrLoadHaikei_blank64   void operator()(RLMachine& machine, int sel) {
65     GraphicsSystem& graphics = machine.system().graphics();
66     graphics.set_default_bgr_name("");
67     graphics.SetHikRenderer(NULL);
68     graphics.set_graphics_background(BACKGROUND_HIK);
69 
70     std::shared_ptr<Surface> before = graphics.RenderToSurface();
71     graphics.GetHaikei()->Fill(RGBAColour::Clear());
72 
73     if (!machine.replaying_graphics_stack())
74       graphics.ClearAndPromoteObjects();
75 
76     std::shared_ptr<Surface> after = graphics.RenderToSurface();
77 
78     LongOperation* effect =
79         EffectFactory::BuildFromSEL(machine, after, before, sel);
80     machine.PushLongOperation(effect);
81   }
82 };
83 
84 struct bgrLoadHaikei_main : RLOpcode<StrConstant_T, IntConstant_T> {
operator ()__anon0d5ae00e0111::bgrLoadHaikei_main85   void operator()(RLMachine& machine, std::string filename, int sel) {
86     System& system = machine.system();
87     GraphicsSystem& graphics = system.graphics();
88     graphics.set_default_bgr_name(filename);
89     graphics.set_graphics_background(BACKGROUND_HIK);
90 
91     // bgrLoadHaikei clears the stack.
92     graphics.ClearStack();
93 
94     fs::path path = system.FindFile(filename, HIK_FILETYPES);
95     if (iends_with(path.string(), "hik")) {
96       if (!machine.replaying_graphics_stack())
97         graphics.ClearAndPromoteObjects();
98 
99       graphics.SetHikRenderer(new HIKRenderer(
100           system, graphics.GetHIKScript(system, filename, path)));
101     } else {
102       std::shared_ptr<Surface> before = graphics.RenderToSurface();
103 
104       if (!path.empty()) {
105         std::shared_ptr<const Surface> source(
106             graphics.GetSurfaceNamedAndMarkViewed(machine, filename));
107         std::shared_ptr<Surface> haikei = graphics.GetHaikei();
108         source->BlitToSurface(
109             *haikei, source->GetRect(), source->GetRect(), 255, true);
110       }
111 
112       // Promote the objects if we're in normal mode. If we're restoring the
113       // graphics stack, we already have our layers promoted.
114       if (!machine.replaying_graphics_stack())
115         graphics.ClearAndPromoteObjects();
116 
117       std::shared_ptr<Surface> after = graphics.RenderToSurface();
118 
119       LongOperation* effect =
120           EffectFactory::BuildFromSEL(machine, after, before, sel);
121       machine.PushLongOperation(effect);
122     }
123   }
124 };
125 
126 struct bgrLoadHaikei_wtf
127     : RLOpcode<StrConstant_T, IntConstant_T, IntConstant_T, IntConstant_T> {
operator ()__anon0d5ae00e0111::bgrLoadHaikei_wtf128   void operator()(RLMachine& machine,
129                   std::string filename,
130                   int sel,
131                   int a,
132                   int b) {
133     bgrLoadHaikei_main()(machine, filename, sel);
134   }
135 };
136 
137 struct bgrLoadHaikei_wtf2 : RLOpcode<StrConstant_T,
138                                     IntConstant_T,
139                                     IntConstant_T,
140                                     IntConstant_T,
141                                     IntConstant_T,
142                                     IntConstant_T> {
operator ()__anon0d5ae00e0111::bgrLoadHaikei_wtf2143   void operator()(RLMachine& machine,
144                   string filename,
145                   int sel,
146                   int a,
147                   int b,
148                   int c,
149                   int d) {
150     // cerr << "Filename: " << filename
151     //      << "(a: " << a << ", b: " << b << ", c: " << c << ", d: " << d <<
152     // ")"
153     //      << endl;
154     bgrLoadHaikei_main()(machine, filename, sel);
155   }
156 };
157 
158 // -----------------------------------------------------------------------
159 
160 typedef Argc_T<Special_T<
161     DefaultSpecialMapper,
162     // 0:copy(strC 'filename')
163     StrConstant_T,
164     // 1:DUMMY. Unknown.
165     Complex_T<StrConstant_T, IntConstant_T>,
166     // 2:copy(strC 'filename', '?')
167     Complex_T<StrConstant_T, IntConstant_T>,
168     // 3:DUMMY. Unknown.
169     Complex_T<StrConstant_T, IntConstant_T>,
170     // 4:copy(strC, '?', '?')
171     Complex_T<StrConstant_T, IntConstant_T, IntConstant_T>>> BgrMultiCommand;
172 
173 struct bgrMulti_1
174     : public RLOpcode<StrConstant_T, IntConstant_T, BgrMultiCommand> {
175  public:
operator ()__anon0d5ae00e0111::bgrMulti_1176   void operator()(RLMachine& machine,
177                   string filename,
178                   int effectNum,
179                   BgrMultiCommand::type commands) {
180     GraphicsSystem& graphics = machine.system().graphics();
181 
182     // Get the state of the world before we do any processing.
183     std::shared_ptr<Surface> before = graphics.RenderToSurface();
184 
185     graphics.set_graphics_background(BACKGROUND_HIK);
186 
187     // May need to use current background.
188     if (filename == "???")
189       filename = graphics.default_bgr_name();
190 
191     // Load "filename" as the background.
192     std::shared_ptr<const Surface> surface(
193         graphics.GetSurfaceNamedAndMarkViewed(machine, filename));
194     surface->BlitToSurface(
195         *graphics.GetHaikei(), surface->GetRect(), surface->GetRect(), 255, true);
196 
197     // TODO(erg): Unsure about the alpha in these implementation.
198     for (BgrMultiCommand::type::const_iterator it = commands.begin();
199          it != commands.end();
200          it++) {
201       switch (it->type) {
202         case 0: {
203           // 0:copy(strC 'filename')
204           surface = graphics.GetSurfaceNamedAndMarkViewed(machine, it->first);
205           surface->BlitToSurface(*graphics.GetHaikei(),
206                                  surface->GetRect(),
207                                  surface->GetRect(),
208                                  255,
209                                  true);
210           break;
211         }
212         case 2: {
213           // 2:copy(strC 'filename', '?')
214           Rect srcRect;
215           Point dest;
216           GetSELPointAndRect(machine, std::get<1>(it->third), srcRect, dest);
217 
218           surface =
219               graphics.GetSurfaceNamedAndMarkViewed(machine,
220                                                     std::get<0>(it->third));
221           Rect destRect = Rect(dest, srcRect.size());
222           surface->BlitToSurface(
223               *graphics.GetHaikei(), srcRect, destRect, 255, true);
224           break;
225         }
226         default: {
227           std::cerr << "Don't know what to do with a type " << it->type
228                     << " in bgrMulti_1" << std::endl;
229           break;
230         }
231       }
232     }
233 
234     // Promote the objects if we're in normal mode. If we're restoring the
235     // graphics stack, we already have our layers promoted.
236     if (!machine.replaying_graphics_stack())
237       graphics.ClearAndPromoteObjects();
238 
239     std::shared_ptr<Surface> after = graphics.RenderToSurface();
240     LongOperation* effect =
241         EffectFactory::BuildFromSEL(machine, after, before, effectNum);
242     machine.PushLongOperation(effect);
243   }
244 };
245 
246 struct bgrNext : public RLOpcode<> {
operator ()__anon0d5ae00e0111::bgrNext247   void operator()(RLMachine& machine) {
248     HIKRenderer* renderer = machine.system().graphics().hik_renderer();
249     if (renderer) {
250       renderer->NextAnimationFrame();
251     }
252   }
253 };
254 
255 struct bgrSetXOffset : public RLOpcode<IntConstant_T> {
operator ()__anon0d5ae00e0111::bgrSetXOffset256   void operator()(RLMachine& machine, int offset) {
257     HIKRenderer* renderer = machine.system().graphics().hik_renderer();
258     if (renderer) {
259       renderer->set_x_offset(offset);
260     }
261   }
262 };
263 
264 struct bgrSetYOffset : public RLOpcode<IntConstant_T> {
operator ()__anon0d5ae00e0111::bgrSetYOffset265   void operator()(RLMachine& machine, int offset) {
266     HIKRenderer* renderer = machine.system().graphics().hik_renderer();
267     if (renderer) {
268       renderer->set_y_offset(offset);
269     }
270   }
271 };
272 
273 struct bgrPreloadScript : public RLOpcode<IntConstant_T, StrConstant_T> {
operator ()__anon0d5ae00e0111::bgrPreloadScript274   void operator()(RLMachine& machine, int slot, string name) {
275     System& system = machine.system();
276     fs::path path = system.FindFile(name, HIK_FILETYPES);
277     if (iends_with(path.string(), "hik")) {
278       system.graphics().PreloadHIKScript(system, slot, name, path);
279     }
280   }
281 };
282 
283 }  // namespace
284 
285 // -----------------------------------------------------------------------
286 
BgrModule()287 BgrModule::BgrModule() : MappedRLModule(GraphicsStackMappingFun, "Bgr", 1, 40) {
288   AddOpcode(10, 0, "bgrLoadHaikei", new bgrLoadHaikei_blank);
289   AddOpcode(10, 1, "bgrLoadHaikei", new bgrLoadHaikei_main);
290   AddOpcode(10, 2, "bgrLoadHaikei", new bgrLoadHaikei_wtf);
291   AddOpcode(10, 3, "bgrLoadHaikei", new bgrLoadHaikei_wtf2);
292 
293   AddUnsupportedOpcode(100, 0, "bgrMulti");
294   AddOpcode(100, 1, "bgrMulti", new bgrMulti_1);
295 
296   AddOpcode(1000, 0, "bgrNext", new bgrNext);
297 
298   AddOpcode(1104, 0, "bgrSetXOffset", new bgrSetXOffset);
299   AddOpcode(1105, 0, "bgrSetYOffset", new bgrSetYOffset);
300 
301   AddOpcode(2000, 0, "bgrPreloadScript", new bgrPreloadScript);
302   AddOpcode(2001,
303             0,
304             "bgrClearPreloadedScript",
305             CallFunction(&GraphicsSystem::ClearPreloadedHIKScript));
306   AddOpcode(2002,
307             0,
308             "bgrClearAllPreloadedScripts",
309             CallFunction(&GraphicsSystem::ClearAllPreloadedHIKScripts));
310 }
311