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:
Binary file not shown.
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Reference in New Issue
Block a user