-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Utils.cpp
1474 lines (1281 loc) · 43.7 KB
/
Utils.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//////////////////////////////////////////////////////////////////////////////
// Utils.cpp --- XWord Giver (Japanese Crossword Generator)
// Copyright (C) 2012-2020 Katayama Hirofumi MZ. All Rights Reserved.
// (Japanese, UTF-8)
#include "XWordGiver.hpp"
#define min std::min
#define max std::max
#include <gdiplus.h>
std::shared_ptr<XG_FileManager> xg_pFileManager;
std::shared_ptr<XG_FileManager>& XgGetFileManager(void)
{
if (!xg_pFileManager)
xg_pFileManager = std::make_shared<XG_FileManager>();
return xg_pFileManager;
}
//////////////////////////////////////////////////////////////////////////////
#ifndef NDEBUG
// デバッグ出力。
void __cdecl DebugPrintfW(const char *file, int lineno, LPCWSTR pszFormat, ...)
{
va_list va;
int cch;
static WCHAR s_szText[1024];
va_start(va, pszFormat);
::EnterCriticalSection(&xg_csLock);
if (file) {
StringCchPrintfW(s_szText, _countof(s_szText), L"%hs (%d): ", file, lineno);
cch = lstrlenW(s_szText);
StringCchVPrintfW(&s_szText[cch], _countof(s_szText) - cch, pszFormat, va);
} else {
StringCchVPrintfW(s_szText, _countof(s_szText), pszFormat, va);
}
OutputDebugStringW(s_szText);
::LeaveCriticalSection(&xg_csLock);
va_end(va);
}
#endif
// リソース文字列を読み込む。
LPWSTR __fastcall XgLoadStringDx1(int id) noexcept
{
static WCHAR sz[512];
LoadStringW(xg_hInstance, id, sz, _countof(sz));
return sz;
}
// リソース文字列を読み込む。
LPWSTR __fastcall XgLoadStringDx2(int id) noexcept
{
static WCHAR sz[512];
LoadStringW(xg_hInstance, id, sz, _countof(sz));
return sz;
}
// フィルター文字列を作る。
LPWSTR __fastcall XgMakeFilterString(LPWSTR psz) noexcept
{
// 文字列中の '|' を '\0' に変換する。
LPWSTR pch = psz;
while (*pch != L'\0') {
if (*pch == L'|')
*pch = L'\0';
pch++;
}
return psz;
}
// ショートカットのターゲットのパスを取得する。
bool __fastcall XgGetPathOfShortcutW(LPCWSTR pszLnkFile, LPWSTR pszPath)
{
IShellLinkW* pShellLink;
IPersistFile* pPersistFile;
WIN32_FIND_DATAW find;
bool bRes = false;
pszPath[0] = L'\0';
HRESULT hRes = CoInitialize(nullptr);
if (SUCCEEDED(hRes)) {
if (SUCCEEDED(hRes = CoCreateInstance(CLSID_ShellLink, nullptr,
CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&pShellLink)))
{
if (SUCCEEDED(hRes = pShellLink->QueryInterface(IID_IPersistFile,
(void **)&pPersistFile)))
{
hRes = pPersistFile->Load(pszLnkFile, STGM_READ);
if (SUCCEEDED(hRes)) {
if (SUCCEEDED(hRes = pShellLink->GetPath(pszPath, MAX_PATH, &find, 0)))
{
bRes = (::GetFileAttributesW(pszPath) != 0xFFFFFFFF);
}
}
pPersistFile->Release();
}
pShellLink->Release();
}
CoUninitialize();
}
return bRes;
}
// 文字列を置換する。
bool __fastcall xg_str_replace_all(XGStringW &s, const XGStringW& from, const XGStringW& to)
{
XGStringW t;
size_t i = 0;
bool ret = false;
while ((i = s.find(from, i)) != XGStringW::npos) {
t = s.substr(0, i);
t += to;
t += s.substr(i + from.size());
s = t;
i += to.size();
ret = true;
}
return ret;
}
// 文字列からマルチセットへ変換する。
void __fastcall xg_str_to_multiset(
std::unordered_multiset<WCHAR>& mset, const XGStringW& str)
{
// マルチセットが空であることを仮定する。
assert(mset.empty());
//mset.clear();
// 事前に予約して、スピードを得る。
mset.reserve(str.size());
// 各文字について。
for (auto ch : str) {
// 黒マスや空白マスを無視する。
if (ch == ZEN_BLACK || ch == ZEN_SPACE)
continue;
// 文字をマルチセットに追加する。
mset.emplace(ch);
}
}
// ベクターからマルチセットへ変換する。
void __fastcall xg_vec_to_multiset(
std::unordered_multiset<WCHAR>& mset, const std::vector<WCHAR>& str)
{
// マルチセットが空であることを仮定する。
assert(mset.empty());
//mset.clear();
// 事前に予約して、スピードを得る。
mset.reserve(str.size());
// 各文字について。
for (auto ch : str) {
// 黒マスや空白マスを無視する。
if (ch == ZEN_BLACK || ch == ZEN_SPACE)
continue;
// 文字をマルチセットに追加する。
mset.emplace(ch);
}
}
// 部分マルチセットかどうか?
bool __fastcall xg_submultiseteq(const std::unordered_multiset<WCHAR>& ms1,
const std::unordered_multiset<WCHAR>& ms2)
{
for (const auto& elem : ms1) {
if (ms1.count(elem) > ms2.count(elem))
return false;
}
return true;
}
// UTF-8 -> Unicode.
XGStringW __fastcall XgUtf8ToUnicode(const std::string& ansi)
{
// 変換先の文字数を取得する。
const int cch = MultiByteToWideChar(CP_UTF8, 0, ansi.data(), -1, nullptr, 0);
if (cch == 0)
return L"";
// 変換先のバッファを確保する。
XGStringW uni(cch - 1, 0);
// 変換して格納する。
MultiByteToWideChar(CP_UTF8, 0, ansi.data(), -1, &uni[0], cch);
return uni;
}
// ダイアログを中央によせる関数。
void __fastcall XgCenterDialog(HWND hwnd) noexcept
{
// 子ウィンドウか?
const bool bChild = !!(::GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_CHILD);
// オーナーウィンドウ(親ウィンドウ)を取得する。
HWND hwndOwner;
if (bChild)
hwndOwner = ::GetParent(hwnd);
else
hwndOwner = ::GetWindow(hwnd, GW_OWNER);
// オーナーウィンドウ(親ウィンドウ)の座標を取得する。
// オーナーウィンドウ(親ウィンドウ)がないときはワークエリアを使う。
RECT rc, rcOwner;
if (hwndOwner != nullptr)
::GetWindowRect(hwndOwner, &rcOwner);
else
::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcOwner, 0);
// ウィンドウの座標をスクリーン座標で取得する。
::GetWindowRect(hwnd, &rc);
// スクリーン座標で中央寄せの位置を計算する。
POINT pt;
pt.x = rcOwner.left +
((rcOwner.right - rcOwner.left) - (rc.right - rc.left)) / 2;
pt.y = rcOwner.top +
((rcOwner.bottom - rcOwner.top) - (rc.bottom - rc.top)) / 2;
// 子ウィンドウなら、スクリーン座標をクライアント座標に変換する。
if (bChild && hwndOwner != nullptr)
::ScreenToClient(hwndOwner, &pt);
// ウィンドウの位置を設定する。
::SetWindowPos(hwnd, nullptr, pt.x, pt.y, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
// ワークエリアからはみでていたら修正する。
::SendMessageW(hwnd, DM_REPOSITION, 0, 0);
}
// メッセージボックスフック用。
static HHOOK s_hMsgBoxHook = nullptr;
// メッセージボックスフック用の関数。
extern "C" LRESULT CALLBACK
XgMsgBoxCbtProc(int nCode, WPARAM wParam, LPARAM /*lParam*/)
{
if (nCode == HCBT_ACTIVATE) {
// ウィンドウがアクティブ化されようとしている。
// ウィンドウハンドルを取得。
HWND hwnd = reinterpret_cast<HWND>(wParam);
// ウィンドウクラスの確認。
WCHAR szClassName[MAX_PATH];
::GetClassNameW(hwnd, szClassName, _countof(szClassName));
if (::lstrcmpiW(szClassName, L"#32770") == 0) {
// ダイアログだった。おそらくメッセージボックス。
// 中央寄せする。
XgCenterDialog(hwnd);
// フックを解除する。
if (s_hMsgBoxHook != nullptr && UnhookWindowsHookEx(s_hMsgBoxHook))
s_hMsgBoxHook = nullptr;
}
}
// allow the operation
return 0;
}
// 中央寄せメッセージボックスを表示する。
int __fastcall
XgCenterMessageBoxW(HWND hwnd, LPCWSTR pszText, LPCWSTR pszCaption, UINT uType) noexcept
{
// フックされていたらフックを解除する。
if (s_hMsgBoxHook != nullptr && UnhookWindowsHookEx(s_hMsgBoxHook))
s_hMsgBoxHook = nullptr;
// フックのためにウィンドウのインスタンスを取得する。
HINSTANCE hInst =
reinterpret_cast<HINSTANCE>(::GetWindowLongPtrW(hwnd, GWLP_HINSTANCE));
// フックを開始。
const auto dwThreadID = ::GetCurrentThreadId();
s_hMsgBoxHook = ::SetWindowsHookEx(WH_CBT, XgMsgBoxCbtProc, hInst, dwThreadID);
// メッセージボックスを表示する。
const int nID = MessageBoxW(hwnd, pszText, pszCaption, uType);
// フックされていたらフックを解除する。
if (s_hMsgBoxHook != nullptr && UnhookWindowsHookEx(s_hMsgBoxHook))
s_hMsgBoxHook = nullptr;
return nID; // メッセージボックスの戻り値。
}
// 中央寄せメッセージボックスを表示する。
int __fastcall
XgCenterMessageBoxIndirectW(LPMSGBOXPARAMS lpMsgBoxParams) noexcept
{
// フックされていたらフックを解除する。
if (s_hMsgBoxHook != nullptr && UnhookWindowsHookEx(s_hMsgBoxHook))
s_hMsgBoxHook = nullptr;
// フックのためにウィンドウのインスタンスを取得する。
HINSTANCE hInst =
reinterpret_cast<HINSTANCE>(
::GetWindowLongPtrW(lpMsgBoxParams->hwndOwner, GWLP_HINSTANCE));
// フックを開始。
const auto dwThreadID = ::GetCurrentThreadId();
s_hMsgBoxHook = ::SetWindowsHookEx(WH_CBT, XgMsgBoxCbtProc, hInst, dwThreadID);
// メッセージボックスを表示する。
const int nID = MessageBoxIndirectW(lpMsgBoxParams);
// フックされていたらフックを解除する。
if (s_hMsgBoxHook != nullptr && UnhookWindowsHookEx(s_hMsgBoxHook))
s_hMsgBoxHook = nullptr;
return nID; // メッセージボックスの戻り値。
}
// ファイルが書き込み可能か?
bool __fastcall XgCanWriteFile(const WCHAR *pszFile)
{
// 書き込みをすべきでない特殊フォルダのID。
static const auto s_anFolders = make_array<int>(
CSIDL_PROGRAM_FILES,
CSIDL_PROGRAM_FILES_COMMON,
CSIDL_PROGRAM_FILES_COMMONX86,
CSIDL_PROGRAM_FILESX86,
CSIDL_SYSTEM,
CSIDL_SYSTEMX86,
CSIDL_WINDOWS
);
// 与えられたパスファイル名。
XGStringW str(pszFile);
for (auto folder : s_anFolders) {
// 特殊フォルダの位置の取得。
LPITEMIDLIST pidl = nullptr;
if (SUCCEEDED(::SHGetSpecialFolderLocation(xg_hMainWnd, folder, &pidl)))
{
// 特殊フォルダのパスを得る。
WCHAR szPath[MAX_PATH];
::SHGetPathFromIDListW(pidl, szPath);
::CoTaskMemFree(pidl);
// パスが一致するか?
if (str.find(szPath) == 0)
return false; // ここには保存すべきでない。
}
}
// 書き込みできるか?
return _waccess(pszFile, 02) == 0;
}
// Unicode -> UTF8
std::string XgUnicodeToUtf8(const XGStringW& wide)
{
const int len = ::WideCharToMultiByte(CP_UTF8, 0, wide.data(), -1, nullptr, 0, nullptr, nullptr);
if (len == 0)
return "";
std::string utf8(len - 1, 0);
::WideCharToMultiByte(CP_UTF8, 0, wide.data(), -1, &utf8[0], len, nullptr, nullptr);
return utf8;
}
// ANSI -> Unicode
XGStringW XgAnsiToUnicode(const std::string& ansi)
{
const int len = ::MultiByteToWideChar(XG_SJIS_CODEPAGE, 0, ansi.data(), -1, nullptr, 0);
if (len == 0)
return L"";
XGStringW uni(len - 1, 0);
::MultiByteToWideChar(XG_SJIS_CODEPAGE, 0, ansi.data(), -1, &uni[0], len);
return uni;
}
// ANSI -> Unicode
XGStringW XgAnsiToUnicode(const std::string& ansi, INT codepage)
{
const int len = ::MultiByteToWideChar(codepage, 0, ansi.data(), -1, nullptr, 0);
if (len == 0)
return L"";
XGStringW uni(len - 1, 0);
::MultiByteToWideChar(codepage, 0, ansi.data(), -1, &uni[0], len);
return uni;
}
// Unicode -> ANSI
std::string XgUnicodeToAnsi(const XGStringW& wide)
{
const int len = ::WideCharToMultiByte(XG_SJIS_CODEPAGE, 0, wide.data(), -1, nullptr, 0, nullptr, nullptr);
if (len == 0)
return "";
std::string ansi(len - 1, 0);
::WideCharToMultiByte(XG_SJIS_CODEPAGE, 0, wide.data(), -1, &ansi[0], len, nullptr, nullptr);
return ansi;
}
// JSON文字列を作る。
XGStringW XgJsonEncodeString(const XGStringW& str)
{
XGStringW encoded;
wchar_t buf[16];
size_t i;
const auto siz = str.size();
encoded.clear();
for (i = 0; i < siz; ++i) {
switch (str[i]) {
case L'\"': encoded += L"\\\""; break;
case L'\\': encoded += L"\\\\"; break;
case L'/': encoded += L"\\/"; break;
case L'\b': encoded += L"\\b"; break;
case L'\f': encoded += L"\\f"; break;
case L'\n': encoded += L"\\n"; break;
case L'\r': encoded += L"\\r"; break;
case L'\t': encoded += L"\\t"; break;
default:
if (0 < str[i] && str[i] <= L'\x1F') {
StringCchPrintf(buf, _countof(buf), L"\\u%04X", str[i]);
encoded += buf;
} else {
encoded += str[i];
}
}
}
return encoded;
}
// 16進で表す。
char XgToHex(char code) noexcept
{
static const char s_hex[] = "0123456789abcdef";
assert(0 <= code && code < 16);
return s_hex[code & 15];
}
//////////////////////////////////////////////////////////////////////////////
// パスを作る。
BOOL XgMakePathW(LPCWSTR pszPath)
{
WCHAR szPath[MAX_PATH];
StringCchCopy(szPath, _countof(szPath), pszPath);
const DWORD attrs = ::GetFileAttributesW(szPath);
if (attrs != 0xFFFFFFFF) {
return (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
LPWSTR pch = wcsrchr(szPath, L'\\');
if (pch == nullptr) {
return TRUE;
}
*pch = 0;
if (XgMakePathW(szPath)) {
return CreateDirectoryW(pszPath, nullptr);
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////
// エンディアン変換。
void XgSwab(LPBYTE pbFile, size_t cbFile) noexcept
{
LPWORD pw = reinterpret_cast<LPWORD>(pbFile);
size_t cw = (cbFile >> 1);
while (cw--) {
const WORD w = *pw;
const BYTE lo = LOBYTE(w);
const BYTE hi = HIBYTE(w);
*pw = MAKEWORD(hi, lo);
++pw;
}
}
//////////////////////////////////////////////////////////////////////////////
// HTML形式のクリップボードデータを作成する。
std::string XgMakeClipHtmlData(const std::string& html_utf8,
const std::string& style_utf8/* = ""*/)
{
using namespace std;
std::string str(
"Version:0.9\r\n"
"StartHTML:00000000\r\n"
"EndHTML:00000000\r\n"
"StartFragment:00000000\r\n"
"EndFragment:00000000\r\n"
"<html><head>\r\n"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\r\n"
"<style type=\"text/css\"><!--\r\n");
str += style_utf8;
str +=
"--></style></head>"
"<body>\r\n"
"<!-- StartFragment -->\r\n";
str += html_utf8;
str += "\r\n<!-- EndFragment -->\r\n";
str += "</body></html>\r\n";
const size_t iHtmlStart = str.find("<html>");
const size_t iHtmlEnd = str.size();
const size_t iFragmentStart = str.find("<!-- StartFragment -->");
const size_t iFragmentEnd = str.find("<!-- EndFragment -->");
char buf[9];
size_t i;
i = str.find("StartHTML:");
i += 10;
StringCchPrintfA(buf, _countof(buf), "%08u", static_cast<UINT>(iHtmlStart));
str.replace(i, 8, buf);
i = str.find("EndHTML:");
i += 8;
StringCchPrintfA(buf, _countof(buf), "%08u", static_cast<UINT>(iHtmlEnd));
str.replace(i, 8, buf);
i = str.find("StartFragment:");
i += 14;
StringCchPrintfA(buf, _countof(buf), "%08u", static_cast<UINT>(iFragmentStart));
str.replace(i, 8, buf);
i = str.find("EndFragment:");
i += 12;
StringCchPrintfA(buf, _countof(buf), "%08u", static_cast<UINT>(iFragmentEnd));
str.replace(i, 8, buf);
return str;
}
// HTML形式のクリップボードデータを作成する。
std::string XgMakeClipHtmlData(const XGStringW& html_wide,
const XGStringW& style_wide/* = L""*/)
{
return XgMakeClipHtmlData(
XgUnicodeToUtf8(html_wide), XgUnicodeToUtf8(style_wide));
}
//////////////////////////////////////////////////////////////////////////////
// 24BPPビットマップを作成。
HBITMAP XgCreate24BppBitmap(HDC hDC, LONG width, LONG height) noexcept
{
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
return CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, nullptr, nullptr, 0);
}
// BITMAPINFOEX構造体。
typedef struct tagBITMAPINFOEX
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
} BITMAPINFOEX, FAR * LPBITMAPINFOEX;
BOOL PackedDIB_CreateFromHandle(std::vector<BYTE>& vecData, HBITMAP hbm)
{
vecData.clear();
BITMAP bm;
if (!GetObject(hbm, sizeof(bm), &bm))
return FALSE;
BITMAPINFOEX bi;
BITMAPINFOHEADER *pbmih;
DWORD cColors, cbColors;
pbmih = &bi.bmiHeader;
ZeroMemory(pbmih, sizeof(BITMAPINFOHEADER));
pbmih->biSize = sizeof(BITMAPINFOHEADER);
pbmih->biWidth = bm.bmWidth;
pbmih->biHeight = bm.bmHeight;
pbmih->biPlanes = 1;
pbmih->biBitCount = bm.bmBitsPixel;
pbmih->biSizeImage = bm.bmWidthBytes * bm.bmHeight;
if (bm.bmBitsPixel < 16)
cColors = 1 << bm.bmBitsPixel;
else
cColors = 0;
cbColors = cColors * sizeof(RGBQUAD);
std::vector<BYTE> Bits(pbmih->biSizeImage);
HDC hDC = CreateCompatibleDC(nullptr);
if (hDC == nullptr)
return FALSE;
auto pbi = reinterpret_cast<LPBITMAPINFO>(&bi);
if (!GetDIBits(hDC, hbm, 0, bm.bmHeight, &Bits[0], pbi, DIB_RGB_COLORS))
{
DeleteDC(hDC);
return FALSE;
}
DeleteDC(hDC);
XGStringA stream;
stream.append(reinterpret_cast<const char *>(pbmih), sizeof(*pbmih));
stream.append(reinterpret_cast<const char *>(bi.bmiColors), cbColors);
stream.append(reinterpret_cast<const char *>(&Bits[0]), Bits.size());
vecData.assign(stream.begin(), stream.end());
return TRUE;
}
// 整数を文字列にする。
LPCWSTR XgIntToStr(int nValue)
{
static WCHAR s_szText[64];
StringCchPrintfW(s_szText, _countof(s_szText), L"%d", nValue);
return s_szText;
}
// バイナリを16進にする。
XGStringW XgBinToHex(const void *ptr, size_t size)
{
XGStringW ret;
auto pb = static_cast<const BYTE *>(ptr);
WCHAR sz[8];
for (size_t i = 0; i < size; ++i)
{
StringCchPrintfW(sz, _countof(sz), L"%02X", pb[i]);
ret += sz;
}
return ret;
}
// 16進をバイナリにする。
void XgHexToBin(std::vector<BYTE>& data, const XGStringW& str)
{
WCHAR sz[3];
data.clear();
bool flag = false;
sz[2] = 0;
for (auto& ch : str)
{
if (flag)
{
sz[1] = ch;
const auto b = static_cast<BYTE>(wcstol(sz, nullptr, 16));
data.insert(data.end(), b);
}
else
{
sz[0] = ch;
}
flag = !flag;
}
}
BOOL XgReadFileAll(LPCWSTR file, std::string& strBinary)
{
strBinary.clear();
if (FILE *fin = _wfopen(file, L"rb")) {
CHAR buf[1024];
for (;;) {
size_t count = fread(buf, 1, 1024, fin);
if (!count)
break;
strBinary.insert(strBinary.end(), &buf[0], &buf[count]);
}
fclose(fin);
return TRUE;
}
return FALSE;
}
// ファイルを読み込む。
BOOL XgWriteFileAll(LPCWSTR file, const std::string& strBinary) noexcept
{
if (FILE *fout = _wfopen(file, L"wb")) {
const bool ret = fwrite(strBinary.c_str(), 1, strBinary.size(), fout);
fclose(fout);
return !!ret;
}
return FALSE;
}
INT XgCharSetToCodePage(INT charset)
{
switch (charset) {
case ANSI_CHARSET: return 1252;
case DEFAULT_CHARSET: return CP_ACP;
case SYMBOL_CHARSET: return CP_SYMBOL;
case SHIFTJIS_CHARSET: return 932;
case HANGEUL_CHARSET: return 949;
case GB2312_CHARSET: return 936;
case CHINESEBIG5_CHARSET: return 950;
case OEM_CHARSET: return CP_OEMCP;
case JOHAB_CHARSET: return 1255;
case HEBREW_CHARSET: return 1255;
case ARABIC_CHARSET: return 1256;
case GREEK_CHARSET: return 1253;
case TURKISH_CHARSET: return 1254;
case VIETNAMESE_CHARSET: return 1258;
case THAI_CHARSET: return 874;
case EASTEUROPE_CHARSET: return 1250;
case RUSSIAN_CHARSET: return 1251;
case MAC_CHARSET: return CP_MACCP;
case BALTIC_CHARSET: return 1257;
default: return CP_ACP;
}
}
INT XgDetectCodePageFromEcw(const std::string& strBinary)
{
auto index = strBinary.find("\nCodePage:");
if (index == strBinary.npos)
return CP_ACP;
// The name is "CodePage" but codepage
index += std::strlen("\nCodePage:");
INT charset = atoi(&strBinary.c_str()[index]);
return XgCharSetToCodePage(charset);
}
// テキストファイルを読み込む。
BOOL XgReadTextFileAll(LPCWSTR file, XGStringW& strText, bool ecw)
{
strText.clear();
std::string strBinary;
if (!XgReadFileAll(file, strBinary))
return FALSE;
if (strBinary.empty())
return TRUE;
if (ecw)
{
strText = XgAnsiToUnicode(strBinary, XgDetectCodePageFromEcw(strBinary));
return TRUE;
}
if (strBinary.size() >= 3)
{
if (memcmp(strBinary.c_str(), "\xEF\xBB\xBF", 3) == 0)
{
// UTF-8 BOM
strText = XgUtf8ToUnicode(&strBinary[3]);
return TRUE;
}
if (memcmp(strBinary.c_str(), "\xFF\xFE", 2) == 0)
{
// UTF-16 LE
auto ptr = reinterpret_cast<LPWSTR>(&strBinary[2]);
const size_t len = (strBinary.size() - 1) / sizeof(WCHAR);
strText.assign(ptr, len);
return TRUE;
}
if (memcmp(strBinary.c_str(), "\xFE\xFF", 2) == 0)
{
// UTF-16 BE
auto ptr = reinterpret_cast<LPWSTR>(&strBinary[2]);
const size_t len = (strBinary.size() - 1) / sizeof(WCHAR);
strText.assign(ptr, len);
XgSwab(reinterpret_cast<LPBYTE>(&strText[0]), len * sizeof(WCHAR));
return TRUE;
}
}
size_t index = 0;
BOOL bUTF16LE = FALSE, bUTF16BE = FALSE;
for (auto ch : strBinary)
{
if (ch == 0)
{
if (index & 1)
{
bUTF16LE = TRUE;
if (bUTF16BE)
break;
}
else
{
bUTF16BE = TRUE;
if (bUTF16LE)
break;
}
}
++index;
if (index >= strBinary.size())
break;
}
if (bUTF16BE && bUTF16LE)
{
// binary
strText = XgUtf8ToUnicode(strBinary);
return TRUE;
}
if (bUTF16BE || bUTF16LE)
{
// UTF-16 BE/LE
auto ptr = reinterpret_cast<LPCWSTR>(strBinary.c_str());
const size_t len = strBinary.size() / sizeof(WCHAR);
strText.assign(ptr, len);
if (bUTF16BE)
XgSwab(reinterpret_cast<LPBYTE>(&strText[0]), len * sizeof(WCHAR));
return TRUE;
}
const BOOL bNotUTF8 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, strBinary.c_str(), -1,
nullptr, 0) == 0;
const BOOL bNotAnsi = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, strBinary.c_str(), -1,
nullptr, 0) == 0;
if (!bNotUTF8 && bNotAnsi)
{
strText = XgUtf8ToUnicode(strBinary);
return TRUE;
}
if (bNotUTF8 && !bNotAnsi)
{
strText = XgAnsiToUnicode(strBinary);
return TRUE;
}
XGStringW strFromUtf8 = XgUtf8ToUnicode(strBinary);
if (XgUnicodeToUtf8(strFromUtf8) == strBinary)
{
strText = std::move(strFromUtf8);
return TRUE;
}
XGStringW strFromAnsi = XgAnsiToUnicode(strBinary);
if (XgUnicodeToAnsi(strFromAnsi) == strBinary)
{
strText = std::move(strFromAnsi);
return TRUE;
}
strText = XgUtf8ToUnicode(strBinary);
return TRUE;
}
// 画像ファイルか?
BOOL XgIsImageFile(LPCWSTR pszFileName) noexcept
{
LPCWSTR pchDotExt = PathFindExtensionW(pszFileName);
if (lstrcmpiW(pchDotExt, L".bmp") == 0 ||
lstrcmpiW(pchDotExt, L".dib") == 0 ||
lstrcmpiW(pchDotExt, L".emf") == 0 ||
lstrcmpiW(pchDotExt, L".gif") == 0 ||
lstrcmpiW(pchDotExt, L".png") == 0 ||
lstrcmpiW(pchDotExt, L".jpg") == 0 ||
lstrcmpiW(pchDotExt, L".jpeg") == 0 ||
lstrcmpiW(pchDotExt, L".jpe") == 0 ||
lstrcmpiW(pchDotExt, L".jfif") == 0 ||
lstrcmpiW(pchDotExt, L".tif") == 0 ||
lstrcmpiW(pchDotExt, L".tiff") == 0)
{
return TRUE;
}
return FALSE;
}
// テキストファイルか?
BOOL XgIsTextFile(LPCWSTR pszFileName) noexcept
{
LPCWSTR pchDotExt = PathFindExtensionW(pszFileName);
return lstrcmpiW(pchDotExt, L".txt") == 0;
}
// 画像を読み込む。
BOOL XgLoadImage(const XGStringW& filename, HBITMAP& hbm, HENHMETAFILE& hEMF)
{
hbm = nullptr;
hEMF = nullptr;
// パス名をセット。
WCHAR szFullPath[MAX_PATH];
if (!GetFullPathNameW(filename.c_str(), _countof(szFullPath), szFullPath, nullptr))
return FALSE;
LPCWSTR pchDotExt = PathFindExtensionW(szFullPath);
if (lstrcmpiW(pchDotExt, L".bmp") == 0)
{
hbm = LoadBitmapFromFile(szFullPath);
return hbm != nullptr;
}
if (lstrcmpiW(pchDotExt, L".emf") == 0)
{
// ロックを防ぐためにメモリーに読み込む。
HENHMETAFILE hGotEMF = ::GetEnhMetaFile(szFullPath);
hEMF = ::CopyEnhMetaFile(hGotEMF, nullptr);
::DeleteEnhMetaFile(hGotEMF);
return hEMF != nullptr;
}
// GDI+で読み込む。
if (HINSTANCE hGdiPlus = LoadLibraryA("gdiplus"))
{
using namespace Gdiplus;
typedef GpStatus (WINAPI *FN_GdiplusStartup)(ULONG_PTR*,GDIPCONST GdiplusStartupInput*,GdiplusStartupOutput*);
typedef VOID (WINAPI *FN_GdiplusShutdown)(ULONG_PTR);
typedef GpStatus (WINAPI *FN_GdipCreateBitmapFromFile)(GDIPCONST WCHAR*,GpBitmap**);
typedef GpStatus (WINAPI *FN_GdipCreateHBITMAPFromBitmap)(GpBitmap*,HBITMAP*,ARGB);
typedef GpStatus (WINAPI *FN_GdipDisposeImage)(GpImage*);
auto GdiplusStartup = reinterpret_cast<FN_GdiplusStartup>(::GetProcAddress(hGdiPlus, "GdiplusStartup"));
auto GdiplusShutdown = reinterpret_cast<FN_GdiplusShutdown>(::GetProcAddress(hGdiPlus, "GdiplusShutdown"));
auto GdipCreateBitmapFromFile = reinterpret_cast<FN_GdipCreateBitmapFromFile>(::GetProcAddress(hGdiPlus, "GdipCreateBitmapFromFile"));
auto GdipCreateHBITMAPFromBitmap = reinterpret_cast<FN_GdipCreateHBITMAPFromBitmap>(::GetProcAddress(hGdiPlus, "GdipCreateHBITMAPFromBitmap"));
auto GdipDisposeImage = reinterpret_cast<FN_GdipDisposeImage>(::GetProcAddress(hGdiPlus, "GdipDisposeImage"));
if (GdiplusStartup &&
GdiplusShutdown &&
GdipCreateBitmapFromFile &&
GdipCreateHBITMAPFromBitmap &&
GdipDisposeImage)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// GDI+の初期化。
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
Color c;
c.SetFromCOLORREF(RGB(255, 255, 255));
GpBitmap *pBitmap = nullptr;
GdipCreateBitmapFromFile(szFullPath, &pBitmap);
if (pBitmap)
{
GdipCreateHBITMAPFromBitmap(pBitmap, &hbm, c.ToCOLORREF());
GdipDisposeImage(pBitmap);
}
// GDI+の後処理。
GdiplusShutdown(gdiplusToken);
}
FreeLibrary(hGdiPlus);
}
return hbm != nullptr;
}
// 文字列をエスケープする。
XGStringW xg_str_escape(const XGStringW& str)
{
XGStringW ret;
ret.reserve(str.size());
for (auto ch : str)
{
switch (ch)
{
case L'\a': ret += L"\\a"; break;
case L'\b': ret += L"\\b"; break;
case L'\t': ret += L"\\t"; break;
case L'\n': ret += L"\\n"; break;
case L'\r': ret += L"\\r"; break;
case L'\f': ret += L"\\f"; break;
case L'\v': ret += L"\\v"; break;
case L'\\': ret += L"\\\\"; break;
default:
if (ch < 0x20 || ch == 0x7F) {
WCHAR sz[8];
StringCchPrintf(sz, 8, L"\\%03o", ch);
ret += sz;
} else {
ret += ch;
}
}
}
return ret;
}
// 文字列をアンエスケープする。
XGStringW xg_str_unescape(const XGStringW& str)
{
XGStringW ret;
ret.reserve(str.size());
for (size_t i = 0; i < str.size(); ++i)
{
auto ch = str[i];
if (ch != L'\\') {
ret += ch;
continue;
}
ch = str[++i];
switch (ch)
{