diff --git a/fmp/fmp_const.go b/fmp/fmp_const.go index f109777..421d065 100644 --- a/fmp/fmp_const.go +++ b/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 ) diff --git a/fmp/fmp_debug.go b/fmp/fmp_debug.go index 536e170..dfff78e 100644 --- a/fmp/fmp_debug.go +++ b/fmp/fmp_debug.go @@ -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 { diff --git a/fmp/fmp_file.go b/fmp/fmp_file.go index 61152dc..3d52e8d 100644 --- a/fmp/fmp_file.go +++ b/fmp/fmp_file.go @@ -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 { diff --git a/fmp/fmp_sector.go b/fmp/fmp_sector.go index c8c611c..fa96cb4 100644 --- a/fmp/fmp_sector.go +++ b/fmp/fmp_sector.go @@ -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 -} diff --git a/fmp/fmp_table.go b/fmp/fmp_table.go index 3338925..1ecd11b 100644 --- a/fmp/fmp_table.go +++ b/fmp/fmp_table.go @@ -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}) diff --git a/fmp/fmp_test.go b/fmp/fmp_test.go index bf3beec..5ccce03 100644 --- a/fmp/fmp_test.go +++ b/fmp/fmp_test.go @@ -1,22 +1,6 @@ package fmp -import ( - "slices" - "testing" -) - -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 -} +import "testing" func TestOpenFile(t *testing.T) { f, err := OpenFile("../files/Untitled.fmp12") diff --git a/fmp/fmp_type.go b/fmp/fmp_type.go deleted file mode 100644 index 401f232..0000000 --- a/fmp/fmp_type.go +++ /dev/null @@ -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 - } - } -} diff --git a/fmp/fmp_util.go b/fmp/fmp_util.go index e2225ad..98f303b 100644 --- a/fmp/fmp_util.go +++ b/fmp/fmp_util.go @@ -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 +}