2020-04-16 03:03:51 +01:00
|
|
|
// package idx parses the Idx/WarHammer.idx file. It groups the sprites in
|
|
|
|
// Anim/WarHammer.ani into playable animations.
|
2020-04-15 21:11:01 +01:00
|
|
|
package idx
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
2020-04-16 03:03:51 +01:00
|
|
|
"fmt"
|
|
|
|
//"log"
|
2020-04-15 21:11:01 +01:00
|
|
|
"os"
|
|
|
|
//"strings"
|
|
|
|
)
|
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
const (
|
|
|
|
NumGroups = 512 // Experimentally determined
|
|
|
|
)
|
|
|
|
|
|
|
|
type Idx struct {
|
|
|
|
Filename string
|
|
|
|
Groups []Group
|
2020-04-15 21:11:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
type Group struct {
|
|
|
|
Spec Spec
|
|
|
|
Records []Record
|
|
|
|
Details []Detail // Records and details are correlated by index
|
2020-04-15 21:11:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
// type Spec links a set of animations to a starting sprite in WarHammer.ani
|
|
|
|
type Spec struct {
|
|
|
|
Offset uint32 // Where the Records for this Spec are to be found
|
|
|
|
Count uint32 // Number of Records for this Spec
|
|
|
|
SpriteIdx uint32 // Index of the first sprite in
|
2020-04-15 21:11:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
type Record struct {
|
|
|
|
// A guess, but each group of 8 records with increasing compass points share
|
|
|
|
// this value.
|
|
|
|
ActionID uint16
|
|
|
|
|
|
|
|
Compass byte // It's odd to only have one byte. Maybe Unknown1 belongs to this too?
|
|
|
|
|
|
|
|
Unknown1 byte // ??? Only see values 0x33 and 0x00 for librarian.
|
|
|
|
Offset uint32 // Where the Detail for this Record is to be found.
|
|
|
|
NumFrames uint32 // A guess, but seems to fit. Number of frames for this action + compass.
|
|
|
|
}
|
|
|
|
|
|
|
|
type Detail struct {
|
|
|
|
FirstSprite uint16 // Relative offset from the group's SpriteIdx
|
|
|
|
LastSprite uint16 // Relative offset from the group's SpriteIdx
|
|
|
|
Unknown1 uint16 // Could also be LastSprite? Something else? AtRestSprite?
|
|
|
|
Unknown2 uint16 // Number of resting sprites, if we're AtRestSprite?
|
2020-04-15 21:11:01 +01:00
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
Padding [12]byte // Set to zero in the cases I've looked at so far.
|
|
|
|
|
|
|
|
// Remainder []byte // FIXME: no idea what this is yet, but we seem to have NumFrames*6 of them
|
2020-04-15 21:11:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func Load(filename string) (*Idx, error) {
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
out := &Idx{
|
|
|
|
Filename: filename,
|
|
|
|
Groups: make([]Group, NumGroups),
|
|
|
|
}
|
2020-04-15 21:11:01 +01:00
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
var specs [NumGroups]Spec
|
|
|
|
|
|
|
|
if err := binary.Read(f, binary.LittleEndian, &specs); err != nil {
|
|
|
|
return nil, fmt.Errorf("reading specs: %v", err)
|
2020-04-15 21:11:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
for i, spec := range specs {
|
|
|
|
group := &out.Groups[i]
|
|
|
|
group.Spec = spec
|
|
|
|
group.Records = make([]Record, spec.Count)
|
|
|
|
group.Details = make([]Detail, spec.Count)
|
|
|
|
|
|
|
|
if _, err := f.Seek(int64(spec.Offset), 0); err != nil {
|
|
|
|
return nil, fmt.Errorf("spec %v: seeking: %v", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can read all records at once
|
|
|
|
if err := binary.Read(f, binary.LittleEndian, &group.Records); err != nil {
|
|
|
|
return nil, fmt.Errorf("spec %v: reading records: %v", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// But we need to step through the records to learn where to read details
|
|
|
|
for j, rec := range group.Records {
|
|
|
|
// group.Details[j].Remainder = make([]byte, rec.NumFrames*6)
|
|
|
|
|
|
|
|
if _, err := f.Seek(int64(rec.Offset), 0); err != nil {
|
|
|
|
return nil, fmt.Errorf("spec %v, record %v: seeking to detail: %v", i, j, err)
|
|
|
|
}
|
2020-04-15 21:11:01 +01:00
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
if err := binary.Read(f, binary.LittleEndian, &group.Details[j]); err != nil {
|
|
|
|
return nil, fmt.Errorf("spec %v, record %v: reading detail: %v", i, j, err)
|
|
|
|
}
|
2020-04-15 21:11:01 +01:00
|
|
|
}
|
|
|
|
|
2020-04-16 03:03:51 +01:00
|
|
|
out.Groups[i] = *group
|
2020-04-15 21:11:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|