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