Add lvl-icon-extractor tool for extracting icon from level files

Rewritten from the original `level-screenshot-converter` Python script into C++ using Principia's own level reader and libpng directly
This commit is contained in:
ROllerozxa 2026-05-31 00:17:18 +02:00
commit 0051979187
5 changed files with 113 additions and 2 deletions

View file

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

View file

@ -2,12 +2,13 @@
This directory contains utility scripts and programs used for Principia development or for working with file formats.
## Scripts
- `prepare_release_builds.sh`:
- `prepare_release_builds.sh`: Download and prepare release builds when making a release
- `set_version.lua`: Update the version number across the codebase when preparing a release (See [Making a Release](https://principia-web.se/wiki/Making_a_Release#incrementing-version-tagging)).
- `update-sandbox-menu.sh`: Update the pre-rendered sandbox and item menu
## Programs
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
- `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 = lvl-icon-extractor
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 -lpng -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 @@
# `lvl-icon-extractor`
Extract the embedded level icon (a 128x128 greyscale bitmap) from a Principia level file and save it as a PNG.
## Usage
```bash
lvl-icon-extractor <level path> <png output path>
```

View file

@ -0,0 +1,82 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
#include "pkgman.hh"
static lvledit lvl;
static int write_png(const char *outpath, uint8_t *icon, int width, int height) {
FILE *fp = fopen(outpath, "wb");
if (!fp) {
fprintf(stderr, "could not open output file %s\n", outpath);
return 1;
}
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
fclose(fp);
fprintf(stderr, "png_create_write_struct failed\n");
return 1;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_write_struct(&png_ptr, NULL);
fclose(fp);
fprintf(stderr, "png_create_info_struct failed\n");
return 1;
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
fprintf(stderr, "libpng fatal error\n");
return 1;
}
png_init_io(png_ptr, fp);
png_set_IHDR(png_ptr, info_ptr, width, height, 8,
PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
for (int y = 0; y < height; ++y) {
// the level icon is stored upside down (GL coord style?), so let's just
// write rows in reverse order here...
png_bytep row = (png_bytep)(icon + (height - 1 - y) * width);
png_write_row(png_ptr, row);
}
png_write_end(png_ptr, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
return 0;
}
int main(int argc, char **argv) {
if (argc != 3) {
fprintf(stderr, "usage: lvl-icon-extractor <level_path> <out.png>\n");
return 1;
}
const char *path = argv[1];
const char *out = argv[2];
if (!lvl.open_from_path(path)) {
fprintf(stderr, "could not open file %s\n", path);
return 1;
}
const int ICON_W = 128;
const int ICON_H = 128;
if (write_png(out, lvl.lvl.icon, ICON_W, ICON_H) != 0) {
fprintf(stderr, "failed to write png %s\n", out);
return 1;
}
return 0;
}