Merge pull request #926 from alvinhochun/translation-refactor

Change internal representation of translation strings
This commit is contained in:
Ben V. Brown
2021-04-10 11:31:57 +10:00
committed by GitHub
9 changed files with 353 additions and 249 deletions

View File

@@ -11,7 +11,8 @@ import sys
from datetime import datetime
from itertools import chain
from pathlib import Path
from typing import Dict, List, TextIO, Tuple, Union
from typing import Dict, List, Optional, TextIO, Tuple, Union
from dataclasses import dataclass
from bdflib import reader as bdfreader
from bdflib.model import Font, Glyph
@@ -78,7 +79,7 @@ def write_start(f: TextIO):
f.write('#include "Translation.h"\n')
def get_constants() -> List[str]:
def get_constants() -> List[Tuple[str, str]]:
# Extra constants that are used in the firmware that are shared across all languages
return [
("SymbolPlus", "+"),
@@ -182,9 +183,10 @@ def get_letter_counts(defs: dict, lang: dict) -> List[str]:
if line:
for letter in line:
symbol_counts[letter] = symbol_counts.get(letter, 0) + 1
symbols_by_occurrence = sorted(symbol_counts.items(), key=lambda kv: (kv[1], kv[0]))
# swap to Big -> little sort order
symbols_by_occurrence = [x[0] for x in symbols_by_occurrence]
symbols_by_occurrence = [
x[0] for x in sorted(symbol_counts.items(), key=lambda kv: (kv[1], kv[0]))
]
symbols_by_occurrence.reverse()
return symbols_by_occurrence
@@ -233,10 +235,9 @@ def get_cjk_glyph(sym: str) -> str:
return s
def get_chars_from_font_index(index: int) -> str:
def get_bytes_from_font_index(index: int) -> bytes:
"""
Converts the font table index into its corresponding string escape
sequence(s).
Converts the font table index into its corresponding bytes
"""
# We want to be able to use more than 254 symbols (excluding \x00 null
@@ -272,7 +273,7 @@ def get_chars_from_font_index(index: int) -> str:
if page > 0x0F:
raise ValueError("page value out of range")
if page == 0:
return f"\\x{index:02X}"
return bytes([index])
else:
# Into extended range
# Leader is 0xFz where z is the page number
@@ -282,13 +283,17 @@ def get_chars_from_font_index(index: int) -> str:
if leader > 0xFF or value > 0xFF:
raise ValueError("value is out of range")
return f"\\x{leader:02X}\\x{value:02X}"
return bytes([leader, value])
def get_font_map_and_table(text_list: List[str]) -> Tuple[str, Dict[str, str]]:
def bytes_to_escaped(b: bytes) -> str:
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]]:
# the text list is sorted
# allocate out these in their order as number codes
symbol_map = {"\n": "\\x01"}
symbol_map: Dict[str, bytes] = {"\n": bytes([1])}
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"]
@@ -310,7 +315,7 @@ def get_font_map_and_table(text_list: List[str]) -> Tuple[str, Dict[str, str]]:
total_symbol_count = len(ordered_normal_sym_list) + len(ordered_cjk_sym_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_chars_from_font_index`):
# `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})"
@@ -322,7 +327,7 @@ def get_font_map_and_table(text_list: List[str]) -> Tuple[str, Dict[str, str]]:
for sym in chain(ordered_normal_sym_list, ordered_cjk_sym_list):
if sym in symbol_map:
raise ValueError("Symbol not found in symbol map")
symbol_map[sym] = get_chars_from_font_index(index)
symbol_map[sym] = get_bytes_from_font_index(index)
index += 1
font_table_strings = []
@@ -332,12 +337,16 @@ def get_font_map_and_table(text_list: List[str]) -> Tuple[str, Dict[str, str]]:
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}//{symbol_map[sym]} -> {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: str = font_small_table[sym]
font_small_table_strings.append(f"{font_line}//{symbol_map[sym]} -> {sym}")
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:
@@ -346,10 +355,12 @@ def get_font_map_and_table(text_list: List[str]) -> Tuple[str, Dict[str, str]]:
if font_line is None:
logging.error(f"Missing Large font element for {sym}")
sys.exit(1)
font_table_strings.append(f"{font_line}//{symbol_map[sym]} -> {sym}")
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"// {symbol_map[sym]} -> {sym}"
f"// {bytes_to_escaped(symbol_map[sym])} -> {sym}"
)
output_table = "const uint8_t USER_FONT_12[] = {\n"
@@ -365,9 +376,9 @@ def get_font_map_and_table(text_list: List[str]) -> Tuple[str, Dict[str, str]]:
return output_table, symbol_map
def convert_string(symbol_conversion_table: Dict[str, str], text: str) -> str:
# convert all of the symbols from the string into escapes for their content
output_string = ""
def convert_string_bytes(symbol_conversion_table: Dict[str, bytes], text: str) -> bytes:
# convert all of the symbols from the string into bytes for their content
output_string = b""
for c in text.replace("\\r", "").replace("\\n", "\n"):
if c not in symbol_conversion_table:
logging.error(f"Missing font definition for {c}")
@@ -377,6 +388,21 @@ def convert_string(symbol_conversion_table: Dict[str, str], text: str) -> str:
return output_string
def convert_string(symbol_conversion_table: Dict[str, bytes], text: str) -> str:
# convert all of the symbols from the string into escapes for their content
return bytes_to_escaped(convert_string_bytes(symbol_conversion_table, text))
def escape(string: str) -> str:
return json.dumps(string, ensure_ascii=False)
@dataclass
class TranslationItem:
info: str
str_index: int
def write_language(lang: dict, defs: dict, f: TextIO) -> None:
language_code: str = lang["languageCode"]
logging.info(f"Generating block for {language_code}")
@@ -394,28 +420,28 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
f.write(font_table_text)
f.write(f"\n// ---- {lang_name} ----\n\n")
# ----- Writing SettingsDescriptions
str_table: List[str] = []
str_group_messages: List[TranslationItem] = []
str_group_messageswarn: List[TranslationItem] = []
str_group_characters: List[TranslationItem] = []
str_group_settingdesc: List[TranslationItem] = []
str_group_settingshortnames: List[TranslationItem] = []
str_group_settingmenuentries: List[TranslationItem] = []
str_group_settingmenuentriesdesc: List[TranslationItem] = []
eid: str
# ----- Reading SettingsDescriptions
obj = lang["menuOptions"]
f.write("const char* SettingsDescriptions[] = {\n")
max_len = 25
index = 0
for mod in defs["menuOptions"]:
for index, mod in enumerate(defs["menuOptions"]):
eid = mod["id"]
if "feature" in mod:
f.write(f"#ifdef {mod['feature']}\n")
f.write(f" /* [{index:02d}] {eid.ljust(max_len)[:max_len]} */ ")
f.write(
f"\"{convert_string(symbol_conversion_table, obj[eid]['desc'])}\",//{obj[eid]['desc']} \n"
str_group_settingdesc.append(
TranslationItem(f"[{index:02d}] {eid}", len(str_table))
)
str_table.append(obj[eid]["desc"])
if "feature" in mod:
f.write("#endif\n")
index += 1
f.write("};\n\n")
# ----- Writing Message strings
# ----- Reading Message strings
obj = lang["messages"]
@@ -426,11 +452,8 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
source_text = mod["default"]
if eid in obj:
source_text = obj[eid]
translated_text = convert_string(symbol_conversion_table, source_text)
source_text = source_text.replace("\n", "_")
f.write(f'const char* {eid} = "{translated_text}";//{source_text} \n')
f.write("\n")
str_group_messages.append(TranslationItem(eid, len(str_table)))
str_table.append(source_text)
obj = lang["messagesWarn"]
@@ -443,22 +466,17 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
source_text = obj[eid][0] + "\n" + obj[eid][1]
else:
source_text = "\n" + obj[eid]
translated_text = convert_string(symbol_conversion_table, source_text)
source_text = source_text.replace("\n", "_")
f.write(f'const char* {eid} = "{translated_text}";//{source_text} \n')
str_group_messageswarn.append(TranslationItem(eid, len(str_table)))
str_table.append(source_text)
f.write("\n")
# ----- Writing Characters
# ----- Reading Characters
obj = lang["characters"]
for mod in defs["characters"]:
eid: str = mod["id"]
f.write(
f'const char* {eid} = "{convert_string(symbol_conversion_table, obj[eid])}";//{obj[eid]} \n'
)
f.write("\n")
eid = mod["id"]
str_group_characters.append(TranslationItem(eid, len(str_table)))
str_table.append(obj[eid])
# Write out firmware constant options
constants = get_constants()
@@ -475,13 +493,10 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
f.write(f'\t "{convert_string(symbol_conversion_table, c)}",//{c} \n')
f.write("};\n\n")
# ----- Writing SettingsDescriptions
# ----- Reading SettingsDescriptions
obj = lang["menuOptions"]
f.write("const char* SettingsShortNames[] = {\n")
max_len = 25
index = 0
for mod in defs["menuOptions"]:
for index, mod in enumerate(defs["menuOptions"]):
eid = mod["id"]
if isinstance(obj[eid]["text2"], list):
if not obj[eid]["text2"][1]:
@@ -490,25 +505,15 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
source_text = obj[eid]["text2"][0] + "\n" + obj[eid]["text2"][1]
else:
source_text = "\n" + obj[eid]["text2"]
if "feature" in mod:
f.write(f"#ifdef {mod['feature']}\n")
f.write(f" /* [{index:02d}] {eid.ljust(max_len)[:max_len]} */ ")
f.write(
f'{{ "{convert_string(symbol_conversion_table, source_text)}" }},//{obj[eid]["text2"]} \n'
str_group_settingshortnames.append(
TranslationItem(f"[{index:02d}] {eid}", len(str_table))
)
str_table.append(source_text)
if "feature" in mod:
f.write("#endif\n")
index += 1
f.write("};\n\n")
# ----- Writing Menu Groups
# ----- Reading Menu Groups
obj = lang["menuGroups"]
f.write(f"const char* SettingsMenuEntries[{len(obj)}] = {{\n")
max_len = 25
for mod in defs["menuGroups"]:
for index, mod in enumerate(defs["menuGroups"]):
eid = mod["id"]
if isinstance(obj[eid]["text2"], list):
if not obj[eid]["text2"][1]:
@@ -517,26 +522,135 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
source_text = obj[eid]["text2"][0] + "\n" + obj[eid]["text2"][1]
else:
source_text = "\n" + obj[eid]["text2"]
f.write(f" /* {eid.ljust(max_len)[:max_len]} */ ")
f.write(
f'"{convert_string(symbol_conversion_table, source_text)}",//{obj[eid]["text2"]} \n'
str_group_settingmenuentries.append(
TranslationItem(f"[{index:02d}] {eid}", len(str_table))
)
str_table.append(source_text)
f.write("};\n\n")
# ----- Writing Menu Groups Descriptions
# ----- Reading Menu Groups Descriptions
obj = lang["menuGroups"]
f.write(f"const char* SettingsMenuEntriesDescriptions[{(len(obj))}] = {{\n")
max_len = 25
for mod in defs["menuGroups"]:
for index, mod in enumerate(defs["menuGroups"]):
eid = mod["id"]
f.write(f" /* {eid.ljust(max_len)[:max_len]} */ ")
f.write(
f"\"{convert_string(symbol_conversion_table, (obj[eid]['desc']))}\",//{obj[eid]['desc']} \n"
str_group_settingmenuentriesdesc.append(
TranslationItem(f"[{index:02d}] {eid}", len(str_table))
)
str_table.append(obj[eid]["desc"])
f.write("\n")
@dataclass
class RemappedTranslationItem:
str_index: int
str_start_offset: int = 0
# ----- Perform suffix merging optimization:
#
# We sort the backward strings so that strings with the same suffix will
# be next to each other, e.g.:
# "ef\0",
# "cdef\0",
# "abcdef\0",
backward_sorted_table: List[Tuple[int, str, bytes]] = sorted(
(
(i, s, bytes(reversed(convert_string_bytes(symbol_conversion_table, s))))
for i, s in enumerate(str_table)
),
key=lambda x: x[2],
)
str_remapping: List[Optional[RemappedTranslationItem]] = [None] * len(str_table)
for i, (str_index, source_str, converted) in enumerate(backward_sorted_table[:-1]):
j = i
while backward_sorted_table[j + 1][2].startswith(converted):
j += 1
if j != i:
str_remapping[str_index] = RemappedTranslationItem(
str_index=backward_sorted_table[j][0],
str_start_offset=len(backward_sorted_table[j][2]) - len(converted),
)
# ----- Write the string table:
str_offsets = [-1] * len(str_table)
offset = 0
write_null = False
f.write("const char TranslationStringsData[] = {\n")
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:
write_null = False
continue
# Find what items use this string
str_used_by = [i] + [
j for j, r in enumerate(str_remapping) if r and r.str_index == i
]
for j in str_used_by:
for group, pre_info in [
(str_group_messages, "messages"),
(str_group_messageswarn, "messagesWarn"),
(str_group_characters, "characters"),
(str_group_settingdesc, "SettingsDescriptions"),
(str_group_settingshortnames, "SettingsShortNames"),
(str_group_settingmenuentries, "SettingsMenuEntries"),
(str_group_settingmenuentriesdesc, "SettingsMenuEntriesDescriptions"),
]:
for item in group:
if item.str_index == j:
f.write(f" // - {pre_info} {item.info}\n")
if j == i:
f.write(f" // {offset: >4}: {escape(source_str)}\n")
str_offsets[j] = offset
else:
remapped = str_remapping[j]
assert remapped is not None
f.write(
f" // {offset + remapped.str_start_offset: >4}: {escape(str_table[j])}\n"
)
str_offsets[j] = offset + remapped.str_start_offset
converted_str = convert_string(symbol_conversion_table, source_str)
f.write(f' "{converted_str}"')
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
offset += len(converted_str) // 4 + 1
f.write("\n};\n\n")
def get_offset(idx: int) -> int:
assert str_offsets[idx] >= 0
return str_offsets[idx]
f.write("const TranslationIndexTable TranslationIndices = {\n")
# ----- Write the messages string indices:
for group in [str_group_messages, str_group_messageswarn, str_group_characters]:
for item in group:
f.write(
f" .{item.info} = {get_offset(item.str_index)}, // {escape(str_table[item.str_index])}\n"
)
f.write("\n")
# ----- Write the settings index tables:
for group, name in [
(str_group_settingdesc, "SettingsDescriptions"),
(str_group_settingshortnames, "SettingsShortNames"),
(str_group_settingmenuentries, "SettingsMenuEntries"),
(str_group_settingmenuentriesdesc, "SettingsMenuEntriesDescriptions"),
]:
max_len = 30
f.write(f" .{name} = {{\n")
for item in group:
f.write(
f" /* {item.info.ljust(max_len)[:max_len]} */ {get_offset(item.str_index)}, // {escape(str_table[item.str_index])}\n"
)
f.write(f" }}, // {name}\n\n")
f.write("}; // TranslationIndices\n\n")
f.write("const TranslationIndexTable *const Tr = &TranslationIndices;\n")
f.write("const char *const TranslationStrings = TranslationStringsData;\n\n")
f.write("};\n\n")
f.write(
f"const bool HasFahrenheit = {('true' if lang.get('tempUnitFahrenheit', True) else 'false')};\n"
)
@@ -547,15 +661,18 @@ def write_language(lang: dict, defs: dict, f: TextIO) -> None:
f.write(
f"static_assert(static_cast<uint8_t>(SettingsItemIndex::{eid}) == {i});\n"
)
f.write(
f"static_assert(static_cast<uint8_t>(SettingsItemIndex::NUM_ITEMS) == {len(defs['menuOptions'])});\n"
)
def read_version() -> str:
with open(HERE.parent / "source" / "version.h") as version_file:
for line in version_file:
if re.findall(r"^.*(?<=(#define)).*(?<=(BUILD_VERSION))", line):
line = re.findall(r"\"(.+?)\"", line)
if line:
version = line[0]
matches = re.findall(r"\"(.+?)\"", line)
if matches:
version = matches[0]
try:
version += f".{subprocess.check_output(['git', 'rev-parse', '--short=7', 'HEAD']).strip().decode('ascii').upper()}"
# --short=7: the shorted hash with 7 digits. Increase/decrease if needed!

View File

@@ -3,20 +3,26 @@ import unittest
class TestMakeTranslation(unittest.TestCase):
def test_get_chars_from_font_index(self):
from make_translation import get_chars_from_font_index
def test_get_bytes_from_font_index(self):
from make_translation import get_bytes_from_font_index
self.assertEqual(get_chars_from_font_index(2), "\\x02")
self.assertEqual(get_chars_from_font_index(239), "\\xEF")
self.assertEqual(get_chars_from_font_index(240), "\\xF0")
self.assertEqual(get_chars_from_font_index(241), "\\xF1\\x01")
self.assertEqual(get_chars_from_font_index(495), "\\xF1\\xFF")
self.assertEqual(get_chars_from_font_index(496), "\\xF2\\x01")
self.assertEqual(get_chars_from_font_index(750), "\\xF2\\xFF")
self.assertEqual(get_chars_from_font_index(751), "\\xF3\\x01")
self.assertEqual(get_chars_from_font_index(0x10 * 0xFF - 15), "\\xFF\\xFF")
self.assertEqual(get_bytes_from_font_index(2), b"\x02")
self.assertEqual(get_bytes_from_font_index(239), b"\xEF")
self.assertEqual(get_bytes_from_font_index(240), b"\xF0")
self.assertEqual(get_bytes_from_font_index(241), b"\xF1\x01")
self.assertEqual(get_bytes_from_font_index(495), b"\xF1\xFF")
self.assertEqual(get_bytes_from_font_index(496), b"\xF2\x01")
self.assertEqual(get_bytes_from_font_index(750), b"\xF2\xFF")
self.assertEqual(get_bytes_from_font_index(751), b"\xF3\x01")
self.assertEqual(get_bytes_from_font_index(0x10 * 0xFF - 15), b"\xFF\xFF")
with self.assertRaises(ValueError):
get_chars_from_font_index(0x10 * 0xFF - 14)
get_bytes_from_font_index(0x10 * 0xFF - 14)
def test_bytes_to_escaped(self):
from make_translation import bytes_to_escaped
self.assertEqual(bytes_to_escaped(b"\x00"), "\\x00")
self.assertEqual(bytes_to_escaped(b"\xF1\xAB"), "\\xF1\\xAB")
if __name__ == "__main__":

View File

@@ -1,9 +1,6 @@
var def =
{
"messages": [
{
"id": "SettingsCalibrationDone"
},
{
"id": "SettingsCalibrationWarning"
},
@@ -23,15 +20,6 @@ var def =
"maxLen": 11,
"note": "Preferably end with a space"
},
{
"id": "WarningTipTempString",
"maxLen": 12,
"note": "Preferably end with a space"
},
{
"id": "BadTipString",
"maxLen": 8
},
{
"id": "SleepingSimpleString",
"maxLen": 4
@@ -40,14 +28,6 @@ var def =
"id": "SleepingAdvancedString",
"maxLen": 16
},
{
"id": "WarningSimpleString",
"maxLen": 4
},
{
"id": "WarningAdvancedString",
"maxLen": 16
},
{
"id": "SleepingTipAdvancedString",
"maxLen": 6
@@ -80,11 +60,6 @@ var def =
{
"id": "OffString",
"maxLen": 3
},
{
"id": "YourGainMessage",
"maxLen": 8,
"default": "Your Gain"
}
],
"messagesWarn": [

View File

@@ -12,61 +12,6 @@ extern const uint8_t USER_FONT_12[];
extern const uint8_t USER_FONT_6x8[];
extern const bool HasFahrenheit;
extern const char *SettingsShortNames[];
extern const char *SettingsDescriptions[];
extern const char *SettingsMenuEntries[];
extern const char *SettingsCalibrationDone;
extern const char *SettingsCalibrationWarning;
extern const char *SettingsResetWarning;
extern const char *UVLOWarningString;
extern const char *UndervoltageString;
extern const char *InputVoltageString;
extern const char *WarningTipTempString;
extern const char *BadTipString;
extern const char *SleepingSimpleString;
extern const char *SleepingAdvancedString;
extern const char *WarningSimpleString;
extern const char *WarningAdvancedString;
extern const char *SleepingTipAdvancedString;
extern const char *IdleTipString;
extern const char *IdleSetString;
extern const char *TipDisconnectedString;
extern const char *SolderingAdvancedPowerPrompt;
extern const char *OffString;
extern const char *YourGainMessage;
extern const char *ResetOKMessage;
extern const char *SettingsResetMessage;
extern const char *NoAccelerometerMessage;
extern const char *NoPowerDeliveryMessage;
extern const char *LockingKeysString;
extern const char *UnlockingKeysString;
extern const char *WarningKeysLockedString;
extern const char *SettingRightChar;
extern const char *SettingLeftChar;
extern const char *SettingAutoChar;
extern const char *SettingStartSolderingChar;
extern const char *SettingStartSleepChar;
extern const char *SettingStartSleepOffChar;
extern const char *SettingStartNoneChar;
extern const char *SettingSensitivityOff;
extern const char *SettingSensitivityLow;
extern const char *SettingSensitivityMedium;
extern const char *SettingSensitivityHigh;
extern const char *SettingLockDisableChar;
extern const char *SettingLockBoostChar;
extern const char *SettingLockFullChar;
extern const char *SettingNAChar;
extern const char *SettingOffChar;
extern const char *SettingFastChar;
extern const char *SettingMediumChar;
extern const char *SettingSlowChar;
extern const char *TipModelStrings[];
extern const char *DebugMenu[];
extern const char *SymbolPlus;
extern const char *SymbolMinus;
extern const char *SymbolSpace;
@@ -113,10 +58,66 @@ enum class SettingsItemIndex : uint8_t {
AnimSpeed,
PowerPulseWait,
PowerPulseDuration,
NUM_ITEMS,
};
struct TranslationIndexTable {
uint16_t SettingsCalibrationWarning;
uint16_t SettingsResetWarning;
uint16_t UVLOWarningString;
uint16_t UndervoltageString;
uint16_t InputVoltageString;
uint16_t SleepingSimpleString;
uint16_t SleepingAdvancedString;
uint16_t SleepingTipAdvancedString;
uint16_t IdleTipString;
uint16_t IdleSetString;
uint16_t TipDisconnectedString;
uint16_t SolderingAdvancedPowerPrompt;
uint16_t OffString;
uint16_t ResetOKMessage;
uint16_t SettingsResetMessage;
uint16_t NoAccelerometerMessage;
uint16_t NoPowerDeliveryMessage;
uint16_t LockingKeysString;
uint16_t UnlockingKeysString;
uint16_t WarningKeysLockedString;
uint16_t SettingRightChar;
uint16_t SettingLeftChar;
uint16_t SettingAutoChar;
uint16_t SettingFastChar;
uint16_t SettingSlowChar;
uint16_t SettingMediumChar;
uint16_t SettingOffChar;
uint16_t SettingStartSolderingChar;
uint16_t SettingStartSleepChar;
uint16_t SettingStartSleepOffChar;
uint16_t SettingStartNoneChar;
uint16_t SettingSensitivityOff;
uint16_t SettingSensitivityLow;
uint16_t SettingSensitivityMedium;
uint16_t SettingSensitivityHigh;
uint16_t SettingLockDisableChar;
uint16_t SettingLockBoostChar;
uint16_t SettingLockFullChar;
uint16_t SettingNAChar;
uint16_t SettingsDescriptions[static_cast<uint32_t>(SettingsItemIndex::NUM_ITEMS)];
uint16_t SettingsShortNames[static_cast<uint32_t>(SettingsItemIndex::NUM_ITEMS)];
uint16_t SettingsMenuEntries[5];
uint16_t SettingsMenuEntriesDescriptions[5]; // unused
};
extern const TranslationIndexTable *const Tr;
extern const char *const TranslationStrings;
constexpr uint8_t settings_item_index(const SettingsItemIndex i) { return static_cast<uint8_t>(i); }
// Use a constexpr function for type-checking.
#define SETTINGS_DESC(i) (SettingsDescriptions[settings_item_index(i)])
#define SETTINGS_DESC(i) (settings_item_index(i) + 1)
const char *translatedString(uint16_t index);
#endif /* TRANSLATION_H_ */

View File

@@ -21,7 +21,9 @@
// Struct for holding the function pointers and descriptions
typedef struct {
const char *description;
// The settings description index, please use the `SETTINGS_DESC` macro with
// the `SettingsItemIndex` enum. Use 0 for no description.
uint8_t description;
// return true if increment reached the maximum value
bool (*const incrementHandler)(void);
bool (*const draw)(void);

View File

@@ -0,0 +1,3 @@
#include "Translation.h"
const char *translatedString(uint16_t offset) { return TranslationStrings + offset; }

View File

@@ -139,12 +139,12 @@ const menuitem rootSettingsMenu[]{
* Advanced Menu
* Exit
*/
{nullptr, settings_enterPowerMenu, settings_displayPowerMenu}, /*Power*/
{nullptr, settings_enterSolderingMenu, settings_displaySolderingMenu}, /*Soldering*/
{nullptr, settings_enterPowerSavingMenu, settings_displayPowerSavingMenu}, /*Sleep Options Menu*/
{nullptr, settings_enterUIMenu, settings_displayUIMenu}, /*UI Menu*/
{nullptr, settings_enterAdvancedMenu, settings_displayAdvancedMenu}, /*Advanced Menu*/
{nullptr, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
{0, settings_enterPowerMenu, settings_displayPowerMenu}, /*Power*/
{0, settings_enterSolderingMenu, settings_displaySolderingMenu}, /*Soldering*/
{0, settings_enterPowerSavingMenu, settings_displayPowerSavingMenu}, /*Sleep Options Menu*/
{0, settings_enterUIMenu, settings_displayUIMenu}, /*UI Menu*/
{0, settings_enterAdvancedMenu, settings_displayAdvancedMenu}, /*Advanced Menu*/
{0, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
};
const menuitem powerMenu[] = {
@@ -158,7 +158,7 @@ const menuitem powerMenu[] = {
#ifdef POW_QC
{SETTINGS_DESC(SettingsItemIndex::QCMaxVoltage), settings_setQCInputV, settings_displayQCInputV}, /*Voltage input*/
#endif
{nullptr, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
{0, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
};
const menuitem solderingMenu[] = {
/*
@@ -173,7 +173,7 @@ const menuitem solderingMenu[] = {
{SETTINGS_DESC(SettingsItemIndex::TempChangeShortStep), settings_setTempChangeShortStep, settings_displayTempChangeShortStep}, /*Temp change short step*/
{SETTINGS_DESC(SettingsItemIndex::TempChangeLongStep), settings_setTempChangeLongStep, settings_displayTempChangeLongStep}, /*Temp change long step*/
{SETTINGS_DESC(SettingsItemIndex::LockingMode), settings_setLockingMode, settings_displayLockingMode}, /*Locking Mode*/
{nullptr, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
{0, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
};
const menuitem UIMenu[] = {
/*
@@ -192,7 +192,7 @@ const menuitem UIMenu[] = {
{SETTINGS_DESC(SettingsItemIndex::ReverseButtonTempChange), settings_setReverseButtonTempChangeEnabled, settings_displayReverseButtonTempChangeEnabled}, /* Reverse Temp change buttons + - */
{SETTINGS_DESC(SettingsItemIndex::AnimSpeed), settings_setAnimationSpeed, settings_displayAnimationSpeed}, /*Animation Speed adjustment */
{SETTINGS_DESC(SettingsItemIndex::AnimLoop), settings_setAnimationLoop, settings_displayAnimationLoop}, /*Animation Loop switch */
{nullptr, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
{0, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
};
const menuitem PowerSavingMenu[] = {
/*
@@ -208,7 +208,7 @@ const menuitem PowerSavingMenu[] = {
#ifdef HALL_SENSOR
{SETTINGS_DESC(SettingsItemIndex::HallEffSensitivity), settings_setHallEffect, settings_displayHallEffect}, /* HallEffect Sensitivity*/
#endif
{nullptr, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
{0, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
};
const menuitem advancedMenu[] = {
@@ -234,7 +234,7 @@ const menuitem advancedMenu[] = {
{SETTINGS_DESC(SettingsItemIndex::PowerPulsePower), settings_setPowerPulse, settings_displayPowerPulse}, /*Power Pulse adjustment */
{SETTINGS_DESC(SettingsItemIndex::PowerPulseWait), settings_setPowerPulseWait, settings_displayPowerPulseWait}, /*Power Pulse Wait adjustment*/
{SETTINGS_DESC(SettingsItemIndex::PowerPulseDuration), settings_setPowerPulseDuration, settings_displayPowerPulseDuration}, /*Power Pulse Duration adjustment*/
{nullptr, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
{0, nullptr, nullptr} // end of menu marker. DO NOT REMOVE
};
/**
@@ -247,7 +247,7 @@ const menuitem advancedMenu[] = {
static void printShortDescription(SettingsItemIndex settingsItemIndex, uint16_t cursorCharPosition) {
// print short description (default single line, explicit double line)
uint8_t shortDescIndex = static_cast<uint8_t>(settingsItemIndex);
OLED::printWholeScreen(SettingsShortNames[shortDescIndex]);
OLED::printWholeScreen(translatedString(Tr->SettingsShortNames[shortDescIndex]));
// prepare cursor for value
// make room for scroll indicator
@@ -362,7 +362,7 @@ static bool settings_displayInputMinVRange(void) {
OLED::printNumber(systemSettings.minVoltageCells % 10, 1, FontStyle::LARGE);
} else {
printShortDescription(SettingsItemIndex::MinVolCell, 5);
OLED::print(SettingNAChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingNAChar), FontStyle::LARGE);
}
return false;
}
@@ -437,7 +437,7 @@ static bool settings_setSleepTime(void) {
static bool settings_displaySleepTime(void) {
printShortDescription(SettingsItemIndex::SleepTimeout, 5);
if (systemSettings.SleepTime == 0) {
OLED::print(OffString, FontStyle::LARGE);
OLED::print(translatedString(Tr->OffString), FontStyle::LARGE);
} else if (systemSettings.SleepTime < 6) {
OLED::printNumber(systemSettings.SleepTime * 10, 2, FontStyle::LARGE);
OLED::print(SymbolSeconds, FontStyle::LARGE);
@@ -461,7 +461,7 @@ static bool settings_setShutdownTime(void) {
static bool settings_displayShutdownTime(void) {
printShortDescription(SettingsItemIndex::ShutdownTimeout, 5);
if (systemSettings.ShutdownTime == 0) {
OLED::print(OffString, FontStyle::LARGE);
OLED::print(translatedString(Tr->OffString), FontStyle::LARGE);
} else {
OLED::printNumber(systemSettings.ShutdownTime, 2, FontStyle::LARGE);
OLED::print(SymbolMinutes, FontStyle::LARGE);
@@ -546,7 +546,7 @@ static bool settings_setPowerLimit(void) {
static bool settings_displayPowerLimit(void) {
printShortDescription(SettingsItemIndex::PowerLimit, 5);
if (systemSettings.powerLimit == 0) {
OLED::print(OffString, FontStyle::LARGE);
OLED::print(translatedString(Tr->OffString), FontStyle::LARGE);
} else {
OLED::printNumber(systemSettings.powerLimit, 2, FontStyle::LARGE);
OLED::print(SymbolWatts, FontStyle::LARGE);
@@ -564,7 +564,7 @@ static bool settings_setScrollSpeed(void) {
static bool settings_displayScrollSpeed(void) {
printShortDescription(SettingsItemIndex::ScrollingSpeed, 7);
OLED::print((systemSettings.descriptionScrollSpeed) ? SettingFastChar : SettingSlowChar, FontStyle::LARGE);
OLED::print(translatedString((systemSettings.descriptionScrollSpeed) ? Tr->SettingFastChar : Tr->SettingSlowChar), FontStyle::LARGE);
return false;
}
@@ -592,16 +592,16 @@ static bool settings_displayDisplayRotation(void) {
switch (systemSettings.OrientationMode) {
case 0:
OLED::print(SettingRightChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingRightChar), FontStyle::LARGE);
break;
case 1:
OLED::print(SettingLeftChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingLeftChar), FontStyle::LARGE);
break;
case 2:
OLED::print(SettingAutoChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingAutoChar), FontStyle::LARGE);
break;
default:
OLED::print(SettingRightChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingRightChar), FontStyle::LARGE);
break;
}
return false;
@@ -637,7 +637,7 @@ static bool settings_displayBoostTemp(void) {
if (systemSettings.BoostTemp) {
OLED::printNumber(systemSettings.BoostTemp, 3, FontStyle::LARGE);
} else {
OLED::print(OffString, FontStyle::LARGE);
OLED::print(translatedString(Tr->OffString), FontStyle::LARGE);
}
return false;
}
@@ -653,19 +653,19 @@ static bool settings_displayAutomaticStartMode(void) {
switch (systemSettings.autoStartMode) {
case 0:
OLED::print(SettingStartNoneChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingStartNoneChar), FontStyle::LARGE);
break;
case 1:
OLED::print(SettingStartSolderingChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingStartSolderingChar), FontStyle::LARGE);
break;
case 2:
OLED::print(SettingStartSleepChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingStartSleepChar), FontStyle::LARGE);
break;
case 3:
OLED::print(SettingStartSleepOffChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingStartSleepOffChar), FontStyle::LARGE);
break;
default:
OLED::print(SettingStartNoneChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingStartNoneChar), FontStyle::LARGE);
break;
}
return false;
@@ -682,16 +682,16 @@ static bool settings_displayLockingMode(void) {
switch (systemSettings.lockingMode) {
case 0:
OLED::print(SettingLockDisableChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingLockDisableChar), FontStyle::LARGE);
break;
case 1:
OLED::print(SettingLockBoostChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingLockBoostChar), FontStyle::LARGE);
break;
case 2:
OLED::print(SettingLockFullChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingLockFullChar), FontStyle::LARGE);
break;
default:
OLED::print(SettingLockDisableChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingLockDisableChar), FontStyle::LARGE);
break;
}
return false;
@@ -709,9 +709,9 @@ static bool settings_displayCoolingBlinkEnabled(void) {
}
static bool settings_setResetSettings(void) {
if (userConfirmation(SettingsResetWarning)) {
if (userConfirmation(translatedString(Tr->SettingsResetWarning))) {
resetSettings();
warnUser(ResetOKMessage, 2 * TICKS_SECOND);
warnUser(translatedString(Tr->ResetOKMessage), 2 * TICKS_SECOND);
}
return false;
}
@@ -754,7 +754,7 @@ static void setTipOffset() {
// If not only do single point tuning as per usual
static bool settings_setCalibrate(void) {
if (userConfirmation(SettingsCalibrationWarning)) {
if (userConfirmation(translatedString(Tr->SettingsCalibrationWarning))) {
// User confirmed
// So we now perform the actual calculation
setTipOffset();
@@ -881,7 +881,7 @@ static bool settings_displayPowerPulse(void) {
OLED::print(SymbolDot, FontStyle::LARGE);
OLED::printNumber(systemSettings.KeepAwakePulse % 10, 1, FontStyle::LARGE);
} else {
OLED::print(OffString, FontStyle::LARGE);
OLED::print(translatedString(Tr->OffString), FontStyle::LARGE);
}
return false;
}
@@ -907,16 +907,16 @@ static bool settings_displayAnimationSpeed(void) {
printShortDescription(SettingsItemIndex::AnimSpeed, 7);
switch (systemSettings.animationSpeed) {
case settingOffSpeed_t::SLOW:
OLED::print(SettingSlowChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingSlowChar), FontStyle::LARGE);
break;
case settingOffSpeed_t::MEDIUM:
OLED::print(SettingMediumChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingMediumChar), FontStyle::LARGE);
break;
case settingOffSpeed_t::FAST:
OLED::print(SettingFastChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingFastChar), FontStyle::LARGE);
break;
default:
OLED::print(SettingOffChar, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingOffChar), FontStyle::LARGE);
break;
}
return false;
@@ -967,17 +967,17 @@ static bool settings_displayHallEffect(void) {
printShortDescription(SettingsItemIndex::HallEffSensitivity, 7);
switch (systemSettings.hallEffectSensitivity) {
case 1:
OLED::print(SettingSensitivityLow, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingSensitivityLow), FontStyle::LARGE);
break;
case 2:
OLED::print(SettingSensitivityMedium, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingSensitivityMedium), FontStyle::LARGE);
break;
case 3:
OLED::print(SettingSensitivityHigh, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingSensitivityHigh), FontStyle::LARGE);
break;
case 0:
default:
OLED::print(SettingSensitivityOff, FontStyle::LARGE);
OLED::print(translatedString(Tr->SettingSensitivityOff), FontStyle::LARGE);
break;
}
return false;
@@ -996,7 +996,7 @@ static bool animOpenState = false;
static void displayMenu(size_t index) {
// Call into the menu
// Draw title
OLED::printWholeScreen(SettingsMenuEntries[index]);
OLED::printWholeScreen(translatedString(Tr->SettingsMenuEntries[index]));
// Draw symbol
// 16 pixel wide image
// 2 pixel wide scrolling indicator
@@ -1110,7 +1110,7 @@ void gui_Menu(const menuitem *menu) {
OLED::setCursor(0, 0);
// If the user has hesitated for >=3 seconds, show the long text
// Otherwise "draw" the option
if ((xTaskGetTickCount() - lastButtonTime < (TICKS_SECOND * 3)) || menu[currentScreen].description == nullptr) {
if ((xTaskGetTickCount() - lastButtonTime < (TICKS_SECOND * 3)) || menu[currentScreen].description == 0) {
lcdRefresh = true;
OLED::clearScreen();
if (menu[currentScreen].draw()) {
@@ -1128,7 +1128,7 @@ void gui_Menu(const menuitem *menu) {
// Draw description
if (descriptionStart == 0)
descriptionStart = xTaskGetTickCount();
const char *description = menu[currentScreen].description;
const char *description = translatedString(Tr->SettingsDescriptions[menu[currentScreen].description - 1]);
// lower the value - higher the speed
int16_t descriptionWidth = FONT_12_WIDTH * (str_display_len(description) + 7);
int16_t descriptionOffset = ((xTaskGetTickCount() - descriptionStart) / (systemSettings.descriptionScrollSpeed == 1 ? (TICKS_100MS / 10) : (TICKS_100MS / 5)));

View File

@@ -107,13 +107,13 @@ static bool checkVoltageForExit() {
OLED::clearScreen();
OLED::setCursor(0, 0);
if (systemSettings.detailedSoldering) {
OLED::print(UndervoltageString, FontStyle::SMALL);
OLED::print(translatedString(Tr->UndervoltageString), FontStyle::SMALL);
OLED::setCursor(0, 8);
OLED::print(InputVoltageString, FontStyle::SMALL);
OLED::print(translatedString(Tr->InputVoltageString), FontStyle::SMALL);
printVoltage();
OLED::print(SymbolVolts, FontStyle::SMALL);
} else {
OLED::print(UVLOWarningString, FontStyle::LARGE);
OLED::print(translatedString(Tr->UVLOWarningString), FontStyle::LARGE);
}
OLED::refresh();
@@ -326,9 +326,9 @@ static int gui_SolderingSleepingMode(bool stayOff, bool autoStarted) {
OLED::clearScreen();
OLED::setCursor(0, 0);
if (systemSettings.detailedSoldering) {
OLED::print(SleepingAdvancedString, FontStyle::SMALL);
OLED::print(translatedString(Tr->SleepingAdvancedString), FontStyle::SMALL);
OLED::setCursor(0, 8);
OLED::print(SleepingTipAdvancedString, FontStyle::SMALL);
OLED::print(translatedString(Tr->SleepingTipAdvancedString), FontStyle::SMALL);
OLED::printNumber(tipTemp, 3, FontStyle::SMALL);
if (systemSettings.temperatureInF)
OLED::print(SymbolDegF, FontStyle::SMALL);
@@ -340,7 +340,7 @@ static int gui_SolderingSleepingMode(bool stayOff, bool autoStarted) {
printVoltage();
OLED::print(SymbolVolts, FontStyle::SMALL);
} else {
OLED::print(SleepingSimpleString, FontStyle::LARGE);
OLED::print(translatedString(Tr->SleepingSimpleString), FontStyle::LARGE);
OLED::printNumber(tipTemp, 3, FontStyle::LARGE);
if (systemSettings.temperatureInF)
OLED::drawSymbol(0);
@@ -462,7 +462,7 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
case BUTTON_BOTH_LONG:
// Unlock buttons
buttonsLocked = false;
warnUser(UnlockingKeysString, TICKS_SECOND);
warnUser(translatedString(Tr->UnlockingKeysString), TICKS_SECOND);
break;
case BUTTON_F_LONG:
// if boost mode is enabled turn it on
@@ -476,7 +476,7 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
case BUTTON_F_SHORT:
case BUTTON_B_SHORT:
// Do nothing and display a lock warming
warnUser(WarningKeysLockedString, TICKS_SECOND / 2);
warnUser(translatedString(Tr->WarningKeysLockedString), TICKS_SECOND / 2);
break;
default:
break;
@@ -511,7 +511,7 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
if (systemSettings.lockingMode != 0) {
// Lock buttons
buttonsLocked = true;
warnUser(LockingKeysString, TICKS_SECOND);
warnUser(translatedString(Tr->LockingKeysString), TICKS_SECOND);
}
break;
default:
@@ -523,7 +523,7 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
OLED::clearScreen();
// Draw in the screen details
if (systemSettings.detailedSoldering) {
OLED::print(SolderingAdvancedPowerPrompt, FontStyle::SMALL); // Power:
OLED::print(translatedString(Tr->SolderingAdvancedPowerPrompt), FontStyle::SMALL); // Power:
OLED::printNumber(x10WattHistory.average() / 10, 2, FontStyle::SMALL);
OLED::print(SymbolDot, FontStyle::SMALL);
OLED::printNumber(x10WattHistory.average() % 10, 1, FontStyle::SMALL);
@@ -535,7 +535,7 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
}
OLED::setCursor(0, 8);
OLED::print(SleepingTipAdvancedString, FontStyle::SMALL);
OLED::print(translatedString(Tr->SleepingTipAdvancedString), FontStyle::SMALL);
gui_drawTipTemp(true, FontStyle::SMALL);
if (boostModeOn) {
@@ -713,7 +713,7 @@ void showDebugMenu(void) {
void showWarnings() {
// Display alert if settings were reset
if (settingsWereReset) {
warnUser(SettingsResetMessage, 10 * TICKS_SECOND);
warnUser(translatedString(Tr->SettingsResetMessage), 10 * TICKS_SECOND);
}
#ifndef NO_WARN_MISSING
// We also want to alert if accel or pd is not detected / not responding
@@ -727,7 +727,7 @@ void showWarnings() {
if (systemSettings.accelMissingWarningCounter < 2) {
systemSettings.accelMissingWarningCounter++;
saveSettings();
warnUser(NoAccelerometerMessage, 10 * TICKS_SECOND);
warnUser(translatedString(Tr->NoAccelerometerMessage), 10 * TICKS_SECOND);
}
}
#ifdef POW_PD
@@ -736,7 +736,7 @@ void showWarnings() {
if (systemSettings.pdMissingWarningCounter < 2) {
systemSettings.pdMissingWarningCounter++;
saveSettings();
warnUser(NoPowerDeliveryMessage, 10 * TICKS_SECOND);
warnUser(translatedString(Tr->NoPowerDeliveryMessage), 10 * TICKS_SECOND);
}
}
#endif
@@ -844,16 +844,16 @@ void startGUITask(void const *argument __unused) {
OLED::setCursor(0, 0);
if (systemSettings.detailedIDLE) {
if (tipTemp > tipDisconnectedThres) {
OLED::print(TipDisconnectedString, FontStyle::SMALL);
OLED::print(translatedString(Tr->TipDisconnectedString), FontStyle::SMALL);
} else {
OLED::print(IdleTipString, FontStyle::SMALL);
OLED::print(translatedString(Tr->IdleTipString), FontStyle::SMALL);
gui_drawTipTemp(false, FontStyle::SMALL);
OLED::print(IdleSetString, FontStyle::SMALL);
OLED::print(translatedString(Tr->IdleSetString), FontStyle::SMALL);
OLED::printNumber(systemSettings.SolderingTemp, 3, FontStyle::SMALL);
}
OLED::setCursor(0, 8);
OLED::print(InputVoltageString, FontStyle::SMALL);
OLED::print(translatedString(Tr->InputVoltageString), FontStyle::SMALL);
printVoltage();
} else {

View File

@@ -322,7 +322,7 @@ Core/Gen/Translation.%.cpp: ../Translations/translation_%.json Makefile ../Trans
@python3 ../Translations/make_translation.py -o $(PWD)/$@ $*
clean :
rm -Rf $(SOURCE_CORE_DIR)/Translation.cpp Core/Gen
rm -Rf Core/Gen
rm -Rf $(OUTPUT_DIR_BASE)
rm -Rf $(HEXFILE_DIR)