205 lines
4.6 KiB
Go
205 lines
4.6 KiB
Go
// Extrahieren der "Rohen" Lump-Bytes einer WAD-Datei
|
|
// siehe def Lumps: https://doomwiki.org/wiki/Lump
|
|
package wad
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type header struct {
|
|
Ident [4]byte // "IWAD" or "PWAD"
|
|
NumLumps int32
|
|
DirOffset int32
|
|
}
|
|
|
|
type DirEntry struct {
|
|
FilePos int32
|
|
Size int32
|
|
Name8 [8]byte
|
|
}
|
|
|
|
func (d DirEntry) Name() string { return trimName(d.Name8) }
|
|
|
|
type Wad struct {
|
|
f *os.File
|
|
dir []DirEntry
|
|
fsize int64
|
|
isIWAD bool
|
|
isPWAD bool
|
|
path string
|
|
}
|
|
|
|
// Öffnen der WAD und checks
|
|
func Open(path string) (*Wad, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open wad: %w", err)
|
|
}
|
|
st, err := f.Stat()
|
|
if err != nil {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("stat wad: %w", err)
|
|
}
|
|
var hdr header
|
|
if err := binary.Read(f, binary.LittleEndian, &hdr); err != nil {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("read header: %w", err)
|
|
}
|
|
id := string(hdr.Ident[:])
|
|
if id != "IWAD" && id != "PWAD" {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("not a WAD file (ident=%q)", id)
|
|
}
|
|
if hdr.DirOffset < 0 || int64(hdr.DirOffset) > st.Size() {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("bad directory offset: %d", hdr.DirOffset)
|
|
}
|
|
if _, err := f.Seek(int64(hdr.DirOffset), io.SeekStart); err != nil {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("seek directory: %w", err)
|
|
}
|
|
if hdr.NumLumps < 0 || hdr.NumLumps > 1<<20 {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("unreasonable lump count: %d", hdr.NumLumps)
|
|
}
|
|
dir := make([]DirEntry, hdr.NumLumps)
|
|
if err := binary.Read(f, binary.LittleEndian, &dir); err != nil {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("read directory: %w", err)
|
|
}
|
|
for i, d := range dir {
|
|
if d.FilePos < 0 || d.Size < 0 {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("dir[%d]: negative pos/size", i)
|
|
}
|
|
end := int64(d.FilePos) + int64(d.Size)
|
|
if end < 0 || end > st.Size() {
|
|
_ = f.Close()
|
|
return nil, fmt.Errorf("dir[%d:%s]: out of file bounds", i, trimName(d.Name8))
|
|
}
|
|
}
|
|
w := &Wad{
|
|
f: f,
|
|
dir: dir,
|
|
fsize: st.Size(),
|
|
isIWAD: id == "IWAD",
|
|
isPWAD: id == "PWAD",
|
|
path: path,
|
|
}
|
|
return w, nil
|
|
}
|
|
|
|
// WAD Closing
|
|
func (w *Wad) Close() error {
|
|
if w == nil || w.f == nil {
|
|
return nil
|
|
}
|
|
err := w.f.Close()
|
|
w.f = nil
|
|
return err
|
|
}
|
|
|
|
func (w *Wad) Dir() []DirEntry { return w.dir }
|
|
|
|
// Lessen der Lump roh Bytes
|
|
func (w *Wad) ReadLump(i int) (name string, data []byte, err error) {
|
|
if i < 0 || i >= len(w.dir) {
|
|
return "", nil, fmt.Errorf("lump index out of range: %d", i)
|
|
}
|
|
de := w.dir[i]
|
|
name = trimName(de.Name8)
|
|
if _, err := w.f.Seek(int64(de.FilePos), io.SeekStart); err != nil {
|
|
return name, nil, fmt.Errorf("seek %s: %w", name, err)
|
|
}
|
|
buf := make([]byte, de.Size)
|
|
_, err = io.ReadFull(w.f, buf)
|
|
if err != nil {
|
|
return name, nil, fmt.Errorf("read %s: %w", name, err)
|
|
}
|
|
return name, buf, nil
|
|
}
|
|
|
|
// Extrahieren der Lump Namen aus den rohen Bytes
|
|
func (w *Wad) ReadLumpByName(name string) ([]byte, int, error) {
|
|
want := strings.ToUpper(name)
|
|
for i, d := range w.dir {
|
|
if trimName(d.Name8) == want {
|
|
_, b, err := w.ReadLump(i)
|
|
return b, i, err
|
|
}
|
|
}
|
|
return nil, -1, fmt.Errorf("lump %q not found", want)
|
|
}
|
|
|
|
// Finden der Map( start und ende) in der WAD
|
|
func (w *Wad) FindMap(marker string) (start, end int, err error) {
|
|
m := strings.ToUpper(marker)
|
|
start = -1
|
|
for i, d := range w.dir {
|
|
if d.Size == 0 && trimName(d.Name8) == m {
|
|
start = i
|
|
break
|
|
}
|
|
}
|
|
if start < 0 {
|
|
return -1, -1, fmt.Errorf("map marker %q not found", m)
|
|
}
|
|
end = len(w.dir)
|
|
for i := start + 1; i < len(w.dir); i++ {
|
|
if w.dir[i].Size == 0 {
|
|
end = i
|
|
break
|
|
}
|
|
}
|
|
return start, end, nil
|
|
}
|
|
|
|
// Laden der Map Lumps
|
|
func (w *Wad) LoadMapLumps(marker string, names ...string) (map[string][]byte, error) {
|
|
start, end, err := w.FindMap(marker)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
want := make(map[string]struct{}, len(names))
|
|
for _, n := range names {
|
|
want[strings.ToUpper(n)] = struct{}{}
|
|
}
|
|
out := make(map[string][]byte, len(names))
|
|
for i := start + 1; i < end; i++ {
|
|
n := trimName(w.dir[i].Name8)
|
|
if _, ok := want[n]; !ok {
|
|
continue
|
|
}
|
|
_, b, err := w.ReadLump(i)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out[n] = b
|
|
if len(out) == len(want) {
|
|
break
|
|
}
|
|
}
|
|
missing := make([]string, 0)
|
|
for n := range want {
|
|
if _, ok := out[n]; !ok {
|
|
missing = append(missing, n)
|
|
}
|
|
}
|
|
if len(missing) > 0 {
|
|
return nil, fmt.Errorf("map %s: missing lumps: %v", strings.ToUpper(marker), missing)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func trimName(n8 [8]byte) string {
|
|
n := string(n8[:])
|
|
if i := strings.IndexByte(n, 0); i >= 0 {
|
|
n = n[:i]
|
|
}
|
|
return strings.ToUpper(strings.TrimSpace(n))
|
|
}
|