Files
panel/pkg/regionmedic/localize.go
T
2026-07-15 01:06:16 -07:00

104 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package regionmedic
import (
"fmt"
"strconv"
"strings"
)
// floorMod returns the mathematical (floored) modulo, always in [0, m).
func floorMod(a, m int) int { return ((a % m) + m) % m }
// floorDiv returns the floored integer division (rounds toward -inf).
func floorDiv(a, m int) int {
q := a / m
if (a%m != 0) && ((a < 0) != (m < 0)) {
q--
}
return q
}
// RegionCoord identifies a region file r.<X>.<Z>.7rg in the 32×32-chunk grid.
type RegionCoord struct {
X int `json:"x"`
Z int `json:"z"`
}
// FileName returns the on-disk region filename, e.g. "r.-2.0.7rg".
func (rc RegionCoord) FileName() string { return fmt.Sprintf("r.%d.%d.7rg", rc.X, rc.Z) }
// String returns the canonical region id, e.g. "r.-2.0".
func (rc RegionCoord) String() string { return fmt.Sprintf("r.%d.%d", rc.X, rc.Z) }
// RegionForChunk maps a global chunk coordinate to its region (floored /32).
func RegionForChunk(cx, cz int) RegionCoord {
return RegionCoord{X: floorDiv(cx, RegionSide), Z: floorDiv(cz, RegionSide)}
}
// ParseRegionFileName parses "r.<X>.<Z>.7rg" -> RegionCoord. ok=false otherwise.
// Handles negative coordinates, e.g. "r.-2.0.7rg" -> {X:-2, Z:0}.
func ParseRegionFileName(name string) (RegionCoord, bool) {
if !strings.HasPrefix(name, "r.") || !strings.HasSuffix(name, ".7rg") {
return RegionCoord{}, false
}
mid := strings.TrimSuffix(strings.TrimPrefix(name, "r."), ".7rg")
// mid is "<X>.<Z>" where X and Z may be negative. Split on the dot that
// separates them: there is exactly one '.' between two integers.
x, z, ok := splitTwoInts(mid)
if !ok {
return RegionCoord{}, false
}
return RegionCoord{X: x, Z: z}, true
}
// ParseErrorBackup parses 7DTD chunk-salvage filenames written on a failed load:
//
// error_backup_<cx>_<cz>.comp.bak
// error_backup_<cx>_<cz>.uncomp.bak
//
// returning the global chunk coordinates. ok=false if name is not such a file.
func ParseErrorBackup(name string) (cx, cz int, ok bool) {
const pfx = "error_backup_"
if !strings.HasPrefix(name, pfx) {
return 0, 0, false
}
rest := name[len(pfx):]
// Strip the known suffixes.
switch {
case strings.HasSuffix(rest, ".comp.bak"):
rest = strings.TrimSuffix(rest, ".comp.bak")
case strings.HasSuffix(rest, ".uncomp.bak"):
rest = strings.TrimSuffix(rest, ".uncomp.bak")
default:
return 0, 0, false
}
// rest is "<cx>_<cz>" with possibly-negative ints separated by '_'.
i := strings.LastIndex(rest, "_")
if i <= 0 || i >= len(rest)-1 {
return 0, 0, false
}
a, err1 := strconv.Atoi(rest[:i])
b, err2 := strconv.Atoi(rest[i+1:])
if err1 != nil || err2 != nil {
return 0, 0, false
}
return a, b, true
}
// splitTwoInts splits "<a>.<b>" into two ints where each may be negative.
// The separator is the first '.' that is not a leading minus sign position.
func splitTwoInts(s string) (int, int, bool) {
// Find a '.' that splits into two parseable ints. Scan candidate dots.
for i := 1; i < len(s); i++ {
if s[i] != '.' {
continue
}
a, err1 := strconv.Atoi(s[:i])
b, err2 := strconv.Atoi(s[i+1:])
if err1 == nil && err2 == nil {
return a, b, true
}
}
return 0, 0, false
}