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/console.h"
24 #include "ags/ags.h"
25 #include "ags/globals.h"
26 #include "ags/shared/ac/sprite_cache.h"
27 #include "ags/shared/gfx/allegro_bitmap.h"
28 #include "ags/shared/script/cc_options.h"
29 #include "image/png.h"
30 
31 namespace AGS {
32 
AGSConsole(AGSEngine * vm)33 AGSConsole::AGSConsole(AGSEngine *vm) : GUI::Debugger(), _vm(vm), _logOutputTarget(nullptr), _agsDebuggerOutput(nullptr) {
34 	registerCmd("ags_debug_groups_list",   WRAP_METHOD(AGSConsole, Cmd_listDebugGroups));
35 	registerCmd("ags_debug_groups_set",  WRAP_METHOD(AGSConsole, Cmd_setDebugGroupLevel));
36 	registerCmd("ags_set_script_dump", WRAP_METHOD(AGSConsole, Cmd_SetScriptDump));
37 	registerCmd("ags_sprite_info",   WRAP_METHOD(AGSConsole, Cmd_getSpriteInfo));
38 	registerCmd("ags_sprite_dump",  WRAP_METHOD(AGSConsole, Cmd_dumpSprite));
39 
40 	_logOutputTarget = new LogOutputTarget();
41 	_agsDebuggerOutput = _GP(DbgMgr).RegisterOutput("ScummVMLog", _logOutputTarget, AGS3::AGS::Shared::kDbgMsg_None);
42 }
43 
~AGSConsole()44 AGSConsole::~AGSConsole() {
45 	delete _logOutputTarget;
46 }
47 
48 struct LevelName {
49 	const char *name;
50 	AGS3::AGS::Shared::MessageType level;
51 };
52 
53 static const LevelName levelNames[] = {
54 	{"none", AGS3::AGS::Shared::kDbgMsg_None},
55 	{"alerts", AGS3::AGS::Shared::kDbgMsg_Alert},
56 	{"fatal", AGS3::AGS::Shared::kDbgMsg_Fatal},
57 	{"errors", AGS3::AGS::Shared::kDbgMsg_Error},
58 	{"warnings", AGS3::AGS::Shared::kDbgMsg_None},
59 	{"info", AGS3::AGS::Shared::kDbgMsg_Info},
60 	{"debug", AGS3::AGS::Shared::kDbgMsg_Debug},
61 	{nullptr, AGS3::AGS::Shared::kDbgMsg_None}
62 };
63 
64 struct GroupName {
65 	const char *name;
66 	AGS3::uint32_t group;
67 };
68 
69 static const GroupName groupNames[] = {
70 	{"Main", AGS3::AGS::Shared::kDbgGroup_Main},
71 	{"Game", AGS3::AGS::Shared::kDbgGroup_Game},
72 	{"Script", AGS3::AGS::Shared::kDbgGroup_Script},
73 	{"SpriteCache", AGS3::AGS::Shared::kDbgGroup_SprCache},
74 	{"ManObj", AGS3::AGS::Shared::kDbgGroup_ManObj},
75 	{nullptr, (AGS3::uint32_t)-1}
76 };
77 
Cmd_listDebugGroups(int argc,const char ** argv)78 bool AGSConsole::Cmd_listDebugGroups(int argc, const char **argv) {
79 	if (argc != 1) {
80 		debugPrintf("Usage: %s\n", argv[0]);
81 		return true;
82 	}
83 
84 	debugPrintf("%-16s %-16s\n", "Name", "Level");
85 	for (int i = 0 ; groupNames[i].name != nullptr ; ++i)
86 		debugPrintf("%-16s %-16s\n", groupNames[i].name, getVerbosityLevel(groupNames[i].group));
87 	return true;
88 }
89 
Cmd_setDebugGroupLevel(int argc,const char ** argv)90 bool AGSConsole::Cmd_setDebugGroupLevel(int argc, const char **argv) {
91 	if (argc != 3) {
92 		debugPrintf("Usage: %s group level\n", argv[0]);
93 		debugPrintf("   valid groups: ");
94 		printGroupList();
95 		debugPrintf("\n");
96 		debugPrintf("   valid levels: ");
97 		printLevelList();
98 		debugPrintf("\n");
99 		return true;
100 	}
101 
102 	bool found = false;
103 	AGS3::uint32_t group = parseGroup(argv[1], found);
104 	if (!found) {
105 		debugPrintf("Unknown debug group '%s'\n", argv[1]);
106 		debugPrintf("Valid groups are: ");
107 		printGroupList();
108 		debugPrintf("\n");
109 		return true;
110 	}
111 
112 	AGS3::AGS::Shared::MessageType level = parseLevel(argv[2], found);
113 	if (!found) {
114 		debugPrintf("Unknown level '%s'\n", argv[2]);
115 		debugPrintf("Valid levels are: ");
116 		printLevelList();
117 		debugPrintf("\n");
118 		return true;
119 	}
120 
121 	_agsDebuggerOutput->SetGroupFilter(group, level);
122 	return true;
123 }
124 
getVerbosityLevel(AGS3::uint32_t groupID) const125 const char *AGSConsole::getVerbosityLevel(AGS3::uint32_t groupID) const {
126 	int i = 1;
127 	while (levelNames[i].name != nullptr) {
128 		if (!_agsDebuggerOutput->TestGroup(groupID, levelNames[i].level))
129 			break;
130 		++i;
131 	}
132 	return levelNames[i - 1].name;
133 }
134 
parseGroup(const char * name,bool & found) const135 AGS3::uint32_t AGSConsole::parseGroup(const char *name, bool &found) const {
136 	int i = 0;
137 	while (groupNames[i].name != nullptr) {
138 		if (scumm_stricmp(name, groupNames[i].name) == 0) {
139 			found = true;
140 			return groupNames[i].group;
141 		}
142 		++i;
143 	}
144 
145 	found = false;
146 	return (AGS3::uint32_t)-1;
147 }
148 
parseLevel(const char * name,bool & found) const149 AGS3::AGS::Shared::MessageType AGSConsole::parseLevel(const char *name, bool &found) const {
150 	int i = 0;
151 	while (levelNames[i].name != nullptr) {
152 		if (scumm_stricmp(name, levelNames[i].name) == 0) {
153 			found = true;
154 			return levelNames[i].level;
155 		}
156 		++i;
157 	}
158 
159 	found = false;
160 	return AGS3::AGS::Shared::kDbgMsg_None;
161 }
162 
printGroupList()163 void AGSConsole::printGroupList() {
164 	debugPrintf("%s", groupNames[0].name);
165 	for (int i = 1 ; groupNames[i].name != nullptr ; ++i)
166 		debugPrintf(", %s", groupNames[i].name);
167 }
168 
printLevelList()169 void AGSConsole::printLevelList() {
170 	debugPrintf("%s", levelNames[0].name);
171 	for (int i = 1 ; levelNames[i].name != nullptr ; ++i)
172 		debugPrintf(", %s", levelNames[i].name);
173 }
174 
Cmd_SetScriptDump(int argc,const char ** argv)175 bool AGSConsole::Cmd_SetScriptDump(int argc, const char **argv) {
176 	if (argc != 2) {
177 		debugPrintf("Usage: %s [on|off]\n", argv[0]);
178 		return true;
179 	}
180 
181 	if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "true") == 0)
182 		AGS3::ccSetOption(SCOPT_DEBUGRUN, 1);
183 	else
184 		AGS3::ccSetOption(SCOPT_DEBUGRUN, 0);
185 	return true;
186 }
187 
Cmd_getSpriteInfo(int argc,const char ** argv)188 bool AGSConsole::Cmd_getSpriteInfo(int argc, const char **argv) {
189 	if (argc != 2) {
190 		debugPrintf("Usage: %s SpriteNumber\n", argv[0]);
191 		return true;
192 	}
193 
194 	int spriteId = atoi(argv[1]);
195 	if (!_GP(spriteset).DoesSpriteExist(spriteId)) {
196 		debugPrintf("Sprite %d does not exist\n", spriteId);
197 		return true;
198 	}
199 
200 	AGS3::Shared::Bitmap *sprite = _GP(spriteset)[spriteId];
201 	if (!sprite) {
202 		debugPrintf("Failed to get sprite %d\n", spriteId);
203 		return true;
204 	}
205 
206 	debugPrintf("Size: %dx%d\n", sprite->GetWidth(), sprite->GetHeight());
207 	debugPrintf("Color depth: %d\n", sprite->GetColorDepth());
208 	return true;
209 }
210 
Cmd_dumpSprite(int argc,const char ** argv)211 bool AGSConsole::Cmd_dumpSprite(int argc, const char **argv) {
212 	if (argc != 2) {
213 		debugPrintf("Usage: %s SpriteNumber\n", argv[0]);
214 		return true;
215 	}
216 
217 	int spriteId = atoi(argv[1]);
218 	if (!_GP(spriteset).DoesSpriteExist(spriteId)) {
219 		debugPrintf("Sprite %d does not exist\n", spriteId);
220 		return true;
221 	}
222 
223 	AGS3::Shared::Bitmap *sprite = _GP(spriteset)[spriteId];
224 	if (!sprite) {
225 		debugPrintf("Failed to get sprite %d\n", spriteId);
226 		return true;
227 	}
228 
229 	Common::String pngFile = Common::String::format("%s-sprite%03d.png", _vm->getGameId().c_str(), spriteId);
230 	Common::DumpFile df;
231 	if (df.open(pngFile)) {
232 		byte *palette = nullptr;
233 		if (sprite->GetColorDepth() == 8) {
234 			palette = new byte[256 * 3];
235 			for (int c = 0, i = 0 ; c < 256 ; ++c, i += 3) {
236 				palette[i] = _G(current_palette)[c].r * 255 / 63;
237 				palette[i + 1] = _G(current_palette)[c].g * 255 / 63;
238 				palette[i + 2] = _G(current_palette)[c].b * 255 / 63;
239 			}
240 		}
241 		Image::writePNG(df, sprite->GetAllegroBitmap()->getSurface().rawSurface(), palette);
242 		delete[] palette;
243 	}
244 
245 	return true;
246 }
247 
LogOutputTarget()248 LogOutputTarget::LogOutputTarget() {
249 }
250 
~LogOutputTarget()251 LogOutputTarget::~LogOutputTarget() {
252 }
253 
PrintMessage(const AGS3::AGS::Shared::DebugMessage & msg)254 void LogOutputTarget::PrintMessage(const AGS3::AGS::Shared::DebugMessage &msg) {
255 	LogMessageType::Type msgType = LogMessageType::kInfo;
256 	switch (msg.MT) {
257 	case AGS3::AGS::Shared::kDbgMsg_None:
258 		return;
259 	case AGS3::AGS::Shared::kDbgMsg_Alert:
260 	case AGS3::AGS::Shared::kDbgMsg_Fatal:
261 	case AGS3::AGS::Shared::kDbgMsg_Error:
262 		msgType = LogMessageType::kError;
263 		break;
264 	case AGS3::AGS::Shared::kDbgMsg_Warn:
265 		msgType = LogMessageType::kWarning;
266 		break;
267 	case AGS3::AGS::Shared::kDbgMsg_Info:
268 		msgType = LogMessageType::kInfo;
269 		break;
270 	case AGS3::AGS::Shared::kDbgMsg_Debug:
271 		msgType = LogMessageType::kDebug;
272 		break;
273 	}
274 	Common::String text = Common::String::format("%s\n", msg.Text.GetCStr());
275 	g_system->logMessage(msgType, text.c_str());
276 }
277 
278 
279 } // End of namespace AGS
280