Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "DebugSerializer.h"
21 :
22 : #include "scriptinterface/FunctionWrapper.h"
23 : #include "scriptinterface/Object.h"
24 : #include "scriptinterface/ScriptRequest.h"
25 : #include "scriptinterface/JSON.h"
26 :
27 : #include "lib/secure_crt.h"
28 : #include "lib/utf8.h"
29 : #include "ps/CStr.h"
30 :
31 : #include <sstream>
32 : #include <iomanip>
33 :
34 : /*
35 : * The output format here is intended to be compatible with YAML,
36 : * so it is human readable and usable in diff and can also be parsed with
37 : * external tools.
38 : */
39 :
40 : // MSVC and GCC give slightly different serializations of floats
41 : // (e.g. "1e+010" vs "1e+10"). To make the debug serialization easily comparable
42 : // across platforms, we want to convert to a canonical form.
43 : // TODO: we just do e+0xx now; ought to handle varying precisions and inf and nan etc too
44 : template<typename T>
45 20 : std::string canonfloat(T value, int prec)
46 : {
47 40 : std::stringstream str;
48 20 : str << std::setprecision(prec) << value;
49 20 : std::string r = str.str();
50 20 : size_t e = r.find('e');
51 20 : if (e == r.npos) // no "e"
52 8 : return r;
53 12 : if (e == r.length() - 5 && r[e + 2] == '0') // e.g. "1e+010"
54 0 : r.erase(e + 2, 1);
55 12 : return r;
56 : }
57 :
58 21 : CDebugSerializer::CDebugSerializer(const ScriptInterface& scriptInterface, std::ostream& stream, bool includeDebugInfo) :
59 21 : m_ScriptInterface(scriptInterface), m_Stream(stream), m_IsDebug(includeDebugInfo), m_Indent(0)
60 : {
61 21 : }
62 :
63 8 : void CDebugSerializer::Indent(int spaces)
64 : {
65 8 : m_Indent += spaces;
66 8 : }
67 :
68 8 : void CDebugSerializer::Dedent(int spaces)
69 : {
70 8 : ENSURE(spaces <= m_Indent);
71 8 : m_Indent -= spaces;
72 8 : }
73 :
74 : #define INDENT std::string(m_Indent, ' ')
75 :
76 1 : void CDebugSerializer::Comment(const std::string& comment)
77 : {
78 1 : m_Stream << INDENT << "# " << comment << "\n";
79 1 : }
80 :
81 25 : void CDebugSerializer::TextLine(const std::string& text)
82 : {
83 25 : m_Stream << INDENT << text << "\n";
84 25 : }
85 :
86 1 : void CDebugSerializer::PutNumber(const char* name, uint8_t value)
87 : {
88 1 : m_Stream << INDENT << name << ": " << (int)value << "\n";
89 1 : }
90 :
91 1 : void CDebugSerializer::PutNumber(const char* name, int8_t value)
92 : {
93 1 : m_Stream << INDENT << name << ": " << (int)value << "\n";
94 1 : }
95 :
96 9 : void CDebugSerializer::PutNumber(const char* name, uint16_t value)
97 : {
98 9 : m_Stream << INDENT << name << ": " << value << "\n";
99 9 : }
100 :
101 1 : void CDebugSerializer::PutNumber(const char* name, int16_t value)
102 : {
103 1 : m_Stream << INDENT << name << ": " << value << "\n";
104 1 : }
105 :
106 43 : void CDebugSerializer::PutNumber(const char* name, uint32_t value)
107 : {
108 43 : m_Stream << INDENT << name << ": " << value << "\n";
109 43 : }
110 :
111 19 : void CDebugSerializer::PutNumber(const char* name, int32_t value)
112 : {
113 19 : m_Stream << INDENT << name << ": " << value << "\n";
114 19 : }
115 :
116 9 : void CDebugSerializer::PutNumber(const char* name, float value)
117 : {
118 9 : m_Stream << INDENT << name << ": " << canonfloat(value, 8) << "\n";
119 9 : }
120 :
121 11 : void CDebugSerializer::PutNumber(const char* name, double value)
122 : {
123 11 : m_Stream << INDENT << name << ": " << canonfloat(value, 17) << "\n";
124 11 : }
125 :
126 56 : void CDebugSerializer::PutNumber(const char* name, fixed value)
127 : {
128 56 : m_Stream << INDENT << name << ": " << value.ToString() << "\n";
129 56 : }
130 :
131 20 : void CDebugSerializer::PutBool(const char* name, bool value)
132 : {
133 20 : m_Stream << INDENT << name << ": " << (value ? "true" : "false") << "\n";
134 20 : }
135 :
136 12 : void CDebugSerializer::PutString(const char* name, const std::string& value)
137 : {
138 24 : std::string escaped;
139 12 : escaped.reserve(value.size());
140 99 : for (size_t i = 0; i < value.size(); ++i)
141 87 : if (value[i] == '"')
142 2 : escaped += "\\\"";
143 85 : else if (value[i] == '\\')
144 1 : escaped += "\\\\";
145 84 : else if (value[i] == '\n')
146 2 : escaped += "\\n";
147 : else
148 82 : escaped += value[i];
149 :
150 12 : m_Stream << INDENT << name << ": " << "\"" << escaped << "\"\n";
151 12 : }
152 :
153 9 : void CDebugSerializer::PutScriptVal(const char* name, JS::MutableHandleValue value)
154 : {
155 18 : ScriptRequest rq(m_ScriptInterface);
156 :
157 18 : JS::RootedValue serialize(rq.cx);
158 9 : if (Script::GetProperty(rq, value, "Serialize", &serialize) && !serialize.isNullOrUndefined())
159 : {
160 : // If the value has a Serialize property, pretty-parse that instead.
161 : // (this gives more accurate OOS reports).
162 1 : ScriptFunction::Call(rq, value, "Serialize", &serialize);
163 2 : std::string serialized_source = Script::ToString(rq, &serialize, true);
164 1 : m_Stream << INDENT << name << ": " << serialized_source << "\n";
165 : }
166 : else
167 : {
168 16 : std::string source = Script::ToString(rq, value, true);
169 8 : m_Stream << INDENT << name << ": " << source << "\n";
170 : }
171 9 : }
172 :
173 1 : void CDebugSerializer::PutRaw(const char* name, const u8* data, size_t len)
174 : {
175 1 : m_Stream << INDENT << name << ": (" << len << " bytes)";
176 :
177 : char buf[4];
178 7 : for (size_t i = 0; i < len; ++i)
179 : {
180 6 : sprintf_s(buf, ARRAY_SIZE(buf), " %02x", (unsigned int)data[i]);
181 6 : m_Stream << buf;
182 : }
183 :
184 1 : m_Stream << "\n";
185 1 : }
186 :
187 6 : bool CDebugSerializer::IsDebug() const
188 : {
189 6 : return m_IsDebug;
190 : }
191 :
192 0 : std::ostream& CDebugSerializer::GetStream()
193 : {
194 0 : return m_Stream;
195 : }
|