mirror of
https://github.com/reactos/reactos.git
synced 2025-04-05 13:11:22 +00:00
[TXT2NLS] Rewrite the tool
With support for multibyte codepages & glyph tables CORE-17571
This commit is contained in:
parent
1549f0837f
commit
79b2d9c2ff
6 changed files with 548 additions and 817 deletions
|
@ -1,7 +1,4 @@
|
|||
|
||||
list(APPEND SOURCE
|
||||
main.c
|
||||
txt.c
|
||||
nls.c)
|
||||
list(APPEND SOURCE main.cpp)
|
||||
|
||||
add_host_tool(txt2nls ${SOURCE})
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* PROJECT: ReactOS TXT to NLS Converter
|
||||
* LICENSE: GNU General Public License Version 2.0 or any later version
|
||||
* FILE: devutils/txt2nls/main.c
|
||||
* COPYRIGHT: Copyright 2016 Dmitry Chapyshev <dmitry@reactos.org>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc != 3)
|
||||
return 1;
|
||||
|
||||
if (!nls_from_txt(argv[1], argv[2]))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
547
sdk/tools/txt2nls/main.cpp
Normal file
547
sdk/tools/txt2nls/main.cpp
Normal file
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* PROJECT: ReactOS TXT to NLS Converter
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later.html)
|
||||
* FILE: sdk/tools/txt2nls/main.c
|
||||
* COPYRIGHT: Copyright 2021 Jérôme Gardou <jerome.gardou@reactos.org>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
static const char whitespaces[] = " \t\f\v\n\r";
|
||||
static long line_number = -1;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
#define MAXIMUM_LEADBYTES 12
|
||||
struct NLS_FILE_HEADER
|
||||
{
|
||||
uint16_t HeaderSize;
|
||||
uint16_t CodePage;
|
||||
uint16_t MaximumCharacterSize;
|
||||
uint16_t DefaultChar;
|
||||
uint16_t UniDefaultChar;
|
||||
uint16_t TransDefaultChar;
|
||||
uint16_t TransUniDefaultChar;
|
||||
uint8_t LeadByte[MAXIMUM_LEADBYTES];
|
||||
};
|
||||
static_assert(sizeof(NLS_FILE_HEADER) == 26, "Wrong size for NLS_FILE_HEADER");
|
||||
#pragma pack(pop)
|
||||
|
||||
static std::istream& get_clean_line(std::istream& stream, std::string& str)
|
||||
{
|
||||
do
|
||||
{
|
||||
std::istream& ret = std::getline(stream, str);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
/* Ignore comments */
|
||||
std::size_t comment_pos = str.find_first_of(';');
|
||||
if (comment_pos != std::string::npos)
|
||||
{
|
||||
str.erase(comment_pos);
|
||||
}
|
||||
|
||||
/* Remove trailing spaces */
|
||||
std::size_t end_of_line = str.find_last_not_of(whitespaces);
|
||||
if (end_of_line != std::string::npos)
|
||||
str.erase(end_of_line + 1);
|
||||
else
|
||||
str.clear();
|
||||
|
||||
line_number++;
|
||||
} while (str.empty());
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static void tokenize(std::string& str, std::string& token)
|
||||
{
|
||||
std::size_t token_start = str.find_first_not_of(whitespaces);
|
||||
if (token_start == std::string::npos)
|
||||
{
|
||||
token = "";
|
||||
str.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t token_end = str.find_first_of(whitespaces, token_start);
|
||||
if (token_end == std::string::npos)
|
||||
{
|
||||
token = str.substr(token_start);
|
||||
str.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
token = str.substr(token_start, token_end);
|
||||
str.erase(0, str.find_first_not_of(whitespaces, token_end));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void tokenize(std::string& str, T& int_token, int base = 0)
|
||||
{
|
||||
std::string token;
|
||||
tokenize(str, token);
|
||||
|
||||
long val;
|
||||
val = std::stol(token, nullptr, base);
|
||||
if ((val > std::numeric_limits<T>::max()) || (val < std::numeric_limits<T>::min()))
|
||||
throw std::invalid_argument(token + " does not fit range ["
|
||||
+ std::to_string(std::numeric_limits<T>::min()) + ":" + std::to_string(std::numeric_limits<T>::max()) + "]");
|
||||
|
||||
int_token = val;
|
||||
}
|
||||
|
||||
void error(const std::string& err)
|
||||
{
|
||||
std::cerr << "Error parsing line " << line_number <<": " << err << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: " << argv[0] << " <txt_in> <nls_out>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::ifstream input(argv[1]);
|
||||
if (!input.is_open())
|
||||
{
|
||||
std::cerr << "Unable to open " << argv[1] << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
NLS_FILE_HEADER FileHeader;
|
||||
memset(&FileHeader, 0, sizeof(FileHeader));
|
||||
|
||||
std::string curr_line;
|
||||
// Get code page
|
||||
if (!get_clean_line(input, curr_line))
|
||||
{
|
||||
std::cerr << "ERROR: File is empty" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string token;
|
||||
tokenize(curr_line, token);
|
||||
if (token != "CODEPAGE")
|
||||
error("expected CODEPAGE, got \"" + token + "\" instead");
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, FileHeader.CodePage, 10);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after CODEPAGE statement: \"" + curr_line + "\"");
|
||||
|
||||
/* Get CPINFO */
|
||||
if (!get_clean_line(input, curr_line))
|
||||
error("Nothing after CODEPAGE statement");
|
||||
|
||||
tokenize(curr_line, token);
|
||||
if (token != "CPINFO")
|
||||
error("Expected CPINFO, got \"" + token + "\" instead");
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, FileHeader.MaximumCharacterSize);
|
||||
tokenize(curr_line, FileHeader.DefaultChar);
|
||||
tokenize(curr_line, FileHeader.UniDefaultChar);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
return 1;
|
||||
}
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after CPINFO statement: \"" + curr_line + "\"");
|
||||
if ((FileHeader.MaximumCharacterSize != 1) && (FileHeader.MaximumCharacterSize != 2))
|
||||
error("Expected 1 or 2 as max char size in CPINFO, got \"" + std::to_string(FileHeader.MaximumCharacterSize) + "\" instead");
|
||||
if ((FileHeader.MaximumCharacterSize == 1) && (FileHeader.DefaultChar > std::numeric_limits<uint8_t>::max()))
|
||||
error("Default MB character " + std::to_string(FileHeader.DefaultChar) + " doesn't fit in a 8-bit value");
|
||||
|
||||
/* Setup tables & default values */
|
||||
bool has_mbtable = false;
|
||||
uint16_t mb_table[256] = {0};
|
||||
|
||||
bool has_wctable = false;
|
||||
uint8_t* wc_table = new uint8_t[65536 * FileHeader.MaximumCharacterSize];
|
||||
if (FileHeader.MaximumCharacterSize == 1)
|
||||
{
|
||||
for (int i = 0; i < 65536; i++)
|
||||
wc_table[i] = FileHeader.DefaultChar;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t* wc_table_dbcs = reinterpret_cast<uint16_t*>(wc_table);
|
||||
for (int i = 0; i < 65536; i++)
|
||||
wc_table_dbcs[i] = FileHeader.DefaultChar;
|
||||
}
|
||||
|
||||
std::vector<uint16_t> dbcs_table;
|
||||
uint16_t lb_offsets[256] = {0};
|
||||
uint16_t dbcs_range_count = 0;
|
||||
|
||||
uint16_t glyph_table[256] = {0};
|
||||
bool has_glyphs = false;
|
||||
|
||||
/* Now parse */
|
||||
while (get_clean_line(input, curr_line))
|
||||
{
|
||||
tokenize(curr_line, token);
|
||||
|
||||
if (token == "ENDCODEPAGE")
|
||||
{
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after ENDCODEPAGE statement: \"" + curr_line + "\"");
|
||||
break;
|
||||
}
|
||||
else if (token == "MBTABLE")
|
||||
{
|
||||
uint16_t table_size;
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, table_size);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (has_mbtable)
|
||||
error("MBTABLE can only be declared once");
|
||||
if (table_size > 256)
|
||||
error("MBTABLE size can't be larger than 256");
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after MBTABLE statement: \"" + curr_line + "\"");
|
||||
|
||||
has_mbtable = true;
|
||||
while (table_size--)
|
||||
{
|
||||
if (!get_clean_line(input, curr_line))
|
||||
error("Expected " + std::to_string(table_size + 1) + " more lines after MBTABLE token");
|
||||
|
||||
uint8_t mb;
|
||||
uint16_t wc;
|
||||
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, mb);
|
||||
tokenize(curr_line, wc);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after MBTABLE entry: \"" + curr_line + "\"");
|
||||
mb_table[mb] = wc;
|
||||
}
|
||||
}
|
||||
else if (token == "WCTABLE")
|
||||
{
|
||||
uint32_t table_size;
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, table_size);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (has_wctable)
|
||||
error("WCTABLE can only be declared once");
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after WCTABLE statement: \"" + curr_line + "\"");
|
||||
if (table_size > 65536)
|
||||
error("WCTABLE size can't be larger than 65536");
|
||||
|
||||
has_wctable = true;
|
||||
|
||||
if (FileHeader.MaximumCharacterSize == 1)
|
||||
{
|
||||
while (table_size--)
|
||||
{
|
||||
if (!get_clean_line(input, curr_line))
|
||||
error("Expected " + std::to_string(table_size + 1) + " more lines after WCTABLE token");
|
||||
|
||||
uint8_t mb;
|
||||
uint16_t wc;
|
||||
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, wc);
|
||||
tokenize(curr_line, mb);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after WCTABLE entry: \"" + curr_line + "\"");
|
||||
wc_table[wc] = mb;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t* wc_table_dbcs = reinterpret_cast<uint16_t*>(wc_table);
|
||||
while (table_size--)
|
||||
{
|
||||
if (!get_clean_line(input, curr_line))
|
||||
error("Expected " + std::to_string(table_size + 1) + " more lines after WCTABLE token");
|
||||
uint16_t mb;
|
||||
uint16_t wc;
|
||||
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, wc);
|
||||
tokenize(curr_line, mb);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after MBTABLE entry: \"" + curr_line + "\"");
|
||||
wc_table_dbcs[wc] = mb;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token == "DBCSRANGE")
|
||||
{
|
||||
if (dbcs_range_count != 0)
|
||||
error("DBCSRANGE can only be declared once");
|
||||
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, dbcs_range_count);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (dbcs_range_count > (MAXIMUM_LEADBYTES / 2))
|
||||
error("DBCSRANGE count can't exceed " + std::to_string(MAXIMUM_LEADBYTES / 2));
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after DBCSRANGE token");
|
||||
|
||||
std::size_t current_offset = 0;
|
||||
|
||||
uint16_t range_count = dbcs_range_count;
|
||||
uint16_t current_range = 0;
|
||||
while (range_count--)
|
||||
{
|
||||
if (!get_clean_line(input, curr_line))
|
||||
error("Expected new range after DBCSRANGE");
|
||||
|
||||
uint8_t RangeStart, RangeEnd;
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, RangeStart);
|
||||
tokenize(curr_line, RangeEnd);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after DBCS range declaration");
|
||||
|
||||
if (RangeStart > RangeEnd)
|
||||
error("Invalid range specified for DBCSRANGE");
|
||||
|
||||
FileHeader.LeadByte[current_range*2] = RangeStart;
|
||||
FileHeader.LeadByte[current_range*2+1] = RangeEnd;
|
||||
current_range++;
|
||||
|
||||
dbcs_table.resize(dbcs_table.size() + 256 * (RangeEnd - RangeStart + 1), FileHeader.UniDefaultChar);
|
||||
|
||||
for (uint8_t LeadByte = RangeStart; LeadByte <= RangeEnd; LeadByte++)
|
||||
{
|
||||
if (!get_clean_line(input, curr_line))
|
||||
error("Expected new DBCSTABLE after DBCS range declaration");
|
||||
|
||||
tokenize(curr_line, token);
|
||||
if (token != "DBCSTABLE")
|
||||
error("Expected new DBCSTABLE after DBCS range declaration");
|
||||
|
||||
uint16_t table_size;
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, table_size);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (table_size > 256)
|
||||
error("DBCSTABLE can't have more than 256 entries");
|
||||
while (table_size--)
|
||||
{
|
||||
if (!get_clean_line(input, curr_line))
|
||||
error("Expected " + std::to_string(table_size + 1) + " more lines after DBCSTABLE token");
|
||||
|
||||
uint8_t mb;
|
||||
uint16_t wc;
|
||||
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, mb);
|
||||
tokenize(curr_line, wc);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after DBCSTABLE entry: \"" + curr_line + "\"");
|
||||
|
||||
dbcs_table[current_offset + mb] = wc;
|
||||
}
|
||||
current_offset += 256;
|
||||
/* Offsets start at 256 for the offset table. */
|
||||
lb_offsets[LeadByte] = current_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token == "GLYPHTABLE")
|
||||
{
|
||||
uint16_t table_size;
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, table_size);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (has_glyphs)
|
||||
error("GLYPHTABLE can only be declared once");
|
||||
if (table_size > 256)
|
||||
error("GLYPHTABLE size can't be larger than 256");
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after GLYPHTABLE statement: \"" + curr_line + "\"");
|
||||
has_glyphs = true;
|
||||
|
||||
while (table_size--)
|
||||
{
|
||||
if (!get_clean_line(input, curr_line))
|
||||
error("Expected " + std::to_string(table_size + 1) + " more lines after GLYPHTABLE token");
|
||||
|
||||
uint8_t mb;
|
||||
uint16_t wc;
|
||||
|
||||
try
|
||||
{
|
||||
tokenize(curr_line, mb);
|
||||
tokenize(curr_line, wc);
|
||||
}
|
||||
catch(const std::invalid_argument& ia)
|
||||
{
|
||||
error(ia.what());
|
||||
}
|
||||
if (!curr_line.empty())
|
||||
error("Garbage after GLYPHTABLE entry: \"" + curr_line + "\"");
|
||||
glyph_table[mb] = wc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Unexpected token \"" + token + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (token != "ENDCODEPAGE")
|
||||
error("Expected last token to be \"ENDCODEPAGE\"");
|
||||
|
||||
input.close();
|
||||
|
||||
/* Ensure this is minimally workable */
|
||||
if (!has_mbtable)
|
||||
error("File has no MBTABLE statement");
|
||||
if (!has_wctable)
|
||||
error("File has no WCTABLE statement");
|
||||
|
||||
/* Glyph table fixup */
|
||||
if (has_glyphs)
|
||||
{
|
||||
for(int i = 0; i < 256; i++)
|
||||
{
|
||||
if (glyph_table[i] == 0)
|
||||
glyph_table[i] = mb_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Translated default char fixup */
|
||||
if (FileHeader.MaximumCharacterSize == 1)
|
||||
{
|
||||
FileHeader.TransDefaultChar = mb_table[FileHeader.DefaultChar];
|
||||
FileHeader.TransUniDefaultChar = wc_table[FileHeader.UniDefaultChar];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FileHeader.DefaultChar > 0xFF)
|
||||
{
|
||||
uint16_t offset = lb_offsets[FileHeader.DefaultChar >> 8];
|
||||
if (!offset)
|
||||
error("Default MB char is not translatable!");
|
||||
FileHeader.TransDefaultChar = dbcs_table[(FileHeader.DefaultChar & 0xFF) + (offset - 256)];
|
||||
}
|
||||
else
|
||||
{
|
||||
FileHeader.TransDefaultChar = mb_table[FileHeader.DefaultChar];
|
||||
}
|
||||
uint16_t* wc_table_dbcs = reinterpret_cast<uint16_t*>(wc_table);
|
||||
FileHeader.TransUniDefaultChar = wc_table_dbcs[FileHeader.UniDefaultChar];
|
||||
}
|
||||
FileHeader.HeaderSize = sizeof(NLS_FILE_HEADER) / sizeof(uint16_t);
|
||||
|
||||
std::ofstream output(argv[2], std::ios_base::binary);
|
||||
|
||||
output.write(reinterpret_cast<char*>(&FileHeader), sizeof(FileHeader));
|
||||
|
||||
uint16_t wc_table_offset = sizeof(mb_table) / sizeof(uint16_t)
|
||||
+ 1 /* size of glyph table */
|
||||
+ (has_glyphs ? 256 : 0) /* Glyph table */
|
||||
+ 1 /* Number of DBCS LeadByte ranges */
|
||||
+ (dbcs_range_count ? 256 : 0) /* offsets of lead byte sub tables */
|
||||
+ dbcs_table.size() /* LeadByte sub tables */
|
||||
+ 1; /* Unknown flag */
|
||||
|
||||
output.write(reinterpret_cast<char*>(&wc_table_offset), sizeof(wc_table_offset));
|
||||
|
||||
output.write(reinterpret_cast<char*>(mb_table), sizeof(mb_table));
|
||||
|
||||
uint16_t glyph_table_size = has_glyphs ? 256 : 0;
|
||||
output.write(reinterpret_cast<char*>(&glyph_table_size), sizeof(glyph_table_size));
|
||||
if (has_glyphs)
|
||||
output.write(reinterpret_cast<char*>(glyph_table), sizeof(glyph_table));
|
||||
|
||||
output.write(reinterpret_cast<char*>(&dbcs_range_count), sizeof(dbcs_range_count));
|
||||
if (dbcs_range_count)
|
||||
{
|
||||
output.write(reinterpret_cast<char*>(lb_offsets), sizeof(lb_offsets));
|
||||
}
|
||||
if (dbcs_table.size())
|
||||
{
|
||||
output.write(reinterpret_cast<char*>(dbcs_table.data()), dbcs_table.size() * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
uint16_t unknown_flag = FileHeader.MaximumCharacterSize == 1 ? 0 : 4;
|
||||
output.write(reinterpret_cast<char*>(&unknown_flag), sizeof(unknown_flag));
|
||||
|
||||
output.write(reinterpret_cast<char*>(wc_table), 65536 * FileHeader.MaximumCharacterSize);
|
||||
|
||||
output.close();
|
||||
delete wc_table;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
/*
|
||||
* PROJECT: ReactOS TXT to NLS Converter
|
||||
* LICENSE: GNU General Public License Version 2.0 or any later version
|
||||
* FILE: devutils/txt2nls/nls.c
|
||||
* COPYRIGHT: Copyright 2016 Dmitry Chapyshev <dmitry@reactos.org>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#define _NLS_DEBUG_PRINT
|
||||
|
||||
#ifdef _NLS_DEBUG_PRINT
|
||||
|
||||
static void
|
||||
nls_print_header(NLS_FILE_HEADER *header)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
printf("HEADER:\n");
|
||||
printf("CodePage: %u\n", header->CodePage);
|
||||
printf("Character size: %u\n", header->MaximumCharacterSize);
|
||||
printf("Default char: 0x%02X\n", header->DefaultChar);
|
||||
printf("Default unicode char: 0x%04X\n", header->UniDefaultChar);
|
||||
printf("Trans default char: 0x%02X\n", header->TransUniDefaultChar);
|
||||
printf("Trans default unicode char: 0x%04X\n", header->TransUniDefaultChar);
|
||||
|
||||
for (i = 0; i < MAXIMUM_LEADBYTES; i++)
|
||||
{
|
||||
printf("LeadByte[%u] = 0x%02X\n", i, header->LeadByte[i]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
nls_print_mb_table(uint16_t *mb_table, uint16_t uni_default_char)
|
||||
{
|
||||
uint32_t ch;
|
||||
|
||||
printf("MBTABLE:\n");
|
||||
|
||||
for (ch = 0; ch <= 0xFF; ch++)
|
||||
{
|
||||
if (mb_table[ch] != uni_default_char)
|
||||
{
|
||||
printf("0x%02X 0x%04X\n", (unsigned int)ch, (unsigned int)mb_table[ch]);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
nls_print_wc_table(uint16_t *wc_table, uint16_t default_char, int is_dbcs)
|
||||
{
|
||||
uint32_t ch;
|
||||
|
||||
printf("WCTABLE:\n");
|
||||
|
||||
for (ch = 0; ch <= 0xFFFF; ch++)
|
||||
{
|
||||
/* DBCS code page */
|
||||
if (is_dbcs)
|
||||
{
|
||||
uint16_t *table = (uint16_t*)wc_table;
|
||||
|
||||
if (table[ch] != default_char)
|
||||
printf("0x%04X 0x%04X\n", (unsigned int)ch, (unsigned int)table[ch]);
|
||||
}
|
||||
/* SBCS code page */
|
||||
else
|
||||
{
|
||||
uint8_t *table = (uint8_t*)wc_table;
|
||||
|
||||
if (table[ch] != default_char)
|
||||
printf("0x%04X 0x%02X\n", (unsigned int)ch, (unsigned int)table[ch]);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
nls_print_glyph_table(uint16_t *glyph_table, uint16_t uni_default_char)
|
||||
{
|
||||
uint32_t ch;
|
||||
|
||||
printf("GLYPHTABLE:\n");
|
||||
|
||||
for (ch = 0; ch <= 0xFF; ch++)
|
||||
{
|
||||
if (glyph_table[ch] != uni_default_char)
|
||||
{
|
||||
printf("0x%02X 0x%04X\n", (unsigned int)ch, (unsigned int)glyph_table[ch]);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#endif /* _NLS_DEBUG_PRINT */
|
||||
|
||||
int
|
||||
nls_from_txt(const char *txt_file_path, const char *nls_file_path)
|
||||
{
|
||||
NLS_FILE_HEADER header;
|
||||
FILE *file = NULL;
|
||||
uint16_t *mb_table = NULL;
|
||||
uint16_t *wc_table = NULL;
|
||||
uint16_t *glyph_table = NULL;
|
||||
uint16_t number_of_lb_ranges;
|
||||
uint16_t size;
|
||||
int is_dbcs;
|
||||
int res = 0;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
|
||||
if (!txt_get_header(txt_file_path, &header))
|
||||
goto Cleanup;
|
||||
|
||||
is_dbcs = (header.MaximumCharacterSize == 2) ? 1 : 0;
|
||||
|
||||
mb_table = txt_get_mb_table(txt_file_path, header.UniDefaultChar);
|
||||
if (!mb_table)
|
||||
goto Cleanup;
|
||||
|
||||
wc_table = txt_get_wc_table(txt_file_path, header.DefaultChar, is_dbcs);
|
||||
if (!wc_table)
|
||||
goto Cleanup;
|
||||
|
||||
/* GLYPHTABLE optionally. We do not leave if it is absent */
|
||||
glyph_table = txt_get_glyph_table(txt_file_path, header.UniDefaultChar);
|
||||
|
||||
if (is_dbcs)
|
||||
{
|
||||
/* DBCS codepage */
|
||||
uint16_t *table = (uint16_t*)wc_table;
|
||||
header.TransUniDefaultChar = table[header.UniDefaultChar];
|
||||
/* TODO: TransDefaultChar for DBCS codepages */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* SBCS codepage */
|
||||
uint8_t *table = (uint8_t*)wc_table;
|
||||
header.TransUniDefaultChar = table[header.UniDefaultChar];
|
||||
header.TransDefaultChar = mb_table[LOBYTE(header.DefaultChar)];
|
||||
}
|
||||
|
||||
#ifdef _NLS_DEBUG_PRINT
|
||||
nls_print_header(&header);
|
||||
nls_print_mb_table(mb_table, header.UniDefaultChar);
|
||||
if (glyph_table)
|
||||
nls_print_glyph_table(glyph_table, header.UniDefaultChar);
|
||||
nls_print_wc_table(wc_table, header.DefaultChar, is_dbcs);
|
||||
#endif /* _NLS_DEBUG_PRINT */
|
||||
|
||||
/* Create binary file with write access */
|
||||
file = fopen(nls_file_path, "wb");
|
||||
if (!file)
|
||||
{
|
||||
printf("Unable to create NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Write NLS file header */
|
||||
if (fwrite(&header, 1, sizeof(header), file) != sizeof(header))
|
||||
{
|
||||
printf("Unable to write NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
size = (256 * sizeof(uint16_t)) + /* Primary CP to Unicode table */
|
||||
sizeof(uint16_t) + /* optional OEM glyph table size in words */
|
||||
(glyph_table ? (256 * sizeof(uint16_t)) : 0) + /* OEM glyph table size in words * sizeof(uint16_t) */
|
||||
sizeof(uint16_t) + /* Number of DBCS LeadByte ranges */
|
||||
0 + /* offsets of lead byte sub tables */
|
||||
0 + /* LeadByte sub tables */
|
||||
sizeof(uint16_t); /* Unknown flag */
|
||||
|
||||
size /= sizeof(uint16_t);
|
||||
|
||||
if (fwrite(&size, 1, sizeof(size), file) != sizeof(size))
|
||||
{
|
||||
printf("Unable to write NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Write multibyte table */
|
||||
if (fwrite(mb_table, 1, (256 * sizeof(uint16_t)), file) != (256 * sizeof(uint16_t)))
|
||||
{
|
||||
printf("Unable to write NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* OEM glyph table size in words */
|
||||
size = (glyph_table ? 256 : 0);
|
||||
|
||||
if (fwrite(&size, 1, sizeof(size), file) != sizeof(size))
|
||||
{
|
||||
printf("Unable to write NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
if (glyph_table)
|
||||
{
|
||||
/* Write OEM glyph table */
|
||||
if (fwrite(glyph_table, 1, (256 * sizeof(uint16_t)), file) != (256 * sizeof(uint16_t)))
|
||||
{
|
||||
printf("Unable to write NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Number of DBCS LeadByte ranges */
|
||||
number_of_lb_ranges = 0;
|
||||
if (fwrite(&number_of_lb_ranges, 1, sizeof(number_of_lb_ranges), file) != sizeof(number_of_lb_ranges))
|
||||
{
|
||||
printf("Unable to write NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Unknown flag */
|
||||
size = 0;
|
||||
if (fwrite(&size, 1, sizeof(size), file) != sizeof(size))
|
||||
{
|
||||
printf("Unable to write NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Write wide char table */
|
||||
if (fwrite(wc_table, 1, (65536 * header.MaximumCharacterSize), file) != (65536 * header.MaximumCharacterSize))
|
||||
{
|
||||
printf("Unable to write NLS file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
res = 1;
|
||||
|
||||
Cleanup:
|
||||
if (file) fclose(file);
|
||||
free(mb_table);
|
||||
free(wc_table);
|
||||
free(glyph_table);
|
||||
|
||||
return res;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* PROJECT: ReactOS TXT to NLS Converter
|
||||
* LICENSE: GNU General Public License Version 2.0 or any later version
|
||||
* FILE: devutils/txt2nls/precomp.h
|
||||
* COPYRIGHT: Copyright 2016 Dmitry Chapyshev <dmitry@reactos.org>
|
||||
*/
|
||||
|
||||
#ifndef __PRECOMP_H
|
||||
#define __PRECOMP_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <memory.h>
|
||||
|
||||
#define LOBYTE(w) ((uint8_t)((uint32_t)(w) & 0xff))
|
||||
|
||||
#define MAXIMUM_LEADBYTES 12
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t HeaderSize;
|
||||
uint16_t CodePage;
|
||||
uint16_t MaximumCharacterSize;
|
||||
uint16_t DefaultChar;
|
||||
uint16_t UniDefaultChar;
|
||||
uint16_t TransDefaultChar;
|
||||
uint16_t TransUniDefaultChar;
|
||||
uint8_t LeadByte[MAXIMUM_LEADBYTES];
|
||||
} NLS_FILE_HEADER;
|
||||
|
||||
/* nls.c */
|
||||
int
|
||||
nls_from_txt(const char *txt_file_path, const char *nls_file_path);
|
||||
|
||||
/* bestfit.c */
|
||||
int
|
||||
txt_get_header(const char *file_path, NLS_FILE_HEADER *header);
|
||||
|
||||
uint16_t*
|
||||
txt_get_mb_table(const char *file_path, uint16_t uni_default_char);
|
||||
|
||||
uint16_t*
|
||||
txt_get_wc_table(const char *file_path, uint16_t default_char, int is_dbcs);
|
||||
|
||||
uint16_t*
|
||||
txt_get_glyph_table(const char *file_path, uint16_t uni_default_char);
|
||||
|
||||
#endif
|
|
@ -1,497 +0,0 @@
|
|||
/*
|
||||
* PROJECT: ReactOS TXT to NLS Converter
|
||||
* LICENSE: GNU General Public License Version 2.0 or any later version
|
||||
* FILE: devutils/txt2nls/txt.c
|
||||
* COPYRIGHT: Copyright 2016 Dmitry Chapyshev <dmitry@reactos.org>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
int
|
||||
txt_get_header(const char *file_path, NLS_FILE_HEADER *header)
|
||||
{
|
||||
FILE *file;
|
||||
char *p;
|
||||
char buf[256];
|
||||
uint32_t line = 0;
|
||||
int res = 0;
|
||||
int found;
|
||||
uint32_t val;
|
||||
|
||||
file = fopen(file_path, "r");
|
||||
if (!file)
|
||||
{
|
||||
printf("Unable to read TXT file.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find CODEPAGE entry */
|
||||
found = 0;
|
||||
while (fgets(buf, sizeof(buf), file))
|
||||
{
|
||||
++line;
|
||||
|
||||
p = strstr(buf, "CODEPAGE");
|
||||
if (p)
|
||||
{
|
||||
/* Length of CODEPAGE string is 8 chars */
|
||||
p += 8;
|
||||
|
||||
/* Skip all spaces after CODEPAGE */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
/* Convert string to uint32_t */
|
||||
val = strtoul(p, &p, 10);
|
||||
|
||||
/* Validate codepage value */
|
||||
if (val > 0xFFFF)
|
||||
{
|
||||
printf("Wrong codepage: %u (line: %u)\n", val, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
header->CodePage = (uint16_t)val;
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
printf("CODEPAGE not found.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Find CPINFO entry */
|
||||
found = 0;
|
||||
while (fgets(buf, sizeof(buf), file))
|
||||
{
|
||||
++line;
|
||||
|
||||
p = strstr(buf, "CPINFO");
|
||||
if (p)
|
||||
{
|
||||
/* Length of CPINFO string is 6 chars */
|
||||
p += 6;
|
||||
|
||||
/* Skip all spaces after CPINFO */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
/* Convert string to uint32_t */
|
||||
val = strtoul(p, &p, 10);
|
||||
|
||||
/* Validate value */
|
||||
if (val != 1 && val != 2)
|
||||
{
|
||||
printf("Wrong character size: %u (line: %u)\n", val, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
header->MaximumCharacterSize = (uint16_t)val;
|
||||
|
||||
/* Skip all spaces after character size */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
/* Convert string to uint32_t */
|
||||
val = strtoul(p, &p, 16);
|
||||
header->DefaultChar = (uint16_t)val;
|
||||
/* By default set value as DefaultChar */
|
||||
header->TransDefaultChar = (uint16_t)val;
|
||||
|
||||
/* Skip all spaces after default char */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
/* Convert string to uint32_t */
|
||||
val = strtoul(p, &p, 16);
|
||||
header->UniDefaultChar = (uint16_t)val;
|
||||
/* By default set value as UniDefaultChar */
|
||||
header->TransUniDefaultChar = (uint16_t)val;
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
printf("CPINFO not found.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
header->HeaderSize = sizeof(NLS_FILE_HEADER) / sizeof(uint16_t);
|
||||
|
||||
res = 1;
|
||||
|
||||
Cleanup:
|
||||
fclose(file);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint16_t*
|
||||
txt_get_mb_table(const char *file_path, uint16_t uni_default_char)
|
||||
{
|
||||
uint16_t *table;
|
||||
char buf[256];
|
||||
char *p;
|
||||
uint32_t count = 0;
|
||||
uint32_t index;
|
||||
uint32_t line = 0;
|
||||
int found;
|
||||
int res = 0;
|
||||
FILE *file;
|
||||
|
||||
table = malloc(256 * sizeof(uint16_t));
|
||||
if (!table)
|
||||
{
|
||||
printf("Memory allocation failure\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set default value for all table items */
|
||||
for (index = 0; index <= 255; index++)
|
||||
table[index] = uni_default_char;
|
||||
|
||||
file = fopen(file_path, "r");
|
||||
if (!file)
|
||||
{
|
||||
printf("Unable to read TXT file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Find MBTABLE entry */
|
||||
found = 0;
|
||||
while (fgets(buf, sizeof(buf), file))
|
||||
{
|
||||
++line;
|
||||
|
||||
p = strstr(buf, "MBTABLE");
|
||||
if (p)
|
||||
{
|
||||
p += 7;
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
count = strtoul(p, &p, 10);
|
||||
if (count == 0 || count > 256)
|
||||
{
|
||||
printf("Wrong MBTABLE size: %u (line: %u)\n", count, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
printf("MBTABLE not found.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Parse next line */
|
||||
while (fgets(buf, sizeof(buf), file) && count)
|
||||
{
|
||||
uint32_t cp_char;
|
||||
uint32_t uni_char;
|
||||
|
||||
++line;
|
||||
|
||||
p = buf;
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
if (!*p || p[0] == ';')
|
||||
continue;
|
||||
|
||||
cp_char = strtoul(p, &p, 16);
|
||||
if (cp_char > 0xFF)
|
||||
{
|
||||
printf("Wrong char value: %u (line: %u)\n", cp_char, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
uni_char = strtoul(p, &p, 16);
|
||||
if (uni_char > 0xFFFF)
|
||||
{
|
||||
printf("Wrong unicode char value: %u (line: %u)\n", uni_char, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
table[cp_char] = uni_char;
|
||||
--count;
|
||||
}
|
||||
|
||||
res = 1;
|
||||
|
||||
Cleanup:
|
||||
if (!res)
|
||||
{
|
||||
free(table);
|
||||
table = NULL;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
uint16_t*
|
||||
txt_get_wc_table(const char *file_path, uint16_t default_char, int is_dbcs)
|
||||
{
|
||||
char buf[256];
|
||||
char *p;
|
||||
uint16_t *table;
|
||||
uint32_t index;
|
||||
uint32_t count = 0;
|
||||
uint32_t line = 0;
|
||||
int res = 0;
|
||||
int found;
|
||||
FILE *file;
|
||||
|
||||
table = malloc(65536 * (is_dbcs ? sizeof(uint16_t) : sizeof(uint8_t)));
|
||||
if (!table)
|
||||
{
|
||||
printf("Memory allocation failure\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set default value for all table items */
|
||||
for (index = 0; index <= 65535; index++)
|
||||
{
|
||||
/* DBCS code page */
|
||||
if (is_dbcs)
|
||||
{
|
||||
uint16_t *tmp = (uint16_t*)table;
|
||||
tmp[index] = default_char;
|
||||
}
|
||||
/* SBCS code page */
|
||||
else
|
||||
{
|
||||
uint8_t *tmp = (uint8_t*)table;
|
||||
tmp[index] = default_char;
|
||||
}
|
||||
}
|
||||
|
||||
file = fopen(file_path, "r");
|
||||
if (!file)
|
||||
{
|
||||
printf("Unable to read TXT file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Find WCTABLE entry */
|
||||
found = 0;
|
||||
while (fgets(buf, sizeof(buf), file))
|
||||
{
|
||||
++line;
|
||||
|
||||
p = strstr(buf, "WCTABLE");
|
||||
if (p)
|
||||
{
|
||||
p += 7;
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
count = strtoul(p, &p, 10);
|
||||
if (count == 0 || count > 65536)
|
||||
{
|
||||
printf("Wrong WCTABLE size: %u (line: %u)\n", count, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
printf("WCTABLE not found.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Parse next line */
|
||||
while (fgets(buf, sizeof(buf), file) && count)
|
||||
{
|
||||
uint32_t cp_char;
|
||||
uint32_t uni_char;
|
||||
|
||||
++line;
|
||||
|
||||
p = buf;
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
if (!*p || p[0] == ';')
|
||||
continue;
|
||||
|
||||
uni_char = strtoul(p, &p, 16);
|
||||
if (uni_char > 0xFFFF)
|
||||
{
|
||||
printf("Wrong unicode char value: %u (line: %u)\n", uni_char, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
cp_char = strtoul(p, &p, 16);
|
||||
if ((is_dbcs && cp_char > 0xFFFF) || (!is_dbcs && cp_char > 0xFF))
|
||||
{
|
||||
printf("Wrong char value: %u (line: %u)\n", cp_char, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* DBCS code page */
|
||||
if (is_dbcs)
|
||||
{
|
||||
uint16_t *tmp = (uint16_t*)table;
|
||||
tmp[uni_char] = cp_char;
|
||||
}
|
||||
/* SBCS code page */
|
||||
else
|
||||
{
|
||||
uint8_t *tmp = (uint8_t*)table;
|
||||
tmp[uni_char] = cp_char;
|
||||
}
|
||||
|
||||
--count;
|
||||
}
|
||||
|
||||
res = 1;
|
||||
|
||||
Cleanup:
|
||||
if (!res)
|
||||
{
|
||||
free(table);
|
||||
table = NULL;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
uint16_t*
|
||||
txt_get_glyph_table(const char *file_path, uint16_t uni_default_char)
|
||||
{
|
||||
uint16_t *table;
|
||||
char buf[256];
|
||||
char *p;
|
||||
uint32_t count = 0;
|
||||
uint32_t index;
|
||||
uint32_t line = 0;
|
||||
int found;
|
||||
int res = 0;
|
||||
FILE *file;
|
||||
|
||||
table = malloc(256 * sizeof(uint16_t));
|
||||
if (!table)
|
||||
{
|
||||
printf("Memory allocation failure\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set default value for all table items */
|
||||
for (index = 0; index <= 255; index++)
|
||||
table[index] = uni_default_char;
|
||||
|
||||
file = fopen(file_path, "r");
|
||||
if (!file)
|
||||
{
|
||||
printf("Unable to read TXT file.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Find GLYPHTABLE entry */
|
||||
found = 0;
|
||||
while (fgets(buf, sizeof(buf), file))
|
||||
{
|
||||
++line;
|
||||
|
||||
p = strstr(buf, "GLYPHTABLE");
|
||||
if (p)
|
||||
{
|
||||
p += 10;
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
count = strtoul(p, &p, 10);
|
||||
if (count == 0 || count > 256)
|
||||
{
|
||||
printf("Wrong GLYPHTABLE size: %u (line: %u)\n", count, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
printf("GLYPHTABLE not found.\n");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Parse next line */
|
||||
while (fgets(buf, sizeof(buf), file) && count)
|
||||
{
|
||||
uint32_t cp_char;
|
||||
uint32_t uni_char;
|
||||
|
||||
++line;
|
||||
|
||||
p = buf;
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
if (!*p || p[0] == ';')
|
||||
continue;
|
||||
|
||||
cp_char = strtoul(p, &p, 16);
|
||||
if (cp_char > 0xFF)
|
||||
{
|
||||
printf("Wrong char value: %u (line: %u)\n", cp_char, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
/* Skip spaces */
|
||||
while (isspace(*p)) ++p;
|
||||
|
||||
uni_char = strtoul(p, &p, 16);
|
||||
if (uni_char > 0xFFFF)
|
||||
{
|
||||
printf("Wrong unicode char value: %u (line: %u)\n", uni_char, line);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
table[cp_char] = uni_char;
|
||||
--count;
|
||||
}
|
||||
|
||||
res = 1;
|
||||
|
||||
Cleanup:
|
||||
if (!res)
|
||||
{
|
||||
free(table);
|
||||
table = NULL;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return table;
|
||||
}
|
Loading…
Reference in a new issue