1/*
2 * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include <framework/global.h>
24
25#include "string.h"
26#include "format.h"
27#include <boost/algorithm/string.hpp>
28#include <ctype.h>
29#include <physfs.h>
30
31#ifdef _MSC_VER
32 #pragma warning(disable:4267) // '?' : conversion from 'A' to 'B', possible loss of data
33#endif
34
35namespace stdext {
36
37std::string resolve_path(const std::string& filePath, std::string sourcePath)
38{
39 if (filePath.empty())
40 return "";
41 if(stdext::starts_with(filePath, "/"))
42 return filePath;
43 if(!stdext::ends_with(sourcePath, "/")) {
44 std::size_t slashPos = sourcePath.find_last_of("/");
45 if(slashPos == std::string::npos)
46 throw_exception(format("invalid source path '%s', for file '%s'", sourcePath, filePath));
47 sourcePath = sourcePath.substr(0, slashPos + 1);
48 }
49 return sourcePath + filePath;
50}
51
52std::string date_time_string()
53{
54 char date[32];
55 std::time_t tnow;
56 std::time(&tnow);
57 std::tm* ts = std::localtime(&tnow);
58 std::strftime(date, 32, "%b %d %Y %H:%M:%S", ts);
59 return std::string(date);
60}
61
62std::string timestamp_to_date(time_t tnow)
63{
64 char date[32];
65 std::tm* ts = std::localtime(&tnow);
66 std::strftime(date, 32, "%b %d %Y %H:%M:%S", ts);
67 return std::string(date);
68}
69
70std::string dec_to_hex(uint32_t num)
71{
72 std::string str;
73 std::ostringstream o;
74 o << std::setw(8) << std::setfill('0') << std::hex << num;
75 str = o.str();
76 return str;
77}
78
79std::string dec_to_hex(uint64_t num)
80{
81 std::string str;
82 std::ostringstream o;
83 o << std::setw(16) << std::setfill('0') << std::hex << num;
84 str = o.str();
85 return str;
86}
87
88uint64_t hex_to_dec(const std::string& str)
89{
90 uint64_t num;
91 std::istringstream i(str);
92 i >> std::hex >> num;
93 return num;
94}
95
96bool is_valid_utf8(const std::string& src)
97{
98 const unsigned char *bytes = (const unsigned char *)src.c_str();
99 while(*bytes) {
100 if( (// ASCII
101 // use bytes[0] <= 0x7F to allow ASCII control characters
102 bytes[0] == 0x09 ||
103 bytes[0] == 0x0A ||
104 bytes[0] == 0x0D ||
105 (0x20 <= bytes[0] && bytes[0] <= 0x7E)
106 )
107 ) {
108 bytes += 1;
109 continue;
110 }
111 if( (// non-overlong 2-byte
112 (0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
113 (0x80 <= bytes[1] && bytes[1] <= 0xBF)
114 )
115 ) {
116 bytes += 2;
117 continue;
118 }
119 if( (// excluding overlongs
120 bytes[0] == 0xE0 &&
121 (0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
122 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
123 ) ||
124 (// straight 3-byte
125 ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
126 bytes[0] == 0xEE ||
127 bytes[0] == 0xEF) &&
128 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
129 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
130 ) ||
131 (// excluding surrogates
132 bytes[0] == 0xED &&
133 (0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
134 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
135 )
136 ) {
137 bytes += 3;
138 continue;
139 }
140 if( (// planes 1-3
141 bytes[0] == 0xF0 &&
142 (0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
143 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
144 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
145 ) ||
146 (// planes 4-15
147 (0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
148 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
149 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
150 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
151 ) ||
152 (// plane 16
153 bytes[0] == 0xF4 &&
154 (0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
155 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
156 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
157 )
158 ) {
159 bytes += 4;
160 continue;
161 }
162 return false;
163 }
164 return true;
165}
166
167std::string utf8_to_latin1(const std::string& src)
168{
169 std::string out;
170 for(uint i=0;i<src.length();) {
171 uchar c = src[i++];
172 if((c >= 32 && c < 128) || c == 0x0d || c == 0x0a || c == 0x09)
173 out += c;
174 else if(c == 0xc2 || c == 0xc3) {
175 uchar c2 = src[i++];
176 if(c == 0xc2) {
177 if(c2 > 0xa1 && c2 < 0xbb)
178 out += c2;
179 } else if(c == 0xc3)
180 out += 64 + c2;
181 } else if(c >= 0xc4 && c <= 0xdf)
182 i += 1;
183 else if(c >= 0xe0 && c <= 0xed)
184 i += 2;
185 else if(c >= 0xf0 && c <= 0xf4)
186 i += 3;
187 }
188 return out;
189}
190
191std::string latin1_to_utf8(const std::string& src)
192{
193 std::string out;
194 for(uchar c : src) {
195 if((c >= 32 && c < 128) || c == 0x0d || c == 0x0a || c == 0x09)
196 out += c;
197 else {
198 out += 0xc2 + (c > 0xbf);
199 out += 0x80 + (c & 0x3f);
200 }
201 }
202 return out;
203}
204
205#ifdef WIN32
206#include <winsock2.h>
207#include <windows.h>
208std::wstring utf8_to_utf16(const std::string& src)
209{
210 std::wstring res;
211 wchar_t out[65536];
212 if(MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, out, 65536))
213 res = out;
214 return res;
215}
216
217std::string utf16_to_utf8(const std::wstring& src)
218{
219 std::string res;
220 char out[65536];
221 if(WideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1, out, 65536, NULL, NULL))
222 res = out;
223 return res;
224}
225
226std::wstring latin1_to_utf16(const std::string& src)
227{
228 return utf8_to_utf16(latin1_to_utf8(src));
229}
230
231std::string utf16_to_latin1(const std::wstring& src)
232{
233 return utf8_to_latin1(utf16_to_utf8(src));
234}
235#endif
236
237void tolower(std::string& str)
238{
239 std::transform(str.begin(), str.end(), str.begin(), lochar);
240}
241
242void toupper(std::string& str)
243{
244 std::transform(str.begin(), str.end(), str.begin(), upchar);
245}
246
247void trim(std::string& str)
248{
249 boost::trim(str);
250}
251
252char upchar(char c)
253{
254 if((c >= 97 && c <= 122) || (uchar)c >= 224)
255 c -= 32;
256 return c;
257}
258
259char lochar(char c)
260{
261 if((c >= 65 && c <= 90) || ((uchar)c >= 192 && (uchar)c <= 223))
262 c += 32;
263 return c;
264}
265
266void ucwords(std::string& str)
267{
268 uint32 strLen = str.length();
269 if(strLen == 0)
270 return;
271
272 str[0] = upchar(str[0]);
273 for(uint32 i = 1; i < strLen; ++i) {
274 if(str[i - 1] == ' ')
275 str[i] = upchar(str[i]);
276 }
277}
278
279bool ends_with(const std::string& str, const std::string& test)
280{
281 return boost::ends_with(str, test);
282}
283
284bool starts_with(const std::string& str, const std::string& test)
285{
286 return boost::starts_with(str, test);
287}
288
289void replace_all(std::string& str, const std::string& search, const std::string& replacement)
290{
291 return boost::replace_all(str, search, replacement);
292}
293
294std::vector<std::string> split(const std::string& str, const std::string& separators)
295{
296 std::vector<std::string> splitted;
297 boost::split(splitted, str, boost::is_any_of(std::string(separators)));
298 return splitted;
299}
300
301}
302