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