// dump2png converts the raw indexed-pixel frame dumps written by the // engine's debug dump hotkey (see ../engine-patches/ and the root // README.md's "Debugging" section) into viewable PNGs, using the real game // palette - see that same README section for the dump file format. package main import ( "encoding/binary" "fmt" "image" "image/color" "image/png" "os" "path/filepath" "sort" "strings" "github.com/inkyblackness/hacked/ss1/content/bitmap" "github.com/inkyblackness/hacked/ss1/resource" "github.com/inkyblackness/hacked/ss1/world" ) func main() { if len(os.Args) != 4 { fmt.Fprintln(os.Stderr, "usage: dump2png ") os.Exit(1) } dataDir, inDir, outDir := os.Args[1], os.Args[2], os.Args[3] entries, err := os.ReadDir(dataDir) if err != nil { panic(err) } merged := &world.ManifestEntry{Origin: []string{dataDir}, ID: dataDir} for _, e := range entries { if e.IsDir() { continue } ext := strings.ToLower(filepath.Ext(e.Name())) if ext != ".res" && ext != ".dat" { continue } fe, err := world.NewManifestEntryFrom([]string{filepath.Join(dataDir, e.Name())}) if err != nil { continue } merged.Resources = append(merged.Resources, fe.Resources...) } sort.Slice(merged.Resources, func(i, j int) bool { return merged.Resources[i].ID < merged.Resources[j].ID }) mod := world.NewMod(func([]resource.ID, []resource.ID) {}, func() {}) if err := mod.World().InsertEntry(0, merged); err != nil { panic(err) } pal, err := bitmap.NewPaletteCache(mod).Palette(resource.KeyOf(0x02BC, resource.LangDefault, 0)) if err != nil { panic(err) } colors := pal.ColorPalette(false) if err := os.MkdirAll(outDir, 0o755); err != nil { panic(err) } files, err := os.ReadDir(inDir) if err != nil { panic(err) } for _, fi := range files { if !strings.HasSuffix(fi.Name(), ".raw") { continue } raw, err := os.ReadFile(filepath.Join(inDir, fi.Name())) if err != nil { fmt.Println("skip", fi.Name(), err) continue } if len(raw) < 4 { continue } w := int(binary.LittleEndian.Uint16(raw[0:2])) h := int(binary.LittleEndian.Uint16(raw[2:4])) pixels := raw[4:] if len(pixels) < w*h { fmt.Println("truncated", fi.Name()) continue } img := image.NewRGBA(image.Rect(0, 0, w, h)) for i := 0; i < w*h; i++ { r, g, b, _ := colors[pixels[i]].RGBA() img.Set(i%w, i/w, color.RGBA{R: byte(r >> 8), G: byte(g >> 8), B: byte(b >> 8), A: 0xff}) } outPath := filepath.Join(outDir, strings.TrimSuffix(fi.Name(), ".raw")+".png") of, err := os.Create(outPath) if err != nil { fmt.Println("create failed", outPath, err) continue } err = png.Encode(of, img) of.Close() if err != nil { fmt.Println("encode failed", outPath, err) continue } fmt.Println("wrote", outPath, w, h) } }