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
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 return enc;
45}
46
47Encoder::Encoder(std::string encoding)
48 : _encoding(filterUtf8Compatible(std::move(encoding)))
49{
50 Init();
51}
52
53Encoder::~Encoder() {
54 Reset();
55}
56
57bool Encoder::IsOk() const {
58 return _encoding.empty() || (_conv_storage && _conv_runtime);
59}
60
61void Encoder::Encode(std::string& str) {
62 if (_encoding.empty() || str.empty()) {
63 return;
64 }
65 Convert(str, _conv_runtime, _conv_storage);
66}
67
68void Encoder::Decode(std::string& str) {
69 if (_encoding.empty() || str.empty()) {
70 return;
71 }
72 Convert(str, _conv_storage, _conv_runtime);
73}
74
75void 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
113void 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
123void 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