TSCrunch is an optimal, byte-aligned, LZ+RLE hybrid encoder, designed to maximize decoding speed on NMOS 6502 and derived CPUs, while keeping decent compression performance (for a bytecruncher, that is). TSCrunch was designed as the default asset cruncher for the game A Pig Quest, and, as such, it's optimized for in-memory level compression, but as of version 1.0 it can also create SFX executables for off-line prg crunching.
TSCrunch includes maintained C99, Go, and Java encoders. They implement the same format and produce identical compressed streams. The C and Go implementations in particular make it straightforward to compile TSCrunch for a wide range of operating systems and processor architectures. The C encoder is the fastest implementation and is used for the native Windows x64 and Linux x64 release binaries. The Go encoder provides a portable implementation and is used for the macOS ARM64 release binary. The Java encoder is also the implementation used by the TSCrunch KickAssembler plugin. Precompiled binaries are available for the following platforms:
- windows x64
- linux x64
- mac / darwin arm64
The memory decrunchers require Kick Assembler, but they should be quite easy to port to your assembler of choice.
tscrunch [option] infile outfile
Crunching examples:
tscrunch -x $0820 game.prg crunched.prg
Crunches the file game.prg and generates a self executable crunched.prg, using $0820 as post-decrunch jmp address
tscrunch -p game.prg crunched.bin
Mem-crunches the file game.prg, stripping the 2-byte header and generates a binary file crunched.bin
tscrunch data.bin crunched.bin
Mem-crunches the file data.bin and generates a binary file crunched.bin
tscrunch -i data.prg crunched.prg
Mem-crunches the file data.bin for in-place use, and generates a prg file crunched.prg with the appropriate load address
tscrunch -x2 49152 game.prg crunched.prg
Crunches the file game.prg and generates a self executable crunched.prg with alternative decrunching code, using $c000 (49152) as post-decrunch jmp address. The alternative decrunching code runs from stack instead of zero-page
tscrunch -x 0x1000 -b game.prg crunched.prg
Crunches the file game.prg and generates a self executable crunched.prg that blank the screen while decrunching, using $1000 (0x1000) as post-decrunch jmp address.
Please refer to the inline help (tscrunch -h) for a detailed description of the different crunching options. Note that with the exception of self executables and in-place, all the files generated by TSCrunch are headless binaries, that is they don't come with a 2 byte loader offset.
If you want the C99 encoder (tscrunch.c), you can build it with a standard C compiler:
cc -std=c99 -O2 -o tscrunch.exe tscrunch.c
On Linux and other Unix-like systems, link the math library and omit the .exe suffix:
cc -std=c99 -O2 -o tscrunch tscrunch.c -lm
# Go 1.13 or newer
go build -o tscrunch tscrunch.go
Build tscrunch.go explicitly as shown above. Running go build . in the repository root also includes tscrunch.c, which requires cgo and is not needed for the Go encoder.
The Java encoder is provided as TSCrunch.java. It embeds the boot code, so it is standalone.
Compile and package:
javac TSCrunch.java
jar --create --file tscrunch.jar --main-class TSCrunch TSCrunch*.class
Run:
java -jar tscrunch.jar [options] infile outfile
The C and Java encoders support a quick self-check flag to compare output size against the Go encoder:
tscrunch --selfcheck [options] infile outfile
This runs the Go version (if available) with the same options and prints the output sizes.
- Improved compression by fixing boundary cases in LZ and LZ2 candidate selection.
- Replaced heap-based parsing with deterministic forward dynamic programming.
- Added indexed match discovery and substantially reduced encoder runtime and memory use.
- Synchronized the C99, Go, and Java encoders and verified byte-identical corpus output.
- The compressed format and the three 6502 decrunchers are unchanged from version 1.3.1.
For memory decrunching, please #include decrunch.asm and include the crunched binaries in your code, then use the macro TS_DECRUNCH, as explained by the following code fragment
.pc = $1000 "test"
//decrunches data to $4000
:TS_DECRUNCH(compressed_data,$4000)
jmp *
.align $100
#include "decrunch.asm"
compressed_data:
.import binary "data.bin"
For inplace decrunching, please #define INPLACE before including the decruncher code, as explained by the following code fragment
#define INPLACE
.pc = $1000 "test"
//decrunches data inplace
:TS_DECRUNCH(compressed_data)
jmp *
.align $100
#include "decrunch.asm"
.pc = LoadAddress //as provided by the cruncher
compressed_data:
.import c64 "data.bin"
decruncher.asm is the recommended decruncher for the general case, but other than it two alternative decrunchers are supplied: a small version, which saves some bytes at the cost of speed, and an extreme version which is generally marginally faster, but comes with a larger footprint.
TSCrunch is designed for ultra-fast decrunching while keeping a decent compression ratio. Being a byte-cruncher, it falls short of popular bit-crunchers, such as exomizer or B2, when comparing compression efficiency, but it is usually much faster at decoding. Furthermore, you can expect a 20% to 40% speed bump compared to popular byte-crunchers with similar compression efficiency. The following benchmark compares TSCrunch performance with those of a fast byte-cruncher, TinyCrunch, and a two fast bit-crunchers, B2 and dali, on a real-case compression scenario: Chopper Command, from the same author.
