schematic.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. // Copyright 2023 prestidigitator (as registered on forum.minetest.net)
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package schematic
  15. import (
  16. "bytes"
  17. "compress/zlib"
  18. "encoding/binary"
  19. "encoding/json"
  20. "fmt"
  21. "io"
  22. "strings"
  23. "text/template"
  24. "gopkg.in/yaml.v3"
  25. )
  26. var yamlTemplate = template.Must(template.New("yaml").Funcs(map[string]any{
  27. "yProbVal": func(prob uint8) uint8 { return (prob & 0x7f) << 1 },
  28. "param1Prob": func(param1 uint8) uint8 { return (param1 & 0x7f) << 1 },
  29. "param1Force": func(param1 uint8) uint8 { return param1 & 0x80 },
  30. "strEsc": func(s string) (string, error) {
  31. bs, err := yaml.Marshal(s); return strings.TrimSpace(string(bs)), err
  32. },
  33. }).Parse(strings.TrimSpace(`
  34. size: {x: {{ .Size.X }}, y: {{ .Size.Y }}, z: {{ .Size.Z -}} }
  35. {{- "\n" -}}
  36. yslice_prob:
  37. {{- range $y, $p := .YProbs -}}
  38. {{- "\n" -}}
  39. - {ypos: {{ $y }}, prob: {{ yProbVal $p -}} }
  40. {{- end -}}
  41. {{- "\n" -}}
  42. data:
  43. {{- range $_, $node := .Nodes -}}
  44. {{- $n := index $.Names $node.Content | strEsc -}}
  45. {{- $p := param1Prob $node.Param1 -}}
  46. {{- $f := param1Force $node.Param1 -}}
  47. {{- $p2 := $node.Param2 -}}
  48. {{- "\n" -}}
  49. - {name: {{ $n }}, prob: {{ $p }}, param2: {{ $p2 }}
  50. {{- if $f -}} , force_place: true {{- end -}}
  51. }
  52. {{- end -}}
  53. {{- "\n" -}}
  54. `)))
  55. var jsonTemplate = template.Must(template.New("json").Funcs(map[string]any{
  56. "yProbVal": func(prob uint8) uint8 { return (prob & 0x7f) << 1 },
  57. "param1Prob": func(param1 uint8) uint8 { return (param1 & 0x7f) << 1 },
  58. "param1Force": func(param1 uint8) uint8 { return param1 & 0x80 },
  59. "strEsc": func(s string) (string, error) {
  60. bs, err := json.Marshal(s); return string(bs), err
  61. },
  62. }).Parse(strings.TrimSpace(`
  63. {{- $s := .schematic -}}
  64. {{- $indent := .indent -}}
  65. {{- $i0 := "" -}}
  66. {{- $i1oc := "" -}}
  67. {{- $i1 := " " -}}
  68. {{- $i2oc := "" -}}
  69. {{- $i2 := " " -}}
  70. {{- if $indent -}}
  71. {{- $i0 = "\n" -}}
  72. {{- $i1oc = printf "\n%s" $indent -}}
  73. {{- $i1 = printf "\n%s" $indent -}}
  74. {{- $i2oc = printf "\n%s%s" $indent $indent -}}
  75. {{- $i2 = printf "\n%s%s" $indent $indent -}}
  76. {{- end -}}
  77. {
  78. {{- $i1oc -}}
  79. "size": {"x": {{ $s.Size.X }}, "y": {{ $s.Size.Y }}, "z": {{ $s.Size.Z -}} },
  80. {{- $i1 -}}
  81. "yslice_prob": [
  82. {{- range $y, $p := $s.YProbs -}}
  83. {{- if gt $y 0 -}} ,{{- $i2 -}} {{- else -}}{{- $i2oc -}}{{- end -}}
  84. {"ypos": {{ $y }}, "prob": {{ yProbVal $p -}} }
  85. {{- end -}}
  86. {{- $i1oc -}}
  87. ],
  88. {{- $i1 -}}
  89. "data": [
  90. {{- range $i, $node := $s.Nodes -}}
  91. {{- $n := index $s.Names $node.Content | strEsc -}}
  92. {{- $p := param1Prob $node.Param1 -}}
  93. {{- $f := param1Force $node.Param1 -}}
  94. {{- $p2 := $node.Param2 -}}
  95. {{- if gt $i 0 -}} ,{{- $i2 -}} {{- else -}}{{- $i2oc -}}{{- end -}}
  96. {"name": {{ $n }}, "prob": {{ $p }}, "param2": {{ $p2 }}
  97. {{- if $f -}} , "force_place": true {{- end -}}
  98. }
  99. {{- end -}}
  100. {{- $i1oc -}}
  101. ]
  102. {{- $i0 -}}
  103. }
  104. {{- $i0 -}}
  105. `)))
  106. var luaTemplate = template.Must(template.New("lua").Funcs(map[string]any{
  107. "inc": func(n int) int { return n+1 },
  108. "yProbVal": func(prob uint8) uint8 { return (prob & 0x7f) << 1 },
  109. "param1Prob": func(param1 uint8) uint8 { return (param1 & 0x7f) << 1 },
  110. "param1Force": func(param1 uint8) uint8 { return param1 & 0x80 },
  111. "strEsc": func(s string) (string, error) {
  112. var sb strings.Builder
  113. sb.Grow(len(s)+2)
  114. sb.WriteRune('"')
  115. for i := 0; i < len(s); i++ {
  116. switch b := s[i]; b {
  117. case '\a': sb.WriteString(`\a`)
  118. case '\b': sb.WriteString(`\b`)
  119. case '\f': sb.WriteString(`\f`)
  120. case '\n': sb.WriteString(`\n`)
  121. case '\r': sb.WriteString(`\r`)
  122. case '\t': sb.WriteString(`\t`)
  123. case '\\': sb.WriteString(`\\`)
  124. case '"': sb.WriteString(`\"`)
  125. case '\'': sb.WriteString(`\'`)
  126. case 0: sb.WriteString(`\0`)
  127. default:
  128. if 0x20 <= b && b <= 0x7e {
  129. sb.WriteByte(b)
  130. } else {
  131. fmt.Fprintf(&sb, `\%03d`, b)
  132. }
  133. }
  134. }
  135. sb.WriteRune('"')
  136. return sb.String(), nil
  137. },
  138. }).Parse(strings.TrimSpace(`
  139. {{- $s := .schematic -}}
  140. {{- $indent := .indent -}}
  141. {{- $comments := .comments -}}
  142. schematic = {
  143. {{- printf "\n%s" $indent -}}
  144. size = {x={{ printf "%d" $s.Size.X }}, y={{ printf "%d" $s.Size.Y -}}
  145. , z={{ printf "%d" $s.Size.Z -}} },
  146. {{- printf "\n%s" $indent -}}
  147. yslice_prob = {
  148. {{- range $y, $p := $s.YProbs -}}
  149. {{- printf "\n%s%s" $indent $indent -}}
  150. {ypos={{ printf "%d" $y }}, prob={{ yProbVal $p | printf "%d" -}} },
  151. {{- end -}}
  152. {{- printf "\n%s" $indent -}}
  153. },
  154. {{- printf "\n%s" $indent -}}
  155. data = {
  156. {{- $x := 0 -}}
  157. {{- $y := 0 -}}
  158. {{- $z := 0 -}}
  159. {{- range $i, $node := $s.Nodes}}
  160. {{- $x = inc $x -}}
  161. {{- if ge $x $s.Size.X -}}
  162. {{- $x = 0 -}}
  163. {{- $y = inc $y -}}
  164. {{- if ge $y $s.Size.Y -}}
  165. {{- $y = 0 -}}
  166. {{- $z = inc $z -}}
  167. {{- end -}}
  168. {{- end -}}
  169. {{- $n := index $s.Names $node.Content | strEsc -}}
  170. {{- $p := param1Prob $node.Param1 -}}
  171. {{- $f := param1Force $node.Param1 -}}
  172. {{- $p2 := $node.Param2 -}}
  173. {{- if and $comments (eq $x 0) -}}
  174. {{- if or (gt $y 0) (gt $z 0) -}}
  175. {{- "\n" -}}
  176. {{- end -}}
  177. {{- printf "\n%s%s" $indent $indent -}}
  178. -- z={{ $z }}, y={{ $y }}
  179. {{- end -}}
  180. {{- printf "\n%s%s" $indent $indent -}}
  181. {name={{ $n }}, prob={{ printf "%d" $p }}, param2={{ printf "%d" $p2 }}
  182. {{- if $f -}} , force_place=true {{- end -}}
  183. },
  184. {{- end -}}
  185. {{- printf "\n%s" $indent -}}
  186. }
  187. {{- "\n" -}}
  188. }
  189. {{- "\n" -}}
  190. `)))
  191. type Schematic struct{
  192. Size Vec3i
  193. YProbs []uint8
  194. Names []string
  195. Nodes []Node
  196. }
  197. type Vec3i struct{
  198. X, Y, Z uint16
  199. }
  200. type Node struct{
  201. Content uint16
  202. Param1, Param2 uint8
  203. }
  204. type schematicSer struct{
  205. Size sizeSer `yaml:"size" json:"size"`
  206. YProbs []yprobSer `yaml:"yslice_prob" json:"yslice_prob"`
  207. Data []datumSer `yaml:"data" json:"data"`
  208. }
  209. type sizeSer struct{
  210. X uint16 `yaml:"x" json:"x"`
  211. Y uint16 `yaml:"y" json:"y"`
  212. Z uint16 `yaml:"z" json:"z"`
  213. }
  214. type yprobSer struct{
  215. YPos uint16 `yaml:"ypos" json:"ypos"`
  216. Prob uint8 `yaml:"prob" json:"prob"`
  217. }
  218. type datumSer struct{
  219. Name string `yaml:"name" json:"name"`
  220. Prob uint8 `yaml:"prob" json:"prob"`
  221. Param2 uint8 `yaml:"param2" json:"param2"`
  222. Force bool `yaml:"force_place,omitempty" json:"force_place,omitempty"`
  223. }
  224. func FromMTSStream(ins io.Reader) (s Schematic, err error) {
  225. defer func() {
  226. if pval := recover(); pval != nil && pval != &err { panic(pval) }
  227. }()
  228. buf := make([]byte, 12)
  229. readnb := func(ins io.Reader, n int) {
  230. if len(buf) < n { buf = make([]byte, n) }
  231. _, err = io.ReadFull(ins, buf[:n])
  232. if err != nil { panic(&err) }
  233. }
  234. readnb(ins, 12)
  235. sig := append([]byte{}, buf[:4]...)
  236. ver := binary.BigEndian.Uint16(buf[4:6])
  237. sx := binary.BigEndian.Uint16(buf[6:8])
  238. sy := binary.BigEndian.Uint16(buf[8:10])
  239. sz := binary.BigEndian.Uint16(buf[10:12])
  240. if bytes.Compare(sig, []byte("MTSM")) != 0 { err = InvalidMTSSignature; return }
  241. if ver > 4 { err = UnsupportedMTSVersion; return }
  242. readnb(ins, int(sy))
  243. yprobs := make([]uint8, sy)
  244. if ver >= 3 {
  245. for i, _ := range yprobs { yprobs[i] = buf[i] }
  246. } else {
  247. for i, _ := range yprobs { yprobs[i] = 255 }
  248. }
  249. readnb(ins, 2)
  250. nnames := binary.BigEndian.Uint16(buf[:2])
  251. names, ignoreIndex := make([]string, nnames), -1
  252. for i, _ := range names {
  253. readnb(ins, 2)
  254. nameLen := int(binary.BigEndian.Uint16(buf[:2]))
  255. readnb(ins, nameLen)
  256. name := string(buf[:nameLen])
  257. if name == "ignore" { name, ignoreIndex = "air", i }
  258. names[i] = name
  259. }
  260. vol := sx * sy * sz
  261. zin, err := zlib.NewReader(ins)
  262. if err != nil { return }
  263. defer zin.Close()
  264. readnb(zin, 2*int(vol))
  265. content := make([]uint16, vol)
  266. for i, _ := range content {
  267. content[i] = binary.BigEndian.Uint16(buf[2*i : 2*i+2])
  268. }
  269. readnb(zin, int(vol))
  270. param1s := make([]uint8, vol)
  271. for i, _ := range param1s { param1s[i] = buf[i] }
  272. readnb(zin, int(vol))
  273. param2s := make([]uint8, vol)
  274. for i, _ := range param2s { param2s[i] = buf[i] }
  275. if ver < 2 {
  276. for i, _ := range param1s {
  277. if ignoreIndex >= 0 && int(content[i]) == ignoreIndex {
  278. param1s[i] = 0
  279. } else if param1s[i] == 0 {
  280. param1s[i] = 255
  281. }
  282. }
  283. }
  284. if ver < 4 {
  285. for i, _ := range yprobs { yprobs[i] >>= 1 }
  286. for i, _ := range param1s { param1s[i] >>= 1 }
  287. }
  288. nodes := make([]Node, vol)
  289. for i, _ := range nodes {
  290. nodes[i] = Node{
  291. Content: content[i],
  292. Param1: param1s[i],
  293. Param2: param2s[i],
  294. }
  295. }
  296. s = Schematic{
  297. Size: Vec3i{X: sx, Y: sy, Z: sz},
  298. YProbs: yprobs,
  299. Names: names,
  300. Nodes: nodes,
  301. }
  302. return
  303. }
  304. func FromYAMLStream(ins io.Reader) (Schematic, error) {
  305. var ss schematicSer
  306. err := yaml.NewDecoder(ins).Decode(&ss)
  307. if err != nil { return Schematic{}, err }
  308. return fromSer(ss)
  309. }
  310. func FromJSONStream(ins io.Reader) (Schematic, error) {
  311. var ss schematicSer
  312. err := json.NewDecoder(ins).Decode(&ss)
  313. if err != nil { return Schematic{}, err }
  314. return fromSer(ss)
  315. }
  316. func fromSer(ss schematicSer) (s Schematic, err error) {
  317. sx, sy, sz := ss.Size.X, ss.Size.Y, ss.Size.Z
  318. s.Size.X, s.Size.Y, s.Size.Z = sx, sy, sz
  319. yprobs := make([]uint8, sy)
  320. for _, yp := range ss.YProbs {
  321. y, p := yp.YPos, yp.Prob
  322. if y >= sy {
  323. err = fmt.Errorf("y-slice y index %d out-of-range [0, %d)", y, sy)
  324. return
  325. }
  326. yprobs[y] = p >> 1
  327. }
  328. s.YProbs = yprobs
  329. names, nameMap := []string{}, map[string]uint16{}
  330. for _, d := range ss.Data {
  331. name := d.Name
  332. if _, ok := nameMap[name]; !ok {
  333. nameMap[name] = uint16(len(names))
  334. names = append(names, name)
  335. }
  336. }
  337. s.Names = names
  338. nodes := make([]Node, len(ss.Data))
  339. for i, d := range ss.Data {
  340. param1 := d.Prob >> 1
  341. if d.Force { param1 |= 0x80 }
  342. nodes[i] = Node{
  343. Content: nameMap[d.Name],
  344. Param1: param1,
  345. Param2: d.Param2,
  346. }
  347. }
  348. s.Nodes = nodes
  349. return
  350. }
  351. func (s Schematic) WriteMTS(outs io.Writer) (err error) {
  352. var header [12]byte
  353. var uint16Buf [2]byte
  354. copy(header[:6], []byte{'M', 'T', 'S', 'M', 0, 4})
  355. binary.BigEndian.PutUint16(header[6:8], s.Size.X)
  356. binary.BigEndian.PutUint16(header[8:10], s.Size.Y)
  357. binary.BigEndian.PutUint16(header[10:12], s.Size.Z)
  358. _, err = outs.Write(header[:])
  359. if err != nil { return err }
  360. for _, p := range s.YProbs {
  361. _, err = outs.Write([]byte{p})
  362. if err != nil { return err }
  363. }
  364. binary.BigEndian.PutUint16(uint16Buf[:2], uint16(len(s.Names)))
  365. _, err = outs.Write(uint16Buf[:])
  366. if err != nil { return err }
  367. for _, name := range s.Names {
  368. binary.BigEndian.PutUint16(uint16Buf[:2], uint16(len(name)))
  369. _, err = outs.Write(uint16Buf[:])
  370. if err != nil { return err }
  371. _, err = outs.Write([]byte(name))
  372. if err != nil { return err }
  373. }
  374. zout := zlib.NewWriter(outs)
  375. defer func() {
  376. if e := zout.Close(); e != nil && err == nil { err = e }
  377. }()
  378. for _, node := range s.Nodes {
  379. binary.BigEndian.PutUint16(uint16Buf[:2], uint16(node.Content))
  380. _, err = zout.Write(uint16Buf[:])
  381. if err != nil { return err }
  382. }
  383. for _, node := range s.Nodes {
  384. _, err = zout.Write([]byte{byte(node.Param1)})
  385. if err != nil { return err }
  386. }
  387. for _, node := range s.Nodes {
  388. _, err = zout.Write([]byte{byte(node.Param2)})
  389. if err != nil { return err }
  390. }
  391. return
  392. }
  393. func (s Schematic) WriteYAML(outs io.Writer, indent string) error {
  394. return yamlTemplate.Execute(outs, s)
  395. }
  396. func (s Schematic) WriteJSON(outs io.Writer, indent string) error {
  397. return jsonTemplate.Execute(outs, map[string]any{"indent": indent, "schematic": s})
  398. }
  399. func (s Schematic) WriteLua(outs io.Writer, indent string, comments bool) (
  400. err error,
  401. ) {
  402. return luaTemplate.Execute(
  403. outs, map[string]any{"schematic": s, "indent": indent, "comments": comments},
  404. )
  405. }