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}