mirror of
https://github.com/garraflavatra/go-fmp.git
synced 2025-06-27 20:15:11 +00:00
Compare commits
3 Commits
bd871b6457
...
10285084eb
Author | SHA1 | Date | |
---|---|---|---|
10285084eb | |||
53134a0d09 | |||
cc8a602b51 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.23.0'
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
269
fmp/fmp_const.go
269
fmp/fmp_const.go
@ -1,7 +1,9 @@
|
||||
package fmp
|
||||
|
||||
type FmpError string
|
||||
type FmpChunkType uint8
|
||||
type FmpCollation uint8
|
||||
type FmpScriptStepType uint8
|
||||
type FmpError string
|
||||
|
||||
func (e FmpError) Error() string { return string(e) }
|
||||
|
||||
@ -16,42 +18,237 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
FMP_CHUNK_SIMPLE_DATA FmpChunkType = iota
|
||||
FMP_CHUNK_SEGMENTED_DATA
|
||||
FMP_CHUNK_SIMPLE_KEY_VALUE
|
||||
FMP_CHUNK_LONG_KEY_VALUE
|
||||
FMP_CHUNK_PATH_PUSH
|
||||
FMP_CHUNK_PATH_PUSH_LONG
|
||||
FMP_CHUNK_PATH_POP
|
||||
FMP_CHUNK_NOOP
|
||||
FmpChunkSimpleData FmpChunkType = iota
|
||||
FmpChunkSegmentedData FmpChunkType = iota
|
||||
FmpChunkSimpleKeyValue FmpChunkType = iota
|
||||
FmpChunkLongKeyValue FmpChunkType = iota
|
||||
FmpChunkPathPush FmpChunkType = iota
|
||||
FmpChunkPathPushLong FmpChunkType = iota
|
||||
FmpChunkPathPop FmpChunkType = iota
|
||||
FmpChunkNoop FmpChunkType = iota
|
||||
)
|
||||
|
||||
const (
|
||||
FMP_COLLATION_ENGLISH = 0x00
|
||||
FMP_COLLATION_FRENCH = 0x01
|
||||
FMP_COLLATION_GERMAN = 0x03
|
||||
FMP_COLLATION_ITALIAN = 0x04
|
||||
FMP_COLLATION_DUTCH = 0x05
|
||||
FMP_COLLATION_SWEDISH = 0x07
|
||||
FMP_COLLATION_SPANISH = 0x08
|
||||
FMP_COLLATION_DANISH = 0x09
|
||||
FMP_COLLATION_PORTUGUESE = 0x0A
|
||||
FMP_COLLATION_NORWEGIAN = 0x0C
|
||||
FMP_COLLATION_FINNISH = 0x11
|
||||
FMP_COLLATION_GREEK = 0x14
|
||||
FMP_COLLATION_ICELANDIC = 0x15
|
||||
FMP_COLLATION_TURKISH = 0x18
|
||||
FMP_COLLATION_ROMANIAN = 0x27
|
||||
FMP_COLLATION_POLISH = 0x2a
|
||||
FMP_COLLATION_HUNGARIAN = 0x2b
|
||||
FMP_COLLATION_RUSSIAN = 0x31
|
||||
FMP_COLLATION_CZECH = 0x38
|
||||
FMP_COLLATION_UKRAINIAN = 0x3e
|
||||
FMP_COLLATION_CROATIAN = 0x42
|
||||
FMP_COLLATION_CATALAN = 0x49
|
||||
FMP_COLLATION_FINNISH_ALT = 0x62
|
||||
FMP_COLLATION_SWEDISH_ALT = 0x63
|
||||
FMP_COLLATION_GERMAN_ALT = 0x64
|
||||
FMP_COLLATION_SPANISH_ALT = 0x65
|
||||
FMP_COLLATION_ASCII = 0x66
|
||||
FmpCollationEnglish FmpCollation = 0x00
|
||||
FmpCollationFrench FmpCollation = 0x01
|
||||
FmpCollationGerman FmpCollation = 0x03
|
||||
FmpCollationItalian FmpCollation = 0x04
|
||||
FmpCollationDutch FmpCollation = 0x05
|
||||
FmpCollationSwedish FmpCollation = 0x07
|
||||
FmpCollationSpanish FmpCollation = 0x08
|
||||
FmpCollationDanish FmpCollation = 0x09
|
||||
FmpCollationPortuguese FmpCollation = 0x0A
|
||||
FmpCollationNorwegian FmpCollation = 0x0C
|
||||
FmpCollationFinnish FmpCollation = 0x11
|
||||
FmpCollationGreek FmpCollation = 0x14
|
||||
FmpCollationIcelandic FmpCollation = 0x15
|
||||
FmpCollationTurkish FmpCollation = 0x18
|
||||
FmpCollationRomanian FmpCollation = 0x27
|
||||
FmpCollationPolish FmpCollation = 0x2a
|
||||
FmpCollationHungarian FmpCollation = 0x2b
|
||||
FmpCollationRussian FmpCollation = 0x31
|
||||
FmpCollationCzech FmpCollation = 0x38
|
||||
FmpCollationUkrainian FmpCollation = 0x3e
|
||||
FmpCollationCroatian FmpCollation = 0x42
|
||||
FmpCollationCatalan FmpCollation = 0x49
|
||||
FmpCollationFinnishAlt FmpCollation = 0x62
|
||||
FmpCollationSwedishAlt FmpCollation = 0x63
|
||||
FmpCollationGermanAlt FmpCollation = 0x64
|
||||
FmpCollationSpanishAlt FmpCollation = 0x65
|
||||
FmpCollationAscii FmpCollation = 0x66
|
||||
)
|
||||
|
||||
const (
|
||||
FmpScriptPerformScript FmpScriptStepType = 1
|
||||
FmpScriptSaveACopyAsXml FmpScriptStepType = 3
|
||||
FmpScriptGoToNextField FmpScriptStepType = 4
|
||||
FmpScriptGoToPreviousField FmpScriptStepType = 5
|
||||
FmpScriptGoToLayout FmpScriptStepType = 6
|
||||
FmpScriptNewRecordRequest FmpScriptStepType = 7
|
||||
FmpScriptDuplicateRecordRequest FmpScriptStepType = 8
|
||||
FmpScriptDeleteRecordRequest FmpScriptStepType = 9
|
||||
FmpScriptDeleteAllRecords FmpScriptStepType = 10
|
||||
FmpScriptInsertFromIndex FmpScriptStepType = 11
|
||||
FmpScriptInsertFromLastVisited FmpScriptStepType = 12
|
||||
FmpScriptInsertCurrentDate FmpScriptStepType = 13
|
||||
FmpScriptInsertCurrentTime FmpScriptStepType = 14
|
||||
FmpScriptGoToRecordRequestPage FmpScriptStepType = 16
|
||||
FmpScriptGoToField FmpScriptStepType = 17
|
||||
FmpScriptCheckSelection FmpScriptStepType = 18
|
||||
FmpScriptCheckRecord FmpScriptStepType = 19
|
||||
FmpScriptCheckFoundSet FmpScriptStepType = 20
|
||||
FmpScriptUnsortRecords FmpScriptStepType = 21
|
||||
FmpScriptEnterFindMode FmpScriptStepType = 22
|
||||
FmpScriptShowAllRecords FmpScriptStepType = 23
|
||||
FmpScriptModifyLastFind FmpScriptStepType = 24
|
||||
FmpScriptOmitRecord FmpScriptStepType = 25
|
||||
FmpScriptOmitMultipleRecords FmpScriptStepType = 26
|
||||
FmpScriptShowOmmitedOnly FmpScriptStepType = 27
|
||||
FmpScriptPerformFind FmpScriptStepType = 28
|
||||
FmpScriptShowHideToolbars FmpScriptStepType = 29
|
||||
FmpScriptViewAs FmpScriptStepType = 30
|
||||
FmpScriptAdjustWindow FmpScriptStepType = 31
|
||||
FmpScriptOpenHelp FmpScriptStepType = 32
|
||||
FmpScriptOpenFile FmpScriptStepType = 33
|
||||
FmpScriptCloseFile FmpScriptStepType = 34
|
||||
FmpScriptImportRecords FmpScriptStepType = 35
|
||||
FmpScriptExportRecords FmpScriptStepType = 36
|
||||
FmpScriptSaveACopyAs FmpScriptStepType = 37
|
||||
FmpScriptOpenManageDatabase FmpScriptStepType = 38
|
||||
FmpScriptSortRecords FmpScriptStepType = 39
|
||||
FmpScriptRelookupFieldContents FmpScriptStepType = 40
|
||||
FmpScriptEnterPreviewMode FmpScriptStepType = 41
|
||||
FmpScriptPrintSetup FmpScriptStepType = 42
|
||||
FmpScriptPrint FmpScriptStepType = 43
|
||||
FmpScriptExitApplication FmpScriptStepType = 44
|
||||
FmpScriptUndoRedo FmpScriptStepType = 45
|
||||
FmpScriptCut FmpScriptStepType = 46
|
||||
FmpScriptCopy FmpScriptStepType = 47
|
||||
FmpScriptPaste FmpScriptStepType = 48
|
||||
FmpScriptClear FmpScriptStepType = 49
|
||||
FmpScriptSelectAll FmpScriptStepType = 50
|
||||
FmpScriptRevertRecordRequest FmpScriptStepType = 51
|
||||
FmpScriptEnterBrowserMode FmpScriptStepType = 55
|
||||
FmpScriptInsertPicture FmpScriptStepType = 56
|
||||
FmpScriptSendEvent FmpScriptStepType = 57
|
||||
FmpScriptInsertCurrentUserName FmpScriptStepType = 60
|
||||
FmpScriptInsertText FmpScriptStepType = 61
|
||||
FmpScriptPauseResumeScript FmpScriptStepType = 62
|
||||
FmpScriptSendMail FmpScriptStepType = 63
|
||||
FmpScriptSendDdeExecute FmpScriptStepType = 64
|
||||
FmpScriptDialPhone FmpScriptStepType = 65
|
||||
FmpScriptSpeak FmpScriptStepType = 66
|
||||
FmpScriptPerformApplescript FmpScriptStepType = 67
|
||||
FmpScriptIf FmpScriptStepType = 68
|
||||
FmpScriptElse FmpScriptStepType = 69
|
||||
FmpScriptEndIf FmpScriptStepType = 70
|
||||
FmpScriptLoop FmpScriptStepType = 71
|
||||
FmpScriptExitLoopIf FmpScriptStepType = 72
|
||||
FmpScriptEndLoop FmpScriptStepType = 73
|
||||
FmpScriptGoToRelatedRecord FmpScriptStepType = 74
|
||||
FmpScriptCommitRecordsRequests FmpScriptStepType = 75
|
||||
FmpScriptSetField FmpScriptStepType = 76
|
||||
FmpScriptInsertCalculatedResult FmpScriptStepType = 77
|
||||
FmpScriptFreezeWindow FmpScriptStepType = 79
|
||||
FmpScriptRefreshWindow FmpScriptStepType = 80
|
||||
FmpScriptScrollWindow FmpScriptStepType = 81
|
||||
FmpScriptNewFile FmpScriptStepType = 82
|
||||
FmpScriptChangePassword FmpScriptStepType = 83
|
||||
FmpScriptSetMultiUser FmpScriptStepType = 84
|
||||
FmpScriptAllowUserAbort FmpScriptStepType = 85
|
||||
FmpScriptSetErrorCapture FmpScriptStepType = 86
|
||||
FmpScriptShowCustomDialog FmpScriptStepType = 87
|
||||
FmpScriptOpenScriptWorkspace FmpScriptStepType = 88
|
||||
FmpScriptBlankLineComment FmpScriptStepType = 89
|
||||
FmpScriptHaltScript FmpScriptStepType = 90
|
||||
FmpScriptReplaceFieldContents FmpScriptStepType = 91
|
||||
FmpScriptShowHideTextRuler FmpScriptStepType = 92
|
||||
FmpScriptBeep FmpScriptStepType = 93
|
||||
FmpScriptSetUseSystemFormats FmpScriptStepType = 94
|
||||
FmpScriptRecoverFile FmpScriptStepType = 95
|
||||
FmpScriptSaveACopyAsAddOnPackage FmpScriptStepType = 96
|
||||
FmpScriptSetZoomLevel FmpScriptStepType = 97
|
||||
FmpScriptCopyAllRecordsRequests FmpScriptStepType = 98
|
||||
FmpScriptGoToPortalRow FmpScriptStepType = 99
|
||||
FmpScriptCopyRecordRequest FmpScriptStepType = 101
|
||||
FmpScriptFluchCacheToDisk FmpScriptStepType = 102
|
||||
FmpScriptExitScript FmpScriptStepType = 103
|
||||
FmpScriptDeletePortalRow FmpScriptStepType = 104
|
||||
FmpScriptOpenPreferences FmpScriptStepType = 105
|
||||
FmpScriptCorrectWord FmpScriptStepType = 106
|
||||
FmpScriptSpellingOptions FmpScriptStepType = 107
|
||||
FmpScriptSelectDictionaries FmpScriptStepType = 108
|
||||
FmpScriptEditUserDictionary FmpScriptStepType = 109
|
||||
FmpScriptOpenUrl FmpScriptStepType = 111
|
||||
FmpScriptOpenManageValueLists FmpScriptStepType = 112
|
||||
FmpScriptOpenSharing FmpScriptStepType = 113
|
||||
FmpScriptOpenFileOptions FmpScriptStepType = 114
|
||||
FmpScriptAllowFormattingBar FmpScriptStepType = 115
|
||||
FmpScriptSetNextSerialValue FmpScriptStepType = 116
|
||||
FmpScriptExecuteSql FmpScriptStepType = 117
|
||||
FmpScriptOpenHosts FmpScriptStepType = 118
|
||||
FmpScriptMoveResizeWindow FmpScriptStepType = 119
|
||||
FmpScriptArrangeAllWindows FmpScriptStepType = 120
|
||||
FmpScriptCloseWindow FmpScriptStepType = 121
|
||||
FmpScriptNewWindow FmpScriptStepType = 122
|
||||
FmpScriptSelectWindow FmpScriptStepType = 123
|
||||
FmpScriptSetWindowTitle FmpScriptStepType = 124
|
||||
FmpScriptElseIf FmpScriptStepType = 125
|
||||
FmpScriptConstrainFoundSet FmpScriptStepType = 126
|
||||
FmpScriptExtendFoundSet FmpScriptStepType = 127
|
||||
FmpScriptPerformFindReplace FmpScriptStepType = 128
|
||||
FmpScriptOpenFindReplace FmpScriptStepType = 129
|
||||
FmpScriptSetSelection FmpScriptStepType = 130
|
||||
FmpScriptInsertFile FmpScriptStepType = 131
|
||||
FmpScriptExportFieldContents FmpScriptStepType = 132
|
||||
FmpScriptOpenRecordRequest FmpScriptStepType = 133
|
||||
FmpScriptAddAccount FmpScriptStepType = 134
|
||||
FmpScriptDeleteAccount FmpScriptStepType = 135
|
||||
FmpScriptResetAccountPassword FmpScriptStepType = 136
|
||||
FmpScriptEnableAccount FmpScriptStepType = 137
|
||||
FmpScriptRelogin FmpScriptStepType = 138
|
||||
FmpScriptConvertFile FmpScriptStepType = 139
|
||||
FmpScriptOpenManageDataSources FmpScriptStepType = 140
|
||||
FmpScriptSetVariable FmpScriptStepType = 141
|
||||
FmpScriptInstallMenuSet FmpScriptStepType = 142
|
||||
FmpScriptSaveRecordsAsExcel FmpScriptStepType = 143
|
||||
FmpScriptSaveRecordsAsPdf FmpScriptStepType = 144
|
||||
FmpScriptGoToObject FmpScriptStepType = 145
|
||||
FmpScriptSetWebViewer FmpScriptStepType = 146
|
||||
FmpScriptSetFieldByName FmpScriptStepType = 147
|
||||
FmpScriptInstallOntimerScript FmpScriptStepType = 148
|
||||
FmpScriptOpenEditSavedFinds FmpScriptStepType = 149
|
||||
FmpScriptPerformQuickFind FmpScriptStepType = 150
|
||||
FmpScriptOpenManageLayouts FmpScriptStepType = 151
|
||||
FmpScriptSaveRecordsAsSnapshotLink FmpScriptStepType = 152
|
||||
FmpScriptSortRecordsByField FmpScriptStepType = 154
|
||||
FmpScriptFindMatchingRecords FmpScriptStepType = 155
|
||||
FmpScriptManageContainers FmpScriptStepType = 156
|
||||
FmpScriptInstallPluginFile FmpScriptStepType = 157
|
||||
FmpScriptInsertPdf FmpScriptStepType = 158
|
||||
FmpScriptInsertAudioVideo FmpScriptStepType = 159
|
||||
FmpScriptInsertFromUrl FmpScriptStepType = 160
|
||||
FmpScriptInsertFromDevice FmpScriptStepType = 161
|
||||
FmpScriptPerformScriptOnServer FmpScriptStepType = 164
|
||||
FmpScriptOpenManageThemes FmpScriptStepType = 165
|
||||
FmpScriptShowHideMenubar FmpScriptStepType = 166
|
||||
FmpScriptRefreshObject FmpScriptStepType = 167
|
||||
FmpScriptSetLayoutObjectAnimation FmpScriptStepType = 168
|
||||
FmpScriptClosePopover FmpScriptStepType = 169
|
||||
FmpScriptOpenUploadToHost FmpScriptStepType = 172
|
||||
FmpScriptEnableTouchKeyboard FmpScriptStepType = 174
|
||||
FmpScriptPerformJavascriptInWebViewer FmpScriptStepType = 175
|
||||
FmpScriptCommentedOut FmpScriptStepType = 176
|
||||
FmpScriptAvplayerPlay FmpScriptStepType = 177
|
||||
FmpScriptAvplayerSetPlaybackState FmpScriptStepType = 178
|
||||
FmpScriptAvplayerSetOptions FmpScriptStepType = 179
|
||||
FmpScriptRefreshPortal FmpScriptStepType = 180
|
||||
FmpScriptGetFolderPath FmpScriptStepType = 181
|
||||
FmpScriptTruncateTable FmpScriptStepType = 182
|
||||
FmpScriptOpenFavorites FmpScriptStepType = 183
|
||||
FmpScriptConfigureRegionMonitorScript FmpScriptStepType = 185
|
||||
FmpScriptConfigureLocalNotification FmpScriptStepType = 187
|
||||
FmpScriptGetFileExists FmpScriptStepType = 188
|
||||
FmpScriptGetFileSize FmpScriptStepType = 189
|
||||
FmpScriptCreateDataFile FmpScriptStepType = 190
|
||||
FmpScriptOpenDataFile FmpScriptStepType = 191
|
||||
FmpScriptWriteToDataFile FmpScriptStepType = 192
|
||||
FmpScriptReadFromDataFile FmpScriptStepType = 193
|
||||
FmpScriptGetDataFilePosition FmpScriptStepType = 194
|
||||
FmpScriptSetDataFilePosition FmpScriptStepType = 195
|
||||
FmpScriptCloseDataFile FmpScriptStepType = 196
|
||||
FmpScriptDeleteFile FmpScriptStepType = 197
|
||||
FmpScriptRenameFile FmpScriptStepType = 199
|
||||
FmpScriptSetErrorLogging FmpScriptStepType = 200
|
||||
FmpScriptConfigureNfcReading FmpScriptStepType = 201
|
||||
FmpScriptConfigureMachineLearningModel FmpScriptStepType = 202
|
||||
FmpScriptExecuteFilemakerDataApi FmpScriptStepType = 203
|
||||
FmpScriptOpenTransaction FmpScriptStepType = 205
|
||||
FmpScriptCommitTransaction FmpScriptStepType = 206
|
||||
FmpScriptRevertTransaction FmpScriptStepType = 207
|
||||
FmpScriptSetSessionIdentifier FmpScriptStepType = 208
|
||||
FmpScriptSetDictionary FmpScriptStepType = 209
|
||||
FmpScriptPerformScriptOnServerWithCallback FmpScriptStepType = 210
|
||||
FmpScriptTriggerClarisConnectFlow FmpScriptStepType = 211
|
||||
FmpScriptAssert FmpScriptStepType = 255
|
||||
)
|
||||
|
@ -9,6 +9,20 @@ func debug(str string, args ...interface{}) {
|
||||
// fmt.Printf(str+"\n", args...)
|
||||
}
|
||||
|
||||
func dump(data []byte) {
|
||||
for _, b := range data {
|
||||
fmt.Printf("%02x ", b)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func dumpPath(path []uint64) {
|
||||
for _, p := range path {
|
||||
fmt.Printf("%v. ", p)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func (f *FmpFile) ToDebugFile(fname string) {
|
||||
f_sectors, err := os.Create(fname + ".sectors")
|
||||
if err != nil {
|
||||
|
@ -20,6 +20,20 @@ const (
|
||||
hbamSize = len(hbamSequence)
|
||||
)
|
||||
|
||||
type FmpFile struct {
|
||||
VersionDate time.Time
|
||||
CreatorName string
|
||||
FileSize uint
|
||||
Sectors []*FmpSector
|
||||
Chunks []*FmpChunk
|
||||
Dictionary *FmpDict
|
||||
|
||||
numSectors uint64 // Excludes the header sector
|
||||
currentSectorID uint64
|
||||
|
||||
stream io.ReadSeeker
|
||||
}
|
||||
|
||||
func OpenFile(path string) (*FmpFile, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
|
@ -1,10 +1,24 @@
|
||||
package fmp
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
type FmpSector struct {
|
||||
ID uint64
|
||||
Level uint8
|
||||
Deleted bool
|
||||
PrevID uint64
|
||||
NextID uint64
|
||||
Prev *FmpSector
|
||||
Next *FmpSector
|
||||
Payload []byte
|
||||
Chunks []*FmpChunk
|
||||
}
|
||||
|
||||
type FmpChunk struct {
|
||||
Type FmpChunkType
|
||||
Length uint64
|
||||
Key uint64 // If Type == FMP_CHUNK_SHORT_KEY_VALUE or FMP_CHUNK_LONG_KEY_VALUE
|
||||
Index uint64 // Segment index, if Type == FMP_CHUNK_SEGMENTED_DATA
|
||||
Value []byte
|
||||
}
|
||||
|
||||
func (sect *FmpSector) readChunks() error {
|
||||
if len(sect.Chunks) > 0 {
|
||||
@ -24,12 +38,9 @@ func (sect *FmpSector) readChunks() error {
|
||||
debug("0x%02x (pos %v, type %v)\n", sect.Payload[0], pos, int(chunk.Type))
|
||||
}
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
debug("chunk error at sector %d", sect.ID)
|
||||
debug(hex.EncodeToString(sect.Payload))
|
||||
dump(sect.Payload)
|
||||
return err
|
||||
}
|
||||
if chunk == nil {
|
||||
@ -58,43 +69,38 @@ func (sect *FmpSector) processChunks(dict *FmpDict) error {
|
||||
currentPath := make([]uint64, 0)
|
||||
for _, chunk := range sect.Chunks {
|
||||
switch chunk.Type {
|
||||
case FMP_CHUNK_PATH_PUSH, FMP_CHUNK_PATH_PUSH_LONG:
|
||||
case FmpChunkPathPush, FmpChunkPathPushLong:
|
||||
currentPath = append(currentPath, parseVarUint64(chunk.Value))
|
||||
dumpPath(currentPath)
|
||||
|
||||
s := ""
|
||||
for _, ent := range currentPath {
|
||||
s += fmt.Sprintf("%v. ", ent)
|
||||
}
|
||||
debug("path: %s", s)
|
||||
|
||||
case FMP_CHUNK_PATH_POP:
|
||||
case FmpChunkPathPop:
|
||||
if len(currentPath) > 0 {
|
||||
currentPath = (currentPath)[:len(currentPath)-1]
|
||||
}
|
||||
|
||||
case FMP_CHUNK_SIMPLE_DATA:
|
||||
case FmpChunkSimpleData:
|
||||
dict.SetValue(currentPath, chunk.Value)
|
||||
|
||||
case FMP_CHUNK_SEGMENTED_DATA:
|
||||
case FmpChunkSegmentedData:
|
||||
// Todo: take index into account
|
||||
dict.SetValue(
|
||||
currentPath,
|
||||
append(dict.GetValue(currentPath), chunk.Value...),
|
||||
)
|
||||
|
||||
case FMP_CHUNK_SIMPLE_KEY_VALUE:
|
||||
case FmpChunkSimpleKeyValue:
|
||||
dict.SetValue(
|
||||
append(currentPath, uint64(chunk.Key)),
|
||||
chunk.Value,
|
||||
)
|
||||
|
||||
case FMP_CHUNK_LONG_KEY_VALUE:
|
||||
case FmpChunkLongKeyValue:
|
||||
dict.SetValue(
|
||||
append(currentPath, uint64(chunk.Key)), // todo: ??
|
||||
chunk.Value,
|
||||
)
|
||||
|
||||
case FMP_CHUNK_NOOP:
|
||||
case FmpChunkNoop:
|
||||
// noop
|
||||
}
|
||||
}
|
||||
@ -112,55 +118,55 @@ func (sect *FmpSector) readChunk(payload []byte) (*FmpChunk, error) {
|
||||
switch chunkCode {
|
||||
case 0x00:
|
||||
chunk.Length = 2
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_DATA
|
||||
chunk.Type = FmpChunkSimpleData
|
||||
chunk.Value = payload[1:chunk.Length]
|
||||
|
||||
case 0x01, 0x02, 0x03, 0x04, 0x05:
|
||||
chunk.Length = 2 + 2*uint64(chunkCode-0x01) + addIf(chunkCode == 0x01, 1)
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_KEY_VALUE
|
||||
chunk.Type = FmpChunkSimpleKeyValue
|
||||
chunk.Key = uint64(payload[1])
|
||||
chunk.Value = payload[2:chunk.Length]
|
||||
|
||||
case 0x06:
|
||||
chunk.Length = 3 + uint64(payload[2])
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_KEY_VALUE
|
||||
chunk.Type = FmpChunkSimpleKeyValue
|
||||
chunk.Key = uint64(payload[1])
|
||||
chunk.Value = payload[3:chunk.Length]
|
||||
|
||||
case 0x07:
|
||||
valueLength := parseVarUint64(payload[2 : 2+2])
|
||||
chunk.Length = min(4+valueLength, uint64(len(payload)))
|
||||
chunk.Type = FMP_CHUNK_SEGMENTED_DATA
|
||||
chunk.Type = FmpChunkSegmentedData
|
||||
chunk.Index = uint64(payload[1])
|
||||
chunk.Value = payload[4:chunk.Length]
|
||||
|
||||
case 0x08:
|
||||
chunk.Length = 3
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_DATA
|
||||
chunk.Type = FmpChunkSimpleData
|
||||
chunk.Value = payload[1:chunk.Length]
|
||||
|
||||
case 0x09:
|
||||
chunk.Length = 4
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_KEY_VALUE
|
||||
chunk.Type = FmpChunkSimpleKeyValue
|
||||
chunk.Key = parseVarUint64(payload[1 : 1+2])
|
||||
chunk.Value = payload[3:chunk.Length]
|
||||
|
||||
case 0x0A, 0x0B, 0x0C, 0x0D:
|
||||
chunk.Length = 3 + 2*uint64(chunkCode-0x09)
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_KEY_VALUE
|
||||
chunk.Type = FmpChunkSimpleKeyValue
|
||||
chunk.Key = parseVarUint64(payload[1 : 1+2])
|
||||
chunk.Value = payload[3:chunk.Length]
|
||||
|
||||
case 0x0E:
|
||||
if payload[1] == 0xFF {
|
||||
chunk.Length = 7
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_DATA
|
||||
chunk.Type = FmpChunkSimpleData
|
||||
chunk.Value = payload[2:chunk.Length]
|
||||
break
|
||||
}
|
||||
|
||||
chunk.Length = 4 + uint64(payload[3])
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_KEY_VALUE
|
||||
chunk.Type = FmpChunkSimpleKeyValue
|
||||
chunk.Key = parseVarUint64(payload[1 : 1+2])
|
||||
chunk.Value = payload[4:chunk.Length]
|
||||
|
||||
@ -170,43 +176,43 @@ func (sect *FmpSector) readChunk(payload []byte) (*FmpChunk, error) {
|
||||
if chunk.Length > 5+valueLength {
|
||||
return nil, ErrBadChunk
|
||||
}
|
||||
chunk.Type = FMP_CHUNK_SEGMENTED_DATA
|
||||
chunk.Type = FmpChunkSegmentedData
|
||||
chunk.Index = parseVarUint64(payload[1 : 1+2])
|
||||
chunk.Value = payload[5:chunk.Length]
|
||||
|
||||
case 0x10, 0x11:
|
||||
chunk.Length = 4 + addIf(chunkCode == 0x11, 1)
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_DATA
|
||||
chunk.Type = FmpChunkSimpleData
|
||||
chunk.Value = payload[1:chunk.Length]
|
||||
|
||||
case 0x12, 0x13, 0x14, 0x15:
|
||||
chunk.Length = 4 + 2*(uint64(chunkCode)-0x11)
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_DATA
|
||||
chunk.Type = FmpChunkSimpleData
|
||||
chunk.Value = payload[1:chunk.Length]
|
||||
|
||||
case 0x16:
|
||||
chunk.Length = 5 + uint64(payload[4])
|
||||
chunk.Type = FMP_CHUNK_LONG_KEY_VALUE
|
||||
chunk.Type = FmpChunkLongKeyValue
|
||||
chunk.Key = parseVarUint64(payload[1 : 1+3])
|
||||
chunk.Value = payload[5:chunk.Length]
|
||||
|
||||
case 0x17:
|
||||
chunk.Length = 6 + parseVarUint64(payload[4:4+2])
|
||||
chunk.Type = FMP_CHUNK_LONG_KEY_VALUE
|
||||
chunk.Type = FmpChunkLongKeyValue
|
||||
chunk.Key = parseVarUint64(payload[1 : 1+3])
|
||||
chunk.Value = payload[6:chunk.Length]
|
||||
|
||||
case 0x19, 0x1A, 0x1B, 0x1C, 0x1D:
|
||||
valueLength := uint64(payload[1])
|
||||
chunk.Length = 2 + valueLength + 2*uint64(chunkCode-0x19) + addIf(chunkCode == 0x19, 1)
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_DATA
|
||||
chunk.Type = FmpChunkSimpleData
|
||||
chunk.Value = payload[2 : 2+valueLength]
|
||||
|
||||
case 0x1E:
|
||||
keyLength := uint64(payload[1])
|
||||
valueLength := uint64(payload[2+keyLength])
|
||||
chunk.Length = 2 + keyLength + 1 + valueLength
|
||||
chunk.Type = FMP_CHUNK_LONG_KEY_VALUE
|
||||
chunk.Type = FmpChunkLongKeyValue
|
||||
chunk.Key = parseVarUint64(payload[2 : 2+keyLength])
|
||||
chunk.Value = payload[2+keyLength+1 : chunk.Length]
|
||||
|
||||
@ -214,44 +220,44 @@ func (sect *FmpSector) readChunk(payload []byte) (*FmpChunk, error) {
|
||||
keyLength := uint64(payload[1])
|
||||
valueLength := parseVarUint64(payload[2+keyLength : 2+keyLength+2+1])
|
||||
chunk.Length = 2 + keyLength + 2 + valueLength
|
||||
chunk.Type = FMP_CHUNK_LONG_KEY_VALUE
|
||||
chunk.Type = FmpChunkLongKeyValue
|
||||
chunk.Key = parseVarUint64(payload[2 : 2+keyLength])
|
||||
chunk.Value = payload[2+keyLength+2 : chunk.Length]
|
||||
|
||||
case 0x20, 0xE0:
|
||||
if payload[1] == 0xFE {
|
||||
chunk.Length = 10
|
||||
chunk.Type = FMP_CHUNK_PATH_PUSH
|
||||
chunk.Type = FmpChunkPathPush
|
||||
chunk.Value = payload[2:chunk.Length]
|
||||
break
|
||||
}
|
||||
|
||||
chunk.Length = 2
|
||||
chunk.Type = FMP_CHUNK_PATH_PUSH
|
||||
chunk.Type = FmpChunkPathPush
|
||||
chunk.Value = payload[1:chunk.Length]
|
||||
|
||||
case 0x23:
|
||||
chunk.Length = 2 + uint64(payload[1])
|
||||
chunk.Type = FMP_CHUNK_SIMPLE_DATA
|
||||
chunk.Type = FmpChunkSimpleData
|
||||
chunk.Value = payload[1:chunk.Length]
|
||||
|
||||
case 0x28, 0x30:
|
||||
chunk.Length = 3 + addIf(chunkCode == 0x30, 1)
|
||||
chunk.Type = FMP_CHUNK_PATH_PUSH
|
||||
chunk.Type = FmpChunkPathPush
|
||||
chunk.Value = payload[1:chunk.Length]
|
||||
|
||||
case 0x38:
|
||||
valueLength := uint64(payload[1])
|
||||
chunk.Length = 2 + valueLength
|
||||
chunk.Type = FMP_CHUNK_PATH_PUSH_LONG
|
||||
chunk.Type = FmpChunkPathPushLong
|
||||
chunk.Value = payload[2:chunk.Length]
|
||||
|
||||
case 0x3D, 0x40:
|
||||
chunk.Type = FMP_CHUNK_PATH_POP
|
||||
chunk.Type = FmpChunkPathPop
|
||||
chunk.Length = 1
|
||||
|
||||
case 0x80:
|
||||
chunk.Type = FMP_CHUNK_NOOP
|
||||
chunk.Type = FmpChunkNoop
|
||||
chunk.Length = 1
|
||||
|
||||
default:
|
||||
@ -260,10 +266,3 @@ func (sect *FmpSector) readChunk(payload []byte) (*FmpChunk, error) {
|
||||
|
||||
return chunk, nil
|
||||
}
|
||||
|
||||
func addIf(cond bool, val uint64) uint64 {
|
||||
if cond {
|
||||
return val
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
package fmp
|
||||
|
||||
type FmpTable struct {
|
||||
ID uint64
|
||||
Name string
|
||||
}
|
||||
|
||||
func (ctx *FmpFile) Tables() []*FmpTable {
|
||||
tables := make([]*FmpTable, 0)
|
||||
ent := ctx.Dictionary.GetEntry([]uint64{3, 16, 5})
|
||||
|
@ -29,16 +29,13 @@ func TestTables(t *testing.T) {
|
||||
}
|
||||
tables := f.Tables()
|
||||
|
||||
expected := "Untitled, WayDomains, WayProcesses"
|
||||
tablesString := ""
|
||||
for i, table := range tables {
|
||||
tablesString += table.Name
|
||||
if i < len(tables)-1 {
|
||||
tablesString += ", "
|
||||
}
|
||||
expectedNames := []string{"Untitled", "WayDomains", "WayProcesses"}
|
||||
tableNames := []string{}
|
||||
for _, table := range tables {
|
||||
tableNames = append(tableNames, table.Name)
|
||||
}
|
||||
|
||||
if tablesString != expected {
|
||||
t.Errorf("expected tables to be '%s', got '%s'", expected, tablesString)
|
||||
if !slicesHaveSameElements(tableNames, expectedNames) {
|
||||
t.Errorf("tables do not match")
|
||||
}
|
||||
}
|
||||
|
@ -1,94 +0,0 @@
|
||||
package fmp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FmpFile struct {
|
||||
VersionDate time.Time
|
||||
CreatorName string
|
||||
FileSize uint
|
||||
Sectors []*FmpSector
|
||||
Chunks []*FmpChunk
|
||||
Dictionary *FmpDict
|
||||
|
||||
numSectors uint64 // Excludes the header sector
|
||||
currentSectorID uint64
|
||||
|
||||
stream io.ReadSeeker
|
||||
}
|
||||
|
||||
type FmpSector struct {
|
||||
ID uint64
|
||||
Level uint8
|
||||
Deleted bool
|
||||
PrevID uint64
|
||||
NextID uint64
|
||||
Prev *FmpSector
|
||||
Next *FmpSector
|
||||
Payload []byte
|
||||
Chunks []*FmpChunk
|
||||
}
|
||||
|
||||
type FmpChunk struct {
|
||||
Type FmpChunkType
|
||||
Length uint64
|
||||
Key uint64 // If Type == FMP_CHUNK_SHORT_KEY_VALUE or FMP_CHUNK_LONG_KEY_VALUE
|
||||
Index uint64 // Segment index, if Type == FMP_CHUNK_SEGMENTED_DATA
|
||||
Value []byte
|
||||
}
|
||||
|
||||
type FmpDict map[uint64]*FmpDictEntry
|
||||
|
||||
type FmpDictEntry struct {
|
||||
Value []byte
|
||||
Children *FmpDict
|
||||
}
|
||||
|
||||
type FmpTable struct {
|
||||
ID uint64
|
||||
Name string
|
||||
}
|
||||
|
||||
func (dict *FmpDict) GetEntry(path []uint64) *FmpDictEntry {
|
||||
for i, key := range path {
|
||||
_, ok := (*dict)[key]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if i == len(path)-1 {
|
||||
return (*dict)[key]
|
||||
} else {
|
||||
dict = (*dict)[key].Children
|
||||
if dict == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dict *FmpDict) GetValue(path []uint64) []byte {
|
||||
ent := dict.GetEntry(path)
|
||||
if ent != nil {
|
||||
return ent.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dict *FmpDict) SetValue(path []uint64, 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
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,56 @@
|
||||
package fmp
|
||||
|
||||
import "slices"
|
||||
|
||||
type FmpDict map[uint64]*FmpDictEntry
|
||||
|
||||
type FmpDictEntry struct {
|
||||
Value []byte
|
||||
Children *FmpDict
|
||||
}
|
||||
|
||||
func (dict *FmpDict) GetEntry(path []uint64) *FmpDictEntry {
|
||||
for i, key := range path {
|
||||
_, ok := (*dict)[key]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if i == len(path)-1 {
|
||||
return (*dict)[key]
|
||||
} else {
|
||||
dict = (*dict)[key].Children
|
||||
if dict == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dict *FmpDict) GetValue(path []uint64) []byte {
|
||||
ent := dict.GetEntry(path)
|
||||
if ent != nil {
|
||||
return ent.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dict *FmpDict) SetValue(path []uint64, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseVarUint64(payload []byte) uint64 {
|
||||
var length uint64
|
||||
n := min(len(payload), 8) // clamp to uint64
|
||||
@ -17,3 +68,23 @@ func decodeByteSeq(payload []byte) string {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func addIf(cond bool, val uint64) uint64 {
|
||||
if cond {
|
||||
return val
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func slicesHaveSameElements[Type comparable](a, b []Type) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for _, av := range a {
|
||||
found := slices.Contains(b, av)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
Reference in New Issue
Block a user