master uint/magnolia / source / mcproto / buffer.c
  1#include <stdarg.h>
  2#include <stdio.h>
  3#include <stdlib.h>
  4#include <string.h>
  5
  6#include "internal.h"
  7
  8void mc_set_error(char* error, size_t error_size, const char* fmt, ...)
  9{
 10	va_list args;
 11
 12	if (!error || error_size == 0)
 13		return;
 14	va_start(args, fmt);
 15	vsnprintf(error, error_size, fmt, args);
 16	va_end(args);
 17}
 18
 19void bufinit(MCBuffer* buffer) { memset(buffer, 0, sizeof(*buffer)); }
 20
 21void buffree(MCBuffer* buffer)
 22{
 23	free(buffer->data);
 24	memset(buffer, 0, sizeof(*buffer));
 25}
 26
 27/* make sure an MCBuffer has can fit its current size + extra bytes */
 28int bufreserve(MCBuffer* buffer, size_t extra)
 29{
 30	size_t needed;
 31	size_t capacity;
 32	u8*    data;
 33
 34	if (extra > MC_MAX_PACKET_SIZE || buffer->size > MC_MAX_PACKET_SIZE - extra)
 35		return 0;
 36	needed = buffer->size + extra;
 37	if (needed <= buffer->capacity)
 38		return 1;
 39	capacity = buffer->capacity ? buffer->capacity : 128;
 40	while (capacity < needed) {
 41		if (capacity > MC_MAX_PACKET_SIZE / 2) {
 42			capacity = MC_MAX_PACKET_SIZE;
 43			break;
 44		}
 45		capacity *= 2;
 46	}
 47	data = (u8*)realloc(buffer->data, capacity);
 48	if (!data)
 49		return 0;
 50	buffer->data = data;
 51	buffer->capacity = capacity;
 52	return 1;
 53}
 54
 55int bufwrite(MCBuffer* buffer, const void* data, size_t size)
 56{
 57	if (!bufreserve(buffer, size))
 58		return 0;
 59	if (size)
 60		memcpy(buffer->data + buffer->size, data, size);
 61	buffer->size += size;
 62	return 1;
 63}
 64
 65int bufwrite_u8(MCBuffer* buffer, u8 value) { return bufwrite(buffer, &value, 1); }
 66
 67int bufwrite_u16(MCBuffer* buffer, u16 value)
 68{
 69	u8 data[2];
 70	/* minecraft protocol integers are big-endian except VarInt
 71	   https://wikivg.booky.dev/Protocol#Data_types */
 72	data[0] = (u8)(value >> 8);
 73	data[1] = (u8)value;
 74	return bufwrite(buffer, data, sizeof(data));
 75}
 76
 77int bufwrite_i32(MCBuffer* buffer, i32 value)
 78{
 79	u32 v = (u32)value;
 80	u8  data[4];
 81	data[0] = (u8)(v >> 24);
 82	data[1] = (u8)(v >> 16);
 83	data[2] = (u8)(v >> 8);
 84	data[3] = (u8)v;
 85	return bufwrite(buffer, data, sizeof(data));
 86}
 87
 88int bufwrite_i64(MCBuffer* buffer, i64 value)
 89{
 90	u64 v = (u64)value;
 91	u8  data[8];
 92	int i;
 93	for (i = 0; i < 8; ++i)
 94		data[i] = (u8)(v >> (56 - i * 8));
 95	return bufwrite(buffer, data, sizeof(data));
 96}
 97
 98int bufwrite_f32(MCBuffer* buffer, float value)
 99{
100	u32 bits;
101	memcpy(&bits, &value, sizeof(bits));
102	return bufwrite_i32(buffer, (i32)bits);
103}
104
105int bufwrite_f64(MCBuffer* buffer, double value)
106{
107	u64 bits;
108	memcpy(&bits, &value, sizeof(bits));
109	return bufwrite_i64(buffer, (i64)bits);
110}
111
112int bufwrite_varint(MCBuffer* buffer, i32 value)
113{
114	u32 v = (u32)value;
115	/* minecraft VarInt is 7 data bits per byte (5 bytes max on i32) and
116	   high bit means more bytes follow
117	   https://wikivg.booky.dev/Protocol#VarInt_and_VarLong */
118	do {
119		u8 byte = (u8)(v & 0x7f);
120		v >>= 7;
121		if (v)
122			byte |= 0x80;
123		if (!bufwrite_u8(buffer, byte))
124			return 0;
125	} while (v);
126	return 1;
127}
128
129int bufwrite_string(MCBuffer* buffer, const char* value)
130{
131	size_t size = strlen(value);
132	if (size > MC_MAX_STRING_SIZE)
133		return 0;
134	return bufwrite_varint(buffer, (i32)size) && bufwrite(buffer, value, size);
135}
136
137int bufread(MCBuffer* buffer, void* out, size_t size)
138{
139	if (size > buffer->size - buffer->cursor)
140		return 0;
141	if (size)
142		memcpy(out, buffer->data + buffer->cursor, size);
143	buffer->cursor += size;
144	return 1;
145}
146
147int bufread_u8(MCBuffer* buffer, u8* value) { return bufread(buffer, value, 1); }
148
149int bufread_u16(MCBuffer* buffer, u16* value)
150{
151	u8 data[2];
152	if (!bufread(buffer, data, sizeof(data)))
153		return 0;
154	*value = (u16)(((u16)data[0] << 8) | data[1]);
155	return 1;
156}
157
158int bufread_i32(MCBuffer* buffer, i32* value)
159{
160	u8  data[4];
161	u32 result;
162	if (!bufread(buffer, data, sizeof(data)))
163		return 0;
164	result = ((u32)data[0] << 24) | ((u32)data[1] << 16) | ((u32)data[2] << 8) | data[3];
165	*value = (i32)result;
166	return 1;
167}
168
169int bufread_i64(MCBuffer* buffer, i64* value)
170{
171	u8  data[8];
172	u64 result = 0;
173	int i;
174	if (!bufread(buffer, data, sizeof(data)))
175		return 0;
176	for (i = 0; i < 8; ++i)
177		result = (result << 8) | data[i];
178	*value = (i64)result;
179	return 1;
180}
181
182int bufread_f32(MCBuffer* buffer, float* value)
183{
184	i32 bits;
185	if (!bufread_i32(buffer, &bits))
186		return 0;
187	memcpy(value, &bits, sizeof(*value));
188	return 1;
189}
190
191int bufread_f64(MCBuffer* buffer, double* value)
192{
193	i64 bits;
194	if (!bufread_i64(buffer, &bits))
195		return 0;
196	memcpy(value, &bits, sizeof(*value));
197	return 1;
198}
199
200int bufread_varint(MCBuffer* buffer, i32* value)
201{
202	u32 result = 0;
203	int shift;
204
205	/* VarInt is at best 5 bytes for a 32-bit value
206	   https://wikivg.booky.dev/Protocol#VarInt_and_VarLong
207
208	   35 = 7*5 */
209	for (shift = 0; shift < 35; shift += 7) {
210		u8 byte;
211		if (!bufread_u8(buffer, &byte))
212			return 0;
213		result |= (u32)(byte & 0x7f) << shift;
214		if (!(byte & 0x80)) {
215			*value = (i32)result;
216			return 1;
217		}
218	}
219	return 0;
220}
221
222int bufread_string(MCBuffer* buffer, char* value, size_t value_size)
223{
224	i32 length;
225	if (!bufread_varint(buffer, &length) || length < 0 || (size_t)length >= value_size ||
226	    (size_t)length > MC_MAX_STRING_SIZE)
227		return 0;
228	if (!bufread(buffer, value, (size_t)length))
229		return 0;
230	value[length] = '\0';
231	return 1;
232}
233
234int bufread_bytes(MCBuffer* buffer, const u8** value, size_t* size)
235{
236	i32 length;
237	if (!bufread_varint(buffer, &length) || length < 0 ||
238	    (size_t)length > buffer->size - buffer->cursor)
239		return 0;
240	*value = buffer->data + buffer->cursor;
241	*size = (size_t)length;
242	buffer->cursor += (size_t)length;
243	return 1;
244}