1package pdfexport23import (4 "encoding/json"5 "fmt"6 "strconv"7 "strings"8)910type nurikabeSave struct {11 Width int `json:"width"`12 Height int `json:"height"`13 Clues string `json:"clues"`14}1516func ParseNurikabePrintData(saveData []byte) (*NurikabeData, error) {17 if len(strings.TrimSpace(string(saveData))) == 0 {18 return nil, nil19 }2021 var save nurikabeSave22 if err := json.Unmarshal(saveData, &save); err != nil {23 return nil, fmt.Errorf("decode nurikabe save: %w", err)24 }2526 width := save.Width27 height := save.Height28 if width <= 0 || height <= 0 {29 return nil, nil30 }3132 clues, err := parseNurikabeClues(save.Clues, width, height)33 if err != nil {34 return nil, err35 }3637 return &NurikabeData{38 Width: width,39 Height: height,40 Clues: clues,41 }, nil42}4344func parseNurikabeClues(raw string, width, height int) ([][]int, error) {45 if width <= 0 || height <= 0 {46 return nil, fmt.Errorf("invalid clue dimensions: %dx%d", width, height)47 }4849 clues := make([][]int, height)50 for y := range height {51 clues[y] = make([]int, width)52 }5354 rows := splitNormalizedLines(raw)55 for y := 0; y < len(rows) && y < height; y++ {56 parts := strings.Split(rows[y], ",")57 for x := 0; x < len(parts) && x < width; x++ {58 token := strings.TrimSpace(parts[x])59 if token == "" {60 continue61 }62 value, err := strconv.Atoi(token)63 if err != nil {64 return nil, fmt.Errorf("invalid clue value %q at (%d,%d): %w", token, x, y, err)65 }66 if value < 0 {67 return nil, fmt.Errorf("negative clue value %d at (%d,%d)", value, x, y)68 }69 clues[y][x] = value70 }71 }7273 return clues, nil74}