1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "packer.h"
4
5#include "compression.h"
6
7#include <base/mem.h>
8#include <base/str.h>
9
10CAbstractPacker::CAbstractPacker(unsigned char *pBuffer, size_t Size) :
11 m_pBuffer(pBuffer),
12 m_BufferSize(Size)
13{
14}
15
16void CAbstractPacker::Reset()
17{
18 m_Error = false;
19 m_pCurrent = m_pBuffer;
20 m_pEnd = m_pCurrent + m_BufferSize;
21}
22
23void CAbstractPacker::AddInt(int i)
24{
25 if(m_Error)
26 return;
27
28 unsigned char *pNext = CVariableInt::Pack(pDst: m_pCurrent, i, DstSize: m_pEnd - m_pCurrent);
29 if(!pNext)
30 {
31 m_Error = true;
32 return;
33 }
34 m_pCurrent = pNext;
35}
36
37void CAbstractPacker::AddString(const char *pStr, int Limit, bool AllowTruncation)
38{
39 if(m_Error)
40 return;
41
42 unsigned char *const pPrevCurrent = m_pCurrent;
43 if(Limit <= 0)
44 {
45 Limit = m_BufferSize;
46 }
47 while(*pStr)
48 {
49 int Codepoint = str_utf8_decode(ptr: &pStr);
50 if(Codepoint == -1)
51 {
52 Codepoint = 0xfffd; // Unicode replacement character.
53 }
54 char aEncoded[4];
55 const int Length = str_utf8_encode(ptr: aEncoded, chr: Codepoint);
56 // Limit must ensure space for null termination if desired.
57 if(Limit < Length)
58 {
59 if(AllowTruncation)
60 {
61 break;
62 }
63 m_Error = true;
64 m_pCurrent = pPrevCurrent;
65 return;
66 }
67 // Ensure space for the null termination.
68 if(m_pCurrent + Length + 1 > m_pEnd)
69 {
70 m_Error = true;
71 m_pCurrent = pPrevCurrent;
72 return;
73 }
74 mem_copy(dest: m_pCurrent, source: aEncoded, size: Length);
75 m_pCurrent += Length;
76 Limit -= Length;
77 }
78 *m_pCurrent++ = '\0';
79}
80
81void CAbstractPacker::AddRaw(const void *pData, int Size)
82{
83 if(m_Error)
84 return;
85
86 if(m_pCurrent + Size > m_pEnd)
87 {
88 m_Error = true;
89 return;
90 }
91
92 mem_copy(dest: m_pCurrent, source: pData, size: Size);
93 m_pCurrent += Size;
94}
95
96void CUnpacker::Reset(const void *pData, int Size)
97{
98 m_Error = false;
99 m_pStart = (const unsigned char *)pData;
100 m_pEnd = m_pStart + Size;
101 m_pCurrent = m_pStart;
102}
103
104int CUnpacker::GetInt()
105{
106 if(m_Error)
107 return 0;
108
109 if(m_pCurrent >= m_pEnd)
110 {
111 m_Error = true;
112 return 0;
113 }
114
115 int i;
116 const unsigned char *pNext = CVariableInt::Unpack(pSrc: m_pCurrent, pInOut: &i, SrcSize: m_pEnd - m_pCurrent);
117 if(!pNext)
118 {
119 m_Error = true;
120 return 0;
121 }
122 m_pCurrent = pNext;
123 return i;
124}
125
126int CUnpacker::GetIntOrDefault(int Default)
127{
128 if(m_Error)
129 {
130 return 0;
131 }
132 if(m_pCurrent == m_pEnd)
133 {
134 return Default;
135 }
136 return GetInt();
137}
138
139int CUnpacker::GetUncompressedInt()
140{
141 if(m_Error)
142 return 0;
143
144 if(m_pCurrent + sizeof(int) > m_pEnd)
145 {
146 m_Error = true;
147 return 0;
148 }
149
150 int i;
151 mem_copy(dest: &i, source: m_pCurrent, size: sizeof(int));
152 m_pCurrent += sizeof(int);
153 return i;
154}
155
156int CUnpacker::GetUncompressedIntOrDefault(int Default)
157{
158 if(m_Error)
159 {
160 return 0;
161 }
162 if(m_pCurrent == m_pEnd)
163 {
164 return Default;
165 }
166 return GetUncompressedInt();
167}
168
169const char *CUnpacker::GetString(int SanitizeType)
170{
171 if(m_Error)
172 return "";
173
174 if(m_pCurrent >= m_pEnd)
175 {
176 m_Error = true;
177 return "";
178 }
179
180 char *pPtr = (char *)m_pCurrent;
181 while(*m_pCurrent) // skip the string
182 {
183 m_pCurrent++;
184 if(m_pCurrent == m_pEnd)
185 {
186 m_Error = true;
187 return "";
188 }
189 }
190 m_pCurrent++;
191
192 if(!str_utf8_check(str: pPtr))
193 {
194 m_Error = true;
195 return "";
196 }
197
198 // sanitize all strings
199 if(SanitizeType & SANITIZE)
200 str_sanitize(str: pPtr);
201 else if(SanitizeType & SANITIZE_CC)
202 str_sanitize_cc(str: pPtr);
203 return SanitizeType & SKIP_START_WHITESPACES ? str_utf8_skip_whitespaces(str: pPtr) : pPtr;
204}
205
206const unsigned char *CUnpacker::GetRaw(int Size)
207{
208 const unsigned char *pPtr = m_pCurrent;
209 if(m_Error)
210 return nullptr;
211
212 // check for nasty sizes
213 if(Size < 0 || m_pCurrent + Size > m_pEnd)
214 {
215 m_Error = true;
216 return nullptr;
217 }
218
219 // "unpack" the data
220 m_pCurrent += Size;
221 return pPtr;
222}
223