Initial parse of Sets/*.set
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"ur.gs/chaos-gate/internal/data"
|
"ur.gs/chaos-gate/internal/data"
|
||||||
"ur.gs/chaos-gate/internal/maps"
|
"ur.gs/chaos-gate/internal/maps"
|
||||||
|
"ur.gs/chaos-gate/internal/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -22,6 +23,7 @@ func main() {
|
|||||||
|
|
||||||
loadMapsFrom("Maps")
|
loadMapsFrom("Maps")
|
||||||
loadMapsFrom("MultiMaps")
|
loadMapsFrom("MultiMaps")
|
||||||
|
loadSets()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadData() {
|
func loadData() {
|
||||||
@@ -109,3 +111,17 @@ func loadMapsFrom(part string) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadSets() {
|
||||||
|
setsPath := filepath.Join(*gamePath, "Sets")
|
||||||
|
log.Printf("Loading sets from %s", setsPath)
|
||||||
|
|
||||||
|
mapSets, err := sets.LoadSets(setsPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse %s/*.set as map sets: %v", setsPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, mapSet := range mapSets {
|
||||||
|
fmt.Printf(" * `%s`: Defs=%#v len(palette)=%d\n", key, mapSet.Defs, len(mapSet.Palette))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -89,7 +89,7 @@ remake.
|
|||||||
* `Save_G/`
|
* `Save_G/`
|
||||||
* `*.sav` # savedata, gzip-compressed, custom format
|
* `*.sav` # savedata, gzip-compressed, custom format
|
||||||
* `*.txt` # Seems to be a copy of one of Maps/*.txt
|
* `*.txt` # Seems to be a copy of one of Maps/*.txt
|
||||||
* `Sets/`
|
* [`Sets/`](sets.md)
|
||||||
* `Data.chk` # checksums? Mentions all the .set files
|
* `Data.chk` # checksums? Mentions all the .set files
|
||||||
* `*.set` # plain text, related to maps. Editor has a concept of map sets, which these must be
|
* `*.set` # plain text, related to maps. Editor has a concept of map sets, which these must be
|
||||||
* [✓] `SMK/`
|
* [✓] `SMK/`
|
||||||
|
77
doc/formats/sets.md
Normal file
77
doc/formats/sets.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Set format information
|
||||||
|
|
||||||
|
`*.set` files seem to act as a palette of objects for a [`.map`](maps.md) file.
|
||||||
|
The map file loads a set, and can then reference objects by a small number.
|
||||||
|
|
||||||
|
Since a maximally-filled map file seems to be able to reference 91,000 * 4
|
||||||
|
objects, this is a necessary optimization for 1998-era hardware.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
These files are plain-text.
|
||||||
|
|
||||||
|
We handily have a `template.set`, which looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
set template
|
||||||
|
|
||||||
|
Defs
|
||||||
|
|
||||||
|
40 40 40 80
|
||||||
|
|
||||||
|
|
||||||
|
dirt
|
||||||
|
h_dirt
|
||||||
|
rocks
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank #
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
blank
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
The files are of varying lengths. `template.set` is 220 lines, `map10.set` only
|
||||||
|
83.
|
||||||
|
|
||||||
|
So it's a line-based format that goes:
|
||||||
|
|
||||||
|
* Set description
|
||||||
|
* Blank line (optional, missing in `GEN_JUN.set`, `GEN_WAS.set`, others)
|
||||||
|
* `Defs`
|
||||||
|
* Blank line
|
||||||
|
* 4 space-separated numbers, variable between sets
|
||||||
|
* At least one blank line, sometimes 2
|
||||||
|
* A list of object names, sometimes with # comments on the right hand side
|
||||||
|
* They all seem to end with a comment of some sort, e.g. `# meaningless comment`
|
||||||
|
|
||||||
|
Questions:
|
||||||
|
|
||||||
|
What are the `Defs` for? Is it `Defaults` or `Definitions`? The values are
|
||||||
|
quite variable between files.
|
||||||
|
|
||||||
|
Is whitespace significant in the list of objects? First assumption is no.
|
||||||
|
|
||||||
|
Is it a simple 0-indexed palette or do maps embed an absolute line number?
|
||||||
|
|
||||||
|
Do positions in the palette have special meaning? e.g. is a particular range
|
||||||
|
always reserved for walls?
|
||||||
|
|
||||||
|
Are there any special values that don't appear as files in the `Obj/` directory?
|
||||||
|
`blank.obj` exists, so I expect not.
|
||||||
|
|
||||||
|
Once the map format is fleshed out a little more, can investigate by creating a
|
||||||
|
map with a single object from the set in it and seeing what line that works out
|
||||||
|
to be.
|
||||||
|
|
120
internal/sets/sets.go
Normal file
120
internal/sets/sets.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package sets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"ur.gs/chaos-gate/internal/util/asciiscan"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MapSet struct {
|
||||||
|
Description string
|
||||||
|
Defs [4]byte // TODO: work out what these are for
|
||||||
|
|
||||||
|
// TODO: is there any more structure than this? Should I preserve empty lines?
|
||||||
|
Palette []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadSets(dir string) (map[string]MapSet, error) {
|
||||||
|
fis, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]MapSet, len(fis))
|
||||||
|
|
||||||
|
for _, fi := range fis {
|
||||||
|
filename := filepath.Join(dir, fi.Name())
|
||||||
|
basename := filepath.Base(filename)
|
||||||
|
extname := filepath.Ext(filename)
|
||||||
|
|
||||||
|
// Don't try to load non-.obj files
|
||||||
|
if !strings.EqualFold(extname, ".set") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := LoadSet(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: %v", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out[basename] = obj
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadSet(filename string) (MapSet, error) {
|
||||||
|
var out MapSet
|
||||||
|
var err error
|
||||||
|
|
||||||
|
s, err := asciiscan.New(filename)
|
||||||
|
if err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
out.Description, err = s.ConsumeString()
|
||||||
|
if err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Defs, err = consumeDefs(s)
|
||||||
|
if err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
str, err := s.ConsumeString()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Palette = append(out.Palette, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func consumeDefs(scanner *asciiscan.Scanner) ([4]byte, error) {
|
||||||
|
var out [4]byte
|
||||||
|
|
||||||
|
expectDefs, err := scanner.ConsumeString()
|
||||||
|
if err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if expectDefs != "Defs" {
|
||||||
|
return out, fmt.Errorf("Couldn't find Defs section")
|
||||||
|
}
|
||||||
|
|
||||||
|
defs, err := scanner.ConsumeString()
|
||||||
|
if err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(defs, " ", -1)
|
||||||
|
if len(parts) != 4 {
|
||||||
|
return out, fmt.Errorf("Defs section did not have 4 components")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, part := range parts {
|
||||||
|
n, err := strconv.ParseInt(part, 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out[i] = byte(n) // safe as we specify 8 bits to ParseInt
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
Reference in New Issue
Block a user