Skip to content

Instantly share code, notes, and snippets.

@yujp
Created October 31, 2020 07:22
Show Gist options
  • Save yujp/187b1d460332b69fd3c7d36263562a83 to your computer and use it in GitHub Desktop.
Save yujp/187b1d460332b69fd3c7d36263562a83 to your computer and use it in GitHub Desktop.
// 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