diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fd5b9f8 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +SAVES := imperial-save +SAVES += skygrid-save +SAVES += witchcraft-save + +all: $(SAVES) + +imperial-save: + mkdir imperial-save + ./spatial-db load worldsave "saves/Imperialcity v14.1/region" --output "imperial-save" + +skygrid-save: + mkdir skygrid-save + ./spatial-db load worldsave "saves/SkyGrid/region" --output "skygrid-save" + +witchcraft-save: + mkdir witchcraft-save + ./spatial-db load worldsave "saves/Witchcraft/region" --output "witchcraft-save" + +.PHONY: clean +clean: + rm -r $(SAVES) diff --git a/loading/load_region.go b/loading/load_region.go index 203faea..5845555 100644 --- a/loading/load_region.go +++ b/loading/load_region.go @@ -1,71 +1,46 @@ package loading import ( - "encoding/json" - "fmt" - "log" - "os" - "path/filepath" - "git.nicholasnovak.io/nnovak/spatial-db/world" "github.com/Tnze/go-mc/save" "github.com/Tnze/go-mc/save/region" - "github.com/spf13/cobra" ) -type SectorPos struct { - x, y int -} +// LoadRegionFile loads a single region file into an array of chunks +// +// A region is a 32x32 grid of chunks, although the final output can store less +func LoadRegionFile(fileName string) ([]world.ChunkData, error) { + regionFile, err := region.Open(fileName) + if err != nil { + return nil, err + } + defer regionFile.Close() -var outputDir string + // A region file is a 32x32 grid of chunks + chunks := []world.ChunkData{} -func init() { - LoadRegionFileCommand.Flags().StringVar(&outputDir, "output", ".", "The output directory for the files") -} - -var LoadRegionFileCommand = &cobra.Command{ - Use: "load", - RunE: func(cmd *cobra.Command, args []string) error { - regionFile, err := region.Open(args[0]) - if err != nil { - return err - } - defer regionFile.Close() - - validSectors := []SectorPos{} - - for i := 0; i < 32; i++ { - for j := 0; j < 32; j++ { - if regionFile.ExistSector(i, j) { - validSectors = append(validSectors, SectorPos{i, j}) + for i := 0; i < 32; i++ { + for j := 0; j < 32; j++ { + if regionFile.ExistSector(i, j) { + sectorFile, err := regionFile.ReadSector(i, j) + if err != nil { + return nil, err } + + // Read each chunk from disk + var chunk save.Chunk + if err := chunk.Load(sectorFile); err != nil { + return nil, err + } + + // Convert each chunk into the database's format + var chunkData world.ChunkData + chunkData.FromMCAChunk(chunk) + + chunks = append(chunks, chunkData) } } + } - for _, sectorPos := range validSectors { - data, err := regionFile.ReadSector(sectorPos.x, sectorPos.y) - if err != nil { - return err - } - - var chunk save.Chunk - if err := chunk.Load(data); err != nil { - return err - } - - var chunkData world.ChunkData - chunkData.FromMCAChunk(chunk) - - outfile, err := os.OpenFile(filepath.Join(outputDir, fmt.Sprintf("p.%d.%d.chunk", chunkData.Pos.X, chunkData.Pos.Z)), os.O_WRONLY|os.O_CREATE, 0644) - if err != nil { - log.Fatal(err) - } - if err := json.NewEncoder(outfile).Encode(chunkData); err != nil { - log.Fatal(err) - } - outfile.Close() - } - - return nil - }, + return chunks, nil } diff --git a/loading/load_save.go b/loading/load_save.go new file mode 100644 index 0000000..f5e1a72 --- /dev/null +++ b/loading/load_save.go @@ -0,0 +1,80 @@ +package loading + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + + log "github.com/sirupsen/logrus" + + "github.com/spf13/cobra" +) + +var ( + saveOutputDir string +) + +func init() { + LoadSaveDirCommand.Flags().StringVar(&saveOutputDir, "output", ".", "Where to place the converted save files") +} + +var LoadSaveDirCommand = &cobra.Command{ + Use: "worldsave ", + Short: "Loads all the regions in the specified world's save directory", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + // First, fetch a list of all the files that are in the specified directory + regionFiles, err := os.ReadDir(args[0]) + if err != nil { + return err + } + + log.Infof("Loading save directory of %s", args[0]) + + for regionIndex, regionFile := range regionFiles { + if regionFile.IsDir() { + continue + } + + if strings.HasSuffix(regionFile.Name(), ".mcr") { + log.Warnf("The file %s is in the MCRegion format, skipping", regionFile.Name()) + continue + } + + log.Infof("Converting region file %s, [%d/%d]", + regionFile.Name(), + regionIndex, + len(regionFiles), + ) + + filePath := filepath.Join(args[0], regionFile.Name()) + + // Load each region file + chunks, err := LoadRegionFile(filePath) + if err != nil { + return err + } + + // Save each chunk to a separate file + for _, chunk := range chunks { + outfile, err := os.OpenFile( + filepath.Join(saveOutputDir, chunk.Pos.ToFileName()), + os.O_WRONLY|os.O_CREATE|os.O_APPEND, + 0664, + ) + if err != nil { + return err + } + + if err := json.NewEncoder(outfile).Encode(chunk); err != nil { + return err + } + + outfile.Close() + } + } + + return nil + }, +} diff --git a/main.go b/main.go index 0e3e28b..6f58ea2 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,15 @@ func main() { rootCmd.AddCommand(visualization.VisualizeCommand) rootCmd.AddCommand(connector.ProxyPortCommand) - rootCmd.AddCommand(loading.LoadRegionFileCommand) + + loadCmd := &cobra.Command{ + Use: "load", + Short: "Loads save files into the database's format", + } + + loadCmd.AddCommand(loading.LoadSaveDirCommand) + + rootCmd.AddCommand(loadCmd) if err := rootCmd.Execute(); err != nil { panic(err)