installer: inline TUI, remove tools dir, remove installer_tui.sh

This commit is contained in:
Dushnilin
2026-08-03 14:55:02 +03:00
parent e371f7a52e
commit 7a10fc9486
9 changed files with 304 additions and 704 deletions
+304 -4
View File
@@ -6,10 +6,310 @@ INSTALLER_VERSION="2.0.1"
REPO_OWNER="Dushnilin" REPO_OWNER="Dushnilin"
REPO_NAME="tachyon" REPO_NAME="tachyon"
# ─── Source TUI helpers (self-contained, no external deps) ─────────────────── # ─── TUI helpers (self-contained, no external deps) ─────────────────────────
_TUI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}" 2>/dev/null)" && pwd)" # POSIX-compatible, works with dash/bash/mksh on OpenWrt.
[ -f "$_TUI_DIR/installer_tui.sh" ] && . "$_TUI_DIR/installer_tui.sh" # Degrades gracefully when TERM is dumb or stdout is not a terminal.
unset _TUI_DIR
# ─── Color detection ──────────────────────────────────────────────────────────
_tui_colors=0
if [ -t 1 ] 2>/dev/null; then
case "${TERM:-dumb}" in
dumb) _tui_colors=0 ;;
*) _tui_colors=1 ;;
esac
fi
if [ "$_tui_colors" -eq 1 ]; then
_c_reset='\033[0m'
_c_bold='\033[1m'
_c_dim='\033[2m'
_c_red='\033[31;1m'
_c_green='\033[32;1m'
_c_yellow='\033[33;1m'
_c_blue='\033[34;1m'
_c_cyan='\033[36;1m'
_c_magenta='\033[35;1m'
_c_white='\033[37;1m'
_c_bg_blue='\033[44m'
_c_bg_green='\033[42m'
_c_bg_red='\033[41m'
_c_bg_yellow='\033[43m'
_c_underline='\033[4m'
else
_c_reset=''
_c_bold=''
_c_dim=''
_c_red=''
_c_green=''
_c_yellow=''
_c_blue=''
_c_cyan=''
_c_magenta=''
_c_white=''
_c_bg_blue=''
_c_bg_green=''
_c_bg_red=''
_c_bg_yellow=''
_c_underline=''
fi
# ─── Terminal width ──────────────────────────────────────────────────────────
_tui_width() {
if [ -t 1 ] 2>/dev/null && command -v stty >/dev/null 2>&1; then
w="$(stty size 2>/dev/null | awk '{print $2}')"
[ -n "$w" ] && [ "$w" -gt 0 ] 2>/dev/null && printf '%s' "$w" && return 0
fi
printf '72'
}
# ─── Horizontal line ─────────────────────────────────────────────────────────
_tui_hline() {
_w="$(_tui_width)"
_char="${1:--}"
_i=0
while [ "$_i" -lt "$_w" ]; do
printf '%s' "$_char"
_i=$((_i + 1))
done
}
# ─── Center text ──────────────────────────────────────────────────────────────
_tui_center() {
_text="$1"
_w="$(_tui_width)"
_len=${#_text}
_pad=$((_w - _len))
[ "$_pad" -lt 0 ] 2>/dev/null && _pad=0
_left=$((_pad / 2))
_right=$((_pad - _left))
_i=0
while [ "$_i" -lt "$_left" ]; do
printf ' '
_i=$((_i + 1))
done
printf '%s' "$_text"
_i=0
while [ "$_i" -lt "$_right" ]; do
printf ' '
_i=$((_i + 1))
done
}
# ─── Pad/truncate to width ──────────────────────────────────────────────────
_tui_pad() {
_text="$1"
_target="${2:-0}"
_len=${#_text}
_i=$_len
while [ "$_i" -lt "$_target" ]; do
printf ' '
_i=$((_i + 1))
done
}
# ─── Print banner ────────────────────────────────────────────────────────────
tui_banner() {
printf '\n'
printf ' %s%sTachyon Installer%s v%s%s\n' "$_c_cyan" "$_c_bold" "$_c_reset" "$_c_bold" "$INSTALLER_VERSION"
printf '%s\n' ""
printf '%s%s%s\n' "$_c_dim" "$(_tui_hline '─')" "$_c_reset"
}
# ─── Print step header ──────────────────────────────────────────────────────
tui_step() {
_step_no="$1"
_step_total="$2"
_step_text="$3"
_step_icon=""
printf '\n'
printf ' %s%s[ %s/%s ]%s %s%s%s\n' \
"$_c_blue" "$_c_bold" \
"$_step_no" "$_step_total" \
"$_c_reset" \
"$_c_bold" "$_step_text" "$_c_reset"
printf ' %s%s\n' "$_c_dim" "$(_tui_hline '·')"
printf '%s' "$_c_reset"
}
# ─── Success message ─────────────────────────────────────────────────────────
tui_ok() {
printf ' %s%s✓%s %s\n' "$_c_green" "$_c_bold" "$_c_reset" "$1"
}
# ─── Warning message ─────────────────────────────────────────────────────────
tui_warn() {
printf ' %s%s⚠%s %s%s\n' "$_c_yellow" "$_c_bold" "$_c_reset" "$1" "$_c_reset" >&2
}
# ─── Error message ───────────────────────────────────────────────────────────
tui_err() {
printf ' %s%s✗%s %s%s\n' "$_c_red" "$_c_bold" "$_c_reset" "$1" "$_c_reset" >&2
printf ' %s%s Log: %s%s\n' "$_c_dim" "$_c_reset" "$LOG_FILE" "$_c_reset" >&2
}
# ─── Info message ────────────────────────────────────────────────────────────
tui_info() {
printf ' %s▸%s %s\n' "$_c_cyan" "$_c_reset" "$1"
}
# ─── Download progress ───────────────────────────────────────────────────────
tui_download() {
_label="$1"
_attempt="${2:-1}"
_max="${3:-3}"
printf ' %s%s↓%s %s %s(%s/%s)%s\n' \
"$_c_cyan" "$_c_bold" "$_c_reset" \
"$_label" \
"$_c_dim" "$_attempt" "$_max" "$_c_reset"
}
# ─── Progress bar ────────────────────────────────────────────────────────────
tui_progress() {
_current="$1"
_total="$2"
_label="${3:-}"
_w=$((_tui_width() - 8))
[ "$_w" -lt 10 ] 2>/dev/null && _w=10
_pct=0
[ "$_total" -gt 0 ] 2>/dev/null && _pct=$((_current * 100 / _total))
_filled=$((_pct * _w / 100))
[ "$_filled" -gt "$_w" ] 2>/dev/null && _filled=$_w
_empty=$((_w - _filled))
printf '\r %s' "$_c_dim"
_i=0
while [ "$_i" -lt "$_filled" ]; do
printf '━'
_i=$((_i + 1))
done
_i=0
while [ "$_i" -lt "$_empty" ]; do
printf '─'
_i=$((_i + 1))
done
printf '%s' "$_c_reset"
printf ' %s%3d%%%s' "$_c_bold" "$_pct" "$_c_reset"
[ -n "$_label" ] && printf ' %s' "$_label"
}
# ─── Spinner (call in background, kill when done) ───────────────────────────
tui_spinner_start() {
_spinner_text="${1:-Working...}"
_spinner_chars='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
_spinner_pid=""
# Only spin on real terminals
[ -t 1 ] 2>/dev/null || { printf ' %s\n' "$_spinner_text"; return 0; }
(
while true; do
for _ch in $_spinner_chars; do
printf '\r %s%s%s %s' "$_c_cyan" "$_ch" "$_c_reset" "$_spinner_text"
sleep 0.1 2>/dev/null || sleep 1
done
done
) &
_spinner_pid=$!
printf '%s' "$_spinner_pid"
}
tui_spinner_stop() {
_pid="$1"
_text="${2:-}"
if [ -n "$_pid" ] && kill -0 "$_pid" 2>/dev/null; then
kill "$_pid" 2>/dev/null
wait "$_pid" 2>/dev/null
fi
# Clear spinner line
printf '\r%s' "$(_tui_hline ' ')"
printf '\r'
[ -n "$_text" ] && tui_ok "$_text"
}
# ─── Boxed section ───────────────────────────────────────────────────────────
tui_box_start() {
_w="$(_tui_width)"
_w=$((_w - 4))
[ "$_w" -lt 10 ] 2>/dev/null && _w=10
printf '\n'
printf ' %s╔' "$_c_blue"
_i=0; while [ "$_i" -lt "$_w" ]; do printf '═'; _i=$((_i + 1)); done
printf '╗%s\n' "$_c_reset"
}
tui_box_line() {
_text="$1"
_w="$(_tui_width)"
_w=$((_w - 4))
_len=${#_text}
_pad=$((_w - _len - 2))
[ "$_pad" -lt 0 ] 2>/dev/null && _pad=0
printf ' %s║%s %s' "$_c_blue" "$_c_reset" "$_text"
_i=0; while [ "$_i" -lt "$_pad" ]; do printf ' '; _i=$((_i + 1)); done
printf '%s║%s\n' "$_c_blue" "$_c_reset"
}
tui_box_line_color() {
_text="$1"
_color="$2"
_w="$(_tui_width)"
_w=$((_w - 4))
_len=${#_text}
_pad=$((_w - _len - 2))
[ "$_pad" -lt 0 ] 2>/dev/null && _pad=0
printf ' %s║%s %s%s' "$_c_blue" "$_c_reset" "$_color" "$_text"
_i=0; while [ "$_i" -lt "$_pad" ]; do printf ' '; _i=$((_i + 1)); done
printf '%s║%s\n' "$_c_reset" "$_c_blue"
}
tui_box_end() {
_w="$(_tui_width)"
_w=$((_w - 4))
[ "$_w" -lt 10 ] 2>/dev/null && _w=10
printf ' %s╚' "$_c_blue"
_i=0; while [ "$_i" -lt "$_w" ]; do printf '═'; _i=$((_i + 1)); done
printf '╝%s\n' "$_c_reset"
}
# ─── Section divider ─────────────────────────────────────────────────────────
tui_divider() {
printf '\n %s%s%s\n' "$_c_dim" "$(_tui_hline '─')" "$_c_reset"
}
tui_box_divider() {
_w="$(_tui_width)"
_w=$((_w - 4))
[ "$_w" -lt 10 ] 2>/dev/null && _w=10
printf ' %s║%s' "$_c_blue" "$_c_reset"
_i=0; while [ "$_i" -lt "$_w" ]; do printf '─'; _i=$((_i + 1)); done
printf '%s║%s\n' "$_c_blue" "$_c_reset"
}
# ─── Interactive menu (returns choice) ──────────────────────────────────────
tui_menu() {
_prompt="$1"
shift
_options="$@"
printf '\n %s%s%s\n' "$_c_bold" "$_prompt" "$_c_reset"
_i=1
for _opt in $_options; do
printf ' %s%s)%s %s\n' "$_c_cyan" "$_c_bold" "$_c_reset" "$_opt"
_i=$((_i + 1))
done
printf '\n'
printf ' %s▸%s ' "$_c_cyan" "$_c_reset"
}
# ─── Confirmation prompt ────────────────────────────────────────────────────
tui_confirm() {
_prompt="$1"
printf ' %s%s?%s %s %s' "$_c_yellow" "$_c_bold" "$_c_reset" "$_prompt" "$_c_dim"
printf '(%s/%s)%s ' "y" "n" "$_c_reset"
}
REQUIRED_SPACE_KB=15360 REQUIRED_SPACE_KB=15360
CONNECT_TIMEOUT_SECONDS=15 CONNECT_TIMEOUT_SECONDS=15
-306
View File
@@ -1,306 +0,0 @@
#!/bin/sh
# shellcheck shell=dash
# ─── TUI helpers for Tachyon installer ───────────────────────────────────────
# POSIX-compatible, works with dash/bash/mksh on OpenWrt.
# Degrades gracefully when TERM is dumb or stdout is not a terminal.
# ─── Color detection ──────────────────────────────────────────────────────────
_tui_colors=0
if [ -t 1 ] 2>/dev/null; then
case "${TERM:-dumb}" in
dumb) _tui_colors=0 ;;
*) _tui_colors=1 ;;
esac
fi
if [ "$_tui_colors" -eq 1 ]; then
_c_reset='\033[0m'
_c_bold='\033[1m'
_c_dim='\033[2m'
_c_red='\033[31;1m'
_c_green='\033[32;1m'
_c_yellow='\033[33;1m'
_c_blue='\033[34;1m'
_c_cyan='\033[36;1m'
_c_magenta='\033[35;1m'
_c_white='\033[37;1m'
_c_bg_blue='\033[44m'
_c_bg_green='\033[42m'
_c_bg_red='\033[41m'
_c_bg_yellow='\033[43m'
_c_underline='\033[4m'
else
_c_reset=''
_c_bold=''
_c_dim=''
_c_red=''
_c_green=''
_c_yellow=''
_c_blue=''
_c_cyan=''
_c_magenta=''
_c_white=''
_c_bg_blue=''
_c_bg_green=''
_c_bg_red=''
_c_bg_yellow=''
_c_underline=''
fi
# ─── Terminal width ──────────────────────────────────────────────────────────
_tui_width() {
if [ -t 1 ] 2>/dev/null && command -v stty >/dev/null 2>&1; then
w="$(stty size 2>/dev/null | awk '{print $2}')"
[ -n "$w" ] && [ "$w" -gt 0 ] 2>/dev/null && printf '%s' "$w" && return 0
fi
printf '72'
}
# ─── Horizontal line ─────────────────────────────────────────────────────────
_tui_hline() {
_w="$(_tui_width)"
_char="${1:--}"
_i=0
while [ "$_i" -lt "$_w" ]; do
printf '%s' "$_char"
_i=$((_i + 1))
done
}
# ─── Center text ──────────────────────────────────────────────────────────────
_tui_center() {
_text="$1"
_w="$(_tui_width)"
_len=${#_text}
_pad=$((_w - _len))
[ "$_pad" -lt 0 ] 2>/dev/null && _pad=0
_left=$((_pad / 2))
_right=$((_pad - _left))
_i=0
while [ "$_i" -lt "$_left" ]; do
printf ' '
_i=$((_i + 1))
done
printf '%s' "$_text"
_i=0
while [ "$_i" -lt "$_right" ]; do
printf ' '
_i=$((_i + 1))
done
}
# ─── Pad/truncate to width ──────────────────────────────────────────────────
_tui_pad() {
_text="$1"
_target="${2:-0}"
_len=${#_text}
_i=$_len
while [ "$_i" -lt "$_target" ]; do
printf ' '
_i=$((_i + 1))
done
}
# ─── Print banner ────────────────────────────────────────────────────────────
tui_banner() {
printf '\n'
printf ' %s%sTachyon Installer%s v%s%s\n' "$_c_cyan" "$_c_bold" "$_c_reset" "$_c_bold" "$INSTALLER_VERSION"
printf '%s\n' ""
printf '%s%s%s\n' "$_c_dim" "$(_tui_hline '─')" "$_c_reset"
}
# ─── Print step header ──────────────────────────────────────────────────────
tui_step() {
_step_no="$1"
_step_total="$2"
_step_text="$3"
_step_icon=""
printf '\n'
printf ' %s%s[ %s/%s ]%s %s%s%s\n' \
"$_c_blue" "$_c_bold" \
"$_step_no" "$_step_total" \
"$_c_reset" \
"$_c_bold" "$_step_text" "$_c_reset"
printf ' %s%s\n' "$_c_dim" "$(_tui_hline '·')"
printf '%s' "$_c_reset"
}
# ─── Success message ─────────────────────────────────────────────────────────
tui_ok() {
printf ' %s%s✓%s %s\n' "$_c_green" "$_c_bold" "$_c_reset" "$1"
}
# ─── Warning message ─────────────────────────────────────────────────────────
tui_warn() {
printf ' %s%s⚠%s %s%s\n' "$_c_yellow" "$_c_bold" "$_c_reset" "$1" "$_c_reset" >&2
}
# ─── Error message ───────────────────────────────────────────────────────────
tui_err() {
printf ' %s%s✗%s %s%s\n' "$_c_red" "$_c_bold" "$_c_reset" "$1" "$_c_reset" >&2
printf ' %s%s Log: %s%s\n' "$_c_dim" "$_c_reset" "$LOG_FILE" "$_c_reset" >&2
}
# ─── Info message ────────────────────────────────────────────────────────────
tui_info() {
printf ' %s▸%s %s\n' "$_c_cyan" "$_c_reset" "$1"
}
# ─── Download progress ───────────────────────────────────────────────────────
tui_download() {
_label="$1"
_attempt="${2:-1}"
_max="${3:-3}"
printf ' %s%s↓%s %s %s(%s/%s)%s\n' \
"$_c_cyan" "$_c_bold" "$_c_reset" \
"$_label" \
"$_c_dim" "$_attempt" "$_max" "$_c_reset"
}
# ─── Progress bar ────────────────────────────────────────────────────────────
tui_progress() {
_current="$1"
_total="$2"
_label="${3:-}"
_w=$((_tui_width() - 8))
[ "$_w" -lt 10 ] 2>/dev/null && _w=10
_pct=0
[ "$_total" -gt 0 ] 2>/dev/null && _pct=$((_current * 100 / _total))
_filled=$((_pct * _w / 100))
[ "$_filled" -gt "$_w" ] 2>/dev/null && _filled=$_w
_empty=$((_w - _filled))
printf '\r %s' "$_c_dim"
_i=0
while [ "$_i" -lt "$_filled" ]; do
printf '━'
_i=$((_i + 1))
done
_i=0
while [ "$_i" -lt "$_empty" ]; do
printf '─'
_i=$((_i + 1))
done
printf '%s' "$_c_reset"
printf ' %s%3d%%%s' "$_c_bold" "$_pct" "$_c_reset"
[ -n "$_label" ] && printf ' %s' "$_label"
}
# ─── Spinner (call in background, kill when done) ───────────────────────────
tui_spinner_start() {
_spinner_text="${1:-Working...}"
_spinner_chars='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
_spinner_pid=""
# Only spin on real terminals
[ -t 1 ] 2>/dev/null || { printf ' %s\n' "$_spinner_text"; return 0; }
(
while true; do
for _ch in $_spinner_chars; do
printf '\r %s%s%s %s' "$_c_cyan" "$_ch" "$_c_reset" "$_spinner_text"
sleep 0.1 2>/dev/null || sleep 1
done
done
) &
_spinner_pid=$!
printf '%s' "$_spinner_pid"
}
tui_spinner_stop() {
_pid="$1"
_text="${2:-}"
if [ -n "$_pid" ] && kill -0 "$_pid" 2>/dev/null; then
kill "$_pid" 2>/dev/null
wait "$_pid" 2>/dev/null
fi
# Clear spinner line
printf '\r%s' "$(_tui_hline ' ')"
printf '\r'
[ -n "$_text" ] && tui_ok "$_text"
}
# ─── Boxed section ───────────────────────────────────────────────────────────
tui_box_start() {
_w="$(_tui_width)"
_w=$((_w - 4))
[ "$_w" -lt 10 ] 2>/dev/null && _w=10
printf '\n'
printf ' %s╔' "$_c_blue"
_i=0; while [ "$_i" -lt "$_w" ]; do printf '═'; _i=$((_i + 1)); done
printf '╗%s\n' "$_c_reset"
}
tui_box_line() {
_text="$1"
_w="$(_tui_width)"
_w=$((_w - 4))
_len=${#_text}
_pad=$((_w - _len - 2))
[ "$_pad" -lt 0 ] 2>/dev/null && _pad=0
printf ' %s║%s %s' "$_c_blue" "$_c_reset" "$_text"
_i=0; while [ "$_i" -lt "$_pad" ]; do printf ' '; _i=$((_i + 1)); done
printf '%s║%s\n' "$_c_blue" "$_c_reset"
}
tui_box_line_color() {
_text="$1"
_color="$2"
_w="$(_tui_width)"
_w=$((_w - 4))
_len=${#_text}
_pad=$((_w - _len - 2))
[ "$_pad" -lt 0 ] 2>/dev/null && _pad=0
printf ' %s║%s %s%s' "$_c_blue" "$_c_reset" "$_color" "$_text"
_i=0; while [ "$_i" -lt "$_pad" ]; do printf ' '; _i=$((_i + 1)); done
printf '%s║%s\n' "$_c_reset" "$_c_blue"
}
tui_box_end() {
_w="$(_tui_width)"
_w=$((_w - 4))
[ "$_w" -lt 10 ] 2>/dev/null && _w=10
printf ' %s╚' "$_c_blue"
_i=0; while [ "$_i" -lt "$_w" ]; do printf '═'; _i=$((_i + 1)); done
printf '╝%s\n' "$_c_reset"
}
# ─── Section divider ─────────────────────────────────────────────────────────
tui_divider() {
printf '\n %s%s%s\n' "$_c_dim" "$(_tui_hline '─')" "$_c_reset"
}
tui_box_divider() {
_w="$(_tui_width)"
_w=$((_w - 4))
[ "$_w" -lt 10 ] 2>/dev/null && _w=10
printf ' %s║%s' "$_c_blue" "$_c_reset"
_i=0; while [ "$_i" -lt "$_w" ]; do printf '─'; _i=$((_i + 1)); done
printf '%s║%s\n' "$_c_blue" "$_c_reset"
}
# ─── Interactive menu (returns choice) ──────────────────────────────────────
tui_menu() {
_prompt="$1"
shift
_options="$@"
printf '\n %s%s%s\n' "$_c_bold" "$_prompt" "$_c_reset"
_i=1
for _opt in $_options; do
printf ' %s%s)%s %s\n' "$_c_cyan" "$_c_bold" "$_c_reset" "$_opt"
_i=$((_i + 1))
done
printf '\n'
printf ' %s▸%s ' "$_c_cyan" "$_c_reset"
}
# ─── Confirmation prompt ────────────────────────────────────────────────────
tui_confirm() {
_prompt="$1"
printf ' %s%s?%s %s %s' "$_c_yellow" "$_c_bold" "$_c_reset" "$_prompt" "$_c_dim"
printf '(%s/%s)%s ' "y" "n" "$_c_reset"
}
-93
View File
@@ -1,93 +0,0 @@
const fs = require('fs');
// LuCI uses this specific SuperFastHash from OpenWrt
// Let me try to match it against the reference lmo
// adblock.bg.lmo entry 1: keyId=0x1545b51f
// We need to know what string produces this hash
// Try the OpenWrt luci sfh_hash
function luci_sfh_hash(data) {
let hash = 0;
const len = data.length;
let i;
for (i = 0; i < len; i++) {
hash += data.charCodeAt(i);
hash = (hash << 10) + hash;
hash ^= hash >>> 6;
}
hash += hash << 3;
hash ^= hash >>> 11;
hash += hash << 15;
return hash >>> 0;
}
console.log('luci_sfh empty:', luci_sfh_hash('').toString(16));
console.log('luci_sfh Plural-Forms:', luci_sfh_hash('Plural-Forms').toString(16));
// Now check my po2lmo sfh_hash
function po2lmo_sfh(data) {
let hash = 0;
let i = data.length;
let k;
while (i >= 4) {
k = (data.charCodeAt(i - 4) & 0xff) |
((data.charCodeAt(i - 3) & 0xff) << 8) |
((data.charCodeAt(i - 2) & 0xff) << 16) |
((data.charCodeAt(i - 1) & 0xff) << 24);
k = Math.imul(k, 0x5bd1e995);
k = k >>> 0;
k ^= k >>> 24;
k = Math.imul(k, 0x5bd1e995);
k = k >>> 0;
hash = Math.imul(hash, 0x5bd1e995) + k;
hash = hash >>> 0;
i -= 4;
}
switch (i) {
case 3: hash ^= (data.charCodeAt(0) & 0xff) << 16;
case 2: hash ^= (data.charCodeAt(i - 2) & 0xff) << 8;
case 1: hash ^= (data.charCodeAt(i - 1) & 0xff);
hash = Math.imul(hash, 0x5bd1e995);
hash = hash >>> 0;
}
hash ^= hash >>> 13;
hash = Math.imul(hash, 0x5bd1e995);
hash = hash >>> 0;
hash ^= hash >>> 15;
return hash >>> 0;
}
console.log('po2lmo_sfh empty:', po2lmo_sfh('').toString(16));
console.log('po2lmo_sfh Plural-Forms:', po2lmo_sfh('Plural-Forms').toString(16));
// Read the reference LMO
const buf = fs.readFileSync('tools/test_ref.lmo');
const indexOffset = buf.readUInt32BE(buf.length - 4);
const entryCount = (buf.length - 4 - indexOffset) / 16;
console.log('\nReference LMO entries:');
for (let i = 0; i < Math.min(10, entryCount); i++) {
const off = indexOffset + i * 16;
const keyId = buf.readUInt32BE(off);
console.log(' keyId:', keyId.toString(16));
}
// Read payload to get actual strings
for (let i = 0; i < Math.min(10, entryCount); i++) {
const off = indexOffset + i * 16;
const keyId = buf.readUInt32BE(off);
const valId = buf.readUInt32BE(off + 4);
const offset = buf.readUInt32BE(off + 8);
const length = buf.readUInt32BE(off + 12);
// Read the payload at offset
const valBuf = buf.slice(offset, offset + length);
const valStr = valBuf.toString('utf8').replace(/\0/g, '');
console.log(` [${i}] keyId:${keyId.toString(16).padStart(8,'0')} off:${offset} len:${length} val:"${valStr}"`);
// If this is entry 1+, try to compute hash of the key
if (i > 0 && valStr.length > 0) {
console.log(` luci_sfh("${valStr}") = ${luci_sfh_hash(valStr).toString(16)}`);
console.log(` po2lmo_sfh("${valStr}") = ${po2lmo_sfh(valStr).toString(16)}`);
}
}
-26
View File
@@ -1,26 +0,0 @@
// OpenWrt luci-core uses this hash (from lmo.c):
function lmo_hash(s) {
let h = 0;
for (let i = 0; i < s.length; i++) {
h += s.charCodeAt(i);
h = (h + (h << 10)) >>> 0;
h ^= h >>> 6;
}
h = (h + (h << 3)) >>> 0;
h ^= h >>> 11;
h = (h + (h << 15)) >>> 0;
return h >>> 0;
}
// Reference: entry 1 keyId=0x1545b51f
console.log('lmo_hash(Client):', lmo_hash('Client').toString(16), '(target: 1545b51f)');
console.log('lmo_hash(Download):', lmo_hash('Download').toString(16));
console.log('lmo_hash(Save):', lmo_hash('Save').toString(16));
console.log('lmo_hash(Action):', lmo_hash('Action').toString(16));
console.log('lmo_hash(Enabled):', lmo_hash('Enabled').toString(16));
console.log('lmo_hash(General):', lmo_hash('General').toString(16));
console.log('lmo_hash(Email):', lmo_hash('Email').toString(16));
console.log('lmo_hash(Subject):', lmo_hash('Subject').toString(16));
console.log('lmo_hash(Enable detailed logs):', lmo_hash('Enable detailed logs').toString(16));
console.log('lmo_hash(Enable):', lmo_hash('Enable').toString(16));
console.log('lmo_hash(Close):', lmo_hash('Close').toString(16));
console.log('lmo_hash(Save & Apply):', lmo_hash('Save & Apply').toString(16));
-150
View File
@@ -1,150 +0,0 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
// OpenWrt LuCI SuperFastHash (from LMO.md / template_lmo.c)
// sfh_get16 reads uint16 in little-endian
function sfh_get16(d, off) {
return (d[off] & 0xff) + ((d[off + 1] & 0xff) << 8);
}
function sfh_hash(str) {
const data = Buffer.from(str, 'utf8');
const len = data.length;
if (len <= 0) return 0;
let hash = len;
let rem = len & 3;
let offset = 0;
const words = len >> 2;
// Main loop
for (let i = 0; i < words; i++) {
hash += sfh_get16(data, offset);
let tmp = (sfh_get16(data, offset + 2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
offset += 4;
hash += hash >>> 11;
}
// Handle end cases
switch (rem) {
case 3:
hash += sfh_get16(data, offset);
hash ^= hash << 16;
hash ^= data[offset + 2] << 18;
hash += hash >>> 11;
break;
case 2:
hash += sfh_get16(data, offset);
hash ^= hash << 11;
hash += hash >>> 17;
break;
case 1:
hash += data[offset];
hash ^= hash << 10;
hash += hash >>> 1;
}
// Force "avalanching" of final 127 bits
hash ^= hash << 3;
hash += hash >>> 5;
hash ^= hash << 4;
hash += hash >>> 17;
hash ^= hash << 25;
hash += hash >>> 6;
return hash >>> 0;
}
function pad4(len) {
return (4 - (len % 4)) % 4;
}
function parsePo(content) {
const entries = [];
const lines = content.split('\n');
let state = null;
let msgid = '';
let msgstr = '';
function flush() {
if (state === 'msgstr' && msgid !== '' && msgstr !== '') {
entries.push({ key: msgid, value: msgstr });
}
msgid = '';
msgstr = '';
state = null;
}
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith('msgid ')) {
flush();
state = 'msgid';
msgid = line.slice(7, -1).replace(/\\n/g, '\n').replace(/\\"/g, '"');
} else if (line.startsWith('msgstr ')) {
state = 'msgstr';
msgstr = line.slice(8, -1).replace(/\\n/g, '\n').replace(/\\"/g, '"');
} else if (line.startsWith('"')) {
const val = line.slice(1, -1).replace(/\\n/g, '\n').replace(/\\"/g, '"');
if (state === 'msgid') msgid += val;
else if (state === 'msgstr') msgstr += val;
}
}
flush();
return entries;
}
function compileLmo(entries) {
const payload = [];
const index = [];
let offset = 0;
for (const entry of entries) {
const keyBuf = Buffer.from(entry.key, 'utf8');
const valBuf = Buffer.from(entry.value, 'utf8');
const keyId = sfh_hash(entry.key);
const valId = sfh_hash(entry.value);
const paddedLen = valBuf.length + pad4(valBuf.length);
const padded = Buffer.alloc(paddedLen, 0);
valBuf.copy(padded);
payload.push(padded);
index.push({ keyId, valId, offset, length: valBuf.length });
offset += paddedLen;
}
const indexBuf = Buffer.alloc(index.length * 16);
for (let i = 0; i < index.length; i++) {
indexBuf.writeUInt32BE(index[i].keyId, i * 16);
indexBuf.writeUInt32BE(index[i].valId, i * 16 + 4);
indexBuf.writeUInt32BE(index[i].offset, i * 16 + 8);
indexBuf.writeUInt32BE(index[i].length, i * 16 + 12);
}
const payloadData = Buffer.concat(payload);
const totalLen = payloadData.length + indexBuf.length + 4;
const out = Buffer.alloc(totalLen);
payloadData.copy(out, 0);
indexBuf.copy(out, payloadData.length);
out.writeUInt32BE(payloadData.length, totalLen - 4);
return out;
}
const [input, output] = process.argv.slice(2);
if (!input || !output) {
console.error('Usage: node po2lmo.js input.po output.lmo');
process.exit(1);
}
const poContent = fs.readFileSync(input, 'utf8');
const entries = parsePo(poContent);
console.log(`Parsed ${entries.length} translation entries from ${input}`);
const lmo = compileLmo(entries);
fs.writeFileSync(output, lmo);
console.log(`Written ${lmo.length} bytes to ${output}`);
-26
View File
@@ -1,26 +0,0 @@
function sfh_hash(data) {
let hash = 0;
let i = data.length;
let k;
while (i >= 4) {
k = (data.charCodeAt(i - 4) & 0xff) | ((data.charCodeAt(i - 3) & 0xff) << 8) | ((data.charCodeAt(i - 2) & 0xff) << 16) | ((data.charCodeAt(i - 1) & 0xff) << 24);
k = (k * 0x5bd1e995) >>> 0;
k ^= k >>> 24;
k = (k * 0x5bd1e995) >>> 0;
hash = (hash * 0x5bd1e995 + k) >>> 0;
i -= 4;
}
switch (i) {
case 3: hash ^= (data.charCodeAt(i - 3) & 0xff) << 16;
case 2: hash ^= (data.charCodeAt(i - 2) & 0xff) << 8;
case 1: hash ^= (data.charCodeAt(i - 1) & 0xff);
hash = (hash * 0x5bd1e995) >>> 0;
}
hash ^= hash >>> 13;
hash = (hash * 0x5bd1e995) >>> 0;
hash ^= hash >>> 15;
return hash >>> 0;
}
console.log('Empty:', sfh_hash('').toString(16));
console.log('Plural-Forms:', sfh_hash('Plural-Forms').toString(16));
console.log('Test str:', sfh_hash('Hello').toString(16));
Binary file not shown.
-66
View File
@@ -1,66 +0,0 @@
const fs = require('fs');
function sfh_get16(d, off) {
return (d[off] & 0xff) + ((d[off + 1] & 0xff) << 8);
}
function sfh_hash(str) {
const data = Buffer.from(str, 'utf8');
const len = data.length;
if (len <= 0) return 0;
let hash = len;
let rem = len & 3;
let offset = 0;
const words = len >> 2;
for (let i = 0; i < words; i++) {
hash += sfh_get16(data, offset);
let tmp = (sfh_get16(data, offset + 2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
offset += 4;
hash += hash >>> 11;
}
switch (rem) {
case 3:
hash += sfh_get16(data, offset);
hash ^= hash << 16;
hash ^= data[offset + 2] << 18;
hash += hash >>> 11;
break;
case 2:
hash += sfh_get16(data, offset);
hash ^= hash << 11;
hash += hash >>> 17;
break;
case 1:
hash += data[offset];
hash ^= hash << 10;
hash += hash >>> 1;
}
hash ^= hash << 3;
hash += hash >>> 5;
hash ^= hash << 4;
hash += hash >>> 17;
hash ^= hash << 25;
hash += hash >>> 6;
return hash >>> 0;
}
// Reference: adblock.bg.lmo entry 1 keyId=0x1545b51f
// msgid for "Клиент" in adblock bg is likely "Client"
console.log('sfh_hash(Client):', sfh_hash('Client').toString(16), '(target: 1545b51f)');
console.log('sfh_hash(Save):', sfh_hash('Save').toString(16));
console.log('sfh_hash(Close):', sfh_hash('Close').toString(16));
console.log('sfh_hash(Enable):', sfh_hash('Enable').toString(16));
console.log('sfh_hash(Enabled):', sfh_hash('Enabled').toString(16));
console.log('sfh_hash(General settings):', sfh_hash('General settings').toString(16));
console.log('sfh_hash(Action):', sfh_hash('Action').toString(16));
console.log('sfh_hash(Download):', sfh_hash('Download').toString(16));
console.log('sfh_hash(Email):', sfh_hash('Email').toString(16));
console.log('sfh_hash(Subject):', sfh_hash('Subject').toString(16));
console.log('sfh_hash(Enable detailed logs):', sfh_hash('Enable detailed logs').toString(16));
console.log('sfh_hash(Save & Apply):', sfh_hash('Save & Apply').toString(16));
// Now test with some of our tachyon strings
console.log('\nTachyon strings:');
console.log('sfh_hash(Periodically checks if the proxy is responding. Restarts Tachyon after consecutive failures.):', sfh_hash('Periodically checks if the proxy is responding. Restarts Tachyon after consecutive failures.').toString(16));
console.log('sfh_hash(Enable Proxy Health Monitor):', sfh_hash('Enable Proxy Health Monitor').toString(16));
-33
View File
@@ -1,33 +0,0 @@
#!/bin/sh
. /tachyon/installer_tui.sh
INSTALLER_VERSION="1.2.56"
tui_banner
tui_step 1 10 "Updating package lists"
tui_ok "Package lists updated"
tui_step 2 10 "Resolving latest Tachyon release version"
tui_info "Fetching release metadata..."
tui_ok "Resolved: 1.2.56"
tui_step 3 10 "Downloading Tachyon release packages"
tui_download "tachyon_1.2.56.apk" 1 3
tui_download "luci-app-tachyon_1.2.56.apk" 2 3
tui_download "sha256sums.txt" 3 3
tui_ok "All packages downloaded"
tui_step 4 10 "Installing Tachyon backend package"
tui_info "Ensuring optional kernel module dependencies..."
tui_warn "Could not install kmod-inet-diag (built-in)"
tui_ok "tachyon 1.2.56 installed"
tui_step 5 10 "Done"
tui_box_start
tui_box_line_color "Tachyon 1.2.56 installed successfully (42s)" "$_c_green"
tui_box_line "Release: Dushnilin/tachyon@1.2.56"
tui_box_line "Log: /tmp/tachyon-install.log"
tui_box_divider
tui_box_line_color "Open LuCI and review your rules before enabling Tachyon" "$_c_yellow"
tui_box_end