Commit Graph
86 Commits
Author SHA1 Message Date
Matiq 2e0b107b4c P4: document PRNG prologue + remaining bit-exact gaps 2026-08-20 15:32:47 +03:00
Matiq 87dbf88e6d P4: add dual warp step (mask *= band768; *= warp) - comb 500/3000 fixed 2026-08-20 15:30:30 +03:00
Matiq 0d262461ea P4: lock bigkernel semantics (exp2(-mask)*blend) + calibrate scale
Read the 0x26b820 SIMD loop: the exp2 result is multiplied by the blend buffer
(vmulpd at 0x18026bba0 with ymm11=blend) => mask = exp2(-level)*blend, sign
inverted for attenuation. framed_model.cpp corrected to exp2(-scratch)*0.8.
Calibrated level_scale=600 -> t1kq err -0.06 dB (ref -14.98). comb per-tone
errors reduced (max ~7.8 dB); warp/FFT-conv smoothing still approximated.
2026-08-20 15:25:49 +03:00
Matiq d4a0a68584 P4: transcribe exact FUN_180529fe0 mask chain (structural, not yet calibrated)
framed_model.cpp now follows the decoded mono-path structure:
  level -> scale(0x540870*0x54088c/0x1a0) -> IIR1 leaky(A1/B1) -> IIR2(A2/B2)
  -> exp2(0.5*(mask-blend)) -> combine/acc -> warp -> IIR3(A3/B3)x2 -> dry/wet
IIR stages are the real live tables (iir_leaky y=A*acc+B*x, B=1-A). Removed the
empirical mask_lut=(1/(1+K*acc))^n. Not numerically calibrated yet: exp2 bigkernel
exact semantics + PRNG prologue (fVar30) + FFT-conv still TBD; t1kq depth -2.9 vs
-14.98 dB ref.
2026-08-20 15:10:29 +03:00
Matiq f23bbfa1f6 P4: decode FUN_180529fe0 mono-path exactly + extract live mask tables
Full decomp of the per-band mask-apply loop (scale -> IIR1/2/3 leaky ->
blend -> exp2 bigkernel -> combine/accumulate -> dual warp -> dry/wet -> FFT-conv).
Extracted runtime tables to rt_mask_tables.{hpp,cpp}: IIR A1/B1,A2/B2,A3/B3
(leaky y=A*acc+B*x, B=1-A), warp 0x5406a8, per-band 0x540768, PRNG LUT 0x5408b0.
bigkernel 0x26b820 = vectorized exp2 (log2e/floor/mantissa tables). Fixed the
broken warp line in framed_model.cpp (compiles again).
2026-08-20 15:00:53 +03:00
Matiq fc48a9fee4 P4: real mask chain in FramedDetector (level-tracker + accumulator + live-calibrated LUT), t1kq err 0.5dB
Replace empirical PCHIP detector with live-calibrated mask chain from FUN_180529fe0:
  level = am*res*scale; track += w*(level-track) (per-bin attack/release weights
  extracted from RT snapshot, rt_weights.hpp); acc = (level-track)+level; mask =
  (1/(1+K*acc))^n (K=9.8026 n=0.25966 fitted to live mask band0). min-combine.

Results (N=2048 hop=512): t1kq single-band err +0.51 dB (-14.47 vs -14.98 dB ref);
comb 4-band per-tone -3.4..+5.8 dB (old PCHIP over-cut comb ~6 dB). Adds framed_test
harness for C++ FramedDetector eval on tone1kq/comb.
2026-08-20 13:11:27 +03:00
Matiq 5c939583f5 P4: multi-band FramedDetector (per-band twin res, sens-scaled GAIN, min-combine); tt_base 61.40%; min-combine wrong (soothe2 uses additive accumulator 0x5407c8) 2026-08-20 10:41:24 +03:00
Matiq 0cdc57972c P4: port framed_render.py model to C++ (FramedDetector: twin-res -> level -> Pchip LUT -> warp -> res^rp gain), replace empirical Detector; tt_base 49.93%->61.45% (single-band); multi-band refs still 100% (model is single-band) 2026-08-20 10:31:43 +03:00
Matiq 45dfe2b8c2 P4: full decomp FUN_18052e260 frame driver (twin side-chain per band, LUT+mask+FFT-conv run per drained frame); wire order documented 2026-08-20 09:28:12 +03:00
Matiq a6126d10ac roadmap: mark P1.5/P2/P2.5 DONE, P3 partial (vlog+arch, butterfly remains); ~65-70% done 2026-08-20 09:27:04 +03:00
Matiq 5a4554a9a3 P4-prep: eval_lut_bin uses float logf/expf (matches decomp FUN_180563440 logf/expf, not double std::log/exp) 2026-08-20 09:26:38 +03:00
Matiq 8fad2bbfd9 P3.5: acc_437c0 = data shuffle (re/im repack + zero-fill), not arithmetic; full butterfly = twiddle load + shuffle + mult + combine 2026-08-20 08:48:00 +03:00
Matiq feb44c3802 P3.4: transcribe plugin's own vectorized ln(x) (vlog.cpp, minimax ln(1+x) poly + ln2 range-reduction); correct prior misread — dispatch reaches runtime ln, not FFT; vlog_check ALL OK (rel 2.4e-7) 2026-08-20 07:12:56 +03:00
Matiq 7bbe7cce05 P3.3: final kernels use own vectorized sin/cos (SVML-like poly+exp, ln2=0.693147) not twiddle table for big-N; poly constants captured from rodata 2026-08-20 05:56:12 +03:00
Matiq 2a7b88e704 P3.2: map full 4-level FFT dispatch (535a70->140a10/70->jumptable->runtime table->6 final split-radix kernels 0x1802a24c0..2ce4a0); kernels force MXCSR round-to-nearest 2026-08-20 02:58:01 +03:00
Matiq 88422034f5 P3.1: extract FFT code from rt snap (extract_fft.py); decode cplx_mul 0x8440 = in-place elementwise double mult, twiddle loader 0x39b00 stride copy, dispatcher 0x535a70->0x140a10/70->jumptable[0x1826159a0]=4; implement cplx_mul_scalar_inplace 2026-08-20 02:25:59 +03:00
Matiq 22e4599e0a P2: decode mask-accumulator combine kernels from raw bytes — combine3(0x8d60)=sub, acc_add(0x5a20)=dst+=src, acc_fma(0x3c40)=dst+=a·b; implement + levelpath_check ALL OK 2026-08-20 01:30:33 +03:00
Matiq d3db772121 P2.5: decode twin-mask factory (FUN_18056e3e0 = constant fill 2π/(count·SR)) + band LUT apply (FUN_180563a60: level->gain t^γ/power-law); implement in levelpath.cpp, levelpath_check ALL OK 2026-08-20 01:23:22 +03:00
Matiq a49e35dc25 P1.5: embed live level-tracker A[] + mask scalars into C++ (dsp/leveltrack_data.hpp); leveltrack_check sanity confirms SR=48000, A_ATTACK plateaus 0.692, attack/release=1.0 2026-08-20 01:12:12 +03:00
Matiq 0e90918226 P1.5 SOLVED: live ctx (0x2370040, +0x24==48000) + level-tracker A[] + mask scalars captured via realtime playback; method play.lua + rtctx_rt.py; prior 'unreachable' was offline-engine artifact 2026-08-20 00:44:58 +03:00
Matiq fe43509d50 NOTES_CAPTURE: P1.5 live level-tracker A[]/ctx confirmed unreachable (window ptr only in registry, no +0x24==40000 object); scalars static-derived from RPP; 2 missing consts 0x24c4348/44a4 2026-08-20 00:11:45 +03:00
Matiq c5d2b2ad7f P1.5: rtctx.py live-capture pipeline (registry-scan + ctx discovery); confirms live level-tracker A[]/ctx not reachable (window ptr only in registry, no +0x24==40000 object) => scalars are static-only 2026-08-20 00:11:25 +03:00
Matiq fd5a46542b roadmap: reflect P0-P2 (tables/fftconv/leveltrack/mask-canon) + exact estimate of remaining code (P1.5-P5, ~60% left) 2026-08-19 23:58:20 +03:00
Matiq 433a0026e6 P2: level-tracker UPDATE-loop located (inline bidirectional IIR in FUN_180529fe0), structural module leveltrack (iir_first_order/bidirectional/unrolled4); scalar==unrolled verified; remaining = live A[] coeffs 2026-08-19 23:53:01 +03:00
Matiq cff28a605c P1.4: full FUN_180529fe0 decomp confirms mask-canon (scale->IIR->blend->combine+weights->warp->dry/wet->FFT-conv); documented remaining live scalars for bit-exact 2026-08-19 23:34:02 +03:00
Matiq 8b8895f68a P1.3: integrate FFT-conv stage (build_fir_from_window step5 memcpy + fir_from_mask + overlap-save conv) with captured WIN_WINDOW; fftconv_check confirms FIR=window[2048:4096]={0.8->1.0} 2026-08-19 23:25:21 +03:00
Matiq 42316efa71 P1.2: confirm f_52d990 memcpy via decomp (FUN_18052d990(lVar22, window+N/2, N/2)); infer N=4096 from freq-axis spacing + window sape; step-5 low-pass near-flat ramp 2026-08-19 23:20:38 +03:00
Matiq 241957bced P1.2: f_52d990 = memmove/memcpy thunk, NOT a multiply; corrected step-3/5 FIR window semantics (copy window tail + const fill); xmm9/xmm13 remain 2026-08-19 23:17:31 +03:00
Matiq fd9b1fd001 P1.1: embed runtime-captured DSP tables (WIN_WINDOW, FREQAXIS@48k, WA/WB/WC/WD), dsp_ctx registry mirror, tables_check ALL OK 2026-08-19 22:48:32 +03:00
Matiq 83af30d7c0 P0: track twin.cpp/twin.hpp/rotor_kernel.hpp (were shadowed by .gitignore) 2026-08-19 22:00:22 +03:00
Matiq f8b91e8015 P0.3-4: harness reads flat params (in/out/[conf]), byte-verified trim; verify_bit_exact.py uses SOURCE WAVE+RENDER_FILE from RPP, sample-report mono/stereo 2026-08-19 22:00:14 +03:00
Matiq 58164f2952 P0: fix build (levelpath in CMake, FLOOR_LIN, exact LUT curve formulas), robust RPP param decoder (607 rpp ok), preserve f_52b570/f529fe0 disasms 2026-08-19 21:54:26 +03:00
Matiq 55623b998f chore: add rt-capture artifacts (rtobj/rtsnap scripts, rwin_*.npy tables, dsp grep dumps, rpp param decoders) 2026-08-19 20:56:58 +03:00
Matiq 02dd6e8aa7 docs: exact structural LUT curve formulas (FUN_180563440 linear/power-law), consts from PE; roadmap Q1 static side closed 2026-08-19 20:45:45 +03:00
Matiq 2484829452 docs: roadmap Q4 (0x540658 window) closed via live registry capture (SR=48000, weights), infra rtsnap* 2026-08-19 20:42:22 +03:00
Matiq 34fc852b4f docs: two-point 180s capture confirms registry stability; twin per-note state = {A[3],B[3]} not 342-dbl; Step C closed 2026-08-19 20:27:12 +03:00
Matiq 9c1516171e docs: runtime capture settled — registry heartbeat decodes live 0x540658 window & freq-axis (internal SR=48000); vptr-scan route closed 2026-08-19 20:10:22 +03:00
Matiq abf09a26cc runtime capture: registry heartbeat decodes live DSP tables (WIN 0.540658, freq-axis@48k, per-bin weights), rtsnap_fast/findctx/rtchunk infra 2026-08-19 20:06:14 +03:00
Matiq 8fdb1dc082 docs: add NOTES_TWIN.md (twin resonator + caller + grid) to the repo 2026-08-19 17:41:09 +03:00
Matiq 6c3ab92405 docs: full roadmap rewrite (drop stale phase-A recap, milestones table, doc index) + README doc links 2026-08-19 17:39:48 +03:00
Matiq 59619e4648 docs: roadmap B.12-B.15 journal + README rewrite (framed_render canonical, honest metric)
- roadmap: current status header (decomp closed, frame-render model, honest metric),
  new assets (fraed_render/render_parity/handoff/nls_dasm/rwin tables), journal
  B.12 (bridge), Phase5 (dB parity 0.268), B.13 (res_power+decomp LUT/IIR),
  B.14 (Q-dep rp + metric artifact correction), B.15 (joint free-knot LUT, dual mean 0.027).
- README: rewrite to framed_render.py as canonical model; 3-line model formula;
  structure + corpus tables updated; honest-metric warning; next steps focused.
2026-08-19 17:09:21 +03:00
Matiq 28cd3b7c62 Q-dep rp + honest TRIMMED metric: mean=0.175 (was misleading 0.144 artifact)
- Exposed artifact: earlier fits measured tone_cmp on untracked-length output
  (nfr*HOP+N); framed_render.synthe trims to len(x). Honest scalar-rp=0.0169
  baseline is 0.280, not 0.160 as previously recorded.
- New model: rp(Q)=rp0*Q^drp, G/W/A/rp0/drp=0.9963/0.3335/0.9807/0.0275/0.2159.
  Trimmed errs: q0.1 0.000/0.001, q1 -0.256/-0.707, q10 +0.086/+0.001.
  q0.1+q10 perfect; bottleneck q1@2000 (structural warp mismatch).
2026-08-19 15:48:56 +03:00
Matiq a909aae7d3 res_power final: dual mean=0.160, envRmse q0.1@500=0.05dB; joint fit confirms LUT is fc-dependent (al_* incompatible) 2026-08-19 13:10:57 +03:00
Matiq 954220139e res_power dual-only: mean=0.080, envRmse 0.64→0.05 at q0.1@500; G/W/A/rp=1.0066/0.3261/1.0764/0.0169 2026-08-19 11:32:25 +03:00
Matiq 627e8373e6 res_power breakthrough: 500Hz residual solved (q0.1 err +0.00), decomp inventory, FUN_180563440 decoded 2026-08-19 09:56:19 +03:00
Matiq 1a57c48318 runtime capture pt2: vst3 loads AT ImageBase 0x180000000; 0x540658 = field [ctx+0x540658] not RVA; beacon->ctx script (racy) 2026-08-18 21:11:29 +03:00
Matiq 41fc402b05 Handoff: multi-band combination measured - depth not split by count, sens=0 band contributes 0, band1+band2=+0.53dB over max (not product/additive) => displacement-sum+saturation; single-band B.12 off 1.4dB at 1778 2026-08-18 20:33:48 +03:00
Matiq bc46f039de Handoff: joint dual+al LUT refit fails (al rmse 0.89) - level-pickoff mechanism not statically recoverable; needs runtime mask/level capture 2026-08-18 20:27:53 +03:00
Matiq 6b1e690247 Handoff: RPP base64-XML parameter decoder; b1f_* are MULTI-band (poisoned for B.12); t1kq_only1_* true single-band; FFT-amp calibration; clean dataset = dual_b1q+al_ 2026-08-18 20:26:29 +03:00
Matiq ae380c93c8 Phase5/step5b: full frame-render pilot works (STFT+per-bin LUT mask+OLA); al_* level-sweep exposes steeper real LUT leg vs B.12 frozen knots; fc-scan premise conflict flagged 2026-08-18 19:43:26 +03:00
Matiq 3b8095cee7 Handoff: step4 partial - attack<=20ms measured on renders; 0x1ac=oversample{2,4,8,16}; coeff tables undecodable 2026-08-18 19:34:46 +03:00
Matiq f35a630722 Handoff: step5 dB-parity result (0.268 dB) + measurement notes 2026-08-18 18:50:31 +03:00
Matiq f96bb42d27 Phase5/step5: render-parity harness (dB) - B.12 vs real wavs rmse=0.268 dB 2026-08-18 18:49:53 +03:00
Matiq f60c85cd43 Checkpoint end-of-session: B.12 + FFT-conv mapped; next=step 5 full pipeline 2026-08-18 18:40:06 +03:00
Matiq 60bf3a2394 Phase5/step3 partial: FFT-conv loop mapped; freq-axis const offline; 0x540658 static-invisible 2026-08-18 18:34:52 +03:00
Matiq c8f97e4ea3 Phase5/step2: B.12 model live (rmse 0.236) + freq-axis builder correction (offline const) 2026-08-18 18:08:57 +03:00
Matiq b1066f31e0 model: диагностический мост FUN_180529fe0 (rmse 0.236 dB): C(f)=1.221*LUT(log10(L0/res)) + 0.358*warp(f)^3.143 — dual2000-глубина через АДДИТИВНЫЙ аккумулятор 0x5407c8 (не warp*LUT, тот >10 dB), res(500)=0.117 Q-независим (C500-const), warp^3.14~pi => кратный каскад 2026-08-18 18:02:31 +03:00
Matiq 111d47530e dsp: freqpath - warp 0x5406a8 = 0.87*x/(1+x/K) (K=exp(2.0723), x=f/2000) из FUN_180530850 (0x530900) + self-check (rel err 1.5e-07, PASS) 2026-08-18 17:08:42 +03:00
Matiq 0a4c0c959b dsp: CMake - twin.cpp/phase_table.cpp в либу, executables harness+twin_check, фикс путей dsp/ 2026-08-18 16:58:00 +03:00
Matiq 32a63955f8 B.11: LUT-кривая уровень->маска извлечена (rmse 0.072 dB, 36 точек, 3 уровня): коллапс dual+t1kq+t1k на ОДНУ кривую x=log10(L0/res); степенной закон C~L0^p НЕ работает на 0dB (p_eff 0.19 vs 0.085) - LUT насыщающая gamma из FUN_180563440 (param_1+0x188); форма резонанса стабильна; model_lut.py канонический + roadmap B.11 2026-08-18 13:02:56 +03:00
Matiq 9abecb60d5 README: человеческое описание проекта, структура репо, статус B.10 и модель 2026-08-18 12:29:07 +03:00
Matiq eb53223a59 B.10: ЕДИНАЯ модель verify (rmse 0.109 dB): C=depth*tilt*(L0/res)^p, p=0.085; dual_b1q парадокс РЕШЁН через per-bin tilt 1-w (FUN_180530d30), tilt ratio 1.27 совпадает с декомпиляцией; Q_eff=0.90, gain=4.13~sens12; model_dual.py + fit_maskaxis.py 2026-08-18 12:08:38 +03:00
Matiq 33a36bfe68 B.7: XML mode=1 исполняет case8 (Q=+0x80c, fvar=sens/20), не case1; Q_eff-маппинг 2026-08-18 10:06:25 +03:00
Matiq cbe7da9698 roadmap: band+0x80c (Q) full trace, band-mode switch table, coefficient generators, constants 2026-08-18 02:31:35 +03:00
Matiq 8e08ecf7e5 roadmap: detector twins FUN_180535880/180536f90 decoded, per-stub dispatch tables 2026-08-17 20:40:18 +03:00
Matiq b1b4f2bdf7 roadmap: detector decrypted in soothe_mem.bin, SNR 19.8dB; integrate twiddle loader
- fft.cpp: build_twiddle via soothe::twiddle_load (Cody-Waite sin/cos); drop dup init_plan
- fft_stage.cpp: cplx_mul/stage_complex/stage_double kernels -> phase fixed (corr +0.995)
- detect.cpp: level-dependent regional floor (no bell-boost), mask 10^(-1.041*depth*floor/20)
- burst500 metrics: ref -26.07 / ours -26.13 dBFS, corr 0.99475, SNR 19.80 dB, diff -0.065 dB
- KEY: FUN_180535880/536f90 bodies are decrypted real SSE in soothe_mem.bin; dispatch
  table 0x182616008[0]=idx=4 -> 0x180009860 -> FUN_180040d40; region 0x18004xxxx = full
  detector algorithm, absent from prior fun_map/decomp (Ghidra ran on encrypted file)
2026-08-17 20:02:32 +03:00
Matiq 847725f5fc dsp/: fix FFT inverse, WOLA normalization, detector model, twiddle loader
- Fixed execute_inverse: removed conj bug, now uses positive twiddle only
- Added WOLA normalization factor (wola_sum/hop_ for Hann+hop=N/4)
- New detector model: floor(level) + bell_curve * boost, calibrated from
  measured data (summary.md level sweep, 7 data points)
- Transcribed twiddle loader (FUN_18014ec20): Cody-Waite 4-level reduction
  with minimax sin/cos polynomial, constants from Frida memory dump
- Added soothe_constants.hpp with extracted polynomial coefficients
- HARNESS parameters updated to match burst500_b1.rpp (depth=0.864)

Results: burst500.wav reduction now -5.8 dB vs Ref -6.8 dB (was -14.4 dB)
2026-08-17 18:39:50 +03:00
Matiq 785aff352b update roadmap.md: C++ DSP implementation status 2026-08-17 15:50:15 +03:00
Matiq 71870129c2 dsp/: add spectral detector with envelope + peak suppression
- Detector computes smoothed spectral envelope
- Finds peaks exceeding envelope
- Creates per-bin suppression mask
- Applies mask in frequency domain before ISTFT
- Default params: sharpness=1.0, selectivity=0.5, depth=0.3

Verified: burst500.wav → output RMS reduced from 0.0678 to 0.0476
2026-08-17 15:49:48 +03:00
Matiq 7dcbcf49b4 dsp/: C++ skeleton with working FFT/WOLA STFT
- Cooley-Tukey radix-2 FFT (forward + inverse with /N normalization)
- WOLA STFT/ISTFT with Hann window (nfft=2048, hop=512)
- WAV16 read + WAV24 write (fixed aliasing bug in read)
- Fixed in-place processing bug (separate input/output buffers)
- Biquad filter (peak/shelf/reject)
- Peak detector skeleton
- MS encode/decode (M8 stereo)
- Harness: WAV16 → STFT → WAV24 passthrough verified non-zero output

Verified: burst500.wav passthrough produces output RMS=0.0678
2026-08-17 11:25:35 +03:00
Matiq 81cfca70a9 add dsp/ with FFT, filters, detector, stereo support; initial harness 2026-08-17 09:51:49 +03:00
Matiq 46badce50d update roadmap.md with C++ DSP implementation status 2026-08-17 03:30:58 +03:00
Matiq 66bac5e1e5 add dsp/ with FFT, filters, detector, stereo support 2026-08-17 03:28:05 +03:00
Matiq 230c6a63ad re: LIVE N0@44100 = 2048, oversample 4 (two SP instances agree); angle-ramp+Cody-Waite twiddle path confirmed for main FFT; rtone/rtver scripts 2026-08-17 00:47:52 +03:00
Matiq abef5c6413 re: twiddle builder FUN_180039b00 is two-path (sin-table copy for N<=1024, angle-ramp+sin/cos for N>1024); loader slots all == 0x18014ec20 2026-08-17 00:27:17 +03:00
Matiq 00d8623b92 notes: twiddle = 1024-entry sin table with stride sampling 2026-08-16 23:38:16 +03:00
Matiq 0d16c5f4b9 docs: giant FFT plan generator analysis (notes_giant_fft.md) 2026-08-16 23:10:12 +03:00
Matiq 1e9bac7a42 chore: transform runtime model complete (stage kernels, slot map, driver loc) 2026-08-16 22:56:26 +03:00
Matiq 9ec0470b86 chore: DSP kernel map (complex-mul/double-mul/max), DumpList getFunctionContaining fix 2026-08-16 22:04:48 +03:00
Matiq 86483ce1fa chore: block-processor decompiled (sp_*.txt); FFT plan/giants + spectral chain documented 2026-08-16 21:35:22 +03:00
Matiq cbbd82136c chore: audio-chain xrefs, PACE-stub pattern, SpectralProcessor vftable id 2026-08-16 21:32:30 +03:00
Matiq 304a926ea4 chore: FFT giants, factor tables, spectral class map, block-processor chain 2026-08-16 21:14:51 +03:00
Matiq 72c1fec3d0 Phase A: DSP FFT plan ID (radix 2/3/5, 1/N inv scale), dispatch-kernel dumps 2026-08-16 20:38:30 +03:00
Matiq de7d0431d0 Phase A: consts extraction (11602), Hann window ID, DSP const map 2026-08-16 19:54:01 +03:00
Matiq 25cf786c54 Initial commit: soothe2 RE workspace + roadmap
- Инфраструктура: RTTI-дампы (rtti_dsp/full.json), декомпиляции DSP-классов (decomp_*.txt),
  Ghidra-скрипты (Dump*.java, ImportRtti*.java, Diag.java), depthcurve/curve_fits LUT.
- Поведенческая модель sim_v5.py + sim.py (RMSE ~0.3dB), утилиты (measure, tt_sweep, patchparam,
  sweep, harness_*, verify_sim).
- summary.md + roadmap.md (контракт из manual M1-M12, топология пайплайна из OCR, фазы A-C).
- pipeline_ocr.txt: OCR диаграммы Appendix A (mid/side ручка, trim/mix/bypass порядок).
2026-08-16 18:54:40 +03:00
Matiq 45a13dcebd Initial commit 2026-08-16 18:51:53 +03:00