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