Add lvlbuf-decompressor tool for decompressing level buffer

Rewritten from the original `level-decompressor` Python script into C++ using Principia's own level reader
This commit is contained in:
ROllerozxa 2026-05-31 01:14:08 +02:00
commit 734fb3f8d7
5 changed files with 76 additions and 1 deletions

View file

@ -1,4 +1,4 @@
SUBDIRS := lvl-icon-extractor lvledit progress-get
SUBDIRS := lvl-icon-extractor lvlbuf-decompressor lvledit progress-get
.PHONY: all clean $(SUBDIRS)

View file

@ -10,5 +10,6 @@ This directory contains utility scripts and programs used for Principia developm
Most of these programs rely on source files from the main Principia codebase and need to be compiled to run. Likely only works on Linux. You can use the `Makefile` in each directory or run `make` in the `utils` directory to build all of them.
- `lvl-icon-extractor`: Extract the embedded level icon in a Principia level file
- `lvlbuf-decompressor`: Decompress the level buffer of a Principia level file
- `lvledit`: Edit metadata of Principia level files
- `progress-get`: Get leaderboard score for a given level from a data.bin file

View file

@ -0,0 +1,20 @@
BIN = ../bin
PRINCIPIA = ../..
PROGRAM = lvlbuf-decompressor
SRCS := main.cc $(PRINCIPIA)/src/pkgman.cc $(PRINCIPIA)/src/rand.c
CXX ?= g++
CXXFLAGS ?= -D_NO_TMS -O2 -ffunction-sections -fdata-sections
INCLUDES ?= -I$(PRINCIPIA)/src -I$(PRINCIPIA)/lib
LDFLAGS ?= -lz -Wl,--gc-sections
all: $(BIN)/$(PROGRAM)
$(BIN)/$(PROGRAM): $(SRCS)
@mkdir -p $(BIN)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $@ $^ $(LDFLAGS)
clean:
rm $(BIN)/$(PROGRAM)
.PHONY: all clean

View file

@ -0,0 +1,8 @@
# `lvlbuf-decompressor`
Decompresses the level buffer of a Principia level file and outputs it to a separate file.
## Usage
```bash
lvlbuf-decompressor <input level path> <buffer output path>
```

View file

@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdlib.h>
#include "pkgman.hh"
static lvledit lvl;
int main(int argc, char **argv) {
if (argc != 3) {
fprintf(stderr, "usage: lvlbuf-decompressor <level_path> <out_path>\n");
return 1;
}
const char *in = argv[1];
const char *out = argv[2];
if (!lvl.open_from_path(in)) {
fprintf(stderr, "could not open file %s\n", in);
return 1;
}
// abort for older level versions
if (lvl.lvl.version < LEVEL_VERSION_1_5) {
fprintf(stderr, "Level versions older than 1.5 don't use compression for their level buffer, aborting\n");
return 1;
}
if (lvl.lvl.compression_length > 0)
lvl.lb.zuncompress(lvl.lvl);
int header = lvl.lvl.get_size();
uint64_t body_len = 0;
if (lvl.lb.size > (uint64_t)header)
body_len = lvl.lb.size - (uint64_t)header;
FILE *fp = fopen(out, "wb");
if (!fp) {
fprintf(stderr, "could not open output file %s\n", out);
return 1;
}
if (body_len)
fwrite(lvl.lb.buf + header, 1, body_len, fp);
fclose(fp);
return 0;
}