Skip to content

Commit 89b655c

Browse files
committed
feat: support NetMessages
Fixes #8
1 parent ce17a98 commit 89b655c

File tree

2 files changed

+155
-1
lines changed

2 files changed

+155
-1
lines changed
Binary file not shown.

src/main.c

+155-1
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,165 @@ static void _output_sar_data(uint32_t tick, struct sar_data data) {
170170
}
171171
}
172172

173+
#define SAR_MSG_INIT_B "&^!$"
174+
#define SAR_MSG_INIT_O "&^!%"
175+
#define SAR_MSG_CONT_B "&^?$"
176+
#define SAR_MSG_CONT_O "&^?%"
177+
178+
static char g_partial[8192];
179+
static int g_expected_len = 0;
180+
181+
///// START BASE92 /////
182+
183+
// This isn't really base92. Instead, we encode 4-byte input chunks into 5 base92 characters. If the
184+
// final chunk is not 4 bytes, each byte of it is sent as 2 base92 characters; the receiver infers
185+
// this from the buffer length and decodes accordingly. This system is almost as space-efficient as
186+
// is possible.
187+
188+
static char base92_chars[93] = // 93 because null terminator
189+
"abcdefghijklmnopqrstuvwxyz"
190+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
191+
"0123456789"
192+
"!$%^&*-_=+()[]{}<>'@#~;:/?,.|\\";
193+
194+
// doing this at runtime is a little silly but shh
195+
static const char *base92_reverse() {
196+
static char map[256];
197+
static int initd = 0;
198+
if (initd == 0) {
199+
initd = 1;
200+
for (int i = 0; i < 92; ++i) {
201+
char c = base92_chars[i];
202+
map[(int)c] = i;
203+
}
204+
}
205+
return map;
206+
}
207+
208+
static char *base92_decode(const char *encoded, int len) {
209+
const char *base92_rev = base92_reverse();
210+
211+
static char out[512]; // leave some overhead lol
212+
static int outLen;
213+
memset(out, 0, sizeof(out));
214+
outLen = 0;
215+
#define push(val) \
216+
out[outLen++] = val;
217+
while (len > 6 || len == 5) {
218+
unsigned val = base92_rev[(unsigned)encoded[4]];
219+
val = (val * 92) + base92_rev[(unsigned)encoded[3]];
220+
val = (val * 92) + base92_rev[(unsigned)encoded[2]];
221+
val = (val * 92) + base92_rev[(unsigned)encoded[1]];
222+
val = (val * 92) + base92_rev[(unsigned)encoded[0]];
223+
224+
char *raw = (char *)&val;
225+
push(raw[0]);
226+
push(raw[1]);
227+
push(raw[2]);
228+
push(raw[3]);
229+
230+
encoded += 5;
231+
len -= 5;
232+
}
233+
while (len > 0) {
234+
unsigned val = base92_rev[(unsigned)encoded[1]];
235+
val = (val * 92) + base92_rev[(unsigned)encoded[0]];
236+
237+
char *raw = (char *)&val;
238+
push(raw[0]);
239+
encoded += 2;
240+
len -= 2;
241+
}
242+
return out;
243+
}
244+
245+
///// END BASE92 /////
246+
247+
static bool handleMessage(struct demo_msg *msg) {
248+
if (strncmp(msg->con_cmd, "say \"", 5)) return false;
249+
if (strlen(msg->con_cmd) < 10) return false;
250+
bool has_prefix = true;
251+
char *prefix = msg->con_cmd + 5;
252+
bool cont, orange;
253+
if (!strncmp(prefix, SAR_MSG_INIT_B, 4)) {
254+
cont = false;
255+
orange = false;
256+
} else if (!strncmp(prefix, SAR_MSG_INIT_O, 4)) {
257+
cont = false;
258+
orange = true;
259+
} else if (!strncmp(prefix, SAR_MSG_CONT_B, 4)) {
260+
cont = true;
261+
orange = false;
262+
} else if (!strncmp(prefix, SAR_MSG_CONT_O, 4)) {
263+
cont = true;
264+
orange = true;
265+
} else {
266+
has_prefix = false;
267+
}
268+
269+
if (!has_prefix) return false;
270+
271+
if (cont) {
272+
if (!g_expected_len) {
273+
fprintf(g_errfile, "\t\t[%5u] Unmatched NetMessage continuation %s\n", msg->tick, msg->con_cmd);
274+
return false;
275+
}
276+
strcat(g_partial, msg->con_cmd + 9);
277+
} else {
278+
char *raw = base92_decode(msg->con_cmd + 9, 5);
279+
g_expected_len = (int)*raw;
280+
strcat(g_partial, msg->con_cmd + 14);
281+
}
282+
283+
if (strlen(g_partial) < g_expected_len) {
284+
fprintf(g_outfile, "\t\t[%5u] NetMessage continuation %d != %d\n", msg->tick, g_expected_len, (int)strlen(g_partial));
285+
return true;
286+
} else if (strlen(g_partial) > g_expected_len) {
287+
fprintf(g_errfile, "\t\t[%5u] NetMessage length mismatch %d != %d\n", msg->tick, g_expected_len, (int)strlen(g_partial));
288+
return false;
289+
} else {
290+
char *decoded = base92_decode(g_partial, g_expected_len);
291+
char *type = decoded;
292+
char *data = decoded + strlen(type) + 1;
293+
294+
fprintf(g_outfile, "\t\t[%5u] NetMessage (%s): %s", msg->tick, orange ? "o" : "b", type);
295+
296+
// print data
297+
int datalen = strlen(data);
298+
bool printdata = true;
299+
if (!strcmp(type, "srtimer")) {
300+
datalen = 4;
301+
printdata = false;
302+
}
303+
if (!strcmp(type, "cmboard")) {
304+
datalen = 0;
305+
}
306+
307+
if (datalen > 0) {
308+
if (printdata) fprintf(g_outfile, " = %s (", data);
309+
else fprintf(g_outfile, " (");
310+
311+
for (int i = 0; i < datalen; ++i) {
312+
fprintf(g_outfile, "%02X", (unsigned char)data[i]);
313+
if (i < datalen - 1) fprintf(g_outfile, " ");
314+
}
315+
if (datalen > 0) fprintf(g_outfile, ")");
316+
}
317+
318+
fprintf(g_outfile, "\n");
319+
}
320+
g_expected_len = 0;
321+
g_partial[0] = 0;
322+
return true;
323+
}
324+
173325
static void _output_msg(struct demo_msg *msg) {
174326
switch (msg->type) {
175327
case DEMO_MSG_CONSOLE_CMD:
176328
if (!config_check_cmd_whitelist(g_cmd_whitelist, msg->con_cmd)) {
177-
fprintf(g_outfile, "\t\t[%5u] %s\n", msg->tick, msg->con_cmd);
329+
if (!handleMessage(msg)) {
330+
fprintf(g_outfile, "\t\t[%5u] %s\n", msg->tick, msg->con_cmd);
331+
}
178332
}
179333
break;
180334
case DEMO_MSG_SAR_DATA:

0 commit comments

Comments
 (0)