Start investigating the maps
This commit is contained in:
@@ -2,10 +2,12 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"ur.gs/chaos-gate/internal/data"
|
"ur.gs/chaos-gate/internal/data"
|
||||||
|
"ur.gs/chaos-gate/internal/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -15,13 +17,19 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
loadData()
|
||||||
|
loadObj()
|
||||||
|
|
||||||
|
loadMapsFrom("Maps")
|
||||||
|
loadMapsFrom("MultiMaps")
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadData() {
|
||||||
dataPath := filepath.Join(*gamePath, "Data")
|
dataPath := filepath.Join(*gamePath, "Data")
|
||||||
accountingPath := filepath.Join(dataPath, "Accounting.dat")
|
accountingPath := filepath.Join(dataPath, "Accounting.dat")
|
||||||
genericDataPath := filepath.Join(dataPath, "GenericData.dat")
|
genericDataPath := filepath.Join(dataPath, "GenericData.dat")
|
||||||
aniObDefPath := filepath.Join(dataPath, "AniObDef.dat")
|
aniObDefPath := filepath.Join(dataPath, "AniObDef.dat")
|
||||||
|
|
||||||
objDataPath := filepath.Join(*gamePath, "Obj")
|
|
||||||
|
|
||||||
log.Printf("Loading %s...", accountingPath)
|
log.Printf("Loading %s...", accountingPath)
|
||||||
accounting, err := data.LoadAccounting(accountingPath)
|
accounting, err := data.LoadAccounting(accountingPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,6 +53,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("%s: %+v", genericDataPath, genericData)
|
log.Printf("%s: %+v", genericDataPath, genericData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadObj() {
|
||||||
|
objDataPath := filepath.Join(*gamePath, "Obj")
|
||||||
|
|
||||||
// TODO: Obj/cpiece.rec isn't loaded by this. Do we need it? How do we know?
|
// TODO: Obj/cpiece.rec isn't loaded by this. Do we need it? How do we know?
|
||||||
log.Printf("Loading %s...", objDataPath)
|
log.Printf("Loading %s...", objDataPath)
|
||||||
@@ -53,11 +65,47 @@ func main() {
|
|||||||
log.Fatalf("Failed to parse %s: %s", objDataPath, err)
|
log.Fatalf("Failed to parse %s: %s", objDataPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inspect := "c_webs"
|
||||||
|
inspect_obj := inspect + ".obj"
|
||||||
|
|
||||||
log.Printf("Objects in %s:", objDataPath)
|
log.Printf("Objects in %s:", objDataPath)
|
||||||
for key, obj := range objects {
|
for key, obj := range objects {
|
||||||
log.Printf("\t%s: %+v", key, obj)
|
if key != inspect_obj {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf(
|
||||||
|
"\t%s\t%d frames\t%d bytes",
|
||||||
|
key, obj.ObjectHeader.NumFrames, obj.ObjectHeader.FrameDataSize,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Tzeentch: %+v", objects["TZEENTCH.OBJ"])
|
log.Printf("%s: %#v", inspect, objects[inspect_obj])
|
||||||
log.Printf("TFrame 0: %+v", objects["TZEENTCH.OBJ"].Frames[0])
|
|
||||||
|
for i, frame := range objects[inspect_obj].Frames {
|
||||||
|
numPixels := frame.Width * frame.Height
|
||||||
|
log.Printf("frame %d: w=%d h=%d sz=%d w*h=%d bpp=%v", i, frame.Width, frame.Height, frame.PixelSize, numPixels, float64(frame.PixelSize)/float64(numPixels))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadMapsFrom(part string) {
|
||||||
|
mapsPath := filepath.Join(*gamePath, part)
|
||||||
|
log.Printf("Loading maps from %s", mapsPath)
|
||||||
|
|
||||||
|
gameMaps, err := maps.LoadGameMaps(mapsPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse %s/*.{MAP,txt} as game maps: %v", mapsPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Maps in %s:", mapsPath)
|
||||||
|
for key, gameMap := range gameMaps {
|
||||||
|
hdr := gameMap.Header
|
||||||
|
fmt.Printf(
|
||||||
|
" * `%s`: IsCampaignMap=%v W=%v:%v L=%v:%v SetName=%s\n",
|
||||||
|
key,
|
||||||
|
hdr.IsCampaignMap,
|
||||||
|
hdr.MinWidth, hdr.MaxWidth,
|
||||||
|
hdr.MinLength, hdr.MaxLength,
|
||||||
|
string(hdr.SetName[:]),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
150
internal/maps/maps.go
Normal file
150
internal/maps/maps.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package maps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
expectedMagic = []byte("\x08\x00WHMAP\x00")
|
||||||
|
expectedSetNameOffset = uint32(0x34)
|
||||||
|
notImplemented = fmt.Errorf("Not implemented")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Header struct {
|
||||||
|
IsCampaignMap uint32 // Tentatively: 0 = no, 1 = yes
|
||||||
|
MinWidth uint32
|
||||||
|
MinLength uint32
|
||||||
|
MaxWidth uint32
|
||||||
|
MaxLength uint32
|
||||||
|
Unknown1 uint32
|
||||||
|
Unknown2 uint32
|
||||||
|
Unknown3 uint32
|
||||||
|
Unknown4 uint32
|
||||||
|
Magic [8]byte // "\x08\x00WHMAP\x00"
|
||||||
|
Unknown5 uint32
|
||||||
|
Unknown6 uint32
|
||||||
|
SetName [8]byte // Or is it a null-terminated string?
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Header) Check() []error {
|
||||||
|
var out []error
|
||||||
|
if h.IsCampaignMap > 1 {
|
||||||
|
out = append(out, fmt.Errorf("Expected 0 or 1 for IsCampaignMap, got %v", h.IsCampaignMap))
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(expectedMagic, h.Magic[:]) != 0 {
|
||||||
|
out = append(out, fmt.Errorf("Unexpected magic value: %v", h.Magic))
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameMap struct {
|
||||||
|
Header
|
||||||
|
|
||||||
|
// TODO: parse this into sections
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A game map contains a .txt and a .map. If they're in the same directory,
|
||||||
|
// just pass the directory + basename to load both
|
||||||
|
func LoadGameMap(prefix string) (*GameMap, error) {
|
||||||
|
for _, mapExt := range []string{".MAP", ".map"} {
|
||||||
|
for _, txtExt := range []string{".TXT", ".txt"} {
|
||||||
|
out, err := LoadGameMapByFiles(prefix+mapExt, prefix+txtExt)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Couldn't find %s.{map,txt}, even ignoring case", prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A game map is composed of two files: .map and .txt
|
||||||
|
func LoadGameMapByFiles(mapFile, txtFile string) (*GameMap, error) {
|
||||||
|
out, err := loadMapFile(mapFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: load text and parse into sections
|
||||||
|
txt, err := ioutil.ReadFile(txtFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out.Text = string(txt)
|
||||||
|
|
||||||
|
for _, err := range out.Check() {
|
||||||
|
log.Printf("%s: %v", mapFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadGameMaps(dir string) (map[string]*GameMap, error) {
|
||||||
|
fis, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]*GameMap)
|
||||||
|
|
||||||
|
for _, fi := range fis {
|
||||||
|
filename := filepath.Join(dir, fi.Name())
|
||||||
|
basename := filepath.Base(filename)
|
||||||
|
extname := filepath.Ext(filename)
|
||||||
|
|
||||||
|
// Only pay attention to .MAP files. Assume they will have a .txt too
|
||||||
|
if !strings.EqualFold(extname, ".MAP") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := filename[0 : len(filename)-4]
|
||||||
|
|
||||||
|
gameMap, err := LoadGameMap(prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error processing %v: %s", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out[basename] = gameMap
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadMapFile(filename string) (*GameMap, error) {
|
||||||
|
var out GameMap
|
||||||
|
|
||||||
|
mf, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer mf.Close()
|
||||||
|
|
||||||
|
zr, err := gzip.NewReader(mf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer zr.Close()
|
||||||
|
|
||||||
|
if err := binary.Read(zr, binary.LittleEndian, &out.Header); err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing %s: %v", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &out, nil
|
||||||
|
}
|
Reference in New Issue
Block a user