Skip to content

Commit

Permalink
Fixed bug and improved algorithm
Browse files Browse the repository at this point in the history
Improved algorithm based on Microsoft specs, fixed
#4
  • Loading branch information
josephspurrier committed Mar 30, 2016
1 parent 71b8c43 commit 379995a
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 67 deletions.
8 changes: 4 additions & 4 deletions goversioninfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,16 @@ func str2Uint32(s string) uint32 {
return uint32(u)
}

func padString(s string, zeroTerminate bool) []byte {
b := make([]byte, 0, len(s)*2+2)
func padString(s string, zeros int) []byte {

b := make([]byte, 0, len(s)*2)

for _, x := range s {
b = append(b, byte(x))
b = append(b, 0x00)
}

if zeroTerminate {
b = append(b, 0x00)
for i := 0; i < zeros; i++ {
b = append(b, 0x00)
}

Expand Down
7 changes: 7 additions & 0 deletions goversioninfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"testing"
Expand All @@ -20,6 +21,7 @@ func TestFile1(t *testing.T) {
testFile(t, "cmd")
testFile(t, "explorer")
testFile(t, "control")
testFile(t, "simple")
}

func testFile(t *testing.T, filename string) {
Expand All @@ -45,6 +47,10 @@ func testFile(t *testing.T, filename string) {

path2, _ := filepath.Abs("./tests/" + filename + ".hex")

// This is for easily exporting results when the algorithm improves
/*path3, _ := filepath.Abs("./tests/" + filename + ".out")
ioutil.WriteFile(path3, vi.Buffer.Bytes(), 0655)*/

expected, err := ioutil.ReadFile(path2)
if err != nil {
t.Error("Could not load "+filename+".hex", err)
Expand Down Expand Up @@ -362,6 +368,7 @@ func TestStr2Uint32(t *testing.T) {
in string
out uint32
}{{"0", 0}, {"", 0}, {"FFEF", 65519}, {"\x00\x00", 0}} {
log.SetOutput(ioutil.Discard)
got := str2Uint32(tt.in)
if got != tt.out {
t.Errorf("%q: awaited %d, got %d.", tt.in, tt.out, got)
Expand Down
159 changes: 96 additions & 63 deletions structbuild.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package goversioninfo

import (
"math"
"reflect"
)

Expand Down Expand Up @@ -81,9 +80,8 @@ type VSString struct {
WValueLength uint16
WType uint16
SzKey []byte
Padding1 []byte
Padding []byte
Value []byte
Padding2 []byte
}

// VSVarFileInfo holds the translation collection of 1.
Expand Down Expand Up @@ -114,46 +112,44 @@ func buildString(i int, v reflect.Value) (VSString, bool, uint16) {

// If the value is set
if sValue != "" {
// 0 for binary, 1 for text
ss.WType = 0x01

// Create key
ss.SzKey = padString(sName, false)
soFar := len(ss.SzKey) + 6
ss.Padding1 = padBytes(4 - int(math.Mod(float64(soFar), 4)))
// Ensure there is at least 4 bytes between the key and value by NOT
// using this code
/*if len(ss.Padding1) == 4 {
ss.Padding1 = []byte{}
}*/
ss.SzKey = padString(sName, 0)

// Create value
ss.Value = padString(sValue, true)
soFar += (len(ss.Value) + len(ss.Padding1))
ss.Padding2 = padBytes(4 - int(math.Mod(float64(soFar), 4)))
// Eliminate too much spacing
if len(ss.Padding2) == 4 {
ss.Padding2 = []byte{}
// Align to 32-bit boundary
soFar := 2
for (len(ss.SzKey)+6+soFar)%4 != 0 {
soFar += 2
}
ss.Padding = padBytes(soFar)
soFar += len(ss.SzKey)

// Align zeros to 32-bit boundary
zeros := 2
for (6+soFar+(len(padString(sValue, 0)))+zeros)%4 != 0 {
zeros += 2
}

// Length of text in words (2 bytes)
ss.WValueLength = uint16(len(ss.Value) / 2)
// This is NOT a good way because the copyright symbol counts as 2 letters
//ss.WValueLength = uint16(len(sValue) + 1)
// Create value
ss.Value = padString(sValue, zeros)

// 0 for binary, 1 for text
ss.WType = 0x01
// Length of text in words (2 bytes) plus zero terminate word
ss.WValueLength = uint16(len(padString(sValue, 0))/2) + 1

// Length of structure
ss.WLength = uint16(soFar)
// Don't include the padding in the length, but you must pass it back to
// the parent to be included
//ss.WLength = uint16(soFar + len(ss.Padding2))
//ss.WLength = 6 + uint16(soFar) + (ss.WValueLength * 2)
ss.WLength = uint16(6 + soFar + len(ss.Value))

return ss, true, uint16(len(ss.Padding2))
// Return the real size
return ss, true, uint16(6 + soFar + len(ss.Value))
}

return ss, false, 0
}

func buildStringTable(vi *VersionInfo) (VSStringTable, uint16) {
func buildStringTable(vi *VersionInfo) VSStringTable {
st := VSStringTable{}

// Always set to 0
Expand All @@ -163,26 +159,37 @@ func buildStringTable(vi *VersionInfo) (VSStringTable, uint16) {
st.WType = 0x01

// Language identifier and Code page
st.SzKey = padString(vi.VarFileInfo.Translation.getTranslationString(), false)
soFar := len(st.SzKey) + 6
st.Padding = padBytes(4 - int(math.Mod(float64(soFar), 4)))
st.SzKey = padString(vi.VarFileInfo.Translation.getTranslationString(), 0)

// Align to 32-bit boundary
soFar := 2
for (len(st.SzKey)+6+soFar)%4 != 0 {
soFar += 2
}
st.Padding = padBytes(soFar)
soFar += len(st.SzKey)

var sRealLength uint16
sRealLength = 0

// Loop through the struct fields
v := reflect.ValueOf(vi.StringFileInfo)
for i := 0; i < v.NumField(); i++ {
// If the struct is valid
if r, ok, extra := buildString(i, v); ok {
st.Children = append(st.Children, r)
st.WLength += (r.WLength + extra)
sRealLength += extra
// Don't use the returned size, it's not correct
//st.WLength += r.WLength
}
}

st.WLength += uint16(soFar)
st.WLength += 6 + uint16(soFar) + sRealLength

return st, uint16(len(st.Padding))
return st
}

func buildStringFileInfo(vi *VersionInfo) (VSStringFileInfo, uint16) {
func buildStringFileInfo(vi *VersionInfo) VSStringFileInfo {
sf := VSStringFileInfo{}

// Always set to 0
Expand All @@ -191,37 +198,50 @@ func buildStringFileInfo(vi *VersionInfo) (VSStringFileInfo, uint16) {
// 0 for binary, 1 for text
sf.WType = 0x01

sf.SzKey = padString("StringFileInfo", false)
soFar := len(sf.SzKey) + 6
sf.Padding = padBytes(4 - int(math.Mod(float64(soFar), 4)))
sf.SzKey = padString("StringFileInfo", 0)

// Align to 32-bit boundary
soFar := 2
for (len(sf.SzKey)+6+soFar)%4 != 0 {
soFar += 2
}
sf.Padding = padBytes(soFar)
soFar += len(sf.SzKey)

// Allows for more than one string table
st, extra := buildStringTable(vi)
st := buildStringTable(vi)
sf.Children = st
sf.WLength += (uint16(soFar) + uint16(len(sf.Padding)) + st.WLength)

return sf, extra
sf.WLength = 6 + uint16(soFar) + st.WLength

return sf
}

func buildVar(vfi VarFileInfo) VSVar {
vs := VSVar{}

// 0 for binary, 1 for text
vs.WType = 0x00

// Create key
vs.SzKey = padString("Translation", false)
soFar := len(vs.SzKey) + 6
vs.Padding = padBytes(4 - int(math.Mod(float64(soFar), 4)))
vs.SzKey = padString("Translation", 0)

// Align to 32-bit boundary
soFar := 2
for (len(vs.SzKey)+6+soFar)%4 != 0 {
soFar += 2
}
vs.Padding = padBytes(soFar)
soFar += len(vs.SzKey)

// Create value
vs.Value = str2Uint32(vfi.Translation.getTranslation())
soFar += (4 + len(vs.Padding))

// Length of text in bytes
vs.WValueLength = 4

// 0 for binary, 1 for text
vs.WType = 0x00

// Length of structure
vs.WLength = uint16(soFar)
vs.WLength = 6 + vs.WValueLength + uint16(soFar)

return vs
}
Expand All @@ -235,14 +255,20 @@ func buildVarFileInfo(vfi VarFileInfo) VSVarFileInfo {
// 0 for binary, 1 for text
vf.WType = 0x01

vf.SzKey = padString("VarFileInfo", false)
soFar := len(vf.SzKey) + 6
vf.Padding = padBytes(4 - int(math.Mod(float64(soFar), 4)))
vf.SzKey = padString("VarFileInfo", 0)

// Allows for more than one string table
// Align to 32-bit boundary
soFar := 2
for (len(vf.SzKey)+6+soFar)%4 != 0 {
soFar += 2
}
vf.Padding = padBytes(soFar)
soFar += len(vf.SzKey)

// TODO Allow for more than one string table
st := buildVar(vfi)
vf.Value = st
vf.WLength += (uint16(soFar) + uint16(len(vf.Padding)) + st.WLength)
vf.WLength = 6 + st.WLength + uint16(soFar)

return vf
}
Expand Down Expand Up @@ -278,28 +304,35 @@ func (v *VersionInfo) Build() {
// 0 for binary, 1 for text
vi.WType = 0x00

vi.SzKey = padString("VS_VERSION_INFO", false)
soFar := len(vi.SzKey) + 6
vi.Padding1 = padBytes(4 - int(math.Mod(float64(soFar), 4)))
vi.SzKey = padString("VS_VERSION_INFO", 0)

// Align to 32-bit boundary
// 6 is for the size of WLength, WValueLength, and WType (each is 1 word or 2 bytes: FF FF)
soFar := 2
for (len(vi.SzKey)+6+soFar)%4 != 0 {
soFar += 2
}
vi.Padding1 = padBytes(soFar)
soFar += len(vi.SzKey)

vi.Value = buildFixedFileInfo(v)

// Length of value (always the same)
// Length of VSFixedFileInfo (always the same)
vi.WValueLength = 0x34

// Never needs padding
// Never needs padding, not included in WLength
vi.Padding2 = []byte{}

// Build strings
sf, extraPadding := buildStringFileInfo(v)
sf := buildStringFileInfo(v)
vi.Children = sf

// Build translation
vf := buildVarFileInfo(v.VarFileInfo)
vi.Children2 = vf

// Calculate the total size
vi.WLength += (uint16(soFar) + uint16(len(vi.Padding1)) + vi.WValueLength + uint16(len(vi.Padding2)) + vi.Children.WLength + vi.Children2.WLength + extraPadding)
vi.WLength += 6 + uint16(soFar) + vi.WValueLength + sf.WLength + vi.Children2.WLength

v.Structure = vi
}
Binary file modified tests/cmd.hex
Binary file not shown.
Binary file modified tests/control.hex
Binary file not shown.
Binary file modified tests/explorer.hex
Binary file not shown.
Binary file added tests/simple.hex
Binary file not shown.
44 changes: 44 additions & 0 deletions tests/simple.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"FixedFileInfo":
{
"FileVersion": {
"Major": 1,
"Minor": 0,
"Patch": 0,
"Build": 0
},
"ProductVersion": {
"Major": 1,
"Minor": 0,
"Patch": 0,
"Build": 0
},
"FileFlagsMask": "3f",
"FileFlags ": "00",
"FileOS": "40004",
"FileType": "01",
"FileSubType": "00"
},
"StringFileInfo":
{
"Comments": "",
"CompanyName": "",
"FileDescription": "",
"FileVersion": "",
"InternalName": "",
"LegalCopyright": "",
"LegalTrademarks": "",
"OriginalFilename": "",
"PrivateBuild": "",
"ProductName": "",
"ProductVersion": "1.0",
"SpecialBuild": ""
},
"VarFileInfo":
{
"Translation": {
"LangID": "0409",
"CharsetID": "04B0"
}
}
}

0 comments on commit 379995a

Please sign in to comment.