liblcf
encoder.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of liblcf. Copyright (c) liblcf authors.
3  * https://github.com/EasyRPG/liblcf - https://easyrpg.org
4  *
5  * liblcf is Free/Libre Open Source Software, released under the MIT License.
6  * For the full copyright and license information, please view the COPYING
7  * file that was distributed with this source code.
8  */
9 
10 #include "lcf/config.h"
11 #include "lcf/encoder.h"
12 #include "lcf/reader_util.h"
13 #include "lcf/scope_guard.h"
14 #include <cstdio>
15 #include <cstdlib>
16 #include <exception>
17 
18 #if LCF_SUPPORT_ICU
19 # include <unicode/ucsdet.h>
20 # include <unicode/ucnv.h>
21 #else
22 # ifdef _MSC_VER
23 # error MSVC builds require ICU
24 # endif
25 #endif
26 
27 #ifdef _WIN32
28 # include <windows.h>
29 #else
30 # if !LCF_SUPPORT_ICU
31 # include <iconv.h>
32 # endif
33 # include <locale>
34 #endif
35 
36 namespace lcf {
37 
38 static std::string filterUtf8Compatible(std::string enc) {
39 #if LCF_SUPPORT_ICU
40  if (ucnv_compareNames(enc.c_str(), "UTF-8") == 0) {
41  return "";
42  }
43 #endif
44 
45  if (enc == "utf-8" || enc == "UTF-8" || enc == "65001") {
46  return "";
47  }
48 
49  return enc;
50 }
51 
52 Encoder::Encoder(std::string encoding)
53  : _encoding(filterUtf8Compatible(std::move(encoding)))
54 {
55  Init();
56 }
57 
58 Encoder::~Encoder() {
59  Reset();
60 }
61 
62 bool Encoder::IsOk() const {
63  return _encoding.empty() || (_conv_storage && _conv_runtime);
64 }
65 
66 void Encoder::Encode(std::string& str) {
67  if (_encoding.empty() || str.empty()) {
68  return;
69  }
70  Convert(str, _conv_runtime, _conv_storage);
71 }
72 
73 void Encoder::Decode(std::string& str) {
74  if (_encoding.empty() || str.empty()) {
75  return;
76  }
77  Convert(str, _conv_storage, _conv_runtime);
78 }
79 
80 void Encoder::Init() {
81  if (_encoding.empty()) {
82  return;
83  }
84 
85 #if LCF_SUPPORT_ICU
86  auto code_page = atoi(_encoding.c_str());
87  const auto& storage_encoding = code_page > 0
88  ? ReaderUtil::CodepageToEncoding(code_page)
89  : _encoding;
90 
91  auto status = U_ZERO_ERROR;
92  constexpr auto runtime_encoding = "UTF-8";
93  auto conv_runtime = ucnv_open(runtime_encoding, &status);
94 
95  if (conv_runtime == nullptr) {
96  fprintf(stderr, "liblcf: ucnv_open() error for encoding \"%s\": %s\n", runtime_encoding, u_errorName(status));
97  return;
98  }
99  status = U_ZERO_ERROR;
100  auto sg = makeScopeGuard([&]() { ucnv_close(conv_runtime); });
101 
102  auto conv_storage = ucnv_open(storage_encoding.c_str(), &status);
103 
104  if (conv_storage == nullptr) {
105  fprintf(stderr, "liblcf: ucnv_open() error for dest encoding \"%s\": %s\n", storage_encoding.c_str(), u_errorName(status));
106  return;
107  }
108 
109  sg.Dismiss();
110 
111  _conv_runtime = conv_runtime;
112  _conv_storage = conv_storage;
113 #else
114  _conv_runtime = const_cast<char*>("UTF-8");
115  _conv_storage = const_cast<char*>(_encoding.c_str());
116 #endif
117 }
118 
119 void Encoder::Reset() {
120 #if LCF_SUPPORT_ICU
121  auto* conv = reinterpret_cast<UConverter*>(_conv_runtime);
122  if (conv) ucnv_close(conv);
123  conv = reinterpret_cast<UConverter*>(_conv_storage);
124  if (conv) ucnv_close(conv);
125 #endif
126 }
127 
128 
129 void Encoder::Convert(std::string& str, void* conv_dst_void, void* conv_src_void) {
130 #if LCF_SUPPORT_ICU
131  const auto& src = str;
132  auto* conv_dst = reinterpret_cast<UConverter*>(conv_dst_void);
133  auto* conv_src = reinterpret_cast<UConverter*>(conv_src_void);
134 
135  auto status = U_ZERO_ERROR;
136  _buffer.resize(src.size() * 4);
137 
138  const auto* src_p = src.c_str();
139  auto* dst_p = _buffer.data();
140 
141  ucnv_convertEx(conv_dst, conv_src,
142  &dst_p, dst_p + _buffer.size(),
143  &src_p, src_p + src.size(),
144  nullptr, nullptr, nullptr, nullptr,
145  true, true,
146  &status);
147 
148  if (U_FAILURE(status)) {
149  fprintf(stderr, "liblcf: ucnv_convertEx() error when encoding \"%s\": %s\n", src.c_str(), u_errorName(status));
150  _buffer.clear();
151  }
152 
153  str.assign(_buffer.data(), dst_p);
154  return;
155 #else
156  auto* conv_dst = reinterpret_cast<const char*>(conv_dst_void);
157  auto* conv_src = reinterpret_cast<const char*>(conv_src_void);
158  iconv_t cd = iconv_open(conv_dst, conv_src);
159  if (cd == (iconv_t)-1)
160  return;
161  char *src = &str.front();
162  size_t src_left = str.size();
163  size_t dst_size = str.size() * 5 + 10;
164  _buffer.resize(dst_size);
165  char *dst = _buffer.data();
166  size_t dst_left = dst_size;
167 # ifdef ICONV_CONST
168  char ICONV_CONST *p = src;
169 # else
170  char *p = src;
171 # endif
172  char *q = dst;
173  size_t status = iconv(cd, &p, &src_left, &q, &dst_left);
174  iconv_close(cd);
175  if (status == (size_t) -1 || src_left > 0) {
176  str.clear();
177  return;
178  }
179  *q++ = '\0';
180  str.assign(dst, dst_size - dst_left);
181  return;
182 #endif
183 }
184 
185 } //namespace lcf
186 
Definition: dbarray.cpp:13
static std::string filterUtf8Compatible(std::string enc)
Definition: encoder.cpp:38