1
0
mirror of https://github.com/garraflavatra/go-fmp.git synced 2025-07-12 17:34:05 +00:00

Row parsing

This commit is contained in:
2025-06-22 18:43:06 +02:00
parent 7359962d98
commit d22b209ca5
5 changed files with 53 additions and 15 deletions

Binary file not shown.

View File

@ -3,6 +3,7 @@ package fmp
import ( import (
"fmt" "fmt"
"os" "os"
"slices"
) )
const debugging = false const debugging = false
@ -80,7 +81,15 @@ func (c *FmpChunk) String() string {
func (dict *FmpDict) string(parentPath string) string { func (dict *FmpDict) string(parentPath string) string {
s := "" s := ""
for k, v := range *dict { keys := make([]uint64, 0, len(*dict))
for k := range *dict {
keys = append(keys, k)
}
slices.Sort(keys)
for _, k := range keys {
v := (*dict)[k]
s += fmt.Sprintf("%v%v: %v\n", parentPath, k, string(v.Value)) s += fmt.Sprintf("%v%v: %v\n", parentPath, k, string(v.Value))
if v.Children != nil { if v.Children != nil {

View File

@ -3,11 +3,12 @@ package fmp
type FmpTable struct { type FmpTable struct {
ID uint64 ID uint64
Name string Name string
Columns []FmpColumn Columns map[uint64]FmpColumn
Records map[uint64]FmpRecord
} }
type FmpColumn struct { type FmpColumn struct {
ID uint64 Index uint64
Name string Name string
Type FmpFieldType Type FmpFieldType
DataType FmpDataType DataType FmpDataType
@ -17,6 +18,11 @@ type FmpColumn struct {
Indexed bool Indexed bool
} }
type FmpRecord struct {
Index uint64
Values map[uint64]string
}
func (ctx *FmpFile) Tables() []*FmpTable { func (ctx *FmpFile) Tables() []*FmpTable {
tables := make([]*FmpTable, 0) tables := make([]*FmpTable, 0)
ent := ctx.Dictionary.GetEntry(3, 16, 5) ent := ctx.Dictionary.GetEntry(3, 16, 5)
@ -28,19 +34,19 @@ func (ctx *FmpFile) Tables() []*FmpTable {
table := &FmpTable{ table := &FmpTable{
ID: path, ID: path,
Name: decodeByteSeq(tableEnt.Children.GetValue(16)), Name: decodeFmpString(tableEnt.Children.GetValue(16)),
Columns: make([]FmpColumn, 0), Columns: map[uint64]FmpColumn{},
Records: map[uint64]FmpRecord{},
} }
tables = append(tables, table) tables = append(tables, table)
colEnt := ctx.Dictionary.GetEntry(table.ID, 3, 5)
for colPath, colEnt := range *colEnt.Children { for colPath, colEnt := range *ctx.Dictionary.GetChildren(table.ID, 3, 5) {
name := decodeByteSeq(colEnt.Children.GetValue(16)) name := decodeFmpString(colEnt.Children.GetValue(16))
flags := colEnt.Children.GetValue(2) flags := colEnt.Children.GetValue(2)
column := FmpColumn{ column := FmpColumn{
ID: colPath, Index: colPath,
Name: name, Name: name,
Type: FmpFieldType(flags[0]), Type: FmpFieldType(flags[0]),
DataType: FmpDataType(flags[1]), DataType: FmpDataType(flags[1]),
@ -55,7 +61,16 @@ func (ctx *FmpFile) Tables() []*FmpTable {
column.AutoEnter = autoEnterOptionMap[flags[11]] column.AutoEnter = autoEnterOptionMap[flags[11]]
} }
table.Columns = append(table.Columns, column) table.Columns[column.Index] = column
}
for recPath, recEnt := range *ctx.Dictionary.GetChildren(table.ID, 5) {
record := FmpRecord{Index: recPath, Values: make(map[uint64]string)}
table.Records[record.Index] = record
for colIndex, value := range *recEnt.Children {
record.Values[colIndex] = decodeFmpString(value.Value)
}
} }
} }

View File

@ -7,11 +7,11 @@ func TestOpenFile(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if f.FileSize != 393216 { if f.FileSize != 229376 {
t.Errorf("expected file size to be 393216, got %d", f.FileSize) t.Errorf("expected file size to be 393216, got %d", f.FileSize)
} }
if f.numSectors != 95 { if f.numSectors != 55 {
t.Errorf("expected number of sectors to be 95, got %d", f.numSectors) t.Errorf("expected number of sectors to be 55, got %d", f.numSectors)
} }
if f.CreatorName != "Pro 12.0" { if f.CreatorName != "Pro 12.0" {
t.Errorf("expected application name to be 'Pro 12.0', got '%s'", f.CreatorName) t.Errorf("expected application name to be 'Pro 12.0', got '%s'", f.CreatorName)
@ -29,7 +29,7 @@ func TestTables(t *testing.T) {
} }
tables := f.Tables() tables := f.Tables()
expectedNames := []string{"Untitled", "WayDomains", "WayProcesses"} expectedNames := []string{"Untitled"}
tableNames := []string{} tableNames := []string{}
for _, table := range tables { for _, table := range tables {
tableNames = append(tableNames, table.Name) tableNames = append(tableNames, table.Name)
@ -67,4 +67,10 @@ func TestTables(t *testing.T) {
if field.AutoEnter != FmpAutoEnterCalculationReplacingExistingValue { if field.AutoEnter != FmpAutoEnterCalculationReplacingExistingValue {
t.Errorf("expected field to have auto enter calculation replacing existing value, but it does not") t.Errorf("expected field to have auto enter calculation replacing existing value, but it does not")
} }
if len(tables[0].Records) != 3 {
t.Errorf("expected table to have 3 records, but it has %d", len(tables[0].Records))
}
if tables[0].Records[1].Values[1] != "629FAA83-50D8-401F-A560-C8D45217D17B" {
t.Errorf("first record has an incorrect ID '%s'", tables[0].Records[0].Values[0])
}
} }

View File

@ -36,6 +36,14 @@ func (dict *FmpDict) GetValue(path ...uint64) []byte {
return nil return nil
} }
func (dict *FmpDict) GetChildren(path ...uint64) *FmpDict {
ent := dict.GetEntry(path...)
if ent != nil {
return ent.Children
}
return &FmpDict{}
}
func (dict *FmpDict) SetValue(path []uint64, value []byte) { func (dict *FmpDict) SetValue(path []uint64, value []byte) {
for i, key := range path { for i, key := range path {
_, ok := (*dict)[key] _, ok := (*dict)[key]
@ -61,7 +69,7 @@ func parseVarUint64(payload []byte) uint64 {
return length return length
} }
func decodeByteSeq(payload []byte) string { func decodeFmpString(payload []byte) string {
result := "" result := ""
for i := range payload { for i := range payload {
result += string(payload[i] ^ 0x5A) result += string(payload[i] ^ 0x5A)