1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include <map>
19
20#include "bspf.hxx"
21#include "FSNode.hxx"
22#include "DefProps.hxx"
23#include "Props.hxx"
24#include "PropsSet.hxx"
25
26// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
27void PropertiesSet::load(const string& filename)
28{
29 ifstream in(filename);
30
31 Properties prop;
32 while(in >> prop)
33 insert(prop);
34}
35
36// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
37bool PropertiesSet::save(const string& filename) const
38{
39 // Only save properties when it won't create an empty file
40 FilesystemNode props(filename);
41 if(!props.exists() && myExternalProps.size() == 0)
42 return false;
43
44 ofstream out(filename);
45 if(!out)
46 return false;
47
48 // Only save those entries in the external list
49 for(const auto& i: myExternalProps)
50 out << i.second;
51
52 return true;
53}
54
55// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
56bool PropertiesSet::getMD5(const string& md5, Properties& properties,
57 bool useDefaults) const
58{
59 properties.setDefaults();
60 bool found = false;
61
62 // There are three lists to search when looking for a properties entry,
63 // which must be done in the following order
64 // If 'useDefaults' is specified, only use the built-in list
65 //
66 // 'save': entries previously inserted that are saved on program exit
67 // 'temp': entries previously inserted that are discarded
68 // 'builtin': the defaults compiled into the program
69
70 // First check properties from external file
71 if(!useDefaults)
72 {
73 // Check external list
74 auto ext = myExternalProps.find(md5);
75 if(ext != myExternalProps.end())
76 {
77 properties = ext->second;
78 found = true;
79 }
80 else // Search temp list
81 {
82 auto tmp = myTempProps.find(md5);
83 if(tmp != myTempProps.end())
84 {
85 properties = tmp->second;
86 found = true;
87 }
88 }
89 }
90
91 // Otherwise, search the internal database using binary search
92 if(!found)
93 {
94 int low = 0, high = DEF_PROPS_SIZE - 1;
95 while(low <= high)
96 {
97 int i = (low + high) / 2;
98 int cmp = BSPF::compareIgnoreCase(md5,
99 DefProps[i][static_cast<uInt8>(PropType::Cart_MD5)]);
100
101 if(cmp == 0) // found it
102 {
103 for(uInt8 p = 0; p < static_cast<uInt8>(PropType::NumTypes); ++p)
104 if(DefProps[i][p][0] != 0)
105 properties.set(PropType(p), DefProps[i][p]);
106
107 found = true;
108 break;
109 }
110 else if(cmp < 0)
111 high = i - 1; // look at lower range
112 else
113 low = i + 1; // look at upper range
114 }
115 }
116
117 return found;
118}
119
120// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121void PropertiesSet::getMD5WithInsert(const FilesystemNode& rom,
122 const string& md5, Properties& properties)
123{
124 if(!getMD5(md5, properties))
125 {
126 properties.set(PropType::Cart_MD5, md5);
127 // Create a name suitable for using in properties
128 properties.set(PropType::Cart_Name, rom.getNameWithExt(""));
129
130 insert(properties, false);
131 }
132}
133
134// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
135void PropertiesSet::insert(const Properties& properties, bool save)
136{
137 // Note that the following code is optimized for insertion when an item
138 // doesn't already exist, and when the external properties file is
139 // relatively small (which is the case with current versions of Stella,
140 // as the properties are built-in)
141 // If an item does exist, it will be removed and insertion done again
142 // This shouldn't be a speed issue, as insertions will only fail with
143 // duplicates when you're changing the current ROM properties, which
144 // most people tend not to do
145
146 // Since the PropSet is keyed by md5, we can't insert without a valid one
147 const string& md5 = properties.get(PropType::Cart_MD5);
148 if(md5 == "")
149 return;
150
151 // Make sure the exact entry isn't already in any list
152 Properties defaultProps;
153 if(getMD5(md5, defaultProps, false) && defaultProps == properties)
154 return;
155 else if(getMD5(md5, defaultProps, true) && defaultProps == properties)
156 {
157 myExternalProps.erase(md5);
158 return;
159 }
160
161 // The status of 'save' determines which list to save to
162 PropsList& list = save ? myExternalProps : myTempProps;
163
164 auto ret = list.emplace(md5, properties);
165 if(ret.second == false)
166 {
167 // Remove old item and insert again
168 list.erase(ret.first);
169 list.emplace(md5, properties);
170 }
171}
172
173// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174void PropertiesSet::print() const
175{
176 // We only look at the external properties and the built-in ones;
177 // the temp properties are ignored
178 // Also, any properties entries in the external file override the built-in
179 // ones
180 // The easiest way to merge the lists is to create another temporary one
181 // This isn't fast, but I suspect this method isn't used too often (or at all)
182
183 // First insert all external props
184 PropsList list = myExternalProps;
185
186 // Now insert all the built-in ones
187 // Note that if we try to insert a duplicate, the insertion will fail
188 // This is fine, since a duplicate in the built-in list means it should
189 // be overrided anyway (and insertion shouldn't be done)
190 Properties properties;
191 for(int i = 0; i < DEF_PROPS_SIZE; ++i)
192 {
193 properties.setDefaults();
194 for(uInt8 p = 0; p < static_cast<uInt8>(PropType::NumTypes); ++p)
195 if(DefProps[i][p][0] != 0)
196 properties.set(PropType(p), DefProps[i][p]);
197
198 list.emplace(DefProps[i][static_cast<uInt8>(PropType::Cart_MD5)], properties);
199 }
200
201 // Now, print the resulting list
202 Properties::printHeader();
203 for(const auto& i: list)
204 i.second.print();
205}
206