mirror of https://gitee.com/godoos/godoos.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
4.2 KiB
148 lines
4.2 KiB
/*
|
|
* GodoOS - A lightweight cloud desktop
|
|
* Copyright (C) 2024 https://godoos.com
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package doc
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
|
|
"github.com/richardlehane/mscfb"
|
|
)
|
|
|
|
var (
|
|
errInvalidPrc = errors.New("Invalid Prc structure")
|
|
errInvalidClx = errors.New("expected last aCP value to equal fib.cpLength (2.8.35)")
|
|
errInvalidPcdt = errors.New("expected clxt to be equal 0x02")
|
|
)
|
|
|
|
type clx struct {
|
|
pcdt pcdt
|
|
}
|
|
|
|
type pcdt struct {
|
|
lcb int
|
|
PlcPcd plcPcd
|
|
}
|
|
|
|
type plcPcd struct {
|
|
aCP []int
|
|
aPcd []pcd
|
|
}
|
|
|
|
type pcd struct {
|
|
fc fcCompressed
|
|
}
|
|
|
|
type fcCompressed struct {
|
|
fc int
|
|
fCompressed bool
|
|
}
|
|
|
|
// read Clx (section 2.9.38)
|
|
func getClx(table *mscfb.File, fib *fib) (*clx, error) {
|
|
if table == nil || fib == nil {
|
|
return nil, errInvalidArgument
|
|
}
|
|
b, err := readClx(table, fib)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pcdtOffset, err := getPrcArrayEnd(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pcdt, err := getPcdt(b, pcdtOffset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if pcdt.PlcPcd.aCP[len(pcdt.PlcPcd.aCP)-1] != fib.fibRgLw.cpLength {
|
|
return nil, errInvalidClx
|
|
}
|
|
|
|
return &clx{pcdt: *pcdt}, nil
|
|
}
|
|
|
|
func readClx(table *mscfb.File, fib *fib) ([]byte, error) {
|
|
b := make([]byte, fib.fibRgFcLcb.lcbClx)
|
|
_, err := table.ReadAt(b, int64(fib.fibRgFcLcb.fcClx))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// read Pcdt from Clx (section 2.9.178)
|
|
func getPcdt(clx []byte, pcdtOffset int) (*pcdt, error) {
|
|
const pcdSize = 8
|
|
if clx[pcdtOffset] != 0x02 { // clxt must be 0x02 or invalid
|
|
return nil, errInvalidPcdt
|
|
}
|
|
lcb := int(binary.LittleEndian.Uint32(clx[pcdtOffset+1 : pcdtOffset+5])) // skip clxt, get lcb
|
|
plcPcdOffset := pcdtOffset + 5 // skip clxt and lcb
|
|
numPcds := (lcb - 4) / (4 + pcdSize) // see 2.2.2 in the spec for equation
|
|
numCps := numPcds + 1 // always 1 more cp than pcds
|
|
|
|
cps := make([]int, numCps)
|
|
for i := 0; i < numCps; i++ {
|
|
cpOffset := plcPcdOffset + i*4
|
|
cps[i] = int(binary.LittleEndian.Uint32(clx[cpOffset : cpOffset+4]))
|
|
}
|
|
|
|
pcdStart := plcPcdOffset + 4*numCps
|
|
pcds := make([]pcd, numPcds)
|
|
for i := 0; i < numPcds; i++ {
|
|
pcdOffset := pcdStart + i*pcdSize
|
|
pcds[i] = *parsePcd(clx[pcdOffset : pcdOffset+pcdSize])
|
|
}
|
|
return &pcdt{lcb: lcb, PlcPcd: plcPcd{aCP: cps, aPcd: pcds}}, nil
|
|
}
|
|
|
|
// find end of RgPrc array (section 2.9.38)
|
|
func getPrcArrayEnd(clx []byte) (int, error) {
|
|
prcOffset := 0
|
|
count := 0
|
|
for {
|
|
clxt := clx[prcOffset]
|
|
if clxt != 0x01 { // this is not a Prc, so exit
|
|
return prcOffset, nil
|
|
}
|
|
prcDataCbGrpprl := binary.LittleEndian.Uint16(clx[prcOffset+1 : prcOffset+3]) // skip the clxt and read 2 bytes
|
|
prcOffset += 1 + 2 + int(prcDataCbGrpprl) // skip clxt, cbGrpprl, and GrpPrl
|
|
|
|
if count > 10000 || prcDataCbGrpprl <= 0 || prcOffset+3 > len(clx) { // ensure no infinite loop
|
|
return 0, errInvalidPrc
|
|
}
|
|
count++
|
|
}
|
|
}
|
|
|
|
// parse Pcd (section 2.9.177)
|
|
func parsePcd(pcdData []byte) *pcd {
|
|
return &pcd{fc: *parseFcCompressed(pcdData[2:6])}
|
|
}
|
|
|
|
// parse FcCompressed (section 2.9.73)
|
|
func parseFcCompressed(fcData []byte) *fcCompressed {
|
|
fCompressed := fcData[3]&64 == 64 // check fcompressed value (second bit from lestmost of the last byte in fcdata)
|
|
fcData[3] = fcData[3] & 63 // clear the fcompressed value from data
|
|
fc := binary.LittleEndian.Uint32(fcData) // word doc generally uses little endian order (1.3.7)
|
|
return &fcCompressed{fc: int(fc), fCompressed: fCompressed}
|
|
}
|
|
|