Kazarov is the shared build system for TheMoonPeople's PS2 decompilation projects, Chronicle and ChronicleTwo. It holds the container image, the driver script, the CMake rules, the compiler helpers and the port.h header that the projects compile against. Each project consumes it as a git submodule at tools/kazarov.
It builds PS2 executables from the original game disc with the Metrowerks MWCC compiler, which runs under wibo inside the image, and builds ports for the host OS with ordinary toolchains.
scripts/ Bash and Windows entry points; Python support modules
scripts/build/ Compiler, cache, context and override helpers
scripts/run/ PCSX2 launcher
scripts/extract/ Disc extraction and texture conversion
scripts/lib/ Configuration, host helpers and locking
src/cpp/ Shared C++ graphics, audio, platform, SDK and test sources
src/rust/ Rust data publisher sources (.rs only)
cmake/ Shared CMake modules
CMakeLists.txt CMake entry point
Cargo.toml Rust package manifest
Cargo.lock Locked Rust dependencies
include/ C++ headers and test fixtures
image/ Dockerfile and compiler patches
tools/ffmpeg/ Shared FFmpeg tool
tests/ Python suites and CMake graph checks
A project provides kazarov.conf at its root, and its own CMake lists, game sources and disc.
kazarov.conf is a bash file that the driver sources from the project root.
| Variable | Meaning |
|---|---|
KZ_PROJECT |
Project name, lower case. Mounted at /work/$KZ_PROJECT in the container. |
KZ_CONFIGS |
Optional array of configuration names, printed by kazarov configs. |
KZ_DEFAULT_BUILD, KZ_DEFAULT_RUN |
Default configuration for build and run. |
KZ_DEFAULT_BUILD_{LINUX,DARWIN,WINDOWS}, KZ_DEFAULT_RUN_{LINUX,DARWIN,WINDOWS} |
Per-OS defaults; these win on that OS. |
KZ_WINDOWS_HOST_SCRIPT |
Optional PowerShell script for KZ_WHERE=windows configurations. |
KZ_CONTAINER_CONFIGS |
Optional single-line, space-separated list of container configuration names. scripts/kazarov.cmd takes the first argument as a config only when it is listed here or in KZ_WINDOWS_CONFIGS; without this variable, any first argument not starting with - is taken as the config. |
KZ_ENV_PASS |
Optional array of variable names to pass into the container. |
It also defines kz_config NAME, which returns 1 for an unknown name and otherwise sets:
| Variable | Meaning |
|---|---|
KZ_CONFIG |
Canonical name. Defaults to NAME. |
KZ_WHERE |
container, host or windows. |
KZ_HOST_OS |
For host configurations: linux, darwin or windows. The driver refuses to build it elsewhere. |
KZ_BUILD_DIR |
Build directory, relative to the project root. Required for container and host configurations. |
KZ_CMAKE_ARGS (array) or KZ_PRESET |
How to configure. |
KZ_SETUP_TARGETS, KZ_SETUP_STAMP |
Targets built before the main build. If the stamp did not exist before setup, CMake reconfigures once after it appears. |
KZ_BUILD_TARGETS, KZ_RUN_TARGETS |
Default targets for build and for run. |
KZ_LOCKS (array) |
Lock names to hold. Defaults to the configuration name. |
KZ_REQUIRE_ANY (array), KZ_REQUIRE_MESSAGE |
Files of which at least one must exist, and the error text when none does. |
KZ_RUN_ISO |
Disc image to boot in PCSX2 after a run. |
KZ_RUN_EXE, KZ_RUN_OS |
Program to run, and the OS it needs. |
Optional hooks, run with the project root as the working directory:
kz_before_buildruns where the build runs (inside the container, or on the host for host configurations), before the configure step. A failure stops the build.kz_after_buildruns inside the container, after the default targets only.kz_after_run_buildruns inside, after the run targets. A failure here is reported and does not stop the run.kz_before_launchruns on the host, in the project root, before an executable starts. It may setKZ_LAUNCH_PREFIX, an array of command words (for example a lock wrapper) put in front of the executable: the launch isexec "${KZ_LAUNCH_PREFIX[@]}" <exe> "$@". It is empty unless the hook sets it.
A minimal example:
KZ_PROJECT=example
KZ_CONFIGS=(ps2 linux)
KZ_DEFAULT_BUILD=ps2
KZ_DEFAULT_RUN=ps2
KZ_DEFAULT_BUILD_LINUX=linux
KZ_DEFAULT_RUN_LINUX=linux
kz_config() {
case $1 in
ps2)
KZ_CONFIG=ps2
KZ_WHERE=container
KZ_BUILD_DIR=build/ps2
KZ_CMAKE_ARGS=(-DGAME_TARGET=ps2)
KZ_BUILD_TARGETS=(game.iso)
KZ_RUN_ISO=build/ps2/game.iso ;;
linux)
KZ_CONFIG=linux
KZ_WHERE=host
KZ_HOST_OS=linux
KZ_BUILD_DIR=build/linux
KZ_CMAKE_ARGS=(-DGAME_TARGET=linux)
KZ_RUN_EXE=build/linux/game
KZ_RUN_OS=linux ;;
*) return 1 ;;
esac
}kazarov [--inside|--locked] build [config] [targets...]
kazarov [--inside|--locked] run [config] [args...]
kazarov shell [cmd...]
kazarov image
kazarov configs
- The first argument of
buildorrunis a configuration name ifkz_configknows it. Otherwise the default configuration is used and the argument is a target (or a program argument forrun). - Run from any directory. The driver resolves its own location and the project root, and maps the caller's directory into the container.
buildconfigures when the cache is missing or the configure arguments or image tag changed. It deletes the cache and reconfigures with--freshwhenCMAKE_HOME_DIRECTORYorCMAKE_CACHEFILE_DIRdo not match the directories on disk, so a moved project works.runbuilds the run targets, then boots the disc withscripts/run/kz-pcsx2or starts the executable on the host. A container run writes a descriptor (iso:<path>orexe:<path>) to.kazarov/, and the host reads it after the container exits.- Locks are per configuration, in
<root>/.kazarov/locks/<name>.lock. Two runs of one configuration queue; different configurations run together.
Environment variables:
| Variable | Effect |
|---|---|
CLEAN=1 |
Deletes the build directory under the lock first. The lock file itself is kept. |
JOBS=N |
Parallel jobs. Defaults to the CPU count. |
BUILDER=docker|podman |
Container runtime. Podman is preferred when both are present, except on Apple silicon. |
REBUILD_IMAGE=1 |
Rebuilds the image even when its tag exists. |
PCSX2=... |
Emulator command for kz-pcsx2. |
Inside the container the driver also reads KZ_PROJECT_ROOT, KZ_HOST_OS and KZ_RUN_OUT (the descriptor path).
All are Python 3 with the standard library, except kazarov and kz-pcsx2, which are bash.
kz-lock [--shared] [--lock FILE]... [--message TEXT] -- CMD...takes theflocklocks in sorted order, exclusive unless--sharedis given (shared holders run together and wait only for an exclusive one), announces a wait once, runs CMD in its own process group, forwards SIGINT and SIGTERM to the group, and exits with CMD's status.kz-extract --iso ISO --checksum FILE --out DIR --id-file FILE [--force]extracts the disc's files intoDIR/iso. It checks the image against the sha256 checksum file, and records a state file (the id file name without.id, plus.json) with the image size, mtime, sha256, extractor version and each file's size. When nothing changed, it touches nothing. The id file changes only when the content id (sha256 and version) changes. Writes are atomic.kz-split-stamp snapshot DIR SNAPandkz-split-stamp restore DIR SNAP STAMPkeep a split's output mtimes steady when a re-split changes nothing. The stamp is rewritten only when the content hash changes.kz-cached --stamp FILE --input PATH... -- CMD...runs CMD only when a command line or an input's size or mtime has changed. The stamp is written after success and removed after failure. With--then-exec -- CMD... -- PROGRAM...it runs nothing: it fails unless the stamp is current for CMD, then execs PROGRAM.kz-mwcc --obj OBJ --depfile DEP -- FLAGS... SOURCEcompiles one unit throughkz-mwcc-execand writes a Ninja depfile. The project root is the working directory. It readsKZ_MWCC(compiler),KZ_MWCC_RUNNER(defaultwibo) andMWCIncludes.kz-mwcc-exec EXE ARGS...is a drop-in for wibo. It runs the compiler in a temporary directory that mirrors the top level of the project root, then rewrites the compiler's dependency map into absolute paths forKZ_DEPFILE. The temporary directory is removed afterwards.kz-pcsx2 ISOboots an image in PCSX2, usingPCSX2,pcsx2-qt, the PCSX2 install directory on Windows,/Applications/PCSX2.appon macOS, or a Flatpak or AppImage install.scripts/build/m2ctx.py -o OUT.c [unit.cpp] [-I DIR] [-D NAME]writes the declaration context that m2c decompiles against:OUT.cppfor the compiler andOUT.cfor m2c. The default include directories areps2/include,ps2/include/stdandps2/include/sce. It needs the libclang Python bindings, which the image provides.scripts/build/port_override.py list|strip|checkfinds thePORT_OVERRIDEdefinitions in a port's source and removes the matching definitions from the PS2 sources.stripandcheckaccept independent opt-in flags--private-variablesand--private-functionsfor tagged replacements of a unit's static storage or callback definitions.check --private-function-unit UNITlimits function opt-in to named relativeps2/srcunits; passstrip --private-functionsonly for those units. This avoids exporting unrelated private callbacks with the same name as public replacements. The host copy keeps an external declaration and existing references; the retail source stays untouched. Both modes participate in each unit's checker cache key and default to off, so other projects retain the original behavior. Port replacement definitions must still have external linkage. Private-variable selection is global by name: use it only when each selected private variable has one owning unit. Same-named private variables in multiple units cannot safely share one external replacement; disambiguate their names before enabling the option.
Include the modules from the project's CMakeLists.txt:
set(KZ_MWCC "tools/compilers/mwcc.exe") # relative to CMAKE_SOURCE_DIR
set(KZ_MWLD "tools/compilers/mwld.exe")
set(KZ_LIB_INCLUDE_DIRS ps2/include ps2/include/std)
include(${CMAKE_SOURCE_DIR}/tools/kazarov/CMakeLists.txt)Inputs, set before the include:
KZ_MWCC,KZ_MWLD: compiler and linker.KZ_LIB_INCLUDE_DIRS: directories passed to MWCC asMWCIncludes. Angle includes need these. Quoted includes search the-idirectories only.KZ_MWCC_RUNNER(defaultwibo),KZ_AS_FLAGS.- Optional:
KZ_AS(a command list, such aspython3 tools/fake_as.py; the script words that name files are rebuild inputs),KZ_OBJCOPY,KZ_OBJDUMP,KZ_READELF,KZ_WIBO,MIPS_TOOL_PREFIX, and the overlay variables below.
Variables set by the include:
KZ_ROOT(the Kazarov checkout),KZ_ROOT_REL,KZ_BUILD_REL,KZ_BIN,KZ_INCLUDE_REL,KZ_PYTHONPATH.KZ_PYTHON: the Python interpreter.KZ_PYTHONPATHis set in every POST command.KZ_AS,KZ_OBJCOPY,KZ_OBJDUMP,KZ_READELF: binutils, fromMIPS_TOOL_PREFIX(defaultmips-ps2-decompals-).KZ_WIBO,KZ_LD(wiboplusKZ_MWLD).KZ_TOOL_DEPENDS:kz-mwcc,kz-mwcc-execand/opt/kazarov/image-id, if present. Every object depends on them, so a changed toolchain rebuilds the objects.
Functions:
kz_object_dirs(<objs>...)creates the directories of the objects. Neither the assembler norkz-mwcccreates them.kz_asm_object(<obj> <src> [FLAGS ...] [POST COMMAND ... [COMMAND ...]] [DEPENDS ...] [COMMENT text])assembles withKZ_AS. EachCOMMANDgroup inPOSTruns after it, withPYTHONPATHset.kz_mwcc_object(<obj> <src> FLAGS ... [ENV N=V ...] [POST ...] [DEPENDS ...] [COMMENT text] [RAW <raw obj>])compiles throughkz-mwcc. It appends-MDunless the flags already have it, and writes<obj>.das the depfile. WithRAW, MWCC writes<raw obj>and only the POST commands produce<obj>, so two outputs can come from one compile. The depfile is retargeted to<obj>.kz_disc_setup(ISO <p> ISO_CHECKSUM <f> EXTRACT_DIR <d> INPUTS <f>... INPUTS_CHECKSUM <f> INPUTS_CHECKSUM_DIR <d> SPLIT_DIR <d> SPLIT_STAMP <f> SPLIT_COMMAND <cmd>... SPLIT_DEPENDS <f>... [SPLIT_BYPRODUCTS <f>...])creates the targetsextract(only when the disc exists at configure time),setup(the split, through a stamp) anddisassemble(the split, forced). The split runssha256sum -c, a snapshot,SPLIT_COMMAND, then a restore. It setsKZ_EXTRACT_IDandKZ_SPLIT_STAMPin the caller's scope.kz_ctx(OUTPUT ctx.c ctx.cpp HEADERS <f>... [ARGS ...])creates the targetctx, which runsscripts/build/m2ctx.py. The second output must have the first one's name with a.cppextension.- Overlays, for links with overlay sections: set
KZ_OVERLAYS,KZ_OVERLAY_ORIGIN, and optionallyKZ_OVERLAY_HEADER_SIZE(default0x40) andKZ_OVERLAY_FILE_ALIGN(default 128). The functions arekz_expand_overlay_markers(template out), which replaces// @OVERLAY <name>markers with the overlay header, andkz_overlay_link_args(out_var), which builds the-ogand-olarguments formwldps2. Others arekz_overlay_rename_flags,kz_overlay_for_object,kz_check_overlay_prefixes,kz_overlay_section_prefix,kz_overlay_headerandkz_overlay_name_bytes. Overlay objects go in<build>/<name>/.
Other included modules: KazarovToolchain.cmake (paths, tools and kz_absolute_path), KazarovRetargetDepfile.cmake (used by RAW).
Built locally from the Kazarov root, with docker build --platform linux/amd64 --build-arg KZ_IMAGE_ID=<tag> -t kazarov-dev:<tag> -f image/Dockerfile .. .dockerignore limits the context to the image files and FFmpeg tool. There is no registry. The driver builds the image when it is missing or when its tag starts with dirty-, and runs containers by the image ID.
- Tag: the first 12 hex digits of the SHA-256 of the
image/,tools/ffmpeg/and.dockerignoreGit object IDs, ordirty-<hash>when those inputs have uncommitted changes or there is no commit, where<hash>covers the path and content of each image input (kazarov.cmduses plaindirty, which is always rebuilt). The image carries the labelorg.themoonpeople.kazarov.image=<tag>. /opt/kazarov/image-idholds the tag. The last layer writes it, so a changed image id rebuilds the objects and an unchanged one rebuilds nothing.- Base:
debian:trixie-slim,linux/amd64only, because wibo needs an x86_64 kernel. - Contents:
- wibo, pinned by digest. Runs the Metrowerks compiler and linker.
- SatansFiddle, built from source at a pinned revision with four patches from
image/patches/. Its tests are in/usr/local/libexec/satansfiddle-tests/. - binutils-mips-ps2-decompals v0.10 in
/usr/local/binutils-mips-ps2-decompals. - clang, lld and llvm 23 from apt.llvm.org; clangd 22.1.6; objdiff-cli v3.7.3.
/opt/venvwith pycdlib, splat64[mips], libclang, graphviz, toml and levenshtein.- m2c and mwccps2-debugger at pinned commits in
/opt/kazarov/. - gdb, lldb-19, cmake, ninja, and the libraries the PC port links against.
- Environment:
PATHincludes/opt/venv/bin, LLVM 23 and binutils.M2C,MWCCPS2_DEBUGGER_DIR,KZ_MWCC_DEBUG_DIRandLLDB_DEBUGSERVER_PATHare set.
Kazarov ships no FFmpeg code. tools/ffmpeg/kz-ffmpeg (launcher scripts/build/kz-ffmpeg) builds FFmpeg 9.0.2 from the pinned ffmpeg.org tarball, checked against its SHA-256, with only the mpeg2video decoder and the mpegvideo parser:
--disable-everything --disable-programs --disable-doc --disable-network --disable-autodetect
--disable-avformat --disable-avfilter --disable-swscale --disable-swresample --disable-avdevice
--enable-decoder=mpeg2video --enable-parser=mpegvideo --enable-pic --disable-shared --enable-static
--disable-x86asm is added when nasm is absent. The build is LGPL: no --enable-gpl, --enable-nonfree or --enable-version3. It refuses to continue unless configure reports License: LGPL version 2.1 or later.
kz-ffmpeg --out DIR [--jobs N] installs DIR/include/{libavcodec,libavutil}, DIR/lib/libavcodec.a, DIR/lib/libavutil.a, DIR/configure.log and the stamp DIR/kz-ffmpeg.json. --check tests the stamp; an up-to-date DIR is skipped.
Per platform:
- Linux: built into the image at
/opt/kazarov/ffmpeg. The image setsKZ_FFMPEG_DIRto it. - macOS: run on the host, for example from a
kz_before_buildhook inkazarov.conf:tools/kazarov/scripts/build/kz-ffmpeg --out .kazarov/ffmpeg/darwin-arm64. It skips itself when the stamp is current. - Windows: there is no POSIX configure.
tools/windows/acquire.ps1downloads BtbN'swin64-lgpl-sharedbuild of the 7.1 release from a datedautobuild-*tag, checked against a pinned SHA-256, and the project passes-DKZ_FFMPEG_DIR.avcodec-61.dll,avutil-59.dllandswresample-5.dll(which BtbN'savcodec-61.dllimports) ship beside the executable. Dynamic linking keeps LGPL relinking trivial; the static links on Linux and macOS are relinkable from the public sources.
CMake, from the project's CMakeLists.txt:
include(${KZ_ROOT}/cmake/KazarovFfmpeg.cmake)
kz_ffmpeg(<target> [PUBLIC|PRIVATE|INTERFACE]) # default PUBLICKZ_FFMPEG_DIR(cache, PATH):$ENV{KZ_FFMPEG_DIR}when set, otherwise.kazarov/ffmpeg/<os>-<arch>underCMAKE_SOURCE_DIR, with<os>linux,darwinorwindowsand<arch>x64orarm64. Configure fails wheninclude/libavcodec/avcodec.his absent.- Targets:
kz::ffmpeg(interface),kz::avcodecandkz::avutil(static on Linux and macOS, shared on Windows). KZ_FFMPEG_RUNTIME_DLLS(global property): on Windows, the DLL paths, for the consumer to copy beside its executable.
KazarovFfmpeg.cmake is not included by CMakeLists.txt. A game that does not call kz_ffmpeg links nothing of FFmpeg.
include/kazarov/port.h lets one source tree serve the PS2 build and the port. The port defines PORT.
#define PORT_INIT(...) // PORT: = __VA_ARGS__ otherwise: nothing
#define PORT_RETURN(...) // PORT: return __VA_ARGS__ otherwise: nothing
#define PORT_FIX(...) // PORT: __VA_ARGS__ otherwise: nothing
#define PORT_OVERRIDE // empty marker, read by scripts/build/port_override.pyUsage:
#include "kazarov/port.h"
int count PORT_INIT(0); /* initialiser only in the port */
Vec origin PORT_INIT({0, 0, 0}); /* commas are fine inside the macro */
int Lookup(int i) { PORT_RETURN(table[i]); } /* final return the PS2 code does not have */
PORT_FIX(if (index >= count) return 0;) /* port-only code */
PORT_OVERRIDE void MGClearScreen(u_char r) { /* replaces the ps2/src definition */ }Do not wrap PORT_RETURN in a PS2-visible if. The PS2 build then compiles that code. Under MWCC the expansions are empty apart from the stray ; that PORT_RETURN(...); and PORT_FIX(...); leave. tests/port_h/run.sh MWCC_EXE checks that the macro uses compile to the same code as their hand-written PS2 text, and that a PORT build takes the values the macros give.
#include "kazarov/port.h" resolves through an -i directory: -i <root>/tools/kazarov/include. Angle-bracket includes do not search -i directories, so they need MWCIncludes.
src/cpp/ is the host code that every PC port shares. Its include root is include/: consumers write #include "gfx/gfx.hpp" and #include "platform/clock.hpp".
| Directory | Contents |
|---|---|
gfx/ |
Vulkan renderer: display lists, pipelines, readback, shaders/ (compiled to headers by the consumer) |
audio/ |
PCM WAV banks, mixer, reverb, versioned JSON musical/effect sequences, WAV/FLAC/Vorbis decoding |
platform/ |
Host services: window, input, display, clock, files, paths, settings, glyphs, overlay, TrueType font, memory, audio output, image files |
platform/arena |
Per-owner arena blocks over platform/memory: zeroed, 64-byte aligned, 4x headroom, a guard page after each, named overflow aborts, a debug check that a block lies above 4 GiB, WidenNear and PortImagePointer for truncated pointers |
sce/ |
PS2 SDK replacements (eekernel, eekernel_threads, libcdvd, libgraph, libmc, libpad, libvu0, sifdev, sifrpc). eekernel_threads runs kernel threads and semaphores as cooperative fibers on the main OS thread, and a switch inside ClockPump aborts; sce/unimplemented.cpp defines Ps2Unimplemented, behind PS2_UNIMPLEMENTED() in kazarov/port.h |
sce/stubs/ |
Stubs for libdev, libdma, libgraph, libmpeg, libpkt, libsdr, modmsin, sifdma |
tests/ |
GoogleTest cases and fixtures, compiled into each game's test executable |
gfx/,audio/andplatform/never include a game header and are built without the game'sport.h.sce/is compiled by each consumer with its ownps2/include/sceon the include path and itsport.hforce-included. The SDK family selects whichscefiles are built.- The game provides:
SettingsGet()andSettingsChange(const HostSettings &), the definitions behindsettings.hpp. The game callsSettingsSetEnvPrefixto name its environment variables (<prefix>_<name>).PathsConfigure(PathsConfig): the per-user directory name and the data and save environment variables.WindowSetIdentity(): the window title and icon.gfx::RendererConfig:app_name,logical_widthandlogical_height, anddisplay_aspect.
- Shared tests run after the game's
main()has set its identity. They must not assume a particular game. - On Android the window is fixed fullscreen in landscape (
WindowSetModechanges nothing), and the game draws its own on-screen gamepad: it passes the gamepad's state toInputSetTouchOverlay, which pad 0 takes whileInputTouchOverlayVisible(), true after anInputPollthat found no gamepad, keyboard or mouse in use. - Behaviour change (main after f1b8e80): the flanks that carry a full-frame
Draw2Drectangle to the edge of a window wider or taller than the logical frame always sample with clamped wrap (gfx/README.md, "Fill"). A full-frame draw of a repeating texture used to tile into the flanks and now repeats its edge texels there, which removes the seam at the frame's border. Output at the logical aspect is unchanged; only non-4:3 windows differ, and only for such draws.
CMake entry points are in cmake/KazarovPort.cmake:
| Name | Purpose |
|---|---|
kz_port_host_library(<target>) |
Builds gfx/, audio/ and platform/ as one host library |
kz_port_sce_sources(<out-var> LIBS <family>...) |
Sets <out-var> to the sce/ sources for the SDK families named |
kz_port_tests(<target>) |
Builds the shared tests into the consumer's test executable |
KZ_PORT_VENDOR_DIRS |
Include directories of the vendored single-header libraries (stb, dr_libs) |
scripts/kazarov.cmd ROOT build|run [config] [args...]is the Windows entry point. It is a thin shim: it runsscripts/kazarov.ps1withpwsh, orpowershellwhenpwshis not on PATH, and forwards the arguments as typed, so cmd never expands them a second time.ROOTis the project directory, and the script readsKZ_PROJECT, the default configurations andKZ_WINDOWS_CONFIGSfrom itskazarov.conf. A configuration listed inKZ_WINDOWS_CONFIGSruns throughKZ_WINDOWS_HOST_SCRIPTwith PowerShell. A configuration listed inKZ_CONTAINER_CONFIGSruns in the container. A first argument in neither list is a program argument, and the default configuration is used. Any other configuration runs in the container, throughdockerorpodman(BUILDERpicks one).runstarts PCSX2 or the executable after the container exits..gitattributeskeeps scripts LF and.cmd/.batfiles CRLF.scripts/kazarov.cmdmust stay CRLF.- Git Bash (MSYS) runs
scripts/kazarovdirectly. The driver setsMSYS_NO_PATHCONV=1and converts mounts withcygpath -m. - Clone the project inside the WSL filesystem (
~/), not under/mnt/c. Bind mounts of Windows drives are much slower for the many small reads of a build.
python -m pytest -q tests/test_kz_mwcc.py tests/test_kz_lock.py tests/test_kz_extract.py \
tests/test_kz_split_stamp.py tests/test_kz_cached.py tests/test_kazarov_elf.py tests/cmake \
tests/test_host_ps1.py
shellcheck scripts/kazarov scripts/run/kz-pcsx2 scripts/lib/*.sh tests/port_h/run.shtests/test_kazarov_elf.py compares kazarov_elf with the elf.py it was ported from and with baseline objects. It reads them from the paths in KZ_ELF_FIXTURES (colon-separated) and KZ_ELF_REFERENCE, and skips when they are absent. tests/test_kz_extract.py needs xorriso, genisoimage or mkisofs and skips without one. tests/cmake needs cmake, ninja and the helper tools in scripts/build/. tests/test_host_ps1.py runs scripts/kazarov.ps1 under pwsh (from KZ_PWSH, or PATH) and skips without one.
The Rust kazarov_core library exports setup_data(title, data_root) through its
cxx bridge. Call it on the main thread before creating the game's window, only
when the game's data-version check reports missing or outdated data. It returns
true after successful extraction/publication and false if the user closes
setup before starting. Window initialization failures cross the bridge as errors.
The game should recheck its data marker before continuing startup.
Setup provides an editable source path, Choose image, Choose folder, a
progress bar and Okay. The source can be a disc folder or an ISO image; a folder
holding no DATA.DAT and a single .iso file stands for that image. The pickers are native
on Windows and macOS, and uses the XDG desktop portal on Linux; Linux installations
need a portal backend for their desktop. The setup window uses eframe/egui with
OpenGL and supports X11 and Wayland. Linux build dependencies are included in the
toolchain image (pkg-config, X11, Wayland, xkbcommon and EGL development files).
Extraction runs on a worker thread. extract_with_progress reports weighted
phases: source checking (0–5%), raw files (5–40%), conversion (40–90%), final
texture manifests and publication (90–100%). Percentages measure completed work,
not estimated elapsed time. Concurrent callbacks are coalesced monotonically by
the window. Only successful publication reaches 100%; then setup closes and
returns to the caller automatically. Errors allow retry. Closing is disabled
while extraction is active so publishing is never abandoned mid-operation.
Run cargo test --locked and cargo clippy --all-targets --locked -- -D warnings
from this directory to check the Rust library. Retail-data tests are opt-in.
Consumers can include cmake/KazarovRust.cmake to import kazarov_core and
kazarov_bridge, including the native GUI system libraries. On Windows, after
initializing FFmpeg, cmake/KazarovWindowsRuntime.cmake provides
kz_windows_runtime(target) to copy the SDL, FFmpeg, compiler/Rust and Vulkan
runtime DLLs beside each executable on every build. INSTALL also installs those
DLLs and emits a runtime package list; EXECUTABLES adds named executable targets
to that list. The Vulkan loader download
is pinned and hash-checked when KZ_VULKAN_RUNTIME_DIR is not supplied. These
rules copy program dependencies only; they never include retail game assets.
src/rust/models.rs converts the game's models, skeletons and motions into glTF with
gameplay sidecars, and src/rust/actions.rs names their actions. Every CFG KEY
interval becomes one glTF animation named in English from
assets/motion_names.json; conversion fails when a motion name has no entry. A
motion that only drives other bundles' models is published into the models the
game's scripts and code load it onto, never as an animation-only glTF; the scan of
compiled scripts is in src/rust/native_scripts.rs. Each glTF keeps its binary data
in one <file>.gltf.bin beside it. Only ChronicleTwo uses these
modules; Chronicle's pinned revision does not contain the Rust publisher. Changes
to the published format bump data_version.json.
Kazarov is released under the MIT License (see LICENSE), copyright TheMoonPeople.
scripts/kazarov_elf/ is derived from mwccgap's elf.py, which is MIT licensed, copyright (c) 2024 Mark Street. Its license text is in scripts/kazarov_elf/LICENSE.mwccgap.