diff --git a/fmp/fmp_chunk.go b/fmp/fmp_chunk.go index fb13298..c70832f 100644 --- a/fmp/fmp_chunk.go +++ b/fmp/fmp_chunk.go @@ -4,14 +4,6 @@ import ( "encoding/binary" ) -type FmpChunk struct { - Type FmpChunkType - Length uint32 - Key uint32 // If Type == FMP_CHUNK_SHORT_KEY_VALUE or FMP_CHUNK_LONG_KEY_VALUE - Index uint32 // Segment index, if Type == FMP_CHUNK_SEGMENTED_DATA - Value []byte -} - func (ctx *FmpFile) readChunk(payload []byte) (*FmpChunk, error) { // Simple data diff --git a/fmp/fmp_file.go b/fmp/fmp_file.go index d7d43ff..ea2af53 100644 --- a/fmp/fmp_file.go +++ b/fmp/fmp_file.go @@ -18,24 +18,6 @@ const ( sectorHeaderSize = 20 ) -type FmpFile struct { - VersionDate time.Time - CreatorName string - FileSize uint - NumSectors uint - Stream io.ReadSeeker - Sectors []*FmpSector - Chunks []*FmpChunk -} - -type FmpSector struct { - Deleted bool - Level uint8 - PrevSectorID uint32 - NextSectorID uint32 - Chunks []*FmpChunk -} - func OpenFile(path string) (*FmpFile, error) { info, err := os.Stat(path) if err != nil { @@ -48,7 +30,7 @@ func OpenFile(path string) (*FmpFile, error) { } defer stream.Close() - ctx := &FmpFile{Stream: stream} + ctx := &FmpFile{Stream: stream, Dictionary: &FmpDict{}} if err := ctx.readHeader(); err != nil { return nil, err } @@ -57,6 +39,8 @@ func OpenFile(path string) (*FmpFile, error) { ctx.NumSectors = ctx.FileSize / sectorSize ctx.Sectors = make([]*FmpSector, ctx.NumSectors) + currentPath := make([]uint16, 0) + for i := uint(0); i < ctx.NumSectors; i++ { sector, err := ctx.readSector() if err == io.EOF { @@ -67,6 +51,43 @@ func OpenFile(path string) (*FmpFile, error) { } ctx.Sectors[i] = sector ctx.Chunks = append(ctx.Chunks, sector.Chunks...) + + for _, chunk := range sector.Chunks { + switch chunk.Type { + case FMP_CHUNK_PATH_PUSH: + currentPath = append(currentPath, uint16(chunk.Value[0])) + + case FMP_CHUNK_PATH_POP: + if len(currentPath) > 0 { + currentPath = currentPath[:len(currentPath)-1] + } + + case FMP_CHUNK_SIMPLE_DATA: + ctx.Dictionary.set(currentPath, chunk.Value) + + case FMP_CHUNK_SEGMENTED_DATA: + // Todo: take index into account + ctx.Dictionary.set( + currentPath, + append(ctx.Dictionary.get(currentPath), chunk.Value...), + ) + + case FMP_CHUNK_SIMPLE_KEY_VALUE: + ctx.Dictionary.set( + append(currentPath, uint16(chunk.Key)), + chunk.Value, + ) + + case FMP_CHUNK_LONG_KEY_VALUE: + ctx.Dictionary.set( + append(currentPath, uint16(chunk.Key)), // todo: ?? + chunk.Value, + ) + + case FMP_CHUNK_NOOP: + // noop + } + } } return ctx, nil diff --git a/fmp/fmp_type.go b/fmp/fmp_type.go new file mode 100644 index 0000000..9436643 --- /dev/null +++ b/fmp/fmp_type.go @@ -0,0 +1,71 @@ +package fmp + +import ( + "io" + "time" +) + +type FmpFile struct { + VersionDate time.Time + CreatorName string + FileSize uint + NumSectors uint + Stream io.ReadSeeker + Sectors []*FmpSector + Chunks []*FmpChunk + Dictionary *FmpDict +} + +type FmpSector struct { + Deleted bool + Level uint8 + PrevSectorID uint32 + NextSectorID uint32 + Chunks []*FmpChunk +} + +type FmpChunk struct { + Type FmpChunkType + Length uint32 + Key uint32 // If Type == FMP_CHUNK_SHORT_KEY_VALUE or FMP_CHUNK_LONG_KEY_VALUE + Index uint32 // Segment index, if Type == FMP_CHUNK_SEGMENTED_DATA + Value []byte +} + +type FmpDict map[uint16]*FmpDictEntry + +type FmpDictEntry struct { + Value []byte + Children *FmpDict +} + +func (dict *FmpDict) get(path []uint16) []byte { + for i, key := range path { + _, ok := (*dict)[key] + if !ok { + (*dict)[key] = &FmpDictEntry{Children: &FmpDict{}} + } + + if i == len(path)-1 { + return (*dict)[key].Value + } else { + dict = (*dict)[key].Children + } + } + return nil +} + +func (dict *FmpDict) set(path []uint16, value []byte) { + for i, key := range path { + _, ok := (*dict)[key] + if !ok { + (*dict)[key] = &FmpDictEntry{Children: &FmpDict{}} + } + + if i == len(path)-1 { + (*dict)[key].Value = value + } else { + dict = (*dict)[key].Children + } + } +}