Compare commits

..

3 Commits

Author SHA1 Message Date
zomo 4619fef9a3 FEAT: room credits 2026-04-15 20:45:21 -05:00
zomo 37f9bc215f FIX: use string builder 2026-04-14 23:22:59 -05:00
zomo 0aee2e904f FEAT: ini parser will now search all sections for all keys 2026-04-14 21:44:46 -05:00
3 changed files with 206 additions and 80 deletions
+120 -56
View File
@@ -2,8 +2,8 @@ package main
import ( import (
"fmt" "fmt"
"log"
"strconv" "strconv"
"strings"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
) )
@@ -12,6 +12,10 @@ type RoomIni struct {
night int night int
room int room int
name string
credits string
links []string
size float64 size float64
speed float64 speed float64
fov float64 fov float64
@@ -28,8 +32,7 @@ func (r *RoomIni) ToIni() string {
return strconv.FormatFloat(n, 'f', -1, 64) return strconv.FormatFloat(n, 'f', -1, 64)
} }
return fmt.Sprintf(` return fmt.Sprintf(`[NIGHT_%d_ROOM_%d]
[NIGHT_%d_ROOM_%d]
size=%s size=%s
speed=%s speed=%s
FOV=%s FOV=%s
@@ -49,6 +52,29 @@ Mini_speed=%s
) )
} }
func (r *RoomIni) ToCredits() string {
name := r.name
if name == "" {
name = "unnamed"
}
credits := r.credits
if credits == "" {
credits = "no name"
}
links := strings.Join(r.links, " | ")
if links == "" {
links = "no links"
}
return fmt.Sprintf(`Night %d - Room %d: %s
By: %s
Links: %s
`,
r.night, r.room, name,
credits, links,
)
}
func NewRoomIni() RoomIni { func NewRoomIni() RoomIni {
return RoomIni{ return RoomIni{
night: -1, night: -1,
@@ -72,29 +98,7 @@ func ReadIni(path string) (RoomIni, error) {
return RoomIni{}, err return RoomIni{}, err
} }
meta, err := cfg.GetSection("DEFAULT") roomIni, err := loadIni(cfg)
if err != nil {
return RoomIni{}, fmt.Errorf("unable to load DEFAULT section in %s: %+v", path, err)
}
sections := cfg.Sections()
var room *ini.Section = nil
for _, sec := range sections {
if room != nil {
log.Printf("WARN: extra section in %s, section: %s", path, sec.Name())
continue
}
if sec.Name() != "default" {
room = sec
}
}
if room == nil {
room = meta
}
roomIni, err := loadIni(meta, room)
if err != nil { if err != nil {
return RoomIni{}, fmt.Errorf("ERROR: error loading ini from %s: %+v", path, err) return RoomIni{}, fmt.Errorf("ERROR: error loading ini from %s: %+v", path, err)
} }
@@ -102,103 +106,163 @@ func ReadIni(path string) (RoomIni, error) {
return roomIni, nil return roomIni, nil
} }
func loadIni(meta *ini.Section, room *ini.Section) (RoomIni, error) { func loadIni(cfg *ini.File) (RoomIni, error) {
roomini := RoomIni{} roomini := RoomIni{}
if val, err := loadInt(meta, "night", -1); err != nil { if err := loadIniMeta(&roomini, cfg); err != nil {
return RoomIni{}, err return RoomIni{}, err
}
if err := loadIniCredits(&roomini, cfg); err != nil {
return RoomIni{}, err
}
if err := loadIniRoom(&roomini, cfg); err != nil {
return RoomIni{}, err
}
return roomini, nil
}
func loadIniMeta(roomini *RoomIni, cfg *ini.File) error {
if val, err := loadInt(cfg, "night", -1); err != nil {
return err
} else { } else {
roomini.night = val roomini.night = val
} }
if val, err := loadInt(meta, "room", -1); err != nil { if val, err := loadInt(cfg, "room", -1); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.room = val roomini.room = val
} }
if roomini.night > 0 && roomini.room < 1 { if roomini.night > 0 && roomini.room < 1 {
return RoomIni{}, fmt.Errorf("night is set but room is not set") return fmt.Errorf("night is set but room is not set")
} }
if val, err := loadFloat(room, "size", 1); err != nil { return nil
return RoomIni{}, err }
func loadIniCredits(roomini *RoomIni, cfg *ini.File) error {
roomini.name = loadString(cfg, "name", "")
roomini.credits = loadString(cfg, "credits", "")
linksstring := loadString(cfg, "links", "")
roomini.links = strings.Split(linksstring, " ")
return nil
}
func loadIniRoom(roomini *RoomIni, cfg *ini.File) error {
if val, err := loadFloat(cfg, "size", 1); err != nil {
return err
} else { } else {
roomini.size = val roomini.size = val
} }
if val, err := loadFloat(room, "speed", 1); err != nil { if val, err := loadFloat(cfg, "speed", 1); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.speed = val roomini.speed = val
} }
if val, err := loadFloat(room, "fov", 55); err != nil { if val, err := loadFloat(cfg, "fov", 55); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.fov = val roomini.fov = val
} }
if val, err := loadFloat(room, "texture_repeat", 3); err != nil { if val, err := loadFloat(cfg, "texture_repeat", 3); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.textureRepeat = val roomini.textureRepeat = val
} }
if val, err := loadFloat(room, "room_height", 350); err != nil { if val, err := loadFloat(cfg, "room_height", 350); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.roomHeight = val roomini.roomHeight = val
} }
if val, err := loadInt(room, "layer_frames", 1); err != nil { if val, err := loadInt(cfg, "layer_frames", 1); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.layerFrames = val roomini.layerFrames = val
} }
if val, err := loadInt(room, "fog_color", 0); err != nil { if val, err := loadInt(cfg, "fog_color", 0); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.fogColor = val roomini.fogColor = val
} }
if val, err := loadFloat(room, "fog_end", -1); err != nil { if val, err := loadFloat(cfg, "fog_end", -1); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.fogEnd = val roomini.fogEnd = val
} }
if val, err := loadFloat(room, "mini_speed", 1); err != nil { if val, err := loadFloat(cfg, "mini_speed", 1); err != nil {
return RoomIni{}, err return err
} else { } else {
roomini.miniSpeed = val roomini.miniSpeed = val
} }
return roomini, nil return nil
} }
func loadInt(sec *ini.Section, key string, def int) (int, error) { func loadInt(cfg *ini.File, key string, def int) (int, error) {
sections := cfg.Sections()
for _, sec := range sections {
val, err := sec.GetKey(key) val, err := sec.GetKey(key)
if err != nil { if err != nil {
return def, nil continue
} }
valint, err := val.Int() valint, err := val.Int()
if err != nil { if err != nil {
return def, err return def, err
} }
return valint, nil return valint, nil
} }
func loadFloat(sec *ini.Section, key string, def float64) (float64, error) {
val, err := sec.GetKey(key)
if err != nil {
return def, nil return def, nil
} }
func loadFloat(cfg *ini.File, key string, def float64) (float64, error) {
sections := cfg.Sections()
for _, sec := range sections {
val, err := sec.GetKey(key)
if err != nil {
continue
}
valfloat, err := val.Float64() valfloat, err := val.Float64()
if err != nil { if err != nil {
return def, err return def, err
} }
return valfloat, nil return valfloat, nil
} }
return def, nil
}
func loadString(cfg *ini.File, key string, def string) string {
sections := cfg.Sections()
for _, sec := range sections {
val, err := sec.GetKey(key)
if err != nil {
continue
}
return val.String()
}
return def
}
+72 -10
View File
@@ -28,17 +28,12 @@ func EmptyOut(outpath string) error {
return err return err
} }
cfpath := path.Join(outpath, "CF.ini") err = emptyOutCredits(outpath)
exists, err := Exists(cfpath)
if err != nil { if err != nil {
return err return err
} }
if !exists {
return nil
}
log.Printf("INFO: removing %s", cfpath) return emptyOutCF(outpath)
return os.Remove(cfpath)
} }
func emptyOutData(outpath string) error { func emptyOutData(outpath string) error {
@@ -77,25 +72,92 @@ func emptyOutData(outpath string) error {
return nil return nil
} }
func emptyOutCredits(outpath string) error {
cfpath := path.Join(outpath, "credits.txt")
exists, err := Exists(cfpath)
if err != nil {
return err
}
if !exists {
return nil
}
log.Printf("INFO: removing %s", cfpath)
return os.Remove(cfpath)
}
func emptyOutCF(outpath string) error {
cfpath := path.Join(outpath, "CF.ini")
exists, err := Exists(cfpath)
if err != nil {
return err
}
if !exists {
return nil
}
log.Printf("INFO: removing %s", cfpath)
return os.Remove(cfpath)
}
func CopyOut(roomFolders []RoomFolder, outpath string, splash1len, splash2len int) error { func CopyOut(roomFolders []RoomFolder, outpath string, splash1len, splash2len int) error {
inistr := fmt.Sprintf(`[SETTINGS] // base ini settings
var inistr strings.Builder
fmt.Fprintf(&inistr, `[SETTINGS]
Sensitivity=6 Sensitivity=6
Splash1len=%d Splash1len=%d
Splash2len=%d Splash2len=%d
`, splash1len, splash2len) `, splash1len, splash2len)
// loop through folders
roomcredits := []string{}
for _, roomFolder := range roomFolders { for _, roomFolder := range roomFolders {
err := copyFolder(roomFolder, outpath) err := copyFolder(roomFolder, outpath)
if err != nil { if err != nil {
return fmt.Errorf("error copying folder %s: %+v", roomFolder.Path, err) return fmt.Errorf("error copying folder %s: %+v", roomFolder.Path, err)
} }
inistr += roomFolder.Cfg.ToIni() inistr.WriteString(roomFolder.Cfg.ToIni())
roomFolder, err = checkCredits(roomFolder);
if err != nil {
return fmt.Errorf("error checking credits in folder %s: %+v", roomFolder.Path, err)
} }
roomcredits = append(roomcredits, roomFolder.Cfg.ToCredits())
}
// save ini file
outini := filepath.Join(outpath, "CF.ini") outini := filepath.Join(outpath, "CF.ini")
log.Println("INFO:") log.Println("INFO:")
log.Printf("INFO: Writing %s", outini) log.Printf("INFO: Writing %s", outini)
return os.WriteFile(outini, []byte(inistr), FILEPERM)
if err := os.WriteFile(outini, []byte(inistr.String()), FILEPERM); err != nil {
return err
}
// save credits file
creditsstring := strings.Join(roomcredits, "\n")
outcredits := filepath.Join(outpath, "credits.txt")
log.Println("INFO:")
log.Printf("INFO: Writing %s", outcredits)
return os.WriteFile(outcredits, []byte(creditsstring), FILEPERM)
}
// doesn't error currently
func checkCredits(roomFolder RoomFolder) (RoomFolder, error) {
if roomFolder.Cfg.name == "" {
log.Println("WARNING: room config has no name, defaulting to folder name")
foldername := filepath.Base(roomFolder.Path)
roomFolder.Cfg.name = foldername
}
if roomFolder.Cfg.credits == "" {
log.Println("WARNING: room config has no credits")
}
return roomFolder, nil
} }
func copyFolder(roomFolder RoomFolder, outpath string) error { func copyFolder(roomFolder RoomFolder, outpath string) error {
+1 -1
View File
@@ -94,7 +94,7 @@ rooms
└───ROOM.ini └───ROOM.ini
``` ```
The folder given by `outPath` can safely be the folder with BAP. This program will only touch the `DATA/NIGHT_*` folders and the `CF.ini` file in this folder. The folder given by `outPath` can safely be the folder with BAP. This program will only touch the `DATA/NIGHT_*` folders, the `CF.ini` file, and a generated `credits.txt` in this folder.
`roomPerNight` is used for rooms that do not have an assigned night and room number. `roomPerNight` is used for rooms that do not have an assigned night and room number.