summaryrefslogtreecommitdiff
path: root/protocols/Steam/src/protobuf-c/protobuf-c-text.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Steam/src/protobuf-c/protobuf-c-text.cpp')
-rw-r--r--protocols/Steam/src/protobuf-c/protobuf-c-text.cpp387
1 files changed, 387 insertions, 0 deletions
diff --git a/protocols/Steam/src/protobuf-c/protobuf-c-text.cpp b/protocols/Steam/src/protobuf-c/protobuf-c-text.cpp
new file mode 100644
index 0000000000..1a4a858218
--- /dev/null
+++ b/protocols/Steam/src/protobuf-c/protobuf-c-text.cpp
@@ -0,0 +1,387 @@
+/** \file
+ * Routines to generate text format protobufs.
+ *
+ * This file contains the internal support functions as well as the
+ * exported functions which are used to generate text format protobufs
+ * from C protobuf data types.
+ *
+ * \author Kevin Lyda <kevin@ie.suberic.net>
+ * \date March 2014
+ */
+
+#include "../stdafx.h"
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "protobuf-c.h"
+#include "protobuf-c-util.h"
+
+ /** Append a string to the ReturnString.
+ *
+ * Append the string built from \c format and its args to the \c rs
+ * string. Note that \c malloc_err is checked and if it's true,
+ * this function won't do anything.
+ *
+ * \param[in,out] rs The string to append to.
+ * \param[in] guess A guess at the number of chars being added.
+ * \param[in] allocator allocator functions.
+ * \param[in] format Printf-style format string.
+ * \param[in] ... Variable number of args for \c format.
+ */
+
+ /** \defgroup generate Functions to generate text format proto bufs
+ * \ingroup internal
+ * @{
+ */
+
+ /** Escape string.
+ *
+ * Add escape characters to strings for problematic characters.
+ *
+ * \param[in] src The unescaped string to process.
+ * \param[in] len Length of \c src. Note that \c src might have ASCII
+ * \c NULs so strlen() isn't good enough here.
+ * \param[in] allocator allocator functions.
+ * \return The fully escaped string, or \c NULL if there has been an
+ * allocation error.
+ */
+static char *
+esc_str(const char *src, size_t len)
+{
+ size_t escapes = 0, dst_len = 0;
+
+ for (size_t i = 0; i < len; i++) {
+ if (!isprint(src[i])) {
+ escapes++;
+ }
+ }
+
+ char *dst = (char *)malloc((escapes * 2) + ((len - escapes) * 2) + 1);
+ if (!dst) {
+ return NULL;
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ switch (src[i]) {
+ /* Special cases. */
+ case '\'':
+ dst[dst_len++] = '\\';
+ dst[dst_len++] = '\'';
+ break;
+ case '\"':
+ dst[dst_len++] = '\\';
+ dst[dst_len++] = '\"';
+ break;
+ case '\\':
+ dst[dst_len++] = '\\';
+ dst[dst_len++] = '\\';
+ break;
+ case '\n':
+ dst[dst_len++] = '\\';
+ dst[dst_len++] = 'n';
+ break;
+ case '\r':
+ dst[dst_len++] = '\\';
+ dst[dst_len++] = 'r';
+ break;
+ case '\t':
+ dst[dst_len++] = '\\';
+ dst[dst_len++] = 't';
+ break;
+
+ /* Escape with octal if !isprint. */
+ default:
+ dst[dst_len++] = src[i];
+ break;
+ }
+ }
+ dst[dst_len] = '\0';
+
+ return dst;
+}
+
+/** Internal function to back API function.
+ *
+ * Has a few extra params to better enable recursion. This function gets
+ * called for each nested message as the \c ProtobufCMessage struct is
+ * traversed.
+ *
+ * \param[in,out] rs The string being built up for the text format protobuf.
+ * \param[in] level Indent level - increments in 2's.
+ * \param[in] m The \c ProtobufCMessage being serialised.
+ * \param[in] d The descriptor for the \c ProtobufCMessage.
+ * \param[in] allocator allocator functions.
+ */
+static void protobuf_c_text_to_string_internal(
+ CMStringA &str,
+ int level,
+ const ProtobufCMessage *m,
+ const ProtobufCMessageDescriptor *d)
+{
+ size_t j, quantifier_offset;
+ double float_var;
+ const ProtobufCFieldDescriptor *f;
+ ProtobufCEnumDescriptor *enumd;
+ const ProtobufCEnumValue *enumv;
+
+ f = d->fields;
+ for (unsigned i = 0; i < d->n_fields; i++) {
+ /* Decide if something needs to be done for this field. */
+ switch (f[i].label) {
+ case PROTOBUF_C_LABEL_OPTIONAL:
+ if (f[i].type == PROTOBUF_C_TYPE_STRING) {
+ if (!STRUCT_MEMBER(char *, m, f[i].offset)
+ || (STRUCT_MEMBER(char *, m, f[i].offset)
+ == (char *)f[i].default_value)) {
+ continue;
+ }
+ }
+ else if (f[i].type == PROTOBUF_C_TYPE_MESSAGE) {
+ if (!STRUCT_MEMBER(char *, m, f[i].offset)) {
+ continue;
+ }
+ }
+ else {
+ if (!STRUCT_MEMBER(protobuf_c_boolean, m, f[i].quantifier_offset)) {
+ continue;
+ }
+ }
+ break;
+ case PROTOBUF_C_LABEL_REPEATED:
+ if (!STRUCT_MEMBER(size_t, m, f[i].quantifier_offset)) {
+ continue;
+ }
+ break;
+ }
+
+ quantifier_offset = STRUCT_MEMBER(size_t, m, f[i].quantifier_offset);
+ /* Field exists and has data, dump it. */
+ switch (f[i].type) {
+ case PROTOBUF_C_TYPE_INT32:
+ case PROTOBUF_C_TYPE_UINT32:
+ case PROTOBUF_C_TYPE_FIXED32:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ str.AppendFormat(
+ "%*s%s: %u\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(uint32_t *, m, f[i].offset)[j]);
+ }
+ }
+ else {
+ str.AppendFormat(
+ "%*s%s: %u\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(uint32_t, m, f[i].offset));
+ }
+ break;
+ case PROTOBUF_C_TYPE_SINT32:
+ case PROTOBUF_C_TYPE_SFIXED32:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ str.AppendFormat(
+ "%*s%s: %d\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(int32_t *, m, f[i].offset)[j]);
+ }
+ }
+ else {
+ str.AppendFormat(
+ "%*s%s: %d\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(int32_t, m, f[i].offset));
+ }
+ break;
+ case PROTOBUF_C_TYPE_INT64:
+ case PROTOBUF_C_TYPE_UINT64:
+ case PROTOBUF_C_TYPE_FIXED64:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ str.AppendFormat(
+ "%*s%s: %lld\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(uint64_t *, m, f[i].offset)[j]);
+ }
+ }
+ else {
+ str.AppendFormat(
+ "%*s%s: %lld\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(uint64_t, m, f[i].offset));
+ }
+ break;
+ case PROTOBUF_C_TYPE_SINT64:
+ case PROTOBUF_C_TYPE_SFIXED64:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ str.AppendFormat(
+ "%*s%s: %lld\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(int64_t *, m, f[i].offset)[j]);
+ }
+ }
+ else {
+ str.AppendFormat(
+ "%*s%s: %lld\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(int64_t, m, f[i].offset));
+ }
+ break;
+ case PROTOBUF_C_TYPE_FLOAT:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ float_var = STRUCT_MEMBER(float *, m, f[i].offset)[j];
+ str.AppendFormat(
+ "%*s%s: %g\n",
+ level, "", f[i].name,
+ float_var);
+ }
+ }
+ else {
+ float_var = STRUCT_MEMBER(float, m, f[i].offset);
+ str.AppendFormat(
+ "%*s%s: %g\n",
+ level, "", f[i].name,
+ float_var);
+ }
+ break;
+ case PROTOBUF_C_TYPE_DOUBLE:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ str.AppendFormat(
+ "%*s%s: %g\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(double *, m, f[i].offset)[j]);
+ }
+ }
+ else {
+ str.AppendFormat(
+ "%*s%s: %g\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(double, m, f[i].offset));
+ }
+ break;
+ case PROTOBUF_C_TYPE_BOOL:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ str.AppendFormat(
+ "%*s%s: %s\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(protobuf_c_boolean *, m, f[i].offset)[j] ?
+ "true" : "false");
+ }
+ }
+ else {
+ str.AppendFormat(
+ "%*s%s: %s\n",
+ level, "", f[i].name,
+ STRUCT_MEMBER(protobuf_c_boolean, m, f[i].offset) ?
+ "true" : "false");
+ }
+ break;
+ case PROTOBUF_C_TYPE_ENUM:
+ enumd = (ProtobufCEnumDescriptor *)f[i].descriptor;
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ enumv = protobuf_c_enum_descriptor_get_value(
+ enumd, STRUCT_MEMBER(int *, m, f[i].offset)[j]);
+ str.AppendFormat(
+ "%*s%s: %s\n",
+ level, "", f[i].name,
+ enumv ? enumv->name : "unknown");
+ }
+ }
+ else {
+ enumv = protobuf_c_enum_descriptor_get_value(
+ enumd, STRUCT_MEMBER(int, m, f[i].offset));
+ str.AppendFormat(
+ "%*s%s: %s\n",
+ level, "", f[i].name,
+ enumv ? enumv->name : "unknown");
+ }
+ break;
+ case PROTOBUF_C_TYPE_STRING:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ char *escaped = esc_str(
+ STRUCT_MEMBER(char **, m, f[i].offset)[j],
+ strlen(STRUCT_MEMBER(char **, m, f[i].offset)[j]));
+ str.AppendFormat("%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
+ free(escaped);
+ }
+ }
+ else {
+ char *escaped = esc_str(STRUCT_MEMBER(char *, m, f[i].offset),
+ strlen(STRUCT_MEMBER(char *, m, f[i].offset)));
+ str.AppendFormat("%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
+ free(escaped);
+ }
+ break;
+ case PROTOBUF_C_TYPE_BYTES:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0; j < quantifier_offset; j++) {
+ auto *member = STRUCT_MEMBER(ProtobufCBinaryData *, m, f[i].offset);
+ if (member->len && member->data) {
+ char *p = (char *)malloc(member->len * 2 + 1);
+ bin2hex(member->data, member->len, p);
+ str.AppendFormat("%*s%s: \"%s\"\n", level, "", f[i].name, p);
+ free(p);
+ }
+ }
+ }
+ else {
+ auto member = STRUCT_MEMBER(ProtobufCBinaryData, m, f[i].offset);
+ if (member.len && member.data) {
+ char *p = (char *)malloc(member.len * 2 + 1);
+ bin2hex(member.data, member.len, p);
+ str.AppendFormat("%*s%s: \"%s\"\n", level, "", f[i].name, p);
+ free(p);
+ }
+ }
+ break;
+
+ case PROTOBUF_C_TYPE_MESSAGE:
+ if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ for (j = 0;
+ j < STRUCT_MEMBER(size_t, m, f[i].quantifier_offset);
+ j++) {
+ str.AppendFormat("%*s%s {\n", level, "", f[i].name);
+ protobuf_c_text_to_string_internal(str, level + 2,
+ STRUCT_MEMBER(ProtobufCMessage **, m, f[i].offset)[j],
+ (ProtobufCMessageDescriptor *)f[i].descriptor);
+ str.AppendFormat("%*s}\n", level, "");
+ }
+ }
+ else {
+ str.AppendFormat("%*s%s {\n", level, "", f[i].name);
+ protobuf_c_text_to_string_internal(str, level + 2,
+ STRUCT_MEMBER(ProtobufCMessage *, m, f[i].offset),
+ (ProtobufCMessageDescriptor *)f[i].descriptor);
+ str.AppendFormat("%*s}\n", level, "");
+ }
+ break;
+
+ default:
+ return;
+ }
+ }
+}
+
+/** @} */ /* End of generate group. */
+
+/* See .h file for API docs. */
+
+CMStringA protobuf_c_text_to_string(const ProtobufCMessage &msg)
+{
+ CMStringA ret;
+
+ ret.AppendFormat("%s {\n", msg.descriptor->c_name);
+ protobuf_c_text_to_string_internal(ret, 1, &msg, msg.descriptor);
+ ret.Append("}\n");
+
+ return ret;
+}