pax_global_header00006660000000000000000000000064134374506450014525gustar00rootroot0000000000000052 comment=b9f6b78f792dc3dddaf295854d968f5b6fdcdba1 rsrc-0.8.0/000077500000000000000000000000001343745064500125035ustar00rootroot00000000000000rsrc-0.8.0/.hgignore000066400000000000000000000000441343745064500143040ustar00rootroot00000000000000glob:*.res glob:*.exe glob:tmp rsrc-0.8.0/.travis.yml000066400000000000000000000000451343745064500146130ustar00rootroot00000000000000language: go go: - 1.x - master rsrc-0.8.0/AUTHORS000066400000000000000000000001521343745064500135510ustar00rootroot00000000000000Mateusz Czapliński Quentin Renard shnmng rsrc-0.8.0/LICENSE.txt000066400000000000000000000021261343745064500143270ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013-2017 The rsrc Authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rsrc-0.8.0/README.txt000066400000000000000000000027121343745064500142030ustar00rootroot00000000000000rsrc - Tool for embedding binary resources in Go programs. INSTALL: go get github.com/akavel/rsrc USAGE: rsrc [-manifest FILE.exe.manifest] [-ico FILE.ico[,FILE2.ico...]] -o FILE.syso Generates a .syso file with specified resources embedded in .rsrc section. The .syso file can be linked by Go linker when building Win32 executables. Icon embedded this way will show up on application's .exe instead of empty icon. Manifest file embedded this way will be recognized and detected by Windows. The generated *.syso files should get automatically recognized by 'go build' command and linked into an executable/library, as long as there are any *.go files in the same directory. OPTIONS: -arch="386": architecture of output file - one of: 386, [EXPERIMENTAL: amd64] -data="": path to raw data file to embed [WARNING: useless for Go 1.4+] -ico="": comma-separated list of paths to .ico files to embed -manifest="": path to a Windows manifest file to embed -o="rsrc.syso": name of output COFF (.res or .syso) file Based on ideas presented by Minux. In case anything does not work, it'd be nice if you could report (either via Github issues, or via email to czapkofan@gmail.com), and please attach the input file(s) which resulted in a problem, plus error message & symptoms, and/or any other details. TODO MAYBE/LATER: - fix or remove FIXMEs LICENSE: MIT Copyright 2013-2017 The rsrc Authors. http://github.com/akavel/rsrc rsrc-0.8.0/binutil/000077500000000000000000000000001343745064500141515ustar00rootroot00000000000000rsrc-0.8.0/binutil/plain.go000066400000000000000000000006001343745064500155770ustar00rootroot00000000000000package binutil import ( "reflect" ) func Plain(kind reflect.Kind) bool { switch kind { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: return true } return false } rsrc-0.8.0/binutil/sizedfile.go000066400000000000000000000012441343745064500164570ustar00rootroot00000000000000package binutil import ( "io" "os" ) type SizedReader interface { io.Reader Size() int64 } type SizedFile struct { f *os.File s *io.SectionReader // helper, for Size() } func (r *SizedFile) Read(p []byte) (n int, err error) { return r.s.Read(p) } func (r *SizedFile) Size() int64 { return r.s.Size() } func (r *SizedFile) Close() error { return r.f.Close() } func SizedOpen(filename string) (*SizedFile, error) { f, err := os.Open(filename) if err != nil { return nil, err } info, err := f.Stat() if err != nil { return nil, err } return &SizedFile{ f: f, s: io.NewSectionReader(f, 0, info.Size()), }, nil } rsrc-0.8.0/binutil/walk.go000066400000000000000000000022511343745064500154360ustar00rootroot00000000000000package binutil import ( "errors" "fmt" "path" "reflect" ) var ( WALK_SKIP = errors.New("") ) type Walker func(v reflect.Value, path string) error func Walk(value interface{}, walker Walker) error { err := walk(reflect.ValueOf(value), "/", walker) if err == WALK_SKIP { err = nil } return err } func stopping(err error) bool { return err != nil && err != WALK_SKIP } func walk(v reflect.Value, spath string, walker Walker) error { err := walker(v, spath) if err != nil { return err } v = reflect.Indirect(v) switch v.Kind() { case reflect.Slice, reflect.Array: for i := 0; i < v.Len(); i++ { err = walk(v.Index(i), spath+fmt.Sprintf("[%d]", i), walker) if stopping(err) { return err } } case reflect.Interface: err = walk(v.Elem(), spath, walker) if stopping(err) { return err } case reflect.Struct: //t := v.Type() for i := 0; i < v.NumField(); i++ { //f := t.Field(i) //TODO: handle unexported fields vv := v.Field(i) err = walk(vv, path.Join(spath, v.Type().Field(i).Name), walker) if stopping(err) { return err } } default: // FIXME: handle other special cases too // String return nil } return nil } rsrc-0.8.0/binutil/writer.go000066400000000000000000000010011343745064500160040ustar00rootroot00000000000000package binutil import ( "encoding/binary" "io" "reflect" ) type Writer struct { W io.Writer Offset uint32 //FIXME: int64? Err error } func (w *Writer) WriteLE(v interface{}) { if w.Err != nil { return } w.Err = binary.Write(w.W, binary.LittleEndian, v) if w.Err != nil { return } w.Offset += uint32(reflect.TypeOf(v).Size()) } func (w *Writer) WriteFromSized(r SizedReader) { if w.Err != nil { return } var n int64 n, w.Err = io.CopyN(w.W, r, r.Size()) w.Offset += uint32(n) } rsrc-0.8.0/buildcross.bat000066400000000000000000000011531343745064500153440ustar00rootroot00000000000000@echo off rem NOTE: see also: rem https://github.com/golang/go/wiki/WindowsCrossCompiling rem https://github.com/golang/go/wiki/InstallFromSource#install-c-tools for %%p in (windows_386 windows_amd64) do call :build rsrc %%p set GOOS= set GOARCH= goto :eof :build set APP=%1 set PLATFORM=%2 :: Split param into GOOS & GOARCH (see: http://ss64.com/nt/syntax-substring.html) set GOARCH=%PLATFORM:*_=% call set GOOS=%%PLATFORM:_%GOARCH%=%% :: Build filename set FNAME=%APP%_%PLATFORM% if "%GOOS%"=="windows" set FNAME=%FNAME%.exe :: Do the build echo %FNAME% go build -i -v -o %FNAME% . goto :eof rsrc-0.8.0/coff/000077500000000000000000000000001343745064500134205ustar00rootroot00000000000000rsrc-0.8.0/coff/coff.go000066400000000000000000000270141343745064500146700ustar00rootroot00000000000000package coff import ( "debug/pe" "encoding/binary" "errors" "io" "reflect" "regexp" "sort" "strconv" "strings" "github.com/akavel/rsrc/binutil" ) type Dir struct { // struct IMAGE_RESOURCE_DIRECTORY Characteristics uint32 TimeDateStamp uint32 MajorVersion uint16 MinorVersion uint16 NumberOfNamedEntries uint16 NumberOfIdEntries uint16 DirEntries Dirs } type DirEntries []DirEntry type Dirs []Dir type DirEntry struct { // struct IMAGE_RESOURCE_DIRECTORY_ENTRY NameOrId uint32 OffsetToData uint32 } type DataEntry struct { // struct IMAGE_RESOURCE_DATA_ENTRY OffsetToData uint32 Size1 uint32 CodePage uint32 //FIXME: what value here? for now just using 0 Reserved uint32 } type RelocationEntry struct { RVA uint32 // "offset within the Section's raw data where the address starts." SymbolIndex uint32 // "(zero based) index in the Symbol table to which the reference refers." Type uint16 } // Values reverse-engineered from windres output; names from teh Internets. // Teh googlies Internets don't seem to have much to say about the AMD64 one, // unfortunately :/ but it works... const ( _IMAGE_REL_AMD64_ADDR32NB = 0x03 _IMAGE_REL_I386_DIR32NB = 0x07 ) type Auxiliary [18]byte type Symbol struct { Name [8]byte Value uint32 SectionNumber uint16 Type uint16 StorageClass uint8 AuxiliaryCount uint8 Auxiliaries []Auxiliary } type StringsHeader struct { Length uint32 } const ( MASK_SUBDIRECTORY = 1 << 31 RT_ICON = 3 RT_GROUP_ICON = 3 + 11 RT_MANIFEST = 24 ) // http://www.delorie.com/djgpp/doc/coff/symtab.html const ( DT_PTR = 1 T_UCHAR = 12 ) var ( STRING_RSRC = [8]byte{'.', 'r', 's', 'r', 'c', 0, 0, 0} STRING_RDATA = [8]byte{'.', 'r', 'd', 'a', 't', 'a', 0, 0} LANG_ENTRY = DirEntry{NameOrId: 0x0409} //FIXME: language; what value should be here? ) type Sizer interface { Size() int64 //NOTE: must not exceed limits of uint32, or behavior is undefined } type Coff struct { pe.FileHeader pe.SectionHeader32 *Dir DataEntries []DataEntry Data []Sizer Relocations []RelocationEntry Symbols []Symbol StringsHeader Strings []Sizer } func NewRDATA() *Coff { return &Coff{ pe.FileHeader{ Machine: pe.IMAGE_FILE_MACHINE_I386, NumberOfSections: 1, // .data TimeDateStamp: 0, NumberOfSymbols: 2, // starting only with '.rdata', will increase; must include auxiliaries, apparently SizeOfOptionalHeader: 0, Characteristics: 0x0105, //http://www.delorie.com/djgpp/doc/coff/filhdr.html }, pe.SectionHeader32{ Name: STRING_RDATA, Characteristics: 0x40000040, // "INITIALIZED_DATA MEM_READ" ? }, // "directory hierarchy" of .rsrc section; empty for .data function nil, []DataEntry{}, []Sizer{}, []RelocationEntry{}, []Symbol{Symbol{ Name: STRING_RDATA, Value: 0, SectionNumber: 1, Type: 0, // FIXME: wtf? StorageClass: 3, // FIXME: is it ok? and uint8? and what does the value mean? AuxiliaryCount: 1, Auxiliaries: []Auxiliary{{}}, //http://www6.cptec.inpe.br/sx4/sx4man2/g1af01e/chap5.html }}, StringsHeader{ Length: uint32(binary.Size(StringsHeader{})), // empty strings table for now -- but we must still show size of the table's header... }, []Sizer{}, } } // NOTE: must be called immediately after NewRSRC, before any other // functions. func (coff *Coff) Arch(arch string) error { switch arch { case "386": coff.Machine = pe.IMAGE_FILE_MACHINE_I386 case "amd64": // Sources: // https://github.com/golang/go/blob/0e23ca41d99c82d301badf1b762888e2c69e6c57/src/debug/pe/pe.go#L116 // https://github.com/yasm/yasm/blob/7160679eee91323db98b0974596c7221eeff772c/modules/objfmts/coff/coff-objfmt.c#L38 // FIXME: currently experimental -- not sure if something more doesn't need to be changed coff.Machine = pe.IMAGE_FILE_MACHINE_AMD64 default: return errors.New("coff: unknown architecture: " + arch) } return nil } //NOTE: only usable for Coff created using NewRDATA //NOTE: symbol names must be probably >8 characters long //NOTE: symbol names should not contain embedded zeroes func (coff *Coff) AddData(symbol string, data Sizer) { coff.addSymbol(symbol) coff.Data = append(coff.Data, data) coff.SectionHeader32.SizeOfRawData += uint32(data.Size()) } // addSymbol appends a symbol to Coff.Symbols and to Coff.Strings. //NOTE: symbol s must be probably >8 characters long //NOTE: symbol s should not contain embedded zeroes func (coff *Coff) addSymbol(s string) { coff.FileHeader.NumberOfSymbols++ buf := strings.NewReader(s + "\000") // ASCIIZ r := io.NewSectionReader(buf, 0, int64(len(s)+1)) coff.Strings = append(coff.Strings, r) coff.StringsHeader.Length += uint32(r.Size()) coff.Symbols = append(coff.Symbols, Symbol{ //Name: // will be filled in Freeze //Value: // as above SectionNumber: 1, Type: 0, // why 0??? // DT_PTR<<4 | T_UCHAR, // unsigned char* // (?) or use void* ? T_VOID=1 StorageClass: 2, // 2=C_EXT, or 5=C_EXTDEF ? AuxiliaryCount: 0, }) } func NewRSRC() *Coff { return &Coff{ pe.FileHeader{ Machine: pe.IMAGE_FILE_MACHINE_I386, NumberOfSections: 1, // .rsrc TimeDateStamp: 0, // was also 0 in sample data from MinGW's windres.exe NumberOfSymbols: 1, SizeOfOptionalHeader: 0, Characteristics: 0x0104, //FIXME: copied from windres.exe output, find out what should be here and why }, pe.SectionHeader32{ Name: STRING_RSRC, Characteristics: 0x40000040, // "INITIALIZED_DATA MEM_READ" ? }, // "directory hierarchy" of .rsrc section: top level goes resource type, then id/name, then language &Dir{}, []DataEntry{}, []Sizer{}, []RelocationEntry{}, []Symbol{Symbol{ Name: STRING_RSRC, Value: 0, SectionNumber: 1, Type: 0, // FIXME: wtf? StorageClass: 3, // FIXME: is it ok? and uint8? and what does the value mean? AuxiliaryCount: 0, // FIXME: wtf? }}, StringsHeader{ Length: uint32(binary.Size(StringsHeader{})), // empty strings table -- but we must still show size of the table's header... }, []Sizer{}, } } //NOTE: function assumes that 'id' is increasing on each entry //NOTE: only usable for Coff created using NewRSRC func (coff *Coff) AddResource(kind uint32, id uint16, data Sizer) { re := RelocationEntry{ // "(zero based) index in the Symbol table to which the // reference refers. Once you have loaded the COFF file into // memory and know where each symbol is, you find the new // updated address for the given symbol and update the // reference accordingly." SymbolIndex: 0, } switch coff.Machine { case pe.IMAGE_FILE_MACHINE_I386: re.Type = _IMAGE_REL_I386_DIR32NB case pe.IMAGE_FILE_MACHINE_AMD64: re.Type = _IMAGE_REL_AMD64_ADDR32NB } coff.Relocations = append(coff.Relocations, re) coff.SectionHeader32.NumberOfRelocations++ // find top level entry, inserting new if necessary at correct sorted position entries0 := coff.Dir.DirEntries dirs0 := coff.Dir.Dirs i0 := sort.Search(len(entries0), func(i int) bool { return entries0[i].NameOrId >= kind }) if i0 >= len(entries0) || entries0[i0].NameOrId != kind { // inserting new entry & dir entries0 = append(entries0[:i0], append([]DirEntry{{NameOrId: kind}}, entries0[i0:]...)...) dirs0 = append(dirs0[:i0], append([]Dir{{}}, dirs0[i0:]...)...) coff.Dir.NumberOfIdEntries++ } coff.Dir.DirEntries = entries0 coff.Dir.Dirs = dirs0 // for second level, assume ID is always increasing, so we don't have to sort dirs0[i0].DirEntries = append(dirs0[i0].DirEntries, DirEntry{NameOrId: uint32(id)}) dirs0[i0].Dirs = append(dirs0[i0].Dirs, Dir{ NumberOfIdEntries: 1, DirEntries: DirEntries{LANG_ENTRY}, }) dirs0[i0].NumberOfIdEntries++ // calculate preceding DirEntry leaves, to find new index in Data & DataEntries n := 0 for _, dir0 := range dirs0[:i0+1] { n += len(dir0.DirEntries) //NOTE: assuming 1 language here; TODO: dwell deeper if more langs added } n-- // insert new data in correct place coff.DataEntries = append(coff.DataEntries[:n], append([]DataEntry{{Size1: uint32(data.Size())}}, coff.DataEntries[n:]...)...) coff.Data = append(coff.Data[:n], append([]Sizer{data}, coff.Data[n:]...)...) } // Freeze fills in some important offsets in resulting file. func (coff *Coff) Freeze() { switch coff.SectionHeader32.Name { case STRING_RSRC: coff.freezeRSRC() case STRING_RDATA: coff.freezeRDATA() } } func (coff *Coff) freezeCommon1(path string, offset, diroff uint32) (newdiroff uint32) { switch path { case "/Dir": coff.SectionHeader32.PointerToRawData = offset diroff = offset case "/Relocations": coff.SectionHeader32.PointerToRelocations = offset coff.SectionHeader32.SizeOfRawData = offset - diroff case "/Symbols": coff.FileHeader.PointerToSymbolTable = offset } return diroff } func freezeCommon2(v reflect.Value, offset *uint32) error { if binutil.Plain(v.Kind()) { *offset += uint32(binary.Size(v.Interface())) // TODO: change to v.Type().Size() ? return nil } vv, ok := v.Interface().(Sizer) if ok { *offset += uint32(vv.Size()) return binutil.WALK_SKIP } return nil } func (coff *Coff) freezeRDATA() { var offset, diroff, stringsoff uint32 binutil.Walk(coff, func(v reflect.Value, path string) error { diroff = coff.freezeCommon1(path, offset, diroff) RE := regexp.MustCompile const N = `\[(\d+)\]` m := matcher{} //TODO: adjust symbol pointers //TODO: fill Symbols.Name, .Value switch { case m.Find(path, RE("^/Data"+N+"$")): n := m[0] coff.Symbols[1+n].Value = offset - diroff // FIXME: is it ok? sz := uint64(coff.Data[n].Size()) binary.LittleEndian.PutUint64(coff.Symbols[0].Auxiliaries[0][0:8], binary.LittleEndian.Uint64(coff.Symbols[0].Auxiliaries[0][0:8])+sz) case path == "/StringsHeader": stringsoff = offset case m.Find(path, RE("^/Strings"+N+"$")): binary.LittleEndian.PutUint32(coff.Symbols[m[0]+1].Name[4:8], offset-stringsoff) } return freezeCommon2(v, &offset) }) coff.SectionHeader32.PointerToRelocations = 0 } func (coff *Coff) freezeRSRC() { leafwalker := make(chan *DirEntry) go func() { for _, dir1 := range coff.Dir.Dirs { // resource type for _, dir2 := range dir1.Dirs { // resource ID for i := range dir2.DirEntries { // resource lang leafwalker <- &dir2.DirEntries[i] } } } }() var offset, diroff uint32 binutil.Walk(coff, func(v reflect.Value, path string) error { diroff = coff.freezeCommon1(path, offset, diroff) RE := regexp.MustCompile const N = `\[(\d+)\]` m := matcher{} switch { case m.Find(path, RE("^/Dir/Dirs"+N+"$")): coff.Dir.DirEntries[m[0]].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff) case m.Find(path, RE("^/Dir/Dirs"+N+"/Dirs"+N+"$")): coff.Dir.Dirs[m[0]].DirEntries[m[1]].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff) case m.Find(path, RE("^/DataEntries"+N+"$")): direntry := <-leafwalker direntry.OffsetToData = offset - diroff case m.Find(path, RE("^/DataEntries"+N+"/OffsetToData$")): coff.Relocations[m[0]].RVA = offset - diroff case m.Find(path, RE("^/Data"+N+"$")): coff.DataEntries[m[0]].OffsetToData = offset - diroff } return freezeCommon2(v, &offset) }) } func mustAtoi(s string) int { i, err := strconv.Atoi(s) if err != nil { panic(err) } return i } type matcher []int func (m *matcher) Find(s string, re *regexp.Regexp) bool { subs := re.FindStringSubmatch(s) if subs == nil { return false } *m = (*m)[:0] for i := 1; i < len(subs); i++ { *m = append(*m, mustAtoi(subs[i])) } return true } rsrc-0.8.0/go.mod000066400000000000000000000000471343745064500136120ustar00rootroot00000000000000module github.com/akavel/rsrc go 1.12 rsrc-0.8.0/ico/000077500000000000000000000000001343745064500132555ustar00rootroot00000000000000rsrc-0.8.0/ico/ico.go000066400000000000000000000117411343745064500143620ustar00rootroot00000000000000// Package ico describes Windows ICO file format. package ico // ICO: http://msdn.microsoft.com/en-us/library/ms997538.aspx // BMP/DIB: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183562%28v=vs.85%29.aspx import ( "bytes" "encoding/binary" "fmt" "image" "image/color" "io" "io/ioutil" "sort" ) const ( BI_RGB = 0 ) type ICONDIR struct { Reserved uint16 // must be 0 Type uint16 // Resource Type (1 for icons) Count uint16 // How many images? } type IconDirEntryCommon struct { Width byte // Width, in pixels, of the image Height byte // Height, in pixels, of the image ColorCount byte // Number of colors in image (0 if >=8bpp) Reserved byte // Reserved (must be 0) Planes uint16 // Color Planes BitCount uint16 // Bits per pixel BytesInRes uint32 // How many bytes in this resource? } type ICONDIRENTRY struct { IconDirEntryCommon ImageOffset uint32 // Where in the file is this image? [from beginning of file] } type BITMAPINFOHEADER struct { Size uint32 Width int32 Height int32 // NOTE: "represents the combined height of the XOR and AND masks. Remember to divide this number by two before using it to perform calculations for either of the XOR or AND masks." Planes uint16 // [BMP/DIB]: "is always 1" BitCount uint16 Compression uint32 // for ico = 0 SizeImage uint32 XPelsPerMeter int32 // for ico = 0 YPelsPerMeter int32 // for ico = 0 ClrUsed uint32 // for ico = 0 ClrImportant uint32 // for ico = 0 } type RGBQUAD struct { Blue byte Green byte Red byte Reserved byte // must be 0 } func skip(r io.Reader, n int64) error { _, err := io.CopyN(ioutil.Discard, r, n) return err } type icoOffset struct { n int offset uint32 } type rawico struct { icoinfo ICONDIRENTRY bmpinfo *BITMAPINFOHEADER idx int data []byte } type byOffsets []rawico func (o byOffsets) Len() int { return len(o) } func (o byOffsets) Less(i, j int) bool { return o[i].icoinfo.ImageOffset < o[j].icoinfo.ImageOffset } func (o byOffsets) Swap(i, j int) { tmp := o[i] o[i] = o[j] o[j] = tmp } type ICO struct { image.Image } func DecodeHeaders(r io.Reader) ([]ICONDIRENTRY, error) { var hdr ICONDIR err := binary.Read(r, binary.LittleEndian, &hdr) if err != nil { return nil, err } if hdr.Reserved != 0 || hdr.Type != 1 { return nil, fmt.Errorf("bad magic number") } entries := make([]ICONDIRENTRY, hdr.Count) for i := 0; i < len(entries); i++ { err = binary.Read(r, binary.LittleEndian, &entries[i]) if err != nil { return nil, err } } return entries, nil } // NOTE: won't succeed on files with overlapping offsets func unused_decodeAll(r io.Reader) ([]*ICO, error) { var hdr ICONDIR err := binary.Read(r, binary.LittleEndian, &hdr) if err != nil { return nil, err } if hdr.Reserved != 0 || hdr.Type != 1 { return nil, fmt.Errorf("bad magic number") } raws := make([]rawico, hdr.Count) for i := 0; i < len(raws); i++ { err = binary.Read(r, binary.LittleEndian, &raws[i].icoinfo) if err != nil { return nil, err } raws[i].idx = i } sort.Sort(byOffsets(raws)) offset := uint32(binary.Size(&hdr) + len(raws)*binary.Size(ICONDIRENTRY{})) for i := 0; i < len(raws); i++ { err = skip(r, int64(raws[i].icoinfo.ImageOffset-offset)) if err != nil { return nil, err } offset = raws[i].icoinfo.ImageOffset raws[i].bmpinfo = &BITMAPINFOHEADER{} err = binary.Read(r, binary.LittleEndian, raws[i].bmpinfo) if err != nil { return nil, err } err = skip(r, int64(raws[i].bmpinfo.Size-uint32(binary.Size(BITMAPINFOHEADER{})))) if err != nil { return nil, err } raws[i].data = make([]byte, raws[i].icoinfo.BytesInRes-raws[i].bmpinfo.Size) _, err = io.ReadFull(r, raws[i].data) if err != nil { return nil, err } } icos := make([]*ICO, len(raws)) for i := 0; i < len(raws); i++ { fmt.Println(i) icos[raws[i].idx], err = decode(raws[i].bmpinfo, &raws[i].icoinfo, raws[i].data) if err != nil { return nil, err } } return icos, nil } func decode(info *BITMAPINFOHEADER, icoinfo *ICONDIRENTRY, data []byte) (*ICO, error) { if info.Compression != BI_RGB { return nil, fmt.Errorf("ICO compression not supported (got %d)", info.Compression) } //if info.ClrUsed!=0 { // panic(info.ClrUsed) //} r := bytes.NewBuffer(data) bottomup := info.Height > 0 if !bottomup { info.Height = -info.Height } switch info.BitCount { case 8: ncol := int(icoinfo.ColorCount) if ncol == 0 { ncol = 256 } pal := make(color.Palette, ncol) for i := 0; i < ncol; i++ { var rgb RGBQUAD err := binary.Read(r, binary.LittleEndian, &rgb) if err != nil { return nil, err } pal[i] = color.NRGBA{R: rgb.Red, G: rgb.Green, B: rgb.Blue, A: 0xff} //FIXME: is Alpha ok 0xff? } fmt.Println(pal) fmt.Println(info.SizeImage, len(data)-binary.Size(RGBQUAD{})*len(pal), info.Width, info.Height) default: return nil, fmt.Errorf("unsupported ICO bit depth (BitCount) %d", info.BitCount) } return nil, nil } rsrc-0.8.0/internal/000077500000000000000000000000001343745064500143175ustar00rootroot00000000000000rsrc-0.8.0/internal/write.go000066400000000000000000000014111343745064500157750ustar00rootroot00000000000000package internal import ( "fmt" "os" "reflect" "github.com/akavel/rsrc/binutil" "github.com/akavel/rsrc/coff" ) // TODO(akavel): maybe promote this to coff.Coff.WriteTo(io.Writer) (int64, error) func Write(coff *coff.Coff, fnameout string) error { out, err := os.Create(fnameout) if err != nil { return err } defer out.Close() w := binutil.Writer{W: out} // write the resulting file to disk binutil.Walk(coff, func(v reflect.Value, path string) error { if binutil.Plain(v.Kind()) { w.WriteLE(v.Interface()) return nil } vv, ok := v.Interface().(binutil.SizedReader) if ok { w.WriteFromSized(vv) return binutil.WALK_SKIP } return nil }) if w.Err != nil { return fmt.Errorf("Error writing output file: %s", w.Err) } return nil } rsrc-0.8.0/rsrc.go000066400000000000000000000061261343745064500140100ustar00rootroot00000000000000package main import ( "flag" "fmt" "io" "os" "regexp" "strings" "github.com/akavel/rsrc/coff" "github.com/akavel/rsrc/binutil" "github.com/akavel/rsrc/internal" "github.com/akavel/rsrc/rsrc" ) var usage = `USAGE: %s [-manifest FILE.exe.manifest] [-ico FILE.ico[,FILE2.ico...]] -o FILE.syso Generates a .syso file with specified resources embedded in .rsrc section, aimed for consumption by Go linker when building Win32 excecutables. The generated *.syso files should get automatically recognized by 'go build' command and linked into an executable/library, as long as there are any *.go files in the same directory. OPTIONS: ` func main() { //TODO: allow in options advanced specification of multiple resources, as a tree (json?) //FIXME: verify that data file size doesn't exceed uint32 max value var fnamein, fnameico, fnamedata, fnameout, arch string flags := flag.NewFlagSet("", flag.ContinueOnError) flags.StringVar(&fnamein, "manifest", "", "path to a Windows manifest file to embed") flags.StringVar(&fnameico, "ico", "", "comma-separated list of paths to .ico files to embed") flags.StringVar(&fnamedata, "data", "", "path to raw data file to embed [WARNING: useless for Go 1.4+]") flags.StringVar(&fnameout, "o", "rsrc.syso", "name of output COFF (.res or .syso) file") flags.StringVar(&arch, "arch", "386", "architecture of output file - one of: 386, [EXPERIMENTAL: amd64]") _ = flags.Parse(os.Args[1:]) if fnameout == "" || (fnamein == "" && fnamedata == "" && fnameico == "") { fmt.Fprintf(os.Stderr, usage, os.Args[0]) flags.PrintDefaults() os.Exit(1) } var err error switch { case fnamein != "" || fnameico != "": err = rsrc.Embed(fnameout, arch, fnamein, fnameico) case fnamedata != "": err = embedData(fnameout, arch, fnamedata) } if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func embedData(fnameout, arch, fnamedata string) error { if !strings.HasSuffix(fnameout, ".syso") { return fmt.Errorf("Output file name '%s' must end with '.syso'", fnameout) } symname := strings.TrimSuffix(fnameout, ".syso") ok, err := regexp.MatchString(`^[a-z0-9_]+$`, symname) if err != nil { return fmt.Errorf("Internal error: %s", err) } if !ok { return fmt.Errorf("Output file name '%s' must be composed of only lowercase letters (a-z), digits (0-9) and underscore (_)", fnameout) } dat, err := binutil.SizedOpen(fnamedata) if err != nil { return fmt.Errorf("Error opening data file '%s': %s", fnamedata, err) } defer dat.Close() coff := coff.NewRDATA() err = coff.Arch(arch) if err != nil { return err } coff.AddData("_brsrc_"+symname, dat) coff.AddData("_ersrc_"+symname, io.NewSectionReader(strings.NewReader("\000\000"), 0, 2)) // TODO: why? copied from as-generated coff.Freeze() err = internal.Write(coff, fnameout) if err != nil { return err } //FIXME: output a .c file fmt.Println(strings.Replace(`#include "runtime.h" extern byte _brsrc_NAME[], _ersrc_NAME; /* func get_NAME() []byte */ void ·get_NAME(Slice a) { a.array = _brsrc_NAME; a.len = a.cap = &_ersrc_NAME - _brsrc_NAME; FLUSH(&a); }`, "NAME", symname, -1)) return nil }rsrc-0.8.0/rsrc/000077500000000000000000000000001343745064500134545ustar00rootroot00000000000000rsrc-0.8.0/rsrc/rsrc.go000066400000000000000000000044201343745064500147540ustar00rootroot00000000000000package rsrc import ( "encoding/binary" "fmt" "io" "os" "strings" "github.com/akavel/rsrc/binutil" "github.com/akavel/rsrc/coff" "github.com/akavel/rsrc/ico" "github.com/akavel/rsrc/internal" ) // on storing icons, see: http://blogs.msdn.com/b/oldnewthing/archive/2012/07/20/10331787.aspx type _GRPICONDIR struct { ico.ICONDIR Entries []_GRPICONDIRENTRY } func (group _GRPICONDIR) Size() int64 { return int64(binary.Size(group.ICONDIR) + len(group.Entries)*binary.Size(group.Entries[0])) } type _GRPICONDIRENTRY struct { ico.IconDirEntryCommon Id uint16 } func Embed(fnameout, arch, fnamein, fnameico string) error { lastid := uint16(0) newid := func() uint16 { lastid++ return lastid } out := coff.NewRSRC() err := out.Arch(arch) if err != nil { return err } if fnamein != "" { manifest, err := binutil.SizedOpen(fnamein) if err != nil { return fmt.Errorf("rsrc: error opening manifest file '%s': %s", fnamein, err) } defer manifest.Close() id := newid() out.AddResource(coff.RT_MANIFEST, id, manifest) // TODO(akavel): reintroduce the Printlns in package main after Embed returns // fmt.Println("Manifest ID: ", id) } if fnameico != "" { for _, fnameicosingle := range strings.Split(fnameico, ",") { f, err := addIcon(out, fnameicosingle, newid) if err != nil { return err } defer f.Close() } } out.Freeze() return internal.Write(out, fnameout) } func addIcon(out *coff.Coff, fname string, newid func() uint16) (io.Closer, error) { f, err := os.Open(fname) if err != nil { return nil, err } icons, err := ico.DecodeHeaders(f) if err != nil { f.Close() return nil, err } if len(icons) > 0 { // RT_ICONs group := _GRPICONDIR{ICONDIR: ico.ICONDIR{ Reserved: 0, // magic num. Type: 1, // magic num. Count: uint16(len(icons)), }} for _, icon := range icons { id := newid() r := io.NewSectionReader(f, int64(icon.ImageOffset), int64(icon.BytesInRes)) out.AddResource(coff.RT_ICON, id, r) group.Entries = append(group.Entries, _GRPICONDIRENTRY{icon.IconDirEntryCommon, id}) } id := newid() out.AddResource(coff.RT_GROUP_ICON, id, group) // TODO(akavel): reintroduce the Printlns in package main after Embed returns // fmt.Println("Icon ", fname, " ID: ", id) } return f, nil }