A few more .idx realisations, and some parsing code
This commit is contained in:
@@ -201,8 +201,12 @@ func loadFonts() {
|
|||||||
|
|
||||||
func loadIdx() {
|
func loadIdx() {
|
||||||
idxPath := filepath.Join(*gamePath, "Idx", "WarHammer.idx")
|
idxPath := filepath.Join(*gamePath, "Idx", "WarHammer.idx")
|
||||||
_, err := idx.Load(idxPath)
|
idx, err := idx.Load(idxPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to parse %s as idx: %v", idxPath, err)
|
log.Fatalf("Failed to parse %s as idx: %v", idxPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i, group := range idx.Groups {
|
||||||
|
log.Printf("Group %2d: %4d records, start sprite is %6d", i, len(group.Records), group.Spec.SpriteIdx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -309,11 +309,14 @@ matches the value at offset 8. This, then, must be the link.
|
|||||||
|
|
||||||
If the first sprite is 0, the displayed sprite is 64 (`0x40`)...
|
If the first sprite is 0, the displayed sprite is 64 (`0x40`)...
|
||||||
|
|
||||||
|
We still need to know how to go from "librarian" to "index 11", though. The
|
||||||
|
`CTYPE_LIBRARIAN` value in `HasAction.ani` gives librarians an 8...
|
||||||
|
|
||||||
|
|
||||||
| Offset | Size | Meaning |
|
| Offset | Size | Meaning |
|
||||||
| ------ | ---- | ------- |
|
| ------ | ---- | ------- |
|
||||||
| 0 | 4 | Offset of type 2 record(s) |
|
| 0 | 4 | Offset of type 2 records |
|
||||||
| 4 | 4 | Unknown |
|
| 4 | 4 | Number of type 2 records |
|
||||||
| 8 | 4 | First sprite in `WarHammer.ani` for this record |
|
| 8 | 4 | First sprite in `WarHammer.ani` for this record |
|
||||||
|
|
||||||
### Type 2 record(s)
|
### Type 2 record(s)
|
||||||
@@ -334,7 +337,8 @@ Next, we read 5x 12-byte records - 60 bytes total - from that offset in the type
|
|||||||
the reads of type 2 records stop - so we were searching for it.
|
the reads of type 2 records stop - so we were searching for it.
|
||||||
|
|
||||||
In the first 12-byte record, we have a close offset: `0x098350`. So we have
|
In the first 12-byte record, we have a close offset: `0x098350`. So we have
|
||||||
1,824 bytes available in this block of type 2 records - enough for 152 of them.
|
1,824 bytes available in this block of type 2 records - enough for 152 of them,
|
||||||
|
which is the number specified in the second position of the type 1 header.
|
||||||
|
|
||||||
What is the significance of the fifth 12-byte read? Why do we move onto type 3
|
What is the significance of the fifth 12-byte read? Why do we move onto type 3
|
||||||
records when we reach it? When we place the librarian, he is **facing** south,
|
records when we reach it? When we place the librarian, he is **facing** south,
|
||||||
@@ -354,7 +358,7 @@ total, but I haven't counted the actions yet.
|
|||||||
|
|
||||||
| Offset | Size | Meaning |
|
| Offset | Size | Meaning |
|
||||||
| ------ | ---- | ------- |
|
| ------ | ---- | ------- |
|
||||||
| 0 | 2? | Static per each group of 8 type-2 records? |
|
| 0 | 2? | ActionID? Static per each group of 8 type-2 records? |
|
||||||
| 2 | 1? | Counts up from `01` to `08` in each group of 8 type-2 records? |
|
| 2 | 1? | Counts up from `01` to `08` in each group of 8 type-2 records? |
|
||||||
| 3 | 1? | Is `0x33` for all but the last 4 groups of 8 type-2 records? |
|
| 3 | 1? | Is `0x33` for all but the last 4 groups of 8 type-2 records? |
|
||||||
| 4 | 4 | Position of type 3 record |
|
| 4 | 4 | Position of type 3 record |
|
||||||
@@ -386,9 +390,13 @@ offsets of the frames we load.
|
|||||||
| ------ | ---- | ------- |
|
| ------ | ---- | ------- |
|
||||||
| 0 | 2 | First sprite in animation (relative offset) |
|
| 0 | 2 | First sprite in animation (relative offset) |
|
||||||
| 2 | 2 | Last sprite in animation (relative offset)? |
|
| 2 | 2 | Last sprite in animation (relative offset)? |
|
||||||
| 4 | 2? | Could also be last sprite in animation? |
|
| 4 | 2? | Could also be last sprite in animation? |
|
||||||
| 6 | 2? | ??? |
|
| 6 | 2? | ??? |
|
||||||
| 8 | 12? | ??? - unset in this case |
|
| 8 | 12? | ??? - unset in this case |
|
||||||
|
|
||||||
The remaining 78-byte chunk is impenetrable so far, but we should now have the
|
The remaining 78-byte chunk is impenetrable so far, but we should now have the
|
||||||
information we need to display all the animated sequences in `WarHammer.ani`!
|
information we need to display all the animated sequences in `WarHammer.ani`!
|
||||||
|
|
||||||
|
How do we know it needs to be 78 bytes? One option is multiplying the final
|
||||||
|
field of the type 2 record by 6. Maybe we have 6 bytes of description per frame,
|
||||||
|
or maybe it's unrelated to frames?
|
||||||
|
@@ -1,39 +1,58 @@
|
|||||||
// package idx parses the Idx/WarHammer.idx file.
|
// package idx parses the Idx/WarHammer.idx file. It groups the sprites in
|
||||||
//
|
// Anim/WarHammer.ani into playable animations.
|
||||||
// No, I don't know what it's for yet.
|
|
||||||
package idx
|
package idx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
//"fmt"
|
"fmt"
|
||||||
"log"
|
//"log"
|
||||||
"os"
|
"os"
|
||||||
//"strings"
|
//"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Type1Record struct {
|
const (
|
||||||
Offset1 uint32 // Where the type2 for this type1 is to be found
|
NumGroups = 512 // Experimentally determined
|
||||||
Unknown1 uint32 // ???
|
)
|
||||||
Offset2 uint32 // Another offset? But what to?
|
|
||||||
}
|
|
||||||
|
|
||||||
type Type2Record struct {
|
|
||||||
Unknown1 [4]byte // ???
|
|
||||||
Offset uint32 // Where the type3 for this type2 is to be found
|
|
||||||
Unknown2 [4]byte // ??
|
|
||||||
}
|
|
||||||
|
|
||||||
type Type3Record struct {
|
|
||||||
First20 [20]byte
|
|
||||||
Last78 [78]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type Idx struct {
|
type Idx struct {
|
||||||
Filename string
|
Filename string
|
||||||
|
Groups []Group
|
||||||
|
}
|
||||||
|
|
||||||
Type1Records [512]Type1Record // Experimentally, there seem to be 512 of these
|
type Group struct {
|
||||||
Type2Records []Type2Record
|
Spec Spec
|
||||||
Type3Records []Type3Record
|
Records []Record
|
||||||
|
Details []Detail // Records and details are correlated by index
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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?
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load(filename string) (*Idx, error) {
|
func Load(filename string) (*Idx, error) {
|
||||||
@@ -44,29 +63,46 @@ func Load(filename string) (*Idx, error) {
|
|||||||
|
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
out := &Idx{Filename: filename}
|
out := &Idx{
|
||||||
|
Filename: filename,
|
||||||
if err := binary.Read(f, binary.LittleEndian, &out.Type1Records); err != nil {
|
Groups: make([]Group, NumGroups),
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, rec := range out.Type1Records {
|
var specs [NumGroups]Spec
|
||||||
var off1diff uint32
|
|
||||||
var off2diff uint32
|
|
||||||
|
|
||||||
if i > 0 && rec.Offset1 > 0 {
|
if err := binary.Read(f, binary.LittleEndian, &specs); err != nil {
|
||||||
lastRec := out.Type1Records[i-1]
|
return nil, fmt.Errorf("reading specs: %v", err)
|
||||||
off1diff = rec.Offset1 - lastRec.Offset1
|
}
|
||||||
off2diff = rec.Offset2 - lastRec.Offset2
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf(
|
// We can read all records at once
|
||||||
"%.3d: 0x%.6x diff=%.6d %.4d 0x%.6x diff=%.6d",
|
if err := binary.Read(f, binary.LittleEndian, &group.Records); err != nil {
|
||||||
i, rec.Offset1, off1diff, rec.Unknown1, rec.Offset2, off2diff,
|
return nil, fmt.Errorf("spec %v: reading records: %v", i, err)
|
||||||
// i,
|
}
|
||||||
// rec.Unknown1[0], rec.Unknown1[1], rec.Unknown1[2], rec.Unknown1[3],
|
|
||||||
// rec.Unknown1[4], rec.Unknown1[5], rec.Unknown1[6], rec.Unknown1[7],
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Groups[i] = *group
|
||||||
}
|
}
|
||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
|
Reference in New Issue
Block a user