Skip to content

Instantly share code, notes, and snippets.

@kystreich
Created December 9, 2025 18:47
Show Gist options
  • Select an option

  • Save kystreich/0bb98fc03620e07f24ac45edc7875d79 to your computer and use it in GitHub Desktop.

Select an option

Save kystreich/0bb98fc03620e07f24ac45edc7875d79 to your computer and use it in GitHub Desktop.
Decompress LBP Tex File
/*
A simple script to show how to decompress LBP TEX files used for images/icons.
This assumes that the file will be DDS, which may not always be true. Some files have been known to use JPEG/PNG
*/
package main
import (
"bytes"
"compress/zlib"
"encoding/binary"
"fmt"
"io"
"os"
)
const (
unusedFlagSize = 2
chunkDefinitionOffset = 8
chunkDefinitionSize = 4
)
// Share a buffer between 2-4b reads to decrease allocation
var sharedBuf = make([]byte, 4)
func loadGameIcon() ([]byte, error) {
data, err := os.ReadFile("./gameIcon")
if err != nil {
return nil, fmt.Errorf("failed to open game icon file")
}
return data, nil
}
func readHeader(r *bytes.Reader) (string, error) {
_, err := r.Read(sharedBuf)
if err != nil {
return "", err
}
return string(sharedBuf), nil
}
func readChunkCount(r *bytes.Reader) (uint16, error) {
r.Seek(unusedFlagSize, io.SeekCurrent)
_, err := r.Read(sharedBuf)
if err != nil {
return 0, fmt.Errorf("failed to read chunk count @%d", io.SeekCurrent)
}
return binary.BigEndian.Uint16(sharedBuf), nil
}
func readChunkDefinition(r *bytes.Reader, offset int64) (compressedSize uint16, uncompressedSize uint16, err error) {
_, err = r.Seek(offset, io.SeekStart)
if err != nil {
return 0, 0, fmt.Errorf("failed to seek to chunk definition @%d: %w", offset, err)
}
_, err = r.Read(sharedBuf)
if err != nil {
return 0, 0, fmt.Errorf("failed to read chunk @%d-%d: %w", offset, offset+chunkDefinitionSize, err)
}
// First two bytes are the size of the compressed data, second two are the uncompressed size.
return binary.BigEndian.Uint16(sharedBuf[0:2]), binary.BigEndian.Uint16(sharedBuf[2:4]), nil
}
func main() {
gameIconData, err := loadGameIcon()
if err != nil {
panic("Failed to read game icon")
}
reader := bytes.NewReader(gameIconData)
header, err := readHeader(reader)
if err != nil {
panic("Failed to read file header")
}
chunkCount, err := readChunkCount(reader)
if err != nil {
panic("Failed to read chunk count")
}
fmt.Printf("Header: %s\n", header)
fmt.Printf("Chunk Count: %d\n\n", chunkCount)
// Probably not the best practice? im not good enough at go to know :p
combined := make([]byte, 0, chunkCount*65535)
chunkDataOffset := int64(chunkDefinitionOffset + chunkDefinitionSize*int(chunkCount))
for i := 0; i < int(chunkCount); i++ {
defOffset := int64(chunkDefinitionOffset + chunkDefinitionSize*i)
compressedSize, decompressedSize, err := readChunkDefinition(reader, defOffset)
if err != nil {
panic("Failed to read chunk definition")
}
chunk := make([]byte, compressedSize)
reader.ReadAt(chunk, chunkDataOffset)
chunkDataOffset += int64(compressedSize)
byteReader := bytes.NewReader(chunk)
zlibReader, err := zlib.NewReader(byteReader)
if err != nil {
panic("Failed to make zlib reader")
}
defer zlibReader.Close()
decompressed, err := io.ReadAll(zlibReader)
if err != nil {
panic(fmt.Sprintf("Failed to read zlib decompression %s", err))
}
combined = append(combined, decompressed...)
fmt.Printf("Chunk %d:\n", i+1)
fmt.Printf("\tCompressed Size: %d\n", compressedSize)
fmt.Printf("\tDecompressed Size: %d\n", decompressedSize)
}
os.WriteFile("output.dds", combined, 0644)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment