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) 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 #ifndef SRC_MACHINE_MEMORY_H_
29 #define SRC_MACHINE_MEMORY_H_
30 
31 #include <boost/dynamic_bitset.hpp>
32 #include <boost/serialization/split_member.hpp>
33 #include <boost/serialization/version.hpp>
34 
35 #include <algorithm>
36 #include <map>
37 #include <memory>
38 #include <string>
39 #include <utility>
40 #include <vector>
41 
42 #include "libreallive/intmemref.h"
43 
44 const int NUMBER_OF_INT_LOCATIONS = 8;
45 const int SIZE_OF_MEM_BANK = 2000;
46 const int SIZE_OF_INT_PASSING_MEM = 40;
47 const int SIZE_OF_NAME_BANK = 702;
48 
49 typedef std::vector<std::pair<int, char>> IntegerBank_t;
50 extern const IntegerBank_t LOCAL_INTEGER_BANKS;
51 extern const IntegerBank_t GLOBAL_INTEGER_BANKS;
52 
53 class RLMachine;
54 class Gameexe;
55 
56 // Struct that represents Global Memory. In any one rlvm process, there
57 // should only be one GlobalMemory struct existing, as it will be
58 // shared over all the Memory objects in the process.
59 struct GlobalMemory {
60   GlobalMemory();
61 
62   int intG[SIZE_OF_MEM_BANK];
63   int intZ[SIZE_OF_MEM_BANK];
64 
65   std::string strM[SIZE_OF_MEM_BANK];
66 
67   std::string global_names[SIZE_OF_NAME_BANK];
68 
69   // A mapping from a scenario number to a dynamic bitset, where each bit
70   // represents a specific kidoku bit.
71   std::map<int, boost::dynamic_bitset<>> kidoku_data;
72 
73   // boost::serialization
74   template <class Archive>
serializeGlobalMemory75   void serialize(Archive& ar, unsigned int version) {
76     ar& intG& intZ& strM;
77 
78     // Starting in version 1, \#NAME variable storage were added.
79     if (version > 0) {
80       ar& global_names;
81       ar& kidoku_data;
82     }
83   }
84 };
85 
86 BOOST_CLASS_VERSION(GlobalMemory, 1)
87 
88 struct dont_initialize {};
89 
90 // Struct that represents Local Memory. In any one rlvm process, lots
91 // of these things will be created, because there are commands
92 struct LocalMemory {
93   LocalMemory();
94 
95   // Constructor that prevents the memory banks from being memset
96   // (since they'll be overwritten entirely by the thawing process.
97   explicit LocalMemory(dont_initialize);
98 
99   // Zeros and clears all of local memory.
100   void reset();
101 
102   int intA[SIZE_OF_MEM_BANK];
103   int intB[SIZE_OF_MEM_BANK];
104   int intC[SIZE_OF_MEM_BANK];
105   int intD[SIZE_OF_MEM_BANK];
106   int intE[SIZE_OF_MEM_BANK];
107   int intF[SIZE_OF_MEM_BANK];
108 
109   // Local string bank
110   std::string strS[SIZE_OF_MEM_BANK];
111 
112   // When one of our values is changed, we put the original value in here. Why?
113   // So that we can save the state of string memory at the time of the last
114   // Savepoint(). Instead of doing some sort of copying entire memory banks
115   // whenever we hit a Savepoint() call, only reconstruct the original memory
116   // when we save.
117   std::map<int, int> original_intA;
118   std::map<int, int> original_intB;
119   std::map<int, int> original_intC;
120   std::map<int, int> original_intD;
121   std::map<int, int> original_intE;
122   std::map<int, int> original_intF;
123   std::map<int, std::string> original_strS;
124 
125   std::string local_names[SIZE_OF_NAME_BANK];
126 
127   // Combines an array with a log of original values and writes the de-modified
128   // array to |ar|.
129   template <class Archive, typename T>
saveArrayRevertingChangesLocalMemory130   void saveArrayRevertingChanges(Archive& ar,
131                                  const T (&a)[SIZE_OF_MEM_BANK],
132                                  const std::map<int, T>& original) const {
133     T merged[SIZE_OF_MEM_BANK];
134     std::copy(a, a + SIZE_OF_MEM_BANK, merged);
135     for (auto it = original.cbegin(); it != original.cend(); ++it) {
136       merged[it->first] = it->second;
137     }
138     ar& merged;
139   }
140 
141   // boost::serialization support
142   template <class Archive>
saveLocalMemory143   void save(Archive& ar, unsigned int version) const {
144     saveArrayRevertingChanges(ar, intA, original_intA);
145     saveArrayRevertingChanges(ar, intB, original_intB);
146     saveArrayRevertingChanges(ar, intC, original_intC);
147     saveArrayRevertingChanges(ar, intD, original_intD);
148     saveArrayRevertingChanges(ar, intE, original_intE);
149     saveArrayRevertingChanges(ar, intF, original_intF);
150 
151     saveArrayRevertingChanges(ar, strS, original_strS);
152 
153     ar& local_names;
154   }
155 
156   template <class Archive>
loadLocalMemory157   void load(Archive& ar, unsigned int version) {
158     ar& intA& intB& intC& intD& intE& intF& strS;
159 
160     // Starting in version 2, we no longer have the intL and strK in
161     // LocalMemory. They were moved to StackFrame because they're stack
162     // local. Throw away old data.
163     if (version < 2) {
164       int intL[SIZE_OF_INT_PASSING_MEM];
165       ar& intL;
166 
167       std::string strK[3];
168       ar& strK;
169     }
170 
171     // Starting in version 1, \#LOCALNAME variable storage were added.
172     if (version > 0)
173       ar& local_names;
174   }
175 
176   BOOST_SERIALIZATION_SPLIT_MEMBER()
177 };
178 
179 BOOST_CLASS_VERSION(LocalMemory, 2)
180 
181 // Class that encapsulates access to all integer and string
182 // memory. Multiple instances of this class will probably exist if
183 // save games are used.
184 //
185 // @note Because I use BSD code from xclannad in some of the methods
186 //       in this class, for licensing purposes, that code is separated
187 //       into memory_intmem.cc.
188 class Memory {
189  public:
190   // Default constructor; creates a Memory object which owns its own
191   // GlobalMemory. Initial memory values are read from the passed in Gameexe
192   // object.
193   //
194   // @note For now, we only read \#NAME and \#LOCALNAME variables, skipping any
195   //       declaration of the form \#intvar[index] or \#strvar[index].
196   Memory(RLMachine& machine, Gameexe& gamexe);
197 
198   // Creates an overlaid memory object. An overlay takes another Memory's
199   // global memory. Local integer memory isn't initialized; it isn't even
200   // memset zeroed out.
201   //
202   // Theoretically, |slot| is the save game slot to read local memory from, but
203   // this is a misnomer. |slot| isn't used and appears only for the type
204   // system(?).
205   Memory(RLMachine& machine, int slot);
206 
207   ~Memory();
208 
209   // Returns the integer value of a certain memory location
210   int GetIntValue(const libreallive::IntMemRef& ref);
211 
212   // Sets the value of a certain memory location
213   void SetIntValue(const libreallive::IntMemRef& ref, int value);
214 
215   // Returns the string value of a string memory bank
216   const std::string& GetStringValue(int type, int location);
217 
218   // Sets the string value of one of the string banks
219   void SetStringValue(int type, int number, const std::string& value);
220 
221   // Name table functions:
222 
223   // Sets the local name slot index to name.
224   void SetName(int index, const std::string& name);
225 
226   // Returns the local name slot index.
227   const std::string& GetName(int index) const;
228 
229   // Sets the local name slot index to name.
230   void SetLocalName(int index, const std::string& name);
231 
232   // Returns the local name slot index.
233   const std::string& GetLocalName(int index) const;
234 
235   // Methods that record whether a piece of text has been read. RealLive
236   // scripts have a piece of metadata called a kidoku marker which signifies if
237   // the text between that and the next kidoku marker have been previously read.
238   bool HasBeenRead(int scenario, int kidoku) const;
239   void RecordKidoku(int scenario, int kidoku);
240 
241   // Accessors for serialization.
global()242   GlobalMemory& global() { return *global_; }
global()243   const GlobalMemory& global() const { return *global_; }
local()244   LocalMemory& local() { return local_; }
local()245   const LocalMemory& local() const { return local_; }
246 
247   // Commit changes in local memory. Unlike the code in src/Systems/ which
248   // copies current values to shadow values, Memory clears a list of changes
249   // that have been made since.
250   void TakeSavepointSnapshot();
251 
252   // Converts a RealLive letter index (A-Z, AA-ZZ) to its numeric
253   // equivalent. These letter indexes are used in \#NAME definitions.
254   static int ConvertLetterIndexToInt(const std::string& value);
255 
256  private:
257   // Connects the memory banks in local_ and in global_ into int_var.
258   void ConnectIntVarPointers();
259 
260   // Input validating function to the {get,set}(Local)?Name set of functions.
261   void CheckNameIndex(int index, const std::string& name) const;
262 
263   // Reads in default memory values from the passed in Gameexe, such as \#NAME
264   // and \#LOCALNAME values.
265   void InitializeDefaultValues(Gameexe& gameexe);
266 
267   // Pointer to the GlobalMemory structure. While there can (and will
268   // be) multiple Memory instances (this is how we implement
269   // GetSaveFlag), we don't really need to duplicate this data
270   // structure and can simply pass a pointer to it.
271   std::shared_ptr<GlobalMemory> global_;
272 
273   // Local memory to a save file
274   LocalMemory local_;
275 
276   // Our owning machine. We keep this reference so we can ask for the current
277   // stackframe.
278   RLMachine& machine_;
279 
280   // Integer variable pointers. This redirect into Global and local
281   // memory (as the case may be) allows us to overlay new views of
282   // local memory without copying global memory.
283   int* int_var[NUMBER_OF_INT_LOCATIONS];
284 
285   // Change records for original.
286   std::map<int, int>* original_int_var[NUMBER_OF_INT_LOCATIONS];
287 };  // end of class Memory
288 
289 // Implementation of getting an integer out of an array. Global because we need
290 // share this implementation with RLMachine which passes in the local stack
291 // frames bank for intL[] access.
292 int GetIntValue(const libreallive::IntMemRef& ref, int* bank);
293 
294 // Implementation of setting an integer out of an array. Global because we need
295 // share this implementation with RLMachine which passes in the local stack
296 // frames bank for intL[] access.
297 void SetIntValue(const libreallive::IntMemRef& ref, int* bank, int value);
298 
299 #endif  // SRC_MACHINE_MEMORY_H_
300