37 Commits
Author SHA1 Message Date
Valiantsin DzerakhandClaude Opus 5.5 c838d9692f Count system memory in the device-local budget where device memory takes host memory
The paths that give memory back under pressure (headroom's spare block, released textures, pool mirrors) watched the
Vulkan heap budget only. On a UMA GPU that budget is gigabytes of the same RAM the game runs in, and on Windows the
AMD driver charges device-local allocations to the process commit one to one (RX 9070: VRAM use and private bytes
grew by the same 1164, 256 and 256 MiB). Crash logs show the result: the process at 4.4-10.5 GB of commit, 16 GB
machines at 80-94% RAM, std::bad_alloc inside amdvlk64.dll (#285), allocation failures on Intel UHD.

vk_mem_host_room (vk_memory.c) is what the system can still give beyond a reserve (TOTK_MEM_RESERVE_MIB, 1536):
commit, and on a UMA GPU physical memory too; Linux overcommits, so there only a UMA GPU's available memory counts.
NVIDIA is left out, not measured. vk_mem_vram caps the budget at use plus that room, and headroom keeps its spare
block only while four blocks fit in it.

RX 9070, field save, 60 s with the room near zero from the start (TOTK_MEM_RESERVE_MIB=21000): commit 7124 MB instead
of 8965, VRAM 2631 MiB instead of 3781, released textures 153 instead of 1866; mirrors refused for 423 MiB of pools,
GPU 4.45 ms a frame instead of 3.34 ms, 60 fps either way. Without pressure nothing changes. The census logs the
room, the device class line the mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 09:02:36 +03:00
Valiantsin DzerakhandClaude Opus 5.5 06d70d0dfa Report host registers, code bytes and memory at a crash
The crash report named the lifted function and printed the guest registers, but the lifted code keeps guest
registers in host locals and writes them back only at calls, so the dump showed stale values and the faulting
instruction stayed unknown. Logs from #1005, #967 and #998 could not be traced further than the function.

The report now adds the x86 registers of the faulting thread (Windows and Linux), 64 bytes of machine code around
rip with the offset into the lifted function, so the instruction can be found in a local build of the same version,
64 bytes of memory around every register value that points at readable memory, and the first 128 bytes of the
guest stack. --probe-kernel checks the new lines with a child that faults with known values in rbx and x19.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:19:31 +03:00
Valiantsin DzerakhandClaude Opus 5.5 3a26046cde Let the threads every frame goes through prefer the P-cores of a hybrid CPU
On an Intel CPU with P-cores and E-cores a thread on an E-core runs at about 60% of a P-core's speed, and the frame
waits for its slowest critical thread. The game's main thread, the thread that presents, and the vk replay, recorder
and present threads now select the CPU sets of the P-core logical processors the process affinity allows. Nothing is
selected on a CPU with one efficiency class or when the affinity leaves only one kind of core. TOTK_PCORE=0 turns it
off; "power:" log lines say what was selected and which threads took it. LAS Recomp does the same for its game,
render and vsync threads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 11:46:10 +03:00
Valiantsin DzerakhandClaude Opus 5.5 320a41e0dc Ask hybrid laptops for the dedicated GPU and opt out of Windows power throttling
The executable exports NvOptimusEnablement and AmdPowerXpressRequestHighPerformance, which NVIDIA Optimus and AMD
switchable graphics read at start to run it on the dedicated GPU; Optimus can otherwise hide its GPU from a program
it put on the integrated one. At start the process opts out of EcoQoS (E-cores and low clocks on hybrid Intel CPUs
when Windows deems it in the background) and of Windows 11 ignoring its timer resolution while the window is hidden.
One "power:" log line says whether Windows accepted it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 11:13:40 +03:00
Valiantsin DzerakhandClaude Opus 5.5 6e977280bb Name the driver code and the C++ exception type in crash reports
An exception no handler took (a fault in a GPU driver, a C++ exception a driver let escape) listed only the return addresses in totk.exe, and those match only the reporter's own build. #879 and #285 crash in the AMD driver with nothing in the log saying which driver code failed or what it threw.

The unhandled-exception filter now lists the frames of every module on the faulting thread with the module name and offset, up to 12 outside totk.exe so this executable's frames still fit. For a C++ exception thrown by MSVC-built code it reads the thrown type's decorated name from the ThrowInfo, and what() for a std::exception, with every read checked. The guest pc of a host function call is printed with that host function's name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 12:14:45 +03:00
itaskaev2andClaude Opus 5.5 cc4df7d140 Load the NGX core through the platform layer, so vk_dlaa.c has no Win32 calls
tools/check_platform.py failed on vk_dlaa.c: it read the NGX core's folder from the registry, loaded it with LoadLibraryA and GetProcAddress and named the executable's folder with GetModuleFileNameW. platform.h gets plat_ngx_core_path (the registry folder plus _nvngx.dll on Windows, libnvidia-ngx.so.1 on Linux), plat_exe_dir_wide (the folder as NGX's wchar_t string; the UTF-8 to UTF-32 conversion moves from vk_dlaa.c to linux.c) and plat_lib_error, and the core loads through plat_lib_open and plat_lib_sym on both hosts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 19:18:21 +01:00
Wintego 11d404930d Default the game resolution to the display's shape 2026-11-05 08:31:30 +03:00
Valiantsin DzerakhandClaude Opus 5.5 46a3b64895 Keep the console at start: until the game window opens it shows the shader prewarm
d2fbe0a freed the console as soon as the log file was open, but the game window only opens after the shader
prewarm. After an update the prewarm translates every shader again (about 5 minutes on a 24-thread CPU), and the
player saw neither a console nor a window all that time. The console release and show_console are taken back; the
window is still raised at start (#521).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:31:19 +03:00
Valiantsin DzerakhandClaude Opus 5.5 69e7bef868 Merge dev into task/discord
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:11:51 +03:00
Valiantsin DzerakhandClaude Opus 5.5 13e9ffec38 Merge pull request #455: Discord Rich Presence, off by default (avrahambenaram)
Conflicts in cfg.c (the key list) and main.c (the usage line) resolved by keeping dev's lines and adding the
Discord keys and --probe-discord.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:11:12 +03:00
Valiantsin DzerakhandClaude Opus 5.5 d2fbe0a2b1 Close the game's own console and bring its window to the front (#208, #521)
A game started from a frontend (Playnite, RetroBat, ES-DE) or a shortcut got a console beside it, and its window
opened behind other windows, out of reach without a mouse. Once the log file is open, the console is freed when no
other process shares it; a terminal launch keeps its output, and show_console = 1 keeps the console. The window is
raised at start, with AttachThreadInput when Windows refuses the foreground. rt_fatal writes only to the log, and a
console owned by the game closed with it anyway, so no error text is lost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:09:23 +03:00
Valiantsin DzerakhandClaude Opus 5.5 869edd45cf Report crashes outside totk.exe instead of ending without a log line
A fault in a GPU driver, the C runtime or another DLL, or a call through a null pointer, ended the process with
nothing in totk.log: crash_veh reports only faults whose rip is in this executable (#506, #531, #488, #415).

Windows: an unhandled-exception filter reports every exception no handler took. It runs only after every handler
declined, so exceptions a driver raises and catches itself never reach it, and it costs nothing until a crash. The
report names the module (from its export directory, no loader lock) and the return addresses in totk.exe on the
faulting thread's stack, unwound from the faulting context.

Linux: fault_handler passes faults in libraries (Mesa, SDL) and at address 0 to the crash hook as well, with the
library name from dladdr.

--probe-crash-call (strlen in ucrtbase.dll reads address 8) and --probe-crash-null crash on purpose. Before: exit
0xc0000005 and no FATAL line. After: "FATAL: access violation ... (ucrtbase.dll+0xf2b61)" with host frames
probe_crash and main, exit 71.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 09:50:27 +03:00
Avraham ab32d00cc9 Merge remote-tracking branch 'origin/dev' into discord-rich-presence 2026-11-02 13:11:39 -03:00
Avraham 664ee03759 Add Discord Rich Presence, off by default.
A host thread speaks Discord's documented IPC protocol to a running client
through platform.h's new plat_ipc_* (a named pipe on Windows, a Unix socket
in $DISCORD_IPC_PATH, $XDG_RUNTIME_DIR, $TMPDIR or /tmp on Linux) and shows
the game update and the elapsed play time as the status of a configurable
application. It never holds a virtual core, picks up settings changes within
half a second of the menu writing them, re-sends the activity once a minute
as its keep-alive, answers the client's Ping frames and reconnects 15 s
after a break; Discord clears the status when the process ends.

The Menu tab of the settings menu switches discord_rpc, and discord_app_id
and discord_image in totk.ini name the application and its art asset (a
player can register their own). The default app id is the one this was
developed against; the maintainer may want to register a project-owned one
and swap it. --probe-discord runs the exchange without the game: it caught
the opcode numbering (Handshake is 0, not 1) against a live client.

Tested on 1.4.3, Linux: probe passed end to end against Vesktop's arRPC
bridge, an invalid app id falls back to the default, selftest clean.
2026-11-01 22:12:58 -03:00
Valiantsin DzerakhandClaude Sonnet 5.5 d3f403ecf6 Delete read-only folders and cloud placeholders of an old save working copy, retry for 2 s, and name the failing path in the fatal
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 22:54:20 +03:00
Valiantsin DzerakhandClaude Opus 5.5 1bebcfd9ef Rebuild the game at start when an enabled mod has unbuilt code cheats
Code cheats need the game lifted and compiled with them. Running the setup
again did that, but it lifted and compiled everything and prepared the
shaders again.

- The setup keeps the generated C and the objects in its temp folder (about
  1.3 GB). gen.py rewrites only the chunks whose C changed, so Ninja
  recompiles only those. One new code cheat on 1.4.3 took a 47 s lift and
  19 s of compile and link on 24 cores.
- Prepare's key no longer follows the compile key, only the runtime, the
  translator and the game version, so a rebuild keeps the prepared shaders.
- The cleanup writes build-cache/rebuild.txt, the command that opens the
  setup window in a new --rebuild mode on the install folder. That mode runs
  at once and starts the game again when it succeeds.
- At start, runtime/cheats.c runs that command and exits when an enabled
  mod has code cheats the build lacks (plat_spawn, new for Windows and
  Linux). build-cache/rebuild.skip keeps a failed or cancelled rebuild from
  starting again for the same cheats. "Rebuild the game now" on the Cheats
  tab tries again, and [mods] mods_rebuild = off turns the rebuild off.
  Scripted runs never start it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:16:53 +03:00
Valiantsin Dzerakh 4524fed813 Merge pull request #211: Show Xbox button icons in the prompts, with a Button prompts setting that follows the pad layout (waxyfeet)
# Conflicts:
#	docs/installer.md
#	runtime/cfg.c
#	runtime/ui/menu.cpp
#	runtime/ui_text.h
2026-11-01 11:14:40 +03:00
Valiantsin Dzerakh 8643d48d4f Merge pull request #273: Default shadow storage to on when running under Wine (koosc) 2026-11-01 10:55:02 +03:00
Chris Koos d9d6264bb2 Default shadow storage to on when running under Wine 2026-10-31 15:02:30 -07:00
Valiantsin DzerakhandClaude Opus 5.5 cacbdf2549 Leave a guest thread by our own register jump instead of the C runtime's longjmp
ExitThread went back to the thread start with longjmp on a buffer from a non-unwinding setjmp, whose frame field is 0.
When Control Flow Guard is on for the process, ucrtbase's longjmp checks the buffer first and ends the process with
fast fail 13 (0xc0000409) on a frame of 0: offset 0xb373c of ucrtbase 10.0.26100.9711, the offset in the event logs of
#222, #176 and #75. Exploit protection can force CFG on any program, and the first guest thread to exit, right after the
game's first six-axis read, killed the process with no line in the log; the abort and invalid-parameter handlers never
run for a fast fail.

plat_jmp_save and plat_jmp_go keep and restore the Win64 callee-saved registers, MXCSR and the x87 control word
themselves and validate nothing; Linux keeps setjmp and longjmp. Started with CFG forced on, the build before this
change ends with 0xc0000409 at the same log line and in --probe-kernel; this one reaches gameplay and passes the probe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 00:53:17 +03:00
Valiantsin DzerakhandClaude Opus 5.5 65fd631fea Keep a save commit that cannot replace the committed save instead of dropping it
Windows refuses to rename a directory while any file under it is open, so a
virus scanner, Explorer's preview or a sync tool holding a file of
save/<uid> made every Commit of a session fail after its 2 s of retries.
The game had its Commit return 0 and showed the new slots from the working
copy, and the next start deleted that copy: hours of play were gone (#260,
#263, log line "cannot move the committed save (error 5)").

A Commit now renames its finished staging copy to save-tx/ready-<n> before it
touches the committed save. That rename is the commit point. When the swap
into save/<uid> fails, ready-<n> stays the newest save: later working copies
and the next start are made from it, and the next Commit or start retries the
swap. The failure log names the programs holding files in the committed save
(Restart Manager) or says the folder refuses the rename (no DELETE access).
--probe-fs holds a committed file open across two Commits and restarts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 20:20:50 +03:00
Valiantsin DzerakhandClaude Opus 5.5 e451ce6a0d Report abort and invalid C runtime parameters as crashes on Windows
abort() (a failed assert, std::terminate) and an invalid parameter to a C runtime function raise no exception: the
C runtime ends the process with 0xc0000409 and the log stops without a FATAL line. A SIGABRT handler and an
invalid-parameter handler now send both to the crash hook with the host return addresses in totk.exe, and the
report lists them as totk+offset frames after the guest registers. Checked with win32.c in a test program: assert,
abort and fclose(NULL) each give one report whose innermost frame is the failing call.

lifted_function no longer names the nearest lifted function for an address above the lifted code, so a host frame
of the runtime is not reported as game code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 19:57:09 +03:00
Deon FoucheandClaude Opus 5.5 01d69bf6d1 Merge dev into xbox-button-icons, with the restart and the prompts on the Linux port's portable calls
The restart moves into gfx_present.c's gfx_window_closed, which the Win32 and SDL windows share, and linux.c gets
plat_restart and plat_restart_wait: posix_spawn of /proc/self/exe with no descriptor past stderr and the default
signals, and a wait on a pidfd. The prompts code takes plat_file_exists, plat_setenv, strcasecmp and strdup, and the
menu and the log say a game resolution of another shape than 16:9 (16:10 now as well as 21:9) keeps the Switch prompts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 22:14:22 +10:00
Deon FoucheandClaude Opus 5.5 703e721bf8 Offer Restart now under the button prompts while they change after restart
The row opens a confirmation; Restart writes totk.ini, closes the game as Quit game does and starts totk.exe again
with the same command line and environment (plat_restart). The new process waits until the old one is gone before it
moves the log and opens the saves (plat_restart_wait), and the confirmation waits while the game writes a save.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 20:50:17 +10:00
Valiantsin DzerakhandClaude Opus 5.5 43a18a9bb1 Merge pull request #60: Linux port (AppImage) and Steam Deck support (wdiasjunior)
The branch forked 79 commits before main and 98 before dev. Resolutions:

1. Aspect ratio: dev's implementation stays (camera patch at page +56 on 1.4.3, tools/ultrawide_layout.py layouts per
   size in romfs-patches-<w>x<h>, the aspect = fit|stretch setting). The PR's second implementation is dropped: the
   aspect sets of patches-v121.txt and patches-v143.txt (the 1.4.3 one patched the same viewport sites as dev's),
   tools/aspect_layouts.py, the resolution.c shapes that limited game resolutions to 16:9, 16:10 and 21:9, and the
   picture_fit setting. Its letterbox code (gfx_fit_rect) is kept and reads TOTK_ASPECT.
2. Code dev changed where the PR moved it: the stick dead zone, the test gamepad and the paddles go to input.c; the
   DPI awareness, keep-awake and IME detach of gfx_window.c go to gfx_window_win.c; device fault reports and GPU
   breadcrumbs to gfx_present.c. gfx_window_dpi gets an SDL version.
3. dev's Win32 calls in shared code go through platform.h: the shader cache pack reader (PlatFile), the romfs
   overlay checks, the audio pacing waits, the motion, ASTC and device fault locks. plat_cpu_usable and
   plat_commit_info get Linux versions; plat_cpu_count counts the machine's processors on both. dpi_probe.c becomes
   dpi_probe_win.c.
4. assume_focus defaults to 1 as the PR has it (a load interrupted by a focus loss can stop for good) and stays live,
   with dev's Game tab row.
5. The frame limits 40 and 45 join dev's list. The BC 3D storage fallback for Vega only runs with
   TOTK_BLOCK_VIEWS_3D=1, since dev makes 3D BC images without block views by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 13:26:42 +03:00
Lucas-blip-png 91c5312932 Size thread pools by the CPUs of the process's affinity mask
Eight places sized a pool (pipeline workers, library prebuild, translator prewarm and seeding, the multithreaded ASTC decoder) by GetActiveProcessorCount, every logical processor of the machine. A process limited with start /affinity, Task Manager or a launcher then started a pool for 16 CPUs on 4. plat_cpu_usable counts the affinity mask (all active processors for a process that spans processor groups); plat_cpu_count, the machine's count, stays for the system info. Idea from MMadmer's UsableCpuCount in a fork of the Shadow of the Colossus PC port.
2026-10-31 04:51:14 -04:00
Wellington Dias 01712150a4 Read the overlay's GPU load and VRAM from the DRM card on the game GPU's PCI bus, so a machine with an AMD iGPU and an NVIDIA GPU no longer shows the idle iGPU's numbers. 2026-10-31 02:03:34 -03:00
Valiantsin DzerakhandClaude Opus 5.5 31fff74776 Log the memory commit charge with every fatal error
#94 ended on a realloc of 1 MiB that failed with 64 GB of commit allowed, and the log could not tell whether the
process had grown towards the limit or the system ran short. rt_fatal now adds one line with the process's private
bytes and the system's commit in use and limit (plat_commit_info).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 07:23:04 +03:00
Wellington Dias 1d51343ac1 Build and run the runtime natively on Linux.
Shared runtime code now goes through runtime/platform/platform.h, implemented by platform/win32.c and a new
platform/linux.c: futex locks, a userfaultfd and PAGEMAP_SCAN write watch, signal-based async preemption, our own
fiber switch, and a SIGSEGV handler for the thaw and crash hooks. The window, input, menu and software keyboard get
SDL3 back ends beside the Win32 ones, with the swapchain (gfx_present.c) and input merging (input.c) shared. SHA-256
replaces BCrypt, ELF builds get their own override sections, and the build tools gain Linux branches.

On Linux the selftest and the gfx-abi, astc, nvn and window probes pass. probe-vkmem still fails: imported guest
memory cannot be write-watched on Linux, which the shadow buffers in vk_memory.c will address.
2026-10-29 16:56:56 -03:00
Valiantsin DzerakhandClaude Opus 5.5 c7b290f2b0 Write the log of a game run to logs\totk.log next to totk.exe when stderr is not a file
A player who starts totk.exe from Explorer or a shortcut had no log to attach to a report: the log went only to stderr.
A game run whose stderr is not a file (a console or a pipe) now also writes logs\totk.log, keeps the four earlier
runs as totk.1.log to totk.4.log, and stops the file at 64 MiB. Runs whose stderr is a file (run_game.py, 2> file) and
the tool modes write no extra file. Lines from before the file opens (mem_init comes first so the CRT heap stays out of
the guest address space) wait in a static buffer.

Crash reports, fatal errors, IPC and SVC diagnostics, guest debug output and the boot: lines now go through
rt_logf / rt_log_raw, so they reach the file too. Open log folder in the menu opens the folder of the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:41:12 +03:00
Valiantsin DzerakhandClaude Opus 5.5 8e15d70fd4 Scale the performance counter by a fixed-point multiplier set before main: plat_now_ns and rt_ticks divided a 128-bit product by the counter frequency on every call, and __udivti3 took 5% of the game's running samples
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:22:11 +03:00
Valiantsin DzerakhandClaude Opus 5.5 97d3762ba1 Strip milestone and task labels from code comments and drop the dead gfx_stubs.c
Comments named the agent task (T1-T7, M2-M5, Task 2.x) that once owned each file or block, and pointed at local
notes the repository does not carry (backlog items, milestone plans, research notes). They now say what the code
does. gfx_stubs.c held weak stand-ins for functions that every build has defined for a long time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 13:12:50 +03:00
Valiantsin DzerakhandClaude Opus 5.5 05ea4f0e56 Add the performance overlay's sampling (perfmon.c), system info for bug reports (sysinfo.c), their Windows calls, GPU timestamps on demand, and option row helpers: buttons, group headings, greyed-out rows, focus by key
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 20:55:08 +03:00
Valiantsin DzerakhandClaude Opus 5.5 f8d584b3db Add the tabbed settings menu: option rows over the cfg table for the Screen, Game, Sound, Controls and Menu tabs, Continue and Quit game in the bottom row, and a note when totk.ini cannot be written
Rows read and write their key through cfg, mark the default, say "after restart" for start-only settings and grey out what an environment variable fixes; totk.ini is written 0.4 s after the last change and when the menu closes. The graphics card list comes from the Vulkan devices, the window sizes from the monitor, and the language names of Japanese, Korean and Chinese are drawn with glyphs merged in from a system font (plat_font_path). The font atlas and style follow the window height. TOTK_MENU_TEST saves one shot per tab and ignores real input meanwhile; TOTK_MENU_TEST_SET sets settings at given frames (SAVE=1 writes totk.ini). A background test window going fullscreen no longer comes to the front or takes focus, and the log names the present mode the swapchain gets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 10:13:33 +03:00
Valiantsin DzerakhandClaude Opus 5.5 c79c614cee Add the menu and overlay text to the host catalog in English and Russian, menu_text for the menu language, and plat_ui_language for the Windows UI language
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:40:22 +03:00
Valiantsin DzerakhandClaude Opus 5.5 7b85a2681a Keep free_cores=0 as gate on, refuse to rewrite totk.ini after a failed read, drop empty-valued lines on Reset, and add plat_file_set_readonly and plat_file_size
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:23:55 +03:00
Valiantsin DzerakhandClaude Opus 5.5 19c9b648d1 Add the recompiler, Windows runtime, tools and docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 22:27:54 +03:00