Task 354: .LGR File Format

Task 354: .LGR File Format

File Format Specifications for .LGR

The .LGR file format is used in Elasto Mania (a 2D motorcycle simulation game) to store custom graphics sets. It is a binary format that bundles multiple PCX images along with metadata for how they are used in the game (e.g., as pictures, textures, or masks). The format includes a header, a pictures.lst section for metadata about the images, the embedded PCX files, and an end marker. The specification is based on community reverse-engineering and is detailed below.

  1. List of all the properties of this file format intrinsic to its file system:
  • Magic string: 3 bytes ASCII ('LGR')
  • Version: 2 bytes ASCII (either '12' or '13')
  • Number of PCX files (x): 4-byte integer
  • Pictures.lst version: 4-byte integer (must be 1002 for validity)
  • Number of lst elements (l): 4-byte integer
  • Image names: List of l strings, each 10 bytes (null-terminated, max name length 8 characters)
  • Image types: List of l 4-byte integers (100 = picture, 101 = texture, 102 = mask)
  • Default distances: List of l 4-byte integers (1-999 for pictures/textures; 0 for masks; special values like 400 for certain items, though often ignored)
  • Default clipping: List of l 4-byte integers (0 = unclipped, 1 = ground, 2 = sky)
  • Transparent locations: List of l 4-byte integers (10 = whole image solid (masks only), 11 = palette color 0 transparent, 12 = topleft pixel transparent, 13 = topright, 14 = bottomleft, 15 = bottomright)
  • PCX objects: List of x structures, each containing:
  • Filename: 20 bytes (null-terminated, including '.pcx'; may have garbage after null in older files)
  • Width: 2-byte short (only if version '13')
  • Height: 2-byte short (only if version '13')
  • Length (z): 4-byte integer (size of PCX data in bytes)
  • Data: z bytes (raw PCX file content)
  • End marker: 4 bytes ([0xE7, 0x05, 0x2E, 0x0B])

All multi-byte integers and shorts are little-endian.

  1. Two direct download links for files of format .LGR:
  1. Ghost blog embedded HTML JavaScript for drag-and-drop .LGR file dump:
LGR File Dumper
Drag and drop .LGR file here
  1. Python class for .LGR file handling:
import struct

class LGRFile:
    def __init__(self, filename=None):
        self.magic = b''
        self.version = b''
        self.num_pcx = 0
        self.lst_version = 0
        self.num_lst = 0
        self.image_names = []
        self.image_types = []
        self.distances = []
        self.clippings = []
        self.trans_locs = []
        self.pcx_objects = []  # List of dicts: {'filename': str, 'width': int, 'height': int, 'length': int, 'data': bytes}
        self.end_marker = b''
        if filename:
            self.open(filename)
            self.read()

    def open(self, filename):
        self.file = open(filename, 'rb')

    def read(self):
        self.file.seek(0)
        self.magic = self.file.read(3)
        self.version = self.file.read(2)
        self.num_pcx = struct.unpack('<i', self.file.read(4))[0]
        self.lst_version = struct.unpack('<i', self.file.read(4))[0]
        self.num_lst = struct.unpack('<i', self.file.read(4))[0]

        self.image_names = []
        for _ in range(self.num_lst):
            name_bytes = self.file.read(10)
            name = name_bytes.split(b'\x00', 1)[0].decode('ascii')
            self.image_names.append(name)

        self.image_types = [struct.unpack('<i', self.file.read(4))[0] for _ in range(self.num_lst)]
        self.distances = [struct.unpack('<i', self.file.read(4))[0] for _ in range(self.num_lst)]
        self.clippings = [struct.unpack('<i', self.file.read(4))[0] for _ in range(self.num_lst)]
        self.trans_locs = [struct.unpack('<i', self.file.read(4))[0] for _ in range(self.num_lst)]

        self.pcx_objects = []
        for _ in range(self.num_pcx):
            filename_bytes = self.file.read(20)
            filename = filename_bytes.split(b'\x00', 1)[0].decode('ascii')
            width = height = 0
            if self.version == b'13':
                width = struct.unpack('<h', self.file.read(2))[0]
                height = struct.unpack('<h', self.file.read(2))[0]
            length = struct.unpack('<i', self.file.read(4))[0]
            data = self.file.read(length)
            self.pcx_objects.append({'filename': filename, 'width': width, 'height': height, 'length': length, 'data': data})

        self.end_marker = self.file.read(4)

    def write(self, filename):
        with open(filename, 'wb') as f:
            f.write(self.magic)
            f.write(self.version)
            f.write(struct.pack('<i', self.num_pcx))
            f.write(struct.pack('<i', self.lst_version))
            f.write(struct.pack('<i', self.num_lst))

            for name in self.image_names:
                f.write(name.encode('ascii').ljust(10, b'\x00'))

            for t in self.image_types: f.write(struct.pack('<i', t))
            for d in self.distances: f.write(struct.pack('<i', d))
            for c in self.clippings: f.write(struct.pack('<i', c))
            for tl in self.trans_locs: f.write(struct.pack('<i', tl))

            for pcx in self.pcx_objects:
                f.write(pcx['filename'].encode('ascii').ljust(20, b'\x00'))
                if self.version == b'13':
                    f.write(struct.pack('<h', pcx['width']))
                    f.write(struct.pack('<h', pcx['height']))
                f.write(struct.pack('<i', pcx['length']))
                f.write(pcx['data'])

            f.write(self.end_marker)

    def print_properties(self):
        print(f"Magic: {self.magic.decode('ascii')}")
        print(f"Version: {self.version.decode('ascii')}")
        print(f"Number of PCX files: {self.num_pcx}")
        print(f"Pictures.lst version: {self.lst_version}")
        print(f"Number of lst elements: {self.num_lst}")
        print(f"Image names: {', '.join(self.image_names)}")
        print(f"Image types: {', '.join(map(str, self.image_types))}")
        print(f"Default distances: {', '.join(map(str, self.distances))}")
        print(f"Default clippings: {', '.join(map(str, self.clippings))}")
        print(f"Transparent locations: {', '.join(map(str, self.trans_locs))}")
        print("PCX objects:")
        for pcx in self.pcx_objects:
            print(f"  - Filename: {pcx['filename']}, Width: {pcx['width']}, Height: {pcx['height']}, Length: {pcx['length']}")
        print(f"End marker: [{', 0x'.join(f'{b:02x}' for b in self.end_marker)}]")
  1. Java class for .LGR file handling:
import java.io.*;
import java.nio.*;
import java.util.*;

public class LGRFile {
    private String magic;
    private String version;
    private int numPcx;
    private int lstVersion;
    private int numLst;
    private List<String> imageNames = new ArrayList<>();
    private List<Integer> imageTypes = new ArrayList<>();
    private List<Integer> distances = new ArrayList<>();
    private List<Integer> clippings = new ArrayList<>();
    private List<Integer> transLocs = new ArrayList<>();
    private List<Map<String, Object>> pcxObjects = new ArrayList<>(); // Each map: "filename" String, "width" int, "height" int, "length" int, "data" byte[]
    private byte[] endMarker = new byte[4];
    private RandomAccessFile file;

    public LGRFile(String filename) throws IOException {
        open(filename);
        read();
    }

    public void open(String filename) throws IOException {
        file = new RandomAccessFile(filename, "r");
    }

    public void read() throws IOException {
        file.seek(0);
        byte[] buf = new byte[3];
        file.read(buf);
        magic = new String(buf);
        buf = new byte[2];
        file.read(buf);
        version = new String(buf);
        numPcx = Integer.reverseBytes(file.readInt()); // Little-endian
        lstVersion = Integer.reverseBytes(file.readInt());
        numLst = Integer.reverseBytes(file.readInt());

        for (int i = 0; i < numLst; i++) {
            buf = new byte[10];
            file.read(buf);
            imageNames.add(new String(buf).split("\0")[0]);
        }

        for (int i = 0; i < numLst; i++) imageTypes.add(Integer.reverseBytes(file.readInt()));
        for (int i = 0; i < numLst; i++) distances.add(Integer.reverseBytes(file.readInt()));
        for (int i = 0; i < numLst; i++) clippings.add(Integer.reverseBytes(file.readInt()));
        for (int i = 0; i < numLst; i++) transLocs.add(Integer.reverseBytes(file.readInt()));

        for (int i = 0; i < numPcx; i++) {
            buf = new byte[20];
            file.read(buf);
            String filename = new String(buf).split("\0")[0];
            int width = 0, height = 0;
            if (version.equals("13")) {
                width = Short.reverseBytes(file.readShort());
                height = Short.reverseBytes(file.readShort());
            }
            int length = Integer.reverseBytes(file.readInt());
            byte[] data = new byte[length];
            file.read(data);
            Map<String, Object> pcx = new HashMap<>();
            pcx.put("filename", filename);
            pcx.put("width", width);
            pcx.put("height", height);
            pcx.put("length", length);
            pcx.put("data", data);
            pcxObjects.add(pcx);
        }

        file.read(endMarker);
    }

    public void write(String filename) throws IOException {
        try (RandomAccessFile out = new RandomAccessFile(filename, "rw")) {
            out.write(magic.getBytes());
            out.write(version.getBytes());
            out.writeInt(Integer.reverseBytes(numPcx));
            out.writeInt(Integer.reverseBytes(lstVersion));
            out.writeInt(Integer.reverseBytes(numLst));

            for (String name : imageNames) {
                byte[] nameBytes = name.getBytes();
                out.write(nameBytes);
                for (int j = nameBytes.length; j < 10; j++) out.write(0);
            }

            for (int t : imageTypes) out.writeInt(Integer.reverseBytes(t));
            for (int d : distances) out.writeInt(Integer.reverseBytes(d));
            for (int c : clippings) out.writeInt(Integer.reverseBytes(c));
            for (int tl : transLocs) out.writeInt(Integer.reverseBytes(tl));

            for (Map<String, Object> pcx : pcxObjects) {
                String fn = (String) pcx.get("filename");
                byte[] fnBytes = fn.getBytes();
                out.write(fnBytes);
                for (int j = fnBytes.length; j < 20; j++) out.write(0);
                if (version.equals("13")) {
                    out.writeShort(Short.reverseBytes((short) (int) pcx.get("width")));
                    out.writeShort(Short.reverseBytes((short) (int) pcx.get("height")));
                }
                out.writeInt(Integer.reverseBytes((int) pcx.get("length")));
                out.write((byte[]) pcx.get("data"));
            }

            out.write(endMarker);
        }
    }

    public void printProperties() {
        System.out.println("Magic: " + magic);
        System.out.println("Version: " + version);
        System.out.println("Number of PCX files: " + numPcx);
        System.out.println("Pictures.lst version: " + lstVersion);
        System.out.println("Number of lst elements: " + numLst);
        System.out.println("Image names: " + String.join(", ", imageNames));
        System.out.println("Image types: " + imageTypes);
        System.out.println("Default distances: " + distances);
        System.out.println("Default clippings: " + clippings);
        System.out.println("Transparent locations: " + transLocs);
        System.out.println("PCX objects:");
        for (Map<String, Object> pcx : pcxObjects) {
            System.out.println("  - Filename: " + pcx.get("filename") + ", Width: " + pcx.get("width") + ", Height: " + pcx.get("height") + ", Length: " + pcx.get("length"));
        }
        System.out.printf("End marker: [0x%02x, 0x%02x, 0x%02x, 0x%02x]%n", endMarker[0], endMarker[1], endMarker[2], endMarker[3]);
    }
}
  1. JavaScript class for .LGR file handling:
class LGRFile {
    constructor(buffer = null) {
        this.magic = '';
        this.version = '';
        this.numPcx = 0;
        this.lstVersion = 0;
        this.numLst = 0;
        this.imageNames = [];
        this.imageTypes = [];
        this.distances = [];
        this.clippings = [];
        this.transLocs = [];
        this.pcxObjects = []; // Array of {filename: str, width: num, height: num, length: num, data: Uint8Array}
        this.endMarker = new Uint8Array(4);
        if (buffer) {
            this.read(buffer);
        }
    }

    read(buffer) {
        const view = new DataView(buffer);
        let offset = 0;

        this.magic = String.fromCharCode(view.getUint8(offset++), view.getUint8(offset++), view.getUint8(offset++));
        this.version = String.fromCharCode(view.getUint8(offset++), view.getUint8(offset++));
        this.numPcx = view.getInt32(offset, true); offset += 4;
        this.lstVersion = view.getInt32(offset, true); offset += 4;
        this.numLst = view.getInt32(offset, true); offset += 4;

        for (let i = 0; i < this.numLst; i++) {
            let name = '';
            for (let j = 0; j < 10; j++) {
                const char = view.getUint8(offset++);
                if (char === 0) break;
                name += String.fromCharCode(char);
            }
            this.imageNames.push(name);
        }

        for (let i = 0; i < this.numLst; i++) { this.imageTypes.push(view.getInt32(offset, true)); offset += 4; }
        for (let i = 0; i < this.numLst; i++) { this.distances.push(view.getInt32(offset, true)); offset += 4; }
        for (let i = 0; i < this.numLst; i++) { this.clippings.push(view.getInt32(offset, true)); offset += 4; }
        for (let i = 0; i < this.numLst; i++) { this.transLocs.push(view.getInt32(offset, true)); offset += 4; }

        for (let i = 0; i < this.numPcx; i++) {
            let filename = '';
            for (let j = 0; j < 20; j++) {
                const char = view.getUint8(offset++);
                if (char === 0) break;
                filename += String.fromCharCode(char);
            }
            let width = 0, height = 0;
            if (this.version === '13') {
                width = view.getInt16(offset, true); offset += 2;
                height = view.getInt16(offset, true); offset += 2;
            }
            const length = view.getInt32(offset, true); offset += 4;
            const data = new Uint8Array(buffer.slice(offset, offset + length));
            this.pcxObjects.push({filename, width, height, length, data});
            offset += length;
        }

        for (let i = 0; i < 4; i++) { this.endMarker[i] = view.getUint8(offset++); }
    }

    write() {
        let size = 3 + 2 + 4*4 + this.numLst*10 + this.numLst*4*4; // Header + lists
        this.pcxObjects.forEach(pcx => {
            size += 20 + 4 + pcx.length;
            if (this.version === '13') size += 4; // width + height
        });
        size += 4; // end marker

        const buffer = new ArrayBuffer(size);
        const view = new DataView(buffer);
        let offset = 0;

        for (let char of this.magic) view.setUint8(offset++, char.charCodeAt(0));
        for (let char of this.version) view.setUint8(offset++, char.charCodeAt(0));
        view.setInt32(offset, this.numPcx, true); offset += 4;
        view.setInt32(offset, this.lstVersion, true); offset += 4;
        view.setInt32(offset, this.numLst, true); offset += 4;

        for (let name of this.imageNames) {
            for (let char of name) view.setUint8(offset++, char.charCodeAt(0));
            while (offset % 10 !== 0) view.setUint8(offset++, 0); // Pad to 10
        }

        this.imageTypes.forEach(t => { view.setInt32(offset, t, true); offset += 4; });
        this.distances.forEach(d => { view.setInt32(offset, d, true); offset += 4; });
        this.clippings.forEach(c => { view.setInt32(offset, c, true); offset += 4; });
        this.transLocs.forEach(tl => { view.setInt32(offset, tl, true); offset += 4; });

        this.pcxObjects.forEach(pcx => {
            for (let char of pcx.filename) view.setUint8(offset++, char.charCodeAt(0));
            while (offset % 20 !== 0) view.setUint8(offset++, 0); // Pad to 20
            if (this.version === '13') {
                view.setInt16(offset, pcx.width, true); offset += 2;
                view.setInt16(offset, pcx.height, true); offset += 2;
            }
            view.setInt32(offset, pcx.length, true); offset += 4;
            for (let b of pcx.data) view.setUint8(offset++, b);
        });

        for (let i = 0; i < 4; i++) view.setUint8(offset++, this.endMarker[i]);

        return buffer;
    }

    printProperties() {
        console.log(`Magic: ${this.magic}`);
        console.log(`Version: ${this.version}`);
        console.log(`Number of PCX files: ${this.numPcx}`);
        console.log(`Pictures.lst version: ${this.lstVersion}`);
        console.log(`Number of lst elements: ${this.numLst}`);
        console.log(`Image names: ${this.imageNames.join(', ')}`);
        console.log(`Image types: ${this.imageTypes.join(', ')}`);
        console.log(`Default distances: ${this.distances.join(', ')}`);
        console.log(`Default clippings: ${this.clippings.join(', ')}`);
        console.log(`Transparent locations: ${this.transLocs.join(', ')}`);
        console.log('PCX objects:');
        this.pcxObjects.forEach(pcx => {
            console.log(`  - Filename: ${pcx.filename}, Width: ${pcx.width}, Height: ${pcx.height}, Length: ${pcx.length}`);
        });
        console.log(`End marker: [${Array.from(this.endMarker).map(b => `0x${b.toString(16)}`).join(', ')}]`);
    }
}

// Example usage: const lgr = new LGRFile(arrayBuffer); lgr.printProperties();
  1. C struct and functions for .LGR file handling (using struct as "class" equivalent, with functions):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

// Assume little-endian, no byte swap needed on x86

typedef struct {
    char *filename;
    int width;
    int height;
    int length;
    uint8_t *data;
} PCXObject;

typedef struct {
    char magic[4]; // Null-terminated
    char version[3]; // Null-terminated
    int32_t num_pcx;
    int32_t lst_version;
    int32_t num_lst;
    char **image_names;
    int32_t *image_types;
    int32_t *distances;
    int32_t *clippings;
    int32_t *trans_locs;
    PCXObject *pcx_objects;
    uint8_t end_marker[4];
    FILE *file;
} LGR;

LGR* lgr_new() {
    LGR *lgr = (LGR*)malloc(sizeof(LGR));
    memset(lgr, 0, sizeof(LGR));
    return lgr;
}

void lgr_open(LGR *lgr, const char *filename) {
    lgr->file = fopen(filename, "rb");
}

void lgr_read(LGR *lgr) {
    fseek(lgr->file, 0, SEEK_SET);
    fread(lgr->magic, 1, 3, lgr->file);
    lgr->magic[3] = '\0';
    fread(lgr->version, 1, 2, lgr->file);
    lgr->version[2] = '\0';
    fread(&lgr->num_pcx, sizeof(int32_t), 1, lgr->file);
    fread(&lgr->lst_version, sizeof(int32_t), 1, lgr->file);
    fread(&lgr->num_lst, sizeof(int32_t), 1, lgr->file);

    lgr->image_names = (char**)malloc(lgr->num_lst * sizeof(char*));
    for (int i = 0; i < lgr->num_lst; i++) {
        lgr->image_names[i] = (char*)malloc(11);
        fread(lgr->image_names[i], 1, 10, lgr->file);
        lgr->image_names[i][10] = '\0';
        // Trim null
        for (int j = 9; j >= 0; j--) if (lgr->image_names[i][j] == 0) lgr->image_names[i][j] = '\0';
    }

    lgr->image_types = (int32_t*)malloc(lgr->num_lst * sizeof(int32_t));
    fread(lgr->image_types, sizeof(int32_t), lgr->num_lst, lgr->file);
    lgr->distances = (int32_t*)malloc(lgr->num_lst * sizeof(int32_t));
    fread(lgr->distances, sizeof(int32_t), lgr->num_lst, lgr->file);
    lgr->clippings = (int32_t*)malloc(lgr->num_lst * sizeof(int32_t));
    fread(lgr->clippings, sizeof(int32_t), lgr->num_lst, lgr->file);
    lgr->trans_locs = (int32_t*)malloc(lgr->num_lst * sizeof(int32_t));
    fread(lgr->trans_locs, sizeof(int32_t), lgr->num_lst, lgr->file);

    lgr->pcx_objects = (PCXObject*)malloc(lgr->num_pcx * sizeof(PCXObject));
    for (int i = 0; i < lgr->num_pcx; i++) {
        lgr->pcx_objects[i].filename = (char*)malloc(21);
        fread(lgr->pcx_objects[i].filename, 1, 20, lgr->file);
        lgr->pcx_objects[i].filename[20] = '\0';
        // Trim null
        for (int j = 19; j >= 0; j--) if (lgr->pcx_objects[i].filename[j] == 0) lgr->pcx_objects[i].filename[j] = '\0';
        lgr->pcx_objects[i].width = 0;
        lgr->pcx_objects[i].height = 0;
        if (strcmp(lgr->version, "13") == 0) {
            int16_t w, h;
            fread(&w, sizeof(int16_t), 1, lgr->file);
            fread(&h, sizeof(int16_t), 1, lgr->file);
            lgr->pcx_objects[i].width = w;
            lgr->pcx_objects[i].height = h;
        }
        fread(&lgr->pcx_objects[i].length, sizeof(int32_t), 1, lgr->file);
        lgr->pcx_objects[i].data = (uint8_t*)malloc(lgr->pcx_objects[i].length);
        fread(lgr->pcx_objects[i].data, 1, lgr->pcx_objects[i].length, lgr->file);
    }

    fread(lgr->end_marker, 1, 4, lgr->file);
}

void lgr_write(LGR *lgr, const char *filename) {
    FILE *out = fopen(filename, "wb");
    fwrite(lgr->magic, 1, 3, out);
    fwrite(lgr->version, 1, 2, out);
    fwrite(&lgr->num_pcx, sizeof(int32_t), 1, out);
    fwrite(&lgr->lst_version, sizeof(int32_t), 1, out);
    fwrite(&lgr->num_lst, sizeof(int32_t), 1, out);

    for (int i = 0; i < lgr->num_lst; i++) {
        fwrite(lgr->image_names[i], 1, strlen(lgr->image_names[i]), out);
        for (int j = strlen(lgr->image_names[i]); j < 10; j++) fputc(0, out);
    }

    fwrite(lgr->image_types, sizeof(int32_t), lgr->num_lst, out);
    fwrite(lgr->distances, sizeof(int32_t), lgr->num_lst, out);
    fwrite(lgr->clippings, sizeof(int32_t), lgr->num_lst, out);
    fwrite(lgr->trans_locs, sizeof(int32_t), lgr->num_lst, out);

    for (int i = 0; i < lgr->num_pcx; i++) {
        fwrite(lgr->pcx_objects[i].filename, 1, strlen(lgr->pcx_objects[i].filename), out);
        for (int j = strlen(lgr->pcx_objects[i].filename); j < 20; j++) fputc(0, out);
        if (strcmp(lgr->version, "13") == 0) {
            int16_t w = (int16_t)lgr->pcx_objects[i].width;
            int16_t h = (int16_t)lgr->pcx_objects[i].height;
            fwrite(&w, sizeof(int16_t), 1, out);
            fwrite(&h, sizeof(int16_t), 1, out);
        }
        fwrite(&lgr->pcx_objects[i].length, sizeof(int32_t), 1, out);
        fwrite(lgr->pcx_objects[i].data, 1, lgr->pcx_objects[i].length, out);
    }

    fwrite(lgr->end_marker, 1, 4, out);
    fclose(out);
}

void lgr_print_properties(LGR *lgr) {
    printf("Magic: %s\n", lgr->magic);
    printf("Version: %s\n", lgr->version);
    printf("Number of PCX files: %d\n", lgr->num_pcx);
    printf("Pictures.lst version: %d\n", lgr->lst_version);
    printf("Number of lst elements: %d\n", lgr->num_lst);
    printf("Image names: ");
    for (int i = 0; i < lgr->num_lst; i++) printf("%s%s", lgr->image_names[i], (i < lgr->num_lst - 1) ? ", " : "\n");
    printf("Image types: ");
    for (int i = 0; i < lgr->num_lst; i++) printf("%d%s", lgr->image_types[i], (i < lgr->num_lst - 1) ? ", " : "\n");
    printf("Default distances: ");
    for (int i = 0; i < lgr->num_lst; i++) printf("%d%s", lgr->distances[i], (i < lgr->num_lst - 1) ? ", " : "\n");
    printf("Default clippings: ");
    for (int i = 0; i < lgr->num_lst; i++) printf("%d%s", lgr->clippings[i], (i < lgr->num_lst - 1) ? ", " : "\n");
    printf("Transparent locations: ");
    for (int i = 0; i < lgr->num_lst; i++) printf("%d%s", lgr->trans_locs[i], (i < lgr->num_lst - 1) ? ", " : "\n");
    printf("PCX objects:\n");
    for (int i = 0; i < lgr->num_pcx; i++) {
        printf("  - Filename: %s, Width: %d, Height: %d, Length: %d\n",
               lgr->pcx_objects[i].filename, lgr->pcx_objects[i].width, lgr->pcx_objects[i].height, lgr->pcx_objects[i].length);
    }
    printf("End marker: [0x%02x, 0x%02x, 0x%02x, 0x%02x]\n",
           lgr->end_marker[0], lgr->end_marker[1], lgr->end_marker[2], lgr->end_marker[3]);
}

void lgr_free(LGR *lgr) {
    for (int i = 0; i < lgr->num_lst; i++) free(lgr->image_names[i]);
    free(lgr->image_names);
    free(lgr->image_types);
    free(lgr->distances);
    free(lgr->clippings);
    free(lgr->trans_locs);
    for (int i = 0; i < lgr->num_pcx; i++) {
        free(lgr->pcx_objects[i].filename);
        free(lgr->pcx_objects[i].data);
    }
    free(lgr->pcx_objects);
    if (lgr->file) fclose(lgr->file);
    free(lgr);
}

// Example usage: LGR *lgr = lgr_new(); lgr_open(lgr, "file.lgr"); lgr_read(lgr); lgr_print_properties(lgr); lgr_write(lgr, "new.lgr"); lgr_free(lgr);