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 "ags/lib/std/algorithm.h"
24 #include "ags/lib/allegro.h" // find files
25 #include "ags/engine/gui/gui_dialog.h"
26 #include "ags/shared/ac/common.h"
27 #include "ags/engine/ac/draw.h"
28 #include "ags/engine/ac/game.h"
29 #include "ags/engine/ac/game_setup.h"
30 #include "ags/shared/ac/game_setup_struct.h"
31 #include "ags/engine/ac/global_game.h"
32 #include "ags/engine/gui/csci_dialog.h"
33 #include "ags/shared/gfx/bitmap.h"
34 #include "ags/engine/gfx/graphics_driver.h"
35 #include "ags/engine/debugging/debug_log.h"
36 #include "ags/shared/util/path.h"
37 #include "ags/ags.h"
38 #include "ags/globals.h"
39 
40 namespace AGS3 {
41 
42 using namespace AGS::Shared;
43 using namespace AGS::Engine;
44 
45 #undef MAXSAVEGAMES
46 #define MAXSAVEGAMES 20
47 
get_gui_dialog_buffer()48 char *get_gui_dialog_buffer() {
49 	return _G(buffer2);
50 }
51 
52 //
53 // TODO: rewrite the whole thing to work inside the main game update and render loop!
54 //
55 
prepare_gui_screen(int x,int y,int width,int height,bool opaque)56 Bitmap *prepare_gui_screen(int x, int y, int width, int height, bool opaque) {
57 	_G(windowPosX) = x;
58 	_G(windowPosY) = y;
59 	_G(windowPosWidth) = width;
60 	_G(windowPosHeight) = height;
61 	if (_G(windowBuffer)) {
62 		_G(windowBuffer) = recycle_bitmap(_G(windowBuffer), _G(windowBuffer)->GetColorDepth(), _G(windowPosWidth), _G(windowPosHeight), !opaque);
63 	} else {
64 		_G(windowBuffer) = BitmapHelper::CreateBitmap(_G(windowPosWidth), _G(windowPosHeight), _GP(game).GetColorDepth());
65 		_G(windowBuffer) = ReplaceBitmapWithSupportedFormat(_G(windowBuffer));
66 	}
67 	_G(dialogDDB) = recycle_ddb_bitmap(_G(dialogDDB), _G(windowBuffer), false, opaque);
68 	return _G(windowBuffer);
69 }
70 
get_gui_screen()71 Bitmap *get_gui_screen() {
72 	return _G(windowBuffer);
73 }
74 
clear_gui_screen()75 void clear_gui_screen() {
76 	if (_G(dialogDDB))
77 		_G(gfxDriver)->DestroyDDB(_G(dialogDDB));
78 	_G(dialogDDB) = nullptr;
79 	delete _G(windowBuffer);
80 	_G(windowBuffer) = nullptr;
81 }
82 
refresh_gui_screen()83 void refresh_gui_screen() {
84 	_G(gfxDriver)->UpdateDDBFromBitmap(_G(dialogDDB), _G(windowBuffer), false);
85 	render_graphics(_G(dialogDDB), _G(windowPosX), _G(windowPosY));
86 }
87 
loadgamedialog()88 int loadgamedialog() {
89 	const int wnd_width = 200;
90 	const int wnd_height = 120;
91 	const int boxleft = _G(myscrnwid) / 2 - wnd_width / 2;
92 	const int boxtop = _G(myscrnhit) / 2 - wnd_height / 2;
93 	const int buttonhit = _GP(usetup).textheight + 5;
94 
95 	int handl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
96 	int ctrlok =
97 	    CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 135, 5, 60, 10, get_global_message(MSG_RESTORE));
98 	int ctrlcancel =
99 	    CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 135, 5 + buttonhit, 60, 10,
100 	                      get_global_message(MSG_CANCEL));
101 	int ctrllist = CSCICreateControl(CNT_LISTBOX, 10, 30, 120, 80, nullptr);
102 	int ctrltex1 = CSCICreateControl(CNT_LABEL, 10, 5, 120, 0, get_global_message(MSG_SELECTLOAD));
103 	CSCISendControlMessage(ctrllist, CLB_CLEAR, 0, 0);
104 
105 	preparesavegamelist(ctrllist);
106 	CSCIMessage mes;
107 	_G(lpTemp) = nullptr;
108 	int toret = -1;
109 	while (1) {
110 		CSCIWaitMessage(&mes);      //printf("mess: %d, id %d ",mes.code,mes.id);
111 		if (mes.code == CM_COMMAND) {
112 			if (mes.id == ctrlok) {
113 				int cursel = CSCISendControlMessage(ctrllist, CLB_GETCURSEL, 0, 0);
114 				if ((cursel >= _G(numsaves)) | (cursel < 0))
115 					_G(lpTemp) = nullptr;
116 				else {
117 					toret = _G(filenumbers)[cursel];
118 					String path = get_save_game_path(toret);
119 					strcpy(_G(bufTemp), path.GetCStr());
120 					_G(lpTemp) = &_G(bufTemp)[0];
121 				}
122 			} else if (mes.id == ctrlcancel) {
123 				_G(lpTemp) = nullptr;
124 			}
125 
126 			break;
127 		}
128 	}
129 
130 	CSCIDeleteControl(ctrltex1);
131 	CSCIDeleteControl(ctrllist);
132 	CSCIDeleteControl(ctrlok);
133 	CSCIDeleteControl(ctrlcancel);
134 	CSCIEraseWindow(handl);
135 	return toret;
136 }
137 
savegamedialog()138 int savegamedialog() {
139 	char okbuttontext[50];
140 	strcpy(okbuttontext, get_global_message(MSG_SAVEBUTTON));
141 	char labeltext[200];
142 	strcpy(labeltext, get_global_message(MSG_SAVEDIALOG));
143 	const int wnd_width = 200;
144 	const int wnd_height = 120;
145 	const int boxleft = _G(myscrnwid) / 2 - wnd_width / 2;
146 	const int boxtop = _G(myscrnhit) / 2 - wnd_height / 2;
147 	const int buttonhit = _GP(usetup).textheight + 5;
148 	int labeltop = 5;
149 
150 	int handl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
151 	int ctrlcancel =
152 	    CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 135, 5 + buttonhit, 60, 10,
153 	                      get_global_message(MSG_CANCEL));
154 	int ctrllist = CSCICreateControl(CNT_LISTBOX, 10, 40, 120, 80, nullptr);
155 	int ctrltbox = 0;
156 
157 	CSCISendControlMessage(ctrllist, CLB_CLEAR, 0, 0);    // clear the list box
158 	preparesavegamelist(ctrllist);
159 	if (_G(toomanygames)) {
160 		strcpy(okbuttontext, get_global_message(MSG_REPLACE));
161 		strcpy(labeltext, get_global_message(MSG_MUSTREPLACE));
162 		labeltop = 2;
163 	} else
164 		ctrltbox = CSCICreateControl(CNT_TEXTBOX, 10, 29, 120, 0, nullptr);
165 
166 	int ctrlok = CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 135, 5, 60, 10, okbuttontext);
167 	int ctrltex1 = CSCICreateControl(CNT_LABEL, 10, labeltop, 120, 0, labeltext);
168 	CSCIMessage mes;
169 
170 	_G(lpTemp) = nullptr;
171 	if (_G(numsaves) > 0)
172 		CSCISendControlMessage(ctrllist, CLB_GETTEXT, 0, &_G(buffer2)[0]);
173 	else
174 		_G(buffer2)[0] = 0;
175 
176 	CSCISendControlMessage(ctrltbox, CTB_SETTEXT, 0, &_G(buffer2)[0]);
177 
178 	int toret = -1;
179 	while (1) {
180 		CSCIWaitMessage(&mes);      //printf("mess: %d, id %d ",mes.code,mes.id);
181 		if (mes.code == CM_COMMAND) {
182 			if (mes.id == ctrlok) {
183 				int cursell = CSCISendControlMessage(ctrllist, CLB_GETCURSEL, 0, 0);
184 				CSCISendControlMessage(ctrltbox, CTB_GETTEXT, 0, &_G(buffer2)[0]);
185 
186 				if (_G(numsaves) > 0)
187 					CSCISendControlMessage(ctrllist, CLB_GETTEXT, cursell, &_G(bufTemp)[0]);
188 				else
189 					strcpy(_G(bufTemp), "_NOSAVEGAMENAME");
190 
191 				if (_G(toomanygames)) {
192 					int nwhand = CSCIDrawWindow(boxleft + 5, boxtop + 20, 190, 65);
193 					int lbl1 =
194 					    CSCICreateControl(CNT_LABEL, 15, 5, 160, 0, get_global_message(MSG_REPLACEWITH1));
195 					int lbl2 = CSCICreateControl(CNT_LABEL, 25, 14, 160, 0, _G(bufTemp));
196 					int lbl3 =
197 					    CSCICreateControl(CNT_LABEL, 15, 25, 160, 0, get_global_message(MSG_REPLACEWITH2));
198 					int txt1 = CSCICreateControl(CNT_TEXTBOX, 15, 35, 160, 0, _G(bufTemp));
199 					int btnOk =
200 					    CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 25, 50, 60, 10,
201 					                      get_global_message(MSG_REPLACE));
202 					int btnCancel =
203 					    CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 95, 50, 60, 10,
204 					                      get_global_message(MSG_CANCEL));
205 
206 					CSCIMessage cmes;
207 					do {
208 						CSCIWaitMessage(&cmes);
209 					} while (cmes.code != CM_COMMAND);
210 
211 					CSCISendControlMessage(txt1, CTB_GETTEXT, 0, &_G(buffer2)[0]);
212 					CSCIDeleteControl(btnCancel);
213 					CSCIDeleteControl(btnOk);
214 					CSCIDeleteControl(txt1);
215 					CSCIDeleteControl(lbl3);
216 					CSCIDeleteControl(lbl2);
217 					CSCIDeleteControl(lbl1);
218 					CSCIEraseWindow(nwhand);
219 					_G(bufTemp)[0] = 0;
220 
221 					if (cmes.id == btnCancel) {
222 						_G(lpTemp) = nullptr;
223 						break;
224 					} else
225 						toret = _G(filenumbers)[cursell];
226 
227 				} else if (strcmp(_G(buffer2), _G(bufTemp)) != 0) {     // create a new game (description different)
228 					int highestnum = 0;
229 					for (int pp = 0; pp < _G(numsaves); pp++) {
230 						if (_G(filenumbers)[pp] > highestnum)
231 							highestnum = _G(filenumbers)[pp];
232 					}
233 
234 					if (highestnum > 90)
235 						quit("Save game directory overflow");
236 
237 					toret = highestnum + 1;
238 					String path = get_save_game_path(toret);
239 					strcpy(_G(bufTemp), path.GetCStr());
240 				} else {
241 					toret = _G(filenumbers)[cursell];
242 					_G(bufTemp)[0] = 0;
243 				}
244 
245 				if (_G(bufTemp)[0] == 0) {
246 					String path = get_save_game_path(toret);
247 					strcpy(_G(bufTemp), path.GetCStr());
248 				}
249 
250 				_G(lpTemp) = &_G(bufTemp)[0];
251 				_G(lpTemp2) = &_G(buffer2)[0];
252 			} else if (mes.id == ctrlcancel) {
253 				_G(lpTemp) = nullptr;
254 			}
255 			break;
256 		} else if (mes.code == CM_SELCHANGE) {
257 			int cursel = CSCISendControlMessage(ctrllist, CLB_GETCURSEL, 0, 0);
258 			if (cursel >= 0) {
259 				CSCISendControlMessage(ctrllist, CLB_GETTEXT, cursel, &_G(buffer2)[0]);
260 				CSCISendControlMessage(ctrltbox, CTB_SETTEXT, 0, &_G(buffer2)[0]);
261 			}
262 		}
263 	}
264 
265 	CSCIDeleteControl(ctrltbox);
266 	CSCIDeleteControl(ctrltex1);
267 	CSCIDeleteControl(ctrllist);
268 	CSCIDeleteControl(ctrlok);
269 	CSCIDeleteControl(ctrlcancel);
270 	CSCIEraseWindow(handl);
271 	return toret;
272 }
273 
preparesavegamelist(int ctrllist)274 void preparesavegamelist(int ctrllist) {
275 	// Get a list of savegames
276 	SaveStateList saveList = ::AGS::g_vm->listSaves();
277 
278 	// The original AGS sorts the list from most recent to oldest.
279 	// We don't have the modification date in ScummVM though. We could try to
280 	// parse the date string, but for now, sort by decreasing slot number, which
281 	// should work better than the default sort by increasing slot.
282 	Common::sort(saveList.begin(), saveList.end(),
283 	[](const SaveStateDescriptor & x, const SaveStateDescriptor & y) {
284 		return x.getSaveSlot() > y.getSaveSlot();
285 	});
286 
287 	// fill in the list box and global savegameindex[] array for backward compatibilty
288 	for (_G(numsaves) = 0; _G(numsaves) < (int)saveList.size(); ++_G(numsaves)) {
289 		CSCISendControlMessage(ctrllist, CLB_ADDITEM, 0,
290 		                       saveList[_G(numsaves)].getDescription().encode().c_str());
291 		_G(filenumbers)[_G(numsaves)] = saveList[_G(numsaves)].getSaveSlot();
292 		_G(filedates)[_G(numsaves)] = 0;
293 		//(long int)saveList[_G(numsaves)].FileTime;
294 	}
295 	_G(toomanygames) = (_G(numsaves) >= MAXSAVEGAMES) ? 1 : 0;
296 	// Select the first item
297 	CSCISendControlMessage(ctrllist, CLB_SETCURSEL, 0, 0);
298 }
299 
enterstringwindow(const char * prompttext,char * stouse)300 void enterstringwindow(const char *prompttext, char *stouse) {
301 	const int wnd_width = 200;
302 	const int wnd_height = 40;
303 	const int boxleft = 60, boxtop = 80;
304 	int wantCancel = 0;
305 	if (prompttext[0] == '!') {
306 		wantCancel = 1;
307 		prompttext++;
308 	}
309 
310 	int handl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
311 	int ctrlok = CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 135, 5, 60, 10, "OK");
312 	int ctrlcancel = -1;
313 	if (wantCancel)
314 		ctrlcancel = CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 135, 20, 60, 10, get_global_message(MSG_CANCEL));
315 	int ctrltbox = CSCICreateControl(CNT_TEXTBOX, 10, 29, 120, 0, nullptr);
316 	int ctrltex1 = CSCICreateControl(CNT_LABEL, 10, 5, 120, 0, prompttext);
317 	CSCIMessage mes;
318 
319 	while (1) {
320 		CSCIWaitMessage(&mes);
321 		if (mes.code == CM_COMMAND) {
322 			if (mes.id == ctrlcancel)
323 				_G(buffer2)[0] = 0;
324 			else
325 				CSCISendControlMessage(ctrltbox, CTB_GETTEXT, 0, &_G(buffer2)[0]);
326 			break;
327 		}
328 	}
329 
330 	CSCIDeleteControl(ctrltex1);
331 	CSCIDeleteControl(ctrltbox);
332 	CSCIDeleteControl(ctrlok);
333 	if (wantCancel)
334 		CSCIDeleteControl(ctrlcancel);
335 	CSCIEraseWindow(handl);
336 	strcpy(stouse, _G(buffer2));
337 }
338 
enternumberwindow(char * prompttext)339 int enternumberwindow(char *prompttext) {
340 	char ourbuf[200];
341 	enterstringwindow(prompttext, ourbuf);
342 	if (ourbuf[0] == 0)
343 		return -9999;
344 	return atoi(ourbuf);
345 }
346 
roomSelectorWindow(int currentRoom,int numRooms,int * roomNumbers,char ** roomNames)347 int roomSelectorWindow(int currentRoom, int numRooms, int *roomNumbers, char **roomNames) {
348 	char labeltext[200];
349 	strcpy(labeltext, get_global_message(MSG_SAVEDIALOG));
350 	const int wnd_width = 240;
351 	const int wnd_height = 160;
352 	const int boxleft = _G(myscrnwid) / 2 - wnd_width / 2;
353 	const int boxtop = _G(myscrnhit) / 2 - wnd_height / 2;
354 	const int labeltop = 5;
355 
356 	int handl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
357 	int ctrllist = CSCICreateControl(CNT_LISTBOX, 10, 40, 220, 100, nullptr);
358 	int ctrlcancel =
359 	    CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 80, 145, 60, 10, "Cancel");
360 
361 	CSCISendControlMessage(ctrllist, CLB_CLEAR, 0, 0);    // clear the list box
362 	for (int aa = 0; aa < numRooms; aa++) {
363 		sprintf(_G(buff), "%3d %s", roomNumbers[aa], roomNames[aa]);
364 		CSCISendControlMessage(ctrllist, CLB_ADDITEM, 0, &_G(buff)[0]);
365 		if (roomNumbers[aa] == currentRoom) {
366 			CSCISendControlMessage(ctrllist, CLB_SETCURSEL, aa, 0);
367 		}
368 	}
369 
370 	int ctrlok = CSCICreateControl(CNT_PUSHBUTTON | CNF_DEFAULT, 10, 145, 60, 10, "OK");
371 	int ctrltex1 = CSCICreateControl(CNT_LABEL, 10, labeltop, 180, 0, "Choose which room to go to:");
372 	CSCIMessage mes;
373 
374 	_G(lpTemp) = nullptr;
375 	_G(buffer2)[0] = 0;
376 
377 	int ctrltbox = CSCICreateControl(CNT_TEXTBOX, 10, 29, 120, 0, nullptr);
378 	CSCISendControlMessage(ctrltbox, CTB_SETTEXT, 0, &_G(buffer2)[0]);
379 
380 	int toret = -1;
381 	while (1) {
382 		CSCIWaitMessage(&mes);      //printf("mess: %d, id %d ",mes.code,mes.id);
383 		if (mes.code == CM_COMMAND) {
384 			if (mes.id == ctrlok) {
385 				CSCISendControlMessage(ctrltbox, CTB_GETTEXT, 0, &_G(buffer2)[0]);
386 				if (Common::isDigit(_G(buffer2)[0])) {
387 					toret = atoi(_G(buffer2));
388 				}
389 			} else if (mes.id == ctrlcancel) {
390 			}
391 			break;
392 		} else if (mes.code == CM_SELCHANGE) {
393 			int cursel = CSCISendControlMessage(ctrllist, CLB_GETCURSEL, 0, 0);
394 			if (cursel >= 0) {
395 				sprintf(_G(buffer2), "%d", roomNumbers[cursel]);
396 				CSCISendControlMessage(ctrltbox, CTB_SETTEXT, 0, &_G(buffer2)[0]);
397 			}
398 		}
399 	}
400 
401 	CSCIDeleteControl(ctrltbox);
402 	CSCIDeleteControl(ctrltex1);
403 	CSCIDeleteControl(ctrllist);
404 	CSCIDeleteControl(ctrlok);
405 	CSCIDeleteControl(ctrlcancel);
406 	CSCIEraseWindow(handl);
407 	return toret;
408 }
409 
myscimessagebox(const char * lpprompt,char * btn1,char * btn2)410 int myscimessagebox(const char *lpprompt, char *btn1, char *btn2) {
411 	const int wnd_width = 240 - 80;
412 	const int wnd_height = 120 - 80;
413 	const int boxleft = 80;
414 	const int boxtop = 80;
415 
416 	int windl = CSCIDrawWindow(boxleft, boxtop, wnd_width, wnd_height);
417 	int lbl1 = CSCICreateControl(CNT_LABEL, 10, 5, 150, 0, lpprompt);
418 	int btflag = CNT_PUSHBUTTON;
419 
420 	if (btn2 == nullptr)
421 		btflag |= CNF_DEFAULT | CNF_CANCEL;
422 	else
423 		btflag |= CNF_DEFAULT;
424 
425 	int btnQuit = CSCICreateControl(btflag, 10, 25, 60, 10, btn1);
426 	int btnPlay = 0;
427 
428 	if (btn2 != nullptr)
429 		btnPlay = CSCICreateControl(CNT_PUSHBUTTON | CNF_CANCEL, 85, 25, 60, 10, btn2);
430 
431 	_GP(smes).code = 0;
432 
433 	do {
434 		if (SHOULD_QUIT)
435 			return 1;
436 
437 		CSCIWaitMessage(&_GP(smes));
438 	} while (_GP(smes).code != CM_COMMAND);
439 
440 	if (btnPlay)
441 		CSCIDeleteControl(btnPlay);
442 
443 	CSCIDeleteControl(btnQuit);
444 	CSCIDeleteControl(lbl1);
445 	CSCIEraseWindow(windl);
446 
447 	if (_GP(smes).id == btnQuit)
448 		return 1;
449 
450 	return 0;
451 }
452 
quitdialog()453 int quitdialog() {
454 	char quitbut[50], playbut[50];
455 	strcpy(quitbut, get_global_message(MSG_QUITBUTTON));
456 	strcpy(playbut, get_global_message(MSG_PLAYBUTTON));
457 	return myscimessagebox(get_global_message(MSG_QUITDIALOG), quitbut, playbut);
458 }
459 
460 } // namespace AGS3
461