From 3f3ae1f05bd86585c6e3d2f95599eb2196564aed Mon Sep 17 00:00:00 2001 From: Dmitry Chapyshev Date: Thu, 15 Sep 2016 21:13:26 +0000 Subject: [PATCH] [ROSAPPS] Add NLS to TXT file converter svn path=/trunk/; revision=72693 --- rosapps/applications/devutils/CMakeLists.txt | 1 + .../devutils/nls2txt/CMakeLists.txt | 12 + .../applications/devutils/nls2txt/bestfit.c | 282 ++++++++++++++++++ rosapps/applications/devutils/nls2txt/main.c | 19 ++ rosapps/applications/devutils/nls2txt/nls.c | 203 +++++++++++++ .../applications/devutils/nls2txt/nls2txt.rc | 18 ++ .../applications/devutils/nls2txt/precomp.h | 83 ++++++ 7 files changed, 618 insertions(+) create mode 100644 rosapps/applications/devutils/nls2txt/CMakeLists.txt create mode 100644 rosapps/applications/devutils/nls2txt/bestfit.c create mode 100644 rosapps/applications/devutils/nls2txt/main.c create mode 100644 rosapps/applications/devutils/nls2txt/nls.c create mode 100644 rosapps/applications/devutils/nls2txt/nls2txt.rc create mode 100644 rosapps/applications/devutils/nls2txt/precomp.h diff --git a/rosapps/applications/devutils/CMakeLists.txt b/rosapps/applications/devutils/CMakeLists.txt index f3ff7ec89e7..f87900ea576 100644 --- a/rosapps/applications/devutils/CMakeLists.txt +++ b/rosapps/applications/devutils/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(createspec) add_subdirectory(gdb2) add_subdirectory(gdihv) add_subdirectory(genguid) +add_subdirectory(nls2txt) add_subdirectory(shimdbg) add_subdirectory(symdump) add_subdirectory(syscalldump) diff --git a/rosapps/applications/devutils/nls2txt/CMakeLists.txt b/rosapps/applications/devutils/nls2txt/CMakeLists.txt new file mode 100644 index 00000000000..ae6be88e774 --- /dev/null +++ b/rosapps/applications/devutils/nls2txt/CMakeLists.txt @@ -0,0 +1,12 @@ + +list(APPEND SOURCE + main.c + nls.c + bestfit.c + precomp.h) + +add_executable(nls2txt ${SOURCE} nls2txt.rc) +add_pch(nls2txt precomp.h SOURCE) +set_module_type(nls2txt win32cui UNICODE) +add_importlibs(nls2txt user32 gdi32 comdlg32 comctl32 getuname msvcrt kernel32) +add_cd_file(TARGET nls2txt DESTINATION reactos/system32 FOR all) diff --git a/rosapps/applications/devutils/nls2txt/bestfit.c b/rosapps/applications/devutils/nls2txt/bestfit.c new file mode 100644 index 00000000000..24cd2cfe348 --- /dev/null +++ b/rosapps/applications/devutils/nls2txt/bestfit.c @@ -0,0 +1,282 @@ +/* + * PROJECT: ReactOS NLS to TXT Converter + * LICENSE: GNU General Public License Version 2.0 or any later version + * FILE: devutils/nls2txt/bestfit.c + * COPYRIGHT: Copyright 2016 Dmitry Chapyshev + */ + +#include "precomp.h" + +static HANDLE +BestFit_CreateFile(const WCHAR *pszFile) +{ + DWORD dwBytesWritten; + HANDLE hFile; + + hFile = CreateFileW(pszFile, + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + /* Write UTF-8 BOM */ + WriteFile(hFile, "\xEF\xBB\xBF", 3, &dwBytesWritten, NULL); + } + + return hFile; +} + +static VOID +BestFit_CloseFile(HANDLE hFile) +{ + CloseHandle(hFile); +} + +static CHAR* +UTF8fromUNICODE(const WCHAR *pszInput, PSIZE_T Size) +{ + ULONG Length; + CHAR *pszOutput; + + if (!pszInput || !Size) return NULL; + + Length = WideCharToMultiByte(CP_UTF8, 0, pszInput, -1, NULL, 0, NULL, NULL); + + *Size = Length * sizeof(CHAR); + + pszOutput = (CHAR *) malloc(*Size); + if (pszOutput) + { + WideCharToMultiByte(CP_UTF8, 0, pszInput, -1, pszOutput, Length, NULL, NULL); + } + + return pszOutput; +} + +static VOID +BestFit_Write(HANDLE hFile, const WCHAR *pszFormat, ...) +{ + LARGE_INTEGER FileSize; + LARGE_INTEGER MoveTo; + LARGE_INTEGER NewPos; + DWORD dwBytesWritten; + + if (hFile == INVALID_HANDLE_VALUE) + return; + + MoveTo.QuadPart = 0; + if (!SetFilePointerEx(hFile, MoveTo, &NewPos, FILE_END)) + return; + + if (!GetFileSizeEx(hFile, &FileSize)) + return; + + if (LockFile(hFile, (DWORD_PTR)NewPos.QuadPart, 0, (DWORD_PTR)FileSize.QuadPart, 0)) + { + WCHAR *pszString; + CHAR *pszUtf8; + va_list Args; + SIZE_T Size; + + va_start(Args, pszFormat); + + Size = (_vscwprintf(pszFormat, Args) + 1) * sizeof(WCHAR); + pszString = (WCHAR*) malloc(Size); + + if (!pszString) + { + UnlockFile(hFile, (DWORD_PTR)NewPos.QuadPart, 0, (DWORD_PTR)FileSize.QuadPart, 0); + va_end(Args); + return; + } + + StringCbVPrintfW(pszString, Size, pszFormat, Args); + va_end(Args); + + pszUtf8 = UTF8fromUNICODE(pszString, &Size); + if (pszUtf8) + { + WriteFile(hFile, pszUtf8, Size - sizeof(CHAR), &dwBytesWritten, NULL); + free(pszUtf8); + } + + free(pszString); + + UnlockFile(hFile, (DWORD_PTR)NewPos.QuadPart, 0, (DWORD_PTR)FileSize.QuadPart, 0); + } +} + +BOOL +BestFit_FromNLS(const WCHAR *pszNLSFile, const WCHAR *pszBestFitFile) +{ + CPTABLEINFO CodePageTable; + PUSHORT CodePage; + HANDLE hFile; + USHORT CodePageChar; + ULONG UnicodeChar; + + CodePage = NLS_ReadFile(pszNLSFile, &CodePageTable); + if (CodePage == NULL) + return FALSE; + + hFile = BestFit_CreateFile(pszBestFitFile); + if (hFile == INVALID_HANDLE_VALUE) + { + free(CodePage); + return FALSE; + } + + /* The only field is the decimal windows code page number for this code page. */ + BestFit_Write(hFile, L"CODEPAGE %u\r\n\r\n", CodePageTable.CodePage); + + BestFit_Write(hFile, + L"CPINFO %u 0x%02X 0x%04X\r\n\r\n", + /* "1" for a single byte code page, "2" for a double byte code page */ + CodePageTable.MaximumCharacterSize, + /* Replacement characters for unassigned Unicode code points when + written to this code page */ + CodePageTable.DefaultChar, + /* Replacement characters for illegal or unassigned code page values + when converting to Unicode. */ + CodePageTable.UniDefaultChar); + + /* This field contains the number of following records of code page to Unicode mappings. */ + BestFit_Write(hFile, L"MBTABLE %u\r\n\r\n", NLS_RecordsCountForMBTable(&CodePageTable)); + + for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++) + { + if (!NLS_IsDefaultCharForMB(&CodePageTable, CodePageChar)) + { + WCHAR szCharName[MAX_STR_LEN] = { 0 }; + + GetUName(CodePageTable.MultiByteTable[CodePageChar], szCharName); + + BestFit_Write(hFile, + L"0x%02X 0x%04X ;%s\r\n", + CodePageChar, + CodePageTable.MultiByteTable[CodePageChar], + szCharName); + } + } + + BestFit_Write(hFile, L"\r\n"); + + if (NLS_IsGlyphTablePresent(&CodePageTable)) + { + PUSHORT GlyphTable = CodePageTable.MultiByteTable + 256 + 1; + + BestFit_Write(hFile, L"GLYPHTABLE %u\r\n\r\n", NLS_RecordsCountForGlyphTable(&CodePageTable)); + + for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++) + { + WCHAR szCharName[MAX_STR_LEN] = { 0 }; + + GetUName(GlyphTable[CodePageChar], szCharName); + + BestFit_Write(hFile, + L"0x%02X 0x%04X ;%s\r\n", + CodePageChar, + GlyphTable[CodePageChar], + szCharName); + } + + BestFit_Write(hFile, L"\r\n"); + } + + if (NLS_IsDBCSCodePage(&CodePageTable)) + { + PUSHORT LeadByteRanges = (PUSHORT)&CodePageTable.LeadByte[0]; + USHORT Index; + USHORT LeadByte; + + BestFit_Write(hFile, + L"DBCSRANGE %u ;%u DBCS Lead Byte Ranges\r\n\r\n", + CodePageTable.DBCSRanges[0], + CodePageTable.DBCSRanges[0]); + + for (Index = 0; Index < MAXIMUM_LEADBYTES / 2; Index++) + { + if (!LeadByteRanges[Index]) + continue; + + BestFit_Write(hFile, + L"0x%X 0x%X ;Lead Byte Range %u\r\n\r\n", + LOBYTE(LeadByteRanges[Index]), + HIBYTE(LeadByteRanges[Index]), + Index + 1); + + for (LeadByte = LOBYTE(LeadByteRanges[Index]); + LeadByte <= HIBYTE(LeadByteRanges[Index]); + LeadByte++) + { + PUSHORT LeadByteInfo = CodePageTable.DBCSOffsets; + + BestFit_Write(hFile, + L"DBCSTABLE %u ;Range = %u, LeadByte = 0x%02X\r\n\r\n", + NLS_RecordsCountForDBCSTable(&CodePageTable, LeadByte), + Index + 1, + LeadByte); + + for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++) + { + USHORT Info = LeadByteInfo[LeadByte]; + + if (Info && LeadByteInfo[Info + CodePageChar] != CodePageTable.UniDefaultChar) + { + BestFit_Write(hFile, + L"0x%02X 0x%04X\r\n", + CodePageChar, + LeadByteInfo[Info + CodePageChar]); + } + } + + BestFit_Write(hFile, L"\r\n"); + } + } + } + + /* This field contains the number of records of Unicode to byte mappings. */ + BestFit_Write(hFile, L"WCTABLE %u\r\n\r\n", NLS_RecordsCountForUnicodeTable(&CodePageTable)); + + for (UnicodeChar = 0; UnicodeChar <= 0xFFFF; UnicodeChar++) + { + if (!NLS_IsDefaultCharForUnicode(&CodePageTable, UnicodeChar)) + { + WCHAR szCharName[MAX_STR_LEN] = { 0 }; + + GetUName(UnicodeChar, szCharName); + + if (NLS_IsDBCSCodePage(&CodePageTable)) + { + PUSHORT MultiByteTable = (PUSHORT)CodePageTable.WideCharTable; + + BestFit_Write(hFile, + L"0x%04X 0x%04X ;%s\r\n", + UnicodeChar, + MultiByteTable[UnicodeChar], + szCharName); + } + else + { + PUCHAR SingleByteTable = (PUCHAR)CodePageTable.WideCharTable; + + BestFit_Write(hFile, + L"0x%04X 0x%02X ;%s\r\n", + UnicodeChar, + SingleByteTable[UnicodeChar], + szCharName); + } + } + } + + /* This tag marks the end of the code page data. Anything after this marker is ignored. */ + BestFit_Write(hFile, L"\r\nENDCODEPAGE\r\n"); + + BestFit_CloseFile(hFile); + free(CodePage); + + return TRUE; +} diff --git a/rosapps/applications/devutils/nls2txt/main.c b/rosapps/applications/devutils/nls2txt/main.c new file mode 100644 index 00000000000..f9c1799513e --- /dev/null +++ b/rosapps/applications/devutils/nls2txt/main.c @@ -0,0 +1,19 @@ +/* + * PROJECT: ReactOS NLS to TXT Converter + * LICENSE: GNU General Public License Version 2.0 or any later version + * FILE: devutils/nls2txt/main.c + * COPYRIGHT: Copyright 2016 Dmitry Chapyshev + */ + +#include "precomp.h" + +INT wmain(INT argc, WCHAR* argv[]) +{ + if (argc != 3) + return 1; + + if (!BestFit_FromNLS(argv[1], argv[2])) + return 1; + + return 0; +} diff --git a/rosapps/applications/devutils/nls2txt/nls.c b/rosapps/applications/devutils/nls2txt/nls.c new file mode 100644 index 00000000000..55eda816cad --- /dev/null +++ b/rosapps/applications/devutils/nls2txt/nls.c @@ -0,0 +1,203 @@ +/* + * PROJECT: ReactOS NLS to TXT Converter + * LICENSE: GNU General Public License Version 2.0 or any later version + * FILE: devutils/nlsedit/nls.c + * COPYRIGHT: Copyright 2016 Dmitry Chapyshev + */ + +#include "precomp.h" + +static VOID +NLS_InitCodePageTable(PUSHORT TableBase, PCPTABLEINFO CodePageTable) +{ + PNLS_FILE_HEADER NlsFileHeader; + + NlsFileHeader = (PNLS_FILE_HEADER)TableBase; + + /* Copy header fields first */ + CodePageTable->CodePage = NlsFileHeader->CodePage; + CodePageTable->MaximumCharacterSize = NlsFileHeader->MaximumCharacterSize; + CodePageTable->DefaultChar = NlsFileHeader->DefaultChar; + CodePageTable->UniDefaultChar = NlsFileHeader->UniDefaultChar; + CodePageTable->TransDefaultChar = NlsFileHeader->TransDefaultChar; + CodePageTable->TransUniDefaultChar = NlsFileHeader->TransUniDefaultChar; + + CopyMemory(&CodePageTable->LeadByte, &NlsFileHeader->LeadByte, MAXIMUM_LEADBYTES); + + /* Offset to wide char table is after the header */ + CodePageTable->WideCharTable = TableBase + NlsFileHeader->HeaderSize + 1 + + TableBase[NlsFileHeader->HeaderSize]; + + /* Then multibyte table (256 wchars) follows */ + CodePageTable->MultiByteTable = TableBase + NlsFileHeader->HeaderSize + 1; + + /* Check the presence of glyph table (256 wchars) */ + if (!CodePageTable->MultiByteTable[256]) + { + CodePageTable->DBCSRanges = CodePageTable->MultiByteTable + 256 + 1; + } + else + { + CodePageTable->DBCSRanges = CodePageTable->MultiByteTable + 256 + 1 + 256; + } + + /* Is this double-byte code page? */ + if (*CodePageTable->DBCSRanges) + { + CodePageTable->DBCSCodePage = 1; + CodePageTable->DBCSOffsets = CodePageTable->DBCSRanges + 1; + } + else + { + CodePageTable->DBCSCodePage = 0; + CodePageTable->DBCSOffsets = NULL; + } +} + +PUSHORT +NLS_ReadFile(const WCHAR *pszFile, PCPTABLEINFO CodePageTable) +{ + HANDLE hFile; + + hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + PUSHORT pData = NULL; + DWORD dwRead; + DWORD dwFileSize; + + dwFileSize = GetFileSize(hFile, NULL); + pData = malloc(dwFileSize); + if (pData != NULL) + { + if (ReadFile(hFile, pData, dwFileSize, &dwRead, NULL) != FALSE) + { + NLS_InitCodePageTable(pData, CodePageTable); + CloseHandle(hFile); + + return pData; + } + + free(pData); + } + + CloseHandle(hFile); + } + + return NULL; +} + +BOOL +NLS_IsDBCSCodePage(PCPTABLEINFO CodePageTable) +{ + return (BOOL)CodePageTable->DBCSCodePage; +} + +BOOL +NLS_IsGlyphTablePresent(PCPTABLEINFO CodePageTable) +{ + return (CodePageTable->MultiByteTable[256]) ? TRUE : FALSE; +} + +BOOL +NLS_IsDefaultCharForMB(PCPTABLEINFO CodePageTable, UCHAR Char) +{ + if (CodePageTable->MultiByteTable[Char] != CodePageTable->UniDefaultChar) + return FALSE; + + return TRUE; +} + +BOOL +NLS_IsDefaultCharForUnicode(PCPTABLEINFO CodePageTable, USHORT Char) +{ + USHORT CodePageChar; + + if (NLS_IsDBCSCodePage(CodePageTable)) + { + PUSHORT MultiByteTable = (PUSHORT)CodePageTable->WideCharTable; + CodePageChar = MultiByteTable[Char]; + } + else + { + PUCHAR SingleByteTable = (PUCHAR)CodePageTable->WideCharTable; + CodePageChar = SingleByteTable[Char]; + } + + if (CodePageChar != CodePageTable->DefaultChar) + return FALSE; + + return TRUE; +} + +USHORT +NLS_RecordsCountForMBTable(PCPTABLEINFO CodePageTable) +{ + USHORT CodePageChar; + USHORT Count = 0; + + for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++) + { + if (!NLS_IsDefaultCharForMB(CodePageTable, CodePageChar)) + Count++; + } + + return Count; +} + +USHORT +NLS_RecordsCountForUnicodeTable(PCPTABLEINFO CodePageTable) +{ + ULONG UnicodeChar; + USHORT Count = 0; + + for (UnicodeChar = 0; UnicodeChar <= 0xFFFF; UnicodeChar++) + { + if (!NLS_IsDefaultCharForUnicode(CodePageTable, UnicodeChar)) + Count++; + } + + return Count; +} + +USHORT +NLS_RecordsCountForGlyphTable(PCPTABLEINFO CodePageTable) +{ + USHORT Count = 0; + + if (NLS_IsGlyphTablePresent(CodePageTable)) + { + PUSHORT GlyphTable = CodePageTable->MultiByteTable + 256 + 1; + USHORT CodePageChar; + + for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++) + { + USHORT Char = GlyphTable[CodePageChar]; + + if (Char != CodePageTable->UniDefaultChar) + Count++; + } + } + + return Count; +} + +USHORT +NLS_RecordsCountForDBCSTable(PCPTABLEINFO CodePageTable, UCHAR LeadByte) +{ + PUSHORT LeadByteInfo = CodePageTable->DBCSOffsets; + USHORT CodePageChar; + USHORT Count = 0; + + for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++) + { + USHORT Info = LeadByteInfo[LeadByte]; + + if (Info && LeadByteInfo[Info + CodePageChar] != CodePageTable->UniDefaultChar) + { + Count++; + } + } + + return Count; +} diff --git a/rosapps/applications/devutils/nls2txt/nls2txt.rc b/rosapps/applications/devutils/nls2txt/nls2txt.rc new file mode 100644 index 00000000000..7c6a1b94c8a --- /dev/null +++ b/rosapps/applications/devutils/nls2txt/nls2txt.rc @@ -0,0 +1,18 @@ +/* + * PROJECT: ReactOS NLS to TXT Converter + * LICENSE: GNU General Public License Version 2.0 or any later version + * FILE: devutils/nls2txt/nls2txt.rc + * COPYRIGHT: Copyright 2016 Dmitry Chapyshev + */ + +#include +#include + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS NLS to TXT Converter" +#define REACTOS_STR_INTERNAL_NAME "nls2txt" +#define REACTOS_STR_ORIGINAL_FILENAME "nls2txt.exe" +#include + +#include diff --git a/rosapps/applications/devutils/nls2txt/precomp.h b/rosapps/applications/devutils/nls2txt/precomp.h new file mode 100644 index 00000000000..9fc00642093 --- /dev/null +++ b/rosapps/applications/devutils/nls2txt/precomp.h @@ -0,0 +1,83 @@ +/* + * PROJECT: ReactOS NLS to TXT Converter + * LICENSE: GNU General Public License Version 2.0 or any later version + * FILE: devutils/nls2txt/precomp.h + * COPYRIGHT: Copyright 2016 Dmitry Chapyshev + */ + +#ifndef __PRECOMP_H +#define __PRECOMP_H + +#include +#include +#include +#include + +#define MAX_STR_LEN 256 + +#define MAXIMUM_LEADBYTES 12 + +typedef struct _NLS_FILE_HEADER +{ + USHORT HeaderSize; + USHORT CodePage; + USHORT MaximumCharacterSize; + USHORT DefaultChar; + USHORT UniDefaultChar; + USHORT TransDefaultChar; + USHORT TransUniDefaultChar; + UCHAR LeadByte[MAXIMUM_LEADBYTES]; +} NLS_FILE_HEADER, *PNLS_FILE_HEADER; + +typedef struct _CPTABLEINFO +{ + USHORT CodePage; + USHORT MaximumCharacterSize; /* 1 = SBCS, 2 = DBCS */ + USHORT DefaultChar; /* Default MultiByte Character for the CP->Unicode conversion */ + USHORT UniDefaultChar; /* Default Unicode Character for the CP->Unicode conversion */ + USHORT TransDefaultChar; /* Default MultiByte Character for the Unicode->CP conversion */ + USHORT TransUniDefaultChar; /* Default Unicode Character for the Unicode->CP conversion */ + USHORT DBCSCodePage; + UCHAR LeadByte[MAXIMUM_LEADBYTES]; + PUSHORT MultiByteTable; /* Table for CP->Unicode conversion */ + PVOID WideCharTable; /* Table for Unicode->CP conversion */ + PUSHORT DBCSRanges; + PUSHORT DBCSOffsets; +} CPTABLEINFO, *PCPTABLEINFO; + +int WINAPI +GetUName(IN WORD wCharCode, OUT LPWSTR lpBuf); + +/* nls.c */ +PUSHORT +NLS_ReadFile(const WCHAR *pszFile, PCPTABLEINFO CodePageTable); + +BOOL +NLS_IsDBCSCodePage(PCPTABLEINFO CodePageTable); + +BOOL +NLS_IsGlyphTablePresent(PCPTABLEINFO CodePageTable); + +BOOL +NLS_IsDefaultCharForMB(PCPTABLEINFO CodePageTable, UCHAR Char); + +BOOL +NLS_IsDefaultCharForUnicode(PCPTABLEINFO CodePageTable, USHORT Char); + +USHORT +NLS_RecordsCountForMBTable(PCPTABLEINFO CodePageTable); + +USHORT +NLS_RecordsCountForUnicodeTable(PCPTABLEINFO CodePageTable); + +USHORT +NLS_RecordsCountForGlyphTable(PCPTABLEINFO CodePageTable); + +USHORT +NLS_RecordsCountForDBCSTable(PCPTABLEINFO CodePageTable, UCHAR LeadByte); + +/* bestfit.c */ +BOOL +BestFit_FromNLS(const WCHAR *pszNLSFile, const WCHAR *pszBestFitFile); + +#endif -- 2.17.1