1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 #include <stdarg.h>
15 #include "debug/debugmanager.h"
16 #include "util/string_types.h"
17
18 namespace AGS
19 {
20 namespace Common
21 {
22
DebugOutput(const String & id,IOutputHandler * handler,MessageType def_verbosity,bool enabled)23 DebugOutput::DebugOutput(const String &id, IOutputHandler *handler, MessageType def_verbosity, bool enabled)
24 : _id(id)
25 , _handler(handler)
26 , _enabled(enabled)
27 , _defaultVerbosity(def_verbosity)
28 {
29 _groupFilter.resize(DbgMgr._lastGroupID + 1, _defaultVerbosity);
30 }
31
GetID() const32 String DebugOutput::GetID() const
33 {
34 return _id;
35 }
36
GetHandler() const37 IOutputHandler *DebugOutput::GetHandler() const
38 {
39 return _handler;
40 }
41
IsEnabled() const42 bool DebugOutput::IsEnabled() const
43 {
44 return _enabled;
45 }
46
SetEnabled(bool enable)47 void DebugOutput::SetEnabled(bool enable)
48 {
49 _enabled = enable;
50 }
51
SetGroupFilter(DebugGroupID id,MessageType verbosity)52 void DebugOutput::SetGroupFilter(DebugGroupID id, MessageType verbosity)
53 {
54 uint32_t key = DbgMgr.GetGroup(id).UID.ID;
55 if (key != kDbgGroup_None)
56 _groupFilter[key] = verbosity;
57 else
58 _unresolvedGroups.insert(std::make_pair(id.SID, verbosity));
59 }
60
ResolveGroupID(DebugGroupID id)61 void DebugOutput::ResolveGroupID(DebugGroupID id)
62 {
63 if (!id.IsValid())
64 return;
65
66 DebugGroupID real_id = DbgMgr.GetGroup(id).UID;
67 if (real_id.IsValid())
68 {
69 if (_groupFilter.size() <= id.ID)
70 _groupFilter.resize(id.ID + 1, _defaultVerbosity);
71 GroupNameToMTMap::const_iterator it = _unresolvedGroups.find(real_id.SID);
72 if (it != _unresolvedGroups.end())
73 {
74 _groupFilter[real_id.ID] = it->second;
75 _unresolvedGroups.erase(it);
76 }
77 }
78 }
79
TestGroup(DebugGroupID id,MessageType mt) const80 bool DebugOutput::TestGroup(DebugGroupID id, MessageType mt) const
81 {
82 DebugGroupID real_id = DbgMgr.GetGroup(id).UID;
83 if (real_id.ID == kDbgGroup_None || real_id.ID >= _groupFilter.size())
84 return false;
85 return (_groupFilter[real_id.ID] & mt) != 0;
86 }
87
DebugManager()88 DebugManager::DebugManager()
89 {
90 // Add hardcoded groups
91 RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_Main, "main"), ""));
92 RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_SprCache, "sprcache"), "Sprite cache"));
93 RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_Script, "script"), "Script"));
94 RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_ManObj, "manobj"), "Managed obj"));
95 _firstFreeGroupID = _groups.size();
96 _lastGroupID = _firstFreeGroupID;
97 }
98
GetGroup(DebugGroupID id)99 DebugGroup DebugManager::GetGroup(DebugGroupID id)
100 {
101 if (id.ID != kDbgGroup_None)
102 {
103 return id.ID < _groups.size() ? _groups[id.ID] : DebugGroup();
104 }
105 else if (!id.SID.IsEmpty())
106 {
107 GroupByStringMap::const_iterator it = _groupByStrLookup.find(id.SID);
108 return it != _groupByStrLookup.end() ? _groups[it->second.ID] : DebugGroup();
109 }
110 return DebugGroup();
111 }
112
GetOutput(const String & id)113 PDebugOutput DebugManager::GetOutput(const String &id)
114 {
115 OutMap::const_iterator it = _outputs.find(id);
116 return it != _outputs.end() ? it->second.Target : PDebugOutput();
117 }
118
RegisterGroup(const String & id,const String & out_name)119 DebugGroup DebugManager::RegisterGroup(const String &id, const String &out_name)
120 {
121 DebugGroup group = GetGroup(id);
122 if (group.UID.IsValid())
123 return group;
124 group = DebugGroup(DebugGroupID(++DbgMgr._lastGroupID, id), out_name);
125 _groups.push_back(group);
126 _groupByStrLookup[group.UID.SID] = group.UID;
127
128 // Resolve group reference on every output target
129 for (OutMap::const_iterator it = _outputs.begin(); it != _outputs.end(); ++it)
130 {
131 it->second.Target->ResolveGroupID(group.UID);
132 }
133 return group;
134 }
135
RegisterGroup(const DebugGroup & group)136 void DebugManager::RegisterGroup(const DebugGroup &group)
137 {
138 _groups.push_back(group);
139 _groupByStrLookup[group.UID.SID] = group.UID;
140 }
141
RegisterOutput(const String & id,IOutputHandler * handler,MessageType def_verbosity,bool enabled)142 PDebugOutput DebugManager::RegisterOutput(const String &id, IOutputHandler *handler, MessageType def_verbosity, bool enabled)
143 {
144 _outputs[id].Target = PDebugOutput(new DebugOutput(id, handler, def_verbosity, enabled));
145 _outputs[id].Suppressed = false;
146 return _outputs[id].Target;
147 }
148
UnregisterAll()149 void DebugManager::UnregisterAll()
150 {
151 _lastGroupID = _firstFreeGroupID;
152 _groups.clear();
153 _groupByStrLookup.clear();
154 _outputs.clear();
155 }
156
UnregisterGroup(DebugGroupID id)157 void DebugManager::UnregisterGroup(DebugGroupID id)
158 {
159 DebugGroup group = GetGroup(id);
160 if (!group.UID.IsValid())
161 return;
162 _groups[group.UID.ID] = DebugGroup();
163 _groupByStrLookup.erase(group.UID.SID);
164 }
165
UnregisterOutput(const String & id)166 void DebugManager::UnregisterOutput(const String &id)
167 {
168 _outputs.erase(id);
169 }
170
Print(DebugGroupID group_id,MessageType mt,const String & text)171 void DebugManager::Print(DebugGroupID group_id, MessageType mt, const String &text)
172 {
173 const DebugGroup &group = GetGroup(group_id);
174 DebugMessage msg(text, group.UID.ID, group.OutputName, mt);
175
176 for (OutMap::iterator it = _outputs.begin(); it != _outputs.end(); ++it)
177 {
178 SendMessage(it->second, msg);
179 }
180 }
181
SendMessage(const String & out_id,const DebugMessage & msg)182 void DebugManager::SendMessage(const String &out_id, const DebugMessage &msg)
183 {
184 OutMap::iterator it = _outputs.find(out_id);
185 if (it != _outputs.end())
186 SendMessage(it->second, msg);
187 }
188
SendMessage(OutputSlot & out,const DebugMessage & msg)189 void DebugManager::SendMessage(OutputSlot &out, const DebugMessage &msg)
190 {
191 IOutputHandler *handler = out.Target->GetHandler();
192 if (!handler || !out.Target->IsEnabled() || out.Suppressed)
193 return;
194 if (!out.Target->TestGroup(msg.GroupID, msg.MT))
195 return;
196 // We suppress current target before the call so that if it makes
197 // a call to output system itself, message would not print to the
198 // same target
199 out.Suppressed = true;
200 handler->PrintMessage(msg);
201 out.Suppressed = false;
202 }
203
204 // TODO: move this to the dynamically allocated engine object whenever it is implemented
205 DebugManager DbgMgr;
206
207
208 namespace Debug
209 {
210
Printf(const char * fmt,...)211 void Printf(const char *fmt, ...)
212 {
213 va_list argptr;
214 va_start(argptr, fmt);
215 DbgMgr.Print(kDbgGroup_Main, kDbgMsg_Default, String::FromFormatV(fmt, argptr));
216 va_end(argptr);
217 }
218
Printf(MessageType mt,const char * fmt,...)219 void Printf(MessageType mt, const char *fmt, ...)
220 {
221 va_list argptr;
222 va_start(argptr, fmt);
223 DbgMgr.Print(kDbgGroup_Main, mt, String::FromFormatV(fmt, argptr));
224 va_end(argptr);
225 }
226
Printf(DebugGroupID group,MessageType mt,const char * fmt,...)227 void Printf(DebugGroupID group, MessageType mt, const char *fmt, ...)
228 {
229 va_list argptr;
230 va_start(argptr, fmt);
231 DbgMgr.Print(group, mt, String::FromFormatV(fmt, argptr));
232 va_end(argptr);
233 }
234
235 } // namespace Debug
236
237 } // namespace Common
238 } // namespace AGS
239