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