Created
October 31, 2020 07:22
-
-
Save yujp/187b1d460332b69fd3c7d36263562a83 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// copyright (c) 2010 yujp | |
package main | |
import ( | |
"bytes" | |
"encoding/binary" | |
"encoding/json" | |
"flag" | |
"fmt" | |
"io" | |
"os" | |
"strings" | |
"github.com/richardlehane/mscfb" | |
"golang.org/x/text/encoding/unicode" | |
"golang.org/x/text/transform" | |
) | |
func main() { | |
var ( | |
debug bool | |
bodyPos int64 | |
) | |
flag.BoolVar(&debug, "d", false, "debug") | |
flag.Int64Var(&bodyPos, "p", 0, "start position of body (246?)") | |
flag.Parse() | |
args := flag.Args() | |
if len(args) < 1 { | |
fmt.Printf("\"%s\" <PathToFile>", os.Args[0]) | |
return | |
} | |
if debug { | |
fmt.Printf("bodyPos: %d\n==========\n", bodyPos) | |
} | |
// bin, err := ioutil.ReadFile("BasicFileInfo") | |
bin, err := func(inputPath string) ([]byte, error) { | |
file, _ := os.Open(inputPath) | |
defer file.Close() | |
doc, err := mscfb.New(file) | |
if err != nil { | |
return nil, err | |
} | |
for entry, err := doc.Next(); err == nil; entry, err = doc.Next() { | |
if strings.ToUpper(entry.Name) != "BASICFILEINFO" { | |
continue | |
} | |
buf := make([]byte, entry.Size) | |
_, err := doc.Read(buf) | |
return buf, err | |
} | |
return nil, nil | |
}(args[0]) | |
if err != nil { | |
panic(err) | |
} | |
br := bytes.NewReader(bin) | |
fi := NewBasicFileInfo(br, binary.LittleEndian) | |
_, err = fi.Skip(14) | |
if err != nil { | |
panic(err) | |
} | |
s, err := fi.ReadInt32SizeAndString() | |
if err != nil { | |
panic(err) | |
} | |
fi.Year = s | |
s, err = fi.ReadInt32SizeAndString() | |
if err != nil { | |
panic(err) | |
} | |
fi.Version = s | |
s, err = fi.ReadInt32SizeAndString() | |
if err != nil { | |
panic(err) | |
} | |
fi.Path = s | |
_, err = fi.Skip(5) | |
if err != nil { | |
panic(err) | |
} | |
s, err = fi.ReadInt32SizeAndString() | |
if err != nil { | |
panic(err) | |
} | |
fi.UID = s | |
if debug { | |
fmt.Printf("UID: %s\n", s) | |
} | |
s, err = fi.ReadInt32SizeAndString() | |
if err != nil { | |
panic(err) | |
} | |
fi.Localization = s | |
if debug { | |
fmt.Printf("Localization: %s\n", s) | |
} | |
_, err = fi.Skip(bodyPos) | |
if err != nil { | |
panic(err) | |
} | |
s, err = fi.ReadAllString() | |
if err != nil { | |
panic(err) | |
} | |
fi.Data = strings.TrimRight(s, "\r\n") | |
if debug { | |
fmt.Println("==========") | |
fmt.Printf("<Body>\n%s\n", fi.Data) | |
} | |
dct := map[string]string{} | |
for _, block := range bytes.Split([]byte(fi.Data), []byte{0}) { | |
for _, line := range strings.Split(string(block), "\r\n") { | |
pair := strings.SplitN(line, ":", 2) | |
if len(pair) != 2 { | |
continue | |
} | |
dct[pair[0]] = strings.TrimLeft(pair[1], " ") | |
} | |
} | |
fi.Properties = dct | |
if debug { | |
fmt.Println("==========") | |
fmt.Println("<Parsed>") | |
for k, v := range dct { | |
fmt.Printf("[%s] %s\n", k, v) | |
} | |
} | |
var bb bytes.Buffer | |
je := json.NewEncoder(&bb) | |
je.SetIndent("", " ") | |
err = je.Encode(fi) | |
if err != nil { | |
panic(err) | |
} | |
if debug { | |
fmt.Println("==========") | |
} | |
fmt.Print(bb.String()) | |
} | |
// BasicFileInfo struct | |
type BasicFileInfo struct { | |
Year string `json:"year"` | |
Version string `json:"version"` | |
Path string `json:"path"` | |
Data string `json:"data"` | |
UID string `json:"uid"` | |
Localization string `json:"localization"` | |
Properties map[string]string `json:"properties"` | |
r io.ReadSeeker | |
bbo binary.ByteOrder | |
} | |
// NewBasicFileInfo func | |
func NewBasicFileInfo(r io.ReadSeeker, bo binary.ByteOrder) *BasicFileInfo { | |
return &BasicFileInfo{ | |
r: r, | |
bbo: bo, | |
} | |
} | |
// Skip method | |
func (me *BasicFileInfo) Skip(size int64) (int64, error) { | |
return me.r.Seek(size, io.SeekCurrent) | |
} | |
// ReadInt32 method | |
func (me *BasicFileInfo) ReadInt32() (int32, error) { | |
var i int32 | |
err := binary.Read(me.r, me.bbo, &i) | |
return i, err | |
} | |
// ReadBytes method | |
func (me *BasicFileInfo) ReadBytes(size int32) ([]byte, error) { | |
b := make([]byte, size*2) | |
err := binary.Read(me.r, me.bbo, &b) | |
return b, err | |
} | |
// ReadString method | |
func (me *BasicFileInfo) ReadString(size int32) (string, error) { | |
b := make([]byte, size*2) | |
err := binary.Read(me.r, me.bbo, &b) | |
if err != nil { | |
return "", err | |
} | |
return me.u16leToS(b) | |
} | |
// ReadAllString method | |
func (me *BasicFileInfo) ReadAllString() (string, error) { | |
pos, err := me.r.Seek(0, io.SeekCurrent) | |
if err != nil { | |
return "", err | |
} | |
end, err := me.r.Seek(0, io.SeekEnd) | |
if err != nil { | |
return "", err | |
} | |
_, err = me.r.Seek(pos, io.SeekStart) | |
if err != nil { | |
return "", err | |
} | |
b := make([]byte, end-pos) | |
err = binary.Read(me.r, me.bbo, &b) | |
if err != nil { | |
return "", err | |
} | |
return me.u16leToS(b) | |
} | |
func (me *BasicFileInfo) u16leToS(b []byte) (string, error) { | |
u16le := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) | |
u16bo := unicode.BOMOverride(u16le.NewDecoder()) | |
b2, _, err := transform.Bytes(u16bo, b) | |
if err != nil { | |
return "", err | |
} | |
return string(b2), err | |
} | |
// ReadInt32SizeAndBytes method | |
func (me *BasicFileInfo) ReadInt32SizeAndBytes() ([]byte, error) { | |
i, err := me.ReadInt32() | |
if err != nil { | |
return nil, err | |
} | |
return me.ReadBytes(i) | |
} | |
// ReadInt32SizeAndString method | |
func (me *BasicFileInfo) ReadInt32SizeAndString() (string, error) { | |
i, err := me.ReadInt32() | |
if err != nil { | |
return "", err | |
} | |
return me.ReadString(i) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment