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