# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License. # See LICENSE.md for license information. # SPDX-License-Identifier: MIT # # Host-side tools for the JoeyLib asset pipeline. # # These build with the host's default `cc` (no cross-toolchain) into # build/tools/ (JOEY_BUILD's, make/common.mk). Currently: # adfTool -- read files out of an Amiga disk image (.adf): the # installer's import. # assetbake -- indexed PNG -> a target's tile bank (.tbk) or sprite # cels (.spr). # diffUberHashes -- two UBER logs compared op by op by surface hash: the # golden gates' check against tests/goldens/uber. # diffUberPerf -- two UBER logs compared op by op by ops/sec, the worst # gaps against a threshold first. # iconBake -- one picture -> each system's program icon. # modloopend -- inject an E8F stop-marker pattern into a .MOD ahead # of time (the Amiga audio HAL marks a song played with # loop false itself). joeymod invokes it for .amod output. # modToNtp -- a .MOD -> a NinjaTrackerPlus song (.NTP), the IIgs # replayer's: joeymod invokes it for .ntp output. # rawCd -- a raw CD image's track, or its El Torito boot floppy: # the installer's BeOS VM. # scanPtrBug -- find the w65816 compiler's far-pointer bug (a pointer's # bank in its offset half) in its assembly output. # songbake -- chip-tracker text notation (.song) -> a JYM1 stream. # spritebake -- the sprite pre-compiler, one per target (below). # szddExpand -- expand a COMPRESS.EXE (SZDD) file: the installer's WinG. # tiled2joey -- a Tiled map (TMX or TMJ) -> a tile sheet and maps. # tileBankExpect -- the tile-bank gates' expected pixels # (tests/host/tileBankStage.sh). # traceLogo -- a flat-color PNG logo -> a layered SVG, through the # potrace program: remakes assets/joeylibLogo.svg. # uberPerfTable -- every port's UBER log -> one Markdown table of ops/sec # against a reference port. # vgm2song -- a tracker's VGM export (Furnace) -> a .song. # winIni -- edit a Windows 3.x .INI file: the installer's Windows 3.11 # and the Win16 gates. # xdftool -- read and write Human68k floppy images (.XDF): joey-run # and the X68000 gates stage programs with it. include $(dir $(lastword $(MAKEFILE_LIST)))/common.mk BUILD_DIR := $(JOEY_BUILD)/tools TOOLS_DIR := $(REPO_DIR)/tools # A tool lands under its name in one rename. The ports' makes, run side by # side (make -j all), each ask this file for the tools they bake with, so two # can build one tool at once, and one ran it while another was still writing # it ("Permission denied"). Each links to a name of its own (the shell's # process id) and renames it into place: whoever runs the tool finds the old # one or the new one, whole. TOOL_TMP = $@.$$$$.tmp TOOL_LAND = mv -f $(TOOL_TMP) $@ # The tools of one C file (and any headers beside it) over tools/common (PNG, # DEFLATE, whole files, byte buffers): tools//.c, or a test's # helper in tests/host. COMMON_TOOLS := adfTool assetbake diffUberHashes diffUberPerf iconBake modToNtp rawCd scanPtrBug songbake szddExpand tiled2joey traceLogo uberPerfTable vgm2song winIni xdftool TOOL_COMMON_SRC := $(wildcard $(TOOLS_DIR)/common/*.c) TOOL_COMMON_HDRS := $(wildcard $(TOOLS_DIR)/common/*.h) # The gates' helpers (scripts/verify-*.sh and friends, which run them through # joeyTool): tests/gate/.c, each over tools/common and the gates' own # shared code in tests/gate/common. GATE_DIR := $(REPO_DIR)/tests/gate GATE_TOOLS := $(basename $(notdir $(wildcard $(GATE_DIR)/*.c))) GATE_COMMON_SRC := $(wildcard $(GATE_DIR)/common/*.c) GATE_COMMON_HDRS := $(wildcard $(GATE_DIR)/common/*.h) MODLOOPEND_SRC := $(TOOLS_DIR)/modloopend/modloopend.c $(SRC_CORE)/modStopMarker.c MODLOOPEND_BIN := $(BUILD_DIR)/modloopend # spritebake -- offline sprite pre-compiler. Built ONCE PER TARGET on the # host: it links the shared staging (spriteStage.c) + that target's emitter, # compiled with -DJOEYLIB_PLATFORM_ so JOEY_SPRITE_SHIFT_COUNT and the # emitted machine code match the target while running on the host. Because # the emitters use only fixed/gated compile-time constants, host output is # byte-identical to the target JIT (see tools/spritebake/spritebake.c). SPRITEBAKE_SRC := $(TOOLS_DIR)/spritebake/spritebake.c $(SRC_CG)/spriteStage.c SPRITEBAKE_INC := -I$(INCLUDE_DIR) -I$(SRC_CORE) -I$(SRC_CG) # The headers that carry the .spc format and the emitted-code ABI (a bump of # SPC_MAGIC must rebuild every baker, or its .spc files would be refused). SPRITEBAKE_HDRS := $(wildcard $(INCLUDE_DIR)/joey/*.h $(SRC_CORE)/*.h $(SRC_CG)/*.h) .PHONY: all tools clean-tools modloopend tileBankExpect $(COMMON_TOOLS) $(GATE_TOOLS) \ spritebake-dos spritebake-amiga spritebake-atarist spritebake-iigs spritebake-x68000 all tools: $(MODLOOPEND_BIN) $(addprefix $(BUILD_DIR)/,$(COMMON_TOOLS)) modloopend: $(MODLOOPEND_BIN) tileBankExpect: $(BUILD_DIR)/tileBankExpect $(foreach t,$(COMMON_TOOLS),$(eval $(t): $(BUILD_DIR)/$(t))) $(foreach t,$(GATE_TOOLS),$(eval $(t): $(BUILD_DIR)/$(t))) spritebake-dos: $(BUILD_DIR)/spritebake-dos spritebake-amiga: $(BUILD_DIR)/spritebake-amiga spritebake-atarist: $(BUILD_DIR)/spritebake-atarist spritebake-iigs: $(BUILD_DIR)/spritebake-iigs spritebake-x68000: $(BUILD_DIR)/spritebake-x68000 # $(call commonToolRule,NAME,SOURCE): NAME from SOURCE and tools/common. define commonToolRule $(BUILD_DIR)/$(1): $(2) $(wildcard $(dir $(2))*.h) $(TOOL_COMMON_SRC) $(TOOL_COMMON_HDRS) @mkdir -p $$(dir $$@) $(HOST_CC) $(HOST_CFLAGS) -I$(TOOLS_DIR)/common $$(filter %.c,$$^) -o $$(TOOL_TMP) -lm && $$(TOOL_LAND) endef $(foreach t,$(COMMON_TOOLS),$(eval $(call commonToolRule,$(t),$(TOOLS_DIR)/$(t)/$(t).c))) $(eval $(call commonToolRule,tileBankExpect,$(REPO_DIR)/tests/host/tileBankExpect.c)) # $(call gateToolRule,NAME): tests/gate/NAME.c over tools/common and # tests/gate/common. define gateToolRule $(BUILD_DIR)/$(1): $(GATE_DIR)/$(1).c $(GATE_COMMON_SRC) $(GATE_COMMON_HDRS) $(TOOL_COMMON_SRC) $(TOOL_COMMON_HDRS) @mkdir -p $$(dir $$@) $(HOST_CC) $(HOST_CFLAGS) -I$(TOOLS_DIR)/common -I$(GATE_DIR)/common $$(filter %.c,$$^) -o $$(TOOL_TMP) -lm && $$(TOOL_LAND) endef $(foreach t,$(GATE_TOOLS),$(eval $(call gateToolRule,$(t)))) $(MODLOOPEND_BIN): $(MODLOOPEND_SRC) $(SRC_CORE)/modStopMarker.h @mkdir -p $(dir $@) $(HOST_CC) $(HOST_CFLAGS) -I$(SRC_CORE) $(MODLOOPEND_SRC) -o $(TOOL_TMP) && $(TOOL_LAND) $(BUILD_DIR)/spritebake-dos: $(SPRITEBAKE_SRC) $(SRC_X86)/spriteEmitX86.c $(SPRITEBAKE_HDRS) @mkdir -p $(dir $@) $(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_DOS $(SPRITEBAKE_INC) -I$(SRC_X86) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND) $(BUILD_DIR)/spritebake-amiga: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS) @mkdir -p $(dir $@) $(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_AMIGA $(SPRITEBAKE_INC) -I$(SRC_DIR)/amiga -I$(SRC_68K) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND) # The X68000 runs the Amiga's planar 68k emitter unchanged (see # make/x68000.mk), so its bake is the same source under its own target byte. $(BUILD_DIR)/spritebake-x68000: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS) @mkdir -p $(dir $@) $(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_X68000 $(SPRITEBAKE_INC) -I$(SRC_DIR)/x68000 -I$(SRC_68K) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND) $(BUILD_DIR)/spritebake-atarist: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitInterleaved68k.c $(SRC_68K)/spriteC2p.c $(SPRITEBAKE_HDRS) @mkdir -p $(dir $@) $(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_ATARIST $(SPRITEBAKE_INC) -I$(SRC_DIR)/atarist -I$(SRC_68K) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND) $(BUILD_DIR)/spritebake-iigs: $(SPRITEBAKE_SRC) $(SRC_DIR)/iigs/spriteEmitIigs.c $(SPRITEBAKE_HDRS) @mkdir -p $(dir $@) $(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_IIGS $(SPRITEBAKE_INC) $(filter %.c,$^) -o $(TOOL_TMP) && $(TOOL_LAND) clean-tools: rm -rf $(BUILD_DIR)