Created
December 9, 2025 18:47
-
-
Save kystreich/0bb98fc03620e07f24ac45edc7875d79 to your computer and use it in GitHub Desktop.
Decompress LBP Tex File
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| 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