liblcf
encoder.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of liblcf. Copyright (c) 2021 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  return enc;
45 }
46 
47 Encoder::Encoder(std::string encoding)
48  : _encoding(filterUtf8Compatible(std::move(encoding)))
49 {
50  Init();
51 }
52 
53 Encoder::~Encoder() {
54  Reset();
55 }
56 
57 bool Encoder::IsOk() const {
58  return _encoding.empty() || (_conv_storage && _conv_runtime);
59 }
60 
61 void Encoder::Encode(std::string& str) {
62  if (_encoding.empty() || str.empty()) {
63  return;
64  }
65  Convert(str, _conv_runtime, _conv_storage);
66 }
67 
68 void Encoder::Decode(std::string& str) {
69  if (_encoding.empty() || str.empty()) {
70  return;
71  }
72  Convert(str, _conv_storage, _conv_runtime);
73 }
74 
75 void Encoder::Init() {
76  if (_encoding.empty()) {
77  return;
78  }
79 #if LCF_SUPPORT_ICU
80  auto code_page = atoi(_encoding.c_str());
81  const auto& storage_encoding = code_page > 0
82  ? ReaderUtil::CodepageToEncoding(code_page)
83  : _encoding;
84 
85  auto status = U_ZERO_ERROR;
86  constexpr auto runtime_encoding = "UTF-8";
87  auto conv_runtime = ucnv_open(runtime_encoding, &status);
88 
89  if (conv_runtime == nullptr) {
90  fprintf(stderr, "liblcf: ucnv_open() error for encoding \"%s\": %s\n", runtime_encoding, u_errorName(status));
91  return;
92  }
93  status = U_ZERO_ERROR;
94  auto sg = makeScopeGuard([&]() { ucnv_close(conv_runtime); });
95 
96  auto conv_storage = ucnv_open(storage_encoding.c_str(), &status);
97 
98  if (conv_storage == nullptr) {
99  fprintf(stderr, "liblcf: ucnv_open() error for dest encoding \"%s\": %s\n", storage_encoding.c_str(), u_errorName(status));
100  return;
101  }
102 
103  sg.Dismiss();
104 
105  _conv_runtime = conv_runtime;
106  _conv_storage = conv_storage;
107 #else
108  _conv_runtime = const_cast<char*>("UTF-8");
109  _conv_storage = const_cast<char*>(_encoding.c_str());
110 #endif
111 }
112 
113 void Encoder::Reset() {
114 #if LCF_SUPPORT_ICU
115  auto* conv = reinterpret_cast<UConverter*>(_conv_runtime);
116  if (conv) ucnv_close(conv);
117  conv = reinterpret_cast<UConverter*>(_conv_storage);
118  if (conv) ucnv_close(conv);
119 #endif
120 }
121 
122 
123 void Encoder::Convert(std::string& str, void* conv_dst_void, void* conv_src_void) {
124 #if LCF_SUPPORT_ICU
125  const auto& src = str;
126  auto* conv_dst = reinterpret_cast<UConverter*>(conv_dst_void);
127  auto* conv_src = reinterpret_cast<UConverter*>(conv_src_void);
128 
129  auto status = U_ZERO_ERROR;
130  _buffer.resize(src.size() * 4);
131 
132  const auto* src_p = src.c_str();
133  auto* dst_p = _buffer.data();
134 
135  ucnv_convertEx(conv_dst, conv_src,
136  &dst_p, dst_p + _buffer.size(),
137  &src_p, src_p + src.size(),
138  nullptr, nullptr, nullptr, nullptr,
139  true, true,
140  &status);
141 
142  if (U_FAILURE(status)) {
143  fprintf(stderr, "liblcf: ucnv_convertEx() error when encoding \"%s\": %s\n", src.c_str(), u_errorName(status));
144  _buffer.clear();
145  }
146 
147  str.assign(_buffer.data(), dst_p);
148  return;
149 #else
150  auto* conv_dst = reinterpret_cast<const char*>(conv_dst_void);
151  auto* conv_src = reinterpret_cast<const char*>(conv_src_void);
152  iconv_t cd = iconv_open(conv_dst, conv_src);
153  if (cd == (iconv_t)-1)
154  return;
155  char *src = &str.front();
156  size_t src_left = str.size();
157  size_t dst_size = str.size() * 5 + 10;
158  _buffer.resize(dst_size);
159  char *dst = _buffer.data();
160  size_t dst_left = dst_size;
161 # ifdef ICONV_CONST
162  char ICONV_CONST *p = src;
163 # else
164  char *p = src;
165 # endif
166  char *q = dst;
167  size_t status = iconv(cd, &p, &src_left, &q, &dst_left);
168  iconv_close(cd);
169  if (status == (size_t) -1 || src_left > 0) {
170  str.clear();
171  return;
172  }
173  *q++ = '\0';
174  str.assign(dst, dst_size - dst_left);
175  return;
176 #endif
177 }
178 
179 } //namespace lcf
180 
Definition: dbarray.cpp:13
static std::string filterUtf8Compatible(std::string enc)
Definition: encoder.cpp:38