package main import ( "bytes" "encoding/binary" "fmt" "image" "image/color" "image/png" "math" "os" "path/filepath" "time" ) const ( resourceLanguageEnglishUS = 1033 resourceTypeIcon = 3 resourceTypeGroupIcon = 14 ) type iconImage struct { ID uint16 Size int PNG []byte Planes uint16 Bits uint16 } type relocation struct { offset uint32 } func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func run() error { if err := os.MkdirAll("assets", 0o755); err != nil { return err } if err := os.MkdirAll(filepath.Join("cmd", "ipecho"), 0o755); err != nil { return err } if err := os.MkdirAll(filepath.Join("internal", "httpapi", "static"), 0o755); err != nil { return err } if err := os.WriteFile(filepath.Join("assets", "icon.svg"), []byte(iconSVG), 0o644); err != nil { return err } if err := os.WriteFile(filepath.Join("assets", "favicon.svg"), []byte(iconSVG), 0o644); err != nil { return err } images, err := renderIconImages([]int{16, 24, 32, 48, 64, 128, 256}) if err != nil { return err } ico, err := buildICO(images) if err != nil { return err } if err := os.WriteFile(filepath.Join("assets", "ipecho.ico"), ico, 0o644); err != nil { return err } if err := os.WriteFile(filepath.Join("assets", "favicon.ico"), ico, 0o644); err != nil { return err } if err := os.WriteFile(filepath.Join("internal", "httpapi", "static", "favicon.ico"), ico, 0o644); err != nil { return err } syso, err := buildWindowsResourceObject(images) if err != nil { return err } return os.WriteFile(filepath.Join("cmd", "ipecho", "rsrc_windows_amd64.syso"), syso, 0o644) } func renderIconImages(sizes []int) ([]iconImage, error) { images := make([]iconImage, 0, len(sizes)) for index, size := range sizes { var pngData bytes.Buffer if err := png.Encode(&pngData, renderIcon(size)); err != nil { return nil, err } images = append(images, iconImage{ ID: uint16(index + 1), Size: size, PNG: pngData.Bytes(), Planes: 1, Bits: 32, }) } return images, nil } func renderIcon(size int) image.Image { const scale = 4 large := image.NewNRGBA(image.Rect(0, 0, size*scale, size*scale)) renderIconAt(large) return downsample(large, scale) } func renderIconAt(img *image.NRGBA) { size := float64(img.Bounds().Dx()) unit := size / 256 blue := color.NRGBA{R: 17, G: 103, B: 177, A: 255} darkBlue := color.NRGBA{R: 10, G: 82, B: 148, A: 255} white := color.NRGBA{R: 255, G: 255, B: 255, A: 255} orange := color.NRGBA{R: 245, G: 159, B: 69, A: 255} cyan := color.NRGBA{R: 65, G: 199, B: 215, A: 255} green := color.NRGBA{R: 139, G: 214, B: 170, A: 255} fillRoundedRect(img, 8*unit, 8*unit, 248*unit, 248*unit, 42*unit, darkBlue) fillRoundedRect(img, 0, 0, 240*unit, 240*unit, 42*unit, blue) drawLine(img, 76*unit, 128*unit, 180*unit, 128*unit, 10*unit, white) drawLine(img, 128*unit, 76*unit, 128*unit, 180*unit, 10*unit, white) fillCircle(img, 76*unit, 128*unit, 31*unit, white) fillCircle(img, 180*unit, 128*unit, 31*unit, white) fillCircle(img, 128*unit, 76*unit, 31*unit, white) fillCircle(img, 128*unit, 180*unit, 31*unit, white) fillCircle(img, 76*unit, 128*unit, 22*unit, orange) fillCircle(img, 180*unit, 128*unit, 22*unit, cyan) fillCircle(img, 128*unit, 76*unit, 22*unit, green) fillCircle(img, 128*unit, 180*unit, 22*unit, white) } func fillRoundedRect(img *image.NRGBA, x0, y0, x1, y1, radius float64, c color.NRGBA) { for y := int(math.Floor(y0)); y < int(math.Ceil(y1)); y++ { for x := int(math.Floor(x0)); x < int(math.Ceil(x1)); x++ { if insideRoundedRect(float64(x)+0.5, float64(y)+0.5, x0, y0, x1, y1, radius) { setNRGBA(img, x, y, c) } } } } func insideRoundedRect(x, y, x0, y0, x1, y1, radius float64) bool { if x < x0 || x >= x1 || y < y0 || y >= y1 { return false } cx := math.Min(math.Max(x, x0+radius), x1-radius) cy := math.Min(math.Max(y, y0+radius), y1-radius) dx := x - cx dy := y - cy return dx*dx+dy*dy <= radius*radius } func drawLine(img *image.NRGBA, x0, y0, x1, y1, radius float64, c color.NRGBA) { dx := x1 - x0 dy := y1 - y0 steps := int(math.Ceil(math.Hypot(dx, dy))) if steps < 1 { steps = 1 } for i := 0; i <= steps; i++ { t := float64(i) / float64(steps) fillCircle(img, x0+dx*t, y0+dy*t, radius, c) } } func fillCircle(img *image.NRGBA, cx, cy, radius float64, c color.NRGBA) { minX := int(math.Floor(cx - radius)) maxX := int(math.Ceil(cx + radius)) minY := int(math.Floor(cy - radius)) maxY := int(math.Ceil(cy + radius)) radius2 := radius * radius for y := minY; y <= maxY; y++ { for x := minX; x <= maxX; x++ { dx := float64(x) + 0.5 - cx dy := float64(y) + 0.5 - cy if dx*dx+dy*dy <= radius2 { setNRGBA(img, x, y, c) } } } } func setNRGBA(img *image.NRGBA, x, y int, c color.NRGBA) { if !image.Pt(x, y).In(img.Bounds()) { return } offset := img.PixOffset(x, y) img.Pix[offset+0] = c.R img.Pix[offset+1] = c.G img.Pix[offset+2] = c.B img.Pix[offset+3] = c.A } func downsample(src *image.NRGBA, scale int) *image.NRGBA { dst := image.NewNRGBA(image.Rect(0, 0, src.Bounds().Dx()/scale, src.Bounds().Dy()/scale)) for y := 0; y < dst.Bounds().Dy(); y++ { for x := 0; x < dst.Bounds().Dx(); x++ { var r, g, b, a uint32 for sy := 0; sy < scale; sy++ { for sx := 0; sx < scale; sx++ { offset := src.PixOffset(x*scale+sx, y*scale+sy) r += uint32(src.Pix[offset+0]) g += uint32(src.Pix[offset+1]) b += uint32(src.Pix[offset+2]) a += uint32(src.Pix[offset+3]) } } count := uint32(scale * scale) offset := dst.PixOffset(x, y) dst.Pix[offset+0] = uint8(r / count) dst.Pix[offset+1] = uint8(g / count) dst.Pix[offset+2] = uint8(b / count) dst.Pix[offset+3] = uint8(a / count) } } return dst } func buildICO(images []iconImage) ([]byte, error) { var buf bytes.Buffer writeLE(&buf, uint16(0)) writeLE(&buf, uint16(1)) writeLE(&buf, uint16(len(images))) imageOffset := 6 + len(images)*16 for _, img := range images { buf.WriteByte(iconDimensionByte(img.Size)) buf.WriteByte(iconDimensionByte(img.Size)) buf.WriteByte(0) buf.WriteByte(0) writeLE(&buf, img.Planes) writeLE(&buf, img.Bits) writeLE(&buf, uint32(len(img.PNG))) writeLE(&buf, uint32(imageOffset)) imageOffset += len(img.PNG) } for _, img := range images { if _, err := buf.Write(img.PNG); err != nil { return nil, err } } return buf.Bytes(), nil } func buildWindowsResourceObject(images []iconImage) ([]byte, error) { section, relocations, err := buildResourceSection(images) if err != nil { return nil, err } const ( machineAMD64 = 0x8664 sectionHeaderSize = 40 fileHeaderSize = 20 symbolSize = 18 relocationSize = 10 imageRelAMD64Addr32NB = 0x0003 imageScnCntInitializedData = 0x00000040 imageScnAlign4Bytes = 0x00300000 imageScnMemRead = 0x40000000 imageSymClassStatic = 3 ) rawDataPointer := uint32(fileHeaderSize + sectionHeaderSize) relocationPointer := rawDataPointer + uint32(len(section)) symbolPointer := relocationPointer + uint32(len(relocations)*relocationSize) var buf bytes.Buffer writeLE(&buf, uint16(machineAMD64)) writeLE(&buf, uint16(1)) writeLE(&buf, uint32(time.Now().Unix())) writeLE(&buf, symbolPointer) writeLE(&buf, uint32(1)) writeLE(&buf, uint16(0)) writeLE(&buf, uint16(0)) writeSectionName(&buf, ".rsrc") writeLE(&buf, uint32(0)) writeLE(&buf, uint32(0)) writeLE(&buf, uint32(len(section))) writeLE(&buf, rawDataPointer) writeLE(&buf, relocationPointer) writeLE(&buf, uint32(0)) writeLE(&buf, uint16(len(relocations))) writeLE(&buf, uint16(0)) writeLE(&buf, uint32(imageScnCntInitializedData|imageScnAlign4Bytes|imageScnMemRead)) buf.Write(section) for _, rel := range relocations { writeLE(&buf, rel.offset) writeLE(&buf, uint32(0)) writeLE(&buf, uint16(imageRelAMD64Addr32NB)) } writeSectionName(&buf, ".rsrc") writeLE(&buf, uint32(0)) writeLE(&buf, int16(1)) writeLE(&buf, uint16(0)) buf.WriteByte(imageSymClassStatic) buf.WriteByte(0) writeLE(&buf, uint32(4)) return buf.Bytes(), nil } func buildResourceSection(images []iconImage) ([]byte, []relocation, error) { var b resourceBuilder b.directory(0, 2) rootEntryIcon := b.entry(resourceTypeIcon, 0) rootEntryGroupIcon := b.entry(resourceTypeGroupIcon, 0) iconTypeDir := b.directory(0, uint16(len(images))) b.patchDirectoryEntry(rootEntryIcon, resourceTypeIcon, iconTypeDir) iconEntryOffsets := make([]int, len(images)) for i, img := range images { iconEntryOffsets[i] = b.entry(uint32(img.ID), 0) } groupTypeDir := b.directory(0, 1) b.patchDirectoryEntry(rootEntryGroupIcon, resourceTypeGroupIcon, groupTypeDir) groupEntry := b.entry(1, 0) for i, img := range images { nameDir := b.directory(0, 1) b.patchDirectoryEntry(iconEntryOffsets[i], uint32(img.ID), nameDir) langEntry := b.entry(resourceLanguageEnglishUS, 0) dataEntry := b.dataEntry(img.PNG) b.patchDataEntry(langEntry, resourceLanguageEnglishUS, dataEntry) } groupNameDir := b.directory(0, 1) b.patchDirectoryEntry(groupEntry, 1, groupNameDir) groupLangEntry := b.entry(resourceLanguageEnglishUS, 0) groupData := buildGroupIconResource(images) groupDataEntry := b.dataEntry(groupData) b.patchDataEntry(groupLangEntry, resourceLanguageEnglishUS, groupDataEntry) return b.bytes(), b.relocations, nil } type resourceBuilder struct { buf bytes.Buffer relocations []relocation } func (b *resourceBuilder) directory(namedEntries, idEntries uint16) int { offset := b.buf.Len() writeLE(&b.buf, uint32(0)) writeLE(&b.buf, uint32(0)) writeLE(&b.buf, uint16(0)) writeLE(&b.buf, uint16(0)) writeLE(&b.buf, namedEntries) writeLE(&b.buf, idEntries) return offset } func (b *resourceBuilder) entry(id uint32, offset uint32) int { entryOffset := b.buf.Len() writeLE(&b.buf, id) writeLE(&b.buf, offset) return entryOffset } func (b *resourceBuilder) patchDirectoryEntry(entryOffset int, id uint32, directoryOffset int) { b.writeAt(entryOffset, id) b.writeAt(entryOffset+4, uint32(directoryOffset)|0x80000000) } func (b *resourceBuilder) patchDataEntry(entryOffset int, id uint32, dataEntryOffset int) { b.writeAt(entryOffset, id) b.writeAt(entryOffset+4, uint32(dataEntryOffset)) } func (b *resourceBuilder) dataEntry(data []byte) int { b.align4() dataEntryOffset := b.buf.Len() writeLE(&b.buf, uint32(0)) writeLE(&b.buf, uint32(len(data))) writeLE(&b.buf, uint32(0)) writeLE(&b.buf, uint32(0)) b.align4() dataOffset := b.buf.Len() b.writeAt(dataEntryOffset, uint32(dataOffset)) b.relocations = append(b.relocations, relocation{offset: uint32(dataEntryOffset)}) b.buf.Write(data) b.align4() return dataEntryOffset } func (b *resourceBuilder) align4() { for b.buf.Len()%4 != 0 { b.buf.WriteByte(0) } } func (b *resourceBuilder) writeAt(offset int, value uint32) { binary.LittleEndian.PutUint32(b.buf.Bytes()[offset:offset+4], value) } func (b *resourceBuilder) bytes() []byte { return b.buf.Bytes() } func buildGroupIconResource(images []iconImage) []byte { var buf bytes.Buffer writeLE(&buf, uint16(0)) writeLE(&buf, uint16(1)) writeLE(&buf, uint16(len(images))) for _, img := range images { buf.WriteByte(iconDimensionByte(img.Size)) buf.WriteByte(iconDimensionByte(img.Size)) buf.WriteByte(0) buf.WriteByte(0) writeLE(&buf, img.Planes) writeLE(&buf, img.Bits) writeLE(&buf, uint32(len(img.PNG))) writeLE(&buf, img.ID) } return buf.Bytes() } func iconDimensionByte(size int) byte { if size >= 256 { return 0 } return byte(size) } func writeLE[T ~uint16 | ~uint32 | ~int16](buf *bytes.Buffer, value T) { _ = binary.Write(buf, binary.LittleEndian, value) } func writeSectionName(buf *bytes.Buffer, name string) { var raw [8]byte copy(raw[:], name) buf.Write(raw[:]) } const iconSVG = ` `