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}