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