1
0
forked from me/IronOS

Merge branch 'master' into messing_with_pd

This commit is contained in:
Ben V. Brown
2021-04-17 10:53:58 +10:00
committed by GitHub
42 changed files with 2316 additions and 1134 deletions

View File

@@ -141,10 +141,36 @@
} }
} }
} }
stringFromMatrix(true, false);
} }
function stringFromMatrix() { function escapedToMatrix(str) {
app.encodedEscapeSequence = str;
clearMatrix();
var strs = str.split("\\x");
var c = 0;
var rs = 7;
for (var i = 0; i<strs.length; i++) {
var d = strs[i];
if (d.length > 0) {
v = parseInt(d, 16);
sv = padLeft(v.toString(2), "0", 8);
for (r = 0; r < 8; r++) {
paint(getCell(rs - r, c), sv.charAt(r) == '1');
}
c++;
if (c >= app.matrix.cols) {
c = 0;
rs += 8;
}
}
}
stringFromMatrix(false, true);
}
function stringFromMatrix(skipEncodedData, skipEncodedEscapeSequence) {
var str = ""; var str = "";
var strEscaped = "";
var delim = ""; var delim = "";
var blocks = app.matrix.rows / 8; var blocks = app.matrix.rows / 8;
var rs = 7; var rs = 7;
@@ -158,11 +184,17 @@
} }
} }
str += delim + "0x" + padLeft(b.toString(16).toUpperCase(), "0", 2); str += delim + "0x" + padLeft(b.toString(16).toUpperCase(), "0", 2);
strEscaped += "\\x" + padLeft(b.toString(16).toUpperCase(), "0", 2);
delim = ","; delim = ",";
} }
rs += 8; rs += 8;
} }
app.encodedData = str; if (!skipEncodedData) {
app.encodedData = str;
}
if (!skipEncodedEscapeSequence) {
app.encodedEscapeSequence = strEscaped;
}
return str; return str;
} }
@@ -175,12 +207,16 @@
rows: 16 rows: 16
}, },
type: "big", type: "big",
encodedData: "" encodedData: "",
encodedEscapeSequence: "",
}, },
methods : { methods : {
VtoMatrix : function(val) { VtoMatrix : function(val) {
toMatrix(val); toMatrix(val);
}, },
escapedToMatrix : function(val) {
escapedToMatrix(val);
},
VchangeSize : function() { VchangeSize : function() {
if (app.type == "big") { if (app.type == "big") {
@@ -236,7 +272,8 @@
<input type="button" value="Clear" onclick="clearMatrix();stringFromMatrix()"> <input type="button" value="Clear" onclick="clearMatrix();stringFromMatrix()">
</div> </div>
<div class="data"> <div class="data">
<textarea v-model="encodedData" style="width:100%" v-on:change="VtoMatrix(encodedData)" rows=5> <textarea v-model="encodedData" style="width:100%" v-on:change="VtoMatrix(encodedData)" rows=5></textarea>
<textarea v-model="encodedEscapeSequence" style="width:100%" v-on:change="escapedToMatrix(encodedEscapeSequence)" rows=5></textarea>
</div> </div>
</div> </div>

View File

@@ -48,8 +48,8 @@
} else if (id == "current-lang-file") { } else if (id == "current-lang-file") {
if (checkTranslationFile(file.name)) { if (checkTranslationFile(file.name)) {
app.current = json; app.current = json;
if (!app.current.cyrillicGlyphs){ if (!app.current.fonts){
app.current.cyrillicGlyphs = false; app.current.fonts = ["ascii_basic"];
} }
app.meta.currentLoaded = true; app.meta.currentLoaded = true;
} }
@@ -137,6 +137,7 @@
loaded: false, loaded: false,
}, },
obsolete : {}, obsolete : {},
fontToAdd: "latin_extended",
}, },
methods : { methods : {
validateInput: function(valMap, id, mode) { validateInput: function(valMap, id, mode) {
@@ -246,7 +247,15 @@
} else { } else {
valMap[id] = message; valMap[id] = message;
} }
} },
removeFont: function(i) {
this.current.fonts.splice(i, 1);
},
addFont: function() {
this.current.fonts.push(this.fontToAdd);
},
} }
}); });
app.def = def; app.def = def;
@@ -289,12 +298,20 @@
<td class="value"><input type="text" v-model="current.languageLocalName" class="short"></td> <td class="value"><input type="text" v-model="current.languageLocalName" class="short"></td>
</tr> </tr>
<tr v-if="meta.currentLoaded"> <tr v-if="meta.currentLoaded">
<td class="label">Font table to use</td> <td class="label">Font tables to use<br>("ascii_basic" must be first)</td>
<td class="value"> <td class="value">
<select v-model="current.cyrillicGlyphs" v-on:change="current.cyrillicGlyphs = current.cyrillicGlyphs=='true'"> <ul>
<option value="false">Latin Extended</option> <li v-for="(font, i) in current.fonts">
<option value="true">Cyrillic Glyphs</option> <button type="button" @click="removeFont(i)" :disabled="i == 0 && font == 'ascii_basic'">-</button> {{ font }}
</li>
</ul>
<select v-model="fontToAdd">
<!-- <option value="ascii_basic">ascii_basic: ASCII Basic</option> -->
<option value="latin_extended">latin_extended: Latin Extended</option>
<option value="cyrillic">cyrillic: Cyrillic Glyphs</option>
<option value="cjk">cjk: Chinese/Japanese/Korean</option>
</select> </select>
<button type="button" @click="addFont()">Add</button>
</td> </td>
</tr> </tr>
</table> </table>

File diff suppressed because it is too large Load Diff

91
Translations/lzfx.py Normal file
View File

@@ -0,0 +1,91 @@
import ctypes
import functools
import os
from pathlib import Path
HERE = Path(__file__).resolve().parent
@functools.lru_cache(maxsize=None)
def _liblzfx():
so_path = os.path.join(HERE, "../source/Objects/host/lzfx/liblzfx.so")
liblzfx = ctypes.cdll.LoadLibrary(so_path)
return liblzfx
@functools.lru_cache(maxsize=None)
def _fn_lzfx_compress():
"""Returns the lzfx_compress C function.
::
/* Buffer-to buffer compression.
Supply pre-allocated input and output buffers via ibuf and obuf, and
their size in bytes via ilen and olen. Buffers may not overlap.
On success, the function returns a non-negative value and the argument
olen contains the compressed size in bytes. On failure, a negative
value is returned and olen is not modified.
*/
int lzfx_compress(const void* ibuf, unsigned int ilen,
void* obuf, unsigned int *olen);
"""
fn = _liblzfx().lzfx_compress
fn.argtype = [
ctypes.c_char_p,
ctypes.c_uint,
ctypes.c_char_p,
ctypes.POINTER(ctypes.c_uint),
]
fn.restype = ctypes.c_int
return fn
def compress(data: bytes) -> bytes:
"""Returns a bytes object of the lzfx-compressed data."""
fn_compress = _fn_lzfx_compress()
output_buffer_len = len(data) + 8
ibuf = data
ilen = len(ibuf)
obuf = ctypes.create_string_buffer(output_buffer_len)
olen = ctypes.c_uint(output_buffer_len)
res = fn_compress(ibuf, ilen, obuf, ctypes.byref(olen))
if res < 0:
raise LzfxError(res)
else:
return bytes(obuf[: olen.value]) # type: ignore
class LzfxError(Exception):
"""Exception raised for lzfx compression or decompression error.
Attributes:
error_code -- The source error code, which is a negative integer
error_name -- The constant name of the error
message -- explanation of the error
"""
# define LZFX_ESIZE -1 /* Output buffer too small */
# define LZFX_ECORRUPT -2 /* Invalid data for decompression */
# define LZFX_EARGS -3 /* Arguments invalid (NULL) */
def __init__(self, error_code):
self.error_code = error_code
if error_code == -1:
self.error_name = "LZFX_ESIZE"
self.message = "Output buffer too small"
elif error_code == -2:
self.error_name = "LZFX_ECORRUPT"
self.message = "Invalid data for decompression"
elif error_code == -3:
self.error_name = "LZFX_EARGS"
self.message = "Arguments invalid (NULL)"
else:
self.error_name = "UNKNOWN"
self.message = "Unknown error"

View File

@@ -5,19 +5,21 @@ import functools
import json import json
import logging import logging
import os import os
import pickle
import re import re
import subprocess import subprocess
import sys import sys
from datetime import datetime from datetime import datetime
from itertools import chain from itertools import chain
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, TextIO, Tuple, Union from typing import BinaryIO, Dict, List, Optional, TextIO, Tuple, Union
from dataclasses import dataclass from dataclasses import dataclass
from bdflib import reader as bdfreader from bdflib import reader as bdfreader
from bdflib.model import Font, Glyph from bdflib.model import Font, Glyph
import font_tables import font_tables
import lzfx
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
@@ -79,7 +81,7 @@ def write_start(f: TextIO):
f.write('#include "Translation.h"\n') f.write('#include "Translation.h"\n')
def get_constants() -> List[Tuple[str, str]]: def get_constants(build_version: str) -> List[Tuple[str, str]]:
# Extra constants that are used in the firmware that are shared across all languages # Extra constants that are used in the firmware that are shared across all languages
return [ return [
("SymbolPlus", "+"), ("SymbolPlus", "+"),
@@ -94,7 +96,7 @@ def get_constants() -> List[Tuple[str, str]]:
("SymbolVolts", "V"), ("SymbolVolts", "V"),
("SymbolDC", "DC"), ("SymbolDC", "DC"),
("SymbolCellCount", "S"), ("SymbolCellCount", "S"),
("SymbolVersionNumber", buildVersion), ("SymbolVersionNumber", build_version),
] ]
@@ -116,7 +118,7 @@ def get_debug_menu() -> List[str]:
] ]
def get_letter_counts(defs: dict, lang: dict) -> List[str]: def get_letter_counts(defs: dict, lang: dict, build_version: str) -> List[str]:
text_list = [] text_list = []
# iterate over all strings # iterate over all strings
obj = lang["menuOptions"] obj = lang["menuOptions"]
@@ -169,7 +171,7 @@ def get_letter_counts(defs: dict, lang: dict) -> List[str]:
for mod in defs["menuGroups"]: for mod in defs["menuGroups"]:
eid = mod["id"] eid = mod["id"]
text_list.append(obj[eid]["desc"]) text_list.append(obj[eid]["desc"])
constants = get_constants() constants = get_constants(build_version)
for x in constants: for x in constants:
text_list.append(x[1]) text_list.append(x[1])
text_list.extend(get_debug_menu()) text_list.extend(get_debug_menu())
@@ -191,7 +193,7 @@ def get_letter_counts(defs: dict, lang: dict) -> List[str]:
return symbols_by_occurrence return symbols_by_occurrence
def get_cjk_glyph(sym: str) -> str: def get_cjk_glyph(sym: str) -> bytes:
glyph: Glyph = cjk_font()[ord(sym)] glyph: Glyph = cjk_font()[ord(sym)]
data = glyph.data data = glyph.data
@@ -224,15 +226,15 @@ def get_cjk_glyph(sym: str) -> str:
# top-most pixel. The data goes from the left-most to the right-most column # top-most pixel. The data goes from the left-most to the right-most column
# of the top half, then from the left-most to the right-most column of the # of the top half, then from the left-most to the right-most column of the
# bottom half. # bottom half.
s = "" bs = bytearray()
for block in range(2): for block in range(2):
for c in range(dst_w): for c in range(dst_w):
b = 0 b = 0
for r in range(8): for r in range(8):
if get_cell(c, r + 8 * block): if get_cell(c, r + 8 * block):
b |= 0x01 << r b |= 0x01 << r
s += f"0x{b:02X}," bs.append(b)
return s return bytes(bs)
def get_bytes_from_font_index(index: int) -> bytes: def get_bytes_from_font_index(index: int) -> bytes:
@@ -290,90 +292,173 @@ def bytes_to_escaped(b: bytes) -> str:
return "".join((f"\\x{i:02X}" for i in b)) return "".join((f"\\x{i:02X}" for i in b))
def get_font_map_and_table(text_list: List[str]) -> Tuple[str, Dict[str, bytes]]: def bytes_to_c_hex(b: bytes) -> str:
return ", ".join((f"0x{i:02X}" for i in b)) + ","
@dataclass
class FontMap:
font12: Dict[str, bytes]
font06: Dict[str, Optional[bytes]]
@dataclass
class FontMapsPerFont:
font12_maps: Dict[str, Dict[str, bytes]]
font06_maps: Dict[str, Dict[str, Optional[bytes]]]
sym_lists: Dict[str, List[str]]
def get_font_map_per_font(text_list: List[str], fonts: List[str]) -> FontMapsPerFont:
pending_sym_set = set(text_list)
if len(pending_sym_set) != len(text_list):
raise ValueError("`text_list` contains duplicated symbols")
if fonts[0] != font_tables.NAME_ASCII_BASIC:
raise ValueError(
f'First item in `fonts` must be "{font_tables.NAME_ASCII_BASIC}"'
)
total_symbol_count = len(text_list)
# \x00 is for NULL termination and \x01 is for newline, so the maximum
# number of symbols allowed is as follow (see also the comments in
# `get_bytes_from_font_index`):
if total_symbol_count > (0x10 * 0xFF - 15) - 2: # 4063
raise ValueError(
f"Error, too many used symbols for this version (total {total_symbol_count})"
)
logging.info(f"Generating fonts for {total_symbol_count} symbols")
# Collect font bitmaps by the defined font order:
font12_maps: Dict[str, Dict[str, bytes]] = {}
font06_maps: Dict[str, Dict[str, Optional[bytes]]] = {}
sym_lists: Dict[str, List[str]] = {}
for font in fonts:
font12_maps[font] = {}
font12_map = font12_maps[font]
font06_maps[font] = {}
font06_map = font06_maps[font]
sym_lists[font] = []
sym_list = sym_lists[font]
if len(pending_sym_set) == 0:
logging.warning(
f"Font {font} not used because all symbols already have font bitmaps"
)
continue
if font == font_tables.NAME_CJK:
is_cjk = True
else:
is_cjk = False
font12: Dict[str, bytes]
font06: Dict[str, bytes]
font12, font06 = font_tables.get_font_maps_for_name(font)
for sym in text_list:
if sym not in pending_sym_set:
continue
if is_cjk:
font12_line = get_cjk_glyph(sym)
if font12_line is None:
continue
font06_line = None
else:
try:
font12_line = font12[sym]
font06_line = font06[sym]
except KeyError:
continue
font12_map[sym] = font12_line
font06_map[sym] = font06_line
sym_list.append(sym)
pending_sym_set.remove(sym)
if len(sym_list) == 0:
logging.warning(f"Font {font} not used by any symbols on the list")
if len(pending_sym_set) > 0:
raise KeyError(f"Symbols not found in specified fonts: {pending_sym_set}")
return FontMapsPerFont(font12_maps, font06_maps, sym_lists)
def get_font_map_and_table(
text_list: List[str], fonts: List[str]
) -> Tuple[List[str], FontMap, Dict[str, bytes]]:
# the text list is sorted # the text list is sorted
# allocate out these in their order as number codes # allocate out these in their order as number codes
symbol_map: Dict[str, bytes] = {"\n": bytes([1])} symbol_map: Dict[str, bytes] = {"\n": bytes([1])}
index = 2 # start at 2, as 0= null terminator,1 = new line index = 2 # start at 2, as 0= null terminator,1 = new line
forced_first_symbols = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] forced_first_symbols = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
# Get the font table, which does not include CJK chars # We enforce that numbers come first.
font_table = font_tables.get_font_map() text_list = forced_first_symbols + [
font_small_table = font_tables.get_small_font_map() x for x in text_list if x not in forced_first_symbols
# We want to put all CJK chars after non-CJK ones so that the CJK chars
# do not need to be in the small font table to save space.
# We assume all symbols not in the font table to be a CJK char.
# We also enforce that numbers are first.
ordered_normal_sym_list: List[str] = forced_first_symbols + [
x for x in text_list if x not in forced_first_symbols and x in font_table
]
ordered_cjk_sym_list: List[str] = [
x for x in text_list if x not in forced_first_symbols and x not in font_table
] ]
total_symbol_count = len(ordered_normal_sym_list) + len(ordered_cjk_sym_list) font_maps = get_font_map_per_font(text_list, fonts)
# \x00 is for NULL termination and \x01 is for newline, so the maximum font12_maps = font_maps.font12_maps
# number of symbols allowed is as follow (see also the comments in font06_maps = font_maps.font06_maps
# `get_bytes_from_font_index`):
if total_symbol_count > (0x10 * 0xFF - 15) - 2: # 4063
logging.error(
f"Error, too many used symbols for this version (total {total_symbol_count})"
)
sys.exit(1)
logging.info(f"Generating fonts for {total_symbol_count} symbols") # Build the full font maps
font12_map = {}
font06_map = {}
for font in fonts:
font12_map.update(font12_maps[font])
font06_map.update(font06_maps[font])
for sym in chain(ordered_normal_sym_list, ordered_cjk_sym_list): # Collect all symbols by the original symbol order, but also making sure
if sym in symbol_map: # all symbols with only large font must be placed after all symbols with
raise ValueError("Symbol not found in symbol map") # both small and large fonts
sym_list_both_fonts = []
sym_list_large_only = []
for sym in text_list:
if font06_map[sym] is None:
sym_list_large_only.append(sym)
else:
sym_list_both_fonts.append(sym)
sym_list = sym_list_both_fonts + sym_list_large_only
# Assign symbol bytes by font index
for index, sym in enumerate(sym_list, index):
assert sym not in symbol_map
symbol_map[sym] = get_bytes_from_font_index(index) symbol_map[sym] = get_bytes_from_font_index(index)
index += 1
font_table_strings = [] return sym_list, FontMap(font12_map, font06_map), symbol_map
font_small_table_strings = []
for sym in ordered_normal_sym_list:
if sym not in font_table:
logging.error(f"Missing Large font element for {sym}")
sys.exit(1)
font_line: str = font_table[sym]
font_table_strings.append(
f"{font_line}//{bytes_to_escaped(symbol_map[sym])} -> {sym}"
)
if sym not in font_small_table:
logging.error(f"Missing Small font element for {sym}")
sys.exit(1)
font_line = font_small_table[sym]
font_small_table_strings.append(
f"{font_line}//{bytes_to_escaped(symbol_map[sym])} -> {sym}"
)
for sym in ordered_cjk_sym_list:
if sym in font_table:
raise ValueError("Symbol already exists in font_table")
font_line = get_cjk_glyph(sym)
if font_line is None:
logging.error(f"Missing Large font element for {sym}")
sys.exit(1)
font_table_strings.append(
f"{font_line}//{bytes_to_escaped(symbol_map[sym])} -> {sym}"
)
# No data to add to the small font table
font_small_table_strings.append(
f"// {bytes_to_escaped(symbol_map[sym])} -> {sym}"
)
def make_font_table_cpp(
sym_list: List[str], font_map: FontMap, symbol_map: Dict[str, bytes]
) -> str:
output_table = make_font_table_12_cpp(sym_list, font_map, symbol_map)
output_table += make_font_table_06_cpp(sym_list, font_map, symbol_map)
return output_table
def make_font_table_12_cpp(
sym_list: List[str], font_map: FontMap, symbol_map: Dict[str, bytes]
) -> str:
output_table = "const uint8_t USER_FONT_12[] = {\n" output_table = "const uint8_t USER_FONT_12[] = {\n"
for line in font_table_strings: for sym in sym_list:
# join font table int one large string output_table += f"{bytes_to_c_hex(font_map.font12[sym])}//{bytes_to_escaped(symbol_map[sym])} -> {sym}\n"
output_table += line + "\n"
output_table += "};\n" output_table += "};\n"
output_table += "const uint8_t USER_FONT_6x8[] = {\n" return output_table
for line in font_small_table_strings:
# join font table int one large string
output_table += line + "\n" def make_font_table_06_cpp(
sym_list: List[str], font_map: FontMap, symbol_map: Dict[str, bytes]
) -> str:
output_table = "const uint8_t USER_FONT_6x8[] = {\n"
for sym in sym_list:
font_bytes = font_map.font06[sym]
if font_bytes:
font_line = bytes_to_c_hex(font_bytes)
else:
font_line = "// " # placeholder
output_table += f"{font_line}//{bytes_to_escaped(symbol_map[sym])} -> {sym}\n"
output_table += "};\n" output_table += "};\n"
return output_table, symbol_map return output_table
def convert_string_bytes(symbol_conversion_table: Dict[str, bytes], text: str) -> bytes: def convert_string_bytes(symbol_conversion_table: Dict[str, bytes], text: str) -> bytes:
@@ -397,29 +482,178 @@ def escape(string: str) -> str:
return json.dumps(string, ensure_ascii=False) return json.dumps(string, ensure_ascii=False)
def write_bytes_as_c_array(
f: TextIO, name: str, data: bytes, indent: int = 2, bytes_per_line: int = 16
) -> None:
f.write(f"const uint8_t {name}[] = {{\n")
for i in range(0, len(data), bytes_per_line):
f.write(" " * indent)
f.write(", ".join((f"0x{b:02X}" for b in data[i : i + bytes_per_line])))
f.write(",\n")
f.write(f"}}; // {name}\n\n")
@dataclass @dataclass
class TranslationItem: class LanguageData:
info: str lang: dict
str_index: int defs: dict
build_version: str
sym_list: List[str]
font_map: FontMap
symbol_conversion_table: Dict[str, bytes]
def write_language(lang: dict, defs: dict, f: TextIO) -> None: def prepare_language(lang: dict, defs: dict, build_version: str) -> LanguageData:
language_code: str = lang["languageCode"]
logging.info(f"Preparing language data for {language_code}")
# Iterate over all of the text to build up the symbols & counts
text_list = get_letter_counts(defs, lang, build_version)
# From the letter counts, need to make a symbol translator & write out the font
fonts = lang["fonts"]
sym_list, font_map, symbol_conversion_table = get_font_map_and_table(
text_list, fonts
)
return LanguageData(
lang, defs, build_version, sym_list, font_map, symbol_conversion_table
)
def write_language(
data: LanguageData,
f: TextIO,
strings_bin: Optional[bytes] = None,
compress_font: bool = False,
) -> None:
lang = data.lang
defs = data.defs
build_version = data.build_version
sym_list = data.sym_list
font_map = data.font_map
symbol_conversion_table = data.symbol_conversion_table
language_code: str = lang["languageCode"] language_code: str = lang["languageCode"]
logging.info(f"Generating block for {language_code}") logging.info(f"Generating block for {language_code}")
# Iterate over all of the text to build up the symbols & counts
text_list = get_letter_counts(defs, lang)
# From the letter counts, need to make a symbol translator & write out the font
font_table_text, symbol_conversion_table = get_font_map_and_table(text_list)
try: try:
lang_name = lang["languageLocalName"] lang_name = lang["languageLocalName"]
except KeyError: except KeyError:
lang_name = language_code lang_name = language_code
f.write(f"\n// ---- {lang_name} ----\n\n") if strings_bin or compress_font:
f.write(font_table_text) f.write('#include "lzfx.h"\n')
f.write(f"\n// ---- {lang_name} ----\n\n") f.write(f"\n// ---- {lang_name} ----\n\n")
if not compress_font:
font_table_text = make_font_table_cpp(
sym_list, font_map, symbol_conversion_table
)
f.write(font_table_text)
else:
font12_uncompressed = bytearray()
for sym in sym_list:
font12_uncompressed.extend(font_map.font12[sym])
font12_compressed = lzfx.compress(bytes(font12_uncompressed))
logging.info(
f"Font table 12x16 compressed from {len(font12_uncompressed)} to {len(font12_compressed)} bytes (ratio {len(font12_compressed) / len(font12_uncompressed):.3})"
)
write_bytes_as_c_array(f, "font_12x16_lzfx", font12_compressed)
font_table_text = make_font_table_06_cpp(
sym_list, font_map, symbol_conversion_table
)
f.write(font_table_text)
f.write(f"\n// ---- {lang_name} ----\n\n")
translation_common_text = get_translation_common_text(
defs, symbol_conversion_table, build_version
)
f.write(translation_common_text)
f.write(
f"const bool HasFahrenheit = {('true' if lang.get('tempUnitFahrenheit', True) else 'false')};\n\n"
)
if not compress_font:
f.write("extern const uint8_t *const Font_12x16 = USER_FONT_12;\n")
else:
f.write(
f"static uint8_t font_out_buffer[{len(font12_uncompressed)}];\n\n"
"extern const uint8_t *const Font_12x16 = font_out_buffer;\n"
)
f.write("extern const uint8_t *const Font_6x8 = USER_FONT_6x8;\n\n")
if not strings_bin:
translation_strings_and_indices_text = get_translation_strings_and_indices_text(
lang, defs, symbol_conversion_table
)
f.write(translation_strings_and_indices_text)
f.write(
"const TranslationIndexTable *const Tr = &TranslationIndices;\n"
"const char *const TranslationStrings = TranslationStringsData;\n\n"
)
else:
compressed = lzfx.compress(strings_bin)
logging.info(
f"Strings compressed from {len(strings_bin)} to {len(compressed)} bytes (ratio {len(compressed) / len(strings_bin):.3})"
)
write_bytes_as_c_array(f, "translation_data_lzfx", compressed)
f.write(
f"static uint8_t translation_data_out_buffer[{len(strings_bin)}] __attribute__((__aligned__(2)));\n\n"
"const TranslationIndexTable *const Tr = reinterpret_cast<const TranslationIndexTable *>(translation_data_out_buffer);\n"
"const char *const TranslationStrings = reinterpret_cast<const char *>(translation_data_out_buffer) + sizeof(TranslationIndexTable);\n\n"
)
if not strings_bin and not compress_font:
f.write("void prepareTranslations() {}\n\n")
else:
f.write("void prepareTranslations() {\n" " unsigned int outsize;\n")
if compress_font:
f.write(
" outsize = sizeof(font_out_buffer);\n"
" lzfx_decompress(font_12x16_lzfx, sizeof(font_12x16_lzfx), font_out_buffer, &outsize);\n"
)
if strings_bin:
f.write(
" outsize = sizeof(translation_data_out_buffer);\n"
" lzfx_decompress(translation_data_lzfx, sizeof(translation_data_lzfx), translation_data_out_buffer, &outsize);\n"
)
f.write("}\n\n")
sanity_checks_text = get_translation_sanity_checks_text(defs)
f.write(sanity_checks_text)
def get_translation_common_text(
defs: dict, symbol_conversion_table: Dict[str, bytes], build_version
) -> str:
translation_common_text = ""
# Write out firmware constant options
constants = get_constants(build_version)
for x in constants:
translation_common_text += f'const char* {x[0]} = "{convert_string(symbol_conversion_table, x[1])}";//{x[1]} \n'
translation_common_text += "\n"
# Debug Menu
translation_common_text += "const char* DebugMenu[] = {\n"
for c in get_debug_menu():
translation_common_text += (
f'\t "{convert_string(symbol_conversion_table, c)}",//{c} \n'
)
translation_common_text += "};\n\n"
return translation_common_text
@dataclass
class TranslationItem:
info: str
str_index: int
def get_translation_strings_and_indices_text(
lang: dict, defs: dict, symbol_conversion_table: Dict[str, bytes]
) -> str:
str_table: List[str] = [] str_table: List[str] = []
str_group_messages: List[TranslationItem] = [] str_group_messages: List[TranslationItem] = []
str_group_messageswarn: List[TranslationItem] = [] str_group_messageswarn: List[TranslationItem] = []
@@ -478,21 +712,6 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
str_group_characters.append(TranslationItem(eid, len(str_table))) str_group_characters.append(TranslationItem(eid, len(str_table)))
str_table.append(obj[eid]) str_table.append(obj[eid])
# Write out firmware constant options
constants = get_constants()
for x in constants:
f.write(
f'const char* {x[0]} = "{convert_string(symbol_conversion_table, x[1])}";//{x[1]} \n'
)
f.write("\n")
# Debug Menu
f.write("const char* DebugMenu[] = {\n")
for c in get_debug_menu():
f.write(f'\t "{convert_string(symbol_conversion_table, c)}",//{c} \n')
f.write("};\n\n")
# ----- Reading SettingsDescriptions # ----- Reading SettingsDescriptions
obj = lang["menuOptions"] obj = lang["menuOptions"]
@@ -537,8 +756,6 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
) )
str_table.append(obj[eid]["desc"]) str_table.append(obj[eid]["desc"])
f.write("\n")
@dataclass @dataclass
class RemappedTranslationItem: class RemappedTranslationItem:
str_index: int str_index: int
@@ -573,14 +790,13 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
str_offsets = [-1] * len(str_table) str_offsets = [-1] * len(str_table)
offset = 0 offset = 0
write_null = False write_null = False
f.write("const char TranslationStringsData[] = {\n") translation_strings_text = "const char TranslationStringsData[] = {\n"
for i, source_str in enumerate(str_table): for i, source_str in enumerate(str_table):
if write_null:
f.write(' "\\0"\n')
write_null = True
if str_remapping[i] is not None: if str_remapping[i] is not None:
write_null = False
continue continue
if write_null:
translation_strings_text += ' "\\0"\n'
write_null = True
# Find what items use this string # Find what items use this string
str_used_by = [i] + [ str_used_by = [i] + [
j for j, r in enumerate(str_remapping) if r and r.str_index == i j for j, r in enumerate(str_remapping) if r and r.str_index == i
@@ -597,40 +813,35 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
]: ]:
for item in group: for item in group:
if item.str_index == j: if item.str_index == j:
f.write(f" // - {pre_info} {item.info}\n") translation_strings_text += (
f" // - {pre_info} {item.info}\n"
)
if j == i: if j == i:
f.write(f" // {offset: >4}: {escape(source_str)}\n") translation_strings_text += f" // {offset: >4}: {escape(source_str)}\n"
str_offsets[j] = offset str_offsets[j] = offset
else: else:
remapped = str_remapping[j] remapped = str_remapping[j]
assert remapped is not None assert remapped is not None
f.write( translation_strings_text += f" // {offset + remapped.str_start_offset: >4}: {escape(str_table[j])}\n"
f" // {offset + remapped.str_start_offset: >4}: {escape(str_table[j])}\n"
)
str_offsets[j] = offset + remapped.str_start_offset str_offsets[j] = offset + remapped.str_start_offset
converted_str = convert_string(symbol_conversion_table, source_str) converted_bytes = convert_string_bytes(symbol_conversion_table, source_str)
f.write(f' "{converted_str}"') translation_strings_text += f' "{bytes_to_escaped(converted_bytes)}"'
str_offsets[i] = offset str_offsets[i] = offset
# Sanity check: Each "char" in `converted_str` should be in format
# `\xFF`, so the length should be divisible by 4.
assert len(converted_str) % 4 == 0
# Add the length and the null terminator # Add the length and the null terminator
offset += len(converted_str) // 4 + 1 offset += len(converted_bytes) + 1
f.write("\n};\n\n") translation_strings_text += "\n}; // TranslationStringsData\n\n"
def get_offset(idx: int) -> int: def get_offset(idx: int) -> int:
assert str_offsets[idx] >= 0 assert str_offsets[idx] >= 0
return str_offsets[idx] return str_offsets[idx]
f.write("const TranslationIndexTable TranslationIndices = {\n") translation_indices_text = "const TranslationIndexTable TranslationIndices = {\n"
# ----- Write the messages string indices: # ----- Write the messages string indices:
for group in [str_group_messages, str_group_messageswarn, str_group_characters]: for group in [str_group_messages, str_group_messageswarn, str_group_characters]:
for item in group: for item in group:
f.write( translation_indices_text += f" .{item.info} = {get_offset(item.str_index)}, // {escape(str_table[item.str_index])}\n"
f" .{item.info} = {get_offset(item.str_index)}, // {escape(str_table[item.str_index])}\n" translation_indices_text += "\n"
)
f.write("\n")
# ----- Write the settings index tables: # ----- Write the settings index tables:
for group, name in [ for group, name in [
@@ -640,30 +851,25 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
(str_group_settingmenuentriesdesc, "SettingsMenuEntriesDescriptions"), (str_group_settingmenuentriesdesc, "SettingsMenuEntriesDescriptions"),
]: ]:
max_len = 30 max_len = 30
f.write(f" .{name} = {{\n") translation_indices_text += f" .{name} = {{\n"
for item in group: for item in group:
f.write( translation_indices_text += f" /* {item.info.ljust(max_len)[:max_len]} */ {get_offset(item.str_index)}, // {escape(str_table[item.str_index])}\n"
f" /* {item.info.ljust(max_len)[:max_len]} */ {get_offset(item.str_index)}, // {escape(str_table[item.str_index])}\n" translation_indices_text += f" }}, // {name}\n\n"
)
f.write(f" }}, // {name}\n\n")
f.write("}; // TranslationIndices\n\n") translation_indices_text += "}; // TranslationIndices\n\n"
f.write("const TranslationIndexTable *const Tr = &TranslationIndices;\n")
f.write("const char *const TranslationStrings = TranslationStringsData;\n\n")
f.write( return translation_strings_text + translation_indices_text
f"const bool HasFahrenheit = {('true' if lang.get('tempUnitFahrenheit', True) else 'false')};\n"
)
f.write("\n// Verify SettingsItemIndex values:\n")
def get_translation_sanity_checks_text(defs: dict) -> str:
sanity_checks_text = "\n// Verify SettingsItemIndex values:\n"
for i, mod in enumerate(defs["menuOptions"]): for i, mod in enumerate(defs["menuOptions"]):
eid = mod["id"] eid = mod["id"]
f.write( sanity_checks_text += (
f"static_assert(static_cast<uint8_t>(SettingsItemIndex::{eid}) == {i});\n" f"static_assert(static_cast<uint8_t>(SettingsItemIndex::{eid}) == {i});\n"
) )
f.write( sanity_checks_text += f"static_assert(static_cast<uint8_t>(SettingsItemIndex::NUM_ITEMS) == {len(defs['menuOptions'])});\n"
f"static_assert(static_cast<uint8_t>(SettingsItemIndex::NUM_ITEMS) == {len(defs['menuOptions'])});\n" return sanity_checks_text
)
def read_version() -> str: def read_version() -> str:
@@ -683,6 +889,34 @@ def read_version() -> str:
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument(
"--output-pickled",
help="Write pickled language data for later reuse",
type=argparse.FileType("wb"),
required=False,
dest="output_pickled",
)
parser.add_argument(
"--input-pickled",
help="Use previously generated pickled language data",
type=argparse.FileType("rb"),
required=False,
dest="input_pickled",
)
parser.add_argument(
"--strings-bin",
help="Use generated TranslationIndices + TranslationStrings data and compress them",
type=argparse.FileType("rb"),
required=False,
dest="strings_bin",
)
parser.add_argument(
"--compress-font",
help="Compress the font table",
action="store_true",
required=False,
dest="compress_font",
)
parser.add_argument( parser.add_argument(
"--output", "-o", help="Target file", type=argparse.FileType("w"), required=True "--output", "-o", help="Target file", type=argparse.FileType("w"), required=True
) )
@@ -690,23 +924,57 @@ def parse_args() -> argparse.Namespace:
return parser.parse_args() return parser.parse_args()
if __name__ == "__main__": def main() -> None:
json_dir = HERE json_dir = HERE
args = parse_args() args = parse_args()
try: if args.input_pickled and args.output_pickled:
buildVersion = read_version() logging.error("error: Both --output-pickled and --input-pickled are specified")
except FileNotFoundError:
logging.error("error: Could not find version info ")
sys.exit(1) sys.exit(1)
logging.info(f"Build version: {buildVersion}") language_data: LanguageData
logging.info(f"Making {args.languageCode} from {json_dir}") if args.input_pickled:
logging.info(f"Reading pickled language data from {args.input_pickled.name}...")
language_data = pickle.load(args.input_pickled)
if language_data.lang["languageCode"] != args.languageCode:
logging.error(
f"error: languageCode {args.languageCode} does not match language data {language_data.lang['languageCode']}"
)
sys.exit(1)
logging.info(f"Read language data for {language_data.lang['languageCode']}")
logging.info(f"Build version: {language_data.build_version}")
else:
try:
build_version = read_version()
except FileNotFoundError:
logging.error("error: Could not find version info ")
sys.exit(1)
logging.info(f"Build version: {build_version}")
logging.info(f"Making {args.languageCode} from {json_dir}")
lang_ = read_translation(json_dir, args.languageCode)
defs_ = load_json(os.path.join(json_dir, "translations_def.js"), True)
language_data = prepare_language(lang_, defs_, build_version)
lang_ = read_translation(json_dir, args.languageCode)
defs_ = load_json(os.path.join(json_dir, "translations_def.js"), True)
out_ = args.output out_ = args.output
write_start(out_) write_start(out_)
write_language(lang_, defs_, out_) if args.strings_bin:
write_language(
language_data,
out_,
args.strings_bin.read(),
compress_font=args.compress_font,
)
else:
write_language(language_data, out_, compress_font=args.compress_font)
if args.output_pickled:
logging.info(f"Writing pickled data to {args.output_pickled.name}")
pickle.dump(language_data, args.output_pickled)
logging.info("Done") logging.info("Done")
if __name__ == "__main__":
main()

View File

@@ -24,6 +24,12 @@ class TestMakeTranslation(unittest.TestCase):
self.assertEqual(bytes_to_escaped(b"\x00"), "\\x00") self.assertEqual(bytes_to_escaped(b"\x00"), "\\x00")
self.assertEqual(bytes_to_escaped(b"\xF1\xAB"), "\\xF1\\xAB") self.assertEqual(bytes_to_escaped(b"\xF1\xAB"), "\\xF1\\xAB")
def test_bytes_to_c_hex(self):
from make_translation import bytes_to_c_hex
self.assertEqual(bytes_to_c_hex(b"\x00"), "0x00,")
self.assertEqual(bytes_to_c_hex(b"\xF1\xAB"), "0xF1, 0xAB,")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "BG", "languageCode": "BG",
"languageLocalName": "Български", "languageLocalName": "Български",
"cyrillicGlyphs": true, "fonts": [
"ascii_basic",
"cyrillic"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Калибрацията завършена!", "SettingsCalibrationDone": "Калибрацията завършена!",
"SettingsCalibrationWarning": "Уверете се, че върха на поялника е със стайна температура преди да продължите!", "SettingsCalibrationWarning": "Уверете се, че върха на поялника е със стайна температура преди да продължите!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "CS", "languageCode": "CS",
"languageLocalName": "Český", "languageLocalName": "Český",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibrace dokončena!", "SettingsCalibrationDone": "Kalibrace dokončena!",
"SettingsCalibrationWarning": "Ujistěte se, že hrot má pokojovou teplotu!", "SettingsCalibrationWarning": "Ujistěte se, že hrot má pokojovou teplotu!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "DA", "languageCode": "DA",
"languageLocalName": "Dansk", "languageLocalName": "Dansk",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Calibration done!", "SettingsCalibrationDone": "Calibration done!",
"SettingsCalibrationWarning": "Sørg for at loddespidsen er ved stuetemperatur, inden du fortsætter!", "SettingsCalibrationWarning": "Sørg for at loddespidsen er ved stuetemperatur, inden du fortsætter!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "DE", "languageCode": "DE",
"languageLocalName": "Deutsch", "languageLocalName": "Deutsch",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"tempUnitFahrenheit": false, "tempUnitFahrenheit": false,
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibrierung abgeschlossen!", "SettingsCalibrationDone": "Kalibrierung abgeschlossen!",
@@ -58,10 +61,10 @@
"SettingSensitivityLow": "N", "SettingSensitivityLow": "N",
"SettingSensitivityMedium": "M", "SettingSensitivityMedium": "M",
"SettingSensitivityHigh": "H", "SettingSensitivityHigh": "H",
"SettingLockDisableChar": "D", "SettingLockDisableChar": "A",
"SettingLockBoostChar": "B", "SettingLockBoostChar": "B",
"SettingLockFullChar": "V", "SettingLockFullChar": "V",
"SettingNAChar": "N/A" "SettingNAChar": "-"
}, },
"menuGroups": { "menuGroups": {
"PowerMenu": { "PowerMenu": {
@@ -267,7 +270,7 @@
"Tasten-", "Tasten-",
"sperre" "sperre"
], ],
"desc": "Langes drücken beider Tasten im Lötmodus sperrt diese <D=deaktiviert | B=nur Boost | V=vollständig>" "desc": "Langes drücken beider Tasten im Lötmodus sperrt diese <A=aus | B=nur Boost | V=vollständig>"
}, },
"MinVolCell": { "MinVolCell": {
"text2": [ "text2": [
@@ -288,14 +291,14 @@
"Anim.", "Anim.",
"Geschw." "Geschw."
], ],
"desc": "Geschwindigkeit der Icon-Animationen im Menü <A=aus | N=niedrig | M=mittel | H=hoch>" "desc": "Geschwindigkeit der Icon-Animationen im Menü <A=aus | L=langsam | M=mittel | S=schnell>"
}, },
"PowerPulseWait": { "PowerPulseWait": {
"text2": [ "text2": [
"Leistungsimpulse", "Leistungsimpulse",
"Wartezeit" "Wartezeit"
], ],
"desc": "Wartezeit vor dem Auslösen jedes Wachhalteimpulses (x 2,5s)" "desc": "Dauer vor Abgabe von Wachhalteimpulsen (x 2,5s)"
}, },
"PowerPulseDuration": { "PowerPulseDuration": {
"text2": [ "text2": [

View File

@@ -1,7 +1,9 @@
{ {
"languageCode": "EN", "languageCode": "EN",
"languageLocalName": "English", "languageLocalName": "English",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic"
],
"tempUnitFahrenheit": true, "tempUnitFahrenheit": true,
"messages": { "messages": {
"SettingsCalibrationDone": "Calibration done!", "SettingsCalibrationDone": "Calibration done!",
@@ -281,21 +283,21 @@
"Anim.", "Anim.",
"loop" "loop"
], ],
"desc": "Loop icon animations in root menu" "desc": "Loop icon animations in main menu"
}, },
"AnimSpeed": { "AnimSpeed": {
"text2": [ "text2": [
"Anim.", "Anim.",
"speed" "speed"
], ],
"desc": "Speed of icon animations in menu <O=off | L=low | M=medium | H=high>" "desc": "Pace of icon animations in menu <O=off | S=slow | M=medium | F=fast>"
}, },
"PowerPulseWait": { "PowerPulseWait": {
"text2": [ "text2": [
"Power pulse", "Power pulse",
"wait time" "delay"
], ],
"desc": "Time to wait before triggering every keep-awake pulse (x 2.5s)" "desc": "Delay before keep-awake pulse is triggered (x 2.5s)"
}, },
"PowerPulseDuration": { "PowerPulseDuration": {
"text2": [ "text2": [

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "ES", "languageCode": "ES",
"languageLocalName": "Castellano", "languageLocalName": "Castellano",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "¡Calibrada!", "SettingsCalibrationDone": "¡Calibrada!",
"SettingsCalibrationWarning": "¡Asegúrate que la punta esté a temperatura ambiente antes de empezar!", "SettingsCalibrationWarning": "¡Asegúrate que la punta esté a temperatura ambiente antes de empezar!",

View File

@@ -1,45 +1,45 @@
{ {
"languageCode": "FI", "languageCode": "FI",
"languageLocalName": "Suomi", "languageLocalName": "Suomi",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Calibration done!",
"SettingsCalibrationWarning": "Varmista että kärki on huoneenlämpöinen ennen jatkamista!", "SettingsCalibrationWarning": "Varmista että kärki on huoneenlämpöinen ennen jatkamista!",
"SettingsResetWarning": "Haluatko varmasti palauttaa oletusarvot?", "SettingsResetWarning": "Haluatko varmasti palauttaa oletusarvot?",
"UVLOWarningString": "DC LOW", "UVLOWarningString": "DC ALH.",
"UndervoltageString": "Alijännite", "UndervoltageString": "Alijännite",
"InputVoltageString": "Jännite: ", "InputVoltageString": "Jännite: ",
"WarningTipTempString": "Lämpötila: ",
"BadTipString": "VIKATILA",
"SleepingSimpleString": "Zzzz", "SleepingSimpleString": "Zzzz",
"SleepingAdvancedString": "Lepotila...", "SleepingAdvancedString": "Lepotila...",
"WarningSimpleString": "HOT",
"WarningAdvancedString": "! KÄRKI KUUMA !",
"SleepingTipAdvancedString": "Kärki:", "SleepingTipAdvancedString": "Kärki:",
"IdleTipString": "Kärki:", "IdleTipString": "Pää:",
"IdleSetString": " Asetus:", "IdleSetString": "Aseta:",
"TipDisconnectedString": "KÄRKI ON IRTI", "TipDisconnectedString": "KÄRKI PUUTTUU",
"SolderingAdvancedPowerPrompt": "Teho: ", "SolderingAdvancedPowerPrompt": "Teho: ",
"OffString": "OFF", "OffString": "Off"
"YourGainMessage": "Your gain:"
}, },
"messagesWarn": { "messagesWarn": {
"ResetOKMessage": "Reset OK", "ResetOKMessage": [
"Palautus",
"onnistui"
],
"SettingsResetMessage": [ "SettingsResetMessage": [
"Settings were", "Asetukset",
"reset!" "palautettu!"
], ],
"NoAccelerometerMessage": [ "NoAccelerometerMessage": [
"No accelerometer", "Kiihtyvyysanturi",
"detected!" "puuttuu!"
], ],
"NoPowerDeliveryMessage": [ "NoPowerDeliveryMessage": [
"No USB-PD IC", "USB-PD IC",
"detected!" "puuttuu!"
], ],
"LockingKeysString": " LOCKED", "LockingKeysString": " LUKITTU",
"UnlockingKeysString": "UNLOCKED", "UnlockingKeysString": "AUKI",
"WarningKeysLockedString": "!LOCKED!" "WarningKeysLockedString": "!LUKKO!"
}, },
"characters": { "characters": {
"SettingRightChar": "O", "SettingRightChar": "O",
@@ -48,27 +48,27 @@
"SettingFastChar": "N", "SettingFastChar": "N",
"SettingSlowChar": "H", "SettingSlowChar": "H",
"SettingMediumChar": "M", "SettingMediumChar": "M",
"SettingOffChar": "O", "SettingOffChar": "P",
"SettingStartSolderingChar": "T", "SettingStartSolderingChar": "K",
"SettingStartSleepChar": "S", "SettingStartSleepChar": "L",
"SettingStartSleepOffChar": "O", "SettingStartSleepOffChar": "N",
"SettingStartNoneChar": "F", "SettingStartNoneChar": "E",
"SettingSensitivityOff": "O", "SettingSensitivityOff": "P",
"SettingSensitivityLow": "L", "SettingSensitivityLow": "A",
"SettingSensitivityMedium": "M", "SettingSensitivityMedium": "K",
"SettingSensitivityHigh": "H", "SettingSensitivityHigh": "S",
"SettingLockDisableChar": "D", "SettingLockDisableChar": "P",
"SettingLockBoostChar": "B", "SettingLockBoostChar": "V",
"SettingLockFullChar": "F", "SettingLockFullChar": "K",
"SettingNAChar": "N/A" "SettingNAChar": "N/A"
}, },
"menuGroups": { "menuGroups": {
"PowerMenu": { "PowerMenu": {
"text2": [ "text2": [
"Power", "Virta-",
"settings" "asetukset"
], ],
"desc": "Power settings" "desc": "Virta-asetukset"
}, },
"SolderingMenu": { "SolderingMenu": {
"text2": [ "text2": [
@@ -105,56 +105,56 @@
"Virtalähde", "Virtalähde",
"DC" "DC"
], ],
"desc": "Käytettävä virtalähde. Asettaa katkaisujänniteen. <DC 10V, 3S=9.9V, 4S=13.2V, 5S=16.5V, 6S=19.8V>" "desc": "Virtalähde. Asettaa katkaisujännitteen. <DC 10V> <S 3.3V per kenno, poistaa virtarajoitukset>"
}, },
"SleepTemperature": { "SleepTemperature": {
"text2": [ "text2": [
"Lepotilan", "Lepotilan",
"lämpötila" "lämpötila"
], ],
"desc": "Lepotilan lämpötila. <C>" "desc": "Kärjen lämpötila \"lepotilassa\""
}, },
"SleepTimeout": { "SleepTimeout": {
"text2": [ "text2": [
"Lepotilan", "Lepotilan",
"viive" "viive"
], ],
"desc": "Lepotilan viive. <minuuttia/sekuntia>" "desc": "\"Lepotilan\" ajastus <S=sekuntia | M=minuuttia>"
}, },
"ShutdownTimeout": { "ShutdownTimeout": {
"text2": [ "text2": [
"Sammutus", "Sammutus",
"viive" "viive"
], ],
"desc": "Automaattisen sammutuksen aikaviive. <minuuttia>" "desc": "Automaattisen sammutuksen ajastus <M=minuuttia>"
}, },
"MotionSensitivity": { "MotionSensitivity": {
"text2": [ "text2": [
"Liikkeen", "Liikkeen",
"herkkyys" "herkkyys"
], ],
"desc": "Liikkeentunnistuksen herkkyys. <0=pois, 1=epäherkin, 9=herkin>" "desc": "0=pois päältä | 1=vähäinen herkkyys | ... | 9=suurin herkkyys"
}, },
"TemperatureUnit": { "TemperatureUnit": {
"text2": [ "text2": [
"Lämpötilan", "Lämpötilan",
"yksikkö" "yksikkö"
], ],
"desc": "Lämpötilan yksikkö. <C=celsius, F=fahrenheit>" "desc": "<C=celsius, F=fahrenheit>"
}, },
"AdvancedIdle": { "AdvancedIdle": {
"text2": [ "text2": [
"Tiedot", "Tiedot",
"lepotilassa" "lepotilassa"
], ],
"desc": "Näyttää yksityiskohtaisemmat tiedot lepotilassa." "desc": "Näyttää yksityiskohtaisemmat pienemmällä fontilla tiedot lepotilassa."
}, },
"DisplayRotation": { "DisplayRotation": {
"text2": [ "text2": [
"Näytön", "Näytön",
"kierto" "kierto"
], ],
"desc": "Näytön kierto. <A=automaattinen O=oikeakätinen V=vasenkätinen>" "desc": "A=automaattinen | V=vasenkätinen | O=oikeakätinen"
}, },
"BoostTemperature": { "BoostTemperature": {
"text2": [ "text2": [
@@ -175,133 +175,133 @@
"Jäähdytyksen", "Jäähdytyksen",
"vilkutus" "vilkutus"
], ],
"desc": "Vilkuttaa jäähtyessä juotoskärjen lämpötilaa sen ollessa vielä vaarallisen kuuma." "desc": "Vilkuttaa jäähtyessä juotoskärjen lämpötilaa sen ollessa vielä vaarallisen kuuma"
}, },
"TemperatureCalibration": { "TemperatureCalibration": {
"text2": [ "text2": [
"Kalibroi", "Kalibroi",
"lämpötila?" "lämpötila?"
], ],
"desc": "Kalibroi kärjen lämpötilaeron." "desc": "Kalibroi kärjen lämpötilaeron"
}, },
"SettingsReset": { "SettingsReset": {
"text2": [ "text2": [
"Palauta", "Palauta",
"tehdasasetukset?" "tehdasasetukset?"
], ],
"desc": "Palauta kaikki asetukset oletusarvoihin." "desc": "Palauta kaikki asetukset oletusarvoihin"
}, },
"VoltageCalibration": { "VoltageCalibration": {
"text2": [ "text2": [
"Kalibroi", "Kalibroi",
"tulojännite?" "tulojännite?"
], ],
"desc": "Tulojännitten kalibrointi (VIN). Painikkeilla säädetään ja pitkään painamalla poistutaan." "desc": "Tulojännitten kalibrointi (VIN) <paina pitkään poistuaksesi>"
}, },
"AdvancedSoldering": { "AdvancedSoldering": {
"text2": [ "text2": [
"Tarkempi", "Tarkempi",
"juotosnäyttö" "juotosnäyttö"
], ],
"desc": "Näyttää yksityiskohtaisemmat tiedot juotostilassa." "desc": "Näyttää yksityiskohtaisemmat tiedot pienellä fontilla juotostilassa"
}, },
"ScrollingSpeed": { "ScrollingSpeed": {
"text2": [ "text2": [
"Tietojen", "Selityksien",
"näyttönopeus" "nopeus"
], ],
"desc": "Näiden selitetekstien vieritysnopeus." "desc": "Selityksien vieritysnopeus <H=hidas | N=nopea>"
}, },
"QCMaxVoltage": { "QCMaxVoltage": {
"text2": [ "text2": [
"Power", "QC",
"Wattage" "jännite"
], ],
"desc": "Power Wattage of the power adapter used" "desc": "Ensisijainen maksimi QC jännite"
}, },
"PowerLimit": { "PowerLimit": {
"text2": [ "text2": [
"Power", "Tehon-",
"Limit" "rajoitus"
], ],
"desc": "Maximum power the iron can use <Watts>" "desc": "Suurin sallittu teho <Watti>"
}, },
"ReverseButtonTempChange": { "ReverseButtonTempChange": {
"text2": [ "text2": [
"Key +-", "Suunnanvaihto",
"reverse?" "+ - näppäimille"
], ],
"desc": "Reverse the tip temperature change buttons plus minus assignment." "desc": "Lämpötilapainikkeiden suunnan vaihtaminen"
}, },
"TempChangeShortStep": { "TempChangeShortStep": {
"text2": [ "text2": [
"Temp change", "Lämmön muutos",
"short?" "lyhyt painal."
], ],
"desc": "Temperature change steps on short button press!" "desc": "Lämpötilan muutos lyhyellä painalluksella"
}, },
"TempChangeLongStep": { "TempChangeLongStep": {
"text2": [ "text2": [
"Temp change", "Lämmön muutos",
"long?" "pitkä painal."
], ],
"desc": "Temperature change steps on long button press!" "desc": "Lämpötilan muutos pitkällä painalluksella"
}, },
"PowerPulsePower": { "PowerPulsePower": {
"text2": [ "text2": [
"Power", "Herätyspulssin",
"Pulse W" "voimakkuus"
], ],
"desc": "Keep awake pulse power intensity" "desc": "Herätyspulssin voimakkuus <watti>"
}, },
"HallEffSensitivity": { "HallEffSensitivity": {
"text2": [ "text2": [
"Hall Eff", "Hall-",
"Sensitivity" "herk."
], ],
"desc": "Sensitivity of the Hall effect sensor in detecting sleep <O=Off,L=Low,M=Medium,H=High>" "desc": "Hall-efektianturin herkkyys lepotilan tunnistuksessa <P=pois | A=alhainen | K=keskiverto | S=suuri>"
}, },
"LockingMode": { "LockingMode": {
"text2": [ "text2": [
"Allow buttons", "Salli nappien",
"locking" "lukitus"
], ],
"desc": "When soldering, long press on both buttons lock them <D=Disable, B=Boost only, F=Full locking>" "desc": "Kolvatessa paina molempia näppäimiä lukitaksesi ne <P=pois | V=vain tehostus | K=kaikki>"
}, },
"MinVolCell": { "MinVolCell": {
"text2": [ "text2": [
"Minimum", "Pienin",
"voltage" "jännite"
], ],
"desc": "Minimum allowed voltage per cell <Volts> <3S: 3.0V - 3.7V, 4/5/6S: 2.4V - 3.7V>" "desc": "Pienin sallittu jännite per kenno <Voltit> <3S: 3.0V - 3.7V, 4/5/6S: 2.4V - 3.7V>"
}, },
"AnimLoop": { "AnimLoop": {
"text2": [ "text2": [
"Anim.", "Animaation",
"loop" "toistaminen"
], ],
"desc": "Loop icon animations in root menu" "desc": "Toista animaatiot valikossa"
}, },
"AnimSpeed": { "AnimSpeed": {
"text2": [ "text2": [
"Anim.", "Animaation",
"speed" "nopeus"
], ],
"desc": "Speed of icon animations in menu <O=off | L=low | M=medium | H=high>" "desc": "Animaatioiden nopeus valikossa <P=pois | A=alhainen | K=keskiverto | S=suuri>"
}, },
"PowerPulseWait": { "PowerPulseWait": {
"text2": [ "text2": [
"Power pulse", "Pulssin",
"wait time" "odotusaika"
], ],
"desc": "Time to wait before triggering every keep-awake pulse (x 2.5s)" "desc": "Odotusaika herätyspulssin lähetykseen (x 2.5s)"
}, },
"PowerPulseDuration": { "PowerPulseDuration": {
"text2": [ "text2": [
"Power pulse", "Pulssin",
"duration" "kesto"
], ],
"desc": "Keep-awake-pulse duration (x 250ms)" "desc": "Herätyspulssin kesto (x 250ms)"
} }
} }
} }

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "FR", "languageCode": "FR",
"languageLocalName": "Français", "languageLocalName": "Français",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Calibration effectuée !", "SettingsCalibrationDone": "Calibration effectuée !",
"SettingsCalibrationWarning": "Assurez-vous que la panne soit à température ambiante avant de continuer !", "SettingsCalibrationWarning": "Assurez-vous que la panne soit à température ambiante avant de continuer !",
@@ -277,31 +280,31 @@
}, },
"AnimLoop": { "AnimLoop": {
"text2": [ "text2": [
"Anim.", "Icônes",
"loop" "animées"
], ],
"desc": "Loop icon animations in root menu" "desc": "Animations des icônes dans le menu principal"
}, },
"AnimSpeed": { "AnimSpeed": {
"text2": [ "text2": [
"Anim.", "Vitesse",
"speed" "d'animations"
], ],
"desc": "Speed of icon animations in menu <O=off | L=low | M=medium | H=high>" "desc": "Vitesse des animations des icônes dans le menu <D=désactivé | L=lente | M=moyenne | H=haute>"
}, },
"PowerPulseWait": { "PowerPulseWait": {
"text2": [ "text2": [
"Power pulse", "Délai entre",
"wait time" "les impulsions"
], ],
"desc": "Time to wait before triggering every keep-awake pulse (x 2.5s)" "desc": "Délai entre chaque impulsions pour empêcher la mise en veille (x 2.5s)"
}, },
"PowerPulseDuration": { "PowerPulseDuration": {
"text2": [ "text2": [
"Power pulse", "Durée des",
"duration" "impulsions"
], ],
"desc": "Keep-awake-pulse duration (x 250ms)" "desc": "Durée des impulsions pour empêcher la mise en veille (x 250ms)"
} }
} }
} }

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "HR", "languageCode": "HR",
"languageLocalName": "Hrvatski", "languageLocalName": "Hrvatski",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibracija gotova!", "SettingsCalibrationDone": "Kalibracija gotova!",
"SettingsCalibrationWarning": "Provjerite da je vršak ohlađen na sobnu temperaturu prije nego što nastavite!", "SettingsCalibrationWarning": "Provjerite da je vršak ohlađen na sobnu temperaturu prije nego što nastavite!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "HU", "languageCode": "HU",
"languageLocalName": "Magyar", "languageLocalName": "Magyar",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibráció befejezve!", "SettingsCalibrationDone": "Kalibráció befejezve!",
"SettingsCalibrationWarning": "Folytatás előtt győződjön meg róla, hogy a páka szobahőmérsékletű!", "SettingsCalibrationWarning": "Folytatás előtt győződjön meg róla, hogy a páka szobahőmérsékletű!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "IT", "languageCode": "IT",
"languageLocalName": "Italiano", "languageLocalName": "Italiano",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Calibrazione effettuata", "SettingsCalibrationDone": "Calibrazione effettuata",
"SettingsCalibrationWarning": "Assicurati che la punta si trovi a temperatura ambiente prima di continuare!", "SettingsCalibrationWarning": "Assicurati che la punta si trovi a temperatura ambiente prima di continuare!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "JA_JP", "languageCode": "JA_JP",
"languageLocalName": "日本語", "languageLocalName": "日本語",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"cjk"
],
"tempUnitFahrenheit": true, "tempUnitFahrenheit": true,
"messages": { "messages": {
"SettingsCalibrationDone": "校正完了", "SettingsCalibrationDone": "校正完了",
@@ -113,7 +116,7 @@
}, },
"DisplayRotation": { "DisplayRotation": {
"text2": "画面の向き", "text2": "画面の向き",
"desc": "A=自動 | L=左利き | R=右利き" "desc": "=自動 | =左利き | =右利き"
}, },
"BoostTemperature": { "BoostTemperature": {
"text2": "ブースト温度", "text2": "ブースト温度",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "LT", "languageCode": "LT",
"languageLocalName": "Lietuvių", "languageLocalName": "Lietuvių",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibravimas atliktas!", "SettingsCalibrationDone": "Kalibravimas atliktas!",
"SettingsCalibrationWarning": "Prieš tęsdami įsitikinkite, kad antgalis yra kambario temperatūros!", "SettingsCalibrationWarning": "Prieš tęsdami įsitikinkite, kad antgalis yra kambario temperatūros!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "NL", "languageCode": "NL",
"languageLocalName": "Nederlands", "languageLocalName": "Nederlands",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Calibratie klaar!", "SettingsCalibrationDone": "Calibratie klaar!",
"SettingsCalibrationWarning": "Zorg ervoor dat te punt op kamertemperatuur is voor je verder gaat!", "SettingsCalibrationWarning": "Zorg ervoor dat te punt op kamertemperatuur is voor je verder gaat!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "NL_BE", "languageCode": "NL_BE",
"languageLocalName": "Vlaams", "languageLocalName": "Vlaams",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Gecalibreerd!", "SettingsCalibrationDone": "Gecalibreerd!",
"SettingsCalibrationWarning": "Zorg vooraf dat de punt op kamertemperatuur is!", "SettingsCalibrationWarning": "Zorg vooraf dat de punt op kamertemperatuur is!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "NO", "languageCode": "NO",
"languageLocalName": "Norsk", "languageLocalName": "Norsk",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Calibration done!", "SettingsCalibrationDone": "Calibration done!",
"SettingsCalibrationWarning": "Sørg for at loddespissen har romtemperatur før du fortsetter!", "SettingsCalibrationWarning": "Sørg for at loddespissen har romtemperatur før du fortsetter!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "PL", "languageCode": "PL",
"languageLocalName": "Polski", "languageLocalName": "Polski",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"tempUnitFahrenheit": false, "tempUnitFahrenheit": false,
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibracja udana!", "SettingsCalibrationDone": "Kalibracja udana!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "PT", "languageCode": "PT",
"languageLocalName": "Português", "languageLocalName": "Português",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Calibração terminada!", "SettingsCalibrationDone": "Calibração terminada!",
"SettingsCalibrationWarning": "A ponta deve estar à temperatura ambiente antes de continuar!", "SettingsCalibrationWarning": "A ponta deve estar à temperatura ambiente antes de continuar!",

View File

@@ -1,7 +1,11 @@
{ {
"languageCode": "RU", "languageCode": "RU",
"languageLocalName": "Русский", "languageLocalName": "Русский",
"cyrillicGlyphs": true, "fonts": [
"ascii_basic",
"latin_extended",
"cyrillic"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Калибровка завершена!", "SettingsCalibrationDone": "Калибровка завершена!",
"SettingsCalibrationWarning": "Прежде чем продолжить, пожалуйста, убедитесь, что жало имеет комнатную температуру!", "SettingsCalibrationWarning": "Прежде чем продолжить, пожалуйста, убедитесь, что жало имеет комнатную температуру!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "SK", "languageCode": "SK",
"languageLocalName": "Slovenčina", "languageLocalName": "Slovenčina",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibrácia hotová!", "SettingsCalibrationDone": "Kalibrácia hotová!",
"SettingsCalibrationWarning": "Najprv sa prosím uistite, že hrot má izbovú teplotu!", "SettingsCalibrationWarning": "Najprv sa prosím uistite, že hrot má izbovú teplotu!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "SL", "languageCode": "SL",
"languageLocalName": "Slovenščina", "languageLocalName": "Slovenščina",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibracija opravljena!", "SettingsCalibrationDone": "Kalibracija opravljena!",
"SettingsCalibrationWarning": "Pred nadaljevanjem mora biti konica segreta na sobno temperaturo!", "SettingsCalibrationWarning": "Pred nadaljevanjem mora biti konica segreta na sobno temperaturo!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "SR_CYRL", "languageCode": "SR_CYRL",
"languageLocalName": "Српски", "languageLocalName": "Српски",
"cyrillicGlyphs": true, "fonts": [
"ascii_basic",
"cyrillic"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Калибрација готова", "SettingsCalibrationDone": "Калибрација готова",
"SettingsCalibrationWarning": "Проверите да ли је врх охлађен на собну температуру пре него што наставите", "SettingsCalibrationWarning": "Проверите да ли је врх охлађен на собну температуру пре него што наставите",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "SR_LATN", "languageCode": "SR_LATN",
"languageLocalName": "Srpski", "languageLocalName": "Srpski",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibracija gotova", "SettingsCalibrationDone": "Kalibracija gotova",
"SettingsCalibrationWarning": "Proverite da li je vrh ohlađen na sobnu temperaturu pre nego što nastavite", "SettingsCalibrationWarning": "Proverite da li je vrh ohlađen na sobnu temperaturu pre nego što nastavite",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "SV", "languageCode": "SV",
"languageLocalName": "Svenska", "languageLocalName": "Svenska",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Calibration done!", "SettingsCalibrationDone": "Calibration done!",
"SettingsCalibrationWarning": "Please ensure the tip is at room temperature before continuing!", "SettingsCalibrationWarning": "Please ensure the tip is at room temperature before continuing!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "TR", "languageCode": "TR",
"languageLocalName": "Türkçe", "languageLocalName": "Türkçe",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"latin_extended"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Kalibrasyon tamamlandı!", "SettingsCalibrationDone": "Kalibrasyon tamamlandı!",
"SettingsCalibrationWarning": "Lütfen devam etmeden önce ucun oda sıcaklığında olduğunu garantiye alın!", "SettingsCalibrationWarning": "Lütfen devam etmeden önce ucun oda sıcaklığında olduğunu garantiye alın!",

View File

@@ -1,7 +1,11 @@
{ {
"languageCode": "UK", "languageCode": "UK",
"languageLocalName": "Українська", "languageLocalName": "Українська",
"cyrillicGlyphs": true, "fonts": [
"ascii_basic",
"latin_extended",
"cyrillic"
],
"messages": { "messages": {
"SettingsCalibrationDone": "Калібрування виконане!", "SettingsCalibrationDone": "Калібрування виконане!",
"SettingsCalibrationWarning": "Переконайтеся, що жало охололо до кімнатної температури, перш ніж продовжувати!", "SettingsCalibrationWarning": "Переконайтеся, що жало охололо до кімнатної температури, перш ніж продовжувати!",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "YUE_HK", "languageCode": "YUE_HK",
"languageLocalName": "廣東話 (香港)", "languageLocalName": "廣東話 (香港)",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"cjk"
],
"tempUnitFahrenheit": true, "tempUnitFahrenheit": true,
"messages": { "messages": {
"SettingsCalibrationDone": "校正完成!", "SettingsCalibrationDone": "校正完成!",
@@ -113,7 +116,7 @@
}, },
"DisplayRotation": { "DisplayRotation": {
"text2": "畫面方向", "text2": "畫面方向",
"desc": "A=自動 | L=使用左手 | R=使用右手" "desc": "=自動 | =使用左手 | =使用右手"
}, },
"BoostTemperature": { "BoostTemperature": {
"text2": "增熱温度", "text2": "增熱温度",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "ZH_CN", "languageCode": "ZH_CN",
"languageLocalName": "简体中文", "languageLocalName": "简体中文",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"cjk"
],
"tempUnitFahrenheit": true, "tempUnitFahrenheit": true,
"messages": { "messages": {
"SettingsCalibrationDone": "校正完成!", "SettingsCalibrationDone": "校正完成!",
@@ -113,7 +116,7 @@
}, },
"DisplayRotation": { "DisplayRotation": {
"text2": "画面方向", "text2": "画面方向",
"desc": "A=自动 | L=使用左手 | R=使用右手" "desc": "=自动 | =使用左手 | =使用右手"
}, },
"BoostTemperature": { "BoostTemperature": {
"text2": "增热温度", "text2": "增热温度",

View File

@@ -1,7 +1,10 @@
{ {
"languageCode": "ZH_TW", "languageCode": "ZH_TW",
"languageLocalName": "正體中文", "languageLocalName": "正體中文",
"cyrillicGlyphs": false, "fonts": [
"ascii_basic",
"cjk"
],
"tempUnitFahrenheit": true, "tempUnitFahrenheit": true,
"messages": { "messages": {
"SettingsCalibrationDone": "校正完成!", "SettingsCalibrationDone": "校正完成!",
@@ -113,7 +116,7 @@
}, },
"DisplayRotation": { "DisplayRotation": {
"text2": "畫面方向", "text2": "畫面方向",
"desc": "A=自動 | L=使用左手 | R=使用右手" "desc": "=自動 | =使用左手 | =使用右手"
}, },
"BoostTemperature": { "BoostTemperature": {
"text2": "增熱溫度", "text2": "增熱溫度",

View File

@@ -117,7 +117,7 @@ void OLED::drawChar(const uint16_t charCode, const FontStyle fontStyle) {
static uint8_t fontWidth, fontHeight; static uint8_t fontWidth, fontHeight;
switch (fontStyle) { switch (fontStyle) {
case FontStyle::SMALL: case FontStyle::SMALL:
currentFont = USER_FONT_6x8; currentFont = Font_6x8;
fontHeight = 8; fontHeight = 8;
fontWidth = 6; fontWidth = 6;
break; break;
@@ -128,7 +128,7 @@ void OLED::drawChar(const uint16_t charCode, const FontStyle fontStyle) {
break; break;
case FontStyle::LARGE: case FontStyle::LARGE:
default: default:
currentFont = USER_FONT_12; currentFont = Font_12x16;
fontHeight = 16; fontHeight = 16;
fontWidth = 12; fontWidth = 12;
break; break;

View File

@@ -8,9 +8,8 @@
#ifndef TRANSLATION_H_ #ifndef TRANSLATION_H_
#define TRANSLATION_H_ #define TRANSLATION_H_
#include "stdint.h" #include "stdint.h"
extern const uint8_t USER_FONT_12[];
extern const uint8_t USER_FONT_6x8[]; extern const bool HasFahrenheit;
extern const bool HasFahrenheit;
extern const char *SymbolPlus; extern const char *SymbolPlus;
extern const char *SymbolMinus; extern const char *SymbolMinus;
@@ -114,10 +113,15 @@ struct TranslationIndexTable {
extern const TranslationIndexTable *const Tr; extern const TranslationIndexTable *const Tr;
extern const char *const TranslationStrings; extern const char *const TranslationStrings;
extern const uint8_t *const Font_12x16;
extern const uint8_t *const Font_6x8;
constexpr uint8_t settings_item_index(const SettingsItemIndex i) { return static_cast<uint8_t>(i); } constexpr uint8_t settings_item_index(const SettingsItemIndex i) { return static_cast<uint8_t>(i); }
// Use a constexpr function for type-checking. // Use a constexpr function for type-checking.
#define SETTINGS_DESC(i) (settings_item_index(i) + 1) #define SETTINGS_DESC(i) (settings_item_index(i) + 1)
const char *translatedString(uint16_t index); const char *translatedString(uint16_t index);
void prepareTranslations();
#endif /* TRANSLATION_H_ */ #endif /* TRANSLATION_H_ */

View File

@@ -746,6 +746,8 @@ void showWarnings() {
uint8_t idleScreenBGF[sizeof(idleScreenBG)]; uint8_t idleScreenBGF[sizeof(idleScreenBG)];
/* StartGUITask function */ /* StartGUITask function */
void startGUITask(void const *argument __unused) { void startGUITask(void const *argument __unused) {
prepareTranslations();
OLED::initialize(); // start up the LCD OLED::initialize(); // start up the LCD
uint8_t tempWarningState = 0; uint8_t tempWarningState = 0;

View File

@@ -0,0 +1,79 @@
This directory contains file originally by other people.
## Simplified LZFX-based compression library
- `lzfx.c`
- `lzfx.h`
The above files are obtained from https://github.com/janding/lzfx (commit
448017f). It is a fork by Jan Ding (GitHub user "janding") based on the LZFX
compression library by Andrew Collette.
### License:
```
LZFX is copyright (c) 2009 Andrew Collette and subject to the BSD license
(below). Original LZF copyright statement follows.
Copyright (c) 2000-2007 Marc Alexander Lehmann <schmorp@schmorp.de>
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
```
## lzfx-boot
- `lzfx-host-compress.c` (original: `lzfx-raw.c`)
The above file is obtained from https://github.com/janding/lzfx-boot (commit
88b1596).
### License:
```
BSD 2-Clause License
Copyright (c) 2017, Jan Ding
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```

357
source/Core/lzfx/lzfx.c Normal file
View File

@@ -0,0 +1,357 @@
/*
* Copyright (c) 2009 Andrew Collette <andrew.collette at gmail.com>
* http://lzfx.googlecode.com
*
* Implements an LZF-compatible compressor/decompressor based on the liblzf
* codebase written by Marc Lehmann. This code is released under the BSD
* license. License and original copyright statement follow.
*
*
* Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "lzfx.h"
#define LZFX_HSIZE (1 << (LZFX_HLOG))
/* We need this for memset */
#ifdef __cplusplus
#include <cstring>
#else
#include <string.h>
#endif
#if __GNUC__ >= 3
#define fx_expect_false(expr) __builtin_expect((expr) != 0, 0)
#define fx_expect_true(expr) __builtin_expect((expr) != 0, 1)
#else
#define fx_expect_false(expr) (expr)
#define fx_expect_true(expr) (expr)
#endif
typedef unsigned char u8;
typedef const u8 * LZSTATE[LZFX_HSIZE];
/* Define the hash function */
#define LZFX_FRST(p) (((p[0]) << 8) | p[1])
#define LZFX_NEXT(v, p) (((v) << 8) | p[2])
#define LZFX_IDX(h) (((h >> (3 * 8 - LZFX_HLOG)) - h) & (LZFX_HSIZE - 1))
/* These cannot be changed, as they are related to the compressed format. */
#define LZFX_MAX_LIT (1 << 5) - 1
#define LZFX_MAX_OFF (1 << 13)
#define LZFX_MAX_REF ((1 << 8) + (1 << 3) - 2)
static int lzfx_getsize(const void *ibuf, unsigned int ilen, unsigned int *olen);
/* Compressed format
There are two kinds of structures in LZF/LZFX: literal runs and back
references. Literals are encoded as follows:
LLLLL000 <L bytes>
Back references are encoded as follows. The smallest possible encoded
length value is 1, as otherwise the control byte would be recognized as
a literal run. At least three bytes must match for a back reference
to be inserted. The offset (distance to the desired data in the output
buffer) is encoded as o - 1, as all offsets are at least 1. The binary
format is:
oooooLLL oooooooo for backrefs of real length < 7 (1 <= L < 7)
ooooo111 LLLLLLLL oooooooo for backrefs of real length >= 7 (L >= 7)
*/
int lzfx_compress(const void *const ibuf, const unsigned int ilen, void *obuf, unsigned int *const olen) {
/* Hash table; an array of u8*'s which point
to various locations in the input buffer */
const u8 *htab[LZFX_HSIZE];
const u8 ** hslot; /* Pointer to entry in hash table */
unsigned int hval; /* Hash value generated by macros above */
const u8 * ref; /* Pointer to candidate match location in input */
const u8 * ip = (const u8 *)ibuf;
const u8 *const in_end = ip + ilen;
u8 * op = (u8 *)obuf;
const u8 *const out_end = (olen == NULL ? NULL : op + *olen);
int lit; /* # of bytes in current literal run */
#if defined(WIN32) && defined(_M_X64)
unsigned _int64 off; /* workaround for missing POSIX compliance */
#else
unsigned long off;
#endif
if (olen == NULL)
return LZFX_EARGS;
if (ibuf == NULL) {
if (ilen != 0)
return LZFX_EARGS;
*olen = 0;
return 0;
}
if (obuf == NULL)
return LZFX_EARGS;
memset(htab, 0, sizeof(htab));
/* Start a literal run. Whenever we do this the output pointer is
advanced because the current byte will hold the encoded length. */
lit = 0;
op++;
hval = LZFX_FRST(ip);
while (ip + 2 < in_end) { /* The NEXT macro reads 2 bytes ahead */
hval = LZFX_NEXT(hval, ip);
hslot = htab + LZFX_IDX(hval);
ref = *hslot;
*hslot = ip;
if (ref < ip && (off = ip - ref - 1) < LZFX_MAX_OFF && ip + 4 < in_end /* Backref takes up to 3 bytes, so don't bother */
&& ref > (u8 *)ibuf && ref[0] == ip[0] && ref[1] == ip[1] && ref[2] == ip[2]) {
unsigned int len = 3; /* We already know 3 bytes match */
const unsigned int maxlen = in_end - ip - 2 > LZFX_MAX_REF ? LZFX_MAX_REF : in_end - ip - 2;
/* lit == 0: op + 3 must be < out_end (because we undo the run)
lit != 0: op + 3 + 1 must be < out_end */
if (fx_expect_false(op - !lit + 3 + 1 >= out_end))
return LZFX_ESIZE;
op[-lit - 1] = lit << 3; /* Terminate literal run */
op -= !lit; /* Undo run if length is zero */
/* Start checking at the fourth byte */
while (len < maxlen && ref[len] == ip[len])
len++;
/* Format 1: [oooooLLL oooooooo] */
if (len < 7) {
*op++ = ((off >> 8) << 3) + len;
*op++ = off;
/* Format 2: [ooooo111 LLLLLLLL oooooooo] */
} else {
*op++ = ((off >> 8) << 3) + 7;
*op++ = len - 7;
*op++ = off;
}
lit = 0;
op++;
ip += len - 1; /* ip = initial ip + #octets - 1 */
if (fx_expect_false(ip + 3 >= in_end)) {
ip++; /* Code following expects exit at bottom of loop */
break;
}
hval = LZFX_FRST(ip);
hval = LZFX_NEXT(hval, ip);
htab[LZFX_IDX(hval)] = ip;
ip++; /* ip = initial ip + #octets */
} else {
/* Keep copying literal bytes */
if (fx_expect_false(op >= out_end))
return LZFX_ESIZE;
lit++;
*op++ = *ip++;
if (fx_expect_false(lit == LZFX_MAX_LIT)) {
op[-lit - 1] = lit << 3; /* stop run */
lit = 0;
op++; /* start run */
}
} /* if() found match in htab */
} /* while(ip < ilen -2) */
/* At most 3 bytes remain in input. We therefore need 4 bytes available
in the output buffer to store them (3 data + ctrl byte).*/
if (op + 3 > out_end)
return LZFX_ESIZE;
while (ip < in_end) {
lit++;
*op++ = *ip++;
if (fx_expect_false(lit == LZFX_MAX_LIT)) {
op[-lit - 1] = lit << 3;
lit = 0;
op++;
}
}
op[-lit - 1] = lit << 3;
op -= !lit;
*olen = op - (u8 *)obuf;
return 0;
}
/* Decompressor */
int lzfx_decompress(const void *ibuf, unsigned int ilen, void *obuf, unsigned int *olen) {
u8 const * ip = (const u8 *)ibuf;
u8 const *const in_end = ip + ilen;
u8 * op = (u8 *)obuf;
u8 const *const out_end = (olen == NULL ? NULL : op + *olen);
unsigned int remain_len = 0;
int rc;
if (olen == NULL)
return LZFX_EARGS;
if (ibuf == NULL) {
if (ilen != 0)
return LZFX_EARGS;
*olen = 0;
return 0;
}
if (obuf == NULL) {
if (olen != 0)
return LZFX_EARGS;
return lzfx_getsize(ibuf, ilen, olen);
}
do {
unsigned int ctrl = *ip++;
/* Format LLLLL000: a literal byte string follows, of length L */
if ((ctrl & 0x7) == 0) {
unsigned int len = ctrl >> 3;
if (fx_expect_false(op + len > out_end)) {
--ip; /* Rewind to control byte */
goto guess;
}
if (fx_expect_false(ip + len > in_end))
return LZFX_ECORRUPT;
do
*op++ = *ip++;
while (--len);
/* Format #1 [oooooLLL oooooooo]: backref of length L+1
^^^^^ ^^^^^^^^
A B
#2 [ooooo111 LLLLLLLL oooooooo] backref of length L+7
^^^^^ ^^^^^^^^
A B
In both cases the location of the backref is computed from the
remaining part of the data as follows:
location = op - A*256 - B - 1
*/
} else {
unsigned int len = ctrl & 0x7;
u8 * ref = op - ((ctrl >> 3) << 8) - 1;
if (len == 7)
len += *ip++; /* i.e. format #2 */
if (fx_expect_false(op + len > out_end)) {
ip -= (len >= 7) ? 2 : 1; /* Rewind to control byte */
goto guess;
}
if (fx_expect_false(ip >= in_end))
return LZFX_ECORRUPT;
ref -= *ip++;
if (fx_expect_false(ref < (u8 *)obuf))
return LZFX_ECORRUPT;
do
*op++ = *ref++;
while (--len);
}
} while (ip < in_end);
*olen = op - (u8 *)obuf;
return 0;
guess:
rc = lzfx_getsize(ip, ilen - (ip - (u8 *)ibuf), &remain_len);
if (rc >= 0)
*olen = remain_len + (op - (u8 *)obuf);
return rc;
}
/* Guess len. No parameters may be NULL; this is not checked. */
static int lzfx_getsize(const void *ibuf, unsigned int ilen, unsigned int *olen) {
u8 const * ip = (const u8 *)ibuf;
u8 const *const in_end = ip + ilen;
int tot_len = 0;
while (ip < in_end) {
unsigned int ctrl = *ip++;
if ((ctrl & 0x7) == 0) {
if (ip + (ctrl >> 3) > in_end)
return LZFX_ECORRUPT;
tot_len += (ctrl >> 3);
ip += (ctrl >> 3);
} else {
unsigned int len = ctrl & 0x7;
if (len == 7) { /* i.e. format #2 */
len += *ip++;
}
if (ip >= in_end)
return LZFX_ECORRUPT;
ip++; /* skip the ref byte */
tot_len += len;
}
}
*olen = tot_len;
return 0;
}

95
source/Core/lzfx/lzfx.h Normal file
View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2009 Andrew Collette <andrew.collette at gmail.com>
* http://lzfx.googlecode.com
*
* Implements an LZF-compatible compressor/decompressor based on the liblzf
* codebase written by Marc Lehmann. This code is released under the BSD
* license. License and original copyright statement follow.
*
*
* Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LZFX_H
#define LZFX_H
#ifdef __cplusplus
extern "C" {
#endif
/* Documented behavior, including function signatures and error codes,
is guaranteed to remain unchanged for releases with the same major
version number. Releases of the same major version are also able
to read each other's output, although the output itself is not
guaranteed to be byte-for-byte identical.
*/
#define LZFX_VERSION_MAJOR 0
#define LZFX_VERSION_MINOR 1
#define LZFX_VERSION_STRING "0.1"
/* Hashtable size (2**LZFX_HLOG entries) */
#ifndef LZFX_HLOG
#define LZFX_HLOG 16
#endif
/* Predefined errors. */
#define LZFX_ESIZE -1 /* Output buffer too small */
#define LZFX_ECORRUPT -2 /* Invalid data for decompression */
#define LZFX_EARGS -3 /* Arguments invalid (NULL) */
/* Buffer-to buffer compression.
Supply pre-allocated input and output buffers via ibuf and obuf, and
their size in bytes via ilen and olen. Buffers may not overlap.
On success, the function returns a non-negative value and the argument
olen contains the compressed size in bytes. On failure, a negative
value is returned and olen is not modified.
*/
int lzfx_compress(const void *ibuf, unsigned int ilen, void *obuf, unsigned int *olen);
/* Buffer-to-buffer decompression.
Supply pre-allocated input and output buffers via ibuf and obuf, and
their size in bytes via ilen and olen. Buffers may not overlap.
On success, the function returns a non-negative value and the argument
olen contains the uncompressed size in bytes. On failure, a negative
value is returned.
If the failure code is LZFX_ESIZE, olen contains the minimum buffer size
required to hold the decompressed data. Otherwise, olen is not modified.
Supplying a zero *olen is a valid and supported strategy to determine the
required buffer size. This does not require decompression of the entire
stream and is consequently very fast. Argument obuf may be NULL in
this case only.
*/
int lzfx_decompress(const void *ibuf, unsigned int ilen, void *obuf, unsigned int *olen);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -13,8 +13,16 @@ endif
ALL_LANGUAGES=BG CS DA DE EN ES FI FR HR HU IT JA_JP LT NL NL_BE NO PL PT RU SK SL SR_CYRL SR_LATN SV TR UK YUE_HK ZH_CN ZH_TW ALL_LANGUAGES=BG CS DA DE EN ES FI FR HR HU IT JA_JP LT NL NL_BE NO PL PT RU SK SL SR_CYRL SR_LATN SV TR UK YUE_HK ZH_CN ZH_TW
# Defines for host tools
ifeq ($(HOST_CC),)
HOST_CC := gcc
endif
HOST_OUTPUT_DIR=Objects/host
# Enumerate all of the include directories # Enumerate all of the include directories
APP_INC_DIR = ./Core/Inc APP_INC_DIR = ./Core/Inc
LZFX_INC_DIR = ./Core/lzfx
MINIWARE_INC_CMSIS_DEVICE = ./Core/BSP/Miniware/Vendor/CMSIS/Device/ST/STM32F1xx/Include MINIWARE_INC_CMSIS_DEVICE = ./Core/BSP/Miniware/Vendor/CMSIS/Device/ST/STM32F1xx/Include
MINIWARE_CMSIS_CORE_INC_DIR = ./Core/BSP/Miniware/Vendor/CMSIS/Include MINIWARE_CMSIS_CORE_INC_DIR = ./Core/BSP/Miniware/Vendor/CMSIS/Include
MINIWARE_HAL_INC_DIR = ./Core/BSP/Miniware/Vendor/STM32F1xx_HAL_Driver/Inc MINIWARE_HAL_INC_DIR = ./Core/BSP/Miniware/Vendor/STM32F1xx_HAL_Driver/Inc
@@ -34,6 +42,7 @@ PINE_NMSIS_INC_DIR = ./Core/BSP/Pine64/Vendor/NMSIS/Core/Include
PINE_FREERTOS_PORT_INC_DIR = ./Core/BSP/Pine64/Vendor/OS/FreeRTOS/Source/portable/GCC PINE_FREERTOS_PORT_INC_DIR = ./Core/BSP/Pine64/Vendor/OS/FreeRTOS/Source/portable/GCC
SOURCE_THREADS_DIR = ./Core/Threads SOURCE_THREADS_DIR = ./Core/Threads
SOURCE_CORE_DIR = ./Core/Src SOURCE_CORE_DIR = ./Core/Src
SOURCE_LZFX_DIR = ./Core/lzfx
SOURCE_DRIVERS_DIR = ./Core/Drivers SOURCE_DRIVERS_DIR = ./Core/Drivers
INC_PD_DRIVERS_DIR = ./Core/Drivers/FUSB302 INC_PD_DRIVERS_DIR = ./Core/Drivers/FUSB302
SOURCE_MIDDLEWARES_DIR = ./Middlewares SOURCE_MIDDLEWARES_DIR = ./Middlewares
@@ -101,6 +110,7 @@ DEV_CXXFLAGS= -MMD -MP -MF "$(@:%.o=%.d)" -MT "$@"
endif endif
INCLUDES = -I$(APP_INC_DIR) \ INCLUDES = -I$(APP_INC_DIR) \
-I$(LZFX_INC_DIR) \
-I$(FRTOS_CMIS_INC_DIR) \ -I$(FRTOS_CMIS_INC_DIR) \
-I$(FRTOS_INC_DIR) \ -I$(FRTOS_INC_DIR) \
-I$(DRIVER_INC_DIR) \ -I$(DRIVER_INC_DIR) \
@@ -114,7 +124,8 @@ SOURCE := $(shell find $(SOURCE_THREADS_DIR) -type f -name '*.c') \
$(shell find $(SOURCE_CORE_DIR) -type f -name '*.c') \ $(shell find $(SOURCE_CORE_DIR) -type f -name '*.c') \
$(shell find $(SOURCE_DRIVERS_DIR) -type f -name '*.c') \ $(shell find $(SOURCE_DRIVERS_DIR) -type f -name '*.c') \
$(shell find $(DEVICE_BSP_DIR) -type f -name '*.c') \ $(shell find $(DEVICE_BSP_DIR) -type f -name '*.c') \
$(shell find $(SOURCE_MIDDLEWARES_DIR) -type f -name '*.c') $(shell find $(SOURCE_MIDDLEWARES_DIR) -type f -name '*.c') \
$(SOURCE_LZFX_DIR)/lzfx.c
SOURCE_CPP := $(shell find $(SOURCE_THREADS_DIR) -type f -name '*.cpp') \ SOURCE_CPP := $(shell find $(SOURCE_THREADS_DIR) -type f -name '*.cpp') \
$(shell find $(SOURCE_CORE_DIR) -type f -name '*.cpp') \ $(shell find $(SOURCE_CORE_DIR) -type f -name '*.cpp') \
$(shell find $(SOURCE_DRIVERS_DIR) -type f -name '*.cpp') \ $(shell find $(SOURCE_DRIVERS_DIR) -type f -name '*.cpp') \
@@ -296,10 +307,35 @@ all: $(ALL_FIRMWARE_TARGETS)
$(SIZE) --format=berkeley $< $(SIZE) --format=berkeley $<
$(OBJCOPY) $< -O binary $@ $(OBJCOPY) $< -O binary $@
$(HEXFILE_DIR)/$(model)_%.elf : $(OUT_OBJS_S) $(OUT_OBJS) $(OUT_OBJS_CPP) $(OUTPUT_DIR)/Core/Gen/Translation.%.o Makefile $(LDSCRIPT) $(HEXFILE_DIR)/$(model)_%.elf : \
$(OUT_OBJS_S) $(OUT_OBJS) $(OUT_OBJS_CPP) \
$(OUTPUT_DIR)/Core/Gen/Translation.%.o \
Makefile $(LDSCRIPT)
@test -d $(@D) || mkdir -p $(@D) @test -d $(@D) || mkdir -p $(@D)
@echo Linking $@ @echo Linking $@
@$(CPP) $(CXXFLAGS) $(OUT_OBJS_S) $(OUT_OBJS) $(OUT_OBJS_CPP) $(OUTPUT_DIR)/Core/Gen/Translation.$*.o $(LIBS) $(LINKER_FLAGS) -o$@ -Wl,-Map=$@.map @$(CPP) $(CXXFLAGS) $(OUT_OBJS_S) $(OUT_OBJS) $(OUT_OBJS_CPP) \
$(OUTPUT_DIR)/Core/Gen/Translation.$*.o \
$(LIBS) $(LINKER_FLAGS) -o$@ -Wl,-Map=$@.map
$(HEXFILE_DIR)/$(model)_string_compressed_%.elf : \
$(OUT_OBJS_S) $(OUT_OBJS) $(OUT_OBJS_CPP) \
$(OUTPUT_DIR)/Core/Gen/Translation_lzfx.%.o \
Makefile $(LDSCRIPT)
@test -d $(@D) || mkdir -p $(@D)
@echo Linking $@
@$(CPP) $(CXXFLAGS) $(OUT_OBJS_S) $(OUT_OBJS) $(OUT_OBJS_CPP) \
$(OUTPUT_DIR)/Core/Gen/Translation_lzfx.$*.o \
$(LIBS) $(LINKER_FLAGS) -o$@ -Wl,-Map=$@.map
$(HEXFILE_DIR)/$(model)_font_compressed_%.elf : \
$(OUT_OBJS_S) $(OUT_OBJS) $(OUT_OBJS_CPP) \
$(OUTPUT_DIR)/Core/Gen/Translation_lzfx_font.%.o \
Makefile $(LDSCRIPT)
@test -d $(@D) || mkdir -p $(@D)
@echo Linking $@
@$(CPP) $(CXXFLAGS) $(OUT_OBJS_S) $(OUT_OBJS) $(OUT_OBJS_CPP) \
$(OUTPUT_DIR)/Core/Gen/Translation_lzfx_font.$*.o \
$(LIBS) $(LINKER_FLAGS) -o$@ -Wl,-Map=$@.map
$(OUT_OBJS): $(OUTPUT_DIR)/%.o : %.c Makefile $(OUT_OBJS): $(OUTPUT_DIR)/%.o : %.c Makefile
@test -d $(@D) || mkdir -p $(@D) @test -d $(@D) || mkdir -p $(@D)
@@ -316,10 +352,61 @@ $(OUT_OBJS_S): $(OUTPUT_DIR)/%.o: %.S Makefile
@echo 'Building file: $<' @echo 'Building file: $<'
@$(AS) -c $(AFLAGS) $< -o $@ @$(AS) -c $(AFLAGS) $< -o $@
Core/Gen/Translation.%.cpp: ../Translations/translation_%.json Makefile ../Translations/make_translation.py ../Translations/translations_def.js ../Translations/font_tables.py ../Translations/wqy-bitmapsong/wenquanyi_9pt.bdf Core/Gen/Translation.%.cpp $(OUTPUT_DIR)/Core/Gen/translation.files/%.pickle: ../Translations/translation_%.json \
@test -d $(@D) || mkdir -p $(@D) ../Translations/make_translation.py \
../Translations/translations_def.js \
../Translations/font_tables.py \
Makefile ../Translations/wqy-bitmapsong/wenquanyi_9pt.bdf
@test -d Core/Gen || mkdir -p Core/Gen
@test -d $(OUTPUT_DIR)/Core/Gen/translation.files || mkdir -p $(OUTPUT_DIR)/Core/Gen/translation.files
@echo 'Generating translations for language $*' @echo 'Generating translations for language $*'
@python3 ../Translations/make_translation.py -o $(PWD)/$@ $* @python3 ../Translations/make_translation.py \
-o $(PWD)/Core/Gen/Translation.$*.cpp \
--output-pickled $(OUTPUT_DIR)/Core/Gen/translation.files/$*.pickle \
$*
#
# The recipes to produce compressed translation data:
#
$(OUTPUT_DIR)/Core/Gen/translation.files/%.o: Core/Gen/Translation.%.cpp
@test -d $(@D) || mkdir -p $(@D)
@echo Generating $@
@$(CPP) -c $(filter-out -flto -g3,$(CXXFLAGS)) $< -o $@
$(HOST_OUTPUT_DIR)/lzfx/liblzfx.so: Core/lzfx/lzfx.c
@test -d $(@D) || mkdir -p $(@D)
@echo Building host lzfx shared library $@
@$(HOST_CC) -Wno-unused-result -fPIC -shared -O $^ -o $@
$(OUTPUT_DIR)/Core/Gen/translation.files/%.strings.bin: $(OUTPUT_DIR)/Core/Gen/translation.files/%.o
@echo Dumping translation strings data from $<
@# Extract the raw strings data from the object file
@$(OBJCOPY) -O binary -j .rodata._ZL18TranslationIndices $< $(@D)/$*.data.TranslationIndices.bin
@test -s $(@D)/$*.data.TranslationIndices.bin || (rm $(@D)/$*.data.TranslationIndices.bin; echo 'ERROR: Output for .rodata._ZL18TranslationIndices is empty!' >&2; false)
@$(OBJCOPY) -O binary -j .rodata._ZL22TranslationStringsData $< $(@D)/$*.data.TranslationStrings.bin
@test -s $(@D)/$*.data.TranslationStrings.bin || (rm $(@D)/$*.data.TranslationStrings.bin; echo 'ERROR: Output for .rodata._ZL22TranslationStringsData is empty!' >&2; false)
@cat $(@D)/$*.data.TranslationIndices.bin $(@D)/$*.data.TranslationStrings.bin > $@
Core/Gen/Translation_lzfx.%.cpp: $(OUTPUT_DIR)/Core/Gen/translation.files/%.strings.bin $(OUTPUT_DIR)/Core/Gen/translation.files/%.pickle $(HOST_OUTPUT_DIR)/lzfx/liblzfx.so
@test -d $(@D) || mkdir -p $(@D)
@echo Generating lzfx compressed translation for $*
@python3 ../Translations/make_translation.py \
-o $(PWD)/Core/Gen/Translation_lzfx.$*.cpp \
--input-pickled $(OUTPUT_DIR)/Core/Gen/translation.files/$*.pickle \
--strings-bin $(OUTPUT_DIR)/Core/Gen/translation.files/$*.strings.bin \
$*
Core/Gen/Translation_lzfx_font.%.cpp: $(OUTPUT_DIR)/Core/Gen/translation.files/%.pickle $(HOST_OUTPUT_DIR)/lzfx/liblzfx.so
@test -d $(@D) || mkdir -p $(@D)
@echo Generating lzfx compressed translation for $*
@python3 ../Translations/make_translation.py \
-o $(PWD)/Core/Gen/Translation_lzfx_font.$*.cpp \
--input-pickled $(OUTPUT_DIR)/Core/Gen/translation.files/$*.pickle \
--compress-font \
$*
clean : clean :
rm -Rf Core/Gen rm -Rf Core/Gen
@@ -350,3 +437,5 @@ check-style:
-include $(OUT_OBJS:.o=.d) -include $(OUT_OBJS:.o=.d)
-include $(OUT_OBJS_CPP:.o=.d) -include $(OUT_OBJS_CPP:.o=.d)
-include $(OUTPUT_DIR)/Core/Gen/Translation.*.d -include $(OUTPUT_DIR)/Core/Gen/Translation.*.d
-include $(OUTPUT_DIR)/Core/Gen/Translation_*.d
-include $(OUTPUT_DIR)/Core/Gen/translation.files/*.d