liblcf
Loading...
Searching...
No Matches
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
36namespace lcf {
37
38static 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
52Encoder::Encoder(std::string encoding)
53 : _encoding(filterUtf8Compatible(std::move(encoding)))
54{
55 Init();
56}
57
58Encoder::~Encoder() {
59 Reset();
60}
61
62bool Encoder::IsOk() const {
63 return _encoding.empty() || (_conv_storage && _conv_runtime);
64}
65
66void Encoder::Encode(std::string& str) {
67 if (_encoding.empty() || str.empty()) {
68 return;
69 }
70 Convert(str, _conv_runtime, _conv_storage);
71}
72
73void Encoder::Decode(std::string& str) {
74 if (_encoding.empty() || str.empty()) {
75 return;
76 }
77 Convert(str, _conv_storage, _conv_runtime);
78}
79
80void 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
119void 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
129void 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