1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software{} you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation{} either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY{} without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program{} if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/conf.h"
24 #include "glk/utils.h"
25 #include "glk/windows.h"
26 #include "common/config-manager.h"
27
28 namespace Glk {
29
30 const byte WHITE[3] = { 0xff, 0xff, 0xff };
31 const byte BLUE[3] = { 0x00, 0x00, 0x60 };
32 const byte SCROLL_BG[3] = { 0xb0, 0xb0, 0xb0 };
33 const byte SCROLL_FG[3] = { 0x80, 0x80, 0x80 };
34
35 WindowStyleStatic T_STYLES[style_NUMSTYLES] = {
36 { PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< Normal
37 { PROPI, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< Emphasized
38 { MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< Preformatted
39 { PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< Header
40 { PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< Subheader
41 { PROPZ, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< Alert
42 { PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< Note
43 { PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< BlockQuote
44 { PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x60, 0x00 }, false }, ///< Input
45 { MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false }, ///< User1
46 { MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, false } ///< User2
47 };
48
49 WindowStyleStatic G_STYLES[style_NUMSTYLES] = {
50 { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< Normal
51 { MONOI, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< Emphasized
52 { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< Preformatted
53 { MONOB, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< Header
54 { MONOB, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< Subheader
55 { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< Alert
56 { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< Note
57 { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< BlockQuote
58 { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< Input
59 { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false }, ///< User1
60 { MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, false } ///< User2
61 };
62
63 Conf *g_conf;
64
Conf(InterpreterType interpType)65 Conf::Conf(InterpreterType interpType) : _interpType(interpType), _graphics(true),
66 _width(640), _height(400), _screenFormat(2, 5, 6, 5, 0, 11, 5, 0, 0),
67 _rows(25), _cols(60), _lockRows(0), _lockCols(0), _wPaddingX(0), _wPaddingY(0),
68 _wBorderX(0), _wBorderY(0), _tMarginX(7), _tMarginY(7), _gamma(1.0),
69 _borderColor(0), _borderSave(0),
70 _windowColor(parseColor(WHITE)), _windowSave(parseColor(WHITE)),
71 _sound(true), _speak(false), _speakInput(false), _styleHint(1),
72 _scrollBg(parseColor(SCROLL_BG)), _scrollFg(parseColor(SCROLL_FG)),
73 _scrollWidth(0), _safeClicks(false) {
74 g_conf = this;
75 _imageW = _width;
76 _imageH = _height;
77
78 _propInfo._morePrompt = "\207 more \207";
79 _propInfo._moreColor = 0;
80 _propInfo._moreSave = 0;
81 _propInfo._moreFont = PROPB;
82 _propInfo._moreAlign = 0;
83 _monoInfo._aspect = 1.0;
84 _propInfo._aspect = 1.0;
85 _monoInfo._size = 11;
86 _propInfo._size = 12;
87 _propInfo._linkColor = parseColor(BLUE);
88 _monoInfo._linkColor = _propInfo._linkColor;
89 _propInfo._linkSave = _propInfo._linkColor;
90 _propInfo._caretColor = 0;
91 _propInfo._caretSave = 0;
92 _propInfo._caretShape = 2;
93 _propInfo._linkStyle = 1;
94 _monoInfo._linkStyle = 1;
95 _propInfo._justify = 0;
96 _propInfo._quotes = 1;
97 _propInfo._dashes = 1;
98 _propInfo._spaces = 0;
99 _propInfo._caps = 0;
100
101 const int DEFAULT_MARGIN_X = (_interpType == INTERPRETER_ZCODE) ? 0 : 15;
102 const int DEFAULT_MARGIN_Y = (_interpType == INTERPRETER_ZCODE) ? 0 : 15;
103 _wMarginX = _wMarginSaveX = DEFAULT_MARGIN_X;
104 _wMarginY = _wMarginSaveY = DEFAULT_MARGIN_Y;
105
106 // For simplicity's sake, only allow graphics when in non-paletted graphics modes
107 if (_screenFormat.bytesPerPixel == 1)
108 _graphics = false;
109
110 for (int i = 0; i < style_NUMSTYLES; ++i) {
111 _tStyles[i].fg = parseColor(T_STYLES[i].fg);
112 _tStyles[i].bg = parseColor(T_STYLES[i].bg);
113 _tStyles[i].font = T_STYLES[i].font;
114 _tStyles[i].reverse = T_STYLES[i].reverse;
115
116 _gStyles[i].fg = parseColor(G_STYLES[i].fg);
117 _gStyles[i].bg = parseColor(G_STYLES[i].bg);
118 _gStyles[i].font = G_STYLES[i].font;
119 _gStyles[i].reverse = G_STYLES[i].reverse;
120 }
121
122 Common::copy(_tStyles, _tStyles + style_NUMSTYLES, _tStylesDefault);
123 Common::copy(_gStyles, _gStyles + style_NUMSTYLES, _gStylesDefault);
124 }
125
synchronize()126 void Conf::synchronize() {
127 syncAsInt("width", _width);
128 syncAsInt("height", _height);
129 syncAsString("moreprompt", _propInfo._morePrompt);
130 syncAsColor("morecolor", _propInfo._moreColor);
131 syncAsColor("morecolor", _propInfo._moreSave);
132 syncAsFont("morefont", _propInfo._moreFont);
133 syncAsInt("morealign", _propInfo._moreAlign);
134 syncAsDouble("monoaspect", _monoInfo._aspect);
135 syncAsDouble("propaspect", _propInfo._aspect);
136 syncAsDouble("monosize", _monoInfo._size);
137 syncAsDouble("propsize", _propInfo._size);
138 syncAsInt("rows", _rows);
139 syncAsInt("cols", _cols);
140
141 _imageW = _width;
142 _imageH = _height;
143
144 syncAsInt("leading", _monoInfo._leading);
145 syncAsInt("leading", _propInfo._leading);
146 syncAsInt("baseline", _propInfo._baseLine);
147
148 if (_isLoading) {
149 if (exists("minrows"))
150 _rows = MAX(_rows, ConfMan.getInt("minrows"));
151 if (exists("maxrows"))
152 _rows = MIN(_rows, ConfMan.getInt("maxrows"));
153 if (exists("mincols"))
154 _cols = MAX(_cols, ConfMan.getInt("mincols"));
155 if (exists("maxcols"))
156 _cols = MIN(_cols, ConfMan.getInt("maxcols"));
157 } else {
158 ConfMan.setInt("minrows", 0);
159 ConfMan.setInt("maxrows", 999);
160 ConfMan.setInt("mincols", 0);
161 ConfMan.setInt("maxcols", 999);
162 }
163
164 syncAsInt("lockrows", _lockRows);
165 syncAsInt("lockcols", _lockCols);
166 syncAsInt("wmarginx", _wMarginX);
167 syncAsInt("wmarginy", _wMarginY);
168
169 _wMarginSaveX = _wMarginX;
170 _wMarginSaveY = _wMarginY;
171
172 syncAsInt("wpaddingx", _wPaddingX);
173 syncAsInt("wpaddingy", _wPaddingY);
174 syncAsInt("wborderx", _wBorderX);
175 syncAsInt("wbordery", _wBorderY);
176 syncAsInt("tmarginx", _tMarginX);
177 syncAsInt("tmarginy", _tMarginY);
178 syncAsDouble("gamma", _gamma);
179
180 syncAsColor("linkcolor", _propInfo._linkColor);
181 _monoInfo._linkColor = _propInfo._linkColor;
182 _propInfo._linkSave = _propInfo._linkColor;
183
184 syncAsColor("bordercolor", _borderColor);
185 syncAsColor("bordercolor", _borderSave);
186 syncAsColor("windowcolor", _windowColor);
187 syncAsColor("windowcolor", _windowSave);
188
189 syncAsColor("caretcolor", _propInfo._caretColor);
190 syncAsInt("caretshape", _propInfo._caretShape);
191 syncAsInt("linkstyle", _propInfo._linkStyle);
192 if (_isLoading) {
193 _propInfo._caretSave = _propInfo._caretColor;
194
195 _monoInfo._caretColor = _propInfo._caretColor;
196 _monoInfo._caretSave = _propInfo._caretSave;
197 _monoInfo._caretShape = _propInfo._caretShape;
198 _monoInfo._linkStyle = _propInfo._linkStyle;
199 }
200
201 syncAsInt("scrollwidth", _scrollWidth);
202 syncAsColor("scrollbg", _scrollBg);
203 syncAsColor("scrollfg", _scrollFg);
204 syncAsInt("justify", _propInfo._justify);
205 syncAsInt("quotes", _propInfo._quotes);
206 syncAsInt("dashes", _propInfo._dashes);
207 syncAsInt("spaces", _propInfo._spaces);
208 syncAsInt("caps", _propInfo._caps);
209
210 syncAsBool("graphics", _graphics);
211 syncAsBool("sound", _sound);
212 syncAsBool("speak", _speak);
213 syncAsBool("speak_input", _speakInput);
214 syncAsString("speak_language", _speakLanguage);
215 syncAsInt("stylehint", _styleHint);
216 syncAsBool("safeclicks", _safeClicks);
217
218 const char *const TG_COLOR[2] = { "tcolor_%d", "gcolor_%d" };
219 for (int tg = 0; tg < 2; ++tg) {
220 for (int style = 0; style <= 10; ++style) {
221 Common::String key = Common::String::format(TG_COLOR[tg], style);
222
223 if (_isLoading) {
224 if (exists(key)) {
225 Common::String line = ConfMan.get(key);
226 if (line.find(',') == 6) {
227 _tStyles[style].fg = parseColor(Common::String(line.c_str(), 6));
228 _tStyles[style].bg = parseColor(Common::String(line.c_str() + 7));
229 }
230 }
231 } else {
232 Common::String line = Common::String::format("%s,%s",
233 encodeColor(_tStyles[style].fg).c_str(),
234 encodeColor(_tStyles[style].bg).c_str()
235 );
236 ConfMan.set(key, line);
237 }
238 }
239 }
240
241 const char *const TG_FONT[2] = { "tfont_%d", "gfont_%d" };
242 for (int tg = 0; tg < 2; ++tg) {
243 for (int style = 0; style <= 10; ++style) {
244 Common::String key = Common::String::format(TG_FONT[tg], style);
245
246 if (_isLoading) {
247 if (exists(key)) {
248 FACES font = Screen::getFontId(ConfMan.get(key));
249 if (tg == 0)
250 _tStyles[style].font = font;
251 else
252 _gStyles[style].font = font;
253 }
254 } else {
255 FACES font = (tg == 0) ? _tStyles[style].font : _gStyles[style].font;
256 ConfMan.set(key, Screen::getFontName(font));
257 }
258 }
259 }
260 }
261
load()262 void Conf::load() {
263 _isLoading = true;
264 synchronize();
265
266 Common::copy(_tStyles, _tStyles + style_NUMSTYLES, _tStylesDefault);
267 Common::copy(_gStyles, _gStyles + style_NUMSTYLES, _gStylesDefault);
268 }
269
flush()270 void Conf::flush() {
271 // Default settings are only saved if they're not already present
272 if (!exists("width") || !exists("height")) {
273 _isLoading = false;
274 synchronize();
275 ConfMan.flushToDisk();
276 }
277 }
278
parseColor(const Common::String & str)279 uint Conf::parseColor(const Common::String &str) {
280 char r[3], g[3], b[3];
281 uint rv, gv, bv;
282
283 if (str.size() == 6) {
284 r[0] = str[0];
285 r[1] = str[1];
286 r[2] = 0;
287 g[0] = str[2];
288 g[1] = str[3];
289 g[2] = 0;
290 b[0] = str[4];
291 b[1] = str[5];
292 b[2] = 0;
293
294 rv = strtol(r, nullptr, 16);
295 gv = strtol(g, nullptr, 16);
296 bv = strtol(b, nullptr, 16);
297 return _screenFormat.RGBToColor(rv, gv, bv);
298 }
299
300 return 0;
301 }
302
encodeColor(uint color)303 Common::String Conf::encodeColor(uint color) {
304 byte r, g, b;
305 _screenFormat.colorToRGB(color, r, g, b);
306 return Common::String::format("%.2x%.2x%.2x", (int)r, (int)g, (int)b);
307 }
308
parseColor(const byte * rgb)309 uint Conf::parseColor(const byte *rgb) {
310 return _screenFormat.RGBToColor(rgb[0], rgb[1], rgb[2]);
311 }
312
parseColor(const uint32 rgb)313 uint Conf::parseColor(const uint32 rgb) {
314 byte r = (rgb >> 16) & 0xff,
315 g = (rgb >> 8) & 0xff,
316 b = rgb & 0xff;
317
318 return _screenFormat.RGBToColor(r, g, b);
319 }
320
syncAsString(const Common::String & name,Common::String & val)321 void Conf::syncAsString(const Common::String &name, Common::String &val) {
322 if (_isLoading && exists(name))
323 val = ConfMan.get(name);
324 else if (!_isLoading)
325 ConfMan.set(name, val);
326 }
327
syncAsInt(const Common::String & name,int & val)328 void Conf::syncAsInt(const Common::String &name, int &val) {
329 if (_isLoading && exists(name))
330 val = ConfMan.getInt(name);
331 else if (!_isLoading)
332 ConfMan.setInt(name, val);
333 }
334
syncAsInt(const Common::String & name,uint & val)335 void Conf::syncAsInt(const Common::String &name, uint &val) {
336 if (_isLoading && exists(name))
337 val = ConfMan.getInt(name);
338 else if (!_isLoading)
339 ConfMan.setInt(name, val);
340 }
341
syncAsDouble(const Common::String & name,double & val)342 void Conf::syncAsDouble(const Common::String &name, double &val) {
343 if (_isLoading && exists(name))
344 val = atof(ConfMan.get(name).c_str());
345 else if (!_isLoading)
346 ConfMan.set(name, Common::String::format("%f", (float)val).c_str());
347 }
348
syncAsBool(const Common::String & name,bool & val)349 void Conf::syncAsBool(const Common::String &name, bool &val) {
350 if (_isLoading && exists(name))
351 val = ConfMan.getBool(name);
352 else if (!_isLoading)
353 ConfMan.setBool(name, val);
354 }
355
syncAsColor(const Common::String & name,uint & val)356 void Conf::syncAsColor(const Common::String &name, uint &val) {
357 if (_isLoading && exists(name))
358 val = parseColor(ConfMan.get(name));
359 else if (!_isLoading)
360 ConfMan.set(name, encodeColor(val));
361 }
362
syncAsFont(const Common::String & name,FACES & val)363 void Conf::syncAsFont(const Common::String &name, FACES &val) {
364 if (_isLoading && exists(name))
365 val = Screen::getFontId(ConfMan.get(name));
366 else if (!_isLoading)
367 ConfMan.set(name, Screen::getFontName(val));
368 }
369
370 } // End of namespace Glk
371