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 "systems/base/graphics_system.h"
29 
30 #include <boost/algorithm/string.hpp>
31 #include <boost/archive/text_iarchive.hpp>
32 #include <boost/archive/text_oarchive.hpp>
33 #include <boost/serialization/deque.hpp>
34 #include <boost/serialization/vector.hpp>
35 
36 #include <algorithm>
37 #include <deque>
38 #include <iostream>
39 #include <iterator>
40 #include <list>
41 #include <sstream>
42 #include <string>
43 #include <utility>
44 #include <vector>
45 
46 #include "base/notification_details.h"
47 #include "base/notification_service.h"
48 #include "base/notification_source.h"
49 #include "libreallive/expression.h"
50 #include "libreallive/gameexe.h"
51 #include "machine/rlmachine.h"
52 #include "machine/serialization.h"
53 #include "machine/stack_frame.h"
54 #include "modules/module_grp.h"
55 #include "systems/base/anm_graphics_object_data.h"
56 #include "systems/base/cgm_table.h"
57 #include "systems/base/event_system.h"
58 #include "systems/base/graphics_object.h"
59 #include "systems/base/graphics_object_data.h"
60 #include "systems/base/graphics_object_of_file.h"
61 #include "systems/base/graphics_stack_frame.h"
62 #include "systems/base/hik_renderer.h"
63 #include "systems/base/hik_script.h"
64 #include "systems/base/mouse_cursor.h"
65 #include "systems/base/object_mutator.h"
66 #include "systems/base/object_settings.h"
67 #include "systems/base/surface.h"
68 #include "systems/base/system.h"
69 #include "systems/base/system_error.h"
70 #include "systems/base/text_system.h"
71 #include "utilities/exception.h"
72 #include "utilities/lazy_array.h"
73 
74 using boost::iends_with;
75 using std::cerr;
76 using std::cout;
77 using std::endl;
78 using std::fill;
79 using std::get;
80 using std::for_each;
81 using std::ostringstream;
82 using std::vector;
83 
84 namespace fs = boost::filesystem;
85 
86 // -----------------------------------------------------------------------
87 // GraphicsSystem::GraphicsObjectSettings
88 // -----------------------------------------------------------------------
89 // Impl object
90 struct GraphicsSystem::GraphicsObjectSettings {
91   // Number of graphical objects in a layer.
92   int objects_in_a_layer;
93 
94   // Each is a valid index into data, associating each object slot with an
95   // ObjectSettings instance.
96   std::unique_ptr<unsigned char[]> position;
97 
98   std::vector<ObjectSettings> data;
99 
100   explicit GraphicsObjectSettings(Gameexe& gameexe);
101 
102   const ObjectSettings& GetObjectSettingsFor(int obj_num);
103 };
104 
105 // -----------------------------------------------------------------------
106 
GraphicsObjectSettings(Gameexe & gameexe)107 GraphicsSystem::GraphicsObjectSettings::GraphicsObjectSettings(
108     Gameexe& gameexe) {
109   if (gameexe.Exists("OBJECT_MAX"))
110     objects_in_a_layer = gameexe("OBJECT_MAX");
111   else
112     objects_in_a_layer = 256;
113 
114   // First we populate everything with the special value
115   position.reset(new unsigned char[objects_in_a_layer]);
116   fill(position.get(), position.get() + objects_in_a_layer, 0);
117 
118   if (gameexe.Exists("OBJECT.999"))
119     data.emplace_back(gameexe("OBJECT.999"));
120   else
121     data.emplace_back();
122 
123   // Read the #OBJECT.xxx entries from the Gameexe
124   GameexeFilteringIterator it = gameexe.filtering_begin("OBJECT.");
125   GameexeFilteringIterator end = gameexe.filtering_end();
126   for (; it != end; ++it) {
127     string s = it->key().substr(it->key().find_first_of(".") + 1);
128     std::list<int> object_nums;
129     string::size_type poscolon = s.find_first_of(":");
130     if (poscolon != string::npos) {
131       int obj_num_first = std::stoi(s.substr(0, poscolon));
132       int obj_num_last = std::stoi(s.substr(poscolon + 1));
133       while (obj_num_first <= obj_num_last) {
134         object_nums.push_back(obj_num_first++);
135       }
136     } else {
137       object_nums.push_back(std::stoi(s));
138     }
139 
140     for (int obj_num : object_nums) {
141       if (obj_num != 999 && obj_num < objects_in_a_layer) {
142         position[obj_num] = data.size();
143         data.emplace_back(*it);
144       }
145     }
146   }
147 }
148 
149 // -----------------------------------------------------------------------
150 
151 const ObjectSettings&
GetObjectSettingsFor(int obj_num)152 GraphicsSystem::GraphicsObjectSettings::GetObjectSettingsFor(int obj_num) {
153   return data.at(position[obj_num]);
154 }
155 
156 // -----------------------------------------------------------------------
157 // GraphicsSystemGlobals
158 // -----------------------------------------------------------------------
GraphicsSystemGlobals()159 GraphicsSystemGlobals::GraphicsSystemGlobals()
160     : show_object_1(false),
161       show_object_2(false),
162       show_weather(false),
163       skip_animations(0),
164       screen_mode(1),
165       cg_table(),
166       tone_curves() {}
167 
GraphicsSystemGlobals(Gameexe & gameexe)168 GraphicsSystemGlobals::GraphicsSystemGlobals(Gameexe& gameexe)
169     : show_object_1(gameexe("INIT_OBJECT1_ONOFF_MOD").ToInt(0) ? 0 : 1),
170       show_object_2(gameexe("INIT_OBJECT2_ONOFF_MOD").ToInt(0) ? 0 : 1),
171       show_weather(gameexe("INIT_WEATHER_ONOFF_MOD").ToInt(0) ? 0 : 1),
172       skip_animations(0),
173       screen_mode(1),
174       cg_table(gameexe),
175       tone_curves(gameexe) {}
176 
177 // -----------------------------------------------------------------------
178 // GraphicsObjectImpl
179 // -----------------------------------------------------------------------
180 struct GraphicsSystem::GraphicsObjectImpl {
181   explicit GraphicsObjectImpl(int objects_in_layer);
182 
183   // Foreground objects
184   LazyArray<GraphicsObject> foreground_objects;
185 
186   // Background objects
187   LazyArray<GraphicsObject> background_objects;
188 
189   // Foreground objects (at the time of the last save)
190   LazyArray<GraphicsObject> saved_foreground_objects;
191 
192   // Background objects (at the time of the last save)
193   LazyArray<GraphicsObject> saved_background_objects;
194 
195   // Whether we restore |old_graphics_stack| using the old method instead of
196   // replaying the new graphics stack format.
197   bool use_old_graphics_stack;
198 
199   // List of commands in RealLive bytecode to rebuild the graphics stack at the
200   // current moment.
201   std::deque<std::string> graphics_stack;
202 
203   // Commands to rebuild the graphics stack (at the time of the last savepoint)
204   std::deque<std::string> saved_graphics_stack;
205 
206   // Old style graphics stack implementation.
207   std::vector<GraphicsStackFrame> old_graphics_stack;
208 };
209 
210 // -----------------------------------------------------------------------
211 
GraphicsObjectImpl(int size)212 GraphicsSystem::GraphicsObjectImpl::GraphicsObjectImpl(int size)
213     : foreground_objects(size),
214       background_objects(size),
215       saved_foreground_objects(size),
216       saved_background_objects(size),
217       use_old_graphics_stack(false) {}
218 
219 // -----------------------------------------------------------------------
220 // GraphicsSystem
221 // -----------------------------------------------------------------------
GraphicsSystem(System & system,Gameexe & gameexe)222 GraphicsSystem::GraphicsSystem(System& system, Gameexe& gameexe)
223     : screen_update_mode_(SCREENUPDATEMODE_AUTOMATIC),
224       background_type_(BACKGROUND_DC0),
225       screen_needs_refresh_(false),
226       object_state_dirty_(false),
227       is_responsible_for_update_(true),
228       display_subtitle_(gameexe("SUBTITLE").ToInt(0)),
229       interface_hidden_(false),
230       globals_(gameexe),
231       time_at_last_queue_change_(0),
232       graphics_object_settings_(new GraphicsObjectSettings(gameexe)),
233       graphics_object_impl_(new GraphicsObjectImpl(
234           graphics_object_settings_->objects_in_a_layer)),
235       use_custom_mouse_cursor_(gameexe("MOUSE_CURSOR").Exists()),
236       show_cursor_from_bytecode_(true),
237       cursor_(gameexe("MOUSE_CURSOR").ToInt(0)),
238       system_(system),
239       preloaded_hik_scripts_(32),
240       preloaded_g00_(256),
241       image_cache_(10) {}
242 
243 // -----------------------------------------------------------------------
244 
~GraphicsSystem()245 GraphicsSystem::~GraphicsSystem() {}
246 
247 // -----------------------------------------------------------------------
248 
MarkScreenAsDirty(GraphicsUpdateType type)249 void GraphicsSystem::MarkScreenAsDirty(GraphicsUpdateType type) {
250   switch (screen_update_mode()) {
251     case SCREENUPDATEMODE_AUTOMATIC:
252     case SCREENUPDATEMODE_SEMIAUTOMATIC: {
253       // Perform a blit of DC0 to the screen, and update it.
254       screen_needs_refresh_ = true;
255       break;
256     }
257     case SCREENUPDATEMODE_MANUAL: {
258       // Don't really do anything.
259       break;
260     }
261     default: {
262       ostringstream oss;
263       oss << "Invalid screen update mode value: " << screen_update_mode();
264       throw SystemError(oss.str());
265     }
266   }
267 }
268 
269 // -----------------------------------------------------------------------
270 
ForceRefresh()271 void GraphicsSystem::ForceRefresh() {
272   screen_needs_refresh_ = true;
273 
274   if (screen_update_mode_ == SCREENUPDATEMODE_MANUAL) {
275     // Note: SDLEventSystem can also set_force_wait(), in the case of automatic
276     // mode.
277     system().set_force_wait(true);
278   }
279 }
280 
OnScreenRefreshed()281 void GraphicsSystem::OnScreenRefreshed() {
282   screen_needs_refresh_ = false;
283   object_state_dirty_ = false;
284 }
285 
286 // -----------------------------------------------------------------------
287 
SetScreenUpdateMode(DCScreenUpdateMode u)288 void GraphicsSystem::SetScreenUpdateMode(DCScreenUpdateMode u) {
289   screen_update_mode_ = u;
290 }
291 
292 // -----------------------------------------------------------------------
293 
QueueShakeSpec(int spec)294 void GraphicsSystem::QueueShakeSpec(int spec) {
295   Gameexe& gameexe = system().gameexe();
296 
297   if (gameexe("SHAKE", spec).Exists()) {
298     vector<int> spec_vector = gameexe("SHAKE", spec).ToIntVector();
299 
300     int x, y, time;
301     vector<int>::const_iterator it = spec_vector.begin();
302     while (it != spec_vector.end()) {
303       x = *it++;
304       if (it != spec_vector.end()) {
305         y = *it++;
306         if (it != spec_vector.end()) {
307           time = *it++;
308           screen_shake_queue_.push(std::make_pair(Point(x, y), time));
309         }
310       }
311     }
312 
313     ForceRefresh();
314     time_at_last_queue_change_ = system().event().GetTicks();
315   }
316 }
317 
318 // -----------------------------------------------------------------------
319 
GetScreenOrigin()320 Point GraphicsSystem::GetScreenOrigin() {
321   if (screen_shake_queue_.empty()) {
322     return Point(0, 0);
323   } else {
324     return screen_shake_queue_.front().first;
325   }
326 }
327 
328 // -----------------------------------------------------------------------
329 
IsShaking() const330 bool GraphicsSystem::IsShaking() const { return !screen_shake_queue_.empty(); }
331 
332 // -----------------------------------------------------------------------
333 
CurrentShakingFrameTime() const334 int GraphicsSystem::CurrentShakingFrameTime() const {
335   if (screen_shake_queue_.empty()) {
336     return 10;
337   } else {
338     return screen_shake_queue_.front().second;
339   }
340 }
341 
342 // -----------------------------------------------------------------------
343 
ShouldUseCustomCursor()344 int GraphicsSystem::ShouldUseCustomCursor() {
345   return use_custom_mouse_cursor_ &&
346          system().gameexe()("MOUSE_CURSOR", cursor_, "NAME").ToString("") != "";
347 }
348 
349 // -----------------------------------------------------------------------
350 
SetCursor(int cursor)351 void GraphicsSystem::SetCursor(int cursor) {
352   cursor_ = cursor;
353   mouse_cursor_.reset();
354 }
355 
356 // -----------------------------------------------------------------------
357 
AddGraphicsStackCommand(const std::string & command)358 void GraphicsSystem::AddGraphicsStackCommand(const std::string& command) {
359   graphics_object_impl_->graphics_stack.push_back(command);
360 
361   // RealLive only allows 127 commands to be on the stack so game programmers
362   // can be lazy and not clear it.
363   if (graphics_object_impl_->graphics_stack.size() > 127)
364     graphics_object_impl_->graphics_stack.pop_front();
365 }
366 
367 // -----------------------------------------------------------------------
368 
StackSize() const369 int GraphicsSystem::StackSize() const {
370   // I don't think this will ever be accurate in the face of multi()
371   // commands. I'm not sure if this matters because the only use of StackSize()
372   // appears to be this recurring pattern in RL bytecode:
373   //
374   //   x = stackSize()
375   //   ... large graphics demo
376   //   stackTrunk(x)
377   return graphics_object_impl_->graphics_stack.size();
378 }
379 
380 // -----------------------------------------------------------------------
381 
ClearStack()382 void GraphicsSystem::ClearStack() {
383   graphics_object_impl_->graphics_stack.clear();
384 }
385 
386 // -----------------------------------------------------------------------
387 
StackPop(int items)388 void GraphicsSystem::StackPop(int items) {
389   for (int i = 0; i < items; ++i) {
390     if (graphics_object_impl_->graphics_stack.size()) {
391       graphics_object_impl_->graphics_stack.pop_back();
392     }
393   }
394 }
395 
396 // -----------------------------------------------------------------------
397 
ReplayGraphicsStack(RLMachine & machine)398 void GraphicsSystem::ReplayGraphicsStack(RLMachine& machine) {
399   if (graphics_object_impl_->use_old_graphics_stack) {
400     // The actual act of replaying the graphics stack will recreate the graphics
401     // stack, so clear it.
402     vector<GraphicsStackFrame> stack_to_replay;
403     stack_to_replay.swap(graphics_object_impl_->old_graphics_stack);
404     ReplayDepricatedGraphicsStackVector(machine, stack_to_replay);
405     graphics_object_impl_->use_old_graphics_stack = false;
406   } else {
407     std::deque<std::string> stack_to_replay;
408     stack_to_replay.swap(graphics_object_impl_->graphics_stack);
409 
410     machine.set_replaying_graphics_stack(true);
411     ReplayGraphicsStackCommand(machine, stack_to_replay);
412     machine.set_replaying_graphics_stack(false);
413   }
414 }
415 
416 // -----------------------------------------------------------------------
417 
SetHikRenderer(HIKRenderer * renderer)418 void GraphicsSystem::SetHikRenderer(HIKRenderer* renderer) {
419   hik_renderer_.reset(renderer);
420 }
421 
422 // -----------------------------------------------------------------------
423 
AddRenderable(Renderable * renderable)424 void GraphicsSystem::AddRenderable(Renderable* renderable) {
425   final_renderers_.insert(renderable);
426 }
427 
428 // -----------------------------------------------------------------------
429 
RemoveRenderable(Renderable * renderable)430 void GraphicsSystem::RemoveRenderable(Renderable* renderable) {
431   final_renderers_.erase(renderable);
432 }
433 
434 // -----------------------------------------------------------------------
435 
SetWindowSubtitle(const std::string & cp932str,int text_encoding)436 void GraphicsSystem::SetWindowSubtitle(const std::string& cp932str,
437                                        int text_encoding) {
438   subtitle_ = cp932str;
439 }
440 
441 // -----------------------------------------------------------------------
442 
SetScreenMode(const int in)443 void GraphicsSystem::SetScreenMode(const int in) {
444   bool changed = globals_.screen_mode != in;
445 
446   globals_.screen_mode = in;
447 
448   if (changed) {
449     NotificationService::current()->Notify(
450         NotificationType::FULLSCREEN_STATE_CHANGED,
451         Source<GraphicsSystem>(this),
452         Details<const int>(&in));
453   }
454 }
455 
456 // -----------------------------------------------------------------------
457 
ToggleFullscreen()458 void GraphicsSystem::ToggleFullscreen() { SetScreenMode(screen_mode() ? 0 : 1); }
459 
460 // -----------------------------------------------------------------------
461 
ToggleInterfaceHidden()462 void GraphicsSystem::ToggleInterfaceHidden() {
463   interface_hidden_ = !interface_hidden_;
464 }
465 
466 // -----------------------------------------------------------------------
467 
GetObjectSettings(const int obj_num)468 const ObjectSettings& GraphicsSystem::GetObjectSettings(const int obj_num) {
469   return graphics_object_settings_->GetObjectSettingsFor(obj_num);
470 }
471 
472 // -----------------------------------------------------------------------
473 
Refresh(std::ostream * tree)474 void GraphicsSystem::Refresh(std::ostream* tree) {
475   BeginFrame();
476   DrawFrame(tree);
477   EndFrame();
478 }
479 
RenderToSurface()480 std::shared_ptr<Surface> GraphicsSystem::RenderToSurface() {
481   BeginFrame();
482   DrawFrame(NULL);
483   return EndFrameToSurface();
484 }
485 
DrawFrame(std::ostream * tree)486 void GraphicsSystem::DrawFrame(std::ostream* tree) {
487   switch (background_type_) {
488     case BACKGROUND_DC0: {
489       // Display DC0
490       GetDC(0)->RenderToScreen(screen_rect(), screen_rect(), 255);
491       if (tree) {
492         // TODO(erg): How do we print the new graphics stack?
493         *tree << "Graphic Stack: UNDER CONSTRUCTION" << endl;
494       }
495       break;
496     }
497     case BACKGROUND_HIK: {
498       if (hik_renderer_) {
499         hik_renderer_->Render(tree);
500       } else {
501         GetHaikei()->RenderToScreen(screen_rect(), screen_rect(), 255);
502         if (tree) {
503           *tree << "[Haikei bitmap: " << default_bgr_name_ << "]" << endl;
504         }
505       }
506     }
507   }
508 
509   RenderObjects(tree);
510 
511   // Render text
512   if (!is_interface_hidden())
513     system().text().Render(tree);
514 }
515 
516 // -----------------------------------------------------------------------
517 
ExecuteGraphicsSystem(RLMachine & machine)518 void GraphicsSystem::ExecuteGraphicsSystem(RLMachine& machine) {
519   // Check to see if any of the graphics objects are reporting that
520   // they want to force a redraw
521   for (GraphicsObject& obj : GetForegroundObjects())
522     obj.Execute(machine);
523 
524   if (mouse_cursor_)
525     mouse_cursor_->Execute(system());
526 
527   if (hik_renderer_ && background_type_ == BACKGROUND_HIK)
528     hik_renderer_->Execute(machine);
529 
530   // Possibly update the screen shaking state
531   if (!screen_shake_queue_.empty()) {
532     unsigned int now = system().event().GetTicks();
533     unsigned int accumulated_ticks = now - time_at_last_queue_change_;
534     while (!screen_shake_queue_.empty() &&
535            accumulated_ticks > screen_shake_queue_.front().second) {
536       int frame_ticks = screen_shake_queue_.front().second;
537       accumulated_ticks -= frame_ticks;
538       time_at_last_queue_change_ += frame_ticks;
539       screen_shake_queue_.pop();
540       ForceRefresh();
541     }
542   }
543 }
544 
545 // -----------------------------------------------------------------------
546 
Reset()547 void GraphicsSystem::Reset() {
548   graphics_object_impl_->foreground_objects.Clear();
549   graphics_object_impl_->background_objects.Clear();
550 
551   ClearAllDCs();
552 
553   preloaded_hik_scripts_.Clear();
554   preloaded_g00_.Clear();
555   hik_renderer_.reset();
556   background_type_ = BACKGROUND_DC0;
557 
558   // Reset the cursor
559   show_cursor_from_bytecode_ = true;
560   cursor_ = system().gameexe()("MOUSE_CURSOR").ToInt(0);
561   mouse_cursor_.reset();
562 
563   default_grp_name_ = "";
564   default_bgr_name_ = "";
565   screen_update_mode_ = SCREENUPDATEMODE_AUTOMATIC;
566   background_type_ = BACKGROUND_DC0;
567   subtitle_ = "";
568   interface_hidden_ = false;
569 }
570 
GetEmojiSurface()571 std::shared_ptr<const Surface> GraphicsSystem::GetEmojiSurface() {
572   GameexeFilteringIterator it = system().gameexe().filtering_begin("E_MOJI.");
573   GameexeFilteringIterator end = system().gameexe().filtering_end();
574   for (; it != end; ++it) {
575     // Try to interpret each key as a filename.
576     std::string file_name = it->ToString("");
577     std::shared_ptr<const Surface> surface = GetSurfaceNamed(file_name);
578     if (surface)
579       return surface;
580   }
581 
582   return std::shared_ptr<const Surface>();
583 }
584 
PreloadHIKScript(System & system,int slot,const std::string & name,const boost::filesystem::path & file_path)585 void GraphicsSystem::PreloadHIKScript(
586     System& system,
587     int slot,
588     const std::string& name,
589     const boost::filesystem::path& file_path) {
590   HIKScript* script = new HIKScript(system, file_path);
591   script->EnsureUploaded();
592 
593   preloaded_hik_scripts_[slot] =
594       std::make_pair(name, std::shared_ptr<HIKScript>(script));
595 }
596 
ClearPreloadedHIKScript(int slot)597 void GraphicsSystem::ClearPreloadedHIKScript(int slot) {
598   preloaded_hik_scripts_[slot] =
599       std::make_pair("", std::shared_ptr<HIKScript>());
600 }
601 
ClearAllPreloadedHIKScripts()602 void GraphicsSystem::ClearAllPreloadedHIKScripts() {
603   preloaded_hik_scripts_.Clear();
604 }
605 
GetHIKScript(System & system,const std::string & name,const boost::filesystem::path & file_path)606 std::shared_ptr<HIKScript> GraphicsSystem::GetHIKScript(
607     System& system,
608     const std::string& name,
609     const boost::filesystem::path& file_path) {
610   for (HIKArrayItem& item : preloaded_hik_scripts_) {
611     if (item.first == name)
612       return item.second;
613   }
614 
615   return std::shared_ptr<HIKScript>(new HIKScript(system, file_path));
616 }
617 
PreloadG00(int slot,const std::string & name)618 void GraphicsSystem::PreloadG00(int slot, const std::string& name) {
619   // We first check our implicit cache just in case so we don't load it twice.
620   std::shared_ptr<const Surface> surface = image_cache_.fetch(name);
621   if (!surface)
622     surface = LoadSurfaceFromFile(name);
623 
624   if (surface)
625     surface->EnsureUploaded();
626 
627   preloaded_g00_[slot] = std::make_pair(name, surface);
628 }
629 
ClearPreloadedG00(int slot)630 void GraphicsSystem::ClearPreloadedG00(int slot) {
631   preloaded_g00_[slot] = std::make_pair("", std::shared_ptr<const Surface>());
632 }
633 
ClearAllPreloadedG00()634 void GraphicsSystem::ClearAllPreloadedG00() { preloaded_g00_.Clear(); }
635 
GetPreloadedG00(const std::string & name)636 std::shared_ptr<const Surface> GraphicsSystem::GetPreloadedG00(
637     const std::string& name) {
638   for (G00ArrayItem& item : preloaded_g00_) {
639     if (item.first == name)
640       return item.second;
641   }
642 
643   return std::shared_ptr<const Surface>();
644 }
645 
646 // -----------------------------------------------------------------------
647 
GetSurfaceNamedAndMarkViewed(RLMachine & machine,const std::string & short_filename)648 std::shared_ptr<const Surface> GraphicsSystem::GetSurfaceNamedAndMarkViewed(
649     RLMachine& machine,
650     const std::string& short_filename) {
651   // Record that we viewed this CG.
652   cg_table().SetViewed(machine, short_filename);
653 
654   return GetSurfaceNamed(short_filename);
655 }
656 
657 // -----------------------------------------------------------------------
658 
GetSurfaceNamed(const std::string & short_filename)659 std::shared_ptr<const Surface> GraphicsSystem::GetSurfaceNamed(
660     const std::string& short_filename) {
661   // Check if this is in the script controlled cache.
662   std::shared_ptr<const Surface> cached_surface =
663       GetPreloadedG00(short_filename);
664   if (cached_surface)
665     return cached_surface;
666 
667   // First check to see if this surface is already in our internal cache
668   cached_surface = image_cache_.fetch(short_filename);
669   if (cached_surface)
670     return cached_surface;
671 
672   std::shared_ptr<const Surface> surface_to_ret =
673       LoadSurfaceFromFile(short_filename);
674   image_cache_.insert(short_filename, surface_to_ret);
675   return surface_to_ret;
676 }
677 
678 // -----------------------------------------------------------------------
679 
ClearAndPromoteObjects()680 void GraphicsSystem::ClearAndPromoteObjects() {
681   typedef LazyArray<GraphicsObject>::full_iterator FullIterator;
682 
683   FullIterator bg = graphics_object_impl_->background_objects.full_begin();
684   FullIterator bg_end = graphics_object_impl_->background_objects.full_end();
685   FullIterator fg = graphics_object_impl_->foreground_objects.full_begin();
686   FullIterator fg_end = graphics_object_impl_->foreground_objects.full_end();
687   for (; bg != bg_end && fg != fg_end; bg++, fg++) {
688     if (fg.valid() && !fg->wipe_copy()) {
689       fg->InitializeParams();
690       fg->FreeObjectData();
691     }
692 
693     if (bg.valid()) {
694       *fg = *bg;
695       bg->InitializeParams();
696       bg->FreeObjectData();
697     }
698   }
699 }
700 
701 // -----------------------------------------------------------------------
702 
GetObject(int layer,int obj_number)703 GraphicsObject& GraphicsSystem::GetObject(int layer, int obj_number) {
704   if (layer < 0 || layer > 1)
705     throw rlvm::Exception("Invalid layer number");
706 
707   if (layer == OBJ_BG)
708     return graphics_object_impl_->background_objects[obj_number];
709   else
710     return graphics_object_impl_->foreground_objects[obj_number];
711 }
712 
713 // -----------------------------------------------------------------------
714 
SetObject(int layer,int obj_number,GraphicsObject & obj)715 void GraphicsSystem::SetObject(int layer, int obj_number, GraphicsObject& obj) {
716   if (layer < 0 || layer > 1)
717     throw rlvm::Exception("Invalid layer number");
718 
719   if (layer == OBJ_BG)
720     graphics_object_impl_->background_objects[obj_number] = obj;
721   else
722     graphics_object_impl_->foreground_objects[obj_number] = obj;
723 }
724 
725 // -----------------------------------------------------------------------
726 
FreeObjectData(int obj_number)727 void GraphicsSystem::FreeObjectData(int obj_number) {
728   graphics_object_impl_->foreground_objects[obj_number].FreeObjectData();
729   graphics_object_impl_->background_objects[obj_number].FreeObjectData();
730 }
731 
732 // -----------------------------------------------------------------------
733 
FreeAllObjectData()734 void GraphicsSystem::FreeAllObjectData() {
735   for (GraphicsObject& object : graphics_object_impl_->foreground_objects)
736     object.FreeObjectData();
737 
738   for (GraphicsObject& object : graphics_object_impl_->background_objects)
739     object.FreeObjectData();
740 }
741 
742 // -----------------------------------------------------------------------
743 
InitializeObjectParams(int obj_number)744 void GraphicsSystem::InitializeObjectParams(int obj_number) {
745   graphics_object_impl_->foreground_objects[obj_number].InitializeParams();
746   graphics_object_impl_->background_objects[obj_number].InitializeParams();
747 }
748 
749 // -----------------------------------------------------------------------
750 
InitializeAllObjectParams()751 void GraphicsSystem::InitializeAllObjectParams() {
752   for (GraphicsObject& object : graphics_object_impl_->foreground_objects)
753     object.InitializeParams();
754 
755   for (GraphicsObject& object : graphics_object_impl_->background_objects)
756     object.InitializeParams();
757 }
758 
759 // -----------------------------------------------------------------------
760 
GetObjectLayerSize()761 int GraphicsSystem::GetObjectLayerSize() {
762   return graphics_object_settings_->objects_in_a_layer;
763 }
764 
765 // -----------------------------------------------------------------------
766 
GetBackgroundObjects()767 LazyArray<GraphicsObject>& GraphicsSystem::GetBackgroundObjects() {
768   return graphics_object_impl_->background_objects;
769 }
770 
771 // -----------------------------------------------------------------------
772 
GetForegroundObjects()773 LazyArray<GraphicsObject>& GraphicsSystem::GetForegroundObjects() {
774   return graphics_object_impl_->foreground_objects;
775 }
776 
777 // -----------------------------------------------------------------------
778 
AnimationsPlaying() const779 bool GraphicsSystem::AnimationsPlaying() const {
780   for (GraphicsObject& object : graphics_object_impl_->foreground_objects) {
781     if (object.has_object_data()) {
782       GraphicsObjectData& data = object.GetObjectData();
783       if (data.IsAnimation() && data.is_currently_playing())
784         return true;
785     }
786   }
787 
788   return false;
789 }
790 
791 // -----------------------------------------------------------------------
792 
TakeSavepointSnapshot()793 void GraphicsSystem::TakeSavepointSnapshot() {
794   GetForegroundObjects().CopyTo(graphics_object_impl_->saved_foreground_objects);
795   GetBackgroundObjects().CopyTo(graphics_object_impl_->saved_background_objects);
796   graphics_object_impl_->saved_graphics_stack =
797       graphics_object_impl_->graphics_stack;
798 }
799 
800 // -----------------------------------------------------------------------
801 
ClearAllDCs()802 void GraphicsSystem::ClearAllDCs() {
803   GetDC(0)->Fill(RGBAColour::Black());
804 
805   for (int i = 1; i < 16; ++i)
806     FreeDC(i);
807 }
808 
809 // -----------------------------------------------------------------------
810 
RenderObjects(std::ostream * tree)811 void GraphicsSystem::RenderObjects(std::ostream* tree) {
812   to_render_.clear();
813 
814   // Collate all objects that we might want to render.
815   AllocatedLazyArrayIterator<GraphicsObject> it =
816       graphics_object_impl_->foreground_objects.begin();
817   AllocatedLazyArrayIterator<GraphicsObject> end =
818       graphics_object_impl_->foreground_objects.end();
819   for (; it != end; ++it) {
820     const ObjectSettings& settings = GetObjectSettings(it.pos());
821     if (settings.obj_on_off == 1 && should_show_object1() == false)
822       continue;
823     else if (settings.obj_on_off == 2 && should_show_object2() == false)
824       continue;
825     else if (settings.weather_on_off && should_show_weather() == false)
826       continue;
827     else if (settings.space_key && is_interface_hidden())
828       continue;
829 
830     to_render_.emplace_back(
831         it->z_order(), it->z_layer(), it->z_depth(), it.pos(), &*it);
832   }
833 
834   // Sort by all the ordering values.
835   std::sort(to_render_.begin(), to_render_.end());
836 
837   for (ToRenderVec::iterator it = to_render_.begin(); it != to_render_.end();
838        ++it) {
839     get<4>(*it)->Render(get<3>(*it), NULL, tree);
840   }
841 }
842 
843 // -----------------------------------------------------------------------
844 
GetCurrentCursor()845 std::shared_ptr<MouseCursor> GraphicsSystem::GetCurrentCursor() {
846   if (!use_custom_mouse_cursor_ || !show_cursor_from_bytecode_)
847     return std::shared_ptr<MouseCursor>();
848 
849   if (use_custom_mouse_cursor_ && !mouse_cursor_) {
850     MouseCursorCache::iterator it = cursor_cache_.find(cursor_);
851     if (it != cursor_cache_.end()) {
852       mouse_cursor_ = it->second;
853     } else {
854       std::shared_ptr<const Surface> cursor_surface;
855       GameexeInterpretObject cursor =
856           system().gameexe()("MOUSE_CURSOR", cursor_);
857       GameexeInterpretObject name_key = cursor("NAME");
858 
859       if (name_key.Exists()) {
860         int count = cursor("CONT").ToInt(1);
861         int speed = cursor("SPEED").ToInt(800);
862 
863         cursor_surface = GetSurfaceNamed(name_key);
864         mouse_cursor_.reset(
865             new MouseCursor(system(), cursor_surface, count, speed));
866         cursor_cache_[cursor_] = mouse_cursor_;
867       } else {
868         mouse_cursor_.reset();
869       }
870     }
871   }
872 
873   return mouse_cursor_;
874 }
875 
876 // -----------------------------------------------------------------------
877 
SetScreenSize(const Size & size)878 void GraphicsSystem::SetScreenSize(const Size& size) {
879   screen_size_ = size;
880   screen_rect_ = Rect(Point(0, 0), size);
881 }
882 
883 // -----------------------------------------------------------------------
884 
MouseMotion(const Point & new_location)885 void GraphicsSystem::MouseMotion(const Point& new_location) {
886   if (use_custom_mouse_cursor_ && show_cursor_from_bytecode_)
887     MarkScreenAsDirty(GUT_MOUSE_MOTION);
888 
889   cursor_pos_ = new_location;
890 }
891 
892 // -----------------------------------------------------------------------
893 
BuildObjOfFile(const std::string & filename)894 GraphicsObjectData* GraphicsSystem::BuildObjOfFile(
895     const std::string& filename) {
896   // Get the path to get the file type (which won't be in filename)
897   fs::path full_path = system().FindFile(filename, OBJ_FILETYPES);
898   if (full_path.empty()) {
899     ostringstream oss;
900     oss << "Could not find Object compatible file \"" << filename << "\".";
901     throw rlvm::Exception(oss.str());
902   }
903 
904   string file_str = full_path.string();
905   if (iends_with(file_str, "g00") || iends_with(file_str, "pdt")) {
906     return new GraphicsObjectOfFile(system(), filename);
907   } else if (iends_with(file_str, "anm")) {
908     return new AnmGraphicsObjectData(system(), filename);
909   } else {
910     ostringstream oss;
911     oss << "Don't know how to handle object file: \"" << filename << "\"";
912     throw rlvm::Exception(oss.str());
913   }
914 }
915 
916 // -----------------------------------------------------------------------
917 
918 template <class Archive>
save(Archive & ar,unsigned int version) const919 void GraphicsSystem::save(Archive& ar, unsigned int version) const {
920   ar& subtitle_& default_grp_name_& default_bgr_name_& graphics_object_impl_
921       ->saved_graphics_stack& graphics_object_impl_->saved_background_objects&
922             graphics_object_impl_->saved_foreground_objects;
923 }
924 
925 // -----------------------------------------------------------------------
926 
927 template <class Archive>
load(Archive & ar,unsigned int version)928 void GraphicsSystem::load(Archive& ar, unsigned int version) {
929   ar& subtitle_;
930   if (version > 0) {
931     ar& default_grp_name_;
932     ar& default_bgr_name_;
933     graphics_object_impl_->use_old_graphics_stack = false;
934     ar& graphics_object_impl_->graphics_stack;
935   } else {
936     graphics_object_impl_->use_old_graphics_stack = true;
937     ar& graphics_object_impl_->old_graphics_stack;
938   }
939 
940   ar& graphics_object_impl_->background_objects& graphics_object_impl_
941       ->foreground_objects;
942 
943   // Now alert all subclasses that we've set the subtitle
944   SetWindowSubtitle(subtitle_,
945                     Serialization::g_current_machine->GetTextEncoding());
946 }
947 
948 // -----------------------------------------------------------------------
949 
950 template void GraphicsSystem::load<boost::archive::text_iarchive>(
951     boost::archive::text_iarchive& ar,
952     unsigned int version);
953 template void GraphicsSystem::save<boost::archive::text_oarchive>(
954     boost::archive::text_oarchive& ar,
955     unsigned int version) const;
956