CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Debugger/WebSocket/WebSocketUtils.cpp
Views: 1401
1
// Copyright (c) 2018- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <cmath>
19
#include <limits>
20
21
#include "Common/Data/Text/Parsers.h"
22
#include "Common/StringUtils.h"
23
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
24
#include "Core/MemMap.h"
25
26
inline void DebuggerJsonAddTicket(JsonWriter &writer, const JsonGet &data) {
27
const JsonNode *value = data.get("ticket");
28
if (value)
29
writer.writeRaw("ticket", json_stringify(value));
30
}
31
32
JsonWriter &DebuggerRequest::Respond() {
33
writer_.begin();
34
writer_.writeString("event", name);
35
DebuggerJsonAddTicket(writer_, data);
36
37
responseBegun_ = true;
38
return writer_;
39
}
40
41
bool DebuggerRequest::Finish() {
42
if (responseBegun_ && !responseSent_) {
43
writer_.end();
44
if (responsePartial_)
45
ws->AddFragment(true, writer_.str());
46
else
47
ws->Send(writer_.str());
48
responseBegun_ = false;
49
responseSent_ = true;
50
responsePartial_ = false;
51
}
52
53
return responseSent_;
54
}
55
56
void DebuggerRequest::Flush() {
57
ws->AddFragment(false, writer_.flush());
58
responsePartial_ = true;
59
}
60
61
static bool U32FromString(const char *str, uint32_t *out, bool allowFloat) {
62
if (TryParse(str, out))
63
return true;
64
65
// Now let's try signed (the above parses only positive.)
66
if (str[0] == '-' && TryParse(&str[1], out)) {
67
*out = static_cast<uint32_t>(-static_cast<int>(*out));
68
return true;
69
}
70
71
// We have to try float last because we use float bits, so 1.0 != 1.
72
if (allowFloat) {
73
union {
74
uint32_t u;
75
float f;
76
} bits;
77
if (TryParse(str, &bits.f)) {
78
*out = bits.u;
79
return true;
80
}
81
82
if (!strcasecmp(str, "nan")) {
83
*out = 0x7FC00000;
84
return true;
85
} else if (!strcasecmp(str, "infinity") || !strcasecmp(str, "inf")) {
86
*out = 0x7F800000;
87
return true;
88
} else if (!strcasecmp(str, "-infinity") || !strcasecmp(str, "-inf")) {
89
*out = 0xFF800000;
90
return true;
91
}
92
}
93
94
return false;
95
}
96
97
bool DebuggerRequest::HasParam(const char *name, bool ignoreNull) {
98
const JsonNode *node = data.get(name);
99
if (!node) {
100
return false;
101
}
102
if (node->value.getTag() == JSON_NULL) {
103
return !ignoreNull;
104
}
105
return true;
106
}
107
108
bool DebuggerRequest::ParamU32(const char *name, uint32_t *out, bool allowFloatBits, DebuggerParamType type) {
109
bool allowLoose = type == DebuggerParamType::REQUIRED_LOOSE || type == DebuggerParamType::OPTIONAL_LOOSE;
110
bool required = type == DebuggerParamType::REQUIRED || type == DebuggerParamType::REQUIRED_LOOSE;
111
112
const JsonNode *node = data.get(name);
113
if (!node) {
114
if (required)
115
Fail(StringFromFormat("Missing '%s' parameter", name));
116
return !required;
117
}
118
119
auto tag = node->value.getTag();
120
if (tag == JSON_NUMBER) {
121
double val = node->value.toNumber();
122
bool isInteger = trunc(val) == val;
123
if (!isInteger && !allowLoose) {
124
// JSON doesn't give a great way to differentiate ints and floats.
125
// Let's play it safe and require a string.
126
if (allowFloatBits)
127
Fail(StringFromFormat("Could not parse '%s' parameter: use a string for non integer values", name));
128
else
129
Fail(StringFromFormat("Could not parse '%s' parameter: integer required", name));
130
return false;
131
} else if (!isInteger && allowFloatBits) {
132
union {
133
float f;
134
uint32_t u;
135
} bits = { (float)val };
136
*out = bits.u;
137
return true;
138
}
139
140
if (val < 0 && val >= std::numeric_limits<int32_t>::min()) {
141
// Convert to unsigned representation.
142
*out = (uint32_t)(int32_t)val;
143
return true;
144
} else if (val >= 0 && val <= std::numeric_limits<uint32_t>::max()) {
145
*out = (uint32_t)val;
146
return true;
147
} else if (allowLoose) {
148
*out = val >= 0 ? std::numeric_limits<uint32_t>::max() : std::numeric_limits<uint32_t>::min();
149
return true;
150
}
151
152
if (allowFloatBits)
153
Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range (use string for float)", name));
154
else
155
Fail(StringFromFormat("Could not parse '%s' parameter: outside 32 bit range", name));
156
return false;
157
}
158
if (tag != JSON_STRING) {
159
if (type == DebuggerParamType::REQUIRED || tag != JSON_NULL) {
160
Fail(StringFromFormat("Invalid '%s' parameter type", name));
161
return false;
162
}
163
return true;
164
}
165
166
if (U32FromString(node->value.toString(), out, allowFloatBits))
167
return true;
168
169
if (allowFloatBits)
170
Fail(StringFromFormat("Could not parse '%s' parameter: number expected", name));
171
else
172
Fail(StringFromFormat("Could not parse '%s' parameter: integer required", name));
173
return false;
174
}
175
176
bool DebuggerRequest::ParamBool(const char *name, bool *out, DebuggerParamType type) {
177
bool allowLoose = type == DebuggerParamType::REQUIRED_LOOSE || type == DebuggerParamType::OPTIONAL_LOOSE;
178
bool required = type == DebuggerParamType::REQUIRED || type == DebuggerParamType::REQUIRED_LOOSE;
179
180
const JsonNode *node = data.get(name);
181
if (!node) {
182
if (required)
183
Fail(StringFromFormat("Missing '%s' parameter", name));
184
return !required;
185
}
186
187
auto tag = node->value.getTag();
188
if (tag == JSON_NUMBER) {
189
double val = node->value.toNumber();
190
if (val == 1.0 || val == 0.0 || allowLoose) {
191
*out = val != 0.0;
192
return true;
193
}
194
195
Fail(StringFromFormat("Could not parse '%s' parameter: should be true/1 or false/0", name));
196
return false;
197
}
198
if (tag == JSON_TRUE) {
199
*out = true;
200
return true;
201
}
202
if (tag == JSON_FALSE) {
203
*out = false;
204
return true;
205
}
206
if (tag != JSON_STRING) {
207
if (type == DebuggerParamType::REQUIRED || tag != JSON_NULL) {
208
Fail(StringFromFormat("Invalid '%s' parameter type", name));
209
return false;
210
}
211
return true;
212
}
213
214
const std::string s = node->value.toString();
215
if (s == "1" || s == "true") {
216
*out = true;
217
return true;
218
}
219
if (s == "0" || s == "false" || (s.empty() && allowLoose)) {
220
*out = false;
221
return true;
222
}
223
224
if (allowLoose) {
225
*out = true;
226
return true;
227
}
228
229
Fail(StringFromFormat("Could not parse '%s' parameter: boolean required", name));
230
return false;
231
}
232
233
bool DebuggerRequest::ParamString(const char *name, std::string *out, DebuggerParamType type) {
234
bool allowLoose = type == DebuggerParamType::REQUIRED_LOOSE || type == DebuggerParamType::OPTIONAL_LOOSE;
235
bool required = type == DebuggerParamType::REQUIRED || type == DebuggerParamType::REQUIRED_LOOSE;
236
237
const JsonNode *node = data.get(name);
238
if (!node) {
239
if (required)
240
Fail(StringFromFormat("Missing '%s' parameter", name));
241
return !required;
242
}
243
244
auto tag = node->value.getTag();
245
if (tag == JSON_STRING) {
246
*out = node->value.toString();
247
return true;
248
} else if (!allowLoose) {
249
if (required || tag != JSON_NULL) {
250
Fail(StringFromFormat("Invalid '%s' parameter type", name));
251
return false;
252
}
253
return true;
254
}
255
256
// For loose, let's allow a few things.
257
if (tag == JSON_TRUE) {
258
*out = "true";
259
return true;
260
} else if (tag == JSON_FALSE) {
261
*out = "false";
262
return true;
263
} else if (tag == JSON_NULL) {
264
if (required) {
265
out->clear();
266
}
267
return true;
268
} else if (tag == JSON_NUMBER) {
269
// Will have a decimal place, though.
270
*out = StringFromFormat("%f", node->value.toNumber());
271
return true;
272
}
273
274
Fail(StringFromFormat("Invalid '%s' parameter type", name));
275
return false;
276
}
277
278
uint32_t RoundMemAddressUp(uint32_t addr) {
279
if (addr < PSP_GetScratchpadMemoryBase())
280
return PSP_GetScratchpadMemoryBase();
281
else if (addr >= PSP_GetScratchpadMemoryEnd() && addr < PSP_GetVidMemBase())
282
return PSP_GetVidMemBase();
283
else if (addr >= PSP_GetVidMemEnd() && addr < PSP_GetKernelMemoryBase())
284
return PSP_GetKernelMemoryBase();
285
else if (addr >= PSP_GetUserMemoryEnd())
286
return PSP_GetScratchpadMemoryBase();
287
return addr;
288
}
289
290